| name | conventions |
| description | Use when writing new packages, adding CLI commands, creating build plugins, or following Meteor code patterns. Covers package.js structure, file naming, and common code patterns. |
Code Conventions
Package structure, file naming, and code patterns for the Meteor codebase.
Package Structure
Every Meteor package follows this structure:
packages/my-package/
โโโ package.js # Package manifest (name, version, dependencies, exports)
โโโ my-package.js # Main implementation (or split by concern)
โโโ my-package-server.js # Server-only code (optional)
โโโ my-package-client.js # Client-only code (optional)
โโโ my-package-tests.js # Tests (loaded via api.addFiles in test mode)
โโโ README.md # Documentation (optional)
Package.js Anatomy
Package.describe({
name: 'my-package',
version: '1.0.0',
summary: 'Brief description',
git: 'https://github.com/meteor/meteor.git',
documentation: 'README.md'
});
Package.onUse(function(api) {
api.versionsFrom(['3.0']);
api.use([
'ecmascript',
'mongo',
'tracker'
]);
api.use('accounts-base', { weak: true });
api.mainModule('my-package-server.js', 'server');
api.mainModule('my-package-client.js', 'client');
api.export('MyPackage');
});
Package.onTest(function(api) {
api.use(['tinytest', 'my-package']);
api.addFiles('my-package-tests.js');
});
Npm.depends({
'lodash': '4.17.21'
});
File Naming Conventions
| Pattern | Purpose |
|---|
*-server.js | Server-only code |
*-client.js | Client-only code |
*-common.js | Shared code |
*-tests.js | Test files |
*.d.ts | TypeScript declarations |
Common Patterns
Adding a New Core Package
- Create directory in
/packages/my-package/
- Add
package.js with proper dependencies
- Implement functionality with proper exports
- Add tests in
*-tests.js
- Update version numbers if needed
Modifying Build System
Key files to understand:
/tools/isobuild/bundler.js - High-level bundling
/tools/isobuild/compiler.js - Package compilation
/tools/project-context.js - Dependency resolution
/tools/cli/commands.js - CLI command handlers
Adding CLI Commands
Edit /tools/cli/commands.js or create new command file:
main.registerCommand({
name: 'my-command',
options: {
'option-name': { type: String, short: 'o' }
},
catalogRefresh: new catalog.Refresh.Never()
}, function(options) {
});
WebApp Middleware Pattern
import { WebApp } from 'meteor/webapp';
WebApp.rawConnectHandlers.use('/api', (req, res, next) => {
next();
});
WebApp.connectHandlers.use('/api', (req, res, next) => {
next();
});
Build Plugin Pattern
Package.registerBuildPlugin({
name: 'compile-my-files',
use: ['ecmascript', 'caching-compiler'],
sources: ['plugin.js'],
npmDependencies: { 'my-compiler': '1.0.0' }
});
Plugin.registerCompiler({
extensions: ['myext'],
archMatching: 'web'
}, () => new MyCompiler());
class MyCompiler extends CachingCompiler {
getCacheKey(inputFile) {
return inputFile.getSourceHash();
}
compileOneFile(inputFile) {
const source = inputFile.getContentsAsString();
const compiled = transform(source);
inputFile.addJavaScript({
data: compiled,
path: inputFile.getPathInPackage() + '.js'
});
}
}
Using tools-core in Packages
api.use('tools-core');
import {
logProgress,
checkNpmDependencyExists,
getMeteorAppConfig,
spawnProcess
} from 'meteor/tools-core';
if (!checkNpmDependencyExists('@rspack/core')) {
installNpmDependency(['@rspack/core@^1.7.1']);
}
const proc = spawnProcess('npx', ['rspack', 'build'], {
cwd: getMeteorAppDir(),
onStdout: (data) => logProgress(data)
});
Version Patterns
Meteor uses X.Y.Z-rcN.M versioning where:
X.Y.Z - Semantic version
rcN - Release candidate number
M - Package-specific revision