jq79 0.4.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dev/dev.ts +12 -2
- package/dev/vite.ts +49 -9
- package/dist/cli.js +8 -2
- package/dist/dev.cjs +8 -2
- package/dist/dev.cjs.map +1 -1
- package/dist/dev.js +8 -2
- package/dist/dev.js.map +1 -1
- package/dist/jq79.cjs +10 -9
- package/dist/jq79.cjs.map +1 -1
- package/dist/jq79.global.js +10 -9
- package/dist/jq79.global.js.map +1 -1
- package/dist/jq79.js +10 -9
- package/dist/jq79.js.map +1 -1
- package/dist/reactive.d.ts +1 -0
- package/dist/vite.cjs +55 -7
- package/dist/vite.cjs.map +1 -1
- package/dist/vite.js +55 -7
- package/dist/vite.js.map +1 -1
- package/package.json +1 -1
- package/src/jq79.ts +137 -36
- package/src/reactive.ts +83 -7
- package/src/transform.ts +102 -1
package/README.md
CHANGED
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
|
|
8
8
|
[](https://www.npmjs.com/package/jq79)
|
|
9
9
|
[](https://jgermade.github.io/jq79/coverage/)
|
|
10
|
-
[](
|
|
11
|
-
[](#cdn)
|
|
11
|
+
[](#cdn)
|
|
12
12
|
|
|
13
13
|
A independent reactive component library that ships as a single file. Svelte-style reactive scripts, fine-grained DOM updates via proxy-based dependency tracking
|
|
14
14
|
|
package/dev/dev.ts
CHANGED
|
@@ -92,7 +92,8 @@ const isDocument = (req: IncomingMessage) => req.headers["sec-fetch-dest"] === "
|
|
|
92
92
|
// fall back to the top of it rather than skipping the injection
|
|
93
93
|
const injectClient = (html: string): string => {
|
|
94
94
|
const tag = `<script src="${CLIENT_URL}"></script>`
|
|
95
|
-
|
|
95
|
+
// (\s[^>]*)? rather than [^>]*, or <header> would pass for <head>
|
|
96
|
+
const open = /<head(\s[^>]*)?>/i.exec(html) ?? /<body(\s[^>]*)?>/i.exec(html)
|
|
96
97
|
if (!open) return tag + html
|
|
97
98
|
const at = open.index + open[0].length
|
|
98
99
|
return html.slice(0, at) + tag + html.slice(at)
|
|
@@ -130,7 +131,16 @@ export const devServer = async (options: DevServerOptions = {}): Promise<DevServ
|
|
|
130
131
|
}
|
|
131
132
|
|
|
132
133
|
try {
|
|
133
|
-
if ((await stat(file)).isDirectory())
|
|
134
|
+
if ((await stat(file)).isDirectory()) {
|
|
135
|
+
// a directory is served through its trailing-slash URL, like every
|
|
136
|
+
// static host: /docs rendered as-is would resolve its relative links
|
|
137
|
+
// against the parent ("img.png" -> /img.png instead of /docs/img.png)
|
|
138
|
+
if (!pathname.endsWith("/")) {
|
|
139
|
+
res.writeHead(301, { location: pathname + "/" }).end()
|
|
140
|
+
return
|
|
141
|
+
}
|
|
142
|
+
file = join(file, "index.html")
|
|
143
|
+
}
|
|
134
144
|
const body = await readFile(file)
|
|
135
145
|
const type = CONTENT_TYPES[extname(file).toLowerCase()] ?? "application/octet-stream"
|
|
136
146
|
|
package/dev/vite.ts
CHANGED
|
@@ -33,13 +33,57 @@ export interface Jq79PluginOptions {
|
|
|
33
33
|
const COMPONENT_QUERY = "?jq79"
|
|
34
34
|
|
|
35
35
|
const SCRIPT_BLOCK_RE = /<script\b[^>]*>([\s\S]*?)<\/script\s*>/gi
|
|
36
|
-
// import("...") with a literal specifier
|
|
37
|
-
//
|
|
38
|
-
const
|
|
36
|
+
// import("...") with a literal specifier, tried at word boundaries the
|
|
37
|
+
// scanner below reaches (which is what skips $__import and foo.import(...))
|
|
38
|
+
const IMPORT_CALL_RE = /import\s*\(\s*(["'])([^"'\n]+?)\1\s*\)/y
|
|
39
39
|
// static import statements (factory scripts): optional clause + literal
|
|
40
40
|
// specifier. The clause can't contain parens/quotes, so dynamic import()
|
|
41
41
|
// and import.meta never match
|
|
42
|
-
const
|
|
42
|
+
const STATIC_IMPORT_RE = /import\s*(?:[\w$\s,{}*]+?\s*from\s*)?(["'])([^"'\n]+)\1/y
|
|
43
|
+
|
|
44
|
+
const skipString = (src: string, start: number): number => {
|
|
45
|
+
const quote = src[start]
|
|
46
|
+
let i = start + 1
|
|
47
|
+
while (i < src.length) {
|
|
48
|
+
if (src[i] === "\\") { i += 2; continue }
|
|
49
|
+
if (src[i] === quote) return i + 1
|
|
50
|
+
i++
|
|
51
|
+
}
|
|
52
|
+
return src.length
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// the literal import specifiers in one script body. A scanner rather than a
|
|
56
|
+
// bare matchAll, because a specifier mentioned in a comment or a string is
|
|
57
|
+
// not an import: hoisting a commented-out `import("./old.html")` would pull
|
|
58
|
+
// dead files into the bundle - or break the build once the file is gone
|
|
59
|
+
const importSpecifiers = (script: string): string[] => {
|
|
60
|
+
const specs: string[] = []
|
|
61
|
+
let i = 0
|
|
62
|
+
while (i < script.length) {
|
|
63
|
+
const ch = script[i]
|
|
64
|
+
if (ch === "'" || ch === '"' || ch === "`") { i = skipString(script, i); continue }
|
|
65
|
+
if (ch === "/" && script[i + 1] === "/") {
|
|
66
|
+
const end = script.indexOf("\n", i)
|
|
67
|
+
i = end === -1 ? script.length : end + 1
|
|
68
|
+
continue
|
|
69
|
+
}
|
|
70
|
+
if (ch === "/" && script[i + 1] === "*") {
|
|
71
|
+
const end = script.indexOf("*/", i + 2)
|
|
72
|
+
i = end === -1 ? script.length : end + 2
|
|
73
|
+
continue
|
|
74
|
+
}
|
|
75
|
+
if (ch === "i" && (i === 0 || !/[\w$.]/.test(script[i - 1]))) {
|
|
76
|
+
IMPORT_CALL_RE.lastIndex = i
|
|
77
|
+
const call = IMPORT_CALL_RE.exec(script)
|
|
78
|
+
if (call) { specs.push(call[2]); i = IMPORT_CALL_RE.lastIndex; continue }
|
|
79
|
+
STATIC_IMPORT_RE.lastIndex = i
|
|
80
|
+
const staticImport = STATIC_IMPORT_RE.exec(script)
|
|
81
|
+
if (staticImport) { specs.push(staticImport[2]); i = STATIC_IMPORT_RE.lastIndex; continue }
|
|
82
|
+
}
|
|
83
|
+
i++
|
|
84
|
+
}
|
|
85
|
+
return specs
|
|
86
|
+
}
|
|
43
87
|
|
|
44
88
|
const isHtmlUrl = (spec: string) => /\.html?([?#]|$)/.test(spec)
|
|
45
89
|
const isExternalUrl = (spec: string) => /^[a-z][a-z0-9+.-]*:/i.test(spec) || spec.startsWith("/")
|
|
@@ -52,11 +96,7 @@ const isExternalUrl = (spec: string) => /^[a-z][a-z0-9+.-]*:/i.test(spec) || spe
|
|
|
52
96
|
const hoistableImports = (source: string, include: RegExp): string[] => {
|
|
53
97
|
const specifiers = new Set<string>()
|
|
54
98
|
for (const [, script] of source.matchAll(SCRIPT_BLOCK_RE)) {
|
|
55
|
-
const
|
|
56
|
-
...[...script.matchAll(IMPORT_LITERAL_RE)].map(match => match[2]),
|
|
57
|
-
...[...script.matchAll(STATIC_IMPORT_LITERAL_RE)].map(match => match[2]),
|
|
58
|
-
]
|
|
59
|
-
for (const spec of specs) {
|
|
99
|
+
for (const spec of importSpecifiers(script)) {
|
|
60
100
|
if (isExternalUrl(spec)) continue
|
|
61
101
|
if (isHtmlUrl(spec) && !include.test(spec)) continue // html left to runtime fetch
|
|
62
102
|
specifiers.add(spec) // a claimed component, a source file or an npm package
|
package/dist/cli.js
CHANGED
|
@@ -46,7 +46,7 @@ var posix = (path) => path.split(sep).join("/");
|
|
|
46
46
|
var isDocument = (req) => req.headers["sec-fetch-dest"] === "document";
|
|
47
47
|
var injectClient = (html) => {
|
|
48
48
|
const tag = `<script src="${CLIENT_URL}"></script>`;
|
|
49
|
-
const open = /<head[^>]
|
|
49
|
+
const open = /<head(\s[^>]*)?>/i.exec(html) ?? /<body(\s[^>]*)?>/i.exec(html);
|
|
50
50
|
if (!open) return tag + html;
|
|
51
51
|
const at = open.index + open[0].length;
|
|
52
52
|
return html.slice(0, at) + tag + html.slice(at);
|
|
@@ -75,7 +75,13 @@ data: ${JSON.stringify(data)}
|
|
|
75
75
|
return;
|
|
76
76
|
}
|
|
77
77
|
try {
|
|
78
|
-
if ((await stat(file)).isDirectory())
|
|
78
|
+
if ((await stat(file)).isDirectory()) {
|
|
79
|
+
if (!pathname.endsWith("/")) {
|
|
80
|
+
res.writeHead(301, { location: pathname + "/" }).end();
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
file = join(file, "index.html");
|
|
84
|
+
}
|
|
79
85
|
const body = await readFile(file);
|
|
80
86
|
const type = CONTENT_TYPES[extname(file).toLowerCase()] ?? "application/octet-stream";
|
|
81
87
|
const html = type.startsWith("text/html") && isDocument(req);
|
package/dist/dev.cjs
CHANGED
|
@@ -68,7 +68,7 @@ var posix = (path) => path.split(import_node_path.sep).join("/");
|
|
|
68
68
|
var isDocument = (req) => req.headers["sec-fetch-dest"] === "document";
|
|
69
69
|
var injectClient = (html) => {
|
|
70
70
|
const tag = `<script src="${CLIENT_URL}"></script>`;
|
|
71
|
-
const open = /<head[^>]
|
|
71
|
+
const open = /<head(\s[^>]*)?>/i.exec(html) ?? /<body(\s[^>]*)?>/i.exec(html);
|
|
72
72
|
if (!open) return tag + html;
|
|
73
73
|
const at = open.index + open[0].length;
|
|
74
74
|
return html.slice(0, at) + tag + html.slice(at);
|
|
@@ -97,7 +97,13 @@ data: ${JSON.stringify(data)}
|
|
|
97
97
|
return;
|
|
98
98
|
}
|
|
99
99
|
try {
|
|
100
|
-
if ((await (0, import_promises.stat)(file)).isDirectory())
|
|
100
|
+
if ((await (0, import_promises.stat)(file)).isDirectory()) {
|
|
101
|
+
if (!pathname.endsWith("/")) {
|
|
102
|
+
res.writeHead(301, { location: pathname + "/" }).end();
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
file = (0, import_node_path.join)(file, "index.html");
|
|
106
|
+
}
|
|
101
107
|
const body = await (0, import_promises.readFile)(file);
|
|
102
108
|
const type = CONTENT_TYPES[(0, import_node_path.extname)(file).toLowerCase()] ?? "application/octet-stream";
|
|
103
109
|
const html = type.startsWith("text/html") && isDocument(req);
|
package/dist/dev.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../dev/dev.ts"],"sourcesContent":["import { createServer, type Server, type IncomingMessage, type ServerResponse } from \"node:http\"\nimport { readFile, realpath, stat } from \"node:fs/promises\"\nimport { watch, type FSWatcher } from \"node:fs\"\nimport { basename, extname, join, relative, resolve, sep } from \"node:path\"\n\n// A dev server for the no-bundle path: serve a directory of .html components\n// over HTTP, watch it, and hot-reload the components that changed.\n//\n// npx jq79 dev // the CLI\n// import { devServer } from \"jq79/dev\" // or from a script\n// await devServer({ rootDir: \".\" })\n//\n// It is a static file server and nothing else - no transforms, no bundling, no\n// module graph. Which is the point: the files it serves are the files you would\n// deploy, so what you develop against and what a static host serves are the same\n// bytes. The one thing it adds is the hot-reload channel, and it adds it to\n// *documents* only (a component fetched by the runtime is served verbatim).\n//\n// The reload is fine-grained. On a change the server pushes the new source down\n// an SSE channel and the runtime swaps it into every live instance of that file,\n// keeping its data - see hotUpdate in jq79.ts. Anything the runtime can't place\n// (a page, a stylesheet, a component nothing has mounted yet) falls back to a\n// full page reload.\n\nexport interface DevServerOptions {\n // the directory to serve, and to watch (default: the current directory)\n rootDir?: string\n // default: 4179, or the first free port after it\n port?: number\n // default: localhost\n host?: string\n}\n\nexport interface DevServer {\n url: string\n port: number\n close: () => Promise<void>\n}\n\nconst CONTENT_TYPES: Record<string, string> = {\n \".html\": \"text/html; charset=utf-8\",\n \".js\": \"text/javascript; charset=utf-8\",\n \".mjs\": \"text/javascript; charset=utf-8\",\n \".json\": \"application/json; charset=utf-8\",\n \".css\": \"text/css; charset=utf-8\",\n \".svg\": \"image/svg+xml\",\n \".png\": \"image/png\",\n \".jpg\": \"image/jpeg\",\n \".jpeg\": \"image/jpeg\",\n \".gif\": \"image/gif\",\n \".webp\": \"image/webp\",\n \".avif\": \"image/avif\",\n \".ico\": \"image/x-icon\",\n \".woff\": \"font/woff\",\n \".woff2\": \"font/woff2\",\n \".map\": \"application/json; charset=utf-8\",\n}\n\nconst CLIENT_URL = \"/__jq79/client.js\"\nconst EVENTS_URL = \"/__jq79/events\"\n\n// Served as a *classic* script, and injected into the <head>: classic scripts\n// run at parse time and module scripts are deferred, so the flag is set before\n// the page's `import ... from \"jq79\"` evaluates - which is what the runtime\n// waits for before it starts tracking instances. The client can't import the\n// runtime itself: the page's copy may come from a CDN or an import map, and a\n// second copy would have a second, empty registry.\nconst CLIENT = `(() => {\n window.__JQ79_HMR_ENABLED__ = true\n\n const events = new EventSource(${JSON.stringify(EVENTS_URL)})\n\n events.addEventListener(\"update\", event => {\n const { url, src } = JSON.parse(event.data)\n const runtime = window.__JQ79_HMR__\n // no runtime (the page doesn't use jq79), or no live instance from this\n // file (it isn't mounted, or it *is* the page) - nothing to swap into\n const patched = runtime ? runtime.update(url, src) : 0\n if (patched) console.log(\"[jq79] hot-updated \" + url + \" (\" + patched + (patched === 1 ? \" instance)\" : \" instances)\"))\n else location.reload()\n })\n\n events.addEventListener(\"reload\", () => location.reload())\n})()`\n\nconst posix = (path: string) => path.split(sep).join(\"/\")\n\nconst isDocument = (req: IncomingMessage) => req.headers[\"sec-fetch-dest\"] === \"document\"\n\n// the client goes in the <head> so it is the first thing the page runs. A file\n// with neither <head> nor <body> is still a document a browser will render, so\n// fall back to the top of it rather than skipping the injection\nconst injectClient = (html: string): string => {\n const tag = `<script src=\"${CLIENT_URL}\"></script>`\n const open = /<head[^>]*>/i.exec(html) ?? /<body[^>]*>/i.exec(html)\n if (!open) return tag + html\n const at = open.index + open[0].length\n return html.slice(0, at) + tag + html.slice(at)\n}\n\nexport const devServer = async (options: DevServerOptions = {}): Promise<DevServer> => {\n // the *real* path: the watcher reports what changed relative to the directory\n // it actually opened, so a root reached through a symlink (/tmp and /var are\n // symlinks on macOS) would hand back paths that don't line up with it\n const root = await realpath(resolve(options.rootDir ?? \".\"))\n const host = options.host ?? \"localhost\"\n\n const clients = new Set<ServerResponse>()\n\n const send = (event: string, data: unknown) => {\n const frame = `event: ${event}\\ndata: ${JSON.stringify(data)}\\n\\n`\n clients.forEach(client => client.write(frame))\n }\n\n // --- serving ---------------------------------------------------------------\n\n const serveStatic = async (req: IncomingMessage, res: ServerResponse, pathname: string) => {\n // a URL path is not a file path: decode it, then keep the result inside the\n // root (\"..\" in a request must not walk out of the served directory)\n let file: string\n try {\n file = resolve(join(root, decodeURIComponent(pathname)))\n } catch {\n res.writeHead(400).end(\"bad request\")\n return\n }\n if (file !== root && !file.startsWith(root + sep)) {\n res.writeHead(403).end(\"forbidden\")\n return\n }\n\n try {\n if ((await stat(file)).isDirectory()) file = join(file, \"index.html\")\n const body = await readFile(file)\n const type = CONTENT_TYPES[extname(file).toLowerCase()] ?? \"application/octet-stream\"\n\n // only a navigation gets the hot-reload client. A component is fetched by\n // the runtime (sec-fetch-dest: empty), and it must arrive as written -\n // injecting a <script> into it would make the runtime parse and run it\n const html = type.startsWith(\"text/html\") && isDocument(req)\n const payload = html ? Buffer.from(injectClient(body.toString(\"utf8\"))) : body\n\n res.writeHead(200, {\n \"content-type\": type,\n \"content-length\": payload.byteLength,\n \"cache-control\": \"no-store\", // the file on disk is always the truth here\n })\n res.end(payload)\n } catch {\n res.writeHead(404, { \"content-type\": \"text/plain; charset=utf-8\" }).end(\"not found\")\n }\n }\n\n const server: Server = createServer((req, res) => {\n const pathname = (req.url ?? \"/\").split(/[?#]/)[0]\n\n if (pathname === CLIENT_URL) {\n res.writeHead(200, { \"content-type\": \"text/javascript; charset=utf-8\", \"cache-control\": \"no-store\" })\n res.end(CLIENT)\n return\n }\n\n if (pathname === EVENTS_URL) {\n res.writeHead(200, {\n \"content-type\": \"text/event-stream\",\n \"cache-control\": \"no-store\",\n connection: \"keep-alive\",\n })\n res.write(\": jq79\\n\\n\") // opens the stream, so the browser fires onopen\n clients.add(res)\n req.on(\"close\", () => clients.delete(res))\n return\n }\n\n void serveStatic(req, res, pathname)\n })\n\n // --- watching --------------------------------------------------------------\n\n const ignored = (rel: string) =>\n rel.split(sep).some(part => part.startsWith(\".\") || part === \"node_modules\")\n\n // one save can arrive as several events (a rename plus a change, an editor's\n // atomic write); collapsing per file keeps that down to one push\n const pending = new Map<string, NodeJS.Timeout>()\n\n const changed = async (rel: string) => {\n const file = join(root, rel)\n\n let src: string | null = null\n try {\n // a directory changes whenever anything inside it does, and the event for\n // the file itself is already on its way - acting on both would reload the\n // page every time a component is saved\n if ((await stat(file)).isDirectory()) return\n if (rel.endsWith(\".html\")) src = await readFile(file, \"utf8\")\n } catch {\n // gone: deleted, or renamed away - and there is nothing to swap in, so the\n // page has to reload. Unless it was never there: macOS reports a change to\n // the watched directory *itself* under its own basename, which resolves to\n // a path inside it that does not exist\n if (rel === basename(root)) return\n }\n\n // the url is the one the component was served from, because that is what the\n // runtime resolves its instances' filenames against\n const url = \"/\" + posix(rel)\n if (src === null) send(\"reload\", { url })\n else send(\"update\", { url, src })\n }\n\n const watcher: FSWatcher = watch(root, { recursive: true }, (_event, filename) => {\n if (!filename) return\n const rel = relative(root, resolve(root, filename.toString()))\n if (!rel || rel.startsWith(\"..\") || ignored(rel)) return\n\n clearTimeout(pending.get(rel))\n pending.set(rel, setTimeout(() => {\n pending.delete(rel)\n void changed(rel)\n }, 30))\n })\n\n // proxies and load balancers cut an idle stream; a comment every 30s is the\n // conventional way to keep it open. unref'd, so it never holds the process up\n const heartbeat = setInterval(() => clients.forEach(client => client.write(\": ping\\n\\n\")), 30_000)\n heartbeat.unref()\n\n // --- go --------------------------------------------------------------------\n\n await new Promise<void>((done, fail) => {\n server.once(\"error\", fail)\n server.listen(options.port ?? 4179, host, done)\n })\n const { port } = server.address() as { port: number }\n\n return {\n url: `http://${host}:${port}`,\n port,\n close: () =>\n new Promise(done => {\n clearInterval(heartbeat)\n watcher.close()\n pending.forEach(clearTimeout)\n clients.forEach(client => client.end())\n clients.clear()\n server.close(() => done())\n }),\n }\n}\n\nexport default devServer\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../dev/dev.ts"],"sourcesContent":["import { createServer, type Server, type IncomingMessage, type ServerResponse } from \"node:http\"\nimport { readFile, realpath, stat } from \"node:fs/promises\"\nimport { watch, type FSWatcher } from \"node:fs\"\nimport { basename, extname, join, relative, resolve, sep } from \"node:path\"\n\n// A dev server for the no-bundle path: serve a directory of .html components\n// over HTTP, watch it, and hot-reload the components that changed.\n//\n// npx jq79 dev // the CLI\n// import { devServer } from \"jq79/dev\" // or from a script\n// await devServer({ rootDir: \".\" })\n//\n// It is a static file server and nothing else - no transforms, no bundling, no\n// module graph. Which is the point: the files it serves are the files you would\n// deploy, so what you develop against and what a static host serves are the same\n// bytes. The one thing it adds is the hot-reload channel, and it adds it to\n// *documents* only (a component fetched by the runtime is served verbatim).\n//\n// The reload is fine-grained. On a change the server pushes the new source down\n// an SSE channel and the runtime swaps it into every live instance of that file,\n// keeping its data - see hotUpdate in jq79.ts. Anything the runtime can't place\n// (a page, a stylesheet, a component nothing has mounted yet) falls back to a\n// full page reload.\n\nexport interface DevServerOptions {\n // the directory to serve, and to watch (default: the current directory)\n rootDir?: string\n // default: 4179, or the first free port after it\n port?: number\n // default: localhost\n host?: string\n}\n\nexport interface DevServer {\n url: string\n port: number\n close: () => Promise<void>\n}\n\nconst CONTENT_TYPES: Record<string, string> = {\n \".html\": \"text/html; charset=utf-8\",\n \".js\": \"text/javascript; charset=utf-8\",\n \".mjs\": \"text/javascript; charset=utf-8\",\n \".json\": \"application/json; charset=utf-8\",\n \".css\": \"text/css; charset=utf-8\",\n \".svg\": \"image/svg+xml\",\n \".png\": \"image/png\",\n \".jpg\": \"image/jpeg\",\n \".jpeg\": \"image/jpeg\",\n \".gif\": \"image/gif\",\n \".webp\": \"image/webp\",\n \".avif\": \"image/avif\",\n \".ico\": \"image/x-icon\",\n \".woff\": \"font/woff\",\n \".woff2\": \"font/woff2\",\n \".map\": \"application/json; charset=utf-8\",\n}\n\nconst CLIENT_URL = \"/__jq79/client.js\"\nconst EVENTS_URL = \"/__jq79/events\"\n\n// Served as a *classic* script, and injected into the <head>: classic scripts\n// run at parse time and module scripts are deferred, so the flag is set before\n// the page's `import ... from \"jq79\"` evaluates - which is what the runtime\n// waits for before it starts tracking instances. The client can't import the\n// runtime itself: the page's copy may come from a CDN or an import map, and a\n// second copy would have a second, empty registry.\nconst CLIENT = `(() => {\n window.__JQ79_HMR_ENABLED__ = true\n\n const events = new EventSource(${JSON.stringify(EVENTS_URL)})\n\n events.addEventListener(\"update\", event => {\n const { url, src } = JSON.parse(event.data)\n const runtime = window.__JQ79_HMR__\n // no runtime (the page doesn't use jq79), or no live instance from this\n // file (it isn't mounted, or it *is* the page) - nothing to swap into\n const patched = runtime ? runtime.update(url, src) : 0\n if (patched) console.log(\"[jq79] hot-updated \" + url + \" (\" + patched + (patched === 1 ? \" instance)\" : \" instances)\"))\n else location.reload()\n })\n\n events.addEventListener(\"reload\", () => location.reload())\n})()`\n\nconst posix = (path: string) => path.split(sep).join(\"/\")\n\nconst isDocument = (req: IncomingMessage) => req.headers[\"sec-fetch-dest\"] === \"document\"\n\n// the client goes in the <head> so it is the first thing the page runs. A file\n// with neither <head> nor <body> is still a document a browser will render, so\n// fall back to the top of it rather than skipping the injection\nconst injectClient = (html: string): string => {\n const tag = `<script src=\"${CLIENT_URL}\"></script>`\n // (\\s[^>]*)? rather than [^>]*, or <header> would pass for <head>\n const open = /<head(\\s[^>]*)?>/i.exec(html) ?? /<body(\\s[^>]*)?>/i.exec(html)\n if (!open) return tag + html\n const at = open.index + open[0].length\n return html.slice(0, at) + tag + html.slice(at)\n}\n\nexport const devServer = async (options: DevServerOptions = {}): Promise<DevServer> => {\n // the *real* path: the watcher reports what changed relative to the directory\n // it actually opened, so a root reached through a symlink (/tmp and /var are\n // symlinks on macOS) would hand back paths that don't line up with it\n const root = await realpath(resolve(options.rootDir ?? \".\"))\n const host = options.host ?? \"localhost\"\n\n const clients = new Set<ServerResponse>()\n\n const send = (event: string, data: unknown) => {\n const frame = `event: ${event}\\ndata: ${JSON.stringify(data)}\\n\\n`\n clients.forEach(client => client.write(frame))\n }\n\n // --- serving ---------------------------------------------------------------\n\n const serveStatic = async (req: IncomingMessage, res: ServerResponse, pathname: string) => {\n // a URL path is not a file path: decode it, then keep the result inside the\n // root (\"..\" in a request must not walk out of the served directory)\n let file: string\n try {\n file = resolve(join(root, decodeURIComponent(pathname)))\n } catch {\n res.writeHead(400).end(\"bad request\")\n return\n }\n if (file !== root && !file.startsWith(root + sep)) {\n res.writeHead(403).end(\"forbidden\")\n return\n }\n\n try {\n if ((await stat(file)).isDirectory()) {\n // a directory is served through its trailing-slash URL, like every\n // static host: /docs rendered as-is would resolve its relative links\n // against the parent (\"img.png\" -> /img.png instead of /docs/img.png)\n if (!pathname.endsWith(\"/\")) {\n res.writeHead(301, { location: pathname + \"/\" }).end()\n return\n }\n file = join(file, \"index.html\")\n }\n const body = await readFile(file)\n const type = CONTENT_TYPES[extname(file).toLowerCase()] ?? \"application/octet-stream\"\n\n // only a navigation gets the hot-reload client. A component is fetched by\n // the runtime (sec-fetch-dest: empty), and it must arrive as written -\n // injecting a <script> into it would make the runtime parse and run it\n const html = type.startsWith(\"text/html\") && isDocument(req)\n const payload = html ? Buffer.from(injectClient(body.toString(\"utf8\"))) : body\n\n res.writeHead(200, {\n \"content-type\": type,\n \"content-length\": payload.byteLength,\n \"cache-control\": \"no-store\", // the file on disk is always the truth here\n })\n res.end(payload)\n } catch {\n res.writeHead(404, { \"content-type\": \"text/plain; charset=utf-8\" }).end(\"not found\")\n }\n }\n\n const server: Server = createServer((req, res) => {\n const pathname = (req.url ?? \"/\").split(/[?#]/)[0]\n\n if (pathname === CLIENT_URL) {\n res.writeHead(200, { \"content-type\": \"text/javascript; charset=utf-8\", \"cache-control\": \"no-store\" })\n res.end(CLIENT)\n return\n }\n\n if (pathname === EVENTS_URL) {\n res.writeHead(200, {\n \"content-type\": \"text/event-stream\",\n \"cache-control\": \"no-store\",\n connection: \"keep-alive\",\n })\n res.write(\": jq79\\n\\n\") // opens the stream, so the browser fires onopen\n clients.add(res)\n req.on(\"close\", () => clients.delete(res))\n return\n }\n\n void serveStatic(req, res, pathname)\n })\n\n // --- watching --------------------------------------------------------------\n\n const ignored = (rel: string) =>\n rel.split(sep).some(part => part.startsWith(\".\") || part === \"node_modules\")\n\n // one save can arrive as several events (a rename plus a change, an editor's\n // atomic write); collapsing per file keeps that down to one push\n const pending = new Map<string, NodeJS.Timeout>()\n\n const changed = async (rel: string) => {\n const file = join(root, rel)\n\n let src: string | null = null\n try {\n // a directory changes whenever anything inside it does, and the event for\n // the file itself is already on its way - acting on both would reload the\n // page every time a component is saved\n if ((await stat(file)).isDirectory()) return\n if (rel.endsWith(\".html\")) src = await readFile(file, \"utf8\")\n } catch {\n // gone: deleted, or renamed away - and there is nothing to swap in, so the\n // page has to reload. Unless it was never there: macOS reports a change to\n // the watched directory *itself* under its own basename, which resolves to\n // a path inside it that does not exist\n if (rel === basename(root)) return\n }\n\n // the url is the one the component was served from, because that is what the\n // runtime resolves its instances' filenames against\n const url = \"/\" + posix(rel)\n if (src === null) send(\"reload\", { url })\n else send(\"update\", { url, src })\n }\n\n const watcher: FSWatcher = watch(root, { recursive: true }, (_event, filename) => {\n if (!filename) return\n const rel = relative(root, resolve(root, filename.toString()))\n if (!rel || rel.startsWith(\"..\") || ignored(rel)) return\n\n clearTimeout(pending.get(rel))\n pending.set(rel, setTimeout(() => {\n pending.delete(rel)\n void changed(rel)\n }, 30))\n })\n\n // proxies and load balancers cut an idle stream; a comment every 30s is the\n // conventional way to keep it open. unref'd, so it never holds the process up\n const heartbeat = setInterval(() => clients.forEach(client => client.write(\": ping\\n\\n\")), 30_000)\n heartbeat.unref()\n\n // --- go --------------------------------------------------------------------\n\n await new Promise<void>((done, fail) => {\n server.once(\"error\", fail)\n server.listen(options.port ?? 4179, host, done)\n })\n const { port } = server.address() as { port: number }\n\n return {\n url: `http://${host}:${port}`,\n port,\n close: () =>\n new Promise(done => {\n clearInterval(heartbeat)\n watcher.close()\n pending.forEach(clearTimeout)\n clients.forEach(client => client.end())\n clients.clear()\n server.close(() => done())\n }),\n }\n}\n\nexport default devServer\n"],"mappings":"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","names":[]}
|
package/dist/dev.js
CHANGED
|
@@ -44,7 +44,7 @@ var posix = (path) => path.split(sep).join("/");
|
|
|
44
44
|
var isDocument = (req) => req.headers["sec-fetch-dest"] === "document";
|
|
45
45
|
var injectClient = (html) => {
|
|
46
46
|
const tag = `<script src="${CLIENT_URL}"></script>`;
|
|
47
|
-
const open = /<head[^>]
|
|
47
|
+
const open = /<head(\s[^>]*)?>/i.exec(html) ?? /<body(\s[^>]*)?>/i.exec(html);
|
|
48
48
|
if (!open) return tag + html;
|
|
49
49
|
const at = open.index + open[0].length;
|
|
50
50
|
return html.slice(0, at) + tag + html.slice(at);
|
|
@@ -73,7 +73,13 @@ data: ${JSON.stringify(data)}
|
|
|
73
73
|
return;
|
|
74
74
|
}
|
|
75
75
|
try {
|
|
76
|
-
if ((await stat(file)).isDirectory())
|
|
76
|
+
if ((await stat(file)).isDirectory()) {
|
|
77
|
+
if (!pathname.endsWith("/")) {
|
|
78
|
+
res.writeHead(301, { location: pathname + "/" }).end();
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
file = join(file, "index.html");
|
|
82
|
+
}
|
|
77
83
|
const body = await readFile(file);
|
|
78
84
|
const type = CONTENT_TYPES[extname(file).toLowerCase()] ?? "application/octet-stream";
|
|
79
85
|
const html = type.startsWith("text/html") && isDocument(req);
|
package/dist/dev.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../dev/dev.ts"],"sourcesContent":["import { createServer, type Server, type IncomingMessage, type ServerResponse } from \"node:http\"\nimport { readFile, realpath, stat } from \"node:fs/promises\"\nimport { watch, type FSWatcher } from \"node:fs\"\nimport { basename, extname, join, relative, resolve, sep } from \"node:path\"\n\n// A dev server for the no-bundle path: serve a directory of .html components\n// over HTTP, watch it, and hot-reload the components that changed.\n//\n// npx jq79 dev // the CLI\n// import { devServer } from \"jq79/dev\" // or from a script\n// await devServer({ rootDir: \".\" })\n//\n// It is a static file server and nothing else - no transforms, no bundling, no\n// module graph. Which is the point: the files it serves are the files you would\n// deploy, so what you develop against and what a static host serves are the same\n// bytes. The one thing it adds is the hot-reload channel, and it adds it to\n// *documents* only (a component fetched by the runtime is served verbatim).\n//\n// The reload is fine-grained. On a change the server pushes the new source down\n// an SSE channel and the runtime swaps it into every live instance of that file,\n// keeping its data - see hotUpdate in jq79.ts. Anything the runtime can't place\n// (a page, a stylesheet, a component nothing has mounted yet) falls back to a\n// full page reload.\n\nexport interface DevServerOptions {\n // the directory to serve, and to watch (default: the current directory)\n rootDir?: string\n // default: 4179, or the first free port after it\n port?: number\n // default: localhost\n host?: string\n}\n\nexport interface DevServer {\n url: string\n port: number\n close: () => Promise<void>\n}\n\nconst CONTENT_TYPES: Record<string, string> = {\n \".html\": \"text/html; charset=utf-8\",\n \".js\": \"text/javascript; charset=utf-8\",\n \".mjs\": \"text/javascript; charset=utf-8\",\n \".json\": \"application/json; charset=utf-8\",\n \".css\": \"text/css; charset=utf-8\",\n \".svg\": \"image/svg+xml\",\n \".png\": \"image/png\",\n \".jpg\": \"image/jpeg\",\n \".jpeg\": \"image/jpeg\",\n \".gif\": \"image/gif\",\n \".webp\": \"image/webp\",\n \".avif\": \"image/avif\",\n \".ico\": \"image/x-icon\",\n \".woff\": \"font/woff\",\n \".woff2\": \"font/woff2\",\n \".map\": \"application/json; charset=utf-8\",\n}\n\nconst CLIENT_URL = \"/__jq79/client.js\"\nconst EVENTS_URL = \"/__jq79/events\"\n\n// Served as a *classic* script, and injected into the <head>: classic scripts\n// run at parse time and module scripts are deferred, so the flag is set before\n// the page's `import ... from \"jq79\"` evaluates - which is what the runtime\n// waits for before it starts tracking instances. The client can't import the\n// runtime itself: the page's copy may come from a CDN or an import map, and a\n// second copy would have a second, empty registry.\nconst CLIENT = `(() => {\n window.__JQ79_HMR_ENABLED__ = true\n\n const events = new EventSource(${JSON.stringify(EVENTS_URL)})\n\n events.addEventListener(\"update\", event => {\n const { url, src } = JSON.parse(event.data)\n const runtime = window.__JQ79_HMR__\n // no runtime (the page doesn't use jq79), or no live instance from this\n // file (it isn't mounted, or it *is* the page) - nothing to swap into\n const patched = runtime ? runtime.update(url, src) : 0\n if (patched) console.log(\"[jq79] hot-updated \" + url + \" (\" + patched + (patched === 1 ? \" instance)\" : \" instances)\"))\n else location.reload()\n })\n\n events.addEventListener(\"reload\", () => location.reload())\n})()`\n\nconst posix = (path: string) => path.split(sep).join(\"/\")\n\nconst isDocument = (req: IncomingMessage) => req.headers[\"sec-fetch-dest\"] === \"document\"\n\n// the client goes in the <head> so it is the first thing the page runs. A file\n// with neither <head> nor <body> is still a document a browser will render, so\n// fall back to the top of it rather than skipping the injection\nconst injectClient = (html: string): string => {\n const tag = `<script src=\"${CLIENT_URL}\"></script>`\n const open = /<head[^>]*>/i.exec(html) ?? /<body[^>]*>/i.exec(html)\n if (!open) return tag + html\n const at = open.index + open[0].length\n return html.slice(0, at) + tag + html.slice(at)\n}\n\nexport const devServer = async (options: DevServerOptions = {}): Promise<DevServer> => {\n // the *real* path: the watcher reports what changed relative to the directory\n // it actually opened, so a root reached through a symlink (/tmp and /var are\n // symlinks on macOS) would hand back paths that don't line up with it\n const root = await realpath(resolve(options.rootDir ?? \".\"))\n const host = options.host ?? \"localhost\"\n\n const clients = new Set<ServerResponse>()\n\n const send = (event: string, data: unknown) => {\n const frame = `event: ${event}\\ndata: ${JSON.stringify(data)}\\n\\n`\n clients.forEach(client => client.write(frame))\n }\n\n // --- serving ---------------------------------------------------------------\n\n const serveStatic = async (req: IncomingMessage, res: ServerResponse, pathname: string) => {\n // a URL path is not a file path: decode it, then keep the result inside the\n // root (\"..\" in a request must not walk out of the served directory)\n let file: string\n try {\n file = resolve(join(root, decodeURIComponent(pathname)))\n } catch {\n res.writeHead(400).end(\"bad request\")\n return\n }\n if (file !== root && !file.startsWith(root + sep)) {\n res.writeHead(403).end(\"forbidden\")\n return\n }\n\n try {\n if ((await stat(file)).isDirectory()) file = join(file, \"index.html\")\n const body = await readFile(file)\n const type = CONTENT_TYPES[extname(file).toLowerCase()] ?? \"application/octet-stream\"\n\n // only a navigation gets the hot-reload client. A component is fetched by\n // the runtime (sec-fetch-dest: empty), and it must arrive as written -\n // injecting a <script> into it would make the runtime parse and run it\n const html = type.startsWith(\"text/html\") && isDocument(req)\n const payload = html ? Buffer.from(injectClient(body.toString(\"utf8\"))) : body\n\n res.writeHead(200, {\n \"content-type\": type,\n \"content-length\": payload.byteLength,\n \"cache-control\": \"no-store\", // the file on disk is always the truth here\n })\n res.end(payload)\n } catch {\n res.writeHead(404, { \"content-type\": \"text/plain; charset=utf-8\" }).end(\"not found\")\n }\n }\n\n const server: Server = createServer((req, res) => {\n const pathname = (req.url ?? \"/\").split(/[?#]/)[0]\n\n if (pathname === CLIENT_URL) {\n res.writeHead(200, { \"content-type\": \"text/javascript; charset=utf-8\", \"cache-control\": \"no-store\" })\n res.end(CLIENT)\n return\n }\n\n if (pathname === EVENTS_URL) {\n res.writeHead(200, {\n \"content-type\": \"text/event-stream\",\n \"cache-control\": \"no-store\",\n connection: \"keep-alive\",\n })\n res.write(\": jq79\\n\\n\") // opens the stream, so the browser fires onopen\n clients.add(res)\n req.on(\"close\", () => clients.delete(res))\n return\n }\n\n void serveStatic(req, res, pathname)\n })\n\n // --- watching --------------------------------------------------------------\n\n const ignored = (rel: string) =>\n rel.split(sep).some(part => part.startsWith(\".\") || part === \"node_modules\")\n\n // one save can arrive as several events (a rename plus a change, an editor's\n // atomic write); collapsing per file keeps that down to one push\n const pending = new Map<string, NodeJS.Timeout>()\n\n const changed = async (rel: string) => {\n const file = join(root, rel)\n\n let src: string | null = null\n try {\n // a directory changes whenever anything inside it does, and the event for\n // the file itself is already on its way - acting on both would reload the\n // page every time a component is saved\n if ((await stat(file)).isDirectory()) return\n if (rel.endsWith(\".html\")) src = await readFile(file, \"utf8\")\n } catch {\n // gone: deleted, or renamed away - and there is nothing to swap in, so the\n // page has to reload. Unless it was never there: macOS reports a change to\n // the watched directory *itself* under its own basename, which resolves to\n // a path inside it that does not exist\n if (rel === basename(root)) return\n }\n\n // the url is the one the component was served from, because that is what the\n // runtime resolves its instances' filenames against\n const url = \"/\" + posix(rel)\n if (src === null) send(\"reload\", { url })\n else send(\"update\", { url, src })\n }\n\n const watcher: FSWatcher = watch(root, { recursive: true }, (_event, filename) => {\n if (!filename) return\n const rel = relative(root, resolve(root, filename.toString()))\n if (!rel || rel.startsWith(\"..\") || ignored(rel)) return\n\n clearTimeout(pending.get(rel))\n pending.set(rel, setTimeout(() => {\n pending.delete(rel)\n void changed(rel)\n }, 30))\n })\n\n // proxies and load balancers cut an idle stream; a comment every 30s is the\n // conventional way to keep it open. unref'd, so it never holds the process up\n const heartbeat = setInterval(() => clients.forEach(client => client.write(\": ping\\n\\n\")), 30_000)\n heartbeat.unref()\n\n // --- go --------------------------------------------------------------------\n\n await new Promise<void>((done, fail) => {\n server.once(\"error\", fail)\n server.listen(options.port ?? 4179, host, done)\n })\n const { port } = server.address() as { port: number }\n\n return {\n url: `http://${host}:${port}`,\n port,\n close: () =>\n new Promise(done => {\n clearInterval(heartbeat)\n watcher.close()\n pending.forEach(clearTimeout)\n clients.forEach(client => client.end())\n clients.clear()\n server.close(() => done())\n }),\n }\n}\n\nexport default devServer\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../dev/dev.ts"],"sourcesContent":["import { createServer, type Server, type IncomingMessage, type ServerResponse } from \"node:http\"\nimport { readFile, realpath, stat } from \"node:fs/promises\"\nimport { watch, type FSWatcher } from \"node:fs\"\nimport { basename, extname, join, relative, resolve, sep } from \"node:path\"\n\n// A dev server for the no-bundle path: serve a directory of .html components\n// over HTTP, watch it, and hot-reload the components that changed.\n//\n// npx jq79 dev // the CLI\n// import { devServer } from \"jq79/dev\" // or from a script\n// await devServer({ rootDir: \".\" })\n//\n// It is a static file server and nothing else - no transforms, no bundling, no\n// module graph. Which is the point: the files it serves are the files you would\n// deploy, so what you develop against and what a static host serves are the same\n// bytes. The one thing it adds is the hot-reload channel, and it adds it to\n// *documents* only (a component fetched by the runtime is served verbatim).\n//\n// The reload is fine-grained. On a change the server pushes the new source down\n// an SSE channel and the runtime swaps it into every live instance of that file,\n// keeping its data - see hotUpdate in jq79.ts. Anything the runtime can't place\n// (a page, a stylesheet, a component nothing has mounted yet) falls back to a\n// full page reload.\n\nexport interface DevServerOptions {\n // the directory to serve, and to watch (default: the current directory)\n rootDir?: string\n // default: 4179, or the first free port after it\n port?: number\n // default: localhost\n host?: string\n}\n\nexport interface DevServer {\n url: string\n port: number\n close: () => Promise<void>\n}\n\nconst CONTENT_TYPES: Record<string, string> = {\n \".html\": \"text/html; charset=utf-8\",\n \".js\": \"text/javascript; charset=utf-8\",\n \".mjs\": \"text/javascript; charset=utf-8\",\n \".json\": \"application/json; charset=utf-8\",\n \".css\": \"text/css; charset=utf-8\",\n \".svg\": \"image/svg+xml\",\n \".png\": \"image/png\",\n \".jpg\": \"image/jpeg\",\n \".jpeg\": \"image/jpeg\",\n \".gif\": \"image/gif\",\n \".webp\": \"image/webp\",\n \".avif\": \"image/avif\",\n \".ico\": \"image/x-icon\",\n \".woff\": \"font/woff\",\n \".woff2\": \"font/woff2\",\n \".map\": \"application/json; charset=utf-8\",\n}\n\nconst CLIENT_URL = \"/__jq79/client.js\"\nconst EVENTS_URL = \"/__jq79/events\"\n\n// Served as a *classic* script, and injected into the <head>: classic scripts\n// run at parse time and module scripts are deferred, so the flag is set before\n// the page's `import ... from \"jq79\"` evaluates - which is what the runtime\n// waits for before it starts tracking instances. The client can't import the\n// runtime itself: the page's copy may come from a CDN or an import map, and a\n// second copy would have a second, empty registry.\nconst CLIENT = `(() => {\n window.__JQ79_HMR_ENABLED__ = true\n\n const events = new EventSource(${JSON.stringify(EVENTS_URL)})\n\n events.addEventListener(\"update\", event => {\n const { url, src } = JSON.parse(event.data)\n const runtime = window.__JQ79_HMR__\n // no runtime (the page doesn't use jq79), or no live instance from this\n // file (it isn't mounted, or it *is* the page) - nothing to swap into\n const patched = runtime ? runtime.update(url, src) : 0\n if (patched) console.log(\"[jq79] hot-updated \" + url + \" (\" + patched + (patched === 1 ? \" instance)\" : \" instances)\"))\n else location.reload()\n })\n\n events.addEventListener(\"reload\", () => location.reload())\n})()`\n\nconst posix = (path: string) => path.split(sep).join(\"/\")\n\nconst isDocument = (req: IncomingMessage) => req.headers[\"sec-fetch-dest\"] === \"document\"\n\n// the client goes in the <head> so it is the first thing the page runs. A file\n// with neither <head> nor <body> is still a document a browser will render, so\n// fall back to the top of it rather than skipping the injection\nconst injectClient = (html: string): string => {\n const tag = `<script src=\"${CLIENT_URL}\"></script>`\n // (\\s[^>]*)? rather than [^>]*, or <header> would pass for <head>\n const open = /<head(\\s[^>]*)?>/i.exec(html) ?? /<body(\\s[^>]*)?>/i.exec(html)\n if (!open) return tag + html\n const at = open.index + open[0].length\n return html.slice(0, at) + tag + html.slice(at)\n}\n\nexport const devServer = async (options: DevServerOptions = {}): Promise<DevServer> => {\n // the *real* path: the watcher reports what changed relative to the directory\n // it actually opened, so a root reached through a symlink (/tmp and /var are\n // symlinks on macOS) would hand back paths that don't line up with it\n const root = await realpath(resolve(options.rootDir ?? \".\"))\n const host = options.host ?? \"localhost\"\n\n const clients = new Set<ServerResponse>()\n\n const send = (event: string, data: unknown) => {\n const frame = `event: ${event}\\ndata: ${JSON.stringify(data)}\\n\\n`\n clients.forEach(client => client.write(frame))\n }\n\n // --- serving ---------------------------------------------------------------\n\n const serveStatic = async (req: IncomingMessage, res: ServerResponse, pathname: string) => {\n // a URL path is not a file path: decode it, then keep the result inside the\n // root (\"..\" in a request must not walk out of the served directory)\n let file: string\n try {\n file = resolve(join(root, decodeURIComponent(pathname)))\n } catch {\n res.writeHead(400).end(\"bad request\")\n return\n }\n if (file !== root && !file.startsWith(root + sep)) {\n res.writeHead(403).end(\"forbidden\")\n return\n }\n\n try {\n if ((await stat(file)).isDirectory()) {\n // a directory is served through its trailing-slash URL, like every\n // static host: /docs rendered as-is would resolve its relative links\n // against the parent (\"img.png\" -> /img.png instead of /docs/img.png)\n if (!pathname.endsWith(\"/\")) {\n res.writeHead(301, { location: pathname + \"/\" }).end()\n return\n }\n file = join(file, \"index.html\")\n }\n const body = await readFile(file)\n const type = CONTENT_TYPES[extname(file).toLowerCase()] ?? \"application/octet-stream\"\n\n // only a navigation gets the hot-reload client. A component is fetched by\n // the runtime (sec-fetch-dest: empty), and it must arrive as written -\n // injecting a <script> into it would make the runtime parse and run it\n const html = type.startsWith(\"text/html\") && isDocument(req)\n const payload = html ? Buffer.from(injectClient(body.toString(\"utf8\"))) : body\n\n res.writeHead(200, {\n \"content-type\": type,\n \"content-length\": payload.byteLength,\n \"cache-control\": \"no-store\", // the file on disk is always the truth here\n })\n res.end(payload)\n } catch {\n res.writeHead(404, { \"content-type\": \"text/plain; charset=utf-8\" }).end(\"not found\")\n }\n }\n\n const server: Server = createServer((req, res) => {\n const pathname = (req.url ?? \"/\").split(/[?#]/)[0]\n\n if (pathname === CLIENT_URL) {\n res.writeHead(200, { \"content-type\": \"text/javascript; charset=utf-8\", \"cache-control\": \"no-store\" })\n res.end(CLIENT)\n return\n }\n\n if (pathname === EVENTS_URL) {\n res.writeHead(200, {\n \"content-type\": \"text/event-stream\",\n \"cache-control\": \"no-store\",\n connection: \"keep-alive\",\n })\n res.write(\": jq79\\n\\n\") // opens the stream, so the browser fires onopen\n clients.add(res)\n req.on(\"close\", () => clients.delete(res))\n return\n }\n\n void serveStatic(req, res, pathname)\n })\n\n // --- watching --------------------------------------------------------------\n\n const ignored = (rel: string) =>\n rel.split(sep).some(part => part.startsWith(\".\") || part === \"node_modules\")\n\n // one save can arrive as several events (a rename plus a change, an editor's\n // atomic write); collapsing per file keeps that down to one push\n const pending = new Map<string, NodeJS.Timeout>()\n\n const changed = async (rel: string) => {\n const file = join(root, rel)\n\n let src: string | null = null\n try {\n // a directory changes whenever anything inside it does, and the event for\n // the file itself is already on its way - acting on both would reload the\n // page every time a component is saved\n if ((await stat(file)).isDirectory()) return\n if (rel.endsWith(\".html\")) src = await readFile(file, \"utf8\")\n } catch {\n // gone: deleted, or renamed away - and there is nothing to swap in, so the\n // page has to reload. Unless it was never there: macOS reports a change to\n // the watched directory *itself* under its own basename, which resolves to\n // a path inside it that does not exist\n if (rel === basename(root)) return\n }\n\n // the url is the one the component was served from, because that is what the\n // runtime resolves its instances' filenames against\n const url = \"/\" + posix(rel)\n if (src === null) send(\"reload\", { url })\n else send(\"update\", { url, src })\n }\n\n const watcher: FSWatcher = watch(root, { recursive: true }, (_event, filename) => {\n if (!filename) return\n const rel = relative(root, resolve(root, filename.toString()))\n if (!rel || rel.startsWith(\"..\") || ignored(rel)) return\n\n clearTimeout(pending.get(rel))\n pending.set(rel, setTimeout(() => {\n pending.delete(rel)\n void changed(rel)\n }, 30))\n })\n\n // proxies and load balancers cut an idle stream; a comment every 30s is the\n // conventional way to keep it open. unref'd, so it never holds the process up\n const heartbeat = setInterval(() => clients.forEach(client => client.write(\": ping\\n\\n\")), 30_000)\n heartbeat.unref()\n\n // --- go --------------------------------------------------------------------\n\n await new Promise<void>((done, fail) => {\n server.once(\"error\", fail)\n server.listen(options.port ?? 4179, host, done)\n })\n const { port } = server.address() as { port: number }\n\n return {\n url: `http://${host}:${port}`,\n port,\n close: () =>\n new Promise(done => {\n clearInterval(heartbeat)\n watcher.close()\n pending.forEach(clearTimeout)\n clients.forEach(client => client.end())\n clients.clear()\n server.close(() => done())\n }),\n }\n}\n\nexport default devServer\n"],"mappings":"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","names":[]}
|
package/dist/jq79.cjs
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
var
|
|
2
|
-
`,t);return n===-1?e.length:n},A=(e,t)=>{let n=e.indexOf("*/",t+2);return n===-1?e.length:n+2},
|
|
3
|
-
`)
|
|
4
|
-
|
|
5
|
-
`||
|
|
6
|
-
`||
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
var z=Object.defineProperty;var Ue=Object.getOwnPropertyDescriptor;var qe=Object.getOwnPropertyNames;var He=Object.prototype.hasOwnProperty;var ze=(e,t,n)=>t in e?z(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;var Ge=(e,t)=>{for(var n in t)z(e,n,{get:t[n],enumerable:!0})},Ke=(e,t,n,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of qe(t))!He.call(e,r)&&r!==n&&z(e,r,{get:()=>t[r],enumerable:!(s=Ue(t,r))||s.enumerable});return e};var Ze=e=>Ke(z({},"__esModule",{value:!0}),e);var T=(e,t,n)=>ze(e,typeof t!="symbol"?t+"":t,n);var Ft={};Ge(Ft,{$:()=>I,$$:()=>G,$create:()=>K,$reactive:()=>q,Component79:()=>F,enableHotReload:()=>We,hotUpdate:()=>Ie,parseComponent:()=>kt,renderComponent:()=>wt});module.exports=Ze(Ft);function I(e,t){return typeof e=="string"?document.querySelector(e):e.querySelector(t)}function G(e,t){return Array.from(typeof e=="string"?document.querySelectorAll(e):e.querySelectorAll(t))}var K=(e,t={})=>{let n=document.createElement(e);for(let[s,r]of Object.entries(t))if(s==="className")n.className=Array.isArray(r)?r.join(" "):r;else if(s==="textContent")n.textContent=r;else if(s==="children")for(let o of r)n.appendChild(o);else n.setAttribute(s,r);return n},Je=new Set(["a","b","i","em","strong","p","br","ul","ol","li","blockquote","code","pre","span","div","h1","h2","h3","h4","h5","h6","img"]),ue={a:new Set(["href","title"]),img:new Set(["src","alt"]),"*":new Set(["class"])},Ve=new Set(["http:","https:","mailto:"]);function Xe(e){try{let t=new URL(e,"https://example.com");return Ve.has(t.protocol)}catch{return!1}}function de(e,t){for(let n of Array.from(e.childNodes))if(n.nodeType===Node.ELEMENT_NODE){let s=Qe(n);s&&t.appendChild(s)}else n.nodeType===Node.TEXT_NODE&&t.appendChild(n.cloneNode())}function Qe(e){let t=e.tagName.toLowerCase();if(!Je.has(t))return null;let n=document.createElement(t);for(let s of Array.from(e.attributes)){let r=s.name.toLowerCase(),o=ue[t]?.has(r),i=ue["*"]?.has(r);!o&&!i||(r==="href"||r==="src")&&!Xe(s.value)||n.setAttribute(r,s.value)}return t==="a"&&n.setAttribute("rel","noopener noreferrer"),de(e,n),n}function pe(e){let t=new DOMParser().parseFromString(e,"text/html"),n=document.createElement("div");return de(t.body,n),n.innerHTML}var Ye=(e,t)=>t.split(".").reduce((n,s)=>n?.[s],e),he=e=>{if(Array.isArray(e))return!0;let t=Object.getPrototypeOf(e);return t===Object.prototype||t===null},me=(e,t,n)=>{Object.entries(e).forEach(([s,r])=>{let o=t?`${t}.${s}`:s;r&&typeof r=="object"&&he(r)?me(r,o,n):n(o,r)})},et=(e,t)=>e===t||e.startsWith(`${t}.`)||t.startsWith(`${e}.`),ee=Symbol("jq79.raw"),Z=e=>{let t=e;for(;t!==null&&typeof t=="object"&&t[ee];)t=t[ee];return t},ge=Symbol("jq79.store"),U=e=>e!==null&&typeof e=="object"&&e[ge]===!0,W=[],te=e=>{W.push(new Set);try{return e()}finally{W.pop()}},q=e=>{let t=new Map,n=new Set,s=new Set,r=new WeakMap,o=Object.create(null),i=(a,u,m=!1)=>{t.get(a)?.forEach(S=>S(u,a)),n.forEach(S=>S(a,u)),s.forEach(S=>{(m||Array.from(S.deps).some(x=>et(x,a)))&&S.run()})},c=a=>a!==null&&typeof a=="object"&&he(a),l=new Map,d=(a,u)=>{let m=l.get(u);m?.store!==a&&(m?.unsubscribe(),l.set(u,{store:a,unsubscribe:a.$onAny((S,x)=>i(`${u}.${S}`,x))}))},f=a=>{l.get(a)?.unsubscribe(),l.delete(a)},p=(a,u)=>{let m=r.get(a);if(m)return m;let S=null,x=new Proxy(a,{has(R,g){return Reflect.has(R,g)||typeof g=="string"&&S?.has(g)===!0},get(R,g,b){if(g===ee)return R;if(g===ge)return u==="";if(typeof g!="string")return Reflect.get(R,g,b);if(u===""&&g in o)return o[g];let w=u?`${u}.${g}`:g;W[W.length-1]?.add(w);let C=Reflect.get(R,g,b);if(U(C))return d(C,w),C;let $=Z(C);return c($)?p($,w):$},set(R,g,b,w){if(w!==x&&!Object.prototype.hasOwnProperty.call(R,g))return Reflect.set(R,g,b,w);let C=u?`${u}.${g}`:g,$=U(b)?b:Z(b),P=!Object.prototype.hasOwnProperty.call(R,g);if(!P&&Object.is(R[g],$)&&($===null||typeof $!="object"))return!0;R[g]=$,S?.delete(g),U($)?d($,C):f(C);let _=U($)||!c($)?$:p($,C);return i(C,_,P),!0},deleteProperty(R,g){if(typeof g!="string")return Reflect.deleteProperty(R,g);let b=Object.prototype.hasOwnProperty.call(R,g),w=Reflect.deleteProperty(R,g);if(w&&b){let C=u?`${u}.${g}`:g;(S??(S=new Set)).add(g),f(C),i(C,void 0)}return w}});return r.set(a,x),x},h=p(Z(e),"");Object.entries(Z(e)).forEach(([a,u])=>{U(u)&&d(u,a)});let y=(a,u,{immediate:m=!1}={})=>(t.has(a)||t.set(a,new Set),t.get(a).add(u),m&&u(Ye(h,a),a),()=>t.get(a)?.delete(u)),E=(a,{immediate:u=!1}={})=>(n.add(a),u&&me(h,"",(m,S)=>a(m,S)),()=>n.delete(a)),v=a=>{let u=!1,m=!1,S={deps:new Set,run:()=>{if(u){m=!0;return}u=!0;try{let x=0;do{m=!1;let R=new Set;W.push(R);try{a()}finally{W.pop(),S.deps=R}}while(m&&++x<100);m&&console.error("jq79: an effect re-woke itself 100 times in a row (it writes what it reads); giving up on it settling")}finally{u=!1}}};return s.add(S),S.run(),()=>{s.delete(S)}},L=()=>{l.forEach(({unsubscribe:a})=>a()),l.clear()};return o.$on=y,o.$onAny=E,o.$effect=v,o.$dispose=L,h},B=e=>{let t=[],n=[];return{effect:s=>{t.push(e.$effect(s)),n.push(s)},onDispose:s=>{t.push(s)},refresh:()=>{n.forEach(s=>s())},dispose:()=>{t.splice(0).forEach(s=>s()),n.length=0}}};var ye=/(?:let|var|const)\s+([A-Za-z_$][\w$]*)/y,Ee=/\$:\s*/y,V=/import(?=\s*\()/y,be=/\$:\s*([A-Za-z_$][\w$]*)\s*=(?!=)/y,j=(e,t)=>{let n=e[t],s=t+1;for(;s<e.length;){if(e[s]==="\\"){s+=2;continue}if(e[s]===n)return s+1;s++}return e.length},O=(e,t)=>{let n=e.indexOf(`
|
|
2
|
+
`,t);return n===-1?e.length:n},A=(e,t)=>{let n=e.indexOf("*/",t+2);return n===-1?e.length:n+2},J=(e,t)=>{let n=t;for(;n<e.length;){if(/\s/.test(e[n])){n++;continue}if(e[n]==="/"&&e[n+1]==="/"){n=O(e,n);continue}if(e[n]==="/"&&e[n+1]==="*"){n=A(e,n);continue}break}return n},tt=new Set(["return","typeof","case","in","instanceof","new","delete","void","do","else","yield","await"]),M=(e,t)=>{let n=t-1;for(;n>=0;){let r=e[n];if(/\s/.test(r)){n--;continue}if(r==="/"&&e[n-1]==="*"){let o=e.lastIndexOf("/*",n-2);if(o===-1)return!0;n=o-1;continue}break}if(n<0)return!0;let s=e[n];if(/[\w$]/.test(s)){let r=n;for(;r>0&&/[\w$]/.test(e[r-1]);)r--;return tt.has(e.slice(r,n+1))}return(s==="+"||s==="-")&&e[n-1]===s?!1:!")]}\"'`.".includes(s)},D=(e,t)=>{let n=t+1,s=!1;for(;n<e.length;){let r=e[n];if(r==="\\"){n+=2;continue}if(r===`
|
|
3
|
+
`)return n;if(r==="[")s=!0;else if(r==="]")s=!1;else if(r==="/"&&!s){for(n++;n<e.length&&/[a-z]/i.test(e[n]);)n++;return n}n++}return e.length},nt=/^(\?\.|\?\?|&&|\|\||\*\*|[.,+\-*/%&|^<>=?:([])/,st=(e,t)=>{let n=0,s=t;for(;s<e.length;){let r=e[s];if(r==="'"||r==='"'||r==="`"){s=j(e,s);continue}if(r==="/"&&e[s+1]==="/"){s=O(e,s);continue}if(r==="/"&&e[s+1]==="*"){s=A(e,s);continue}if(r==="/"&&M(e,s)){s=D(e,s);continue}if("([{".includes(r))n++;else if(")]}".includes(r))n--;else{if(n<=0&&r===";")return s;if(n<=0&&r===`
|
|
4
|
+
`){let o=J(e,s+1);if(o>=e.length||!nt.test(e.slice(o,o+2)))return s;s=o;continue}}s++}return e.length},ne=e=>{let t=[],n="",s=0,r=0,o=!0;for(;s<e.length;){let i=e[s],c=e[s+1];if(i==="'"||i==='"'||i==="`"){let l=j(e,s);n+=e.slice(s,l),s=l,o=!1;continue}if(i==="/"&&(c==="/"||c==="*")){let l=c==="/"?O(e,s):A(e,s);n+=e.slice(s,l),s=l;continue}if(i==="/"&&M(e,s)){let l=D(e,s);n+=e.slice(s,l),s=l,o=!1;continue}if(i==="i"&&(s===0||!/[\w$.]/.test(e[s-1]))&&(V.lastIndex=s,V.test(e))){n+="$__import",s+=6,o=!1;continue}if(r===0&&o){ye.lastIndex=s;let l=ye.exec(e);if(l){t.push(l[1]),n+=l[1],s+=l[0].length,o=!1;continue}Ee.lastIndex=s;let d=Ee.exec(e);if(d){be.lastIndex=s;let f=be.exec(e);f&&t.push(f[1]);let p=s+d[0].length,h=st(e,p);n+=`$__effect(() => { ${ne(e.slice(p,h)).code} });`,s=h;continue}}"([{".includes(i)?r++:")]}".includes(i)&&(r=Math.max(0,r-1)),i===`
|
|
5
|
+
`||i===";"||i==="}"?o=!0:/\s/.test(i)||(o=!1),n+=i,s++}return{vars:t,code:n}},X=/export\s+default(?![\w$])/y,Se=/import\s*(?:([\w$\s,{}*]+?)\s*from\s*)?(["'])([^"'\n]+)\2/y,rt=e=>{let t=[],n=0,s=0;for(let r=0;r<=e.length;r++){let o=e[r];if(o==="{")n++;else if(o==="}")n--;else if(r===e.length||o===","&&n===0){let i=e.slice(s,r).trim();i&&t.push(i),s=r+1}}return t},ot=(e,t,n)=>{let s=`await $__import(${JSON.stringify(t)})`;if(e===void 0)return s;let r=rt(e),o=[],i=s;if(r.length>1){let c=`$__mod${n}`;o.push(`${c} = ${s}`),i=c}for(let c of r)c.startsWith("{")?o.push(`${c.replace(/\s+as\s+/g,": ")} = ${i}`):c.startsWith("*")?o.push(`${c.replace(/^\*\s*as\s+/,"")} = ${i}`):o.push(`${c} = $__default(${i})`);return`const ${o.join(", ")}`},it=/^[A-Za-z_$][\w$]*$/,we=(e,t)=>{let n=0,s=0;for(;s<e.length;){let r=e[s];if(r==="'"||r==='"'||r==="`"){s=j(e,s);continue}if(r==="/"&&e[s+1]==="/"){s=O(e,s);continue}if(r==="/"&&e[s+1]==="*"){s=A(e,s);continue}if(r==="/"&&r!==t&&M(e,s)){s=D(e,s);continue}if("([{".includes(r))n++;else if(")]}".includes(r))n--;else if(n===0&&r===t)return s;s++}return-1},Re=e=>{let t=[],n=0,s=0,r=0;for(;r<=e.length;){let o=e[r];if(o==="'"||o==='"'||o==="`"){r=j(e,r);continue}if(o==="/"&&e[r+1]==="/"){r=O(e,r);continue}if(o==="/"&&e[r+1]==="*"){r=A(e,r);continue}if(o==="/"&&M(e,r)){r=D(e,r);continue}if(o!==void 0&&"([{".includes(o))n++;else if(o!==void 0&&")]}".includes(o))n--;else if(r===e.length||o===","&&n===0){let i=e.slice(s,r).trim();i&&t.push(i),s=r+1}r++}return t},ct=e=>{let t=0;for(;t<e.length;){let n=we(e.slice(t),"=");if(n===-1)return-1;let s=t+n;if(e[s+1]!=="="&&e[s+1]!==">"&&e[s-1]!=="="&&e[s-1]!=="!")return s;t=s+1}return-1},se=e=>{let t=(e??"").trim();if(!t.startsWith("{"))return null;let n=0,s=0;for(;s<t.length;){let o=t[s];if(o==="'"||o==='"'||o==="`"){s=j(t,s);continue}if(o==="/"&&t[s+1]==="/"){s=O(t,s);continue}if(o==="/"&&t[s+1]==="*"){s=A(t,s);continue}if(o==="/"&&M(t,s)){s=D(t,s);continue}if("([{".includes(o))n++;else if(")]}".includes(o)&&--n===0)break;s++}if(s>=t.length)return null;let r=[];for(let o of Re(t.slice(1,s))){if(o.startsWith("..."))continue;let i=ct(o),c=i===-1?o:o.slice(0,i),l=i===-1?void 0:o.slice(i+1).trim(),d=we(c,":"),f=(d===-1?c:c.slice(0,d)).trim();it.test(f)&&r.push(l===void 0?{name:f}:{name:f,default:l})}return r},at=e=>{let t=0,n=0,s=!0;for(;t<e.length;){let r=e[t];if(r==="'"||r==='"'||r==="`"){t=j(e,t),s=!1;continue}if(r==="/"&&e[t+1]==="/"){t=O(e,t);continue}if(r==="/"&&e[t+1]==="*"){t=A(e,t);continue}if(r==="/"&&M(e,t)){t=D(e,t),s=!1;continue}if(r==="e"&&n===0&&s&&(t===0||!/[\w$.]/.test(e[t-1]))){X.lastIndex=t;let o=X.exec(e);if(o)return t+o[0].length}"([{".includes(r)?n++:")]}".includes(r)&&(n=Math.max(0,n-1)),r===`
|
|
6
|
+
`||r===";"||r==="}"?s=!0:/\s/.test(r)||(s=!1),t++}return-1},lt=/^async(?![\w$])/,ft=/^function(?![\w$])\s*\*?\s*[A-Za-z_$][\w$]*|^function(?![\w$])\s*\*?/,ut=e=>{let t=at(e);if(t===-1)return null;let n=J(e,t),s=e.slice(n);lt.test(s)&&(n=J(e,n+5));let r=ft.exec(e.slice(n));if(r&&(n=J(e,n+r[0].length)),e[n]!=="(")return null;let o=0,i=n;for(;i<e.length;){let c=e[i];if(c==="'"||c==='"'||c==="`"){i=j(e,i);continue}if(c==="/"&&e[i+1]==="/"){i=O(e,i);continue}if(c==="/"&&e[i+1]==="*"){i=A(e,i);continue}if(c==="/"&&M(e,i)){i=D(e,i);continue}if("([{".includes(c))o++;else if(")]}".includes(c)&&--o===0)break;i++}return Re(e.slice(n+1,i))[0]??""},$e=e=>{let t=ut(e);if(t===null)return null;let n=se(t),s=n?.find(r=>r.name.startsWith("$"))?.name;if(s)throw new Error(`jq79: the factory signature is (props, ctx), so \`${s}\` can't be destructured from the first parameter. Write \`export default (props, { ${s} }) => \u2026\`, or \`_\` in place of props if the component takes none.`);return n},xe=e=>{let t="",n=0,s=0,r=!0,o=!1,i=0;for(;n<e.length;){let c=e[n],l=e[n+1],d=n===0||!/[\w$.]/.test(e[n-1]);if(c==="'"||c==='"'||c==="`"){let f=j(e,n);t+=e.slice(n,f),n=f,r=!1;continue}if(c==="/"&&(l==="/"||l==="*")){let f=l==="/"?O(e,n):A(e,n);t+=e.slice(n,f),n=f;continue}if(c==="/"&&M(e,n)){let f=D(e,n);t+=e.slice(n,f),n=f,r=!1;continue}if(c==="i"&&d){if(V.lastIndex=n,V.test(e)){t+="$__import",n+=6,r=!1;continue}if(s===0&&r){Se.lastIndex=n;let f=Se.exec(e);if(f){t+=ot(f[1],f[3],i++),n+=f[0].length,r=!1;continue}}}if(c==="e"&&d&&s===0&&r){X.lastIndex=n;let f=X.exec(e);if(f){o=!0,t+="$__exports.default =",n+=f[0].length,r=!1;continue}}"([{".includes(c)?s++:")]}".includes(c)&&(s=Math.max(0,s-1)),c===`
|
|
7
|
+
`||c===";"||c==="}"?r=!0:/\s/.test(c)||(r=!1),t+=c,n++}return o?t:null};var Ae=e=>Object.fromEntries(Array.from(e.attributes).map(t=>[t.name,t.value])),je=e=>({tag:e.tagName.toLowerCase(),attrs:Ae(e),children:Array.from(e.childNodes).flatMap(t=>{if(t.nodeType===Node.TEXT_NODE){let n=t.textContent??"";return n?[n]:[]}return t.nodeType===Node.ELEMENT_NODE?[je(t)]:[]})}),Te=new Map,dt=(e,t)=>{let n=`${t.join(",")}|${e}`,s=Te.get(n);if(s===void 0){try{s=new Function("$scope",...t,`with ($scope) { return (${e}); }`)}catch{s=null}Te.set(n,s)}return s},N=(e,t,n)=>{let s=dt(e,n?Object.keys(n):[]);if(s)try{return s(t,...n?Object.values(n):[])}catch{return}},pt=(e,t)=>e.replace(/{{\s*([\s\S]+?)\s*}}/g,(n,s)=>N(s,t)??""),Me=new Set([":attrs",":if",":elseif",":else",":each",":key",":with",":text",":html"]),ht=/^\s*\(?\s*(\w+)\s*(?:,\s*(\w+))?\s*\)?\s+in\s+([\s\S]+)$/,mt=(e,t,n,s)=>{let[r,...o]=t.slice(1).split("."),i=new Set(o);e.addEventListener(r,c=>{if(i.has("self")&&c.target!==e)return;i.has("prevent")&&c.preventDefault(),i.has("stop")&&c.stopPropagation();let l=N(n,s,{$event:c});typeof l=="function"&&l.call(e,c)},{once:i.has("once"),capture:i.has("capture")})},Ce=e=>e.replace(/-(\w)/g,(t,n)=>n.toUpperCase()),oe=e=>e instanceof DocumentFragment?{first:e.firstChild,last:e.lastChild}:{first:e,last:e},Q=({first:e,last:t})=>{for(let n=e;n;){let s=n===t?null:n.nextSibling;n.parentNode?.removeChild(n),n=s}},gt=({first:e,last:t},n)=>{let s=n.nextSibling;for(let r=e;r;){let o=r===t?null:r.nextSibling;n.parentNode.insertBefore(r,s),r=o}},Ne=(e,t)=>{let n=t.replace(/-/g,"").toLowerCase();for(let s=e;s&&s!==Object.prototype;s=Object.getPrototypeOf(s))for(let r of Object.keys(s))if(/^[A-Z]/.test(r)&&r.replace(/-/g,"").toLowerCase()===n)return r;return null},_e=(e,t,n,s,r)=>{let o=document.createComment(e),i=document.createComment(`/${e}`),c=document.createDocumentFragment();c.append(o,i);let l={};Object.entries(t.attrs).forEach(([h,y])=>{if(!(h===ae||Me.has(h)||h.startsWith("@")))if(h.startsWith(":")){let E=Ce(h.slice(1));l[E]=y||E}else l[Ce(h)]=JSON.stringify(y)});let d=null,f=null,p=null;return s.effect(()=>{let h=N(e,n),y=h instanceof F?h:null;if(y===f||(p?.dispose(),p=null,d?.destroy(),d=null,f=y,!y))return;let E=new F({template:y.template,scripts:y.scripts,styles:y.styles,modules:y.modules,filename:y.filename}),v=te(()=>Object.fromEntries(Object.entries(l).map(([u,m])=>[u,N(m,n)]))),L=document.createDocumentFragment();(r?E.renderShadow(v):E.render(v)).mount(L),i.parentNode.insertBefore(L,i);let a=B(n);Object.entries(l).forEach(([u,m])=>{a.effect(()=>{E.data[u]=N(m,n)})}),p=a,d=E}),s.onDispose(()=>{p?.dispose(),d?.destroy()}),c},yt=(e,t)=>{let n=()=>{let s=N(e,t);return s!==null&&typeof s=="object"?s:null};return new Proxy(t,{has(s,r){let o=n();return o!==null&&Reflect.has(o,r)||Reflect.has(s,r)},get(s,r){let o=n();return o!==null&&Reflect.has(o,r)?o[r]:Reflect.get(s,r)},set(s,r,o){let i=n();return i!==null&&Reflect.has(i,r)?(i[r]=o,!0):Reflect.set(s,r,o)}})},ie=(e,t,n,s)=>{let r=e.attrs[":with"],o=r!==void 0?yt(r,t):t,i=Ne(o,e.tag);if(i)return _e(i,e,o,n,s);let c=document.createElement(e.tag);if(c instanceof HTMLUnknownElement||e.tag.includes("-")){let p=!1;n.effect(()=>{if(p)return;let h=Ne(o,e.tag);if(!h)return;p=!0;let y=_e(h,e,o,n,s),E=oe(y);n.onDispose(()=>Q(E)),c.replaceWith(y)})}Object.entries(e.attrs).forEach(([p,h])=>{p.startsWith("@")?mt(c,p,h,o):Me.has(p)||c.setAttribute(p,h)});let l=e.attrs[":attrs"];if(l!==void 0){let p=[];n.effect(()=>{p.forEach(y=>c.removeAttribute(y));let h=N(l,o);p=h&&typeof h=="object"?Object.keys(h):[],p.forEach(y=>{let E=h[y];E!=null&&E!==!1&&c.setAttribute(y,String(E))})})}let d=e.attrs[":text"],f=e.attrs[":html"];return d!==void 0?n.effect(()=>{c.textContent=String(N(d,o)??"")}):f!==void 0?n.effect(()=>{c.innerHTML=pe(String(N(f,o)??""))}):c.appendChild(ce(e.children,o,n,s)),c},Et=(e,t,n,s)=>{let r=document.createComment("if"),o=document.createDocumentFragment();o.appendChild(r);let i=null,c=null,l=null;return n.effect(()=>{let d=e.find(p=>p.expr===void 0||N(p.expr,t))??null;if(d===c||(l?.dispose(),i&&Q(i),i=null,c=d,!d))return;l=B(t);let f=ie(d.node,t,l,s);i=oe(f),r.parentNode.insertBefore(f,r.nextSibling)}),o},H=(e,t,n)=>{Object.defineProperty(e,t,{value:n,writable:!0,enumerable:!0,configurable:!0})},bt=e=>{if(e===null||typeof e!="object"||Array.isArray(e))return!1;let t=Object.getPrototypeOf(e);return t===Object.prototype||t===null},St=(e,t,n,s)=>{let r=e.attrs[":each"].match(ht);if(!r)return document.createComment(`invalid :each expression "${e.attrs[":each"]}"`);let[,o,i,c]=r,l=e.attrs[":key"],{[":each"]:d,[":key"]:f,...p}=e.attrs,h={...e,attrs:p},y=document.createComment("each"),E=document.createDocumentFragment();E.appendChild(y),(":if"in e.attrs||":elseif"in e.attrs||":else"in e.attrs)&&console.warn("jq79: :if/:elseif/:else on a :each element is ignored; filter the list expression instead");let v=[],L=!1;return n.effect(()=>{let a=N(c,t),u=Array.isArray(a)?a.map((b,w)=>[w,b]):bt(a)?Object.entries(a):[],m=new Map;v.forEach(b=>{let w=m.get(b.key);w?w.push(b):m.set(b.key,[b])});let S=new Set,x=[],R=u.map(([b,w],C)=>{let $=Object.create(t);H($,o,w),i&&H($,i,b),H($,"$index",C);let P=l!==void 0?N(l,$):b;S.has(P)&&!L&&(L=!0,console.warn(`jq79: duplicate :key in :each "${e.attrs[":each"]}"; duplicates pair up by position`)),S.add(P);let _=m.get(P)?.shift();if(_&&Object.is(_.item,w))return _.scope.$index!==C&&x.push(_),H(_.scope,"$index",C),i&&H(_.scope,i,b),_;_&&(_.fx.dispose(),Q(_.range));let fe=B(t),Be=oe(ie(h,$,fe,s));return{key:P,item:w,scope:$,fx:fe,range:Be}});m.forEach(b=>b.forEach(w=>{w.fx.dispose(),Q(w.range)}));let g=y;R.forEach(b=>{g.nextSibling!==b.range.first&>(b.range,g),g=b.range.last}),x.forEach(b=>te(()=>b.fx.refresh())),v=R}),E},ce=(e,t,n,s=!1)=>{let r=document.createDocumentFragment(),o=0;for(;o<e.length;){let i=e[o];if(typeof i=="string"){let c=document.createTextNode(i);i.includes("{{")&&n.effect(()=>{c.textContent=pt(i,t)}),r.appendChild(c),o++;continue}if(":each"in i.attrs){r.appendChild(St(i,t,n,s)),o++;continue}if(":if"in i.attrs){let c=[{expr:i.attrs[":if"],node:i}];o++;let l=f=>{let p=o;for(;p<e.length&&typeof e[p]=="string"&&!e[p].trim();)p++;let h=e[p];if(typeof h=="object"&&f in h.attrs)return o=p+1,h};for(let f=l(":elseif");f;f=l(":elseif"))c.push({expr:f.attrs[":elseif"],node:f});let d=l(":else");d&&c.push({node:d}),r.appendChild(Et(c,t,n,s));continue}r.appendChild(ie(i,t,n,s)),o++}return r},wt=(e,t,n=!1)=>ce(e.template,t,B(t),n),Rt=new Set(["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"]),$t=/<([A-Za-z][\w-]*)((?:"[^"]*"|'[^']*'|[^>"'])*?)\/>/g,xt=/(<script[\s\S]*?<\/script\s*>|<style[\s\S]*?<\/style\s*>)/gi,Tt=e=>e.split(xt).map((t,n)=>n%2===1?t:t.replace($t,(s,r,o)=>Rt.has(r.toLowerCase())?s:`<${r}${o}></${r}>`)).join(""),ae="data-jq79",Ct=e=>{let t=2166136261;for(let n=0;n<e.length;n++)t=Math.imul(t^e.charCodeAt(n),16777619);return(t>>>0).toString(36)},De=(e,t)=>{e.forEach(n=>{typeof n!="string"&&(n.attrs[ae]=t,De(n.children,t))})},Nt=(e,t)=>e.split(",").map(n=>{let s=n.trim(),r=s.indexOf("::"),o=r===-1?s:s.slice(0,r),i=r===-1?"":s.slice(r);return`${o}[${ae}="${t}"]${i}`}).join(", "),Pe=(e,t)=>{Array.from(e).forEach(n=>{n instanceof CSSStyleRule?n.selectorText=Nt(n.selectorText,t):n instanceof CSSGroupingRule&&Pe(n.cssRules,t)})},_t=(e,t)=>{/:deep\(|::v-deep|>>>/.test(e)&&console.warn("jq79: :deep()/::v-deep/>>> are not supported in <style scoped>; the rule will be dropped by the browser");let n=new CSSStyleSheet;return n.replaceSync(e),Pe(n.cssRules,t),Array.from(n.cssRules).map(s=>s.cssText).join(`
|
|
8
|
+
`)},re=e=>{let n=new DOMParser().parseFromString(`<template>${Tt(e)}</template>`,"text/html").querySelector("template"),s=[],r=[],o=[];Array.from(n.content.children).forEach(c=>{let l={attrs:Ae(c),content:c.textContent??""};c.tagName==="SCRIPT"?s.push(l):c.tagName==="STYLE"?r.push(l):o.push(je(c))}),r.forEach(c=>{"lang"in c.attrs&&console.warn(`jq79: <style lang="${c.attrs.lang}"> needs the jq79/vite plugin to compile it. This component didn't go through the bundler, so its styles were left uncompiled and the browser will ignore them.`)});let i=c=>"scoped"in c.attrs&&!("lang"in c.attrs);if(r.some(i)){let c=Ct(e);De(o,c),r.forEach(l=>{i(l)&&(l.scoped=_t(l.content,c))})}return{template:o,scripts:s,styles:r}},le=e=>/\.html?([?#]|$)/.test(e)?F.fetch(e):import(e),ke=(e,t)=>e?`
|
|
9
|
+
//# sourceURL=${e}?jq79-script=${t}`:"",ve=e=>e.scoped??e.content,Y=new Map,vt=e=>{let t=Y.get(e);if(!t){let n=document.createElement("style");n.textContent=e,document.head.appendChild(n),t={el:n,count:0},Y.set(e,t)}t.count++},Lt=e=>{let t=Y.get(e);t&&--t.count<=0&&(t.el.remove(),Y.delete(e))},Fe={$:I,$$:G,$create:K,$reactive:q},Ot=(e,t,n,s={},r=le,o={})=>{let i={...Fe,...s},c=new Proxy(t,{has:(d,f)=>f!=="$__effect"&&f!=="$__import"&&(Reflect.has(d,f)||!(f in globalThis)&&!(f in i))});new Function("$scope","$__effect","$__import",...Object.keys(i),`return (async () => { with ($scope) { ${e} } })()${ke(o.filename,o.index??0)}`)(c,n,r,...Object.values(i)).catch(d=>console.error("jq79: error in :setup script",d))},Le=(e,t)=>{t?.forEach(({name:n,default:s})=>{e[n]===void 0&&(e[n]=s===void 0?void 0:N(s,e))})},At=e=>e&&e.default!==void 0?e.default:e,jt=(e,t,n,s={},r=le,o={})=>{let i={...Fe,...s},c={},l=new Function("$__exports","$__default","$__import",...Object.keys(i),`return (async () => { "use strict";
|
|
9
10
|
${e}
|
|
10
|
-
;$__exports.done = true })()${
|
|
11
|
+
;$__exports.done = true })()${ke(o.filename,o.index??0)}`)(c,At,r,...Object.values(i)),d=h=>console.error("jq79: error in factory script",h),f=!1,p=()=>{if(f)return;f=!0;let h=c.default;if(typeof h!="function")return;let y=E=>{E&&typeof E=="object"&&Object.assign(t,E)};try{let E=h(t,{$data:t,$props:t,$effect:n,...s});E instanceof Promise?E.then(y).catch(d):y(E)}catch(E){d(E)}};l.then(p,d),c.done&&p()},Mt="__JQ79_HMR_ENABLED__",Dt="__JQ79_HMR__",k=null,Pt=e=>{if(!k||!e.filename)return;let t=k.get(e.filename);t||k.set(e.filename,t=new Set),t.add(new WeakRef(e))},Oe=e=>{try{return new URL(e,document.baseURI).pathname}catch{return e}},Ie=(e,t)=>{if(!k)return 0;let n=Oe(e),s=re(t),r=0;for(let[o,i]of k)if(Oe(o)===n){for(let c of i){let l=c.deref();if(!l){i.delete(c);continue}l.hotReplace(s)&&r++}i.size||k.delete(o)}return r},We=()=>{k??(k=new Map),globalThis[Dt]={update:Ie}},F=class e{constructor(t,n={}){T(this,"template");T(this,"scripts");T(this,"styles");T(this,"modules");T(this,"filename");T(this,"data",null);T(this,"fx",null);T(this,"content",null);T(this,"startMarker",null);T(this,"endMarker",null);T(this,"styleEls",[]);T(this,"ownsSharedStyles",!1);T(this,"useShadow",!1);T(this,"mountRoot",null);T(this,"resolveMounted",null);T(this,"emitListeners",new Map);let s=typeof t=="string"?re(t):t;this.template=s.template,this.scripts=s.scripts,this.styles=s.styles,this.modules=n.modules??(typeof t=="string"?void 0:t.modules),this.filename=n.filename??(typeof t=="string"?void 0:t.filename),Pt(this)}hotReplace(t){let n=typeof t=="string"?re(t):t,s=this.startMarker,r=!!(s&&this.content),o=r&&s.isConnected,i=o?s.parentNode:null,c=o?this.endMarker.nextSibling:null,l={...this.data},d=this.useShadow;return r&&this.destroy(),this.template=n.template,this.scripts=n.scripts,this.styles=n.styles,!r||(this.renderWith(l,d),!i)?!1:(d&&this.styleEls.forEach(f=>i.insertBefore(f,c)),i.insertBefore(this.content,c),this.mountRoot=i,this.resolveMounted?.(),!0)}static async fetch(t){let n=await fetch(t);if(!n.ok)throw new Error(`failed to fetch component from ${t}: ${n.status}`);return new e(await n.text(),{filename:t})}on(t,n){return this.emitListeners.has(t)||this.emitListeners.set(t,new Set),this.emitListeners.get(t).add(n),this}off(t,n){return this.emitListeners.get(t)?.delete(n),this}render(t={}){return this.renderWith(t,!1)}renderShadow(t={}){return this.renderWith(t,!0)}renderWith(t,n){this.destroy();let s=q({...t}),r=B(s);this.data=s,this.fx=r,this.useShadow=n,this.startMarker=document.createComment("jq79"),this.endMarker=document.createComment("/jq79");let o=this.startMarker,i=(a,u)=>{let m=new CustomEvent(a,{detail:u,bubbles:!0,composed:!0}),S=o.dispatchEvent(m);return o===this.startMarker&&this.emitListeners.get(a)?.forEach(x=>x(m,u)),S},c,l=new Promise(a=>{c=a});this.resolveMounted=c;let d=()=>l,f=this.endMarker,p=a=>{let u=[];for(let m=o.nextSibling;m&&m!==f;m=m.nextSibling)m instanceof Element&&(m.matches(a)&&u.push(m),u.push(...Array.from(m.querySelectorAll(a))));return u},h=a=>p(a)[0]??null,y=this.modules,E=a=>y&&a in y?Promise.resolve(y[a]):le(a),v=a=>`await $mounted();${a}`;this.scripts.forEach((a,u)=>{let m={$emit:i,$mounted:d,$self:h,$$self:p},S={filename:this.filename,index:u},x=xe(a.content);if(x!==null){Le(s,$e(a.content));let w=":mounted"in a.attrs?v(x):x;jt(w,s,r.effect,m,E,S);return}let{vars:R,code:g}=ne(a.content);Le(s,se(a.attrs[":setup"])),R.forEach(w=>{w in s||(s[w]=void 0)});let b=":mounted"in a.attrs?v(g):g;Ot(b,s,r.effect,m,E,S)});let L=document.createDocumentFragment();return L.append(this.startMarker,ce(this.template,s,r,n),this.endMarker),this.content=L,n?this.styleEls=this.styles.map(a=>{let u=document.createElement("style");return u.textContent=a.content,u}):(this.styles.forEach(a=>vt(ve(a))),this.ownsSharedStyles=!0),this}mount(t,n){let s=typeof t=="string"?I(t):t;if(!s)throw new Error(`mount target not found: ${t}`);return(!this.content||n!==void 0)&&this.renderWith(n??{},this.useShadow),this.attach(s)}mountShadow(t,n){let s=typeof t=="string"?I(t):t;if(!s)throw new Error(`mount target not found: ${t}`);return(!this.content||n!==void 0||!this.useShadow)&&this.renderWith(n??{},!0),this.attach(s)}attach(t){this.mountRoot&&this.detach();let n=this.useShadow&&t instanceof Element?t.shadowRoot??t.attachShadow({mode:"open"}):t;return this.useShadow&&this.styleEls.forEach(s=>n.appendChild(s)),n.appendChild(this.content),this.mountRoot=n,this.resolveMounted?.(),this}detach(){if(!this.mountRoot||!this.content||!this.startMarker||!this.endMarker)return this;let t=this.startMarker;for(;t;){let n=t.nextSibling;if(this.content.appendChild(t),t===this.endMarker)break;t=n}return this.mountRoot=null,this}destroy(){return this.detach(),this.fx?.dispose(),this.fx=null,this.data?.$dispose(),this.styleEls.forEach(t=>t.parentNode?.removeChild(t)),this.styleEls=[],this.ownsSharedStyles&&(this.styles.forEach(t=>Lt(ve(t))),this.ownsSharedStyles=!1),this.content=null,this.startMarker=null,this.endMarker=null,this.data=null,this.resolveMounted=null,this}},kt=e=>new F(e);typeof globalThis<"u"&&globalThis[Mt]&&We();0&&(module.exports={$,$$,$create,$reactive,Component79,enableHotReload,hotUpdate,parseComponent,renderComponent});
|
|
11
12
|
//# sourceMappingURL=jq79.cjs.map
|