From 38421578a2573bcbc86c927ed4015e20fc39f04a Mon Sep 17 00:00:00 2001 From: Matthieu Riegler Date: Fri, 18 Nov 2022 10:11:29 +0100 Subject: [PATCH] feat(core): Make the `isStandalone()` function available in public API (#48114) This commit updates an internal `isStandalone` function and exposes it as a public API, so that it can be used in applications code. fixes #47919 PR Close #48114 --- goldens/public-api/core/index.md | 3 + packages/core/src/core.ts | 1 + .../core/src/core_render3_private_export.ts | 1 - packages/core/src/render3/definition.ts | 11 +++- packages/core/src/render3/jit/module.ts | 4 +- .../core/test/acceptance/standalone_spec.ts | 66 ++++++++++++++++++- packages/router/src/utils/config.ts | 2 +- 7 files changed, 80 insertions(+), 8 deletions(-) diff --git a/goldens/public-api/core/index.md b/goldens/public-api/core/index.md index 93b8d269dafea..b068908a80f31 100644 --- a/goldens/public-api/core/index.md +++ b/goldens/public-api/core/index.md @@ -787,6 +787,9 @@ export interface InputDecorator { // @public export function isDevMode(): boolean; +// @public +export function isStandalone(type: Type): boolean; + // @public export interface IterableChangeRecord { readonly currentIndex: number | null; diff --git a/packages/core/src/core.ts b/packages/core/src/core.ts index 5a698caa3eabb..ac1fe45cef8db 100644 --- a/packages/core/src/core.ts +++ b/packages/core/src/core.ts @@ -38,6 +38,7 @@ export {SecurityContext} from './sanitization/security'; export {Sanitizer} from './sanitization/sanitizer'; export {createNgModule, createNgModuleRef, createEnvironmentInjector} from './render3/ng_module_ref'; export {createComponent, reflectComponentType, ComponentMirror} from './render3/component'; +export {isStandalone} from './render3/definition'; import {global} from './util/global'; if (typeof ngDevMode !== 'undefined' && ngDevMode) { diff --git a/packages/core/src/core_render3_private_export.ts b/packages/core/src/core_render3_private_export.ts index 84ecfdd973aa7..b9832acda5e9a 100644 --- a/packages/core/src/core_render3_private_export.ts +++ b/packages/core/src/core_render3_private_export.ts @@ -248,7 +248,6 @@ export { export { compilePipe as ɵcompilePipe, } from './render3/jit/pipe'; -export { isStandalone as ɵisStandalone} from './render3/definition'; export { Profiler as ɵProfiler, ProfilerEvent as ɵProfilerEvent } from './render3/profiler'; export { publishDefaultGlobalUtils as ɵpublishDefaultGlobalUtils diff --git a/packages/core/src/render3/definition.ts b/packages/core/src/render3/definition.ts index 7d216319108e3..c32a47d1f31a0 100644 --- a/packages/core/src/render3/definition.ts +++ b/packages/core/src/render3/definition.ts @@ -7,7 +7,6 @@ */ import {ChangeDetectionStrategy} from '../change_detection/constants'; -import {NG_PROV_DEF} from '../di/interface/defs'; import {Mutable, Type} from '../interface/type'; import {NgModuleDef} from '../metadata/ng_module_def'; import {SchemaMetadata} from '../metadata/schema'; @@ -741,7 +740,15 @@ export function getPipeDef(type: any): PipeDef|null { return type[NG_PIPE_DEF] || null; } -export function isStandalone(type: Type): boolean { +/** + * Checks whether a given Component, Directive or Pipe is marked as standalone. + * This will return false if passed anything other than a Component, Directive, or Pipe class + * See this guide for additional information: https://angular.io/guide/standalone-components + * + * @param type A reference to a Component, Directive or Pipe. + * @publicApi + */ +export function isStandalone(type: Type): boolean { const def = getComponentDef(type) || getDirectiveDef(type) || getPipeDef(type); return def !== null ? def.standalone : false; } diff --git a/packages/core/src/render3/jit/module.ts b/packages/core/src/render3/jit/module.ts index 8b511262440b6..33105f463f8a1 100644 --- a/packages/core/src/render3/jit/module.ts +++ b/packages/core/src/render3/jit/module.ts @@ -173,9 +173,7 @@ export function compileNgModuleDefs( Object.defineProperty(moduleType, NG_INJ_DEF, { get: () => { if (ngInjectorDef === null) { - ngDevMode && - verifySemanticsOfNgModuleDef( - moduleType as any as NgModuleType, allowDuplicateDeclarationsInRoot); + ngDevMode && verifySemanticsOfNgModuleDef(moduleType, allowDuplicateDeclarationsInRoot); const meta: R3InjectorMetadataFacade = { name: moduleType.name, type: moduleType, diff --git a/packages/core/test/acceptance/standalone_spec.ts b/packages/core/test/acceptance/standalone_spec.ts index 7f3057848492a..4ee8f144a3f4b 100644 --- a/packages/core/test/acceptance/standalone_spec.ts +++ b/packages/core/test/acceptance/standalone_spec.ts @@ -7,7 +7,7 @@ */ import {CommonModule} from '@angular/common'; -import {Component, createEnvironmentInjector, Directive, EnvironmentInjector, forwardRef, Injector, Input, NgModule, NO_ERRORS_SCHEMA, OnInit, Pipe, PipeTransform, ViewChild, ViewContainerRef} from '@angular/core'; +import {Component, createEnvironmentInjector, Directive, EnvironmentInjector, forwardRef, Injector, Input, isStandalone, NgModule, NO_ERRORS_SCHEMA, OnInit, Pipe, PipeTransform, ViewChild, ViewContainerRef} from '@angular/core'; import {TestBed} from '@angular/core/testing'; describe('standalone components, directives, and pipes', () => { @@ -847,4 +847,68 @@ describe('standalone components, directives, and pipes', () => { expect(fixture.nativeElement.textContent).toBe(''); }); }); + + describe('isStandalone()', () => { + it('should return `true` if component is standalone', () => { + @Component({selector: 'standalone-cmp', standalone: true}) + class StandaloneCmp { + } + + expect(isStandalone(StandaloneCmp)).toBeTrue(); + }); + + it('should return `false` if component is not standalone', () => { + @Component({selector: 'standalone-cmp', standalone: false}) + class StandaloneCmp { + } + + expect(isStandalone(StandaloneCmp)).toBeFalse(); + }); + + it('should return `true` if directive is standalone', () => { + @Directive({selector: '[standaloneDir]', standalone: true}) + class StandAloneDirective { + } + + expect(isStandalone(StandAloneDirective)).toBeTrue(); + }); + + it('should return `false` if directive is standalone', () => { + @Directive({selector: '[standaloneDir]', standalone: false}) + class StandAloneDirective { + } + + expect(isStandalone(StandAloneDirective)).toBeFalse(); + }); + + it('should return `true` if pipe is standalone', () => { + @Pipe({name: 'standalonePipe', standalone: true}) + class StandAlonePipe { + } + + expect(isStandalone(StandAlonePipe)).toBeTrue(); + }); + + it('should return `false` if pipe is standalone', () => { + @Pipe({name: 'standalonePipe', standalone: false}) + class StandAlonePipe { + } + + expect(isStandalone(StandAlonePipe)).toBeFalse(); + }); + + it('should return `false` if the class is not annotated', () => { + class NonAnnotatedClass {} + + expect(isStandalone(NonAnnotatedClass)).toBeFalse(); + }); + + it('should return `false` if the class is an NgModule', () => { + @NgModule({}) + class Module { + } + + expect(isStandalone(Module)).toBeFalse(); + }); + }); }); diff --git a/packages/router/src/utils/config.ts b/packages/router/src/utils/config.ts index 5e674df8ae0e0..87460271d111d 100644 --- a/packages/router/src/utils/config.ts +++ b/packages/router/src/utils/config.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {createEnvironmentInjector, EnvironmentInjector, Type, ɵisStandalone as isStandalone, ɵRuntimeError as RuntimeError} from '@angular/core'; +import {createEnvironmentInjector, EnvironmentInjector, isStandalone, Type, ɵRuntimeError as RuntimeError} from '@angular/core'; import {EmptyOutletComponent} from '../components/empty_outlet'; import {RuntimeErrorCode} from '../errors';