@travetto/manifest 5.0.0-rc.1 → 5.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/README.md CHANGED
@@ -35,7 +35,7 @@ During the compilation process, it is helpful to know how the output content dif
35
35
  ## Class and Function Metadata
36
36
  For the framework to work properly, metadata needs to be collected about files, classes and functions to uniquely identify them, with support for detecting changes during live reloads. To achieve this, every `class` is decorated with an additional field of `Ⲑid`. `Ⲑid` represents a computed id that is tied to the file/class combination.
37
37
 
38
- `Ⲑid` is used heavily throughout the framework for determining which classes are owned by the framework, and being able to lookup the needed data from the [RuntimeIndex](https://github.com/travetto/travetto/tree/main/module/manifest/src/runtime.ts#L12) using the `getFunctionMetadata` method.
38
+ `Ⲑid` is used heavily throughout the framework for determining which classes are owned by the framework, and being able to lookup associated data by the id.
39
39
 
40
40
  **Code: Test Class**
41
41
  ```typescript
@@ -50,32 +50,13 @@ export class TestClass {
50
50
  Object.defineProperty(exports, "__esModule", { value: true });
51
51
  exports.TestClass = void 0;
52
52
  const tslib_1 = require("tslib");
53
- const Ⲑ_runtime_1 = tslib_1.__importStar(require("@travetto/manifest/src/runtime.js"));
54
- const Ⲑ_decorator_1 = tslib_1.__importStar(require("@travetto/registry/src/decorator.js"));
55
- var ᚕf = "@travetto/manifest/doc/test-class.js";
56
- let TestClass = class TestClass {
57
- static Ⲑinit = Ⲑ_runtime_1.RuntimeIndex.registerFunction(TestClass, ᚕf, { hash: 197152026, lines: [1, 3] }, { doStuff: { hash: 51337554, lines: [2, 2] } }, false, false);
53
+ const Ⲑ_function_1 = tslib_1.__importStar(require("@travetto/runtime/src/function.js"));
54
+ var ᚕm = ["@travetto/manifest", "doc/test-class"];
55
+ class TestClass {
56
+ static Ⲑinit = Ⲑ_function_1.registerFunction(TestClass, ᚕm, { hash: 197152026, lines: [1, 3] }, { doStuff: { hash: 51337554, lines: [2, 2] } }, false, false);
58
57
  async doStuff() { }
59
- };
60
- exports.TestClass = TestClass;
61
- exports.TestClass = TestClass = tslib_1.__decorate([
62
- Ⲑ_decorator_1.Register()
63
- ], TestClass);
64
- ```
65
-
66
- **Terminal: Index Lookup at Runtime**
67
- ```bash
68
- $ trv main ./doc/lookup.ts
69
-
70
- {
71
- id: '@travetto/manifest:doc/test-class○TestClass',
72
- source: './doc/test-class.ts',
73
- hash: 197152026,
74
- lines: [ 1, 3 ],
75
- methods: { doStuff: { hash: 51337554, lines: [ 2, 2 ] } },
76
- abstract: false,
77
- synthetic: false
78
58
  }
59
+ exports.TestClass = TestClass;
79
60
  ```
80
61
 
81
62
  ## Module Indexing
@@ -91,8 +72,6 @@ Once the manifest is created, the application runtime can now read this manifest
91
72
  ## Path Normalization
92
73
  By default, all paths within the framework are assumed to be in a POSIX style, and all input paths are converted to the POSIX style. This works appropriately within a Unix and a Windows environment. This module offers up [path](https://github.com/travetto/travetto/tree/main/module/manifest/src/path.ts#L9) as an equivalent to [Node](https://nodejs.org)'s [http](https://nodejs.org/api/path.html) library. This allows for consistent behavior across all file-interactions.
93
74
 
94
- Imports pointing at $`node:path` and $`path` are rewritten at compile time to point to the implementation provided by the module. This allows for seamless import/usage patterns with the reliability needed for cross platform support.
95
-
96
75
  ## Anatomy of a Manifest
97
76
 
98
77
  **Code: Manifest for @travetto/manifest**
@@ -140,7 +119,6 @@ Imports pointing at $`node:path` and $`path` are rewritten at compile time to po
140
119
  ],
141
120
  "doc": [
142
121
  [ "DOC.tsx", "ts", 1868155200000, "doc" ],
143
- [ "doc/lookup.ts", "ts", 1868155200000, "doc" ],
144
122
  [ "doc/test-class.ts", "ts", 1868155200000, "doc" ]
145
123
  ],
146
124
  "$index": [
@@ -150,26 +128,19 @@ Imports pointing at $`node:path` and $`path` are rewritten at compile time to po
150
128
  [ "package.json", "package-json", 1868155200000 ]
151
129
  ],
152
130
  "test": [
153
- [ "test/path.ts", "ts", 1868155200000, "test" ],
154
- [ "test/runtime.ts", "ts", 1868155200000, "test" ]
131
+ [ "test/path.ts", "ts", 1868155200000, "test" ]
155
132
  ],
156
133
  "test/fixtures": [
157
134
  [ "test/fixtures/simple.ts", "fixture", 1868155200000, "test" ]
158
135
  ],
159
- "$transformer": [
160
- [ "support/transformer.function-metadata.ts", "ts", 1868155200000, "compile" ],
161
- [ "support/transformer.rewrite-path-import.ts", "ts", 1868155200000, "compile" ]
162
- ],
163
136
  "src": [
164
137
  [ "src/delta.ts", "ts", 1868155200000 ],
165
138
  [ "src/dependencies.ts", "ts", 1868155200000 ],
166
139
  [ "src/file.ts", "ts", 1868155200000 ],
167
- [ "src/global.d.ts", "typings", 1868155200000 ],
168
140
  [ "src/manifest-index.ts", "ts", 1868155200000 ],
169
141
  [ "src/module.ts", "ts", 1868155200000 ],
170
142
  [ "src/package.ts", "ts", 1868155200000 ],
171
143
  [ "src/path.ts", "ts", 1868155200000 ],
172
- [ "src/runtime.ts", "ts", 1868155200000 ],
173
144
  [ "src/util.ts", "ts", 1868155200000 ],
174
145
  [ "src/types/common.ts", "ts", 1868155200000 ],
175
146
  [ "src/types/context.ts", "ts", 1868155200000 ],
package/__index__.ts CHANGED
@@ -1,9 +1,8 @@
1
- /// <reference path="./src/global.d.ts" />
1
+ /// <reference path="../runtime/src/global.d.ts" />
2
2
 
3
3
  export * from './src/module';
4
4
  export * from './src/delta';
5
5
  export * from './src/manifest-index';
6
- export * from './src/runtime';
7
6
  export * from './src/package';
8
7
  export * from './src/util';
9
8
  export * from './src/file';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/manifest",
3
- "version": "5.0.0-rc.1",
3
+ "version": "5.0.0-rc.3",
4
4
  "description": "Support for project indexing, manifesting, along with file watching",
5
5
  "keywords": [
6
6
  "path",
package/src/file.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import fs from 'node:fs/promises';
2
2
  import { readFileSync } from 'node:fs';
3
+ import os from 'node:os';
3
4
 
4
5
  import { path } from './path';
5
6
 
@@ -8,9 +9,9 @@ export class ManifestFileUtil {
8
9
  * Write file and copy over when ready
9
10
  */
10
11
  static async bufferedFileWrite(file: string, content: string): Promise<void> {
11
- const temp = path.resolve(path.dirname(file), `.${process.hrtime()[0]}.${path.basename(file)}`);
12
- await fs.mkdir(path.dirname(file), { recursive: true });
12
+ const temp = path.resolve(os.tmpdir(), `${process.hrtime()[1]}.${path.basename(file)}`);
13
13
  await fs.writeFile(temp, content, 'utf8');
14
+ await fs.mkdir(path.dirname(file), { recursive: true });
14
15
  await fs.rename(temp, file);
15
16
  }
16
17
 
@@ -22,13 +22,12 @@ export class ManifestIndex {
22
22
  #manifest: ManifestRoot;
23
23
  #modules: IndexedModule[];
24
24
  #modulesByName: Record<string, IndexedModule> = {};
25
- #modulesByFolder: Record<string, IndexedModule> = {};
26
25
  #outputRoot: string;
27
26
  #outputToEntry = new Map<string, IndexedFile>();
28
27
  #sourceToEntry = new Map<string, IndexedFile>();
29
28
  #importToEntry = new Map<string, IndexedFile>();
30
29
 
31
- constructor(manifest: string) {
30
+ constructor(manifest: string = process.env.TRV_MANIFEST!) {
32
31
  this.init(manifest);
33
32
  }
34
33
 
@@ -50,6 +49,14 @@ export class ManifestIndex {
50
49
  this.#index();
51
50
  }
52
51
 
52
+ /**
53
+ * **WARNING**: This is a destructive operation, and should only be called before loading any code
54
+ * @private
55
+ */
56
+ reinitForModule(module: string): void {
57
+ this.init(`${this.outputRoot}/node_modules/${module}`);
58
+ }
59
+
53
60
  #moduleFiles(m: ManifestModule, files: ManifestModuleFile[]): IndexedFile[] {
54
61
  return files.map(([f, type, ts, role = 'std']) => {
55
62
  const isSource = type === 'ts' || type === 'js';
@@ -98,7 +105,6 @@ export class ManifestIndex {
98
105
  }
99
106
  }
100
107
  this.#modulesByName = Object.fromEntries(this.#modules.map(x => [x.name, x]));
101
- this.#modulesByFolder = Object.fromEntries(this.#modules.map(x => [x.sourceFolder, x]));
102
108
 
103
109
  // Store child information
104
110
  for (const mod of this.#modules) {
@@ -162,13 +168,6 @@ export class ManifestIndex {
162
168
  return this.#modulesByName[name];
163
169
  }
164
170
 
165
- /**
166
- * Get module by folder
167
- */
168
- getModuleByFolder(folder: string): IndexedModule | undefined {
169
- return this.#modulesByFolder[folder];
170
- }
171
-
172
171
  /**
173
172
  * Resolve import
174
173
  */
@@ -203,15 +202,6 @@ export class ManifestIndex {
203
202
  return name ? this.getModule(name) : undefined;
204
203
  }
205
204
 
206
- /**
207
- * Get module from import name
208
- * @param importName
209
- */
210
- getModuleFromImport(importName: string): IndexedModule | undefined {
211
- const name = this.getFromImport(importName)?.module;
212
- return name ? this.getModule(name) : undefined;
213
- }
214
-
215
205
  /**
216
206
  * Build module list from an expression list (e.g. `@travetto/rest,-@travetto/log)
217
207
  */
@@ -266,4 +256,34 @@ export class ManifestIndex {
266
256
  );
267
257
  return lookup(file.replace(`${base}/`, '').split('/'));
268
258
  }
259
+
260
+ /**
261
+ * Get manifest module by name
262
+ */
263
+ getManifestModule(mod: string): ManifestModule {
264
+ return this.manifest.modules[mod];
265
+ }
266
+
267
+ /**
268
+ * Get manifest modules
269
+ */
270
+ getManifestModules(): ManifestModule[] {
271
+ return Object.values(this.manifest.modules);
272
+ }
273
+
274
+ /**
275
+ * Get main module for manifest
276
+ */
277
+ get mainModule(): IndexedModule {
278
+ return this.getModule(this.manifest.main.name)!;
279
+ }
280
+
281
+ /**
282
+ * Get source file from import location
283
+ * @param importFile
284
+ */
285
+ getSourceFile(importFile: string | [string, string]): string {
286
+ importFile = Array.isArray(importFile) ? importFile.join('/') : importFile;
287
+ return this.getFromImport(importFile)?.sourceFile ?? importFile;
288
+ }
269
289
  }
@@ -8,13 +8,4 @@ export type ManifestModuleFolderType =
8
8
  'test/fixtures' | 'support/fixtures' | 'support/resources' |
9
9
  '$other' | '$transformer';
10
10
 
11
- export type ManifestModuleRole = 'std' | 'test' | 'doc' | 'compile' | 'build';
12
-
13
- export type FunctionMetadataTag = { hash: number, lines: [number, number] };
14
- export type FunctionMetadata = FunctionMetadataTag & {
15
- id: string;
16
- source: string;
17
- methods?: Record<string, FunctionMetadataTag>;
18
- synthetic?: boolean;
19
- abstract?: boolean;
20
- };
11
+ export type ManifestModuleRole = 'std' | 'test' | 'doc' | 'compile' | 'build';
package/src/global.d.ts DELETED
@@ -1,3 +0,0 @@
1
- declare interface Function {
2
- Ⲑid: string;
3
- }
package/src/runtime.ts DELETED
@@ -1,115 +0,0 @@
1
- import { path } from './path';
2
- import { ManifestIndex } from './manifest-index';
3
- import type { FunctionMetadata, FunctionMetadataTag } from './types/common';
4
- import type { IndexedModule, ManifestModule } from './types/manifest';
5
-
6
- const METADATA = Symbol.for('@travetto/manifest:metadata');
7
- type Metadated = { [METADATA]: FunctionMetadata };
8
-
9
- /**
10
- * Extended manifest index geared for application execution
11
- */
12
- class $RuntimeIndex extends ManifestIndex {
13
-
14
- #metadata = new Map<string, FunctionMetadata>();
15
-
16
- /**
17
- * **WARNING**: This is a destructive operation, and should only be called before loading any code
18
- * @private
19
- */
20
- reinitForModule(module: string): void {
21
- this.init(`${this.outputRoot}/node_modules/${module}`);
22
- }
23
-
24
- /**
25
- * Get internal id from file name and optionally, class name
26
- */
27
- getId(filename: string, clsName?: string): string {
28
- filename = path.toPosix(filename);
29
- const id = this.getEntry(filename)?.id ?? filename;
30
- return clsName ? `${id}○${clsName}` : id;
31
- }
32
-
33
- /**
34
- * Get main module for manifest
35
- */
36
- get mainModule(): IndexedModule {
37
- return this.getModule(this.manifest.main.name)!;
38
- }
39
-
40
- /**
41
- * Get source file from import location
42
- * @param outputFile
43
- */
44
- getSourceFile(importFile: string): string {
45
- return this.getFromImport(importFile)?.sourceFile ?? importFile;
46
- }
47
-
48
- /**
49
- * Initialize the meta data for a function/class
50
- * @param cls Class
51
- * @param `file` Filename
52
- * @param `hash` Hash of class contents
53
- * @param `line` Line number in source
54
- * @param `methods` Methods and their hashes
55
- * @param `abstract` Is the class abstract
56
- * @param `synthetic` Is this code generated at build time
57
- * @private
58
- */
59
- registerFunction(
60
- cls: Function, fileOrImport: string, tag: FunctionMetadataTag,
61
- methods?: Record<string, FunctionMetadataTag>, abstract?: boolean, synthetic?: boolean
62
- ): boolean {
63
- const source = this.getSourceFile(fileOrImport);
64
- const id = this.getId(source, cls.name);
65
- Object.defineProperty(cls, 'Ⲑid', { value: id });
66
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
67
- (cls as unknown as Metadated)[METADATA] = { id, source, ...tag, methods, abstract, synthetic };
68
- this.#metadata.set(id, { id, source, ...tag, methods, abstract, synthetic });
69
- return true;
70
- }
71
-
72
- /**
73
- * Retrieve function metadata by function, or function id
74
- */
75
- getFunctionMetadataFromClass(cls: Function | undefined): FunctionMetadata | undefined {
76
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
77
- return (cls as unknown as Metadated)?.[METADATA];
78
- }
79
-
80
- /**
81
- * Retrieve function metadata by function, or function id
82
- */
83
- getFunctionMetadata(clsId?: string | Function): FunctionMetadata | undefined {
84
- const id = clsId === undefined ? '' : typeof clsId === 'string' ? clsId : clsId.Ⲑid;
85
- return this.#metadata.get(id);
86
- }
87
-
88
- /**
89
- * Resolve module path to folder, with support for main module and monorepo support
90
- */
91
- resolveModulePath(modulePath: string): string {
92
- const main = this.manifest.main.name;
93
- const workspace = this.manifest.workspace.path;
94
- const [base, sub] = modulePath
95
- .replace(/^(@@?)(#|$)/g, (_, v, r) => `${v === '@' ? main : workspace}${r}`)
96
- .split('#');
97
- return path.resolve(this.hasModule(base) ? this.getModule(base)!.sourcePath : base, sub ?? '.');
98
- }
99
-
100
- /**
101
- * Get manifest module by name
102
- */
103
- getManifestModule(mod: string): ManifestModule {
104
- return this.manifest.modules[mod];
105
- }
106
-
107
- /**
108
- * Get manifest modules
109
- */
110
- getManifestModules(): ManifestModule[] {
111
- return Object.values(this.manifest.modules);
112
- }
113
- }
114
-
115
- export const RuntimeIndex = new $RuntimeIndex(process.env.TRV_MANIFEST!);
@@ -1,142 +0,0 @@
1
- import ts from 'typescript';
2
-
3
- import {
4
- TransformerState, OnMethod, OnClass, AfterClass,
5
- AfterFunction, CoreUtil, SystemUtil, Import
6
- } from '@travetto/transformer';
7
-
8
- import type { FunctionMetadataTag } from '../src/types/common';
9
-
10
- const MANIFEST_MOD = '@travetto/manifest';
11
- const MANIFEST_MOD_SRC = `${MANIFEST_MOD}/src`;
12
- const MANIFEST_IDX = `${MANIFEST_MOD}/__index__`;
13
-
14
- const RUNTIME_IDX_IMPORT = `${MANIFEST_MOD_SRC}/runtime`;
15
- const RUNTIME_IDX_CLS = 'RuntimeIndex';
16
-
17
- const methods = Symbol.for(`${MANIFEST_MOD}:methods`);
18
- const cls = Symbol.for(`${MANIFEST_MOD}:class`);
19
- const fn = Symbol.for(`${MANIFEST_MOD}:function`);
20
- const runtimeIdx = Symbol.for(`${MANIFEST_MOD}:runtimeIndex`);
21
-
22
- interface MetadataInfo {
23
- [runtimeIdx]?: Import;
24
- [methods]?: Record<string, FunctionMetadataTag>;
25
- [cls]?: FunctionMetadataTag;
26
- [fn]?: number;
27
- }
28
-
29
- /**
30
- * Providing metadata for classes
31
- */
32
- export class RegisterTransformer {
33
-
34
- static #tag(state: TransformerState, node: ts.Node): FunctionMetadataTag {
35
- const hash = SystemUtil.naiveHash(node.getText());
36
- try {
37
- const range = CoreUtil.getRangeOf(state.source, node) ?? [0, 0];
38
- return { hash, lines: range };
39
- } catch (err) {
40
- return { hash, lines: [0, 0] };
41
- }
42
- }
43
-
44
- static #valid({ importName: imp }: TransformerState): boolean {
45
- return !imp.startsWith(MANIFEST_MOD_SRC) && imp !== MANIFEST_IDX;
46
- }
47
-
48
- /**
49
- * Hash each class
50
- */
51
- @OnClass()
52
- static collectClassMetadata(state: TransformerState & MetadataInfo, node: ts.ClassDeclaration): ts.ClassDeclaration {
53
- if (!this.#valid(state)) {
54
- return node; // Exclude self
55
- }
56
- state[cls] = this.#tag(state, node);
57
- return node;
58
- }
59
-
60
- /**
61
- * Hash each method
62
- */
63
- @OnMethod()
64
- static collectMethodMetadata(state: TransformerState & MetadataInfo, node: ts.MethodDeclaration): ts.MethodDeclaration {
65
- if (state[cls] && ts.isIdentifier(node.name) && !CoreUtil.isAbstract(node) && ts.isClassDeclaration(node.parent)) {
66
- state[methods] ??= {};
67
- state[methods]![node.name.escapedText.toString()] = this.#tag(state, node);
68
- }
69
- return node;
70
- }
71
-
72
- /**
73
- * After visiting each class, register all the collected metadata
74
- */
75
- @AfterClass()
76
- static registerClassMetadata(state: TransformerState & MetadataInfo, node: ts.ClassDeclaration): ts.ClassDeclaration {
77
- if (!state[cls]) {
78
- return node;
79
- }
80
-
81
- state[runtimeIdx] ??= state.importFile(RUNTIME_IDX_IMPORT);
82
- const ident = state.createAccess(state[runtimeIdx].ident, RUNTIME_IDX_CLS);
83
-
84
- const name = node.name?.escapedText.toString() ?? '';
85
-
86
- const meta = state.factory.createCallExpression(
87
- state.createAccess(ident, 'registerFunction'),
88
- [],
89
- [
90
- state.createIdentifier(name),
91
- state.getFilenameIdentifier(),
92
- state.fromLiteral(state[cls]),
93
- state.extendObjectLiteral(state[methods] || {}),
94
- state.fromLiteral(CoreUtil.isAbstract(node)),
95
- state.fromLiteral(name.endsWith(TransformerState.SYNTHETIC_EXT))
96
- ]
97
- );
98
-
99
- state[methods] = {};
100
- delete state[cls];
101
-
102
- return state.factory.updateClassDeclaration(
103
- node,
104
- node.modifiers,
105
- node.name,
106
- node.typeParameters,
107
- node.heritageClauses,
108
- [
109
- state.createStaticField('Ⲑinit', meta),
110
- ...node.members
111
- ]
112
- );
113
- }
114
-
115
- /**
116
- * Give proper functions a file name
117
- */
118
- @AfterFunction()
119
- static registerFunctionMetadata(state: TransformerState & MetadataInfo, node: ts.FunctionDeclaration | ts.FunctionExpression): typeof node {
120
- if (!this.#valid(state)) {
121
- return node;
122
- }
123
-
124
- if (ts.isFunctionDeclaration(node) && node.name && node.parent && ts.isSourceFile(node.parent)) {
125
- // If we have a class like function
126
- state[runtimeIdx] ??= state.importFile(RUNTIME_IDX_IMPORT);
127
- const ident = state.createAccess(state[runtimeIdx].ident, RUNTIME_IDX_CLS);
128
- const tag = this.#tag(state, node);
129
- const meta = state.factory.createCallExpression(
130
- state.createAccess(ident, 'registerFunction'),
131
- [],
132
- [
133
- state.createIdentifier(node.name),
134
- state.getFilenameIdentifier(),
135
- state.fromLiteral(tag),
136
- ]
137
- );
138
- state.addStatements([state.factory.createExpressionStatement(meta)]);
139
- }
140
- return node;
141
- }
142
- }
@@ -1,39 +0,0 @@
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
- }