feat(bazel): add an ng_package rule (#22221)

This produces a directory following the Angular Package layout spec.

Includes integration test coverage by making a minimal ng_package in integration/bazel.
Unit tests verify the content of the @angular/core and @angular/common packages.

This doesn't totally match our current output, but is good enough to unblock some
early adopters.

It re-uses logic from the rollup_bundle rule in rules_nodejs. It should also
eventually have the .pack and .publish secondary targets like npm_package rule.

PR Close #22221
This commit is contained in:
Alex Eagle
2018-02-13 11:26:06 -08:00
committed by Victor Berchet
parent 1dcbc12fd3
commit b43b164a61
23 changed files with 1101 additions and 61 deletions

View File

@ -8,5 +8,7 @@ Users should not load files under "/src"
"""
load("//packages/bazel/src:ng_module.bzl", _ng_module = "ng_module")
load("//packages/bazel/src/ng_package:ng_package.bzl", _ng_package = "ng_package")
ng_module = _ng_module
ng_package = _ng_package

View File

@ -4,13 +4,16 @@
"description": "Angular - bazel build rules",
"author": "angular",
"license": "MIT",
"dependencies": {
"@types/node": "6.0.84",
"@types/shelljs": "0.7.7",
"shelljs": "0.7.8",
"protobufjs": "5.0.0"
},
"peerDependencies": {
"@angular/compiler-cli": "0.0.0-PLACEHOLDER",
"typescript": ">=2.4.2 <2.7"
},
"dependencies": {
"@types/node": "6.0.84"
},
"repository": {
"type": "git",
"url": "https://github.com/angular/angular.git"

View File

@ -35,8 +35,10 @@ def _expected_outs(ctx):
factory_basename_set = depset([_basename_of(ctx, src) for src in ctx.files.factories])
for src in ctx.files.srcs + ctx.files.assets:
package_prefix = ctx.label.package + "/" if ctx.label.package else ""
if src.short_path.endswith(".ts") and not src.short_path.endswith(".d.ts"):
basename = src.short_path[len(ctx.label.package) + 1:-len(".ts")]
basename = src.short_path[len(package_prefix):-len(".ts")]
if len(factory_basename_set) == 0 or basename in factory_basename_set:
devmode_js = [
".ngfactory.js",
@ -48,7 +50,7 @@ def _expected_outs(ctx):
devmode_js = [".js"]
summaries = []
elif src.short_path.endswith(".css"):
basename = src.short_path[len(ctx.label.package) + 1:-len(".css")]
basename = src.short_path[len(package_prefix):-len(".css")]
devmode_js = [
".css.shim.ngstyle.js",
".css.ngstyle.js",
@ -252,15 +254,19 @@ def _write_bundle_index(ctx):
if ctx.attr.module_name:
tsconfig["angularCompilerOptions"]["flatModuleId"] = ctx.attr.module_name
entry_point = ctx.attr.entry_point if ctx.attr.entry_point else "index.ts"
# createBundleIndexHost in bundle_index_host.ts will throw if the "files" has more than one entry.
# We don't want to fail() here, however, because not all ng_module's will have the bundle index written.
# So we make the assumption that the index.ts file in the highest parent directory is the entry point.
index_file = None
for f in tsconfig["files"]:
if f.endswith("/index.ts"):
if f.endswith("/" + entry_point):
if not index_file or len(f) < len(index_file):
index_file = f
tsconfig["files"] = [index_file]
if index_file:
tsconfig["files"] = [index_file]
ctx.actions.write(tsconfig_file, json_marshal(tsconfig))
@ -361,6 +367,9 @@ ng_module = rule(
"node_modules": attr.label(
default = Label("@//:node_modules")
),
"entry_point": attr.string(),
"_index_bundler": attr.label(
executable = True,
cfg = "host",

View File

@ -0,0 +1,18 @@
package(default_visibility = ["//visibility:public"])
load("@build_bazel_rules_nodejs//:defs.bzl", "nodejs_binary")
load("@build_bazel_rules_typescript//:defs.bzl", "ts_library")
exports_files(["rollup.config.js"])
ts_library(
name = "lib",
srcs = glob(["*.ts"]),
tsconfig = ":tsconfig.json",
)
nodejs_binary(
name = "packager",
data = ["lib"],
entry_point = "angular/packages/bazel/src/ng_package/packager.js",
)

View File

@ -0,0 +1,229 @@
# 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
"""Implementation of the ng_package rule.
"""
load("@build_bazel_rules_nodejs//:internal/collect_es6_sources.bzl", "collect_es6_sources")
load("@build_bazel_rules_nodejs//:internal/rollup/rollup_bundle.bzl",
"write_rollup_config",
"rollup_module_mappings_aspect",
"run_uglify",
"ROLLUP_ATTRS")
load("//packages/bazel/src:esm5.bzl", "esm5_outputs_aspect", "ESM5Info")
# TODO(alexeagle): this list is incomplete, add more as material ramps up
WELL_KNOWN_GLOBALS = {
"@angular/core": "ng.core",
"@angular/common": "ng.common",
"@angular/platform-browser": "ng.platformBrowser",
"rxjs/Observable": "Rx",
"rxjs/Observer": "Rx",
"rxjs/Subject": "Rx",
"rxjs/Subscription": "Rx",
"rxjs/observable/merge": "Rx.Observable",
"rxjs/observable/of": "Rx.Observable.prototype",
"rxjs/operator/concatMap": "Rx.Observable.prototype",
"rxjs/operator/filter": "Rx.Observable.prototype",
"rxjs/operator/map": "Rx.Observable.prototype",
"rxjs/operator/share": "Rx.Observable.prototype",
}
def _rollup(ctx, rollup_config, entry_point, inputs, js_output, format = "es"):
map_output = ctx.actions.declare_file(js_output.basename + ".map", sibling = js_output)
args = ctx.actions.args()
args.add(["--config", rollup_config.path])
args.add(["--input", entry_point])
args.add(["--output.file", js_output.path])
args.add(["--output.format", format])
args.add(["--name", ctx.label.name])
# Note: if the input has external source maps then we need to also install and use
# `rollup-plugin-sourcemaps`, which will require us to use rollup.config.js file instead
# of command line args
args.add("--sourcemap")
globals = dict(WELL_KNOWN_GLOBALS, **ctx.attr.globals)
externals = globals.keys()
args.add("--external")
args.add(externals, join_with=",")
other_inputs = [ctx.executable._rollup, rollup_config]
if ctx.file.stamp_data:
other_inputs.append(ctx.file.stamp_data)
if ctx.file.license_banner:
other_inputs.append(ctx.file.license_banner)
ctx.actions.run(
progress_message = "Angular Packaging: rolling up %s" % ctx.label.name,
mnemonic = "AngularPackageRollup",
inputs = inputs.to_list() + other_inputs,
outputs = [js_output, map_output],
executable = ctx.executable._rollup,
arguments = [args],
)
return struct(
js = js_output,
map = map_output,
)
# convert from [{js: js_file1, map: map_file1}, ...] to
# [js_filepath1, map_filepath1, ...]
def _flatten_paths(directory):
result = []
for f in directory:
result.extend([f.js.path, f.map.path])
return result
# ng_package produces package that is npm-ready.
def _ng_package_impl(ctx):
npm_package_directory = ctx.actions.declare_directory(ctx.label.name)
esm_2015_files = collect_es6_sources(ctx)
esm5_sources = depset()
root_dirs = []
for dep in ctx.attr.deps:
if ESM5Info in dep:
# TODO(alexeagle): we could make the module resolution in the rollup plugin
# faster if we kept the files grouped with their root dir. This approach just
# passes in both lists and requires multiple lookups (with expensive exception
# handling) to locate the files again.
transitive_output = dep[ESM5Info].transitive_output
root_dirs.extend(transitive_output.keys())
esm5_sources = depset(transitive=[esm5_sources] + transitive_output.values())
# These accumulators match the directory names where the files live in the
# Angular package format.
esm2015 = []
esm5 = []
bundles = []
for entry_point in [''] + ctx.attr.secondary_entry_points:
es2015_entry_point = "/".join([p for p in [
ctx.bin_dir.path,
ctx.label.package,
ctx.label.name + ".es6",
ctx.label.package,
entry_point,
"index.js",
] if p])
es5_entry_point = "/".join([p for p in [
ctx.label.package,
entry_point,
"index.js",
] if p])
if entry_point:
# TODO jasonaden says there is no particular reason these filenames differ
umd_output_filename = "-".join([ctx.label.package.split("/")[-1]] + entry_point.split("/"))
fesm_output_filename = entry_point.replace("/", "__")
fesm2015_output = ctx.actions.declare_file("fesm2015/%s.js" % fesm_output_filename)
fesm5_output = ctx.actions.declare_file("%s.js" % fesm_output_filename)
umd_output = ctx.actions.declare_file("%s.umd.js" % umd_output_filename)
min_output = ctx.actions.declare_file("%s.umd.min.js" % umd_output_filename)
else:
fesm2015_output = ctx.outputs.fesm2015
fesm5_output = ctx.outputs.fesm5
umd_output = ctx.outputs.umd
min_output = ctx.outputs.umd_min
config = write_rollup_config(ctx, [], root_dirs)
esm2015.append(_rollup(ctx, config, es2015_entry_point, esm_2015_files, fesm2015_output))
esm5.append(_rollup(ctx, config, es5_entry_point, esm5_sources, fesm5_output))
bundles.append(_rollup(ctx, config, es5_entry_point, esm5_sources, umd_output, format = "umd"))
uglify_sourcemap = run_uglify(ctx, umd_output, min_output,
config_name = entry_point.replace("/", "_"))
bundles.append(struct(js = min_output, map = uglify_sourcemap))
metadata_files = depset(transitive = [getattr(dep, "angular").flat_module_metadata
for dep in ctx.attr.deps
if hasattr(dep, "angular")])
# TODO: the args look long, maybe need to spill to a params file:
# https://docs.bazel.build/versions/master/skylark/lib/Args.html#use_param_file
args = ctx.actions.args()
args.add(npm_package_directory.path)
args.add(ctx.label.package)
args.add([ctx.bin_dir.path, ctx.label.package], join_with="/")
args.add(ctx.file.readme_md.path if ctx.file.readme_md else "")
args.add(_flatten_paths(esm2015), join_with=",")
args.add(_flatten_paths(esm5), join_with=",")
args.add(_flatten_paths(bundles), join_with=",")
args.add([s.path for s in ctx.files.srcs], join_with=",")
args.add(ctx.file.stamp_data.path if ctx.file.stamp_data else "")
args.add(ctx.file.license_banner.path if ctx.file.license_banner else "")
other_inputs = (metadata_files.to_list() +
[f.js for f in esm2015 + esm5 + bundles] +
[f.map for f in esm2015 + esm5 + bundles])
if ctx.file.stamp_data:
other_inputs.append(ctx.file.stamp_data)
if ctx.file.readme_md:
other_inputs.append(ctx.file.readme_md)
if ctx.file.license_banner:
other_inputs.append(ctx.file.license_banner)
ctx.actions.run(
progress_message = "Angular Packaging: building npm package for %s" % ctx.label.name,
mnemonic = "AngularPackage",
inputs = esm5_sources.to_list() +
ctx.files.deps +
ctx.files.srcs +
other_inputs,
outputs = [npm_package_directory],
executable = ctx.executable._packager,
arguments = [args],
)
return struct(
files = depset([npm_package_directory])
)
NG_PACKAGE_ATTRS = dict(ROLLUP_ATTRS, **{
"srcs": attr.label_list(allow_files = True),
"deps": attr.label_list(aspects = [
rollup_module_mappings_aspect,
esm5_outputs_aspect,
]),
"readme_md": attr.label(allow_single_file = FileType([".md"])),
"globals": attr.string_dict(default={}),
"secondary_entry_points": attr.string_list(),
"_packager": attr.label(
default=Label("//packages/bazel/src/ng_package:packager"),
executable=True, cfg="host"),
"_rollup": attr.label(
default=Label("@build_bazel_rules_nodejs//internal/rollup"),
executable=True, cfg="host"),
"_rollup_config_tmpl": attr.label(
default=Label("@build_bazel_rules_nodejs//internal/rollup:rollup.config.js"),
allow_single_file=True),
"_uglify": attr.label(
default=Label("@build_bazel_rules_nodejs//internal/rollup:uglify"),
executable=True, cfg="host"),
})
def ng_package_outputs(name, entry_point):
# Angular wants these named after the entry_point,
# eg. for //packages/core it looks like "packages/core/index.js", we want
# core.js produced by this rule.
# Currently we just borrow the entry point for this, if it looks like
# some/path/to/my/package/index.js
# we assume the files should be named "package.*.js"
basename = entry_point.split("/")[-2] if entry_point.find("/") >=0 else name
return {
"fesm5": "%s.js" % basename,
"fesm2015": "fesm2015/%s.js" % basename,
"umd": "%s.umd.js" % basename,
"umd_min": "%s.umd.min.js" % basename,
}
ng_package = rule(
implementation = _ng_package_impl,
attrs = NG_PACKAGE_ATTRS,
outputs = ng_package_outputs,
)

View File

@ -0,0 +1,141 @@
/**
* @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 * as shx from 'shelljs';
function filter(ext: string): (path: string) => boolean {
return f => f.endsWith(ext) && !f.endsWith(`.ngfactory${ext}`) && !f.endsWith(`.ngsummary${ext}`);
}
function main(args: string[]): number {
shx.set('-e');
const
[out, srcDir, binDir, readmeMd, fesms2015Arg, fesms5Arg, bundlesArg, srcsArg, stampData,
licenseFile] = args;
const fesms2015 = fesms2015Arg.split(',').filter(s => !!s);
const fesms5 = fesms5Arg.split(',').filter(s => !!s);
const bundles = bundlesArg.split(',').filter(s => !!s);
const srcs = srcsArg.split(',').filter(s => !!s);
shx.mkdir('-p', out);
let primaryEntryPoint: string|null = null;
const secondaryEntryPoints = new Set<string>();
function replaceVersionPlaceholders(filePath: string) {
if (stampData) {
const version = shx.grep('BUILD_SCM_VERSION', stampData).split(' ')[1].trim();
return shx.sed(/0.0.0-PLACEHOLDER/, version, filePath);
}
return shx.cat(filePath);
}
function writeFesm(file: string, baseDir: string) {
const parts = path.basename(file).split('__');
const entryPointName = parts.join('/').replace(/\..*/, '');
if (primaryEntryPoint === null || primaryEntryPoint === entryPointName) {
primaryEntryPoint = entryPointName;
} else {
secondaryEntryPoints.add(entryPointName);
}
const filename = parts.splice(-1)[0];
const dir = path.join(baseDir, ...parts);
shx.mkdir('-p', dir);
shx.cp(file, dir);
shx.mv(path.join(dir, path.basename(file)), path.join(dir, filename));
}
function moveBundleIndex(f: string) {
let ext: string;
if (f.endsWith('.d.ts'))
ext = '.d.ts';
else if (f.endsWith('.metadata.json'))
ext = '.metadata.json';
else
throw new Error('Bundle index files should be .d.ts or .metadata.json');
const relative = path.relative(binDir, f);
let outputPath: string|undefined = undefined;
for (const secondary of secondaryEntryPoints.values()) {
if (relative.startsWith(secondary)) {
const filename = secondary.split('/').pop();
outputPath = path.join(out, secondary, filename + ext);
}
}
if (!outputPath) {
outputPath = path.join(out, primaryEntryPoint + ext);
}
return outputPath;
}
if (readmeMd) {
shx.cp(readmeMd, path.join(out, 'README.md'));
}
fesms2015.forEach(fesm2015 => writeFesm(fesm2015, path.join(out, 'esm2015')));
fesms5.forEach(fesm5 => writeFesm(fesm5, path.join(out, 'esm5')));
const bundlesDir = path.join(out, 'bundles');
shx.mkdir('-p', bundlesDir);
bundles.forEach(bundle => { shx.cp(bundle, bundlesDir); });
const allsrcs = shx.find('-R', binDir);
allsrcs.filter(filter('.d.ts')).forEach((f: string) => {
const content = fs.readFileSync(f, {encoding: 'utf-8'})
// Strip the named AMD module for compatibility with non-bazel users
.replace(/^\/\/\/ <amd-module name=.*\/>\n/, '');
let outputPath: string;
if (f.endsWith('.bundle_index.d.ts')) {
outputPath = moveBundleIndex(f);
} else {
outputPath = path.join(out, path.relative(binDir, f));
}
shx.mkdir('-p', path.dirname(outputPath));
fs.writeFileSync(outputPath, content);
});
for (const src of srcs) {
replaceVersionPlaceholders(src).to(path.join(out, path.relative(srcDir, src)));
}
allsrcs.filter(filter('.bundle_index.metadata.json')).forEach((f: string) => {
replaceVersionPlaceholders(f).to(moveBundleIndex(f));
});
const licenseBanner = licenseFile ? fs.readFileSync(licenseFile, {encoding: 'utf-8'}) : '';
for (const secondaryEntryPoint of secondaryEntryPoints.values()) {
const baseName = secondaryEntryPoint.split('/').pop();
const dirName = path.join(...secondaryEntryPoint.split('/').slice(0, -1));
fs.writeFileSync(path.join(out, dirName, `${baseName}.metadata.json`), JSON.stringify({
'__symbolic': 'module',
'version': 3,
'metadata': {},
'exports': [{'from': `./${baseName}/${baseName}`}],
'flatModuleIndexRedirect': true
}) + '\n');
fs.writeFileSync(
path.join(out, dirName, `${baseName}.d.ts`),
// Format carefully to match existing build.sh output
licenseBanner + ' ' +
`
export * from './${baseName}/${baseName}'
`);
}
return 0;
}
if (require.main === module) {
process.exitCode = main(process.argv.slice(2));
}

View File

@ -0,0 +1,5 @@
{
"compilerOptions": {
"lib": ["es2015"]
}
}

View File

@ -0,0 +1,36 @@
load("//tools:defaults.bzl", "ts_library")
load("@build_bazel_rules_nodejs//:defs.bzl", "jasmine_node_test")
exports_files(["package.json"])
# The tests in this package must run in separate targets, since they change
# working directory and therefore have mutable global state that causes test
# isolation failures.
ts_library(
name = "core_spec_lib",
testonly = True,
srcs = ["core_package.spec.ts"],
deps = ["//packages:types"],
)
jasmine_node_test(
name = "core_package",
srcs = [":core_spec_lib"],
data = [
"//packages/core:npm_package",
],
)
ts_library(
name = "common_spec_lib",
testonly = True,
srcs = ["common_package.spec.ts"],
deps = ["//packages:types"],
)
jasmine_node_test(
name = "common_package",
srcs = [":common_spec_lib"],
data = ["//packages/common:npm_package"],
)

View File

@ -0,0 +1,50 @@
/**
* @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 shx from 'shelljs';
shx.cd(path.join(process.env['TEST_SRCDIR'], 'angular', 'packages', 'common', 'npm_package'));
describe('ng_package', () => {
it('should have right bundle files', () => {
expect(shx.ls('-R', 'bundles').stdout.split('\n').filter(n => !!n).sort()).toEqual([
'common-http-testing.umd.js',
'common-http-testing.umd.js.map',
'common-http-testing.umd.min.js',
'common-http-testing.umd.min.js.map',
'common-http.umd.js',
'common-http.umd.js.map',
'common-http.umd.min.js',
'common-http.umd.min.js.map',
'common-testing.umd.js',
'common-testing.umd.js.map',
'common-testing.umd.min.js',
'common-testing.umd.min.js.map',
'common.umd.js',
'common.umd.js.map',
'common.umd.min.js',
'common.umd.min.js.map',
]);
});
it('should have right fesm files', () => {
const expected = [
'common.js',
'common.js.map',
'http',
'http.js',
'http.js.map',
'http/testing.js',
'http/testing.js.map',
'testing.js',
'testing.js.map',
];
expect(shx.ls('-R', 'esm5').stdout.split('\n').filter(n => !!n).sort()).toEqual(expected);
expect(shx.ls('-R', 'esm2015').stdout.split('\n').filter(n => !!n).sort()).toEqual(expected);
});
});

View File

@ -0,0 +1,227 @@
/**
* @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 shx from 'shelljs';
const corePackagePath =
path.join(process.env['TEST_SRCDIR'], 'angular', 'packages', 'core', 'npm_package');
shx.cd(corePackagePath);
/**
* Utility functions that allows me to create fs paths
* p`${foo}/some/${{bar}}/path` rather than path.join(foo, 'some',
*/
function p(templateStringArray: TemplateStringsArray) {
const segments = [];
for (const entry of templateStringArray) {
segments.push(...entry.split('/').filter(s => s !== ''));
}
return path.join(...segments);
}
describe('ng_package', () => {
describe('misc root files', () => {
describe('README.md', () => {
it('should have a README.md file with basic info', () => {
expect(shx.cat('README.md')).toContain(`Angular`);
expect(shx.cat('README.md')).toContain(`https://github.com/angular/angular`);
});
});
});
describe('primary entry-point', () => {
describe('package.json', () => {
const packageJson = 'package.json';
it('should have a package.json file',
() => { expect(shx.grep('"name":', packageJson)).toContain(`@angular/core`); });
it('should contain correct version number with the PLACEHOLDER string replaced', () => {
expect(shx.grep('"version":', packageJson)).toMatch(/\d+\.\d+\.\d+(?!-PLACEHOLDER)/);
});
it('should contain module resolution mappings', () => {
const packageJson = 'package.json';
expect(shx.grep('"main":', packageJson)).toContain(`./bundles/core.umd.js`);
expect(shx.grep('"module":', packageJson)).toContain(`./esm5/core.js`);
expect(shx.grep('"es2015":', packageJson)).toContain(`./esm2015/core.js`);
expect(shx.grep('"typings":', packageJson)).toContain(`./core.d.ts`);
});
});
describe('typescript support', () => {
it('should have an index.d.ts file',
() => { expect(shx.cat('core.d.ts')).toContain(`export *`); });
it('should not have amd module names',
() => { expect(shx.cat('public_api.d.ts')).not.toContain('<amd-module name'); });
});
describe('closure', () => {
it('should contain externs', () => {
expect(shx.cat('src/testability/testability.externs.js')).toContain('/** @externs */');
});
});
describe('angular metadata', () => {
it('should have metadata.json files',
() => { expect(shx.cat('core.metadata.json')).toContain(`"__symbolic":"module"`); });
});
describe('fesm15', () => {
it('should have a fesm15 file in the /esm2015 directory',
() => { expect(shx.cat('esm2015/core.js')).toContain(`export {`); });
it('should have a source map', () => {
expect(shx.cat('esm2015/core.js.map'))
.toContain(`{"version":3,"file":"core.js","sources":`);
});
it('should have the version info in the header', () => {
expect(shx.cat('esm2015/core.js'))
.toMatch(/@license Angular v\d+\.\d+\.\d+(?!-PLACEHOLDER)/);
});
});
describe('fesm5', () => {
it('should have a fesm5 file in the /esm5 directory',
() => { expect(shx.cat('esm5/core.js')).toContain(`export {`); });
it('should have a source map', () => {
expect(shx.cat('esm5/core.js.map')).toContain(`{"version":3,"file":"core.js","sources":`);
});
it('should not be processed by tsickle', () => {
expect(shx.cat('esm5/core.js')).not.toContain('@fileoverview added by tsickle');
});
});
describe('umd', () => {
it('should have a umd file in the /bundles directory',
() => { expect(shx.ls('bundles/core.umd.js').length).toBe(1, 'File not found'); });
it('should have a source map next to the umd file',
() => { expect(shx.ls('bundles/core.umd.js.map').length).toBe(1, 'File not found'); });
it('should have a minified umd file in the /bundles directory',
() => { expect(shx.ls('bundles/core.umd.min.js').length).toBe(1, 'File not found'); });
it('should have a source map next to the minified umd file',
() => { expect(shx.ls('bundles/core.umd.min.js.map').length).toBe(1, 'File not found'); });
});
});
describe('secondary entry-point', () => {
describe('package.json', () => {
const packageJson = p `testing/package.json`;
it('should have a package.json file',
() => { expect(shx.grep('"name":', packageJson)).toContain(`@angular/core/testing`); });
it('should have its module resolution mappings defined in the nested package.json', () => {
const packageJson = p `testing/package.json`;
expect(shx.grep('"main":', packageJson)).toContain(`../bundles/core-testing.umd.js`);
expect(shx.grep('"module":', packageJson)).toContain(`../esm5/testing.js`);
expect(shx.grep('"es2015":', packageJson)).toContain(`../esm2015/testing.js`);
expect(shx.grep('"typings":', packageJson)).toContain(`./testing.d.ts`);
});
});
describe('typings', () => {
const typingsFile = p `testing/testing.d.ts`;
it('should have a typings file',
() => { expect(shx.cat(typingsFile)).toContain('export * from \'./public_api\';'); });
});
describe('typescript support', () => {
// TODO(i): why in the parent dir?
it('should have an \'redirect\' d.ts file in the parent dir',
() => { expect(shx.cat('testing.d.ts')).toContain(`export *`); });
it('should have a \'actual\' d.ts file in the parent dir', () => {
expect(shx.cat('testing/testing.d.ts')).toContain(`export * from './public_api';`);
});
});
describe('angular metadata file', () => {
it('should have a \'redirect\' metadata.json file next to the d.ts file', () => {
expect(shx.cat('testing.metadata.json'))
.toContain(`"exports":[{"from":"./testing/testing"}],"flatModuleIndexRedirect":true`);
});
it('should have an \'actual\' metadata.json file', () => {
expect(shx.cat('testing/testing.metadata.json'))
.toContain(`"metadata":{"async":{"__symbolic":"function"},`);
});
});
describe('fesm15', () => {
it('should have a fesm15 file in the /esm2015 directory',
() => { expect(shx.cat('esm2015/testing.js')).toContain(`export {`); });
it('should have a source map', () => {
expect(shx.cat('esm2015/testing.js.map'))
.toContain(`{"version":3,"file":"testing.js","sources":`);
});
it('should have the version info in the header', () => {
expect(shx.cat('esm2015/testing.js'))
.toMatch(/@license Angular v\d+\.\d+\.\d+(?!-PLACEHOLDER)/);
});
});
describe('fesm5', () => {
it('should have a fesm5 file in the /esm5 directory',
() => { expect(shx.cat('esm5/testing.js')).toContain(`export {`); });
it('should have a source map', () => {
expect(shx.cat('esm5/testing.js.map'))
.toContain(`{"version":3,"file":"testing.js","sources":`);
});
});
describe('umd', () => {
it('should have a umd file in the /bundles directory',
() => { expect(shx.ls('bundles/core-testing.umd.js').length).toBe(1, 'File not found'); });
it('should have a source map next to the umd file', () => {
expect(shx.ls('bundles/core-testing.umd.js.map').length).toBe(1, 'File not found');
});
it('should have a minified umd file in the /bundles directory', () => {
expect(shx.ls('bundles/core-testing.umd.min.js').length).toBe(1, 'File not found');
});
it('should have a source map next to the minified umd file', () => {
expect(shx.ls('bundles/core-testing.umd.min.js.map').length).toBe(1, 'File not found');
});
});
});
});

View File

@ -1,6 +1,6 @@
package(default_visibility = ["//visibility:public"])
load("//tools:defaults.bzl", "ng_module")
load("//tools:defaults.bzl", "ng_module", "ng_package")
ng_module(
name = "common",
@ -16,3 +16,19 @@ ng_module(
"@rxjs",
],
)
ng_package(
name = "npm_package",
entry_point = "packages/common/index.js",
secondary_entry_points = [
"testing",
"http",
"http/testing",
],
deps = [
"//packages/common",
"//packages/common/http",
"//packages/common/http/testing",
"//packages/common/testing",
],
)

View File

@ -3,6 +3,7 @@ load("//packages/bazel:index.bzl", "ng_module")
ng_module(
name = "test_module",
srcs = glob(["*.ts"]),
entry_point = "index.ts",
module_name = "some_npm_module",
deps = ["//packages/core"],
)

View File

@ -1,6 +1,6 @@
package(default_visibility = ["//visibility:public"])
load("//tools:defaults.bzl", "ng_module")
load("//tools:defaults.bzl", "ng_module", "ng_package")
ng_module(
name = "core",
@ -16,3 +16,14 @@ ng_module(
"@rxjs",
],
)
ng_package(
name = "npm_package",
srcs = glob(["**/*.externs.js"] + ["**/package.json"]) + ["//packages/core/testing:npm_package_assets"],
entry_point = "packages/core/index.js",
secondary_entry_points = ["testing"],
deps = [
":core",
"//packages/core/testing",
],
)

View File

@ -4,10 +4,17 @@ load("//tools:defaults.bzl", "ng_module")
ng_module(
name = "testing",
srcs = glob(["**/*.ts"]),
srcs = glob(
["**/*.ts"],
),
module_name = "@angular/core/testing",
deps = [
"//packages:types",
"//packages/core",
],
)
filegroup(
name = "npm_package_assets",
srcs = ["package.json"],
)