diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index e1d1e9de..6bb0a761 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -55,10 +55,8 @@ jobs: run: npm install -f - name: Install ESLint v${{ matrix.eslint }} run: node scripts/ci-install-eslint ${{ matrix.eslint }} - - name: Build - run: npm run -s build - name: Test - run: npm run -s test:mocha + run: npm run -s test test-for-old-eslint: name: Test strategy: @@ -82,4 +80,4 @@ jobs: - name: Install ESLint v${{ matrix.eslint }} run: node scripts/ci-install-eslint ${{ matrix.eslint }} - name: Test - run: npm run -s test:debug + run: npm run -s test diff --git a/.gitignore b/.gitignore index 8bf60fc5..387f51ab 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ -/.nyc_output /.temp /coverage node_modules diff --git a/.nycrc b/.nycrc deleted file mode 100644 index a31275f2..00000000 --- a/.nycrc +++ /dev/null @@ -1,19 +0,0 @@ -{ - "include": [ - "src/**/*.ts" - ], - "exclude": [ - "src/external/**/*.ts" - ], - "extension": [ - ".ts" - ], - "require": [ - "ts-node/register" - ], - "reporter": [ - "lcov", - "text-summary" - ], - "sourceMap": true -} diff --git a/eslint.config.mjs b/eslint.config.mjs index 5e81622f..a46acd53 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -22,7 +22,6 @@ const compat = new FlatCompat({ export default [ { ignores: [ - ".nyc_output", ".temp", "coverage", "**/node_modules", @@ -64,7 +63,7 @@ export default [ }, loggerFn: false, - project: "tsconfig.json", + project: ["tsconfig.json", "tsconfig.test.json"], }, }, diff --git a/package.json b/package.json index 63586655..b325e750 100644 --- a/package.json +++ b/package.json @@ -30,11 +30,12 @@ "@eslint/js": "^9.19.0", "@types/debug": "^4.1.7", "@types/estree": "^1.0.0", - "@types/mocha": "^9.0.0", "@types/node": "^18.8.4", "@types/semver": "^7.3.12", "@typescript-eslint/eslint-plugin": "^8.22.0", "@typescript-eslint/parser": "^8.22.0", + "@vitest/coverage-v8": "^3.2.4", + "@vitest/ui": "^3.2.4", "chokidar": "^3.5.2", "cross-spawn": "^7.0.3", "dts-bundle": "^0.7.3", @@ -47,10 +48,7 @@ "eslint-plugin-unicorn": "^57.0.0", "fs-extra": "^10.0.0", "jsonc-eslint-parser": "^2.0.3", - "mocha": "^9.1.3", "npm-run-all": "^4.1.5", - "nyc": "^15.1.0", - "opener": "^1.5.2", "prettier": "^3.4.2", "rimraf": "^3.0.2", "rollup": "^2.60.0", @@ -59,20 +57,19 @@ "rollup-plugin-sourcemaps": "^0.6.3", "ts-node": "^10.9.2", "typescript": "~5.7.3", + "vite": "^6.3.5", + "vitest": "^3.2.4", "wait-on": "^6.0.0", "warun": "^1.0.0" }, "scripts": { "prebuild": "npm run -s clean", "build": "tsc --module es2015 && rollup -c -o index.js && dts-bundle --name vue-eslint-parser --main .temp/index.d.ts --out ../index.d.ts", - "clean": "rimraf .nyc_output .temp coverage index.*", - "coverage": "opener ./coverage/lcov-report/index.html", + "clean": "rimraf .temp index.*", + "coverage": "vitest --coverage --ui", "lint": "eslint src test package.json", - "pretest": "run-s build lint", - "test": "npm run -s test:mocha", - "test:mocha": "mocha --require ts-node/register \"test/*.js\" --reporter dot --timeout 60000", - "test:cover": "nyc mocha \"test/*.js\" --reporter dot --timeout 60000", - "test:debug": "mocha --require ts-node/register/transpile-only \"test/*.js\" --reporter dot --timeout 60000", + "test": "vitest", + "test:cover": "vitest --coverage", "update-fixtures": "ts-node --transpile-only scripts/update-fixtures-ast.js && ts-node --transpile-only scripts/update-fixtures-document-fragment.js", "preversion": "npm test", "version": "npm run -s build", @@ -81,9 +78,7 @@ "watch": "run-p watch:*", "watch:tsc": "tsc --module es2015 --watch", "watch:rollup": "wait-on .temp/index.js && rollup -c -o index.js --watch", - "watch:test": "wait-on index.js && warun index.js \"test/*.js\" \"test/fixtures/ast/*/*.json\" \"test/fixtures/*\" --debounce 1000 --no-initial -- nyc mocha \"test/*.js\" --reporter dot --timeout 10000", - "watch:update-ast": "wait-on index.js && warun index.js \"test/fixtures/ast/*/*.vue\" -- ts-node scripts/update-fixtures-ast.js", - "watch:coverage-report": "wait-on coverage/lcov-report/index.html && opener coverage/lcov-report/index.html" + "watch:update-ast": "wait-on index.js && warun index.js \"test/fixtures/ast/*/*.vue\" -- ts-node scripts/update-fixtures-ast.js" }, "repository": { "type": "git", diff --git a/scripts/ci-install-eslint.js b/scripts/ci-install-eslint.js index c3626692..fe2f96c3 100644 --- a/scripts/ci-install-eslint.js +++ b/scripts/ci-install-eslint.js @@ -30,6 +30,8 @@ function sh(command) { // Install ESLint of the requested version await sh(`npm install eslint@${requestedVersionSpec} -f`) + if (Number(requestedVersion) < 9) + await sh(`npm install @types/eslint -D -f`) // Install ESLint submodule of the requested version // const installedVersion = require("eslint/package.json").version diff --git a/src/ast/nodes.ts b/src/ast/nodes.ts index d652e4e0..c24e3727 100644 --- a/src/ast/nodes.ts +++ b/src/ast/nodes.ts @@ -8,6 +8,7 @@ import type { ParseError } from "./errors" import type { HasLocation } from "./locations" import type { Token } from "./tokens" import type { TSESTree } from "@typescript-eslint/utils" +import type { ParserServices } from "../parser-services" //------------------------------------------------------------------------------ // Common @@ -70,7 +71,7 @@ export type ESLintNode = */ export interface ESLintExtendedProgram { ast: ESLintProgram - services?: {} + services?: ParserServices visitorKeys?: { [type: string]: string[] } scopeManager?: ScopeManager } diff --git a/src/index.ts b/src/index.ts index 13cb5300..45d43d64 100644 --- a/src/index.ts +++ b/src/index.ts @@ -84,7 +84,7 @@ export function parseForESLint( * @param options The parser options. * @returns The parsing result. */ -export function parse(code: string, options: any): AST.ESLintProgram { +export function parse(code: string, options?: any): AST.ESLintProgram { return parseForESLint(code, options).ast } diff --git a/src/parser-services.ts b/src/parser-services.ts index 3b830c41..b84faa43 100644 --- a/src/parser-services.ts +++ b/src/parser-services.ts @@ -83,7 +83,7 @@ export interface ParserServices { | ((lang: string | null, customBlock: VElement) => boolean) create: CustomBlockVisitorFactory }, - scriptVisitor: { [key: string]: (...args: any) => void }, + scriptVisitor?: { [key: string]: (...args: any) => void }, ): { [key: string]: (...args: any) => void } /** @@ -262,7 +262,7 @@ export function define( | ((lang: string | null, customBlock: VElement) => boolean) create: CustomBlockVisitorFactory }, - scriptVisitor: { [key: string]: (...args: any) => void }, + scriptVisitor?: { [key: string]: (...args: any) => void }, ): { [key: string]: (...args: any) => void } { if (scriptVisitor == null) { scriptVisitor = {} //eslint-disable-line no-param-reassign diff --git a/test/ast.js b/test/ast.test.ts similarity index 74% rename from test/ast.js rename to test/ast.test.ts index 0331042e..b402aa91 100644 --- a/test/ast.js +++ b/test/ast.test.ts @@ -3,27 +3,29 @@ * @copyright 2017 Toru Nagashima. All rights reserved. * See LICENSE file in root directory for full license. */ -"use strict" //------------------------------------------------------------------------------ // Requirements //------------------------------------------------------------------------------ -const assert = require("assert") -const fs = require("fs") -const path = require("path") -const parser = require("../src") -const eslint = require("eslint") -const semver = require("semver") -const { scopeToJSON, analyze, replacer, getAllTokens } = require("./test-utils") +import type { Rule } from "eslint" +import type { Node } from "../src/ast" +import type { ParserOptions } from "../src/common/parser-options" +import fs from "node:fs" +import path from "node:path" +import { describe, it, assert } from "vitest" +import { Linter } from "eslint" +import semver from "semver" +import * as parser from "../src" +import { scopeToJSON, analyze, replacer, getAllTokens } from "./test-utils" //------------------------------------------------------------------------------ // Helpers //------------------------------------------------------------------------------ -const Linter = eslint.Linter +// eslint-disable-next-line no-undef const ROOT = path.join(__dirname, "fixtures/ast") const TARGETS = fs.readdirSync(ROOT) -const PARSER_OPTIONS = { +const PARSER_OPTIONS: ParserOptions = { comment: true, ecmaVersion: "latest", sourceType: "module", @@ -33,22 +35,28 @@ const PARSER_OPTIONS = { eslintScopeManager: true, } +type TreeNode = { + type?: string + text?: string + children: TreeNode[] +} + /** * Create simple tree. - * @param {string} source The source code. - * @param {object} parserOptions The parser options. - * @returns {object} Simple tree. + * @param source The source code. + * @param parserOptions The parser options. + * @returns Simple tree. */ -function getTree(source, parserOptions) { +function getTree(source: string, parserOptions: any) { const linter = new Linter({ configType: "flat" }) - const stack = [] - const root = { children: [] } - let current = root + const stack: TreeNode[] = [] + const root: TreeNode = { children: [] } + let current: TreeNode = root - const maketree = { + const maketree: Rule.RuleModule = { create: (ruleContext) => ruleContext.sourceCode.parserServices.defineTemplateBodyVisitor({ - "*"(node) { + "*"(node: Node) { stack.push(current) current.children.push( (current = { @@ -59,32 +67,27 @@ function getTree(source, parserOptions) { ) }, "*:exit"() { - current = stack.pop() + current = stack.pop()! }, }), } - const result = linter.verify( - source, - { - files: ["**"], - plugins: { - test: { - rules: { - maketree, - }, + const result = linter.verify(source, { + files: ["**"], + plugins: { + test: { + rules: { + maketree, }, }, - languageOptions: { - parser: parser, - ecmaVersion: parserOptions.ecmaVersion ?? "latest", - sourceType: parserOptions.sourceType ?? "module", - parserOptions: parserOptions, - }, - rules: { "test/maketree": "error" }, }, - undefined, - true, - ) + languageOptions: { + parser, + ecmaVersion: parserOptions.ecmaVersion ?? "latest", + sourceType: parserOptions.sourceType ?? "module", + parserOptions, + }, + rules: { "test/maketree": "error" }, + }) assert.deepStrictEqual(result, []) return root.children @@ -92,30 +95,29 @@ function getTree(source, parserOptions) { /** * Convert a given node to string. - * @param {Node} node The node to make string expression. - * @param {string} source The source code. - * @returns {string} The string expression of the node. + * @param node The node to make string expression. + * @param source The source code. + * @returns The string expression of the node. */ -function nodeToString(node, source) { +function nodeToString(node: Node, source: string): string { return node ? `${node.type}[${source.slice(...node.range)}]` : "undefined" } /** * Validate the parent property of every node. - * @param {string} source The source code. - * @param {object} parserOptions The parser options. - * @returns {void} + * @param source The source code. + * @param parserOptions The parser options. */ -function validateParent(source, parserOptions) { +function validateParent(source: string, parserOptions: any) { const linter = new Linter({ configType: "flat" }) - const stack = [] + const stack: Node[] = [] - const validateparent = { + const validateparent: Rule.RuleModule = { create: (ruleContext) => ruleContext.sourceCode.parserServices.defineTemplateBodyVisitor({ - "*"(node) { + "*"(node: Node) { if (stack.length >= 1) { - const parent = stack.at(-1) + const parent = stack.at(-1)! assert( node.parent === parent, `The parent of ${nodeToString( @@ -124,7 +126,7 @@ function validateParent(source, parserOptions) { )} should be ${nodeToString( parent, source, - )}, but got ${nodeToString(node.parent, source)}`, + )}, but got ${nodeToString(node.parent!, source)}`, ) } stack.push(node) @@ -134,28 +136,23 @@ function validateParent(source, parserOptions) { }, }), } - const result = linter.verify( - source, - { - files: ["**"], - plugins: { - test: { - rules: { - validateparent, - }, + const result = linter.verify(source, { + files: ["**"], + plugins: { + test: { + rules: { + validateparent, }, }, - languageOptions: { - parser, - ecmaVersion: parserOptions.ecmaVersion ?? "latest", - sourceType: parserOptions.sourceType ?? "module", - parserOptions: parserOptions, - }, - rules: { "test/validateparent": "error" }, }, - undefined, - true, - ) + languageOptions: { + parser, + ecmaVersion: parserOptions.ecmaVersion ?? "latest", + sourceType: parserOptions.sourceType ?? "module", + parserOptions, + }, + rules: { "test/validateparent": "error" }, + }) assert.deepStrictEqual(result, []) } @@ -173,8 +170,13 @@ describe("Template AST", () => { const requirementsPath = path.join(ROOT, `${name}/requirements.json`) const servicesPath = path.join(ROOT, `${name}/services.json`) const source = fs.readFileSync(sourcePath, "utf8") - const parserOptions = optionsPath ? require(optionsPath) : {} - const requirements = fs.existsSync(requirementsPath) + + const parserOptions: ParserOptions = optionsPath + ? require(optionsPath) // eslint-disable-line @typescript-eslint/no-require-imports + : {} + const requirements: Record = fs.existsSync( + requirementsPath, + ) ? JSON.parse(fs.readFileSync(requirementsPath, "utf8")) : {} const services = fs.existsSync(servicesPath) @@ -185,12 +187,13 @@ describe("Template AST", () => { Object.entries(parserOptions.templateTokenizer).map( ([key, value]) => [ key, - path.resolve(__dirname, "../", value), + // eslint-disable-next-line no-undef + path.resolve(__dirname, "../", value as string), ], ), ) } - const options = { + const options: ParserOptions = { filePath: sourcePath, ...PARSER_OPTIONS, ...parserOptions, @@ -201,7 +204,7 @@ describe("Template AST", () => { const version = pkgName === "node" ? process.version - : require(`${pkgName}/package.json`).version + : require(`${pkgName}/package.json`).version // eslint-disable-line @typescript-eslint/no-require-imports return !semver.satisfies(version, pkgVersion) }) ) { @@ -252,7 +255,8 @@ describe("Template AST", () => { }) it("should have correct location.", () => { - const lines = source.match(/[^\r\n]*(?:\r?\n|$)/gu) ?? [] + const lines: string[] = + source.match(/[^\r\n]*(?:\r?\n|$)/gu) ?? [] lines.push(String.fromCodePoint(0)) for (const token of getAllTokens(actual.ast)) { const line0 = token.loc.start.line - 1 @@ -318,7 +322,7 @@ describe("Template AST", () => { if (services) { it("should have correct services.", () => { assert.deepStrictEqual( - Object.keys(actual.services).sort(), + Object.keys(actual.services!).sort(), services, ) }) diff --git a/test/crlf.js b/test/crlf.test.ts similarity index 73% rename from test/crlf.js rename to test/crlf.test.ts index d35ce260..c2ecb46d 100644 --- a/test/crlf.js +++ b/test/crlf.test.ts @@ -1,9 +1,10 @@ -const assert = require("assert") -const parser = require("../src") +import type { VElement, VText } from "../src/ast" +import { describe, it, assert } from "vitest" +import { parseForESLint } from "../src" describe("About CRLF tests", () => { it("should not contain CR in `" - const config = { + const config: eslint.Linter.Config = { languageOptions: { parser, parserOptions: { @@ -882,7 +890,7 @@ describe("Basic tests", async () => { it("should notify 1 no-undef error", () => { const code = "" - const config = { + const config: eslint.Linter.Config = { languageOptions: { parser, }, @@ -917,7 +925,7 @@ export default {} ` const result = parseForESLint(code, { sourceType: "module" }) - const comments = result.ast.comments + const comments = result.ast.comments! // Should have 2 comments assert.strictEqual(comments.length, 2) @@ -937,7 +945,7 @@ export default {} }) }) -function buildPlugins(rule) { +function buildPlugins(rule: Rule.RuleModule) { return { test: { rules: { diff --git a/test/integrations.js b/test/integrations.test.ts similarity index 67% rename from test/integrations.js rename to test/integrations.test.ts index 88e9c501..651e6616 100644 --- a/test/integrations.js +++ b/test/integrations.test.ts @@ -2,16 +2,18 @@ // Requirements //------------------------------------------------------------------------------ -const assert = require("assert") -const path = require("path") -const fs = require("fs-extra") -const cp = require("child_process") -const eslintCompat = require("./lib/eslint-compat") +import { assert, beforeAll, describe, it } from "vitest" +import path from "node:path" +import fs from "node:fs" +import cp from "child_process" +import eslintCompat from "./lib/eslint-compat" +import ESLintRaw from "eslint" //------------------------------------------------------------------------------ // Helpers //------------------------------------------------------------------------------ +// eslint-disable-next-line no-undef const FIXTURE_DIR = path.join(__dirname, "fixtures/integrations") //------------------------------------------------------------------------------ @@ -19,15 +21,19 @@ const FIXTURE_DIR = path.join(__dirname, "fixtures/integrations") //------------------------------------------------------------------------------ describe("Integration tests", () => { + beforeAll(async () => { + await import("ts-node/register") + }) for (const target of fs.readdirSync(FIXTURE_DIR)) { it(target, async () => { - let ESLint = eslintCompat(require("eslint")).ESLint + let ESLint = eslintCompat(ESLintRaw).ESLint if (fs.existsSync(path.join(FIXTURE_DIR, target, "package.json"))) { const originalCwd = process.cwd() try { process.chdir(path.join(FIXTURE_DIR, target)) cp.execSync("npm i", { stdio: "inherit" }) ESLint = eslintCompat( + // eslint-disable-next-line @typescript-eslint/no-require-imports require( path.join( FIXTURE_DIR, @@ -46,7 +52,7 @@ describe("Integration tests", () => { }) const report = await cli.lintFiles(["**/*.vue"]) - const outputPath = path.join(FIXTURE_DIR, target, `output.json`) + const outputPath = path.join(FIXTURE_DIR, target, "output.json") const expected = JSON.parse(fs.readFileSync(outputPath, "utf8")) try { assert.deepStrictEqual( @@ -59,7 +65,7 @@ describe("Integration tests", () => { const actualPath = path.join( FIXTURE_DIR, target, - `_actual.json`, + "_actual.json", ) fs.writeFileSync( actualPath, @@ -69,25 +75,24 @@ describe("Integration tests", () => { throw e } - function normalizeReport(report, option = {}) { - return report + function normalizeReport( + result: ESLintRaw.ESLint.LintResult[], + option: { withoutMessage?: boolean } = {}, + ) { + return result .filter((res) => res.messages.length) - .map((res) => { - return { - filePath: res.filePath - .replace(cwd, "") - .replace(/\\/gu, "/"), - messages: res.messages.map((msg) => { - return { - ruleId: msg.ruleId, - line: msg.line, - ...(option.withoutMessage - ? {} - : { message: msg.message }), - } - }), - } - }) + .map((res) => ({ + filePath: res.filePath + .replace(cwd, "") + .replace(/\\/gu, "/"), + messages: res.messages.map((msg) => ({ + ruleId: msg.ruleId, + line: msg.line, + ...(option.withoutMessage + ? {} + : { message: msg.message }), + })), + })) .sort((a, b) => a.filePath < b.filePath ? -1 diff --git a/test/lib/eslint-compat.js b/test/lib/eslint-compat.ts similarity index 60% rename from test/lib/eslint-compat.js rename to test/lib/eslint-compat.ts index 9077ec55..ae96362f 100644 --- a/test/lib/eslint-compat.js +++ b/test/lib/eslint-compat.ts @@ -1,11 +1,10 @@ -"use strict" +import type { ESLint, Linter, RuleTester } from "eslint" -/** - * @typedef {import('eslint')} eslint - */ - -/** @param {eslint} eslint */ -module.exports = function compat(eslint) { +export default function compat(eslint: any): { + ESLint: typeof ESLint + RuleTester: typeof RuleTester + Linter: typeof Linter +} { return { ESLint: eslint.ESLint || getESLintClassForV6(eslint), RuleTester: eslint.RuleTester, @@ -13,15 +12,17 @@ module.exports = function compat(eslint) { } } -/** @returns {typeof eslint.ESLint} */ -function getESLintClassForV6(eslint) { +function getESLintClassForV6(eslint: any): typeof ESLint { class ESLintForV6 { + public engine + + // eslint-disable-next-line @typescript-eslint/explicit-member-accessibility static get version() { return eslint.CLIEngine.version } - /** @param {eslint.ESLint.Options} options */ - constructor(options) { + // eslint-disable-next-line @typescript-eslint/explicit-member-accessibility + constructor(options: any) { const { overrideConfig: { plugins, @@ -33,14 +34,13 @@ function getESLintClassForV6(eslint) { plugins: [], globals: {}, rules: {}, - }, + } as any, overrideConfigFile, fix, reportUnusedDisableDirectives, plugins: pluginsMap, ...otherOptions } = options || {} - /** @type {eslint.CLIEngine.Options} */ const newOptions = { fix: Boolean(fix), reportUnusedDisableDirectives: reportUnusedDisableDirectives @@ -64,7 +64,7 @@ function getESLintClassForV6(eslint) { } return o }, - /** @type {NonNullable} */ {}, + {} as Record, ) : undefined, ...overrideConfig, @@ -76,41 +76,37 @@ function getESLintClassForV6(eslint) { } } - /** - * @param {Parameters} params - * @returns {ReturnType} - */ - async lintText(...params) { - const result = this.engine.executeOnText( + // eslint-disable-next-line @typescript-eslint/explicit-member-accessibility + async lintText( + ...params: Parameters + ): ReturnType { + const result = await this.engine.executeOnText( params[0], - params[1].filePath, + params[1]!.filePath, ) return result.results } - /** - * @param {Parameters} params - * @returns {ReturnType} - */ - async lintFiles(...params) { - const result = this.engine.executeOnFiles( + // eslint-disable-next-line @typescript-eslint/explicit-member-accessibility + async lintFiles( + ...params: Parameters + ): ReturnType { + const result = await this.engine.executeOnFiles( Array.isArray(params[0]) ? params[0] : [params[0]], ) return result.results } - /** - * @param {Parameters} params - * @returns {ReturnType} - */ - static async outputFixes(...params) { - return eslint.CLIEngine.outputFixes({ + // eslint-disable-next-line @typescript-eslint/explicit-member-accessibility + static async outputFixes( + ...params: Parameters + ): ReturnType { + // eslint-disable-next-line no-return-await + return await eslint.CLIEngine.outputFixes({ results: params[0], }) } } - /** @type {typeof eslint.ESLint} */ - const eslintClass = /** @type {any} */ ESLintForV6 - return eslintClass + return ESLintForV6 as any } diff --git a/test/parser-options-project.js b/test/parser-options-project.test.ts similarity index 93% rename from test/parser-options-project.js rename to test/parser-options-project.test.ts index 1fc3b12a..257f4ded 100644 --- a/test/parser-options-project.js +++ b/test/parser-options-project.test.ts @@ -1,8 +1,7 @@ -"use strict" - -const assert = require("assert") -const { parseForESLint } = require("../src") -const espree = require("espree") +import { describe, it, assert } from "vitest" +import { parseForESLint } from "../src" +import * as espree from "espree" +import type { Linter } from "eslint" describe("use `project: undefined` when parsing template script-let", () => { it("should be the project option is defined only once in Simple SFC.", () => { @@ -39,7 +38,7 @@ describe("use `project: undefined` when parsing template script-let", () => { ast: espree.parse(code, options), } }, - }, + } satisfies Linter.Parser, }, ) assert.strictEqual(projectCount, 1) @@ -83,7 +82,7 @@ describe("use `project: undefined` when parsing template script-let", () => { ast: espree.parse(code, options), } }, - }, + } satisfies Linter.Parser, }, ) assert.strictEqual(projectCount, 1) @@ -126,7 +125,7 @@ describe("use `project: undefined` when parsing template script-let", () => { ast: espree.parse(code, options), } }, - }, + } satisfies Linter.Parser, }, ) assert.strictEqual(projectCount, 1) diff --git a/test/parser-options.js b/test/parser-options.test.ts similarity index 86% rename from test/parser-options.js rename to test/parser-options.test.ts index 3c7e4e1b..37aa60a9 100644 --- a/test/parser-options.js +++ b/test/parser-options.test.ts @@ -2,18 +2,17 @@ * @author Toru Nagashima * See LICENSE file in root directory for full license. */ -"use strict" -const assert = require("assert") -const { parseForESLint } = require("../src") -const eslint = require("eslint") -const Linter = eslint.Linter +import { describe, it, assert } from "vitest" +import { parseForESLint } from "../src" +import type { ESLint } from "eslint" +import { Linter } from "eslint" describe("parserOptions", () => { describe("parser", () => { const linter = new Linter({ configType: "flat" }) - const parser = { parseForESLint } - const plugin = { + const parser: Linter.Parser = { parseForESLint } + const plugin: ESLint.Plugin = { rules: { "template-test": { create(context) { @@ -33,7 +32,7 @@ describe("parserOptions", () => { it("false then skip parsing '` - const config = { + const config: Linter.Config = { files: ["*.vue"], plugins: { vue: plugin, @@ -57,7 +56,7 @@ describe("parserOptions", () => { it("Fail in ` - const config = { + const config: Linter.Config = { files: ["*.vue"], plugins: { vue: plugin, diff --git a/test/test-utils.js b/test/test-utils.ts similarity index 68% rename from test/test-utils.js rename to test/test-utils.ts index 8ea0b760..f5305181 100644 --- a/test/test-utils.js +++ b/test/test-utils.ts @@ -1,14 +1,22 @@ -const escope = require("eslint-scope") - -module.exports = { replacer, getAllTokens, scopeToJSON, analyze } +import type { Identifier, Node } from "estree" +import type { + Scope, + ScopeManager, + Variable, + VariableDefinition, +} from "eslint-scope" +import type { ESLintProgram, Token } from "../src/ast" +import type { ParserOptions } from "../src/common/parser-options" +import * as escope from "eslint-scope" +import { getFallbackKeys } from "../src/ast" /** * Remove `parent` properties from the given AST. - * @param {string} key The key. - * @param {any} value The value of the key. - * @returns {any} The value of the key to output. + * @param key The key. + * @param value The value of the key. + * @returns The value of the key to output. */ -function replacer(key, value) { +export function replacer(key: string, value: any): any { if (key === "parent") { return undefined } @@ -25,10 +33,10 @@ function replacer(key, value) { /** * Get all tokens of the given AST. - * @param {ASTNode} ast The root node of AST. - * @returns {Token[]} Tokens. + * @param ast The root node of AST. + * @returns Tokens. */ -function getAllTokens(ast) { +export function getAllTokens(ast: ESLintProgram): Token[] { const tokenArrays = [ast.tokens, ast.comments] if (ast.templateBody != null) { tokenArrays.push(ast.templateBody.tokens, ast.templateBody.comments) @@ -36,10 +44,10 @@ function getAllTokens(ast) { return Array.prototype.concat.apply([], tokenArrays) } -function scopeToJSON(scopeManager) { +export function scopeToJSON(scopeManager: ScopeManager) { return JSON.stringify(normalizeScope(scopeManager.globalScope), replacer, 4) - function normalizeScope(scope) { + function normalizeScope(scope: Scope): any { return { type: scope.type, variables: scope.variables.map(normalizeVar), @@ -49,7 +57,7 @@ function scopeToJSON(scopeManager) { } } - function normalizeVar(v) { + function normalizeVar(v: Variable) { return { name: v.name, identifiers: v.identifiers.map(normalizeId), @@ -58,7 +66,7 @@ function scopeToJSON(scopeManager) { } } - function normalizeReference(reference) { + function normalizeReference(reference: any) { return { identifier: normalizeId(reference.identifier), from: reference.from.type, @@ -75,7 +83,7 @@ function scopeToJSON(scopeManager) { } } - function normalizeDef(def) { + function normalizeDef(def: VariableDefinition) { return { type: def.type, node: normalizeDefNode(def.node), @@ -83,7 +91,7 @@ function scopeToJSON(scopeManager) { } } - function normalizeId(identifier) { + function normalizeId(identifier: Identifier | null) { return ( identifier && { type: identifier.type, @@ -93,7 +101,7 @@ function scopeToJSON(scopeManager) { ) } - function normalizeDefNode(node) { + function normalizeDefNode(node: Node) { return { type: node.type, loc: node.loc, @@ -104,7 +112,10 @@ function scopeToJSON(scopeManager) { /** * Analyze scope */ -function analyze(ast, parserOptions) { +export function analyze( + ast: ESLintProgram, + parserOptions: ParserOptions, +): ScopeManager { const ecmaVersion = parserOptions.ecmaVersion ?? 2022 const ecmaFeatures = parserOptions.ecmaFeatures ?? {} const sourceType = parserOptions.sourceType ?? "script" @@ -118,23 +129,4 @@ function analyze(ast, parserOptions) { }) return result - - function getFallbackKeys(node) { - return Object.keys(node).filter(fallbackKeysFilter, node) - } - - function fallbackKeysFilter(key) { - const value = null - return ( - key !== "comments" && - key !== "leadingComments" && - key !== "loc" && - key !== "parent" && - key !== "range" && - key !== "tokens" && - key !== "trailingComments" && - typeof value === "object" && - (typeof value.type === "string" || Array.isArray(value)) - ) - } } diff --git a/test/tokens.js b/test/tokens.test.ts similarity index 70% rename from test/tokens.js rename to test/tokens.test.ts index de370298..b7133625 100644 --- a/test/tokens.js +++ b/test/tokens.test.ts @@ -3,14 +3,20 @@ * @copyright 2017 Toru Nagashima. All rights reserved. * See LICENSE file in root directory for full license. */ -"use strict" //------------------------------------------------------------------------------ // Requirements //------------------------------------------------------------------------------ -const assert = require("assert") -const parse = require("../src").parseForESLint +import type { + ESLintProgram, + Token, + VElement, + VExpressionContainer, +} from "../src/ast" +import type TokenStore from "../src/external/token-store" +import { assert, beforeAll, describe, it } from "vitest" +import { parseForESLint as parse } from "../src" //------------------------------------------------------------------------------ // Helpers @@ -26,10 +32,10 @@ const PARSER_OPTIONS = { /** * Get the value of the given node. - * @param {ASTNode} token The node to get value. - * @returns {string} The value of the node. + * @param token The node to get value. + * @returns The value of the node. */ -function toValue(token) { +function toValue(token: Token): string { if (token.type === "HTMLAssociation") { return "=" } @@ -48,18 +54,18 @@ describe("services.getTemplateBodyTokenStore", () => {
{{ message /*comment3*/ }}
` - let ast = null - let tokens = null + let ast: ESLintProgram | null = null + let tokens: TokenStore | null = null - before(() => { + beforeAll(() => { const result = parse(code, { filePath: "test.vue", ...PARSER_OPTIONS }) ast = result.ast - tokens = result.services.getTemplateBodyTokenStore() + tokens = result.services!.getTemplateBodyTokenStore() }) describe("ast.templateBody", () => { it("should return all tokens (except comments) in the template.", () => { - const actual = tokens.getTokens(ast.templateBody).map(toValue) + const actual = tokens!.getTokens(ast!.templateBody!).map(toValue) assert.deepStrictEqual(actual, [ "template", @@ -94,8 +100,8 @@ describe("services.getTemplateBodyTokenStore", () => { }) it("should return all tokens (include comments) in the template if you give {includeComments: true} option.", () => { - const actual = tokens - .getTokens(ast.templateBody, { includeComments: true }) + const actual = tokens! + .getTokens(ast!.templateBody!, { includeComments: true }) .map(toValue) assert.deepStrictEqual(actual, [ @@ -137,8 +143,8 @@ describe("services.getTemplateBodyTokenStore", () => { describe("ast.templateBody.children[0] (VText)", () => { it("should return a text token.", () => { - const node = ast.templateBody.children[0] - const actual = tokens.getTokens(node).map(toValue) + const node = ast!.templateBody!.children[0] + const actual = tokens!.getTokens(node).map(toValue) assert.deepStrictEqual(actual, ["\n "]) }) @@ -146,8 +152,8 @@ describe("services.getTemplateBodyTokenStore", () => { describe("ast.templateBody.children[2] (VElement)", () => { it("should return all tokens in the element.", () => { - const node = ast.templateBody.children[2] - const actual = tokens.getTokens(node).map(toValue) + const node = ast!.templateBody!.children[2] + const actual = tokens!.getTokens(node).map(toValue) assert.deepStrictEqual(actual, [ "div", @@ -177,8 +183,8 @@ describe("services.getTemplateBodyTokenStore", () => { describe("ast.templateBody.children[2].startTag (VStartTag)", () => { it("should return all tokens in the tag.", () => { - const node = ast.templateBody.children[2].startTag - const actual = tokens.getTokens(node).map(toValue) + const node = (ast!.templateBody!.children[2] as VElement).startTag + const actual = tokens!.getTokens(node).map(toValue) assert.deepStrictEqual(actual, [ "div", @@ -203,8 +209,9 @@ describe("services.getTemplateBodyTokenStore", () => { describe("ast.templateBody.children[2].startTag.attributes[0] (VAttribute)", () => { it("should return all tokens in the attribute.", () => { - const node = ast.templateBody.children[2].startTag.attributes[0] - const actual = tokens.getTokens(node).map(toValue) + const node = (ast!.templateBody!.children[2] as VElement).startTag + .attributes[0] + const actual = tokens!.getTokens(node).map(toValue) assert.deepStrictEqual(actual, ["a", "=", "b"]) }) @@ -212,8 +219,9 @@ describe("services.getTemplateBodyTokenStore", () => { describe("ast.templateBody.children[2].startTag.attributes[0].key (VIdentifier)", () => { it("should return the identifier token.", () => { - const node = ast.templateBody.children[2].startTag.attributes[0].key - const actual = tokens.getTokens(node).map(toValue) + const node = (ast!.templateBody!.children[2] as VElement).startTag + .attributes[0].key + const actual = tokens!.getTokens(node).map(toValue) assert.deepStrictEqual(actual, ["a"]) }) @@ -221,9 +229,9 @@ describe("services.getTemplateBodyTokenStore", () => { describe("ast.templateBody.children[2].startTag.attributes[0].value (VAttributeValue)", () => { it("should return the value token.", () => { - const node = - ast.templateBody.children[2].startTag.attributes[0].value - const actual = tokens.getTokens(node).map(toValue) + const node = (ast!.templateBody!.children[2] as VElement).startTag + .attributes[0].value! + const actual = tokens!.getTokens(node).map(toValue) assert.deepStrictEqual(actual, ["b"]) }) @@ -231,8 +239,9 @@ describe("services.getTemplateBodyTokenStore", () => { describe("ast.templateBody.children[2].startTag.attributes[1].key (VDirectiveKey)", () => { it("should return the identifier token.", () => { - const node = ast.templateBody.children[2].startTag.attributes[1].key - const actual = tokens.getTokens(node).map(toValue) + const node = (ast!.templateBody!.children[2] as VElement).startTag + .attributes[1].key + const actual = tokens!.getTokens(node).map(toValue) assert.deepStrictEqual(actual, ["v-show"]) }) @@ -240,9 +249,9 @@ describe("services.getTemplateBodyTokenStore", () => { describe("ast.templateBody.children[2].startTag.attributes[1].value (VExpressionContainer)", () => { it("should return all tokens in the value.", () => { - const node = - ast.templateBody.children[2].startTag.attributes[1].value - const actual = tokens.getTokens(node).map(toValue) + const node = (ast!.templateBody!.children[2] as VElement).startTag + .attributes[1].value! + const actual = tokens!.getTokens(node).map(toValue) assert.deepStrictEqual(actual, [ '"', @@ -260,10 +269,11 @@ describe("services.getTemplateBodyTokenStore", () => { describe("ast.templateBody.children[2].startTag.attributes[1].value.expression (BinaryExpression)", () => { it("should return all tokens in the expression.", () => { - const node = - ast.templateBody.children[2].startTag.attributes[1].value - .expression - const actual = tokens.getTokens(node).map(toValue) + const node = ( + (ast!.templateBody!.children[2] as VElement).startTag + .attributes[1].value as VExpressionContainer + ).expression! + const actual = tokens!.getTokens(node).map(toValue) assert.deepStrictEqual(actual, [ "c", @@ -279,8 +289,8 @@ describe("services.getTemplateBodyTokenStore", () => { describe("ast.templateBody.children[2].endTag (VEndTag)", () => { it("should return all tokens in the tag.", () => { - const node = ast.templateBody.children[2].endTag - const actual = tokens.getTokens(node).map(toValue) + const node = (ast!.templateBody!.children[2] as VElement).endTag! + const actual = tokens!.getTokens(node).map(toValue) assert.deepStrictEqual(actual, ["div", ">"]) }) @@ -289,15 +299,13 @@ describe("services.getTemplateBodyTokenStore", () => { describe("TokenStore#get{Range,Loc}()", () => { it("should return loc and range.", () => { const { - templateBody: { - children: [node], - tokens: [token], - }, - } = ast - assert.equal(typeof tokens.getRange(node)[0], "number") - assert.equal(typeof tokens.getRange(token)[1], "number") - assert.equal(typeof tokens.getLoc(node).start.line, "number") - assert.equal(typeof tokens.getLoc(node).end.column, "number") + children: [node], + tokens: [token], + } = ast!.templateBody! + assert.equal(typeof tokens!.getRange(node)[0], "number") + assert.equal(typeof tokens!.getRange(token)[1], "number") + assert.equal(typeof tokens!.getLoc(node).start.line, "number") + assert.equal(typeof tokens!.getLoc(node).end.column, "number") }) }) }) diff --git a/test/variables-references.js b/test/variables-references.test.ts similarity index 53% rename from test/variables-references.js rename to test/variables-references.test.ts index 4e6a590b..e0a9e765 100644 --- a/test/variables-references.js +++ b/test/variables-references.test.ts @@ -3,14 +3,26 @@ * @copyright 2017 Toru Nagashima. All rights reserved. * See LICENSE file in root directory for full license. */ -"use strict" //------------------------------------------------------------------------------ // Requirements //------------------------------------------------------------------------------ -const assert = require("assert") -const parse = require("../src").parseForESLint +import type { + ESLintBinaryExpression, + ESLintCallExpression, + ESLintExpressionStatement, + ESLintProgram, + Reference, + Variable, + VDirective, + VElement, + VExpressionContainer, + VForExpression, + VOnExpression, +} from "../src/ast" +import { describe, it, assert, beforeAll } from "vitest" +import { parseForESLint as parse } from "../src" //------------------------------------------------------------------------------ // Helpers @@ -31,65 +43,82 @@ const PARSER_OPTIONS = { describe("[references] expression containers", () => { describe("in directives", () => { const code = '' - let ast = null + // @ts-expect-error init in beforeAll + let ast: ESLintProgram = null - before(() => { + beforeAll(() => { ast = parse(code, { filePath: "test.vue", ...PARSER_OPTIONS }).ast }) it("should have references", () => { - const directive = - ast.templateBody.children[0].startTag.attributes[0] + const element = ast.templateBody!.children[0] as VElement + const directive = element.startTag.attributes[0] as VDirective assert(directive.key.type === "VDirectiveKey") - assert(directive.value.references != null) + assert(directive.value!.references != null) assert( - directive.value.references[0].id === - directive.value.expression.left, + directive.value!.references[0].id === + (directive.value!.expression as ESLintBinaryExpression) + .left, ) assert( - directive.value.references[1].id === - directive.value.expression.right, + directive.value!.references[1].id === + (directive.value!.expression as ESLintBinaryExpression) + .right, ) }) }) describe("in text", () => { const code = "" - let ast = null + // @ts-expect-error init in beforeAll + let ast: ESLintProgram = null - before(() => { + beforeAll(() => { ast = parse(code, { filePath: "test.vue", ...PARSER_OPTIONS }).ast }) it("should have references", () => { - const container = ast.templateBody.children[0] + const container = ast.templateBody! + .children[0] as VExpressionContainer assert(container.type === "VExpressionContainer") assert(container.references != null) - assert(container.references[0].id === container.expression.left) - assert(container.references[1].id === container.expression.right) + assert( + container.references[0].id === + (container.expression as ESLintBinaryExpression).left, + ) + assert( + container.references[1].id === + (container.expression as ESLintBinaryExpression).right, + ) }) }) describe("in v-on directive", () => { const code = '' - let ast = null + // @ts-expect-error init in beforeAll + let ast: ESLintProgram = null - before(() => { + beforeAll(() => { ast = parse(code, { filePath: "test.vue", ...PARSER_OPTIONS }).ast }) it("should not include $event references.", () => { - const directive = - ast.templateBody.children[0].startTag.attributes[0] + const element = ast.templateBody!.children[0] as VElement + const directive = element.startTag.attributes[0] as VDirective assert(directive.key.type === "VDirectiveKey") assert(directive.key.name.name === "on") - assert(directive.value.references.length === 1) + assert(directive.value!.references.length === 1) assert( - directive.value.references[0].id === - directive.value.expression.body[0].expression.callee, + directive.value!.references[0].id === + ( + ( + (directive.value!.expression as VOnExpression) + .body[0] as ESLintExpressionStatement + ).expression as ESLintCallExpression + ).callee, ) }) }) @@ -98,68 +127,61 @@ describe("[references] expression containers", () => { describe("[variables] elements", () => { describe("which have v-for directive", () => { const code = '' - let ast = null + // @ts-expect-error init in beforeAll + let ast: ESLintProgram = null - before(() => { + beforeAll(() => { ast = parse(code, { filePath: "test.vue", ...PARSER_OPTIONS }).ast }) it("should have references", () => { - const element = ast.templateBody.children[0] - const directive = element.startTag.attributes[0] + const element = ast.templateBody!.children[0] as VElement + const directive = element.startTag.attributes[0] as VDirective + const vForExpression = directive.value!.expression as VForExpression assert(element.type === "VElement") assert(element.variables.length === 1) - assert( - element.variables[0].id === directive.value.expression.left[0], - ) - assert(directive.value.references.length === 1) - assert( - directive.value.references[0].id === - directive.value.expression.right, - ) + assert(element.variables[0].id === vForExpression.left[0]) + assert(directive.value!.references.length === 1) + assert(directive.value!.references[0].id === vForExpression.right) }) }) describe("which have v-for directive (with index)", () => { const code = '' - let ast = null + // @ts-expect-error init in beforeAll + let ast: ESLintProgram = null - before(() => { + beforeAll(() => { ast = parse(code, { filePath: "test.vue", ...PARSER_OPTIONS }).ast }) it("should have references", () => { - const element = ast.templateBody.children[0] - const directive = element.startTag.attributes[0] + const element = ast.templateBody!.children[0] as VElement + const directive = element.startTag.attributes[0] as VDirective + const vForExpression = directive.value!.expression as VForExpression assert(element.type === "VElement") assert(element.variables.length === 2) - assert( - element.variables[0].id === directive.value.expression.left[0], - ) - assert( - element.variables[1].id === directive.value.expression.left[1], - ) - assert(directive.value.references.length === 1) - assert( - directive.value.references[0].id === - directive.value.expression.right, - ) + assert(element.variables[0].id === vForExpression.left[0]) + assert(element.variables[1].id === vForExpression.left[1]) + assert(directive.value!.references.length === 1) + assert(directive.value!.references[0].id === vForExpression.right) }) }) describe("which have scope attribute", () => { const code = '' - let ast = null + // @ts-expect-error init in beforeAll + let ast: ESLintProgram = null - before(() => { + beforeAll(() => { ast = parse(code, { filePath: "test.vue", ...PARSER_OPTIONS }).ast }) it("should have variables", () => { - const element = ast.templateBody.children[0] - const attribute = element.startTag.attributes[0] + const element = ast.templateBody!.children[0] as VElement + const attribute = element.startTag.attributes[0] as VDirective assert(element.type === "VElement") assert(element.variables.length === 1) @@ -167,10 +189,10 @@ describe("[variables] elements", () => { assert(element.variables[0].id.range[0] === 27) assert(element.variables[0].id.range[1] === 28) assert(element.variables[0].kind === "scope") - assert(attribute.value.type === "VExpressionContainer") - assert(attribute.value.expression.type === "VSlotScopeExpression") - assert(attribute.value.expression.params[0].type === "Identifier") - assert(attribute.value.expression.params[0].name === "a") + assert(attribute.value!.type === "VExpressionContainer") + assert(attribute.value!.expression!.type === "VSlotScopeExpression") + assert(attribute.value!.expression.params[0].type === "Identifier") + assert(attribute.value!.expression.params[0].name === "a") }) }) }) @@ -178,25 +200,38 @@ describe("[variables] elements", () => { describe("Variables of v-for and references", () => { const code = '' - let variables = null - let vForReferences = null - let vBindKeyReferences = null - let mustacheReferences1 = null - let mustacheReferences2 = null - let mustacheReferences3 = null - - before(() => { + // @ts-expect-error init in beforeAll + let variables: Variable[] = null + // @ts-expect-error init in beforeAll + let vForReferences: Reference[] = null + // @ts-expect-error init in beforeAll + let vBindKeyReferences: Reference[] = null + // @ts-expect-error init in beforeAll + let mustacheReferences1: Reference[] = null + // @ts-expect-error init in beforeAll + let mustacheReferences2: Reference[] = null + // @ts-expect-error init in beforeAll + let mustacheReferences3: Reference[] = null + + beforeAll(() => { const ast = parse(code, { filePath: "test.vue", ...PARSER_OPTIONS }).ast - variables = ast.templateBody.children[0].variables - vForReferences = - ast.templateBody.children[0].startTag.attributes[0].value.references - vBindKeyReferences = - ast.templateBody.children[0].startTag.attributes[1].value.references - mustacheReferences1 = - ast.templateBody.children[0].children[0].references - mustacheReferences2 = - ast.templateBody.children[0].children[1].children[0].references - mustacheReferences3 = ast.templateBody.children[1].references + const firstChild = ast.templateBody!.children[0] as VElement + const secondChild = ast.templateBody! + .children[1] as VExpressionContainer + variables = firstChild.variables + vForReferences = ( + firstChild.startTag.attributes[0].value as VExpressionContainer + ).references + vBindKeyReferences = ( + firstChild.startTag.attributes[1].value as VExpressionContainer + ).references + mustacheReferences1 = (firstChild.children[0] as VExpressionContainer) + .references + mustacheReferences2 = ( + (firstChild.children[1] as VElement) + .children[0] as VExpressionContainer + ).references + mustacheReferences3 = secondChild.references }) it("should have relationship each other", () => { @@ -221,14 +256,14 @@ describe("Variables of v-for and references", () => { it("`Variable#references` should be non-enumerable", () => { for (const variable of variables) { assert( - Object.getOwnPropertyDescriptor(variable, "references") + Object.getOwnPropertyDescriptor(variable, "references")! .enumerable === false, ) } }) it("`Reference#variable` should be non-enumerable", () => { - for (const reference of [].concat( + for (const reference of ([] as Reference[]).concat( vForReferences, vBindKeyReferences, mustacheReferences1, @@ -236,7 +271,7 @@ describe("Variables of v-for and references", () => { mustacheReferences3, )) { assert( - Object.getOwnPropertyDescriptor(reference, "variable") + Object.getOwnPropertyDescriptor(reference, "variable")! .enumerable === false, ) } @@ -246,22 +281,33 @@ describe("Variables of v-for and references", () => { describe("Variables of template-scope and references", () => { const code = '' - let variables = null - let vBindKeyReferences = null - let mustacheReferences1 = null - let mustacheReferences2 = null - let mustacheReferences3 = null - - before(() => { + // @ts-expect-error init in beforeAll + let variables: Variable[] = null + // @ts-expect-error init in beforeAll + let vBindKeyReferences: Reference[] = null + // @ts-expect-error init in beforeAll + let mustacheReferences1: Reference[] = null + // @ts-expect-error init in beforeAll + let mustacheReferences2: Reference[] = null + // @ts-expect-error init in beforeAll + let mustacheReferences3: Reference[] = null + + beforeAll(() => { const ast = parse(code, { filePath: "test.vue", ...PARSER_OPTIONS }).ast - variables = ast.templateBody.children[0].variables - vBindKeyReferences = - ast.templateBody.children[0].startTag.attributes[1].value.references - mustacheReferences1 = - ast.templateBody.children[0].children[0].references - mustacheReferences2 = - ast.templateBody.children[0].children[1].children[0].references - mustacheReferences3 = ast.templateBody.children[1].references + const element = ast.templateBody!.children[0] as VElement + const secondElement = ast.templateBody! + .children[1] as VExpressionContainer + + variables = element.variables + vBindKeyReferences = (element.startTag.attributes[1] as VDirective) + .value!.references + mustacheReferences1 = (element.children[0] as VExpressionContainer) + .references + mustacheReferences2 = ( + (element.children[1] as VElement) + .children[0] as VExpressionContainer + ).references + mustacheReferences3 = secondElement.references }) it("should have relationship each other", () => { @@ -284,21 +330,21 @@ describe("Variables of template-scope and references", () => { it("`Variable#references` should be non-enumerable", () => { for (const variable of variables) { assert( - Object.getOwnPropertyDescriptor(variable, "references") + Object.getOwnPropertyDescriptor(variable, "references")! .enumerable === false, ) } }) it("`Reference#variable` should be non-enumerable", () => { - for (const reference of [].concat( + for (const reference of ([] as Reference[]).concat( vBindKeyReferences, mustacheReferences1, mustacheReferences2, mustacheReferences3, )) { assert( - Object.getOwnPropertyDescriptor(reference, "variable") + Object.getOwnPropertyDescriptor(reference, "variable")! .enumerable === false, ) } @@ -307,18 +353,24 @@ describe("Variables of template-scope and references", () => { describe("Variables of v-for and references of dynamic arguments", () => { const code = '' - let variables = null - let vForReferences = null - let vBindKeyReferences = null - - before(() => { + // @ts-expect-error init in beforeAll + let variables: Variable[] = null + // @ts-expect-error init in beforeAll + let vForReferences: Reference[] = null + // @ts-expect-error init in beforeAll + let vBindKeyReferences: Reference[] = null + + beforeAll(() => { const ast = parse(code, { filePath: "test.vue", ...PARSER_OPTIONS }).ast - variables = ast.templateBody.children[0].variables - vForReferences = - ast.templateBody.children[0].startTag.attributes[0].value.references - vBindKeyReferences = - ast.templateBody.children[0].startTag.attributes[1].key.argument - .references + const element = ast.templateBody!.children[0] as VElement + + variables = element.variables + vForReferences = (element.startTag.attributes[0] as VDirective).value! + .references + vBindKeyReferences = ( + (element.startTag.attributes[1] as VDirective).key + .argument as VExpressionContainer + ).references }) it("should have relationship each other", () => { @@ -334,17 +386,22 @@ describe("Variables of v-for and references of dynamic arguments", () => { describe("Variables of v-for and references of v-bind same-name shorthand", () => { const code = '' - let variables = null - let vForReferences = null - let vBindReferences = null - - before(() => { + // @ts-expect-error init in beforeAll + let variables: Variable[] = null + // @ts-expect-error init in beforeAll + let vForReferences: Reference[] = null + // @ts-expect-error init in beforeAll + let vBindReferences: Reference[] = null + + beforeAll(() => { const ast = parse(code, { filePath: "test.vue", ...PARSER_OPTIONS }).ast - variables = ast.templateBody.children[0].variables - vForReferences = - ast.templateBody.children[0].startTag.attributes[0].value.references - vBindReferences = - ast.templateBody.children[0].startTag.attributes[1].value.references + const element = ast.templateBody!.children[0] as VElement + + variables = element.variables + vForReferences = (element.startTag.attributes[0] as VDirective).value! + .references + vBindReferences = (element.startTag.attributes[1] as VDirective).value! + .references }) it("should have relationship each other", () => { @@ -360,17 +417,22 @@ describe("Variables of v-for and references of v-bind same-name shorthand", () = describe("Variables of v-for and references of v-bind same-name shorthand with kebab-case", () => { const code = '' - let variables = null - let vForReferences = null - let vBindReferences = null - - before(() => { + // @ts-expect-error init in beforeAll + let variables: Variable[] = null + // @ts-expect-error init in beforeAll + let vForReferences: Reference[] = null + // @ts-expect-error init in beforeAll + let vBindReferences: Reference[] = null + + beforeAll(() => { const ast = parse(code, { filePath: "test.vue", ...PARSER_OPTIONS }).ast - variables = ast.templateBody.children[0].variables - vForReferences = - ast.templateBody.children[0].startTag.attributes[0].value.references - vBindReferences = - ast.templateBody.children[0].startTag.attributes[1].value.references + const element = ast.templateBody!.children[0] as VElement + + variables = element.variables + vForReferences = (element.startTag.attributes[0] as VDirective).value! + .references + vBindReferences = (element.startTag.attributes[1] as VDirective).value! + .references }) it("should have relationship each other", () => { diff --git a/tsconfig.json b/tsconfig.json index c5dc0e4c..677d8fec 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -37,5 +37,6 @@ "skipLibCheck": true }, - "include": ["src/**/*.ts"] + "include": ["src/**/*.ts"], + "references": [{ "path": "./tsconfig.test.json" }] } diff --git a/tsconfig.test.json b/tsconfig.test.json new file mode 100644 index 00000000..a5e080f7 --- /dev/null +++ b/tsconfig.test.json @@ -0,0 +1,12 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "composite": true, + "paths": { + "*": ["typings/*"] + }, + "module": "esnext", + "moduleResolution": "Bundler" + }, + "include": ["test/**/*.ts", "vitest.config.ts"] +} diff --git a/typings/eslint-scope/index.d.ts b/typings/eslint-scope/index.d.ts index 6d14afcf..1469f501 100644 --- a/typings/eslint-scope/index.d.ts +++ b/typings/eslint-scope/index.d.ts @@ -14,7 +14,7 @@ export interface AnalysisOptions { impliedStrict?: boolean fallback?: string | Function sourceType?: "script" | "module" - ecmaVersion?: number + ecmaVersion?: number | "latest" childVisitorKeys?: VisitorKeys } diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 00000000..9df42d5d --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from "vitest/config" + +export default defineConfig({ + test: { + reporters: "dot", + include: ["test/**/*.test.ts"], + testTimeout: 60000, + coverage: { + include: ["src/**/*.ts"], + exclude: ["src/external/**/*.ts"], + reporter: ["html", "lcov", "text-summary"], + }, + }, +}) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy