bun-plugin-stx 0.2.0 → 0.2.3

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.
@@ -0,0 +1,4 @@
1
+ // @bun
2
+ var __require = import.meta.require;
3
+
4
+ export { __require };
@@ -0,0 +1,458 @@
1
+ // @bun
2
+ // src/index.ts
3
+ import path2 from "path";
4
+ import { buildWebComponents, cacheTemplate, checkCache, defaultConfig, extractVariables, processClientScript, processDirectives, readMarkdownFile } from "@stacksjs/stx";
5
+
6
+ // src/watch.ts
7
+ import fs from "fs";
8
+ import path from "path";
9
+ import process from "process";
10
+ function debounce(fn, delay) {
11
+ let timeoutId = null;
12
+ return (...args) => {
13
+ if (timeoutId) {
14
+ clearTimeout(timeoutId);
15
+ }
16
+ timeoutId = setTimeout(() => {
17
+ fn(...args);
18
+ timeoutId = null;
19
+ }, delay);
20
+ };
21
+ }
22
+ function shouldWatch(filePath, options) {
23
+ const ext = path.extname(filePath);
24
+ const extensions = options.extensions || [".stx", ".md"];
25
+ const ignore = options.ignore || ["node_modules", ".git", "dist", ".stx"];
26
+ if (!extensions.includes(ext)) {
27
+ return false;
28
+ }
29
+ for (const ignorePath of ignore) {
30
+ if (filePath.includes(ignorePath)) {
31
+ return false;
32
+ }
33
+ }
34
+ return true;
35
+ }
36
+ function createWatcher(options = {}) {
37
+ const watchedPaths = new Set;
38
+ const watchers = new Map;
39
+ let isActive = true;
40
+ const paths = options.paths || [process.cwd()];
41
+ const debounceDelay = options.debounce ?? 100;
42
+ const pendingChanges = [];
43
+ const handleChanges = debounce(() => {
44
+ if (pendingChanges.length > 0 && options.onChange) {
45
+ for (const event of pendingChanges) {
46
+ options.onChange(event);
47
+ }
48
+ pendingChanges.length = 0;
49
+ }
50
+ }, debounceDelay);
51
+ function watchDirectory(dirPath) {
52
+ if (watchers.has(dirPath)) {
53
+ return;
54
+ }
55
+ try {
56
+ const watcher = fs.watch(dirPath, { recursive: true }, (eventType, filename) => {
57
+ if (!isActive || !filename) {
58
+ return;
59
+ }
60
+ const fullPath = path.join(dirPath, filename);
61
+ if (!shouldWatch(fullPath, options)) {
62
+ return;
63
+ }
64
+ const event = {
65
+ type: eventType,
66
+ path: fullPath,
67
+ timestamp: Date.now()
68
+ };
69
+ if (options.verbose) {
70
+ console.log(`[stx-watch] ${eventType}: ${fullPath}`);
71
+ }
72
+ pendingChanges.push(event);
73
+ handleChanges();
74
+ });
75
+ watcher.on("error", (error) => {
76
+ if (options.onError) {
77
+ options.onError(error);
78
+ } else {
79
+ console.error(`[stx-watch] Error watching ${dirPath}:`, error);
80
+ }
81
+ });
82
+ watchers.set(dirPath, watcher);
83
+ watchedPaths.add(dirPath);
84
+ if (options.verbose) {
85
+ console.log(`[stx-watch] Watching: ${dirPath}`);
86
+ }
87
+ } catch (error) {
88
+ if (options.onError) {
89
+ options.onError(error);
90
+ }
91
+ }
92
+ }
93
+ for (const watchPath of paths) {
94
+ const absolutePath = path.resolve(watchPath);
95
+ if (fs.existsSync(absolutePath)) {
96
+ watchDirectory(absolutePath);
97
+ }
98
+ }
99
+ return {
100
+ close: () => {
101
+ isActive = false;
102
+ for (const watcher of watchers.values()) {
103
+ watcher.close();
104
+ }
105
+ watchers.clear();
106
+ watchedPaths.clear();
107
+ if (options.verbose) {
108
+ console.log("[stx-watch] Stopped watching");
109
+ }
110
+ },
111
+ getWatchedPaths: () => Array.from(watchedPaths),
112
+ addPath: (newPath) => {
113
+ const absolutePath = path.resolve(newPath);
114
+ if (fs.existsSync(absolutePath) && !watchers.has(absolutePath)) {
115
+ watchDirectory(absolutePath);
116
+ }
117
+ },
118
+ removePath: (pathToRemove) => {
119
+ const absolutePath = path.resolve(pathToRemove);
120
+ const watcher = watchers.get(absolutePath);
121
+ if (watcher) {
122
+ watcher.close();
123
+ watchers.delete(absolutePath);
124
+ watchedPaths.delete(absolutePath);
125
+ }
126
+ },
127
+ isWatching: () => isActive && watchers.size > 0
128
+ };
129
+ }
130
+ async function watchAndBuild(options) {
131
+ const {
132
+ entrypoints,
133
+ outdir = "./dist",
134
+ buildConfig = {},
135
+ stxOptions,
136
+ onRebuild,
137
+ ...watchOptions
138
+ } = options;
139
+ let triggeredBy = [];
140
+ async function build() {
141
+ const startTime = Date.now();
142
+ const currentTriggers = [...triggeredBy];
143
+ triggeredBy = [];
144
+ try {
145
+ const result = await Bun.build({
146
+ entrypoints,
147
+ outdir,
148
+ plugins: [stxPlugin(stxOptions)],
149
+ ...buildConfig
150
+ });
151
+ const duration = Date.now() - startTime;
152
+ const outputs = result.outputs.map((o) => o.path);
153
+ if (!result.success) {
154
+ const errors = result.logs.filter((log) => log.level === "error").map((log) => new Error(log.message));
155
+ return {
156
+ success: false,
157
+ errors,
158
+ duration,
159
+ triggeredBy: currentTriggers
160
+ };
161
+ }
162
+ return {
163
+ success: true,
164
+ outputs,
165
+ duration,
166
+ triggeredBy: currentTriggers
167
+ };
168
+ } catch (error) {
169
+ return {
170
+ success: false,
171
+ errors: [error],
172
+ duration: Date.now() - startTime,
173
+ triggeredBy: currentTriggers
174
+ };
175
+ }
176
+ }
177
+ if (options.verbose) {
178
+ console.log("[stx-watch] Performing initial build...");
179
+ }
180
+ const initialResult = await build();
181
+ if (onRebuild) {
182
+ onRebuild(initialResult);
183
+ }
184
+ const dirsToWatch = new Set;
185
+ for (const entrypoint of entrypoints) {
186
+ const dir = path.dirname(path.resolve(entrypoint));
187
+ dirsToWatch.add(dir);
188
+ }
189
+ const commonDirs = ["src", "pages", "components", "layouts", "partials"];
190
+ for (const dir of commonDirs) {
191
+ const fullPath = path.resolve(dir);
192
+ if (fs.existsSync(fullPath)) {
193
+ dirsToWatch.add(fullPath);
194
+ }
195
+ }
196
+ const watcher = createWatcher({
197
+ ...watchOptions,
198
+ paths: watchOptions.paths || Array.from(dirsToWatch),
199
+ onChange: async (event) => {
200
+ triggeredBy.push(event.path);
201
+ if (watchOptions.onChange) {
202
+ watchOptions.onChange(event);
203
+ }
204
+ if (options.verbose) {
205
+ console.log(`[stx-watch] Rebuilding due to change in ${event.path}...`);
206
+ }
207
+ const result = await build();
208
+ if (onRebuild) {
209
+ onRebuild(result);
210
+ }
211
+ }
212
+ });
213
+ if (options.verbose) {
214
+ console.log(`[stx-watch] Watching for changes in: ${watcher.getWatchedPaths().join(", ")}`);
215
+ }
216
+ return watcher;
217
+ }
218
+ async function startWatchMode(args) {
219
+ console.log("\x1B[36m[stx]\x1B[0m Starting watch mode...");
220
+ const watcher = await watchAndBuild({
221
+ entrypoints: args.entrypoints,
222
+ outdir: args.outdir,
223
+ verbose: args.verbose,
224
+ stxOptions: args.stxOptions,
225
+ onRebuild: (result) => {
226
+ if (result.success) {
227
+ const files = result.triggeredBy.length > 0 ? ` (${result.triggeredBy.map((f) => path.basename(f)).join(", ")})` : "";
228
+ console.log(`\x1B[32m[stx]\x1B[0m Built in ${result.duration}ms${files}`);
229
+ } else {
230
+ console.error("\x1B[31m[stx]\x1B[0m Build failed:");
231
+ for (const error of result.errors || []) {
232
+ console.error(` ${error.message}`);
233
+ }
234
+ }
235
+ }
236
+ });
237
+ process.on("SIGINT", () => {
238
+ console.log(`
239
+ \x1B[36m[stx]\x1B[0m Stopping watch mode...`);
240
+ watcher.close();
241
+ process.exit(0);
242
+ });
243
+ process.on("SIGTERM", () => {
244
+ watcher.close();
245
+ process.exit(0);
246
+ });
247
+ return watcher;
248
+ }
249
+
250
+ // src/index.ts
251
+ import { createMiddleware, createRoute, readMarkdownFile as readMarkdownFile2 } from "@stacksjs/stx";
252
+ function escapeHtmlForError(str) {
253
+ return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
254
+ }
255
+ function generateErrorPage(title, message, filePath) {
256
+ const escapedMessage = escapeHtmlForError(message);
257
+ const escapedPath = filePath ? escapeHtmlForError(filePath) : "";
258
+ return `<!DOCTYPE html>
259
+ <html>
260
+ <head>
261
+ <title>stx Error</title>
262
+ <style>
263
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; padding: 2rem; background: #1a1a2e; color: #eee; }
264
+ h1 { color: #ff6b6b; }
265
+ pre { background: #16213e; padding: 1rem; border-radius: 4px; overflow-x: auto; border-left: 4px solid #ff6b6b; }
266
+ .file-path { color: #888; font-size: 0.9rem; margin-bottom: 1rem; }
267
+ </style>
268
+ </head>
269
+ <body>
270
+ <h1>${title}</h1>
271
+ ${escapedPath ? `<div class="file-path">File: ${escapedPath}</div>` : ""}
272
+ <pre>${escapedMessage}</pre>
273
+ </body>
274
+ </html>`;
275
+ }
276
+ function stxPlugin(userOptions) {
277
+ return {
278
+ name: "bun-plugin-stx",
279
+ async setup(build) {
280
+ const options = {
281
+ ...defaultConfig,
282
+ ...userOptions
283
+ };
284
+ const allDependencies = new Set;
285
+ const webComponentsPath = options.webComponents?.enabled ? `./${path2.relative(path2.dirname(build.config?.outdir || "dist"), options.webComponents.outputDir || "dist/web-components")}` : "/web-components";
286
+ const builtComponents = [];
287
+ if (options.webComponents?.enabled) {
288
+ try {
289
+ const components = await buildWebComponents(options, allDependencies);
290
+ builtComponents.push(...components);
291
+ if (options.debug && components.length > 0) {
292
+ console.log(`Successfully built ${components.length} web components`);
293
+ }
294
+ } catch (error) {
295
+ console.error("Failed to build web components:", error);
296
+ }
297
+ }
298
+ build.onLoad({ filter: /\.md$/ }, async ({ path: filePath }) => {
299
+ try {
300
+ const { content: htmlContent, data: frontmatter } = await readMarkdownFile(filePath, options);
301
+ const jsContent = `// ${filePath}
302
+ var content = ${JSON.stringify(htmlContent)};
303
+ var data = ${JSON.stringify(frontmatter)};
304
+
305
+ export { content, data };
306
+ export { content as default };
307
+ `;
308
+ return {
309
+ contents: jsContent,
310
+ loader: "js"
311
+ };
312
+ } catch (error) {
313
+ console.error("Markdown Processing Error:", error);
314
+ const errorContent = generateErrorPage("Markdown Processing Error", error.message || "Unknown error", filePath);
315
+ const jsContent = `// ${filePath}
316
+ var content = ${JSON.stringify(errorContent)};
317
+ var data = {};
318
+
319
+ export { content, data };
320
+ export { content as default };
321
+ `;
322
+ return {
323
+ contents: jsContent,
324
+ loader: "js"
325
+ };
326
+ }
327
+ });
328
+ build.onLoad({ filter: /\.(jsx|tsx)$/ }, async ({ path: filePath }) => {
329
+ try {
330
+ const source = await Bun.file(filePath).text();
331
+ const loader = filePath.endsWith(".tsx") ? "tsx" : "jsx";
332
+ const transpiler = new Bun.Transpiler({
333
+ loader,
334
+ jsxOptimizationInline: true
335
+ });
336
+ const code = transpiler.transformSync(source);
337
+ return {
338
+ contents: code,
339
+ loader: "js"
340
+ };
341
+ } catch (error) {
342
+ console.error("JSX Processing Error:", error);
343
+ return {
344
+ contents: `export default function() { return null; }`,
345
+ loader: "js"
346
+ };
347
+ }
348
+ });
349
+ build.onLoad({ filter: /\.stx$/ }, async ({ path: filePath }) => {
350
+ try {
351
+ const dependencies = new Set;
352
+ if (options.cache && options.cachePath) {
353
+ const cachedOutput = await checkCache(filePath, options);
354
+ if (cachedOutput) {
355
+ if (options.debug) {
356
+ console.log(`Using cached version of ${filePath}`);
357
+ }
358
+ return {
359
+ contents: cachedOutput,
360
+ loader: "html"
361
+ };
362
+ }
363
+ }
364
+ const content = await Bun.file(filePath).text();
365
+ let workingContent = content;
366
+ const templateTagMatch = content.match(/<template\b[^>]*>([\s\S]*?)<\/template>/i);
367
+ if (templateTagMatch) {
368
+ workingContent = templateTagMatch[1].trim();
369
+ }
370
+ const scriptRegex = /<script\b([^>]*)>([\s\S]*?)<\/script>/gi;
371
+ const clientScripts = [];
372
+ const serverScripts = [];
373
+ const signalsScripts = [];
374
+ let scriptMatch;
375
+ const usesSignalsAPI = (content2) => /\b(?:state|derived|effect|batch)\s*\(/.test(content2);
376
+ while ((scriptMatch = scriptRegex.exec(content)) !== null) {
377
+ const attrs = scriptMatch[1];
378
+ const scriptContent = scriptMatch[2];
379
+ const fullScript = scriptMatch[0];
380
+ const isClientScript = attrs.includes("client") || attrs.includes('type="module"') || attrs.includes("src=");
381
+ const isSignalsScript = usesSignalsAPI(scriptContent);
382
+ if (isSignalsScript) {
383
+ signalsScripts.push(fullScript);
384
+ } else if (isClientScript) {
385
+ clientScripts.push(fullScript);
386
+ } else {
387
+ serverScripts.push(scriptContent);
388
+ }
389
+ }
390
+ let templateContent = workingContent;
391
+ for (const script of serverScripts) {
392
+ const escapedScript = script.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
393
+ templateContent = templateContent.replace(new RegExp(`<script\\b[^>]*>${escapedScript}<\\/script>`, "gi"), "");
394
+ }
395
+ for (const script of clientScripts) {
396
+ const escapedScript = script.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
397
+ templateContent = templateContent.replace(escapedScript, "");
398
+ }
399
+ const context = {
400
+ __filename: filePath,
401
+ __dirname: path2.dirname(filePath),
402
+ __stx: {
403
+ webComponentsPath,
404
+ builtComponents
405
+ },
406
+ __stx_options: options,
407
+ __stx_sfc_mode: true
408
+ };
409
+ for (const scriptContent of serverScripts) {
410
+ await extractVariables(scriptContent, context, filePath);
411
+ }
412
+ let output = templateContent;
413
+ output = await processDirectives(output, context, filePath, options, dependencies);
414
+ if (clientScripts.length > 0) {
415
+ const eventBindings = context.__stx_event_bindings || [];
416
+ const transformedScripts = clientScripts.map((fullScript) => {
417
+ const contentMatch = fullScript.match(/<script\b[^>]*>([\s\S]*?)<\/script>/);
418
+ if (!contentMatch)
419
+ return fullScript;
420
+ return processClientScript(contentMatch[1], { eventBindings });
421
+ });
422
+ const scriptsHtml = transformedScripts.join(`
423
+ `);
424
+ const bodyEndMatch = output.match(/(<\/body>)/i);
425
+ if (bodyEndMatch) {
426
+ output = output.replace(/(<\/body>)/i, `${scriptsHtml}
427
+ $1`);
428
+ } else {
429
+ output += `
430
+ ${scriptsHtml}`;
431
+ }
432
+ context.__stx_event_bindings = [];
433
+ }
434
+ dependencies.forEach((dep) => allDependencies.add(dep));
435
+ if (options.cache && options.cachePath) {
436
+ await cacheTemplate(filePath, output, dependencies, options);
437
+ if (options.debug) {
438
+ console.log(`Cached template ${filePath} with ${dependencies.size} dependencies`);
439
+ }
440
+ }
441
+ return {
442
+ contents: output,
443
+ loader: "html"
444
+ };
445
+ } catch (error) {
446
+ console.error("stx Plugin Error:", error);
447
+ return {
448
+ contents: generateErrorPage("stx Rendering Error", error.message || String(error), filePath),
449
+ loader: "html"
450
+ };
451
+ }
452
+ });
453
+ }
454
+ };
455
+ }
456
+ var src_default = stxPlugin;
457
+
458
+ export { createWatcher, watchAndBuild, startWatchMode, stxPlugin, createMiddleware, createRoute, readMarkdownFile2 as readMarkdownFile, src_default };
package/dist/client.js CHANGED
@@ -1,2 +1,117 @@
1
1
  // @bun
2
- import"./chunk-h0s48z3v.js";var R="1.0.0",L=typeof window<"u"&&typeof document<"u";async function N(x,E,G){if(!L)return;try{let j=x.getAttribute("data-island-id"),q=document.querySelector(`script[data-island-props="${j}"]`),C=q?JSON.parse(q.textContent||"{}"):{},J=await G(),K=J.default||J.hydrate;if(typeof K!=="function"){console.error(`Island handler for "${E}" does not export a valid hydration function`);return}await K(x,C),x.setAttribute("data-hydrated","true")}catch(j){console.error(`Failed to hydrate island "${E}":`,j)}}function P(x){if(!L)return;let E=document.querySelectorAll("[data-island]"),G=[];if(E.forEach((j)=>{let q=j.getAttribute("data-island"),C=j.getAttribute("data-priority")||"lazy";if(!q||!x[q]||j.getAttribute("data-hydrated")==="true")return;if(C==="eager")N(j,q,x[q]);else G.push([j,q])}),G.length>0&&"IntersectionObserver"in window){let j=new IntersectionObserver((q)=>{q.forEach((C)=>{if(C.isIntersecting){let J=C.target,K=J.getAttribute("data-island");if(K&&x[K]&&J.getAttribute("data-hydrated")!=="true")N(J,K,x[K]),j.unobserve(J)}})},{rootMargin:"200px",threshold:0});G.forEach(([q])=>{j.observe(q)})}else setTimeout(()=>{G.forEach(([j,q])=>{if(j.getAttribute("data-hydrated")!=="true")N(j,q,x[q])})},1000)}function Q(x){if(!L)return;Object.entries(x).forEach(([E,G])=>{try{let q=G.toString().match(/import\(['"](.+)['"]\)/);if(q&&q[1]){let C=document.createElement("link");C.rel="modulepreload",C.href=q[1],C.setAttribute("data-island-module",E),document.head.appendChild(C)}}catch(j){console.warn(`Failed to preload island handler for "${E}":`,j)}})}function T(x,E={}){if(!L)return;let j={...{autoStart:!0,preload:"lazy"},...E};if(j.preload!=="none")Q(x);if(j.autoStart)if(document.readyState==="loading")document.addEventListener("DOMContentLoaded",()=>{P(x)});else P(x)}export{R as version,Q as preloadIslandHandlers,T as initIslands,P as hydrateIslands};
2
+ import"./chunk-eecw9x2f.js";
3
+
4
+ // src/client.ts
5
+ var version = "1.0.0";
6
+ var isBrowser = typeof window !== "undefined" && typeof document !== "undefined";
7
+ async function hydrateIsland(element, name, handler) {
8
+ if (!isBrowser)
9
+ return;
10
+ try {
11
+ const id = element.getAttribute("data-island-id");
12
+ const propsScript = document.querySelector(`script[data-island-props="${id}"]`);
13
+ const props = propsScript ? JSON.parse(propsScript.textContent || "{}") : {};
14
+ const module = await handler();
15
+ const hydrateFn = module.default || module.hydrate;
16
+ if (typeof hydrateFn !== "function") {
17
+ console.error(`Island handler for "${name}" does not export a valid hydration function`);
18
+ return;
19
+ }
20
+ await hydrateFn(element, props);
21
+ element.setAttribute("data-hydrated", "true");
22
+ } catch (error) {
23
+ console.error(`Failed to hydrate island "${name}":`, error);
24
+ }
25
+ }
26
+ function hydrateIslands(handlers) {
27
+ if (!isBrowser)
28
+ return;
29
+ const islands = document.querySelectorAll("[data-island]");
30
+ const lazyIslands = [];
31
+ islands.forEach((island) => {
32
+ const name = island.getAttribute("data-island");
33
+ const priority = island.getAttribute("data-priority") || "lazy";
34
+ if (!name || !handlers[name] || island.getAttribute("data-hydrated") === "true") {
35
+ return;
36
+ }
37
+ if (priority === "eager") {
38
+ hydrateIsland(island, name, handlers[name]);
39
+ } else {
40
+ lazyIslands.push([island, name]);
41
+ }
42
+ });
43
+ if (lazyIslands.length > 0 && "IntersectionObserver" in window) {
44
+ const observer = new IntersectionObserver((entries) => {
45
+ entries.forEach((entry) => {
46
+ if (entry.isIntersecting) {
47
+ const element = entry.target;
48
+ const name = element.getAttribute("data-island");
49
+ if (name && handlers[name] && element.getAttribute("data-hydrated") !== "true") {
50
+ hydrateIsland(element, name, handlers[name]);
51
+ observer.unobserve(element);
52
+ }
53
+ }
54
+ });
55
+ }, {
56
+ rootMargin: "200px",
57
+ threshold: 0
58
+ });
59
+ lazyIslands.forEach(([element]) => {
60
+ observer.observe(element);
61
+ });
62
+ } else {
63
+ setTimeout(() => {
64
+ lazyIslands.forEach(([element, name]) => {
65
+ if (element.getAttribute("data-hydrated") !== "true") {
66
+ hydrateIsland(element, name, handlers[name]);
67
+ }
68
+ });
69
+ }, 1000);
70
+ }
71
+ }
72
+ function preloadIslandHandlers(handlers) {
73
+ if (!isBrowser)
74
+ return;
75
+ Object.entries(handlers).forEach(([name, getHandler]) => {
76
+ try {
77
+ const handlerString = getHandler.toString();
78
+ const moduleMatch = handlerString.match(/import\(['"](.+)['"]\)/);
79
+ if (moduleMatch && moduleMatch[1]) {
80
+ const link = document.createElement("link");
81
+ link.rel = "modulepreload";
82
+ link.href = moduleMatch[1];
83
+ link.setAttribute("data-island-module", name);
84
+ document.head.appendChild(link);
85
+ }
86
+ } catch (error) {
87
+ console.warn(`Failed to preload island handler for "${name}":`, error);
88
+ }
89
+ });
90
+ }
91
+ function initIslands(handlers, config = {}) {
92
+ if (!isBrowser)
93
+ return;
94
+ const defaultConfig = {
95
+ autoStart: true,
96
+ preload: "lazy"
97
+ };
98
+ const options = { ...defaultConfig, ...config };
99
+ if (options.preload !== "none") {
100
+ preloadIslandHandlers(handlers);
101
+ }
102
+ if (options.autoStart) {
103
+ if (document.readyState === "loading") {
104
+ document.addEventListener("DOMContentLoaded", () => {
105
+ hydrateIslands(handlers);
106
+ });
107
+ } else {
108
+ hydrateIslands(handlers);
109
+ }
110
+ }
111
+ }
112
+ export {
113
+ version,
114
+ preloadIslandHandlers,
115
+ initIslands,
116
+ hydrateIslands
117
+ };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,34 @@
1
1
  import type { BunPlugin } from 'bun';
2
2
  import { readMarkdownFile } from '@stacksjs/stx';
3
3
  import type { StxOptions } from '@stacksjs/stx';
4
- export type { ServeOptions, ServeResult, StxOptions } from '@stacksjs/stx';
4
+ export type {
5
+ WatchAndBuildOptions,
6
+ WatchBuildResult,
7
+ WatcherInstance,
8
+ WatchEvent,
9
+ WatchOptions,
10
+ } from './watch';
11
+ export type { CustomDirective, CustomDirectiveHandler, ServeOptions, ServeResult, StxOptions } from '@stacksjs/stx';
12
+ /**
13
+ * Create a Bun plugin for processing .stx and .md files
14
+ *
15
+ * @param userOptions - Optional configuration to override defaults
16
+ * @returns BunPlugin instance
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * // Basic usage
21
+ * const plugin = stxPlugin()
22
+ *
23
+ * // With options
24
+ * const plugin = stxPlugin({
25
+ * debug: true,
26
+ * cache: true,
27
+ * cachePath: '.stx/cache'
28
+ * })
29
+ * ```
30
+ */
5
31
  export declare function stxPlugin(userOptions?: StxOptions): BunPlugin;
6
- export { createMiddleware, createRoute, readMarkdownFile, serve } from '@stacksjs/stx';
32
+ export { createWatcher, startWatchMode, watchAndBuild } from './watch';
33
+ export { createMiddleware, createRoute, readMarkdownFile } from '@stacksjs/stx';
7
34
  export default stxPlugin;
package/dist/index.js CHANGED
@@ -1,20 +1,22 @@
1
1
  // @bun
2
- import{b as E,c as M,d as F,e as g,f as P,g as N}from"./chunk-mjj2b826.js";import"./chunk-h0s48z3v.js";import u from"path";import q from"fs";import L from"path";var i=new Map;async function b(z,j){try{let Z=L.resolve(j.cachePath),V=L.join(Z,`${w(z)}.html`),K=L.join(Z,`${w(z)}.meta.json`);if(!await M(V)||!await M(K))return null;let _=await Bun.file(K).text(),Q=JSON.parse(_);if(Q.cacheVersion!==j.cacheVersion)return null;if((await q.promises.stat(z)).mtime.getTime()>Q.mtime)return null;for(let J of Q.dependencies)if(await M(J)){if((await q.promises.stat(J)).mtime.getTime()>Q.mtime)return null}else return null;return await Bun.file(V).text()}catch(Z){return console.warn(`Cache error for ${z}:`,Z),null}}async function C(z,j,Z,V){try{let K=L.resolve(V.cachePath);await q.promises.mkdir(K,{recursive:!0});let _=L.join(K,`${w(z)}.html`),Q=L.join(K,`${w(z)}.meta.json`),H=await q.promises.stat(z);await Bun.write(_,j);let J={sourcePath:z,mtime:H.mtime.getTime(),dependencies:Array.from(Z),cacheVersion:V.cacheVersion,generatedAt:Date.now()};await Bun.write(Q,JSON.stringify(J,null,2)),i.set(z,{output:j,mtime:H.mtime.getTime(),dependencies:Z})}catch(K){console.warn(`Failed to cache template ${z}:`,K)}}function w(z){return new Bun.CryptoHasher("sha1").update(z).digest("hex").substring(0,16)}var{serve:n}=globalThis.Bun;import k from"fs";import S from"path";async function r(z={}){let{port:j=3000,root:Z=".",stxOptions:V={},watch:K=!0,onRequest:_,routes:Q={},middleware:H=[],on404:J,onError:W}=z,y=S.resolve(Z),A=new Map;async function T($){let G=k.statSync($),U=`${$}:${G.mtimeMs}`,Y=A.get(U);if(Y)return Y.content;let X=await Bun.file($).text(),B=X.match(/<script\b[^>]*>([\s\S]*?)<\/script>/i),m=B?B[1]:"",p=X.match(/<script\b[^>]*>[\s\S]*?<\/script>/gi)||[],l=X.replace(/<script\b[^>]*>[\s\S]*?<\/script>/gi,""),f={__filename:$,__dirname:S.dirname($)};await F(m,f,$);let R=await E(l,f,$,V,new Set),D=[...p];if(D.length>0)if(R.match(/(<\/body>)/i)){let c=D.join(`
3
- `);R=R.replace(/(<\/body>)/i,`${c}
4
- $1`)}else R+=`
5
- ${D.join(`
6
- `)}`;return A.set(U,{content:R,mtime:G.mtimeMs}),R}async function O($){let G=k.statSync($),U=`${$}:${G.mtimeMs}`,Y=A.get(U);if(Y)return Y.content;let{content:X}=await N($,V);return A.set(U,{content:X,mtime:G.mtimeMs}),X}function I($){let G=$.startsWith("/")?$.slice(1):$,U=[G,`${G}.stx`,`${G}.md`,`${G}.html`,S.join(G,"index.stx"),S.join(G,"index.md"),S.join(G,"index.html")];for(let Y of U){let X=S.join(y,Y);if(k.existsSync(X)&&k.statSync(X).isFile())return X}return null}async function h($){let G=new URL($.url),U=async()=>{if(Q[G.pathname])return await Q[G.pathname]($);if(_){let X=await _($);if(X)return X}let Y=I(G.pathname);if(!Y){if(J)return await J($);return new Response("Not Found",{status:404})}try{let X,B="text/html";if(Y.endsWith(".stx"))X=await T(Y);else if(Y.endsWith(".md"))X=await O(Y);else if(Y.endsWith(".html"))X=await Bun.file(Y).text();else if(Y.endsWith(".css"))X=await Bun.file(Y).text(),B="text/css";else if(Y.endsWith(".js"))X=await Bun.file(Y).text(),B="text/javascript";else if(Y.endsWith(".json"))X=await Bun.file(Y).text(),B="application/json";else return new Response(Bun.file(Y));return new Response(X,{headers:{"Content-Type":B}})}catch(X){if(W)return await W(X,$);return new Response(`<h1>Error</h1><pre>${X.message}
7
- ${X.stack}</pre>`,{status:500,headers:{"Content-Type":"text/html"}})}};for(let Y=H.length-1;Y>=0;Y--){let X=H[Y],B=U;U=async()=>await X($,B)}return await U()}let x=n({port:j,fetch:h}),v=null;if(K)v=k.watch(y,{recursive:!0},($,G)=>{if(G&&(G.endsWith(".stx")||G.endsWith(".md")||G.endsWith(".html")))A.clear()});return{server:x,url:x.url.toString(),stop(){if(v)v.close();x.stop()}}}function s(z){return z}function o(z){return z}function e(z){return{name:"bun-plugin-stx",async setup(j){let Z={...P,...z},V=new Set,K=Z.webComponents?.enabled?`./${u.relative(u.dirname(j.config?.outdir||"dist"),Z.webComponents.outputDir||"dist/web-components")}`:"/web-components",_=[];if(Z.webComponents?.enabled)try{let Q=await g(Z,V);if(_.push(...Q),Z.debug&&Q.length>0)console.log(`Successfully built ${Q.length} web components`)}catch(Q){console.error("Failed to build web components:",Q)}j.onLoad({filter:/\.md$/},async({path:Q})=>{try{let{content:H,data:J}=await N(Q,Z);return{contents:`// ${Q}
8
- var content = ${JSON.stringify(H)};
9
- var data = ${JSON.stringify(J)};
10
-
11
- export { content, data };
12
- export { content as default };
13
- `,loader:"js"}}catch(H){console.error("Markdown Processing Error:",H);let J=`<!DOCTYPE html><html><body><h1>Markdown Error</h1><pre>${H.message||"Unknown error"}</pre></body></html>`;return{contents:`// ${Q}
14
- var content = ${JSON.stringify(J)};
15
- var data = {};
16
-
17
- export { content, data };
18
- export { content as default };
19
- `,loader:"js"}}}),j.onLoad({filter:/\.stx$/},async({path:Q})=>{try{let H=new Set;if(Z.cache&&Z.cachePath){let I=await b(Q,Z);if(I){if(Z.debug)console.log(`Using cached version of ${Q}`);return{contents:I,loader:"html"}}}let J=await Bun.file(Q).text(),W=J.match(/<script\b[^>]*>([\s\S]*?)<\/script>/i),y=W?W[1]:"",A=J.replace(/<script\b[^>]*>[\s\S]*?<\/script>/i,""),T={__filename:Q,__dirname:u.dirname(Q),__stx:{webComponentsPath:K,builtComponents:_}};await F(y,T,Q);let O=A;if(O=await E(O,T,Q,Z,H),H.forEach((I)=>V.add(I)),Z.cache&&Z.cachePath){if(await C(Q,O,H,Z),Z.debug)console.log(`Cached template ${Q} with ${H.size} dependencies`)}return{contents:O,loader:"html"}}catch(H){return console.error("stx Plugin Error:",H),{contents:`<!DOCTYPE html><html><body><h1>stx Rendering Error</h1><pre>${H.message||String(H)}</pre></body></html>`,loader:"html"}}})}}}var X7=e;export{e as stxPlugin,r as serve,N as readMarkdownFile,X7 as default,o as createRoute,s as createMiddleware};
20
- export{X7 as a};
2
+ import {
3
+ createMiddleware,
4
+ createRoute,
5
+ createWatcher,
6
+ readMarkdownFile,
7
+ src_default,
8
+ startWatchMode,
9
+ stxPlugin,
10
+ watchAndBuild
11
+ } from "./chunk-ymdk9x8z.js";
12
+ import"./chunk-eecw9x2f.js";
13
+ export {
14
+ watchAndBuild,
15
+ stxPlugin,
16
+ startWatchMode,
17
+ readMarkdownFile,
18
+ src_default as default,
19
+ createWatcher,
20
+ createRoute,
21
+ createMiddleware
22
+ };
package/dist/preload.js CHANGED
@@ -1,2 +1,10 @@
1
1
  // @bun
2
- import{a as c}from"./index.js";import"./chunk-mjj2b826.js";import"./chunk-h0s48z3v.js";var{plugin:j}=globalThis.Bun;j(c());console.log("\u2705 stx plugin loaded - .stx files can now be imported");
2
+ import {
3
+ src_default
4
+ } from "./chunk-ymdk9x8z.js";
5
+ import"./chunk-eecw9x2f.js";
6
+
7
+ // src/preload.ts
8
+ var {plugin } = globalThis.Bun;
9
+ plugin(src_default());
10
+ console.log("\u2705 stx plugin loaded - .stx files can now be imported");