@dimina-kit/devkit 0.1.2-dev.20260706131625 → 0.1.2-dev.20260707070110
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/dist/compile-log.d.ts.map +1 -1
- package/dist/compile-log.js +10 -0
- package/dist/compile-log.test.js +44 -0
- package/dist/esbuild-binary-path.d.ts +38 -0
- package/dist/esbuild-binary-path.d.ts.map +1 -0
- package/dist/esbuild-binary-path.js +29 -0
- package/dist/esbuild-binary-path.test.d.ts +2 -0
- package/dist/esbuild-binary-path.test.d.ts.map +1 -0
- package/dist/esbuild-binary-path.test.js +134 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +22 -16
- package/dist/project-watcher.test.js +68 -0
- package/fe/dimina-fe-container/assets/index.js +8 -8
- package/fe/dimina-fe-container/assets/pageFrame.js +2 -2
- package/fe/dimina-fe-container/dimina-version.json +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compile-log.d.ts","sourceRoot":"","sources":["../src/compile-log.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;
|
|
1
|
+
{"version":3,"file":"compile-log.d.ts","sourceRoot":"","sources":["../src/compile-log.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAgCH;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAO7D"}
|
package/dist/compile-log.js
CHANGED
|
@@ -25,6 +25,16 @@ const DROP_RULES = [
|
|
|
25
25
|
// Stack-trace frames (esbuild / Node internals) — the message line above
|
|
26
26
|
// them is kept; frames are noise for a compile panel.
|
|
27
27
|
/^\s+at /,
|
|
28
|
+
// Node process-level DeprecationWarnings + the `--trace-deprecation` hint
|
|
29
|
+
// Node prints after the first one. The developer's code never runs in the
|
|
30
|
+
// compile worker, so these are never actionable in a compile panel. Packaged
|
|
31
|
+
// Electron apps ALWAYS emit one: the asar fs shim's asarStatsToFsStats uses
|
|
32
|
+
// the deprecated fs.Stats constructor (electron/electron#47390), so the
|
|
33
|
+
// first stat of an in-asar file prints the DEP0180 pair to stderr. Only the
|
|
34
|
+
// DeprecationWarning form is dropped — other `(node:pid)` warnings
|
|
35
|
+
// (MaxListenersExceeded, Experimental) can carry real signal and are kept.
|
|
36
|
+
/^\(node:\d+\) (?:\[DEP\d+\] )?DeprecationWarning: /,
|
|
37
|
+
/^\(Use `.+--trace-deprecation .*` to show where the warning was created\)/,
|
|
28
38
|
];
|
|
29
39
|
/**
|
|
30
40
|
* Strip ANSI escapes, then decide keep/drop. Returns the cleaned line, or
|
package/dist/compile-log.test.js
CHANGED
|
@@ -116,6 +116,50 @@ describe('filterDmccLogLine — KEEP rules (real dmcc signal from the spike arch
|
|
|
116
116
|
}
|
|
117
117
|
});
|
|
118
118
|
});
|
|
119
|
+
describe('filterDmccLogLine — DROP rules (packaged-app Node DeprecationWarning noise, electron/electron#47390)', () => {
|
|
120
|
+
// Electron's asar fs shim (`asarStatsToFsStats`) uses the deprecated
|
|
121
|
+
// `fs.Stats` constructor, so Node prints a process-level
|
|
122
|
+
// DeprecationWarning pair on the compile worker's stderr the first time a
|
|
123
|
+
// packaged app stats a file inside app.asar. The developer's own code
|
|
124
|
+
// never runs in the compile worker, so these lines are never actionable
|
|
125
|
+
// and must be dropped.
|
|
126
|
+
it('drops the Node process-level DeprecationWarning line, with or without a DEP code', () => {
|
|
127
|
+
const filter = getFilter();
|
|
128
|
+
expect(filter('(node:4984) [DEP0180] DeprecationWarning: fs.Stats constructor is deprecated.')).toBeNull();
|
|
129
|
+
expect(filter('(node:90165) DeprecationWarning: Buffer() is deprecated due to security and usability issues.')).toBeNull();
|
|
130
|
+
});
|
|
131
|
+
it('drops the trace-deprecation hint line that follows, regardless of the binary name', () => {
|
|
132
|
+
const filter = getFilter();
|
|
133
|
+
expect(filter('(Use `千岛开发者工具 Helper --trace-deprecation ...` to show where the warning was created)')).toBeNull();
|
|
134
|
+
expect(filter('(Use `node --trace-deprecation ...` to show where the warning was created)')).toBeNull();
|
|
135
|
+
});
|
|
136
|
+
it('drops ANSI-colored variants of both lines', () => {
|
|
137
|
+
const filter = getFilter();
|
|
138
|
+
expect(filter('[33m(node:4984) [DEP0180] DeprecationWarning: fs.Stats constructor is deprecated.[39m')).toBeNull();
|
|
139
|
+
expect(filter('[33m(Use `node --trace-deprecation ...` to show where the warning was created)[39m')).toBeNull();
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
describe('filterDmccLogLine — KEEP rules (guards against over-filtering Node warnings)', () => {
|
|
143
|
+
it('keeps other Node process-level warnings that are not DeprecationWarnings', () => {
|
|
144
|
+
const filter = getFilter();
|
|
145
|
+
// These can carry real signal (leak / experimental-API notices) and are
|
|
146
|
+
// not the asar-shim noise this filter targets.
|
|
147
|
+
const lines = [
|
|
148
|
+
'(node:123) MaxListenersExceededWarning: Possible EventEmitter memory leak detected.',
|
|
149
|
+
'(node:123) ExperimentalWarning: VM Modules is an experimental feature.',
|
|
150
|
+
];
|
|
151
|
+
for (const line of lines) {
|
|
152
|
+
expect(filter(line), `non-deprecation Node warning must be kept: ${JSON.stringify(line)}`).toBe(line);
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
it('keeps a compiler diagnostic that merely mentions DeprecationWarning mid-sentence', () => {
|
|
156
|
+
const filter = getFilter();
|
|
157
|
+
// Does not start with the `(node:<pid>)` prefix, so it is not the
|
|
158
|
+
// Node process-level warning form — default-keep applies.
|
|
159
|
+
const line = '[logic] esbuild 转换失败 app.js: DeprecationWarning something';
|
|
160
|
+
expect(filter(line)).toBe(line);
|
|
161
|
+
});
|
|
162
|
+
});
|
|
119
163
|
describe('filterDmccLogLine — ANSI stripping happens BEFORE rule matching', () => {
|
|
120
164
|
it('strips ANSI from a kept line and returns the cleaned text', () => {
|
|
121
165
|
const filter = getFilter();
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ESBUILD_BINARY_PATH redirect for Electron app.asar hosts.
|
|
3
|
+
*
|
|
4
|
+
* esbuild's JS lib computes its native binary path from its own module
|
|
5
|
+
* location. Packaged via electron-builder that location sits inside app.asar,
|
|
6
|
+
* and `child_process.spawn` (unlike execFile) is not patched by Electron's
|
|
7
|
+
* asar shim — spawning an in-asar path always fails ENOENT even though
|
|
8
|
+
* asarUnpack put the real binary in app.asar.unpacked. esbuild honors the
|
|
9
|
+
* ESBUILD_BINARY_PATH env var, so devkit points it at the unpacked binary.
|
|
10
|
+
*
|
|
11
|
+
* The platform packages lay their binary out differently (esbuild's own
|
|
12
|
+
* pkgAndSubpathForCurrentPlatform): win32 ships `esbuild.exe` at the package
|
|
13
|
+
* ROOT — there is no `bin/` directory — while every unix-like platform ships
|
|
14
|
+
* `bin/esbuild`. Resolving the unix shape on win32 throws, and a swallowed
|
|
15
|
+
* throw leaves the env var unset, which is exactly the packaged-Windows
|
|
16
|
+
* failure mode this module guards against.
|
|
17
|
+
*
|
|
18
|
+
* All I/O is dependency-injected so the branching is unit-testable; index.ts
|
|
19
|
+
* wires the real process/require/fs at module load.
|
|
20
|
+
*/
|
|
21
|
+
export interface ApplyEsbuildBinaryPathOptions {
|
|
22
|
+
/** Caller module's directory — an `app.asar` segment means "packaged". */
|
|
23
|
+
dirname: string;
|
|
24
|
+
/** process.env-like map, mutated in place. */
|
|
25
|
+
env: Record<string, string | undefined>;
|
|
26
|
+
/** process.platform value. */
|
|
27
|
+
platform: string;
|
|
28
|
+
/** process.arch value. */
|
|
29
|
+
arch: string;
|
|
30
|
+
/** require.resolve-like; throws when the id cannot be resolved. */
|
|
31
|
+
resolve: (id: string) => string;
|
|
32
|
+
/** fs.existsSync-like, probed against the rewritten unpacked path. */
|
|
33
|
+
exists: (p: string) => boolean;
|
|
34
|
+
/** Diagnostics sink — every failure path reports here instead of going silent. */
|
|
35
|
+
warn: (msg: string) => void;
|
|
36
|
+
}
|
|
37
|
+
export declare function applyEsbuildBinaryPath(opts: ApplyEsbuildBinaryPathOptions): void;
|
|
38
|
+
//# sourceMappingURL=esbuild-binary-path.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"esbuild-binary-path.d.ts","sourceRoot":"","sources":["../src/esbuild-binary-path.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,WAAW,6BAA6B;IAC7C,0EAA0E;IAC1E,OAAO,EAAE,MAAM,CAAA;IACf,8CAA8C;IAC9C,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;IACvC,8BAA8B;IAC9B,QAAQ,EAAE,MAAM,CAAA;IAChB,0BAA0B;IAC1B,IAAI,EAAE,MAAM,CAAA;IACZ,mEAAmE;IACnE,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,MAAM,CAAA;IAC/B,sEAAsE;IACtE,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,OAAO,CAAA;IAC9B,kFAAkF;IAClF,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;CAC3B;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,6BAA6B,GAAG,IAAI,CA0BhF"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export function applyEsbuildBinaryPath(opts) {
|
|
2
|
+
const { dirname, env, platform, arch, resolve, exists, warn } = opts;
|
|
3
|
+
if (env.ESBUILD_BINARY_PATH)
|
|
4
|
+
return;
|
|
5
|
+
if (!dirname.includes('app.asar'))
|
|
6
|
+
return;
|
|
7
|
+
const subpath = platform === 'win32' ? 'esbuild.exe' : 'bin/esbuild';
|
|
8
|
+
const id = `@esbuild/${platform}-${arch}/${subpath}`;
|
|
9
|
+
let resolved;
|
|
10
|
+
try {
|
|
11
|
+
resolved = resolve(id);
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
warn(`[devkit] could not resolve ${id} — esbuild's platform binary package is missing `
|
|
15
|
+
+ 'from the packaged app, so the compiler cannot spawn esbuild. Ship the package '
|
|
16
|
+
+ '(electron-builder dependency collection must include it) or set ESBUILD_BINARY_PATH.');
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
// asarUnpack mirrors the archive's layout on disk — only the first app.asar
|
|
20
|
+
// segment moves to app.asar.unpacked. A path already outside the archive
|
|
21
|
+
// (or already unpacked) has no `app.asar/` segment and passes through as-is.
|
|
22
|
+
const unpacked = resolved.replace(/app\.asar([\\/])/, 'app.asar.unpacked$1');
|
|
23
|
+
if (!exists(unpacked)) {
|
|
24
|
+
warn(`[devkit] esbuild binary not found at ${unpacked} — the Electron host's packaging must `
|
|
25
|
+
+ "asarUnpack '**/node_modules/esbuild/**' and '**/node_modules/@esbuild/**' so the native "
|
|
26
|
+
+ 'binary exists outside app.asar (spawn cannot execute from inside the archive).');
|
|
27
|
+
}
|
|
28
|
+
env.ESBUILD_BINARY_PATH = unpacked;
|
|
29
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"esbuild-binary-path.test.d.ts","sourceRoot":"","sources":["../src/esbuild-binary-path.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { applyEsbuildBinaryPath } from './esbuild-binary-path.js';
|
|
3
|
+
/**
|
|
4
|
+
* Contract for `applyEsbuildBinaryPath` — the ESBUILD_BINARY_PATH redirect
|
|
5
|
+
* that lets esbuild spawn its native binary from app.asar.unpacked when
|
|
6
|
+
* devkit runs packaged inside an Electron app.asar.
|
|
7
|
+
*
|
|
8
|
+
* The platform package layout differs by OS:
|
|
9
|
+
* - darwin/linux: `@esbuild/<platform>-<arch>/bin/esbuild`
|
|
10
|
+
* - win32: `@esbuild/win32-<arch>/esbuild.exe` at the package ROOT, no
|
|
11
|
+
* `bin/` dir. Resolving the darwin/linux shape on win32 throws, and a
|
|
12
|
+
* swallowed throw means ESBUILD_BINARY_PATH is silently never set —
|
|
13
|
+
* packaged Windows apps then fail with
|
|
14
|
+
* `spawn ...app.asar\node_modules\@esbuild\win32-x64\esbuild.exe ENOENT`.
|
|
15
|
+
*
|
|
16
|
+
* All I/O (require.resolve, fs.existsSync, console.warn, process.env) is
|
|
17
|
+
* dependency-injected so the tests exercise pure branching logic.
|
|
18
|
+
*/
|
|
19
|
+
function makeOpts(overrides = {}) {
|
|
20
|
+
return {
|
|
21
|
+
dirname: 'C:\\Users\\x\\AppData\\Local\\Programs\\myapp\\resources\\app.asar\\packages\\devkit\\dist',
|
|
22
|
+
env: {},
|
|
23
|
+
platform: 'win32',
|
|
24
|
+
arch: 'x64',
|
|
25
|
+
resolve: vi.fn(() => {
|
|
26
|
+
throw new Error('resolve not stubbed for this test');
|
|
27
|
+
}),
|
|
28
|
+
exists: vi.fn(() => true),
|
|
29
|
+
warn: vi.fn(),
|
|
30
|
+
...overrides,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
describe('applyEsbuildBinaryPath — no-op guards', () => {
|
|
34
|
+
it('does nothing when ESBUILD_BINARY_PATH is already set', () => {
|
|
35
|
+
const env = { ESBUILD_BINARY_PATH: '/already/set/esbuild' };
|
|
36
|
+
const resolve = vi.fn();
|
|
37
|
+
const exists = vi.fn();
|
|
38
|
+
const warn = vi.fn();
|
|
39
|
+
applyEsbuildBinaryPath(makeOpts({ env, resolve, exists, warn }));
|
|
40
|
+
expect(env.ESBUILD_BINARY_PATH).toBe('/already/set/esbuild');
|
|
41
|
+
expect(resolve).not.toHaveBeenCalled();
|
|
42
|
+
expect(exists).not.toHaveBeenCalled();
|
|
43
|
+
expect(warn).not.toHaveBeenCalled();
|
|
44
|
+
});
|
|
45
|
+
it('does nothing when dirname is not inside an app.asar (unpackaged dev run)', () => {
|
|
46
|
+
const env = {};
|
|
47
|
+
const resolve = vi.fn();
|
|
48
|
+
const exists = vi.fn();
|
|
49
|
+
const warn = vi.fn();
|
|
50
|
+
applyEsbuildBinaryPath(makeOpts({
|
|
51
|
+
dirname: '/Users/dev/code/dimina-kit/packages/devkit/dist',
|
|
52
|
+
env,
|
|
53
|
+
resolve,
|
|
54
|
+
exists,
|
|
55
|
+
warn,
|
|
56
|
+
}));
|
|
57
|
+
expect(env.ESBUILD_BINARY_PATH).toBeUndefined();
|
|
58
|
+
expect(resolve).not.toHaveBeenCalled();
|
|
59
|
+
expect(exists).not.toHaveBeenCalled();
|
|
60
|
+
expect(warn).not.toHaveBeenCalled();
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
describe('applyEsbuildBinaryPath — platform-correct binary subpath', () => {
|
|
64
|
+
it('resolves the win32 package root exe, not bin/esbuild (the Windows bug)', () => {
|
|
65
|
+
const resolve = vi.fn(() => 'C:\\app\\app.asar\\node_modules\\@esbuild\\win32-x64\\esbuild.exe');
|
|
66
|
+
applyEsbuildBinaryPath(makeOpts({ platform: 'win32', arch: 'x64', resolve }));
|
|
67
|
+
expect(resolve).toHaveBeenCalledWith('@esbuild/win32-x64/esbuild.exe');
|
|
68
|
+
expect(resolve).not.toHaveBeenCalledWith('@esbuild/win32-x64/bin/esbuild');
|
|
69
|
+
});
|
|
70
|
+
it('resolves the darwin bin/esbuild subpath', () => {
|
|
71
|
+
const resolve = vi.fn(() => '/app/app.asar/node_modules/@esbuild/darwin-arm64/bin/esbuild');
|
|
72
|
+
applyEsbuildBinaryPath(makeOpts({ platform: 'darwin', arch: 'arm64', resolve }));
|
|
73
|
+
expect(resolve).toHaveBeenCalledWith('@esbuild/darwin-arm64/bin/esbuild');
|
|
74
|
+
});
|
|
75
|
+
it('resolves the linux bin/esbuild subpath', () => {
|
|
76
|
+
const resolve = vi.fn(() => '/app/app.asar/node_modules/@esbuild/linux-x64/bin/esbuild');
|
|
77
|
+
applyEsbuildBinaryPath(makeOpts({ platform: 'linux', arch: 'x64', resolve }));
|
|
78
|
+
expect(resolve).toHaveBeenCalledWith('@esbuild/linux-x64/bin/esbuild');
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
describe('applyEsbuildBinaryPath — app.asar → app.asar.unpacked rewrite', () => {
|
|
82
|
+
it('rewrites the first app.asar\\ segment to app.asar.unpacked\\ on a Windows backslash path', () => {
|
|
83
|
+
const env = {};
|
|
84
|
+
const resolve = vi.fn(() => 'C:\\Users\\x\\AppData\\app.asar\\node_modules\\@esbuild\\win32-x64\\esbuild.exe');
|
|
85
|
+
applyEsbuildBinaryPath(makeOpts({ platform: 'win32', arch: 'x64', env, resolve }));
|
|
86
|
+
expect(env.ESBUILD_BINARY_PATH).toBe('C:\\Users\\x\\AppData\\app.asar.unpacked\\node_modules\\@esbuild\\win32-x64\\esbuild.exe');
|
|
87
|
+
});
|
|
88
|
+
it('rewrites the first app.asar/ segment to app.asar.unpacked/ on a POSIX forward-slash path', () => {
|
|
89
|
+
const env = {};
|
|
90
|
+
const resolve = vi.fn(() => '/Applications/MyApp.app/Contents/Resources/app.asar/node_modules/@esbuild/darwin-arm64/bin/esbuild');
|
|
91
|
+
applyEsbuildBinaryPath(makeOpts({ platform: 'darwin', arch: 'arm64', dirname: '/Applications/MyApp.app/Contents/Resources/app.asar/packages/devkit/dist', env, resolve }));
|
|
92
|
+
expect(env.ESBUILD_BINARY_PATH).toBe('/Applications/MyApp.app/Contents/Resources/app.asar.unpacked/node_modules/@esbuild/darwin-arm64/bin/esbuild');
|
|
93
|
+
});
|
|
94
|
+
it('does not double-rewrite a resolved path that already contains app.asar.unpacked', () => {
|
|
95
|
+
const env = {};
|
|
96
|
+
const resolve = vi.fn(() => '/Applications/MyApp.app/Contents/Resources/app.asar.unpacked/node_modules/@esbuild/darwin-arm64/bin/esbuild');
|
|
97
|
+
applyEsbuildBinaryPath(makeOpts({ platform: 'darwin', arch: 'arm64', dirname: '/Applications/MyApp.app/Contents/Resources/app.asar/packages/devkit/dist', env, resolve }));
|
|
98
|
+
expect(env.ESBUILD_BINARY_PATH).toBe('/Applications/MyApp.app/Contents/Resources/app.asar.unpacked/node_modules/@esbuild/darwin-arm64/bin/esbuild');
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
describe('applyEsbuildBinaryPath — resolve failure (the swallowed-throw half of the bug)', () => {
|
|
102
|
+
it('does not throw, leaves env unset, and warns once naming the platform package when resolve throws', () => {
|
|
103
|
+
const env = {};
|
|
104
|
+
const resolve = vi.fn(() => {
|
|
105
|
+
throw new Error("Cannot find module '@esbuild/win32-x64/esbuild.exe'");
|
|
106
|
+
});
|
|
107
|
+
const warn = vi.fn();
|
|
108
|
+
expect(() => {
|
|
109
|
+
applyEsbuildBinaryPath(makeOpts({ platform: 'win32', arch: 'x64', env, resolve, warn }));
|
|
110
|
+
}).not.toThrow();
|
|
111
|
+
expect(env.ESBUILD_BINARY_PATH).toBeUndefined();
|
|
112
|
+
expect(warn).toHaveBeenCalledTimes(1);
|
|
113
|
+
expect(warn.mock.calls[0]?.[0]).toEqual(expect.stringContaining('@esbuild/win32-x64'));
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
describe('applyEsbuildBinaryPath — unpacked-path existence check', () => {
|
|
117
|
+
it('still sets ESBUILD_BINARY_PATH and warns once with an asarUnpack hint when the unpacked path does not exist', () => {
|
|
118
|
+
const env = {};
|
|
119
|
+
const resolve = vi.fn(() => 'C:\\app\\app.asar\\node_modules\\@esbuild\\win32-x64\\esbuild.exe');
|
|
120
|
+
const exists = vi.fn(() => false);
|
|
121
|
+
const warn = vi.fn();
|
|
122
|
+
applyEsbuildBinaryPath(makeOpts({ platform: 'win32', arch: 'x64', env, resolve, exists, warn }));
|
|
123
|
+
expect(env.ESBUILD_BINARY_PATH).toBe('C:\\app\\app.asar.unpacked\\node_modules\\@esbuild\\win32-x64\\esbuild.exe');
|
|
124
|
+
expect(warn).toHaveBeenCalledTimes(1);
|
|
125
|
+
expect(warn.mock.calls[0]?.[0]).toEqual(expect.stringContaining('asarUnpack'));
|
|
126
|
+
});
|
|
127
|
+
it('never warns when the unpacked path exists', () => {
|
|
128
|
+
const resolve = vi.fn(() => 'C:\\app\\app.asar\\node_modules\\@esbuild\\win32-x64\\esbuild.exe');
|
|
129
|
+
const exists = vi.fn(() => true);
|
|
130
|
+
const warn = vi.fn();
|
|
131
|
+
applyEsbuildBinaryPath(makeOpts({ platform: 'win32', arch: 'x64', resolve, exists, warn }));
|
|
132
|
+
expect(warn).not.toHaveBeenCalled();
|
|
133
|
+
});
|
|
134
|
+
});
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAgB,eAAe,EAAiB,MAAM,qBAAqB,CAAA;AAEvF,OAAO,KAAK,EAAE,oBAAoB,EAAE,2BAA2B,EAAE,MAAM,6BAA6B,CAAA;AAEpG,OAAO,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAA;AAC/D,YAAY,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AACpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AACzD,YAAY,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACzE,OAAO,EAAE,0BAA0B,EAAE,MAAM,6BAA6B,CAAA;AACxE,YAAY,EACX,oBAAoB,EACpB,2BAA2B,EAC3B,YAAY,EACZ,YAAY,GACZ,MAAM,6BAA6B,CAAA;AAYpC;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CACzC,IAAI,CAAC,EAAE,2BAA2B,GAChC,oBAAoB,CAKtB;AAoDD,MAAM,WAAW,OAAO;IACvB,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;CACZ;AAED,MAAM,WAAW,cAAc;IAC9B,OAAO,EAAE,OAAO,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC1B;AAED,MAAM,WAAW,kBAAkB;IAClC,WAAW,EAAE,MAAM,CAAA;IACnB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB;;;;OAIG;IACH,SAAS,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAA;IAC5E,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,oFAAoF;IACpF,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;IACtB,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC;;;;OAIG;IACH,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAA;IACxC;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAA;CACvC;AAuFD,wBAAsB,WAAW,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,cAAc,CAAC,CA0JnF;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CACnC,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,EACpC,cAAc,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,GACrC;IAAE,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAAC,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAiEtD"}
|
package/dist/index.js
CHANGED
|
@@ -6,6 +6,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
6
6
|
import { createServer } from 'node:http';
|
|
7
7
|
import { createRequire } from 'node:module';
|
|
8
8
|
import chokidar from 'chokidar';
|
|
9
|
+
import { applyEsbuildBinaryPath } from './esbuild-binary-path.js';
|
|
9
10
|
import { createRebuildScheduler } from './rebuild-scheduler.js';
|
|
10
11
|
import { createCompileWorker } from './compile-worker.js';
|
|
11
12
|
import { createCompileWorkerStandby } from './compile-worker-standby.js';
|
|
@@ -50,21 +51,20 @@ function refillStandby() {
|
|
|
50
51
|
}
|
|
51
52
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
52
53
|
const require = createRequire(import.meta.url);
|
|
53
|
-
// esbuild's native binary
|
|
54
|
-
//
|
|
55
|
-
//
|
|
56
|
-
//
|
|
57
|
-
//
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
54
|
+
// Packaged inside app.asar, esbuild's native binary must be spawned from
|
|
55
|
+
// app.asar.unpacked — resolved via require.resolve so the hoisting depth
|
|
56
|
+
// (pnpm vs npm layouts) is never hard-coded. The per-platform binary layout
|
|
57
|
+
// and every failure diagnostic live in esbuild-binary-path.ts. The env var is
|
|
58
|
+
// set before any compile worker forks, and forks inherit it.
|
|
59
|
+
applyEsbuildBinaryPath({
|
|
60
|
+
dirname: __dirname,
|
|
61
|
+
env: process.env,
|
|
62
|
+
platform: process.platform,
|
|
63
|
+
arch: process.arch,
|
|
64
|
+
resolve: require.resolve,
|
|
65
|
+
exists: fs.existsSync,
|
|
66
|
+
warn: console.warn,
|
|
67
|
+
});
|
|
68
68
|
function getRandomPort() {
|
|
69
69
|
return new Promise((resolve, reject) => {
|
|
70
70
|
const srv = createServer();
|
|
@@ -309,7 +309,13 @@ export function createProjectWatcher(projectPath, onChange, onWatcherError) {
|
|
|
309
309
|
// (rel starts with '..') must never be ignored.
|
|
310
310
|
if (!rel || rel.startsWith('..'))
|
|
311
311
|
return false;
|
|
312
|
-
|
|
312
|
+
// `node_modules` (any depth) is excluded like vite/webpack exclude
|
|
313
|
+
// it: the compiler never reads it directly (its real npm input is
|
|
314
|
+
// the built `miniprogram_npm/`, which stays watched), while watching
|
|
315
|
+
// it makes chokidar hold thousands of per-directory fs.watch handles
|
|
316
|
+
// — a multi-second `watcher.close()` on session close, a slow
|
|
317
|
+
// initial scan, and spurious rebuilds on dependency churn.
|
|
318
|
+
return rel.split('/').some(seg => seg.startsWith('.') || seg === 'node_modules');
|
|
313
319
|
},
|
|
314
320
|
persistent: true,
|
|
315
321
|
ignoreInitial: true,
|
|
@@ -186,4 +186,72 @@ describe('createProjectWatcher', () => {
|
|
|
186
186
|
await settle(500);
|
|
187
187
|
expect(onChange).not.toHaveBeenCalled();
|
|
188
188
|
});
|
|
189
|
+
it('stays quiet when a file inside node_modules changes', async () => {
|
|
190
|
+
// Why this matters: a project with 87 dependencies puts chokidar under
|
|
191
|
+
// watch for 1232+ directories inside node_modules, making close() take
|
|
192
|
+
// seconds and letting dependency-internal file churn (postinstall
|
|
193
|
+
// scripts, lockfile-driven reinstalls) trigger spurious rebuilds.
|
|
194
|
+
// node_modules anywhere under the project — not just at the root — must
|
|
195
|
+
// be excluded from the watch entirely.
|
|
196
|
+
const nodeModulesDir = path.join(tempDir, 'node_modules', 'lodash');
|
|
197
|
+
fs.mkdirSync(nodeModulesDir, { recursive: true });
|
|
198
|
+
const target = path.join(nodeModulesDir, 'index.js');
|
|
199
|
+
fs.writeFileSync(target, '// vendored');
|
|
200
|
+
const onChange = vi.fn();
|
|
201
|
+
handle = createProjectWatcher(tempDir, onChange);
|
|
202
|
+
await handle.ready;
|
|
203
|
+
fs.writeFileSync(target, '// vendored, changed');
|
|
204
|
+
await settle(500);
|
|
205
|
+
expect(onChange).not.toHaveBeenCalled();
|
|
206
|
+
});
|
|
207
|
+
it('stays quiet when a file inside a nested package node_modules changes', async () => {
|
|
208
|
+
// Why this matters: node_modules can recur at any depth (npm/pnpm
|
|
209
|
+
// dependency trees, workspace packages with their own node_modules).
|
|
210
|
+
// A watcher that only ignores the top-level node_modules segment would
|
|
211
|
+
// still get flooded by these nested trees.
|
|
212
|
+
const nestedNodeModules = path.join(tempDir, 'packages', 'a', 'node_modules', 'b');
|
|
213
|
+
fs.mkdirSync(nestedNodeModules, { recursive: true });
|
|
214
|
+
const target = path.join(nestedNodeModules, 'x.js');
|
|
215
|
+
fs.writeFileSync(target, '// nested vendored');
|
|
216
|
+
const onChange = vi.fn();
|
|
217
|
+
handle = createProjectWatcher(tempDir, onChange);
|
|
218
|
+
await handle.ready;
|
|
219
|
+
fs.writeFileSync(target, '// nested vendored, changed');
|
|
220
|
+
await settle(500);
|
|
221
|
+
expect(onChange).not.toHaveBeenCalled();
|
|
222
|
+
});
|
|
223
|
+
it('fires onChange for changes inside miniprogram_npm', async () => {
|
|
224
|
+
// Why this matters: miniprogram_npm is the mini-app compiler's real
|
|
225
|
+
// input — the built npm output that the compiler reads directly, not a
|
|
226
|
+
// vendor tree to hide. A node_modules-ignoring rule that over-matches
|
|
227
|
+
// (e.g. any path segment containing "node_modules" as a substring, or a
|
|
228
|
+
// blanket "*npm*" pattern) would wrongly silence this directory too.
|
|
229
|
+
const npmDir = path.join(tempDir, 'miniprogram_npm', 'some-pkg');
|
|
230
|
+
fs.mkdirSync(npmDir, { recursive: true });
|
|
231
|
+
const target = path.join(npmDir, 'index.js');
|
|
232
|
+
fs.writeFileSync(target, '// built npm output');
|
|
233
|
+
const onChange = vi.fn();
|
|
234
|
+
handle = createProjectWatcher(tempDir, onChange);
|
|
235
|
+
await settle();
|
|
236
|
+
fs.writeFileSync(target, '// built npm output, changed');
|
|
237
|
+
await vi.waitFor(() => expect(onChange).toHaveBeenCalled(), { timeout: 2000 });
|
|
238
|
+
});
|
|
239
|
+
it('fires onChange for source edits when the project itself sits under an ancestor node_modules directory', async () => {
|
|
240
|
+
// Why this matters: same shape as the dotted-ancestor regression above —
|
|
241
|
+
// a project can legitimately live inside a node_modules directory (e.g.
|
|
242
|
+
// a demo/fixture project bundled inside a pnpm-installed package). Only
|
|
243
|
+
// node_modules segments *under* projectPath should be ignored; an
|
|
244
|
+
// ancestor node_modules segment must not disable the watch for the
|
|
245
|
+
// project's own source.
|
|
246
|
+
const ancestorNodeModules = path.join(tempDir, 'node_modules', 'some-demo-pkg');
|
|
247
|
+
const projectRoot = path.join(ancestorNodeModules, 'demo-app');
|
|
248
|
+
fs.mkdirSync(projectRoot, { recursive: true });
|
|
249
|
+
const target = path.join(projectRoot, 'app.json');
|
|
250
|
+
fs.writeFileSync(target, '{}');
|
|
251
|
+
const onChange = vi.fn();
|
|
252
|
+
handle = createProjectWatcher(projectRoot, onChange);
|
|
253
|
+
await settle();
|
|
254
|
+
fs.writeFileSync(target, '{"a":1}');
|
|
255
|
+
await vi.waitFor(() => expect(onChange).toHaveBeenCalled(), { timeout: 2000 });
|
|
256
|
+
});
|
|
189
257
|
});
|