Self-contained editor html for extensions#1890
Conversation
…DE / Open VSX The pyret-parley webview is blank in the GitLab Web IDE (issue brownplt#21). There, the webview's resources resolve to Open VSX, which serves every file as text/plain + nosniff (so <script src>/<link> are refused execution) and 503s files over a ~15MB cap (so the 37MB cpo-main.jarr.js never loads). Desktop VS Code and vscode.dev serve with an executable MIME type, which is why this was missed. Fix, entirely in vscode/ (no code.pyret.org/beforePyret/editor.html edits): - self-contained-webview.js: a string transform on the rendered editor.html. Inlines the shell scripts/styles into the injected HTML (immune to MIME, since the HTML is injected not fetched), and defers the runtime tail into a bootstrap that fetch()es cpo-main.jarr.gz.js (fetch ignores MIME; Open VSX sends CORS; 5.6MB < cap) and inflates it in-page with the native DecompressionStream, then publishes it as a Blob URL on window.PYRET. No pako, no external CDN. - webpack.config.js: ship the already-built cpo-main.jarr.gz.js and drop the 37MB raw bundle + intermediates; bundle the shell files as asset/source. - getHtmlForWebview: apply the transform (no-op-equivalent where MIME is fine). Reduces the vsix from ~37MB+ of bundle to a 5.4MB gz. Note: the HTML rewrites here are deliberately a bit ugly; a follow-up will push minimal support into editor.html so the transform can shrink. browser-test: new --env=vscode-ovsx that reproduces Open VSX's hostile serving (shared/ovsx-server.js: text/plain + nosniff + size cap) so this class of bug is caught without deploying. Green hostile + faithful; real --env=vscode 29/29 check-blocks; hostile errors+type-check 196/196. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…-side transform) Refines the issue brownplt#21 fix (GitLab Web IDE / Open VSX): instead of the extension rewriting the editor HTML at runtime, the CPO build emits a self-contained template and the extension just fills three literal placeholders. - code.pyret.org/src/web/editor.html: a `window.PYRET_GZIPPED` flag and a conditional runtime preload (`{{^PYRET_GZIPPED}}`), so one template serves both the normal server (Content-Encoding: gzip) and the gzipped-in-JS webview case. - code.pyret.org/src/web/js/beforePyret.js: the runtime loader branches on window.PYRET_GZIPPED -> fetch + DecompressionStream + Blob URL; otherwise the original <script src> (server path unchanged, reuses the same error/backup). - code.pyret.org/src/scripts/inline-selfcontained.js + Makefile: `make web` emits build/web/views/editor.selfcontained.html with the shell scripts/styles inlined. It runs mustache on the CLEAN template FIRST (so the engine only sees its intended input; minified shell JS is full of `{{`/`}}` that mustache would otherwise eat), leaving literal sentinels for the three values only the extension knows at runtime (base URL, theme/layout hash, url-file-mode). - vscode extension: requires editor.selfcontained.html and fills the sentinels with plain split/join string replacement -- no template engine, mustache dep dropped. self-contained-webview.js and its webpack asset rule are gone. - browser-test/ovsx-server.js: fills the same sentinels; drops its mini-renderer. Validated: real --env=vscode 29/29; hostile vscode-ovsx errors+type-check 196/196; faithful + hostile smoke green; server --env=cpo 29/29 (no regression). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…matrix Runs the freshly-built extension's webview through assets served the way Open VSX / the GitLab Web IDE serve them (text/plain + nosniff + ~15MB cap) -- the reproduction of issue brownplt#21 -- alongside the cpo/embed/vscode envs. Like the vscode env it restores vscode/dist and needs no CPO server (startsWith(env,'vscode')). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…te, + unit tests Audit hardening of the issue brownplt#21 fix. Same architecture and byte-identical output on the current build; what changes is every silent-failure mode: - a missing referenced asset was silently left as an external <script src>/ <link> (dead only at webview runtime, only on Open VSX-backed hosts); now a build error naming the file. - post-conditions on the output: no <script src>/<link .css> may still point at the BASE sentinel (catches template drift the inline patterns don't match, e.g. single-quoted attrs or a new asset dir), and the HASH/ URL_FILE_MODE sentinels must survive exactly once (the extension's split/join contract). - escapeForScript now hard-errors if an asset contains <!--, which flips the HTML parser into the script-data escaped states where a bare <script changes how </script> is matched (beforePyret.js already contains <script in strings/comments, so a future <!-- would break the page). No asset contains it today; auto-escaping could change the meaning of legal Annex-B comment lines, so demand a human look instead. - core factored into buildSelfContained() and unit-tested (node --test src/scripts/inline-selfcontained.test.js): script-data escaping incl. the '</script foo>' form, $-safe replacement, css url() rebasing, mustache- before-inline ordering, and each fail-loud path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
(I left a message via the github app, but it got evaporated into the ether) I haven't read through this yet, but two quick clarification questions:
|
…derer is gone) Since ad839fa this file fills the self-contained template's literal placeholders with string replacement; the header still described the old mustache-rendering approach. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
(I made two PRs, this is the real one) Good suggestions. This doesn't ditch mustache on its own (the server still uses it to render), but that's a good thing to do as well. I will stage a separate PR (on top of this branch) to do that, and we can separately decide which order to merge in. That will remove the "mustache is over-eager on For matching on the source files themselves, I think |
… in inlined assets
The same asset files are served two ways: inlined into the self-contained
template (where the extension's placeholder fill processes the assembled
string at webview startup) and un-inlined as plain <script src>/<link> by
the normal server (where no substitution pass ever touches them). So an
asset containing a __PYRET_WEBVIEW_* sentinel would be rewritten in one
deployment and left alone in the other, and a {{...}} tag naming one of
this build's template variables reads as substituted but isn't (and would
be, if the render/inline passes were ever reordered).
checkInlinable() makes both a build error, on the raw asset bytes before
any of our own rewrites introduce sentinels. Keyed to exactly this build's
dictionary (TEMPLATE_VARS, now hoisted): unrelated client-side template
text like {{SOME_OTHER_TAG}} and minified-JS braces stay legal.
No shipped asset contains either pattern; output is byte-identical.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This is a substantial build update for the VScode extension that:
The root causes are:
text/plain. This is a known open issue, so they won't load as e.g.scripttags and styles.The overall idea is to add a new build target that inlines a bunch of stuff into
editor.htmlto make it more of a standalone HTML file. There's some subtlety here, because there is:HASHparams as a template variable. It may be possible to inline all of these eventually but at the moment "inlining the gzipped source into HTML" feels like a bit much.One issue is that we've been using mustache for (2), but mustache can find and mess with
{{in minified JS... bad news.So this has several fixes:
So let's talk about the inliner. It's regexy and my first instinct is that it's horrible:
There are two horribles here:
The argument for doing the hacky thing for 1 is that if you actually use a parser, find the tags via search and re-emit, you get re-emitted HTML, which is hard to diff, doesn't necessarily look like the input, and so on. A really good one is going to be a really big npm dependency, as well. A rabbit-hole of figuring out which one to use, etc. And HTML parsers are super permissive on bad input, anyway, so it's not clear the “no surprises” intent of a parser will come through.
I think for 2 there's a better simple counterproposal, which is to use
data:urls in script tags with base64 encoded code. This works, and avoids any and all escaping issues. We may want to do it. However:data:fkh439nafhaouif340as opposed to a specific script tag with inlined code with a clearly-settable breakpoint)This is also all build-side; we control all the code being templated in.
Also-also, there are npm packages for doing inlining of source... if you look at their code they use regexes, too. That's not to say “hey existing code sucks so we should, too”, more that if we had said “oh let's use an off-the-shelf”, that's what we're getting.
And to be clear we have to do something here if we want to work on Gitlab's web editor (which we should). There is no way to ship these as separate JS files and load them as tags in Gitlab's web editor. At best we could load them all with
fetchof the plaintext andevalthem (but that changes semantics, not quite script tags), and it's not clear that works for the css as well. So given how well this works (and there are tests for it), I'm OK with it as a build-for-vscode.Note that this doesn't do anything to the normal CPO build; the changes there are minimal flag-outs for the "are we doing the fancy gzip-in-JS thing”.
Also, this is tested and it works on Gitlab (I did an out-of-band build of the VSIX and deployed it).
Other stuff:
DecompressionStream(https://github.com/jpolitz/pyret-lang/pull/8/changes#diff-6f16fedf22976ab5c1109c9066b53bd0dacda3a803245e4ac0f52b824512f53dR1497). This is very cool and I don't think controversial at all given constraints (I mean, you could also base64 in the whole gzipped thing and inline it, but that breaks all kinds of nice caching, inflates the compression, etc)