diff --git a/fixtures/imports-constructor-call/file1.js b/fixtures/imports-constructor-call/file1.js new file mode 100644 index 000000000..e69de29bb diff --git a/fixtures/imports-constructor-call/file2.js b/fixtures/imports-constructor-call/file2.js new file mode 100644 index 000000000..e69de29bb diff --git a/fixtures/imports-constructor-call/index.ts b/fixtures/imports-constructor-call/index.ts new file mode 100644 index 000000000..b8c8368f1 --- /dev/null +++ b/fixtures/imports-constructor-call/index.ts @@ -0,0 +1,15 @@ +// import.meta.url: URL in browsers, file: protocol in Node.js etc. + +// new URL('./exists.js'); +new URL('https://example.org/url1.js'); +new URL('file1.js', import.meta.url); +new URL('./file2.js', import.meta.url); +new URL('file3.js', 'https://example.org'); +const baseUrl = 'https://example.org'; +new URL('/', baseUrl); +new URL(baseUrl); + +// TODO Implement? +new Worker('worker1.js'); +new Worker(new URL('worker2.js', import.meta.url)); +new Worker('./worker3.js'); diff --git a/fixtures/imports-constructor-call/package.json b/fixtures/imports-constructor-call/package.json new file mode 100644 index 000000000..a6e29364e --- /dev/null +++ b/fixtures/imports-constructor-call/package.json @@ -0,0 +1,3 @@ +{ + "name": "imports-constructor-call" +} diff --git a/fixtures/imports-constructor-call/worker2.js b/fixtures/imports-constructor-call/worker2.js new file mode 100644 index 000000000..e69de29bb diff --git a/src/typescript/ast-helpers.ts b/src/typescript/ast-helpers.ts index b8b8b8db0..7c3b7b18b 100644 --- a/src/typescript/ast-helpers.ts +++ b/src/typescript/ast-helpers.ts @@ -50,6 +50,10 @@ export function isPropertyAccessCall(node: ts.Node, identifier: string): node is ); } +export function isConstructorCall(node: ts.Node, identifier: string): node is ts.CallExpression { + return ts.isNewExpression(node) && node.expression.getText() === identifier; +} + export function getAccessExpressionName(node: ts.PropertyAccessExpression | ts.ElementAccessExpression) { return 'argumentExpression' in node ? stripQuotes(node.argumentExpression.getText()) : node.name.getText(); } diff --git a/src/typescript/visitors/imports/constructorCall.ts b/src/typescript/visitors/imports/constructorCall.ts new file mode 100644 index 000000000..c741598b6 --- /dev/null +++ b/src/typescript/visitors/imports/constructorCall.ts @@ -0,0 +1,21 @@ +import ts from 'typescript'; +import { isConstructorCall } from '../../ast-helpers.js'; +import { importVisitor as visit } from '../index.js'; + +export default visit( + () => true, + node => { + // Pattern: new URL('specifier', import.meta.url) + if (isConstructorCall(node, 'URL')) { + if ( + node.arguments.length === 2 && + ts.isStringLiteralLike(node.arguments[0]) && + ts.isPropertyAccessExpression(node.arguments[1]) && + node.arguments[1].getText() === 'import.meta.url' + ) { + const specifier = node.arguments[0].text; + if (specifier) return { specifier }; + } + } + } +); diff --git a/src/typescript/visitors/imports/index.ts b/src/typescript/visitors/imports/index.ts index a2c295de7..59adf693b 100644 --- a/src/typescript/visitors/imports/index.ts +++ b/src/typescript/visitors/imports/index.ts @@ -1,4 +1,5 @@ import ts from 'typescript'; +import constructorCall from './constructorCall.js'; import importCall from './importCall.js'; import importDeclaration from './importDeclaration.js'; import importEqualsDeclaration from './importEqualsDeclaration.js'; @@ -8,6 +9,7 @@ import reExportDeclaration from './reExportDeclaration.js'; import requireCall from './requireCall.js'; const visitors = [ + constructorCall, importCall, importDeclaration, importEqualsDeclaration, diff --git a/tests/imports-constructor-call.test.ts b/tests/imports-constructor-call.test.ts new file mode 100644 index 000000000..75b2eb63e --- /dev/null +++ b/tests/imports-constructor-call.test.ts @@ -0,0 +1,21 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; +import { main } from '../src/index.js'; +import { resolve } from '../src/util/path.js'; +import baseArguments from './helpers/baseArguments.js'; +import baseCounters from './helpers/baseCounters.js'; + +const cwd = resolve('fixtures/imports-constructor-call'); + +test('Support various constructor calls', async () => { + const { counters } = await main({ + ...baseArguments, + cwd, + }); + + assert.deepEqual(counters, { + ...baseCounters, + processed: 4, + total: 4, + }); +});