diff --git a/packages/addon-dev/src/rollup-hbs-plugin.ts b/packages/addon-dev/src/rollup-hbs-plugin.ts index 27a75d0c2..edf90249a 100644 --- a/packages/addon-dev/src/rollup-hbs-plugin.ts +++ b/packages/addon-dev/src/rollup-hbs-plugin.ts @@ -29,29 +29,34 @@ export default function rollupHbsPlugin({ } }, - transform(code: string, id: string) { - let meta = getMeta(this, id); - if (hbsFilter(id) && meta?.type !== 'template-js') { - return getHbsToJSCode(code); + async transform(code: string, id: string) { + if (!hbsFilter(id)) { + return; } - if (meta) { - if (meta?.type === 'template-js') { - return getHbsToJSCode(code); - } + let meta = getMeta(this, id); + if (meta?.type === 'template-only-component-js') { return { - code: templateOnlyComponent, + code: templateOnlyComponent(code), }; } + return getHbsToJSCode(code); }, }; } -const templateOnlyComponent = - `import templateOnly from '@ember/component/template-only';\n` + - `export default templateOnly();\n`; +function templateOnlyComponent(hbsCode: string) { + const code = hbsCode.replace(/`/g, '\\`').replace(/\$/g, '\\$'); + return ` + import templateOnly from '@ember/component/template-only'; + import { precompileTemplate } from '@ember/template-compilation'; + import { setComponentTemplate } from '@ember/component'; + export default setComponentTemplate(precompileTemplate(\`${code}\`), templateOnly()); + `; +} type Meta = { type: 'template-only-component-js' | 'template-js'; + hbsFile: string; }; function getMeta(context: PluginContext, id: string): Meta | null { diff --git a/packages/addon-dev/src/rollup-incremental-plugin.ts b/packages/addon-dev/src/rollup-incremental-plugin.ts index fbfcd9463..972205f3e 100644 --- a/packages/addon-dev/src/rollup-incremental-plugin.ts +++ b/packages/addon-dev/src/rollup-incremental-plugin.ts @@ -7,6 +7,7 @@ import { existsSync } from 'fs-extra'; export default function incremental(): Plugin { const changed = new Set(); const generatedAssets = new Map(); + const generatedFiles = new Set(); function isEqual(v1: string | Uint8Array, v2: string | Uint8Array): boolean { if (typeof v1 === 'string' && typeof v2 === 'string') { @@ -18,6 +19,71 @@ export default function incremental(): Plugin { return false; } + let firstTime = true; + + function initGeneratedFiles(outDir: string) { + if (existsSync(outDir)) { + const files = walkSync(outDir, { + globs: ['*/**'], + directories: false, + }); + for (const file of files) { + generatedFiles.add(file); + } + } + } + + function deleteRemovedFiles(bundle: Record, outDir: string) { + for (const file of generatedFiles) { + if (!bundle[file]) { + generatedAssets.delete(file); + rmSync(join(outDir, file)); + } + } + generatedFiles.clear(); + for (const file of Object.keys(bundle)) { + generatedFiles.add(file); + } + } + + function syncFiles(bundle: Record) { + for (const key of Object.keys(bundle)) { + let checkKey = key; + if (key.endsWith('.js.map')) { + checkKey = key.replace('.js.map', '.js'); + if (!bundle[checkKey]) { + delete bundle[key]; + continue; + } + } + if (bundle[checkKey]?.type === 'asset') { + if ( + generatedAssets.has(checkKey) && + isEqual( + (bundle[checkKey] as OutputAsset).source, + generatedAssets.get(checkKey) + ) + ) { + delete bundle[key]; + continue; + } else { + generatedAssets.set( + checkKey, + (bundle[checkKey] as OutputAsset).source + ); + } + } + if ( + (bundle[checkKey] as any)?.moduleIds?.every( + (m: string) => !changed.has(m) + ) + ) { + delete bundle[key]; + } + } + changed.clear(); + } + return { name: 'clean', transform(_code, id) { @@ -31,57 +97,15 @@ export default function incremental(): Plugin { } }, generateBundle(options, bundle) { + if (firstTime) { + firstTime = false; + initGeneratedFiles(options.dir!); + } if (existsSync(options.dir!)) { - const files = walkSync(options.dir!, { - globs: ['*/**'], - directories: false, - }); - for (const file of files) { - if (!bundle[file]) { - generatedAssets.delete(file); - rmSync(join(options.dir!, file)); - } - } + deleteRemovedFiles(bundle, options.dir!); } - for (const key of Object.keys(bundle)) { - let checkKey = key; - if (key.endsWith('.js.map')) { - checkKey = key.replace('.js.map', '.js'); - if (!bundle[checkKey]) { - delete bundle[key]; - continue; - } - } - if ( - bundle[checkKey]?.type === 'asset' && - generatedAssets.has(checkKey) - ) { - if ( - isEqual( - (bundle[checkKey] as OutputAsset).source, - generatedAssets.get(checkKey) - ) - ) { - delete bundle[key]; - continue; - } else { - generatedAssets.set( - checkKey, - (bundle[checkKey] as OutputAsset).source - ); - } - } - if ( - (bundle[checkKey] as any)?.moduleIds?.every( - (m: string) => !changed.has(m) - ) - ) { - delete bundle[key]; - continue; - } - } - changed.clear(); + syncFiles(bundle); }, }; } diff --git a/tests/scenarios/v2-addon-dev-test.ts b/tests/scenarios/v2-addon-dev-test.ts index 142b17889..2bce7b863 100644 --- a/tests/scenarios/v2-addon-dev-test.ts +++ b/tests/scenarios/v2-addon-dev-test.ts @@ -73,6 +73,8 @@ appScenarios exclude: ['**/-excluded/**/*'], }), + addon.clean(), + addon.hbs({ excludeColocation: ['**/just-a-template.hbs'], }), @@ -81,8 +83,6 @@ appScenarios addon.publicAssets('public'), babel({ babelHelpers: 'bundled', extensions: ['.js', '.hbs', '.gjs'] }), - - addon.clean(), ], }; `, @@ -210,12 +210,12 @@ appScenarios exclude: ['**/-excluded/**/*'], }), + addon.clean(), + addon.hbs(), addon.publicAssets('public', { namespace: '' }), babel({ babelHelpers: 'bundled', extensions: ['.js', '.hbs', '.gjs'] }), - - addon.clean(), ], }; `, diff --git a/tests/scenarios/v2-addon-dev-typescript-test.ts b/tests/scenarios/v2-addon-dev-typescript-test.ts index 94c44f327..8afabf4fe 100644 --- a/tests/scenarios/v2-addon-dev-typescript-test.ts +++ b/tests/scenarios/v2-addon-dev-typescript-test.ts @@ -115,6 +115,8 @@ appScenarios } }), + addon.clean(), + addon.dependencies(), babel({ extensions: ['.js', '.ts'], babelHelpers: 'inline' }), @@ -125,10 +127,6 @@ appScenarios }), addon.hbs(), - - - - addon.clean(), ], };