-
Notifications
You must be signed in to change notification settings - Fork 142
/
rollup-hbs-plugin.ts
128 lines (116 loc) · 2.99 KB
/
rollup-hbs-plugin.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import { createFilter } from '@rollup/pluginutils';
import type {
Plugin,
PluginContext,
CustomPluginOptions,
ResolvedId,
} from 'rollup';
import { readFileSync } from 'fs';
import { hbsToJS } from '@embroider/core';
import assertNever from 'assert-never';
import { parse as pathParse } from 'path';
export default function rollupHbsPlugin(): Plugin {
return {
name: 'rollup-hbs-plugin',
async resolveId(source: string, importer: string | undefined, options) {
let resolution = await this.resolve(source, importer, {
skipSelf: true,
...options,
});
if (!resolution) {
return maybeSynthesizeComponentJS(this, source, importer, options);
} else {
return maybeRewriteHBS(resolution);
}
},
load(id: string) {
const meta = getMeta(this, id);
if (!meta) {
return;
}
switch (meta.type) {
case 'template':
this.addWatchFile(meta.originalId);
let input = readFileSync(meta.originalId, 'utf8');
let code = hbsToJS(input);
return {
code,
};
case 'template-only-component-js':
return {
code: templateOnlyComponent,
};
default:
assertNever(meta);
}
},
};
}
const templateOnlyComponent =
`import templateOnly from '@ember/component/template-only';\n` +
`export default templateOnly();\n`;
type Meta =
| {
type: 'template';
originalId: string;
}
| {
type: 'template-only-component-js';
};
function getMeta(context: PluginContext, id: string): Meta | null {
const meta = context.getModuleInfo(id)?.meta?.['rollup-hbs-plugin'];
if (meta) {
return meta as Meta;
} else {
return null;
}
}
function correspondingTemplate(filename: string): string {
let { ext } = pathParse(filename);
return filename.slice(0, filename.length - ext.length) + '.hbs';
}
async function maybeSynthesizeComponentJS(
context: PluginContext,
source: string,
importer: string | undefined,
options: { custom?: CustomPluginOptions; isEntry: boolean }
) {
let templateResolution = await context.resolve(
correspondingTemplate(source),
importer,
{
skipSelf: true,
...options,
}
);
if (!templateResolution) {
return null;
}
// we're trying to resolve a JS module but only the corresponding HBS
// file exists. Synthesize the template-only component JS.
return {
id: templateResolution.id.replace(/\.hbs$/, '.js'),
meta: {
'rollup-hbs-plugin': {
type: 'template-only-component-js',
},
},
};
}
const hbsFilter = createFilter('**/*.hbs');
function maybeRewriteHBS(resolution: ResolvedId) {
if (!hbsFilter(resolution.id)) {
return null;
}
// This creates an `*.hbs.js` that we will populate in `load()` hook.
return {
...resolution,
id: resolution.id + '.js',
meta: {
'rollup-hbs-plugin': {
type: 'template',
originalId: resolution.id,
},
},
};
}