fix(compiler): add first bazel test for `ng_module` (#19703)
We were missing quite a bit of test coverage, this is the start of recreating it. PR Close #19703
This commit is contained in:
parent
621f87b2bd
commit
ad130d62d8
|
@ -42,6 +42,7 @@ jobs:
|
|||
|
||||
- run: bazel run @yarn//:yarn
|
||||
- run: bazel build packages/...
|
||||
- run: bazel test @angular//...
|
||||
- save_cache:
|
||||
key: angular-{{ .Branch }}-{{ checksum "yarn.lock" }}
|
||||
paths:
|
||||
|
|
|
@ -12,6 +12,7 @@ filegroup(
|
|||
# This won't scale in the general case.
|
||||
# TODO(alexeagle): figure out what to do
|
||||
srcs = glob(["/".join(["node_modules", pkg, "**", ext]) for pkg in [
|
||||
"jasmine",
|
||||
"typescript",
|
||||
"zone.js",
|
||||
"rxjs",
|
||||
|
|
|
@ -5,7 +5,8 @@ load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
|
|||
git_repository(
|
||||
name = "build_bazel_rules_nodejs",
|
||||
remote = "https://github.com/bazelbuild/rules_nodejs.git",
|
||||
tag = "0.1.6",
|
||||
# TODO(alexeagle): use the correct tag here.
|
||||
commit = "2c6243df53fd33fdab283ebdd01582e4eb815db8",
|
||||
)
|
||||
|
||||
load("@build_bazel_rules_nodejs//:defs.bzl", "node_repositories")
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
load("@build_bazel_rules_nodejs//:defs.bzl", "nodejs_binary")
|
||||
load("@build_bazel_rules_typescript//:defs.bzl", "ts_library")
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
|
||||
ts_library(
|
||||
name = "ngc_lib",
|
||||
srcs = [
|
||||
"index.ts",
|
||||
"extract_i18n.ts",
|
||||
],
|
||||
module_name = "@angular/bazel",
|
||||
deps = [
|
||||
# BEGIN-INTERNAL
|
||||
# Only needed when compiling within the Angular repo.
|
||||
|
@ -18,6 +17,7 @@ ts_library(
|
|||
"@build_bazel_rules_typescript//internal/tsc_wrapped"
|
||||
],
|
||||
tsconfig = ":tsconfig.json",
|
||||
visibility = ["//test/ngc-wrapped:__subpackages__"],
|
||||
)
|
||||
|
||||
nodejs_binary(
|
||||
|
|
|
@ -40,7 +40,7 @@ export function main(args) {
|
|||
/** The one FileCache instance used in this process. */
|
||||
const fileCache = new FileCache<ts.SourceFile>(debug);
|
||||
|
||||
function runOneBuild(args: string[], inputs?: {[path: string]: string}): boolean {
|
||||
export function runOneBuild(args: string[], inputs?: {[path: string]: string}): boolean {
|
||||
if (args[0] === '-p') args.shift();
|
||||
// Strip leading at-signs, used to indicate a params file
|
||||
const project = args[0].replace(/^@+/, '');
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
load("@build_bazel_rules_nodejs//:defs.bzl", "jasmine_node_test")
|
||||
load("@build_bazel_rules_typescript//:defs.bzl", "ts_library")
|
||||
|
||||
ts_library(
|
||||
name = "ngc_test_lib",
|
||||
srcs = [
|
||||
"index_test.ts",
|
||||
"test_support.ts",
|
||||
"tsconfig_template.ts",
|
||||
],
|
||||
deps = [
|
||||
# BEGIN-INTERNAL
|
||||
# Only needed when compiling within the Angular repo.
|
||||
# Users will get this dependency from node_modules.
|
||||
"@//packages/compiler-cli",
|
||||
# END-INTERNAL
|
||||
"//src/ngc-wrapped:ngc_lib"
|
||||
],
|
||||
tsconfig = ":tsconfig.json",
|
||||
)
|
||||
|
||||
# We need a filegroup so that we can refer
|
||||
# .d.ts files (by default, jasmine_node_test would get the .js files).
|
||||
filegroup(
|
||||
name = "angular_core",
|
||||
srcs = ["@//packages/core"]
|
||||
)
|
||||
|
||||
jasmine_node_test(
|
||||
name = "ngc_test",
|
||||
srcs = [":ngc_test_lib"],
|
||||
data = [
|
||||
"@build_bazel_rules_typescript//internal:worker_protocol.proto",
|
||||
":angular_core",
|
||||
"//test/ngc-wrapped/empty:empty_tsconfig.json",
|
||||
"//test/ngc-wrapped/empty:tsconfig.json",
|
||||
],
|
||||
size="small",
|
||||
)
|
|
@ -0,0 +1,10 @@
|
|||
load("@angular//:index.bzl", "ng_module")
|
||||
|
||||
package(default_visibility=["//test:__subpackages__"])
|
||||
|
||||
ng_module(
|
||||
name = "empty",
|
||||
srcs = ["empty.ts"],
|
||||
deps = ["@//packages/core"],
|
||||
tsconfig = ":tsconfig.json",
|
||||
)
|
|
@ -0,0 +1 @@
|
|||
# Empty ng_module to capture the default tsconfig.json
|
|
@ -0,0 +1,10 @@
|
|||
/**
|
||||
* @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
|
||||
*/
|
||||
|
||||
// Empty file for an empty ng_module
|
||||
// to capture the tsconfig used by ng_module.
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"paths": {
|
||||
"@angular/core": ["../../../../dist/packages/core"]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/**
|
||||
* @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 path from 'path';
|
||||
|
||||
import {setup} from './test_support';
|
||||
|
||||
describe('ngc_wrapped', () => {
|
||||
|
||||
it('should work', () => {
|
||||
const {read, write, runOneBuild, writeConfig, shouldExist, basePath} = setup();
|
||||
|
||||
write('some_project/index.ts', `
|
||||
import {Component} from '@angular/core';
|
||||
console.log('works: ', Component);
|
||||
`);
|
||||
|
||||
writeConfig({
|
||||
srcTargetPath: 'some_project',
|
||||
});
|
||||
|
||||
// expect no error
|
||||
expect(runOneBuild()).toBe(true);
|
||||
|
||||
shouldExist('bazel-bin/some_project/index.js');
|
||||
|
||||
expect(read('bazel-bin/some_project/index.js'))
|
||||
.toContain(`console.log('works: ', core_1.Component);`);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,161 @@
|
|||
/**
|
||||
* @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 {runOneBuild} from '@angular/bazel';
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {createTsConfig} from './tsconfig_template';
|
||||
|
||||
export interface TestSupport {
|
||||
basePath: string;
|
||||
runfilesPath: string;
|
||||
angularCorePath: string;
|
||||
writeConfig({
|
||||
srcTargetPath, depPaths, pathMapping,
|
||||
}: {
|
||||
srcTargetPath: string,
|
||||
depPaths?: string[],
|
||||
pathMapping?: Array<{moduleName: string; path: string;}>,
|
||||
}): void;
|
||||
read(fileName: string): string;
|
||||
write(fileName: string, content: string): void;
|
||||
writeFiles(...mockDirs: {[fileName: string]: string}[]): void;
|
||||
shouldExist(fileName: string): void;
|
||||
shouldNotExist(fileName: string): void;
|
||||
runOneBuild(): void;
|
||||
}
|
||||
|
||||
export function setup(
|
||||
{
|
||||
bazelBin = 'bazel-bin', tsconfig = 'tsconfig.json',
|
||||
}: {
|
||||
bazelBin?: string,
|
||||
tsconfig?: string,
|
||||
} = {}): TestSupport {
|
||||
const runfilesPath = process.env['RUNFILES'];
|
||||
|
||||
const basePath = makeTempDir(runfilesPath);
|
||||
|
||||
const bazelBinPath = path.resolve(basePath, bazelBin);
|
||||
fs.mkdirSync(bazelBinPath);
|
||||
|
||||
const angularCorePath = path.resolve(runfilesPath, 'angular_src', 'packages', 'core');
|
||||
const ngFiles = listFilesRecursive(angularCorePath);
|
||||
|
||||
const tsConfigJsonPath = path.resolve(basePath, tsconfig);
|
||||
|
||||
return {
|
||||
basePath,
|
||||
runfilesPath,
|
||||
angularCorePath,
|
||||
write,
|
||||
read,
|
||||
writeFiles,
|
||||
writeConfig,
|
||||
shouldExist,
|
||||
shouldNotExist,
|
||||
runOneBuild: runOneBuildImpl
|
||||
};
|
||||
|
||||
// -----------------
|
||||
// helpers
|
||||
|
||||
function write(fileName: string, content: string) {
|
||||
const dir = path.dirname(fileName);
|
||||
if (dir != '.') {
|
||||
const newDir = path.resolve(basePath, dir);
|
||||
if (!fs.existsSync(newDir)) fs.mkdirSync(newDir);
|
||||
}
|
||||
fs.writeFileSync(path.resolve(basePath, fileName), content, {encoding: 'utf-8'});
|
||||
}
|
||||
|
||||
function read(fileName: string) {
|
||||
return fs.readFileSync(path.resolve(basePath, fileName), {encoding: 'utf-8'});
|
||||
}
|
||||
|
||||
function writeFiles(...mockDirs: {[fileName: string]: string}[]) {
|
||||
mockDirs.forEach(
|
||||
(dir) => { Object.keys(dir).forEach((fileName) => { write(fileName, dir[fileName]); }); });
|
||||
}
|
||||
|
||||
function writeConfig({
|
||||
srcTargetPath, depPaths = [], pathMapping = [],
|
||||
}: {
|
||||
srcTargetPath: string,
|
||||
depPaths?: string[],
|
||||
pathMapping?: Array<{moduleName: string; path: string;}>,
|
||||
}) {
|
||||
srcTargetPath = path.resolve(basePath, srcTargetPath);
|
||||
const compilationTargetSrc = listFilesRecursive(srcTargetPath);
|
||||
const target = '//' + path.relative(basePath, srcTargetPath);
|
||||
const files = [...compilationTargetSrc];
|
||||
|
||||
depPaths = depPaths.concat([angularCorePath]);
|
||||
pathMapping = pathMapping.concat([{moduleName: '@angular/core', path: angularCorePath}]);
|
||||
|
||||
for (const depPath of depPaths) {
|
||||
files.push(...listFilesRecursive(depPath).filter(f => f.endsWith('.d.ts')));
|
||||
}
|
||||
|
||||
const pathMappingObj = {};
|
||||
for (const mapping of pathMapping) {
|
||||
pathMappingObj[mapping.moduleName] = [mapping.path];
|
||||
pathMappingObj[path.join(mapping.moduleName, '*')] = [path.join(mapping.path, '*')];
|
||||
}
|
||||
|
||||
const emptyTsConfig = ts.readConfigFile(
|
||||
path.resolve(
|
||||
runfilesPath, 'angular', 'test', 'ngc-wrapped', 'empty', 'empty_tsconfig.json'),
|
||||
read);
|
||||
|
||||
const tsconfig = createTsConfig({
|
||||
defaultTsConfig: emptyTsConfig.config,
|
||||
rootDir: basePath,
|
||||
target: target,
|
||||
outDir: bazelBinPath, compilationTargetSrc,
|
||||
files: files,
|
||||
pathMapping: pathMappingObj,
|
||||
});
|
||||
write(path.resolve(basePath, tsConfigJsonPath), JSON.stringify(tsconfig, null, 2));
|
||||
}
|
||||
|
||||
function shouldExist(fileName: string) {
|
||||
if (!fs.existsSync(path.resolve(basePath, fileName))) {
|
||||
throw new Error(`Expected ${fileName} to be emitted (basePath: ${basePath})`);
|
||||
}
|
||||
}
|
||||
|
||||
function shouldNotExist(fileName: string) {
|
||||
if (fs.existsSync(path.resolve(basePath, fileName))) {
|
||||
throw new Error(`Did not expect ${fileName} to be emitted (basePath: ${basePath})`);
|
||||
}
|
||||
}
|
||||
|
||||
function runOneBuildImpl(): boolean { return runOneBuild(['@' + tsConfigJsonPath]); }
|
||||
}
|
||||
|
||||
function makeTempDir(baseDir): string {
|
||||
const id = (Math.random() * 1000000).toFixed(0);
|
||||
const dir = path.join(baseDir, `tmp.${id}`);
|
||||
fs.mkdirSync(dir);
|
||||
return dir;
|
||||
}
|
||||
|
||||
export function listFilesRecursive(dir: string, fileList: string[] = []) {
|
||||
fs.readdirSync(dir).map(file => {
|
||||
if (fs.statSync(path.join(dir, file)).isDirectory()) {
|
||||
listFilesRecursive(path.join(dir, file), fileList);
|
||||
} else {
|
||||
fileList.push(path.join(dir, file));
|
||||
}
|
||||
});
|
||||
return fileList;
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"lib": ["es5", "es2015.collection", "es2015.core"],
|
||||
"types": ["node", "jasmine"]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
/**
|
||||
* @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 path from 'path';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
const EXT = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
|
||||
|
||||
export interface TsConfigOptions {
|
||||
defaultTsConfig: any;
|
||||
outDir: string;
|
||||
rootDir: string;
|
||||
pathMapping: {[pattern: string]: string[]};
|
||||
// e.g. //packages/core:core
|
||||
target: string;
|
||||
compilationTargetSrc: string[];
|
||||
files: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a tsconfig based on the default tsconfig
|
||||
* to adjust paths, ...
|
||||
*
|
||||
* @param options
|
||||
*/
|
||||
export function createTsConfig(options: TsConfigOptions) {
|
||||
const result = options.defaultTsConfig;
|
||||
|
||||
return {
|
||||
'extends': '../angular/test/ngc-wrapped/empty/tsconfig',
|
||||
'compilerOptions': {
|
||||
...result.compilerOptions,
|
||||
'outDir': options.outDir,
|
||||
'rootDir': options.rootDir,
|
||||
'rootDirs': [
|
||||
options.rootDir,
|
||||
],
|
||||
'baseUrl': options.rootDir,
|
||||
'paths': {
|
||||
'*': [
|
||||
'./*',
|
||||
],
|
||||
...options.pathMapping,
|
||||
},
|
||||
// we have to set this as the default tsconfig is made of es6 mode
|
||||
'target': 'es5',
|
||||
// we have to set this as the default tsconfig is made of es6 mode
|
||||
'module': 'commonjs',
|
||||
// if we specify declarationDir, we also have to specify
|
||||
// declaration in the same tsconfig.json, otherwise ts will error.
|
||||
'declaration': true,
|
||||
'declarationDir': options.outDir,
|
||||
},
|
||||
'bazelOptions': {
|
||||
...result.bazelOptions,
|
||||
'workspaceName': 'angular',
|
||||
'target': options.target,
|
||||
// we have to set this as the default tsconfig is made of es6 mode
|
||||
'es5Mode': true,
|
||||
'manifest': createManifestPath(options),
|
||||
'compilationTargetSrc': options.compilationTargetSrc,
|
||||
},
|
||||
'files': options.files,
|
||||
'angularCompilerOptions': {
|
||||
...result.angularCompilerOptions,
|
||||
'expectedOut': [
|
||||
...options.compilationTargetSrc.map(src => srcToExpectedOut(src, 'js', options)),
|
||||
...options.compilationTargetSrc.map(src => srcToExpectedOut(src, 'd.ts', options)),
|
||||
...options.compilationTargetSrc.map(src => srcToExpectedOut(src, 'ngfactory.js', options)),
|
||||
...options.compilationTargetSrc.map(
|
||||
src => srcToExpectedOut(src, 'ngfactory.d.ts', options)),
|
||||
...options.compilationTargetSrc.map(src => srcToExpectedOut(src, 'ngsummary.js', options)),
|
||||
...options.compilationTargetSrc.map(
|
||||
src => srcToExpectedOut(src, 'ngsummary.d.ts', options)),
|
||||
...options.compilationTargetSrc.map(
|
||||
src => srcToExpectedOut(src, 'ngsummary.json', options)),
|
||||
]
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function srcToExpectedOut(srcFile: string, suffix: string, options: TsConfigOptions): string {
|
||||
const baseName = path.basename(srcFile).replace(EXT, '');
|
||||
return path.join(
|
||||
path.relative(options.rootDir, options.outDir),
|
||||
path.relative(options.rootDir, path.dirname(srcFile)), baseName) +
|
||||
'.' + suffix;
|
||||
}
|
||||
|
||||
function createManifestPath(options: TsConfigOptions): string {
|
||||
return path.resolve(options.outDir, options.target.replace(/\/\/|@/g, '').replace(/:/g, '/')) +
|
||||
'.es5.MF';
|
||||
}
|
Loading…
Reference in New Issue