UNPKG

20.8 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3const fs = require("fs");
4const path = require("path");
5const ts = require("typescript");
6const webpack = require("webpack");
7const app_utils_1 = require("../utilities/app-utils");
8const webpack_config_1 = require("../models/webpack-config");
9const config_1 = require("../models/config");
10const webpack_1 = require("@ngtools/webpack");
11const chalk_1 = require("chalk");
12const denodeify = require("denodeify");
13const common_tags_1 = require("common-tags");
14const exists = (p) => Promise.resolve(fs.existsSync(p));
15const writeFile = denodeify(fs.writeFile);
16const angularCliPlugins = require('../plugins/webpack');
17const autoprefixer = require('autoprefixer');
18const postcssUrl = require('postcss-url');
19const ExtractTextPlugin = require('extract-text-webpack-plugin');
20const HtmlWebpackPlugin = require('html-webpack-plugin');
21const SilentError = require('silent-error');
22const Task = require('../ember-cli/lib/models/task');
23const LoaderOptionsPlugin = webpack.LoaderOptionsPlugin;
24const ProgressPlugin = require('webpack/lib/ProgressPlugin');
25exports.pluginArgs = Symbol('plugin-args');
26exports.postcssArgs = Symbol('postcss-args');
27const pree2eNpmScript = `webdriver-manager update --standalone false --gecko false --quiet`;
28class JsonWebpackSerializer {
29 constructor(_root, _dist) {
30 this._root = _root;
31 this._dist = _dist;
32 this.imports = {};
33 this.variableImports = {
34 'path': 'path'
35 };
36 this.variables = {
37 'nodeModules': `path.join(process.cwd(), 'node_modules')`,
38 };
39 }
40 _escape(str) {
41 return '\uFF01' + str + '\uFF01';
42 }
43 _serializeRegExp(re) {
44 return this._escape(re.toString());
45 }
46 _serializeFunction(fn) {
47 return this._escape(fn.toString());
48 }
49 _relativePath(of, to) {
50 return this._escape(`path.join(${of}, ${JSON.stringify(to)})`);
51 }
52 _addImport(module, importName) {
53 if (!this.imports[module]) {
54 this.imports[module] = [];
55 }
56 if (this.imports[module].indexOf(importName) == -1) {
57 this.imports[module].push(importName);
58 }
59 }
60 _commonsChunkPluginSerialize(value) {
61 let minChunks = value.minChunks;
62 switch (typeof minChunks) {
63 case 'function':
64 minChunks = this._serializeFunction(value.minChunks);
65 break;
66 }
67 return {
68 name: value.chunkNames,
69 filename: value.filenameTemplate,
70 minChunks,
71 chunks: value.selectedChunks,
72 async: value.async,
73 minSize: value.minSize
74 };
75 }
76 _extractTextPluginSerialize(value) {
77 return {
78 filename: value.filename,
79 disable: value.options.disable
80 };
81 }
82 _aotPluginSerialize(value) {
83 const tsConfigPath = path.relative(this._root, value.options.tsConfigPath);
84 const basePath = path.dirname(tsConfigPath);
85 return Object.assign({}, value.options, {
86 tsConfigPath,
87 mainPath: path.relative(value.basePath, value.options.mainPath),
88 hostReplacementPaths: Object.keys(value.options.hostReplacementPaths)
89 .reduce((acc, key) => {
90 const replacementPath = value.options.hostReplacementPaths[key];
91 key = path.relative(basePath, key);
92 acc[key] = path.relative(basePath, replacementPath);
93 return acc;
94 }, {}),
95 exclude: Array.isArray(value.options.exclude)
96 ? value.options.exclude.map((p) => {
97 return p.startsWith('/') ? path.relative(value.basePath, p) : p;
98 })
99 : value.options.exclude
100 });
101 }
102 _htmlWebpackPlugin(value) {
103 const chunksSortMode = value.options.chunksSortMode;
104 this.variables['entryPoints'] = JSON.stringify(chunksSortMode.entryPoints);
105 return Object.assign({}, value.options, {
106 template: './' + path.relative(this._root, value.options.template),
107 filename: './' + path.relative(this._dist, value.options.filename),
108 chunksSortMode: this._serializeFunction(chunksSortMode)
109 });
110 }
111 _loaderOptionsPlugin(plugin) {
112 return Object.assign({}, plugin.options, {
113 test: plugin.options.test instanceof RegExp
114 ? this._serializeRegExp(plugin.options.test)
115 : undefined,
116 options: Object.assign({}, plugin.options.options, {
117 context: '',
118 postcss: plugin.options.options.postcss.map((x) => {
119 if (x && x.toString() == autoprefixer()) {
120 this.variableImports['autoprefixer'] = 'autoprefixer';
121 return this._escape('autoprefixer()');
122 }
123 else if (x && x.toString() == postcssUrl()) {
124 this.variableImports['postcss-url'] = 'postcssUrl';
125 let args = '';
126 if (x[exports.postcssArgs] && x[exports.postcssArgs].url) {
127 this.variables['baseHref'] = JSON.stringify(x[exports.postcssArgs].baseHref);
128 this.variables['deployUrl'] = JSON.stringify(x[exports.postcssArgs].deployUrl);
129 args = `{"url": ${x[exports.postcssArgs].url.toString()}}`;
130 }
131 return this._escape(`postcssUrl(${args})`);
132 }
133 else if (x && x.postcssPlugin == 'cssnano') {
134 this.variableImports['cssnano'] = 'cssnano';
135 return this._escape('cssnano({ safe: true, autoprefixer: false })');
136 }
137 else {
138 if (typeof x == 'function') {
139 return this._serializeFunction(x);
140 }
141 else {
142 return x;
143 }
144 }
145 })
146 })
147 });
148 }
149 _definePlugin(plugin) {
150 return plugin.definitions;
151 }
152 _pluginsReplacer(plugins) {
153 return plugins.map(plugin => {
154 let args = plugin.options || undefined;
155 switch (plugin.constructor) {
156 case ProgressPlugin:
157 this.variableImports['webpack/lib/ProgressPlugin'] = 'ProgressPlugin';
158 break;
159 case webpack.NoEmitOnErrorsPlugin:
160 this._addImport('webpack', 'NoEmitOnErrorsPlugin');
161 break;
162 case webpack.HashedModuleIdsPlugin:
163 this._addImport('webpack', 'HashedModuleIdsPlugin');
164 break;
165 case webpack.optimize.UglifyJsPlugin:
166 this._addImport('webpack.optimize', 'UglifyJsPlugin');
167 break;
168 case angularCliPlugins.BaseHrefWebpackPlugin:
169 case angularCliPlugins.GlobCopyWebpackPlugin:
170 case angularCliPlugins.SuppressExtractedTextChunksWebpackPlugin:
171 this._addImport('@angular/cli/plugins/webpack', plugin.constructor.name);
172 break;
173 case webpack.optimize.CommonsChunkPlugin:
174 args = this._commonsChunkPluginSerialize(plugin);
175 this._addImport('webpack.optimize', 'CommonsChunkPlugin');
176 break;
177 case ExtractTextPlugin:
178 args = this._extractTextPluginSerialize(plugin);
179 this.variableImports['extract-text-webpack-plugin'] = 'ExtractTextPlugin';
180 break;
181 case webpack_1.AotPlugin:
182 args = this._aotPluginSerialize(plugin);
183 this._addImport('@ngtools/webpack', 'AotPlugin');
184 break;
185 case HtmlWebpackPlugin:
186 args = this._htmlWebpackPlugin(plugin);
187 this.variableImports['html-webpack-plugin'] = 'HtmlWebpackPlugin';
188 break;
189 case LoaderOptionsPlugin:
190 args = this._loaderOptionsPlugin(plugin);
191 this._addImport('webpack', 'LoaderOptionsPlugin');
192 break;
193 case webpack.DefinePlugin:
194 args = this._definePlugin(plugin);
195 this._addImport('webpack', 'DefinePlugin');
196 break;
197 default:
198 if (plugin.constructor.name == 'AngularServiceWorkerPlugin') {
199 this._addImport('@angular/service-worker/build/webpack', plugin.constructor.name);
200 }
201 break;
202 }
203 const argsSerialized = JSON.stringify(args, (k, v) => this._replacer(k, v), 2) || '';
204 return `\uFF02${plugin.constructor.name}(${argsSerialized})\uFF02`;
205 });
206 }
207 _resolveReplacer(value) {
208 return Object.assign({}, value, {
209 modules: value.modules.map((x) => './' + path.relative(this._root, x))
210 });
211 }
212 _outputReplacer(value) {
213 return Object.assign({}, value, {
214 path: this._relativePath('process.cwd()', path.relative(this._root, value.path))
215 });
216 }
217 _path(l) {
218 return l.split('!').map(x => {
219 return path.isAbsolute(x) ? './' + path.relative(this._root, x) : x;
220 }).join('!');
221 }
222 _entryReplacer(value) {
223 const newValue = Object.assign({}, value);
224 for (const key of Object.keys(newValue)) {
225 newValue[key] = newValue[key].map((l) => this._path(l));
226 }
227 return newValue;
228 }
229 _loaderReplacer(loader) {
230 if (typeof loader == 'string') {
231 if (loader.match(/\/node_modules\/extract-text-webpack-plugin\//)) {
232 return 'extract-text-webpack-plugin';
233 }
234 else if (loader.match(/@ngtools\/webpack\/src\/index.ts/)) {
235 // return '@ngtools/webpack';
236 }
237 }
238 else {
239 if (loader.loader) {
240 loader.loader = this._loaderReplacer(loader.loader);
241 }
242 }
243 return loader;
244 }
245 _ruleReplacer(value) {
246 const replaceExcludeInclude = (v) => {
247 if (typeof v == 'object') {
248 if (v.constructor == RegExp) {
249 return this._serializeRegExp(v);
250 }
251 return v;
252 }
253 else if (typeof v == 'string') {
254 if (v === path.join(this._root, 'node_modules')) {
255 return this._serializeRegExp(/\/node_modules\//);
256 }
257 return this._relativePath('process.cwd()', path.relative(this._root, v));
258 }
259 else {
260 return v;
261 }
262 };
263 if (value[exports.pluginArgs]) {
264 return {
265 include: Array.isArray(value.include)
266 ? value.include.map((x) => replaceExcludeInclude(x))
267 : replaceExcludeInclude(value.include),
268 test: this._serializeRegExp(value.test),
269 loaders: this._escape(`ExtractTextPlugin.extract(${JSON.stringify(value[exports.pluginArgs], null, 2)})`)
270 };
271 }
272 if (value.loaders) {
273 value.loaders = value.loaders.map((loader) => this._loaderReplacer(loader));
274 }
275 if (value.loader) {
276 value.loader = this._loaderReplacer(value.loader);
277 }
278 if (value.exclude) {
279 value.exclude = Array.isArray(value.exclude)
280 ? value.exclude.map((x) => replaceExcludeInclude(x))
281 : replaceExcludeInclude(value.exclude);
282 }
283 if (value.include) {
284 value.include = Array.isArray(value.include)
285 ? value.include.map((x) => replaceExcludeInclude(x))
286 : replaceExcludeInclude(value.include);
287 }
288 return value;
289 }
290 _moduleReplacer(value) {
291 return Object.assign({}, value, {
292 rules: value.rules && value.rules.map((x) => this._ruleReplacer(x))
293 });
294 }
295 _replacer(_key, value) {
296 if (value === undefined) {
297 return value;
298 }
299 if (value === null) {
300 return null;
301 }
302 if (value.constructor === RegExp) {
303 return this._serializeRegExp(value);
304 }
305 return value;
306 }
307 serialize(config) {
308 // config = Object.assign({}, config);
309 config['plugins'] = this._pluginsReplacer(config['plugins']);
310 config['resolve'] = this._resolveReplacer(config['resolve']);
311 config['resolveLoader'] = this._resolveReplacer(config['resolveLoader']);
312 config['entry'] = this._entryReplacer(config['entry']);
313 config['output'] = this._outputReplacer(config['output']);
314 config['module'] = this._moduleReplacer(config['module']);
315 config['context'] = undefined;
316 return JSON.stringify(config, (k, v) => this._replacer(k, v), 2)
317 .replace(/"\uFF01(.*?)\uFF01"/g, (_, v) => {
318 return JSON.parse(`"${v}"`);
319 })
320 .replace(/(\s*)(.*?)"\uFF02(.*?)\uFF02"(,?).*/g, (_, indent, key, value, comma) => {
321 const ctor = JSON.parse(`"${value}"`).split(/\n+/g).join(indent);
322 return `${indent}${key}new ${ctor}${comma}`;
323 })
324 .replace(/"\uFF01(.*?)\uFF01"/g, (_, v) => {
325 return JSON.parse(`"${v}"`);
326 });
327 }
328 generateVariables() {
329 let variableOutput = '';
330 Object.keys(this.variableImports)
331 .forEach((key) => {
332 const [module, name] = key.split(/\./);
333 variableOutput += `const ${this.variableImports[key]} = require` + `('${module}')`;
334 if (name) {
335 variableOutput += '.' + name;
336 }
337 variableOutput += ';\n';
338 });
339 variableOutput += '\n';
340 Object.keys(this.imports)
341 .forEach((key) => {
342 const [module, name] = key.split(/\./);
343 variableOutput += `const { ${this.imports[key].join(', ')} } = require` + `('${module}')`;
344 if (name) {
345 variableOutput += '.' + name;
346 }
347 variableOutput += ';\n';
348 });
349 variableOutput += '\n';
350 Object.keys(this.variables)
351 .forEach((key) => {
352 variableOutput += `const ${key} = ${this.variables[key]};\n`;
353 });
354 variableOutput += '\n\n';
355 return variableOutput;
356 }
357}
358exports.default = Task.extend({
359 run: function (runTaskOptions) {
360 const project = this.cliProject;
361 const cliConfig = config_1.CliConfig.fromProject();
362 const config = cliConfig.config;
363 const appConfig = app_utils_1.getAppFromConfig(config.apps, runTaskOptions.app);
364 const tsConfigPath = path.join(process.cwd(), appConfig.root, appConfig.tsconfig);
365 const outputPath = runTaskOptions.outputPath || appConfig.outDir;
366 const force = runTaskOptions.force;
367 if (project.root === outputPath) {
368 throw new SilentError('Output path MUST not be project root directory!');
369 }
370 const webpackConfig = new webpack_config_1.NgCliWebpackConfig(runTaskOptions, appConfig).buildConfig();
371 const serializer = new JsonWebpackSerializer(process.cwd(), outputPath);
372 const output = serializer.serialize(webpackConfig);
373 const webpackConfigStr = `${serializer.generateVariables()}\n\nmodule.exports = ${output};\n`;
374 return Promise.resolve()
375 .then(() => exists('webpack.config.js'))
376 .then(webpackConfigExists => {
377 if (webpackConfigExists && !force) {
378 throw new SilentError('The webpack.config.js file already exists.');
379 }
380 })
381 .then(() => ts.sys.readFile('package.json'))
382 .then((packageJson) => JSON.parse(packageJson))
383 .then((packageJson) => {
384 const scripts = packageJson['scripts'];
385 if (scripts['build'] && scripts['build'] !== 'ng build' && !force) {
386 throw new SilentError(common_tags_1.oneLine `
387 Your package.json scripts needs to not contain a build script as it will be overwritten.
388 `);
389 }
390 if (scripts['start'] && scripts['start'] !== 'ng serve' && !force) {
391 throw new SilentError(common_tags_1.oneLine `
392 Your package.json scripts needs to not contain a start script as it will be overwritten.
393 `);
394 }
395 if (scripts['pree2e'] && scripts['pree2e'] !== pree2eNpmScript && !force) {
396 throw new SilentError(common_tags_1.oneLine `
397 Your package.json scripts needs to not contain a pree2e script as it will be
398 overwritten.
399 `);
400 }
401 if (scripts['e2e'] && scripts['e2e'] !== 'ng e2e' && !force) {
402 throw new SilentError(common_tags_1.oneLine `
403 Your package.json scripts needs to not contain a e2e script as it will be overwritten.
404 `);
405 }
406 if (scripts['test'] && scripts['test'] !== 'ng test' && !force) {
407 throw new SilentError(common_tags_1.oneLine `
408 Your package.json scripts needs to not contain a test script as it will be overwritten.
409 `);
410 }
411 packageJson['scripts']['build'] = 'webpack';
412 packageJson['scripts']['start'] = 'webpack-dev-server';
413 packageJson['scripts']['test'] = 'karma start ./karma.conf.js';
414 packageJson['scripts']['pree2e'] = pree2eNpmScript;
415 packageJson['scripts']['e2e'] = 'protractor ./protractor.conf.js';
416 // Add new dependencies based on our dependencies.
417 const ourPackageJson = require('../package.json');
418 packageJson['devDependencies']['webpack-dev-server']
419 = ourPackageJson['dependencies']['webpack-dev-server'];
420 // Update all loaders from webpack, plus postcss plugins.
421 [
422 'autoprefixer',
423 'css-loader',
424 'cssnano',
425 'exports-loader',
426 'file-loader',
427 'json-loader',
428 'karma-sourcemap-loader',
429 'less-loader',
430 'postcss-loader',
431 'postcss-url',
432 'raw-loader',
433 'sass-loader',
434 'script-loader',
435 'source-map-loader',
436 'istanbul-instrumenter-loader',
437 'style-loader',
438 'stylus-loader',
439 'url-loader',
440 ].forEach((packageName) => {
441 packageJson['devDependencies'][packageName] = ourPackageJson['dependencies'][packageName];
442 });
443 return writeFile('package.json', JSON.stringify(packageJson, null, 2));
444 })
445 .then(() => JSON.parse(ts.sys.readFile(tsConfigPath)))
446 .then((tsConfigJson) => {
447 if (!tsConfigJson.exclude || force) {
448 // Make sure we now include tests. Do not touch otherwise.
449 tsConfigJson.exclude = [
450 'test.ts',
451 '**/*.spec.ts'
452 ];
453 }
454 return writeFile(tsConfigPath, JSON.stringify(tsConfigJson, null, 2));
455 })
456 .then(() => writeFile('webpack.config.js', webpackConfigStr))
457 .then(() => {
458 // Update the CLI Config.
459 config.project.ejected = true;
460 cliConfig.save();
461 })
462 .then(() => {
463 console.log(chalk_1.yellow(common_tags_1.stripIndent `
464 ==========================================================================================
465 Ejection was successful.
466
467 To run your builds, you now need to do the following commands:
468 - "npm run build" to build.
469 - "npm run test" to run unit tests.
470 - "npm start" to serve the app using webpack-dev-server.
471 - "npm run e2e" to run protractor.
472
473 Running the equivalent CLI commands will result in an error.
474
475 ==========================================================================================
476 Some packages were added. Please run "npm install".
477 `));
478 });
479 }
480});
481//# sourceMappingURL=/users/hans/sources/angular-cli/tasks/eject.js.map
\No newline at end of file