Skip to content

Commit

Permalink
feat: treat jsdoc with @typescript-eslint/parser
Browse files Browse the repository at this point in the history
  • Loading branch information
baseballyama committed Dec 1, 2024
1 parent 00d988f commit 16f1abb
Show file tree
Hide file tree
Showing 17 changed files with 1,299 additions and 44 deletions.
20 changes: 15 additions & 5 deletions src/context/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,17 @@ import { LetDirectiveCollections } from "./let-directive-collection";
import { parseAttributes } from "../parser/html";
import { sortedLastIndex } from "../utils";
import {
isTypeScript,
getLanguage,
type NormalizedParserOptions,
} from "../parser/parser-options";

export class ScriptsSourceCode {
private raw: string;

public getRaw(): string {
return this.raw;
}

private trimmedRaw: string;

public readonly attrs: Record<string, string | undefined>;
Expand Down Expand Up @@ -175,7 +179,7 @@ export class Context {
public readonly snippets: SvelteSnippetBlock[] = [];

// ----- States ------
private readonly state: { isTypeScript?: boolean } = {};
private readonly state: { language?: "js" | "ts" | "jsdoc" | string } = {};

private readonly blocks: Block[] = [];

Expand Down Expand Up @@ -321,11 +325,17 @@ export class Context {
}

public isTypeScript(): boolean {
if (this.state.isTypeScript != null) {
return this.state.isTypeScript;
return this.getLanguage() === "ts";
}

public getLanguage(): "js" | "ts" | "jsdoc" | string {
if (this.state.language != null) {
return this.state.language;
}
const lang = this.sourceCode.scripts.attrs.lang;
return (this.state.isTypeScript = isTypeScript(this.parserOptions, lang));
const code = this.sourceCode.scripts.getRaw();
const language = getLanguage(this.parserOptions, lang, code);
return (this.state.language = language);
}

public stripScriptCode(start: number, end: number): void {
Expand Down
47 changes: 30 additions & 17 deletions src/parser/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import {
} from "./style-context";
import { getGlobalsForSvelte, getGlobalsForSvelteScript } from "./globals";
import type { NormalizedParserOptions } from "./parser-options";
import { isTypeScript, normalizeParserOptions } from "./parser-options";
import { getLanguage, normalizeParserOptions } from "./parser-options";
import { getFragmentFromRoot } from "./compat";
import {
isEnableRunes,
Expand Down Expand Up @@ -140,18 +140,24 @@ function parseAsSvelte(
);

const scripts = ctx.sourceCode.scripts;
const resultScript = ctx.isTypeScript()
? parseTypeScriptInSvelte(
scripts.getCurrentVirtualCodeInfo(),
scripts.attrs,
parserOptions,
{ slots: ctx.slots, svelteParseContext },
)
: parseScriptInSvelte(
scripts.getCurrentVirtualCode(),
scripts.attrs,
parserOptions,
);
const language = ctx.getLanguage();

const resultScript =
language === "ts" || language === "jsdoc"
? parseTypeScriptInSvelte(
scripts.getCurrentVirtualCodeInfo(),
{
...scripts.attrs,
lang: language === "jsdoc" ? "ts" : scripts.attrs.lang,
},
parserOptions,
{ slots: ctx.slots, svelteParseContext },
)
: parseScriptInSvelte(
scripts.getCurrentVirtualCode(),
scripts.attrs,
parserOptions,
);
ctx.scriptLet.restore(resultScript);
ctx.tokens.push(...resultScript.ast.tokens);
ctx.comments.push(...resultScript.ast.comments);
Expand Down Expand Up @@ -249,10 +255,17 @@ function parseAsScript(
parserOptions: NormalizedParserOptions,
svelteParseContext: SvelteParseContext,
): ParseResult {
const lang = parserOptions.filePath?.split(".").pop();
const resultScript = isTypeScript(parserOptions, lang)
? parseTypeScript(code, { lang }, parserOptions, svelteParseContext)
: parseScript(code, { lang }, parserOptions);
const rawLang = parserOptions.filePath?.split(".").pop();
const lang = getLanguage(parserOptions, rawLang, code);
const resultScript =
lang === "ts" || lang === "jsdoc"
? parseTypeScript(
code,
{ lang: lang === "jsdoc" ? "ts" : rawLang },
parserOptions,
svelteParseContext,
)
: parseScript(code, { lang: rawLang }, parserOptions);

// Add $$xxx variable
addGlobalVariables(
Expand Down
57 changes: 45 additions & 12 deletions src/parser/parser-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,35 +63,50 @@ const TS_PARSER_NAMES = [
"typescript-eslint-parser-for-extra-files",
];

export function isTypeScript(
export function getLanguage(
parserOptions: NormalizedParserOptions,
lang: string | undefined,
): boolean {
if (!lang) {
return false;
code: string | undefined,
): "js" | "ts" | "jsdoc" | string {
const hasJsDoc = code ? jsdocTags.some((tag) => tag.test(code)) : false;
if (!lang && !hasJsDoc) {
return "js";
}
const parserValue = getParserForLang(lang, parserOptions?.parser);

function getFinalLang(isTS: boolean): string {
if (isTS) {
if (lang) return lang;
return hasJsDoc ? "jsdoc" : "ts";
}
return lang || "js";
}

const parserValue = getParserForLang(
lang || (hasJsDoc ? "ts" : undefined),
parserOptions?.parser,
);
if (typeof parserValue !== "string") {
return (
const isTS =
maybeTSESLintParserObject(parserValue) ||
isTSESLintParserObject(parserValue)
);
isTSESLintParserObject(parserValue);
return getFinalLang(isTS);
}
const parserName = parserValue;
if (TS_PARSER_NAMES.includes(parserName)) {
return true;
return getFinalLang(true);
}
if (TS_PARSER_NAMES.some((nm) => parserName.includes(nm))) {
let targetPath = parserName;
while (targetPath) {
const pkgPath = path.join(targetPath, "package.json");
if (fs.existsSync(pkgPath)) {
try {
return TS_PARSER_NAMES.includes(
const isTS = TS_PARSER_NAMES.includes(
JSON.parse(fs.readFileSync(pkgPath, "utf-8"))?.name,
);
return getFinalLang(isTS);
} catch {
return false;
return getFinalLang(false);
}
}
const parent = path.dirname(targetPath);
Expand All @@ -102,5 +117,23 @@ export function isTypeScript(
}
}

return false;
return getFinalLang(false);
}

const jsdocTags = [
/@type\s/,
/@param\s/,
/@arg\s/,
/@argument\s/,
/@returns\s/,
/@return\s/,
/@typedef\s/,
/@callback\s/,
/@template\s/,
/@class\s/,
/@constructor\s/,
/@this\s/,
/@extends\s/,
/@augments\s/,
/@enum\s/,
] as const;
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export function getConfig(): Linter.Config {
return {
languageOptions: {
parser,
parserOptions: generateParserOptions({ parser: { ts } }),
parserOptions: generateParserOptions({ parser: ts }),
globals: {
...globals.browser,
...globals.es2021,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<script>
/** @param {number} x */
function test(x) {
return x + 1;
}
</script>
9 changes: 9 additions & 0 deletions tests/fixtures/integrations/type-info-tests/jsdoc-output.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[
{
"ruleId": "@typescript-eslint/no-unsafe-return",
"code": "return x + 1;",
"line": 4,
"column": 5,
"message": "Unsafe return of a value of type `any`."
}
]
31 changes: 31 additions & 0 deletions tests/fixtures/integrations/type-info-tests/jsdoc-setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import type { Linter } from "eslint";
import { generateParserOptions } from "../../../src/parser/test-utils";
import { rules } from "@typescript-eslint/eslint-plugin";
import * as ts from "@typescript-eslint/parser";
import * as parser from "../../../../src";
import globals from "globals";

export function getConfig(): Linter.Config {
return {
plugins: {
"@typescript-eslint": {
rules: rules as any,
},
},
languageOptions: {
parser,
parserOptions: generateParserOptions({ parser: ts }),
globals: {
...globals.browser,
...globals.es2021,
},
},
rules: {
"@typescript-eslint/no-unsafe-argument": "error",
"@typescript-eslint/no-unsafe-assignment": "error",
"@typescript-eslint/no-unsafe-call": "error",
"@typescript-eslint/no-unsafe-member-access": "error",
"@typescript-eslint/no-unsafe-return": "error",
},
};
}
6 changes: 6 additions & 0 deletions tests/fixtures/parser/ast/jsdoc-input.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<script>
/** @param {number} x */
function test(x) {
return x + 1;
}
</script>
8 changes: 8 additions & 0 deletions tests/fixtures/parser/ast/jsdoc-no-unused-vars-result.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[
{
"ruleId": "no-unused-vars",
"code": "test",
"line": 3,
"column": 12
}
]
Loading

0 comments on commit 16f1abb

Please sign in to comment.