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:

committed by
Victor Berchet

parent
1dcbc12fd3
commit
b43b164a61
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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",
|
||||
|
18
packages/bazel/src/ng_package/BUILD.bazel
Normal file
18
packages/bazel/src/ng_package/BUILD.bazel
Normal 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",
|
||||
)
|
229
packages/bazel/src/ng_package/ng_package.bzl
Normal file
229
packages/bazel/src/ng_package/ng_package.bzl
Normal 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,
|
||||
)
|
141
packages/bazel/src/ng_package/packager.ts
Normal file
141
packages/bazel/src/ng_package/packager.ts
Normal 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));
|
||||
}
|
5
packages/bazel/src/ng_package/tsconfig.json
Normal file
5
packages/bazel/src/ng_package/tsconfig.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"lib": ["es2015"]
|
||||
}
|
||||
}
|
36
packages/bazel/test/ng_package/BUILD.bazel
Normal file
36
packages/bazel/test/ng_package/BUILD.bazel
Normal 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"],
|
||||
)
|
50
packages/bazel/test/ng_package/common_package.spec.ts
Normal file
50
packages/bazel/test/ng_package/common_package.spec.ts
Normal 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);
|
||||
});
|
||||
});
|
227
packages/bazel/test/ng_package/core_package.spec.ts
Normal file
227
packages/bazel/test/ng_package/core_package.spec.ts
Normal 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');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -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",
|
||||
],
|
||||
)
|
||||
|
@ -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"],
|
||||
)
|
||||
|
@ -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",
|
||||
],
|
||||
)
|
||||
|
@ -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"],
|
||||
)
|
||||
|
Reference in New Issue
Block a user