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 run @yarn//:yarn
|
||||||
- run: bazel build packages/...
|
- run: bazel build packages/...
|
||||||
|
- run: bazel test @angular//...
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: angular-{{ .Branch }}-{{ checksum "yarn.lock" }}
|
key: angular-{{ .Branch }}-{{ checksum "yarn.lock" }}
|
||||||
paths:
|
paths:
|
||||||
|
|
|
@ -12,6 +12,7 @@ filegroup(
|
||||||
# This won't scale in the general case.
|
# This won't scale in the general case.
|
||||||
# TODO(alexeagle): figure out what to do
|
# TODO(alexeagle): figure out what to do
|
||||||
srcs = glob(["/".join(["node_modules", pkg, "**", ext]) for pkg in [
|
srcs = glob(["/".join(["node_modules", pkg, "**", ext]) for pkg in [
|
||||||
|
"jasmine",
|
||||||
"typescript",
|
"typescript",
|
||||||
"zone.js",
|
"zone.js",
|
||||||
"rxjs",
|
"rxjs",
|
||||||
|
|
|
@ -5,7 +5,8 @@ load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
|
||||||
git_repository(
|
git_repository(
|
||||||
name = "build_bazel_rules_nodejs",
|
name = "build_bazel_rules_nodejs",
|
||||||
remote = "https://github.com/bazelbuild/rules_nodejs.git",
|
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")
|
load("@build_bazel_rules_nodejs//:defs.bzl", "node_repositories")
|
||||||
|
@ -20,4 +21,4 @@ local_repository(
|
||||||
local_repository(
|
local_repository(
|
||||||
name = "angular",
|
name = "angular",
|
||||||
path = "packages/bazel",
|
path = "packages/bazel",
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
load("@build_bazel_rules_nodejs//:defs.bzl", "nodejs_binary")
|
load("@build_bazel_rules_nodejs//:defs.bzl", "nodejs_binary")
|
||||||
load("@build_bazel_rules_typescript//:defs.bzl", "ts_library")
|
load("@build_bazel_rules_typescript//:defs.bzl", "ts_library")
|
||||||
|
|
||||||
licenses(["notice"]) # Apache 2.0
|
|
||||||
|
|
||||||
ts_library(
|
ts_library(
|
||||||
name = "ngc_lib",
|
name = "ngc_lib",
|
||||||
srcs = [
|
srcs = [
|
||||||
"index.ts",
|
"index.ts",
|
||||||
"extract_i18n.ts",
|
"extract_i18n.ts",
|
||||||
],
|
],
|
||||||
|
module_name = "@angular/bazel",
|
||||||
deps = [
|
deps = [
|
||||||
# BEGIN-INTERNAL
|
# BEGIN-INTERNAL
|
||||||
# Only needed when compiling within the Angular repo.
|
# Only needed when compiling within the Angular repo.
|
||||||
|
@ -18,6 +17,7 @@ ts_library(
|
||||||
"@build_bazel_rules_typescript//internal/tsc_wrapped"
|
"@build_bazel_rules_typescript//internal/tsc_wrapped"
|
||||||
],
|
],
|
||||||
tsconfig = ":tsconfig.json",
|
tsconfig = ":tsconfig.json",
|
||||||
|
visibility = ["//test/ngc-wrapped:__subpackages__"],
|
||||||
)
|
)
|
||||||
|
|
||||||
nodejs_binary(
|
nodejs_binary(
|
||||||
|
|
|
@ -40,7 +40,7 @@ export function main(args) {
|
||||||
/** The one FileCache instance used in this process. */
|
/** The one FileCache instance used in this process. */
|
||||||
const fileCache = new FileCache<ts.SourceFile>(debug);
|
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();
|
if (args[0] === '-p') args.shift();
|
||||||
// Strip leading at-signs, used to indicate a params file
|
// Strip leading at-signs, used to indicate a params file
|
||||||
const project = args[0].replace(/^@+/, '');
|
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