fix(sdk): scan template literal imports

This commit is contained in:
2026-09-05 08:24:11 +08:00
parent 88784ad52a
commit 2570dd7b97
2 changed files with 41 additions and 3 deletions
@@ -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/); 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 () => { test('scanner rejects Cocos imports throughout sdk runtime', async () => {
for (const specifier of ['cc', 'cc/env']) { for (const specifier of ['cc', 'cc/env']) {
const root = await createFixtureRoot(); 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 () => { test('sdk migration barrel exception permits only its four retained legacy dependencies', async () => {
const root = await createFixtureRoot(); const root = await createFixtureRoot();
for (const file of [ for (const file of [
@@ -211,7 +211,7 @@ function extractImports(source, file) {
const visit = (node) => { const visit = (node) => {
if ((ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) if ((ts.isImportDeclaration(node) || ts.isExportDeclaration(node))
&& node.moduleSpecifier && node.moduleSpecifier
&& ts.isStringLiteral(node.moduleSpecifier)) { && ts.isStringLiteralLike(node.moduleSpecifier)) {
imports.push({ imports.push({
specifier: node.moduleSpecifier.text, specifier: node.moduleSpecifier.text,
bindings: ts.isImportDeclaration(node) && node.importClause bindings: ts.isImportDeclaration(node) && node.importClause
@@ -221,13 +221,13 @@ function extractImports(source, file) {
} }
if (ts.isImportTypeNode(node) if (ts.isImportTypeNode(node)
&& ts.isLiteralTypeNode(node.argument) && ts.isLiteralTypeNode(node.argument)
&& ts.isStringLiteral(node.argument.literal)) { && ts.isStringLiteralLike(node.argument.literal)) {
imports.push({ specifier: node.argument.literal.text, bindings: '' }); imports.push({ specifier: node.argument.literal.text, bindings: '' });
} }
if (ts.isCallExpression(node) if (ts.isCallExpression(node)
&& node.expression.kind === ts.SyntaxKind.ImportKeyword && node.expression.kind === ts.SyntaxKind.ImportKeyword
&& node.arguments.length === 1 && node.arguments.length === 1
&& ts.isStringLiteral(node.arguments[0])) { && ts.isStringLiteralLike(node.arguments[0])) {
imports.push({ specifier: node.arguments[0].text, bindings: '' }); imports.push({ specifier: node.arguments[0].text, bindings: '' });
} }
ts.forEachChild(node, visit); ts.forEachChild(node, visit);