diff --git a/package-lock.json b/package-lock.json index 5812ee6..f442020 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,7 +22,7 @@ "prettier": "^2.3.2", "pretty-quick": "^3.1.3", "rimraf": "^5.0.1", - "rollup": "^3.29.4", + "rollup": "~4.5.0", "ts-jest": "^29.1.1", "ts-node": "^10.9.1", "tsconfig-paths": "^4.2.0", @@ -1755,6 +1755,162 @@ "node": ">=14" } }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.5.0.tgz", + "integrity": "sha512-OINaBGY+Wc++U0rdr7BLuFClxcoWaVW3vQYqmQq6B3bqQ/2olkaoz+K8+af/Mmka/C2yN5j+L9scBkv4BtKsDA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.5.0.tgz", + "integrity": "sha512-UdMf1pOQc4ZmUA/NTmKhgJTBimbSKnhPS2zJqucqFyBRFPnPDtwA8MzrGNTjDeQbIAWfpJVAlxejw+/lQyBK/w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.5.0.tgz", + "integrity": "sha512-L0/CA5p/idVKI+c9PcAPGorH6CwXn6+J0Ys7Gg1axCbTPgI8MeMlhA6fLM9fK+ssFhqogMHFC8HDvZuetOii7w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.5.0.tgz", + "integrity": "sha512-QZCbVqU26mNlLn8zi/XDDquNmvcr4ON5FYAHQQsyhrHx8q+sQi/6xduoznYXwk/KmKIXG5dLfR0CvY+NAWpFYQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.5.0.tgz", + "integrity": "sha512-VpSQ+xm93AeV33QbYslgf44wc5eJGYfYitlQzAi3OObu9iwrGXEnmu5S3ilkqE3Pr/FkgOiJKV/2p0ewf4Hrtg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.5.0.tgz", + "integrity": "sha512-OrEyIfpxSsMal44JpEVx9AEcGpdBQG1ZuWISAanaQTSMeStBW+oHWwOkoqR54bw3x8heP8gBOyoJiGg+fLY8qQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.5.0.tgz", + "integrity": "sha512-1H7wBbQuE6igQdxMSTjtFfD+DGAudcYWhp106z/9zBA8OQhsJRnemO4XGavdzHpGhRtRxbgmUGdO3YQgrWf2RA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.5.0.tgz", + "integrity": "sha512-FVyFI13tXw5aE65sZdBpNjPVIi4Q5mARnL/39UIkxvSgRAIqCo5sCpCELk0JtXHGee2owZz5aNLbWNfBHzr71Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.5.0.tgz", + "integrity": "sha512-eBPYl2sLpH/o8qbSz6vPwWlDyThnQjJfcDOGFbNjmjb44XKC1F5dQfakOsADRVrXCNzM6ZsSIPDG5dc6HHLNFg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.5.0.tgz", + "integrity": "sha512-xaOHIfLOZypoQ5U2I6rEaugS4IYtTgP030xzvrBf5js7p9WI9wik07iHmsKaej8Z83ZDxN5GyypfoyKV5O5TJA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.5.0.tgz", + "integrity": "sha512-Al6quztQUrHwcOoU2TuFblUQ5L+/AmPBXFR6dUvyo4nRj2yQRK0WIUaGMF/uwKulvRcXkpHe3k9A8Vf93VDktA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.5.0.tgz", + "integrity": "sha512-8kdW+brNhI/NzJ4fxDufuJUjepzINqJKLGHuxyAtpPG9bMbn8P5mtaCcbOm0EzLJ+atg+kF9dwg8jpclkVqx5w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -6946,18 +7102,30 @@ } }, "node_modules/rollup": { - "version": "3.29.4", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", - "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.5.0.tgz", + "integrity": "sha512-41xsWhzxqjMDASCxH5ibw1mXk+3c4TNI2UjKbLxe6iEzrSQnqOzmmK8/3mufCPbzHNJ2e04Fc1ddI35hHy+8zg==", "dev": true, "bin": { "rollup": "dist/bin/rollup" }, "engines": { - "node": ">=14.18.0", + "node": ">=18.0.0", "npm": ">=8.0.0" }, "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.5.0", + "@rollup/rollup-android-arm64": "4.5.0", + "@rollup/rollup-darwin-arm64": "4.5.0", + "@rollup/rollup-darwin-x64": "4.5.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.5.0", + "@rollup/rollup-linux-arm64-gnu": "4.5.0", + "@rollup/rollup-linux-arm64-musl": "4.5.0", + "@rollup/rollup-linux-x64-gnu": "4.5.0", + "@rollup/rollup-linux-x64-musl": "4.5.0", + "@rollup/rollup-win32-arm64-msvc": "4.5.0", + "@rollup/rollup-win32-ia32-msvc": "4.5.0", + "@rollup/rollup-win32-x64-msvc": "4.5.0", "fsevents": "~2.3.2" } }, @@ -9410,6 +9578,90 @@ "dev": true, "optional": true }, + "@rollup/rollup-android-arm-eabi": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.5.0.tgz", + "integrity": "sha512-OINaBGY+Wc++U0rdr7BLuFClxcoWaVW3vQYqmQq6B3bqQ/2olkaoz+K8+af/Mmka/C2yN5j+L9scBkv4BtKsDA==", + "dev": true, + "optional": true + }, + "@rollup/rollup-android-arm64": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.5.0.tgz", + "integrity": "sha512-UdMf1pOQc4ZmUA/NTmKhgJTBimbSKnhPS2zJqucqFyBRFPnPDtwA8MzrGNTjDeQbIAWfpJVAlxejw+/lQyBK/w==", + "dev": true, + "optional": true + }, + "@rollup/rollup-darwin-arm64": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.5.0.tgz", + "integrity": "sha512-L0/CA5p/idVKI+c9PcAPGorH6CwXn6+J0Ys7Gg1axCbTPgI8MeMlhA6fLM9fK+ssFhqogMHFC8HDvZuetOii7w==", + "dev": true, + "optional": true + }, + "@rollup/rollup-darwin-x64": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.5.0.tgz", + "integrity": "sha512-QZCbVqU26mNlLn8zi/XDDquNmvcr4ON5FYAHQQsyhrHx8q+sQi/6xduoznYXwk/KmKIXG5dLfR0CvY+NAWpFYQ==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.5.0.tgz", + "integrity": "sha512-VpSQ+xm93AeV33QbYslgf44wc5eJGYfYitlQzAi3OObu9iwrGXEnmu5S3ilkqE3Pr/FkgOiJKV/2p0ewf4Hrtg==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-arm64-gnu": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.5.0.tgz", + "integrity": "sha512-OrEyIfpxSsMal44JpEVx9AEcGpdBQG1ZuWISAanaQTSMeStBW+oHWwOkoqR54bw3x8heP8gBOyoJiGg+fLY8qQ==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-arm64-musl": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.5.0.tgz", + "integrity": "sha512-1H7wBbQuE6igQdxMSTjtFfD+DGAudcYWhp106z/9zBA8OQhsJRnemO4XGavdzHpGhRtRxbgmUGdO3YQgrWf2RA==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-x64-gnu": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.5.0.tgz", + "integrity": "sha512-FVyFI13tXw5aE65sZdBpNjPVIi4Q5mARnL/39UIkxvSgRAIqCo5sCpCELk0JtXHGee2owZz5aNLbWNfBHzr71Q==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-x64-musl": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.5.0.tgz", + "integrity": "sha512-eBPYl2sLpH/o8qbSz6vPwWlDyThnQjJfcDOGFbNjmjb44XKC1F5dQfakOsADRVrXCNzM6ZsSIPDG5dc6HHLNFg==", + "dev": true, + "optional": true + }, + "@rollup/rollup-win32-arm64-msvc": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.5.0.tgz", + "integrity": "sha512-xaOHIfLOZypoQ5U2I6rEaugS4IYtTgP030xzvrBf5js7p9WI9wik07iHmsKaej8Z83ZDxN5GyypfoyKV5O5TJA==", + "dev": true, + "optional": true + }, + "@rollup/rollup-win32-ia32-msvc": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.5.0.tgz", + "integrity": "sha512-Al6quztQUrHwcOoU2TuFblUQ5L+/AmPBXFR6dUvyo4nRj2yQRK0WIUaGMF/uwKulvRcXkpHe3k9A8Vf93VDktA==", + "dev": true, + "optional": true + }, + "@rollup/rollup-win32-x64-msvc": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.5.0.tgz", + "integrity": "sha512-8kdW+brNhI/NzJ4fxDufuJUjepzINqJKLGHuxyAtpPG9bMbn8P5mtaCcbOm0EzLJ+atg+kF9dwg8jpclkVqx5w==", + "dev": true, + "optional": true + }, "@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -13158,11 +13410,23 @@ } }, "rollup": { - "version": "3.29.4", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", - "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==", - "dev": true, - "requires": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.5.0.tgz", + "integrity": "sha512-41xsWhzxqjMDASCxH5ibw1mXk+3c4TNI2UjKbLxe6iEzrSQnqOzmmK8/3mufCPbzHNJ2e04Fc1ddI35hHy+8zg==", + "dev": true, + "requires": { + "@rollup/rollup-android-arm-eabi": "4.5.0", + "@rollup/rollup-android-arm64": "4.5.0", + "@rollup/rollup-darwin-arm64": "4.5.0", + "@rollup/rollup-darwin-x64": "4.5.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.5.0", + "@rollup/rollup-linux-arm64-gnu": "4.5.0", + "@rollup/rollup-linux-arm64-musl": "4.5.0", + "@rollup/rollup-linux-x64-gnu": "4.5.0", + "@rollup/rollup-linux-x64-musl": "4.5.0", + "@rollup/rollup-win32-arm64-msvc": "4.5.0", + "@rollup/rollup-win32-ia32-msvc": "4.5.0", + "@rollup/rollup-win32-x64-msvc": "4.5.0", "fsevents": "~2.3.2" } }, diff --git a/package.json b/package.json index 8287397..22c23fd 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "prettier": "^2.3.2", "pretty-quick": "^3.1.3", "rimraf": "^5.0.1", - "rollup": "^3.29.4", + "rollup": "~4.5.0", "ts-jest": "^29.1.1", "ts-node": "^10.9.1", "tsconfig-paths": "^4.2.0", diff --git a/readme.md b/readme.md index 7bdefbc..a54359e 100644 --- a/readme.md +++ b/readme.md @@ -3,9 +3,9 @@ - 这是 Angualr 依赖注入的独立版本,初始版本逻辑为从 Angular 源码中提取出来,并修复了一些已知 bug - 使用方法与 Angular 的依赖注入完全一致 # 来源 -- Angular 16.2.2 +- Angular 17.0.4 # 兼容 -- ts 5.1.6 +- ts 5.2.2 # 使用方法 - 以`Injector.create`创建第一级依赖注入器 diff --git a/src/import/di/initializer_token.ts b/src/import/di/initializer_token.ts index cb3ab76..068dae3 100644 --- a/src/import/di/initializer_token.ts +++ b/src/import/di/initializer_token.ts @@ -14,6 +14,6 @@ import { InjectionToken } from './injection_token'; * * @publicApi */ -export const ENVIRONMENT_INITIALIZER = new InjectionToken<() => void>( - 'ENVIRONMENT_INITIALIZER' -); +export const ENVIRONMENT_INITIALIZER = new InjectionToken< + ReadonlyArray<() => void> +>('ENVIRONMENT_INITIALIZER'); diff --git a/src/import/di/injection_token.ts b/src/import/di/injection_token.ts index 89aa669..30e8033 100644 --- a/src/import/di/injection_token.ts +++ b/src/import/di/injection_token.ts @@ -20,11 +20,17 @@ import { ɵɵdefineInjectable } from './interface/defs'; * `InjectionToken` is parameterized on `T` which is the type of object which will be returned by * the `Injector`. This provides an additional level of type safety. * - * ``` - * interface MyInterface {...} - * const myInterface = injector.get(new InjectionToken('SomeToken')); - * // myInterface is inferred to be MyInterface. - * ``` + *
+ * + * **Important Note**: Ensure that you use the same instance of the `InjectionToken` in both the + * provider and the injection call. Creating a new instance of `InjectionToken` in different places, + * even with the same description, will be treated as different tokens by Angular's DI system, + * leading to a `NullInjectorError`. + * + *
+ * + * * * When creating an `InjectionToken`, you can optionally specify a factory function which returns * (possibly by creating) a default value of the parameterized type `T`. This sets up the @@ -51,7 +57,6 @@ import { ɵɵdefineInjectable } from './interface/defs'; * * {@example core/di/ts/injector_spec.ts region='ShakableInjectionToken'} * - * * @publicApi */ export class InjectionToken { diff --git a/src/import/di/internal_tokens.ts b/src/import/di/internal_tokens.ts index 7172d52..bdf0c93 100644 --- a/src/import/di/internal_tokens.ts +++ b/src/import/di/internal_tokens.ts @@ -10,6 +10,6 @@ import { Type } from '../interface/type'; import { InjectionToken } from './injection_token'; -export const INJECTOR_DEF_TYPES = new InjectionToken>( - 'INJECTOR_DEF_TYPES' -); +export const INJECTOR_DEF_TYPES = new InjectionToken< + ReadonlyArray> +>('INJECTOR_DEF_TYPES'); diff --git a/src/import/di/r3_injector.ts b/src/import/di/r3_injector.ts index 3b0911a..9d20276 100644 --- a/src/import/di/r3_injector.ts +++ b/src/import/di/r3_injector.ts @@ -47,6 +47,7 @@ import { isEnvironmentProviders, Provider, StaticClassProvider, + TypeProvider, } from './interface/provider'; import { INJECTOR_DEF_TYPES } from './internal_tokens'; import { NullInjector } from './null_injector'; @@ -227,7 +228,7 @@ export class R3Injector extends EnvironmentInjector { } this.injectorDefTypes = new Set( - this.get(INJECTOR_DEF_TYPES.multi, EMPTY_ARRAY, InjectFlags.Self) + this.get(INJECTOR_DEF_TYPES, EMPTY_ARRAY, InjectFlags.Self) ); } @@ -315,6 +316,10 @@ export class R3Injector extends EnvironmentInjector { if (def && this.injectableDefInScope(def)) { // Found an injectable def and it's scoped to this injector. Pretend as if it was here // all along. + + if (false) { + } + record = makeRecord( injectableDefOrInjectorDefFactory(token), NOT_YET @@ -373,7 +378,7 @@ export class R3Injector extends EnvironmentInjector { try { const initializers = this.get( - ENVIRONMENT_INITIALIZER.multi, + ENVIRONMENT_INITIALIZER, EMPTY_ARRAY, InjectFlags.Self ); diff --git a/src/import/errors.ts b/src/import/errors.ts index 2f9379b..ca02090 100644 --- a/src/import/errors.ts +++ b/src/import/errors.ts @@ -31,6 +31,7 @@ export const enum RuntimeErrorCode { EXPRESSION_CHANGED_AFTER_CHECKED = -100, RECURSIVE_APPLICATION_REF_TICK = 101, RECURSIVE_APPLICATION_RENDER = 102, + INFINITE_CHANGE_DETECTION = 103, // Dependency Injection Errors CYCLIC_DI_DEPENDENCY = -200, @@ -54,7 +55,7 @@ export const enum RuntimeErrorCode { INVALID_EVENT_BINDING = 306, HOST_DIRECTIVE_UNRESOLVABLE = 307, HOST_DIRECTIVE_NOT_STANDALONE = 308, - DUPLICATE_DIRECTITVE = 309, + DUPLICATE_DIRECTIVE = 309, HOST_DIRECTIVE_COMPONENT = 310, HOST_DIRECTIVE_UNDEFINED_BINDING = 311, HOST_DIRECTIVE_CONFLICTING_ALIAS = 312, @@ -83,6 +84,7 @@ export const enum RuntimeErrorCode { // Signal Errors SIGNAL_WRITE_FROM_ILLEGAL_CONTEXT = 600, REQUIRE_SYNC_WITHOUT_SYNC_EMIT = 601, + ASSERTION_NOT_INSIDE_REACTIVE_CONTEXT = -602, // Styling Errors @@ -92,6 +94,9 @@ export const enum RuntimeErrorCode { INVALID_I18N_STRUCTURE = 700, MISSING_LOCALE_DATA = 701, + // Defer errors (750-799 range) + DEFER_LOADING_FAILED = 750, + // standalone errors IMPORT_PROVIDERS_FROM_STANDALONE = 800, @@ -110,9 +115,11 @@ export const enum RuntimeErrorCode { UNSAFE_IFRAME_ATTRS = -910, VIEW_ALREADY_DESTROYED = 911, COMPONENT_ID_COLLISION = -912, + IMAGE_PERFORMANCE_WARNING = -913, // Runtime dependency tracker errors RUNTIME_DEPS_INVALID_IMPORTED_TYPE = 1000, + RUNTIME_DEPS_ORPHAN_COMPONENT = 1001, } /** diff --git a/src/import/render3/util/stringify_utils.ts b/src/import/render3/util/stringify_utils.ts index 55773b1..67203ca 100644 --- a/src/import/render3/util/stringify_utils.ts +++ b/src/import/render3/util/stringify_utils.ts @@ -6,6 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ +import { Type } from '../../interface/type'; + /** * Used for stringify render output in Ivy. * Important! This function is very performance-sensitive and we should @@ -22,6 +24,7 @@ export function renderStringify(value: any): string { /** * Used to stringify a value so that it can be displayed in an error message. + * * Important! This function contains a megamorphic read and should only be * used for error messages. */ diff --git a/src/import/util/stringify.ts b/src/import/util/stringify.ts index cdda337..74dade5 100644 --- a/src/import/util/stringify.ts +++ b/src/import/util/stringify.ts @@ -36,3 +36,20 @@ export function stringify(token: any): string { const newLineIndex = res.indexOf('\n'); return newLineIndex === -1 ? res : res.substring(0, newLineIndex); } + +/** + * Ellipses the string in the middle when longer than the max length + * + * @param string + * @param maxLength of the output string + * @returns elispsed string with ... in the middle + */ +export function truncateMiddle(str: string, maxLength = 100): string { + if (!str || maxLength < 1 || str.length <= maxLength) return str; + if (maxLength == 1) return str.substring(0, 1) + '...'; + + const halfLimit = Math.round(maxLength / 2); + return ( + str.substring(0, halfLimit) + '...' + str.substring(str.length - halfLimit) + ); +} diff --git a/src/package.json b/src/package.json index 4c208a3..5a89dd7 100644 --- a/src/package.json +++ b/src/package.json @@ -1,12 +1,13 @@ { "name": "static-injector", - "version": "2.2.1", + "version": "3.0.0", "description": "Angular的静态注入器独立使用版本", "keywords": [ "angular", + "angular 17.0.4", "injector", "typescript", - "typescript 5.1.6", + "typescript 5.2.2", "injectable", "static-inject" ], diff --git a/src/transform/compiler-cli/src/ngtsc/annotations/common/src/di.ts b/src/transform/compiler-cli/src/ngtsc/annotations/common/src/di.ts index 32ca015..adbfaff 100644 --- a/src/transform/compiler-cli/src/ngtsc/annotations/common/src/di.ts +++ b/src/transform/compiler-cli/src/ngtsc/annotations/common/src/di.ts @@ -10,6 +10,7 @@ import { Expression, LiteralExpr, R3DependencyMetadata, + ReadPropExpr, WrappedNodeExpr, } from 'static-injector/transform/compiler'; import ts from 'typescript'; @@ -27,6 +28,7 @@ import { UnavailableValue, ValueUnavailableKind, } from '../../../reflection'; +import { CompilationMode } from '../../../transform'; import { isAngularCore, valueReferenceToExpression } from './util'; @@ -48,7 +50,8 @@ export interface ConstructorDepError { export function getConstructorDependencies( clazz: ClassDeclaration, reflector: ReflectionHost, - isCore: boolean + isCore: boolean, + compilationMode: CompilationMode ): ConstructorDeps | null { const deps: R3DependencyMetadata[] = []; const errors: ConstructorDepError[] = []; @@ -61,7 +64,31 @@ export function getConstructorDependencies( } } ctorParams.forEach((param, idx) => { - let token = valueReferenceToExpression(param.typeValueReference); + let token: Expression | null = null; + + if ( + compilationMode === CompilationMode.LOCAL && + param.typeValueReference.kind === TypeValueReferenceKind.UNAVAILABLE && + param.typeValueReference.reason.kind !== ValueUnavailableKind.MISSING_TYPE + ) { + // The case of local compilation where injection token cannot be resolved because it is + // "probably" imported from another file + + const typeNode = param.typeValueReference.reason.typeNode; + + if (ts.isTypeReferenceNode(typeNode)) { + // Here we manually create the token out of the typeName without caring about its + // references for better TS tracking. This is because in this code path the typeNode is + // imported from another file and since we are in local compilation mode (=single file + // mode) the reference of this node (or its typeName node) cannot be resolved. So all we + // can do is just to create a new expression. + token = toQualifiedExpression(typeNode.typeName); + } + } else { + // In all other cases resolve the injection token + token = valueReferenceToExpression(param.typeValueReference); + } + let attributeNameType: Expression | null = null; let optional = false, self = false, @@ -123,6 +150,18 @@ export function getConstructorDependencies( } } +/** Converts a TS qualified name to output expression. */ +function toQualifiedExpression(entity: ts.EntityName): Expression { + if (ts.isIdentifier(entity)) { + return new WrappedNodeExpr(entity); + } + + return new ReadPropExpr( + toQualifiedExpression(entity.left), + entity.right.text + ); +} + /** * Convert `ConstructorDeps` into the `R3DependencyMetadata` array for those deps if they're valid, * or into an `'invalid'` signal if they're not. @@ -146,11 +185,12 @@ export function unwrapConstructorDependencies( export function getValidConstructorDependencies( clazz: ClassDeclaration, reflector: ReflectionHost, - isCore: boolean + isCore: boolean, + compilationMode: CompilationMode ): R3DependencyMetadata[] | null { return validateConstructorDependencies( clazz, - getConstructorDependencies(clazz, reflector, isCore) + getConstructorDependencies(clazz, reflector, isCore, compilationMode) ); } diff --git a/src/transform/compiler-cli/src/ngtsc/annotations/src/injectable.ts b/src/transform/compiler-cli/src/ngtsc/annotations/src/injectable.ts index e949468..8e8ad57 100644 --- a/src/transform/compiler-cli/src/ngtsc/annotations/src/injectable.ts +++ b/src/transform/compiler-cli/src/ngtsc/annotations/src/injectable.ts @@ -31,6 +31,7 @@ import { } from '../../reflection'; import { AnalysisOutput, + CompilationMode, CompileResult, DecoratorHandler, DetectResult, @@ -67,6 +68,7 @@ export class InjectableDecoratorHandler private strictCtorDeps: boolean, private includeClassMetadata: boolean, + private readonly compilationMode: CompilationMode, /** * What to do if the injectable already contains a ɵprov property. * @@ -114,7 +116,8 @@ export class InjectableDecoratorHandler decorator, this.reflector, this.isCore, - this.strictCtorDeps + this.strictCtorDeps, + this.compilationMode ), // Avoid generating multiple factories if a class has @@ -305,7 +308,8 @@ function extractInjectableCtorDeps( decorator: Decorator, reflector: ReflectionHost, isCore: boolean, - strictCtorDeps: boolean + strictCtorDeps: boolean, + compilationMode: CompilationMode ) { if (decorator.args === null) { throw new FatalDiagnosticError( @@ -327,16 +331,26 @@ function extractInjectableCtorDeps( // constructor signature does not work for DI then a factory definition (ɵfac) that throws is // generated. if (strictCtorDeps && !isAbstractClassDeclaration(clazz)) { - ctorDeps = getValidConstructorDependencies(clazz, reflector, isCore); + ctorDeps = getValidConstructorDependencies( + clazz, + reflector, + isCore, + compilationMode + ); } else { ctorDeps = unwrapConstructorDependencies( - getConstructorDependencies(clazz, reflector, isCore) + getConstructorDependencies(clazz, reflector, isCore, compilationMode) ); } return ctorDeps; } else if (decorator.args.length === 1) { - const rawCtorDeps = getConstructorDependencies(clazz, reflector, isCore); + const rawCtorDeps = getConstructorDependencies( + clazz, + reflector, + isCore, + compilationMode + ); if ( strictCtorDeps && diff --git a/src/transform/compiler-cli/src/ngtsc/transform/src/api.ts b/src/transform/compiler-cli/src/ngtsc/transform/src/api.ts index 92d2463..2a4ff07 100644 --- a/src/transform/compiler-cli/src/ngtsc/transform/src/api.ts +++ b/src/transform/compiler-cli/src/ngtsc/transform/src/api.ts @@ -15,6 +15,27 @@ import ts from 'typescript'; import { ClassDeclaration, Decorator, ReflectionHost } from '../../reflection'; +/** + * Specifies the compilation mode that is used for the compilation. + */ +export enum CompilationMode { + /** + * Generates fully AOT compiled code using Ivy instructions. + */ + FULL, + + /** + * Generates code using a stable, but intermediate format suitable to be published to NPM. + */ + PARTIAL, + + /** + * Generates code based on each individual source file without using its + * dependencies (suitable for local dev edit/refresh workflow). + */ + LOCAL, +} + /** * Provides the interface between a decorator compiler from @angular/compiler and the Typescript * compiler/transform. diff --git a/src/transform/compiler-cli/src/ngtsc/translator/src/api/ast_factory.ts b/src/transform/compiler-cli/src/ngtsc/translator/src/api/ast_factory.ts index b5e5e29..2822c00 100644 --- a/src/transform/compiler-cli/src/ngtsc/translator/src/api/ast_factory.ts +++ b/src/transform/compiler-cli/src/ngtsc/translator/src/api/ast_factory.ts @@ -131,6 +131,18 @@ export interface AstFactory { body: TStatement ): TExpression; + /** + * Create an expression that represents an arrow function + * (e.g. `(param1, param2) => body`). + * + * @param parameters the names of the function's parameters. + * @param body an expression or block of statements that are the body of the function. + */ + createArrowFunctionExpression( + parameters: string[], + body: TExpression | TStatement + ): TExpression; + /** * Creates an expression that represents a dynamic import * (e.g. `import('./some/path')`) diff --git a/src/transform/compiler-cli/src/ngtsc/translator/src/translator.ts b/src/transform/compiler-cli/src/ngtsc/translator/src/translator.ts index e0459e4..c661b21 100644 --- a/src/transform/compiler-cli/src/ngtsc/translator/src/translator.ts +++ b/src/transform/compiler-cli/src/ngtsc/translator/src/translator.ts @@ -298,6 +298,15 @@ export class ExpressionTranslatorVisitor ); } + visitArrowFunctionExpr(ast: o.ArrowFunctionExpr, context: any) { + return this.factory.createArrowFunctionExpression( + ast.params.map((param) => param.name), + Array.isArray(ast.body) + ? this.factory.createBlock(this.visitStatements(ast.body, context)) + : ast.body.visitExpression(this, context) + ); + } + visitBinaryOperatorExpr( ast: o.BinaryOperatorExpr, context: Context diff --git a/src/transform/compiler-cli/src/ngtsc/translator/src/typescript_ast_factory.ts b/src/transform/compiler-cli/src/ngtsc/translator/src/typescript_ast_factory.ts index eb07a1e..35074f4 100644 --- a/src/transform/compiler-cli/src/ngtsc/translator/src/typescript_ast_factory.ts +++ b/src/transform/compiler-cli/src/ngtsc/translator/src/typescript_ast_factory.ts @@ -194,6 +194,28 @@ export class TypeScriptAstFactory ); } + createArrowFunctionExpression( + parameters: string[], + body: ts.Statement | ts.Expression + ): ts.Expression { + if (ts.isStatement(body) && !ts.isBlock(body)) { + throw new Error( + `Invalid syntax, expected a block, but got ${ts.SyntaxKind[body.kind]}.` + ); + } + + return ts.factory.createArrowFunction( + undefined, + undefined, + parameters.map((param) => + ts.factory.createParameterDeclaration(undefined, undefined, param) + ), + undefined, + undefined, + body + ); + } + createIdentifier = ts.factory.createIdentifier; createIfStatement( diff --git a/src/transform/compiler/src/injectable_compiler_2.ts b/src/transform/compiler/src/injectable_compiler_2.ts index 6ba6ad7..0fa1ee3 100644 --- a/src/transform/compiler/src/injectable_compiler_2.ts +++ b/src/transform/compiler/src/injectable_compiler_2.ts @@ -100,10 +100,7 @@ export function compileInjectable( } else { result = { statements: [], - expression: o.fn( - [], - [new o.ReturnStatement(meta.useFactory.callFn([]))] - ), + expression: o.arrowFn([], meta.useFactory.callFn([])), }; } } else if (meta.useValue !== undefined) { @@ -202,9 +199,9 @@ function delegateToFactory( return createFactoryFunction(unwrappedType); } -function createFactoryFunction(type: o.Expression): o.FunctionExpr { - return o.fn( +function createFactoryFunction(type: o.Expression): o.ArrowFunctionExpr { + return o.arrowFn( [new o.FnParam('t', o.DYNAMIC_TYPE)], - [new o.ReturnStatement(type.prop('ɵfac').callFn([o.variable('t')]))] + type.prop('ɵfac').callFn([o.variable('t')]) ); } diff --git a/src/transform/compiler/src/output/output_ast.ts b/src/transform/compiler/src/output/output_ast.ts index 195c9c7..94fcdd4 100644 --- a/src/transform/compiler/src/output/output_ast.ts +++ b/src/transform/compiler/src/output/output_ast.ts @@ -795,9 +795,9 @@ export class FunctionExpr extends Expression { super(type, sourceSpan); } - override isEquivalent(e: Expression): boolean { + override isEquivalent(e: Expression | Statement): boolean { return ( - e instanceof FunctionExpr && + (e instanceof FunctionExpr || e instanceof DeclareFunctionStmt) && areAllEquivalent(this.params, e.params) && areAllEquivalent(this.statements, e.statements) ); @@ -834,6 +834,67 @@ export class FunctionExpr extends Expression { } } +export class ArrowFunctionExpr extends Expression { + // Note that `body: Expression` represents `() => expr` whereas + // `body: Statement[]` represents `() => { expr }`. + + constructor( + public params: FnParam[], + public body: Expression | Statement[], + type?: Type | null, + sourceSpan?: ParseSourceSpan | null + ) { + super(type, sourceSpan); + } + + override isEquivalent(e: Expression): boolean { + if ( + !(e instanceof ArrowFunctionExpr) || + !areAllEquivalent(this.params, e.params) + ) { + return false; + } + + if (this.body instanceof Expression && e.body instanceof Expression) { + return this.body.isEquivalent(e.body); + } + + if (Array.isArray(this.body) && Array.isArray(e.body)) { + return areAllEquivalent(this.body, e.body); + } + + return false; + } + + override isConstant(): boolean { + return false; + } + + override visitExpression(visitor: ExpressionVisitor, context: any) { + return visitor.visitArrowFunctionExpr(this, context); + } + + override clone(): Expression { + // TODO: Should we deep clone statements? + return new ArrowFunctionExpr( + this.params.map((p) => p.clone()), + Array.isArray(this.body) ? this.body : this.body.clone(), + this.type, + this.sourceSpan + ); + } + + toDeclStmt(name: string, modifiers?: StmtModifier): DeclareVarStmt { + return new DeclareVarStmt( + name, + this, + INFERRED_TYPE, + modifiers, + this.sourceSpan + ); + } +} + export class UnaryOperatorExpr extends Expression { constructor( public operator: UnaryOperator, @@ -1141,6 +1202,7 @@ export interface ExpressionVisitor { visitCommaExpr(ast: CommaExpr, context: any): any; visitWrappedNodeExpr(ast: WrappedNodeExpr, context: any): any; visitTypeofExpr(ast: TypeofExpr, context: any): any; + visitArrowFunctionExpr(ast: ArrowFunctionExpr, context: any): any; } export const NULL_EXPR = new LiteralExpr(null, null, null); @@ -1420,6 +1482,15 @@ export function fn( return new FunctionExpr(params, body, type, sourceSpan, name); } +export function arrowFn( + params: FnParam[], + body: Expression | Statement[], + type?: Type | null, + sourceSpan?: ParseSourceSpan | null +) { + return new ArrowFunctionExpr(params, body, type, sourceSpan); +} + export function ifStmt( condition: Expression, thenClause: Statement[], diff --git a/src/transform/compiler/src/render3/r3_factory.ts b/src/transform/compiler/src/render3/r3_factory.ts index 48978cd..4479775 100644 --- a/src/transform/compiler/src/render3/r3_factory.ts +++ b/src/transform/compiler/src/render3/r3_factory.ts @@ -201,7 +201,7 @@ export function compileFactoryFunction( // There is a base factory variable so wrap its declaration along with the factory function into // an IIFE. factoryFn = o - .fn( + .arrowFn( [], [ new o.DeclareVarStmt(baseFactoryVar.name!), diff --git a/src/transform/compiler/src/render3/util.ts b/src/transform/compiler/src/render3/util.ts index 123b905..f04ac51 100644 --- a/src/transform/compiler/src/render3/util.ts +++ b/src/transform/compiler/src/render3/util.ts @@ -112,9 +112,7 @@ export function convertFromMaybeForwardRefExpression({ * ``` */ export function generateForwardRef(expr: o.Expression): o.Expression { - return o - .importExpr(Identifiers.forwardRef) - .callFn([o.fn([], [new o.ReturnStatement(expr)])]); + return o.importExpr(Identifiers.forwardRef).callFn([o.arrowFn([], expr)]); } /** diff --git a/src/transform/compiler/src/render3/view/util.ts b/src/transform/compiler/src/render3/view/util.ts index 7034202..87e7b48 100644 --- a/src/transform/compiler/src/render3/view/util.ts +++ b/src/transform/compiler/src/render3/view/util.ts @@ -8,6 +8,9 @@ import * as o from '../../output/output_ast'; +/** Special value representing a direct access to a template's context. */ +export const DIRECT_CONTEXT_REFERENCE = '#context'; + /** * A representation for an object literal used during codegen of definition objects. The generic * type `T` allows to reference a documented type of the generated structure, such that the @@ -18,7 +21,13 @@ export class DefinitionMap { set(key: keyof T, value: o.Expression | null): void { if (value) { - this.values.push({ key: key as string, value, quoted: false }); + const existing = this.values.find((value) => value.key === key); + + if (existing) { + existing.value = value; + } else { + this.values.push({ key: key as string, value, quoted: false }); + } } } diff --git a/src/transform/injectable-transform.ts b/src/transform/injectable-transform.ts index 3843d5e..8032b71 100644 --- a/src/transform/injectable-transform.ts +++ b/src/transform/injectable-transform.ts @@ -5,7 +5,11 @@ import { Decorator, TypeScriptReflectionHost, } from './compiler-cli/src/ngtsc/reflection'; -import { addImports, CompileResult } from './compiler-cli/src/ngtsc/transform'; +import { + addImports, + CompilationMode, + CompileResult, +} from './compiler-cli/src/ngtsc/transform'; import { ImportManager, translateExpression, @@ -47,7 +51,8 @@ export class InjectableTransformerFactory { this.reflectionHost, false, !!this.options.strictCtorDeps, - false + false, + CompilationMode.FULL ); } getTransform() {