0.9.0-alpha1
LessJS 0.9.0-alpha1
@lessjs/content — 全新内容插件
Breaking Change: @lessjs/blog 已被 @lessjs/content 替代,不再提供向后兼容。
@lessjs/content 是一个统一的构建时内容插件,集成三个独立模块,按需启用:
| 模块 | 功能 | 状态 |
|---|---|---|
| Blog | Markdown 解析 + 前端元数据 + 动态路由生成 | 从 @lessjs/blog 迁移 |
| Nav | 自动扫描路由文件的 meta 导出,生成侧边栏导航数据 |
🆕 |
| Sitemap | 从 SSG 产物自动生成 sitemap.xml + robots.txt |
🆕 |
import { lessContent } from '@lessjs/content';
export default defineConfig({
plugins: [
less(),
lessContent({
blog: { contentDir: 'content/blog', basePath: '/blog' },
nav: { routesDir: 'app/routes', headerNav: [...] },
sitemap: { hostname: 'https://lessjs.org' },
}),
],
});Blog 模块
与 @lessjs/blog 相同的 Markdown 解析能力(gray-matter + marked),但通过统一的 lessContent() 插件配置。支持自定义 Markdown 渲染器。
Nav 模块 🆕
自动从路由文件的 meta 导出提取导航数据,生成 virtual:less-nav 虚拟模块供 <less-layout> 侧边栏使用。无需手动维护 nav-data.ts。
// app/routes/guide/getting-started.ts
export const meta = { section: 'Guide', label: 'Getting Started', order: 10 };Sitemap 模块 🆕
SSG 构建完成后自动扫描 dist/ 目录,生成 sitemap.xml 和 robots.txt。支持排除路径、自定义 changefreq/priority。
SSR 属性绑定保留
Breaking Change: @lessjs/adapter-lit 的 interpolate() 函数现在将 Lit 属性绑定(.prop="${val}")序列化为 kebab-case HTML 属性,而不是直接丢弃。
| 之前 (v0.8.0) | 之后 (v0.9.0) |
|---|---|
.navItems="${data}" → 被丢弃 |
.navItems="${data}" → nav-items="[{...}]" |
| 嵌套组件收不到数据 | 嵌套组件在 SSR 阶段获得完整数据 |
renderNestedCustomElements() 中的 parseAttrsToProps() 也已更新:自动检测 JSON 格式的属性值并解析回 JS 对象/数组。
新增 camelToKebab() 辅助函数(adapter-lit/ssr.ts),将 navItems → nav-items 等属性名转换。
islandEffect() 泄漏修复
@lessjs/signal 的 islandEffect() 修复了三个资源泄漏问题:
- MutationObserver 未断开:组件移除后 MO 仍监听
- setInterval 未清除:轮询回调持续执行
- 重复清理:多个清理路径可能重复调用
dispose()
新实现使用统一的 teardown() 函数,保证 MO + interval + effect 三者只被清理一次。
SSG 管道增强
- virtual:less-nav 修复:Phase 3 SSG server 现在包含
less:ssg-virtual-nav插件,确保路由文件中import 'virtual:less-nav'正确解析 - @lessjs/content 初始化:Phase 3 自动检测并初始化 content 插件的 blog 数据存储
- Sitemap 生成:SSG 构建完成后自动调用
@lessjs/content/sitemap生成sitemap.xml
文档与仓库清理
- 删除
demo/目录(v0.4.0 遗留代码,无 CI、无引用) - 删除
/docs/routes/demo/和/docs/routes/examples/(mock 展示页) - 合并
/ui+/styling/less-ui→ 单一/ui页面 @lessjs/blog→@lessjs/content全局替换- 删除
deliverables/(审计报告 + PRD,不属于源码仓库) - 删除
docs/package.json、docs/CNAME(遗留配置) - 迁移
e2e/→docs/e2e/(Playwright 测试定位到 docs 站点) - 删除
playwright-report/、test-results/(构建产物,已加入.gitignore)
包版本
| 包 | 旧版本 | 新版本 |
|---|---|---|
| @lessjs/core | 0.8.1 | 0.9.0 |
| @lessjs/adapter-lit | 0.6.4 | 0.7.0 |
| @lessjs/content | 0.1.0 (原 @lessjs/blog) | 0.2.0 |
| @lessjs/rpc | 0.6.1 | — |
| @lessjs/signal | 0.6.1 | 0.6.2 |
| @lessjs/ui | 0.6.2 | — |
| @lessjs/create | 0.6.1 | — |
已知问题
serializeAttributes()在@lessjs/core/render-dsd.ts中缺少camelToKebab转换,currentPath被输出为currentpath而非current-path(#1)route-scanner.ts中await import(pkg)变量动态导入触发 JSRunanalyzable-dynamic-import警告(不影响功能,仅影响发布提示)
升级指南
-
替换 @lessjs/blog → @lessjs/content:
- import { lessBlog } from '@lessjs/blog'; + import { lessContent } from '@lessjs/content'; - lessBlog({ contentDir: 'posts' }) + lessContent({ blog: { contentDir: 'posts' } })
-
侧边栏导航迁移:删除手动维护的
nav-data.ts,在路由文件中添加meta导出,配置lessContent({ nav: { routesDir: 'app/routes', headerNav: [...] } }) -
Sitemap 启用:添加
lessContent({ sitemap: { hostname: 'https://yoursite.com' } }) -
SSR 属性绑定:如果之前有依赖属性绑定被丢弃的行为(不太可能),现在这些绑定会以 kebab-case 属性形式保留在 HTML 中
LessJS v0.9.0-alpha1
@lessjs/content — New Unified Content Plugin
Breaking Change: @lessjs/blog has been replaced by @lessjs/content with no backward compatibility.
@lessjs/content is a unified build-time content plugin that integrates three independent modules, each opt-in:
| Module | Purpose | Status |
|---|---|---|
| Blog | Markdown parsing + frontmatter + dynamic route generation | Migrated from @lessjs/blog |
| Nav | Auto-scans route files for meta exports, generates sidebar navigation data |
🆕 |
| Sitemap | Auto-generates sitemap.xml + robots.txt from SSG output |
🆕 |
import { lessContent } from '@lessjs/content';
export default defineConfig({
plugins: [
less(),
lessContent({
blog: { contentDir: 'content/blog', basePath: '/blog' },
nav: { routesDir: 'app/routes', headerNav: [...] },
sitemap: { hostname: 'https://lessjs.org' },
}),
],
});Blog Module
Same Markdown parsing capabilities as @lessjs/blog (gray-matter + marked), but configured through the unified lessContent() plugin. Supports custom Markdown renderer via the markdown option.
Nav Module 🆕
Automatically extracts navigation data from route file meta exports, generating a virtual:less-nav virtual module consumed by <less-layout> sidebar. No more manually maintained nav-data.ts.
// app/routes/guide/getting-started.ts
export const meta = { section: 'Guide', label: 'Getting Started', order: 10 };Sitemap Module 🆕
Automatically scans dist/ after SSG build completes, generating sitemap.xml and robots.txt. Supports path exclusion, custom changefreq/priority.
SSR Property Binding Preservation
Breaking Change: @lessjs/adapter-lit's interpolate() now serializes Lit property bindings (.prop="${val}") as kebab-case HTML attributes instead of stripping them entirely.
| Before (v0.8.0) | After (v0.9.0) |
|---|---|
.navItems="${data}" → stripped |
.navItems="${data}" → nav-items="[{...}]" |
| Nested components receive no data | Nested components get full data during SSR |
parseAttrsToProps() in renderNestedCustomElements() also updated: auto-detects JSON attribute values and parses them back to JS objects/arrays.
Added camelToKebab() helper in adapter-lit/ssr.ts for navItems → nav-items style conversions.
islandEffect() Leak Fix
@lessjs/signal's islandEffect() fixed three resource leaks:
- MutationObserver not disconnected: MO continued observing after element removal
- setInterval not cleared: Polling callback kept running indefinitely
- Duplicate cleanup: Multiple cleanup paths could call
dispose()more than once
New implementation uses a unified teardown() function guaranteeing MO + interval + effect are cleaned exactly once.
SSG Pipeline Enhancements
- virtual:less-nav fix: Phase 3 SSG server now includes
less:ssg-virtual-navplugin, ensuringimport 'virtual:less-nav'in route files resolves correctly - @lessjs/content initialization: Phase 3 auto-detects and initializes content plugin's blog data store
- Sitemap generation: After SSG build completes, automatically invokes
@lessjs/content/sitemapto generatesitemap.xml
Docs & Repository Cleanup
- Removed
demo/directory (v0.4.0 legacy, no CI, no references) - Removed
/docs/routes/demo/and/docs/routes/examples/(mock showcase pages) - Merged
/ui+/styling/less-ui→ single/uipage @lessjs/blog→@lessjs/contentglobal replacement- Removed
deliverables/(audit reports + PRDs, not source code) - Removed
docs/package.json,docs/CNAME(legacy configs) - Relocated
e2e/→docs/e2e/(Playwright tests target docs site) - Removed
playwright-report/,test-results/(build artifacts, added to.gitignore)
Package Versions
| Package | Old | New |
|---|---|---|
| @lessjs/core | 0.8.1 | 0.9.0 |
| @lessjs/adapter-lit | 0.6.4 | 0.7.0 |
| @lessjs/content | 0.1.0 (was @lessjs/blog) | 0.2.0 |
| @lessjs/rpc | 0.6.1 | — |
| @lessjs/signal | 0.6.1 | 0.6.2 |
| @lessjs/ui | 0.6.2 | — |
| @lessjs/create | 0.6.1 | — |
Known Issues
serializeAttributes()in@lessjs/core/render-dsd.tslackscamelToKebabconversion —currentPathis output ascurrentpathinstead ofcurrent-path(#1)await import(pkg)variable dynamic import inroute-scanner.tstriggers JSRunanalyzable-dynamic-importwarning (does not affect functionality, publish advisory only)
Upgrade Guide
-
Replace @lessjs/blog → @lessjs/content:
- import { lessBlog } from '@lessjs/blog'; + import { lessContent } from '@lessjs/content'; - lessBlog({ contentDir: 'posts' }) + lessContent({ blog: { contentDir: 'posts' } })
-
Sidebar navigation migration: Delete manually maintained
nav-data.ts, addmetaexports to route files, configurelessContent({ nav: { routesDir: 'app/routes', headerNav: [...] } }) -
Enable sitemap: Add
lessContent({ sitemap: { hostname: 'https://yoursite.com' } }) -
SSR property bindings: If you previously relied on property bindings being stripped (unlikely), they are now preserved as kebab-case HTML attributes
Full Changelog: v0.8.1...0.9.0-alpha1