index.js 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  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 node_path_1 = require("node:path");
  14. const utility_1 = require("../utility");
  15. const dependencies_1 = require("../utility/dependencies");
  16. const json_file_1 = require("../utility/json-file");
  17. const latest_versions_1 = require("../utility/latest-versions");
  18. const ng_ast_utils_1 = require("../utility/ng-ast-utils");
  19. const paths_1 = require("../utility/paths");
  20. const project_targets_1 = require("../utility/project-targets");
  21. const util_1 = require("../utility/standalone/util");
  22. const workspace_1 = require("../utility/workspace");
  23. const workspace_models_1 = require("../utility/workspace-models");
  24. const serverMainEntryName = 'main.server.ts';
  25. function updateConfigFileBrowserBuilder(options, tsConfigDirectory) {
  26. return (0, workspace_1.updateWorkspace)((workspace) => {
  27. const clientProject = workspace.projects.get(options.project);
  28. if (clientProject) {
  29. // In case the browser builder hashes the assets
  30. // we need to add this setting to the server builder
  31. // as otherwise when assets it will be requested twice.
  32. // One for the server which will be unhashed, and other on the client which will be hashed.
  33. const getServerOptions = (options = {}) => {
  34. return {
  35. buildOptimizer: options?.buildOptimizer,
  36. outputHashing: options?.outputHashing === 'all' ? 'media' : options?.outputHashing,
  37. fileReplacements: options?.fileReplacements,
  38. optimization: options?.optimization === undefined ? undefined : !!options?.optimization,
  39. sourceMap: options?.sourceMap,
  40. localization: options?.localization,
  41. stylePreprocessorOptions: options?.stylePreprocessorOptions,
  42. resourcesOutputPath: options?.resourcesOutputPath,
  43. deployUrl: options?.deployUrl,
  44. i18nMissingTranslation: options?.i18nMissingTranslation,
  45. preserveSymlinks: options?.preserveSymlinks,
  46. extractLicenses: options?.extractLicenses,
  47. inlineStyleLanguage: options?.inlineStyleLanguage,
  48. vendorChunk: options?.vendorChunk,
  49. };
  50. };
  51. const buildTarget = clientProject.targets.get('build');
  52. if (buildTarget?.options) {
  53. buildTarget.options.outputPath = `dist/${options.project}/browser`;
  54. }
  55. const buildConfigurations = buildTarget?.configurations;
  56. const configurations = {};
  57. if (buildConfigurations) {
  58. for (const [key, options] of Object.entries(buildConfigurations)) {
  59. configurations[key] = getServerOptions(options);
  60. }
  61. }
  62. const sourceRoot = clientProject.sourceRoot ?? (0, core_1.join)((0, core_1.normalize)(clientProject.root), 'src');
  63. const serverTsConfig = (0, core_1.join)(tsConfigDirectory, 'tsconfig.server.json');
  64. clientProject.targets.add({
  65. name: 'server',
  66. builder: workspace_models_1.Builders.Server,
  67. defaultConfiguration: 'production',
  68. options: {
  69. outputPath: `dist/${options.project}/server`,
  70. main: (0, core_1.join)((0, core_1.normalize)(sourceRoot), serverMainEntryName),
  71. tsConfig: serverTsConfig,
  72. ...(buildTarget?.options ? getServerOptions(buildTarget?.options) : {}),
  73. },
  74. configurations,
  75. });
  76. }
  77. });
  78. }
  79. function updateConfigFileApplicationBuilder(options) {
  80. return (0, workspace_1.updateWorkspace)((workspace) => {
  81. const project = workspace.projects.get(options.project);
  82. if (!project) {
  83. return;
  84. }
  85. const buildTarget = project.targets.get('build');
  86. if (!buildTarget) {
  87. return;
  88. }
  89. buildTarget.options ??= {};
  90. buildTarget.options['server'] = node_path_1.posix.join(project.sourceRoot ?? node_path_1.posix.join(project.root, 'src'), serverMainEntryName);
  91. if (options.serverRouting) {
  92. buildTarget.options['outputMode'] = 'static';
  93. }
  94. });
  95. }
  96. function updateTsConfigFile(tsConfigPath) {
  97. return (host) => {
  98. const json = new json_file_1.JSONFile(host, tsConfigPath);
  99. const filesPath = ['files'];
  100. const files = new Set(json.get(filesPath) ?? []);
  101. files.add('src/' + serverMainEntryName);
  102. json.modify(filesPath, [...files]);
  103. const typePath = ['compilerOptions', 'types'];
  104. const types = new Set(json.get(typePath) ?? []);
  105. types.add('node');
  106. json.modify(typePath, [...types]);
  107. };
  108. }
  109. function addDependencies(skipInstall) {
  110. return (host) => {
  111. const coreDep = (0, dependencies_1.getPackageJsonDependency)(host, '@angular/core');
  112. if (coreDep === null) {
  113. throw new schematics_1.SchematicsException('Could not find version.');
  114. }
  115. const install = skipInstall ? utility_1.InstallBehavior.None : utility_1.InstallBehavior.Auto;
  116. return (0, schematics_1.chain)([
  117. (0, utility_1.addDependency)('@angular/ssr', latest_versions_1.latestVersions.AngularSSR, {
  118. type: utility_1.DependencyType.Default,
  119. install,
  120. }),
  121. (0, utility_1.addDependency)('@angular/platform-server', coreDep.version, {
  122. type: utility_1.DependencyType.Default,
  123. install,
  124. }),
  125. (0, utility_1.addDependency)('@types/node', latest_versions_1.latestVersions['@types/node'], {
  126. type: utility_1.DependencyType.Dev,
  127. install,
  128. }),
  129. ]);
  130. };
  131. }
  132. function default_1(options) {
  133. return async (host) => {
  134. const workspace = await (0, workspace_1.getWorkspace)(host);
  135. const clientProject = workspace.projects.get(options.project);
  136. if (clientProject?.extensions.projectType !== 'application') {
  137. throw new schematics_1.SchematicsException(`Server schematic requires a project type of "application".`);
  138. }
  139. const clientBuildTarget = clientProject.targets.get('build');
  140. if (!clientBuildTarget) {
  141. throw (0, project_targets_1.targetBuildNotFoundError)();
  142. }
  143. const isUsingApplicationBuilder = clientBuildTarget.builder === workspace_models_1.Builders.Application ||
  144. clientBuildTarget.builder === workspace_models_1.Builders.BuildApplication;
  145. if (clientProject.targets.has('server') ||
  146. (isUsingApplicationBuilder && clientBuildTarget.options?.server !== undefined)) {
  147. // Server has already been added.
  148. return;
  149. }
  150. const clientBuildOptions = clientBuildTarget.options;
  151. const browserEntryPoint = await (0, util_1.getMainFilePath)(host, options.project);
  152. const isStandalone = (0, ng_ast_utils_1.isStandaloneApp)(host, browserEntryPoint);
  153. const sourceRoot = clientProject.sourceRoot ?? (0, core_1.join)((0, core_1.normalize)(clientProject.root), 'src');
  154. let filesUrl = `./files/${isUsingApplicationBuilder ? 'application-builder/' : 'server-builder/'}`;
  155. filesUrl += isStandalone ? 'standalone-src' : 'ngmodule-src';
  156. const templateSource = (0, schematics_1.apply)((0, schematics_1.url)(filesUrl), [
  157. options.serverRouting
  158. ? (0, schematics_1.noop)()
  159. : (0, schematics_1.filter)((path) => !path.endsWith('app.routes.server.ts.template')),
  160. (0, schematics_1.applyTemplates)({
  161. ...schematics_1.strings,
  162. ...options,
  163. }),
  164. (0, schematics_1.move)(sourceRoot),
  165. ]);
  166. const clientTsConfig = (0, core_1.normalize)(clientBuildOptions.tsConfig);
  167. const tsConfigExtends = (0, core_1.basename)(clientTsConfig);
  168. const tsConfigDirectory = (0, core_1.dirname)(clientTsConfig);
  169. return (0, schematics_1.chain)([
  170. (0, schematics_1.mergeWith)(templateSource),
  171. ...(isUsingApplicationBuilder
  172. ? [
  173. updateConfigFileApplicationBuilder(options),
  174. updateTsConfigFile(clientBuildOptions.tsConfig),
  175. ]
  176. : [
  177. (0, schematics_1.mergeWith)((0, schematics_1.apply)((0, schematics_1.url)('./files/server-builder/root'), [
  178. (0, schematics_1.applyTemplates)({
  179. ...schematics_1.strings,
  180. ...options,
  181. stripTsExtension: (s) => s.replace(/\.ts$/, ''),
  182. tsConfigExtends,
  183. hasLocalizePackage: !!(0, dependencies_1.getPackageJsonDependency)(host, '@angular/localize'),
  184. relativePathToWorkspaceRoot: (0, paths_1.relativePathToWorkspaceRoot)(tsConfigDirectory),
  185. }),
  186. (0, schematics_1.move)(tsConfigDirectory),
  187. ])),
  188. updateConfigFileBrowserBuilder(options, tsConfigDirectory),
  189. ]),
  190. addDependencies(options.skipInstall),
  191. (0, utility_1.addRootProvider)(options.project, ({ code, external }) => code `${external('provideClientHydration', '@angular/platform-browser')}(${external('withEventReplay', '@angular/platform-browser')}())`),
  192. ]);
  193. };
  194. }