feat(compiler-cli): add parameters to ngc main needed by bazel rules (#17885)
This commit is contained in:
parent
0ede642cb9
commit
c1474f33be
@ -29,9 +29,9 @@ function formatDiagnostics(cwd: string, diags: Diagnostics): string {
|
|||||||
if (diags && diags.length) {
|
if (diags && diags.length) {
|
||||||
if (isTsDiagnostics(diags)) {
|
if (isTsDiagnostics(diags)) {
|
||||||
return ts.formatDiagnostics(diags, {
|
return ts.formatDiagnostics(diags, {
|
||||||
getCurrentDirectory(): string{return cwd;},
|
getCurrentDirectory: () => cwd,
|
||||||
getCanonicalFileName(fileName: string): string{return fileName;},
|
getCanonicalFileName: fileName => fileName,
|
||||||
getNewLine(): string{return '\n';}
|
getNewLine: () => ts.sys.newLine
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
return diags
|
return diags
|
||||||
@ -101,30 +101,54 @@ export function readConfiguration(
|
|||||||
const ngOptions = config.angularCompilerOptions || {};
|
const ngOptions = config.angularCompilerOptions || {};
|
||||||
// Ignore the genDir option
|
// Ignore the genDir option
|
||||||
ngOptions.genDir = basePath;
|
ngOptions.genDir = basePath;
|
||||||
for (const key of Object.keys(parsed.options)) {
|
|
||||||
ngOptions[key] = parsed.options[key];
|
|
||||||
}
|
|
||||||
|
|
||||||
return {parsed, ngOptions};
|
return {parsed, ngOptions};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function main(args: string[], consoleError: (s: string) => void = console.error): number {
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function main(
|
||||||
|
args: string[], consoleError: (s: string) => void = console.error, files?: string[],
|
||||||
|
options?: ts.CompilerOptions, ngOptions?: any): number {
|
||||||
try {
|
try {
|
||||||
const parsedArgs = require('minimist')(args);
|
const parsedArgs = require('minimist')(args);
|
||||||
const project = parsedArgs.p || parsedArgs.project || '.';
|
const project = parsedArgs.p || parsedArgs.project || '.';
|
||||||
|
|
||||||
const projectDir = fs.lstatSync(project).isFile() ? path.dirname(project) : project;
|
const projectDir = getProjectDirectory(project);
|
||||||
|
|
||||||
|
|
||||||
// file names in tsconfig are resolved relative to this absolute path
|
// file names in tsconfig are resolved relative to this absolute path
|
||||||
const basePath = path.resolve(process.cwd(), projectDir);
|
const basePath = path.resolve(process.cwd(), projectDir);
|
||||||
|
|
||||||
const {parsed, ngOptions} = readConfiguration(project, basePath);
|
if (!files || !options || !ngOptions) {
|
||||||
|
const {parsed, ngOptions: readNgOptions} = readConfiguration(project, basePath);
|
||||||
|
if (!files) files = parsed.fileNames;
|
||||||
|
if (!options) options = parsed.options;
|
||||||
|
if (!ngOptions) ngOptions = readNgOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore what the tsconfig.json for baseDir and genDir
|
||||||
ngOptions.basePath = basePath;
|
ngOptions.basePath = basePath;
|
||||||
|
ngOptions.genDir = basePath;
|
||||||
|
|
||||||
let host = ts.createCompilerHost(parsed.options, true);
|
let host = ts.createCompilerHost(options, true);
|
||||||
|
host.realpath = p => p;
|
||||||
|
|
||||||
const rootFileNames = parsed.fileNames.slice(0);
|
const rootFileNames = files.map(f => path.normalize(f));
|
||||||
|
|
||||||
const addGeneratedFileName =
|
const addGeneratedFileName =
|
||||||
(fileName: string) => {
|
(fileName: string) => {
|
||||||
@ -141,10 +165,11 @@ export function main(args: string[], consoleError: (s: string) => void = console
|
|||||||
host = bundleHost;
|
host = bundleHost;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ngHost = ng.createHost({tsHost: host, options: ngOptions});
|
const ngHostOptions = {...options, ...ngOptions};
|
||||||
|
const ngHost = ng.createHost({tsHost: host, options: ngHostOptions});
|
||||||
|
|
||||||
const ngProgram =
|
const ngProgram =
|
||||||
ng.createProgram({rootNames: rootFileNames, host: ngHost, options: ngOptions});
|
ng.createProgram({rootNames: rootFileNames, host: ngHost, options: ngHostOptions});
|
||||||
|
|
||||||
// Check parameter diagnostics
|
// Check parameter diagnostics
|
||||||
check(basePath, ngProgram.getTsOptionDiagnostics(), ngProgram.getNgOptionDiagnostics());
|
check(basePath, ngProgram.getTsOptionDiagnostics(), ngProgram.getNgOptionDiagnostics());
|
||||||
|
@ -385,6 +385,7 @@ function createProgramWithStubsHost(
|
|||||||
getCanonicalFileName = (fileName: string) => originalHost.getCanonicalFileName(fileName);
|
getCanonicalFileName = (fileName: string) => originalHost.getCanonicalFileName(fileName);
|
||||||
useCaseSensitiveFileNames = () => originalHost.useCaseSensitiveFileNames();
|
useCaseSensitiveFileNames = () => originalHost.useCaseSensitiveFileNames();
|
||||||
getNewLine = () => originalHost.getNewLine();
|
getNewLine = () => originalHost.getNewLine();
|
||||||
|
realPath = (p: string) => p;
|
||||||
fileExists = (fileName: string) =>
|
fileExists = (fileName: string) =>
|
||||||
this.generatedFiles.has(fileName) || originalHost.fileExists(fileName);
|
this.generatedFiles.has(fileName) || originalHost.fileExists(fileName);
|
||||||
};
|
};
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
import {makeTempDir} from '@angular/tsc-wrapped/test/test_support';
|
import {makeTempDir} from '@angular/tsc-wrapped/test/test_support';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
import {main} from '../src/ngc';
|
import {main} from '../src/ngc';
|
||||||
|
|
||||||
@ -40,6 +41,7 @@ describe('ngc command-line', () => {
|
|||||||
write('tsconfig-base.json', `{
|
write('tsconfig-base.json', `{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
"types": [],
|
"types": [],
|
||||||
"outDir": "built",
|
"outDir": "built",
|
||||||
"declaration": true,
|
"declaration": true,
|
||||||
@ -72,6 +74,28 @@ describe('ngc command-line', () => {
|
|||||||
expect(result).toBe(0);
|
expect(result).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should be able to be called without a config file by passing options explicitly', () => {
|
||||||
|
write('test.ts', 'export const A = 1;');
|
||||||
|
|
||||||
|
const mockConsole = {error: (s: string) => {}};
|
||||||
|
|
||||||
|
spyOn(mockConsole, 'error');
|
||||||
|
|
||||||
|
const result = main(
|
||||||
|
['-p', basePath], mockConsole.error, [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,
|
||||||
|
},
|
||||||
|
{});
|
||||||
|
expect(mockConsole.error).not.toHaveBeenCalled();
|
||||||
|
expect(result).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
it('should not print the stack trace if user input file does not exist', () => {
|
it('should not print the stack trace if user input file does not exist', () => {
|
||||||
writeConfig(`{
|
writeConfig(`{
|
||||||
"extends": "./tsconfig-base.json",
|
"extends": "./tsconfig-base.json",
|
||||||
@ -365,6 +389,61 @@ describe('ngc command-line', () => {
|
|||||||
shouldExist('index.metadata.json');
|
shouldExist('index.metadata.json');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should be able to build a flat module passing explicit options', () => {
|
||||||
|
write('public-api.ts', `
|
||||||
|
export * from './src/flat.component';
|
||||||
|
export * from './src/flat.module';`);
|
||||||
|
write('src/flat.component.html', '<div>flat module component</div>');
|
||||||
|
write('src/flat.component.ts', `
|
||||||
|
import {Component} from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'flat-comp',
|
||||||
|
templateUrl: 'flat.component.html',
|
||||||
|
})
|
||||||
|
export class FlatComponent {
|
||||||
|
}`);
|
||||||
|
write('src/flat.module.ts', `
|
||||||
|
import {NgModule} from '@angular/core';
|
||||||
|
|
||||||
|
import {FlatComponent} from './flat.component';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
FlatComponent,
|
||||||
|
],
|
||||||
|
exports: [
|
||||||
|
FlatComponent,
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class FlatModule {
|
||||||
|
}`);
|
||||||
|
|
||||||
|
const exitCode = main(
|
||||||
|
['-p', path.join(basePath, 'tsconfig.json')], undefined,
|
||||||
|
[path.join(basePath, 'public-api.ts')], {
|
||||||
|
target: ts.ScriptTarget.ES5,
|
||||||
|
experimentalDecorators: true,
|
||||||
|
noImplicitAny: true,
|
||||||
|
moduleResolution: ts.ModuleResolutionKind.NodeJs,
|
||||||
|
rootDir: basePath,
|
||||||
|
declaration: true,
|
||||||
|
lib: ['lib.es2015.d.ts', 'lib.dom.d.ts'],
|
||||||
|
baseUrl: basePath,
|
||||||
|
outDir: path.join(basePath, 'built'),
|
||||||
|
typeRoots: [path.join(basePath, 'node_modules/@types')]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
genDir: 'ng',
|
||||||
|
flatModuleId: 'flat_module',
|
||||||
|
flatModuleOutFile: 'index.js',
|
||||||
|
skipTemplateCodegen: true
|
||||||
|
});
|
||||||
|
expect(exitCode).toEqual(0);
|
||||||
|
shouldExist('index.js');
|
||||||
|
shouldExist('index.metadata.json');
|
||||||
|
});
|
||||||
|
|
||||||
describe('with a third-party library', () => {
|
describe('with a third-party library', () => {
|
||||||
const writeGenConfig = (skipCodegen: boolean) => {
|
const writeGenConfig = (skipCodegen: boolean) => {
|
||||||
writeConfig(`{
|
writeConfig(`{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user