From bab4bc8f617e602736b62b298f133ebd18c36915 Mon Sep 17 00:00:00 2001 From: camc314 <18101008+camc314@users.noreply.github.com> Date: Mon, 24 Nov 2025 22:18:13 +0000 Subject: [PATCH] feat(napi/parser): add type annotations to parse-raw-worker test (#15998) --- napi/parser/test/parse-raw-worker.ts | 126 +++++++++++++++++++-------- 1 file changed, 88 insertions(+), 38 deletions(-) diff --git a/napi/parser/test/parse-raw-worker.ts b/napi/parser/test/parse-raw-worker.ts index c6f3b5033a962..65173a4205dd6 100644 --- a/napi/parser/test/parse-raw-worker.ts +++ b/napi/parser/test/parse-raw-worker.ts @@ -22,13 +22,22 @@ import { TS_ESTREE_DIR_PATH, } from './parse-raw-common.ts'; import { makeUnitsFromTest } from './typescript-make-units-from-test.ts'; +import type { Node, ParserOptions } from '../src-js/index.js'; const { hasOwn } = Object, { isArray } = Array; +interface ExtendedParserOptions extends ParserOptions { + experimentalRawTransfer?: boolean; + experimentalParent?: boolean; + experimentalLazy?: boolean; +} + +type TestCaseProps = string | { filename: string; sourceText: string }; + // Run test case and return whether it passes. // This is the entry point when run as a worker. -export default async function (data) { +export default async function (data: { type: number; props: TestCaseProps }): Promise { try { await runCase(data, simpleExpect); return true; @@ -40,7 +49,10 @@ export default async function (data) { // Run test case with specified `expect` implementation. // If test fails, will throw an error. // Can be called from main thread. -export async function runCase({ type, props }, expect) { +export async function runCase( + { type, props }: { type: number; props: TestCaseProps }, + expect: ExpectFunction, +): Promise { const rangeParent = (type & TEST_TYPE_RANGE_PARENT) !== 0, lazy = (type & TEST_TYPE_LAZY) !== 0, pretty = (type & TEST_TYPE_PRETTY) !== 0; @@ -48,19 +60,19 @@ export async function runCase({ type, props }, expect) { switch (type) { case TEST_TYPE_TEST262: - await runTest262Case(props, rangeParent, lazy, expect); + await runTest262Case(props as string, rangeParent, lazy, expect); break; case TEST_TYPE_JSX: - await runJsxCase(props, rangeParent, lazy, expect); + await runJsxCase(props as string, rangeParent, lazy, expect); break; case TEST_TYPE_TS: - await runTsCase(props, rangeParent, lazy, expect); + await runTsCase(props as string, rangeParent, lazy, expect); break; case TEST_TYPE_FIXTURE: - await runFixture(props, rangeParent, lazy, pretty, expect); + await runFixture(props as string, rangeParent, lazy, pretty, expect); break; case TEST_TYPE_INLINE_FIXTURE: - await runInlineFixture(props, rangeParent, lazy, pretty, expect); + await runInlineFixture(props as { filename: string; sourceText: string }, rangeParent, lazy, pretty, expect); break; default: throw new Error('Unexpected test type'); @@ -68,7 +80,12 @@ export async function runCase({ type, props }, expect) { } // Run Test262 test case -async function runTest262Case(path, rangeParent, lazy, expect) { +async function runTest262Case( + path: string, + rangeParent: boolean, + lazy: boolean, + expect: ExpectFunction, +): Promise { const filename = basename(path); const [sourceText, acornJson] = await Promise.all([ readFile(pathJoin(TEST262_DIR_PATH, path), 'utf8'), @@ -93,7 +110,12 @@ async function runTest262Case(path, rangeParent, lazy, expect) { } // Run JSX test case -async function runJsxCase(filename, rangeParent, lazy, expect) { +async function runJsxCase( + filename: string, + rangeParent: boolean, + lazy: boolean, + expect: ExpectFunction, +): Promise { const sourcePath = pathJoin(JSX_DIR_PATH, filename), jsonPath = sourcePath.slice(0, -1) + 'on'; // `.jsx` -> `.json` const [sourceText, acornJson] = await Promise.all([readFile(sourcePath, 'utf8'), readFile(jsonPath, 'utf8')]); @@ -120,7 +142,7 @@ const TS_CASE_HEADER = '__ESTREE_TEST__:PASS:\n```json\n'; const TS_CASE_FOOTER = '\n```\n'; const TS_CASE_FOOTER_LEN = TS_CASE_FOOTER.length; -async function runTsCase(path, rangeParent, lazy, expect) { +async function runTsCase(path: string, rangeParent: boolean, lazy: boolean, expect: ExpectFunction): Promise { const tsPath = path.slice(0, -3); // Trim off `.md` let [sourceText, casesJson] = await Promise.all([ readFile(pathJoin(TS_DIR_PATH, tsPath), 'utf8'), @@ -140,7 +162,7 @@ async function runTsCase(path, rangeParent, lazy, expect) { for (let i = 0; i < tests.length; i++) { const { name: filename, content: code, sourceType } = tests[i]; - const options = { + const options: ExtendedParserOptions = { sourceType: sourceType.module ? 'module' : 'unambiguous', astType: 'ts', preserveParens: false, @@ -156,8 +178,7 @@ async function runTsCase(path, rangeParent, lazy, expect) { continue; } - // @ts-ignore - const { program, errors } = parseSync(filename, code, options); + const { program, errors } = parseSync(filename, code, options as ParserOptions); const oxcJson = stringifyAcornTest262Style(program); const estreeJson = estreeJsons[i]; @@ -168,8 +189,7 @@ async function runTsCase(path, rangeParent, lazy, expect) { // Fall back to comparing to AST parsed via JSON transfer. // We can fail to match the TS-ESLint snapshots where there are syntax errors, // because our parser is not recoverable. - // @ts-ignore - const standard = parseSync(filename, code, { ...options, experimentalRawTransfer: false }); + const standard = parseSync(filename, code, { ...options, experimentalRawTransfer: false } as ParserOptions); const standardJson = stringifyAcornTest262Style(standard.program); const errorsStandard = standard.errors; @@ -183,7 +203,13 @@ async function runTsCase(path, rangeParent, lazy, expect) { } // Test raw transfer output matches standard (via JSON) output for a fixture file -async function runFixture(path, rangeParent, lazy, pretty, expect) { +async function runFixture( + path: string, + rangeParent: boolean, + lazy: boolean, + pretty: boolean, + expect: ExpectFunction, +): Promise { const filename = basename(path); const sourceText = await readFile(pathJoin(ROOT_DIR_PATH, path), 'utf8'); @@ -197,7 +223,13 @@ async function runFixture(path, rangeParent, lazy, pretty, expect) { } // Test raw transfer output matches standard (via JSON) output for a fixture, with provided source text -async function runInlineFixture({ filename, sourceText }, rangeParent, lazy, pretty, expect) { +async function runInlineFixture( + { filename, sourceText }: { filename: string; sourceText: string }, + rangeParent: boolean, + lazy: boolean, + pretty: boolean, + expect: ExpectFunction, +): Promise { if (rangeParent) { testRangeParent(filename, sourceText, null, expect); } else if (lazy) { @@ -208,17 +240,21 @@ async function runInlineFixture({ filename, sourceText }, rangeParent, lazy, pre } // Test `range` and `parent` fields are correct on all AST nodes. -function testRangeParent(filename, sourceText, options, expect) { - // @ts-ignore +function testRangeParent( + filename: string, + sourceText: string, + options: ExtendedParserOptions | null, + expect: ExpectFunction, +): void { const ret = parseSync(filename, sourceText, { ...options, range: true, experimentalRawTransfer: true, experimentalParent: true, - }); + } as ParserOptions); - let parent = null; - function walk(node) { + let parent: any = null; + function walk(node: null | Node[] | Node): void { if (node === null || typeof node !== 'object') return; if (isArray(node)) { @@ -260,13 +296,12 @@ function testRangeParent(filename, sourceText, options, expect) { // Test lazy deserialization does not throw an error. // We don't test the correctness of the output. -function testLazy(filename, sourceText, options) { - // @ts-ignore +function testLazy(filename: string, sourceText: string, options: ExtendedParserOptions | null): void { const ret = parseSync(filename, sourceText, { ...options, experimentalRawTransfer: false, experimentalLazy: true, - }); + } as ParserOptions); JSON.stringify(ret.program); JSON.stringify(ret.comments); JSON.stringify(ret.errors); @@ -274,7 +309,12 @@ function testLazy(filename, sourceText, options) { } // Assert raw transfer output matches standard (via JSON) output -function assertRawAndStandardMatch(filename, sourceText, pretty, expect) { +function assertRawAndStandardMatch( + filename: string, + sourceText: string, + pretty: boolean, + expect: ExpectFunction, +): void { const retStandard = parseSync(filename, sourceText); const { program: programStandard, @@ -290,8 +330,7 @@ function assertRawAndStandardMatch(filename, sourceText, pretty, expect) { moveStartAndEndToLast(moduleStandard.staticExports, true); moveStartAndEndToLast(moduleStandard.dynamicImports, false); - // @ts-ignore - const retRaw = parseSync(filename, sourceText, { experimentalRawTransfer: true }); + const retRaw = parseSync(filename, sourceText, { experimentalRawTransfer: true } as ParserOptions); const { program: programRaw, comments: commentsRaw } = retRaw; // Remove `null` values, to match what NAPI-RS does const moduleRaw = removeNullProperties(retRaw.module); @@ -309,14 +348,17 @@ function assertRawAndStandardMatch(filename, sourceText, pretty, expect) { expect(jsonRaw).toEqual(jsonStandard); } -function moveStartAndEndToLast(arr, reorderEntries) { +function moveStartAndEndToLast( + arr: T[], + reorderEntries: boolean, +): void { for (const obj of arr) { const { start, end } = obj; delete obj.start; delete obj.end; obj.start = start; obj.end = end; - if (reorderEntries) moveStartAndEndToLast(obj.entries, false); + if (reorderEntries && obj.entries) moveStartAndEndToLast(obj.entries, false); } } @@ -329,14 +371,14 @@ function moveStartAndEndToLast(arr, reorderEntries) { // } // ``` // For speed, extract `sourceType` with a slice, rather than parsing the JSON. -function getSourceTypeFromJSON(json) { +function getSourceTypeFromJSON(json: string): 'script' | 'module' { const index = json.lastIndexOf('"sourceType": "'); - return json.slice(index + 15, index + 21); + return json.slice(index + 15, index + 21) as 'script' | 'module'; } // Stringify to JSON, replacing values which are invalid in JSON. // If `pretty === true`, JSON is pretty-printed. -function stringify(obj, pretty) { +function stringify(obj: any, pretty: boolean): string { return JSON.stringify( obj, (_key, value) => { @@ -354,7 +396,7 @@ function stringify(obj, pretty) { const INFINITY_PLACEHOLDER = '__INFINITY__INFINITY__INFINITY__'; const INFINITY_REGEXP = new RegExp(`"${INFINITY_PLACEHOLDER}"`, 'g'); -function stringifyAcornTest262Style(obj) { +function stringifyAcornTest262Style(obj: any): string { let containsInfinity = false; const json = JSON.stringify( obj, @@ -373,16 +415,24 @@ function stringifyAcornTest262Style(obj) { } // Remove `null` values, to match what NAPI-RS does -function removeNullProperties(obj) { +function removeNullProperties(obj: any): any { return JSON.parse(JSON.stringify(obj, (_key, value) => (value === null ? undefined : value))); } +// Type for expect function +interface ExpectFunction { + (value: any): { + toEqual: (expected: any) => void; + toBe: (expected: any) => void; + }; +} + // Very simple `expect` implementation. // Only supports `expect(x).toEqual(y)` and `expect(x).toBe(y)`, and both use only a simple `===` comparison. // Therefore, only works for primitive values e.g. strings. -function simpleExpect(value) { - const toBe = (expected) => { +const simpleExpect: ExpectFunction = (value: any) => { + const toBe = (expected: any): void => { if (value !== expected) throw new Error('Mismatch'); }; return { toEqual: toBe, toBe }; -} +};