From a0108868847e9951254f49baf84d8ff1a928c7fb Mon Sep 17 00:00:00 2001 From: Keen Yee Liau Date: Wed, 28 Mar 2018 13:09:49 -0700 Subject: [PATCH] build(bazel): Emit metadata.json under Blaze 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. --- packages/bazel/src/ng_module.bzl | 25 +++++++++++++------- packages/bazel/src/ngc-wrapped/index.ts | 31 +++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 8 deletions(-) diff --git a/packages/bazel/src/ng_module.bzl b/packages/bazel/src/ng_module.bzl index 65596e2849741..1f7d2a4eb2c53 100644 --- a/packages/bazel/src/ng_module.bzl +++ b/packages/bazel/src/ng_module.bzl @@ -22,6 +22,11 @@ def _basename_of(ctx, file): ext_len = len(".html") 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): """Provide a default for the flat_module_out_file attribute. @@ -50,8 +55,7 @@ def _should_produce_flat_module_outs(ctx): Returns: 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 # in the library. Most of these will be produced as empty files but it is @@ -81,9 +85,11 @@ def _expected_outs(ctx): ".js", ] summaries = [".ngsummary.json"] + metadata = [".metadata.json"] else: devmode_js = [".js"] summaries = [] + metadata = [] elif short_path.endswith(".css"): basename = short_path[len(package_prefix):-len(".css")] devmode_js = [ @@ -91,7 +97,7 @@ def _expected_outs(ctx): ".css.ngstyle.js", ] summaries = [] - + metadata = [] else: 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")] 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] - closure_js_files += [ctx.new_file(ctx.bin_dir, basename + ext) for ext in closure_js] - declaration_files += [ctx.new_file(ctx.bin_dir, basename + ext) for ext in declarations] - summary_files += [ctx.new_file(ctx.bin_dir, basename + ext) for ext in summaries] + devmode_js_files += [ctx.actions.declare_file(basename + ext) for ext in devmode_js] + closure_js_files += [ctx.actions.declare_file(basename + ext) for ext in closure_js] + declaration_files += [ctx.actions.declare_file(basename + ext) for ext in declarations] + 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 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) providers["angular"] = { - "summaries": outs.summaries + "summaries": outs.summaries, + "metadata": outs.metadata } providers["ngc_messages"] = outs.i18n_messages diff --git a/packages/bazel/src/ngc-wrapped/index.ts b/packages/bazel/src/ngc-wrapped/index.ts index 928cfe8e592d2..193ce58bc28fc 100644 --- a/packages/bazel/src/ngc-wrapped/index.ts +++ b/packages/bazel/src/ngc-wrapped/index.ts @@ -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) { // Note: when tsickleExternsPath is provided, we always write a file as a // 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}; } +/** + * 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 { return !NGC_GEN_FILES.test(sf.fileName) && (bazelOpts.compilationTargetSrc.indexOf(sf.fileName) !== -1);