feat: refactor SSR#2205
Conversation
🦋 Changeset detectedLatest commit: d8dac79 The changes in this PR will be included in the next version bump. This PR includes changesets to release 2 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds server-side rendering support and APIs across Rust, WASM bindings, and TypeScript: new MainThreadServerContext and server style manager, StyleSheetResource refactor, template decode/execute (deploy), SSR dev middleware, server TS element APIs, updated d.ts wasm bindings, tests (Vitest & Playwright), and E2E infra. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 16
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/web-platform/web-core-wasm/AGENTS.md (1)
16-16:⚠️ Potential issue | 🟡 MinorArchitecture overview on Line 16 doesn't mention the new Server Runtime layer.
Line 16 still lists only three layers: "Rust Core (
src), the Client Runtime (ts/client), and the Encoder (ts/encode)." Since section 3 now introduces Server Runtime (ts/server), this line should be updated to include it.📝 Suggested fix
-The codebase is structured into three main layers: the **Rust Core** (`src`), the **Client Runtime** (`ts/client`), and the **Encoder** (`ts/encode`). +The codebase is structured into four main layers: the **Rust Core** (`src`), the **Client Runtime** (`ts/client`), the **Server Runtime** (`ts/server`), and the **Encoder** (`ts/encode`).
🤖 Fix all issues with AI agents
In `@packages/web-platform/web-core-wasm-e2e/shell-project/devMiddleware.ts`:
- Around line 48-58: The code in devMiddleware.ts builds viewAttributes by
interpolating url.searchParams values into attribute strings (see
url.searchParams.forEach, the attributes Map, and the viewAttributes
concatenation), which allows injection via unescaped quotes; fix by
HTML-escaping attribute values before concatenation — at minimum replace or
encode double-quotes (") to " and also escape &, <, > (and optionally
single-quote) when reading values from attributes (or during the for...of
attributes loop) so the generated key="value" pairs cannot break out of the
attribute context.
- Around line 83-86: The catch block that handles SSR errors currently logs the
error and sets res.statusCode = 500 but never finalizes the response, leaving
the request hanging; update the catch (err: any) handler to call res.end() (or
res.end('Internal Server Error') for a body) after setting the status code and
logging, ensuring the response is completed; modify the same catch block where
console.error('SSR Error:', err) and res.statusCode = 500 are used so that
res.end() is invoked in all error paths.
- Around line 25-30: Validate the caseName extracted from the request query
before using it to construct bundlePath: in devMiddleware.ts (where caseName and
hasdir are used to build bundlePath) add an early guard that checks if caseName
is null/empty/invalid and either respond with a 400 error (or set a safe
default) instead of letting path.join receive null; ensure the guard runs before
computing bundlePath and references the caseName and hasdir variables so you do
not pass null into path.join.
In `@packages/web-platform/web-core-wasm/src/lib.rs`:
- Around line 35-36: Add the missing benchmark helper module by declaring the
bench_support module in lib.rs using the exact cfg attributes: #[cfg(all(feature
= "client", feature = "encode", target_arch = "wasm32"))] and #[path =
"../benches/support/bench_support.rs"]] mod bench_support; (refer to symbol
bench_support) and create the corresponding file
benches/support/bench_support.rs containing the shared benchmark utilities /
support structure used by tests/benches (name the file’s module contents to
match bench_support so imports resolve).
In
`@packages/web-platform/web-core-wasm/src/main_thread/client/main_thread_context.rs`:
- Around line 63-69: Add a unit/integration test in tests/element-apis.spec.ts
that exercises the JS-facing API which triggers the Rust
MainThreadContext::push_style_sheet path: construct a StyleSheetResource-shaped
object (matching the Rust/TS type used by LynxViewInstance/createElementAPI),
call the exposed pushStyleSheet (or equivalent) method on the element
API/LynxViewInstance, and assert expected JS-side effects (e.g., the style
manager received/registered the stylesheet, a style entry exists under the
provided entry_name, or the DOM/style registry was updated). Name the test to
indicate it covers push_style_sheet and ensure it passes the StyleSheetResource
and optional entry_name to replicate the production call sites
(LynxViewInstance.ts / createElementAPI.ts).
In `@packages/web-platform/web-core-wasm/src/main_thread/client/style_manager.rs`:
- Around line 103-105: Add JS-side tests in tests/element-apis.spec.ts that
exercise the exported wasm function MainThreadWasmContext.push_style_sheet:
instantiate or obtain MainThreadWasmContext from the wasm module, construct or
mock a StyleSheetResource matching the Rust type shape, call push_style_sheet
from JS, and assert the expected DOM/host-side effect (e.g., a new <style>
element is created, the sheet count or stylesheet text contains the provided
rules, or the resource is referenced). Ensure tests cover success and at least
one error/edge case, and import the wasm module so the wasm_bindgen export for
push_style_sheet is exercised.
In `@packages/web-platform/web-core-wasm/src/main_thread/element_data.rs`:
- Around line 161-171: The set_style function currently panics on empty values
which can crash the SSR process; update set_style (in element_data::set_style)
to avoid panicking by returning early when value.is_empty() (no-op) or change
the signature to return a Result and return an Err for unsupported remove-style
attempts; ensure the attributes map manipulation
(self.attributes.entry("style"...).or_default() and subsequent push_str/push
calls) only runs when value is non-empty and, if you choose Result, propagate a
clear error variant instead of calling panic!.
In
`@packages/web-platform/web-core-wasm/src/main_thread/server/main_thread_server_context.rs`:
- Around line 213-241: The test fails to compile because
MainThreadServerContext::new requires two args (templates: js_sys::Object,
view_attributes: String) but the test only passes templates; update the test to
call MainThreadServerContext::new with a suitable second argument (e.g., an
empty or sample String for view_attributes) and gate the whole test with
#[cfg(target_arch = "wasm32")] so it only compiles on the wasm32 target (wrap or
annotate the test function test_html_generation), ensuring any use of
js_sys::Object stays behind the wasm-only cfg.
- Around line 155-165: The attribute values in render_element are not escaped
(see element.attributes loop pushing value into buffer), causing XSS; implement
an HTML attribute-escaping helper (e.g., escape_attribute_value or
escape_html_attribute) that replaces &, <, > and " (and optionally ') with their
corresponding HTML entities and use that helper when appending the attribute
value to buffer instead of pushing raw value; update the loop in render_element
to call the new escape function for each value before buffer.push_str so all SSR
attribute output is properly escaped.
In `@packages/web-platform/web-core-wasm/tests/decode.spec.ts`:
- Around line 184-195: The test fails because decodeTemplate (see decode.ts
handling of TemplateSectionLabel.CustomSections) decodes custom section bytes as
UTF-16LE and JSON.parses them, so the test should supply a UTF-16LE-encoded JSON
string and assert on the parsed object rather than raw Uint8Array; update the
test to encode a JSON value (e.g., an object or array) as a UTF-16LE byte
sequence for the custom section and expect result.customSections toEqual the
parsed JS value returned by decodeTemplate (referencing the test's use of
TemplateSectionLabel.CustomSections and decodeTemplate).
In `@packages/web-platform/web-core-wasm/tests/server-ssr.spec.ts`:
- Around line 13-15: The test initializes SSRBinding and config with missing
required fields causing strict TS errors; update the SSRBinding initialization
to include the required ssrResult property (e.g., set ssrResult: '' or
appropriate fixture) and supply the required config properties expected by
createElementAPI—add defaultOverflowVisible and defaultDisplayLinear with
suitable boolean/string values—so the variables match the SSRBinding type and
the createElementAPI signature (look for SSRBinding and createElementAPI in the
file to update their initializers).
In `@packages/web-platform/web-core-wasm/ts/client/mainthread/TemplateManager.ts`:
- Around line 203-213: The code creates a StyleSheetResource unconditionally in
the TemplateSectionLabel.StyleInfo case but then may find no template via
this.#templates.get(url), leaking the resource; fix by guarding so you only
construct the new StyleSheetResource if a template exists (check template =
this.#templates.get(url) first) or, if you must construct it earlier, ensure you
call the resource's cleanup/dispose/free method when template is undefined
before calling instance.onStyleInfoReady(url); update references to
StyleSheetResource, this.#templates.get(url), template.styleSheet and
instance.onStyleInfoReady accordingly.
In `@packages/web-platform/web-core-wasm/ts/server/decode.ts`:
- Around line 25-27: The check in decode.ts compares `magic` to
`BigInt(MagicHeader)`, but `MagicHeader` is defined as a Number constant and
loses precision for the 64-bit value; update the constant definition in
constants.ts to be a BigInt literal (e.g., change `0x464F525741524453` to
`0x464F525741524453n`) so `MagicHeader` is already a BigInt and the comparison
in `decode.ts` (using `magic !== MagicHeader` or `magic !==
BigInt(MagicHeader)`) will be accurate against Rust-encoded templates; locate
the `MagicHeader` symbol in constants.ts and replace its numeric literal with
the `n` BigInt suffix.
In `@packages/web-platform/web-core-wasm/ts/server/deploy.ts`:
- Around line 59-72: The promise can hang if renderPageFunction is never set;
update the deploy flow to detect failures and reject the render promise: wrap
the VM execution that sets renderPageFunction (the call that populates sandbox
where renderPage is expected) in a try/catch and on catch call the same
rejection path as resolveRender (rejectRender or equivalent), and add a clear
timeout fallback that rejects after a configurable timeout if renderPageFunction
(the setter logic for renderPageFunction/queueMicrotask) was never invoked;
ensure you reference and use the existing symbols renderPageFunction,
queueMicrotask, resolveRender (or its reject counterpart),
sandbox['processData'], and binding.ssrResult so the rejection path mirrors the
success path.
- Around line 83-107: The code currently skips assigning globalThis.renderPage
when result.lepusCode['root'] is undefined, causing renderPromise returned by
executeTemplate to never settle; update executeTemplate to detect missing root
(check result.lepusCode['root'] / rootCodeBuf) and immediately reject (or
resolve with a clear error) the renderPromise instead of skipping
execution—ensure the branch that currently contains vm.runInContext(...) (and
references wrappedCode, context, and renderPage) calls the promise rejector with
a descriptive Error so callers do not hang.
In
`@packages/web-platform/web-core-wasm/ts/server/elementAPIs/createElementAPI.ts`:
- Around line 209-221: The split on clsVal can produce [''] when classNames is
null, so update the call sites around wasmContext.update_css_og_style to pass a
cleaned array: after computing clsVal (from classNames || ''), split on /\s+/
then filter out empty strings (or use trim() and conditional) so you never pass
[''] to WASM; modify the occurrences that call wasmContext.update_css_og_style
(use uniqueIdSymbol, cssIdAttribute, lynxEntryNameAttribute,
setAttributeInternal/getAttributeInternal context) and ensure the same filtering
is applied at the similar call around line 239.
🧹 Nitpick comments (24)
packages/web-platform/web-core-wasm/ts/client/mainthread/utils/requestIdleCallback.ts (1)
1-5: Polyfill type signature doesn't matchrequestIdleCallback.The fallback's signature
(callback: () => void) => ...drops both theIdleDeadlineparameter and the optionaloptionsargument that nativerequestIdleCallbackaccepts. This is fine for the single current call site (which uses neither), but if another caller passes{ timeout: ... }or readsdeadline.timeRemaining(), it will silently misbehave.Consider aligning the fallback signature so it's a drop-in replacement:
Suggested improvement
// Safari doesn't support requestIdleCallback export const requestIdleCallbackImpl = typeof requestIdleCallback === 'undefined' - ? (callback: () => void) => setTimeout(callback, 16) + ? (callback: IdleRequestCallback, _options?: IdleRequestOptions): number => + setTimeout(() => callback(Object.freeze({ didTimeout: false, timeRemaining: () => 0 }) as IdleDeadline), 16) as unknown as number : requestIdleCallback;Also note that Safari has supported
requestIdleCallbacksince version 16.4 (March 2023), so the polyfill is only needed for fairly old Safari versions. The comment is still helpful though.packages/web-platform/web-core-wasm/ts/client/mainthread/TemplateManager.ts (2)
265-268:#removeTemplateusescreateTemplatefor side-effect cleanup.Calling
createTemplate(which also re-inserts a new empty template) just to leverage its cleanup logic, then immediately deleting, is a bit roundabout. Consider extracting the cleanup logic into a dedicated private method (e.g.,#clearTemplateResources) that bothcreateTemplateand#removeTemplatecan call.
304-307:getStyleSheetreturn type isany.The
StyleSheetResourcetype is available from the wasm typings. Usinganyweakens type safety for all downstream consumers. Consider importing and using the concrete type (or at minimumStyleSheetResource | undefined).packages/web-platform/web-core-wasm/ts/server/createServerLynx.ts (2)
10-13: Parameters are required but defensively accessed as if optional.
globalPropsandcustomSectionsare both required in the signature, yet line 31 usesglobalProps ?? {}and line 33 usescustomSections?.[key]. Either mark the parameters as optional (e.g.,globalProps?: Cloneable) or drop the defensive fallbacks for consistency.Proposed fix (make parameters optional)
export function createServerLynx( - globalProps: Cloneable, - customSections: Record<string, Cloneable>, + globalProps?: Cloneable, + customSections?: Record<string, Cloneable>, ): MainThreadLynx {
19-27: Consider trimming the development-note comments before merging.Lines 20-24 read like internal reasoning rather than documentation. A single concise comment (e.g.,
// Use setTimeout as a no-op stand-in for rAF in SSR) would suffice.packages/web-platform/web-core-wasm/src/template/template_sections/style_info/style_sheet_resource.rs (2)
49-58: Shadowed variable namestyle_content_elementinside the font-face block is misleading.Line 51 declares
let style_content_element = document.create_element("style")...inside the block that creates the font face element. This shadows the outerstyle_content_elementfrom line 40 and reads as though it's building style content rather than font-face content.📝 Suggested rename
- let style_content_element = document.create_element("style").map_err(|e| { + let font_face_style_element = document.create_element("style").map_err(|e| { wasm_bindgen::JsError::new(&format!("Failed to create style element: {e:?}")) })?; - style_content_element.set_text_content(Some(font_face_content)); - Some(style_content_element) + font_face_style_element.set_text_content(Some(font_face_content)); + Some(font_face_style_element)
62-66: Remove unnecessary.clone()calls on the server feature path — move values instead.On the server feature path,
decoded_style_data.style_contentanddecoded_style_data.font_face_contentare owned values that are only used once. After line 65, the only subsequent use ofdecoded_style_dataaccesses a different field (css_og_css_id_to_class_selector_name_to_declarations_map), so a partial move is safe and avoids unnecessary allocations.♻️ Suggested change
#[cfg(feature = "server")] let (style_content_str, font_face_content_str) = ( - decoded_style_data.style_content.clone(), - decoded_style_data.font_face_content.clone(), + decoded_style_data.style_content, + decoded_style_data.font_face_content, );packages/web-platform/web-core-wasm-e2e/server-tests/server-e2e.test.ts (1)
7-23: Consider usingtest.eachto reduce boilerplate.All 17 tests follow the identical pattern of calling
runSnapshotTestwith a bundle name. This can be simplified with Vitest'stest.each:♻️ Proposed refactor using test.each
+const bundles = [ + 'basic-pink-rect.web.bundle', + 'config-css-default-display-linear-false.web.bundle', + 'config-css-remove-scope-false-display-linear.web.bundle', + 'config-css-selector-false-remove-css-and-style-collapsed.web.bundle', + 'basic-element-text-baseline.web.bundle', + 'basic-scroll-view.web.bundle', + 'basic-element-x-audio-tt-play.web.bundle', + 'basic-element-image-src.web.bundle', + 'basic-element-x-input-value.web.bundle', + 'basic-element-list-basic.web.bundle', + 'basic-element-x-overlay-ng-demo.web.bundle', + 'basic-element-x-refresh-view-demo.web.bundle', + 'basic-element-svg-with-css.web.bundle', + 'basic-element-x-swiper-autoplay.web.bundle', + 'basic-element-text-color.web.bundle', + 'basic-element-x-textarea-placeholder.web.bundle', + 'basic-element-x-viewpager-ng-bindchange.web.bundle', +]; + +test.each(bundles)( + 'executeTemplate should run lepusCode.root from %s', + async (bundleName) => { + await runSnapshotTest(bundleName); + }, +);packages/web-platform/web-core-wasm/tests/server-compat.spec.ts (2)
13-15: Unused variablewasmCtxin all three tests.
wasmCtxis declared but never referenced in any of the three tests (lines 15, 34, 58). TheMainThreadServerContextimport on line 9 also becomes unused.🧹 Proposed fix
Remove the unused declarations from each test and the unused import:
-import { MainThreadServerContext } from '../ts/server/wasm.js';And in each test body:
- const wasmCtx = binding.wasmContext as MainThreadServerContext;
66-78: Remove investigation/exploration comments before merging.Lines 66–78 read like working notes from exploring the API (e.g., "Wait,
createElementAPImaps…", "Let's checkcreateElementAPI.tsimplementation details."). These should be cleaned up or replaced with a concise comment explaining the intent.packages/web-platform/web-core-wasm/ts/client/decodeWorker/decode.worker.ts (2)
280-286: Same unnecessary cast on Manifestcode.Same as above —
Uint8Arrayis a validBlobPart.- code as unknown as BlobPart, + code,
241-252: Remove unnecessary double cast oncode.
Uint8Arrayalready satisfies theBlobParttype (it's aBufferSource), soas unknown as BlobPartcan be removed. The Blob constructor will accept it directly.🧹 Suggested simplification
- code as unknown as BlobPart, + code,packages/web-platform/web-core-wasm/ts/server/deploy.ts (1)
62-62: Remove leftover refactoring comment.
// Removed: capturedRenderPage = true;is a development artifact.packages/web-platform/web-core-wasm/tests/server-ssr.spec.ts (2)
36-36: Removeconsole.logdebug statements from tests.Lines 36 and 62 contain debug logging that should be removed before merging.
- console.log('Generated HTML:', html);- console.log('Image HTML:', html);
49-51: Same type issues in second and third tests.Lines 49 and 71 use
binding: anywhich bypasses type checking entirely. Consider using the properly typedSSRBindingwith the correct initialization as suggested for the first test.packages/web-platform/web-core-wasm/tests/decode.spec.ts (1)
59-64: Unnecessary@ts-ignore.
callbacks.magicis typedbigint | undefined, andBigInt(MagicHeader)returnsbigint. The ternary expression type-checks cleanly without the suppression.Proposed fix
// Magic Header const magicBuf = new Uint8Array(8); - // `@ts-ignore` const magicVal = callbacks.magic !== undefined ? callbacks.magic : BigInt(MagicHeader);packages/web-platform/web-core-wasm-e2e/shell-project/devMiddleware.ts (1)
33-33: Synchronous file reads inside an async handler block the event loop.
fs.readFileSyncis used twice in anasyncfunction. Since this is dev-only middleware the impact is limited, but switching tofs.promises.readFilewould be more consistent with the async pattern and avoid blocking the server during concurrent requests.Also applies to: 70-73
packages/web-platform/web-core-wasm/ts/server/decode.ts (1)
76-83: Variablebuffershadows the function parameter.The
const bufferon line 77 shadows the outerbufferparameter (line 13), reducing readability. Consider renaming tostyleInfoBufferordecodedStyleInfo.Also, the inline comment on line 79 reads like a thinking-out-loud note rather than a code comment — consider cleaning it up before merging.
Proposed fix
case TemplateSectionLabel.StyleInfo: { - const buffer = decode_style_info( + const decodedStyleInfo = decode_style_info( content, - config['isLazy'] === 'true' ? '' : undefined, // URL is not available in synchronous decode usually, or passed as arg? The user req says "uint8array as params decode directly". Assuming URL is empty or unneeded for sync server decode unless specified. + config['isLazy'] === 'true' ? '' : undefined, config['enableCSSSelector'] === 'true', ); - styleInfo = buffer; + styleInfo = decodedStyleInfo; break; }packages/web-platform/web-core-wasm/src/main_thread/server/style_manager_server.rs (1)
17-24: Consider derivingDefaultforStyleManagerServer.Since
new()is just default initialization, you could#[derive(Default)]on the struct (or implementDefault) and let callers useStyleManagerServer::default(). This is idiomatic Rust and also silences theclippy::new_without_defaultlint.packages/web-platform/web-core-wasm/ts/server/elementAPIs/createElementAPI.ts (2)
97-104:SSRBindingtype is very loosely defined.
SSRBindingonly has assrResult: stringproperty, but on Line 137 the code attacheswasmContextvia anas anycast. Consider extending the type to include an optionalwasmContextproperty so downstream consumers can access it type-safely and avoid theanyescape hatch.♻️ Suggested type improvement
export type SSRBinding = { ssrResult: string; + wasmContext?: MainThreadServerContext; };Then on line 137:
- (mtsBinding as any).wasmContext = wasmContext; + mtsBinding.wasmContext = wasmContext;
458-462:__FlushElementTreesilently does nothing whenpageElementIdisundefined.If
__CreatePagewas never called before__FlushElementTree, thessrResultremains unset. This could be confusing to consumers. Consider logging a warning or throwing ifpageElementIdis undefined at flush time, since it almost certainly indicates a programming error.♻️ Add a guard for missing page element
__FlushElementTree: (() => { if (pageElementId !== undefined) { mtsBinding.ssrResult = wasmContext.generate_html(pageElementId); + } else { + console.warn('[SSR] __FlushElementTree called before __CreatePage'); } }),packages/web-platform/web-core-wasm/src/main_thread/client/main_thread_context.rs (1)
146-151: Remove or track the commented-outgcmethod.This has been commented out with the old logic referencing
get_dom(). If GC is planned for a future iteration, a TODO/issue reference would be better than leaving dead code.packages/web-platform/web-core-wasm/src/main_thread/server/main_thread_server_context.rs (1)
73-78:create_elementuses aVecwith index-based lookup — no bounds validation inappend_child,set_attribute, etc.The element ID is the index in
self.elements. The helper methods (Lines 80-109) useself.elements.get_mut(element_id)which safely returnsNoneon out-of-bounds access. However, the silent failure (no error, no log) when an invalid ID is passed could mask bugs during SSR rendering. Consider at least a debug assertion.packages/web-platform/web-core-wasm/src/main_thread/element_data.rs (1)
177-238: Large block of commented-out code should be removed.Lines 177-238 contain multiple commented-out methods (
clone_node,should_enable_exposure_event,add_event_listener_with_js_function,remove_js_function_event_listener). These appear to be leftover from the client-only implementation. Since this is a WIP PR, consider cleaning these up before merge or adding a tracking TODO.
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
Merging this PR will degrade performance by 14.47%
Performance Changes
Comparing Footnotes
|
Web Explorer#7839 Bundle Size — 383.64KiB (-0.03%).d8dac79(current) vs 09929fb main#7815(baseline) Bundle metrics
Bundle size by type
Bundle analysis report Branch PupilTong:p/hw/rust-refactor-ssr Project dashboard Generated by RelativeCI Documentation Report issue |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
.github/workflows/test.yml (1)
138-155:⚠️ Potential issue | 🟠 Major
web-core-wasm-e2e: SSR missing from render matrix, and job excluded fromdonegate.Two confirmed issues:
SSR not in render matrix (Line 141): The matrix only includes
[CSR], but this PR adds SSR E2E tests (ssr-no-js.spec.ts). The conditionalif [ "${{ matrix.render }}" = "SSR" ]exists in the script but is unreachable, meaning SSR tests won't run in CI.Not listed in
donejob (Lines 287–300): Theweb-core-wasm-e2ejob is not included in thedonejob'sneedslist, so its failure won't block the PR status check.
🤖 Fix all issues with AI agents
In `@packages/web-platform/playwright-fixtures/src/playwright.common.ts`:
- Line 45: The testMatch glob currently set in testMatch should be tightened to
only pick up actual test files and nested tests: change the pattern from
'**/tests/*' to a glob that filters by test filename conventions and extensions
(e.g., include .ts/.tsx/.js/.jsx and typical test/spec suffixes) and allow
recursive matching under tests/ so files like common.css or jsdom.ts are
excluded; update the testMatch value used in the Playwright fixture
configuration to the new glob.
1882359 to
fabeb16
Compare
There was a problem hiding this comment.
Actionable comments posted: 8
🤖 Fix all issues with AI agents
In `@packages/web-platform/web-core-wasm-e2e/server-tests/server-e2e.test.ts`:
- Around line 7-22: The test uses __dirname inside runSnapshotTest but the
project is ESM, so define __dirname from import.meta.url before it's used:
import fileURLToPath and derive __dirname via
path.dirname(fileURLToPath(import.meta.url)), placing that definition near the
top of the file after imports so runSnapshotTest and the bundlePath computation
can use it without runtime errors.
In
`@packages/web-platform/web-core-wasm/src/main_thread/server/main_thread_server_context.rs`:
- Around line 111-124: The generate_html function injects self.view_attributes
raw into the <lynx-view ...> tag (in generate_html) which allows
tag-breaking/XSS; fix by validating or escaping view_attributes before
rendering: either parse/normalize view_attributes into a structured map at
construction and render each key/value with proper HTML attribute escaping, or
at minimum sanitize/validate the string in generate_html to disallow or
percent-escape unsafe chars (e.g. '"', '>', '<', and '`') and reject/trim
invalid input; update the code paths that set/construct view_attributes so the
invariant (safe escaped attributes) is maintained and keep
render_element/style_manager usage unchanged.
- Around line 73-78: The server-side create_element
(main_thread_server_context.rs::create_element) always constructs
LynxElementData with css_id 0; replicate the client-side inheritance from
create_element_common (client/main_thread_context.rs::create_element_common
lines ~88-96) so the server method inherits the parent's css_id when a parent is
present (lookup parent in self.elements and use its css_id unless an explicit
css_id is provided), update the call to LynxElementData::new_with_tag_name
accordingly, and add a test in tests/element-apis.spec.ts that mounts a parent
with a nonzero css_id then creates a child via the server path and asserts the
child receives the inherited css_id (or adjust if SSR intentionally differs).
In `@packages/web-platform/web-core-wasm/tests/server-ssr.spec.ts`:
- Line 30: The test calls api.__FlushElementTree with two arguments but the
function is defined with zero parameters (see createElementAPI.ts), causing a
TypeScript error; update the call in server-ssr.spec.ts to invoke
api.__FlushElementTree with no arguments (remove page and {}), ensuring the call
matches the zero-argument signature of __FlushElementTree.
- Line 9: The test incorrectly imports the TypeScript type SSRBinding from the
WASM re-export; update the imports so MainThreadServerContext continues to be
imported from wasm.js but SSRBinding is imported from the module that actually
declares it (the createElementAPI TypeScript module that exports SSRBinding).
Remove SSRBinding from the wasm import list, add a separate import for
SSRBinding from the createElementAPI module (use the module name where
SSRBinding is exported), and run a quick search for "export.*SSRBinding" to
confirm the correct export surface before committing.
In `@packages/web-platform/web-core-wasm/ts/client/decodeWorker/decode.worker.ts`:
- Around line 241-252: The JSON handling path should include the same '//#
allFunctionsCalledOnLoad\n' hint as the binary blob path so
behavior/optimization is consistent; update the handleJSON branch in
decode.worker.ts to prepend that exact string before the wrapped code (matching
how the binary path builds the Blob using isLazy and code) or, if the hint is
only meaningful for binary bundles, add a brief inline comment in handleJSON
explaining why it is intentionally omitted to avoid confusion for future
maintainers.
In
`@packages/web-platform/web-core-wasm/ts/server/elementAPIs/createElementAPI.ts`:
- Around line 116-136: The new MainThreadServerContext and StyleSheetResource
(wasmContext and resource) are allocated but never disposed; after calling
__FlushElementTree() (or before returning element APIs) explicitly call their
disposal APIs (resource.free() or resource[Symbol.dispose]() and
wasmContext.free() or wasmContext[Symbol.dispose]()) or instead return a cleanup
function/closer that callers can invoke to free wasmContext and resource when
done; update createElementAPI to ensure disposal runs in all code paths
(including error paths) referencing MainThreadServerContext, StyleSheetResource,
wasmContext, resource, and __FlushElementTree.
- Around line 395-408: The server-side __GetTag implementation returns the raw
HTML tag (e.g., 'div') instead of the Lynx tag ('page'); update the server
__GetTag to reverse-map the stored tagName through HTML_TAG_TO_LYNX_TAG_MAP (the
same logic used client-side) and return the Lynx tag when a mapping exists,
otherwise fall back to the original tagName; locate the __GetTag function in the
same module that uses __CreatePage and apply the reverse lookup using
HTML_TAG_TO_LYNX_TAG_MAP so page elements return 'page' on the server as they do
on the client.
🧹 Nitpick comments (19)
packages/web-platform/web-core-wasm/src/main_thread/element_data.rs (2)
177-238: Remove commented-out dead code.This is a ~60-line block of commented-out methods (
clone_node,should_enable_exposure_event,add_event_listener_with_js_function,remove_js_function_event_listener). Since this is tracked in version control, leaving large dead code blocks hurts readability. Remove them now and recover from git history if needed later.
166-171:set_styleallocates a new"style"String on every call.
"style".to_string()on line 166 allocates a new heap String each timeset_styleis invoked. Consider using a constant orCowto avoid repeated allocations, especially if styles are set frequently during SSR rendering.♻️ Minor optimization
+const STYLE_ATTR: &str = "style"; + pub(crate) fn set_style(&mut self, key: String, value: String) { if value.is_empty() { // In SSR we do not support remove style panic!("Remove style is not supported in SSR"); } - let style = self.attributes.entry("style".to_string()).or_default(); + let style = self.attributes.entry(STYLE_ATTR.to_string()).or_default();Alternatively, if
FnvHashMapwere keyed by&'static strorCow<'static, str>, the allocation could be avoided entirely, but that would be a larger refactor.packages/web-platform/web-core-wasm/ts/common/decodeUtils.ts (1)
47-47:subarrayreturns a view, not a copy — callers must not detach the underlying buffer.
buffer.subarray(offset, offset + valLen)shares the sameArrayBuffer. If any caller transfers or detachesbuffer.buffer(e.g., viapostMessagewith a transfer list), all returnedUint8Arrayvalues become unusable. Current usage in the worker (Blob construction) appears safe, but this is a subtle contract worth documenting or defensively switching tosliceif the buffer lifecycle isn't guaranteed.packages/web-platform/web-core-wasm/ts/client/mainthread/LynxViewInstance.ts (2)
28-28: Module-level side effect:loadAllWebElements()fires at import time.This means web element preloading starts even if no
LynxViewInstanceis ever constructed. If that's the intended warm-up behaviour, a brief comment would help future readers understand the intent.
172-193:webElementsLoadingPromisesis populated inloadUnknownElementbut never consumed.
onMTSScriptsExecutedimmediately clears the array (line 174) without awaiting any promises. MeanwhileloadUnknownElement(line 217) still pushes to it. If the new preload path (loadAllWebElementsPromise) fully supersedes this mechanism, consider removing the field andloadUnknownElement's push to avoid dead-code confusion. Otherwise, if some elements can only be discovered at render time, the promises need to be awaited somewhere.packages/web-platform/web-core-wasm/src/main_thread/server/style_manager_server.rs (3)
26-44: Unnecessaryclone()and avoidable allocation forentry_name.On line 31,
entry_name.clone().unwrap_or_else(…)clones theOption<String>for no reason —entry_nameis already an ownedOption<String>and is never used after this line. Additionally,push_style_sheettakesresourceby reference only to.clone()it on line 42. If callers always yield ownership, acceptingresource: StyleSheetResourceavoids the extra clone.Proposed fix
pub fn push_style_sheet( &mut self, - resource: &StyleSheetResource, + resource: StyleSheetResource, entry_name: Option<String>, ) -> Result<(), String> { - let entry_key = entry_name.clone().unwrap_or_else(|| "__Card__".to_string()); + let entry_key = entry_name.unwrap_or_else(|| "__Card__".to_string()); if let Some(content) = &resource.style_content_str { self.global_style_buffer.push_str(content); @@ .. self .css_query_map_by_entry_name - .insert(entry_key, resource.clone()); + .insert(entry_key, resource); Ok(()) }
31-31: Duplicated magic string"__Card__".The default entry name
"__Card__"appears on both line 31 and line 53. Extract it into aconstto keep the two call sites consistent and to make renaming painless.Proposed fix
+const DEFAULT_ENTRY_NAME: &str = "__Card__"; + impl StyleManagerServer { ... - let entry_key = entry_name.clone().unwrap_or_else(|| "__Card__".to_string()); + let entry_key = entry_name.unwrap_or_else(|| DEFAULT_ENTRY_NAME.to_string()); ... - let entry_name = entry_name.as_deref().unwrap_or("__Card__"); + let entry_name = entry_name.as_deref().unwrap_or(DEFAULT_ENTRY_NAME);Also applies to: 53-53
74-80:get_css_stringclones the entireglobal_style_buffer.For large stylesheets in SSR, this allocates a full copy. If
get_css_stringis the terminal call (i.e., the server context is consumed afterwards), consider a variant that takesselfby value (fn into_css_string(self) -> String) to avoid the clone. Not critical, but worth noting for performance-sensitive SSR paths.packages/web-platform/web-core-wasm-e2e/server-tests/server-e2e.test.ts (1)
25-95: Consider parameterized tests to reduce boilerplate.All 17 test cases follow the identical pattern of calling
runSnapshotTestwith a bundle name. Vitest'stest.each(orit.each) would collapse this to a single declaration with a table of bundle names, making additions trivial and the file much shorter.Example
-test('executeTemplate should run lepusCode.root from basic-pink-rect.web.bundle', async () => { - await runSnapshotTest('basic-pink-rect.web.bundle'); -}); -// ... 16 more copies +const bundles = [ + 'basic-pink-rect.web.bundle', + 'config-css-default-display-linear-false.web.bundle', + 'config-css-remove-scope-false-display-linear.web.bundle', + 'config-css-selector-false-remove-css-and-style-collapsed.web.bundle', + 'basic-element-text-baseline.web.bundle', + 'basic-scroll-view.web.bundle', + 'basic-element-x-audio-tt-play.web.bundle', + 'basic-element-image-src.web.bundle', + 'basic-element-x-input-value.web.bundle', + 'basic-element-list-basic.web.bundle', + 'basic-element-x-overlay-ng-demo.web.bundle', + 'basic-element-x-refresh-view-demo.web.bundle', + 'basic-element-svg-with-css.web.bundle', + 'basic-element-x-swiper-autoplay.web.bundle', + 'basic-element-text-color.web.bundle', + 'basic-element-x-textarea-placeholder.web.bundle', + 'basic-element-x-viewpager-ng-bindchange.web.bundle', +]; + +test.each(bundles)( + 'executeTemplate should run lepusCode.root from %s', + async (bundle) => { + await runSnapshotTest(bundle); + }, +);packages/web-platform/web-core-wasm/tests/server-ssr.spec.ts (1)
36-36: Removeconsole.logdebug statements from tests.Lines 36 and 62 contain
console.logcalls that pollute test output. In a CI environment, this noise makes it harder to spot real problems.Also applies to: 62-62
packages/web-platform/web-core-wasm/ts/client/mainthread/TemplateManager.ts (2)
304-307:getStyleSheetreturnsany— loses type safety.The
StyleSheetResourceclass has a well-defined shape (withfree(),[Symbol.dispose](), etc. per the.d.tsfiles). Returninganydiscards all that information for callers. Consider a narrower return type, even if the exact WASM-generated type isn't directly importable at this level.Proposed fix
- public getStyleSheet(url: string): any { + public getStyleSheet(url: string): DecodedTemplate['styleSheet'] { return this.#templates.get(url)?.styleSheet; }(Assumes
DecodedTemplateis updated to include a typedstyleSheetproperty — if not, at minimum useunknowninstead ofany.)
265-268:#removeTemplatecreates an empty template only to immediately delete it.
createTemplate(url)on line 266 cleans up the old entry (which is good) but also inserts a fresh empty{}into the map (line 262). The very next line then deletes it. This is correct but wasteful and confusing to readers.Consider extracting the cleanup logic from
createTemplateinto a private helper (e.g.,#cleanupTemplate) that both methods can call, so#removeTemplatedoesn't perform the unnecessary insert.packages/web-platform/web-core-wasm/tests/decode.spec.ts (1)
59-64: Unnecessary@ts-ignoreon line 60.The ternary on lines 61-63 should type-check without suppression —
callbacks.magicisbigint | undefined, and the ternary produces abigint. Consider removing the@ts-ignore.Suggested fix
- // `@ts-ignore` const magicVal = callbacks.magic !== undefined ? callbacks.magic : BigInt(MagicHeader);packages/web-platform/web-core-wasm/ts/server/decode.ts (2)
76-83:buffervariable shadows the function parameter.The
const bufferon line 77 shadows the outerbufferparameter. While it doesn't cause a bug in this specificcaseblock, it's confusing and error-prone if the code is later refactored.Also, the comment on line 79 reads like internal development notes — consider cleaning it up or removing it.
Suggested fix
case TemplateSectionLabel.StyleInfo: { - const buffer = decode_style_info( + const decodedStyleInfo = decode_style_info( content, - config['isLazy'] === 'true' ? '' : undefined, // URL is not available in synchronous decode usually, or passed as arg? The user req says "uint8array as params decode directly". Assuming URL is empty or unneeded for sync server decode unless specified. + config['isLazy'] === 'true' ? '' : undefined, config['enableCSSSelector'] === 'true', ); - styleInfo = buffer; + styleInfo = decodedStyleInfo; break; }
47-49: Inconsistent handling of partial label read vs. partial length read.When there are fewer than 4 bytes remaining for the label (line 48), the code silently
breaks out of the loop. But if there are fewer than 4 bytes for the section length (line 54), it throws. A partial label is likely a truncated stream too — consider throwing in both cases or documenting why the asymmetry is intentional.packages/web-platform/web-core-wasm/ts/server/elementAPIs/createElementAPI.ts (1)
88-96: Wrapping user code in an IIFE with shadowed globals —documentis not shadowed.The wrapped code shadows
navigator,postMessage, andwindow, butdocumentis not shadowed. If template code referencesdocument, it could leak into the host process. If this is server-side SSR code that should never touch the DOM directly, consider also shadowingdocument.Suggested addition in deploy.ts
const navigator = undefined; const postMessage = undefined; const window = undefined; + const document = undefined;packages/web-platform/web-core-wasm/src/main_thread/client/main_thread_context.rs (1)
146-151: Consider removing commented-outgcmethod.This dead code has been commented out. If it's not planned for near-term use, removing it keeps the file cleaner. Version control preserves history if needed later.
packages/web-platform/web-core-wasm/src/main_thread/server/main_thread_server_context.rs (2)
12-12: Remove commented-out import.-// use crate::constants;
80-84: No validation thatchild_idrefers to a valid element or isn't already parented.
append_childsilently does nothing ifparent_idis invalid, but doesn't check ifchild_idactually exists inself.elements. If an invalidchild_idis appended,render_elementwill silently skip it (line 152 checksself.elements.get(element_id)), so there's no crash, but it's a silent data integrity issue.This is acceptable for a WIP, but worth noting for robustness.
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/web-platform/web-core-wasm/ts/client/mainthread/LynxViewInstance.ts (1)
128-136:⚠️ Potential issue | 🟠 MajorMultiple
renderPageassignments will trigger duplicateonMTSScriptsExecutedcalls.Every assignment to
renderPagequeues a new microtask that invokesonMTSScriptsExecuted, which starts the web worker and callsrenderPage. If the setter is invoked more than once (even by the same frame script re-assigning it), you'll get duplicate worker starts and render calls.Consider guarding with a flag or replacing repeated
queueMicrotaskcalls:Proposed guard
+ `#mtsScriptsExecutedScheduled` = false; + // inside the setter: this.#renderPageFunction = v; - queueMicrotask(() => { - this.onMTSScriptsExecuted(); - }); + if (!this.#mtsScriptsExecutedScheduled) { + this.#mtsScriptsExecutedScheduled = true; + queueMicrotask(() => { + this.onMTSScriptsExecuted(); + }); + }
🤖 Fix all issues with AI agents
In
`@packages/web-platform/web-core-wasm/ts/client/mainthread/LynxViewInstance.ts`:
- Line 28: The module-level fire-and-forget call to loadAllWebElements() can
produce unhandled promise rejections if the dynamic import fails; update the
call site to handle errors (for example, append .catch(err => { /* log error */
}) or invoke it from an async init function wrapped with try/catch) so any
failure is logged; reference the existing loadAllWebElements invocation in
LynxViewInstance.ts and ensure the catch logs the error (console.error or the
project's logger) to avoid unhandled rejections.
🧹 Nitpick comments (1)
packages/web-platform/web-elements/src/elements/XSwiper/XSwiperIndicator.ts (1)
138-149: PreferparseInt(or a bitwise floor) overparseFloatfor a child-element index.
parseFloatcan yield a fractional number (e.g."1.5"→1.5), andHTMLCollectionindexed by a non-integer returnsundefined, silently skipping the highlight. UsingparseInt(…, 10)(orNumber(…) | 0) makes the integer intent explicit and handles the sameNaN-to-undefinedfallback via?..Proposed fix
- const firstPaintIndex = parseFloat( - this.#dom.getAttribute('current') ?? '0', - ); + const firstPaintIndex = parseInt( + this.#dom.getAttribute('current') ?? '0', + 10, + );
…age, and refine Rust type casting.
…or style info, and add server E2E tests with Vitest.
…I parameters, update test paths, and enable composite compilation for e2e tests.
…t and server APIs, and add e2e tests.
…ut in `<lynx-view>` with attributes.
dd394ac to
fc3c9a6
Compare
There was a problem hiding this comment.
Pull request overview
This PR introduces a server-side rendering (SSR) runtime for web-core-wasm, adds a Rust web_elements crate to mirror web-elements shadow DOM templates for SSR, and expands SSR-focused test/bench infrastructure.
Changes:
- Add
ts/serverSSR runtime (decode + VM execution + server ElementPAPIs) and a new server Wasm target. - Introduce
packages/web-platform/web-elementsRust crate mirroringhtmlTemplates.ts, with a parity test. - Refactor style-sheet handling to use
StyleSheetResourceacross client/server paths and add SSR/e2e tests & snapshots.
Reviewed changes
Copilot reviewed 74 out of 80 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| pnpm-lock.yaml | Adds new dev deps (e.g., Prettier/Vitest/CodSpeed) for the e2e package. |
| packages/web-platform/web-elements/tests/template_sync.rs | Rust test to enforce TS/Rust template parity via Node execution. |
| packages/web-platform/web-elements/src/template.rs | Rust mirror of htmlTemplates.ts templates and helpers. |
| packages/web-platform/web-elements/src/lib.rs | Exposes the template module for the new Rust crate. |
| packages/web-platform/web-elements/src/elements/htmlTemplates.ts | Adds synchronization notice for mirrored Rust templates. |
| packages/web-platform/web-elements/src/elements/common/constants.ts | Removes UA-derived exports, keeps useScrollEnd and symbols. |
| packages/web-platform/web-elements/src/elements/XSwiper/XSwiperIndicator.ts | Updates indicator initial paint logic (removes microtask usage). |
| packages/web-platform/web-elements/src/compat/LinearContainer/LinearContainer.ts | Removes direct CSS import (now handled via global CSS imports). |
| packages/web-platform/web-elements/Cargo.toml | Adds new web_elements Rust crate manifest. |
| packages/web-platform/web-elements/AGENTS.md | Documents TS/Rust template synchronization + test location. |
| packages/web-platform/web-core-wasm/ts/types/DecodedTemplate.ts | Extends decoded template type with styleSheet. |
| packages/web-platform/web-core-wasm/ts/server/wasm.ts | Exposes server wasm bindings via a server entrypoint. |
| packages/web-platform/web-core-wasm/ts/server/index.ts | Adds public SSR exports (deploy/decode/ElementAPIs/wasm). |
| packages/web-platform/web-core-wasm/ts/server/elementAPIs/pureElementAPIs.ts | Introduces SSR-safe/stub element APIs for server runtime. |
| packages/web-platform/web-core-wasm/ts/server/elementAPIs/createElementAPI.ts | Implements server ElementPAPIs backed by MainThreadServerContext. |
| packages/web-platform/web-core-wasm/ts/server/deploy.ts | Executes bundled templates in a Node vm sandbox to produce SSR HTML. |
| packages/web-platform/web-core-wasm/ts/server/decode.ts | Adds synchronous server-side template bundle decoder. |
| packages/web-platform/web-core-wasm/ts/server/createServerLynx.ts | Adds SSR lynx global mock implementation for template execution. |
| packages/web-platform/web-core-wasm/ts/common/decodeUtils.ts | Extracts shared decodeBinaryMap utility (used by worker/server). |
| packages/web-platform/web-core-wasm/ts/client/wasm.ts | Removes TemplateManager wasm singleton usage from client path. |
| packages/web-platform/web-core-wasm/ts/client/mainthread/utils/requestIdleCallback.ts | Adds requestIdleCallback fallback helper for timing dispatch. |
| packages/web-platform/web-core-wasm/ts/client/mainthread/elementAPIs/createElementAPI.ts | Switches timing dispatch from rAF to requestIdleCallback fallback. |
| packages/web-platform/web-core-wasm/ts/client/mainthread/TemplateManager.ts | Refactors style-info handling to store/free StyleSheetResource directly. |
| packages/web-platform/web-core-wasm/ts/client/mainthread/LynxViewInstance.ts | Updates style push path to use TemplateManager-stored StyleSheetResource. |
| packages/web-platform/web-core-wasm/ts/client/mainthread/Background.ts | Minor cleanup in RPC initialization. |
| packages/web-platform/web-core-wasm/ts/client/decodeWorker/decode.worker.ts | Reuses shared decodeBinaryMap; adjusts blob sourceURL generation. |
| packages/web-platform/web-core-wasm/tests/server-ssr.spec.ts | Adds vitest SSR unit tests for server ElementPAPIs/HTML generation. |
| packages/web-platform/web-core-wasm/tests/server-compat.spec.ts | Adds snapshot-based SSR compatibility/perf-style tests. |
| packages/web-platform/web-core-wasm/tests/decode.spec.ts | Adds tests for decodeTemplate server decoder. |
| packages/web-platform/web-core-wasm/tests/snapshots/server-compat.spec.ts.snap | New snapshots for server compat tests. |
| packages/web-platform/web-core-wasm/src/template/template_sections/style_info/style_sheet_resource.rs | Makes StyleSheetResource usable for both client/server; adds server string fields. |
| packages/web-platform/web-core-wasm/src/template/template_sections/style_info/style_info_decoder.rs | Minor test variable rename cleanup. |
| packages/web-platform/web-core-wasm/src/template/template_sections/style_info/raw_style_info.rs | Removes wasm_bindgen annotations from some methods (feature-gated encode stays). |
| packages/web-platform/web-core-wasm/src/template/template_sections/style_info/mod.rs | Exposes style resource module for both client and server features. |
| packages/web-platform/web-core-wasm/src/template/template_manager.rs | Removes TemplateManager implementation (no longer used). |
| packages/web-platform/web-core-wasm/src/template/mod.rs | Removes TemplateManager export and module include. |
| packages/web-platform/web-core-wasm/src/style_transformer/transformer.rs | Enables tokenizer usage for server feature as well. |
| packages/web-platform/web-core-wasm/src/style_transformer/mod.rs | Adjusts exports/feature-gates for server usage. |
| packages/web-platform/web-core-wasm/src/style_transformer/inline_style.rs | Feature-gates key-value transform to client; keeps string transform for server. |
| packages/web-platform/web-core-wasm/src/main_thread/server/style_manager_server.rs | Adds server-side style manager for SSR CSS string generation and CSSOG rules. |
| packages/web-platform/web-core-wasm/src/main_thread/server/mod.rs | Introduces server main_thread modules. |
| packages/web-platform/web-core-wasm/src/main_thread/server/main_thread_server_context.rs | Adds SSR element tree store + HTML renderer using web_elements templates. |
| packages/web-platform/web-core-wasm/src/main_thread/mod.rs | Splits main_thread into client/server modules with feature gating. |
| packages/web-platform/web-core-wasm/src/main_thread/element_data.rs | Adds server-specific element data fields (tag/attributes/children) + style setters. |
| packages/web-platform/web-core-wasm/src/main_thread/client/style_manager.rs | Refactors to store cloned StyleSheetResource (no Rc in map). |
| packages/web-platform/web-core-wasm/src/main_thread/client/mod.rs | Adds client module root after main_thread refactor. |
| packages/web-platform/web-core-wasm/src/main_thread/client/main_thread_context.rs | Updates push_style_sheet signature to accept StyleSheetResource directly. |
| packages/web-platform/web-core-wasm/src/main_thread/client/element_apis/style_apis.rs | Removes redundant wasm_bindgen annotations after refactor. |
| packages/web-platform/web-core-wasm/src/main_thread/client/element_apis/mod.rs | Removes old element_data module wiring. |
| packages/web-platform/web-core-wasm/src/main_thread/client/element_apis/event_apis.rs | Removes redundant wasm_bindgen annotations. |
| packages/web-platform/web-core-wasm/src/main_thread/client/element_apis/dataset_apis.rs | Removes redundant wasm_bindgen annotations. |
| packages/web-platform/web-core-wasm/src/main_thread/client/element_apis/component_apis.rs | Removes redundant wasm_bindgen annotations. |
| packages/web-platform/web-core-wasm/src/lib.rs | Updates exports for new client module structure; removes TemplateManager export. |
| packages/web-platform/web-core-wasm/src/constants.rs | Makes LYNX_UNIQUE_ID_ATTRIBUTE available for server feature. |
| packages/web-platform/web-core-wasm/scripts/build.js | Adds server wasm build target. |
| packages/web-platform/web-core-wasm/package.json | Adds ./server export and updates prod asset export paths. |
| packages/web-platform/web-core-wasm/binary/server/server_bg.wasm.d.ts | Adds generated server wasm typings. |
| packages/web-platform/web-core-wasm/binary/server/server.d.ts | Adds generated server JS typings (SSR APIs + StyleSheetResource). |
| packages/web-platform/web-core-wasm/binary/client/client_bg.wasm.d.ts | Updates generated client wasm typings after TemplateManager removal. |
| packages/web-platform/web-core-wasm/binary/client/client.d.ts | Updates generated client JS typings (push_style_sheet signature; StyleSheetResource). |
| packages/web-platform/web-core-wasm/Cargo.toml | Adds dependency on new web_elements crate. |
| packages/web-platform/web-core-wasm/AGENTS.md | Documents new ts/server runtime and updated build variants. |
| packages/web-platform/web-core-wasm-e2e/vitest.config.ts | Adds vitest config, aliases, and optional CodSpeed plugin for SSR e2e/bench. |
| packages/web-platform/web-core-wasm-e2e/tsconfig.json | Enables composite builds for the e2e project. |
| packages/web-platform/web-core-wasm-e2e/tests/web-core.test.ts | Updates e2e tests to flush without relying on renderPage assignment. |
| packages/web-platform/web-core-wasm-e2e/tests/ssr-no-js.spec.ts | Adds Playwright “No JS” SSR page validation test. |
| packages/web-platform/web-core-wasm-e2e/shell-project/ssr.html | Adds SSR shell page that injects server-rendered output. |
| packages/web-platform/web-core-wasm-e2e/shell-project/devMiddleware.ts | Adds dev SSR middleware using executeTemplate. |
| packages/web-platform/web-core-wasm-e2e/server-tests/server-e2e.test.ts | Adds snapshot-driven SSR execution tests against built bundles. |
| packages/web-platform/web-core-wasm-e2e/server-tests/snapshots/server-e2e.test.ts.snap | Adds SSR output snapshots for many bundle scenarios. |
| packages/web-platform/web-core-wasm-e2e/rsbuild.config.ts | Registers SSR middleware in dev server pipeline. |
| packages/web-platform/web-core-wasm-e2e/package.json | Adds bench script and dev deps (vitest/prettier/codspeed). |
| packages/web-platform/web-core-wasm-e2e/bench/server.bench.vitest.spec.ts | Adds server-side SSR performance benchmarks. |
| packages/web-platform/playwright-fixtures/src/playwright.common.ts | Changes default Playwright testMatch pattern. |
| packages/web-platform/playwright-fixtures/src/coverage-fixture.ts | Skips JS coverage collection for the No-JS SSR Playwright suite. |
| Cargo.toml | Adds packages/web-platform/web-elements to the Rust workspace. |
| Cargo.lock | Locks new workspace crate dependencies (web_elements). |
| .vscode/settings.json | Disables typescript.experimental.useTsgo. |
| .github/workflows/test.yml | Adjusts Playwright sharding across jobs. |
| .changeset/lemon-pigs-teach.md | Adds a changeset file (currently empty/invalid). |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
Comments suppressed due to low confidence (2)
packages/web-platform/web-core-wasm/ts/server/decode.ts:82
- For lazy templates, the client passes the template
urlintodecode_style_info, but the server decoder passes an empty string. Passing""is not equivalent toundefinedand can produce incorrectly-scoped CSS (e.g., selectors keyed by an empty entry name). Consider accepting a template identifier/url as a parameter todecodeTemplate(orexecuteTemplate) and pass it through here; otherwise keep behavior consistent with the client by usingundefinedwhen no url is available.
packages/web-platform/web-core-wasm/ts/client/mainthread/utils/requestIdleCallback.ts:5 - The exported
requestIdleCallbackImplends up with an imprecise union type between the realrequestIdleCallbacksignature (callback receives anIdleDeadline) and the fallback(callback: () => void). This can cause type errors at call sites. Consider typing it explicitly (e.g.,const requestIdleCallbackImpl: typeof requestIdleCallback = ...) and adapting the fallback to accept theIdleDeadlineparameter (even if ignored).
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
ce97e9a to
2254b4d
Compare
d7b1ab7 to
28deb51
Compare
… and `__AddDataset` no-ops in SSR, and update e2e snapshots.
28deb51 to
d964453
Compare
…ransformation and refactor CSS Object Graph (CSSOG) attribute handling.
…eate_element` with parent context.
…s for `l-css-id` attribute.
This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated. # Releases ## @lynx-js/react@0.116.4 ### Patch Changes - Support `ReactLynx::hooks::setState` trace for function components. ([#2198](#2198)) - fix: properly cleanup `__DestroyLifetime` listeners and listCallbacks in `snapshotDestroyList`. ([#2224](#2224)) ## @lynx-js/qrcode-rsbuild-plugin@0.4.6 ### Patch Changes - Print all entries with all schema URLs in non-TTY environments instead of only showing the first entry's QR code. ([#2227](#2227)) ## @lynx-js/react-rsbuild-plugin@0.12.9 ### Patch Changes - Add alias for `use-sync-external-store/with-selector.js` and `use-sync-external-store/shim/with-selector.js` pointing to @lynx-js/use-sync-external-store. ([#2200](#2200)) - Updated dependencies \[[`9033e2d`](9033e2d)]: - @lynx-js/template-webpack-plugin@0.10.4 - @lynx-js/react-alias-rsbuild-plugin@0.12.9 - @lynx-js/use-sync-external-store@1.5.0 - @lynx-js/react-refresh-webpack-plugin@0.3.4 - @lynx-js/react-webpack-plugin@0.7.4 - @lynx-js/css-extract-webpack-plugin@0.7.0 ## @lynx-js/css-serializer@0.1.4 ### Patch Changes - Move `cssChunksToMap` implementation from `@lynx-js/template-webpack-plugin` to `@lynx-js/css-serializer` for future reuse. ([#2269](#2269)) ## @lynx-js/web-core-wasm@0.0.4 ### Patch Changes - Refactor web element templates and server-side rendering logic ([#2205](#2205)) - Updated dependencies \[[`94e5779`](94e5779), [`9033e2d`](9033e2d)]: - @lynx-js/web-elements@0.11.3 - @lynx-js/css-serializer@0.1.4 ## @lynx-js/web-elements@0.11.3 ### Patch Changes - fix: firefox 147+ layout issue ([#2205](#2205)) ## @lynx-js/template-webpack-plugin@0.10.4 ### Patch Changes - Move `cssChunksToMap` implementation from `@lynx-js/template-webpack-plugin` to `@lynx-js/css-serializer` for future reuse. ([#2269](#2269)) - Updated dependencies \[[`9033e2d`](9033e2d)]: - @lynx-js/css-serializer@0.1.4 ## @lynx-js/react-alias-rsbuild-plugin@0.12.9 Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Summary by CodeRabbit
New Features
Tests
Chores
Documentation
Checklist