Skip to main content

打包

你可以使用 SvelteKit 构建应用以及组件库,使用 @sveltejs/package 包(npx sv create 有一个选项可以为你设置)。

¥You can use SvelteKit to build apps as well as component libraries, using the @sveltejs/package package (npx sv create has an option to set this up for you).

当你创建应用时,src/routes 的内容是面向公众的内容;src/lib 包含你的应用的内部库。

¥When you’re creating an app, the contents of src/routes is the public-facing stuff; src/lib contains your app’s internal library.

组件库具有与 SvelteKit 应用完全相同的结构,只是 src/lib 是面向公众的部分,而根 package.json 用于发布包。src/routes 可能是随库一起提供的文档或演示站点,也可能只是你在开发过程中使用的沙箱。

¥A component library has the exact same structure as a SvelteKit app, except that src/lib is the public-facing bit, and your root package.json is used to publish the package. src/routes might be a documentation or demo site that accompanies the library, or it might just be a sandbox you use during development.

@sveltejs/package 运行 svelte-package 命令将获取 src/lib 的内容并生成包含以下内容的 dist 目录(可以是 configured):

¥Running the svelte-package command from @sveltejs/package will take the contents of src/lib and generate a dist directory (which can be configured) containing the following:

  • src/lib 中的所有文件。Svelte 组件将被预处理,TypeScript 文件将被转换为 JavaScript。

    ¥All the files in src/lib. Svelte components will be preprocessed, TypeScript files will be transpiled to JavaScript.

  • 为 Svelte、JavaScript 和 TypeScript 文件生成的类型定义(d.ts 文件)。你需要为此安装 typescript >= 4.0.0。类型定义放在其实现旁边,手写的 d.ts 文件按原样复制。你可以使用 禁用生成,但我们强烈建议不要这样做 - 使用你的库的人可能会使用 TypeScript,他们需要这些类型定义文件。

    ¥Type definitions (d.ts files) which are generated for Svelte, JavaScript and TypeScript files. You need to install typescript >= 4.0.0 for this. Type definitions are placed next to their implementation, hand-written d.ts files are copied over as is. You can disable generation, but we strongly recommend against it — people using your library might use TypeScript, for which they require these type definition files.

@sveltejs/package 版本 1 生成了一个 package.json。现在情况不再如此,它将使用你项目中的 package.json 并验证它是否正确。如果你仍在使用版本 1,请参阅 此 PR 了解迁移说明。

¥[!NOTE] @sveltejs/package version 1 generated a package.json. This is no longer the case and it will now use the package.json from your project and validate that it is correct instead. If you’re still on version 1, see this PR for migration instructions.

package.json 的剖析(Anatomy of a package.json)

¥Anatomy of a package.json

由于你现在正在构建一个供公众使用的库,因此你的 package.json 的内容将变得更加重要。通过它,你可以配置包的入口点、哪些文件发布到 npm 以及你的库具有哪些依赖。让我们逐一介绍最重要的字段。

¥Since you’re now building a library for public use, the contents of your package.json will become more important. Through it, you configure the entry points of your package, which files are published to npm, and which dependencies your library has. Let’s go through the most important fields one by one.

name

这是你的包的名称。它将可供其他人使用该名称安装,并在 https://npmjs.com/package/<name> 上可见。

¥This is the name of your package. It will be available for others to install using that name, and visible on https://npmjs.com/package/<name>.

{
	"name": "your-library"
}

阅读有关它的更多信息 此处

¥Read more about it here.

license

每个包都应该有一个许可证字段,以便人们知道如何使用它。MIT 是一种非常流行的许可证,它在分发和重用方面也非常宽松,没有保证。

¥Every package should have a license field so people know how they are allowed to use it. A very popular license which is also very permissive in terms of distribution and reuse without warranty is MIT.

{
	"license": "MIT"
}

阅读有关它的更多信息 此处。请注意,你还应该在包中包含一个 LICENSE 文件。

¥Read more about it here. Note that you should also include a LICENSE file in your package.

files

这会告诉 npm 它将打包哪些文件并上传到 npm。它应该包含你的输出文件夹(默认为 dist)。你的 package.jsonREADMELICENSE 将始终包含在内,因此你无需指定它们。

¥This tells npm which files it will pack up and upload to npm. It should contain your output folder (dist by default). Your package.json and README and LICENSE will always be included, so you don’t need to specify them.

{
	"files": ["dist"]
}

要排除不必要的文件(例如单元测试或仅从 src/routes 导入的模块等),你可以将它们添加到 .npmignore 文件中。这将导致更小的软件包,安装速度更快。

¥To exclude unnecessary files (such as unit tests, or modules that are only imported from src/routes etc) you can add them to an .npmignore file. This will result in smaller packages that are faster to install.

阅读有关它的更多信息 此处

¥Read more about it here.

exports

"exports" 字段包含包的入口点。如果你通过 npx sv create 设置了一个新的库项目,它将被设置为单个导出,即包根:

¥The "exports" field contains the package’s entry points. If you set up a new library project through npx sv create, it’s set to a single export, the package root:

{
	"exports": {
		".": {
			"types": "./dist/index.d.ts",
			"svelte": "./dist/index.js"
		}
	}
}

这会告诉打包器和工具,你的包只有一个入口点,即根,所有内容都应该通过它导入,如下所示:

¥This tells bundlers and tooling that your package only has one entry point, the root, and everything should be imported through that, like this:

import { import SomethingSomething } from 'your-library';

typessvelte 的键是 导出条件。当它们查找 your-library 导入时,它们会告诉工具要导入哪个文件:

¥The types and svelte keys are export conditions. They tell tooling what file to import when they look up the your-library import:

  • TypeScript 看到 types 条件并查找类型定义文件。如果你不发布类型定义,请忽略此条件。

    ¥TypeScript sees the types condition and looks up the type definition file. If you don’t publish type definitions, omit this condition.

  • Svelte 感知工具可以看到 svelte 条件并知道这是一个 Svelte 组件库。如果你发布的库不导出任何 Svelte 组件,并且也可以在非 Svelte 项目中工作(例如 Svelte 存储库),则可以用 default 替换此条件。

    ¥Svelte-aware tooling sees the svelte condition and knows this is a Svelte component library. If you publish a library that does not export any Svelte components and that could also work in non-Svelte projects (for example a Svelte store library), you can replace this condition with default.

@sveltejs/package 的先前版本还添加了 package.json 导出。这不再是模板的一部分,因为现在所有工具都可以处理未明确导出的 package.json

¥[!NOTE] Previous versions of @sveltejs/package also added a package.json export. This is no longer part of the template because all tooling can now deal with a package.json not being explicitly exported.

你可以根据自己的喜好调整 exports 并提供更多入口点。例如,如果你想要直接公开 src/lib/Foo.svelte 组件,而不是重新导出组件的 src/lib/index.js 文件,则可以创建以下导出映射...

¥You can adjust exports to your liking and provide more entry points. For example, if instead of a src/lib/index.js file that re-exported components you wanted to expose a src/lib/Foo.svelte component directly, you could create the following export map...

{
	"exports": {
		"./Foo.svelte": {
			"types": "./dist/Foo.svelte.d.ts",
			"svelte": "./dist/Foo.svelte"
		}
	}
}

...并且你的库的消费者可以像这样导入组件:

¥...and a consumer of your library could import the component like so:

import module "your-library/Foo.svelte"Foo from 'your-library/Foo.svelte';

请注意,如果你提供类型定义,则执行此操作将需要额外注意。阅读有关警告 此处 的更多信息

¥[!NOTE] Beware that doing this will need additional care if you provide type definitions. Read more about the caveat here

一般来说,导出映射的每个键都是用户必须用来从包中导入某些内容的路径,值是将要导入的文件的路径或导出条件映射,而导出条件映射又包含这些文件路径。

¥In general, each key of the exports map is the path the user will have to use to import something from your package, and the value is the path to the file that will be imported or a map of export conditions which in turn contains these file paths.

阅读有关 exports 此处 的更多信息。

¥Read more about exports here.

svelte

这是一个遗留字段,使工具能够识别 Svelte 组件库。使用 svelte 导出条件 时不再需要它,但为了向后兼容尚不知道导出条件的过时工具,最好保留它。它应该指向你的根入口点。

¥This is a legacy field that enabled tooling to recognise Svelte component libraries. It’s no longer necessary when using the svelte export condition, but for backwards compatibility with outdated tooling that doesn’t yet know about export conditions it’s good to keep it around. It should point towards your root entry point.

{
	"svelte": "./dist/index.js"
}

sideEffects

package.json 中的 sideEffects 字段由打包器用于确定模块是否可能包含具有副作用的代码。如果模块在导入时所做的更改可从模块外部的其他脚本观察到,则该模块被认为具有副作用。例如,副作用包括修改全局变量或内置 JavaScript 对象的原型。由于副作用可能会影响应用其他部分的行为,因此无论应用中是否使用它们的导出,这些文件/模块都将包含在最终包中。避免代码中的副作用是一种最佳实践。

¥The sideEffects field in package.json is used by bundlers to determine if a module may contain code that has side effects. A module is considered to have side effects if it makes changes that are observable from other scripts outside the module when it’s imported. For example, side effects include modifying global variables or the prototype of built-in JavaScript objects. Because a side effect could potentially affect the behavior of other parts of the application, these files/modules will be included in the final bundle regardless of whether their exports are used in the application. It is a best practice to avoid side effects in your code.

package.json 中设置 sideEffects 字段可以帮助打包器更积极地从最终打包包中消除未使用的导出,这一过程称为 tree-shaking。这会导致更小、更高效的包。不同的打包器以不同的方式处理 sideEffects。虽然对于 Vite 来说不是必需的,但我们建议库声明所有 CSS 文件都有副作用,以便你的库将是 与 webpack 兼容。这是新创建的项目附带的配置:

¥Setting the sideEffects field in package.json can help the bundler to be more aggressive in eliminating unused exports from the final bundle, a process known as tree-shaking. This results in smaller and more efficient bundles. Different bundlers handle sideEffects in various manners. While not necessary for Vite, we recommend that libraries state that all CSS files have side effects so that your library will be compatible with webpack. This is the configuration that comes with newly created projects:

package
{
	"sideEffects": ["**/*.css"]
}

如果库中的脚本有副作用,请确保更新 sideEffects 字段。在新建的项目中,所有脚本默认标记为无副作用。如果将具有副作用的文件错误地标记为没有副作用,则可能导致功能中断。

¥If the scripts in your library have side effects, ensure that you update the sideEffects field. All scripts are marked as side effect free by default in newly created projects. If a file with side effects is incorrectly marked as having no side effects, it can result in broken functionality.

如果你的包中有具有副作用的文件,你可以在数组中指定它们:

¥If your package has files with side effects, you can specify them in an array:

package
{
	"sideEffects": [
		"**/*.css",
		"./dist/sideEffectfulFile.js"
	]
}

这将仅将指定的文件视为具有副作用。

¥This will treat only the specified files as having side effects.

TypeScript

即使你自己不使用 TypeScript,你也应该为你的库提供类型定义,以便使用你的库的人获得正确的智能感知。@sveltejs/package 使生成类型的过程对你来说大多不透明。默认情况下,在打包库时,会自动为 JavaScript、TypeScript 和 Svelte 文件生成类型定义。你需要确保的是 exports 映射中的 types 条件指向正确的文件。通过 npx sv create 初始化库项目时,将自动为根导出设置。

¥You should ship type definitions for your library even if you don’t use TypeScript yourself so that people who do get proper intellisense when using your library. @sveltejs/package makes the process of generating types mostly opaque to you. By default, when packaging your library, type definitions are auto-generated for JavaScript, TypeScript and Svelte files. All you need to ensure is that the types condition in the exports map points to the correct files. When initialising a library project through npx sv create, this is automatically setup for the root export.

但是,如果你有除根导出之外的其他内容(例如提供 your-library/foo 导入),则需要特别注意提供类型定义。不幸的是,TypeScript 默认不会解决像 { "./foo": { "types": "./dist/foo.d.ts", ... }} 这样的导出的 types 条件。相反,它将搜索相对于库根的 foo.d.ts(即 your-library/foo.d.ts 而不是 your-library/dist/foo.d.ts)。要解决此问题,你有两个选择:

¥If you have something else than a root export however — for example providing a your-library/foo import — you need to take additional care for providing type definitions. Unfortunately, TypeScript by default will not resolve the types condition for an export like { "./foo": { "types": "./dist/foo.d.ts", ... }}. Instead, it will search for a foo.d.ts relative to the root of your library (i.e. your-library/foo.d.ts instead of your-library/dist/foo.d.ts). To fix this, you have two options:

第一个选项是要求使用你的库的人将他们的 tsconfig.json(或 jsconfig.json)中的 moduleResolution 选项设置为 bundler(自 TypeScript 5 开始可用,这是未来最佳和推荐的选项)、node16nodenext。这选择 TypeScript 实际查看导出映射并正确解析类型。

¥The first option is to require people using your library to set the moduleResolution option in their tsconfig.json (or jsconfig.json) to bundler (available since TypeScript 5, the best and recommended option in the future), node16 or nodenext. This opts TypeScript into actually looking at the exports map and resolving the types correctly.

第二种选择是(滥用)使用 TypeScript 中的 typesVersions 功能来连接类型。这是 package.json 中的一个字段,TypeScript 使用它根据 TypeScript 版本检查不同的类型定义,并且还包含一个路径映射功能。我们利用该路径映射功能来获得我们想要的东西。对于上面提到的 foo 导出,相应的 typesVersions 如下所示:

¥The second option is to (ab)use the typesVersions feature from TypeScript to wire up the types. This is a field inside package.json TypeScript uses to check for different type definitions depending on the TypeScript version, and also contains a path mapping feature for that. We leverage that path mapping feature to get what we want. For the mentioned foo export above, the corresponding typesVersions looks like this:

{
	"exports": {
		"./foo": {
			"types": "./dist/foo.d.ts",
			"svelte": "./dist/foo.js"
		}
	},
	"typesVersions": {
		">4.0": {
			"foo": ["./dist/foo.d.ts"]
		}
	}
}

>4.0 告诉 TypeScript 检查内部映射,如果使用的 TypeScript 版本大于 4(实际上应该始终如此)。内部映射告诉 TypeScript,your-library/foo 的类型在 ./dist/foo.d.ts 中找到,这基本上复制了 exports 条件。你还可以使用 * 作为通配符,以便一次提供许多类型定义而无需重复。请注意,如果你选择加入 typesVersions,则必须通过它声明所有类型导入,包括根导入(定义为 "index.d.ts": [..])。

¥>4.0 tells TypeScript to check the inner map if the used TypeScript version is greater than 4 (which should in practice always be true). The inner map tells TypeScript that the typings for your-library/foo are found within ./dist/foo.d.ts, which essentially replicates the exports condition. You also have * as a wildcard at your disposal to make many type definitions at once available without repeating yourself. Note that if you opt into typesVersions you have to declare all type imports through it, including the root import (which is defined as "index.d.ts": [..]).

你可以阅读有关该功能 此处 的更多信息。

¥You can read more about that feature here.

最佳实践(Best practices)

¥Best practices

你应该避免在你的软件包中使用 SvelteKit 特定的模块(如 $app/environment),除非你打算让它们仅供其他 SvelteKit 项目使用。例如你可以使用 import { BROWSER } from 'esm-env'参见 esm-env 文档),而不是使用 import { browser } from '$app/environment'。你可能还希望将当前 URL 或导航操作等内容作为 prop 传递,而不是直接依赖 $app/state$app/navigation 等。以这种更通用的方式编写应用还将使设置测试、UI 演示等工具变得更加容易。

¥You should avoid using SvelteKit-specific modules like $app/environment in your packages unless you intend for them to only be consumable by other SvelteKit projects. E.g. rather than using import { browser } from '$app/environment' you could use import { BROWSER } from 'esm-env' (see esm-env docs). You may also wish to pass in things like the current URL or a navigation action as a prop rather than relying directly on $app/state, $app/navigation, etc. Writing your app in this more generic fashion will also make it easier to setup tools for testing, UI demos and so on.

确保通过 svelte.config.js(而不是 vite.config.jstsconfig.json)添加 aliases,以便它们由 svelte-package 处理。

¥Ensure that you add aliases via svelte.config.js (not vite.config.js or tsconfig.json), so that they are processed by svelte-package.

你应该仔细考虑对软件包所做的更改是错误修复、新功能还是重大更改,并相应地更新软件包版本。请注意,如果你从现有库中删除 exports 中的任何路径或其中的任何 export 条件,则应将其视为重大更改。

¥You should think carefully about whether or not the changes you make to your package are a bug fix, a new feature, or a breaking change, and update the package version accordingly. Note that if you remove any paths from exports or any export conditions inside them from your existing library, that should be regarded as a breaking change.

{
	"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"
		}
	}
}

源映射(Source maps)

¥Source maps

你可以通过在 tsconfig.json 中设置 "declarationMap": true 来创建所谓的声明映射(d.ts.map 文件)。这将允许 VS Code 等编辑器在使用“转到定义”等功能时转到原始 .ts.svelte 文件。这意味着你还需要将源文件与 dist 文件夹一起发布,以便声明文件内的相对路径指向磁盘上的文件。假设你拥有 Svelte CLI 建议的所有库代码都在 src/lib 中,这就像在 package.json 中将 src/lib 添加到 files 一样简单:

¥You can create so-called declaration maps (d.ts.map files) by setting "declarationMap": true in your tsconfig.json. This will allow editors such as VS Code to go to the original .ts or .svelte file when using features like Go to Definition. This means you also need to publish your source files alongside your dist folder in a way that the relative path inside the declaration files leads to a file on disk. Assuming that you have all your library code inside src/lib as suggested by Svelte’s CLI, this is as simple as adding src/lib to files in your package.json:

{
	"files": [
		"dist",
		"!dist/**/*.test.*",
		"!dist/**/*.spec.*",
		"src/lib",
		"!src/lib/**/*.test.*",
		"!src/lib/**/*.spec.*"
	]
}

选项(Options)

¥Options

svelte-package 接受以下选项:

¥svelte-package accepts the following options:

  • -w / --watch — 监视 src/lib 中的文件是否有变化并重建包

    ¥-w / --watch — watch files in src/lib for changes and rebuild the package

  • -i / --input — 包含包的所有文件的输入目录。默认为 src/lib

    ¥-i / --input — the input directory which contains all the files of the package. Defaults to src/lib

  • -o / --output — 处理后的文件写入的输出目录。你的 package.jsonexports 应该指向里面的文件,并且 files 数组应该包含该文件夹。默认为 dist

    ¥-o / --output — the output directory where the processed files are written to. Your package.json’s exports should point to files inside there, and the files array should include that folder. Defaults to dist

  • -t / --types — 是否创建类型定义(d.ts 文件)。我们强烈建议这样做,因为它可以提高生态系统库的质量。默认为 true

    ¥-t / --types — whether or not to create type definitions (d.ts files). We strongly recommend doing this as it fosters ecosystem library quality. Defaults to true

  • --tsconfig - tsconfig 或 jsconfig 的路径。未提供时,在工作区路径中搜索下一个上层 tsconfig/jsconfig。

    ¥--tsconfig - the path to a tsconfig or jsconfig. When not provided, searches for the next upper tsconfig/jsconfig in the workspace path.

发布(Publishing)

¥Publishing

要发布生成的包:

¥To publish the generated package:

npm publish

注意事项(Caveats)

¥Caveats

所有相关文件导入都需要完全指定,并遵守 Node 的 ESM 算法。这意味着对于像 src/lib/something/index.js 这样的文件,你必须包含带有扩展名的文件名:

¥All relative file imports need to be fully specified, adhering to Node’s ESM algorithm. This means that for a file like src/lib/something/index.js, you must include the filename with the extension:

import { import somethingsomething } from './something/index.js';

如果你使用的是 TypeScript,则需要以相同的方式导入 .ts 文件,但使用 .js 文件结尾,而不是 .ts 文件结尾。(这是我们无法控制的 TypeScript 设计决策。)在你的 tsconfig.jsonjsconfig.json 中设置 "moduleResolution": "NodeNext" 将帮助你实现这一点。

¥If you are using TypeScript, you need to import .ts files the same way, but using a .js file ending, not a .ts file ending. (This is a TypeScript design decision outside our control.) Setting "moduleResolution": "NodeNext" in your tsconfig.json or jsconfig.json will help you with this.

除 Svelte 文件(预处理)和 TypeScript 文件(转换为 JavaScript)之外的所有文件都按原样复制。

¥All files except Svelte files (preprocessed) and TypeScript files (transpiled to JavaScript) are copied across as-is.

上一页 下一页