-
-
Notifications
You must be signed in to change notification settings - Fork 65
Expand file tree
/
Copy pathtemplate-compiler-plugin.js
More file actions
155 lines (126 loc) · 4.64 KB
/
template-compiler-plugin.js
File metadata and controls
155 lines (126 loc) · 4.64 KB
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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
'use strict';
const path = require('path');
const utils = require('./utils');
const Filter = require('broccoli-persistent-filter');
const crypto = require('crypto');
const stringify = require('safe-stable-stringify');
const stripBom = require('strip-bom');
function rethrowBuildError(error) {
if (!error) {
throw new Error('Unknown Error');
}
if (typeof error === 'string') {
throw new Error('[string exception]: ' + error);
} else {
// augment with location and type information and re-throw.
error.type = 'Template Compiler Error';
error.location = error.location && error.location.start;
throw error;
}
}
class TemplateCompiler extends Filter {
constructor(inputTree, _options, requiresModuleApiPolyfill = true) {
let options = _options || {};
if (!('persist' in options)) {
options.persist = true;
}
super(inputTree, options);
this.options = options;
this.inputTree = inputTree;
this.requiresModuleApiPolyfill = requiresModuleApiPolyfill;
// TODO: do we need this?
this.precompile = this.options.templateCompiler.precompile;
let { templateCompiler, EmberENV } = options;
utils.initializeEmberENV(templateCompiler, EmberENV);
}
baseDir() {
return __dirname;
}
processString(string, relativePath) {
let srcDir = this.inputPaths[0];
let srcName = path.join(srcDir, relativePath);
try {
// we have to reverse these for reasons that are a bit bonkers. the initial
// version of this system used `registeredPlugin` from
// `ember-template-compiler.js` to set up these plugins (because Ember ~ 1.13
// only had `registerPlugin`, and there was no way to pass plugins directly
// to the call to `compile`/`precompile`). calling `registerPlugin`
// unfortunately **inverted** the order of plugins (it essentially did
// `PLUGINS = [plugin, ...PLUGINS]`).
//
// sooooooo...... we are forced to maintain that **absolutely bonkers** ordering
let astPlugins = this.options.plugins ? [...this.options.plugins.ast].reverse() : [];
let precompiled = this.options.templateCompiler.precompile(stripBom(string), {
contents: string,
isProduction: this.options.isProduction,
moduleName: relativePath,
parseOptions: {
srcName: srcName,
},
// intentionally not using `plugins: this.options.plugins` here
// because if we do, Ember will mutate the shared plugins object (adding
// all of the built in AST transforms into plugins.ast, which breaks
// persistent caching)
plugins: {
ast: astPlugins,
},
});
if (this.options.dependencyInvalidation) {
let plugins = pluginsWithDependencies(this.options.plugins.ast);
let dependencies = [];
for (let i = 0; i < plugins.length; i++) {
let pluginDeps = plugins[i].getDependencies(relativePath);
dependencies = dependencies.concat(pluginDeps);
}
this.dependencies.setDependencies(relativePath, dependencies);
}
if (this.requiresModuleApiPolyfill) {
return `export default Ember.HTMLBars.template(${precompiled});`;
} else {
return `import { createTemplateFactory } from '@ember/template-factory';\n\nexport default createTemplateFactory(${precompiled});`;
}
} catch (error) {
rethrowBuildError(error);
}
}
_buildOptionsForHash() {
let strippedOptions = {};
for (let key in this.options) {
if (key !== 'templateCompiler') {
strippedOptions[key] = this.options[key];
}
}
strippedOptions._requiresModuleApiPolyfill = this.requiresModuleApiPolyfill;
return strippedOptions;
}
optionsHash() {
if (!this._optionsHash) {
let templateCompilerCacheKey = utils.getTemplateCompilerCacheKey(
this.options.templateCompilerPath
);
this._optionsHash = crypto
.createHash('md5')
.update(stringify(this._buildOptionsForHash()), 'utf8')
.update(templateCompilerCacheKey, 'utf8')
.digest('hex');
}
return this._optionsHash;
}
cacheKeyProcessString(string, relativePath) {
return (
this.optionsHash() + Filter.prototype.cacheKeyProcessString.call(this, string, relativePath)
);
}
}
TemplateCompiler.prototype.extensions = ['hbs', 'handlebars'];
TemplateCompiler.prototype.targetExtension = 'js';
function pluginsWithDependencies(registeredPlugins) {
let found = [];
for (let i = 0; i < registeredPlugins.length; i++) {
if (registeredPlugins[i].getDependencies) {
found.push(registeredPlugins[i]);
}
}
return found;
}
module.exports = TemplateCompiler;