diff --git a/packages/edge-bundler/node/utils/import_attributes.test.ts b/packages/edge-bundler/node/utils/import_attributes.test.ts index 7f98abc346..40e0ae6958 100644 --- a/packages/edge-bundler/node/utils/import_attributes.test.ts +++ b/packages/edge-bundler/node/utils/import_attributes.test.ts @@ -153,6 +153,30 @@ import data2 from './data.json' with { type: 'json' }; expect(result).toEqual(expectedResult) }) + test('handles TsTypeAssertion despite no support in acorn-walk', () => { + // edge case, because " inputs" is valid JSX syntax (a component with name "Params" and children "inputs"), + // but also valid TypeScript syntax (type assertion). In this case, the acorn-jsx parser will parse it as JSX, + // but then throw an error when it encounters the "assert" keyword in the import assertion. + // We want to make sure we can handle this case and still rewrite the import assertions correctly. + const source = ` +import data3 from './data.json' assert { type: 'json' }; +const params = inputs; +const [,foo]=[1,2] +import data2 from './data.json' assert { type: 'json' }; +` + const expectedResult = ` +import data3 from './data.json' with { type: 'json' }; +const params = inputs; +const [,foo]=[1,2] +import data2 from './data.json' with { type: 'json' }; +` + + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call + const result = rewriteSourceImportAssertions(source) + + expect(result).toEqual(expectedResult) + }) + test('complex JSX import assertion case', () => { const source = `<> import('./foo.json', { assert: { type: 'json' } })} />` const expectedResult = `<> import('./foo.json', { with: { type: 'json' } })} />` diff --git a/packages/edge-bundler/node/utils/import_attributes.ts b/packages/edge-bundler/node/utils/import_attributes.ts index 46fb82ed4a..3c536d6afc 100644 --- a/packages/edge-bundler/node/utils/import_attributes.ts +++ b/packages/edge-bundler/node/utils/import_attributes.ts @@ -1,8 +1,35 @@ import { Parser, Node } from 'acorn' -import type { ExportAllDeclaration, ExportNamedDeclaration, ImportDeclaration, ImportExpression } from 'acorn' +import type { + ExportAllDeclaration, + ExportNamedDeclaration, + ImportDeclaration, + ImportExpression, + Options as AcornOptions, + Program, +} from 'acorn' import { tsPlugin } from '@sveltejs/acorn-typescript' -const acorn = Parser.extend(tsPlugin({ jsx: true })) +const acornNoJSX = Parser.extend(tsPlugin({ jsx: false })) +const acornJSX = Parser.extend(tsPlugin({ jsx: true })) + +const parseOptions: AcornOptions = { + ecmaVersion: 'latest', + sourceType: 'module', + locations: true, +} + +const parseAST = (source: string): Program => { + try { + return acornJSX.parse(source, parseOptions) + } catch (error) { + // for non-jsx typescript casting to type via " value" (normally done with "value as type") will throw an "Unexpected token" error in acorn-jsx, + // but is valid syntax in TypeScript. In this case, we can retry parsing with the non-jsx parser. + if (error instanceof SyntaxError) { + return acornNoJSX.parse(source, parseOptions) + } + throw error + } +} /** * Given source code rewrites import assert into import with @@ -15,11 +42,7 @@ export function rewriteSourceImportAssertions(source: string): string { let modified = source try { - const parsedAST = acorn.parse(source, { - ecmaVersion: 'latest', - sourceType: 'module', - locations: true, - }) + const parsedAST = parseAST(source) const statements = collectImportAssertions(source, parsedAST.body)