refactor(elements): ng-add schematics (#33723)
This PR brings a couple of changes; - Removes undeed dependencies in bazel targets such as `//packages/common` & `//packages/core` - Removes RxJs usage - Adds `document-register-element` to architect test targets - Use @schematics/angular helpers - Uses the standard `$source": "projectName"` to get the projectName, which is defined in the `schema.json` - Use workspace writer to update the workspace config PR Close #33723
This commit is contained in:
parent
de043a4bcb
commit
588823b437
|
@ -3,7 +3,8 @@
|
||||||
"schematics": {
|
"schematics": {
|
||||||
"ng-add": {
|
"ng-add": {
|
||||||
"description": "Adds the document-register-element polyfill.",
|
"description": "Adds the document-register-element polyfill.",
|
||||||
"factory": "./ng-add"
|
"factory": "./ng-add",
|
||||||
|
"schema": "ng-add/schema.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,10 +10,8 @@ ts_library(
|
||||||
"schema.ts",
|
"schema.ts",
|
||||||
],
|
],
|
||||||
deps = [
|
deps = [
|
||||||
"//packages/common",
|
|
||||||
"//packages/core",
|
|
||||||
"@npm//@angular-devkit/schematics",
|
"@npm//@angular-devkit/schematics",
|
||||||
"@npm//rxjs",
|
"@npm//@schematics/angular",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -23,12 +21,13 @@ ts_library(
|
||||||
srcs = [
|
srcs = [
|
||||||
"index_spec.ts",
|
"index_spec.ts",
|
||||||
],
|
],
|
||||||
|
data = [
|
||||||
|
"schema.json",
|
||||||
|
],
|
||||||
deps = [
|
deps = [
|
||||||
":ng-add",
|
":ng-add",
|
||||||
"//packages/common",
|
|
||||||
"//packages/core",
|
|
||||||
"@npm//@angular-devkit/schematics",
|
"@npm//@angular-devkit/schematics",
|
||||||
"@npm//rxjs",
|
"@npm//@schematics/angular",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -37,6 +36,5 @@ jasmine_node_test(
|
||||||
deps = [
|
deps = [
|
||||||
":test_lib",
|
":test_lib",
|
||||||
"//packages/elements/schematics:collection",
|
"//packages/elements/schematics:collection",
|
||||||
"@npm//@schematics/angular",
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
|
@ -5,72 +5,74 @@
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
* 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
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
import {Rule, SchematicContext, Tree, chain, noop} from '@angular-devkit/schematics';
|
import {Rule, SchematicContext, SchematicsException, Tree, chain, noop} from '@angular-devkit/schematics';
|
||||||
import {NodePackageInstallTask} from '@angular-devkit/schematics/tasks';
|
import {NodePackageInstallTask} from '@angular-devkit/schematics/tasks';
|
||||||
|
import {NodeDependencyType, addPackageJsonDependency} from '@schematics/angular/utility/dependencies';
|
||||||
|
import {getWorkspace} from '@schematics/angular/utility/workspace';
|
||||||
|
|
||||||
import {Schema} from './schema';
|
import {Schema} from './schema';
|
||||||
|
|
||||||
export default function(options: Schema): Rule {
|
export default function(options: Schema): Rule {
|
||||||
return chain([
|
return chain([
|
||||||
options && options.skipPackageJson ? noop() : addPackageJsonDependency(), addScript(options)
|
options && options.skipPackageJson ? noop() : addPolyfillDependency(),
|
||||||
|
addPolyfill(options),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Adds a package.json dependency for document-register-element */
|
/** Adds a package.json dependency for document-register-element */
|
||||||
function addPackageJsonDependency() {
|
function addPolyfillDependency(): Rule {
|
||||||
return (host: Tree, context: SchematicContext) => {
|
return (host: Tree, context: SchematicContext) => {
|
||||||
|
addPackageJsonDependency(host, {
|
||||||
if (host.exists('package.json')) {
|
type: NodeDependencyType.Default,
|
||||||
const jsonStr = host.read('package.json') !.toString('utf-8');
|
name: 'document-register-element',
|
||||||
const json = JSON.parse(jsonStr);
|
version: '^1.7.2',
|
||||||
|
});
|
||||||
// If there are no dependencies, create an entry for dependencies.
|
context.logger.info('Added "document-register-element" as a dependency.');
|
||||||
const type = 'dependencies';
|
|
||||||
if (!json[type]) {
|
|
||||||
json[type] = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
// If not already present, add the dependency.
|
|
||||||
const pkg = 'document-register-element';
|
|
||||||
const version = '^1.7.2';
|
|
||||||
if (!json[type][pkg]) {
|
|
||||||
json[type][pkg] = version;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write the JSON back to package.json
|
|
||||||
host.overwrite('package.json', JSON.stringify(json, null, 2));
|
|
||||||
context.logger.log('info', 'Added `document-register-element` as a dependency.');
|
|
||||||
|
|
||||||
// Install the dependency
|
// Install the dependency
|
||||||
context.addTask(new NodePackageInstallTask());
|
context.addTask(new NodePackageInstallTask());
|
||||||
}
|
|
||||||
|
|
||||||
return host;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Adds the document-register-element.js script to the angular CLI json. */
|
/** Adds the document-register-element.js to the polyfills file. */
|
||||||
function addScript(options: Schema) {
|
function addPolyfill(options: Schema): Rule {
|
||||||
return (host: Tree, context: SchematicContext) => {
|
return async(host: Tree, context: SchematicContext) => {
|
||||||
const script = 'node_modules/document-register-element/build/document-register-element.js';
|
const projectName = options.project;
|
||||||
|
|
||||||
|
if (!projectName) {
|
||||||
try {
|
throw new SchematicsException('Option "project" is required.');
|
||||||
// Handle the new json - angular.json
|
|
||||||
const angularJsonFile = host.read('angular.json');
|
|
||||||
if (angularJsonFile) {
|
|
||||||
const json = JSON.parse(angularJsonFile.toString('utf-8'));
|
|
||||||
const project = Object.keys(json['projects'])[0] || options.project;
|
|
||||||
const scripts = json['projects'][project]['architect']['build']['options']['scripts'];
|
|
||||||
scripts.push({input: script});
|
|
||||||
host.overwrite('angular.json', JSON.stringify(json, null, 2));
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
context.logger.log(
|
|
||||||
'warn', 'Failed to add the polyfill document-register-element.js to scripts');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
context.logger.log('info', 'Added document-register-element.js polyfill to scripts');
|
const workspace = await getWorkspace(host);
|
||||||
|
const project = workspace.projects.get(projectName);
|
||||||
|
|
||||||
return host;
|
if (!project) {
|
||||||
|
throw new SchematicsException(`Project ${projectName} is not defined in this workspace.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (project.extensions['projectType'] !== 'application') {
|
||||||
|
throw new SchematicsException(
|
||||||
|
`@angular/elements requires a project type of "application" but ${projectName} isn't.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const buildTarget = project.targets.get('build');
|
||||||
|
if (!buildTarget || !buildTarget.options) {
|
||||||
|
throw new SchematicsException(`Cannot find 'options' for ${projectName} build target.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const {polyfills} = buildTarget.options;
|
||||||
|
if (typeof polyfills !== 'string') {
|
||||||
|
throw new SchematicsException(`polyfills for ${projectName} build target is not a string.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const content = host.read(polyfills).toString();
|
||||||
|
if (!content.includes('document-register-element')) {
|
||||||
|
// Add string at the end of the file.
|
||||||
|
const recorder = host.beginUpdate(polyfills);
|
||||||
|
recorder.insertRight(content.length, `import 'document-register-element';\n`);
|
||||||
|
host.commitUpdate(recorder);
|
||||||
|
}
|
||||||
|
|
||||||
|
context.logger.info('Added "document-register-element" to polyfills.');
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,19 +7,14 @@
|
||||||
*/
|
*/
|
||||||
import {SchematicTestRunner, UnitTestTree} from '@angular-devkit/schematics/testing';
|
import {SchematicTestRunner, UnitTestTree} from '@angular-devkit/schematics/testing';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import {Observable} from 'rxjs';
|
|
||||||
import {concatMap} from 'rxjs/operators';
|
|
||||||
|
|
||||||
import {Schema as ElementsOptions} from './schema';
|
import {Schema as ElementsOptions} from './schema';
|
||||||
|
|
||||||
|
|
||||||
const polyfillPath = 'node_modules/document-register-element/build/document-register-element.js';
|
|
||||||
|
|
||||||
// tslint:disable:max-line-length
|
// tslint:disable:max-line-length
|
||||||
describe('Elements Schematics', () => {
|
describe('Elements Schematics', () => {
|
||||||
const schematicRunner = new SchematicTestRunner(
|
const schematicRunner = new SchematicTestRunner(
|
||||||
'@angular/elements', path.join(__dirname, '../test-collection.json'), );
|
'@angular/elements', path.join(__dirname, '../test-collection.json'), );
|
||||||
const defaultOptions: ElementsOptions = {project: 'bar', skipPackageJson: false};
|
const defaultOptions: ElementsOptions = {project: 'elements', skipPackageJson: false};
|
||||||
|
|
||||||
let appTree: UnitTestTree;
|
let appTree: UnitTestTree;
|
||||||
|
|
||||||
|
@ -40,20 +35,29 @@ describe('Elements Schematics', () => {
|
||||||
skipTests: false,
|
skipTests: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach((done) => {
|
beforeEach(async() => {
|
||||||
schematicRunner.runExternalSchematicAsync('@schematics/angular', 'workspace', workspaceOptions)
|
appTree = await schematicRunner
|
||||||
.pipe(concatMap(
|
.runExternalSchematicAsync('@schematics/angular', 'workspace', workspaceOptions)
|
||||||
(tree) => schematicRunner.runExternalSchematicAsync(
|
.toPromise();
|
||||||
'@schematics/angular', 'application', appOptions, tree)))
|
appTree =
|
||||||
.subscribe((tree: UnitTestTree) => appTree = tree, done.fail, done);
|
await schematicRunner
|
||||||
|
.runExternalSchematicAsync('@schematics/angular', 'application', appOptions, appTree)
|
||||||
|
.toPromise();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should run the ng-add schematic', async() => {
|
it('should run the ng-add schematic', async() => {
|
||||||
const tree =
|
const tree =
|
||||||
await schematicRunner.runSchematicAsync('ng-add', defaultOptions, appTree).toPromise();
|
await schematicRunner.runSchematicAsync('ng-add', defaultOptions, appTree).toPromise();
|
||||||
const configText = tree.readContent('/angular.json');
|
expect(tree.readContent('/projects/elements/src/polyfills.ts'))
|
||||||
const config = JSON.parse(configText);
|
.toContain(`import 'document-register-element';`);
|
||||||
const scripts = config.projects.elements.architect.build.options.scripts;
|
});
|
||||||
expect(scripts[0].input).toEqual(polyfillPath);
|
|
||||||
|
it('should add polyfill as a dependency in package.json', async() => {
|
||||||
|
const tree =
|
||||||
|
await schematicRunner.runSchematicAsync('ng-add', defaultOptions, appTree).toPromise();
|
||||||
|
const pkgJsonText = tree.readContent('/package.json');
|
||||||
|
const pkgJson = JSON.parse(pkgJsonText);
|
||||||
|
const {dependencies} = pkgJson;
|
||||||
|
expect(dependencies['document-register-element']).toBeDefined();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/schema",
|
||||||
|
"id": "SchematicsAngularElementsNgAdd",
|
||||||
|
"title": "Angular Elements Ng Add Schema",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"project": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The name of the project.",
|
||||||
|
"$default": {
|
||||||
|
"$source": "projectName"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"skipPackageJson": {
|
||||||
|
"description": "When true, does not add dependencies to the \"package.json\" file.",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
]
|
||||||
|
}
|
Loading…
Reference in New Issue