From 291e9a94ecbcc1bc98dce9bad9780a18ff32fc28 Mon Sep 17 00:00:00 2001
From: PatrickJS
Date: Thu, 17 Aug 2017 12:04:54 -0700
Subject: [PATCH 0001/1016] fix(@schematics/angular): correct blog link to
blog.angular.io
---
.../angular/application/other-files/app.component.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/schematics/angular/application/other-files/app.component.html b/packages/schematics/angular/application/other-files/app.component.html
index 8aa3f3f387..ad57621395 100644
--- a/packages/schematics/angular/application/other-files/app.component.html
+++ b/packages/schematics/angular/application/other-files/app.component.html
@@ -14,7 +14,7 @@ CLI Documentation
-
+
<% if (routing) { %>
From 5852ef2978fba1676e8d6e1f4a30eba5315ddf3e Mon Sep 17 00:00:00 2001
From: Hans Larsen
Date: Wed, 16 Aug 2017 13:57:02 -0700
Subject: [PATCH 0002/1016] build: add hashes to package map
---
lib/packages.ts | 52 +++++++++++++++++++++++++++++++++++++++++++---
scripts/release.ts | 33 +++--------------------------
2 files changed, 52 insertions(+), 33 deletions(-)
diff --git a/lib/packages.ts b/lib/packages.ts
index 0844d1f5a5..281261f233 100644
--- a/lib/packages.ts
+++ b/lib/packages.ts
@@ -8,10 +8,12 @@
'use strict';
import { JsonObject } from '@angular-devkit/core';
+import * as crypto from 'crypto';
import * as fs from 'fs';
import * as path from 'path';
import * as ts from 'typescript';
+const glob = require('glob');
const distRoot = path.join(__dirname, '../dist');
@@ -27,10 +29,44 @@ export interface PackageInfo {
private: boolean;
packageJson: JsonObject;
dependencies: string[];
+
+ hash: string;
}
export type PackageMap = { [name: string]: PackageInfo };
+const hashCache: {[name: string]: string | null} = {};
+function _getHashOf(pkg: PackageInfo): string {
+ if (!(pkg.name in hashCache)) {
+ hashCache[pkg.name] = null;
+ const md5Stream = crypto.createHash('md5');
+
+ // Update the stream with all files content.
+ const files: string[] = glob.sync(path.join(pkg.root, '**'), { nodir: true });
+ files.forEach(filePath => {
+ md5Stream.write(`\0${filePath}\0`);
+ md5Stream.write(fs.readFileSync(filePath));
+ });
+ // Update the stream with all versions of upstream dependencies.
+ pkg.dependencies.forEach(depName => {
+ md5Stream.write(`\0${depName}\0${_getHashOf(packages[depName])}\0`);
+ });
+
+ md5Stream.end();
+
+ hashCache[pkg.name] = (md5Stream.read() as Buffer).toString('hex');
+ }
+
+ if (!hashCache[pkg.name]) {
+ // Protect against circular dependency.
+ throw new Error('Circular dependency detected between the following packages: '
+ + Object.keys(hashCache).filter(key => hashCache[key] == null).join(', '));
+ }
+
+ return hashCache[pkg.name] !;
+}
+
+
function loadPackageJson(p: string) {
const root = require('../package.json');
const pkg = require(p);
@@ -108,8 +144,10 @@ const packageJsonPaths = _findAllPackageJson(path.join(__dirname, '..'), exclude
// Remove the root package.json.
.filter(p => p != path.join(__dirname, '../package.json'));
-// All the supported packages. Go through the packages directory and create a _map of
-// name => fullPath.
+
+// All the supported packages. Go through the packages directory and create a map of
+// name => PackageInfo. This map is partial as it lacks some information that requires the
+// map itself to finish building.
export const packages: PackageMap =
packageJsonPaths
.map(pkgPath => ({ root: path.dirname(pkgPath) }))
@@ -136,12 +174,14 @@ export const packages: PackageMap =
root: pkgRoot,
relative: path.relative(path.dirname(__dirname), pkgRoot),
main: path.resolve(pkgRoot, 'src/index.ts'),
- dependencies: [],
private: packageJson.private,
tar: path.join(distRoot, name.replace('/', '_') + '.tgz'),
bin,
name,
packageJson,
+
+ dependencies: [],
+ hash: '',
};
return packages;
@@ -158,3 +198,9 @@ for (const pkgName of Object.keys(packages)) {
|| name in (pkgJson.peerDependencies || {});
});
}
+
+
+// Update the hash values of each.
+for (const pkgName of Object.keys(packages)) {
+ packages[pkgName].hash = _getHashOf(packages[pkgName]);
+}
diff --git a/scripts/release.ts b/scripts/release.ts
index a56ef6cc00..eeb4d96531 100644
--- a/scripts/release.ts
+++ b/scripts/release.ts
@@ -10,38 +10,11 @@ import * as fs from 'fs';
import * as path from 'path';
import * as semver from 'semver';
import { ReleaseType } from 'semver';
-import { PackageInfo, packages } from '../lib/packages';
+import { packages } from '../lib/packages';
-const crypto = require('crypto');
-const glob = require('glob');
const { hashes, versions } = require('../versions.json');
-const hashCache: {[name: string]: string} = {};
-function _getHashOf(pkg: PackageInfo): string {
- if (!(pkg.name in hashCache)) {
- const md5Stream = crypto.createHash('md5');
-
- // Update the stream with all files content.
- const files: string[] = glob.sync(path.join(pkg.root, '**'), { nodir: true });
- files.forEach(filePath => {
- md5Stream.write(`\0${filePath}\0`);
- md5Stream.write(fs.readFileSync(filePath));
- });
- // Update the stream with all versions of upstream dependencies.
- pkg.dependencies.forEach(depName => {
- md5Stream.write(`\0${depName}\0${_getHashOf(packages[depName])}\0`);
- });
-
- md5Stream.end();
-
- hashCache[pkg.name] = md5Stream.read().toString('hex');
- }
-
- return hashCache[pkg.name];
-}
-
-
function _showVersions(logger: Logger) {
for (const pkg of Object.keys(versions)) {
if (!(pkg in packages)) {
@@ -49,7 +22,7 @@ function _showVersions(logger: Logger) {
}
const version = versions[pkg] || '???';
- const hash = _getHashOf(packages[pkg]);
+ const hash = packages[pkg].hash;
const diff = hashes[pkg] !== hash ? '!' : '';
const pad1 = ' '.slice(pkg.length);
@@ -70,7 +43,7 @@ function _upgrade(release: string, logger: Logger) {
versions[pkg] = '0.0.0';
}
- const hash = _getHashOf(packages[pkg]);
+ const hash = packages[pkg].hash;
const version = versions[pkg];
let newVersion: string = version;
From 165a080574093307cddc1d2e90dd6858186561e7 Mon Sep 17 00:00:00 2001
From: Hans Larsen
Date: Wed, 16 Aug 2017 15:31:08 -0700
Subject: [PATCH 0003/1016] test: only test changed packages
There is a --full flag now to allow for testing everything, otherwise
will only run unit tests for packages that changed since the last
release.
---
.circleci/config.yml | 2 +-
scripts/test.ts | 24 +++++++++++++++++++-----
2 files changed, 20 insertions(+), 6 deletions(-)
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 9506fbf106..7c1943de92 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -39,7 +39,7 @@ jobs:
key: angular_devkit-{{ .Branch }}-{{ checksum "package-lock.json" }}
- run: npm install --quiet
- - run: npm run test -- --code-coverage
+ - run: npm run test -- --code-coverage --full
integration:
<<: *defaults
diff --git a/scripts/test.ts b/scripts/test.ts
index 26690f1b55..92f0842717 100644
--- a/scripts/test.ts
+++ b/scripts/test.ts
@@ -5,16 +5,19 @@
* 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 { Logger } from '@angular-devkit/core';
import * as glob from 'glob';
import * as Istanbul from 'istanbul';
import 'jasmine';
-import {SpecReporter as JasmineSpecReporter } from 'jasmine-spec-reporter';
+import { SpecReporter as JasmineSpecReporter } from 'jasmine-spec-reporter';
import { ParsedArgs } from 'minimist';
import { join, relative } from 'path';
import { Position, SourceMapConsumer, SourceMapGenerator } from 'source-map';
+import { packages } from '../lib/packages';
const Jasmine = require('jasmine');
+const { versions } = require('../versions.json');
const projectBaseDir = join(__dirname, '../packages');
require('source-map-support').install({
@@ -201,7 +204,7 @@ glob.sync('packages/**/*.spec.ts')
console.error(`Invalid spec file name: ${path}. You're using the old convention.`);
});
-export default function (args: ParsedArgs) {
+export default function (args: ParsedArgs, logger: Logger) {
let regex = 'packages/**/*_spec.ts';
if (args.glob) {
regex = `packages/**/${args.glob}/**/*_spec.ts`;
@@ -215,7 +218,18 @@ export default function (args: ParsedArgs) {
// Run the tests.
const allTests =
glob.sync(regex)
- .map(p => relative(projectBaseDir, p))
- .filter(p => !/schematics_cli\/schematics\//.test(p));
- runner.execute(allTests);
+ .map(p => relative(projectBaseDir, p));
+
+ let tests = allTests;
+ if (!args.full) {
+ // Remove the tests from packages that haven't changed.
+ tests = tests
+ .filter(p => Object.keys(packages).some(name => {
+ return p.startsWith(packages[name].root) && packages[name].hash !== versions[name];
+ }));
+
+ logger.info(`Found ${tests.length} spec files, out of ${allTests.length}.`);
+ }
+
+ runner.execute(tests);
}
From 6e4c90fa1b771ceb0cccf60c5f590f0d0cbefc4d Mon Sep 17 00:00:00 2001
From: Stepan Suvorov
Date: Fri, 18 Aug 2017 18:23:52 +0200
Subject: [PATCH 0004/1016] fix(@angular-devkit/build-optimizer): comma in
webpack example is missing (#108)
---
packages/angular_devkit/build_optimizer/README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/angular_devkit/build_optimizer/README.md b/packages/angular_devkit/build_optimizer/README.md
index dfa16ba96f..795cc21846 100644
--- a/packages/angular_devkit/build_optimizer/README.md
+++ b/packages/angular_devkit/build_optimizer/README.md
@@ -122,7 +122,7 @@ module.exports = {
rules: [
{
test: /\.js$/,
- loader: '@angular-devkit/build-optimizer/webpack-loader'
+ loader: '@angular-devkit/build-optimizer/webpack-loader',
options: {
sourceMap: false
}
From 1327e49ee29c448010f0e8e12f458107e2bd1bd5 Mon Sep 17 00:00:00 2001
From: Hans Larsen
Date: Fri, 18 Aug 2017 13:37:04 -0700
Subject: [PATCH 0005/1016] release: patch
---
versions.json | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/versions.json b/versions.json
index eb841b1f7b..e82ac647f2 100644
--- a/versions.json
+++ b/versions.json
@@ -1,16 +1,16 @@
{
"versions": {
"@_/benchmark": "0.0.5",
- "@angular-devkit/build-optimizer": "0.0.13",
+ "@angular-devkit/build-optimizer": "0.0.14",
"@angular-devkit/core": "0.0.10",
"@angular-devkit/schematics": "0.0.17",
- "@schematics/angular": "0.0.27"
+ "@schematics/angular": "0.0.28"
},
"hashes": {
"@_/benchmark": "faab0531c82005d6b85fb084e9a0d6ac",
"@angular-devkit/core": "7dd3de13eae953f8678da365f1e19640",
"@angular-devkit/schematics": "308994128827efee83f95d0fbff6df70",
- "@schematics/angular": "db93c90ae20b1f7b2bb3fe589543a5b9",
- "@angular-devkit/build-optimizer": "2bcb5bf0dfa32cea9cccdf678b56d437"
+ "@schematics/angular": "b4208888740181855da3916fba0f9471",
+ "@angular-devkit/build-optimizer": "6a3f84e0b3e3497d8179ffdaa5cae247"
}
}
From b72f342751b180b9d7ad8e09e91d0efea9c70d7d Mon Sep 17 00:00:00 2001
From: Santhosh Kumar Srinivasan
Date: Mon, 21 Aug 2017 13:55:52 -0400
Subject: [PATCH 0006/1016] fix(@schematics/angular): Add error handling to
bootstrapping logic (#109)
This one is recommended in the style guide. https://angular.io/guide/styleguide#style-02-05 If it's part of the blueprint, it'd be better.
---
packages/schematics/angular/application/files/__path__/main.ts | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/packages/schematics/angular/application/files/__path__/main.ts b/packages/schematics/angular/application/files/__path__/main.ts
index a9ca1caf8c..91ec6da5f0 100644
--- a/packages/schematics/angular/application/files/__path__/main.ts
+++ b/packages/schematics/angular/application/files/__path__/main.ts
@@ -8,4 +8,5 @@ if (environment.production) {
enableProdMode();
}
-platformBrowserDynamic().bootstrapModule(AppModule);
+platformBrowserDynamic().bootstrapModule(AppModule)
+ .catch(err => console.log(err));
From 7b1ebaac59d6d446dc566a70f861761052c5c4f1 Mon Sep 17 00:00:00 2001
From: Artem Berezin
Date: Tue, 22 Aug 2017 03:56:54 +1000
Subject: [PATCH 0007/1016] refactor(angular-devkit/schematics): optimize svg
src (logo) (#107)
use base64 of
```xml
```
instead of Adobe Illustrator export svg result
---
.../angular/application/other-files/app.component.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/schematics/angular/application/other-files/app.component.html b/packages/schematics/angular/application/other-files/app.component.html
index ad57621395..d6f57769f6 100644
--- a/packages/schematics/angular/application/other-files/app.component.html
+++ b/packages/schematics/angular/application/other-files/app.component.html
@@ -3,7 +3,7 @@
Welcome to {{title}}!
-
+
Here are some links to help you start:
From 9faf0691c9e94ee4773eb5d1e9d680c7fd1334da Mon Sep 17 00:00:00 2001
From: Hans Larsen
Date: Sun, 20 Aug 2017 12:36:31 -0700
Subject: [PATCH 0008/1016] build: move profiling code to bootstrap
So that all commands (which all use bootstrap) can profile, not just devkit-admin.
---
bin/devkit-admin | 27 ---------------------------
lib/bootstrap-local.js | 27 +++++++++++++++++++++++++++
2 files changed, 27 insertions(+), 27 deletions(-)
diff --git a/bin/devkit-admin b/bin/devkit-admin
index ed3d1604d0..b1dcbc4ede 100755
--- a/bin/devkit-admin
+++ b/bin/devkit-admin
@@ -30,33 +30,6 @@ const scriptPath = path.join('../scripts', scriptName);
process.chdir(path.join(__dirname, '..'));
-// Check if we need to profile this CLI run.
-let profiler = null;
-if (process.env['DEVKIT_PROFILING']) {
- profiler = require('v8-profiler');
- profiler.startProfiling();
-
- function exitHandler(options, _err) {
- if (options.cleanup) {
- const cpuProfile = profiler.stopProfiling();
- const profileData = JSON.stringify(cpuProfile);
- const filePath = path.resolve(process.cwd(), process.env.DEVKIT_PROFILING) + '.cpuprofile';
-
- console.log(`Profiling data saved in "${filePath}": ${profileData.length} bytes`);
- fs.writeFileSync(filePath, profileData);
- }
-
- if (options.exit) {
- process.exit();
- }
- }
-
- process.on('exit', exitHandler.bind(null, { cleanup: true }));
- process.on('SIGINT', exitHandler.bind(null, { exit: true }));
- process.on('uncaughtException', exitHandler.bind(null, { exit: true }));
-}
-
-
// This might get awkward, so we fallback to console if there was an error.
let logger = null;
try {
diff --git a/lib/bootstrap-local.js b/lib/bootstrap-local.js
index 5c9160fb74..613a571944 100644
--- a/lib/bootstrap-local.js
+++ b/lib/bootstrap-local.js
@@ -13,6 +13,33 @@ const path = require('path');
const ts = require('typescript');
+// Check if we need to profile this CLI run.
+let profiler = null;
+if (process.env['DEVKIT_PROFILING']) {
+ profiler = require('v8-profiler');
+ profiler.startProfiling();
+
+ function exitHandler(options, _err) {
+ if (options.cleanup) {
+ const cpuProfile = profiler.stopProfiling();
+ const profileData = JSON.stringify(cpuProfile);
+ const filePath = path.resolve(process.cwd(), process.env.DEVKIT_PROFILING) + '.cpuprofile';
+
+ console.log(`Profiling data saved in "${filePath}": ${profileData.length} bytes`);
+ fs.writeFileSync(filePath, profileData);
+ }
+
+ if (options.exit) {
+ process.exit();
+ }
+ }
+
+ process.on('exit', exitHandler.bind(null, { cleanup: true }));
+ process.on('SIGINT', exitHandler.bind(null, { exit: true }));
+ process.on('uncaughtException', exitHandler.bind(null, { exit: true }));
+}
+
+
Error.stackTraceLimit = Infinity;
global._DevKitIsLocal = true;
From bd094be96f2785996fcd081e9c4be048b2b9662f Mon Sep 17 00:00:00 2001
From: Hans Larsen
Date: Sun, 20 Aug 2017 13:08:12 -0700
Subject: [PATCH 0009/1016] refactor(@schematics/angular): add required field
to schema
Also export SchematicsError to be used by schematics for error reporting.
---
.../schematics/src/exception/exception.ts | 4 ++++
packages/angular_devkit/schematics/src/index.ts | 2 +-
packages/schematics/angular/application/index.ts | 11 ++++++++---
packages/schematics/angular/application/schema.d.ts | 2 +-
packages/schematics/angular/application/schema.json | 3 ++-
5 files changed, 16 insertions(+), 6 deletions(-)
diff --git a/packages/angular_devkit/schematics/src/exception/exception.ts b/packages/angular_devkit/schematics/src/exception/exception.ts
index cc82a6b71a..bc52c14b5a 100644
--- a/packages/angular_devkit/schematics/src/exception/exception.ts
+++ b/packages/angular_devkit/schematics/src/exception/exception.ts
@@ -13,6 +13,10 @@ export class BaseException extends Error {
}
+// Used by schematics to throw exceptions.
+export class SchematicsError extends BaseException {}
+
+
// Exceptions
export class FileDoesNotExistException extends BaseException {
constructor(path: string) { super(`Path "${path}" does not exist.`); }
diff --git a/packages/angular_devkit/schematics/src/index.ts b/packages/angular_devkit/schematics/src/index.ts
index c5f8cd99a7..60a337ea76 100644
--- a/packages/angular_devkit/schematics/src/index.ts
+++ b/packages/angular_devkit/schematics/src/index.ts
@@ -10,7 +10,7 @@ import {Tree as TreeInterface } from './tree/interface';
import { branch, empty, merge, optimize, partition } from './tree/static';
-export { BaseException } from './exception/exception';
+export { SchematicsError } from './exception/exception';
export * from './tree/action';
export * from './engine/collection';
diff --git a/packages/schematics/angular/application/index.ts b/packages/schematics/angular/application/index.ts
index 44ec2e7d17..72b2056e4c 100644
--- a/packages/schematics/angular/application/index.ts
+++ b/packages/schematics/angular/application/index.ts
@@ -9,6 +9,7 @@ import {
MergeStrategy,
Rule,
SchematicContext,
+ SchematicsError,
Tree,
apply,
chain,
@@ -30,7 +31,11 @@ import { Schema as ApplicationOptions } from './schema';
function addBootstrapToNgModule(directory: string): Rule {
return (host: Tree) => {
const modulePath = `${directory}/src/app/app.module.ts`;
- const sourceText = host.read(modulePath) !.toString('utf-8');
+ const content = host.read(modulePath);
+ if (!content) {
+ throw new SchematicsError(`File ${modulePath} does not exist.`);
+ }
+ const sourceText = content.toString('utf-8');
const source = ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true);
const componentModule = './app.component';
@@ -93,7 +98,7 @@ export default function (options: ApplicationOptions): Rule {
'dot': '.',
...options as object,
}),
- move(options.directory !),
+ move(options.directory),
])),
schematic('module', {
name: 'app',
@@ -110,7 +115,7 @@ export default function (options: ApplicationOptions): Rule {
flat: true,
...componentOptions,
}),
- addBootstrapToNgModule(options.directory !),
+ addBootstrapToNgModule(options.directory),
mergeWith(
apply(url('./other-files'), [
componentOptions.inlineTemplate ? filter(path => !path.endsWith('.html')) : noop(),
diff --git a/packages/schematics/angular/application/schema.d.ts b/packages/schematics/angular/application/schema.d.ts
index cb6aac57e5..787c9c13cc 100644
--- a/packages/schematics/angular/application/schema.d.ts
+++ b/packages/schematics/angular/application/schema.d.ts
@@ -10,7 +10,7 @@ export interface Schema {
/**
* The directory name to create the app in.
*/
- directory?: string;
+ directory: string;
path?: string;
sourceDir?: string;
name: string;
diff --git a/packages/schematics/angular/application/schema.json b/packages/schematics/angular/application/schema.json
index ebed038b8a..944437ffc1 100644
--- a/packages/schematics/angular/application/schema.json
+++ b/packages/schematics/angular/application/schema.json
@@ -76,6 +76,7 @@
}
},
"required": [
- "name"
+ "name",
+ "directory"
]
}
From c2c07acc60bd4880ddb456941c7a19f1326174b0 Mon Sep 17 00:00:00 2001
From: Hans Larsen
Date: Sun, 20 Aug 2017 14:02:53 -0700
Subject: [PATCH 0010/1016] refactor: add a linting rule to disallow "!"
non-null op
And fix all files that used it.
---
lib/packages.ts | 5 +--
.../src/helpers/transform-javascript.ts | 18 ++++++++--
.../schematics/bin/schematics.ts | 8 ++---
.../schematics/src/engine/schematic_spec.ts | 1 +
.../schematics/src/rules/base_spec.ts | 1 +
.../schematics/src/rules/move_spec.ts | 1 +
.../schematics/src/tree/virtual.ts | 4 +--
.../schematics/src/tree/virtual_spec.ts | 1 +
.../schematics/src/utility/update-buffer.ts | 4 +--
.../schematics/angular/application/index.ts | 4 +--
packages/schematics/angular/class/index.ts | 7 +++-
.../schematics/angular/component/index.ts | 25 ++++++++++---
.../schematics/angular/directive/index.ts | 24 ++++++++++---
packages/schematics/angular/enum/index.ts | 7 +++-
packages/schematics/angular/guard/index.ts | 14 ++++++--
.../schematics/angular/interface/index.ts | 7 +++-
packages/schematics/angular/module/index.ts | 13 +++++--
packages/schematics/angular/pipe/index.ts | 23 +++++++++---
packages/schematics/angular/service/index.ts | 18 ++++++++--
.../schematics/angular/utility/ast-utils.ts | 9 +++--
rules/nonNullOperatorRule.ts | 36 +++++++++++++++++++
scripts/test.ts | 5 +--
scripts/validate-commits.ts | 4 +--
tslint.json | 1 +
24 files changed, 195 insertions(+), 45 deletions(-)
create mode 100644 rules/nonNullOperatorRule.ts
diff --git a/lib/packages.ts b/lib/packages.ts
index 281261f233..afffc87653 100644
--- a/lib/packages.ts
+++ b/lib/packages.ts
@@ -57,13 +57,14 @@ function _getHashOf(pkg: PackageInfo): string {
hashCache[pkg.name] = (md5Stream.read() as Buffer).toString('hex');
}
- if (!hashCache[pkg.name]) {
+ const value = hashCache[pkg.name];
+ if (!value) {
// Protect against circular dependency.
throw new Error('Circular dependency detected between the following packages: '
+ Object.keys(hashCache).filter(key => hashCache[key] == null).join(', '));
}
- return hashCache[pkg.name] !;
+ return value;
}
diff --git a/packages/angular_devkit/build_optimizer/src/helpers/transform-javascript.ts b/packages/angular_devkit/build_optimizer/src/helpers/transform-javascript.ts
index 5d0fdd1e19..b086420802 100644
--- a/packages/angular_devkit/build_optimizer/src/helpers/transform-javascript.ts
+++ b/packages/angular_devkit/build_optimizer/src/helpers/transform-javascript.ts
@@ -83,7 +83,14 @@ export function transformJavascript(
k, ts.createSourceFile(k, v, ts.ScriptTarget.ES2015)));
const host: ts.CompilerHost = {
- getSourceFile: (fileName) => sourcesMap.get(fileName)!,
+ getSourceFile: (fileName) => {
+ const sourceFile = sourcesMap.get(fileName);
+ if (!sourceFile) {
+ throw new Error(`File ${fileName} does not have a sourceFile.`);
+ }
+
+ return sourceFile;
+ },
getDefaultLibFileName: () => defaultLibFileName,
getCurrentDirectory: () => '',
getDirectories: () => [],
@@ -91,7 +98,14 @@ export function transformJavascript(
useCaseSensitiveFileNames: () => true,
getNewLine: () => '\n',
fileExists: (fileName) => fileMap.has(fileName),
- readFile: (fileName) => fileMap.has(fileName) ? fileMap.get(fileName)! : '',
+ readFile: (fileName) => {
+ const content = fileMap.get(fileName);
+ if (!content) {
+ throw new Error(`File ${fileName} does not exist.`);
+ }
+
+ return content;
+ },
writeFile: (fileName, text) => outputs.set(fileName, text),
};
diff --git a/packages/angular_devkit/schematics/bin/schematics.ts b/packages/angular_devkit/schematics/bin/schematics.ts
index 6024193d4f..53bab5223e 100644
--- a/packages/angular_devkit/schematics/bin/schematics.ts
+++ b/packages/angular_devkit/schematics/bin/schematics.ts
@@ -68,11 +68,11 @@ function usage(exitCode = 0): never {
function parseSchematicName(str: string | null): { collection: string, schematic: string } {
let collection = '@schematics/angular';
- if (!str) {
+ if (!str || str === null) {
usage(1);
}
- let schematic: string = str !;
+ let schematic: string = str as string;
if (schematic.indexOf(':') != -1) {
[collection, schematic] = schematic.split(':', 2);
@@ -112,8 +112,8 @@ const engine = new SchematicEngine(engineHost);
// Add support for schemaJson.
engineHost.registerOptionsTransform((schematic: FileSystemSchematicDesc, options: {}) => {
- if (schematic.schema) {
- const SchemaMetaClass = SchemaClassFactory<{}>(schematic.schemaJson !);
+ if (schematic.schema && schematic.schemaJson) {
+ const SchemaMetaClass = SchemaClassFactory<{}>(schematic.schemaJson);
const schemaClass = new SchemaMetaClass(options);
return schemaClass.$$root();
diff --git a/packages/angular_devkit/schematics/src/engine/schematic_spec.ts b/packages/angular_devkit/schematics/src/engine/schematic_spec.ts
index 2571503fe4..dd9937af6d 100644
--- a/packages/angular_devkit/schematics/src/engine/schematic_spec.ts
+++ b/packages/angular_devkit/schematics/src/engine/schematic_spec.ts
@@ -5,6 +5,7 @@
* 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
*/
+// tslint:disable:non-null-operator
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/toArray';
import 'rxjs/add/operator/toPromise';
diff --git a/packages/angular_devkit/schematics/src/rules/base_spec.ts b/packages/angular_devkit/schematics/src/rules/base_spec.ts
index c82a818ea5..5e694127ce 100644
--- a/packages/angular_devkit/schematics/src/rules/base_spec.ts
+++ b/packages/angular_devkit/schematics/src/rules/base_spec.ts
@@ -5,6 +5,7 @@
* 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
*/
+// tslint:disable:non-null-operator
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/toPromise';
import { Rule, SchematicContext, Source } from '../engine/interface';
diff --git a/packages/angular_devkit/schematics/src/rules/move_spec.ts b/packages/angular_devkit/schematics/src/rules/move_spec.ts
index de59c4f45d..523213b1e1 100644
--- a/packages/angular_devkit/schematics/src/rules/move_spec.ts
+++ b/packages/angular_devkit/schematics/src/rules/move_spec.ts
@@ -5,6 +5,7 @@
* 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
*/
+// tslint:disable:non-null-operator
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/toPromise';
import { SchematicContext } from '../engine/interface';
diff --git a/packages/angular_devkit/schematics/src/tree/virtual.ts b/packages/angular_devkit/schematics/src/tree/virtual.ts
index d5698c5f41..0d611d1b10 100644
--- a/packages/angular_devkit/schematics/src/tree/virtual.ts
+++ b/packages/angular_devkit/schematics/src/tree/virtual.ts
@@ -154,7 +154,8 @@ export class VirtualTree implements Tree {
this.set(new SimpleFileEntry(path, content as Buffer));
}
protected _rename(path: SchematicPath, to: SchematicPath, action?: Action, force = false) {
- if (!this._cacheMap.has(path)) {
+ const entry = this.get(path);
+ if (!entry) {
throw new FileDoesNotExistException(path);
}
if (this._cacheMap.has(to) && !force) {
@@ -167,7 +168,6 @@ export class VirtualTree implements Tree {
this._actions.rename(path, to);
}
- const entry = this.get(path) !;
this.set(new SimpleFileEntry(to, entry.content));
this._cacheMap.delete(path);
}
diff --git a/packages/angular_devkit/schematics/src/tree/virtual_spec.ts b/packages/angular_devkit/schematics/src/tree/virtual_spec.ts
index 678a5c80ed..a6816ff340 100644
--- a/packages/angular_devkit/schematics/src/tree/virtual_spec.ts
+++ b/packages/angular_devkit/schematics/src/tree/virtual_spec.ts
@@ -5,6 +5,7 @@
* 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
*/
+// tslint:disable:non-null-operator
import { FileAlreadyExistException, FileDoesNotExistException } from '../exception/exception';
import { FileSystemTree } from './filesystem';
import { FileEntry, MergeStrategy } from './interface';
diff --git a/packages/angular_devkit/schematics/src/utility/update-buffer.ts b/packages/angular_devkit/schematics/src/utility/update-buffer.ts
index c6895ebfcd..7b548b7ffc 100644
--- a/packages/angular_devkit/schematics/src/utility/update-buffer.ts
+++ b/packages/angular_devkit/schematics/src/utility/update-buffer.ts
@@ -210,10 +210,10 @@ export class UpdateBuffer {
}
if (start == h.end && h.next !== null) {
- return [h !, h.next !];
+ return [h, h.next];
}
- return [h !, h.slice(start) !];
+ return [h, h.slice(start)];
}
get length(): number {
diff --git a/packages/schematics/angular/application/index.ts b/packages/schematics/angular/application/index.ts
index 72b2056e4c..1145adfaff 100644
--- a/packages/schematics/angular/application/index.ts
+++ b/packages/schematics/angular/application/index.ts
@@ -98,7 +98,7 @@ export default function (options: ApplicationOptions): Rule {
'dot': '.',
...options as object,
}),
- move(options.directory),
+ move(options.directory),
])),
schematic('module', {
name: 'app',
@@ -115,7 +115,7 @@ export default function (options: ApplicationOptions): Rule {
flat: true,
...componentOptions,
}),
- addBootstrapToNgModule(options.directory),
+ addBootstrapToNgModule(options.directory),
mergeWith(
apply(url('./other-files'), [
componentOptions.inlineTemplate ? filter(path => !path.endsWith('.html')) : noop(),
diff --git a/packages/schematics/angular/class/index.ts b/packages/schematics/angular/class/index.ts
index 037280ff11..4499692235 100644
--- a/packages/schematics/angular/class/index.ts
+++ b/packages/schematics/angular/class/index.ts
@@ -7,6 +7,7 @@
*/
import {
Rule,
+ SchematicsError,
apply,
branchAndMerge,
chain,
@@ -25,6 +26,10 @@ import { Schema as ClassOptions } from './schema';
export default function (options: ClassOptions): Rule {
options.type = !!options.type ? `.${options.type}` : '';
options.path = options.path ? normalizePath(options.path) : options.path;
+ const sourceDir = options.sourceDir;
+ if (!sourceDir) {
+ throw new SchematicsError(`sourceDir option is required.`);
+ }
const templateSource = apply(url('./files'), [
options.spec ? noop() : filter(path => !path.endsWith('.spec.ts')),
@@ -32,7 +37,7 @@ export default function (options: ClassOptions): Rule {
...stringUtils,
...options as object,
}),
- move(options.sourceDir !),
+ move(sourceDir),
]);
return chain([
diff --git a/packages/schematics/angular/component/index.ts b/packages/schematics/angular/component/index.ts
index a8ff4ab36c..593b4294c5 100644
--- a/packages/schematics/angular/component/index.ts
+++ b/packages/schematics/angular/component/index.ts
@@ -8,6 +8,7 @@
import {
Rule,
SchematicContext,
+ SchematicsError,
Tree,
apply,
branchAndMerge,
@@ -36,8 +37,12 @@ function addDeclarationToNgModule(options: ComponentOptions): Rule {
}
const modulePath = options.module;
- let sourceText = host.read(modulePath) !.toString('utf-8');
- let source = ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true);
+ const text = host.read(modulePath);
+ if (text === null) {
+ throw new SchematicsError(`File ${modulePath} does not exist.`);
+ }
+ const sourceText = text.toString('utf-8');
+ const source = ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true);
const componentPath = `/${options.sourceDir}/${options.path}/`
+ (options.flat ? '' : stringUtils.dasherize(options.name) + '/')
@@ -59,8 +64,13 @@ function addDeclarationToNgModule(options: ComponentOptions): Rule {
host.commitUpdate(declarationRecorder);
if (options.export) {
- sourceText = host.read(modulePath) !.toString('utf-8');
- source = ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true);
+ // Need to refresh the AST because we overwrote the file in the host.
+ const text = host.read(modulePath);
+ if (text === null) {
+ throw new SchematicsError(`File ${modulePath} does not exist.`);
+ }
+ const sourceText = text.toString('utf-8');
+ const source = ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true);
const exportRecorder = host.beginUpdate(modulePath);
const exportChanges = addExportToModule(source, modulePath,
@@ -92,6 +102,11 @@ function buildSelector(options: ComponentOptions) {
export default function(options: ComponentOptions): Rule {
+ const sourceDir = options.sourceDir;
+ if (!sourceDir) {
+ throw new SchematicsError(`sourceDir option is required.`);
+ }
+
return (host: Tree, context: SchematicContext) => {
options.selector = options.selector || buildSelector(options);
options.path = options.path ? normalizePath(options.path) : options.path;
@@ -106,7 +121,7 @@ export default function(options: ComponentOptions): Rule {
'if-flat': (s: string) => options.flat ? '' : s,
...options as object,
}),
- move(options.sourceDir !),
+ move(sourceDir),
]);
return chain([
diff --git a/packages/schematics/angular/directive/index.ts b/packages/schematics/angular/directive/index.ts
index 81f2ae77e5..e1c9f80d9f 100644
--- a/packages/schematics/angular/directive/index.ts
+++ b/packages/schematics/angular/directive/index.ts
@@ -8,6 +8,7 @@
import {
Rule,
SchematicContext,
+ SchematicsError,
Tree,
apply,
branchAndMerge,
@@ -36,8 +37,12 @@ function addDeclarationToNgModule(options: DirectiveOptions): Rule {
}
const modulePath = options.module;
- let sourceText = host.read(modulePath) !.toString('utf-8');
- let source = ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true);
+ const text = host.read(modulePath);
+ if (text === null) {
+ throw new SchematicsError(`File ${modulePath} does not exist.`);
+ }
+ const sourceText = text.toString('utf-8');
+ const source = ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true);
const directivePath = `/${options.sourceDir}/${options.path}/`
+ (options.flat ? '' : stringUtils.dasherize(options.name) + '/')
@@ -58,8 +63,13 @@ function addDeclarationToNgModule(options: DirectiveOptions): Rule {
host.commitUpdate(declarationRecorder);
if (options.export) {
- sourceText = host.read(modulePath) !.toString('utf-8');
- source = ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true);
+ // Need to refresh the AST because we overwrote the file in the host.
+ const text = host.read(modulePath);
+ if (text === null) {
+ throw new SchematicsError(`File ${modulePath} does not exist.`);
+ }
+ const sourceText = text.toString('utf-8');
+ const source = ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true);
const exportRecorder = host.beginUpdate(modulePath);
const exportChanges = addExportToModule(source, modulePath,
@@ -91,6 +101,10 @@ function buildSelector(options: DirectiveOptions) {
export default function (options: DirectiveOptions): Rule {
options.selector = options.selector || buildSelector(options);
options.path = options.path ? normalizePath(options.path) : options.path;
+ const sourceDir = options.sourceDir;
+ if (!sourceDir) {
+ throw new SchematicsError(`sourceDir option is required.`);
+ }
return (host: Tree, context: SchematicContext) => {
options.module = findModuleFromOptions(host, options);
@@ -101,7 +115,7 @@ export default function (options: DirectiveOptions): Rule {
'if-flat': (s: string) => options.flat ? '' : s,
...options as object,
}),
- move(options.sourceDir !),
+ move(sourceDir),
]);
return chain([
diff --git a/packages/schematics/angular/enum/index.ts b/packages/schematics/angular/enum/index.ts
index 13b5dc85f1..e359c0c0b9 100644
--- a/packages/schematics/angular/enum/index.ts
+++ b/packages/schematics/angular/enum/index.ts
@@ -7,6 +7,7 @@
*/
import {
Rule,
+ SchematicsError,
apply,
branchAndMerge,
chain,
@@ -22,13 +23,17 @@ import { Schema as EnumOptions } from './schema';
export default function (options: EnumOptions): Rule {
options.path = options.path ? normalizePath(options.path) : options.path;
+ const sourceDir = options.sourceDir;
+ if (!sourceDir) {
+ throw new SchematicsError(`sourceDir option is required.`);
+ }
const templateSource = apply(url('./files'), [
template({
...stringUtils,
...options as object,
}),
- move(options.sourceDir !),
+ move(sourceDir),
]);
return chain([
diff --git a/packages/schematics/angular/guard/index.ts b/packages/schematics/angular/guard/index.ts
index 1bf2ab8173..84c357bc6c 100644
--- a/packages/schematics/angular/guard/index.ts
+++ b/packages/schematics/angular/guard/index.ts
@@ -8,6 +8,7 @@
import {
Rule,
SchematicContext,
+ SchematicsError,
Tree,
apply,
branchAndMerge,
@@ -36,7 +37,12 @@ function addDeclarationToNgModule(options: GuardOptions): Rule {
}
const modulePath = options.module;
- const sourceText = host.read(modulePath) !.toString('utf-8');
+ const text = host.read(modulePath);
+ if (text === null) {
+ throw new SchematicsError(`File ${modulePath} does not exist.`);
+ }
+ const sourceText = text.toString('utf-8');
+
const source = ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true);
const guardPath = `/${options.sourceDir}/${options.path}/`
@@ -61,6 +67,10 @@ function addDeclarationToNgModule(options: GuardOptions): Rule {
export default function (options: GuardOptions): Rule {
options.path = options.path ? normalizePath(options.path) : options.path;
+ const sourceDir = options.sourceDir;
+ if (!sourceDir) {
+ throw new SchematicsError(`sourceDir option is required.`);
+ }
return (host: Tree, context: SchematicContext) => {
if (options.module) {
@@ -73,7 +83,7 @@ export default function (options: GuardOptions): Rule {
...stringUtils,
...options as object,
}),
- move(options.sourceDir !),
+ move(sourceDir),
]);
return chain([
diff --git a/packages/schematics/angular/interface/index.ts b/packages/schematics/angular/interface/index.ts
index 6a663e3c43..57b11b1512 100644
--- a/packages/schematics/angular/interface/index.ts
+++ b/packages/schematics/angular/interface/index.ts
@@ -7,6 +7,7 @@
*/
import {
Rule,
+ SchematicsError,
apply,
branchAndMerge,
chain,
@@ -24,13 +25,17 @@ export default function (options: InterfaceOptions): Rule {
options.prefix = options.prefix ? options.prefix : '';
options.type = !!options.type ? `.${options.type}` : '';
options.path = options.path ? normalizePath(options.path) : options.path;
+ const sourceDir = options.sourceDir;
+ if (!sourceDir) {
+ throw new SchematicsError(`sourceDir option is required.`);
+ }
const templateSource = apply(url('./files'), [
template({
...stringUtils,
...options as object,
}),
- move(options.sourceDir !),
+ move(sourceDir),
]);
return chain([
diff --git a/packages/schematics/angular/module/index.ts b/packages/schematics/angular/module/index.ts
index 976378d571..5980d5e736 100644
--- a/packages/schematics/angular/module/index.ts
+++ b/packages/schematics/angular/module/index.ts
@@ -8,6 +8,7 @@
import {
Rule,
SchematicContext,
+ SchematicsError,
Tree,
apply,
branchAndMerge,
@@ -36,7 +37,11 @@ function addDeclarationToNgModule(options: ModuleOptions): Rule {
const modulePath = options.module;
- const sourceText = host.read(modulePath) !.toString('utf-8');
+ const text = host.read(modulePath);
+ if (text === null) {
+ throw new SchematicsError(`File ${modulePath} does not exist.`);
+ }
+ const sourceText = text.toString('utf-8');
const source = ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true);
const importModulePath = `/${options.sourceDir}/${options.path}/`
@@ -62,6 +67,10 @@ function addDeclarationToNgModule(options: ModuleOptions): Rule {
export default function (options: ModuleOptions): Rule {
options.path = options.path ? normalizePath(options.path) : options.path;
+ const sourceDir = options.sourceDir;
+ if (!sourceDir) {
+ throw new SchematicsError(`sourceDir option is required.`);
+ }
return (host: Tree, context: SchematicContext) => {
if (options.module) {
@@ -76,7 +85,7 @@ export default function (options: ModuleOptions): Rule {
'if-flat': (s: string) => options.flat ? '' : s,
...options as object,
}),
- move(options.sourceDir !),
+ move(sourceDir),
]);
return chain([
diff --git a/packages/schematics/angular/pipe/index.ts b/packages/schematics/angular/pipe/index.ts
index 82ba272d3f..24e086a074 100644
--- a/packages/schematics/angular/pipe/index.ts
+++ b/packages/schematics/angular/pipe/index.ts
@@ -8,6 +8,7 @@
import {
Rule,
SchematicContext,
+ SchematicsError,
Tree,
apply,
branchAndMerge,
@@ -36,8 +37,12 @@ function addDeclarationToNgModule(options: PipeOptions): Rule {
}
const modulePath = options.module;
- let sourceText = host.read(modulePath) !.toString('utf-8');
- let source = ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true);
+ const text = host.read(modulePath);
+ if (text === null) {
+ throw new SchematicsError(`File ${modulePath} does not exist.`);
+ }
+ const sourceText = text.toString('utf-8');
+ const source = ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true);
const pipePath = `/${options.sourceDir}/${options.path}/`
+ (options.flat ? '' : stringUtils.dasherize(options.name) + '/')
@@ -56,8 +61,12 @@ function addDeclarationToNgModule(options: PipeOptions): Rule {
host.commitUpdate(recorder);
if (options.export) {
- sourceText = host.read(modulePath) !.toString('utf-8');
- source = ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true);
+ const text = host.read(modulePath);
+ if (text === null) {
+ throw new SchematicsError(`File ${modulePath} does not exist.`);
+ }
+ const sourceText = text.toString('utf-8');
+ const source = ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true);
const exportRecorder = host.beginUpdate(modulePath);
const exportChanges = addExportToModule(source, modulePath,
@@ -78,6 +87,10 @@ function addDeclarationToNgModule(options: PipeOptions): Rule {
export default function (options: PipeOptions): Rule {
options.path = options.path ? normalizePath(options.path) : options.path;
+ const sourceDir = options.sourceDir;
+ if (!sourceDir) {
+ throw new SchematicsError(`sourceDir option is required.`);
+ }
return (host: Tree, context: SchematicContext) => {
options.module = findModuleFromOptions(host, options);
@@ -89,7 +102,7 @@ export default function (options: PipeOptions): Rule {
'if-flat': (s: string) => options.flat ? '' : s,
...options as object,
}),
- move(options.sourceDir !),
+ move(sourceDir),
]);
return chain([
diff --git a/packages/schematics/angular/service/index.ts b/packages/schematics/angular/service/index.ts
index 41acb29c38..c2dd63fdad 100644
--- a/packages/schematics/angular/service/index.ts
+++ b/packages/schematics/angular/service/index.ts
@@ -8,6 +8,7 @@
import {
Rule,
SchematicContext,
+ SchematicsError,
Tree,
apply,
branchAndMerge,
@@ -36,7 +37,16 @@ function addProviderToNgModule(options: ServiceOptions): Rule {
}
const modulePath = options.module;
- const sourceText = host.read(modulePath) !.toString('utf-8');
+ if (!host.exists(options.module)) {
+ throw new Error('Specified module does not exist');
+ }
+
+ const text = host.read(modulePath);
+ if (text === null) {
+ throw new SchematicsError(`File ${modulePath} does not exist.`);
+ }
+ const sourceText = text.toString('utf-8');
+
const source = ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true);
const servicePath = `/${options.sourceDir}/${options.path}/`
@@ -61,6 +71,10 @@ function addProviderToNgModule(options: ServiceOptions): Rule {
export default function (options: ServiceOptions): Rule {
options.path = options.path ? normalizePath(options.path) : options.path;
+ const sourceDir = options.sourceDir;
+ if (!sourceDir) {
+ throw new SchematicsError(`sourceDir option is required.`);
+ }
return (host: Tree, context: SchematicContext) => {
if (options.module) {
@@ -74,7 +88,7 @@ export default function (options: ServiceOptions): Rule {
'if-flat': (s: string) => options.flat ? '' : s,
...options as object,
}),
- move(options.sourceDir !),
+ move(sourceDir),
]);
return chain([
diff --git a/packages/schematics/angular/utility/ast-utils.ts b/packages/schematics/angular/utility/ast-utils.ts
index 02b9c96b5c..56e352515d 100644
--- a/packages/schematics/angular/utility/ast-utils.ts
+++ b/packages/schematics/angular/utility/ast-utils.ts
@@ -98,8 +98,11 @@ export function insertAfterLastOccurrence(nodes: ts.Node[],
fallbackPos: number,
syntaxKind?: ts.SyntaxKind): Change {
let lastItem = nodes.sort(nodesByPosition).pop();
+ if (!lastItem) {
+ throw new Error();
+ }
if (syntaxKind) {
- lastItem = findNodes(lastItem !, syntaxKind).sort(nodesByPosition).pop();
+ lastItem = findNodes(lastItem, syntaxKind).sort(nodesByPosition).pop();
}
if (!lastItem && fallbackPos == undefined) {
throw new Error(`tried to insert ${toInsert} as first occurence with no fallback position`);
@@ -124,7 +127,7 @@ export function getContentOfKeyLiteral(_source: ts.SourceFile, node: ts.Node): s
function _angularImportsFromNode(node: ts.ImportDeclaration,
_sourceFile: ts.SourceFile): {[name: string]: string} {
const ms = node.moduleSpecifier;
- let modulePath: string | null = null;
+ let modulePath: string;
switch (ms.kind) {
case ts.SyntaxKind.StringLiteral:
modulePath = (ms as ts.StringLiteral).text;
@@ -155,7 +158,7 @@ function _angularImportsFromNode(node: ts.ImportDeclaration,
return namedImports.elements
.map((is: ts.ImportSpecifier) => is.propertyName ? is.propertyName.text : is.name.text)
.reduce((acc: {[name: string]: string}, curr: string) => {
- acc[curr] = modulePath !;
+ acc[curr] = modulePath;
return acc;
}, {});
diff --git a/rules/nonNullOperatorRule.ts b/rules/nonNullOperatorRule.ts
new file mode 100644
index 0000000000..1a05d704bc
--- /dev/null
+++ b/rules/nonNullOperatorRule.ts
@@ -0,0 +1,36 @@
+/**
+ * @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 * as Lint from 'tslint';
+import * as ts from 'typescript';
+
+
+export class Rule extends Lint.Rules.AbstractRule {
+ public static metadata: Lint.IRuleMetadata = {
+ ruleName: 'non-null-operator',
+ type: 'typescript',
+ description: `Ensure the NonNull operator (!) can be used or not.`,
+ rationale: 'strictNullChecks are meant to avoid issues, which the non-null operator removes '
+ + 'if used too frequently. Please use the non-null operator responsibly.',
+ options: null,
+ optionsDescription: `Not configurable.`,
+ typescriptOnly: false,
+ };
+
+ public static FAILURE_STRING = 'The Non-Null operator `!` is illegal.';
+
+ public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
+ return this.applyWithWalker(new Walker(sourceFile, this.getOptions()));
+ }
+}
+
+
+class Walker extends Lint.RuleWalker {
+ visitNonNullExpression(node: ts.NonNullExpression): void {
+ this.addFailureAt(node.getStart(), node.getWidth(), Rule.FAILURE_STRING);
+ }
+}
diff --git a/scripts/test.ts b/scripts/test.ts
index 92f0842717..a873f3bc98 100644
--- a/scripts/test.ts
+++ b/scripts/test.ts
@@ -55,8 +55,9 @@ function istanbulDevKitRequireHook(code: string, filename: string) {
if (filename.match(/_spec\.ts$/)) {
return code;
}
- if (codeMap.get(filename)) {
- return codeMap.get(filename)!.code;
+ const codeFile = codeMap.get(filename);
+ if (codeFile) {
+ return codeFile.code;
}
const instrumenter = new Istanbul.Instrumenter({
diff --git a/scripts/validate-commits.ts b/scripts/validate-commits.ts
index eda7784439..1877f06d7d 100644
--- a/scripts/validate-commits.ts
+++ b/scripts/validate-commits.ts
@@ -42,8 +42,8 @@ export default function (_: {}, logger: Logger) {
const commits = log.split(/\n/)
.map(i => i.match(/(^[0-9a-f]+) (.+)$/))
- .filter(x => !!x)
- .map(x => Array.from(x !).slice(1));
+ .map(x => x ? Array.from(x).slice(1) : null)
+ .filter(x => !!x) as [string, string][];
logger.info(`Found ${commits.length} commits...`);
const output = new Logger('check', logger);
diff --git a/tslint.json b/tslint.json
index 3006fde8d5..8f08532807 100644
--- a/tslint.json
+++ b/tslint.json
@@ -4,6 +4,7 @@
],
"rules": {
"import-groups": true,
+ "non-null-operator": true,
"import-blacklist": [
true,
From c44e40d8ea7a318fd996816089e169179527094f Mon Sep 17 00:00:00 2001
From: Hans Larsen
Date: Sun, 20 Aug 2017 14:50:59 -0700
Subject: [PATCH 0011/1016] refactor: replace quotes for linting
---
packages/schematics/angular/application/schema.d.ts | 4 ++--
packages/schematics/angular/component/schema.d.ts | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/packages/schematics/angular/application/schema.d.ts b/packages/schematics/angular/application/schema.d.ts
index 787c9c13cc..5747c51f19 100644
--- a/packages/schematics/angular/application/schema.d.ts
+++ b/packages/schematics/angular/application/schema.d.ts
@@ -25,11 +25,11 @@ export interface Schema {
/**
* Specifies the view encapsulation strategy.
*/
- viewEncapsulation?: ("Emulated" | "Native" | "None");
+ viewEncapsulation?: ('Emulated' | 'Native' | 'None');
/**
* Specifies the change detection strategy.
*/
- changeDetection?: ("Default" | "OnPush");
+ changeDetection?: ('Default' | 'OnPush');
version?: string;
routing?: boolean;
/**
diff --git a/packages/schematics/angular/component/schema.d.ts b/packages/schematics/angular/component/schema.d.ts
index 8c248c06fb..cd7076154e 100644
--- a/packages/schematics/angular/component/schema.d.ts
+++ b/packages/schematics/angular/component/schema.d.ts
@@ -21,11 +21,11 @@ export interface Schema {
/**
* Specifies the view encapsulation strategy.
*/
- viewEncapsulation?: ("Emulated" | "Native" | "None");
+ viewEncapsulation?: ('Emulated' | 'Native' | 'None');
/**
* Specifies the change detection strategy.
*/
- changeDetection?: ("Default" | "OnPush");
+ changeDetection?: ('Default' | 'OnPush');
routing?: boolean;
/**
* The prefix to apply to generated selectors.
From 86c00ac7dcfad224c8261c12677c28de006090b0 Mon Sep 17 00:00:00 2001
From: Hans Larsen
Date: Sun, 20 Aug 2017 15:32:26 -0700
Subject: [PATCH 0012/1016] build: if an error happens in the script report it
---
bin/devkit-admin | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/bin/devkit-admin b/bin/devkit-admin
index b1dcbc4ede..3e97882dde 100755
--- a/bin/devkit-admin
+++ b/bin/devkit-admin
@@ -68,4 +68,9 @@ try {
}
-require(scriptPath).default(args, logger);
+try {
+ require(scriptPath).default(args, logger);
+} catch (err) {
+ logger.fatal(err.toString());
+}
+
From 780734f3b00ea79f0e7c6c7abf90995eb6ab827e Mon Sep 17 00:00:00 2001
From: Hans Larsen
Date: Sun, 20 Aug 2017 15:58:45 -0700
Subject: [PATCH 0013/1016] build: add linting script with pretty formatter
Having a script allows us to better control the output, and add
extra non-tslint validations.
---
package.json | 4 +--
scripts/lint.ts | 74 +++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 76 insertions(+), 2 deletions(-)
create mode 100644 scripts/lint.ts
diff --git a/package.json b/package.json
index 198b21baf3..333a295acd 100644
--- a/package.json
+++ b/package.json
@@ -16,8 +16,8 @@
"admin": "node ./bin/devkit-admin",
"build": "npm run admin -- build",
"build-tsc": "tsc -p tsconfig.json",
- "fix": "npm run lint -- --fix",
- "lint": "tsc -p rules/tsconfig.json && tslint --config tslint.json --project tsconfig.json --type-check",
+ "fix": "npm run admin -- lint --fix",
+ "lint": "npm run admin -- lint",
"test": "node ./bin/devkit-admin test",
"validate-commits": "./bin/devkit-admin validate-commits",
"integration": "npm run build && npm run integration:build-optimizer",
diff --git a/scripts/lint.ts b/scripts/lint.ts
new file mode 100644
index 0000000000..2a85470edb
--- /dev/null
+++ b/scripts/lint.ts
@@ -0,0 +1,74 @@
+/**
+ * @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 { Logger } from '@angular-devkit/core';
+import { ParsedArgs } from 'minimist';
+import * as path from 'path';
+import { Configuration, ILinterOptions, Linter, findFormatter } from 'tslint';
+import * as ts from 'typescript';
+
+
+function _buildRules(logger: Logger) {
+ const tsConfigPath = path.join(__dirname, '../rules/tsconfig.json');
+ const configFile = ts.readConfigFile(tsConfigPath, ts.sys.readFile);
+
+ const parsedTsConfig = ts.parseJsonConfigFileContent(
+ configFile.config,
+ ts.sys,
+ path.dirname(tsConfigPath),
+ );
+ const lintRulesProgram = ts.createProgram(parsedTsConfig.fileNames, parsedTsConfig.options);
+ const result = lintRulesProgram.emit();
+
+ if (result.emitSkipped) {
+ const host: ts.FormatDiagnosticsHost = {
+ getCurrentDirectory: () => ts.sys.getCurrentDirectory(),
+ getNewLine: () => ts.sys.newLine,
+ getCanonicalFileName: (fileName: string) => fileName,
+ };
+ logger.fatal(ts.formatDiagnostics(result.diagnostics, host));
+ process.exit(100);
+ }
+}
+
+
+export default function (options: ParsedArgs, logger: Logger) {
+ _buildRules(logger);
+
+ const lintOptions: ILinterOptions = {
+ fix: options.fix,
+ };
+
+ const program = Linter.createProgram(path.join(__dirname, '../tsconfig.json'));
+ const linter = new Linter(lintOptions, program);
+ const tsLintPath = path.join(__dirname, '../tslint.json');
+ const tsLintConfig = Configuration.loadConfigurationFromPath(tsLintPath);
+
+ program.getRootFileNames().forEach(fileName => {
+ linter.lint(fileName, ts.sys.readFile(fileName), tsLintConfig);
+ });
+
+ const result = linter.getResult();
+ const Formatter = findFormatter('codeFrame');
+ if (!Formatter) {
+ throw new Error('Cannot find lint formatter.');
+ }
+ const formatter = new Formatter();
+
+ if (result.errorCount > 0) {
+ logger.error(formatter.format(result.failures));
+ logger.info(`Errors: ${result.errorCount}`);
+ if (result.warningCount > 0) {
+ logger.info(`Warnings: ${result.warningCount}`);
+ }
+ process.exit(2);
+ } else if (result.warningCount > 0) {
+ logger.warn(formatter.format(result.failures));
+ logger.info(`Warnings: ${result.warningCount}`);
+ process.exit(1);
+ }
+}
From a23c251d723e32ac2be9022b8daf1af91b792e39 Mon Sep 17 00:00:00 2001
From: Hans Larsen
Date: Sun, 20 Aug 2017 16:41:59 -0700
Subject: [PATCH 0014/1016] ci: add tslint rule to disable tslint:disable
pragmas
They will be only allowed in spec files.
---
rules/noGlobalTslintDisableRule.ts | 62 ++++++++++++++++++++++++++++++
tslint.json | 1 +
2 files changed, 63 insertions(+)
create mode 100644 rules/noGlobalTslintDisableRule.ts
diff --git a/rules/noGlobalTslintDisableRule.ts b/rules/noGlobalTslintDisableRule.ts
new file mode 100644
index 0000000000..01e6d6d827
--- /dev/null
+++ b/rules/noGlobalTslintDisableRule.ts
@@ -0,0 +1,62 @@
+/**
+ * @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 * as Lint from 'tslint';
+import * as ts from 'typescript';
+
+
+export class Rule extends Lint.Rules.AbstractRule {
+ public static metadata: Lint.IRuleMetadata = {
+ ruleName: 'no-global-tslint-disable',
+ type: 'style',
+ description: `Ensure global tslint disable are only used for unit tests.`,
+ rationale: `Some projects want to disallow tslint disable and only use per-line ones.`,
+ options: null,
+ optionsDescription: `Not configurable.`,
+ typescriptOnly: false,
+ };
+
+ public static FAILURE_STRING = 'tslint:disable is not allowed in this context.';
+
+ public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
+ return this.applyWithWalker(new Walker(sourceFile, this.getOptions()));
+ }
+}
+
+
+class Walker extends Lint.RuleWalker {
+ private _findComments(node: ts.Node): ts.CommentRange[] {
+ return ([] as ts.CommentRange[]).concat(
+ ts.getLeadingCommentRanges(node.getFullText(), 0) || [],
+ ts.getTrailingCommentRanges(node.getFullText(), 0) || [],
+ node.getChildren().reduce((acc, n) => {
+ return acc.concat(this._findComments(n));
+ }, [] as ts.CommentRange[]),
+ );
+ }
+
+ walk(sourceFile: ts.SourceFile) {
+ super.walk(sourceFile);
+
+ // Ignore spec files.
+ if (sourceFile.fileName.match(/_spec.ts$/)) {
+ return;
+ }
+
+ // Find all comment nodes.
+ const ranges = this._findComments(sourceFile);
+ ranges.forEach(range => {
+ const text = sourceFile.getFullText().substring(range.pos, range.end);
+ let i = text.indexOf('tslint:disable:');
+
+ while (i != -1) {
+ this.addFailureAt(range.pos + i + 1, range.pos + i + 15, Rule.FAILURE_STRING);
+ i = text.indexOf('tslint:disable:', i + 1);
+ }
+ });
+ }
+}
diff --git a/tslint.json b/tslint.json
index 8f08532807..c3254c57a0 100644
--- a/tslint.json
+++ b/tslint.json
@@ -5,6 +5,7 @@
"rules": {
"import-groups": true,
"non-null-operator": true,
+ "no-global-tslint-disable": true,
"import-blacklist": [
true,
From a0a10241c683f48393ec6ef4490094b21e94354e Mon Sep 17 00:00:00 2001
From: Filipe Silva
Date: Tue, 8 Aug 2017 20:17:57 +0100
Subject: [PATCH 0015/1016] feat(@angular-devkit/build-optimizer): add prefix
class transformer
---
.../angular_devkit/build_optimizer/README.md | 29 ++-
.../src/build-optimizer/build-optimizer.ts | 13 +-
.../build_optimizer/src/purify/purify.ts | 11 --
.../build_optimizer/src/purify/purify_spec.ts | 62 -------
.../src/transforms/prefix-classes.ts | 175 ++++++++++++++++++
.../src/transforms/prefix-classes_spec.ts | 82 ++++++++
6 files changed, 293 insertions(+), 79 deletions(-)
create mode 100644 packages/angular_devkit/build_optimizer/src/transforms/prefix-classes.ts
create mode 100644 packages/angular_devkit/build_optimizer/src/transforms/prefix-classes_spec.ts
diff --git a/packages/angular_devkit/build_optimizer/README.md b/packages/angular_devkit/build_optimizer/README.md
index 795cc21846..fcd4989746 100644
--- a/packages/angular_devkit/build_optimizer/README.md
+++ b/packages/angular_devkit/build_optimizer/README.md
@@ -53,7 +53,7 @@ Clazz.decorators = [{ type: NotInjectable }];
### Prefix functions
-Adds `/*@__PURE__*/` comments to top level downleveled class declarations and instantiation.
+Adds `/*@__PURE__*/` comments to top level downleveled class declarations and instantiation.
Webpack library imports are also marked as `/*@__PURE__*/` when used with [Purify Plugin](#purify-plugin).
```typescript
@@ -69,6 +69,31 @@ var newClazzTwo = /*@__PURE__*/ Clazz();
```
+### Prefix Classes
+
+Adds `/*@__PURE__*/` to downlevered TypeScript classes.
+
+```typescript
+// input
+var ReplayEvent = (function () {
+ function ReplayEvent(time, value) {
+ this.time = time;
+ this.value = value;
+ }
+ return ReplayEvent;
+}());
+
+// output
+var ReplayEvent = /*@__PURE__*/ (function () {
+ function ReplayEvent(time, value) {
+ this.time = time;
+ this.value = value;
+ }
+ return ReplayEvent;
+}());
+```
+
+
### Import tslib
TypeScript helpers (`__extends/__decorate/__metadata/__param`) are replaced with `tslib` imports whenever found.
@@ -88,7 +113,7 @@ import { __extends } from "tslib";
### Purify Plugin
-Performs regex based replacements on all files that add `/*@__PURE__*/` comments to downleveled classes, TypeScript
+Performs regex based replacements on all files that add `/*@__PURE__*/` comments to downleveled classes, TypeScript
enums and webpack imports (used with [Prefix functions](#prefix-functions))
diff --git a/packages/angular_devkit/build_optimizer/src/build-optimizer/build-optimizer.ts b/packages/angular_devkit/build_optimizer/src/build-optimizer/build-optimizer.ts
index 2c725210d1..38736fcb73 100644
--- a/packages/angular_devkit/build_optimizer/src/build-optimizer/build-optimizer.ts
+++ b/packages/angular_devkit/build_optimizer/src/build-optimizer/build-optimizer.ts
@@ -9,6 +9,7 @@ import { readFileSync } from 'fs';
import { TransformJavascriptOutput, transformJavascript } from '../helpers/transform-javascript';
import { getFoldFileTransformer } from '../transforms/class-fold';
import { getImportTslibTransformer } from '../transforms/import-tslib';
+import { getPrefixClassesTransformer, prefixClassRegexes } from '../transforms/prefix-classes';
import { getPrefixFunctionsTransformer } from '../transforms/prefix-functions';
import { getScrubFileTransformer } from '../transforms/scrub-file';
@@ -58,10 +59,6 @@ export function buildOptimizer(options: BuildOptimizerOptions): TransformJavascr
// Determine which transforms to apply.
const getTransforms = [];
- if (hasTsHelpers.test(content)) {
- getTransforms.push(getImportTslibTransformer);
- }
-
if (inputFilePath
&& isAngularModuleFile.test(inputFilePath)
&& whitelistedAngularModules.some((re) => re.test(inputFilePath))
@@ -82,5 +79,13 @@ export function buildOptimizer(options: BuildOptimizerOptions): TransformJavascr
);
}
+ if (hasTsHelpers.test(content)) {
+ getTransforms.push(getImportTslibTransformer);
+ }
+
+ if (prefixClassRegexes.some((regex) => regex.test(content as string))) {
+ getTransforms.push(getPrefixClassesTransformer);
+ }
+
return transformJavascript({ ...options, getTransforms, content });
}
diff --git a/packages/angular_devkit/build_optimizer/src/purify/purify.ts b/packages/angular_devkit/build_optimizer/src/purify/purify.ts
index 775877ade2..b09aa44d47 100644
--- a/packages/angular_devkit/build_optimizer/src/purify/purify.ts
+++ b/packages/angular_devkit/build_optimizer/src/purify/purify.ts
@@ -14,17 +14,6 @@ export function purify(content: string) {
const pureImportMatches = getMatches(content, importCommentRegex, 1).join('|');
const newContent = content
- /* prefix downleveled classes w/ the @__PURE__ annotation */
- .replace(
- // tslint:disable-next-line:max-line-length
- /^(var (\S+) = )(\(function \(\) \{\r?\n(?: (?:\/\*\*| \*|\*\/|\/\/)[^\r?\n]*\r?\n)* function \2\([^\)]*\) \{\r?\n)/mg,
- '$1/*@__PURE__*/$3',
- )
- /* prefix downleveled classes that extend another class w/ the @__PURE__ annotation */
- .replace(
- /^(var (\S+) = )(\(function \(_super\) \{\r?\n \w*__extends\(\w+, _super\);\r?\n)/mg,
- '$1/*@__PURE__*/$3',
- )
/* wrap TS 2.2 enums w/ an IIFE */
.replace(
// tslint:disable-next-line:max-line-length
diff --git a/packages/angular_devkit/build_optimizer/src/purify/purify_spec.ts b/packages/angular_devkit/build_optimizer/src/purify/purify_spec.ts
index 1e5f96e1bd..6749b1d87c 100644
--- a/packages/angular_devkit/build_optimizer/src/purify/purify_spec.ts
+++ b/packages/angular_devkit/build_optimizer/src/purify/purify_spec.ts
@@ -10,68 +10,6 @@ import { purify } from './purify';
// tslint:disable:max-line-length
describe('purify', () => {
- it('prefix downleveled classes with /*@__PURE__*/', () => {
- const input = stripIndent`
- var ReplayEvent = (function () {
- function ReplayEvent(time, value) {
- this.time = time;
- this.value = value;
- }
- return ReplayEvent;
- }());
- `;
- const output = stripIndent`
- var ReplayEvent = /*@__PURE__*/(function () {
- function ReplayEvent(time, value) {
- this.time = time;
- this.value = value;
- }
- return ReplayEvent;
- }());
- `;
-
- expect(oneLine`${purify(input)}`).toEqual(oneLine`${output}`);
- });
-
- it('prefix downleveled classes that extend another class with /*@__PURE__*/', () => {
- const input = stripIndent`
- var TakeUntilSubscriber = (function (_super) {
- __extends(TakeUntilSubscriber, _super);
- function TakeUntilSubscriber(destination, notifier) {
- _super.call(this, destination);
- this.notifier = notifier;
- this.add(subscribeToResult_1.subscribeToResult(this, notifier));
- }
- TakeUntilSubscriber.prototype.notifyNext = function (outerValue, innerValue, outerIndex, innerIndex, innerSub) {
- this.complete();
- };
- TakeUntilSubscriber.prototype.notifyComplete = function () {
- // noop
- };
- return TakeUntilSubscriber;
- }(OuterSubscriber_1.OuterSubscriber));
- `;
- const output = stripIndent`
- var TakeUntilSubscriber = /*@__PURE__*/(function (_super) {
- __extends(TakeUntilSubscriber, _super);
- function TakeUntilSubscriber(destination, notifier) {
- _super.call(this, destination);
- this.notifier = notifier;
- this.add(subscribeToResult_1.subscribeToResult(this, notifier));
- }
- TakeUntilSubscriber.prototype.notifyNext = function (outerValue, innerValue, outerIndex, innerIndex, innerSub) {
- this.complete();
- };
- TakeUntilSubscriber.prototype.notifyComplete = function () {
- // noop
- };
- return TakeUntilSubscriber;
- }(OuterSubscriber_1.OuterSubscriber));
- `;
-
- expect(oneLine`${purify(input)}`).toEqual(oneLine`${output}`);
- });
-
it('wraps ts 2.2 enums in IIFE', () => {
const input = stripIndent`
var ChangeDetectionStrategy = {};
diff --git a/packages/angular_devkit/build_optimizer/src/transforms/prefix-classes.ts b/packages/angular_devkit/build_optimizer/src/transforms/prefix-classes.ts
new file mode 100644
index 0000000000..f376923f5d
--- /dev/null
+++ b/packages/angular_devkit/build_optimizer/src/transforms/prefix-classes.ts
@@ -0,0 +1,175 @@
+/**
+ * @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 * as ts from 'typescript';
+
+
+export const prefixClassRegexes = [
+ // tslint:disable-next-line:max-line-length
+ /^(var (\S+) = )(\(function \(\) \{\r?\n(?: (?:\/\*\*| \*|\*\/|\/\/)[^\r?\n]*\r?\n)* function \2\([^\)]*\) \{\r?\n)/,
+ /^(var (\S+) = )(\(function \(_super\) \{\r?\n \w*__extends\(\w+, _super\);\r?\n)/,
+];
+
+export function getPrefixClassesTransformer(): ts.TransformerFactory {
+ return (context: ts.TransformationContext): ts.Transformer => {
+ const transformer: ts.Transformer = (sf: ts.SourceFile) => {
+
+ const pureFunctionComment = '@__PURE__';
+
+ const visitor: ts.Visitor = (node: ts.Node): ts.Node => {
+
+ // Add pure comment to downleveled classes.
+ if (isDownleveledClass(node)) {
+ const varDecl = node as ts.VariableDeclaration;
+ const varDeclInit = varDecl.initializer as ts.Expression;
+
+ // Create a new node with the pure comment before the variable declaration initializer.
+ const newNode = ts.createVariableDeclaration(
+ varDecl.name,
+ undefined,
+ ts.addSyntheticLeadingComment(
+ varDeclInit, ts.SyntaxKind.MultiLineCommentTrivia, pureFunctionComment, false,
+ ),
+ );
+
+ // Replace node with modified one.
+ return ts.visitEachChild(newNode, visitor, context);
+ }
+
+ // Otherwise return node as is.
+ return ts.visitEachChild(node, visitor, context);
+ };
+
+ return ts.visitNode(sf, visitor);
+ };
+
+ return transformer;
+ };
+}
+
+// Determine if a node matched the structure of a downleveled TS class.
+function isDownleveledClass(node: ts.Node): boolean {
+ let isExtendedClass = false;
+
+ if (node.kind !== ts.SyntaxKind.VariableDeclaration) {
+ return false;
+ }
+
+ const varDecl = node as ts.VariableDeclaration;
+
+ if (varDecl.name.kind !== ts.SyntaxKind.Identifier) {
+ return false;
+ }
+
+ // The variable name should be the class name.
+ const className = (varDecl.name as ts.Identifier).text;
+
+ if (!varDecl.initializer || varDecl.initializer.kind !== ts.SyntaxKind.ParenthesizedExpression) {
+ return false;
+ }
+
+ const parenExpr = varDecl.initializer as ts.ParenthesizedExpression;
+
+ if (parenExpr.expression.kind !== ts.SyntaxKind.CallExpression) {
+ return false;
+ }
+
+ const callExpr = parenExpr.expression as ts.CallExpression;
+
+ if (callExpr.expression.kind !== ts.SyntaxKind.FunctionExpression) {
+ return false;
+ }
+
+ const funcExpr = callExpr.expression as ts.FunctionExpression;
+
+ // Extended classes have the `_super` parameter.
+ if (funcExpr.parameters.length === 1
+ && (funcExpr.parameters[0].name as ts.Identifier).text === '_super') {
+ isExtendedClass = true;
+ }
+
+ // IIFE inner parameters should be empty or `_super`.
+ if (funcExpr.parameters.length !== 0 && !isExtendedClass) {
+ return false;
+ }
+
+ const stmts = funcExpr.body.statements;
+
+ if (stmts.length === 0) {
+ return false;
+ }
+
+ const firstStatement = stmts[0];
+
+ // Check if `node` is a FunctionDeclaration named `name`.
+ function isFunDeclNamed(node: ts.Node, name: string) {
+ if (node.kind === ts.SyntaxKind.FunctionDeclaration) {
+ const funcDecl = node as ts.FunctionDeclaration;
+ if (funcDecl.name && funcDecl.name.text === name) {
+ return true;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ // If the class is extending another, the first statement is a _extends(..., _super) call.
+ if (isExtendedClass) {
+ if (firstStatement.kind !== ts.SyntaxKind.ExpressionStatement) {
+ return false;
+ }
+ const exprStmt = firstStatement as ts.ExpressionStatement;
+
+ if (exprStmt.expression.kind !== ts.SyntaxKind.CallExpression) {
+ return false;
+ }
+
+ const extendsCallExpr = exprStmt.expression as ts.CallExpression;
+
+ // Function should be called `__extends`.
+ if (extendsCallExpr.expression.kind !== ts.SyntaxKind.Identifier) {
+ return false;
+ }
+
+ const callExprName = (extendsCallExpr.expression as ts.Identifier).text;
+
+ // Reserved TS names are retrieved with three underscores instead of two.
+ if (callExprName !== '___extends') {
+ return false;
+ }
+
+ // Function should have 1+ arguments, with the last being named `_super`.
+ if (extendsCallExpr.arguments.length === 0) {
+ return false;
+ }
+
+ const lastArg = extendsCallExpr.arguments[extendsCallExpr.arguments.length - 1];
+
+ if (lastArg.kind !== ts.SyntaxKind.Identifier) {
+ return false;
+ }
+
+ const lastArgName = (lastArg as ts.Identifier).text;
+
+ if (lastArgName !== '_super') {
+ return false;
+ }
+
+ const secondStatement = stmts[1];
+
+ if (secondStatement && isFunDeclNamed(secondStatement, className)) {
+ // This seems to be downleveled class that extends another class.
+ return true;
+ }
+
+ } else if (isFunDeclNamed(firstStatement, className)) {
+ // This seems to be downleveled class.
+ return true;
+ }
+
+ return false;
+}
diff --git a/packages/angular_devkit/build_optimizer/src/transforms/prefix-classes_spec.ts b/packages/angular_devkit/build_optimizer/src/transforms/prefix-classes_spec.ts
new file mode 100644
index 0000000000..f734dbe67a
--- /dev/null
+++ b/packages/angular_devkit/build_optimizer/src/transforms/prefix-classes_spec.ts
@@ -0,0 +1,82 @@
+/**
+ * @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 { oneLine, stripIndent } from 'common-tags';
+import { transformJavascript } from '../helpers/transform-javascript';
+import { getPrefixClassesTransformer, prefixClassRegexes } from './prefix-classes';
+
+
+const transform = (content: string) => transformJavascript(
+ { content, getTransforms: [getPrefixClassesTransformer] }).content;
+
+describe('prefix-classes', () => {
+ it('prefix downleveled classes with /*@__PURE__*/', () => {
+ const input = stripIndent`
+ var ReplayEvent = (function () {
+ function ReplayEvent(time, value) {
+ this.time = time;
+ this.value = value;
+ }
+ return ReplayEvent;
+ }());
+ `;
+ const output = stripIndent`
+ var ReplayEvent = /*@__PURE__*/ (function () {
+ function ReplayEvent(time, value) {
+ this.time = time;
+ this.value = value;
+ }
+ return ReplayEvent;
+ }());
+ `;
+
+ expect(prefixClassRegexes.some((regex) => regex.test(input))).toBeTruthy();
+ expect(oneLine`${transform(input)}`).toEqual(oneLine`${output}`);
+ });
+
+ // tslint:disable:max-line-length
+ it('prefix downleveled classes that extend another class with /*@__PURE__*/', () => {
+ const input = stripIndent`
+ var TakeUntilSubscriber = (function (_super) {
+ __extends(TakeUntilSubscriber, _super);
+ function TakeUntilSubscriber(destination, notifier) {
+ _super.call(this, destination);
+ this.notifier = notifier;
+ this.add(subscribeToResult_1.subscribeToResult(this, notifier));
+ }
+ TakeUntilSubscriber.prototype.notifyNext = function (outerValue, innerValue, outerIndex, innerIndex, innerSub) {
+ this.complete();
+ };
+ TakeUntilSubscriber.prototype.notifyComplete = function () {
+ // noop
+ };
+ return TakeUntilSubscriber;
+ }(OuterSubscriber_1.OuterSubscriber));
+ `;
+ const output = stripIndent`
+ var TakeUntilSubscriber = /*@__PURE__*/ (function (_super) {
+ __extends(TakeUntilSubscriber, _super);
+ function TakeUntilSubscriber(destination, notifier) {
+ _super.call(this, destination);
+ this.notifier = notifier;
+ this.add(subscribeToResult_1.subscribeToResult(this, notifier));
+ }
+ TakeUntilSubscriber.prototype.notifyNext = function (outerValue, innerValue, outerIndex, innerIndex, innerSub) {
+ this.complete();
+ };
+ TakeUntilSubscriber.prototype.notifyComplete = function () {
+ // noop
+ };
+ return TakeUntilSubscriber;
+ }(OuterSubscriber_1.OuterSubscriber));
+ `;
+
+ expect(prefixClassRegexes.some((regex) => regex.test(input))).toBeTruthy();
+ expect(oneLine`${transform(input)}`).toEqual(oneLine`${output}`);
+ });
+ // tslint:enable:max-line-length
+});
From a6f213087937136168ed35f8d4ed7ac9de838493 Mon Sep 17 00:00:00 2001
From: Filipe Silva
Date: Tue, 8 Aug 2017 21:14:55 +0100
Subject: [PATCH 0016/1016] refactor(@angular-devkit/build-optimizer): move
regexes into transform
---
.../src/build-optimizer/build-optimizer.ts | 25 ++++++++---------
.../build-optimizer/build-optimizer_spec.ts | 10 +++----
.../src/transforms/import-tslib.ts | 3 ++
.../src/transforms/import-tslib_spec.ts | 28 +++++++++++--------
.../src/transforms/prefix-functions.ts | 5 ++++
.../src/transforms/scrub-file.ts | 6 ++++
.../src/transforms/scrub-file_spec.ts | 5 +++-
7 files changed, 50 insertions(+), 32 deletions(-)
diff --git a/packages/angular_devkit/build_optimizer/src/build-optimizer/build-optimizer.ts b/packages/angular_devkit/build_optimizer/src/build-optimizer/build-optimizer.ts
index 38736fcb73..c250a1da28 100644
--- a/packages/angular_devkit/build_optimizer/src/build-optimizer/build-optimizer.ts
+++ b/packages/angular_devkit/build_optimizer/src/build-optimizer/build-optimizer.ts
@@ -8,15 +8,12 @@
import { readFileSync } from 'fs';
import { TransformJavascriptOutput, transformJavascript } from '../helpers/transform-javascript';
import { getFoldFileTransformer } from '../transforms/class-fold';
-import { getImportTslibTransformer } from '../transforms/import-tslib';
+import { getImportTslibTransformer, importTslibRegexes } from '../transforms/import-tslib';
import { getPrefixClassesTransformer, prefixClassRegexes } from '../transforms/prefix-classes';
import { getPrefixFunctionsTransformer } from '../transforms/prefix-functions';
-import { getScrubFileTransformer } from '../transforms/scrub-file';
+import { getScrubFileTransformer, scrubFileRegexes } from '../transforms/scrub-file';
-const hasDecorators = /decorators/;
-const hasCtorParameters = /ctorParameters/;
-const hasTsHelpers = /var (__extends|__decorate|__metadata|__param) = /;
const isAngularModuleFile = /\.es5\.js$/;
const whitelistedAngularModules = [
/(\\|\/)node_modules(\\|\/)@angular(\\|\/)animations(\\|\/)/,
@@ -59,6 +56,14 @@ export function buildOptimizer(options: BuildOptimizerOptions): TransformJavascr
// Determine which transforms to apply.
const getTransforms = [];
+ if (importTslibRegexes.some((regex) => regex.test(content as string))) {
+ getTransforms.push(getImportTslibTransformer);
+ }
+
+ if (prefixClassRegexes.some((regex) => regex.test(content as string))) {
+ getTransforms.push(getPrefixClassesTransformer);
+ }
+
if (inputFilePath
&& isAngularModuleFile.test(inputFilePath)
&& whitelistedAngularModules.some((re) => re.test(inputFilePath))
@@ -72,20 +77,12 @@ export function buildOptimizer(options: BuildOptimizerOptions): TransformJavascr
getScrubFileTransformer,
getFoldFileTransformer,
);
- } else if (hasDecorators.test(content) || hasCtorParameters.test(content)) {
+ } else if (scrubFileRegexes.some((regex) => regex.test(content as string))) {
getTransforms.push(
getScrubFileTransformer,
getFoldFileTransformer,
);
}
- if (hasTsHelpers.test(content)) {
- getTransforms.push(getImportTslibTransformer);
- }
-
- if (prefixClassRegexes.some((regex) => regex.test(content as string))) {
- getTransforms.push(getPrefixClassesTransformer);
- }
-
return transformJavascript({ ...options, getTransforms, content });
}
diff --git a/packages/angular_devkit/build_optimizer/src/build-optimizer/build-optimizer_spec.ts b/packages/angular_devkit/build_optimizer/src/build-optimizer/build-optimizer_spec.ts
index 64b9c0cc7c..974e0531f9 100644
--- a/packages/angular_devkit/build_optimizer/src/build-optimizer/build-optimizer_spec.ts
+++ b/packages/angular_devkit/build_optimizer/src/build-optimizer/build-optimizer_spec.ts
@@ -5,7 +5,7 @@
* 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 { oneLine } from 'common-tags';
+import { oneLine, stripIndent } from 'common-tags';
import { RawSourceMap } from 'source-map';
import { buildOptimizer } from './build-optimizer';
@@ -18,12 +18,12 @@ describe('build-optimizer', () => {
describe('basic functionality', () => {
it('applies class-fold, scrub-file and prefix-functions', () => {
- const input = oneLine`
+ const input = stripIndent`
${imports}
var __extends = (this && this.__extends) || function (d, b) {
- for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
- function __() { this.constructor = d; }
- d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+ for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
+ function __() { this.constructor = d; }
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
${clazz}
${staticProperty}
diff --git a/packages/angular_devkit/build_optimizer/src/transforms/import-tslib.ts b/packages/angular_devkit/build_optimizer/src/transforms/import-tslib.ts
index d75f397dff..60518c39b2 100644
--- a/packages/angular_devkit/build_optimizer/src/transforms/import-tslib.ts
+++ b/packages/angular_devkit/build_optimizer/src/transforms/import-tslib.ts
@@ -7,6 +7,9 @@
*/
import * as ts from 'typescript';
+export const importTslibRegexes = [
+ /var (__extends|__decorate|__metadata|__param) = \(.*\r?\n( .*\r?\n)*\};/,
+];
export function getImportTslibTransformer(): ts.TransformerFactory {
return (context: ts.TransformationContext): ts.Transformer => {
diff --git a/packages/angular_devkit/build_optimizer/src/transforms/import-tslib_spec.ts b/packages/angular_devkit/build_optimizer/src/transforms/import-tslib_spec.ts
index a6c5bbb58b..0d991a70e4 100644
--- a/packages/angular_devkit/build_optimizer/src/transforms/import-tslib_spec.ts
+++ b/packages/angular_devkit/build_optimizer/src/transforms/import-tslib_spec.ts
@@ -7,29 +7,30 @@
*/
import { oneLine, stripIndent } from 'common-tags';
import { transformJavascript } from '../helpers/transform-javascript';
-import { getImportTslibTransformer } from './import-tslib';
+import { getImportTslibTransformer, importTslibRegexes } from './import-tslib';
const transform = (content: string) => transformJavascript(
{ content, getTransforms: [getImportTslibTransformer] }).content;
describe('import-tslib', () => {
- it('replaces __extends with', () => {
+ it('replaces __extends', () => {
const input = stripIndent`
var __extends = (this && this.__extends) || function (d, b) {
- for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
- function __() { this.constructor = d; }
- d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+ for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
+ function __() { this.constructor = d; }
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
`;
const output = stripIndent`
import { __extends } from "tslib";
`;
+ expect(importTslibRegexes.some((regex) => regex.test(input))).toBeTruthy();
expect(oneLine`${transform(input)}`).toEqual(oneLine`${output}`);
});
- it('replaces __decorate with', () => {
+ it('replaces __decorate', () => {
// tslint:disable:max-line-length
const input = stripIndent`
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
@@ -44,14 +45,15 @@ describe('import-tslib', () => {
import { __decorate } from "tslib";
`;
+ expect(importTslibRegexes.some((regex) => regex.test(input))).toBeTruthy();
expect(oneLine`${transform(input)}`).toEqual(oneLine`${output}`);
});
- it('replaces __metadata with', () => {
+ it('replaces __metadata', () => {
// tslint:disable:max-line-length
const input = stripIndent`
var __metadata = (this && this.__metadata) || function (k, v) {
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
`;
// tslint:enable:max-line-length
@@ -59,10 +61,11 @@ describe('import-tslib', () => {
import { __metadata } from "tslib";
`;
+ expect(importTslibRegexes.some((regex) => regex.test(input))).toBeTruthy();
expect(oneLine`${transform(input)}`).toEqual(oneLine`${output}`);
});
- it('replaces __param with', () => {
+ it('replaces __param', () => {
const input = stripIndent`
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
@@ -73,15 +76,16 @@ describe('import-tslib', () => {
import { __param } from "tslib";
`;
+ expect(importTslibRegexes.some((regex) => regex.test(input))).toBeTruthy();
expect(oneLine`${transform(input)}`).toEqual(oneLine`${output}`);
});
it('replaces uses "require" instead of "import" on CJS modules', () => {
const input = stripIndent`
var __extends = (this && this.__extends) || function (d, b) {
- for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
- function __() { this.constructor = d; }
- d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+ for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
+ function __() { this.constructor = d; }
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
exports.meaning = 42;
`;
diff --git a/packages/angular_devkit/build_optimizer/src/transforms/prefix-functions.ts b/packages/angular_devkit/build_optimizer/src/transforms/prefix-functions.ts
index 5ee82d9637..a2ae3a6b8c 100644
--- a/packages/angular_devkit/build_optimizer/src/transforms/prefix-functions.ts
+++ b/packages/angular_devkit/build_optimizer/src/transforms/prefix-functions.ts
@@ -7,6 +7,11 @@
*/
import * as ts from 'typescript';
+export const prefixClassRegexes = [
+ // tslint:disable-next-line:max-line-length
+ /^(var (\S+) = )(\(function \(\) \{\r?\n(?: (?:\/\*\*| \*|\*\/|\/\/)[^\r?\n]*\r?\n)* function \2\([^\)]*\) \{\r?\n)/mg,
+ /^(var (\S+) = )(\(function \(_super\) \{\r?\n \w*__extends\(\w+, _super\);\r?\n)/mg,
+];
export function getPrefixFunctionsTransformer(): ts.TransformerFactory {
return (context: ts.TransformationContext): ts.Transformer => {
diff --git a/packages/angular_devkit/build_optimizer/src/transforms/scrub-file.ts b/packages/angular_devkit/build_optimizer/src/transforms/scrub-file.ts
index 10ec497a1f..0e3fc3dfa4 100644
--- a/packages/angular_devkit/build_optimizer/src/transforms/scrub-file.ts
+++ b/packages/angular_devkit/build_optimizer/src/transforms/scrub-file.ts
@@ -8,6 +8,12 @@
import * as ts from 'typescript';
+export const scrubFileRegexes = [
+ /decorators/,
+ /propDecorators/,
+ /ctorParameters/,
+];
+
// Don't remove `ctorParameters` from these.
const platformWhitelist = [
'PlatformRef_',
diff --git a/packages/angular_devkit/build_optimizer/src/transforms/scrub-file_spec.ts b/packages/angular_devkit/build_optimizer/src/transforms/scrub-file_spec.ts
index 7a12e33bcd..97334591db 100644
--- a/packages/angular_devkit/build_optimizer/src/transforms/scrub-file_spec.ts
+++ b/packages/angular_devkit/build_optimizer/src/transforms/scrub-file_spec.ts
@@ -7,7 +7,7 @@
*/
import { oneLine, stripIndent } from 'common-tags';
import { transformJavascript } from '../helpers/transform-javascript';
-import { getScrubFileTransformer } from './scrub-file';
+import { getScrubFileTransformer, scrubFileRegexes } from './scrub-file';
const transform = (content: string) => transformJavascript(
@@ -27,6 +27,7 @@ describe('scrub-file', () => {
Clazz.decorators = [ { type: Injectable } ];
`;
+ expect(scrubFileRegexes.some((regex) => regex.test(input))).toBeTruthy();
expect(oneLine`${transform(input)}`).toEqual(oneLine`${output}`);
});
@@ -69,6 +70,7 @@ describe('scrub-file', () => {
Clazz.propDecorators = { 'ngIf': [{ type: Input }] }
`;
+ expect(scrubFileRegexes.some((regex) => regex.test(input))).toBeTruthy();
expect(oneLine`${transform(input)}`).toEqual(oneLine`${output}`);
});
@@ -115,6 +117,7 @@ describe('scrub-file', () => {
Clazz.ctorParameters = function () { return []; };
`;
+ expect(scrubFileRegexes.some((regex) => regex.test(input))).toBeTruthy();
expect(oneLine`${transform(input)}`).toEqual(oneLine`${output}`);
});
From d92d0ee2ff34a283ddccce7b71dd6da14d399ec0 Mon Sep 17 00:00:00 2001
From: Filipe Silva
Date: Thu, 10 Aug 2017 17:53:32 +0100
Subject: [PATCH 0017/1016] feat(@angular-devkit/build-optimizer): add wrap
enum transformer
---
.../angular_devkit/build_optimizer/README.md | 29 +-
.../src/build-optimizer/build-optimizer.ts | 5 +
.../build-optimizer/build-optimizer_spec.ts | 12 +-
.../build_optimizer/src/helpers/ast-utils.ts | 41 +++
.../build_optimizer/src/purify/purify.ts | 12 -
.../build_optimizer/src/purify/purify_spec.ts | 41 ---
.../src/transforms/prefix-functions.ts | 20 +-
.../src/transforms/scrub-file.ts | 15 +-
.../src/transforms/wrap-enums.ts | 302 ++++++++++++++++++
.../src/transforms/wrap-enums_spec.ts | 60 ++++
10 files changed, 461 insertions(+), 76 deletions(-)
create mode 100644 packages/angular_devkit/build_optimizer/src/helpers/ast-utils.ts
create mode 100644 packages/angular_devkit/build_optimizer/src/transforms/wrap-enums.ts
create mode 100644 packages/angular_devkit/build_optimizer/src/transforms/wrap-enums_spec.ts
diff --git a/packages/angular_devkit/build_optimizer/README.md b/packages/angular_devkit/build_optimizer/README.md
index fcd4989746..b9d1354f8f 100644
--- a/packages/angular_devkit/build_optimizer/README.md
+++ b/packages/angular_devkit/build_optimizer/README.md
@@ -56,6 +56,8 @@ Clazz.decorators = [{ type: NotInjectable }];
Adds `/*@__PURE__*/` comments to top level downleveled class declarations and instantiation.
Webpack library imports are also marked as `/*@__PURE__*/` when used with [Purify Plugin](#purify-plugin).
+Warning: this transform assumes the file is a pure module. It should not be used with unpure modules.
+
```typescript
// input
var Clazz = (function () { function Clazz() { } return Clazz; }());
@@ -71,7 +73,7 @@ var newClazzTwo = /*@__PURE__*/ Clazz();
### Prefix Classes
-Adds `/*@__PURE__*/` to downlevered TypeScript classes.
+Adds `/*@__PURE__*/` to downleveled TypeScript classes.
```typescript
// input
@@ -110,11 +112,32 @@ var __extends = (this && this.__extends) || function (d, b) {
import { __extends } from "tslib";
```
+### Wrap enums
+
+Wrap downleveled TypeScript enums in a function, and adds `/*@__PURE__*/` comment.
+
+```typescript
+// input
+var ChangeDetectionStrategy;
+(function (ChangeDetectionStrategy) {
+ ChangeDetectionStrategy[ChangeDetectionStrategy["OnPush"] = 0] = "OnPush";
+ ChangeDetectionStrategy[ChangeDetectionStrategy["Default"] = 1] = "Default";
+})(ChangeDetectionStrategy || (ChangeDetectionStrategy = {}));
+
+// output
+var ChangeDetectionStrategy = /*@__PURE__*/ (function () {
+ var ChangeDetectionStrategy = {};
+ ChangeDetectionStrategy[ChangeDetectionStrategy["OnPush"] = 0] = "OnPush";
+ ChangeDetectionStrategy[ChangeDetectionStrategy["Default"] = 1] = "Default";
+ return ChangeDetectionStrategy;
+})();
+```
+
### Purify Plugin
-Performs regex based replacements on all files that add `/*@__PURE__*/` comments to downleveled classes, TypeScript
-enums and webpack imports (used with [Prefix functions](#prefix-functions))
+Performs regex based replacements on all bundles that add `/*@__PURE__*/` comments to
+known pure webpack imports (used with [Prefix functions](#prefix-functions)).
## Library Usage
diff --git a/packages/angular_devkit/build_optimizer/src/build-optimizer/build-optimizer.ts b/packages/angular_devkit/build_optimizer/src/build-optimizer/build-optimizer.ts
index c250a1da28..8ec6f161b1 100644
--- a/packages/angular_devkit/build_optimizer/src/build-optimizer/build-optimizer.ts
+++ b/packages/angular_devkit/build_optimizer/src/build-optimizer/build-optimizer.ts
@@ -12,6 +12,7 @@ import { getImportTslibTransformer, importTslibRegexes } from '../transforms/imp
import { getPrefixClassesTransformer, prefixClassRegexes } from '../transforms/prefix-classes';
import { getPrefixFunctionsTransformer } from '../transforms/prefix-functions';
import { getScrubFileTransformer, scrubFileRegexes } from '../transforms/scrub-file';
+import { getWrapEnumsTransformer, wrapEnumsRegexes } from '../transforms/wrap-enums';
const isAngularModuleFile = /\.es5\.js$/;
@@ -56,6 +57,10 @@ export function buildOptimizer(options: BuildOptimizerOptions): TransformJavascr
// Determine which transforms to apply.
const getTransforms = [];
+ if (wrapEnumsRegexes.some((regex) => regex.test(content as string))) {
+ getTransforms.push(getWrapEnumsTransformer);
+ }
+
if (importTslibRegexes.some((regex) => regex.test(content as string))) {
getTransforms.push(getImportTslibTransformer);
}
diff --git a/packages/angular_devkit/build_optimizer/src/build-optimizer/build-optimizer_spec.ts b/packages/angular_devkit/build_optimizer/src/build-optimizer/build-optimizer_spec.ts
index 974e0531f9..de32a3e46c 100644
--- a/packages/angular_devkit/build_optimizer/src/build-optimizer/build-optimizer_spec.ts
+++ b/packages/angular_devkit/build_optimizer/src/build-optimizer/build-optimizer_spec.ts
@@ -25,6 +25,11 @@ describe('build-optimizer', () => {
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
+ var ChangeDetectionStrategy;
+ (function (ChangeDetectionStrategy) {
+ ChangeDetectionStrategy[ChangeDetectionStrategy["OnPush"] = 0] = "OnPush";
+ ChangeDetectionStrategy[ChangeDetectionStrategy["Default"] = 1] = "Default";
+ })(ChangeDetectionStrategy || (ChangeDetectionStrategy = {}));
${clazz}
${staticProperty}
${decorators}
@@ -36,9 +41,14 @@ describe('build-optimizer', () => {
/** PURE_IMPORTS_START _angular_core,tslib PURE_IMPORTS_END */
${imports}
import { __extends } from "tslib";
+ var ChangeDetectionStrategy = /*@__PURE__*/ (function () {
+ var ChangeDetectionStrategy = {};
+ ChangeDetectionStrategy[ChangeDetectionStrategy["OnPush"] = 0] = "OnPush";
+ ChangeDetectionStrategy[ChangeDetectionStrategy["Default"] = 1] = "Default";
+ return ChangeDetectionStrategy;
+ })();
var Clazz = /*@__PURE__*/ (function () { function Clazz() { } ${staticProperty} return Clazz; }());
`;
- // tslint:enable:max-line-length
const inputFilePath = '/node_modules/@angular/core/@angular/core.es5.js';
const boOutput = buildOptimizer({ content: input, inputFilePath });
diff --git a/packages/angular_devkit/build_optimizer/src/helpers/ast-utils.ts b/packages/angular_devkit/build_optimizer/src/helpers/ast-utils.ts
new file mode 100644
index 0000000000..c99c23922c
--- /dev/null
+++ b/packages/angular_devkit/build_optimizer/src/helpers/ast-utils.ts
@@ -0,0 +1,41 @@
+/**
+ * @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 * as ts from 'typescript';
+
+// Find all nodes from the AST in the subtree of node of SyntaxKind kind.
+export function collectDeepNodes(node: ts.Node, kind: ts.SyntaxKind): T[] {
+ const nodes: T[] = [];
+ const helper = (child: ts.Node) => {
+ if (child.kind === kind) {
+ nodes.push(child as T);
+ }
+ ts.forEachChild(child, helper);
+ };
+ ts.forEachChild(node, helper);
+
+ return nodes;
+}
+
+export function drilldownNodes(
+ startingNode: ts.Node,
+ path: { prop: string | null, kind: ts.SyntaxKind }[],
+): T | null {
+ let currentNode: T | ts.Node | undefined = startingNode;
+ for (const segment of path) {
+ if (segment.prop) {
+ // ts.Node has no index signature, so we need to cast it as any.
+ // tslint:disable-next-line:no-any
+ currentNode = (currentNode as any)[segment.prop];
+ }
+ if (!currentNode || currentNode.kind !== segment.kind) {
+ return null;
+ }
+ }
+
+ return currentNode as T;
+}
diff --git a/packages/angular_devkit/build_optimizer/src/purify/purify.ts b/packages/angular_devkit/build_optimizer/src/purify/purify.ts
index b09aa44d47..450a031367 100644
--- a/packages/angular_devkit/build_optimizer/src/purify/purify.ts
+++ b/packages/angular_devkit/build_optimizer/src/purify/purify.ts
@@ -14,18 +14,6 @@ export function purify(content: string) {
const pureImportMatches = getMatches(content, importCommentRegex, 1).join('|');
const newContent = content
- /* wrap TS 2.2 enums w/ an IIFE */
- .replace(
- // tslint:disable-next-line:max-line-length
- /var (\S+) = \{\};\r?\n(\1\.(\S+) = \d+;\r?\n)+\1\[\1\.(\S+)\] = "\4";\r?\n(\1\[\1\.(\S+)\] = "\S+";\r?\n*)+/mg,
- 'var $1 = /*@__PURE__*/(function() {\n$&; return $1;})();\n',
- )
- /* wrap TS 2.3 enums w/ an IIFE */
- .replace(
- // tslint:disable-next-line:max-line-length
- /var (\S+);(\/\*@__PURE__\*\/)*\r?\n\(function \(\1\) \{\s+(\1\[\1\["(\S+)"\] = 0\] = "\4";(\s+\1\[\1\["\S+"\] = \d\] = "\S+";)*\r?\n)\}\)\(\1 \|\| \(\1 = \{\}\)\);/mg,
- 'var $1 = /*@__PURE__*/(function() {\n var $1 = {};\n $3 return $1;\n})();',
- )
/* Prefix safe imports with pure */
.replace(new RegExp(`(_(${pureImportMatches})__ = )(__webpack_require__\\(\\S+\\);)`, 'mg'),
'$1/*@__PURE__*/$3',
diff --git a/packages/angular_devkit/build_optimizer/src/purify/purify_spec.ts b/packages/angular_devkit/build_optimizer/src/purify/purify_spec.ts
index 6749b1d87c..78e53b84b0 100644
--- a/packages/angular_devkit/build_optimizer/src/purify/purify_spec.ts
+++ b/packages/angular_devkit/build_optimizer/src/purify/purify_spec.ts
@@ -10,47 +10,6 @@ import { purify } from './purify';
// tslint:disable:max-line-length
describe('purify', () => {
- it('wraps ts 2.2 enums in IIFE', () => {
- const input = stripIndent`
- var ChangeDetectionStrategy = {};
- ChangeDetectionStrategy.OnPush = 0;
- ChangeDetectionStrategy.Default = 1;
- ChangeDetectionStrategy[ChangeDetectionStrategy.OnPush] = "OnPush";
- ChangeDetectionStrategy[ChangeDetectionStrategy.Default] = "Default";
- `;
- const output = stripIndent`
- var ChangeDetectionStrategy = /*@__PURE__*/(function() {
- var ChangeDetectionStrategy = {};
- ChangeDetectionStrategy.OnPush = 0;
- ChangeDetectionStrategy.Default = 1;
- ChangeDetectionStrategy[ChangeDetectionStrategy.OnPush] = "OnPush";
- ChangeDetectionStrategy[ChangeDetectionStrategy.Default] = "Default";;
- return ChangeDetectionStrategy;})();
- `;
-
- expect(oneLine`${purify(input)}`).toEqual(oneLine`${output}`);
- });
-
- it('wraps ts 2.3 enums in IIFE', () => {
- const input = stripIndent`
- var ChangeDetectionStrategy;
- (function (ChangeDetectionStrategy) {
- ChangeDetectionStrategy[ChangeDetectionStrategy["OnPush"] = 0] = "OnPush";
- ChangeDetectionStrategy[ChangeDetectionStrategy["Default"] = 1] = "Default";
- })(ChangeDetectionStrategy || (ChangeDetectionStrategy = {}));
- `;
- const output = stripIndent`
- var ChangeDetectionStrategy = /*@__PURE__*/(function() {
- var ChangeDetectionStrategy = {};
- ChangeDetectionStrategy[ChangeDetectionStrategy["OnPush"] = 0] = "OnPush";
- ChangeDetectionStrategy[ChangeDetectionStrategy["Default"] = 1] = "Default";
- return ChangeDetectionStrategy;
- })();
- `;
-
- expect(oneLine`${purify(input)}`).toEqual(oneLine`${output}`);
- });
-
it('prefixes safe imports with /*@__PURE__*/', () => {
const input = stripIndent`
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2_rxjs_Subject__ = __webpack_require__("EEr4");
diff --git a/packages/angular_devkit/build_optimizer/src/transforms/prefix-functions.ts b/packages/angular_devkit/build_optimizer/src/transforms/prefix-functions.ts
index a2ae3a6b8c..43093116d9 100644
--- a/packages/angular_devkit/build_optimizer/src/transforms/prefix-functions.ts
+++ b/packages/angular_devkit/build_optimizer/src/transforms/prefix-functions.ts
@@ -13,11 +13,12 @@ export const prefixClassRegexes = [
/^(var (\S+) = )(\(function \(_super\) \{\r?\n \w*__extends\(\w+, _super\);\r?\n)/mg,
];
+const pureFunctionComment = '@__PURE__';
+
export function getPrefixFunctionsTransformer(): ts.TransformerFactory {
return (context: ts.TransformationContext): ts.Transformer => {
const transformer: ts.Transformer = (sf: ts.SourceFile) => {
- const pureFunctionComment = '@__PURE__';
const topLevelFunctions = findTopLevelFunctions(sf);
const pureImports = findPureImports(sf);
const pureImportsComment = `* PURE_IMPORTS_START ${pureImports.join(',')} PURE_IMPORTS_END `;
@@ -68,11 +69,14 @@ export function findTopLevelFunctions(parentNode: ts.Node): ts.Node[] {
// expressions: `(function() {}())` Their start pos doesn't include the opening paren
// and must be adjusted.
if (isIIFE(node)
- && previousNode.kind === ts.SyntaxKind.ParenthesizedExpression
- && node.parent) {
+ && previousNode.kind === ts.SyntaxKind.ParenthesizedExpression
+ && node.parent
+ && !hasPureComment(node.parent)) {
topLevelFunctions.push(node.parent);
- } else if (node.kind === ts.SyntaxKind.CallExpression
- || node.kind === ts.SyntaxKind.NewExpression) {
+ } else if ((node.kind === ts.SyntaxKind.CallExpression
+ || node.kind === ts.SyntaxKind.NewExpression)
+ && !hasPureComment(node)
+ ) {
topLevelFunctions.push(node);
}
@@ -112,3 +116,9 @@ export function findPureImports(parentNode: ts.Node): string[] {
return pureImports;
}
+
+function hasPureComment(node: ts.Node) {
+ const leadingComment = ts.getSyntheticLeadingComments(node);
+
+ return leadingComment && leadingComment.some((comment) => comment.text === pureFunctionComment);
+}
diff --git a/packages/angular_devkit/build_optimizer/src/transforms/scrub-file.ts b/packages/angular_devkit/build_optimizer/src/transforms/scrub-file.ts
index 0e3fc3dfa4..c49a160921 100644
--- a/packages/angular_devkit/build_optimizer/src/transforms/scrub-file.ts
+++ b/packages/angular_devkit/build_optimizer/src/transforms/scrub-file.ts
@@ -6,6 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import * as ts from 'typescript';
+import { collectDeepNodes } from '../helpers/ast-utils';
export const scrubFileRegexes = [
@@ -96,20 +97,6 @@ export function expect(node: ts.Node, kind: ts.SyntaxKind): T
return node as T;
}
-function collectDeepNodes(node: ts.Node, kind: ts.SyntaxKind): T[] {
- const nodes: T[] = [];
- const helper = (child: ts.Node) => {
- if (child.kind === kind) {
- // tslint:disable-next-line:no-any
- nodes.push(child as any as T);
- }
- ts.forEachChild(child, helper);
- };
- ts.forEachChild(node, helper);
-
- return nodes;
-}
-
function nameOfSpecifier(node: ts.ImportSpecifier): string {
return node.name && node.name.text || '';
}
diff --git a/packages/angular_devkit/build_optimizer/src/transforms/wrap-enums.ts b/packages/angular_devkit/build_optimizer/src/transforms/wrap-enums.ts
new file mode 100644
index 0000000000..3123554cdc
--- /dev/null
+++ b/packages/angular_devkit/build_optimizer/src/transforms/wrap-enums.ts
@@ -0,0 +1,302 @@
+/**
+ * @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 * as ts from 'typescript';
+import { collectDeepNodes, drilldownNodes } from '../helpers/ast-utils';
+
+
+export const wrapEnumsRegexes = [
+ // tslint:disable:max-line-length
+ /var (\S+) = \{\};\r?\n(\1\.(\S+) = \d+;\r?\n)+\1\[\1\.(\S+)\] = "\4";\r?\n(\1\[\1\.(\S+)\] = "\S+";\r?\n*)+/,
+ /var (\S+);(\/\*@__PURE__\*\/)*\r?\n\(function \(\1\) \{\s+(\1\[\1\["(\S+)"\] = 0\] = "\4";(\s+\1\[\1\["\S+"\] = \d\] = "\S+";)*\r?\n)\}\)\(\1 \|\| \(\1 = \{\}\)\);/,
+ // tslint:enable:max-line-length
+];
+
+interface EnumData {
+ name: string;
+ hostNode: ts.Node;
+ statements: ts.ExpressionStatement[];
+ dropNodes: ts.Node[];
+}
+
+export function getWrapEnumsTransformer(): ts.TransformerFactory {
+ return (context: ts.TransformationContext): ts.Transformer => {
+ const transformer: ts.Transformer = (sf: ts.SourceFile) => {
+
+ const enums = findEnumDeclarations(sf);
+ const dropNodes = enums.reduce((acc: ts.Node[], curr) => acc.concat(curr.dropNodes), []);
+
+ const visitor: ts.Visitor = (node: ts.Node): ts.Node => {
+
+ const enumData = enums.find((e) => e.hostNode === node);
+ if (enumData) {
+ // Replace node with a wrapped enum.
+ return ts.visitEachChild(createWrappedEnum(enumData), visitor, context);
+ }
+
+ // Drop enum nodes we relocated.
+ if (dropNodes.find((n) => n === node)) {
+ // According to @mhegazy returning undefined is supported.
+ // https://github.com/Microsoft/TypeScript/pull/17044
+ // tslint:disable-next-line:no-any
+ return undefined as any;
+ }
+
+ // Otherwise return node as is.
+ return ts.visitEachChild(node, visitor, context);
+ };
+
+ return ts.visitNode(sf, visitor);
+ };
+
+ return transformer;
+ };
+}
+
+
+// Find all enum declarations, build a EnumData for each.
+function findEnumDeclarations(sourceFile: ts.SourceFile): EnumData[] {
+ const enums: EnumData[] = [];
+
+ const enumHoldingNodes = [
+ sourceFile,
+ ...collectDeepNodes(sourceFile, ts.SyntaxKind.Block),
+ ];
+
+ enumHoldingNodes.forEach((node) => {
+
+ const stmts = node.statements;
+
+ stmts.forEach((stmt, idx) => {
+ // We're looking for a variable statement with more statements after it.
+ if (idx >= stmts.length - 1
+ || stmt.kind !== ts.SyntaxKind.VariableStatement) {
+ return;
+ }
+
+ const varStmt = stmt as ts.VariableStatement;
+
+ if (varStmt.declarationList.declarations.length !== 1) {
+ return;
+ }
+
+ // We've found a single variable declaration statement, it might be the start of an enum.
+ const maybeHostNode = varStmt;
+ const varDecl = maybeHostNode.declarationList.declarations[0];
+
+ if (varDecl.name.kind !== ts.SyntaxKind.Identifier) {
+ return;
+ }
+
+ const maybeName = (varDecl.name as ts.Identifier).text;
+ const enumStatements: ts.ExpressionStatement[] = [], enumDropNodes: ts.Node[] = [];
+
+ // Try to figure out the enum type from the variable declaration.
+ if (!varDecl.initializer) {
+ // Typescript 2.3 enums have no initializer.
+ const nextStatement = stmts[idx + 1];
+ enumStatements.push(...findTs2_3EnumStatements(maybeName, nextStatement));
+ enumDropNodes.push(nextStatement);
+ } else if (varDecl.initializer
+ && varDecl.initializer.kind === ts.SyntaxKind.ObjectLiteralExpression
+ && (varDecl.initializer as ts.ObjectLiteralExpression).properties.length === 0) {
+ // Typescript 2.2 enums have a {} initializer.
+ const nextStatements = stmts.slice(idx + 1);
+ const statements = findTs2_2EnumStatements(maybeName, nextStatements);
+ // We have to create new statements so we can keep new ones and drop old ones.
+ enumStatements.push(...statements.map(stmt => ts.createStatement(stmt.expression)));
+ enumDropNodes.push(...statements);
+ } else {
+ return;
+ }
+
+ if (enumStatements.length === 0) {
+ return;
+ }
+
+ enums.push({
+ name: maybeName,
+ hostNode: maybeHostNode,
+ statements: enumStatements,
+ dropNodes: enumDropNodes,
+ });
+ });
+ });
+
+ return enums;
+}
+
+// TS 2.3 enums have statements are inside a IIFE.
+function findTs2_3EnumStatements(name: string, statement: ts.Statement): ts.ExpressionStatement[] {
+ const enumStatements: ts.ExpressionStatement[] = [];
+ const noNodes: ts.ExpressionStatement[] = [];
+
+ const funcExpr = drilldownNodes(statement,
+ [
+ { prop: null, kind: ts.SyntaxKind.ExpressionStatement },
+ { prop: 'expression', kind: ts.SyntaxKind.CallExpression },
+ { prop: 'expression', kind: ts.SyntaxKind.ParenthesizedExpression },
+ { prop: 'expression', kind: ts.SyntaxKind.FunctionExpression },
+ ]);
+
+ if (funcExpr === null) { return noNodes; }
+
+ if (!(
+ funcExpr.parameters.length === 1
+ && funcExpr.parameters[0].name.kind === ts.SyntaxKind.Identifier
+ && (funcExpr.parameters[0].name as ts.Identifier).text === name
+ )) {
+ return noNodes;
+ }
+
+ // In TS 2.3 enums, the IIFE contains only expressions with a certain format.
+ // If we find any that is different, we ignore the whole thing.
+ for (const innerStmt of funcExpr.body.statements) {
+
+ const innerBinExpr = drilldownNodes(innerStmt,
+ [
+ { prop: null, kind: ts.SyntaxKind.ExpressionStatement },
+ { prop: 'expression', kind: ts.SyntaxKind.BinaryExpression },
+ ]);
+
+ if (innerBinExpr === null) { return noNodes; }
+
+ const exprStmt = innerStmt as ts.ExpressionStatement;
+
+ if (!(innerBinExpr.operatorToken.kind === ts.SyntaxKind.FirstAssignment
+ && innerBinExpr.left.kind === ts.SyntaxKind.ElementAccessExpression)) {
+ return noNodes;
+ }
+
+ const innerElemAcc = innerBinExpr.left as ts.ElementAccessExpression;
+
+ if (!(
+ innerElemAcc.expression.kind === ts.SyntaxKind.Identifier
+ && (innerElemAcc.expression as ts.Identifier).text === name
+ && innerElemAcc.argumentExpression
+ && innerElemAcc.argumentExpression.kind === ts.SyntaxKind.BinaryExpression
+ )) {
+ return noNodes;
+ }
+
+ const innerArgBinExpr = innerElemAcc.argumentExpression as ts.BinaryExpression;
+
+ if (innerArgBinExpr.left.kind !== ts.SyntaxKind.ElementAccessExpression) {
+ return noNodes;
+ }
+
+ const innerArgElemAcc = innerArgBinExpr.left as ts.ElementAccessExpression;
+
+ if (!(
+ innerArgElemAcc.expression.kind === ts.SyntaxKind.Identifier
+ && (innerArgElemAcc.expression as ts.Identifier).text === name
+ )) {
+ return noNodes;
+ }
+
+ enumStatements.push(exprStmt);
+ }
+
+ return enumStatements;
+}
+
+// TS 2.2 enums have statements after the variable declaration, with index statements followed
+// by value statements.
+function findTs2_2EnumStatements(
+ name: string,
+ statements: ts.Statement[],
+): ts.ExpressionStatement[] {
+ const enumStatements: ts.ExpressionStatement[] = [];
+ let beforeValueStatements = true;
+
+ for (const stmt of statements) {
+ // Ensure all statements are of the expected format and using the right identifer.
+ // When we find a statement that isn't part of the enum, return what we collected so far.
+ const binExpr = drilldownNodes(stmt,
+ [
+ { prop: null, kind: ts.SyntaxKind.ExpressionStatement },
+ { prop: 'expression', kind: ts.SyntaxKind.BinaryExpression },
+ ]);
+
+ if (binExpr === null
+ || (binExpr.left.kind !== ts.SyntaxKind.PropertyAccessExpression
+ && binExpr.left.kind !== ts.SyntaxKind.ElementAccessExpression)
+ ) {
+ return beforeValueStatements ? [] : enumStatements;
+ }
+
+ const exprStmt = stmt as ts.ExpressionStatement;
+ const leftExpr = binExpr.left as ts.PropertyAccessExpression | ts.ElementAccessExpression;
+
+ if (!(leftExpr.expression.kind === ts.SyntaxKind.Identifier
+ && (leftExpr.expression as ts.Identifier).text === name)) {
+ return beforeValueStatements ? [] : enumStatements;
+ }
+
+ if (!beforeValueStatements && leftExpr.kind === ts.SyntaxKind.PropertyAccessExpression) {
+ // We shouldn't find index statements after value statements.
+ return [];
+ } else if (beforeValueStatements && leftExpr.kind === ts.SyntaxKind.ElementAccessExpression) {
+ beforeValueStatements = false;
+ }
+
+ enumStatements.push(exprStmt);
+ }
+
+ return enumStatements;
+}
+
+function createWrappedEnum(enumData: EnumData): ts.Node {
+ const pureFunctionComment = '@__PURE__';
+
+ const { name, statements } = enumData;
+
+ const innerVarStmt = ts.createVariableStatement(
+ undefined,
+ ts.createVariableDeclarationList([
+ ts.createVariableDeclaration(name, undefined, ts.createObjectLiteral()),
+ ]),
+ );
+
+ const innerReturn = ts.createReturn(ts.createIdentifier(name));
+
+ const iife = ts.createCall(
+ ts.createParen(
+ ts.createFunctionExpression(
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ [],
+ undefined,
+ ts.createBlock([
+ innerVarStmt,
+ ...statements,
+ innerReturn,
+ ]),
+ ),
+ ),
+ undefined,
+ [],
+ );
+
+ // Create a new node with the pure comment before the variable declaration initializer.
+ const outerVarStmt = ts.createVariableStatement(
+ undefined,
+ ts.createVariableDeclarationList([
+ ts.createVariableDeclaration(
+ name,
+ undefined,
+ ts.addSyntheticLeadingComment(
+ iife, ts.SyntaxKind.MultiLineCommentTrivia, pureFunctionComment, false,
+ ),
+ ),
+ ]),
+ );
+
+ return outerVarStmt;
+}
diff --git a/packages/angular_devkit/build_optimizer/src/transforms/wrap-enums_spec.ts b/packages/angular_devkit/build_optimizer/src/transforms/wrap-enums_spec.ts
new file mode 100644
index 0000000000..4e2cf8cfd2
--- /dev/null
+++ b/packages/angular_devkit/build_optimizer/src/transforms/wrap-enums_spec.ts
@@ -0,0 +1,60 @@
+/**
+ * @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 { oneLine, stripIndent } from 'common-tags';
+import { transformJavascript } from '../helpers/transform-javascript';
+import { getWrapEnumsTransformer, wrapEnumsRegexes } from './wrap-enums';
+
+
+const transform = (content: string) => transformJavascript(
+ { content, getTransforms: [getWrapEnumsTransformer] }).content;
+
+describe('prefix-classes', () => {
+ it('wraps ts 2.2 enums in IIFE', () => {
+ const input = stripIndent`
+ var ChangeDetectionStrategy = {};
+ ChangeDetectionStrategy.OnPush = 0;
+ ChangeDetectionStrategy.Default = 1;
+ ChangeDetectionStrategy[ChangeDetectionStrategy.OnPush] = "OnPush";
+ ChangeDetectionStrategy[ChangeDetectionStrategy.Default] = "Default";
+ `;
+ const output = stripIndent`
+ var ChangeDetectionStrategy = /*@__PURE__*/ (function () {
+ var ChangeDetectionStrategy = {};
+ ChangeDetectionStrategy.OnPush = 0;
+ ChangeDetectionStrategy.Default = 1;
+ ChangeDetectionStrategy[ChangeDetectionStrategy.OnPush] = "OnPush";
+ ChangeDetectionStrategy[ChangeDetectionStrategy.Default] = "Default";
+ return ChangeDetectionStrategy;
+ })();
+ `;
+
+ expect(wrapEnumsRegexes.some((regex) => regex.test(input))).toBeTruthy();
+ expect(oneLine`${transform(input)}`).toEqual(oneLine`${output}`);
+ });
+
+ it('wraps ts 2.3 enums in IIFE', () => {
+ const input = stripIndent`
+ var ChangeDetectionStrategy;
+ (function (ChangeDetectionStrategy) {
+ ChangeDetectionStrategy[ChangeDetectionStrategy["OnPush"] = 0] = "OnPush";
+ ChangeDetectionStrategy[ChangeDetectionStrategy["Default"] = 1] = "Default";
+ })(ChangeDetectionStrategy || (ChangeDetectionStrategy = {}));
+ `;
+ const output = stripIndent`
+ var ChangeDetectionStrategy = /*@__PURE__*/ (function () {
+ var ChangeDetectionStrategy = {};
+ ChangeDetectionStrategy[ChangeDetectionStrategy["OnPush"] = 0] = "OnPush";
+ ChangeDetectionStrategy[ChangeDetectionStrategy["Default"] = 1] = "Default";
+ return ChangeDetectionStrategy;
+ })();
+ `;
+
+ expect(wrapEnumsRegexes.some((regex) => regex.test(input))).toBeTruthy();
+ expect(oneLine`${transform(input)}`).toEqual(oneLine`${output}`);
+ });
+});
From 90383e86116b4c0399bc05bb607bbbd942a5aa8c Mon Sep 17 00:00:00 2001
From: Filipe Silva
Date: Wed, 16 Aug 2017 09:58:35 +0100
Subject: [PATCH 0018/1016] refactor(@angular-devkit/build-optimizer): add
individual transformer test
---
.../src/build-optimizer/build-optimizer.ts | 16 ++++++++--------
.../src/transforms/import-tslib.ts | 9 ++++++---
.../src/transforms/import-tslib_spec.ts | 10 +++++-----
.../src/transforms/prefix-classes.ts | 14 +++++++++-----
.../src/transforms/prefix-classes_spec.ts | 6 +++---
.../src/transforms/prefix-functions.ts | 5 -----
.../build_optimizer/src/transforms/scrub-file.ts | 14 +++++++++-----
.../src/transforms/scrub-file_spec.ts | 8 ++++----
.../build_optimizer/src/transforms/wrap-enums.ts | 14 +++++++++-----
.../src/transforms/wrap-enums_spec.ts | 6 +++---
10 files changed, 56 insertions(+), 46 deletions(-)
diff --git a/packages/angular_devkit/build_optimizer/src/build-optimizer/build-optimizer.ts b/packages/angular_devkit/build_optimizer/src/build-optimizer/build-optimizer.ts
index 8ec6f161b1..27d61f5896 100644
--- a/packages/angular_devkit/build_optimizer/src/build-optimizer/build-optimizer.ts
+++ b/packages/angular_devkit/build_optimizer/src/build-optimizer/build-optimizer.ts
@@ -8,11 +8,11 @@
import { readFileSync } from 'fs';
import { TransformJavascriptOutput, transformJavascript } from '../helpers/transform-javascript';
import { getFoldFileTransformer } from '../transforms/class-fold';
-import { getImportTslibTransformer, importTslibRegexes } from '../transforms/import-tslib';
-import { getPrefixClassesTransformer, prefixClassRegexes } from '../transforms/prefix-classes';
+import { getImportTslibTransformer, testImportTslib } from '../transforms/import-tslib';
+import { getPrefixClassesTransformer, testPrefixClasses } from '../transforms/prefix-classes';
import { getPrefixFunctionsTransformer } from '../transforms/prefix-functions';
-import { getScrubFileTransformer, scrubFileRegexes } from '../transforms/scrub-file';
-import { getWrapEnumsTransformer, wrapEnumsRegexes } from '../transforms/wrap-enums';
+import { getScrubFileTransformer, testScrubFile } from '../transforms/scrub-file';
+import { getWrapEnumsTransformer, testWrapEnums } from '../transforms/wrap-enums';
const isAngularModuleFile = /\.es5\.js$/;
@@ -57,15 +57,15 @@ export function buildOptimizer(options: BuildOptimizerOptions): TransformJavascr
// Determine which transforms to apply.
const getTransforms = [];
- if (wrapEnumsRegexes.some((regex) => regex.test(content as string))) {
+ if (testWrapEnums(content)) {
getTransforms.push(getWrapEnumsTransformer);
}
- if (importTslibRegexes.some((regex) => regex.test(content as string))) {
+ if (testImportTslib(content)) {
getTransforms.push(getImportTslibTransformer);
}
- if (prefixClassRegexes.some((regex) => regex.test(content as string))) {
+ if (testPrefixClasses(content)) {
getTransforms.push(getPrefixClassesTransformer);
}
@@ -82,7 +82,7 @@ export function buildOptimizer(options: BuildOptimizerOptions): TransformJavascr
getScrubFileTransformer,
getFoldFileTransformer,
);
- } else if (scrubFileRegexes.some((regex) => regex.test(content as string))) {
+ } else if (testScrubFile(content)) {
getTransforms.push(
getScrubFileTransformer,
getFoldFileTransformer,
diff --git a/packages/angular_devkit/build_optimizer/src/transforms/import-tslib.ts b/packages/angular_devkit/build_optimizer/src/transforms/import-tslib.ts
index 60518c39b2..f3455d6d95 100644
--- a/packages/angular_devkit/build_optimizer/src/transforms/import-tslib.ts
+++ b/packages/angular_devkit/build_optimizer/src/transforms/import-tslib.ts
@@ -7,9 +7,12 @@
*/
import * as ts from 'typescript';
-export const importTslibRegexes = [
- /var (__extends|__decorate|__metadata|__param) = \(.*\r?\n( .*\r?\n)*\};/,
-];
+
+export function testImportTslib(content: string) {
+ const regex = /var (__extends|__decorate|__metadata|__param) = \(.*\r?\n( .*\r?\n)*\};/;
+
+ return regex.test(content);
+}
export function getImportTslibTransformer(): ts.TransformerFactory {
return (context: ts.TransformationContext): ts.Transformer => {
diff --git a/packages/angular_devkit/build_optimizer/src/transforms/import-tslib_spec.ts b/packages/angular_devkit/build_optimizer/src/transforms/import-tslib_spec.ts
index 0d991a70e4..05ec7499f4 100644
--- a/packages/angular_devkit/build_optimizer/src/transforms/import-tslib_spec.ts
+++ b/packages/angular_devkit/build_optimizer/src/transforms/import-tslib_spec.ts
@@ -7,7 +7,7 @@
*/
import { oneLine, stripIndent } from 'common-tags';
import { transformJavascript } from '../helpers/transform-javascript';
-import { getImportTslibTransformer, importTslibRegexes } from './import-tslib';
+import { getImportTslibTransformer, testImportTslib } from './import-tslib';
const transform = (content: string) => transformJavascript(
@@ -26,7 +26,7 @@ describe('import-tslib', () => {
import { __extends } from "tslib";
`;
- expect(importTslibRegexes.some((regex) => regex.test(input))).toBeTruthy();
+ expect(testImportTslib(input)).toBeTruthy();
expect(oneLine`${transform(input)}`).toEqual(oneLine`${output}`);
});
@@ -45,7 +45,7 @@ describe('import-tslib', () => {
import { __decorate } from "tslib";
`;
- expect(importTslibRegexes.some((regex) => regex.test(input))).toBeTruthy();
+ expect(testImportTslib(input)).toBeTruthy();
expect(oneLine`${transform(input)}`).toEqual(oneLine`${output}`);
});
@@ -61,7 +61,7 @@ describe('import-tslib', () => {
import { __metadata } from "tslib";
`;
- expect(importTslibRegexes.some((regex) => regex.test(input))).toBeTruthy();
+ expect(testImportTslib(input)).toBeTruthy();
expect(oneLine`${transform(input)}`).toEqual(oneLine`${output}`);
});
@@ -76,7 +76,7 @@ describe('import-tslib', () => {
import { __param } from "tslib";
`;
- expect(importTslibRegexes.some((regex) => regex.test(input))).toBeTruthy();
+ expect(testImportTslib(input)).toBeTruthy();
expect(oneLine`${transform(input)}`).toEqual(oneLine`${output}`);
});
diff --git a/packages/angular_devkit/build_optimizer/src/transforms/prefix-classes.ts b/packages/angular_devkit/build_optimizer/src/transforms/prefix-classes.ts
index f376923f5d..0fe83552d7 100644
--- a/packages/angular_devkit/build_optimizer/src/transforms/prefix-classes.ts
+++ b/packages/angular_devkit/build_optimizer/src/transforms/prefix-classes.ts
@@ -8,11 +8,15 @@
import * as ts from 'typescript';
-export const prefixClassRegexes = [
- // tslint:disable-next-line:max-line-length
- /^(var (\S+) = )(\(function \(\) \{\r?\n(?: (?:\/\*\*| \*|\*\/|\/\/)[^\r?\n]*\r?\n)* function \2\([^\)]*\) \{\r?\n)/,
- /^(var (\S+) = )(\(function \(_super\) \{\r?\n \w*__extends\(\w+, _super\);\r?\n)/,
-];
+export function testPrefixClasses(content: string) {
+ const regexes = [
+ // tslint:disable-next-line:max-line-length
+ /^(var (\S+) = )(\(function \(\) \{\r?\n(?: (?:\/\*\*| \*|\*\/|\/\/)[^\r?\n]*\r?\n)* function \2\([^\)]*\) \{\r?\n)/,
+ /^(var (\S+) = )(\(function \(_super\) \{\r?\n \w*__extends\(\w+, _super\);\r?\n)/,
+ ];
+
+ return regexes.some((regex) => regex.test(content));
+}
export function getPrefixClassesTransformer(): ts.TransformerFactory {
return (context: ts.TransformationContext): ts.Transformer => {
diff --git a/packages/angular_devkit/build_optimizer/src/transforms/prefix-classes_spec.ts b/packages/angular_devkit/build_optimizer/src/transforms/prefix-classes_spec.ts
index f734dbe67a..e214697868 100644
--- a/packages/angular_devkit/build_optimizer/src/transforms/prefix-classes_spec.ts
+++ b/packages/angular_devkit/build_optimizer/src/transforms/prefix-classes_spec.ts
@@ -7,7 +7,7 @@
*/
import { oneLine, stripIndent } from 'common-tags';
import { transformJavascript } from '../helpers/transform-javascript';
-import { getPrefixClassesTransformer, prefixClassRegexes } from './prefix-classes';
+import { getPrefixClassesTransformer, testPrefixClasses } from './prefix-classes';
const transform = (content: string) => transformJavascript(
@@ -34,7 +34,7 @@ describe('prefix-classes', () => {
}());
`;
- expect(prefixClassRegexes.some((regex) => regex.test(input))).toBeTruthy();
+ expect(testPrefixClasses(input)).toBeTruthy();
expect(oneLine`${transform(input)}`).toEqual(oneLine`${output}`);
});
@@ -75,7 +75,7 @@ describe('prefix-classes', () => {
}(OuterSubscriber_1.OuterSubscriber));
`;
- expect(prefixClassRegexes.some((regex) => regex.test(input))).toBeTruthy();
+ expect(testPrefixClasses(input)).toBeTruthy();
expect(oneLine`${transform(input)}`).toEqual(oneLine`${output}`);
});
// tslint:enable:max-line-length
diff --git a/packages/angular_devkit/build_optimizer/src/transforms/prefix-functions.ts b/packages/angular_devkit/build_optimizer/src/transforms/prefix-functions.ts
index 43093116d9..1e94b34eae 100644
--- a/packages/angular_devkit/build_optimizer/src/transforms/prefix-functions.ts
+++ b/packages/angular_devkit/build_optimizer/src/transforms/prefix-functions.ts
@@ -7,11 +7,6 @@
*/
import * as ts from 'typescript';
-export const prefixClassRegexes = [
- // tslint:disable-next-line:max-line-length
- /^(var (\S+) = )(\(function \(\) \{\r?\n(?: (?:\/\*\*| \*|\*\/|\/\/)[^\r?\n]*\r?\n)* function \2\([^\)]*\) \{\r?\n)/mg,
- /^(var (\S+) = )(\(function \(_super\) \{\r?\n \w*__extends\(\w+, _super\);\r?\n)/mg,
-];
const pureFunctionComment = '@__PURE__';
diff --git a/packages/angular_devkit/build_optimizer/src/transforms/scrub-file.ts b/packages/angular_devkit/build_optimizer/src/transforms/scrub-file.ts
index c49a160921..3c19594331 100644
--- a/packages/angular_devkit/build_optimizer/src/transforms/scrub-file.ts
+++ b/packages/angular_devkit/build_optimizer/src/transforms/scrub-file.ts
@@ -9,11 +9,15 @@ import * as ts from 'typescript';
import { collectDeepNodes } from '../helpers/ast-utils';
-export const scrubFileRegexes = [
- /decorators/,
- /propDecorators/,
- /ctorParameters/,
-];
+export function testScrubFile(content: string) {
+ const markers = [
+ 'decorators',
+ 'propDecorators',
+ 'ctorParameters',
+ ];
+
+ return markers.some((marker) => content.indexOf(marker) !== -1);
+}
// Don't remove `ctorParameters` from these.
const platformWhitelist = [
diff --git a/packages/angular_devkit/build_optimizer/src/transforms/scrub-file_spec.ts b/packages/angular_devkit/build_optimizer/src/transforms/scrub-file_spec.ts
index 97334591db..5844f103d2 100644
--- a/packages/angular_devkit/build_optimizer/src/transforms/scrub-file_spec.ts
+++ b/packages/angular_devkit/build_optimizer/src/transforms/scrub-file_spec.ts
@@ -7,7 +7,7 @@
*/
import { oneLine, stripIndent } from 'common-tags';
import { transformJavascript } from '../helpers/transform-javascript';
-import { getScrubFileTransformer, scrubFileRegexes } from './scrub-file';
+import { getScrubFileTransformer, testScrubFile } from './scrub-file';
const transform = (content: string) => transformJavascript(
@@ -27,7 +27,7 @@ describe('scrub-file', () => {
Clazz.decorators = [ { type: Injectable } ];
`;
- expect(scrubFileRegexes.some((regex) => regex.test(input))).toBeTruthy();
+ expect(testScrubFile(input)).toBeTruthy();
expect(oneLine`${transform(input)}`).toEqual(oneLine`${output}`);
});
@@ -70,7 +70,7 @@ describe('scrub-file', () => {
Clazz.propDecorators = { 'ngIf': [{ type: Input }] }
`;
- expect(scrubFileRegexes.some((regex) => regex.test(input))).toBeTruthy();
+ expect(testScrubFile(input)).toBeTruthy();
expect(oneLine`${transform(input)}`).toEqual(oneLine`${output}`);
});
@@ -117,7 +117,7 @@ describe('scrub-file', () => {
Clazz.ctorParameters = function () { return []; };
`;
- expect(scrubFileRegexes.some((regex) => regex.test(input))).toBeTruthy();
+ expect(testScrubFile(input)).toBeTruthy();
expect(oneLine`${transform(input)}`).toEqual(oneLine`${output}`);
});
diff --git a/packages/angular_devkit/build_optimizer/src/transforms/wrap-enums.ts b/packages/angular_devkit/build_optimizer/src/transforms/wrap-enums.ts
index 3123554cdc..eceb5040cb 100644
--- a/packages/angular_devkit/build_optimizer/src/transforms/wrap-enums.ts
+++ b/packages/angular_devkit/build_optimizer/src/transforms/wrap-enums.ts
@@ -9,12 +9,16 @@ import * as ts from 'typescript';
import { collectDeepNodes, drilldownNodes } from '../helpers/ast-utils';
-export const wrapEnumsRegexes = [
- // tslint:disable:max-line-length
- /var (\S+) = \{\};\r?\n(\1\.(\S+) = \d+;\r?\n)+\1\[\1\.(\S+)\] = "\4";\r?\n(\1\[\1\.(\S+)\] = "\S+";\r?\n*)+/,
- /var (\S+);(\/\*@__PURE__\*\/)*\r?\n\(function \(\1\) \{\s+(\1\[\1\["(\S+)"\] = 0\] = "\4";(\s+\1\[\1\["\S+"\] = \d\] = "\S+";)*\r?\n)\}\)\(\1 \|\| \(\1 = \{\}\)\);/,
+export function testWrapEnums(content: string) {
+ const regexes = [
+ // tslint:disable:max-line-length
+ /var (\S+) = \{\};\r?\n(\1\.(\S+) = \d+;\r?\n)+\1\[\1\.(\S+)\] = "\4";\r?\n(\1\[\1\.(\S+)\] = "\S+";\r?\n*)+/,
+ /var (\S+);(\/\*@__PURE__\*\/)*\r?\n\(function \(\1\) \{\s+(\1\[\1\["(\S+)"\] = 0\] = "\4";(\s+\1\[\1\["\S+"\] = \d\] = "\S+";)*\r?\n)\}\)\(\1 \|\| \(\1 = \{\}\)\);/,
// tslint:enable:max-line-length
-];
+ ];
+
+ return regexes.some((regex) => regex.test(content));
+}
interface EnumData {
name: string;
diff --git a/packages/angular_devkit/build_optimizer/src/transforms/wrap-enums_spec.ts b/packages/angular_devkit/build_optimizer/src/transforms/wrap-enums_spec.ts
index 4e2cf8cfd2..4f81ef13c5 100644
--- a/packages/angular_devkit/build_optimizer/src/transforms/wrap-enums_spec.ts
+++ b/packages/angular_devkit/build_optimizer/src/transforms/wrap-enums_spec.ts
@@ -7,7 +7,7 @@
*/
import { oneLine, stripIndent } from 'common-tags';
import { transformJavascript } from '../helpers/transform-javascript';
-import { getWrapEnumsTransformer, wrapEnumsRegexes } from './wrap-enums';
+import { getWrapEnumsTransformer, testWrapEnums } from './wrap-enums';
const transform = (content: string) => transformJavascript(
@@ -33,7 +33,7 @@ describe('prefix-classes', () => {
})();
`;
- expect(wrapEnumsRegexes.some((regex) => regex.test(input))).toBeTruthy();
+ expect(testWrapEnums(input)).toBeTruthy();
expect(oneLine`${transform(input)}`).toEqual(oneLine`${output}`);
});
@@ -54,7 +54,7 @@ describe('prefix-classes', () => {
})();
`;
- expect(wrapEnumsRegexes.some((regex) => regex.test(input))).toBeTruthy();
+ expect(testWrapEnums(input)).toBeTruthy();
expect(oneLine`${transform(input)}`).toEqual(oneLine`${output}`);
});
});
From e7e96788a7c0ddfa7b053c1ecfa3f1301ae4e45c Mon Sep 17 00:00:00 2001
From: Mike Brocchi
Date: Tue, 22 Aug 2017 14:05:03 -0400
Subject: [PATCH 0019/1016] fix(@schematics/angular): Use prefix for root
component
fixes angular/angular-cli#7440
---
packages/schematics/angular/application/index.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/schematics/angular/application/index.ts b/packages/schematics/angular/application/index.ts
index 1145adfaff..594d3a5741 100644
--- a/packages/schematics/angular/application/index.ts
+++ b/packages/schematics/angular/application/index.ts
@@ -74,7 +74,7 @@ function minimalPathFilter(path: string): boolean {
}
export default function (options: ApplicationOptions): Rule {
return (host: Tree, context: SchematicContext) => {
- const appRootSelector = 'app-root';
+ const appRootSelector = `${options.prefix}-root`;
const componentOptions = !options.minimal ?
{
inlineStyle: options.inlineStyle,
From 71db0def3885a0a7a10936e5acb9ffca3c9f6718 Mon Sep 17 00:00:00 2001
From: Hans Larsen
Date: Tue, 22 Aug 2017 15:50:22 -0700
Subject: [PATCH 0020/1016] test: add a test for update buffer
---
.../schematics/src/utility/update-buffer_spec.ts | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/packages/angular_devkit/schematics/src/utility/update-buffer_spec.ts b/packages/angular_devkit/schematics/src/utility/update-buffer_spec.ts
index 8103052b37..b8e8b9aa98 100644
--- a/packages/angular_devkit/schematics/src/utility/update-buffer_spec.ts
+++ b/packages/angular_devkit/schematics/src/utility/update-buffer_spec.ts
@@ -159,6 +159,14 @@ describe('UpdateBuffer', () => {
expect(() => mb.insertRight(6, new Buffer('B'), true)).toThrow();
expect(mb.toString()).toBe('01234A');
});
+
+ it('works for content at start/end of buffer', () => {
+ const buffer = new UpdateBuffer(new Buffer('012345'));
+ buffer.insertLeft(0, new Buffer('ABC'));
+ buffer.insertRight(6, new Buffer('DEF'));
+ buffer.remove(0, 6);
+ expect(buffer.toString()).toBe('ABC');
+ });
});
describe('generate', () => {
From 046cb5b7034696e39336dc9c4e931acdf9e925c4 Mon Sep 17 00:00:00 2001
From: Mike Brocchi
Date: Wed, 23 Aug 2017 12:02:25 -0400
Subject: [PATCH 0021/1016] fix(@schematics/angular): Prevent finding modules
in other sub-directories
fixes angular/angular-cli#7439
---
packages/schematics/angular/utility/find-module.ts | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/packages/schematics/angular/utility/find-module.ts b/packages/schematics/angular/utility/find-module.ts
index 7c4f7b9dd1..268b424ff0 100644
--- a/packages/schematics/angular/utility/find-module.ts
+++ b/packages/schematics/angular/utility/find-module.ts
@@ -65,7 +65,10 @@ export function findModule(host: Tree, generateDir: string): SchematicPath {
while (closestModule) {
const normalizedRoot = normalizePath(closestModule);
const matches = allFiles
- .filter(p => moduleRe.test(p) && !routingModuleRe.test(p) && p.startsWith(normalizedRoot));
+ .filter(p => moduleRe.test(p) &&
+ !routingModuleRe.test(p) &&
+ p.startsWith(normalizedRoot) &&
+ p.split('/').length === normalizedRoot.split('/').length);
if (matches.length == 1) {
modulePath = matches[0];
From d22c93ad7a7a6c29ea8dd614c1b6efe5169bb3c5 Mon Sep 17 00:00:00 2001
From: Hans Larsen
Date: Wed, 23 Aug 2017 14:17:53 -0700
Subject: [PATCH 0022/1016] ci: fix circleci typings
---
scripts/release.ts | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/scripts/release.ts b/scripts/release.ts
index eeb4d96531..1671a44e9d 100644
--- a/scripts/release.ts
+++ b/scripts/release.ts
@@ -44,8 +44,8 @@ function _upgrade(release: string, logger: Logger) {
}
const hash = packages[pkg].hash;
- const version = versions[pkg];
- let newVersion: string = version;
+ const version = versions[pkg] || '0.0.0';
+ let newVersion: string | null = version;
if (release == 'minor-beta') {
if (hash !== hashes[pkg]) {
@@ -88,7 +88,7 @@ function _upgrade(release: string, logger: Logger) {
}
let message = '';
- if (version !== newVersion) {
+ if (newVersion && version !== newVersion) {
message = `${pkg} changed... updating v${version} => v${newVersion}`;
versions[pkg] = newVersion;
hashes[pkg] = hash;
From b35021e9ea1f3cfc6a3af68204ed4a96b0d819b7 Mon Sep 17 00:00:00 2001
From: Mike Brocchi
Date: Wed, 23 Aug 2017 10:03:18 -0400
Subject: [PATCH 0023/1016] fix(@angular-devkit/schematics): Add ability to
extend/alias a schematic
---
.../schematics/src/engine/engine.ts | 1 +
.../schematics/tools/description.ts | 1 +
.../tools/file-system-engine-host-base.ts | 38 ++++++++++++++++++-
3 files changed, 38 insertions(+), 2 deletions(-)
diff --git a/packages/angular_devkit/schematics/src/engine/engine.ts b/packages/angular_devkit/schematics/src/engine/engine.ts
index a58130121c..525950561e 100644
--- a/packages/angular_devkit/schematics/src/engine/engine.ts
+++ b/packages/angular_devkit/schematics/src/engine/engine.ts
@@ -85,6 +85,7 @@ export class SchematicEngine(description, factory, collection, this);
diff --git a/packages/angular_devkit/schematics/tools/description.ts b/packages/angular_devkit/schematics/tools/description.ts
index 4797e2c1a7..db3ffa6aea 100644
--- a/packages/angular_devkit/schematics/tools/description.ts
+++ b/packages/angular_devkit/schematics/tools/description.ts
@@ -25,6 +25,7 @@ export interface FileSystemSchematicJsonDescription {
readonly factory: string;
readonly description: string;
readonly schema?: string;
+ readonly extends?: string;
}
export interface FileSystemSchematicDescription extends FileSystemSchematicJsonDescription {
diff --git a/packages/angular_devkit/schematics/tools/file-system-engine-host-base.ts b/packages/angular_devkit/schematics/tools/file-system-engine-host-base.ts
index fb4df647c7..a1b3a751b6 100644
--- a/packages/angular_devkit/schematics/tools/file-system-engine-host-base.ts
+++ b/packages/angular_devkit/schematics/tools/file-system-engine-host-base.ts
@@ -58,7 +58,20 @@ export abstract class FileSystemEngineHostBase implements
private _transforms: OptionTransform
<% if(routing) { %>
+ <% } %>
`,<% } else { %>
templateUrl: './app.component.html',<% } if(inlineStyle) { %>
styles: []<% } else { %>
From d3f4fe888ba706c8c12eba49c91bc03cecf67283 Mon Sep 17 00:00:00 2001
From: Charles Lyding <19598772+clydin@users.noreply.github.com>
Date: Tue, 12 Sep 2017 21:49:56 -0400
Subject: [PATCH 0059/1016] refactor(@angular-devkit/build-optimizer): optimize
javascript transform process
---
.../src/helpers/transform-javascript.ts | 88 ++++++++-----------
1 file changed, 35 insertions(+), 53 deletions(-)
diff --git a/packages/angular_devkit/build_optimizer/src/helpers/transform-javascript.ts b/packages/angular_devkit/build_optimizer/src/helpers/transform-javascript.ts
index b086420802..08e16b8709 100644
--- a/packages/angular_devkit/build_optimizer/src/helpers/transform-javascript.ts
+++ b/packages/angular_devkit/build_optimizer/src/helpers/transform-javascript.ts
@@ -5,8 +5,6 @@
* 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 { readFileSync } from 'fs';
-import { dirname, join } from 'path';
import { RawSourceMap } from 'source-map';
import * as ts from 'typescript';
@@ -62,79 +60,48 @@ export function transformJavascript(
}
};
- // Make a in-memory host and populate it with a single file
- const fileMap = new Map();
- const sourcesMap = new Map();
const outputs = new Map();
-
- // We're not actually writing anything to disk, but still need to define an outDir
- // because otherwise TS will fail to emit JS since it would overwrite the original.
- const tempOutDir = '$$_temp/';
const tempFilename = 'bo-default-file.js';
- fileMap.set(tempFilename, content);
-
- // We need to load the default lib for noEmitOnError to work properly.
- const defaultLibFileName = 'lib.d.ts';
- const defaultLibContent = readFileSync(join(dirname(require.resolve('typescript')),
- defaultLibFileName), 'UTF-8');
- fileMap.set(defaultLibFileName, defaultLibContent);
-
- fileMap.forEach((v, k) => sourcesMap.set(
- k, ts.createSourceFile(k, v, ts.ScriptTarget.ES2015)));
+ const tempSourceFile = ts.createSourceFile(tempFilename, content, ts.ScriptTarget.Latest);
const host: ts.CompilerHost = {
getSourceFile: (fileName) => {
- const sourceFile = sourcesMap.get(fileName);
- if (!sourceFile) {
+ if (fileName !== tempFilename) {
throw new Error(`File ${fileName} does not have a sourceFile.`);
}
- return sourceFile;
+ return tempSourceFile;
},
- getDefaultLibFileName: () => defaultLibFileName,
+ getDefaultLibFileName: () => 'lib.d.ts',
getCurrentDirectory: () => '',
getDirectories: () => [],
getCanonicalFileName: (fileName) => fileName,
useCaseSensitiveFileNames: () => true,
getNewLine: () => '\n',
- fileExists: (fileName) => fileMap.has(fileName),
- readFile: (fileName) => {
- const content = fileMap.get(fileName);
- if (!content) {
- throw new Error(`File ${fileName} does not exist.`);
- }
-
- return content;
- },
+ fileExists: (fileName) => fileName === tempFilename,
+ readFile: (_fileName) => '',
writeFile: (fileName, text) => outputs.set(fileName, text),
};
const tsOptions: ts.CompilerOptions = {
- noEmitOnError: true,
- allowJs: true,
- // Using just line feed makes test comparisons easier, and doesn't matter for generated files.
- newLine: ts.NewLineKind.LineFeed,
- // We target next so that there is no downleveling.
- target: ts.ScriptTarget.ESNext,
- skipLibCheck: true,
- outDir: '$$_temp/',
+ // We target latest so that there is no downleveling.
+ target: ts.ScriptTarget.Latest,
+ isolatedModules: true,
+ suppressOutputPathCheck: true,
+ allowNonTsExtensions: true,
+ noLib: true,
+ noResolve: true,
sourceMap: emitSourceMap,
inlineSources: emitSourceMap,
inlineSourceMap: false,
};
- const program = ts.createProgram(Array.from(fileMap.keys()), tsOptions, host);
+ const program = ts.createProgram([tempFilename], tsOptions, host);
- // We need the checker inside transforms.
- const transforms = getTransforms.map((getTf) => getTf(program));
-
- const { emitSkipped, diagnostics } = program.emit(
- undefined, host.writeFile, undefined, undefined,
- { before: transforms, after: [] });
+ const diagnostics = program.getSyntacticDiagnostics(tempSourceFile);
+ const hasError = diagnostics.some(diag => diag.category === ts.DiagnosticCategory.Error);
- let transformedContent = outputs.get(`${tempOutDir}${tempFilename}`);
-
- if (emitSkipped || !transformedContent) {
+ if (hasError) {
// Throw only if we're in strict mode, otherwise return original content.
if (strict) {
throw new Error(`
@@ -151,12 +118,27 @@ export function transformJavascript(
}
}
+ // We need the checker inside transforms.
+ const transforms = getTransforms.map((getTf) => getTf(program));
+
+ program.emit(undefined, undefined, undefined, undefined, { before: transforms, after: [] });
+
+ let transformedContent = outputs.get(tempFilename);
+
+ if (!transformedContent) {
+ return {
+ content: null,
+ sourceMap: null,
+ emitSkipped: true,
+ };
+ }
+
let sourceMap: RawSourceMap | null = null;
+ const tsSourceMap = outputs.get(`${tempFilename}.map`);
- if (emitSourceMap) {
- const tsSourceMap = outputs.get(`${tempOutDir}${tempFilename}.map`);
+ if (emitSourceMap && tsSourceMap) {
const urlRegExp = /^\/\/# sourceMappingURL=[^\r\n]*/gm;
- sourceMap = JSON.parse(tsSourceMap as string) as RawSourceMap;
+ sourceMap = JSON.parse(tsSourceMap) as RawSourceMap;
// Fix sourcemaps file references.
if (outputFilePath) {
sourceMap.file = outputFilePath;
From 66eda91df56a78cd4715889b8c33cec7598ad088 Mon Sep 17 00:00:00 2001
From: laco0416
Date: Fri, 15 Sep 2017 14:13:42 +0900
Subject: [PATCH 0060/1016] docs(@schematics/angular): fix wrong package name
for animations
---
.../schematics/angular/application/files/__path__/polyfills.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/schematics/angular/application/files/__path__/polyfills.ts b/packages/schematics/angular/application/files/__path__/polyfills.ts
index 7831e97b79..581aadfead 100644
--- a/packages/schematics/angular/application/files/__path__/polyfills.ts
+++ b/packages/schematics/angular/application/files/__path__/polyfills.ts
@@ -43,7 +43,7 @@ import 'core-js/es7/reflect';
/**
- * Required to support Web Animations `@angular/animation`.
+ * Required to support Web Animations `@angular/platform-browser/animations`.
* Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation
**/
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
From a002586c36384da3c5969ef75723547bc7523c73 Mon Sep 17 00:00:00 2001
From: Ricardo Varanda
Date: Mon, 18 Sep 2017 15:29:13 +0100
Subject: [PATCH 0061/1016] fix(@schematics/angular): fix inconsistency in
service schema
---
packages/schematics/angular/service/schema.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/schematics/angular/service/schema.json b/packages/schematics/angular/service/schema.json
index 8e775df491..6585669a18 100644
--- a/packages/schematics/angular/service/schema.json
+++ b/packages/schematics/angular/service/schema.json
@@ -20,7 +20,7 @@
},
"flat": {
"type": "boolean",
- "default": false,
+ "default": true,
"description": "Flag to indicate if a dir is created."
},
"spec": {
From b1fccf65e9bf0e6c1cb4ddfe27ba6a3c45c895b1 Mon Sep 17 00:00:00 2001
From: Hans Larsen
Date: Thu, 14 Sep 2017 19:51:02 -0700
Subject: [PATCH 0062/1016] refactor: remove duplicated types
---
.../tools/file-system-engine-host-base.ts | 25 +++++---------
.../tools/file-system-engine-host.ts | 31 ++++++-----------
.../angular_devkit/schematics/tools/index.ts | 1 +
.../tools/node-module-engine-host.ts | 34 ++++++++-----------
4 files changed, 34 insertions(+), 57 deletions(-)
diff --git a/packages/angular_devkit/schematics/tools/file-system-engine-host-base.ts b/packages/angular_devkit/schematics/tools/file-system-engine-host-base.ts
index f15c7f60dd..dd3eab2ceb 100644
--- a/packages/angular_devkit/schematics/tools/file-system-engine-host-base.ts
+++ b/packages/angular_devkit/schematics/tools/file-system-engine-host-base.ts
@@ -7,34 +7,25 @@
*/
import { JsonObject } from '@angular-devkit/core';
import {
- Collection,
- CollectionDescription,
EngineHost,
FileSystemTree,
RuleFactory,
- SchematicDescription,
Source,
- TypedSchematicContext,
} from '@angular-devkit/schematics';
import { dirname, isAbsolute, join, resolve } from 'path';
import { Url } from 'url';
-import { FileSystemCollectionDescription, FileSystemSchematicDescription } from './description';
+import {
+ FileSystemCollection,
+ FileSystemCollectionDesc,
+ FileSystemCollectionDescription,
+ FileSystemSchematicContext,
+ FileSystemSchematicDesc,
+ FileSystemSchematicDescription,
+} from './description';
import { FileSystemHost } from './file-system-host';
import { readJsonFile } from './file-system-utility';
-/**
- * Used to simplify typings.
- */
-export declare type FileSystemCollection
- = Collection;
-export declare type FileSystemCollectionDesc
- = CollectionDescription;
-export declare type FileSystemSchematicDesc
- = SchematicDescription;
-export declare type FileSystemSchematicContext
- = TypedSchematicContext;
-
export declare type OptionTransform
= (schematic: FileSystemSchematicDescription, options: T) => R;
diff --git a/packages/angular_devkit/schematics/tools/file-system-engine-host.ts b/packages/angular_devkit/schematics/tools/file-system-engine-host.ts
index 2553c7a1af..9acae0ee43 100644
--- a/packages/angular_devkit/schematics/tools/file-system-engine-host.ts
+++ b/packages/angular_devkit/schematics/tools/file-system-engine-host.ts
@@ -5,27 +5,14 @@
* 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 {
- CollectionDescription,
- RuleFactory,
- SchematicDescription,
-} from '@angular-devkit/schematics';
+import { RuleFactory } from '@angular-devkit/schematics';
import { existsSync } from 'fs';
import { join } from 'path';
-import { FileSystemCollectionDescription, FileSystemSchematicDescription } from './description';
+import { FileSystemCollectionDesc, FileSystemSchematicDesc } from './description';
import { ExportStringRef } from './export-ref';
import { FileSystemEngineHostBase } from './file-system-engine-host-base';
-/**
- * Used to simplify typings.
- */
-export declare type FileSystemCollectionDesc
- = CollectionDescription;
-export declare type FileSystemSchematicDesc
- = SchematicDescription;
-
-
/**
* A simple EngineHost that uses a root with one directory per collection inside of it. The
* collection declaration follows the same rules as the regular FileSystemEngineHostBase.
@@ -57,8 +44,10 @@ export class FileSystemEngineHost extends FileSystemEngineHostBase {
return { ref: ref.ref, path: ref.module };
}
- protected _transformCollectionDescription(_name: string,
- desc: Partial) {
+ protected _transformCollectionDescription(
+ _name: string,
+ desc: Partial,
+ ): FileSystemCollectionDesc | null {
if (!desc.name || !desc.path || !desc.schematics || !desc.version) {
return null;
}
@@ -69,9 +58,11 @@ export class FileSystemEngineHost extends FileSystemEngineHostBase {
return desc as FileSystemCollectionDesc;
}
- protected _transformSchematicDescription(_name: string,
- _collection: FileSystemCollectionDesc,
- desc: Partial) {
+ protected _transformSchematicDescription(
+ _name: string,
+ _collection: FileSystemCollectionDesc,
+ desc: Partial,
+ ): FileSystemSchematicDesc | null {
if (!desc.factoryFn || !desc.path || !desc.description) {
return null;
}
diff --git a/packages/angular_devkit/schematics/tools/index.ts b/packages/angular_devkit/schematics/tools/index.ts
index c0b58e5d1f..1b5ec377d8 100644
--- a/packages/angular_devkit/schematics/tools/index.ts
+++ b/packages/angular_devkit/schematics/tools/index.ts
@@ -5,6 +5,7 @@
* 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 * from './description';
export * from './file-system-host';
export * from './file-system-engine-host-base';
diff --git a/packages/angular_devkit/schematics/tools/node-module-engine-host.ts b/packages/angular_devkit/schematics/tools/node-module-engine-host.ts
index 46a77139c5..27560af227 100644
--- a/packages/angular_devkit/schematics/tools/node-module-engine-host.ts
+++ b/packages/angular_devkit/schematics/tools/node-module-engine-host.ts
@@ -5,26 +5,16 @@
* 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 {
- CollectionDescription,
- RuleFactory,
- SchematicDescription,
-} from '@angular-devkit/schematics';
+import { RuleFactory } from '@angular-devkit/schematics';
import { join } from 'path';
-import { FileSystemCollectionDescription, FileSystemSchematicDescription } from './description';
+import {
+ FileSystemCollectionDesc,
+ FileSystemSchematicDesc,
+} from './description';
import { ExportStringRef } from './export-ref';
import { FileSystemEngineHostBase } from './file-system-engine-host-base';
-/**
- * Used to simplify typings.
- */
-export declare type FileSystemCollectionDesc
- = CollectionDescription;
-export declare type FileSystemSchematicDesc
- = SchematicDescription;
-
-
/**
* A simple EngineHost that uses NodeModules to resolve collections.
*/
@@ -47,8 +37,10 @@ export class NodeModulesEngineHost extends FileSystemEngineHostBase {
return { ref: ref.ref, path: ref.module };
}
- protected _transformCollectionDescription(name: string,
- desc: Partial) {
+ protected _transformCollectionDescription(
+ name: string,
+ desc: Partial,
+ ): FileSystemCollectionDesc | null {
if (!desc.path || !desc.schematics) {
return null;
}
@@ -64,9 +56,11 @@ export class NodeModulesEngineHost extends FileSystemEngineHostBase {
} as FileSystemCollectionDesc;
}
- protected _transformSchematicDescription(_name: string,
- _collection: FileSystemCollectionDesc,
- desc: Partial) {
+ protected _transformSchematicDescription(
+ _name: string,
+ _collection: FileSystemCollectionDesc,
+ desc: Partial,
+ ): FileSystemSchematicDesc | null {
if (!desc.factoryFn || !desc.path || !desc.description) {
return null;
}
From 59385ba9c575e3f5c9bfaec1dbab8fd88fd1848b Mon Sep 17 00:00:00 2001
From: Hans Larsen
Date: Thu, 14 Sep 2017 20:07:33 -0700
Subject: [PATCH 0063/1016] refactor: use the core BaseException
And rename SchematicsError to SchematicsException.
---
.../schematics/src/engine/engine.ts | 2 +-
.../schematics/src/engine/schematic.ts | 2 +-
.../schematics/src/exception/exception.ts | 9 ++-------
packages/angular_devkit/schematics/src/index.ts | 3 ++-
.../angular_devkit/schematics/src/rules/call.ts | 2 +-
.../schematics/src/rules/template.ts | 2 +-
.../angular_devkit/schematics/src/tree/action.ts | 2 +-
.../angular_devkit/schematics/src/tree/null.ts | 3 ++-
.../schematics/src/utility/path.ts | 2 +-
.../schematics/src/utility/update-buffer.ts | 2 +-
.../schematics/tools/node-module-engine-host.ts | 16 +++++++++++-----
packages/schematics/angular/application/index.ts | 4 ++--
packages/schematics/angular/class/index.ts | 4 ++--
packages/schematics/angular/component/index.ts | 8 ++++----
packages/schematics/angular/directive/index.ts | 8 ++++----
packages/schematics/angular/enum/index.ts | 4 ++--
packages/schematics/angular/guard/index.ts | 6 +++---
packages/schematics/angular/interface/index.ts | 4 ++--
packages/schematics/angular/module/index.ts | 6 +++---
packages/schematics/angular/pipe/index.ts | 8 ++++----
packages/schematics/angular/service/index.ts | 6 +++---
21 files changed, 53 insertions(+), 50 deletions(-)
diff --git a/packages/angular_devkit/schematics/src/engine/engine.ts b/packages/angular_devkit/schematics/src/engine/engine.ts
index 525950561e..d04100d057 100644
--- a/packages/angular_devkit/schematics/src/engine/engine.ts
+++ b/packages/angular_devkit/schematics/src/engine/engine.ts
@@ -5,9 +5,9 @@
* 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 { BaseException } from '@angular-devkit/core';
import 'rxjs/add/operator/map';
import { Url } from 'url';
-import { BaseException } from '../exception/exception';
import { MergeStrategy } from '../tree/interface';
import { NullTree } from '../tree/null';
import { empty } from '../tree/static';
diff --git a/packages/angular_devkit/schematics/src/engine/schematic.ts b/packages/angular_devkit/schematics/src/engine/schematic.ts
index 44e0e4be26..e088eb1fe7 100644
--- a/packages/angular_devkit/schematics/src/engine/schematic.ts
+++ b/packages/angular_devkit/schematics/src/engine/schematic.ts
@@ -5,10 +5,10 @@
* 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 { BaseException } from '@angular-devkit/core';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/of';
import 'rxjs/add/operator/concatMap';
-import { BaseException } from '../exception/exception';
import { Tree } from '../tree/interface';
import {
Collection,
diff --git a/packages/angular_devkit/schematics/src/exception/exception.ts b/packages/angular_devkit/schematics/src/exception/exception.ts
index bc52c14b5a..6be8c85d7b 100644
--- a/packages/angular_devkit/schematics/src/exception/exception.ts
+++ b/packages/angular_devkit/schematics/src/exception/exception.ts
@@ -5,16 +5,11 @@
* 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 class BaseException extends Error {
- constructor(message = '') {
- super(message);
- }
-}
+import { BaseException } from '@angular-devkit/core';
// Used by schematics to throw exceptions.
-export class SchematicsError extends BaseException {}
+export class SchematicsException extends BaseException {}
// Exceptions
diff --git a/packages/angular_devkit/schematics/src/index.ts b/packages/angular_devkit/schematics/src/index.ts
index 3cd7aa6c4f..fa254dea9c 100644
--- a/packages/angular_devkit/schematics/src/index.ts
+++ b/packages/angular_devkit/schematics/src/index.ts
@@ -10,12 +10,13 @@ import {Tree as TreeInterface } from './tree/interface';
import { branch, empty, merge, optimize, partition } from './tree/static';
-export { SchematicsError } from './exception/exception';
+export { SchematicsException } from './exception/exception';
export * from './tree/action';
export * from './engine/collection';
export * from './engine/engine';
export * from './engine/interface';
+export * from './exception/exception';
export * from './tree/interface';
export * from './rules/base';
export * from './rules/move';
diff --git a/packages/angular_devkit/schematics/src/rules/call.ts b/packages/angular_devkit/schematics/src/rules/call.ts
index 61f1098f37..82c288ea23 100644
--- a/packages/angular_devkit/schematics/src/rules/call.ts
+++ b/packages/angular_devkit/schematics/src/rules/call.ts
@@ -5,9 +5,9 @@
* 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 { BaseException } from '@angular-devkit/core';
import { Observable } from 'rxjs/Observable';
import { Rule, SchematicContext, Source } from '../engine/interface';
-import { BaseException } from '../exception/exception';
import { Tree } from '../tree/interface';
import { VirtualTree } from '../tree/virtual';
diff --git a/packages/angular_devkit/schematics/src/rules/template.ts b/packages/angular_devkit/schematics/src/rules/template.ts
index 82c789ca14..606885b44d 100644
--- a/packages/angular_devkit/schematics/src/rules/template.ts
+++ b/packages/angular_devkit/schematics/src/rules/template.ts
@@ -5,8 +5,8 @@
* 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 { BaseException } from '@angular-devkit/core';
import { FileOperator, Rule } from '../engine/interface';
-import { BaseException } from '../exception/exception';
import { FileEntry } from '../tree/interface';
import { normalizePath } from '../utility/path';
import { chain, forEach } from './base';
diff --git a/packages/angular_devkit/schematics/src/tree/action.ts b/packages/angular_devkit/schematics/src/tree/action.ts
index b3360483e7..177f6df2ca 100644
--- a/packages/angular_devkit/schematics/src/tree/action.ts
+++ b/packages/angular_devkit/schematics/src/tree/action.ts
@@ -5,7 +5,7 @@
* 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 { BaseException } from '../exception/exception';
+import { BaseException } from '@angular-devkit/core';
import { SchematicPath } from '../utility/path';
diff --git a/packages/angular_devkit/schematics/src/tree/null.ts b/packages/angular_devkit/schematics/src/tree/null.ts
index c8d82cc751..4525644726 100644
--- a/packages/angular_devkit/schematics/src/tree/null.ts
+++ b/packages/angular_devkit/schematics/src/tree/null.ts
@@ -5,7 +5,8 @@
* 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 { BaseException, FileDoesNotExistException } from '../exception/exception';
+import { BaseException } from '@angular-devkit/core';
+import { FileDoesNotExistException } from '../exception/exception';
import { Action } from './action';
import { MergeStrategy, Tree, UpdateRecorder } from './interface';
import { UpdateRecorderBase } from './recorder';
diff --git a/packages/angular_devkit/schematics/src/utility/path.ts b/packages/angular_devkit/schematics/src/utility/path.ts
index 4a148a8e77..11d78a1d96 100644
--- a/packages/angular_devkit/schematics/src/utility/path.ts
+++ b/packages/angular_devkit/schematics/src/utility/path.ts
@@ -5,7 +5,7 @@
* 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 { BaseException } from '../exception/exception';
+import { BaseException } from '@angular-devkit/core';
export type SchematicPath = string & {
diff --git a/packages/angular_devkit/schematics/src/utility/update-buffer.ts b/packages/angular_devkit/schematics/src/utility/update-buffer.ts
index 7b548b7ffc..44a9f620a9 100644
--- a/packages/angular_devkit/schematics/src/utility/update-buffer.ts
+++ b/packages/angular_devkit/schematics/src/utility/update-buffer.ts
@@ -5,7 +5,7 @@
* 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 { BaseException } from '../exception/exception';
+import { BaseException } from '@angular-devkit/core';
import { LinkedList } from './linked-list';
diff --git a/packages/angular_devkit/schematics/tools/node-module-engine-host.ts b/packages/angular_devkit/schematics/tools/node-module-engine-host.ts
index 27560af227..cdb74713c0 100644
--- a/packages/angular_devkit/schematics/tools/node-module-engine-host.ts
+++ b/packages/angular_devkit/schematics/tools/node-module-engine-host.ts
@@ -5,6 +5,7 @@
* 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 { BaseException } from '@angular-devkit/core';
import { RuleFactory } from '@angular-devkit/schematics';
import { join } from 'path';
import {
@@ -15,6 +16,14 @@ import { ExportStringRef } from './export-ref';
import { FileSystemEngineHostBase } from './file-system-engine-host-base';
+export class CollectionMissingSchematicsMapException extends BaseException {
+ constructor(name: string) { super(`Collection "${name}" does not have a schematics map.`); }
+}
+export class SchematicMissingDescriptionException extends BaseException {
+ constructor(name: string) { super(`Schematics "${name}" does not have a description.`); }
+}
+
+
/**
* A simple EngineHost that uses NodeModules to resolve collections.
*/
@@ -41,11 +50,8 @@ export class NodeModulesEngineHost extends FileSystemEngineHostBase {
name: string,
desc: Partial,
): FileSystemCollectionDesc | null {
- if (!desc.path || !desc.schematics) {
- return null;
- }
- if (typeof desc.schematics != 'object') {
- return null;
+ if (!desc.schematics || typeof desc.schematics != 'object') {
+ throw new CollectionMissingSchematicsMapException(name);
}
const version = require(join(name, 'package.json'))['version'];
diff --git a/packages/schematics/angular/application/index.ts b/packages/schematics/angular/application/index.ts
index f07503cce2..9c9f0c7798 100644
--- a/packages/schematics/angular/application/index.ts
+++ b/packages/schematics/angular/application/index.ts
@@ -9,7 +9,7 @@ import {
MergeStrategy,
Rule,
SchematicContext,
- SchematicsError,
+ SchematicsException,
Tree,
apply,
chain,
@@ -33,7 +33,7 @@ function addBootstrapToNgModule(directory: string, sourceDir: string): Rule {
const modulePath = `${directory}/${sourceDir}/app/app.module.ts`;
const content = host.read(modulePath);
if (!content) {
- throw new SchematicsError(`File ${modulePath} does not exist.`);
+ throw new SchematicsException(`File ${modulePath} does not exist.`);
}
const sourceText = content.toString('utf-8');
const source = ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true);
diff --git a/packages/schematics/angular/class/index.ts b/packages/schematics/angular/class/index.ts
index 4499692235..912e5a4025 100644
--- a/packages/schematics/angular/class/index.ts
+++ b/packages/schematics/angular/class/index.ts
@@ -7,7 +7,7 @@
*/
import {
Rule,
- SchematicsError,
+ SchematicsException,
apply,
branchAndMerge,
chain,
@@ -28,7 +28,7 @@ export default function (options: ClassOptions): Rule {
options.path = options.path ? normalizePath(options.path) : options.path;
const sourceDir = options.sourceDir;
if (!sourceDir) {
- throw new SchematicsError(`sourceDir option is required.`);
+ throw new SchematicsException(`sourceDir option is required.`);
}
const templateSource = apply(url('./files'), [
diff --git a/packages/schematics/angular/component/index.ts b/packages/schematics/angular/component/index.ts
index 593b4294c5..86ff126142 100644
--- a/packages/schematics/angular/component/index.ts
+++ b/packages/schematics/angular/component/index.ts
@@ -8,7 +8,7 @@
import {
Rule,
SchematicContext,
- SchematicsError,
+ SchematicsException,
Tree,
apply,
branchAndMerge,
@@ -39,7 +39,7 @@ function addDeclarationToNgModule(options: ComponentOptions): Rule {
const modulePath = options.module;
const text = host.read(modulePath);
if (text === null) {
- throw new SchematicsError(`File ${modulePath} does not exist.`);
+ throw new SchematicsException(`File ${modulePath} does not exist.`);
}
const sourceText = text.toString('utf-8');
const source = ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true);
@@ -67,7 +67,7 @@ function addDeclarationToNgModule(options: ComponentOptions): Rule {
// Need to refresh the AST because we overwrote the file in the host.
const text = host.read(modulePath);
if (text === null) {
- throw new SchematicsError(`File ${modulePath} does not exist.`);
+ throw new SchematicsException(`File ${modulePath} does not exist.`);
}
const sourceText = text.toString('utf-8');
const source = ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true);
@@ -104,7 +104,7 @@ function buildSelector(options: ComponentOptions) {
export default function(options: ComponentOptions): Rule {
const sourceDir = options.sourceDir;
if (!sourceDir) {
- throw new SchematicsError(`sourceDir option is required.`);
+ throw new SchematicsException(`sourceDir option is required.`);
}
return (host: Tree, context: SchematicContext) => {
diff --git a/packages/schematics/angular/directive/index.ts b/packages/schematics/angular/directive/index.ts
index e1c9f80d9f..048c28acb4 100644
--- a/packages/schematics/angular/directive/index.ts
+++ b/packages/schematics/angular/directive/index.ts
@@ -8,7 +8,7 @@
import {
Rule,
SchematicContext,
- SchematicsError,
+ SchematicsException,
Tree,
apply,
branchAndMerge,
@@ -39,7 +39,7 @@ function addDeclarationToNgModule(options: DirectiveOptions): Rule {
const modulePath = options.module;
const text = host.read(modulePath);
if (text === null) {
- throw new SchematicsError(`File ${modulePath} does not exist.`);
+ throw new SchematicsException(`File ${modulePath} does not exist.`);
}
const sourceText = text.toString('utf-8');
const source = ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true);
@@ -66,7 +66,7 @@ function addDeclarationToNgModule(options: DirectiveOptions): Rule {
// Need to refresh the AST because we overwrote the file in the host.
const text = host.read(modulePath);
if (text === null) {
- throw new SchematicsError(`File ${modulePath} does not exist.`);
+ throw new SchematicsException(`File ${modulePath} does not exist.`);
}
const sourceText = text.toString('utf-8');
const source = ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true);
@@ -103,7 +103,7 @@ export default function (options: DirectiveOptions): Rule {
options.path = options.path ? normalizePath(options.path) : options.path;
const sourceDir = options.sourceDir;
if (!sourceDir) {
- throw new SchematicsError(`sourceDir option is required.`);
+ throw new SchematicsException(`sourceDir option is required.`);
}
return (host: Tree, context: SchematicContext) => {
diff --git a/packages/schematics/angular/enum/index.ts b/packages/schematics/angular/enum/index.ts
index e359c0c0b9..39c2c856ed 100644
--- a/packages/schematics/angular/enum/index.ts
+++ b/packages/schematics/angular/enum/index.ts
@@ -7,7 +7,7 @@
*/
import {
Rule,
- SchematicsError,
+ SchematicsException,
apply,
branchAndMerge,
chain,
@@ -25,7 +25,7 @@ export default function (options: EnumOptions): Rule {
options.path = options.path ? normalizePath(options.path) : options.path;
const sourceDir = options.sourceDir;
if (!sourceDir) {
- throw new SchematicsError(`sourceDir option is required.`);
+ throw new SchematicsException(`sourceDir option is required.`);
}
const templateSource = apply(url('./files'), [
diff --git a/packages/schematics/angular/guard/index.ts b/packages/schematics/angular/guard/index.ts
index 84c357bc6c..9f842064d0 100644
--- a/packages/schematics/angular/guard/index.ts
+++ b/packages/schematics/angular/guard/index.ts
@@ -8,7 +8,7 @@
import {
Rule,
SchematicContext,
- SchematicsError,
+ SchematicsException,
Tree,
apply,
branchAndMerge,
@@ -39,7 +39,7 @@ function addDeclarationToNgModule(options: GuardOptions): Rule {
const modulePath = options.module;
const text = host.read(modulePath);
if (text === null) {
- throw new SchematicsError(`File ${modulePath} does not exist.`);
+ throw new SchematicsException(`File ${modulePath} does not exist.`);
}
const sourceText = text.toString('utf-8');
@@ -69,7 +69,7 @@ export default function (options: GuardOptions): Rule {
options.path = options.path ? normalizePath(options.path) : options.path;
const sourceDir = options.sourceDir;
if (!sourceDir) {
- throw new SchematicsError(`sourceDir option is required.`);
+ throw new SchematicsException(`sourceDir option is required.`);
}
return (host: Tree, context: SchematicContext) => {
diff --git a/packages/schematics/angular/interface/index.ts b/packages/schematics/angular/interface/index.ts
index 57b11b1512..d5d59aa0c6 100644
--- a/packages/schematics/angular/interface/index.ts
+++ b/packages/schematics/angular/interface/index.ts
@@ -7,7 +7,7 @@
*/
import {
Rule,
- SchematicsError,
+ SchematicsException,
apply,
branchAndMerge,
chain,
@@ -27,7 +27,7 @@ export default function (options: InterfaceOptions): Rule {
options.path = options.path ? normalizePath(options.path) : options.path;
const sourceDir = options.sourceDir;
if (!sourceDir) {
- throw new SchematicsError(`sourceDir option is required.`);
+ throw new SchematicsException(`sourceDir option is required.`);
}
const templateSource = apply(url('./files'), [
diff --git a/packages/schematics/angular/module/index.ts b/packages/schematics/angular/module/index.ts
index 5980d5e736..fb55a7e365 100644
--- a/packages/schematics/angular/module/index.ts
+++ b/packages/schematics/angular/module/index.ts
@@ -8,7 +8,7 @@
import {
Rule,
SchematicContext,
- SchematicsError,
+ SchematicsException,
Tree,
apply,
branchAndMerge,
@@ -39,7 +39,7 @@ function addDeclarationToNgModule(options: ModuleOptions): Rule {
const text = host.read(modulePath);
if (text === null) {
- throw new SchematicsError(`File ${modulePath} does not exist.`);
+ throw new SchematicsException(`File ${modulePath} does not exist.`);
}
const sourceText = text.toString('utf-8');
const source = ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true);
@@ -69,7 +69,7 @@ export default function (options: ModuleOptions): Rule {
options.path = options.path ? normalizePath(options.path) : options.path;
const sourceDir = options.sourceDir;
if (!sourceDir) {
- throw new SchematicsError(`sourceDir option is required.`);
+ throw new SchematicsException(`sourceDir option is required.`);
}
return (host: Tree, context: SchematicContext) => {
diff --git a/packages/schematics/angular/pipe/index.ts b/packages/schematics/angular/pipe/index.ts
index 24e086a074..96fd1e02e5 100644
--- a/packages/schematics/angular/pipe/index.ts
+++ b/packages/schematics/angular/pipe/index.ts
@@ -8,7 +8,7 @@
import {
Rule,
SchematicContext,
- SchematicsError,
+ SchematicsException,
Tree,
apply,
branchAndMerge,
@@ -39,7 +39,7 @@ function addDeclarationToNgModule(options: PipeOptions): Rule {
const modulePath = options.module;
const text = host.read(modulePath);
if (text === null) {
- throw new SchematicsError(`File ${modulePath} does not exist.`);
+ throw new SchematicsException(`File ${modulePath} does not exist.`);
}
const sourceText = text.toString('utf-8');
const source = ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true);
@@ -63,7 +63,7 @@ function addDeclarationToNgModule(options: PipeOptions): Rule {
if (options.export) {
const text = host.read(modulePath);
if (text === null) {
- throw new SchematicsError(`File ${modulePath} does not exist.`);
+ throw new SchematicsException(`File ${modulePath} does not exist.`);
}
const sourceText = text.toString('utf-8');
const source = ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true);
@@ -89,7 +89,7 @@ export default function (options: PipeOptions): Rule {
options.path = options.path ? normalizePath(options.path) : options.path;
const sourceDir = options.sourceDir;
if (!sourceDir) {
- throw new SchematicsError(`sourceDir option is required.`);
+ throw new SchematicsException(`sourceDir option is required.`);
}
return (host: Tree, context: SchematicContext) => {
diff --git a/packages/schematics/angular/service/index.ts b/packages/schematics/angular/service/index.ts
index c2dd63fdad..2456af82e5 100644
--- a/packages/schematics/angular/service/index.ts
+++ b/packages/schematics/angular/service/index.ts
@@ -8,7 +8,7 @@
import {
Rule,
SchematicContext,
- SchematicsError,
+ SchematicsException,
Tree,
apply,
branchAndMerge,
@@ -43,7 +43,7 @@ function addProviderToNgModule(options: ServiceOptions): Rule {
const text = host.read(modulePath);
if (text === null) {
- throw new SchematicsError(`File ${modulePath} does not exist.`);
+ throw new SchematicsException(`File ${modulePath} does not exist.`);
}
const sourceText = text.toString('utf-8');
@@ -73,7 +73,7 @@ export default function (options: ServiceOptions): Rule {
options.path = options.path ? normalizePath(options.path) : options.path;
const sourceDir = options.sourceDir;
if (!sourceDir) {
- throw new SchematicsError(`sourceDir option is required.`);
+ throw new SchematicsException(`sourceDir option is required.`);
}
return (host: Tree, context: SchematicContext) => {
From 48909a92619cb3151a2b3c923034c9e6b4042198 Mon Sep 17 00:00:00 2001
From: Hans Larsen
Date: Thu, 14 Sep 2017 21:10:43 -0700
Subject: [PATCH 0064/1016] refactor: move Path to core and add more logic.
---
packages/angular_devkit/core/src/index.ts | 1 +
.../core/src/virtual-fs/index.ts | 8 +
.../core/src/virtual-fs/path.ts | 246 ++++++++++++++++++
.../core/src/virtual-fs/path_benchmark.ts | 19 ++
.../core/src/virtual-fs/path_spec.ts | 147 +++++++++++
.../angular_devkit/schematics/src/index.ts | 1 -
.../schematics/src/rules/move.ts | 8 +-
.../schematics/src/rules/template.ts | 7 +-
.../schematics/src/rules/template_spec.ts | 4 +-
.../schematics/src/tree/action.ts | 15 +-
.../schematics/src/tree/action_spec.ts | 34 +--
.../schematics/src/tree/entry.ts | 6 +-
.../schematics/src/tree/filesystem.ts | 8 +-
.../schematics/src/tree/interface.ts | 6 +-
.../schematics/src/tree/memory-host.ts | 12 +-
.../schematics/src/tree/virtual.ts | 18 +-
.../schematics/src/utility/path.ts | 71 -----
.../schematics/src/utility/path_spec.ts | 70 -----
packages/schematics/angular/class/index.ts | 4 +-
.../schematics/angular/component/index.ts | 4 +-
.../schematics/angular/directive/index.ts | 4 +-
packages/schematics/angular/enum/index.ts | 4 +-
packages/schematics/angular/guard/index.ts | 4 +-
.../schematics/angular/interface/index.ts | 4 +-
packages/schematics/angular/module/index.ts | 4 +-
packages/schematics/angular/pipe/index.ts | 4 +-
packages/schematics/angular/service/index.ts | 4 +-
.../schematics/angular/utility/find-module.ts | 42 +--
28 files changed, 518 insertions(+), 241 deletions(-)
create mode 100644 packages/angular_devkit/core/src/virtual-fs/index.ts
create mode 100644 packages/angular_devkit/core/src/virtual-fs/path.ts
create mode 100644 packages/angular_devkit/core/src/virtual-fs/path_benchmark.ts
create mode 100644 packages/angular_devkit/core/src/virtual-fs/path_spec.ts
delete mode 100644 packages/angular_devkit/schematics/src/utility/path.ts
delete mode 100644 packages/angular_devkit/schematics/src/utility/path_spec.ts
diff --git a/packages/angular_devkit/core/src/index.ts b/packages/angular_devkit/core/src/index.ts
index c3e8ef769a..2b1236c336 100644
--- a/packages/angular_devkit/core/src/index.ts
+++ b/packages/angular_devkit/core/src/index.ts
@@ -10,3 +10,4 @@ export * from './json';
export * from './logger';
export * from './terminal';
export * from './utils';
+export * from './virtual-fs';
diff --git a/packages/angular_devkit/core/src/virtual-fs/index.ts b/packages/angular_devkit/core/src/virtual-fs/index.ts
new file mode 100644
index 0000000000..dc88e8ba13
--- /dev/null
+++ b/packages/angular_devkit/core/src/virtual-fs/index.ts
@@ -0,0 +1,8 @@
+/**
+ * @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 * from './path';
diff --git a/packages/angular_devkit/core/src/virtual-fs/path.ts b/packages/angular_devkit/core/src/virtual-fs/path.ts
new file mode 100644
index 0000000000..515d33fd85
--- /dev/null
+++ b/packages/angular_devkit/core/src/virtual-fs/path.ts
@@ -0,0 +1,246 @@
+/**
+ * @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 { BaseException } from '@angular-devkit/core';
+
+
+export class InvalidPathException extends BaseException {
+ constructor(path: string) { super(`Path ${JSON.stringify(path)} is invalid.`); }
+}
+export class PathMustBeAbsoluteException extends BaseException {
+ constructor(path: string) { super(`Path ${JSON.stringify(path)} must be absolute.`); }
+}
+
+
+/**
+ * A Path recognized by most methods in the DevKit.
+ */
+export type Path = string & {
+ __PRIVATE_DEVKIT_PATH: void;
+};
+
+
+/**
+ * The Separator for normalized path.
+ * @type {Path}
+ */
+export const NormalizedSep = '/' as Path;
+
+
+/**
+ * The root of a normalized path.
+ * @type {Path}
+ */
+export const NormalizedRoot = NormalizedSep as Path;
+
+
+/**
+ * Split a path into multiple path fragments. Each fragments except the last one will end with
+ * a path separator.
+ * @param {Path} path The path to split.
+ * @returns {Path[]} An array of path fragments.
+ */
+export function split(path: Path): Path[] {
+ const arr = path.split(NormalizedSep);
+
+ return arr.map((fragment, i) => fragment + (i < arr.length - 1 ? NormalizedSep : '')) as Path[];
+}
+
+/**
+ *
+ */
+export function extname(path: Path): string {
+ const base = basename(path);
+ const i = base.lastIndexOf('.');
+ if (i < 1) {
+ return '';
+ } else {
+ return base.substr(i);
+ }
+}
+
+/**
+ * This is the equivalent of calling dirname() over and over, until the root, then getting the
+ * basename.
+ *
+ * @example rootname('/a/b/c') == 'a'
+ * @example rootname('a/b') == '.'
+ * @param path The path to get the rootname from.
+ * @returns {Path} The first directory name.
+ */
+export function rootname(path: Path): Path {
+ const i = path.indexOf(NormalizedSep);
+ if (!isAbsolute(path)) {
+ return '.' as Path;
+ } else if (i == -1) {
+ return path;
+ } else {
+ return path.substr(path.lastIndexOf(NormalizedSep) + 1) as Path;
+ }
+}
+
+
+/**
+ * Return the basename of the path, as a Path. See path.basename
+ */
+export function basename(path: Path): Path {
+ const i = path.lastIndexOf(NormalizedSep);
+ if (i == -1) {
+ return path;
+ } else {
+ return path.substr(path.lastIndexOf(NormalizedSep) + 1) as Path;
+ }
+}
+
+
+/**
+ * Return the dirname of the path, as a Path. See path.dirname
+ */
+export function dirname(path: Path): Path {
+ const i = path.lastIndexOf(NormalizedSep);
+ if (i == -1) {
+ return '' as Path;
+ } else {
+ return normalize(path.substr(0, i));
+ }
+}
+
+
+/**
+ * Join multiple paths together, and normalize the result. Accepts strings that will be
+ * normalized as well (but the original must be a path).
+ */
+export function join(p1: Path, ...others: string[]): Path {
+ if (others.length > 0) {
+ return normalize((p1 ? p1 + NormalizedSep : '') + others.join(NormalizedSep));
+ } else {
+ return p1;
+ }
+}
+
+
+/**
+ * Returns true if a path is absolute.
+ */
+export function isAbsolute(p: Path) {
+ return p.startsWith(NormalizedSep);
+}
+
+
+/**
+ * Returns a path such that `join(from, relative(from, to)) == to`.
+ * Both paths must be absolute, otherwise it does not make much sense.
+ */
+export function relative(from: Path, to: Path): Path {
+ if (!isAbsolute(from)) {
+ throw new PathMustBeAbsoluteException(from);
+ }
+ if (!isAbsolute(to)) {
+ throw new PathMustBeAbsoluteException(to);
+ }
+
+ let p: string;
+
+ if (from == to) {
+ p = '';
+ } else {
+ const splitFrom = from.split(NormalizedSep);
+ const splitTo = to.split(NormalizedSep);
+
+ while (splitFrom.length > 0 && splitTo.length > 0 && splitFrom[0] == splitTo[0]) {
+ splitFrom.shift();
+ splitTo.shift();
+ }
+
+ if (splitFrom.length == 0) {
+ p = splitTo.join(NormalizedSep);
+ } else {
+ p = splitFrom.map(_ => '..').concat(splitTo).join(NormalizedSep);
+ }
+ }
+
+ return normalize(p);
+}
+
+
+/**
+ * Returns a Path that is the resolution of p2, from p1. If p2 is absolute, it will return p2,
+ * otherwise will join both p1 and p2.
+ */
+export function resolve(p1: Path, p2: Path) {
+ if (isAbsolute(p2)) {
+ return p2;
+ } else {
+ return join(p1, p2);
+ }
+}
+
+
+/**
+ * Normalize a string into a Path. This is the only mean to get a Path type from a string that
+ * represents a system path. Normalization includes:
+ * - Windows backslashes `\\` are replaced with `/`.
+ * - Windows drivers are replaced with `/X/`, where X is the drive letter.
+ * - Absolute paths starts with `/`.
+ * - Multiple `/` are replaced by a single one.
+ * - Path segments `.` are removed.
+ * - Path segments `..` are resolved.
+ * - If a path is absolute, having a `..` at the start is invalid (and will throw).
+ */
+export function normalize(path: string): Path {
+ if (path == '' || path == '.') {
+ return '' as Path;
+ } else if (path == NormalizedRoot) {
+ return NormalizedRoot;
+ }
+
+ // Match absolute windows path.
+ const original = path;
+ if (path.match(/^[A-Z]:\\/)) {
+ path = '\\' + path[0] + '\\' + path.substr(3);
+ }
+
+ // We convert Windows paths as well here.
+ const p = path.split(/[\/\\]/g);
+ let relative = false;
+ let i = 1;
+
+ // Special case the first one.
+ if (p[0] != '') {
+ p.unshift('.');
+ relative = true;
+ }
+
+ while (i < p.length) {
+ if (p[i] == '.') {
+ p.splice(i, 1);
+ } else if (p[i] == '..') {
+ if (i < 2 && !relative) {
+ throw new InvalidPathException(original);
+ } else if (i >= 2) {
+ p.splice(i - 1, 2);
+ i--;
+ } else {
+ i++;
+ }
+ } else if (p[i] == '') {
+ p.splice(i, 1);
+ } else {
+ i++;
+ }
+ }
+
+ if (p.length == 1) {
+ return p[0] == '' ? NormalizedSep : '' as Path;
+ } else {
+ if (p[0] == '.') {
+ p.shift();
+ }
+
+ return p.join(NormalizedSep) as Path;
+ }
+}
diff --git a/packages/angular_devkit/core/src/virtual-fs/path_benchmark.ts b/packages/angular_devkit/core/src/virtual-fs/path_benchmark.ts
new file mode 100644
index 0000000000..b2fbef6926
--- /dev/null
+++ b/packages/angular_devkit/core/src/virtual-fs/path_benchmark.ts
@@ -0,0 +1,19 @@
+/**
+ * @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 { benchmark } from '@_/benchmark';
+import { join, normalize } from './path';
+
+
+const p1 = '/b/././a/tt/../../../a/b/./d/../c';
+const p2 = '/a/tt/../../../a/b/./d';
+
+
+describe('Virtual FS Path', () => {
+ benchmark('normalize', () => normalize(p1));
+ benchmark('join', () => join(normalize(p1), normalize(p2)));
+});
diff --git a/packages/angular_devkit/core/src/virtual-fs/path_spec.ts b/packages/angular_devkit/core/src/virtual-fs/path_spec.ts
new file mode 100644
index 0000000000..54052acd8f
--- /dev/null
+++ b/packages/angular_devkit/core/src/virtual-fs/path_spec.ts
@@ -0,0 +1,147 @@
+/**
+ * @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 {
+ InvalidPathException,
+ Path,
+ basename,
+ dirname,
+ join,
+ normalize,
+ relative, split,
+} from './path';
+
+
+describe('path', () => {
+ it('normalize', () => {
+ expect(normalize('////')).toBe('/');
+ expect(normalize('././././.')).toBe('');
+ expect(normalize('/./././.')).toBe('/');
+
+ // Regular use cases.
+ expect(normalize('a')).toBe('a');
+ expect(normalize('a/b/c')).toBe('a/b/c');
+ expect(normalize('/a/b/c')).toBe('/a/b/c');
+ expect(normalize('./a/b/c')).toBe('a/b/c');
+ expect(normalize('/./a/b/c')).toBe('/a/b/c');
+ expect(normalize('/./a/b/c/')).toBe('/a/b/c');
+ expect(normalize('/./a/b/./c')).toBe('/a/b/c');
+ expect(normalize('./a/b/./c')).toBe('a/b/c');
+ expect(normalize('/./a/b/d/../c')).toBe('/a/b/c');
+ expect(normalize('/./a/b/./d/../c')).toBe('/a/b/c');
+ expect(normalize('././a/b/./d/../c')).toBe('a/b/c');
+ expect(normalize('a/')).toBe('a');
+ expect(normalize('a/./..')).toBe('');
+
+ // Reducing to nothing use cases.
+ expect(normalize('')).toBe('');
+ expect(normalize('.')).toBe('');
+ expect(normalize('/.')).toBe('/');
+ expect(normalize('/./.')).toBe('/');
+ expect(normalize('/././.')).toBe('/');
+ expect(normalize('/c/..')).toBe('/');
+
+ // Out of directory.
+ expect(normalize('..')).toBe('..');
+ expect(normalize('./..')).toBe('..');
+ expect(normalize('../a/b/c')).toBe('../a/b/c');
+ expect(normalize('./a/../../a/b/c')).toBe('../a/b/c');
+
+ // Invalid use cases.
+ expect(() => normalize('/./././../././/'))
+ .toThrow(new InvalidPathException('/./././../././/'));
+ expect(() => normalize('/./././../././/../'))
+ .toThrow(new InvalidPathException('/./././../././/../'));
+ expect(() => normalize('/./././../././a/.'))
+ .toThrow(new InvalidPathException('/./././../././a/.'));
+
+ expect(() => normalize('/c/../../')).toThrow(new InvalidPathException('/c/../../'));
+
+ // Windows use cases.
+ expect(normalize('a\\b\\c')).toBe('a/b/c');
+ expect(normalize('\\a\\b\\c')).toBe('/a/b/c');
+ expect(normalize('.\\a\\b\\c')).toBe('a/b/c');
+ expect(normalize('C:\\a\\b\\c')).toBe('/C/a/b/c');
+ expect(normalize('A:\\a\\b\\c')).toBe('/A/a/b/c');
+ expect(() => normalize('A:\\..\\..'))
+ .toThrow(new InvalidPathException('A:\\..\\..'));
+ expect(normalize('\\.\\a\\b\\c')).toBe('/a/b/c');
+ expect(normalize('\\.\\a\\b\\.\\c')).toBe('/a/b/c');
+ expect(normalize('\\.\\a\\b\\d\\..\\c')).toBe('/a/b/c');
+ expect(normalize('\\.\\a\\b\\.\\d\\..\\c')).toBe('/a/b/c');
+ expect(normalize('a\\')).toBe('a');
+ });
+
+ describe('split', () => {
+ const tests = [
+ ['a', ['a']],
+ ['/a/b', ['/', 'a/', 'b']],
+ ['a/b', ['a/', 'b']],
+ ];
+
+ for (const goldens of tests) {
+ const result = goldens.pop();
+ const args = goldens.map((x: string) => normalize(x)) as Path[];
+
+ it(`(${JSON.stringify(args)}) == "${result}"`, () => {
+ expect(split.apply(null, args)).toEqual(result);
+ });
+ }
+ });
+
+ describe('join', () => {
+ const tests = [
+ ['a', 'a'],
+ ['/a', '/b', '/a/b'],
+ ['/a', '/b', '/c', '/a/b/c'],
+ ['/a', 'b', 'c', '/a/b/c'],
+ ['a', 'b', 'c', 'a/b/c'],
+ ];
+
+ for (const goldens of tests) {
+ const result = goldens.pop();
+ const args = goldens.map(x => normalize(x)) as Path[];
+
+ it(`(${JSON.stringify(args)}) == "${result}"`, () => {
+ expect(join.apply(null, args)).toBe(result);
+ });
+ }
+ });
+
+ describe('relative', () => {
+ const tests = [
+ ['/a/b/c', '/a/b/c', ''],
+ ['/a/b', '/a/b/c', 'c'],
+ ['/a/b/c', '/a/b', '..'],
+ ['/a/b/c', '/a/b/d', '../d'],
+ ];
+
+ for (const [from, to, result] of tests) {
+ it(`("${from}", "${to}") == "${result}"`, () => {
+ const f = normalize(from);
+ const t = normalize(to);
+
+ expect(relative(f, t)).toBe(result);
+ });
+ }
+ });
+
+ it('dirname', () => {
+ expect(dirname(normalize('a'))).toBe('');
+ expect(dirname(normalize('/a/b/c'))).toBe('/a/b');
+ expect(dirname(normalize('./c'))).toBe('');
+ expect(dirname(normalize('./a/b/c'))).toBe('a/b');
+ });
+
+ it('basename', () => {
+ expect(basename(normalize('a'))).toBe('a');
+ expect(basename(normalize('/a/b/c'))).toBe('c');
+ expect(basename(normalize('./c'))).toBe('c');
+ expect(basename(normalize('.'))).toBe('');
+ expect(basename(normalize('./a/b/c'))).toBe('c');
+ });
+});
diff --git a/packages/angular_devkit/schematics/src/index.ts b/packages/angular_devkit/schematics/src/index.ts
index fa254dea9c..410ee80078 100644
--- a/packages/angular_devkit/schematics/src/index.ts
+++ b/packages/angular_devkit/schematics/src/index.ts
@@ -32,7 +32,6 @@ export {UpdateRecorder} from './tree/interface';
export * from './engine/schematic';
export * from './sink/dryrun';
export {FileSystemSink} from './sink/filesystem';
-export * from './utility/path';
export interface TreeConstructor {
diff --git a/packages/angular_devkit/schematics/src/rules/move.ts b/packages/angular_devkit/schematics/src/rules/move.ts
index 24cece95e3..0c09ae592b 100644
--- a/packages/angular_devkit/schematics/src/rules/move.ts
+++ b/packages/angular_devkit/schematics/src/rules/move.ts
@@ -5,9 +5,9 @@
* 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 { normalize } from '@angular-devkit/core';
import { FileOperator, Rule } from '../engine/interface';
import { FileEntry } from '../tree/interface';
-import { normalizePath } from '../utility/path';
import { forEach } from './base';
@@ -17,14 +17,14 @@ export function moveOp(from: string, to?: string): FileOperator {
from = '/';
}
- const fromPath = normalizePath(from);
- const toPath = normalizePath(to);
+ const fromPath = normalize(from);
+ const toPath = normalize(to);
return (entry: FileEntry) => {
if (entry.path.startsWith(fromPath)) {
return {
content: entry.content,
- path: normalizePath(toPath + '/' + entry.path.substr(fromPath.length)),
+ path: normalize(toPath + '/' + entry.path.substr(fromPath.length)),
};
}
diff --git a/packages/angular_devkit/schematics/src/rules/template.ts b/packages/angular_devkit/schematics/src/rules/template.ts
index 606885b44d..08aa0cd677 100644
--- a/packages/angular_devkit/schematics/src/rules/template.ts
+++ b/packages/angular_devkit/schematics/src/rules/template.ts
@@ -5,12 +5,11 @@
* 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 { BaseException } from '@angular-devkit/core';
+import { BaseException, normalize } from '@angular-devkit/core';
import { FileOperator, Rule } from '../engine/interface';
import { FileEntry } from '../tree/interface';
-import { normalizePath } from '../utility/path';
import { chain, forEach } from './base';
-import {template as templateImpl } from './template/template';
+import { template as templateImpl } from './template/template';
import { isBinary } from './utils/is-binary';
@@ -67,7 +66,7 @@ export function applyPathTemplate(options: T): FileOp
const original = path;
// Path template.
- path = normalizePath(path.replace(kPathTemplateComponentRE, (_, match) => {
+ path = normalize(path.replace(kPathTemplateComponentRE, (_, match) => {
const [name, ...pipes] = match.split(kPathTemplatePipeRE);
const value = typeof options[name] == 'function'
? (options[name] as TemplatePipeFunction).call(options, original)
diff --git a/packages/angular_devkit/schematics/src/rules/template_spec.ts b/packages/angular_devkit/schematics/src/rules/template_spec.ts
index da5ee8ef25..40f41f0411 100644
--- a/packages/angular_devkit/schematics/src/rules/template_spec.ts
+++ b/packages/angular_devkit/schematics/src/rules/template_spec.ts
@@ -5,8 +5,8 @@
* 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 { normalize } from '@angular-devkit/core';
import { FileEntry } from '../tree/interface';
-import { normalizePath } from '../utility/path';
import {
InvalidPipeException,
OptionIsNotDefinedException,
@@ -25,7 +25,7 @@ function _entry(path?: string, content?: string): FileEntry {
}
return {
- path: normalizePath(path),
+ path: normalize(path),
content: new Buffer(content),
};
}
diff --git a/packages/angular_devkit/schematics/src/tree/action.ts b/packages/angular_devkit/schematics/src/tree/action.ts
index 177f6df2ca..3de30cc5b4 100644
--- a/packages/angular_devkit/schematics/src/tree/action.ts
+++ b/packages/angular_devkit/schematics/src/tree/action.ts
@@ -5,8 +5,7 @@
* 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 { BaseException } from '@angular-devkit/core';
-import { SchematicPath } from '../utility/path';
+import { BaseException, Path } from '@angular-devkit/core';
export class UnknownActionException extends BaseException {
@@ -23,7 +22,7 @@ export type Action = CreateFileAction
export interface ActionBase {
readonly id: number;
readonly parent: number;
- readonly path: SchematicPath;
+ readonly path: Path;
}
@@ -39,16 +38,16 @@ export class ActionList implements Iterable {
}, action) as Action);
}
- create(path: SchematicPath, content: Buffer) {
+ create(path: Path, content: Buffer) {
this._action({ kind: 'c', path, content });
}
- overwrite(path: SchematicPath, content: Buffer) {
+ overwrite(path: Path, content: Buffer) {
this._action({ kind: 'o', path, content });
}
- rename(path: SchematicPath, to: SchematicPath) {
+ rename(path: Path, to: Path) {
this._action({ kind: 'r', path, to });
}
- delete(path: SchematicPath) {
+ delete(path: Path) {
this._action({ kind: 'd', path });
}
@@ -156,7 +155,7 @@ export interface OverwriteFileAction extends ActionBase {
// If the target path already exists, this is an error.
export interface RenameFileAction extends ActionBase {
readonly kind: 'r';
- readonly to: SchematicPath;
+ readonly to: Path;
}
// Delete a file. If the file does not exist, this is an error.
diff --git a/packages/angular_devkit/schematics/src/tree/action_spec.ts b/packages/angular_devkit/schematics/src/tree/action_spec.ts
index e50e316d09..a1dd916d8f 100644
--- a/packages/angular_devkit/schematics/src/tree/action_spec.ts
+++ b/packages/angular_devkit/schematics/src/tree/action_spec.ts
@@ -5,7 +5,7 @@
* 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 { normalizePath } from '../utility/path';
+import { normalize } from '@angular-devkit/core';
import { ActionList } from './action';
@@ -14,9 +14,9 @@ describe('Action', () => {
it('works with create', () => {
const actions = new ActionList;
- actions.create(normalizePath('/a/b'), new Buffer('1'));
- actions.create(normalizePath('/a/c'), new Buffer('2'));
- actions.create(normalizePath('/a/c'), new Buffer('3'));
+ actions.create(normalize('/a/b'), new Buffer('1'));
+ actions.create(normalize('/a/c'), new Buffer('2'));
+ actions.create(normalize('/a/c'), new Buffer('3'));
expect(actions.length).toBe(3);
actions.optimize();
@@ -25,10 +25,10 @@ describe('Action', () => {
it('works with overwrite', () => {
const actions = new ActionList;
- actions.create(normalizePath('/a/b'), new Buffer('1'));
- actions.create(normalizePath('/a/c'), new Buffer('2'));
- actions.overwrite(normalizePath('/a/c'), new Buffer('3'));
- actions.overwrite(normalizePath('/a/b'), new Buffer('4'));
+ actions.create(normalize('/a/b'), new Buffer('1'));
+ actions.create(normalize('/a/c'), new Buffer('2'));
+ actions.overwrite(normalize('/a/c'), new Buffer('3'));
+ actions.overwrite(normalize('/a/b'), new Buffer('4'));
expect(actions.length).toBe(4);
actions.optimize();
@@ -38,11 +38,11 @@ describe('Action', () => {
it('works with cloning a list', () => {
const actions = new ActionList;
- actions.create(normalizePath('/a/b'), new Buffer('1'));
- actions.create(normalizePath('/a/c'), new Buffer('2'));
- actions.overwrite(normalizePath('/a/c'), new Buffer('3'));
- actions.overwrite(normalizePath('/a/b'), new Buffer('4'));
- actions.create(normalizePath('/a/d'), new Buffer('5'));
+ actions.create(normalize('/a/b'), new Buffer('1'));
+ actions.create(normalize('/a/c'), new Buffer('2'));
+ actions.overwrite(normalize('/a/c'), new Buffer('3'));
+ actions.overwrite(normalize('/a/b'), new Buffer('4'));
+ actions.create(normalize('/a/d'), new Buffer('5'));
const actions2 = new ActionList;
actions.forEach(x => actions2.push(x));
@@ -59,10 +59,10 @@ describe('Action', () => {
it('handles edge cases (1)', () => {
const actions = new ActionList;
- actions.create(normalizePath('/test'), new Buffer('1'));
- actions.overwrite(normalizePath('/test'), new Buffer('3'));
- actions.overwrite(normalizePath('/hello'), new Buffer('2'));
- actions.overwrite(normalizePath('/test'), new Buffer('4'));
+ actions.create(normalize('/test'), new Buffer('1'));
+ actions.overwrite(normalize('/test'), new Buffer('3'));
+ actions.overwrite(normalize('/hello'), new Buffer('2'));
+ actions.overwrite(normalize('/test'), new Buffer('4'));
const actions2 = new ActionList;
actions.forEach(x => actions2.push(x));
diff --git a/packages/angular_devkit/schematics/src/tree/entry.ts b/packages/angular_devkit/schematics/src/tree/entry.ts
index a652b33285..a4dd2c02d7 100644
--- a/packages/angular_devkit/schematics/src/tree/entry.ts
+++ b/packages/angular_devkit/schematics/src/tree/entry.ts
@@ -5,12 +5,12 @@
* 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 { SchematicPath } from '../utility/path';
+import { Path } from '@angular-devkit/core';
import { FileEntry } from './interface';
export class SimpleFileEntry implements FileEntry {
- constructor(private _path: SchematicPath, private _content: Buffer) {}
+ constructor(private _path: Path, private _content: Buffer) {}
get path() { return this._path; }
get content() { return this._content; }
@@ -20,7 +20,7 @@ export class SimpleFileEntry implements FileEntry {
export class LazyFileEntry implements FileEntry {
private _content: Buffer | null = null;
- constructor(private _path: SchematicPath, private _load: (path?: SchematicPath) => Buffer) {}
+ constructor(private _path: Path, private _load: (path?: Path) => Buffer) {}
get path() { return this._path; }
get content() { return this._content || (this._content = this._load(this._path)); }
diff --git a/packages/angular_devkit/schematics/src/tree/filesystem.ts b/packages/angular_devkit/schematics/src/tree/filesystem.ts
index fa1321c0f3..73c2cc0a7e 100644
--- a/packages/angular_devkit/schematics/src/tree/filesystem.ts
+++ b/packages/angular_devkit/schematics/src/tree/filesystem.ts
@@ -5,7 +5,7 @@
* 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 { SchematicPath, normalizePath } from '../utility/path';
+import { Path, normalize } from '@angular-devkit/core';
import { LazyFileEntry } from './entry';
import { VirtualTree } from './virtual';
@@ -32,14 +32,14 @@ export class FileSystemTree extends VirtualTree {
});
}
- protected _recursiveFileList(): [ string, SchematicPath ][] {
+ protected _recursiveFileList(): [ string, Path ][] {
const host = this._host;
- const list: [string, SchematicPath][] = [];
+ const list: [string, Path][] = [];
function recurse(systemPath: string, schematicPath: string) {
for (const name of host.listDirectory(systemPath)) {
const systemName = host.join(systemPath, name);
- const normalizedPath = normalizePath(schematicPath + '/' + name);
+ const normalizedPath = normalize(schematicPath + '/' + name);
if (host.isDirectory(normalizedPath)) {
recurse(systemName, normalizedPath);
} else {
diff --git a/packages/angular_devkit/schematics/src/tree/interface.ts b/packages/angular_devkit/schematics/src/tree/interface.ts
index fb6a015392..9fee87080b 100644
--- a/packages/angular_devkit/schematics/src/tree/interface.ts
+++ b/packages/angular_devkit/schematics/src/tree/interface.ts
@@ -5,7 +5,7 @@
* 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 { SchematicPath } from '../utility/path';
+import { Path } from '@angular-devkit/core';
import { Action } from './action';
@@ -32,13 +32,13 @@ export enum MergeStrategy {
export interface FileEntry {
- readonly path: SchematicPath;
+ readonly path: Path;
readonly content: Buffer;
}
export interface FilePredicate {
- (path: SchematicPath, entry?: Readonly | null): T;
+ (path: Path, entry?: Readonly | null): T;
}
diff --git a/packages/angular_devkit/schematics/src/tree/memory-host.ts b/packages/angular_devkit/schematics/src/tree/memory-host.ts
index 82a3795617..e75a4f37a4 100644
--- a/packages/angular_devkit/schematics/src/tree/memory-host.ts
+++ b/packages/angular_devkit/schematics/src/tree/memory-host.ts
@@ -5,7 +5,7 @@
* 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 { normalizePath } from '../utility/path';
+import { normalize } from '@angular-devkit/core';
import { FileSystemTreeHost } from './filesystem';
@@ -15,14 +15,14 @@ export class InMemoryFileSystemTreeHost implements FileSystemTreeHost {
constructor(content: { [path: string]: string }) {
this._content = Object.create(null);
Object.keys(content).forEach(path => {
- path = normalizePath(path);
+ path = normalize(path);
this._content[path] = new Buffer(content[path]);
});
this._files = Object.keys(this._content);
}
listDirectory(path: string) {
- path = normalizePath(path).replace(/\/?$/, '/');
+ path = normalize(path).replace(/\/?$/, '/');
return Object.keys(
this._files
@@ -33,17 +33,17 @@ export class InMemoryFileSystemTreeHost implements FileSystemTreeHost {
).sort();
}
isDirectory(path: string) {
- path = normalizePath(path);
+ path = normalize(path);
return path == '/' || this._files.some(p => p.split('/').slice(0, -1).join('/') == path);
}
readFile(path: string) {
- path = normalizePath(path);
+ path = normalize(path);
return this._content[path] || new Buffer('');
}
join(path1: string, path2: string) {
- return normalizePath(path1 + '/' + path2);
+ return normalize(path1 + '/' + path2);
}
}
diff --git a/packages/angular_devkit/schematics/src/tree/virtual.ts b/packages/angular_devkit/schematics/src/tree/virtual.ts
index 0d611d1b10..362f0afab4 100644
--- a/packages/angular_devkit/schematics/src/tree/virtual.ts
+++ b/packages/angular_devkit/schematics/src/tree/virtual.ts
@@ -5,6 +5,7 @@
* 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 { Path, normalize } from '@angular-devkit/core';
import {
ContentHasMutatedException,
FileAlreadyExistException,
@@ -12,7 +13,6 @@ import {
InvalidUpdateRecordException,
MergeConflictException,
} from '../exception/exception';
-import { SchematicPath, normalizePath } from '../utility/path';
import { Action, ActionList, UnknownActionException } from './action';
import { SimpleFileEntry } from './entry';
import { FileEntry, MergeStrategy, Tree, UpdateRecorder } from './interface';
@@ -23,17 +23,17 @@ import { UpdateRecorderBase } from './recorder';
* The root class of most trees.
*/
export class VirtualTree implements Tree {
- protected _root = new Map();
+ protected _root = new Map();
protected _actions = new ActionList();
- protected _cacheMap = new Map();
+ protected _cacheMap = new Map();
/**
* Normalize the path. Made available to subclasses to overload.
* @param path The path to normalize.
* @returns {string} A path that is resolved and normalized.
*/
- protected _normalizePath(path: string): SchematicPath {
- return normalizePath(path);
+ protected _normalizePath(path: string): Path {
+ return normalize(path);
}
/**
@@ -129,7 +129,7 @@ export class VirtualTree implements Tree {
this._delete(this._normalizePath(path));
}
- protected _overwrite(path: SchematicPath, content: Buffer, action?: Action) {
+ protected _overwrite(path: Path, content: Buffer, action?: Action) {
if (!this.has(path)) {
throw new FileDoesNotExistException(path);
}
@@ -141,7 +141,7 @@ export class VirtualTree implements Tree {
}
this.set(new SimpleFileEntry(path, content));
}
- protected _create(path: SchematicPath, content: Buffer, action?: Action) {
+ protected _create(path: Path, content: Buffer, action?: Action) {
if (this._cacheMap.has(path)) {
throw new FileAlreadyExistException(path);
}
@@ -153,7 +153,7 @@ export class VirtualTree implements Tree {
}
this.set(new SimpleFileEntry(path, content as Buffer));
}
- protected _rename(path: SchematicPath, to: SchematicPath, action?: Action, force = false) {
+ protected _rename(path: Path, to: Path, action?: Action, force = false) {
const entry = this.get(path);
if (!entry) {
throw new FileDoesNotExistException(path);
@@ -171,7 +171,7 @@ export class VirtualTree implements Tree {
this.set(new SimpleFileEntry(to, entry.content));
this._cacheMap.delete(path);
}
- protected _delete(path: SchematicPath, action?: Action) {
+ protected _delete(path: Path, action?: Action) {
if (!this.has(path)) {
throw new FileDoesNotExistException(path);
}
diff --git a/packages/angular_devkit/schematics/src/utility/path.ts b/packages/angular_devkit/schematics/src/utility/path.ts
deleted file mode 100644
index 11d78a1d96..0000000000
--- a/packages/angular_devkit/schematics/src/utility/path.ts
+++ /dev/null
@@ -1,71 +0,0 @@
-/**
- * @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 { BaseException } from '@angular-devkit/core';
-
-
-export type SchematicPath = string & {
- __PRIVATE_SCHEMATIC_PATH: void;
-};
-
-
-export class InvalidPathException extends BaseException {
- constructor(path: string) { super(`Path "${path}" is invalid.`); }
-}
-
-
-export function relativePath(from: SchematicPath, to: SchematicPath): SchematicPath {
- let p: string;
-
- if (from == to) {
- p = '';
- } else {
- const splitFrom = from.split('/');
- const splitTo = to.split('/');
-
- while (splitFrom.length > 0 && splitTo.length > 0 && splitFrom[0] == splitTo[0]) {
- splitFrom.shift();
- splitTo.shift();
- }
-
- if (splitFrom.length == 0) {
- p = splitTo.join('/');
- } else {
- p = splitFrom.map(_ => '..').concat(splitTo).join('/');
- }
- }
-
- return (p as SchematicPath);
-}
-
-
-export function normalizePath(path: string): SchematicPath {
- let p = path;
- if (p[0] != '/') {
- p = '/' + p;
- }
- if (p.endsWith('..')) {
- throw new InvalidPathException(path);
- }
-
- let oldP: string | null = null;
- while (oldP !== p) {
- oldP = p;
- p = p
- .replace(/\/[^\/]+\/\.\.\//g, '/')
- .replace(/\/[^\/]+\/\.\.$/g, '/')
- .replace(/\/\.?$/g, '/')
- .replace(/\/\.?\//g, '/')
- .replace(/\\/g, '/');
- }
-
- if (p.startsWith('/../') || (p.endsWith('/') && p !== '/')) {
- throw new InvalidPathException(path);
- }
-
- return (p as SchematicPath);
-}
diff --git a/packages/angular_devkit/schematics/src/utility/path_spec.ts b/packages/angular_devkit/schematics/src/utility/path_spec.ts
deleted file mode 100644
index 1c10dae8e8..0000000000
--- a/packages/angular_devkit/schematics/src/utility/path_spec.ts
+++ /dev/null
@@ -1,70 +0,0 @@
-/**
- * @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 { InvalidPathException, normalizePath, relativePath } from './path';
-
-
-describe('normalizePath', () => {
- it('works', () => {
- // Regular use cases.
- expect(normalizePath('a/b/c')).toBe('/a/b/c');
- expect(normalizePath('/a/b/c')).toBe('/a/b/c');
- expect(normalizePath('./a/b/c')).toBe('/a/b/c');
- expect(normalizePath('/./a/b/c')).toBe('/a/b/c');
- expect(normalizePath('/./a/b/./c')).toBe('/a/b/c');
- expect(normalizePath('/./a/b/d/../c')).toBe('/a/b/c');
- expect(normalizePath('/./a/b/./d/../c')).toBe('/a/b/c');
-
- // Reducing to nothing use cases.
- expect(normalizePath('')).toBe('/');
- expect(normalizePath('.')).toBe('/');
- expect(normalizePath('/.')).toBe('/');
- expect(normalizePath('/./.')).toBe('/');
- expect(normalizePath('/././.')).toBe('/');
- expect(normalizePath('/./././../././/../')).toBe('/');
- expect(normalizePath('/./././../././/')).toBe('/');
-
- // Directories use cases.
- expect(() => normalizePath('/./././../././a/.'))
- .toThrow(new InvalidPathException('/./././../././a/.'));
- expect(() => normalizePath('a/')).toThrow(new InvalidPathException('a/'));
- expect(() => normalizePath('a/./..')).toThrow(new InvalidPathException('a/./..'));
-
- expect(() => normalizePath('../a/b/c')).toThrow(new InvalidPathException('../a/b/c'));
- expect(() => normalizePath('/c/..')).toThrow(new InvalidPathException('/c/..'));
- expect(() => normalizePath('/c/../../')).toThrow(new InvalidPathException('/c/../../'));
-
- // Windows use cases.
- expect(normalizePath('a\\b\\c')).toBe('/a/b/c');
- expect(normalizePath('\\a\\b\\c')).toBe('/a/b/c');
- expect(normalizePath('.\\a\\b\\c')).toBe('/a/b/c');
- expect(normalizePath('\\.\\a\\b\\c')).toBe('/a/b/c');
- expect(normalizePath('\\.\\a\\b\\.\\c')).toBe('/a/b/c');
- expect(normalizePath('\\.\\a\\b\\d\\..\\c')).toBe('/a/b/c');
- expect(normalizePath('\\.\\a\\b\\.\\d\\..\\c')).toBe('/a/b/c');
- expect(() => normalizePath('a\\')).toThrow(new InvalidPathException('a\\'));
- });
-});
-
-
-describe('relativePath', () => {
- const tests = [
- ['a/b/c', 'a/b/c', ''],
- ['a/b', 'a/b/c', 'c'],
- ['a/b/c', 'a/b', '..'],
- ['a/b/c', 'a/b/d', '../d'],
- ];
-
- for (const [from, to, result] of tests) {
- it(`("${from}", "${to}") == "${result}"`, () => {
- const f = normalizePath(from);
- const t = normalizePath(to);
-
- expect(relativePath(f, t)).toBe(result);
- });
- }
-});
diff --git a/packages/schematics/angular/class/index.ts b/packages/schematics/angular/class/index.ts
index 912e5a4025..f07335365c 100644
--- a/packages/schematics/angular/class/index.ts
+++ b/packages/schematics/angular/class/index.ts
@@ -5,6 +5,7 @@
* 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 { normalize } from '@angular-devkit/core';
import {
Rule,
SchematicsException,
@@ -15,7 +16,6 @@ import {
mergeWith,
move,
noop,
- normalizePath,
template,
url,
} from '@angular-devkit/schematics';
@@ -25,7 +25,7 @@ import { Schema as ClassOptions } from './schema';
export default function (options: ClassOptions): Rule {
options.type = !!options.type ? `.${options.type}` : '';
- options.path = options.path ? normalizePath(options.path) : options.path;
+ options.path = options.path ? normalize(options.path) : options.path;
const sourceDir = options.sourceDir;
if (!sourceDir) {
throw new SchematicsException(`sourceDir option is required.`);
diff --git a/packages/schematics/angular/component/index.ts b/packages/schematics/angular/component/index.ts
index 86ff126142..378d3d83c7 100644
--- a/packages/schematics/angular/component/index.ts
+++ b/packages/schematics/angular/component/index.ts
@@ -5,6 +5,7 @@
* 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 { normalize } from '@angular-devkit/core';
import {
Rule,
SchematicContext,
@@ -17,7 +18,6 @@ import {
mergeWith,
move,
noop,
- normalizePath,
template,
url,
} from '@angular-devkit/schematics';
@@ -109,7 +109,7 @@ export default function(options: ComponentOptions): Rule {
return (host: Tree, context: SchematicContext) => {
options.selector = options.selector || buildSelector(options);
- options.path = options.path ? normalizePath(options.path) : options.path;
+ options.path = options.path ? normalize(options.path) : options.path;
options.module = findModuleFromOptions(host, options);
const templateSource = apply(url('./files'), [
diff --git a/packages/schematics/angular/directive/index.ts b/packages/schematics/angular/directive/index.ts
index 048c28acb4..a043661c29 100644
--- a/packages/schematics/angular/directive/index.ts
+++ b/packages/schematics/angular/directive/index.ts
@@ -5,6 +5,7 @@
* 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 { normalize } from '@angular-devkit/core';
import {
Rule,
SchematicContext,
@@ -17,7 +18,6 @@ import {
mergeWith,
move,
noop,
- normalizePath,
template,
url,
} from '@angular-devkit/schematics';
@@ -100,7 +100,7 @@ function buildSelector(options: DirectiveOptions) {
export default function (options: DirectiveOptions): Rule {
options.selector = options.selector || buildSelector(options);
- options.path = options.path ? normalizePath(options.path) : options.path;
+ options.path = options.path ? normalize(options.path) : options.path;
const sourceDir = options.sourceDir;
if (!sourceDir) {
throw new SchematicsException(`sourceDir option is required.`);
diff --git a/packages/schematics/angular/enum/index.ts b/packages/schematics/angular/enum/index.ts
index 39c2c856ed..5f46c1dd41 100644
--- a/packages/schematics/angular/enum/index.ts
+++ b/packages/schematics/angular/enum/index.ts
@@ -5,6 +5,7 @@
* 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 { normalize } from '@angular-devkit/core';
import {
Rule,
SchematicsException,
@@ -13,7 +14,6 @@ import {
chain,
mergeWith,
move,
- normalizePath,
template,
url,
} from '@angular-devkit/schematics';
@@ -22,7 +22,7 @@ import { Schema as EnumOptions } from './schema';
export default function (options: EnumOptions): Rule {
- options.path = options.path ? normalizePath(options.path) : options.path;
+ options.path = options.path ? normalize(options.path) : options.path;
const sourceDir = options.sourceDir;
if (!sourceDir) {
throw new SchematicsException(`sourceDir option is required.`);
diff --git a/packages/schematics/angular/guard/index.ts b/packages/schematics/angular/guard/index.ts
index 9f842064d0..cd1c3634ae 100644
--- a/packages/schematics/angular/guard/index.ts
+++ b/packages/schematics/angular/guard/index.ts
@@ -5,6 +5,7 @@
* 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 { normalize } from '@angular-devkit/core';
import {
Rule,
SchematicContext,
@@ -17,7 +18,6 @@ import {
mergeWith,
move,
noop,
- normalizePath,
template,
url,
} from '@angular-devkit/schematics';
@@ -66,7 +66,7 @@ function addDeclarationToNgModule(options: GuardOptions): Rule {
}
export default function (options: GuardOptions): Rule {
- options.path = options.path ? normalizePath(options.path) : options.path;
+ options.path = options.path ? normalize(options.path) : options.path;
const sourceDir = options.sourceDir;
if (!sourceDir) {
throw new SchematicsException(`sourceDir option is required.`);
diff --git a/packages/schematics/angular/interface/index.ts b/packages/schematics/angular/interface/index.ts
index d5d59aa0c6..c1e466d205 100644
--- a/packages/schematics/angular/interface/index.ts
+++ b/packages/schematics/angular/interface/index.ts
@@ -5,6 +5,7 @@
* 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 { normalize } from '@angular-devkit/core';
import {
Rule,
SchematicsException,
@@ -13,7 +14,6 @@ import {
chain,
mergeWith,
move,
- normalizePath,
template,
url,
} from '@angular-devkit/schematics';
@@ -24,7 +24,7 @@ import { Schema as InterfaceOptions } from './schema';
export default function (options: InterfaceOptions): Rule {
options.prefix = options.prefix ? options.prefix : '';
options.type = !!options.type ? `.${options.type}` : '';
- options.path = options.path ? normalizePath(options.path) : options.path;
+ options.path = options.path ? normalize(options.path) : options.path;
const sourceDir = options.sourceDir;
if (!sourceDir) {
throw new SchematicsException(`sourceDir option is required.`);
diff --git a/packages/schematics/angular/module/index.ts b/packages/schematics/angular/module/index.ts
index fb55a7e365..c81905d1e0 100644
--- a/packages/schematics/angular/module/index.ts
+++ b/packages/schematics/angular/module/index.ts
@@ -5,6 +5,7 @@
* 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 { normalize } from '@angular-devkit/core';
import {
Rule,
SchematicContext,
@@ -17,7 +18,6 @@ import {
mergeWith,
move,
noop,
- normalizePath,
template,
url,
} from '@angular-devkit/schematics';
@@ -66,7 +66,7 @@ function addDeclarationToNgModule(options: ModuleOptions): Rule {
}
export default function (options: ModuleOptions): Rule {
- options.path = options.path ? normalizePath(options.path) : options.path;
+ options.path = options.path ? normalize(options.path) : options.path;
const sourceDir = options.sourceDir;
if (!sourceDir) {
throw new SchematicsException(`sourceDir option is required.`);
diff --git a/packages/schematics/angular/pipe/index.ts b/packages/schematics/angular/pipe/index.ts
index 96fd1e02e5..4d6a058890 100644
--- a/packages/schematics/angular/pipe/index.ts
+++ b/packages/schematics/angular/pipe/index.ts
@@ -5,6 +5,7 @@
* 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 { normalize } from '@angular-devkit/core';
import {
Rule,
SchematicContext,
@@ -17,7 +18,6 @@ import {
mergeWith,
move,
noop,
- normalizePath,
template,
url,
} from '@angular-devkit/schematics';
@@ -86,7 +86,7 @@ function addDeclarationToNgModule(options: PipeOptions): Rule {
}
export default function (options: PipeOptions): Rule {
- options.path = options.path ? normalizePath(options.path) : options.path;
+ options.path = options.path ? normalize(options.path) : options.path;
const sourceDir = options.sourceDir;
if (!sourceDir) {
throw new SchematicsException(`sourceDir option is required.`);
diff --git a/packages/schematics/angular/service/index.ts b/packages/schematics/angular/service/index.ts
index 2456af82e5..b8800f9905 100644
--- a/packages/schematics/angular/service/index.ts
+++ b/packages/schematics/angular/service/index.ts
@@ -5,6 +5,7 @@
* 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 { normalize } from '@angular-devkit/core';
import {
Rule,
SchematicContext,
@@ -17,7 +18,6 @@ import {
mergeWith,
move,
noop,
- normalizePath,
template,
url,
} from '@angular-devkit/schematics';
@@ -70,7 +70,7 @@ function addProviderToNgModule(options: ServiceOptions): Rule {
}
export default function (options: ServiceOptions): Rule {
- options.path = options.path ? normalizePath(options.path) : options.path;
+ options.path = options.path ? normalize(options.path) : options.path;
const sourceDir = options.sourceDir;
if (!sourceDir) {
throw new SchematicsException(`sourceDir option is required.`);
diff --git a/packages/schematics/angular/utility/find-module.ts b/packages/schematics/angular/utility/find-module.ts
index da57f0f578..cee5dfd371 100644
--- a/packages/schematics/angular/utility/find-module.ts
+++ b/packages/schematics/angular/utility/find-module.ts
@@ -5,7 +5,8 @@
* 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 { SchematicPath, Tree, normalizePath, relativePath } from '@angular-devkit/schematics';
+import { Path, normalize, relative } from '@angular-devkit/core';
+import { Tree } from '@angular-devkit/schematics';
import { dasherize } from '../strings';
@@ -21,10 +22,10 @@ export interface ModuleOptions {
/**
- * Find the module refered by a set of options passed to the schematics.
+ * Find the module referred by a set of options passed to the schematics.
*/
export function findModuleFromOptions(host: Tree,
- options: ModuleOptions): SchematicPath | undefined {
+ options: ModuleOptions): Path | undefined {
if (options.hasOwnProperty('skipImport') && options.skipImport) {
return undefined;
}
@@ -33,20 +34,20 @@ export function findModuleFromOptions(host: Tree,
const pathToCheck = (options.sourceDir || '') + '/' + (options.path || '')
+ (options.flat ? '' : '/' + dasherize(options.name));
- return normalizePath(findModule(host, pathToCheck));
+ return normalize(findModule(host, pathToCheck));
} else {
- const modulePath = normalizePath(
+ const modulePath = normalize(
options.sourceDir + '/' + (options.appRoot || options.path) + '/' + options.module);
- const moduleBaseName = normalizePath(modulePath).split('/').pop();
+ const moduleBaseName = normalize(modulePath).split('/').pop();
if (host.exists(modulePath)) {
- return normalizePath(modulePath);
+ return normalize(modulePath);
} else if (host.exists(modulePath + '.ts')) {
- return normalizePath(modulePath + '.ts');
+ return normalize(modulePath + '.ts');
} else if (host.exists(modulePath + '.module.ts')) {
- return normalizePath(modulePath + '.module.ts');
+ return normalize(modulePath + '.module.ts');
} else if (host.exists(modulePath + '/' + moduleBaseName + '.module.ts')) {
- return normalizePath(modulePath + '/' + moduleBaseName + '.module.ts');
+ return normalize(modulePath + '/' + moduleBaseName + '.module.ts');
} else {
throw new Error('Specified module does not exist');
}
@@ -56,8 +57,8 @@ export function findModuleFromOptions(host: Tree,
/**
* Function to find the "closest" module to a generated file's path.
*/
-export function findModule(host: Tree, generateDir: string): SchematicPath {
- let closestModule: string = normalizePath(generateDir.replace(/[\\/]$/, ''));
+export function findModule(host: Tree, generateDir: string): Path {
+ let closestModule: string = normalize(generateDir.replace(/[\\/]$/, ''));
const allFiles = host.files;
let modulePath: string | null = null;
@@ -65,7 +66,7 @@ export function findModule(host: Tree, generateDir: string): SchematicPath {
const routingModuleRe = /-routing\.module\.ts/;
while (closestModule) {
- const normalizedRoot = normalizePath(closestModule);
+ const normalizedRoot = normalize(closestModule);
const matches = allFiles
.filter(p => moduleRe.test(p) &&
!routingModuleRe.test(p) &&
@@ -86,15 +87,15 @@ export function findModule(host: Tree, generateDir: string): SchematicPath {
+ 'option to skip importing components in NgModule.');
}
- return normalizePath(modulePath);
+ return normalize(modulePath);
}
/**
* Build a relative path from one file path to another file path.
*/
export function buildRelativePath(from: string, to: string): string {
- from = normalizePath(from);
- to = normalizePath(to);
+ from = normalize(from);
+ to = normalize(to);
// Convert to arrays.
const fromParts = from.split('/');
@@ -104,19 +105,18 @@ export function buildRelativePath(from: string, to: string): string {
fromParts.pop();
const toFileName = toParts.pop();
- const relative = relativePath(normalizePath(fromParts.join('/')),
- normalizePath(toParts.join('/')));
+ const relativePath = relative(normalize(fromParts.join('/')), normalize(toParts.join('/')));
let pathPrefix = '';
// Set the path prefix for same dir or child dir, parent dir starts with `..`
- if (!relative) {
+ if (!relativePath) {
pathPrefix = '.';
- } else if (!relative.startsWith('.')) {
+ } else if (!relativePath.startsWith('.')) {
pathPrefix = `./`;
}
if (pathPrefix && !pathPrefix.endsWith('/')) {
pathPrefix += '/';
}
- return pathPrefix + (relative ? relative + '/' : '') + toFileName;
+ return pathPrefix + (relativePath ? relativePath + '/' : '') + toFileName;
}
From 97ade512b1f246b5c368d50314c9bf2a89808ca7 Mon Sep 17 00:00:00 2001
From: Hans Larsen
Date: Thu, 14 Sep 2017 21:50:34 -0700
Subject: [PATCH 0065/1016] refactor: remove null from typings for descriptions
---
packages/angular_devkit/schematics/BUILD | 1 +
.../schematics/src/engine/engine.ts | 7 +-
.../schematics/src/engine/interface.ts | 4 +-
.../tools/file-system-engine-host-base.ts | 106 +++++++++++-------
.../tools/file-system-engine-host.ts | 28 +++--
.../tools/node-module-engine-host.ts | 26 ++---
.../schematics/tools/registry-engine-host.ts | 27 +++--
7 files changed, 116 insertions(+), 83 deletions(-)
diff --git a/packages/angular_devkit/schematics/BUILD b/packages/angular_devkit/schematics/BUILD
index 8b9fbde25c..fd007e59dd 100644
--- a/packages/angular_devkit/schematics/BUILD
+++ b/packages/angular_devkit/schematics/BUILD
@@ -14,6 +14,7 @@ ts_library(
exclude = ["src/**/*_spec.ts", "src/**/*_benchmark.ts"],
),
deps = [
+ "//packages/angular_devkit/core",
# @deps: rxjs
],
tsconfig = "//:tsconfig.json",
diff --git a/packages/angular_devkit/schematics/src/engine/engine.ts b/packages/angular_devkit/schematics/src/engine/engine.ts
index d04100d057..1104f9be28 100644
--- a/packages/angular_devkit/schematics/src/engine/engine.ts
+++ b/packages/angular_devkit/schematics/src/engine/engine.ts
@@ -6,6 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import { BaseException } from '@angular-devkit/core';
+import { CollectionDescription } from '@angular-devkit/schematics';
import 'rxjs/add/operator/map';
import { Url } from 'url';
import { MergeStrategy } from '../tree/interface';
@@ -30,8 +31,8 @@ export class UnknownCollectionException extends BaseException {
constructor(name: string) { super(`Unknown collection "${name}".`); }
}
export class UnknownSchematicException extends BaseException {
- constructor(name: string, collection: Collection<{}, {}>) {
- super(`Schematic "${name}" not found in collection "${collection.description.name}".`);
+ constructor(name: string, collection: CollectionDescription<{}>) {
+ super(`Schematic "${name}" not found in collection "${collection.name}".`);
}
}
@@ -83,7 +84,7 @@ export class SchematicEngine {
- createCollectionDescription(name: string): CollectionDescription | null;
+ createCollectionDescription(name: string): CollectionDescription;
createSchematicDescription(
name: string,
collection: CollectionDescription):
- SchematicDescription | null;
+ SchematicDescription;
getSchematicRuleFactory(
schematic: SchematicDescription,
collection: CollectionDescription): RuleFactory;
diff --git a/packages/angular_devkit/schematics/tools/file-system-engine-host-base.ts b/packages/angular_devkit/schematics/tools/file-system-engine-host-base.ts
index dd3eab2ceb..947c78bbdb 100644
--- a/packages/angular_devkit/schematics/tools/file-system-engine-host-base.ts
+++ b/packages/angular_devkit/schematics/tools/file-system-engine-host-base.ts
@@ -5,12 +5,13 @@
* 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 { JsonObject } from '@angular-devkit/core';
+import { BaseException, JsonObject } from '@angular-devkit/core';
import {
EngineHost,
FileSystemTree,
RuleFactory,
Source,
+ UnknownSchematicException,
} from '@angular-devkit/schematics';
import { dirname, isAbsolute, join, resolve } from 'path';
import { Url } from 'url';
@@ -30,27 +31,61 @@ export declare type OptionTransform
= (schematic: FileSystemSchematicDescription, options: T) => R;
+export class CollectionCannotBeResolvedException extends BaseException {
+ constructor(name: string) {
+ super(`Collection ${JSON.stringify(name)} cannot be resolved.`);
+ }
+}
+export class InvalidCollectionJsonException extends BaseException {
+ constructor(_name: string, path: string) {
+ super(`Collection JSON at path ${JSON.stringify(path)} is invalid.`);
+ }
+}
+export class SchematicMissingFactoryException extends BaseException {
+ constructor(name: string) {
+ super(`Schematic ${JSON.stringify(name)} is missing a factory.`);
+ }
+}
+export class FactoryCannotBeResolvedException extends BaseException {
+ constructor(name: string) {
+ super(`Schematic ${JSON.stringify(name)} cannot resolve the factory.`);
+ }
+}
+export class CollectionMissingSchematicsMapException extends BaseException {
+ constructor(name: string) { super(`Collection "${name}" does not have a schematics map.`); }
+}
+export class CollectionMissingFieldsException extends BaseException {
+ constructor(name: string) { super(`Collection "${name}" is missing fields.`); }
+}
+export class SchematicMissingFieldsException extends BaseException {
+ constructor(name: string) { super(`Schematic "${name}" is missing fields.`); }
+}
+export class SchematicMissingDescriptionException extends BaseException {
+ constructor(name: string) { super(`Schematics "${name}" does not have a description.`); }
+}
+
+
/**
* A EngineHost base class that uses the file system to resolve collections. This is the base of
* all other EngineHost provided by the tooling part of the Schematics library.
*/
export abstract class FileSystemEngineHostBase implements
EngineHost {
- protected abstract _resolveCollectionPath(name: string): string | null;
+ protected abstract _resolveCollectionPath(name: string): string;
protected abstract _resolveReferenceString(
name: string, parentPath: string): { ref: RuleFactory<{}>, path: string } | null;
protected abstract _transformCollectionDescription(
- name: string, desc: Partial): FileSystemCollectionDesc | null;
+ name: string, desc: Partial): FileSystemCollectionDesc;
protected abstract _transformSchematicDescription(
name: string,
collection: FileSystemCollectionDesc,
- desc: Partial): FileSystemSchematicDesc | null;
+ desc: Partial): FileSystemSchematicDesc;
private _transforms: OptionTransform