In ES5 code, TypeScript requires certain helpers (such as
`__spreadArrays()`) to be able to support ES2015+ features. These
helpers can be either imported from `tslib` (by setting the
`importHelpers` TS compiler option to `true`) or emitted inline (by
setting the `importHelpers` and `noEmitHelpers` TS compiler options to
`false`, which is the default value for both).
Ngtsc's `StaticInterpreter` (which is also used during ngcc processing)
is able to statically evaluate some of these helpers (currently
`__assign()`, `__spread()` and `__spreadArrays()`), as long as
`ReflectionHost#getDefinitionOfFunction()` correctly detects the
declaration of the helper. For this to happen, the left-hand side of the
corresponding call expression (i.e. `__spread(...)` or
`tslib.__spread(...)`) must be evaluated as a function declaration for
`getDefinitionOfFunction()` to be called with.
In the case of imported helpers, the `tslib.__someHelper` expression was
resolved to a function declaration of the form
`export declare function __someHelper(...args: any[][]): any[];`, which
allows `getDefinitionOfFunction()` to correctly map it to a TS helper.
In contrast, in the case of emitted helpers (and regardless of the
module format: `CommonJS`, `ESNext`, `UMD`, etc.)), the `__someHelper`
identifier was resolved to a variable declaration of the form
`var __someHelper = (this && this.__someHelper) || function () { ... }`,
which upon further evaluation was categorized as a `DynamicValue`
(prohibiting further evaluation by the `getDefinitionOfFunction()`).
As a result of the above, emitted TypeScript helpers were not evaluated
in ES5 code.
---
This commit changes the detection of TS helpers to leverage the existing
`KnownFn` feature (previously only used for built-in functions).
`Esm5ReflectionHost` is changed to always return `KnownDeclaration`s for
TS helpers, both imported (`getExportsOfModule()`) as well as emitted
(`getDeclarationOfIdentifier()`).
Similar changes are made to `CommonJsReflectionHost` and
`UmdReflectionHost`.
The `KnownDeclaration`s are then mapped to `KnownFn`s in
`StaticInterpreter`, allowing it to statically evaluate call expressions
involving any kind of TS helpers.
Jira issue: https://angular-team.atlassian.net/browse/FW-1689
PR Close #35191
		
	
			
		
			
				
	
	
		
			1613 lines
		
	
	
		
			62 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			1613 lines
		
	
	
		
			62 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
/**
 | 
						|
 * @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
 | 
						|
 */
 | 
						|
 | 
						|
/// <reference types="node" />
 | 
						|
 | 
						|
import * as os from 'os';
 | 
						|
 | 
						|
import {AbsoluteFsPath, FileSystem, absoluteFrom, getFileSystem, join} from '../../../src/ngtsc/file_system';
 | 
						|
import {Folder, MockFileSystem, TestFile, runInEachFileSystem} from '../../../src/ngtsc/file_system/testing';
 | 
						|
import {loadStandardTestFiles, loadTestFiles} from '../../../test/helpers';
 | 
						|
import {LockFileSync} from '../../src/execution/lock_file';
 | 
						|
import {mainNgcc} from '../../src/main';
 | 
						|
import {markAsProcessed} from '../../src/packages/build_marker';
 | 
						|
import {EntryPointJsonProperty, EntryPointPackageJson, SUPPORTED_FORMAT_PROPERTIES} from '../../src/packages/entry_point';
 | 
						|
import {Transformer} from '../../src/packages/transformer';
 | 
						|
import {DirectPackageJsonUpdater, PackageJsonUpdater} from '../../src/writing/package_json_updater';
 | 
						|
import {MockLogger} from '../helpers/mock_logger';
 | 
						|
import {compileIntoApf, compileIntoFlatEs5Package} from './util';
 | 
						|
 | 
						|
 | 
						|
const testFiles = loadStandardTestFiles({fakeCore: false, rxjs: true});
 | 
						|
 | 
						|
runInEachFileSystem(() => {
 | 
						|
  describe('ngcc main()', () => {
 | 
						|
    let _: typeof absoluteFrom;
 | 
						|
    let fs: FileSystem;
 | 
						|
    let pkgJsonUpdater: PackageJsonUpdater;
 | 
						|
 | 
						|
    beforeEach(() => {
 | 
						|
      _ = absoluteFrom;
 | 
						|
      fs = getFileSystem();
 | 
						|
      pkgJsonUpdater = new DirectPackageJsonUpdater(fs);
 | 
						|
      initMockFileSystem(fs, testFiles);
 | 
						|
 | 
						|
      // Force single-process execution in unit tests by mocking available CPUs to 1.
 | 
						|
      spyOn(os, 'cpus').and.returnValue([{model: 'Mock CPU'}]);
 | 
						|
    });
 | 
						|
 | 
						|
    it('should run ngcc without errors for esm2015', () => {
 | 
						|
      expect(() => mainNgcc({basePath: '/node_modules', propertiesToConsider: ['esm2015']}))
 | 
						|
          .not.toThrow();
 | 
						|
    });
 | 
						|
 | 
						|
    it('should run ngcc without errors for esm5', () => {
 | 
						|
      expect(() => mainNgcc({
 | 
						|
               basePath: '/node_modules',
 | 
						|
               propertiesToConsider: ['esm5'],
 | 
						|
               logger: new MockLogger(),
 | 
						|
             }))
 | 
						|
          .not.toThrow();
 | 
						|
    });
 | 
						|
 | 
						|
    it('should run ngcc without errors when "main" property is not present', () => {
 | 
						|
      mainNgcc({
 | 
						|
        basePath: '/dist',
 | 
						|
        propertiesToConsider: ['main', 'es2015'],
 | 
						|
        logger: new MockLogger(),
 | 
						|
      });
 | 
						|
 | 
						|
      expect(loadPackage('local-package', _('/dist')).__processed_by_ivy_ngcc__).toEqual({
 | 
						|
        es2015: '0.0.0-PLACEHOLDER',
 | 
						|
        typings: '0.0.0-PLACEHOLDER',
 | 
						|
      });
 | 
						|
    });
 | 
						|
 | 
						|
    it('should throw, if some of the entry-points are unprocessable', () => {
 | 
						|
      const createEntryPoint = (name: string, prop: EntryPointJsonProperty): TestFile[] => {
 | 
						|
        return [
 | 
						|
          {
 | 
						|
            name: _(`/dist/${name}/package.json`),
 | 
						|
            contents: `{"name": "${name}", "typings": "./index.d.ts", "${prop}": "./index.js"}`,
 | 
						|
          },
 | 
						|
          {name: _(`/dist/${name}/index.js`), contents: 'var DUMMY_DATA = true;'},
 | 
						|
          {name: _(`/dist/${name}/index.d.ts`), contents: 'export type DummyData = boolean;'},
 | 
						|
          {name: _(`/dist/${name}/index.metadata.json`), contents: 'DUMMY DATA'},
 | 
						|
        ];
 | 
						|
      };
 | 
						|
 | 
						|
      loadTestFiles([
 | 
						|
        ...createEntryPoint('processable-1', 'es2015'),
 | 
						|
        ...createEntryPoint('unprocessable-2', 'main'),
 | 
						|
        ...createEntryPoint('unprocessable-3', 'main'),
 | 
						|
      ]);
 | 
						|
 | 
						|
      expect(() => mainNgcc({
 | 
						|
               basePath: '/dist',
 | 
						|
               propertiesToConsider: ['es2015', 'fesm5', 'module'],
 | 
						|
               logger: new MockLogger(),
 | 
						|
             }))
 | 
						|
          .toThrowError(
 | 
						|
              'Unable to process any formats for the following entry-points (tried es2015, fesm5, module): \n' +
 | 
						|
              `  - ${_('/dist/unprocessable-2')}\n` +
 | 
						|
              `  - ${_('/dist/unprocessable-3')}`);
 | 
						|
    });
 | 
						|
 | 
						|
    it('should throw, if an error happens during processing', () => {
 | 
						|
      spyOn(Transformer.prototype, 'transform').and.throwError('Test error.');
 | 
						|
 | 
						|
      expect(() => mainNgcc({
 | 
						|
               basePath: '/dist',
 | 
						|
               targetEntryPointPath: 'local-package',
 | 
						|
               propertiesToConsider: ['main', 'es2015'],
 | 
						|
               logger: new MockLogger(),
 | 
						|
             }))
 | 
						|
          .toThrowError(`Test error.`);
 | 
						|
 | 
						|
      expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toBeUndefined();
 | 
						|
      expect(loadPackage('local-package', _('/dist')).__processed_by_ivy_ngcc__).toBeUndefined();
 | 
						|
    });
 | 
						|
 | 
						|
    it('should generate correct metadata for decorated getter/setter properties', () => {
 | 
						|
      compileIntoFlatEs5Package('test-package', {
 | 
						|
        '/index.ts': `
 | 
						|
          import {Directive, Input, NgModule} from '@angular/core';
 | 
						|
 | 
						|
          @Directive({selector: '[foo]'})
 | 
						|
          export class FooDirective {
 | 
						|
            @Input() get bar() { return 'bar'; }
 | 
						|
            set bar(value: string) {}
 | 
						|
          }
 | 
						|
 | 
						|
          @NgModule({
 | 
						|
            declarations: [FooDirective],
 | 
						|
          })
 | 
						|
          export class FooModule {}
 | 
						|
        `,
 | 
						|
      });
 | 
						|
 | 
						|
      mainNgcc({
 | 
						|
        basePath: '/node_modules',
 | 
						|
        targetEntryPointPath: 'test-package',
 | 
						|
        propertiesToConsider: ['main'],
 | 
						|
      });
 | 
						|
 | 
						|
      const jsContents = fs.readFile(_(`/node_modules/test-package/index.js`)).replace(/\s+/g, ' ');
 | 
						|
      expect(jsContents)
 | 
						|
          .toContain(
 | 
						|
              '/*@__PURE__*/ (function () { ɵngcc0.ɵsetClassMetadata(FooDirective, ' +
 | 
						|
              '[{ type: Directive, args: [{ selector: \'[foo]\' }] }], ' +
 | 
						|
              'function () { return []; }, ' +
 | 
						|
              '{ bar: [{ type: Input }] }); })();');
 | 
						|
    });
 | 
						|
 | 
						|
    ['esm5', 'esm2015'].forEach(target => {
 | 
						|
      it(`should be able to process spread operator inside objects for ${target} format (imported helpers)`,
 | 
						|
         () => {
 | 
						|
           compileIntoApf(
 | 
						|
               'test-package', {
 | 
						|
                 '/index.ts': `
 | 
						|
                  import {Directive, Input, NgModule} from '@angular/core';
 | 
						|
 | 
						|
                  const a = { '[class.a]': 'true' };
 | 
						|
                  const b = { '[class.b]': 'true' };
 | 
						|
 | 
						|
                  @Directive({
 | 
						|
                    selector: '[foo]',
 | 
						|
                    host: {...a, ...b, '[class.c]': 'false'}
 | 
						|
                  })
 | 
						|
                  export class FooDirective {}
 | 
						|
 | 
						|
                  @NgModule({
 | 
						|
                    declarations: [FooDirective],
 | 
						|
                  })
 | 
						|
                  export class FooModule {}
 | 
						|
                `,
 | 
						|
               },
 | 
						|
               {importHelpers: true, noEmitHelpers: true});
 | 
						|
 | 
						|
           fs.writeFile(
 | 
						|
               _('/node_modules/tslib/index.d.ts'),
 | 
						|
               `export declare function __assign(...args: object[]): object;`);
 | 
						|
 | 
						|
           mainNgcc({
 | 
						|
             basePath: '/node_modules',
 | 
						|
             targetEntryPointPath: 'test-package',
 | 
						|
             propertiesToConsider: [target],
 | 
						|
           });
 | 
						|
 | 
						|
           const jsContents = fs.readFile(_(`/node_modules/test-package/${target}/src/index.js`))
 | 
						|
                                  .replace(/\s+/g, ' ');
 | 
						|
           expect(jsContents).toContain('ngcc0.ɵɵclassProp("a", true)("b", true)("c", false)');
 | 
						|
         });
 | 
						|
 | 
						|
      it(`should be able to process emitted spread operator inside objects for ${target} format (emitted helpers)`,
 | 
						|
         () => {
 | 
						|
           compileIntoApf(
 | 
						|
               'test-package', {
 | 
						|
                 '/index.ts': `
 | 
						|
                    import {Directive, Input, NgModule} from '@angular/core';
 | 
						|
 | 
						|
                    const a = { '[class.a]': 'true' };
 | 
						|
                    const b = { '[class.b]': 'true' };
 | 
						|
 | 
						|
                    @Directive({
 | 
						|
                      selector: '[foo]',
 | 
						|
                      host: {...a, ...b, '[class.c]': 'false'}
 | 
						|
                    })
 | 
						|
                    export class FooDirective {}
 | 
						|
 | 
						|
                    @NgModule({
 | 
						|
                      declarations: [FooDirective],
 | 
						|
                    })
 | 
						|
                    export class FooModule {}
 | 
						|
                  `,
 | 
						|
               },
 | 
						|
               {importHelpers: false, noEmitHelpers: false});
 | 
						|
 | 
						|
           mainNgcc({
 | 
						|
             basePath: '/node_modules',
 | 
						|
             targetEntryPointPath: 'test-package',
 | 
						|
             propertiesToConsider: [target],
 | 
						|
           });
 | 
						|
 | 
						|
           const jsContents = fs.readFile(_(`/node_modules/test-package/${target}/src/index.js`))
 | 
						|
                                  .replace(/\s+/g, ' ');
 | 
						|
           expect(jsContents).toContain('ngcc0.ɵɵclassProp("a", true)("b", true)("c", false)');
 | 
						|
         });
 | 
						|
    });
 | 
						|
 | 
						|
    it('should not add `const` in ES5 generated code', () => {
 | 
						|
      compileIntoFlatEs5Package('test-package', {
 | 
						|
        '/index.ts': `
 | 
						|
          import {Directive, Input, NgModule} from '@angular/core';
 | 
						|
 | 
						|
          @Directive({
 | 
						|
            selector: '[foo]',
 | 
						|
            host: {bar: ''},
 | 
						|
          })
 | 
						|
          export class FooDirective {
 | 
						|
          }
 | 
						|
 | 
						|
          @NgModule({
 | 
						|
            declarations: [FooDirective],
 | 
						|
          })
 | 
						|
          export class FooModule {}
 | 
						|
        `,
 | 
						|
      });
 | 
						|
 | 
						|
      mainNgcc({
 | 
						|
        basePath: '/node_modules',
 | 
						|
        targetEntryPointPath: 'test-package',
 | 
						|
        propertiesToConsider: ['main'],
 | 
						|
      });
 | 
						|
 | 
						|
      const jsContents = fs.readFile(_(`/node_modules/test-package/index.js`));
 | 
						|
      expect(jsContents).not.toMatch(/\bconst \w+\s*=/);
 | 
						|
    });
 | 
						|
 | 
						|
    it('should add ɵfac but not duplicate ɵprov properties on injectables', () => {
 | 
						|
      compileIntoFlatEs5Package('test-package', {
 | 
						|
        '/index.ts': `
 | 
						|
        import {Injectable, ɵɵdefineInjectable} from '@angular/core';
 | 
						|
        export const TestClassToken = 'TestClassToken';
 | 
						|
        @Injectable({providedIn: 'module'})
 | 
						|
        export class TestClass {
 | 
						|
          static ɵprov = ɵɵdefineInjectable({ factory: () => {}, token: TestClassToken, providedIn: "module" });
 | 
						|
        }
 | 
						|
        `,
 | 
						|
      });
 | 
						|
 | 
						|
      const before = fs.readFile(_(`/node_modules/test-package/index.js`));
 | 
						|
      const originalProp = /ɵprov[^;]+/.exec(before) ![0];
 | 
						|
      mainNgcc({
 | 
						|
        basePath: '/node_modules',
 | 
						|
        targetEntryPointPath: 'test-package',
 | 
						|
        propertiesToConsider: ['main'],
 | 
						|
      });
 | 
						|
      const after = fs.readFile(_(`/node_modules/test-package/index.js`));
 | 
						|
 | 
						|
      expect(before).toContain(originalProp);
 | 
						|
      expect(countOccurrences(before, 'ɵprov')).toEqual(1);
 | 
						|
      expect(countOccurrences(before, 'ɵfac')).toEqual(0);
 | 
						|
 | 
						|
      expect(after).toContain(originalProp);
 | 
						|
      expect(countOccurrences(after, 'ɵprov')).toEqual(1);
 | 
						|
      expect(countOccurrences(after, 'ɵfac')).toEqual(1);
 | 
						|
    });
 | 
						|
 | 
						|
    // This is necessary to ensure XPipeDef.fac is defined when delegated from injectable def
 | 
						|
    it('should always generate factory def (fac) before injectable def (prov)', () => {
 | 
						|
      compileIntoFlatEs5Package('test-package', {
 | 
						|
        '/index.ts': `
 | 
						|
        import {Injectable, Pipe, PipeTransform} from '@angular/core';
 | 
						|
 | 
						|
        @Injectable()
 | 
						|
        @Pipe({
 | 
						|
          name: 'myTestPipe'
 | 
						|
        })
 | 
						|
        export class TestClass implements PipeTransform {
 | 
						|
          transform(value: any) { return value; }
 | 
						|
        }
 | 
						|
        `,
 | 
						|
      });
 | 
						|
 | 
						|
      mainNgcc({
 | 
						|
        basePath: '/node_modules',
 | 
						|
        targetEntryPointPath: 'test-package',
 | 
						|
        propertiesToConsider: ['main'],
 | 
						|
      });
 | 
						|
 | 
						|
      const jsContents = fs.readFile(_(`/node_modules/test-package/index.js`));
 | 
						|
      expect(jsContents)
 | 
						|
          .toContain(
 | 
						|
              `TestClass.ɵfac = function TestClass_Factory(t) { return new (t || TestClass)(); };\n` +
 | 
						|
              `TestClass.ɵpipe = ɵngcc0.ɵɵdefinePipe({ name: "myTestPipe", type: TestClass, pure: true });\n` +
 | 
						|
              `TestClass.ɵprov = ɵngcc0.ɵɵdefineInjectable({`);
 | 
						|
    });
 | 
						|
 | 
						|
    it('should use the correct type name in typings files when an export has a different name in source files',
 | 
						|
       () => {
 | 
						|
         // We need to make sure that changes to the typings files use the correct name
 | 
						|
         // static ɵprov: ɵngcc0.ɵɵInjectableDef<ɵangular_packages_common_common_a>;
 | 
						|
         mainNgcc({
 | 
						|
           basePath: '/node_modules',
 | 
						|
           targetEntryPointPath: '@angular/common',
 | 
						|
           propertiesToConsider: ['esm2015']
 | 
						|
         });
 | 
						|
 | 
						|
         // In `@angular/common` the `BrowserPlatformLocation` class gets exported as something like
 | 
						|
         // `ɵangular_packages_common_common_a`.
 | 
						|
         const jsContents = fs.readFile(_(`/node_modules/@angular/common/fesm2015/common.js`));
 | 
						|
         const exportedNameMatch =
 | 
						|
             jsContents.match(/export.* BrowserPlatformLocation as ([^ ,}]+)/);
 | 
						|
         if (exportedNameMatch === null) {
 | 
						|
           return fail(
 | 
						|
               'Expected `/node_modules/@angular/common/fesm2015/common.js` to export `BrowserPlatformLocation` via an alias');
 | 
						|
         }
 | 
						|
         const exportedName = exportedNameMatch[1];
 | 
						|
 | 
						|
         // We need to make sure that the flat typings file exports this directly
 | 
						|
         const dtsContents = fs.readFile(_('/node_modules/@angular/common/common.d.ts'));
 | 
						|
         expect(dtsContents)
 | 
						|
             .toContain(`export declare class ${exportedName} extends PlatformLocation`);
 | 
						|
         // And that ngcc's modifications to that class use the correct (exported) name
 | 
						|
         expect(dtsContents).toContain(`static ɵfac: ɵngcc0.ɵɵFactoryDef<${exportedName}>`);
 | 
						|
       });
 | 
						|
 | 
						|
    it('should add generic type for ModuleWithProviders and generate exports for private modules',
 | 
						|
       () => {
 | 
						|
         compileIntoApf('test-package', {
 | 
						|
           '/index.ts': `
 | 
						|
              import {ModuleWithProviders} from '@angular/core';
 | 
						|
              import {InternalFooModule} from './internal';
 | 
						|
 | 
						|
              export class FooModule {
 | 
						|
                static forRoot(): ModuleWithProviders {
 | 
						|
                  return {
 | 
						|
                    ngModule: InternalFooModule,
 | 
						|
                  };
 | 
						|
                }
 | 
						|
              }
 | 
						|
            `,
 | 
						|
           '/internal.ts': `
 | 
						|
              import {NgModule} from '@angular/core';
 | 
						|
 | 
						|
              @NgModule()
 | 
						|
              export class InternalFooModule {}
 | 
						|
           `,
 | 
						|
         });
 | 
						|
 | 
						|
         mainNgcc({
 | 
						|
           basePath: '/node_modules',
 | 
						|
           targetEntryPointPath: 'test-package',
 | 
						|
           propertiesToConsider: ['esm2015', 'esm5', 'module'],
 | 
						|
         });
 | 
						|
 | 
						|
         // The .d.ts where FooModule is declared should have a generic type added
 | 
						|
         const dtsContents = fs.readFile(_(`/node_modules/test-package/src/index.d.ts`));
 | 
						|
         expect(dtsContents).toContain(`import * as ɵngcc0 from './internal';`);
 | 
						|
         expect(dtsContents)
 | 
						|
             .toContain(`static forRoot(): ModuleWithProviders<ɵngcc0.InternalFooModule>`);
 | 
						|
 | 
						|
         // The public facing .d.ts should export the InternalFooModule
 | 
						|
         const entryDtsContents = fs.readFile(_(`/node_modules/test-package/index.d.ts`));
 | 
						|
         expect(entryDtsContents).toContain(`export {InternalFooModule} from './src/internal';`);
 | 
						|
 | 
						|
         // The esm2015 index source should export the InternalFooModule
 | 
						|
         const esm2015Contents = fs.readFile(_(`/node_modules/test-package/esm2015/index.js`));
 | 
						|
         expect(esm2015Contents).toContain(`export {InternalFooModule} from './src/internal';`);
 | 
						|
 | 
						|
         // The esm5 index source should also export the InternalFooModule
 | 
						|
         const esm5Contents = fs.readFile(_(`/node_modules/test-package/esm5/index.js`));
 | 
						|
         expect(esm5Contents).toContain(`export {InternalFooModule} from './src/internal';`);
 | 
						|
       });
 | 
						|
 | 
						|
    it('should use `$localize` calls rather than tagged templates in ES5 generated code', () => {
 | 
						|
      compileIntoFlatEs5Package('test-package', {
 | 
						|
        '/index.ts': `
 | 
						|
        import {Component, Input, NgModule} from '@angular/core';
 | 
						|
 | 
						|
        @Component({
 | 
						|
          selector: '[foo]',
 | 
						|
          template: '<div i18n="some:\`description\`">A message</div>'
 | 
						|
        })
 | 
						|
        export class FooComponent {
 | 
						|
        }
 | 
						|
 | 
						|
        @NgModule({
 | 
						|
          declarations: [FooComponent],
 | 
						|
        })
 | 
						|
        export class FooModule {}
 | 
						|
      `,
 | 
						|
      });
 | 
						|
 | 
						|
      mainNgcc({
 | 
						|
        basePath: '/node_modules',
 | 
						|
        targetEntryPointPath: 'test-package',
 | 
						|
        propertiesToConsider: ['main'],
 | 
						|
      });
 | 
						|
 | 
						|
      const jsContents = fs.readFile(_(`/node_modules/test-package/index.js`));
 | 
						|
      expect(jsContents).not.toMatch(/\$localize\s*`/);
 | 
						|
      expect(jsContents)
 | 
						|
          .toMatch(
 | 
						|
              /\$localize\(ɵngcc\d+\.__makeTemplateObject\(\[":some:`description`\\u241Fefc92f285b3c24b083a8a594f62c7fccf3118766\\u241F3806630072763809030:A message"], \[":some\\\\:\\\\`description\\\\`\\u241Fefc92f285b3c24b083a8a594f62c7fccf3118766\\u241F3806630072763809030:A message"]\)\);/);
 | 
						|
    });
 | 
						|
 | 
						|
    describe('in async mode', () => {
 | 
						|
      it('should run ngcc without errors for fesm2015', async() => {
 | 
						|
        const promise = mainNgcc({
 | 
						|
          basePath: '/node_modules',
 | 
						|
          propertiesToConsider: ['fesm2015'],
 | 
						|
          async: true,
 | 
						|
        });
 | 
						|
 | 
						|
        expect(promise).toEqual(jasmine.any(Promise));
 | 
						|
        await promise;
 | 
						|
      });
 | 
						|
 | 
						|
      it('should reject, if some of the entry-points are unprocessable', async() => {
 | 
						|
        const createEntryPoint = (name: string, prop: EntryPointJsonProperty): TestFile[] => {
 | 
						|
          return [
 | 
						|
            {
 | 
						|
              name: _(`/dist/${name}/package.json`),
 | 
						|
              contents: `{"name": "${name}", "typings": "./index.d.ts", "${prop}": "./index.js"}`,
 | 
						|
            },
 | 
						|
            {name: _(`/dist/${name}/index.js`), contents: 'var DUMMY_DATA = true;'},
 | 
						|
            {name: _(`/dist/${name}/index.d.ts`), contents: 'export type DummyData = boolean;'},
 | 
						|
            {name: _(`/dist/${name}/index.metadata.json`), contents: 'DUMMY DATA'},
 | 
						|
          ];
 | 
						|
        };
 | 
						|
 | 
						|
        loadTestFiles([
 | 
						|
          ...createEntryPoint('processable-1', 'es2015'),
 | 
						|
          ...createEntryPoint('unprocessable-2', 'main'),
 | 
						|
          ...createEntryPoint('unprocessable-3', 'main'),
 | 
						|
        ]);
 | 
						|
 | 
						|
        const promise = mainNgcc({
 | 
						|
          basePath: '/dist',
 | 
						|
          propertiesToConsider: ['es2015', 'fesm5', 'module'],
 | 
						|
          logger: new MockLogger(),
 | 
						|
          async: true,
 | 
						|
        });
 | 
						|
 | 
						|
        await promise.then(
 | 
						|
            () => Promise.reject('Expected promise to be rejected.'),
 | 
						|
            err => expect(err).toEqual(new Error(
 | 
						|
                'Unable to process any formats for the following entry-points (tried es2015, fesm5, module): \n' +
 | 
						|
                `  - ${_('/dist/unprocessable-2')}\n` +
 | 
						|
                `  - ${_('/dist/unprocessable-3')}`)));
 | 
						|
      });
 | 
						|
 | 
						|
      it('should reject, if an error happens during processing', async() => {
 | 
						|
        spyOn(Transformer.prototype, 'transform').and.throwError('Test error.');
 | 
						|
 | 
						|
        const promise = mainNgcc({
 | 
						|
          basePath: '/dist',
 | 
						|
          targetEntryPointPath: 'local-package',
 | 
						|
          propertiesToConsider: ['main', 'es2015'],
 | 
						|
          logger: new MockLogger(),
 | 
						|
          async: true,
 | 
						|
        });
 | 
						|
 | 
						|
        await promise.then(
 | 
						|
            () => Promise.reject('Expected promise to be rejected.'),
 | 
						|
            err => expect(err).toEqual(new Error('Test error.')));
 | 
						|
 | 
						|
        expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toBeUndefined();
 | 
						|
        expect(loadPackage('local-package', _('/dist')).__processed_by_ivy_ngcc__).toBeUndefined();
 | 
						|
      });
 | 
						|
    });
 | 
						|
 | 
						|
    describe('with targetEntryPointPath', () => {
 | 
						|
      it('should only compile the given package entry-point (and its dependencies).', () => {
 | 
						|
        const STANDARD_MARKERS = {
 | 
						|
          main: '0.0.0-PLACEHOLDER',
 | 
						|
          module: '0.0.0-PLACEHOLDER',
 | 
						|
          es2015: '0.0.0-PLACEHOLDER',
 | 
						|
          esm5: '0.0.0-PLACEHOLDER',
 | 
						|
          esm2015: '0.0.0-PLACEHOLDER',
 | 
						|
          fesm5: '0.0.0-PLACEHOLDER',
 | 
						|
          fesm2015: '0.0.0-PLACEHOLDER',
 | 
						|
          typings: '0.0.0-PLACEHOLDER',
 | 
						|
        };
 | 
						|
 | 
						|
        mainNgcc({basePath: '/node_modules', targetEntryPointPath: '@angular/common/http/testing'});
 | 
						|
        expect(loadPackage('@angular/common/http/testing').__processed_by_ivy_ngcc__)
 | 
						|
            .toEqual(STANDARD_MARKERS);
 | 
						|
        // * `common/http` is a dependency of `common/http/testing`, so is compiled.
 | 
						|
        expect(loadPackage('@angular/common/http').__processed_by_ivy_ngcc__)
 | 
						|
            .toEqual(STANDARD_MARKERS);
 | 
						|
        // * `core` is a dependency of `common/http`, so is compiled.
 | 
						|
        expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toEqual(STANDARD_MARKERS);
 | 
						|
        // * `common` is a private (only in .js not .d.ts) dependency so is compiled.
 | 
						|
        expect(loadPackage('@angular/common').__processed_by_ivy_ngcc__).toEqual(STANDARD_MARKERS);
 | 
						|
        // * `common/testing` is not a dependency so is not compiled.
 | 
						|
        expect(loadPackage('@angular/common/testing').__processed_by_ivy_ngcc__).toBeUndefined();
 | 
						|
      });
 | 
						|
 | 
						|
      it('should not mark a non-Angular package as processed if it is the target', () => {
 | 
						|
        mainNgcc({basePath: '/node_modules', targetEntryPointPath: 'test-package'});
 | 
						|
 | 
						|
        // * `test-package` has no Angular and is not marked as processed.
 | 
						|
        expect(loadPackage('test-package').__processed_by_ivy_ngcc__).toBeUndefined();
 | 
						|
 | 
						|
        // * `core` is a dependency of `test-package`, but it is also not processed, since
 | 
						|
        // `test-package` was not processed.
 | 
						|
        expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toBeUndefined();
 | 
						|
      });
 | 
						|
 | 
						|
      it('should not mark a non-Angular package as processed if it is a dependency', () => {
 | 
						|
        // `test-package-user` is a valid Angular package that depends upon `test-package`.
 | 
						|
        loadTestFiles([
 | 
						|
          {
 | 
						|
            name: _('/node_modules/test-package-user/package.json'),
 | 
						|
            contents:
 | 
						|
                '{"name": "test-package-user", "es2015": "./index.js", "typings": "./index.d.ts"}'
 | 
						|
          },
 | 
						|
          {
 | 
						|
            name: _('/node_modules/test-package-user/index.js'),
 | 
						|
            contents: 'import * as x from \'test-package\';'
 | 
						|
          },
 | 
						|
          {
 | 
						|
            name: _('/node_modules/test-package-user/index.d.ts'),
 | 
						|
            contents: 'import * as x from \'test-package\';'
 | 
						|
          },
 | 
						|
          {name: _('/node_modules/test-package-user/index.metadata.json'), contents: 'DUMMY DATA'},
 | 
						|
        ]);
 | 
						|
 | 
						|
        mainNgcc({basePath: '/node_modules', targetEntryPointPath: 'test-package-user'});
 | 
						|
 | 
						|
        // * `test-package-user` is processed because it is compiled by Angular
 | 
						|
        expect(loadPackage('test-package-user').__processed_by_ivy_ngcc__).toEqual({
 | 
						|
          es2015: '0.0.0-PLACEHOLDER',
 | 
						|
          typings: '0.0.0-PLACEHOLDER',
 | 
						|
        });
 | 
						|
 | 
						|
        // * `test-package` is a dependency of `test-package-user` but has not been compiled by
 | 
						|
        // Angular, and so is not marked as processed
 | 
						|
        expect(loadPackage('test-package').__processed_by_ivy_ngcc__).toBeUndefined();
 | 
						|
 | 
						|
        // * `core` is a dependency of `test-package`, but it is not processed, because
 | 
						|
        // `test-package` was not processed.
 | 
						|
        expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toBeUndefined();
 | 
						|
      });
 | 
						|
 | 
						|
      it('should report an error if a dependency of the target does not exist', () => {
 | 
						|
        expect(() => {
 | 
						|
          mainNgcc({basePath: '/node_modules', targetEntryPointPath: 'invalid-package'});
 | 
						|
        })
 | 
						|
            .toThrowError(
 | 
						|
                'The target entry-point "invalid-package" has missing dependencies:\n - @angular/missing\n');
 | 
						|
      });
 | 
						|
    });
 | 
						|
 | 
						|
    describe('early skipping of target entry-point', () => {
 | 
						|
      describe('[compileAllFormats === true]', () => {
 | 
						|
        it('should skip all processing if all the properties are marked as processed', () => {
 | 
						|
          const logger = new MockLogger();
 | 
						|
          markPropertiesAsProcessed('@angular/common/http/testing', SUPPORTED_FORMAT_PROPERTIES);
 | 
						|
          mainNgcc({
 | 
						|
            basePath: '/node_modules',
 | 
						|
            targetEntryPointPath: '@angular/common/http/testing', logger,
 | 
						|
          });
 | 
						|
          expect(logger.logs.debug).toContain([
 | 
						|
            'The target entry-point has already been processed'
 | 
						|
          ]);
 | 
						|
        });
 | 
						|
 | 
						|
        it('should process the target if any `propertyToConsider` is not marked as processed',
 | 
						|
           () => {
 | 
						|
             const logger = new MockLogger();
 | 
						|
             markPropertiesAsProcessed('@angular/common/http/testing', ['esm2015', 'fesm2015']);
 | 
						|
             mainNgcc({
 | 
						|
               basePath: '/node_modules',
 | 
						|
               targetEntryPointPath: '@angular/common/http/testing',
 | 
						|
               propertiesToConsider: ['fesm2015', 'esm5', 'esm2015'], logger,
 | 
						|
             });
 | 
						|
             expect(logger.logs.debug).not.toContain([
 | 
						|
               'The target entry-point has already been processed'
 | 
						|
             ]);
 | 
						|
           });
 | 
						|
      });
 | 
						|
 | 
						|
      describe('[compileAllFormats === false]', () => {
 | 
						|
        it('should process the target if the first matching `propertyToConsider` is not marked as processed',
 | 
						|
           () => {
 | 
						|
             const logger = new MockLogger();
 | 
						|
             markPropertiesAsProcessed('@angular/common/http/testing', ['esm2015']);
 | 
						|
             mainNgcc({
 | 
						|
               basePath: '/node_modules',
 | 
						|
               targetEntryPointPath: '@angular/common/http/testing',
 | 
						|
               propertiesToConsider: ['esm5', 'esm2015'],
 | 
						|
               compileAllFormats: false, logger,
 | 
						|
             });
 | 
						|
 | 
						|
             expect(logger.logs.debug).not.toContain([
 | 
						|
               'The target entry-point has already been processed'
 | 
						|
             ]);
 | 
						|
           });
 | 
						|
 | 
						|
        it('should skip all processing if the first matching `propertyToConsider` is marked as processed',
 | 
						|
           () => {
 | 
						|
             const logger = new MockLogger();
 | 
						|
             markPropertiesAsProcessed('@angular/common/http/testing', ['esm2015']);
 | 
						|
             mainNgcc({
 | 
						|
               basePath: '/node_modules',
 | 
						|
               targetEntryPointPath: '@angular/common/http/testing',
 | 
						|
               // Simulate a property that does not exist on the package.json and will be ignored.
 | 
						|
               propertiesToConsider: ['missing', 'esm2015', 'esm5'],
 | 
						|
               compileAllFormats: false, logger,
 | 
						|
             });
 | 
						|
 | 
						|
             expect(logger.logs.debug).toContain([
 | 
						|
               'The target entry-point has already been processed'
 | 
						|
             ]);
 | 
						|
           });
 | 
						|
      });
 | 
						|
 | 
						|
      it('should skip all processing if the first matching `propertyToConsider` is marked as processed',
 | 
						|
         () => {
 | 
						|
           const logger = new MockLogger();
 | 
						|
           markPropertiesAsProcessed('@angular/common/http/testing', ['esm2015']);
 | 
						|
           mainNgcc({
 | 
						|
             basePath: '/node_modules',
 | 
						|
             targetEntryPointPath: '@angular/common/http/testing',
 | 
						|
             // Simulate a property that does not exist on the package.json and will be ignored.
 | 
						|
             propertiesToConsider: ['missing', 'esm2015', 'esm5'],
 | 
						|
             compileAllFormats: false, logger,
 | 
						|
           });
 | 
						|
 | 
						|
           expect(logger.logs.debug).toContain([
 | 
						|
             'The target entry-point has already been processed'
 | 
						|
           ]);
 | 
						|
         });
 | 
						|
    });
 | 
						|
 | 
						|
    function markPropertiesAsProcessed(packagePath: string, properties: EntryPointJsonProperty[]) {
 | 
						|
      const basePath = _('/node_modules');
 | 
						|
      const targetPackageJsonPath = join(basePath, packagePath, 'package.json');
 | 
						|
      const targetPackage = loadPackage(packagePath);
 | 
						|
      markAsProcessed(
 | 
						|
          pkgJsonUpdater, targetPackage, targetPackageJsonPath, ['typings', ...properties]);
 | 
						|
    }
 | 
						|
 | 
						|
    it('should clean up outdated artifacts', () => {
 | 
						|
      compileIntoFlatEs5Package('test-package', {
 | 
						|
        'index.ts': `
 | 
						|
        import {Directive} from '@angular/core';
 | 
						|
 | 
						|
        @Directive({selector: '[foo]'})
 | 
						|
        export class FooDirective {
 | 
						|
        }
 | 
						|
      `,
 | 
						|
      });
 | 
						|
      mainNgcc({
 | 
						|
        basePath: '/node_modules',
 | 
						|
        propertiesToConsider: ['main'],
 | 
						|
        logger: new MockLogger(),
 | 
						|
      });
 | 
						|
 | 
						|
      // Now hack the files to look like it was processed by an outdated version of ngcc
 | 
						|
      const packageJson = loadPackage('test-package', _('/node_modules'));
 | 
						|
      packageJson.__processed_by_ivy_ngcc__ !.typings = '8.0.0';
 | 
						|
      packageJson.main_ivy_ngcc = '__ivy_ngcc__/main.js';
 | 
						|
      fs.writeFile(_('/node_modules/test-package/package.json'), JSON.stringify(packageJson));
 | 
						|
      fs.writeFile(_('/node_modules/test-package/x.js'), 'processed content');
 | 
						|
      fs.writeFile(_('/node_modules/test-package/x.js.__ivy_ngcc_bak'), 'original content');
 | 
						|
      fs.ensureDir(_('/node_modules/test-package/__ivy_ngcc__/foo'));
 | 
						|
 | 
						|
      // Now run ngcc again to see that it cleans out the outdated artifacts
 | 
						|
      mainNgcc({
 | 
						|
        basePath: '/node_modules',
 | 
						|
        propertiesToConsider: ['main'],
 | 
						|
        logger: new MockLogger(),
 | 
						|
      });
 | 
						|
      const newPackageJson = loadPackage('test-package', _('/node_modules'));
 | 
						|
      expect(newPackageJson.__processed_by_ivy_ngcc__).toEqual({
 | 
						|
        main: '0.0.0-PLACEHOLDER',
 | 
						|
        typings: '0.0.0-PLACEHOLDER',
 | 
						|
      });
 | 
						|
      expect(newPackageJson.main_ivy_ngcc).toBeUndefined();
 | 
						|
      expect(fs.exists(_('/node_modules/test-package/x.js'))).toBe(true);
 | 
						|
      expect(fs.exists(_('/node_modules/test-package/x.js.__ivy_ngcc_bak'))).toBe(false);
 | 
						|
      expect(fs.readFile(_('/node_modules/test-package/x.js'))).toEqual('original content');
 | 
						|
      expect(fs.exists(_('/node_modules/test-package/__ivy_ngcc__'))).toBe(false);
 | 
						|
    });
 | 
						|
 | 
						|
 | 
						|
    describe('with propertiesToConsider', () => {
 | 
						|
      it('should complain if none of the properties in the `propertiesToConsider` list is supported',
 | 
						|
         () => {
 | 
						|
           const propertiesToConsider = ['es1337', 'fesm42'];
 | 
						|
           const errorMessage =
 | 
						|
               'No supported format property to consider among [es1337, fesm42]. Supported ' +
 | 
						|
               'properties: fesm2015, fesm5, es2015, esm2015, esm5, main, module';
 | 
						|
 | 
						|
           expect(() => mainNgcc({basePath: '/node_modules', propertiesToConsider}))
 | 
						|
               .toThrowError(errorMessage);
 | 
						|
         });
 | 
						|
 | 
						|
      it('should only compile the entry-point formats given in the `propertiesToConsider` list',
 | 
						|
         () => {
 | 
						|
           mainNgcc({
 | 
						|
             basePath: '/node_modules',
 | 
						|
             propertiesToConsider: ['main', 'esm5', 'module', 'fesm5'],
 | 
						|
             logger: new MockLogger(),
 | 
						|
 | 
						|
           });
 | 
						|
 | 
						|
           // The ES2015 formats are not compiled as they are not in `propertiesToConsider`.
 | 
						|
           expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toEqual({
 | 
						|
             esm5: '0.0.0-PLACEHOLDER',
 | 
						|
             main: '0.0.0-PLACEHOLDER',
 | 
						|
             module: '0.0.0-PLACEHOLDER',
 | 
						|
             fesm5: '0.0.0-PLACEHOLDER',
 | 
						|
             typings: '0.0.0-PLACEHOLDER',
 | 
						|
           });
 | 
						|
           expect(loadPackage('@angular/common').__processed_by_ivy_ngcc__).toEqual({
 | 
						|
             esm5: '0.0.0-PLACEHOLDER',
 | 
						|
             main: '0.0.0-PLACEHOLDER',
 | 
						|
             module: '0.0.0-PLACEHOLDER',
 | 
						|
             fesm5: '0.0.0-PLACEHOLDER',
 | 
						|
             typings: '0.0.0-PLACEHOLDER',
 | 
						|
           });
 | 
						|
           expect(loadPackage('@angular/common/testing').__processed_by_ivy_ngcc__).toEqual({
 | 
						|
             esm5: '0.0.0-PLACEHOLDER',
 | 
						|
             main: '0.0.0-PLACEHOLDER',
 | 
						|
             module: '0.0.0-PLACEHOLDER',
 | 
						|
             fesm5: '0.0.0-PLACEHOLDER',
 | 
						|
             typings: '0.0.0-PLACEHOLDER',
 | 
						|
           });
 | 
						|
           expect(loadPackage('@angular/common/http').__processed_by_ivy_ngcc__).toEqual({
 | 
						|
             esm5: '0.0.0-PLACEHOLDER',
 | 
						|
             main: '0.0.0-PLACEHOLDER',
 | 
						|
             module: '0.0.0-PLACEHOLDER',
 | 
						|
             fesm5: '0.0.0-PLACEHOLDER',
 | 
						|
             typings: '0.0.0-PLACEHOLDER',
 | 
						|
           });
 | 
						|
         });
 | 
						|
 | 
						|
      it('should mark all matching properties as processed in order not to compile them on a subsequent run',
 | 
						|
         () => {
 | 
						|
           const logger = new MockLogger();
 | 
						|
           const logs = logger.logs.debug;
 | 
						|
 | 
						|
           // `fesm2015` and `es2015` map to the same file: `./fesm2015/common.js`
 | 
						|
           mainNgcc({
 | 
						|
             basePath: '/node_modules/@angular/common',
 | 
						|
             propertiesToConsider: ['fesm2015'], logger,
 | 
						|
           });
 | 
						|
 | 
						|
           expect(logs).not.toContain(['Skipping @angular/common : es2015 (already compiled).']);
 | 
						|
           expect(loadPackage('@angular/common').__processed_by_ivy_ngcc__).toEqual({
 | 
						|
             es2015: '0.0.0-PLACEHOLDER',
 | 
						|
             fesm2015: '0.0.0-PLACEHOLDER',
 | 
						|
             typings: '0.0.0-PLACEHOLDER',
 | 
						|
           });
 | 
						|
 | 
						|
           // Now, compiling `es2015` should be a no-op.
 | 
						|
           mainNgcc({
 | 
						|
             basePath: '/node_modules/@angular/common',
 | 
						|
             propertiesToConsider: ['es2015'], logger,
 | 
						|
           });
 | 
						|
 | 
						|
           expect(logs).toContain(['Skipping @angular/common : es2015 (already compiled).']);
 | 
						|
         });
 | 
						|
    });
 | 
						|
 | 
						|
    describe('with compileAllFormats set to false', () => {
 | 
						|
      it('should only compile the first matching format', () => {
 | 
						|
        mainNgcc({
 | 
						|
          basePath: '/node_modules',
 | 
						|
          propertiesToConsider: ['module', 'fesm5', 'esm5'],
 | 
						|
          compileAllFormats: false,
 | 
						|
          logger: new MockLogger(),
 | 
						|
        });
 | 
						|
        // * In the Angular packages fesm5 and module have the same underlying format,
 | 
						|
        //   so both are marked as compiled.
 | 
						|
        // * The `esm5` is not compiled because we stopped after the `fesm5` format.
 | 
						|
        expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toEqual({
 | 
						|
          fesm5: '0.0.0-PLACEHOLDER',
 | 
						|
          module: '0.0.0-PLACEHOLDER',
 | 
						|
          typings: '0.0.0-PLACEHOLDER',
 | 
						|
        });
 | 
						|
        expect(loadPackage('@angular/common').__processed_by_ivy_ngcc__).toEqual({
 | 
						|
          fesm5: '0.0.0-PLACEHOLDER',
 | 
						|
          module: '0.0.0-PLACEHOLDER',
 | 
						|
          typings: '0.0.0-PLACEHOLDER',
 | 
						|
        });
 | 
						|
        expect(loadPackage('@angular/common/testing').__processed_by_ivy_ngcc__).toEqual({
 | 
						|
          fesm5: '0.0.0-PLACEHOLDER',
 | 
						|
          module: '0.0.0-PLACEHOLDER',
 | 
						|
          typings: '0.0.0-PLACEHOLDER',
 | 
						|
        });
 | 
						|
        expect(loadPackage('@angular/common/http').__processed_by_ivy_ngcc__).toEqual({
 | 
						|
          fesm5: '0.0.0-PLACEHOLDER',
 | 
						|
          module: '0.0.0-PLACEHOLDER',
 | 
						|
          typings: '0.0.0-PLACEHOLDER',
 | 
						|
        });
 | 
						|
      });
 | 
						|
 | 
						|
      it('should cope with compiling the same entry-point multiple times with different formats',
 | 
						|
         () => {
 | 
						|
           mainNgcc({
 | 
						|
             basePath: '/node_modules',
 | 
						|
             propertiesToConsider: ['module'],
 | 
						|
             compileAllFormats: false,
 | 
						|
             logger: new MockLogger(),
 | 
						|
 | 
						|
           });
 | 
						|
           expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toEqual({
 | 
						|
             fesm5: '0.0.0-PLACEHOLDER',
 | 
						|
             module: '0.0.0-PLACEHOLDER',
 | 
						|
             typings: '0.0.0-PLACEHOLDER',
 | 
						|
           });
 | 
						|
           // If ngcc tries to write out the typings files again, this will throw an exception.
 | 
						|
           mainNgcc({
 | 
						|
             basePath: '/node_modules',
 | 
						|
             propertiesToConsider: ['esm5'],
 | 
						|
             compileAllFormats: false,
 | 
						|
             logger: new MockLogger(),
 | 
						|
           });
 | 
						|
           expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toEqual({
 | 
						|
             esm5: '0.0.0-PLACEHOLDER',
 | 
						|
             fesm5: '0.0.0-PLACEHOLDER',
 | 
						|
             module: '0.0.0-PLACEHOLDER',
 | 
						|
             typings: '0.0.0-PLACEHOLDER',
 | 
						|
           });
 | 
						|
         });
 | 
						|
    });
 | 
						|
 | 
						|
    describe('with createNewEntryPointFormats', () => {
 | 
						|
      it('should create new files rather than overwriting the originals', () => {
 | 
						|
        const ANGULAR_CORE_IMPORT_REGEX = /import \* as ɵngcc\d+ from '@angular\/core';/;
 | 
						|
        mainNgcc({
 | 
						|
          basePath: '/node_modules',
 | 
						|
          createNewEntryPointFormats: true,
 | 
						|
          propertiesToConsider: ['esm5'],
 | 
						|
          logger: new MockLogger(),
 | 
						|
 | 
						|
        });
 | 
						|
 | 
						|
        // Updates the package.json
 | 
						|
        expect(loadPackage('@angular/common').esm5).toEqual('./esm5/common.js');
 | 
						|
        expect((loadPackage('@angular/common') as any).esm5_ivy_ngcc)
 | 
						|
            .toEqual('__ivy_ngcc__/esm5/common.js');
 | 
						|
 | 
						|
        // Doesn't touch original files
 | 
						|
        expect(fs.readFile(_(`/node_modules/@angular/common/esm5/src/common_module.js`)))
 | 
						|
            .not.toMatch(ANGULAR_CORE_IMPORT_REGEX);
 | 
						|
        // Or create a backup of the original
 | 
						|
        expect(
 | 
						|
            fs.exists(_(`/node_modules/@angular/common/esm5/src/common_module.js.__ivy_ngcc_bak`)))
 | 
						|
            .toBe(false);
 | 
						|
 | 
						|
        // Creates new files
 | 
						|
        expect(
 | 
						|
            fs.readFile(_(`/node_modules/@angular/common/__ivy_ngcc__/esm5/src/common_module.js`)))
 | 
						|
            .toMatch(ANGULAR_CORE_IMPORT_REGEX);
 | 
						|
 | 
						|
        // Copies over files (unchanged) that did not need compiling
 | 
						|
        expect(fs.exists(_(`/node_modules/@angular/common/__ivy_ngcc__/esm5/src/version.js`)));
 | 
						|
        expect(fs.readFile(_(`/node_modules/@angular/common/__ivy_ngcc__/esm5/src/version.js`)))
 | 
						|
            .toEqual(fs.readFile(_(`/node_modules/@angular/common/esm5/src/version.js`)));
 | 
						|
 | 
						|
        // Overwrites .d.ts files (as usual)
 | 
						|
        expect(fs.readFile(_(`/node_modules/@angular/common/common.d.ts`)))
 | 
						|
            .toMatch(ANGULAR_CORE_IMPORT_REGEX);
 | 
						|
        expect(fs.exists(_(`/node_modules/@angular/common/common.d.ts.__ivy_ngcc_bak`))).toBe(true);
 | 
						|
      });
 | 
						|
 | 
						|
      it('should update `package.json` for all matching format properties', () => {
 | 
						|
        mainNgcc({
 | 
						|
          basePath: '/node_modules/@angular/core',
 | 
						|
          createNewEntryPointFormats: true,
 | 
						|
          propertiesToConsider: ['fesm2015', 'fesm5'],
 | 
						|
        });
 | 
						|
 | 
						|
        const pkg: any = loadPackage('@angular/core');
 | 
						|
 | 
						|
        // `es2015` is an alias of `fesm2015`.
 | 
						|
        expect(pkg.fesm2015).toEqual('./fesm2015/core.js');
 | 
						|
        expect(pkg.es2015).toEqual('./fesm2015/core.js');
 | 
						|
        expect(pkg.fesm2015_ivy_ngcc).toEqual('__ivy_ngcc__/fesm2015/core.js');
 | 
						|
        expect(pkg.es2015_ivy_ngcc).toEqual('__ivy_ngcc__/fesm2015/core.js');
 | 
						|
 | 
						|
        // `module` is an alias of `fesm5`.
 | 
						|
        expect(pkg.fesm5).toEqual('./fesm5/core.js');
 | 
						|
        expect(pkg.module).toEqual('./fesm5/core.js');
 | 
						|
        expect(pkg.fesm5_ivy_ngcc).toEqual('__ivy_ngcc__/fesm5/core.js');
 | 
						|
        expect(pkg.module_ivy_ngcc).toEqual('__ivy_ngcc__/fesm5/core.js');
 | 
						|
      });
 | 
						|
 | 
						|
      it('should update `package.json` deterministically (regardless of entry-point processing order)',
 | 
						|
         () => {
 | 
						|
           // Ensure formats are not marked as processed in `package.json` at the beginning.
 | 
						|
           let pkg = loadPackage('@angular/core');
 | 
						|
           expectNotToHaveProp(pkg, 'esm5_ivy_ngcc');
 | 
						|
           expectNotToHaveProp(pkg, 'fesm2015_ivy_ngcc');
 | 
						|
           expectNotToHaveProp(pkg, 'fesm5_ivy_ngcc');
 | 
						|
           expectNotToHaveProp(pkg, '__processed_by_ivy_ngcc__');
 | 
						|
 | 
						|
           // Process `fesm2015` and update `package.json`.
 | 
						|
           pkg = processFormatAndUpdatePackageJson('fesm2015');
 | 
						|
           expectNotToHaveProp(pkg, 'esm5_ivy_ngcc');
 | 
						|
           expectToHaveProp(pkg, 'fesm2015_ivy_ngcc');
 | 
						|
           expectNotToHaveProp(pkg, 'fesm5_ivy_ngcc');
 | 
						|
           expectToHaveProp(pkg.__processed_by_ivy_ngcc__ !, 'fesm2015');
 | 
						|
 | 
						|
           // Process `fesm5` and update `package.json`.
 | 
						|
           pkg = processFormatAndUpdatePackageJson('fesm5');
 | 
						|
           expectNotToHaveProp(pkg, 'esm5_ivy_ngcc');
 | 
						|
           expectToHaveProp(pkg, 'fesm2015_ivy_ngcc');
 | 
						|
           expectToHaveProp(pkg, 'fesm5_ivy_ngcc');
 | 
						|
           expectToHaveProp(pkg.__processed_by_ivy_ngcc__ !, 'fesm5');
 | 
						|
 | 
						|
           // Process `esm5` and update `package.json`.
 | 
						|
           pkg = processFormatAndUpdatePackageJson('esm5');
 | 
						|
           expectToHaveProp(pkg, 'esm5_ivy_ngcc');
 | 
						|
           expectToHaveProp(pkg, 'fesm2015_ivy_ngcc');
 | 
						|
           expectToHaveProp(pkg, 'fesm5_ivy_ngcc');
 | 
						|
           expectToHaveProp(pkg.__processed_by_ivy_ngcc__ !, 'esm5');
 | 
						|
 | 
						|
           // Ensure the properties are in deterministic order (regardless of processing order).
 | 
						|
           const pkgKeys = stringifyKeys(pkg);
 | 
						|
           expect(pkgKeys).toContain('|esm5_ivy_ngcc|esm5|');
 | 
						|
           expect(pkgKeys).toContain('|fesm2015_ivy_ngcc|fesm2015|');
 | 
						|
           expect(pkgKeys).toContain('|fesm5_ivy_ngcc|fesm5|');
 | 
						|
 | 
						|
           // NOTE:
 | 
						|
           // Along with the first format that is processed, the typings are processed as well.
 | 
						|
           // Also, once a property has been processed, alias properties as also marked as
 | 
						|
           // processed. Aliases properties are properties that point to the same entry-point file.
 | 
						|
           // For example:
 | 
						|
           // - `fesm2015` <=> `es2015`
 | 
						|
           // - `fesm5` <=> `module`
 | 
						|
           expect(stringifyKeys(pkg.__processed_by_ivy_ngcc__ !))
 | 
						|
               .toBe('|es2015|esm5|fesm2015|fesm5|module|typings|');
 | 
						|
 | 
						|
           // Helpers
 | 
						|
           function expectNotToHaveProp(obj: object, prop: string) {
 | 
						|
             expect(obj.hasOwnProperty(prop))
 | 
						|
                 .toBe(
 | 
						|
                     false,
 | 
						|
                     `Expected object not to have property '${prop}': ${JSON.stringify(obj, null, 2)}`);
 | 
						|
           }
 | 
						|
 | 
						|
           function expectToHaveProp(obj: object, prop: string) {
 | 
						|
             expect(obj.hasOwnProperty(prop))
 | 
						|
                 .toBe(
 | 
						|
                     true,
 | 
						|
                     `Expected object to have property '${prop}': ${JSON.stringify(obj, null, 2)}`);
 | 
						|
           }
 | 
						|
 | 
						|
           function processFormatAndUpdatePackageJson(formatProp: string) {
 | 
						|
             mainNgcc({
 | 
						|
               basePath: '/node_modules/@angular/core',
 | 
						|
               createNewEntryPointFormats: true,
 | 
						|
               propertiesToConsider: [formatProp],
 | 
						|
             });
 | 
						|
             return loadPackage('@angular/core');
 | 
						|
           }
 | 
						|
 | 
						|
           function stringifyKeys(obj: object) { return `|${Object.keys(obj).join('|')}|`; }
 | 
						|
         });
 | 
						|
    });
 | 
						|
 | 
						|
    describe('diagnostics', () => {
 | 
						|
      it('should fail with formatted diagnostics when an error diagnostic is produced', () => {
 | 
						|
        loadTestFiles([
 | 
						|
          {
 | 
						|
            name: _('/node_modules/fatal-error/package.json'),
 | 
						|
            contents: '{"name": "fatal-error", "es2015": "./index.js", "typings": "./index.d.ts"}',
 | 
						|
          },
 | 
						|
          {name: _('/node_modules/fatal-error/index.metadata.json'), contents: 'DUMMY DATA'},
 | 
						|
          {
 | 
						|
            name: _('/node_modules/fatal-error/index.js'),
 | 
						|
            contents: `
 | 
						|
              import {Component} from '@angular/core';
 | 
						|
              export class FatalError {}
 | 
						|
              FatalError.decorators = [
 | 
						|
                {type: Component, args: [{selector: 'fatal-error'}]}
 | 
						|
              ];
 | 
						|
            `,
 | 
						|
          },
 | 
						|
          {
 | 
						|
            name: _('/node_modules/fatal-error/index.d.ts'),
 | 
						|
            contents: `
 | 
						|
              export declare class FatalError {}
 | 
						|
            `,
 | 
						|
          },
 | 
						|
        ]);
 | 
						|
 | 
						|
        try {
 | 
						|
          mainNgcc({
 | 
						|
            basePath: '/node_modules',
 | 
						|
            targetEntryPointPath: 'fatal-error',
 | 
						|
            propertiesToConsider: ['es2015']
 | 
						|
          });
 | 
						|
          fail('should have thrown');
 | 
						|
        } catch (e) {
 | 
						|
          expect(e.message).toContain(
 | 
						|
              'Failed to compile entry-point fatal-error (es2015 as esm2015) due to compilation errors:');
 | 
						|
          expect(e.message).toContain('NG2001');
 | 
						|
          expect(e.message).toContain('component is missing a template');
 | 
						|
        }
 | 
						|
      });
 | 
						|
    });
 | 
						|
 | 
						|
    describe('logger', () => {
 | 
						|
      it('should log info message to the console by default', () => {
 | 
						|
        const consoleInfoSpy = spyOn(console, 'info');
 | 
						|
        mainNgcc({basePath: '/node_modules', propertiesToConsider: ['esm2015']});
 | 
						|
        expect(consoleInfoSpy)
 | 
						|
            .toHaveBeenCalledWith('Compiling @angular/common/http : esm2015 as esm2015');
 | 
						|
      });
 | 
						|
 | 
						|
      it('should use a custom logger if provided', () => {
 | 
						|
        const logger = new MockLogger();
 | 
						|
        mainNgcc({
 | 
						|
          basePath: '/node_modules',
 | 
						|
          propertiesToConsider: ['esm2015'], logger,
 | 
						|
        });
 | 
						|
        expect(logger.logs.info).toContain(['Compiling @angular/common/http : esm2015 as esm2015']);
 | 
						|
      });
 | 
						|
    });
 | 
						|
 | 
						|
    describe('with pathMappings', () => {
 | 
						|
      it('should find and compile packages accessible via the pathMappings', () => {
 | 
						|
        mainNgcc({
 | 
						|
          basePath: '/node_modules',
 | 
						|
          propertiesToConsider: ['es2015'],
 | 
						|
          pathMappings: {paths: {'*': ['dist/*']}, baseUrl: '/'},
 | 
						|
        });
 | 
						|
        expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toEqual({
 | 
						|
          es2015: '0.0.0-PLACEHOLDER',
 | 
						|
          fesm2015: '0.0.0-PLACEHOLDER',
 | 
						|
          typings: '0.0.0-PLACEHOLDER',
 | 
						|
        });
 | 
						|
        expect(loadPackage('local-package', _('/dist')).__processed_by_ivy_ngcc__).toEqual({
 | 
						|
          es2015: '0.0.0-PLACEHOLDER',
 | 
						|
          typings: '0.0.0-PLACEHOLDER',
 | 
						|
        });
 | 
						|
      });
 | 
						|
    });
 | 
						|
 | 
						|
    describe('with configuration files', () => {
 | 
						|
      it('should process a configured deep-import as an entry-point', () => {
 | 
						|
        loadTestFiles([
 | 
						|
          {
 | 
						|
            name: _('/ngcc.config.js'),
 | 
						|
            contents: `module.exports = { packages: {
 | 
						|
            'deep_import': {
 | 
						|
              entryPoints: {
 | 
						|
                './entry_point': { override: { typings: '../entry_point.d.ts', es2015: '../entry_point.js' } }
 | 
						|
              }
 | 
						|
            }
 | 
						|
          }};`,
 | 
						|
          },
 | 
						|
          {
 | 
						|
            name: _('/node_modules/deep_import/package.json'),
 | 
						|
            contents: '{"name": "deep-import", "es2015": "./index.js", "typings": "./index.d.ts"}',
 | 
						|
          },
 | 
						|
          {
 | 
						|
            name: _('/node_modules/deep_import/entry_point.js'),
 | 
						|
            contents: `
 | 
						|
              import {Component} from '@angular/core';
 | 
						|
              @Component({selector: 'entry-point'})
 | 
						|
              export class EntryPoint {}
 | 
						|
            `,
 | 
						|
          },
 | 
						|
          {
 | 
						|
            name: _('/node_modules/deep_import/entry_point.d.ts'),
 | 
						|
            contents: `
 | 
						|
              import {Component} from '@angular/core';
 | 
						|
              @Component({selector: 'entry-point'})
 | 
						|
              export class EntryPoint {}
 | 
						|
            `,
 | 
						|
          },
 | 
						|
        ]);
 | 
						|
        mainNgcc({
 | 
						|
          basePath: '/node_modules',
 | 
						|
          targetEntryPointPath: 'deep_import/entry_point',
 | 
						|
          propertiesToConsider: ['es2015']
 | 
						|
        });
 | 
						|
        // The containing package is not processed
 | 
						|
        expect(loadPackage('deep_import').__processed_by_ivy_ngcc__).toBeUndefined();
 | 
						|
        // But the configured entry-point and its dependency (@angular/core) are processed.
 | 
						|
        expect(loadPackage('deep_import/entry_point').__processed_by_ivy_ngcc__).toEqual({
 | 
						|
          es2015: '0.0.0-PLACEHOLDER',
 | 
						|
          typings: '0.0.0-PLACEHOLDER',
 | 
						|
        });
 | 
						|
        expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toEqual({
 | 
						|
          es2015: '0.0.0-PLACEHOLDER',
 | 
						|
          fesm2015: '0.0.0-PLACEHOLDER',
 | 
						|
          typings: '0.0.0-PLACEHOLDER',
 | 
						|
        });
 | 
						|
      });
 | 
						|
 | 
						|
      it('should not process ignored entry-points', () => {
 | 
						|
        loadTestFiles([
 | 
						|
          {
 | 
						|
            name: _('/ngcc.config.js'),
 | 
						|
            contents: `module.exports = { packages: {
 | 
						|
            '@angular/core': {
 | 
						|
              entryPoints: {
 | 
						|
                './testing': {ignore: true}
 | 
						|
              },
 | 
						|
            },
 | 
						|
            '@angular/common': {
 | 
						|
              entryPoints: {
 | 
						|
                '.': {ignore: true}
 | 
						|
              },
 | 
						|
            }
 | 
						|
          }};`,
 | 
						|
          },
 | 
						|
        ]);
 | 
						|
        mainNgcc({basePath: '/node_modules', propertiesToConsider: ['es2015']});
 | 
						|
        // We process core but not core/testing.
 | 
						|
        expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toEqual({
 | 
						|
          es2015: '0.0.0-PLACEHOLDER',
 | 
						|
          fesm2015: '0.0.0-PLACEHOLDER',
 | 
						|
          typings: '0.0.0-PLACEHOLDER',
 | 
						|
        });
 | 
						|
        expect(loadPackage('@angular/core/testing').__processed_by_ivy_ngcc__).toBeUndefined();
 | 
						|
        // We do not compile common but we do compile its sub-entry-points.
 | 
						|
        expect(loadPackage('@angular/common').__processed_by_ivy_ngcc__).toBeUndefined();
 | 
						|
        expect(loadPackage('@angular/common/http').__processed_by_ivy_ngcc__).toEqual({
 | 
						|
          es2015: '0.0.0-PLACEHOLDER',
 | 
						|
          fesm2015: '0.0.0-PLACEHOLDER',
 | 
						|
          typings: '0.0.0-PLACEHOLDER',
 | 
						|
        });
 | 
						|
      });
 | 
						|
 | 
						|
      it('should support removing a format property by setting it to `undefined`', () => {
 | 
						|
        loadTestFiles([
 | 
						|
          {
 | 
						|
            name: _('/ngcc.config.js'),
 | 
						|
            contents: `
 | 
						|
              module.exports = {
 | 
						|
                packages: {
 | 
						|
                  'test-package': {
 | 
						|
                    entryPoints: {
 | 
						|
                      '.': {
 | 
						|
                        override: {
 | 
						|
                          fesm2015: undefined,
 | 
						|
                        },
 | 
						|
                      },
 | 
						|
                    },
 | 
						|
                  },
 | 
						|
                },
 | 
						|
              };
 | 
						|
            `,
 | 
						|
          },
 | 
						|
          {
 | 
						|
            name: _('/node_modules/test-package/package.json'),
 | 
						|
            contents: `
 | 
						|
              {
 | 
						|
                "name": "test-package",
 | 
						|
                "fesm2015": "./index.es2015.js",
 | 
						|
                "fesm5": "./index.es5.js",
 | 
						|
                "typings": "./index.d.ts"
 | 
						|
              }
 | 
						|
            `,
 | 
						|
          },
 | 
						|
          {
 | 
						|
            name: _('/node_modules/test-package/index.es5.js'),
 | 
						|
            contents: `
 | 
						|
              var TestService = (function () {
 | 
						|
                function TestService() {
 | 
						|
                }
 | 
						|
                return TestService;
 | 
						|
              }());
 | 
						|
            `,
 | 
						|
          },
 | 
						|
          {
 | 
						|
            name: _('/node_modules/test-package/index.d.js'),
 | 
						|
            contents: `
 | 
						|
              export declare class TestService {}
 | 
						|
            `,
 | 
						|
          },
 | 
						|
        ]);
 | 
						|
 | 
						|
        mainNgcc({
 | 
						|
          basePath: '/node_modules',
 | 
						|
          targetEntryPointPath: 'test-package',
 | 
						|
          propertiesToConsider: ['fesm2015', 'fesm5'],
 | 
						|
        });
 | 
						|
 | 
						|
        expect(loadPackage('test-package').__processed_by_ivy_ngcc__).toEqual({
 | 
						|
          fesm5: '0.0.0-PLACEHOLDER',
 | 
						|
          typings: '0.0.0-PLACEHOLDER',
 | 
						|
        });
 | 
						|
      });
 | 
						|
    });
 | 
						|
 | 
						|
    describe('undecorated child class migration', () => {
 | 
						|
      it('should generate a directive definition with CopyDefinitionFeature for an undecorated child directive',
 | 
						|
         () => {
 | 
						|
           compileIntoFlatEs5Package('test-package', {
 | 
						|
             '/index.ts': `
 | 
						|
              import {Directive, NgModule} from '@angular/core';
 | 
						|
 | 
						|
              @Directive({
 | 
						|
                selector: '[base]',
 | 
						|
                exportAs: 'base1, base2',
 | 
						|
              })
 | 
						|
              export class BaseDir {}
 | 
						|
 | 
						|
              export class DerivedDir extends BaseDir {}
 | 
						|
 | 
						|
              @NgModule({
 | 
						|
                declarations: [DerivedDir],
 | 
						|
              })
 | 
						|
              export class Module {}
 | 
						|
            `,
 | 
						|
           });
 | 
						|
 | 
						|
           mainNgcc({
 | 
						|
             basePath: '/node_modules',
 | 
						|
             targetEntryPointPath: 'test-package',
 | 
						|
             propertiesToConsider: ['main'],
 | 
						|
           });
 | 
						|
 | 
						|
 | 
						|
           const jsContents = fs.readFile(_(`/node_modules/test-package/index.js`));
 | 
						|
           expect(jsContents)
 | 
						|
               .toContain(
 | 
						|
                   'DerivedDir.ɵdir = ɵngcc0.ɵɵdefineDirective({ type: DerivedDir, ' +
 | 
						|
                   'selectors: [["", "base", ""]], exportAs: ["base1", "base2"], ' +
 | 
						|
                   'features: [ɵngcc0.ɵɵInheritDefinitionFeature, ɵngcc0.ɵɵCopyDefinitionFeature] });');
 | 
						|
 | 
						|
           const dtsContents = fs.readFile(_(`/node_modules/test-package/index.d.ts`));
 | 
						|
           expect(dtsContents)
 | 
						|
               .toContain(
 | 
						|
                   'static ɵdir: ɵngcc0.ɵɵDirectiveDefWithMeta<DerivedDir, "[base]", ["base1", "base2"], {}, {}, never>;');
 | 
						|
         });
 | 
						|
 | 
						|
      it('should generate a component definition with CopyDefinitionFeature for an undecorated child component',
 | 
						|
         () => {
 | 
						|
           compileIntoFlatEs5Package('test-package', {
 | 
						|
             '/index.ts': `
 | 
						|
           import {Component, NgModule} from '@angular/core';
 | 
						|
 | 
						|
           @Component({
 | 
						|
             selector: '[base]',
 | 
						|
             template: '<span>This is the base template</span>',
 | 
						|
           })
 | 
						|
           export class BaseCmp {}
 | 
						|
 | 
						|
           export class DerivedCmp extends BaseCmp {}
 | 
						|
 | 
						|
           @NgModule({
 | 
						|
             declarations: [DerivedCmp],
 | 
						|
           })
 | 
						|
           export class Module {}
 | 
						|
         `,
 | 
						|
           });
 | 
						|
 | 
						|
           mainNgcc({
 | 
						|
             basePath: '/node_modules',
 | 
						|
             targetEntryPointPath: 'test-package',
 | 
						|
             propertiesToConsider: ['main'],
 | 
						|
           });
 | 
						|
 | 
						|
 | 
						|
           const jsContents = fs.readFile(_(`/node_modules/test-package/index.js`));
 | 
						|
           expect(jsContents).toContain('DerivedCmp.ɵcmp = ɵngcc0.ɵɵdefineComponent');
 | 
						|
           expect(jsContents)
 | 
						|
               .toContain(
 | 
						|
                   'features: [ɵngcc0.ɵɵInheritDefinitionFeature, ɵngcc0.ɵɵCopyDefinitionFeature]');
 | 
						|
 | 
						|
           const dtsContents = fs.readFile(_(`/node_modules/test-package/index.d.ts`));
 | 
						|
           expect(dtsContents)
 | 
						|
               .toContain(
 | 
						|
                   'static ɵcmp: ɵngcc0.ɵɵComponentDefWithMeta<DerivedCmp, "[base]", never, {}, {}, never>;');
 | 
						|
         });
 | 
						|
 | 
						|
      it('should generate directive definitions with CopyDefinitionFeature for undecorated child directives in a long inheritance chain',
 | 
						|
         () => {
 | 
						|
           compileIntoFlatEs5Package('test-package', {
 | 
						|
             '/index.ts': `
 | 
						|
           import {Directive, NgModule} from '@angular/core';
 | 
						|
 | 
						|
           @Directive({
 | 
						|
             selector: '[base]',
 | 
						|
           })
 | 
						|
           export class BaseDir {}
 | 
						|
 | 
						|
           export class DerivedDir1 extends BaseDir {}
 | 
						|
 | 
						|
           export class DerivedDir2 extends DerivedDir1 {}
 | 
						|
 | 
						|
           export class DerivedDir3 extends DerivedDir2 {}
 | 
						|
 | 
						|
           @NgModule({
 | 
						|
             declarations: [DerivedDir3],
 | 
						|
           })
 | 
						|
           export class Module {}
 | 
						|
         `,
 | 
						|
           });
 | 
						|
 | 
						|
           mainNgcc({
 | 
						|
             basePath: '/node_modules',
 | 
						|
             targetEntryPointPath: 'test-package',
 | 
						|
             propertiesToConsider: ['main'],
 | 
						|
           });
 | 
						|
 | 
						|
           const dtsContents = fs.readFile(_(`/node_modules/test-package/index.d.ts`));
 | 
						|
           expect(dtsContents)
 | 
						|
               .toContain(
 | 
						|
                   'static ɵdir: ɵngcc0.ɵɵDirectiveDefWithMeta<DerivedDir1, "[base]", never, {}, {}, never>;');
 | 
						|
           expect(dtsContents)
 | 
						|
               .toContain(
 | 
						|
                   'static ɵdir: ɵngcc0.ɵɵDirectiveDefWithMeta<DerivedDir2, "[base]", never, {}, {}, never>;');
 | 
						|
           expect(dtsContents)
 | 
						|
               .toContain(
 | 
						|
                   'static ɵdir: ɵngcc0.ɵɵDirectiveDefWithMeta<DerivedDir3, "[base]", never, {}, {}, never>;');
 | 
						|
         });
 | 
						|
    });
 | 
						|
 | 
						|
    describe('aliasing re-exports in commonjs', () => {
 | 
						|
      it('should add re-exports to commonjs files', () => {
 | 
						|
        loadTestFiles([
 | 
						|
          {
 | 
						|
            name: _('/node_modules/test-package/package.json'),
 | 
						|
            contents: `
 | 
						|
              {
 | 
						|
                "name": "test-package",
 | 
						|
                "main": "./index.js",
 | 
						|
                "typings": "./index.d.ts"
 | 
						|
              }
 | 
						|
            `,
 | 
						|
          },
 | 
						|
          {
 | 
						|
            name: _('/node_modules/test-package/index.js'),
 | 
						|
            contents: `
 | 
						|
              var __export = null;
 | 
						|
              __export(require("./module"));
 | 
						|
            `,
 | 
						|
          },
 | 
						|
          {
 | 
						|
            name: _('/node_modules/test-package/index.d.ts'),
 | 
						|
            contents: `
 | 
						|
              export * from "./module";
 | 
						|
            `,
 | 
						|
          },
 | 
						|
          {
 | 
						|
            name: _('/node_modules/test-package/index.metadata.json'),
 | 
						|
            contents: '{}',
 | 
						|
          },
 | 
						|
          {
 | 
						|
            name: _('/node_modules/test-package/module.js'),
 | 
						|
            contents: `
 | 
						|
              var __decorate = null;
 | 
						|
              var core_1 = require("@angular/core");
 | 
						|
              var directive_1 = require("./directive");
 | 
						|
              var LocalDir = /** @class */ (function () {
 | 
						|
                  function LocalDir() {
 | 
						|
                  }
 | 
						|
                  LocalDir = __decorate([
 | 
						|
                      core_1.Directive({
 | 
						|
                          selector: '[local]',
 | 
						|
                      })
 | 
						|
                  ], LocalDir);
 | 
						|
                  return LocalDir;
 | 
						|
              }());
 | 
						|
              var FooModule = /** @class */ (function () {
 | 
						|
                  function FooModule() {
 | 
						|
                  }
 | 
						|
                  FooModule = __decorate([
 | 
						|
                      core_1.NgModule({
 | 
						|
                          declarations: [directive_1.Foo, LocalDir],
 | 
						|
                          exports: [directive_1.Foo, LocalDir],
 | 
						|
                      })
 | 
						|
                  ], FooModule);
 | 
						|
                  return FooModule;
 | 
						|
              }());
 | 
						|
              exports.LocalDir = LocalDir;
 | 
						|
              exports.FooModule = FooModule;
 | 
						|
            `,
 | 
						|
          },
 | 
						|
          {
 | 
						|
            name: _('/node_modules/test-package/module.d.ts'),
 | 
						|
            contents: `
 | 
						|
              export declare class LocalDir {}
 | 
						|
              export declare class FooModule {}
 | 
						|
            `,
 | 
						|
          },
 | 
						|
          {
 | 
						|
            name: _('/node_modules/test-package/module.metadata.json'),
 | 
						|
            contents: '{}',
 | 
						|
          },
 | 
						|
          {
 | 
						|
            name: _('/node_modules/test-package/directive.js'),
 | 
						|
            contents: `
 | 
						|
              var __decorate = null;
 | 
						|
              var core_1 = require("@angular/core");
 | 
						|
              var Foo = /** @class */ (function () {
 | 
						|
                  function Foo() {
 | 
						|
                  }
 | 
						|
                  Foo = __decorate([
 | 
						|
                      core_1.Directive({
 | 
						|
                          selector: '[foo]',
 | 
						|
                      })
 | 
						|
                  ], Foo);
 | 
						|
                  return Foo;
 | 
						|
              }());
 | 
						|
              exports.Foo = Foo;
 | 
						|
            `,
 | 
						|
          },
 | 
						|
          {
 | 
						|
            name: _('/node_modules/test-package/directive.d.ts'),
 | 
						|
            contents: `
 | 
						|
              export declare class Foo {}
 | 
						|
            `,
 | 
						|
          },
 | 
						|
          {
 | 
						|
            name: _('/node_modules/test-package/directive.metadata.json'),
 | 
						|
            contents: '{}',
 | 
						|
          },
 | 
						|
          {
 | 
						|
            name: _('/ngcc.config.js'),
 | 
						|
            contents: `
 | 
						|
              module.exports = {
 | 
						|
                packages: {
 | 
						|
                  'test-package': {
 | 
						|
                    entryPoints: {
 | 
						|
                      '.': {
 | 
						|
                        generateDeepReexports: true
 | 
						|
                      },
 | 
						|
                    },
 | 
						|
                  },
 | 
						|
                },
 | 
						|
              };
 | 
						|
            `,
 | 
						|
          }
 | 
						|
        ]);
 | 
						|
 | 
						|
        mainNgcc({
 | 
						|
          basePath: '/node_modules',
 | 
						|
          targetEntryPointPath: 'test-package',
 | 
						|
          propertiesToConsider: ['main'],
 | 
						|
        });
 | 
						|
 | 
						|
        expect(loadPackage('test-package').__processed_by_ivy_ngcc__).toEqual({
 | 
						|
          main: '0.0.0-PLACEHOLDER',
 | 
						|
          typings: '0.0.0-PLACEHOLDER',
 | 
						|
        });
 | 
						|
 | 
						|
        const jsContents = fs.readFile(_(`/node_modules/test-package/module.js`));
 | 
						|
        const dtsContents = fs.readFile(_(`/node_modules/test-package/module.d.ts`));
 | 
						|
        expect(jsContents).toContain(`var ɵngcc1 = require('./directive');`);
 | 
						|
        expect(jsContents).toContain('exports.ɵngExportɵFooModuleɵFoo = ɵngcc1.Foo;');
 | 
						|
        expect(dtsContents)
 | 
						|
            .toContain(`export {Foo as ɵngExportɵFooModuleɵFoo} from './directive';`);
 | 
						|
        expect(dtsContents.match(/ɵngExportɵFooModuleɵFoo/g) !.length).toBe(1);
 | 
						|
        expect(dtsContents).not.toContain(`ɵngExportɵFooModuleɵLocalDir`);
 | 
						|
      });
 | 
						|
    });
 | 
						|
 | 
						|
    describe('legacy message ids', () => {
 | 
						|
      it('should render legacy message ids when compiling i18n tags in component templates', () => {
 | 
						|
        compileIntoApf('test-package', {
 | 
						|
          '/index.ts': `
 | 
						|
           import {Component} from '@angular/core';
 | 
						|
 | 
						|
           @Component({
 | 
						|
             selector: '[base]',
 | 
						|
             template: '<div i18n>Some message</div>'
 | 
						|
           })
 | 
						|
           export class AppComponent {}
 | 
						|
         `,
 | 
						|
        });
 | 
						|
 | 
						|
        mainNgcc({
 | 
						|
          basePath: '/node_modules',
 | 
						|
          targetEntryPointPath: 'test-package',
 | 
						|
          propertiesToConsider: ['esm2015'],
 | 
						|
        });
 | 
						|
 | 
						|
 | 
						|
        const jsContents = fs.readFile(_(`/node_modules/test-package/esm2015/src/index.js`));
 | 
						|
        expect(jsContents)
 | 
						|
            .toContain(
 | 
						|
                '$localize `:␟888aea0e46f7e9dddbd95fc1ef380a3ff70ada9d␟1812794354835616626:Some message');
 | 
						|
      });
 | 
						|
 | 
						|
      it('should not render legacy message ids when compiling i18n tags in component templates if `enableI18nLegacyMessageIdFormat` is false',
 | 
						|
         () => {
 | 
						|
           compileIntoApf('test-package', {
 | 
						|
             '/index.ts': `
 | 
						|
           import {Component} from '@angular/core';
 | 
						|
 | 
						|
           @Component({
 | 
						|
             selector: '[base]',
 | 
						|
             template: '<div i18n>Some message</div>'
 | 
						|
           })
 | 
						|
           export class AppComponent {}
 | 
						|
         `,
 | 
						|
           });
 | 
						|
 | 
						|
           mainNgcc({
 | 
						|
             basePath: '/node_modules',
 | 
						|
             targetEntryPointPath: 'test-package',
 | 
						|
             propertiesToConsider: ['esm2015'],
 | 
						|
             enableI18nLegacyMessageIdFormat: false,
 | 
						|
           });
 | 
						|
 | 
						|
 | 
						|
           const jsContents = fs.readFile(_(`/node_modules/test-package/esm2015/src/index.js`));
 | 
						|
           expect(jsContents).not.toContain('␟888aea0e46f7e9dddbd95fc1ef380a3ff70ada9d');
 | 
						|
           expect(jsContents).not.toContain('␟1812794354835616626');
 | 
						|
           expect(jsContents).not.toContain('␟');
 | 
						|
         });
 | 
						|
    });
 | 
						|
 | 
						|
    function loadPackage(
 | 
						|
        packageName: string, basePath: AbsoluteFsPath = _('/node_modules')): EntryPointPackageJson {
 | 
						|
      return JSON.parse(fs.readFile(fs.resolve(basePath, packageName, 'package.json')));
 | 
						|
    }
 | 
						|
 | 
						|
    function initMockFileSystem(fs: FileSystem, testFiles: Folder) {
 | 
						|
      if (fs instanceof MockFileSystem) {
 | 
						|
        fs.init(testFiles);
 | 
						|
        fs.ensureDir(fs.dirname(new LockFileSync(fs).lockFilePath));
 | 
						|
      }
 | 
						|
 | 
						|
      // a random test package that no metadata.json file so not compiled by Angular.
 | 
						|
      loadTestFiles([
 | 
						|
        {
 | 
						|
          name: _('/node_modules/test-package/package.json'),
 | 
						|
          contents: '{"name": "test-package", "es2015": "./index.js", "typings": "./index.d.ts"}'
 | 
						|
        },
 | 
						|
        {
 | 
						|
          name: _('/node_modules/test-package/index.js'),
 | 
						|
          contents:
 | 
						|
              'import {AppModule} from "@angular/common"; export class MyApp extends AppModule {};'
 | 
						|
        },
 | 
						|
        {
 | 
						|
          name: _('/node_modules/test-package/index.d.ts'),
 | 
						|
          contents:
 | 
						|
              'import {AppModule} from "@angular/common"; export declare class MyApp extends AppModule;'
 | 
						|
        },
 | 
						|
      ]);
 | 
						|
 | 
						|
      // An Angular package that has been built locally and stored in the `dist` directory.
 | 
						|
      loadTestFiles([
 | 
						|
        {
 | 
						|
          name: _('/dist/local-package/package.json'),
 | 
						|
          contents: '{"name": "local-package", "es2015": "./index.js", "typings": "./index.d.ts"}'
 | 
						|
        },
 | 
						|
        {name: _('/dist/local-package/index.metadata.json'), contents: 'DUMMY DATA'},
 | 
						|
        {
 | 
						|
          name: _('/dist/local-package/index.js'),
 | 
						|
          contents:
 | 
						|
              `import {Component} from '@angular/core';\nexport class AppComponent {};\nAppComponent.decorators = [\n{ type: Component, args: [{selector: 'app', template: '<h2>Hello</h2>'}] }\n];`
 | 
						|
        },
 | 
						|
        {
 | 
						|
          name: _('/dist/local-package/index.d.ts'),
 | 
						|
          contents: `export declare class AppComponent {};`
 | 
						|
        },
 | 
						|
      ]);
 | 
						|
 | 
						|
      // An Angular package that has a missing dependency
 | 
						|
      loadTestFiles([
 | 
						|
        {
 | 
						|
          name: _('/node_modules/invalid-package/package.json'),
 | 
						|
          contents: '{"name": "invalid-package", "es2015": "./index.js", "typings": "./index.d.ts"}'
 | 
						|
        },
 | 
						|
        {
 | 
						|
          name: _('/node_modules/invalid-package/index.js'),
 | 
						|
          contents: `
 | 
						|
          import {AppModule} from "@angular/missing";
 | 
						|
          import {Component} from '@angular/core';
 | 
						|
          export class AppComponent {};
 | 
						|
          AppComponent.decorators = [
 | 
						|
            { type: Component, args: [{selector: 'app', template: '<h2>Hello</h2>'}] }
 | 
						|
          ];
 | 
						|
          `
 | 
						|
        },
 | 
						|
        {
 | 
						|
          name: _('/node_modules/invalid-package/index.d.ts'),
 | 
						|
          contents: `export declare class AppComponent {}`
 | 
						|
        },
 | 
						|
        {name: _('/node_modules/invalid-package/index.metadata.json'), contents: 'DUMMY DATA'},
 | 
						|
      ]);
 | 
						|
    }
 | 
						|
  });
 | 
						|
});
 | 
						|
 | 
						|
function countOccurrences(haystack: string, needle: string): number {
 | 
						|
  const matches = haystack.match(new RegExp(needle, 'g'));
 | 
						|
  return matches !== null ? matches.length : 0;
 | 
						|
}
 |