1 | 'use strict';
|
2 |
|
3 | const {
|
4 | _shouldCompileModules,
|
5 | _shouldIncludeHelpers,
|
6 | _shouldHandleTypeScript,
|
7 | _getExtensions,
|
8 | _parentName,
|
9 | _shouldHighlightCode,
|
10 | } = require("./lib/babel-options-util");
|
11 |
|
12 | const VersionChecker = require('ember-cli-version-checker');
|
13 | const getBabelOptions = require('./lib/get-babel-options');
|
14 | const findApp = require('./lib/find-app');
|
15 | const emberPlugins = require('./lib/ember-plugins');
|
16 | const cacheKeyForTree = require('calculate-cache-key-for-tree');
|
17 |
|
18 | const APP_BABEL_RUNTIME_VERSION = new WeakMap();
|
19 | const PROJECTS_WITH_VALID_EMBER_CLI = new WeakSet();
|
20 |
|
21 | let count = 0;
|
22 |
|
23 | module.exports = {
|
24 | name: 'ember-cli-babel',
|
25 | configKey: 'ember-cli-babel',
|
26 |
|
27 |
|
28 | buildEmberPlugins: emberPlugins,
|
29 |
|
30 | init() {
|
31 | this._super.init && this._super.init.apply(this, arguments);
|
32 |
|
33 | if (!PROJECTS_WITH_VALID_EMBER_CLI.has(this.project)) {
|
34 | let checker = new VersionChecker(this);
|
35 | let dep = checker.for('ember-cli', 'npm');
|
36 |
|
37 | if (dep.lt('2.13.0')) {
|
38 | throw new Error(`ember-cli-babel@7 (used by ${_parentName(this.parent)} at ${this.parent.root}) cannot be used by ember-cli versions older than 2.13, you used ${dep.version}`);
|
39 | }
|
40 |
|
41 | PROJECTS_WITH_VALID_EMBER_CLI.add(this.project);
|
42 | }
|
43 | },
|
44 |
|
45 | buildBabelOptions(configOrType, _config) {
|
46 | let resultType;
|
47 |
|
48 | if (typeof configOrType !== 'string') {
|
49 | _config = configOrType;
|
50 | resultType = 'broccoli';
|
51 | } else if (configOrType === 'broccoli') {
|
52 | resultType = 'broccoli';
|
53 | } else if (configOrType === 'babel') {
|
54 | resultType = 'babel';
|
55 | }
|
56 |
|
57 | let config = _config || this._getAddonOptions();
|
58 |
|
59 | const customAddonConfig = config['ember-cli-babel'];
|
60 | const shouldUseBabelConfigFile = customAddonConfig && customAddonConfig.useBabelConfig;
|
61 |
|
62 | let options;
|
63 |
|
64 | if (shouldUseBabelConfigFile) {
|
65 | const babel = require('@babel/core');
|
66 |
|
67 | let babelConfig = babel.loadPartialConfig({
|
68 | root: this.parent.root,
|
69 | rootMode: 'root',
|
70 | envName: process.env.EMBER_ENV || process.env.BABEL_ENV || process.env.NODE_ENV || "development",
|
71 | });
|
72 |
|
73 | if (babelConfig.config === undefined) {
|
74 |
|
75 |
|
76 |
|
77 |
|
78 | throw new Error(
|
79 | "Missing babel config file in the project root. Please double check if the babel config file exists or turn off the `useBabelConfig` option in your ember-cli-build.js file."
|
80 | );
|
81 | }
|
82 |
|
83 |
|
84 |
|
85 | options = { configFile: babelConfig.config };
|
86 | } else {
|
87 | options = getBabelOptions(config, this);
|
88 | }
|
89 |
|
90 | if (resultType === 'babel') {
|
91 | return options;
|
92 | } else {
|
93 |
|
94 | return Object.assign({}, this._buildBroccoliBabelTranspilerOptions(config), options);
|
95 | }
|
96 | },
|
97 |
|
98 | _debugTree() {
|
99 | if (!this._cachedDebugTree) {
|
100 | this._cachedDebugTree = require('broccoli-debug').buildDebugCallback(`ember-cli-babel:${_parentName(this.parent)}`);
|
101 | }
|
102 |
|
103 | return this._cachedDebugTree.apply(null, arguments);
|
104 | },
|
105 |
|
106 | getSupportedExtensions(config = {}) {
|
107 | return _getExtensions(config, this.parent, this.project);
|
108 | },
|
109 |
|
110 | _buildBroccoliBabelTranspilerOptions(config = {}) {
|
111 | let emberCLIBabelConfig = config["ember-cli-babel"];
|
112 |
|
113 | let providedAnnotation;
|
114 | let throwUnlessParallelizable;
|
115 | let sourceMaps = false;
|
116 | let generatorOpts = {};
|
117 | let shouldCompileModules = _shouldCompileModules(config, this.project);
|
118 |
|
119 | if (emberCLIBabelConfig) {
|
120 | providedAnnotation = emberCLIBabelConfig.annotation;
|
121 | throwUnlessParallelizable = emberCLIBabelConfig.throwUnlessParallelizable;
|
122 | }
|
123 |
|
124 | if (config.babel && "sourceMaps" in config.babel) {
|
125 | sourceMaps = config.babel.sourceMaps;
|
126 | }
|
127 |
|
128 | if (config.babel && "generatorOpts" in config.babel) {
|
129 | generatorOpts = config.babel.generatorOpts;
|
130 | }
|
131 |
|
132 | let options = {
|
133 | annotation: providedAnnotation || `Babel: ${_parentName(this.parent)}`,
|
134 | sourceMaps,
|
135 | generatorOpts,
|
136 | throwUnlessParallelizable,
|
137 | filterExtensions: this.getSupportedExtensions(config),
|
138 | plugins: [],
|
139 | };
|
140 |
|
141 | if (shouldCompileModules) {
|
142 | options.moduleIds = true;
|
143 | options.getModuleId = require("./lib/relative-module-paths").getRelativeModulePath;
|
144 | }
|
145 |
|
146 | options.highlightCode = _shouldHighlightCode(this.parent);
|
147 | options.babelrc = false;
|
148 | options.configFile = false;
|
149 |
|
150 | return options;
|
151 | },
|
152 |
|
153 | transpileTree(inputTree, _config) {
|
154 | let config = _config || this._getAddonOptions();
|
155 | let description = `000${++count}`.slice(-3);
|
156 | let postDebugTree = this._debugTree(inputTree, `${description}:input`);
|
157 | let {
|
158 |
|
159 | babelrc,
|
160 | configFile,
|
161 | getModuleId,
|
162 | highlightCode,
|
163 | moduleIds,
|
164 | plugins,
|
165 | sourceMaps,
|
166 | ...transpilerOptions
|
167 | } = this._buildBroccoliBabelTranspilerOptions(config);
|
168 | let options = {
|
169 | ...transpilerOptions,
|
170 |
|
171 |
|
172 | babel: {
|
173 | babelrc,
|
174 | configFile,
|
175 | getModuleId,
|
176 | highlightCode,
|
177 | moduleIds,
|
178 | plugins,
|
179 | sourceMaps,
|
180 | ...this.buildBabelOptions('babel', config),
|
181 | },
|
182 | };
|
183 |
|
184 |
|
185 |
|
186 | Object.keys(options.babel).forEach(key => {
|
187 | if (options.babel[key] === undefined) {
|
188 | delete options.babel[key];
|
189 | }
|
190 | });
|
191 |
|
192 | let output;
|
193 |
|
194 | const customAddonConfig = config['ember-cli-babel'];
|
195 | const shouldUseBabelConfigFile = customAddonConfig && customAddonConfig.useBabelConfig;
|
196 |
|
197 | if (!shouldUseBabelConfigFile && this._shouldDoNothing(options)) {
|
198 | output = postDebugTree;
|
199 | } else {
|
200 | let BabelTranspiler = require('broccoli-babel-transpiler');
|
201 | let transpilationInput = postDebugTree;
|
202 |
|
203 | if (_shouldHandleTypeScript(config, this.parent, this.project)) {
|
204 | let Funnel = require('broccoli-funnel');
|
205 | let inputWithoutDeclarations = new Funnel(transpilationInput, { exclude: ['**/*.d.ts'] });
|
206 | transpilationInput = this._debugTree(inputWithoutDeclarations, `${description}:filtered-input`);
|
207 | }
|
208 |
|
209 | output = new BabelTranspiler(transpilationInput, options);
|
210 | }
|
211 |
|
212 | return this._debugTree(output, `${description}:output`);
|
213 | },
|
214 |
|
215 | setupPreprocessorRegistry(type, registry) {
|
216 | registry.add('js', {
|
217 | name: 'ember-cli-babel',
|
218 | ext: _getExtensions(this._getAddonOptions(), this.parent, this.project),
|
219 | toTree: (tree) => this.transpileTree(tree)
|
220 | });
|
221 | },
|
222 |
|
223 | _getHelperVersion() {
|
224 | if (!APP_BABEL_RUNTIME_VERSION.has(this.project)) {
|
225 | let checker = new VersionChecker(this.project);
|
226 | APP_BABEL_RUNTIME_VERSION.set(this.project, checker.for('@babel/runtime', 'npm').version);
|
227 | }
|
228 |
|
229 | return APP_BABEL_RUNTIME_VERSION.get(this.project);
|
230 | },
|
231 |
|
232 | _getHelpersPlugin() {
|
233 | return [
|
234 | [
|
235 | require.resolve('@babel/plugin-transform-runtime'),
|
236 | {
|
237 | version: this._getHelperVersion(),
|
238 | regenerator: false,
|
239 | useESModules: true
|
240 | }
|
241 | ]
|
242 | ]
|
243 | },
|
244 |
|
245 | treeForAddon() {
|
246 |
|
247 |
|
248 | let isRootBabel = this.parent === this.project;
|
249 | let shouldIncludeHelpers = isRootBabel && _shouldIncludeHelpers(this._getAppOptions(), this);
|
250 |
|
251 | if (!shouldIncludeHelpers) { return; }
|
252 |
|
253 | const path = require('path');
|
254 | const Funnel = require('broccoli-funnel');
|
255 | const UnwatchedDir = require('broccoli-source').UnwatchedDir;
|
256 |
|
257 | const babelHelpersPath = path.dirname(require.resolve('@babel/runtime/package.json'));
|
258 |
|
259 | let babelHelpersTree = new Funnel(new UnwatchedDir(babelHelpersPath), {
|
260 | srcDir: 'helpers/esm',
|
261 | destDir: '@babel/runtime/helpers/esm'
|
262 | });
|
263 |
|
264 | const transpiledHelpers = this.transpileTree(babelHelpersTree, {
|
265 | 'ember-cli-babel': {
|
266 |
|
267 | disablePresetEnv: true
|
268 | }
|
269 | });
|
270 |
|
271 | return new Funnel(transpiledHelpers, {
|
272 | destDir: this.moduleName(),
|
273 | });
|
274 | },
|
275 |
|
276 | cacheKeyForTree(treeType) {
|
277 | if (treeType === 'addon') {
|
278 | let isRootBabel = this.parent === this.project;
|
279 | let shouldIncludeHelpers = isRootBabel && _shouldIncludeHelpers(this._getAppOptions(), this);
|
280 |
|
281 | return cacheKeyForTree('addon', this, [shouldIncludeHelpers]);
|
282 | }
|
283 |
|
284 | return cacheKeyForTree(treeType, this);
|
285 | },
|
286 |
|
287 | isPluginRequired(pluginName) {
|
288 | let targets = this._getTargets();
|
289 |
|
290 |
|
291 | if (!targets) { return true; }
|
292 |
|
293 | const isPluginRequired = require('@babel/helper-compilation-targets').isRequired;
|
294 | return isPluginRequired(pluginName, targets);
|
295 | },
|
296 |
|
297 | _getAddonOptions() {
|
298 | let parentOptions = this.parent && this.parent.options;
|
299 | let appOptions = this.app && this.app.options;
|
300 |
|
301 | if (parentOptions) {
|
302 | let customAddonOptions = parentOptions['ember-cli-babel'];
|
303 |
|
304 | if (customAddonOptions && 'includeExternalHelpers' in customAddonOptions) {
|
305 | throw new Error('includeExternalHelpers is not supported in addon configurations, it is an app-wide configuration option');
|
306 | }
|
307 | }
|
308 |
|
309 | return parentOptions || appOptions || {};
|
310 | },
|
311 |
|
312 | _getAppOptions() {
|
313 | let app = findApp(this);
|
314 |
|
315 | return (app && app.options) || {};
|
316 | },
|
317 |
|
318 | _getTargets() {
|
319 | let targets = this.project && this.project.targets;
|
320 |
|
321 | let parser = require('@babel/helper-compilation-targets').default;
|
322 | if (typeof targets === 'object' && targets !== null) {
|
323 | return parser(targets);
|
324 | } else {
|
325 | return targets;
|
326 | }
|
327 | },
|
328 |
|
329 | |
330 |
|
331 |
|
332 |
|
333 |
|
334 |
|
335 |
|
336 | shouldCompileModules() {
|
337 | return _shouldCompileModules(this._getAddonOptions(), this.project);
|
338 | },
|
339 |
|
340 |
|
341 | _shouldDoNothing(options) {
|
342 | return !options.babel.sourceMaps && !options.babel.plugins.length;
|
343 | }
|
344 | };
|