| 
									
										
										
										
											2019-09-20 16:05:45 +01:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @license | 
					
						
							| 
									
										
										
										
											2020-05-19 12:08:49 -07:00
										 |  |  |  * Copyright Google LLC All Rights Reserved. | 
					
						
							| 
									
										
										
										
											2019-09-20 16:05:45 +01:00
										 |  |  |  * | 
					
						
							|  |  |  |  * Use of this source code is governed by an MIT-style license that can be | 
					
						
							|  |  |  |  * found in the LICENSE file at https://angular.io/license
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @fileoverview Schematics for ng-new project that builds with Bazel. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-05 12:47:12 +03:00
										 |  |  | import {virtualFs, workspaces} from '@angular-devkit/core'; | 
					
						
							| 
									
										
										
										
											2020-09-02 16:16:10 +01:00
										 |  |  | import {chain, noop, Rule, SchematicContext, SchematicsException, Tree} from '@angular-devkit/schematics'; | 
					
						
							|  |  |  | import {NodePackageInstallTask} from '@angular-devkit/schematics/tasks'; | 
					
						
							|  |  |  | import {addPackageJsonDependency, NodeDependencyType, removePackageJsonDependency} from '@schematics/angular/utility/dependencies'; | 
					
						
							| 
									
										
										
										
											2020-05-05 12:47:12 +03:00
										 |  |  | import {getWorkspace} from '@schematics/angular/utility/workspace'; | 
					
						
							|  |  |  | import {Builders} from '@schematics/angular/utility/workspace-models'; | 
					
						
							| 
									
										
										
										
											2019-09-20 16:05:45 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | import {Schema} from './schema'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export const localizePolyfill = `import '@angular/localize/init';`; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-05 12:47:12 +03:00
										 |  |  | function getRelevantTargetDefinitions( | 
					
						
							|  |  |  |     project: workspaces.ProjectDefinition, builderName: Builders): workspaces.TargetDefinition[] { | 
					
						
							|  |  |  |   const definitions: workspaces.TargetDefinition[] = []; | 
					
						
							|  |  |  |   project.targets.forEach((target: workspaces.TargetDefinition): void => { | 
					
						
							|  |  |  |     if (target.builder === builderName) { | 
					
						
							|  |  |  |       definitions.push(target); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  |   return definitions; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-09-20 16:05:45 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-05 12:47:12 +03:00
										 |  |  | function getOptionValuesForTargetDefinition( | 
					
						
							|  |  |  |     definition: workspaces.TargetDefinition, optionName: string): string[] { | 
					
						
							|  |  |  |   const optionValues: string[] = []; | 
					
						
							|  |  |  |   if (definition.options && optionName in definition.options) { | 
					
						
							|  |  |  |     let optionValue: unknown = definition.options[optionName]; | 
					
						
							|  |  |  |     if (typeof optionValue === 'string') { | 
					
						
							|  |  |  |       optionValues.push(optionValue); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   if (!definition.configurations) { | 
					
						
							|  |  |  |     return optionValues; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   Object.values(definition.configurations) | 
					
						
							|  |  |  |       .forEach((configuration: Record<string, unknown>|undefined): void => { | 
					
						
							|  |  |  |         if (configuration && optionName in configuration) { | 
					
						
							|  |  |  |           const optionValue: unknown = configuration[optionName]; | 
					
						
							|  |  |  |           if (typeof optionValue === 'string') { | 
					
						
							|  |  |  |             optionValues.push(optionValue); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2019-09-20 16:05:45 +01:00
										 |  |  |   return optionValues; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-05 12:47:12 +03:00
										 |  |  | function getFileListForRelevantTargetDefinitions( | 
					
						
							|  |  |  |     project: workspaces.ProjectDefinition, builderName: Builders, optionName: string): string[] { | 
					
						
							|  |  |  |   const fileList: string[] = []; | 
					
						
							|  |  |  |   const definitions = getRelevantTargetDefinitions(project, builderName); | 
					
						
							|  |  |  |   definitions.forEach((definition: workspaces.TargetDefinition): void => { | 
					
						
							|  |  |  |     const optionValues = getOptionValuesForTargetDefinition(definition, optionName); | 
					
						
							|  |  |  |     optionValues.forEach((filePath: string): void => { | 
					
						
							|  |  |  |       if (fileList.indexOf(filePath) === -1) { | 
					
						
							|  |  |  |         fileList.push(filePath); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  |   return fileList; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-09-20 16:05:45 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-05 12:47:12 +03:00
										 |  |  | function prependToTargetFiles( | 
					
						
							|  |  |  |     project: workspaces.ProjectDefinition, builderName: Builders, optionName: string, str: string) { | 
					
						
							| 
									
										
										
										
											2019-09-20 16:05:45 +01:00
										 |  |  |   return (host: Tree) => { | 
					
						
							| 
									
										
										
										
											2020-05-05 12:47:12 +03:00
										 |  |  |     const fileList = getFileListForRelevantTargetDefinitions(project, builderName, optionName); | 
					
						
							| 
									
										
										
										
											2019-09-20 16:05:45 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-05 12:47:12 +03:00
										 |  |  |     fileList.forEach((path: string): void => { | 
					
						
							| 
									
										
										
										
											2019-09-20 16:05:45 +01:00
										 |  |  |       const data = host.read(path); | 
					
						
							|  |  |  |       if (!data) { | 
					
						
							|  |  |  |         // If the file doesn't exist, just ignore it.
 | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       const content = virtualFs.fileBufferToString(data); | 
					
						
							|  |  |  |       if (content.includes(localizePolyfill) || | 
					
						
							|  |  |  |           content.includes(localizePolyfill.replace(/'/g, '"'))) { | 
					
						
							|  |  |  |         // If the file already contains the polyfill (or variations), ignore it too.
 | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // Add string at the start of the file.
 | 
					
						
							|  |  |  |       const recorder = host.beginUpdate(path); | 
					
						
							|  |  |  |       recorder.insertLeft(0, str); | 
					
						
							|  |  |  |       host.commitUpdate(recorder); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-02 16:16:10 +01:00
										 |  |  | function moveToDependencies(host: Tree, context: SchematicContext) { | 
					
						
							|  |  |  |   if (host.exists('package.json')) { | 
					
						
							|  |  |  |     // Remove the previous dependency and add in a new one under the desired type.
 | 
					
						
							|  |  |  |     removePackageJsonDependency(host, '@angular/localize'); | 
					
						
							|  |  |  |     addPackageJsonDependency(host, { | 
					
						
							|  |  |  |       name: '@angular/localize', | 
					
						
							|  |  |  |       type: NodeDependencyType.Default, | 
					
						
							|  |  |  |       version: `~0.0.0-PLACEHOLDER` | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Add a task to run the package manager. This is necessary because we updated
 | 
					
						
							|  |  |  |     // "package.json" and we want lock files to reflect this.
 | 
					
						
							|  |  |  |     context.addTask(new NodePackageInstallTask()); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-20 16:05:45 +01:00
										 |  |  | export default function(options: Schema): Rule { | 
					
						
							| 
									
										
										
										
											2020-05-05 12:47:12 +03:00
										 |  |  |   return async (host: Tree) => { | 
					
						
							| 
									
										
										
										
											2019-09-20 16:05:45 +01:00
										 |  |  |     if (!options.name) { | 
					
						
							| 
									
										
										
										
											2020-05-05 12:47:12 +03:00
										 |  |  |       throw new SchematicsException('Option "name" is required.'); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const workspace = await getWorkspace(host); | 
					
						
							|  |  |  |     const project: workspaces.ProjectDefinition|undefined = workspace.projects.get(options.name); | 
					
						
							|  |  |  |     if (!project) { | 
					
						
							|  |  |  |       throw new SchematicsException(`Invalid project name (${options.name})`); | 
					
						
							| 
									
										
										
										
											2019-09-20 16:05:45 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const localizeStr = | 
					
						
							|  |  |  |         `/***************************************************************************************************
 | 
					
						
							|  |  |  |  * Load \`$localize\` onto the global scope - used if i18n tags appear in Angular templates.
 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | ${localizePolyfill} | 
					
						
							|  |  |  | `;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return chain([ | 
					
						
							| 
									
										
										
										
											2020-05-05 12:47:12 +03:00
										 |  |  |       prependToTargetFiles(project, Builders.Browser, 'polyfills', localizeStr), | 
					
						
							|  |  |  |       prependToTargetFiles(project, Builders.Server, 'main', localizeStr), | 
					
						
							| 
									
										
										
										
											2020-09-02 16:16:10 +01:00
										 |  |  |       // If `$localize` will be used at runtime then must install `@angular/localize`
 | 
					
						
							|  |  |  |       // into `dependencies`, rather than the default of `devDependencies`.
 | 
					
						
							|  |  |  |       options.useAtRuntime ? moveToDependencies : noop() | 
					
						
							| 
									
										
										
										
											2019-09-20 16:05:45 +01:00
										 |  |  |     ]); | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | } |