Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

build(bazel): add data attr to ng_package #22919

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 9 additions & 0 deletions packages/bazel/src/ng_package/ng_package.bzl
Expand Up @@ -172,6 +172,7 @@ def _ng_package_impl(ctx):

packager_inputs = (
ctx.files.srcs +
ctx.files.data +
esm5_sources.to_list() +
depset(transitive = [d.typescript.transitive_declarations
for d in ctx.attr.deps
Expand All @@ -186,6 +187,7 @@ def _ng_package_impl(ctx):
packager_args.add(npm_package_directory.path)
packager_args.add(ctx.label.package)
packager_args.add([ctx.bin_dir.path, ctx.label.package], join_with="/")
packager_args.add([ctx.genfiles_dir.path, ctx.label.package], join_with="/")

# Marshal the metadata into a JSON string so we can parse the data structure
# in the TypeScript program easily.
Expand Down Expand Up @@ -213,6 +215,9 @@ def _ng_package_impl(ctx):
packager_args.add(_flatten_paths(bundles), join_with=",")
packager_args.add([s.path for s in ctx.files.srcs], join_with=",")

# TODO: figure out a better way to gather runfiles providers from the transitive closure.
packager_args.add([d.path for d in ctx.files.data], join_with=",")

if ctx.file.license_banner:
packager_inputs.append(ctx.file.license_banner)
packager_args.add(ctx.file.license_banner.path)
Expand Down Expand Up @@ -246,6 +251,10 @@ NG_PACKAGE_ATTRS = dict(NPM_PACKAGE_ATTRS, **dict(ROLLUP_ATTRS, **{
esm5_outputs_aspect,
sources_aspect,
]),
"data": attr.label_list(
doc = "Additional, non-Angular files to be added to the package, e.g. global CSS assets.",
allow_files = True,
),
"include_devmode_srcs": attr.bool(default = False),
"readme_md": attr.label(allow_single_file = FileType([".md"])),
"globals": attr.string_dict(default={}),
Expand Down
94 changes: 60 additions & 34 deletions packages/bazel/src/ng_package/packager.ts
Expand Up @@ -14,6 +14,9 @@ function main(args: string[]): number {
// Exit immediately when encountering an error.
shx.set('-e');

// Keep track of whether an error has occured so that we can return an appropriate exit code.
let errorHasOccured = false;

// This utility expects all of its arguments to be specified in a params file generated by
// bazel (see https://docs.bazel.build/versions/master/skylark/lib/Args.html#use_param_file).
const paramFilePath = args[0];
Expand All @@ -35,6 +38,9 @@ function main(args: string[]): number {
// This is the intended output location for package artifacts.
binDir,

// The bazel-genfiles dir joined with the srcDir (e.g. 'bazel-bin/package.common').
genfilesDir,

// JSON data mapping each entry point to the generated bundle index and
// flat module metadata, for example
// {"@angular/core": {
Expand Down Expand Up @@ -67,6 +73,9 @@ function main(args: string[]): number {
// List of all files in the ng_package rule's srcs.
srcsArg,

// List of all files in the ng_package rule's data.
dataArg,

// Path to the package's LICENSE.
licenseFile,
] = params;
Expand All @@ -77,37 +86,46 @@ function main(args: string[]): number {
const esm5 = esm5Arg.split(',').filter(s => !!s);
const bundles = bundlesArg.split(',').filter(s => !!s);
const srcs = srcsArg.split(',').filter(s => !!s);
const dataFiles: string[] = dataArg.split(',').filter(s => !!s);
const modulesManifest = JSON.parse(modulesManifestArg);

if (readmeMd) {
copyFile(readmeMd, out);
}

/**
* Relativize the path where the file is written.
* @param f a path relative to the srcDir, typically from a file in the srcs[]
* @param c content of the file
* Writes a file into the package based on its input path, relativizing to the package path.
* @param inputPath Path to the file in the input tree.
* @param fileContent Content of the file.
*/
function writeSrcFile(f: string, c: string) {
mkDirWriteFile(path.join(out, path.relative(srcDir, f)), c);
}
function writeFileFromInputPath(inputPath: string, fileContent: string) {
// We want the relative path from the given file to its ancestor "root" directory.
// This root depends on whether the file lives in the source tree (srcDir) as a basic file
// input to ng_package, the bin output tree (binDir) as the output of another rule, or
// the genfiles output tree (genfilesDir) as the output of a genrule.
let rootDir: string;
if (inputPath.includes(binDir)) {
rootDir = binDir;
} else if (inputPath.includes(genfilesDir)) {
rootDir = genfilesDir;
} else {
rootDir = srcDir;
}

/**
* Relativize the path where the file is written.
* @param f a path relative to the binDir, typically from a file in the deps[]
* @param c content of the file
*/
function writeBinFile(f: string, c: string) {
const outputPath = path.join(out, path.relative(binDir, f));
mkDirWriteFile(outputPath, c);
return outputPath;
const outputPath = path.join(out, path.relative(rootDir, inputPath));

// Always ensure that the target directory exists.
shx.mkdir('-p', path.dirname(outputPath));
fs.writeFileSync(outputPath, fileContent, 'utf-8');
}

/**
* Copy the file, relativizing the path.
* @param f a path relative to the binDir, typically from a file in the deps[]
* Copies a file into the package based on its input path, relativizing to the package path.
* @param inputPath a path relative to the binDir, typically from a file in the deps[]
*/
function copyBinFile(f: string) { writeBinFile(f, fs.readFileSync(f, 'utf-8')); }
function copyFileFromInputPath(inputPath: string) {
writeFileFromInputPath(inputPath, fs.readFileSync(inputPath, 'utf-8'));
}

/**
* Relativize the path where a file is written.
Expand Down Expand Up @@ -135,9 +153,13 @@ function main(args: string[]): number {
const content = fs.readFileSync(f, 'utf-8')
// Strip the named AMD module for compatibility with non-bazel users
.replace(/^\/\/\/ <amd-module name=.*\/>\n/, '');
const outputPath = writeBinFile(f, content);
writeFileFromInputPath(f, content);
});

// Copy all `data` files into the package. These are files that aren't built by the ng_package
// rule, but instead are just straight copied into the package, e.g. global CSS assets.
dataFiles.forEach(f => copyFileFromInputPath(f));

// Iterate through the entry point modules
// We do this first because we also record new paths for the esm5 and esm2015 copies
// of the index JS file, which we need to amend the package.json.
Expand All @@ -149,18 +171,25 @@ function main(args: string[]): number {
moduleFiles['esm5_index'] = path.join(binDir, 'esm5', relative);
moduleFiles['esm2015_index'] = path.join(binDir, 'esm2015', relative);

writeBinFile(moduleFiles['esm5_index'], indexContent);
writeBinFile(moduleFiles['esm2015_index'], indexContent);
writeFileFromInputPath(moduleFiles['esm5_index'], indexContent);
writeFileFromInputPath(moduleFiles['esm2015_index'], indexContent);

copyBinFile(moduleFiles['typings']);
copyBinFile(moduleFiles['metadata']);
copyFileFromInputPath(moduleFiles['typings']);
copyFileFromInputPath(moduleFiles['metadata']);
});

// Root package name (e.g. '@angular/common'), captures as we iterate through sources below.
let rootPackageName = '';
const packagesWithExistingPackageJson = new Set<string>();

for (const src of srcs) {
if (src.includes(binDir) || src.includes(genfilesDir)) {
errorHasOccured = true;
console.error(
'The "srcs" for ng_package should not include output of other rules. Found:\n' +
` ${src}`);
}

let content = fs.readFileSync(src, 'utf-8');
// Modify package.json files as necessary for publishing
if (path.basename(src) === 'package.json') {
Expand All @@ -177,7 +206,7 @@ function main(args: string[]): number {
rootPackageName = packageJson['name'];
}
}
writeSrcFile(src, content);
writeFileFromInputPath(src, content);
}

const licenseBanner = licenseFile ? fs.readFileSync(licenseFile, 'utf-8') : '';
Expand All @@ -196,7 +225,7 @@ function main(args: string[]): number {
}
});

return 0;
return errorHasOccured ? 1 : 0;

/**
* Convert a binDir-relative path to srcDir-relative
Expand All @@ -216,11 +245,6 @@ function main(args: string[]): number {
!f.endsWith(`.ngsummary${ext}`);
}

function mkDirWriteFile(p: string, content: string) {
shx.mkdir('-p', path.dirname(p));
fs.writeFileSync(p, content, 'utf-8');
}

function copyFile(file: string, baseDir: string, relative = '.') {
const dir = path.join(baseDir, relative);
shx.mkdir('-p', dir);
Expand All @@ -240,6 +264,7 @@ function main(args: string[]): number {
* Inserts or edits properties into the package.json file(s) in the package so that
* they point to all the right generated artifacts.
*
* @param packageJson The path to the package.json file.
* @param parsedPackage Parsed package.json content
*/
function amendPackageJson(packageJson: string, parsedPackage: {[key: string]: string}) {
Expand Down Expand Up @@ -298,13 +323,13 @@ function main(args: string[]): number {
/** Creates metadata re-export file for a secondary entry-point. */
function createMetadataReexportFile(entryPointName: string, metadataFile: string) {
const inputPath = path.join(srcDir, `${entryPointName}.metadata.json`);
writeSrcFile(inputPath, JSON.stringify({
writeFileFromInputPath(inputPath, JSON.stringify({
'__symbolic': 'module',
'version': 3,
'metadata': {},
'exports':
[{'from': `${srcDirRelative(inputPath, metadataFile.replace(/.metadata.json$/, ''))}`}],
'flatModuleIndexRedirect': true
'flatModuleIndexRedirect': true,
}) + '\n');
}

Expand All @@ -317,18 +342,19 @@ function main(args: string[]): number {
const content = `${license}
export * from '${srcDirRelative(inputPath, typingsFile.replace(/\.d\.tsx?$/, ''))}';
`;
writeSrcFile(inputPath, content);
writeFileFromInputPath(inputPath, content);
}

/**
* Creates a package.json for a secondary entry-point.
* @param dir The directory under which the package.json should be written.
* @param entryPointPackageName The full package name for the entry point,
* e.g. '@angular/common/http'.
*/
function createEntryPointPackageJson(dir: string, entryPointPackageName: string) {
const pkgJson = path.join(srcDir, dir, 'package.json');
const content = amendPackageJson(pkgJson, {name: entryPointPackageName});
writeSrcFile(pkgJson, content);
writeFileFromInputPath(pkgJson, content);
}
}

Expand Down
22 changes: 22 additions & 0 deletions packages/bazel/test/ng_package/example/BUILD.bazel
Expand Up @@ -15,9 +15,31 @@ ng_package(
"package.json",
"some-file.txt",
],
data = [
":arbitrary_bin_file",
":arbitrary_genfiles_file",
":extra-styles.css",
],
entry_point = "packages/bazel/test/ng_package/example/index.js",
deps = [
":example",
"//packages/bazel/test/ng_package/example/secondary",
],
)

# Use a genrule to create a file in bazel-genfiles to ensure that the genfiles output of
# a rule can be passed through to the `data` of ng_package.
genrule(
name = "arbitrary_genfiles_file",
outs = ["arbitrary_genfiles.txt"],
cmd = "echo Hello > $@",
)

# Use a genrule to create a file in bazel-bin to ensure that the bin output of
# a rule can be passed through to the `data` of ng_package.
genrule(
name = "arbitrary_bin_file",
outs = ["arbitrary_bin.txt"],
cmd = "echo World > $@",
output_to_bindir = True,
)
3 changes: 3 additions & 0 deletions packages/bazel/test/ng_package/example/extra-styles.css
@@ -0,0 +1,3 @@
.special {
color: goldenrod;
}
20 changes: 20 additions & 0 deletions packages/bazel/test/ng_package/example_package.golden
@@ -1,3 +1,5 @@
arbitrary_bin.txt
arbitrary_genfiles.txt
bundles
bundles/example-secondary.umd.js
bundles/example-secondary.umd.js.map
Expand Down Expand Up @@ -43,6 +45,7 @@ esm5
esm5/secondary/secondarymodule.ngsummary.js
example_public_index.d.ts
example_public_index.metadata.json
extra-styles.css
fesm2015
fesm2015/example.js
fesm2015/example.js.map
Expand All @@ -65,6 +68,16 @@ secondary
secondary.d.ts
secondary.metadata.json
some-file.txt
--- arbitrary_bin.txt ---

World


--- arbitrary_genfiles.txt ---

Hello


--- bundles/example-secondary.umd.js ---

(function (global, factory) {
Expand Down Expand Up @@ -661,6 +674,13 @@ export * from './index';

{"__symbolic":"module","version":4,"metadata":{"MyModule":{"__symbolic":"class","decorators":[{"__symbolic":"call","expression":{"__symbolic":"reference","module":"@angular/core","name":"NgModule","line":11,"character":1},"arguments":[{}]}],"members":{}}},"origins":{"MyModule":"./mymodule"},"importAs":"example"}

--- extra-styles.css ---

.special {
color: goldenrod;
}


--- fesm2015/example.js ---

import { NgModule } from '@angular/core';
Expand Down