151 lines
5.8 KiB
Markdown
151 lines
5.8 KiB
Markdown
# E39 — WebWorker plugin
|
||
|
||
Cluster 39 of `plans/hs-conformance-to-100.md`. Goal: +1 test.
|
||
|
||
## 1. The 1 failing test
|
||
|
||
Suite `hs-upstream-worker`, test:
|
||
|
||
> `raises a helpful error when the worker plugin is not installed`
|
||
|
||
Translated upstream (`test/features/worker.js`) — currently emitted as
|
||
`SKIP (untranslated)` in `spec/tests/test-hyperscript-behavioral.sx` path
|
||
`(117 2 2)`:
|
||
|
||
```
|
||
(deftest "raises a helpful error when the worker plugin is not installed"
|
||
(error "SKIP (untranslated): raises a helpful ..."))
|
||
```
|
||
|
||
Upstream assertion:
|
||
|
||
```js
|
||
const msg = await error("worker MyWorker def noop() end end")
|
||
expect(msg).toContain('worker plugin')
|
||
expect(msg).toContain('hyperscript.org/features/worker')
|
||
```
|
||
|
||
Two substring checks on the error produced by compiling the source.
|
||
|
||
## 2. Upstream `worker` syntax
|
||
|
||
From `https://hyperscript.org/features/worker/`:
|
||
|
||
```
|
||
worker <name>[(<external-script-url>*)]
|
||
(<def-feature> | <js-block>)+
|
||
end
|
||
```
|
||
|
||
- Declared at top level alongside `on`, `init`, `def`, `behavior`.
|
||
- Body restricted to `def` functions and `js ... end` blocks; no DOM / `window` access.
|
||
- Callers invoke as `WorkerName.fn(args)` from the main thread; calls
|
||
return promises but are async-transparent.
|
||
- Stock `_hyperscript` ships only a **stub** in the core bundle — the
|
||
real implementation is in the `worker` ext. Without the ext loaded,
|
||
parsing must fail with a message mentioning both `worker plugin` and
|
||
`hyperscript.org/features/worker`.
|
||
|
||
## 3. Proposed SX shape
|
||
|
||
We ship the stub only. Full runtime is out of scope for a 1-test cluster.
|
||
|
||
### Parser addition (`lib/hyperscript/parser.sx`, `parse-feat`)
|
||
|
||
Extend the feature-dispatch `cond` (around line 2620) with one branch
|
||
**before** the fallthrough to `parse-cmd-list`:
|
||
|
||
```
|
||
((= val "worker") (parse-worker-stub))
|
||
```
|
||
|
||
`parse-worker-stub` raises immediately — it does not consume tokens,
|
||
does not need to recognise the body grammar. The error string contains
|
||
both required substrings:
|
||
|
||
```
|
||
"worker plugin is not installed — see https://hyperscript.org/features/worker"
|
||
```
|
||
|
||
`(error ...)` at parser scope is the existing failure channel (used by
|
||
`Expected 'of' or 'from' at position …` etc.), so this slots in without
|
||
changes to the error plumbing.
|
||
|
||
### Compile output
|
||
|
||
None. The stub never returns an AST; the error propagates out of
|
||
`hs-compile` and through `hs-to-sx-from-source`, surfacing to the test
|
||
runner as a caught exception.
|
||
|
||
## 4. Runtime architecture
|
||
|
||
For the 1 test: **no runtime.** Parsing fails, so `runtime.sx` never sees
|
||
a worker form. No `Worker` class needed in the `hs-run-filtered.js`
|
||
mock. Nothing touches the DOM shim.
|
||
|
||
For a hypothetical full implementation (explicitly out of scope):
|
||
|
||
- Server would bind `WorkerName` in the HS top-level env to a record
|
||
`{:worker-handle H :exports (list ...)}`.
|
||
- `WorkerName.fn(args)` would compile via the existing property-access
|
||
path to `(hs-method-call WorkerName "fn" args)`, which would detect
|
||
the worker handle and dispatch over a `postMessage` channel.
|
||
- Mock env would need a `Worker` class constructor + a serialisable
|
||
message loop driving the worker script's own tiny SX interpreter.
|
||
Deferred until the 7 skipped upstream tests become a target cluster.
|
||
|
||
## 5. Test delta estimate
|
||
|
||
+1 test. Feasibility: **high**. Two substring checks. Implementation
|
||
is a single `cond` branch plus a 2-line error string. Generator patch
|
||
to un-skip the test is mechanical.
|
||
|
||
## 6. Risks
|
||
|
||
- **Drift from future real plugin** — if we later implement the plugin,
|
||
the stub must be replaced, not shadowed. Mitigation: the stub lives
|
||
inline in `parse-feat`; adding the real plugin means deleting the
|
||
stub branch and replacing it with `(parse-worker-feat)`. Single site.
|
||
- **In-browser vs mock divergence** — none in this cluster. The stub
|
||
errors identically in both hosts because it's pure parser logic.
|
||
- **Message serialisation** — N/A until full plugin.
|
||
- **Error-message phrasing drift** — the upstream test only checks for
|
||
two substrings. We must keep `worker plugin` and
|
||
`hyperscript.org/features/worker` verbatim; change-detector tests
|
||
should reference this design if the wording is ever touched.
|
||
- **Generator mis-translation** — the upstream JS uses `await error(src)`
|
||
which the SX generator currently bails on (`return None`), producing
|
||
the `SKIP (untranslated)`. We can either (a) teach the generator to
|
||
translate `await error(src)` + two `toContain` checks, or (b) hand-
|
||
write the test. Recommend (b) for one test: less generator churn.
|
||
|
||
## 7. Implementation checklist (single commit)
|
||
|
||
1. `sx_replace_by_pattern` in `lib/hyperscript/parser.sx` — insert the
|
||
`((= val "worker") ...)` branch inside `parse-feat`'s `cond`. Body:
|
||
`(error "worker plugin is not installed — see https://hyperscript.org/features/worker")`.
|
||
2. `sx_replace_node` in `spec/tests/test-hyperscript-behavioral.sx`
|
||
path `(117 2)` — replace the `SKIP (untranslated)` body with:
|
||
```
|
||
(deftest "raises a helpful error when the worker plugin is not installed"
|
||
(let ((result (guard (e (true (if (string? e) e (str e))))
|
||
(hs-compile "worker MyWorker def noop() end end")
|
||
"")))
|
||
(assert (contains? result "worker plugin"))
|
||
(assert (contains? result "hyperscript.org/features/worker"))))
|
||
```
|
||
3. `sx_validate` on both files.
|
||
4. Run `node tests/hs-run-filtered.js` with `HS_SUITE=hs-upstream-worker`.
|
||
Expect 1/1.
|
||
5. Run full suite smoke (0–195) — expect no regressions (pure additive
|
||
branch in `parse-feat`; `"worker"` was previously caught by the
|
||
fallthrough `parse-cmd-list`, which would have errored anyway on
|
||
an unknown identifier — confirm by checking baseline).
|
||
6. Commit: `HS: E39 WebWorker plugin stub (+1 test)`.
|
||
|
||
## Non-goals
|
||
|
||
- Any worker runtime. Any real `Worker` object. Any message passing.
|
||
- The 7 sibling `test.skip(...)` cases upstream — they remain skipped.
|
||
- Generator patches — the test is hand-written per §6 above.
|