/
githubmirror
/
angular
Обзор
Документация
Войти
/
githubmirror
/
angular
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
packages/compiler/src/injectable_compiler_2.ts
188 строк
6 KB
Kristiyan Kostadinov
feat(core): introduce @Service decorator
22 апр 2026, 21:01
22 апр 2026, 21:01
8f3d0b9
Код
Авторство
О чём код?
/** * @license * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.dev/license */ import {FactoryTarget} from './compiler_facade_interface'; import * as o from './output/output_ast'; import { compileFactoryFunction, R3DependencyMetadata, R3FactoryDelegateType, R3FactoryMetadata, } from './render3/r3_factory'; import {Identifiers} from './render3/r3_identifiers'; import { convertFromMaybeForwardRefExpression, MaybeForwardRefExpression, R3CompiledExpression, R3Reference, typeWithParameters, } from './render3/util'; import {DefinitionMap} from './render3/view/util'; export interface R3InjectableMetadata { name: string; type: R3Reference; typeArgumentCount: number; providedIn: MaybeForwardRefExpression; useClass?: MaybeForwardRefExpression; useFactory?: o.Expression; useExisting?: MaybeForwardRefExpression; useValue?: MaybeForwardRefExpression; deps?: R3DependencyMetadata[]; } export function compileInjectable( meta: R3InjectableMetadata, resolveForwardRefs: boolean, ): R3CompiledExpression { let result: {expression: o.Expression; statements: o.Statement[]} | null = null; const factoryMeta: R3FactoryMetadata = { name: meta.name, type: meta.type, typeArgumentCount: meta.typeArgumentCount, deps: [], target: FactoryTarget.Injectable, }; if (meta.useClass !== undefined) { // meta.useClass has two modes of operation. Either deps are specified, in which case `new` is // used to instantiate the class with dependencies injected, or deps are not specified and // the factory of the class is used to instantiate it. // // A special case exists for useClass: Type where Type is the injectable type itself and no // deps are specified, in which case 'useClass' is effectively ignored. const useClassOnSelf = meta.useClass.expression.isEquivalent(meta.type.value); let deps: R3DependencyMetadata[] | undefined = undefined; if (meta.deps !== undefined) { deps = meta.deps; } if (deps !== undefined) { // factory: () => new meta.useClass(...deps) result = compileFactoryFunction({ ...factoryMeta, delegate: meta.useClass.expression, delegateDeps: deps, delegateType: R3FactoryDelegateType.Class, }); } else if (useClassOnSelf) { result = compileFactoryFunction(factoryMeta); } else { result = { statements: [], expression: delegateToFactory( meta.type.value as o.WrappedNodeExpr<any>, meta.useClass.expression as o.WrappedNodeExpr<any>, resolveForwardRefs, ), }; } } else if (meta.useFactory !== undefined) { if (meta.deps !== undefined) { result = compileFactoryFunction({ ...factoryMeta, delegate: meta.useFactory, delegateDeps: meta.deps || [], delegateType: R3FactoryDelegateType.Function, }); } else { result = {statements: [], expression: o.arrowFn([], meta.useFactory.callFn([]))}; } } else if (meta.useValue !== undefined) { // Note: it's safe to use `meta.useValue` instead of the `USE_VALUE in meta` check used for // client code because meta.useValue is an Expression which will be defined even if the actual // value is undefined. result = compileFactoryFunction({ ...factoryMeta, expression: meta.useValue.expression, }); } else if (meta.useExisting !== undefined) { // useExisting is an `inject` call on the existing token. result = compileFactoryFunction({ ...factoryMeta, expression: o.importExpr(Identifiers.inject).callFn([meta.useExisting.expression]), }); } else { result = { statements: [], expression: delegateToFactory( meta.type.value as o.WrappedNodeExpr<any>, meta.type.value as o.WrappedNodeExpr<any>, resolveForwardRefs, ), }; } const token = meta.type.value; const injectableProps = new DefinitionMap<{ token: o.Expression; factory: o.Expression; providedIn: o.Expression; }>(); injectableProps.set('token', token); injectableProps.set('factory', result.expression); // Only generate providedIn property if it has a non-null value if ((meta.providedIn.expression as o.LiteralExpr).value !== null) { injectableProps.set('providedIn', convertFromMaybeForwardRefExpression(meta.providedIn)); } const expression = o .importExpr(Identifiers.ɵɵdefineInjectable) .callFn([injectableProps.toLiteralMap()], undefined, true); return { expression, type: createInjectableType(meta.type.type, meta.typeArgumentCount), statements: result.statements, }; } export function createInjectableType(type: o.Expression, typeArgumentCount: number) { return new o.ExpressionType( o.importExpr(Identifiers.InjectableDeclaration, [typeWithParameters(type, typeArgumentCount)]), ); } export function delegateToFactory( type: o.WrappedNodeExpr<any>, useType: o.WrappedNodeExpr<any>, unwrapForwardRefs: boolean, ): o.Expression { if (type.node === useType.node) { // The types are the same, so we can simply delegate directly to the type's factory. // ``` // factory: type.ɵfac // ``` return useType.prop('ɵfac'); } if (!unwrapForwardRefs) { // The type is not wrapped in a `forwardRef()`, so we create a simple factory function that // accepts a sub-type as an argument. // ``` // factory: function(t) { return useType.ɵfac(t); } // ``` return createFactoryFunction(useType); } // The useType is actually wrapped in a `forwardRef()` so we need to resolve that before // calling its factory. // ``` // factory: function(t) { return core.resolveForwardRef(type).ɵfac(t); } // ``` const unwrappedType = o.importExpr(Identifiers.resolveForwardRef).callFn([useType]); return createFactoryFunction(unwrappedType); } function createFactoryFunction(type: o.Expression): o.ArrowFunctionExpr { const t = new o.FnParam('__ngFactoryType__', o.DYNAMIC_TYPE); return o.arrowFn([t], type.prop('ɵfac').callFn([o.variable(t.name)])); }