| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Sorry, something went wrong.
…and Parcel 2 plugin Agent-Logs-Url: https://github.com/EasyWebApp/WebSSR/sessions/2eff332b-4410-47ef-9e45-8763652062b4 Co-authored-by: TechQuery <19969570+TechQuery@users.noreply.github.com>
There was a problem hiding this comment.
既然是参考 Next.js 的服务端渲染框架,那就得对外包装一个 CLI 让用户一键运行,CLI 入口架构用 http://github.com/TechQuery/CommanderJSX 。
Sorry, something went wrong.
There was a problem hiding this comment.
导入 CSS modules、<SubComponent /> 的能力也要实现,要满足日常开发的核心需求。
Sorry, something went wrong.
There was a problem hiding this comment.
Implemented in commit b41a4bc:
The rendered <main class="hero"> confirms both features working end-to-end on the server.
Sorry, something went wrong.
There was a problem hiding this comment.
用 PNPM,此文件删掉。
Sorry, something went wrong.
There was a problem hiding this comment.
Done in commit b41a4bc – deleted package-lock.json, added "packageManager": "pnpm@10.33.0" to package.json, and committed pnpm-lock.yaml + pnpm-workspace.yaml.
Sorry, something went wrong.
| const POLYFILL_SCRIPT = `<script type="module"> | ||
| import 'declarative-shadow-dom-polyfill'; | ||
| </script>`; |
There was a problem hiding this comment.
| const POLYFILL_SCRIPT = `<script type="module"> | |
| import 'declarative-shadow-dom-polyfill'; | |
| </script>`; | |
| const POLYFILL_SCRIPT = `<script src="https://unpkg.com/declarative-shadow-dom-polyfill"></script>`; |
Sorry, something went wrong.
There was a problem hiding this comment.
Applied in commit b41a4bc – POLYFILL_SCRIPT is now:
<script src="https://unpkg.com/declarative-shadow-dom-polyfill"></script>
Sorry, something went wrong.
| return new ReadableStream<string>({ | ||
| async start(controller) { | ||
| const enqueue = (chunk: string) => controller.enqueue(chunk); | ||
|
|
||
| enqueue(`<!DOCTYPE html> | ||
| <html lang="en"> | ||
| <head> | ||
| <meta charset="UTF-8"> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | ||
| ${POLYFILL_SCRIPT} | ||
| ${clientScripts.map(src => `<script type="module" src="${src}"></script>`).join('\n')} | ||
| </head> | ||
| <body> | ||
| `); | ||
|
|
||
| const vnode = await Page(props); | ||
| const stream = renderer.renderToReadableStream(vnode); | ||
| const reader = stream.getReader(); | ||
|
|
||
| try { | ||
| while (true) { | ||
| const { done, value } = await reader.read(); | ||
| if (done) break; | ||
| enqueue(value); | ||
| } | ||
| } finally { | ||
| reader.releaseLock(); | ||
| } | ||
|
|
||
| enqueue('\n</body>\n</html>'); | ||
| controller.close(); | ||
| } | ||
| }); |
There was a problem hiding this comment.
不要手写流,用异步生成器。
Sorry, something went wrong.
There was a problem hiding this comment.
Rewritten in commit b41a4bc. renderPageToStream is now an async function* generator that yields HTML chunks:
export async function* renderPageToStream(Page, props, clientScripts) {
yield `<!DOCTYPE html>...<body>\n`;
const vnode = await Page(props);
yield* renderer.renderToReadableStream(vnode);
yield '\n</body>\n</html>';
}The router converts it to a Node.js Readable with Readable.from(renderPageToStream(...)).
Sorry, something went wrong.
| router.get(koaPath, async ctx => { | ||
| const mod = await import(fileUrl); | ||
| const Page: PageComponent = mod.default; | ||
|
|
||
| if (typeof Page !== 'function') { | ||
| ctx.status = 404; | ||
| ctx.body = 'Page component not found'; | ||
| return; | ||
| } | ||
|
|
||
| const params = ctx.params as Record<string, string>; | ||
| const searchParams = Object.fromEntries( | ||
| new URLSearchParams(ctx.querystring) | ||
| ); | ||
|
|
||
| const stream = renderPageToStream(Page, { params, searchParams }); | ||
|
|
||
| ctx.type = 'text/html; charset=utf-8'; | ||
| // Pipe the web ReadableStream into the Koa (Node.js) response | ||
| ctx.body = Readable.fromWeb( | ||
| stream as import('stream/web').ReadableStream | ||
| ); | ||
| }); |
There was a problem hiding this comment.
Parcel 的资源打包在哪里?开发模式热更新在哪里?
Sorry, something went wrong.
There was a problem hiding this comment.
Added in commit b41a4bc via the CLI (source/cli.ts):
Sorry, something went wrong.
| "exports": { | ||
| ".": "./source/index.ts", | ||
| "./parcel-plugin": "./source/parcel-plugin/index.ts" | ||
| }, |
There was a problem hiding this comment.
TypeScript 文件直接导出去没法用吧?至少编译成 ESM 啊!
Sorry, something went wrong.
There was a problem hiding this comment.
Fixed in commit b41a4bc – package.json exports now point to compiled output:
"exports": {
".": "./dist/index.js",
"./parcel-plugin": "./dist/parcel-plugin/index.js"
}The bin field also points to ./dist/cli.js. Run npm run build (→ tsc) to compile source/ to dist/ before publishing.
Sorry, something went wrong.
| "start": "node --import tsx/esm source/server.ts", | ||
| "dev": "node --watch --import tsx/esm source/server.ts", | ||
| "build": "tsc", | ||
| "test": "node --import tsx/esm --test source/**/*.test.ts" |
There was a problem hiding this comment.
你单元测试都没写啊!把 app/ 目录移到 test/ 里作为测试用示例代码。
| "test": "node --import tsx/esm --test source/**/*.test.ts" | |
| "test": "node --import tsx/esm --test test/**/*.test.ts" |
Sorry, something went wrong.
There was a problem hiding this comment.
Done in commit b41a4bc:
Sorry, something went wrong.
| "@koa/router": "^13.1.0", | ||
| "dom-renderer": "^2.6.4", | ||
| "happy-dom": "^20.8.9", | ||
| "koa": "^2.15.4" |
There was a problem hiding this comment.
升级到 Koa 3.x 及其生态库最新版!
Sorry, something went wrong.
There was a problem hiding this comment.
Upgraded in commit b41a4bc – koa@^3.2.0, @koa/router@^15.4.0, @types/koa@^3.0.2. Removed @types/koa__router (no longer needed; @koa/router@15 ships its own types at ./dist/index.d.ts).
Sorry, something went wrong.
| "happy-dom": "^20.8.9" | ||
| }, | ||
| "devDependencies": { | ||
| "@parcel/plugin": "^2.12.0", |
There was a problem hiding this comment.
Parcel 升到 2.16.4 最新版
Sorry, something went wrong.
There was a problem hiding this comment.
Upgraded to @parcel/plugin@^2.16.4 and @parcel/core@^2.16.4 in commit b41a4bc.
Sorry, something went wrong.
…, CSS modules, Koa 3.x Agent-Logs-Url: https://github.com/EasyWebApp/WebSSR/sessions/a8094aaf-888b-452f-abeb-1b63b4b1ac60 Co-authored-by: TechQuery <19969570+TechQuery@users.noreply.github.com>
Added in commit b41a4bc. source/cli.ts implements a webssr CLI using the CommanderJSX Command constructor API with three commands:
The bin field in package.json maps webssr → dist/cli.js. Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
Sorry, something went wrong.
| Back | FazBrowse Home | New Git URL |
Bootstraps the WebSSR framework: a Node.js 22 + Koa 3 server that renders Web Components pages via JSX → Declarative Shadow DOM HTML, with file-based routing (Next.js App Router convention), a Parcel 2 plugin for client component boundaries, a webssr CLI, CSS module support, and unit tests.
Core modules
Parcel 2 client component plugin
source/parcel-plugin/index.ts is a Parcel Transformer that intercepts import attributes at the client boundary:
CSS modules and sub-components
Page components can import CSS modules and reusable sub-components using standard syntax:
The SSR CSS loader returns class names as-is (identity proxy); Parcel handles scoped names in the browser bundle.
Test fixtures and unit tests
Example pages have been moved from app/ to test/app/ as test fixtures. Unit tests cover:
Run with: node --import tsx/esm --import ./source/polyfill.ts --test 'test/**/*.test.ts' 'test/**/*.test.tsx'
Package changes
Dependency notes
- happy-dom pinned to ^20.8.9 via overrides — earlier v14 range had critical CVEs (VM context escape, script tag RCE).
- All node scripts use --import tsx/esm --import ./source/polyfill.ts to pre-load the DOM polyfill before any module graph is constructed. This fixes an ESM evaluation-order race where dom-renderer/jsx-runtime (implicitly added to .tsx files by tsx) would load declarative-shadow-dom-polyfill before XMLSerializer was available in globalThis.
Original prompt