@ttsc/unplugin 0.30.2 → 0.30.3
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/lib/bun.js +3 -16
- package/lib/bun.js.map +1 -1
- package/lib/bun.mjs +3 -16
- package/lib/bun.mjs.map +1 -1
- package/lib/core/esbuild.d.cts +4 -0
- package/lib/core/esbuild.d.mts +4 -0
- package/lib/core/esbuild.d.ts +4 -0
- package/lib/core/esbuild.js +110 -0
- package/lib/core/esbuild.js.map +1 -0
- package/lib/core/esbuild.mjs +108 -0
- package/lib/core/esbuild.mjs.map +1 -0
- package/lib/core/index.js +49 -89
- package/lib/core/index.js.map +1 -1
- package/lib/core/index.mjs +50 -90
- package/lib/core/index.mjs.map +1 -1
- package/lib/core/transform.d.cts +5 -3
- package/lib/core/transform.d.mts +5 -3
- package/lib/core/transform.d.ts +5 -3
- package/lib/core/transform.js +25 -25
- package/lib/core/transform.js.map +1 -1
- package/lib/core/transform.mjs +25 -25
- package/lib/core/transform.mjs.map +1 -1
- package/lib/core/viteServe.d.cts +16 -38
- package/lib/core/viteServe.d.mts +16 -38
- package/lib/core/viteServe.d.ts +16 -38
- package/lib/core/viteServe.js +234 -98
- package/lib/core/viteServe.js.map +1 -1
- package/lib/core/viteServe.mjs +235 -98
- package/lib/core/viteServe.mjs.map +1 -1
- package/package.json +4 -3
- package/src/bun.ts +2 -17
- package/src/core/esbuild.ts +127 -0
- package/src/core/index.ts +51 -100
- package/src/core/transform.ts +30 -28
- package/src/core/viteServe.ts +275 -164
package/src/core/viteServe.ts
CHANGED
|
@@ -1,21 +1,16 @@
|
|
|
1
|
+
import { type FSWatcher, watch } from "chokidar";
|
|
1
2
|
import fs from "node:fs";
|
|
2
3
|
import path from "node:path";
|
|
3
|
-
import type { ITtscCompilerTransformation } from "ttsc";
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
* cannot observe a path whose parent directories do not exist yet. One `stat`
|
|
15
|
-
* every half second per unavailable path is negligible against a dev server's
|
|
16
|
-
* baseline. Rich inputs replay only the predicates the compiler recorded.
|
|
17
|
-
*/
|
|
18
|
-
const MISSING_INPUT_POLL_INTERVAL = 500;
|
|
5
|
+
import {
|
|
6
|
+
type TtscWatchInput,
|
|
7
|
+
type TtscWatchInputBaseline,
|
|
8
|
+
type TtscWatchInputEvidence,
|
|
9
|
+
captureWatchInputBaseline,
|
|
10
|
+
pathIdentityKey,
|
|
11
|
+
validateGraphInputObservation,
|
|
12
|
+
watchInputEvidenceMatchesBaseline,
|
|
13
|
+
} from "./transform";
|
|
19
14
|
|
|
20
15
|
/** One module node inside a Vite module graph; opaque to this module. */
|
|
21
16
|
type ViteModuleNodeLike = object;
|
|
@@ -55,188 +50,304 @@ export interface ViteDevServerLike {
|
|
|
55
50
|
ws?: ViteHotChannelLike;
|
|
56
51
|
}
|
|
57
52
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
* Vite serve treats every transform-context `addWatchFile()` registration as an
|
|
63
|
-
* added import: `TransformPluginContext.addWatchFile` stores the path in
|
|
64
|
-
* `_addedImports`, and `vite:import-analysis` resolves each entry like a real
|
|
65
|
-
* import of the transformed module. A missing path or an existing directory
|
|
66
|
-
* that failed a compiler file predicate then fails that resolve and turns the
|
|
67
|
-
* importer's first request into a 500, even though the transform succeeded.
|
|
68
|
-
*
|
|
69
|
-
* This registry is the serve-only replacement for those registrations. Each
|
|
70
|
-
* path is polled until its exact compiler predicate observation changes; legacy
|
|
71
|
-
* envelopes retain their exists-or-file availability check. Its importers are
|
|
72
|
-
* then invalidated in the server's module graphs and one full-reload is sent,
|
|
73
|
-
* so the next request retransforms against the new resolution winner. The
|
|
74
|
-
* project transform cache re-validates the compiler observation, so the
|
|
75
|
-
* retransform recompiles instead of replaying.
|
|
76
|
-
*/
|
|
77
|
-
export interface ViteServeMissingInputWatch {
|
|
78
|
-
/** Adopt the dev server whose module graphs creation events invalidate. */
|
|
79
|
-
attach(server: ViteDevServerLike): void;
|
|
80
|
-
/** Stop every poll; safe to call repeatedly. */
|
|
81
|
-
dispose(): void;
|
|
82
|
-
/**
|
|
83
|
-
* Report whether a dev server has ever been attached. This is not a liveness
|
|
84
|
-
* predicate — the reference intentionally survives the server's close (see
|
|
85
|
-
* {@link dispose}) — so route decisions must also gate on the resolved
|
|
86
|
-
* config's `command`, as the adapter does.
|
|
87
|
-
*/
|
|
88
|
-
serving(): boolean;
|
|
89
|
-
/** Register one unsafe watch input and its exact recorded condition. */
|
|
90
|
-
watch(
|
|
91
|
-
input: string,
|
|
92
|
-
importer: string,
|
|
93
|
-
condition: ViteServeInputWatchCondition,
|
|
94
|
-
): void;
|
|
53
|
+
interface InputCondition {
|
|
54
|
+
baseline?: TtscWatchInputBaseline;
|
|
55
|
+
evidence?: TtscWatchInputEvidence;
|
|
56
|
+
importers: Set<string>;
|
|
95
57
|
}
|
|
96
58
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
59
|
+
interface InputEntry {
|
|
60
|
+
conditions: Map<string, InputCondition>;
|
|
61
|
+
file: string;
|
|
62
|
+
/** Missing paths and directory predicates still need the predicate poll. */
|
|
63
|
+
poll: boolean;
|
|
64
|
+
observed: boolean;
|
|
65
|
+
links: Set<string>;
|
|
66
|
+
}
|
|
102
67
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
importers: Set<string>;
|
|
107
|
-
spelling: string;
|
|
68
|
+
interface LinkedPath {
|
|
69
|
+
target: string | undefined;
|
|
70
|
+
inputs: Set<InputEntry>;
|
|
108
71
|
}
|
|
109
72
|
|
|
110
|
-
/**
|
|
111
|
-
export
|
|
112
|
-
|
|
113
|
-
|
|
73
|
+
/** Serve-time compiler dependencies never enter Vite's runtime import graph. */
|
|
74
|
+
export interface ViteServeInputWatch {
|
|
75
|
+
attach(server: ViteDevServerLike): void;
|
|
76
|
+
dispose(): Promise<void>;
|
|
77
|
+
replace(
|
|
78
|
+
importer: string,
|
|
79
|
+
inputs: readonly TtscWatchInput[],
|
|
80
|
+
failed?: boolean,
|
|
81
|
+
): void;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* One filesystem subscription per unique input, shared by all served modules.
|
|
86
|
+
*
|
|
87
|
+
* Vite resolves transform-context addWatchFile as a runtime import, including
|
|
88
|
+
* type-only .server files and non-module plugin assets. Use a separate watcher
|
|
89
|
+
* for compiler inputs, including node_modules, which Vite's watcher ignores.
|
|
90
|
+
* Ordinary files use events after their initial subscription is observed.
|
|
91
|
+
* Missing spellings and directory predicates keep a shared predicate poll.
|
|
92
|
+
* Linked files also share topology checks by directory because retargeting a
|
|
93
|
+
* junction need not emit events on its previously watched descendants.
|
|
94
|
+
*/
|
|
95
|
+
export function createViteServeInputWatch(): ViteServeInputWatch {
|
|
96
|
+
const entries = new Map<string, InputEntry>();
|
|
97
|
+
const importerInputs = new Map<string, Map<string, string>>();
|
|
98
|
+
const pending = new Set<InputEntry>();
|
|
99
|
+
const links = new Map<string, LinkedPath>();
|
|
114
100
|
let server: ViteDevServerLike | undefined;
|
|
101
|
+
let watcher: FSWatcher | undefined;
|
|
102
|
+
let poller: NodeJS.Timeout | undefined;
|
|
103
|
+
let flushTimer: NodeJS.Timeout | undefined;
|
|
104
|
+
let failed = false;
|
|
115
105
|
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
106
|
+
const remove = (entry: InputEntry): void => {
|
|
107
|
+
entries.delete(entry.file);
|
|
108
|
+
watcher?.unwatch(entry.file);
|
|
109
|
+
for (const file of entry.links) {
|
|
110
|
+
const link = links.get(file);
|
|
111
|
+
link?.inputs.delete(entry);
|
|
112
|
+
if (link?.inputs.size === 0) links.delete(file);
|
|
119
113
|
}
|
|
120
|
-
clearInterval(poller);
|
|
121
|
-
poller = undefined;
|
|
122
114
|
};
|
|
123
|
-
|
|
115
|
+
|
|
116
|
+
const check = (selected: Iterable<InputEntry>): void => {
|
|
124
117
|
const importers = new Set<string>();
|
|
125
|
-
for (const
|
|
126
|
-
if (
|
|
127
|
-
|
|
118
|
+
for (const entry of selected) {
|
|
119
|
+
if (entries.get(entry.file) !== entry) continue;
|
|
120
|
+
let baseline: TtscWatchInputBaseline | undefined;
|
|
121
|
+
for (const [key, condition] of entry.conditions) {
|
|
122
|
+
const state = condition.evidence?.state;
|
|
123
|
+
let changed: boolean;
|
|
124
|
+
if (state?.codec === "predicates") {
|
|
125
|
+
changed =
|
|
126
|
+
validateGraphInputObservation(entry.file, state.observation)
|
|
127
|
+
.length !== 0;
|
|
128
|
+
} else {
|
|
129
|
+
baseline ??= captureWatchInputBaseline(entry.file);
|
|
130
|
+
changed =
|
|
131
|
+
baseline === undefined ||
|
|
132
|
+
(condition.evidence?.state !== undefined
|
|
133
|
+
? !watchInputEvidenceMatchesBaseline(condition.evidence, baseline)
|
|
134
|
+
: JSON.stringify(condition.baseline) !==
|
|
135
|
+
JSON.stringify(baseline));
|
|
136
|
+
}
|
|
137
|
+
if (!changed) continue;
|
|
138
|
+
for (const importer of condition.importers) importers.add(importer);
|
|
139
|
+
entry.conditions.delete(key);
|
|
128
140
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
importers.add(importer);
|
|
141
|
+
if (entry.conditions.size === 0) {
|
|
142
|
+
remove(entry);
|
|
132
143
|
}
|
|
133
144
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
145
|
+
if (server !== undefined && importers.size !== 0) {
|
|
146
|
+
invalidateImporters(server, importers);
|
|
147
|
+
sendFullReload(server);
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const enqueue = (file: string, observed: boolean): void => {
|
|
152
|
+
const absolute = path.resolve(file);
|
|
153
|
+
const direct = entries.get(absolute);
|
|
154
|
+
if (direct !== undefined && observed) direct.observed = true;
|
|
155
|
+
for (const candidate of [absolute, path.dirname(absolute)]) {
|
|
156
|
+
const entry = entries.get(candidate);
|
|
157
|
+
if (entry !== undefined) pending.add(entry);
|
|
137
158
|
}
|
|
138
|
-
|
|
139
|
-
|
|
159
|
+
if (pending.size === 0 || flushTimer !== undefined) return;
|
|
160
|
+
flushTimer = setTimeout(() => {
|
|
161
|
+
flushTimer = undefined;
|
|
162
|
+
const selected = [...pending];
|
|
163
|
+
pending.clear();
|
|
164
|
+
check(selected);
|
|
165
|
+
}, 0);
|
|
166
|
+
flushTimer.unref();
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
const ensureWatcher = (): FSWatcher => {
|
|
170
|
+
if (watcher !== undefined) return watcher;
|
|
171
|
+
// Chokidar's persistent:false backend omits its native error listener on
|
|
172
|
+
// Windows. Keep the owned subscription alive until dispose() closes it.
|
|
173
|
+
const active = watch([], { depth: 0, ignoreInitial: false });
|
|
174
|
+
watcher = active;
|
|
175
|
+
// Initial add events compare compiler-time evidence too: an edit between
|
|
176
|
+
// compilation and asynchronous watcher setup must not be missed.
|
|
177
|
+
active.on("all", (event, file) => {
|
|
178
|
+
if (watcher === active)
|
|
179
|
+
enqueue(file, event === "add" || event === "addDir");
|
|
180
|
+
});
|
|
181
|
+
active.on("error", () => {
|
|
182
|
+
if (watcher === active) failed = true;
|
|
183
|
+
});
|
|
184
|
+
poller = setInterval(() => {
|
|
185
|
+
const selected = new Set(
|
|
186
|
+
[...entries.values()].filter(
|
|
187
|
+
(entry) => failed || entry.poll || !entry.observed,
|
|
188
|
+
),
|
|
189
|
+
);
|
|
190
|
+
// Files reached through one linked directory share one topology check.
|
|
191
|
+
// Content edits remain event-driven; retargeting a junction does not
|
|
192
|
+
// reliably emit an event on its previously watched descendants.
|
|
193
|
+
for (const [file, link] of links) {
|
|
194
|
+
const target = realpath(file);
|
|
195
|
+
if (target !== link.target) {
|
|
196
|
+
link.target = target;
|
|
197
|
+
for (const entry of link.inputs) {
|
|
198
|
+
selected.add(entry);
|
|
199
|
+
entry.observed = false;
|
|
200
|
+
active.unwatch(entry.file);
|
|
201
|
+
active.add(entry.file);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
check(selected);
|
|
206
|
+
}, 500);
|
|
207
|
+
poller.unref();
|
|
208
|
+
return watcher;
|
|
140
209
|
};
|
|
141
210
|
|
|
142
211
|
return {
|
|
143
212
|
attach(next) {
|
|
144
213
|
server = next;
|
|
145
214
|
},
|
|
146
|
-
dispose() {
|
|
215
|
+
async dispose() {
|
|
147
216
|
entries.clear();
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
217
|
+
importerInputs.clear();
|
|
218
|
+
pending.clear();
|
|
219
|
+
links.clear();
|
|
220
|
+
if (poller !== undefined) clearInterval(poller);
|
|
221
|
+
if (flushTimer !== undefined) clearTimeout(flushTimer);
|
|
222
|
+
poller = undefined;
|
|
223
|
+
flushTimer = undefined;
|
|
224
|
+
const closing = watcher;
|
|
225
|
+
watcher = undefined;
|
|
226
|
+
failed = false;
|
|
227
|
+
await closing?.close();
|
|
228
|
+
// Retain the attached server across overlapping Vite restart containers.
|
|
158
229
|
},
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
230
|
+
replace(importer, inputs, failed = false) {
|
|
231
|
+
if (server === undefined) return;
|
|
232
|
+
importer = path.resolve(importer);
|
|
233
|
+
const previous =
|
|
234
|
+
importerInputs.get(importer) ?? new Map<string, string>();
|
|
235
|
+
if (failed) {
|
|
236
|
+
// An exception can omit the dependency whose deletion caused it.
|
|
237
|
+
// Keep the last successful spellings until a successful delivery can
|
|
238
|
+
// replace them, observing their current failed state for recovery.
|
|
239
|
+
const reported = new Set(
|
|
240
|
+
inputs.map((input) => path.resolve(input.file)),
|
|
241
|
+
);
|
|
242
|
+
inputs = [
|
|
243
|
+
...inputs,
|
|
244
|
+
...[...previous.keys()]
|
|
245
|
+
.filter((file) => !reported.has(file))
|
|
246
|
+
.map((file) => ({ file })),
|
|
247
|
+
];
|
|
248
|
+
}
|
|
249
|
+
const current = new Map<string, string>();
|
|
250
|
+
const added: string[] = [];
|
|
251
|
+
for (const input of inputs) {
|
|
252
|
+
const file = path.resolve(input.file);
|
|
253
|
+
const evidence = input.evidence;
|
|
254
|
+
const key = JSON.stringify(evidence ?? null);
|
|
255
|
+
current.set(file, key);
|
|
256
|
+
let entry = entries.get(file);
|
|
257
|
+
if (entry === undefined) {
|
|
258
|
+
entry = {
|
|
259
|
+
file,
|
|
260
|
+
conditions: new Map(),
|
|
261
|
+
poll: false,
|
|
262
|
+
observed: false,
|
|
263
|
+
links: new Set(),
|
|
264
|
+
};
|
|
265
|
+
entries.set(file, entry);
|
|
266
|
+
added.push(file);
|
|
267
|
+
const directory = path.dirname(file);
|
|
268
|
+
const directoryTarget = realpath(directory);
|
|
269
|
+
const fileTarget = realpath(file);
|
|
270
|
+
const linked = [
|
|
271
|
+
...(directoryTarget !== undefined &&
|
|
272
|
+
!sameSpelling(directory, directoryTarget)
|
|
273
|
+
? [directory]
|
|
274
|
+
: []),
|
|
275
|
+
...(fileTarget !== undefined &&
|
|
276
|
+
directoryTarget !== undefined &&
|
|
277
|
+
!sameSpelling(
|
|
278
|
+
fileTarget,
|
|
279
|
+
path.join(directoryTarget, path.basename(file)),
|
|
280
|
+
)
|
|
281
|
+
? [file]
|
|
282
|
+
: []),
|
|
283
|
+
];
|
|
284
|
+
for (const linkedFile of linked) {
|
|
285
|
+
let link = links.get(linkedFile);
|
|
286
|
+
if (link === undefined) {
|
|
287
|
+
link = { target: realpath(linkedFile), inputs: new Set() };
|
|
288
|
+
links.set(linkedFile, link);
|
|
289
|
+
}
|
|
290
|
+
link.inputs.add(entry);
|
|
291
|
+
entry.links.add(linkedFile);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
let condition = entry.conditions.get(key);
|
|
295
|
+
if (condition === undefined) {
|
|
296
|
+
condition = {
|
|
297
|
+
evidence,
|
|
298
|
+
baseline:
|
|
299
|
+
evidence?.state === undefined
|
|
300
|
+
? captureWatchInputBaseline(file)
|
|
301
|
+
: undefined,
|
|
302
|
+
importers: new Set(),
|
|
303
|
+
};
|
|
304
|
+
entry.conditions.set(key, condition);
|
|
305
|
+
}
|
|
306
|
+
condition.importers.add(importer);
|
|
307
|
+
const observation =
|
|
308
|
+
evidence?.state?.codec === "predicates"
|
|
309
|
+
? evidence.state.observation
|
|
310
|
+
: undefined;
|
|
311
|
+
entry.poll ||=
|
|
312
|
+
evidence?.missing === true ||
|
|
313
|
+
evidence?.unavailable !== undefined ||
|
|
314
|
+
(observation !== undefined &&
|
|
315
|
+
observation.fileExists !== true &&
|
|
316
|
+
observation.stat !== "file" &&
|
|
317
|
+
observation.readFile?.ok !== true) ||
|
|
318
|
+
(evidence?.state === undefined &&
|
|
319
|
+
condition.baseline?.fileExists !== true);
|
|
169
320
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
321
|
+
for (const [file, key] of previous) {
|
|
322
|
+
if (current.get(file) === key) continue;
|
|
323
|
+
const entry = entries.get(file);
|
|
324
|
+
const condition = entry?.conditions.get(key);
|
|
325
|
+
condition?.importers.delete(importer);
|
|
326
|
+
if (condition?.importers.size === 0) entry?.conditions.delete(key);
|
|
327
|
+
if (entry?.conditions.size === 0 && !current.has(file)) {
|
|
328
|
+
remove(entry);
|
|
329
|
+
}
|
|
179
330
|
}
|
|
331
|
+
importerInputs.set(importer, current);
|
|
332
|
+
if (added.length !== 0) ensureWatcher().add(added);
|
|
180
333
|
},
|
|
181
334
|
};
|
|
182
335
|
}
|
|
183
336
|
|
|
184
|
-
|
|
185
|
-
export function viteServeMissingInputWatchKey(
|
|
186
|
-
input: string,
|
|
187
|
-
condition: ViteServeInputWatchCondition,
|
|
188
|
-
): string {
|
|
189
|
-
// Missing aliases can share a physical parent now and later retarget or
|
|
190
|
-
// diverge. Neither predicate may let one lexical spelling answer for another.
|
|
191
|
-
const predicate =
|
|
192
|
-
typeof condition === "string"
|
|
193
|
-
? condition
|
|
194
|
-
: `predicates:${JSON.stringify([
|
|
195
|
-
condition.accessibleEntries === undefined
|
|
196
|
-
? null
|
|
197
|
-
: [
|
|
198
|
-
condition.accessibleEntries.directories,
|
|
199
|
-
condition.accessibleEntries.files,
|
|
200
|
-
],
|
|
201
|
-
condition.directoryExists ?? null,
|
|
202
|
-
condition.fileExists ?? null,
|
|
203
|
-
condition.readFile === undefined
|
|
204
|
-
? null
|
|
205
|
-
: condition.readFile.ok
|
|
206
|
-
? [true, condition.readFile.hash]
|
|
207
|
-
: [false],
|
|
208
|
-
condition.realpath === undefined
|
|
209
|
-
? null
|
|
210
|
-
: condition.realpath.ok
|
|
211
|
-
? [true, condition.realpath.path]
|
|
212
|
-
: [false],
|
|
213
|
-
condition.stat ?? null,
|
|
214
|
-
])}`;
|
|
215
|
-
return `${predicate}:${path.resolve(input)}`;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
/** Whether one registered condition no longer describes its lexical path. */
|
|
219
|
-
function viteServeInputWatchConditionChanged(
|
|
220
|
-
entry: IMissingInputEntry,
|
|
221
|
-
): boolean {
|
|
222
|
-
if (typeof entry.condition !== "string") {
|
|
223
|
-
return (
|
|
224
|
-
validateGraphInputObservation(entry.spelling, entry.condition).length !==
|
|
225
|
-
0
|
|
226
|
-
);
|
|
227
|
-
}
|
|
337
|
+
function realpath(file: string): string | undefined {
|
|
228
338
|
try {
|
|
229
|
-
|
|
230
|
-
return false;
|
|
231
|
-
}
|
|
232
|
-
return (
|
|
233
|
-
entry.condition === "exists" || !fs.statSync(entry.spelling).isDirectory()
|
|
234
|
-
);
|
|
339
|
+
return fs.realpathSync.native(file);
|
|
235
340
|
} catch {
|
|
236
|
-
return
|
|
341
|
+
return undefined;
|
|
237
342
|
}
|
|
238
343
|
}
|
|
239
344
|
|
|
345
|
+
function sameSpelling(left: string, right: string): boolean {
|
|
346
|
+
return process.platform === "win32"
|
|
347
|
+
? left.toLowerCase() === right.toLowerCase()
|
|
348
|
+
: left === right;
|
|
349
|
+
}
|
|
350
|
+
|
|
240
351
|
/**
|
|
241
352
|
* Invalidate every module-graph node of the registered importers so the next
|
|
242
353
|
* request retransforms them. Importers keep their original absolute spelling so
|