UNPKG

8.69 kBJavaScriptView Raw
1'use strict';
2
3const MergeTrees = require('broccoli-merge-trees');
4const Funnel = require('broccoli-funnel');
5const path = require('path');
6const resolve = require('resolve');
7const concatBundle = require('./concat-bundle');
8const buildDebugMacroPlugin = require('./build-debug-macro-plugin');
9const buildStripClassCallcheckPlugin = require('./build-strip-class-callcheck-plugin');
10const injectBabelHelpers = require('./transforms/inject-babel-helpers').injectBabelHelpers;
11const debugTree = require('broccoli-debug').buildDebugCallback('ember-source:addon');
12
13const PRE_BUILT_TARGETS = [
14 'last 1 Chrome versions',
15 'last 1 Firefox versions',
16 'last 1 Safari versions',
17];
18
19const paths = {};
20const absolutePaths = {};
21
22function add(paths, name, path) {
23 Object.defineProperty(paths, name, {
24 configurable: false,
25 get: function() {
26 return path;
27 },
28 });
29}
30
31add(paths, 'prod', 'vendor/ember/ember.js');
32add(paths, 'debug', 'vendor/ember/ember.js');
33add(paths, 'testing', 'vendor/ember/ember-testing.js');
34add(paths, 'jquery', 'vendor/ember/jquery/jquery.js');
35
36add(
37 absolutePaths,
38 'templateCompiler',
39 path.join(__dirname, '..', 'dist', 'ember-template-compiler.js')
40);
41
42module.exports = {
43 init() {
44 this._super.init && this._super.init.apply(this, arguments);
45
46 if ('ember' in this.project.bowerDependencies()) {
47 // TODO: move this to a throw soon.
48 this.ui.writeWarnLine(
49 'Ember.js is now provided by node_module `ember-source`, please remove it from bower'
50 );
51 }
52
53 // resets `this.root` to the correct location by default ember-cli
54 // considers `__dirname` here to be the root, but since our main entry
55 // point is within a subfolder we need to correct that
56 this.root = path.join(__dirname, '..');
57
58 // Updates the vendor tree to point to dist, so we get the correct tree in
59 // treeForVendor
60 this.treePaths.vendor = 'dist';
61 },
62
63 name: 'ember-source',
64 paths,
65 absolutePaths,
66
67 included() {
68 this._super.included.apply(this, arguments);
69
70 const { has } = require('@ember/edition-utils');
71
72 if (has('octane')) {
73 let optionalFeatures = this.project.addons.find(a => a.name === '@ember/optional-features');
74 let optionalFeaturesMissing = optionalFeatures === undefined;
75 let message = [];
76
77 if (optionalFeaturesMissing) {
78 message.push(
79 `* the @ember/optional-features addon is missing, run \`ember install @ember/optional-features\` to install it`
80 );
81 }
82
83 if (
84 optionalFeaturesMissing ||
85 typeof optionalFeatures.isFeatureExplicitlySet !== 'function'
86 ) {
87 message.push(
88 '* Unable to detect if jquery-integration is explicitly set to a value, please update `@ember/optional-features` to the latest version'
89 );
90 }
91
92 if (
93 optionalFeaturesMissing ||
94 (typeof optionalFeatures.isFeatureExplicitlySet === 'function' &&
95 !optionalFeatures.isFeatureExplicitlySet('jquery-integration'))
96 ) {
97 message.push(
98 `* The jquery-integration optional feature should be explicitly set to a value under Octane, run \`ember feature:disable jquery-integration\` to disable it, or \`ember feature:enable jquery-integration\` to explicitly enable it`
99 );
100 }
101
102 if (
103 optionalFeaturesMissing ||
104 optionalFeatures.isFeatureEnabled('application-template-wrapper')
105 ) {
106 message.push(
107 `* The application-template-wrapper optional feature should be disabled under Octane, run \`ember feature:disable application-template-wrapper\` to disable it`
108 );
109 }
110
111 if (
112 optionalFeaturesMissing ||
113 !optionalFeatures.isFeatureEnabled('template-only-glimmer-components')
114 ) {
115 message.push(
116 `* The template-only-glimmer-components optional feature should be enabled under Octane, run \`ember feature:enable template-only-glimmer-components\` to enable it`
117 );
118 }
119
120 if (message.length > 0) {
121 message.unshift(
122 `You have configured your application to indicate that it is using the 'octane' edition (via \`setEdition('octane')\`), but the appropriate Octane features were not enabled:\n`
123 );
124
125 const SilentError = require('silent-error');
126 throw new SilentError(message.join('\n\t'));
127 }
128 }
129 },
130
131 transpileTree(tree, isProduction, shouldCompileModules) {
132 let emberCliBabel = this.addons.find(a => a.name === 'ember-cli-babel');
133
134 let parentOptions = this.parent && this.parent.options;
135 let appOptions = this.app && this.app.options;
136 let babelOptions = (parentOptions || appOptions || {}).babel;
137
138 // We want to enable async/generator helpers if we are developing locally,
139 // but not for any other project.
140 let isEmberSource = this.project.name() === 'ember-source';
141 let babelHelperPlugin = injectBabelHelpers(isEmberSource);
142
143 let options = {
144 'ember-cli-babel': {
145 disableDebugTooling: true,
146 disableEmberModulesAPIPolyfill: true,
147 },
148 babel: Object.assign({}, babelOptions, {
149 loose: true,
150 plugins: [
151 babelHelperPlugin,
152 buildDebugMacroPlugin(!isProduction),
153 [
154 require.resolve('@babel/plugin-transform-block-scoping'),
155 { throwIfClosureRequired: true },
156 ],
157 [require.resolve('@babel/plugin-transform-object-assign')],
158 ],
159 }),
160 };
161
162 if (shouldCompileModules !== undefined) {
163 // ember-cli-babel internally uses **any** value that was provided IIF
164 // the option is set so this option must only be set when we have a
165 // useful value for it
166 options['ember-cli-babel'].compileModules = shouldCompileModules;
167 }
168
169 if (isProduction) {
170 options.babel.plugins.push(buildStripClassCallcheckPlugin());
171 }
172
173 return emberCliBabel.transpileTree(tree, options);
174 },
175
176 buildEmberBundles(tree, isProduction) {
177 let packages = this.transpileTree(new Funnel(tree, { srcDir: 'packages' }), isProduction);
178
179 let dependencies = this.transpileTree(
180 new Funnel(tree, { srcDir: 'dependencies' }),
181 isProduction
182 );
183
184 let headerFiles = this.transpileTree(
185 new Funnel(tree, { srcDir: 'header' }),
186 isProduction,
187 false
188 );
189
190 let exclude = isProduction ? ['ember-testing/**'] : [];
191
192 let emberFiles = new MergeTrees([new Funnel(packages, { exclude }), dependencies, headerFiles]);
193
194 let emberTestingFiles = new MergeTrees([
195 new Funnel(packages, {
196 include: [
197 '@ember/debug/lib/**',
198 '@ember/debug/index.js',
199 'ember-testing/index.js',
200 'ember-testing/lib/**',
201 ],
202 }),
203 headerFiles,
204 ]);
205
206 return new MergeTrees([
207 concatBundle(emberFiles, {
208 outputFile: 'ember.js',
209 footer: "require('ember');",
210 }),
211
212 concatBundle(emberTestingFiles, {
213 outputFile: 'ember-testing.js',
214 footer: `
215 var testing = require('ember-testing');
216 Ember.Test = testing.Test;
217 Ember.Test.Adapter = testing.Adapter;
218 Ember.Test.QUnitAdapter = testing.QUnitAdapter;
219 Ember.setupForTesting = testing.setupForTesting;
220 `,
221 }),
222 ]);
223 },
224
225 treeForVendor(tree) {
226 let jqueryPath;
227
228 try {
229 jqueryPath = path.dirname(
230 resolve.sync('jquery/package.json', { basedir: this.project.root })
231 );
232 } catch (error) {
233 jqueryPath = path.dirname(require.resolve('jquery/package.json'));
234 }
235
236 let jquery = new Funnel(jqueryPath + '/dist', {
237 destDir: 'ember/jquery',
238 files: ['jquery.js'],
239 });
240
241 let templateCompiler = new Funnel(tree, {
242 destDir: 'ember',
243 include: ['ember-template-compiler.js', 'ember-template-compiler.map'],
244 });
245
246 let ember;
247 let targets = (this.project && this.project.targets && this.project.targets.browsers) || [];
248
249 const isProduction = process.env.EMBER_ENV === 'production';
250
251 if (
252 !isProduction &&
253 (PRE_BUILT_TARGETS.every(target => targets.includes(target)) &&
254 targets.length === PRE_BUILT_TARGETS.length)
255 ) {
256 ember = new Funnel(tree, {
257 destDir: 'ember',
258 include: ['ember.debug.js', 'ember.debug.map', 'ember-testing.js', 'ember-testing.map'],
259 getDestinationPath(path) {
260 return path.replace('ember.debug.', 'ember.');
261 },
262 });
263 } else {
264 ember = new Funnel(this.buildEmberBundles(tree, isProduction), {
265 destDir: 'ember',
266 });
267 }
268
269 return debugTree(new MergeTrees([ember, templateCompiler, jquery]), 'vendor:final');
270 },
271};