diff --git a/package.json b/package.json index 20b013dab04d..a51cd0073937 100644 --- a/package.json +++ b/package.json @@ -98,7 +98,6 @@ "markdownlint-cli": "^0.44.0", "nx": "20.7.2", "prettier": "3.5.0", - "pretty-format": "^29.7.0", "rimraf": "^5.0.5", "semver": "7.7.0", "tsx": "*", @@ -116,7 +115,6 @@ "@types/react": "^18.2.14", "eslint-plugin-eslint-plugin@^5.5.0": "patch:eslint-plugin-eslint-plugin@npm%3A5.5.1#./.yarn/patches/eslint-plugin-eslint-plugin-npm-5.5.1-4206c2506d.patch", "prettier": "3.5.0", - "pretty-format": "^29", "react-split-pane@^0.1.92": "patch:react-split-pane@npm%3A0.1.92#./.yarn/patches/react-split-pane-npm-0.1.92-93dbf51dff.patch", "tmp": "0.2.1", "tsx": "^4.7.2", diff --git a/packages/parser/package.json b/packages/parser/package.json index 6fb66eac1ae8..ca32c3eb4d7e 100644 --- a/packages/parser/package.json +++ b/packages/parser/package.json @@ -40,8 +40,7 @@ ], "scripts": { "build": "tsc -b tsconfig.build.json", - "clean": "tsc -b tsconfig.build.json --clean", - "postclean": "rimraf dist/ coverage/", + "clean": "rimraf dist/ coverage/", "format": "prettier --write \"./**/*.{ts,mts,cts,tsx,js,mjs,cjs,jsx,json,md,css}\" --ignore-path ../../.prettierignore", "lint": "npx nx lint", "test": "vitest --run --config=$INIT_CWD/vitest.config.mts", diff --git a/packages/parser/tests/lib/parser.test.ts b/packages/parser/tests/lib/parser.test.ts index 39c072a8d42e..a36d76c11edd 100644 --- a/packages/parser/tests/lib/parser.test.ts +++ b/packages/parser/tests/lib/parser.test.ts @@ -2,24 +2,30 @@ import type { ParserOptions } from '@typescript-eslint/types'; import * as scopeManager from '@typescript-eslint/scope-manager'; import * as typescriptESTree from '@typescript-eslint/typescript-estree'; -import path from 'node:path'; import { ScriptTarget } from 'typescript'; -import { parse, parseForESLint } from '../../src/parser'; +import { parse, parseForESLint } from '../../src/parser.js'; +import { FIXTURES_DIR } from '../test-utils/test-utils.js'; describe('parser', () => { - beforeEach(() => { + afterEach(() => { vi.clearAllMocks(); }); + afterAll(() => { + vi.restoreAllMocks(); + }); + it('parse() should return just the AST from parseForESLint()', () => { const code = 'const valid = true;'; - expect(parse(code)).toEqual(parseForESLint(code).ast); + expect(parse(code)).toStrictEqual(parseForESLint(code).ast); }); it('parseForESLint() should work if options are `null`', () => { const code = 'const valid = true;'; - expect(() => parseForESLint(code, null)).not.toThrow(); + expect(() => { + parseForESLint(code, null); + }).not.toThrow(); }); it('parseAndGenerateServices() should be called with options', () => { @@ -36,7 +42,7 @@ describe('parser', () => { extraFileExtensions: ['.foo'], filePath: './isolated-file.src.ts', project: 'tsconfig.json', - tsconfigRootDir: path.join(__dirname, '..', 'fixtures', 'services'), + tsconfigRootDir: FIXTURES_DIR, }; parseForESLint(code, config); expect(spy).toHaveBeenCalledExactlyOnceWith(code, { @@ -107,7 +113,7 @@ describe('parser', () => { errorOnTypeScriptSyntacticAndSemanticIssues: false, filePath: 'isolated-file.src.ts', project: 'tsconfig.json', - tsconfigRootDir: path.join(__dirname, '..', 'fixtures', 'services'), + tsconfigRootDir: FIXTURES_DIR, }; parseForESLint(code, config); @@ -141,7 +147,7 @@ describe('parser', () => { const config: ParserOptions = { filePath: 'isolated-file.src.ts', project: 'tsconfig.json', - tsconfigRootDir: path.join(__dirname, '..', 'fixtures', 'services'), + tsconfigRootDir: FIXTURES_DIR, }; vi.spyOn( @@ -185,7 +191,7 @@ describe('parser', () => { extraFileExtensions: ['.foo'], filePath: 'isolated-file.src.ts', project: 'tsconfig.json', - tsconfigRootDir: path.join(__dirname, '..', 'fixtures', 'services'), + tsconfigRootDir: FIXTURES_DIR, }; parseForESLint(code, config); diff --git a/packages/parser/tests/lib/services.test.ts b/packages/parser/tests/lib/services.test.ts index 3719c7127e82..5db0a81f44fb 100644 --- a/packages/parser/tests/lib/services.test.ts +++ b/packages/parser/tests/lib/services.test.ts @@ -1,57 +1,58 @@ import { createProgram } from '@typescript-eslint/typescript-estree'; import * as glob from 'glob'; -import fs from 'node:fs/promises'; -import path from 'node:path'; - -import type { ParserOptions } from '../../src/parser'; +import * as fs from 'node:fs/promises'; +import * as path from 'node:path'; +import { parseForESLint } from '../../src/index.js'; import { - createSnapshotTestBlock, - formatSnapshotName, - testServices, -} from '../test-utils/test-utils'; + createConfig, + FIXTURES_DIR, + getRaw, +} from '../test-utils/test-utils.js'; //------------------------------------------------------------------------------ // Setup //------------------------------------------------------------------------------ -const FIXTURES_DIR = path.join( - __dirname, - '..', - '..', - 'tests', - 'fixtures', - 'services', -); -const testFiles = glob.sync(`**/*.src.ts`, { +const testFiles = glob.sync('**/*.src.ts', { + absolute: true, cwd: FIXTURES_DIR, }); -function createConfig(filename: string): ParserOptions { - return { - filePath: filename, - project: './tsconfig.json', - tsconfigRootDir: path.resolve(FIXTURES_DIR), - }; -} - //------------------------------------------------------------------------------ // Tests //------------------------------------------------------------------------------ -const program = createProgram(path.resolve(FIXTURES_DIR, 'tsconfig.json')); +const program = createProgram(path.join(FIXTURES_DIR, 'tsconfig.json')); describe.for(testFiles)('services', async filename => { - const code = await fs.readFile(path.join(FIXTURES_DIR, filename), { + const code = await fs.readFile(filename, { encoding: 'utf-8', }); - const config = createConfig(filename); - const snapshotName = formatSnapshotName(filename, FIXTURES_DIR, '.ts'); - it(snapshotName, createSnapshotTestBlock(code, config)); + + const { base, name } = path.parse(filename); + + const config = createConfig(base); + + const snapshotName = path.posix.join('fixtures', name); + + it(snapshotName, () => { + const { ast } = parseForESLint(code, config); + + const result = getRaw(ast); + + expect(result).toMatchSnapshot(); + }); + it(`${snapshotName} services`, () => { - testServices(code, config); + const { services } = parseForESLint(code, config); + + assert.isNotNull(services.program); }); + it(`${snapshotName} services with provided program`, () => { - testServices(code, { ...config, program }); + const { services } = parseForESLint(code, { ...config, program }); + + assert.isNotNull(services.program); }); }); diff --git a/packages/parser/tests/lib/tsx.test.ts b/packages/parser/tests/lib/tsx.test.ts index f58a859f41f7..ad7d5c8fd7f3 100644 --- a/packages/parser/tests/lib/tsx.test.ts +++ b/packages/parser/tests/lib/tsx.test.ts @@ -1,5 +1,5 @@ -import { parseForESLint } from '../../src/parser'; -import { serializer } from '../test-utils/ts-error-serializer'; +import { parseForESLint } from '../../src/index.js'; +import { serializer } from '../test-utils/ts-error-serializer.js'; //------------------------------------------------------------------------------ // Tests @@ -12,7 +12,9 @@ describe('TSX', () => { it('filePath was not provided', () => { const code = 'const element = '; - expect(() => parseForESLint(code)).toThrowErrorMatchingInlineSnapshot(` + expect(() => { + parseForESLint(code); + }).toThrowErrorMatchingInlineSnapshot(` TSError { "column": 18, "index": 18, @@ -24,22 +26,22 @@ describe('TSX', () => { it("filePath was not provided and 'jsx:true' option", () => { const code = 'const element = '; - expect(() => + expect(() => { parseForESLint(code, { ecmaFeatures: { jsx: true, }, - }), - ).not.toThrow(); + }); + }).not.toThrow(); }); it('test.ts', () => { const code = 'const element = '; - expect(() => + expect(() => { parseForESLint(code, { filePath: 'test.ts', - }), - ).toThrowErrorMatchingInlineSnapshot(` + }); + }).toThrowErrorMatchingInlineSnapshot(` TSError { "column": 18, "index": 18, @@ -52,14 +54,14 @@ describe('TSX', () => { it("test.ts with 'jsx:true' option", () => { const code = 'const element = '; - expect(() => + expect(() => { parseForESLint(code, { ecmaFeatures: { jsx: true, }, filePath: 'test.ts', - }), - ).toThrowErrorMatchingInlineSnapshot(` + }); + }).toThrowErrorMatchingInlineSnapshot(` TSError { "column": 18, "index": 18, @@ -71,23 +73,23 @@ describe('TSX', () => { it('test.tsx', () => { const code = 'const element = '; - expect(() => + expect(() => { parseForESLint(code, { filePath: 'test.tsx', - }), - ).not.toThrow(); + }); + }).not.toThrow(); }); it("test.tsx with 'jsx:false' option", () => { const code = 'const element = '; - expect(() => + expect(() => { parseForESLint(code, { ecmaFeatures: { jsx: false, }, filePath: 'test.tsx', - }), - ).not.toThrow(); + }); + }).not.toThrow(); }); }); }); diff --git a/packages/parser/tests/test-utils/test-utils.ts b/packages/parser/tests/test-utils/test-utils.ts index a286f9df4a86..1177c5923236 100644 --- a/packages/parser/tests/test-utils/test-utils.ts +++ b/packages/parser/tests/test-utils/test-utils.ts @@ -1,25 +1,35 @@ +import type { ParserOptions } from '@typescript-eslint/types'; import type { TSESTree } from '@typescript-eslint/typescript-estree'; -import type { ParserOptions } from '../../src/parser'; +import * as path from 'node:path'; -import * as parser from '../../src/parser'; +export const FIXTURES_DIR = path.join(__dirname, '..', 'fixtures', 'services'); -const defaultConfig = { +const DEFAULT_PARSER_OPTIONS = { comment: true, errorOnUnknownASTType: true, loc: true, range: true, raw: true, - sourceType: 'module' as const, + sourceType: 'module', tokens: true, -}; +} as const satisfies ParserOptions; + +export function createConfig(filename: string): ParserOptions { + return { + ...DEFAULT_PARSER_OPTIONS, + filePath: filename, + project: './tsconfig.json', + tsconfigRootDir: FIXTURES_DIR, + }; +} /** * Returns a raw copy of the given AST * @param ast the AST object * @returns copy of the AST object */ -function getRaw(ast: TSESTree.Program): TSESTree.Program { +export function getRaw(ast: TSESTree.Program): TSESTree.Program { return JSON.parse( JSON.stringify(ast, (key, value) => { if ((key === 'start' || key === 'end') && typeof value === 'number') { @@ -29,65 +39,3 @@ function getRaw(ast: TSESTree.Program): TSESTree.Program { }), ); } - -/** - * Returns a function which can be used as the callback of a Jest test() block, - * and which performs an assertion on the snapshot for the given code and config. - * @param code The source code to parse - * @param config the parser configuration - * @returns callback for Jest test() block - */ -export function createSnapshotTestBlock( - code: string, - config: ParserOptions = {}, -): () => void { - config = { ...defaultConfig, ...config }; - - /** - * @returns the AST object - */ - function parse(): TSESTree.Program { - const ast = parser.parseForESLint(code, config).ast; - return getRaw(ast); - } - - return (): void => { - try { - const result = parse(); - expect(result).toMatchSnapshot(); - } catch (error) { - /** - * If we are deliberately throwing because of encountering an unknown - * AST_NODE_TYPE, we rethrow to cause the test to fail - */ - if ((error as Error).message.includes('Unknown AST_NODE_TYPE')) { - throw error; - } - expect(parse).toThrowErrorMatchingSnapshot(); - } - }; -} - -/** - * @param code The code being parsed - * @param config The configuration object for the parser - */ -export function testServices(code: string, config: ParserOptions = {}): void { - config = { ...defaultConfig, ...config }; - - const services = parser.parseForESLint(code, config).services; - expect(services).toBeDefined(); - expect(services.program).toBeDefined(); - expect(services.esTreeNodeToTSNodeMap).toBeDefined(); - expect(services.tsNodeToESTreeNodeMap).toBeDefined(); -} - -export function formatSnapshotName( - filename: string, - fixturesDir: string, - fileExtension = '.js', -): string { - return `fixtures/${filename - .replace(`${fixturesDir}/`, '') - .replace(fileExtension, '')}`; -} diff --git a/packages/parser/tests/test-utils/ts-error-serializer.ts b/packages/parser/tests/test-utils/ts-error-serializer.ts index 9c9acca97eee..98eb146ce27d 100644 --- a/packages/parser/tests/test-utils/ts-error-serializer.ts +++ b/packages/parser/tests/test-utils/ts-error-serializer.ts @@ -1,8 +1,8 @@ -import type { Plugin } from 'pretty-format'; +import type { SnapshotSerializer } from 'vitest'; import { TSError } from '@typescript-eslint/typescript-estree'; -export const serializer: Plugin = { +export const serializer: SnapshotSerializer = { serialize(val: TSError, config, indentation, depth, refs, printer) { const format = (value: unknown): string => printer(value, config, indentation, depth + 1, refs); diff --git a/yarn.lock b/yarn.lock index c51e1648780d..89d54984e2db 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5739,7 +5739,6 @@ __metadata: markdownlint-cli: ^0.44.0 nx: 20.7.2 prettier: 3.5.0 - pretty-format: ^29.7.0 rimraf: ^5.0.5 semver: 7.7.0 tsx: "*" @@ -16047,7 +16046,7 @@ __metadata: languageName: node linkType: hard -"pretty-format@npm:^29": +"pretty-format@npm:^29.7.0": version: 29.7.0 resolution: "pretty-format@npm:29.7.0" dependencies: