Skip to content

Commit

Permalink
Support template-only imports
Browse files Browse the repository at this point in the history
  • Loading branch information
NullVoxPopuli committed Apr 10, 2022
1 parent d60d0b9 commit 73591d1
Show file tree
Hide file tree
Showing 5 changed files with 463 additions and 21 deletions.
90 changes: 85 additions & 5 deletions packages/addon-dev/src/rollup-hbs-plugin.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,64 @@
import { createFilter } from '@rollup/pluginutils';
import type { Plugin } from 'rollup';
import { readFileSync } from 'fs';
import { hbsToJS } from '@embroider/shared-internals';
import path from 'path';
import { pathExists } from 'fs-extra';

import type { Plugin } from 'rollup';

export default function rollupHbsPlugin(): Plugin {
const filter = createFilter('**/*.hbs');

return {
name: 'rollup-hbs-plugin',
async resolveId(source: string, importer: string | undefined, options) {
const resolution = await this.resolve(source, importer, {
let resolution = await this.resolve(source, importer, {
skipSelf: true,
...options,
});

const id = resolution?.id;
let id = resolution?.id;

if (!filter(id)) return null;
// if id is undefined, it's possible we're importing a file that that rollup
// doesn't natively support such as a template-only component that the author
// doesn't want to be available on the globals resolver
//
// e.g.:
// import { default as Button } from './button';
//
// where button.hbs is the sole "button" file.
//
// if someone where to specify the `.hbs` extension themselves as in:
//
// import { default as Button } from './button';
//
// then this whole block will be skipped
if (importer && !id) {
// We can't just emit the js side of the template-only component (export default templateOnly())
// because we can't tell rollup where to put the file -- all emitted files are
// not-on-disk-area-used-at-build-time -- emitted files are for the build output
//
// https://github.com/rollup/rollup/blob/master/docs/05-plugin-development.md
//
// So, to deal with this, we need to ensure there _is no corresponding js/ts file_
// for the imported hbs, and then, add in some meta so that the load hook can
// generate the setComponentTemplate + templateOnly() combo
let fileName = path.join(path.dirname(importer), source);
let hbsExists = await pathExists(fileName + '.hbs');

if (!hbsExists) return null;

resolution = await this.resolve(source + '.hbs', importer, {
skipSelf: true,
...options,
});

id = resolution?.id;
}

if (!filter(id) || !id) return null;

let isTO = await isTemplateOnly(id);

// This creates an `*.hbs.js` that we will populate in `load()` hook.
return {
Expand All @@ -25,18 +67,28 @@ export default function rollupHbsPlugin(): Plugin {
meta: {
'rollup-hbs-plugin': {
originalId: id,
isTemplateOnly: isTO,
},
},
};
},
load(id: string) {
const meta = this.getModuleInfo(id)?.meta;
const originalId = meta?.['rollup-hbs-plugin']?.originalId;
const pluginMeta = meta?.['rollup-hbs-plugin'];
const originalId = pluginMeta?.originalId;
const isTemplateOnly = pluginMeta?.isTemplateOnly;

if (!originalId) {
return;
}

if (isTemplateOnly) {
let code = getTemplateOnly(originalId);

return { code };
}

// Co-located js + hbs
let input = readFileSync(originalId, 'utf8');
let code = hbsToJS(input);
return {
Expand All @@ -45,3 +97,31 @@ export default function rollupHbsPlugin(): Plugin {
},
};
}

const backtick = '`';

async function isTemplateOnly(hbsPath: string) {
let jsPath = hbsPath.replace(/\.hbs$/, '.js');
let tsPath = hbsPath.replace(/\.hbs$/, '.ts');

let [hasJs, hasTs] = await Promise.all([
pathExists(jsPath),
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}${backtick}, templateOnly());`;

return code;
}
15 changes: 9 additions & 6 deletions packages/addon-dev/src/rollup-public-entrypoints.ts
Original file line number Diff line number Diff line change
@@ -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: {
Expand All @@ -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),
Expand Down
23 changes: 15 additions & 8 deletions tests/scenarios/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"dependencies": {
"@embroider/shared-internals": "1.6.0",
"@types/qunit": "^2.11.1",
"execa": "^5.0.0",
"fastboot": "^3.1.0",
"fs-extra": "^10.0.0",
"globby": "^11.0.3",
Expand All @@ -21,37 +22,43 @@
},
"license": "MIT",
"devDependencies": {
"@babel/core": "^7.17.5",
"@babel/plugin-proposal-class-properties": "^7.16.7",
"@babel/plugin-proposal-decorators": "^7.17.2",
"@babel/preset-env": "^7.16.11",
"@ember/string": "^1.0.0",
"@embroider/macros": "1.6.0",
"@embroider/addon-shim": "1.6.0",
"@embroider/router": "1.6.0",
"@embroider/util": "1.6.0",
"@rollup/plugin-babel": "^5.3.1",
"bootstrap": "^4.3.1",
"broccoli-funnel": "^3.0.5",
"broccoli-merge-trees": "^3.0.2",
"broccoli-persistent-filter": "^3.1.2",
"ember-bootstrap": "^5.0.0",
"ember-cli": "~3.28.0",
"ember-cli-3.16": "npm:ember-cli@~3.16.0",
"ember-cli-3.24": "npm:ember-cli@~3.24.0",
"ember-cli": "~3.28.0",
"ember-cli-beta": "npm:ember-cli@beta",
"ember-cli-htmlbars-inline-precompile": "^2.1.0",
"ember-cli-fastboot": "^3.2.0",
"ember-cli-htmlbars-3": "npm:ember-cli-htmlbars@3",
"ember-cli-htmlbars-inline-precompile": "^2.1.0",
"ember-cli-latest": "npm:ember-cli@latest",
"ember-cli-fastboot": "^3.2.0",
"ember-composable-helpers": "^4.4.1",
"ember-data": "~3.28.0",
"ember-data-3.16": "npm:ember-data@~3.16.0",
"ember-data-3.24": "npm:ember-data@~3.24.0",
"ember-data": "~3.28.0",
"ember-data-latest": "npm:ember-data@latest",
"ember-engines": "^0.8.17",
"ember-inline-svg": "^0.2.1",
"ember-source-latest": "npm:ember-source@latest",
"ember-source-beta": "npm:ember-source@beta",
"ember-source": "~3.28.0",
"ember-source-3.16": "npm:ember-source@~3.16.0",
"ember-source-3.24": "npm:ember-source@~3.24.0",
"ember-source": "~3.28.0",
"ember-truth-helpers": "^3.0.0"
"ember-source-beta": "npm:ember-source@beta",
"ember-source-latest": "npm:ember-source@latest",
"ember-truth-helpers": "^3.0.0",
"rollup": "^2.69.1"
},
"volta": {
"node": "14.16.1",
Expand Down
Loading

0 comments on commit 73591d1

Please sign in to comment.