chore(lint): format tools dir

This commit is contained in:
Alex Eagle 2016-05-26 10:45:37 -07:00
parent 5936624d11
commit 9096481744
46 changed files with 633 additions and 654 deletions

View File

@ -238,17 +238,17 @@ repository, allowing many tools and editors to share our settings.
To check the formatting of your code, run To check the formatting of your code, run
gulp check-format gulp lint
Note that the continuous build on Travis runs `gulp enforce-format`. Unlike the `check-format` task, Note that the continuous build on CircleCI will fail the build if files aren't formatted according
this will actually fail the build if files aren't formatted according to the style guide. to the style guide.
Your life will be easier if you include the formatter in your standard workflow. Otherwise, you'll Your life will be easier if you include the formatter in your standard workflow. Otherwise, you'll
likely forget to check the formatting, and waste time waiting for a build on Travis that fails due likely forget to check the formatting, and waste time waiting for a build on Travis that fails due
to some whitespace difference. to some whitespace difference.
* Use `$(npm bin)/clang-format -i [file name]` to format a file (or multiple). * Use `gulp format` to format everything.
* Use `gulp enforce-format` to check if your code is `clang-format` clean. This also gives * Use `gulp lint` to check if your code is `clang-format` clean. This also gives
you a command line to format your code. you a command line to format your code.
* `clang-format` also includes a git hook, run `git clang-format` to format all files you * `clang-format` also includes a git hook, run `git clang-format` to format all files you
touched. touched.

View File

@ -11,10 +11,7 @@ export type ProjectMap = {
}; };
export type Options = { export type Options = {
projects: ProjectMap; projects: ProjectMap; noTypeChecks: boolean; generateEs6: boolean; useBundles: boolean;
noTypeChecks: boolean;
generateEs6: boolean;
useBundles: boolean;
}; };
export interface AngularBuilderOptions { export interface AngularBuilderOptions {
@ -133,14 +130,16 @@ export class AngularBuilder {
writeBuildLog(result, name); writeBuildLog(result, name);
return result; return result;
}, },
(error): any => { (error):
// the build tree is the same during rebuilds, only leaf properties of the nodes change any => {
// so let's traverse it and get updated values for input/cache/output paths // the build tree is the same during rebuilds, only leaf properties of the nodes
if (this.firstResult) { // change
writeBuildLog(this.firstResult, name); // so let's traverse it and get updated values for input/cache/output paths
} if (this.firstResult) {
throw error; writeBuildLog(this.firstResult, name);
}); }
throw error;
});
} }
} }
@ -159,16 +158,17 @@ function writeBuildLog(result: BuildResult, name: string) {
function broccoliNodeToBuildNode(broccoliNode: BroccoliNode): BuildNode { function broccoliNodeToBuildNode(broccoliNode: BroccoliNode): BuildNode {
let tree = broccoliNode.tree.newStyleTree || broccoliNode.tree; let tree = broccoliNode.tree.newStyleTree || broccoliNode.tree;
return new BuildNode(tree.description || (<any>tree.constructor).name, return new BuildNode(
tree.inputPath ? [tree.inputPath] : tree.inputPaths, tree.cachePath, tree.description || (<any>tree.constructor).name,
tree.outputPath, broccoliNode.selfTime / (1000 * 1000 * 1000), tree.inputPath ? [tree.inputPath] : tree.inputPaths, tree.cachePath, tree.outputPath,
broccoliNode.totalTime / (1000 * 1000 * 1000), broccoliNode.selfTime / (1000 * 1000 * 1000), broccoliNode.totalTime / (1000 * 1000 * 1000),
broccoliNode.subtrees.map(broccoliNodeToBuildNode)); broccoliNode.subtrees.map(broccoliNodeToBuildNode));
} }
class BuildNode { class BuildNode {
constructor(public pluginName: string, public inputPaths: string[], public cachePath: string, constructor(
public outputPath: string, public selfTime: number, public totalTime: number, public pluginName: string, public inputPaths: string[], public cachePath: string,
public inputNodes: BuildNode[]) {} public outputPath: string, public selfTime: number, public totalTime: number,
public inputNodes: BuildNode[]) {}
} }

View File

@ -9,16 +9,16 @@ import {wrapDiffingPlugin, DiffingBroccoliPlugin, DiffResult} from './diffing-br
* This guarantees that platform-independent modules remain platoform-independent. * This guarantees that platform-independent modules remain platoform-independent.
*/ */
class CheckImports implements DiffingBroccoliPlugin { class CheckImports implements DiffingBroccoliPlugin {
static IMPORT_DECL_REGEXP = new RegExp(`^import[^;]+;`, "mg"); static IMPORT_DECL_REGEXP = new RegExp(`^import[^;]+;`, 'mg');
static IMPORT_PATH_REGEXP = new RegExp(`['"]([^'"]+)+['"]`, "m"); static IMPORT_PATH_REGEXP = new RegExp(`['"]([^'"]+)+['"]`, 'm');
static ALLOWED_IMPORTS: {[s: string]: string[]} = { static ALLOWED_IMPORTS: {[s: string]: string[]} = {
"angular2/src/core": ["angular2/src/facade"], 'angular2/src/core': ['angular2/src/facade'],
"angular2/src/facade": ["rxjs"], 'angular2/src/facade': ['rxjs'],
"angular2/src/common": ["angular2/core", "angular2/src/facade"], 'angular2/src/common': ['angular2/core', 'angular2/src/facade'],
"angular2/src/http": ["angular2/core", "angular2/src/facade", "rxjs"], 'angular2/src/http': ['angular2/core', 'angular2/src/facade', 'rxjs'],
"angular2/src/upgrade": 'angular2/src/upgrade':
["angular2/core", "angular2/src/facade", "angular2/platform/browser", "angular2/compiler"] ['angular2/core', 'angular2/src/facade', 'angular2/platform/browser', 'angular2/compiler']
//"angular2/src/render": [ //"angular2/src/render": [
// "angular2/animate", // "angular2/animate",
// "angular2/core", // "angular2/core",
@ -55,8 +55,8 @@ class CheckImports implements DiffingBroccoliPlugin {
private checkFilePath(filePath: string) { private checkFilePath(filePath: string) {
const sourceFilePath = path.join(this.inputPath, filePath); const sourceFilePath = path.join(this.inputPath, filePath);
if (endsWith(sourceFilePath, ".ts") && fs.existsSync(sourceFilePath)) { if (endsWith(sourceFilePath, '.ts') && fs.existsSync(sourceFilePath)) {
const content = fs.readFileSync(sourceFilePath, "UTF-8"); const content = fs.readFileSync(sourceFilePath, 'UTF-8');
const imports = content.match(CheckImports.IMPORT_DECL_REGEXP); const imports = content.match(CheckImports.IMPORT_DECL_REGEXP);
if (imports) { if (imports) {
return imports.filter(i => !this.isAllowedImport(filePath, i)) return imports.filter(i => !this.isAllowedImport(filePath, i))
@ -73,14 +73,14 @@ class CheckImports implements DiffingBroccoliPlugin {
if (!res || res.length < 2) return true; // non-es6 import if (!res || res.length < 2) return true; // non-es6 import
const importPath = res[1]; const importPath = res[1];
if (startsWith(importPath, "./") || startsWith(importPath, "../")) return true; if (startsWith(importPath, './') || startsWith(importPath, '../')) return true;
const c = CheckImports.ALLOWED_IMPORTS; const c = CheckImports.ALLOWED_IMPORTS;
for (var prop in c) { for (var prop in c) {
if (c.hasOwnProperty(prop) && startsWith(sourceFile, prop)) { if (c.hasOwnProperty(prop) && startsWith(sourceFile, prop)) {
const allowedPaths = c[prop]; const allowedPaths = c[prop];
return startsWith(importPath, prop) || return startsWith(importPath, prop) ||
allowedPaths.filter(p => startsWith(importPath, p)).length > 0; allowedPaths.filter(p => startsWith(importPath, p)).length > 0;
} }
} }
@ -88,7 +88,7 @@ class CheckImports implements DiffingBroccoliPlugin {
} }
private formatError(filePath: string, importPath: string): string { private formatError(filePath: string, importPath: string): string {
const i = importPath.replace(new RegExp(`\n`, 'g'), "\\n"); const i = importPath.replace(new RegExp(`\n`, 'g'), '\\n');
return `${filePath}: ${i}`; return `${filePath}: ${i}`;
} }
} }

View File

@ -24,7 +24,7 @@ class DartFormatter implements DiffingBroccoliPlugin {
private firstBuild: boolean = true; private firstBuild: boolean = true;
constructor(public inputPath: string, public cachePath: string, options: AngularBuilderOptions) { constructor(public inputPath: string, public cachePath: string, options: AngularBuilderOptions) {
if (!options.dartSDK) throw new Error("Missing Dart SDK"); if (!options.dartSDK) throw new Error('Missing Dart SDK');
this.DARTFMT = options.dartSDK.DARTFMT; this.DARTFMT = options.dartSDK.DARTFMT;
this.verbose = options.logs.dartfmt; this.verbose = options.logs.dartfmt;
} }
@ -34,22 +34,21 @@ class DartFormatter implements DiffingBroccoliPlugin {
let argsLength = 2; let argsLength = 2;
let argPackages: string[][] = []; let argPackages: string[][] = [];
let firstBuild = this.firstBuild; let firstBuild = this.firstBuild;
treeDiff.addedPaths.concat(treeDiff.changedPaths) treeDiff.addedPaths.concat(treeDiff.changedPaths).forEach((changedFile) => {
.forEach((changedFile) => { let sourcePath = path.join(this.inputPath, changedFile);
let sourcePath = path.join(this.inputPath, changedFile); let destPath = path.join(this.cachePath, changedFile);
let destPath = path.join(this.cachePath, changedFile); if (!firstBuild && /\.dart$/.test(changedFile)) {
if (!firstBuild && /\.dart$/.test(changedFile)) { if ((argsLength + destPath.length + 2) >= 0x2000) {
if ((argsLength + destPath.length + 2) >= 0x2000) { // Win32 command line arguments length
// Win32 command line arguments length argPackages.push(args);
argPackages.push(args); args = ['-w'];
args = ['-w']; argsLength = 2;
argsLength = 2; }
} args.push(destPath);
args.push(destPath); argsLength += destPath.length + 2;
argsLength += destPath.length + 2; }
} fse.copySync(sourcePath, destPath);
fse.copySync(sourcePath, destPath); });
});
treeDiff.removedPaths.forEach((removedFile) => { treeDiff.removedPaths.forEach((removedFile) => {
let destPath = path.join(this.cachePath, removedFile); let destPath = path.join(this.cachePath, removedFile);
fse.removeSync(destPath); fse.removeSync(destPath);
@ -60,8 +59,7 @@ class DartFormatter implements DiffingBroccoliPlugin {
} }
let execute = (args: string[]) => { let execute = (args: string[]) => {
if (args.length < 2) if (args.length < 2) return Promise.resolve();
return Promise.resolve();
return new Promise<void>((resolve, reject) => { return new Promise<void>((resolve, reject) => {
exec(this.DARTFMT + ' ' + args.join(' '), (err: Error, stdout: string, stderr: string) => { exec(this.DARTFMT + ' ' + args.join(' '), (err: Error, stdout: string, stderr: string) => {
if (this.verbose) { if (this.verbose) {

View File

@ -12,14 +12,13 @@ class DestCopy implements DiffingBroccoliPlugin {
rebuild(treeDiff: DiffResult) { rebuild(treeDiff: DiffResult) {
treeDiff.addedPaths.concat(treeDiff.changedPaths) treeDiff.addedPaths.concat(treeDiff.changedPaths).forEach((changedFilePath) => {
.forEach((changedFilePath) => { var destFilePath = path.join(this.outputRoot, changedFilePath);
var destFilePath = path.join(this.outputRoot, changedFilePath);
var destDirPath = path.dirname(destFilePath); var destDirPath = path.dirname(destFilePath);
fse.mkdirsSync(destDirPath); fse.mkdirsSync(destDirPath);
fse.copySync(path.join(this.inputPath, changedFilePath), destFilePath); fse.copySync(path.join(this.inputPath, changedFilePath), destFilePath);
}); });
treeDiff.removedPaths.forEach((removedFilePath) => { treeDiff.removedPaths.forEach((removedFilePath) => {
var destFilePath = path.join(this.outputRoot, removedFilePath); var destFilePath = path.join(this.outputRoot, removedFilePath);

View File

@ -8,10 +8,10 @@ describe('Flatten', () => {
afterEach(() => mockfs.restore()); afterEach(() => mockfs.restore());
let flatten = (inputPaths: string) => new DiffingFlatten(inputPaths, 'output', null); let flatten = (inputPaths: string) => new DiffingFlatten(inputPaths, 'output', null);
let read = (path: string) => fs.readFileSync(path, {encoding: "utf-8"}); let read = (path: string) => fs.readFileSync(path, {encoding: 'utf-8'});
let rm = (path: string) => fs.unlinkSync(path); let rm = (path: string) => fs.unlinkSync(path);
let write = let write =
(path: string, content: string) => { fs.writeFileSync(path, content, {encoding: "utf-8"}); } (path: string, content: string) => { fs.writeFileSync(path, content, {encoding: 'utf-8'}); }
it('should flatten files and be incremental', () => { it('should flatten files and be incremental', () => {
@ -67,7 +67,8 @@ describe('Flatten', () => {
let differ = new TreeDiffer('testLabel', 'input'); let differ = new TreeDiffer('testLabel', 'input');
let flattenedTree = flatten('input'); let flattenedTree = flatten('input');
expect(() => flattenedTree.rebuild(differ.diffTree())) expect(() => flattenedTree.rebuild(differ.diffTree()))
.toThrowError("Duplicate file 'file-1.txt' found in path 'dir1" + path.sep + "subdir-1" + .toThrowError(
path.sep + "file-1.txt'"); 'Duplicate file \'file-1.txt\' found in path \'dir1' + path.sep + 'subdir-1' +
path.sep + 'file-1.txt\'');
}); });
}); });

View File

@ -14,8 +14,9 @@ var isWindows = process.platform === 'win32';
* the associated changes. * the associated changes.
*/ */
export class DiffingFlatten implements DiffingBroccoliPlugin { export class DiffingFlatten implements DiffingBroccoliPlugin {
constructor(private inputPath: string, private cachePath: string, constructor(
private options: AngularBuilderOptions) {} private inputPath: string, private cachePath: string,
private options: AngularBuilderOptions) {}
rebuild(treeDiff: DiffResult) { rebuild(treeDiff: DiffResult) {
@ -39,8 +40,9 @@ export class DiffingFlatten implements DiffingBroccoliPlugin {
if (!fs.existsSync(destFilePath)) { if (!fs.existsSync(destFilePath)) {
symlinkOrCopy(sourceFilePath, destFilePath); symlinkOrCopy(sourceFilePath, destFilePath);
} else { } else {
throw new Error(`Duplicate file '${path.basename(changedFilePath)}' ` + throw new Error(
`found in path '${changedFilePath}'`); `Duplicate file '${path.basename(changedFilePath)}' ` +
`found in path '${changedFilePath}'`);
} }
}); });

View File

@ -13,23 +13,26 @@ import {wrapDiffingPlugin, DiffingBroccoliPlugin, DiffResult} from './diffing-br
class GeneratorForTest implements DiffingBroccoliPlugin { class GeneratorForTest implements DiffingBroccoliPlugin {
private seenFiles: {[key: string]: boolean} = {}; private seenFiles: {[key: string]: boolean} = {};
constructor(private inputPath: string, private outputPath: string, constructor(private inputPath: string, private outputPath: string, private options: {
private options: { files: string[], dartPath: string }) {} files: string[],
dartPath: string
}) {}
rebuild(treeDiff: DiffResult) { rebuild(treeDiff: DiffResult) {
var matchedFiles: string[] = []; var matchedFiles: string[] = [];
this.options.files.forEach( this.options.files.forEach(
(file) => { matchedFiles = matchedFiles.concat(glob.sync(file, {cwd: this.inputPath})); }); (file) => { matchedFiles = matchedFiles.concat(glob.sync(file, {cwd: this.inputPath})); });
return Promise.all(matchedFiles.map((matchedFile) => { return Promise
var inputFilePath = path.join(this.inputPath, matchedFile); .all(matchedFiles.map((matchedFile) => {
var outputFilePath = path.join(this.outputPath, matchedFile); var inputFilePath = path.join(this.inputPath, matchedFile);
var outputFilePath = path.join(this.outputPath, matchedFile);
var outputDirPath = path.dirname(outputFilePath); var outputDirPath = path.dirname(outputFilePath);
if (!fs.existsSync(outputDirPath)) { if (!fs.existsSync(outputDirPath)) {
fse.mkdirpSync(outputDirPath); fse.mkdirpSync(outputDirPath);
} }
return this.invokeGenerator(matchedFile, inputFilePath, outputFilePath) return this.invokeGenerator(matchedFile, inputFilePath, outputFilePath)
})) }))
.then(() => { .then(() => {
var result = new DiffResult(); var result = new DiffResult();
matchedFiles.forEach((file) => { matchedFiles.forEach((file) => {
@ -44,8 +47,8 @@ class GeneratorForTest implements DiffingBroccoliPlugin {
}); });
} }
private invokeGenerator(file: string, inputFilePath: string, private invokeGenerator(file: string, inputFilePath: string, outputFilePath: string):
outputFilePath: string): Promise<any> { Promise<any> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
var args: string[]; var args: string[];
var vmPath: string; var vmPath: string;
@ -62,15 +65,14 @@ class GeneratorForTest implements DiffingBroccoliPlugin {
} }
var stdoutStream = fs.createWriteStream(outputFilePath); var stdoutStream = fs.createWriteStream(outputFilePath);
var proc = childProcess.spawn( var proc = childProcess.spawn(vmPath, args, {
vmPath, args, stdio: ['ignore', 'pipe', 'inherit'],
{ stdio: ['ignore', 'pipe', 'inherit'], env: (<any>Object)['assign']({}, process.env, env)
env: (<any>Object)['assign']({}, process.env, env) });
});
proc.on('error', function(code: any) { proc.on('error', function(code: any) {
console.error(code); console.error(code);
reject(new Error('Failed while generating code. Please run manually: ' + vmPath + ' ' + reject(new Error(
args.join(' '))); 'Failed while generating code. Please run manually: ' + vmPath + ' ' + args.join(' ')));
}); });
proc.on('close', function() { proc.on('close', function() {
stdoutStream.close(); stdoutStream.close();

View File

@ -23,8 +23,9 @@ const kDefaultOptions: LodashRendererOptions = {
* the associated changes. * the associated changes.
*/ */
export class LodashRenderer implements DiffingBroccoliPlugin { export class LodashRenderer implements DiffingBroccoliPlugin {
constructor(private inputPath: string, private cachePath: string, constructor(
private options: LodashRendererOptions = kDefaultOptions) {} private inputPath: string, private cachePath: string,
private options: LodashRendererOptions = kDefaultOptions) {}
rebuild(treeDiff: DiffResult) { rebuild(treeDiff: DiffResult) {
let {encoding = 'utf-8', context = {}} = this.options; let {encoding = 'utf-8', context = {}} = this.options;

View File

@ -15,7 +15,7 @@ describe('MergeTrees', () => {
} }
let diffTrees = (differs: TreeDiffer[]): DiffResult[] => differs.map(tree => tree.diffTree()); let diffTrees = (differs: TreeDiffer[]): DiffResult[] => differs.map(tree => tree.diffTree());
function read(path: string) { return fs.readFileSync(path, "utf-8"); } function read(path: string) { return fs.readFileSync(path, 'utf-8'); }
it('should copy the file from the right-most inputTree with overwrite=true', () => { it('should copy the file from the right-most inputTree with overwrite=true', () => {
let testDir: any = { let testDir: any = {

View File

@ -24,8 +24,8 @@ export class MergeTrees implements DiffingBroccoliPlugin {
public options: MergeTreesOptions; public options: MergeTreesOptions;
private firstBuild: boolean = true; private firstBuild: boolean = true;
constructor(public inputPaths: string[], public cachePath: string, constructor(
options: MergeTreesOptions = {}) { public inputPaths: string[], public cachePath: string, options: MergeTreesOptions = {}) {
this.options = options || {}; this.options = options || {};
} }

View File

@ -18,37 +18,36 @@ class DiffingReplace implements DiffingBroccoliPlugin {
var patterns = this.options.patterns; var patterns = this.options.patterns;
var files = this.options.files; var files = this.options.files;
treeDiff.addedPaths.concat(treeDiff.changedPaths) treeDiff.addedPaths.concat(treeDiff.changedPaths).forEach((changedFilePath) => {
.forEach((changedFilePath) => { var sourceFilePath = path.join(this.inputPath, changedFilePath);
var sourceFilePath = path.join(this.inputPath, changedFilePath); var destFilePath = path.join(this.cachePath, changedFilePath);
var destFilePath = path.join(this.cachePath, changedFilePath); var destDirPath = path.dirname(destFilePath);
var destDirPath = path.dirname(destFilePath);
if (!fs.existsSync(destDirPath)) { if (!fs.existsSync(destDirPath)) {
fse.mkdirpSync(destDirPath); fse.mkdirpSync(destDirPath);
} }
var fileMatches = files.some((filePath: string) => minimatch(changedFilePath, filePath)); var fileMatches = files.some((filePath: string) => minimatch(changedFilePath, filePath));
if (fileMatches) { if (fileMatches) {
var content = fs.readFileSync(sourceFilePath, FILE_ENCODING); var content = fs.readFileSync(sourceFilePath, FILE_ENCODING);
patterns.forEach((pattern: any) => { patterns.forEach((pattern: any) => {
var replacement = pattern.replacement; var replacement = pattern.replacement;
if (typeof replacement === 'function') { if (typeof replacement === 'function') {
replacement = function(content: string) { replacement = function(content: string) {
return pattern.replacement(content, changedFilePath); return pattern.replacement(content, changedFilePath);
}; };
}
content = content.replace(pattern.match, replacement);
});
fs.writeFileSync(destFilePath, content, FILE_ENCODING);
} else if (!fs.existsSync(destFilePath)) {
try {
fs.symlinkSync(sourceFilePath, destFilePath);
} catch (e) {
fs.writeFileSync(destFilePath, fs.readFileSync(sourceFilePath));
}
} }
content = content.replace(pattern.match, replacement);
}); });
fs.writeFileSync(destFilePath, content, FILE_ENCODING);
} else if (!fs.existsSync(destFilePath)) {
try {
fs.symlinkSync(sourceFilePath, destFilePath);
} catch (e) {
fs.writeFileSync(destFilePath, fs.readFileSync(sourceFilePath));
}
}
});
treeDiff.removedPaths.forEach((removedFilePath) => { treeDiff.removedPaths.forEach((removedFilePath) => {
var destFilePath = path.join(this.cachePath, removedFilePath); var destFilePath = path.join(this.cachePath, removedFilePath);

View File

@ -8,8 +8,9 @@ class TSToDartTranspiler implements DiffingBroccoliPlugin {
private transpiler: any /*ts2dart.Transpiler*/; private transpiler: any /*ts2dart.Transpiler*/;
constructor(public inputPath: string, public cachePath: string, constructor(
public options: any /*ts2dart.TranspilerOptions*/) { public inputPath: string, public cachePath: string,
public options: any /*ts2dart.TranspilerOptions*/) {
options.basePath = inputPath; options.basePath = inputPath;
options.tsconfig = path.join(inputPath, options.tsconfig); options.tsconfig = path.join(inputPath, options.tsconfig);
// Workaround for https://github.com/dart-lang/dart_style/issues/493 // Workaround for https://github.com/dart-lang/dart_style/issues/493
@ -24,17 +25,16 @@ class TSToDartTranspiler implements DiffingBroccoliPlugin {
path.resolve(this.inputPath, 'angular2/typings/es6-collections/es6-collections.d.ts') path.resolve(this.inputPath, 'angular2/typings/es6-collections/es6-collections.d.ts')
]; ];
let getDartFilePath = (path: string) => path.replace(/((\.js)|(\.ts))$/i, '.dart'); let getDartFilePath = (path: string) => path.replace(/((\.js)|(\.ts))$/i, '.dart');
treeDiff.addedPaths.concat(treeDiff.changedPaths) treeDiff.addedPaths.concat(treeDiff.changedPaths).forEach((changedPath) => {
.forEach((changedPath) => { let inputFilePath = path.resolve(this.inputPath, changedPath);
let inputFilePath = path.resolve(this.inputPath, changedPath);
// Ignore files which don't need to be transpiled to Dart // Ignore files which don't need to be transpiled to Dart
let dartInputFilePath = getDartFilePath(inputFilePath); let dartInputFilePath = getDartFilePath(inputFilePath);
if (fs.existsSync(dartInputFilePath)) return; if (fs.existsSync(dartInputFilePath)) return;
// Prepare to rebuild // Prepare to rebuild
toEmit.push(path.resolve(this.inputPath, changedPath)); toEmit.push(path.resolve(this.inputPath, changedPath));
}); });
treeDiff.removedPaths.forEach((removedPath) => { treeDiff.removedPaths.forEach((removedPath) => {
let absolutePath = path.resolve(this.inputPath, removedPath); let absolutePath = path.resolve(this.inputPath, removedPath);

View File

@ -85,8 +85,8 @@ class DiffingTSCompiler implements DiffingBroccoliPlugin {
this.tsOpts.baseUrl = inputPath; this.tsOpts.baseUrl = inputPath;
this.tsOpts.outDir = this.cachePath; this.tsOpts.outDir = this.cachePath;
this.tsServiceHost = new CustomLanguageServiceHost(this.tsOpts, this.rootFilePaths, this.tsServiceHost = new CustomLanguageServiceHost(
this.fileRegistry, this.inputPath); this.tsOpts, this.rootFilePaths, this.fileRegistry, this.inputPath);
this.tsService = ts.createLanguageService(this.tsServiceHost, ts.createDocumentRegistry()); this.tsService = ts.createLanguageService(this.tsServiceHost, ts.createDocumentRegistry());
this.metadataCollector = new MetadataCollector(); this.metadataCollector = new MetadataCollector();
} }
@ -97,17 +97,16 @@ class DiffingTSCompiler implements DiffingBroccoliPlugin {
let pathsWithErrors: string[] = []; let pathsWithErrors: string[] = [];
let errorMessages: string[] = []; let errorMessages: string[] = [];
treeDiff.addedPaths.concat(treeDiff.changedPaths) treeDiff.addedPaths.concat(treeDiff.changedPaths).forEach((tsFilePath) => {
.forEach((tsFilePath) => { if (!this.fileRegistry[tsFilePath]) {
if (!this.fileRegistry[tsFilePath]) { this.fileRegistry[tsFilePath] = {version: 0};
this.fileRegistry[tsFilePath] = {version: 0}; this.rootFilePaths.push(tsFilePath);
this.rootFilePaths.push(tsFilePath); } else {
} else { this.fileRegistry[tsFilePath].version++;
this.fileRegistry[tsFilePath].version++; }
}
pathsToEmit.push(path.join(this.inputPath, tsFilePath)); pathsToEmit.push(path.join(this.inputPath, tsFilePath));
}); });
treeDiff.removedPaths.forEach((tsFilePath) => { treeDiff.removedPaths.forEach((tsFilePath) => {
console.log('removing outputs for', tsFilePath); console.log('removing outputs for', tsFilePath);
@ -181,7 +180,7 @@ class DiffingTSCompiler implements DiffingBroccoliPlugin {
let errors: string[] = []; let errors: string[] = [];
allDiagnostics.forEach(diagnostic => { allDiagnostics.forEach(diagnostic => {
let message = ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n"); let message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
if (diagnostic.file) { if (diagnostic.file) {
let {line, character} = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start); let {line, character} = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
errors.push(` ${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`); errors.push(` ${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`);
@ -257,8 +256,8 @@ class DiffingTSCompiler implements DiffingBroccoliPlugin {
* Emit a .metadata.json file to correspond to the .d.ts file if the module contains classes that * Emit a .metadata.json file to correspond to the .d.ts file if the module contains classes that
* use decorators or exported constants. * use decorators or exported constants.
*/ */
private emitMetadata(dtsFileName: string, sourceFile: ts.SourceFile, private emitMetadata(
typeChecker: ts.TypeChecker) { dtsFileName: string, sourceFile: ts.SourceFile, typeChecker: ts.TypeChecker) {
if (sourceFile) { if (sourceFile) {
const metadata = this.metadataCollector.getMetadata(sourceFile, typeChecker); const metadata = this.metadataCollector.getMetadata(sourceFile, typeChecker);
if (metadata && metadata.metadata) { if (metadata && metadata.metadata) {
@ -281,16 +280,16 @@ class DiffingTSCompiler implements DiffingBroccoliPlugin {
*/ */
private fixSourceMapSources(content: string): string { private fixSourceMapSources(content: string): string {
try { try {
const marker = "//# sourceMappingURL=data:application/json;base64,"; const marker = '//# sourceMappingURL=data:application/json;base64,';
const index = content.indexOf(marker); const index = content.indexOf(marker);
if (index == -1) return content; if (index == -1) return content;
const base = content.substring(0, index + marker.length); const base = content.substring(0, index + marker.length);
const sourceMapBit = const sourceMapBit =
new Buffer(content.substring(index + marker.length), 'base64').toString("utf8"); new Buffer(content.substring(index + marker.length), 'base64').toString('utf8');
const sourceMaps = JSON.parse(sourceMapBit); const sourceMaps = JSON.parse(sourceMapBit);
const source = sourceMaps.sources[0]; const source = sourceMaps.sources[0];
sourceMaps.sources = [source.substring(source.lastIndexOf("../") + 3)]; sourceMaps.sources = [source.substring(source.lastIndexOf('../') + 3)];
return `${base}${new Buffer(JSON.stringify(sourceMaps)).toString('base64')}`; return `${base}${new Buffer(JSON.stringify(sourceMaps)).toString('base64')}`;
} catch (e) { } catch (e) {
return content; return content;
@ -319,8 +318,9 @@ class CustomLanguageServiceHost implements ts.LanguageServiceHost {
private defaultLibFilePath: string; private defaultLibFilePath: string;
constructor(private compilerOptions: ts.CompilerOptions, private fileNames: string[], constructor(
private fileRegistry: FileRegistry, private treeInputPath: string) { private compilerOptions: ts.CompilerOptions, private fileNames: string[],
private fileRegistry: FileRegistry, private treeInputPath: string) {
this.currentDirectory = process.cwd(); this.currentDirectory = process.cwd();
this.defaultLibFilePath = ts.getDefaultLibFilePath(compilerOptions).replace(/\\/g, '/'); this.defaultLibFilePath = ts.getDefaultLibFilePath(compilerOptions).replace(/\\/g, '/');
} }

View File

@ -1,4 +1,4 @@
declare module "broccoli-writer" { declare module 'broccoli-writer' {
class Writer { class Writer {
write(readTree: (tree: BroccoliTree) => Promise<string>, destDir: string): Promise<any>; write(readTree: (tree: BroccoliTree) => Promise<string>, destDir: string): Promise<any>;
} }

View File

@ -63,13 +63,13 @@ interface BroccoliTree {
*/ */
description?: string; description?: string;
rebuild(): (Promise<any>| void); rebuild(): (Promise<any>|void);
cleanup(): void; cleanup(): void;
} }
interface OldBroccoliTree { interface OldBroccoliTree {
read?(readTree: (tree: BroccoliTree) => Promise<string>): (Promise<string>| string); read?(readTree: (tree: BroccoliTree) => Promise<string>): (Promise<string>|string);
} }

View File

@ -23,13 +23,13 @@ export function wrapDiffingPlugin(pluginClass: PluginClass): DiffingPluginWrappe
export interface DiffingBroccoliPlugin { export interface DiffingBroccoliPlugin {
rebuild(diff: (DiffResult | DiffResult[])): (Promise<DiffResult | void>| DiffResult | void); rebuild(diff: (DiffResult|DiffResult[])): (Promise<DiffResult|void>|DiffResult|void);
cleanup ? () : void; cleanup?(): void;
} }
export type DiffingPluginWrapperFactory = export type DiffingPluginWrapperFactory =
(inputTrees: (BroccoliTree | BroccoliTree[]), options?: any) => BroccoliTree; (inputTrees: (BroccoliTree | BroccoliTree[]), options?: any) => BroccoliTree;
class DiffingPluginWrapper implements BroccoliTree { class DiffingPluginWrapper implements BroccoliTree {
@ -59,7 +59,7 @@ class DiffingPluginWrapper implements BroccoliTree {
this.description = this.pluginClass.name; this.description = this.pluginClass.name;
} }
private getDiffResult(): (DiffResult | DiffResult[]) { private getDiffResult(): (DiffResult|DiffResult[]) {
let returnOrCalculateDiffResult = (tree: BroccoliTree, index: number) => { let returnOrCalculateDiffResult = (tree: BroccoliTree, index: number) => {
// returnOrCalculateDiffResult will do one of two things: // returnOrCalculateDiffResult will do one of two things:
// //
@ -80,16 +80,16 @@ class DiffingPluginWrapper implements BroccoliTree {
} else if (this.inputTree) { } else if (this.inputTree) {
return returnOrCalculateDiffResult(this.inputTree, -1); return returnOrCalculateDiffResult(this.inputTree, -1);
} else { } else {
throw new Error("Missing TreeDiffer"); throw new Error('Missing TreeDiffer');
} }
} }
private maybeStoreDiffResult(value: (DiffResult | void)) { private maybeStoreDiffResult(value: (DiffResult|void)) {
if (!(value instanceof DiffResult)) value = null; if (!(value instanceof DiffResult)) value = null;
this.diffResult = <DiffResult>(value); this.diffResult = <DiffResult>(value);
} }
rebuild(): (Promise<any>| void) { rebuild(): (Promise<any>|void) {
try { try {
let firstRun = !this.initialized; let firstRun = !this.initialized;
this.init(); this.init();
@ -99,7 +99,7 @@ class DiffingPluginWrapper implements BroccoliTree {
let result = this.wrappedPlugin.rebuild(diffResult); let result = this.wrappedPlugin.rebuild(diffResult);
if (result) { if (result) {
let resultPromise = <Promise<DiffResult | void>>(result); let resultPromise = <Promise<DiffResult|void>>(result);
if (resultPromise.then) { if (resultPromise.then) {
// rebuild() -> Promise<> // rebuild() -> Promise<>
return resultPromise.then((result: (DiffResult | void)) => { return resultPromise.then((result: (DiffResult | void)) => {
@ -139,15 +139,15 @@ class DiffingPluginWrapper implements BroccoliTree {
let description = this.description; let description = this.description;
this.initialized = true; this.initialized = true;
if (this.inputPaths) { if (this.inputPaths) {
this.treeDiffers = this.treeDiffers = this.inputPaths.map(
this.inputPaths.map((inputPath) => new TreeDiffer( (inputPath) =>
description, inputPath, includeExtensions, excludeExtensions)); new TreeDiffer(description, inputPath, includeExtensions, excludeExtensions));
} else if (this.inputPath) { } else if (this.inputPath) {
this.treeDiffer = this.treeDiffer =
new TreeDiffer(description, this.inputPath, includeExtensions, excludeExtensions); new TreeDiffer(description, this.inputPath, includeExtensions, excludeExtensions);
} }
this.wrappedPlugin = new this.pluginClass(this.inputPaths || this.inputPath, this.cachePath, this.wrappedPlugin = new this.pluginClass(
this.wrappedPluginArguments[1]); this.inputPaths || this.inputPath, this.cachePath, this.wrappedPluginArguments[1]);
} }
} }

View File

@ -3,9 +3,9 @@ var path = require('path');
module.exports = read; module.exports = read;
function read(file: string) { function read(file: string) {
var content = fs.readFileSync(path.join('tools/broccoli/html-replace', file + '.html'), var content = fs.readFileSync(
{encoding: 'utf-8'}); path.join('tools/broccoli/html-replace', file + '.html'), {encoding: 'utf-8'});
// TODO(broccoli): we don't really need this, it's here to make the output match the // TODO(broccoli): we don't really need this, it's here to make the output match the
// tools/build/html // tools/build/html
return content.substring(0, content.lastIndexOf("\n")); return content.substring(0, content.lastIndexOf('\n'));
} }

View File

@ -7,5 +7,5 @@ function readJs(file: string) {
fs.readFileSync(path.join('tools/broccoli/js-replace', file + '.js'), {encoding: 'utf-8'}); fs.readFileSync(path.join('tools/broccoli/js-replace', file + '.js'), {encoding: 'utf-8'});
// TODO(broccoli): we don't really need this, it's here to make the output match the // TODO(broccoli): we don't really need this, it's here to make the output match the
// tools/build/html // tools/build/html
return content.substring(0, content.lastIndexOf("\n")); return content.substring(0, content.lastIndexOf('\n'));
} }

View File

@ -24,24 +24,23 @@ export class MultiCopy extends Writer {
constructor(private inputTree: BroccoliTree, private options: MultiCopyOptions) { super(); } constructor(private inputTree: BroccoliTree, private options: MultiCopyOptions) { super(); }
write(readTree: (tree: BroccoliTree) => Promise<string>, destDir: string): Promise<any> { write(readTree: (tree: BroccoliTree) => Promise<string>, destDir: string): Promise<any> {
return readTree(this.inputTree) return readTree(this.inputTree).then((inputPath: string) => {
.then((inputPath: string) => { var fileName = path.basename(this.options.srcPath);
var fileName = path.basename(this.options.srcPath); var data = fs.readFileSync(path.join(inputPath, this.options.srcPath), 'utf-8');
var data = fs.readFileSync(path.join(inputPath, this.options.srcPath), 'utf-8');
this.options.targetPatterns.forEach(pattern => { this.options.targetPatterns.forEach(pattern => {
var paths: string[] = glob.sync(pattern); var paths: string[] = glob.sync(pattern);
paths = paths.filter(p => fs.statSync(p).isDirectory()); paths = paths.filter(p => fs.statSync(p).isDirectory());
if (this.options.exclude) { if (this.options.exclude) {
paths = paths.filter(p => !this.options.exclude.some((excl) => minimatch(p, excl))); paths = paths.filter(p => !this.options.exclude.some((excl) => minimatch(p, excl)));
} }
paths.forEach(p => { paths.forEach(p => {
var folder = path.join(destDir, p); var folder = path.join(destDir, p);
fsx.mkdirsSync(folder); fsx.mkdirsSync(folder);
var outputPath = path.join(folder, fileName); var outputPath = path.join(folder, fileName);
fs.writeFileSync(outputPath, data); fs.writeFileSync(outputPath, data);
});
});
}); });
});
});
} }
} }

View File

@ -28,8 +28,9 @@ describe('TreeDiffer', () => {
let diffResult = differ.diffTree(); let diffResult = differ.diffTree();
expect(diffResult.addedPaths) expect(diffResult.addedPaths).toEqual([
.toEqual(['file-1.txt', 'file-2.txt', 'subdir-1' + path.sep + 'file-1.1.txt']); 'file-1.txt', 'file-2.txt', 'subdir-1' + path.sep + 'file-1.1.txt'
]);
expect(diffResult.changedPaths).toEqual([]); expect(diffResult.changedPaths).toEqual([]);
expect(diffResult.removedPaths).toEqual([]); expect(diffResult.removedPaths).toEqual([]);
@ -81,8 +82,9 @@ describe('TreeDiffer', () => {
let diffResult = differ.diffTree(); let diffResult = differ.diffTree();
expect(diffResult.addedPaths) expect(diffResult.addedPaths).toEqual([
.toEqual(['file-1.txt', 'file-2.txt', 'subdir-1' + path.sep + 'file-1.1.txt']); 'file-1.txt', 'file-2.txt', 'subdir-1' + path.sep + 'file-1.1.txt'
]);
// change two files // change two files
testDir['dir1']['file-1.txt'] = mockfs.file({content: 'new content', mtime: new Date(1000)}); testDir['dir1']['file-1.txt'] = mockfs.file({content: 'new content', mtime: new Date(1000)});
@ -92,8 +94,9 @@ describe('TreeDiffer', () => {
diffResult = differ.diffTree(); diffResult = differ.diffTree();
expect(diffResult.changedPaths) expect(diffResult.changedPaths).toEqual([
.toEqual(['file-1.txt', 'subdir-1' + path.sep + 'file-1.1.txt']); 'file-1.txt', 'subdir-1' + path.sep + 'file-1.1.txt'
]);
expect(diffResult.removedPaths).toEqual([]); expect(diffResult.removedPaths).toEqual([]);
@ -128,8 +131,9 @@ describe('TreeDiffer', () => {
let diffResult = differ.diffTree(); let diffResult = differ.diffTree();
expect(diffResult.addedPaths) expect(diffResult.addedPaths).toEqual([
.toEqual(['file-1.txt', 'file-2.txt', 'subdir-1' + path.sep + 'file-1.1.txt']); 'file-1.txt', 'file-2.txt', 'subdir-1' + path.sep + 'file-1.1.txt'
]);
// change two files // change two files
testDir['orig_path']['file-1.txt'] = testDir['orig_path']['file-1.txt'] =
@ -141,8 +145,9 @@ describe('TreeDiffer', () => {
diffResult = differ.diffTree(); diffResult = differ.diffTree();
expect(diffResult.addedPaths).toEqual([]); expect(diffResult.addedPaths).toEqual([]);
expect(diffResult.changedPaths) expect(diffResult.changedPaths).toEqual([
.toEqual(['file-1.txt', 'subdir-1' + path.sep + 'file-1.1.txt']); 'file-1.txt', 'subdir-1' + path.sep + 'file-1.1.txt'
]);
expect(diffResult.removedPaths).toEqual([]); expect(diffResult.removedPaths).toEqual([]);
// change one file // change one file
@ -181,14 +186,14 @@ describe('TreeDiffer', () => {
}); });
it("should throw an error if an extension isn't prefixed with doc", () => { it('should throw an error if an extension isn\'t prefixed with doc', () => {
// includeExtensions // includeExtensions
expect(() => new TreeDiffer('testLabel', 'dir1', ['js'])) expect(() => new TreeDiffer('testLabel', 'dir1', ['js']))
.toThrowError("Extension must begin with '.'. Was: 'js'"); .toThrowError('Extension must begin with \'.\'. Was: \'js\'');
// excludeExtentions // excludeExtentions
expect(() => new TreeDiffer('testLabel', 'dir1', [], ['js'])) expect(() => new TreeDiffer('testLabel', 'dir1', [], ['js']))
.toThrowError("Extension must begin with '.'. Was: 'js'"); .toThrowError('Extension must begin with \'.\'. Was: \'js\'');
}); });

View File

@ -6,7 +6,7 @@ function tryStatSync(path: string) {
try { try {
return fs.statSync(path); return fs.statSync(path);
} catch (e) { } catch (e) {
if (e.code === "ENOENT") return null; if (e.code === 'ENOENT') return null;
throw e; throw e;
} }
} }
@ -19,17 +19,18 @@ export class TreeDiffer {
private include: RegExp = null; private include: RegExp = null;
private exclude: RegExp = null; private exclude: RegExp = null;
constructor(private label: string, private rootPath: string, includeExtensions?: string[], constructor(
excludeExtensions?: string[]) { private label: string, private rootPath: string, includeExtensions?: string[],
excludeExtensions?: string[]) {
this.rootDirName = path.basename(rootPath); this.rootDirName = path.basename(rootPath);
let buildRegexp = (arr: string[]) => new RegExp(`(${arr.reduce(combine, "")})$`, "i"); let buildRegexp = (arr: string[]) => new RegExp(`(${arr.reduce(combine, "")})$`, 'i');
this.include = (includeExtensions || []).length ? buildRegexp(includeExtensions) : null; this.include = (includeExtensions || []).length ? buildRegexp(includeExtensions) : null;
this.exclude = (excludeExtensions || []).length ? buildRegexp(excludeExtensions) : null; this.exclude = (excludeExtensions || []).length ? buildRegexp(excludeExtensions) : null;
function combine(prev: string, curr: string) { function combine(prev: string, curr: string) {
if (curr.charAt(0) !== ".") { if (curr.charAt(0) !== '.') {
throw new Error(`Extension must begin with '.'. Was: '${curr}'`); throw new Error(`Extension must begin with '.'. Was: '${curr}'`);
} }
let kSpecialRegexpChars = /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g; let kSpecialRegexpChars = /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g;
@ -144,17 +145,17 @@ class DirtyCheckingDiffResult extends DiffResult {
toString() { toString() {
return `${pad(this.label, 30)}, ${pad(this.endTime - this.startTime, 5)}ms, ` + return `${pad(this.label, 30)}, ${pad(this.endTime - this.startTime, 5)}ms, ` +
`${pad(this.addedPaths.length + this.changedPaths.length + this.removedPaths.length, 5)} changes ` + `${pad(this.addedPaths.length + this.changedPaths.length + this.removedPaths.length, 5)} changes ` +
`(files: ${pad(this.filesChecked, 5)}, dirs: ${pad(this.directoriesChecked, 4)})`; `(files: ${pad(this.filesChecked, 5)}, dirs: ${pad(this.directoriesChecked, 4)})`;
} }
log(verbose: boolean) { log(verbose: boolean) {
let prefixedPaths = this.addedPaths.map(p => `+ ${p}`) let prefixedPaths = this.addedPaths.map(p => `+ ${p}`)
.concat(this.changedPaths.map(p => `* ${p}`)) .concat(this.changedPaths.map(p => `* ${p}`))
.concat(this.removedPaths.map(p => `- ${p}`)); .concat(this.removedPaths.map(p => `- ${p}`));
console.log(`Tree diff: ${this}` + ((verbose && prefixedPaths.length) ? console.log(
` [\n ${prefixedPaths.join('\n ')}\n]` : `Tree diff: ${this}` +
'')); ((verbose && prefixedPaths.length) ? ` [\n ${prefixedPaths.join('\n ')}\n]` : ''));
} }
} }

View File

@ -63,7 +63,7 @@ const kServedPaths = [
'playground/src/web_workers/images', 'playground/src/web_workers/images',
'playground/src/web_workers/message_broker', 'playground/src/web_workers/message_broker',
'playground/src/web_workers/router', 'playground/src/web_workers/router',
'playground/src/web_workers/input' 'playground/src/web_workers/input',
]; ];
@ -87,9 +87,9 @@ module.exports = function makeBrowserTree(options: any, destinationPath: string)
} }
if (modules.benchmarks) { if (modules.benchmarks) {
var benchmarksTree = var benchmarksTree = new Funnel(
new Funnel('modules/benchmarks', 'modules/benchmarks',
{include: ['**/**'], exclude: ['e2e_test/**'], destDir: '/benchmarks/'}); {include: ['**/**'], exclude: ['e2e_test/**'], destDir: '/benchmarks/'});
} }
if (modules.benchmarks_external) { if (modules.benchmarks_external) {
@ -99,21 +99,21 @@ module.exports = function makeBrowserTree(options: any, destinationPath: string)
} }
if (modules.payload_tests) { if (modules.payload_tests) {
var payloadTestsTree = var payloadTestsTree = new Funnel(
new Funnel('modules/payload_tests', 'modules/payload_tests',
{include: ['**/ts/**'], exclude: ['e2e_test/**'], destDir: '/payload_tests/'}); {include: ['**/ts/**'], exclude: ['e2e_test/**'], destDir: '/payload_tests/'});
} }
if (modules.playground) { if (modules.playground) {
var playgroundTree = var playgroundTree = new Funnel(
new Funnel('modules/playground', 'modules/playground',
{include: ['**/**'], exclude: ['e2e_test/**'], destDir: '/playground/'}); {include: ['**/**'], exclude: ['e2e_test/**'], destDir: '/playground/'});
} }
if (modules.benchpress) { if (modules.benchpress) {
var benchpressTree = var benchpressTree = new Funnel(
new Funnel('modules/benchpress', 'modules/benchpress',
{include: ['**/**'], exclude: ['e2e_test/**'], destDir: '/benchpress/'}); {include: ['**/**'], exclude: ['e2e_test/**'], destDir: '/benchpress/'});
} }
let externalTypings = let externalTypings =
@ -160,7 +160,7 @@ module.exports = function makeBrowserTree(options: any, destinationPath: string)
modulesTree = checkImports(modulesTree); modulesTree = checkImports(modulesTree);
modulesTree = replace(modulesTree, { modulesTree = replace(modulesTree, {
files: ["playground*/**/*.js"], files: ['playground*/**/*.js'],
patterns: [{match: /\$SCRIPTS\$/, replacement: jsReplace('SCRIPTS')}] patterns: [{match: /\$SCRIPTS\$/, replacement: jsReplace('SCRIPTS')}]
}); });
@ -170,7 +170,7 @@ module.exports = function makeBrowserTree(options: any, destinationPath: string)
'node_modules/zone.js/dist/zone.js.d.ts', 'node_modules/zone.js/dist/zone.js.d.ts',
'angular2/manual_typings/globals.d.ts', 'angular2/manual_typings/globals.d.ts',
'angular2/typings/es6-collections/es6-collections.d.ts', 'angular2/typings/es6-collections/es6-collections.d.ts',
'angular2/typings/es6-promise/es6-promise.d.ts' 'angular2/typings/es6-promise/es6-promise.d.ts',
]; ];
// Use TypeScript to transpile the *.ts files to ES5 // Use TypeScript to transpile the *.ts files to ES5
@ -195,7 +195,7 @@ module.exports = function makeBrowserTree(options: any, destinationPath: string)
'node_modules/zone.js/dist/long-stack-trace-zone.js', 'node_modules/zone.js/dist/long-stack-trace-zone.js',
'node_modules/systemjs/dist/system.src.js', 'node_modules/systemjs/dist/system.src.js',
'node_modules/base64-js/lib/b64.js', 'node_modules/base64-js/lib/b64.js',
'node_modules/reflect-metadata/Reflect.js' 'node_modules/reflect-metadata/Reflect.js',
] ]
})); }));
@ -233,10 +233,8 @@ module.exports = function makeBrowserTree(options: any, destinationPath: string)
htmlTree = replace(htmlTree, { htmlTree = replace(htmlTree, {
files: ['playground*/**/*.html'], files: ['playground*/**/*.html'],
patterns: [ patterns: [
{match: /\$SCRIPTS\$/, replacement: htmlReplace('SCRIPTS')}, {match: /\$SCRIPTS\$/, replacement: htmlReplace('SCRIPTS')}, scriptPathPatternReplacement,
scriptPathPatternReplacement, scriptFilePatternReplacement, useBundlesPatternReplacement
scriptFilePatternReplacement,
useBundlesPatternReplacement
] ]
}); });
} }
@ -246,9 +244,7 @@ module.exports = function makeBrowserTree(options: any, destinationPath: string)
files: ['benchmarks/**'], files: ['benchmarks/**'],
patterns: [ patterns: [
{match: /\$SCRIPTS\$/, replacement: htmlReplace('SCRIPTS_benchmarks')}, {match: /\$SCRIPTS\$/, replacement: htmlReplace('SCRIPTS_benchmarks')},
scriptPathPatternReplacement, scriptPathPatternReplacement, scriptFilePatternReplacement, useBundlesPatternReplacement
scriptFilePatternReplacement,
useBundlesPatternReplacement
] ]
}); });
} }
@ -258,9 +254,7 @@ module.exports = function makeBrowserTree(options: any, destinationPath: string)
files: ['benchmarks_external/**'], files: ['benchmarks_external/**'],
patterns: [ patterns: [
{match: /\$SCRIPTS\$/, replacement: htmlReplace('SCRIPTS_benchmarks_external')}, {match: /\$SCRIPTS\$/, replacement: htmlReplace('SCRIPTS_benchmarks_external')},
scriptPathPatternReplacement, scriptPathPatternReplacement, scriptFilePatternReplacement, useBundlesPatternReplacement
scriptFilePatternReplacement,
useBundlesPatternReplacement
] ]
}); });
} }
@ -270,7 +264,7 @@ module.exports = function makeBrowserTree(options: any, destinationPath: string)
// for web-worker e2e tests. // for web-worker e2e tests.
htmlTree = replace(htmlTree, { htmlTree = replace(htmlTree, {
files: ['playground*/**/web_workers/**/*.html'], files: ['playground*/**/web_workers/**/*.html'],
patterns: [{match: "/bundle/angular2.dev.js", replacement: "/bundle/web_worker/ui.dev.js"}] patterns: [{match: '/bundle/angular2.dev.js', replacement: '/bundle/web_worker/ui.dev.js'}]
}); });
} }
@ -284,7 +278,7 @@ module.exports = function makeBrowserTree(options: any, destinationPath: string)
'bower_components/polymer/polymer.html', 'bower_components/polymer/polymer.html',
'bower_components/polymer/polymer-micro.html', 'bower_components/polymer/polymer-micro.html',
'bower_components/polymer/polymer-mini.html', 'bower_components/polymer/polymer-mini.html',
'tools/build/snippets/url_params_to_form.js' 'tools/build/snippets/url_params_to_form.js',
] ]
}); });
var polymer = stew.mv(flatten(polymerFiles), 'benchmarks_external/src/tree/polymer'); var polymer = stew.mv(flatten(polymerFiles), 'benchmarks_external/src/tree/polymer');

View File

@ -26,7 +26,7 @@ var global_excludes = [
'playground/src/http/**/*', 'playground/src/http/**/*',
'playground/src/jsonp/**/*', 'playground/src/jsonp/**/*',
'playground/test/http/**/*', 'playground/test/http/**/*',
'playground/test/jsonp/**/*' 'playground/test/jsonp/**/*',
]; ];
@ -50,7 +50,7 @@ function replaceScriptTagInHtml(placeholder: string, relativePath: string): stri
} }
var scriptName = relativePath.replace(/\\/g, '/').replace(/.*\/([^/]+)\.html$/, '$1.dart'); var scriptName = relativePath.replace(/\\/g, '/').replace(/.*\/([^/]+)\.html$/, '$1.dart');
scriptTags += '<script src="' + scriptName + '" type="application/dart"></script>\n' + scriptTags += '<script src="' + scriptName + '" type="application/dart"></script>\n' +
'<script src="packages/browser/dart.js" type="text/javascript"></script>'; '<script src="packages/browser/dart.js" type="text/javascript"></script>';
return scriptTags; return scriptTags;
} }
@ -69,7 +69,7 @@ function getSourceTree(options: AngularBuilderOptions) {
'zone-ts2dart.d.ts', 'zone-ts2dart.d.ts',
'**/*.js', '**/*.js',
'**/*.ts', '**/*.ts',
'**/*.dart' '**/*.dart',
], ],
[ [
'rollup-test/**/*', 'rollup-test/**/*',
@ -115,7 +115,8 @@ function fixDartFolderLayout(sourceTree: BroccoliTree) {
{pattern: /^playground\/test\//, insertion: ''}, {pattern: /^playground\/test\//, insertion: ''},
{pattern: /^playground\//, insertion: 'web/'}, {pattern: /^playground\//, insertion: 'web/'},
{pattern: /^[^\/]*\/test\//, insertion: ''}, {pattern: /^[^\/]*\/test\//, insertion: ''},
{pattern: /^./, insertion: 'lib'}, // catch all. // catch all.
{pattern: /^./, insertion: 'lib'},
]; ];
for (var i = 0; i < replacements.length; i++) { for (var i = 0; i < replacements.length; i++) {
@ -181,18 +182,20 @@ function getDocsTree() {
'*/angular1_router', '*/angular1_router',
'*/angular2/src/http', '*/angular2/src/http',
'*/payload_tests', '*/payload_tests',
'*/upgrade' '*/upgrade',
] // Not in dart. ] // Not in dart.
}); });
licenses = stew.rename(licenses, stripModulePrefix); licenses = stew.rename(licenses, stripModulePrefix);
// Documentation. // Documentation.
// Rename *.dart.md -> *.dart. // Rename *.dart.md -> *.dart.
var mdTree = stew.rename(modulesFunnel(['**/*.dart.md']), var mdTree = stew.rename(
(relativePath: string) => relativePath.replace(/\.dart\.md$/, '.md')); modulesFunnel(['**/*.dart.md']),
(relativePath: string) => relativePath.replace(/\.dart\.md$/, '.md'));
// Copy all assets, ignore .js. and .dart. (handled above). // Copy all assets, ignore .js. and .dart. (handled above).
var docs = modulesFunnel(['**/*.md', '**/*.png', '**/*.html', '**/*.css', '**/*.scss'], var docs = modulesFunnel(
['**/*.js.md', '**/*.dart.md', 'angular1_router/**/*']); ['**/*.md', '**/*.png', '**/*.html', '**/*.css', '**/*.scss'],
['**/*.js.md', '**/*.dart.md', 'angular1_router/**/*']);
var assets = modulesFunnel(['playground/**/*.json']); var assets = modulesFunnel(['playground/**/*.json']);

View File

@ -1,8 +1,7 @@
'use strict'; 'use strict';
import destCopy from '../broccoli-dest-copy'; import destCopy from '../broccoli-dest-copy';
import compileWithTypescript, { INTERNAL_TYPINGS_PATH } import compileWithTypescript, {INTERNAL_TYPINGS_PATH} from '../broccoli-typescript';
from '../broccoli-typescript';
var Funnel = require('broccoli-funnel'); var Funnel = require('broccoli-funnel');
import mergeTrees from '../broccoli-merge-trees'; import mergeTrees from '../broccoli-merge-trees';
var path = require('path'); var path = require('path');
@ -32,7 +31,7 @@ module.exports = function makeNodeTree(projects: string[], destinationPath: stri
'angular2/upgrade.ts', 'angular2/upgrade.ts',
'angular2/platform/testing/**', 'angular2/platform/testing/**',
'angular2/manual_typings/**', 'angular2/manual_typings/**',
'angular2/typings/**' 'angular2/typings/**',
] ]
}); });
@ -41,7 +40,7 @@ module.exports = function makeNodeTree(projects: string[], destinationPath: stri
'angular2/typings/node/node.d.ts', 'angular2/typings/node/node.d.ts',
'angular2/manual_typings/globals.d.ts', 'angular2/manual_typings/globals.d.ts',
'angular2/typings/es6-collections/es6-collections.d.ts', 'angular2/typings/es6-collections/es6-collections.d.ts',
'angular2/typings/es6-promise/es6-promise.d.ts' 'angular2/typings/es6-promise/es6-promise.d.ts',
]; ];
let externalTypingsTree = new Funnel('modules', {files: externalTypings}); let externalTypingsTree = new Funnel('modules', {files: externalTypings});
@ -95,7 +94,7 @@ module.exports = function makeNodeTree(projects: string[], destinationPath: stri
'angular2/test/upgrade/**/*.ts', 'angular2/test/upgrade/**/*.ts',
'angular1_router/**', 'angular1_router/**',
'payload_tests/**' 'payload_tests/**',
] ]
}); });
@ -130,17 +129,14 @@ module.exports = function makeNodeTree(projects: string[], destinationPath: stri
// Compile generated test files against the src @internal .d.ts and the test files // Compile generated test files against the src @internal .d.ts and the test files
compiledTree = mergeTrees( compiledTree = mergeTrees(
[ [
compiledTree, compiledTree, generatedJsTestFiles,
generatedJsTestFiles,
compileTree( compileTree(
new Funnel( new Funnel(
mergeTrees([ mergeTrees([
packageTypings, packageTypings,
new Funnel('modules', new Funnel(
{include: ['angular2/manual_typings/**', 'angular2/typings/**']}), 'modules', {include: ['angular2/manual_typings/**', 'angular2/typings/**']}),
generatedTsTestFiles, generatedTsTestFiles, srcPrivateDeclarations, compiledTestTree
srcPrivateDeclarations,
compiledTestTree
]), ]),
{include: ['angular2/**', 'rxjs/**', 'zone.js/**']}), {include: ['angular2/**', 'rxjs/**', 'zone.js/**']}),
false, []) false, [])
@ -153,9 +149,9 @@ module.exports = function makeNodeTree(projects: string[], destinationPath: stri
files: ['**/*.d.ts'], files: ['**/*.d.ts'],
patterns: [ patterns: [
// all readonly keywords // all readonly keywords
{match: /^(\s*(static\s+|private\s+)*)readonly\s+/mg, replacement: "$1"}, {match: /^(\s*(static\s+|private\s+)*)readonly\s+/mg, replacement: '$1'},
// abstract properties (but not methods or classes) // abstract properties (but not methods or classes)
{match: /^(\s+)abstract\s+([^\(\n]*$)/mg, replacement: "$1$2"}, {match: /^(\s+)abstract\s+([^\(\n]*$)/mg, replacement: '$1$2'},
] ]
}); });
@ -176,17 +172,16 @@ module.exports = function makeNodeTree(projects: string[], destinationPath: stri
// Copy es6 typings so quickstart doesn't require typings install // Copy es6 typings so quickstart doesn't require typings install
let typingsTree = mergeTrees([ let typingsTree = mergeTrees([
new Funnel('modules', new Funnel('modules', {
{ include: [
include: [ 'angular2/typings/es6-collections/es6-collections.d.ts',
'angular2/typings/es6-collections/es6-collections.d.ts', 'angular2/typings/es6-promise/es6-promise.d.ts',
'angular2/typings/es6-promise/es6-promise.d.ts', ]
] }),
}), writeFile(
writeFile('angular2/typings/browser.d.ts', 'angular2/typings/browser.d.ts', '// Typings needed for compilation with --target=es5\n' +
'// Typings needed for compilation with --target=es5\n' + '///<reference path="./es6-collections/es6-collections.d.ts"/>\n' +
'///<reference path="./es6-collections/es6-collections.d.ts"/>\n' + '///<reference path="./es6-promise/es6-promise.d.ts"/>\n')
'///<reference path="./es6-promise/es6-promise.d.ts"/>\n')
]); ]);
var nodeTree = var nodeTree =
@ -201,9 +196,9 @@ module.exports = function makeNodeTree(projects: string[], destinationPath: stri
replacement: replacement:
() => () =>
`var parse5Adapter = require('angular2/src/platform/server/parse5_adapter');\r\n` + `var parse5Adapter = require('angular2/src/platform/server/parse5_adapter');\r\n` +
`parse5Adapter.Parse5DomAdapter.makeCurrent();` `parse5Adapter.Parse5DomAdapter.makeCurrent();`
}, },
{match: /$/, replacement: (_: any, relativePath: string) => "\r\n main(); \r\n"} {match: /$/, replacement: (_: any, relativePath: string) => '\r\n main(); \r\n'}
] ]
}); });
@ -215,24 +210,24 @@ module.exports = function makeNodeTree(projects: string[], destinationPath: stri
return destCopy(nodeTree, destinationPath); return destCopy(nodeTree, destinationPath);
}; };
function compileTree(tree: BroccoliTree, genInternalTypings: boolean, function compileTree(
rootFilePaths: string[] = []) { tree: BroccoliTree, genInternalTypings: boolean, rootFilePaths: string[] = []) {
return compileWithTypescript(tree, { return compileWithTypescript(tree, {
// build pipeline options // build pipeline options
"rootFilePaths": rootFilePaths, 'rootFilePaths': rootFilePaths,
"internalTypings": genInternalTypings, 'internalTypings': genInternalTypings,
// tsc options // tsc options
"emitDecoratorMetadata": true, 'emitDecoratorMetadata': true,
"experimentalDecorators": true, 'experimentalDecorators': true,
"declaration": true, 'declaration': true,
"stripInternal": true, 'stripInternal': true,
"module": "commonjs", 'module': 'commonjs',
"moduleResolution": "classic", 'moduleResolution': 'classic',
"noEmitOnError": true, 'noEmitOnError': true,
"rootDir": ".", 'rootDir': '.',
"inlineSourceMap": true, 'inlineSourceMap': true,
"inlineSources": true, 'inlineSources': true,
"target": "es5" 'target': 'es5'
}); });
} }

View File

@ -30,7 +30,7 @@ if (globsIndex < 0) {
} }
var specFiles = args.map(function(globstr: string) { return glob.sync(globstr, {cwd: toolsDir}); }) var specFiles = args.map(function(globstr: string) { return glob.sync(globstr, {cwd: toolsDir}); })
.reduce((specFiles:string[], paths: string[]) => specFiles.concat(paths), []); .reduce((specFiles: string[], paths: string[]) => specFiles.concat(paths), []);
jasmine.DEFAULT_TIMEOUT_INTERVAL = 100; jasmine.DEFAULT_TIMEOUT_INTERVAL = 100;

View File

@ -32,25 +32,26 @@ if (globsIndex < 0) {
} }
var specFiles: any = var specFiles: any =
args.map(function(globstr: string): string[] { args.map(function(globstr: string):
return glob.sync(globstr, { string[] {
cwd: distAll, return glob.sync(globstr, {
ignore: [ cwd: distAll,
// the following code and tests are not compatible with CJS/node environment ignore: [
'@angular/platform-browser/**', // the following code and tests are not compatible with CJS/node environment
'@angular/core/test/zone/**', '@angular/platform-browser/**',
'@angular/core/test/fake_async_spec.*', '@angular/core/test/zone/**',
'@angular/common/test/forms/**', '@angular/core/test/fake_async_spec.*',
'@angular/router/test/route_config/route_config_spec.*', '@angular/common/test/forms/**',
'@angular/router/test/integration/bootstrap_spec.*', '@angular/router/test/route_config/route_config_spec.*',
'@angular/integration_test/symbol_inspector/**', '@angular/router/test/integration/bootstrap_spec.*',
'@angular/upgrade/**', '@angular/integration_test/symbol_inspector/**',
'@angular/examples/**', '@angular/upgrade/**',
'angular1_router/**', '@angular/examples/**',
'payload_tests/**' 'angular1_router/**',
] 'payload_tests/**',
}); ]
}) });
})
// The security spec however works (and must work!) on the server side. // The security spec however works (and must work!) on the server side.
.concat(glob.sync('@angular/platform-browser/test/security/**/*_spec.js', {cwd: distAll})) .concat(glob.sync('@angular/platform-browser/test/security/**/*_spec.js', {cwd: distAll}))
.reduce((specFiles: string[], paths: string[]) => specFiles.concat(paths), <string[]>[]); .reduce((specFiles: string[], paths: string[]) => specFiles.concat(paths), <string[]>[]);

View File

@ -1,5 +1,6 @@
var testingPlatformServer = require('../../all/@angular/platform-server/testing/server.js'); var testingPlatformServer = require('../../all/@angular/platform-server/testing/server.js');
var testing = require('../../all/@angular/core/testing'); var testing = require('../../all/@angular/core/testing');
testing.setBaseTestProviders(testingPlatformServer.TEST_SERVER_PLATFORM_PROVIDERS, testing.setBaseTestProviders(
testingPlatformServer.TEST_SERVER_APPLICATION_PROVIDERS); testingPlatformServer.TEST_SERVER_PLATFORM_PROVIDERS,
testingPlatformServer.TEST_SERVER_APPLICATION_PROVIDERS);

View File

@ -581,7 +581,7 @@ const CORE = [
'state(stateNameExpr:string, styles:AnimationStyleMetadata):AnimationStateDeclarationMetadata', 'state(stateNameExpr:string, styles:AnimationStyleMetadata):AnimationStateDeclarationMetadata',
'style(tokens:string|{[key:string]:string|number}|Array<string|{[key:string]:string|number}>):AnimationStyleMetadata', 'style(tokens:string|{[key:string]:string|number}|Array<string|{[key:string]:string|number}>):AnimationStyleMetadata',
'transition(stateChangeExpr:string, animationData:AnimationMetadata|AnimationMetadata[]):AnimationStateTransitionMetadata', 'transition(stateChangeExpr:string, animationData:AnimationMetadata|AnimationMetadata[]):AnimationStateTransitionMetadata',
'trigger(name:string, animation:AnimationMetadata|AnimationMetadata[]):AnimationEntryMetadata' 'trigger(name:string, animation:AnimationMetadata|AnimationMetadata[]):AnimationEntryMetadata',
]; ];
const COMMON = [ const COMMON = [
@ -1320,11 +1320,11 @@ const PLATFORM_BROWSER = [
]; ];
describe('public API', () => { describe('public API', () => {
check("@angular/core", CORE); check('@angular/core', CORE);
check("@angular/common", COMMON); check('@angular/common', COMMON);
check("@angular/compiler", COMPILER); check('@angular/compiler', COMPILER);
check("@angular/upgrade", UPGRADE); check('@angular/upgrade', UPGRADE);
check("@angular/platform-browser", PLATFORM_BROWSER); check('@angular/platform-browser', PLATFORM_BROWSER);
}); });
function check(file: string, expected: string[]) { function check(file: string, expected: string[]) {
@ -1342,12 +1342,12 @@ function checkPublicApi(file: string, expected: string[]) {
console.log('================================================================='); console.log('=================================================================');
console.log('================================================================='); console.log('=================================================================');
console.log('================================================================='); console.log('=================================================================');
console.log("Missing:"); console.log('Missing:');
missing.forEach((m) => console.log(m)); missing.forEach((m) => console.log(m));
} }
if (extra.length > 0) { if (extra.length > 0) {
console.log("Extra:"); console.log('Extra:');
extra.forEach((m) => console.log(m)); extra.forEach((m) => console.log(m));
} }

View File

@ -1,14 +1,14 @@
import {spawn} from 'child_process'; import {spawn} from 'child_process';
import {TscWatch, TSC, reportError} from './tsc_watch';
import {writeFileSync, mkdirSync, existsSync} from 'fs'; import {writeFileSync, mkdirSync, existsSync} from 'fs';
import {TscWatch, TSC, reportError} from './tsc_watch';
export * from './tsc_watch'; export * from './tsc_watch';
import 'reflect-metadata'; import 'reflect-metadata';
const OFFLINE_COMPILE = [ const OFFLINE_COMPILE = [
'output/output_emitter_codegen_untyped', 'output/output_emitter_codegen_untyped', 'output/output_emitter_codegen_typed',
'output/output_emitter_codegen_typed', 'offline_compiler_codegen_untyped', 'offline_compiler_codegen_typed'
'offline_compiler_codegen_untyped',
'offline_compiler_codegen_typed'
]; ];
function processOutputEmitterCodeGen(): Promise<number> { function processOutputEmitterCodeGen(): Promise<number> {
@ -20,8 +20,9 @@ function processOutputEmitterCodeGen(): Promise<number> {
var codegen = require('../../all/@angular/compiler/test/' + file + '.js'); var codegen = require('../../all/@angular/compiler/test/' + file + '.js');
if (codegen.emit) { if (codegen.emit) {
console.log(` ${file} has changed, regenerating...`); console.log(` ${file} has changed, regenerating...`);
promises.push(Promise.resolve(codegen.emit()) promises.push(Promise.resolve(codegen.emit()).then((code) => {
.then((code) => { writeFileSync(outDir + file + '.ts', code); })); writeFileSync(outDir + file + '.ts', code);
}));
} }
}); });
if (promises.length) { if (promises.length) {
@ -33,8 +34,9 @@ function processOutputEmitterCodeGen(): Promise<number> {
var tsc = spawn(TSC, args, {stdio: 'pipe'}); var tsc = spawn(TSC, args, {stdio: 'pipe'});
tsc.stdout.on('data', (data: any) => process.stdout.write(data)); tsc.stdout.on('data', (data: any) => process.stdout.write(data));
tsc.stderr.on('data', (data: any) => process.stderr.write(data)); tsc.stderr.on('data', (data: any) => process.stderr.write(data));
tsc.on('close', tsc.on(
(code: any) => code ? reject('Tsc exited with: ' + code) : resolve(code)); 'close',
(code: any) => code ? reject('Tsc exited with: ' + code) : resolve(code));
}) })
.catch(reportError); .catch(reportError);
} else { } else {
@ -68,10 +70,7 @@ if (platform == 'node') {
onChangeCmds: [ onChangeCmds: [
processOutputEmitterCodeGen, processOutputEmitterCodeGen,
[ [
'node', 'node', 'dist/tools/cjs-jasmine', '--', '{@angular,benchpress}/**/*_spec.js',
'dist/tools/cjs-jasmine',
'--',
'{@angular,benchpress}/**/*_spec.js',
'@angular/compiler_cli/test/**/*_spec.js' '@angular/compiler_cli/test/**/*_spec.js'
] ]
] ]
@ -92,9 +91,10 @@ if (platform == 'node') {
start: 'File change detected. Starting incremental compilation...', start: 'File change detected. Starting incremental compilation...',
error: 'error', error: 'error',
complete: 'Compilation complete. Watching for file changes.', complete: 'Compilation complete. Watching for file changes.',
onChangeCmds: [ onChangeCmds: [[
['node', 'dist/tools/cjs-jasmine/index-tools', '--', '{public_api_guard,tsc-wrapped}/**/*{_,.}spec.js'] 'node', 'dist/tools/cjs-jasmine/index-tools', '--',
] '{public_api_guard,tsc-wrapped}/**/*{_,.}spec.js'
]]
}); });
} }

View File

@ -12,22 +12,20 @@ export type Command = (stdIn: any, stdErr: any) => Promise<number>;
export class TscWatch { export class TscWatch {
private tsconfig: string; private tsconfig: string;
private start: string | RegExp; private start: string|RegExp;
private error: string | RegExp; private error: string|RegExp;
private complete: string | RegExp; private complete: string|RegExp;
private onStartCmds: Array<string[] | Command>; private onStartCmds: Array<string[]|Command>;
private onChangeCmds: Array<string[] | Command>; private onChangeCmds: Array<string[]|Command>;
private state: State; private state: State;
private triggered: Promise<number> = null; private triggered: Promise<number> = null;
private runOnce: boolean = false; private runOnce: boolean = false;
constructor({tsconfig, start, error, complete, onStartCmds = null, onChangeCmds = null}: { constructor({tsconfig, start, error, complete, onStartCmds = null, onChangeCmds = null}: {
tsconfig: string, tsconfig: string,
error: string | RegExp, error: string|RegExp,
start: string, start: string,
complete: string, complete: string, onStartCmds?: Array<string[]|Command>, onChangeCmds?: Array<string[]|Command>
onStartCmds?: Array<string[] | Command>,
onChangeCmds?: Array<string[] | Command>
}) { }) {
console.log('Watching:', tsconfig, 'in', process.cwd()); console.log('Watching:', tsconfig, 'in', process.cwd());
this.tsconfig = tsconfig; this.tsconfig = tsconfig;
@ -50,9 +48,9 @@ export class TscWatch {
this.onStartCmds.forEach((cmd) => this.runCmd(cmd, null, () => null, () => null)); this.onStartCmds.forEach((cmd) => this.runCmd(cmd, null, () => null, () => null));
} }
private runCmd(argsOrCmd: string[] | Command, env?: {[k: string]: string}, private runCmd(
stdOut = pipeStdOut, stdErr = pipeStdErr): Promise<number> argsOrCmd: string[]|Command, env?: {[k: string]: string}, stdOut = pipeStdOut,
{ stdErr = pipeStdErr): Promise<number> {
if (typeof argsOrCmd == 'function') { if (typeof argsOrCmd == 'function') {
return (argsOrCmd as Command)(stdErr, stdOut); return (argsOrCmd as Command)(stdErr, stdOut);
} else if (argsOrCmd instanceof Array) { } else if (argsOrCmd instanceof Array) {
@ -105,8 +103,9 @@ export class TscWatch {
this.state = State.idle; this.state = State.idle;
} else { } else {
if (this.triggered) { if (this.triggered) {
this.triggered.then(() => this.triggerCmds(), this.triggered.then(
(e) => {console.log("Error while running commands....", e)}); () => this.triggerCmds(),
(e) => {console.log('Error while running commands....', e)});
} else { } else {
this.triggerCmds(); this.triggerCmds();
} }

View File

@ -1,17 +1,9 @@
import * as ts from 'typescript'; import * as ts from 'typescript';
import {Evaluator, ImportMetadata, ImportSpecifierMetadata} from './evaluator'; import {Evaluator, ImportMetadata, ImportSpecifierMetadata} from './evaluator';
import {ClassMetadata, ConstructorMetadata, ModuleMetadata, MemberMetadata, MetadataMap, MetadataSymbolicExpression, MetadataSymbolicReferenceExpression, MetadataValue, MethodMetadata} from './schema';
import {Symbols} from './symbols'; import {Symbols} from './symbols';
import {
ClassMetadata,
ConstructorMetadata,
ModuleMetadata,
MemberMetadata,
MetadataMap,
MetadataSymbolicExpression,
MetadataSymbolicReferenceExpression,
MetadataValue,
MethodMetadata
} from './schema';
/** /**
* Collect decorator metadata from a TypeScript module. * Collect decorator metadata from a TypeScript module.
@ -40,14 +32,13 @@ export class MetadataCollector {
switch (bindings.kind) { switch (bindings.kind) {
case ts.SyntaxKind.NamedImports: case ts.SyntaxKind.NamedImports:
const namedImports: ImportSpecifierMetadata[] = []; const namedImports: ImportSpecifierMetadata[] = [];
(<ts.NamedImports>bindings) (<ts.NamedImports>bindings).elements.forEach(i => {
.elements.forEach(i => { const namedImport = {name: i.name.text};
const namedImport = {name: i.name.text}; if (i.propertyName) {
if (i.propertyName) { (<any>namedImport)['propertyName'] = i.propertyName.text;
(<any>namedImport)['propertyName'] = i.propertyName.text; }
} namedImports.push(namedImport);
namedImports.push(namedImport); });
});
(<any>newImport)['namedImports'] = namedImports; (<any>newImport)['namedImports'] = namedImports;
break; break;
case ts.SyntaxKind.NamespaceImport: case ts.SyntaxKind.NamespaceImport:
@ -86,7 +77,7 @@ export class MetadataCollector {
} }
function classMetadataOf(classDeclaration: ts.ClassDeclaration): ClassMetadata { function classMetadataOf(classDeclaration: ts.ClassDeclaration): ClassMetadata {
let result: ClassMetadata = {__symbolic: "class"}; let result: ClassMetadata = {__symbolic: 'class'};
function getDecorators(decorators: ts.Decorator[]): MetadataSymbolicExpression[] { function getDecorators(decorators: ts.Decorator[]): MetadataSymbolicExpression[] {
if (decorators && decorators.length) if (decorators && decorators.length)
@ -114,7 +105,7 @@ export class MetadataCollector {
isConstructor = true; isConstructor = true;
// fallthrough // fallthrough
case ts.SyntaxKind.MethodDeclaration: case ts.SyntaxKind.MethodDeclaration:
const method = <ts.MethodDeclaration | ts.ConstructorDeclaration>member; const method = <ts.MethodDeclaration|ts.ConstructorDeclaration>member;
const methodDecorators = getDecorators(method.decorators); const methodDecorators = getDecorators(method.decorators);
const parameters = method.parameters; const parameters = method.parameters;
const parameterDecoratorData: MetadataSymbolicExpression[][] = []; const parameterDecoratorData: MetadataSymbolicExpression[][] = [];
@ -131,8 +122,8 @@ export class MetadataCollector {
hasParameterData = true; hasParameterData = true;
} }
} }
const data: MethodMetadata = {__symbolic: isConstructor ? "constructor" : "method"}; const data: MethodMetadata = {__symbolic: isConstructor ? 'constructor' : 'method'};
const name = isConstructor ? "__ctor__" : evaluator.nameOf(member.name); const name = isConstructor ? '__ctor__' : evaluator.nameOf(member.name);
if (methodDecorators) { if (methodDecorators) {
data.decorators = methodDecorators; data.decorators = methodDecorators;
} }
@ -150,8 +141,9 @@ export class MetadataCollector {
const property = <ts.PropertyDeclaration>member; const property = <ts.PropertyDeclaration>member;
const propertyDecorators = getDecorators(property.decorators); const propertyDecorators = getDecorators(property.decorators);
if (propertyDecorators) { if (propertyDecorators) {
recordMember(evaluator.nameOf(property.name), recordMember(
{__symbolic: 'property', decorators: propertyDecorators}); evaluator.nameOf(property.name),
{__symbolic: 'property', decorators: propertyDecorators});
} }
break; break;
} }
@ -194,6 +186,6 @@ export class MetadataCollector {
} }
} }
return metadata && {__symbolic: "module", metadata}; return metadata && {__symbolic: 'module', metadata};
} }
} }

View File

@ -1,8 +1,10 @@
import * as ts from 'typescript';
import {writeFileSync} from 'fs'; import {writeFileSync} from 'fs';
import {convertDecorators} from 'tsickle'; import {convertDecorators} from 'tsickle';
import * as ts from 'typescript';
import {MetadataCollector} from './collector'; import {MetadataCollector} from './collector';
/** /**
* Implementation of CompilerHost that forwards all methods to another instance. * Implementation of CompilerHost that forwards all methods to another instance.
* Useful for partial implementations to override only methods they care about. * Useful for partial implementations to override only methods they care about.
@ -58,9 +60,7 @@ const IGNORED_FILES = /\.ngfactory\.js$|\.css\.js$|\.css\.shim\.js$/;
export class MetadataWriterHost extends DelegatingHost { export class MetadataWriterHost extends DelegatingHost {
private metadataCollector = new MetadataCollector(); private metadataCollector = new MetadataCollector();
constructor(delegate: ts.CompilerHost, private program: ts.Program) { constructor(delegate: ts.CompilerHost, private program: ts.Program) { super(delegate); }
super(delegate);
}
private writeMetadata(emitFilePath: string, sourceFile: ts.SourceFile) { private writeMetadata(emitFilePath: string, sourceFile: ts.SourceFile) {
// TODO: replace with DTS filePath when https://github.com/Microsoft/TypeScript/pull/8412 is // TODO: replace with DTS filePath when https://github.com/Microsoft/TypeScript/pull/8412 is
@ -68,7 +68,7 @@ export class MetadataWriterHost extends DelegatingHost {
if (/*DTS*/ /\.js$/.test(emitFilePath)) { if (/*DTS*/ /\.js$/.test(emitFilePath)) {
const path = emitFilePath.replace(/*DTS*/ /\.js$/, '.metadata.json'); const path = emitFilePath.replace(/*DTS*/ /\.js$/, '.metadata.json');
const metadata = const metadata =
this.metadataCollector.getMetadata(sourceFile, this.program.getTypeChecker()); this.metadataCollector.getMetadata(sourceFile, this.program.getTypeChecker());
if (metadata && metadata.metadata) { if (metadata && metadata.metadata) {
const metadataText = JSON.stringify(metadata); const metadataText = JSON.stringify(metadata);
writeFileSync(path, metadataText, {encoding: 'utf-8'}); writeFileSync(path, metadataText, {encoding: 'utf-8'});
@ -76,29 +76,31 @@ export class MetadataWriterHost extends DelegatingHost {
} }
} }
writeFile: ts.WriteFileCallback = (fileName: string, data: string, writeByteOrderMark: boolean, writeFile: ts.WriteFileCallback =
onError?: (message: string) => void, (fileName: string, data: string, writeByteOrderMark: boolean,
sourceFiles?: ts.SourceFile[]) => { onError?: (message: string) => void, sourceFiles?: ts.SourceFile[]) => {
if (/\.d\.ts$/.test(fileName)) { if (/\.d\.ts$/.test(fileName)) {
// Let the original file be written first; this takes care of creating parent directories // Let the original file be written first; this takes care of creating parent directories
this.delegate.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles); this.delegate.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles);
// TODO: remove this early return after https://github.com/Microsoft/TypeScript/pull/8412 is // TODO: remove this early return after https://github.com/Microsoft/TypeScript/pull/8412
// released // is
return; // released
} return;
}
if (IGNORED_FILES.test(fileName)) { if (IGNORED_FILES.test(fileName)) {
return; return;
} }
if (!sourceFiles) { if (!sourceFiles) {
throw new Error('Metadata emit requires the sourceFiles are passed to WriteFileCallback. ' + throw new Error(
'Update to TypeScript ^1.9.0-dev'); 'Metadata emit requires the sourceFiles are passed to WriteFileCallback. ' +
} 'Update to TypeScript ^1.9.0-dev');
if (sourceFiles.length > 1) { }
throw new Error('Bundled emit with --out is not supported'); if (sourceFiles.length > 1) {
} throw new Error('Bundled emit with --out is not supported');
this.writeMetadata(fileName, sourceFiles[0]); }
}; this.writeMetadata(fileName, sourceFiles[0]);
};
} }

View File

@ -1,11 +1,8 @@
import * as ts from 'typescript'; import * as ts from 'typescript';
import {MetadataValue, MetadataSymbolicCallExpression, MetadataSymbolicReferenceExpression} from './schema';
import {Symbols} from './symbols'; import {Symbols} from './symbols';
import {
MetadataValue,
MetadataSymbolicCallExpression,
MetadataSymbolicReferenceExpression
} from './schema';
// TOOD: Remove when tools directory is upgraded to support es6 target // TOOD: Remove when tools directory is upgraded to support es6 target
interface Map<K, V> { interface Map<K, V> {
@ -75,8 +72,9 @@ export interface ImportMetadata {
* possible. * possible.
*/ */
export class Evaluator { export class Evaluator {
constructor(private typeChecker: ts.TypeChecker, private symbols: Symbols, constructor(
private imports: ImportMetadata[]) {} private typeChecker: ts.TypeChecker, private symbols: Symbols,
private imports: ImportMetadata[]) {}
symbolReference(symbol: ts.Symbol): MetadataSymbolicReferenceExpression { symbolReference(symbol: ts.Symbol): MetadataSymbolicReferenceExpression {
if (symbol) { if (symbol) {
@ -97,7 +95,7 @@ export class Evaluator {
} }
} }
} }
return {__symbolic: "reference", name, module}; return {__symbolic: 'reference', name, module};
} }
} }
@ -169,7 +167,7 @@ export class Evaluator {
case ts.SyntaxKind.CallExpression: case ts.SyntaxKind.CallExpression:
const callExpression = <ts.CallExpression>node; const callExpression = <ts.CallExpression>node;
// We can fold a <array>.concat(<v>). // We can fold a <array>.concat(<v>).
if (isMethodCallOf(callExpression, "concat") && callExpression.arguments.length === 1) { if (isMethodCallOf(callExpression, 'concat') && callExpression.arguments.length === 1) {
const arrayNode = (<ts.PropertyAccessExpression>callExpression.expression).expression; const arrayNode = (<ts.PropertyAccessExpression>callExpression.expression).expression;
if (this.isFoldableWorker(arrayNode, folding) && if (this.isFoldableWorker(arrayNode, folding) &&
this.isFoldableWorker(callExpression.arguments[0], folding)) { this.isFoldableWorker(callExpression.arguments[0], folding)) {
@ -182,7 +180,7 @@ export class Evaluator {
} }
// We can fold a call to CONST_EXPR // We can fold a call to CONST_EXPR
if (isCallOf(callExpression, "CONST_EXPR") && callExpression.arguments.length === 1) if (isCallOf(callExpression, 'CONST_EXPR') && callExpression.arguments.length === 1)
return this.isFoldableWorker(callExpression.arguments[0], folding); return this.isFoldableWorker(callExpression.arguments[0], folding);
return false; return false;
case ts.SyntaxKind.NoSubstitutionTemplateLiteral: case ts.SyntaxKind.NoSubstitutionTemplateLiteral:
@ -206,7 +204,7 @@ export class Evaluator {
case ts.SyntaxKind.AmpersandAmpersandToken: case ts.SyntaxKind.AmpersandAmpersandToken:
case ts.SyntaxKind.BarBarToken: case ts.SyntaxKind.BarBarToken:
return this.isFoldableWorker(binaryExpression.left, folding) && return this.isFoldableWorker(binaryExpression.left, folding) &&
this.isFoldableWorker(binaryExpression.right, folding); this.isFoldableWorker(binaryExpression.right, folding);
} }
case ts.SyntaxKind.PropertyAccessExpression: case ts.SyntaxKind.PropertyAccessExpression:
const propertyAccessExpression = <ts.PropertyAccessExpression>node; const propertyAccessExpression = <ts.PropertyAccessExpression>node;
@ -214,7 +212,7 @@ export class Evaluator {
case ts.SyntaxKind.ElementAccessExpression: case ts.SyntaxKind.ElementAccessExpression:
const elementAccessExpression = <ts.ElementAccessExpression>node; const elementAccessExpression = <ts.ElementAccessExpression>node;
return this.isFoldableWorker(elementAccessExpression.expression, folding) && return this.isFoldableWorker(elementAccessExpression.expression, folding) &&
this.isFoldableWorker(elementAccessExpression.argumentExpression, folding); this.isFoldableWorker(elementAccessExpression.argumentExpression, folding);
case ts.SyntaxKind.Identifier: case ts.SyntaxKind.Identifier:
let symbol = this.typeChecker.getSymbolAtLocation(node); let symbol = this.typeChecker.getSymbolAtLocation(node);
if (symbol.flags & ts.SymbolFlags.Alias) { if (symbol.flags & ts.SymbolFlags.Alias) {
@ -277,14 +275,14 @@ export class Evaluator {
const callExpression = <ts.CallExpression>node; const callExpression = <ts.CallExpression>node;
const args = callExpression.arguments.map(arg => this.evaluateNode(arg)); const args = callExpression.arguments.map(arg => this.evaluateNode(arg));
if (this.isFoldable(callExpression)) { if (this.isFoldable(callExpression)) {
if (isMethodCallOf(callExpression, "concat")) { if (isMethodCallOf(callExpression, 'concat')) {
const arrayValue = <MetadataValue[]>this.evaluateNode( const arrayValue = <MetadataValue[]>this.evaluateNode(
(<ts.PropertyAccessExpression>callExpression.expression).expression); (<ts.PropertyAccessExpression>callExpression.expression).expression);
return arrayValue.concat(args[0]); return arrayValue.concat(args[0]);
} }
} }
// Always fold a CONST_EXPR even if the argument is not foldable. // Always fold a CONST_EXPR even if the argument is not foldable.
if (isCallOf(callExpression, "CONST_EXPR") && callExpression.arguments.length === 1) { if (isCallOf(callExpression, 'CONST_EXPR') && callExpression.arguments.length === 1) {
return args[0]; return args[0];
} }
if (isCallOf(callExpression, 'forwardRef') && callExpression.arguments.length === 1) { if (isCallOf(callExpression, 'forwardRef') && callExpression.arguments.length === 1) {
@ -297,7 +295,7 @@ export class Evaluator {
const expression = this.evaluateNode(callExpression.expression); const expression = this.evaluateNode(callExpression.expression);
if (isDefined(expression) && args.every(isDefined)) { if (isDefined(expression) && args.every(isDefined)) {
const result: const result:
MetadataSymbolicCallExpression = {__symbolic: "call", expression: expression}; MetadataSymbolicCallExpression = {__symbolic: 'call', expression: expression};
if (args && args.length) { if (args && args.length) {
result.arguments = args; result.arguments = args;
} }
@ -309,7 +307,7 @@ export class Evaluator {
const newArgs = newExpression.arguments.map(arg => this.evaluateNode(arg)); const newArgs = newExpression.arguments.map(arg => this.evaluateNode(arg));
const newTarget = this.evaluateNode(newExpression.expression); const newTarget = this.evaluateNode(newExpression.expression);
if (isDefined(newTarget) && newArgs.every(isDefined)) { if (isDefined(newTarget) && newArgs.every(isDefined)) {
const result: MetadataSymbolicCallExpression = {__symbolic: "new", expression: newTarget}; const result: MetadataSymbolicCallExpression = {__symbolic: 'new', expression: newTarget};
if (newArgs.length) { if (newArgs.length) {
result.arguments = newArgs; result.arguments = newArgs;
} }
@ -325,7 +323,7 @@ export class Evaluator {
return this.nodeSymbolReference(propertyAccessExpression); return this.nodeSymbolReference(propertyAccessExpression);
} }
if (isDefined(expression)) { if (isDefined(expression)) {
return {__symbolic: "select", expression, member}; return {__symbolic: 'select', expression, member};
} }
break; break;
} }
@ -335,9 +333,9 @@ export class Evaluator {
const index = this.evaluateNode(elementAccessExpression.argumentExpression); const index = this.evaluateNode(elementAccessExpression.argumentExpression);
if (this.isFoldable(elementAccessExpression.expression) && if (this.isFoldable(elementAccessExpression.expression) &&
this.isFoldable(elementAccessExpression.argumentExpression)) this.isFoldable(elementAccessExpression.argumentExpression))
return (<any>expression)[<string | number>index]; return (<any>expression)[<string|number>index];
if (isDefined(expression) && isDefined(index)) { if (isDefined(expression) && isDefined(index)) {
return {__symbolic: "index", expression, index}; return {__symbolic: 'index', expression, index};
} }
break; break;
} }
@ -404,7 +402,7 @@ export class Evaluator {
default: default:
return undefined; return undefined;
} }
return {__symbolic: "pre", operator: operatorText, operand: operand }; return {__symbolic: 'pre', operator: operatorText, operand: operand};
case ts.SyntaxKind.BinaryExpression: case ts.SyntaxKind.BinaryExpression:
const binaryExpression = <ts.BinaryExpression>node; const binaryExpression = <ts.BinaryExpression>node;
const left = this.evaluateNode(binaryExpression.left); const left = this.evaluateNode(binaryExpression.left);
@ -456,7 +454,7 @@ export class Evaluator {
return <any>left % <any>right; return <any>left % <any>right;
} }
return { return {
__symbolic: "binop", __symbolic: 'binop',
operator: binaryExpression.operatorToken.getText(), operator: binaryExpression.operatorToken.getText(),
left: left, left: left,
right: right right: right

View File

@ -5,10 +5,10 @@ import {tsc, check} from './tsc';
import NgOptions from './options'; import NgOptions from './options';
import {MetadataWriterHost, TsickleHost} from './compiler_host'; import {MetadataWriterHost, TsickleHost} from './compiler_host';
export type CodegenExtension = (ngOptions: NgOptions, program: ts.Program, host: ts.CompilerHost) => Promise<any>; export type CodegenExtension = (ngOptions: NgOptions, program: ts.Program, host: ts.CompilerHost) =>
Promise<any>;
export function main(project: string, basePath?: string, export function main(project: string, basePath?: string, codegen?: CodegenExtension): Promise<any> {
codegen?: CodegenExtension): Promise<any> {
try { try {
let projectDir = project; let projectDir = project;
if (fs.lstatSync(project).isFile()) { if (fs.lstatSync(project).isFile()) {
@ -58,7 +58,7 @@ if (require.main === module) {
.then(exitCode => process.exit(exitCode)) .then(exitCode => process.exit(exitCode))
.catch(e => { .catch(e => {
console.error(e.stack); console.error(e.stack);
console.error("Compilation failed"); console.error('Compilation failed');
process.exit(1); process.exit(1);
}); });
} }

View File

@ -1,32 +1,32 @@
export interface ModuleMetadata { export interface ModuleMetadata {
__symbolic: "module"; __symbolic: 'module';
metadata: {[name: string]: (ClassMetadata | MetadataValue)}; metadata: {[name: string]: (ClassMetadata | MetadataValue)};
} }
export function isModuleMetadata(value: any): value is ModuleMetadata { export function isModuleMetadata(value: any): value is ModuleMetadata {
return value && value.__symbolic === "module"; return value && value.__symbolic === 'module';
} }
export interface ClassMetadata { export interface ClassMetadata {
__symbolic: "class"; __symbolic: 'class';
decorators?: MetadataSymbolicExpression[]; decorators?: MetadataSymbolicExpression[];
members?: MetadataMap; members?: MetadataMap;
} }
export function isClassMetadata(value: any): value is ClassMetadata { export function isClassMetadata(value: any): value is ClassMetadata {
return value && value.__symbolic === "class"; return value && value.__symbolic === 'class';
} }
export interface MetadataMap { [name: string]: MemberMetadata[]; } export interface MetadataMap { [name: string]: MemberMetadata[]; }
export interface MemberMetadata { export interface MemberMetadata {
__symbolic: "constructor" | "method" | "property"; __symbolic: 'constructor'|'method'|'property';
decorators?: MetadataSymbolicExpression[]; decorators?: MetadataSymbolicExpression[];
} }
export function isMemberMetadata(value: any): value is MemberMetadata { export function isMemberMetadata(value: any): value is MemberMetadata {
if (value) { if (value) {
switch (value.__symbolic) { switch (value.__symbolic) {
case "constructor": case 'constructor':
case "method": case 'method':
case "property": case 'property':
return true; return true;
} }
} }
@ -34,19 +34,19 @@ export function isMemberMetadata(value: any): value is MemberMetadata {
} }
export interface MethodMetadata extends MemberMetadata { export interface MethodMetadata extends MemberMetadata {
__symbolic: "constructor" | "method"; __symbolic: 'constructor'|'method';
parameterDecorators?: MetadataSymbolicExpression[][]; parameterDecorators?: MetadataSymbolicExpression[][];
} }
export function isMethodMetadata(value: any): value is MemberMetadata { export function isMethodMetadata(value: any): value is MemberMetadata {
return value && (value.__symbolic === "constructor" || value.__symbolic === "method"); return value && (value.__symbolic === 'constructor' || value.__symbolic === 'method');
} }
export interface ConstructorMetadata extends MethodMetadata { export interface ConstructorMetadata extends MethodMetadata {
__symbolic: "constructor"; __symbolic: 'constructor';
parameters?: MetadataSymbolicExpression[]; parameters?: MetadataSymbolicExpression[];
} }
export function isConstructorMetadata(value: any): value is ConstructorMetadata { export function isConstructorMetadata(value: any): value is ConstructorMetadata {
return value && value.__symbolic === "constructor"; return value && value.__symbolic === 'constructor';
} }
export type MetadataValue = export type MetadataValue =
@ -57,18 +57,18 @@ export interface MetadataObject { [name: string]: MetadataValue; }
export interface MetadataArray { [name: number]: MetadataValue; } export interface MetadataArray { [name: number]: MetadataValue; }
export interface MetadataSymbolicExpression { export interface MetadataSymbolicExpression {
__symbolic: "binary" | "call" | "index" | "new" | "pre" | "reference" | "select" __symbolic: 'binary'|'call'|'index'|'new'|'pre'|'reference'|'select'
} }
export function isMetadataSymbolicExpression(value: any): value is MetadataSymbolicExpression { export function isMetadataSymbolicExpression(value: any): value is MetadataSymbolicExpression {
if (value) { if (value) {
switch (value.__symbolic) { switch (value.__symbolic) {
case "binary": case 'binary':
case "call": case 'call':
case "index": case 'index':
case "new": case 'new':
case "pre": case 'pre':
case "reference": case 'reference':
case "select": case 'select':
return true; return true;
} }
} }
@ -76,63 +76,63 @@ export function isMetadataSymbolicExpression(value: any): value is MetadataSymbo
} }
export interface MetadataSymbolicBinaryExpression extends MetadataSymbolicExpression { export interface MetadataSymbolicBinaryExpression extends MetadataSymbolicExpression {
__symbolic: "binary"; __symbolic: 'binary';
operator: "&&" | "||" | "|" | "^" | "&" | "==" | "!=" | "===" | "!==" | "<" | ">" | "<=" | ">=" | operator: '&&'|'||'|'|'|'^'|'&'|'=='|'!='|'==='|'!=='|'<'|'>'|'<='|'>='|'instanceof'|'in'|'as'|
"instanceof" | "in" | "as" | "<<" | ">>" | ">>>" | "+" | "-" | "*" | "/" | "%" | "**"; '<<'|'>>'|'>>>'|'+'|'-'|'*'|'/'|'%'|'**';
left: MetadataValue; left: MetadataValue;
right: MetadataValue; right: MetadataValue;
} }
export function isMetadataSymbolicBinaryExpression( export function isMetadataSymbolicBinaryExpression(value: any):
value: any): value is MetadataSymbolicBinaryExpression { value is MetadataSymbolicBinaryExpression {
return value && value.__symbolic === "binary"; return value && value.__symbolic === 'binary';
} }
export interface MetadataSymbolicIndexExpression extends MetadataSymbolicExpression { export interface MetadataSymbolicIndexExpression extends MetadataSymbolicExpression {
__symbolic: "index"; __symbolic: 'index';
expression: MetadataValue; expression: MetadataValue;
index: MetadataValue; index: MetadataValue;
} }
export function isMetadataSymbolicIndexExpression( export function isMetadataSymbolicIndexExpression(value: any):
value: any): value is MetadataSymbolicIndexExpression { value is MetadataSymbolicIndexExpression {
return value && value.__symbolic === "index"; return value && value.__symbolic === 'index';
} }
export interface MetadataSymbolicCallExpression extends MetadataSymbolicExpression { export interface MetadataSymbolicCallExpression extends MetadataSymbolicExpression {
__symbolic: "call" | "new"; __symbolic: 'call'|'new';
expression: MetadataValue; expression: MetadataValue;
arguments?: MetadataValue[]; arguments?: MetadataValue[];
} }
export function isMetadataSymbolicCallExpression( export function isMetadataSymbolicCallExpression(value: any):
value: any): value is MetadataSymbolicCallExpression { value is MetadataSymbolicCallExpression {
return value && (value.__symbolic === "call" || value.__symbolic === "new"); return value && (value.__symbolic === 'call' || value.__symbolic === 'new');
} }
export interface MetadataSymbolicPrefixExpression extends MetadataSymbolicExpression { export interface MetadataSymbolicPrefixExpression extends MetadataSymbolicExpression {
__symbolic: "pre"; __symbolic: 'pre';
operator: "+" | "-" | "~" | "!"; operator: '+'|'-'|'~'|'!';
operand: MetadataValue; operand: MetadataValue;
} }
export function isMetadataSymbolicPrefixExpression( export function isMetadataSymbolicPrefixExpression(value: any):
value: any): value is MetadataSymbolicPrefixExpression { value is MetadataSymbolicPrefixExpression {
return value && value.__symbolic === "pre"; return value && value.__symbolic === 'pre';
} }
export interface MetadataSymbolicReferenceExpression extends MetadataSymbolicExpression { export interface MetadataSymbolicReferenceExpression extends MetadataSymbolicExpression {
__symbolic: "reference"; __symbolic: 'reference';
name: string; name: string;
module: string; module: string;
} }
export function isMetadataSymbolicReferenceExpression( export function isMetadataSymbolicReferenceExpression(value: any):
value: any): value is MetadataSymbolicReferenceExpression { value is MetadataSymbolicReferenceExpression {
return value && value.__symbolic === "reference"; return value && value.__symbolic === 'reference';
} }
export interface MetadataSymbolicSelectExpression extends MetadataSymbolicExpression { export interface MetadataSymbolicSelectExpression extends MetadataSymbolicExpression {
__symbolic: "select"; __symbolic: 'select';
expression: MetadataValue; expression: MetadataValue;
name: string; name: string;
} }
export function isMetadataSymbolicSelectExpression( export function isMetadataSymbolicSelectExpression(value: any):
value: any): value is MetadataSymbolicSelectExpression { value is MetadataSymbolicSelectExpression {
return value && value.__symbolic === "select"; return value && value.__symbolic === 'select';
} }

View File

@ -26,7 +26,9 @@ export class Symbols {
public has(symbol: ts.Symbol): boolean { return this.map.has(symbol.getDeclarations()[0]); } public has(symbol: ts.Symbol): boolean { return this.map.has(symbol.getDeclarations()[0]); }
public set(symbol: ts.Symbol, value: any): void { this.map.set(symbol.getDeclarations()[0], value); } public set(symbol: ts.Symbol, value: any): void {
this.map.set(symbol.getDeclarations()[0], value);
}
public get(symbol: ts.Symbol): any { return this.map.get(symbol.getDeclarations()[0]); } public get(symbol: ts.Symbol): any { return this.map.get(symbol.getDeclarations()[0]); }

View File

@ -1,5 +1,6 @@
import * as ts from 'typescript';
import * as path from 'path'; import * as path from 'path';
import * as ts from 'typescript';
import AngularCompilerOptions from './options'; import AngularCompilerOptions from './options';
import {TsickleHost} from './compiler_host'; import {TsickleHost} from './compiler_host';
@ -9,9 +10,8 @@ import {TsickleHost} from './compiler_host';
* you should implement a similar interface. * you should implement a similar interface.
*/ */
export interface CompilerInterface { export interface CompilerInterface {
readConfiguration( readConfiguration(project: string, basePath: string):
project: string, {parsed: ts.ParsedCommandLine, ngOptions: AngularCompilerOptions};
basePath: string): {parsed: ts.ParsedCommandLine, ngOptions: AngularCompilerOptions};
typeCheck(compilerHost: ts.CompilerHost, program: ts.Program): void; typeCheck(compilerHost: ts.CompilerHost, program: ts.Program): void;
emit(compilerHost: ts.CompilerHost, program: ts.Program): number; emit(compilerHost: ts.CompilerHost, program: ts.Program): number;
} }
@ -23,16 +23,17 @@ function debug(msg: string, ...o: any[]) {
} }
export function formatDiagnostics(diags: ts.Diagnostic[]): string { export function formatDiagnostics(diags: ts.Diagnostic[]): string {
return diags.map((d) => { return diags
let res = ts.DiagnosticCategory[d.category]; .map((d) => {
if (d.file) { let res = ts.DiagnosticCategory[d.category];
res += ' at ' + d.file.fileName + ':'; if (d.file) {
const {line, character} = d.file.getLineAndCharacterOfPosition(d.start); res += ' at ' + d.file.fileName + ':';
res += (line + 1) + ':' + (character + 1) + ':'; const {line, character} = d.file.getLineAndCharacterOfPosition(d.start);
} res += (line + 1) + ':' + (character + 1) + ':';
res += ' ' + ts.flattenDiagnosticMessageText(d.messageText, '\n'); }
return res; res += ' ' + ts.flattenDiagnosticMessageText(d.messageText, '\n');
}) return res;
})
.join('\n'); .join('\n');
} }
@ -55,7 +56,7 @@ export class Tsc implements CompilerInterface {
// Allow a directory containing tsconfig.json as the project value // Allow a directory containing tsconfig.json as the project value
try { try {
this.readDirectory(project); this.readDirectory(project);
project = path.join(project, "tsconfig.json"); project = path.join(project, 'tsconfig.json');
} catch (e) { } catch (e) {
// Was not a directory, continue on assuming it's a file // Was not a directory, continue on assuming it's a file
} }
@ -82,11 +83,11 @@ export class Tsc implements CompilerInterface {
// Create a new program since codegen files were created after making the old program // Create a new program since codegen files were created after making the old program
const program = const program =
ts.createProgram(this.parsed.fileNames, this.parsed.options, compilerHost, oldProgram); ts.createProgram(this.parsed.fileNames, this.parsed.options, compilerHost, oldProgram);
debug("Checking global diagnostics..."); debug('Checking global diagnostics...');
check(program.getGlobalDiagnostics()); check(program.getGlobalDiagnostics());
let diagnostics: ts.Diagnostic[] = []; let diagnostics: ts.Diagnostic[] = [];
debug("Type checking..."); debug('Type checking...');
for (let sf of program.getSourceFiles()) { for (let sf of program.getSourceFiles()) {
diagnostics.push(...ts.getPreEmitDiagnostics(program, sf)); diagnostics.push(...ts.getPreEmitDiagnostics(program, sf));
@ -97,7 +98,7 @@ export class Tsc implements CompilerInterface {
emit(compilerHost: TsickleHost, oldProgram: ts.Program): number { emit(compilerHost: TsickleHost, oldProgram: ts.Program): number {
// Create a new program since the host may be different from the old program. // Create a new program since the host may be different from the old program.
const program = ts.createProgram(this.parsed.fileNames, this.parsed.options, compilerHost); const program = ts.createProgram(this.parsed.fileNames, this.parsed.options, compilerHost);
debug("Emitting outputs..."); debug('Emitting outputs...');
const emitResult = program.emit(); const emitResult = program.emit();
let diagnostics: ts.Diagnostic[] = []; let diagnostics: ts.Diagnostic[] = [];
diagnostics.push(...emitResult.diagnostics); diagnostics.push(...emitResult.diagnostics);

View File

@ -29,22 +29,21 @@ describe('Collector', () => {
expect(metadata).toBeUndefined(); expect(metadata).toBeUndefined();
}); });
it("should be able to collect import statements", () => { it('should be able to collect import statements', () => {
const sourceFile = program.getSourceFile('app/app.component.ts'); const sourceFile = program.getSourceFile('app/app.component.ts');
expect(collector.collectImports(sourceFile)) expect(collector.collectImports(sourceFile)).toEqual([
.toEqual([ {
{ from: 'angular2/core',
from: 'angular2/core', namedImports: [{name: 'MyComponent', propertyName: 'Component'}, {name: 'OnInit'}]
namedImports: [{name: 'MyComponent', propertyName: 'Component'}, {name: 'OnInit'}] },
}, {from: 'angular2/common', namespace: 'common'},
{from: 'angular2/common', namespace: 'common'}, {from: './hero', namedImports: [{name: 'Hero'}]},
{from: './hero', namedImports: [{name: 'Hero'}]}, {from: './hero-detail.component', namedImports: [{name: 'HeroDetailComponent'}]},
{from: './hero-detail.component', namedImports: [{name: 'HeroDetailComponent'}]}, {from: './hero.service', defaultName: 'HeroService'}
{from: './hero.service', defaultName: 'HeroService'} ]);
]);
}); });
it("should be able to collect a simple component's metadata", () => { it('should be able to collect a simple component\'s metadata', () => {
const sourceFile = program.getSourceFile('app/hero-detail.component.ts'); const sourceFile = program.getSourceFile('app/hero-detail.component.ts');
const metadata = collector.getMetadata(sourceFile, typeChecker); const metadata = collector.getMetadata(sourceFile, typeChecker);
expect(metadata).toEqual({ expect(metadata).toEqual({
@ -52,14 +51,12 @@ describe('Collector', () => {
metadata: { metadata: {
HeroDetailComponent: { HeroDetailComponent: {
__symbolic: 'class', __symbolic: 'class',
decorators: [ decorators: [{
{ __symbolic: 'call',
__symbolic: 'call', expression: {__symbolic: 'reference', name: 'Component', module: 'angular2/core'},
expression: {__symbolic: 'reference', name: 'Component', module: 'angular2/core'}, arguments: [{
arguments: [ selector: 'my-hero-detail',
{ template: `
selector: 'my-hero-detail',
template: `
<div *ngIf="hero"> <div *ngIf="hero">
<h2>{{hero.name}} details!</h2> <h2>{{hero.name}} details!</h2>
<div><label>id: </label>{{hero.id}}</div> <div><label>id: </label>{{hero.id}}</div>
@ -69,30 +66,24 @@ describe('Collector', () => {
</div> </div>
</div> </div>
` `
} }]
] }],
}
],
members: { members: {
hero: [ hero: [{
{ __symbolic: 'property',
__symbolic: 'property', decorators: [{
decorators: [ __symbolic: 'call',
{ expression:
__symbolic: 'call', {__symbolic: 'reference', name: 'Input', module: 'angular2/core'}
expression: }]
{__symbolic: 'reference', name: 'Input', module: 'angular2/core'} }]
}
]
}
]
} }
} }
} }
}); });
}); });
it("should be able to get a more complicated component's metadata", () => { it('should be able to get a more complicated component\'s metadata', () => {
const sourceFile = program.getSourceFile('/app/app.component.ts'); const sourceFile = program.getSourceFile('/app/app.component.ts');
const metadata = collector.getMetadata(sourceFile, typeChecker); const metadata = collector.getMetadata(sourceFile, typeChecker);
expect(metadata).toEqual({ expect(metadata).toEqual({
@ -100,14 +91,12 @@ describe('Collector', () => {
metadata: { metadata: {
AppComponent: { AppComponent: {
__symbolic: 'class', __symbolic: 'class',
decorators: [ decorators: [{
{ __symbolic: 'call',
__symbolic: 'call', expression: {__symbolic: 'reference', name: 'Component', module: 'angular2/core'},
expression: {__symbolic: 'reference', name: 'Component', module: 'angular2/core'}, arguments: [{
arguments: [ selector: 'my-app',
{ template: `
selector: 'my-app',
template: `
<h2>My Heroes</h2> <h2>My Heroes</h2>
<ul class="heroes"> <ul class="heroes">
<li *ngFor="#hero of heroes" <li *ngFor="#hero of heroes"
@ -118,35 +107,26 @@ describe('Collector', () => {
</ul> </ul>
<my-hero-detail [hero]="selectedHero"></my-hero-detail> <my-hero-detail [hero]="selectedHero"></my-hero-detail>
`, `,
directives: [ directives: [
{ {
__symbolic: 'reference', __symbolic: 'reference',
name: 'HeroDetailComponent', name: 'HeroDetailComponent',
module: './hero-detail.component' module: './hero-detail.component'
}, },
{__symbolic: 'reference', name: 'NgFor', module: 'angular2/common'} {__symbolic: 'reference', name: 'NgFor', module: 'angular2/common'}
], ],
providers: [{__symbolic: 'reference', name: undefined, module: './hero.service'}], providers: [{__symbolic: 'reference', name: undefined, module: './hero.service'}],
pipes: [ pipes: [
{__symbolic: 'reference', name: 'LowerCasePipe', module: 'angular2/common'}, {__symbolic: 'reference', name: 'LowerCasePipe', module: 'angular2/common'},
{ {__symbolic: 'reference', name: 'UpperCasePipe', module: 'angular2/common'}
__symbolic: 'reference',
name: 'UpperCasePipe',
module: 'angular2/common'
}
]
}
] ]
} }]
], }],
members: { members: {
__ctor__: [ __ctor__: [{
{ __symbolic: 'constructor',
__symbolic: 'constructor', parameters: [{__symbolic: 'reference', name: undefined, module: './hero.service'}]
parameters: }],
[{__symbolic: 'reference', name: undefined, module: './hero.service'}]
}
],
onSelect: [{__symbolic: 'method'}], onSelect: [{__symbolic: 'method'}],
ngOnInit: [{__symbolic: 'method'}], ngOnInit: [{__symbolic: 'method'}],
getHeroes: [{__symbolic: 'method'}] getHeroes: [{__symbolic: 'method'}]
@ -163,16 +143,11 @@ describe('Collector', () => {
__symbolic: 'module', __symbolic: 'module',
metadata: { metadata: {
HEROES: [ HEROES: [
{"id": 11, "name": "Mr. Nice"}, {'id': 11, 'name': 'Mr. Nice'}, {'id': 12, 'name': 'Narco'},
{"id": 12, "name": "Narco"}, {'id': 13, 'name': 'Bombasto'}, {'id': 14, 'name': 'Celeritas'},
{"id": 13, "name": "Bombasto"}, {'id': 15, 'name': 'Magneta'}, {'id': 16, 'name': 'RubberMan'},
{"id": 14, "name": "Celeritas"}, {'id': 17, 'name': 'Dynama'}, {'id': 18, 'name': 'Dr IQ'}, {'id': 19, 'name': 'Magma'},
{"id": 15, "name": "Magneta"}, {'id': 20, 'name': 'Tornado'}
{"id": 16, "name": "RubberMan"},
{"id": 17, "name": "Dynama"},
{"id": 18, "name": "Dr IQ"},
{"id": 19, "name": "Magma"},
{"id": 20, "name": "Tornado"}
] ]
} }
}); });
@ -202,18 +177,14 @@ describe('Collector', () => {
it('should record annotations on set and get declarations', () => { it('should record annotations on set and get declarations', () => {
const propertyData = { const propertyData = {
name: [ name: [{
{ __symbolic: 'property',
__symbolic: 'property', decorators: [{
decorators: [ __symbolic: 'call',
{ expression: {__symbolic: 'reference', module: 'angular2/core', name: 'Input'},
__symbolic: 'call', arguments: ['firstName']
expression: {__symbolic: 'reference', module: 'angular2/core', name: 'Input'}, }]
arguments: ['firstName'] }]
}
]
}
]
}; };
const caseGetProp = <ClassMetadata>casesMetadata.metadata['GetProp']; const caseGetProp = <ClassMetadata>casesMetadata.metadata['GetProp'];
expect(caseGetProp.members).toEqual(propertyData); expect(caseGetProp.members).toEqual(propertyData);
@ -238,7 +209,9 @@ const FILES: Directory = {
@MyComponent({ @MyComponent({
selector: 'my-app', selector: 'my-app',
template:` + "`" + ` template:` +
'`' +
`
<h2>My Heroes</h2> <h2>My Heroes</h2>
<ul class="heroes"> <ul class="heroes">
<li *ngFor="#hero of heroes" <li *ngFor="#hero of heroes"
@ -249,7 +222,8 @@ const FILES: Directory = {
</ul> </ul>
<my-hero-detail [hero]="selectedHero"></my-hero-detail> <my-hero-detail [hero]="selectedHero"></my-hero-detail>
` + ` +
"`" + `, '`' +
`,
directives: [HeroDetailComponent, common.NgFor], directives: [HeroDetailComponent, common.NgFor],
providers: [HeroService], providers: [HeroService],
pipes: [common.LowerCasePipe, common.UpperCasePipe] pipes: [common.LowerCasePipe, common.UpperCasePipe]
@ -282,7 +256,9 @@ const FILES: Directory = {
@Component({ @Component({
selector: 'my-hero-detail', selector: 'my-hero-detail',
template: ` + "`" + ` template: ` +
'`' +
`
<div *ngIf="hero"> <div *ngIf="hero">
<h2>{{hero.name}} details!</h2> <h2>{{hero.name}} details!</h2>
<div><label>id: </label>{{hero.id}}</div> <div><label>id: </label>{{hero.id}}</div>
@ -291,7 +267,9 @@ const FILES: Directory = {
<input [(ngModel)]="hero.name" placeholder="name"/> <input [(ngModel)]="hero.name" placeholder="name"/>
</div> </div>
</div> </div>
` + "`" + `, ` +
'`' +
`,
}) })
export class HeroDetailComponent { export class HeroDetailComponent {
@Input() public hero: Hero; @Input() public hero: Hero;

View File

@ -1,9 +1,11 @@
import * as ts from 'typescript';
import * as fs from 'fs'; import * as fs from 'fs';
import {Directory, Host, expectNoDiagnostics, findVar} from './typescript.mocks'; import * as ts from 'typescript';
import {Evaluator} from '../src/evaluator'; import {Evaluator} from '../src/evaluator';
import {Symbols} from '../src/symbols'; import {Symbols} from '../src/symbols';
import {Directory, Host, expectNoDiagnostics, findVar} from './typescript.mocks';
describe('Evaluator', () => { describe('Evaluator', () => {
let host: ts.LanguageServiceHost; let host: ts.LanguageServiceHost;
let service: ts.LanguageService; let service: ts.LanguageService;
@ -76,13 +78,13 @@ describe('Evaluator', () => {
expect(evaluator.evaluateNode(findVar(expressions, 'bBAnd').initializer)).toEqual(0x11 & 0x03); expect(evaluator.evaluateNode(findVar(expressions, 'bBAnd').initializer)).toEqual(0x11 & 0x03);
expect(evaluator.evaluateNode(findVar(expressions, 'bXor').initializer)).toEqual(0x11 ^ 0x21); expect(evaluator.evaluateNode(findVar(expressions, 'bXor').initializer)).toEqual(0x11 ^ 0x21);
expect(evaluator.evaluateNode(findVar(expressions, 'bEqual').initializer)) expect(evaluator.evaluateNode(findVar(expressions, 'bEqual').initializer))
.toEqual(1 == <any>"1"); .toEqual(1 == <any>'1');
expect(evaluator.evaluateNode(findVar(expressions, 'bNotEqual').initializer)) expect(evaluator.evaluateNode(findVar(expressions, 'bNotEqual').initializer))
.toEqual(1 != <any>"1"); .toEqual(1 != <any>'1');
expect(evaluator.evaluateNode(findVar(expressions, 'bIdentical').initializer)) expect(evaluator.evaluateNode(findVar(expressions, 'bIdentical').initializer))
.toEqual(1 === <any>"1"); .toEqual(1 === <any>'1');
expect(evaluator.evaluateNode(findVar(expressions, 'bNotIdentical').initializer)) expect(evaluator.evaluateNode(findVar(expressions, 'bNotIdentical').initializer))
.toEqual(1 !== <any>"1"); .toEqual(1 !== <any>'1');
expect(evaluator.evaluateNode(findVar(expressions, 'bLessThan').initializer)).toEqual(1 < 2); expect(evaluator.evaluateNode(findVar(expressions, 'bLessThan').initializer)).toEqual(1 < 2);
expect(evaluator.evaluateNode(findVar(expressions, 'bGreaterThan').initializer)).toEqual(1 > 2); expect(evaluator.evaluateNode(findVar(expressions, 'bGreaterThan').initializer)).toEqual(1 > 2);
expect(evaluator.evaluateNode(findVar(expressions, 'bLessThanEqual').initializer)) expect(evaluator.evaluateNode(findVar(expressions, 'bLessThanEqual').initializer))
@ -100,9 +102,9 @@ describe('Evaluator', () => {
it('should report recursive references as symbolic', () => { it('should report recursive references as symbolic', () => {
var expressions = program.getSourceFile('expressions.ts'); var expressions = program.getSourceFile('expressions.ts');
expect(evaluator.evaluateNode(findVar(expressions, 'recursiveA').initializer)) expect(evaluator.evaluateNode(findVar(expressions, 'recursiveA').initializer))
.toEqual({__symbolic: "reference", name: "recursiveB", module: undefined}); .toEqual({__symbolic: 'reference', name: 'recursiveB', module: undefined});
expect(evaluator.evaluateNode(findVar(expressions, 'recursiveB').initializer)) expect(evaluator.evaluateNode(findVar(expressions, 'recursiveB').initializer))
.toEqual({__symbolic: "reference", name: "recursiveA", module: undefined}); .toEqual({__symbolic: 'reference', name: 'recursiveA', module: undefined});
}); });
it('should correctly handle special cases for CONST_EXPR', () => { it('should correctly handle special cases for CONST_EXPR', () => {
@ -121,18 +123,16 @@ describe('Evaluator', () => {
evaluator = evaluator =
new Evaluator(typeChecker, symbols, [{from: './classes', namedImports: [{name: 'Value'}]}]); new Evaluator(typeChecker, symbols, [{from: './classes', namedImports: [{name: 'Value'}]}]);
var newExpression = program.getSourceFile('newExpression.ts'); var newExpression = program.getSourceFile('newExpression.ts');
expect(evaluator.evaluateNode(findVar(newExpression, 'someValue').initializer)) expect(evaluator.evaluateNode(findVar(newExpression, 'someValue').initializer)).toEqual({
.toEqual({ __symbolic: 'new',
__symbolic: "new", expression: {__symbolic: 'reference', name: 'Value', module: './classes'},
expression: {__symbolic: "reference", name: "Value", module: "./classes"}, arguments: ['name', 12]
arguments: ["name", 12] });
}); expect(evaluator.evaluateNode(findVar(newExpression, 'complex').initializer)).toEqual({
expect(evaluator.evaluateNode(findVar(newExpression, 'complex').initializer)) __symbolic: 'new',
.toEqual({ expression: {__symbolic: 'reference', name: 'Value', module: './classes'},
__symbolic: "new", arguments: ['name', 12]
expression: {__symbolic: "reference", name: "Value", module: "./classes"}, });
arguments: ["name", 12]
});
}); });
}); });

View File

@ -3,7 +3,8 @@ import {Tsc} from '../src/tsc';
describe('options parsing', () => { describe('options parsing', () => {
const tsc = new Tsc(() => ` const tsc = new Tsc(
() => `
{ {
"angularCompilerOptions": { "angularCompilerOptions": {
"googleClosureOutput": true "googleClosureOutput": true
@ -12,13 +13,14 @@ describe('options parsing', () => {
"module": "commonjs", "module": "commonjs",
"outDir": "built" "outDir": "built"
} }
}`, () => ['tsconfig.json']); }`,
() => ['tsconfig.json']);
it('should combine all options into ngOptions', () => { it('should combine all options into ngOptions', () => {
const {parsed, ngOptions} = tsc.readConfiguration('projectDir', 'basePath'); const {parsed, ngOptions} = tsc.readConfiguration('projectDir', 'basePath');
expect(ngOptions).toEqual({ expect(ngOptions).toEqual({
genDir:'basePath', genDir: 'basePath',
googleClosureOutput: true, googleClosureOutput: true,
module: ts.ModuleKind.CommonJS, module: ts.ModuleKind.CommonJS,
outDir: 'basePath/built', outDir: 'basePath/built',

View File

@ -1,8 +1,8 @@
import * as path from 'path';
import * as fs from 'fs'; import * as fs from 'fs';
import * as path from 'path';
import * as ts from 'typescript'; import * as ts from 'typescript';
export interface Directory { [name: string]: (Directory | string); } export interface Directory { [name: string]: (Directory|string); }
export class Host implements ts.LanguageServiceHost { export class Host implements ts.LanguageServiceHost {
constructor(private directory: Directory, private scripts: string[]) {} constructor(private directory: Directory, private scripts: string[]) {}
@ -17,7 +17,7 @@ export class Host implements ts.LanguageServiceHost {
getScriptFileNames(): string[] { return this.scripts; } getScriptFileNames(): string[] { return this.scripts; }
getScriptVersion(fileName: string): string { return "1"; } getScriptVersion(fileName: string): string { return '1'; }
getScriptSnapshot(fileName: string): ts.IScriptSnapshot { getScriptSnapshot(fileName: string): ts.IScriptSnapshot {
let content = this.getFileContent(fileName); let content = this.getFileContent(fileName);
@ -33,7 +33,7 @@ export class Host implements ts.LanguageServiceHost {
if (names[names.length - 1] === 'lib.d.ts') { if (names[names.length - 1] === 'lib.d.ts') {
return fs.readFileSync(ts.getDefaultLibFilePath(this.getCompilationSettings()), 'utf8'); return fs.readFileSync(ts.getDefaultLibFilePath(this.getCompilationSettings()), 'utf8');
} }
let current: Directory | string = this.directory; let current: Directory|string = this.directory;
if (names.length && names[0] === '') names.shift(); if (names.length && names[0] === '') names.shift();
for (const name of names) { for (const name of names) {
if (!current || typeof current === 'string') return undefined; if (!current || typeof current === 'string') return undefined;
@ -44,8 +44,9 @@ export class Host implements ts.LanguageServiceHost {
} }
export class MockNode implements ts.Node { export class MockNode implements ts.Node {
constructor(public kind: ts.SyntaxKind = ts.SyntaxKind.Identifier, public flags: ts.NodeFlags = 0, constructor(
public pos: number = 0, public end: number = 0) {} public kind: ts.SyntaxKind = ts.SyntaxKind.Identifier, public flags: ts.NodeFlags = 0,
public pos: number = 0, public end: number = 0) {}
getSourceFile(): ts.SourceFile { return null; } getSourceFile(): ts.SourceFile { return null; }
getChildCount(sourceFile?: ts.SourceFile): number { return 0 } getChildCount(sourceFile?: ts.SourceFile): number { return 0 }
getChildAt(index: number, sourceFile?: ts.SourceFile): ts.Node { return null; } getChildAt(index: number, sourceFile?: ts.SourceFile): ts.Node { return null; }
@ -71,8 +72,9 @@ export class MockIdentifier extends MockNode implements ts.Identifier {
public _unaryExpressionBrand: any; public _unaryExpressionBrand: any;
public _expressionBrand: any; public _expressionBrand: any;
constructor(public name: string, kind: ts.SyntaxKind = ts.SyntaxKind.Identifier, constructor(
flags: ts.NodeFlags = 0, pos: number = 0, end: number = 0) { public name: string, kind: ts.SyntaxKind = ts.SyntaxKind.Identifier, flags: ts.NodeFlags = 0,
pos: number = 0, end: number = 0) {
super(kind, flags, pos, end); super(kind, flags, pos, end);
this.text = name; this.text = name;
} }
@ -81,31 +83,33 @@ export class MockIdentifier extends MockNode implements ts.Identifier {
export class MockVariableDeclaration extends MockNode implements ts.VariableDeclaration { export class MockVariableDeclaration extends MockNode implements ts.VariableDeclaration {
public _declarationBrand: any; public _declarationBrand: any;
constructor(public name: ts.Identifier, kind: ts.SyntaxKind = ts.SyntaxKind.VariableDeclaration, constructor(
flags: ts.NodeFlags = 0, pos: number = 0, end: number = 0) { public name: ts.Identifier, kind: ts.SyntaxKind = ts.SyntaxKind.VariableDeclaration,
flags: ts.NodeFlags = 0, pos: number = 0, end: number = 0) {
super(kind, flags, pos, end); super(kind, flags, pos, end);
} }
static of(name: string): MockVariableDeclaration { static of (name: string): MockVariableDeclaration {
return new MockVariableDeclaration(new MockIdentifier(name)); return new MockVariableDeclaration(new MockIdentifier(name));
} }
} }
export class MockSymbol implements ts.Symbol { export class MockSymbol implements ts.Symbol {
constructor(public name: string, private node: ts.Declaration = MockVariableDeclaration.of(name), constructor(
public flags: ts.SymbolFlags = 0) {} public name: string, private node: ts.Declaration = MockVariableDeclaration.of(name),
public flags: ts.SymbolFlags = 0) {}
getFlags(): ts.SymbolFlags { return this.flags; } getFlags(): ts.SymbolFlags { return this.flags; }
getName(): string { return this.name; } getName(): string { return this.name; }
getDeclarations(): ts.Declaration[] { return [this.node]; } getDeclarations(): ts.Declaration[] { return [this.node]; }
getDocumentationComment(): ts.SymbolDisplayPart[] { return []; } getDocumentationComment(): ts.SymbolDisplayPart[] { return []; }
static of(name: string): MockSymbol { return new MockSymbol(name); } static of (name: string): MockSymbol { return new MockSymbol(name); }
} }
export function expectNoDiagnostics(diagnostics: ts.Diagnostic[]) { export function expectNoDiagnostics(diagnostics: ts.Diagnostic[]) {
for (const diagnostic of diagnostics) { for (const diagnostic of diagnostics) {
let message = ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n"); let message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
let {line, character} = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start); let {line, character} = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
console.log(`${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`); console.log(`${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`);
} }
@ -131,13 +135,13 @@ export function allChildren<T>(node: ts.Node, cb: (node: ts.Node) => T): T {
} }
export function findClass(sourceFile: ts.SourceFile, name: string): ts.ClassDeclaration { export function findClass(sourceFile: ts.SourceFile, name: string): ts.ClassDeclaration {
return ts.forEachChild(sourceFile, return ts.forEachChild(
node => isClass(node) && isNamed(node.name, name) ? node : undefined); sourceFile, node => isClass(node) && isNamed(node.name, name) ? node : undefined);
} }
export function findVar(sourceFile: ts.SourceFile, name: string): ts.VariableDeclaration { export function findVar(sourceFile: ts.SourceFile, name: string): ts.VariableDeclaration {
return allChildren(sourceFile, return allChildren(
node => isVar(node) && isNamed(node.name, name) ? node : undefined); sourceFile, node => isVar(node) && isNamed(node.name, name) ? node : undefined);
} }
export function isClass(node: ts.Node): node is ts.ClassDeclaration { export function isClass(node: ts.Node): node is ts.ClassDeclaration {

View File

@ -1,10 +1,10 @@
import {RuleWalker} from 'tslint/lib/language/walker';
import {RuleFailure} from 'tslint/lib/lint'; import {RuleFailure} from 'tslint/lib/lint';
import {AbstractRule} from 'tslint/lib/rules'; import {AbstractRule} from 'tslint/lib/rules';
import {RuleWalker} from 'tslint/lib/language/walker';
import * as ts from 'typescript'; import * as ts from 'typescript';
export class Rule extends AbstractRule { export class Rule extends AbstractRule {
public static FAILURE_STRING = "duplicate module import"; public static FAILURE_STRING = 'duplicate module import';
public apply(sourceFile: ts.SourceFile): RuleFailure[] { public apply(sourceFile: ts.SourceFile): RuleFailure[] {
const typedefWalker = new ModuleImportWalker(sourceFile, this.getOptions()); const typedefWalker = new ModuleImportWalker(sourceFile, this.getOptions());
@ -27,12 +27,12 @@ class ModuleImportWalker extends RuleWalker {
private checkTypeAnnotation(location: number, typeAnnotation: ts.TypeNode, name?: ts.Node) { private checkTypeAnnotation(location: number, typeAnnotation: ts.TypeNode, name?: ts.Node) {
if (typeAnnotation == null) { if (typeAnnotation == null) {
let ns = "<name missing>"; let ns = '<name missing>';
if (name != null && name.kind === ts.SyntaxKind.Identifier) { if (name != null && name.kind === ts.SyntaxKind.Identifier) {
ns = (<ts.Identifier>name).text; ns = (<ts.Identifier>name).text;
} }
if (ns.charAt(0) === '_') return; if (ns.charAt(0) === '_') return;
let failure = this.createFailure(location, 1, "expected parameter " + ns + " to have a type"); let failure = this.createFailure(location, 1, 'expected parameter ' + ns + ' to have a type');
this.addFailure(failure); this.addFailure(failure);
} }
} }
@ -41,7 +41,7 @@ class ModuleImportWalker extends RuleWalker {
var text = moduleSpecifier.getText(); var text = moduleSpecifier.getText();
if (this.importModulesSeen.indexOf(text) >= 0) { if (this.importModulesSeen.indexOf(text) >= 0) {
let failure = let failure =
this.createFailure(moduleSpecifier.getEnd(), 1, "Duplicate imports from module " + text); this.createFailure(moduleSpecifier.getEnd(), 1, 'Duplicate imports from module ' + text);
this.addFailure(failure); this.addFailure(failure);
} }
this.importModulesSeen.push(text); this.importModulesSeen.push(text);

View File

@ -1,6 +1,6 @@
import {RuleWalker} from 'tslint/lib/language/walker';
import {RuleFailure} from 'tslint/lib/lint'; import {RuleFailure} from 'tslint/lib/lint';
import {AbstractRule} from 'tslint/lib/rules'; import {AbstractRule} from 'tslint/lib/rules';
import {RuleWalker} from 'tslint/lib/language/walker';
import * as ts from 'typescript'; import * as ts from 'typescript';
export class Rule extends AbstractRule { export class Rule extends AbstractRule {
@ -24,7 +24,7 @@ class TypedefWalker extends RuleWalker {
private hasInternalAnnotation(range: ts.CommentRange): boolean { private hasInternalAnnotation(range: ts.CommentRange): boolean {
let text = this.getSourceFile().text; let text = this.getSourceFile().text;
let comment = text.substring(range.pos, range.end); let comment = text.substring(range.pos, range.end);
return comment.indexOf("@internal") >= 0; return comment.indexOf('@internal') >= 0;
} }
private assertInternalAnnotationPresent(node: ts.Declaration) { private assertInternalAnnotationPresent(node: ts.Declaration) {

View File

@ -1,10 +1,10 @@
import {RuleWalker} from 'tslint/lib/language/walker';
import {RuleFailure} from 'tslint/lib/lint'; import {RuleFailure} from 'tslint/lib/lint';
import {AbstractRule} from 'tslint/lib/rules'; import {AbstractRule} from 'tslint/lib/rules';
import {RuleWalker} from 'tslint/lib/language/walker';
import * as ts from 'typescript'; import * as ts from 'typescript';
export class Rule extends AbstractRule { export class Rule extends AbstractRule {
public static FAILURE_STRING = "missing type declaration"; public static FAILURE_STRING = 'missing type declaration';
public apply(sourceFile: ts.SourceFile): RuleFailure[] { public apply(sourceFile: ts.SourceFile): RuleFailure[] {
const typedefWalker = new TypedefWalker(sourceFile, this.getOptions()); const typedefWalker = new TypedefWalker(sourceFile, this.getOptions());
@ -28,12 +28,12 @@ class TypedefWalker extends RuleWalker {
private checkTypeAnnotation(location: number, typeAnnotation: ts.TypeNode, name?: ts.Node) { private checkTypeAnnotation(location: number, typeAnnotation: ts.TypeNode, name?: ts.Node) {
if (typeAnnotation == null) { if (typeAnnotation == null) {
let ns = "<name missing>"; let ns = '<name missing>';
if (name != null && name.kind === ts.SyntaxKind.Identifier) { if (name != null && name.kind === ts.SyntaxKind.Identifier) {
ns = (<ts.Identifier>name).text; ns = (<ts.Identifier>name).text;
} }
if (ns.charAt(0) === '_') return; if (ns.charAt(0) === '_') return;
let failure = this.createFailure(location, 1, "expected parameter " + ns + " to have a type"); let failure = this.createFailure(location, 1, 'expected parameter ' + ns + ' to have a type');
this.addFailure(failure); this.addFailure(failure);
} }
} }

View File

@ -1,10 +1,10 @@
import {RuleWalker} from 'tslint/lib/language/walker';
import {RuleFailure} from 'tslint/lib/lint'; import {RuleFailure} from 'tslint/lib/lint';
import {AbstractRule} from 'tslint/lib/rules'; import {AbstractRule} from 'tslint/lib/rules';
import {RuleWalker} from 'tslint/lib/language/walker';
import * as ts from 'typescript'; import * as ts from 'typescript';
export class Rule extends AbstractRule { export class Rule extends AbstractRule {
public static FAILURE_STRING = "missing type declaration"; public static FAILURE_STRING = 'missing type declaration';
public apply(sourceFile: ts.SourceFile): RuleFailure[] { public apply(sourceFile: ts.SourceFile): RuleFailure[] {
const typedefWalker = new TypedefWalker(sourceFile, this.getOptions()); const typedefWalker = new TypedefWalker(sourceFile, this.getOptions());
@ -50,12 +50,12 @@ class TypedefWalker extends RuleWalker {
private checkTypeAnnotation(typeAnnotation: ts.TypeNode, name: ts.Node, start: number) { private checkTypeAnnotation(typeAnnotation: ts.TypeNode, name: ts.Node, start: number) {
if (typeAnnotation == null) { if (typeAnnotation == null) {
let ns = "<name missing>"; let ns = '<name missing>';
if (name != null && name.kind === ts.SyntaxKind.Identifier) { if (name != null && name.kind === ts.SyntaxKind.Identifier) {
ns = (<ts.Identifier>name).text; ns = (<ts.Identifier>name).text;
} }
if (ns.charAt(0) === '_') return; if (ns.charAt(0) === '_') return;
let failure = this.createFailure(start, 1, "expected " + ns + " to have a return type"); let failure = this.createFailure(start, 1, 'expected ' + ns + ' to have a return type');
this.addFailure(failure); this.addFailure(failure);
} }
} }