index.js 12 KB

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