UNPKG

14.6 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3/**
4 * @license
5 * Copyright Google Inc. All Rights Reserved.
6 *
7 * Use of this source code is governed by an MIT-style license that can be
8 * found in the LICENSE file at https://angular.io/license
9 */
10const core_1 = require("@angular-devkit/core");
11const schematics_1 = require("@angular-devkit/schematics");
12const tasks_1 = require("@angular-devkit/schematics/tasks");
13const dependencies_1 = require("../utility/dependencies");
14const json_utils_1 = require("../utility/json-utils");
15const latest_versions_1 = require("../utility/latest-versions");
16const lint_fix_1 = require("../utility/lint-fix");
17const paths_1 = require("../utility/paths");
18const tsconfig_1 = require("../utility/tsconfig");
19const validation_1 = require("../utility/validation");
20const workspace_1 = require("../utility/workspace");
21const workspace_models_1 = require("../utility/workspace-models");
22const schema_1 = require("./schema");
23function addDependenciesToPackageJson(options) {
24 return (host, context) => {
25 [
26 {
27 type: dependencies_1.NodeDependencyType.Dev,
28 name: '@angular/compiler-cli',
29 version: latest_versions_1.latestVersions.Angular,
30 },
31 {
32 type: dependencies_1.NodeDependencyType.Dev,
33 name: '@angular-devkit/build-angular',
34 version: latest_versions_1.latestVersions.DevkitBuildAngular,
35 },
36 {
37 type: dependencies_1.NodeDependencyType.Dev,
38 name: 'typescript',
39 version: latest_versions_1.latestVersions.TypeScript,
40 },
41 ].forEach(dependency => dependencies_1.addPackageJsonDependency(host, dependency));
42 if (!options.skipInstall) {
43 context.addTask(new tasks_1.NodePackageInstallTask());
44 }
45 return host;
46 };
47}
48function readTsLintConfig(host, path) {
49 const buffer = host.read(path);
50 if (!buffer) {
51 throw new schematics_1.SchematicsException(`Could not read ${path}.`);
52 }
53 const config = core_1.parseJsonAst(buffer.toString(), core_1.JsonParseMode.Loose);
54 if (config.kind !== 'object') {
55 throw new schematics_1.SchematicsException(`Invalid ${path}. Was expecting an object.`);
56 }
57 return config;
58}
59/**
60 * Merges the application tslint.json with the workspace tslint.json
61 * when the application being created is a root application
62 *
63 * @param {Tree} parentHost The root host of the schematic
64 */
65function mergeWithRootTsLint(parentHost) {
66 return (host) => {
67 const tsLintPath = '/tslint.json';
68 if (!host.exists(tsLintPath)) {
69 return;
70 }
71 const rootTslintConfig = readTsLintConfig(parentHost, tsLintPath);
72 const appTslintConfig = readTsLintConfig(host, tsLintPath);
73 const recorder = host.beginUpdate(tsLintPath);
74 rootTslintConfig.properties.forEach(prop => {
75 if (json_utils_1.findPropertyInAstObject(appTslintConfig, prop.key.value)) {
76 // property already exists. Skip!
77 return;
78 }
79 json_utils_1.insertPropertyInAstObjectInOrder(recorder, appTslintConfig, prop.key.value, prop.value.value, 2);
80 });
81 const rootRules = json_utils_1.findPropertyInAstObject(rootTslintConfig, 'rules');
82 const appRules = json_utils_1.findPropertyInAstObject(appTslintConfig, 'rules');
83 if (!appRules || appRules.kind !== 'object' || !rootRules || rootRules.kind !== 'object') {
84 // rules are not valid. Skip!
85 return;
86 }
87 rootRules.properties.forEach(prop => {
88 json_utils_1.insertPropertyInAstObjectInOrder(recorder, appRules, prop.key.value, prop.value.value, 4);
89 });
90 host.commitUpdate(recorder);
91 // this shouldn't be needed but at the moment without this formatting is not correct.
92 const content = readTsLintConfig(host, tsLintPath);
93 host.overwrite(tsLintPath, JSON.stringify(content.value, undefined, 2));
94 };
95}
96function addAppToWorkspaceFile(options, appDir) {
97 let projectRoot = appDir;
98 if (projectRoot) {
99 projectRoot += '/';
100 }
101 const schematics = {};
102 if (options.inlineTemplate
103 || options.inlineStyle
104 || options.minimal
105 || options.style !== schema_1.Style.Css) {
106 const componentSchematicsOptions = {};
107 if (options.inlineTemplate || options.minimal) {
108 componentSchematicsOptions.inlineTemplate = true;
109 }
110 if (options.inlineStyle || options.minimal) {
111 componentSchematicsOptions.inlineStyle = true;
112 }
113 if (options.style && options.style !== schema_1.Style.Css) {
114 componentSchematicsOptions.style = options.style;
115 }
116 schematics['@schematics/angular:component'] = componentSchematicsOptions;
117 }
118 if (options.skipTests || options.minimal) {
119 ['class', 'component', 'directive', 'guard', 'interceptor', 'module', 'pipe', 'service'].forEach((type) => {
120 if (!(`@schematics/angular:${type}` in schematics)) {
121 schematics[`@schematics/angular:${type}`] = {};
122 }
123 schematics[`@schematics/angular:${type}`].skipTests = true;
124 });
125 }
126 if (options.strict) {
127 if (!('@schematics/angular:application' in schematics)) {
128 schematics['@schematics/angular:application'] = {};
129 }
130 schematics['@schematics/angular:application'].strict = true;
131 }
132 const sourceRoot = core_1.join(core_1.normalize(projectRoot), 'src');
133 let budgets = [];
134 if (options.strict) {
135 budgets = [
136 {
137 type: 'initial',
138 maximumWarning: '500kb',
139 maximumError: '1mb',
140 },
141 {
142 type: 'anyComponentStyle',
143 maximumWarning: '2kb',
144 maximumError: '4kb',
145 },
146 ];
147 }
148 else {
149 budgets = [
150 {
151 type: 'initial',
152 maximumWarning: '2mb',
153 maximumError: '5mb',
154 },
155 {
156 type: 'anyComponentStyle',
157 maximumWarning: '6kb',
158 maximumError: '10kb',
159 },
160 ];
161 }
162 const project = {
163 root: core_1.normalize(projectRoot),
164 sourceRoot,
165 projectType: workspace_models_1.ProjectType.Application,
166 prefix: options.prefix || 'app',
167 schematics,
168 targets: {
169 build: {
170 builder: workspace_models_1.Builders.Browser,
171 options: {
172 outputPath: `dist/${options.name}`,
173 index: `${sourceRoot}/index.html`,
174 main: `${sourceRoot}/main.ts`,
175 polyfills: `${sourceRoot}/polyfills.ts`,
176 tsConfig: `${projectRoot}tsconfig.app.json`,
177 aot: true,
178 assets: [
179 `${sourceRoot}/favicon.ico`,
180 `${sourceRoot}/assets`,
181 ],
182 styles: [
183 `${sourceRoot}/styles.${options.style}`,
184 ],
185 scripts: [],
186 },
187 configurations: {
188 production: {
189 fileReplacements: [{
190 replace: `${sourceRoot}/environments/environment.ts`,
191 with: `${sourceRoot}/environments/environment.prod.ts`,
192 }],
193 optimization: true,
194 outputHashing: 'all',
195 sourceMap: false,
196 extractCss: true,
197 namedChunks: false,
198 extractLicenses: true,
199 vendorChunk: false,
200 buildOptimizer: true,
201 budgets,
202 },
203 },
204 },
205 serve: {
206 builder: workspace_models_1.Builders.DevServer,
207 options: {
208 browserTarget: `${options.name}:build`,
209 },
210 configurations: {
211 production: {
212 browserTarget: `${options.name}:build:production`,
213 },
214 },
215 },
216 'extract-i18n': {
217 builder: workspace_models_1.Builders.ExtractI18n,
218 options: {
219 browserTarget: `${options.name}:build`,
220 },
221 },
222 test: options.minimal ? undefined : {
223 builder: workspace_models_1.Builders.Karma,
224 options: {
225 main: `${sourceRoot}/test.ts`,
226 polyfills: `${sourceRoot}/polyfills.ts`,
227 tsConfig: `${projectRoot}tsconfig.spec.json`,
228 karmaConfig: `${projectRoot}karma.conf.js`,
229 assets: [
230 `${sourceRoot}/favicon.ico`,
231 `${sourceRoot}/assets`,
232 ],
233 styles: [
234 `${sourceRoot}/styles.${options.style}`,
235 ],
236 scripts: [],
237 },
238 },
239 lint: options.minimal ? undefined : {
240 builder: workspace_models_1.Builders.TsLint,
241 options: {
242 tsConfig: [
243 `${projectRoot}tsconfig.app.json`,
244 `${projectRoot}tsconfig.spec.json`,
245 ],
246 exclude: [
247 '**/node_modules/**',
248 ],
249 },
250 },
251 },
252 };
253 return workspace_1.updateWorkspace(workspace => {
254 if (workspace.projects.size === 0) {
255 workspace.extensions.defaultProject = options.name;
256 }
257 workspace.projects.add({
258 name: options.name,
259 ...project,
260 });
261 });
262}
263function minimalPathFilter(path) {
264 const toRemoveList = /(test.ts|tsconfig.spec.json|karma.conf.js|tslint.json).template$/;
265 return !toRemoveList.test(path);
266}
267function default_1(options) {
268 return async (host) => {
269 if (!options.name) {
270 throw new schematics_1.SchematicsException(`Invalid options, "name" is required.`);
271 }
272 validation_1.validateProjectName(options.name);
273 tsconfig_1.verifyBaseTsConfigExists(host);
274 const appRootSelector = `${options.prefix}-root`;
275 const componentOptions = !options.minimal ?
276 {
277 inlineStyle: options.inlineStyle,
278 inlineTemplate: options.inlineTemplate,
279 skipTests: options.skipTests,
280 style: options.style,
281 viewEncapsulation: options.viewEncapsulation,
282 } :
283 {
284 inlineStyle: true,
285 inlineTemplate: true,
286 skipTests: true,
287 style: options.style,
288 };
289 const workspace = await workspace_1.getWorkspace(host);
290 const newProjectRoot = workspace.extensions.newProjectRoot || '';
291 const isRootApp = options.projectRoot !== undefined;
292 const appDir = isRootApp
293 ? core_1.normalize(options.projectRoot || '')
294 : core_1.join(core_1.normalize(newProjectRoot), options.name);
295 const sourceDir = `${appDir}/src/app`;
296 const e2eOptions = {
297 relatedAppName: options.name,
298 rootSelector: appRootSelector,
299 };
300 return schematics_1.chain([
301 addAppToWorkspaceFile(options, appDir),
302 schematics_1.mergeWith(schematics_1.apply(schematics_1.url('./files'), [
303 options.minimal ? schematics_1.filter(minimalPathFilter) : schematics_1.noop(),
304 schematics_1.applyTemplates({
305 utils: core_1.strings,
306 ...options,
307 relativePathToWorkspaceRoot: paths_1.relativePathToWorkspaceRoot(appDir),
308 appName: options.name,
309 isRootApp,
310 }),
311 isRootApp ? mergeWithRootTsLint(host) : schematics_1.noop(),
312 schematics_1.move(appDir),
313 ]), schematics_1.MergeStrategy.Overwrite),
314 schematics_1.schematic('module', {
315 name: 'app',
316 commonModule: false,
317 flat: true,
318 routing: options.routing,
319 routingScope: 'Root',
320 path: sourceDir,
321 project: options.name,
322 }),
323 schematics_1.schematic('component', {
324 name: 'app',
325 selector: appRootSelector,
326 flat: true,
327 path: sourceDir,
328 skipImport: true,
329 project: options.name,
330 ...componentOptions,
331 }),
332 schematics_1.mergeWith(schematics_1.apply(schematics_1.url('./other-files'), [
333 options.strict
334 ? schematics_1.noop()
335 : schematics_1.filter(path => path !== '/package.json.template'),
336 componentOptions.inlineTemplate
337 ? schematics_1.filter(path => !path.endsWith('.html.template'))
338 : schematics_1.noop(),
339 componentOptions.skipTests
340 ? schematics_1.filter(path => !path.endsWith('.spec.ts.template'))
341 : schematics_1.noop(),
342 schematics_1.applyTemplates({
343 utils: core_1.strings,
344 ...options,
345 selector: appRootSelector,
346 ...componentOptions,
347 }),
348 schematics_1.move(sourceDir),
349 ]), schematics_1.MergeStrategy.Overwrite),
350 tsconfig_1.addTsConfigProjectReferences([
351 core_1.join(appDir, 'tsconfig.app.json'),
352 core_1.join(appDir, 'tsconfig.spec.json'),
353 ]),
354 options.minimal ? schematics_1.noop() : schematics_1.schematic('e2e', e2eOptions),
355 options.skipPackageJson ? schematics_1.noop() : addDependenciesToPackageJson(options),
356 options.lintFix ? lint_fix_1.applyLintFix(appDir) : schematics_1.noop(),
357 ]);
358 };
359}
360exports.default = default_1;