almostnode 0.2.4 → 0.2.6

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.mjs CHANGED
@@ -39,7 +39,8 @@ let __tla = (async () => {
39
39
  __publicField(this, "eventListeners", /* @__PURE__ */ new Map());
40
40
  this.root = {
41
41
  type: "directory",
42
- children: /* @__PURE__ */ new Map()
42
+ children: /* @__PURE__ */ new Map(),
43
+ mtime: Date.now()
43
44
  };
44
45
  }
45
46
  on(event, listener) {
@@ -152,7 +153,8 @@ let __tla = (async () => {
152
153
  const content = typeof data === "string" ? this.encoder.encode(data) : data;
153
154
  parent.children.set(basename2, {
154
155
  type: "file",
155
- content
156
+ content,
157
+ mtime: Date.now()
156
158
  });
157
159
  if (emitEvent) {
158
160
  this.notifyWatchers(normalized, existed ? "change" : "rename");
@@ -213,7 +215,8 @@ let __tla = (async () => {
213
215
  if (!child) {
214
216
  child = {
215
217
  type: "directory",
216
- children: /* @__PURE__ */ new Map()
218
+ children: /* @__PURE__ */ new Map(),
219
+ mtime: Date.now()
217
220
  };
218
221
  current.children.set(segment, child);
219
222
  } else if (child.type !== "directory") {
@@ -232,8 +235,8 @@ let __tla = (async () => {
232
235
  if (!node) {
233
236
  throw createNodeError("ENOENT", "stat", path2);
234
237
  }
235
- const now = Date.now();
236
238
  const size = node.type === "file" ? ((_a2 = node.content) == null ? void 0 : _a2.length) || 0 : 0;
239
+ const mtime = node.mtime;
237
240
  return {
238
241
  isFile: () => node.type === "file",
239
242
  isDirectory: () => node.type === "directory",
@@ -244,14 +247,14 @@ let __tla = (async () => {
244
247
  isSocket: () => false,
245
248
  size,
246
249
  mode: node.type === "directory" ? 493 : 420,
247
- mtime: new Date(now),
248
- atime: new Date(now),
249
- ctime: new Date(now),
250
- birthtime: new Date(now),
251
- mtimeMs: now,
252
- atimeMs: now,
253
- ctimeMs: now,
254
- birthtimeMs: now,
250
+ mtime: new Date(mtime),
251
+ atime: new Date(mtime),
252
+ ctime: new Date(mtime),
253
+ birthtime: new Date(mtime),
254
+ mtimeMs: mtime,
255
+ atimeMs: mtime,
256
+ ctimeMs: mtime,
257
+ birthtimeMs: mtime,
255
258
  nlink: 1,
256
259
  uid: 1e3,
257
260
  gid: 1e3,
@@ -305,7 +308,8 @@ let __tla = (async () => {
305
308
  }
306
309
  parent.children.set(basename2, {
307
310
  type: "directory",
308
- children: /* @__PURE__ */ new Map()
311
+ children: /* @__PURE__ */ new Map(),
312
+ mtime: Date.now()
309
313
  });
310
314
  }
311
315
  readdirSync(path2) {
@@ -589,6 +593,14 @@ let __tla = (async () => {
589
593
  };
590
594
  }
591
595
  };
596
+ function simpleHash(str) {
597
+ let hash = 0;
598
+ for (let i = 0; i < str.length; i++) {
599
+ hash = (hash << 5) - hash + str.charCodeAt(i);
600
+ hash |= 0;
601
+ }
602
+ return hash.toString(36);
603
+ }
592
604
  class Dirent {
593
605
  constructor(name, isDirectory, isFile) {
594
606
  __publicField(this, "name");
@@ -9188,7 +9200,23 @@ let __tla = (async () => {
9188
9200
  "@sentry/node": sentryShim,
9189
9201
  "@sentry/core": sentryShim
9190
9202
  };
9191
- function createRequire(vfs, fsShim, process, currentDir, moduleCache, options) {
9203
+ function createRequire(vfs, fsShim, process, currentDir, moduleCache, options, processedCodeCache) {
9204
+ const resolutionCache = /* @__PURE__ */ new Map();
9205
+ const packageJsonCache = /* @__PURE__ */ new Map();
9206
+ const getParsedPackageJson = (pkgPath) => {
9207
+ if (packageJsonCache.has(pkgPath)) {
9208
+ return packageJsonCache.get(pkgPath);
9209
+ }
9210
+ try {
9211
+ const content = vfs.readFileSync(pkgPath, "utf8");
9212
+ const parsed = JSON.parse(content);
9213
+ packageJsonCache.set(pkgPath, parsed);
9214
+ return parsed;
9215
+ } catch {
9216
+ packageJsonCache.set(pkgPath, null);
9217
+ return null;
9218
+ }
9219
+ };
9192
9220
  const resolveModule = (id, fromDir) => {
9193
9221
  if (id.startsWith("node:")) {
9194
9222
  id = id.slice(5);
@@ -9196,15 +9224,25 @@ let __tla = (async () => {
9196
9224
  if (builtinModules[id] || id === "fs" || id === "process" || id === "url" || id === "querystring" || id === "util") {
9197
9225
  return id;
9198
9226
  }
9227
+ const cacheKey = `${fromDir}|${id}`;
9228
+ const cached = resolutionCache.get(cacheKey);
9229
+ if (cached !== void 0) {
9230
+ if (cached === null) {
9231
+ throw new Error(`Cannot find module '${id}'`);
9232
+ }
9233
+ return cached;
9234
+ }
9199
9235
  if (id.startsWith("./") || id.startsWith("../") || id.startsWith("/")) {
9200
9236
  const resolved = id.startsWith("/") ? id : resolve$2(fromDir, id);
9201
9237
  if (vfs.existsSync(resolved)) {
9202
9238
  const stats = vfs.statSync(resolved);
9203
9239
  if (stats.isFile()) {
9240
+ resolutionCache.set(cacheKey, resolved);
9204
9241
  return resolved;
9205
9242
  }
9206
9243
  const indexPath = join(resolved, "index.js");
9207
9244
  if (vfs.existsSync(indexPath)) {
9245
+ resolutionCache.set(cacheKey, indexPath);
9208
9246
  return indexPath;
9209
9247
  }
9210
9248
  }
@@ -9215,9 +9253,11 @@ let __tla = (async () => {
9215
9253
  for (const ext of extensions) {
9216
9254
  const withExt = resolved + ext;
9217
9255
  if (vfs.existsSync(withExt)) {
9256
+ resolutionCache.set(cacheKey, withExt);
9218
9257
  return withExt;
9219
9258
  }
9220
9259
  }
9260
+ resolutionCache.set(cacheKey, null);
9221
9261
  throw new Error(`Cannot find module '${id}' from '${fromDir}'`);
9222
9262
  }
9223
9263
  const tryResolveFile = (basePath) => {
@@ -9252,9 +9292,8 @@ let __tla = (async () => {
9252
9292
  const pkgName = parts[0].startsWith("@") && parts.length > 1 ? `${parts[0]}/${parts[1]}` : parts[0];
9253
9293
  const pkgRoot = join(nodeModulesDir, pkgName);
9254
9294
  const pkgPath = join(pkgRoot, "package.json");
9255
- if (vfs.existsSync(pkgPath)) {
9256
- const pkgContent = vfs.readFileSync(pkgPath, "utf8");
9257
- const pkg = JSON.parse(pkgContent);
9295
+ const pkg = getParsedPackageJson(pkgPath);
9296
+ if (pkg) {
9258
9297
  if (pkg.exports) {
9259
9298
  try {
9260
9299
  const resolved2 = resolve$3(pkg, moduleId, {
@@ -9282,11 +9321,18 @@ let __tla = (async () => {
9282
9321
  while (searchDir !== "/") {
9283
9322
  const nodeModulesDir = join(searchDir, "node_modules");
9284
9323
  const resolved = tryResolveFromNodeModules(nodeModulesDir, id);
9285
- if (resolved) return resolved;
9324
+ if (resolved) {
9325
+ resolutionCache.set(cacheKey, resolved);
9326
+ return resolved;
9327
+ }
9286
9328
  searchDir = dirname(searchDir);
9287
9329
  }
9288
9330
  const rootResolved = tryResolveFromNodeModules("/node_modules", id);
9289
- if (rootResolved) return rootResolved;
9331
+ if (rootResolved) {
9332
+ resolutionCache.set(cacheKey, rootResolved);
9333
+ return rootResolved;
9334
+ }
9335
+ resolutionCache.set(cacheKey, null);
9290
9336
  throw new Error(`Cannot find module '${id}'`);
9291
9337
  };
9292
9338
  const loadModule = (resolvedPath) => {
@@ -9308,19 +9354,25 @@ let __tla = (async () => {
9308
9354
  module.loaded = true;
9309
9355
  return module;
9310
9356
  }
9311
- let code = vfs.readFileSync(resolvedPath, "utf8");
9357
+ const rawCode = vfs.readFileSync(resolvedPath, "utf8");
9312
9358
  const dirname$1 = dirname(resolvedPath);
9313
- const isCjsFile = resolvedPath.endsWith(".cjs");
9314
- const isAlreadyBundledCjs = code.startsWith('"use strict";\nvar __') || code.startsWith("'use strict';\nvar __");
9315
- const hasEsmImport = /\bimport\s+[\w{*'"]/m.test(code);
9316
- const hasEsmExport = /\bexport\s+(?:default|const|let|var|function|class|{|\*)/m.test(code);
9317
- if (!isCjsFile && !isAlreadyBundledCjs) {
9318
- if (resolvedPath.endsWith(".mjs") || resolvedPath.includes("/esm/") || hasEsmImport || hasEsmExport) {
9319
- code = transformEsmToCjs(code, resolvedPath);
9320
- }
9321
- }
9322
- code = transformDynamicImports(code);
9323
- const moduleRequire = createRequire(vfs, fsShim, process, dirname$1, moduleCache, options);
9359
+ const codeCacheKey = `${resolvedPath}|${simpleHash(rawCode)}`;
9360
+ let code = processedCodeCache == null ? void 0 : processedCodeCache.get(codeCacheKey);
9361
+ if (!code) {
9362
+ code = rawCode;
9363
+ const isCjsFile = resolvedPath.endsWith(".cjs");
9364
+ const isAlreadyBundledCjs = code.startsWith('"use strict";\nvar __') || code.startsWith("'use strict';\nvar __");
9365
+ const hasEsmImport = /\bimport\s+[\w{*'"]/m.test(code);
9366
+ const hasEsmExport = /\bexport\s+(?:default|const|let|var|function|class|{|\*)/m.test(code);
9367
+ if (!isCjsFile && !isAlreadyBundledCjs) {
9368
+ if (resolvedPath.endsWith(".mjs") || resolvedPath.includes("/esm/") || hasEsmImport || hasEsmExport) {
9369
+ code = transformEsmToCjs(code, resolvedPath);
9370
+ }
9371
+ }
9372
+ code = transformDynamicImports(code);
9373
+ processedCodeCache == null ? void 0 : processedCodeCache.set(codeCacheKey, code);
9374
+ }
9375
+ const moduleRequire = createRequire(vfs, fsShim, process, dirname$1, moduleCache, options, processedCodeCache);
9324
9376
  moduleRequire.cache = moduleCache;
9325
9377
  const consoleWrapper = createConsoleWrapper(options.onConsole);
9326
9378
  try {
@@ -9492,6 +9544,7 @@ ${code}
9492
9544
  __publicField(this, "process");
9493
9545
  __publicField(this, "moduleCache", {});
9494
9546
  __publicField(this, "options");
9547
+ __publicField(this, "processedCodeCache", /* @__PURE__ */ new Map());
9495
9548
  __publicField(this, "executeSync", this.execute);
9496
9549
  __publicField(this, "runFileSync", this.runFile);
9497
9550
  this.vfs = vfs2;
@@ -9576,7 +9629,7 @@ ${code}
9576
9629
  execute(code, filename = "/index.js") {
9577
9630
  const dirname$1 = dirname(filename);
9578
9631
  this.vfs.writeFileSync(filename, code);
9579
- const require = createRequire(this.vfs, this.fsShim, this.process, dirname$1, this.moduleCache, this.options);
9632
+ const require = createRequire(this.vfs, this.fsShim, this.process, dirname$1, this.moduleCache, this.options, this.processedCodeCache);
9580
9633
  const module = {
9581
9634
  id: filename,
9582
9635
  filename,
@@ -9658,7 +9711,7 @@ ${code}
9658
9711
  __publicField(this, "deleteListener", null);
9659
9712
  this.vfs = vfs2;
9660
9713
  this.options = options2;
9661
- this.worker = new Worker(new URL("/assets/runtime-worker-D9x_Ddwz.js", import.meta.url), {
9714
+ this.worker = new Worker(new URL("/assets/runtime-worker-B8_LZkBX.js", import.meta.url), {
9662
9715
  type: "module"
9663
9716
  });
9664
9717
  this.workerApi = wrap(this.worker);
@@ -11626,6 +11679,7 @@ console.log('[HMR] React Refresh initialized');
11626
11679
  __publicField(this, "watcherCleanup", null);
11627
11680
  __publicField(this, "options");
11628
11681
  __publicField(this, "hmrTargetWindow", null);
11682
+ __publicField(this, "transformCache", /* @__PURE__ */ new Map());
11629
11683
  this.options = {
11630
11684
  jsx: true,
11631
11685
  jsxFactory: "React.createElement",
@@ -11742,7 +11796,28 @@ console.log('[HMR] React Refresh initialized');
11742
11796
  async transformAndServe(filePath, urlPath) {
11743
11797
  try {
11744
11798
  const content = this.vfs.readFileSync(filePath, "utf8");
11799
+ const hash = simpleHash(content);
11800
+ const cached = this.transformCache.get(filePath);
11801
+ if (cached && cached.hash === hash) {
11802
+ const buffer22 = BufferPolyfill.from(cached.code);
11803
+ return {
11804
+ statusCode: 200,
11805
+ statusMessage: "OK",
11806
+ headers: {
11807
+ "Content-Type": "application/javascript; charset=utf-8",
11808
+ "Content-Length": String(buffer22.length),
11809
+ "Cache-Control": "no-cache",
11810
+ "X-Transformed": "true",
11811
+ "X-Cache": "hit"
11812
+ },
11813
+ body: buffer22
11814
+ };
11815
+ }
11745
11816
  const transformed = await this.transformCode(content, urlPath);
11817
+ this.transformCache.set(filePath, {
11818
+ code: transformed,
11819
+ hash
11820
+ });
11746
11821
  const buffer2 = BufferPolyfill.from(transformed);
11747
11822
  return {
11748
11823
  statusCode: 200,
@@ -11902,6 +11977,123 @@ export default css;
11902
11977
  }
11903
11978
  }
11904
11979
  };
11980
+ const CONFIG_FILE_NAMES = [
11981
+ "/tailwind.config.ts",
11982
+ "/tailwind.config.js",
11983
+ "/tailwind.config.mjs"
11984
+ ];
11985
+ async function loadTailwindConfig(vfs2, root = "/") {
11986
+ let configPath = null;
11987
+ let configContent = null;
11988
+ for (const fileName of CONFIG_FILE_NAMES) {
11989
+ const fullPath = root === "/" ? fileName : `${root}${fileName}`;
11990
+ try {
11991
+ const content = vfs2.readFileSync(fullPath);
11992
+ configContent = typeof content === "string" ? content : content instanceof Uint8Array ? new TextDecoder("utf-8").decode(content) : Buffer.from(content).toString("utf-8");
11993
+ configPath = fullPath;
11994
+ break;
11995
+ } catch {
11996
+ continue;
11997
+ }
11998
+ }
11999
+ if (!configPath || configContent === null) {
12000
+ return {
12001
+ configScript: "",
12002
+ success: true
12003
+ };
12004
+ }
12005
+ try {
12006
+ const jsConfig = stripTypescriptSyntax(configContent);
12007
+ const configObject = extractConfigObject(jsConfig);
12008
+ if (!configObject) {
12009
+ return {
12010
+ configScript: "",
12011
+ success: false,
12012
+ error: "Could not extract config object from tailwind.config"
12013
+ };
12014
+ }
12015
+ const configScript = generateConfigScript(configObject);
12016
+ return {
12017
+ configScript,
12018
+ success: true
12019
+ };
12020
+ } catch (error) {
12021
+ return {
12022
+ configScript: "",
12023
+ success: false,
12024
+ error: `Failed to parse tailwind.config: ${error instanceof Error ? error.message : String(error)}`
12025
+ };
12026
+ }
12027
+ }
12028
+ function stripTypescriptSyntax(content) {
12029
+ let result = content;
12030
+ result = result.replace(/import\s+type\s+\{[^}]*\}\s+from\s+['"][^'"]*['"]\s*;?\s*/g, "");
12031
+ result = result.replace(/import\s+\{[^}]*\}\s+from\s+['"][^'"]*['"]\s*;?\s*/g, "");
12032
+ result = result.replace(/\s+satisfies\s+\w+\s*$/gm, "");
12033
+ result = result.replace(/\s+satisfies\s+\w+\s*;?\s*$/gm, "");
12034
+ result = result.replace(/:\s*Config\s*=/g, " =");
12035
+ result = result.replace(/\s+as\s+const\s*/g, " ");
12036
+ return result;
12037
+ }
12038
+ function extractConfigObject(content) {
12039
+ const exportDefaultMatch = content.match(/export\s+default\s*/);
12040
+ if (!exportDefaultMatch || exportDefaultMatch.index === void 0) {
12041
+ return null;
12042
+ }
12043
+ const startIndex = exportDefaultMatch.index + exportDefaultMatch[0].length;
12044
+ const remaining = content.substring(startIndex);
12045
+ const trimmedRemaining = remaining.trimStart();
12046
+ if (!trimmedRemaining.startsWith("{")) {
12047
+ return null;
12048
+ }
12049
+ const objectStart = startIndex + (remaining.length - trimmedRemaining.length);
12050
+ const objectContent = content.substring(objectStart);
12051
+ let braceCount = 0;
12052
+ let inString = false;
12053
+ let stringChar = "";
12054
+ let escaped = false;
12055
+ let endIndex = -1;
12056
+ for (let i = 0; i < objectContent.length; i++) {
12057
+ const char = objectContent[i];
12058
+ if (escaped) {
12059
+ escaped = false;
12060
+ continue;
12061
+ }
12062
+ if (char === "\\") {
12063
+ escaped = true;
12064
+ continue;
12065
+ }
12066
+ if (inString) {
12067
+ if (char === stringChar) {
12068
+ inString = false;
12069
+ }
12070
+ continue;
12071
+ }
12072
+ if (char === '"' || char === "'" || char === "`") {
12073
+ inString = true;
12074
+ stringChar = char;
12075
+ continue;
12076
+ }
12077
+ if (char === "{") {
12078
+ braceCount++;
12079
+ } else if (char === "}") {
12080
+ braceCount--;
12081
+ if (braceCount === 0) {
12082
+ endIndex = i + 1;
12083
+ break;
12084
+ }
12085
+ }
12086
+ }
12087
+ if (endIndex === -1) {
12088
+ return null;
12089
+ }
12090
+ return objectContent.substring(0, endIndex);
12091
+ }
12092
+ function generateConfigScript(configObject) {
12093
+ return `<script>
12094
+ tailwind.config = ${configObject};
12095
+ <\/script>`;
12096
+ }
11905
12097
  const isBrowser = typeof window !== "undefined" && typeof window.navigator !== "undefined" && "serviceWorker" in window.navigator;
11906
12098
  async function initEsbuild() {
11907
12099
  if (!isBrowser) return;
@@ -12126,25 +12318,31 @@ const applyVirtualBase = (url) => {
12126
12318
 
12127
12319
  export default function Link({ href, children, ...props }) {
12128
12320
  const handleClick = (e) => {
12321
+ console.log('[Link] Click handler called, href:', href);
12322
+
12129
12323
  if (props.onClick) {
12130
12324
  props.onClick(e);
12131
12325
  }
12132
12326
 
12133
12327
  // Allow cmd/ctrl click to open in new tab
12134
12328
  if (e.metaKey || e.ctrlKey) {
12329
+ console.log('[Link] Meta/Ctrl key pressed, allowing default behavior');
12135
12330
  return;
12136
12331
  }
12137
12332
 
12138
12333
  if (typeof href !== 'string' || !href || href.startsWith('#') || href.startsWith('?')) {
12334
+ console.log('[Link] Skipping navigation for href:', href);
12139
12335
  return;
12140
12336
  }
12141
12337
 
12142
12338
  if (/^(https?:)?\\/\\//.test(href)) {
12339
+ console.log('[Link] External URL, allowing default behavior:', href);
12143
12340
  return;
12144
12341
  }
12145
12342
 
12146
12343
  e.preventDefault();
12147
12344
  const resolvedHref = applyVirtualBase(href);
12345
+ console.log('[Link] Navigating to:', resolvedHref);
12148
12346
  window.history.pushState({}, '', resolvedHref);
12149
12347
  window.dispatchEvent(new PopStateEvent('popstate'));
12150
12348
  };
@@ -12482,6 +12680,305 @@ export default function Head({ children }) {
12482
12680
 
12483
12681
  return null;
12484
12682
  }
12683
+ `;
12684
+ const NEXT_IMAGE_SHIM = `
12685
+ import React from 'react';
12686
+
12687
+ function Image({
12688
+ src,
12689
+ alt = '',
12690
+ width,
12691
+ height,
12692
+ fill,
12693
+ loader,
12694
+ quality = 75,
12695
+ priority,
12696
+ loading,
12697
+ placeholder,
12698
+ blurDataURL,
12699
+ unoptimized,
12700
+ onLoad,
12701
+ onError,
12702
+ style,
12703
+ className,
12704
+ sizes,
12705
+ ...rest
12706
+ }) {
12707
+ // Handle src - could be string or StaticImageData object
12708
+ const imageSrc = typeof src === 'object' ? src.src : src;
12709
+
12710
+ // Build style object
12711
+ const imgStyle = { ...style };
12712
+ if (fill) {
12713
+ imgStyle.position = 'absolute';
12714
+ imgStyle.width = '100%';
12715
+ imgStyle.height = '100%';
12716
+ imgStyle.objectFit = imgStyle.objectFit || 'cover';
12717
+ imgStyle.inset = '0';
12718
+ }
12719
+
12720
+ return React.createElement('img', {
12721
+ src: imageSrc,
12722
+ alt,
12723
+ width: fill ? undefined : width,
12724
+ height: fill ? undefined : height,
12725
+ loading: priority ? 'eager' : (loading || 'lazy'),
12726
+ decoding: 'async',
12727
+ style: imgStyle,
12728
+ className,
12729
+ onLoad,
12730
+ onError,
12731
+ ...rest
12732
+ });
12733
+ }
12734
+
12735
+ export default Image;
12736
+ export { Image };
12737
+ `;
12738
+ const NEXT_DYNAMIC_SHIM = `
12739
+ import React from 'react';
12740
+
12741
+ function dynamic(importFn, options = {}) {
12742
+ const {
12743
+ loading: LoadingComponent,
12744
+ ssr = true,
12745
+ } = options;
12746
+
12747
+ // Create a lazy component
12748
+ const LazyComponent = React.lazy(importFn);
12749
+
12750
+ // Wrapper component that handles loading state
12751
+ function DynamicComponent(props) {
12752
+ const fallback = LoadingComponent
12753
+ ? React.createElement(LoadingComponent, { isLoading: true })
12754
+ : null;
12755
+
12756
+ return React.createElement(
12757
+ React.Suspense,
12758
+ { fallback },
12759
+ React.createElement(LazyComponent, props)
12760
+ );
12761
+ }
12762
+
12763
+ return DynamicComponent;
12764
+ }
12765
+
12766
+ export default dynamic;
12767
+ export { dynamic };
12768
+ `;
12769
+ const NEXT_SCRIPT_SHIM = `
12770
+ import React from 'react';
12771
+
12772
+ function Script({
12773
+ src,
12774
+ strategy = 'afterInteractive',
12775
+ onLoad,
12776
+ onReady,
12777
+ onError,
12778
+ children,
12779
+ dangerouslySetInnerHTML,
12780
+ ...rest
12781
+ }) {
12782
+ React.useEffect(function() {
12783
+ if (!src && !children && !dangerouslySetInnerHTML) return;
12784
+
12785
+ var script = document.createElement('script');
12786
+
12787
+ if (src) {
12788
+ script.src = src;
12789
+ script.async = strategy !== 'beforeInteractive';
12790
+ }
12791
+
12792
+ Object.keys(rest).forEach(function(key) {
12793
+ script.setAttribute(key, rest[key]);
12794
+ });
12795
+
12796
+ if (children) {
12797
+ script.textContent = children;
12798
+ } else if (dangerouslySetInnerHTML && dangerouslySetInnerHTML.__html) {
12799
+ script.textContent = dangerouslySetInnerHTML.__html;
12800
+ }
12801
+
12802
+ script.onload = function() {
12803
+ if (onLoad) onLoad();
12804
+ if (onReady) onReady();
12805
+ };
12806
+ script.onerror = onError;
12807
+
12808
+ document.head.appendChild(script);
12809
+
12810
+ return function() {
12811
+ if (script.parentNode) {
12812
+ script.parentNode.removeChild(script);
12813
+ }
12814
+ };
12815
+ }, [src]);
12816
+
12817
+ return null;
12818
+ }
12819
+
12820
+ export default Script;
12821
+ export { Script };
12822
+ `;
12823
+ const NEXT_FONT_GOOGLE_SHIM = `
12824
+ // Track loaded fonts to avoid duplicate style injections
12825
+ const loadedFonts = new Set();
12826
+
12827
+ /**
12828
+ * Convert font function name to Google Fonts family name
12829
+ * Examples:
12830
+ * DM_Sans -> DM Sans
12831
+ * Open_Sans -> Open Sans
12832
+ * Fraunces -> Fraunces
12833
+ */
12834
+ function toFontFamily(fontName) {
12835
+ return fontName.replace(/_/g, ' ');
12836
+ }
12837
+
12838
+ /**
12839
+ * Inject font CSS into document
12840
+ * - Adds preconnect links for faster font loading
12841
+ * - Loads the font from Google Fonts CDN
12842
+ * - Creates a CSS class that sets the CSS variable
12843
+ */
12844
+ function injectFontCSS(fontFamily, variableName, weight, style) {
12845
+ const fontKey = fontFamily + '-' + (variableName || 'default');
12846
+ if (loadedFonts.has(fontKey)) {
12847
+ return;
12848
+ }
12849
+ loadedFonts.add(fontKey);
12850
+
12851
+ if (typeof document === 'undefined') {
12852
+ return;
12853
+ }
12854
+
12855
+ // Add preconnect links for faster loading (only once)
12856
+ if (!document.querySelector('link[href="https://fonts.googleapis.com"]')) {
12857
+ const preconnect1 = document.createElement('link');
12858
+ preconnect1.rel = 'preconnect';
12859
+ preconnect1.href = 'https://fonts.googleapis.com';
12860
+ document.head.appendChild(preconnect1);
12861
+
12862
+ const preconnect2 = document.createElement('link');
12863
+ preconnect2.rel = 'preconnect';
12864
+ preconnect2.href = 'https://fonts.gstatic.com';
12865
+ preconnect2.crossOrigin = 'anonymous';
12866
+ document.head.appendChild(preconnect2);
12867
+ }
12868
+
12869
+ // Build Google Fonts URL
12870
+ const escapedFamily = fontFamily.replace(/ /g, '+');
12871
+
12872
+ // Build axis list based on options
12873
+ let axisList = '';
12874
+ const axes = [];
12875
+
12876
+ // Handle italic style
12877
+ if (style === 'italic') {
12878
+ axes.push('ital');
12879
+ }
12880
+
12881
+ // Handle weight - use specific weight or variable range
12882
+ if (weight && weight !== '400' && !Array.isArray(weight)) {
12883
+ // Specific weight requested
12884
+ axes.push('wght');
12885
+ if (style === 'italic') {
12886
+ axisList = ':ital,wght@1,' + weight;
12887
+ } else {
12888
+ axisList = ':wght@' + weight;
12889
+ }
12890
+ } else if (Array.isArray(weight)) {
12891
+ // Multiple weights
12892
+ axes.push('wght');
12893
+ axisList = ':wght@' + weight.join(';');
12894
+ } else {
12895
+ // Default: request common weights for flexibility
12896
+ axisList = ':wght@400;500;600;700';
12897
+ }
12898
+
12899
+ const fontUrl = 'https://fonts.googleapis.com/css2?family=' +
12900
+ escapedFamily + axisList + '&display=swap';
12901
+
12902
+ // Add link element for Google Fonts (if not already present)
12903
+ if (!document.querySelector('link[href*="family=' + escapedFamily + '"]')) {
12904
+ const link = document.createElement('link');
12905
+ link.rel = 'stylesheet';
12906
+ link.href = fontUrl;
12907
+ document.head.appendChild(link);
12908
+ }
12909
+
12910
+ // Create style element for CSS variable at :root level (globally available)
12911
+ // This makes the variable work without needing to apply the class to body
12912
+ if (variableName) {
12913
+ const styleEl = document.createElement('style');
12914
+ styleEl.setAttribute('data-font-var', variableName);
12915
+ styleEl.textContent = ':root { ' + variableName + ': "' + fontFamily + '", ' + (fontFamily.includes('Serif') ? 'serif' : 'sans-serif') + '; }';
12916
+ document.head.appendChild(styleEl);
12917
+ }
12918
+ }
12919
+
12920
+ /**
12921
+ * Create a font loader function for a specific font
12922
+ */
12923
+ function createFontLoader(fontName) {
12924
+ const fontFamily = toFontFamily(fontName);
12925
+
12926
+ return function(options = {}) {
12927
+ const {
12928
+ weight,
12929
+ style = 'normal',
12930
+ subsets = ['latin'],
12931
+ variable,
12932
+ display = 'swap',
12933
+ preload = true,
12934
+ fallback = ['sans-serif'],
12935
+ adjustFontFallback = true
12936
+ } = options;
12937
+
12938
+ // Inject the font CSS
12939
+ injectFontCSS(fontFamily, variable, weight, style);
12940
+
12941
+ // Generate class name from variable (--font-inter -> __font-inter)
12942
+ const className = variable
12943
+ ? variable.replace('--', '__')
12944
+ : '__font-' + fontName.toLowerCase().replace(/_/g, '-');
12945
+
12946
+ return {
12947
+ className,
12948
+ variable: className,
12949
+ style: {
12950
+ fontFamily: '"' + fontFamily + '", ' + fallback.join(', ')
12951
+ }
12952
+ };
12953
+ };
12954
+ }
12955
+
12956
+ /**
12957
+ * Use a Proxy to dynamically create font loaders for ANY font name
12958
+ * This allows: import { AnyGoogleFont } from "next/font/google"
12959
+ */
12960
+ const fontProxy = new Proxy({}, {
12961
+ get(target, prop) {
12962
+ // Handle special properties
12963
+ if (prop === '__esModule') return true;
12964
+ if (prop === 'default') return fontProxy;
12965
+ if (typeof prop !== 'string') return undefined;
12966
+
12967
+ // Create a font loader for this font name
12968
+ return createFontLoader(prop);
12969
+ }
12970
+ });
12971
+
12972
+ // Export the proxy as both default and named exports
12973
+ export default fontProxy;
12974
+
12975
+ // Re-export through proxy for named imports
12976
+ export const {
12977
+ Fraunces, Inter, DM_Sans, DM_Serif_Text, Roboto, Open_Sans, Lato,
12978
+ Montserrat, Poppins, Playfair_Display, Merriweather, Raleway, Nunito,
12979
+ Ubuntu, Oswald, Quicksand, Work_Sans, Fira_Sans, Barlow, Mulish, Rubik,
12980
+ Noto_Sans, Manrope, Space_Grotesk, Geist, Geist_Mono
12981
+ } = fontProxy;
12485
12982
  `;
12486
12983
  NextDevServer = class extends DevServer {
12487
12984
  constructor(vfs2, options2) {
@@ -12493,6 +12990,11 @@ export default function Head({ children }) {
12493
12990
  __publicField(this, "watcherCleanup", null);
12494
12991
  __publicField(this, "hmrTargetWindow", null);
12495
12992
  __publicField(this, "options");
12993
+ __publicField(this, "transformCache", /* @__PURE__ */ new Map());
12994
+ __publicField(this, "pathAliases", /* @__PURE__ */ new Map());
12995
+ __publicField(this, "tailwindConfigScript", "");
12996
+ __publicField(this, "tailwindConfigLoaded", false);
12997
+ __publicField(this, "assetPrefix", "");
12496
12998
  this.options = options2;
12497
12999
  this.pagesDir = options2.pagesDir || "/pages";
12498
13000
  this.appDir = options2.appDir || "/app";
@@ -12502,6 +13004,82 @@ export default function Head({ children }) {
12502
13004
  } else {
12503
13005
  this.useAppRouter = this.hasAppRouter();
12504
13006
  }
13007
+ this.loadPathAliases();
13008
+ this.loadAssetPrefix(options2.assetPrefix);
13009
+ }
13010
+ loadPathAliases() {
13011
+ var _a2;
13012
+ try {
13013
+ const tsconfigPath = "/tsconfig.json";
13014
+ if (!this.vfs.existsSync(tsconfigPath)) {
13015
+ return;
13016
+ }
13017
+ const content = this.vfs.readFileSync(tsconfigPath, "utf-8");
13018
+ const tsconfig = JSON.parse(content);
13019
+ const paths = (_a2 = tsconfig == null ? void 0 : tsconfig.compilerOptions) == null ? void 0 : _a2.paths;
13020
+ if (!paths) {
13021
+ return;
13022
+ }
13023
+ for (const [alias, targets] of Object.entries(paths)) {
13024
+ if (Array.isArray(targets) && targets.length > 0) {
13025
+ const aliasPrefix = alias.replace(/\*$/, "");
13026
+ const targetPrefix = targets[0].replace(/\*$/, "").replace(/^\./, "");
13027
+ this.pathAliases.set(aliasPrefix, targetPrefix);
13028
+ }
13029
+ }
13030
+ } catch (e) {
13031
+ }
13032
+ }
13033
+ loadAssetPrefix(optionValue) {
13034
+ if (optionValue !== void 0) {
13035
+ this.assetPrefix = optionValue.startsWith("/") ? optionValue : `/${optionValue}`;
13036
+ if (this.assetPrefix.endsWith("/")) {
13037
+ this.assetPrefix = this.assetPrefix.slice(0, -1);
13038
+ }
13039
+ return;
13040
+ }
13041
+ try {
13042
+ const configFiles = [
13043
+ "/next.config.ts",
13044
+ "/next.config.js",
13045
+ "/next.config.mjs"
13046
+ ];
13047
+ for (const configPath of configFiles) {
13048
+ if (!this.vfs.existsSync(configPath)) {
13049
+ continue;
13050
+ }
13051
+ const content = this.vfs.readFileSync(configPath, "utf-8");
13052
+ const match = content.match(/assetPrefix\s*:\s*["']([^"']+)["']/);
13053
+ if (match) {
13054
+ let prefix = match[1];
13055
+ if (!prefix.startsWith("/")) {
13056
+ prefix = `/${prefix}`;
13057
+ }
13058
+ if (prefix.endsWith("/")) {
13059
+ prefix = prefix.slice(0, -1);
13060
+ }
13061
+ this.assetPrefix = prefix;
13062
+ return;
13063
+ }
13064
+ }
13065
+ } catch (e) {
13066
+ }
13067
+ }
13068
+ resolvePathAliases(code2, currentFile) {
13069
+ if (this.pathAliases.size === 0) {
13070
+ return code2;
13071
+ }
13072
+ const virtualBase = `/__virtual__/${this.port}`;
13073
+ let result = code2;
13074
+ for (const [alias, target] of this.pathAliases) {
13075
+ const aliasEscaped = alias.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
13076
+ const pattern = new RegExp(`(from\\s*['"]|import\\s*\\(\\s*['"])${aliasEscaped}([^'"]+)(['"])`, "g");
13077
+ result = result.replace(pattern, (match, prefix, path2, quote) => {
13078
+ const resolvedPath2 = `${virtualBase}${target}${path2}`;
13079
+ return `${prefix}${resolvedPath2}${quote}`;
13080
+ });
13081
+ }
13082
+ return result;
12505
13083
  }
12506
13084
  setEnv(key, value) {
12507
13085
  this.options.env = this.options.env || {};
@@ -12517,20 +13095,38 @@ export default function Head({ children }) {
12517
13095
  }
12518
13096
  generateEnvScript() {
12519
13097
  const env = this.options.env || {};
12520
- const publicEnvVars = Object.entries(env).filter(([key]) => key.startsWith("NEXT_PUBLIC_")).reduce((acc, [k, v]) => ({
12521
- ...acc,
12522
- [k]: v
12523
- }), {});
12524
- if (Object.keys(publicEnvVars).length === 0) {
12525
- return "";
13098
+ const publicEnvVars = {};
13099
+ for (const [key, value] of Object.entries(env)) {
13100
+ if (key.startsWith("NEXT_PUBLIC_")) {
13101
+ publicEnvVars[key] = value;
13102
+ }
12526
13103
  }
12527
13104
  return `<script>
12528
- // NEXT_PUBLIC_* environment variables (injected by NextDevServer)
13105
+ // Environment variables (injected by NextDevServer)
12529
13106
  window.process = window.process || {};
12530
13107
  window.process.env = window.process.env || {};
12531
13108
  Object.assign(window.process.env, ${JSON.stringify(publicEnvVars)});
12532
13109
  <\/script>`;
12533
13110
  }
13111
+ async loadTailwindConfigIfNeeded() {
13112
+ if (this.tailwindConfigLoaded) {
13113
+ return this.tailwindConfigScript;
13114
+ }
13115
+ try {
13116
+ const result = await loadTailwindConfig(this.vfs, this.root);
13117
+ if (result.success) {
13118
+ this.tailwindConfigScript = result.configScript;
13119
+ } else if (result.error) {
13120
+ console.warn("[NextDevServer] Tailwind config warning:", result.error);
13121
+ this.tailwindConfigScript = "";
13122
+ }
13123
+ } catch (error) {
13124
+ console.warn("[NextDevServer] Failed to load tailwind.config:", error);
13125
+ this.tailwindConfigScript = "";
13126
+ }
13127
+ this.tailwindConfigLoaded = true;
13128
+ return this.tailwindConfigScript;
13129
+ }
12534
13130
  hasAppRouter() {
12535
13131
  try {
12536
13132
  if (!this.exists(this.appDir)) return false;
@@ -12550,10 +13146,26 @@ export default function Head({ children }) {
12550
13146
  }
12551
13147
  async handleRequest(method, url2, headers, body) {
12552
13148
  const urlObj = new URL(url2, "http://localhost");
12553
- const pathname = urlObj.pathname;
13149
+ let pathname = urlObj.pathname;
13150
+ const virtualPrefixMatch = pathname.match(/^\/__virtual__\/\d+/);
13151
+ if (virtualPrefixMatch) {
13152
+ pathname = pathname.slice(virtualPrefixMatch[0].length) || "/";
13153
+ }
13154
+ if (this.assetPrefix && pathname.startsWith(this.assetPrefix)) {
13155
+ const rest = pathname.slice(this.assetPrefix.length);
13156
+ if (rest === "" || rest.startsWith("/")) {
13157
+ pathname = rest || "/";
13158
+ if (pathname.startsWith("//")) {
13159
+ pathname = pathname.slice(1);
13160
+ }
13161
+ }
13162
+ }
12554
13163
  if (pathname.startsWith("/_next/shims/")) {
12555
13164
  return this.serveNextShim(pathname);
12556
13165
  }
13166
+ if (pathname === "/_next/route-info") {
13167
+ return this.serveRouteInfo(urlObj.searchParams.get("pathname") || "/");
13168
+ }
12557
13169
  if (pathname.startsWith("/_next/pages/")) {
12558
13170
  return this.servePageComponent(pathname);
12559
13171
  }
@@ -12573,6 +13185,13 @@ export default function Head({ children }) {
12573
13185
  if (this.needsTransform(pathname) && this.exists(pathname)) {
12574
13186
  return this.transformAndServe(pathname, pathname);
12575
13187
  }
13188
+ const resolvedFile = this.resolveFileWithExtension(pathname);
13189
+ if (resolvedFile) {
13190
+ if (this.needsTransform(resolvedFile)) {
13191
+ return this.transformAndServe(resolvedFile, pathname);
13192
+ }
13193
+ return this.serveFile(resolvedFile);
13194
+ }
12576
13195
  if (this.exists(pathname) && !this.isDirectory(pathname)) {
12577
13196
  return this.serveFile(pathname);
12578
13197
  }
@@ -12594,6 +13213,18 @@ export default function Head({ children }) {
12594
13213
  case "navigation":
12595
13214
  code2 = NEXT_NAVIGATION_SHIM;
12596
13215
  break;
13216
+ case "image":
13217
+ code2 = NEXT_IMAGE_SHIM;
13218
+ break;
13219
+ case "dynamic":
13220
+ code2 = NEXT_DYNAMIC_SHIM;
13221
+ break;
13222
+ case "script":
13223
+ code2 = NEXT_SCRIPT_SHIM;
13224
+ break;
13225
+ case "font/google":
13226
+ code2 = NEXT_FONT_GOOGLE_SHIM;
13227
+ break;
12597
13228
  default:
12598
13229
  return this.notFound(pathname);
12599
13230
  }
@@ -12609,6 +13240,28 @@ export default function Head({ children }) {
12609
13240
  body: buffer2
12610
13241
  };
12611
13242
  }
13243
+ serveRouteInfo(pathname) {
13244
+ const route = this.resolveAppRoute(pathname);
13245
+ const info = route ? {
13246
+ params: route.params,
13247
+ found: true
13248
+ } : {
13249
+ params: {},
13250
+ found: false
13251
+ };
13252
+ const json = JSON.stringify(info);
13253
+ const buffer2 = BufferPolyfill.from(json);
13254
+ return {
13255
+ statusCode: 200,
13256
+ statusMessage: "OK",
13257
+ headers: {
13258
+ "Content-Type": "application/json; charset=utf-8",
13259
+ "Content-Length": String(buffer2.length),
13260
+ "Cache-Control": "no-cache"
13261
+ },
13262
+ body: buffer2
13263
+ };
13264
+ }
12612
13265
  serveStaticAsset(pathname) {
12613
13266
  const filePath = pathname.replace("/_next/static/", "/");
12614
13267
  if (this.exists(filePath)) {
@@ -13117,7 +13770,8 @@ export default function Head({ children }) {
13117
13770
  if (this.exists(pagePath)) {
13118
13771
  return {
13119
13772
  page: pagePath,
13120
- layouts
13773
+ layouts,
13774
+ params: {}
13121
13775
  };
13122
13776
  }
13123
13777
  }
@@ -13130,7 +13784,7 @@ export default function Head({ children }) {
13130
13784
  ".js",
13131
13785
  ".ts"
13132
13786
  ];
13133
- const tryPath = (dirPath, remainingSegments, layouts2) => {
13787
+ const tryPath = (dirPath, remainingSegments, layouts2, params) => {
13134
13788
  for (const ext of extensions) {
13135
13789
  const layoutPath = `${dirPath}/layout${ext}`;
13136
13790
  if (this.exists(layoutPath) && !layouts2.includes(layoutPath)) {
@@ -13146,7 +13800,8 @@ export default function Head({ children }) {
13146
13800
  if (this.exists(pagePath)) {
13147
13801
  return {
13148
13802
  page: pagePath,
13149
- layouts: layouts2
13803
+ layouts: layouts2,
13804
+ params
13150
13805
  };
13151
13806
  }
13152
13807
  }
@@ -13155,16 +13810,35 @@ export default function Head({ children }) {
13155
13810
  const [current, ...rest] = remainingSegments;
13156
13811
  const exactPath = `${dirPath}/${current}`;
13157
13812
  if (this.isDirectory(exactPath)) {
13158
- const result = tryPath(exactPath, rest, layouts2);
13813
+ const result = tryPath(exactPath, rest, layouts2, params);
13159
13814
  if (result) return result;
13160
13815
  }
13161
13816
  try {
13162
13817
  const entries = this.vfs.readdirSync(dirPath);
13163
13818
  for (const entry of entries) {
13164
- if (entry.startsWith("[") && entry.endsWith("]") && !entry.includes(".")) {
13819
+ if (entry.startsWith("[...") && entry.endsWith("]")) {
13820
+ const dynamicPath = `${dirPath}/${entry}`;
13821
+ if (this.isDirectory(dynamicPath)) {
13822
+ const paramName = entry.slice(4, -1);
13823
+ const newParams = {
13824
+ ...params,
13825
+ [paramName]: [
13826
+ current,
13827
+ ...rest
13828
+ ]
13829
+ };
13830
+ const result = tryPath(dynamicPath, [], layouts2, newParams);
13831
+ if (result) return result;
13832
+ }
13833
+ } else if (entry.startsWith("[") && entry.endsWith("]") && !entry.includes(".")) {
13165
13834
  const dynamicPath = `${dirPath}/${entry}`;
13166
13835
  if (this.isDirectory(dynamicPath)) {
13167
- const result = tryPath(dynamicPath, rest, layouts2);
13836
+ const paramName = entry.slice(1, -1);
13837
+ const newParams = {
13838
+ ...params,
13839
+ [paramName]: current
13840
+ };
13841
+ const result = tryPath(dynamicPath, rest, layouts2, newParams);
13168
13842
  if (result) return result;
13169
13843
  }
13170
13844
  }
@@ -13181,7 +13855,7 @@ export default function Head({ children }) {
13181
13855
  break;
13182
13856
  }
13183
13857
  }
13184
- return tryPath(this.appDir, segments, layouts);
13858
+ return tryPath(this.appDir, segments, layouts, {});
13185
13859
  }
13186
13860
  async generateAppRouterHtml(route, pathname) {
13187
13861
  const virtualPrefix = `/__virtual__/${this.port}`;
@@ -13201,6 +13875,7 @@ export default function Head({ children }) {
13201
13875
  for (let i = route.layouts.length - 1; i >= 0; i--) {
13202
13876
  }
13203
13877
  const envScript = this.generateEnvScript();
13878
+ const tailwindConfigScript = await this.loadTailwindConfigIfNeeded();
13204
13879
  return `<!DOCTYPE html>
13205
13880
  <html lang="en">
13206
13881
  <head>
@@ -13210,6 +13885,7 @@ export default function Head({ children }) {
13210
13885
  <title>Next.js App</title>
13211
13886
  ${envScript}
13212
13887
  ${TAILWIND_CDN_SCRIPT}
13888
+ ${tailwindConfigScript}
13213
13889
  ${CORS_PROXY_SCRIPT}
13214
13890
  ${globalCssLinks.join("\n ")}
13215
13891
  ${REACT_REFRESH_PREAMBLE}
@@ -13231,7 +13907,11 @@ export default function Head({ children }) {
13231
13907
  "next/link": "${virtualPrefix}/_next/shims/link.js",
13232
13908
  "next/router": "${virtualPrefix}/_next/shims/router.js",
13233
13909
  "next/head": "${virtualPrefix}/_next/shims/head.js",
13234
- "next/navigation": "${virtualPrefix}/_next/shims/navigation.js"
13910
+ "next/navigation": "${virtualPrefix}/_next/shims/navigation.js",
13911
+ "next/image": "${virtualPrefix}/_next/shims/image.js",
13912
+ "next/dynamic": "${virtualPrefix}/_next/shims/dynamic.js",
13913
+ "next/script": "${virtualPrefix}/_next/shims/script.js",
13914
+ "next/font/google": "${virtualPrefix}/_next/shims/font/google.js"
13235
13915
  }
13236
13916
  }
13237
13917
  <\/script>
@@ -13245,6 +13925,39 @@ export default function Head({ children }) {
13245
13925
 
13246
13926
  const virtualBase = '${virtualPrefix}';
13247
13927
 
13928
+ // Initial route params (embedded by server for initial page load)
13929
+ const initialRouteParams = ${JSON.stringify(route.params)};
13930
+ const initialPathname = '${pathname}';
13931
+
13932
+ // Route params cache for client-side navigation
13933
+ const routeParamsCache = new Map();
13934
+ routeParamsCache.set(initialPathname, initialRouteParams);
13935
+
13936
+ // Extract route params from server for client-side navigation
13937
+ async function extractRouteParams(pathname) {
13938
+ // Strip virtual base if present
13939
+ let route = pathname;
13940
+ if (route.startsWith(virtualBase)) {
13941
+ route = route.slice(virtualBase.length);
13942
+ }
13943
+ route = route.replace(/^\\/+/, '/') || '/';
13944
+
13945
+ // Check cache first
13946
+ if (routeParamsCache.has(route)) {
13947
+ return routeParamsCache.get(route);
13948
+ }
13949
+
13950
+ try {
13951
+ const response = await fetch(virtualBase + '/_next/route-info?pathname=' + encodeURIComponent(route));
13952
+ const info = await response.json();
13953
+ routeParamsCache.set(route, info.params || {});
13954
+ return info.params || {};
13955
+ } catch (e) {
13956
+ console.error('[Router] Failed to extract route params:', e);
13957
+ return {};
13958
+ }
13959
+ }
13960
+
13248
13961
  // Convert URL path to app router page module path
13249
13962
  function getAppPageModulePath(pathname) {
13250
13963
  let route = pathname;
@@ -13311,11 +14024,60 @@ export default function Head({ children }) {
13311
14024
  return layouts;
13312
14025
  }
13313
14026
 
14027
+ // Wrapper for async Server Components
14028
+ function AsyncComponent({ component: Component, pathname, search }) {
14029
+ const [content, setContent] = React.useState(null);
14030
+ const [error, setError] = React.useState(null);
14031
+
14032
+ React.useEffect(() => {
14033
+ let cancelled = false;
14034
+ async function render() {
14035
+ try {
14036
+ // Create searchParams as a Promise (Next.js 15 pattern)
14037
+ const url = new URL(window.location.href);
14038
+ const searchParamsObj = Object.fromEntries(url.searchParams);
14039
+ const searchParams = Promise.resolve(searchParamsObj);
14040
+
14041
+ // Extract route params from pathname (fetches from server for dynamic routes)
14042
+ const routeParams = await extractRouteParams(pathname);
14043
+ const params = Promise.resolve(routeParams);
14044
+
14045
+ // Call component with props like Next.js does for page components
14046
+ const result = Component({ searchParams, params });
14047
+ if (result && typeof result.then === 'function') {
14048
+ // It's a Promise (async component)
14049
+ const resolved = await result;
14050
+ if (!cancelled) setContent(resolved);
14051
+ } else {
14052
+ // Synchronous component - result is already JSX
14053
+ if (!cancelled) setContent(result);
14054
+ }
14055
+ } catch (e) {
14056
+ console.error('[AsyncComponent] Error rendering:', e);
14057
+ if (!cancelled) setError(e);
14058
+ }
14059
+ }
14060
+ render();
14061
+ return () => { cancelled = true; };
14062
+ }, [Component, pathname, search]);
14063
+
14064
+ if (error) {
14065
+ return React.createElement('div', { style: { color: 'red', padding: '20px' } },
14066
+ 'Error: ' + error.message
14067
+ );
14068
+ }
14069
+ if (!content) {
14070
+ return React.createElement('div', { style: { padding: '20px' } }, 'Loading...');
14071
+ }
14072
+ return content;
14073
+ }
14074
+
13314
14075
  // Router component
13315
14076
  function Router() {
13316
14077
  const [Page, setPage] = React.useState(null);
13317
14078
  const [layouts, setLayouts] = React.useState([]);
13318
14079
  const [path, setPath] = React.useState(window.location.pathname);
14080
+ const [search, setSearch] = React.useState(window.location.search);
13319
14081
 
13320
14082
  React.useEffect(() => {
13321
14083
  Promise.all([loadPage(path), loadLayouts(path)]).then(([P, L]) => {
@@ -13327,21 +14089,35 @@ export default function Head({ children }) {
13327
14089
  React.useEffect(() => {
13328
14090
  const handleNavigation = async () => {
13329
14091
  const newPath = window.location.pathname;
14092
+ const newSearch = window.location.search;
14093
+ console.log('[Router] handleNavigation called, newPath:', newPath, 'current path:', path);
14094
+
14095
+ // Always update search params
14096
+ if (newSearch !== search) {
14097
+ setSearch(newSearch);
14098
+ }
14099
+
13330
14100
  if (newPath !== path) {
14101
+ console.log('[Router] Path changed, loading new page...');
13331
14102
  setPath(newPath);
13332
14103
  const [P, L] = await Promise.all([loadPage(newPath), loadLayouts(newPath)]);
14104
+ console.log('[Router] Page loaded:', !!P, 'Layouts:', L.length);
13333
14105
  if (P) setPage(() => P);
13334
14106
  setLayouts(L);
14107
+ } else {
14108
+ console.log('[Router] Path unchanged, skipping navigation');
13335
14109
  }
13336
14110
  };
13337
14111
  window.addEventListener('popstate', handleNavigation);
14112
+ console.log('[Router] Added popstate listener for path:', path);
13338
14113
  return () => window.removeEventListener('popstate', handleNavigation);
13339
- }, [path]);
14114
+ }, [path, search]);
13340
14115
 
13341
14116
  if (!Page) return null;
13342
14117
 
13343
- // Build nested layout structure
13344
- let content = React.createElement(Page);
14118
+ // Use AsyncComponent wrapper to handle async Server Components
14119
+ // Pass search to force re-render when query params change
14120
+ let content = React.createElement(AsyncComponent, { component: Page, pathname: path, search: search });
13345
14121
  for (let i = layouts.length - 1; i >= 0; i--) {
13346
14122
  content = React.createElement(layouts[i], null, content);
13347
14123
  }
@@ -13463,6 +14239,7 @@ export default function Head({ children }) {
13463
14239
  }
13464
14240
  }
13465
14241
  const envScript = this.generateEnvScript();
14242
+ const tailwindConfigScript = await this.loadTailwindConfigIfNeeded();
13466
14243
  return `<!DOCTYPE html>
13467
14244
  <html lang="en">
13468
14245
  <head>
@@ -13472,6 +14249,7 @@ export default function Head({ children }) {
13472
14249
  <title>Next.js App</title>
13473
14250
  ${envScript}
13474
14251
  ${TAILWIND_CDN_SCRIPT}
14252
+ ${tailwindConfigScript}
13475
14253
  ${CORS_PROXY_SCRIPT}
13476
14254
  ${globalCssLinks.join("\n ")}
13477
14255
  ${REACT_REFRESH_PREAMBLE}
@@ -13485,7 +14263,12 @@ export default function Head({ children }) {
13485
14263
  "react-dom/client": "https://esm.sh/react-dom@18.2.0/client?dev",
13486
14264
  "next/link": "${virtualPrefix}/_next/shims/link.js",
13487
14265
  "next/router": "${virtualPrefix}/_next/shims/router.js",
13488
- "next/head": "${virtualPrefix}/_next/shims/head.js"
14266
+ "next/head": "${virtualPrefix}/_next/shims/head.js",
14267
+ "next/navigation": "${virtualPrefix}/_next/shims/navigation.js",
14268
+ "next/image": "${virtualPrefix}/_next/shims/image.js",
14269
+ "next/dynamic": "${virtualPrefix}/_next/shims/dynamic.js",
14270
+ "next/script": "${virtualPrefix}/_next/shims/script.js",
14271
+ "next/font/google": "${virtualPrefix}/_next/shims/font/google.js"
13489
14272
  }
13490
14273
  }
13491
14274
  <\/script>
@@ -13601,13 +14384,58 @@ export default function Head({ children }) {
13601
14384
  body: buffer2
13602
14385
  };
13603
14386
  }
14387
+ resolveFileWithExtension(pathname) {
14388
+ if (/\.\w+$/.test(pathname) && this.exists(pathname)) {
14389
+ return pathname;
14390
+ }
14391
+ const extensions = [
14392
+ ".tsx",
14393
+ ".ts",
14394
+ ".jsx",
14395
+ ".js"
14396
+ ];
14397
+ for (const ext of extensions) {
14398
+ const withExt = pathname + ext;
14399
+ if (this.exists(withExt)) {
14400
+ return withExt;
14401
+ }
14402
+ }
14403
+ for (const ext of extensions) {
14404
+ const indexPath = pathname + "/index" + ext;
14405
+ if (this.exists(indexPath)) {
14406
+ return indexPath;
14407
+ }
14408
+ }
14409
+ return null;
14410
+ }
13604
14411
  needsTransform(path2) {
13605
14412
  return /\.(jsx|tsx|ts)$/.test(path2);
13606
14413
  }
13607
14414
  async transformAndServe(filePath, urlPath) {
13608
14415
  try {
13609
14416
  const content = this.vfs.readFileSync(filePath, "utf8");
13610
- const transformed = await this.transformCode(content, urlPath);
14417
+ const hash = simpleHash(content);
14418
+ const cached = this.transformCache.get(filePath);
14419
+ if (cached && cached.hash === hash) {
14420
+ const buffer22 = BufferPolyfill.from(cached.code);
14421
+ return {
14422
+ statusCode: 200,
14423
+ statusMessage: "OK",
14424
+ headers: {
14425
+ "Content-Type": "application/javascript; charset=utf-8",
14426
+ "Content-Length": String(buffer22.length),
14427
+ "Cache-Control": "no-cache",
14428
+ "X-Transformed": "true",
14429
+ "X-Cache": "hit"
14430
+ },
14431
+ body: buffer22
14432
+ };
14433
+ }
14434
+ const transformed = await this.transformCode(content, filePath);
14435
+ this.transformCache.set(filePath, {
14436
+ code: transformed,
14437
+ hash
14438
+ });
13611
14439
  const buffer2 = BufferPolyfill.from(transformed);
13612
14440
  return {
13613
14441
  statusCode: 200,
@@ -13646,11 +14474,12 @@ console.error(${JSON.stringify(message)});`;
13646
14474
  throw new Error("esbuild not available");
13647
14475
  }
13648
14476
  const codeWithoutCssImports = this.stripCssImports(code2);
14477
+ const codeWithResolvedAliases = this.resolvePathAliases(codeWithoutCssImports, filename2);
13649
14478
  let loader = "js";
13650
14479
  if (filename2.endsWith(".jsx")) loader = "jsx";
13651
14480
  else if (filename2.endsWith(".tsx")) loader = "tsx";
13652
14481
  else if (filename2.endsWith(".ts")) loader = "ts";
13653
- const result = await esbuild2.transform(codeWithoutCssImports, {
14482
+ const result = await esbuild2.transform(codeWithResolvedAliases, {
13654
14483
  loader,
13655
14484
  format: "esm",
13656
14485
  target: "esnext",
@@ -13659,15 +14488,57 @@ console.error(${JSON.stringify(message)});`;
13659
14488
  sourcemap: "inline",
13660
14489
  sourcefile: filename2
13661
14490
  });
14491
+ const codeWithCdnImports = this.redirectNpmImports(result.code);
13662
14492
  if (/\.(jsx|tsx)$/.test(filename2)) {
13663
- return this.addReactRefresh(result.code, filename2);
14493
+ return this.addReactRefresh(codeWithCdnImports, filename2);
13664
14494
  }
13665
- return result.code;
14495
+ return codeWithCdnImports;
14496
+ }
14497
+ redirectNpmImports(code2) {
14498
+ const explicitMappings = {
14499
+ "react": "https://esm.sh/react@18.2.0?dev",
14500
+ "react/jsx-runtime": "https://esm.sh/react@18.2.0&dev/jsx-runtime",
14501
+ "react/jsx-dev-runtime": "https://esm.sh/react@18.2.0&dev/jsx-dev-runtime",
14502
+ "react-dom": "https://esm.sh/react-dom@18.2.0?dev",
14503
+ "react-dom/client": "https://esm.sh/react-dom@18.2.0/client?dev"
14504
+ };
14505
+ const localPackages = /* @__PURE__ */ new Set([
14506
+ "next/link",
14507
+ "next/router",
14508
+ "next/head",
14509
+ "next/navigation",
14510
+ "next/dynamic",
14511
+ "next/image",
14512
+ "next/script",
14513
+ "next/font/google",
14514
+ "convex/_generated/api"
14515
+ ]);
14516
+ const importPattern = /(from\s*['"])([^'"./][^'"]*?)(['"])/g;
14517
+ return code2.replace(importPattern, (match, prefix, packageName, suffix) => {
14518
+ if (packageName.startsWith("http://") || packageName.startsWith("https://") || packageName.startsWith("/__virtual__")) {
14519
+ return match;
14520
+ }
14521
+ if (explicitMappings[packageName]) {
14522
+ return `${prefix}${explicitMappings[packageName]}${suffix}`;
14523
+ }
14524
+ if (localPackages.has(packageName)) {
14525
+ return match;
14526
+ }
14527
+ const basePkg = packageName.includes("/") ? packageName.split("/")[0] : packageName;
14528
+ const isScoped = basePkg.startsWith("@");
14529
+ const scopedBasePkg = isScoped && packageName.includes("/") ? packageName.split("/").slice(0, 2).join("/") : basePkg;
14530
+ if (localPackages.has(scopedBasePkg)) {
14531
+ return match;
14532
+ }
14533
+ const esmUrl = `https://esm.sh/${packageName}?external=react`;
14534
+ return `${prefix}${esmUrl}${suffix}`;
14535
+ });
13666
14536
  }
13667
14537
  stripCssImports(code2) {
13668
- return code2.replace(/import\s+['"][^'"]+\.css['"]\s*;?/g, "// CSS import removed (loaded via <link>)");
14538
+ return code2.replace(/import\s+['"][^'"]+\.css['"]\s*;?/g, "");
13669
14539
  }
13670
14540
  async transformApiHandler(code2, filename2) {
14541
+ const codeWithResolvedAliases = this.resolvePathAliases(code2, filename2);
13671
14542
  if (isBrowser) {
13672
14543
  await initEsbuild();
13673
14544
  const esbuild2 = getEsbuild();
@@ -13678,7 +14549,7 @@ console.error(${JSON.stringify(message)});`;
13678
14549
  if (filename2.endsWith(".jsx")) loader = "jsx";
13679
14550
  else if (filename2.endsWith(".tsx")) loader = "tsx";
13680
14551
  else if (filename2.endsWith(".ts")) loader = "ts";
13681
- const result = await esbuild2.transform(code2, {
14552
+ const result = await esbuild2.transform(codeWithResolvedAliases, {
13682
14553
  loader,
13683
14554
  format: "cjs",
13684
14555
  target: "esnext",
@@ -13687,7 +14558,7 @@ console.error(${JSON.stringify(message)});`;
13687
14558
  });
13688
14559
  return result.code;
13689
14560
  }
13690
- let transformed = code2;
14561
+ let transformed = codeWithResolvedAliases;
13691
14562
  transformed = transformed.replace(/import\s+(\w+)\s+from\s+['"]([^'"]+)['"]/g, 'const $1 = require("$2")');
13692
14563
  transformed = transformed.replace(/import\s+\{([^}]+)\}\s+from\s+['"]([^'"]+)['"]/g, 'const {$1} = require("$2")');
13693
14564
  transformed = transformed.replace(/export\s+default\s+function\s+(\w+)/g, "module.exports = function $1");
@@ -13803,6 +14674,50 @@ ${registrations}
13803
14674
  }
13804
14675
  }
13805
14676
  }
14677
+ serveFile(filePath) {
14678
+ if (filePath.endsWith(".json")) {
14679
+ try {
14680
+ const normalizedPath = this.resolvePath(filePath);
14681
+ const content = this.vfs.readFileSync(normalizedPath);
14682
+ let jsonContent;
14683
+ if (typeof content === "string") {
14684
+ jsonContent = content;
14685
+ } else if (content instanceof Uint8Array) {
14686
+ jsonContent = new TextDecoder("utf-8").decode(content);
14687
+ } else {
14688
+ jsonContent = BufferPolyfill.from(content).toString("utf-8");
14689
+ }
14690
+ const esModuleContent = `export default ${jsonContent};`;
14691
+ const buffer2 = BufferPolyfill.from(esModuleContent);
14692
+ return {
14693
+ statusCode: 200,
14694
+ statusMessage: "OK",
14695
+ headers: {
14696
+ "Content-Type": "application/javascript; charset=utf-8",
14697
+ "Content-Length": String(buffer2.length),
14698
+ "Cache-Control": "no-cache"
14699
+ },
14700
+ body: buffer2
14701
+ };
14702
+ } catch (error) {
14703
+ if (error.code === "ENOENT") {
14704
+ return this.notFound(filePath);
14705
+ }
14706
+ return this.serverError(error);
14707
+ }
14708
+ }
14709
+ return super.serveFile(filePath);
14710
+ }
14711
+ resolvePath(urlPath) {
14712
+ let path2 = urlPath.split("?")[0].split("#")[0];
14713
+ if (!path2.startsWith("/")) {
14714
+ path2 = "/" + path2;
14715
+ }
14716
+ if (this.root !== "/") {
14717
+ path2 = this.root + path2;
14718
+ }
14719
+ return path2;
14720
+ }
13806
14721
  stop() {
13807
14722
  if (this.watcherCleanup) {
13808
14723
  this.watcherCleanup();