@travetto/runtime 6.0.0-rc.1 → 6.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 +7 -7
- package/__index__.ts +19 -19
- package/package.json +3 -3
- package/src/binary.ts +27 -28
- package/src/console.ts +10 -2
- package/src/context.ts +9 -9
- package/src/debug.ts +2 -2
- package/src/env.ts +1 -1
- package/src/error.ts +1 -1
- package/src/exec.ts +15 -11
- package/src/file-loader.ts +9 -1
- package/src/function.ts +4 -2
- package/src/global.d.ts +24 -0
- package/src/queue.ts +2 -4
- package/src/resources.ts +3 -3
- package/src/shutdown.ts +12 -8
- package/src/trv.d.ts +2 -2
- package/src/types.ts +4 -0
- package/src/util.ts +47 -16
- package/src/watch.ts +5 -5
- package/support/transformer/metadata.ts +104 -0
- package/support/transformer.concrete-type.ts +87 -0
- package/support/transformer.console-log.ts +1 -1
- package/support/transformer.debug-method.ts +2 -2
- package/support/transformer.dynamic-import.ts +2 -9
- package/support/transformer.function-metadata.ts +21 -91
- package/support/transformer.rewrite-path-import.ts +2 -2
- package/support/transformer.type-helpers.ts +0 -25
package/README.md
CHANGED
|
@@ -57,7 +57,7 @@ class $Runtime {
|
|
|
57
57
|
/** Produce a workspace path for tooling, with '@' being replaced by node_module/name folder */
|
|
58
58
|
toolPath(...rel: string[]): string;
|
|
59
59
|
/** Resolve single module path */
|
|
60
|
-
modulePath(modulePath: string): string;
|
|
60
|
+
modulePath(modulePath: string, overrides?: Record<string, string>): string;
|
|
61
61
|
/** Resolve resource paths */
|
|
62
62
|
resourcePaths(paths: string[] = []): string[];
|
|
63
63
|
/** Get source for function */
|
|
@@ -125,7 +125,7 @@ interface EnvData {
|
|
|
125
125
|
/**
|
|
126
126
|
* trvc log level
|
|
127
127
|
*/
|
|
128
|
-
TRV_BUILD: 'none' | 'info' | 'debug' | 'error' | 'warn'
|
|
128
|
+
TRV_BUILD: 'none' | 'info' | 'debug' | 'error' | 'warn';
|
|
129
129
|
/**
|
|
130
130
|
* Should break on first line of a method when using the @DebugBreak decorator
|
|
131
131
|
* @default false
|
|
@@ -168,7 +168,7 @@ export class EnvProp<T> {
|
|
|
168
168
|
```
|
|
169
169
|
|
|
170
170
|
## Standard Error Support
|
|
171
|
-
While the framework is 100 % compatible with standard `Error` instances, there are cases in which additional functionality is desired. Within the framework we use [AppError](https://github.com/travetto/travetto/tree/main/module/runtime/src/error.ts#L26) (or its derivatives) to represent framework errors. This class is available for use in your own projects. Some of the additional benefits of using this class is enhanced error reporting, as well as better integration with other modules (e.g. the [
|
|
171
|
+
While the framework is 100 % compatible with standard `Error` instances, there are cases in which additional functionality is desired. Within the framework we use [AppError](https://github.com/travetto/travetto/tree/main/module/runtime/src/error.ts#L26) (or its derivatives) to represent framework errors. This class is available for use in your own projects. Some of the additional benefits of using this class is enhanced error reporting, as well as better integration with other modules (e.g. the [Web API](https://github.com/travetto/travetto/tree/main/module/web#readme "Declarative api for Web Applications with support for the dependency injection.") module and HTTP status codes).
|
|
172
172
|
|
|
173
173
|
The [AppError](https://github.com/travetto/travetto/tree/main/module/runtime/src/error.ts#L26) takes in a message, and an optional payload and / or error classification. The currently supported error classifications are:
|
|
174
174
|
* `general` - General purpose errors
|
|
@@ -193,7 +193,7 @@ The supported operations are:
|
|
|
193
193
|
**Note**: All other console methods are excluded, specifically `trace`, `inspect`, `dir`, `time`/`timeEnd`
|
|
194
194
|
|
|
195
195
|
### How Logging is Instrumented
|
|
196
|
-
All of the logging instrumentation occurs at transpilation time. All `console.*` methods are replaced with a call to a globally defined variable that delegates to the [ConsoleManager](https://github.com/travetto/travetto/tree/main/module/runtime/src/console.ts#
|
|
196
|
+
All of the logging instrumentation occurs at transpilation time. All `console.*` methods are replaced with a call to a globally defined variable that delegates to the [ConsoleManager](https://github.com/travetto/travetto/tree/main/module/runtime/src/console.ts#L43). This module, hooks into the [ConsoleManager](https://github.com/travetto/travetto/tree/main/module/runtime/src/console.ts#L43) and receives all logging events from all files compiled by the [Travetto](https://travetto.dev).
|
|
197
197
|
|
|
198
198
|
A sample of the instrumentation would be:
|
|
199
199
|
|
|
@@ -242,7 +242,7 @@ The `debug` messages can be filtered using the patterns from the [debug](https:/
|
|
|
242
242
|
# Debug
|
|
243
243
|
$ DEBUG=-@travetto/model npx trv run app
|
|
244
244
|
$ DEBUG=-@travetto/registry npx trv run app
|
|
245
|
-
$ DEBUG=@travetto/
|
|
245
|
+
$ DEBUG=@travetto/web npx trv run app
|
|
246
246
|
$ DEBUG=@travetto/*,-@travetto/model npx trv run app
|
|
247
247
|
```
|
|
248
248
|
|
|
@@ -252,7 +252,7 @@ Additionally, the logging framework will merge [debug](https://www.npmjs.com/pac
|
|
|
252
252
|
```bash
|
|
253
253
|
|
|
254
254
|
# Debug
|
|
255
|
-
$ DEBUG=express:*,@travetto/
|
|
255
|
+
$ DEBUG=express:*,@travetto/web npx trv run web
|
|
256
256
|
```
|
|
257
257
|
|
|
258
258
|
## Resource Access
|
|
@@ -263,7 +263,7 @@ The [FileLoader](https://github.com/travetto/travetto/tree/main/module/runtime/s
|
|
|
263
263
|
The [FileLoader](https://github.com/travetto/travetto/tree/main/module/runtime/src/file-loader.ts#L11) also supports tying itself to [Env](https://github.com/travetto/travetto/tree/main/module/runtime/src/env.ts#L114)'s `TRV_RESOURCES` information on where to attempt to find a requested resource.
|
|
264
264
|
|
|
265
265
|
## Common Utilities
|
|
266
|
-
Common utilities used throughout the framework. Currently [Util](https://github.com/travetto/travetto/tree/main/module/runtime/src/util.ts#
|
|
266
|
+
Common utilities used throughout the framework. Currently [Util](https://github.com/travetto/travetto/tree/main/module/runtime/src/util.ts#L12) includes:
|
|
267
267
|
* `uuid(len: number)` generates a simple uuid for use within the application.
|
|
268
268
|
* `allowDenyMatcher(rules[])` builds a matching function that leverages the rules as an allow/deny list, where order of the rules matters. Negative rules are prefixed by '!'.
|
|
269
269
|
* `hash(text: string, size?: number)` produces a full sha512 hash.
|
package/__index__.ts
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
import type { } from './src/global';
|
|
2
|
-
import type { } from './src/trv';
|
|
3
|
-
export * from './src/binary';
|
|
4
|
-
export * from './src/console';
|
|
5
|
-
export * from './src/context';
|
|
6
|
-
export * from './src/debug';
|
|
7
|
-
export * from './src/error';
|
|
8
|
-
export * from './src/exec';
|
|
9
|
-
export * from './src/env';
|
|
10
|
-
export * from './src/file-loader';
|
|
11
|
-
export * from './src/function';
|
|
12
|
-
export * from './src/manifest-index';
|
|
13
|
-
export * from './src/queue';
|
|
14
|
-
export * from './src/resources';
|
|
15
|
-
export * from './src/shutdown';
|
|
16
|
-
export * from './src/time';
|
|
17
|
-
export * from './src/types';
|
|
18
|
-
export * from './src/watch';
|
|
19
|
-
export * from './src/util';
|
|
1
|
+
import type { } from './src/global.d.ts';
|
|
2
|
+
import type { } from './src/trv.d.ts';
|
|
3
|
+
export * from './src/binary.ts';
|
|
4
|
+
export * from './src/console.ts';
|
|
5
|
+
export * from './src/context.ts';
|
|
6
|
+
export * from './src/debug.ts';
|
|
7
|
+
export * from './src/error.ts';
|
|
8
|
+
export * from './src/exec.ts';
|
|
9
|
+
export * from './src/env.ts';
|
|
10
|
+
export * from './src/file-loader.ts';
|
|
11
|
+
export * from './src/function.ts';
|
|
12
|
+
export * from './src/manifest-index.ts';
|
|
13
|
+
export * from './src/queue.ts';
|
|
14
|
+
export * from './src/resources.ts';
|
|
15
|
+
export * from './src/shutdown.ts';
|
|
16
|
+
export * from './src/time.ts';
|
|
17
|
+
export * from './src/types.ts';
|
|
18
|
+
export * from './src/watch.ts';
|
|
19
|
+
export * from './src/util.ts';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/runtime",
|
|
3
|
-
"version": "6.0.0-rc.
|
|
3
|
+
"version": "6.0.0-rc.2",
|
|
4
4
|
"description": "Runtime for travetto applications.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"console-manager",
|
|
@@ -25,12 +25,12 @@
|
|
|
25
25
|
"directory": "module/runtime"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@travetto/manifest": "^6.0.0-rc.
|
|
28
|
+
"@travetto/manifest": "^6.0.0-rc.2",
|
|
29
29
|
"@types/debug": "^4.1.12",
|
|
30
30
|
"debug": "^4.4.0"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
|
-
"@travetto/transformer": "^6.0.0-rc.
|
|
33
|
+
"@travetto/transformer": "^6.0.0-rc.3"
|
|
34
34
|
},
|
|
35
35
|
"peerDependenciesMeta": {
|
|
36
36
|
"@travetto/transformer": {
|
package/src/binary.ts
CHANGED
|
@@ -2,21 +2,38 @@ import path from 'node:path';
|
|
|
2
2
|
import os from 'node:os';
|
|
3
3
|
import crypto from 'node:crypto';
|
|
4
4
|
import fs from 'node:fs/promises';
|
|
5
|
-
import { PassThrough, Readable
|
|
5
|
+
import { PassThrough, Readable } from 'node:stream';
|
|
6
6
|
import { pipeline } from 'node:stream/promises';
|
|
7
7
|
import { ReadableStream } from 'node:stream/web';
|
|
8
|
-
import { text as toText, arrayBuffer as
|
|
8
|
+
import { text as toText, arrayBuffer as toArrayBuffer } from 'node:stream/consumers';
|
|
9
|
+
import { isArrayBuffer } from 'node:util/types';
|
|
9
10
|
|
|
10
|
-
import { BinaryInput, BlobMeta } from './types';
|
|
11
|
-
import {
|
|
12
|
-
import { Util } from './util';
|
|
11
|
+
import { BinaryInput, BlobMeta, hasFunction } from './types.ts';
|
|
12
|
+
import { Util } from './util.ts';
|
|
13
13
|
|
|
14
|
-
const BlobMetaSymbol = Symbol
|
|
14
|
+
const BlobMetaSymbol = Symbol();
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* Common functions for dealing with binary data/streams
|
|
18
18
|
*/
|
|
19
19
|
export class BinaryUtil {
|
|
20
|
+
/** Is Array Buffer */
|
|
21
|
+
static isArrayBuffer = isArrayBuffer;
|
|
22
|
+
/** Is Readable */
|
|
23
|
+
static isReadable = hasFunction<Readable>('pipe');
|
|
24
|
+
/** Is ReadableStream */
|
|
25
|
+
static isReadableStream = hasFunction<ReadableStream>('pipeTo');
|
|
26
|
+
/** Is Async Iterable */
|
|
27
|
+
static isAsyncIterable = (v: unknown): v is AsyncIterable<unknown> =>
|
|
28
|
+
!!v && (typeof v === 'object' || typeof v === 'function') && Symbol.asyncIterator in v;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Is src a binary type
|
|
32
|
+
*/
|
|
33
|
+
static isBinaryType(src: unknown): boolean {
|
|
34
|
+
return src instanceof Blob || Buffer.isBuffer(src) || this.isReadable(src) ||
|
|
35
|
+
this.isArrayBuffer(src) || this.isReadableStream(src) || this.isAsyncIterable(src);
|
|
36
|
+
}
|
|
20
37
|
|
|
21
38
|
/**
|
|
22
39
|
* Generate a proper sha512 hash from a src value
|
|
@@ -26,8 +43,8 @@ export class BinaryUtil {
|
|
|
26
43
|
static hash(src: string, len: number = -1): string {
|
|
27
44
|
const hash = crypto.createHash('sha512');
|
|
28
45
|
hash.update(src);
|
|
29
|
-
const
|
|
30
|
-
return len > 0 ?
|
|
46
|
+
const digest = hash.digest('hex');
|
|
47
|
+
return len > 0 ? digest.substring(0, len) : digest;
|
|
31
48
|
}
|
|
32
49
|
|
|
33
50
|
/**
|
|
@@ -83,9 +100,9 @@ export class BinaryUtil {
|
|
|
83
100
|
return Object.defineProperties(out, {
|
|
84
101
|
size: { value: size },
|
|
85
102
|
stream: { value: () => ReadableStream.from(go()) },
|
|
86
|
-
arrayBuffer: { value: () =>
|
|
103
|
+
arrayBuffer: { value: () => toArrayBuffer(go()) },
|
|
87
104
|
text: { value: () => toText(go()) },
|
|
88
|
-
bytes: { value: () =>
|
|
105
|
+
bytes: { value: () => toArrayBuffer(go()).then(v => new Uint8Array(v)) },
|
|
89
106
|
[BlobMetaSymbol]: { value: metadata }
|
|
90
107
|
});
|
|
91
108
|
}
|
|
@@ -98,24 +115,6 @@ export class BinaryUtil {
|
|
|
98
115
|
return withMeta[BlobMetaSymbol];
|
|
99
116
|
}
|
|
100
117
|
|
|
101
|
-
/**
|
|
102
|
-
* Write limiter
|
|
103
|
-
* @returns
|
|
104
|
-
*/
|
|
105
|
-
static limitWrite(maxSize: number): Transform {
|
|
106
|
-
let read = 0;
|
|
107
|
-
return new Transform({
|
|
108
|
-
transform(chunk, encoding, callback): void {
|
|
109
|
-
read += (Buffer.isBuffer(chunk) || typeof chunk === 'string') ? chunk.length : (chunk instanceof Uint8Array ? chunk.byteLength : 0);
|
|
110
|
-
if (read > maxSize) {
|
|
111
|
-
callback(new AppError('File size exceeded', { category: 'data', details: { read, size: maxSize } }));
|
|
112
|
-
} else {
|
|
113
|
-
callback(null, chunk);
|
|
114
|
-
}
|
|
115
|
-
},
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
|
|
119
118
|
/**
|
|
120
119
|
* Get a hashed location/path for a blob
|
|
121
120
|
*/
|
package/src/console.ts
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import util from 'node:util';
|
|
2
2
|
import debug from 'debug';
|
|
3
3
|
|
|
4
|
-
import { RuntimeIndex } from './manifest-index';
|
|
4
|
+
import { RuntimeIndex } from './manifest-index.ts';
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
/**
|
|
7
|
+
* @concrete
|
|
8
|
+
*/
|
|
9
|
+
export interface ConsoleEvent {
|
|
7
10
|
/** Time of event */
|
|
8
11
|
timestamp: Date;
|
|
9
12
|
/** The level of the console event */
|
|
@@ -20,6 +23,9 @@ export type ConsoleEvent = {
|
|
|
20
23
|
args: unknown[];
|
|
21
24
|
};
|
|
22
25
|
|
|
26
|
+
/**
|
|
27
|
+
* @concrete
|
|
28
|
+
*/
|
|
23
29
|
export interface ConsoleListener {
|
|
24
30
|
log(ev: ConsoleEvent): void;
|
|
25
31
|
}
|
|
@@ -31,6 +37,8 @@ const DEBUG_OG = { formatArgs: debug.formatArgs, log: debug.log };
|
|
|
31
37
|
*
|
|
32
38
|
* The transpiler will replace all console.* calls in the typescript files for the framework and those provided by the user.
|
|
33
39
|
* Any console.log statements elsewhere will not be affected.
|
|
40
|
+
*
|
|
41
|
+
* @alias ConsoleManager
|
|
34
42
|
*/
|
|
35
43
|
class $ConsoleManager implements ConsoleListener {
|
|
36
44
|
|
package/src/context.ts
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import { existsSync } from 'node:fs';
|
|
2
1
|
import fs from 'node:fs/promises';
|
|
3
2
|
import path from 'node:path';
|
|
4
3
|
|
|
5
4
|
import { type ManifestIndex, type ManifestContext, ManifestModuleUtil } from '@travetto/manifest';
|
|
6
5
|
|
|
7
|
-
import { Env } from './env';
|
|
8
|
-
import { RuntimeIndex } from './manifest-index';
|
|
9
|
-
import { describeFunction } from './function';
|
|
10
|
-
import { castTo } from './types';
|
|
6
|
+
import { Env } from './env.ts';
|
|
7
|
+
import { RuntimeIndex } from './manifest-index.ts';
|
|
8
|
+
import { describeFunction } from './function.ts';
|
|
9
|
+
import { castTo } from './types.ts';
|
|
11
10
|
|
|
12
11
|
/** Constrained version of {@type ManifestContext} */
|
|
13
12
|
class $Runtime {
|
|
@@ -85,8 +84,9 @@ class $Runtime {
|
|
|
85
84
|
}
|
|
86
85
|
|
|
87
86
|
/** Resolve single module path */
|
|
88
|
-
modulePath(modulePath: string): string {
|
|
89
|
-
const
|
|
87
|
+
modulePath(modulePath: string, overrides?: Record<string, string>): string {
|
|
88
|
+
const combined = { ...this.#resourceOverrides, ...overrides };
|
|
89
|
+
const [base, sub] = (combined[modulePath] ?? modulePath)
|
|
90
90
|
.replace(/^([^#]*)(#|$)/g, (_, v, r) => `${this.#moduleAliases[v] ?? v}${r}`)
|
|
91
91
|
.split('#');
|
|
92
92
|
return path.resolve(this.#idx.getModule(base)?.sourcePath ?? base, sub ?? '.');
|
|
@@ -110,7 +110,7 @@ class $Runtime {
|
|
|
110
110
|
/** Import from a given path */
|
|
111
111
|
async importFrom<T = unknown>(imp?: string): Promise<T> {
|
|
112
112
|
const file = path.resolve(this.#idx.mainModule.sourcePath, imp!);
|
|
113
|
-
if (
|
|
113
|
+
if (await fs.stat(file).catch(() => false)) {
|
|
114
114
|
imp = this.#idx.getFromSource(file)?.import;
|
|
115
115
|
}
|
|
116
116
|
|
|
@@ -121,7 +121,7 @@ class $Runtime {
|
|
|
121
121
|
return fs.readFile(imp, 'utf8').then(JSON.parse);
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
if (
|
|
124
|
+
if (!ManifestModuleUtil.SOURCE_EXT_RE.test(imp)) {
|
|
125
125
|
if (imp.startsWith('@')) {
|
|
126
126
|
if (/[/].*?[/]/.test(imp)) {
|
|
127
127
|
imp = `${imp}.ts`;
|
package/src/debug.ts
CHANGED
package/src/env.ts
CHANGED
package/src/error.ts
CHANGED
package/src/exec.ts
CHANGED
|
@@ -2,11 +2,11 @@ import { ChildProcess } from 'node:child_process';
|
|
|
2
2
|
import { Readable } from 'node:stream';
|
|
3
3
|
import { createInterface } from 'node:readline/promises';
|
|
4
4
|
|
|
5
|
-
import { castTo } from './types';
|
|
5
|
+
import { castTo } from './types.ts';
|
|
6
6
|
|
|
7
7
|
const MINUTE = (1000 * 60);
|
|
8
8
|
|
|
9
|
-
const
|
|
9
|
+
const ResultSymbol = Symbol();
|
|
10
10
|
|
|
11
11
|
interface ExecutionBaseResult {
|
|
12
12
|
/**
|
|
@@ -49,14 +49,18 @@ export class ExecUtil {
|
|
|
49
49
|
* @param run The factory to produce the next running process
|
|
50
50
|
* @param maxRetriesPerMinute The number of times to allow a retry within a minute
|
|
51
51
|
*/
|
|
52
|
-
static async withRestart(
|
|
52
|
+
static async withRestart(
|
|
53
|
+
run: () => ChildProcess,
|
|
54
|
+
maxRetriesPerMinute?: number,
|
|
55
|
+
killSignal: 'SIGINT' | 'SIGTERM' = 'SIGINT'
|
|
56
|
+
): Promise<ExecutionResult> {
|
|
53
57
|
const maxRetries = maxRetriesPerMinute ?? 5;
|
|
54
58
|
const restarts: number[] = [];
|
|
55
59
|
|
|
56
60
|
for (; ;) {
|
|
57
61
|
const proc = run();
|
|
58
62
|
|
|
59
|
-
const toKill = (): void => { proc.kill(
|
|
63
|
+
const toKill = (): void => { proc.kill(killSignal); };
|
|
60
64
|
const toMessage = (v: unknown): void => { proc.send?.(v!); };
|
|
61
65
|
|
|
62
66
|
// Proxy kill requests
|
|
@@ -95,12 +99,12 @@ export class ExecUtil {
|
|
|
95
99
|
static getResult(proc: ChildProcess, options: { catch?: boolean, binary?: false }): Promise<ExecutionResult<string>>;
|
|
96
100
|
static getResult(proc: ChildProcess, options: { catch?: boolean, binary: true }): Promise<ExecutionResult<Buffer>>;
|
|
97
101
|
static getResult<T extends string | Buffer>(proc: ChildProcess, options: { catch?: boolean, binary?: boolean } = {}): Promise<ExecutionResult<T>> {
|
|
98
|
-
const _proc: ChildProcess & { [
|
|
99
|
-
const
|
|
102
|
+
const _proc: ChildProcess & { [ResultSymbol]?: Promise<ExecutionResult> } = proc;
|
|
103
|
+
const result = _proc[ResultSymbol] ??= new Promise<ExecutionResult>(resolve => {
|
|
100
104
|
const stdout: Buffer[] = [];
|
|
101
105
|
const stderr: Buffer[] = [];
|
|
102
106
|
let done = false;
|
|
103
|
-
const finish = (
|
|
107
|
+
const finish = (finalResult: ExecutionBaseResult): void => {
|
|
104
108
|
if (done) {
|
|
105
109
|
return;
|
|
106
110
|
}
|
|
@@ -114,7 +118,7 @@ export class ExecUtil {
|
|
|
114
118
|
const final = {
|
|
115
119
|
stdout: options.binary ? buffers.stdout : buffers.stdout.toString('utf8'),
|
|
116
120
|
stderr: options.binary ? buffers.stderr : buffers.stderr.toString('utf8'),
|
|
117
|
-
...
|
|
121
|
+
...finalResult
|
|
118
122
|
};
|
|
119
123
|
|
|
120
124
|
resolve(!final.valid ?
|
|
@@ -123,8 +127,8 @@ export class ExecUtil {
|
|
|
123
127
|
);
|
|
124
128
|
};
|
|
125
129
|
|
|
126
|
-
proc.stdout?.on('data', (d: string | Buffer) => stdout.push(Buffer.from(d)));
|
|
127
|
-
proc.stderr?.on('data', (d: string | Buffer) => stderr.push(Buffer.from(d)));
|
|
130
|
+
proc.stdout?.on('data', (d: string | Buffer) => stdout.push(Buffer.isBuffer(d) ? d : Buffer.from(d)));
|
|
131
|
+
proc.stderr?.on('data', (d: string | Buffer) => stderr.push(Buffer.isBuffer(d) ? d : Buffer.from(d)));
|
|
128
132
|
|
|
129
133
|
proc.on('error', (err: Error) =>
|
|
130
134
|
finish({ code: 1, message: err.message, valid: false }));
|
|
@@ -137,7 +141,7 @@ export class ExecUtil {
|
|
|
137
141
|
}
|
|
138
142
|
});
|
|
139
143
|
|
|
140
|
-
return castTo(options.catch ?
|
|
144
|
+
return castTo(options.catch ? result : result.then(v => {
|
|
141
145
|
if (v.valid) {
|
|
142
146
|
return v;
|
|
143
147
|
} else {
|
package/src/file-loader.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { Readable } from 'node:stream';
|
|
|
3
3
|
import fs from 'node:fs/promises';
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
|
|
6
|
-
import { AppError } from './error';
|
|
6
|
+
import { AppError } from './error.ts';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* File loader that will search for files across the provided search paths
|
|
@@ -56,4 +56,12 @@ export class FileLoader {
|
|
|
56
56
|
const file = await this.resolve(relativePath);
|
|
57
57
|
return createReadStream(file, { encoding: binary ? undefined : 'utf8' });
|
|
58
58
|
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Read a file as a File object
|
|
62
|
+
* @param relativePath The path to read
|
|
63
|
+
*/
|
|
64
|
+
async readFile(relativePath: string): Promise<File> {
|
|
65
|
+
return new File([await this.read(relativePath, true)], path.basename(relativePath));
|
|
66
|
+
}
|
|
59
67
|
}
|
package/src/function.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { ManifestModuleUtil } from '@travetto/manifest';
|
|
2
|
+
|
|
1
3
|
export type FunctionMetadataTag = { hash: number, lines: [start: number, end: number, bodyStart?: number] };
|
|
2
4
|
export type FunctionMetadata = FunctionMetadataTag & {
|
|
3
5
|
id: string;
|
|
@@ -9,7 +11,7 @@ export type FunctionMetadata = FunctionMetadataTag & {
|
|
|
9
11
|
abstract?: boolean;
|
|
10
12
|
};
|
|
11
13
|
|
|
12
|
-
const MetadataSymbol = Symbol
|
|
14
|
+
const MetadataSymbol = Symbol();
|
|
13
15
|
|
|
14
16
|
const pending = new Set<Function>([]);
|
|
15
17
|
|
|
@@ -27,7 +29,7 @@ export function registerFunction(
|
|
|
27
29
|
fn: Function, [pkg, pth]: [string, string], tag: FunctionMetadataTag,
|
|
28
30
|
methods?: Record<string, FunctionMetadataTag>, abstract?: boolean,
|
|
29
31
|
): void {
|
|
30
|
-
const modulePath =
|
|
32
|
+
const modulePath = ManifestModuleUtil.withoutSourceExtension(pth);
|
|
31
33
|
|
|
32
34
|
const metadata: FunctionMetadata = {
|
|
33
35
|
id: (fn.name ? `${pkg}:${modulePath}#${fn.name}` : `${pkg}:${modulePath}`),
|
package/src/global.d.ts
CHANGED
|
@@ -11,4 +11,28 @@ declare global {
|
|
|
11
11
|
/* Exposed for use within framework, only applies to framework managed classes */
|
|
12
12
|
readonly Ⲑid: string;
|
|
13
13
|
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @concrete node:buffer#Blob
|
|
17
|
+
*/
|
|
18
|
+
interface Blob { }
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @concrete node:buffer#File
|
|
22
|
+
*/
|
|
23
|
+
interface File { }
|
|
24
|
+
|
|
25
|
+
namespace NodeJS {
|
|
26
|
+
/**
|
|
27
|
+
* @concrete node:stream#Readable
|
|
28
|
+
*/
|
|
29
|
+
interface ReadableStream { }
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
declare module 'stream' {
|
|
34
|
+
/**
|
|
35
|
+
* @concrete node:stream#Readable
|
|
36
|
+
*/
|
|
37
|
+
interface Readable { }
|
|
14
38
|
}
|
package/src/queue.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { Util } from './util';
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
2
|
* An asynchronous queue
|
|
5
3
|
*/
|
|
@@ -7,7 +5,7 @@ export class AsyncQueue<X> implements AsyncIterator<X>, AsyncIterable<X> {
|
|
|
7
5
|
|
|
8
6
|
#queue: X[] = [];
|
|
9
7
|
#done = false;
|
|
10
|
-
#ready =
|
|
8
|
+
#ready = Promise.withResolvers<void>();
|
|
11
9
|
|
|
12
10
|
/**
|
|
13
11
|
* Initial set of items
|
|
@@ -31,7 +29,7 @@ export class AsyncQueue<X> implements AsyncIterator<X>, AsyncIterable<X> {
|
|
|
31
29
|
async next(): Promise<IteratorResult<X>> {
|
|
32
30
|
while (!this.#done && !this.#queue.length) {
|
|
33
31
|
await this.#ready.promise;
|
|
34
|
-
this.#ready =
|
|
32
|
+
this.#ready = Promise.withResolvers<void>();
|
|
35
33
|
}
|
|
36
34
|
return { value: (this.#queue.length ? this.#queue.shift() : undefined)!, done: this.#done };
|
|
37
35
|
}
|
package/src/resources.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Runtime } from './context';
|
|
2
|
-
import { Env } from './env';
|
|
3
|
-
import { FileLoader } from './file-loader';
|
|
1
|
+
import { Runtime } from './context.ts';
|
|
2
|
+
import { Env } from './env.ts';
|
|
3
|
+
import { FileLoader } from './file-loader.ts';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Environment aware file loader
|
package/src/shutdown.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Env } from './env';
|
|
2
|
-
import { Util } from './util';
|
|
3
|
-
import { TimeUtil } from './time';
|
|
1
|
+
import { Env } from './env.ts';
|
|
2
|
+
import { Util } from './util.ts';
|
|
3
|
+
import { TimeUtil } from './time.ts';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Shutdown manager, allowing for listening for graceful shutdowns
|
|
@@ -8,14 +8,14 @@ import { TimeUtil } from './time';
|
|
|
8
8
|
export class ShutdownManager {
|
|
9
9
|
|
|
10
10
|
static #registered = false;
|
|
11
|
-
static #handlers: { name?: string, handler: () => Promise<void> }[] = [];
|
|
11
|
+
static #handlers: { name?: string, handler: () => (void | Promise<void>) }[] = [];
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* On Shutdown requested
|
|
15
15
|
* @param name name to log for
|
|
16
16
|
* @param handler synchronous or asynchronous handler
|
|
17
17
|
*/
|
|
18
|
-
static onGracefulShutdown(handler: () => Promise<void
|
|
18
|
+
static onGracefulShutdown(handler: () => (void | Promise<void>), name?: string | { constructor: Function }): () => void {
|
|
19
19
|
if (!this.#registered) {
|
|
20
20
|
this.#registered = true;
|
|
21
21
|
const done = (): void => { this.gracefulShutdown(0); };
|
|
@@ -34,6 +34,8 @@ export class ShutdownManager {
|
|
|
34
34
|
* Wait for graceful shutdown to run and complete
|
|
35
35
|
*/
|
|
36
36
|
static async gracefulShutdown(code: number | string | undefined = process.exitCode): Promise<void> {
|
|
37
|
+
await Util.queueMacroTask(); // Force the event loop to wait one cycle
|
|
38
|
+
|
|
37
39
|
if (code !== undefined) {
|
|
38
40
|
process.exitCode = code;
|
|
39
41
|
}
|
|
@@ -42,13 +44,15 @@ export class ShutdownManager {
|
|
|
42
44
|
console.debug('Graceful shutdown: started');
|
|
43
45
|
|
|
44
46
|
const items = this.#handlers.splice(0, this.#handlers.length);
|
|
45
|
-
const handlers = Promise.all(items.map(({ name, handler }) => {
|
|
47
|
+
const handlers = Promise.all(items.map(async ({ name, handler }) => {
|
|
46
48
|
if (name) {
|
|
47
49
|
console.debug('Stopping', { name });
|
|
48
50
|
}
|
|
49
|
-
|
|
51
|
+
try {
|
|
52
|
+
return await handler();
|
|
53
|
+
} catch (err) {
|
|
50
54
|
console.error('Error shutting down', { name, err });
|
|
51
|
-
}
|
|
55
|
+
}
|
|
52
56
|
}));
|
|
53
57
|
|
|
54
58
|
await Promise.race([
|
package/src/trv.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type ManifestModuleRole } from '@travetto/manifest';
|
|
2
|
-
import { type TimeSpan } from './time';
|
|
2
|
+
import { type TimeSpan } from './time.ts';
|
|
3
3
|
type Role = Exclude<ManifestModuleRole, 'std' | 'compile'>;
|
|
4
4
|
|
|
5
5
|
declare module "@travetto/runtime" {
|
|
@@ -51,7 +51,7 @@ declare module "@travetto/runtime" {
|
|
|
51
51
|
/**
|
|
52
52
|
* trvc log level
|
|
53
53
|
*/
|
|
54
|
-
TRV_BUILD: 'none' | 'info' | 'debug' | 'error' | 'warn'
|
|
54
|
+
TRV_BUILD: 'none' | 'info' | 'debug' | 'error' | 'warn';
|
|
55
55
|
/**
|
|
56
56
|
* Should break on first line of a method when using the @DebugBreak decorator
|
|
57
57
|
* @default false
|
package/src/types.ts
CHANGED
|
@@ -49,6 +49,10 @@ export const hasFunction = <T>(key: keyof T) => (o: unknown): o is T =>
|
|
|
49
49
|
|
|
50
50
|
export const hasToJSON = hasFunction<{ toJSON(): object }>('toJSON');
|
|
51
51
|
|
|
52
|
+
export function toConcrete<T extends unknown>(): Class<T> {
|
|
53
|
+
return arguments[0];
|
|
54
|
+
}
|
|
55
|
+
|
|
52
56
|
/**
|
|
53
57
|
* Range of bytes, inclusive
|
|
54
58
|
*/
|
package/src/util.ts
CHANGED
|
@@ -1,13 +1,8 @@
|
|
|
1
1
|
import crypto from 'node:crypto';
|
|
2
2
|
import timers from 'node:timers/promises';
|
|
3
3
|
|
|
4
|
-
import { castTo } from './types';
|
|
5
|
-
|
|
6
|
-
type PromiseWithResolvers<T> = {
|
|
7
|
-
resolve: (v: T) => void;
|
|
8
|
-
reject: (err?: unknown) => void;
|
|
9
|
-
promise: Promise<T>;
|
|
10
|
-
};
|
|
4
|
+
import { castTo, hasToJSON } from './types.ts';
|
|
5
|
+
import { AppError } from './error.ts';
|
|
11
6
|
|
|
12
7
|
type MapFn<T, U> = (val: T, i: number) => U | Promise<U>;
|
|
13
8
|
|
|
@@ -56,15 +51,6 @@ export class Util {
|
|
|
56
51
|
return bytes.toString('hex').substring(0, len);
|
|
57
52
|
}
|
|
58
53
|
|
|
59
|
-
/**
|
|
60
|
-
* Produce a promise that is externally resolvable
|
|
61
|
-
*/
|
|
62
|
-
static resolvablePromise<T = void>(): PromiseWithResolvers<T> {
|
|
63
|
-
let ops: Pick<PromiseWithResolvers<T>, 'reject' | 'resolve'>;
|
|
64
|
-
const prom = new Promise<T>((resolve, reject) => ops = { resolve, reject });
|
|
65
|
-
return { ...ops!, promise: prom };
|
|
66
|
-
}
|
|
67
|
-
|
|
68
54
|
/**
|
|
69
55
|
* Map an async iterable with various mapping functions
|
|
70
56
|
*/
|
|
@@ -164,4 +150,49 @@ export class Util {
|
|
|
164
150
|
|
|
165
151
|
return JSON.parse(decoded, undefined);
|
|
166
152
|
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Serialize to JSON
|
|
156
|
+
*/
|
|
157
|
+
static serializeToJSON<T>(out: T): string {
|
|
158
|
+
return JSON.stringify(out, function (k, v) {
|
|
159
|
+
const ov = this[k];
|
|
160
|
+
if (ov && ov instanceof Error) {
|
|
161
|
+
return {
|
|
162
|
+
$: true,
|
|
163
|
+
...hasToJSON(ov) ? ov.toJSON() : ov,
|
|
164
|
+
name: ov.name,
|
|
165
|
+
message: ov.message,
|
|
166
|
+
stack: ov.stack,
|
|
167
|
+
};
|
|
168
|
+
} else if (typeof v === 'bigint') {
|
|
169
|
+
return `${v.toString()}$n`;
|
|
170
|
+
} else {
|
|
171
|
+
return v;
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Deserialize from JSON
|
|
178
|
+
*/
|
|
179
|
+
static deserializeFromJson<T = unknown>(input: string): T {
|
|
180
|
+
return JSON.parse(input, function (k, v) {
|
|
181
|
+
if (v && typeof v === 'object' && '$' in v) {
|
|
182
|
+
const err = AppError.fromJSON(v) ?? new Error();
|
|
183
|
+
if (!(err instanceof AppError)) {
|
|
184
|
+
const { $: _, ...rest } = v;
|
|
185
|
+
Object.assign(err, rest);
|
|
186
|
+
}
|
|
187
|
+
err.message = v.message;
|
|
188
|
+
err.stack = v.stack;
|
|
189
|
+
err.name = v.name;
|
|
190
|
+
return err;
|
|
191
|
+
} else if (typeof v === 'string' && /^\d+[$]n$/.test(v)) {
|
|
192
|
+
return BigInt(v.split('$')[0]);
|
|
193
|
+
} else {
|
|
194
|
+
return v;
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
}
|
|
167
198
|
}
|
package/src/watch.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { RuntimeIndex } from './manifest-index';
|
|
2
|
-
import { ExecUtil } from './exec';
|
|
3
|
-
import { ShutdownManager } from './shutdown';
|
|
4
|
-
import { Util } from './util';
|
|
1
|
+
import { RuntimeIndex } from './manifest-index.ts';
|
|
2
|
+
import { ExecUtil } from './exec.ts';
|
|
3
|
+
import { ShutdownManager } from './shutdown.ts';
|
|
4
|
+
import { Util } from './util.ts';
|
|
5
5
|
|
|
6
6
|
export type WatchEvent = { file: string, action: 'create' | 'update' | 'delete', output: string, module: string, time: number };
|
|
7
7
|
|
|
8
8
|
export async function* watchCompiler(cfg?: { restartOnExit?: boolean, signal?: AbortSignal }): AsyncIterable<WatchEvent> {
|
|
9
9
|
// Load at runtime
|
|
10
|
-
const { CompilerClient } = await import('@travetto/compiler/support/server/client');
|
|
10
|
+
const { CompilerClient } = await import('@travetto/compiler/support/server/client.ts');
|
|
11
11
|
|
|
12
12
|
const client = new CompilerClient(RuntimeIndex.manifest, {
|
|
13
13
|
warn(message, ...args): void { console.error('warn', message, ...args); },
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import * as ts from 'typescript';
|
|
2
|
+
|
|
3
|
+
import { FunctionMetadataTag } from '@travetto/runtime';
|
|
4
|
+
import { CoreUtil, Import, SystemUtil, TransformerState } from '@travetto/transformer';
|
|
5
|
+
|
|
6
|
+
const RegisterImportSymbol = Symbol();
|
|
7
|
+
|
|
8
|
+
interface MetadataInfo {
|
|
9
|
+
[RegisterImportSymbol]?: Import;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Utils for registering function/class metadata at compile time
|
|
14
|
+
*/
|
|
15
|
+
export class MetadataRegistrationUtil {
|
|
16
|
+
|
|
17
|
+
static REGISTER_IMPORT = '@travetto/runtime/src/function.ts';
|
|
18
|
+
static REGISTER_FN = 'registerFunction';
|
|
19
|
+
|
|
20
|
+
static isValid({ importName: imp }: TransformerState): boolean {
|
|
21
|
+
return imp !== this.REGISTER_IMPORT;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
static tag(state: TransformerState, node: ts.Node): FunctionMetadataTag {
|
|
25
|
+
const text = node.getText();
|
|
26
|
+
const hash = SystemUtil.naiveHash(text);
|
|
27
|
+
try {
|
|
28
|
+
const range = CoreUtil.getRangeOf(state.source, node) ?? [0, 0];
|
|
29
|
+
if (ts.isMethodDeclaration(node) || ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node)) {
|
|
30
|
+
const bodyStart = CoreUtil.getRangeOf(state.source, node?.body?.statements[0])?.[0];
|
|
31
|
+
if (bodyStart) {
|
|
32
|
+
range.push(bodyStart);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return { hash, lines: range };
|
|
36
|
+
} catch {
|
|
37
|
+
return { hash, lines: [0, 0] };
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Register metadata on a function
|
|
43
|
+
*/
|
|
44
|
+
static registerFunction(state: TransformerState & MetadataInfo,
|
|
45
|
+
node: ts.FunctionDeclaration | ts.FunctionExpression,
|
|
46
|
+
src?: ts.FunctionDeclaration | ts.FunctionExpression | ts.InterfaceDeclaration | ts.TypeAliasDeclaration
|
|
47
|
+
): void {
|
|
48
|
+
// If we have a class like function
|
|
49
|
+
state[RegisterImportSymbol] ??= state.importFile(this.REGISTER_IMPORT);
|
|
50
|
+
|
|
51
|
+
const tag = this.tag(state, src ?? node);
|
|
52
|
+
const meta = state.factory.createCallExpression(
|
|
53
|
+
state.createAccess(state[RegisterImportSymbol].ident, this.REGISTER_FN),
|
|
54
|
+
[],
|
|
55
|
+
[
|
|
56
|
+
state.createIdentifier(node.name!.text),
|
|
57
|
+
state.getModuleIdentifier(),
|
|
58
|
+
state.fromLiteral(tag),
|
|
59
|
+
]
|
|
60
|
+
);
|
|
61
|
+
state.addStatements([state.factory.createExpressionStatement(meta)]);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Register metadata on a class
|
|
66
|
+
*/
|
|
67
|
+
static registerClass(
|
|
68
|
+
state: TransformerState & MetadataInfo, node: ts.ClassDeclaration,
|
|
69
|
+
cls: FunctionMetadataTag, methods?: Record<string, FunctionMetadataTag>
|
|
70
|
+
): ts.ClassDeclaration {
|
|
71
|
+
|
|
72
|
+
state[RegisterImportSymbol] ??= state.importFile(this.REGISTER_IMPORT);
|
|
73
|
+
|
|
74
|
+
const name = node.name?.escapedText.toString() ?? '';
|
|
75
|
+
|
|
76
|
+
const meta = state.factory.createCallExpression(
|
|
77
|
+
state.createAccess(state[RegisterImportSymbol].ident, this.REGISTER_FN),
|
|
78
|
+
[],
|
|
79
|
+
[
|
|
80
|
+
state.createIdentifier(name),
|
|
81
|
+
state.getModuleIdentifier(),
|
|
82
|
+
state.fromLiteral(cls),
|
|
83
|
+
state.extendObjectLiteral(methods ?? {}),
|
|
84
|
+
state.fromLiteral(CoreUtil.isAbstract(node)),
|
|
85
|
+
]
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
return state.factory.updateClassDeclaration(
|
|
89
|
+
node,
|
|
90
|
+
node.modifiers,
|
|
91
|
+
node.name,
|
|
92
|
+
node.typeParameters,
|
|
93
|
+
node.heritageClauses,
|
|
94
|
+
[
|
|
95
|
+
state.factory.createClassStaticBlockDeclaration(
|
|
96
|
+
state.factory.createBlock([
|
|
97
|
+
state.factory.createExpressionStatement(meta)
|
|
98
|
+
])
|
|
99
|
+
),
|
|
100
|
+
...node.members
|
|
101
|
+
]
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
|
|
3
|
+
import { TransformerState, OnInterface, OnCall, OnTypeAlias } from '@travetto/transformer';
|
|
4
|
+
|
|
5
|
+
import { MetadataRegistrationUtil } from './transformer/metadata.ts';
|
|
6
|
+
|
|
7
|
+
const SRC = '@travetto/runtime/src/types.ts';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Providing support for concrete types
|
|
11
|
+
*/
|
|
12
|
+
export class ConcreteTransformer {
|
|
13
|
+
|
|
14
|
+
static #isConcreteSimple(node: ts.InterfaceDeclaration | ts.TypeAliasDeclaration): boolean {
|
|
15
|
+
return /^\s*[*]\s+@concrete\s*$/gm.test(node.getFullText());
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
static #createConcreteFunction(state: TransformerState, name: string | ts.Identifier): ts.FunctionDeclaration {
|
|
19
|
+
const final = typeof name === 'string' ? name : name.getText();
|
|
20
|
+
|
|
21
|
+
const dec = state.factory.createFunctionDeclaration(
|
|
22
|
+
// eslint-disable-next-line no-bitwise
|
|
23
|
+
state.factory.createModifiersFromModifierFlags(ts.ModifierFlags.Export | ts.ModifierFlags.Const),
|
|
24
|
+
undefined, `${final}$Concrete`, [], [], undefined,
|
|
25
|
+
state.factory.createBlock([])
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
state.addStatements([
|
|
29
|
+
dec,
|
|
30
|
+
state.factory.createExpressionStatement(
|
|
31
|
+
state.factory.createCallExpression(
|
|
32
|
+
state.createAccess('Object', 'defineProperty'),
|
|
33
|
+
undefined,
|
|
34
|
+
[
|
|
35
|
+
dec.name!,
|
|
36
|
+
state.fromLiteral('name'),
|
|
37
|
+
state.fromLiteral({ value: final })
|
|
38
|
+
]
|
|
39
|
+
)
|
|
40
|
+
)
|
|
41
|
+
]);
|
|
42
|
+
|
|
43
|
+
return dec;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Handle concrete interface
|
|
48
|
+
*/
|
|
49
|
+
@OnInterface()
|
|
50
|
+
static onInterface(state: TransformerState, node: ts.InterfaceDeclaration): typeof node {
|
|
51
|
+
if (this.#isConcreteSimple(node)) {
|
|
52
|
+
const func = this.#createConcreteFunction(state, node.name);
|
|
53
|
+
MetadataRegistrationUtil.registerFunction(state, func, node);
|
|
54
|
+
}
|
|
55
|
+
return node;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Handle type alias
|
|
60
|
+
*/
|
|
61
|
+
@OnTypeAlias()
|
|
62
|
+
static onTypeAlias(state: TransformerState, node: ts.TypeAliasDeclaration): typeof node {
|
|
63
|
+
if (this.#isConcreteSimple(node)) {
|
|
64
|
+
const func = this.#createConcreteFunction(state, node.name);
|
|
65
|
+
MetadataRegistrationUtil.registerFunction(state, func, node);
|
|
66
|
+
}
|
|
67
|
+
return node;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
@OnCall()
|
|
71
|
+
static onToConcreteCall(state: TransformerState, node: ts.CallExpression): typeof node {
|
|
72
|
+
if (ts.isIdentifier(node.expression) && node.expression.text === 'toConcrete' && node.typeArguments?.length && node.arguments.length === 0) {
|
|
73
|
+
const type = state.resolveType(node.expression);
|
|
74
|
+
if ('importName' in type && type.importName === SRC) {
|
|
75
|
+
const [target] = node.typeArguments;
|
|
76
|
+
return state.factory.updateCallExpression(
|
|
77
|
+
node,
|
|
78
|
+
node.expression,
|
|
79
|
+
node.typeArguments,
|
|
80
|
+
[state.getConcreteType(target)]
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return node;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -2,7 +2,7 @@ import ts from 'typescript';
|
|
|
2
2
|
|
|
3
3
|
import { TransformerState, OnCall, LiteralUtil, OnClass, AfterClass, OnMethod, AfterMethod, AfterFunction, OnFunction } from '@travetto/transformer';
|
|
4
4
|
|
|
5
|
-
const CONSOLE_IMPORT = '@travetto/runtime/src/console';
|
|
5
|
+
const CONSOLE_IMPORT = '@travetto/runtime/src/console.ts';
|
|
6
6
|
|
|
7
7
|
type CustomState = TransformerState & {
|
|
8
8
|
scope: { type: 'method' | 'class' | 'function', name: string }[];
|
|
@@ -2,7 +2,7 @@ import ts from 'typescript';
|
|
|
2
2
|
|
|
3
3
|
import { TransformerState, OnMethod, CoreUtil } from '@travetto/transformer';
|
|
4
4
|
|
|
5
|
-
const DebugSymbol = Symbol
|
|
5
|
+
const DebugSymbol = Symbol();
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Debug transformation state
|
|
@@ -19,7 +19,7 @@ export class DebugEntryTransformer {
|
|
|
19
19
|
@OnMethod('DebugBreak')
|
|
20
20
|
static debugOnEntry(state: TransformerState & DebugState, node: ts.MethodDeclaration): ts.MethodDeclaration {
|
|
21
21
|
if (!state[DebugSymbol]) {
|
|
22
|
-
const imp = state.importFile('@travetto/runtime/src/debug').ident;
|
|
22
|
+
const imp = state.importFile('@travetto/runtime/src/debug.ts').ident;
|
|
23
23
|
state[DebugSymbol] = CoreUtil.createAccess(state.factory, imp, 'tryDebugger');
|
|
24
24
|
}
|
|
25
25
|
|
|
@@ -1,19 +1,12 @@
|
|
|
1
1
|
import ts from 'typescript';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { OnCall, TransformerState } from '@travetto/transformer';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Dynamic Import Transformer
|
|
7
7
|
*/
|
|
8
8
|
export class DynamicImportTransformer {
|
|
9
9
|
|
|
10
|
-
static #rewriteModuleSpecifier(state: TransformerState, spec: ts.LiteralExpression | ts.Expression | undefined): ts.Expression | undefined {
|
|
11
|
-
if (spec && ts.isStringLiteral(spec) && state.isUntypedImport(spec)) {
|
|
12
|
-
return LiteralUtil.fromLiteral(state.factory, `${spec.text.replace(/['"]/g, '')}.js`);
|
|
13
|
-
}
|
|
14
|
-
return spec;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
10
|
@OnCall()
|
|
18
11
|
static onLogCall(state: TransformerState, node: ts.CallExpression): typeof node | ts.Identifier {
|
|
19
12
|
if (
|
|
@@ -22,7 +15,7 @@ export class DynamicImportTransformer {
|
|
|
22
15
|
node.arguments.length &&
|
|
23
16
|
ts.isLiteralExpression(node.arguments[0])
|
|
24
17
|
) {
|
|
25
|
-
const lit =
|
|
18
|
+
const lit = state.normalizeModuleSpecifier(node.arguments[0])!;
|
|
26
19
|
if (lit !== node.arguments[0]) {
|
|
27
20
|
return state.factory.updateCallExpression(node, node.expression, node.typeArguments, [lit, ...node.arguments.slice(1)]);
|
|
28
21
|
}
|
|
@@ -1,24 +1,16 @@
|
|
|
1
1
|
import ts from 'typescript';
|
|
2
2
|
|
|
3
|
-
import { TransformerState, OnMethod, OnClass, AfterClass, CoreUtil,
|
|
3
|
+
import { TransformerState, OnMethod, OnClass, AfterClass, CoreUtil, OnFunction } from '@travetto/transformer';
|
|
4
4
|
|
|
5
|
-
import type { FunctionMetadataTag } from '../src/function';
|
|
5
|
+
import type { FunctionMetadataTag } from '../src/function.ts';
|
|
6
|
+
import { MetadataRegistrationUtil } from './transformer/metadata.ts';
|
|
6
7
|
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const REGISTER_IMPORT = `${RUNTIME_MOD_SRC}/function`;
|
|
10
|
-
|
|
11
|
-
const methods = Symbol.for(`${RUNTIME_MOD}:methods`);
|
|
12
|
-
const cls = Symbol.for(`${RUNTIME_MOD}:class`);
|
|
13
|
-
const fn = Symbol.for(`${RUNTIME_MOD}:function`);
|
|
14
|
-
const registerImport = Symbol.for(`${RUNTIME_MOD}:registerImport`);
|
|
15
|
-
const registerFn = 'registerFunction';
|
|
8
|
+
const MethodsSymbol = Symbol();
|
|
9
|
+
const ClassSymbol = Symbol();
|
|
16
10
|
|
|
17
11
|
interface MetadataInfo {
|
|
18
|
-
[
|
|
19
|
-
[
|
|
20
|
-
[cls]?: FunctionMetadataTag;
|
|
21
|
-
[fn]?: number;
|
|
12
|
+
[MethodsSymbol]?: Record<string, FunctionMetadataTag>;
|
|
13
|
+
[ClassSymbol]?: FunctionMetadataTag;
|
|
22
14
|
}
|
|
23
15
|
|
|
24
16
|
/**
|
|
@@ -26,35 +18,17 @@ interface MetadataInfo {
|
|
|
26
18
|
*/
|
|
27
19
|
export class RegisterTransformer {
|
|
28
20
|
|
|
29
|
-
static #tag(state: TransformerState, node: ts.Node): FunctionMetadataTag {
|
|
30
|
-
const hash = SystemUtil.naiveHash(node.getText());
|
|
31
|
-
try {
|
|
32
|
-
const range = CoreUtil.getRangeOf(state.source, node) ?? [0, 0];
|
|
33
|
-
if (ts.isMethodDeclaration(node) || ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node)) {
|
|
34
|
-
const bodyStart = CoreUtil.getRangeOf(state.source, node?.body?.statements[0])?.[0];
|
|
35
|
-
if (bodyStart) {
|
|
36
|
-
range.push(bodyStart);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
return { hash, lines: range };
|
|
40
|
-
} catch {
|
|
41
|
-
return { hash, lines: [0, 0] };
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
static #valid({ importName: imp }: TransformerState): boolean {
|
|
46
|
-
return !imp.startsWith(REGISTER_IMPORT);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
21
|
/**
|
|
50
22
|
* Hash each class
|
|
51
23
|
*/
|
|
52
24
|
@OnClass()
|
|
53
25
|
static collectClassMetadata(state: TransformerState & MetadataInfo, node: ts.ClassDeclaration): ts.ClassDeclaration {
|
|
54
|
-
if (!
|
|
26
|
+
if (!MetadataRegistrationUtil.isValid(state)) {
|
|
55
27
|
return node; // Exclude self
|
|
56
28
|
}
|
|
57
|
-
|
|
29
|
+
|
|
30
|
+
state[ClassSymbol] = MetadataRegistrationUtil.tag(state, node);
|
|
31
|
+
state[MethodsSymbol] = {};
|
|
58
32
|
return node;
|
|
59
33
|
}
|
|
60
34
|
|
|
@@ -63,9 +37,8 @@ export class RegisterTransformer {
|
|
|
63
37
|
*/
|
|
64
38
|
@OnMethod()
|
|
65
39
|
static collectMethodMetadata(state: TransformerState & MetadataInfo, node: ts.MethodDeclaration): ts.MethodDeclaration {
|
|
66
|
-
if (state[
|
|
67
|
-
state[
|
|
68
|
-
state[methods]![node.name.escapedText.toString()] = this.#tag(state, node);
|
|
40
|
+
if (state[ClassSymbol] && ts.isIdentifier(node.name) && !CoreUtil.isAbstract(node) && ts.isClassDeclaration(node.parent)) {
|
|
41
|
+
state[MethodsSymbol]![node.name.escapedText.toString()] = MetadataRegistrationUtil.tag(state, node);
|
|
69
42
|
}
|
|
70
43
|
return node;
|
|
71
44
|
}
|
|
@@ -75,69 +48,26 @@ export class RegisterTransformer {
|
|
|
75
48
|
*/
|
|
76
49
|
@AfterClass()
|
|
77
50
|
static registerClassMetadata(state: TransformerState & MetadataInfo, node: ts.ClassDeclaration): ts.ClassDeclaration {
|
|
78
|
-
if (!state[
|
|
51
|
+
if (!state[ClassSymbol]) {
|
|
79
52
|
return node;
|
|
80
53
|
}
|
|
81
54
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
const meta = state.factory.createCallExpression(
|
|
87
|
-
state.createAccess(state[registerImport].ident, registerFn),
|
|
88
|
-
[],
|
|
89
|
-
[
|
|
90
|
-
state.createIdentifier(name),
|
|
91
|
-
state.getModuleIdentifier(),
|
|
92
|
-
state.fromLiteral(state[cls]),
|
|
93
|
-
state.extendObjectLiteral(state[methods] || {}),
|
|
94
|
-
state.fromLiteral(CoreUtil.isAbstract(node)),
|
|
95
|
-
]
|
|
96
|
-
);
|
|
97
|
-
|
|
98
|
-
state[methods] = {};
|
|
99
|
-
delete state[cls];
|
|
100
|
-
|
|
101
|
-
return state.factory.updateClassDeclaration(
|
|
102
|
-
node,
|
|
103
|
-
node.modifiers,
|
|
104
|
-
node.name,
|
|
105
|
-
node.typeParameters,
|
|
106
|
-
node.heritageClauses,
|
|
107
|
-
[
|
|
108
|
-
state.factory.createClassStaticBlockDeclaration(
|
|
109
|
-
state.factory.createBlock([
|
|
110
|
-
state.factory.createExpressionStatement(meta)
|
|
111
|
-
])
|
|
112
|
-
),
|
|
113
|
-
...node.members
|
|
114
|
-
]
|
|
115
|
-
);
|
|
55
|
+
const { [MethodsSymbol]: m, [ClassSymbol]: c } = state;
|
|
56
|
+
delete state[ClassSymbol];
|
|
57
|
+
return MetadataRegistrationUtil.registerClass(state, node, c!, m);
|
|
116
58
|
}
|
|
117
59
|
|
|
118
60
|
/**
|
|
119
|
-
*
|
|
61
|
+
* Register proper functions
|
|
120
62
|
*/
|
|
121
63
|
@OnFunction()
|
|
122
64
|
static registerFunctionMetadata(state: TransformerState & MetadataInfo, node: ts.FunctionDeclaration | ts.FunctionExpression): typeof node {
|
|
123
|
-
if (!
|
|
65
|
+
if (!MetadataRegistrationUtil.isValid(state) || !ts.isFunctionDeclaration(node)) {
|
|
124
66
|
return node;
|
|
125
67
|
}
|
|
126
68
|
|
|
127
|
-
if (
|
|
128
|
-
|
|
129
|
-
state[registerImport] ??= state.importFile(REGISTER_IMPORT);
|
|
130
|
-
const tag = this.#tag(state, node);
|
|
131
|
-
const meta = state.factory.createCallExpression(
|
|
132
|
-
state.createAccess(state[registerImport].ident, registerFn),
|
|
133
|
-
[],
|
|
134
|
-
[
|
|
135
|
-
state.createIdentifier(node.name),
|
|
136
|
-
state.getModuleIdentifier(),
|
|
137
|
-
state.fromLiteral(tag),
|
|
138
|
-
]
|
|
139
|
-
);
|
|
140
|
-
state.addStatements([state.factory.createExpressionStatement(meta)]);
|
|
69
|
+
if (node.name && node.parent && ts.isSourceFile(node.parent)) {
|
|
70
|
+
MetadataRegistrationUtil.registerFunction(state, node);
|
|
141
71
|
}
|
|
142
72
|
return node;
|
|
143
73
|
}
|
|
@@ -3,7 +3,7 @@ import ts from 'typescript';
|
|
|
3
3
|
import { TransformerState, OnFile } from '@travetto/transformer';
|
|
4
4
|
|
|
5
5
|
const PATH_REGEX = /^['"](node:)?path['"]$/;
|
|
6
|
-
const
|
|
6
|
+
const PATH_IMPORT = '@travetto/manifest/src/path.ts';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Rewriting path imports to use manifest's path
|
|
@@ -22,7 +22,7 @@ export class PathImportTransformer {
|
|
|
22
22
|
stmt,
|
|
23
23
|
stmt.modifiers,
|
|
24
24
|
stmt.importClause,
|
|
25
|
-
state.factory.createStringLiteral(
|
|
25
|
+
state.factory.createStringLiteral(PATH_IMPORT),
|
|
26
26
|
stmt.attributes
|
|
27
27
|
);
|
|
28
28
|
return state.factory.updateSourceFile(node, node.statements.map(x =>
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import ts from 'typescript';
|
|
2
|
-
|
|
3
|
-
import { TransformerState, OnCall } from '@travetto/transformer';
|
|
4
|
-
|
|
5
|
-
const SRC = '@travetto/runtime/src/types.ts';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Allows for removal of type helpers at compile time
|
|
9
|
-
*/
|
|
10
|
-
export class TypeHelpersTransformer {
|
|
11
|
-
@OnCall()
|
|
12
|
-
static onTypeHelper(state: TransformerState, node: ts.CallExpression): ts.Node {
|
|
13
|
-
if (
|
|
14
|
-
ts.isIdentifier(node.expression) &&
|
|
15
|
-
node.arguments.length === 1 &&
|
|
16
|
-
/as(Class|Constructable|Full)|cast(To|Key)/.test(node.expression.escapedText.toString())
|
|
17
|
-
) {
|
|
18
|
-
const type = state.resolveType(node.expression);
|
|
19
|
-
if (type.key === 'unknown' && 'importName' in type && type.importName === SRC) {
|
|
20
|
-
// return node.arguments[0];
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
return node;
|
|
24
|
-
}
|
|
25
|
-
}
|