parent
84feda1db7
commit
e41cbfb585
|
@ -83,6 +83,7 @@ module.exports = function(config) {
|
|||
'dist/all/@angular/examples/**/e2e_test/*',
|
||||
'dist/all/@angular/language-service/**',
|
||||
'dist/all/@angular/localize/**/test/**',
|
||||
'dist/all/@angular/localize/schematics/**',
|
||||
'dist/all/@angular/router/**/test/**',
|
||||
'dist/all/@angular/platform-browser/testing/e2e_util.js',
|
||||
'dist/all/angular1_router.js',
|
||||
|
|
|
@ -25,6 +25,9 @@ ng_package(
|
|||
"//packages/localize/init:package.json",
|
||||
],
|
||||
entry_point = ":index.ts",
|
||||
packages = [
|
||||
"//packages/localize/schematics:npm_package",
|
||||
],
|
||||
tags = [
|
||||
"release-with-framework",
|
||||
],
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
load("//tools:defaults.bzl", "npm_package")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
filegroup(
|
||||
name = "package_assets",
|
||||
srcs = [
|
||||
"collection.json",
|
||||
],
|
||||
visibility = ["//packages/localize:__subpackages__"],
|
||||
)
|
||||
|
||||
npm_package(
|
||||
name = "npm_package",
|
||||
srcs = [
|
||||
"collection.json",
|
||||
],
|
||||
deps = [
|
||||
"//packages/localize/schematics/ng-add",
|
||||
],
|
||||
)
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"$schema": "../node_modules/@angular-devkit/schematics/collection-schema.json",
|
||||
"schematics": {
|
||||
"ng-add": {
|
||||
"description": "Add @angular/localize polyfill to a project.",
|
||||
"factory": "./ng-add",
|
||||
"schema": "ng-add/schema.json"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load("//tools:defaults.bzl", "jasmine_node_test", "ts_library")
|
||||
load("@npm_bazel_typescript//:index.bzl", "ts_config")
|
||||
|
||||
ts_config(
|
||||
name = "tsconfig",
|
||||
src = "tsconfig-build.json",
|
||||
deps = ["//packages:tsconfig-build.json"],
|
||||
)
|
||||
|
||||
ts_library(
|
||||
name = "ng-add",
|
||||
srcs = [
|
||||
"index.ts",
|
||||
"schema.d.ts",
|
||||
],
|
||||
data = glob(["files/**/*"]) + [
|
||||
"schema.json",
|
||||
],
|
||||
tsconfig = ":tsconfig",
|
||||
deps = [
|
||||
"@npm//@angular-devkit/core",
|
||||
"@npm//@angular-devkit/schematics",
|
||||
"@npm//@schematics/angular",
|
||||
],
|
||||
)
|
||||
|
||||
ts_library(
|
||||
name = "test_lib",
|
||||
testonly = True,
|
||||
srcs = [
|
||||
"index_spec.ts",
|
||||
],
|
||||
data = [
|
||||
"//packages/localize/schematics:package_assets",
|
||||
],
|
||||
deps = [
|
||||
":ng-add",
|
||||
"@npm//@angular-devkit/schematics",
|
||||
],
|
||||
)
|
||||
|
||||
jasmine_node_test(
|
||||
name = "test",
|
||||
bootstrap = ["angular/tools/testing/init_node_spec.js"],
|
||||
deps = [
|
||||
":test_lib",
|
||||
"//tools/testing:node",
|
||||
],
|
||||
)
|
|
@ -0,0 +1,6 @@
|
|||
# @angular/localize schematic for `ng add`
|
||||
|
||||
This schematic will be executed when a Angular CLI user runs `ng add @angular/localize`.
|
||||
|
||||
It will search their `angular.json` file, and find polyfills and main files for server builders.
|
||||
Then it will add the `@angular/localize/init` polyfill that `@angular/localize` needs to work.
|
|
@ -0,0 +1,102 @@
|
|||
/**
|
||||
* @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),
|
||||
]);
|
||||
};
|
||||
}
|
|
@ -0,0 +1,168 @@
|
|||
/**
|
||||
* @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
|
||||
*/
|
||||
|
||||
import {HostTree} from '@angular-devkit/schematics';
|
||||
import {SchematicTestRunner, UnitTestTree} from '@angular-devkit/schematics/testing';
|
||||
|
||||
import {localizePolyfill} from './index';
|
||||
|
||||
|
||||
describe('ng-add schematic', () => {
|
||||
|
||||
const countInstances = (str: string, substr: string) => str.split(substr).length - 1;
|
||||
const defaultOptions = {name: 'demo'};
|
||||
let host: UnitTestTree;
|
||||
let schematicRunner: SchematicTestRunner;
|
||||
// The real polyfills file is bigger than this, but for the test it shouldn't matter.
|
||||
const polyfillsContent =
|
||||
`/***************************************************************************************************
|
||||
* Zone JS is required by default for Angular itself.
|
||||
*/
|
||||
import 'zone.js/dist/zone';`;
|
||||
const mainServerContent = `import { enableProdMode } from '@angular/core';
|
||||
import { environment } from './environments/environment';
|
||||
if (environment.production) {
|
||||
enableProdMode();
|
||||
}
|
||||
export { AppServerModule } from './app/app.server.module';
|
||||
export { renderModule, renderModuleFactory } from '@angular/platform-server';`;
|
||||
|
||||
beforeEach(() => {
|
||||
host = new UnitTestTree(new HostTree());
|
||||
host.create('src/polyfills.ts', polyfillsContent);
|
||||
host.create('src/another-polyfills.ts', polyfillsContent);
|
||||
host.create('src/unrelated-polyfills.ts', polyfillsContent);
|
||||
host.create('src/another-unrelated-polyfills.ts', polyfillsContent);
|
||||
host.create('src/main.server.ts', mainServerContent);
|
||||
host.create('src/another-main.server.ts', mainServerContent);
|
||||
host.create('src/unrelated-main.server.ts', mainServerContent);
|
||||
host.create('src/another-unrelated-main.server.ts', mainServerContent);
|
||||
host.create('angular.json', JSON.stringify({
|
||||
projects: {
|
||||
'demo': {
|
||||
architect: {
|
||||
build: {
|
||||
builder: '@angular-devkit/build-angular:browser',
|
||||
options: {
|
||||
polyfills: 'src/polyfills.ts',
|
||||
},
|
||||
configurations: {
|
||||
production: {
|
||||
polyfills: 'src/another-polyfills.ts',
|
||||
}
|
||||
}
|
||||
},
|
||||
'another-build': {
|
||||
builder: '@angular-devkit/build-angular:browser',
|
||||
options: {
|
||||
polyfills: 'src/polyfills.ts',
|
||||
},
|
||||
configurations: {
|
||||
production: {
|
||||
polyfills: 'src/another-polyfills.ts',
|
||||
}
|
||||
}
|
||||
},
|
||||
server: {
|
||||
builder: '@angular-devkit/build-angular:server',
|
||||
options: {
|
||||
main: 'src/main.server.ts',
|
||||
},
|
||||
configurations: {
|
||||
production: {
|
||||
main: 'src/another-main.server.ts',
|
||||
}
|
||||
}
|
||||
},
|
||||
'another-server': {
|
||||
builder: '@angular-devkit/build-angular:server',
|
||||
options: {
|
||||
main: 'src/main.server.ts',
|
||||
},
|
||||
configurations: {
|
||||
production: {
|
||||
main: 'src/another-main.server.ts',
|
||||
}
|
||||
}
|
||||
},
|
||||
'not-browser-or-server': {
|
||||
builder: '@angular-devkit/build-angular:something-else',
|
||||
options: {
|
||||
polyfills: 'src/unrelated-polyfills.ts',
|
||||
main: 'src/unrelated-main.server.ts',
|
||||
},
|
||||
configurations: {
|
||||
production: {
|
||||
polyfills: 'src/other-unrelated-polyfills.ts',
|
||||
main: 'src/another-unrelated-main.server.ts',
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
defaultProject: 'demo',
|
||||
}));
|
||||
schematicRunner =
|
||||
new SchematicTestRunner('@angular/localize', require.resolve('../collection.json'));
|
||||
});
|
||||
|
||||
it('should add localize polyfill to polyfill files', async() => {
|
||||
host = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise();
|
||||
expect(host.readContent('/src/polyfills.ts')).toContain(localizePolyfill);
|
||||
expect(host.readContent('/src/another-polyfills.ts')).toContain(localizePolyfill);
|
||||
});
|
||||
|
||||
it('should add localize polyfill to server main files', async() => {
|
||||
host = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise();
|
||||
expect(host.readContent('/src/main.server.ts')).toContain(localizePolyfill);
|
||||
expect(host.readContent('/src/another-main.server.ts')).toContain(localizePolyfill);
|
||||
});
|
||||
|
||||
it('should add localize polyfill at the start of file', async() => {
|
||||
host = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise();
|
||||
const content = host.readContent('/src/polyfills.ts');
|
||||
expect(content.indexOf(localizePolyfill)).toBeLessThan(content.indexOf(polyfillsContent));
|
||||
});
|
||||
|
||||
it('should not add localize polyfill to files referenced in other targets files', async() => {
|
||||
host = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise();
|
||||
expect(host.readContent('/src/unrelated-polyfills.ts')).not.toContain(localizePolyfill);
|
||||
expect(host.readContent('/src/another-unrelated-polyfills.ts')).not.toContain(localizePolyfill);
|
||||
expect(host.readContent('/src/unrelated-main.server.ts')).not.toContain(localizePolyfill);
|
||||
expect(host.readContent('/src/another-unrelated-main.server.ts'))
|
||||
.not.toContain(localizePolyfill);
|
||||
});
|
||||
|
||||
it('should only add localize polyfill once if multiple builds reference it', async() => {
|
||||
host = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise();
|
||||
const content = host.readContent('/src/polyfills.ts');
|
||||
expect(countInstances(content, localizePolyfill)).toBe(1);
|
||||
});
|
||||
|
||||
it('should not add localize polyfill if it\'s already there', async() => {
|
||||
const polyfillVariation = localizePolyfill.replace(/'/g, '"');
|
||||
host.overwrite('/src/polyfills.ts', `${localizePolyfill}\n${polyfillsContent}`);
|
||||
host.overwrite('/src/another-polyfills.ts', `${polyfillVariation}\n${polyfillsContent}`);
|
||||
host = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise();
|
||||
expect(countInstances(host.readContent('/src/polyfills.ts'), localizePolyfill)).toBe(1);
|
||||
expect(countInstances(host.readContent('/src/another-polyfills.ts'), localizePolyfill)).toBe(0);
|
||||
});
|
||||
|
||||
it('should not break when there are no polyfills', async() => {
|
||||
host.overwrite('angular.json', JSON.stringify({
|
||||
projects: {
|
||||
'demo': {
|
||||
architect: {},
|
||||
}
|
||||
},
|
||||
defaultProject: 'demo',
|
||||
}));
|
||||
await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,14 @@
|
|||
/**
|
||||
* @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
|
||||
*/
|
||||
|
||||
export interface Schema {
|
||||
/**
|
||||
* The name of the project.
|
||||
*/
|
||||
name?: string;
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"$schema": "http://json-schema.org/schema",
|
||||
"id": "SchematicsAngularLocalizeNgAdd",
|
||||
"title": "Angular Localize Ng Add Schema",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The name of the project.",
|
||||
"$default": {
|
||||
"$source": "projectName"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
]
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"extends": "../../../tsconfig-build.json",
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"stripInternal": false,
|
||||
"target": "es2015",
|
||||
"lib": [
|
||||
"es2015",
|
||||
"es2017.object",
|
||||
],
|
||||
},
|
||||
"bazelOptions": {
|
||||
"suppressTsconfigOverrideWarnings": true,
|
||||
},
|
||||
"exclude": [
|
||||
"index_spec.ts",
|
||||
],
|
||||
"files": [
|
||||
"index.ts",
|
||||
]
|
||||
}
|
Loading…
Reference in New Issue