diff --git a/.circleci/config.yml b/.circleci/config.yml index c49a2cc944..4aabb53b68 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -85,7 +85,7 @@ jobs: # This avoids waiting for the slowest build target to finish before running the first test # See https://github.com/bazelbuild/bazel/issues/4257 # NOTE: Angular developers should typically just bazel build //packages/... or bazel test //packages/... - - run: bazel query --output=label //... | xargs bazel test + - run: bazel query --output=label //... | xargs bazel test --build_tag_filters=-ivy-only --test_tag_filters=-manual,-ivy-only # CircleCI will allow us to go back and view/download these artifacts from past builds. # Also we can use a service like https://buildsize.org/ to automatically track binary size of these artifacts. diff --git a/packages/bazel/src/ng_module.bzl b/packages/bazel/src/ng_module.bzl index afeeccc845..0daa736273 100644 --- a/packages/bazel/src/ng_module.bzl +++ b/packages/bazel/src/ng_module.bzl @@ -14,6 +14,90 @@ load(":rules_typescript.bzl", "ts_providers_dict_to_struct", ) +def _compile_strategy(ctx): + """Detect which strategy should be used to implement ng_module. + + Depending on the value of the 'compile' define flag or the '_global_mode' attribute, ng_module + can be implemented in various ways. This function reads the configuration passed by the user and + determines which mode is active. + + Args: + ctx: skylark rule execution context + + Returns: + one of 'legacy', 'local', 'jit', or 'global' depending on the configuration in ctx + """ + + strategy = 'legacy' + if 'compile' in ctx.var: + strategy = ctx.var['compile'] + + if strategy not in ['legacy', 'local', 'jit']: + fail("Unknown --define=compile value '%s'" % strategy) + + if strategy == 'legacy' and hasattr(ctx.attr, '_global_mode') and ctx.attr._global_mode: + strategy = 'global' + + return strategy + +def _compiler_name(ctx): + """Selects a user-visible name depending on the current compilation strategy. + + Args: + ctx: skylark rule execution context + + Returns: + the name of the current compiler to be displayed in build output + """ + + strategy = _compile_strategy(ctx) + if strategy == 'legacy': + return 'ngc' + elif strategy == 'global': + return 'ngc.ivy' + elif strategy == 'local': + return 'ngtsc' + elif strategy == 'jit': + return 'tsc' + else: + fail('unreachable') + +def _enable_ivy_value(ctx): + """Determines the value of the enableIvy option in the generated tsconfig. + + Args: + ctx: skylark rule execution context + + Returns: + the value of enableIvy that needs to be set in angularCompilerOptions in the generated tsconfig + """ + + strategy = _compile_strategy(ctx) + if strategy == 'legacy': + return False + elif strategy == 'global': + return True + elif strategy == 'local': + return 'ngtsc' + elif strategy == 'jit': + return 'tsc' + else: + fail('unreachable') + +def _include_ng_files(ctx): + """Determines whether Angular outputs will be produced by the current compilation strategy. + + Args: + ctx: skylark rule execution context + + Returns: + true iff the current compilation strategy will produce View Engine compilation outputs (such as + factory files), false otherwise + """ + + strategy = _compile_strategy(ctx) + return strategy == 'legacy' or strategy == 'global' + def _basename_of(ctx, file): ext_len = len(".ts") if file.short_path.endswith(".ng.html"): @@ -61,6 +145,8 @@ def _should_produce_flat_module_outs(ctx): # in the library. Most of these will be produced as empty files but it is # unknown, without parsing, which will be empty. def _expected_outs(ctx): + include_ng_files = _include_ng_files(ctx) + devmode_js_files = [] closure_js_files = [] declaration_files = [] @@ -78,7 +164,7 @@ def _expected_outs(ctx): if short_path.endswith(".ts") and not short_path.endswith(".d.ts"): basename = short_path[len(package_prefix):-len(".ts")] - if len(factory_basename_set) == 0 or basename in factory_basename_set: + if include_ng_files and (len(factory_basename_set) == 0 or basename in factory_basename_set): devmode_js = [ ".ngfactory.js", ".ngsummary.js", @@ -90,7 +176,7 @@ def _expected_outs(ctx): devmode_js = [".js"] summaries = [] metadata = [] - elif short_path.endswith(".css"): + elif include_ng_files and short_path.endswith(".css"): basename = short_path[len(package_prefix):-len(".css")] devmode_js = [ ".css.shim.ngstyle.js", @@ -113,7 +199,7 @@ def _expected_outs(ctx): 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): + if include_ng_files and _should_produce_flat_module_outs(ctx): flat_module_out = _flat_module_out_file(ctx) devmode_js_files.append(ctx.actions.declare_file("%s.js" % flat_module_out)) closure_js_files.append(ctx.actions.declare_file("%s.closure.js" % flat_module_out)) @@ -123,7 +209,12 @@ def _expected_outs(ctx): else: bundle_index_typings = None - i18n_messages_files = [ctx.new_file(ctx.genfiles_dir, ctx.label.name + "_ngc_messages.xmb")] + # TODO(alxhub): i18n is only produced by the legacy compiler currently. This should be re-enabled + # when ngtsc can extract messages + if include_ng_files: + i18n_messages_files = [ctx.new_file(ctx.genfiles_dir, ctx.label.name + "_ngc_messages.xmb")] + else: + i18n_messages_files = [] return struct( closure_js = closure_js_files, @@ -135,14 +226,9 @@ def _expected_outs(ctx): i18n_messages = i18n_messages_files, ) -def _ivy_tsconfig(ctx, files, srcs, **kwargs): - return _ngc_tsconfig_helper(ctx, files, srcs, True, **kwargs) - def _ngc_tsconfig(ctx, files, srcs, **kwargs): - return _ngc_tsconfig_helper(ctx, files, srcs, False, **kwargs) - -def _ngc_tsconfig_helper(ctx, files, srcs, enable_ivy, **kwargs): outs = _expected_outs(ctx) + include_ng_files = _include_ng_files(ctx) if "devmode_manifest" in kwargs: expected_outs = outs.devmode_js + outs.declarations + outs.summaries + outs.metadata else: @@ -152,8 +238,9 @@ def _ngc_tsconfig_helper(ctx, files, srcs, enable_ivy, **kwargs): "enableResourceInlining": ctx.attr.inline_resources, "generateCodeForLibraries": False, "allowEmptyCodegenFiles": True, - "enableSummariesForJit": True, - "enableIvy": enable_ivy, + # Summaries are only enabled if Angular outputs are to be produced. + "enableSummariesForJit": include_ng_files, + "enableIvy": _enable_ivy_value(ctx), "fullTemplateTypeCheck": ctx.attr.type_check, # FIXME: wrong place to de-dupe "expectedOut": depset([o.path for o in expected_outs]).to_list() @@ -216,8 +303,10 @@ def ngc_compile_action(ctx, label, inputs, outputs, messages_out, tsconfig_file, the parameters of the compilation which will be used to replay the ngc action for i18N. """ + include_ng_files = _include_ng_files(ctx) + mnemonic = "AngularTemplateCompile" - progress_message = "Compiling Angular templates (ngc) %s" % label + progress_message = "Compiling Angular templates (%s) %s" % (_compiler_name(ctx), label) if locale: mnemonic = "AngularI18NMerging" @@ -251,7 +340,7 @@ def ngc_compile_action(ctx, label, inputs, outputs, messages_out, tsconfig_file, }, ) - if messages_out != None: + if include_ng_files and messages_out != None: ctx.actions.run( inputs = list(inputs), outputs = messages_out, @@ -313,7 +402,7 @@ def _ts_expected_outs(ctx, label): _ignored = [label] return _expected_outs(ctx) -def ng_module_impl(ctx, ts_compile_actions, ivy = False): +def ng_module_impl(ctx, ts_compile_actions): """Implementation function for the ng_module rule. This is exposed so that google3 can have its own entry point that re-uses this @@ -322,29 +411,30 @@ def ng_module_impl(ctx, ts_compile_actions, ivy = False): Args: ctx: the skylark rule context ts_compile_actions: generates all the actions to run an ngc compilation - ivy: if True, run the compiler in Ivy mode (internal only) Returns: the result of the ng_module rule as a dict, suitable for conversion by ts_providers_dict_to_struct """ - tsconfig = _ngc_tsconfig if not ivy else _ivy_tsconfig + include_ng_files = _include_ng_files(ctx) providers = ts_compile_actions( ctx, is_library=True, compile_action=_prodmode_compile_action, devmode_compile_action=_devmode_compile_action, - tsc_wrapped_tsconfig=tsconfig, + tsc_wrapped_tsconfig=_ngc_tsconfig, outputs = _ts_expected_outs) outs = _expected_outs(ctx) - providers["angular"] = { - "summaries": outs.summaries, - "metadata": outs.metadata - } - providers["ngc_messages"] = outs.i18n_messages - if _should_produce_flat_module_outs(ctx): + if include_ng_files: + providers["angular"] = { + "summaries": outs.summaries, + "metadata": outs.metadata + } + providers["ngc_messages"] = outs.i18n_messages + + if include_ng_files and _should_produce_flat_module_outs(ctx): if len(outs.metadata) > 1: fail("expecting exactly one metadata output for " + str(ctx.label)) @@ -360,9 +450,6 @@ def ng_module_impl(ctx, ts_compile_actions, ivy = False): def _ng_module_impl(ctx): return ts_providers_dict_to_struct(ng_module_impl(ctx, compile_ts)) -def _ivy_module_impl(ctx): - return ts_providers_dict_to_struct(ng_module_impl(ctx, compile_ts, True)) - NG_MODULE_ATTRIBUTES = { "srcs": attr.label_list(allow_files = [".ts"]), @@ -429,11 +516,16 @@ ng_module = rule( outputs = COMMON_OUTPUTS, ) -# TODO(alxhub): this rule exists to allow early testing of the Ivy compiler within angular/angular, -# and should not be made public. When ng_module() supports Ivy-mode outputs, this rule should be -# removed and its usages refactored to use ng_module() directly. -internal_ivy_ng_module = rule( - implementation = _ivy_module_impl, - attrs = NG_MODULE_RULE_ATTRS, + +# TODO(alxhub): this rule causes legacy ngc to produce Ivy outputs from global analysis information. +# It to facilitate testing of the Ivy runtime until ngtsc is mature enough to be used instead, and +# should be removed once ngtsc is capable of fulfilling the same requirements. +internal_global_ng_module = rule( + implementation = _ng_module_impl, + attrs = dict(NG_MODULE_RULE_ATTRS, **{ + "_global_mode": attr.bool( + default = True, + ), + }), outputs = COMMON_OUTPUTS, ) diff --git a/packages/bazel/src/ng_rollup_bundle.bzl b/packages/bazel/src/ng_rollup_bundle.bzl index 8a4b375d53..e4584d6133 100644 --- a/packages/bazel/src/ng_rollup_bundle.bzl +++ b/packages/bazel/src/ng_rollup_bundle.bzl @@ -27,6 +27,29 @@ PLUGIN_CONFIG="{sideEffectFreeModules: [\n%s]}" % ",\n".join( BO_ROLLUP="angular_devkit/packages/angular_devkit/build_optimizer/src/build-optimizer/rollup-plugin.js" BO_PLUGIN="require('%s').default(%s)" % (BO_ROLLUP, PLUGIN_CONFIG) +def _use_plain_rollup(ctx): + """Determine whether to use the Angular or upstream versions of the rollup_bundle rule. + + In legacy mode, the Angular version of rollup is used. This runs build optimizer as part of its + processing, which affects decorators and annotations. + + In other modes, an emulation of the upstream rollup_bundle rule is used. This avoids running + build optimizer on code which isn't designed to be optimized by it. + + Args: + ctx: skylark rule execution context + + Returns: + true iff the Angular version of rollup with build optimizer should be used, false otherwise + """ + + if 'compile' not in ctx.var: + return False + + strategy = ctx.var['compile'] + return strategy != 'legacy' + + def run_brotli(ctx, input, output): ctx.actions.run( executable = ctx.executable._brotli, @@ -35,7 +58,41 @@ def run_brotli(ctx, input, output): arguments = ["--output=%s" % output.path, input.path], ) +# Borrowed from bazelbuild/rules_nodejs +def _run_tsc(ctx, input, output): + args = ctx.actions.args() + args.add(["--target", "es5"]) + args.add("--allowJS") + args.add(input.path) + args.add(["--outFile", output.path]) + + ctx.action( + executable = ctx.executable._tsc, + inputs = [input], + outputs = [output], + arguments = [args] + ) + +# Borrowed from bazelbuild/rules_nodejs, with the addition of brotli compression output +def _plain_rollup_bundle(ctx): + rollup_config = write_rollup_config(ctx) + run_rollup(ctx, collect_es2015_sources(ctx), rollup_config, ctx.outputs.build_es6) + _run_tsc(ctx, ctx.outputs.build_es6, ctx.outputs.build_es5) + source_map = run_uglify(ctx, ctx.outputs.build_es5, ctx.outputs.build_es5_min) + run_uglify(ctx, ctx.outputs.build_es5, ctx.outputs.build_es5_min_debug, debug = True) + umd_rollup_config = write_rollup_config(ctx, filename = "_%s_umd.rollup.conf.js", output_format = "umd") + run_rollup(ctx, collect_es2015_sources(ctx), umd_rollup_config, ctx.outputs.build_umd) + run_sourcemapexplorer(ctx, ctx.outputs.build_es5_min, source_map, ctx.outputs.explore_html) + + run_brotli(ctx, ctx.outputs.build_es5_min, ctx.outputs.build_es5_min_compressed) + files = [ctx.outputs.build_es5_min, source_map] + return DefaultInfo(files = depset(files), runfiles = ctx.runfiles(files)) + def _ng_rollup_bundle(ctx): + # Escape and use the plain rollup rule if the compilation strategy requires it + if _use_plain_rollup(ctx): + return _plain_rollup_bundle(ctx) + # We don't expect anyone to make use of this bundle yet, but it makes this rule # compatible with rollup_bundle which allows them to be easily swapped back and # forth. diff --git a/packages/bazel/src/ngc-wrapped/index.ts b/packages/bazel/src/ngc-wrapped/index.ts index 02b9b14b04..a787b4026c 100644 --- a/packages/bazel/src/ngc-wrapped/index.ts +++ b/packages/bazel/src/ngc-wrapped/index.ts @@ -119,6 +119,15 @@ export function compile({allowNonHermeticReads, allDepsCompiledWithBazel = true, compilerOpts.annotationsAs = 'static fields'; } + // Detect from compilerOpts whether the entrypoint is being invoked in Ivy mode. + const isInIvyMode = compilerOpts.enableIvy === 'ngtsc' || compilerOpts.enableIvy === 'tsc'; + + // Disable downleveling and Closure annotation if in Ivy mode. + if (isInIvyMode) { + compilerOpts.annotateForClosureCompiler = false; + compilerOpts.annotationsAs = 'decorators'; + } + if (!compilerOpts.rootDirs) { throw new Error('rootDirs is not set!'); } @@ -172,6 +181,12 @@ export function compile({allowNonHermeticReads, allDepsCompiledWithBazel = true, const bazelHost = new CompilerHost( files, compilerOpts, bazelOpts, tsHost, fileLoader, allowNonHermeticReads, generatedFileModuleResolver); + + // Also need to disable decorator downleveling in the BazelHost in Ivy mode. + if (isInIvyMode) { + bazelHost.transformDecorators = false; + } + // Prevent tsickle adding any types at all if we don't want closure compiler annotations. bazelHost.transformTypesToClosure = compilerOpts.annotateForClosureCompiler; const origBazelHostFileExist = bazelHost.fileExists; diff --git a/packages/compiler-cli/integrationtest/bazel/injector_def/ivy_build/app/BUILD.bazel b/packages/compiler-cli/integrationtest/bazel/injector_def/ivy_build/app/BUILD.bazel index 8cbf1b54a4..15a6b28c75 100644 --- a/packages/compiler-cli/integrationtest/bazel/injector_def/ivy_build/app/BUILD.bazel +++ b/packages/compiler-cli/integrationtest/bazel/injector_def/ivy_build/app/BUILD.bazel @@ -1,6 +1,6 @@ package(default_visibility = ["//visibility:public"]) -load("//tools:defaults.bzl", "ivy_ng_module", "ts_library") +load("//tools:defaults.bzl", "ivy_ng_module") load("//packages/bazel/src:ng_rollup_bundle.bzl", "ng_rollup_bundle") ivy_ng_module( diff --git a/packages/compiler-cli/src/main.ts b/packages/compiler-cli/src/main.ts index 25dcd54281..1e17fc9d62 100644 --- a/packages/compiler-cli/src/main.ts +++ b/packages/compiler-cli/src/main.ts @@ -40,8 +40,8 @@ export function main( function createEmitCallback(options: api.CompilerOptions): api.TsEmitCallback|undefined { - const transformDecorators = - options.enableIvy !== 'ngtsc' && options.annotationsAs !== 'decorators'; + const transformDecorators = options.enableIvy !== 'ngtsc' && options.enableIvy !== 'tsc' && + options.annotationsAs !== 'decorators'; const transformTypesToClosure = options.annotateForClosureCompiler; if (!transformDecorators && !transformTypesToClosure) { return undefined; diff --git a/packages/compiler-cli/src/transformers/api.ts b/packages/compiler-cli/src/transformers/api.ts index ce1cd4e233..b37033443d 100644 --- a/packages/compiler-cli/src/transformers/api.ts +++ b/packages/compiler-cli/src/transformers/api.ts @@ -182,9 +182,17 @@ export interface CompilerOptions extends ts.CompilerOptions { * Not all features are supported with this option enabled. It is only supported * for experimentation and testing of Render3 style code generation. * + * Acceptable values are as follows: + * + * `false` - run ngc normally + * `true` - run ngc with its usual global analysis, but compile decorators to Ivy fields instead + * of running the View Engine compilers + * `ngtsc` - run the ngtsc compiler instead of the normal ngc compiler + * `tsc` - behave like plain tsc as much as possible (used for testing JIT code) + * * @experimental */ - enableIvy?: boolean|'ngtsc'; + enableIvy?: boolean|'ngtsc'|'tsc'; /** @internal */ collectAllErrors?: boolean; diff --git a/packages/compiler-cli/src/transformers/compiler_host.ts b/packages/compiler-cli/src/transformers/compiler_host.ts index 972313ddd8..a91d056320 100644 --- a/packages/compiler-cli/src/transformers/compiler_host.ts +++ b/packages/compiler-cli/src/transformers/compiler_host.ts @@ -24,7 +24,7 @@ const EXT = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/; export function createCompilerHost( {options, tsHost = ts.createCompilerHost(options, true)}: {options: CompilerOptions, tsHost?: ts.CompilerHost}): CompilerHost { - if (options.enableIvy) { + if (options.enableIvy === 'ngtsc' || options.enableIvy === 'tsc') { return new NgtscCompilerHost(tsHost); } return tsHost; diff --git a/packages/compiler-cli/src/transformers/program.ts b/packages/compiler-cli/src/transformers/program.ts index 78764efca3..5b9f4c67fc 100644 --- a/packages/compiler-cli/src/transformers/program.ts +++ b/packages/compiler-cli/src/transformers/program.ts @@ -26,6 +26,7 @@ import {getAngularEmitterTransformFactory} from './node_emitter_transform'; import {PartialModuleMetadataTransformer} from './r3_metadata_transform'; import {StripDecoratorsMetadataTransformer, getDecoratorStripTransformerFactory} from './r3_strip_decorators'; import {getAngularClassTransformerFactory} from './r3_transform'; +import {TscPassThroughProgram} from './tsc_pass_through'; import {DTS, GENERATED_FILES, StructureIsReused, TS, createMessageDiagnostic, isInRootDir, ngToTsDiagnostic, tsStructureIsReused, userError} from './util'; @@ -287,7 +288,7 @@ class AngularCompilerProgram implements Program { emitCallback?: TsEmitCallback, mergeEmitResultsCallback?: TsMergeEmitResultsCallback, } = {}): ts.EmitResult { - if (this.options.enableIvy === 'ngtsc') { + if (this.options.enableIvy === 'ngtsc' || this.options.enableIvy === 'tsc') { throw new Error('Cannot run legacy compiler in ngtsc mode'); } return this.options.enableIvy === true ? this._emitRender3(parameters) : @@ -337,14 +338,34 @@ class AngularCompilerProgram implements Program { /* genFiles */ undefined, /* partialModules */ modules, /* stripDecorators */ this.reifiedDecorators, customTransformers); - const emitResult = emitCallback({ - program: this.tsProgram, - host: this.host, - options: this.options, - writeFile: writeTsFile, emitOnlyDtsFiles, - customTransformers: tsCustomTransformers - }); - return emitResult; + + // Restore the original references before we emit so TypeScript doesn't emit + // a reference to the .d.ts file. + const augmentedReferences = new Map>(); + for (const sourceFile of this.tsProgram.getSourceFiles()) { + const originalReferences = getOriginalReferences(sourceFile); + if (originalReferences) { + augmentedReferences.set(sourceFile, sourceFile.referencedFiles); + sourceFile.referencedFiles = originalReferences; + } + } + + try { + return emitCallback({ + program: this.tsProgram, + host: this.host, + options: this.options, + writeFile: writeTsFile, emitOnlyDtsFiles, + customTransformers: tsCustomTransformers + }); + } finally { + // Restore the references back to the augmented value to ensure that the + // checks that TypeScript makes for project structure reuse will succeed. + for (const [sourceFile, references] of Array.from(augmentedReferences)) { + // TODO(chuckj): Remove any cast after updating build to 2.6 + (sourceFile as any).referencedFiles = references; + } + } } private _emitRender2( @@ -909,6 +930,8 @@ export function createProgram({rootNames, options, host, oldProgram}: { }): Program { if (options.enableIvy === 'ngtsc') { return new NgtscProgram(rootNames, options, host, oldProgram); + } else if (options.enableIvy === 'tsc') { + return new TscPassThroughProgram(rootNames, options, host, oldProgram); } return new AngularCompilerProgram(rootNames, options, host, oldProgram); } diff --git a/packages/compiler-cli/src/transformers/tsc_pass_through.ts b/packages/compiler-cli/src/transformers/tsc_pass_through.ts new file mode 100644 index 0000000000..fde85062b4 --- /dev/null +++ b/packages/compiler-cli/src/transformers/tsc_pass_through.ts @@ -0,0 +1,107 @@ +/** + * @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 {GeneratedFile} from '@angular/compiler'; +import * as path from 'path'; +import * as ts from 'typescript'; + +import * as api from '../transformers/api'; + +/** + * An implementation of the `Program` API which behaves like plain `tsc` and does not include any + * Angular-specific behavior whatsoever. + * + * This allows `ngc` to behave like `tsc` in cases where JIT code needs to be tested. + */ +export class TscPassThroughProgram implements api.Program { + private tsProgram: ts.Program; + + constructor( + rootNames: ReadonlyArray, private options: api.CompilerOptions, + private host: api.CompilerHost, oldProgram?: api.Program) { + this.tsProgram = + ts.createProgram(rootNames, options, host, oldProgram && oldProgram.getTsProgram()); + } + + getTsProgram(): ts.Program { return this.tsProgram; } + + getTsOptionDiagnostics(cancellationToken?: ts.CancellationToken| + undefined): ReadonlyArray { + return this.tsProgram.getOptionsDiagnostics(cancellationToken); + } + + getNgOptionDiagnostics(cancellationToken?: ts.CancellationToken| + undefined): ReadonlyArray { + return []; + } + + getTsSyntacticDiagnostics( + sourceFile?: ts.SourceFile|undefined, + cancellationToken?: ts.CancellationToken|undefined): ReadonlyArray { + return this.tsProgram.getSyntacticDiagnostics(sourceFile, cancellationToken); + } + + getNgStructuralDiagnostics(cancellationToken?: ts.CancellationToken| + undefined): ReadonlyArray { + return []; + } + + getTsSemanticDiagnostics( + sourceFile?: ts.SourceFile|undefined, + cancellationToken?: ts.CancellationToken|undefined): ReadonlyArray { + return this.tsProgram.getSemanticDiagnostics(sourceFile, cancellationToken); + } + + getNgSemanticDiagnostics( + fileName?: string|undefined, + cancellationToken?: ts.CancellationToken|undefined): ReadonlyArray { + return []; + } + + loadNgStructureAsync(): Promise { return Promise.resolve(); } + + listLazyRoutes(entryRoute?: string|undefined): api.LazyRoute[] { + throw new Error('Method not implemented.'); + } + + getLibrarySummaries(): Map { + throw new Error('Method not implemented.'); + } + + getEmittedGeneratedFiles(): Map { + throw new Error('Method not implemented.'); + } + + getEmittedSourceFiles(): Map { + throw new Error('Method not implemented.'); + } + + emit(opts?: { + emitFlags?: api.EmitFlags, + cancellationToken?: ts.CancellationToken, + customTransformers?: api.CustomTransformers, + emitCallback?: api.TsEmitCallback, + mergeEmitResultsCallback?: api.TsMergeEmitResultsCallback + }): ts.EmitResult { + const emitCallback = opts && opts.emitCallback || defaultEmitCallback; + + const emitResult = emitCallback({ + program: this.tsProgram, + host: this.host, + options: this.options, + emitOnlyDtsFiles: false, + }); + return emitResult; + } +} + +const defaultEmitCallback: api.TsEmitCallback = + ({program, targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, + customTransformers}) => + program.emit( + targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers); diff --git a/packages/compiler/src/aot/compiler_options.ts b/packages/compiler/src/aot/compiler_options.ts index 10f87a333c..e4d7d1682e 100644 --- a/packages/compiler/src/aot/compiler_options.ts +++ b/packages/compiler/src/aot/compiler_options.ts @@ -18,5 +18,5 @@ export interface AotCompilerOptions { fullTemplateTypeCheck?: boolean; allowEmptyCodegenFiles?: boolean; strictInjectionParameters?: boolean; - enableIvy?: boolean|'ngtsc'; + enableIvy?: boolean|'ngtsc'|'tsc'; } diff --git a/packages/core/BUILD.bazel b/packages/core/BUILD.bazel index 68a4cea600..6da82e9b34 100644 --- a/packages/core/BUILD.bazel +++ b/packages/core/BUILD.bazel @@ -18,6 +18,7 @@ ng_module( "//packages:types", "//packages/compiler", "@rxjs", + "@rxjs//operators", ], ) @@ -38,16 +39,18 @@ ng_package( ## Controls if Ivy is enabled. (Temporary target until we permanently switch over to Ivy) ## ## This file generates `src/ivy_switch.ts` file which reexports symbols for `ViewEngine` or `Ivy.` -## - append `--define=ivy=false` (default) to `bazel` command to reexport `./ivy_switch_false` -## and use `ViewEngine`; -## - append `--define=ivy=true` to `bazel` command to rexport `./ivy_switch_true` and use `Ivy`; +## - append `--define=compile=legacy` (default) to `bazel` command to reexport `./ivy_switch_legacy` +## and use `ViewEngine` +## - append `--define=compile=jit` to `bazel` command to rexport `./ivy_switch_jit` and use `Ivy` +## - append `--define=compile=local` to `bazel` command to rexport `./ivy_switch_jit` and use `Ivy` +## in the local analysis mode. (run as part of `ngtsc`) ## -## NOTE: `--define=ivy=true` works with any `bazel` command or target across the repo. +## NOTE: `--define=compile=jit` works with any `bazel` command or target across the repo. ## ## See: `//tools/bazel.rc` where `--define=ivy=false` is defined as default. ## See: `./src/ivy_switch.ts` for more details. genrule( name = "ivy_switch", outs = ["src/ivy_switch.ts"], - cmd = "echo export '*' from \"'./ivy_switch_$(ivy)';\" > $@", + cmd = "echo export '*' from \"'./ivy_switch_$(compile)';\" > $@", ) diff --git a/packages/core/src/ivy_switch.ts b/packages/core/src/ivy_switch.ts index e461fc900a..6a03ff8c7f 100644 --- a/packages/core/src/ivy_switch.ts +++ b/packages/core/src/ivy_switch.ts @@ -31,6 +31,6 @@ * 3) Import the symbol from `./ivy_switch`. The imported symbol will that point to either the * symbol in `./ivy_switch_false` and `./ivy_switch_false` depending on the compilation mode. */ -export * from './ivy_switch_false'; +export * from './ivy_switch_legacy'; // TODO(alxhub): debug why metadata doesn't properly propagate through this file. diff --git a/packages/core/src/ivy_switch_jit.ts b/packages/core/src/ivy_switch_jit.ts new file mode 100644 index 0000000000..60cf825342 --- /dev/null +++ b/packages/core/src/ivy_switch_jit.ts @@ -0,0 +1,9 @@ +/** + * @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 + */ + +export * from './ivy_switch_on'; \ No newline at end of file diff --git a/packages/core/src/ivy_switch_false.ts b/packages/core/src/ivy_switch_legacy.ts similarity index 100% rename from packages/core/src/ivy_switch_false.ts rename to packages/core/src/ivy_switch_legacy.ts diff --git a/packages/core/src/ivy_switch_local.ts b/packages/core/src/ivy_switch_local.ts new file mode 100644 index 0000000000..60cf825342 --- /dev/null +++ b/packages/core/src/ivy_switch_local.ts @@ -0,0 +1,9 @@ +/** + * @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 + */ + +export * from './ivy_switch_on'; \ No newline at end of file diff --git a/packages/core/src/ivy_switch_true.ts b/packages/core/src/ivy_switch_on.ts similarity index 100% rename from packages/core/src/ivy_switch_true.ts rename to packages/core/src/ivy_switch_on.ts diff --git a/packages/core/test/bundling/hello_world/BUILD.bazel b/packages/core/test/bundling/hello_world/BUILD.bazel index afe5cbd8a9..66549bedb6 100644 --- a/packages/core/test/bundling/hello_world/BUILD.bazel +++ b/packages/core/test/bundling/hello_world/BUILD.bazel @@ -44,9 +44,12 @@ jasmine_node_test( data = [ ":bundle", ":bundle.js", - ":bundle.min.js.br", + ":bundle.min.js", ":bundle.min_debug.js", ], + tags = [ + "ivy-jit", + ], deps = [":test_lib"], ) diff --git a/packages/core/test/bundling/hello_world/treeshaking_spec.ts b/packages/core/test/bundling/hello_world/treeshaking_spec.ts index fc7e758315..90b309c27b 100644 --- a/packages/core/test/bundling/hello_world/treeshaking_spec.ts +++ b/packages/core/test/bundling/hello_world/treeshaking_spec.ts @@ -29,7 +29,8 @@ describe('treeshaking with uglify', () => { expect(content).not.toContain('createCommonjsModule'); }); - it('should not contain zone.js', () => { expect(content).not.toContain('scheduleMicroTask'); }); + it('should not contain zone.js', + () => { expect(content).not.toContain('global[\'Zone\'] = Zone'); }); describe('functional test in domino', () => { it('should render hello world when not minified', diff --git a/packages/core/test/bundling/hello_world_jit/BUILD.bazel b/packages/core/test/bundling/hello_world_jit/BUILD.bazel deleted file mode 100644 index bd6acab2b3..0000000000 --- a/packages/core/test/bundling/hello_world_jit/BUILD.bazel +++ /dev/null @@ -1,58 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -load("//tools:defaults.bzl", "ts_library", "ivy_ng_module") -load("//tools/symbol-extractor:index.bzl", "js_expected_symbol_test") -load("@build_bazel_rules_nodejs//:defs.bzl", "jasmine_node_test", "rollup_bundle") -load("//tools/http-server:http_server.bzl", "http_server") - -ts_library( - name = "hello_world_jit", - srcs = ["index.ts"], - deps = [ - "//packages/core", - ], -) - -rollup_bundle( - name = "bundle", - # TODO(alexeagle): This is inconsistent. - # We try to teach users to always have their workspace at the start of a - # path, to disambiguate from other workspaces. - # Here, the rule implementation is looking in an execroot where the layout - # has an "external" directory for external dependencies. - # This should probably start with "angular/" and let the rule deal with it. - entry_point = "packages/core/test/bundling/hello_world_jit/index.js", - deps = [ - ":hello_world_jit", - "//packages/core", - ], -) - -ts_library( - name = "test_lib", - testonly = 1, - srcs = glob(["*_spec.ts"]), - deps = [ - "//packages:types", - "//packages/core", - "//packages/core/testing", - ], -) - -jasmine_node_test( - name = "test", - data = [ - ":bundle", - ":bundle.js", - ], - deps = [":test_lib"], -) - -http_server( - name = "devserver", - data = [ - "index.html", - ":bundle.min.js", - ":bundle.min_debug.js", - ], -) diff --git a/packages/core/test/bundling/hello_world_jit/index.html b/packages/core/test/bundling/hello_world_jit/index.html deleted file mode 100644 index c5c7bb3e0b..0000000000 --- a/packages/core/test/bundling/hello_world_jit/index.html +++ /dev/null @@ -1,31 +0,0 @@ - - - - - Angular Hello World Example - - - - - - - - - \ No newline at end of file diff --git a/packages/core/test/bundling/hello_world_jit/index.ts b/packages/core/test/bundling/hello_world_jit/index.ts deleted file mode 100644 index af6817a05e..0000000000 --- a/packages/core/test/bundling/hello_world_jit/index.ts +++ /dev/null @@ -1,38 +0,0 @@ -/** - * @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 'reflect-metadata'; - -import {Component, NgModule, ɵrenderComponent as renderComponent} from '@angular/core'; - -@Component({ - selector: 'greeting-cmp', - template: 'Hello World!', -}) -export class Greeting { -} - -@NgModule({ - declarations: [Greeting], - exports: [Greeting], -}) -export class GreetingModule { -} - -@Component({selector: 'hello-world', template: ''}) -export class HelloWorld { -} - -@NgModule({ - declarations: [HelloWorld], - imports: [GreetingModule], -}) -export class HelloWorldModule { -} - -renderComponent(HelloWorld); diff --git a/packages/core/test/bundling/hello_world_jit/integration_spec.ts b/packages/core/test/bundling/hello_world_jit/integration_spec.ts deleted file mode 100644 index ee349da0a6..0000000000 --- a/packages/core/test/bundling/hello_world_jit/integration_spec.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * @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 {ɵivyEnabled as ivyEnabled} from '@angular/core'; -import {withBody} from '@angular/core/testing'; -import * as fs from 'fs'; -import * as path from 'path'; - -const PACKAGE = 'angular/packages/core/test/bundling/hello_world_jit'; - -ivyEnabled && describe('Ivy JIT hello world', () => { - it('should render hello world', withBody('', () => { - require(path.join(PACKAGE, 'bundle.js')); - expect(document.body.textContent).toEqual('Hello World!'); - })); -}); - -xit('ensure at least one spec exists', () => {}); diff --git a/packages/core/test/bundling/todo/BUILD.bazel b/packages/core/test/bundling/todo/BUILD.bazel index c979e1c49e..f8d5684cbb 100644 --- a/packages/core/test/bundling/todo/BUILD.bazel +++ b/packages/core/test/bundling/todo/BUILD.bazel @@ -47,7 +47,7 @@ jasmine_node_test( data = [ ":bundle", ":bundle.js", - ":bundle.min.js.br", + ":bundle.min.js", ":bundle.min_debug.js", ], deps = [":test_lib"], diff --git a/tools/bazel.rc b/tools/bazel.rc index 52af863bea..d9f63f2da3 100644 --- a/tools/bazel.rc +++ b/tools/bazel.rc @@ -57,6 +57,6 @@ test --experimental_ui ################################ # Temporary Settings for Ivy # ################################ -# to determine if the compiler used should be Ivy or ViewEngine one can use `--define=ivy=true` on -# any bazel target. This is a temporary flag until codebase is permanently switched to ViewEngine. -build --define=ivy=false \ No newline at end of file +# to determine if the compiler used should be Ivy or ViewEngine one can use `--define=compile=local` on +# any bazel target. This is a temporary flag until codebase is permanently switched to Ivy. +build --define=compile=legacy \ No newline at end of file diff --git a/tools/defaults.bzl b/tools/defaults.bzl index 9954668f3d..c58c92b0e9 100644 --- a/tools/defaults.bzl +++ b/tools/defaults.bzl @@ -2,7 +2,7 @@ load("@build_bazel_rules_nodejs//:defs.bzl", _npm_package = "npm_package") load("@build_bazel_rules_typescript//:defs.bzl", _ts_library = "ts_library", _ts_web_test_suite = "ts_web_test_suite") load("//packages/bazel:index.bzl", _ng_module = "ng_module", _ng_package = "ng_package") -load("//packages/bazel/src:ng_module.bzl", _ivy_ng_module = "internal_ivy_ng_module") +load("//packages/bazel/src:ng_module.bzl", _internal_global_ng_module = "internal_global_ng_module") DEFAULT_TSCONFIG = "//packages:tsconfig-build.json" @@ -49,6 +49,16 @@ def ng_module(name, tsconfig = None, entry_point = None, **kwargs): entry_point = "public_api.ts" _ng_module(name = name, flat_module_out_file = name, tsconfig = tsconfig, entry_point = entry_point, **kwargs) +# ivy_ng_module behaves like ng_module, and under --define=compile=legacy it runs ngc with global +# analysis but produces Ivy outputs. Under other compile modes, it behaves as ng_module. +# TODO(alxhub): remove when ngtsc supports the same use cases. +def ivy_ng_module(name, tsconfig = None, entry_point = None, **kwargs): + if not tsconfig: + tsconfig = DEFAULT_TSCONFIG + if not entry_point: + entry_point = "public_api.ts" + _internal_global_ng_module(name = name, flat_module_out_file = name, tsconfig = tsconfig, entry_point = entry_point, **kwargs) + def ng_package(name, readme_md = None, license_banner = None, **kwargs): if not readme_md: readme_md = "//packages:README.md" @@ -91,8 +101,3 @@ def ts_web_test_suite(bootstrap = [], deps = [], **kwargs): # TODO(alexeagle): add remote browsers on SauceLabs ], **kwargs) - -def ivy_ng_module(name, tsconfig = None, **kwargs): - if not tsconfig: - tsconfig = DEFAULT_TSCONFIG - _ivy_ng_module(name = name, tsconfig = tsconfig, **kwargs)