index.js 10 KB

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