Skip to content

Commit 5d63324

Browse files
pmvaldthePunderWoman
authored andcommitted
fix(compiler-cli): show the correct message for the error LOCAL_COMPILATION_UNRESOLVED_CONST when an unresolved symbol used for @component.styles (angular#54230)
Currently the correct error message is shown only if @component.styles is an array with some unresolved element. This change supports the new case of string type for the @component.styles field. PR Close angular#54230
1 parent 6c8b094 commit 5d63324

3 files changed

Lines changed: 97 additions & 14 deletions

File tree

packages/compiler-cli/src/ngtsc/annotations/common/src/diagnostics.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -410,13 +410,16 @@ function getInheritedUndecoratedCtorDiagnostic(
410410
* if the compilation mode is local and the value is not resolved due to being imported
411411
* from external files. This is a common scenario for errors in local compilation mode,
412412
* and so this helper can be used to quickly generate the relevant errors.
413+
*
414+
* @param nodeToHighlight Node to be highlighted in teh error message.
415+
* Will default to value.node if not provided.
413416
*/
414-
export function assertLocalCompilationUnresolvedConst(compilationMode: CompilationMode, value: ResolvedValue, nodeToHighlight: ts.Node, errorMessage: string): void {
417+
export function assertLocalCompilationUnresolvedConst(compilationMode: CompilationMode, value: ResolvedValue, nodeToHighlight: ts.Node|null, errorMessage: string): void {
415418
if (compilationMode === CompilationMode.LOCAL && value instanceof DynamicValue &&
416419
value.isFromUnknownIdentifier()) {
417420
throw new FatalDiagnosticError(
418421
ErrorCode.LOCAL_COMPILATION_UNRESOLVED_CONST,
419-
nodeToHighlight,
422+
nodeToHighlight ?? value.node,
420423
errorMessage);
421424
}
422425
}

packages/compiler-cli/src/ngtsc/annotations/directive/src/shared.ts

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {ClassPropertyMapping, DecoratorInputTransform, HostDirectiveMeta, InputM
1515
import {DynamicValue, EnumValue, PartialEvaluator, ResolvedValue, traceDynamicValue} from '../../../partial_evaluator';
1616
import {AmbientImport, ClassDeclaration, ClassMember, ClassMemberKind, Decorator, filterToMembersWithDecorator, isNamedClassDeclaration, ReflectionHost, reflectObjectLiteral} from '../../../reflection';
1717
import {CompilationMode} from '../../../transform';
18-
import {createSourceSpan, createValueHasWrongTypeError, forwardRefResolver, getAngularDecorators, getConstructorDependencies, isAngularDecorator, ReferencesRegistry, toR3Reference, tryUnwrapForwardRef, unwrapConstructorDependencies, unwrapExpression, validateConstructorDependencies, wrapFunctionExpressionsInParens, wrapTypeReference} from '../../common';
18+
import {createSourceSpan, createValueHasWrongTypeError, forwardRefResolver, getAngularDecorators, getConstructorDependencies, isAngularDecorator, ReferencesRegistry, toR3Reference, tryUnwrapForwardRef, unwrapConstructorDependencies, unwrapExpression, validateConstructorDependencies, assertLocalCompilationUnresolvedConst, wrapFunctionExpressionsInParens, wrapTypeReference} from '../../common';
1919

2020
import {tryParseSignalInputMapping} from './input_function';
2121
import {tryParseInitializerBasedOutput} from './output_function';
@@ -124,6 +124,12 @@ export function extractDirectiveMetadata(
124124
if (directive.has('selector')) {
125125
const expr = directive.get('selector')!;
126126
const resolved = evaluator.evaluate(expr);
127+
assertLocalCompilationUnresolvedConst(compilationMode, resolved, null,
128+
'Unresolved identifier found for @Component.selector field! Did you ' +
129+
'import this identifier from a file outside of the compilation unit? ' +
130+
'This is not allowed when Angular compiler runs in local mode. Possible ' +
131+
'solutions: 1) Move the declarations into a file within the compilation ' +
132+
'unit, 2) Inline the selector');
127133
if (typeof resolved !== 'string') {
128134
throw createValueHasWrongTypeError(expr, resolved, `selector must be a string`);
129135
}
@@ -493,16 +499,26 @@ export function parseDirectiveStyles(
493499
const evaluated = evaluator.evaluate(expression);
494500
const value = typeof evaluated === 'string' ? [evaluated] : evaluated;
495501

496-
// The identifier used for @Component.styles cannot be resolved in local compilation mode. An error specific to this situation is generated.
497-
if (compilationMode === CompilationMode.LOCAL && Array.isArray(value)) {
498-
for (const entry of value) {
499-
if (entry instanceof DynamicValue && entry.isFromUnknownIdentifier()) {
500-
501-
throw new FatalDiagnosticError(
502-
ErrorCode.LOCAL_COMPILATION_UNRESOLVED_CONST,
503-
entry.node,
504-
'Unresolved identifier found for @Component.styles field! Did you import this identifier from a file outside of the compilation unit? This is not allowed when Angular compiler runs in local mode. Possible solutions: 1) Move the declarations into a file within the compilation unit, 2) Inline the styles, 3) Move the styles into separate files and include it using @Component.styleUrls');
505-
}
502+
// Check if the identifier used for @Component.styles cannot be resolved in local compilation mode. if the case, an error specific to this situation is generated.
503+
if (compilationMode === CompilationMode.LOCAL) {
504+
let unresolvedNode: ts.Node|null = null;
505+
if (Array.isArray(value)) {
506+
const entry = value.find(e => e instanceof DynamicValue && e.isFromUnknownIdentifier()) as DynamicValue|undefined;
507+
unresolvedNode = entry?.node ?? null;
508+
} else if (value instanceof DynamicValue && value.isFromUnknownIdentifier()) {
509+
unresolvedNode = value.node;
510+
}
511+
512+
if (unresolvedNode !== null) {
513+
throw new FatalDiagnosticError(
514+
ErrorCode.LOCAL_COMPILATION_UNRESOLVED_CONST,
515+
unresolvedNode,
516+
'Unresolved identifier found for @Component.styles field! Did you import ' +
517+
'this identifier from a file outside of the compilation unit? This is ' +
518+
'not allowed when Angular compiler runs in local mode. Possible ' +
519+
'solutions: 1) Move the declarations into a file within the compilation ' +
520+
'unit, 2) Inline the styles, 3) Move the styles into separate files and ' +
521+
'include it using @Component.styleUrls');
506522
}
507523
}
508524

packages/compiler-cli/test/ngtsc/local_compilation_spec.ts

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1222,7 +1222,7 @@ runInEachFileSystem(
12221222
'Unresolved identifier found for @Component.template field! Did you import this identifier from a file outside of the compilation unit? This is not allowed when Angular compiler runs in local mode. Possible solutions: 1) Move the declaration into a file within the compilation unit, 2) Inline the template, 3) Move the template into a separate .html file and include it using @Component.templateUrl');
12231223
});
12241224

1225-
it('should show correct error message when using an external symbol for component styles',
1225+
it('should show correct error message when using an external symbol for component styles array',
12261226
() => {
12271227
env.write('test.ts', `
12281228
import {Component} from '@angular/core';
@@ -1255,6 +1255,70 @@ runInEachFileSystem(
12551255
expect(text).toEqual(
12561256
'Unresolved identifier found for @Component.styles field! Did you import this identifier from a file outside of the compilation unit? This is not allowed when Angular compiler runs in local mode. Possible solutions: 1) Move the declarations into a file within the compilation unit, 2) Inline the styles, 3) Move the styles into separate files and include it using @Component.styleUrls');
12571257
});
1258+
1259+
it('should show correct error message when using an external symbol for component styles',
1260+
() => {
1261+
env.write('test.ts', `
1262+
import {Component} from '@angular/core';
1263+
import {ExternalString} from './some-where';
1264+
1265+
@Component({
1266+
styles: ExternalString,
1267+
template: '',
1268+
1269+
})
1270+
export class Main {
1271+
}
1272+
`);
1273+
1274+
const errors = env.driveDiagnostics();
1275+
1276+
expect(errors.length).toBe(1);
1277+
1278+
const {code, messageText, relatedInformation, length} = errors[0];
1279+
1280+
expect(code).toBe(
1281+
ngErrorCode(ErrorCode.LOCAL_COMPILATION_UNRESOLVED_CONST));
1282+
expect(length).toBe(14),
1283+
expect(relatedInformation).toBeUndefined();
1284+
1285+
const text = ts.flattenDiagnosticMessageText(messageText, '\n');
1286+
1287+
expect(text).toEqual(
1288+
'Unresolved identifier found for @Component.styles field! Did you import this identifier from a file outside of the compilation unit? This is not allowed when Angular compiler runs in local mode. Possible solutions: 1) Move the declarations into a file within the compilation unit, 2) Inline the styles, 3) Move the styles into separate files and include it using @Component.styleUrls');
1289+
});
1290+
1291+
it('should show correct error message when using an external symbol for component selector',
1292+
() => {
1293+
env.write('test.ts', `
1294+
import {Component} from '@angular/core';
1295+
import {ExternalString} from './some-where';
1296+
1297+
@Component({
1298+
selector: ExternalString,
1299+
template: '',
1300+
1301+
})
1302+
export class Main {
1303+
}
1304+
`);
1305+
1306+
const errors = env.driveDiagnostics();
1307+
1308+
expect(errors.length).toBe(1);
1309+
1310+
const {code, messageText, relatedInformation, length} = errors[0];
1311+
1312+
expect(code).toBe(
1313+
ngErrorCode(ErrorCode.LOCAL_COMPILATION_UNRESOLVED_CONST));
1314+
expect(length).toBe(14),
1315+
expect(relatedInformation).toBeUndefined();
1316+
1317+
const text = ts.flattenDiagnosticMessageText(messageText, '\n');
1318+
1319+
expect(text).toEqual(
1320+
'Unresolved identifier found for @Component.selector field! Did you import this identifier from a file outside of the compilation unit? This is not allowed when Angular compiler runs in local mode. Possible solutions: 1) Move the declarations into a file within the compilation unit, 2) Inline the selector');
1321+
});
12581322
});
12591323

12601324
describe('ng module bootstrap def', () => {

0 commit comments

Comments
 (0)