@timber-js/app 0.2.0-alpha.112 → 0.2.0-alpha.113

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.js CHANGED
@@ -1979,6 +1979,52 @@ function timberMdx(ctx) {
1979
1979
  };
1980
1980
  }
1981
1981
  //#endregion
1982
+ //#region src/routing/segment-codegen.ts
1983
+ /**
1984
+ * _segment type generation and directory-to-route mapping.
1985
+ *
1986
+ * Generates per-route _segment.d.ts files in .timber/types/ that mirror
1987
+ * the app/ directory structure. TypeScript's rootDirs feature makes these
1988
+ * importable as './_segment' from route files.
1989
+ *
1990
+ * Design doc: design/41-global-params.md §The _segment Module
1991
+ */
1992
+ /**
1993
+ * Build a map of absolute filesystem directory → route pattern.
1994
+ * Used by resolveId to map a _segment import's importer to the correct route.
1995
+ *
1996
+ * Walks every segment (not just leaves) so layouts and middleware
1997
+ * at any depth can import _segment.
1998
+ */
1999
+ function buildDirToRouteMap(tree, appDir) {
2000
+ const map = /* @__PURE__ */ new Map();
2001
+ function walk(node, fsPath) {
2002
+ const routePattern = node.urlPath || "/";
2003
+ map.set(fsPath, routePattern);
2004
+ for (const child of node.children) walk(child, join(fsPath, child.segmentName));
2005
+ for (const [slotName, slotNode] of Object.entries(node.slots)) walk(slotNode, join(fsPath, slotName));
2006
+ }
2007
+ walk(tree.root, appDir);
2008
+ return map;
2009
+ }
2010
+ /**
2011
+ * Generate per-route _segment.d.ts files in .timber/types/ mirroring
2012
+ * the app/ directory structure. These files are importable via TypeScript's
2013
+ * rootDirs feature.
2014
+ */
2015
+ async function writeSegmentTypes(routeTree, appDir, root) {
2016
+ const typesBase = join(root, ".timber", "types", appDir.slice(root.length + 1));
2017
+ const map = buildDirToRouteMap(routeTree, appDir);
2018
+ const writes = [];
2019
+ for (const [fsDir, routePattern] of map) {
2020
+ const segmentDir = join(typesBase, fsDir.slice(appDir.length));
2021
+ const segmentPath = join(segmentDir, "_segment.d.ts");
2022
+ const content = `export const route: '${routePattern}';\n`;
2023
+ writes.push(mkdir(segmentDir, { recursive: true }).then(() => writeFile(segmentPath, content, "utf-8")));
2024
+ }
2025
+ await Promise.all(writes);
2026
+ }
2027
+ //#endregion
1982
2028
  //#region src/routing/convention-lint.ts
1983
2029
  /**
1984
2030
  * Convention linter — validates common misconfigurations in the route tree.
@@ -2217,6 +2263,7 @@ var SEARCH_PARAMS_REGISTRY_ID = "virtual:timber-search-params-registry";
2217
2263
  var RESOLVED_SEARCH_PARAMS_REGISTRY_ID = `\0${SEARCH_PARAMS_REGISTRY_ID}`;
2218
2264
  var SCHEMA_ID = "virtual:timber-schema";
2219
2265
  var RESOLVED_SCHEMA_ID = `\0${SCHEMA_ID}`;
2266
+ var SEGMENT_PREFIX = "\0_segment:";
2220
2267
  /**
2221
2268
  * File convention names we track for changes that require manifest regeneration.
2222
2269
  */
@@ -2257,13 +2304,19 @@ function writeCodegen(ctx) {
2257
2304
  });
2258
2305
  const routesPath = join(ctx.root, CODEGEN_OUTPUT);
2259
2306
  const envPath = join(ctx.root, "timber-env.d.ts");
2260
- mkdir(timberDir, { recursive: true }).then(() => Promise.all([writeFile(routesPath, content, "utf-8"), writeFile(envPath, TIMBER_ENV_DTS, "utf-8")])).catch((err) => {
2307
+ mkdir(timberDir, { recursive: true }).then(() => Promise.all([
2308
+ writeFile(routesPath, content, "utf-8"),
2309
+ writeFile(envPath, TIMBER_ENV_DTS, "utf-8"),
2310
+ writeSegmentTypes(ctx.routeTree, ctx.appDir, ctx.root)
2311
+ ])).catch((err) => {
2261
2312
  console.warn("[timber] Failed to write codegen output:", err);
2262
2313
  });
2263
2314
  }
2264
2315
  function timberRouting(ctx) {
2265
2316
  /** Track warned files to avoid repeating warnings on rescan. */
2266
2317
  const warnedFiles = /* @__PURE__ */ new Set();
2318
+ /** Maps absolute filesystem directory → route pattern for _segment resolution. */
2319
+ let dirToRoute = /* @__PURE__ */ new Map();
2267
2320
  function rescan() {
2268
2321
  const appDirWarning = checkAppDirExists(ctx.appDir);
2269
2322
  if (appDirWarning) {
@@ -2281,6 +2334,7 @@ function timberRouting(ctx) {
2281
2334
  ctx.timer.start("route-scan");
2282
2335
  ctx.routeTree = scanRoutes(ctx.appDir, { pageExtensions: ctx.config.pageExtensions });
2283
2336
  ctx.timer.end("route-scan");
2337
+ dirToRoute = buildDirToRouteMap(ctx.routeTree, ctx.appDir);
2284
2338
  writeCodegen(ctx);
2285
2339
  warnUnregisteredSegments(ctx);
2286
2340
  const newConventionWarnings = lintConventions(ctx.routeTree, ctx.appDir).filter((w) => !warnedFiles.has(w.id));
@@ -2292,12 +2346,18 @@ function timberRouting(ctx) {
2292
2346
  }
2293
2347
  return {
2294
2348
  name: "timber-routing",
2295
- resolveId(id) {
2349
+ resolveId(id, importer) {
2296
2350
  const cleanId = id.startsWith("\0") ? id.slice(1) : id;
2297
2351
  if (cleanId === VIRTUAL_MODULE_ID$1) return RESOLVED_VIRTUAL_ID$1;
2298
2352
  if (cleanId.endsWith(`/${VIRTUAL_MODULE_ID$1}`)) return RESOLVED_VIRTUAL_ID$1;
2299
2353
  if (cleanId === SEARCH_PARAMS_REGISTRY_ID || cleanId.endsWith(`/${SEARCH_PARAMS_REGISTRY_ID}`)) return RESOLVED_SEARCH_PARAMS_REGISTRY_ID;
2300
2354
  if (cleanId === SCHEMA_ID || cleanId.endsWith(`/${SCHEMA_ID}`)) return RESOLVED_SCHEMA_ID;
2355
+ if (importer && (cleanId === "./_segment" || cleanId === "./_segment.js" || cleanId === "./_segment.ts")) {
2356
+ const cleanImporter = importer.startsWith("\0") ? importer.slice(1) : importer;
2357
+ const importerDir = cleanImporter.substring(0, cleanImporter.lastIndexOf("/"));
2358
+ const routePattern = dirToRoute.get(importerDir);
2359
+ if (routePattern !== void 0) return `${SEGMENT_PREFIX}${routePattern}`;
2360
+ }
2301
2361
  return null;
2302
2362
  },
2303
2363
  load(id) {
@@ -2310,6 +2370,10 @@ function timberRouting(ctx) {
2310
2370
  return generateSearchParamsRegistryModule(ctx.routeTree);
2311
2371
  }
2312
2372
  if (id === RESOLVED_SCHEMA_ID) return generateSchemaModule(ctx.appDir);
2373
+ if (id.startsWith(SEGMENT_PREFIX)) {
2374
+ const routePattern = id.slice(10);
2375
+ return `export const route = ${JSON.stringify(routePattern)};\n`;
2376
+ }
2313
2377
  return null;
2314
2378
  },
2315
2379
  buildStart() {
@@ -2391,6 +2455,7 @@ function invalidateManifest(server) {
2391
2455
  const mod = env.moduleGraph.getModuleById(virtualId);
2392
2456
  if (mod) env.moduleGraph.invalidateModule(mod);
2393
2457
  }
2458
+ for (const mod of env.moduleGraph.idToModuleMap.values()) if (mod.id?.startsWith(SEGMENT_PREFIX)) env.moduleGraph.invalidateModule(mod);
2394
2459
  }
2395
2460
  server.hot.send({ type: "full-reload" });
2396
2461
  }