@travetto/runtime 7.0.0-rc.1 → 7.0.0-rc.3

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/src/watch.ts CHANGED
@@ -1,11 +1,24 @@
1
+ import { ManifestModuleUtil, type ChangeEventType, type ManifestModuleFileType } from '@travetto/manifest';
2
+
1
3
  import { RuntimeIndex } from './manifest-index.ts';
2
- import { ExecUtil } from './exec.ts';
3
4
  import { ShutdownManager } from './shutdown.ts';
4
5
  import { Util } from './util.ts';
6
+ import { AppError } from './error.ts';
7
+
8
+ type WatchEvent = { file: string, action: ChangeEventType, output: string, module: string, import: string, time: number };
5
9
 
6
- export type WatchEvent = { file: string, action: 'create' | 'update' | 'delete', output: string, module: string, time: number };
10
+ type WatchCompilerOptions = {
11
+ /**
12
+ * Restart the watch loop on compiler exit
13
+ */
14
+ restartOnCompilerExit?: boolean;
15
+ /**
16
+ * Run on restart
17
+ */
18
+ onRestart?: () => void;
19
+ };
7
20
 
8
- export async function* watchCompiler(cfg?: { restartOnExit?: boolean, signal?: AbortSignal }): AsyncIterable<WatchEvent> {
21
+ export async function* watchCompiler(config?: WatchCompilerOptions): AsyncIterable<WatchEvent> {
9
22
  // Load at runtime
10
23
  const { CompilerClient } = await import('@travetto/compiler/support/server/client.ts');
11
24
 
@@ -16,24 +29,59 @@ export async function* watchCompiler(cfg?: { restartOnExit?: boolean, signal?: A
16
29
  info(message, ...args): void { console.error('info', message, ...args); },
17
30
  });
18
31
 
19
- const ctrl = new AbortController();
20
- const remove = ShutdownManager.onGracefulShutdown(async () => ctrl.abort());
32
+ const controller = new AbortController();
33
+ const remove = ShutdownManager.onGracefulShutdown(async () => controller.abort());
34
+
35
+ const maxIterations = 10;
36
+ const maxWindow = 10 * 1000;
37
+ const iterations: number[] = [];
38
+ let iterationsExhausted = false;
21
39
 
22
- await client.waitForState(['compile-end', 'watch-start'], undefined, ctrl.signal);
40
+ while (
41
+ !controller.signal.aborted &&
42
+ !iterationsExhausted &&
43
+ (config?.restartOnCompilerExit || iterations.length === 0)
44
+ ) {
45
+ if (iterations.length) { // Wait on next iteration
46
+ await Util.nonBlockingTimeout(10);
47
+ }
48
+
49
+ await client.waitForState(['compile-end', 'watch-start'], undefined, controller.signal);
23
50
 
24
- if (!await client.isWatching()) { // If we get here, without a watch
25
- while (!await client.isWatching()) { // Wait until watch starts
26
- await Util.nonBlockingTimeout(1000 * 60);
51
+ if (!await client.isWatching()) { // If we get here, without a watch
52
+ throw new AppError('Compiler is not running');
53
+ } else {
54
+ if (iterations.length) {
55
+ config?.onRestart?.();
56
+ }
57
+ yield* client.fetchEvents('change', { signal: controller.signal, enforceIteration: true });
58
+ }
59
+
60
+ iterations.push(Date.now());
61
+ if (iterations.length >= maxIterations) {
62
+ iterationsExhausted = (Date.now() - iterations[0]) > maxWindow;
63
+ iterations.shift();
27
64
  }
28
- } else {
29
- yield* client.fetchEvents('change', { signal: ctrl.signal, enforceIteration: true });
30
65
  }
31
66
 
32
67
  remove();
68
+ }
69
+
70
+ export function listenForSourceChanges(onChange: () => void, debounceDelay = 10): void {
71
+ let timeout: ReturnType<typeof setTimeout> | undefined;
72
+
73
+ const validFileTypes = new Set<ManifestModuleFileType>(['ts', 'js', 'package-json', 'typings']);
33
74
 
34
- if (cfg?.restartOnExit) {
35
- // We are done, request restart
36
- await ShutdownManager.gracefulShutdown('@travetto/runtime:watchCompiler');
37
- process.exit(ExecUtil.RESTART_EXIT_CODE);
75
+ function send(): void {
76
+ clearTimeout(timeout);
77
+ timeout = setTimeout(onChange, debounceDelay);
38
78
  }
79
+
80
+ (async function (): Promise<void> {
81
+ for await (const item of watchCompiler({ restartOnCompilerExit: true, onRestart: send })) {
82
+ if (validFileTypes.has(ManifestModuleUtil.getFileType(item.file)) && RuntimeIndex.findModuleForArbitraryFile(item.file)) {
83
+ send();
84
+ }
85
+ }
86
+ })();
39
87
  }
@@ -1,4 +1,4 @@
1
- import * as ts from 'typescript';
1
+ import ts from 'typescript';
2
2
 
3
3
  import { FunctionMetadataTag } from '@travetto/runtime';
4
4
  import { CoreUtil, Import, SystemUtil, TransformerState } from '@travetto/transformer';
@@ -43,14 +43,14 @@ export class MetadataRegistrationUtil {
43
43
  */
44
44
  static registerFunction(state: TransformerState & MetadataInfo,
45
45
  node: ts.FunctionDeclaration | ts.FunctionExpression,
46
- src?: ts.FunctionDeclaration | ts.FunctionExpression | ts.InterfaceDeclaration | ts.TypeAliasDeclaration
46
+ source?: ts.FunctionDeclaration | ts.FunctionExpression | ts.InterfaceDeclaration | ts.TypeAliasDeclaration
47
47
  ): void {
48
48
  // If we have a class like function
49
49
  state[RegisterImportSymbol] ??= state.importFile(this.REGISTER_IMPORT);
50
50
 
51
- const tag = this.tag(state, src ?? node);
51
+ const tag = this.tag(state, source ?? node);
52
52
  const meta = state.factory.createCallExpression(
53
- state.createAccess(state[RegisterImportSymbol].ident, this.REGISTER_FN),
53
+ state.createAccess(state[RegisterImportSymbol].identifier, this.REGISTER_FN),
54
54
  [],
55
55
  [
56
56
  state.createIdentifier(node.name!.text),
@@ -74,7 +74,7 @@ export class MetadataRegistrationUtil {
74
74
  const name = node.name?.escapedText.toString() ?? '';
75
75
 
76
76
  const meta = state.factory.createCallExpression(
77
- state.createAccess(state[RegisterImportSymbol].ident, this.REGISTER_FN),
77
+ state.createAccess(state[RegisterImportSymbol].identifier, this.REGISTER_FN),
78
78
  [],
79
79
  [
80
80
  state.createIdentifier(name),
@@ -18,7 +18,7 @@ export class ConcreteTransformer {
18
18
  static #createConcreteFunction(state: TransformerState, name: string | ts.Identifier): ts.FunctionDeclaration {
19
19
  const final = typeof name === 'string' ? name : name.getText();
20
20
 
21
- const dec = state.factory.createFunctionDeclaration(
21
+ const declaration = state.factory.createFunctionDeclaration(
22
22
  // eslint-disable-next-line no-bitwise
23
23
  state.factory.createModifiersFromModifierFlags(ts.ModifierFlags.Export | ts.ModifierFlags.Const),
24
24
  undefined, `${final}$Concrete`, [], [], undefined,
@@ -26,13 +26,13 @@ export class ConcreteTransformer {
26
26
  );
27
27
 
28
28
  state.addStatements([
29
- dec,
29
+ declaration,
30
30
  state.factory.createExpressionStatement(
31
31
  state.factory.createCallExpression(
32
32
  state.createAccess('Object', 'defineProperty'),
33
33
  undefined,
34
34
  [
35
- dec.name!,
35
+ declaration.name!,
36
36
  state.fromLiteral('name'),
37
37
  state.fromLiteral({ value: final })
38
38
  ]
@@ -40,7 +40,7 @@ export class ConcreteTransformer {
40
40
  )
41
41
  ]);
42
42
 
43
- return dec;
43
+ return declaration;
44
44
  }
45
45
 
46
46
  /**
@@ -83,26 +83,26 @@ export class ConsoleLogTransformer {
83
83
 
84
84
  const chain = node.expression;
85
85
  const name = chain.name;
86
- const prop = chain.expression;
86
+ const expr = chain.expression;
87
87
 
88
- if (!ts.isIdentifier(prop) || prop.escapedText !== 'console' || !ts.isIdentifier(name)) {
88
+ if (!ts.isIdentifier(expr) || expr.escapedText !== 'console' || !ts.isIdentifier(name)) {
89
89
  return node;
90
90
  }
91
91
 
92
92
  const level = name.escapedText!;
93
93
 
94
94
  if (VALID_LEVELS[level]) {
95
- const ident = state.imported ??= state.importFile(CONSOLE_IMPORT).ident;
95
+ const identifier = state.imported ??= state.importFile(CONSOLE_IMPORT).identifier;
96
96
  return state.factory.updateCallExpression(
97
97
  node,
98
- state.createAccess(ident, 'log'),
98
+ state.createAccess(identifier, 'log'),
99
99
  node.typeArguments,
100
100
  [
101
101
  LiteralUtil.fromLiteral(state.factory, {
102
102
  level: state.factory.createStringLiteral(VALID_LEVELS[level]),
103
103
  import: state.getModuleIdentifier(),
104
104
  line: state.source.getLineAndCharacterOfPosition(node.getStart(state.source)).line + 1,
105
- scope: state.scope?.map(x => x.name).join(':'),
105
+ scope: state.scope?.map(part => part.name).join(':'),
106
106
  args: node.arguments.slice(0)
107
107
  }),
108
108
  ]
@@ -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.ts').ident;
22
+ const imp = state.importFile('@travetto/runtime/src/debug.ts').identifier;
23
23
  state[DebugSymbol] = CoreUtil.createAccess(state.factory, imp, 'tryDebugger');
24
24
  }
25
25
 
@@ -52,9 +52,9 @@ export class RegisterTransformer {
52
52
  return node;
53
53
  }
54
54
 
55
- const { [MethodsSymbol]: m, [ClassSymbol]: c } = state;
55
+ const { [MethodsSymbol]: method, [ClassSymbol]: cls } = state;
56
56
  delete state[ClassSymbol];
57
- return MetadataRegistrationUtil.registerClass(state, node, c!, m);
57
+ return MetadataRegistrationUtil.registerClass(state, node, cls!, method);
58
58
  }
59
59
 
60
60
  /**
@@ -5,6 +5,9 @@ import { TransformerState, OnFile } from '@travetto/transformer';
5
5
  const PATH_REGEX = /^['"](node:)?path['"]$/;
6
6
  const PATH_IMPORT = '@travetto/manifest/src/path.ts';
7
7
 
8
+ const isImport = (node: ts.Node): node is ts.ImportDeclaration =>
9
+ ts.isImportDeclaration(node) && PATH_REGEX.test(node.moduleSpecifier?.getText() ?? '');
10
+
8
11
  /**
9
12
  * Rewriting path imports to use manifest's path
10
13
  */
@@ -15,18 +18,17 @@ export class PathImportTransformer {
15
18
  */
16
19
  @OnFile()
17
20
  static rewritePathImport(state: TransformerState, node: ts.SourceFile): ts.SourceFile {
18
- const stmt = node.statements.find((x): x is ts.ImportDeclaration =>
19
- ts.isImportDeclaration(x) && PATH_REGEX.test(x.moduleSpecifier?.getText() ?? ''));
20
- if (stmt) {
21
+ const statement = node.statements.find(isImport);
22
+ if (statement) {
21
23
  const updated = state.factory.updateImportDeclaration(
22
- stmt,
23
- stmt.modifiers,
24
- stmt.importClause,
24
+ statement,
25
+ statement.modifiers,
26
+ statement.importClause,
25
27
  state.factory.createStringLiteral(PATH_IMPORT),
26
- stmt.attributes
28
+ statement.attributes
27
29
  );
28
- return state.factory.updateSourceFile(node, node.statements.map(x =>
29
- x === stmt ? updated : x
30
+ return state.factory.updateSourceFile(node, node.statements.map(item =>
31
+ item === statement ? updated : item
30
32
  ));
31
33
  }
32
34
  return node;