Skip to content

Commit

Permalink
fix(compiler-cli): show proper error for custom decorators in local c…
Browse files Browse the repository at this point in the history
…ompilation mode (#53983)

At the moment local compilation mode does not support custom decorators, and it leads to unhandled errors. In this change a compile time diagnostic is produced in local mode for custom decorators. This is a temporary solution since there are few custom decorators are in use in g3. Custom decorators will be eventually supported in local mode.

PR Close #53983
  • Loading branch information
pmvald authored and thePunderWoman committed Jan 26, 2024
1 parent 23eeea5 commit 0970129
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 0 deletions.
26 changes: 26 additions & 0 deletions packages/compiler-cli/src/ngtsc/transform/src/compilation.ts
Expand Up @@ -259,12 +259,21 @@ export class TraitCompiler implements ProgramTypeCheckAdapter {
let record: ClassRecord|null = this.recordFor(clazz);
let foundTraits: PendingTrait<unknown, unknown, SemanticSymbol|null, unknown>[] = [];

// A set to track the non-Angular decorators in local compilation mode. An error will be issued
// if non-Angular decorators is found in local compilation mode.
const nonNgDecoratorsInLocalMode =
this.compilationMode === CompilationMode.LOCAL ? new Set(decorators) : null;

for (const handler of this.handlers) {
const result = handler.detect(clazz, decorators);
if (result === undefined) {
continue;
}

if (nonNgDecoratorsInLocalMode !== null && result.decorator !== null) {
nonNgDecoratorsInLocalMode.delete(result.decorator);
}

const isPrimaryHandler = handler.precedence === HandlerPrecedence.PRIMARY;
const isWeakHandler = handler.precedence === HandlerPrecedence.WEAK;
const trait = Trait.pending(handler, result);
Expand Down Expand Up @@ -332,6 +341,23 @@ export class TraitCompiler implements ProgramTypeCheckAdapter {
}
}

if (nonNgDecoratorsInLocalMode !== null && nonNgDecoratorsInLocalMode.size > 0 &&
record !== null && record.metaDiagnostics === null) {
// Custom decorators found in local compilation mode! In this mode we don't support custom
// decorators yet. But will eventually do (b/320536434). For now a temporary error is thrown.
record.metaDiagnostics = [...nonNgDecoratorsInLocalMode].map(
decorator => ({
category: ts.DiagnosticCategory.Error,
code: Number('-99' + ErrorCode.DECORATOR_UNEXPECTED),
file: getSourceFile(clazz),
start: decorator.node.getStart(),
length: decorator.node.getWidth(),
messageText:
'In local compilation mode, Angular does not support custom decorators. Ensure all class decorators are from Angular.',
}));
record.traits = foundTraits = [];
}

return foundTraits.length > 0 ? foundTraits : null;
}

Expand Down
31 changes: 31 additions & 0 deletions packages/compiler-cli/test/ngtsc/local_compilation_spec.ts
Expand Up @@ -1734,5 +1734,36 @@ runInEachFileSystem(
}
});
});

describe('custom decorator', () => {
it('should produce diagnostic for each custom decorator', () => {
env.write('test.ts', `
import {Component} from '@angular/core';
import {customDecorator1, customDecorator2} from './some-where';
@customDecorator1('hello')
@Component({
template: ExternalString,
})
@customDecorator2
export class Main {
}
`);

const errors = env.driveDiagnostics();

expect(errors.length).toBe(2);

const text1 = ts.flattenDiagnosticMessageText(errors[0].messageText, '\n');
const text2 = ts.flattenDiagnosticMessageText(errors[1].messageText, '\n');

expect(errors[0].code).toBe(ngErrorCode(ErrorCode.DECORATOR_UNEXPECTED));
expect(errors[1].code).toBe(ngErrorCode(ErrorCode.DECORATOR_UNEXPECTED));
expect(text1).toContain(
'In local compilation mode, Angular does not support custom decorators');
expect(text2).toContain(
'In local compilation mode, Angular does not support custom decorators');
});
});
});
});

0 comments on commit 0970129

Please sign in to comment.