103 lines
3.7 KiB
TypeScript
103 lines
3.7 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright Google Inc. All Rights Reserved.
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
import {virtualFs} from '@angular-devkit/core';
|
|
import {Rule, Tree, chain} from '@angular-devkit/schematics';
|
|
import {getWorkspace} from '@schematics/angular/utility/config';
|
|
import {getProjectTargets} from '@schematics/angular/utility/project-targets';
|
|
import {validateProjectName} from '@schematics/angular/utility/validation';
|
|
import {BrowserBuilderTarget, Builders, ServeBuilderTarget} from '@schematics/angular/utility/workspace-models';
|
|
|
|
import {Schema} from './schema';
|
|
|
|
|
|
export const localizePolyfill = `import '@angular/localize/init';`;
|
|
|
|
function getAllOptionValues<T>(
|
|
host: Tree, projectName: string, builderName: string, optionName: string) {
|
|
const targets = getProjectTargets(host, projectName);
|
|
|
|
// Find all targets of a specific build in a project.
|
|
const builderTargets: (BrowserBuilderTarget | ServeBuilderTarget)[] =
|
|
Object.values(targets).filter(
|
|
(target: BrowserBuilderTarget | ServeBuilderTarget) => target.builder === builderName);
|
|
|
|
// Get all options contained in target configuration partials.
|
|
const configurationOptions = builderTargets.filter(t => t.configurations)
|
|
.map(t => Object.values(t.configurations !))
|
|
.reduce((acc, cur) => acc.concat(...cur), []);
|
|
|
|
// Now we have all option sets. We can use it to find all references to a given property.
|
|
const allOptions = [
|
|
...builderTargets.map(t => t.options),
|
|
...configurationOptions,
|
|
];
|
|
|
|
// Get all values for the option name and dedupe them.
|
|
// Deduping will only work for primitives, but the keys we want here are strings so it's ok.
|
|
const optionValues: T[] =
|
|
allOptions.filter(o => o[optionName])
|
|
.map(o => o[optionName])
|
|
.reduce((acc, cur) => !acc.includes(cur) ? acc.concat(cur) : acc, []);
|
|
|
|
return optionValues;
|
|
}
|
|
|
|
|
|
function prendendToTargetOptionFile(
|
|
projectName: string, builderName: string, optionName: string, str: string) {
|
|
return (host: Tree) => {
|
|
// Get all known polyfills for browser builders on this project.
|
|
const optionValues = getAllOptionValues<string>(host, projectName, builderName, optionName);
|
|
|
|
optionValues.forEach(path => {
|
|
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);
|
|
});
|
|
};
|
|
}
|
|
|
|
export default function(options: Schema): Rule {
|
|
return (host: Tree) => {
|
|
options.name = options.name || getWorkspace(host).defaultProject;
|
|
if (!options.name) {
|
|
throw new Error('Please specify a project using "--name project-name"');
|
|
}
|
|
validateProjectName(options.name);
|
|
|
|
const localizeStr =
|
|
`/***************************************************************************************************
|
|
* Load \`$localize\` onto the global scope - used if i18n tags appear in Angular templates.
|
|
*/
|
|
${localizePolyfill}
|
|
`;
|
|
|
|
return chain([
|
|
prendendToTargetOptionFile(options.name, Builders.Browser, 'polyfills', localizeStr),
|
|
prendendToTargetOptionFile(options.name, Builders.Server, 'main', localizeStr),
|
|
]);
|
|
};
|
|
}
|