@lokascript/htmx-adapter 3.0.0 → 3.1.0
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/CHANGELOG.md +183 -3
- package/README.md +44 -16
- package/dist/htmx-i18n.global.js +1 -1
- package/dist/htmx-i18n.global.js.map +1 -1
- package/dist/index.cjs +207 -54
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +141 -31
- package/dist/index.d.ts +141 -31
- package/dist/index.js +198 -53
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/browser.ts +20 -5
- package/src/canonicalize.ts +131 -45
- package/src/extension.ts +175 -26
- package/src/hx-on.ts +153 -43
- package/src/index.ts +15 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,183 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [3.1.0] - 2026-09-04
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **`hyperscript.use(frontEnd)`** — register the multilingual front-end
|
|
15
|
+
`compile()` consults for a non-English program (and `toLSE`/`fromLSE` for
|
|
16
|
+
a semantic node and a renderer). The contract is `FrontEnd` in
|
|
17
|
+
`@hyperfixi/core` (`parseToAST`, optional `parse`/`render`);
|
|
18
|
+
`@lokascript/semantic` satisfies it through
|
|
19
|
+
`createBridgeFrontEnd(new SemanticGrammarBridge())` from
|
|
20
|
+
`@hyperfixi/core/multilingual`. The full browser bundle registers it at
|
|
21
|
+
boot. The library entry registers nothing and, through 3.x, builds the same
|
|
22
|
+
bridge lazily on the first non-English compile — so nothing changes for a
|
|
23
|
+
consumer with `@lokascript/semantic` installed. **4.0 will remove that
|
|
24
|
+
default:** a non-English `compile()` with no front-end registered will
|
|
25
|
+
return the same result the core-parser-only path returns today, and
|
|
26
|
+
`@lokascript/semantic` becomes an optional peer. Engine-migration plan
|
|
27
|
+
Arc 1, steps 2 and 3.
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
- **Language server: valid non-English code no longer gets a `parse-error`.**
|
|
32
|
+
In `lokascript`/`hyperscript-i18n` mode the server ran core's English
|
|
33
|
+
parser on every region regardless of what the semantic front-end said, so
|
|
34
|
+
`al hacer clic alternar .activo` (accepted at 0.89 confidence) was published
|
|
35
|
+
as `Unexpected token: hacer`. Core's errors are now suppressed for a region
|
|
36
|
+
the front-end accepted in a non-English language. The integration suite
|
|
37
|
+
that claimed to cover this had been discarding every notification; it now
|
|
38
|
+
records `publishDiagnostics` and asserts on it.
|
|
39
|
+
|
|
40
|
+
- **Language server: settings actually reach the server.** Neither VS Code
|
|
41
|
+
extension set `synchronize.configurationSection`, so the client pushed
|
|
42
|
+
`settings: null` and every `mode`/`language`/`maxDiagnostics` value was
|
|
43
|
+
ignored; `initializationOptions` was ignored too. The server now reads
|
|
44
|
+
`initializationOptions`, a pushed `lokascript`/`hyperscript` section, or
|
|
45
|
+
pulls `workspace/configuration` when the push is empty — and merges partial
|
|
46
|
+
objects over the defaults instead of replacing them (a bare
|
|
47
|
+
`{ maxDiagnostics }` used to leave `language` undefined and error on every
|
|
48
|
+
document). Both extensions push their section, and the LokaScript one now
|
|
49
|
+
contributes `lokascript.mode`.
|
|
50
|
+
|
|
51
|
+
- **Both VS Code extension server bundles build and run.** The standalone
|
|
52
|
+
hyperscript extension's `bundle:server` had failed since #1016 (no shim for
|
|
53
|
+
`@hyperfixi/core/multilingual`); the LokaScript extension externalized
|
|
54
|
+
`@hyperfixi/core` without shipping it, so every install ran with core
|
|
55
|
+
absent ("diagnostics and hover degraded"). Core is bundled in now, and CI
|
|
56
|
+
runs both bundle scripts.
|
|
57
|
+
|
|
58
|
+
- **`@lokascript/language-server` declares what it imports.**
|
|
59
|
+
`@lokascript/semantic` was an optional peer but a static import (the
|
|
60
|
+
server crashed at startup without it); `@lokascript/framework` was
|
|
61
|
+
undeclared and got inlined together with `@lokascript/intent` (261 KB) —
|
|
62
|
+
and without `--clean`, `dist/` shipped every previous build's chunk (three
|
|
63
|
+
dead ones, 63% of the tarball). Both are regular dependencies now, the
|
|
64
|
+
build cleans, the 20-byte `--dts` output is gone, and `npm test` rebuilds
|
|
65
|
+
the server it spawns.
|
|
66
|
+
|
|
67
|
+
- **Hyperscript-compat mode stops flagging portable code.** `as Int`, `Float`,
|
|
68
|
+
`JSON`, `Date`, `Set`, `Map` are upstream conversions (the extension list
|
|
69
|
+
is now `Math`/`Values`, the ones core registers and upstream lacks);
|
|
70
|
+
`my.x`/`your.x` evaluate upstream (only `its.` is flagged); and the
|
|
71
|
+
extension-command scan no longer fires inside `y.replace(`, `arr.push(`,
|
|
72
|
+
string literals, `:process`, `--` comments, or the upstream trailing
|
|
73
|
+
`… unless …` modifier.
|
|
74
|
+
|
|
75
|
+
- **Language-server false positives and misses.** A `.hs` file containing a
|
|
76
|
+
query literal like `<li/>` was classified as HTML and lost every feature;
|
|
77
|
+
possessives after `)`/`]`/`>` and apostrophes in `--` comments raised
|
|
78
|
+
`unmatched-quote`; the outline reported the `on` in `toggle .x on me` as a
|
|
79
|
+
handler and `.init` as an init block; `install Foo(args)` produced
|
|
80
|
+
overlapping rename edits VS Code rejects; `on draggable:start` became a
|
|
81
|
+
variable named `:start`; and keyword boundaries were ASCII `\b`, so
|
|
82
|
+
definitions and symbols were silently absent in the ten non-Latin
|
|
83
|
+
languages. Completion-context and document-symbol logic moved out of the
|
|
84
|
+
entry script into `completion-context.ts` / `document-symbols.ts` so the
|
|
85
|
+
unit suite tests the shipped code rather than the hand-copied twins it
|
|
86
|
+
used to carry.
|
|
87
|
+
|
|
88
|
+
- **MCP `get_diagnostics`, `get_completions` and `get_document_symbols` read
|
|
89
|
+
the AST.** The bridge guarded those paths on helper names core never
|
|
90
|
+
exported, so all three had only ever taken their token-based fallback.
|
|
91
|
+
They now read the interchange: the core parser's own errors become
|
|
92
|
+
diagnostics, completions inside a command offer that command's argument
|
|
93
|
+
shapes, symbols carry their commands as children, and a document with
|
|
94
|
+
several `end`-terminated handlers keeps all of them.
|
|
95
|
+
|
|
96
|
+
- **`hyperscript.process()` honours the event grammar.** The API-side DOM
|
|
97
|
+
processor carried its own listener installer for `on …` attributes and
|
|
98
|
+
silently dropped what the runtime's installer handles: `on
|
|
99
|
+
click[event.shiftKey]` fired on a plain click, `on mouseenter or click` never
|
|
100
|
+
fired on mouseenter, `on click from document` never fired. Every browser
|
|
101
|
+
bundle already went through the runtime; `process()` now does too, and
|
|
102
|
+
`config.logAll` moved into the runtime so both paths log. Pinned by
|
|
103
|
+
`api/dom-processor.test.ts`, which runs each case on both paths.
|
|
104
|
+
|
|
105
|
+
- **The browser bundles' attribute processor now runs the element lifecycle,
|
|
106
|
+
and its lazy stub only takes the shapes it can run faithfully.** Measured
|
|
107
|
+
side by side with `hyperscript.process()`: the bundle path never dispatched
|
|
108
|
+
`hyperscript:before:init` / `hyperscript:after:init` and never set
|
|
109
|
+
`data-hyperscript-powered` — both lived only in the API processor, so a
|
|
110
|
+
morph engine reading the marker saw nothing on a bundle-processed page, and
|
|
111
|
+
canceling `before:init` was impossible there. It also compiled every
|
|
112
|
+
attribute as English; it now detects the element's language (`data-lang`,
|
|
113
|
+
closest `lang`, the document's) the way `process()` always did. And the
|
|
114
|
+
`lazyParsing` stub read only the event NAME from the header, so for the
|
|
115
|
+
first event it ignored a filter (`on click[event.shiftKey]` fired on a
|
|
116
|
+
plain click), listened for the first name of an `or` list only, and never
|
|
117
|
+
saw `from <target>` at all; a header with a filter, `(args)`, `or`, `from`,
|
|
118
|
+
`elsewhere`, `queue`, `debounced`, `throttled` or `in` is now processed
|
|
119
|
+
eagerly. `dom/processor-parity.test.ts` runs every row on all three paths
|
|
120
|
+
(API, eager, lazy) and is the gate the DOM-processor collapse landed under.
|
|
121
|
+
|
|
122
|
+
- **`hyperscript.process()` and the browser bundles share one DOM
|
|
123
|
+
processor.** `process()` and `cleanup()` now run on the attribute
|
|
124
|
+
processor every bundle uses, which receives its compiler and runtime from
|
|
125
|
+
the API by injection instead of importing it (the `dom -> api` layering
|
|
126
|
+
edge is gone). What changes for `process()` callers, all of it upstream
|
|
127
|
+
parity: an element is initialized once however many times it is passed or
|
|
128
|
+
scanned (it used to re-install on every call); `<script
|
|
129
|
+
type="text/hyperscript">` tags inside the tree are processed, with or
|
|
130
|
+
without `for=`, before the elements that may `install` what they define;
|
|
131
|
+
each element gets a non-bubbling `load` event once its program has run,
|
|
132
|
+
and an unparseable attribute is reported through `console.error`, the
|
|
133
|
+
`hyperfixi:compile-error` event and `config.onCompileError` (it was logged
|
|
134
|
+
at debug level only). And `cleanup(container)` now un-marks every processed
|
|
135
|
+
descendant — it stripped the root's `data-hyperscript-powered` only — and
|
|
136
|
+
drops a lazy stub still waiting on an element, so cleanup-then-process
|
|
137
|
+
re-initializes the tree once. A second scan in `lazyParsing` mode no longer
|
|
138
|
+
registers a second stub.
|
|
139
|
+
The API-side processor module is deleted; the multilingual bundle's own
|
|
140
|
+
copy of the language walk now calls the shared one, narrowed to the
|
|
141
|
+
languages that bundle ships; and a subtree the `MutationObserver` picks up
|
|
142
|
+
takes the same entry as `process()`.
|
|
143
|
+
|
|
144
|
+
- **`set *<css-property>` writes inline style in every spelling upstream
|
|
145
|
+
accepts.** `set *opacity to 0.5`, `set *opacity of me to 0.5` and
|
|
146
|
+
`set *background-color of me to "red"` were silent no-ops and
|
|
147
|
+
`set the *opacity of me to 0.5` threw; only the possessive `set my *opacity`
|
|
148
|
+
worked. The tokenizer classes `*opacity` as a selector (for `measure`), and
|
|
149
|
+
`set`'s parser now re-types it into the identifier property the runtime's
|
|
150
|
+
style rungs already handle, so one path serves all of them — bare, `of
|
|
151
|
+
<target>`, `the … of <target>`, `<target>'s`. Ten rows pinned against
|
|
152
|
+
`_hyperscript` 0.9.93.
|
|
153
|
+
|
|
154
|
+
- **`require('@hyperfixi/core')` returned `{}`, and the subpath `require`s
|
|
155
|
+
threw — since `"type": "module"`, on 3.0.0 too.** Every `exports.*.require`
|
|
156
|
+
and `main` pointed at a `.js` file built as CommonJS, which Node reads as
|
|
157
|
+
ESM under that flag. The CJS outputs are `.cjs` now (main entry, the 21
|
|
158
|
+
subpaths, the parser modules); the `import` conditions are unchanged. The
|
|
159
|
+
bare-Node check in CI now `require()`s the entries as well as importing
|
|
160
|
+
them, which is the check that would have caught this. Found by the
|
|
161
|
+
end-to-end probe that landed Arc 1's build half.
|
|
162
|
+
|
|
163
|
+
### Changed
|
|
164
|
+
|
|
165
|
+
- **`@hyperfixi/core`'s library entry no longer bundles the multilingual
|
|
166
|
+
front-end.** `dist/index.mjs`/`index.js` had `@lokascript/semantic`,
|
|
167
|
+
`@lokascript/intent` and `@lokascript/framework` inlined whole (3.33 MB, zero
|
|
168
|
+
dynamic imports left); they are external now (1.04 MB) and load on the first
|
|
169
|
+
non-English `compileAsync`, the way the source always said they would. No
|
|
170
|
+
API change: semantic and intent stay `dependencies`, framework stays an
|
|
171
|
+
optional peer — and is now genuinely one. A consumer that imported both core
|
|
172
|
+
and semantic no longer holds two copies of semantic. `dist/index.min.js`
|
|
173
|
+
(UMD, not in `exports`) stays self-contained. Gated by
|
|
174
|
+
`scripts/check-node-import.mjs` on the sourcemap. Engine-migration plan Arc 1
|
|
175
|
+
step 2, build half.
|
|
176
|
+
|
|
177
|
+
- **`@lokascript/semantic` drops its `asyncSchema`.** Core deleted the `async`
|
|
178
|
+
command in 3.0.0 (#1102); the semantic front-end never emitted the action
|
|
179
|
+
anyway — its `stripAsyncModifier` pass removes the keyword (in all 24
|
|
180
|
+
languages) and parses the following command, so `async fetch /api as json`
|
|
181
|
+
is a `fetch`. The schema, its registration, the `'async'` `ActionType`
|
|
182
|
+
member and the AST-builder mapper row are gone. Kept on purpose: every
|
|
183
|
+
profile's `keywords.async` (the stripper matches on it) and the
|
|
184
|
+
`'then' | 'and' | 'async'` chain type (a different concept). The IR-level
|
|
185
|
+
`async all/race` node lives in `@lokascript/intent` and is untouched.
|
|
186
|
+
|
|
10
187
|
## [3.0.0] - 2026-09-03
|
|
11
188
|
|
|
12
189
|
Full notes: [GitHub Releases](https://github.com/codetalcott/hyperfixi/releases/tag/v3.0.0).
|
|
@@ -14,9 +191,11 @@ Full notes: [GitHub Releases](https://github.com/codetalcott/hyperfixi/releases/
|
|
|
14
191
|
One major, two themes. `@lokascript/i18n`'s grammar transformer is gone,
|
|
15
192
|
because `@lokascript/semantic` overtook it on every row of the translation
|
|
16
193
|
corpus (#973–#1001). And the engine-migration plan
|
|
17
|
-
(`docs-internal/ENGINE_MIGRATION_PLAN.md`)
|
|
194
|
+
(`docs-internal/ENGINE_MIGRATION_PLAN.md`) closed its last arc, which deleted
|
|
18
195
|
the exported dead code it had been carrying and collapsed the browser bundle
|
|
19
|
-
lineup to two names (#1099–#1105).
|
|
196
|
+
lineup to two names (#1099–#1105). One arc is still open: Arc 1's steps 2
|
|
197
|
+
and 3 — `@hyperfixi/core`'s library entry still bundles the semantic
|
|
198
|
+
front-end (recorded in the plan's History, 2026-09-03).
|
|
20
199
|
|
|
21
200
|
### ⚠ BREAKING
|
|
22
201
|
|
|
@@ -514,7 +693,8 @@ _Synchronized version release. See git history for details._
|
|
|
514
693
|
- npm access token stored in GitHub Secrets
|
|
515
694
|
- 2FA recommended for npm organization
|
|
516
695
|
|
|
517
|
-
[Unreleased]: https://github.com/codetalcott/hyperfixi/compare/
|
|
696
|
+
[Unreleased]: https://github.com/codetalcott/hyperfixi/compare/v3.1.0...HEAD
|
|
697
|
+
[3.1.0]: https://github.com/codetalcott/hyperfixi/compare/v3.0.0...v3.1.0
|
|
518
698
|
[2.10.0]: https://github.com/codetalcott/hyperfixi/compare/v2.9.0...v2.10.0
|
|
519
699
|
[2.9.0]: https://github.com/codetalcott/hyperfixi/compare/v2.8.0...v2.9.0
|
|
520
700
|
[2.8.0]: https://github.com/codetalcott/hyperfixi/compare/v2.5.1...v2.8.0
|
package/README.md
CHANGED
|
@@ -39,7 +39,7 @@ All three consumers share one vocabulary source: the generated modules under
|
|
|
39
39
|
|
|
40
40
|
htmx v4 has no hook to override attribute-name resolution — core reads `hx-get`
|
|
41
41
|
literally. We maintain a proposed upstream seam plus a working ~30-line
|
|
42
|
-
reference patch against
|
|
42
|
+
reference patch against 4.0.0 and a ready-to-file Discussion draft
|
|
43
43
|
([docs/UPSTREAM_HOOK_PROPOSAL.md](docs/UPSTREAM_HOOK_PROPOSAL.md)); against a
|
|
44
44
|
patched build, `installResolverMode(htmx)` localizes with **zero DOM
|
|
45
45
|
mutation** (proven in the e2e suite). Until the seam exists upstream, the
|
|
@@ -51,19 +51,27 @@ attribute is copied to its canonical name on the same element
|
|
|
51
51
|
_before_ the htmx `<script>` so the sweep listener registers first
|
|
52
52
|
(loka-js's "orchestrator before libraries" rule).
|
|
53
53
|
2. **Swapped-in content** — a registered htmx v4 extension
|
|
54
|
-
(`lokascript-i18n`) canonicalizes each subtree in
|
|
55
|
-
`htmx_before_process_node
|
|
56
|
-
fallback covers htmx v1/v2.)
|
|
54
|
+
(`lokascript-i18n`) canonicalizes each subtree in `htmx_before_process`
|
|
55
|
+
(`htmx_before_process_node` is kept as an inert alias). (A best-effort
|
|
56
|
+
`defineExtension`/`onEvent` fallback covers htmx v1/v2.)
|
|
57
57
|
|
|
58
58
|
Guarantees, mirroring loka-js where the mechanism allows:
|
|
59
59
|
|
|
60
60
|
- **Authored attributes are never removed or rewritten** — devtools shows what
|
|
61
|
-
the author wrote.
|
|
61
|
+
the author wrote. Documented exceptions: an author-written canonical
|
|
62
62
|
`hx-trigger` whose _value_ uses localized event names (`hx-trigger="clic"`)
|
|
63
63
|
is translated in place (idempotently), since there is no separate canonical
|
|
64
|
-
target; and canonical-named `hx-on:*`
|
|
65
|
-
|
|
66
|
-
|
|
64
|
+
target; and in opt-in executor mode a canonical-named `hx-on:*` attr may be
|
|
65
|
+
removed so htmx never JS-evals its hyperscript body. On htmx **v4 with the
|
|
66
|
+
extension accepted** that removal does not happen: the extension cancels
|
|
67
|
+
the node's `htmx:before:on:init` instead and the claimed attr stays
|
|
68
|
+
authored-verbatim (removal survives only as a per-node fallback when the
|
|
69
|
+
element also carries an hx-on form htmx must still bind — the legacy
|
|
70
|
+
composite `hx-on="…"`, the `config.prefix` spelling, a custom
|
|
71
|
+
`metaCharacter`). On htmx **v2**, or when v4 **rejected** the registration
|
|
72
|
+
(`htmx.config.extensions` allowlist), or with no htmx at all, the attr is
|
|
73
|
+
removed at claim time — the only guard that needs no hook. Adapter-created
|
|
74
|
+
canonical siblings are not authored and are always cleaned up.
|
|
67
75
|
- **An existing canonical attribute always wins** — `hx-get` is never
|
|
68
76
|
overwritten by `hx-obtener`.
|
|
69
77
|
- **No vocab loaded → no-op.** Stock htmx pages pay nothing.
|
|
@@ -141,15 +149,35 @@ reliable detection):
|
|
|
141
149
|
unchanged.
|
|
142
150
|
- **Localized-named attrs** (`hx-en:clic`) stay verbatim in the DOM and get
|
|
143
151
|
no canonical sibling — htmx never recognized them anyway.
|
|
144
|
-
- **Canonical-named attrs** (`hx-on:click`)
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
152
|
+
- **Canonical-named attrs** (`hx-on:click`) must be kept away from htmx's own
|
|
153
|
+
binder — left bound, htmx would eval the hyperscript body as JS, giving a
|
|
154
|
+
console error plus a double-execution attempt on every fire. A claim
|
|
155
|
+
_records_ the attribute; what keeps htmx off it depends on the runtime:
|
|
156
|
+
- **htmx v4, extension accepted** (the `registerWith` result, not the API's
|
|
157
|
+
shape): no mutation. The extension's `htmx:before:on:init` hook consults
|
|
158
|
+
the record per node and cancels htmx's binding; the authored attribute
|
|
159
|
+
stays in the DOM, and `htmx.process(elt, true)` after editing it runs the
|
|
160
|
+
new body. If the node also carries an hx-on form htmx must bind and the
|
|
161
|
+
adapter never claims (composite `hx-on="event -> code"`, the
|
|
162
|
+
`config.prefix` spelling, a custom `metaCharacter`), per-node cancellation
|
|
163
|
+
would kill that too, so the hook removes the claimed canonical attrs and
|
|
164
|
+
lets htmx bind the rest.
|
|
165
|
+
- **htmx v2** (2.0.10 binds `hx-on` before it fires `beforeProcessNode`, so
|
|
166
|
+
there is no pre-bind seam), **a rejected v4 registration** (the adapter
|
|
167
|
+
warns, naming `htmx.config.extensions`), or **no htmx**: the attr is
|
|
168
|
+
removed at claim time — the documented exception to the never-mutate
|
|
169
|
+
rule, and the only guard that does not depend on a hook being installed.
|
|
170
|
+
- Wiring `createExtension()` into `htmx.registerExtension` yourself? Call
|
|
171
|
+
`setNeutralizeOnClaim(false)` after it succeeds to get the v4 behaviour.
|
|
148
172
|
- The `hx-on::after-swap` shorthand maps to the `htmx:` event namespace and
|
|
149
173
|
works unchanged (the listener hears htmx's real CustomEvents).
|
|
150
174
|
- Re-sweeps never stack duplicate listeners (claims are keyed per element by
|
|
151
175
|
resolved event name), and an executor configured _after_ the initial sweep
|
|
152
|
-
triggers a healing re-sweep.
|
|
176
|
+
triggers a healing re-sweep. The initial sweep waits for `DOMContentLoaded`
|
|
177
|
+
to have fired (a `load` listener covers a script injected in between), and
|
|
178
|
+
the browser entry registers the extension before it re-detects
|
|
179
|
+
`_hyperscript`, so a late `_hyperscript`, a `defer`ed adapter, or a
|
|
180
|
+
module script all see the same behaviour as the recommended order.
|
|
153
181
|
|
|
154
182
|
A side benefit: executor mode never uses `eval`, so hyperscript bodies work on
|
|
155
183
|
CSP-restricted pages where htmx's native `hx-on` JS eval cannot.
|
|
@@ -169,7 +197,7 @@ only the suffix is localized. The `_=` attribute is
|
|
|
169
197
|
## Tests
|
|
170
198
|
|
|
171
199
|
```bash
|
|
172
|
-
npm test --prefix packages/htmx-adapter # vitest, jsdom
|
|
200
|
+
npm test --prefix packages/htmx-adapter # vitest, jsdom
|
|
173
201
|
npm run test:browser --prefix packages/htmx-adapter # Playwright e2e (build dist first)
|
|
174
202
|
```
|
|
175
203
|
|
|
@@ -177,11 +205,11 @@ The unit suite includes a reuse guard that loads every generated
|
|
|
177
205
|
`packages/core/vocab/htmx/{lang}.js` module against this adapter's registry, so
|
|
178
206
|
generator drift fails here rather than in a browser.
|
|
179
207
|
|
|
180
|
-
The Playwright suite drives **real vendored libraries** — htmx `4.0.0
|
|
208
|
+
The Playwright suite drives **real vendored libraries** — htmx `4.0.0`,
|
|
181
209
|
htmx `2.0.10`, `_hyperscript` `0.9.93` (`test/browser/vendor/`) — verifying the
|
|
182
210
|
end-to-end truths mocks can't: the v4 extension hook name and firing
|
|
183
211
|
granularity (`htmx_before_process`, per processed root — validated against the
|
|
184
|
-
|
|
212
|
+
4.0.0 source), request/swap from a localized button, both script orders,
|
|
185
213
|
localized attributes inside swapped-in content, executor-mode `hx-on` bodies
|
|
186
214
|
running through real `_hyperscript` with `me` bound, and the htmx 2.x
|
|
187
215
|
`defineExtension` fallback.
|
package/dist/htmx-i18n.global.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var HtmxI18n=(()=>{var
|
|
1
|
+
"use strict";var HtmxI18n=(()=>{var B=Object.defineProperty;var ae=Object.getOwnPropertyDescriptor;var le=Object.getOwnPropertyNames;var ce=Object.prototype.hasOwnProperty;var ue=(e,t)=>{for(var n in t)B(e,n,{get:t[n],enumerable:!0})},fe=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of le(t))!ce.call(e,o)&&o!==n&&B(e,o,{get:()=>t[o],enumerable:!(r=ae(t,o))||r.enumerable});return e};var de=e=>fe(B({},"__esModule",{value:!0}),e);var Ae={};ue(Ae,{EXTENSION_NAME:()=>m,canonicalizeElement:()=>H,canonicalizeTree:()=>p,installAutoSweep:()=>U,installResolverMode:()=>te,langOf:()=>b,normLang:()=>g,register:()=>O,registerWith:()=>M,setBodyExecutor:()=>$,setBodyTranslator:()=>N,setNeutralizeOnClaim:()=>v,translateTriggerValue:()=>R});function g(e){return e?e.split(/[-_]/)[0].toLowerCase():"en"}function b(e){let t=e.getAttribute?.("data-hyperfixi-lang");if(t)return g(t);let n=e.closest?.("[data-hyperfixi-lang]");if(n)return g(n.getAttribute("data-hyperfixi-lang"));let r=e.closest?.("[lang]");return r?g(r.getAttribute("lang")):"en"}var A=new Map,_=new Set,T=new Set;function O(e,t){let n=g(e);A.set(n,t?.hyperfixi??{}),_.delete(n);for(let r of T)r()}function E(e){return A.get(e)}function G(){return A.size>0}function K(){return[...A.keys()]}function C(e){return T.add(e),()=>{T.delete(e)}}function Y(e){_.has(e)||(_.add(e),typeof console<"u"&&console.warn(`[htmx-i18n] No vocab registered for lang="${e}". Elements in this language scope keep their localized attribute names and htmx will not see them. Load the ${e} vocab module (packages/core/vocab/htmx/${e}.js) before htmx processes the page.`))}var ge=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),h=null,S=null,z=new Set,V=new WeakMap,L=!0;function v(e){L=e}function $(e){h=e;for(let t of z)t()}function N(e){S=e}function j(){return h!==null}function J(e){return z.add(e),()=>{z.delete(e)}}function W(e){let t=new Set,n=V.get(e);if(n)for(let r of n.values())t.add(r.attrName);return t}function Q(e){let t=0;for(let n of W(e))n.startsWith("hx-on:")&&e.hasAttribute(n)&&(e.removeAttribute(n),t++);return t}function pe(e,t){return e.startsWith(":")?`htmx${e}`:ge(t,e)?t[e]:e}function D(e,t,n,r,o={}){if(!h)return!1;let i=t.indexOf(":");if(i<=0)return!1;let s=pe(t.slice(i+1),r),f=t.startsWith("hx-on:"),u=e.getAttribute(t)??"",c=V.get(e),a=c?.get(s);if(a)return a.attrName===t?(a.body!==u&&(a.body=u,a.translated=null),f&&L&&e.hasAttribute(t)?(e.removeAttribute(t),!0):!1):f&&(o.adapterCreated||L)&&e.hasAttribute(t)?(e.removeAttribute(t),!0):!1;let l={attrName:t,lang:n,body:u,translated:null};return e.addEventListener(s,d=>{if(h){l.translated===null&&(l.translated=l.lang!=="en"&&S?S(l.body,l.lang):l.body);try{h(l.translated,e,d)}catch(y){typeof console<"u"&&console.error(`[htmx-i18n] hx-on body execution failed (${t} \u2192 ${s})`,y)}}}),c||(c=new Map,V.set(e,c)),c.set(s,l),f&&L&&e.removeAttribute(t),!0}function P(e){let t=e;if(!h&&typeof t._hyperscript=="function"){let n=t._hyperscript;$((r,o,i)=>typeof n.evaluate=="function"?n.evaluate(r,{me:o,event:i}):n(r))}if(!S&&typeof t.HyperscriptI18n?.preprocess=="function"){let n=t.HyperscriptI18n.preprocess;N((r,o)=>n(r,o))}}var I=!1;function ee(){return I}function me(e,t){let n=b(e);if(n==="en")return null;let r=E(n)?.attrs;if(!r)return null;for(let[o,i]of Object.entries(r))if(i===t&&e.hasAttribute?.(o))return o;return null}function Z(){let e=new Set;for(let t of K()){let n=E(t)?.attrs??{};for(let[r,o]of Object.entries(n))o!=="hx-on"&&e.add(`[${r}]`)}return[...e]}function te(e){if(!e?.config)throw new Error("[htmx-i18n] installResolverMode: htmx.config not found");let t=e.config;t.attributeResolver=me,t.additionalAttributeSelectors=Z();let n=C(()=>{t.additionalAttributeSelectors=Z()});return I=!0,()=>{n(),t.attributeResolver=null,t.additionalAttributeSelectors=[],I=!1}}var k=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),ne=/^(?:hx|sse|ws)-/,xe=/,(?![^\[]*\])(?![^(]*\))(?![^<]*\/>)(?=(?:[^"']|"[^"]*"|'[^']*')*$)/,oe=e=>e.split(xe),ie=oe;function se(e){ie=e??oe}function re(e){return e==="hx-trigger"||e.startsWith("hx-trigger:")}function R(e,t){return ie(e).map(n=>n.replace(/^(\s*)([^[\s]+)/,(r,o,i)=>k(t,i)?o+t[i]:r)).join(",")}var F=new WeakMap;function be(e,t){let n=F.get(e);n||(n=new Set,F.set(e,n)),n.add(t)}function he(e,t){return F.get(e)?.has(t)??!1}function H(e){if(!e.attributes||e.attributes.length===0)return!1;let t=!1;for(let c of Array.from(e.attributes))if(ne.test(c.name)){t=!0;break}if(!t)return!1;let n=j(),r=b(e);if(r==="en"&&!n)return!1;let o=E(r);if(r!=="en"&&!o&&(Y(r),!n))return!1;let i=o?.attrs??{},s=o?.events??{},f=!1;for(let c in s){f=!0;break}let u=!1;for(let c of Array.from(e.attributes)){let a=c.name;if(!ne.test(a))continue;if(n&&a.startsWith("hx-on:")){D(e,a,r,s,{adapterCreated:he(e,a)})&&(u=!0);continue}if(re(a)){if(f){let x=R(c.value,s);x!==c.value&&(e.setAttribute(a,x),u=!0)}continue}let l=k(i,a)?i[a]:void 0,d;if(!l){let x=a.indexOf(":");if(x>0){let X=a.slice(0,x);if(d=k(i,X)?i[X]:void 0,d){let w=a.slice(x+1);l=d==="hx-on"?`${d}:${k(s,w)?s[w]:w}`:`${d}:${w}`}}}if(!l)continue;if(n&&d==="hx-on"){D(e,a,r,s)&&(u=!0);continue}if(l===a||e.hasAttribute(l))continue;let y=c.value;re(l)&&(y=R(y,s)),e.setAttribute(l,y),be(e,l),u=!0}return u}function p(e){if(!e||ee()||!G()&&!j())return 0;let t=0;if(e instanceof Element&&H(e)&&t++,typeof e.querySelectorAll=="function")for(let n of Array.from(e.querySelectorAll("*")))H(n)&&t++;return t}var m="lokascript-i18n";function ve(e,t,n){for(let r of t)if(e===r||e.startsWith(r)&&e.charAt(r.length)===n)return!0;return!1}function ye(e){let t=e?.config,n=t&&"prefix"in t?t.prefix??"":"data-hx-",r=t?.metaCharacter||":",o=n?["hx-on",n+"on"]:["hx-on"];return{init(i){let s=i?.HCON?.split;typeof s=="function"&&se(s.bind(i.HCON))},htmx_before_process(i){p(i)},htmx_before_process_node(i){p(i)},htmx_before_on_init(i){let s=W(i);if(s.size===0)return;let f=!1;for(let u of Array.from(i.attributes)){let c=u.name;if(!s.has(c)&&ve(c,o,r)){f=!0;break}}if(!f)return!1;Q(i)},onEvent(i,s){if(i!=="htmx:beforeProcessNode")return;let u=s?.detail?.elt??(s?.target instanceof Element?s.target:null);u&&p(u)}}}function M(e){if(!e)return null;let t=ye(e);return typeof e.registerExtension=="function"?e.registerExtension(m,t)===!1?(typeof console<"u"&&console.warn(`[htmx-i18n] htmx.registerExtension("${m}") returned false \u2014 the extension is not registered. If htmx.config.extensions (the <meta name="htmx-config"> allowlist) is set, add "${m}" to it. Swapped-in content will not be canonicalized; executor-mode claims fall back to removing canonical hx-on:* attrs.`),v(!0),null):(v(!1),"v4"):typeof e.defineExtension=="function"?(e.defineExtension(m,t),v(!0),"v2"):null}function U(e=document){let t=()=>{p(e.body??e.documentElement)},n=e.readyState==="complete",r=null;if(n)t();else{let s=()=>{n||(n=!0,r?.(),t())},f=e.defaultView;e.addEventListener("DOMContentLoaded",s),f?.addEventListener("load",s),r=()=>{e.removeEventListener("DOMContentLoaded",s),f?.removeEventListener("load",s)}}let o=C(()=>{n&&t()}),i=J(()=>{n&&t()});return()=>{r?.(),o(),i()}}var q="__hyperfixi_i18n";function Ee(){if(typeof window>"u")return;let e=window,t=e[q];if(t){let n=t.register.bind(t);e[q]={register:(r,o)=>{n(r,o),O(r,o)}};return}e[q]={register:O}}function we(){if(typeof window>"u"||typeof document>"u")return;let e=window;P(window),M(e.htmx)||document.addEventListener("DOMContentLoaded",()=>{!M(e.htmx)&&!e.htmx&&typeof console<"u"&&console.warn(`[htmx-i18n] window.htmx not found \u2014 the "${m}" extension was not registered. Dynamically swapped content will not be canonicalized (the initial document sweep still ran). Load htmx on this page.`)},{once:!0}),document.addEventListener("DOMContentLoaded",()=>P(window),{once:!0}),U(document)}Ee();we();return de(Ae);})();
|
|
2
2
|
//# sourceMappingURL=htmx-i18n.global.js.map
|