index.js 13 KB

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