diff --git a/packages/core/test/render3/compiler_canonical/back_patch_types_specs.ts b/packages/core/test/render3/compiler_canonical/back_patch_types_specs.ts new file mode 100644 index 0000000000..7c3e597dfe --- /dev/null +++ b/packages/core/test/render3/compiler_canonical/back_patch_types_specs.ts @@ -0,0 +1,138 @@ +/** + * @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 {Component, ContentChild, Directive, Injectable, Injector, Input, NgModule, NgModuleFactory, NgModuleRef, OnDestroy, Optional, Pipe, PipeTransform, QueryList, SimpleChanges, TemplateRef, Type, ViewChild, ViewContainerRef} from '../../../src/core'; +import * as r3 from '../../../src/render3/index'; + +import {pending_pull_22005} from './small_app_spec'; + +const details_elided = { + type: Object, +} as any; +export type $ComponentDef$ = any; + +/////////// +// Lib A - Compiled pre-Ivy +// "enableIvy": false +////////// + +// BEGIN FILE: node_modules/libA/module.ts (Compiled without Ivy) +@Component({}) +export class LibAComponent { +} + +@NgModule({declarations: [LibAComponent], imports: []}) +export class LibAModule { +} +// END FILE: node_modules/libA/module.ts +// BEGIN FILE: node_modules/libA/module.metadata.json +// Abridged version of metadata +const node_modules_libA_module_metadata = { + 'LibAModule': { + refs: ['LibAComponent'], + constructorDes: [], + }, + 'LibAComponent': { + constructorDes: [], + } +}; +// END FILE: node_modules/libA/module.metadata.json + + +/////////// +// Lib B - Compiled with Ivy +// "enableIvy": true +////////// + + +// BEGIN FILE: node_modules/libB/module.ts (Compiled with Ivy) +@Component({}) +export class LibBComponent { + // COMPILER GENERATED + static ngComponentDef: $ComponentDef$ = r3.defineComponent(details_elided); +} + +@NgModule({declarations: [LibAComponent], imports: []}) +export class LibBModule { + // COMPILER GENERATED + static ngInjectorDef = pending_pull_22005.defineInjector(details_elided); +} +// END FILE: node_modules/libB/module.ts +// BEGIN FILE: node_modules/libB/module.metadata.json +// Abridged version of metadata +// Must still generate metadata in case it should be consumed with non-ivy application +// Must mark the metadata with `hasNgDef: true` so that Ivy knows to ignore it. +const node_modules_libB_module_metadata = { + 'LibBModule': {refs: ['LibBComponent'], constructorDes: [], hasNgDef: true}, + 'LibBComponent': {constructorDes: [], hasNgDef: true} +}; +// END FILE: node_modules/libA/module.metadata.json + + + +/////////// +// Lib B - Compiled with Ivy +// "enableIvy": true +// "enableIvyBackPatch": true +////////// + + +// BEGIN FILE: src/app.ts (Compiled with Ivy) +@Component({}) +export class AppComponent { + // COMPILER GENERATED + static ngComponentDef: $ComponentDef$ = r3.defineComponent(details_elided); +} + +@NgModule({declarations: [LibAComponent], imports: []}) +export class AppModule { + // COMPILER GENERATED + static ngInjectorDef = pending_pull_22005.defineInjector(details_elided); +} +// END FILE: src/app.ts + +// BEGIN FILE: src/main.ts +// platformBrowserDynamic().bootstrapModule(AppModule); +// CLI rewrites it later to: +// platformBrowser().bootstrapModuleFactory(AppModuleFactory); +// END FILE: src/main.ts + +// BEGIN FILE: src/app.ngfactory.ts +function ngBackPatch_node_modules_libB_module() { + ngBackPatch_node_modules_libB_module_LibAComponent(); + ngBackPatch_node_modules_libB_module_LibAModule(); +} + +function ngBackPatch_node_modules_libB_module_LibAComponent() { + (LibAComponent as any).ngComponentDef = r3.defineComponent(details_elided); +} + +function ngBackPatch_node_modules_libB_module_LibAModule() { + (LibAModule as any).ngInjectorDef = pending_pull_22005.defineInjector(details_elided); +} + +export const AppModuleFactory: NgModuleFactory&{patchedDeps: boolean} = { + moduleType: AppModule, + patchedDeps: false, + create(parentInjector: Injector | null): NgModuleRef{ + this.patchedDeps && ngBackPatch_node_modules_libB_module() && (this.patchedDeps = true); + return details_elided;} +}; +// BEGIN FILE: src/app.ngfactory.ts + + +// ISSUE: I don't think this works. The issue is that multiple modules get flattened into single +// module and hence we can't patch transitively. +// ISSUE: can non-ivy @NgModule import Ivy @NgModule? I assume no, since the flattening of modules +// happens during compilation. + +// BEGIN FILE: src/main.ts +// platformBrowserDynamic().bootstrapModule(AppModule); +// CLI rewrites it to: +// platformBrowser().bootstrapModuleFactory(AppModuleFactory); +// END FILE: src/main.ts diff --git a/packages/core/test/render3/compiler_canonical/patch_types_spec.ts b/packages/core/test/render3/compiler_canonical/patch_types_spec.ts new file mode 100644 index 0000000000..ca4e37374c --- /dev/null +++ b/packages/core/test/render3/compiler_canonical/patch_types_spec.ts @@ -0,0 +1,78 @@ +/** + * @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 {Component, ContentChild, Directive, Injectable, Input, NgModule, OnDestroy, Optional, Pipe, PipeTransform, QueryList, SimpleChanges, TemplateRef, Type, ViewChild, ViewContainerRef} from '../../../src/core'; +import * as r3 from '../../../src/render3/index'; +import {pending_pull_22005} from './small_app_spec'; + +/** + * GOALS: + * - Patch types in tree shakable way + * - Generate these types for files which have `metadata.json` (since those are the files which + * have not been compiled with Ivy) + * - Have build optimizer hoist the patch functions into corresponding types to allow tree-shaking. + */ + +// File: node_modules/some_library/path/public.ts +// Implies metadata: node_modules/some_library/path/public.metadata.json +// Assume: node_modules/some_library/index.js re-exports ./path/public.ts#ThirdPartyClass +@Injectable() +class ThirdPartyClass { +} + + +@Injectable() +class CompiledWithIvy { + // NORMATIVE + static ngInjectableDef = pending_pull_22005.defineInjectable( + {factory: function CompileWithIvy_Factory() { return new CompiledWithIvy(); }}); + // /NORMATIVE +} + +// import { CompiledWithIvy } from 'some_library'; +@NgModule({providers: [ThirdPartyClass, CompiledWithIvy]}) +class CompiledWithIvyModule { + // NORMATIVE + static ngInjectorDef = pending_pull_22005.defineInjector({ + providers: [ThirdPartyClass, CompiledWithIvy], + factory: function CompiledWithIvyModule_Factory() { return new CompiledWithIvyModule(); } + }); + // /NORMATIVE +} + +/** + * Below is a function which should be generated right next to the `@NgModule` which + * imports types which have `.metadata.json` files. + * + * # JIT Mode + * - Because the `ngPatch_CompiledWithIvyModule` is invoked all parts get patched. + * + * # AOT Mode + * - Build Optimizer detects `@__BUILD_OPTIMIZER_COLOCATE__` annotation and moves the + * code from the current location to the destination. + * - The resulting `ngPatch_CompiledWithIvyModule` becomes empty and eligible for tree-shaking. + * - Uglify removes the `ngPatch_CompiledWithIvyModule` since it is empty. + * + * # AOT Closure Mode + * - Option A: not supported. (Preferred option) + * - Externally very few people use closure they will just have to wait until all of their + * libraries are Ivy. + * - Internally (g3) we build from source hence everyone switches to Ivy at the same time. + * - Option B: Write a closure pass similar to Build Optimizer which would move the code. + */ +// NORMATIVE +ngPatch_depsOf_CompiledWithIvyModule(); +function ngPatch_depsOf_CompiledWithIvyModule() { + ngPatch_node_modules_some_library_path_public_CompileWithIvy(); +} +function ngPatch_node_modules_some_library_path_public_CompileWithIvy() { + /** @__BUILD_OPTIMIZER_COLOCATE__ */ + (ThirdPartyClass as any).ngInjectableDef = pending_pull_22005.defineInjectable( + {factory: function CompileWithIvy_Factory() { return new ThirdPartyClass(); }}); +} +// /NORMATIVE