跳至内容

Package.js

包是一个包含 package.js 文件的目录,该文件包含大约三个主要部分:基本描述、包定义和测试定义。默认情况下,目录名称是包的名称。

下面的 package.js 文件是一个如何使用打包 API 的示例。本节的其余部分将更详细地解释特定的 API 命令。

js
// Information about this package:
Package.describe({
  // Short two-sentence summary
  summary: 'What this does',
  // Version number
  version: '1.0.0',
  // Optional, default is package directory name
  name: 'username:package-name',
  // Optional GitHub URL to your source repository
  git: 'https://github.com/something/something.git'
});

// This defines your actual package:
Package.onUse((api) => {
  // If no version is specified for an `api.use` dependency, use the one defined
  // in Meteor 1.12.1.
  api.versionsFrom('1.12.1');
  // Use the `underscore` package, but only on the server. Version not
  // specified, so it will be as of Meteor 1.12.1.
  api.use('underscore', 'server');
  // Use `ostrio:flow-router-extra`, version 3.9.0 or newer.
  api.use('ostrio:[email protected]');
  // Give users of this package access to active-route's JavaScript helpers.
  api.imply('zimme:[email protected]')
  // Export the object `Email` to packages or apps that use this package.
  api.export('Email', 'server');
  // Specify the source code for the package.
  api.addFiles('email.js', 'server');
  // When using `ecmascript` or `modules` packages, you can use this instead of
  // `api.export` and `api.addFiles`.
  api.mainModule('email.js', 'server');
});

// This defines the tests for the package:
Package.onTest((api) => {
  // Sets up a dependency on this package.
  api.use('username:package-name');
  // Use the Mocha test framework.
  api.use('practicalmeteor:[email protected]_6');
  // Specify the source code for the package tests.
  api.addFiles('email_tests.js', 'server');
});

// This lets you use npm packages in your package:
Npm.depends({
  simplesmtp: '0.3.10',
  'stream-buffers': '0.2.5'
});

api.mainModulemodules 部分有说明。

构建插件是用 Package.registerBuildPlugin 创建的。请参阅 coffeescript 包以获取示例。构建插件本身就是功能齐全的 Meteor 程序,并且拥有自己的命名空间、包依赖项、源文件和 npm 需求。

您可以使用本地包为您的应用程序定义自定义构建插件,但有一个需要注意的地方。在已发布的包中,构建插件已与其传递依赖项捆绑在一起。因此,如果您希望构建插件的依赖项由本地包满足,则必须使用定义该插件的包的本地副本(即使您没有对该包进行任何更改),以便 Meteor 会获取本地依赖项。

在包的生命周期中,可能会因各种原因而结束开发,或者被取代。无论哪种情况,Meteor 都允许您通过将弃用标志设置为 true:deprecated: true 来轻松通知包的用户。此外,您用一个字符串替换它,告诉用户在哪里可以找到替换或该做什么。

使用 Package.describe(options) 提供基本包信息。要发布包,您必须定义 summaryversion

api.describe

仅限 package.js

摘要

提供基本包信息。

参数

源代码
名称类型描述必需
options对象

选项

名称类型描述必需
summary字符串

包的简明 1-2 句描述,发布时需要。

version字符串

您包的(扩展)semver 版本。此外,Meteor 允许使用包装编号:版本号后面的正整数。如果您正在移植使用 semver 版本控制的另一个包,您可能希望使用原始版本,后缀为 _wrapnumber。例如,1.2.3_12.4.5-rc1_4。包装编号排序在原始编号之后:1.2.3 < 1.2.3_1 < 1.2.3_2 < 1.2.4-rc.0。如果未指定版本,则此字段默认为 0.0.0。如果您想将您的包发布到包服务器,则必须指定一个版本。

name字符串

可选名称覆盖。默认情况下,包名称来自其目录的名称。

git字符串

源代码库的可选 Git URL。

documentation字符串

可选文档文件路径。默认设置为 'README.md'。将其设置为 null 以不提交任何文档。

debugOnly布尔值

将此标志设置为 true 的包不会捆绑到生产版本中。这对于仅供开发使用的包很有用。

prodOnly布尔值

将此标志设置为 true 的包将仅捆绑到生产版本中。

testOnly布尔值

将此标志设置为 true 的包将仅作为 meteor test 的一部分捆绑。

deprecated布尔值

一个标志,用于将包标记为已弃用。提供字符串以覆盖默认消息。

js

// api is an instance of PackageNamespace

const result = api.describe();
  options
);

使用 Package.onUse 处理程序定义依赖项并公开包方法。在本节中,您可以定义您的包依赖于哪些包、您的包隐含了哪些包以及您的包导出到哪个对象。

api.onUse

仅限 package.js

摘要

定义包依赖项并公开包方法。

参数

源代码
名称类型描述必需
func函数

一个函数,它接收包控制 api 对象,该对象跟踪依赖项和导出。

js

// api is an instance of PackageNamespace

const result = api.onUse();
  () => {}
);

api.versionsFrom

仅限 package.js

摘要

使用某个发行版中的核心包版本。除非提供,否则所有包都将默认为与 meteorRelease 一起发布的版本。这将使您无需弄清楚要使用的核心包的确切版本。例如,如果 Meteor 的最新版本是 [email protected],并且它包含 [email protected],则您可以在包中编写 api.versionsFrom('[email protected]'),当您稍后编写 api.use('jquery') 时,它将等效于 api.use('[email protected]')。您可以指定多个发行版的数组,在这种情况下,约束的默认值将是每个发行版中版本的“或”:api.versionsFrom(['[email protected]', '[email protected]']) 可能会导致 api.use('jquery') 被解释为 api.use('[email protected] || 2.0.0')

参数

源代码
名称类型描述必需
meteorRelease字符串或数组.<字符串>

发行版的规范:track@version。如果使用默认发行版 track METEOR,则只需 'version'(例如 "0.9.0")就足够了。可以是规范的数组。

js

// api is an instance of PackageAPI

const result = api.versionsFrom();
  "meteorRelease"
);

谨慎选择 Meteor 版本。首先确定包中使用的 API 所需的 Meteor 的最低版本。这应基于包的特定需求,例如需要 *Async 调用,这需要最低版本至少为 2.8。另一个例子是包发生了主要版本更新,例如 Meteor 2.3 中的账户包就发生了这种情况。如果您希望向后和向前兼容,最好在数组中包含 2.3 之前的 Meteor 版本和 2.3.6。对于大多数账户包的兼容性(除非您需要在 Meteor 2.3 中受影响的 API),一般建议在 versionsFrom 中包含以下数组:['1.12.1', '2.3.6', '2.8.1'],这给了我们最广泛的范围。对于一般包,您可以省略版本 2.3.6。如果您希望获得最广泛的兼容性范围,建议最低版本为 1.12.1,并且还包括 Meteor 当前版本附近的另一个版本。

api.use

仅限 package.js

摘要

依赖于包 packagename

参数

源代码
名称类型描述必需
packageNames字符串或数组.<字符串>

正在依赖的包。包名称可能后缀有 @version 标签。

通常,您必须指定包的版本(例如,'[email protected]' 以使用版本 1.0.0 或更高兼容版本(例如:1.0.1、1.5.0 等)的 accounts 包)。如果您使用 versionsFrom 从 Meteor 发行版中获取核心包,则可以省略核心包的版本名称。您还可以指定约束,例如 my:forms@=1.0.0(此包要求 my:forms 准确为 1.0.0),或 my:[email protected] || =2.0.1my:forms1.x.y,或准确为 2.0.1)。

architecture字符串或数组.<字符串>

如果您只在服务器端(或客户端)使用该包,则可以传递第二个参数(例如,'server''client''web.browser''web.cordova')以指定使用该包的体系结构。您可以通过传递数组来指定多个体系结构,例如 ['web.cordova', 'os.linux']

options对象

选项

名称类型描述必需
weak布尔值

建立对包的弱依赖。如果包 A 对包 B 有弱依赖,则意味着在应用程序中包含 A 不会强制包含 B——但是,如果 B 由其他包包含,则 B 将在 A 之前加载。您可以使用它来创建可选地与其他包集成或增强其他包的包(如果这些包存在)。当您弱依赖于一个包时,您看不到它的导出。您可以通过查看 Package.foo 是否存在来检测可能存在的弱依赖包是否存在,并从相同的位置获取其导出。

unordered布尔值

可以加载此依赖项,然后再加载您的包。(通常,由 api.use 指定的依赖项会在您的包之前加载。)您可以使用此选项来打破循环依赖。

js

// api is an instance of PackageAPI

const result = api.use();
  "packageNames",
"architecture", // this param is optional

options, // this param is optional
);

api.imply

仅限 package.js

摘要

允许此包的用户访问另一个包(通过传递字符串 packagename)或一组包(通过传递字符串数组 [packagename1packagename2]

参数

源代码
名称类型描述必需
packageNames字符串或数组.<字符串>

包的名称,或包名称的数组,每个包名称都有一个可选的 @version 组件。

architecture字符串或数组.<字符串>

如果您只在服务器端(或客户端)使用该包,则可以传递第二个参数(例如,'server''client''web.browser''web.cordova')以指定使用该包的体系结构。您可以通过传递数组来指定多个体系结构,例如 ['web.cordova', 'os.linux']

js

// api is an instance of PackageAPI

const result = api.imply();
  "packageNames",
"architecture", // this param is optional
);

api.export

仅限 package.js

摘要

在您的软件包中导出包级变量。指定的变量(在源代码中未声明为var)将可供使用您的软件包的软件包使用。如果您的软件包在调用Package.describe()时将debugOnlyprodOnlytestOnly选项设置为true,则使用您的软件包的软件包需要使用Package["package-name"].ExportedVariableName来访问导出变量的值。

参数

源代码
名称类型描述必需
exportedObjects字符串或数组.<字符串>

要导出的对象名称,或对象名称数组。

architecture字符串或数组.<字符串>

如果您只想在服务器(或客户端)上导出对象,则可以传递第二个参数(例如,“server”、“client”、“web.browser”、“web.cordova”)以指定导出使用的体系结构。您可以通过传递数组来指定多个体系结构,例如['web.cordova', 'os.linux']

exportOptions对象----
exportOptions.testOnly布尔值

如果为 true,则仅在运行此软件包的测试时导出此符号。

js

// api is an instance of PackageAPI

const result = api.export();
  "exportedObjects",
"architecture", // this param is optional

exportOptions, // this param is optional

false,
);

api.addFiles

仅限 package.js

摘要

指定软件包的源代码文件。

参数

源代码
名称类型描述必需
filenames字符串或数组.<字符串>

源文件的路径。

architecture字符串或数组.<字符串>

如果您只想在服务器(或客户端)上使用该文件,则可以传递此参数(例如,“server”、“legacy”、“client”、“web.browser”、“web.cordova”)以指定文件使用的体系结构。您可以在 package.js 配置文件中调用 api.addFiles(files, "legacy") 将额外文件添加到旧版 bundle 中,或调用 api.addFiles(files, "client") 将文件添加到所有客户端 bundle 中,或调用 api.addFiles(files, "web.browser") 仅将文件添加到现代 bundle 中。您可以通过传递数组来指定多个体系结构,例如['web.cordova', 'os.linux']。默认情况下,文件将在服务器和客户端上加载。

options对象

将传递给构建插件的选项。

选项

名称类型描述必需
bare布尔值

如果此文件是 JavaScript 代码或将由构建插件编译成 JavaScript 代码,则不要将结果文件包装在闭包中。与将文件放入应用程序的client/compatibility目录的效果相同。

js

// api is an instance of PackageAPI

const result = api.addFiles();
  "filenames",
"architecture", // this param is optional

options, // this param is optional
);

api.addAssets

仅限 package.js

摘要

指定软件包的资产文件。可以从服务器通过Assets API访问它们,或者从客户端通过 URL /packages/username_package-name/file-name 访问它们,具体取决于传递的体系结构。

参数

源代码
名称类型描述必需
filenames字符串或数组.<字符串>

资产文件的路径。

architecture字符串或数组.<字符串>

指定此资产应在何处可用(例如,“server”、“client”、“web.browser”、“web.cordova”)。您可以通过传递数组来指定多个体系结构,例如['web.cordova', 'os.linux']

js

// api is an instance of PackageAPI

const result = api.addAssets();
  "filenames",
"architecture",
);

使用Package.onTest处理程序设置您的测试,该处理程序的接口与onUse处理程序的接口并行。测试需要依赖于您刚刚创建的软件包。例如,如果您的软件包是email软件包,则必须调用api.use('email')才能测试该软件包。

如果您使用meteor create设置了软件包,Meteor 将在package.js中创建所需的脚手架,您只需在创建的_test.js文件中添加单元测试代码即可。

api.onTest

仅限 package.js

摘要

定义依赖项并公开单元测试的软件包方法。

参数

源代码
名称类型描述必需
func函数

一个函数,它接收软件包控制“api”对象,该对象跟踪依赖项和导出。

js

// api is an instance of PackageNamespace

const result = api.onTest();
  () => {}
);

Meteor 软件包可以通过在package.js文件中使用Npm.dependsCordova.depends来包含 NPM 软件包和 Cordova 插件。

api.depends

仅限 package.js

摘要

指定您的 Meteor 软件包依赖哪些NPM软件包。

参数

源代码
名称类型描述必需
dependencies对象

一个对象,其中键是软件包名称,值是以下之一

  1. 字符串形式的版本号
  2. npm 软件包的 http(s) URL
  3. 此处描述的 Git URL 格式here

Https URL 示例

Npm.depends({
  moment: "2.8.3",
  async: "https://github.com/caolan/async/archive/71fa2638973dafd8761fa5457c472a312cc820fe.tar.gz"
});

Git URL 示例

Npm.depends({
  moment: "2.8.3",
  async: "git+https://github.com/caolan/async#master"
});
js

// api is an instance of PackageNpm

const result = api.depends();
  dependencies
);

Npm.require

仅限服务器

摘要

需要使用Npm.depends()指定的软件包。

参数

源代码
名称类型描述必需
name字符串

要需要的软件包的名称。

js



const result = Npm.require();
  "name"
);

api.depends

仅限 package.js

摘要

指定您的 Meteor 软件包依赖哪些Cordova / PhoneGap插件。

插件从plugins.cordova.io安装,因此指定的插件和版本必须存在于其中。或者,版本可以用 GitHub tarball URL 替换,如 GitHub 上 Meteor wiki 的Cordova页面中所述。

参数

源代码
名称类型描述必需
dependencies对象

一个对象,其中键是插件名称,值是字符串形式的版本号或 GitHub tarball URL。示例

Cordova.depends({
  "org.apache.cordova.camera": "0.3.0"
});

或者,使用 GitHub URL

Cordova.depends({
  "org.apache.cordova.camera":
    "https://github.com/apache/cordova-plugin-camera/tarball/d84b875c449d68937520a1b352e09f6d39044fbf"
});
js

// api is an instance of PackageCordova

const result = api.depends();
  dependencies
);

api.registerBuildPlugin

仅限 package.js

摘要

定义构建插件。构建插件扩展了使用此软件包的应用程序和软件包的构建过程。例如,coffeescript软件包使用构建插件将 CoffeeScript 源文件编译成 JavaScript。

参数

源代码
名称类型描述必需
options对象

选项

名称类型描述必需
name字符串

一个修饰名称,在软件包中必须唯一。

use字符串

此插件使用的 Meteor 软件包,独立于api.onUse中指定的软件包。

sourcesArray.<String>

构成构建插件的源文件,独立于api.addFiles

npmDependencies对象

一个对象,其中键是 NPM 软件包名称,值是所需 NPM 软件包的版本号,就像在Npm.depends中一样。

js

// api is an instance of PackageNamespace

const result = api.registerBuildPlugin();
  options
);

选项

在某些情况下,我们需要在软件包中提供选项,其中这些选项不会在运行时更改。

我们更希望在配置文件中定义这些选项,而不是使用 JS 代码调用特定函数来在运行时定义选项。

例如,在quave:collections软件包中,您可以强制集合仅在服务器上可用,如下所示

json
  "packages": {
    "quave:collections": {
      "isServerOnly": true
    }
  }

我们鼓励每个软件包作者遵循此标准来提供选项

  1. 使用官方的 Meteor settings文件
  2. 在从Meteor.packages.<package name>.<your option name>读取的settings文件中。

    如果需要在客户端可用,请在public键内遵循相同的结构。

您可以使用quave:settings软件包以上述格式读取选项,并已合并私有和公共选项。

这样,我们避免了在软件包中的另一个特定代码之前调用特定代码,因为设置存储在设置中,并且软件包可以在需要时加载它,而不是依赖于开发人员在应用程序代码中调用的特定顺序。

我们已开始在 Meteor 1.10.2 上的核心软件包中采用此标准。

Plugin.registerSourceHandler

仅限构建插件

摘要

Package.registerBuildPlugin中指定的构建插件源文件中,添加一个处理程序以使用特定文件扩展名编译文件。

参数

源代码
名称类型描述必需
fileExtension字符串

此插件应处理的文件扩展名,不包括第一个点。示例:"coffee""coffee.md"

handler函数

一个函数,它接受一个参数,即 CompileStep 对象。

CompileStep 的文档可在GitHub Wiki上找到。

js



const result = Plugin.registerSourceHandler();
  "fileExtension",
() => {},
);

构建插件 API

Meteor 软件包可以提供构建插件 - 与用于编译和捆绑应用程序的构建工具 Isobuild 集成的程序。

从 Meteor 1.2 开始,用于插入构建过程的 API 称为“构建插件”。软件包的插件可以运行 3 个阶段:代码风格检查、编译和压缩。以下是 Isobuild 对应用程序和软件包源执行的操作概述

  1. 从应用程序文件夹收集源文件或读取软件包的package.js文件。
  2. 检查所有源文件并打印代码风格检查警告。
  3. 编译源文件,如 CoffeeScript、ES2015、Less 或模板,转换为纯 JavaScript 和 CSS。
  4. 链接 JavaScript 文件:将它们包装到闭包中并提供必要的软件包导入。
  5. 压缩 JavaScript 和 CSS 文件。还可以包括所有文件的连接。

构建插件填充阶段 2、3 和 5。

通常,构建插件实现一个类,该类会获得要处理的文件列表。通常,此类文件具有以下方法

  • getContentsAsBuffer - 将文件的完整内容作为缓冲区返回。
  • getContentsAsString - 将文件的完整内容作为字符串返回。
  • getPackageName - 返回软件包的名称,如果文件不在软件包中,则返回null
  • getPathInPackage - 返回文件到软件包或应用程序根目录的相对路径。返回的路径始终使用正斜杠。
  • getSourceHash - 返回文件的哈希字符串,可用于实现缓存。
  • getArch - 返回在处理此文件时所针对的体系结构。
  • getBasename - 返回文件的名称。
  • getDirname - 返回相对于软件包或应用程序根目录的目录路径。返回的路径始终使用正斜杠。
  • error - 调用此方法以引发文件的编译或代码风格检查错误。

代码风格检查工具

代码风格检查工具是检查代码中是否存在未声明的变量或查找不符合某些样式指南的代码的程序。一些流行的代码风格检查工具示例包括JSHintESLint。一些非 JavaScript 代码风格检查工具示例包括 CoffeeScript 的CoffeeLint和 CSS 的CSSLint

要在软件包中注册代码风格检查工具构建插件,您需要在package.js中执行以下几件事

  • 依赖于isobuild:[email protected]软件包
  • 注册构建插件:Package.registerBuildPlugin({ name, sources, ... });(参见docs

在您的构建插件源中,注册 Linter 插件:提供详细信息,例如名称、插件将处理的扩展名和文件名列表,以及返回 linter 类实例的工厂函数。示例

js
Plugin.registerLinter({
  extensions: ['js'],
  filenames: ['.linterrc']
}, () => new MyLinter);

在此示例中,我们注册了一个在所有js文件上运行的代码风格检查工具,并读取名为.linterrc的文件以获取配置。

MyLinter类现在应该实现processFilesForPackage方法。该方法应接受两个参数:文件列表和选项对象。

js
class MyLinter {
  processFilesForPackage(files, options) {
    files.forEach((file) => {
      // Lint the file.
      const lint = lintFile(file.getContentsAsString());

      if (lint) {
        // If there are linting errors, output them.
        const { message, line, column } = lint;
        file.error({ message, line, column });
      }
    });
  }
}

全局变量通过 options 对象传递,以便 linter 可以忽略那些看起来像全局变量的包导入的警告。

列表中的每个文件都是一个对象,它拥有上面描述的所有构建插件提供的所有方法。

请参阅 Core 中实现的 linting 插件示例:jshint

编译器

编译器是将源文件作为输入并输出 JavaScript 或 CSS 的程序。它们还可以输出添加到 <head> 标签中的 HTML 部分和静态资源。编译器插件的示例包括:CoffeeScript、Babel.js、JSX 编译器、Pug 模板编译器等。

要在你的包中注册编译器插件,你需要在你的 package.js 文件中执行以下操作

  • 依赖 isobuild:[email protected]
  • 注册构建插件:Package.registerBuildPlugin({ name, sources, ... });(参见docs

在你的构建插件源代码中,注册一个编译器插件:类似于其他类型的构建插件,提供详细信息、扩展名和文件名,以及返回编译器实例的工厂函数。例如:

js
Plugin.registerCompiler({
  extensions: ['pug', 'tpl.pug'],
  filenames: []
}, () => new PugCompiler);

编译器类必须实现 processFilesForTarget 方法,该方法接收目标(包/应用程序的服务器或客户端部分)的源文件。

js
class PugCompiler {
  processFilesForTarget(files) {
    files.forEach((file) => {
      // Process and add the output.
      const output = compilePug(file.getContentsAsString());

      file.addJavaScript({
        data: output,
        path: `${file.getPathInPackage()}.js`
      });
    });
  }
}

除了输入文件类上可用的通用方法外,还可以使用以下方法

  • getExtension - 返回与编译器插件匹配的扩展名。优先选择最长的前缀。
  • getDeclaredExports - 返回在此目标中声明为导出的符号列表。是目标控制文件(例如 package.js)中 api.export('symbol') 调用的结果。
  • getDisplayPath 返回可用于形成错误消息或其他显示属性的相对路径。可以用作源映射的输入。
  • addStylesheet - 仅限 Web 目标。将样式表添加到文档中。linter 构建插件不可用。
  • addJavaScript - 添加 JavaScript 代码。添加的代码只能看到此包使用 'api.use' 作为运行时依赖项导入的命名空间。如果正在编译的文件使用裸标记添加,则生成的 JavaScript 不会包装在闭包中。
  • addAsset - 添加一个文件,根据目标将其原样提供给浏览器或包含在浏览器中。在 Web 上,它将在请求的精确路径下提供服务。对于服务器目标,可以使用 Assets.getTextAsyncAssets.getBinaryAsync 检索它。
  • addHtml - 仅在 Web 目标中有效。将标记添加到文档的 headbody 部分。
  • hmrAvailable - 如果文件可以使用 HMR 更新,则返回 true。除其他事项外,它还会检查 HMR 是否支持当前架构和构建模式,以及 unibuild 是否使用 hot-module-replacement 包。在某些罕见情况下,hmrAvailable 返回 true,但在构建过程的后期获得更多信息后,Meteor 决定该文件无法使用 HMR 更新。
  • readAndWatchFileWithHash - 接受绝对路径,并返回 { contents, hash } 确保 Meteor 监视文件,以便对其进行的任何更改都会触发重建

Meteor 将一些编译器实现为 Core 包,很好的例子是 Blaze 模板 包和 ecmascript 包(将 ES2015+ 编译为可以在浏览器中运行的 JavaScript)。

压缩器

压缩器在源代码编译完成后以及 JavaScript 代码链接后最后运行。压缩器仅针对客户端程序(web.browserweb.cordova)运行。

可以添加两种类型的压缩器:处理 JavaScript 的压缩器(注册扩展名:['js'])和处理 CSS 的压缩器(扩展名:['css'])。

要在你的包中注册压缩器插件,请在你的 package.js 文件中添加以下内容

  • 依赖 isobuild:[email protected]
  • 注册构建插件:Package.registerBuildPlugin({ name, sources, ... });(参见docs

在你的构建插件源代码中,注册一个压缩器插件。与 Linter 和编译器插件类似,指定感兴趣的扩展名(cssjs)。工厂函数返回压缩器类的实例。

js
Plugin.registerMinifier({
  extensions: ['js']
}, () => new UglifyJsMinifier);

压缩器类必须实现 processFilesForBundle 方法。第一个参数是已处理文件的列表,options 对象指定压缩器是在生产模式还是开发模式下运行。

信息

此方法可以是异步的。如果它返回一个 Promise,则构建过程将在继续之前等待它解析。

js
class UglifyJsMinifier {
  processFilesForBundle(files, options) {
    const { minifyMode } = options;

    if (minifyMode === 'development') {
      // Don't minify in development.
      file.forEach((file) => {
        file.addJavaScript({
          data: file.getContentsAsBuffer(),
          sourceMap: file.getSourceMap(),
          path: file.getPathInBundle()
        });
      });

      return;
    }

    // Minify in production.
    files.forEach((file) => {
      file.addJavaScript({
        data: uglifyjs.minify(file.getContentsAsBuffer()),
        path: file.getPathInBundle()
      });
    });
  }
}

在此示例中,我们在开发模式下重新添加了相同的文件以避免不必要的工作,然后在生产模式下压缩文件。

除了常见的输入文件方法外,还可以使用以下方法

  • getPathInBundle - 返回包中已处理文件的路径。
  • getSourcePath - 如果可用,则返回输入文件的绝对路径,否则返回 null。
  • getSourceMap - 如果存在,则返回已处理文件的源映射。
  • addJavaScript - 与编译器相同
  • addStylesheet - 与编译器相同
  • readAndWatchFileWithHash - 仅适用于 css 压缩器。与编译器相同。

目前,Meteor Core 附带 standard-minifiers 包,该包可以用自定义包替换。该包的 源代码 是一个很好的示例,说明如何构建自己的压缩插件。

在开发构建中,压缩器必须满足以下要求才能不阻止热模块替换

  • 为每个文件调用 addJavasScript 一次以添加文件的内容
  • 文件的内容不会被修改

将来,Meteor 将允许压缩器在开发过程中连接或修改文件,而不会影响热模块替换。

缓存

由于 API 允许构建插件一次处理多个文件,因此我们鼓励包作者至少为其插件实现一些内存缓存。对于 linter 和编译器,使用 getSourceHash 函数将允许在未重新处理文件(即使内容未更改)时快速增量重新编译。

对于 Isobuild 进程运行之间的快速重建,插件可以实现磁盘缓存。如果插件实现了 setDiskCacheDirectory 方法,则会定期调用它,并提供磁盘上的新路径,插件可以在该路径上写入其脱机缓存。当插件重建或出于任何原因应使缓存无效(例如,选择的包版本集已更改)时,文件夹会正确重置。

缓存编译器

有一个名为 caching-compiler 的核心包,它实现了保持内存和磁盘缓存的大多数常见逻辑。正确实现缓存的最简单方法是在你的构建插件中从该包继承 CachingCompilerMultiFileCachingCompiler 类。CachingCompiler 用于将每个文件完全独立考虑的编译器;MultiFileCachingCompiler 用于允许文件相互引用的编译器。要在插件命名空间中获取此类,请在插件定义中添加一个依赖项

js
Package.registerBuildPlugin({
  name: 'compileGG',
  use: ['[email protected]'],
  sources: ['plugin/compile-gg.js']
});

访问文件系统

由于构建插件作为 Meteor 工具的一部分运行,因此它们遵循相同的系统文件访问约定 - 所有文件系统路径始终看起来像 Unix 路径:使用正斜杠并在 '/' 处具有根目录,即使在 Windows 上也是如此。例如:路径 /usr/bin/program/C/Program Files/Program/program.exe 是有效的路径,而 C:\Program Files\Program\program.exe 不是。

因此,无论何时你在构建插件实现中通过 getPathInPackagesetDiskCacheDirectory 方法的参数获取路径,该路径都将是 Unix 路径。

现在,在 Windows 上运行时,常用的 node 模块 fspath 期望获取 DOS 路径。为了帮助你编写正确的代码,Plugin 符号提供了它自己的 fspath 版本,你可以使用它们代替(请注意,fs 上的所有方法都是 fiberized 的,同步版本更喜欢使用 Fibers 而不是冻结整个事件循环)。

此外,Plugin 提供了辅助函数 convertToStandardPathconvertToOSPath,用于转换为 Unix 路径或 node 库期望的路径,而不管路径来源如何。

示例

js
// On Windows
const fs = Plugin.fs;
const path = Plugin.path;

const filePath = path.join('/C/Program Files', 'Program/file.txt');
console.log(filePath); // Prints '/C/Program Files/Program/file.txt'

fs.writeFileSync(filePath, 'Hello.'); // Writes to 'C:\Program Files\Program\file.txt'

console.log(Plugin.convertToOsPath(filePath)); // Prints 'C:\Program Files\Program\file.txt'

Isobuild 功能包

从 Meteor 1.2 开始,包可以声明它们需要 Meteor 工具的一个版本,其 Isobuild 构建系统支持某个功能。例如,包必须编写 api.use('isobuild:[email protected]') 才能调用 Plugin.registerCompiler。这意味着包可以从旧的 registerSourceHandler API 过渡到 registerCompiler,而版本求解器将正确防止旧工具选择 registerCompiler 版本,因为这些旧工具不知道如何处理它。

这是已知的 Isobuild 功能“包”,按支持它们的 Meteor 的第一个版本排序。

在 Meteor 1.2 中引入