@jk2908/cxx 0.2.6 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,30 +1,41 @@
1
1
  import type { NextConfig } from 'next';
2
2
  import type { Logger } from '../logger.js';
3
- import type { PluginConfig, Tags } from '../types.js';
3
+ import type { PluginConfig } from '../types.js';
4
4
  /**
5
- * Transform one module and, in development, register it with the shared watcher state
5
+ * Transform one module and, in development, register it with the shared watcher state.
6
6
  */
7
7
  export declare function processFile(source: string, filePath: string, pluginConfig: PluginConfig, logger: Logger): {
8
8
  source: string;
9
+ config: string;
9
10
  template: string;
10
- tags: Tags;
11
- } | {
12
- template: string;
13
- tags: Tags;
11
+ tags: import("../types.js").Tags;
12
+ map: import("magic-string").SourceMap | null;
14
13
  };
15
- export declare function withCxx(nextConfig?: NextConfig, pluginConfig?: PluginConfig): Promise<{
14
+ export declare function withCxx(nextConfig?: NextConfig, pluginConfig?: PluginConfig): {
16
15
  turbopack: {
17
16
  rules: {
18
17
  "*.{tsx,jsx,ts,js}": ({
19
18
  loader: string;
20
19
  options: {
21
- pluginConfig: PluginConfig;
20
+ pluginConfig: {
21
+ watch: {
22
+ root: string;
23
+ ignore?: string[];
24
+ };
25
+ typeSuffix?: string | false;
26
+ };
22
27
  };
23
28
  } | import("next/dist/server/config-shared.js").TurbopackRuleConfigItem | import("next/dist/server/config-shared.js").TurbopackLoaderItem)[] | {
24
29
  loaders: ({
25
30
  loader: string;
26
31
  options: {
27
- pluginConfig: PluginConfig;
32
+ pluginConfig: {
33
+ watch: {
34
+ root: string;
35
+ ignore?: string[];
36
+ };
37
+ typeSuffix?: string | false;
38
+ };
28
39
  };
29
40
  } | import("next/dist/server/config-shared.js").TurbopackLoaderItem)[];
30
41
  as?: string;
@@ -41,6 +52,12 @@ export declare function withCxx(nextConfig?: NextConfig, pluginConfig?: PluginCo
41
52
  title?: string | RegExp;
42
53
  description?: string | RegExp;
43
54
  }>;
55
+ chunkLoadingGlobal?: string;
56
+ };
57
+ webpack(config: unknown, context: unknown): {
58
+ module?: {
59
+ rules?: unknown[];
60
+ };
44
61
  };
45
62
  allowedDevOrigins?: string[];
46
63
  exportPathMap?: (defaultMap: import("next/dist/server/config-shared.js").ExportPathMap, ctx: {
@@ -65,7 +82,6 @@ export declare function withCxx(nextConfig?: NextConfig, pluginConfig?: PluginCo
65
82
  };
66
83
  redirects?: () => Promise<import("next/dist/lib/load-custom-routes.js").Redirect[]> | import("next/dist/lib/load-custom-routes.js").Redirect[];
67
84
  excludeDefaultMomentLocales?: boolean;
68
- webpack?: import("next/dist/server/config-shared.js").NextJsWebpackConfig | null;
69
85
  trailingSlash?: boolean;
70
86
  env?: Record<string, string | undefined>;
71
87
  distDir?: string;
@@ -84,6 +100,7 @@ export declare function withCxx(nextConfig?: NextConfig, pluginConfig?: PluginCo
84
100
  generateBuildId?: () => string | null | Promise<string | null>;
85
101
  generateEtags?: boolean;
86
102
  pageExtensions?: string[];
103
+ instrumentationClientInject?: string[];
87
104
  compress?: boolean;
88
105
  poweredByHeader?: boolean;
89
106
  images?: import("next/dist/shared/lib/image-config.js").ImageConfig;
@@ -95,6 +112,7 @@ export declare function withCxx(nextConfig?: NextConfig, pluginConfig?: PluginCo
95
112
  pagesBufferLength?: number;
96
113
  };
97
114
  deploymentId?: string;
115
+ supportsImmutableAssets?: boolean;
98
116
  basePath?: string;
99
117
  sassOptions?: {
100
118
  implementation?: string;
@@ -148,6 +166,7 @@ export declare function withCxx(nextConfig?: NextConfig, pluginConfig?: PluginCo
148
166
  logging?: import("next/dist/server/config-shared.js").LoggingConfig | false;
149
167
  enablePrerenderSourceMaps?: boolean;
150
168
  cacheComponents?: boolean;
169
+ partialPrefetching?: boolean | "unstable_eager";
151
170
  cacheLife?: {
152
171
  [profile: string]: {
153
172
  stale?: number;
@@ -156,15 +175,17 @@ export declare function withCxx(nextConfig?: NextConfig, pluginConfig?: PluginCo
156
175
  };
157
176
  };
158
177
  expireTime?: number;
178
+ agentRules?: boolean;
159
179
  experimental?: import("next/dist/server/config-shared.js").ExperimentalConfig;
160
180
  bundlePagesRouterDependencies?: boolean;
161
181
  serverExternalPackages?: string[];
162
182
  outputFileTracingRoot?: string;
163
183
  outputFileTracingExcludes?: Record<string, string[]>;
164
184
  outputFileTracingIncludes?: Record<string, string[]>;
185
+ outputHashSalt?: string;
165
186
  watchOptions?: {
166
187
  pollIntervalMs?: number;
167
188
  };
168
189
  htmlLimitedBots?: RegExp;
169
- }>;
190
+ };
170
191
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/next/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAA;AAQtC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAC1C,OAAO,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,aAAa,CAAA;AA2DrD;;GAEG;AACH,wBAAgB,WAAW,CAC1B,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,YAAY,EAC1B,MAAM,EAAE,MAAM;;;;;;;EA8Cd;AAwBD,wBAAsB,OAAO,CAC5B,UAAU,GAAE,UAAe,EAC3B,YAAY,GAAE,YAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;iBA+Bk2E,CAAC;uBAAsC,CAAC;;;;;;;;;;;;;;;;mBAAsn5B,CAAC;kBAA+B,CAAC;gBAA6B,CAAC;;mBAAuD,CAAC;kBAA+B,CAAC;gBAA6B,CAAC;;;;;;;;;;;;;eAA8lF,CAAC;cAAwB,CAAC;cAAwB,CAAC;;;;;;;;;;;;gBAAo9D,CAAC;;;sBAAie,CAAC;yBAA4H,CAAC;;;;;sBAAkiB,CAAC;;;;;;;;;iBAA4+C,CAAC;;;;;6BAA+sB,CAAC;sBAAoC,CAAC;;aAAoC,CAAC;;6BAA0D,CAAC;oBAA8B,CAAC;0BAAkE,CAAC;;qBAA2C,CAAC;mBAAiC,CAAC;;wBAA+C,CAAC;eAAmD,CAAC;iBAA4C,CAAC;2BAAyC,CAAC;;cAA6L,CAAC;oBAAyP,CAAC;iCAA8S,CAAC;;;;;;;;;;;;yBAAm0D,CAAC;6BAAwC,CAAC;;;;;;;iBAAowB,CAAC;sBAAgC,CAAC;kBAA4B,CAAC;;;;;;;;;;;sBAA21C,CAAC;;;GAD514C"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/next/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAA;AAMtC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAC1C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAqI/C;;GAEG;AACH,wBAAgB,WAAW,CAC1B,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,YAAY,EAC1B,MAAM,EAAE,MAAM;;;;;;EAgBd;AAID,wBAAgB,OAAO,CAAC,UAAU,GAAE,UAAe,EAAE,YAAY,GAAE,YAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAsDkpE,CAAC;uBAAsC,CAAC;;;;oBApB5vE,OAAO,WAAW,OAAO;iBAI9B;YAAE,KAAK,CAAC,EAAE,OAAO,EAAE,CAAA;SAAE;;;;;;;;;;;;;;;mBAgBg05C,CAAC;kBAA+B,CAAC;gBAA6B,CAAC;;mBAAuD,CAAC;kBAA+B,CAAC;gBAA6B,CAAC;;;;;;;;;;;;eAA8lF,CAAC;cAAwB,CAAC;cAAwB,CAAC;;;;;;;;;;;;;gBAAkxE,CAAC;;;sBAAie,CAAC;yBAA4H,CAAC;;;;;;sBAAuyB,CAAC;;;;;;;;;iBAA4+C,CAAC;;;;;6BAA+sB,CAAC;sBAAoC,CAAC;;aAAoC,CAAC;;6BAA0D,CAAC;oBAA8B,CAAC;0BAAkE,CAAC;;qBAA2C,CAAC;mBAAiC,CAAC;;wBAA+C,CAAC;eAAmD,CAAC;iBAA4C,CAAC;2BAAyC,CAAC;;cAA6L,CAAC;oBAAyP,CAAC;iCAA8S,CAAC;;;;;;;;;;;;yBAAm0D,CAAC;6BAAwC,CAAC;;;;;;;;iBAAgxD,CAAC;sBAAgC,CAAC;kBAA4B,CAAC;;;;;;;;;;;;;sBAA+vE,CAAC;;;EADjp5D"}
@@ -1,8 +1,12 @@
1
+ import { createRequire } from "node:module";
2
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
3
+
1
4
  // src/build.ts
2
5
  import { createHash } from "node:crypto";
3
6
  import fs from "node:fs/promises";
4
7
  import path from "node:path";
5
8
  import { transform } from "lightningcss";
9
+ import MagicString from "magic-string";
6
10
 
7
11
  // src/config.ts
8
12
  var NAME = "cxx";
@@ -52,31 +56,73 @@ function capitalise(str) {
52
56
  function pascalise(str) {
53
57
  return str.split(/[ _-]+/).map(capitalise).join("");
54
58
  }
55
- function debounce(fn, wait) {
56
- let timeoutId = null;
57
- return (...args) => {
58
- if (timeoutId) {
59
- clearTimeout(timeoutId);
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;
60
75
  }
61
- timeoutId = setTimeout(() => {
62
- fn.apply(null, args);
76
+ }
77
+ function schedule() {
78
+ if (timer)
79
+ clearTimeout(timer);
80
+ timer = setTimeout(() => {
81
+ timer = null;
82
+ drain();
63
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
+ }
64
97
  };
65
98
  }
66
99
 
67
100
  // src/build.ts
101
+ var decoder = new TextDecoder;
68
102
  var collectCache = new LruCache;
69
103
  var fileCache = new LruCache;
104
+ var writeChain = Promise.resolve();
70
105
  function isENOENT(err) {
71
106
  return err instanceof Error && "code" in err && err.code === "ENOENT";
72
107
  }
108
+ async function writeFileAtomic(filePath, content) {
109
+ const tmp = `${filePath}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
110
+ await fs.writeFile(tmp, content);
111
+ await fs.rename(tmp, filePath);
112
+ }
113
+ function enqueueWrite(task) {
114
+ const run = writeChain.then(task, task);
115
+ writeChain = run.then(() => {
116
+ return;
117
+ }, () => {
118
+ return;
119
+ });
120
+ return run;
121
+ }
73
122
  async function maybeWrite(filePath, content) {
74
123
  const cached = fileCache.get(filePath);
75
- if (cached !== content) {
76
- await fs.writeFile(filePath, content);
77
- fileCache.set(filePath, content);
78
- return true;
79
- }
124
+ if (cached === content)
125
+ return false;
80
126
  try {
81
127
  if (await fs.readFile(filePath, "utf-8") === content) {
82
128
  fileCache.set(filePath, content);
@@ -86,11 +132,12 @@ async function maybeWrite(filePath, content) {
86
132
  if (!isENOENT(err))
87
133
  throw err;
88
134
  }
89
- await fs.writeFile(filePath, content);
135
+ await writeFileAtomic(filePath, content);
90
136
  fileCache.set(filePath, content);
91
137
  return true;
92
138
  }
93
- var regex = /(?:const|var|let)\s*\[(\w+|)\s*,?\s*(\w+|)\s*,?\s*(\w+|)\s*\]\s*=\s*cxx(?:\s*`([\s\S]*?)`|\s*\.\s*tag(?:\s*<[^>]+>)?\s*\(\s*(['"])(.*?)\5\s*\)\s*`([\s\S]*?)`)/gm;
139
+ 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;
140
+ var IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
94
141
  function hashCss(code) {
95
142
  return `cxx-${createHash("sha256").update(code).digest("base64url").slice(0, 12)}`;
96
143
  }
@@ -101,12 +148,26 @@ class DuplicateTagError extends Error {
101
148
  super(`Tag ${tag} has already been seen. Tags must be unique across your application`);
102
149
  }
103
150
  }
151
+
152
+ class InvalidTagError extends Error {
153
+ name = "InvalidTagError";
154
+ constructor(tag) {
155
+ super(`Tag "${tag}" does not map to a valid TypeScript identifier. ` + `Use letters, numbers, spaces, "_" or "-"`);
156
+ }
157
+ }
104
158
  function collect(source, id, config) {
105
- const tags = new Map;
159
+ const configKey = JSON.stringify(config);
106
160
  const cached = collectCache.get(id);
107
- if (cached?.source === source)
161
+ if (cached?.source === source && cached.config === configKey)
108
162
  return cached;
109
- const template = source.replace(regex, (_match, varOne, varTwo, varThree, plainCss, _quote, tag, taggedCss) => {
163
+ const tags = new Map;
164
+ const magic = new MagicString(source);
165
+ let matched = false;
166
+ regex.lastIndex = 0;
167
+ let match;
168
+ while (match = regex.exec(source)) {
169
+ matched = true;
170
+ const [, exp, varOne, varTwo, varThree, plainCss, , tag, taggedCss] = match;
110
171
  const css = plainCss ?? taggedCss;
111
172
  const { code, exports = {} } = transform({
112
173
  minify: true,
@@ -119,49 +180,112 @@ function collect(source, id, config) {
119
180
  if (tag) {
120
181
  const suffix = typeof config.typeSuffix === "string" ? config.typeSuffix : config.typeSuffix === false ? "" : "Classes";
121
182
  const tagWithSuffix = pascalise(`${tag}${suffix}`);
183
+ if (!IDENTIFIER.test(tagWithSuffix))
184
+ throw new InvalidTagError(tag);
122
185
  if (tags.has(tagWithSuffix))
123
186
  throw new DuplicateTagError(tagWithSuffix);
124
187
  tags.set(tagWithSuffix, Object.keys(styles));
125
188
  }
126
- return [
127
- varOne && `const ${varOne} = \`${code}\``,
128
- varTwo && `const ${varTwo} = ${JSON.stringify(styles)}`,
129
- varThree && `const ${varThree} = \`${href}\``
130
- ].filter(Boolean).join(`
189
+ const statements = [];
190
+ if (varOne) {
191
+ statements.push(`${exp}const ${varOne} = ${JSON.stringify(decoder.decode(code))}`);
192
+ }
193
+ if (varTwo) {
194
+ statements.push(`${exp}const ${varTwo} = ${JSON.stringify(styles)}`);
195
+ }
196
+ if (varThree) {
197
+ statements.push(`${exp}const ${varThree} = ${JSON.stringify(href)}`);
198
+ }
199
+ magic.overwrite(match.index, match.index + match[0].length, statements.join(`
200
+ `));
201
+ }
202
+ const template = magic.toString();
203
+ const map = matched ? magic.generateMap({ source: id, includeContent: true, hires: true }) : null;
204
+ const result = { source, config: configKey, template, tags, map };
205
+ collectCache.set(id, result);
206
+ return result;
207
+ }
208
+ function renderTypes(tags) {
209
+ return [
210
+ AUTOGEN_MSG,
211
+ "",
212
+ `import '${PKG_NAME}'`,
213
+ "",
214
+ `declare module '${PKG_NAME}' {`,
215
+ tagsToType(tags),
216
+ "}",
217
+ ""
218
+ ].join(`
131
219
  `);
132
- });
133
- collectCache.set(id, { source, template, tags });
134
- return {
135
- template,
136
- tags
137
- };
138
220
  }
139
- async function writeTypes(tags) {
221
+ async function writeRenderedTypes(tags) {
140
222
  const filePath = path.join(GENERATED_DIR, "index.d.ts");
141
223
  if (!tags.size) {
142
- try {
143
- await fs.rm(filePath, { force: true });
144
- fileCache.delete(filePath);
145
- } catch (err) {
146
- if (!isENOENT(err))
147
- throw err;
148
- }
224
+ await fs.rm(filePath, { force: true });
225
+ fileCache.delete(filePath);
149
226
  return;
150
227
  }
151
228
  await fs.mkdir(GENERATED_DIR, { recursive: true });
152
- await maybeWrite(filePath, `${AUTOGEN_MSG}
153
-
154
- import '${PKG_NAME}'
155
-
156
- declare module '${PKG_NAME}' {
157
- ${tagsToType(tags)}
158
- }
159
- `);
229
+ await maybeWrite(filePath, renderTypes(tags));
230
+ }
231
+ function writeTypes(tags) {
232
+ return enqueueWrite(() => writeRenderedTypes(tags));
233
+ }
234
+ var PARTIAL_DIR = path.join(GENERATED_DIR, "tags");
235
+ function partialPath(filePath) {
236
+ return path.join(PARTIAL_DIR, `${createHash("sha1").update(filePath).digest("hex")}.json`);
237
+ }
238
+ async function readPartials() {
239
+ let entries;
240
+ try {
241
+ entries = await fs.readdir(PARTIAL_DIR);
242
+ } catch (err) {
243
+ if (isENOENT(err))
244
+ return new Map;
245
+ throw err;
246
+ }
247
+ const tags = new Map;
248
+ for (const entry of entries) {
249
+ if (!entry.endsWith(".json"))
250
+ continue;
251
+ const partial = path.join(PARTIAL_DIR, entry);
252
+ let parsed;
253
+ try {
254
+ parsed = JSON.parse(await fs.readFile(partial, "utf-8"));
255
+ } catch {
256
+ continue;
257
+ }
258
+ try {
259
+ await fs.access(parsed.file);
260
+ } catch {
261
+ await fs.rm(partial, { force: true });
262
+ continue;
263
+ }
264
+ for (const [name, classes] of Object.entries(parsed.tags)) {
265
+ if (tags.has(name))
266
+ throw new DuplicateTagError(name);
267
+ tags.set(name, classes);
268
+ }
269
+ }
270
+ return tags;
271
+ }
272
+ function writeTypesForFile(filePath, tags) {
273
+ return enqueueWrite(async () => {
274
+ await fs.mkdir(PARTIAL_DIR, { recursive: true });
275
+ const partial = partialPath(filePath);
276
+ if (!tags.size) {
277
+ await fs.rm(partial, { force: true });
278
+ } else {
279
+ await writeFileAtomic(partial, JSON.stringify({ file: filePath, tags: Object.fromEntries(tags) }));
280
+ }
281
+ await writeRenderedTypes(await readPartials());
282
+ });
160
283
  }
161
284
  function tagsToType(tags) {
162
- return [...tags].map(([tag, classes]) => `
163
- export type ${tag} = ${classes.length > 0 ? classes.toSorted((a, b) => a.length - b.length).map((c) => `'${c}'`).join(" | ") : "never"}
164
- `).join(`
285
+ return [...tags].map(([tag, classes]) => {
286
+ const union = classes.length > 0 ? classes.toSorted((a, b) => a.length - b.length).map((c) => `'${c}'`).join(" | ") : "never";
287
+ return ` export type ${tag} = ${union}`;
288
+ }).join(`
165
289
  `);
166
290
  }
167
291
  function flattenTags(tagsByFile) {
@@ -180,106 +304,119 @@ function flattenTags(tagsByFile) {
180
304
  import fs2 from "node:fs/promises";
181
305
  import path2 from "node:path";
182
306
  import { fileURLToPath } from "node:url";
183
- import watcher from "@parcel/watcher";
184
307
  var nextDir = path2.dirname(fileURLToPath(import.meta.url));
185
308
  var loader = path2.join(nextDir, "cxx-loader.cjs");
186
- var tagsByFile = new Map;
187
309
  var watchedFiles = new Map;
188
- var subscriptionsByDir = new Map;
189
- async function rebuildFile(filePath, event) {
190
- const watchedFile = watchedFiles.get(filePath);
191
- if (!watchedFile)
310
+ var subscription = null;
311
+ var subscriptionPromise = null;
312
+ var cleanupRegistered = false;
313
+ var watcherLogger = null;
314
+ var DEFAULT_IGNORE = [
315
+ "**/node_modules/**",
316
+ "**/.next/**",
317
+ `**/${GENERATED_DIR}/**`,
318
+ "**/.git/**"
319
+ ];
320
+ async function onEvent(event, filePath) {
321
+ const watched = watchedFiles.get(filePath);
322
+ if (!watched)
192
323
  return;
193
- const { pluginConfig, logger } = watchedFile;
194
- const hadTags = tagsByFile.has(filePath);
324
+ const { pluginConfig, logger } = watched;
195
325
  if (event === "delete") {
196
- tagsByFile.delete(filePath);
197
- if (!hadTags)
198
- return;
199
- await writeTypes(flattenTags(tagsByFile));
326
+ await writeTypesForFile(filePath, new Map);
200
327
  logger.info(`updated types from ${filePath}`);
201
328
  return;
202
329
  }
330
+ let source;
203
331
  try {
204
- const source = await fs2.readFile(filePath, "utf-8");
205
- const { tags } = collect(source, filePath, pluginConfig);
206
- const hasTags = tags.size > 0;
207
- if (hasTags) {
208
- tagsByFile.set(filePath, tags);
209
- } else {
210
- tagsByFile.delete(filePath);
211
- }
212
- if (!hadTags && !hasTags)
213
- return;
214
- await writeTypes(flattenTags(tagsByFile));
215
- logger.info(`updated types from ${filePath}`);
332
+ source = await fs2.readFile(filePath, "utf-8");
216
333
  } catch (err) {
217
334
  if (!isENOENT(err))
218
335
  throw err;
219
- if (!hadTags)
220
- return;
221
- tagsByFile.delete(filePath);
222
- await writeTypes(flattenTags(tagsByFile));
336
+ await writeTypesForFile(filePath, new Map);
223
337
  logger.info(`updated types from ${filePath}`);
338
+ return;
224
339
  }
340
+ const { tags } = collect(source, filePath, pluginConfig);
341
+ await writeTypesForFile(filePath, tags);
342
+ logger.info(`updated types from ${filePath}`);
343
+ }
344
+ function ensureWatcher(pluginConfig, logger) {
345
+ watcherLogger = logger;
346
+ if (subscriptionPromise)
347
+ return;
348
+ const root = pluginConfig.watch?.root ?? process.cwd();
349
+ const ignore = [...DEFAULT_IGNORE, ...pluginConfig.watch?.ignore ?? []];
350
+ subscriptionPromise = (async () => {
351
+ const { subscribe } = await import("@parcel/watcher");
352
+ subscription = await subscribe(root, (watchErr, events) => {
353
+ if (watchErr) {
354
+ watcherLogger?.error(`failed to watch ${root}`, watchErr);
355
+ return;
356
+ }
357
+ for (const event of events) {
358
+ const filePath = path2.resolve(event.path);
359
+ if (!watchedFiles.has(filePath))
360
+ continue;
361
+ onEvent(event.type, filePath).catch((err) => {
362
+ watcherLogger?.error(`failed to update types from ${filePath}`, err);
363
+ });
364
+ }
365
+ }, { ignore });
366
+ registerCleanup();
367
+ })().catch((err) => {
368
+ watcherLogger?.error("failed to start cxx watcher", err);
369
+ subscriptionPromise = null;
370
+ });
371
+ }
372
+ async function cleanup() {
373
+ const active = subscription;
374
+ subscription = null;
375
+ subscriptionPromise = null;
376
+ watchedFiles.clear();
377
+ if (active)
378
+ await active.unsubscribe();
379
+ }
380
+ async function shutdown(signal) {
381
+ try {
382
+ await cleanup();
383
+ } finally {
384
+ process.kill(process.pid, signal);
385
+ }
386
+ }
387
+ function registerCleanup() {
388
+ if (cleanupRegistered)
389
+ return;
390
+ cleanupRegistered = true;
391
+ process.once("SIGINT", () => void shutdown("SIGINT"));
392
+ process.once("SIGTERM", () => void shutdown("SIGTERM"));
225
393
  }
226
394
  function processFile(source, filePath, pluginConfig, logger) {
227
395
  const resolvedFilePath = path2.resolve(filePath);
228
- const directoryPath = path2.dirname(resolvedFilePath);
229
- let subscription = subscriptionsByDir.get(directoryPath);
230
- if (true) {
396
+ if (process.env["NODE_ENV"] !== "production") {
231
397
  watchedFiles.set(resolvedFilePath, { pluginConfig, logger });
232
- if (!subscription) {
233
- subscription = watcher.subscribe(directoryPath, async (err, events) => {
234
- if (err) {
235
- logger.error(`failed to update types from ${directoryPath}`, err);
236
- return;
237
- }
238
- for (const event of events) {
239
- const eventFilePath = path2.resolve(event.path);
240
- if (!watchedFiles.has(eventFilePath))
241
- continue;
242
- try {
243
- await rebuildFile(eventFilePath, event.type);
244
- } catch (watchErr) {
245
- logger.error(`failed to update types from ${eventFilePath}`, watchErr);
246
- }
247
- }
248
- });
249
- subscriptionsByDir.set(directoryPath, subscription);
250
- }
251
- }
252
- const result = collect(source, filePath, pluginConfig);
253
- if (result.tags.size > 0) {
254
- tagsByFile.set(filePath, result.tags);
255
- } else {
256
- tagsByFile.delete(filePath);
398
+ ensureWatcher(pluginConfig, logger);
257
399
  }
258
- writeTypes(flattenTags(tagsByFile));
400
+ const result = collect(source, resolvedFilePath, pluginConfig);
401
+ writeTypesForFile(resolvedFilePath, result.tags).catch((err) => {
402
+ logger.error(`failed to write types for ${resolvedFilePath}`, err);
403
+ });
259
404
  return result;
260
405
  }
261
- async function cleanup() {
262
- await Promise.all([...subscriptionsByDir].map(async ([directoryPath, subPromise]) => {
263
- subscriptionsByDir.delete(directoryPath);
264
- await (await subPromise).unsubscribe();
265
- }));
266
- subscriptionsByDir.clear();
267
- }
268
- process.once("SIGINT", () => {
269
- cleanup().finally(() => process.exit(130));
270
- });
271
- process.once("SIGTERM", () => {
272
- cleanup().finally(() => process.exit(143));
273
- });
274
- async function withCxx(nextConfig = {}, pluginConfig = {}) {
275
- const TURBOPACK_GLOB = "*.{tsx,jsx,ts,js}";
406
+ var TURBOPACK_GLOB = "*.{tsx,jsx,ts,js}";
407
+ function withCxx(nextConfig = {}, pluginConfig = {}) {
408
+ const watchRoot = pluginConfig.watch?.root ?? nextConfig.turbopack?.root ?? process.cwd();
276
409
  const loaderItem = {
277
410
  loader,
278
411
  options: {
279
- pluginConfig
412
+ pluginConfig: {
413
+ ...pluginConfig,
414
+ watch: { ...pluginConfig.watch, root: watchRoot }
415
+ }
280
416
  }
281
417
  };
282
418
  const currentRule = nextConfig.turbopack?.rules?.[TURBOPACK_GLOB];
419
+ const userWebpack = nextConfig.webpack;
283
420
  return {
284
421
  ...nextConfig,
285
422
  turbopack: {
@@ -291,12 +428,24 @@ async function withCxx(nextConfig = {}, pluginConfig = {}) {
291
428
  loaders: [...currentRule.loaders ?? [], loaderItem]
292
429
  } : { loaders: [loaderItem] }
293
430
  }
431
+ },
432
+ webpack(config, context) {
433
+ const next = userWebpack ? userWebpack(config, context) : config;
434
+ next.module ??= {};
435
+ next.module.rules ??= [];
436
+ next.module.rules.push({
437
+ test: /\.[jt]sx?$/,
438
+ exclude: /node_modules/,
439
+ enforce: "pre",
440
+ use: [loaderItem]
441
+ });
442
+ return next;
294
443
  }
295
444
  };
296
445
  }
297
446
  export {
298
- withCxx,
299
- processFile
447
+ processFile,
448
+ withCxx
300
449
  };
301
450
 
302
- //# debugId=B9F61A8763996E6064756E2164756E21
451
+ //# debugId=A4FD970DD20B088B64756E2164756E21