feat(create-module): lint server-runtime-unavailable APIs in server files#710
Draft
romain-pm wants to merge 2 commits into
Draft
feat(create-module): lint server-runtime-unavailable APIs in server files#710romain-pm wants to merge 2 commits into
romain-pm wants to merge 2 commits into
Conversation
…iles
Server files run in GraalJS embedded in the Jahia JVM: plain ECMAScript,
no event loop, no I/O, no Node.js and no browser. Until now that was only
discovered at render time, e.g. `{"error":"setTimeout is not defined"}`
on the first call of an action using a timer.
Add a config-only guard, built on the ESLint core rules
`no-restricted-globals` and `no-restricted-imports`, scoped to
`**/*.server.{js,jsx,ts,tsx}` and `**/*.action.{js,ts}`:
- 40 globals verified `undefined` in a GraalJS 23.0.5 context configured
like `GraalVMEngine.ContextPoolFactory#create` (timers, network, Node.js,
browser and a few web-platform extras);
- `node:*` specifiers and bare Node.js built-in module names.
Names the runtime does provide are deliberately not restricted: `console`,
`Promise`, `Intl`, `Atomics`, and the `TextEncoder`/`TextDecoder` polyfill
the engine installs at startup. Client files are untouched.
Every message links the new reference page, which documents the
microtask-only async model, what is and isn't available, and the
`process.env.NODE_ENV` exception (statically replaced by the Vite plugin).
The same block is dogfooded by the repository root config, the hydrogen
sample and the test module. The only violation it surfaced is the
`process.env.NODE_ENV` vite-plugin fixture, opted out explicitly.
Closes #703
📝 Documentation GuidelinesThank you for contributing to our documentation! To ensure your contributions meet our standards, please review these resources:
This comment is posted automatically when changes are detected in the |
🦜 Chachalog
|
5 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #703 — part of EPIC #698 (developer experience).
Problem
The server runtime is GraalJS: microtask-only, no timers, no
fetch, no Node built-ins. Today you discover that at runtime, on the first call —{"error":"setTimeout is not defined"}— and only for the one API you happened to reach first. It is the first wall a developer coming from the JS ecosystem hits.Change
ESLint core rules (
no-restricted-globals,no-restricted-imports) scoped to**/*.server.{js,jsx,ts,tsx}and**/*.action.{js,ts}, shipped in the scaffolded config and dogfooded on the samples and the test module. No new plugin, no new dependency. Client files are untouched — they run in a browser, where all of this exists.Each message points at a new reference page,
docs/3-reference/3-server-runtime-boundary, documenting what is available, what is not, and why.How the restricted list was established
Rather than assuming, the restricted names were probed against the exact GraalJS version the engine pins (
23.0.5), in a context built with the same options asGraalVMEngine.ContextPoolFactory#create, evaluatingtypeof <name>for ~80 names. The 40 restricted globals are those the probe reportedundefined, grouped as timers/scheduling, network & async I/O, Node.js, browser, and web-platform extras.node:*and bare Node built-in imports both throw in the probe (Unsupported URI scheme node/Error reading: fs), hence the import patterns.Deliberately not restricted:
console— present, used throughout the samples;TextEncoder/TextDecoder— absent from raw GraalJS but polyfilled by the engine (fast-text-encodinginsrc/server/index.ts), so they work at runtime;Promise,Intl,Atomics,WeakRef, all ECMAScript builtins — present;Worker,ReadableStream, …) documented as equally unavailable but left out of the rule, to keep it to what people actually reach for.One real violation found
Repo-wide, exactly one:
vite-plugin/fixtures/src/process.env.server.tsxreadsprocess.env.NODE_ENV— the one legitimate use, since the Vite plugin statically replaces that expression at build time. It gets a targetedeslint-disable-next-linewith the reason, and the exception is documented on the reference page. The rule was not weakened.Verification
node --test→ 4 tests: a server file usingsetTimeout,fetch,process.envand both Node import forms fails with both rule IDs and messages ending in the docs URL; a clean server file usingconsole+TextEncoderpasses; a client file usingfetch,AbortController,windowanddocumentpasses.eslint --print-configindependently confirms the scoping (40 restricted globals on a server file, none on a client file). Repoyarn lintis green, and the vite-plugin snapshot test passes after regenerating the one affected map.Not done
The build-time scan for transitive references (item 2 of the issue) is out of scope by design: the rule is syntactic and does not follow imports, which the docs page states.
Authored with Claude Code and reviewed before pushing; the test and lint output above was reproduced independently.