@travetto/compiler 5.0.10 → 5.0.12
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/bin/common.js +2 -1
- package/bin/manifest-context.mjs +32 -97
- package/package.json +4 -4
- package/src/compiler.ts +4 -4
- package/src/types.ts +2 -0
- package/src/watch.ts +175 -201
package/bin/common.js
CHANGED
|
@@ -57,10 +57,11 @@ async function getEntry() {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
// Load module on demand
|
|
60
|
+
/** @type {import('@travetto/manifest/src/context')} */
|
|
60
61
|
const { getManifestContext } = await import(manifestJs);
|
|
61
62
|
|
|
62
63
|
/** @type {Ctx} */
|
|
63
|
-
const ctx = getManifestContext();
|
|
64
|
+
const ctx = getManifestContext(process.cwd(), process.env.TRV_MODULE);
|
|
64
65
|
const target = getTarget.bind(null, ctx);
|
|
65
66
|
|
|
66
67
|
// Setup Tsconfig
|
package/bin/manifest-context.mjs
CHANGED
|
@@ -1,121 +1,56 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { createRequire } from 'node:module';
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
function readPackage(dir) {
|
|
10
|
-
dir = dir.endsWith('.json') ? path.dirname(dir) : dir;
|
|
11
|
-
try {
|
|
12
|
-
const v = readFileSync(path.resolve(dir, 'package.json'), 'utf8');
|
|
13
|
-
return ({ ...JSON.parse(v), path: path.resolve(dir) });
|
|
14
|
-
}
|
|
15
|
-
catch { }
|
|
16
|
-
}
|
|
17
|
-
function findPackage(dir) {
|
|
18
|
-
let prev;
|
|
19
|
-
let pkg, curr = path.resolve(dir);
|
|
20
|
-
while (!pkg && curr !== prev) {
|
|
21
|
-
pkg = readPackage(curr);
|
|
22
|
-
[prev, curr] = [curr, path.dirname(curr)];
|
|
23
|
-
}
|
|
24
|
-
if (!pkg) {
|
|
25
|
-
throw new Error('Could not find a package.json');
|
|
26
|
-
}
|
|
27
|
-
else {
|
|
28
|
-
return pkg;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
function resolveWorkspace(base = process.cwd()) {
|
|
32
|
-
if (base in WS_ROOT) {
|
|
33
|
-
return WS_ROOT[base];
|
|
34
|
-
}
|
|
35
|
-
let folder = base;
|
|
4
|
+
const toPort = (pth) => (Math.abs([...pth].reduce((a, b) => (a * 33) ^ b.charCodeAt(0), 5381)) % 29000) + 20000;
|
|
5
|
+
const toPosix = (pth) => pth.replaceAll('\\', '/');
|
|
6
|
+
const readPackage = (file) => ({ ...JSON.parse(readFileSync(file, 'utf8')), path: toPosix(path.dirname(file)) });
|
|
7
|
+
function findPackage(base, pred) {
|
|
8
|
+
let folder = `${base}/.`;
|
|
36
9
|
let prev;
|
|
37
10
|
let pkg;
|
|
38
|
-
|
|
11
|
+
do {
|
|
39
12
|
prev = folder;
|
|
40
|
-
pkg = readPackage(folder) ?? pkg;
|
|
41
|
-
if ((pkg && (!!pkg.workspaces || !!pkg.travetto?.build?.isolated)) ||
|
|
42
|
-
existsSync(path.resolve(folder, '.git'))) {
|
|
43
|
-
break;
|
|
44
|
-
}
|
|
45
13
|
folder = path.dirname(folder);
|
|
46
|
-
|
|
14
|
+
const folderPkg = path.resolve(folder, 'package.json');
|
|
15
|
+
pkg = existsSync(folderPkg) ? readPackage(folderPkg) : pkg;
|
|
16
|
+
} while (prev !== folder &&
|
|
17
|
+
!pred(pkg) &&
|
|
18
|
+
!existsSync(path.resolve(folder, '.git')));
|
|
47
19
|
if (!pkg) {
|
|
48
20
|
throw new Error('Could not find a package.json');
|
|
49
21
|
}
|
|
50
|
-
return
|
|
51
|
-
...pkg,
|
|
52
|
-
name: pkg.name ?? 'untitled',
|
|
53
|
-
type: pkg.type,
|
|
54
|
-
manager: existsSync(path.resolve(pkg.path, 'yarn.lock')) ? 'yarn' : 'npm',
|
|
55
|
-
resolve: createRequire(`${pkg.path}/node_modules`).resolve.bind(null),
|
|
56
|
-
stripRoot: (full) => full === pkg.path ? '' : full.replace(`${pkg.path}/`, ''),
|
|
57
|
-
mono: !!pkg.workspaces
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
function getCompilerUrl(ws) {
|
|
61
|
-
const port = (Math.abs([...ws.path].reduce((a, b) => (a * 33) ^ b.charCodeAt(0), 5381)) % 29000) + 20000;
|
|
62
|
-
return `http://localhost:${port}`;
|
|
63
|
-
}
|
|
64
|
-
function resolveModule(workspace, folder) {
|
|
65
|
-
let mod;
|
|
66
|
-
if (!folder && process.env.TRV_MODULE) {
|
|
67
|
-
mod = process.env.TRV_MODULE;
|
|
68
|
-
if (/[.][cm]?(t|j)sx?$/.test(mod)) {
|
|
69
|
-
try {
|
|
70
|
-
process.env.TRV_MODULE = mod = findPackage(path.dirname(mod)).name;
|
|
71
|
-
}
|
|
72
|
-
catch {
|
|
73
|
-
process.env.TRV_MODULE = mod = '';
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
if (mod) {
|
|
78
|
-
try {
|
|
79
|
-
folder = path.dirname(workspace.resolve(`${mod}/package.json`));
|
|
80
|
-
}
|
|
81
|
-
catch {
|
|
82
|
-
const workspacePkg = readPackage(workspace.path);
|
|
83
|
-
if (workspacePkg?.name === mod) {
|
|
84
|
-
folder = workspace.path;
|
|
85
|
-
}
|
|
86
|
-
else {
|
|
87
|
-
throw new Error(`Unable to resolve location for ${folder}`);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
return findPackage(folder ?? '.');
|
|
22
|
+
return pkg;
|
|
92
23
|
}
|
|
93
|
-
export function getManifestContext(
|
|
94
|
-
const workspace =
|
|
95
|
-
const mod = resolveModule(workspace, folder);
|
|
24
|
+
export function getManifestContext(root, mod) {
|
|
25
|
+
const workspace = findPackage(root, pkg => !!pkg?.workspaces || !!pkg?.travetto?.build?.isolated);
|
|
96
26
|
const build = workspace.travetto?.build ?? {};
|
|
27
|
+
const resolve = createRequire(path.resolve(workspace.path, 'node_modules')).resolve.bind(null);
|
|
28
|
+
const wsPrefix = `${workspace.path}/`;
|
|
29
|
+
const modPkg = mod ?
|
|
30
|
+
readPackage(resolve(`${mod}/package.json`)) :
|
|
31
|
+
findPackage(root, pkg => !!pkg) ?? workspace;
|
|
97
32
|
return {
|
|
98
33
|
workspace: {
|
|
99
|
-
name: workspace.name,
|
|
34
|
+
name: workspace.name ?? 'untitled',
|
|
100
35
|
path: workspace.path,
|
|
101
|
-
mono: workspace.
|
|
102
|
-
manager: workspace.
|
|
36
|
+
mono: !!workspace.workspaces,
|
|
37
|
+
manager: existsSync(path.resolve(workspace.path, 'yarn.lock')) ? 'yarn' : 'npm',
|
|
103
38
|
type: workspace.type ?? 'commonjs',
|
|
104
39
|
defaultEnv: workspace.travetto?.defaultEnv ?? 'local'
|
|
105
40
|
},
|
|
106
41
|
build: {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
outputFolder: build.outputFolder ??
|
|
111
|
-
toolFolder: build.toolFolder ??
|
|
112
|
-
typesFolder: build.typesFolder ??
|
|
42
|
+
compilerUrl: build.compilerUrl ?? `http://localhost:${toPort(wsPrefix)}`,
|
|
43
|
+
compilerModuleFolder: toPosix(path.dirname(resolve('@travetto/compiler/package.json'))).replace(wsPrefix, ''),
|
|
44
|
+
compilerFolder: toPosix(build.compilerFolder ?? '.trv/compiler'),
|
|
45
|
+
outputFolder: toPosix(build.outputFolder ?? '.trv/output'),
|
|
46
|
+
toolFolder: toPosix(build.toolFolder ?? '.trv/tool'),
|
|
47
|
+
typesFolder: toPosix(build.typesFolder ?? '.trv/types')
|
|
113
48
|
},
|
|
114
49
|
main: {
|
|
115
|
-
name:
|
|
116
|
-
folder:
|
|
117
|
-
version:
|
|
118
|
-
description:
|
|
50
|
+
name: modPkg.name ?? 'untitled',
|
|
51
|
+
folder: modPkg.path.replace(wsPrefix, ''),
|
|
52
|
+
version: modPkg.version,
|
|
53
|
+
description: modPkg.description
|
|
119
54
|
}
|
|
120
55
|
};
|
|
121
56
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/compiler",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.12",
|
|
4
4
|
"description": "The compiler infrastructure for the Travetto framework",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"compiler",
|
|
@@ -33,12 +33,12 @@
|
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@parcel/watcher": "^2.4.1",
|
|
36
|
-
"@travetto/manifest": "^5.0.
|
|
37
|
-
"@travetto/transformer": "^5.0.
|
|
36
|
+
"@travetto/manifest": "^5.0.6",
|
|
37
|
+
"@travetto/transformer": "^5.0.7",
|
|
38
38
|
"@types/node": "^22.7.4"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
|
-
"@travetto/cli": "^5.0.
|
|
41
|
+
"@travetto/cli": "^5.0.9"
|
|
42
42
|
},
|
|
43
43
|
"peerDependenciesMeta": {
|
|
44
44
|
"@travetto/cli": {
|
package/src/compiler.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { ManifestIndex, ManifestModuleUtil } from '@travetto/manifest';
|
|
|
6
6
|
import { CompilerUtil } from './util';
|
|
7
7
|
import { CompilerState } from './state';
|
|
8
8
|
import { CompilerWatcher } from './watch';
|
|
9
|
-
import { CompileEmitEvent, CompileEmitter } from './types';
|
|
9
|
+
import { CompileEmitEvent, CompileEmitter, CompilerReset } from './types';
|
|
10
10
|
import { EventUtil } from './event';
|
|
11
11
|
|
|
12
12
|
import { IpcLogger } from '../support/log';
|
|
@@ -74,7 +74,7 @@ export class Compiler {
|
|
|
74
74
|
break;
|
|
75
75
|
}
|
|
76
76
|
case 'reset': {
|
|
77
|
-
log.info('
|
|
77
|
+
log.info('Reset due to', err?.message);
|
|
78
78
|
EventUtil.sendEvent('state', { state: 'reset' });
|
|
79
79
|
process.exitCode = 0;
|
|
80
80
|
break;
|
|
@@ -164,7 +164,7 @@ export class Compiler {
|
|
|
164
164
|
|
|
165
165
|
EventUtil.sendEvent('state', { state: 'watch-start' });
|
|
166
166
|
try {
|
|
167
|
-
for await (const ev of new CompilerWatcher(this.#state, this.#signal)
|
|
167
|
+
for await (const ev of new CompilerWatcher(this.#state, this.#signal)) {
|
|
168
168
|
if (ev.action !== 'delete') {
|
|
169
169
|
const err = await emitter(ev.entry.sourceFile, true);
|
|
170
170
|
if (err) {
|
|
@@ -193,7 +193,7 @@ export class Compiler {
|
|
|
193
193
|
|
|
194
194
|
} catch (err) {
|
|
195
195
|
if (err instanceof Error) {
|
|
196
|
-
this.#shutdown(err
|
|
196
|
+
this.#shutdown(err instanceof CompilerReset ? 'reset' : 'error', err);
|
|
197
197
|
}
|
|
198
198
|
}
|
|
199
199
|
}
|
package/src/types.ts
CHANGED
|
@@ -6,3 +6,5 @@ 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 = { sourceFile: string, tscOutputFile: string, outputFile?: string, module: ManifestModule };
|
|
9
|
+
export type CompilerWatchEvent = { action: 'create' | 'update' | 'delete', file: string, entry: CompileStateEntry };
|
|
10
|
+
export class CompilerReset extends Error { }
|
package/src/watch.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import { watch } from 'node:fs';
|
|
2
3
|
|
|
3
|
-
import {
|
|
4
|
+
import { ManifestFileUtil, ManifestModuleUtil, ManifestUtil, PackageUtil, path } from '@travetto/manifest';
|
|
4
5
|
|
|
5
|
-
import type
|
|
6
|
+
import { CompilerReset, type CompilerWatchEvent, type CompileStateEntry } from './types';
|
|
6
7
|
import { CompilerState } from './state';
|
|
7
8
|
import { CompilerUtil } from './util';
|
|
8
9
|
|
|
@@ -11,249 +12,222 @@ import { IpcLogger } from '../support/log';
|
|
|
11
12
|
|
|
12
13
|
const log = new IpcLogger({ level: 'debug' });
|
|
13
14
|
|
|
14
|
-
type
|
|
15
|
-
|
|
16
|
-
type CompilerWatchEvent = WatchEvent & { entry: CompileStateEntry };
|
|
17
|
-
type FileShape = {
|
|
18
|
-
mod: string;
|
|
19
|
-
folderKey: ManifestModuleFolderType;
|
|
20
|
-
fileType: ManifestModuleFileType;
|
|
21
|
-
moduleFile: string;
|
|
22
|
-
action: WatchAction;
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
const DEFAULT_WRITE_LIMIT = 1000 * 60 * 5;
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Watch support, based on compiler state and manifest details
|
|
29
|
-
*/
|
|
15
|
+
type CompilerWatchEventCandidate = Omit<CompilerWatchEvent, 'entry'> & { entry?: CompileStateEntry };
|
|
16
|
+
|
|
30
17
|
export class CompilerWatcher {
|
|
31
18
|
#state: CompilerState;
|
|
32
|
-
#
|
|
19
|
+
#cleanup: Partial<Record<'tool' | 'workspace' | 'canary', () => (void | Promise<void>)>> = {};
|
|
20
|
+
#watchCanary: string = '.trv/canary.id';
|
|
21
|
+
#lastWorkspaceModified = Date.now();
|
|
22
|
+
#watchCanaryFreq = 5;
|
|
23
|
+
#root: string;
|
|
24
|
+
#q: AsyncQueue<CompilerWatchEvent>;
|
|
33
25
|
|
|
34
26
|
constructor(state: CompilerState, signal: AbortSignal) {
|
|
35
27
|
this.#state = state;
|
|
36
|
-
this.#
|
|
28
|
+
this.#root = state.manifest.workspace.path;
|
|
29
|
+
this.#q = new AsyncQueue(signal);
|
|
30
|
+
signal.addEventListener('abort', () => Object.values(this.#cleanup).forEach(x => x()));
|
|
37
31
|
}
|
|
38
32
|
|
|
39
|
-
#
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
33
|
+
async #getWatchIgnores(): Promise<string[]> {
|
|
34
|
+
const pkg = PackageUtil.readPackage(this.#root);
|
|
35
|
+
const patterns = [
|
|
36
|
+
...pkg?.travetto?.build?.watchIgnores ?? [],
|
|
37
|
+
'**/node_modules',
|
|
38
|
+
'.*/**/node_modules'
|
|
39
|
+
];
|
|
40
|
+
const ignores = new Set(['node_modules', '.git']);
|
|
41
|
+
for (const item of patterns) {
|
|
42
|
+
if (item.includes('*')) {
|
|
43
|
+
for await (const sub of fs.glob(item, { cwd: this.#root })) {
|
|
44
|
+
if (sub.startsWith('node_modules')) {
|
|
45
|
+
continue;
|
|
46
|
+
} else if (sub.endsWith('/node_modules')) {
|
|
47
|
+
ignores.add(sub.split('/node_modules')[0]);
|
|
48
|
+
} else {
|
|
49
|
+
ignores.add(sub);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
} else {
|
|
53
|
+
ignores.add(item);
|
|
59
54
|
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
#reset(ev: WatchEvent): never {
|
|
64
|
-
throw new Error('RESET', { cause: `${ev.action}:${ev.file}` });
|
|
55
|
+
}
|
|
56
|
+
return [...ignores].sort().map(x => x.endsWith('/') ? x : `${x}/`);
|
|
65
57
|
}
|
|
66
58
|
|
|
67
|
-
#
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
59
|
+
#toCandidateEvent(action: CompilerWatchEvent['action'], file: string): CompilerWatchEventCandidate {
|
|
60
|
+
let entry = this.#state.getBySource(file);
|
|
61
|
+
const mod = entry?.module ?? this.#state.manifestIndex.findModuleForArbitraryFile(file);
|
|
62
|
+
if (mod && action === 'create' && !entry) {
|
|
63
|
+
const modRoot = mod.sourceFolder || this.#root;
|
|
64
|
+
const moduleFile = file.includes(modRoot) ? file.split(`${modRoot}/`)[1] : file;
|
|
65
|
+
entry = this.#state.registerInput(mod, moduleFile);
|
|
73
66
|
}
|
|
74
|
-
|
|
75
|
-
// TODO: Fix once node/parcel sort this out
|
|
76
|
-
return os.platform() === 'linux' ? [] : [
|
|
77
|
-
...ignores,
|
|
78
|
-
'.git', '**/.git',
|
|
79
|
-
`${this.#state.manifest.build.outputFolder}/node_modules/**`,
|
|
80
|
-
`${this.#state.manifest.build.compilerFolder}/node_modules/**`,
|
|
81
|
-
`${this.#state.manifest.build.toolFolder}/**`
|
|
82
|
-
];
|
|
67
|
+
return { entry, file: entry?.sourceFile ?? file, action };
|
|
83
68
|
}
|
|
84
69
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
if (this.#signal.aborted) { // If already aborted, can happen async
|
|
100
|
-
cleanup.unsubscribe();
|
|
101
|
-
return;
|
|
70
|
+
#isValidEvent(ev: CompilerWatchEventCandidate): ev is CompilerWatchEvent {
|
|
71
|
+
const relativeFile = ev.file.replace(`${this.#root}/`, '');
|
|
72
|
+
if (relativeFile === this.#watchCanary) {
|
|
73
|
+
return false;
|
|
74
|
+
} else if (relativeFile.startsWith('.')) {
|
|
75
|
+
return false;
|
|
76
|
+
} else if (!ev.entry) {
|
|
77
|
+
log.debug(`Skipping unknown file ${relativeFile}`);
|
|
78
|
+
return false;
|
|
79
|
+
} else if (ev.action === 'update' && !this.#state.checkIfSourceChanged(ev.entry.sourceFile)) {
|
|
80
|
+
log.debug(`Skipping update, as contents unchanged ${relativeFile}`);
|
|
81
|
+
return false;
|
|
82
|
+
} else if (!CompilerUtil.validFile(ManifestModuleUtil.getFileType(relativeFile))) {
|
|
83
|
+
return false;
|
|
102
84
|
}
|
|
103
|
-
|
|
104
|
-
this.#signal.addEventListener('abort', () => cleanup.unsubscribe());
|
|
105
|
-
|
|
106
|
-
yield* q;
|
|
85
|
+
return true;
|
|
107
86
|
}
|
|
108
87
|
|
|
109
|
-
async #
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
if (!events.length) {
|
|
88
|
+
async #reconcileAddRemove(compilerEvents: CompilerWatchEvent[]): Promise<void> {
|
|
89
|
+
const nonUpdates = compilerEvents.filter(x => x.entry.outputFile && x.action !== 'update');
|
|
90
|
+
if (!nonUpdates.length) {
|
|
113
91
|
return;
|
|
114
92
|
}
|
|
115
93
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
const parents = new Map<string, string[]>(
|
|
119
|
-
mods.map(m => [m, this.#state.manifestIndex.getDependentModules(m, 'parents').map(x => x.name)])
|
|
120
|
-
);
|
|
121
|
-
|
|
122
|
-
const moduleToFiles = new Map<string, { context: ManifestContext, files: FileShape[] }>(
|
|
123
|
-
[...mods, ...parents.values()].flat().map(m => [m, {
|
|
124
|
-
context: ManifestUtil.getModuleContext(this.#state.manifest, this.#state.manifestIndex.getManifestModule(m)!.sourceFolder),
|
|
125
|
-
files: []
|
|
126
|
-
}])
|
|
127
|
-
);
|
|
128
|
-
|
|
129
|
-
const allFiles = events.map(ev => {
|
|
130
|
-
const modRoot = ev.entry.module.sourceFolder || this.#state.manifest.workspace.path;
|
|
131
|
-
const moduleFile = ev.file.includes(modRoot) ? ev.file.split(`${modRoot}/`)[1] : ev.file;
|
|
132
|
-
const folderKey = ManifestModuleUtil.getFolderKey(moduleFile);
|
|
133
|
-
const fileType = ManifestModuleUtil.getFileType(moduleFile);
|
|
134
|
-
return { mod: ev.entry.module.name, action: ev.action, moduleFile, folderKey, fileType };
|
|
135
|
-
});
|
|
94
|
+
try {
|
|
95
|
+
const eventsByMod = new Map<string, CompilerWatchEvent[]>();
|
|
136
96
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
97
|
+
for (const ev of nonUpdates) {
|
|
98
|
+
const mod = ev.entry.module;
|
|
99
|
+
if (ev.action === 'delete') {
|
|
100
|
+
this.#state.removeSource(ev.entry.sourceFile);
|
|
101
|
+
}
|
|
102
|
+
for (const m of [mod, ...this.#state.manifestIndex.getDependentModules(mod.name, 'parents')]) {
|
|
103
|
+
if (!eventsByMod.has(m.name)) {
|
|
104
|
+
eventsByMod.set(m.name, []);
|
|
105
|
+
}
|
|
106
|
+
eventsByMod.get(m.name)!.push(ev);
|
|
142
107
|
}
|
|
143
|
-
mod.files.push(file);
|
|
144
108
|
}
|
|
145
|
-
}
|
|
146
109
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
110
|
+
for (const [mod, events] of eventsByMod.entries()) {
|
|
111
|
+
const modRoot = this.#state.manifestIndex.getManifestModule(mod)!.sourceFolder;
|
|
112
|
+
const context = ManifestUtil.getModuleContext(this.#state.manifest, modRoot);
|
|
113
|
+
const newManifest = ManifestUtil.readManifestSync(ManifestUtil.getManifestLocation(context));
|
|
114
|
+
log.debug('Updating manifest', { module: mod });
|
|
115
|
+
for (const { action, file } of events) {
|
|
116
|
+
const resolvedRoot = modRoot || this.#root;
|
|
117
|
+
const moduleFile = file.includes(resolvedRoot) ? file.split(`${resolvedRoot}/`)[1] : file;
|
|
118
|
+
const folderKey = ManifestModuleUtil.getFolderKey(moduleFile);
|
|
119
|
+
const fileType = ManifestModuleUtil.getFileType(moduleFile);
|
|
120
|
+
|
|
121
|
+
const modFiles = newManifest.modules[mod].files[folderKey] ??= [];
|
|
122
|
+
const idx = modFiles.findIndex(x => x[0] === moduleFile);
|
|
123
|
+
switch (action) {
|
|
124
|
+
case 'create': modFiles[idx < 0 ? modFiles.length : idx] = [moduleFile, fileType, Date.now()]; break;
|
|
125
|
+
case 'delete': modFiles.splice(idx, 1); break;
|
|
160
126
|
}
|
|
161
127
|
}
|
|
128
|
+
await ManifestUtil.writeManifest(newManifest);
|
|
162
129
|
}
|
|
163
|
-
await ManifestUtil.writeManifest(newManifest);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// Reindex at workspace root
|
|
167
|
-
this.#state.manifestIndex.init(ManifestUtil.getManifestLocation(this.#state.manifest));
|
|
168
|
-
}
|
|
169
130
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
* @returns
|
|
175
|
-
*/
|
|
176
|
-
async * watchChanges(): AsyncIterable<CompilerWatchEvent> {
|
|
177
|
-
if (this.#signal.aborted) {
|
|
178
|
-
return;
|
|
131
|
+
this.#state.manifestIndex.init(ManifestUtil.getManifestLocation(this.#state.manifest));
|
|
132
|
+
} catch (mErr) {
|
|
133
|
+
log.info('Restarting due to manifest rebuild failure', mErr);
|
|
134
|
+
throw new CompilerReset(`Manifest rebuild failure: ${mErr}`);
|
|
179
135
|
}
|
|
136
|
+
}
|
|
180
137
|
|
|
181
|
-
|
|
182
|
-
const
|
|
183
|
-
const
|
|
184
|
-
const
|
|
185
|
-
const OUTPUT_PATH = manifest.build.outputFolder;
|
|
186
|
-
const COMPILER_PATH = manifest.build.compilerFolder;
|
|
187
|
-
const TYPES_PATH = manifest.build.typesFolder;
|
|
188
|
-
const COMPILER_ROOT = path.dirname(COMPILER_PATH);
|
|
189
|
-
|
|
190
|
-
const IGNORE_RE = new RegExp(`^[.]|(${COMPILER_PATH}|${TYPES_PATH}|${OUTPUT_PATH})`);
|
|
191
|
-
|
|
192
|
-
const watchDog = this.#watchDog();
|
|
193
|
-
|
|
194
|
-
for await (const events of this.#watchFolder(ROOT)) {
|
|
138
|
+
async #listenWorkspace(): Promise<void> {
|
|
139
|
+
const lib = await import('@parcel/watcher');
|
|
140
|
+
const ignore = await this.#getWatchIgnores();
|
|
141
|
+
const packageFiles = new Set(['package-lock.json', 'yarn.lock', 'package.json'].map(x => path.resolve(this.#root, x)));
|
|
195
142
|
|
|
196
|
-
|
|
143
|
+
log.debug('Ignore Globs', ignore);
|
|
144
|
+
log.debug('Watching', this.#root);
|
|
197
145
|
|
|
198
|
-
|
|
199
|
-
const { action, file: sourceFile } = ev;
|
|
146
|
+
await this.#cleanup.workspace?.();
|
|
200
147
|
|
|
201
|
-
|
|
148
|
+
const listener = await lib.subscribe(this.#root, async (err, events) => {
|
|
149
|
+
this.#lastWorkspaceModified = Date.now();
|
|
202
150
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
151
|
+
try {
|
|
152
|
+
if (err) {
|
|
153
|
+
throw err instanceof Error ? err : new Error(`${err}`);
|
|
154
|
+
} else if (events.length > 25) {
|
|
155
|
+
throw new CompilerReset(`Large influx of file changes: ${events.length}`);
|
|
156
|
+
} else if (events.some(ev => packageFiles.has(path.toPosix(ev.path)))) {
|
|
157
|
+
throw new CompilerReset('Package information changed');
|
|
209
158
|
}
|
|
210
159
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
160
|
+
const items = events
|
|
161
|
+
.map(x => this.#toCandidateEvent(x.type, path.toPosix(x.path)))
|
|
162
|
+
.filter(x => this.#isValidEvent(x));
|
|
214
163
|
|
|
215
|
-
|
|
164
|
+
await this.#reconcileAddRemove(items);
|
|
216
165
|
|
|
217
|
-
const
|
|
218
|
-
|
|
219
|
-
continue;
|
|
166
|
+
for (const item of items) {
|
|
167
|
+
this.#q.add(item);
|
|
220
168
|
}
|
|
169
|
+
} catch (out) {
|
|
170
|
+
return this.#q.throw(out instanceof Error ? out : new Error(`${out}`));
|
|
171
|
+
}
|
|
172
|
+
}, { ignore });
|
|
221
173
|
|
|
222
|
-
|
|
174
|
+
this.#cleanup.workspace = (): Promise<void> => listener.unsubscribe();
|
|
175
|
+
}
|
|
223
176
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
177
|
+
async #listenToolFolder(): Promise<void> {
|
|
178
|
+
const build = this.#state.manifest.build;
|
|
179
|
+
const toolRootFolder = path.dirname(path.resolve(this.#root, build.compilerFolder));
|
|
180
|
+
const toolFolders = new Set([
|
|
181
|
+
toolRootFolder, build.compilerFolder, build.typesFolder, build.outputFolder
|
|
182
|
+
].map(x => path.resolve(this.#root, x)));
|
|
229
183
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
if (action === 'create') {
|
|
234
|
-
entry = this.#state.registerInput(mod, moduleFile);
|
|
235
|
-
} else if (!entry) {
|
|
236
|
-
log.debug(`Unknown file ${relativeFile}`);
|
|
237
|
-
continue;
|
|
238
|
-
} else if (action === 'update' && !this.#state.checkIfSourceChanged(entry.sourceFile)) {
|
|
239
|
-
log.debug(`Skipping update, as contents unchanged ${relativeFile}`);
|
|
240
|
-
continue;
|
|
241
|
-
} else if (action === 'delete') {
|
|
242
|
-
this.#state.removeSource(entry.sourceFile);
|
|
243
|
-
}
|
|
184
|
+
log.debug('Tooling Folders', [...toolFolders].map(x => x.replace(`${this.#root}/`, '')));
|
|
185
|
+
|
|
186
|
+
await this.#cleanup.tool?.();
|
|
244
187
|
|
|
245
|
-
|
|
188
|
+
const listener = watch(toolRootFolder, { encoding: 'utf8' }, async (ev, f) => {
|
|
189
|
+
if (!f) {
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
const full = path.resolve(toolRootFolder, f);
|
|
193
|
+
const stat = await fs.stat(full).catch(() => null);
|
|
194
|
+
if (toolFolders.has(full) && !stat) {
|
|
195
|
+
this.#q.throw(new CompilerReset(`Tooling folder removal ${full}`));
|
|
246
196
|
}
|
|
197
|
+
});
|
|
198
|
+
this.#cleanup.tool = (): void => listener.close();
|
|
199
|
+
}
|
|
247
200
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
201
|
+
async #listenCanary(): Promise<void> {
|
|
202
|
+
await this.#cleanup.canary?.();
|
|
203
|
+
const full = path.resolve(this.#root, this.#watchCanary);
|
|
204
|
+
await ManifestFileUtil.bufferedFileWrite(full, '');
|
|
205
|
+
|
|
206
|
+
log.debug('Starting workspace canary');
|
|
207
|
+
const canaryId = setInterval(async () => {
|
|
208
|
+
const delta = Math.trunc((Date.now() - this.#lastWorkspaceModified) / 1000);
|
|
209
|
+
if (delta > 600) {
|
|
210
|
+
log.error('Restarting canary due to extra long delay');
|
|
211
|
+
this.#lastWorkspaceModified = Date.now(); // Reset
|
|
212
|
+
} else if (delta > this.#watchCanaryFreq * 2) {
|
|
213
|
+
this.#q.throw(new CompilerReset(`Workspace watch stopped responding ${delta}s ago`));
|
|
214
|
+
} else if (delta > this.#watchCanaryFreq) {
|
|
215
|
+
log.error('Restarting parcel due to inactivity');
|
|
216
|
+
await this.#listenWorkspace();
|
|
217
|
+
} else {
|
|
218
|
+
await fs.utimes(full, new Date(), new Date());
|
|
253
219
|
}
|
|
254
|
-
|
|
255
|
-
}
|
|
220
|
+
}, this.#watchCanaryFreq * 1000);
|
|
256
221
|
|
|
257
|
-
|
|
222
|
+
this.#cleanup.canary = (): void => clearInterval(canaryId);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
[Symbol.asyncIterator](): AsyncIterator<CompilerWatchEvent> {
|
|
226
|
+
if (!this.#cleanup.workspace) {
|
|
227
|
+
this.#listenWorkspace();
|
|
228
|
+
this.#listenToolFolder();
|
|
229
|
+
this.#listenCanary();
|
|
230
|
+
}
|
|
231
|
+
return this.#q[Symbol.asyncIterator]();
|
|
258
232
|
}
|
|
259
233
|
}
|