@jk2908/cxx 0.3.1 → 0.3.4
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 +0 -3
- package/dist/build.d.ts +19 -11
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +204 -82
- package/dist/build.js.map +6 -5
- package/dist/logger.d.ts +1 -1
- package/dist/logger.d.ts.map +1 -1
- package/dist/next/cxx-loader.cjs +109 -58
- package/dist/next/cxx-loader.cjs.map +7 -7
- package/dist/next/index.d.ts +5 -3
- package/dist/next/index.d.ts.map +1 -1
- package/dist/next/index.js +219 -107
- package/dist/next/index.js.map +7 -6
- package/dist/types.d.ts +1 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/utils.d.ts +0 -16
- package/dist/utils.d.ts.map +1 -1
- package/dist/vite/index.d.ts +1 -7
- package/dist/vite/index.d.ts.map +1 -1
- package/dist/vite/index.js +240 -247
- package/dist/vite/index.js.map +7 -7
- package/package.json +6 -6
- package/src/build.ts +202 -73
- package/src/logger.ts +2 -7
- package/src/next/cxx-loader.js +8 -1
- package/src/next/index.ts +25 -27
- package/src/types.ts +1 -7
- package/src/utils.ts +0 -79
- package/src/vite/index.ts +60 -100
package/dist/vite/index.js
CHANGED
|
@@ -14,6 +14,86 @@ var PKG_NAME = `@jk2908/${NAME}`;
|
|
|
14
14
|
var GENERATED_DIR = `.${NAME}`;
|
|
15
15
|
var AUTOGEN_MSG = `// auto-generated by ${NAME}`;
|
|
16
16
|
|
|
17
|
+
// src/logger.ts
|
|
18
|
+
var LEVELS = {
|
|
19
|
+
debug: 0,
|
|
20
|
+
info: 1,
|
|
21
|
+
warn: 2,
|
|
22
|
+
error: 3,
|
|
23
|
+
fatal: 4
|
|
24
|
+
};
|
|
25
|
+
var defaultLevel = process.env["NODE_ENV"] === "production" ? "error" : "info";
|
|
26
|
+
|
|
27
|
+
class Logger {
|
|
28
|
+
name;
|
|
29
|
+
#level = defaultLevel;
|
|
30
|
+
constructor(name) {
|
|
31
|
+
this.name = name;
|
|
32
|
+
}
|
|
33
|
+
static toError(err) {
|
|
34
|
+
return err instanceof Error ? err : new Error(String(err), { cause: err });
|
|
35
|
+
}
|
|
36
|
+
static print(err) {
|
|
37
|
+
if (err instanceof Error) {
|
|
38
|
+
return err.message + (err.stack ? `
|
|
39
|
+
${err.stack}` : "");
|
|
40
|
+
}
|
|
41
|
+
if (typeof err === "object" && err !== null) {
|
|
42
|
+
try {
|
|
43
|
+
return JSON.stringify(err, null, 2);
|
|
44
|
+
} catch {
|
|
45
|
+
return String(err);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return String(err);
|
|
49
|
+
}
|
|
50
|
+
set level(level) {
|
|
51
|
+
this.#level = level;
|
|
52
|
+
}
|
|
53
|
+
get level() {
|
|
54
|
+
return this.#level;
|
|
55
|
+
}
|
|
56
|
+
log(level, message, error) {
|
|
57
|
+
if (LEVELS[level] < LEVELS[this.level])
|
|
58
|
+
return;
|
|
59
|
+
const entry = {
|
|
60
|
+
ts: Date.now(),
|
|
61
|
+
level,
|
|
62
|
+
message
|
|
63
|
+
};
|
|
64
|
+
if (level === "error" || level === "fatal") {
|
|
65
|
+
entry.error = error ? Logger.toError(error) : new Error(message);
|
|
66
|
+
}
|
|
67
|
+
const line = `${this.name ? `[${this.name}]` : ""} [${entry.ts}] [${level.toUpperCase()}] ${message}`;
|
|
68
|
+
const extra = entry.error ? `
|
|
69
|
+
${Logger.print(entry.error)}` : "";
|
|
70
|
+
if (level === "warn") {
|
|
71
|
+
console.warn(line, extra);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
if (level === "error" || level === "fatal") {
|
|
75
|
+
console.error(line, extra);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
console.log(line, extra);
|
|
79
|
+
}
|
|
80
|
+
debug(...messages) {
|
|
81
|
+
this.log("debug", messages.join(" "));
|
|
82
|
+
}
|
|
83
|
+
info(...messages) {
|
|
84
|
+
this.log("info", messages.join(" "));
|
|
85
|
+
}
|
|
86
|
+
warn(...messages) {
|
|
87
|
+
this.log("warn", messages.join(" "));
|
|
88
|
+
}
|
|
89
|
+
error(message, error) {
|
|
90
|
+
this.log("error", message, error === undefined ? undefined : Logger.toError(error));
|
|
91
|
+
}
|
|
92
|
+
fatal(message, error) {
|
|
93
|
+
this.log("fatal", message, error === undefined ? undefined : Logger.toError(error));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
17
97
|
// src/lru.ts
|
|
18
98
|
var CACHE_MAX_SIZE = 100;
|
|
19
99
|
|
|
@@ -56,51 +136,12 @@ function capitalise(str) {
|
|
|
56
136
|
function pascalise(str) {
|
|
57
137
|
return str.split(/[ _-]+/).map(capitalise).join("");
|
|
58
138
|
}
|
|
59
|
-
function createBatcher(wait, flush) {
|
|
60
|
-
const pending = new Map;
|
|
61
|
-
let timer = null;
|
|
62
|
-
let running = false;
|
|
63
|
-
async function drain() {
|
|
64
|
-
if (running)
|
|
65
|
-
return;
|
|
66
|
-
running = true;
|
|
67
|
-
try {
|
|
68
|
-
while (pending.size > 0) {
|
|
69
|
-
const batch = new Map(pending);
|
|
70
|
-
pending.clear();
|
|
71
|
-
await flush(batch);
|
|
72
|
-
}
|
|
73
|
-
} finally {
|
|
74
|
-
running = false;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
function schedule() {
|
|
78
|
-
if (timer)
|
|
79
|
-
clearTimeout(timer);
|
|
80
|
-
timer = setTimeout(() => {
|
|
81
|
-
timer = null;
|
|
82
|
-
drain();
|
|
83
|
-
}, wait);
|
|
84
|
-
}
|
|
85
|
-
return {
|
|
86
|
-
add(key, value) {
|
|
87
|
-
pending.set(key, value);
|
|
88
|
-
schedule();
|
|
89
|
-
},
|
|
90
|
-
flush() {
|
|
91
|
-
if (timer) {
|
|
92
|
-
clearTimeout(timer);
|
|
93
|
-
timer = null;
|
|
94
|
-
}
|
|
95
|
-
return drain();
|
|
96
|
-
}
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
139
|
|
|
100
140
|
// src/build.ts
|
|
141
|
+
var logger = new Logger(NAME);
|
|
101
142
|
var decoder = new TextDecoder;
|
|
143
|
+
var encoder = new TextEncoder;
|
|
102
144
|
var collectCache = new LruCache;
|
|
103
|
-
var fileCache = new LruCache;
|
|
104
145
|
var writeChain = Promise.resolve();
|
|
105
146
|
function isENOENT(err) {
|
|
106
147
|
return err instanceof Error && "code" in err && err.code === "ENOENT";
|
|
@@ -108,7 +149,12 @@ function isENOENT(err) {
|
|
|
108
149
|
async function writeFileAtomic(filePath, content) {
|
|
109
150
|
const tmp = `${filePath}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
110
151
|
await fs.writeFile(tmp, content);
|
|
111
|
-
|
|
152
|
+
try {
|
|
153
|
+
await fs.rename(tmp, filePath);
|
|
154
|
+
} catch (err) {
|
|
155
|
+
await fs.rm(tmp, { force: true });
|
|
156
|
+
throw err;
|
|
157
|
+
}
|
|
112
158
|
}
|
|
113
159
|
function enqueueWrite(task) {
|
|
114
160
|
const run = writeChain.then(task, task);
|
|
@@ -120,20 +166,14 @@ function enqueueWrite(task) {
|
|
|
120
166
|
return run;
|
|
121
167
|
}
|
|
122
168
|
async function maybeWrite(filePath, content) {
|
|
123
|
-
const cached = fileCache.get(filePath);
|
|
124
|
-
if (cached === content)
|
|
125
|
-
return false;
|
|
126
169
|
try {
|
|
127
|
-
if (await fs.readFile(filePath, "utf-8") === content)
|
|
128
|
-
fileCache.set(filePath, content);
|
|
170
|
+
if (await fs.readFile(filePath, "utf-8") === content)
|
|
129
171
|
return false;
|
|
130
|
-
}
|
|
131
172
|
} catch (err) {
|
|
132
173
|
if (!isENOENT(err))
|
|
133
174
|
throw err;
|
|
134
175
|
}
|
|
135
176
|
await writeFileAtomic(filePath, content);
|
|
136
|
-
fileCache.set(filePath, content);
|
|
137
177
|
return true;
|
|
138
178
|
}
|
|
139
179
|
var regex = /((?:export\s+)?)(?:const|var|let)\s*\[(\w*)\s*,?\s*(\w*)\s*,?\s*(\w*)\s*\]\s*=\s*cxx(?:\s*`([\s\S]*?)`|\s*\.\s*tag(?:\s*<[^>]+>)?\s*\(\s*(['"])(.*?)\6\s*\)\s*`([\s\S]*?)`)/gm;
|
|
@@ -169,14 +209,23 @@ function collect(source, id, config) {
|
|
|
169
209
|
matched = true;
|
|
170
210
|
const [, exp, varOne, varTwo, varThree, plainCss, , tag, taggedCss] = match;
|
|
171
211
|
const css = plainCss ?? taggedCss;
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
212
|
+
const matchIndex = match.index;
|
|
213
|
+
const { code, exports: classExports = {} } = (() => {
|
|
214
|
+
try {
|
|
215
|
+
return transform({
|
|
216
|
+
minify: true,
|
|
217
|
+
cssModules: true,
|
|
218
|
+
filename: id,
|
|
219
|
+
code: encoder.encode(css)
|
|
220
|
+
});
|
|
221
|
+
} catch (err) {
|
|
222
|
+
const line = source.slice(0, matchIndex).split(`
|
|
223
|
+
`).length;
|
|
224
|
+
throw new Error(`Failed to compile cxx CSS at ${id}:${line}`, { cause: err });
|
|
225
|
+
}
|
|
226
|
+
})();
|
|
178
227
|
const href = hashCss(code);
|
|
179
|
-
const styles = Object.fromEntries(Object.entries(
|
|
228
|
+
const styles = Object.fromEntries(Object.entries(classExports).map(([k, v]) => [k, v.name]));
|
|
180
229
|
if (tag) {
|
|
181
230
|
const suffix = typeof config.typeSuffix === "string" ? config.typeSuffix : config.typeSuffix === false ? "" : "Classes";
|
|
182
231
|
const tagWithSuffix = pascalise(`${tag}${suffix}`);
|
|
@@ -196,7 +245,7 @@ function collect(source, id, config) {
|
|
|
196
245
|
if (varThree) {
|
|
197
246
|
statements.push(`${exp}const ${varThree} = ${JSON.stringify(href)}`);
|
|
198
247
|
}
|
|
199
|
-
magic.overwrite(
|
|
248
|
+
magic.overwrite(matchIndex, matchIndex + match[0].length, statements.join(`
|
|
200
249
|
`));
|
|
201
250
|
}
|
|
202
251
|
const template = magic.toString();
|
|
@@ -222,16 +271,14 @@ async function writeRenderedTypes(tags) {
|
|
|
222
271
|
const filePath = path.join(GENERATED_DIR, "index.d.ts");
|
|
223
272
|
if (!tags.size) {
|
|
224
273
|
await fs.rm(filePath, { force: true });
|
|
225
|
-
fileCache.delete(filePath);
|
|
226
274
|
return;
|
|
227
275
|
}
|
|
228
276
|
await fs.mkdir(GENERATED_DIR, { recursive: true });
|
|
229
277
|
await maybeWrite(filePath, renderTypes(tags));
|
|
230
278
|
}
|
|
231
|
-
function writeTypes(tags) {
|
|
232
|
-
return enqueueWrite(() => writeRenderedTypes(tags));
|
|
233
|
-
}
|
|
234
279
|
var PARTIAL_DIR = path.join(GENERATED_DIR, "tags");
|
|
280
|
+
var REBUILD_WAIT = 100;
|
|
281
|
+
var rebuildTimer = null;
|
|
235
282
|
function partialPath(filePath) {
|
|
236
283
|
return path.join(PARTIAL_DIR, `${createHash("sha1").update(filePath).digest("hex")}.json`);
|
|
237
284
|
}
|
|
@@ -241,10 +288,12 @@ async function readPartials() {
|
|
|
241
288
|
entries = await fs.readdir(PARTIAL_DIR);
|
|
242
289
|
} catch (err) {
|
|
243
290
|
if (isENOENT(err))
|
|
244
|
-
return new Map;
|
|
291
|
+
return { tags: new Map, duplicates: [] };
|
|
245
292
|
throw err;
|
|
246
293
|
}
|
|
247
294
|
const tags = new Map;
|
|
295
|
+
const fileByTag = new Map;
|
|
296
|
+
const duplicates = [];
|
|
248
297
|
for (const entry of entries) {
|
|
249
298
|
if (!entry.endsWith(".json"))
|
|
250
299
|
continue;
|
|
@@ -255,19 +304,28 @@ async function readPartials() {
|
|
|
255
304
|
} catch {
|
|
256
305
|
continue;
|
|
257
306
|
}
|
|
307
|
+
let source;
|
|
258
308
|
try {
|
|
259
|
-
await fs.
|
|
309
|
+
source = await fs.readFile(parsed.file, "utf-8");
|
|
260
310
|
} catch {
|
|
261
311
|
await fs.rm(partial, { force: true });
|
|
262
312
|
continue;
|
|
263
313
|
}
|
|
314
|
+
if (!source.includes("cxx")) {
|
|
315
|
+
await fs.rm(partial, { force: true });
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
264
318
|
for (const [name, classes] of Object.entries(parsed.tags)) {
|
|
265
|
-
|
|
266
|
-
|
|
319
|
+
const existing = fileByTag.get(name);
|
|
320
|
+
if (existing) {
|
|
321
|
+
duplicates.push({ tag: name, files: [existing, parsed.file] });
|
|
322
|
+
continue;
|
|
323
|
+
}
|
|
324
|
+
fileByTag.set(name, parsed.file);
|
|
267
325
|
tags.set(name, classes);
|
|
268
326
|
}
|
|
269
327
|
}
|
|
270
|
-
return tags;
|
|
328
|
+
return { tags, duplicates };
|
|
271
329
|
}
|
|
272
330
|
function writeTypesForFile(filePath, tags) {
|
|
273
331
|
return enqueueWrite(async () => {
|
|
@@ -278,201 +336,149 @@ function writeTypesForFile(filePath, tags) {
|
|
|
278
336
|
} else {
|
|
279
337
|
await writeFileAtomic(partial, JSON.stringify({ file: filePath, tags: Object.fromEntries(tags) }));
|
|
280
338
|
}
|
|
281
|
-
|
|
339
|
+
}).then(() => scheduleRebuild()).catch((err) => {
|
|
340
|
+
logger.error(`failed to write types for ${filePath}`, err);
|
|
282
341
|
});
|
|
283
342
|
}
|
|
284
|
-
function
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
343
|
+
function scheduleRebuild() {
|
|
344
|
+
if (rebuildTimer)
|
|
345
|
+
clearTimeout(rebuildTimer);
|
|
346
|
+
rebuildTimer = setTimeout(() => {
|
|
347
|
+
rebuildTimer = null;
|
|
348
|
+
flushTypes().catch((err) => {
|
|
349
|
+
logger.error("failed to generate types", err);
|
|
350
|
+
});
|
|
351
|
+
}, REBUILD_WAIT);
|
|
290
352
|
}
|
|
291
|
-
function
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
if (tags.has(name))
|
|
296
|
-
throw new DuplicateTagError(name);
|
|
297
|
-
tags.set(name, classes);
|
|
298
|
-
}
|
|
353
|
+
function flushTypes() {
|
|
354
|
+
if (rebuildTimer) {
|
|
355
|
+
clearTimeout(rebuildTimer);
|
|
356
|
+
rebuildTimer = null;
|
|
299
357
|
}
|
|
300
|
-
return
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
import fs2 from "node:fs/promises";
|
|
306
|
-
import path2 from "node:path";
|
|
307
|
-
|
|
308
|
-
// src/logger.ts
|
|
309
|
-
var LEVELS = {
|
|
310
|
-
debug: 0,
|
|
311
|
-
info: 1,
|
|
312
|
-
warn: 2,
|
|
313
|
-
error: 3,
|
|
314
|
-
fatal: 4
|
|
315
|
-
};
|
|
316
|
-
var defaultLevel = process.env["NODE_ENV"] === "production" ? "error" : "info";
|
|
317
|
-
|
|
318
|
-
class Logger {
|
|
319
|
-
name;
|
|
320
|
-
#level;
|
|
321
|
-
constructor(level, name) {
|
|
322
|
-
this.name = name;
|
|
323
|
-
this.#level = level ?? defaultLevel;
|
|
324
|
-
}
|
|
325
|
-
static toError(err) {
|
|
326
|
-
return err instanceof Error ? err : new Error(String(err), { cause: err });
|
|
327
|
-
}
|
|
328
|
-
static print(err) {
|
|
329
|
-
if (err instanceof Error) {
|
|
330
|
-
return err.message + (err.stack ? `
|
|
331
|
-
${err.stack}` : "");
|
|
358
|
+
return enqueueWrite(async () => {
|
|
359
|
+
const { tags, duplicates } = await readPartials();
|
|
360
|
+
await writeRenderedTypes(tags);
|
|
361
|
+
for (const duplicate of duplicates) {
|
|
362
|
+
logger.error(`Tag "${duplicate.tag}" is defined in multiple files: ${duplicate.files.join(", ")}`);
|
|
332
363
|
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
var SOURCE_EXTENSIONS = new Set([".js", ".jsx", ".ts", ".tsx"]);
|
|
367
|
+
var SKIP_DIRS = new Set(["node_modules", "dist", "build", "coverage", "out"]);
|
|
368
|
+
async function runWithConcurrency(items, concurrency, fn) {
|
|
369
|
+
let cursor = 0;
|
|
370
|
+
const workers = Array.from({ length: Math.min(concurrency, items.length) }, async () => {
|
|
371
|
+
while (cursor < items.length) {
|
|
372
|
+
const index = cursor++;
|
|
373
|
+
await fn(items[index]);
|
|
339
374
|
}
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
375
|
+
});
|
|
376
|
+
await Promise.all(workers);
|
|
377
|
+
}
|
|
378
|
+
async function refreshFile(file, config) {
|
|
379
|
+
let source;
|
|
380
|
+
try {
|
|
381
|
+
source = await fs.readFile(file, "utf-8");
|
|
382
|
+
} catch (err) {
|
|
383
|
+
if (!isENOENT(err))
|
|
384
|
+
throw err;
|
|
385
|
+
await writeTypesForFile(file, new Map);
|
|
386
|
+
return;
|
|
347
387
|
}
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
entry.error = error ? Logger.toError(error) : new Error(message);
|
|
358
|
-
}
|
|
359
|
-
const line = `${this.name ? `[${this.name}]` : ""} [${entry.ts}] [${level.toUpperCase()}] ${message}`;
|
|
360
|
-
const extra = entry.error ? `
|
|
361
|
-
${Logger.print(entry.error)}` : "";
|
|
362
|
-
if (level === "warn") {
|
|
363
|
-
console.warn(line, extra);
|
|
364
|
-
return;
|
|
365
|
-
}
|
|
366
|
-
if (level === "error" || level === "fatal") {
|
|
367
|
-
console.error(line, extra);
|
|
388
|
+
const tags = source.includes("cxx") ? collect(source, file, config).tags : new Map;
|
|
389
|
+
await writeTypesForFile(file, tags);
|
|
390
|
+
}
|
|
391
|
+
async function seedTypes(root, config) {
|
|
392
|
+
async function scan(dir) {
|
|
393
|
+
let entries;
|
|
394
|
+
try {
|
|
395
|
+
entries = await fs.readdir(dir, { withFileTypes: true });
|
|
396
|
+
} catch {
|
|
368
397
|
return;
|
|
369
398
|
}
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
this.log("error", message, error === undefined ? undefined : Logger.toError(error));
|
|
383
|
-
}
|
|
384
|
-
fatal(message, error) {
|
|
385
|
-
this.log("fatal", message, error === undefined ? undefined : Logger.toError(error));
|
|
399
|
+
await runWithConcurrency(entries, 8, async (entry) => {
|
|
400
|
+
if (entry.name.startsWith(".") || SKIP_DIRS.has(entry.name))
|
|
401
|
+
return;
|
|
402
|
+
const full = path.join(dir, entry.name);
|
|
403
|
+
if (entry.isDirectory()) {
|
|
404
|
+
await scan(full);
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
if (!SOURCE_EXTENSIONS.has(path.extname(entry.name)))
|
|
408
|
+
return;
|
|
409
|
+
await refreshFile(full, config);
|
|
410
|
+
});
|
|
386
411
|
}
|
|
412
|
+
await scan(root);
|
|
413
|
+
await flushTypes();
|
|
414
|
+
}
|
|
415
|
+
function tagsToType(tags) {
|
|
416
|
+
return [...tags].toSorted(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([tag, classes]) => {
|
|
417
|
+
const union = classes.length > 0 ? classes.toSorted((a, b) => a.length - b.length).map((c) => `'${c}'`).join(" | ") : "never";
|
|
418
|
+
return ` export type ${tag} = ${union}`;
|
|
419
|
+
}).join(`
|
|
420
|
+
`);
|
|
387
421
|
}
|
|
388
422
|
|
|
389
423
|
// src/vite/index.ts
|
|
390
|
-
|
|
424
|
+
import { realpathSync } from "node:fs";
|
|
425
|
+
import path2 from "node:path";
|
|
391
426
|
function normaliseWatchPath(p) {
|
|
392
427
|
return p.replace(/\\/g, "/");
|
|
393
428
|
}
|
|
394
429
|
function cxx(pluginConfig = {}) {
|
|
395
|
-
const
|
|
396
|
-
tagsByFile: new Map
|
|
397
|
-
};
|
|
398
|
-
const logger = new Logger;
|
|
430
|
+
const logger2 = new Logger;
|
|
399
431
|
let watchRoot = normaliseWatchPath(process.cwd());
|
|
432
|
+
let seeded = false;
|
|
433
|
+
const realpaths = new Map;
|
|
400
434
|
function resolveWatchFile(filePath) {
|
|
401
435
|
const absolutePath = path2.resolve(watchRoot, filePath);
|
|
402
436
|
const parentPath = path2.dirname(absolutePath);
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
437
|
+
let resolvedParentPath = realpaths.get(parentPath);
|
|
438
|
+
if (resolvedParentPath === undefined) {
|
|
439
|
+
try {
|
|
440
|
+
resolvedParentPath = normaliseWatchPath(realpathSync.native(parentPath));
|
|
441
|
+
} catch {
|
|
442
|
+
resolvedParentPath = normaliseWatchPath(parentPath);
|
|
443
|
+
}
|
|
444
|
+
realpaths.set(parentPath, resolvedParentPath);
|
|
408
445
|
}
|
|
446
|
+
return normaliseWatchPath(path2.join(resolvedParentPath, path2.basename(absolutePath)));
|
|
409
447
|
}
|
|
410
448
|
function watchedFile(filePath) {
|
|
411
449
|
const resolvedPath = resolveWatchFile(filePath);
|
|
412
|
-
if (resolvedPath.startsWith(`${watchRoot}/`) && !resolvedPath.includes(`/${GENERATED_DIR}/`) &&
|
|
450
|
+
if (resolvedPath.startsWith(`${watchRoot}/`) && !resolvedPath.includes(`/${GENERATED_DIR}/`) && SOURCE_EXTENSIONS.has(path2.extname(resolvedPath))) {
|
|
413
451
|
return resolvedPath;
|
|
414
452
|
}
|
|
415
453
|
return null;
|
|
416
454
|
}
|
|
417
|
-
async function
|
|
418
|
-
const hadTags = buildContext.tagsByFile.has(file);
|
|
455
|
+
async function handleEvent(event, file) {
|
|
419
456
|
if (event === "unlink") {
|
|
420
|
-
|
|
421
|
-
return;
|
|
422
|
-
buildContext.tagsByFile.delete(file);
|
|
423
|
-
await writeTypes(flattenTags(buildContext.tagsByFile));
|
|
424
|
-
logger.info(`updated types from ${file}`);
|
|
425
|
-
return;
|
|
426
|
-
}
|
|
427
|
-
let source;
|
|
428
|
-
try {
|
|
429
|
-
source = await fs2.readFile(file, "utf-8");
|
|
430
|
-
} catch (err) {
|
|
431
|
-
if (!isENOENT(err))
|
|
432
|
-
throw err;
|
|
433
|
-
if (!hadTags)
|
|
434
|
-
return;
|
|
435
|
-
buildContext.tagsByFile.delete(file);
|
|
436
|
-
await writeTypes(flattenTags(buildContext.tagsByFile));
|
|
437
|
-
logger.info(`updated types from ${file}`);
|
|
438
|
-
return;
|
|
439
|
-
}
|
|
440
|
-
const { tags } = collect(source, file, pluginConfig);
|
|
441
|
-
const hasTags = tags.size > 0;
|
|
442
|
-
if (hasTags) {
|
|
443
|
-
buildContext.tagsByFile.set(file, tags);
|
|
457
|
+
await writeTypesForFile(file, new Map);
|
|
444
458
|
} else {
|
|
445
|
-
|
|
459
|
+
await refreshFile(file, pluginConfig);
|
|
446
460
|
}
|
|
447
|
-
|
|
461
|
+
logger2.info(`updated types from ${file}`);
|
|
462
|
+
}
|
|
463
|
+
function onWatch(event) {
|
|
464
|
+
return (p) => {
|
|
465
|
+
const file = watchedFile(p);
|
|
466
|
+
if (!file)
|
|
467
|
+
return;
|
|
468
|
+
handleEvent(event, file).catch((err) => {
|
|
469
|
+
logger2.error(`failed to update types from ${file}`, err);
|
|
470
|
+
});
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
async function seed() {
|
|
474
|
+
if (seeded)
|
|
448
475
|
return;
|
|
449
|
-
|
|
450
|
-
|
|
476
|
+
seeded = true;
|
|
477
|
+
await seedTypes(watchRoot, pluginConfig);
|
|
451
478
|
}
|
|
452
|
-
const queue = createBatcher(75, async (batch) => {
|
|
453
|
-
for (const [file, event] of batch) {
|
|
454
|
-
try {
|
|
455
|
-
await onEvent(event, file);
|
|
456
|
-
} catch (err) {
|
|
457
|
-
logger.error(`failed to update types from ${file}`, err);
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
});
|
|
461
479
|
return {
|
|
462
480
|
name: "cxx",
|
|
463
481
|
enforce: "pre",
|
|
464
|
-
config() {
|
|
465
|
-
const ignore = pluginConfig.watch?.ignore;
|
|
466
|
-
if (!ignore?.length)
|
|
467
|
-
return null;
|
|
468
|
-
return {
|
|
469
|
-
server: {
|
|
470
|
-
watch: {
|
|
471
|
-
ignored: ignore
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
};
|
|
475
|
-
},
|
|
476
482
|
configResolved(config) {
|
|
477
483
|
const root = pluginConfig.watch?.root ?? config.root;
|
|
478
484
|
try {
|
|
@@ -480,6 +486,10 @@ function cxx(pluginConfig = {}) {
|
|
|
480
486
|
} catch {
|
|
481
487
|
watchRoot = normaliseWatchPath(root);
|
|
482
488
|
}
|
|
489
|
+
realpaths.clear();
|
|
490
|
+
},
|
|
491
|
+
async buildStart() {
|
|
492
|
+
await seed();
|
|
483
493
|
},
|
|
484
494
|
transform(code, id) {
|
|
485
495
|
if (id.startsWith("\x00") || id.includes("/node_modules/"))
|
|
@@ -487,40 +497,23 @@ function cxx(pluginConfig = {}) {
|
|
|
487
497
|
if (!code.includes("cxx"))
|
|
488
498
|
return null;
|
|
489
499
|
const cleanId = id.split("?")[0];
|
|
490
|
-
if (!
|
|
500
|
+
if (!SOURCE_EXTENSIONS.has(path2.extname(cleanId)))
|
|
491
501
|
return null;
|
|
492
502
|
const { template, tags, map } = collect(code, cleanId, pluginConfig);
|
|
493
503
|
if (template === code)
|
|
494
504
|
return null;
|
|
495
|
-
|
|
496
|
-
if (tags.size) {
|
|
497
|
-
buildContext.tagsByFile.set(resolved, tags);
|
|
498
|
-
} else {
|
|
499
|
-
buildContext.tagsByFile.delete(resolved);
|
|
500
|
-
}
|
|
505
|
+
writeTypesForFile(resolveWatchFile(cleanId), tags);
|
|
501
506
|
return {
|
|
502
507
|
code: template,
|
|
503
508
|
map
|
|
504
509
|
};
|
|
505
510
|
},
|
|
506
511
|
configureServer(server) {
|
|
507
|
-
|
|
508
|
-
server.watcher.on("add", (
|
|
509
|
-
const file = watchedFile(p);
|
|
510
|
-
if (file)
|
|
511
|
-
queue.add(file, "add");
|
|
512
|
-
}).on("change", (p) => {
|
|
513
|
-
const file = watchedFile(p);
|
|
514
|
-
if (file)
|
|
515
|
-
queue.add(file, "change");
|
|
516
|
-
}).on("unlink", (p) => {
|
|
517
|
-
const file = watchedFile(p);
|
|
518
|
-
if (file)
|
|
519
|
-
queue.add(file, "unlink");
|
|
520
|
-
});
|
|
512
|
+
logger2.info("Watching for changes...");
|
|
513
|
+
server.watcher.on("add", onWatch("add")).on("change", onWatch("change")).on("unlink", onWatch("unlink"));
|
|
521
514
|
},
|
|
522
515
|
async writeBundle() {
|
|
523
|
-
await
|
|
516
|
+
await flushTypes();
|
|
524
517
|
}
|
|
525
518
|
};
|
|
526
519
|
}
|
|
@@ -528,4 +521,4 @@ export {
|
|
|
528
521
|
cxx as default
|
|
529
522
|
};
|
|
530
523
|
|
|
531
|
-
//# debugId=
|
|
524
|
+
//# debugId=294541A31A244C8364756E2164756E21
|