@travetto/registry 7.0.0-rc.1 → 7.0.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -17
- package/package.json +4 -4
- package/src/internal/commonjs-loader.ts +11 -11
- package/src/internal/file-loader.ts +12 -12
- package/src/proxy.ts +17 -17
- package/src/service/registry.ts +15 -15
- package/src/service/store.ts +1 -1
- package/src/source/class-source.ts +43 -43
- package/src/source/method-source.ts +19 -15
- package/src/types.ts +4 -4
package/README.md
CHANGED
|
@@ -54,13 +54,13 @@ class SampleRegistryAdapter implements RegistryAdapter<Group> {
|
|
|
54
54
|
this.#class = cls;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
register(...
|
|
58
|
-
for (const
|
|
57
|
+
register(...groups: Partial<Partial<Group>>[]): Group {
|
|
58
|
+
for (const group of groups) {
|
|
59
59
|
Object.assign(this.#config, {
|
|
60
|
-
...
|
|
60
|
+
...group,
|
|
61
61
|
children: [
|
|
62
62
|
...(this.#config?.children ?? []),
|
|
63
|
-
...(
|
|
63
|
+
...(group.children ?? [])
|
|
64
64
|
]
|
|
65
65
|
});
|
|
66
66
|
}
|
|
@@ -115,12 +115,12 @@ As mentioned in [Manifest](https://github.com/travetto/travetto/tree/main/module
|
|
|
115
115
|
const next = new Map<string, Class>(classes.map(cls => [cls.Ⲑid, cls] as const));
|
|
116
116
|
const sourceFile = RuntimeIndex.getSourceFile(importFile);
|
|
117
117
|
|
|
118
|
-
let
|
|
118
|
+
let previous = new Map<string, Class>();
|
|
119
119
|
if (this.#classes.has(sourceFile)) {
|
|
120
|
-
|
|
120
|
+
previous = new Map(this.#classes.get(sourceFile)!.entries());
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
const keys = new Set([...Array.from(
|
|
123
|
+
const keys = new Set([...Array.from(previous.keys()), ...Array.from(next.keys())]);
|
|
124
124
|
|
|
125
125
|
if (!this.#classes.has(sourceFile)) {
|
|
126
126
|
this.#classes.set(sourceFile, new Map());
|
|
@@ -129,22 +129,22 @@ As mentioned in [Manifest](https://github.com/travetto/travetto/tree/main/module
|
|
|
129
129
|
let changes = 0;
|
|
130
130
|
|
|
131
131
|
// Determine delta based on the various classes (if being added, removed or updated)
|
|
132
|
-
for (const
|
|
133
|
-
if (!next.has(
|
|
132
|
+
for (const key of keys) {
|
|
133
|
+
if (!next.has(key)) {
|
|
134
134
|
changes += 1;
|
|
135
|
-
this.emit({ type: 'removing',
|
|
136
|
-
this.#classes.get(sourceFile)!.delete(
|
|
135
|
+
this.emit({ type: 'removing', previous: previous.get(key)! });
|
|
136
|
+
this.#classes.get(sourceFile)!.delete(key);
|
|
137
137
|
} else {
|
|
138
|
-
this.#classes.get(sourceFile)!.set(
|
|
139
|
-
if (!
|
|
138
|
+
this.#classes.get(sourceFile)!.set(key, next.get(key)!);
|
|
139
|
+
if (!previous.has(key)) {
|
|
140
140
|
changes += 1;
|
|
141
|
-
this.emit({ type: 'added',
|
|
141
|
+
this.emit({ type: 'added', current: next.get(key)! });
|
|
142
142
|
} else {
|
|
143
|
-
const prevHash = describeFunction(
|
|
144
|
-
const nextHash = describeFunction(next.get(
|
|
143
|
+
const prevHash = describeFunction(previous.get(key)!)?.hash;
|
|
144
|
+
const nextHash = describeFunction(next.get(key)!)?.hash;
|
|
145
145
|
if (prevHash !== nextHash) {
|
|
146
146
|
changes += 1;
|
|
147
|
-
this.emit({ type: 'changed',
|
|
147
|
+
this.emit({ type: 'changed', current: next.get(key)!, previous: previous.get(key)! });
|
|
148
148
|
}
|
|
149
149
|
}
|
|
150
150
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/registry",
|
|
3
|
-
"version": "7.0.0-rc.
|
|
3
|
+
"version": "7.0.0-rc.2",
|
|
4
4
|
"description": "Patterns and utilities for handling registration of metadata and functionality for run-time use",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ast-transformations",
|
|
@@ -27,11 +27,11 @@
|
|
|
27
27
|
"directory": "module/registry"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@travetto/runtime": "^7.0.0-rc.
|
|
30
|
+
"@travetto/runtime": "^7.0.0-rc.2"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
|
-
"@travetto/cli": "^7.0.0-rc.
|
|
34
|
-
"@travetto/transformer": "^7.0.0-rc.
|
|
33
|
+
"@travetto/cli": "^7.0.0-rc.2",
|
|
34
|
+
"@travetto/transformer": "^7.0.0-rc.2"
|
|
35
35
|
},
|
|
36
36
|
"peerDependenciesMeta": {
|
|
37
37
|
"@travetto/transformer": {
|
|
@@ -7,9 +7,9 @@ import { RetargettingProxy } from '../proxy.ts';
|
|
|
7
7
|
|
|
8
8
|
declare module 'module' {
|
|
9
9
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
10
|
-
function _resolveFilename(
|
|
10
|
+
function _resolveFilename(request: string, parent: typeof Module): string;
|
|
11
11
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
12
|
-
function _load(
|
|
12
|
+
function _load(request: string, parent: typeof Module): unknown;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
type ModuleLoader = typeof Module['_load'];
|
|
@@ -30,22 +30,22 @@ export class DynamicCommonjsLoader {
|
|
|
30
30
|
let mod: unknown;
|
|
31
31
|
try {
|
|
32
32
|
mod = moduleLoad.apply(null, [request, parent]);
|
|
33
|
-
} catch (
|
|
33
|
+
} catch (error: unknown) {
|
|
34
34
|
const name = Module._resolveFilename!(request, parent);
|
|
35
|
-
if (
|
|
36
|
-
const
|
|
37
|
-
console.debug(`Unable to load ${name}: stubbing out with error proxy.`,
|
|
38
|
-
const
|
|
39
|
-
mod = new Proxy({}, { getOwnPropertyDescriptor:
|
|
35
|
+
if (error instanceof Error && Runtime.dynamic && !name.startsWith('test/')) {
|
|
36
|
+
const message = error.message;
|
|
37
|
+
console.debug(`Unable to load ${name}: stubbing out with error proxy.`, message);
|
|
38
|
+
const fail = (): never => { throw new Error(message); };
|
|
39
|
+
mod = new Proxy({}, { getOwnPropertyDescriptor: fail, get: fail, has: fail });
|
|
40
40
|
} else {
|
|
41
|
-
throw
|
|
41
|
+
throw error;
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
const file = Module._resolveFilename!(request, parent);
|
|
46
|
-
const
|
|
46
|
+
const source = RuntimeIndex.getEntry(file)?.sourceFile;
|
|
47
47
|
// Only proxy workspace modules
|
|
48
|
-
if (
|
|
48
|
+
if (source && RuntimeIndex.getModuleFromSource(source)?.workspace) {
|
|
49
49
|
return proxyModuleLoad ? proxyModuleLoad(file, mod) : mod;
|
|
50
50
|
} else {
|
|
51
51
|
return mod;
|
|
@@ -3,11 +3,11 @@ import { watchCompiler, WatchEvent, Runtime, RuntimeIndex } from '@travetto/runt
|
|
|
3
3
|
|
|
4
4
|
const VALID_FILE_TYPES = new Set(['js', 'ts']);
|
|
5
5
|
|
|
6
|
-
const handle = (
|
|
7
|
-
if (
|
|
8
|
-
console.error('Cannot find module', { error
|
|
6
|
+
const handle = (error: Error): void => {
|
|
7
|
+
if (error && (error.message ?? '').includes('Cannot find module')) { // Handle module reloading
|
|
8
|
+
console.error('Cannot find module', { error });
|
|
9
9
|
} else {
|
|
10
|
-
throw
|
|
10
|
+
throw error;
|
|
11
11
|
}
|
|
12
12
|
};
|
|
13
13
|
|
|
@@ -27,19 +27,19 @@ export class DynamicFileLoader {
|
|
|
27
27
|
.on('uncaughtException', handle);
|
|
28
28
|
|
|
29
29
|
// Fire off, and let it run in the bg. Restart on exit
|
|
30
|
-
for await (const
|
|
31
|
-
if (
|
|
32
|
-
if (
|
|
33
|
-
await loader.unload(
|
|
30
|
+
for await (const event of watchCompiler({ restartOnExit: true })) {
|
|
31
|
+
if (event.file && RuntimeIndex.hasModule(event.module) && VALID_FILE_TYPES.has(ManifestModuleUtil.getFileType(event.file))) {
|
|
32
|
+
if (event.action === 'update' || event.action === 'delete') {
|
|
33
|
+
await loader.unload(event.output);
|
|
34
34
|
}
|
|
35
|
-
if (
|
|
35
|
+
if (event.action === 'create' || event.action === 'delete') {
|
|
36
36
|
RuntimeIndex.reinitForModule(Runtime.main.name);
|
|
37
37
|
}
|
|
38
|
-
if (
|
|
39
|
-
await loader.load(
|
|
38
|
+
if (event.action === 'create' || event.action === 'update') {
|
|
39
|
+
await loader.load(event.output);
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
yield
|
|
42
|
+
yield event;
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
}
|
package/src/proxy.ts
CHANGED
|
@@ -7,8 +7,8 @@ const AsyncGeneratorFunction = Object.getPrototypeOf(async function* () { });
|
|
|
7
7
|
const GeneratorFunction = Object.getPrototypeOf(function* () { });
|
|
8
8
|
const AsyncFunction = Object.getPrototypeOf(async function () { });
|
|
9
9
|
|
|
10
|
-
function isFunction(
|
|
11
|
-
const proto =
|
|
10
|
+
function isFunction(value: unknown): value is Function {
|
|
11
|
+
const proto = value && Object.getPrototypeOf(value);
|
|
12
12
|
return proto && (proto === Function.prototype || proto === AsyncFunction || proto === AsyncGeneratorFunction || proto === GeneratorFunction);
|
|
13
13
|
}
|
|
14
14
|
|
|
@@ -43,19 +43,19 @@ export class RetargettingHandler<T> implements ProxyHandler<Any> {
|
|
|
43
43
|
return classConstruct(castTo(this.target), argArray);
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
setPrototypeOf(target: T,
|
|
47
|
-
return Object.setPrototypeOf(this.target, castTo(
|
|
46
|
+
setPrototypeOf(target: T, value: unknown): boolean {
|
|
47
|
+
return Object.setPrototypeOf(this.target, castTo(value));
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
getPrototypeOf(target: T): object | null {
|
|
51
51
|
return Object.getPrototypeOf(this.target);
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
get(target: T,
|
|
55
|
-
if (
|
|
54
|
+
get(target: T, property: PropertyKey, receiver: unknown): Any {
|
|
55
|
+
if (property === ProxyTargetSymbol) {
|
|
56
56
|
return this.target;
|
|
57
57
|
}
|
|
58
|
-
let result = this.target[castKey<T>(
|
|
58
|
+
let result = this.target[castKey<T>(property)];
|
|
59
59
|
if (isFunction(result) && !/^class\s/.test(Function.prototype.toString.call(result))) {
|
|
60
60
|
// Bind class members to class instance instead of proxy propagating
|
|
61
61
|
result = result.bind(this.target);
|
|
@@ -63,12 +63,12 @@ export class RetargettingHandler<T> implements ProxyHandler<Any> {
|
|
|
63
63
|
return result;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
has(target: T,
|
|
67
|
-
return castTo<object>(this.target).hasOwnProperty(
|
|
66
|
+
has(target: T, property: PropertyKey): boolean {
|
|
67
|
+
return castTo<object>(this.target).hasOwnProperty(property);
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
set(target: T,
|
|
71
|
-
this.target[castKey<T>(
|
|
70
|
+
set(target: T, property: PropertyKey, value: unknown): boolean {
|
|
71
|
+
this.target[castKey<T>(property)] = castTo(value);
|
|
72
72
|
return true;
|
|
73
73
|
}
|
|
74
74
|
|
|
@@ -79,12 +79,12 @@ export class RetargettingHandler<T> implements ProxyHandler<Any> {
|
|
|
79
79
|
.concat(Object.getOwnPropertySymbols(this.target));
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
deleteProperty(target: T,
|
|
83
|
-
return delete this.target[castKey<T>(
|
|
82
|
+
deleteProperty(target: T, property: PropertyKey): boolean {
|
|
83
|
+
return delete this.target[castKey<T>(property)];
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
defineProperty(target: T,
|
|
87
|
-
Object.defineProperty(this.target,
|
|
86
|
+
defineProperty(target: T, property: PropertyKey, attributes: PropertyDescriptor): boolean {
|
|
87
|
+
Object.defineProperty(this.target, property, attributes);
|
|
88
88
|
return true;
|
|
89
89
|
}
|
|
90
90
|
}
|
|
@@ -99,8 +99,8 @@ export class RetargettingProxy<T> {
|
|
|
99
99
|
/**
|
|
100
100
|
* Unwrap proxy
|
|
101
101
|
*/
|
|
102
|
-
static unwrap<U>(
|
|
103
|
-
return castTo<{ [ProxyTargetSymbol]: U }>(
|
|
102
|
+
static unwrap<U>(value: U): U {
|
|
103
|
+
return castTo<{ [ProxyTargetSymbol]: U }>(value)?.[ProxyTargetSymbol] ?? value;
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
#handler: RetargettingHandler<T>;
|
package/src/service/registry.ts
CHANGED
|
@@ -11,7 +11,7 @@ class $Registry {
|
|
|
11
11
|
#resolved = false;
|
|
12
12
|
#initialized?: Promise<unknown>;
|
|
13
13
|
trace = false;
|
|
14
|
-
#
|
|
14
|
+
#uniqueId = Util.uuid();
|
|
15
15
|
|
|
16
16
|
// Lookups
|
|
17
17
|
#indexes = new Map<RegistryIndexClass, RegistryIndex>();
|
|
@@ -53,22 +53,22 @@ class $Registry {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
process(events: ChangeEvent<Class>[]): void {
|
|
56
|
-
this.#finalizeItems(events.filter(
|
|
56
|
+
this.#finalizeItems(events.filter(event => 'current' in event).map(event => event.current));
|
|
57
57
|
|
|
58
58
|
for (const indexCls of this.#indexOrder) { // Visit every index, in order
|
|
59
59
|
const inst = this.instance(indexCls);
|
|
60
|
-
const matched = events.filter(
|
|
60
|
+
const matched = events.filter(event => inst.store.has('current' in event ? event.current : event.previous!));
|
|
61
61
|
if (matched.length) {
|
|
62
62
|
inst.process(matched);
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
Util.queueMacroTask().then(() => {
|
|
67
|
-
this.#removeItems(events.filter(
|
|
67
|
+
this.#removeItems(events.filter(event => 'previous' in event).map(event => event.previous!));
|
|
68
68
|
});
|
|
69
69
|
|
|
70
|
-
for (const
|
|
71
|
-
this.#emitter.emit('event',
|
|
70
|
+
for (const event of events) {
|
|
71
|
+
this.#emitter.emit('event', event);
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
74
|
|
|
@@ -80,12 +80,12 @@ class $Registry {
|
|
|
80
80
|
this.#resolved = false;
|
|
81
81
|
|
|
82
82
|
if (this.trace) {
|
|
83
|
-
console.debug('Initializing', {
|
|
83
|
+
console.debug('Initializing', { uniqueId: this.#uniqueId });
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
const added = await this.#classSource.init();
|
|
87
|
-
this.process(added.map(cls => ({ type: 'added',
|
|
88
|
-
this.#classSource.on(
|
|
87
|
+
this.process(added.map(cls => ({ type: 'added', current: cls })));
|
|
88
|
+
this.#classSource.on(event => this.process([event]));
|
|
89
89
|
} finally {
|
|
90
90
|
this.#resolved = true;
|
|
91
91
|
}
|
|
@@ -116,7 +116,7 @@ class $Registry {
|
|
|
116
116
|
*/
|
|
117
117
|
async init(): Promise<unknown> {
|
|
118
118
|
if (this.trace && this.#initialized) {
|
|
119
|
-
console.trace('Trying to re-initialize', {
|
|
119
|
+
console.trace('Trying to re-initialize', { uniqueId: this.#uniqueId, initialized: !!this.#initialized });
|
|
120
120
|
}
|
|
121
121
|
return this.#initialized ??= this.#init();
|
|
122
122
|
}
|
|
@@ -134,7 +134,7 @@ class $Registry {
|
|
|
134
134
|
} else {
|
|
135
135
|
const inst = this.instance(matches);
|
|
136
136
|
this.#emitter.on('event', (event) => {
|
|
137
|
-
if (inst.store.has('
|
|
137
|
+
if (inst.store.has('current' in event ? event.current : event.previous!)) {
|
|
138
138
|
handler(event);
|
|
139
139
|
}
|
|
140
140
|
});
|
|
@@ -149,13 +149,13 @@ class $Registry {
|
|
|
149
149
|
handler: (event: ChangeEvent<[Class, Function]>) => void,
|
|
150
150
|
matches?: RegistryIndexClass,
|
|
151
151
|
): void {
|
|
152
|
-
const
|
|
152
|
+
const emitter = this.#methodSource ??= new MethodSource(this.#classSource);
|
|
153
153
|
if (!matches) {
|
|
154
|
-
|
|
154
|
+
emitter.on(handler);
|
|
155
155
|
} else {
|
|
156
156
|
const inst = this.instance(matches);
|
|
157
|
-
|
|
158
|
-
if (inst.store.has('
|
|
157
|
+
emitter.on((event) => {
|
|
158
|
+
if (inst.store.has('current' in event ? event.current[0] : event.previous[0])) {
|
|
159
159
|
handler(event);
|
|
160
160
|
}
|
|
161
161
|
});
|
package/src/service/store.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { EXPIRED_CLASS, RegistrationMethods, RegistryAdapter } from './types';
|
|
|
5
5
|
function ExchangeExpired<R = unknown>() {
|
|
6
6
|
return function (
|
|
7
7
|
target: Any,
|
|
8
|
-
propertyKey: string
|
|
8
|
+
propertyKey: string,
|
|
9
9
|
descriptor: TypedPropertyDescriptor<(this: RegistryIndexStore, cls: Class) => R>
|
|
10
10
|
): void {
|
|
11
11
|
if (Runtime.dynamic) {
|
|
@@ -27,7 +27,7 @@ export class ClassSource implements ChangeSource<Class> {
|
|
|
27
27
|
/**
|
|
28
28
|
* Are we in a mode that should have enhanced debug info
|
|
29
29
|
*/
|
|
30
|
-
trace = Env.DEBUG.
|
|
30
|
+
trace = Env.DEBUG.value?.includes('@travetto/registry');
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
33
|
* Flush classes
|
|
@@ -35,12 +35,12 @@ export class ClassSource implements ChangeSource<Class> {
|
|
|
35
35
|
#flush(): Class[] {
|
|
36
36
|
const flushed = flushPendingFunctions().filter(isClass);
|
|
37
37
|
for (const cls of flushed) {
|
|
38
|
-
const
|
|
39
|
-
if (!this.#classes.has(
|
|
40
|
-
this.#classes.set(
|
|
38
|
+
const source = Runtime.getSourceFile(cls);
|
|
39
|
+
if (!this.#classes.has(source)) {
|
|
40
|
+
this.#classes.set(source, new Map());
|
|
41
41
|
}
|
|
42
|
-
this.#classes.get(
|
|
43
|
-
this.emit({ type: 'added',
|
|
42
|
+
this.#classes.get(source)!.set(cls.Ⲑid, cls);
|
|
43
|
+
this.emit({ type: 'added', current: cls });
|
|
44
44
|
}
|
|
45
45
|
return flushed;
|
|
46
46
|
}
|
|
@@ -50,7 +50,7 @@ export class ClassSource implements ChangeSource<Class> {
|
|
|
50
50
|
if (data) {
|
|
51
51
|
this.#classes.delete(file);
|
|
52
52
|
for (const cls of data) {
|
|
53
|
-
this.emit({ type: 'removing',
|
|
53
|
+
this.emit({ type: 'removing', previous: cls[1] });
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
}
|
|
@@ -62,12 +62,12 @@ export class ClassSource implements ChangeSource<Class> {
|
|
|
62
62
|
const next = new Map<string, Class>(classes.map(cls => [cls.Ⲑid, cls] as const));
|
|
63
63
|
const sourceFile = RuntimeIndex.getSourceFile(importFile);
|
|
64
64
|
|
|
65
|
-
let
|
|
65
|
+
let previous = new Map<string, Class>();
|
|
66
66
|
if (this.#classes.has(sourceFile)) {
|
|
67
|
-
|
|
67
|
+
previous = new Map(this.#classes.get(sourceFile)!.entries());
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
const keys = new Set([...Array.from(
|
|
70
|
+
const keys = new Set([...Array.from(previous.keys()), ...Array.from(next.keys())]);
|
|
71
71
|
|
|
72
72
|
if (!this.#classes.has(sourceFile)) {
|
|
73
73
|
this.#classes.set(sourceFile, new Map());
|
|
@@ -76,22 +76,22 @@ export class ClassSource implements ChangeSource<Class> {
|
|
|
76
76
|
let changes = 0;
|
|
77
77
|
|
|
78
78
|
// Determine delta based on the various classes (if being added, removed or updated)
|
|
79
|
-
for (const
|
|
80
|
-
if (!next.has(
|
|
79
|
+
for (const key of keys) {
|
|
80
|
+
if (!next.has(key)) {
|
|
81
81
|
changes += 1;
|
|
82
|
-
this.emit({ type: 'removing',
|
|
83
|
-
this.#classes.get(sourceFile)!.delete(
|
|
82
|
+
this.emit({ type: 'removing', previous: previous.get(key)! });
|
|
83
|
+
this.#classes.get(sourceFile)!.delete(key);
|
|
84
84
|
} else {
|
|
85
|
-
this.#classes.get(sourceFile)!.set(
|
|
86
|
-
if (!
|
|
85
|
+
this.#classes.get(sourceFile)!.set(key, next.get(key)!);
|
|
86
|
+
if (!previous.has(key)) {
|
|
87
87
|
changes += 1;
|
|
88
|
-
this.emit({ type: 'added',
|
|
88
|
+
this.emit({ type: 'added', current: next.get(key)! });
|
|
89
89
|
} else {
|
|
90
|
-
const prevHash = describeFunction(
|
|
91
|
-
const nextHash = describeFunction(next.get(
|
|
90
|
+
const prevHash = describeFunction(previous.get(key)!)?.hash;
|
|
91
|
+
const nextHash = describeFunction(next.get(key)!)?.hash;
|
|
92
92
|
if (prevHash !== nextHash) {
|
|
93
93
|
changes += 1;
|
|
94
|
-
this.emit({ type: 'changed',
|
|
94
|
+
this.emit({ type: 'changed', current: next.get(key)!, previous: previous.get(key)! });
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
97
|
}
|
|
@@ -104,17 +104,17 @@ export class ClassSource implements ChangeSource<Class> {
|
|
|
104
104
|
*/
|
|
105
105
|
#handleChanges(classes: Class[] = []): void {
|
|
106
106
|
const classesByFile = new Map<string, Class[]>();
|
|
107
|
-
for (const
|
|
108
|
-
const
|
|
109
|
-
if (!classesByFile.has(
|
|
110
|
-
classesByFile.set(
|
|
107
|
+
for (const cls of classes) {
|
|
108
|
+
const importPath = Runtime.getImport(cls);
|
|
109
|
+
if (!classesByFile.has(importPath)) {
|
|
110
|
+
classesByFile.set(importPath, []);
|
|
111
111
|
}
|
|
112
|
-
classesByFile.get(
|
|
112
|
+
classesByFile.get(importPath)!.push(cls);
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
for (const [
|
|
116
|
-
if (!this.#handleFileChanges(
|
|
117
|
-
this.#emitter.emit('unchanged-import',
|
|
115
|
+
for (const [importPath, items] of classesByFile.entries()) {
|
|
116
|
+
if (!this.#handleFileChanges(importPath, items)) {
|
|
117
|
+
this.#emitter.emit('unchanged-import', importPath);
|
|
118
118
|
}
|
|
119
119
|
}
|
|
120
120
|
}
|
|
@@ -122,15 +122,15 @@ export class ClassSource implements ChangeSource<Class> {
|
|
|
122
122
|
/**
|
|
123
123
|
* Emit a change event
|
|
124
124
|
*/
|
|
125
|
-
emit(
|
|
125
|
+
emit(event: ChangeEvent<Class>): void {
|
|
126
126
|
if (this.trace) {
|
|
127
127
|
console.debug('Emitting change', {
|
|
128
|
-
type:
|
|
129
|
-
|
|
130
|
-
|
|
128
|
+
type: event.type,
|
|
129
|
+
current: (event.type !== 'removing' ? event.current?.Ⲑid : undefined),
|
|
130
|
+
previous: (event.type !== 'added' ? event.previous?.Ⲑid : undefined)
|
|
131
131
|
});
|
|
132
132
|
}
|
|
133
|
-
this.#emitter.emit('change',
|
|
133
|
+
this.#emitter.emit('change', event);
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
/**
|
|
@@ -139,14 +139,14 @@ export class ClassSource implements ChangeSource<Class> {
|
|
|
139
139
|
async init(): Promise<Class[]> {
|
|
140
140
|
if (Runtime.dynamic && !this.#listening) {
|
|
141
141
|
this.#listening = (async (): Promise<void> => {
|
|
142
|
-
for await (const
|
|
143
|
-
if (
|
|
144
|
-
this.#removeFile(
|
|
142
|
+
for await (const event of await DynamicFileLoader.listen()) {
|
|
143
|
+
if (event.action === 'delete') {
|
|
144
|
+
this.#removeFile(event.file); // File no longer exists
|
|
145
145
|
} else {
|
|
146
146
|
this.#handleChanges(flushPendingFunctions().filter(isClass));
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
-
if (
|
|
149
|
+
if (event.action === 'create') {
|
|
150
150
|
this.#flush();
|
|
151
151
|
}
|
|
152
152
|
}
|
|
@@ -155,16 +155,16 @@ export class ClassSource implements ChangeSource<Class> {
|
|
|
155
155
|
|
|
156
156
|
// Ensure everything is loaded
|
|
157
157
|
for (const entry of RuntimeIndex.find({
|
|
158
|
-
module: (
|
|
159
|
-
const role = Env.TRV_ROLE.
|
|
158
|
+
module: (mod) => {
|
|
159
|
+
const role = Env.TRV_ROLE.value;
|
|
160
160
|
return role !== 'test' && // Skip all modules when in test
|
|
161
|
-
|
|
161
|
+
mod.roles.includes('std') &&
|
|
162
162
|
(
|
|
163
|
-
!Runtime.production ||
|
|
164
|
-
(role === 'doc' &&
|
|
163
|
+
!Runtime.production || mod.prod ||
|
|
164
|
+
(role === 'doc' && mod.roles.includes(role))
|
|
165
165
|
);
|
|
166
166
|
},
|
|
167
|
-
folder:
|
|
167
|
+
folder: folder => folder === 'src' || folder === '$index'
|
|
168
168
|
})) {
|
|
169
169
|
await Runtime.importFrom(entry.import);
|
|
170
170
|
}
|
|
@@ -16,39 +16,43 @@ export class MethodSource implements ChangeSource<[Class, Function]> {
|
|
|
16
16
|
* Define parent change source, generally will be the class source
|
|
17
17
|
*/
|
|
18
18
|
constructor(classSource: ChangeSource<Class>) {
|
|
19
|
-
classSource.on(
|
|
19
|
+
classSource.on(event => this.onClassEvent(event));
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
async init(): Promise<void> { }
|
|
23
23
|
|
|
24
|
-
emit(
|
|
25
|
-
this.#emitter.emit('change',
|
|
24
|
+
emit(event: ChangeEvent<[Class, Function]>): void {
|
|
25
|
+
this.#emitter.emit('change', event);
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
29
|
* On a class being emitted, check methods
|
|
30
30
|
*/
|
|
31
|
-
onClassEvent(
|
|
32
|
-
const next = (
|
|
33
|
-
const
|
|
31
|
+
onClassEvent(event: ChangeEvent<Class>): void {
|
|
32
|
+
const next = (event.type !== 'removing' ? describeFunction(event.current!)?.methods : null) ?? {};
|
|
33
|
+
const previous = (event.type !== 'added' ? describeFunction(event.previous!)?.methods : null) ?? {};
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
36
|
* Go through each method, comparing hashes. To see added/removed and changed
|
|
37
37
|
*/
|
|
38
|
-
for (const
|
|
39
|
-
if ((!
|
|
40
|
-
this.emit({ type: 'added',
|
|
41
|
-
} else if (next[
|
|
42
|
-
// FIXME: Why is
|
|
43
|
-
this.emit({
|
|
38
|
+
for (const key of Object.keys(next)) {
|
|
39
|
+
if ((!previous[key] || !('previous' in event)) && event.type !== 'removing') {
|
|
40
|
+
this.emit({ type: 'added', current: [event.current!, event.current!.prototype[key]] });
|
|
41
|
+
} else if (next[key].hash !== previous[key].hash && event.type === 'changed') {
|
|
42
|
+
// FIXME: Why is event.previous undefined sometimes?
|
|
43
|
+
this.emit({
|
|
44
|
+
type: 'changed',
|
|
45
|
+
current: [event.current, event.current.prototype[key]],
|
|
46
|
+
previous: [event.previous, event.previous.prototype[key]]
|
|
47
|
+
});
|
|
44
48
|
} else {
|
|
45
49
|
// Method Unchanged
|
|
46
50
|
}
|
|
47
51
|
}
|
|
48
52
|
|
|
49
|
-
for (const
|
|
50
|
-
if (!next[
|
|
51
|
-
this.emit({ type: 'removing',
|
|
53
|
+
for (const key of Object.keys(previous)) {
|
|
54
|
+
if (!next[key] && event.type !== 'added') {
|
|
55
|
+
this.emit({ type: 'removing', previous: [event.previous, event.previous.prototype[key]] });
|
|
52
56
|
}
|
|
53
57
|
}
|
|
54
58
|
}
|
package/src/types.ts
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
* A change event
|
|
3
3
|
*/
|
|
4
4
|
export type ChangeEvent<T> =
|
|
5
|
-
{ type: 'changed',
|
|
6
|
-
{ type: 'added',
|
|
7
|
-
{ type: 'removing',
|
|
5
|
+
{ type: 'changed', previous: T, current: T } |
|
|
6
|
+
{ type: 'added', current: T } |
|
|
7
|
+
{ type: 'removing', previous: T };
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Change handler
|
|
11
11
|
*/
|
|
12
|
-
export type ChangeHandler<T> = (
|
|
12
|
+
export type ChangeHandler<T> = (event: ChangeEvent<T>) => unknown;
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Change source
|