index.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. "use strict";
  2. /**
  3. * @license
  4. * Copyright Google LLC All Rights Reserved.
  5. *
  6. * Use of this source code is governed by an MIT-style license that can be
  7. * found in the LICENSE file at https://angular.dev/license
  8. */
  9. Object.defineProperty(exports, "__esModule", { value: true });
  10. exports.default = default_1;
  11. const core_1 = require("@angular-devkit/core");
  12. const schematics_1 = require("@angular-devkit/schematics");
  13. const tasks_1 = require("@angular-devkit/schematics/tasks");
  14. const dependencies_1 = require("../utility/dependencies");
  15. const latest_versions_1 = require("../utility/latest-versions");
  16. const paths_1 = require("../utility/paths");
  17. const workspace_1 = require("../utility/workspace");
  18. const workspace_models_1 = require("../utility/workspace-models");
  19. const schema_1 = require("./schema");
  20. function default_1(options) {
  21. return async (host, context) => {
  22. const { appDir, appRootSelector, componentOptions, folderName, sourceDir } = await getAppOptions(host, options);
  23. return (0, schematics_1.chain)([
  24. addAppToWorkspaceFile(options, appDir, folderName),
  25. options.standalone
  26. ? (0, schematics_1.noop)()
  27. : (0, schematics_1.schematic)('module', {
  28. name: 'app',
  29. commonModule: false,
  30. flat: true,
  31. routing: options.routing,
  32. routingScope: 'Root',
  33. path: sourceDir,
  34. project: options.name,
  35. }),
  36. (0, schematics_1.schematic)('component', {
  37. name: 'app',
  38. selector: appRootSelector,
  39. flat: true,
  40. path: sourceDir,
  41. skipImport: true,
  42. project: options.name,
  43. ...componentOptions,
  44. }),
  45. (0, schematics_1.mergeWith)((0, schematics_1.apply)((0, schematics_1.url)(options.standalone ? './files/standalone-files' : './files/module-files'), [
  46. options.routing ? (0, schematics_1.noop)() : (0, schematics_1.filter)((path) => !path.endsWith('app.routes.ts.template')),
  47. componentOptions.skipTests
  48. ? (0, schematics_1.filter)((path) => !path.endsWith('.spec.ts.template'))
  49. : (0, schematics_1.noop)(),
  50. (0, schematics_1.applyTemplates)({
  51. utils: schematics_1.strings,
  52. ...options,
  53. ...componentOptions,
  54. selector: appRootSelector,
  55. relativePathToWorkspaceRoot: (0, paths_1.relativePathToWorkspaceRoot)(appDir),
  56. appName: options.name,
  57. folderName,
  58. }),
  59. (0, schematics_1.move)(appDir),
  60. ]), schematics_1.MergeStrategy.Overwrite),
  61. (0, schematics_1.mergeWith)((0, schematics_1.apply)((0, schematics_1.url)('./files/common-files'), [
  62. options.minimal
  63. ? (0, schematics_1.filter)((path) => !path.endsWith('tsconfig.spec.json.template'))
  64. : (0, schematics_1.noop)(),
  65. componentOptions.inlineTemplate
  66. ? (0, schematics_1.filter)((path) => !path.endsWith('component.html.template'))
  67. : (0, schematics_1.noop)(),
  68. (0, schematics_1.applyTemplates)({
  69. utils: schematics_1.strings,
  70. ...options,
  71. selector: appRootSelector,
  72. relativePathToWorkspaceRoot: (0, paths_1.relativePathToWorkspaceRoot)(appDir),
  73. appName: options.name,
  74. folderName,
  75. }),
  76. (0, schematics_1.move)(appDir),
  77. ]), schematics_1.MergeStrategy.Overwrite),
  78. options.ssr
  79. ? (0, schematics_1.schematic)('ssr', {
  80. project: options.name,
  81. serverRouting: options.serverRouting,
  82. skipInstall: true,
  83. })
  84. : (0, schematics_1.noop)(),
  85. options.skipPackageJson ? (0, schematics_1.noop)() : addDependenciesToPackageJson(options),
  86. ]);
  87. };
  88. }
  89. function addDependenciesToPackageJson(options) {
  90. return (host, context) => {
  91. [
  92. {
  93. type: dependencies_1.NodeDependencyType.Dev,
  94. name: '@angular/compiler-cli',
  95. version: latest_versions_1.latestVersions.Angular,
  96. },
  97. {
  98. type: dependencies_1.NodeDependencyType.Dev,
  99. name: '@angular-devkit/build-angular',
  100. version: latest_versions_1.latestVersions.DevkitBuildAngular,
  101. },
  102. {
  103. type: dependencies_1.NodeDependencyType.Dev,
  104. name: 'typescript',
  105. version: latest_versions_1.latestVersions['typescript'],
  106. },
  107. ].forEach((dependency) => (0, dependencies_1.addPackageJsonDependency)(host, dependency));
  108. if (!options.skipInstall) {
  109. context.addTask(new tasks_1.NodePackageInstallTask());
  110. }
  111. return host;
  112. };
  113. }
  114. function addAppToWorkspaceFile(options, appDir, folderName) {
  115. let projectRoot = appDir;
  116. if (projectRoot) {
  117. projectRoot += '/';
  118. }
  119. const schematics = {};
  120. if (options.inlineTemplate ||
  121. options.inlineStyle ||
  122. options.minimal ||
  123. options.style !== schema_1.Style.Css) {
  124. const componentSchematicsOptions = {};
  125. if (options.inlineTemplate ?? options.minimal) {
  126. componentSchematicsOptions.inlineTemplate = true;
  127. }
  128. if (options.inlineStyle ?? options.minimal) {
  129. componentSchematicsOptions.inlineStyle = true;
  130. }
  131. if (options.style && options.style !== schema_1.Style.Css) {
  132. componentSchematicsOptions.style = options.style;
  133. }
  134. schematics['@schematics/angular:component'] = componentSchematicsOptions;
  135. }
  136. if (options.skipTests || options.minimal) {
  137. const schematicsWithTests = [
  138. 'class',
  139. 'component',
  140. 'directive',
  141. 'guard',
  142. 'interceptor',
  143. 'pipe',
  144. 'resolver',
  145. 'service',
  146. ];
  147. schematicsWithTests.forEach((type) => {
  148. (schematics[`@schematics/angular:${type}`] ??= {}).skipTests = true;
  149. });
  150. }
  151. if (!options.standalone) {
  152. const schematicsWithStandalone = ['component', 'directive', 'pipe'];
  153. schematicsWithStandalone.forEach((type) => {
  154. (schematics[`@schematics/angular:${type}`] ??= {}).standalone = false;
  155. });
  156. }
  157. const sourceRoot = (0, core_1.join)((0, core_1.normalize)(projectRoot), 'src');
  158. let budgets = [];
  159. if (options.strict) {
  160. budgets = [
  161. {
  162. type: 'initial',
  163. maximumWarning: '500kB',
  164. maximumError: '1MB',
  165. },
  166. {
  167. type: 'anyComponentStyle',
  168. maximumWarning: '4kB',
  169. maximumError: '8kB',
  170. },
  171. ];
  172. }
  173. else {
  174. budgets = [
  175. {
  176. type: 'initial',
  177. maximumWarning: '2MB',
  178. maximumError: '5MB',
  179. },
  180. {
  181. type: 'anyComponentStyle',
  182. maximumWarning: '6kB',
  183. maximumError: '10kB',
  184. },
  185. ];
  186. }
  187. const inlineStyleLanguage = options?.style !== schema_1.Style.Css ? options.style : undefined;
  188. const project = {
  189. root: (0, core_1.normalize)(projectRoot),
  190. sourceRoot,
  191. projectType: workspace_models_1.ProjectType.Application,
  192. prefix: options.prefix || 'app',
  193. schematics,
  194. targets: {
  195. build: {
  196. builder: workspace_models_1.Builders.Application,
  197. defaultConfiguration: 'production',
  198. options: {
  199. outputPath: `dist/${folderName}`,
  200. index: `${sourceRoot}/index.html`,
  201. browser: `${sourceRoot}/main.ts`,
  202. polyfills: options.experimentalZoneless ? [] : ['zone.js'],
  203. tsConfig: `${projectRoot}tsconfig.app.json`,
  204. inlineStyleLanguage,
  205. assets: [{ 'glob': '**/*', 'input': `${projectRoot}public` }],
  206. styles: [`${sourceRoot}/styles.${options.style}`],
  207. scripts: [],
  208. },
  209. configurations: {
  210. production: {
  211. budgets,
  212. outputHashing: 'all',
  213. },
  214. development: {
  215. optimization: false,
  216. extractLicenses: false,
  217. sourceMap: true,
  218. },
  219. },
  220. },
  221. serve: {
  222. builder: workspace_models_1.Builders.DevServer,
  223. defaultConfiguration: 'development',
  224. options: {},
  225. configurations: {
  226. production: {
  227. buildTarget: `${options.name}:build:production`,
  228. },
  229. development: {
  230. buildTarget: `${options.name}:build:development`,
  231. },
  232. },
  233. },
  234. 'extract-i18n': {
  235. builder: workspace_models_1.Builders.ExtractI18n,
  236. },
  237. test: options.minimal
  238. ? undefined
  239. : {
  240. builder: workspace_models_1.Builders.Karma,
  241. options: {
  242. polyfills: options.experimentalZoneless ? [] : ['zone.js', 'zone.js/testing'],
  243. tsConfig: `${projectRoot}tsconfig.spec.json`,
  244. inlineStyleLanguage,
  245. assets: [{ 'glob': '**/*', 'input': `${projectRoot}public` }],
  246. styles: [`${sourceRoot}/styles.${options.style}`],
  247. scripts: [],
  248. },
  249. },
  250. },
  251. };
  252. return (0, workspace_1.updateWorkspace)((workspace) => {
  253. workspace.projects.add({
  254. name: options.name,
  255. ...project,
  256. });
  257. });
  258. }
  259. async function getAppOptions(host, options) {
  260. const appRootSelector = `${options.prefix}-root`;
  261. const componentOptions = getComponentOptions(options);
  262. const workspace = await (0, workspace_1.getWorkspace)(host);
  263. const newProjectRoot = workspace.extensions.newProjectRoot || '';
  264. // If scoped project (i.e. "@foo/bar"), convert dir to "foo/bar".
  265. let folderName = options.name.startsWith('@') ? options.name.slice(1) : options.name;
  266. if (/[A-Z]/.test(folderName)) {
  267. folderName = schematics_1.strings.dasherize(folderName);
  268. }
  269. const appDir = options.projectRoot === undefined
  270. ? (0, core_1.join)((0, core_1.normalize)(newProjectRoot), folderName)
  271. : (0, core_1.normalize)(options.projectRoot);
  272. const sourceDir = `${appDir}/src/app`;
  273. return {
  274. appDir,
  275. appRootSelector,
  276. componentOptions,
  277. folderName,
  278. sourceDir,
  279. };
  280. }
  281. function getComponentOptions(options) {
  282. const componentOptions = !options.minimal
  283. ? {
  284. inlineStyle: options.inlineStyle,
  285. inlineTemplate: options.inlineTemplate,
  286. skipTests: options.skipTests,
  287. style: options.style,
  288. viewEncapsulation: options.viewEncapsulation,
  289. }
  290. : {
  291. inlineStyle: options.inlineStyle ?? true,
  292. inlineTemplate: options.inlineTemplate ?? true,
  293. skipTests: true,
  294. style: options.style,
  295. viewEncapsulation: options.viewEncapsulation,
  296. };
  297. return componentOptions;
  298. }