Skip to content

feat(lang-ts): support lang="ts" in <style> and <custom> blocks #208

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions src/common/ast-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ import type {
* @returns `true` if the node is a `<script>` element.
*/
export function isScriptElement(node: VNode): node is VElement {
return node.type === "VElement" && node.name === "script"
return (
node.type === "VElement" &&
(node.name === "script" || getLang(node) === "ts")
)
}

/**
Expand Down Expand Up @@ -44,7 +47,11 @@ export function isTemplateElement(node: VNode): node is VElement {
* @returns `true` if the node is a `<style>` element.
*/
export function isStyleElement(node: VNode): node is VElement {
return node.type === "VElement" && node.name === "style"
return (
node.type === "VElement" &&
node.name === "style" &&
!(getLang(node) !== "ts")
)
}

/**
Expand Down
4 changes: 2 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,12 +131,12 @@ function parseAsSFC(code: string, options: ParserOptions) {
parser: scriptParser,
})
} else if (
scripts.length === 2 &&
scripts.length >= 2 &&
(scriptSetup = scripts.find(isScriptSetupElement))
) {
result = parseScriptSetupElements(
scriptSetup,
scripts.find((e) => e !== scriptSetup)!,
scripts.filter((e) => e !== scriptSetup)!,
code,
new LinesAndColumns(tokenizer.lineTerminators),
{
Expand Down
36 changes: 21 additions & 15 deletions src/script-setup/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ function parseScript(
*/
export function parseScriptSetupElements(
scriptSetupElement: VElement,
scriptElement: VElement,
scriptElements: VElement[],
sfcCode: string,
linesAndColumns: LinesAndColumns,
originalParserOptions: ParserOptions,
Expand All @@ -216,7 +216,7 @@ export function parseScriptSetupElements(
)
const scriptSetupModuleCodeBlocks = getScriptSetupModuleCodeBlocks(
scriptSetupElement,
scriptElement,
scriptElements,
sfcCode,
linesAndColumns,
parserOptions,
Expand Down Expand Up @@ -283,7 +283,7 @@ export function parseScriptSetupElements(

// Adjust AST and tokens
if (result.ast.tokens != null) {
for (const node of [scriptSetupElement, scriptElement]) {
for (const node of [scriptSetupElement, ...scriptElements]) {
const startTag = node.startTag
const endTag = node.endTag

Expand Down Expand Up @@ -314,7 +314,7 @@ export function parseScriptSetupElements(
result.ast.loc.start =
locationCalculator.getLocFromIndex(programStartOffset)
if (result.ast.start != null) {
result.ast.start = [scriptSetupElement, scriptElement].reduce(
result.ast.start = [scriptSetupElement, ...scriptElements].reduce(
(start, node) => {
const textNode = node.children[0]
return Math.min(
Expand All @@ -335,7 +335,7 @@ export function parseScriptSetupElements(
result.ast.range[1] = programEndOffset
result.ast.loc.end = locationCalculator.getLocFromIndex(programEndOffset)
if (result.ast.end != null) {
result.ast.end = [scriptSetupElement, scriptElement].reduce(
result.ast.end = [scriptSetupElement, ...scriptElements].reduce(
(end, node) => {
const textNode = node.children[0]
return Math.max(
Expand Down Expand Up @@ -447,7 +447,7 @@ export function parseScriptSetupElements(
*/
function getScriptSetupModuleCodeBlocks(
scriptSetupElement: VElement,
scriptElement: VElement,
scriptElements: VElement[],
sfcCode: string,
linesAndColumns: LinesAndColumns,
parserOptions: ParserOptions,
Expand All @@ -459,24 +459,30 @@ function getScriptSetupModuleCodeBlocks(
parserOptions,
)

const textNode = scriptElement.children[0]
if (textNode == null || textNode.type !== "VText") {
return scriptSetupCodeBlocks
const codeBlocks = new CodeBlocks()

for (const scriptElement of scriptElements) {
const textNode = scriptElement.children[0]
if (textNode == null || textNode.type !== "VText") {
continue
}

const [scriptStartOffset, scriptEndOffset] = textNode.range

codeBlocks.append(
sfcCode.slice(scriptStartOffset, scriptEndOffset),
scriptStartOffset,
)
}

const [scriptStartOffset, scriptEndOffset] = textNode.range
const codeBlocks = new CodeBlocks()
codeBlocks.append(
sfcCode.slice(scriptStartOffset, scriptEndOffset),
scriptStartOffset,
)
if (scriptSetupCodeBlocks == null) {
return { codeBlocks }
}

codeBlocks.appendSplitPunctuators(";")
const scriptSetupOffset = codeBlocks.length
codeBlocks.appendCodeBlocks(scriptSetupCodeBlocks.codeBlocks)

return {
codeBlocks,
scriptSetupBlockRange: [
Expand Down
22 changes: 22 additions & 0 deletions test/fixtures/lang-ts.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<template>
<div>
</div>
</template>

<script setup lang="ts">
let test = 'hello-world'
</script>

<style lang="ts">
const testStyle = {
backgroundColor: 'red',
color: 'blue'
}
</style>

<custom lang="ts">
const testi18n = {
en: 'hello World',
de: 'Hallo Welt'
}
</custom>
30 changes: 30 additions & 0 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,36 @@ describe("Basic tests", () => {
})
})

describe("About fixtures/lang-ts", () => {
it('Should parse lang="ts" blocks as script blocks', () => {
const ast = parse(
[
"<template><div></div></template>",
'<script lang="ts">const test = "test"</script>',
'<style lang="ts">const testStyle = {}</style>',
'<custom lang="ts">const testCustom = {}</custom>',
].join("\n"),
)
const body = ast
})
it('Should treat lang="ts" blocks as script tags', async () => {
const cli = new ESLint({
cwd: FIXTURE_DIR,
overrideConfig: {
env: { browser: true, node: true },
parser: PARSER_PATH,
parserOptions: {
...BABEL_PARSER_OPTIONS,
sourceType: "module",
ecmaVersion: 2017,
},
},
useEslintrc: false,
})
const report = await cli.lintFiles(["lang-ts.vue"])
})
})

describe("About unexpected-null-character errors", () => {
it("should keep NULL in DATA state.", () => {
const ast = parse("<template>\u0000</template>")
Expand Down