bun-plugin-stx 0.2.413 → 0.2.415

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.
@@ -33,7 +33,7 @@ function shouldWatch(filePath, options) {
33
33
  }
34
34
  return true;
35
35
  }
36
- function createWatcher(options = {}) {
36
+ function createWatcher2(options = {}) {
37
37
  const watchedPaths = new Set;
38
38
  const watchers = new Map;
39
39
  let isActive = true;
@@ -127,7 +127,7 @@ function createWatcher(options = {}) {
127
127
  isWatching: () => isActive && watchers.size > 0
128
128
  };
129
129
  }
130
- async function watchAndBuild(options) {
130
+ async function watchAndBuild2(options) {
131
131
  const {
132
132
  entrypoints,
133
133
  outdir = "./dist",
@@ -145,7 +145,7 @@ async function watchAndBuild(options) {
145
145
  const result = await Bun.build({
146
146
  entrypoints,
147
147
  outdir,
148
- plugins: [stxPlugin(stxOptions)],
148
+ plugins: [stxPlugin2(stxOptions)],
149
149
  ...buildConfig
150
150
  });
151
151
  const duration = Date.now() - startTime;
@@ -193,7 +193,7 @@ async function watchAndBuild(options) {
193
193
  dirsToWatch.add(fullPath);
194
194
  }
195
195
  }
196
- const watcher = createWatcher({
196
+ const watcher = createWatcher2({
197
197
  ...watchOptions,
198
198
  paths: watchOptions.paths || Array.from(dirsToWatch),
199
199
  onChange: async (event) => {
@@ -215,9 +215,9 @@ async function watchAndBuild(options) {
215
215
  }
216
216
  return watcher;
217
217
  }
218
- async function startWatchMode(args) {
218
+ async function startWatchMode2(args) {
219
219
  console.log("\x1B[36m[stx]\x1B[0m Starting watch mode...");
220
- const watcher = await watchAndBuild({
220
+ const watcher = await watchAndBuild2({
221
221
  entrypoints: args.entrypoints,
222
222
  outdir: args.outdir,
223
223
  verbose: args.verbose,
@@ -248,7 +248,7 @@ async function startWatchMode(args) {
248
248
  }
249
249
 
250
250
  // src/index.ts
251
- import { createMiddleware, createRoute, readMarkdownFile as readMarkdownFile2 } from "@stacksjs/stx";
251
+ import { createMiddleware as createMiddleware2, createRoute as createRoute2, readMarkdownFile as readMarkdownFile2 } from "@stacksjs/stx";
252
252
  function escapeHtmlForError(str) {
253
253
  return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
254
254
  }
@@ -273,7 +273,7 @@ function generateErrorPage(title, message, filePath) {
273
273
  </body>
274
274
  </html>`;
275
275
  }
276
- function stxPlugin(userOptions) {
276
+ function stxPlugin2(userOptions) {
277
277
  return {
278
278
  name: "bun-plugin-stx",
279
279
  async setup(build) {
@@ -462,6 +462,6 @@ ${scriptsHtml}`;
462
462
  }
463
463
  };
464
464
  }
465
- var src_default = stxPlugin;
465
+ var src_default = stxPlugin2;
466
466
 
467
- export { createWatcher, watchAndBuild, startWatchMode, stxPlugin, createMiddleware, createRoute, readMarkdownFile2 as readMarkdownFile, src_default };
467
+ export { createWatcher2, watchAndBuild2, startWatchMode2, stxPlugin2, createMiddleware2, createRoute2, readMarkdownFile2, src_default };
@@ -0,0 +1,286 @@
1
+ // @bun
2
+ // ../router/src/file-router.ts
3
+ import fs3 from "fs";
4
+ import path4 from "path";
5
+
6
+ // ../router/src/codegen.ts
7
+ import fs from "fs";
8
+ import path from "path";
9
+
10
+ // ../router/src/page-routes.ts
11
+ var NON_PAGE_DIRS = ["components", "layouts", "partials", "emails"];
12
+ function isNonPageRoutePath(relativePath) {
13
+ const segments = relativePath.split(/[\\/]/).filter(Boolean);
14
+ return segments.slice(0, -1).some((segment) => NON_PAGE_DIRS.includes(segment));
15
+ }
16
+ function isNonPageRoutePattern(pattern) {
17
+ return isNonPageRoutePath(pattern.replace(/^\//, ""));
18
+ }
19
+
20
+ // ../router/src/codegen.ts
21
+ function generateRouteTypes(routes, outputDir) {
22
+ const pageRoutes = routes.filter((r) => !isNonPageRoutePattern(r.pattern));
23
+ const lines = ["// Auto-generated by STX - do not edit"];
24
+ if (pageRoutes.length === 0) {
25
+ lines.push("// No page routes found \u2014 route paths stay typed as string.", "export {}", "");
26
+ } else {
27
+ lines.push(`declare module '@stacksjs/stx' {`, " interface KnownRoutes {");
28
+ for (const route of pageRoutes) {
29
+ const paramEntries = [];
30
+ for (const p of route.params) {
31
+ const optional = route.pattern.includes(`:${p}?`) ? "?" : "";
32
+ paramEntries.push(`${p}${optional}: string`);
33
+ }
34
+ const paramsStr = paramEntries.join("; ");
35
+ lines.push(` '${route.pattern}': { ${paramsStr} }`);
36
+ }
37
+ lines.push(" }", "}", "", "export {}", "");
38
+ }
39
+ const contents = lines.join(`
40
+ `);
41
+ const target = path.join(outputDir, "route-types.d.ts");
42
+ try {
43
+ if (fs.existsSync(target) && fs.readFileSync(target, "utf8") === contents)
44
+ return;
45
+ fs.mkdirSync(outputDir, { recursive: true });
46
+ fs.writeFileSync(target, contents);
47
+ } catch {}
48
+ }
49
+ function generateRouteManifest(routes, outputDir, pagesDir) {
50
+ const cwd = process.cwd();
51
+ const relativePages = path.relative(cwd, pagesDir);
52
+ const pageRoutes = routes.filter((r) => !isNonPageRoutePattern(r.pattern));
53
+ const lines = [
54
+ "// Auto-generated by stx \u2014 do not edit",
55
+ `// Source: ${relativePages || "pages"}/`,
56
+ "",
57
+ "export const routes = ["
58
+ ];
59
+ for (const route of pageRoutes) {
60
+ const relFile = path.relative(cwd, route.filePath).replace(/\\/g, "/");
61
+ const params = JSON.stringify(route.params);
62
+ const layoutStr = route.layout ? `, layout: '${path.relative(cwd, route.layout).replace(/\\/g, "/")}'` : "";
63
+ lines.push(` { pattern: '${route.pattern}', filePath: '${relFile}', isDynamic: ${route.isDynamic}, params: ${params}${layoutStr} },`);
64
+ }
65
+ lines.push("] as const", "");
66
+ lines.push("export type RouteName = typeof routes[number]['pattern']", "");
67
+ fs.mkdirSync(outputDir, { recursive: true });
68
+ fs.writeFileSync(path.join(outputDir, "routes.ts"), lines.join(`
69
+ `));
70
+ }
71
+
72
+ // ../router/src/matcher.ts
73
+ import path2 from "path";
74
+ function filePathToPattern(filePath, pagesDir) {
75
+ const relativePath = path2.relative(pagesDir, filePath);
76
+ let route = relativePath.replace(/\.(stx|md)$/, "");
77
+ route = route.split(path2.sep).join("/");
78
+ if (route === "index") {
79
+ return "/";
80
+ }
81
+ if (route.endsWith("/index")) {
82
+ route = route.slice(0, -6);
83
+ }
84
+ route = route.replace(/\[\[([^\]]+)\]\]/g, ":$1?");
85
+ route = route.replace(/\[([^\]]+)\]/g, ":$1");
86
+ route = route.replace(/:\.\.\.([^/]+)/g, ":$1*");
87
+ return `/${route}`;
88
+ }
89
+ function patternToRegex(pattern) {
90
+ const params = [];
91
+ let regexStr = "";
92
+ let i = 0;
93
+ while (i < pattern.length) {
94
+ const rest = pattern.slice(i);
95
+ const optional = /^\/:([^/?*]+)\?/.exec(rest);
96
+ if (optional) {
97
+ params.push(optional[1]);
98
+ regexStr += "(?:/([^/]+))?";
99
+ i += optional[0].length;
100
+ continue;
101
+ }
102
+ const catchAll = /^:([^/?*]+)\*/.exec(rest);
103
+ if (catchAll) {
104
+ params.push(catchAll[1]);
105
+ regexStr += "(.+)";
106
+ i += catchAll[0].length;
107
+ continue;
108
+ }
109
+ const required = /^:([^/?*]+)/.exec(rest);
110
+ if (required) {
111
+ params.push(required[1]);
112
+ regexStr += "([^/]+)";
113
+ i += required[0].length;
114
+ continue;
115
+ }
116
+ regexStr += pattern[i].replace(/[.+^${}()|[\]\\*?]/g, "\\$&");
117
+ i += 1;
118
+ }
119
+ return { regex: new RegExp(`^${regexStr}$`), params };
120
+ }
121
+
122
+ // ../router/src/nested-layouts.ts
123
+ import fs2 from "fs";
124
+ import path3 from "path";
125
+ function resolveLayoutChain(routeFilePath, pagesDir) {
126
+ const layouts = [];
127
+ let dir = path3.dirname(routeFilePath);
128
+ while (dir.startsWith(pagesDir) || dir === pagesDir) {
129
+ const layoutFile = path3.join(dir, "_layout.stx");
130
+ if (fs2.existsSync(layoutFile)) {
131
+ layouts.unshift(layoutFile);
132
+ }
133
+ if (dir === pagesDir)
134
+ break;
135
+ dir = path3.dirname(dir);
136
+ }
137
+ return layouts;
138
+ }
139
+
140
+ // ../router/src/file-router.ts
141
+ function scanDirectory(dir, extensions) {
142
+ const files = [];
143
+ if (!fs3.existsSync(dir)) {
144
+ return files;
145
+ }
146
+ const entries = fs3.readdirSync(dir, { withFileTypes: true });
147
+ for (const entry of entries) {
148
+ const fullPath = path4.join(dir, entry.name);
149
+ if (entry.isDirectory()) {
150
+ if (entry.name.startsWith(".")) {
151
+ continue;
152
+ }
153
+ if (NON_PAGE_DIRS.includes(entry.name)) {
154
+ continue;
155
+ }
156
+ files.push(...scanDirectory(fullPath, extensions));
157
+ } else if (entry.isFile()) {
158
+ const ext = path4.extname(entry.name);
159
+ if (extensions.includes(ext)) {
160
+ if (entry.name.startsWith("_") && entry.name !== "_layout.stx") {
161
+ continue;
162
+ }
163
+ if (entry.name === "_layout.stx") {
164
+ continue;
165
+ }
166
+ files.push(fullPath);
167
+ }
168
+ }
169
+ }
170
+ return files;
171
+ }
172
+
173
+ class Router {
174
+ routes;
175
+ pagesDir;
176
+ pagesDirs;
177
+ config;
178
+ constructor(baseDir, config = {}) {
179
+ this.config = config;
180
+ const rawDirs = config.pagesDirs && config.pagesDirs.length > 0 ? config.pagesDirs : [config.pagesDir || "pages"];
181
+ this.pagesDirs = rawDirs.map((d) => path4.resolve(baseDir, d));
182
+ this.pagesDir = this.pagesDirs[0];
183
+ const extensions = config.extensions || [".stx"];
184
+ const canonical = (entry) => {
185
+ const resolved = path4.resolve(baseDir, entry);
186
+ try {
187
+ return fs3.realpathSync(resolved);
188
+ } catch {
189
+ return resolved;
190
+ }
191
+ };
192
+ const excluded = (config.exclude ?? []).filter((entry) => typeof entry === "string" && entry.trim() !== "").map(canonical);
193
+ const isExcluded = (file) => {
194
+ if (excluded.length === 0)
195
+ return false;
196
+ const real = canonical(file);
197
+ return excluded.some((root) => real === root || real.startsWith(`${root}${path4.sep}`));
198
+ };
199
+ const seenPatterns = new Set;
200
+ this.routes = [];
201
+ for (const dir of this.pagesDirs) {
202
+ const files = scanDirectory(dir, extensions).filter((file) => !isExcluded(file));
203
+ const localPatterns = new Map;
204
+ for (const filePath of files) {
205
+ const pattern = filePathToPattern(filePath, dir);
206
+ const identity = config.pagesDirs ? pattern.replace(/:[\w]+/g, ":param") : pattern;
207
+ if (config.pagesDirs && localPatterns.has(identity))
208
+ throw new Error(`Conflicting layer pages for ${pattern}: ${localPatterns.get(identity)} and ${filePath}`);
209
+ localPatterns.set(identity, filePath);
210
+ if (seenPatterns.has(identity))
211
+ continue;
212
+ seenPatterns.add(identity);
213
+ const { regex, params } = patternToRegex(pattern);
214
+ const layouts = config.layouts !== false ? resolveLayoutChain(filePath, dir) : [];
215
+ this.routes.push({
216
+ pattern,
217
+ regex,
218
+ params,
219
+ filePath,
220
+ isDynamic: params.length > 0,
221
+ layout: layouts.length > 0 ? layouts[layouts.length - 1] : undefined
222
+ });
223
+ }
224
+ }
225
+ this.routes.sort((a, b) => {
226
+ if (!a.isDynamic && b.isDynamic)
227
+ return -1;
228
+ if (a.isDynamic && !b.isDynamic)
229
+ return 1;
230
+ const aSegments = a.pattern.split("/").length;
231
+ const bSegments = b.pattern.split("/").length;
232
+ if (aSegments !== bSegments)
233
+ return bSegments - aSegments;
234
+ return a.pattern.localeCompare(b.pattern);
235
+ });
236
+ if (config.emit !== false) {
237
+ const stxDir = path4.resolve(baseDir, config.stateDir || process.env.STX_DIR?.trim() || ".stx");
238
+ generateRouteTypes(this.routes, stxDir);
239
+ generateRouteManifest(this.routes, stxDir, this.pagesDir);
240
+ }
241
+ }
242
+ match(pathname) {
243
+ const normalizedPath = pathname === "" ? "/" : pathname;
244
+ for (const route of this.routes) {
245
+ const m = normalizedPath.match(route.regex);
246
+ if (m) {
247
+ const params = {};
248
+ route.params.forEach((name, index) => {
249
+ params[name] = m[index + 1];
250
+ });
251
+ return { route, params };
252
+ }
253
+ }
254
+ return null;
255
+ }
256
+ resolve(name, params) {
257
+ const route = this.routes.find((r) => r.pattern === name || r.filePath.includes(name));
258
+ if (!route)
259
+ return "#not-found";
260
+ let resolved = route.pattern;
261
+ if (params) {
262
+ for (const [key, value] of Object.entries(params)) {
263
+ resolved = resolved.replace(`:${key}`, encodeURIComponent(value));
264
+ }
265
+ }
266
+ return resolved;
267
+ }
268
+ addRoute(route) {
269
+ this.routes.push(route);
270
+ this.routes.sort((a, b) => {
271
+ if (!a.isDynamic && b.isDynamic)
272
+ return -1;
273
+ if (a.isDynamic && !b.isDynamic)
274
+ return 1;
275
+ return a.pattern.localeCompare(b.pattern);
276
+ });
277
+ }
278
+ getLayout(route) {
279
+ return route.layout ?? null;
280
+ }
281
+ }
282
+ // ../router/src/middleware.ts
283
+ var middlewareRegistry = new Map;
284
+ export {
285
+ Router
286
+ };
@@ -20,4 +20,4 @@ function extractLayoutMetadata(html) {
20
20
  };
21
21
  }
22
22
 
23
- export { deriveLayoutGroup, extractLayoutMetadata };
23
+ export { extractLayoutMetadata };
package/dist/client.js CHANGED
@@ -1,6 +1,4 @@
1
1
  // @bun
2
- import"./chunk-2mx7fq49.js";
3
-
4
2
  // src/client.ts
5
3
  var version = "1.0.0";
6
4
  var isBrowser = typeof window !== "undefined" && typeof document !== "undefined";
@@ -110,8 +108,8 @@ function initIslands(handlers, config = {}) {
110
108
  }
111
109
  }
112
110
  export {
113
- version,
114
- preloadIslandHandlers,
111
+ hydrateIslands,
115
112
  initIslands,
116
- hydrateIslands
113
+ preloadIslandHandlers,
114
+ version
117
115
  };
package/dist/index.js CHANGED
@@ -1,22 +1,21 @@
1
1
  // @bun
2
2
  import {
3
- createMiddleware,
4
- createRoute,
5
- createWatcher,
6
- readMarkdownFile,
7
- src_default,
8
- startWatchMode,
9
- stxPlugin,
10
- watchAndBuild
11
- } from "./chunk-anqecs1e.js";
12
- import"./chunk-2mx7fq49.js";
3
+ createWatcher2,
4
+ watchAndBuild2,
5
+ startWatchMode2,
6
+ stxPlugin2,
7
+ createMiddleware2,
8
+ createRoute2,
9
+ readMarkdownFile2,
10
+ src_default
11
+ } from "./chunk-18kmm7vg.js";
13
12
  export {
14
- watchAndBuild,
15
- stxPlugin,
16
- startWatchMode,
17
- readMarkdownFile,
13
+ createMiddleware2 as createMiddleware,
14
+ createRoute2 as createRoute,
15
+ createWatcher2 as createWatcher,
18
16
  src_default as default,
19
- createWatcher,
20
- createRoute,
21
- createMiddleware
17
+ readMarkdownFile2 as readMarkdownFile,
18
+ startWatchMode2 as startWatchMode,
19
+ stxPlugin2 as stxPlugin,
20
+ watchAndBuild2 as watchAndBuild
22
21
  };
package/dist/preload.js CHANGED
@@ -1,8 +1,7 @@
1
1
  // @bun
2
2
  import {
3
3
  src_default
4
- } from "./chunk-anqecs1e.js";
5
- import"./chunk-2mx7fq49.js";
4
+ } from "./chunk-18kmm7vg.js";
6
5
 
7
6
  // src/preload.ts
8
7
  var {plugin } = globalThis.Bun;