@travetto/compiler 3.3.1 → 3.4.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 +42 -44
- package/bin/{trv.js → common.js} +22 -27
- package/bin/trvc.js +62 -0
- package/package.json +7 -6
- package/src/compiler.ts +40 -24
- package/src/event.ts +7 -0
- package/src/internal/watch-core.ts +104 -0
- package/src/log.ts +8 -9
- package/src/state.ts +4 -4
- package/src/types.ts +0 -6
- package/src/watch.ts +58 -97
- package/support/entry.trvc.ts +39 -0
- package/support/log.ts +40 -29
- package/support/queue.ts +41 -0
- package/support/server/client.ts +118 -0
- package/support/server/runner.ts +104 -0
- package/support/server/server.ts +189 -0
- package/support/setup.ts +229 -0
- package/support/types.ts +28 -0
- package/support/util.ts +108 -0
- package/tsconfig.trv.json +7 -0
- package/support/launcher.ts +0 -160
- package/support/lock-pinger.ts +0 -25
- package/support/lock.ts +0 -234
- package/support/transpile.ts +0 -256
- /package/support/{compiler-entry.ts → entry.compiler.ts} +0 -0
package/README.md
CHANGED
|
@@ -21,56 +21,62 @@ This module expands upon the [Typescript](https://typescriptlang.org) compiler,
|
|
|
21
21
|
Beyond the [Typescript](https://typescriptlang.org) compiler functionality, the module provides the primary entry point into the development process.
|
|
22
22
|
|
|
23
23
|
## CLI
|
|
24
|
-
The cli, [
|
|
24
|
+
The compiler cli, [trvc](https://github.com/travetto/travetto/tree/main/module/compiler/bin/trvc.js#L7) 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.
|
|
25
25
|
|
|
26
|
-
The
|
|
27
|
-
* `
|
|
28
|
-
* `
|
|
29
|
-
* `
|
|
30
|
-
* `
|
|
31
|
-
*
|
|
26
|
+
The compiler cli supports the following operations:
|
|
27
|
+
* `start|watch` - Run the compiler in watch mode
|
|
28
|
+
* `stop` - Stop the compiler if running
|
|
29
|
+
* `restart` - Restart the compiler in watch mode
|
|
30
|
+
* `build` - Ensure the project is built and upto date
|
|
31
|
+
* `clean` - Clean out the output and compiler caches
|
|
32
|
+
* `info` - Retrieve the compiler information, if running
|
|
33
|
+
* `manifest` - Generate the project manifest
|
|
32
34
|
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`.
|
|
33
35
|
|
|
34
36
|
**Terminal: Sample trv output with debug logging**
|
|
35
37
|
```bash
|
|
36
|
-
$ TRV_BUILD=debug
|
|
38
|
+
$ TRV_BUILD=debug trvc build
|
|
37
39
|
|
|
38
|
-
2029-03-14T04:00:00.618Z [
|
|
39
|
-
2029-03-14T04:00:00.837Z [
|
|
40
|
-
2029-03-14T04:00:01.510Z [
|
|
41
|
-
2029-03-14T04:00:02.450Z [precompile ]
|
|
42
|
-
2029-03-14T04:00:02.762Z [
|
|
43
|
-
2029-03-14T04:00:02.947Z [precompile ] @travetto/
|
|
44
|
-
2029-03-14T04:00:03.093Z [precompile ]
|
|
45
|
-
2029-03-14T04:00:04.003Z [
|
|
46
|
-
2029-03-14T04:00:04.495Z [
|
|
47
|
-
2029-03-14T04:00:05.066Z [
|
|
48
|
-
2029-03-14T04:00:05.307Z [
|
|
49
|
-
2029-03-14T04:00:05.952Z [
|
|
50
|
-
2029-03-14T04:00:06.859Z [transformers ]
|
|
51
|
-
2029-03-14T04:00:07.720Z [transformers ] @travetto/
|
|
52
|
-
2029-03-14T04:00:08.179Z [transformers ] @travetto/
|
|
53
|
-
2029-03-14T04:00:08.588Z [transformers ]
|
|
54
|
-
2029-03-14T04:00:09.493Z [
|
|
55
|
-
2029-03-14T04:00:10.395Z [
|
|
56
|
-
2029-03-14T04:00:10.407Z [
|
|
57
|
-
2029-03-14T04:00:10.799Z [
|
|
58
|
-
2029-03-14T04:00:11.013Z [
|
|
59
|
-
2029-03-14T04:00:11.827Z [
|
|
60
|
-
2029-03-14T04:00:11.894Z [
|
|
61
|
-
2029-03-14T04:00:12.133Z [
|
|
62
|
-
2029-03-14T04:00:13.123Z [
|
|
40
|
+
2029-03-14T04:00:00.618Z info [compiler-server] Starting server http://127.0.0.1:29222
|
|
41
|
+
2029-03-14T04:00:00.837Z debug [compiler-client] Starting watch for events of type "log"
|
|
42
|
+
2029-03-14T04:00:01.510Z debug [event-stream ] Started event stream
|
|
43
|
+
2029-03-14T04:00:02.450Z debug [precompile ] Started
|
|
44
|
+
2029-03-14T04:00:02.762Z debug [compiler-server] Receive request { action: 'event', subAction: 'log' }
|
|
45
|
+
2029-03-14T04:00:02.947Z debug [precompile ] Skipped @travetto/terminal
|
|
46
|
+
2029-03-14T04:00:03.093Z debug [precompile ] Skipped @travetto/manifest
|
|
47
|
+
2029-03-14T04:00:04.003Z debug [precompile ] Skipped @travetto/transformer
|
|
48
|
+
2029-03-14T04:00:04.495Z debug [precompile ] Skipped @travetto/compiler
|
|
49
|
+
2029-03-14T04:00:05.066Z debug [precompile ] Completed
|
|
50
|
+
2029-03-14T04:00:05.307Z debug [manifest ] Started
|
|
51
|
+
2029-03-14T04:00:05.952Z debug [manifest ] Completed
|
|
52
|
+
2029-03-14T04:00:06.859Z debug [transformers ] Started
|
|
53
|
+
2029-03-14T04:00:07.720Z debug [transformers ] Skipped @travetto/base
|
|
54
|
+
2029-03-14T04:00:08.179Z debug [transformers ] Skipped @travetto/cli
|
|
55
|
+
2029-03-14T04:00:08.588Z debug [transformers ] Skipped @travetto/manifest
|
|
56
|
+
2029-03-14T04:00:09.493Z debug [transformers ] Skipped @travetto/registry
|
|
57
|
+
2029-03-14T04:00:10.395Z debug [transformers ] Skipped @travetto/schema
|
|
58
|
+
2029-03-14T04:00:10.407Z debug [transformers ] Completed
|
|
59
|
+
2029-03-14T04:00:10.799Z debug [delta ] Started
|
|
60
|
+
2029-03-14T04:00:11.013Z debug [delta ] Completed
|
|
61
|
+
2029-03-14T04:00:11.827Z debug [manifest ] Started
|
|
62
|
+
2029-03-14T04:00:11.894Z debug [manifest ] Wrote manifest @travetto-doc/compiler
|
|
63
|
+
2029-03-14T04:00:12.133Z debug [manifest ] Completed
|
|
64
|
+
2029-03-14T04:00:13.123Z info [compiler-server] State changed: compile-end
|
|
65
|
+
2029-03-14T04:00:14.014Z debug [compiler-exec ] Skipped
|
|
66
|
+
2029-03-14T04:00:14.924Z debug [event-stream ] Finished event stream
|
|
67
|
+
2029-03-14T04:00:15.690Z info [compiler-server] Closing down server
|
|
68
|
+
2029-03-14T04:00:15.865Z debug [compiler-client] Stopping watch for events of type "log"
|
|
63
69
|
```
|
|
64
70
|
|
|
65
71
|
**Terminal: Sample trv output with default log level**
|
|
66
72
|
```bash
|
|
67
|
-
$
|
|
73
|
+
$ trvc build
|
|
68
74
|
```
|
|
69
75
|
|
|
70
76
|
## Compilation Architecture
|
|
71
77
|
The compiler will move through the following phases on a given compilation execution:
|
|
72
78
|
* `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
|
|
73
|
-
* `
|
|
79
|
+
* `Compiler Server` - Provides a simple HTTP interface to watching compiler file and state changes, and synchronizing multiple processes
|
|
74
80
|
* `Build Compiler` - Leverages [Typescript](https://typescriptlang.org) to build files needed to execute compiler
|
|
75
81
|
* `Build Manifest` - Produces the manifest for the given execution
|
|
76
82
|
* `Build Transformers` - Leverages [Typescript](https://typescriptlang.org) to compile all transformers defined in the manifest
|
|
@@ -80,12 +86,4 @@ The compiler will move through the following phases on a given compilation execu
|
|
|
80
86
|
* `Invoke Compiler` - Run [Typescript](https://typescriptlang.org) compiler with the aforementioned enhancements
|
|
81
87
|
|
|
82
88
|
### Bootstrapping
|
|
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 [
|
|
84
|
-
|
|
85
|
-
### Lock Management
|
|
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:
|
|
87
|
-
* No Watch - Building
|
|
88
|
-
* Watch - No Build
|
|
89
|
-
* Watch - Building
|
|
90
|
-
* Inactive / Stale
|
|
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.
|
|
89
|
+
Given that the framework is distributed as [Typescript](https://typescriptlang.org) only files, there is a bootstrapping problem that needs to be mitigated. The [trvc](https://github.com/travetto/travetto/tree/main/module/compiler/bin/trvc.js#L7) 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 [trvc](https://github.com/travetto/travetto/tree/main/module/compiler/bin/trvc.js#L7) 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.
|
package/bin/{trv.js → common.js}
RENAMED
|
@@ -1,21 +1,18 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
1
|
// @ts-check
|
|
2
|
+
|
|
4
3
|
import fs from 'fs/promises';
|
|
5
4
|
import path from 'path';
|
|
6
5
|
import { createRequire } from 'module';
|
|
7
6
|
|
|
8
7
|
import { getManifestContext } from '@travetto/manifest/bin/context.js';
|
|
9
8
|
|
|
10
|
-
const
|
|
9
|
+
const COMPILER_FILES = [...['entry.trvc', 'log', 'queue', 'server/client', 'server/runner', 'server/server', 'setup', 'util'].map(x => `support/${x}.ts`), 'package.json'];
|
|
11
10
|
|
|
12
|
-
|
|
11
|
+
/** @typedef {import('@travetto/manifest/src/types').ManifestContext} Ctx */
|
|
12
|
+
/** @typedef {import('@travetto/compiler/support/types').EntryOp} EntryOp */
|
|
13
13
|
|
|
14
|
-
/**
|
|
15
|
-
|
|
16
|
-
* @return {Promise<import('@travetto/compiler/support/launcher').launch>}
|
|
17
|
-
*/
|
|
18
|
-
const $getLauncher = async (ctx) => {
|
|
14
|
+
/** @return {Promise<import('@travetto/compiler/support/entry.trvc').main>} */
|
|
15
|
+
const $getEntry = async (/** @type {Ctx} */ ctx) => {
|
|
19
16
|
const tsconfigFile = path.resolve(ctx.workspacePath, 'tsconfig.json');
|
|
20
17
|
if (!(await fs.stat(tsconfigFile).catch(() => undefined))) {
|
|
21
18
|
await fs.writeFile(tsconfigFile, JSON.stringify({ extends: '@travetto/compiler/tsconfig.trv.json' }), 'utf8');
|
|
@@ -38,7 +35,7 @@ const $getLauncher = async (ctx) => {
|
|
|
38
35
|
if (/[.]tsx?$/.test(file)) {
|
|
39
36
|
const content = ts.transpile(
|
|
40
37
|
text,
|
|
41
|
-
{ target: ts.ScriptTarget.
|
|
38
|
+
{ target: ts.ScriptTarget.ES2022, module, esModuleInterop: true, allowSyntheticDefaultImports: true }
|
|
42
39
|
)
|
|
43
40
|
.replace(/^((?:im|ex)port .*from '[.][^']+)(')/mg, (_, a, b) => `${a}.js${b}`)
|
|
44
41
|
.replace(/^(import [^\n]*from '[^.][^\n/]+[/][^\n/]+[/][^\n']+)(')/mg, (_, a, b) => `${a}.js${b}`);
|
|
@@ -53,23 +50,21 @@ const $getLauncher = async (ctx) => {
|
|
|
53
50
|
files.push(target);
|
|
54
51
|
}
|
|
55
52
|
|
|
56
|
-
try { return require(files[0]).
|
|
57
|
-
catch { return import(files[0]).then(x => x.
|
|
53
|
+
try { return require(files[0]).main; }
|
|
54
|
+
catch { return import(files[0]).then(x => x.main); }
|
|
58
55
|
};
|
|
59
56
|
|
|
60
|
-
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
if (op === 'clean') {
|
|
65
|
-
const folders = process.argv.find(x => x === '--all' || x === '-a') ? [ctx.outputFolder, ctx.compilerFolder] : [ctx.outputFolder];
|
|
66
|
-
for (const f of folders) {
|
|
67
|
-
await fs.rm(path.resolve(ctx.workspacePath, f), { force: true, recursive: true });
|
|
68
|
-
}
|
|
69
|
-
return console.log(`Cleaned ${ctx.workspacePath}: [${folders.join(', ')}]`);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const rootCtx = ctx.monoRepo ? await getManifestContext(ctx.workspacePath) : ctx;
|
|
57
|
+
async function $compile(/** @type {Ctx} ctx*/ ctx, /** @type {EntryOp} op */ op) {
|
|
58
|
+
const rootCtx = await (ctx.monoRepo ? getManifestContext(ctx.workspacePath) : ctx);
|
|
59
|
+
return (await $getEntry(ctx))(ctx, rootCtx, op, process.argv.slice(3));
|
|
60
|
+
}
|
|
73
61
|
|
|
74
|
-
|
|
75
|
-
|
|
62
|
+
/**
|
|
63
|
+
* @template T
|
|
64
|
+
* @param {(ctx: Ctx, compile: typeof $compile) => Promise<T>} fn
|
|
65
|
+
* @returns {Promise<T>}
|
|
66
|
+
*/
|
|
67
|
+
export async function withContext(fn) {
|
|
68
|
+
const ctx = await getManifestContext();
|
|
69
|
+
return fn(ctx, $compile);
|
|
70
|
+
}
|
package/bin/trvc.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// @ts-check
|
|
4
|
+
import fs from 'fs/promises';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
|
|
7
|
+
import { withContext } from './common.js';
|
|
8
|
+
|
|
9
|
+
/** @typedef {import('@travetto/manifest/src/types').ManifestContext} Ctx */
|
|
10
|
+
|
|
11
|
+
const stop = async (/** @typedef {Ctx} */ ctx) => {
|
|
12
|
+
if (await fetch(`${ctx.compilerUrl}/stop`).then(v => v.ok, () => false)) {
|
|
13
|
+
console.log(`Stopped server ${ctx.workspacePath}: [${ctx.compilerUrl}]`);
|
|
14
|
+
} else {
|
|
15
|
+
console.log(`Server not running ${ctx.workspacePath}: [${ctx.compilerUrl}]`);
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const info = async (/** @typedef {Ctx} */ctx) => console.log(
|
|
20
|
+
JSON.stringify(await fetch(ctx.compilerUrl).then(v => v.json(), () => undefined) ?? { state: 'Server not running' }, null, 2)
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
const clean = async (/** @typedef {Ctx} */ctx) => {
|
|
24
|
+
const folders = [ctx.outputFolder, ctx.compilerFolder];
|
|
25
|
+
if (await fetch(`${ctx.compilerUrl}/clean`).then(v => v.ok, () => false)) {
|
|
26
|
+
return console.log(`Clean triggered ${ctx.workspacePath}:`, folders);
|
|
27
|
+
} else {
|
|
28
|
+
await Promise.all(folders.map(f => fs.rm(path.resolve(ctx.workspacePath, f), { force: true, recursive: true })));
|
|
29
|
+
return console.log(`Cleaned ${ctx.workspacePath}:`, folders);
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const help = () => [
|
|
34
|
+
'npx trvc [command]',
|
|
35
|
+
'',
|
|
36
|
+
'Available Commands:',
|
|
37
|
+
' * start|watch - Run the compiler in watch mode',
|
|
38
|
+
' * stop - Stop the compiler if running',
|
|
39
|
+
' * restart - Restart the compiler in watch mode',
|
|
40
|
+
' * build - Ensure the project is built and upto date',
|
|
41
|
+
' * clean - Clean out the output and compiler caches',
|
|
42
|
+
' * info - Retrieve the compiler information, if running',
|
|
43
|
+
' * manifest - Generate the project manifest',
|
|
44
|
+
].join('\n');
|
|
45
|
+
|
|
46
|
+
withContext(async (ctx, compile) => {
|
|
47
|
+
const op = process.argv[2];
|
|
48
|
+
|
|
49
|
+
switch (op) {
|
|
50
|
+
case 'restart': return stop(ctx).then(() => compile(ctx, 'watch'));
|
|
51
|
+
case 'stop': return stop(ctx);
|
|
52
|
+
case 'info': return info(ctx);
|
|
53
|
+
case 'clean': return clean(ctx);
|
|
54
|
+
case 'watch':
|
|
55
|
+
case 'start': return compile(ctx, 'watch');
|
|
56
|
+
case 'build':
|
|
57
|
+
case 'manifest': return compile(ctx, op);
|
|
58
|
+
case undefined:
|
|
59
|
+
case 'help': return console.log(`\n${help()}\n`);
|
|
60
|
+
default: console.error(`Unknown trvc operation: ${op}\n`); return console.error(help());
|
|
61
|
+
}
|
|
62
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/compiler",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.4.0-rc.0",
|
|
4
4
|
"description": "The compiler infrastructure for the Travetto framework",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"compiler",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
],
|
|
24
24
|
"main": "__index__.ts",
|
|
25
25
|
"bin": {
|
|
26
|
-
"
|
|
26
|
+
"trvc": "bin/trvc.js"
|
|
27
27
|
},
|
|
28
28
|
"repository": {
|
|
29
29
|
"url": "https://github.com/travetto/travetto.git",
|
|
@@ -31,12 +31,13 @@
|
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@parcel/watcher": "^2.3.0",
|
|
34
|
-
"@travetto/manifest": "^3.
|
|
35
|
-
"@travetto/terminal": "^3.
|
|
36
|
-
"@travetto/transformer": "^3.
|
|
34
|
+
"@travetto/manifest": "^3.4.0-rc.0",
|
|
35
|
+
"@travetto/terminal": "^3.4.0-rc.0",
|
|
36
|
+
"@travetto/transformer": "^3.4.0-rc.0",
|
|
37
|
+
"@types/node": "^20.8.9"
|
|
37
38
|
},
|
|
38
39
|
"peerDependencies": {
|
|
39
|
-
"@travetto/cli": "^3.
|
|
40
|
+
"@travetto/cli": "^3.4.0-rc.0"
|
|
40
41
|
},
|
|
41
42
|
"peerDependenciesMeta": {
|
|
42
43
|
"@travetto/cli": {
|
package/src/compiler.ts
CHANGED
|
@@ -2,7 +2,6 @@ import { install } from 'source-map-support';
|
|
|
2
2
|
import ts from 'typescript';
|
|
3
3
|
import fs from 'fs/promises';
|
|
4
4
|
|
|
5
|
-
import { GlobalTerminal, TerminalProgressEvent } from '@travetto/terminal';
|
|
6
5
|
import { ManifestModuleUtil, RootIndex } from '@travetto/manifest';
|
|
7
6
|
|
|
8
7
|
import { CompilerUtil } from './util';
|
|
@@ -10,6 +9,7 @@ import { CompilerState } from './state';
|
|
|
10
9
|
import { CompilerWatcher } from './watch';
|
|
11
10
|
import { Log } from './log';
|
|
12
11
|
import { CompileEmitError, CompileEmitEvent, CompileEmitter } from './types';
|
|
12
|
+
import { EventUtil } from './event';
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Compilation support
|
|
@@ -39,7 +39,6 @@ export class Compiler {
|
|
|
39
39
|
this.#watch = watch;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
|
|
43
42
|
/**
|
|
44
43
|
* Compile in a single pass, only emitting dirty files
|
|
45
44
|
*/
|
|
@@ -79,9 +78,10 @@ export class Compiler {
|
|
|
79
78
|
yield { file: imp, i: i += 1, err, total: files.length };
|
|
80
79
|
if ((Date.now() - lastSent) > 50) { // Limit to 1 every 50ms
|
|
81
80
|
lastSent = Date.now();
|
|
82
|
-
|
|
81
|
+
EventUtil.sendEvent('progress', { total: files.length, idx: i, message: imp, operation: 'compile' });
|
|
83
82
|
}
|
|
84
83
|
}
|
|
84
|
+
EventUtil.sendEvent('progress', { total: files.length, idx: files.length, message: 'Complete', operation: 'compile', complete: true });
|
|
85
85
|
Log.debug(`Compiled ${i} files`);
|
|
86
86
|
}
|
|
87
87
|
|
|
@@ -89,11 +89,12 @@ export class Compiler {
|
|
|
89
89
|
* Run the compiler
|
|
90
90
|
*/
|
|
91
91
|
async run(): Promise<void> {
|
|
92
|
-
await GlobalTerminal.init();
|
|
93
92
|
install();
|
|
94
93
|
|
|
95
94
|
Log.debug('Compilation started');
|
|
96
95
|
|
|
96
|
+
EventUtil.sendEvent('state', { state: 'init', extra: { pid: process.pid } });
|
|
97
|
+
|
|
97
98
|
if (process.send) {
|
|
98
99
|
process.on('disconnect', () => process.exit(0));
|
|
99
100
|
}
|
|
@@ -103,18 +104,16 @@ export class Compiler {
|
|
|
103
104
|
|
|
104
105
|
Log.debug('Compiler loaded');
|
|
105
106
|
|
|
106
|
-
|
|
107
|
-
if (err) {
|
|
108
|
-
failed = true;
|
|
109
|
-
console.error(CompilerUtil.buildTranspileError(file, err));
|
|
110
|
-
}
|
|
111
|
-
return { idx: i, total, text: `Compiling [%idx/%total] -- ${file}` };
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
process.send?.({ type: 'start' });
|
|
107
|
+
EventUtil.sendEvent('state', { state: 'compile-start' });
|
|
115
108
|
|
|
116
109
|
if (this.#dirtyFiles.length) {
|
|
117
|
-
await
|
|
110
|
+
for await (const ev of this.emit(this.#dirtyFiles, emitter)) {
|
|
111
|
+
if (ev.err) {
|
|
112
|
+
failed = true;
|
|
113
|
+
const compileError = CompilerUtil.buildTranspileError(ev.file, ev.err);
|
|
114
|
+
EventUtil.sendEvent('log', { level: 'error', message: compileError.toString(), time: Date.now() });
|
|
115
|
+
}
|
|
116
|
+
}
|
|
118
117
|
if (failed) {
|
|
119
118
|
Log.debug('Compilation failed');
|
|
120
119
|
process.exit(1);
|
|
@@ -127,28 +126,45 @@ export class Compiler {
|
|
|
127
126
|
await emitter(resolved, true);
|
|
128
127
|
}
|
|
129
128
|
|
|
130
|
-
|
|
129
|
+
EventUtil.sendEvent('state', { state: 'compile-end' });
|
|
131
130
|
|
|
132
131
|
if (this.#watch) {
|
|
133
132
|
Log.info('Watch is ready');
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
133
|
+
|
|
134
|
+
EventUtil.sendEvent('state', { state: 'watch-start' });
|
|
135
|
+
|
|
136
|
+
for await (const ev of CompilerWatcher.watch(this.#state)) {
|
|
137
|
+
if (ev.action === 'reset') {
|
|
138
|
+
Log.info(`Triggering reset due to change in ${ev.file}`);
|
|
139
|
+
EventUtil.sendEvent('state', { state: 'reset' });
|
|
138
140
|
return;
|
|
139
141
|
}
|
|
140
|
-
const
|
|
142
|
+
const { action, entry } = ev;
|
|
141
143
|
if (action !== 'delete') {
|
|
142
|
-
const err = await emitter(
|
|
144
|
+
const err = await emitter(entry.input, true);
|
|
143
145
|
if (err) {
|
|
144
|
-
Log.info('Compilation Error', CompilerUtil.buildTranspileError(
|
|
146
|
+
Log.info('Compilation Error', CompilerUtil.buildTranspileError(entry.input, err));
|
|
145
147
|
} else {
|
|
146
|
-
Log.info(`Compiled ${
|
|
148
|
+
Log.info(`Compiled ${entry.source}`);
|
|
147
149
|
}
|
|
148
150
|
} else {
|
|
149
|
-
|
|
151
|
+
// Remove output
|
|
152
|
+
Log.info(`Removed ${entry.source}, ${entry.output}`);
|
|
153
|
+
await fs.rm(entry.output!, { force: true }); // Ensure output is deleted
|
|
150
154
|
}
|
|
155
|
+
|
|
156
|
+
// Send change events
|
|
157
|
+
EventUtil.sendEvent('change', {
|
|
158
|
+
action: ev.action,
|
|
159
|
+
time: Date.now(),
|
|
160
|
+
file: ev.file,
|
|
161
|
+
folder: ev.folder,
|
|
162
|
+
output: ev.entry.output!,
|
|
163
|
+
module: ev.entry.module.name
|
|
164
|
+
});
|
|
151
165
|
}
|
|
166
|
+
|
|
167
|
+
EventUtil.sendEvent('state', { state: 'watch-end' });
|
|
152
168
|
}
|
|
153
169
|
}
|
|
154
170
|
}
|
package/src/event.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { CompilerServerEvent, CompilerServerEventType } from '../support/types';
|
|
2
|
+
|
|
3
|
+
export class EventUtil {
|
|
4
|
+
static sendEvent<K extends CompilerServerEventType, T extends CompilerServerEvent & { type: K }>(type: K, payload: T['payload']): void {
|
|
5
|
+
process.send?.({ type, payload });
|
|
6
|
+
}
|
|
7
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
|
|
3
|
+
import { IndexedModule, ManifestContext, ManifestModuleUtil, RootIndex, path } from '@travetto/manifest';
|
|
4
|
+
|
|
5
|
+
import { AsyncQueue } from '../../support/queue';
|
|
6
|
+
|
|
7
|
+
export type WatchEvent<T = {}> =
|
|
8
|
+
({ action: 'create' | 'update' | 'delete', file: string, folder: string } & T) |
|
|
9
|
+
{ action: 'reset', file: string };
|
|
10
|
+
|
|
11
|
+
const CREATE_THRESHOLD = 50;
|
|
12
|
+
const VALID_TYPES = new Set(['ts', 'typings', 'js', 'package-json']);
|
|
13
|
+
type ToWatch = { file: string, actions: string[] };
|
|
14
|
+
|
|
15
|
+
/** Watch file for reset */
|
|
16
|
+
async function watchForReset(q: AsyncQueue<WatchEvent>, root: string, files: ToWatch[], signal: AbortSignal): Promise<void> {
|
|
17
|
+
const watchers: Record<string, { folder: string, files: Map<string, (ToWatch & { name: string, actionSet: Set<string> })> }> = {};
|
|
18
|
+
// Group by base path
|
|
19
|
+
for (const el of files) {
|
|
20
|
+
const full = path.resolve(root, el.file);
|
|
21
|
+
const folder = path.dirname(full);
|
|
22
|
+
const tgt = { ...el, name: path.basename(el.file), actionSet: new Set(el.actions) };
|
|
23
|
+
const watcher = (watchers[folder] ??= { folder, files: new Map() });
|
|
24
|
+
watcher.files.set(tgt.name, tgt);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Fire them all off
|
|
28
|
+
Object.values(watchers).map(async (watcher) => {
|
|
29
|
+
for await (const ev of fs.watch(watcher.folder, { persistent: true, encoding: 'utf8', signal })) {
|
|
30
|
+
const toWatch = watcher.files.get(ev.filename!);
|
|
31
|
+
if (toWatch) {
|
|
32
|
+
const stat = await fs.stat(path.resolve(root, ev.filename!)).catch(() => undefined);
|
|
33
|
+
const action = !stat ? 'delete' : ((Date.now() - stat.ctimeMs) < CREATE_THRESHOLD) ? 'create' : 'update';
|
|
34
|
+
if (toWatch.actionSet.has(action)) {
|
|
35
|
+
q.add({ action: 'reset', file: ev.filename! });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Watch recursive files for a given folder */
|
|
43
|
+
async function watchFolder(ctx: ManifestContext, q: AsyncQueue<WatchEvent>, src: string, target: string, signal: AbortSignal): Promise<void> {
|
|
44
|
+
const lib = await import('@parcel/watcher');
|
|
45
|
+
const ignore = [
|
|
46
|
+
'node_modules', '**/.trv',
|
|
47
|
+
...((!ctx.monoRepo || src === ctx.workspacePath) ? [ctx.compilerFolder, ctx.outputFolder, ctx.toolFolder] : []),
|
|
48
|
+
...(await fs.readdir(src)).filter(x => x.startsWith('.'))
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
const cleanup = await lib.subscribe(src, async (err, events) => {
|
|
52
|
+
if (err) {
|
|
53
|
+
console.error('Watch Error', err);
|
|
54
|
+
}
|
|
55
|
+
for (const ev of events) {
|
|
56
|
+
const finalEv = { action: ev.type, file: path.toPosix(ev.path), folder: target };
|
|
57
|
+
if (ev.type !== 'delete') {
|
|
58
|
+
const stats = await fs.stat(finalEv.file);
|
|
59
|
+
if ((Date.now() - stats.ctimeMs) < CREATE_THRESHOLD) {
|
|
60
|
+
ev.type = 'create'; // Force create on newly stated files
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (ev.type === 'delete' && finalEv.file === src) {
|
|
65
|
+
return q.close();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const matches = !finalEv.file.includes('/.') && VALID_TYPES.has(ManifestModuleUtil.getFileType(finalEv.file));
|
|
69
|
+
if (matches) {
|
|
70
|
+
q.add(finalEv);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}, { ignore });
|
|
74
|
+
signal.addEventListener('abort', () => cleanup.unsubscribe());
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Watch files */
|
|
78
|
+
export async function* fileWatchEvents(manifest: ManifestContext, modules: IndexedModule[], signal: AbortSignal): AsyncIterable<WatchEvent> {
|
|
79
|
+
const q = new AsyncQueue<WatchEvent>(signal);
|
|
80
|
+
|
|
81
|
+
for (const m of modules.filter(x => !manifest.monoRepo || x.sourcePath !== manifest.workspacePath)) {
|
|
82
|
+
watchFolder(manifest, q, m.sourcePath, m.sourcePath, signal);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Add monorepo folders
|
|
86
|
+
if (manifest.monoRepo) {
|
|
87
|
+
const mono = modules.find(x => x.sourcePath === manifest.workspacePath)!;
|
|
88
|
+
for (const folder of Object.keys(mono.files)) {
|
|
89
|
+
if (!folder.startsWith('$')) {
|
|
90
|
+
watchFolder(manifest, q, path.resolve(mono.sourcePath, folder), mono.sourcePath, signal);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
watchForReset(q, RootIndex.manifest.workspacePath, [
|
|
96
|
+
{ file: RootIndex.manifest.outputFolder, actions: ['delete'] },
|
|
97
|
+
{ file: RootIndex.manifest.compilerFolder, actions: ['delete'] },
|
|
98
|
+
{ file: RootIndex.manifest.toolFolder, actions: ['delete'] },
|
|
99
|
+
{ file: 'package-lock.json', actions: ['delete', 'update', 'create'] },
|
|
100
|
+
{ file: 'package.json', actions: ['delete', 'update', 'create'] }
|
|
101
|
+
], signal);
|
|
102
|
+
|
|
103
|
+
yield* q;
|
|
104
|
+
}
|
package/src/log.ts
CHANGED
|
@@ -1,18 +1,17 @@
|
|
|
1
|
-
import
|
|
1
|
+
import type { CompilerLogLevel } from '../support/types';
|
|
2
|
+
import { EventUtil } from './event';
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
if (process.send) {
|
|
7
|
-
const ev: CompilerLogEvent = [level, util.format(message, ...args)];
|
|
8
|
-
process.send(ev);
|
|
9
|
-
} else {
|
|
4
|
+
function log(level: CompilerLogLevel, message: string, ...args: unknown[]): void {
|
|
5
|
+
EventUtil.sendEvent('log', { level, message, args, time: Date.now(), scope: 'compiler-exec' });
|
|
6
|
+
if (!process.send) {
|
|
10
7
|
// eslint-disable-next-line no-console
|
|
11
8
|
console[level](message, ...args);
|
|
12
9
|
}
|
|
13
10
|
}
|
|
14
11
|
|
|
15
12
|
export const Log = {
|
|
13
|
+
warn: log.bind(null, 'warn'),
|
|
16
14
|
debug: log.bind(null, 'debug'),
|
|
17
|
-
info: log.bind(null, 'info')
|
|
15
|
+
info: log.bind(null, 'info'),
|
|
16
|
+
error: log.bind(null, 'error'),
|
|
18
17
|
};
|
package/src/state.ts
CHANGED
|
@@ -4,8 +4,8 @@ import { path, ManifestModuleUtil, ManifestModule, ManifestRoot, ManifestIndex }
|
|
|
4
4
|
import { TransformerManager } from '@travetto/transformer';
|
|
5
5
|
|
|
6
6
|
import { CompilerUtil } from './util';
|
|
7
|
-
import { TranspileUtil } from '../support/transpile';
|
|
8
7
|
import { CompileStateEntry } from './types';
|
|
8
|
+
import { CommonUtil } from '../support/util';
|
|
9
9
|
|
|
10
10
|
function folderMapper(root: string, prefix: string): { dir: string, translate: (val: string) => string } {
|
|
11
11
|
let matched: string = '~~';
|
|
@@ -74,7 +74,7 @@ export class CompilerState implements ts.CompilerHost {
|
|
|
74
74
|
this.#transformerManager = await TransformerManager.create(this.#manifestIndex);
|
|
75
75
|
|
|
76
76
|
this.#compilerOptions = {
|
|
77
|
-
...await
|
|
77
|
+
...await CommonUtil.getCompilerOptions(this.#manifest),
|
|
78
78
|
rootDir: this.#rootDir,
|
|
79
79
|
outDir: this.#outputPath
|
|
80
80
|
};
|
|
@@ -123,7 +123,7 @@ export class CompilerState implements ts.CompilerHost {
|
|
|
123
123
|
return this.#sourceToEntry.get(sourceFile);
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
-
registerInput(module: ManifestModule, moduleFile: string):
|
|
126
|
+
registerInput(module: ManifestModule, moduleFile: string): CompileStateEntry {
|
|
127
127
|
const relativeInput = `${module.outputFolder}/${moduleFile}`;
|
|
128
128
|
const sourceFile = path.resolve(this.#manifest.workspacePath, module.sourceFolder, moduleFile);
|
|
129
129
|
const sourceFolder = path.dirname(sourceFile);
|
|
@@ -146,7 +146,7 @@ export class CompilerState implements ts.CompilerHost {
|
|
|
146
146
|
|
|
147
147
|
this.#inputFiles.add(inputFile);
|
|
148
148
|
|
|
149
|
-
return
|
|
149
|
+
return entry;
|
|
150
150
|
}
|
|
151
151
|
|
|
152
152
|
removeInput(inputFile: string): void {
|
package/src/types.ts
CHANGED
|
@@ -6,9 +6,3 @@ export type CompileEmitError = Error | readonly ts.Diagnostic[];
|
|
|
6
6
|
export type CompileEmitter = (file: string, newProgram?: boolean) => Promise<CompileEmitError | undefined>;
|
|
7
7
|
export type CompileEmitEvent = { file: string, i: number, total: number, err?: CompileEmitError };
|
|
8
8
|
export type CompileStateEntry = { source: string, input: string, relativeInput: string, output?: string, module: ManifestModule };
|
|
9
|
-
|
|
10
|
-
export type CompileWatcherHandler = {
|
|
11
|
-
create: (inputFile: string) => void;
|
|
12
|
-
update: (inputFile: string) => void;
|
|
13
|
-
delete: (outputFile: string) => void;
|
|
14
|
-
};
|