Skip to main content
每个商店接受的都是已构建扩展的 zip,并且 manifest.json 要位于压缩包根目录。当上传内容经过压缩或打包时,Firefox Add-ons 还会要求一份源码 zip,而生产构建正是这种情况。Safari 是例外:扩展随 Xcode 构建的 macOS 或 iOS 应用一起发布。本页解释生成压缩包的三种方式,列出 extension build --zip 实际写出的文件名,并说明每个商店接受什么。本页还会引用 CLI 打包时打印的控制台输出。

生成压缩包的三种方式

手动压缩 dist/<browser>。 运行 extension build --browser=chrome,打开 dist/chrome,选中其中的内容并压缩。陷阱在于文件夹本身:如果压缩的是 chrome 文件夹,manifest.json 就会下沉一层,商店会拒绝。 extension build --zip。 构建写出 dist/<browser>,然后在该文件夹旁边打包,绝不放在它里面。source map 不会进入压缩包。加上 --zip-source 即可得到 AMO 要求的源码压缩包。 CI 任务。 同一条 extension build --zip 命令在 runner 上运行,任务把 dist/*.zip 作为构建产物上传,或交给商店 API。商店凭据留在 CI secret 里。参见 CI 模板。 手动路线用一次就好,用来了解商店期望什么。之后的每次发布都用 --zip,当不止一个人负责发布时,把同一条命令搬进 CI。

命令片段

这些标志属于 build:
如果你希望每次 build 都不加标志就打包,同样的选项也可以放进 extension.config.js:
extension.config.js
有了这个文件,extension build --browser=chrome 会写出 dist/store-chrome.zip 和 dist/store-source.zip。

构建写出了什么

每个压缩包都落在 dist/ 里,与 dist/<browser> 文件夹并列。不传 --zip-filename 时,文件名是 manifest 的 name 转小写并去掉 a-z0-9 和空格之外的所有字符,再接 manifest 的 version,再接浏览器。一个名为 zip-probe、版本 1.0.0 的 manifest 会打包成 zipprobe-1.0.0-chrome.zip:连字符没了,所以请从构建输出里读路径,而不是自己拼。以下运行使用的是 Extension.js 4.1.31: 从表里可以得出两条规则。默认浏览器是 chromium,所以目标是 Chrome Web Store 时要传 --browser=chrome。另外,单独的 --zip-filename 永远不会写源码压缩包:写它的是 --zip-source。此时显式名称同时管两个文件,发行压缩包后面追加浏览器,另一个追加 -source。 发行压缩包装的是 dist/<browser> 的内容去掉 .map 文件,manifest.json 位于根目录。源码压缩包装的是项目文件夹去掉 node_modules、.git、dist、extensions/ 伴随扩展文件夹、除 *.example 之外的所有 .env* 文件,以及你的 .gitignore 排除的内容。符号链接会被跳过并给出警告,因为压缩包存储的是文件。 在 --output json 下,每个压缩包都会列在 zip_artifacts 里,带有 kind(dist 或 source)、path 和以字节计的 size,所以 CI 步骤不必解析控制台就能拿到文件。

各浏览器差异

Firefox 自行分发是商店唯一会把文件交还给你的情况。AMO 会对上传内容签名,签名副本可从你的提交页面下载时会通过邮件通知你。Chrome 和 Edge 永远不会返回 .crx,Extension.js 也不会写出 .crx 或 .xpi 文件:商店接受的是 zip。 Opera 的商店拒绝压缩过的代码,所以 extension build --browser=opera 默认关闭 --minify。

你会看到的控制台输出

把你看到的那一行复制到搜索里。每一行对应一个原因。 Packaged dist/zipprobe-1.0.0-chrome.zip (34.2 KB). 发行压缩包已写出。该路径就是要上传的文件,由于上面的清洗规则,它的名字可能与你的 manifest name 不一致。 Packaged dist/zipprobe-1.0.0-source.zip (37.2 KB). 源码压缩包已写出。同一次运行里它打印在发行压缩包之前。构建多个浏览器时每个浏览器打印一次,因为源码对每个浏览器都是一样的。 Skipped the addons.mozilla.org lint: addons-linter is not installed. Install it with: npm install -D addons-linter or pass --no-addon-lint to silence this. Firefox 构建完成但没有做 AMO 检查。zip 是完整的。安装 addons-linter 可以在上传前看到 AMO 会标记什么,或者传 --no-addon-lint 让这一行不再出现。 Edge Add-ons refuses a package whose manifest carries key, so the edge production build dropped it. Edge 构建移除了顶层的 key 字段。Partner Center 会分配扩展 ID,所以这个字段在该包里没有用处。如果它只用于 Chrome Web Store 的构建,写成 chrome:key。 default_locale is set, but the _locales folder is missing. manifest 检查在打包前停止了构建。商店会拒绝缺少默认语言环境的包,所以请恢复 _locales/<default>/messages.json 再重新构建。 The source zip skipped a symlink, because an archive stores files. 项目里的一个符号链接被排除在源码压缩包之外。如果压缩包需要它,把链接指向的内容复制进项目。 The source zip was requested and not created. 构建完成了,但源码压缩包没有生成。这一行会给出路径和原因。发行压缩包不受影响,因为两个压缩包各自独立失败。

Extension.js 的做法

用命令打包,而不是用文件管理器:
  • 为每个商店运行 extension build --browser=<browser> --zip,商店是 Firefox Add-ons 时再加 --zip-source。
  • 在 CI 里传 --zip-filename=release,让产物名称稳定,并让 CLI 追加浏览器。
  • 从 Packaged 那一行或 --output json 下的 zip_artifacts 读压缩包路径,永远不要从 manifest 的 name 推算。
  • 让 dist/ 远离 git。构建每次运行都会清理 dist/<browser>。之前运行留下的压缩包绝不会进入下一个源码压缩包。
  • 在第一次上传前写好 STORE.md,让审核者备注和 AMO 构建说明随代码一起走。
构建就是打包步骤。没有单独的 pack 命令,extension publish 是 extension.dev 上的分享链接,不是商店提交。参见 Publish 命令。

另请参阅