ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Astryx 集成主题分发:让 integration 包贡献可安装、可按 owner 复制的源码主题

Astryx 集成主题分发:让 integration 包贡献可安装、可按 owner 复制的源码主题 Astryx 集成主题分发让 integration 包贡献可安装、可按 owner 复制的源码主题【免费下载链接】astryxAn open source design system thats fully customizable and agent ready项目地址: https://gitcode.com/GitHub_Trending/as/astryx本文对应变更[feat] Let integration packages contribute source themesastryxdesign/cli patch 版本。核心能力一句话概括一个 Astryx integration 包可以在自己的astryx.integration.*清单中声明一个themes根目录该目录采用与 Astryx CLI 内置主题完全相同的 bundle 结构一旦这个 integration 被安装进项目它贡献的主题就会出现在astryx theme list中开发者可以像使用内置主题一样用astryx theme add把它们复制到自己的项目里并完全拥有源码。读完本文你将掌握integration 主题的清单声明方式、主题 catalog 的标准结构、theme list/theme add的完整用法与参数、以及这套机制背后的发现、校验与路径安全实现。背景integration 是什么主题如何进入项目Astryx 是一个开源的、完全可定制且面向 Agent 的设计系统见仓库根 README.md。除了核心包 packages/core 和 CLI packages/cli 之外Astryx 通过“integration”机制让第三方包贡献各种制品组件components、模板templates、codemodcodemods、文档主题docs、Agent 指南agentDocs等。这些声明都集中在 integration 包根目录下、与package.json并排的astryx.integration.{ts,mjs,js}清单文件中。本次变更让themes成为清单支持的又一个可选字段integration 可以直接贡献“源码主题”source themes。这与内置主题走同一条分发与消费链路区别只在于主题文件的物理来源——内置主题来自 CLI 自身的 bundleintegration 主题来自你安装的第三方包。一、在 integration 清单中声明 themes 根目录主题声明的入口是 integration 清单的themes字段。完整字段清单与类型见 integration.doc.mjs字段类型定义见 type.tsthemes?: string与 zod 校验 schema.mjs。一个典型的astryx.integration.mjs示例export default { components: ./src/components, templates: ./src/templates, codemods: ./codemods, docs: ./docs, themes: ./themes, // 本次新增源码主题目录 agentDocs: { append: [Run acme verify before finishing.], }, issuesUrl: https://github.com/acme/widgets/issues, };要点themes是相对路径指向一个“源码主题 catalog 根目录”运行时会被解析为绝对路径。清单中每个字段都是可选的themes不写也不影响其他贡献类型。integration 的身份名称与版本来自包自己的package.json而不是这份清单。该字段是前向兼容但版本门控的在themes字段发布之前推出的 CLI 会忽略它并给出警告同时继续加载它能理解的其他贡献类型也就是说旧版 CLI 无法列出或添加这些主题见 integration.doc.mjs 的 notes。发布前可用astryx doctor integration validate校验清单集成校验实现见 validate-integration.mjs。二、主题 catalog 的标准结构与内置主题同构声明themes根目录后这个目录必须包含一个manifest.json其旁边是每个主题 slug 对应一个子目录。这正是 CLI 内置主题 bundle 所用的结构对应常量定义在 theme-discovery.mjsBUNDLED_THEME_PACKAGE astryxdesign/cli内置主题的 owner 包名THEMES_DIRCLI 内置主题目录packages/cli/assets/templates/themesTHEME_MANIFEST_BASENAME manifest.jsoncatalog 清单文件名。先看 CLI 内置 catalog 的真实形态packages/cli/assets/templates/themes/manifest.json由scripts/generate-cli-themes.mjs生成{ version: 1, generatedBy: scripts/generate-cli-themes.mjs, themes: [ { slug: butter, displayName: Butter, description: Warm, creamy yellows with a friendly blue accent. ..., maintained: false, entry: butterTheme.ts, exportName: butterTheme, files: [butterTheme.ts, icons.tsx] }, { slug: neutral, displayName: Neutral, description: Restrained warm grays. Minimal and quiet, so the content stays the focus., maintained: true, entry: neutralTheme.ts, exportName: neutralTheme, files: [ neutralTheme.ts, icons.tsx, neutralPalettes.ts, neutralPalettes.generated.ts, neutralPaletteRefs.generated.ts, neutralPalettes.generated.receipt.json, palette.config.json ] } ] }integration 主题目录完全照搬这套结构。以仓库测试 integration-themes.test.mjs 构造的acme/themes为例node_modules/acme/themes/ ├── package.json ├── astryx.integration.mjs # export default {themes: ./themes}; └── themes/ ├── manifest.json └── ocean/ ├── oceanTheme.ts └── tokens/ └── colors.ts # oceanTheme.ts 的本地依赖同样在 files 中列出对应的manifest.json{ version: 1, themes: [ { slug: ocean, displayName: Ocean, description: Blue and calm., maintained: true, entry: oceanTheme.ts, exportName: oceanTheme, files: [oceanTheme.ts, tokens/colors.ts] } ] }catalog 中每个条目由 theme-discovery.mjs 的discoverThemeCatalog严格校验必须满足字段类型校验规则versionnumber必须是1themesarray必须存在且为数组slugstring非空且匹配^[a-z][a-z0-9]*(?:-[a-z0-9])*$小写 kebab-case、以字母开头同一 catalog 内不允许重复displayNamestring非空字符串descriptionstring必须是字符串maintainedboolean必须是布尔值entrystring非空必须是源码文件.ts/.tsx/.mjs/.js且必须出现在files中exportNamestring非空且匹配 JS 标识符规则^[$A-Z_a-z][$\w]*$filesstring[]至少一个文件不允许重复每个文件必须真实存在于themes/slug/目录下更细的约束还包括entry指向的模块必须真实导出exportName且files中列出的每个文件都必须真实存在于磁盘上。本地模块依赖会被静态解析验证——catalog 里列出的文件可以互相引用但任何本地import/export都必须能解析到catalog 内已列出的文件否则报ThemeModuleReferenceError实现见validateThemeModuleGraph与moduleExportsName。这意味着 integration 主题必须是“自包含”的你引用的每个本地文件都要写进files否则安装方theme add时会因缺失文件而失败。三、从零脚手架一个 integration 主题integration add theme与其手写上述文件Astryx 提供了脚手架命令astryx integration add theme对应实现 add-theme.mjs。它会一次性完成在无清单时创建astryx.integration.mjs、写入一个带命名导出的defineTheme源码文件、把条目追加进主题 catalog、首次使用时声明themes根目录并在包已使用 allowlist 时把清单与根目录加入package.json的files字段。# 在 integration 包目录内执行 astryx integration add theme ocean # 只预览将要写入的文件不落盘 astryx integration add theme ocean --dry-run以ocean为例生成的主题源码来自themeSourceimport {defineTheme} from astryxdesign/core/theme; export const oceanTheme defineTheme({ name: ocean, });同时自动生成themes/ocean/oceanTheme.ts上面的文件createOnly绝不覆盖已存在文件themes/manifest.json追加{slug, displayName, description, maintained, entry, exportName, files}条目displayName由 slug 拆词首字母大写而来exportName为驼峰 Theme后缀首次使用时通过patchIntegrationRoot在清单中声明themes: ./themes默认根目录常量DEFAULT_THEMES_ROOT ./themes若package.json已有filesallowlist则把清单与根目录追加进去保证发布时带上这些文件。命令成功后返回类型化回执IntegrationAddResponse见 integration-authoring.type.mjs包含kind: theme、name、root含是否新建、manifest、files所有将写入的相对于包根目录的路径与written/dryRun标记。程序的integrationAddThemeAPI 签名与行为integrationAddTheme.doc.mjsintegrationAddTheme(name: string, options?: IntegrationAddThemeOptions): PromiseIntegrationAddResponse // options: {cwd?: string, dryRun?: boolean}其中name必须是小写 kebab-case否则抛出ERR_INVALID_ARGUMENT。写入采用“先暂存、失败回滚”的策略任何一步写失败都会把已暂存文件全部回滚绝不留下半个主题已有文件一律拒绝覆盖ERR_FILE_EXISTS。注意脚手架生成的是最小占位主题。要做出真正的主题参考 CLI 自带的完整主题模板 packages/cli/assets/theme.template.ts——它覆盖defineTheme的每一个字段color、typography、radius、motion、tokens、components、adaptations等并解释了主题如何被消费用Theme theme{myTheme} modesystem包裹应用、自行加载命名字体、以及用astryx theme build为生产环境构建 CSS。四、安装后出现在theme list从内置目录到项目感知这是本次变更的消费端核心。旧的themeList()是同步的、只面向内置 bundle的兼容 API见 themeList.doc.mjs 与 list.mjsconst {data} themeList(); // 每个条目{slug, displayName, description, maintained}它不感知项目里装了哪些 integration。因此 CLI 命令现在绑定的是新增的、项目感知的themeListAvailable()见 themeListAvailable.doc.mjsthemeListAvailable(options?: {cwd?: string, package?: string}): PromiseThemeListResponse // 每个条目额外带 package 字段{slug, displayName, description, maintained, package}实现要点见 _adapter.mjslistAvailableThemes(cwd)通过Project.load(cwd)加载项目配置调用project.themes()得到“CLI 内置 catalog 每个已安装 integration 的 catalog”的合并结果若项目配置加载失败则降级为仅内置 catalog保留该命令在未配置项目中的历史可用性themeList()保持同步、仅内置的旧契约不变兼容既有程序化调用者。对应 CLI 命令theme-list.doc.mjs# 列出所有可用主题内置 已安装 integration 贡献 astryx theme list --json # 只看某个 owner 包贡献的主题 astryx theme list --package acme/themes输出中的package字段标明每个主题的归属内置主题的 owner 固定为astryxdesign/cli即BUNDLED_THEME_PACKAGEintegration 主题的 owner 是其所在的 npm 包名来自该包package.json的name。测试 list.test.mjs 验证了这条链路构造node_modules/acme/themes含astryx.integration.mjs与themes/manifest.json后themeListAvailable返回的主题中同时包含 owner 为astryxdesign/cli的内置主题。五、theme add按 owner 复制主题源码到你的项目astryx theme add是消费端最后一步把某个可用主题内置或 integration 贡献的的全部 catalog 列出文件复制进项目让你完全拥有这份源码。CLI 文档见 theme-add.doc.mjs实现见 add.mjs。命令行用法# 复制内置主题 astryx theme add matcha # 复制 integration 贡献的主题同一 slug 有多个 owner 时必须 --package astryx theme add ocean --package acme/themes # 覆盖已存在文件 astryx theme add ocean --package acme/themes -f # 等价于 --overwrite # 不带 slug 或加 --list列出可用主题 astryx theme add --list参数 / 选项说明slug要复制的主题 slug大小写不敏感匹配[path]目标目录必须解析到 cwd 内默认src/themes/slug-f, --overwrite覆盖已存在文件而不是拒绝--list列出所有可用主题等价于theme list--package package指定拥有该主题的包用于消解同名 slug 的歧义程序化 API 签名themeAdd.doc.mjsthemeAdd(slug: string, options?: { targetPath?: string, // 默认 src/themes/slug overwrite?: boolean, // 默认 false cwd?: string, // 用于 integration 发现与目标路径解析 package?: string, // 精确的 owner 包名 }): PromiseThemeAddResponse复制成功后的回执theme.add包含slug、displayName、maintained、packageowner、outputDir相对 cwd 的目标目录、entry、exportName、files。复制流程与安全保障themeAdd的执行链路add.mjs可以拆成五步解析主题findTheme(slug, {cwd, package})从可用主题中做大小写不敏感匹配找不到时抛出ERR_UNKNOWN_THEME并附带可用主题清单作为提示。同名 slug 失败关闭若多个包都提供同一 slug 且未指定--package抛出ERR_AMBIGUOUS_THEME并把每个候选者的slug --package owner列出来强制调用者显式选择 owner。路径安全目标目录与每个目标文件都经过assertWithin校验任何逃逸 cwd 的路径包括 POSIX/Windows 绝对路径与../.段都会以ERR_PATH_TRAVERSAL拒绝。测试 integration-themes.test.mjs 专门验证了“嵌套目标符号链接逃逸项目”会被拒绝且不会留下任何半成品文件。源文件完备性catalog 列出的每个文件都必须真实存在否则报ERR_NO_SOURCE并提示重新运行node scripts/generate-cli-themes.mjs重建内置 bundle。暂存后原子写入所有文件先写到临时文件dest.pid.tmp全部写完后统一 rename期间任何失败都会回滚已暂存文件保证不会留下写了一半的主题。写入时会通过stripCopyrightHeader去掉脚手架文件的仓库样板头避免把内部版权声明带进消费方代码树。错误码速查CLI 退出码 1 对应这些失败场景错误码触发条件ERR_UNKNOWN_THEME无主题匹配给定 slug 与 packageERR_AMBIGUOUS_THEME多个包拥有同一 slug 且未指定--packageERR_THEME_INVALID所选包存在阻塞性 integration 或主题 catalog 错误ERR_PATH_TRAVERSAL目标路径逃逸 cwdERR_NO_SOURCEcatalog 列出的源文件缺失ERR_FILE_EXISTS目标已存在且未设置overwriteERR_WRITE_FAILED写入失败已回滚另外当--package指定了一个已安装但 catalog 损坏的包时findTheme会优先报告该包的错误ERR_THEME_INVALID而不是静默回退到别的 owner见 _adapter.mjs 与对应测试。六、主题目录的深度校验integration 主题为何“可安全复制”integration 主题与内置主题共用同一个发现与校验入口discoverThemeCatalog但 integration 主题还会额外经过两道静态分析见 theme-discovery.mjs 的discoverIntegrationThemes本地模块图校验validateThemeModuleGraph用 jscodeshift 解析 entry 模块及其本地依赖收集所有ImportDeclaration、ExportNamedDeclaration、ExportAllDeclaration、动态import()等语句的说明符凡是以.开头的本地引用都必须能解析到 catalog 已列出、且被assertWithin限制在主题目录内的文件解析不到的模块引用会直接报错。导出存在性证明moduleExportsName在不执行模块的前提下静态证明 entry 确实导出了清单声明的exportName本地 ESM 再导出会被递归追踪纯类型声明interface / type / declare不计为运行时导出。这两道检查保证了一旦某个 integration 主题出现在theme list中它就是“自包含且可复制”的——theme add复制出来的文件集必然完整、可运行不会出现复制后发现缺了某个本地依赖文件的尴尬。这解释了为什么测试 integration-themes.test.mjs 中“删掉 catalog 里列出的tokens/colors.ts”会立刻让themeAdd以ERR_THEME_INVALID失败catalog 与磁盘不一致在发现阶段就会被拦截。七、端到端工作流总结把三块拼起来一个完整的“integration 主题分发与消费”闭环是作者侧在 integration 包内运行astryx integration add theme ocean或手写themes/manifest.json 主题源码确认astryx.integration.mjs中声明了themes用astryx doctor integration validate校验后发布。消费侧项目pnpm add acme/themes安装 integration然后astryx theme list就能看到它贡献的oceanowner 显示为acme/themes。落地astryx theme add ocean --package acme/themes把主题源码复制到src/themes/ocean/修改、astryx theme build src/theme.ts构建并用Theme theme{oceanTheme}应用到应用。这套机制的意义在于主题不再局限于 CLI 内置的几张皮——任何第三方包都可以按同一契约贡献源码主题并且内置主题与集成主题在list/add/build全链路中行为一致唯一的新增维度是packageowner字段带来的归属与消歧能力。后续想要深挖主题本身的编写可从仓库的 theme.template.ts、theme.doc.mjs 与主题包 packages/themes 继续深入。参考资料变更记录本文对应 packages/cli/CHANGELOG.md 中关于 integration 主题贡献的 patch 条目发现与校验packages/cli/foundation/discovery/theme-discovery.mjs主题 catalog 的 schema 校验、模块图验证、导出证明合并与降级packages/cli/api/theme/_adapter.mjslistThemes/listAvailableThemes/findTheme列表 API 与命令packages/cli/api/theme/list/list.mjs、packages/cli/clients/cli/commands/theme-list.doc.mjs复制 API 与命令packages/cli/api/theme/add/add.mjs、packages/cli/clients/cli/commands/theme-add.doc.mjs脚手架packages/cli/api/integration/add-theme.mjs、packages/cli/api/integration/integrationAddTheme.doc.mjs清单 schemapackages/cli/authoring/integration/integration.doc.mjs、packages/cli/authoring/integration/type.ts、packages/cli/authoring/integration/schema.mjs测试packages/cli/api/theme/integration-themes.test.mjs、packages/cli/api/theme/list/list.test.mjs内置主题 catalog 实物packages/cli/assets/templates/themes/manifest.json主题编写模板packages/cli/assets/theme.template.ts【免费下载链接】astryxAn open source design system thats fully customizable and agent ready项目地址: https://gitcode.com/GitHub_Trending/as/astryx创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表