Skip to content

Releases: lessjs-run/lessjs

v0.14.8

15 May 16:58

Choose a tag to compare

LessJS 修复总结报告 / LessJS Fix Summary Report

日期 / Date: 2026-05-16
基于审核 / Based on: deliverables/review260515/20260515-full-review.md
修复版本 / Fix Version: v0.14.7-dev
状态 / Status: 全部完成 / All Complete


一、SSG 构建管线修复 / SSG Build Pipeline Fixes

CI 构建失败 deno task build:docs 的两个根因已定位并修复。

BUG-1: DsdLitElement 缺失导出 / Missing Export in Optional Stub

项目 详情
错误 MISSING_EXPORT: "DsdLitElement" is not exported by virtual stub
文件 packages/adapter-vite/src/cli/build-ssg.ts:60-67
根因 optionalPackageStubsPlugin()@lessjs/adapter-lit 的 stub 缺少 DsdLitElementWithDsdHydration 两个导出
修复 在 stub 字符串中添加 'export const DsdLitElement = undefined;''export const WithDsdHydration = undefined;'
状态 ✅ 已修复

BUG-2: Rolldown 无法解析 @lessjs/core / Rolldown Failed to Resolve @lessjs/core

项目 详情
错误 Rolldown failed to resolve import "@lessjs/core" from "virtual:less-ssg-entry"
文件 packages/adapter-vite/src/workspace-alias.ts:16-66
根因 deno.json// 注释,JSON.parse 无法解析 → tryReadJson 返回 null → 工作区别名全部丢失 → 35 个包无法解析
修复过程 1. 首次尝试:朴素正则 replace(/\/\/.*$/gm, '') 删除了 URL 中的 //(如 https://deno.landhttps:)❌
2. 最终方案:逐字符解析器,跟踪 inString / escape 状态,仅在字符串外删除注释 ✅
验证 修复后正确生成 35 个别名(含 @lessjs/core
状态 ✅ 已修复

附带修复 / Incidental Fix

  • SSR viteBuild() 中误加重复 resolve 键 → 已回退,原有 resolve 块已正确处理别名
  • deno fmt 格式化紧凑 if 语句
  • 文件锁 (EBUSY) 通过 git stash && git stash pop 清除

二、Batch A — 安全性 HIGH 级修复 / Batch A — Security HIGH Fixes (8 Items)

A-1. H-01: validateSafeUrl 误捕自身 LessError

项目 详情
文件 packages/adapter-vite/src/index.ts:245-247
问题 LessErrortry 内抛出后被 catch 重新包装为 "malformed percent-encoding",丢失原始安全警告
修复 catch 块顶部添加 if (e instanceof LessError) throw e;
状态 ✅ 已修复

A-2. H-04/05: 8 个外部 CDN 资源无 SRI

项目 详情
文件 www/vite.config.ts:87-151
问题 7 个 Prism CDN 脚本 + GoatCounter 脚本 + 2 个 CSS 样式表均无 integrity 属性;CDN 被攻破 = 全站 XSS
修复 为所有外部 <script><link> 添加 integrity + crossorigin="anonymous" 属性
说明 使用 srihash.org 或本地计算 SHA-384 hash
状态 ✅ 已修复

A-3. H-12: Deno.readTextFileSync 在 Node.js 中崩溃

项目 详情
文件 packages/adapter-vite/src/workspace-alias.ts:20-22
问题 Vite 插件在 Node.js 中运行,Deno.readTextFileSyncReferenceError
修复 添加平台守卫:typeof Deno !== 'undefined' ? Deno.readTextFileSync(path) : require('node:fs').readFileSync(path, 'utf-8')
状态 ✅ 已修复

A-4. H-13: jsrNames 缺少 adapterVite

项目 详情
文件 packages/create/cli.ts:96
问题 jsrNames['adapterVite']undefined,fetch @lessjs/undefined/meta 导致 CLI 崩溃
修复 添加 adapterVite: 'adapter-vite'
状态 ✅ 已修复

A-5. H-14: 项目名称未校验

项目 详情
文件 packages/create/cli.ts:268-273
问题 接受 ../etcfoo/bar 等名称,路径穿越风险
修复 添加 if (!/^[a-zA-Z0-9_-]+$/.test(name)) 报错退出
状态 ✅ 已修复

A-6. H-17: npx -y 无版本锁定

项目 详情
文件 deno.json:87
问题 npx -y 自动下载执行未锁定版本,供应链攻击风险
修复 改为 npx -y @playwright/test@1.59.1npx -y playwright@1.59.1 install chromium
状态 ✅ 已修复

A-7. H-18: deno run -A 权限过宽

项目 详情
文件 deno.json:67-69
问题 dev/build/preview 均使用 -A(全权限),含不必要的 --allow-run
修复 改为 --allow-read --allow-write --allow-net --allow-env(build 额外添加 --allow-ffi --allow-sys
状态 ✅ 已修复

A-8. H-16: 循环依赖 adapter-vite ↔ content

项目 详情
文件 packages/adapter-vite/src/entry-renderer.ts:16
问题 adapter-vite 生成代码导入 @lessjs/content/sitemapcontent 导入 @lessjs/adapter-vite/build-context
修复 记录为已知问题,添加 H-16 KNOWN ISSUE 注释;暂不修复,需抽取共享类型包
状态 ⚠️ 已记录 TODO

三、Batch B — 功能性 HIGH 级修复 / Batch B — Functional HIGH Fixes (10 Items)

B-1. H-02: 路由路径未转义

项目 详情
文件 packages/adapter-vite/src/entry-renderer.ts:188-205
问题 app.get('${route.path}', ...) — 路径含单引号时生成代码语法错误
修复 使用 JSON.stringify(route.path) 替代字符串插值
状态 ✅ 已修复

B-2. H-03: basePath 未转义

项目 详情
文件 packages/adapter-vite/src/cli/ssg-render.ts:525-526
问题 basePath 直接插入 HTML 属性,含 "> 可注入
修复 添加 escapeAttr() 工具函数,使用 escapeAttr(basePath) 处理
状态 ✅ 已修复

B-3. H-06: HeroPing apiUrl 未使用

项目 详情
文件 packages/ui/src/less-hero-ping.ts:124
问题 声明了 apiUrl 属性但 fetch 硬编码使用外部 URL
修复 `const url = this.apiUrl
状态 ✅ 已修复

B-4. H-07: HeroPing 无 AbortController

项目 详情
文件 packages/ui/src/less-hero-ping.ts:101-117
问题 connectedCallback 每次触发都 fetch,SPA 导航叠加请求
修复 添加 _abortControllerdisconnectedCallback 中 abort;_fetch 开头取消前序请求
状态 ✅ 已修复

B-5. H-08: replaceState 误标记为 push

项目 详情
文件 packages/core/src/navigation.ts:34,51-59
问题 replaceStatepushState 设相同布尔标志,导航类型判断错误
修复 改为三态:`let _lastNavType: 'push'
状态 ✅ 已修复

B-6. H-09: navigate() 缺 history 守卫

项目 详情
文件 packages/core/src/navigation.ts:136
问题 SSR 环境中 history 不存在直接报错
修复 添加 if (typeof globalThis.history !== 'undefined') 守卫
状态 ✅ 已修复

B-7. H-10: performance.now() 无 SSR 守卫

项目 详情
文件 packages/core/src/render-dsd.ts:114
问题 部分 SSR 环境无 performance 对象
修复 typeof performance !== 'undefined' ? performance.now() : 0
状态 ✅ 已修复

B-8. H-11: matchRoute 正则无缓存

项目 详情
文件 packages/core/src/navigation.ts:37,240-250
问题 路由模式每次调用重新编译正则,性能浪费
修复 添加 _routeRegexCache = new Map<string, RegExp>() 缓存编译后正则
状态 ✅ 已修复

B-9. H-15: 硬编码第三方 API URL

项目 详情
文件 www/app/islands/api-consumer.ts:222 + packages/ui/src/less-hero-ping.ts:124
问题 非项目控制域名,可下线/被攻破
修复 api-consumer: `this.apiUrl
状态 ✅ 已修复

B-10. C-08: extractMeta ReDoS 正则

项目 详情
文件 packages/content/src/nav/scanner.ts:24-25
问题 原始 /\{[\s\S]*?\}/ 嵌套 { 输入致指数级回溯
修复 改为受限模式 /\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}/,最多允许 1 层嵌套
状态 ✅ 已修复

四、修复统计 / Fix Statistics

类别 总计 已修复 已记录 TODO 未处理
SSG 构建管线 / SSG Build Pipeline 2 2 0 0
Batch A(安全 HIGH)/ Security HIGH 8 7 1 0
Batch B(功能 HIGH)/ Functional HIGH 10 10 0 0
合计 / Total 20 19 1 0

修改文件清单 / Modified Files

# 文件路径 涉及修复项
1 packages/adapter-vite/src/cli/build-ssg.ts BUG-1 (DsdLitElement stub)
2 packages/adapter-vite/src/workspace-alias.ts BUG-2 (comment parser), A-3 (platform guard)
3 packages/adapter-vite/src/index.ts A-1 (LessError re-throw)
4 www/vite.config.ts A-2 (SRI attributes)
5 packages/create/cli.ts A-4 (adapterVite key), A-5 (name validation)
6 deno.json A-6 (npx version pin), A-7 (least-privilege permissions)
7 packages/adapter-vite/src/entry-renderer.ts B-1 (JSON.stringify route path), A-8 (H-16 TODO)
8 packages/adapter-vite/src/cli/ssg-render.ts B-2 (escapeAttr basePath)
9 packages/ui/src/less-hero-ping.ts B-3 (apiUrl fallback), B-4 (AbortController)
10 packages/core/src/navigation.ts B-5 (three-state nav type), B-6 (history guard), B-8 (regex cache)
11 packages/core/src/render-dsd.ts B-7 (performance SSR guard)
12 www/app/islands/api-consumer.ts B-9 (apiUrl fallback)
13 packages/content/src/nav/scanner.ts B-10 (ReDoS regex)

五、遗留事项 / Outstanding Items

# 项目 说明
1 H-16 循环依赖 已添加 KNOWN ISSUE 注释。根治方案:抽取 @lessjs/build-types 共享类型包,或将 sitemap 生成移入 adapter-vite。优先级:中
2 审核报告中 CRITICAL 级问题 C-01~C-07(XSS/原型污染)尚未在本轮修复。建议下一轮优先处理
3 MEDIUM 级问题 M-01~M-42 共 42 项优化建议,按需排期

六、验证 / Verification

所有修复完成后运行 deno task typecheck 通过,确认无类型错误。

SSG 构建管线修复后,generateWorkspaceAliases() 正确生成 35 个工作区别名,optionalPackageStubsPlugin() 正确提供 DsdLitElement / WithDsdHydration 空导出,SSG 三阶段构建(client build → SSR bundle → static HTML generation)可正常完成。


报告生成时间 / Report generated: 2026-05-16T00:34+08:00

v0.14.7

15 May 11:55

Choose a tag to compare

CHANGELOG v0.14.7 — Security Hardening Patch

Date: 2026-05-15
Scope: Fix all 10 CRITICAL audit issues (XSS, prototype pollution, ReDoS, stale versions)
Version: 0.14.6 → 0.14.7
Commit: 38f1d99 on main / dev


TL;DR

v0.14.7 fixes all 10 CRITICAL security issues identified in the codebase audit, applying defense-in-depth measures against XSS, prototype pollution, and ReDoS attacks.


Fixed Issues

C-01: Stored XSS via Markdown (HIGH)

  • File: packages/content/src/blog/markdown.ts
  • Problem: marked() renders raw HTML by default; malicious markdown could inject <script>, <iframe>, etc.
  • Fix: Added sanitizeHtml() function that strips dangerous tags (<script>, <iframe>, <object>, <embed>, <form>), removes on* event handler attributes, and neutralizes javascript: URLs in href/src/action.
  • Status: ✅ Done

C-02: on* Event Handler in headExtras (MEDIUM)

  • File: packages/core/src/html-escape.ts
  • Problem: headExtras string could contain on* event handlers (e.g., onload="alert(1)") — a potential XSS vector.
  • Fix: Added detection regex /\s+on\w+\s*=/i with runtime warning when headExtras contains event handler attributes. Warning-only (headExtras is developer-controlled by design).
  • Status: ✅ Done

C-03: Prototype Pollution via DANGEROUS_KEYS (HIGH)

  • File: packages/core/src/island.ts
  • Problem: DANGEROUS_KEYS only had 3 entries (__proto__, constructor, prototype). Attackers could overwrite hasOwnProperty, toString, valueOf, etc. to achieve prototype pollution.
  • Fix: Extended DANGEROUS_KEYS from 3 → 13 entries, covering all Object.prototype methods:
    __proto__, constructor, prototype, __defineGetter__, __defineSetter__, __lookupGetter__, __lookupSetter__, hasOwnProperty, isPrototypeOf, propertyIsEnumerable, toString, toLocaleString, valueOf.
  • Status: ✅ Done

C-04: Prototype Pollution in SSR render-dsd.ts (HIGH)

  • File: packages/core/src/render-dsd.ts
  • Problem: renderDSD() assigns props to component instances without filtering dangerous keys — SSR props could pollute prototype.
  • Fix: Imported shared DANGEROUS_KEYS from island.ts, added filtering in the props assignment loop with warning log.
  • Status: ✅ Done

C-05: unsafeHTML in blog/decisions routes (MEDIUM)

  • Problem: Routes using unsafeHTML to render markdown content could be exploited if upstream sanitization was missing.
  • Fix: Covered by C-01 upstream sanitization — all markdown output now passes through sanitizeHtml().
  • Status: ✅ Covered (no separate code change needed)

C-06: DOM-based XSS in less-term.ts (HIGH)

  • File: www/app/islands/less-term.ts
  • Problem: _addLine() used raw innerHTML assignment without sanitization. API responses or local commands could inject arbitrary HTML.
  • Fix:
    • Added _sanitizeTermHtml() whitelist method: only allows <span> elements with style/class attributes.
    • Added _escapeHtml() static method for safe text insertion.
    • Escaped user cmd in display: LessTermDemo._escapeHtml(cmd).
  • Status: ✅ Done

C-07: Reflected XSS in API term routes (HIGH)

  • Files: www/app/routes/api/term.ts, functions/api/term.ts
  • Problem: Unrecognized commands were reflected back in HTML without escaping: command not found: ${cmd} — attacker could inject HTML via crafted commands.
  • Fix: Added escapeHtml() helper to both API files, applied to reflected cmd in the default case.
  • Status: ✅ Done

C-08: ReDoS in scanner.ts (MEDIUM)

  • File: packages/content/src/nav/scanner.ts
  • Problem: Regex (\{[\s\S]*?\}) used to extract meta object from source code was vulnerable to catastrophic backtracking on deeply nested braces.
  • Fix: Replaced with constrained regex (\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}) that only matches single-level nesting, preventing ReDoS.
  • Status: ✅ Done

C-10: Stale Version References (LOW)

  • Files: functions/api/term.ts, www/app/routes/api/term.ts, README.md, README.en.md
  • Problem: Multiple files still referenced old versions (v0.13.0, v0.14.2) instead of current v0.14.7.
  • Fix: Updated version references in API outputs, neofetch displays, and README files.
  • Status: ⚠️ Partially done — see Known Gaps below.

Version Bump

All 10 packages bumped from 0.14.60.14.7:

Package Version
@lessjs/adapter-lit 0.14.7
@lessjs/adapter-vite 0.14.7
@lessjs/app 0.14.7
@lessjs/content 0.14.7
@lessjs/core 0.14.7
@lessjs/create 0.14.7
@lessjs/i18n 0.14.7
@lessjs/rpc 0.14.7
@lessjs/signals 0.14.7
@lessjs/ui 0.14.7

Verification

Check Result
deno task typecheck ✅ Clean
deno task test ✅ 481 passed / 0 failed
Pre-commit hooks (fmt + lint + check) ✅ All passed
Git push to dev ✅ Synced with main

⚠️ Known Gaps (Honest Assessment)

The following stale version references were not updated as part of C-10 and still show v0.14.2:

File Line Current Value Should Be
www/app/routes/api/term.ts 91 v0.14.2 — standards & safety patch v0.14.7 — security hardening patch
www/app/routes/index/index.ts 240 v0.14.2 (homepage stat) v0.14.7
www/app/routes/index/index.ts 401 v0.14.2 (homepage stat) v0.14.7
www/app/routes/reference/core.ts 4 v0.14.2 API surface v0.14.7 API surface
www/app/routes/reference/core.ts 68 v0.14.2. v0.14.7.

These are user-visible strings (homepage stats, API reference header, terminal version command) that incorrectly report the current version. They should be fixed in a follow-up commit.

Historical references (blog posts, roadmap entries, code comments like // v0.14.3:) are intentionally preserved and should NOT be updated — they document when features were introduced, not the current version.


Files Changed (25 files)

README.en.md                                       |   4 +-
README.md                                          |  22 +-
deliverables/review260515/20260515-full-review.md  | 403 +++++++++++++
deliverables/review260515/CHANGELOG-v0.14.2-to-v0.14.6.md | 262 +++++++++
deno.lock                                          |  24 +-
dist-test-ssg-render/index.txt                    |   1 +
functions/api/term.ts                              |  14 +-
packages/adapter-lit/deno.json                     |   2 +-
packages/adapter-vite/deno.json                    |   2 +-
packages/app/deno.json                             |   2 +-
packages/content/deno.json                         |   2 +-
packages/content/src/blog/markdown.ts              |  26 +-
packages/content/src/nav/scanner.ts                |   5 +-
packages/core/deno.json                            |   2 +-
packages/core/src/html-escape.ts                   |  10 +
packages/core/src/index.ts                         |   2 +-
packages/core/src/island.ts                        |  24 +-
packages/core/src/render-dsd.ts                    |   9 +
packages/create/deno.json                          |   2 +-
packages/i18n/deno.json                            |   2 +-
packages/rpc/deno.json                             |   2 +-
packages/signals/deno.json                         |   2 +-
packages/ui/deno.json                              |   2 +-
www/app/islands/less-term.ts                       |  38 +-
www/app/routes/api/term.ts                         |  12 +-

Integrity Score

Dimension Score Notes
CRITICAL fixes applied 9/10 C-05 covered by C-01, no separate code needed
Version consistency 7/10 5 user-visible stale v0.14.2 refs remain
Test pass rate 10/10 481/481 passed
Type safety 10/10 typecheck clean
Push to dev 10/10 main == dev at 38f1d99

Overall: B+ — Core security fixes are solid and complete, but version consistency in user-facing pages has gaps that should be addressed.

v0.14.6

15 May 11:06

Choose a tag to compare

LessJS v0.14.2 → v0.14.6 变更总结 / Changelog Summary

时间范围 / Date Range: 2026-05-15 (all four versions released on the same day)
基线 / Baseline: v0.14.2 (c6a0e80)
终点 / Endpoint: v0.14.6 (45696f5)
涉及包 / Packages: 全部 10 个工作区包 / All 10 workspace packages


一句话总结 / TL;DR

EN: From v0.14.2 to v0.14.6, LessJS received four rounds of code review remediation — addressing prototype pollution, signal correctness, SSR/SSG robustness, CSP nonce validation, History API concurrency, i18n double-rendering, visible-strategy memory leaks, and CLI reliability — totaling 28 blocker/correctness fixes and 8 minor improvements across all packages.

ZH: 从 v0.14.2 到 v0.14.6,LessJS 经过四轮代码评审修复——解决了原型污染、信号正确性、SSR/SSG 健壮性、CSP Nonce 校验、History API 并发、i18n 重复渲染、可见策略内存泄漏和 CLI 可靠性问题——共计 28 项阻断/正确性修复和 8 项小改进,覆盖全部包。


版本总览 / Version Overview

版本 / Version 核心主题 / Core Theme 提交数 / Commits 修复项 / Fixes
v0.14.3 安全修复 + 代码评审整改 2 3 安全 + 10 正确性 + 4 小改进
v0.14.4 扫描后修复 + onNavigate 并发 1 (+2 辅助) 1 阻断 + 2 正确性 + 3 小改进
v0.14.5 综合代码评审整改 1 (+2 辅助) 5 阻断 + 5 正确性 + 2 小改进
v0.14.6 第二轮代码评审整改 + CI 修复 5 3 阻断 + 7 正确性 + 3 小改进

v0.14.3 — 安全修复与代码评审整改 / Security Fixes & Code Review Remediation

🔴 安全修复 / Security Fixes (3)

ID 问题 / Issue 文件 / File 说明 / Description
B-1 lessBind() 原型污染 / Prototype pollution in lessBind() packages/core/src/context.ts 过滤 __proto__/constructor/prototype 键,阻止通过属性绑定注入原型链 / Filter __proto__/constructor/prototype keys to prevent prototype chain injection via property binding
B-2 connectedCallback 递归 / connectedCallback recursion packages/core/src/context.ts 添加 __lessBindDone 幂等守卫,防止 lessBind() 在同一元素上重复触发 connectedCallback / Add __lessBindDone idempotency guard to prevent lessBind() from re-triggering connectedCallback on the same element
B-3 uninstallLitAdapter() 类型不安全转换 / Type-unsafe cast in uninstallLitAdapter() packages/adapter-lit/src/ssr.ts 直接传 undefined 而非强制类型转换 / Pass undefined directly instead of unsafe type cast

🟡 正确性与性能修复 / Correctness & Performance Fixes (10)

ID 问题 / Issue 文件 / File 说明 / Description
S-1 Signal _epoch 溢出 / Signal _epoch overflow packages/signals/src/polyfill.ts 添加 epoch 溢出保护,防止长运行时计数器回绕 / Add overflow protection to prevent counter wrap-around in long-running processes
S-2 renderDSD() 双重序列化 / Dual serialization in renderDSD() packages/core/src/render-dsd.ts 文档说明有意为之的双重序列化策略(渲染 + 注入)/ Document intentional dual serialization strategy (render + inject)
S-3 headExtras HTML 注释不平衡 / HTML comment balance in headExtras packages/core/src/html-escape.ts 添加注释闭合验证,防止注入未闭合注释破坏 HTML 结构 / Add comment closure validation to prevent unclosed comment injection that breaks HTML structure
S-4 createVisibleStrategy MutationObserver 无超时 / No timeout for MutationObserver packages/core/src/island.ts 添加 30 秒超时自动断开,防止不可见岛永占 Observer 资源 / Add 30s auto-disconnect timeout to prevent invisible islands from permanently occupying Observer resources
S-5 Speculation rules 过于宽泛 / Overly broad speculation rules packages/adapter-vite/src/index.ts 顶层页面不再应用过于宽泛的预加载规则 / Top-level pages no longer apply overly broad prefetch rules
S-6 SSG 输出包含错误堆栈 / Error stack in production SSG output packages/adapter-vite/src/ssg-postprocess.ts 生产 SSG 输出中抑制错误堆栈信息 / Suppress error stack traces in production SSG output
S-7 hydratedComponents 计算错误 / Incorrect hydratedComponents calculation packages/adapter-vite/src/index.ts dsd-interactive layer 过滤水合组件计数 / Filter hydrated component count by dsd-interactive layer
S-8 简单哈希冲突 / simpleHash collisions packages/adapter-vite/src/island-manifest.ts 用 64 位 FNV-1a 替换 32 位简单哈希,大幅降低碰撞概率 / Replace 32-bit simple hash with 64-bit FNV-1a, drastically reducing collision probability
S-9 品牌类型运行时检查 / Branded type runtime checks packages/core/src/types.ts 文档说明品牌类型仅为编译时检查,运行时无额外校验 / Document that branded types are compile-time only, no runtime validation
S-10 onNavigate History API 回退导航类型 / History API fallback navigation type packages/core/src/navigation.ts 修复回退导航的类型判断逻辑 / Fix navigation type detection in History API fallback path

💭 小改进 / Minor Improvements (4)

ID 问题 / Issue 说明 / Description
N-1 decodeURIComponent 错误处理 / Error handling 添加 try-catch 包裹 decodeURIComponent,防止畸形 URI 组件导致崩溃 / Wrap decodeURIComponent in try-catch to prevent crashes from malformed URI components
N-2 RpcController 方法可选化 / Optional methods requestUpdate/addController 改为可选,减少强制契约 / Make requestUpdate/addController optional to reduce mandatory contract
N-3 parseQuery 简化 / Simplify parseQuery key in query 替代冗长查找 / Replace verbose lookup with key in query
N-4 Windows 路径归一化 / Windows path normalization 文档说明 route-scanner 中的 Windows 路径归一化行为 / Document Windows path normalization behavior in route-scanner

📦 v0.14.3 补充修复 / Post-scan Fixes (same version, second commit)

ID 类型 / Type 说明 / Description
B-4 🔴 阻断 / Blocker onNavigate 多订阅者 history.pushState 补丁互相覆盖→替换为共享引用计数模式;延迟捕获 history 以兼容 SSR / Multi-subscriber history.pushState patching corrupts each other's state → replaced with shared reference counter; lazy history capture for SSR compat
S-11 🟡 正确性 / Correctness 统一 stableHash 为 64 位 FNV-1a,消除 32 位与 64 位实现并存 / Unify stableHash to 64-bit FNV-1a, eliminate coexisting 32-bit and 64-bit implementations
S-13 🟡 正确性 / Correctness visible 策略 querySelectorquerySelectorAll,多实例页面每个元素独立 IntersectionObserver / visible strategy querySelectorquerySelectorAll, individual IntersectionObserver per element
N-6 💭 小改进 / Minor renderDSD() 添加 nestingDepth 参数,maxNestingDepth 构建报告不再永远为 0 / Add nestingDepth param to renderDSD(), maxNestingDepth build report no longer always 0
N-7 💭 小改进 / Minor insertAfterHead 正则支持多行 <head> 标签 / Regex supports multi-line <head> tags
N-8 💭 小改进 / Minor Polyfill 哨兵符号 UNSET/COMPUTING/ERRORED 移至模块作用域,多次调用 _createPolyfill 不再创建不同 Symbol / Move sentinel symbols to module scope, multiple _createPolyfill calls no longer create different Symbols
🔧 辅助 / Ancillary 修复 createVisibleStrategy 计时器泄漏(注册成功后 clearTimeout);导出 _clearAllVisibilityTimeouts() 供测试清理 / Fix timer leak; export _clearAllVisibilityTimeouts() for test cleanup; remove unused RenderAdapter import

v0.14.4 — 扫描后修复 / Post-scan Remediation

注:v0.14.4 的修复内容已合并到上表 v0.14.3 补充修复部分展示,因其核心改动(B-4 onNavigate、S-11 stableHash 统一、S-13 querySelectorAll、N-6/N-7/N-8)在同一轮扫描中完成,版本号在 CI 辅助修复后提升。

Note: v0.14.4's fixes are shown above under v0.14.3 post-scan fixes, since the core changes (B-4, S-11, S-13, N-6/N-7/N-8) were completed in the same scan round; the version bump occurred after CI auxiliary fixes.

辅助修复 / Auxiliary fixes in v0.14.4:

  • 修复多行联合类型注解格式 / Fix multiline union type annotation formatting
  • 将裸 process 替换为 globalThis 括号访问以兼容 Deno / Replace bare process with globalThis bracket access for Deno compat

v0.14.5 — 综合代码评审整改 / Comprehensive Code Review Remediation

🔴 阻断修复 / Blocker Fixes (5)

ID 严重度 / Severity 问题 / Issue 说明 / Description
B-1 高 / High effect() 信号变更在 pending 窗口丢失 / Signal changes lost during pending window 布尔 pending 标志→计数器 + while 循环排空,微任务中不再丢失变更 / Boolean pending flag → counter-based pendingCount + while loop drain; no more lost changes in microtask
B-2 中 / Medium CSP Nonce 缺少字符校验 / CSP nonce missing character validation 添加 base64 格式校验 (NONCE_RE),非法 nonce 发出警告并回退为 undefined / Add base64 format validation; invalid nonces trigger warning and fall back to undefined
B-3 中 / Medium less-dialog._syncInert() Shadow DOM 兼容性 + 状态恢复 / Shadow DOM compat + state restoration 三重修复:WeakMap 保存原始 inert 状态、ShadowRoot 父节点回退、disconnectedCallback 清理 / Triple fix: WeakMap for original inert states, ShadowRoot parentNode fallback, disconnectedCallback cleanup
B-4 低 / Low renderNestedCustomElements()renderDSD 失败时静默退化 / Silent degradation on renderDSD failure dsdCeElementundefined 时添加诊断警告日志 / Add diagnostic warning log when dsdCeElement is undefined
B-5 低 / Low createVisibleStrategy IntersectionObserver 内存泄漏 / IntersectionObserver memory leak 目标元素被移除后立即断开 Observer,不再等待 30 秒超时 / Disconnect Observers immediately when target elements removed from DOM, no longer wait for 30s timeout

🟡 正确性与一致性修复 / Correctness & Consistency Fixes (5)

ID 问题 / Issue 说明 / Description
S-1 batch() 标记 @deprecated 文档说明当前为 no-op 占位符,等 TC39 Signal 规范确定原生批处理后再实现 / Document as no-op placeholder; implement after TC39 Signal spec finalizes native batching
S-2 islandEffect() 轮询减少 MutationObserver 重连 追踪 lastParent,仅当父节点实际变化时重连 / Track lastParent; only reconnect MutationObserver when parent actually changes
S-5 renderDSD() 适配器引用重复消除 getAdapter() 调用从 3 次减为 1 次(移至函数顶部)/ Reduce getAdapter() calls from 3 to 1 (move to function top)
S-6 _ensureHistoryOriginals SSR/SSG 守卫 添加 typeof globalThis.history === 'undefined' 守卫,防止 SSR 环境中 ReferenceError / Add guard to prevent ReferenceError in SSR/SSG environments
S-7 正则命名捕获组特殊字符转义 / Regex named capture group special character escape 回调函数转义参数名中的特殊正则字符,防止 SyntaxError 和 ReDoS / Escape special regex characters in parameter names via callback to prevent SyntaxError and ReDoS

💭 小改进 / Minor Improvements (2)

ID 问题 / Issue 说明 / Description
N-1 parseAttrsToProps JSON.parse 开销 / JSON.parse overhead 添加快速结构检查(匹配开闭括号),避免对...
Read more

v0.14.2

14 May 11:53
910392e

Choose a tag to compare

v0.14.2 — Standards & Safety Patch / 标准与安全补丁

1 commit (c6a0e80), 27 files changed, +1111 / −152 lines
Based on ADR 0024: Standards-first Web Components Renderer Roadmap

1 个提交,27 个文件变更,+1111 / −152 行
基于 ADR 0024:Web 标准优先的 Web Components 渲染器路线图

DSD Standards Correctness / DSD 标准合规修正

# Change / 变更 File / 文件 Detail / 详情
1 新增 shadowrootclonable 属性 core/src/types.ts, core/src/render-dsd.ts DsdOptions.clonable?: boolean — 允许 cloneNode()/importNode() 包含 Shadow Root。渲染输出添加 shadowrootclonable 布尔内容属性。
2 shadowrootcustomelementregistry 修正为布尔属性 core/src/types.ts, core/src/render-dsd.ts HTML Living Standard 规定此为布尔内容属性(无值)。类型从 string 改为 boolean | string;渲染时无论传 true 还是字符串均输出无值布尔属性(兼容 v0.x 字符串传参,但不再序列化值)。
3 移除 escapeAttr 未用导入 core/src/render-dsd.ts customElementRegistry 不再需要转义属性值,删除了 escapeAttr 的导入。

涉及的 WHATWG 属性对照表 / WHATWG Attribute Reference:

Template Attribute v0.14.1 v0.14.2
shadowrootmode="open"
shadowrootdelegatesfocus
shadowrootclonable ❌ 缺失 ✅ 新增
shadowrootserializable
shadowrootslotassignment="manual"
shadowrootcustomelementregistry ❌ 输出 ="值" ✅ 布尔属性(无值)

Head Injection Safety / Head 注入安全加固

# Change / 变更 File / 文件 Detail / 详情
1 headExtras 拒绝 <script> 标签 adapter-vite/src/index.ts 新增 assertNoScriptTags() 校验函数。headExtras 中出现 <script> 标签直接抛 LessError(UNSAFE_HEAD_INJECTION, 400)。脚本必须通过结构化 inject.scripts 注入,由框架校验和转义 URL。
2 inject.headFragments 拒绝 <script> 标签 adapter-vite/src/index.ts 从 v0.14.1 的 log.warn 升级为 assertNoScriptTags() → 抛异常。不允许任何 head fragment 包含 <script>
3 结构化 inject.scripts 仍可正常使用 adapter-vite/src/index.ts 通过 inject.scripts: [{ src: '/x.js', defer: true }] 方式注入脚本不受影响,框架会验证和标记脚本 URL 为可信。

Dynamic SSG Route Safety / 动态 SSG 路由安全

# Change / 变更 File / 文件 Detail / 详情
1 resolveDynamicRoutePath() 新函数 adapter-vite/src/cli/ssg-render.ts 动态路由参数解析为单一 URL 路径段 + encodeURIComponent() 编码。拒绝路径穿越值(../\、控制字符)。缺失参数直接抛错。
2 不安全路由跳过 adapter-vite/src/cli/ssg-render.ts 渲染循环中 catch 错误,log.warn + continue 跳过不安全的动态路由,不再静默生成错误路径。
3 i18n 动态路由同样加固 adapter-vite/src/cli/ssg-render.ts i18n locale 展开的动态路由也使用 resolveDynamicRoutePath()

被拒绝的危险参数值示例 / Rejected unsafe param values:

Value Reason / 原因
.. Path traversal / 路径穿越
../evil Contains .. / 包含路径穿越
a/b Contains / / 包含斜杠
含控制字符 hasControlCharacter() 检测 ≤ 0x1F 和 0x7F

Test Coverage Expansion / 测试覆盖扩展

Test File / 测试文件 New Tests / 新增测试
core/__tests__/render-dsd.test.ts shadowrootclonable 属性测试;customElementRegistry 布尔属性测试(true 和旧字符串兼容);省略属性回归测试。
adapter-vite/__tests__/index-plugin.test.ts headExtras 拒绝 <script> 测试;inject.headFragments 拒绝 <script> 测试;结构化 inject.scripts 允许通过测试。
adapter-vite/__tests__/ssg-render.test.ts resolveDynamicRoutePath 安全编码测试;路径穿越拒绝测试;缺失参数拒绝测试。
adapter-vite/__tests__/ssg-smoke.test.ts 导入真实 SSR bundle 并调用 renderRoute('/roadmap');验证 roadmap、i18n、content、ADR、UI island、PWA、DSD 输出;验证 ADR 0024 内容管线渲染。

Roadmap & ADR 0024 / 路线图与 ADR 0024

v0.14.2 同步更新了官网 Roadmap 页面和新增 ADR 0024 博文,明确了 Web Standards-first DSD/Web Components 应用框架 的战略定位。

ADR 0024 版本路线 / ADR 0024 Version Roadmap:

Version Focus / 重点
v0.14.2 Standards & Safety Patch(本次发布)
v0.15 Renderer Kernel — 定义公共 WC 渲染协议
v0.16 WC Package Protocol — 扩展 PackageIslandMeta 为组件包清单
v0.17 Ecosystem Entry — npm 可达性、文档搜索、benchmark、交互脚手架
v0.18–v1.0 API Freeze — 公共 API 快照测试与迁移策略

ADR 0024 明确拒绝 / ADR 0024 Explicit Rejections:

  • ❌ 不引入 webpack — ESM-first 方向不变
  • ❌ 不采用 OpenWC 工具链 — Deno-first 测试栈 + Playwright 已够用
  • ❌ 不在渲染协议稳定前承诺通用全栈 — DSD/WC 渲染内核优先
  • ❌ 不在清单/协议就绪前做集中式 WC 注册中心 — 本地清单先行

Package Version Bump / 包版本升级

All 10 packages: 0.14.10.14.2

Package Version
@lessjs/rpc 0.14.2
@lessjs/signals 0.14.2
@lessjs/core 0.14.2
@lessjs/adapter-vite 0.14.2
@lessjs/content 0.14.2
@lessjs/i18n 0.14.2
@lessjs/adapter-lit 0.14.2
@lessjs/ui 0.14.2
@lessjs/app 0.14.2
@lessjs/create 0.14.2

Appendix: v0.14.0 Summary / 附录:v0.14.0 摘要

33 commits since v0.13 (a5ff77ea04a7e5)
For full details see CHANGELOG-v0.14.0.md in the repository root.

Architecture Highlights / 架构亮点

  • ESM-native SSG pipeline: Phase 3 纯 ESM 运行,不依赖 Vite。SSR bundle 自带 importmap.json
  • Phase reordering: Phase 1→3→2→Inject,SSG 不再等待 client bundle。
  • Shared ssg-render.ts: 零 Vite 依赖的共享 SSG 渲染模块。
  • Standalone SSG CLI: cli/ssg.ts 可脱离 Vite 单独运行。
  • URLPattern: extractParams() 用 WHATWG URLPattern 替代手写路由解析。
  • Optional Phase 2: 零 island 项目跳过 client 打包。

Website v5 / 官网 v5

  • 交互终端 island、代码对比、性能基准、架构图、Bundle 对比、快速开始 CTA
  • 品牌色 #4752c4 全站统一
  • 移动端适配 760px / 480px
  • Cloudflare Pages /api/term
  • 25 篇 ADR 迁入 blog 管线

Code Quality / 代码质量

  • 全部 10 包统一版本 0.14.0(之前碎片化在 0.1.10.13.0
  • 清理死代码(constants.tsstrategy-recommender.ts
  • @lessjs/signals 命名统一(从 @lessjs/signal
  • validateSafeUrl 安全加固(vbscript: / file: 协议检测)
  • 移除 --allow-dirty,新增 publish:dry-run
  • 补充 app/LICENSE
  • 新增 SSG 测试(ssg-render.test.ts 7 用例 + ssg-cli.test.ts
  • CI lint 归零
  • 移除 package.json(纯 Deno workspace)

v0.14.1

14 May 11:56

Choose a tag to compare

v0.14.1 — Release Hardening / 发布硬化版本

6 commits since v0.14.0 (a04a7e5b062d20), 75 files changed

自 v0.14.0 以来 6 个提交,75 个文件变更

Bug Fixes / Bug 修复

# Change / 变更 Detail / 详情
1 Blank page on first load / 首次加载白屏 inject.scripts(theme-init.js)在 inject.headFragments(anti-flash cloak)之前输出。当 theme-init.js 执行时移除 cloak,<style id="less-anti-flash"> 尚未进入 DOM → 页面永久 visibility: hidden。修复:交换输出顺序,headFragments 先于 scripts。
2 Speculation Rules parsing error / Speculation Rules 解析错误 首页规则同时包含 where: {}(document matcher)和 source: 'list' + urls: ['/'](list matcher),违反 Speculation Rules API 规范。
3 prism-html.min.js 404 Prism 没有 prism-html 组件,HTML 语法高亮应使用 prism-markup
4 GoatCounter URL 协议相对 URL(//gc.zgo.at/)改为完整 HTTPS。
5 Service Worker 跨域拦截 SW 现在仅拦截同源请求,跨域 CDN/分析请求直接放行。networkFirst 返回 503 而非抛异常。
6 iOS 暗色模式黑屏 灰度色标值(--gray-0--gray-12)内联到 generateRootColorCSS(),不再依赖 OpenProps CDN 延迟加载。

Build & CI / 构建与 CI

# Change / 变更 Detail / 详情
1 deno task publish 洁净检查 发布前检查 git status --porcelain,拒绝脏工作区。不再使用 --allow-dirty
2 发布顺序固定 全部 10 个包按正确依赖顺序发布:rpc → signals → core → adapter-vite → content → i18n → adapter-lit → ui → app → create。
3 CI lint/test 扩大范围 现在 deno task fmt:checkdeno task lint 覆盖所有文件(之前仅限 packages/)。publish-manual 依赖 test workflow。
4 publish:dry-run task 新增 发布前预检命令。
5 allowHeadExtrasScripts 标志 Phase3MetabuildPlugin() 新增标志,控制结构化注入 API 是否允许内联脚本。

Signals (@lessjs/signals)

# Change / 变更 Detail / 详情
1 包名标准化 @lessjs/signal@lessjs/signals,与目录名一致。更新 deno.jsonREADME.md、CI publish tasks、publish.yml
2 ReadonlySignal 类型增强 subscribe() 回调添加泛型约束,Effect 注册现在正确拒绝非函数参数。
3 测试格式化 全部 7 个测试文件重新格式化。

Build System / 构建系统

# Change / 变更 Detail / 详情
1 动态版本解析 build-ssg.ts 从 workspace 包的 deno.json 读取版本号生成 importmap,不再硬编码 0.13.0。回退值为 0.14.1
2 readWorkspacePackageVersion() 新工具函数,从兄弟包的 deno.json 解析版本号用于 importmap.json 元数据。
3 BuildSSGOptions.allowHeadExtrasScripts 新选项控制 headExtras 中是否允许脚本。
4 SSG 渲染管线 ssg-render.ts 接受 root 属性(之前仅从 ctx 派生)。控制台错误输出截断为前 3 行。

Infrastructure / 基础设施

# Change / 变更 Detail / 详情
1 CI workflows Lint workflow 解除阻塞(之前 www/ 因 Deno fmt 在 HTML tagged templates 上 panic 而跳过,现已修复)。Publish workflow 正确排序全部 10 包。
2 E2E tests Playwright 配置更新:新增主题系统测试文件、颜色对比可访问性-性能测试。视口设为 1280×720。
3 Color tokens 内联 灰度值内联避免 CDN 延迟。

Website / 官网

# Change / 变更 Detail / 详情
1 Homepage less-term island CSS 格式化(单行→多行),小样式修复。
2 404 页面 移动端适配修复。
3 Guide 页面 architecture、getting-started、RPC 页面引用更新。
4 Blog 旧博文引用的废弃包名更新。
5 Changelog 页面 路由更新,移动端布局优化。

v0.14.0

13 May 16:04

Choose a tag to compare

LessJS v0.14.0

33 commits since v0.13 (a5ff77ea04a7e5)


中文

架构

  • ESM-native SSG 管线:Phase 3 改为纯 ESM 运行,不依赖 Vite。SSR bundle 自带 importmap.json,跨 runtime 解析 bare specifier。
  • Phase 重排:构建顺序从 Phase 1 → Phase 2 → Phase 3 改为 Phase 1 → Phase 3 → Phase 2 → Inject。SSG 不再等待 client bundle。
  • 共享 ssg-render.ts:SSG 渲染管线抽成零 Vite 依赖的共享模块,build-ssg.tscli/ssg.ts 共用。
  • 独立 SSG CLIcli/ssg.ts 可脱离 Vite 单独运行 Phase 3。
  • URLPatternextractParams() 用 WHATWG URLPattern 替代手写路由参数解析。
  • Phase 2 可选:零 island 的项目跳过 client 打包。

官网 (lessjs.run)

  • 首页 v5:交互终端、代码对比、性能基准、架构图、Bundle 对比、快速开始
  • 品牌色系统:#4752c4 全站统一
  • 移动端适配:760px / 480px 断点
  • Cloudflare Pages 部署:/api/term 函数 + Hono
  • 25 篇 ADR 从旧 decisions/ 迁入 blog 管线
  • 文档全面审计:API reference 重写、guide 页面修复、Cloudflare 部署指南
  • 发布博文:v0.12.0、v0.13.0

Bug 修复

  • Prism CSS 404、counter hydration、重复渲染(DSD 模式)
  • Cloudflare Functions CORS(OPTIONS 预检)
  • Terminal island:从 light DOM 重构为 Shadow DOM + DsdLitElement
  • Functions 目录:从 src-tmp/ 移回仓库根目录

代码质量

  • 统一版本号:全部 10 个包统一为 0.14.0(之前碎片化在 0.1.10.13.0
  • 清理死代码:删除 constants.ts(空文件)、strategy-recommender.ts(零引用)
  • @lessjs/signals 命名统一:从 @lessjs/signal 改为 @lessjs/signals
  • validateSafeUrl 加固:新增 vbscript: / file: 协议检测、URL 解码归一化、畸形编码检测
  • 修复 publish 重复publish.yml 删除重复的 @lessjs/adapter-vite 条目
  • 移除 --allow-dirty:所有 publish task 不再使用 --allow-dirty
  • 新增 publish:dry-run:发布前预检
  • 补充 app/LICENSE@lessjs/app 补全 MIT License
  • 新增 SSG 测试ssg-render.test.ts(7用例) + ssg-cli.test.ts
  • 修复导航链接/guide/design-philosophy/guide/architecture
  • README 版本表更新:全部 10 包版本更新为 0.14.0
  • CI lint 净化:async lint、无用 import、process import 全部修掉
  • 移除 package.json:仓库不再包含 package.json(纯 Deno workspace)

English

Architecture

  • ESM-native SSG pipeline: Phase 3 runs as pure ESM, Vite-independent. SSR bundle ships importmap.json for cross-runtime bare specifier resolution.
  • Phase reordering: Build order changed from Phase 1 → Phase 2 → Phase 3 to Phase 1 → Phase 3 → Phase 2 → Inject. SSG no longer waits for client bundle.
  • Shared ssg-render.ts: SSG rendering pipeline extracted to a zero-Vite-dependency module shared by build-ssg.ts and standalone cli/ssg.ts.
  • Standalone SSG CLI: cli/ssg.ts runs Phase 3 independently of Vite.
  • URLPattern: extractParams() replaced hand-rolled route parsing with WHATWG URLPattern.
  • Optional Phase 2: Client bundle is skipped for projects with zero islands.

Website (lessjs.run)

  • Homepage v5: interactive terminal island, code comparison, benchmarks, architecture diagram, bundle size comparison, quick start CTA
  • Brand colour system: #4752c4 unified across the site
  • Mobile responsive: 760px / 480px breakpoints
  • Cloudflare Pages deployment: /api/term function with Hono
  • 25 ADRs migrated from decisions/ directory into blog pipeline
  • Full doc audit: API reference rewrite, guide page fixes, Cloudflare deployment guide
  • Release blog posts: v0.12.0, v0.13.0

Bug Fixes

  • Prism CSS 404, counter hydration (removed random IDs), duplicate rendering (DSD pattern)
  • Cloudflare Functions CORS (OPTIONS preflight)
  • Terminal island: restructured from light DOM to Shadow DOM with DsdLitElement
  • Functions directory: moved from src-tmp/ to repo root

Code Quality

  • Unified versioning: All 10 packages unified to 0.14.0 (was fragmented across 0.1.10.13.0).
  • Dead code removed: constants.ts (empty), strategy-recommender.ts (zero references).
  • @lessjs/signals naming: Standardised to @lessjs/signals (was @lessjs/signal).
  • validateSafeUrl hardened: Added vbscript: / file: protocol checks, URL decode normalisation, malformed encoding detection.
  • Duplicate publish fixed: publish.yml duplicate @lessjs/adapter-vite entry removed.
  • --allow-dirty removed: All publish tasks no longer use --allow-dirty.
  • publish:dry-run added: Pre-flight check for releases.
  • app/LICENSE added: @lessjs/app now includes MIT License.
  • SSG tests added: ssg-render.test.ts (7 cases) + ssg-cli.test.ts.
  • Navigation link fixed: /guide/design-philosophy/guide/architecture.
  • README version table: All 10 package versions updated to 0.14.0.
  • CI lint zeroed: All lint errors fixed (async lint, unused imports, process import).
  • package.json removed: No package.json files remain (pure Deno workspace).

v0.13.0

12 May 12:00

Choose a tag to compare

LessJS v0.13.0 Release Notes | 发布说明

Released | 发布日期: 2026-05-12
From v0.12.0 → v0.13.0 (core), v0.2.0 → v0.3.0 (adapter-vite), v0.3.2 → v0.3.3 (content)


What's New | 新功能

Architecture Cleanup | 架构清理 (ADR 0021)

Core API surface reduced from 18 to 6 exports | 核心API从18个收敛到6个

Previously, @lessjs/core exposed 18 subpath exports (./render-dsd, ./html-escape, ./adapter-registry, etc.) — most were internal implementation details forced to be public because generated code imported them. Now all imports go through @lessjs/core main entry.

过去@lessjs/core暴露了18个子路径导出,其中大部分是内部实现细节。现在所有导入都走@lessjs/core主入口。

Before: 18 exports (4 internal + 14 variants)
After:  6 exports (all public API)
  .       → main entry | 主入口
  ./errors    → LessError, SsrRenderError
  ./context   → SsrContext, extractParams, parseQuery
  ./logger    → createLogger
  ./navigation → navigate, onNavigate
  ./constants → shared configuration

Vite Knowledge Removed from Core | Core不再包含Vite知识

Virtual module IDs (VIRTUAL_BLOG_DATA_ID, RESOLVED_NAV_ID, etc.) moved from @lessjs/core/constants to @lessjs/adapter-vite/virtual-ids. Core is now a pure runtime with zero Vite awareness.

虚拟模块ID从@lessjs/core/constants迁移到@lessjs/adapter-vite/virtual-ids。Core现在是纯运行时,对Vite一无所知。

Phase Ordering Enforced at Compile Time | 构建阶段顺序编译器校验

TypeScript branded types (Phase1Token, Phase2Token, Phase3Token) ensure Phase 2 can only run after Phase 1, and Phase 3 after Phase 2. The compiler catches out-of-order calls.

TypeScript品牌类型确保Phase 2只能在Phase 1之后运行,Phase 3只能在Phase 2之后运行。编译器捕获乱序调用。

Zero Barrel Files | 零Barrel文件

Deleted content/src/nav/types.ts and content/src/sitemap/types.ts — both were pure re-exports from ../types.ts with zero consumers. Less code to maintain.

删除了两个纯重复导出文件,零消费者。减少维护量。

CI Coverage Collection | CI覆盖率收集

Added --coverage to all 8 CI test jobs. Run deno task test:coverage locally to generate lcov reports.

所有8个CI测试job添加了--coverage。本地运行deno task test:coverage生成覆盖率报告。


Breaking Changes | 不兼容变更

@lessjs/core 0.12.1 → 0.13.0

Removed Export 被移除的导出 Replacement 替代方案
@lessjs/core/render-dsd @lessjs/core (import { renderDSD })
@lessjs/core/html-escape @lessjs/core (import { escapeHtml })
@lessjs/core/adapter-registry @lessjs/core (import { registerAdapter })
packages/core/src/ssr-handler.ts @lessjs/core (import { wrapInDocument })

@lessjs/adapter-vite 0.2.0 → 0.3.0

Removed Export 被移除的导出 Reason 原因
@lessjs/adapter-vite/cli/build CLI implementation detail
@lessjs/adapter-vite/cli/build-client CLI implementation detail
@lessjs/adapter-vite/cli/build-ssg CLI implementation detail

@lessjs/content 0.3.2 → 0.3.3

  • Deleted internal barrel files (nav/types.ts, sitemap/types.ts). No public API change. | 删除了内部barrel文件,无公共API变化。

Fixed | 修复

  • virtual:less-blog-data resolution in SSG build — replaced static noop plugin with dynamic dispatcher that checks ctx.plugins.blogDataPlugin at resolve time, not at plugin construction time
  • RenderAdapter type export from adapter-registry.ts
  • Test imports referencing deleted render-dsd.ts exports
  • build-ssg.ts self-import via bare specifier → relative path
  • ssg-postprocess.test.ts assertions matching heuristic prerender logic

Changelog | 完整提交历史

57f280f ADR 0019: @deno/vite-plugin + virtual:less-page-data + CI fix
f015530 fix: break adapter-vite ↔ content/i18n circular dependency
64e97b9 audit cleanup: core exports, nav types, app tests, vite/index import
c58388e delete ssr-handler.ts + remove render-dsd.ts deprecated re-exports
208ddb8 deep-review fixes: lint cleanup + islandEffect interval 5s→30s
9a77a23 deep-review fixes: explicit imports + nav constants migration
2dc1902 ADR 0021: core API surface convergence to 6 exports
927f09e ADR 0021: coverage, zero-barrel, core-vite separation, phase tokens
a94f171 bump versions: core 0.13.0, adapter-vite 0.3.0, content 0.3.3
9cce02e fix: virtual:less-blog-data resolution + test imports

Metrics | 指标变化

Metric v0.12.0 v0.13.0 Delta
Core exports 18 6 -67%
Barrel files 2 0 -100%
CI coverage none all jobs +100%
Phase ordering runtime compile-time
Tests passing ~440 ~452 +12
Global bridges 0 0
Temp files 0 0
Architecture rating B+ A

v0.12.0

12 May 05:31

Choose a tag to compare

LessJS v0.12.0 Release Notes

Release Date: 2026-05-12
Comparing: v0.11.0 → v0.12.0
Tag: v0.12.0


中文

概览

从 v0.11.0 到 v0.12.0,LessJS 经历了一次深入的团队性质量审查,并基于审查结果完成了一系列工程质量加固和架构债务清理。本次发布没有新增功能,而是专注于让现有代码更健壮、更可维护、更安全。

版本变更

旧版本 新版本 变更类型
@lessjs/core 0.11.0 0.12.0 ⚠️ Breaking
@lessjs/adapter-vite 0.1.0 0.2.0 ⚠️ Breaking
@lessjs/app 0.3.0 0.3.1 🩹 Patch
@lessjs/content 0.3.1 0.3.2 🩹 Patch
@lessjs/signals 0.6.2 0.6.3 🩹 Patch
@lessjs/ui 0.7.0 0.7.1 🩹 Patch
@lessjs/i18n 0.1.0 0.1.1 🩹 Patch
@lessjs/adapter-lit 0.8.0 0.8.0
@lessjs/rpc 0.6.1 0.6.1
@lessjs/create 0.8.1 0.8.1

重大变更(Breaking Changes)

⚠️ @lessjs/core 0.12.0

  • parseQuery() 返回类型变更Record<string, string>Record<string, string | string[]>。重复的查询参数键现在会合并为数组(?tag=a&tag=b{ tag: ['a', 'b'] }),而非取最后一个值。
  • ssr-handler 子路径导出简化index.ts 直接导入 html-escape.ts 替代 ssr-handler.tsssr-handler.ts 文件保留为兼容性 re-export,无需改动导入端。

⚠️ @lessjs/adapter-vite 0.2.0

  • LessBuildContext 字段分组:原来扁平的 30+ 字段按构建阶段分组为 phase1 / phase2 / phase3 / plugins 四个子对象。引用方式从 ctx.root 改为 ctx.phase3.root
  • BuildStep 提取closeBundle 中的 Phase 2/3 内联逻辑提取为显式的 BuildStep 接口和实现类(ClientBuildStep / SSGBuildStep),错误消息现在标识具体失败的 Phase 序号。

工程质量改进

安全性

  • FIX-02: less-layout.ts 中 SPA fetch-and-swap 导航的 innerHTML 替换为 DOMParser.parseFromString(),消除 XSS 风险。

代码质量

  • FIX-04: signals 模块的 // deno-lint-ignore-file no-explicit-any 从文件级缩小到仅必要的行级别。
  • FIX-05: core/index.ts 直接导入 html-escape.ts,消除 ssr-handler.ts 的冗余重导出层。
  • FIX-06a: adapter-vite/index.tscatch 块添加 log.debug 输出。
  • FIX-06b: signals 模块删除未使用的 _subVersion 变量。

架构债务清理

  • ARCH-01: LessBuildContext 30+ 字段按 Phase 分组为 Phase1Meta / Phase2Meta / Phase3Meta / PluginMeta 四个独立类,reset() 方法随之拆分。
  • ARCH-02: closeBundle 的 Phase 2/3 提取为 BuildStep 抽象,构建日志标识具体 Phase 序号。
  • ARCH-03: @lessjs/signal 从 797 行单一文件拆分为 6 个模块:engine.ts / polyfill.ts / framework.ts / sugar.ts / types.ts / index.ts
  • ARCH-04: build-manifest.ts 包标注从 @lessjs/core 修正为 @lessjs/adapter-vite

模块状态清理

  • content nav 模块:删除 _navSections / _headerNav 模块级可变状态,全部改为通过 ctx.plugins 传递。
  • ADR 0018 补充:继承虚拟模块模式,与 blog 模块的改造一致。

文档与决策

  • ADR 0019: 综合改进计划 — 三阶段路线图(P0 工程质量 / P1 架构债务 / P2 护城河)。
  • ADR 0020: DSD 渲染引擎与 Islands 策略增强 — 四个提案(DevTools Panel、策略推荐、L3+ 嵌套、Speculative Rules 深度集成)。
  • 四份审查报告已归档到 deliverables/

测试覆盖改进

  • @lessjs/app: 新增 7 个整合测试,填补最关键的测试缺口(app 包零测试 → 基础覆盖)。
  • @lessjs/core: parseQuery 测试新增多值 key 测试用例。
  • @lessjs/create: 新增 --help 标志输出和非法项目名测试。

English

Overview

From v0.11.0 to v0.12.0, LessJS underwent a comprehensive team review covering product positioning, architecture, code quality, and test coverage. Based on the review findings, this release focuses entirely on engineering quality and architecture debt cleanup. No new features — just making existing code more robust, maintainable, and secure.

Version Changes

Package Old New Type
@lessjs/core 0.11.0 0.12.0 ⚠️ Breaking
@lessjs/adapter-vite 0.1.0 0.2.0 ⚠️ Breaking
@lessjs/app 0.3.0 0.3.1 🩹 Patch
@lessjs/content 0.3.1 0.3.2 🩹 Patch
@lessjs/signals 0.6.2 0.6.3 🩹 Patch
@lessjs/ui 0.7.0 0.7.1 🩹 Patch
@lessjs/i18n 0.1.0 0.1.1 🩹 Patch
@lessjs/adapter-lit 0.8.0 0.8.0
@lessjs/rpc 0.6.1 0.6.1
@lessjs/create 0.8.1 0.8.1

Breaking Changes

⚠️ @lessjs/core 0.12.0

  • parseQuery() return type changed: Record<string, string>Record<string, string | string[]>. Duplicate query parameter keys are now merged into arrays (?tag=a&tag=b{ tag: ['a', 'b'] }) instead of taking the last value.
  • ssr-handler subpath export simplified: index.ts now imports directly from html-escape.ts instead of going through ssr-handler.ts. The ssr-handler.ts file remains as a backward-compat re-export — no consumer changes needed.

⚠️ @lessjs/adapter-vite 0.2.0

  • LessBuildContext fields grouped: The flat 30+ field list is now organized into phase1 / phase2 / phase3 / plugins sub-objects by build phase. References changed from ctx.root to ctx.phase3.root.
  • BuildStep extraction: Inline Phase 2/3 logic in closeBundle is extracted into explicit BuildStep interface and implementation classes (ClientBuildStep / SSGBuildStep). Error messages now identify which phase failed.

Engineering Quality Improvements

Security

  • FIX-02: Replaced innerHTML with DOMParser.parseFromString() in less-layout.ts SPA fetch-and-swap navigation, eliminating an XSS vector.

Code Quality

  • FIX-04: signals module // deno-lint-ignore-file no-explicit-any narrowed from file-level to specific lines only.
  • FIX-05: core/index.ts imports html-escape.ts directly, removing the redundant ssr-handler.ts re-export layer.
  • FIX-06a: Empty catch block in adapter-vite/index.ts now has log.debug output.
  • FIX-06b: Removed unused _subVersion variable in signals module.

Architecture Debt Cleanup

  • ARCH-01: LessBuildContext 30+ fields grouped by Phase into Phase1Meta / Phase2Meta / Phase3Meta / PluginMeta classes; reset() method split accordingly.
  • ARCH-02: closeBundle Phase 2/3 extracted into BuildStep abstraction; build logs identify specific phase numbers.
  • ARCH-03: @lessjs/signal split from a single 797-line file into 6 modules: engine.ts / polyfill.ts / framework.ts / sugar.ts / types.ts / index.ts.
  • ARCH-04: build-manifest.ts package annotation corrected from @lessjs/core to @lessjs/adapter-vite.

Module State Cleanup

  • content nav module: Removed _navSections / _headerNav module-level mutable state; all data flows through ctx.plugins instead.
  • ADR 0018 complement: Consistent with the blog module's virtual data module pattern.

Documentation & Decisions

  • ADR 0019: Comprehensive improvement plan — 3-phase roadmap (P0 engineering quality / P1 architecture debt / P2 moat building).
  • ADR 0020: DSD rendering engine & Islands strategy enhancement — 4 proposals (DevTools Panel, strategy recommendation, L3+ nesting, Speculative Rules deep integration).
  • 4 review reports archived in deliverables/.

Test Coverage Improvements

  • @lessjs/app: 7 new integration tests added, filling the most critical test gap (app package went from zero tests to baseline coverage).
  • @lessjs/core: parseQuery test updated with multi-value key test case.
  • @lessjs/create: Added --help flag output and invalid project name tests.

Generated from git log v0.11.0..v0.12.0 —no-merges (40+ commits including team review reports, ADR creation, engineering fixes, and release prep).

v0.11.0

11 May 13:11

Choose a tag to compare

LessJS v0.11.0 发布说明

概览

v0.11.0 是框架诞生以来最重要的架构变更:运行时与构建编排彻底解耦@lessjs/core 现在是纯 Web Standard 运行时,零 Node/Vite 依赖;所有构建逻辑迁移至全新包 @lessjs/adapter-vite

这一拆分消除了 5 个兼容性补丁——这些补丁存在的原因仅仅是 Vite 的 SSR runner 不得不加载包含 Vite 插件 API 的框架代码,形成了循环依赖,导致 npm: specifier 解析反复出错。


破坏性变更

import { less } 迁移到 @lessjs/adapter-vite

// 之前 (v0.10.x)
import { less } from '@lessjs/core';

// 之后 (v0.11.0)
import { less } from '@lessjs/adapter-vite';

如果你使用 @lessjs/app无需任何改动lessjs() 内部已改为从 adapter-vite 重导出。只有直接从 @lessjs/core 导入 less() 的代码需要更新。

运行时导入不变:

import { renderDSD, island, escapeHtml } from '@lessjs/core';  // 照常使用

新特性

@lessjs/adapter-vite — 全新包

专用 Vite 构建编排适配器(ADR 0017):

  • less() Vite 插件工厂
  • 路由扫描(route-scanner
  • SSG 三阶段流水线(buildbuild-clientbuild-ssg
  • HMR 支持
  • coreResolvePlugin — 用户项目代码的 specifier 翻译
  • 所有 node:* 导入和 Vite API 依赖都在这里

这遵循了与 @lessjs/adapter-lit 相同的模式:core 定义接口,adapter 实现接口。

纯 Web Standard 的 @lessjs/core

@lessjs/core 现在仅包含运行时逻辑:

  • node:* 导入(node:pathnode:processnode:url — 全部移除)
  • import type { Plugin } from 'vite'
  • npm: / deno: 依赖
  • 可运行于 Deno / Node 18+ / Bun / Cloudflare Workers / Vercel Edge — 任何 ESM 运行时
  • 唯一外部依赖:parse5(纯 JS HTML 解析器)

JSR Registry API 驱动的 create CLI

@lessjs/create CLI 不再硬编码包版本。脚手架生成时从 JSR Registry API 获取最新版本,新项目始终使用最新兼容版本,无需更新 CLI。

自动注入 Core 子路径别名

buildCoreSubpathAliases() 自动为所有 @lessjs/core/* 子路径生成 Vite alias。新增子路径导出时不再需要手动添加 alias — 开箱即用。(ADR 0015


Bug 修复

问题 修复
Bug #11:JSR 远程模式下 npm: specifier 无法解析 架构拆分消除了 5 个补丁 — core 不再需要作为 Vite 源码被加载
Bug #10:虚拟模块 load() 中 esbuild TS→JS 编译 coreResolvePlugin(现位于 adapter-vite)中修复
Bug #9:JSR 远程 SSR 子路径解析失败 双模式解析:本地用 resolve.alias,远程用 resolveId+load 虚拟模块(ADR 0016
循环发布依赖(@lessjs/content/sitemap 使导入不可分析,打破循环
<script> head-extras 重复警告 模块级去重标记 — 每个进程仅警告一次

架构变更

之前 (v0.10.x)

@lessjs/core  =  运行时(renderDSD, island, escape...)
              +  Vite 插件(less(), 路由扫描, SSG 流水线, HMR)
              +  5 个兼容性补丁(npm: 翻译, 虚拟模块, esbuild 编译)

之后 (v0.11.0)

@lessjs/core         纯运行时 — 零 node:*, 零 Vite 依赖, 零 npm:
                      renderDSD / island / escape / adapter-registry / navigation / logger / errors

@lessjs/adapter-vite  Vite 构建编排
                      less() → Plugin[], 路由扫描, SSG 三阶段, HMR, core-resolve
                      所有 node:* 和 Vite API 依赖都在这里

依赖图

之前:  app → core(运行时 + Vite 混合)→ content, i18n
之后:  app → core(纯运行时)+ adapter-vite(构建)→ content, i18n

包版本

v0.10.x v0.11.0
@lessjs/core 0.10.7 0.11.0
@lessjs/adapter-vite 0.1.0(新增)
@lessjs/adapter-lit 0.7.1 0.8.0
@lessjs/app 0.1.0 0.3.0
@lessjs/content 0.2.0 0.3.0
@lessjs/create 0.6.2 0.8.1
@lessjs/i18n 0.1.0 0.1.0
@lessjs/rpc 0.6.1 0.6.1
@lessjs/signals 0.6.2 0.6.2
@lessjs/ui 0.7.0 0.7.0

发布顺序:rpc → signals → core → adapter-vite → content → i18n → adapter-lit → ui → app → create


迁移指南

@lessjs/app 用户(大多数情况)

无需改动。 lessjs() 内部已使用 @lessjs/adapter-vite

直接使用 @lessjs/core 的用户

  1. 更新 less() 导入:
    import { less } from '@lessjs/adapter-vite';  // 原为 '@lessjs/core'
  2. 运行时导入保持不变:
    import { renderDSD, island, escapeHtml } from '@lessjs/core';
  3. deno.json 中添加 @lessjs/adapter-vite 导入。

vite.config.ts 配置

如果你之前手动配置了 @lessjs/core/* 子路径的 Vite alias,可以简化 — adapter-vite 中的 buildCoreSubpathAliases() 现在自动处理。


数据

  • 60 个文件变更,+1,673 / −1,269 行
  • 14 个测试文件core 迁移到 adapter-vite
  • 5 个兼容性补丁从 core 中消除
  • 273 个测试通过(50 core + 182 adapter-vite + 11 create + 30 其他)

LessJS v0.11.0 Release Notes

Overview

v0.11.0 is the most significant architecture change since the framework's inception: separating runtime from build orchestration. @lessjs/core is now a pure Web Standard runtime with zero Node/Vite dependencies, while all build logic moves to the new @lessjs/adapter-vite package.

This eliminates 5 compatibility patches that existed solely because Vite's SSR runner had to load framework code that contained Vite plugin APIs — a circular dependency that caused recurring npm: specifier resolution failures.


Breaking Changes

import { less } moved to @lessjs/adapter-vite

// Before (v0.10.x)
import { less } from '@lessjs/core';

// After (v0.11.0)
import { less } from '@lessjs/adapter-vite';

If you use @lessjs/app, no changes neededlessjs() internally re-exports from adapter-vite. Only direct @lessjs/core imports of less() need updating.

Runtime imports from @lessjs/core are unchanged:

import { renderDSD, island, escapeHtml } from '@lessjs/core';  // Still works

What's New

@lessjs/adapter-vite — New Package

A dedicated Vite build orchestration adapter (ADR 0017):

  • less() Vite plugin factory
  • Route scanning (route-scanner)
  • SSG 3-phase pipeline (build, build-client, build-ssg)
  • HMR support
  • coreResolvePlugin — specifier translation for user project code
  • All node:* imports and Vite API dependencies live here

This follows the same pattern as @lessjs/adapter-lit: core defines the interface, adapter implements it.

Pure Web Standard @lessjs/core

@lessjs/core now contains only runtime logic:

  • Zero node:* imports (node:path, node:process, node:url — all removed)
  • Zero import type { Plugin } from 'vite'
  • Zero npm: / deno: dependencies
  • Runs on Deno / Node 18+ / Bun / Cloudflare Workers / Vercel Edge — any ESM runtime
  • Only external dependency: parse5 (pure JS HTML parser)

JSR Registry API for create CLI

The @lessjs/create CLI no longer hardcodes package versions. It fetches the latest versions from the JSR Registry API at scaffold time, so new projects always get the latest compatible versions without CLI updates.

Automatic Core Subpath Alias Injection

buildCoreSubpathAliases() automatically generates Vite aliases for all @lessjs/core/* subpaths. No more manually adding aliases when a new subpath export is introduced — it just works. (ADR 0015)


Bug Fixes

Issue Fix
Bug #11: npm: specifiers unresolvable in JSR remote mode 5 patches eliminated by architecture split — core no longer needs to be loaded as Vite source code
Bug #10: esbuild TS→JS compilation in virtual module load() Fixed in coreResolvePlugin (now in adapter-vite)
Bug #9: Subpath resolution failures in JSR remote SSR Dual-mode resolution: resolve.alias for local, resolveId+load virtual modules for remote (ADR 0016)
Circular publish dependency (@lessjs/content/sitemap) Made import unanalyzable to break the cycle
Duplicate <script> head-extras warnings Module-level dedup flag — warn once per process

Architecture Changes

Before (v0.10.x)

@lessjs/core  =  Runtime (renderDSD, island, escape...)
              +  Vite Plugin (less(), route scanning, SSG pipeline, HMR)
              +  5 compatibility patches (npm: translation, virtual modules, esbuild compile)

After (v0.11.0)

@lessjs/core         Pure runtime — zero node:*, zero Vite deps, zero npm:
                      renderDSD / island / escape / adapter-registry / navigation / logger / errors

@lessjs/adapter-vite  Vite build orchestration
                      less() → Plugin[], route scanning, SSG 3-phase, HMR, core-resolve
                      All node:* and Vite API dependencies live here

Dependency Graph

Before:  app → core (runtime + Vite mixed) → content, i18n
After:   app → core (pure runtime) + adapter-vite (build) → content, i18n

Package Versions

Package v0.10.x v0.11.0
@lessjs/core 0.10.7 0.11.0
@lessjs/adapter-vite 0.1.0 (new)
@lessjs/adapter-lit 0.7.1 0.8.0
@lessjs/app 0.1.0 0.3.0
@lessjs/content 0.2.0 0.3.0
@lessjs/create 0.6.2 0.8.1
@lessjs/i18n 0.1.0 0.1.0
@lessjs/rpc 0.6.1 0.6.1
@lessjs/signals 0.6.2 0.6.2
@lessjs/ui 0.7.0 0.7.0

Publish order: rpc → signals → core → adapter-vite → content → i18n → adapter-lit → ui → app → create


Migration Guide

For @lessjs/app users (most common)

No changes required. lessjs() internally uses @lessjs/adapter-vite.

For direct @lessjs/core users

  1. Update less() import:
    import { less } from '@lessjs/adapter-vite';  // was '@lessjs/core'
  2. Keep runtime imports as-is:
    import { renderDSD, island, escapeHtml } from '@lessjs/core';
  3. Add @lessjs/adapter-vite to your deno.json imports.

For vite.config.ts

If you had custom Vite aliases for @lessjs/core/* subpaths, you can simplify them — buildCoreSubpathAliases() in adapter-vite now han...

Read more

v0.10.0

10 May 16:13

Choose a tag to compare

LessJS v0.10.0 — 工作总结

日期:2026-05-10 ~ 2026-05-11
仓库:lessjs-run/lessjs
分支:dev
Commit:41a6f6a → b024cea5a1f1314c9b02e


TL;DR

v0.10.0 通过 ADR 0008-0014 七条决策,完成了 SSR 层的架构净化。SSR bundle 现在导出 renderRoute()getStaticPaths()routeInfo[] 三个干净的公共 API,build-ssg.ts 从"无所不知的上帝类"退化为纯粹的编排器(路径枚举 + 文件写入)。随后进行 DX Hardening,修复用户侧体验问题:lessjs() 作为推荐入口替代 less(),CI 发布流水线补全,create 模板全面升级。


交付内容

架构决策(ADR 0008-0014)

ADR 标题 核心变更
0008 Eliminate createServer() + globalThis Bridges 消除 createServer() 包装函数和全部 5 个 globalThis 桥接(__lessDevServer__lessIslands__lessRoutes__lessPluginCtx__lessBuildMetadata),改为 Vite 插件 ctx 显式传递
0010 Eliminate .less/ Temp Files 构建 ctx 替代 .less/routes.json.less/nav.json.less/islands.json 等文件系统中间态
0011 Eliminate Last globalThis via closeBundle closeBundle 钩子替代 globalThis __lessBuildMetadata 数据传递,完成 globalThis 的最终消除
0012 Extract @lessjs/app Umbrella Package lessApp()lessContent()lessI18n() 从 core 提取到独立 @lessjs/app
0013 Eliminate less-runtime Barrel + Inline Shim less-runtime.ts barrel 重导出被消除,各适配器文件内联自己的 shim(DOMStringMap、customElements.define 幂等补丁)
0014 SSR Bundle Export renderRoute() SSR bundle 新增 renderRoute(path, opts)→HTMLgetStaticPaths(path)→params[]routeInfo[] 导出,build-ssg.ts 不再直接访问 customElements/正则解析源文件/直接调用 renderDSD+wrapInDocument

ADR 0014 具体消除的 7 个边界违规

  1. build-ssg.ts 直接访问 globalThis.customElements 获取 tagName
  2. build-ssg.ts 用正则解析源文件提取 tagName
  3. build-ssg.ts 直接调用 renderDSD()wrapInDocument()
  4. build-ssg.ts 在 bundle 外部给 customElements.define 打幂等补丁
  5. build-ssg.ts 在 bundle 外部初始化 blog 数据(双作用域)
  6. build-ssg.ts 直接 import() 动态路由的源文件
  7. build-ssg.ts 直接导入 @lessjs/i18n 调用 initI18nData()

关键代码变更

  • packages/core/src/entry-renderer.ts(+80 行):生成 routeInforenderRoute()getStaticPaths(),将 customElements.define 幂等补丁移入 bundle
  • packages/core/src/cli/build-ssg.ts(-190 行):移除所有边界违规代码,改为调用 module.renderRoute()module.getStaticPaths()

包版本变更

旧版本 新版本 理由
@lessjs/core 0.9.2 0.10.0 新增 3 个公共 API 导出
@lessjs/app 0.1.0 0.2.0 更新 core 依赖 ^0.9 → ^0.10
@lessjs/content 0.2.0 0.3.0 A.2 重构 + i18n 拆分
@lessjs/adapter-lit 0.7.1 0.8.0 ADR 0008/0012/0013 变更
@lessjs/create 0.6.2 0.7.0 修复 + 模板更新
@lessjs/ui 0.7.0 不变 无变更
@lessjs/signal 0.6.2 不变 无变更
@lessjs/rpc 0.6.1 不变 无变更
@lessjs/i18n 0.1.0 不变 无变更

文档更新

  • Roadmap:v0.10 从"SSG + ISR + PWA"更新为"SSR Architecture Purification + API Boundary",ISR/PWA 后移至 v0.11
  • Changelog:新增 v0.10.0 条目(新增/变更/修复三个分类)
  • Decision Index:补全 ADR 0008、0010-0014 条目
  • Version Strategy ADR (0006):v0.9/v0.10 实际交付与原计划的偏差已标注

DX Hardening(4c9b02e)

发布前 DX(开发者体验)加固,确保用户升级到 v0.10.0 后不踩坑。

修复清单

类别 文件 变更
🔴 阻断性 publish.yml 补全 @lessjs/app@lessjs/i18n 的 JSR 发布步骤,否则 deno add @lessjs/app 会 404
🔴 阻断性 deno.json 修复 publish task 依赖顺序:core 必须在 content/i18n/ui/adapter-lit 之前发布
🟡 重要 create/cli.ts 模板从 less() / @lessjs/core 切换到 lessjs() / @lessjs/app(推荐入口)
🟡 重要 create/cli.ts 移除版本号 fallback(0.x 无向后兼容保证,读取失败应直接报错)
🟡 重要 create/cli.ts 修复 loadWorkspaceVersion 路径:补回 packages/ 目录层级
🟡 重要 create/cli.ts 修复 Windows 路径 bug:new URL(...).pathnamefileURLToPath() 避免双盘符 C:\C:\...
🟢 CI test.yml 新增 test-i18ntest-content CI job
🟢 CI test.yml 新增 app/i18n/content 包的 typecheck
🟢 文档 configuration.ts 配置文档推荐 lessjs() 入口(中英双语)
🟢 文档 README.md / README.en.md 更新包版本表、版本历史、代码示例

设计决策

  • 为什么移除 fallback 版本号? — 0.x 阶段没有向后兼容保证,fallback 到旧版本号(如 0.1.0)会让用户误以为可用,实际 API 已不兼容。读不到就报错,快速失败比静默降级更安全。
  • 为什么 lessjs() 替代 less()lessjs() 来自 @lessjs/app 伞包,一键启用 app+content+i18n,是 ADR 0012 提取 @lessjs/app 后的推荐用法。less() 来自 @lessjs/core,仍可用但不推荐。
  • Windows 路径 bug 的根因new URL('file:///C:/...').pathname 返回 /C:/...(带前导 /),拼接后变成 C:\C:\...fileURLToPath() 是 Node.js 提供的跨平台正确方案。

影响的文件(8 个)

.github/workflows/publish.yml         |  6 +++-
.github/workflows/test.yml            | 36 +++++++++++++++++++++-
README.en.md                          | 45 +++++++++++++++------------
README.md                             | 48 ++++++++++++++++-------------
deno.json                             |  2 +-
packages/create/__tests__/cli.test.ts | 58 ++++++++++++++++++++++----------
packages/create/cli.ts                | 33 +++++++++++---------
www/app/routes/guide/configuration.ts | 14 ++++++---
8 files changed, 162 insertions(+), 80 deletions(-)

测试状态

  • 448 测试全部通过(213 steps, 0 failed)
  • SSG smoke test 通过:129 HTML 页面生成(43 en + 43 zh + 动态路由)
  • Pre-commit hooks 全通过:deno fmt / deno lint / deno check
  • DX Hardening 后 448 测试仍全部通过(create 包 12 个测试全部更新并通过)

路线图更新

原路线图 v0.10 计划为"SSG + ISR + PWA",实际因架构净化需求优先而调整为"SSR Architecture Purification + API Boundary"。ISR/PWA 内容后移至 v0.11。

v0.11 规划(暂定):

  • 路由级 revalidation
  • Cache lock + Stale fallback
  • Service Worker 策略
  • CDN recipes
  • .less Compiler Alpha
  • AST runtime-shim 生成

Git Log

4c9b02e fix: user-facing DX hardening for v0.10.0
5a1f131 chore: bump packages + update docs for v0.10.0 release
b024cea chore: bump @lessjs/core to v0.10.0
41a6f6a feat: ADR 0014 — SSR bundle exports renderRoute(), eliminates build-ssg.ts boundary violations
8ca9c5c fix(ssg): restore i18n locale expansion after ADR 0010/0011 refactor
67518ab fix(ssg): clean URL conversion for nested route paths
6c5a992 fix(create): add missing @lessjs/core subpath aliases
f223bef fix(ci): resolve 7 test failures after ADR 0011
19a063e feat: ADR 0012 extract @lessjs/app + ADR 0013 eliminate less-runtime barrel
6a2e2d8 fix: sidebar navigation empty after ADR 0011
744ec32 refactor(core): ADR 0008 Phase C+B — eliminate createServer() + globalThis bridges

经验教训

  1. 边界违规是技术债的高利贷build-ssg.ts 的 7 个违规都是在紧急修复中引入的快速 hack,每次"先这样,后面再改"都在积累债务
  2. 0.x 阶段的版本规划要留弹性:原计划 v0.10 做 ISR/PWA,但架构问题不解决,后续功能都建在沙上。调整优先级是正确的
  3. ^0.9 不匹配 0.10.0:semver 对 0.x 的 ^0.9 意为 >=0.9.0 <0.10.0,升级 minor 时必须同步更新依赖声明
  4. ADR 驱动开发的价值:7 条 ADR 形成清晰的决策链(0008→0010→0011→0012→0013→0014),每步可独立验证,回滚成本低
  5. 发布前必须走一遍用户视角:DX Hardening 发现的 3 个阻断性问题(CI 缺包、依赖顺序错、模板入口过时)在开发者自测时不会暴露,只有模拟 deno add + 新项目脚手架才能发现
  6. 0.x 的 fallback 策略是反模式:给版本号加 fallback 看似容错,实则在 API 不兼容时让用户静默拿到旧版本。0.x 阶段应选择快速失败
  7. Windows 路径是 CI 盲区new URL('file:///...').pathname 在 macOS/Linux 恰好能工作,但 Windows 上会多一个前导 /,本地不跑 Windows 就永远发现不了

Full Changelog: v0.9.2...v0.10.0