From 2570dd7b9764f57938ecb50b4275e7437b7d60ba Mon Sep 17 00:00:00 2001 From: Joywayer Date: Sat, 5 Sep 2026 08:24:11 +0800 Subject: [PATCH] fix(sdk): scan template literal imports --- .../architecture/import-boundaries.test.mjs | 38 +++++++++++++++++++ .../scripts/lib/import-boundaries.mjs | 6 +-- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/cocoscreator_projects/framework-tests/architecture/import-boundaries.test.mjs b/cocoscreator_projects/framework-tests/architecture/import-boundaries.test.mjs index 5ae7a96..cdf7d01 100644 --- a/cocoscreator_projects/framework-tests/architecture/import-boundaries.test.mjs +++ b/cocoscreator_projects/framework-tests/architecture/import-boundaries.test.mjs @@ -242,6 +242,30 @@ test('scanner resolves and rejects an sdk runtime path alias into framework impl assert.match(violations[0].message, /sdk.*framework implementation.*framework\/core/); }); +test('scanner resolves and rejects a no-substitution-template dynamic sdk path alias', async () => { + const root = await createFixtureRoot(); + await writeFixture(root, 'framework/core/internal.ts', 'export const value = true\n'); + await writeFixture( + root, + 'framework/sdk/runtime/nested/bad.ts', + 'void import(`@framework/core/internal`)\n', + ); + await writeFixture(root, 'tsconfig.json', JSON.stringify({ + compilerOptions: { + baseUrl: '.', + paths: { '@framework/*': ['framework/*'] }, + module: 'ESNext', + moduleResolution: 'Bundler', + allowImportingTsExtensions: true, + }, + })); + + const violations = scan(root, 'tsconfig.json'); + assert.equal(violations.length, 1); + assert.equal(violations[0].specifier, '@framework/core/internal'); + assert.match(violations[0].message, /sdk.*framework implementation.*framework\/core/); +}); + test('scanner rejects Cocos imports throughout sdk runtime', async () => { for (const specifier of ['cc', 'cc/env']) { const root = await createFixtureRoot(); @@ -258,6 +282,20 @@ test('scanner rejects Cocos imports throughout sdk runtime', async () => { } }); +test('scanner rejects a no-substitution-template dynamic Cocos import in nested sdk code', async () => { + const root = await createFixtureRoot(); + await writeFixture( + root, + 'framework/sdk/testing/nested/bad.ts', + 'void import(`cc/env`)\n', + ); + + const violations = scan(root); + assert.equal(violations.length, 1); + assert.equal(violations[0].specifier, 'cc/env'); + assert.match(violations[0].message, /sdk.*Cocos/); +}); + test('sdk migration barrel exception permits only its four retained legacy dependencies', async () => { const root = await createFixtureRoot(); for (const file of [ diff --git a/cocoscreator_projects/scripts/lib/import-boundaries.mjs b/cocoscreator_projects/scripts/lib/import-boundaries.mjs index ad78ca3..e4cf665 100644 --- a/cocoscreator_projects/scripts/lib/import-boundaries.mjs +++ b/cocoscreator_projects/scripts/lib/import-boundaries.mjs @@ -211,7 +211,7 @@ function extractImports(source, file) { const visit = (node) => { if ((ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) && node.moduleSpecifier - && ts.isStringLiteral(node.moduleSpecifier)) { + && ts.isStringLiteralLike(node.moduleSpecifier)) { imports.push({ specifier: node.moduleSpecifier.text, bindings: ts.isImportDeclaration(node) && node.importClause @@ -221,13 +221,13 @@ function extractImports(source, file) { } if (ts.isImportTypeNode(node) && ts.isLiteralTypeNode(node.argument) - && ts.isStringLiteral(node.argument.literal)) { + && ts.isStringLiteralLike(node.argument.literal)) { imports.push({ specifier: node.argument.literal.text, bindings: '' }); } if (ts.isCallExpression(node) && node.expression.kind === ts.SyntaxKind.ImportKeyword && node.arguments.length === 1 - && ts.isStringLiteral(node.arguments[0])) { + && ts.isStringLiteralLike(node.arguments[0])) { imports.push({ specifier: node.arguments[0].text, bindings: '' }); } ts.forEachChild(node, visit);