Skip to content

Commit da89071

Browse files
committed
refactor(yok): split the facade surface into subsystem contracts
IInjector recomposes from per-subsystem faces - CommandRegistry, KeyCommandRegistry, ModuleRegistry, PublicApiBuilder - each an @contract token the facade registers itself under. One object still implements everything until the subsystems are physically extracted; extraction then becomes a provider swap for the face's token instead of a consumer migration. Consumers can depend on the narrow face they actually use, and deprecation becomes per-face instead of a flat everything-is-legacy. The contracts are internal (lib/common/contracts) and deliberately not re-exported from nativescript/contracts; promoting one is a per-contract decision.
1 parent 81f542a commit da89071

8 files changed

Lines changed: 132 additions & 101 deletions

File tree

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { Contract } from "../di/contract";
2+
import type { ICommand } from "../definitions/commands";
3+
4+
/**
5+
* The command-registry face of the injector facade. Transitional contract: it
6+
* mirrors what consumers call today, so that extracting the registry from the
7+
* facade later is a provider swap for this token, not a consumer migration.
8+
* Members slated for replacement keep their deprecation markers.
9+
*/
10+
@Contract({ name: "commandRegistry" })
11+
export abstract class CommandRegistry {
12+
/**
13+
* @deprecated Path-based command registration; slated for replacement by
14+
* manifest-declared commands.
15+
*/
16+
abstract requireCommand(names: string | string[], file: string): void;
17+
abstract registerCommand(names: string | string[], resolver: any): void;
18+
abstract resolveCommand(name: string): ICommand;
19+
abstract getRegisteredCommandsNames(includeDev: boolean): string[];
20+
abstract getChildrenCommandsNames(commandName: string): string[];
21+
abstract buildHierarchicalCommand(
22+
parentCommandName: string,
23+
commandLineArguments: string[],
24+
): any;
25+
/** Side-effecting: fails with help output on a bad subcommand. */
26+
abstract isValidHierarchicalCommand(
27+
commandName: string,
28+
commandArguments: string[],
29+
): Promise<boolean>;
30+
abstract isDefaultCommand(commandName: string): boolean;
31+
}

lib/common/contracts/index.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Internal subsystem contracts of the injector facade. Deliberately NOT
2+
// re-exported from nativescript/contracts: promoting one to the public
3+
// surface is a one-line decision that should be made per contract, not by
4+
// default. Each token resolves to the facade itself until its subsystem is
5+
// physically extracted — at which point the provider is swapped and consumers
6+
// keep working unchanged.
7+
export { CommandRegistry } from "./command-registry";
8+
export { KeyCommandRegistry } from "./key-command-registry";
9+
export { ModuleRegistry } from "./module-registry";
10+
export { PublicApiBuilder } from "./public-api-builder";
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { Contract } from "../di/contract";
2+
import type { IKeyCommand, IValidKeyName } from "../definitions/key-commands";
3+
4+
/**
5+
* The key-command face of the injector facade (the `keyCommands.` namespace).
6+
* Kept separate from CommandRegistry because the two registries are redesigned
7+
* on different tracks.
8+
*/
9+
@Contract({ name: "keyCommandRegistry" })
10+
export abstract class KeyCommandRegistry {
11+
abstract requireKeyCommand(name: IValidKeyName, file: string): void;
12+
abstract registerKeyCommand(name: IValidKeyName, resolver: any): void;
13+
abstract resolveKeyCommand(name: string): IKeyCommand;
14+
abstract getRegisteredKeyCommandsNames(): string[];
15+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { Contract } from "../di/contract";
2+
3+
/**
4+
* @deprecated The lazy module-loader face of the injector facade — the
5+
* require-time path map. Wholly replaced by provideLazy(); this contract
6+
* exists so its remaining consumers are typed against exactly what they use
7+
* until the bootstrap migrates.
8+
*/
9+
@Contract({ name: "moduleRegistry" })
10+
export abstract class ModuleRegistry {
11+
abstract require(names: string | string[], file: string): void;
12+
abstract overrideAlreadyRequiredModule: boolean;
13+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { Contract } from "../di/contract";
2+
3+
/**
4+
* The public-API-builder face of the injector facade: the machinery behind
5+
* `require('nativescript')`. Bound by the compatibility constraints of the
6+
* published library surface; do not add new entries through it.
7+
*/
8+
@Contract({ name: "publicApiBuilder" })
9+
export abstract class PublicApiBuilder {
10+
abstract requirePublic(names: string | string[], file: string): void;
11+
abstract requirePublicClass(names: string | string[], file: string): void;
12+
abstract publicApi: any;
13+
}

lib/common/definitions/yok.d.ts

Lines changed: 11 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,24 @@
11
import { IDictionary } from "../declarations";
2-
import { ICommand } from "./commands";
3-
import { IKeyCommand, IValidKeyName } from "./key-commands";
42
import { Injector } from "../di/injector";
53
import { Provider } from "../di/providers";
4+
import { CommandRegistry } from "../contracts/command-registry";
5+
import { KeyCommandRegistry } from "../contracts/key-command-registry";
6+
import { ModuleRegistry } from "../contracts/module-registry";
7+
import { PublicApiBuilder } from "../contracts/public-api-builder";
68

79
/**
810
* The legacy injector facade surface. It extends the token-based `Injector` —
911
* the facade IS an injector — and adds the legacy subsystems, whose members
1012
* are individually @deprecated. Only the `Yok` class hierarchy implements
1113
* this; the interface survives until the hook/extension deprecation completes.
1214
*/
13-
interface IInjector extends Injector {
14-
/**
15-
* @deprecated Use provideLazy() — the same deferred loading, token-based.
16-
*/
17-
require(name: string, file: string): void;
18-
/**
19-
* @deprecated Use provideLazy() — the same deferred loading, token-based.
20-
*/
21-
require(names: string[], file: string): void;
22-
/**
23-
* @deprecated Legacy public-API builder.
24-
*/
25-
requirePublic(names: string | string[], file: string): void;
26-
/**
27-
* @deprecated Legacy public-API builder.
28-
*/
29-
requirePublicClass(names: string | string[], file: string): void;
30-
/**
31-
* @deprecated Path-based command registration; slated for replacement by
32-
* manifest-declared commands.
33-
*/
34-
requireCommand(name: string, file: string): void;
35-
/**
36-
* @deprecated Path-based command registration; slated for replacement by
37-
* manifest-declared commands.
38-
*/
39-
requireCommand(names: string[], file: string): void;
40-
/**
41-
* @deprecated Replaced together with the command registry.
42-
*/
43-
requireKeyCommand(name: IValidKeyName, file: string): void;
15+
interface IInjector
16+
extends
17+
Injector,
18+
CommandRegistry,
19+
KeyCommandRegistry,
20+
ModuleRegistry,
21+
PublicApiBuilder {
4422
/**
4523
* Resolves an implementation by constructor function.
4624
* The injector will create new instances for every call.
@@ -62,43 +40,12 @@ interface IInjector extends Injector {
6240
*/
6341
resolve<T>(name: string, ctorArguments?: IDictionary<any>): T;
6442

65-
/**
66-
* @deprecated Legacy command-registry lookup.
67-
*/
68-
resolveCommand(name: string): ICommand;
69-
/**
70-
* @deprecated Legacy command-registry lookup.
71-
*/
72-
resolveKeyCommand(key: string): IKeyCommand;
7343
/**
7444
* @deprecated Legacy name-based registration. Use the Provider overload or
7545
* provide(); a contract's token name keeps string spellings resolvable.
7646
*/
7747
register(name: string, resolver: any, shared?: boolean): void;
7848
register(providers: Provider | Provider[]): void;
79-
/**
80-
* @deprecated Slated for replacement by defineCommand and manifest-declared
81-
* commands.
82-
*/
83-
registerCommand(name: string, resolver: any): void;
84-
/**
85-
* @deprecated Slated for replacement by defineCommand and manifest-declared
86-
* commands.
87-
*/
88-
registerCommand(names: string[], resolver: any): void;
89-
/**
90-
* @deprecated Replaced together with the command registry.
91-
*/
92-
registerKeyCommand(key: IValidKeyName, resolver: any): void;
93-
/**
94-
* @deprecated Legacy command-registry enumeration; feeds shell
95-
* autocompletion and help.
96-
*/
97-
getRegisteredCommandsNames(includeDev: boolean): string[];
98-
/**
99-
* @deprecated Legacy command-registry enumeration.
100-
*/
101-
getRegisteredKeyCommandsNames(): string[];
10249
/**
10350
* @deprecated String-reflective help templating; removable only together
10451
* with the help-template pipeline.
@@ -108,43 +55,6 @@ interface IInjector extends Injector {
10855
* @deprecated See dynamicCallRegex.
10956
*/
11057
dynamicCall(call: string, args?: any[]): Promise<any>;
111-
/**
112-
* @deprecated Legacy command-registry routing.
113-
*/
114-
isDefaultCommand(commandName: string): boolean;
115-
/**
116-
* @deprecated Legacy command-registry routing.
117-
* Side-effecting: fails with help output on a bad subcommand.
118-
*/
119-
isValidHierarchicalCommand(
120-
commandName: string,
121-
commandArguments: string[],
122-
): Promise<boolean>;
123-
/**
124-
* @deprecated Legacy command-registry routing.
125-
*/
126-
getChildrenCommandsNames(commandName: string): string[];
127-
/**
128-
* @deprecated Hierarchical-routing internals of the legacy command
129-
* registry.
130-
*/
131-
buildHierarchicalCommand(
132-
parentCommandName: string,
133-
commandLineArguments: string[],
134-
): any;
135-
/**
136-
* @deprecated Backing store of the require('nativescript') surface.
137-
* Do not add new entries through it.
138-
*/
139-
publicApi: any;
140-
141-
/**
142-
* Defines if it's allowed to override already required module.
143-
* This can be used in order to allow redefinition of modules, for example $logger can be replaced by a plugin.
144-
* Default value is false.
145-
* @deprecated Escape hatch of the legacy require-time module map.
146-
*/
147-
overrideAlreadyRequiredModule: boolean;
14858
}
14959

15060
/**

lib/common/yok.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ import { ICommandArgument, ICommand } from "./definitions/commands";
1010
import { IKeyCommand, IValidKeyName } from "./definitions/key-commands";
1111
import { Injector } from "./di/injector";
1212
import type { Provider } from "./di/providers";
13+
import {
14+
CommandRegistry,
15+
KeyCommandRegistry,
16+
ModuleRegistry,
17+
PublicApiBuilder,
18+
} from "./contracts";
1319

1420
/**
1521
* The legacy global facade binding. New code should obtain the container via
@@ -63,6 +69,15 @@ export class Yok extends Injector implements IInjector {
6369
constructor() {
6470
super();
6571
this.register("injector", this);
72+
// Each subsystem face resolves to the facade until it is physically
73+
// extracted; extraction then swaps the provider without touching
74+
// consumers of the token.
75+
this.register([
76+
{ provide: CommandRegistry, useValue: this },
77+
{ provide: KeyCommandRegistry, useValue: this },
78+
{ provide: ModuleRegistry, useValue: this },
79+
{ provide: PublicApiBuilder, useValue: this },
80+
]);
6681
}
6782

6883
private COMMANDS_NAMESPACE: string = "commands";

test/compat/injector-facade-surface.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import { assert } from "chai";
22
import { Yok } from "../../lib/common/yok";
33
import { Injector, inject, runInInjectionContext } from "../../lib/common/di";
4+
import {
5+
CommandRegistry,
6+
KeyCommandRegistry,
7+
ModuleRegistry,
8+
PublicApiBuilder,
9+
} from "../../lib/common/contracts";
410

511
// Pins the externally reachable injector surface: every IInjector member
612
// (lib/common/definitions/yok.d.ts) plus dispose, subclassability, the
@@ -84,6 +90,24 @@ describe("injector facade surface", () => {
8490
});
8591
});
8692

93+
it("registers its subsystem faces as tokens that resolve to the facade", () => {
94+
const inj = new Yok();
95+
96+
for (const token of [
97+
CommandRegistry,
98+
KeyCommandRegistry,
99+
ModuleRegistry,
100+
PublicApiBuilder,
101+
]) {
102+
assert.strictEqual(inj.get(<any>token), inj);
103+
}
104+
assert.strictEqual(inj.resolve("commandRegistry"), inj);
105+
106+
runInInjectionContext(inj, () => {
107+
assert.strictEqual(inject(CommandRegistry), inj);
108+
});
109+
});
110+
87111
it("calls lowercase/anonymous resolvers as factories instead of new-ing them", () => {
88112
const inj = new Yok();
89113
inj.register("factoryMade", function () {

0 commit comments

Comments
 (0)