build(bazel): Emit metadata.json under Blaze (#23049)

This commit modifies the compilation to emit metadata.json files when
compiled under Blaze. The default behavior of ngc is to emit metadata
only when the --flatModuleOutFile flag is specified, but this mode
is not used in Blaze.
Emitting metadata for individual Angular components is needed for
Angular Language Service to work with projects compiled with Blaze.

PR Close #23049
This commit is contained in:
Keen Yee Liau 2018-03-28 13:09:49 -07:00 committed by Alex Rickabaugh
parent 6880766fb7
commit 0c4e3718f5
2 changed files with 48 additions and 8 deletions

View File

@ -22,6 +22,11 @@ def _basename_of(ctx, file):
ext_len = len(".html") ext_len = len(".html")
return file.short_path[len(ctx.label.package) + 1:-ext_len] return file.short_path[len(ctx.label.package) + 1:-ext_len]
# Return true if run with bazel (the open-sourced version of blaze), false if
# run with blaze.
def _is_bazel():
return not hasattr(native, "genmpm")
def _flat_module_out_file(ctx): def _flat_module_out_file(ctx):
"""Provide a default for the flat_module_out_file attribute. """Provide a default for the flat_module_out_file attribute.
@ -50,8 +55,7 @@ def _should_produce_flat_module_outs(ctx):
Returns: Returns:
true iff we should run the bundle_index_host to produce flat module metadata and bundle index true iff we should run the bundle_index_host to produce flat module metadata and bundle index
""" """
is_bazel = not hasattr(native, "genmpm") return _is_bazel() and ctx.attr.module_name
return is_bazel and ctx.attr.module_name
# Calculate the expected output of the template compiler for every source in # Calculate the expected output of the template compiler for every source in
# in the library. Most of these will be produced as empty files but it is # in the library. Most of these will be produced as empty files but it is
@ -81,9 +85,11 @@ def _expected_outs(ctx):
".js", ".js",
] ]
summaries = [".ngsummary.json"] summaries = [".ngsummary.json"]
metadata = [".metadata.json"]
else: else:
devmode_js = [".js"] devmode_js = [".js"]
summaries = [] summaries = []
metadata = []
elif short_path.endswith(".css"): elif short_path.endswith(".css"):
basename = short_path[len(package_prefix):-len(".css")] basename = short_path[len(package_prefix):-len(".css")]
devmode_js = [ devmode_js = [
@ -91,7 +97,7 @@ def _expected_outs(ctx):
".css.ngstyle.js", ".css.ngstyle.js",
] ]
summaries = [] summaries = []
metadata = []
else: else:
continue continue
@ -99,10 +105,12 @@ def _expected_outs(ctx):
closure_js = [f.replace(".js", ".closure.js") for f in devmode_js if not filter_summaries or not f.endswith(".ngsummary.js")] closure_js = [f.replace(".js", ".closure.js") for f in devmode_js if not filter_summaries or not f.endswith(".ngsummary.js")]
declarations = [f.replace(".js", ".d.ts") for f in devmode_js] declarations = [f.replace(".js", ".d.ts") for f in devmode_js]
devmode_js_files += [ctx.new_file(ctx.bin_dir, basename + ext) for ext in devmode_js] devmode_js_files += [ctx.actions.declare_file(basename + ext) for ext in devmode_js]
closure_js_files += [ctx.new_file(ctx.bin_dir, basename + ext) for ext in closure_js] closure_js_files += [ctx.actions.declare_file(basename + ext) for ext in closure_js]
declaration_files += [ctx.new_file(ctx.bin_dir, basename + ext) for ext in declarations] declaration_files += [ctx.actions.declare_file(basename + ext) for ext in declarations]
summary_files += [ctx.new_file(ctx.bin_dir, basename + ext) for ext in summaries] summary_files += [ctx.actions.declare_file(basename + ext) for ext in summaries]
if not _is_bazel():
metadata_files += [ctx.actions.declare_file(basename + ext) for ext in metadata]
# We do this just when producing a flat module index for a publishable ng_module # We do this just when producing a flat module index for a publishable ng_module
if _should_produce_flat_module_outs(ctx): if _should_produce_flat_module_outs(ctx):
@ -330,7 +338,8 @@ def ng_module_impl(ctx, ts_compile_actions, ivy = False):
outs = _expected_outs(ctx) outs = _expected_outs(ctx)
providers["angular"] = { providers["angular"] = {
"summaries": outs.summaries "summaries": outs.summaries,
"metadata": outs.metadata
} }
providers["ngc_messages"] = outs.i18n_messages providers["ngc_messages"] = outs.i18n_messages

View File

@ -268,6 +268,12 @@ export function compile({allowNonHermeticReads, allDepsCompiledWithBazel = true,
} }
} }
if (!bazelOpts.nodeModulesPrefix) {
// If there is no node modules, then metadata.json should be emitted since
// there is no other way to obtain the information
generateMetadataJson(program.getTsProgram(), files, compilerOpts.rootDirs, bazelBin, tsHost);
}
if (bazelOpts.tsickleExternsPath) { if (bazelOpts.tsickleExternsPath) {
// Note: when tsickleExternsPath is provided, we always write a file as a // Note: when tsickleExternsPath is provided, we always write a file as a
// marker that compilation succeeded, even if it's empty (just containing an // marker that compilation succeeded, even if it's empty (just containing an
@ -282,6 +288,31 @@ export function compile({allowNonHermeticReads, allDepsCompiledWithBazel = true,
return {program, diagnostics}; return {program, diagnostics};
} }
/**
* Generate metadata.json for the specified `files`. By default, metadata.json
* is only generated by the compiler if --flatModuleOutFile is specified. But
* if compiled under blaze, we want the metadata to be generated for each
* Angular component.
*/
function generateMetadataJson(
program: ts.Program, files: string[], rootDirs: string[], bazelBin: string,
tsHost: ts.CompilerHost) {
const collector = new ng.MetadataCollector();
for (const file of files) {
const sourceFile = program.getSourceFile(file);
if (sourceFile) {
const metadata = collector.getMetadata(sourceFile);
if (metadata) {
const relative = relativeToRootDirs(file, rootDirs);
const shortPath = relative.replace(EXT, '.metadata.json');
const outFile = resolveNormalizedPath(bazelBin, shortPath);
const data = JSON.stringify(metadata);
tsHost.writeFile(outFile, data, false, undefined, []);
}
}
}
}
function isCompilationTarget(bazelOpts: BazelOptions, sf: ts.SourceFile): boolean { function isCompilationTarget(bazelOpts: BazelOptions, sf: ts.SourceFile): boolean {
return !NGC_GEN_FILES.test(sf.fileName) && return !NGC_GEN_FILES.test(sf.fileName) &&
(bazelOpts.compilationTargetSrc.indexOf(sf.fileName) !== -1); (bazelOpts.compilationTargetSrc.indexOf(sf.fileName) !== -1);