@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
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import fs from 'fs/promises';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
import cp from 'child_process';
|
|
5
|
+
import { createRequire } from 'module';
|
|
6
|
+
|
|
7
|
+
import { DeltaEvent, ManifestContext, ManifestRoot, Package } from '@travetto/manifest';
|
|
8
|
+
|
|
9
|
+
import { LogUtil } from './log';
|
|
10
|
+
|
|
11
|
+
type ModFile = { input: string, output: string, stale: boolean };
|
|
12
|
+
export type CompileResult = 'restart' | 'complete' | 'skipped';
|
|
13
|
+
|
|
14
|
+
const OPT_CACHE: Record<string, import('typescript').CompilerOptions> = {};
|
|
15
|
+
const SRC_REQ = createRequire(path.resolve('node_modules'));
|
|
16
|
+
const RECENT_STAT = (stat: { ctimeMs: number, mtimeMs: number }): number => Math.max(stat.ctimeMs, stat.mtimeMs);
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Transpile utilities for launching
|
|
20
|
+
*/
|
|
21
|
+
export class TranspileUtil {
|
|
22
|
+
/**
|
|
23
|
+
* Write text file, and ensure folder exists
|
|
24
|
+
*/
|
|
25
|
+
static writeTextFile = (file: string, content: string): Promise<void> =>
|
|
26
|
+
fs.mkdir(path.dirname(file), { recursive: true }).then(() => fs.writeFile(file, content, 'utf8'));
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Returns the compiler options
|
|
30
|
+
*/
|
|
31
|
+
static async getCompilerOptions(ctx: ManifestContext): Promise<{}> {
|
|
32
|
+
if (!(ctx.workspacePath in OPT_CACHE)) {
|
|
33
|
+
let tsconfig = path.resolve(ctx.workspacePath, 'tsconfig.json');
|
|
34
|
+
|
|
35
|
+
if (!await fs.stat(tsconfig).then(_ => true, _ => false)) {
|
|
36
|
+
tsconfig = SRC_REQ.resolve('@travetto/compiler/tsconfig.trv.json');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const ts = (await import('typescript')).default;
|
|
40
|
+
|
|
41
|
+
const { options } = ts.parseJsonSourceFileConfigFileContent(
|
|
42
|
+
ts.readJsonConfigFile(tsconfig, ts.sys.readFile), ts.sys, ctx.workspacePath
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
OPT_CACHE[ctx.workspacePath] = {
|
|
46
|
+
...options,
|
|
47
|
+
allowJs: true,
|
|
48
|
+
sourceMap: false,
|
|
49
|
+
inlineSourceMap: true,
|
|
50
|
+
resolveJsonModule: true,
|
|
51
|
+
sourceRoot: ctx.workspacePath,
|
|
52
|
+
rootDir: ctx.workspacePath,
|
|
53
|
+
outDir: path.resolve(ctx.workspacePath),
|
|
54
|
+
module: ctx.moduleType === 'commonjs' ? ts.ModuleKind.CommonJS : ts.ModuleKind.ESNext,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
return OPT_CACHE[ctx.workspacePath];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Output a file, support for ts, js, and package.json
|
|
62
|
+
*/
|
|
63
|
+
static async transpileFile(ctx: ManifestContext, inputFile: string, outputFile: string): Promise<void> {
|
|
64
|
+
if (inputFile.endsWith('.ts') || inputFile.endsWith('.js')) {
|
|
65
|
+
const compilerOut = path.resolve(ctx.workspacePath, ctx.compilerFolder, 'node_modules');
|
|
66
|
+
|
|
67
|
+
const text = (await fs.readFile(inputFile, 'utf8'))
|
|
68
|
+
.replace(/from '([.][^']+)'/g, (_, i) => `from '${i.replace(/[.]js$/, '')}.js'`)
|
|
69
|
+
.replace(/from '(@travetto\/(.*?))'/g, (_, i, s) => `from '${path.resolve(compilerOut, `${i}${s.includes('/') ? '.js' : '/__index__.js'}`)}'`);
|
|
70
|
+
|
|
71
|
+
const ts = (await import('typescript')).default;
|
|
72
|
+
const content = ts.transpile(text, await this.getCompilerOptions(ctx), inputFile);
|
|
73
|
+
await this.writeTextFile(outputFile, content);
|
|
74
|
+
} else if (inputFile.endsWith('package.json')) {
|
|
75
|
+
const pkg: Package = JSON.parse(await fs.readFile(inputFile, 'utf8'));
|
|
76
|
+
const main = pkg.main?.replace(/[.]ts$/, '.js');
|
|
77
|
+
const files = pkg.files?.map(x => x.replace('.ts', '.js'));
|
|
78
|
+
|
|
79
|
+
const content = JSON.stringify({ ...pkg, main, type: ctx.moduleType, files }, null, 2);
|
|
80
|
+
await this.writeTextFile(outputFile, content);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Scan directory to find all project sources for comparison
|
|
86
|
+
*/
|
|
87
|
+
static async getModuleSources(ctx: ManifestContext, module: string, seed: string[]): Promise<ModFile[]> {
|
|
88
|
+
const inputFolder = (ctx.mainModule === module) ?
|
|
89
|
+
process.cwd() :
|
|
90
|
+
path.dirname(SRC_REQ.resolve(`${module}/package.json`));
|
|
91
|
+
|
|
92
|
+
const folders = seed.filter(x => !/[.]/.test(x)).map(x => path.resolve(inputFolder, x));
|
|
93
|
+
const files = seed.filter(x => /[.]/.test(x)).map(x => path.resolve(inputFolder, x));
|
|
94
|
+
|
|
95
|
+
while (folders.length) {
|
|
96
|
+
const sub = folders.pop();
|
|
97
|
+
if (!sub) {
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
for (const file of await fs.readdir(sub).catch(() => [])) {
|
|
102
|
+
if (file.startsWith('.')) {
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
const resolvedInput = path.resolve(sub, file);
|
|
106
|
+
const stat = await fs.stat(resolvedInput);
|
|
107
|
+
|
|
108
|
+
if (stat.isDirectory()) {
|
|
109
|
+
folders.push(resolvedInput);
|
|
110
|
+
} else if (file.endsWith('.d.ts')) {
|
|
111
|
+
// Do nothing
|
|
112
|
+
} else if (file.endsWith('.ts') || file.endsWith('.js')) {
|
|
113
|
+
files.push(resolvedInput);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const outputFolder = path.resolve(ctx.workspacePath, ctx.compilerFolder, 'node_modules', module);
|
|
119
|
+
const out: ModFile[] = [];
|
|
120
|
+
for (const input of files) {
|
|
121
|
+
const output = input.replace(inputFolder, outputFolder).replace(/[.]ts$/, '.js');
|
|
122
|
+
const inputTs = await fs.stat(input).then(RECENT_STAT, () => 0);
|
|
123
|
+
if (inputTs) {
|
|
124
|
+
const outputTs = await fs.stat(output).then(RECENT_STAT, () => 0);
|
|
125
|
+
await fs.mkdir(path.dirname(output), { recursive: true, });
|
|
126
|
+
out.push({ input, output, stale: inputTs > outputTs });
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return out;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Recompile folder if stale
|
|
135
|
+
*/
|
|
136
|
+
static async compileIfStale(ctx: ManifestContext, scope: string, mod: string, seed: string[]): Promise<string[]> {
|
|
137
|
+
const files = await this.getModuleSources(ctx, mod, seed);
|
|
138
|
+
const changes = files.filter(x => x.stale).map(x => x.input);
|
|
139
|
+
const out: string[] = [];
|
|
140
|
+
|
|
141
|
+
try {
|
|
142
|
+
await LogUtil.withLogger(scope, async log => {
|
|
143
|
+
if (files.some(f => f.stale)) {
|
|
144
|
+
log('debug', 'Starting');
|
|
145
|
+
for (const file of files.filter(x => x.stale)) {
|
|
146
|
+
await this.transpileFile(ctx, file.input, file.output);
|
|
147
|
+
}
|
|
148
|
+
if (changes.length) {
|
|
149
|
+
out.push(...changes.map(x => `${mod}/${x}`));
|
|
150
|
+
log('debug', `Source changed: ${changes.join(', ')}`);
|
|
151
|
+
}
|
|
152
|
+
log('debug', 'Completed');
|
|
153
|
+
} else {
|
|
154
|
+
log('debug', 'Skipped');
|
|
155
|
+
}
|
|
156
|
+
}, false, [mod]);
|
|
157
|
+
} catch (err) {
|
|
158
|
+
console.error(err);
|
|
159
|
+
}
|
|
160
|
+
return out;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Run compiler
|
|
165
|
+
*/
|
|
166
|
+
static async runCompiler(ctx: ManifestContext, manifest: ManifestRoot, changed: DeltaEvent[], watch: boolean, onMessage: (msg: unknown) => void): Promise<CompileResult> {
|
|
167
|
+
const compiler = path.resolve(ctx.workspacePath, ctx.compilerFolder);
|
|
168
|
+
const main = path.resolve(compiler, 'node_modules', '@travetto/compiler/support/compiler-entry.js');
|
|
169
|
+
const deltaFile = path.resolve(os.tmpdir(), `manifest-delta.${process.pid}.${process.ppid}.${Date.now()}.json`);
|
|
170
|
+
|
|
171
|
+
const changedFiles = changed[0]?.file === '*' ? ['*'] : changed.map(ev =>
|
|
172
|
+
path.resolve(manifest.workspacePath, manifest.modules[ev.module].sourceFolder, ev.file)
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
let proc: cp.ChildProcess | undefined;
|
|
176
|
+
let kill: (() => void) | undefined;
|
|
177
|
+
|
|
178
|
+
try {
|
|
179
|
+
await this.writeTextFile(deltaFile, changedFiles.join('\n'));
|
|
180
|
+
|
|
181
|
+
return await LogUtil.withLogger('compiler-exec', log => new Promise<CompileResult>((res, rej) => {
|
|
182
|
+
proc = cp.spawn(process.argv0, [main, deltaFile, `${watch}`], {
|
|
183
|
+
env: {
|
|
184
|
+
...process.env,
|
|
185
|
+
TRV_MANIFEST: path.resolve(ctx.workspacePath, ctx.outputFolder, 'node_modules', ctx.mainModule),
|
|
186
|
+
},
|
|
187
|
+
stdio: [0, 1, 2, 'ipc'],
|
|
188
|
+
})
|
|
189
|
+
.on('message', msg => {
|
|
190
|
+
if (LogUtil.isLogEvent(msg)) {
|
|
191
|
+
log(...msg);
|
|
192
|
+
} else if (msg === 'restart') {
|
|
193
|
+
res(msg);
|
|
194
|
+
} else {
|
|
195
|
+
onMessage(msg);
|
|
196
|
+
}
|
|
197
|
+
})
|
|
198
|
+
.on('exit', code => (code !== null && code > 0) ? rej(new Error('Failed during compilation')) : res('complete'));
|
|
199
|
+
kill = (): void => { proc?.kill('SIGKILL'); };
|
|
200
|
+
process.on('exit', kill);
|
|
201
|
+
}));
|
|
202
|
+
} finally {
|
|
203
|
+
if (proc?.killed === false) { proc.kill('SIGKILL'); }
|
|
204
|
+
if (kill) {
|
|
205
|
+
process.removeListener('exit', kill);
|
|
206
|
+
}
|
|
207
|
+
await fs.rm(deltaFile, { force: true });
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
package/tsconfig.trv.json
CHANGED
package/bin/transpile.d.ts
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import type { ManifestContext, Package } from '@travetto/manifest';
|
|
2
|
-
|
|
3
|
-
declare namespace Transpile {
|
|
4
|
-
type CompileCommand = 'build' | 'watch' | 'clean' | 'manifest';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Writes a package json file
|
|
8
|
-
*/
|
|
9
|
-
function writePackageJson(ctx: ManifestContext, inputFile: string, outputFile: string, transform?: (pkg: Package) => Package): Promise<void>;
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Transpiles a file
|
|
13
|
-
*/
|
|
14
|
-
function transpileFile(ctx: ManifestContext, inputFile: string, outputFile: string): Promise<void>;
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Write js file
|
|
18
|
-
*/
|
|
19
|
-
function writeJsFile(ctx: ManifestContext, inputFile: string, outputFile: string): Promise<void>;
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Build an entire package
|
|
23
|
-
*/
|
|
24
|
-
function buildPackage(ctx: ManifestContext, name: string, sourcePath: string, mainSource: string, extraSource: string[]): Promise<string>;
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Get Context for building
|
|
28
|
-
*/
|
|
29
|
-
function getContext(folder?: string): Promise<ManifestContext>;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export = Transpile;
|
package/bin/transpile.js
DELETED
|
@@ -1,227 +0,0 @@
|
|
|
1
|
-
// @ts-check
|
|
2
|
-
|
|
3
|
-
const _opts = {};
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* @typedef {import('@travetto/manifest').Package} Pkg
|
|
7
|
-
* @typedef {import('@travetto/manifest').ManifestContext} ManifestContext
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
function $imp(mod) {
|
|
11
|
-
try { return require(mod); } catch { return import(mod).then(x => x.default); }
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/** @type {() => import('typescript')} */
|
|
15
|
-
const $getTs = $imp.bind(null, 'typescript');
|
|
16
|
-
/** @type {() => import('fs/promises')} */
|
|
17
|
-
const $getFs = $imp.bind(null, 'fs/promises');
|
|
18
|
-
/** @type {() => import('path')} */
|
|
19
|
-
const $getPath = $imp.bind(null, 'path');
|
|
20
|
-
/** @type {() => ({createRequire:(folder:string) => ({ resolve: (file:string)=>string})})} */
|
|
21
|
-
const $getModule = $imp.bind(null, 'module');
|
|
22
|
-
/** @param {string} x */
|
|
23
|
-
const toPosix = x => x.replace(/\\/g, '/');
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Returns the package.json
|
|
27
|
-
* @param {string} inputFolder
|
|
28
|
-
* @returns {Promise<Pkg>}
|
|
29
|
-
*/
|
|
30
|
-
async function $getPkg(inputFolder) {
|
|
31
|
-
const fs = await $getFs();
|
|
32
|
-
const path = await $getPath();
|
|
33
|
-
if (!inputFolder.endsWith('.json')) {
|
|
34
|
-
inputFolder = path.resolve(inputFolder, 'package.json');
|
|
35
|
-
}
|
|
36
|
-
return JSON.parse(await fs.readFile(inputFolder, 'utf8'));
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Get workspace root
|
|
41
|
-
* @return {Promise<string>}
|
|
42
|
-
*/
|
|
43
|
-
async function $getWorkspaceRoot() {
|
|
44
|
-
const path = await $getPath();
|
|
45
|
-
const fs = await $getFs();
|
|
46
|
-
let folder = process.cwd();
|
|
47
|
-
let prevFolder = '';
|
|
48
|
-
while (folder !== prevFolder) {
|
|
49
|
-
try {
|
|
50
|
-
const pkg = await $getPkg(folder);
|
|
51
|
-
if (!!pkg.workspaces || !!pkg.travetto?.isolated) {
|
|
52
|
-
return folder;
|
|
53
|
-
}
|
|
54
|
-
} catch { }
|
|
55
|
-
if (await fs.stat(path.resolve(folder, '.git')).catch(() => { })) {
|
|
56
|
-
break;
|
|
57
|
-
}
|
|
58
|
-
prevFolder = folder;
|
|
59
|
-
folder = path.dirname(folder);
|
|
60
|
-
}
|
|
61
|
-
return process.cwd();
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Returns the compiler options
|
|
66
|
-
* @param {ManifestContext} ctx
|
|
67
|
-
* @returns
|
|
68
|
-
*/
|
|
69
|
-
async function $getOpts(ctx) {
|
|
70
|
-
if (!(ctx.workspacePath in _opts)) {
|
|
71
|
-
const path = await $getPath();
|
|
72
|
-
const fs = await $getFs();
|
|
73
|
-
const ts = await $getTs();
|
|
74
|
-
const mod = await $getModule();
|
|
75
|
-
const req = mod.createRequire(`${ctx.workspacePath}/node_modules`);
|
|
76
|
-
|
|
77
|
-
const framework = req.resolve('@travetto/compiler/tsconfig.trv.json');
|
|
78
|
-
const self = path.resolve(ctx.workspacePath, 'tsconfig.json');
|
|
79
|
-
const loc = (await fs.stat(self).catch(() => false)) ? self : framework;
|
|
80
|
-
const { options } = ts.parseJsonSourceFileConfigFileContent(
|
|
81
|
-
ts.readJsonConfigFile(loc, ts.sys.readFile), ts.sys, ctx.workspacePath
|
|
82
|
-
);
|
|
83
|
-
options.inlineSourceMap = true;
|
|
84
|
-
options.sourceMap = false;
|
|
85
|
-
try {
|
|
86
|
-
const { type } = await $getPkg(ctx.workspacePath);
|
|
87
|
-
if (type) {
|
|
88
|
-
options.module = type.toLowerCase() === 'commonjs' ? ts.ModuleKind.CommonJS : ts.ModuleKind.ESNext;
|
|
89
|
-
}
|
|
90
|
-
} catch { }
|
|
91
|
-
|
|
92
|
-
_opts[ctx.workspacePath] = options;
|
|
93
|
-
}
|
|
94
|
-
return _opts[ctx.workspacePath];
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Writes a package json file
|
|
99
|
-
* @param {ManifestContext} ctx
|
|
100
|
-
* @param {string} inputFile
|
|
101
|
-
* @param {string} outputFile
|
|
102
|
-
* @param {(pkg:Pkg) => Pkg} transform
|
|
103
|
-
*/
|
|
104
|
-
async function writePackageJson(ctx, inputFile, outputFile, transform) {
|
|
105
|
-
const opts = await $getOpts(ctx);
|
|
106
|
-
const ts = await $getTs();
|
|
107
|
-
const isEsm = opts.module !== ts.ModuleKind.CommonJS;
|
|
108
|
-
let pkg = await $getPkg(inputFile);
|
|
109
|
-
pkg = transform?.(pkg) ?? pkg;
|
|
110
|
-
pkg.main = pkg.main?.replace(/[.]ts$/, '.js');
|
|
111
|
-
pkg.type = isEsm ? 'module' : 'commonjs';
|
|
112
|
-
pkg.files = pkg.files?.map(x => x.replace('.ts', '.js'));
|
|
113
|
-
|
|
114
|
-
ts.sys.writeFile(outputFile, JSON.stringify(pkg, null, 2));
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Transpiles a file
|
|
119
|
-
* @param {ManifestContext} ctx
|
|
120
|
-
* @param {string} inputFile
|
|
121
|
-
* @param {string} outputFile
|
|
122
|
-
*/
|
|
123
|
-
async function transpileFile(ctx, inputFile, outputFile) {
|
|
124
|
-
const ts = await $getTs();
|
|
125
|
-
const fs = await $getFs();
|
|
126
|
-
|
|
127
|
-
const opts = await $getOpts(ctx);
|
|
128
|
-
const content = ts.transpile(await fs.readFile(inputFile, 'utf8'), opts, inputFile)
|
|
129
|
-
.replace(/^((?:im|ex)port .*from '[.][^']+)(')/mg, (_, a, b) => `${a}.js${b}`)
|
|
130
|
-
.replace(/^(import [^\n]*from '[^.][^\n/]+[/][^\n/]+[/][^\n']+)(')/mg, (_, a, b) => `${a}.js${b}`);
|
|
131
|
-
|
|
132
|
-
ts.sys.writeFile(outputFile, content);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Writes a js file
|
|
137
|
-
* @param {ManifestContext} ctx
|
|
138
|
-
* @param {string} inputFile
|
|
139
|
-
* @param {string} outputFile
|
|
140
|
-
*/
|
|
141
|
-
async function writeJsFile(ctx, inputFile, outputFile) {
|
|
142
|
-
const ts = await $getTs();
|
|
143
|
-
const fs = await $getFs();
|
|
144
|
-
|
|
145
|
-
const opts = await $getOpts(ctx);
|
|
146
|
-
const isEsm = opts.module !== ts.ModuleKind.CommonJS;
|
|
147
|
-
|
|
148
|
-
let content = await fs.readFile(inputFile, 'utf8');
|
|
149
|
-
if (isEsm) {
|
|
150
|
-
content = content
|
|
151
|
-
.replace(/^(?:async )?function [^$]/mg, a => `export ${a}`)
|
|
152
|
-
.replace(/^module.exports.*/mg, '');
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
await fs.writeFile(outputFile, content, 'utf8');
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Write an entire package
|
|
160
|
-
* @param {ManifestContext} ctx
|
|
161
|
-
* @param {string} name
|
|
162
|
-
* @param {string} sourcePath
|
|
163
|
-
* @param {string} mainSource
|
|
164
|
-
* @param {string[]} extraSources
|
|
165
|
-
*/
|
|
166
|
-
async function buildPackage(ctx, name, sourcePath, mainSource, extraSources) {
|
|
167
|
-
const path = await $getPath();
|
|
168
|
-
const fs = await $getFs();
|
|
169
|
-
|
|
170
|
-
const files = [mainSource, ...extraSources].map(x => ({ src: x, out: x.replace(/[.]ts$/, '.js') }));
|
|
171
|
-
const main = files[0].out;
|
|
172
|
-
const outputPath = path.resolve(ctx.workspacePath, ctx.compilerFolder, 'node_modules', name);
|
|
173
|
-
|
|
174
|
-
for (const { src, out } of files) {
|
|
175
|
-
const inputFile = path.resolve(sourcePath, src);
|
|
176
|
-
const outputFile = path.resolve(outputPath, out);
|
|
177
|
-
|
|
178
|
-
const [outStat, inStat] = await Promise.all([
|
|
179
|
-
fs.stat(outputFile).catch(() => undefined),
|
|
180
|
-
fs.stat(inputFile)
|
|
181
|
-
]);
|
|
182
|
-
|
|
183
|
-
if (!outStat || (outStat.mtimeMs < inStat.mtimeMs)) {
|
|
184
|
-
await fs.mkdir(path.dirname(outputFile), { recursive: true });
|
|
185
|
-
|
|
186
|
-
if (inputFile.endsWith('.ts')) {
|
|
187
|
-
await transpileFile(ctx, inputFile, outputFile);
|
|
188
|
-
} else if (inputFile.endsWith('.js')) {
|
|
189
|
-
await writeJsFile(ctx, inputFile, outputFile);
|
|
190
|
-
} else if (inputFile.endsWith('.json')) {
|
|
191
|
-
await writePackageJson(ctx, inputFile, outputFile,
|
|
192
|
-
(pkg) => ({ ...pkg, files: files.map(x => x.out), name, main }));
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
return path.resolve(outputPath, main);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
* Gets build context
|
|
202
|
-
* @return {Promise<ManifestContext>}
|
|
203
|
-
*/
|
|
204
|
-
async function getContext(folder = process.cwd()) {
|
|
205
|
-
const path = await $getPath();
|
|
206
|
-
|
|
207
|
-
const workspacePath = path.resolve(await $getWorkspaceRoot());
|
|
208
|
-
const mainPath = toPosix(folder);
|
|
209
|
-
|
|
210
|
-
const { name: mainModule, workspaces, travetto } = (await $getPkg(mainPath));
|
|
211
|
-
const monoRepo = workspacePath !== mainPath || !!workspaces;
|
|
212
|
-
|
|
213
|
-
// All relative to workspacePath
|
|
214
|
-
const manifestFile = `node_modules/${mainModule}/manifest.json`;
|
|
215
|
-
|
|
216
|
-
return {
|
|
217
|
-
mainModule,
|
|
218
|
-
mainPath,
|
|
219
|
-
workspacePath,
|
|
220
|
-
monoRepo,
|
|
221
|
-
manifestFile,
|
|
222
|
-
outputFolder: travetto?.outputFolder ?? '.trv_output',
|
|
223
|
-
compilerFolder: '.trv_compiler'
|
|
224
|
-
};
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
module.exports = { transpileFile, writePackageJson, writeJsFile, buildPackage, getContext };
|
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
import path from 'path';
|
|
2
|
-
import fs from 'fs/promises';
|
|
3
|
-
import cp from 'child_process';
|
|
4
|
-
|
|
5
|
-
import type { ManifestState, ManifestContext, ManifestRoot } from '@travetto/manifest';
|
|
6
|
-
|
|
7
|
-
import { log, compileIfStale, getProjectSources, addNodePath, importManifest } from './utils';
|
|
8
|
-
|
|
9
|
-
const PRECOMPILE_MODS = [
|
|
10
|
-
'@travetto/terminal',
|
|
11
|
-
'@travetto/manifest',
|
|
12
|
-
'@travetto/transformer',
|
|
13
|
-
'@travetto/compiler'
|
|
14
|
-
];
|
|
15
|
-
|
|
16
|
-
let manifestTemp;
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Step 0
|
|
20
|
-
*/
|
|
21
|
-
export async function precompile(ctx: ManifestContext): Promise<void> {
|
|
22
|
-
for (const mod of PRECOMPILE_MODS) {
|
|
23
|
-
await compileIfStale(ctx, `[0] Compiling ${mod}`, await getProjectSources(ctx, mod),);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export async function writeManifest(ctx: ManifestContext, manifest: ManifestRoot): Promise<void> {
|
|
28
|
-
const { ManifestUtil } = await importManifest(ctx);
|
|
29
|
-
return ManifestUtil.writeManifest(ctx, manifest);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
async function rewriteManifests(ctx: ManifestContext, state: ManifestState): Promise<void> {
|
|
33
|
-
const { ManifestUtil } = await importManifest(ctx);
|
|
34
|
-
|
|
35
|
-
// Write out all changed manifests
|
|
36
|
-
const { getContext } = await import('../../bin/transpile');
|
|
37
|
-
|
|
38
|
-
const dirtyModules = [...Object.entries(state.delta)].filter(x => x[1].length > 0).map(([mod]) => mod);
|
|
39
|
-
for (const module of dirtyModules) {
|
|
40
|
-
const subCtx = await getContext(state.manifest.modules[module].source);
|
|
41
|
-
await ManifestUtil.createAndWriteManifest(subCtx);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Step 1
|
|
47
|
-
*/
|
|
48
|
-
export async function buildManifest(ctx: ManifestContext): Promise<ManifestState> {
|
|
49
|
-
log('[1] Manifest Generation');
|
|
50
|
-
const { ManifestUtil } = await importManifest(ctx);
|
|
51
|
-
return ManifestUtil.produceState(ctx);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function shouldRebuildCompiler({ delta }: ManifestState): { total: boolean, transformers: [string, string][] } {
|
|
55
|
-
// Did enough things change to re-stage and build the compiler
|
|
56
|
-
const transformersChanged = Object.entries(delta)
|
|
57
|
-
.flatMap(([mod, files]) => files.map(x => [mod, x.file]))
|
|
58
|
-
.filter((ev): ev is [string, string] => ev[1].startsWith('support/transform'));
|
|
59
|
-
const transformerChanged = (delta['@travetto/transformer'] ?? []);
|
|
60
|
-
const compilerChanged = delta['@travetto/compiler'] ?? [];
|
|
61
|
-
|
|
62
|
-
const changed = transformerChanged.length || transformersChanged.length || compilerChanged.length;
|
|
63
|
-
if (changed) {
|
|
64
|
-
if (compilerChanged.length) {
|
|
65
|
-
log('[2] Compiler source changed @travetto/compiler', compilerChanged);
|
|
66
|
-
}
|
|
67
|
-
if (transformerChanged.length) {
|
|
68
|
-
log('[2] Compiler source changed @travetto/transformer', transformerChanged);
|
|
69
|
-
}
|
|
70
|
-
if (transformersChanged.length) {
|
|
71
|
-
log('[2] Compiler source changed */support/transform', transformersChanged);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
return { total: changed > 0, transformers: transformersChanged };
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Step 2
|
|
79
|
-
*/
|
|
80
|
-
async function buildCompiler(state: ManifestState, ctx: ManifestContext): Promise<void> {
|
|
81
|
-
const changed = shouldRebuildCompiler(state);
|
|
82
|
-
|
|
83
|
-
if (changed.transformers.length) {
|
|
84
|
-
state = await buildManifest(ctx);
|
|
85
|
-
let x = 0;
|
|
86
|
-
for (const [mod, file] of changed.transformers) {
|
|
87
|
-
await compileIfStale(
|
|
88
|
-
ctx,
|
|
89
|
-
`[2.${x += 1}] ${file} Bootstrapping`,
|
|
90
|
-
await getProjectSources(ctx, mod, ['package.json', file])
|
|
91
|
-
);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
log('[2] Compiler Ready');
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Step 4
|
|
100
|
-
*/
|
|
101
|
-
async function compileOutput(state: ManifestState, ctx: ManifestContext, watch?: boolean): Promise<void> {
|
|
102
|
-
let changes = Object.values(state.delta).flat();
|
|
103
|
-
|
|
104
|
-
// Remove files that should go away
|
|
105
|
-
await Promise.all(changes.filter(x => x.type === 'removed')
|
|
106
|
-
.map(x => fs.unlink(path.resolve(ctx.workspacePath, ctx.outputFolder, x.file)).catch(() => { })));
|
|
107
|
-
|
|
108
|
-
changes = changes.filter(x => x.type !== 'removed');
|
|
109
|
-
|
|
110
|
-
const { ManifestUtil } = await importManifest(ctx);
|
|
111
|
-
const resolve = ManifestUtil.resolveFile.bind(null, ctx, state.manifest, '@travetto/compiler');
|
|
112
|
-
|
|
113
|
-
manifestTemp ??= await ManifestUtil.writeState(state);
|
|
114
|
-
const cwd = path.resolve(ctx.workspacePath, ctx.compilerFolder);
|
|
115
|
-
|
|
116
|
-
if (changes.length) {
|
|
117
|
-
log('[3] Changed Sources', changes);
|
|
118
|
-
|
|
119
|
-
// Blocking call, compile only
|
|
120
|
-
const res = cp.spawnSync(process.argv0,
|
|
121
|
-
[resolve('support/main.output'), manifestTemp],
|
|
122
|
-
{ cwd, stdio: 'inherit', encoding: 'utf8' }
|
|
123
|
-
);
|
|
124
|
-
|
|
125
|
-
if (res.status) {
|
|
126
|
-
throw new Error(res.stderr);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
await rewriteManifests(ctx, state);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
if (watch) {
|
|
133
|
-
// Rewrite state with updated manifest
|
|
134
|
-
const newState = await ManifestUtil.produceState(ctx);
|
|
135
|
-
manifestTemp = await ManifestUtil.writeState(newState);
|
|
136
|
-
|
|
137
|
-
// Run with watching
|
|
138
|
-
cp.spawnSync(process.argv0,
|
|
139
|
-
[resolve('support/main.output'), manifestTemp, 'true'], { cwd, stdio: 'inherit' }
|
|
140
|
-
);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
export async function compile(ctx: ManifestContext, watch?: boolean): Promise<ManifestState> {
|
|
145
|
-
await precompile(ctx); // Step 0
|
|
146
|
-
const state = await buildManifest(ctx); // Step 1
|
|
147
|
-
await buildCompiler(state, ctx); // Step 2
|
|
148
|
-
await compileOutput(state, ctx, watch); // Step 3
|
|
149
|
-
await addNodePath(path.resolve(ctx.workspacePath, ctx.outputFolder));
|
|
150
|
-
return state;
|
|
151
|
-
}
|