diff --git a/packages/compiler-cli/test/ngtsc/standalone_spec.ts b/packages/compiler-cli/test/ngtsc/standalone_spec.ts index e7952539863..d176e623af1 100644 --- a/packages/compiler-cli/test/ngtsc/standalone_spec.ts +++ b/packages/compiler-cli/test/ngtsc/standalone_spec.ts @@ -45,7 +45,9 @@ runInEachFileSystem(() => { export class TestCmp {} `); env.driveMain(); - expect(env.getContents('test.js')).toContain('directives: [TestDir]'); + const jsCode = env.getContents('test.js'); + expect(jsCode).toContain('directives: [TestDir]'); + expect(jsCode).toContain('standalone: true'); }); it('should error when a non-standalone component tries to use imports', () => { @@ -397,5 +399,35 @@ runInEachFileSystem(() => { .toEqual('imports: [TestDir]'); }); }); + + describe('other types', () => { + it('should compile a basic standalone directive', () => { + env.write('test.ts', ` + import {Directive} from '@angular/core'; + + @Directive({ + selector: '[dir]', + standalone: true, + }) + export class TestDir {} + `); + env.driveMain(); + expect(env.getContents('test.js')).toContain('standalone: true'); + }); + + it('should compile a basic standalone pipe', () => { + env.write('test.ts', ` + import {Pipe} from '@angular/core'; + + @Pipe({ + name: 'testpipe', + standalone: true, + }) + export class TestPipe {} + `); + env.driveMain(); + expect(env.getContents('test.js')).toContain('standalone: true'); + }); + }); }); }); diff --git a/packages/compiler/src/render3/r3_pipe_compiler.ts b/packages/compiler/src/render3/r3_pipe_compiler.ts index b0eb31c246c..e4b8b0c3431 100644 --- a/packages/compiler/src/render3/r3_pipe_compiler.ts +++ b/packages/compiler/src/render3/r3_pipe_compiler.ts @@ -69,6 +69,10 @@ export function compilePipeFromMetadata(metadata: R3PipeMetadata): R3CompiledExp // e.g. `pure: true` definitionMapValues.push({key: 'pure', value: o.literal(metadata.pure), quoted: false}); + if (metadata.isStandalone) { + definitionMapValues.push({key: 'standalone', value: o.literal(true), quoted: false}); + } + const expression = o.importExpr(R3.definePipe).callFn([o.literalMap(definitionMapValues)], undefined, true); const type = createPipeType(metadata); diff --git a/packages/compiler/src/render3/view/compiler.ts b/packages/compiler/src/render3/view/compiler.ts index bcf934b190a..3b25a1e351f 100644 --- a/packages/compiler/src/render3/view/compiler.ts +++ b/packages/compiler/src/render3/view/compiler.ts @@ -77,6 +77,10 @@ function baseDirectiveFields( definitionMap.set('exportAs', o.literalArr(meta.exportAs.map(e => o.literal(e)))); } + if (meta.isStandalone) { + definitionMap.set('standalone', o.literal(true)); + } + return definitionMap; } diff --git a/packages/core/src/render3/definition.ts b/packages/core/src/render3/definition.ts index 4406fe5d0a7..72e32570f9d 100644 --- a/packages/core/src/render3/definition.ts +++ b/packages/core/src/render3/definition.ts @@ -284,6 +284,11 @@ export function ɵɵdefineComponent(componentDefinition: { * The set of schemas that declare elements to be allowed in the component's template. */ schemas?: SchemaMetadata[] | null; + + /** + * Whether this directive/component is standalone. + */ + standalone?: boolean; }): unknown { return noSideEffects(() => { // Initialize ngDevMode. This must be the first statement in ɵɵdefineComponent. @@ -312,6 +317,7 @@ export function ɵɵdefineComponent(componentDefinition: { onPush: componentDefinition.changeDetection === ChangeDetectionStrategy.OnPush, directiveDefs: null!, // assigned in noSideEffects pipeDefs: null!, // assigned in noSideEffects + standalone: componentDefinition.standalone === true, selectors: componentDefinition.selectors || EMPTY_ARRAY, viewQuery: componentDefinition.viewQuery || null, features: componentDefinition.features as DirectiveDefFeature[] || null, @@ -709,13 +715,19 @@ export function ɵɵdefinePipe(pipeDef: { type: Type, /** Whether the pipe is pure. */ - pure?: boolean + pure?: boolean, + + /** + * Whether the pipe is standalone. + */ + standalone?: boolean, }): unknown { return (>{ type: pipeDef.type, name: pipeDef.name, factory: null, pure: pipeDef.pure !== false, + standalone: pipeDef.standalone === true, onDestroy: pipeDef.type.prototype.ngOnDestroy || null }); } diff --git a/packages/core/src/render3/interfaces/definition.ts b/packages/core/src/render3/interfaces/definition.ts index 9f0c2e09b33..4b5c3eceebb 100644 --- a/packages/core/src/render3/interfaces/definition.ts +++ b/packages/core/src/render3/interfaces/definition.ts @@ -189,6 +189,11 @@ export interface DirectiveDef { */ readonly exportAs: string[]|null; + /** + * Whether this directive (or component) is standalone. + */ + readonly standalone: boolean; + /** * Factory function used to create a new directive instance. Will be null initially. * Populated when the factory is first requested by directive instantiation logic. @@ -355,6 +360,11 @@ export interface PipeDef { */ readonly pure: boolean; + /** + * Whether this pipe is standalone. + */ + readonly standalone: boolean; + /* The following are lifecycle hooks for this pipe */ onDestroy: (() => void)|null; }