mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
658 lines
21 KiB
TypeScript
658 lines
21 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright Google LLC 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.dev/license
|
|
*/
|
|
|
|
import ts from 'typescript';
|
|
|
|
import {ErrorCode, ngErrorCode} from '../../src/ngtsc/diagnostics';
|
|
import {runInEachFileSystem} from '../../src/ngtsc/file_system/testing';
|
|
import {loadStandardTestFiles} from '../../src/ngtsc/testing';
|
|
|
|
import {NgtscTestEnvironment} from './env';
|
|
|
|
const testFiles = loadStandardTestFiles();
|
|
|
|
const tsconfigBase = {
|
|
extends: '../tsconfig-base.json',
|
|
compilerOptions: {
|
|
rootDirs: ['/app'],
|
|
emitDeclarationOnly: true,
|
|
noCheck: true,
|
|
},
|
|
};
|
|
|
|
runInEachFileSystem(() => {
|
|
describe('declaration-only emission', () => {
|
|
let env!: NgtscTestEnvironment;
|
|
|
|
beforeEach(() => {
|
|
env = NgtscTestEnvironment.setup(testFiles);
|
|
const tsconfig: {[key: string]: any} = {
|
|
...tsconfigBase,
|
|
angularCompilerOptions: {
|
|
_experimentalAllowEmitDeclarationOnly: true,
|
|
},
|
|
};
|
|
env.write('tsconfig.json', JSON.stringify(tsconfig, null, 2));
|
|
});
|
|
|
|
it('fails with config diagnostic if experimental flag is not provided', () => {
|
|
env.write('tsconfig.json', JSON.stringify(tsconfigBase, null, 2));
|
|
env.write('test.ts', '');
|
|
|
|
const errors = env.driveDiagnostics();
|
|
|
|
expect(errors.length).toBe(1);
|
|
expect(errors[0].code).toBe(ngErrorCode(ErrorCode.CONFIG_EMIT_DECLARATION_ONLY_UNSUPPORTED));
|
|
expect(errors[0].messageText).toBe(
|
|
'TS compiler option "emitDeclarationOnly" is not supported.',
|
|
);
|
|
});
|
|
|
|
it('fails with config diagnostic if experimental flag is disabled', () => {
|
|
const tsconfig = {
|
|
...tsconfigBase,
|
|
angularCompilerOptions: {
|
|
_experimentalAllowEmitDeclarationOnly: false,
|
|
},
|
|
};
|
|
env.write('tsconfig.json', JSON.stringify(tsconfig, null, 2));
|
|
env.write('test.ts', '');
|
|
|
|
const errors = env.driveDiagnostics();
|
|
|
|
expect(errors.length).toBe(1);
|
|
expect(errors[0].code).toBe(ngErrorCode(ErrorCode.CONFIG_EMIT_DECLARATION_ONLY_UNSUPPORTED));
|
|
expect(errors[0].messageText).toBe(
|
|
'TS compiler option "emitDeclarationOnly" is not supported.',
|
|
);
|
|
});
|
|
|
|
it('should show correct error message when using an @NgModule with an external reference in declarations', () => {
|
|
env.write(
|
|
'test.ts',
|
|
`
|
|
import {NgModule} from '@angular/core';
|
|
import {Comp} from './comp';
|
|
|
|
@NgModule({
|
|
declarations: [Comp],
|
|
})
|
|
export class CompModule {}
|
|
`,
|
|
);
|
|
|
|
const errors = env.driveDiagnostics();
|
|
|
|
expect(errors.length).toBe(1);
|
|
expect(errors[0].code).toBe(ngErrorCode(ErrorCode.VALUE_HAS_WRONG_TYPE));
|
|
expect(ts.flattenDiagnosticMessageText(errors[0].messageText, '\n')).toContain(
|
|
'Value at position 0 in the NgModule.declarations of CompModule is an external reference. ' +
|
|
'External references in @NgModule declarations are not supported in experimental declaration-only emission mode',
|
|
);
|
|
});
|
|
|
|
it('should show correct error message when using an @NgModule with an external reference in imports', () => {
|
|
env.write(
|
|
'test.ts',
|
|
`
|
|
import {NgModule} from '@angular/core';
|
|
import {Comp} from './comp';
|
|
|
|
@NgModule({
|
|
imports: [Comp],
|
|
})
|
|
export class CompModule {}
|
|
`,
|
|
);
|
|
|
|
const errors = env.driveDiagnostics();
|
|
|
|
expect(errors.length).toBe(1);
|
|
expect(errors[0].code).toBe(ngErrorCode(ErrorCode.VALUE_HAS_WRONG_TYPE));
|
|
expect(ts.flattenDiagnosticMessageText(errors[0].messageText, '\n')).toContain(
|
|
'Value at position 0 in the NgModule.imports of CompModule is an external reference. ' +
|
|
'External references in @NgModule declarations are not supported in experimental declaration-only emission mode',
|
|
);
|
|
});
|
|
|
|
it('should show correct error message when using an @NgModule with an external reference in exports', () => {
|
|
env.write(
|
|
'test.ts',
|
|
`
|
|
import {NgModule} from '@angular/core';
|
|
import {Comp} from './comp';
|
|
|
|
@NgModule({
|
|
exports: [Comp],
|
|
})
|
|
export class CompModule {}
|
|
`,
|
|
);
|
|
|
|
const errors = env.driveDiagnostics();
|
|
|
|
expect(errors.length).toBe(1);
|
|
expect(errors[0].code).toBe(ngErrorCode(ErrorCode.VALUE_HAS_WRONG_TYPE));
|
|
expect(ts.flattenDiagnosticMessageText(errors[0].messageText, '\n')).toContain(
|
|
'Value at position 0 in the NgModule.exports of CompModule is an external reference. ' +
|
|
'External references in @NgModule declarations are not supported in experimental declaration-only emission mode',
|
|
);
|
|
});
|
|
|
|
it('should show correct error message when using an @NgModule with an external reference in bootstrap', () => {
|
|
env.write(
|
|
'test.ts',
|
|
`
|
|
import {NgModule} from '@angular/core';
|
|
import {Comp} from './comp';
|
|
|
|
@NgModule({
|
|
bootstrap: [Comp],
|
|
})
|
|
export class CompModule {}
|
|
`,
|
|
);
|
|
|
|
const errors = env.driveDiagnostics();
|
|
|
|
expect(errors.length).toBe(1);
|
|
expect(errors[0].code).toBe(ngErrorCode(ErrorCode.VALUE_HAS_WRONG_TYPE));
|
|
expect(ts.flattenDiagnosticMessageText(errors[0].messageText, '\n')).toContain(
|
|
'Value at position 0 in the NgModule.bootstrap of CompModule is an external reference. ' +
|
|
'External references in @NgModule declarations are not supported in experimental declaration-only emission mode',
|
|
);
|
|
});
|
|
|
|
it('should emit type declarations containing external reference in simple host directive on a component', () => {
|
|
env.write(
|
|
'test.ts',
|
|
`
|
|
import {Component} from '@angular/core';
|
|
import {Dir} from './dir';
|
|
|
|
@Component({
|
|
template: '',
|
|
selector: 'host-comp',
|
|
hostDirectives: [Dir],
|
|
})
|
|
export class HostComp {}
|
|
`,
|
|
);
|
|
|
|
env.driveMain();
|
|
const dtsContent = env.getContents('test.d.ts');
|
|
|
|
expect(dtsContent).toContain(
|
|
'static ɵcmp: i0.ɵɵComponentDeclaration<HostComp, "host-comp", never, {}, {}, never, never, true, [{ directive: typeof i1.Dir; inputs: {}; outputs: {}; }]>;',
|
|
);
|
|
});
|
|
|
|
it('should emit type declarations containing external reference in host directive object on a component', () => {
|
|
env.write(
|
|
'test.ts',
|
|
`
|
|
import {Component} from '@angular/core';
|
|
import {Dir} from './dir';
|
|
|
|
@Component({
|
|
template: '',
|
|
selector: 'host-comp',
|
|
hostDirectives: [{
|
|
directive: Dir,
|
|
}],
|
|
})
|
|
export class HostComp {}
|
|
`,
|
|
);
|
|
|
|
env.driveMain();
|
|
const dtsContent = env.getContents('test.d.ts');
|
|
|
|
expect(dtsContent).toContain(
|
|
'static ɵcmp: i0.ɵɵComponentDeclaration<HostComp, "host-comp", never, {}, {}, never, never, true, [{ directive: typeof i1.Dir; inputs: {}; outputs: {}; }]>;',
|
|
);
|
|
});
|
|
|
|
it('should emit type declarations containing external reference in simple host directive on a directive', () => {
|
|
env.write(
|
|
'test.ts',
|
|
`
|
|
import {Directive} from '@angular/core';
|
|
import {Dir} from './dir';
|
|
|
|
@Directive({
|
|
selector: '[host-dir]',
|
|
hostDirectives: [Dir],
|
|
})
|
|
export class HostDir {}
|
|
`,
|
|
);
|
|
|
|
env.driveMain();
|
|
const dtsContent = env.getContents('test.d.ts');
|
|
|
|
expect(dtsContent).toContain(
|
|
'static ɵdir: i0.ɵɵDirectiveDeclaration<HostDir, "[host-dir]", never, {}, {}, never, never, true, [{ directive: typeof i1.Dir; inputs: {}; outputs: {}; }]>;',
|
|
);
|
|
});
|
|
|
|
it('should emit type declarations containing external reference in host directive object on a directive', () => {
|
|
env.write(
|
|
'test.ts',
|
|
`
|
|
import {Directive} from '@angular/core';
|
|
import {Dir} from './dir';
|
|
|
|
@Directive({
|
|
selector: '[host-dir]',
|
|
hostDirectives: [{
|
|
directive: Dir,
|
|
}],
|
|
})
|
|
export class HostDir {}
|
|
`,
|
|
);
|
|
|
|
env.driveMain();
|
|
const dtsContent = env.getContents('test.d.ts');
|
|
|
|
expect(dtsContent).toContain(
|
|
'static ɵdir: i0.ɵɵDirectiveDeclaration<HostDir, "[host-dir]", never, {}, {}, never, never, true, [{ directive: typeof i1.Dir; inputs: {}; outputs: {}; }]>;',
|
|
);
|
|
});
|
|
|
|
it('should show correct error message when using an indirect external reference in a simple host directive on a component', () => {
|
|
env.write(
|
|
'test.ts',
|
|
`
|
|
import {Component} from '@angular/core';
|
|
import {Dir} from './dir';
|
|
|
|
const DirIndirect = Dir;
|
|
|
|
@Component({
|
|
template: '',
|
|
selector: 'host-comp',
|
|
hostDirectives: [DirIndirect],
|
|
})
|
|
export class HostComp {}
|
|
`,
|
|
);
|
|
|
|
const errors = env.driveDiagnostics();
|
|
|
|
expect(errors.length).toBe(1);
|
|
expect(errors[0].code).toBe(ngErrorCode(ErrorCode.LOCAL_COMPILATION_UNSUPPORTED_EXPRESSION));
|
|
expect(ts.flattenDiagnosticMessageText(errors[0].messageText, '\n')).toBe(
|
|
'In experimental declaration-only emission mode, host directive cannot use indirect external indentifiers. Use a direct external identifier instead',
|
|
);
|
|
});
|
|
|
|
it('should show correct error message when using an indirect external reference in host directive object on a component', () => {
|
|
env.write(
|
|
'test.ts',
|
|
`
|
|
import {Component} from '@angular/core';
|
|
import {Dir} from './dir';
|
|
|
|
const DirIndirect = Dir;
|
|
|
|
@Component({
|
|
template: '',
|
|
selector: 'host-comp',
|
|
hostDirectives: [{
|
|
directive: DirIndirect,
|
|
}],
|
|
})
|
|
export class HostComp {}
|
|
`,
|
|
);
|
|
|
|
const errors = env.driveDiagnostics();
|
|
|
|
expect(errors.length).toBe(1);
|
|
expect(errors[0].code).toBe(ngErrorCode(ErrorCode.LOCAL_COMPILATION_UNSUPPORTED_EXPRESSION));
|
|
expect(ts.flattenDiagnosticMessageText(errors[0].messageText, '\n')).toBe(
|
|
'In experimental declaration-only emission mode, host directive cannot use indirect external indentifiers. Use a direct external identifier instead',
|
|
);
|
|
});
|
|
|
|
it('should show correct error message when using an indirect external reference in a simple host directive on a directive', () => {
|
|
env.write(
|
|
'test.ts',
|
|
`
|
|
import {Directive} from '@angular/core';
|
|
import {Dir} from './dir';
|
|
|
|
const DirIndirect = Dir;
|
|
|
|
@Directive({
|
|
selector: '[host-dir]',
|
|
hostDirectives: [DirIndirect],
|
|
})
|
|
export class HostDir {}
|
|
`,
|
|
);
|
|
|
|
const errors = env.driveDiagnostics();
|
|
|
|
expect(errors.length).toBe(1);
|
|
expect(errors[0].code).toBe(ngErrorCode(ErrorCode.LOCAL_COMPILATION_UNSUPPORTED_EXPRESSION));
|
|
expect(ts.flattenDiagnosticMessageText(errors[0].messageText, '\n')).toBe(
|
|
'In experimental declaration-only emission mode, host directive cannot use indirect external indentifiers. Use a direct external identifier instead',
|
|
);
|
|
});
|
|
|
|
it('should show correct error message when using an indirect external reference in host directive object on a directive', () => {
|
|
env.write(
|
|
'test.ts',
|
|
`
|
|
import {Directive} from '@angular/core';
|
|
import {Dir} from './dir';
|
|
|
|
const DirIndirect = Dir;
|
|
|
|
@Directive({
|
|
selector: '[host-dir]',
|
|
hostDirectives: [{
|
|
directive: DirIndirect,
|
|
}],
|
|
})
|
|
export class HostDir {}
|
|
`,
|
|
);
|
|
|
|
const errors = env.driveDiagnostics();
|
|
|
|
expect(errors.length).toBe(1);
|
|
expect(errors[0].code).toBe(ngErrorCode(ErrorCode.LOCAL_COMPILATION_UNSUPPORTED_EXPRESSION));
|
|
expect(ts.flattenDiagnosticMessageText(errors[0].messageText, '\n')).toBe(
|
|
'In experimental declaration-only emission mode, host directive cannot use indirect external indentifiers. Use a direct external identifier instead',
|
|
);
|
|
});
|
|
|
|
it('should show correct error message when using a property access expression resolving to an indirect external reference in a simple host directive on a component', () => {
|
|
env.write(
|
|
'test.ts',
|
|
`
|
|
import {Component} from '@angular/core';
|
|
import {Dir} from './dir';
|
|
|
|
const DIR = {
|
|
Dir
|
|
};
|
|
|
|
@Component({
|
|
template: '',
|
|
selector: 'host-comp',
|
|
hostDirectives: [DIR.Dir],
|
|
})
|
|
export class HostComp {}
|
|
`,
|
|
);
|
|
|
|
const errors = env.driveDiagnostics();
|
|
|
|
expect(errors.length).toBe(1);
|
|
expect(errors[0].code).toBe(ngErrorCode(ErrorCode.LOCAL_COMPILATION_UNSUPPORTED_EXPRESSION));
|
|
expect(ts.flattenDiagnosticMessageText(errors[0].messageText, '\n')).toBe(
|
|
'In experimental declaration-only emission mode, host directive cannot be an expression. Use an identifier instead',
|
|
);
|
|
});
|
|
|
|
it('should show correct error message when using a property access expression resolving to an indirect external reference in host directive object on a component', () => {
|
|
env.write(
|
|
'test.ts',
|
|
`
|
|
import {Component} from '@angular/core';
|
|
import {Dir} from './dir';
|
|
|
|
const DIR = {
|
|
Dir
|
|
};
|
|
|
|
@Component({
|
|
template: '',
|
|
selector: 'host-comp',
|
|
hostDirectives: [{
|
|
directive: DIR.Dir,
|
|
}],
|
|
})
|
|
export class HostComp {}
|
|
`,
|
|
);
|
|
|
|
const errors = env.driveDiagnostics();
|
|
|
|
expect(errors.length).toBe(1);
|
|
expect(errors[0].code).toBe(ngErrorCode(ErrorCode.LOCAL_COMPILATION_UNSUPPORTED_EXPRESSION));
|
|
expect(ts.flattenDiagnosticMessageText(errors[0].messageText, '\n')).toBe(
|
|
'In experimental declaration-only emission mode, host directive cannot be an expression. Use an identifier instead',
|
|
);
|
|
});
|
|
|
|
it('should show correct error message when using a property access expression resolving to an indirect external reference in a simple host directive on a directive', () => {
|
|
env.write(
|
|
'test.ts',
|
|
`
|
|
import {Directive} from '@angular/core';
|
|
import {Dir} from './dir';
|
|
|
|
const DIR = {
|
|
Dir
|
|
};
|
|
|
|
@Directive({
|
|
selector: '[host-dir]',
|
|
hostDirectives: [DIR.Dir],
|
|
})
|
|
export class HostDir {}
|
|
`,
|
|
);
|
|
|
|
const errors = env.driveDiagnostics();
|
|
|
|
expect(errors.length).toBe(1);
|
|
expect(errors[0].code).toBe(ngErrorCode(ErrorCode.LOCAL_COMPILATION_UNSUPPORTED_EXPRESSION));
|
|
expect(ts.flattenDiagnosticMessageText(errors[0].messageText, '\n')).toBe(
|
|
'In experimental declaration-only emission mode, host directive cannot be an expression. Use an identifier instead',
|
|
);
|
|
});
|
|
|
|
it('should show correct error message when using a property access expression resolving to an indirect external reference in host directive object on a directive', () => {
|
|
env.write(
|
|
'test.ts',
|
|
`
|
|
import {Directive} from '@angular/core';
|
|
import {Dir} from './dir';
|
|
|
|
const DIR = {
|
|
Dir
|
|
};
|
|
|
|
@Directive({
|
|
selector: '[host-dir]',
|
|
hostDirectives: [{
|
|
directive: DIR.Dir,
|
|
}],
|
|
})
|
|
export class HostDir {}
|
|
`,
|
|
);
|
|
|
|
const errors = env.driveDiagnostics();
|
|
|
|
expect(errors.length).toBe(1);
|
|
expect(errors[0].code).toBe(ngErrorCode(ErrorCode.LOCAL_COMPILATION_UNSUPPORTED_EXPRESSION));
|
|
expect(ts.flattenDiagnosticMessageText(errors[0].messageText, '\n')).toBe(
|
|
'In experimental declaration-only emission mode, host directive cannot be an expression. Use an identifier instead',
|
|
);
|
|
});
|
|
|
|
it('should show correct error message when using an expression resovling to an external reference in a simple host directive on a component', () => {
|
|
env.write(
|
|
'test.ts',
|
|
`
|
|
import {Component} from '@angular/core';
|
|
import {Dir} from './dir';
|
|
|
|
const DirArray = [Dir];
|
|
|
|
@Component({
|
|
template: '',
|
|
selector: 'host-comp',
|
|
hostDirectives: [DirArray[0]],
|
|
})
|
|
export class HostComp {}
|
|
`,
|
|
);
|
|
|
|
const errors = env.driveDiagnostics();
|
|
|
|
expect(errors.length).toBe(1);
|
|
expect(errors[0].code).toBe(ngErrorCode(ErrorCode.LOCAL_COMPILATION_UNSUPPORTED_EXPRESSION));
|
|
expect(ts.flattenDiagnosticMessageText(errors[0].messageText, '\n')).toBe(
|
|
'In experimental declaration-only emission mode, host directive cannot be an expression. Use an identifier instead',
|
|
);
|
|
});
|
|
|
|
it('should show correct error message when using an expression resovling to an external reference in host directive object on a component', () => {
|
|
env.write(
|
|
'test.ts',
|
|
`
|
|
import {Component} from '@angular/core';
|
|
import {Dir} from './dir';
|
|
|
|
const DirArray = [Dir];
|
|
|
|
@Component({
|
|
template: '',
|
|
selector: 'host-comp',
|
|
hostDirectives: [{
|
|
directive: DirArray[0],
|
|
}],
|
|
})
|
|
export class HostComp {}
|
|
`,
|
|
);
|
|
|
|
const errors = env.driveDiagnostics();
|
|
|
|
expect(errors.length).toBe(1);
|
|
expect(errors[0].code).toBe(ngErrorCode(ErrorCode.LOCAL_COMPILATION_UNSUPPORTED_EXPRESSION));
|
|
expect(ts.flattenDiagnosticMessageText(errors[0].messageText, '\n')).toBe(
|
|
'In experimental declaration-only emission mode, host directive cannot be an expression. Use an identifier instead',
|
|
);
|
|
});
|
|
|
|
it('should show correct error message when using an expression resovling to an external reference in a simple host directive on a directive', () => {
|
|
env.write(
|
|
'test.ts',
|
|
`
|
|
import {Directive} from '@angular/core';
|
|
import {Dir} from './dir';
|
|
|
|
const DirArray = [Dir];
|
|
|
|
@Directive({
|
|
selector: '[host-dir]',
|
|
hostDirectives: [DirArray[0]],
|
|
})
|
|
export class HostDir {}
|
|
`,
|
|
);
|
|
|
|
const errors = env.driveDiagnostics();
|
|
|
|
expect(errors.length).toBe(1);
|
|
expect(errors[0].code).toBe(ngErrorCode(ErrorCode.LOCAL_COMPILATION_UNSUPPORTED_EXPRESSION));
|
|
expect(ts.flattenDiagnosticMessageText(errors[0].messageText, '\n')).toBe(
|
|
'In experimental declaration-only emission mode, host directive cannot be an expression. Use an identifier instead',
|
|
);
|
|
});
|
|
|
|
it('should show correct error message when using an expression resovling to an external reference in host directive object on a directive', () => {
|
|
env.write(
|
|
'test.ts',
|
|
`
|
|
import {Directive} from '@angular/core';
|
|
import {Dir} from './dir';
|
|
|
|
const DirArray = [Dir];
|
|
|
|
@Directive({
|
|
selector: '[host-dir]',
|
|
hostDirectives: [{
|
|
directive: DirArray[0],
|
|
}],
|
|
})
|
|
export class HostDir {}
|
|
`,
|
|
);
|
|
|
|
const errors = env.driveDiagnostics();
|
|
|
|
expect(errors.length).toBe(1);
|
|
expect(errors[0].code).toBe(ngErrorCode(ErrorCode.LOCAL_COMPILATION_UNSUPPORTED_EXPRESSION));
|
|
expect(ts.flattenDiagnosticMessageText(errors[0].messageText, '\n')).toBe(
|
|
'In experimental declaration-only emission mode, host directive cannot be an expression. Use an identifier instead',
|
|
);
|
|
});
|
|
|
|
it('should show correct error message when using an @Input decorator with a transform function', () => {
|
|
env.write(
|
|
'test.ts',
|
|
`
|
|
import {booleanAttribute, Component, Input} from '@angular/core';
|
|
|
|
@Component({template: '', selector: 'comp'})
|
|
export class Comp {
|
|
@Input({ transform: booleanAttribute }) decoratedInput!: boolean;
|
|
}
|
|
`,
|
|
);
|
|
|
|
const errors = env.driveDiagnostics();
|
|
|
|
expect(errors.length).toBe(1);
|
|
expect(errors[0].code).toBe(ngErrorCode(ErrorCode.DECORATOR_UNEXPECTED));
|
|
const errorMessage = ts.flattenDiagnosticMessageText(errors[0].messageText, '\n');
|
|
expect(errorMessage).toContain(
|
|
'@Input decorators with a transform function are not supported in experimental declaration-only emission mode',
|
|
);
|
|
expect(errorMessage).toContain(
|
|
`Consider converting 'Comp.decoratedInput' to an input signal`,
|
|
);
|
|
});
|
|
|
|
it('should show correct error message when using custom decorators', () => {
|
|
env.write(
|
|
'test.ts',
|
|
`
|
|
import {Component} from '@angular/core';
|
|
|
|
export function Custom() {
|
|
return function(target: any) {};
|
|
}
|
|
|
|
@Custom()
|
|
@Component({template: '', selector: 'comp'})
|
|
export class Comp {}
|
|
`,
|
|
);
|
|
|
|
const errors = env.driveDiagnostics();
|
|
|
|
expect(errors.length).toBe(1);
|
|
expect(errors[0].code).toBe(ngErrorCode(ErrorCode.DECORATOR_UNEXPECTED));
|
|
expect(ts.flattenDiagnosticMessageText(errors[0].messageText, '\n')).toBe(
|
|
'In experimental declaration-only emission mode, Angular does not support custom decorators. Ensure all class decorators are from Angular.',
|
|
);
|
|
});
|
|
});
|
|
});
|