Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,14 @@
"@types/base64-js": "1.2.5",
"@types/chai": "^4.1.2",
"@types/chokidar": "1.7.3",
"@types/convert-source-map": "^1.5.1",
"@types/diff": "^3.2.2",
"@types/fs-extra": "4.0.2",
"@types/hammerjs": "2.0.35",
"@types/jasmine": "^2.8.8",
"@types/jasminewd2": "^2.0.3",
"@types/minimist": "^1.2.0",
"@types/mock-fs": "^3.6.30",
"@types/node": "6.0.88",
"@types/selenium-webdriver": "3.0.7",
"@types/shelljs": "^0.7.8",
Expand All @@ -72,6 +74,7 @@
"cldr-data-downloader": "0.3.2",
"cldrjs": "0.5.0",
"conventional-changelog": "1.1.0",
"convert-source-map": "^1.5.1",
"cors": "2.8.4",
"diff": "^3.5.0",
"domino": "2.0.1",
Expand All @@ -97,7 +100,9 @@
"karma-sauce-launcher": "^1.2.0",
"karma-sourcemap-loader": "^0.3.7",
"madge": "0.5.0",
"magic-string": "^0.25.0",
"minimist": "1.2.0",
"mock-fs": "^4.5.0",
"mutation-observer": "^1.0.3",
"node-uuid": "1.4.8",
"protobufjs": "5.0.0",
Expand All @@ -110,7 +115,7 @@
"selenium-webdriver": "3.5.0",
"semver": "5.4.1",
"shelljs": "^0.8.1",
"source-map": "0.5.7",
"source-map": "^0.6.1",
"source-map-support": "0.4.18",
"systemjs": "0.18.10",
"tsickle": "0.32",
Expand Down
5 changes: 4 additions & 1 deletion packages/compiler-cli/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,8 @@ npm_package(
"ivy-local",
"release-with-framework",
],
deps = [":compiler-cli"],
deps = [
":compiler-cli",
"//packages/compiler-cli/src/ngcc",
],
)
6 changes: 5 additions & 1 deletion packages/compiler-cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,18 @@
"main": "index.js",
"typings": "index.d.ts",
"bin": {
"ivy-ngcc": "./src/ngcc/main-ngcc.js",
"ngc": "./src/main.js",
"ng-xi18n": "./src/extract_i18n.js"
},
"dependencies": {
"reflect-metadata": "^0.1.2",
"minimist": "^1.2.0",
"tsickle": "^0.32.1",
"chokidar": "^1.4.2"
"chokidar": "^1.4.2",
"convert-source-map": "^1.5.1",
"magic-string": "^0.25.0",
"source-map": "^0.6.1"
},
"peerDependencies": {
"typescript": ">=2.7.2 <2.10",
Expand Down
21 changes: 21 additions & 0 deletions packages/compiler-cli/src/ngcc/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package(default_visibility = ["//visibility:public"])

load("//tools:defaults.bzl", "ts_library")
load("@build_bazel_rules_nodejs//:defs.bzl", "jasmine_node_test")

ts_library(
name = "ngcc",
srcs = glob([
"*.ts",
"**/*.ts",
]),
module_name = "@angular/compiler-cli/src/ngcc",
deps = [
"//packages:types",
"//packages/compiler",
"//packages/compiler-cli/src/ngtsc/annotations",
"//packages/compiler-cli/src/ngtsc/host",
"//packages/compiler-cli/src/ngtsc/metadata",
"//packages/compiler-cli/src/ngtsc/transform",
],
)
30 changes: 30 additions & 0 deletions packages/compiler-cli/src/ngcc/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Angular Compatibility Compiler (ngcc)

This compiler will convert `node_modules` compiled with `ngc`, into `node_modules` which
appear to have been compiled with `ngtsc`.

This conversion will allow such "legacy" packages to be used by the Ivy rendering engine.

## Building

The project is built using Bazel:

```bash
bazel build //packages/compiler-cli/src/ngcc
```

## Unit Testing

The unit tests are built and run using Bazel:

```bash
bazel test //packages/compiler-cli/src/ngcc/test
```

## Integration Testing

There are tests that check the behaviour of the overall executable:

```bash
bazel test //packages/compiler-cli/test/ngcc
```
9 changes: 9 additions & 0 deletions packages/compiler-cli/src/ngcc/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/

export {mainNgcc} from './src/main';
16 changes: 16 additions & 0 deletions packages/compiler-cli/src/ngcc/main-ngcc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/usr/bin/env node
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/

import {mainNgcc} from './src/main';

// CLI entry point
if (require.main === module) {
const args = process.argv.slice(2);
process.exitCode = mainNgcc(args);
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need both index.ts and main-ngcc.ts?
Couldn't we just export mainNgcc from main-ngcc.ts?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure. This is how Alex set it up for us.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mirrored the ngc setup. I think the goal there was to have a side-effect free import and a side effectful entrypoint.

97 changes: 97 additions & 0 deletions packages/compiler-cli/src/ngcc/src/analyzer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import * as fs from 'fs';
import * as ts from 'typescript';
import {ComponentDecoratorHandler, DirectiveDecoratorHandler, InjectableDecoratorHandler, NgModuleDecoratorHandler, PipeDecoratorHandler, ResourceLoader, SelectorScopeRegistry} from '../../ngtsc/annotations';
import {Decorator} from '../../ngtsc/host';
import {CompileResult, DecoratorHandler} from '../../ngtsc/transform';
import {NgccReflectionHost} from './host/ngcc_host';
import {ParsedClass} from './parsing/parsed_class';
import {ParsedFile} from './parsing/parsed_file';
import {isDefined} from './utils';

export interface AnalyzedClass<T = any> extends ParsedClass {
handler: DecoratorHandler<T>;
analysis: any;
diagnostics?: ts.Diagnostic[];
compilation: CompileResult[];
}

export interface AnalyzedFile {
analyzedClasses: AnalyzedClass[];
sourceFile: ts.SourceFile;
}

export interface MatchingHandler<T> {
handler: DecoratorHandler<T>;
decorator: Decorator;
}

/**
* `ResourceLoader` which directly uses the filesystem to resolve resources synchronously.
*/
export class FileResourceLoader implements ResourceLoader {
load(url: string): string { return fs.readFileSync(url, 'utf8'); }
}

export class Analyzer {
resourceLoader = new FileResourceLoader();
scopeRegistry = new SelectorScopeRegistry(this.typeChecker, this.host);
handlers: DecoratorHandler<any>[] = [
new ComponentDecoratorHandler(
this.typeChecker, this.host, this.scopeRegistry, false, this.resourceLoader),
new DirectiveDecoratorHandler(this.typeChecker, this.host, this.scopeRegistry, false),
new InjectableDecoratorHandler(this.host, false),
new NgModuleDecoratorHandler(this.typeChecker, this.host, this.scopeRegistry, false),
new PipeDecoratorHandler(this.typeChecker, this.host, this.scopeRegistry, false),
];

constructor(private typeChecker: ts.TypeChecker, private host: NgccReflectionHost) {}

/**
* Analyize a parsed file to generate the information about decorated classes that
* should be converted to use ivy definitions.
* @param file The file to be analysed for decorated classes.
*/
analyzeFile(file: ParsedFile): AnalyzedFile {
const analyzedClasses =
file.decoratedClasses.map(clazz => this.analyzeClass(file.sourceFile, clazz))
.filter(isDefined);

return {
analyzedClasses,
sourceFile: file.sourceFile,
};
}

protected analyzeClass(file: ts.SourceFile, clazz: ParsedClass): AnalyzedClass|undefined {
const matchingHandlers =
this.handlers.map(handler => ({handler, decorator: handler.detect(clazz.decorators)}))
.filter(isMatchingHandler);

if (matchingHandlers.length > 1) {
throw new Error('TODO.Diagnostic: Class has multiple Angular decorators.');
}

if (matchingHandlers.length == 0) {
return undefined;
}

const {handler, decorator} = matchingHandlers[0];
const {analysis, diagnostics} = handler.analyze(clazz.declaration, decorator);
let compilation = handler.compile(clazz.declaration, analysis);
if (!Array.isArray(compilation)) {
compilation = [compilation];
}
return {...clazz, handler, analysis, diagnostics, compilation};
}
}

function isMatchingHandler<T>(handler: Partial<MatchingHandler<T>>): handler is MatchingHandler<T> {
return !!handler.decorator;
}
Loading