@travetto/compiler 3.4.4 → 4.0.0-rc.1
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 +29 -27
- package/bin/common.js +51 -46
- package/bin/trvc.js +21 -14
- package/package.json +6 -7
- package/src/compiler.ts +96 -43
- package/src/event.ts +3 -3
- package/src/log.ts +1 -1
- package/src/state.ts +43 -25
- package/src/types.ts +1 -1
- package/src/util.ts +6 -6
- package/src/watch.ts +107 -117
- package/support/entry.trvc.ts +67 -37
- package/support/log.ts +55 -24
- package/support/queue.ts +11 -6
- package/support/server/client.ts +122 -81
- package/support/server/process-handle.ts +57 -0
- package/support/server/runner.ts +37 -64
- package/support/server/server.ts +86 -58
- package/support/setup.ts +38 -36
- package/support/types.ts +3 -4
- package/support/util.ts +20 -25
- package/src/internal/watch-core.ts +0 -104
package/support/server/server.ts
CHANGED
|
@@ -1,53 +1,47 @@
|
|
|
1
|
-
import http from 'http';
|
|
2
|
-
import fs from 'fs/promises';
|
|
3
|
-
import path from 'path';
|
|
1
|
+
import http from 'node:http';
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { setMaxListeners } from 'node:events';
|
|
4
5
|
|
|
5
6
|
import type { ManifestContext } from '@travetto/manifest';
|
|
6
7
|
|
|
7
|
-
import type { CompilerMode,
|
|
8
|
+
import type { CompilerMode, CompilerProgressEvent, CompilerEvent, CompilerEventType, CompilerServerInfo } from '../types';
|
|
8
9
|
import { LogUtil } from '../log';
|
|
9
|
-
import {
|
|
10
|
+
import { CompilerClient } from './client';
|
|
10
11
|
import { CommonUtil } from '../util';
|
|
12
|
+
import { ProcessHandle } from './process-handle';
|
|
11
13
|
|
|
12
|
-
const log = LogUtil.
|
|
14
|
+
const log = LogUtil.logger('compiler-server');
|
|
13
15
|
|
|
14
16
|
/**
|
|
15
17
|
* Compiler Server Class
|
|
16
18
|
*/
|
|
17
19
|
export class CompilerServer {
|
|
18
20
|
|
|
19
|
-
static readJSONRequest<T>(req: http.IncomingMessage): Promise<T> {
|
|
20
|
-
return new Promise<T>((res, rej) => {
|
|
21
|
-
const body: Buffer[] = [];
|
|
22
|
-
req.on('data', (chunk) => body.push(typeof chunk === 'string' ? Buffer.from(chunk) : chunk));
|
|
23
|
-
req.on('end', () => {
|
|
24
|
-
try {
|
|
25
|
-
res(JSON.parse(Buffer.concat(body).toString('utf8')));
|
|
26
|
-
} catch (err) {
|
|
27
|
-
rej(err);
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
req.on('error', rej);
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
|
|
34
21
|
#ctx: ManifestContext;
|
|
35
22
|
#server: http.Server;
|
|
36
|
-
#listeners: { res: http.ServerResponse, type:
|
|
23
|
+
#listeners: { res: http.ServerResponse, type: CompilerEventType }[] = [];
|
|
37
24
|
#shutdown = new AbortController();
|
|
38
25
|
signal = this.#shutdown.signal;
|
|
39
26
|
info: CompilerServerInfo;
|
|
27
|
+
#client: CompilerClient;
|
|
28
|
+
#url: string;
|
|
29
|
+
#handle: Record<'compiler' | 'server', ProcessHandle>;
|
|
40
30
|
|
|
41
|
-
constructor(ctx: ManifestContext,
|
|
31
|
+
constructor(ctx: ManifestContext, mode: CompilerMode) {
|
|
42
32
|
this.#ctx = ctx;
|
|
33
|
+
this.#client = new CompilerClient(ctx, LogUtil.logger('client.server'));
|
|
34
|
+
this.#url = this.#client.url;
|
|
35
|
+
this.#handle = { server: new ProcessHandle(ctx, 'server'), compiler: new ProcessHandle(ctx, 'compiler') };
|
|
36
|
+
|
|
43
37
|
this.info = {
|
|
44
38
|
state: 'startup',
|
|
45
39
|
iteration: Date.now(),
|
|
46
|
-
mode
|
|
40
|
+
mode,
|
|
47
41
|
serverPid: process.pid,
|
|
48
42
|
compilerPid: -1,
|
|
49
|
-
path: ctx.
|
|
50
|
-
url:
|
|
43
|
+
path: ctx.workspace.path,
|
|
44
|
+
url: this.#url
|
|
51
45
|
};
|
|
52
46
|
|
|
53
47
|
this.#server = http.createServer({
|
|
@@ -56,15 +50,14 @@ export class CompilerServer {
|
|
|
56
50
|
keepAliveTimeout: 1000 * 60 * 60,
|
|
57
51
|
}, (req, res) => this.#onRequest(req, res));
|
|
58
52
|
|
|
59
|
-
|
|
60
|
-
process.on('SIGINT', () => this.#shutdown.abort());
|
|
53
|
+
setMaxListeners(1000, this.signal);
|
|
61
54
|
}
|
|
62
55
|
|
|
63
56
|
get mode(): CompilerMode {
|
|
64
57
|
return this.info.mode;
|
|
65
58
|
}
|
|
66
59
|
|
|
67
|
-
isResetEvent(ev:
|
|
60
|
+
isResetEvent(ev: CompilerEvent): boolean {
|
|
68
61
|
return ev.type === 'state' && ev.payload.state === 'reset';
|
|
69
62
|
}
|
|
70
63
|
|
|
@@ -74,14 +67,16 @@ export class CompilerServer {
|
|
|
74
67
|
.on('listening', () => resolve('ok'))
|
|
75
68
|
.on('error', async err => {
|
|
76
69
|
if ('code' in err && err.code === 'EADDRINUSE') {
|
|
77
|
-
const info = await
|
|
70
|
+
const info = await this.#client.info();
|
|
78
71
|
resolve((info && info.mode === 'build' && this.mode === 'watch') ? 'retry' : 'running');
|
|
79
72
|
} else {
|
|
73
|
+
log('warn', 'Failed in running server', err);
|
|
80
74
|
reject(err);
|
|
81
75
|
}
|
|
82
|
-
})
|
|
76
|
+
})
|
|
77
|
+
.on('close', () => log('debug', 'Server close event'));
|
|
83
78
|
|
|
84
|
-
const url = new URL(this.#
|
|
79
|
+
const url = new URL(this.#url);
|
|
85
80
|
setTimeout(() => this.#server.listen(+url.port, url.hostname), 1); // Run async
|
|
86
81
|
});
|
|
87
82
|
|
|
@@ -91,8 +86,10 @@ export class CompilerServer {
|
|
|
91
86
|
}
|
|
92
87
|
log('info', 'Waiting for build to finish, before retrying');
|
|
93
88
|
// Let the server finish
|
|
94
|
-
await
|
|
89
|
+
await this.#client.waitForState(['close'], 'Server closed', this.signal);
|
|
95
90
|
return this.#tryListen(attempt + 1);
|
|
91
|
+
} else if (output === 'ok') {
|
|
92
|
+
await this.#handle.server.writePid(this.info.serverPid);
|
|
96
93
|
}
|
|
97
94
|
|
|
98
95
|
return output;
|
|
@@ -102,12 +99,16 @@ export class CompilerServer {
|
|
|
102
99
|
res.writeHead(200);
|
|
103
100
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
104
101
|
this.#listeners.push({ res, type: type as 'change' });
|
|
102
|
+
if (type === 'state') { // Send on initial connect
|
|
103
|
+
res.write(JSON.stringify({ state: this.info.state }));
|
|
104
|
+
}
|
|
105
|
+
res.write('\n'); // Send at least one byte on listen
|
|
105
106
|
await new Promise(resolve => res.on('close', resolve));
|
|
106
107
|
this.#listeners.splice(this.#listeners.findIndex(x => x.res === res), 1);
|
|
107
108
|
res.end();
|
|
108
109
|
}
|
|
109
110
|
|
|
110
|
-
#emitEvent(ev:
|
|
111
|
+
#emitEvent(ev: CompilerEvent): void {
|
|
111
112
|
const msg = `${JSON.stringify(ev.payload)}\n`;
|
|
112
113
|
for (const el of this.#listeners) {
|
|
113
114
|
if (!el.res.closed && el.type === ev.type) {
|
|
@@ -120,12 +121,14 @@ export class CompilerServer {
|
|
|
120
121
|
log('info', 'Server disconnect requested');
|
|
121
122
|
this.info.iteration = Date.now();
|
|
122
123
|
await new Promise(r => setTimeout(r, 20));
|
|
123
|
-
this.#
|
|
124
|
+
for (const el of this.#listeners) {
|
|
125
|
+
el.res.destroy();
|
|
126
|
+
}
|
|
124
127
|
}
|
|
125
128
|
|
|
126
129
|
async #clean(): Promise<{ clean: boolean }> {
|
|
127
|
-
await Promise.all([this.#ctx.compilerFolder, this.#ctx.outputFolder]
|
|
128
|
-
.map(f => fs.rm(path.resolve(this.#ctx.
|
|
130
|
+
await Promise.all([this.#ctx.build.compilerFolder, this.#ctx.build.outputFolder]
|
|
131
|
+
.map(f => fs.rm(path.resolve(this.#ctx.workspace.path, f), { recursive: true, force: true })));
|
|
129
132
|
return { clean: true };
|
|
130
133
|
}
|
|
131
134
|
|
|
@@ -135,16 +138,20 @@ export class CompilerServer {
|
|
|
135
138
|
async #onRequest(req: http.IncomingMessage, res: http.ServerResponse): Promise<void> {
|
|
136
139
|
res.setHeader('Content-Type', 'application/json');
|
|
137
140
|
|
|
138
|
-
const [, action, subAction] = new URL(`${this.#
|
|
141
|
+
const [, action, subAction] = new URL(`${this.#url}${req.url}`).pathname.split('/');
|
|
139
142
|
|
|
140
143
|
log('debug', 'Receive request', { action, subAction });
|
|
141
144
|
|
|
142
145
|
let out: unknown;
|
|
143
146
|
switch (action) {
|
|
144
|
-
case 'send-event': await this.#emitEvent(await CompilerServer.readJSONRequest(req)); out = { received: true }; break;
|
|
145
147
|
case 'event': return await this.#addListener(subAction, res);
|
|
146
|
-
case 'stop': out = await this.close(); break;
|
|
147
148
|
case 'clean': out = await this.#clean(); break;
|
|
149
|
+
case 'stop': {
|
|
150
|
+
// Must send immediately
|
|
151
|
+
res.end(JSON.stringify({ closing: true }));
|
|
152
|
+
await this.close();
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
148
155
|
case 'info':
|
|
149
156
|
default: out = this.info ?? {}; break;
|
|
150
157
|
}
|
|
@@ -154,19 +161,23 @@ export class CompilerServer {
|
|
|
154
161
|
/**
|
|
155
162
|
* Process events
|
|
156
163
|
*/
|
|
157
|
-
async processEvents(src: (signal: AbortSignal) => AsyncIterable<
|
|
158
|
-
|
|
159
|
-
CompilerClientUtil.streamLogs(this.#ctx, this.signal); // Send logs to stdout
|
|
160
|
-
|
|
164
|
+
async processEvents(src: (signal: AbortSignal) => AsyncIterable<CompilerEvent>): Promise<void> {
|
|
161
165
|
for await (const ev of CommonUtil.restartableEvents(src, this.signal, this.isResetEvent)) {
|
|
166
|
+
if (ev.type === 'progress') {
|
|
167
|
+
await LogUtil.logProgress?.(ev.payload);
|
|
168
|
+
}
|
|
169
|
+
|
|
162
170
|
this.#emitEvent(ev);
|
|
163
171
|
|
|
164
172
|
if (ev.type === 'state') {
|
|
165
173
|
this.info.state = ev.payload.state;
|
|
174
|
+
await this.#handle.compiler.writePid(this.info.compilerPid);
|
|
166
175
|
if (ev.payload.state === 'init' && ev.payload.extra && 'pid' in ev.payload.extra && typeof ev.payload.extra.pid === 'number') {
|
|
167
176
|
this.info.compilerPid = ev.payload.extra.pid;
|
|
168
177
|
}
|
|
169
178
|
log('info', `State changed: ${this.info.state}`);
|
|
179
|
+
} else if (ev.type === 'log') {
|
|
180
|
+
LogUtil.logEvent(ev.payload);
|
|
170
181
|
}
|
|
171
182
|
if (this.isResetEvent(ev)) {
|
|
172
183
|
await this.#disconnectActive();
|
|
@@ -175,27 +186,44 @@ export class CompilerServer {
|
|
|
175
186
|
|
|
176
187
|
// Terminate, after letting all remaining events emit
|
|
177
188
|
await this.close();
|
|
189
|
+
|
|
190
|
+
log('debug', 'Finished processing events');
|
|
178
191
|
}
|
|
179
192
|
|
|
180
193
|
/**
|
|
181
194
|
* Close server
|
|
182
195
|
*/
|
|
183
|
-
async close(): Promise<
|
|
184
|
-
|
|
185
|
-
|
|
196
|
+
async close(): Promise<void> {
|
|
197
|
+
log('info', 'Closing down server');
|
|
198
|
+
|
|
199
|
+
// If we are in a place where progress exists
|
|
200
|
+
if (this.info.state === 'compile-start') {
|
|
201
|
+
const cancel: CompilerProgressEvent = { complete: true, idx: 0, total: 0, message: 'Complete', operation: 'compile' };
|
|
202
|
+
LogUtil.logProgress?.(cancel);
|
|
203
|
+
this.#emitEvent({ type: 'progress', payload: cancel });
|
|
186
204
|
}
|
|
187
205
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
206
|
+
try {
|
|
207
|
+
await new Promise((resolve, reject) => {
|
|
208
|
+
setTimeout(reject, 2000).unref(); // 2s max wait
|
|
209
|
+
this.#server.close(resolve);
|
|
210
|
+
this.#emitEvent({ type: 'state', payload: { state: 'close' } });
|
|
211
|
+
setImmediate(() => {
|
|
212
|
+
this.#server.closeAllConnections();
|
|
213
|
+
this.#shutdown.abort();
|
|
214
|
+
});
|
|
196
215
|
});
|
|
197
|
-
}
|
|
198
|
-
|
|
216
|
+
} catch { // Timeout or other error
|
|
217
|
+
// Force shutdown
|
|
218
|
+
this.#server.closeAllConnections();
|
|
219
|
+
if (this.info.compilerPid) { // Ensure its killed
|
|
220
|
+
try {
|
|
221
|
+
process.kill(this.info.compilerPid);
|
|
222
|
+
} catch { }
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
log('info', 'Closed down server');
|
|
199
227
|
}
|
|
200
228
|
|
|
201
229
|
/**
|
|
@@ -203,7 +231,7 @@ export class CompilerServer {
|
|
|
203
231
|
*/
|
|
204
232
|
async listen(): Promise<CompilerServer | undefined> {
|
|
205
233
|
const running = await this.#tryListen() === 'ok';
|
|
206
|
-
log('info', running ? 'Starting server' : 'Server already running under a different process', this.#
|
|
234
|
+
log('info', running ? 'Starting server' : 'Server already running under a different process', this.#url);
|
|
207
235
|
return running ? this : undefined;
|
|
208
236
|
}
|
|
209
237
|
}
|
package/support/setup.ts
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
|
-
import path from 'path';
|
|
2
|
-
import
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import fs from 'node:fs/promises';
|
|
3
4
|
|
|
4
|
-
import { type DeltaEvent, type ManifestContext, type
|
|
5
|
+
import { type DeltaEvent, type ManifestContext, type Package } from '@travetto/manifest';
|
|
5
6
|
|
|
6
7
|
import { LogUtil } from './log';
|
|
7
8
|
import { CommonUtil } from './util';
|
|
8
9
|
|
|
9
10
|
type ModFile = { input: string, output: string, stale: boolean };
|
|
10
11
|
|
|
11
|
-
const SOURCE_SEED = ['package.json', '
|
|
12
|
-
const PRECOMPILE_MODS = ['@travetto/
|
|
12
|
+
const SOURCE_SEED = ['package.json', '__index__.ts', 'src', 'support', 'bin'];
|
|
13
|
+
const PRECOMPILE_MODS = ['@travetto/manifest', '@travetto/transformer', '@travetto/compiler'];
|
|
13
14
|
const RECENT_STAT = (stat: { ctimeMs: number, mtimeMs: number }): number => Math.max(stat.ctimeMs, stat.mtimeMs);
|
|
15
|
+
const REQ = createRequire(path.resolve('node_modules')).resolve.bind(null);
|
|
14
16
|
|
|
15
17
|
/**
|
|
16
18
|
* Compiler Setup Utilities
|
|
@@ -18,10 +20,16 @@ const RECENT_STAT = (stat: { ctimeMs: number, mtimeMs: number }): number => Math
|
|
|
18
20
|
export class CompilerSetup {
|
|
19
21
|
|
|
20
22
|
/**
|
|
21
|
-
* Import
|
|
23
|
+
* Import compiled manifest utilities
|
|
22
24
|
*/
|
|
23
|
-
static #importManifest = (ctx: ManifestContext): Promise<
|
|
24
|
-
import(
|
|
25
|
+
static #importManifest = (ctx: ManifestContext): Promise<
|
|
26
|
+
Pick<typeof import('@travetto/manifest'), 'ManifestDeltaUtil' | 'ManifestUtil'>
|
|
27
|
+
> => {
|
|
28
|
+
const all = ['util', 'delta'].map(f =>
|
|
29
|
+
import(path.resolve(ctx.workspace.path, ctx.build.compilerFolder, 'node_modules', `@travetto/manifest/src/${f}.js`))
|
|
30
|
+
);
|
|
31
|
+
return Promise.all(all).then(props => Object.assign({}, ...props));
|
|
32
|
+
};
|
|
25
33
|
|
|
26
34
|
/** Convert a file to a given ext */
|
|
27
35
|
static #sourceToExtension(inputFile: string, ext: string): string {
|
|
@@ -41,7 +49,7 @@ export class CompilerSetup {
|
|
|
41
49
|
static async #transpileFile(ctx: ManifestContext, inputFile: string, outputFile: string): Promise<void> {
|
|
42
50
|
const type = CommonUtil.getFileType(inputFile);
|
|
43
51
|
if (type === 'js' || type === 'ts') {
|
|
44
|
-
const compilerOut = path.resolve(ctx.
|
|
52
|
+
const compilerOut = path.resolve(ctx.workspace.path, ctx.build.compilerFolder, 'node_modules');
|
|
45
53
|
|
|
46
54
|
const text = (await fs.readFile(inputFile, 'utf8'))
|
|
47
55
|
.replace(/from '([.][^']+)'/g, (_, i) => `from '${i.replace(/[.]js$/, '')}.js'`)
|
|
@@ -59,7 +67,7 @@ export class CompilerSetup {
|
|
|
59
67
|
const main = pkg.main ? this.#sourceToOutputExt(pkg.main) : undefined;
|
|
60
68
|
const files = pkg.files?.map(x => this.#sourceToOutputExt(x));
|
|
61
69
|
|
|
62
|
-
const content = JSON.stringify({ ...pkg, main, type: ctx.
|
|
70
|
+
const content = JSON.stringify({ ...pkg, main, type: ctx.workspace.type, files }, null, 2);
|
|
63
71
|
await CommonUtil.writeTextFile(outputFile, content);
|
|
64
72
|
}
|
|
65
73
|
}
|
|
@@ -68,9 +76,7 @@ export class CompilerSetup {
|
|
|
68
76
|
* Scan directory to find all project sources for comparison
|
|
69
77
|
*/
|
|
70
78
|
static async #getModuleSources(ctx: ManifestContext, module: string, seed: string[]): Promise<ModFile[]> {
|
|
71
|
-
const inputFolder = (
|
|
72
|
-
process.cwd() :
|
|
73
|
-
CommonUtil.resolveModuleFolder(module);
|
|
79
|
+
const inputFolder = path.dirname(REQ(`${module}/package.json`));
|
|
74
80
|
|
|
75
81
|
const folders = seed.filter(x => !/[.]/.test(x)).map(x => path.resolve(inputFolder, x));
|
|
76
82
|
const files = seed.filter(x => /[.]/.test(x)).map(x => path.resolve(inputFolder, x));
|
|
@@ -98,7 +104,7 @@ export class CompilerSetup {
|
|
|
98
104
|
}
|
|
99
105
|
}
|
|
100
106
|
|
|
101
|
-
const outputFolder = path.resolve(ctx.
|
|
107
|
+
const outputFolder = path.resolve(ctx.workspace.path, ctx.build.compilerFolder, 'node_modules', module);
|
|
102
108
|
const out: ModFile[] = [];
|
|
103
109
|
for (const input of files) {
|
|
104
110
|
const output = this.#sourceToOutputExt(input.replace(inputFolder, outputFolder));
|
|
@@ -162,25 +168,21 @@ export class CompilerSetup {
|
|
|
162
168
|
}
|
|
163
169
|
|
|
164
170
|
/**
|
|
165
|
-
* Sets up compiler, and produces a
|
|
171
|
+
* Sets up compiler, and produces a set of changes that need to be processed
|
|
166
172
|
*/
|
|
167
|
-
static async setup(ctx: ManifestContext): Promise<
|
|
173
|
+
static async setup(ctx: ManifestContext): Promise<DeltaEvent[]> {
|
|
168
174
|
let changes = 0;
|
|
169
175
|
|
|
170
176
|
await LogUtil.withLogger('precompile', async () => {
|
|
171
177
|
for (const mod of PRECOMPILE_MODS) {
|
|
172
|
-
|
|
173
|
-
if (mod !== '@travetto/terminal') {
|
|
174
|
-
changes += count;
|
|
175
|
-
}
|
|
178
|
+
changes += (await this.#compileIfStale(ctx, 'precompile', mod, SOURCE_SEED)).length;
|
|
176
179
|
}
|
|
177
180
|
});
|
|
178
181
|
|
|
179
|
-
const { ManifestUtil, ManifestDeltaUtil
|
|
180
|
-
|
|
181
|
-
PackageUtil.clearCache();
|
|
182
|
+
const { ManifestUtil, ManifestDeltaUtil } = await this.#importManifest(ctx);
|
|
182
183
|
|
|
183
|
-
const manifest = await LogUtil.withLogger('manifest', () =>
|
|
184
|
+
const manifest = await LogUtil.withLogger('manifest', () =>
|
|
185
|
+
ManifestUtil.buildManifest(ManifestUtil.getWorkspaceContext(ctx)));
|
|
184
186
|
|
|
185
187
|
await LogUtil.withLogger('transformers', async () => {
|
|
186
188
|
for (const mod of Object.values(manifest.modules).filter(m => m.files.$transformer?.length)) {
|
|
@@ -191,30 +193,32 @@ export class CompilerSetup {
|
|
|
191
193
|
const delta = await LogUtil.withLogger('delta', async log => {
|
|
192
194
|
if (changes) {
|
|
193
195
|
log('debug', 'Skipping, everything changed');
|
|
194
|
-
return [{ type: 'changed', file: '*', module: ctx.
|
|
196
|
+
return [{ type: 'changed', file: '*', module: ctx.workspace.name, sourceFile: '' } as const];
|
|
195
197
|
} else {
|
|
196
|
-
return ManifestDeltaUtil.produceDelta(
|
|
198
|
+
return ManifestDeltaUtil.produceDelta(manifest);
|
|
197
199
|
}
|
|
198
200
|
});
|
|
199
201
|
|
|
200
202
|
if (changes) {
|
|
201
203
|
await LogUtil.withLogger('reset', async log => {
|
|
202
|
-
await fs.rm(path.resolve(ctx.
|
|
204
|
+
await fs.rm(path.resolve(ctx.workspace.path, ctx.build.outputFolder), { recursive: true, force: true });
|
|
203
205
|
log('info', 'Clearing output due to compiler changes');
|
|
204
206
|
}, false);
|
|
205
207
|
}
|
|
206
208
|
|
|
207
209
|
// Write manifest
|
|
208
210
|
await LogUtil.withLogger('manifest', async log => {
|
|
209
|
-
await ManifestUtil.writeManifest(
|
|
210
|
-
log('debug', `Wrote manifest ${ctx.
|
|
211
|
+
await ManifestUtil.writeManifest(manifest);
|
|
212
|
+
log('debug', `Wrote manifest ${ctx.workspace.name}`);
|
|
211
213
|
|
|
212
|
-
// Update all manifests
|
|
213
|
-
if (delta.length && ctx.
|
|
214
|
+
// Update all manifests when in mono repo
|
|
215
|
+
if (delta.length && ctx.workspace.mono) {
|
|
214
216
|
const names: string[] = [];
|
|
215
|
-
const mods = Object.values(manifest.modules).filter(x => x.
|
|
217
|
+
const mods = Object.values(manifest.modules).filter(x => x.workspace && x.name !== ctx.workspace.name);
|
|
216
218
|
for (const mod of mods) {
|
|
217
|
-
|
|
219
|
+
const modCtx = ManifestUtil.getModuleContext(ctx, mod.sourceFolder);
|
|
220
|
+
const modManifest = await ManifestUtil.buildManifest(modCtx);
|
|
221
|
+
await ManifestUtil.writeManifest(modManifest);
|
|
218
222
|
names.push(mod.name);
|
|
219
223
|
}
|
|
220
224
|
log('debug', `Changes triggered ${delta.slice(0, 10).map(x => `${x.type}:${x.module}:${x.file}`)}`);
|
|
@@ -222,8 +226,6 @@ export class CompilerSetup {
|
|
|
222
226
|
}
|
|
223
227
|
});
|
|
224
228
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
return { manifest, changed };
|
|
229
|
+
return delta.filter(x => x.type === 'added' || x.type === 'changed');
|
|
228
230
|
}
|
|
229
231
|
}
|
package/support/types.ts
CHANGED
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
export type CompilerMode = 'build' | 'watch';
|
|
2
|
-
export type CompilerOp = CompilerMode | 'run';
|
|
3
2
|
|
|
4
3
|
export type CompilerStateType = 'startup' | 'init' | 'compile-start' | 'compile-end' | 'watch-start' | 'watch-end' | 'reset' | 'close';
|
|
5
|
-
export type CompilerChangeEvent = { file: string, action: 'create' | 'update' | 'delete',
|
|
4
|
+
export type CompilerChangeEvent = { file: string, action: 'create' | 'update' | 'delete', output: string, module: string, time: number };
|
|
6
5
|
export type CompilerLogLevel = 'info' | 'debug' | 'warn' | 'error';
|
|
7
6
|
export type CompilerLogEvent = { level: CompilerLogLevel, message: string, time: number, args?: unknown[], scope?: string };
|
|
8
7
|
export type CompilerProgressEvent = { idx: number, total: number, message: string, operation: 'compile', complete?: boolean };
|
|
9
8
|
export type CompilerStateEvent = { state: CompilerStateType, extra?: Record<string, unknown> };
|
|
10
9
|
|
|
11
|
-
export type
|
|
10
|
+
export type CompilerEvent =
|
|
12
11
|
{ type: 'change', payload: CompilerChangeEvent } |
|
|
13
12
|
{ type: 'log', payload: CompilerLogEvent } |
|
|
14
13
|
{ type: 'progress', payload: CompilerProgressEvent } |
|
|
15
14
|
{ type: 'state', payload: CompilerStateEvent };
|
|
16
15
|
|
|
17
|
-
export type
|
|
16
|
+
export type CompilerEventType = CompilerEvent['type'];
|
|
18
17
|
|
|
19
18
|
export type CompilerServerInfo = {
|
|
20
19
|
path: string;
|
package/support/util.ts
CHANGED
|
@@ -1,50 +1,42 @@
|
|
|
1
|
-
import fs from 'fs/promises';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import {
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { setMaxListeners } from 'node:events';
|
|
4
4
|
|
|
5
5
|
import type { ManifestContext } from '@travetto/manifest';
|
|
6
6
|
|
|
7
7
|
import { LogUtil } from './log';
|
|
8
8
|
|
|
9
9
|
const OPT_CACHE: Record<string, import('typescript').CompilerOptions> = {};
|
|
10
|
-
const SRC_REQ = createRequire(path.resolve('node_modules'));
|
|
11
10
|
|
|
12
11
|
export class CommonUtil {
|
|
13
12
|
/**
|
|
14
13
|
* Returns the compiler options
|
|
15
14
|
*/
|
|
16
15
|
static async getCompilerOptions(ctx: ManifestContext): Promise<{}> {
|
|
17
|
-
if (!(ctx.
|
|
18
|
-
let tsconfig = path.resolve(ctx.
|
|
16
|
+
if (!(ctx.workspace.path in OPT_CACHE)) {
|
|
17
|
+
let tsconfig = path.resolve(ctx.workspace.path, 'tsconfig.json');
|
|
19
18
|
|
|
20
19
|
if (!await fs.stat(tsconfig).then(_ => true, _ => false)) {
|
|
21
|
-
tsconfig =
|
|
20
|
+
tsconfig = path.resolve(ctx.workspace.path, ctx.build.compilerModuleFolder, 'tsconfig.trv.json');
|
|
22
21
|
}
|
|
23
22
|
|
|
24
23
|
const ts = (await import('typescript')).default;
|
|
25
24
|
|
|
26
25
|
const { options } = ts.parseJsonSourceFileConfigFileContent(
|
|
27
|
-
ts.readJsonConfigFile(tsconfig, ts.sys.readFile), ts.sys, ctx.
|
|
26
|
+
ts.readJsonConfigFile(tsconfig, ts.sys.readFile), ts.sys, ctx.workspace.path
|
|
28
27
|
);
|
|
29
28
|
|
|
30
|
-
OPT_CACHE[ctx.
|
|
29
|
+
OPT_CACHE[ctx.workspace.path] = {
|
|
31
30
|
...options,
|
|
32
31
|
allowJs: true,
|
|
33
32
|
resolveJsonModule: true,
|
|
34
|
-
sourceRoot: ctx.
|
|
35
|
-
rootDir: ctx.
|
|
36
|
-
outDir: path.resolve(ctx.
|
|
37
|
-
module: ctx.
|
|
33
|
+
sourceRoot: ctx.workspace.path,
|
|
34
|
+
rootDir: ctx.workspace.path,
|
|
35
|
+
outDir: path.resolve(ctx.workspace.path),
|
|
36
|
+
module: ctx.workspace.type === 'commonjs' ? ts.ModuleKind.CommonJS : ts.ModuleKind.ESNext,
|
|
38
37
|
};
|
|
39
38
|
}
|
|
40
|
-
return OPT_CACHE[ctx.
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Resolve module location
|
|
45
|
-
*/
|
|
46
|
-
static resolveModuleFolder(mod: string): string {
|
|
47
|
-
return path.dirname(SRC_REQ.resolve(`${mod}/package.json`));
|
|
39
|
+
return OPT_CACHE[ctx.workspace.path];
|
|
48
40
|
}
|
|
49
41
|
|
|
50
42
|
/**
|
|
@@ -66,25 +58,28 @@ export class CommonUtil {
|
|
|
66
58
|
* Restartable Event Stream
|
|
67
59
|
*/
|
|
68
60
|
static async * restartableEvents<T>(src: (signal: AbortSignal) => AsyncIterable<T>, parent: AbortSignal, shouldRestart: (item: T) => boolean): AsyncIterable<T> {
|
|
61
|
+
const log = LogUtil.logger('event-stream');
|
|
69
62
|
outer: while (!parent.aborted) {
|
|
70
63
|
const controller = new AbortController();
|
|
64
|
+
setMaxListeners(1000, controller.signal);
|
|
71
65
|
// Chain
|
|
72
66
|
parent.addEventListener('abort', () => controller.abort());
|
|
73
67
|
|
|
74
68
|
const comp = src(controller.signal);
|
|
75
69
|
|
|
76
|
-
|
|
70
|
+
log('debug', 'Started event stream');
|
|
77
71
|
|
|
78
72
|
// Wait for all events, close at the end
|
|
79
73
|
for await (const ev of comp) {
|
|
80
74
|
yield ev;
|
|
81
75
|
if (shouldRestart(ev)) {
|
|
76
|
+
log('debug', 'Restarting stream');
|
|
82
77
|
controller.abort(); // Ensure terminated of process
|
|
83
78
|
continue outer;
|
|
84
79
|
}
|
|
85
80
|
}
|
|
86
81
|
|
|
87
|
-
|
|
82
|
+
log('debug', 'Finished event stream');
|
|
88
83
|
|
|
89
84
|
// Natural exit, we done
|
|
90
85
|
if (!controller.signal.aborted) { // Shutdown source if still running
|
|
@@ -100,8 +95,8 @@ export class CommonUtil {
|
|
|
100
95
|
*/
|
|
101
96
|
static moduleLoader(ctx: ManifestContext): (mod: string) => Promise<unknown> {
|
|
102
97
|
return (mod) => {
|
|
103
|
-
const outputRoot = path.resolve(ctx.
|
|
104
|
-
process.env.TRV_MANIFEST = path.resolve(outputRoot, 'node_modules', ctx.
|
|
98
|
+
const outputRoot = path.resolve(ctx.workspace.path, ctx.build.outputFolder);
|
|
99
|
+
process.env.TRV_MANIFEST = path.resolve(outputRoot, 'node_modules', ctx.main.name); // Setup for running
|
|
105
100
|
return import(path.join(outputRoot, 'node_modules', mod)); // Return function to run import on a module
|
|
106
101
|
};
|
|
107
102
|
}
|