@unlockable/vite-plugin-unlock 0.1.0

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/index.cjs ADDED
@@ -0,0 +1,809 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var src_exports = {};
32
+ __export(src_exports, {
33
+ unlock: () => unlock
34
+ });
35
+ module.exports = __toCommonJS(src_exports);
36
+
37
+ // src/plugin.ts
38
+ var import_path5 = __toESM(require("path"), 1);
39
+ var import_fs4 = __toESM(require("fs"), 1);
40
+
41
+ // src/config.ts
42
+ var import_path = __toESM(require("path"), 1);
43
+ var import_fs = __toESM(require("fs"), 1);
44
+ var import_module = require("module");
45
+
46
+ // src/constants.ts
47
+ var DEFAULT_EXTENSIONS = [
48
+ ".tsx",
49
+ ".ts",
50
+ ".jsx",
51
+ ".js",
52
+ ".vue",
53
+ ".svelte",
54
+ ".mts",
55
+ ".mjs"
56
+ ];
57
+ var DEFAULT_OVERRIDE_DIR = "./src/overrides";
58
+ var DEFAULT_SRC_DIR = "src";
59
+ var MAX_SCAN_DEPTH = 20;
60
+ var PLUGIN_NAME = "vite-plugin-unlock";
61
+
62
+ // src/utils.ts
63
+ var PREFIX = `[${PLUGIN_NAME}]`;
64
+ function createLogger(debug) {
65
+ return {
66
+ info(msg) {
67
+ if (debug) console.log(`${PREFIX} ${msg}`);
68
+ },
69
+ warn(msg) {
70
+ console.warn(`${PREFIX} ${msg}`);
71
+ },
72
+ error(msg) {
73
+ console.error(`${PREFIX} ${msg}`);
74
+ }
75
+ };
76
+ }
77
+ function normalizePath(p) {
78
+ return p.replace(/\\/g, "/");
79
+ }
80
+ function stripExtension(filename) {
81
+ return filename.replace(/\.(tsx?|jsx?|mts|mjs|vue|svelte)$/, "");
82
+ }
83
+ function generateAlias(packageName) {
84
+ const parts = packageName.split("/");
85
+ const lastPart = parts[parts.length - 1];
86
+ return `~${lastPart}`;
87
+ }
88
+
89
+ // src/config.ts
90
+ function findPackageSrcPath(packageName, srcDir) {
91
+ const cwd = process.cwd();
92
+ try {
93
+ const req = (0, import_module.createRequire)(import_path.default.join(cwd, "package.json"));
94
+ const pkgJsonPath = req.resolve(`${packageName}/package.json`);
95
+ const pkgRoot = import_path.default.dirname(pkgJsonPath);
96
+ const srcPath = import_path.default.join(pkgRoot, srcDir);
97
+ if (import_fs.default.existsSync(srcPath)) return srcPath;
98
+ return pkgRoot;
99
+ } catch {
100
+ }
101
+ const parts = packageName.startsWith("@") ? packageName.split("/") : [packageName];
102
+ const candidates = [
103
+ import_path.default.join(cwd, "node_modules", ...parts, srcDir),
104
+ import_path.default.join(cwd, ".yalc", ...parts, srcDir)
105
+ ];
106
+ for (const dir of candidates) {
107
+ if (import_fs.default.existsSync(dir)) return dir;
108
+ }
109
+ const rootCandidates = [
110
+ import_path.default.join(cwd, "node_modules", ...parts),
111
+ import_path.default.join(cwd, ".yalc", ...parts)
112
+ ];
113
+ for (const dir of rootCandidates) {
114
+ if (import_fs.default.existsSync(dir)) return dir;
115
+ }
116
+ return null;
117
+ }
118
+ function resolveTarget(input) {
119
+ const target = typeof input === "string" ? {
120
+ package: input,
121
+ alias: generateAlias(input),
122
+ srcDir: DEFAULT_SRC_DIR,
123
+ srcPath: ""
124
+ } : {
125
+ package: input.package,
126
+ alias: input.alias ?? generateAlias(input.package),
127
+ srcDir: input.srcDir ?? DEFAULT_SRC_DIR,
128
+ srcPath: "",
129
+ entryRedirect: input.entryRedirect,
130
+ hmr: input.hmr
131
+ };
132
+ const srcPath = findPackageSrcPath(target.package, target.srcDir);
133
+ if (!srcPath) return null;
134
+ target.srcPath = srcPath;
135
+ if (target.entryRedirect && target.hmr) {
136
+ const pkgDir = import_path.default.dirname(srcPath);
137
+ const entryFile = import_path.default.resolve(pkgDir, target.entryRedirect.to);
138
+ if (import_fs.default.existsSync(entryFile)) {
139
+ target.entryFilePath = entryFile;
140
+ }
141
+ }
142
+ return target;
143
+ }
144
+ var CONFIG_EXTENSIONS = [".tsx", ".ts", ".jsx", ".js"];
145
+ function findConfigFile(basename, overrideDirs) {
146
+ for (const dir of overrideDirs) {
147
+ for (const ext of CONFIG_EXTENSIONS) {
148
+ const p = import_path.default.resolve(dir, `${basename}${ext}`);
149
+ if (import_fs.default.existsSync(p)) return p;
150
+ }
151
+ }
152
+ return null;
153
+ }
154
+ function resolveOptions(options) {
155
+ const extensions = options.extensions ?? DEFAULT_EXTENSIONS;
156
+ const overrideInput = options.overrides ?? DEFAULT_OVERRIDE_DIR;
157
+ const overrideDirs = (Array.isArray(overrideInput) ? overrideInput : [overrideInput]).map((dir) => import_path.default.resolve(process.cwd(), dir));
158
+ const targets = [];
159
+ for (const input of options.targets) {
160
+ const resolved = resolveTarget(input);
161
+ if (resolved) {
162
+ targets.push(resolved);
163
+ }
164
+ }
165
+ const patches = (options.patches ?? []).map((p) => ({
166
+ target: p.target,
167
+ configFile: p.configFile,
168
+ apply: p.apply,
169
+ configPath: findConfigFile(p.configFile, overrideDirs)
170
+ }));
171
+ return {
172
+ targets,
173
+ overrideDirs,
174
+ match: options.match ?? "basename",
175
+ onConflict: options.onConflict ?? "error",
176
+ debug: options.debug ?? false,
177
+ extensions,
178
+ extensionSet: new Set(extensions),
179
+ patches,
180
+ hmrBoundaries: options.hmrBoundaries ?? []
181
+ };
182
+ }
183
+
184
+ // src/scanner.ts
185
+ var import_path2 = __toESM(require("path"), 1);
186
+ var import_fs2 = __toESM(require("fs"), 1);
187
+ function collectFiles(dir, extensionSet, depth = 0) {
188
+ if (depth > MAX_SCAN_DEPTH) return [];
189
+ const results = [];
190
+ let entries;
191
+ try {
192
+ entries = import_fs2.default.readdirSync(dir, { withFileTypes: true });
193
+ } catch {
194
+ return results;
195
+ }
196
+ for (const entry of entries) {
197
+ if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
198
+ const fullPath = import_path2.default.resolve(dir, entry.name);
199
+ if (entry.isDirectory()) {
200
+ results.push(...collectFiles(fullPath, extensionSet, depth + 1));
201
+ } else if (entry.isFile()) {
202
+ const ext = import_path2.default.extname(entry.name);
203
+ if (extensionSet.has(ext)) {
204
+ results.push(fullPath);
205
+ }
206
+ }
207
+ }
208
+ return results;
209
+ }
210
+ function getOverrideKey(filePath, baseDir, match) {
211
+ const normalized = normalizePath(filePath);
212
+ const parts = normalized.split("/").filter(Boolean);
213
+ if (parts.length === 0) return null;
214
+ const fileName = parts[parts.length - 1];
215
+ const baseName = stripExtension(fileName);
216
+ if (!baseName) return null;
217
+ if (match === "path") {
218
+ const normalizedBase = normalizePath(baseDir);
219
+ const relative = normalizePath(
220
+ import_path2.default.relative(normalizedBase, normalized)
221
+ );
222
+ return stripExtension(relative);
223
+ }
224
+ if (baseName === "index") {
225
+ return parts.length >= 2 ? parts[parts.length - 2] || null : null;
226
+ }
227
+ return baseName;
228
+ }
229
+ function scanTarget(target, opts) {
230
+ const map = /* @__PURE__ */ new Map();
231
+ for (const f of collectFiles(target.srcPath, opts.extensionSet)) {
232
+ const key = getOverrideKey(f, target.srcPath, opts.match);
233
+ if (key) map.set(key, f);
234
+ }
235
+ return map;
236
+ }
237
+ function scanAllTargets(opts, logger) {
238
+ const combined = /* @__PURE__ */ new Map();
239
+ for (const target of opts.targets) {
240
+ const targetMap = scanTarget(target, opts);
241
+ logger.info(
242
+ `Scanned ${targetMap.size} files in ${target.package} (${target.srcPath})`
243
+ );
244
+ for (const [key, filePath] of targetMap) {
245
+ combined.set(key, { target, filePath });
246
+ }
247
+ }
248
+ return combined;
249
+ }
250
+ function detectNamespace(filePath, overrideDir) {
251
+ const relative = normalizePath(import_path2.default.relative(overrideDir, filePath));
252
+ const scopedMatch = relative.match(/^(@[^/]+\/[^/]+)\//);
253
+ if (scopedMatch) return scopedMatch[1];
254
+ return null;
255
+ }
256
+ function scanOverrides(opts, logger) {
257
+ const flat = /* @__PURE__ */ new Map();
258
+ const namespaced = /* @__PURE__ */ new Map();
259
+ const targetPackages = new Set(opts.targets.map((t) => t.package));
260
+ for (const dir of opts.overrideDirs) {
261
+ if (!import_fs2.default.existsSync(dir)) continue;
262
+ for (const fullPath of collectFiles(dir, opts.extensionSet).sort()) {
263
+ const relative = normalizePath(import_path2.default.relative(dir, fullPath));
264
+ if (relative.split("/").some((part) => part.startsWith("_"))) continue;
265
+ const ns = detectNamespace(fullPath, dir);
266
+ if (ns && targetPackages.has(ns)) {
267
+ const nsDir = import_path2.default.join(dir, ns);
268
+ const key = getOverrideKey(fullPath, nsDir, opts.match);
269
+ if (key && key !== "index") {
270
+ if (!namespaced.has(ns)) namespaced.set(ns, /* @__PURE__ */ new Map());
271
+ namespaced.get(ns).set(key, fullPath);
272
+ logger.info(`Override [${ns}]: ${key} -> ${fullPath}`);
273
+ }
274
+ } else {
275
+ const key = getOverrideKey(fullPath, dir, opts.match);
276
+ if (key && key !== "index") {
277
+ flat.set(key, fullPath);
278
+ logger.info(`Override: ${key} -> ${fullPath}`);
279
+ }
280
+ }
281
+ }
282
+ }
283
+ return { flat, namespaced };
284
+ }
285
+
286
+ // src/resolver.ts
287
+ var import_path3 = __toESM(require("path"), 1);
288
+ var import_fs3 = __toESM(require("fs"), 1);
289
+ function findImporterTarget(importer, opts) {
290
+ const norm = normalizePath(importer);
291
+ for (const target of opts.targets) {
292
+ const normSrc = normalizePath(target.srcPath);
293
+ if (norm.startsWith(normSrc + "/") || norm === normSrc) {
294
+ return target;
295
+ }
296
+ const pkgDir = normalizePath(import_path3.default.dirname(target.srcPath));
297
+ if (norm.startsWith(pkgDir + "/")) {
298
+ return target;
299
+ }
300
+ }
301
+ return null;
302
+ }
303
+ function resolveEntryRedirect(resolvedId, opts, logger) {
304
+ const norm = normalizePath(resolvedId).replace(/\?.*$/, "");
305
+ for (const target of opts.targets) {
306
+ if (target.entryRedirect) {
307
+ const fromPattern = normalizePath(target.entryRedirect.from);
308
+ if (norm.endsWith(`/${fromPattern}`) || norm.includes(`/${fromPattern}`)) {
309
+ const pkgDir = import_path3.default.dirname(target.srcPath);
310
+ const srcEntry = import_path3.default.join(pkgDir, target.entryRedirect.to);
311
+ if (import_fs3.default.existsSync(srcEntry)) {
312
+ logger.info(
313
+ `Entry redirect: ${import_path3.default.basename(resolvedId)} -> ${target.entryRedirect.to}`
314
+ );
315
+ return srcEntry;
316
+ }
317
+ }
318
+ }
319
+ if (!target.entryRedirect) {
320
+ const parts = target.package.split("/");
321
+ const lastPart = parts[parts.length - 1];
322
+ if (norm.includes(`/${lastPart}/dist/app.`)) {
323
+ const srcEntry = norm.replace(
324
+ /\/dist\/app\.(mjs|js)$/,
325
+ "/src/app.tsx"
326
+ );
327
+ if (import_fs3.default.existsSync(srcEntry)) {
328
+ logger.info(`Entry redirect (auto): dist/app -> src/app.tsx`);
329
+ return srcEntry;
330
+ }
331
+ }
332
+ }
333
+ }
334
+ return null;
335
+ }
336
+
337
+ // src/watcher.ts
338
+ var import_path4 = __toESM(require("path"), 1);
339
+ function setupWatcher(server, state, opts, logger) {
340
+ let debounceTimer = null;
341
+ const handleStructuralChange = (filePath) => {
342
+ const ext = import_path4.default.extname(filePath);
343
+ if (!opts.extensionSet.has(ext)) return;
344
+ const normFile = normalizePath(filePath);
345
+ const isOverrideFile = opts.overrideDirs.some(
346
+ (dir) => normFile.startsWith(normalizePath(dir) + "/")
347
+ );
348
+ if (!isOverrideFile) return;
349
+ if (debounceTimer) clearTimeout(debounceTimer);
350
+ debounceTimer = setTimeout(() => {
351
+ const oldFlat = new Map(state.flatOverrides);
352
+ const oldNamespaced = new Map(
353
+ [...state.namespacedOverrides].map(
354
+ ([k, v]) => [k, new Map(v)]
355
+ )
356
+ );
357
+ const { flat, namespaced } = scanOverrides(opts, logger);
358
+ const fileOverrides = /* @__PURE__ */ new Map();
359
+ for (const [key, overridePath] of flat) {
360
+ if (state.targetFiles.has(key)) {
361
+ fileOverrides.set(key, overridePath);
362
+ }
363
+ }
364
+ Object.assign(state, {
365
+ flatOverrides: fileOverrides,
366
+ namespacedOverrides: namespaced
367
+ });
368
+ let hasChanges = false;
369
+ const allKeys = /* @__PURE__ */ new Set([...oldFlat.keys(), ...fileOverrides.keys()]);
370
+ for (const key of allKeys) {
371
+ const wasOverride = oldFlat.has(key);
372
+ const isOverride = fileOverrides.has(key);
373
+ if (wasOverride === isOverride && oldFlat.get(key) === fileOverrides.get(key))
374
+ continue;
375
+ const action = isOverride ? wasOverride ? "changed" : "created" : "deleted";
376
+ logger.info(`Override "${key}" ${action}`);
377
+ invalidateForKey(key, isOverride, oldFlat, state, server, logger);
378
+ hasChanges = true;
379
+ }
380
+ const allNs = /* @__PURE__ */ new Set([
381
+ ...oldNamespaced.keys(),
382
+ ...namespaced.keys()
383
+ ]);
384
+ for (const ns of allNs) {
385
+ const oldMap = oldNamespaced.get(ns) ?? /* @__PURE__ */ new Map();
386
+ const newMap = namespaced.get(ns) ?? /* @__PURE__ */ new Map();
387
+ const nsKeys = /* @__PURE__ */ new Set([...oldMap.keys(), ...newMap.keys()]);
388
+ for (const key of nsKeys) {
389
+ const was = oldMap.has(key);
390
+ const is = newMap.has(key);
391
+ if (was === is && oldMap.get(key) === newMap.get(key)) continue;
392
+ const action = is ? was ? "changed" : "created" : "deleted";
393
+ logger.info(`Override [${ns}] "${key}" ${action}`);
394
+ invalidateForKey(key, is, oldMap, state, server, logger);
395
+ hasChanges = true;
396
+ }
397
+ }
398
+ if (hasChanges) {
399
+ logger.info("Structural change detected -> full reload");
400
+ const ws = server.hot ?? server.ws;
401
+ ws.send({ type: "full-reload" });
402
+ }
403
+ }, 50);
404
+ };
405
+ server.watcher.on("add", handleStructuralChange);
406
+ server.watcher.on("unlink", handleStructuralChange);
407
+ server.watcher.on("change", (filePath) => {
408
+ const ext = import_path4.default.extname(filePath);
409
+ if (!opts.extensionSet.has(ext)) return;
410
+ const normFile = normalizePath(filePath);
411
+ const isOverrideFile = opts.overrideDirs.some(
412
+ (dir) => normFile.startsWith(normalizePath(dir) + "/")
413
+ );
414
+ if (!isOverrideFile) return;
415
+ const basename = stripExtension(import_path4.default.basename(filePath));
416
+ if (!basename) return;
417
+ const isTrackedOverride = state.flatOverrides.has(basename) || [...state.namespacedOverrides.values()].some((m) => m.has(basename));
418
+ if (!isTrackedOverride) return;
419
+ const trackedMods = server.moduleGraph.getModulesByFile(normFile);
420
+ if (trackedMods && trackedMods.size > 0) return;
421
+ const targetInfo = state.targetFiles.get(basename);
422
+ if (!targetInfo) return;
423
+ const origMods = server.moduleGraph.getModulesByFile(
424
+ normalizePath(targetInfo.filePath)
425
+ );
426
+ if (origMods) {
427
+ for (const mod of origMods) {
428
+ server.moduleGraph.invalidateModule(mod);
429
+ }
430
+ logger.info(`Override content changed: ${basename} -> reload`);
431
+ const ws = server.hot ?? server.ws;
432
+ ws.send({ type: "full-reload" });
433
+ }
434
+ });
435
+ if (opts.patches.length > 0) {
436
+ const handlePatchConfigStructural = (filePath) => {
437
+ const basename = stripExtension(import_path4.default.basename(filePath));
438
+ if (!basename) return;
439
+ const normFile = normalizePath(filePath);
440
+ const isOverrideFile = opts.overrideDirs.some(
441
+ (dir) => normFile.startsWith(normalizePath(dir) + "/")
442
+ );
443
+ if (!isOverrideFile) return;
444
+ for (const patch of opts.patches) {
445
+ if (basename !== patch.configFile) continue;
446
+ const newPath = findConfigFile(patch.configFile, opts.overrideDirs);
447
+ if (newPath === patch.configPath) continue;
448
+ patch.configPath = newPath;
449
+ logger.info(
450
+ `Patch config ${newPath ? "detected" : "removed"}: ${patch.configFile}`
451
+ );
452
+ invalidatePatchTarget(patch, server, logger);
453
+ const ws = server.hot ?? server.ws;
454
+ ws.send({ type: "full-reload" });
455
+ }
456
+ };
457
+ const handlePatchConfigContentEdit = (filePath) => {
458
+ const normFile = normalizePath(filePath);
459
+ for (const patch of opts.patches) {
460
+ if (!patch.configPath) continue;
461
+ if (normalizePath(patch.configPath) !== normFile) continue;
462
+ logger.info(`Patch config content changed: ${patch.configFile}`);
463
+ invalidatePatchTarget(patch, server, logger);
464
+ const ws = server.hot ?? server.ws;
465
+ ws.send({ type: "full-reload" });
466
+ return;
467
+ }
468
+ };
469
+ server.watcher.on("add", handlePatchConfigStructural);
470
+ server.watcher.on("unlink", handlePatchConfigStructural);
471
+ server.watcher.on("change", handlePatchConfigContentEdit);
472
+ }
473
+ for (const dir of opts.overrideDirs) {
474
+ server.watcher.add(dir);
475
+ }
476
+ }
477
+ function invalidatePatchTarget(patch, server, logger) {
478
+ const { moduleGraph } = server;
479
+ const roots = /* @__PURE__ */ new Set();
480
+ const timestamp = Date.now();
481
+ for (const mod of moduleGraph.idToModuleMap.values()) {
482
+ if (mod.file && patch.target.test(normalizePath(mod.file))) {
483
+ moduleGraph.invalidateModule(mod, /* @__PURE__ */ new Set(), timestamp, true);
484
+ roots.add(mod);
485
+ logger.info(`Invalidated patch target: ${import_path4.default.basename(mod.file)}`);
486
+ }
487
+ }
488
+ const seen = /* @__PURE__ */ new Set();
489
+ const queue = [];
490
+ for (const mod of roots) {
491
+ for (const parent of mod.importers) {
492
+ queue.push(parent);
493
+ }
494
+ }
495
+ while (queue.length > 0) {
496
+ const mod = queue.shift();
497
+ if (seen.has(mod)) continue;
498
+ seen.add(mod);
499
+ moduleGraph.invalidateModule(mod, seen, timestamp, true);
500
+ for (const parent of mod.importers) {
501
+ queue.push(parent);
502
+ }
503
+ }
504
+ if (roots.size > 0 && seen.size > 0) {
505
+ logger.info(`Invalidated ${seen.size} ancestor modules`);
506
+ }
507
+ }
508
+ function invalidateForKey(key, isNowOverride, oldOverrides, state, server, logger) {
509
+ const { moduleGraph } = server;
510
+ const roots = /* @__PURE__ */ new Set();
511
+ const targetInfo = state.targetFiles.get(key);
512
+ if (targetInfo) {
513
+ const mods = moduleGraph.getModulesByFile(
514
+ normalizePath(targetInfo.filePath)
515
+ );
516
+ if (mods) for (const mod of mods) roots.add(mod);
517
+ }
518
+ const overridePath = isNowOverride ? state.flatOverrides.get(key) : oldOverrides.get(key);
519
+ if (overridePath) {
520
+ const mods = moduleGraph.getModulesByFile(normalizePath(overridePath));
521
+ if (mods) for (const mod of mods) roots.add(mod);
522
+ }
523
+ if (roots.size > 0) {
524
+ for (const mod of roots) {
525
+ moduleGraph.invalidateModule(mod);
526
+ }
527
+ const seen = /* @__PURE__ */ new Set();
528
+ const queue = [];
529
+ for (const mod of roots) {
530
+ for (const parent of mod.importers) {
531
+ queue.push(parent);
532
+ }
533
+ }
534
+ while (queue.length > 0) {
535
+ const mod = queue.shift();
536
+ if (seen.has(mod)) continue;
537
+ seen.add(mod);
538
+ moduleGraph.invalidateModule(mod);
539
+ for (const parent of mod.importers) {
540
+ queue.push(parent);
541
+ }
542
+ }
543
+ logger.info(`Invalidated "${key}" + ${seen.size} ancestor modules`);
544
+ } else {
545
+ logger.info(`Override map updated for "${key}" (module not in graph yet)`);
546
+ }
547
+ }
548
+
549
+ // src/plugin.ts
550
+ function unlock(userOptions) {
551
+ const opts = resolveOptions(userOptions);
552
+ const logger = createLogger(opts.debug);
553
+ if (opts.targets.length === 0) {
554
+ logger.warn("No target packages found. Plugin will be inactive.");
555
+ return { name: PLUGIN_NAME };
556
+ }
557
+ const targetFiles = scanAllTargets(opts, logger);
558
+ const { flat, namespaced } = scanOverrides(opts, logger);
559
+ const fileOverrides = /* @__PURE__ */ new Map();
560
+ for (const [key, overridePath] of flat) {
561
+ if (targetFiles.has(key)) {
562
+ fileOverrides.set(key, overridePath);
563
+ } else {
564
+ logger.warn(
565
+ `Override "${key}" does not match any file in target packages \u2014 skipped`
566
+ );
567
+ }
568
+ }
569
+ if (opts.targets.length > 1 && fileOverrides.size > 0) {
570
+ detectConflicts(fileOverrides, targetFiles, opts, logger, scanTarget);
571
+ }
572
+ const state = {
573
+ flatOverrides: fileOverrides,
574
+ namespacedOverrides: namespaced,
575
+ targetFiles
576
+ };
577
+ const totalFileOverrides = fileOverrides.size + [...namespaced.values()].reduce((sum, m) => sum + m.size, 0);
578
+ if (totalFileOverrides > 0) {
579
+ logger.info(`Active overrides: ${totalFileOverrides}`);
580
+ if (fileOverrides.size > 0) {
581
+ logger.info(` File-level: ${[...fileOverrides.keys()].join(", ")}`);
582
+ }
583
+ for (const [ns, map] of namespaced) {
584
+ logger.info(` [${ns}]: ${[...map.keys()].join(", ")}`);
585
+ }
586
+ }
587
+ for (const patch of opts.patches) {
588
+ if (patch.configPath) {
589
+ logger.info(`Patch: ${patch.configFile} -> ${patch.configPath}`);
590
+ } else {
591
+ logger.info(`Patch: ${patch.configFile} (no config file found)`);
592
+ }
593
+ }
594
+ return {
595
+ name: PLUGIN_NAME,
596
+ enforce: "pre",
597
+ config(config) {
598
+ config.server = config.server || {};
599
+ config.server.fs = config.server.fs || {};
600
+ config.server.fs.allow = config.server.fs.allow || [];
601
+ for (const dir of opts.overrideDirs) {
602
+ config.server.fs.allow.push(import_path5.default.resolve(dir));
603
+ }
604
+ config.optimizeDeps = config.optimizeDeps || {};
605
+ config.optimizeDeps.exclude = config.optimizeDeps.exclude || [];
606
+ for (const target of opts.targets) {
607
+ config.optimizeDeps.exclude.push(target.package);
608
+ if (config.optimizeDeps.include) {
609
+ config.optimizeDeps.include = config.optimizeDeps.include.filter(
610
+ (dep) => dep !== target.package
611
+ );
612
+ }
613
+ }
614
+ config.resolve = config.resolve || {};
615
+ config.resolve.alias = config.resolve.alias || {};
616
+ for (const target of opts.targets) {
617
+ if (Array.isArray(config.resolve.alias)) {
618
+ config.resolve.alias.push({
619
+ find: target.alias,
620
+ replacement: target.srcPath
621
+ });
622
+ } else {
623
+ ;
624
+ config.resolve.alias[target.alias] = target.srcPath;
625
+ }
626
+ }
627
+ for (const target of opts.targets) {
628
+ const entryFile = import_path5.default.join(target.srcPath, "app.tsx");
629
+ if (import_fs4.default.existsSync(entryFile)) {
630
+ const existing = config.optimizeDeps.entries;
631
+ if (Array.isArray(existing)) {
632
+ existing.push(entryFile);
633
+ } else if (typeof existing === "string") {
634
+ config.optimizeDeps.entries = [existing, entryFile];
635
+ } else {
636
+ config.optimizeDeps.entries = [entryFile];
637
+ }
638
+ }
639
+ }
640
+ },
641
+ async resolveId(source, importer) {
642
+ if (source.startsWith("\0") || !importer || importer.startsWith("\0"))
643
+ return null;
644
+ const target = findImporterTarget(importer, opts);
645
+ if (target) {
646
+ const basename = stripExtension(import_path5.default.basename(source));
647
+ if (basename && basename !== "index") {
648
+ const nsOverrides = state.namespacedOverrides.get(target.package);
649
+ if (nsOverrides?.has(basename)) {
650
+ const p = nsOverrides.get(basename);
651
+ logger.info(
652
+ `Override [${target.package}]: ${basename} -> ${import_path5.default.basename(p)}`
653
+ );
654
+ return p;
655
+ }
656
+ if (state.flatOverrides.has(basename)) {
657
+ const p = state.flatOverrides.get(basename);
658
+ logger.info(`Override: ${basename} -> ${import_path5.default.basename(p)}`);
659
+ return p;
660
+ }
661
+ }
662
+ if (source === target.package || source.startsWith(target.package + "/")) {
663
+ const resolved = await this.resolve(source, importer, {
664
+ skipSelf: true
665
+ });
666
+ if (resolved) {
667
+ const redirect = resolveEntryRedirect(
668
+ resolved.id,
669
+ opts,
670
+ logger
671
+ );
672
+ if (redirect) return redirect;
673
+ }
674
+ }
675
+ return null;
676
+ }
677
+ for (const target2 of opts.targets) {
678
+ if (source !== target2.package && !source.startsWith(target2.package + "/"))
679
+ continue;
680
+ const resolved = await this.resolve(source, importer, {
681
+ skipSelf: true
682
+ });
683
+ if (resolved) {
684
+ const redirect = resolveEntryRedirect(resolved.id, opts, logger);
685
+ if (redirect) return redirect;
686
+ }
687
+ }
688
+ return null;
689
+ },
690
+ load(id) {
691
+ const target = findImporterTarget(id, opts);
692
+ if (!target) return null;
693
+ const basename = stripExtension(import_path5.default.basename(id));
694
+ if (!basename || basename === "index") return null;
695
+ const nsOverrides = state.namespacedOverrides.get(target.package);
696
+ const overridePath = nsOverrides?.get(basename) ?? state.flatOverrides.get(basename);
697
+ if (overridePath && import_fs4.default.existsSync(overridePath)) {
698
+ this.addWatchFile(overridePath);
699
+ const normalizedPath = normalizePath(overridePath).replace(/"/g, '\\"');
700
+ logger.info(
701
+ `Load override: ${basename} -> ${import_path5.default.basename(overridePath)}`
702
+ );
703
+ return `export { default } from "${normalizedPath}"
704
+ export * from "${normalizedPath}"`;
705
+ }
706
+ const normalizedId = normalizePath(id);
707
+ for (const patch of opts.patches) {
708
+ if (!patch.target.test(normalizedId)) continue;
709
+ if (!patch.configPath || !import_fs4.default.existsSync(patch.configPath)) continue;
710
+ this.addWatchFile(patch.configPath);
711
+ let original;
712
+ try {
713
+ original = import_fs4.default.readFileSync(id, "utf-8");
714
+ } catch (err) {
715
+ logger.error(`Failed to read file for patching: ${id}`);
716
+ return null;
717
+ }
718
+ const patched = patch.apply(original, patch.configPath);
719
+ logger.info(
720
+ `Patch applied: ${import_path5.default.basename(id)} via ${patch.configFile}`
721
+ );
722
+ return { code: patched, map: null };
723
+ }
724
+ return null;
725
+ },
726
+ transform(code, id) {
727
+ for (const target of opts.targets) {
728
+ if (!target.hmr || !target.entryFilePath) continue;
729
+ const normId = normalizePath(id);
730
+ const normEntry = normalizePath(target.entryFilePath);
731
+ if (normId !== normEntry) continue;
732
+ let modified = false;
733
+ if (target.hmr.cssRedirect) {
734
+ const { from, to } = target.hmr.cssRedirect;
735
+ const importPattern = `import "${from}"`;
736
+ if (code.includes(importPattern)) {
737
+ code = code.replace(importPattern, `import "${to}"`);
738
+ logger.info(`CSS rewritten: ${from} -> ${to}`);
739
+ modified = true;
740
+ }
741
+ }
742
+ if (target.hmr.entryBoundary) {
743
+ code += "\nif (import.meta.hot) { import.meta.hot.accept() }";
744
+ logger.info(
745
+ `HMR boundary injected: ${target.package} entry`
746
+ );
747
+ modified = true;
748
+ }
749
+ if (modified) return { code, map: null };
750
+ }
751
+ if (opts.hmrBoundaries.length > 0) {
752
+ const normId = normalizePath(id);
753
+ if (!normId.includes("/node_modules/")) {
754
+ const needsBoundary = opts.hmrBoundaries.some(
755
+ (p) => code.includes(p)
756
+ );
757
+ if (needsBoundary) {
758
+ logger.info(
759
+ `HMR boundary injected: ${import_path5.default.basename(id)}`
760
+ );
761
+ return {
762
+ code: code + "\nif (import.meta.hot) { import.meta.hot.accept() }",
763
+ map: null
764
+ };
765
+ }
766
+ }
767
+ }
768
+ return null;
769
+ },
770
+ configureServer(server) {
771
+ const fsConfig = server.config.server?.fs;
772
+ if (fsConfig && Array.isArray(fsConfig.allow)) {
773
+ for (const dir of opts.overrideDirs) {
774
+ const resolved = import_path5.default.resolve(dir);
775
+ if (!fsConfig.allow.includes(resolved)) {
776
+ fsConfig.allow.push(resolved);
777
+ }
778
+ }
779
+ }
780
+ setupWatcher(server, state, opts, logger);
781
+ }
782
+ };
783
+ }
784
+ function detectConflicts(flat, _targetFiles, opts, logger, scan) {
785
+ const perTargetMaps = opts.targets.map((t) => ({
786
+ target: t,
787
+ files: scan(t, opts)
788
+ }));
789
+ for (const [key] of flat) {
790
+ const matchingTargets = perTargetMaps.filter(({ files }) => files.has(key));
791
+ if (matchingTargets.length > 1) {
792
+ const names = matchingTargets.map(({ target }) => target.package).join(", ");
793
+ if (opts.onConflict === "error") {
794
+ throw new Error(
795
+ `[${PLUGIN_NAME}] Override "${key}" matches files in multiple targets: ${names}. Use namespaced overrides (overrides/@scope/pkg/${key}.tsx) or set onConflict: "warn".`
796
+ );
797
+ } else if (opts.onConflict === "warn") {
798
+ logger.warn(
799
+ `Override "${key}" matches files in multiple targets: ${names}. Using first match.`
800
+ );
801
+ }
802
+ }
803
+ }
804
+ }
805
+ // Annotate the CommonJS export names for ESM import in node:
806
+ 0 && (module.exports = {
807
+ unlock
808
+ });
809
+ //# sourceMappingURL=index.cjs.map