@travetto/compiler 3.0.2 → 3.1.0-rc.0
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 +31 -22
- package/bin/trv.js +5 -5
- package/package.json +5 -5
- package/src/compiler.ts +16 -22
- package/src/state.ts +13 -12
- package/src/util.ts +5 -15
- package/src/watch.ts +53 -28
- package/support/transpile.ts +50 -14
- package/tsconfig.trv.json +1 -0
package/README.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<!-- This file was generated by @travetto/doc and should not be modified directly -->
|
|
2
|
-
<!-- Please modify https://github.com/travetto/travetto/tree/main/module/compiler/DOC.
|
|
2
|
+
<!-- Please modify https://github.com/travetto/travetto/tree/main/module/compiler/DOC.tsx and execute "npx trv doc" to rebuild -->
|
|
3
3
|
# Compiler
|
|
4
|
+
|
|
4
5
|
## The compiler infrastructure for the Travetto framework
|
|
5
6
|
|
|
6
7
|
**Install: @travetto/compiler**
|
|
@@ -13,37 +14,52 @@ yarn add @travetto/compiler
|
|
|
13
14
|
```
|
|
14
15
|
|
|
15
16
|
This module expands upon the [Typescript](https://typescriptlang.org) compiler, with the additional features:
|
|
16
|
-
|
|
17
17
|
* Integration with the [Transformation](https://github.com/travetto/travetto/tree/main/module/transformer#readme "Functionality for AST transformations, with transformer registration, and general utils") module, allowing for rich, type-aware transformations
|
|
18
18
|
* Automatic conversion to either [Ecmascript Module](https://nodejs.org/api/esm.html) or [CommonJS](https://nodejs.org/api/modules.html) based on the [Package JSON](https://docs.npmjs.com/cli/v9/configuring-npm/package-json) `type` value
|
|
19
19
|
* Removal of type only imports which can break [Ecmascript Module](https://nodejs.org/api/esm.html)-style output
|
|
20
20
|
* Automatic addition of `.js` extension to imports to also support [Ecmascript Module](https://nodejs.org/api/esm.html)-style output
|
|
21
|
-
|
|
22
|
-
Beyond the [Typescript](https://typescriptlang.org) compiler functionality, the module provides the primary entry point into the development process.
|
|
21
|
+
Beyond the [Typescript](https://typescriptlang.org) compiler functionality, the module provides the primary entry point into the development process.
|
|
23
22
|
|
|
24
23
|
## CLI
|
|
25
|
-
|
|
26
|
-
The cli, [trv](https://github.com/travetto/travetto/tree/main/module/compiler/bin/trv.js#L60) is a compilation aware entry point, that has the ability to check for active builds, and ongoing watch operations to ensure only one process is building at a time. Within the framework, regardless of mono-repo or not, always builds the entire project. With the efficient caching behavior, this leads to generally a minimal overhead but allows for centralization of all operations.
|
|
24
|
+
The cli, [trv](https://github.com/travetto/travetto/tree/main/module/compiler/bin/trv.js#L60) is a compilation aware entry point, that has the ability to check for active builds, and ongoing watch operations to ensure only one process is building at a time. Within the framework, regardless of mono-repo or not, always builds the entire project. With the efficient caching behavior, this leads to generally a minimal overhead but allows for centralization of all operations.
|
|
27
25
|
|
|
28
26
|
The CLI supports the following operations:
|
|
29
|
-
|
|
30
|
-
|
|
31
27
|
* `clean` - Removes the output folder, and if `-a` is also passed, will also clean out the compiler folder
|
|
32
28
|
* `build` - Will attempt to build the project. If the project is already built, will return immediately. If the project is being built somewhere else, will wait until a build is completed.
|
|
33
29
|
* `watch` - If nothing else is watching, will start the watch operation. Otherwise will return immediately.
|
|
34
|
-
* `manifest` - Will produce a manifest. If no file is passed in the command line arguments, will output to stdout
|
|
30
|
+
* `manifest` - Will produce a manifest. If no file is passed in the command line arguments, will output to stdout.
|
|
35
31
|
* `<other>` - Will be delegated to the [Command Line Interface](https://github.com/travetto/travetto/tree/main/module/cli#readme "CLI infrastructure for Travetto framework") entry point after a successful build.
|
|
36
|
-
|
|
37
32
|
In addition to the normal output, the compiler supports an environment variable `TRV_BUILD` that supports the following values: `debug`, `info`, `warn` or `none`. This provides different level of logging during the build process which is helpful to diagnose any odd behaviors. When invoking an unknown command (e.g. `<other>` from above), the default level is `warn`. Otherwise the default logging level is `info`.
|
|
38
33
|
|
|
39
34
|
**Terminal: Sample trv output with debug logging**
|
|
40
35
|
```bash
|
|
41
36
|
$ TRV_BUILD=debug trv build
|
|
42
37
|
|
|
43
|
-
2029-03-14T04:00:00.618Z [lock ]
|
|
44
|
-
2029-03-14T04:00:00.837Z [
|
|
45
|
-
2029-03-14T04:00:01.510Z [
|
|
46
|
-
2029-03-14T04:00:02.450Z [
|
|
38
|
+
2029-03-14T04:00:00.618Z [lock ] Acquiring build
|
|
39
|
+
2029-03-14T04:00:00.837Z [precompile ] Started
|
|
40
|
+
2029-03-14T04:00:01.510Z [precompile ] @travetto/terminal Skipped
|
|
41
|
+
2029-03-14T04:00:02.450Z [precompile ] @travetto/manifest Skipped
|
|
42
|
+
2029-03-14T04:00:02.762Z [precompile ] @travetto/transformer Skipped
|
|
43
|
+
2029-03-14T04:00:02.947Z [precompile ] @travetto/compiler Skipped
|
|
44
|
+
2029-03-14T04:00:03.093Z [precompile ] Completed
|
|
45
|
+
2029-03-14T04:00:04.003Z [manifest ] Started
|
|
46
|
+
2029-03-14T04:00:04.495Z [manifest ] Completed
|
|
47
|
+
2029-03-14T04:00:05.066Z [transformers ] Started
|
|
48
|
+
2029-03-14T04:00:05.307Z [transformers ] @travetto/base Skipped
|
|
49
|
+
2029-03-14T04:00:05.952Z [transformers ] @travetto/cli Skipped
|
|
50
|
+
2029-03-14T04:00:06.859Z [transformers ] @travetto/manifest Skipped
|
|
51
|
+
2029-03-14T04:00:07.720Z [transformers ] @travetto/registry Skipped
|
|
52
|
+
2029-03-14T04:00:08.179Z [transformers ] @travetto/schema Skipped
|
|
53
|
+
2029-03-14T04:00:08.588Z [transformers ] Completed
|
|
54
|
+
2029-03-14T04:00:09.493Z [delta ] Started
|
|
55
|
+
2029-03-14T04:00:10.395Z [delta ] Completed
|
|
56
|
+
2029-03-14T04:00:10.407Z [manifest ] Started
|
|
57
|
+
2029-03-14T04:00:10.799Z [manifest ] Wrote manifest @travetto-doc/compiler
|
|
58
|
+
2029-03-14T04:00:11.013Z [manifest ] Completed
|
|
59
|
+
2029-03-14T04:00:11.827Z [compile ] Started action=build changed=
|
|
60
|
+
2029-03-14T04:00:11.894Z [compile ] Skipped
|
|
61
|
+
2029-03-14T04:00:12.133Z [lock ] Releasing build
|
|
62
|
+
2029-03-14T04:00:13.123Z [build ] Successfully built
|
|
47
63
|
```
|
|
48
64
|
|
|
49
65
|
**Terminal: Sample trv output with default log level**
|
|
@@ -52,9 +68,7 @@ $ trv build
|
|
|
52
68
|
```
|
|
53
69
|
|
|
54
70
|
## Compilation Architecture
|
|
55
|
-
|
|
56
71
|
The compiler will move through the following phases on a given compilation execution:
|
|
57
|
-
|
|
58
72
|
* `Bootstrapping` - Initial compilation of [Compiler](https://github.com/travetto/travetto/tree/main/module/compiler#readme "The compiler infrastructure for the Travetto framework")'s `support/*.ts` files
|
|
59
73
|
* `Lock Management` - Manages cross-process interaction to ensure single compiler
|
|
60
74
|
* `Build Compiler` - Leverages [Typescript](https://typescriptlang.org) to build files needed to execute compiler
|
|
@@ -66,17 +80,12 @@ The compiler will move through the following phases on a given compilation execu
|
|
|
66
80
|
* `Invoke Compiler` - Run [Typescript](https://typescriptlang.org) compiler with the aforementioned enhancements
|
|
67
81
|
|
|
68
82
|
### Bootstrapping
|
|
69
|
-
|
|
70
|
-
Given that the framework is distributed as [Typescript](https://typescriptlang.org) only files, there is a bootstrapping problem that needs to be mitigated. The [trv](https://github.com/travetto/travetto/tree/main/module/compiler/bin/trv.js#L60) entrypoint, along with a small context utility in [Manifest](https://github.com/travetto/travetto/tree/main/module/manifest#readme "Support for project indexing, manifesting, along with file watching") are the only [Javascript](https://developer.mozilla.org/en-US/docs/Web/JavaScript) files needed to run the project. The [trv](https://github.com/travetto/travetto/tree/main/module/compiler/bin/trv.js#L60) entry point will compile `@travetto/compiler/support/*` files as the set that is used at startup. These files are also accessible to the compiler as they get re-compiled after the fact.
|
|
83
|
+
Given that the framework is distributed as [Typescript](https://typescriptlang.org) only files, there is a bootstrapping problem that needs to be mitigated. The [trv](https://github.com/travetto/travetto/tree/main/module/compiler/bin/trv.js#L60) entrypoint, along with a small context utility in [Manifest](https://github.com/travetto/travetto/tree/main/module/manifest#readme "Support for project indexing, manifesting, along with file watching") are the only [Javascript](https://developer.mozilla.org/en-US/docs/Web/JavaScript) files needed to run the project. The [trv](https://github.com/travetto/travetto/tree/main/module/compiler/bin/trv.js#L60) entry point will compile `@travetto/compiler/support/*` files as the set that is used at startup. These files are also accessible to the compiler as they get re-compiled after the fact.
|
|
71
84
|
|
|
72
85
|
### Lock Management
|
|
73
|
-
|
|
74
86
|
The compiler supports invocation from multiple locations at the same time, and provides a layer of orchestration to ensure a single process is building at a time. For a given project, there are four main states:
|
|
75
|
-
|
|
76
|
-
|
|
77
87
|
* No Watch - Building
|
|
78
88
|
* Watch - No Build
|
|
79
89
|
* Watch - Building
|
|
80
90
|
* Inactive / Stale
|
|
81
|
-
|
|
82
91
|
Depending on what state the project is in (depending on various processes), will influence what the supporting tooling should do. [LockManager](https://github.com/travetto/travetto/tree/main/module/compiler/support/lock.ts#L25) represents the majority of the logic for tracking various states, and informing what action should happen when in the above states.
|
package/bin/trv.js
CHANGED
|
@@ -9,7 +9,7 @@ import { getManifestContext } from '@travetto/manifest/bin/context.js';
|
|
|
9
9
|
|
|
10
10
|
const VALID_OPS = { watch: 'watch', build: 'build', clean: 'clean', manifest: 'manifest' };
|
|
11
11
|
|
|
12
|
-
const COMPILER_FILES = [...['launcher', 'transpile', 'lock', 'log', 'lock-pinger'].map(x => `support/${x}.
|
|
12
|
+
const COMPILER_FILES = [...['launcher', 'transpile', 'lock', 'log', 'lock-pinger'].map(x => `support/${x}.ts`), 'package.json'];
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* @param {import('@travetto/manifest').ManifestContext} ctx
|
|
@@ -20,12 +20,12 @@ const $getLauncher = async (ctx) => {
|
|
|
20
20
|
if (!(await fs.stat(tsconfigFile).catch(() => undefined))) {
|
|
21
21
|
await fs.writeFile(tsconfigFile, JSON.stringify({ extends: '@travetto/compiler/tsconfig.trv.json' }), 'utf8');
|
|
22
22
|
}
|
|
23
|
-
const
|
|
23
|
+
const compMod = path.dirname(createRequire(path.resolve(ctx.workspacePath, 'node_modules')).resolve('@travetto/compiler/package.json'));
|
|
24
24
|
const files = [];
|
|
25
25
|
|
|
26
26
|
for (const file of COMPILER_FILES) {
|
|
27
|
-
const target = path.resolve(ctx.workspacePath, ctx.compilerFolder, 'node_modules', '@travetto/compiler', file);
|
|
28
|
-
const src =
|
|
27
|
+
const target = path.resolve(ctx.workspacePath, ctx.compilerFolder, 'node_modules', '@travetto/compiler', file).replace(/[.]tsx?$/, '.js');
|
|
28
|
+
const src = path.resolve(compMod, file);
|
|
29
29
|
|
|
30
30
|
const targetTime = await fs.stat(target).then(s => Math.max(s.mtimeMs, s.ctimeMs)).catch(() => 0);
|
|
31
31
|
const srcTime = await fs.stat(src).then(s => Math.max(s.mtimeMs, s.ctimeMs));
|
|
@@ -35,7 +35,7 @@ const $getLauncher = async (ctx) => {
|
|
|
35
35
|
const module = ctx.moduleType === 'module' ? ts.ModuleKind.ESNext : ts.ModuleKind.CommonJS;
|
|
36
36
|
await fs.mkdir(path.dirname(target), { recursive: true });
|
|
37
37
|
const text = await fs.readFile(src, 'utf8');
|
|
38
|
-
if (
|
|
38
|
+
if (/[.]tsx?$/.test(file)) {
|
|
39
39
|
const content = ts.transpile(
|
|
40
40
|
text,
|
|
41
41
|
{ target: ts.ScriptTarget.ES2020, module, esModuleInterop: true, allowSyntheticDefaultImports: true }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/compiler",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.1.0-rc.0",
|
|
4
4
|
"description": "The compiler infrastructure for the Travetto framework",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"compiler",
|
|
@@ -31,12 +31,12 @@
|
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@parcel/watcher": "^2.1.0",
|
|
34
|
-
"@travetto/manifest": "^3.0.
|
|
35
|
-
"@travetto/terminal": "^3.0.
|
|
36
|
-
"@travetto/transformer": "^3.0.
|
|
34
|
+
"@travetto/manifest": "^3.1.0-rc.0",
|
|
35
|
+
"@travetto/terminal": "^3.1.0-rc.0",
|
|
36
|
+
"@travetto/transformer": "^3.1.0-rc.0"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
|
-
"@travetto/cli": "^3.0.
|
|
39
|
+
"@travetto/cli": "^3.1.0-rc.0"
|
|
40
40
|
},
|
|
41
41
|
"peerDependenciesMeta": {
|
|
42
42
|
"@travetto/cli": {
|
package/src/compiler.ts
CHANGED
|
@@ -3,7 +3,7 @@ import ts from 'typescript';
|
|
|
3
3
|
import fs from 'fs/promises';
|
|
4
4
|
|
|
5
5
|
import { GlobalTerminal, TerminalProgressEvent } from '@travetto/terminal';
|
|
6
|
-
import { RootIndex } from '@travetto/manifest';
|
|
6
|
+
import { ManifestModuleUtil, RootIndex } from '@travetto/manifest';
|
|
7
7
|
|
|
8
8
|
import { CompilerUtil } from './util';
|
|
9
9
|
import { CompilerState } from './state';
|
|
@@ -22,7 +22,7 @@ export class Compiler {
|
|
|
22
22
|
static async main(): Promise<void> {
|
|
23
23
|
const [dirty, watch] = process.argv.slice(2);
|
|
24
24
|
const state = await CompilerState.get(RootIndex);
|
|
25
|
-
const dirtyFiles = (await fs.readFile(dirty, 'utf8')).split(/\n/).filter(x => !!x);
|
|
25
|
+
const dirtyFiles = ManifestModuleUtil.getFileType(dirty) === 'ts' ? [dirty] : (await fs.readFile(dirty, 'utf8')).split(/\n/).filter(x => !!x);
|
|
26
26
|
await new Compiler(state, dirtyFiles, watch === 'true').run();
|
|
27
27
|
process.exit(0);
|
|
28
28
|
}
|
|
@@ -39,20 +39,6 @@ export class Compiler {
|
|
|
39
39
|
this.#watch = watch;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
/**
|
|
43
|
-
* Watches local modules
|
|
44
|
-
*/
|
|
45
|
-
#watchLocalModules(emit: CompileEmitter): Promise<() => Promise<void>> {
|
|
46
|
-
return new CompilerWatcher(this.#state).watchFiles(async file => {
|
|
47
|
-
const err = await emit(file, true);
|
|
48
|
-
if (err) {
|
|
49
|
-
Log.info('Compilation Error', CompilerUtil.buildTranspileError(file, err));
|
|
50
|
-
} else {
|
|
51
|
-
Log.info(`Compiled ${file.split('node_modules/')[1]}`);
|
|
52
|
-
}
|
|
53
|
-
return err;
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
42
|
|
|
57
43
|
/**
|
|
58
44
|
* Compile in a single pass, only emitting dirty files
|
|
@@ -119,7 +105,7 @@ export class Compiler {
|
|
|
119
105
|
};
|
|
120
106
|
|
|
121
107
|
if (this.#dirtyFiles.length) {
|
|
122
|
-
await GlobalTerminal.trackProgress(this.emit(this.#dirtyFiles, emitter), resolveEmittedFile, { position: 'bottom' });
|
|
108
|
+
await GlobalTerminal.trackProgress(this.emit(this.#dirtyFiles, emitter), resolveEmittedFile, { position: 'bottom', minDelay: 50 });
|
|
123
109
|
if (failed) {
|
|
124
110
|
Log.debug('Compilation failed');
|
|
125
111
|
process.exit(1);
|
|
@@ -136,13 +122,21 @@ export class Compiler {
|
|
|
136
122
|
|
|
137
123
|
if (this.#watch) {
|
|
138
124
|
Log.info('Watch is ready');
|
|
139
|
-
await this.#
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
125
|
+
for await (const { file, action } of CompilerWatcher.watch(this.#state)) {
|
|
126
|
+
if (action !== 'delete') {
|
|
127
|
+
const err = await emitter(file, true);
|
|
128
|
+
if (err) {
|
|
129
|
+
Log.info('Compilation Error', CompilerUtil.buildTranspileError(file, err));
|
|
130
|
+
} else {
|
|
131
|
+
Log.info(`Compiled ${file.split('node_modules/')[1]}`);
|
|
132
|
+
}
|
|
133
|
+
} else {
|
|
134
|
+
Log.info(`Removed ${file.split('node_modules/')[1]}`);
|
|
144
135
|
}
|
|
145
136
|
}
|
|
137
|
+
if (!process.exitCode) {
|
|
138
|
+
process.send?.('restart');
|
|
139
|
+
}
|
|
146
140
|
}
|
|
147
141
|
}
|
|
148
142
|
}
|
package/src/state.ts
CHANGED
|
@@ -104,17 +104,18 @@ export class CompilerState implements ts.CompilerHost {
|
|
|
104
104
|
return prog;
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
-
writeInputFile(program: ts.Program, inputFile: string): ts.EmitResult | undefined {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
program.
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
107
|
+
writeInputFile(program: ts.Program, inputFile: string): ts.EmitResult | undefined | void {
|
|
108
|
+
switch (ManifestModuleUtil.getFileType(inputFile)) {
|
|
109
|
+
case 'package-json':
|
|
110
|
+
return this.writeFile(this.#inputToEntry.get(inputFile)!.output!, this.readFile(inputFile)!, false);
|
|
111
|
+
case 'js':
|
|
112
|
+
return this.writeFile(this.#inputToEntry.get(inputFile)!.output!, ts.transpile(this.readFile(inputFile)!, this.#compilerOptions), false);
|
|
113
|
+
case 'ts':
|
|
114
|
+
return program.emit(
|
|
115
|
+
program.getSourceFile(inputFile)!,
|
|
116
|
+
(...args) => this.writeFile(...args), undefined, false,
|
|
117
|
+
this.#transformerManager.get()
|
|
118
|
+
);
|
|
118
119
|
}
|
|
119
120
|
}
|
|
120
121
|
|
|
@@ -131,7 +132,7 @@ export class CompilerState implements ts.CompilerHost {
|
|
|
131
132
|
const fileType = ManifestModuleUtil.getFileType(moduleFile);
|
|
132
133
|
const outputFile = fileType === 'typings' ?
|
|
133
134
|
undefined :
|
|
134
|
-
path.resolve(this.#outputPath,
|
|
135
|
+
path.resolve(this.#outputPath, ManifestModuleUtil.sourceToOutputExt(relativeInput));
|
|
135
136
|
|
|
136
137
|
const entry = { source: sourceFile, input: inputFile, output: outputFile, module, relativeInput };
|
|
137
138
|
|
package/src/util.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import ts from 'typescript';
|
|
2
2
|
|
|
3
|
-
import { ManifestContext, ManifestModuleFileType, ManifestRoot, Package, path } from '@travetto/manifest';
|
|
3
|
+
import { ManifestContext, ManifestModuleFileType, ManifestModuleUtil, ManifestRoot, Package, path } from '@travetto/manifest';
|
|
4
4
|
|
|
5
5
|
type OutputToSource = (outputFile: string) => ({ source: string } | undefined);
|
|
6
|
-
export type FileWatchEvent = { type: 'create' | 'delete' | 'update', path: string };
|
|
7
6
|
|
|
8
7
|
const nativeCwd = process.cwd();
|
|
9
8
|
|
|
@@ -17,15 +16,6 @@ export class CompilerUtil {
|
|
|
17
16
|
*/
|
|
18
17
|
static validFile = (type: ManifestModuleFileType): boolean => type === 'ts' || type === 'package-json' || type === 'js';
|
|
19
18
|
|
|
20
|
-
/**
|
|
21
|
-
* Map input file to output format, generally converting ts extensions to js
|
|
22
|
-
* @param file
|
|
23
|
-
* @returns
|
|
24
|
-
*/
|
|
25
|
-
static inputToOutput(file: string): string {
|
|
26
|
-
return file.replace(/[.][tj]s$/, '.js');
|
|
27
|
-
}
|
|
28
|
-
|
|
29
19
|
/**
|
|
30
20
|
* Determines if write callback data has sourcemap information
|
|
31
21
|
* @param data
|
|
@@ -41,7 +31,7 @@ export class CompilerUtil {
|
|
|
41
31
|
*/
|
|
42
32
|
static rewriteSourceMap(ctx: ManifestContext, text: string, outputToSource: OutputToSource): string {
|
|
43
33
|
const data: { sourceRoot?: string, sources: string[] } = JSON.parse(text);
|
|
44
|
-
const output =
|
|
34
|
+
const output = ManifestModuleUtil.sourceToOutputExt(path.resolve(ctx.workspacePath, ctx.outputFolder, data.sources[0]));
|
|
45
35
|
const { source: file } = outputToSource(output) ?? {};
|
|
46
36
|
|
|
47
37
|
if (file) {
|
|
@@ -77,7 +67,7 @@ export class CompilerUtil {
|
|
|
77
67
|
}
|
|
78
68
|
|
|
79
69
|
/**
|
|
80
|
-
* Rewrites the package.json to target
|
|
70
|
+
* Rewrites the package.json to target output file names, and pins versions
|
|
81
71
|
* @param manifest
|
|
82
72
|
* @param file
|
|
83
73
|
* @param text
|
|
@@ -86,10 +76,10 @@ export class CompilerUtil {
|
|
|
86
76
|
static rewritePackageJSON(manifest: ManifestRoot, text: string): string {
|
|
87
77
|
const pkg: Package = JSON.parse(text);
|
|
88
78
|
if (pkg.files) {
|
|
89
|
-
pkg.files = pkg.files.map(x =>
|
|
79
|
+
pkg.files = pkg.files.map(x => ManifestModuleUtil.sourceToOutputExt(x));
|
|
90
80
|
}
|
|
91
81
|
if (pkg.main) {
|
|
92
|
-
pkg.main =
|
|
82
|
+
pkg.main = ManifestModuleUtil.sourceToOutputExt(pkg.main);
|
|
93
83
|
}
|
|
94
84
|
pkg.type = manifest.moduleType;
|
|
95
85
|
for (const key of ['devDependencies', 'dependencies', 'peerDependencies'] as const) {
|
package/src/watch.ts
CHANGED
|
@@ -1,21 +1,28 @@
|
|
|
1
1
|
import { readFileSync } from 'fs';
|
|
2
|
-
import fs from 'fs/promises';
|
|
3
2
|
|
|
4
3
|
import {
|
|
5
4
|
ManifestContext, ManifestModuleUtil, ManifestUtil, WatchEvent, ManifestModuleFolderType,
|
|
6
|
-
ManifestModuleFileType, path, ManifestModule, watchFolders,
|
|
5
|
+
ManifestModuleFileType, path, ManifestModule, watchFolders, WatchFolder, RootIndex, WatchStream
|
|
7
6
|
} from '@travetto/manifest';
|
|
8
7
|
import { getManifestContext } from '@travetto/manifest/bin/context';
|
|
9
8
|
|
|
10
9
|
import { CompilerState } from './state';
|
|
11
10
|
import { CompilerUtil } from './util';
|
|
12
|
-
import { CompileEmitter, CompileWatcherHandler } from './types';
|
|
13
11
|
|
|
14
12
|
/**
|
|
15
13
|
* Utils for watching
|
|
16
14
|
*/
|
|
17
15
|
export class CompilerWatcher {
|
|
18
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Watch state
|
|
19
|
+
* @param state
|
|
20
|
+
* @returns
|
|
21
|
+
*/
|
|
22
|
+
static watch(state: CompilerState): AsyncIterable<WatchEvent> {
|
|
23
|
+
return new CompilerWatcher(state).watchChanges();
|
|
24
|
+
}
|
|
25
|
+
|
|
19
26
|
#sourceHashes = new Map<string, number>();
|
|
20
27
|
#manifestContexts = new Map<string, ManifestContext>();
|
|
21
28
|
#dirtyFiles: { modFolder: string, mod: string, moduleFile?: string, folderKey?: ManifestModuleFolderType, type?: ManifestModuleFileType }[] = [];
|
|
@@ -62,15 +69,21 @@ export class CompilerWatcher {
|
|
|
62
69
|
}
|
|
63
70
|
|
|
64
71
|
/**
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
72
|
+
* Get a watcher for a given compiler state
|
|
73
|
+
* @param state
|
|
74
|
+
* @param handler
|
|
75
|
+
* @returns
|
|
76
|
+
*/
|
|
77
|
+
async * watchChanges(): AsyncIterable<WatchEvent> {
|
|
78
|
+
const stream = this.#watchFiles();
|
|
79
|
+
|
|
71
80
|
const mods = this.#getModuleMap();
|
|
81
|
+
for await (const { file: sourceFile, action, folder } of stream) {
|
|
82
|
+
|
|
83
|
+
if (folder === '.trv_internal') {
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
72
86
|
|
|
73
|
-
return async ({ file: sourceFile, action }: WatchEvent, folder: string): Promise<void> => {
|
|
74
87
|
const mod = mods[folder];
|
|
75
88
|
const moduleFile = mod.sourceFolder ?
|
|
76
89
|
(sourceFile.includes(mod.sourceFolder) ? sourceFile.split(`${mod.sourceFolder}/`)[1] : sourceFile) :
|
|
@@ -91,7 +104,7 @@ export class CompilerWatcher {
|
|
|
91
104
|
const hash = CompilerUtil.naiveHash(readFileSync(sourceFile, 'utf8'));
|
|
92
105
|
const input = this.#state.registerInput(mod, moduleFile);
|
|
93
106
|
this.#sourceHashes.set(sourceFile, hash);
|
|
94
|
-
|
|
107
|
+
yield { action, file: input, folder };
|
|
95
108
|
}
|
|
96
109
|
break;
|
|
97
110
|
}
|
|
@@ -103,7 +116,7 @@ export class CompilerWatcher {
|
|
|
103
116
|
if (this.#sourceHashes.get(sourceFile) !== hash) {
|
|
104
117
|
this.#state.resetInputSource(entry.input);
|
|
105
118
|
this.#sourceHashes.set(sourceFile, hash);
|
|
106
|
-
|
|
119
|
+
yield { action, file: entry.input, folder };
|
|
107
120
|
}
|
|
108
121
|
}
|
|
109
122
|
break;
|
|
@@ -114,46 +127,58 @@ export class CompilerWatcher {
|
|
|
114
127
|
this.#state.removeInput(entry.input);
|
|
115
128
|
if (entry.output) {
|
|
116
129
|
this.#dirtyFiles.push({ mod: mod.name, modFolder: folder });
|
|
117
|
-
|
|
130
|
+
yield { action, file: entry.output, folder };
|
|
118
131
|
}
|
|
119
132
|
}
|
|
120
133
|
}
|
|
121
134
|
}
|
|
122
|
-
}
|
|
135
|
+
}
|
|
123
136
|
}
|
|
124
137
|
|
|
125
138
|
/**
|
|
126
139
|
* Watch files based on root index
|
|
127
140
|
*/
|
|
128
|
-
|
|
129
|
-
let watchRoot: (() => Promise<void>) | undefined = undefined;
|
|
130
|
-
|
|
141
|
+
#watchFiles(): WatchStream {
|
|
131
142
|
const idx = this.#state.manifestIndex;
|
|
132
143
|
const modules = [...idx.getModuleList('all')].map(x => idx.getModule(x)!);
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
144
|
+
const options: Partial<WatchFolder> = {
|
|
145
|
+
filter: (ev: WatchEvent): boolean => {
|
|
146
|
+
const type = ManifestModuleUtil.getFileType(ev.file);
|
|
147
|
+
return type === 'ts' || type === 'typings' || type === 'js' || type === 'package-json';
|
|
148
|
+
},
|
|
149
|
+
ignore: ['node_modules', '**/.trv_*'],
|
|
138
150
|
};
|
|
139
151
|
|
|
140
|
-
const moduleFolders = modules
|
|
152
|
+
const moduleFolders: WatchFolder[] = modules
|
|
141
153
|
.filter(x => !idx.manifest.monoRepo || x.sourcePath !== idx.manifest.workspacePath)
|
|
142
|
-
.map(x =>
|
|
154
|
+
.map(x => ({ src: x.sourcePath, target: x.sourcePath }));
|
|
143
155
|
|
|
144
156
|
// Add monorepo folders
|
|
145
157
|
if (idx.manifest.monoRepo) {
|
|
146
158
|
const mono = modules.find(x => x.sourcePath === idx.manifest.workspacePath)!;
|
|
147
159
|
for (const folder of Object.keys(mono.files)) {
|
|
148
160
|
if (!folder.startsWith('$')) {
|
|
149
|
-
moduleFolders.push(
|
|
161
|
+
moduleFolders.push({ src: path.resolve(mono.sourcePath, folder), target: mono.sourcePath });
|
|
150
162
|
}
|
|
151
163
|
}
|
|
152
|
-
|
|
164
|
+
moduleFolders.push({ src: mono.sourcePath, target: mono.sourcePath, immediate: true });
|
|
153
165
|
}
|
|
154
166
|
|
|
155
|
-
|
|
167
|
+
// Watch output folders
|
|
168
|
+
const outputWatch = (root: string, sources: string[]): WatchFolder => {
|
|
169
|
+
const valid = new Set(sources.map(src => path.resolve(root, src)));
|
|
170
|
+
return {
|
|
171
|
+
src: root, target: '.trv_internal', immediate: true, includeHidden: true,
|
|
172
|
+
filter: ev => ev.action === 'delete' && valid.has(path.resolve(root, ev.file))
|
|
173
|
+
};
|
|
174
|
+
};
|
|
175
|
+
moduleFolders.push(
|
|
176
|
+
outputWatch(RootIndex.manifest.workspacePath, [
|
|
177
|
+
RootIndex.manifest.outputFolder,
|
|
178
|
+
RootIndex.manifest.compilerFolder
|
|
179
|
+
])
|
|
180
|
+
);
|
|
156
181
|
|
|
157
|
-
return (
|
|
182
|
+
return watchFolders(moduleFolders, options);
|
|
158
183
|
}
|
|
159
184
|
}
|
package/support/transpile.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
2
|
import fs from 'fs/promises';
|
|
3
3
|
import os from 'os';
|
|
4
|
+
import timers from 'timers/promises';
|
|
4
5
|
import cp from 'child_process';
|
|
5
6
|
import { createRequire } from 'module';
|
|
6
7
|
|
|
@@ -19,6 +20,27 @@ const RECENT_STAT = (stat: { ctimeMs: number, mtimeMs: number }): number => Math
|
|
|
19
20
|
* Transpile utilities for launching
|
|
20
21
|
*/
|
|
21
22
|
export class TranspileUtil {
|
|
23
|
+
/**
|
|
24
|
+
* Determine file type
|
|
25
|
+
*/
|
|
26
|
+
static getFileType(file: string): 'ts' | 'js' | 'package-json' | 'typings' | undefined {
|
|
27
|
+
return file.endsWith('package.json') ? 'package-json' :
|
|
28
|
+
(file.endsWith('.js') ? 'js' :
|
|
29
|
+
(file.endsWith('.d.ts') ? 'typings' : (/[.]tsx?$/.test(file) ? 'ts' : undefined)));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Convert a file to a given ext */
|
|
33
|
+
static #sourceToExtension(inputFile: string, ext: string): string {
|
|
34
|
+
return inputFile.replace(/[.][tj]sx?$/, ext);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Get the output file name for a given input
|
|
39
|
+
*/
|
|
40
|
+
static sourceToOutputExt(inputFile: string): string {
|
|
41
|
+
return this.#sourceToExtension(inputFile, '.js');
|
|
42
|
+
}
|
|
43
|
+
|
|
22
44
|
/**
|
|
23
45
|
* Write text file, and ensure folder exists
|
|
24
46
|
*/
|
|
@@ -45,8 +67,6 @@ export class TranspileUtil {
|
|
|
45
67
|
OPT_CACHE[ctx.workspacePath] = {
|
|
46
68
|
...options,
|
|
47
69
|
allowJs: true,
|
|
48
|
-
sourceMap: false,
|
|
49
|
-
inlineSourceMap: true,
|
|
50
70
|
resolveJsonModule: true,
|
|
51
71
|
sourceRoot: ctx.workspacePath,
|
|
52
72
|
rootDir: ctx.workspacePath,
|
|
@@ -61,7 +81,8 @@ export class TranspileUtil {
|
|
|
61
81
|
* Output a file, support for ts, js, and package.json
|
|
62
82
|
*/
|
|
63
83
|
static async transpileFile(ctx: ManifestContext, inputFile: string, outputFile: string): Promise<void> {
|
|
64
|
-
|
|
84
|
+
const type = this.getFileType(inputFile);
|
|
85
|
+
if (type === 'js' || type === 'ts') {
|
|
65
86
|
const compilerOut = path.resolve(ctx.workspacePath, ctx.compilerFolder, 'node_modules');
|
|
66
87
|
|
|
67
88
|
const text = (await fs.readFile(inputFile, 'utf8'))
|
|
@@ -69,12 +90,16 @@ export class TranspileUtil {
|
|
|
69
90
|
.replace(/from '(@travetto\/(.*?))'/g, (_, i, s) => `from '${path.resolve(compilerOut, `${i}${s.includes('/') ? '.js' : '/__index__.js'}`)}'`);
|
|
70
91
|
|
|
71
92
|
const ts = (await import('typescript')).default;
|
|
72
|
-
const content = ts.transpile(text,
|
|
93
|
+
const content = ts.transpile(text, {
|
|
94
|
+
...await this.getCompilerOptions(ctx),
|
|
95
|
+
sourceMap: false,
|
|
96
|
+
inlineSourceMap: true,
|
|
97
|
+
}, inputFile);
|
|
73
98
|
await this.writeTextFile(outputFile, content);
|
|
74
|
-
} else if (
|
|
99
|
+
} else if (type === 'package-json') {
|
|
75
100
|
const pkg: Package = JSON.parse(await fs.readFile(inputFile, 'utf8'));
|
|
76
|
-
const main = pkg.main
|
|
77
|
-
const files = pkg.files?.map(x =>
|
|
101
|
+
const main = pkg.main ? this.sourceToOutputExt(pkg.main) : undefined;
|
|
102
|
+
const files = pkg.files?.map(x => this.sourceToOutputExt(x));
|
|
78
103
|
|
|
79
104
|
const content = JSON.stringify({ ...pkg, main, type: ctx.moduleType, files }, null, 2);
|
|
80
105
|
await this.writeTextFile(outputFile, content);
|
|
@@ -107,10 +132,12 @@ export class TranspileUtil {
|
|
|
107
132
|
|
|
108
133
|
if (stat.isDirectory()) {
|
|
109
134
|
folders.push(resolvedInput);
|
|
110
|
-
} else
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
135
|
+
} else {
|
|
136
|
+
switch (this.getFileType(file)) {
|
|
137
|
+
case 'js':
|
|
138
|
+
case 'ts':
|
|
139
|
+
files.push(resolvedInput);
|
|
140
|
+
}
|
|
114
141
|
}
|
|
115
142
|
}
|
|
116
143
|
}
|
|
@@ -118,7 +145,7 @@ export class TranspileUtil {
|
|
|
118
145
|
const outputFolder = path.resolve(ctx.workspacePath, ctx.compilerFolder, 'node_modules', module);
|
|
119
146
|
const out: ModFile[] = [];
|
|
120
147
|
for (const input of files) {
|
|
121
|
-
const output = input.replace(inputFolder, outputFolder)
|
|
148
|
+
const output = this.sourceToOutputExt(input.replace(inputFolder, outputFolder));
|
|
122
149
|
const inputTs = await fs.stat(input).then(RECENT_STAT, () => 0);
|
|
123
150
|
if (inputTs) {
|
|
124
151
|
const outputTs = await fs.stat(output).then(RECENT_STAT, () => 0);
|
|
@@ -178,7 +205,7 @@ export class TranspileUtil {
|
|
|
178
205
|
try {
|
|
179
206
|
await this.writeTextFile(deltaFile, changedFiles.join('\n'));
|
|
180
207
|
|
|
181
|
-
|
|
208
|
+
const result = await LogUtil.withLogger('compiler-exec', log => new Promise<CompileResult>((res, rej) => {
|
|
182
209
|
proc = cp.spawn(process.argv0, [main, deltaFile, `${watch}`], {
|
|
183
210
|
env: {
|
|
184
211
|
...process.env,
|
|
@@ -199,10 +226,19 @@ export class TranspileUtil {
|
|
|
199
226
|
kill = (): void => { proc?.kill('SIGKILL'); };
|
|
200
227
|
process.on('exit', kill);
|
|
201
228
|
}));
|
|
229
|
+
|
|
230
|
+
if (result === 'restart') {
|
|
231
|
+
await timers.setTimeout(150 + 100 * Math.random());
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return result;
|
|
202
235
|
} finally {
|
|
203
236
|
if (proc?.killed === false) { proc.kill('SIGKILL'); }
|
|
204
237
|
if (kill) {
|
|
205
|
-
process.
|
|
238
|
+
process.off('exit', kill);
|
|
239
|
+
}
|
|
240
|
+
if (process.stdout.isTTY) {
|
|
241
|
+
process.stdout.write('\x1b[s\x1b[?25h\x1b[r\x1b[u');
|
|
206
242
|
}
|
|
207
243
|
await fs.rm(deltaFile, { force: true });
|
|
208
244
|
}
|