|
|
@ -54,17 +54,8 @@ function setup(files: TestFile[], dtsFiles?: TestFile[]) {
|
|
|
|
};
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
runInEachFileSystem(() => {
|
|
|
|
const PROGRAM_CONTENT: Record<string, string> = {
|
|
|
|
describe('EsmRenderingFormatter', () => {
|
|
|
|
'top-level': `
|
|
|
|
let _: typeof absoluteFrom;
|
|
|
|
|
|
|
|
let PROGRAM: TestFile;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
beforeEach(() => {
|
|
|
|
|
|
|
|
_ = absoluteFrom;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PROGRAM = {
|
|
|
|
|
|
|
|
name: _('/node_modules/test-package/some/file.js'),
|
|
|
|
|
|
|
|
contents: `
|
|
|
|
|
|
|
|
/* A copyright notice */
|
|
|
|
/* A copyright notice */
|
|
|
|
import 'some-side-effect';
|
|
|
|
import 'some-side-effect';
|
|
|
|
import {Directive} from '@angular/core';
|
|
|
|
import {Directive} from '@angular/core';
|
|
|
@ -97,245 +88,298 @@ function compileNgModuleFactory__POST_R3__(injector, options, moduleType) {
|
|
|
|
ngDevMode && assertNgModuleType(moduleType);
|
|
|
|
ngDevMode && assertNgModuleType(moduleType);
|
|
|
|
return Promise.resolve(new R3NgModuleFactory(moduleType));
|
|
|
|
return Promise.resolve(new R3NgModuleFactory(moduleType));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Some other content`
|
|
|
|
// Some other content`,
|
|
|
|
};
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
describe('addImports', () => {
|
|
|
|
|
|
|
|
it('should insert the given imports after existing imports of the source file', () => {
|
|
|
|
'iife-wrapped': `
|
|
|
|
const {renderer, sourceFile} = setup([PROGRAM]);
|
|
|
|
/* A copyright notice */
|
|
|
|
const output = new MagicString(PROGRAM.contents);
|
|
|
|
import 'some-side-effect';
|
|
|
|
renderer.addImports(
|
|
|
|
import {Directive} from '@angular/core';
|
|
|
|
output,
|
|
|
|
let A = /** @class */ (() => {
|
|
|
|
[
|
|
|
|
class A {}
|
|
|
|
{specifier: '@angular/core', qualifier: 'i0'},
|
|
|
|
A.decorators = [
|
|
|
|
{specifier: '@angular/common', qualifier: 'i1'}
|
|
|
|
{ type: Directive, args: [{ selector: '[a]' }] },
|
|
|
|
],
|
|
|
|
{ type: OtherA }
|
|
|
|
sourceFile);
|
|
|
|
];
|
|
|
|
expect(output.toString()).toContain(`/* A copyright notice */
|
|
|
|
return A;
|
|
|
|
|
|
|
|
})();
|
|
|
|
|
|
|
|
let B = /** @class */ (() => {
|
|
|
|
|
|
|
|
class B {}
|
|
|
|
|
|
|
|
B.decorators = [
|
|
|
|
|
|
|
|
{ type: OtherB },
|
|
|
|
|
|
|
|
{ type: Directive, args: [{ selector: '[b]' }] }
|
|
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
return B;
|
|
|
|
|
|
|
|
})();
|
|
|
|
|
|
|
|
var C_1;
|
|
|
|
|
|
|
|
let C = C_1 = /** @class */ (() => {
|
|
|
|
|
|
|
|
class C {}
|
|
|
|
|
|
|
|
C.decorators = [
|
|
|
|
|
|
|
|
{ type: Directive, args: [{ selector: '[c]' }] },
|
|
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
return C;
|
|
|
|
|
|
|
|
})();
|
|
|
|
|
|
|
|
export A, B, C;
|
|
|
|
|
|
|
|
let compileNgModuleFactory = compileNgModuleFactory__PRE_R3__;
|
|
|
|
|
|
|
|
let badlyFormattedVariable = __PRE_R3__badlyFormattedVariable;
|
|
|
|
|
|
|
|
function compileNgModuleFactory__PRE_R3__(injector, options, moduleType) {
|
|
|
|
|
|
|
|
const compilerFactory = injector.get(CompilerFactory);
|
|
|
|
|
|
|
|
const compiler = compilerFactory.createCompiler([options]);
|
|
|
|
|
|
|
|
return compiler.compileModuleAsync(moduleType);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
function compileNgModuleFactory__POST_R3__(injector, options, moduleType) {
|
|
|
|
|
|
|
|
ngDevMode && assertNgModuleType(moduleType);
|
|
|
|
|
|
|
|
return Promise.resolve(new R3NgModuleFactory(moduleType));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Some other content`
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
runInEachFileSystem(() => {
|
|
|
|
|
|
|
|
['top-level', 'iife-wrapped'].forEach(classLayout => {
|
|
|
|
|
|
|
|
describe(`EsmRenderingFormatter {${classLayout} classes}`, () => {
|
|
|
|
|
|
|
|
let _: typeof absoluteFrom;
|
|
|
|
|
|
|
|
let PROGRAM: TestFile;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
beforeEach(() => {
|
|
|
|
|
|
|
|
_ = absoluteFrom;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PROGRAM = {
|
|
|
|
|
|
|
|
name: _('/node_modules/test-package/some/file.js'),
|
|
|
|
|
|
|
|
contents: PROGRAM_CONTENT[classLayout],
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
describe('addImports', () => {
|
|
|
|
|
|
|
|
it('should insert the given imports after existing imports of the source file', () => {
|
|
|
|
|
|
|
|
const {renderer, sourceFile} = setup([PROGRAM]);
|
|
|
|
|
|
|
|
const output = new MagicString(PROGRAM.contents);
|
|
|
|
|
|
|
|
renderer.addImports(
|
|
|
|
|
|
|
|
output,
|
|
|
|
|
|
|
|
[
|
|
|
|
|
|
|
|
{specifier: '@angular/core', qualifier: 'i0'},
|
|
|
|
|
|
|
|
{specifier: '@angular/common', qualifier: 'i1'}
|
|
|
|
|
|
|
|
],
|
|
|
|
|
|
|
|
sourceFile);
|
|
|
|
|
|
|
|
expect(output.toString()).toContain(`/* A copyright notice */
|
|
|
|
import 'some-side-effect';
|
|
|
|
import 'some-side-effect';
|
|
|
|
import {Directive} from '@angular/core';
|
|
|
|
import {Directive} from '@angular/core';
|
|
|
|
import * as i0 from '@angular/core';
|
|
|
|
import * as i0 from '@angular/core';
|
|
|
|
import * as i1 from '@angular/common';`);
|
|
|
|
import * as i1 from '@angular/common';`);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
it('should leave the file unchanged if there are no imports to add', () => {
|
|
|
|
|
|
|
|
const {renderer, sourceFile} = setup([PROGRAM]);
|
|
|
|
|
|
|
|
const output = new MagicString(PROGRAM.contents);
|
|
|
|
|
|
|
|
const contentsBefore = output.toString();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
renderer.addImports(output, [], sourceFile);
|
|
|
|
|
|
|
|
const contentsAfter = output.toString();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
expect(contentsAfter).toBe(contentsBefore);
|
|
|
|
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('should leave the file unchanged if there are no imports to add', () => {
|
|
|
|
describe('addExports', () => {
|
|
|
|
const {renderer, sourceFile} = setup([PROGRAM]);
|
|
|
|
it('should insert the given exports at the end of the source file', () => {
|
|
|
|
const output = new MagicString(PROGRAM.contents);
|
|
|
|
const {importManager, renderer, sourceFile} = setup([PROGRAM]);
|
|
|
|
const contentsBefore = output.toString();
|
|
|
|
const output = new MagicString(PROGRAM.contents);
|
|
|
|
|
|
|
|
renderer.addExports(
|
|
|
|
renderer.addImports(output, [], sourceFile);
|
|
|
|
output, _(PROGRAM.name.replace(/\.js$/, '')),
|
|
|
|
const contentsAfter = output.toString();
|
|
|
|
[
|
|
|
|
|
|
|
|
{
|
|
|
|
expect(contentsAfter).toBe(contentsBefore);
|
|
|
|
from: _('/node_modules/test-package/some/a.js'),
|
|
|
|
});
|
|
|
|
dtsFrom: _('/node_modules/test-package/some/a.d.ts'),
|
|
|
|
});
|
|
|
|
identifier: 'ComponentA1'
|
|
|
|
|
|
|
|
},
|
|
|
|
describe('addExports', () => {
|
|
|
|
{
|
|
|
|
it('should insert the given exports at the end of the source file', () => {
|
|
|
|
from: _('/node_modules/test-package/some/a.js'),
|
|
|
|
const {importManager, renderer, sourceFile} = setup([PROGRAM]);
|
|
|
|
dtsFrom: _('/node_modules/test-package/some/a.d.ts'),
|
|
|
|
const output = new MagicString(PROGRAM.contents);
|
|
|
|
identifier: 'ComponentA2'
|
|
|
|
renderer.addExports(
|
|
|
|
},
|
|
|
|
output, _(PROGRAM.name.replace(/\.js$/, '')),
|
|
|
|
{
|
|
|
|
[
|
|
|
|
from: _('/node_modules/test-package/some/foo/b.js'),
|
|
|
|
{
|
|
|
|
dtsFrom: _('/node_modules/test-package/some/foo/b.d.ts'),
|
|
|
|
from: _('/node_modules/test-package/some/a.js'),
|
|
|
|
identifier: 'ComponentB'
|
|
|
|
dtsFrom: _('/node_modules/test-package/some/a.d.ts'),
|
|
|
|
},
|
|
|
|
identifier: 'ComponentA1'
|
|
|
|
{from: PROGRAM.name, dtsFrom: PROGRAM.name, identifier: 'TopLevelComponent'},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
{
|
|
|
|
importManager, sourceFile);
|
|
|
|
from: _('/node_modules/test-package/some/a.js'),
|
|
|
|
expect(output.toString()).toContain(`
|
|
|
|
dtsFrom: _('/node_modules/test-package/some/a.d.ts'),
|
|
|
|
|
|
|
|
identifier: 'ComponentA2'
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
from: _('/node_modules/test-package/some/foo/b.js'),
|
|
|
|
|
|
|
|
dtsFrom: _('/node_modules/test-package/some/foo/b.d.ts'),
|
|
|
|
|
|
|
|
identifier: 'ComponentB'
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{from: PROGRAM.name, dtsFrom: PROGRAM.name, identifier: 'TopLevelComponent'},
|
|
|
|
|
|
|
|
],
|
|
|
|
|
|
|
|
importManager, sourceFile);
|
|
|
|
|
|
|
|
expect(output.toString()).toContain(`
|
|
|
|
|
|
|
|
// Some other content
|
|
|
|
// Some other content
|
|
|
|
export {ComponentA1} from './a';
|
|
|
|
export {ComponentA1} from './a';
|
|
|
|
export {ComponentA2} from './a';
|
|
|
|
export {ComponentA2} from './a';
|
|
|
|
export {ComponentB} from './foo/b';
|
|
|
|
export {ComponentB} from './foo/b';
|
|
|
|
export {TopLevelComponent};`);
|
|
|
|
export {TopLevelComponent};`);
|
|
|
|
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
describe('addConstants', () => {
|
|
|
|
describe('addConstants', () => {
|
|
|
|
it('should insert the given constants after imports in the source file', () => {
|
|
|
|
it('should insert the given constants after imports in the source file', () => {
|
|
|
|
const {renderer, program} = setup([PROGRAM]);
|
|
|
|
const {renderer, program} = setup([PROGRAM]);
|
|
|
|
const file = getSourceFileOrError(program, _('/node_modules/test-package/some/file.js'));
|
|
|
|
const file = getSourceFileOrError(program, _('/node_modules/test-package/some/file.js'));
|
|
|
|
const output = new MagicString(PROGRAM.contents);
|
|
|
|
const output = new MagicString(PROGRAM.contents);
|
|
|
|
renderer.addConstants(output, 'const x = 3;', file);
|
|
|
|
renderer.addConstants(output, 'const x = 3;', file);
|
|
|
|
expect(output.toString()).toContain(`
|
|
|
|
expect(output.toString()).toContain(`
|
|
|
|
import {Directive} from '@angular/core';
|
|
|
|
import {Directive} from '@angular/core';
|
|
|
|
|
|
|
|
|
|
|
|
const x = 3;
|
|
|
|
const x = 3;
|
|
|
|
export class A {}`);
|
|
|
|
`);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('should insert constants after inserted imports', () => {
|
|
|
|
it('should insert constants after inserted imports', () => {
|
|
|
|
const {renderer, program} = setup([PROGRAM]);
|
|
|
|
const {renderer, program} = setup([PROGRAM]);
|
|
|
|
const file = getSourceFileOrError(program, _('/node_modules/test-package/some/file.js'));
|
|
|
|
const file = getSourceFileOrError(program, _('/node_modules/test-package/some/file.js'));
|
|
|
|
const output = new MagicString(PROGRAM.contents);
|
|
|
|
const output = new MagicString(PROGRAM.contents);
|
|
|
|
renderer.addConstants(output, 'const x = 3;', file);
|
|
|
|
renderer.addConstants(output, 'const x = 3;', file);
|
|
|
|
renderer.addImports(output, [{specifier: '@angular/core', qualifier: 'i0'}], file);
|
|
|
|
renderer.addImports(output, [{specifier: '@angular/core', qualifier: 'i0'}], file);
|
|
|
|
expect(output.toString()).toContain(`
|
|
|
|
expect(output.toString()).toContain(`
|
|
|
|
import {Directive} from '@angular/core';
|
|
|
|
import {Directive} from '@angular/core';
|
|
|
|
import * as i0 from '@angular/core';
|
|
|
|
import * as i0 from '@angular/core';
|
|
|
|
|
|
|
|
|
|
|
|
const x = 3;
|
|
|
|
const x = 3;
|
|
|
|
export class A {`);
|
|
|
|
`);
|
|
|
|
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
describe('rewriteSwitchableDeclarations', () => {
|
|
|
|
describe('rewriteSwitchableDeclarations', () => {
|
|
|
|
it('should switch marked declaration initializers', () => {
|
|
|
|
it('should switch marked declaration initializers', () => {
|
|
|
|
const {renderer, program, switchMarkerAnalyses, sourceFile} = setup([PROGRAM]);
|
|
|
|
const {renderer, program, switchMarkerAnalyses, sourceFile} = setup([PROGRAM]);
|
|
|
|
const file = getSourceFileOrError(program, _('/node_modules/test-package/some/file.js'));
|
|
|
|
const file = getSourceFileOrError(program, _('/node_modules/test-package/some/file.js'));
|
|
|
|
const output = new MagicString(PROGRAM.contents);
|
|
|
|
const output = new MagicString(PROGRAM.contents);
|
|
|
|
renderer.rewriteSwitchableDeclarations(
|
|
|
|
renderer.rewriteSwitchableDeclarations(
|
|
|
|
output, file, switchMarkerAnalyses.get(sourceFile)!.declarations);
|
|
|
|
output, file, switchMarkerAnalyses.get(sourceFile)!.declarations);
|
|
|
|
expect(output.toString())
|
|
|
|
expect(output.toString())
|
|
|
|
.not.toContain(`let compileNgModuleFactory = compileNgModuleFactory__PRE_R3__;`);
|
|
|
|
.not.toContain(`let compileNgModuleFactory = compileNgModuleFactory__PRE_R3__;`);
|
|
|
|
expect(output.toString())
|
|
|
|
expect(output.toString())
|
|
|
|
.toContain(`let badlyFormattedVariable = __PRE_R3__badlyFormattedVariable;`);
|
|
|
|
.toContain(`let badlyFormattedVariable = __PRE_R3__badlyFormattedVariable;`);
|
|
|
|
expect(output.toString())
|
|
|
|
expect(output.toString())
|
|
|
|
.toContain(`let compileNgModuleFactory = compileNgModuleFactory__POST_R3__;`);
|
|
|
|
.toContain(`let compileNgModuleFactory = compileNgModuleFactory__POST_R3__;`);
|
|
|
|
expect(output.toString())
|
|
|
|
expect(output.toString())
|
|
|
|
.toContain(
|
|
|
|
.toContain(
|
|
|
|
`function compileNgModuleFactory__PRE_R3__(injector, options, moduleType) {`);
|
|
|
|
`function compileNgModuleFactory__PRE_R3__(injector, options, moduleType) {`);
|
|
|
|
expect(output.toString())
|
|
|
|
expect(output.toString())
|
|
|
|
.toContain(
|
|
|
|
.toContain(
|
|
|
|
`function compileNgModuleFactory__POST_R3__(injector, options, moduleType) {`);
|
|
|
|
`function compileNgModuleFactory__POST_R3__(injector, options, moduleType) {`);
|
|
|
|
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
describe('addDefinitions', () => {
|
|
|
|
describe('addDefinitions', () => {
|
|
|
|
it('should insert the definitions directly after the class declaration', () => {
|
|
|
|
it('should insert the definitions directly after the class declaration', () => {
|
|
|
|
const {renderer, decorationAnalyses, sourceFile} = setup([PROGRAM]);
|
|
|
|
const {renderer, decorationAnalyses, sourceFile} = setup([PROGRAM]);
|
|
|
|
const output = new MagicString(PROGRAM.contents);
|
|
|
|
const output = new MagicString(PROGRAM.contents);
|
|
|
|
const compiledClass =
|
|
|
|
const compiledClass =
|
|
|
|
decorationAnalyses.get(sourceFile)!.compiledClasses.find(c => c.name === 'A')!;
|
|
|
|
decorationAnalyses.get(sourceFile)!.compiledClasses.find(c => c.name === 'A')!;
|
|
|
|
renderer.addDefinitions(output, compiledClass, 'SOME DEFINITION TEXT');
|
|
|
|
renderer.addDefinitions(output, compiledClass, 'SOME DEFINITION TEXT');
|
|
|
|
expect(output.toString()).toContain(`
|
|
|
|
expect(output.toString()).toContain(`class A {}
|
|
|
|
export class A {}
|
|
|
|
|
|
|
|
SOME DEFINITION TEXT
|
|
|
|
SOME DEFINITION TEXT
|
|
|
|
A.decorators = [
|
|
|
|
A.decorators = [
|
|
|
|
`);
|
|
|
|
`);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('should insert the definitions after the variable declaration of class expressions',
|
|
|
|
it('should insert the definitions after the variable declaration of class expressions',
|
|
|
|
() => {
|
|
|
|
|
|
|
|
const {renderer, decorationAnalyses, sourceFile} = setup([PROGRAM]);
|
|
|
|
|
|
|
|
const output = new MagicString(PROGRAM.contents);
|
|
|
|
|
|
|
|
const compiledClass =
|
|
|
|
|
|
|
|
decorationAnalyses.get(sourceFile)!.compiledClasses.find(c => c.name === 'C')!;
|
|
|
|
|
|
|
|
renderer.addDefinitions(output, compiledClass, 'SOME DEFINITION TEXT');
|
|
|
|
|
|
|
|
expect(output.toString()).toContain(`
|
|
|
|
|
|
|
|
let C = C_1 = class C {};
|
|
|
|
|
|
|
|
SOME DEFINITION TEXT
|
|
|
|
|
|
|
|
C.decorators = [
|
|
|
|
|
|
|
|
`);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
describe('addAdjacentStatements', () => {
|
|
|
|
|
|
|
|
const contents = `import {Directive, NgZone, Console} from '@angular/core';\n` +
|
|
|
|
|
|
|
|
`export class SomeDirective {\n` +
|
|
|
|
|
|
|
|
` constructor(zone, cons) {}\n` +
|
|
|
|
|
|
|
|
` method() {}\n` +
|
|
|
|
|
|
|
|
`}\n` +
|
|
|
|
|
|
|
|
`SomeDirective.decorators = [\n` +
|
|
|
|
|
|
|
|
` { type: Directive, args: [{ selector: '[a]' }] },\n` +
|
|
|
|
|
|
|
|
` { type: OtherA }\n` +
|
|
|
|
|
|
|
|
`];\n` +
|
|
|
|
|
|
|
|
`SomeDirective.ctorParameters = () => [\n` +
|
|
|
|
|
|
|
|
` { type: NgZone },\n` +
|
|
|
|
|
|
|
|
` { type: Console }\n` +
|
|
|
|
|
|
|
|
`];`;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
it('should insert the statements after all the static methods of the class', () => {
|
|
|
|
|
|
|
|
const program = {name: _('/node_modules/test-package/some/file.js'), contents};
|
|
|
|
|
|
|
|
const {renderer, decorationAnalyses, sourceFile} = setup([program]);
|
|
|
|
|
|
|
|
const output = new MagicString(contents);
|
|
|
|
|
|
|
|
const compiledClass = decorationAnalyses.get(sourceFile)!.compiledClasses.find(
|
|
|
|
|
|
|
|
c => c.name === 'SomeDirective')!;
|
|
|
|
|
|
|
|
renderer.addAdjacentStatements(output, compiledClass, 'SOME STATEMENTS');
|
|
|
|
|
|
|
|
expect(output.toString())
|
|
|
|
|
|
|
|
.toContain(
|
|
|
|
|
|
|
|
`SomeDirective.ctorParameters = () => [\n` +
|
|
|
|
|
|
|
|
` { type: NgZone },\n` +
|
|
|
|
|
|
|
|
` { type: Console }\n` +
|
|
|
|
|
|
|
|
`];\n` +
|
|
|
|
|
|
|
|
`SOME STATEMENTS`);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
it('should insert the statements after any definitions', () => {
|
|
|
|
|
|
|
|
const program = {name: _('/node_modules/test-package/some/file.js'), contents};
|
|
|
|
|
|
|
|
const {renderer, decorationAnalyses, sourceFile} = setup([program]);
|
|
|
|
|
|
|
|
const output = new MagicString(contents);
|
|
|
|
|
|
|
|
const compiledClass = decorationAnalyses.get(sourceFile)!.compiledClasses.find(
|
|
|
|
|
|
|
|
c => c.name === 'SomeDirective')!;
|
|
|
|
|
|
|
|
renderer.addDefinitions(output, compiledClass, 'SOME DEFINITIONS');
|
|
|
|
|
|
|
|
renderer.addAdjacentStatements(output, compiledClass, 'SOME STATEMENTS');
|
|
|
|
|
|
|
|
const definitionsPosition = output.toString().indexOf('SOME DEFINITIONS');
|
|
|
|
|
|
|
|
const statementsPosition = output.toString().indexOf('SOME STATEMENTS');
|
|
|
|
|
|
|
|
expect(definitionsPosition).not.toEqual(-1, 'definitions should exist');
|
|
|
|
|
|
|
|
expect(statementsPosition).not.toEqual(-1, 'statements should exist');
|
|
|
|
|
|
|
|
expect(statementsPosition).toBeGreaterThan(definitionsPosition);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
describe('removeDecorators', () => {
|
|
|
|
|
|
|
|
describe('[static property declaration]', () => {
|
|
|
|
|
|
|
|
it('should delete the decorator (and following comma) that was matched in the analysis',
|
|
|
|
|
|
|
|
() => {
|
|
|
|
() => {
|
|
|
|
const {decorationAnalyses, sourceFile, renderer} = setup([PROGRAM]);
|
|
|
|
const {renderer, decorationAnalyses, sourceFile} = setup([PROGRAM]);
|
|
|
|
const output = new MagicString(PROGRAM.contents);
|
|
|
|
const output = new MagicString(PROGRAM.contents);
|
|
|
|
const compiledClass =
|
|
|
|
const compiledClass =
|
|
|
|
decorationAnalyses.get(sourceFile)!.compiledClasses.find(c => c.name === 'A')!;
|
|
|
|
decorationAnalyses.get(sourceFile)!.compiledClasses.find(c => c.name === 'C')!;
|
|
|
|
const decorator = compiledClass.decorators![0];
|
|
|
|
renderer.addDefinitions(output, compiledClass, 'SOME DEFINITION TEXT');
|
|
|
|
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
|
|
|
|
|
|
|
decoratorsToRemove.set(decorator.node!.parent!, [decorator.node!]);
|
|
|
|
|
|
|
|
renderer.removeDecorators(output, decoratorsToRemove);
|
|
|
|
|
|
|
|
expect(output.toString())
|
|
|
|
expect(output.toString())
|
|
|
|
.not.toContain(`{ type: Directive, args: [{ selector: '[a]' }] },`);
|
|
|
|
.toMatch(/class C \{\};?\nSOME DEFINITION TEXT\nC.decorators = \[/);
|
|
|
|
expect(output.toString()).toContain(`{ type: OtherA }`);
|
|
|
|
|
|
|
|
expect(output.toString())
|
|
|
|
|
|
|
|
.toContain(`{ type: Directive, args: [{ selector: '[b]' }] }`);
|
|
|
|
|
|
|
|
expect(output.toString()).toContain(`{ type: OtherB }`);
|
|
|
|
|
|
|
|
expect(output.toString())
|
|
|
|
|
|
|
|
.toContain(`{ type: Directive, args: [{ selector: '[c]' }] }`);
|
|
|
|
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
describe('addAdjacentStatements', () => {
|
|
|
|
|
|
|
|
const contents = `import {Directive, NgZone, Console} from '@angular/core';\n` +
|
|
|
|
|
|
|
|
`export class SomeDirective {\n` +
|
|
|
|
|
|
|
|
` constructor(zone, cons) {}\n` +
|
|
|
|
|
|
|
|
` method() {}\n` +
|
|
|
|
|
|
|
|
`}\n` +
|
|
|
|
|
|
|
|
`SomeDirective.decorators = [\n` +
|
|
|
|
|
|
|
|
` { type: Directive, args: [{ selector: '[a]' }] },\n` +
|
|
|
|
|
|
|
|
` { type: OtherA }\n` +
|
|
|
|
|
|
|
|
`];\n` +
|
|
|
|
|
|
|
|
`SomeDirective.ctorParameters = () => [\n` +
|
|
|
|
|
|
|
|
` { type: NgZone },\n` +
|
|
|
|
|
|
|
|
` { type: Console }\n` +
|
|
|
|
|
|
|
|
`];`;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
it('should insert the statements after all the static methods of the class', () => {
|
|
|
|
|
|
|
|
const program = {name: _('/node_modules/test-package/some/file.js'), contents};
|
|
|
|
|
|
|
|
const {renderer, decorationAnalyses, sourceFile} = setup([program]);
|
|
|
|
|
|
|
|
const output = new MagicString(contents);
|
|
|
|
|
|
|
|
const compiledClass = decorationAnalyses.get(sourceFile)!.compiledClasses.find(
|
|
|
|
|
|
|
|
c => c.name === 'SomeDirective')!;
|
|
|
|
|
|
|
|
renderer.addAdjacentStatements(output, compiledClass, 'SOME STATEMENTS');
|
|
|
|
|
|
|
|
expect(output.toString())
|
|
|
|
|
|
|
|
.toContain(
|
|
|
|
|
|
|
|
`SomeDirective.ctorParameters = () => [\n` +
|
|
|
|
|
|
|
|
` { type: NgZone },\n` +
|
|
|
|
|
|
|
|
` { type: Console }\n` +
|
|
|
|
|
|
|
|
`];\n` +
|
|
|
|
|
|
|
|
`SOME STATEMENTS`);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
it('should insert the statements after any definitions', () => {
|
|
|
|
|
|
|
|
const program = {name: _('/node_modules/test-package/some/file.js'), contents};
|
|
|
|
|
|
|
|
const {renderer, decorationAnalyses, sourceFile} = setup([program]);
|
|
|
|
|
|
|
|
const output = new MagicString(contents);
|
|
|
|
|
|
|
|
const compiledClass = decorationAnalyses.get(sourceFile)!.compiledClasses.find(
|
|
|
|
|
|
|
|
c => c.name === 'SomeDirective')!;
|
|
|
|
|
|
|
|
renderer.addDefinitions(output, compiledClass, 'SOME DEFINITIONS');
|
|
|
|
|
|
|
|
renderer.addAdjacentStatements(output, compiledClass, 'SOME STATEMENTS');
|
|
|
|
|
|
|
|
const definitionsPosition = output.toString().indexOf('SOME DEFINITIONS');
|
|
|
|
|
|
|
|
const statementsPosition = output.toString().indexOf('SOME STATEMENTS');
|
|
|
|
|
|
|
|
expect(definitionsPosition).not.toEqual(-1, 'definitions should exist');
|
|
|
|
|
|
|
|
expect(statementsPosition).not.toEqual(-1, 'statements should exist');
|
|
|
|
|
|
|
|
expect(statementsPosition).toBeGreaterThan(definitionsPosition);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
describe('removeDecorators', () => {
|
|
|
|
|
|
|
|
describe('[static property declaration]', () => {
|
|
|
|
|
|
|
|
it('should delete the decorator (and following comma) that was matched in the analysis',
|
|
|
|
|
|
|
|
() => {
|
|
|
|
|
|
|
|
const {decorationAnalyses, sourceFile, renderer} = setup([PROGRAM]);
|
|
|
|
|
|
|
|
const output = new MagicString(PROGRAM.contents);
|
|
|
|
|
|
|
|
const compiledClass =
|
|
|
|
|
|
|
|
decorationAnalyses.get(sourceFile)!.compiledClasses.find(c => c.name === 'A')!;
|
|
|
|
|
|
|
|
const decorator = compiledClass.decorators![0];
|
|
|
|
|
|
|
|
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
|
|
|
|
|
|
|
decoratorsToRemove.set(decorator.node!.parent!, [decorator.node!]);
|
|
|
|
|
|
|
|
renderer.removeDecorators(output, decoratorsToRemove);
|
|
|
|
|
|
|
|
expect(output.toString())
|
|
|
|
|
|
|
|
.not.toContain(`{ type: Directive, args: [{ selector: '[a]' }] },`);
|
|
|
|
|
|
|
|
expect(output.toString()).toContain(`{ type: OtherA }`);
|
|
|
|
|
|
|
|
expect(output.toString())
|
|
|
|
|
|
|
|
.toContain(`{ type: Directive, args: [{ selector: '[b]' }] }`);
|
|
|
|
|
|
|
|
expect(output.toString()).toContain(`{ type: OtherB }`);
|
|
|
|
|
|
|
|
expect(output.toString())
|
|
|
|
|
|
|
|
.toContain(`{ type: Directive, args: [{ selector: '[c]' }] }`);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
it('should delete the decorator (but cope with no trailing comma) that was matched in the analysis',
|
|
|
|
it('should delete the decorator (but cope with no trailing comma) that was matched in the analysis',
|
|
|
|
() => {
|
|
|
|
() => {
|
|
|
|
const {decorationAnalyses, sourceFile, renderer} = setup([PROGRAM]);
|
|
|
|
const {decorationAnalyses, sourceFile, renderer} = setup([PROGRAM]);
|
|
|
|
const output = new MagicString(PROGRAM.contents);
|
|
|
|
const output = new MagicString(PROGRAM.contents);
|
|
|
|
const compiledClass =
|
|
|
|
const compiledClass =
|
|
|
|
decorationAnalyses.get(sourceFile)!.compiledClasses.find(c => c.name === 'B')!;
|
|
|
|
decorationAnalyses.get(sourceFile)!.compiledClasses.find(c => c.name === 'B')!;
|
|
|
|
const decorator = compiledClass.decorators![0];
|
|
|
|
const decorator = compiledClass.decorators![0];
|
|
|
|
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
|
|
|
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
|
|
|
decoratorsToRemove.set(decorator.node!.parent!, [decorator.node!]);
|
|
|
|
decoratorsToRemove.set(decorator.node!.parent!, [decorator.node!]);
|
|
|
|
renderer.removeDecorators(output, decoratorsToRemove);
|
|
|
|
renderer.removeDecorators(output, decoratorsToRemove);
|
|
|
|
expect(output.toString())
|
|
|
|
expect(output.toString())
|
|
|
|
.toContain(`{ type: Directive, args: [{ selector: '[a]' }] },`);
|
|
|
|
.toContain(`{ type: Directive, args: [{ selector: '[a]' }] },`);
|
|
|
|
expect(output.toString()).toContain(`{ type: OtherA }`);
|
|
|
|
expect(output.toString()).toContain(`{ type: OtherA }`);
|
|
|
|
expect(output.toString())
|
|
|
|
expect(output.toString())
|
|
|
|
.not.toContain(`{ type: Directive, args: [{ selector: '[b]' }] }`);
|
|
|
|
.not.toContain(`{ type: Directive, args: [{ selector: '[b]' }] }`);
|
|
|
|
expect(output.toString()).toContain(`{ type: OtherB }`);
|
|
|
|
expect(output.toString()).toContain(`{ type: OtherB }`);
|
|
|
|
expect(output.toString())
|
|
|
|
expect(output.toString())
|
|
|
|
.toContain(`{ type: Directive, args: [{ selector: '[c]' }] }`);
|
|
|
|
.toContain(`{ type: Directive, args: [{ selector: '[c]' }] }`);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('should handle a decorator with a trailing comment', () => {
|
|
|
|
it('should handle a decorator with a trailing comment', () => {
|
|
|
|
const text = `
|
|
|
|
const text = `
|
|
|
|
import {Directive} from '@angular/core';
|
|
|
|
import {Directive} from '@angular/core';
|
|
|
|
export class A {}
|
|
|
|
export class A {}
|
|
|
|
A.decorators = [
|
|
|
|
A.decorators = [
|
|
|
@ -343,50 +387,50 @@ A.decorators = [
|
|
|
|
{ type: OtherA }
|
|
|
|
{ type: OtherA }
|
|
|
|
];
|
|
|
|
];
|
|
|
|
`;
|
|
|
|
`;
|
|
|
|
const file = {name: _('/node_modules/test-package/index.js'), contents: text};
|
|
|
|
const file = {name: _('/node_modules/test-package/index.js'), contents: text};
|
|
|
|
const {decorationAnalyses, sourceFile, renderer} = setup([file]);
|
|
|
|
const {decorationAnalyses, sourceFile, renderer} = setup([file]);
|
|
|
|
const output = new MagicString(text);
|
|
|
|
const output = new MagicString(text);
|
|
|
|
const compiledClass =
|
|
|
|
const compiledClass =
|
|
|
|
decorationAnalyses.get(sourceFile)!.compiledClasses.find(c => c.name === 'A')!;
|
|
|
|
decorationAnalyses.get(sourceFile)!.compiledClasses.find(c => c.name === 'A')!;
|
|
|
|
const decorator = compiledClass.decorators![0];
|
|
|
|
const decorator = compiledClass.decorators![0];
|
|
|
|
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
|
|
|
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
|
|
|
decoratorsToRemove.set(decorator.node!.parent!, [decorator.node!]);
|
|
|
|
decoratorsToRemove.set(decorator.node!.parent!, [decorator.node!]);
|
|
|
|
renderer.removeDecorators(output, decoratorsToRemove);
|
|
|
|
renderer.removeDecorators(output, decoratorsToRemove);
|
|
|
|
// The decorator should have been removed correctly.
|
|
|
|
// The decorator should have been removed correctly.
|
|
|
|
expect(output.toString()).toContain('A.decorators = [ { type: OtherA }');
|
|
|
|
expect(output.toString()).toContain('A.decorators = [ { type: OtherA }');
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
it('should delete the decorator (and its container if there are no other decorators left) that was matched in the analysis',
|
|
|
|
|
|
|
|
() => {
|
|
|
|
|
|
|
|
const {decorationAnalyses, sourceFile, renderer} = setup([PROGRAM]);
|
|
|
|
|
|
|
|
const output = new MagicString(PROGRAM.contents);
|
|
|
|
|
|
|
|
const compiledClass =
|
|
|
|
|
|
|
|
decorationAnalyses.get(sourceFile)!.compiledClasses.find(c => c.name === 'C')!;
|
|
|
|
|
|
|
|
const decorator = compiledClass.decorators![0];
|
|
|
|
|
|
|
|
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
|
|
|
|
|
|
|
decoratorsToRemove.set(decorator.node!.parent!, [decorator.node!]);
|
|
|
|
|
|
|
|
renderer.removeDecorators(output, decoratorsToRemove);
|
|
|
|
|
|
|
|
expect(output.toString())
|
|
|
|
|
|
|
|
.toContain(`{ type: Directive, args: [{ selector: '[a]' }] },`);
|
|
|
|
|
|
|
|
expect(output.toString()).toContain(`{ type: OtherA }`);
|
|
|
|
|
|
|
|
expect(output.toString())
|
|
|
|
|
|
|
|
.toContain(`{ type: Directive, args: [{ selector: '[b]' }] }`);
|
|
|
|
|
|
|
|
expect(output.toString()).toContain(`{ type: OtherB }`);
|
|
|
|
|
|
|
|
expect(output.toString())
|
|
|
|
|
|
|
|
.not.toContain(`{ type: Directive, args: [{ selector: '[c]' }] }`);
|
|
|
|
|
|
|
|
expect(output.toString()).not.toContain(`C.decorators = [`);
|
|
|
|
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
it('should delete the decorator (and its container if there are no other decorators left) that was matched in the analysis',
|
|
|
|
|
|
|
|
() => {
|
|
|
|
|
|
|
|
const {decorationAnalyses, sourceFile, renderer} = setup([PROGRAM]);
|
|
|
|
|
|
|
|
const output = new MagicString(PROGRAM.contents);
|
|
|
|
|
|
|
|
const compiledClass =
|
|
|
|
|
|
|
|
decorationAnalyses.get(sourceFile)!.compiledClasses.find(c => c.name === 'C')!;
|
|
|
|
|
|
|
|
const decorator = compiledClass.decorators![0];
|
|
|
|
|
|
|
|
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
|
|
|
|
|
|
|
decoratorsToRemove.set(decorator.node!.parent!, [decorator.node!]);
|
|
|
|
|
|
|
|
renderer.removeDecorators(output, decoratorsToRemove);
|
|
|
|
|
|
|
|
expect(output.toString())
|
|
|
|
|
|
|
|
.toContain(`{ type: Directive, args: [{ selector: '[a]' }] },`);
|
|
|
|
|
|
|
|
expect(output.toString()).toContain(`{ type: OtherA }`);
|
|
|
|
|
|
|
|
expect(output.toString())
|
|
|
|
|
|
|
|
.toContain(`{ type: Directive, args: [{ selector: '[b]' }] }`);
|
|
|
|
|
|
|
|
expect(output.toString()).toContain(`{ type: OtherB }`);
|
|
|
|
|
|
|
|
expect(output.toString())
|
|
|
|
|
|
|
|
.not.toContain(`{ type: Directive, args: [{ selector: '[c]' }] }`);
|
|
|
|
|
|
|
|
expect(output.toString()).not.toContain(`C.decorators = [`);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
describe('[__decorate declarations]', () => {
|
|
|
|
describe('[__decorate declarations]', () => {
|
|
|
|
let PROGRAM_DECORATE_HELPER: TestFile;
|
|
|
|
let PROGRAM_DECORATE_HELPER: TestFile;
|
|
|
|
|
|
|
|
|
|
|
|
beforeEach(() => {
|
|
|
|
beforeEach(() => {
|
|
|
|
PROGRAM_DECORATE_HELPER = {
|
|
|
|
PROGRAM_DECORATE_HELPER = {
|
|
|
|
name: _('/node_modules/test-package/some/file.js'),
|
|
|
|
name: _('/node_modules/test-package/some/file.js'),
|
|
|
|
contents: `
|
|
|
|
contents: `
|
|
|
|
import * as tslib_1 from "tslib";
|
|
|
|
import * as tslib_1 from "tslib";
|
|
|
|
var D_1;
|
|
|
|
var D_1;
|
|
|
|
/* A copyright notice */
|
|
|
|
/* A copyright notice */
|
|
|
@ -420,72 +464,72 @@ D = D_1 = tslib_1.__decorate([
|
|
|
|
], D);
|
|
|
|
], D);
|
|
|
|
export { D };
|
|
|
|
export { D };
|
|
|
|
// Some other content`
|
|
|
|
// Some other content`
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
it('should delete the decorator (and following comma) that was matched in the analysis',
|
|
|
|
|
|
|
|
() => {
|
|
|
|
|
|
|
|
const {renderer, decorationAnalyses, sourceFile} = setup([PROGRAM_DECORATE_HELPER]);
|
|
|
|
|
|
|
|
const output = new MagicString(PROGRAM_DECORATE_HELPER.contents);
|
|
|
|
|
|
|
|
const compiledClass =
|
|
|
|
|
|
|
|
decorationAnalyses.get(sourceFile)!.compiledClasses.find(c => c.name === 'A')!;
|
|
|
|
|
|
|
|
const decorator = compiledClass.decorators!.find(d => d.name === 'Directive')!;
|
|
|
|
|
|
|
|
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
|
|
|
|
|
|
|
decoratorsToRemove.set(decorator.node!.parent!, [decorator.node!]);
|
|
|
|
|
|
|
|
renderer.removeDecorators(output, decoratorsToRemove);
|
|
|
|
|
|
|
|
expect(output.toString()).not.toContain(`Directive({ selector: '[a]' }),`);
|
|
|
|
|
|
|
|
expect(output.toString()).toContain(`OtherA()`);
|
|
|
|
|
|
|
|
expect(output.toString()).toContain(`Directive({ selector: '[b]' })`);
|
|
|
|
|
|
|
|
expect(output.toString()).toContain(`OtherB()`);
|
|
|
|
|
|
|
|
expect(output.toString()).toContain(`Directive({ selector: '[c]' })`);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
it('should delete the decorator (but cope with no trailing comma) that was matched in the analysis',
|
|
|
|
|
|
|
|
() => {
|
|
|
|
|
|
|
|
const {renderer, decorationAnalyses, sourceFile} = setup([PROGRAM_DECORATE_HELPER]);
|
|
|
|
|
|
|
|
const output = new MagicString(PROGRAM_DECORATE_HELPER.contents);
|
|
|
|
|
|
|
|
const compiledClass =
|
|
|
|
|
|
|
|
decorationAnalyses.get(sourceFile)!.compiledClasses.find(c => c.name === 'B')!;
|
|
|
|
|
|
|
|
const decorator = compiledClass.decorators!.find(d => d.name === 'Directive')!;
|
|
|
|
|
|
|
|
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
|
|
|
|
|
|
|
decoratorsToRemove.set(decorator.node!.parent!, [decorator.node!]);
|
|
|
|
|
|
|
|
renderer.removeDecorators(output, decoratorsToRemove);
|
|
|
|
|
|
|
|
expect(output.toString()).toContain(`Directive({ selector: '[a]' }),`);
|
|
|
|
|
|
|
|
expect(output.toString()).toContain(`OtherA()`);
|
|
|
|
|
|
|
|
expect(output.toString()).not.toContain(`Directive({ selector: '[b]' })`);
|
|
|
|
|
|
|
|
expect(output.toString()).toContain(`OtherB()`);
|
|
|
|
|
|
|
|
expect(output.toString()).toContain(`Directive({ selector: '[c]' })`);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
it('should delete the decorator (and its container if there are not other decorators left) that was matched in the analysis',
|
|
|
|
|
|
|
|
() => {
|
|
|
|
|
|
|
|
const {renderer, decorationAnalyses, sourceFile} = setup([PROGRAM_DECORATE_HELPER]);
|
|
|
|
|
|
|
|
const output = new MagicString(PROGRAM_DECORATE_HELPER.contents);
|
|
|
|
|
|
|
|
const compiledClass =
|
|
|
|
|
|
|
|
decorationAnalyses.get(sourceFile)!.compiledClasses.find(c => c.name === 'C')!;
|
|
|
|
|
|
|
|
const decorator = compiledClass.decorators!.find(d => d.name === 'Directive')!;
|
|
|
|
|
|
|
|
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
|
|
|
|
|
|
|
decoratorsToRemove.set(decorator.node!.parent!, [decorator.node!]);
|
|
|
|
|
|
|
|
renderer.removeDecorators(output, decoratorsToRemove);
|
|
|
|
|
|
|
|
expect(output.toString()).toContain(`Directive({ selector: '[a]' }),`);
|
|
|
|
|
|
|
|
expect(output.toString()).toContain(`OtherA()`);
|
|
|
|
|
|
|
|
expect(output.toString()).toContain(`Directive({ selector: '[b]' })`);
|
|
|
|
|
|
|
|
expect(output.toString()).toContain(`OtherB()`);
|
|
|
|
|
|
|
|
expect(output.toString()).not.toContain(`Directive({ selector: '[c]' })`);
|
|
|
|
|
|
|
|
expect(output.toString()).not.toContain(`C = tslib_1.__decorate([`);
|
|
|
|
|
|
|
|
expect(output.toString()).toContain(`let C = class C {\n};\nexport { C };`);
|
|
|
|
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('should delete the decorator (and following comma) that was matched in the analysis',
|
|
|
|
describe('addModuleWithProvidersParams', () => {
|
|
|
|
() => {
|
|
|
|
let MODULE_WITH_PROVIDERS_PROGRAM: TestFile[];
|
|
|
|
const {renderer, decorationAnalyses, sourceFile} = setup([PROGRAM_DECORATE_HELPER]);
|
|
|
|
let MODULE_WITH_PROVIDERS_DTS_PROGRAM: TestFile[];
|
|
|
|
const output = new MagicString(PROGRAM_DECORATE_HELPER.contents);
|
|
|
|
beforeEach(() => {
|
|
|
|
const compiledClass =
|
|
|
|
MODULE_WITH_PROVIDERS_PROGRAM = [
|
|
|
|
decorationAnalyses.get(sourceFile)!.compiledClasses.find(c => c.name === 'A')!;
|
|
|
|
{
|
|
|
|
const decorator = compiledClass.decorators!.find(d => d.name === 'Directive')!;
|
|
|
|
name: _('/node_modules/test-package/src/index.js'),
|
|
|
|
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
|
|
|
contents: `
|
|
|
|
decoratorsToRemove.set(decorator.node!.parent!, [decorator.node!]);
|
|
|
|
|
|
|
|
renderer.removeDecorators(output, decoratorsToRemove);
|
|
|
|
|
|
|
|
expect(output.toString()).not.toContain(`Directive({ selector: '[a]' }),`);
|
|
|
|
|
|
|
|
expect(output.toString()).toContain(`OtherA()`);
|
|
|
|
|
|
|
|
expect(output.toString()).toContain(`Directive({ selector: '[b]' })`);
|
|
|
|
|
|
|
|
expect(output.toString()).toContain(`OtherB()`);
|
|
|
|
|
|
|
|
expect(output.toString()).toContain(`Directive({ selector: '[c]' })`);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
it('should delete the decorator (but cope with no trailing comma) that was matched in the analysis',
|
|
|
|
|
|
|
|
() => {
|
|
|
|
|
|
|
|
const {renderer, decorationAnalyses, sourceFile} = setup([PROGRAM_DECORATE_HELPER]);
|
|
|
|
|
|
|
|
const output = new MagicString(PROGRAM_DECORATE_HELPER.contents);
|
|
|
|
|
|
|
|
const compiledClass =
|
|
|
|
|
|
|
|
decorationAnalyses.get(sourceFile)!.compiledClasses.find(c => c.name === 'B')!;
|
|
|
|
|
|
|
|
const decorator = compiledClass.decorators!.find(d => d.name === 'Directive')!;
|
|
|
|
|
|
|
|
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
|
|
|
|
|
|
|
decoratorsToRemove.set(decorator.node!.parent!, [decorator.node!]);
|
|
|
|
|
|
|
|
renderer.removeDecorators(output, decoratorsToRemove);
|
|
|
|
|
|
|
|
expect(output.toString()).toContain(`Directive({ selector: '[a]' }),`);
|
|
|
|
|
|
|
|
expect(output.toString()).toContain(`OtherA()`);
|
|
|
|
|
|
|
|
expect(output.toString()).not.toContain(`Directive({ selector: '[b]' })`);
|
|
|
|
|
|
|
|
expect(output.toString()).toContain(`OtherB()`);
|
|
|
|
|
|
|
|
expect(output.toString()).toContain(`Directive({ selector: '[c]' })`);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
it('should delete the decorator (and its container if there are not other decorators left) that was matched in the analysis',
|
|
|
|
|
|
|
|
() => {
|
|
|
|
|
|
|
|
const {renderer, decorationAnalyses, sourceFile} = setup([PROGRAM_DECORATE_HELPER]);
|
|
|
|
|
|
|
|
const output = new MagicString(PROGRAM_DECORATE_HELPER.contents);
|
|
|
|
|
|
|
|
const compiledClass =
|
|
|
|
|
|
|
|
decorationAnalyses.get(sourceFile)!.compiledClasses.find(c => c.name === 'C')!;
|
|
|
|
|
|
|
|
const decorator = compiledClass.decorators!.find(d => d.name === 'Directive')!;
|
|
|
|
|
|
|
|
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
|
|
|
|
|
|
|
decoratorsToRemove.set(decorator.node!.parent!, [decorator.node!]);
|
|
|
|
|
|
|
|
renderer.removeDecorators(output, decoratorsToRemove);
|
|
|
|
|
|
|
|
expect(output.toString()).toContain(`Directive({ selector: '[a]' }),`);
|
|
|
|
|
|
|
|
expect(output.toString()).toContain(`OtherA()`);
|
|
|
|
|
|
|
|
expect(output.toString()).toContain(`Directive({ selector: '[b]' })`);
|
|
|
|
|
|
|
|
expect(output.toString()).toContain(`OtherB()`);
|
|
|
|
|
|
|
|
expect(output.toString()).not.toContain(`Directive({ selector: '[c]' })`);
|
|
|
|
|
|
|
|
expect(output.toString()).not.toContain(`C = tslib_1.__decorate([`);
|
|
|
|
|
|
|
|
expect(output.toString()).toContain(`let C = class C {\n};\nexport { C };`);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
describe('addModuleWithProvidersParams', () => {
|
|
|
|
|
|
|
|
let MODULE_WITH_PROVIDERS_PROGRAM: TestFile[];
|
|
|
|
|
|
|
|
let MODULE_WITH_PROVIDERS_DTS_PROGRAM: TestFile[];
|
|
|
|
|
|
|
|
beforeEach(() => {
|
|
|
|
|
|
|
|
MODULE_WITH_PROVIDERS_PROGRAM = [
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
name: _('/node_modules/test-package/src/index.js'),
|
|
|
|
|
|
|
|
contents: `
|
|
|
|
|
|
|
|
import {ExternalModule} from './module';
|
|
|
|
import {ExternalModule} from './module';
|
|
|
|
import {LibraryModule} from 'some-library';
|
|
|
|
import {LibraryModule} from 'some-library';
|
|
|
|
export class SomeClass {}
|
|
|
|
export class SomeClass {}
|
|
|
@ -509,25 +553,25 @@ export { D };
|
|
|
|
export function withProviders8() { return {ngModule: SomeModule}; }
|
|
|
|
export function withProviders8() { return {ngModule: SomeModule}; }
|
|
|
|
export {ExternalModule} from './module';
|
|
|
|
export {ExternalModule} from './module';
|
|
|
|
`
|
|
|
|
`
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
{
|
|
|
|
name: _('/node_modules/test-package/src/module.js'),
|
|
|
|
name: _('/node_modules/test-package/src/module.js'),
|
|
|
|
contents: `
|
|
|
|
contents: `
|
|
|
|
export class ExternalModule {
|
|
|
|
export class ExternalModule {
|
|
|
|
static withProviders1() { return {ngModule: ExternalModule}; }
|
|
|
|
static withProviders1() { return {ngModule: ExternalModule}; }
|
|
|
|
static withProviders2() { return {ngModule: ExternalModule}; }
|
|
|
|
static withProviders2() { return {ngModule: ExternalModule}; }
|
|
|
|
}`
|
|
|
|
}`
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
{
|
|
|
|
name: _('/node_modules/some-library/index.d.ts'),
|
|
|
|
name: _('/node_modules/some-library/index.d.ts'),
|
|
|
|
contents: 'export declare class LibraryModule {}'
|
|
|
|
contents: 'export declare class LibraryModule {}'
|
|
|
|
},
|
|
|
|
},
|
|
|
|
];
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
MODULE_WITH_PROVIDERS_DTS_PROGRAM = [
|
|
|
|
MODULE_WITH_PROVIDERS_DTS_PROGRAM = [
|
|
|
|
{
|
|
|
|
{
|
|
|
|
name: _('/node_modules/test-package/typings/index.d.ts'),
|
|
|
|
name: _('/node_modules/test-package/typings/index.d.ts'),
|
|
|
|
contents: `
|
|
|
|
contents: `
|
|
|
|
import {ModuleWithProviders} from '@angular/core';
|
|
|
|
import {ModuleWithProviders} from '@angular/core';
|
|
|
|
export declare class SomeClass {}
|
|
|
|
export declare class SomeClass {}
|
|
|
|
export interface MyModuleWithProviders extends ModuleWithProviders {}
|
|
|
|
export interface MyModuleWithProviders extends ModuleWithProviders {}
|
|
|
@ -551,41 +595,41 @@ export { D };
|
|
|
|
export declare function withProviders8(): MyModuleWithProviders;
|
|
|
|
export declare function withProviders8(): MyModuleWithProviders;
|
|
|
|
export {ExternalModule} from './module';
|
|
|
|
export {ExternalModule} from './module';
|
|
|
|
`
|
|
|
|
`
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
{
|
|
|
|
name: _('/node_modules/test-package/typings/module.d.ts'),
|
|
|
|
name: _('/node_modules/test-package/typings/module.d.ts'),
|
|
|
|
contents: `
|
|
|
|
contents: `
|
|
|
|
export interface ModuleWithProviders {}
|
|
|
|
export interface ModuleWithProviders {}
|
|
|
|
export declare class ExternalModule {
|
|
|
|
export declare class ExternalModule {
|
|
|
|
static withProviders1(): ModuleWithProviders;
|
|
|
|
static withProviders1(): ModuleWithProviders;
|
|
|
|
static withProviders2(): ModuleWithProviders;
|
|
|
|
static withProviders2(): ModuleWithProviders;
|
|
|
|
}`
|
|
|
|
}`
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
{
|
|
|
|
name: _('/node_modules/some-library/index.d.ts'),
|
|
|
|
name: _('/node_modules/some-library/index.d.ts'),
|
|
|
|
contents: 'export declare class LibraryModule {}'
|
|
|
|
contents: 'export declare class LibraryModule {}'
|
|
|
|
},
|
|
|
|
},
|
|
|
|
];
|
|
|
|
];
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('should fixup functions/methods that return ModuleWithProviders structures', () => {
|
|
|
|
it('should fixup functions/methods that return ModuleWithProviders structures', () => {
|
|
|
|
const {bundle, renderer, host} =
|
|
|
|
const {bundle, renderer, host} =
|
|
|
|
setup(MODULE_WITH_PROVIDERS_PROGRAM, MODULE_WITH_PROVIDERS_DTS_PROGRAM);
|
|
|
|
setup(MODULE_WITH_PROVIDERS_PROGRAM, MODULE_WITH_PROVIDERS_DTS_PROGRAM);
|
|
|
|
|
|
|
|
|
|
|
|
const referencesRegistry = new NgccReferencesRegistry(host);
|
|
|
|
const referencesRegistry = new NgccReferencesRegistry(host);
|
|
|
|
const moduleWithProvidersAnalyses =
|
|
|
|
const moduleWithProvidersAnalyses =
|
|
|
|
new ModuleWithProvidersAnalyzer(
|
|
|
|
new ModuleWithProvidersAnalyzer(
|
|
|
|
host, bundle.src.program.getTypeChecker(), referencesRegistry, true)
|
|
|
|
host, bundle.src.program.getTypeChecker(), referencesRegistry, true)
|
|
|
|
.analyzeProgram(bundle.src.program);
|
|
|
|
.analyzeProgram(bundle.src.program);
|
|
|
|
const typingsFile = getSourceFileOrError(
|
|
|
|
const typingsFile = getSourceFileOrError(
|
|
|
|
bundle.dts!.program, _('/node_modules/test-package/typings/index.d.ts'));
|
|
|
|
bundle.dts!.program, _('/node_modules/test-package/typings/index.d.ts'));
|
|
|
|
const moduleWithProvidersInfo = moduleWithProvidersAnalyses.get(typingsFile)!;
|
|
|
|
const moduleWithProvidersInfo = moduleWithProvidersAnalyses.get(typingsFile)!;
|
|
|
|
|
|
|
|
|
|
|
|
const output = new MagicString(MODULE_WITH_PROVIDERS_DTS_PROGRAM[0].contents);
|
|
|
|
const output = new MagicString(MODULE_WITH_PROVIDERS_DTS_PROGRAM[0].contents);
|
|
|
|
const importManager = new ImportManager(new NoopImportRewriter(), 'i');
|
|
|
|
const importManager = new ImportManager(new NoopImportRewriter(), 'i');
|
|
|
|
renderer.addModuleWithProvidersParams(output, moduleWithProvidersInfo, importManager);
|
|
|
|
renderer.addModuleWithProvidersParams(output, moduleWithProvidersInfo, importManager);
|
|
|
|
|
|
|
|
|
|
|
|
expect(output.toString()).toContain(`
|
|
|
|
expect(output.toString()).toContain(`
|
|
|
|
static withProviders1(): ModuleWithProviders<SomeModule>;
|
|
|
|
static withProviders1(): ModuleWithProviders<SomeModule>;
|
|
|
|
static withProviders2(): ModuleWithProviders<SomeModule>;
|
|
|
|
static withProviders2(): ModuleWithProviders<SomeModule>;
|
|
|
|
static withProviders3(): ModuleWithProviders<SomeClass>;
|
|
|
|
static withProviders3(): ModuleWithProviders<SomeClass>;
|
|
|
@ -594,7 +638,7 @@ export { D };
|
|
|
|
static withProviders6(): ModuleWithProviders<i2.LibraryModule>;
|
|
|
|
static withProviders6(): ModuleWithProviders<i2.LibraryModule>;
|
|
|
|
static withProviders7(): ({ngModule: SomeModule, providers: any[]})&{ngModule:SomeModule};
|
|
|
|
static withProviders7(): ({ngModule: SomeModule, providers: any[]})&{ngModule:SomeModule};
|
|
|
|
static withProviders8(): (MyModuleWithProviders)&{ngModule:SomeModule};`);
|
|
|
|
static withProviders8(): (MyModuleWithProviders)&{ngModule:SomeModule};`);
|
|
|
|
expect(output.toString()).toContain(`
|
|
|
|
expect(output.toString()).toContain(`
|
|
|
|
export declare function withProviders1(): ModuleWithProviders<SomeModule>;
|
|
|
|
export declare function withProviders1(): ModuleWithProviders<SomeModule>;
|
|
|
|
export declare function withProviders2(): ModuleWithProviders<SomeModule>;
|
|
|
|
export declare function withProviders2(): ModuleWithProviders<SomeModule>;
|
|
|
|
export declare function withProviders3(): ModuleWithProviders<SomeClass>;
|
|
|
|
export declare function withProviders3(): ModuleWithProviders<SomeClass>;
|
|
|
@ -603,42 +647,44 @@ export { D };
|
|
|
|
export declare function withProviders6(): ModuleWithProviders<i2.LibraryModule>;
|
|
|
|
export declare function withProviders6(): ModuleWithProviders<i2.LibraryModule>;
|
|
|
|
export declare function withProviders7(): ({ngModule: SomeModule, providers: any[]})&{ngModule:SomeModule};
|
|
|
|
export declare function withProviders7(): ({ngModule: SomeModule, providers: any[]})&{ngModule:SomeModule};
|
|
|
|
export declare function withProviders8(): (MyModuleWithProviders)&{ngModule:SomeModule};`);
|
|
|
|
export declare function withProviders8(): (MyModuleWithProviders)&{ngModule:SomeModule};`);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('should not mistake `ModuleWithProviders` types that are not imported from `@angular/core',
|
|
|
|
it('should not mistake `ModuleWithProviders` types that are not imported from `@angular/core',
|
|
|
|
() => {
|
|
|
|
() => {
|
|
|
|
const {bundle, renderer, host} =
|
|
|
|
const {bundle, renderer, host} =
|
|
|
|
setup(MODULE_WITH_PROVIDERS_PROGRAM, MODULE_WITH_PROVIDERS_DTS_PROGRAM);
|
|
|
|
setup(MODULE_WITH_PROVIDERS_PROGRAM, MODULE_WITH_PROVIDERS_DTS_PROGRAM);
|
|
|
|
|
|
|
|
|
|
|
|
const referencesRegistry = new NgccReferencesRegistry(host);
|
|
|
|
const referencesRegistry = new NgccReferencesRegistry(host);
|
|
|
|
const moduleWithProvidersAnalyses =
|
|
|
|
const moduleWithProvidersAnalyses =
|
|
|
|
new ModuleWithProvidersAnalyzer(
|
|
|
|
new ModuleWithProvidersAnalyzer(
|
|
|
|
host, bundle.src.program.getTypeChecker(), referencesRegistry, true)
|
|
|
|
host, bundle.src.program.getTypeChecker(), referencesRegistry, true)
|
|
|
|
.analyzeProgram(bundle.src.program);
|
|
|
|
.analyzeProgram(bundle.src.program);
|
|
|
|
const typingsFile = getSourceFileOrError(
|
|
|
|
const typingsFile = getSourceFileOrError(
|
|
|
|
bundle.dts!.program, _('/node_modules/test-package/typings/module.d.ts'));
|
|
|
|
bundle.dts!.program, _('/node_modules/test-package/typings/module.d.ts'));
|
|
|
|
const moduleWithProvidersInfo = moduleWithProvidersAnalyses.get(typingsFile)!;
|
|
|
|
const moduleWithProvidersInfo = moduleWithProvidersAnalyses.get(typingsFile)!;
|
|
|
|
|
|
|
|
|
|
|
|
const output = new MagicString(MODULE_WITH_PROVIDERS_DTS_PROGRAM[1].contents);
|
|
|
|
const output = new MagicString(MODULE_WITH_PROVIDERS_DTS_PROGRAM[1].contents);
|
|
|
|
const importManager = new ImportManager(new NoopImportRewriter(), 'i');
|
|
|
|
const importManager = new ImportManager(new NoopImportRewriter(), 'i');
|
|
|
|
renderer.addModuleWithProvidersParams(output, moduleWithProvidersInfo, importManager);
|
|
|
|
renderer.addModuleWithProvidersParams(output, moduleWithProvidersInfo, importManager);
|
|
|
|
expect(output.toString()).toContain(`
|
|
|
|
expect(output.toString()).toContain(`
|
|
|
|
static withProviders1(): (ModuleWithProviders)&{ngModule:ExternalModule};
|
|
|
|
static withProviders1(): (ModuleWithProviders)&{ngModule:ExternalModule};
|
|
|
|
static withProviders2(): (ModuleWithProviders)&{ngModule:ExternalModule};`);
|
|
|
|
static withProviders2(): (ModuleWithProviders)&{ngModule:ExternalModule};`);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
describe('printStatement', () => {
|
|
|
|
describe('printStatement', () => {
|
|
|
|
it('should transpile code to ES2015', () => {
|
|
|
|
it('should transpile code to ES2015', () => {
|
|
|
|
const {renderer, sourceFile, importManager} = setup([PROGRAM]);
|
|
|
|
const {renderer, sourceFile, importManager} = setup([PROGRAM]);
|
|
|
|
|
|
|
|
|
|
|
|
const stmt1 = new DeclareVarStmt('foo', new LiteralExpr(42), null, [StmtModifier.Final]);
|
|
|
|
const stmt1 = new DeclareVarStmt('foo', new LiteralExpr(42), null, [StmtModifier.Final]);
|
|
|
|
const stmt2 = new DeclareVarStmt('bar', new LiteralExpr(true));
|
|
|
|
const stmt2 = new DeclareVarStmt('bar', new LiteralExpr(true));
|
|
|
|
const stmt3 = new DeclareVarStmt('baz', new LiteralExpr('qux'), undefined, []);
|
|
|
|
const stmt3 = new DeclareVarStmt('baz', new LiteralExpr('qux'), undefined, []);
|
|
|
|
|
|
|
|
|
|
|
|
expect(renderer.printStatement(stmt1, sourceFile, importManager)).toBe('const foo = 42;');
|
|
|
|
expect(renderer.printStatement(stmt1, sourceFile, importManager)).toBe('const foo = 42;');
|
|
|
|
expect(renderer.printStatement(stmt2, sourceFile, importManager)).toBe('var bar = true;');
|
|
|
|
expect(renderer.printStatement(stmt2, sourceFile, importManager)).toBe('var bar = true;');
|
|
|
|
expect(renderer.printStatement(stmt3, sourceFile, importManager)).toBe('var baz = "qux";');
|
|
|
|
expect(renderer.printStatement(stmt3, sourceFile, importManager))
|
|
|
|
|
|
|
|
.toBe('var baz = "qux";');
|
|
|
|
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|