@lovable.dev/vite-tanstack-config 2.23.1 → 2.24.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/README.md +3 -1
- package/dist/hmr-gate.cjs +1 -1
- package/dist/hmr-gate.js +1 -1
- package/dist/index.cjs +310 -20
- package/dist/index.js +311 -21
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -19,4 +19,6 @@ Inside a Lovable build with prerender enabled, the build process is forced to ex
|
|
|
19
19
|
|
|
20
20
|
## Route manifest
|
|
21
21
|
|
|
22
|
-
Inside a Lovable build, the route surface of the app is written to `dist/.lovable/routes.json` after the build: `{ version, generator, basepath?, serverEntry, requestMiddleware?, routes }`, where `routes` is the generated route tree's `fullPaths` union in TanStack path syntax (`/posts/$postId`, `/files/$`, `/docs/{-$version}`). Lovable's serving layer uses it to answer requests no route can match without starting the app. The file is enumerated with `@tanstack/router-generator` through an in-memory overlay, so the build never rewrites `routeTree.gen.ts`, and it is skipped — with a warning naming the reason — whenever the app can answer paths outside its route tree: `tanstackStart.spa`, `router.virtualRouteConfig`, `nitro.routeRules`/`handlers`, a project `nitro.config.*` / `.config/nitro.*` / `.nitrorc` file (Nitro loads it on its own, so the wrapper cannot see what it adds), a `rewrite` in the router file, a `basepath` there that `tanstackStart.router.basepath` does not declare (a computed or shorthand one included), or a router file (`router.entry`, resolved the way TanStack Start resolves it) that cannot be read to check either.
|
|
22
|
+
Inside a Lovable build, the route surface of the app is written to `dist/.lovable/routes.json` after the build: `{ version, generator, basepath?, serverEntry, requestMiddleware?, urlInspection, routes }`, where `routes` is the generated route tree's `fullPaths` union in TanStack path syntax (`/posts/$postId`, `/files/$`, `/docs/{-$version}`). Lovable's serving layer uses it to answer requests no route can match without starting the app. The file is enumerated with `@tanstack/router-generator` through an in-memory overlay, so the build never rewrites `routeTree.gen.ts`, and it is skipped — with a warning naming the reason — whenever the app can answer paths outside its route tree: `tanstackStart.spa`, `router.virtualRouteConfig`, `nitro.routeRules`/`handlers`, a project `nitro.config.*` / `.config/nitro.*` / `.nitrorc` file (Nitro loads it on its own, so the wrapper cannot see what it adds), a `rewrite` in the router file, a `basepath` there that `tanstackStart.router.basepath` does not declare (a computed or shorthand one included), or a router file (`router.entry`, resolved the way TanStack Start resolves it) that cannot be read to check either.
|
|
23
|
+
|
|
24
|
+
`urlInspection` tells the serving layer whether the code that runs ahead of route matching could answer a request from its URL; only `none` lets it enforce the manifest, every other value keeps it advisory. The wrapper reads the server entry (`server.entry`, or `src/server.ts` by convention — the same rule sets `serverEntry`) and the start entry (`start.entry` or `src/start.ts`, whose `requestMiddleware` runs before the router) together with every module they import through relative or `@/` paths, with comments and string contents removed, and reports `server_entry`, `request_middleware` or `both` when any of it names the URL, one of its parts (`pathname`, `searchParams`, `path`), a redirect (`redirect`, `Location`, a `30x` status) or a not-found answer (`404`, `notFound`). It reports `unknown` instead of guessing when a module cannot be resolved or read, when a module is loaded by a computed specifier or `import.meta.glob`, when the closure imports a package other than `node:*`/`@tanstack/*` (a mounted framework can route without naming the URL), or when it outgrows 32 files / 256 KiB. The template's `server.ts` and `start.ts` read as `none`; keep them free of those names, or new projects stop asserting it.
|
package/dist/hmr-gate.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
let node_fs = require("node:fs");
|
|
3
2
|
let node_crypto = require("node:crypto");
|
|
3
|
+
let node_fs = require("node:fs");
|
|
4
4
|
//#region src/hmr-gate/index.ts
|
|
5
5
|
const statusCorsOriginRe = /^(?:https:\/\/(?:[^/]+\.)?lovable\.dev|https?:\/\/(?:(?:[^/:]+\.)?localhost|127\.0\.0\.1|\[::1\]))(?::\d+)?$/;
|
|
6
6
|
function requestPathname(req) {
|
package/dist/hmr-gate.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { readFileSync } from "node:fs";
|
|
2
1
|
import { createHash } from "node:crypto";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
3
|
//#region src/hmr-gate/index.ts
|
|
4
4
|
const statusCorsOriginRe = /^(?:https:\/\/(?:[^/]+\.)?lovable\.dev|https?:\/\/(?:(?:[^/:]+\.)?localhost|127\.0\.0\.1|\[::1\]))(?::\d+)?$/;
|
|
5
5
|
function requestPathname(req) {
|
package/dist/index.cjs
CHANGED
|
@@ -21,12 +21,12 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
21
21
|
enumerable: true
|
|
22
22
|
}) : target, mod));
|
|
23
23
|
//#endregion
|
|
24
|
-
let node_fs = require("node:fs");
|
|
25
24
|
let node_path = require("node:path");
|
|
26
25
|
let node_url = require("node:url");
|
|
27
26
|
let vite = require("vite");
|
|
28
27
|
let node_https = require("node:https");
|
|
29
28
|
node_https = __toESM(node_https, 1);
|
|
29
|
+
let node_fs_promises = require("node:fs/promises");
|
|
30
30
|
//#region src/assetsProxyPlugin.ts
|
|
31
31
|
const ASSET_RE = /^\/__l5e\/assets-v1\//;
|
|
32
32
|
const PREVIEW_HOST_ENV = "LOVABLE_PREVIEW_HOST";
|
|
@@ -154,7 +154,7 @@ function bundledDevCssUrlShim() {
|
|
|
154
154
|
//#endregion
|
|
155
155
|
//#region package.json
|
|
156
156
|
var name = "@lovable.dev/vite-tanstack-config";
|
|
157
|
-
var version = "2.
|
|
157
|
+
var version = "2.24.0";
|
|
158
158
|
//#endregion
|
|
159
159
|
//#region src/logging.ts
|
|
160
160
|
const PACKAGE_NAME = "@lovable.dev/vite-tanstack-config";
|
|
@@ -368,6 +368,305 @@ function createDocumentSet(routerBasePath) {
|
|
|
368
368
|
};
|
|
369
369
|
}
|
|
370
370
|
//#endregion
|
|
371
|
+
//#region src/linkedDependencies.ts
|
|
372
|
+
function linkedDependenciesPlugin() {
|
|
373
|
+
return {
|
|
374
|
+
name: "lovable-linked-dependencies",
|
|
375
|
+
apply: "serve",
|
|
376
|
+
async configResolved(config) {
|
|
377
|
+
let dependencies;
|
|
378
|
+
let root;
|
|
379
|
+
try {
|
|
380
|
+
root = await (0, node_fs_promises.realpath)(config.root);
|
|
381
|
+
dependencies = await (0, node_fs_promises.realpath)((0, node_path.join)(root, "node_modules"));
|
|
382
|
+
} catch {
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
if (dependencies.startsWith(root + node_path.sep)) return;
|
|
386
|
+
if (!config.server.fs.allow.includes(dependencies)) config.server.fs.allow.push(dependencies);
|
|
387
|
+
}
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
//#endregion
|
|
391
|
+
//#region src/urlInspection.ts
|
|
392
|
+
const URL_TOKENS = /\b(?:url|URL|URLPattern|URLSearchParams|pathname|searchParams|path|404|30\d|notFound|isNotFound|redirect|isRedirect|Location|location)\b/;
|
|
393
|
+
const REQUEST_MIDDLEWARE = /\brequestMiddleware\b/;
|
|
394
|
+
const STATIC_IMPORT = /\b(?:import|export)\b(?<type>\s+type\b)?[^'"`;]*?(?<!\.)\bfrom\s*(['"])(?<from>[^'"\n]+)\2|\bimport\s*(['"])(?<side>[^'"\n]+)\4/g;
|
|
395
|
+
const DYNAMIC_IMPORT = /\b(?:import|require)\s*\(\s*(['"`])(?<spec>[^'"`\n]+)\1\s*(?:,[^)]*)?\)/g;
|
|
396
|
+
const DYNAMIC_LOAD = /\b(?:import|require)\s*(?:\.\s*meta\s*\.\s*glob\s*)?\(/g;
|
|
397
|
+
const READABLE_LOAD = /\b(?:import|require)\s*\(\s*(['"`])\1\s*(?:,[^)]*)?\)/y;
|
|
398
|
+
const IMPORT_EXTENSIONS = [
|
|
399
|
+
".ts",
|
|
400
|
+
".js",
|
|
401
|
+
".mts",
|
|
402
|
+
".mjs",
|
|
403
|
+
".tsx",
|
|
404
|
+
".jsx"
|
|
405
|
+
];
|
|
406
|
+
const JS_TO_TS = {
|
|
407
|
+
".js": [".ts", ".tsx"],
|
|
408
|
+
".jsx": [".tsx"],
|
|
409
|
+
".mjs": [".mts"]
|
|
410
|
+
};
|
|
411
|
+
const CODE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
412
|
+
".ts",
|
|
413
|
+
".tsx",
|
|
414
|
+
".js",
|
|
415
|
+
".jsx",
|
|
416
|
+
".mts",
|
|
417
|
+
".mjs",
|
|
418
|
+
".cts",
|
|
419
|
+
".cjs"
|
|
420
|
+
]);
|
|
421
|
+
const TRUSTED_SPECIFIERS = ["node:", "@tanstack/"];
|
|
422
|
+
const REGEX_AFTER_KEYWORDS = /* @__PURE__ */ new Set([
|
|
423
|
+
"return",
|
|
424
|
+
"typeof",
|
|
425
|
+
"instanceof",
|
|
426
|
+
"in",
|
|
427
|
+
"of",
|
|
428
|
+
"new",
|
|
429
|
+
"delete",
|
|
430
|
+
"void",
|
|
431
|
+
"throw",
|
|
432
|
+
"case",
|
|
433
|
+
"do",
|
|
434
|
+
"else",
|
|
435
|
+
"yield",
|
|
436
|
+
"await"
|
|
437
|
+
]);
|
|
438
|
+
/**
|
|
439
|
+
* Splits comments, string and regex literals from code with a small state machine, so a
|
|
440
|
+
* `//` inside a URL literal or a quote inside a regex cannot swallow the code after it.
|
|
441
|
+
*/
|
|
442
|
+
function lexSource(source) {
|
|
443
|
+
const code = [];
|
|
444
|
+
const text = [];
|
|
445
|
+
let i = 0;
|
|
446
|
+
let word = "";
|
|
447
|
+
let slashDivides = false;
|
|
448
|
+
let prev = "";
|
|
449
|
+
let closedIncrement = false;
|
|
450
|
+
const resetOperand = (divides) => {
|
|
451
|
+
word = "";
|
|
452
|
+
slashDivides = divides;
|
|
453
|
+
prev = "";
|
|
454
|
+
closedIncrement = false;
|
|
455
|
+
};
|
|
456
|
+
const regexAllowed = () => word === "" ? !slashDivides : REGEX_AFTER_KEYWORDS.has(word);
|
|
457
|
+
const readQuoted = (quote) => {
|
|
458
|
+
const start = i++;
|
|
459
|
+
while (i < source.length && source[i] !== quote && source[i] !== "\n") {
|
|
460
|
+
if (source[i] === "\\") i++;
|
|
461
|
+
i++;
|
|
462
|
+
}
|
|
463
|
+
if (source[i] === quote) i++;
|
|
464
|
+
text.push(source.slice(start, i));
|
|
465
|
+
code.push(quote + quote);
|
|
466
|
+
resetOperand(true);
|
|
467
|
+
};
|
|
468
|
+
const readRegex = () => {
|
|
469
|
+
const start = i;
|
|
470
|
+
let inClass = false;
|
|
471
|
+
for (i++; i < source.length && source[i] !== "\n"; i++) {
|
|
472
|
+
const char = source[i];
|
|
473
|
+
if (char === "\\") i++;
|
|
474
|
+
else if (char === "[") inClass = true;
|
|
475
|
+
else if (char === "]") inClass = false;
|
|
476
|
+
else if (char === "/" && !inClass) {
|
|
477
|
+
i++;
|
|
478
|
+
while (i < source.length && /[a-z]/.test(source[i])) i++;
|
|
479
|
+
text.push(source.slice(start, i));
|
|
480
|
+
code.push("//");
|
|
481
|
+
resetOperand(true);
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
i = start + 1;
|
|
486
|
+
text.push("/");
|
|
487
|
+
code.push("/");
|
|
488
|
+
resetOperand(false);
|
|
489
|
+
};
|
|
490
|
+
const readTemplate = () => {
|
|
491
|
+
i++;
|
|
492
|
+
text.push("`");
|
|
493
|
+
code.push("`");
|
|
494
|
+
while (i < source.length) {
|
|
495
|
+
const char = source[i];
|
|
496
|
+
if (char === "\\") {
|
|
497
|
+
text.push(source.slice(i, i + 2));
|
|
498
|
+
i += 2;
|
|
499
|
+
} else if (char === "`") {
|
|
500
|
+
text.push("`");
|
|
501
|
+
code.push("`");
|
|
502
|
+
i++;
|
|
503
|
+
resetOperand(true);
|
|
504
|
+
return;
|
|
505
|
+
} else if (char === "$" && source[i + 1] === "{") {
|
|
506
|
+
text.push("${");
|
|
507
|
+
code.push("${");
|
|
508
|
+
i += 2;
|
|
509
|
+
resetOperand(false);
|
|
510
|
+
readCode(true);
|
|
511
|
+
text.push("}");
|
|
512
|
+
code.push("}");
|
|
513
|
+
} else {
|
|
514
|
+
text.push(char);
|
|
515
|
+
i++;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
};
|
|
519
|
+
const readCode = (untilBrace) => {
|
|
520
|
+
let depth = 0;
|
|
521
|
+
while (i < source.length) {
|
|
522
|
+
const char = source[i];
|
|
523
|
+
const next = source[i + 1];
|
|
524
|
+
if (char === "/" && next === "/") {
|
|
525
|
+
const end = source.indexOf("\n", i);
|
|
526
|
+
i = end === -1 ? source.length : end;
|
|
527
|
+
continue;
|
|
528
|
+
}
|
|
529
|
+
if (char === "/" && next === "*") {
|
|
530
|
+
const end = source.indexOf("*/", i + 2);
|
|
531
|
+
i = end === -1 ? source.length : end + 2;
|
|
532
|
+
text.push(" ");
|
|
533
|
+
code.push(" ");
|
|
534
|
+
prev = " ";
|
|
535
|
+
continue;
|
|
536
|
+
}
|
|
537
|
+
if (char === "'" || char === "\"") {
|
|
538
|
+
readQuoted(char);
|
|
539
|
+
continue;
|
|
540
|
+
}
|
|
541
|
+
if (char === "`") {
|
|
542
|
+
readTemplate();
|
|
543
|
+
continue;
|
|
544
|
+
}
|
|
545
|
+
if (char === "/" && regexAllowed()) {
|
|
546
|
+
readRegex();
|
|
547
|
+
continue;
|
|
548
|
+
}
|
|
549
|
+
if (untilBrace && char === "{") depth++;
|
|
550
|
+
if (untilBrace && char === "}") {
|
|
551
|
+
if (depth === 0) {
|
|
552
|
+
i++;
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
depth--;
|
|
556
|
+
}
|
|
557
|
+
text.push(char);
|
|
558
|
+
code.push(char);
|
|
559
|
+
i++;
|
|
560
|
+
if (/[\w$]/.test(char)) word += char;
|
|
561
|
+
else if (!/\s/.test(char)) {
|
|
562
|
+
const closesIncrement = (char === "+" || char === "-") && prev === char && !closedIncrement;
|
|
563
|
+
closedIncrement = closesIncrement;
|
|
564
|
+
word = "";
|
|
565
|
+
slashDivides = closesIncrement || /[)\]}<]/.test(char);
|
|
566
|
+
}
|
|
567
|
+
prev = char;
|
|
568
|
+
}
|
|
569
|
+
};
|
|
570
|
+
readCode(false);
|
|
571
|
+
return {
|
|
572
|
+
code: code.join(""),
|
|
573
|
+
text: text.join("")
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
/** Every module specifier `text` (comment-free source) imports at runtime, in source order. */
|
|
577
|
+
function importSpecifiers(text) {
|
|
578
|
+
const specifiers = [];
|
|
579
|
+
for (const match of text.matchAll(STATIC_IMPORT)) {
|
|
580
|
+
if (match.groups?.type !== void 0) continue;
|
|
581
|
+
const specifier = match.groups?.from ?? match.groups?.side;
|
|
582
|
+
if (specifier !== void 0) specifiers.push(specifier);
|
|
583
|
+
}
|
|
584
|
+
for (const match of text.matchAll(DYNAMIC_IMPORT)) if (match.groups?.spec !== void 0) specifiers.push(match.groups.spec);
|
|
585
|
+
return specifiers;
|
|
586
|
+
}
|
|
587
|
+
/** Whether `code` loads a module importSpecifiers cannot name: a computed specifier, `import.meta.glob`, an argument list it does not read. */
|
|
588
|
+
function hasUnreadImports(code) {
|
|
589
|
+
for (const match of code.matchAll(DYNAMIC_LOAD)) {
|
|
590
|
+
READABLE_LOAD.lastIndex = match.index;
|
|
591
|
+
if (!READABLE_LOAD.test(code)) return true;
|
|
592
|
+
}
|
|
593
|
+
return false;
|
|
594
|
+
}
|
|
595
|
+
function inspectsUrl(code) {
|
|
596
|
+
return URL_TOKENS.test(code);
|
|
597
|
+
}
|
|
598
|
+
function classifySpecifier(specifier, fromDir, aliasDir, nodePath) {
|
|
599
|
+
if (specifier.includes("${")) return "opaque";
|
|
600
|
+
const file = specifier.replace(/\?.*$/, "");
|
|
601
|
+
if (file.startsWith("./") || file.startsWith("../")) return { file: nodePath.resolve(fromDir, file) };
|
|
602
|
+
if (file.startsWith("@/")) return { file: nodePath.resolve(aliasDir, file.slice(2)) };
|
|
603
|
+
return TRUSTED_SPECIFIERS.some((prefix) => specifier.startsWith(prefix)) ? "trusted" : "opaque";
|
|
604
|
+
}
|
|
605
|
+
async function isFile(file) {
|
|
606
|
+
const { stat } = await import("node:fs/promises");
|
|
607
|
+
return stat(file).then((stats) => stats.isFile(), () => false);
|
|
608
|
+
}
|
|
609
|
+
async function resolveLocalImport(base, nodePath) {
|
|
610
|
+
const candidates = [base, ...IMPORT_EXTENSIONS.map((extension) => `${base}${extension}`)];
|
|
611
|
+
const extension = nodePath.extname(base);
|
|
612
|
+
for (const swapped of JS_TO_TS[extension] ?? []) candidates.push(`${base.slice(0, -extension.length)}${swapped}`);
|
|
613
|
+
candidates.push(...IMPORT_EXTENSIONS.map((suffix) => nodePath.join(base, `index${suffix}`)));
|
|
614
|
+
for (const candidate of candidates) if (await isFile(candidate)) return candidate;
|
|
615
|
+
}
|
|
616
|
+
/**
|
|
617
|
+
* Scans `entryFile` and every local module it imports, transitively; undefined without
|
|
618
|
+
* an entry. Anything it cannot read or vouch for leaves the scan incomplete, not guessed.
|
|
619
|
+
*/
|
|
620
|
+
async function scanEntry(entryFile, options) {
|
|
621
|
+
if (entryFile === void 0) return void 0;
|
|
622
|
+
const nodePath = await import("node:path");
|
|
623
|
+
const { readFile } = await import("node:fs/promises");
|
|
624
|
+
const scan = {
|
|
625
|
+
complete: true,
|
|
626
|
+
inspectsUrl: false,
|
|
627
|
+
requestMiddleware: false
|
|
628
|
+
};
|
|
629
|
+
const incomplete = () => ({
|
|
630
|
+
...scan,
|
|
631
|
+
complete: false
|
|
632
|
+
});
|
|
633
|
+
const seen = /* @__PURE__ */ new Set();
|
|
634
|
+
const queue = [entryFile];
|
|
635
|
+
let bytes = 0;
|
|
636
|
+
while (queue.length > 0) {
|
|
637
|
+
const file = queue.shift();
|
|
638
|
+
if (seen.has(file)) continue;
|
|
639
|
+
seen.add(file);
|
|
640
|
+
if (seen.size > 32 || file.split(nodePath.sep).includes("node_modules")) return incomplete();
|
|
641
|
+
const source = await readFile(file, "utf8").catch(() => void 0);
|
|
642
|
+
if (source === void 0) return incomplete();
|
|
643
|
+
bytes += Buffer.byteLength(source, "utf8");
|
|
644
|
+
if (bytes > 262144) return incomplete();
|
|
645
|
+
const { code, text } = lexSource(source);
|
|
646
|
+
scan.inspectsUrl ||= inspectsUrl(code);
|
|
647
|
+
scan.requestMiddleware ||= REQUEST_MIDDLEWARE.test(code);
|
|
648
|
+
if (hasUnreadImports(code)) return incomplete();
|
|
649
|
+
for (const specifier of importSpecifiers(text)) {
|
|
650
|
+
const resolution = classifySpecifier(specifier, nodePath.dirname(file), options.aliasDir, nodePath);
|
|
651
|
+
if (resolution === "trusted") continue;
|
|
652
|
+
const resolved = resolution === "opaque" ? void 0 : await resolveLocalImport(resolution.file, nodePath);
|
|
653
|
+
if (resolved === void 0) return incomplete();
|
|
654
|
+
if (CODE_EXTENSIONS.has(nodePath.extname(resolved))) queue.push(resolved);
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
return scan;
|
|
658
|
+
}
|
|
659
|
+
/** The manifest's verdict over the server entry and the start entry (request middleware). */
|
|
660
|
+
function urlInspectionOf(server, start) {
|
|
661
|
+
const serverInspects = server?.inspectsUrl === true;
|
|
662
|
+
const middlewareInspects = start?.inspectsUrl === true;
|
|
663
|
+
if (serverInspects && middlewareInspects) return "both";
|
|
664
|
+
if (serverInspects) return "server_entry";
|
|
665
|
+
if (middlewareInspects) return "request_middleware";
|
|
666
|
+
if (server?.complete === false || start?.complete === false) return "unknown";
|
|
667
|
+
return "none";
|
|
668
|
+
}
|
|
669
|
+
//#endregion
|
|
371
670
|
//#region src/routes.ts
|
|
372
671
|
const ROUTE_MANIFEST_VERSION = 1;
|
|
373
672
|
const GENERATOR = `${name}@${version}`;
|
|
@@ -642,13 +941,17 @@ function createRouteManifestPlugin(inputs) {
|
|
|
642
941
|
logWarning("Not publishing a route manifest: the route tree has no routes.");
|
|
643
942
|
return;
|
|
644
943
|
}
|
|
645
|
-
const
|
|
944
|
+
const serverFile = await resolveEntryFile(sourceDir, stringOption(subOptions(tanstackStart, "server"), "entry", "server"));
|
|
945
|
+
const startFile = await resolveEntryFile(sourceDir, stringOption(subOptions(tanstackStart, "start"), "entry", "start"));
|
|
946
|
+
const aliasDir = nodePath.resolve(process.cwd(), "src");
|
|
947
|
+
const [server, start] = await Promise.all([scanEntry(serverFile, { aliasDir }), scanEntry(startFile, { aliasDir })]);
|
|
646
948
|
const payload = serializeRouteManifest({
|
|
647
949
|
version: ROUTE_MANIFEST_VERSION,
|
|
648
950
|
generator: GENERATOR,
|
|
649
951
|
...basepath === "" ? {} : { basepath },
|
|
650
|
-
serverEntry:
|
|
651
|
-
...
|
|
952
|
+
serverEntry: serverFile === void 0 ? "default" : "custom",
|
|
953
|
+
...start === void 0 ? {} : { requestMiddleware: start.requestMiddleware },
|
|
954
|
+
urlInspection: urlInspectionOf(server, start),
|
|
652
955
|
routes
|
|
653
956
|
});
|
|
654
957
|
if (payload === void 0) return;
|
|
@@ -669,18 +972,6 @@ const LOVABLE_SANDBOX_ENV_VAR = "LOVABLE_SANDBOX";
|
|
|
669
972
|
function isSandboxEnvironment() {
|
|
670
973
|
return process.env[LOVABLE_SANDBOX_ENV_VAR] === "1" || !!process.env[SANDBOX_ENV_VAR];
|
|
671
974
|
}
|
|
672
|
-
function sandboxFsAllow(root) {
|
|
673
|
-
let nodeModules;
|
|
674
|
-
let realRoot;
|
|
675
|
-
try {
|
|
676
|
-
nodeModules = node_fs.realpathSync.native((0, node_path.join)(root, "node_modules"));
|
|
677
|
-
realRoot = node_fs.realpathSync.native(root);
|
|
678
|
-
} catch {
|
|
679
|
-
return;
|
|
680
|
-
}
|
|
681
|
-
if (nodeModules.startsWith(realRoot + node_path.sep)) return void 0;
|
|
682
|
-
return [(0, vite.searchForWorkspaceRoot)(root), nodeModules];
|
|
683
|
-
}
|
|
684
975
|
const TOOL_MANAGED_WATCH_IGNORES = [
|
|
685
976
|
"**/.workspace/**",
|
|
686
977
|
"**/.agents/**",
|
|
@@ -1606,6 +1897,7 @@ function defineConfig(configOrOptions = {}) {
|
|
|
1606
1897
|
}
|
|
1607
1898
|
}
|
|
1608
1899
|
if (command === "serve" && isSandbox) {
|
|
1900
|
+
internalPlugins.push(linkedDependenciesPlugin());
|
|
1609
1901
|
const { devServerBridgePlugin } = await Promise.resolve().then(() => require("./dev-server-bridge.cjs"));
|
|
1610
1902
|
internalPlugins.push(devServerBridgePlugin());
|
|
1611
1903
|
}
|
|
@@ -1656,14 +1948,12 @@ function defineConfig(configOrOptions = {}) {
|
|
|
1656
1948
|
}
|
|
1657
1949
|
if (isSandbox) {
|
|
1658
1950
|
validateConfig(config);
|
|
1659
|
-
const fsAllow = sandboxFsAllow(process.cwd());
|
|
1660
1951
|
config = cleanServerConfig((0, vite.mergeConfig)(config, { server: {
|
|
1661
1952
|
host: "::",
|
|
1662
1953
|
port: 8080,
|
|
1663
1954
|
strictPort: true,
|
|
1664
1955
|
watch: { ignored: [...TOOL_MANAGED_WATCH_IGNORES] },
|
|
1665
|
-
hmr: { overlay: false }
|
|
1666
|
-
...fsAllow ? { fs: { allow: fsAllow } } : {}
|
|
1956
|
+
hmr: { overlay: false }
|
|
1667
1957
|
} }));
|
|
1668
1958
|
} else config = (0, vite.mergeConfig)({ server: {
|
|
1669
1959
|
host: "::",
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { realpathSync } from "node:fs";
|
|
2
1
|
import { basename, extname, join, relative, resolve, sep } from "node:path";
|
|
3
2
|
import { fileURLToPath } from "node:url";
|
|
4
|
-
import { loadEnv, mergeConfig
|
|
3
|
+
import { loadEnv, mergeConfig } from "vite";
|
|
5
4
|
import https from "node:https";
|
|
5
|
+
import { realpath } from "node:fs/promises";
|
|
6
6
|
//#region src/assetsProxyPlugin.ts
|
|
7
7
|
const ASSET_RE = /^\/__l5e\/assets-v1\//;
|
|
8
8
|
const PREVIEW_HOST_ENV = "LOVABLE_PREVIEW_HOST";
|
|
@@ -130,7 +130,7 @@ function bundledDevCssUrlShim() {
|
|
|
130
130
|
//#endregion
|
|
131
131
|
//#region package.json
|
|
132
132
|
var name = "@lovable.dev/vite-tanstack-config";
|
|
133
|
-
var version = "2.
|
|
133
|
+
var version = "2.24.0";
|
|
134
134
|
//#endregion
|
|
135
135
|
//#region src/logging.ts
|
|
136
136
|
const PACKAGE_NAME = "@lovable.dev/vite-tanstack-config";
|
|
@@ -344,6 +344,305 @@ function createDocumentSet(routerBasePath) {
|
|
|
344
344
|
};
|
|
345
345
|
}
|
|
346
346
|
//#endregion
|
|
347
|
+
//#region src/linkedDependencies.ts
|
|
348
|
+
function linkedDependenciesPlugin() {
|
|
349
|
+
return {
|
|
350
|
+
name: "lovable-linked-dependencies",
|
|
351
|
+
apply: "serve",
|
|
352
|
+
async configResolved(config) {
|
|
353
|
+
let dependencies;
|
|
354
|
+
let root;
|
|
355
|
+
try {
|
|
356
|
+
root = await realpath(config.root);
|
|
357
|
+
dependencies = await realpath(join(root, "node_modules"));
|
|
358
|
+
} catch {
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
if (dependencies.startsWith(root + sep)) return;
|
|
362
|
+
if (!config.server.fs.allow.includes(dependencies)) config.server.fs.allow.push(dependencies);
|
|
363
|
+
}
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
//#endregion
|
|
367
|
+
//#region src/urlInspection.ts
|
|
368
|
+
const URL_TOKENS = /\b(?:url|URL|URLPattern|URLSearchParams|pathname|searchParams|path|404|30\d|notFound|isNotFound|redirect|isRedirect|Location|location)\b/;
|
|
369
|
+
const REQUEST_MIDDLEWARE = /\brequestMiddleware\b/;
|
|
370
|
+
const STATIC_IMPORT = /\b(?:import|export)\b(?<type>\s+type\b)?[^'"`;]*?(?<!\.)\bfrom\s*(['"])(?<from>[^'"\n]+)\2|\bimport\s*(['"])(?<side>[^'"\n]+)\4/g;
|
|
371
|
+
const DYNAMIC_IMPORT = /\b(?:import|require)\s*\(\s*(['"`])(?<spec>[^'"`\n]+)\1\s*(?:,[^)]*)?\)/g;
|
|
372
|
+
const DYNAMIC_LOAD = /\b(?:import|require)\s*(?:\.\s*meta\s*\.\s*glob\s*)?\(/g;
|
|
373
|
+
const READABLE_LOAD = /\b(?:import|require)\s*\(\s*(['"`])\1\s*(?:,[^)]*)?\)/y;
|
|
374
|
+
const IMPORT_EXTENSIONS = [
|
|
375
|
+
".ts",
|
|
376
|
+
".js",
|
|
377
|
+
".mts",
|
|
378
|
+
".mjs",
|
|
379
|
+
".tsx",
|
|
380
|
+
".jsx"
|
|
381
|
+
];
|
|
382
|
+
const JS_TO_TS = {
|
|
383
|
+
".js": [".ts", ".tsx"],
|
|
384
|
+
".jsx": [".tsx"],
|
|
385
|
+
".mjs": [".mts"]
|
|
386
|
+
};
|
|
387
|
+
const CODE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
388
|
+
".ts",
|
|
389
|
+
".tsx",
|
|
390
|
+
".js",
|
|
391
|
+
".jsx",
|
|
392
|
+
".mts",
|
|
393
|
+
".mjs",
|
|
394
|
+
".cts",
|
|
395
|
+
".cjs"
|
|
396
|
+
]);
|
|
397
|
+
const TRUSTED_SPECIFIERS = ["node:", "@tanstack/"];
|
|
398
|
+
const REGEX_AFTER_KEYWORDS = /* @__PURE__ */ new Set([
|
|
399
|
+
"return",
|
|
400
|
+
"typeof",
|
|
401
|
+
"instanceof",
|
|
402
|
+
"in",
|
|
403
|
+
"of",
|
|
404
|
+
"new",
|
|
405
|
+
"delete",
|
|
406
|
+
"void",
|
|
407
|
+
"throw",
|
|
408
|
+
"case",
|
|
409
|
+
"do",
|
|
410
|
+
"else",
|
|
411
|
+
"yield",
|
|
412
|
+
"await"
|
|
413
|
+
]);
|
|
414
|
+
/**
|
|
415
|
+
* Splits comments, string and regex literals from code with a small state machine, so a
|
|
416
|
+
* `//` inside a URL literal or a quote inside a regex cannot swallow the code after it.
|
|
417
|
+
*/
|
|
418
|
+
function lexSource(source) {
|
|
419
|
+
const code = [];
|
|
420
|
+
const text = [];
|
|
421
|
+
let i = 0;
|
|
422
|
+
let word = "";
|
|
423
|
+
let slashDivides = false;
|
|
424
|
+
let prev = "";
|
|
425
|
+
let closedIncrement = false;
|
|
426
|
+
const resetOperand = (divides) => {
|
|
427
|
+
word = "";
|
|
428
|
+
slashDivides = divides;
|
|
429
|
+
prev = "";
|
|
430
|
+
closedIncrement = false;
|
|
431
|
+
};
|
|
432
|
+
const regexAllowed = () => word === "" ? !slashDivides : REGEX_AFTER_KEYWORDS.has(word);
|
|
433
|
+
const readQuoted = (quote) => {
|
|
434
|
+
const start = i++;
|
|
435
|
+
while (i < source.length && source[i] !== quote && source[i] !== "\n") {
|
|
436
|
+
if (source[i] === "\\") i++;
|
|
437
|
+
i++;
|
|
438
|
+
}
|
|
439
|
+
if (source[i] === quote) i++;
|
|
440
|
+
text.push(source.slice(start, i));
|
|
441
|
+
code.push(quote + quote);
|
|
442
|
+
resetOperand(true);
|
|
443
|
+
};
|
|
444
|
+
const readRegex = () => {
|
|
445
|
+
const start = i;
|
|
446
|
+
let inClass = false;
|
|
447
|
+
for (i++; i < source.length && source[i] !== "\n"; i++) {
|
|
448
|
+
const char = source[i];
|
|
449
|
+
if (char === "\\") i++;
|
|
450
|
+
else if (char === "[") inClass = true;
|
|
451
|
+
else if (char === "]") inClass = false;
|
|
452
|
+
else if (char === "/" && !inClass) {
|
|
453
|
+
i++;
|
|
454
|
+
while (i < source.length && /[a-z]/.test(source[i])) i++;
|
|
455
|
+
text.push(source.slice(start, i));
|
|
456
|
+
code.push("//");
|
|
457
|
+
resetOperand(true);
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
i = start + 1;
|
|
462
|
+
text.push("/");
|
|
463
|
+
code.push("/");
|
|
464
|
+
resetOperand(false);
|
|
465
|
+
};
|
|
466
|
+
const readTemplate = () => {
|
|
467
|
+
i++;
|
|
468
|
+
text.push("`");
|
|
469
|
+
code.push("`");
|
|
470
|
+
while (i < source.length) {
|
|
471
|
+
const char = source[i];
|
|
472
|
+
if (char === "\\") {
|
|
473
|
+
text.push(source.slice(i, i + 2));
|
|
474
|
+
i += 2;
|
|
475
|
+
} else if (char === "`") {
|
|
476
|
+
text.push("`");
|
|
477
|
+
code.push("`");
|
|
478
|
+
i++;
|
|
479
|
+
resetOperand(true);
|
|
480
|
+
return;
|
|
481
|
+
} else if (char === "$" && source[i + 1] === "{") {
|
|
482
|
+
text.push("${");
|
|
483
|
+
code.push("${");
|
|
484
|
+
i += 2;
|
|
485
|
+
resetOperand(false);
|
|
486
|
+
readCode(true);
|
|
487
|
+
text.push("}");
|
|
488
|
+
code.push("}");
|
|
489
|
+
} else {
|
|
490
|
+
text.push(char);
|
|
491
|
+
i++;
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
};
|
|
495
|
+
const readCode = (untilBrace) => {
|
|
496
|
+
let depth = 0;
|
|
497
|
+
while (i < source.length) {
|
|
498
|
+
const char = source[i];
|
|
499
|
+
const next = source[i + 1];
|
|
500
|
+
if (char === "/" && next === "/") {
|
|
501
|
+
const end = source.indexOf("\n", i);
|
|
502
|
+
i = end === -1 ? source.length : end;
|
|
503
|
+
continue;
|
|
504
|
+
}
|
|
505
|
+
if (char === "/" && next === "*") {
|
|
506
|
+
const end = source.indexOf("*/", i + 2);
|
|
507
|
+
i = end === -1 ? source.length : end + 2;
|
|
508
|
+
text.push(" ");
|
|
509
|
+
code.push(" ");
|
|
510
|
+
prev = " ";
|
|
511
|
+
continue;
|
|
512
|
+
}
|
|
513
|
+
if (char === "'" || char === "\"") {
|
|
514
|
+
readQuoted(char);
|
|
515
|
+
continue;
|
|
516
|
+
}
|
|
517
|
+
if (char === "`") {
|
|
518
|
+
readTemplate();
|
|
519
|
+
continue;
|
|
520
|
+
}
|
|
521
|
+
if (char === "/" && regexAllowed()) {
|
|
522
|
+
readRegex();
|
|
523
|
+
continue;
|
|
524
|
+
}
|
|
525
|
+
if (untilBrace && char === "{") depth++;
|
|
526
|
+
if (untilBrace && char === "}") {
|
|
527
|
+
if (depth === 0) {
|
|
528
|
+
i++;
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
depth--;
|
|
532
|
+
}
|
|
533
|
+
text.push(char);
|
|
534
|
+
code.push(char);
|
|
535
|
+
i++;
|
|
536
|
+
if (/[\w$]/.test(char)) word += char;
|
|
537
|
+
else if (!/\s/.test(char)) {
|
|
538
|
+
const closesIncrement = (char === "+" || char === "-") && prev === char && !closedIncrement;
|
|
539
|
+
closedIncrement = closesIncrement;
|
|
540
|
+
word = "";
|
|
541
|
+
slashDivides = closesIncrement || /[)\]}<]/.test(char);
|
|
542
|
+
}
|
|
543
|
+
prev = char;
|
|
544
|
+
}
|
|
545
|
+
};
|
|
546
|
+
readCode(false);
|
|
547
|
+
return {
|
|
548
|
+
code: code.join(""),
|
|
549
|
+
text: text.join("")
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
/** Every module specifier `text` (comment-free source) imports at runtime, in source order. */
|
|
553
|
+
function importSpecifiers(text) {
|
|
554
|
+
const specifiers = [];
|
|
555
|
+
for (const match of text.matchAll(STATIC_IMPORT)) {
|
|
556
|
+
if (match.groups?.type !== void 0) continue;
|
|
557
|
+
const specifier = match.groups?.from ?? match.groups?.side;
|
|
558
|
+
if (specifier !== void 0) specifiers.push(specifier);
|
|
559
|
+
}
|
|
560
|
+
for (const match of text.matchAll(DYNAMIC_IMPORT)) if (match.groups?.spec !== void 0) specifiers.push(match.groups.spec);
|
|
561
|
+
return specifiers;
|
|
562
|
+
}
|
|
563
|
+
/** Whether `code` loads a module importSpecifiers cannot name: a computed specifier, `import.meta.glob`, an argument list it does not read. */
|
|
564
|
+
function hasUnreadImports(code) {
|
|
565
|
+
for (const match of code.matchAll(DYNAMIC_LOAD)) {
|
|
566
|
+
READABLE_LOAD.lastIndex = match.index;
|
|
567
|
+
if (!READABLE_LOAD.test(code)) return true;
|
|
568
|
+
}
|
|
569
|
+
return false;
|
|
570
|
+
}
|
|
571
|
+
function inspectsUrl(code) {
|
|
572
|
+
return URL_TOKENS.test(code);
|
|
573
|
+
}
|
|
574
|
+
function classifySpecifier(specifier, fromDir, aliasDir, nodePath) {
|
|
575
|
+
if (specifier.includes("${")) return "opaque";
|
|
576
|
+
const file = specifier.replace(/\?.*$/, "");
|
|
577
|
+
if (file.startsWith("./") || file.startsWith("../")) return { file: nodePath.resolve(fromDir, file) };
|
|
578
|
+
if (file.startsWith("@/")) return { file: nodePath.resolve(aliasDir, file.slice(2)) };
|
|
579
|
+
return TRUSTED_SPECIFIERS.some((prefix) => specifier.startsWith(prefix)) ? "trusted" : "opaque";
|
|
580
|
+
}
|
|
581
|
+
async function isFile(file) {
|
|
582
|
+
const { stat } = await import("node:fs/promises");
|
|
583
|
+
return stat(file).then((stats) => stats.isFile(), () => false);
|
|
584
|
+
}
|
|
585
|
+
async function resolveLocalImport(base, nodePath) {
|
|
586
|
+
const candidates = [base, ...IMPORT_EXTENSIONS.map((extension) => `${base}${extension}`)];
|
|
587
|
+
const extension = nodePath.extname(base);
|
|
588
|
+
for (const swapped of JS_TO_TS[extension] ?? []) candidates.push(`${base.slice(0, -extension.length)}${swapped}`);
|
|
589
|
+
candidates.push(...IMPORT_EXTENSIONS.map((suffix) => nodePath.join(base, `index${suffix}`)));
|
|
590
|
+
for (const candidate of candidates) if (await isFile(candidate)) return candidate;
|
|
591
|
+
}
|
|
592
|
+
/**
|
|
593
|
+
* Scans `entryFile` and every local module it imports, transitively; undefined without
|
|
594
|
+
* an entry. Anything it cannot read or vouch for leaves the scan incomplete, not guessed.
|
|
595
|
+
*/
|
|
596
|
+
async function scanEntry(entryFile, options) {
|
|
597
|
+
if (entryFile === void 0) return void 0;
|
|
598
|
+
const nodePath = await import("node:path");
|
|
599
|
+
const { readFile } = await import("node:fs/promises");
|
|
600
|
+
const scan = {
|
|
601
|
+
complete: true,
|
|
602
|
+
inspectsUrl: false,
|
|
603
|
+
requestMiddleware: false
|
|
604
|
+
};
|
|
605
|
+
const incomplete = () => ({
|
|
606
|
+
...scan,
|
|
607
|
+
complete: false
|
|
608
|
+
});
|
|
609
|
+
const seen = /* @__PURE__ */ new Set();
|
|
610
|
+
const queue = [entryFile];
|
|
611
|
+
let bytes = 0;
|
|
612
|
+
while (queue.length > 0) {
|
|
613
|
+
const file = queue.shift();
|
|
614
|
+
if (seen.has(file)) continue;
|
|
615
|
+
seen.add(file);
|
|
616
|
+
if (seen.size > 32 || file.split(nodePath.sep).includes("node_modules")) return incomplete();
|
|
617
|
+
const source = await readFile(file, "utf8").catch(() => void 0);
|
|
618
|
+
if (source === void 0) return incomplete();
|
|
619
|
+
bytes += Buffer.byteLength(source, "utf8");
|
|
620
|
+
if (bytes > 262144) return incomplete();
|
|
621
|
+
const { code, text } = lexSource(source);
|
|
622
|
+
scan.inspectsUrl ||= inspectsUrl(code);
|
|
623
|
+
scan.requestMiddleware ||= REQUEST_MIDDLEWARE.test(code);
|
|
624
|
+
if (hasUnreadImports(code)) return incomplete();
|
|
625
|
+
for (const specifier of importSpecifiers(text)) {
|
|
626
|
+
const resolution = classifySpecifier(specifier, nodePath.dirname(file), options.aliasDir, nodePath);
|
|
627
|
+
if (resolution === "trusted") continue;
|
|
628
|
+
const resolved = resolution === "opaque" ? void 0 : await resolveLocalImport(resolution.file, nodePath);
|
|
629
|
+
if (resolved === void 0) return incomplete();
|
|
630
|
+
if (CODE_EXTENSIONS.has(nodePath.extname(resolved))) queue.push(resolved);
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
return scan;
|
|
634
|
+
}
|
|
635
|
+
/** The manifest's verdict over the server entry and the start entry (request middleware). */
|
|
636
|
+
function urlInspectionOf(server, start) {
|
|
637
|
+
const serverInspects = server?.inspectsUrl === true;
|
|
638
|
+
const middlewareInspects = start?.inspectsUrl === true;
|
|
639
|
+
if (serverInspects && middlewareInspects) return "both";
|
|
640
|
+
if (serverInspects) return "server_entry";
|
|
641
|
+
if (middlewareInspects) return "request_middleware";
|
|
642
|
+
if (server?.complete === false || start?.complete === false) return "unknown";
|
|
643
|
+
return "none";
|
|
644
|
+
}
|
|
645
|
+
//#endregion
|
|
347
646
|
//#region src/routes.ts
|
|
348
647
|
const ROUTE_MANIFEST_VERSION = 1;
|
|
349
648
|
const GENERATOR = `${name}@${version}`;
|
|
@@ -618,13 +917,17 @@ function createRouteManifestPlugin(inputs) {
|
|
|
618
917
|
logWarning("Not publishing a route manifest: the route tree has no routes.");
|
|
619
918
|
return;
|
|
620
919
|
}
|
|
621
|
-
const
|
|
920
|
+
const serverFile = await resolveEntryFile(sourceDir, stringOption(subOptions(tanstackStart, "server"), "entry", "server"));
|
|
921
|
+
const startFile = await resolveEntryFile(sourceDir, stringOption(subOptions(tanstackStart, "start"), "entry", "start"));
|
|
922
|
+
const aliasDir = nodePath.resolve(process.cwd(), "src");
|
|
923
|
+
const [server, start] = await Promise.all([scanEntry(serverFile, { aliasDir }), scanEntry(startFile, { aliasDir })]);
|
|
622
924
|
const payload = serializeRouteManifest({
|
|
623
925
|
version: ROUTE_MANIFEST_VERSION,
|
|
624
926
|
generator: GENERATOR,
|
|
625
927
|
...basepath === "" ? {} : { basepath },
|
|
626
|
-
serverEntry:
|
|
627
|
-
...
|
|
928
|
+
serverEntry: serverFile === void 0 ? "default" : "custom",
|
|
929
|
+
...start === void 0 ? {} : { requestMiddleware: start.requestMiddleware },
|
|
930
|
+
urlInspection: urlInspectionOf(server, start),
|
|
628
931
|
routes
|
|
629
932
|
});
|
|
630
933
|
if (payload === void 0) return;
|
|
@@ -645,18 +948,6 @@ const LOVABLE_SANDBOX_ENV_VAR = "LOVABLE_SANDBOX";
|
|
|
645
948
|
function isSandboxEnvironment() {
|
|
646
949
|
return process.env[LOVABLE_SANDBOX_ENV_VAR] === "1" || !!process.env[SANDBOX_ENV_VAR];
|
|
647
950
|
}
|
|
648
|
-
function sandboxFsAllow(root) {
|
|
649
|
-
let nodeModules;
|
|
650
|
-
let realRoot;
|
|
651
|
-
try {
|
|
652
|
-
nodeModules = realpathSync.native(join(root, "node_modules"));
|
|
653
|
-
realRoot = realpathSync.native(root);
|
|
654
|
-
} catch {
|
|
655
|
-
return;
|
|
656
|
-
}
|
|
657
|
-
if (nodeModules.startsWith(realRoot + sep)) return void 0;
|
|
658
|
-
return [searchForWorkspaceRoot(root), nodeModules];
|
|
659
|
-
}
|
|
660
951
|
const TOOL_MANAGED_WATCH_IGNORES = [
|
|
661
952
|
"**/.workspace/**",
|
|
662
953
|
"**/.agents/**",
|
|
@@ -1582,6 +1873,7 @@ function defineConfig(configOrOptions = {}) {
|
|
|
1582
1873
|
}
|
|
1583
1874
|
}
|
|
1584
1875
|
if (command === "serve" && isSandbox) {
|
|
1876
|
+
internalPlugins.push(linkedDependenciesPlugin());
|
|
1585
1877
|
const { devServerBridgePlugin } = await import("./dev-server-bridge.js");
|
|
1586
1878
|
internalPlugins.push(devServerBridgePlugin());
|
|
1587
1879
|
}
|
|
@@ -1632,14 +1924,12 @@ function defineConfig(configOrOptions = {}) {
|
|
|
1632
1924
|
}
|
|
1633
1925
|
if (isSandbox) {
|
|
1634
1926
|
validateConfig(config);
|
|
1635
|
-
const fsAllow = sandboxFsAllow(process.cwd());
|
|
1636
1927
|
config = cleanServerConfig(mergeConfig(config, { server: {
|
|
1637
1928
|
host: "::",
|
|
1638
1929
|
port: 8080,
|
|
1639
1930
|
strictPort: true,
|
|
1640
1931
|
watch: { ignored: [...TOOL_MANAGED_WATCH_IGNORES] },
|
|
1641
|
-
hmr: { overlay: false }
|
|
1642
|
-
...fsAllow ? { fs: { allow: fsAllow } } : {}
|
|
1932
|
+
hmr: { overlay: false }
|
|
1643
1933
|
} }));
|
|
1644
1934
|
} else config = mergeConfig({ server: {
|
|
1645
1935
|
host: "::",
|