Skip to content

feat: expose the CSS and HTML language workers - #18

Merged
knolleary merged 1 commit into
add-testsfrom
feat/expose-css-html-workers
Oct 12, 2026
Merged

knolleary merged 1 commit into
add-testsfrom
feat/expose-css-html-workers

Conversation

@Steve-Mcl

Copy link
Copy Markdown
Collaborator

Related to #9 and node-red/node-red#5994

Summary

The main reason for this is working towards Vue and Vuetify support in Dashboard 2 (see #9 and https://discourse.nodered.org/t/weather-card-struggling-with-methods/85434/6?u=steve-mcl).

node-red/node-red#5994 adds JavaScript and CSS support to <script> and <style> blocks in Node-RED's HTML editors, which Dashboard 2's ui-template builds on. The JavaScript side uses monaco.typescript.getJavaScriptWorker(), which Monaco already exposes. There is no equivalent for CSS or HTML: Monaco has public worker getters for TypeScript and JSON (monaco.json.getWorker()) but not for CSS and HTML. So #5994 can only show CSS errors, not completion, hover or colour previews.

This adds the two missing getters, with the same shape as monaco.json.getWorker():

  • monaco.css.getWorker(languageId = "css") for css, scss and less
  • monaco.html.getWorker(languageId = "html") for html, handlebars, razor, and any language added with monaco.html.registerHTMLLanguageService()

Each resolves to (...uris) => Promise<worker> and rejects with "<languageId> not registered!" until that language has been used, because its worker starts lazily.

Changes

  • generate-monaco-esm-i18n.js: new exposeLanguageWorkers() step after the copy. It patches cssMode.js / htmlMode.js so each language keeps its worker accessor, exports getWorker from register.js, and re-exports it from the older vs/language/{css,html}/monaco.contribution.js path.
    • Unlike the existing replacements, each patch must match an exact number of times or the build fails. A Monaco update that moves this code can't silently drop the API.
    • Gotcha for anyone touching it: replace-in-file counts matches with String.match(), so a non-global regex reports its capture groups as matches. patchExactly() makes the regex global before counting.
  • test/browser/editor.test.js: new test. The API is present; an unused language rejects; CSS doComplete and findDocumentColors; SCSS doHover; HTML doComplete; and a language added with registerHTMLLanguageService(). It has its own timeout, because a worker that fails to start makes its calls wait forever.
  • README.md: a note under Notes.

Testing

  • npm run build: the patch applies cleanly against Monaco 0.57.0. editor.js grows by 720 bytes (4,985,273 to 4,985,993); the workers are unchanged.
  • npm test 81/81, test:build output tier 85/85, test:browser 7/7 (6 before, plus the new one).
  • Checked inside Node-RED with this build: the getters work, CSS completion and colours show up in #5994's <style> support, no console errors.

Notes

  • This is stacked on Add tests #17 (tests), which is stacked on Bump 0.57.0 #16 (the 0.57.0 bump), so the diff here only shows this change.
  • CI won't run on this PR as it stands: test.yml only runs for PRs into main and monaco-0.57.0. I can add a commit to include this PR's base, or we can just wait until it's rebased onto main.
  • A language added with registerHTMLLanguageService() asks MonacoEnvironment.getWorkerUrl for a worker labelled with its own id. The test maps that label to html, which is what feat: add JavaScript and CSS support to script and style blocks in HT… node-red#5994 now does in Node-RED's getWorkerUrl.
  • Worth considering offering upstream to microsoft/monaco-editor too, since JSON already has this. If it lands there, this patch can go in the bin.

Monaco has public getters for its TypeScript and JSON workers but not for CSS
and HTML, so editor code can't use those language services for content embedded
in another language, e.g. a <style> block in an HTML or Vue template.
node-red/node-red#5994 uses this for CSS completion, hover and colours.

- monaco.css.getWorker(languageId = "css") for css, scss and less
- monaco.html.getWorker(languageId = "html") for html, handlebars, razor and
  languages added with registerHTMLLanguageService()
- Same shape as monaco.json.getWorker(): resolves to (...uris) => Promise<worker>,
  rejects until the language has been used
- Applied in generate-monaco-esm-i18n.js; the build fails if a patch no longer
  matches, so a Monaco update can't drop it silently
- Browser test for both getters
@Steve-Mcl
Steve-Mcl requested a review from knolleary October 12, 2026 16:14
@knolleary
knolleary merged commit 3c3da18 into add-tests Oct 12, 2026
1 check passed
knolleary added a commit that referenced this pull request Oct 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants