@travetto/runtime 5.0.0-rc.10

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.
@@ -0,0 +1,112 @@
1
+ import ts from 'typescript';
2
+
3
+ import {
4
+ TransformerState, OnCall, LiteralUtil,
5
+ OnClass, AfterClass, OnMethod, AfterMethod, AfterFunction, OnFunction
6
+ } from '@travetto/transformer';
7
+
8
+ const CONSOLE_IMPORT = '@travetto/runtime/src/console';
9
+ const MANIFEST_MOD = '@travetto/manifest';
10
+
11
+ type CustomState = TransformerState & {
12
+ scope: { type: 'method' | 'class' | 'function', name: string }[];
13
+ imported?: ts.Identifier;
14
+ };
15
+
16
+ const VALID_LEVELS: Record<string, string> = {
17
+ log: 'info',
18
+ info: 'info',
19
+ debug: 'debug',
20
+ warn: 'warn',
21
+ error: 'error'
22
+ };
23
+
24
+ /**
25
+ * Logging support with code-location aware messages.
26
+ */
27
+ export class ConsoleLogTransformer {
28
+
29
+ static initState(state: CustomState): void {
30
+ state.scope = state.scope ?? [];
31
+ }
32
+
33
+ @OnClass()
34
+ static startClassForLog(state: CustomState, node: ts.ClassDeclaration): typeof node {
35
+ this.initState(state);
36
+ state.scope.push({ type: 'class', name: node.name?.text ?? 'unknown' });
37
+ return node;
38
+ }
39
+
40
+ @AfterClass()
41
+ static leaveClassForLog(state: CustomState, node: ts.ClassDeclaration): typeof node {
42
+ state.scope.pop();
43
+ return node;
44
+ }
45
+
46
+ @OnMethod()
47
+ static startMethodForLog(state: CustomState, node: ts.MethodDeclaration): typeof node {
48
+ this.initState(state);
49
+ let name = 'unknown';
50
+ if (ts.isIdentifier(node.name) || ts.isPrivateIdentifier(node.name)) {
51
+ name = node.name?.text ?? name;
52
+ }
53
+ state.scope.push({ type: 'method', name });
54
+ return node;
55
+ }
56
+
57
+ @AfterMethod()
58
+ static leaveMethodForLog(state: CustomState, node: ts.MethodDeclaration): typeof node {
59
+ state.scope.pop();
60
+ return node;
61
+ }
62
+
63
+ @OnFunction()
64
+ static startFunctionForLog(state: CustomState, node: ts.FunctionDeclaration | ts.FunctionExpression): typeof node {
65
+ this.initState(state);
66
+ state.scope.push({ type: 'function', name: node.name?.text ?? 'unknown' });
67
+ return node;
68
+ }
69
+
70
+ @AfterFunction()
71
+ static leaveFunctionForLog(state: CustomState, node: ts.FunctionDeclaration | ts.FunctionExpression): typeof node {
72
+ state.scope.pop();
73
+ return node;
74
+ }
75
+
76
+ @OnCall()
77
+ static onLogCall(state: CustomState, node: ts.CallExpression): typeof node | ts.Identifier {
78
+ if (!ts.isPropertyAccessExpression(node.expression) || state.importName.startsWith(MANIFEST_MOD)) {
79
+ return node;
80
+ }
81
+
82
+ const chain = node.expression;
83
+ const name = chain.name;
84
+ const prop = chain.expression;
85
+
86
+ if (!ts.isIdentifier(prop) || prop.escapedText !== 'console' || !ts.isIdentifier(name)) {
87
+ return node;
88
+ }
89
+
90
+ const level = name.escapedText!;
91
+
92
+ if (VALID_LEVELS[level]) {
93
+ const ident = state.imported ??= state.importFile(CONSOLE_IMPORT, 'ᚕ_c').ident;
94
+ return state.factory.updateCallExpression(
95
+ node,
96
+ state.createAccess(ident, 'log'),
97
+ node.typeArguments,
98
+ [
99
+ LiteralUtil.fromLiteral(state.factory, {
100
+ level: state.factory.createStringLiteral(VALID_LEVELS[level]),
101
+ import: state.getModuleIdentifier(),
102
+ line: state.source.getLineAndCharacterOfPosition(node.getStart(state.source)).line + 1,
103
+ scope: state.scope?.map(x => x.name).join(':'),
104
+ args: node.arguments.slice(0)
105
+ }),
106
+ ]
107
+ );
108
+ } else {
109
+ return node;
110
+ }
111
+ }
112
+ }
@@ -0,0 +1,41 @@
1
+ import ts from 'typescript';
2
+
3
+ import { TransformerState, OnMethod, CoreUtil } from '@travetto/transformer';
4
+
5
+ const DebugⲐ = Symbol.for('@travetto/runtime:debug');
6
+
7
+ /**
8
+ * Debug transformation state
9
+ */
10
+ interface DebugState {
11
+ [DebugⲐ]?: ts.Expression;
12
+ }
13
+
14
+ /**
15
+ * Add debugger-optional statement to methods that should be debuggable
16
+ */
17
+ export class DebugEntryTransformer {
18
+
19
+ @OnMethod('DebugBreak')
20
+ static debugOnEntry(state: TransformerState & DebugState, node: ts.MethodDeclaration): ts.MethodDeclaration {
21
+ if (!state[DebugⲐ]) {
22
+ const imp = state.importFile('@travetto/runtime/src/debug').ident;
23
+ state[DebugⲐ] = CoreUtil.createAccess(state.factory, imp, 'tryDebugger');
24
+ }
25
+
26
+ return state.factory.updateMethodDeclaration(node,
27
+ node.modifiers,
28
+ node.asteriskToken,
29
+ node.name,
30
+ node.questionToken,
31
+ node.typeParameters,
32
+ node.parameters,
33
+ node.type,
34
+ node.body ? state.factory.updateBlock(node.body, [
35
+ state.factory.createIfStatement(state[DebugⲐ]!,
36
+ state.factory.createExpressionStatement(state.factory.createIdentifier('debugger'))),
37
+ ...node.body.statements
38
+ ]) : node.body
39
+ );
40
+ }
41
+ }
@@ -0,0 +1,144 @@
1
+ import ts from 'typescript';
2
+
3
+ import {
4
+ TransformerState, OnMethod, OnClass, AfterClass,
5
+ CoreUtil, SystemUtil, Import, OnFunction
6
+ } from '@travetto/transformer';
7
+
8
+ import type { FunctionMetadataTag } from '../src/function';
9
+
10
+ const RUNTIME_MOD = '@travetto/runtime';
11
+ const RUNTIME_MOD_SRC = `${RUNTIME_MOD}/src`;
12
+ const REGISTER_IMPORT = `${RUNTIME_MOD_SRC}/function`;
13
+
14
+ const methods = Symbol.for(`${RUNTIME_MOD}:methods`);
15
+ const cls = Symbol.for(`${RUNTIME_MOD}:class`);
16
+ const fn = Symbol.for(`${RUNTIME_MOD}:function`);
17
+ const registerImport = Symbol.for(`${RUNTIME_MOD}:registerImport`);
18
+ const registerFn = 'registerFunction';
19
+
20
+ interface MetadataInfo {
21
+ [registerImport]?: Import;
22
+ [methods]?: Record<string, FunctionMetadataTag>;
23
+ [cls]?: FunctionMetadataTag;
24
+ [fn]?: number;
25
+ }
26
+
27
+ /**
28
+ * Providing metadata for classes
29
+ */
30
+ export class RegisterTransformer {
31
+
32
+ static #tag(state: TransformerState, node: ts.Node): FunctionMetadataTag {
33
+ const hash = SystemUtil.naiveHash(node.getText());
34
+ try {
35
+ const range = CoreUtil.getRangeOf(state.source, node) ?? [0, 0];
36
+ if (ts.isMethodDeclaration(node) || ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node)) {
37
+ const bodyStart = CoreUtil.getRangeOf(state.source, node?.body?.statements[0])?.[0];
38
+ if (bodyStart) {
39
+ range.push(bodyStart);
40
+ }
41
+ }
42
+ return { hash, lines: range };
43
+ } catch {
44
+ return { hash, lines: [0, 0] };
45
+ }
46
+ }
47
+
48
+ static #valid({ importName: imp }: TransformerState): boolean {
49
+ return !imp.startsWith(REGISTER_IMPORT);
50
+ }
51
+
52
+ /**
53
+ * Hash each class
54
+ */
55
+ @OnClass()
56
+ static collectClassMetadata(state: TransformerState & MetadataInfo, node: ts.ClassDeclaration): ts.ClassDeclaration {
57
+ if (!this.#valid(state)) {
58
+ return node; // Exclude self
59
+ }
60
+ state[cls] = this.#tag(state, node);
61
+ return node;
62
+ }
63
+
64
+ /**
65
+ * Hash each method
66
+ */
67
+ @OnMethod()
68
+ static collectMethodMetadata(state: TransformerState & MetadataInfo, node: ts.MethodDeclaration): ts.MethodDeclaration {
69
+ if (state[cls] && ts.isIdentifier(node.name) && !CoreUtil.isAbstract(node) && ts.isClassDeclaration(node.parent)) {
70
+ state[methods] ??= {};
71
+ state[methods]![node.name.escapedText.toString()] = this.#tag(state, node);
72
+ }
73
+ return node;
74
+ }
75
+
76
+ /**
77
+ * After visiting each class, register all the collected metadata
78
+ */
79
+ @AfterClass()
80
+ static registerClassMetadata(state: TransformerState & MetadataInfo, node: ts.ClassDeclaration): ts.ClassDeclaration {
81
+ if (!state[cls]) {
82
+ return node;
83
+ }
84
+
85
+ state[registerImport] ??= state.importFile(REGISTER_IMPORT);
86
+
87
+ const name = node.name?.escapedText.toString() ?? '';
88
+
89
+ const meta = state.factory.createCallExpression(
90
+ state.createAccess(state[registerImport].ident, registerFn),
91
+ [],
92
+ [
93
+ state.createIdentifier(name),
94
+ state.getModuleIdentifier(),
95
+ state.fromLiteral(state[cls]),
96
+ state.extendObjectLiteral(state[methods] || {}),
97
+ state.fromLiteral(CoreUtil.isAbstract(node)),
98
+ state.fromLiteral(name.endsWith(TransformerState.SYNTHETIC_EXT))
99
+ ]
100
+ );
101
+
102
+ state[methods] = {};
103
+ delete state[cls];
104
+
105
+ return state.factory.updateClassDeclaration(
106
+ node,
107
+ node.modifiers,
108
+ node.name,
109
+ node.typeParameters,
110
+ node.heritageClauses,
111
+ [
112
+ state.createStaticField('Ⲑinit', meta),
113
+ ...node.members
114
+ ]
115
+ );
116
+ }
117
+
118
+ /**
119
+ * Give proper functions a file name
120
+ */
121
+ @OnFunction()
122
+ static registerFunctionMetadata(state: TransformerState & MetadataInfo, node: ts.FunctionDeclaration | ts.FunctionExpression): typeof node {
123
+ if (!this.#valid(state)) {
124
+ return node;
125
+ }
126
+
127
+ if (ts.isFunctionDeclaration(node) && node.name && node.parent && ts.isSourceFile(node.parent)) {
128
+ // If we have a class like function
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)]);
141
+ }
142
+ return node;
143
+ }
144
+ }
@@ -0,0 +1,39 @@
1
+ import ts from 'typescript';
2
+
3
+ import { TransformerState, OnFile } from '@travetto/transformer';
4
+
5
+ const PATH_REGEX = /^['"](node:)?path['"]$/;
6
+ const PATH_TARGET = '@travetto/manifest/src/path';
7
+ const SKIP_SRC = /^@travetto\/manifest\/(src|support)/;
8
+
9
+ /**
10
+ * Rewriting path imports to use manifest's path
11
+ */
12
+ export class PathImportTransformer {
13
+
14
+ /**
15
+ * Hash each class
16
+ */
17
+ @OnFile()
18
+ static rewritePathImport(state: TransformerState, node: ts.SourceFile): ts.SourceFile {
19
+ if (SKIP_SRC.test(state.importName)) {
20
+ return node;
21
+ }
22
+
23
+ const stmt = node.statements.find((x): x is ts.ImportDeclaration =>
24
+ ts.isImportDeclaration(x) && PATH_REGEX.test(x.moduleSpecifier?.getText() ?? ''));
25
+ if (stmt) {
26
+ const updated = state.factory.updateImportDeclaration(
27
+ stmt,
28
+ stmt.modifiers,
29
+ stmt.importClause,
30
+ state.factory.createStringLiteral(PATH_TARGET),
31
+ stmt.attributes
32
+ );
33
+ return state.factory.updateSourceFile(node, node.statements.map(x =>
34
+ x === stmt ? updated : x
35
+ ));
36
+ }
37
+ return node;
38
+ }
39
+ }
@@ -0,0 +1,25 @@
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
+ }