diff --git a/packages/engine-compiler/src/core/compiler.ts b/packages/engine-compiler/src/core/compiler.ts index 11da7d65f..e40eb50f1 100644 --- a/packages/engine-compiler/src/core/compiler.ts +++ b/packages/engine-compiler/src/core/compiler.ts @@ -4,7 +4,6 @@ import { editorBrowserslistQuery } from '@cocos/lib-programming/dist/utils'; import * as ps from 'path'; import * as fsExtra from 'fs-extra'; import { IFeatureItem, IModuleItem, ModuleRenderConfig } from './modules'; -import { fixImportMapExtensions } from './import-map-utils'; const VERSION = '3'; const TEMP_ENGINE_CONFIG: any = { configs: { defaultConfig: { name: '默认配置', cache: { base: { _value: true }, 'gfx-webgl': { _value: true }, 'gfx-webgl2': { _value: false }, 'gfx-webgpu': { _value: false }, animation: { _value: true }, 'skeletal-animation': { _value: true }, '3d': { _value: true }, meshopt: { _value: false }, '2d': { _value: true }, 'sorting-2d': { _value: false }, 'rich-text': { _value: true }, mask: { _value: true }, graphics: { _value: true }, 'ui-skew': { _value: true }, 'affine-transform': { _value: true }, ui: { _value: true }, particle: { _value: true }, physics: { _value: true, _option: 'physics-physx' }, 'physics-ammo': { _value: true, _flags: { LOAD_BULLET_MANUALLY: false } }, 'physics-cannon': { _value: false }, 'physics-physx': { _value: false, _flags: { LOAD_PHYSX_MANUALLY: false } }, 'physics-builtin': { _value: false }, 'physics-2d': { _value: true, _option: 'physics-2d-box2d' }, 'physics-2d-box2d': { _value: true }, 'physics-2d-box2d-wasm': { _value: false, _flags: { LOAD_BOX2D_MANUALLY: false } }, 'physics-2d-builtin': { _value: false }, 'physics-2d-box2d-jsb': { _value: false }, 'intersection-2d': { _value: true }, primitive: { _value: true }, profiler: { _value: true }, 'occlusion-query': { _value: false }, 'geometry-renderer': { _value: false }, 'debug-renderer': { _value: false }, 'particle-2d': { _value: true }, audio: { _value: true }, video: { _value: true }, webview: { _value: true }, tween: { _value: true }, websocket: { _value: true }, 'websocket-server': { _value: false }, terrain: { _value: true }, 'light-probe': { _value: true }, 'tiled-map': { _value: true }, 'vendor-google': { _value: false }, spine: { _value: true, _option: 'spine-3.8' }, 'spine-3.8': { _value: true, _flags: { LOAD_SPINE_MANUALLY: false } }, 'spine-4.2': { _value: false, _flags: { LOAD_SPINE_MANUALLY: false } }, 'dragon-bones': { _value: true }, marionette: { _value: true }, 'procedural-animation': { _value: true }, 'custom-pipeline-post-process': { _value: false }, 'render-pipeline': { _value: true, _option: 'custom-pipeline' }, 'custom-pipeline': { _value: true }, 'legacy-pipeline': { _value: false }, xr: { _value: false } }, flags: { LOAD_BULLET_MANUALLY: false, LOAD_SPINE_MANUALLY: false, LOAD_PHYSX_MANUALLY: false }, includeModules: ['2d', '3d', 'affine-transform', 'animation', 'audio', 'base', 'custom-pipeline', 'dragon-bones', 'gfx-webgl', 'graphics', 'intersection-2d', 'light-probe', 'marionette', 'mask', 'particle', 'particle-2d', 'physics-2d-box2d', 'physics-physx', 'primitive', 'procedural-animation', 'profiler', 'rich-text', 'skeletal-animation', 'spine-3.8', 'terrain', 'tiled-map', 'tween', 'ui', 'ui-skew', 'video', 'websocket', 'webview'], noDeprecatedFeatures: { value: false, version: '' } } }, globalConfigKey: 'defaultConfig', graphics: { pipeline: 'custom-pipeline', 'custom-pipeline-post-process': false } }; @@ -385,15 +384,8 @@ export class EngineCompiler { configurableFlags, }, ); - // 修正 import-map 中无扩展名的模块映射(见 import-map-utils)。 - // quick-compiler 的 buildImportMap 直接用 cc.config override 字符串生成映射值:override - // 无扩展名(如 pal/*)时生成的 q-bundled URL 也无扩展名,与 bundle 里以 .js 注册的模块 - // 错位,编辑器/预览的静态 import-map 运行期会 fetch 无扩展名 URL 失败。这里补成 .js。 - const outputDir = this.isWeb ? ps.join(this.outDir, 'web') : ps.join(this.outDir, 'editor'); - await fixImportMapExtensions(ps.join(outputDir, 'import-map.json'), this.enginePath); } - async getConfigurableFlagsOfFeatures(features: string[]) { const flags: Record = {}; const EngineModulesConfig = TEMP_ENGINE_CONFIG; diff --git a/packages/engine-compiler/src/core/import-map-utils.ts b/packages/engine-compiler/src/core/import-map-utils.ts deleted file mode 100644 index abc4ffbc5..000000000 --- a/packages/engine-compiler/src/core/import-map-utils.ts +++ /dev/null @@ -1,56 +0,0 @@ -import * as ps from 'path'; -import * as fsExtra from 'fs-extra'; - -/** - * 修正 import-map 中无扩展名的模块映射值。 - * - * 背景:`@cocos/quick-compiler` 的 `buildImportMap` 直接使用 `cc.config.json` 的 override - * 字符串生成 import-map 的映射值,只做 `.ts→.js`/`.json→.js` 的扩展名转换。若 override 写成 - * 无扩展名(如 `pal/system-info/nodejs/system-info`),生成的 `q-bundled:///fs/*` URL 也无 - * 扩展名,而 bundle 里模块是以输出扩展名 `.js` 注册的。编辑器/预览用的是**静态 import-map** - * (运行期没有解析器补扩展名),于是会去 fetch 无扩展名 URL 而失败。 - * - * 这里在 import-map 生成后做一次后处理:把无扩展名的 `q-bundled:///fs/*` 值,按引擎目录下实际 - * 存在的源文件(`.js` 或 `.ts`)补成输出扩展名 `.js`(输出扩展名恒为 `.js`,`.ts` 也会被编译成 - * `.js`)。只加不改——已带扩展名(`.js`/`.mjs`/`.cjs`/`.jsx`/`.json`)、非 `fs` 值(如 - * `q-bundled:///virtual/cc.js`、外部 `cce:/*`)、或没有对应源文件的值都保持原样,因此对 vanilla - * 引擎(override 本就带 `.ts`,生成值已是 `.js`)零影响。 - * - * @param importMapFile import-map.json 路径。 - * @param enginePath 引擎根目录,用于校验 `fs/*` 值对应的源文件是否存在。 - * @returns 是否发生了修改(未修改时不重写文件)。 - */ -export async function fixImportMapExtensions(importMapFile: string, enginePath: string): Promise { - let importMap: { imports?: Record }; - try { - importMap = await fsExtra.readJson(importMapFile); - } catch { - return false; - } - const imports = importMap && importMap.imports; - if (!imports) { - return false; - } - const prefix = 'q-bundled:///fs/'; - let changed = false; - for (const key of Object.keys(imports)) { - const value = imports[key]; - if (typeof value !== 'string' || !value.startsWith(prefix)) { - continue; - } - const rel = value.slice(prefix.length); - if (/\.[cm]?jsx?$|\.json$/i.test(rel)) { - continue; // 已带扩展名 - } - const relPath = decodeURIComponent(rel); - if (fsExtra.existsSync(ps.join(enginePath, `${relPath}.js`)) - || fsExtra.existsSync(ps.join(enginePath, `${relPath}.ts`))) { - imports[key] = `${value}.js`; - changed = true; - } - } - if (changed) { - await fsExtra.outputJson(importMapFile, importMap, { spaces: 2 }); - } - return changed; -} diff --git a/tests/engine-compiler-fix-import-map-extensions.test.ts b/tests/engine-compiler-fix-import-map-extensions.test.ts deleted file mode 100644 index 788563bb4..000000000 --- a/tests/engine-compiler-fix-import-map-extensions.test.ts +++ /dev/null @@ -1,93 +0,0 @@ -import * as os from 'os'; -import * as ps from 'path'; -import * as fsExtra from 'fs-extra'; -import { fixImportMapExtensions } from '../packages/engine-compiler/src/core/import-map-utils'; - -/** - * 覆盖 engine-compiler 的 import-map 后处理: - * pal 等 override 无扩展名时,编辑器/预览编译生成的静态 import-map 会停在无扩展名 URL, - * 与 bundle 里以 .js 注册的模块错位。fixImportMapExtensions 负责按实际源文件补成 .js。 - */ -describe('engine-compiler / fixImportMapExtensions', () => { - let engineRoot: string; - let importMapFile: string; - - beforeEach(async () => { - engineRoot = await fsExtra.mkdtemp(ps.join(os.tmpdir(), 'engine-compiler-imap-')); - importMapFile = ps.join(engineRoot, 'bin', '.cache', 'dev-cli', 'editor', 'import-map.json'); - // 造 pal 源文件:一个只有 .js(模拟私有包产物),一个只有 .ts(模拟本地源码调试) - await fsExtra.outputFile(ps.join(engineRoot, 'pal/system-info/nodejs/system-info.js'), '// js'); - await fsExtra.outputFile(ps.join(engineRoot, 'pal/system-info/nodejs/system-info.d.ts'), '// dts'); - await fsExtra.outputFile(ps.join(engineRoot, 'pal/audio/tsonly/player.ts'), '// ts'); - }); - - afterEach(async () => { - await fsExtra.remove(engineRoot); - }); - - async function run(imports: Record) { - await fsExtra.outputJson(importMapFile, { imports }); - const changed = await fixImportMapExtensions(importMapFile, engineRoot); - const result = (await fsExtra.readJson(importMapFile)).imports as Record; - return { changed, result }; - } - - it('无扩展名 + 有 .js 源文件 → 补成 .js', async () => { - const { changed, result } = await run({ - 'pal/system-info': 'q-bundled:///fs/pal/system-info/nodejs/system-info', - }); - expect(changed).toBe(true); - expect(result['pal/system-info']).toBe('q-bundled:///fs/pal/system-info/nodejs/system-info.js'); - }); - - it('无扩展名 + 只有 .ts 源文件 → 也补成 .js(输出扩展名恒为 .js)', async () => { - const { result } = await run({ - 'pal/audio': 'q-bundled:///fs/pal/audio/tsonly/player', - }); - expect(result['pal/audio']).toBe('q-bundled:///fs/pal/audio/tsonly/player.js'); - }); - - it('无扩展名 + 无对应源文件 → 保持不变', async () => { - const value = 'q-bundled:///fs/pal/does/not/exist'; - const { changed, result } = await run({ 'pal/missing': value }); - expect(changed).toBe(false); - expect(result['pal/missing']).toBe(value); - }); - - it('已带 .js 扩展名 → 不变(vanilla / 私有化写 .js 的情况)', async () => { - const value = 'q-bundled:///fs/pal/system-info/nodejs/system-info.js'; - const { changed, result } = await run({ 'pal/system-info': value }); - expect(changed).toBe(false); - expect(result['pal/system-info']).toBe(value); - }); - - it('虚拟模块与外部值(非 q-bundled fs) → 不变', async () => { - const { changed, result } = await run({ - cc: 'q-bundled:///virtual/cc.js', - 'internal:constants': 'cce:/internal/x/constants', - }); - expect(changed).toBe(false); - expect(result['cc']).toBe('q-bundled:///virtual/cc.js'); - expect(result['internal:constants']).toBe('cce:/internal/x/constants'); - }); - - it('混合场景:只改无扩展名且有源文件的项', async () => { - const { result } = await run({ - 'pal/system-info': 'q-bundled:///fs/pal/system-info/nodejs/system-info', - 'pal/system-info-typed': 'q-bundled:///fs/pal/system-info/nodejs/system-info.js', - cc: 'q-bundled:///virtual/cc.js', - 'pal/missing': 'q-bundled:///fs/pal/does/not/exist', - }); - expect(result['pal/system-info']).toBe('q-bundled:///fs/pal/system-info/nodejs/system-info.js'); - expect(result['pal/system-info-typed']).toBe('q-bundled:///fs/pal/system-info/nodejs/system-info.js'); - expect(result['cc']).toBe('q-bundled:///virtual/cc.js'); - expect(result['pal/missing']).toBe('q-bundled:///fs/pal/does/not/exist'); - }); - - it('import-map 不存在 / 无 imports 字段 → 安全返回 false,不抛异常', async () => { - await expect(fixImportMapExtensions(ps.join(engineRoot, 'no-such.json'), engineRoot)).resolves.toBe(false); - const empty = ps.join(engineRoot, 'empty.json'); - await fsExtra.outputJson(empty, {}); - await expect(fixImportMapExtensions(empty, engineRoot)).resolves.toBe(false); - }); -});