jq79 0.7.1 → 0.7.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.
- package/README.md +1 -0
- package/dev/vite.ts +192 -24
- package/dist/dom.d.ts +22 -4
- package/dist/html.d.ts +9 -0
- package/dist/jq79-sw.js +32 -0
- package/dist/jq79.cjs +18 -14
- package/dist/jq79.cjs.map +1 -1
- package/dist/jq79.d.ts +15 -12
- package/dist/jq79.global.js +18 -14
- package/dist/jq79.global.js.map +1 -1
- package/dist/jq79.js +18 -14
- package/dist/jq79.js.map +1 -1
- package/dist/precompile.cjs +24 -0
- package/dist/precompile.cjs.map +1 -0
- package/dist/precompile.d.ts +3 -0
- package/dist/precompile.js +24 -0
- package/dist/precompile.js.map +1 -0
- package/dist/reactive.d.ts +6 -0
- package/dist/source.d.ts +65 -0
- package/dist/vite.cjs +102 -11
- package/dist/vite.cjs.map +1 -1
- package/dist/vite.d.ts +3 -0
- package/dist/vite.js +103 -12
- package/dist/vite.js.map +1 -1
- package/package.json +12 -2
- package/src/dom.ts +28 -4
- package/src/html.ts +289 -0
- package/src/jq79.ts +551 -366
- package/src/precompile.ts +237 -0
- package/src/reactive.ts +149 -9
- package/src/source.ts +388 -0
- package/src/sw.ts +87 -0
package/README.md
CHANGED
|
@@ -178,6 +178,7 @@ When the fetch resolves, the assignments to `firstName`/`lastName` re-run the `$
|
|
|
178
178
|
- [Reactive data](docs/reactive-data.md) — the standalone `$reactive` store: `$on`, `$onAny`, `$effect`.
|
|
179
179
|
- [DOM helpers](docs/dom-helpers.md) — `$`, `$$` and `$create`.
|
|
180
180
|
- [Vite plugin](docs/vite-plugin.md) — importing `.html` components as bundled modules, HMR, options.
|
|
181
|
+
- [Content Security Policy](docs/csp.md) — `safeEval`: running without `'unsafe-eval'`, with Vite or with no bundler at all.
|
|
181
182
|
- [Dev server](docs/dev-server.md) — `npx jq79 dev`: serve and hot-reload components with no build step.
|
|
182
183
|
- [Development](docs/development.md) — running tests, building, publishing releases.
|
|
183
184
|
|
package/dev/vite.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises"
|
|
2
|
-
import { relative } from "node:path"
|
|
2
|
+
import { basename, relative } from "node:path"
|
|
3
3
|
import { preprocessCSS, transformWithEsbuild } from "vite"
|
|
4
4
|
import type { Plugin, ResolvedConfig } from "vite"
|
|
5
|
+
// by package name rather than ../src: this file's types are emitted with dev/
|
|
6
|
+
// as their root, and the generator is the runtime's own code - resolved through
|
|
7
|
+
// the package's exports (external in the build, aliased to src in the tests)
|
|
8
|
+
import { precompile, precompiledScript } from "jq79/precompile"
|
|
5
9
|
|
|
6
10
|
// Vite plugin: import .html single-file components as modules.
|
|
7
11
|
//
|
|
@@ -12,11 +16,12 @@ import type { Plugin, ResolvedConfig } from "vite"
|
|
|
12
16
|
// thing `await Component79.fetch(url)` resolves to, but bundled at build time
|
|
13
17
|
// instead of fetched at runtime. The component source is inlined verbatim, so
|
|
14
18
|
// a file keeps working unchanged if it's ever served from public/ and loaded
|
|
15
|
-
// with fetch instead - with one deliberate exception,
|
|
16
|
-
//
|
|
17
|
-
// plain
|
|
18
|
-
//
|
|
19
|
-
//
|
|
19
|
+
// with fetch instead - with one deliberate exception, a block that says it is
|
|
20
|
+
// written in something else: <style lang="scss"> (or less/stylus/sass) is
|
|
21
|
+
// compiled to plain CSS here, and a TypeScript script - <script lang="ts">, or
|
|
22
|
+
// the same mark spelled <script type="text/typescript"> - to plain JS. Such a
|
|
23
|
+
// component only works through the bundler; loaded with fetch() it would reach
|
|
24
|
+
// the runtime uncompiled, which the runtime warns about.
|
|
20
25
|
//
|
|
21
26
|
// Only .html files imported from other modules are claimed; entry points
|
|
22
27
|
// (index.html) have no importer and imports carrying an explicit query
|
|
@@ -27,12 +32,105 @@ export interface Jq79PluginOptions {
|
|
|
27
32
|
include?: RegExp
|
|
28
33
|
// resolved absolute paths to skip even when `include` matches
|
|
29
34
|
exclude?: RegExp
|
|
35
|
+
// run the app without eval, for a CSP with no 'unsafe-eval' - the value
|
|
36
|
+
// Component79.safeEval() takes, and meaning the same thing
|
|
37
|
+
// (RECORD/2026-09-23.no-unsafe-eval.md). Every component is precompiled, and
|
|
38
|
+
// its functions ship as a script of their own. `true`: those and nothing
|
|
39
|
+
// else - whatever the build couldn't see is reported, never evaluated.
|
|
40
|
+
// `{ nonce: true }`: whatever it couldn't see (a component built from a
|
|
41
|
+
// string, an edit during HMR) is built as a <script> carrying the page's nonce
|
|
42
|
+
safeEval?: boolean | { nonce?: boolean }
|
|
30
43
|
}
|
|
31
44
|
|
|
32
45
|
// claimed modules get this suffix so their id no longer ends in ".html" and
|
|
33
46
|
// Vite's own html handling (entries, asset pipeline) leaves them alone
|
|
34
47
|
const COMPONENT_QUERY = "?jq79"
|
|
35
48
|
|
|
49
|
+
// safeEval. A component's precompiled functions travel as a classic script of
|
|
50
|
+
// their own - Card.html.jq79.js - and not inside its module: they compile
|
|
51
|
+
// under `with`, which is a SyntaxError in strict code, and every module is
|
|
52
|
+
// strict. In a build the script is an asset beside the chunk; in dev the
|
|
53
|
+
// server hands it out (PRECOMPILED_PATH). Either way it pushes its functions
|
|
54
|
+
// onto the queue the runtime drains (see "safe eval" in src/jq79.ts).
|
|
55
|
+
//
|
|
56
|
+
// The component's module waits for it - a top-level await - before it builds
|
|
57
|
+
// anything, through a module every component module imports. That module
|
|
58
|
+
// evaluates once, so safe mode is turned on once per app, and a page with no
|
|
59
|
+
// nonce is reported once rather than once per component; and it loads each
|
|
60
|
+
// script once, however many modules ask. The <script> carries the page's
|
|
61
|
+
// nonce when there is one, so a CSP that works by nonce admits it; under
|
|
62
|
+
// `script-src 'self'` the URL is what admits it.
|
|
63
|
+
//
|
|
64
|
+
// Component79 is passed in rather than imported, so the module resolves
|
|
65
|
+
// nothing: a bare "jq79" from a virtual importer is the one resolution that
|
|
66
|
+
// would depend on how the app is laid out.
|
|
67
|
+
//
|
|
68
|
+
// A component built from a string *before* any .html module is imported runs
|
|
69
|
+
// in the default mode; such an app calls Component79.safeEval itself. The
|
|
70
|
+
// await needs a build target with top-level await in it - Vite 7 and later
|
|
71
|
+
// have one by default; on Vite 5 or 6, raise build.target to es2022
|
|
72
|
+
const SAFE_EVAL_ID = "virtual:jq79/safe-eval"
|
|
73
|
+
const RESOLVED_SAFE_EVAL_ID = `\0${SAFE_EVAL_ID}`
|
|
74
|
+
const safeEvalModule = (options: { nonce?: boolean; worker: false }) => `
|
|
75
|
+
let started = null
|
|
76
|
+
const loading = new Map()
|
|
77
|
+
const pageNonce = () => {
|
|
78
|
+
for (const script of document.querySelectorAll("script[nonce]")) {
|
|
79
|
+
const nonce = script.nonce || script.getAttribute("nonce")
|
|
80
|
+
if (nonce) return nonce
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
export const safeEval = (Component79, url) => {
|
|
84
|
+
if (!started) started = Component79.safeEval(${JSON.stringify(options)}).catch(error => console.error(error))
|
|
85
|
+
let loaded = loading.get(url)
|
|
86
|
+
if (!loaded) {
|
|
87
|
+
loaded = new Promise((resolve, reject) => {
|
|
88
|
+
const script = document.createElement("script")
|
|
89
|
+
const nonce = pageNonce()
|
|
90
|
+
if (nonce) script.setAttribute("nonce", nonce)
|
|
91
|
+
script.src = url
|
|
92
|
+
script.onload = () => { script.remove(); resolve() }
|
|
93
|
+
script.onerror = () => {
|
|
94
|
+
script.remove()
|
|
95
|
+
reject(new Error("jq79: the precompiled functions at " + url + " did not load, and safeEval can't render without them"))
|
|
96
|
+
}
|
|
97
|
+
document.head.append(script)
|
|
98
|
+
})
|
|
99
|
+
loading.set(url, loaded)
|
|
100
|
+
}
|
|
101
|
+
return loaded
|
|
102
|
+
}
|
|
103
|
+
`
|
|
104
|
+
|
|
105
|
+
// where the dev server hands out a component's precompiled script
|
|
106
|
+
const PRECOMPILED_PATH = "/@jq79/precompiled.js"
|
|
107
|
+
|
|
108
|
+
// the plugin's safeEval option, as what the modules pass the runtime: null
|
|
109
|
+
// for off, or Component79.safeEval's own options
|
|
110
|
+
const safeEvalOptions = (option: Jq79PluginOptions["safeEval"]): { nonce?: boolean } | null => {
|
|
111
|
+
if (option === undefined || option === false) return null
|
|
112
|
+
return typeof option === "object" && option !== null && option.nonce === true ? { nonce: true } : {}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// a scoped-form body: the runtime falls back to the `with` form when one
|
|
116
|
+
// doesn't compile, so only the `with` form's failure is worth a word
|
|
117
|
+
const SCOPED_BODY_RE = /^(?:let \$t;| return \()/
|
|
118
|
+
|
|
119
|
+
// a component's precompiled functions, as the classic script that registers
|
|
120
|
+
// them (precompiledScript, in jq79/precompile). Each is compiled here first -
|
|
121
|
+
// node can, where the page may not - so the script holds only functions that
|
|
122
|
+
// parse; one that doesn't ships as null, and the build says so
|
|
123
|
+
const componentScript = (source: string, warn: (message: string) => void): string =>
|
|
124
|
+
precompiledScript(precompile(source), (params, body) => {
|
|
125
|
+
try {
|
|
126
|
+
new Function(...params, body)
|
|
127
|
+
return true
|
|
128
|
+
} catch (error) {
|
|
129
|
+
if (!SCOPED_BODY_RE.test(body)) warn(`${(error as Error).message}, in: ${body.length > 120 ? `${body.slice(0, 120)}…` : body}`)
|
|
130
|
+
return false
|
|
131
|
+
}
|
|
132
|
+
})
|
|
133
|
+
|
|
36
134
|
// a <script> block with its attribute string, so `lang` can be read and the
|
|
37
135
|
// body replaced - the same shape as STYLE_BLOCK_RE below, quote-aware so a
|
|
38
136
|
// ">" inside an attribute value (`:setup="{ n = a > 1 }"`) doesn't end the tag
|
|
@@ -152,6 +250,14 @@ const declaredComponents = (source: string): string[] => {
|
|
|
152
250
|
// inside one doesn't end the tag early
|
|
153
251
|
const STYLE_BLOCK_RE = /<style((?:"[^"]*"|'[^']*'|[^>"'])*)>([\s\S]*?)<\/style\s*>/gi
|
|
154
252
|
const LANG_ATTR_RE = /\blang\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))/i
|
|
253
|
+
const TYPE_ATTR_RE = /\btype\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))/i
|
|
254
|
+
|
|
255
|
+
// one attribute's value out of a tag's attribute string, whichever way it was
|
|
256
|
+
// quoted - null when the attribute isn't there at all
|
|
257
|
+
const attrValue = (attrs: string, re: RegExp): string | null => {
|
|
258
|
+
const found = attrs.match(re)
|
|
259
|
+
return found ? found[1] ?? found[2] ?? found[3] : null
|
|
260
|
+
}
|
|
155
261
|
|
|
156
262
|
// compiles <style lang="scss|less|styl|sass"> blocks to plain CSS with Vite's
|
|
157
263
|
// own preprocessing (the same call @vitejs/plugin-vue makes), so the runtime
|
|
@@ -170,9 +276,8 @@ const compileStyleBlocks = async (
|
|
|
170
276
|
const blocks = [...source.matchAll(STYLE_BLOCK_RE)]
|
|
171
277
|
const compiled = await Promise.all(
|
|
172
278
|
blocks.map(async ([, attrs, content]) => {
|
|
173
|
-
const
|
|
174
|
-
if (
|
|
175
|
-
const extension = lang[1] ?? lang[2] ?? lang[3]
|
|
279
|
+
const extension = attrValue(attrs, LANG_ATTR_RE)
|
|
280
|
+
if (extension === null) return null
|
|
176
281
|
const result = await preprocessCSS(content, `${file}.${extension}`, config)
|
|
177
282
|
result.deps?.forEach(addWatchFile)
|
|
178
283
|
return { attrs: attrs.replace(LANG_ATTR_RE, "").trimEnd(), css: result.code }
|
|
@@ -193,6 +298,26 @@ const compileStyleBlocks = async (
|
|
|
193
298
|
// languages a <script lang> is compiled from. Anything else is left as written
|
|
194
299
|
// for the runtime to warn about, rather than guessed at
|
|
195
300
|
const TS_LANGS = new Set(["ts", "typescript"])
|
|
301
|
+
// the other spelling of the same mark. A component is a plain .html file, not
|
|
302
|
+
// an SFC, so nothing in an editor knows what `lang` means there, and each mark
|
|
303
|
+
// gets half of one: VS Code's HTML language service checks a `type` of
|
|
304
|
+
// text/typescript as TypeScript (and `lang="ts"` as JavaScript), while its
|
|
305
|
+
// grammar colors that `type` not at all (and `lang="ts"` as JavaScript). See
|
|
306
|
+
// docs/vite-plugin.md#which-mark-in-vs-code. The `x-` forms are the historical
|
|
307
|
+
// spelling of the same two media types
|
|
308
|
+
const TS_TYPE_RE = /^(?:text|application)\/(?:x-)?typescript$/
|
|
309
|
+
|
|
310
|
+
// what marks a script block as TypeScript, given back as the attribute to drop
|
|
311
|
+
// from the emitted tag - the block is compiled to JS, so the mark goes with the
|
|
312
|
+
// types whichever one carried it. A `lang` answers on its own: `lang="coffee"`
|
|
313
|
+
// is a language this plugin doesn't compile, and reading `type` past it would
|
|
314
|
+
// compile a block the author said was something else
|
|
315
|
+
const typescriptAttr = (attrs: string): RegExp | null => {
|
|
316
|
+
const lang = attrValue(attrs, LANG_ATTR_RE)
|
|
317
|
+
if (lang !== null) return TS_LANGS.has(lang.trim().toLowerCase()) ? LANG_ATTR_RE : null
|
|
318
|
+
const type = attrValue(attrs, TYPE_ATTR_RE)
|
|
319
|
+
return type !== null && TS_TYPE_RE.test(type.trim().toLowerCase()) ? TYPE_ATTR_RE : null
|
|
320
|
+
}
|
|
196
321
|
|
|
197
322
|
type ViteTransform = (code: string, id: string, options?: unknown) => Promise<{ code: string }>
|
|
198
323
|
|
|
@@ -231,10 +356,10 @@ const SETUP_ATTR_RE = /(:setup\s*=\s*)(?:"([^"]*)"|'([^']*)')/i
|
|
|
231
356
|
const SIGNATURE_MARKER = "__jq79_signature__"
|
|
232
357
|
|
|
233
358
|
// a component's props signature lives in the `:setup` *attribute*, not in the
|
|
234
|
-
// script body, so the body's transform never sees it - and
|
|
235
|
-
// mean the same thing on both halves of the block, or a typed signature
|
|
236
|
-
// survives into a component the plugin just promised was JS or, for
|
|
237
|
-
// stops reading as a signature at all.
|
|
359
|
+
// script body, so the body's transform never sees it - and the TypeScript mark
|
|
360
|
+
// has to mean the same thing on both halves of the block, or a typed signature
|
|
361
|
+
// either survives into a component the plugin just promised was JS or, for
|
|
362
|
+
// `_: Props`, stops reading as a signature at all.
|
|
238
363
|
//
|
|
239
364
|
// It goes through the same transform as everything else, wrapped as a
|
|
240
365
|
// parameter list, rather than being cut with a scanner of its own: the
|
|
@@ -259,12 +384,13 @@ const stripSignatureTypes = async (value: string, file: string): Promise<string>
|
|
|
259
384
|
// already wrote `"`
|
|
260
385
|
const quoteAttrValue = (value: string) => value.replace(/"/g, """)
|
|
261
386
|
|
|
262
|
-
// compiles
|
|
387
|
+
// compiles TypeScript script blocks to plain JS, so the runtime only ever sees
|
|
263
388
|
// JS - the same deal <style lang="scss"> gets, for a sharper reason. The setup
|
|
264
389
|
// scanner is not a parser: `let count: number = 0` reaching it is not a syntax
|
|
265
390
|
// error but a labeled statement that assigns to `number`, so it runs and leaves
|
|
266
|
-
// `count` undeclared.
|
|
267
|
-
//
|
|
391
|
+
// `count` undeclared. Whichever attribute marked the block (`lang="ts"` or
|
|
392
|
+
// `type="text/typescript"`) is dropped from the emitted tag and every other one
|
|
393
|
+
// (`:setup`, `:mounted`) is left as written.
|
|
268
394
|
//
|
|
269
395
|
// This runs before hoistableImports reads the source, so an `import type`
|
|
270
396
|
// specifier is already gone by the time the plugin decides what to bundle
|
|
@@ -272,11 +398,10 @@ const compileScriptBlocks = async (source: string, file: string): Promise<string
|
|
|
272
398
|
const blocks = [...source.matchAll(SCRIPT_BLOCK_RE)]
|
|
273
399
|
const compiled = await Promise.all(
|
|
274
400
|
blocks.map(async ([, attrs, content]) => {
|
|
275
|
-
const
|
|
276
|
-
|
|
277
|
-
if (!name || !TS_LANGS.has(name)) return null
|
|
401
|
+
const marker = typescriptAttr(attrs)
|
|
402
|
+
if (!marker) return null
|
|
278
403
|
|
|
279
|
-
let rest = attrs.replace(
|
|
404
|
+
let rest = attrs.replace(marker, "").trimEnd()
|
|
280
405
|
const setup = rest.match(SETUP_ATTR_RE)
|
|
281
406
|
if (setup) {
|
|
282
407
|
const signature = await stripSignatureTypes(setup[2] ?? setup[3], `${file}.signature.ts`)
|
|
@@ -324,7 +449,9 @@ const compileScriptBlocks = async (source: string, file: string): Promise<string
|
|
|
324
449
|
// component's markers. An instance only used as a definition has nothing to
|
|
325
450
|
// re-render (nested clones can't be reached from this module), so it falls
|
|
326
451
|
// back to a full reload.
|
|
327
|
-
|
|
452
|
+
// `precompiled` is the precompiled script's URL, as a JS expression - present
|
|
453
|
+
// only in safeEval mode
|
|
454
|
+
const componentModule = (source: string, include: RegExp, filename: string, precompiled?: string): string => {
|
|
328
455
|
const hoisted = hoistableImports(source, include)
|
|
329
456
|
const imports = hoisted
|
|
330
457
|
.map((spec, i) =>
|
|
@@ -335,9 +462,13 @@ const componentModule = (source: string, include: RegExp, filename: string): str
|
|
|
335
462
|
.join("\n")
|
|
336
463
|
const modulesMap = `{ ${hoisted.map((spec, i) => `${JSON.stringify(spec)}: __jq79_${i}`).join(", ")} }`
|
|
337
464
|
|
|
465
|
+
// without safeEval, the module is exactly what it always was
|
|
466
|
+
const safeEvalImport = precompiled ? `\nimport { safeEval } from "${SAFE_EVAL_ID}"` : ""
|
|
467
|
+
const safeEvalCall = precompiled ? `\nawait safeEval(Component79, ${precompiled})` : ""
|
|
468
|
+
|
|
338
469
|
return `
|
|
339
|
-
import { Component79 } from "jq79"
|
|
340
|
-
${imports}
|
|
470
|
+
import { Component79 } from "jq79"${safeEvalImport}
|
|
471
|
+
${imports}${safeEvalCall}
|
|
341
472
|
|
|
342
473
|
const src = ${JSON.stringify(source)}
|
|
343
474
|
const modules = ${modulesMap}
|
|
@@ -371,6 +502,11 @@ ${declaredComponents(source).map(name => `export const ${name} = component.${nam
|
|
|
371
502
|
export function jq79(options: Jq79PluginOptions = {}): Plugin {
|
|
372
503
|
const include = options.include ?? /\.html$/
|
|
373
504
|
const { exclude } = options
|
|
505
|
+
const safeEval = safeEvalOptions(options.safeEval)
|
|
506
|
+
// dev only: each component's precompiled script, by the id its module asks
|
|
507
|
+
// for - so the server hands out what the plugin compiled and nothing else
|
|
508
|
+
const devScripts = new Map<string, string>()
|
|
509
|
+
const devIds = new Map<string, number>()
|
|
374
510
|
|
|
375
511
|
let config: ResolvedConfig | null = null
|
|
376
512
|
|
|
@@ -382,7 +518,21 @@ export function jq79(options: Jq79PluginOptions = {}): Plugin {
|
|
|
382
518
|
config = resolved
|
|
383
519
|
},
|
|
384
520
|
|
|
521
|
+
configureServer(server) {
|
|
522
|
+
if (!safeEval) return
|
|
523
|
+
server.middlewares.use((req, res, next) => {
|
|
524
|
+
const url = new URL(req.url ?? "/", "http://localhost")
|
|
525
|
+
if (!url.pathname.endsWith(PRECOMPILED_PATH)) return next()
|
|
526
|
+
const script = devScripts.get(url.searchParams.get("id") ?? "")
|
|
527
|
+
if (script === undefined) return next()
|
|
528
|
+
res.setHeader("Content-Type", "text/javascript")
|
|
529
|
+
res.setHeader("Cache-Control", "no-cache")
|
|
530
|
+
res.end(script)
|
|
531
|
+
})
|
|
532
|
+
},
|
|
533
|
+
|
|
385
534
|
async resolveId(source, importer) {
|
|
535
|
+
if (source === SAFE_EVAL_ID) return RESOLVED_SAFE_EVAL_ID
|
|
386
536
|
if (!importer) return null // entry points are never components
|
|
387
537
|
if (source.includes("?")) return null // ?raw, ?url, ... keep their meaning
|
|
388
538
|
if (!include.test(source)) return null
|
|
@@ -394,6 +544,8 @@ export function jq79(options: Jq79PluginOptions = {}): Plugin {
|
|
|
394
544
|
},
|
|
395
545
|
|
|
396
546
|
async load(id) {
|
|
547
|
+
// a Vite page's functions come from the build, so it never registers the worker
|
|
548
|
+
if (id === RESOLVED_SAFE_EVAL_ID) return safeEvalModule({ ...safeEval, worker: false })
|
|
397
549
|
if (!id.endsWith(COMPONENT_QUERY)) return null
|
|
398
550
|
const file = id.slice(0, -COMPONENT_QUERY.length)
|
|
399
551
|
|
|
@@ -405,7 +557,23 @@ export function jq79(options: Jq79PluginOptions = {}): Plugin {
|
|
|
405
557
|
// shows a path the user recognizes instead of an anonymous VM script
|
|
406
558
|
const filename = config ? relative(config.root, file) : file
|
|
407
559
|
|
|
408
|
-
return { code: componentModule(source, include, filename), map: null }
|
|
560
|
+
if (!safeEval) return { code: componentModule(source, include, filename), map: null }
|
|
561
|
+
|
|
562
|
+
// the component's functions, compiled after its TypeScript and styles are:
|
|
563
|
+
// the runtime compiles what reaches it, and this is what reaches it
|
|
564
|
+
const script = componentScript(source, message => this.warn(`jq79: ${filename}: ${message}`))
|
|
565
|
+
let url: string
|
|
566
|
+
if (config?.command === "serve") {
|
|
567
|
+
let devId = devIds.get(file)
|
|
568
|
+
if (devId === undefined) devIds.set(file, devId = devIds.size)
|
|
569
|
+
devScripts.set(String(devId), script)
|
|
570
|
+
// versioned, so an edit loads the new functions rather than a cached copy
|
|
571
|
+
url = JSON.stringify(`${config.base}${PRECOMPILED_PATH.slice(1)}?id=${devId}&v=${Date.now()}`)
|
|
572
|
+
} else {
|
|
573
|
+
const ref = this.emitFile({ type: "asset", name: `${basename(file)}.jq79.js`, source: script })
|
|
574
|
+
url = `import.meta.ROLLUP_FILE_URL_${ref}`
|
|
575
|
+
}
|
|
576
|
+
return { code: componentModule(source, include, filename, url), map: null }
|
|
409
577
|
},
|
|
410
578
|
}
|
|
411
579
|
}
|
package/dist/dom.d.ts
CHANGED
|
@@ -1,7 +1,25 @@
|
|
|
1
|
-
export
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
export type QueryOne = {
|
|
2
|
+
<K extends keyof HTMLElementTagNameMap>(selector: K): HTMLElementTagNameMap[K] | null;
|
|
3
|
+
<K extends keyof SVGElementTagNameMap>(selector: K): SVGElementTagNameMap[K] | null;
|
|
4
|
+
<E extends Element = HTMLElement>(selector: string): E | null;
|
|
5
|
+
};
|
|
6
|
+
export type QueryAll = {
|
|
7
|
+
<K extends keyof HTMLElementTagNameMap>(selector: K): HTMLElementTagNameMap[K][];
|
|
8
|
+
<K extends keyof SVGElementTagNameMap>(selector: K): SVGElementTagNameMap[K][];
|
|
9
|
+
<E extends Element = HTMLElement>(selector: string): E[];
|
|
10
|
+
};
|
|
11
|
+
export declare function $<K extends keyof HTMLElementTagNameMap>(selector: K): HTMLElementTagNameMap[K] | null;
|
|
12
|
+
export declare function $<K extends keyof SVGElementTagNameMap>(selector: K): SVGElementTagNameMap[K] | null;
|
|
13
|
+
export declare function $<E extends Element = HTMLElement>(selector: string): E | null;
|
|
14
|
+
export declare function $<K extends keyof HTMLElementTagNameMap>(el: Element, selector: K): HTMLElementTagNameMap[K] | null;
|
|
15
|
+
export declare function $<K extends keyof SVGElementTagNameMap>(el: Element, selector: K): SVGElementTagNameMap[K] | null;
|
|
16
|
+
export declare function $<E extends Element = HTMLElement>(el: Element, selector: string): E | null;
|
|
17
|
+
export declare function $$<K extends keyof HTMLElementTagNameMap>(selector: K): HTMLElementTagNameMap[K][];
|
|
18
|
+
export declare function $$<K extends keyof SVGElementTagNameMap>(selector: K): SVGElementTagNameMap[K][];
|
|
19
|
+
export declare function $$<E extends Element = HTMLElement>(selector: string): E[];
|
|
20
|
+
export declare function $$<K extends keyof HTMLElementTagNameMap>(el: Element, selector: K): HTMLElementTagNameMap[K][];
|
|
21
|
+
export declare function $$<K extends keyof SVGElementTagNameMap>(el: Element, selector: K): SVGElementTagNameMap[K][];
|
|
22
|
+
export declare function $$<E extends Element = HTMLElement>(el: Element, selector: string): E[];
|
|
5
23
|
export declare const $create: (tag: string, attrs?: Record<string, any>) => HTMLElement;
|
|
6
24
|
export declare function isSafeUrl(value: string): boolean;
|
|
7
25
|
export type AllowUrl = (url: URL, tag: string, attr: string) => boolean;
|
package/dist/html.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type HTMLElementNode = {
|
|
2
|
+
tag: string;
|
|
3
|
+
attrs: Record<string, string>;
|
|
4
|
+
children: HTMLNode[];
|
|
5
|
+
};
|
|
6
|
+
export type HTMLNode = HTMLElementNode | string;
|
|
7
|
+
export declare const CHARACTER_REFERENCE_RE: RegExp;
|
|
8
|
+
export declare const decodeReference: (text: string, whole: string, ref: string, offset: number, inAttribute: boolean) => string;
|
|
9
|
+
export declare const parseHTML: (input: string) => HTMLNode[];
|