SvelteKit 既能构建应用,也能通过 @sveltejs/package 构建组件库。npx sv create 提供了自动配置选项。
构建应用时,src/routes 面向用户,src/lib 是内部库。组件库的目录结构与应用相同,但面向使用者的是 src/lib,发布使用根目录的 package.json。src/routes 可以是随库提供的文档、演示站点,也可以只是开发时的试验场。
运行 svelte-package 后,会将 src/lib 内容处理到可配置的输出目录,默认 dist,其中包含:
- src/lib 内全部文件。Svelte 组件经过预处理,TypeScript 转为 JavaScript。
- 为 Svelte、JavaScript 和 TypeScript 生成的 .d.ts 类型声明。需要安装 TypeScript 4.0.0 或更新版本。声明放在实现文件旁边,手写声明原样复制。可以禁用生成,但强烈不建议,因为使用者可能依赖 TypeScript 类型。
@sveltejs/package 第 1 版会生成 package.json,现在改为使用项目现有文件并验证其正确性。仍在使用第 1 版时,请参阅原文链接的迁移 PR。
package.json 的结构
公开发布库时,package.json 更加重要。它配置入口、发布文件与依赖。下面介绍主要字段。
name
包名用于安装,并显示在 https://npmjs.com/package/<name>:
{
"name": "your-library"
}
license
每个包都应有 license 字段,让使用者了解授权方式。MIT 是常见且宽松的许可,允许分发与复用,但不提供担保:
{
"license": "MIT"
}
包中还应包含 LICENSE 文件。
files
指定 npm 打包上传的文件,应包含输出目录,默认 dist。package.json、README 和 LICENSE 始终会包含,无需额外列出:
{
"files": ["dist"]
}
可在 .npmignore 排除单元测试、仅供 src/routes 导入的模块等不必要文件,使包更小、安装更快。
exports
exports 指定包入口。通过 npx sv create 建库时,默认只导出包根路径:
{
"exports": {
".": {
"types": "./dist/index.d.ts",
"svelte": "./dist/index.js"
}
}
}
这告诉打包器和工具,库只有一个根入口,应从这里导入全部内容:
import { Something } from 'your-library';
types 与 svelte 是导出条件,指导工具解析:
- TypeScript 识别 types 并加载声明文件。不发布类型声明时,应省略它。
- 支持 Svelte 的工具识别 svelte,知道这是组件库。如果库不导出 Svelte 组件,且也适用于非 Svelte 项目,例如某些 store 库,可以改为 default。
旧版 @sveltejs/package 还会导出 package.json,现在模板不再包含,因为工具已能处理未显式导出的 package.json。
可以自定义 exports,提供更多入口。例如,不通过 src/lib/index.js 重新导出,而是直接暴露 src/lib/Foo.svelte:
{
"exports": {
"./Foo.svelte": {
"types": "./dist/Foo.svelte.d.ts",
"svelte": "./dist/Foo.svelte"
}
}
}
使用者可以这样导入:
import Foo from 'your-library/Foo.svelte';
如果发布类型声明,需要额外注意,详见后面的 TypeScript 部分。
一般而言,exports 的键就是用户导入时使用的路径,值则是目标文件路径,或包含这些路径的条件映射。
svelte
这是帮助工具识别 Svelte 组件库的旧字段。使用 svelte 导出条件后已不再必需,但为了兼容尚不认识条件导出的旧工具,保留它仍有价值。它应指向根入口:
{
"svelte": "./dist/index.js"
}
sideEffects
打包器用 sideEffects 判断模块是否含有副作用。导入模块时,若发生可被其他脚本观察到的变化,例如修改全局变量或内置对象原型,就属于副作用。由于可能影响应用其他部分,这些模块即使导出未被使用,也会保留在最终包中。尽量避免副作用是良好实践。
正确声明 sideEffects,可以让打包器更积极地执行 tree-shaking,删除未用导出,得到更小、更高效的包。不同打包器处理方式不同。Vite 虽不要求,但为兼容 webpack,建议将全部 CSS 声明为有副作用。新项目默认:
{
"sideEffects": ["**/*.css"]
}
如果库中的脚本有副作用,务必更新字段。新项目默认把所有脚本视为无副作用,标错可能破坏功能。
可以用数组明确指定:
{
"sideEffects": [
"**/*.css",
"./dist/sideEffectfulFile.js"
]
}
这样只有列出的文件会被视为有副作用。
TypeScript
即使自己不用 TypeScript,也应提供类型声明,让使用者获得正确的智能提示。@sveltejs/package 自动处理大部分生成工作,默认在打包时为 JavaScript、TypeScript 和 Svelte 生成声明。只需确保 exports 中的 types 指向正确文件。通过 CLI 创建项目时,根导出会自动配置。
如果有根入口以外的导出,例如 your-library/foo,就需要格外注意。TypeScript 默认不会按 { "./foo": { "types": "./dist/foo.d.ts", ... }} 解析,而是从库根目录寻找 foo.d.ts,即寻找 your-library/foo.d.ts,而不是 dist 下的文件。
有两种解决方案:
- 要求使用者在 tsconfig.json 或 jsconfig.json 中,将 moduleResolution 设为 bundler、node16 或 nodenext。bundler 从 TypeScript 5 开始提供,是推荐的未来选择。这样 TypeScript 会读取 exports 映射并正确解析。
- 利用 typesVersions 的路径映射。它原本根据 TypeScript 版本选择声明,也能用于建立子路径映射:
{
"exports": {
"./foo": {
"types": "./dist/foo.d.ts",
"svelte": "./dist/foo.js"
}
},
"typesVersions": {
">4.0": {
"foo": ["./dist/foo.d.ts"]
}
}
}
>4.0 表示使用的 TypeScript 大于 4 时读取内部映射,实际中通常成立。内部映射说明 your-library/foo 的类型位于 ./dist/foo.d.ts,本质上复现了 exports 条件。也可以用 * 通配符批量声明,避免重复。
一旦使用 typesVersions,就必须通过它声明所有类型导入,包括根导入,后者使用 "index.d.ts": [..]。
最佳实践
除非库只面向 SvelteKit 项目,否则避免使用 $app/env 等专有模块。例如,可用 import { BROWSER } from 'esm-env' 替代 import { browser } from '$app/env'。也可将当前 URL、导航操作等作为属性传入,避免直接依赖 $app/state、$app/navigation。这种通用设计也更容易搭配测试、UI 演示等工具。
库内别名应使用子路径导入。打包时,svelte-package 会根据 package.json 的 imports,将 # 前缀导入改写为相对路径。
仔细判断变更是修复、新功能还是破坏性变化,并相应更新版本。从已有库的 exports 删除路径或导出条件,都应视为破坏性变更:
{
"exports": {
".": {
"types": "./dist/index.d.ts",
// changing `svelte` to `default` is a breaking change:
"svelte": "./dist/index.js"
"default": "./dist/index.js"
},
// removing this is a breaking change:
"./foo": {
"types": "./dist/foo.d.ts",
"svelte": "./dist/foo.js",
"default": "./dist/foo.js"
},
// adding this is ok:
"./bar": {
"types": "./dist/bar.d.ts",
"svelte": "./dist/bar.js",
"default": "./dist/bar.js"
}
}
}
源映射
在 tsconfig.json 设置 "declarationMap": true,可以生成 .d.ts.map 声明映射,让 VS Code 等编辑器在“转到定义”时进入原始 .ts 或 .svelte 文件。
这也要求把源文件与 dist 一起发布,确保声明文件中的相对路径指向实际存在的文件。若库代码都在 CLI 推荐的 src/lib,只需将它加入 package.json 的 files:
{
"files": [
"dist",
"!dist/**/*.test.*",
"!dist/**/*.spec.*",
"src/lib",
"!src/lib/**/*.test.*",
"!src/lib/**/*.spec.*"
]
}
命令选项
svelte-package 支持:
-w / --watch:监视 src/lib 变化并重新构建。-i / --input:包含全部包文件的输入目录,默认 src/lib。-o / --output:处理后文件的输出目录,默认 dist。exports 应指向其中的文件,files 应包含该目录。-p / --preserve-output:打包前不删除输出目录。默认 false,即先清空。-t / --types:是否生成 .d.ts,默认 true。强烈建议启用,有助于生态中的库质量。--tsconfig:指定 tsconfig 或 jsconfig 路径;未提供时,向工作区路径的上层查找最近的配置。
发布
发布生成的包:
npm publish
注意事项
所有相对文件导入都必须遵循 Node ESM 算法,写出完整文件名与扩展名。例如 src/lib/something/index.js:
import { something } from './something/index.js';
使用 TypeScript 时,导入 .ts 文件也应按同样方式书写,但扩展名写 .js,而不是 .ts。这是 TypeScript 的设计决定。将 moduleResolution 设为 NodeNext,有助于发现相关问题。
除 Svelte 文件会预处理、TypeScript 会转为 JavaScript 外,其他文件都原样复制。
原文:Packaging。作者/维护者:SvelteKit 文档贡献者。本文为原文的中文译文;代码保留原文内容。











暂无评论内容