@travetto/compiler 3.0.0-rc.8 → 3.0.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 +68 -14
- package/__index__.ts +4 -1
- package/bin/trv.js +57 -63
- package/package.json +8 -6
- package/src/compiler.ts +74 -113
- package/src/log.ts +18 -0
- package/src/state.ts +160 -166
- package/src/types.ts +14 -0
- package/src/util.ts +32 -87
- package/src/watch.ts +142 -0
- package/support/compiler-entry.ts +2 -0
- package/support/launcher.ts +152 -0
- package/support/lock-pinger.ts +25 -0
- package/support/lock.ts +234 -0
- package/support/log.ts +51 -0
- package/support/transpile.ts +210 -0
- package/tsconfig.trv.json +1 -0
- package/bin/transpile.d.ts +0 -32
- package/bin/transpile.js +0 -227
- package/support/bin/compiler-bootstrap.ts +0 -151
- package/support/bin/utils.ts +0 -116
- package/support/main.output.ts +0 -11
package/README.md
CHANGED
|
@@ -1,28 +1,82 @@
|
|
|
1
1
|
<!-- This file was generated by @travetto/doc and should not be modified directly -->
|
|
2
2
|
<!-- Please modify https://github.com/travetto/travetto/tree/main/module/compiler/DOC.ts and execute "npx trv doc" to rebuild -->
|
|
3
3
|
# Compiler
|
|
4
|
-
##
|
|
4
|
+
## The compiler infrastructure for the Travetto framework
|
|
5
5
|
|
|
6
6
|
**Install: @travetto/compiler**
|
|
7
7
|
```bash
|
|
8
8
|
npm install @travetto/compiler
|
|
9
|
+
|
|
10
|
+
# or
|
|
11
|
+
|
|
12
|
+
yarn add @travetto/compiler
|
|
9
13
|
```
|
|
10
14
|
|
|
11
|
-
This module expands upon [Typescript](https://typescriptlang.org), with
|
|
15
|
+
This module expands upon the [Typescript](https://typescriptlang.org) compiler, with the additional features:
|
|
12
16
|
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
* Utilizes `es2015` `Proxy`s to allow for swapping out implementation at runtime
|
|
20
|
-
Additionally, there is support for common AST transformations via [Transformation](https://github.com/travetto/travetto/tree/main/module/transformer#readme "Functionality for AST transformations, with transformer registration, and general utils")
|
|
21
|
-
## Debugging
|
|
22
|
-
When dealing with transformers, logging is somewhat tricky as the compiler executes before the code is loaded. To that end, the file `compiler.log` is created in the cache directory during the compilation process. This is a location that transformers should be free to log to, for debugging, and any additional feedback.
|
|
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
|
+
* 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
|
+
* Removal of type only imports which can break [Ecmascript Module](https://nodejs.org/api/esm.html)-style output
|
|
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.
|
|
23
23
|
|
|
24
24
|
## CLI
|
|
25
25
|
|
|
26
|
-
The module
|
|
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.
|
|
27
|
+
|
|
28
|
+
The CLI supports the following operations:
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
* `clean` - Removes the output folder, and if `-a` is also passed, will also clean out the compiler folder
|
|
32
|
+
* `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
|
+
* `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
|
|
35
|
+
* `<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
|
+
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
|
+
|
|
39
|
+
**Terminal: Sample trv output with debug logging**
|
|
40
|
+
```bash
|
|
41
|
+
$ TRV_BUILD=debug trv build
|
|
42
|
+
|
|
43
|
+
2029-03-14T04:00:00.618Z [lock ] watch pid=000000 Started
|
|
44
|
+
2029-03-14T04:00:00.837Z [lock ] watch pid=000000 Already running, and has built
|
|
45
|
+
2029-03-14T04:00:01.510Z [lock ] watch pid=000000 Completed
|
|
46
|
+
2029-03-14T04:00:02.450Z [build ] Successfully built
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**Terminal: Sample trv output with default log level**
|
|
50
|
+
```bash
|
|
51
|
+
$ trv build
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Compilation Architecture
|
|
55
|
+
|
|
56
|
+
The compiler will move through the following phases on a given compilation execution:
|
|
57
|
+
|
|
58
|
+
* `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
|
+
* `Lock Management` - Manages cross-process interaction to ensure single compiler
|
|
60
|
+
* `Build Compiler` - Leverages [Typescript](https://typescriptlang.org) to build files needed to execute compiler
|
|
61
|
+
* `Build Manifest` - Produces the manifest for the given execution
|
|
62
|
+
* `Build Transformers` - Leverages [Typescript](https://typescriptlang.org) to compile all transformers defined in the manifest
|
|
63
|
+
* `Produce Manifest Delta` - Compare the output file system with the manifest to determine what needs to be compiled
|
|
64
|
+
* `Clear all output if needed` - When the compiler source or transformers change, invalidate the entire output
|
|
65
|
+
* `Persist Manifest(s)` - Ensure the manifest is available for the compiler to leverage. Multiple will be written if in a monorepo
|
|
66
|
+
* `Invoke Compiler` - Run [Typescript](https://typescriptlang.org) compiler with the aforementioned enhancements
|
|
67
|
+
|
|
68
|
+
### 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.
|
|
71
|
+
|
|
72
|
+
### Lock Management
|
|
73
|
+
|
|
74
|
+
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
|
+
* No Watch - Building
|
|
78
|
+
* Watch - No Build
|
|
79
|
+
* Watch - Building
|
|
80
|
+
* Inactive / Stale
|
|
27
81
|
|
|
28
|
-
|
|
82
|
+
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/__index__.ts
CHANGED
package/bin/trv.js
CHANGED
|
@@ -1,81 +1,75 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// @ts-check
|
|
4
|
+
import fs from 'fs/promises';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import { createRequire } from 'module';
|
|
4
7
|
|
|
5
|
-
|
|
6
|
-
* @param {import('@travetto/manifest').ManifestContext} ctx
|
|
7
|
-
* @return {Promise<import('../support/bin/compiler-bootstrap')>}
|
|
8
|
-
*/
|
|
9
|
-
async function $getBootstrap(ctx) {
|
|
10
|
-
const path = require('path');
|
|
11
|
-
const { buildPackage } = require('./transpile');
|
|
8
|
+
import { getManifestContext } from '@travetto/manifest/bin/context.js';
|
|
12
9
|
|
|
13
|
-
|
|
10
|
+
const VALID_OPS = { watch: 'watch', build: 'build', clean: 'clean', manifest: 'manifest' };
|
|
14
11
|
|
|
15
|
-
|
|
16
|
-
ctx, '__compiler_bootstrap__', root, 'support/bin/compiler-bootstrap.ts',
|
|
17
|
-
['support/bin/utils.ts', 'bin/transpile.js', 'package.json']
|
|
18
|
-
);
|
|
19
|
-
|
|
20
|
-
try { return require(loc); } catch { return import(loc); }
|
|
21
|
-
}
|
|
12
|
+
const COMPILER_FILES = [...['launcher', 'transpile', 'lock', 'log', 'lock-pinger'].map(x => `support/${x}.js`), 'package.json'];
|
|
22
13
|
|
|
23
14
|
/**
|
|
24
15
|
* @param {import('@travetto/manifest').ManifestContext} ctx
|
|
25
|
-
* @
|
|
16
|
+
* @return {Promise<import('@travetto/compiler/support/launcher').launch>}
|
|
26
17
|
*/
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
const flags = Object.fromEntries(args.filter(x => x.startsWith('-')).map(x => x.split('-').pop()).map(x => [x, true]));
|
|
18
|
+
const $getLauncher = async (ctx) => {
|
|
19
|
+
const tsconfigFile = path.resolve(ctx.workspacePath, 'tsconfig.json');
|
|
20
|
+
if (!(await fs.stat(tsconfigFile).catch(() => undefined))) {
|
|
21
|
+
await fs.writeFile(tsconfigFile, JSON.stringify({ extends: '@travetto/compiler/tsconfig.trv.json' }), 'utf8');
|
|
22
|
+
}
|
|
23
|
+
const compPkg = createRequire(path.resolve(ctx.workspacePath, 'node_modules')).resolve('@travetto/compiler/package.json');
|
|
24
|
+
const files = [];
|
|
35
25
|
|
|
36
|
-
const
|
|
37
|
-
|
|
26
|
+
for (const file of COMPILER_FILES) {
|
|
27
|
+
const target = path.resolve(ctx.workspacePath, ctx.compilerFolder, 'node_modules', '@travetto/compiler', file);
|
|
28
|
+
const src = compPkg.replace('package.json', file.replace(/[.]js$/, '.ts'));
|
|
38
29
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
30
|
+
const targetTime = await fs.stat(target).then(s => Math.max(s.mtimeMs, s.ctimeMs)).catch(() => 0);
|
|
31
|
+
const srcTime = await fs.stat(src).then(s => Math.max(s.mtimeMs, s.ctimeMs));
|
|
32
|
+
// If stale
|
|
33
|
+
if (srcTime > targetTime) {
|
|
34
|
+
const ts = (await import('typescript')).default;
|
|
35
|
+
const module = ctx.moduleType === 'module' ? ts.ModuleKind.ESNext : ts.ModuleKind.CommonJS;
|
|
36
|
+
await fs.mkdir(path.dirname(target), { recursive: true });
|
|
37
|
+
const text = await fs.readFile(src, 'utf8');
|
|
38
|
+
if (file.endsWith('.js')) {
|
|
39
|
+
const content = ts.transpile(
|
|
40
|
+
text,
|
|
41
|
+
{ target: ts.ScriptTarget.ES2020, module, esModuleInterop: true, allowSyntheticDefaultImports: true }
|
|
42
|
+
)
|
|
43
|
+
.replace(/^((?:im|ex)port .*from '[.][^']+)(')/mg, (_, a, b) => `${a}.js${b}`)
|
|
44
|
+
.replace(/^(import [^\n]*from '[^.][^\n/]+[/][^\n/]+[/][^\n']+)(')/mg, (_, a, b) => `${a}.js${b}`);
|
|
45
|
+
await fs.writeFile(target, content, 'utf8');
|
|
46
|
+
} else {
|
|
47
|
+
const pkg = JSON.parse(text);
|
|
48
|
+
pkg.type = ctx.moduleType;
|
|
49
|
+
await fs.writeFile(target, JSON.stringify(pkg, null, 2), 'utf8');
|
|
50
|
+
}
|
|
51
|
+
// Compile
|
|
46
52
|
}
|
|
53
|
+
files.push(target);
|
|
47
54
|
}
|
|
48
55
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
await compile(ctx, true);
|
|
62
|
-
return;
|
|
63
|
-
case 'build':
|
|
64
|
-
await compile(ctx);
|
|
65
|
-
message(`Built to ${ctx.workspacePath}/${ctx.outputFolder}`);
|
|
66
|
-
break;
|
|
67
|
-
default: {
|
|
68
|
-
const path = require('path/posix');
|
|
69
|
-
const { manifest } = await compile(ctx);
|
|
70
|
-
const out = path.join(ctx.workspacePath, ctx.outputFolder);
|
|
71
|
-
// TODO: Externalize somehow?
|
|
72
|
-
const cliMain = path.join(out, manifest.modules['@travetto/cli'].output, 'support', 'main.cli.js');
|
|
73
|
-
process.env.TRV_MANIFEST = ctx.mainModule;
|
|
74
|
-
process.env.TRV_OUTPUT = out;
|
|
75
|
-
await import(process.env.TRV_MAIN = cliMain);
|
|
76
|
-
return;
|
|
56
|
+
try { return require(files[0]).launch; }
|
|
57
|
+
catch { return import(files[0]).then(x => x.launch); }
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
(async () => {
|
|
61
|
+
const ctx = await getManifestContext();
|
|
62
|
+
const [op, args] = [VALID_OPS[process.argv[2]], process.argv.slice(3)];
|
|
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 });
|
|
77
68
|
}
|
|
69
|
+
return console.log(`Cleaned ${ctx.workspacePath}: [${folders.join(', ')}]`);
|
|
78
70
|
}
|
|
79
|
-
}
|
|
80
71
|
|
|
81
|
-
|
|
72
|
+
const rootCtx = ctx.monoRepo ? await getManifestContext(ctx.workspacePath) : ctx;
|
|
73
|
+
|
|
74
|
+
return (await $getLauncher(ctx))(ctx, rootCtx, op, args);
|
|
75
|
+
})();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/compiler",
|
|
3
|
-
"version": "3.0.0
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
|
+
"description": "The compiler infrastructure for the Travetto framework",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"compiler",
|
|
7
7
|
"travetto",
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"email": "travetto.framework@gmail.com",
|
|
14
14
|
"name": "Travetto Framework"
|
|
15
15
|
},
|
|
16
|
+
"type": "module",
|
|
16
17
|
"files": [
|
|
17
18
|
"__index__.ts",
|
|
18
19
|
"src",
|
|
@@ -29,12 +30,13 @@
|
|
|
29
30
|
"directory": "module/compiler"
|
|
30
31
|
},
|
|
31
32
|
"dependencies": {
|
|
32
|
-
"@
|
|
33
|
-
"@travetto/
|
|
34
|
-
"@travetto/
|
|
33
|
+
"@parcel/watcher": "^2.1.0",
|
|
34
|
+
"@travetto/manifest": "^3.0.0",
|
|
35
|
+
"@travetto/terminal": "^3.0.0",
|
|
36
|
+
"@travetto/transformer": "^3.0.0"
|
|
35
37
|
},
|
|
36
38
|
"peerDependencies": {
|
|
37
|
-
"@travetto/cli": "^3.0.0
|
|
39
|
+
"@travetto/cli": "^3.0.0"
|
|
38
40
|
},
|
|
39
41
|
"peerDependenciesMeta": {
|
|
40
42
|
"@travetto/cli": {
|
package/src/compiler.ts
CHANGED
|
@@ -1,127 +1,72 @@
|
|
|
1
|
+
import { install } from 'source-map-support';
|
|
1
2
|
import ts from 'typescript';
|
|
2
3
|
import fs from 'fs/promises';
|
|
3
|
-
import path from 'path';
|
|
4
4
|
|
|
5
|
-
import { ManifestState } from '@travetto/manifest';
|
|
6
5
|
import { GlobalTerminal, TerminalProgressEvent } from '@travetto/terminal';
|
|
6
|
+
import { RootIndex } from '@travetto/manifest';
|
|
7
7
|
|
|
8
8
|
import { CompilerUtil } from './util';
|
|
9
9
|
import { CompilerState } from './state';
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
get(): ts.CustomTransformers | undefined;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
type EmitError = Error | readonly ts.Diagnostic[];
|
|
17
|
-
type Emitter = (file: string, newProgram?: boolean) => EmitError | undefined;
|
|
18
|
-
type EmitEvent = { file: string, i: number, total: number, err?: EmitError };
|
|
10
|
+
import { CompilerWatcher } from './watch';
|
|
11
|
+
import { Log } from './log';
|
|
12
|
+
import { CompileEmitError, CompileEmitEvent, CompileEmitter } from './types';
|
|
19
13
|
|
|
20
14
|
/**
|
|
21
15
|
* Compilation support
|
|
22
16
|
*/
|
|
23
17
|
export class Compiler {
|
|
24
18
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
x => (x.files.support ?? [])
|
|
35
|
-
.filter(([f, type]) => type === 'ts' && f.startsWith('support/transformer.'))
|
|
36
|
-
.map(([f]) =>
|
|
37
|
-
path.resolve(
|
|
38
|
-
this.#state.manifest.workspacePath,
|
|
39
|
-
this.#state.manifest.compilerFolder,
|
|
40
|
-
x.output,
|
|
41
|
-
f.replace(/[.][tj]s$/, '.js')
|
|
42
|
-
)
|
|
43
|
-
)
|
|
44
|
-
);
|
|
45
|
-
|
|
46
|
-
return this;
|
|
19
|
+
/**
|
|
20
|
+
* Run compiler as a main entry point
|
|
21
|
+
*/
|
|
22
|
+
static async main(): Promise<void> {
|
|
23
|
+
const [dirty, watch] = process.argv.slice(2);
|
|
24
|
+
const state = await CompilerState.get(RootIndex);
|
|
25
|
+
const dirtyFiles = (await fs.readFile(dirty, 'utf8')).split(/\n/).filter(x => !!x);
|
|
26
|
+
await new Compiler(state, dirtyFiles, watch === 'true').run();
|
|
27
|
+
process.exit(0);
|
|
47
28
|
}
|
|
48
29
|
|
|
49
|
-
|
|
50
|
-
|
|
30
|
+
#state: CompilerState;
|
|
31
|
+
#dirtyFiles: string[];
|
|
32
|
+
#watch?: boolean;
|
|
33
|
+
|
|
34
|
+
constructor(state: CompilerState, dirtyFiles: string[], watch?: boolean) {
|
|
35
|
+
this.#state = state;
|
|
36
|
+
this.#dirtyFiles = dirtyFiles[0] === '*' ?
|
|
37
|
+
this.#state.getAllFiles() :
|
|
38
|
+
dirtyFiles.map(f => this.#state.getBySource(f)!.input);
|
|
39
|
+
this.#watch = watch;
|
|
51
40
|
}
|
|
52
41
|
|
|
53
42
|
/**
|
|
54
43
|
* Watches local modules
|
|
55
44
|
*/
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const err = emit(file, true);
|
|
45
|
+
#watchLocalModules(emit: CompileEmitter): Promise<() => Promise<void>> {
|
|
46
|
+
return new CompilerWatcher(this.#state).watchFiles(async file => {
|
|
47
|
+
const err = await emit(file, true);
|
|
60
48
|
if (err) {
|
|
61
|
-
|
|
49
|
+
Log.info('Compilation Error', CompilerUtil.buildTranspileError(file, err));
|
|
62
50
|
} else {
|
|
63
|
-
|
|
51
|
+
Log.info(`Compiled ${file.split('node_modules/')[1]}`);
|
|
64
52
|
}
|
|
65
|
-
|
|
66
|
-
const watcher = this.state.getWatcher({
|
|
67
|
-
create: (inputFile) => emitWithError(inputFile),
|
|
68
|
-
update: (inputFile) => emitWithError(inputFile),
|
|
69
|
-
delete: (outputFile) => fs.unlink(outputFile).catch(() => { })
|
|
53
|
+
return err;
|
|
70
54
|
});
|
|
71
|
-
return CompilerUtil.fileWatcher(folders, watcher);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
async createTransformerProvider(): Promise<TransformerProvider> {
|
|
75
|
-
const { TransformerManager } = await import('@travetto/transformer');
|
|
76
|
-
return TransformerManager.create(this.#transformers, this.state.manifest);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
async writeRawFile(file: string, contents: string, mode?: string): Promise<void> {
|
|
80
|
-
const outFile = path.resolve(
|
|
81
|
-
this.#state.manifest.workspacePath,
|
|
82
|
-
this.#state.manifest.outputFolder,
|
|
83
|
-
file
|
|
84
|
-
);
|
|
85
|
-
await fs.mkdir(path.dirname(outFile), { recursive: true });
|
|
86
|
-
await fs.writeFile(outFile, contents, { encoding: 'utf8', mode });
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
async outputInit(): Promise<void> {
|
|
90
|
-
// Write manifest
|
|
91
|
-
await this.writeRawFile(this.#state.manifest.manifestFile, JSON.stringify(this.state.manifest));
|
|
92
|
-
// TODO: This needs to be isolated, just like in the bootstrap
|
|
93
|
-
await this.writeRawFile('trv', '#!/bin/sh\nnode node_modules/@travetto/cli/support/main.cli.js $@\n', '755');
|
|
94
|
-
await this.writeRawFile('trv.cmd', 'node node_modules/@travetto/cli/support/main.cli.js %*\n', '755');
|
|
95
55
|
}
|
|
96
56
|
|
|
97
57
|
/**
|
|
98
58
|
* Compile in a single pass, only emitting dirty files
|
|
99
59
|
*/
|
|
100
|
-
async getCompiler(): Promise<
|
|
60
|
+
async getCompiler(): Promise<CompileEmitter> {
|
|
101
61
|
let program: ts.Program;
|
|
102
62
|
|
|
103
|
-
const
|
|
104
|
-
const options = await CompilerUtil.getCompilerOptions(
|
|
105
|
-
path.resolve(
|
|
106
|
-
this.#state.manifest.workspacePath,
|
|
107
|
-
this.#state.manifest.outputFolder,
|
|
108
|
-
),
|
|
109
|
-
this.#bootTsconfig,
|
|
110
|
-
this.#state.manifest.workspacePath
|
|
111
|
-
);
|
|
112
|
-
const host = this.state.getCompilerHost(options);
|
|
113
|
-
|
|
114
|
-
const emit = (file: string, needsNewProgram = program === undefined): EmitError | undefined => {
|
|
115
|
-
if (needsNewProgram) {
|
|
116
|
-
program = ts.createProgram({ rootNames: this.#state.getAllFiles(), host, options, oldProgram: program });
|
|
117
|
-
transformers.init(program.getTypeChecker());
|
|
118
|
-
}
|
|
63
|
+
const emit = async (inputFile: string, needsNewProgram = program === undefined): Promise<CompileEmitError | undefined> => {
|
|
119
64
|
try {
|
|
120
|
-
|
|
121
|
-
program
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
if (result
|
|
65
|
+
if (needsNewProgram) {
|
|
66
|
+
program = this.#state.createProgram(program);
|
|
67
|
+
}
|
|
68
|
+
const result = this.#state.writeInputFile(program, inputFile);
|
|
69
|
+
if (result?.diagnostics?.length) {
|
|
125
70
|
return result.diagnostics;
|
|
126
71
|
}
|
|
127
72
|
} catch (err) {
|
|
@@ -139,49 +84,65 @@ export class Compiler {
|
|
|
139
84
|
/**
|
|
140
85
|
* Emit all files as a stream
|
|
141
86
|
*/
|
|
142
|
-
async * emit(files: string[], emitter:
|
|
87
|
+
async * emit(files: string[], emitter: CompileEmitter): AsyncIterable<CompileEmitEvent> {
|
|
143
88
|
let i = 0;
|
|
144
|
-
const manifest = this.#state.manifest;
|
|
145
89
|
for (const file of files) {
|
|
146
|
-
const err = emitter(file);
|
|
147
|
-
const
|
|
148
|
-
|
|
149
|
-
.replace(manifest.compilerFolder, manifest.outputFolder);
|
|
150
|
-
yield { file: outputFile, i: i += 1, err, total: files.length };
|
|
90
|
+
const err = await emitter(file);
|
|
91
|
+
const imp = file.replace(/.*node_modules\//, '');
|
|
92
|
+
yield { file: imp, i: i += 1, err, total: files.length };
|
|
151
93
|
}
|
|
94
|
+
Log.debug(`Compiled ${i} files`);
|
|
152
95
|
}
|
|
153
96
|
|
|
154
97
|
/**
|
|
155
98
|
* Run the compiler
|
|
156
99
|
*/
|
|
157
|
-
async run(
|
|
158
|
-
await
|
|
100
|
+
async run(): Promise<void> {
|
|
101
|
+
await GlobalTerminal.init();
|
|
102
|
+
install();
|
|
103
|
+
|
|
104
|
+
Log.debug('Compilation started');
|
|
105
|
+
|
|
106
|
+
process.on('disconnect', () => process.exit(0));
|
|
107
|
+
|
|
159
108
|
const emitter = await this.getCompiler();
|
|
160
109
|
let failed = false;
|
|
161
110
|
|
|
162
|
-
|
|
111
|
+
Log.debug('Compiler loaded');
|
|
112
|
+
|
|
113
|
+
const resolveEmittedFile = ({ file, total, i, err }: CompileEmitEvent): TerminalProgressEvent => {
|
|
163
114
|
if (err) {
|
|
164
115
|
failed = true;
|
|
165
116
|
console.error(CompilerUtil.buildTranspileError(file, err));
|
|
166
117
|
}
|
|
167
|
-
return { idx: i, total, text: `Compiling [%idx/%total] -- ${file
|
|
118
|
+
return { idx: i, total, text: `Compiling [%idx/%total] -- ${file}` };
|
|
168
119
|
};
|
|
169
120
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
files = this.state.getAllFiles();
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
if (files.length) {
|
|
176
|
-
await GlobalTerminal.trackProgress(this.emit(files, emitter), resolveEmittedFile, { position: 'bottom' });
|
|
121
|
+
if (this.#dirtyFiles.length) {
|
|
122
|
+
await GlobalTerminal.trackProgress(this.emit(this.#dirtyFiles, emitter), resolveEmittedFile, { position: 'bottom' });
|
|
177
123
|
if (failed) {
|
|
124
|
+
Log.debug('Compilation failed');
|
|
178
125
|
process.exit(1);
|
|
126
|
+
} else {
|
|
127
|
+
Log.debug('Compilation succeeded');
|
|
179
128
|
}
|
|
129
|
+
} else if (this.#watch) {
|
|
130
|
+
// Prime compiler before complete
|
|
131
|
+
const resolved = this.#state.getArbitraryInputFile();
|
|
132
|
+
await emitter(resolved, true);
|
|
180
133
|
}
|
|
181
134
|
|
|
182
|
-
|
|
135
|
+
process.send?.('build-complete');
|
|
136
|
+
|
|
137
|
+
if (this.#watch) {
|
|
138
|
+
Log.info('Watch is ready');
|
|
183
139
|
await this.#watchLocalModules(emitter);
|
|
184
|
-
|
|
140
|
+
const output = this.#state.resolveOutputFile('.');
|
|
141
|
+
for await (const _ of fs.watch(output)) {
|
|
142
|
+
if (!await fs.stat(output).catch(() => false)) {
|
|
143
|
+
process.send?.('restart');
|
|
144
|
+
}
|
|
145
|
+
}
|
|
185
146
|
}
|
|
186
147
|
}
|
|
187
|
-
}
|
|
148
|
+
}
|
package/src/log.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import util from 'util';
|
|
2
|
+
|
|
3
|
+
import type { CompilerLogEvent } from '../support/log';
|
|
4
|
+
|
|
5
|
+
function log(level: 'info' | 'debug', message: string, ...args: unknown[]): void {
|
|
6
|
+
if (process.send) {
|
|
7
|
+
const ev: CompilerLogEvent = [level, util.format(message, ...args)];
|
|
8
|
+
process.send(ev);
|
|
9
|
+
} else {
|
|
10
|
+
// eslint-disable-next-line no-console
|
|
11
|
+
console[level](message, ...args);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const Log = {
|
|
16
|
+
debug: log.bind(null, 'debug'),
|
|
17
|
+
info: log.bind(null, 'info')
|
|
18
|
+
};
|