jq79 0.7.0 → 0.7.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 +51 -6
- package/assets/blur-style-cover.svg +74 -0
- package/dev/vite.ts +122 -7
- package/dist/jq79.cjs +8 -8
- package/dist/jq79.cjs.map +1 -1
- package/dist/jq79.global.js +8 -8
- package/dist/jq79.global.js.map +1 -1
- package/dist/jq79.js +8 -8
- package/dist/jq79.js.map +1 -1
- package/dist/vite.cjs +59 -2
- package/dist/vite.cjs.map +1 -1
- package/dist/vite.js +50 -3
- package/dist/vite.js.map +1 -1
- package/package.json +2 -2
- package/src/jq79.ts +30 -0
package/README.md
CHANGED
|
@@ -10,12 +10,36 @@
|
|
|
10
10
|
[](#cdn)
|
|
11
11
|
[](#cdn)
|
|
12
12
|
|
|
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
|
|
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
|
|
|
15
|
-
|
|
15
|
+
**No compiler, no bundler, no dependencies.** A component is a `.html` file — the browser already knows how to fetch one. Drop the library from a CDN, serve your components from any static host, and the page works. No `npm install`, no build step, no config.
|
|
16
16
|
|
|
17
17
|
**[Take the tutorial →](https://jgermade.github.io/jq79/tutorial/)** — a handful of exercises you edit in the browser, with a live preview. Because there's no compiler, the tutorial runs the real library: the component you write is the component that mounts.
|
|
18
18
|
|
|
19
|
+
## No build step
|
|
20
|
+
|
|
21
|
+
A component is a `.html` file. The browser already knows how to fetch one — so
|
|
22
|
+
nothing has to happen to your components between writing them and serving them:
|
|
23
|
+
|
|
24
|
+
```html
|
|
25
|
+
<!doctype html>
|
|
26
|
+
<div id="app"></div>
|
|
27
|
+
|
|
28
|
+
<script type="module">
|
|
29
|
+
import { C79 } from "https://esm.sh/jq79"
|
|
30
|
+
|
|
31
|
+
C79.fetch("./app.html").mount("#app", { title: "Today" })
|
|
32
|
+
</script>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
That is the whole deployment. The library from a CDN, the component from your
|
|
36
|
+
own host, no build step in between. `C79` is `Component79` under a shorter name
|
|
37
|
+
— the same class, the same API.
|
|
38
|
+
|
|
39
|
+
While you're writing them, `npx jq79 dev` serves that folder and hot-reloads the
|
|
40
|
+
components you edit, keeping their state — no build step there either. See the
|
|
41
|
+
[dev server](docs/dev-server.md).
|
|
42
|
+
|
|
19
43
|
## Installation
|
|
20
44
|
|
|
21
45
|
### npm
|
|
@@ -25,7 +49,7 @@ npm install jq79
|
|
|
25
49
|
```
|
|
26
50
|
|
|
27
51
|
```js
|
|
28
|
-
import { Component79, $,
|
|
52
|
+
import { Component79, C79, $, $$, $reactive, $toRaw, parseComponent } from "jq79"
|
|
29
53
|
```
|
|
30
54
|
|
|
31
55
|
### Vite
|
|
@@ -73,11 +97,11 @@ Once published to npm, the package is automatically served by every major CDN
|
|
|
73
97
|
Pin a version in production: `https://cdn.jsdelivr.net/npm/jq79@0.1.0/...` (the GitHub Pages copy always tracks the latest release).
|
|
74
98
|
|
|
75
99
|
Which is enough for a whole page: the library from a CDN, the component from your
|
|
76
|
-
own host, no build step in between. `
|
|
77
|
-
|
|
100
|
+
own host, no build step in between. `C79` (short for `Component79`, the same class)
|
|
101
|
+
hands back a pending component you can mount right away:
|
|
78
102
|
|
|
79
103
|
```html
|
|
80
|
-
<main></main>
|
|
104
|
+
<main id="app"></main>
|
|
81
105
|
|
|
82
106
|
<script type="module">
|
|
83
107
|
import { C79 } from "https://jgermade.github.io/jq79/jq79.js"
|
|
@@ -91,10 +115,31 @@ under a shorter name) hands back a pending component you can mount right away:
|
|
|
91
115
|
`fetchAll([...])` fetches several at once instead. See
|
|
92
116
|
[loading remote components](docs/components.md#loading-remote-components).
|
|
93
117
|
|
|
118
|
+
The library also exports `parseComponent(source)` as a shorthand for
|
|
119
|
+
`new Component79(source)`, and `enableHotReload()` / `hotUpdate(filename, src)`
|
|
120
|
+
for custom dev setups — see the [dev server](docs/dev-server.md).
|
|
121
|
+
|
|
94
122
|
The source is small enough to read in a sitting: the core (parsing, rendering, components) lives in [`src/jq79.ts`](src/jq79.ts), with three leaf helpers — [`dom.ts`](src/dom.ts), [`reactive.ts`](src/reactive.ts) and [`transform.ts`](src/transform.ts). The published build is a single dependency-free file.
|
|
95
123
|
|
|
96
124
|
## Quick start
|
|
97
125
|
|
|
126
|
+
### From a CDN (no build step)
|
|
127
|
+
|
|
128
|
+
```html
|
|
129
|
+
<!doctype html>
|
|
130
|
+
<div id="app"></div>
|
|
131
|
+
|
|
132
|
+
<script type="module">
|
|
133
|
+
import { C79 } from "https://esm.sh/jq79"
|
|
134
|
+
|
|
135
|
+
C79.fetch("./app.html").mount("#app", { title: "Today" })
|
|
136
|
+
</script>
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
The component file is served as-is from your host — no bundler, no config.
|
|
140
|
+
|
|
141
|
+
### From npm
|
|
142
|
+
|
|
98
143
|
```js
|
|
99
144
|
import { Component79 } from "jq79"
|
|
100
145
|
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 600" width="600" height="600" role="img" aria-label="Four flat-style portraits on coloured quadrants with a lowercase wordmark">
|
|
2
|
+
<title>Four-quadrant pop portraits</title>
|
|
3
|
+
|
|
4
|
+
<defs>
|
|
5
|
+
<!-- shared facial features: eyes, brows, nose, mouth -->
|
|
6
|
+
<g id="features">
|
|
7
|
+
<path d="M112 128q14-9 30-2" fill="none" stroke="#1a1a1a" stroke-width="5" stroke-linecap="round"/>
|
|
8
|
+
<path d="M158 126q16-7 30 2" fill="none" stroke="#1a1a1a" stroke-width="5" stroke-linecap="round"/>
|
|
9
|
+
<ellipse cx="127" cy="146" rx="7" ry="8" fill="#1a1a1a"/>
|
|
10
|
+
<ellipse cx="173" cy="146" rx="7" ry="8" fill="#1a1a1a"/>
|
|
11
|
+
<path d="M150 150v26q0 6-8 8" fill="none" stroke="#1a1a1a" stroke-width="4" stroke-linecap="round"/>
|
|
12
|
+
<path d="M132 199q18 9 36 0" fill="none" stroke="#1a1a1a" stroke-width="5" stroke-linecap="round"/>
|
|
13
|
+
</g>
|
|
14
|
+
|
|
15
|
+
<!-- head, neck and outline, reused by every portrait -->
|
|
16
|
+
<g id="head">
|
|
17
|
+
<rect x="134" y="196" width="32" height="42" rx="14" fill="#e8bd97"/>
|
|
18
|
+
<ellipse cx="150" cy="142" rx="62" ry="76" fill="#f2cdaa" stroke="#1a1a1a" stroke-width="3"/>
|
|
19
|
+
<path d="M88 150q-9 12 0 22" fill="#f2cdaa" stroke="#1a1a1a" stroke-width="3"/>
|
|
20
|
+
<path d="M212 150q9 12 0 22" fill="#f2cdaa" stroke="#1a1a1a" stroke-width="3"/>
|
|
21
|
+
</g>
|
|
22
|
+
</defs>
|
|
23
|
+
|
|
24
|
+
<!-- ── top-left ─────────────────────────────────────────── -->
|
|
25
|
+
<g>
|
|
26
|
+
<rect width="300" height="300" fill="#1f5fa8"/>
|
|
27
|
+
<path d="M34 300q6-58 62-72l54-12 54 12q56 14 62 72z" fill="#2b2b33" stroke="#1a1a1a" stroke-width="3"/>
|
|
28
|
+
<use href="#head"/>
|
|
29
|
+
<path d="M88 142a62 76 0 0 1 124 0L212 116C200 94 180 86 150 90 120 94 100 98 88 118Z" fill="#3a2a1c" stroke="#1a1a1a" stroke-width="3"/>
|
|
30
|
+
<use href="#features"/>
|
|
31
|
+
<g fill="none" stroke="#1a1a1a" stroke-width="5">
|
|
32
|
+
<circle cx="127" cy="146" r="21"/>
|
|
33
|
+
<circle cx="173" cy="146" r="21"/>
|
|
34
|
+
<path d="M148 144h4M106 140l-16-4M194 140l16-4"/>
|
|
35
|
+
</g>
|
|
36
|
+
</g>
|
|
37
|
+
|
|
38
|
+
<!-- ── top-right ────────────────────────────────────────── -->
|
|
39
|
+
<g transform="translate(300 0)">
|
|
40
|
+
<rect width="300" height="300" fill="#c8202e"/>
|
|
41
|
+
<path d="M34 300q6-58 62-72l54-12 54 12q56 14 62 72z" fill="#1e1e24" stroke="#1a1a1a" stroke-width="3"/>
|
|
42
|
+
<use href="#head"/>
|
|
43
|
+
<path d="M88 142a62 76 0 0 1 124 0l2 26C200 132 178 128 150 126 122 124 100 132 86 168Z" fill="#16110e" stroke="#1a1a1a" stroke-width="3"/>
|
|
44
|
+
<use href="#features"/>
|
|
45
|
+
</g>
|
|
46
|
+
|
|
47
|
+
<!-- ── bottom-left ──────────────────────────────────────── -->
|
|
48
|
+
<g transform="translate(0 300)">
|
|
49
|
+
<rect width="300" height="300" fill="#2f8a4c"/>
|
|
50
|
+
<path d="M34 300q6-58 62-72l54-12 54 12q56 14 62 72z" fill="#dcdcd6" stroke="#1a1a1a" stroke-width="3"/>
|
|
51
|
+
<use href="#head"/>
|
|
52
|
+
<path d="M88 142a62 76 0 0 1 124 0l0-26-18 10-6-16-16 14-14-16-16 14-14-12-16 12-8-8Z" fill="#c98a3c" stroke="#1a1a1a" stroke-width="3" stroke-linejoin="round"/>
|
|
53
|
+
<use href="#features"/>
|
|
54
|
+
</g>
|
|
55
|
+
|
|
56
|
+
<!-- ── bottom-right ─────────────────────────────────────── -->
|
|
57
|
+
<g transform="translate(300 300)">
|
|
58
|
+
<rect width="300" height="300" fill="#f2c318"/>
|
|
59
|
+
<path d="M34 300q6-58 62-72l54-12 54 12q56 14 62 72z" fill="#2f4d8a" stroke="#1a1a1a" stroke-width="3"/>
|
|
60
|
+
<use href="#head"/>
|
|
61
|
+
<path d="M88 142a62 76 0 0 1 124 0l-2-30C196 96 176 88 150 90 124 92 102 96 90 118Z" fill="#8a6a3c" stroke="#1a1a1a" stroke-width="3"/>
|
|
62
|
+
<use href="#features"/>
|
|
63
|
+
<g fill="none" stroke="#1a1a1a" stroke-width="5">
|
|
64
|
+
<rect x="106" y="130" width="42" height="32" rx="6"/>
|
|
65
|
+
<rect x="152" y="130" width="42" height="32" rx="6"/>
|
|
66
|
+
<path d="M148 144h4M106 138l-16-4M194 138l16-4"/>
|
|
67
|
+
</g>
|
|
68
|
+
</g>
|
|
69
|
+
|
|
70
|
+
<!-- wordmark -->
|
|
71
|
+
<text x="300" y="332" text-anchor="middle"
|
|
72
|
+
font-family="'Helvetica Neue', Helvetica, Arial, sans-serif"
|
|
73
|
+
font-size="92" font-weight="700" letter-spacing="-2" fill="#ffffff">blur</text>
|
|
74
|
+
</svg>
|
package/dev/vite.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises"
|
|
2
2
|
import { relative } from "node:path"
|
|
3
|
-
import { preprocessCSS } from "vite"
|
|
3
|
+
import { preprocessCSS, transformWithEsbuild } from "vite"
|
|
4
4
|
import type { Plugin, ResolvedConfig } from "vite"
|
|
5
5
|
|
|
6
6
|
// Vite plugin: import .html single-file components as modules.
|
|
@@ -12,10 +12,11 @@ import type { Plugin, ResolvedConfig } from "vite"
|
|
|
12
12
|
// thing `await Component79.fetch(url)` resolves to, but bundled at build time
|
|
13
13
|
// instead of fetched at runtime. The component source is inlined verbatim, so
|
|
14
14
|
// a file keeps working unchanged if it's ever served from public/ and loaded
|
|
15
|
-
// with fetch instead - with one deliberate exception
|
|
16
|
-
// less/stylus/sass) is compiled to plain CSS here
|
|
17
|
-
// therefore only works through the bundler;
|
|
18
|
-
// reach the runtime uncompiled, which the runtime
|
|
15
|
+
// with fetch instead - with one deliberate exception, `lang`: <style lang="scss">
|
|
16
|
+
// (or less/stylus/sass) is compiled to plain CSS here, and <script lang="ts"> to
|
|
17
|
+
// plain JS. A component using `lang` therefore only works through the bundler;
|
|
18
|
+
// loaded with fetch() it would reach the runtime uncompiled, which the runtime
|
|
19
|
+
// warns about.
|
|
19
20
|
//
|
|
20
21
|
// Only .html files imported from other modules are claimed; entry points
|
|
21
22
|
// (index.html) have no importer and imports carrying an explicit query
|
|
@@ -32,7 +33,10 @@ export interface Jq79PluginOptions {
|
|
|
32
33
|
// Vite's own html handling (entries, asset pipeline) leaves them alone
|
|
33
34
|
const COMPONENT_QUERY = "?jq79"
|
|
34
35
|
|
|
35
|
-
|
|
36
|
+
// a <script> block with its attribute string, so `lang` can be read and the
|
|
37
|
+
// body replaced - the same shape as STYLE_BLOCK_RE below, quote-aware so a
|
|
38
|
+
// ">" inside an attribute value (`:setup="{ n = a > 1 }"`) doesn't end the tag
|
|
39
|
+
const SCRIPT_BLOCK_RE = /<script((?:"[^"]*"|'[^']*'|[^>"'])*)>([\s\S]*?)<\/script\s*>/gi
|
|
36
40
|
// import("...") with a literal specifier, tried at word boundaries the
|
|
37
41
|
// scanner below reaches (which is what skips $__import and foo.import(...))
|
|
38
42
|
const IMPORT_CALL_RE = /import\s*\(\s*(["'])([^"'\n]+?)\1\s*\)/y
|
|
@@ -95,7 +99,7 @@ const isExternalUrl = (spec: string) => /^[a-z][a-z0-9+.-]*:/i.test(spec) || spe
|
|
|
95
99
|
// are .html specifiers the plugin wouldn't claim as components
|
|
96
100
|
const hoistableImports = (source: string, include: RegExp): string[] => {
|
|
97
101
|
const specifiers = new Set<string>()
|
|
98
|
-
for (const [, script] of source.matchAll(SCRIPT_BLOCK_RE)) {
|
|
102
|
+
for (const [, , script] of source.matchAll(SCRIPT_BLOCK_RE)) {
|
|
99
103
|
for (const spec of importSpecifiers(script)) {
|
|
100
104
|
if (isExternalUrl(spec)) continue
|
|
101
105
|
if (isHtmlUrl(spec) && !include.test(spec)) continue // html left to runtime fetch
|
|
@@ -186,6 +190,116 @@ const compileStyleBlocks = async (
|
|
|
186
190
|
return out + source.slice(last)
|
|
187
191
|
}
|
|
188
192
|
|
|
193
|
+
// languages a <script lang> is compiled from. Anything else is left as written
|
|
194
|
+
// for the runtime to warn about, rather than guessed at
|
|
195
|
+
const TS_LANGS = new Set(["ts", "typescript"])
|
|
196
|
+
|
|
197
|
+
type ViteTransform = (code: string, id: string, options?: unknown) => Promise<{ code: string }>
|
|
198
|
+
|
|
199
|
+
// what strips the types: vite's own transform, so the plugin carries no
|
|
200
|
+
// compiler of its own. *Which* transform is a version question, and neither
|
|
201
|
+
// answer covers the peer range (vite >= 5) alone - transformWithOxc is the one
|
|
202
|
+
// vite is moving to but only exists from vite 7, while transformWithEsbuild is
|
|
203
|
+
// deprecated under vite 8 and throws there unless esbuild is installed
|
|
204
|
+
// separately. So it is looked up at call time: oxc where it exists, esbuild on
|
|
205
|
+
// the older versions that ship it.
|
|
206
|
+
//
|
|
207
|
+
// Both drop unused value imports by default, because a TS transform can't tell
|
|
208
|
+
// a type-only import from an unused one. That would be silent damage here: a
|
|
209
|
+
// factory script's `import Row from "./row.html"` would vanish, taking with it
|
|
210
|
+
// the specifier hoistableImports needs to pull the child into the bundle. The
|
|
211
|
+
// flags below are what keep it - only `import type` is erased
|
|
212
|
+
const stripTypes = async (ts: string, file: string): Promise<string> => {
|
|
213
|
+
const vite = (await import("vite")) as unknown as { transformWithOxc?: ViteTransform }
|
|
214
|
+
const { code } = vite.transformWithOxc
|
|
215
|
+
? await vite.transformWithOxc(ts, file, { lang: "ts", typescript: { onlyRemoveTypeImports: true } })
|
|
216
|
+
: await transformWithEsbuild(ts, file, {
|
|
217
|
+
loader: "ts",
|
|
218
|
+
tsconfigRaw: { compilerOptions: { verbatimModuleSyntax: true } },
|
|
219
|
+
})
|
|
220
|
+
// a script whose only imports were `import type` comes back marked as a
|
|
221
|
+
// module. `export {}` exports nothing, and it is a SyntaxError inside the
|
|
222
|
+
// Function body the runtime compiles a script into
|
|
223
|
+
return code.replace(/^[ \t]*export\s*\{\s*\}\s*;?[ \t]*$/m, "")
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// the `:setup` attribute, when it carries a value (a bare `:setup` is the
|
|
227
|
+
// closed signature and can't be typed)
|
|
228
|
+
const SETUP_ATTR_RE = /(:setup\s*=\s*)(?:"([^"]*)"|'([^']*)')/i
|
|
229
|
+
// the arrow body the signature is wrapped in, so the parameter list can be
|
|
230
|
+
// found again in the output without matching brackets
|
|
231
|
+
const SIGNATURE_MARKER = "__jq79_signature__"
|
|
232
|
+
|
|
233
|
+
// 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 `lang="ts"` has to
|
|
235
|
+
// mean the same thing on both halves of the block, or a typed signature either
|
|
236
|
+
// survives into a component the plugin just promised was JS or, for `_: Props`,
|
|
237
|
+
// stops reading as a signature at all.
|
|
238
|
+
//
|
|
239
|
+
// It goes through the same transform as everything else, wrapped as a
|
|
240
|
+
// parameter list, rather than being cut with a scanner of its own: the
|
|
241
|
+
// annotation can sit on the pattern (`{ a }: Props`), on the permissive `_`, or
|
|
242
|
+
// inside a default (`{ step = 1 as number }`, which the runtime would evaluate
|
|
243
|
+
// and silently drop), and telling those apart is a parser's job. Both transforms
|
|
244
|
+
// hand back `(<params>) => <marker>` on one line, parens intact
|
|
245
|
+
const stripSignatureTypes = async (value: string, file: string): Promise<string> => {
|
|
246
|
+
const compiled = await stripTypes(`(${value}) => ${SIGNATURE_MARKER}`, file)
|
|
247
|
+
const marker = compiled.indexOf(SIGNATURE_MARKER)
|
|
248
|
+
if (marker === -1) return value // not the shape expected: leave it as written
|
|
249
|
+
const head = compiled.slice(0, marker).trimEnd()
|
|
250
|
+
const params = (head.endsWith("=>") ? head.slice(0, -2) : head).trim()
|
|
251
|
+
return params.startsWith("(") && params.endsWith(")") ? params.slice(1, -1).trim() : params
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// the signature back into a double-quoted attribute. Only `"` needs escaping:
|
|
255
|
+
// the transforms normalize string literals to double quotes, so a default the
|
|
256
|
+
// author wrote as `'x'` comes back as `"x"` and would end the attribute early.
|
|
257
|
+
// `&` is deliberately left alone - `&&` in a default is not an entity and
|
|
258
|
+
// survives the parse, while escaping it would double-encode a source that
|
|
259
|
+
// already wrote `"`
|
|
260
|
+
const quoteAttrValue = (value: string) => value.replace(/"/g, """)
|
|
261
|
+
|
|
262
|
+
// compiles <script lang="ts"> blocks to plain JS, so the runtime only ever sees
|
|
263
|
+
// JS - the same deal <style lang="scss"> gets, for a sharper reason. The setup
|
|
264
|
+
// scanner is not a parser: `let count: number = 0` reaching it is not a syntax
|
|
265
|
+
// error but a labeled statement that assigns to `number`, so it runs and leaves
|
|
266
|
+
// `count` undeclared. `lang` is dropped from the emitted tag and every other
|
|
267
|
+
// attribute (`:setup`, `:mounted`) is left as written.
|
|
268
|
+
//
|
|
269
|
+
// This runs before hoistableImports reads the source, so an `import type`
|
|
270
|
+
// specifier is already gone by the time the plugin decides what to bundle
|
|
271
|
+
const compileScriptBlocks = async (source: string, file: string): Promise<string> => {
|
|
272
|
+
const blocks = [...source.matchAll(SCRIPT_BLOCK_RE)]
|
|
273
|
+
const compiled = await Promise.all(
|
|
274
|
+
blocks.map(async ([, attrs, content]) => {
|
|
275
|
+
const lang = attrs.match(LANG_ATTR_RE)
|
|
276
|
+
const name = (lang?.[1] ?? lang?.[2] ?? lang?.[3])?.toLowerCase()
|
|
277
|
+
if (!name || !TS_LANGS.has(name)) return null
|
|
278
|
+
|
|
279
|
+
let rest = attrs.replace(LANG_ATTR_RE, "").trimEnd()
|
|
280
|
+
const setup = rest.match(SETUP_ATTR_RE)
|
|
281
|
+
if (setup) {
|
|
282
|
+
const signature = await stripSignatureTypes(setup[2] ?? setup[3], `${file}.signature.ts`)
|
|
283
|
+
// a function replacement, not a string: `$&` and friends are live in a
|
|
284
|
+
// replacement string and a default value is arbitrary source
|
|
285
|
+
rest = rest.replace(SETUP_ATTR_RE, () => `${setup[1]}"${quoteAttrValue(signature)}"`)
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
return { attrs: rest, js: await stripTypes(content, `${file}.ts`) }
|
|
289
|
+
})
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
let out = ""
|
|
293
|
+
let last = 0
|
|
294
|
+
blocks.forEach((block, i) => {
|
|
295
|
+
const done = compiled[i]
|
|
296
|
+
if (!done) return
|
|
297
|
+
out += source.slice(last, block.index) + `<script${done.attrs}>${done.js}</script>`
|
|
298
|
+
last = block.index + block[0].length
|
|
299
|
+
})
|
|
300
|
+
return out + source.slice(last)
|
|
301
|
+
}
|
|
302
|
+
|
|
189
303
|
// the emitted module. Literal import("...") specifiers found in the
|
|
190
304
|
// component's scripts become real module imports, handed to Component79 as a
|
|
191
305
|
// resolution map: at runtime $__import checks the map before falling back to
|
|
@@ -284,6 +398,7 @@ export function jq79(options: Jq79PluginOptions = {}): Plugin {
|
|
|
284
398
|
const file = id.slice(0, -COMPONENT_QUERY.length)
|
|
285
399
|
|
|
286
400
|
let source = await readFile(file, "utf8")
|
|
401
|
+
source = await compileScriptBlocks(source, file)
|
|
287
402
|
if (config) source = await compileStyleBlocks(source, file, config, dep => this.addWatchFile(dep))
|
|
288
403
|
|
|
289
404
|
// the runtime names the component's setup scripts after this, so devtools
|