files.ts (3853B)
1 /** 2 * @license 3 * Copyright 2022 Google Inc. 4 * SPDX-License-Identifier: Apache-2.0 5 */ 6 7 import {relative, resolve} from 'node:path'; 8 9 import {getSystemPath, normalize, strings} from '@angular-devkit/core'; 10 import type {Rule} from '@angular-devkit/schematics'; 11 import { 12 apply, 13 applyTemplates, 14 chain, 15 mergeWith, 16 move, 17 url, 18 } from '@angular-devkit/schematics'; 19 20 import type {AngularProject, TestRunner} from './types.js'; 21 22 export interface FilesOptions { 23 options: { 24 testRunner: TestRunner; 25 port: number; 26 name?: string; 27 exportConfig?: boolean; 28 ext?: string; 29 route?: string; 30 }; 31 applyPath: string; 32 relativeToWorkspacePath: string; 33 movePath?: string; 34 } 35 36 export function addFilesToProjects( 37 projects: Record<string, AngularProject>, 38 options: FilesOptions, 39 ): Rule { 40 return chain( 41 Object.keys(projects).map(name => { 42 return addFilesSingle(name, projects[name] as AngularProject, options); 43 }), 44 ); 45 } 46 47 export function addFilesSingle( 48 name: string, 49 project: AngularProject, 50 {options, applyPath, movePath, relativeToWorkspacePath}: FilesOptions, 51 ): Rule { 52 const projectPath = resolve(getSystemPath(normalize(project.root))); 53 const workspacePath = resolve(getSystemPath(normalize(''))); 54 55 const relativeToWorkspace = relative( 56 `${projectPath}${relativeToWorkspacePath}`, 57 workspacePath, 58 ); 59 60 const baseUrl = getProjectBaseUrl(project, options.port); 61 const tsConfigPath = getTsConfigPath(project); 62 63 return mergeWith( 64 apply(url(applyPath), [ 65 move(movePath ? `${project.root}${movePath}` : project.root), 66 applyTemplates({ 67 ...options, 68 ...strings, 69 root: project.root ? `${project.root}/` : project.root, 70 baseUrl, 71 tsConfigPath, 72 project: name, 73 relativeToWorkspace, 74 }), 75 ]), 76 ); 77 } 78 79 function getProjectBaseUrl(project: AngularProject, port: number): string { 80 let options = {protocol: 'http', port, host: 'localhost'}; 81 82 if (project.architect?.serve?.options) { 83 const projectOptions = project.architect?.serve?.options; 84 const projectPort = port !== 4200 ? port : (projectOptions?.port ?? port); 85 options = {...options, ...projectOptions, port: projectPort}; 86 options.protocol = projectOptions.ssl ? 'https' : 'http'; 87 } 88 89 return `${options.protocol}://${options.host}:${options.port}/`; 90 } 91 92 function getTsConfigPath(project: AngularProject): string { 93 const filename = 'tsconfig.json'; 94 95 if (!project.root) { 96 return `../${filename}`; 97 } 98 99 const nested = project.root 100 .split('/') 101 .map(() => { 102 return '../'; 103 }) 104 .join(''); 105 106 // Prepend a single `../` as we put the test inside `e2e` folder 107 return `../${nested}${filename}`; 108 } 109 110 export function addCommonFiles( 111 projects: Record<string, AngularProject>, 112 filesOptions: Omit<FilesOptions, 'applyPath' | 'relativeToWorkspacePath'>, 113 ): Rule { 114 const options: FilesOptions = { 115 ...filesOptions, 116 applyPath: './files/common', 117 relativeToWorkspacePath: `/`, 118 }; 119 120 return addFilesToProjects(projects, options); 121 } 122 123 export function addFrameworkFiles( 124 projects: Record<string, AngularProject>, 125 filesOptions: Omit<FilesOptions, 'applyPath' | 'relativeToWorkspacePath'>, 126 ): Rule { 127 const testRunner = filesOptions.options.testRunner; 128 const options: FilesOptions = { 129 ...filesOptions, 130 applyPath: `./files/${testRunner}`, 131 relativeToWorkspacePath: `/`, 132 }; 133 134 return addFilesToProjects(projects, options); 135 } 136 137 export function hasE2ETester( 138 projects: Record<string, AngularProject>, 139 ): boolean { 140 return Object.values(projects).some((project: AngularProject) => { 141 return Boolean(project.architect?.e2e); 142 }); 143 } 144 145 export function getNgCommandName( 146 projects: Record<string, AngularProject>, 147 ): string { 148 if (!hasE2ETester(projects)) { 149 return 'e2e'; 150 } 151 return 'puppeteer'; 152 }