From 3c456151d34f617c8b39819c2cda2457fdffb2bf Mon Sep 17 00:00:00 2001 From: NullVoxPopuli Date: Thu, 10 Feb 2022 22:31:57 -0500 Subject: [PATCH] Support template-only imports --- packages/addon-dev/src/rollup-hbs-plugin.ts | 81 +++++++++++++++++-- .../src/rollup-public-entrypoints.ts | 15 ++-- 2 files changed, 83 insertions(+), 13 deletions(-) diff --git a/packages/addon-dev/src/rollup-hbs-plugin.ts b/packages/addon-dev/src/rollup-hbs-plugin.ts index 3c62d71e5..2baf865e0 100644 --- a/packages/addon-dev/src/rollup-hbs-plugin.ts +++ b/packages/addon-dev/src/rollup-hbs-plugin.ts @@ -1,6 +1,10 @@ import { createFilter } from '@rollup/pluginutils'; -import type { Plugin } from 'rollup'; +import { pathExists } from 'fs-extra'; import { readFileSync } from 'fs'; +import path from 'path'; + +import type { Plugin } from 'rollup'; + const backtick = '`'; export default function rollupHbsPlugin(): Plugin { @@ -8,16 +12,79 @@ export default function rollupHbsPlugin(): Plugin { return { name: 'rollup-hbs-plugin', - load(id: string) { + async load(id: string) { if (!filter(id)) return; - let input = readFileSync(id, 'utf8'); - let code = - `import { hbs } from 'ember-cli-htmlbars';\n` + - `export default hbs${backtick}${input}${backtick};`; + + if (await isTemplateOnly(id)) { + return { code: getTemplateOnly(id), id }; + } + + // For templates with a backing class return { - code, + code: getTemplateFactory(id), id: id + '.js', }; }, + + // template-only components may be imported + async resolveId(source, importer, options) { + let resolution = await this.resolve(source, importer, { + ...options, + skipSelf: true, + }); + + let ext = path.extname(source); + + // For hbs files, we _might_ have a template-only situation + if (resolution && ext !== '.hbs') return resolution; + if (!importer) return null; + + // source may or may not have '.hbs' on the path + let dir = path.dirname(importer); + let nameWithExt = ext ? source : source + '.hbs'; + let maybeHbs = path.join(dir, nameWithExt); + + resolution = await this.resolve(maybeHbs, importer, { + ...options, + skipSelf: true, + }); + + if (resolution) return resolution; + + return null; + }, }; } + +async function isTemplateOnly(hbsPath: string) { + let jsPath = hbsPath.replace(/\.hbs$/, '.js'); + let tsPath = hbsPath.replace(/\.hbs$/, '.ts'); + + let hasJs = await pathExists(jsPath); + let hasTs = await pathExists(tsPath); + + let hasClass = hasJs || hasTs; + + return !hasClass; +} + +function getTemplateOnly(hbsPath: string) { + let input = readFileSync(hbsPath, 'utf8'); + let code = + `import { hbs } from 'ember-cli-htmlbars';\n` + + `import templateOnly from '@ember/component/template-only';\n` + + `import { setComponentTemplate } from '@ember/component';\n` + + `export default setComponentTemplate(\n` + + `hbs${backtick}${input.trim()}${backtick}, templateOnly());`; + + return code; +} + +function getTemplateFactory(hbsPath: string) { + let input = readFileSync(hbsPath, 'utf8'); + let code = + `import { hbs } from 'ember-cli-htmlbars';\n` + + `export default hbs${backtick}${input.trim()}${backtick};`; + + return code; +} diff --git a/packages/addon-dev/src/rollup-public-entrypoints.ts b/packages/addon-dev/src/rollup-public-entrypoints.ts index 2e788334c..a85c8d379 100644 --- a/packages/addon-dev/src/rollup-public-entrypoints.ts +++ b/packages/addon-dev/src/rollup-public-entrypoints.ts @@ -1,9 +1,10 @@ import walkSync from 'walk-sync'; -import type { Plugin } from 'rollup'; import { join } from 'path'; +import type { Plugin } from 'rollup'; + function normalizeFileExt(fileName: string) { - return fileName.replace(/\.ts|\.gts|\.gjs$/, '.js'); + return fileName.replace(/\.ts|\.hbs|\.gts|\.gjs$/, '.js'); } export default function publicEntrypoints(args: { @@ -12,10 +13,12 @@ export default function publicEntrypoints(args: { }): Plugin { return { name: 'addon-modules', - buildStart() { - for (let name of walkSync(args.srcDir, { - globs: args.include, - })) { + async buildStart() { + let matches = walkSync(args.srcDir, { + globs: [...args.include], + }); + + for (let name of matches) { this.emitFile({ type: 'chunk', id: join(args.srcDir, name),