Skip to content

Commit d9687f4

Browse files
committed
feat(compiler-cli): 'strictStandalone' flag enforces standalone (angular#57935)
Add the `strictStandalone` flag to `angularCompilerOptions`. When set to true, the compiler will require that all declarations of components, directive, and pipes be standalone. When `standalone: false` is provided, an error is raised. Note that until the default value of the standalone flag is flipped, this does not catch the case where a declaration does not specify a value for `standalone`. The default value of the `strictStandalone` flag is `false`. PR Close angular#57935
1 parent 3240598 commit d9687f4

File tree

12 files changed

+114
-0
lines changed

12 files changed

+114
-0
lines changed

goldens/public-api/compiler-cli/compiler_options.api.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export interface DiagnosticOptions {
2828
[Name in ExtendedTemplateDiagnosticName]?: DiagnosticCategoryLabel;
2929
};
3030
};
31+
strictStandalone?: boolean;
3132
}
3233

3334
// @public

goldens/public-api/compiler-cli/error_code.api.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ export enum ErrorCode {
8484
NGMODULE_INVALID_REEXPORT = 6004,
8585
NGMODULE_MODULE_WITH_PROVIDERS_MISSING_GENERIC = 6005,
8686
NGMODULE_REEXPORT_NAME_COLLISION = 6006,
87+
NON_STANDALONE_NOT_ALLOWED = 2023,
8788
NULLISH_COALESCING_NOT_NULLABLE = 8102,
8889
OPTIONAL_CHAIN_NOT_NULLABLE = 8107,
8990
// (undocumented)

packages/compiler-cli/src/ngtsc/annotations/component/src/handler.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,7 @@ export class ComponentDecoratorHandler
252252
private readonly localCompilationExtraImportsTracker: LocalCompilationExtraImportsTracker | null,
253253
private readonly jitDeclarationRegistry: JitDeclarationRegistry,
254254
private readonly i18nPreserveSignificantWhitespace: boolean,
255+
private readonly strictStandalone: boolean,
255256
) {
256257
this.extractTemplateOptions = {
257258
enableI18nLegacyMessageIdFormat: this.enableI18nLegacyMessageIdFormat,
@@ -431,6 +432,7 @@ export class ComponentDecoratorHandler
431432
this.annotateForClosureCompiler,
432433
this.compilationMode,
433434
this.elementSchemaRegistry.getDefaultComponentElementName(),
435+
this.strictStandalone,
434436
);
435437
// `extractDirectiveMetadata` returns `jitForced = true` when the `@Component` has
436438
// set `jit: true`. In this case, compilation of the decorator is skipped. Returning

packages/compiler-cli/src/ngtsc/annotations/component/test/component_spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ function setup(
148148
/* localCompilationExtraImportsTracker */ null,
149149
jitDeclarationRegistry,
150150
/* i18nPreserveSignificantWhitespace */ true,
151+
/* strictStandalone */ false,
151152
);
152153
return {reflectionHost, handler, resourceLoader, metaRegistry};
153154
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ export class DirectiveDecoratorHandler
136136
private includeClassMetadata: boolean,
137137
private readonly compilationMode: CompilationMode,
138138
private readonly jitDeclarationRegistry: JitDeclarationRegistry,
139+
private readonly strictStandalone: boolean,
139140
) {}
140141

141142
readonly precedence = HandlerPrecedence.PRIMARY;
@@ -190,6 +191,7 @@ export class DirectiveDecoratorHandler
190191
this.annotateForClosureCompiler,
191192
this.compilationMode,
192193
/* defaultSelector */ null,
194+
this.strictStandalone,
193195
);
194196
// `extractDirectiveMetadata` returns `jitForced = true` when the `@Directive` has
195197
// set `jit: true`. In this case, compilation of the decorator is skipped. Returning

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ export function extractDirectiveMetadata(
115115
annotateForClosureCompiler: boolean,
116116
compilationMode: CompilationMode,
117117
defaultSelector: string | null,
118+
strictStandalone: boolean,
118119
):
119120
| {
120121
jitForced: false;
@@ -342,6 +343,14 @@ export function extractDirectiveMetadata(
342343
throw createValueHasWrongTypeError(expr, resolved, `standalone flag must be a boolean`);
343344
}
344345
isStandalone = resolved;
346+
347+
if (!isStandalone && strictStandalone) {
348+
throw new FatalDiagnosticError(
349+
ErrorCode.NON_STANDALONE_NOT_ALLOWED,
350+
expr,
351+
`Only standalone components/directives are allowed when 'strictStandalone' is enabled.`,
352+
);
353+
}
345354
}
346355
let isSignal = false;
347356
if (directive.has('signals')) {

packages/compiler-cli/src/ngtsc/annotations/directive/test/directive_spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ runInEachFileSystem(() => {
214214
/*includeClassMetadata*/ true,
215215
/*compilationMode */ CompilationMode.FULL,
216216
jitDeclarationRegistry,
217+
/* strictStandalone */ false,
217218
);
218219

219220
const DirNode = getDeclaration(program, _('/entry.ts'), dirName, isNamedClassDeclaration);

packages/compiler-cli/src/ngtsc/annotations/src/pipe.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ export class PipeDecoratorHandler
9595
private includeClassMetadata: boolean,
9696
private readonly compilationMode: CompilationMode,
9797
private readonly generateExtraImportsInLocalMode: boolean,
98+
private readonly strictStandalone: boolean,
9899
) {}
99100

100101
readonly precedence = HandlerPrecedence.PRIMARY;
@@ -183,6 +184,14 @@ export class PipeDecoratorHandler
183184
throw createValueHasWrongTypeError(expr, resolved, `standalone flag must be a boolean`);
184185
}
185186
isStandalone = resolved;
187+
188+
if (!isStandalone && this.strictStandalone) {
189+
throw new FatalDiagnosticError(
190+
ErrorCode.NON_STANDALONE_NOT_ALLOWED,
191+
expr,
192+
`Only standalone pipes are allowed when 'strictStandalone' is enabled.`,
193+
);
194+
}
186195
}
187196

188197
return {

packages/compiler-cli/src/ngtsc/core/api/src/public_options.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,11 @@ export interface DiagnosticOptions {
261261
*/
262262
checks?: {[Name in ExtendedTemplateDiagnosticName]?: DiagnosticCategoryLabel};
263263
};
264+
265+
/**
266+
* If enabled, non-standalone declarations are prohibited and result in build errors.
267+
*/
268+
strictStandalone?: boolean;
264269
}
265270

266271
/**

packages/compiler-cli/src/ngtsc/core/src/compiler.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1460,6 +1460,7 @@ export class NgCompiler {
14601460
localCompilationExtraImportsTracker,
14611461
jitDeclarationRegistry,
14621462
this.options.i18nPreserveWhitespaceForLegacyExtraction ?? true,
1463+
!!this.options.strictStandalone,
14631464
),
14641465

14651466
// TODO(alxhub): understand why the cast here is necessary (something to do with `null`
@@ -1482,6 +1483,7 @@ export class NgCompiler {
14821483
supportTestBed,
14831484
compilationMode,
14841485
jitDeclarationRegistry,
1486+
!!this.options.strictStandalone,
14851487
) as Readonly<DecoratorHandler<unknown, unknown, SemanticSymbol | null, unknown>>,
14861488
// Pipe handler must be before injectable handler in list so pipe factories are printed
14871489
// before injectable factories (so injectable factories can delegate to them)
@@ -1496,6 +1498,7 @@ export class NgCompiler {
14961498
supportTestBed,
14971499
compilationMode,
14981500
!!this.options.generateExtraImportsInLocalMode,
1501+
!!this.options.strictStandalone,
14991502
),
15001503
new InjectableDecoratorHandler(
15011504
reflector,

0 commit comments

Comments
 (0)