refactor(compiler_cli): move it into modules/@angular and integrate properly into the build
This also does no more depend on a version on npm for the compiler_cli. Also runs the tests for tools/metadata
This commit is contained in:
96
modules/@angular/compiler_cli/README.md
Normal file
96
modules/@angular/compiler_cli/README.md
Normal file
@ -0,0 +1,96 @@
|
||||
# Angular Template Compiler
|
||||
|
||||
Angular applications are built with templates, which may be `.html` or `.css` files,
|
||||
or may be inline `template` attributes on Decorators like `@Component`.
|
||||
|
||||
These templates are compiled into executable JS at application runtime (except in `interpretation` mode).
|
||||
This compilation can occur on the client, but it results in slower bootstrap time, and also
|
||||
requires that the compiler be included in the code downloaded to the client.
|
||||
|
||||
You can produce smaller, faster applications by running Angular's compiler as a build step,
|
||||
and then downloading only the executable JS to the client.
|
||||
|
||||
## Install and use
|
||||
|
||||
```
|
||||
# First install angular, see https://github.com/angular/angular/blob/master/CHANGELOG.md#200-rc0-2016-05-02
|
||||
$ npm install @angular/compiler-cli typescript@next @angular/platform-server @angular/compiler
|
||||
# Optional sanity check, make sure TypeScript can compile.
|
||||
$ ./node_modules/.bin/tsc -p path/to/project
|
||||
# ngc is a drop-in replacement for tsc.
|
||||
$ ./node_modules/.bin/ngc -p path/to/project
|
||||
```
|
||||
|
||||
In order to write a `bootstrap` that imports the generated code, you should first write your
|
||||
top-level component, and run `ngc` once to produce a generated `.ngfactory.ts` file.
|
||||
Then you can add an import statement in the `bootstrap` allowing you to bootstrap off the
|
||||
generated code.
|
||||
|
||||
## Configuration
|
||||
|
||||
The `tsconfig.json` file may contain an additional configuration block:
|
||||
```
|
||||
"angularCompilerOptions": {
|
||||
"genDir": "."
|
||||
}
|
||||
```
|
||||
the `genDir` option controls the path (relative to `tsconfig.json`) where the generated file tree
|
||||
will be written. If `genDir` is not set, then the code will be generated in the source tree, next
|
||||
to your original sources. More options may be added as we implement more features.
|
||||
|
||||
We recommend you avoid checking generated files into version control. This permits a state where
|
||||
the generated files in the repository were created from sources that were never checked in,
|
||||
making it impossible to reproduce the current state. Also, your changes will effectively appear
|
||||
twice in code reviews, with the generated version inscrutible by the reviewer.
|
||||
|
||||
In TypeScript 1.8, the generated sources will have to be written alongside your originals,
|
||||
so set `genDir` to the same location as your files (typicially the same as `rootDir`).
|
||||
Add `**/*.ngfactory.ts` to your `.gitignore` or other mechanism for your version control system.
|
||||
|
||||
In TypeScript 1.9 and above, you can add a generated folder into your application,
|
||||
such as `codegen`. Using the `rootDirs` option, you can allow relative imports like
|
||||
`import {} from './foo.ngfactory'` even though the `src` and `codegen` trees are distinct.
|
||||
Add `**/codegen` to your `.gitignore` or similar.
|
||||
|
||||
Note that in the second option, TypeScript will emit the code into two parallel directories
|
||||
as well. This is by design, see https://github.com/Microsoft/TypeScript/issues/8245.
|
||||
This makes the configuration of your runtime module loader more complex, so we don't recommend
|
||||
this option yet.
|
||||
|
||||
See the example in the `test/` directory for a working example.
|
||||
|
||||
## Compiler CLI
|
||||
|
||||
This program mimics the TypeScript tsc command line. It accepts a `-p` flag which points to a
|
||||
`tsconfig.json` file, or a directory containing one.
|
||||
|
||||
This CLI is intended for demos, prototyping, or for users with simple build systems
|
||||
that run bare `tsc`.
|
||||
|
||||
Users with a build system should expect an Angular 2 template plugin. Such a plugin would be
|
||||
based on the `index.ts` in this directory, but should share the TypeScript compiler instance
|
||||
with the one already used in the plugin for TypeScript typechecking and emit.
|
||||
|
||||
## Design
|
||||
At a high level, this program
|
||||
- collects static metadata about the sources using the `ts-metadata-collector` package in angular2
|
||||
- uses the `OfflineCompiler` from `angular2/src/compiler/compiler` to codegen additional `.ts` files
|
||||
- these `.ts` files are written to the `genDir` path, then compiled together with the application.
|
||||
|
||||
## For developers
|
||||
Run the compiler from source:
|
||||
```
|
||||
# Build angular2 and the compiler
|
||||
./node_modules/.bin/tsc -p modules
|
||||
# Run it on the test project
|
||||
$ export NODE_PATH=$NODE_PATH:dist/all:dist/tools
|
||||
$ node dist/packages-dist/compiler_cli/src/main -p modules/@angular/compiler_cli/integrationtest
|
||||
```
|
||||
|
||||
Release:
|
||||
```
|
||||
$ node dist/tools/cjs-jasmine -- @angular/compiler_cli/integrationtest/**/*_spec.js
|
||||
$ cp modules/@angular/compiler_cli/README.md modules/@angular/compiler_cli/package.json dist/all/@angular/compiler_cli
|
||||
# npm login as angular
|
||||
$ npm publish dist/all/@angular/compiler_cli --access=public
|
||||
```
|
3
modules/@angular/compiler_cli/index.ts
Normal file
3
modules/@angular/compiler_cli/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export {CodeGenerator} from './src/codegen';
|
||||
export {NodeReflectorHost} from './src/reflector_host';
|
||||
export {TsickleHost, MetadataWriterHost} from './src/compiler_host';
|
@ -0,0 +1 @@
|
||||
<div></div>
|
@ -0,0 +1,21 @@
|
||||
import {Component} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'my-comp',
|
||||
template: '<div></div>',
|
||||
})
|
||||
export class MyComp {
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'next-comp',
|
||||
templateUrl: './multiple_components.html',
|
||||
})
|
||||
export class NextComp {
|
||||
}
|
||||
|
||||
// Verify that exceptions from DirectiveResolver don't propagate
|
||||
export function NotADirective(c: any): void {}
|
||||
@NotADirective
|
||||
export class HasCustomDecorator {
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
@import './shared.css';
|
||||
|
||||
.green { color: green }
|
@ -0,0 +1,3 @@
|
||||
<div>{{ctxProp}}</div>
|
||||
<form><input type="button" [(ngModel)]="ctxProp"/></form>
|
||||
<my-comp></my-comp>
|
15
modules/@angular/compiler_cli/integrationtest/src/basic.ts
Normal file
15
modules/@angular/compiler_cli/integrationtest/src/basic.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import {Component, Inject} from '@angular/core';
|
||||
import {FORM_DIRECTIVES} from '@angular/common';
|
||||
import {MyComp} from './a/multiple_components';
|
||||
|
||||
@Component({
|
||||
selector: 'basic',
|
||||
templateUrl: './basic.html',
|
||||
styles: ['.red { color: red }'],
|
||||
styleUrls: ['./basic.css'],
|
||||
directives: [MyComp, FORM_DIRECTIVES]
|
||||
})
|
||||
export class Basic {
|
||||
ctxProp: string;
|
||||
constructor() { this.ctxProp = 'initiaValue'; }
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
import {coreBootstrap, ReflectiveInjector} from '@angular/core';
|
||||
import {browserPlatform, BROWSER_APP_STATIC_PROVIDERS} from '@angular/platform-browser';
|
||||
import {BasicNgFactory} from './basic.ngfactory';
|
||||
import {Basic} from './basic';
|
||||
|
||||
const appInjector =
|
||||
ReflectiveInjector.resolveAndCreate(BROWSER_APP_STATIC_PROVIDERS, browserPlatform().injector);
|
||||
coreBootstrap(appInjector, BasicNgFactory);
|
2
modules/@angular/compiler_cli/integrationtest/src/dep.d.ts
vendored
Normal file
2
modules/@angular/compiler_cli/integrationtest/src/dep.d.ts
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
// Verify we don't try to extract metadata for .d.ts files
|
||||
export declare var a: string;
|
@ -0,0 +1,29 @@
|
||||
import {Component, Inject, OpaqueToken} from '@angular/core';
|
||||
import {NgIf} from '@angular/common';
|
||||
|
||||
export const SOME_OPAQUE_TOKEN = new OpaqueToken('opaqueToken');
|
||||
|
||||
@Component({
|
||||
selector: 'comp-providers',
|
||||
template: '',
|
||||
providers: [
|
||||
{provide: 'strToken', useValue: 'strValue'},
|
||||
{provide: SOME_OPAQUE_TOKEN, useValue: 10},
|
||||
{provide: 'reference', useValue: NgIf},
|
||||
{provide: 'complexToken', useValue: {a: 1, b: ['test', SOME_OPAQUE_TOKEN]}},
|
||||
]
|
||||
})
|
||||
export class CompWithProviders {
|
||||
constructor(@Inject('strToken') public ctxProp) {}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'cmp-reference',
|
||||
template: `
|
||||
<input #a>{{a.value}}
|
||||
<div *ngIf="true">{{a.value}}</div>
|
||||
`,
|
||||
directives: [NgIf]
|
||||
})
|
||||
export class CompWithReferences {
|
||||
}
|
@ -0,0 +1 @@
|
||||
.blue { color: blue }
|
@ -0,0 +1,26 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
describe("template codegen output", () => {
|
||||
const outDir = path.join('dist', 'all', '@angular', 'compiler_cli', 'integrationtest', 'src');
|
||||
|
||||
it("should lower Decorators without reflect-metadata", () => {
|
||||
const jsOutput = path.join(outDir, 'basic.js');
|
||||
expect(fs.existsSync(jsOutput)).toBeTruthy();
|
||||
expect(fs.readFileSync(jsOutput, {encoding: 'utf-8'})).not.toContain('Reflect.decorate');
|
||||
});
|
||||
|
||||
it("should produce metadata.json outputs", () => {
|
||||
const metadataOutput = path.join(outDir, 'basic.metadata.json');
|
||||
expect(fs.existsSync(metadataOutput)).toBeTruthy();
|
||||
const output = fs.readFileSync(metadataOutput, {encoding: 'utf-8'});
|
||||
expect(output).toContain('"decorators":');
|
||||
expect(output).toContain('"name":"Component","module":"@angular/core"');
|
||||
});
|
||||
|
||||
it("should write .d.ts files", () => {
|
||||
const dtsOutput = path.join(outDir, 'basic.d.ts');
|
||||
expect(fs.existsSync(dtsOutput)).toBeTruthy();
|
||||
expect(fs.readFileSync(dtsOutput, {encoding: 'utf-8'})).toContain('Basic');
|
||||
});
|
||||
});
|
23
modules/@angular/compiler_cli/integrationtest/tsconfig.json
Normal file
23
modules/@angular/compiler_cli/integrationtest/tsconfig.json
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"angularCompilerOptions": {
|
||||
// For TypeScript 1.8, we have to lay out generated files
|
||||
// in the same source directory with your code.
|
||||
"genDir": ".",
|
||||
"legacyPackageLayout": false
|
||||
},
|
||||
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"experimentalDecorators": true,
|
||||
"noImplicitAny": false,
|
||||
"moduleResolution": "node",
|
||||
"outDir": "../../../../dist/all/@angular/compiler_cli/integrationtest",
|
||||
"rootDir": "",
|
||||
"declaration": true,
|
||||
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@angular/*": ["../../../../dist/all/@angular/*"]
|
||||
}
|
||||
}
|
||||
}
|
4
modules/@angular/compiler_cli/integrationtest/typings.d.ts
vendored
Normal file
4
modules/@angular/compiler_cli/integrationtest/typings.d.ts
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
/// <reference path="../../typings/es6-collections/es6-collections.d.ts" />
|
||||
/// <reference path="../../typings/es6-promise/es6-promise.d.ts" />
|
||||
/// <reference path="../../typings/node/node.d.ts" />
|
||||
/// <reference path="../../typings/jasmine/jasmine.d.ts" />
|
39
modules/@angular/compiler_cli/package.json
Normal file
39
modules/@angular/compiler_cli/package.json
Normal file
@ -0,0 +1,39 @@
|
||||
{
|
||||
"name": "@angular/compiler-cli",
|
||||
"version": "0.2.0",
|
||||
"description": "Execute angular2 template compiler in nodejs.",
|
||||
"main": "index.js",
|
||||
"typings": "index.d.ts",
|
||||
"bin": {
|
||||
"ngc": "./src/main.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"ts-metadata-collector": "^0.1.0",
|
||||
"tsickle": "^0.1.2",
|
||||
"reflect-metadata": "^0.1.2",
|
||||
"parse5": "1.3.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^1.9.0-dev",
|
||||
"@angular/compiler": "^2.0.0-rc",
|
||||
"@angular/platform-server": "^2.0.0-rc",
|
||||
"@angular/core": "^2.0.0-rc"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/angular/angular.git"
|
||||
},
|
||||
"keywords": [
|
||||
"angular",
|
||||
"compiler"
|
||||
],
|
||||
"contributors": [
|
||||
"Tobias Bosch <tbosch@google.com> (https://angular.io/)",
|
||||
"Alex Eagle <alexeagle@google.com> (https://angular.io/)"
|
||||
],
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/angular/angular/issues"
|
||||
},
|
||||
"homepage": "https://github.com/angular/angular/tree/master/tools/compiler_cli"
|
||||
}
|
176
modules/@angular/compiler_cli/src/codegen.ts
Normal file
176
modules/@angular/compiler_cli/src/codegen.ts
Normal file
@ -0,0 +1,176 @@
|
||||
/**
|
||||
* Transform template html and css into executable code.
|
||||
* Intended to be used in a build step.
|
||||
*/
|
||||
import * as ts from 'typescript';
|
||||
import * as path from 'path';
|
||||
|
||||
import * as compiler from '@angular/compiler';
|
||||
import {ViewEncapsulation} from '@angular/core';
|
||||
import {StaticReflector} from './static_reflector';
|
||||
import {CompileMetadataResolver, HtmlParser, DirectiveNormalizer, Lexer, Parser,
|
||||
TemplateParser, DomElementSchemaRegistry, StyleCompiler,
|
||||
ViewCompiler, TypeScriptEmitter
|
||||
} from './compiler_private';
|
||||
|
||||
import {Parse5DomAdapter} from '@angular/platform-server';
|
||||
|
||||
import {MetadataCollector} from 'ts-metadata-collector';
|
||||
import {NodeReflectorHost} from './reflector_host';
|
||||
|
||||
const GENERATED_FILES = /\.ngfactory\.ts$|\.css\.ts$|\.css\.shim\.ts$/;
|
||||
|
||||
const PREAMBLE = `/**
|
||||
* This file is generated by the Angular 2 template compiler.
|
||||
* Do not edit.
|
||||
*/
|
||||
`;
|
||||
|
||||
// TODO(alexeagle): we end up passing options and ngOptions everywhere.
|
||||
// Maybe this should extend ts.CompilerOptions so we only need this one.
|
||||
export interface AngularCompilerOptions {
|
||||
// Absolute path to a directory where generated file structure is written
|
||||
genDir: string;
|
||||
|
||||
// Path to the directory containing the tsconfig.json file.
|
||||
basePath: string;
|
||||
|
||||
// Don't do the template code generation
|
||||
skipTemplateCodegen: boolean;
|
||||
|
||||
// Don't produce .metadata.json files (they don't work for bundled emit with --out)
|
||||
skipMetadataEmit: boolean;
|
||||
|
||||
// Lookup angular's symbols using the old angular2/... npm namespace.
|
||||
legacyPackageLayout: boolean;
|
||||
|
||||
// Print extra information while running the compiler
|
||||
trace: boolean;
|
||||
}
|
||||
|
||||
export class CodeGenerator {
|
||||
constructor(private options: ts.CompilerOptions, private ngOptions: AngularCompilerOptions,
|
||||
private program: ts.Program, public host: ts.CompilerHost,
|
||||
private staticReflector: StaticReflector, private resolver: CompileMetadataResolver,
|
||||
private compiler: compiler.OfflineCompiler,
|
||||
private reflectorHost: NodeReflectorHost) {}
|
||||
|
||||
private generateSource(metadatas: compiler.CompileDirectiveMetadata[]) {
|
||||
const normalize = (metadata: compiler.CompileDirectiveMetadata) => {
|
||||
const directiveType = metadata.type.runtime;
|
||||
const directives = this.resolver.getViewDirectivesMetadata(directiveType);
|
||||
const pipes = this.resolver.getViewPipesMetadata(directiveType);
|
||||
return new compiler.NormalizedComponentWithViewDirectives(metadata, directives, pipes);
|
||||
};
|
||||
|
||||
return this.compiler.compileTemplates(metadatas.map(normalize));
|
||||
}
|
||||
|
||||
private readComponents(absSourcePath: string) {
|
||||
const result: Promise<compiler.CompileDirectiveMetadata>[] = [];
|
||||
const metadata = this.staticReflector.getModuleMetadata(absSourcePath);
|
||||
if (!metadata) {
|
||||
console.log(`WARNING: no metadata found for ${absSourcePath}`);
|
||||
return result;
|
||||
}
|
||||
|
||||
const symbols = Object.keys(metadata['metadata']);
|
||||
if (!symbols || !symbols.length) {
|
||||
return result;
|
||||
}
|
||||
for (const symbol of symbols) {
|
||||
const staticType = this.reflectorHost.findDeclaration(absSourcePath, symbol, absSourcePath);
|
||||
let directive: compiler.CompileDirectiveMetadata;
|
||||
directive = this.resolver.maybeGetDirectiveMetadata(<any>staticType);
|
||||
|
||||
if (!directive || !directive.isComponent) {
|
||||
continue;
|
||||
}
|
||||
result.push(this.compiler.normalizeDirectiveMetadata(directive));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Write codegen in a directory structure matching the sources.
|
||||
private calculateEmitPath(filePath: string) {
|
||||
let root = this.ngOptions.basePath;
|
||||
for (let eachRootDir of this.options.rootDirs || []) {
|
||||
if (this.ngOptions.trace) {
|
||||
console.log(
|
||||
`Check if ${filePath} is under rootDirs element ${eachRootDir}`);
|
||||
}
|
||||
if (path.relative(eachRootDir, filePath).indexOf('.') !== 0) {
|
||||
root = eachRootDir;
|
||||
}
|
||||
}
|
||||
|
||||
return path.join(this.ngOptions.genDir, path.relative(root, filePath));
|
||||
}
|
||||
|
||||
// TODO(tbosch): add a cache for shared css files
|
||||
// TODO(tbosch): detect cycles!
|
||||
private generateStylesheet(filepath: string, shim: boolean): Promise<any> {
|
||||
return this.compiler.loadAndCompileStylesheet(filepath, shim, '.ts')
|
||||
.then((sourceWithImports) => {
|
||||
const emitPath = this.calculateEmitPath(sourceWithImports.source.moduleUrl);
|
||||
// TODO(alexeagle): should include the sourceFile to the WriteFileCallback
|
||||
this.host.writeFile(emitPath, PREAMBLE + sourceWithImports.source.source, false);
|
||||
return Promise.all(
|
||||
sourceWithImports.importedUrls.map(url => this.generateStylesheet(url, shim)));
|
||||
});
|
||||
}
|
||||
|
||||
codegen(): Promise<any> {
|
||||
Parse5DomAdapter.makeCurrent();
|
||||
|
||||
const generateOneFile = (absSourcePath: string) =>
|
||||
Promise.all(this.readComponents(absSourcePath))
|
||||
.then((metadatas: compiler.CompileDirectiveMetadata[]) => {
|
||||
if (!metadatas || !metadatas.length) {
|
||||
return;
|
||||
}
|
||||
let stylesheetPromises: Promise<any>[] = [];
|
||||
metadatas.forEach((metadata) => {
|
||||
let stylesheetPaths = metadata && metadata.template && metadata.template.styleUrls;
|
||||
if (stylesheetPaths) {
|
||||
stylesheetPaths.forEach((path) => {
|
||||
stylesheetPromises.push(this.generateStylesheet(
|
||||
path, metadata.template.encapsulation === ViewEncapsulation.Emulated));
|
||||
});
|
||||
}
|
||||
});
|
||||
const generated = this.generateSource(metadatas);
|
||||
const sourceFile = this.program.getSourceFile(absSourcePath);
|
||||
const emitPath = this.calculateEmitPath(generated.moduleUrl);
|
||||
this.host.writeFile(emitPath, PREAMBLE + generated.source, false, () => {},
|
||||
[sourceFile]);
|
||||
return Promise.all(stylesheetPromises);
|
||||
})
|
||||
.catch((e) => { console.error(e.stack); });
|
||||
return Promise.all(
|
||||
this.program.getRootFileNames().filter(f => !GENERATED_FILES.test(f)).map(generateOneFile));
|
||||
}
|
||||
|
||||
static create(ngOptions: AngularCompilerOptions, program: ts.Program, options: ts.CompilerOptions,
|
||||
compilerHost: ts.CompilerHost): CodeGenerator {
|
||||
const xhr: compiler.XHR = {get: (s: string) => Promise.resolve(compilerHost.readFile(s))};
|
||||
const urlResolver: compiler.UrlResolver = compiler.createOfflineCompileUrlResolver();
|
||||
const reflectorHost = new NodeReflectorHost(program, compilerHost, options, ngOptions);
|
||||
const staticReflector = new StaticReflector(reflectorHost);
|
||||
const htmlParser = new HtmlParser();
|
||||
const normalizer = new DirectiveNormalizer(xhr, urlResolver, htmlParser);
|
||||
const parser = new Parser(new Lexer());
|
||||
const tmplParser = new TemplateParser(parser, new DomElementSchemaRegistry(), htmlParser,
|
||||
/*console*/ null, []);
|
||||
const offlineCompiler = new compiler.OfflineCompiler(
|
||||
normalizer, tmplParser, new StyleCompiler(urlResolver),
|
||||
new ViewCompiler(new compiler.CompilerConfig(true, true, true)),
|
||||
new TypeScriptEmitter(reflectorHost), xhr);
|
||||
const resolver = new CompileMetadataResolver(
|
||||
new compiler.DirectiveResolver(staticReflector), new compiler.PipeResolver(staticReflector),
|
||||
new compiler.ViewResolver(staticReflector), null, null, staticReflector);
|
||||
|
||||
return new CodeGenerator(options, ngOptions, program, compilerHost,
|
||||
staticReflector, resolver, offlineCompiler, reflectorHost);
|
||||
}
|
||||
}
|
93
modules/@angular/compiler_cli/src/compiler_host.ts
Normal file
93
modules/@angular/compiler_cli/src/compiler_host.ts
Normal file
@ -0,0 +1,93 @@
|
||||
import * as ts from 'typescript';
|
||||
import * as path from 'path';
|
||||
import {convertDecorators} from 'tsickle';
|
||||
import {NodeReflectorHost} from './reflector_host';
|
||||
import {AngularCompilerOptions} from './codegen';
|
||||
|
||||
/**
|
||||
* Implementation of CompilerHost that forwards all methods to another instance.
|
||||
* Useful for partial implementations to override only methods they care about.
|
||||
*/
|
||||
export abstract class DelegatingHost implements ts.CompilerHost {
|
||||
constructor(protected delegate: ts.CompilerHost) {}
|
||||
getSourceFile =
|
||||
(fileName: string, languageVersion: ts.ScriptTarget, onError?: (message: string) => void) =>
|
||||
this.delegate.getSourceFile(fileName, languageVersion, onError);
|
||||
|
||||
getCancellationToken = () => this.delegate.getCancellationToken();
|
||||
getDefaultLibFileName = (options: ts.CompilerOptions) =>
|
||||
this.delegate.getDefaultLibFileName(options);
|
||||
getDefaultLibLocation = () => this.delegate.getDefaultLibLocation();
|
||||
writeFile: ts.WriteFileCallback = this.delegate.writeFile;
|
||||
getCurrentDirectory = () => this.delegate.getCurrentDirectory();
|
||||
getCanonicalFileName = (fileName: string) => this.delegate.getCanonicalFileName(fileName);
|
||||
useCaseSensitiveFileNames = () => this.delegate.useCaseSensitiveFileNames();
|
||||
getNewLine = () => this.delegate.getNewLine();
|
||||
fileExists = (fileName: string) => this.delegate.fileExists(fileName);
|
||||
readFile = (fileName: string) => this.delegate.readFile(fileName);
|
||||
trace = (s: string) => this.delegate.trace(s);
|
||||
directoryExists = (directoryName: string) => this.delegate.directoryExists(directoryName);
|
||||
}
|
||||
|
||||
export class TsickleHost extends DelegatingHost {
|
||||
// Additional diagnostics gathered by pre- and post-emit transformations.
|
||||
public diagnostics: ts.Diagnostic[] = [];
|
||||
private TSICKLE_SUPPORT = `
|
||||
interface DecoratorInvocation {
|
||||
type: Function;
|
||||
args?: any[];
|
||||
}
|
||||
`;
|
||||
constructor(delegate: ts.CompilerHost, private options: ts.CompilerOptions) { super(delegate); }
|
||||
|
||||
getSourceFile =
|
||||
(fileName: string, languageVersion: ts.ScriptTarget, onError?: (message: string) => void) => {
|
||||
const originalContent = this.delegate.readFile(fileName);
|
||||
let newContent = originalContent;
|
||||
if (!/\.d\.ts$/.test(fileName)) {
|
||||
const converted = convertDecorators(fileName, originalContent);
|
||||
if (converted.diagnostics) {
|
||||
this.diagnostics.push(...converted.diagnostics);
|
||||
}
|
||||
newContent = converted.output + this.TSICKLE_SUPPORT;
|
||||
}
|
||||
return ts.createSourceFile(fileName, newContent, languageVersion, true);
|
||||
}
|
||||
}
|
||||
|
||||
const IGNORED_FILES = /\.ngfactory\.js$|\.css\.js$|\.css\.shim\.js$/;
|
||||
|
||||
export class MetadataWriterHost extends DelegatingHost {
|
||||
private reflectorHost: NodeReflectorHost;
|
||||
constructor(delegate: ts.CompilerHost, program: ts.Program, options: ts.CompilerOptions,
|
||||
ngOptions: AngularCompilerOptions) {
|
||||
super(delegate);
|
||||
this.reflectorHost = new NodeReflectorHost(program, this, options, ngOptions);
|
||||
}
|
||||
|
||||
writeFile: ts.WriteFileCallback = (fileName: string, data: string, writeByteOrderMark: boolean,
|
||||
onError?: (message: string) => void,
|
||||
sourceFiles?: ts.SourceFile[]) => {
|
||||
if (/\.d\.ts$/.test(fileName)) {
|
||||
// Let the original file be written first; this takes care of creating parent directories
|
||||
this.delegate.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles);
|
||||
|
||||
// TODO: remove this early return after https://github.com/Microsoft/TypeScript/pull/8412 is
|
||||
// released
|
||||
return;
|
||||
}
|
||||
|
||||
if (IGNORED_FILES.test(fileName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!sourceFiles) {
|
||||
throw new Error('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');
|
||||
}
|
||||
this.reflectorHost.writeMetadata(fileName, sourceFiles[0]);
|
||||
}
|
||||
}
|
37
modules/@angular/compiler_cli/src/compiler_private.ts
Normal file
37
modules/@angular/compiler_cli/src/compiler_private.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import {__compiler_private__ as _c} from '@angular/compiler';
|
||||
|
||||
export var AssetUrl: typeof _c.AssetUrl = _c.AssetUrl;
|
||||
export type AssetUrl = _c.AssetUrl;
|
||||
|
||||
export var ImportGenerator: typeof _c.ImportGenerator = _c.ImportGenerator;
|
||||
export type ImportGenerator = _c.ImportGenerator;
|
||||
|
||||
export type CompileMetadataResolver = _c.CompileMetadataResolver;
|
||||
export var CompileMetadataResolver: typeof _c.CompileMetadataResolver = _c.CompileMetadataResolver;
|
||||
|
||||
export type HtmlParser = _c.HtmlParser;
|
||||
export var HtmlParser: typeof _c.HtmlParser = _c.HtmlParser;
|
||||
|
||||
export type DirectiveNormalizer = _c.DirectiveNormalizer;
|
||||
export var DirectiveNormalizer: typeof _c.DirectiveNormalizer = _c.DirectiveNormalizer;
|
||||
|
||||
export type Lexer = _c.Lexer;
|
||||
export var Lexer: typeof _c.Lexer = _c.Lexer;
|
||||
|
||||
export type Parser = _c.Parser;
|
||||
export var Parser: typeof _c.Parser = _c.Parser;
|
||||
|
||||
export type TemplateParser = _c.TemplateParser;
|
||||
export var TemplateParser: typeof _c.TemplateParser = _c.TemplateParser;
|
||||
|
||||
export type DomElementSchemaRegistry = _c.DomElementSchemaRegistry;
|
||||
export var DomElementSchemaRegistry: typeof _c.DomElementSchemaRegistry = _c.DomElementSchemaRegistry;
|
||||
|
||||
export type StyleCompiler = _c.StyleCompiler;
|
||||
export var StyleCompiler: typeof _c.StyleCompiler = _c.StyleCompiler;
|
||||
|
||||
export type ViewCompiler = _c.ViewCompiler;
|
||||
export var ViewCompiler: typeof _c.ViewCompiler = _c.ViewCompiler;
|
||||
|
||||
export type TypeScriptEmitter = _c.TypeScriptEmitter;
|
||||
export var TypeScriptEmitter: typeof _c.TypeScriptEmitter = _c.TypeScriptEmitter;
|
4
modules/@angular/compiler_cli/src/core_private.ts
Normal file
4
modules/@angular/compiler_cli/src/core_private.ts
Normal file
@ -0,0 +1,4 @@
|
||||
import {__core_private__ as r, __core_private_types__ as t} from '@angular/core';
|
||||
|
||||
export type ReflectorReader = t.ReflectorReader;
|
||||
export var ReflectorReader: typeof t.ReflectorReader = r.ReflectorReader;
|
78
modules/@angular/compiler_cli/src/main.ts
Normal file
78
modules/@angular/compiler_cli/src/main.ts
Normal file
@ -0,0 +1,78 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
// Must be imported first, because angular2 decorators throws on load.
|
||||
import 'reflect-metadata';
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as ts from 'typescript';
|
||||
import {tsc, check} from './tsc';
|
||||
import {MetadataWriterHost, TsickleHost} from './compiler_host';
|
||||
import {NodeReflectorHost} from './reflector_host';
|
||||
import {CodeGenerator} from './codegen';
|
||||
import {MetadataCollector, ModuleMetadata} from 'ts-metadata-collector';
|
||||
|
||||
const DEBUG = false;
|
||||
|
||||
function debug(msg: string, ...o: any[]) {
|
||||
if (DEBUG) console.log(msg, ...o);
|
||||
}
|
||||
|
||||
export function main(project: string, basePath?: string): Promise<any> {
|
||||
try {
|
||||
let projectDir = project;
|
||||
if (fs.lstatSync(project).isFile()) {
|
||||
projectDir = path.dirname(project);
|
||||
}
|
||||
// file names in tsconfig are resolved relative to this absolute path
|
||||
basePath = path.join(process.cwd(), basePath || projectDir);
|
||||
|
||||
// read the configuration options from wherever you store them
|
||||
const {parsed, ngOptions} = tsc.readConfiguration(project, basePath);
|
||||
ngOptions.basePath = basePath;
|
||||
|
||||
const host = ts.createCompilerHost(parsed.options, true);
|
||||
|
||||
let codegenStep: Promise<any>;
|
||||
|
||||
const program = ts.createProgram(parsed.fileNames, parsed.options, host);
|
||||
const errors = program.getOptionsDiagnostics();
|
||||
check(errors);
|
||||
|
||||
const doCodegen = ngOptions.skipTemplateCodegen ?
|
||||
Promise.resolve(null) :
|
||||
CodeGenerator.create(ngOptions, program, parsed.options, host).codegen();
|
||||
|
||||
return doCodegen.then(() => {
|
||||
tsc.typeCheck(host, program);
|
||||
|
||||
// Emit *.js with Decorators lowered to Annotations, and also *.js.map
|
||||
const tsicklePreProcessor = new TsickleHost(host, parsed.options);
|
||||
tsc.emit(tsicklePreProcessor, program);
|
||||
|
||||
if (!ngOptions.skipMetadataEmit) {
|
||||
// Emit *.metadata.json and *.d.ts
|
||||
// Not in the same emit pass with above, because tsickle erases
|
||||
// decorators which we want to read or document.
|
||||
// Do this emit second since TypeScript will create missing directories for us
|
||||
// in the standard emit.
|
||||
const metadataWriter = new MetadataWriterHost(host, program, parsed.options, ngOptions);
|
||||
tsc.emit(metadataWriter, program);
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
return Promise.reject(e);
|
||||
}
|
||||
}
|
||||
|
||||
// CLI entry point
|
||||
if (require.main === module) {
|
||||
const args = require('minimist')(process.argv.slice(2));
|
||||
main(args.p || args.project || '.', args.basePath)
|
||||
.then(exitCode => process.exit(exitCode))
|
||||
.catch(e => {
|
||||
console.error(e.stack);
|
||||
console.error("Compilation failed");
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
184
modules/@angular/compiler_cli/src/reflector_host.ts
Normal file
184
modules/@angular/compiler_cli/src/reflector_host.ts
Normal file
@ -0,0 +1,184 @@
|
||||
import {StaticReflectorHost, StaticSymbol} from './static_reflector';
|
||||
import * as ts from 'typescript';
|
||||
import {MetadataCollector, ModuleMetadata} from 'ts-metadata-collector';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import {AngularCompilerOptions} from './codegen';
|
||||
import {ImportGenerator, AssetUrl} from './compiler_private';
|
||||
|
||||
const EXT = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
|
||||
const DTS = /\.d\.ts$/;
|
||||
|
||||
export class NodeReflectorHost implements StaticReflectorHost, ImportGenerator {
|
||||
private metadataCollector = new MetadataCollector();
|
||||
constructor(private program: ts.Program, private compilerHost: ts.CompilerHost,
|
||||
private options: ts.CompilerOptions, private ngOptions: AngularCompilerOptions) {}
|
||||
|
||||
angularImportLocations() {
|
||||
if (this.ngOptions.legacyPackageLayout) {
|
||||
return {
|
||||
coreDecorators: 'angular2/src/core/metadata',
|
||||
diDecorators: 'angular2/src/core/di/decorators',
|
||||
diMetadata: 'angular2/src/core/di/metadata',
|
||||
provider: 'angular2/src/core/di/provider'
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
coreDecorators: '@angular/core/src/metadata',
|
||||
diDecorators: '@angular/core/src/di/decorators',
|
||||
diMetadata: '@angular/core/src/di/metadata',
|
||||
provider: '@angular/core/src/di/provider'
|
||||
};
|
||||
}
|
||||
}
|
||||
private resolve(m: string, containingFile: string) {
|
||||
const resolved =
|
||||
ts.resolveModuleName(m, containingFile, this.options, this.compilerHost).resolvedModule;
|
||||
return resolved ? resolved.resolvedFileName : null;
|
||||
};
|
||||
|
||||
|
||||
private resolveAssetUrl(url: string, containingFile: string): string {
|
||||
let assetUrl = AssetUrl.parse(url);
|
||||
if (assetUrl) {
|
||||
return this.resolve(`${assetUrl.packageName}/${assetUrl.modulePath}`, containingFile);
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
* We want a moduleId that will appear in import statements in the generated code.
|
||||
* These need to be in a form that system.js can load, so absolute file paths don't work.
|
||||
* Relativize the paths by checking candidate prefixes of the absolute path, to see if
|
||||
* they are resolvable by the moduleResolution strategy from the CompilerHost.
|
||||
*/
|
||||
getImportPath(containingFile: string, importedFile: string) {
|
||||
importedFile = this.resolveAssetUrl(importedFile, containingFile);
|
||||
containingFile = this.resolveAssetUrl(containingFile, '');
|
||||
|
||||
// TODO(tbosch): if a file does not yet exist (because we compile it later),
|
||||
// we still need to create it so that the `resolve` method works!
|
||||
if (!this.compilerHost.fileExists(importedFile)) {
|
||||
if (this.ngOptions.trace) {
|
||||
console.log(`Generating empty file ${importedFile} to allow resolution of import`);
|
||||
}
|
||||
this.compilerHost.writeFile(importedFile, '', false);
|
||||
fs.writeFileSync(importedFile, '');
|
||||
}
|
||||
|
||||
const parts = importedFile.replace(EXT, '').split(path.sep).filter(p => !!p);
|
||||
|
||||
for (let index = parts.length - 1; index >= 0; index--) {
|
||||
let candidate = parts.slice(index, parts.length).join(path.sep);
|
||||
if (this.resolve('.' + path.sep + candidate, containingFile) === importedFile) {
|
||||
return `./${candidate}`;
|
||||
}
|
||||
if (this.resolve(candidate, containingFile) === importedFile) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
throw new Error(
|
||||
`Unable to find any resolvable import for ${importedFile} relative to ${containingFile}`);
|
||||
}
|
||||
|
||||
findDeclaration(module: string, symbolName: string, containingFile: string,
|
||||
containingModule?: string): StaticSymbol {
|
||||
if (!containingFile || !containingFile.length) {
|
||||
if (module.indexOf(".") === 0) {
|
||||
throw new Error("Resolution of relative paths requires a containing file.");
|
||||
}
|
||||
// Any containing file gives the same result for absolute imports
|
||||
containingFile = path.join(this.ngOptions.basePath, 'index.ts');
|
||||
}
|
||||
|
||||
try {
|
||||
const filePath = this.resolve(module, containingFile);
|
||||
|
||||
if (!filePath) {
|
||||
throw new Error(`Could not resolve module ${module} relative to ${containingFile}`);
|
||||
}
|
||||
|
||||
const tc = this.program.getTypeChecker();
|
||||
const sf = this.program.getSourceFile(filePath);
|
||||
|
||||
let symbol = tc.getExportsOfModule((<any>sf).symbol).find(m => m.name === symbolName);
|
||||
if (!symbol) {
|
||||
throw new Error(`can't find symbol ${symbolName} exported from module ${filePath}`);
|
||||
}
|
||||
if (symbol &&
|
||||
symbol.flags & ts.SymbolFlags.Alias) { // This is an alias, follow what it aliases
|
||||
symbol = tc.getAliasedSymbol(symbol);
|
||||
}
|
||||
const declaration = symbol.getDeclarations()[0];
|
||||
const declarationFile = declaration.getSourceFile().fileName;
|
||||
|
||||
return this.getStaticSymbol(declarationFile, symbol.getName());
|
||||
} catch (e) {
|
||||
console.error(`can't resolve module ${module} from ${containingFile}`);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private typeCache = new Map<string, StaticSymbol>();
|
||||
|
||||
/**
|
||||
* getStaticSymbol produces a Type whose metadata is known but whose implementation is not loaded.
|
||||
* All types passed to the StaticResolver should be pseudo-types returned by this method.
|
||||
*
|
||||
* @param declarationFile the absolute path of the file where the symbol is declared
|
||||
* @param name the name of the type.
|
||||
*/
|
||||
getStaticSymbol(declarationFile: string, name: string): StaticSymbol {
|
||||
let key = `"${declarationFile}".${name}`;
|
||||
let result = this.typeCache.get(key);
|
||||
if (!result) {
|
||||
result = new StaticSymbol(declarationFile, name);
|
||||
this.typeCache.set(key, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// TODO(alexeagle): take a statictype
|
||||
getMetadataFor(filePath: string): ModuleMetadata {
|
||||
if (!fs.existsSync(filePath)) {
|
||||
throw new Error(`No such file '${filePath}'`);
|
||||
}
|
||||
if (DTS.test(filePath)) {
|
||||
const metadataPath = filePath.replace(DTS, '.metadata.json');
|
||||
if (fs.existsSync(metadataPath)) {
|
||||
return this.readMetadata(metadataPath);
|
||||
}
|
||||
}
|
||||
|
||||
let sf = this.program.getSourceFile(filePath);
|
||||
if (!sf) {
|
||||
throw new Error(`Source file ${filePath} not present in program.`);
|
||||
}
|
||||
const metadata = this.metadataCollector.getMetadata(sf, this.program.getTypeChecker());
|
||||
return metadata;
|
||||
}
|
||||
|
||||
readMetadata(filePath: string) {
|
||||
try {
|
||||
const result = JSON.parse(fs.readFileSync(filePath, {encoding: 'utf-8'}));
|
||||
return result;
|
||||
} catch (e) {
|
||||
console.error(`Failed to read JSON file ${filePath}`);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
writeMetadata(emitFilePath: string, sourceFile: ts.SourceFile) {
|
||||
// TODO: replace with DTS filePath when https://github.com/Microsoft/TypeScript/pull/8412 is
|
||||
// released
|
||||
if (/*DTS*/ /\.js$/.test(emitFilePath)) {
|
||||
const path = emitFilePath.replace(/*DTS*/ /\.js$/, '.metadata.json');
|
||||
const metadata =
|
||||
this.metadataCollector.getMetadata(sourceFile, this.program.getTypeChecker());
|
||||
if (metadata && metadata.metadata) {
|
||||
const metadataText = JSON.stringify(metadata);
|
||||
fs.writeFileSync(path, metadataText, {encoding: 'utf-8'});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
392
modules/@angular/compiler_cli/src/static_reflector.ts
Normal file
392
modules/@angular/compiler_cli/src/static_reflector.ts
Normal file
@ -0,0 +1,392 @@
|
||||
import {
|
||||
AttributeMetadata,
|
||||
DirectiveMetadata,
|
||||
ComponentMetadata,
|
||||
ContentChildrenMetadata,
|
||||
ContentChildMetadata,
|
||||
InputMetadata,
|
||||
HostBindingMetadata,
|
||||
HostListenerMetadata,
|
||||
OutputMetadata,
|
||||
PipeMetadata,
|
||||
ViewChildMetadata,
|
||||
ViewChildrenMetadata,
|
||||
ViewQueryMetadata,
|
||||
QueryMetadata,
|
||||
} from '@angular/core';
|
||||
import {ReflectorReader} from './core_private';
|
||||
import {Provider} from '@angular/core';
|
||||
import {
|
||||
HostMetadata,
|
||||
OptionalMetadata,
|
||||
InjectableMetadata,
|
||||
SelfMetadata,
|
||||
SkipSelfMetadata,
|
||||
InjectMetadata,
|
||||
} from "@angular/core";
|
||||
|
||||
/**
|
||||
* The host of the static resolver is expected to be able to provide module metadata in the form of
|
||||
* ModuleMetadata. Angular 2 CLI will produce this metadata for a module whenever a .d.ts files is
|
||||
* produced and the module has exported variables or classes with decorators. Module metadata can
|
||||
* also be produced directly from TypeScript sources by using MetadataCollector in tools/metadata.
|
||||
*/
|
||||
export interface StaticReflectorHost {
|
||||
/**
|
||||
* Return a ModuleMetadata for the given module.
|
||||
*
|
||||
* @param modulePath is a string identifier for a module as an absolute path.
|
||||
* @returns the metadata for the given module.
|
||||
*/
|
||||
getMetadataFor(modulePath: string): {[key: string]: any};
|
||||
|
||||
/**
|
||||
* Resolve a symbol from an import statement form, to the file where it is declared.
|
||||
* @param module the location imported from
|
||||
* @param containingFile for relative imports, the path of the file containing the import
|
||||
*/
|
||||
findDeclaration(modulePath: string, symbolName: string, containingFile?: string): StaticSymbol;
|
||||
|
||||
getStaticSymbol(declarationFile: string, name: string): StaticSymbol;
|
||||
|
||||
angularImportLocations():
|
||||
{coreDecorators: string, diDecorators: string, diMetadata: string, provider: string};
|
||||
}
|
||||
|
||||
/**
|
||||
* A token representing the a reference to a static type.
|
||||
*
|
||||
* This token is unique for a filePath and name and can be used as a hash table key.
|
||||
*/
|
||||
export class StaticSymbol {
|
||||
constructor(public filePath: string, public name: string) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* A static reflector implements enough of the Reflector API that is necessary to compile
|
||||
* templates statically.
|
||||
*/
|
||||
export class StaticReflector implements ReflectorReader {
|
||||
private annotationCache = new Map<StaticSymbol, any[]>();
|
||||
private propertyCache = new Map<StaticSymbol, {[key: string]: any}>();
|
||||
private parameterCache = new Map<StaticSymbol, any[]>();
|
||||
private metadataCache = new Map<string, {[key: string]: any}>();
|
||||
private conversionMap = new Map<StaticSymbol, (context: StaticSymbol, args: any[]) => any>();
|
||||
|
||||
constructor(private host: StaticReflectorHost) { this.initializeConversionMap(); }
|
||||
|
||||
importUri(typeOrFunc: any): string { return (<StaticSymbol>typeOrFunc).filePath; }
|
||||
|
||||
public annotations(type: StaticSymbol): any[] {
|
||||
let annotations = this.annotationCache.get(type);
|
||||
if (!annotations) {
|
||||
let classMetadata = this.getTypeMetadata(type);
|
||||
if (classMetadata['decorators']) {
|
||||
annotations = this.simplify(type, classMetadata['decorators']);
|
||||
} else {
|
||||
annotations = [];
|
||||
}
|
||||
this.annotationCache.set(type, annotations.filter(ann => !!ann));
|
||||
}
|
||||
return annotations;
|
||||
}
|
||||
|
||||
public propMetadata(type: StaticSymbol): {[key: string]: any} {
|
||||
let propMetadata = this.propertyCache.get(type);
|
||||
if (!propMetadata) {
|
||||
let classMetadata = this.getTypeMetadata(type);
|
||||
let members = classMetadata ? classMetadata['members'] : {};
|
||||
propMetadata = mapStringMap(members, (propData, propName) => {
|
||||
let prop = (<any[]>propData).find(a => a['__symbolic'] == 'property');
|
||||
if (prop && prop['decorators']) {
|
||||
return this.simplify(type, prop['decorators']);
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
});
|
||||
this.propertyCache.set(type, propMetadata);
|
||||
}
|
||||
return propMetadata;
|
||||
}
|
||||
|
||||
public parameters(type: StaticSymbol): any[] {
|
||||
if (!(type instanceof StaticSymbol)) {
|
||||
throw new Error(`parameters recieved ${JSON.stringify(type)} which is not a StaticSymbol`);
|
||||
}
|
||||
try {
|
||||
let parameters = this.parameterCache.get(type);
|
||||
if (!parameters) {
|
||||
let classMetadata = this.getTypeMetadata(type);
|
||||
let members = classMetadata ? classMetadata['members'] : null;
|
||||
let ctorData = members ? members['__ctor__'] : null;
|
||||
if (ctorData) {
|
||||
let ctor = (<any[]>ctorData).find(a => a['__symbolic'] == 'constructor');
|
||||
let parameterTypes = <any[]>this.simplify(type, ctor['parameters']);
|
||||
let parameterDecorators = <any[]>this.simplify(type, ctor['parameterDecorators']);
|
||||
|
||||
parameters = [];
|
||||
parameterTypes.forEach( (paramType, index) => {
|
||||
let nestedResult: any[] = [];
|
||||
if (paramType) {
|
||||
nestedResult.push(paramType);
|
||||
}
|
||||
let decorators = parameterDecorators ? parameterDecorators[index] : null;
|
||||
if (decorators) {
|
||||
nestedResult.push(...decorators);
|
||||
}
|
||||
parameters.push(nestedResult);
|
||||
});
|
||||
}
|
||||
if (!parameters) {
|
||||
parameters = [];
|
||||
}
|
||||
this.parameterCache.set(type, parameters);
|
||||
}
|
||||
return parameters;
|
||||
} catch (e) {
|
||||
console.log(`Failed on type ${JSON.stringify(type)} with error ${e}`);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private registerDecoratorOrConstructor(type: StaticSymbol, ctor: any): void {
|
||||
this.conversionMap.set(type, (context: StaticSymbol, args: any[]) => {
|
||||
let argValues: any[] = [];
|
||||
args.forEach( (arg, index) => {
|
||||
let argValue: any;
|
||||
if (typeof arg === 'object' && !arg['__symbolic']) {
|
||||
argValue = mapStringMap(arg, (value, key) => this.simplify(context, value));
|
||||
} else {
|
||||
argValue = this.simplify(context, arg);
|
||||
}
|
||||
argValues.push(argValue);
|
||||
});
|
||||
var metadata = Object.create(ctor.prototype);
|
||||
ctor.apply(metadata, argValues);
|
||||
return metadata;
|
||||
});
|
||||
}
|
||||
|
||||
private initializeConversionMap(): void {
|
||||
const {coreDecorators, diDecorators, diMetadata, provider} = this.host.angularImportLocations();
|
||||
this.registerDecoratorOrConstructor(this.host.findDeclaration(provider, 'Provider'), Provider);
|
||||
|
||||
this.registerDecoratorOrConstructor(this.host.findDeclaration(diDecorators, 'Host'),
|
||||
HostMetadata);
|
||||
this.registerDecoratorOrConstructor(this.host.findDeclaration(diDecorators, 'Injectable'),
|
||||
InjectableMetadata);
|
||||
this.registerDecoratorOrConstructor(this.host.findDeclaration(diDecorators, 'Self'),
|
||||
SelfMetadata);
|
||||
this.registerDecoratorOrConstructor(this.host.findDeclaration(diDecorators, 'SkipSelf'),
|
||||
SkipSelfMetadata);
|
||||
this.registerDecoratorOrConstructor(this.host.findDeclaration(diDecorators, 'Inject'),
|
||||
InjectMetadata);
|
||||
this.registerDecoratorOrConstructor(this.host.findDeclaration(diDecorators, 'Optional'),
|
||||
OptionalMetadata);
|
||||
this.registerDecoratorOrConstructor(this.host.findDeclaration(coreDecorators, 'Attribute'),
|
||||
AttributeMetadata);
|
||||
this.registerDecoratorOrConstructor(this.host.findDeclaration(coreDecorators, 'Query'),
|
||||
QueryMetadata);
|
||||
this.registerDecoratorOrConstructor(this.host.findDeclaration(coreDecorators, 'ViewQuery'),
|
||||
ViewQueryMetadata);
|
||||
this.registerDecoratorOrConstructor(this.host.findDeclaration(coreDecorators, 'ContentChild'),
|
||||
ContentChildMetadata);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(coreDecorators, 'ContentChildren'), ContentChildrenMetadata);
|
||||
this.registerDecoratorOrConstructor(this.host.findDeclaration(coreDecorators, 'ViewChild'),
|
||||
ViewChildMetadata);
|
||||
this.registerDecoratorOrConstructor(this.host.findDeclaration(coreDecorators, 'ViewChildren'),
|
||||
ViewChildrenMetadata);
|
||||
this.registerDecoratorOrConstructor(this.host.findDeclaration(coreDecorators, 'Input'),
|
||||
InputMetadata);
|
||||
this.registerDecoratorOrConstructor(this.host.findDeclaration(coreDecorators, 'Output'),
|
||||
OutputMetadata);
|
||||
this.registerDecoratorOrConstructor(this.host.findDeclaration(coreDecorators, 'Pipe'),
|
||||
PipeMetadata);
|
||||
this.registerDecoratorOrConstructor(this.host.findDeclaration(coreDecorators, 'HostBinding'),
|
||||
HostBindingMetadata);
|
||||
this.registerDecoratorOrConstructor(this.host.findDeclaration(coreDecorators, 'HostListener'),
|
||||
HostListenerMetadata);
|
||||
this.registerDecoratorOrConstructor(this.host.findDeclaration(coreDecorators, 'Directive'),
|
||||
DirectiveMetadata);
|
||||
this.registerDecoratorOrConstructor(this.host.findDeclaration(coreDecorators, 'Component'),
|
||||
ComponentMetadata);
|
||||
|
||||
// Note: Some metadata classes can be used directly with Provider.deps.
|
||||
this.registerDecoratorOrConstructor(this.host.findDeclaration(diMetadata, 'HostMetadata'),
|
||||
HostMetadata);
|
||||
this.registerDecoratorOrConstructor(this.host.findDeclaration(diMetadata, 'SelfMetadata'),
|
||||
SelfMetadata);
|
||||
this.registerDecoratorOrConstructor(this.host.findDeclaration(diMetadata, 'SkipSelfMetadata'),
|
||||
SkipSelfMetadata);
|
||||
this.registerDecoratorOrConstructor(this.host.findDeclaration(diMetadata, 'OptionalMetadata'),
|
||||
OptionalMetadata);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
public simplify(context: StaticSymbol, value: any): any {
|
||||
let _this = this;
|
||||
|
||||
function simplify(expression: any): any {
|
||||
if (isPrimitive(expression)) {
|
||||
return expression;
|
||||
}
|
||||
if (expression instanceof Array) {
|
||||
let result: any[] = [];
|
||||
for (let item of(<any>expression)) {
|
||||
result.push(simplify(item));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
if (expression) {
|
||||
if (expression['__symbolic']) {
|
||||
let staticSymbol: StaticSymbol;
|
||||
switch (expression['__symbolic']) {
|
||||
case "binop":
|
||||
let left = simplify(expression['left']);
|
||||
let right = simplify(expression['right']);
|
||||
switch (expression['operator']) {
|
||||
case '&&':
|
||||
return left && right;
|
||||
case '||':
|
||||
return left || right;
|
||||
case '|':
|
||||
return left | right;
|
||||
case '^':
|
||||
return left ^ right;
|
||||
case '&':
|
||||
return left & right;
|
||||
case '==':
|
||||
return left == right;
|
||||
case '!=':
|
||||
return left != right;
|
||||
case '===':
|
||||
return left === right;
|
||||
case '!==':
|
||||
return left !== right;
|
||||
case '<':
|
||||
return left < right;
|
||||
case '>':
|
||||
return left > right;
|
||||
case '<=':
|
||||
return left <= right;
|
||||
case '>=':
|
||||
return left >= right;
|
||||
case '<<':
|
||||
return left << right;
|
||||
case '>>':
|
||||
return left >> right;
|
||||
case '+':
|
||||
return left + right;
|
||||
case '-':
|
||||
return left - right;
|
||||
case '*':
|
||||
return left * right;
|
||||
case '/':
|
||||
return left / right;
|
||||
case '%':
|
||||
return left % right;
|
||||
}
|
||||
return null;
|
||||
case "pre":
|
||||
let operand = simplify(expression['operand']);
|
||||
switch (expression['operator']) {
|
||||
case '+':
|
||||
return operand;
|
||||
case '-':
|
||||
return -operand;
|
||||
case '!':
|
||||
return !operand;
|
||||
case '~':
|
||||
return ~operand;
|
||||
}
|
||||
return null;
|
||||
case "index":
|
||||
let indexTarget = simplify(expression['expression']);
|
||||
let index = simplify(expression['index']);
|
||||
if (indexTarget && isPrimitive(index)) return indexTarget[index];
|
||||
return null;
|
||||
case "select":
|
||||
let selectTarget = simplify(expression['expression']);
|
||||
let member = simplify(expression['member']);
|
||||
if (selectTarget && isPrimitive(member)) return selectTarget[member];
|
||||
return null;
|
||||
case "reference":
|
||||
if (expression['module']) {
|
||||
staticSymbol = _this.host.findDeclaration(expression['module'], expression['name'],
|
||||
context.filePath);
|
||||
} else {
|
||||
staticSymbol = _this.host.getStaticSymbol(context.filePath, expression['name']);
|
||||
}
|
||||
let result = staticSymbol;
|
||||
let moduleMetadata = _this.getModuleMetadata(staticSymbol.filePath);
|
||||
let declarationValue =
|
||||
moduleMetadata ? moduleMetadata['metadata'][staticSymbol.name] : null;
|
||||
if (declarationValue) {
|
||||
result = _this.simplify(staticSymbol, declarationValue);
|
||||
}
|
||||
return result;
|
||||
case "class":
|
||||
return context;
|
||||
case "new":
|
||||
case "call":
|
||||
let target = expression['expression'];
|
||||
staticSymbol =
|
||||
_this.host.findDeclaration(target['module'], target['name'], context.filePath);
|
||||
let converter = _this.conversionMap.get(staticSymbol);
|
||||
if (converter) {
|
||||
let args = expression['arguments'];
|
||||
if (!args) {
|
||||
args = [];
|
||||
}
|
||||
return converter(context, args);
|
||||
} else {
|
||||
return context;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return mapStringMap(expression, (value, name) => simplify(value));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
return simplify(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param module an absolute path to a module file.
|
||||
*/
|
||||
public getModuleMetadata(module: string): {[key: string]: any} {
|
||||
let moduleMetadata = this.metadataCache.get(module);
|
||||
if (!moduleMetadata) {
|
||||
moduleMetadata = this.host.getMetadataFor(module);
|
||||
if (!moduleMetadata) {
|
||||
moduleMetadata = {__symbolic: "module", module: module, metadata: {}};
|
||||
}
|
||||
this.metadataCache.set(module, moduleMetadata);
|
||||
}
|
||||
return moduleMetadata;
|
||||
}
|
||||
|
||||
private getTypeMetadata(type: StaticSymbol): {[key: string]: any} {
|
||||
let moduleMetadata = this.getModuleMetadata(type.filePath);
|
||||
let result = moduleMetadata['metadata'][type.name];
|
||||
if (!result) {
|
||||
result = {__symbolic: "class"};
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
function mapStringMap(input: {[key: string]: any},
|
||||
transform: (value: any, key: string) => any): {[key: string]: any} {
|
||||
if (!input) return {};
|
||||
var result: {[key: string]: any} = {};
|
||||
Object.keys(input).forEach((key) => { result[key] = transform(input[key], key); });
|
||||
return result;
|
||||
}
|
||||
|
||||
function isPrimitive(o: any):boolean {
|
||||
return o === null || (typeof o !== "function" && typeof o !== "object");
|
||||
}
|
104
modules/@angular/compiler_cli/src/tsc.ts
Normal file
104
modules/@angular/compiler_cli/src/tsc.ts
Normal file
@ -0,0 +1,104 @@
|
||||
import * as ts from 'typescript';
|
||||
// Don't import from fs in general, that's the CompilerHost's job
|
||||
import {lstatSync} from 'fs';
|
||||
import * as path from 'path';
|
||||
import {AngularCompilerOptions} from './codegen';
|
||||
import {TsickleHost} from './compiler_host';
|
||||
|
||||
/**
|
||||
* Our interface to the TypeScript standard compiler.
|
||||
* If you write an Angular compiler plugin for another build tool,
|
||||
* you should implement a similar interface.
|
||||
*/
|
||||
export interface CompilerInterface {
|
||||
readConfiguration(
|
||||
project: string,
|
||||
basePath: string): {parsed: ts.ParsedCommandLine, ngOptions: AngularCompilerOptions};
|
||||
typeCheck(compilerHost: ts.CompilerHost, program: ts.Program): void;
|
||||
emit(compilerHost: ts.CompilerHost, program: ts.Program): number;
|
||||
}
|
||||
|
||||
const DEBUG = false;
|
||||
const SOURCE_EXTENSION = /\.[jt]s$/;
|
||||
|
||||
function debug(msg: string, ...o: any[]) {
|
||||
if (DEBUG) console.log(msg, ...o);
|
||||
}
|
||||
|
||||
export function formatDiagnostics(diags: ts.Diagnostic[]): string {
|
||||
return diags.map((d) => {
|
||||
let res = ts.DiagnosticCategory[d.category];
|
||||
if (d.file) {
|
||||
res += ' at ' + d.file.fileName + ':';
|
||||
const {line, character} = d.file.getLineAndCharacterOfPosition(d.start);
|
||||
res += (line + 1) + ':' + (character + 1) + ':';
|
||||
}
|
||||
res += ' ' + ts.flattenDiagnosticMessageText(d.messageText, '\n');
|
||||
return res;
|
||||
})
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
export function check(diags: ts.Diagnostic[]) {
|
||||
if (diags && diags.length && diags[0]) {
|
||||
throw new Error(formatDiagnostics(diags));
|
||||
}
|
||||
}
|
||||
|
||||
export class Tsc implements CompilerInterface {
|
||||
public ngOptions: AngularCompilerOptions;
|
||||
public parsed: ts.ParsedCommandLine;
|
||||
private basePath: string;
|
||||
|
||||
readConfiguration(project: string, basePath: string) {
|
||||
this.basePath = basePath;
|
||||
|
||||
// Allow a directory containing tsconfig.json as the project value
|
||||
if (lstatSync(project).isDirectory()) {
|
||||
project = path.join(project, "tsconfig.json");
|
||||
}
|
||||
|
||||
const {config, error} = ts.readConfigFile(project, ts.sys.readFile);
|
||||
check([error]);
|
||||
|
||||
this.parsed =
|
||||
ts.parseJsonConfigFileContent(config, {readDirectory: ts.sys.readDirectory}, basePath);
|
||||
|
||||
check(this.parsed.errors);
|
||||
|
||||
// Default codegen goes to the current directory
|
||||
// Parsed options are already converted to absolute paths
|
||||
this.ngOptions = config.angularCompilerOptions || {};
|
||||
this.ngOptions.genDir = path.join(basePath, this.ngOptions.genDir || '.');
|
||||
return {parsed: this.parsed, ngOptions: this.ngOptions};
|
||||
}
|
||||
|
||||
typeCheck(compilerHost: ts.CompilerHost, oldProgram: ts.Program): void {
|
||||
// Create a new program since codegen files were created after making the old program
|
||||
const program =
|
||||
ts.createProgram(this.parsed.fileNames, this.parsed.options, compilerHost, oldProgram);
|
||||
debug("Checking global diagnostics...");
|
||||
check(program.getGlobalDiagnostics());
|
||||
|
||||
let diagnostics: ts.Diagnostic[] = [];
|
||||
debug("Type checking...");
|
||||
|
||||
for (let sf of program.getSourceFiles()) {
|
||||
diagnostics.push(...ts.getPreEmitDiagnostics(program, sf));
|
||||
}
|
||||
check(diagnostics);
|
||||
}
|
||||
|
||||
emit(compilerHost: TsickleHost, oldProgram: ts.Program): number {
|
||||
// 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);
|
||||
debug("Emitting outputs...");
|
||||
const emitResult = program.emit();
|
||||
let diagnostics: ts.Diagnostic[] = [];
|
||||
diagnostics.push(...emitResult.diagnostics);
|
||||
|
||||
check(compilerHost.diagnostics);
|
||||
return emitResult.emitSkipped ? 1 : 0;
|
||||
}
|
||||
}
|
||||
export var tsc: CompilerInterface = new Tsc();
|
425
modules/@angular/compiler_cli/test/static_reflector_spec.ts
Normal file
425
modules/@angular/compiler_cli/test/static_reflector_spec.ts
Normal file
@ -0,0 +1,425 @@
|
||||
import {
|
||||
describe,
|
||||
it,
|
||||
iit,
|
||||
expect,
|
||||
ddescribe,
|
||||
beforeEach
|
||||
} from '@angular/core/testing/testing_internal';
|
||||
import {isBlank} from '@angular/facade/src/lang';
|
||||
import {ListWrapper} from '@angular/facade/src/collection';
|
||||
|
||||
import {StaticReflector, StaticReflectorHost, StaticSymbol} from '@angular/compiler_cli/src/static_reflector';
|
||||
|
||||
describe('StaticReflector', () => {
|
||||
let noContext = new StaticSymbol('', '');
|
||||
let host: StaticReflectorHost;
|
||||
let reflector: StaticReflector;
|
||||
|
||||
beforeEach(() => {
|
||||
host = new MockReflectorHost();
|
||||
reflector = new StaticReflector(host);
|
||||
});
|
||||
|
||||
function simplify(context: StaticSymbol, value: any) {
|
||||
return reflector.simplify(context, value);
|
||||
}
|
||||
|
||||
it('should get annotations for NgFor', () => {
|
||||
let NgFor = host.findDeclaration('angular2/src/common/directives/ng_for', 'NgFor');
|
||||
let annotations = reflector.annotations(NgFor);
|
||||
expect(annotations.length).toEqual(1);
|
||||
let annotation = annotations[0];
|
||||
expect(annotation.selector).toEqual('[ngFor][ngForOf]');
|
||||
expect(annotation.inputs).toEqual(['ngForTrackBy', 'ngForOf', 'ngForTemplate']);
|
||||
});
|
||||
|
||||
it('should get constructor for NgFor', () => {
|
||||
let NgFor = host.findDeclaration('angular2/src/common/directives/ng_for', 'NgFor');
|
||||
let ViewContainerRef =
|
||||
host.findDeclaration('angular2/src/core/linker/view_container_ref', 'ViewContainerRef');
|
||||
let TemplateRef = host.findDeclaration('angular2/src/core/linker/template_ref', 'TemplateRef');
|
||||
let IterableDiffers = host.findDeclaration(
|
||||
'angular2/src/core/change_detection/differs/iterable_differs', 'IterableDiffers');
|
||||
let ChangeDetectorRef = host.findDeclaration(
|
||||
'angular2/src/core/change_detection/change_detector_ref', 'ChangeDetectorRef');
|
||||
|
||||
let parameters = reflector.parameters(NgFor);
|
||||
expect(parameters)
|
||||
.toEqual([[ViewContainerRef], [TemplateRef], [IterableDiffers], [ChangeDetectorRef]]);
|
||||
});
|
||||
|
||||
it('should get annotations for HeroDetailComponent', () => {
|
||||
let HeroDetailComponent =
|
||||
host.findDeclaration('src/app/hero-detail.component', 'HeroDetailComponent');
|
||||
let annotations = reflector.annotations(HeroDetailComponent);
|
||||
expect(annotations.length).toEqual(1);
|
||||
let annotation = annotations[0];
|
||||
expect(annotation.selector).toEqual('my-hero-detail');
|
||||
expect(annotation.directives)
|
||||
.toEqual([[host.findDeclaration('angular2/src/common/directives/ng_for', 'NgFor')]]);
|
||||
});
|
||||
|
||||
it('should get and empty annotation list for an unknown class', () => {
|
||||
let UnknownClass = host.findDeclaration('src/app/app.component', 'UnknownClass');
|
||||
let annotations = reflector.annotations(UnknownClass);
|
||||
expect(annotations).toEqual([]);
|
||||
});
|
||||
|
||||
it('should get propMetadata for HeroDetailComponent', () => {
|
||||
let HeroDetailComponent =
|
||||
host.findDeclaration('src/app/hero-detail.component', 'HeroDetailComponent');
|
||||
let props = reflector.propMetadata(HeroDetailComponent);
|
||||
expect(props['hero']).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should get an empty object from propMetadata for an unknown class', () => {
|
||||
let UnknownClass = host.findDeclaration('src/app/app.component', 'UnknownClass');
|
||||
let properties = reflector.propMetadata(UnknownClass);
|
||||
expect(properties).toEqual({});
|
||||
});
|
||||
|
||||
it('should get empty parameters list for an unknown class ', () => {
|
||||
let UnknownClass = host.findDeclaration('src/app/app.component', 'UnknownClass');
|
||||
let parameters = reflector.parameters(UnknownClass);
|
||||
expect(parameters).toEqual([]);
|
||||
});
|
||||
|
||||
it('should simplify primitive into itself', () => {
|
||||
expect(simplify(noContext, 1)).toBe(1);
|
||||
expect(simplify(noContext, true)).toBe(true);
|
||||
expect(simplify(noContext, "some value")).toBe("some value");
|
||||
});
|
||||
|
||||
it('should simplify an array into a copy of the array',
|
||||
() => { expect(simplify(noContext, [1, 2, 3])).toEqual([1, 2, 3]); });
|
||||
|
||||
it('should simplify an object to a copy of the object', () => {
|
||||
let expr = {a: 1, b: 2, c: 3};
|
||||
expect(simplify(noContext, expr)).toEqual(expr);
|
||||
});
|
||||
|
||||
it('should simplify &&', () => {
|
||||
expect(simplify(noContext, ({ __symbolic: 'binop', operator: '&&', left: true, right: true}))).toBe(true);
|
||||
expect(simplify(noContext, ({ __symbolic: 'binop', operator: '&&', left: true, right: false}))).toBe(false);
|
||||
expect(simplify(noContext, ({ __symbolic: 'binop', operator: '&&', left: false, right: true}))).toBe(false);
|
||||
expect(simplify(noContext, ({ __symbolic: 'binop', operator: '&&', left: false, right: false}))).toBe(false);
|
||||
});
|
||||
|
||||
it('should simplify ||', () => {
|
||||
expect(simplify(noContext, ({ __symbolic: 'binop', operator: '||', left: true, right: true}))).toBe(true);
|
||||
expect(simplify(noContext, ({ __symbolic: 'binop', operator: '||', left: true, right: false}))).toBe(true);
|
||||
expect(simplify(noContext, ({ __symbolic: 'binop', operator: '||', left: false, right: true}))).toBe(true);
|
||||
expect(simplify(noContext, ({ __symbolic: 'binop', operator: '||', left: false, right: false}))).toBe(false);
|
||||
});
|
||||
|
||||
it('should simplify &', () => {
|
||||
expect(simplify(noContext, ({ __symbolic: 'binop', operator: '&', left: 0x22, right: 0x0F}))).toBe(0x22 & 0x0F);
|
||||
expect(simplify(noContext, ({ __symbolic: 'binop', operator: '&', left: 0x22, right: 0xF0}))).toBe(0x22 & 0xF0);
|
||||
});
|
||||
|
||||
it('should simplify |', () => {
|
||||
expect(simplify(noContext, ({ __symbolic: 'binop', operator: '|', left: 0x22, right: 0x0F}))).toBe(0x22 | 0x0F);
|
||||
expect(simplify(noContext, ({ __symbolic: 'binop', operator: '|', left: 0x22, right: 0xF0}))).toBe(0x22 | 0xF0);
|
||||
});
|
||||
|
||||
it('should simplify ^', () => {
|
||||
expect(simplify(noContext, ({ __symbolic: 'binop', operator: '|', left: 0x22, right: 0x0F}))).toBe(0x22 | 0x0F);
|
||||
expect(simplify(noContext, ({ __symbolic: 'binop', operator: '|', left: 0x22, right: 0xF0}))).toBe(0x22 | 0xF0);
|
||||
});
|
||||
|
||||
it('should simplify ==', () => {
|
||||
expect(simplify(noContext, ({ __symbolic: 'binop', operator: '==', left: 0x22, right: 0x22}))).toBe(0x22 == 0x22);
|
||||
expect(simplify(noContext, ({ __symbolic: 'binop', operator: '==', left: 0x22, right: 0xF0}))).toBe(0x22 == 0xF0);
|
||||
});
|
||||
|
||||
it('should simplify !=', () => {
|
||||
expect(simplify(noContext, ({ __symbolic: 'binop', operator: '!=', left: 0x22, right: 0x22}))).toBe(0x22 != 0x22);
|
||||
expect(simplify(noContext, ({ __symbolic: 'binop', operator: '!=', left: 0x22, right: 0xF0}))).toBe(0x22 != 0xF0);
|
||||
});
|
||||
|
||||
it('should simplify ===', () => {
|
||||
expect(simplify(noContext, ({ __symbolic: 'binop', operator: '===', left: 0x22, right: 0x22}))).toBe(0x22 === 0x22);
|
||||
expect(simplify(noContext, ({ __symbolic: 'binop', operator: '===', left: 0x22, right: 0xF0}))).toBe(0x22 === 0xF0);
|
||||
});
|
||||
|
||||
it('should simplify !==', () => {
|
||||
expect(simplify(noContext, ({ __symbolic: 'binop', operator: '!==', left: 0x22, right: 0x22}))).toBe(0x22 !== 0x22);
|
||||
expect(simplify(noContext, ({ __symbolic: 'binop', operator: '!==', left: 0x22, right: 0xF0}))).toBe(0x22 !== 0xF0);
|
||||
});
|
||||
|
||||
it('should simplify >', () => {
|
||||
expect(simplify(noContext, ({ __symbolic: 'binop', operator: '>', left: 1, right: 1}))).toBe(1 > 1);
|
||||
expect(simplify(noContext, ({ __symbolic: 'binop', operator: '>', left: 1, right: 0}))).toBe(1 > 0);
|
||||
expect(simplify(noContext, ({ __symbolic: 'binop', operator: '>', left: 0, right: 1}))).toBe(0 > 1);
|
||||
});
|
||||
|
||||
it('should simplify >=', () => {
|
||||
expect(simplify(noContext, ({ __symbolic: 'binop', operator: '>=', left: 1, right: 1}))).toBe(1 >= 1);
|
||||
expect(simplify(noContext, ({ __symbolic: 'binop', operator: '>=', left: 1, right: 0}))).toBe(1 >= 0);
|
||||
expect(simplify(noContext, ({ __symbolic: 'binop', operator: '>=', left: 0, right: 1}))).toBe(0 >= 1);
|
||||
});
|
||||
|
||||
it('should simplify <=', () => {
|
||||
expect(simplify(noContext, ({ __symbolic: 'binop', operator: '<=', left: 1, right: 1}))).toBe(1 <= 1);
|
||||
expect(simplify(noContext, ({ __symbolic: 'binop', operator: '<=', left: 1, right: 0}))).toBe(1 <= 0);
|
||||
expect(simplify(noContext, ({ __symbolic: 'binop', operator: '<=', left: 0, right: 1}))).toBe(0 <= 1);
|
||||
});
|
||||
|
||||
it('should simplify <', () => {
|
||||
expect(simplify(noContext, ({ __symbolic: 'binop', operator: '<', left: 1, right: 1}))).toBe(1 < 1);
|
||||
expect(simplify(noContext, ({ __symbolic: 'binop', operator: '<', left: 1, right: 0}))).toBe(1 < 0);
|
||||
expect(simplify(noContext, ({ __symbolic: 'binop', operator: '<', left: 0, right: 1}))).toBe(0 < 1);
|
||||
});
|
||||
|
||||
it('should simplify <<', () => {
|
||||
expect(simplify(noContext, ({ __symbolic: 'binop', operator: '<<', left: 0x55, right: 2}))).toBe(0x55 << 2);
|
||||
});
|
||||
|
||||
it('should simplify >>', () => {
|
||||
expect(simplify(noContext, ({ __symbolic: 'binop', operator: '>>', left: 0x55, right: 2}))).toBe(0x55 >> 2);
|
||||
});
|
||||
|
||||
it('should simplify +', () => {
|
||||
expect(simplify(noContext, ({ __symbolic: 'binop', operator: '+', left: 0x55, right: 2}))).toBe(0x55 + 2);
|
||||
});
|
||||
|
||||
it('should simplify -', () => {
|
||||
expect(simplify(noContext, ({ __symbolic: 'binop', operator: '-', left: 0x55, right: 2}))).toBe(0x55 - 2);
|
||||
});
|
||||
|
||||
it('should simplify *', () => {
|
||||
expect(simplify(noContext, ({ __symbolic: 'binop', operator: '*', left: 0x55, right: 2}))).toBe(0x55 * 2);
|
||||
});
|
||||
|
||||
it('should simplify /', () => {
|
||||
expect(simplify(noContext, ({ __symbolic: 'binop', operator: '/', left: 0x55, right: 2}))).toBe(0x55 / 2);
|
||||
});
|
||||
|
||||
it('should simplify %', () => {
|
||||
expect(simplify(noContext, ({ __symbolic: 'binop', operator: '%', left: 0x55, right: 2}))).toBe(0x55 % 2);
|
||||
});
|
||||
|
||||
it('should simplify prefix -', () => {
|
||||
expect(simplify(noContext, ({ __symbolic: 'pre', operator: '-', operand: 2}))).toBe(-2);
|
||||
});
|
||||
|
||||
it('should simplify prefix ~', () => {
|
||||
expect(simplify(noContext, ({ __symbolic: 'pre', operator: '~', operand: 2}))).toBe(~2);
|
||||
});
|
||||
|
||||
it('should simplify prefix !', () => {
|
||||
expect(simplify(noContext, ({ __symbolic: 'pre', operator: '!', operand: true}))).toBe(!true);
|
||||
expect(simplify(noContext, ({ __symbolic: 'pre', operator: '!', operand: false}))).toBe(!false);
|
||||
});
|
||||
|
||||
it('should simplify an array index', () => {
|
||||
expect(simplify(noContext, ({__symbolic: "index", expression: [1, 2, 3], index: 2}))).toBe(3);
|
||||
});
|
||||
|
||||
it('should simplify an object index', () => {
|
||||
let expr = {__symbolic: "select", expression: {a: 1, b: 2, c: 3}, member: "b"};
|
||||
expect(simplify(noContext, expr)).toBe(2);
|
||||
});
|
||||
|
||||
it('should simplify a module reference', () => {
|
||||
expect(simplify(new StaticSymbol('/src/cases', ''),
|
||||
({__symbolic: "reference", module: "./extern", name: "s"})))
|
||||
.toEqual("s");
|
||||
});
|
||||
|
||||
it('should simplify a non existing reference as a static symbol', () => {
|
||||
expect(simplify(new StaticSymbol('/src/cases', ''),
|
||||
({__symbolic: "reference", module: "./extern", name: "nonExisting"})))
|
||||
.toEqual(host.getStaticSymbol('/src/extern.d.ts', 'nonExisting'));
|
||||
});
|
||||
});
|
||||
|
||||
class MockReflectorHost implements StaticReflectorHost {
|
||||
private staticTypeCache = new Map<string, StaticSymbol>();
|
||||
|
||||
angularImportLocations() {
|
||||
return {
|
||||
coreDecorators: 'angular2/src/core/metadata',
|
||||
diDecorators: 'angular2/src/core/di/decorators',
|
||||
diMetadata: 'angular2/src/core/di/metadata',
|
||||
provider: 'angular2/src/core/di/provider'
|
||||
};
|
||||
}
|
||||
getStaticSymbol(declarationFile: string, name: string): StaticSymbol {
|
||||
var cacheKey = `${declarationFile}:${name}`;
|
||||
var result = this.staticTypeCache.get(cacheKey);
|
||||
if (isBlank(result)) {
|
||||
result = new StaticSymbol(declarationFile, name);
|
||||
this.staticTypeCache.set(cacheKey, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// In tests, assume that symbols are not re-exported
|
||||
findDeclaration(modulePath: string, symbolName: string, containingFile?: string): StaticSymbol {
|
||||
function splitPath(path: string): string[] { return path.split(/\/|\\/g); }
|
||||
|
||||
function resolvePath(pathParts: string[]): string {
|
||||
let result: string[] = [];
|
||||
ListWrapper.forEachWithIndex(pathParts, (part, index) => {
|
||||
switch (part) {
|
||||
case '':
|
||||
case '.':
|
||||
if (index > 0) return;
|
||||
break;
|
||||
case '..':
|
||||
if (index > 0 && result.length != 0) result.pop();
|
||||
return;
|
||||
}
|
||||
result.push(part);
|
||||
});
|
||||
return result.join('/');
|
||||
}
|
||||
|
||||
function pathTo(from: string, to: string): string {
|
||||
let result = to;
|
||||
if (to.startsWith('.')) {
|
||||
let fromParts = splitPath(from);
|
||||
fromParts.pop(); // remove the file name.
|
||||
let toParts = splitPath(to);
|
||||
result = resolvePath(fromParts.concat(toParts));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
if (modulePath.indexOf('.') === 0) {
|
||||
return this.getStaticSymbol(pathTo(containingFile, modulePath) + '.d.ts', symbolName);
|
||||
}
|
||||
return this.getStaticSymbol('/tmp/' + modulePath + '.d.ts', symbolName);
|
||||
}
|
||||
|
||||
getMetadataFor(moduleId: string): any {
|
||||
let data: {[key: string]: any} = {
|
||||
'/tmp/angular2/src/common/forms/directives.d.ts': {
|
||||
"__symbolic": "module",
|
||||
"metadata": {
|
||||
"FORM_DIRECTIVES": [
|
||||
{
|
||||
"__symbolic": "reference",
|
||||
"name": "NgFor",
|
||||
"module": "angular2/src/common/directives/ng_for"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
'/tmp/angular2/src/common/directives/ng_for.d.ts': {
|
||||
"__symbolic": "module",
|
||||
"metadata": {
|
||||
"NgFor": {
|
||||
"__symbolic": "class",
|
||||
"decorators": [
|
||||
{
|
||||
"__symbolic": "call",
|
||||
"expression": {
|
||||
"__symbolic": "reference",
|
||||
"name": "Directive",
|
||||
"module": "../../core/metadata"
|
||||
},
|
||||
"arguments": [
|
||||
{
|
||||
"selector": "[ngFor][ngForOf]",
|
||||
"inputs": ["ngForTrackBy", "ngForOf", "ngForTemplate"]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"members": {
|
||||
"__ctor__": [
|
||||
{
|
||||
"__symbolic": "constructor",
|
||||
"parameters": [
|
||||
{
|
||||
"__symbolic": "reference",
|
||||
"module": "../../core/linker/view_container_ref",
|
||||
"name": "ViewContainerRef"
|
||||
},
|
||||
{
|
||||
"__symbolic": "reference",
|
||||
"module": "../../core/linker/template_ref",
|
||||
"name": "TemplateRef"
|
||||
},
|
||||
{
|
||||
"__symbolic": "reference",
|
||||
"module": "../../core/change_detection/differs/iterable_differs",
|
||||
"name": "IterableDiffers"
|
||||
},
|
||||
{
|
||||
"__symbolic": "reference",
|
||||
"module": "../../core/change_detection/change_detector_ref",
|
||||
"name": "ChangeDetectorRef"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'/tmp/angular2/src/core/linker/view_container_ref.d.ts':
|
||||
{"metadata": {"ViewContainerRef": {"__symbolic": "class"}}},
|
||||
'/tmp/angular2/src/core/linker/template_ref.d.ts':
|
||||
{"module": "./template_ref", "metadata": {"TemplateRef": {"__symbolic": "class"}}},
|
||||
'/tmp/angular2/src/core/change_detection/differs/iterable_differs.d.ts':
|
||||
{"metadata": {"IterableDiffers": {"__symbolic": "class"}}},
|
||||
'/tmp/angular2/src/core/change_detection/change_detector_ref.d.ts':
|
||||
{"metadata": {"ChangeDetectorRef": {"__symbolic": "class"}}},
|
||||
'/tmp/src/app/hero-detail.component.d.ts': {
|
||||
"__symbolic": "module",
|
||||
"metadata": {
|
||||
"HeroDetailComponent": {
|
||||
"__symbolic": "class",
|
||||
"decorators": [
|
||||
{
|
||||
"__symbolic": "call",
|
||||
"expression": {
|
||||
"__symbolic": "reference",
|
||||
"name": "Component",
|
||||
"module": "angular2/src/core/metadata"
|
||||
},
|
||||
"arguments": [
|
||||
{
|
||||
"selector": "my-hero-detail",
|
||||
"template":
|
||||
"\n <div *ngIf=\"hero\">\n <h2>{{hero.name}} details!</h2>\n <div><label>id: </label>{{hero.id}}</div>\n <div>\n <label>name: </label>\n <input [(ngModel)]=\"hero.name\" placeholder=\"name\"/>\n </div>\n </div>\n",
|
||||
"directives": [
|
||||
{
|
||||
"__symbolic": "reference",
|
||||
"name": "FORM_DIRECTIVES",
|
||||
"module": "angular2/src/common/forms/directives"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"members": {
|
||||
"hero": [
|
||||
{
|
||||
"__symbolic": "property",
|
||||
"decorators": [
|
||||
{
|
||||
"__symbolic": "call",
|
||||
"expression": {
|
||||
"__symbolic": "reference",
|
||||
"name": "Input",
|
||||
"module": "angular2/src/core/metadata"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'/src/extern.d.ts': {"__symbolic": "module", metadata: {s: "s"}}
|
||||
};
|
||||
return data[moduleId];
|
||||
}
|
||||
}
|
34
modules/@angular/compiler_cli/tsconfig-es5.json
Normal file
34
modules/@angular/compiler_cli/tsconfig-es5.json
Normal file
@ -0,0 +1,34 @@
|
||||
{
|
||||
"angularCompilerOptions": {
|
||||
"skipTemplateCodegen": true
|
||||
},
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es5",
|
||||
"lib": ["es6", "dom"],
|
||||
"noImplicitAny": true,
|
||||
"sourceMap": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@angular/core": ["../../../dist/packages-dist/core"],
|
||||
"@angular/common": ["../../../dist/packages-dist/common"],
|
||||
"@angular/compiler": ["../../../dist/packages-dist/compiler"],
|
||||
"@angular/platform-server": ["../../../dist/packages-dist/platform-server"],
|
||||
"@angular/platform-browser": ["../../../dist/packages-dist/platform-browser"],
|
||||
"ts-metadata-collector": ["dist/tools/metadata"]
|
||||
},
|
||||
"experimentalDecorators": true,
|
||||
"rootDir": ".",
|
||||
"sourceRoot": ".",
|
||||
"outDir": "../../../dist/packages-dist/compiler_cli",
|
||||
"declaration": true
|
||||
},
|
||||
"exclude": ["integrationtest"],
|
||||
"files": [
|
||||
"index.ts",
|
||||
"src/main.ts",
|
||||
"../typings/node/node.d.ts",
|
||||
"../typings/jasmine/jasmine.d.ts",
|
||||
"../../../node_modules/zone.js/dist/zone.js.d.ts"
|
||||
]
|
||||
}
|
Reference in New Issue
Block a user