refactor(compiler-cli): use the transformer based compiler by default
The source map does not currently work with the transformer pipeline. It will be re-enabled after TypeScript 2.4 is made the min version. To revert to the former compiler, use the `disableTransformerPipeline` in tsconfig.json: ``` { "angularCompilerOptions": { "disableTransformerPipeline": true } } ```
This commit is contained in:
parent
06faac8b5c
commit
679608db65
|
@ -13,7 +13,7 @@
|
|||
"@angular/tsc-wrapped": "file:../../dist/packages-dist/tsc-wrapped",
|
||||
"google-closure-compiler": "20170409.0.0",
|
||||
"rxjs": "5.3.1",
|
||||
"typescript": "2.1.6",
|
||||
"typescript": "~2.3.1",
|
||||
"zone.js": "0.8.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
--compilation_level=ADVANCED_OPTIMIZATIONS
|
||||
--language_out=ES5
|
||||
--js_output_file=dist/bundle.js
|
||||
--output_manifest=dist/manifest.MF
|
||||
--variable_renaming_report=dist/variable_renaming_report
|
||||
--property_renaming_report=dist/property_renaming_report
|
||||
--create_source_map=%outname%.map
|
||||
|
||||
--warning_level=QUIET
|
||||
--dependency_mode=STRICT
|
||||
--rewrite_polyfills=false
|
||||
|
||||
node_modules/zone.js/dist/zone_externs.js
|
||||
|
||||
--js node_modules/rxjs/**.js
|
||||
--process_common_js_modules
|
||||
--module_resolution=node
|
||||
|
||||
node_modules/@angular/core/@angular/core.js
|
||||
--js_module_root=node_modules/@angular/core
|
||||
node_modules/@angular/core/src/testability/testability.externs.js
|
||||
|
||||
node_modules/@angular/common/@angular/common.js
|
||||
--js_module_root=node_modules/@angular/common
|
||||
|
||||
node_modules/@angular/platform-browser/@angular/platform-browser.js
|
||||
--js_module_root=node_modules/@angular/platform-browser
|
||||
|
||||
--js built/**.js
|
||||
--entry_point=built/src/main
|
|
@ -0,0 +1,10 @@
|
|||
import { browser, element, by } from 'protractor';
|
||||
|
||||
describe('i18n E2E Tests', function () {
|
||||
it('remove i18n attributes', function () {
|
||||
browser.get('');
|
||||
const div = element(by.css('div'));
|
||||
expect(div.getAttribute('title')).not.toBe(null);
|
||||
expect(div.getAttribute('i18n')).toBe(null);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"open": false,
|
||||
"logLevel": "silent",
|
||||
"port": 8080,
|
||||
"server": {
|
||||
"baseDir": "src",
|
||||
"routes": {
|
||||
"/dist": "dist",
|
||||
"/node_modules": "node_modules"
|
||||
},
|
||||
"middleware": {
|
||||
"0": null
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
exports.config = {
|
||||
specs: [
|
||||
'../built/e2e/*.e2e-spec.js'
|
||||
],
|
||||
capabilities: {
|
||||
browserName: 'chrome',
|
||||
chromeOptions: {
|
||||
args: ['--no-sandbox'],
|
||||
binary: process.env.CHROME_BIN,
|
||||
}
|
||||
},
|
||||
directConnect: true,
|
||||
baseUrl: 'http://localhost:8080/',
|
||||
framework: 'jasmine',
|
||||
useAllAngular2AppRoots: true
|
||||
};
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"outDir": "../built/e2e",
|
||||
"types": ["jasmine"],
|
||||
// TODO(alexeagle): was required for Protractor 4.0.11
|
||||
"skipLibCheck": true
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
{
|
||||
"name": "angular-integration",
|
||||
"version": "0.0.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@angular/animations": "file:../../dist/packages-dist/animations",
|
||||
"@angular/common": "file:../../dist/packages-dist/common",
|
||||
"@angular/compiler": "file:../../dist/packages-dist/compiler",
|
||||
"@angular/compiler-cli": "file:../../dist/packages-dist/compiler-cli",
|
||||
"@angular/core": "file:../../dist/packages-dist/core",
|
||||
"@angular/platform-browser": "file:../../dist/packages-dist/platform-browser",
|
||||
"@angular/platform-server": "file:../../dist/packages-dist/platform-server",
|
||||
"@angular/tsc-wrapped": "file:../../dist/packages-dist/tsc-wrapped",
|
||||
"google-closure-compiler": "20170409.0.0",
|
||||
"rxjs": "5.3.1",
|
||||
"typescript": "~2.3.1",
|
||||
"zone.js": "0.8.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jasmine": "2.5.41",
|
||||
"concurrently": "3.4.0",
|
||||
"lite-server": "2.2.2",
|
||||
"protractor": "file:../../node_modules/protractor"
|
||||
},
|
||||
"scripts": {
|
||||
"closure": "java -jar node_modules/google-closure-compiler/compiler.jar --flagfile closure.conf",
|
||||
"test": "ngc && yarn run closure && concurrently \"yarn run serve\" \"yarn run protractor\" --kill-others --success first",
|
||||
"serve": "lite-server -c e2e/browser.config.json",
|
||||
"preprotractor": "tsc -p e2e",
|
||||
"protractor": "protractor e2e/protractor.config.js"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
import {HelloWorldComponent} from './hello-world.component';
|
||||
|
||||
import {NgModule} from '@angular/core';
|
||||
import {BrowserModule} from '@angular/platform-browser';
|
||||
|
||||
@NgModule({
|
||||
declarations: [HelloWorldComponent],
|
||||
bootstrap: [HelloWorldComponent],
|
||||
imports: [BrowserModule],
|
||||
})
|
||||
export class AppModule {}
|
|
@ -0,0 +1,9 @@
|
|||
import {Component} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'hello-world-app',
|
||||
template: `<div i18n="desc|meaning" title="i18n attribute should be removed">Hello {{ name }}!</div>`,
|
||||
})
|
||||
export class HelloWorldComponent {
|
||||
name: string = 'world';
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Hello World</title>
|
||||
<base href="/">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<hello-world-app>Loading...</hello-world-app>
|
||||
|
||||
<script src="node_modules/zone.js/dist/zone.min.js"></script>
|
||||
<script src="dist/bundle.js"></script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,4 @@
|
|||
import {platformBrowser} from '@angular/platform-browser';
|
||||
import {AppModuleNgFactory} from './app.ngfactory';
|
||||
|
||||
platformBrowser().bootstrapModuleFactory(AppModuleNgFactory);
|
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"angularCompilerOptions": {
|
||||
"annotationsAs": "static fields",
|
||||
"annotateForClosureCompiler": true,
|
||||
"alwaysCompileGeneratedCode": true
|
||||
},
|
||||
|
||||
"compilerOptions": {
|
||||
"module": "es2015",
|
||||
"moduleResolution": "node",
|
||||
// TODO(i): strictNullChecks should turned on but are temporarily disabled due to #15432
|
||||
"strictNullChecks": false,
|
||||
"target": "es6",
|
||||
"noImplicitAny": false,
|
||||
"sourceMap": false,
|
||||
"experimentalDecorators": true,
|
||||
"outDir": "built",
|
||||
"rootDir": ".",
|
||||
"declaration": true,
|
||||
"types": []
|
||||
},
|
||||
|
||||
"exclude": [
|
||||
"vendor",
|
||||
"node_modules",
|
||||
"built",
|
||||
"dist",
|
||||
"e2e"
|
||||
]
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {BasicComp} from '../../src/basic';
|
||||
import {MainModuleNgFactory} from './module.ngfactory';
|
||||
|
||||
MainModuleNgFactory.create(null).instance.appRef.bootstrap(BasicComp);
|
|
@ -12,7 +12,6 @@ import * as path from 'path';
|
|||
import {MultipleComponentsMyComp} from '../src/a/multiple_components';
|
||||
import {BasicComp} from '../src/basic';
|
||||
import {createComponent} from './util';
|
||||
import {createComponentAlt} from './util_alt';
|
||||
|
||||
describe('template codegen output', () => {
|
||||
const outDir = 'src';
|
||||
|
@ -37,9 +36,9 @@ describe('template codegen output', () => {
|
|||
expect(fs.readFileSync(dtsOutput, {encoding: 'utf-8'})).toContain('Basic');
|
||||
});
|
||||
|
||||
it('should write .ngfactory.ts for .d.ts inputs', () => {
|
||||
it('should write .ngfactory.js for .d.ts inputs', () => {
|
||||
const factoryOutput =
|
||||
path.join('node_modules', '@angular2-material', 'button', 'button.ngfactory.ts');
|
||||
path.join('node_modules', '@angular2-material', 'button', 'button.ngfactory.js');
|
||||
expect(fs.existsSync(factoryOutput)).toBeTruthy();
|
||||
});
|
||||
|
||||
|
@ -95,11 +94,5 @@ describe('template codegen output', () => {
|
|||
expect(containerElement.attributes['title']).toBe('käännä teksti');
|
||||
expect(containerElement.attributes['i18n-title']).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should have removed i18n markup event without translations', () => {
|
||||
const containerElement = createComponentAlt(BasicComp).debugElement.children[0];
|
||||
expect(containerElement.attributes['title']).toBe('translate me');
|
||||
expect(containerElement.attributes['i18n-title']).toBeUndefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -10,7 +10,8 @@ import './init';
|
|||
import {BindingErrorComp} from '../src/errors';
|
||||
import {createComponent} from './util';
|
||||
|
||||
describe('source maps', () => {
|
||||
// TODO(tbosch): source maps does not currently work with the transformer pipeline
|
||||
xdescribe('source maps', () => {
|
||||
it('should report source location for binding errors', () => {
|
||||
const comp = createComponent(BindingErrorComp);
|
||||
let error: any;
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {NgModuleRef} from '@angular/core';
|
||||
import {ComponentFixture} from '@angular/core/testing';
|
||||
import {platformServerTesting} from '@angular/platform-server/testing';
|
||||
|
||||
import {MainModuleNgFactory} from '../alt/src/module.ngfactory';
|
||||
import {MainModule} from '../src/module';
|
||||
|
||||
let mainModuleRef: NgModuleRef<MainModule> = null !;
|
||||
beforeEach((done) => {
|
||||
platformServerTesting().bootstrapModuleFactory(MainModuleNgFactory).then((moduleRef: any) => {
|
||||
mainModuleRef = moduleRef;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
export function createModule(): NgModuleRef<MainModule> {
|
||||
return mainModuleRef;
|
||||
}
|
||||
|
||||
export function createComponentAlt<C>(comp: {new (...args: any[]): C}): ComponentFixture<C> {
|
||||
const moduleRef = createModule();
|
||||
const compRef =
|
||||
moduleRef.componentFactoryResolver.resolveComponentFactory(comp).create(moduleRef.injector);
|
||||
return new ComponentFixture(compRef, null, false);
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
{
|
||||
"angularCompilerOptions": {
|
||||
// For TypeScript 1.8, we have to lay out generated files
|
||||
// in the same source directory with your code.
|
||||
"genDir": "./alt",
|
||||
"debug": true,
|
||||
"enableSummariesForJit": true,
|
||||
"alwaysCompileGeneratedCode": true
|
||||
},
|
||||
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"experimentalDecorators": true,
|
||||
"noImplicitAny": true,
|
||||
"strictNullChecks": true,
|
||||
"skipLibCheck": true,
|
||||
"moduleResolution": "node",
|
||||
"rootDir": "",
|
||||
"declaration": true,
|
||||
"lib": ["es6", "dom"],
|
||||
"baseUrl": ".",
|
||||
// Prevent scanning up the directory tree for types
|
||||
"typeRoots": ["node_modules/@types"],
|
||||
"noUnusedLocals": true,
|
||||
"sourceMap": true
|
||||
},
|
||||
|
||||
"files": [
|
||||
"src/module",
|
||||
"alt/src/bootstrap"
|
||||
]
|
||||
}
|
|
@ -5,7 +5,9 @@
|
|||
"genDir": ".",
|
||||
"debug": true,
|
||||
"enableSummariesForJit": true,
|
||||
"alwaysCompileGeneratedCode": true
|
||||
"alwaysCompileGeneratedCode": true,
|
||||
"locale": "fi",
|
||||
"i18nFormat": "xlf"
|
||||
},
|
||||
|
||||
"compilerOptions": {
|
||||
|
|
|
@ -11,7 +11,8 @@
|
|||
"dependencies": {
|
||||
"@angular/tsc-wrapped": "5.0.0-beta.3",
|
||||
"reflect-metadata": "^0.1.2",
|
||||
"minimist": "^1.2.0"
|
||||
"minimist": "^1.2.0",
|
||||
"tsickle": "^0.23.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^2.0.2",
|
||||
|
|
|
@ -56,7 +56,7 @@ function getReferences(info: DiagnosticTemplateInfo): SymbolDeclaration[] {
|
|||
name: reference.name,
|
||||
kind: 'reference',
|
||||
type: type || info.query.getBuiltinType(BuiltinType.Any),
|
||||
get definition() { return getDefintionOf(info, reference); }
|
||||
get definition() { return getDefinitionOf(info, reference); }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -77,7 +77,7 @@ function getReferences(info: DiagnosticTemplateInfo): SymbolDeclaration[] {
|
|||
return result;
|
||||
}
|
||||
|
||||
function getDefintionOf(info: DiagnosticTemplateInfo, ast: TemplateAst): Definition|undefined {
|
||||
function getDefinitionOf(info: DiagnosticTemplateInfo, ast: TemplateAst): Definition|undefined {
|
||||
if (info.fileName) {
|
||||
const templateOffset = info.offset;
|
||||
return [{
|
||||
|
@ -124,7 +124,7 @@ function getVarDeclarations(
|
|||
}
|
||||
result.push({
|
||||
name,
|
||||
kind: 'variable', type, get definition() { return getDefintionOf(info, variable); }
|
||||
kind: 'variable', type, get definition() { return getDefinitionOf(info, variable); }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,14 +7,19 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
|
||||
// Must be imported first, because Angular decorators throw on load.
|
||||
import 'reflect-metadata';
|
||||
|
||||
import * as ts from 'typescript';
|
||||
import * as tsc from '@angular/tsc-wrapped';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as ngc from './ngc';
|
||||
|
||||
import {isSyntaxError} from '@angular/compiler';
|
||||
|
||||
import {readConfiguration} from './perform-compile';
|
||||
|
||||
import {CodeGenerator} from './codegen';
|
||||
|
||||
function codegen(
|
||||
|
@ -46,6 +51,19 @@ export function main(
|
|||
|
||||
// CLI entry point
|
||||
if (require.main === module) {
|
||||
const args = require('minimist')(process.argv.slice(2));
|
||||
main(args).then((exitCode: number) => process.exit(exitCode));
|
||||
const args = process.argv.slice(2);
|
||||
const parsedArgs = require('minimist')(args);
|
||||
const project = parsedArgs.p || parsedArgs.project || '.';
|
||||
|
||||
const projectDir = fs.lstatSync(project).isFile() ? path.dirname(project) : project;
|
||||
|
||||
// file names in tsconfig are resolved relative to this absolute path
|
||||
const basePath = path.resolve(process.cwd(), projectDir);
|
||||
const {ngOptions} = readConfiguration(project, basePath);
|
||||
|
||||
if (ngOptions.disableTransformerPipeline) {
|
||||
main(parsedArgs).then((exitCode: number) => process.exit(exitCode));
|
||||
} else {
|
||||
process.exit(ngc.main(args, s => console.error(s)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,11 +9,12 @@
|
|||
// Must be imported first, because Angular decorators throw on load.
|
||||
import 'reflect-metadata';
|
||||
|
||||
import {isSyntaxError, syntaxError} from '@angular/compiler';
|
||||
import {isSyntaxError} from '@angular/compiler';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
import {performCompilation, readConfiguration, throwOnDiagnostics} from './perform-compile';
|
||||
import {CompilerOptions} from './transformers/api';
|
||||
|
||||
export function main(
|
||||
args: string[], consoleError: (s: string) => void = console.error,
|
||||
|
@ -27,15 +28,40 @@ export function main(
|
|||
// file names in tsconfig are resolved relative to this absolute path
|
||||
const basePath = path.resolve(process.cwd(), projectDir);
|
||||
const {parsed, ngOptions} = readConfiguration(project, basePath, checkFunc);
|
||||
return performCompilation(
|
||||
basePath, parsed.fileNames, parsed.options, ngOptions, consoleError, checkFunc);
|
||||
|
||||
// CLI arguments can override the i18n options
|
||||
const ngcOptions = mergeCommandLine(parsedArgs, ngOptions);
|
||||
|
||||
const res = performCompilation(
|
||||
basePath, parsed.fileNames, parsed.options, ngcOptions, consoleError, checkFunc);
|
||||
|
||||
return res.errorCode;
|
||||
} catch (e) {
|
||||
if (isSyntaxError(e)) {
|
||||
consoleError(e.message);
|
||||
return 1;
|
||||
}
|
||||
|
||||
consoleError(e.stack);
|
||||
consoleError('Compilation failed');
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
// Merge command line parameters
|
||||
function mergeCommandLine(
|
||||
parsedArgs: {[k: string]: string}, options: CompilerOptions): CompilerOptions {
|
||||
if (parsedArgs.i18nFile) options.i18nInFile = parsedArgs.i18nFile;
|
||||
if (parsedArgs.i18nFormat) options.i18nInFormat = parsedArgs.i18nFormat;
|
||||
if (parsedArgs.locale) options.i18nInLocale = parsedArgs.locale;
|
||||
const mt = parsedArgs.missingTranslation;
|
||||
if (mt === 'error' || mt === 'warning' || mt === 'ignore') {
|
||||
options.i18nInMissingTranslations = mt;
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
// CLI entry point
|
||||
if (require.main === module) {
|
||||
process.exit(main(process.argv.slice(2), s => console.error(s)));
|
||||
|
|
|
@ -7,10 +7,11 @@
|
|||
*/
|
||||
|
||||
import {isSyntaxError, syntaxError} from '@angular/compiler';
|
||||
import {MetadataBundler, createBundleIndexHost} from '@angular/tsc-wrapped';
|
||||
import {createBundleIndexHost} from '@angular/tsc-wrapped';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import * as api from './transformers/api';
|
||||
import * as ng from './transformers/entry_points';
|
||||
|
||||
|
@ -70,17 +71,6 @@ export function throwOnDiagnostics(cwd: string, ...args: Diagnostics[]) {
|
|||
}
|
||||
}
|
||||
|
||||
function syntheticError(message: string): ts.Diagnostic {
|
||||
return {
|
||||
file: null as any as ts.SourceFile,
|
||||
start: 0,
|
||||
length: 0,
|
||||
messageText: message,
|
||||
category: ts.DiagnosticCategory.Error,
|
||||
code: 0
|
||||
};
|
||||
}
|
||||
|
||||
export function readConfiguration(
|
||||
project: string, basePath: string,
|
||||
checkFunc: (cwd: string, ...args: any[]) => void = throwOnDiagnostics,
|
||||
|
@ -111,28 +101,22 @@ export function readConfiguration(
|
|||
return {parsed, ngOptions};
|
||||
}
|
||||
|
||||
function getProjectDirectory(project: string): string {
|
||||
let isFile: boolean;
|
||||
try {
|
||||
isFile = fs.lstatSync(project).isFile();
|
||||
} catch (e) {
|
||||
// Project doesn't exist. Assume it is a file has an extension. This case happens
|
||||
// when the project file is passed to set basePath but no tsconfig.json file exists.
|
||||
// It is used in tests to ensure that the options can be passed in without there being
|
||||
// an actual config file.
|
||||
isFile = path.extname(project) !== '';
|
||||
}
|
||||
|
||||
// If project refers to a file, the project directory is the file's parent directory
|
||||
// otherwise project is the project directory.
|
||||
return isFile ? path.dirname(project) : project;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an object with two properties:
|
||||
* - `errorCode` is 0 when the compilation was successful,
|
||||
* - `result` is an `EmitResult` when the errorCode is 0, `undefined` otherwise.
|
||||
*/
|
||||
export function performCompilation(
|
||||
basePath: string, files: string[], options: ts.CompilerOptions, ngOptions: any,
|
||||
basePath: string, files: string[], options: ts.CompilerOptions, ngOptions: api.CompilerOptions,
|
||||
consoleError: (s: string) => void = console.error,
|
||||
checkFunc: (cwd: string, ...args: any[]) => void = throwOnDiagnostics,
|
||||
tsCompilerHost?: ts.CompilerHost) {
|
||||
tsCompilerHost?: ts.CompilerHost): {errorCode: number, result?: api.EmitResult} {
|
||||
const [major, minor] = ts.version.split('.');
|
||||
|
||||
if (+major < 2 || (+major === 2 && +minor < 3)) {
|
||||
throw new Error('Must use TypeScript > 2.3 to have transformer support');
|
||||
}
|
||||
|
||||
try {
|
||||
ngOptions.basePath = basePath;
|
||||
ngOptions.genDir = basePath;
|
||||
|
@ -175,18 +159,20 @@ export function performCompilation(
|
|||
// Check Angular semantic diagnostics
|
||||
checkFunc(basePath, ngProgram.getNgSemanticDiagnostics());
|
||||
|
||||
ngProgram.emit({
|
||||
const result = ngProgram.emit({
|
||||
emitFlags: api.EmitFlags.Default |
|
||||
((ngOptions.skipMetadataEmit || ngOptions.flatModuleOutFile) ? 0 : api.EmitFlags.Metadata)
|
||||
});
|
||||
|
||||
checkFunc(basePath, result.diagnostics);
|
||||
|
||||
return {errorCode: 0, result};
|
||||
} catch (e) {
|
||||
if (isSyntaxError(e)) {
|
||||
console.error(e.message);
|
||||
consoleError(e.message);
|
||||
return 1;
|
||||
return {errorCode: 1};
|
||||
}
|
||||
|
||||
throw e;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -24,6 +24,7 @@ export interface Diagnostic {
|
|||
export interface CompilerOptions extends ts.CompilerOptions {
|
||||
// Absolute path to a directory where generated file structure is written.
|
||||
// If unspecified, generated files will be written alongside sources.
|
||||
// @deprecated - no effect
|
||||
genDir?: string;
|
||||
|
||||
// Path to the directory containing the tsconfig.json file.
|
||||
|
@ -95,6 +96,27 @@ export interface CompilerOptions extends ts.CompilerOptions {
|
|||
// Whether to enable lowering expressions lambdas and expressions in a reference value
|
||||
// position.
|
||||
disableExpressionLowering?: boolean;
|
||||
|
||||
// The list of expected files, when provided:
|
||||
// - extra files are filtered out,
|
||||
// - missing files are created empty.
|
||||
expectedOut?: string[];
|
||||
|
||||
// Locale of the application
|
||||
i18nOutLocale?: string;
|
||||
// Export format (xlf, xlf2 or xmb)
|
||||
i18nOutFormat?: string;
|
||||
// Path to the extracted message file
|
||||
i18nOutFile?: string;
|
||||
|
||||
// Import format if different from `i18nFormat`
|
||||
i18nInFormat?: string;
|
||||
// Locale of the imported translations
|
||||
i18nInLocale?: string;
|
||||
// Path to the translation file
|
||||
i18nInFile?: string;
|
||||
// How to handle missing messages
|
||||
i18nInMissingTranslations?: 'error'|'warning'|'ignore';
|
||||
}
|
||||
|
||||
export interface ModuleFilenameResolver {
|
||||
|
@ -146,6 +168,11 @@ export enum EmitFlags {
|
|||
// afterTs?: ts.TransformerFactory<ts.SourceFile>[];
|
||||
// }
|
||||
|
||||
export interface EmitResult extends ts.EmitResult {
|
||||
modulesManifest: {modules: string[]; fileNames: string[];};
|
||||
externs: {[fileName: string]: string;};
|
||||
}
|
||||
|
||||
export interface Program {
|
||||
/**
|
||||
* Retrieve the TypeScript program used to produce semantic diagnostics and emit the sources.
|
||||
|
@ -155,7 +182,7 @@ export interface Program {
|
|||
getTsProgram(): ts.Program;
|
||||
|
||||
/**
|
||||
* Retreive options diagnostics for the TypeScript options used to create the program. This is
|
||||
* Retrieve options diagnostics for the TypeScript options used to create the program. This is
|
||||
* faster than calling `getTsProgram().getOptionsDiagnostics()` since it does not need to
|
||||
* collect Angular structural information to produce the errors.
|
||||
*/
|
||||
|
@ -167,7 +194,7 @@ export interface Program {
|
|||
getNgOptionDiagnostics(cancellationToken?: ts.CancellationToken): Diagnostic[];
|
||||
|
||||
/**
|
||||
* Retrive the syntax diagnostics from TypeScript. This is faster than calling
|
||||
* Retrieve the syntax diagnostics from TypeScript. This is faster than calling
|
||||
* `getTsProgram().getSyntacticDiagnostics()` since it does not need to collect Angular structural
|
||||
* information to produce the errors.
|
||||
*/
|
||||
|
@ -188,7 +215,7 @@ export interface Program {
|
|||
getNgStructuralDiagnostics(cancellationToken?: ts.CancellationToken): Diagnostic[];
|
||||
|
||||
/**
|
||||
* Retreive the semantic diagnostics from TypeScript. This is equivilent to calling
|
||||
* Retrieve the semantic diagnostics from TypeScript. This is equivilent to calling
|
||||
* `getTsProgram().getSemanticDiagnostics()` directly and is included for completeness.
|
||||
*/
|
||||
getTsSemanticDiagnostics(sourceFile?: ts.SourceFile, cancellationToken?: ts.CancellationToken):
|
||||
|
@ -227,5 +254,5 @@ export interface Program {
|
|||
emitFlags: EmitFlags,
|
||||
// transformers?: CustomTransformers, // See TODO above
|
||||
cancellationToken?: ts.CancellationToken,
|
||||
}): void;
|
||||
}): EmitResult;
|
||||
}
|
||||
|
|
|
@ -55,19 +55,26 @@ function transformSourceFile(
|
|||
context: ts.TransformationContext): ts.SourceFile {
|
||||
const inserts: DeclarationInsert[] = [];
|
||||
|
||||
// Calculate the range of intersting locations. The transform will only visit nodes in this
|
||||
// Calculate the range of interesting locations. The transform will only visit nodes in this
|
||||
// range to improve the performance on large files.
|
||||
const locations = Array.from(requests.keys());
|
||||
const min = Math.min(...locations);
|
||||
const max = Math.max(...locations);
|
||||
|
||||
// Visit nodes matching the request and synthetic nodes added by tsickle
|
||||
function shouldVisit(pos: number, end: number): boolean {
|
||||
return (pos <= max && end >= min) || pos == -1;
|
||||
}
|
||||
|
||||
function visitSourceFile(sourceFile: ts.SourceFile): ts.SourceFile {
|
||||
function topLevelStatement(node: ts.Node): ts.Node {
|
||||
const declarations: Declaration[] = [];
|
||||
|
||||
function visitNode(node: ts.Node): ts.Node {
|
||||
const nodeRequest = requests.get(node.pos);
|
||||
if (nodeRequest && nodeRequest.kind == node.kind && nodeRequest.end == node.end) {
|
||||
// Get the original node before tsickle
|
||||
const {pos, end, kind} = ts.getOriginalNode(node);
|
||||
const nodeRequest = requests.get(pos);
|
||||
if (nodeRequest && nodeRequest.kind == kind && nodeRequest.end == end) {
|
||||
// This node is requested to be rewritten as a reference to the exported name.
|
||||
// Record that the node needs to be moved to an exported variable with the given name
|
||||
const name = nodeRequest.name;
|
||||
|
@ -75,14 +82,16 @@ function transformSourceFile(
|
|||
return ts.createIdentifier(name);
|
||||
}
|
||||
let result = node;
|
||||
if (node.pos <= max && node.end >= min && !isLexicalScope(node)) {
|
||||
|
||||
if (shouldVisit(pos, end) && !isLexicalScope(node)) {
|
||||
result = ts.visitEachChild(node, visitNode, context);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
const result =
|
||||
(node.pos <= max && node.end >= min) ? ts.visitEachChild(node, visitNode, context) : node;
|
||||
// Get the original node before tsickle
|
||||
const {pos, end} = ts.getOriginalNode(node);
|
||||
const result = shouldVisit(pos, end) ? ts.visitEachChild(node, visitNode, context) : node;
|
||||
|
||||
if (declarations.length) {
|
||||
inserts.push({priorTo: result, declarations});
|
||||
|
@ -91,6 +100,7 @@ function transformSourceFile(
|
|||
}
|
||||
|
||||
const traversedSource = ts.visitEachChild(sourceFile, topLevelStatement, context);
|
||||
|
||||
if (inserts.length) {
|
||||
// Insert the declarations before the rewritten statement that references them.
|
||||
const insertMap = toMap(inserts, i => i.priorTo);
|
||||
|
|
|
@ -19,8 +19,10 @@ export class TypeScriptNodeEmitter {
|
|||
updateSourceFile(sourceFile: ts.SourceFile, stmts: Statement[], preamble?: string):
|
||||
[ts.SourceFile, Map<ts.Node, Node>] {
|
||||
const converter = new _NodeEmitterVisitor();
|
||||
const statements =
|
||||
stmts.map(stmt => stmt.visitStatement(converter, null)).filter(stmt => stmt != null);
|
||||
// [].concat flattens the result so that each `visit...` method can also return an array of
|
||||
// stmts.
|
||||
const statements: any[] = [].concat(
|
||||
...stmts.map(stmt => stmt.visitStatement(converter, null)).filter(stmt => stmt != null));
|
||||
const newSourceFile = ts.updateSourceFileNode(
|
||||
sourceFile, [...converter.getReexports(), ...converter.getImports(), ...statements]);
|
||||
if (preamble) {
|
||||
|
@ -118,20 +120,30 @@ class _NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor {
|
|||
}
|
||||
}
|
||||
|
||||
return this.record(
|
||||
stmt, ts.createVariableStatement(
|
||||
this.getModifiers(stmt),
|
||||
ts.createVariableDeclarationList([ts.createVariableDeclaration(
|
||||
ts.createIdentifier(stmt.name),
|
||||
/* type */ undefined,
|
||||
(stmt.value && stmt.value.visitExpression(this, null)) || undefined)])));
|
||||
const varDeclList = ts.createVariableDeclarationList([ts.createVariableDeclaration(
|
||||
ts.createIdentifier(stmt.name),
|
||||
/* type */ undefined,
|
||||
(stmt.value && stmt.value.visitExpression(this, null)) || undefined)]);
|
||||
|
||||
if (stmt.hasModifier(StmtModifier.Exported)) {
|
||||
// Note: We need to add an explicit variable and export declaration so that
|
||||
// the variable can be referred in the same file as well.
|
||||
const tsVarStmt =
|
||||
this.record(stmt, ts.createVariableStatement(/* modifiers */[], varDeclList));
|
||||
const exportStmt = this.record(
|
||||
stmt, ts.createExportDeclaration(
|
||||
/*decorators*/ undefined, /*modifiers*/ undefined,
|
||||
ts.createNamedExports([ts.createExportSpecifier(stmt.name, stmt.name)])));
|
||||
return [tsVarStmt, exportStmt];
|
||||
}
|
||||
return this.record(stmt, ts.createVariableStatement(this.getModifiers(stmt), varDeclList));
|
||||
}
|
||||
|
||||
visitDeclareFunctionStmt(stmt: DeclareFunctionStmt, context: any) {
|
||||
return this.record(
|
||||
stmt, ts.createFunctionDeclaration(
|
||||
/* decorators */ undefined, this.getModifiers(stmt),
|
||||
/* astrictToken */ undefined, stmt.name, /* typeParameters */ undefined,
|
||||
/* asteriskToken */ undefined, stmt.name, /* typeParameters */ undefined,
|
||||
stmt.params.map(
|
||||
p => ts.createParameter(
|
||||
/* decorators */ undefined, /* modifiers */ undefined,
|
||||
|
|
|
@ -6,20 +6,22 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {AotCompiler, GeneratedFile, NgAnalyzedModules, createAotCompiler, getParseErrors, isSyntaxError, toTypeScript} from '@angular/compiler';
|
||||
import {MetadataCollector, ModuleMetadata} from '@angular/tsc-wrapped';
|
||||
import {writeFileSync} from 'fs';
|
||||
import {AotCompiler, AotCompilerOptions, GeneratedFile, NgAnalyzedModules, createAotCompiler, getParseErrors, isSyntaxError, toTypeScript} from '@angular/compiler';
|
||||
import {MissingTranslationStrategy} from '@angular/core';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as tsickle from 'tsickle';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {CompilerHost as AotCompilerHost, CompilerHostContext} from '../compiler_host';
|
||||
import {CompilerHost as AotCompilerHost} from '../compiler_host';
|
||||
import {TypeChecker} from '../diagnostics/check_types';
|
||||
|
||||
import {CompilerHost, CompilerOptions, Diagnostic, DiagnosticCategory, EmitFlags, Program} from './api';
|
||||
import {CompilerHost, CompilerOptions, Diagnostic, DiagnosticCategory, EmitFlags, EmitResult, Program} from './api';
|
||||
import {LowerMetadataCache, getExpressionLoweringTransformFactory} from './lower_expressions';
|
||||
import {getAngularEmitterTransformFactory} from './node_emitter_transform';
|
||||
|
||||
const GENERATED_FILES = /\.ngfactory\.js$|\.ngstyle\.js$|\.ngsummary\.js$/;
|
||||
|
||||
const SUMMARY_JSON_FILES = /\.ngsummary.json$/;
|
||||
|
||||
const emptyModules: NgAnalyzedModules = {
|
||||
|
@ -52,17 +54,20 @@ class AngularCompilerProgram implements Program {
|
|||
private rootNames: string[], private options: CompilerOptions, private host: CompilerHost,
|
||||
private oldProgram?: Program) {
|
||||
this.oldTsProgram = oldProgram ? oldProgram.getTsProgram() : undefined;
|
||||
|
||||
this.tsProgram = ts.createProgram(rootNames, options, host, this.oldTsProgram);
|
||||
this.srcNames = this.tsProgram.getSourceFiles().map(sf => sf.fileName);
|
||||
this.srcNames =
|
||||
this.tsProgram.getSourceFiles()
|
||||
.map(sf => sf.fileName)
|
||||
.filter(f => !f.match(/\.ngfactory\.[\w.]+$|\.ngstyle\.[\w.]+$|\.ngsummary\.[\w.]+$/));
|
||||
this.metadataCache = new LowerMetadataCache({quotedNames: true}, !!options.strictMetadataEmit);
|
||||
this.aotCompilerHost = new AotCompilerHost(
|
||||
this.tsProgram, options, host, /* collectorOptions */ undefined, this.metadataCache);
|
||||
if (host.readResource) {
|
||||
this.aotCompilerHost.loadResource = host.readResource.bind(host);
|
||||
}
|
||||
const {compiler} = createAotCompiler(this.aotCompilerHost, options);
|
||||
this.compiler = compiler;
|
||||
|
||||
const aotOptions = getAotCompilerOptions(options);
|
||||
this.compiler = createAotCompiler(this.aotCompilerHost, aotOptions).compiler;
|
||||
}
|
||||
|
||||
// Program implementation
|
||||
|
@ -115,25 +120,56 @@ class AngularCompilerProgram implements Program {
|
|||
getLazyRoutes(cancellationToken?: ts.CancellationToken): {[route: string]: string} { return {}; }
|
||||
|
||||
emit({emitFlags = EmitFlags.Default, cancellationToken}:
|
||||
{emitFlags?: EmitFlags, cancellationToken?: ts.CancellationToken}): ts.EmitResult {
|
||||
{emitFlags?: EmitFlags, cancellationToken?: ts.CancellationToken}): EmitResult {
|
||||
const emitMap = new Map<string, string>();
|
||||
const result = this.programWithStubs.emit(
|
||||
|
||||
const tsickleCompilerHostOptions: tsickle.TransformerOptions = {
|
||||
googmodule: false,
|
||||
untyped: true,
|
||||
convertIndexImportShorthand: true,
|
||||
transformDecorators: this.options.annotationsAs !== 'decorators',
|
||||
transformTypesToClosure: this.options.annotateForClosureCompiler,
|
||||
};
|
||||
|
||||
const tsickleHost: tsickle.TransformerHost = {
|
||||
shouldSkipTsickleProcessing: (fileName) => /\.d\.ts$/.test(fileName),
|
||||
pathToModuleName: (context, importPath) => '',
|
||||
shouldIgnoreWarningsForPath: (filePath) => false,
|
||||
fileNameToModuleId: (fileName) => fileName,
|
||||
};
|
||||
|
||||
const expectedOut = this.options.expectedOut ?
|
||||
this.options.expectedOut.map(f => path.resolve(process.cwd(), f)) :
|
||||
undefined;
|
||||
|
||||
const result = tsickle.emitWithTsickle(
|
||||
this.programWithStubs, tsickleHost, tsickleCompilerHostOptions, this.host, this.options,
|
||||
/* targetSourceFile */ undefined,
|
||||
createWriteFileCallback(emitFlags, this.host, this.metadataCache, emitMap),
|
||||
createWriteFileCallback(emitFlags, this.host, this.metadataCache, emitMap, expectedOut),
|
||||
cancellationToken, (emitFlags & (EmitFlags.DTS | EmitFlags.JS)) == EmitFlags.DTS,
|
||||
this.calculateTransforms());
|
||||
|
||||
this.generatedFiles.forEach(file => {
|
||||
// In order not to replicate the TS calculation of the out folder for files
|
||||
// derive the out location for .json files from the out location of the .ts files
|
||||
if (file.source && file.source.length && SUMMARY_JSON_FILES.test(file.genFileUrl)) {
|
||||
// If we have emitted the ngsummary.ts file, ensure the ngsummary.json file is emitted to
|
||||
// the same location.
|
||||
|
||||
const emittedFile = emitMap.get(file.srcFileUrl);
|
||||
const fileName = emittedFile ?
|
||||
path.join(path.dirname(emittedFile), path.basename(file.genFileUrl)) :
|
||||
file.genFileUrl;
|
||||
this.host.writeFile(fileName, file.source, false, error => {});
|
||||
|
||||
if (emittedFile) {
|
||||
const fileName = path.join(path.dirname(emittedFile), path.basename(file.genFileUrl));
|
||||
this.host.writeFile(fileName, file.source, false, error => {});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Ensure that expected output files exist.
|
||||
for (const out of expectedOut || []) {
|
||||
fs.appendFileSync(out, '', 'utf8');
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -183,19 +219,15 @@ class AngularCompilerProgram implements Program {
|
|||
return this.generatedFiles && this._generatedFileDiagnostics !;
|
||||
}
|
||||
|
||||
private calculateTransforms(): ts.CustomTransformers {
|
||||
const before: ts.TransformerFactory<ts.SourceFile>[] = [];
|
||||
const after: ts.TransformerFactory<ts.SourceFile>[] = [];
|
||||
private calculateTransforms(): tsickle.EmitTransformers {
|
||||
const beforeTs: ts.TransformerFactory<ts.SourceFile>[] = [];
|
||||
if (!this.options.disableExpressionLowering) {
|
||||
before.push(getExpressionLoweringTransformFactory(this.metadataCache));
|
||||
beforeTs.push(getExpressionLoweringTransformFactory(this.metadataCache));
|
||||
}
|
||||
if (!this.options.skipTemplateCodegen) {
|
||||
after.push(getAngularEmitterTransformFactory(this.generatedFiles));
|
||||
beforeTs.push(getAngularEmitterTransformFactory(this.generatedFiles));
|
||||
}
|
||||
const result: ts.CustomTransformers = {};
|
||||
if (before.length) result.before = before;
|
||||
if (after.length) result.after = after;
|
||||
return result;
|
||||
return {beforeTs};
|
||||
}
|
||||
|
||||
private catchAnalysisError(e: any): NgAnalyzedModules {
|
||||
|
@ -228,8 +260,8 @@ class AngularCompilerProgram implements Program {
|
|||
private generateStubs() {
|
||||
return this.options.skipTemplateCodegen ? [] :
|
||||
this.options.generateCodeForLibraries === false ?
|
||||
this.compiler.emitAllStubs(this.analyzedModules) :
|
||||
this.compiler.emitPartialStubs(this.analyzedModules);
|
||||
this.compiler.emitPartialStubs(this.analyzedModules) :
|
||||
this.compiler.emitAllStubs(this.analyzedModules);
|
||||
}
|
||||
|
||||
private generateFiles() {
|
||||
|
@ -270,6 +302,40 @@ export function createProgram(
|
|||
return new AngularCompilerProgram(rootNames, options, host, oldProgram);
|
||||
}
|
||||
|
||||
// Compute the AotCompiler options
|
||||
function getAotCompilerOptions(options: CompilerOptions): AotCompilerOptions {
|
||||
let missingTranslation = MissingTranslationStrategy.Warning;
|
||||
|
||||
switch (options.i18nInMissingTranslations) {
|
||||
case 'ignore':
|
||||
missingTranslation = MissingTranslationStrategy.Ignore;
|
||||
break;
|
||||
case 'error':
|
||||
missingTranslation = MissingTranslationStrategy.Error;
|
||||
break;
|
||||
}
|
||||
|
||||
let translations: string = '';
|
||||
|
||||
if (options.i18nInFile) {
|
||||
if (!options.locale) {
|
||||
throw new Error(`The translation file (${options.i18nInFile}) locale must be provided.`);
|
||||
}
|
||||
translations = fs.readFileSync(options.i18nInFile, 'utf8');
|
||||
} else {
|
||||
// No translations are provided, ignore any errors
|
||||
// We still go through i18n to remove i18n attributes
|
||||
missingTranslation = MissingTranslationStrategy.Ignore;
|
||||
}
|
||||
|
||||
return {
|
||||
locale: options.i18nInLocale,
|
||||
i18nFormat: options.i18nInFormat || options.i18nOutFormat, translations, missingTranslation,
|
||||
enableLegacyTemplate: options.enableLegacyTemplate,
|
||||
enableSummariesForJit: true,
|
||||
};
|
||||
}
|
||||
|
||||
function writeMetadata(
|
||||
emitFilePath: string, sourceFile: ts.SourceFile, metadataCache: LowerMetadataCache) {
|
||||
if (/\.js$/.test(emitFilePath)) {
|
||||
|
@ -287,38 +353,36 @@ function writeMetadata(
|
|||
const metadata = metadataCache.getMetadata(collectableFile);
|
||||
if (metadata) {
|
||||
const metadataText = JSON.stringify([metadata]);
|
||||
writeFileSync(path, metadataText, {encoding: 'utf-8'});
|
||||
fs.writeFileSync(path, metadataText, {encoding: 'utf-8'});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function createWriteFileCallback(
|
||||
emitFlags: EmitFlags, host: ts.CompilerHost, metadataCache: LowerMetadataCache,
|
||||
emitMap: Map<string, string>) {
|
||||
const withMetadata =
|
||||
(fileName: string, data: string, writeByteOrderMark: boolean,
|
||||
onError?: (message: string) => void, sourceFiles?: ts.SourceFile[]) => {
|
||||
const generatedFile = GENERATED_FILES.test(fileName);
|
||||
if (!generatedFile || data != '') {
|
||||
host.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles);
|
||||
}
|
||||
if (!generatedFile && sourceFiles && sourceFiles.length == 1) {
|
||||
emitMap.set(sourceFiles[0].fileName, fileName);
|
||||
writeMetadata(fileName, sourceFiles[0], metadataCache);
|
||||
}
|
||||
};
|
||||
const withoutMetadata =
|
||||
(fileName: string, data: string, writeByteOrderMark: boolean,
|
||||
onError?: (message: string) => void, sourceFiles?: ts.SourceFile[]) => {
|
||||
const generatedFile = GENERATED_FILES.test(fileName);
|
||||
if (!generatedFile || data != '') {
|
||||
host.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles);
|
||||
}
|
||||
if (!generatedFile && sourceFiles && sourceFiles.length == 1) {
|
||||
emitMap.set(sourceFiles[0].fileName, fileName);
|
||||
}
|
||||
};
|
||||
return (emitFlags & EmitFlags.Metadata) != 0 ? withMetadata : withoutMetadata;
|
||||
emitMap: Map<string, string>, expectedOut?: string[]) {
|
||||
return (fileName: string, data: string, writeByteOrderMark: boolean,
|
||||
onError?: (message: string) => void, sourceFiles?: ts.SourceFile[]) => {
|
||||
|
||||
let srcFile: ts.SourceFile|undefined;
|
||||
|
||||
if (sourceFiles && sourceFiles.length == 1) {
|
||||
srcFile = sourceFiles[0];
|
||||
emitMap.set(srcFile.fileName, fileName);
|
||||
}
|
||||
|
||||
const absFile = path.resolve(process.cwd(), fileName);
|
||||
const generatedFile = GENERATED_FILES.test(fileName);
|
||||
|
||||
// Don't emit unexpected files nor empty generated files
|
||||
if ((!expectedOut || expectedOut.indexOf(absFile) > -1) && (!generatedFile || data)) {
|
||||
host.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles);
|
||||
|
||||
if (srcFile && !generatedFile && (emitFlags & EmitFlags.Metadata) != 0) {
|
||||
writeMetadata(fileName, srcFile, metadataCache);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function getNgOptionDiagnostics(options: CompilerOptions): Diagnostic[] {
|
||||
|
|
|
@ -82,19 +82,19 @@ describe('ngc command-line', () => {
|
|||
|
||||
spyOn(mockConsole, 'error');
|
||||
|
||||
const result = performCompilation(
|
||||
basePath, [path.join(basePath, 'test.ts')], {
|
||||
experimentalDecorators: true,
|
||||
skipLibCheck: true,
|
||||
types: [],
|
||||
outDir: path.join(basePath, 'built'),
|
||||
declaration: true,
|
||||
module: ts.ModuleKind.ES2015,
|
||||
moduleResolution: ts.ModuleResolutionKind.NodeJs,
|
||||
},
|
||||
{}, mockConsole.error);
|
||||
expect(mockConsole.error).not.toHaveBeenCalled();
|
||||
expect(result).toBe(0);
|
||||
expect(
|
||||
() => performCompilation(
|
||||
basePath, [path.join(basePath, 'test.ts')], {
|
||||
experimentalDecorators: true,
|
||||
skipLibCheck: true,
|
||||
types: [],
|
||||
outDir: path.join(basePath, 'built'),
|
||||
declaration: true,
|
||||
module: ts.ModuleKind.ES2015,
|
||||
moduleResolution: ts.ModuleResolutionKind.NodeJs,
|
||||
},
|
||||
{}))
|
||||
.not.toThrow();
|
||||
});
|
||||
|
||||
it('should not print the stack trace if user input file does not exist', () => {
|
||||
|
@ -292,7 +292,7 @@ describe('ngc command-line', () => {
|
|||
.toBe(true);
|
||||
});
|
||||
|
||||
it('should compile with a explicit tsconfig reference', () => {
|
||||
it('should compile with an explicit tsconfig reference', () => {
|
||||
writeConfig(`{
|
||||
"extends": "./tsconfig-base.json",
|
||||
"files": ["mymodule.ts"]
|
||||
|
@ -316,6 +316,92 @@ describe('ngc command-line', () => {
|
|||
.toBe(true);
|
||||
});
|
||||
|
||||
describe('closure', () => {
|
||||
it('should not generate closure specific code by default', () => {
|
||||
writeConfig(`{
|
||||
"extends": "./tsconfig-base.json",
|
||||
"files": ["mymodule.ts"]
|
||||
}`);
|
||||
write('mymodule.ts', `
|
||||
import {NgModule, Component} from '@angular/core';
|
||||
|
||||
@Component({template: ''})
|
||||
export class MyComp {}
|
||||
|
||||
@NgModule({declarations: [MyComp]})
|
||||
export class MyModule {}
|
||||
`);
|
||||
|
||||
const mockConsole = {error: (s: string) => {}};
|
||||
const exitCode = main(['-p', basePath], mockConsole.error);
|
||||
expect(exitCode).toEqual(0);
|
||||
|
||||
const mymodulejs = path.resolve(outDir, 'mymodule.js');
|
||||
const mymoduleSource = fs.readFileSync(mymodulejs, 'utf8');
|
||||
expect(mymoduleSource).not.toContain('@fileoverview added by tsickle');
|
||||
expect(mymoduleSource).toContain('MyComp.decorators = [');
|
||||
});
|
||||
|
||||
it('should add closure annotations', () => {
|
||||
writeConfig(`{
|
||||
"extends": "./tsconfig-base.json",
|
||||
"angularCompilerOptions": {
|
||||
"annotateForClosureCompiler": true
|
||||
},
|
||||
"files": ["mymodule.ts"]
|
||||
}`);
|
||||
write('mymodule.ts', `
|
||||
import {NgModule, Component} from '@angular/core';
|
||||
|
||||
@Component({template: ''})
|
||||
export class MyComp {
|
||||
fn(p: any) {}
|
||||
}
|
||||
|
||||
@NgModule({declarations: [MyComp]})
|
||||
export class MyModule {}
|
||||
`);
|
||||
|
||||
const mockConsole = {error: (s: string) => {}};
|
||||
const exitCode = main(['-p', basePath], mockConsole.error);
|
||||
expect(exitCode).toEqual(0);
|
||||
|
||||
const mymodulejs = path.resolve(outDir, 'mymodule.js');
|
||||
const mymoduleSource = fs.readFileSync(mymodulejs, 'utf8');
|
||||
expect(mymoduleSource).toContain('@fileoverview added by tsickle');
|
||||
expect(mymoduleSource).toContain('@param {?} p');
|
||||
});
|
||||
|
||||
it('should add metadata as decorators', () => {
|
||||
writeConfig(`{
|
||||
"extends": "./tsconfig-base.json",
|
||||
"angularCompilerOptions": {
|
||||
"annotationsAs": "decorators"
|
||||
},
|
||||
"files": ["mymodule.ts"]
|
||||
}`);
|
||||
write('mymodule.ts', `
|
||||
import {NgModule, Component} from '@angular/core';
|
||||
|
||||
@Component({template: ''})
|
||||
export class MyComp {
|
||||
fn(p: any) {}
|
||||
}
|
||||
|
||||
@NgModule({declarations: [MyComp]})
|
||||
export class MyModule {}
|
||||
`);
|
||||
|
||||
const mockConsole = {error: (s: string) => {}};
|
||||
const exitCode = main(['-p', basePath], mockConsole.error);
|
||||
expect(exitCode).toEqual(0);
|
||||
|
||||
const mymodulejs = path.resolve(outDir, 'mymodule.js');
|
||||
const mymoduleSource = fs.readFileSync(mymodulejs, 'utf8');
|
||||
expect(mymoduleSource).toContain('MyComp = __decorate([');
|
||||
});
|
||||
});
|
||||
|
||||
describe('expression lowering', () => {
|
||||
beforeEach(() => {
|
||||
writeConfig(`{
|
||||
|
@ -437,7 +523,7 @@ describe('ngc command-line', () => {
|
|||
import {CommonModule} from '@angular/common';
|
||||
import {NgModule} from '@angular/core';
|
||||
|
||||
class Foo {}
|
||||
export class Foo {}
|
||||
|
||||
export const factory = () => new Foo();
|
||||
|
||||
|
@ -557,7 +643,7 @@ describe('ngc command-line', () => {
|
|||
export class FlatModule {
|
||||
}`);
|
||||
|
||||
const exitCode = performCompilation(
|
||||
const emitResult = performCompilation(
|
||||
basePath, [path.join(basePath, 'public-api.ts')], {
|
||||
target: ts.ScriptTarget.ES5,
|
||||
experimentalDecorators: true,
|
||||
|
@ -578,7 +664,7 @@ describe('ngc command-line', () => {
|
|||
});
|
||||
|
||||
|
||||
expect(exitCode).toEqual(0);
|
||||
expect(emitResult.errorCode).toEqual(0);
|
||||
shouldExist('index.js');
|
||||
shouldExist('index.metadata.json');
|
||||
});
|
||||
|
@ -758,7 +844,7 @@ describe('ngc command-line', () => {
|
|||
write(path.join(dir, 'tsconfig.json'), `
|
||||
{
|
||||
"angularCompilerOptions": {
|
||||
"generateCodeForLibraries": false,
|
||||
"generateCodeForLibraries": true,
|
||||
"enableSummariesForJit": true
|
||||
},
|
||||
"compilerOptions": {
|
||||
|
@ -820,7 +906,7 @@ describe('ngc command-line', () => {
|
|||
shouldExist('lib1/module.ngfactory.d.ts');
|
||||
});
|
||||
|
||||
it('should be able to compiler library 2', () => {
|
||||
it('should be able to compile library 2', () => {
|
||||
expect(main(['-p', path.join(basePath, 'lib1')])).toBe(0);
|
||||
expect(main(['-p', path.join(basePath, 'lib2')])).toBe(0);
|
||||
shouldExist('lib2/module.js');
|
||||
|
|
|
@ -63,7 +63,7 @@ describe('TypeScriptNodeEmitter', () => {
|
|||
expect(emitStmt(someVar.set(o.literal(1)).toDeclStmt(null, [o.StmtModifier.Final])))
|
||||
.toEqual(`var someVar = 1;`);
|
||||
expect(emitStmt(someVar.set(o.literal(1)).toDeclStmt(null, [o.StmtModifier.Exported])))
|
||||
.toEqual(`exports.someVar = 1;`);
|
||||
.toEqual(`var someVar = 1; exports.someVar = someVar;`);
|
||||
});
|
||||
|
||||
describe('declare variables with ExternExpressions as values', () => {
|
||||
|
@ -71,7 +71,7 @@ describe('TypeScriptNodeEmitter', () => {
|
|||
// identifier is in the same module -> no reexport
|
||||
expect(emitStmt(someVar.set(o.importExpr(sameModuleIdentifier)).toDeclStmt(null, [
|
||||
o.StmtModifier.Exported
|
||||
]))).toEqual('exports.someVar = someLocalId;');
|
||||
]))).toEqual('var someVar = someLocalId; exports.someVar = someVar;');
|
||||
});
|
||||
|
||||
it('should create no reexport if the variable is not exported', () => {
|
||||
|
@ -84,7 +84,7 @@ describe('TypeScriptNodeEmitter', () => {
|
|||
expect(emitStmt(someVar.set(o.importExpr(externalModuleIdentifier))
|
||||
.toDeclStmt(o.DYNAMIC_TYPE, [o.StmtModifier.Exported])))
|
||||
.toEqual(
|
||||
`const i0 = require("/somePackage/someOtherPath"); exports.someVar = i0.someExternalId;`);
|
||||
`const i0 = require("/somePackage/someOtherPath"); var someVar = i0.someExternalId; exports.someVar = someVar;`);
|
||||
});
|
||||
|
||||
it('should create a reexport', () => {
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
"@angular/platform-server": "file:../../../dist/packages-dist/platform-server",
|
||||
"express": "^4.14.1",
|
||||
"rxjs": "file:../../../node_modules/rxjs",
|
||||
"typescript": "2.1.6",
|
||||
"typescript": "2.3.x",
|
||||
"zone.js": "^0.8.10"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
@ -8,7 +8,7 @@ LINKABLE_PKGS=(
|
|||
$(pwd)/dist/packages-dist/{common,forms,core,compiler,compiler-cli,platform-{browser,server},platform-browser-dynamic,router,http,animations,tsc-wrapped}
|
||||
)
|
||||
|
||||
TYPESCRIPT_2_1=typescript@2.1.5
|
||||
TYPESCRIPT_2_3=typescript@2.3.x
|
||||
PKGS=(
|
||||
reflect-metadata@0.1.8
|
||||
zone.js@0.6.25
|
||||
|
@ -33,7 +33,7 @@ cp -v package.json $TMP
|
|||
(
|
||||
cd $TMP
|
||||
set -ex -o pipefail
|
||||
npm install ${PKGS[*]} $TYPESCRIPT_2_1
|
||||
npm install ${PKGS[*]} $TYPESCRIPT_2_3
|
||||
# TODO(alexeagle): allow this to be npm link instead
|
||||
npm install ${LINKABLE_PKGS[*]}
|
||||
|
||||
|
@ -54,7 +54,6 @@ cp -v package.json $TMP
|
|||
# Copy the html files from source to the emitted output
|
||||
cp flat_module/src/*.html node_modules/flat_module/src
|
||||
|
||||
./node_modules/.bin/ngc -p tsconfig-build-alt.json --missingTranslation=error --i18nFormat=xlf
|
||||
./node_modules/.bin/ngc -p tsconfig-build.json --i18nFile=src/messages.fi.xlf --locale=fi --i18nFormat=xlf
|
||||
|
||||
./node_modules/.bin/ng-xi18n -p tsconfig-xi18n.json --i18nFormat=xlf --locale=fr
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
// TODO(chuckj): Remove the requirment for a fake 'reflect` implementation from
|
||||
// TODO(chuckj): Remove the requirement for a fake 'reflect` implementation from
|
||||
// the compiler
|
||||
import 'reflect-metadata';
|
||||
import {performCompilation} from '@angular/compiler-cli';
|
||||
|
@ -28,16 +28,7 @@ function main(args: string[]) {
|
|||
const basePath = path.resolve(process.cwd(), projectDir);
|
||||
const result = performCompilation(basePath, files, options, ngOptions, undefined);
|
||||
|
||||
if (result === 0) {
|
||||
// Ensure that expected output files exist.
|
||||
if (ngOptions && ngOptions.expectedOut) {
|
||||
for (const out of ngOptions.expectedOut) {
|
||||
fs.appendFileSync(out, '', 'utf-8');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return result.errorCode;
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
|
|
Loading…
Reference in New Issue