mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
Merge branch 'devel' into ci/removing-travis
This commit is contained in:
@@ -1,93 +0,0 @@
|
||||
android_bundle/
|
||||
dev_bundle/
|
||||
docs/
|
||||
examples/
|
||||
scripts/
|
||||
!tools/*.js
|
||||
!tools/isobuild/*.js
|
||||
!tools/catalog/*.js
|
||||
!tools/packaging/*.js
|
||||
!tools/cli/*.js
|
||||
!tools/runners/*.js
|
||||
!tools/tool-env/*.js
|
||||
!tools/fs/*.js
|
||||
|
||||
# Below, files that have yet to be converted to match the linter
|
||||
tools/archinfo.js
|
||||
tools/auth-client.js
|
||||
tools/auth.js
|
||||
tools/buildmessage.js
|
||||
tools/cleanup.js
|
||||
tools/colon-converter.js
|
||||
tools/config.js
|
||||
tools/console.js
|
||||
tools/deploy.js
|
||||
tools/fiber-helpers.js
|
||||
tools/fs/files.js
|
||||
tools/fs/mini-files.js
|
||||
tools/http-helpers.js
|
||||
tools/inspector.js
|
||||
tools/index.js
|
||||
tools/mongo-exit-codes.ts
|
||||
tools/processes.ts
|
||||
tools/progress.ts
|
||||
tools/project-context.js
|
||||
tools/runners/run-log.js
|
||||
tools/fs/safe-pathwatcher.js
|
||||
tools/selftest.js
|
||||
tools/service-connection.js
|
||||
tools/shell-client.ts
|
||||
tools/stats.js
|
||||
tools/test-utils.js
|
||||
tools/upgraders.js
|
||||
tools/utils/utils.js
|
||||
tools/fs/watch.js
|
||||
|
||||
tools/catalog/catalog-local.js
|
||||
tools/catalog/catalog-remote.js
|
||||
tools/catalog/catalog.js
|
||||
tools/catalog/catalog-utils.js
|
||||
|
||||
tools/cli/commands-cordova.js
|
||||
tools/cli/commands-packages-query.js
|
||||
tools/cli/commands-packages.js
|
||||
tools/cli/commands.js
|
||||
tools/cli/main.js
|
||||
|
||||
tools/tool-env/flush-buffers-on-exit-in-windows.js
|
||||
tools/tool-env/install-babel.js
|
||||
tools/tool-env/isopackets.js
|
||||
tools/tool-env/profile-require.js
|
||||
tools/tool-env/profile.js
|
||||
|
||||
tools/runners/run-all.js
|
||||
tools/runners/run-app.js
|
||||
tools/runners/run-mongo.js
|
||||
tools/runners/run-proxy.js
|
||||
tools/runners/run-selenium.js
|
||||
|
||||
tools/packaging/package-client.js
|
||||
tools/packaging/package-map.js
|
||||
tools/packaging/package-version-parser.js
|
||||
tools/packaging/release.js
|
||||
tools/packaging/tropohouse.js
|
||||
tools/packaging/updater.js
|
||||
tools/packaging/warehouse.js
|
||||
|
||||
tools/isobuild/build-plugin.js
|
||||
tools/isobuild/builder.js
|
||||
tools/isobuild/bundler.js
|
||||
tools/isobuild/compiler-deprecated-compile-step.js
|
||||
tools/isobuild/compiler-plugin.js
|
||||
tools/isobuild/compiler.js
|
||||
tools/isobuild/import-scanner.js
|
||||
tools/isobuild/isopack-cache.js
|
||||
tools/isobuild/isopack.js
|
||||
tools/isobuild/js-analyze.js
|
||||
tools/isobuild/linker.js
|
||||
tools/isobuild/linter-plugin.js
|
||||
tools/isobuild/meteor-npm.js
|
||||
tools/isobuild/npm-discards.ts
|
||||
tools/isobuild/package-api.js
|
||||
tools/isobuild/package-source.js
|
||||
tools/isobuild/source-arch.js
|
||||
33
.fmtignore
Normal file
33
.fmtignore
Normal file
@@ -0,0 +1,33 @@
|
||||
# Ignore everything by default
|
||||
# oxfmt uses .prettierignore automatically
|
||||
|
||||
# Root and top-level directories
|
||||
*.yml
|
||||
*.md
|
||||
*.json
|
||||
tsconfig.json
|
||||
.travis.yml
|
||||
android_bundle/
|
||||
dev_bundle/
|
||||
docs/
|
||||
examples/
|
||||
guide/
|
||||
scripts/
|
||||
tools/
|
||||
npm-packages/
|
||||
v3-docs/
|
||||
node_modules/
|
||||
.meteor/
|
||||
.circleci/
|
||||
.github/
|
||||
.coderabbit.yaml
|
||||
|
||||
# Ignore node_modules/npm inside packages
|
||||
packages/**/node_modules/
|
||||
packages/**/.npm/
|
||||
|
||||
# All packages ignored by default
|
||||
# Only those listed below are formatted
|
||||
packages/*
|
||||
|
||||
!packages/facts-base/
|
||||
5
.git-blame-ignore-revs
Normal file
5
.git-blame-ignore-revs
Normal file
@@ -0,0 +1,5 @@
|
||||
# This file lists commits that should be ignored by git blame.
|
||||
# Configure with: git config blame.ignoreRevsFile .git-blame-ignore-revs
|
||||
# auto-format and lint fixes 2026-03-27
|
||||
1bfad0bcbb9c495172d1475f1c6d54df8d34ceae
|
||||
|
||||
10
.github/workflows/check-code-style.yml
vendored
10
.github/workflows/check-code-style.yml
vendored
@@ -2,18 +2,22 @@ name: Check code-style
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'packages/**'
|
||||
- 'npm-packages/meteor-installer/**'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'packages/**'
|
||||
- 'npm-packages/meteor-installer/**'
|
||||
jobs:
|
||||
check-code-style:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: oss-vm
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22.x
|
||||
- run: npm ci
|
||||
- name: Run ESLint@8
|
||||
run: npx eslint@8 "./npm-packages/meteor-installer/**/*.js"
|
||||
- name: Check formatting with oxfmt
|
||||
run: npx oxfmt --ignore-path .fmtignore --check .
|
||||
- name: Run lint with oxlint
|
||||
run: npm run lint
|
||||
|
||||
11
.oxfmtrc.json
Normal file
11
.oxfmtrc.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"semi": true,
|
||||
"singleQuote": false,
|
||||
"printWidth": 100,
|
||||
"tabWidth": 2,
|
||||
"useTabs": false,
|
||||
"trailingComma": "all",
|
||||
"bracketSpacing": true,
|
||||
"arrowParens": "always",
|
||||
"endOfLine": "lf"
|
||||
}
|
||||
30
.oxlintignore
Normal file
30
.oxlintignore
Normal file
@@ -0,0 +1,30 @@
|
||||
# Ignore everything by default
|
||||
|
||||
# Root and top-level directories
|
||||
*.yml
|
||||
*.md
|
||||
*.json
|
||||
android_bundle/
|
||||
dev_bundle/
|
||||
docs/
|
||||
examples/
|
||||
guide/
|
||||
scripts/
|
||||
tools/
|
||||
npm-packages/
|
||||
v3-docs/
|
||||
node_modules/
|
||||
.meteor/
|
||||
.circleci/
|
||||
.github/
|
||||
.coderabbit.yaml
|
||||
|
||||
# Ignore node_modules/npm inside packages
|
||||
packages/**/node_modules/
|
||||
packages/**/.npm/
|
||||
|
||||
# All packages ignored by default
|
||||
# Only those listed below are linted
|
||||
packages/*
|
||||
|
||||
!packages/facts-base/
|
||||
9
.oxlintrc.json
Normal file
9
.oxlintrc.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/oxc-project/oxc/main/npm/oxlint/configuration_schema.json",
|
||||
"rules": {
|
||||
"no-var": "error",
|
||||
"no-unused-vars": "error",
|
||||
"prefer-const": "error",
|
||||
"prefer-template": "error"
|
||||
}
|
||||
}
|
||||
6
lefthook.yml
Normal file
6
lefthook.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
pre-push:
|
||||
commands:
|
||||
fmt-check:
|
||||
run: npm run fmt:check
|
||||
lint:
|
||||
run: npm run lint
|
||||
4839
package-lock.json
generated
4839
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
21
package.json
21
package.json
@@ -13,28 +13,21 @@
|
||||
"homepage": "https://www.meteor.com/",
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.21.3",
|
||||
"@babel/eslint-parser": "^7.21.3",
|
||||
"@babel/eslint-plugin": "^7.19.1",
|
||||
"@babel/preset-react": "^7.18.6",
|
||||
"@types/lodash.isempty": "^4.4.9",
|
||||
"@types/node": "^18.16.18",
|
||||
"@types/sockjs": "^0.3.36",
|
||||
"@types/sockjs-client": "^1.5.4",
|
||||
"@typescript-eslint/eslint-plugin": "^5.56.0",
|
||||
"@typescript-eslint/parser": "^5.56.0",
|
||||
"eslint": "^8.36.0",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"eslint-config-vazco": "^7.1.0",
|
||||
"eslint-plugin-eslint-comments": "^3.2.0",
|
||||
"eslint-plugin-import": "^2.27.5",
|
||||
"eslint-plugin-jsx-a11y": "^6.7.1",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-plugin-react": "^7.32.2",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"prettier": "^2.8.8",
|
||||
"lefthook": "^2.1.4",
|
||||
"oxfmt": "^0.42.0",
|
||||
"oxlint": "^1.57.0",
|
||||
"typescript": "^5.4.5"
|
||||
},
|
||||
"scripts": {
|
||||
"fmt": "oxfmt --ignore-path .fmtignore .",
|
||||
"fmt:check": "oxfmt --ignore-path .fmtignore --check .",
|
||||
"lint": "oxlint --ignore-path .oxlintignore .",
|
||||
"lint:fix": "oxlint --fix --ignore-path .oxlintignore .",
|
||||
"install:unit": "cd tools/unit-tests && npm install",
|
||||
"test:unit": "cd tools/unit-tests && npm test",
|
||||
"test:idle-bot": "node --test .github/scripts/__tests__/inactive-issues.test.js",
|
||||
|
||||
@@ -1,12 +1,173 @@
|
||||
Tinytest.add('facts-base - increments server facts', test => {
|
||||
Facts.resetServerFacts()
|
||||
function mockSub() {
|
||||
const calls = { added: [], changed: [] };
|
||||
return {
|
||||
added(collection, id, fields) {
|
||||
calls.added.push({ collection, id, fields });
|
||||
},
|
||||
changed(collection, id, fields) {
|
||||
calls.changed.push({ collection, id, fields });
|
||||
},
|
||||
calls,
|
||||
};
|
||||
}
|
||||
|
||||
Facts.incrementServerFact('newPackage', 'skyIsBlue', 42);
|
||||
test.equal(Facts._factsByPackage.newPackage, { skyIsBlue: 42 });
|
||||
// -- resetServerFacts --
|
||||
|
||||
Facts.incrementServerFact('newPackage', 'skyIsBlue', 21);
|
||||
test.equal(Facts._factsByPackage.newPackage, { skyIsBlue: 63 });
|
||||
Tinytest.add("facts-base - resetServerFacts clears all facts", (test) => {
|
||||
Facts.resetServerFacts();
|
||||
Facts.incrementServerFact("pkg-a", "fact1", 10);
|
||||
Facts.incrementServerFact("pkg-b", "fact2", 20);
|
||||
|
||||
Facts.incrementServerFact('newPackage', 'newFact', 7);
|
||||
test.equal(Facts._factsByPackage.newPackage, { skyIsBlue: 63, newFact: 7 });
|
||||
Facts.resetServerFacts();
|
||||
|
||||
test.equal(Facts._factsByPackage, {});
|
||||
});
|
||||
|
||||
Tinytest.add("facts-base - resetServerFacts on already empty state is a no-op", (test) => {
|
||||
Facts.resetServerFacts();
|
||||
Facts.resetServerFacts();
|
||||
|
||||
test.equal(Facts._factsByPackage, {});
|
||||
});
|
||||
|
||||
// -- incrementServerFact: new package --
|
||||
|
||||
Tinytest.add("facts-base - incrementServerFact creates entry for new package", (test) => {
|
||||
Facts.resetServerFacts();
|
||||
|
||||
Facts.incrementServerFact("new-pkg", "connections", 5);
|
||||
|
||||
test.equal(Facts._factsByPackage["new-pkg"], { connections: 5 });
|
||||
});
|
||||
|
||||
// -- incrementServerFact: existing package, new fact --
|
||||
|
||||
Tinytest.add("facts-base - incrementServerFact adds new fact to existing package", (test) => {
|
||||
Facts.resetServerFacts();
|
||||
Facts.incrementServerFact("my-pkg", "factA", 1);
|
||||
|
||||
Facts.incrementServerFact("my-pkg", "factB", 7);
|
||||
|
||||
test.equal(Facts._factsByPackage["my-pkg"], { factA: 1, factB: 7 });
|
||||
});
|
||||
|
||||
// -- incrementServerFact: existing package, existing fact --
|
||||
|
||||
Tinytest.add("facts-base - incrementServerFact accumulates on existing fact", (test) => {
|
||||
Facts.resetServerFacts();
|
||||
Facts.incrementServerFact("pkg", "counter", 10);
|
||||
|
||||
Facts.incrementServerFact("pkg", "counter", 3);
|
||||
|
||||
test.equal(Facts._factsByPackage["pkg"].counter, 13);
|
||||
});
|
||||
|
||||
// -- incrementServerFact: negative increment --
|
||||
|
||||
Tinytest.add("facts-base - incrementServerFact handles negative increment", (test) => {
|
||||
Facts.resetServerFacts();
|
||||
Facts.incrementServerFact("pkg", "counter", 10);
|
||||
|
||||
Facts.incrementServerFact("pkg", "counter", -4);
|
||||
|
||||
test.equal(Facts._factsByPackage["pkg"].counter, 6);
|
||||
});
|
||||
|
||||
// -- incrementServerFact: multiple independent packages --
|
||||
|
||||
Tinytest.add("facts-base - incrementServerFact keeps packages independent", (test) => {
|
||||
Facts.resetServerFacts();
|
||||
|
||||
Facts.incrementServerFact("alpha", "x", 1);
|
||||
Facts.incrementServerFact("beta", "x", 100);
|
||||
Facts.incrementServerFact("alpha", "x", 2);
|
||||
|
||||
test.equal(Facts._factsByPackage["alpha"].x, 3);
|
||||
test.equal(Facts._factsByPackage["beta"].x, 100);
|
||||
});
|
||||
|
||||
// -- subscription notifications: sub.added on new package --
|
||||
|
||||
Tinytest.add("facts-base - notifies subscriptions with added when package is new", (test) => {
|
||||
Facts.resetServerFacts();
|
||||
const sub = mockSub();
|
||||
Facts._setActiveSubscriptions([sub]);
|
||||
|
||||
Facts.incrementServerFact("fresh-pkg", "sessions", 42);
|
||||
|
||||
test.equal(sub.calls.added.length, 1);
|
||||
test.equal(sub.calls.added[0].collection, "meteor_Facts_server");
|
||||
test.equal(sub.calls.added[0].id, "fresh-pkg");
|
||||
test.equal(sub.calls.added[0].fields, { sessions: 42 });
|
||||
test.equal(sub.calls.changed.length, 0);
|
||||
|
||||
Facts._setActiveSubscriptions([]);
|
||||
});
|
||||
|
||||
// -- subscription notifications: sub.changed on existing package --
|
||||
|
||||
Tinytest.add("facts-base - notifies subscriptions with changed when fact is updated", (test) => {
|
||||
Facts.resetServerFacts();
|
||||
const sub = mockSub();
|
||||
Facts.incrementServerFact("pkg", "rps", 10);
|
||||
|
||||
Facts._setActiveSubscriptions([sub]);
|
||||
Facts.incrementServerFact("pkg", "rps", 5);
|
||||
|
||||
test.equal(sub.calls.changed.length, 1);
|
||||
test.equal(sub.calls.changed[0].collection, "meteor_Facts_server");
|
||||
test.equal(sub.calls.changed[0].id, "pkg");
|
||||
test.equal(sub.calls.changed[0].fields, { rps: 15 });
|
||||
test.equal(sub.calls.added.length, 0);
|
||||
|
||||
Facts._setActiveSubscriptions([]);
|
||||
});
|
||||
|
||||
// -- subscription notifications: multiple subscriptions --
|
||||
|
||||
Tinytest.add("facts-base - notifies all active subscriptions on increment", (test) => {
|
||||
Facts.resetServerFacts();
|
||||
const sub1 = mockSub();
|
||||
const sub2 = mockSub();
|
||||
Facts._setActiveSubscriptions([sub1, sub2]);
|
||||
|
||||
Facts.incrementServerFact("pkg", "hits", 1);
|
||||
|
||||
test.equal(sub1.calls.added.length, 1);
|
||||
test.equal(sub2.calls.added.length, 1);
|
||||
|
||||
Facts.incrementServerFact("pkg", "hits", 1);
|
||||
|
||||
test.equal(sub1.calls.changed.length, 1);
|
||||
test.equal(sub2.calls.changed.length, 1);
|
||||
|
||||
Facts._setActiveSubscriptions([]);
|
||||
});
|
||||
|
||||
// -- subscription notifications: no subscriptions --
|
||||
|
||||
Tinytest.add("facts-base - incrementServerFact works with no active subscriptions", (test) => {
|
||||
Facts.resetServerFacts();
|
||||
Facts._setActiveSubscriptions([]);
|
||||
|
||||
Facts.incrementServerFact("lonely-pkg", "value", 99);
|
||||
|
||||
test.equal(Facts._factsByPackage["lonely-pkg"], { value: 99 });
|
||||
});
|
||||
|
||||
// -- setUserIdFilter --
|
||||
|
||||
Tinytest.add("facts-base - setUserIdFilter replaces the filter", (test) => {
|
||||
let filterCalled = false;
|
||||
Facts.setUserIdFilter(function (userId) {
|
||||
filterCalled = true;
|
||||
return userId === "admin";
|
||||
});
|
||||
|
||||
test.isFalse(filterCalled);
|
||||
|
||||
// Restore default to not affect other tests
|
||||
Facts.setUserIdFilter(function () {
|
||||
return !!Package.autopublish;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
const Facts = {};
|
||||
const FACTS_COLLECTION = 'meteor_Facts_server';
|
||||
const FACTS_PUBLICATION = 'meteor_facts';
|
||||
const FACTS_COLLECTION = "meteor_Facts_server";
|
||||
const FACTS_PUBLICATION = "meteor_facts";
|
||||
|
||||
export {
|
||||
Facts,
|
||||
FACTS_COLLECTION,
|
||||
FACTS_PUBLICATION,
|
||||
};
|
||||
export { Facts, FACTS_COLLECTION, FACTS_PUBLICATION };
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Facts, FACTS_COLLECTION, FACTS_PUBLICATION } from './facts_base_common';
|
||||
import { Facts, FACTS_COLLECTION, FACTS_PUBLICATION } from "./facts_base_common";
|
||||
|
||||
const hasOwn = Object.prototype.hasOwnProperty;
|
||||
|
||||
@@ -6,7 +6,7 @@ const hasOwn = Object.prototype.hasOwnProperty;
|
||||
|
||||
// By default, we publish facts to no user if autopublish is off, and to all
|
||||
// users if autopublish is on.
|
||||
let userIdFilter = function (userId) {
|
||||
let userIdFilter = function () {
|
||||
return !!Package.autopublish;
|
||||
};
|
||||
|
||||
@@ -20,8 +20,14 @@ Facts.setUserIdFilter = function (filter) {
|
||||
const factsByPackage = {};
|
||||
let activeSubscriptions = [];
|
||||
|
||||
// Make factsByPackage data available to the server environment
|
||||
// Make internal state available to the server environment
|
||||
Facts._factsByPackage = factsByPackage;
|
||||
Facts._getActiveSubscriptions = function () {
|
||||
return activeSubscriptions;
|
||||
};
|
||||
Facts._setActiveSubscriptions = function (subs) {
|
||||
activeSubscriptions = subs;
|
||||
};
|
||||
|
||||
Facts.incrementServerFact = function (pkg, fact, increment) {
|
||||
if (!hasOwn.call(factsByPackage, pkg)) {
|
||||
@@ -46,7 +52,7 @@ Facts.incrementServerFact = function (pkg, fact, increment) {
|
||||
};
|
||||
|
||||
Facts.resetServerFacts = function () {
|
||||
for (let pkg in factsByPackage) {
|
||||
for (const pkg in factsByPackage) {
|
||||
delete factsByPackage[pkg];
|
||||
}
|
||||
};
|
||||
@@ -56,27 +62,26 @@ Facts.resetServerFacts = function () {
|
||||
// called?
|
||||
Meteor.defer(function () {
|
||||
// XXX Also publish facts-by-package.
|
||||
Meteor.publish(FACTS_PUBLICATION, function () {
|
||||
const sub = this;
|
||||
if (!userIdFilter(this.userId)) {
|
||||
sub.ready();
|
||||
return;
|
||||
}
|
||||
Meteor.publish(
|
||||
FACTS_PUBLICATION,
|
||||
function () {
|
||||
const sub = this;
|
||||
if (!userIdFilter(this.userId)) {
|
||||
sub.ready();
|
||||
return;
|
||||
}
|
||||
|
||||
activeSubscriptions.push(sub);
|
||||
Object.keys(factsByPackage).forEach(function (pkg) {
|
||||
sub.added(FACTS_COLLECTION, pkg, factsByPackage[pkg]);
|
||||
});
|
||||
sub.onStop(function () {
|
||||
activeSubscriptions =
|
||||
activeSubscriptions.filter(activeSub => activeSub !== sub);
|
||||
});
|
||||
sub.ready();
|
||||
}, {is_auto: true});
|
||||
activeSubscriptions.push(sub);
|
||||
Object.keys(factsByPackage).forEach(function (pkg) {
|
||||
sub.added(FACTS_COLLECTION, pkg, factsByPackage[pkg]);
|
||||
});
|
||||
sub.onStop(function () {
|
||||
activeSubscriptions = activeSubscriptions.filter((activeSub) => activeSub !== sub);
|
||||
});
|
||||
sub.ready();
|
||||
},
|
||||
{ is_auto: true },
|
||||
);
|
||||
});
|
||||
|
||||
export {
|
||||
Facts,
|
||||
FACTS_COLLECTION,
|
||||
FACTS_PUBLICATION,
|
||||
};
|
||||
export { Facts, FACTS_COLLECTION, FACTS_PUBLICATION };
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
Package.describe({
|
||||
summary: "Publish internal app statistics",
|
||||
version: '1.0.2',
|
||||
version: "1.0.2",
|
||||
});
|
||||
|
||||
Package.onUse(function (api) {
|
||||
api.use('ecmascript', ['client', 'server']);
|
||||
api.use("ecmascript", ["client", "server"]);
|
||||
|
||||
// Detect whether autopublish is used.
|
||||
api.use('autopublish', 'server', {weak: true});
|
||||
api.use("autopublish", "server", { weak: true });
|
||||
|
||||
// Unordered dependency on livedata, since livedata has a (weak) dependency on
|
||||
// us.
|
||||
api.use('ddp', 'server', {unordered: false});
|
||||
api.use("ddp", "server", { unordered: false });
|
||||
|
||||
api.mainModule('facts_base_server.js', 'server');
|
||||
api.mainModule('facts_base_common.js', 'client');
|
||||
api.mainModule("facts_base_server.js", "server");
|
||||
api.mainModule("facts_base_common.js", "client");
|
||||
|
||||
api.export('Facts');
|
||||
api.export("Facts");
|
||||
});
|
||||
|
||||
Package.onTest(function (api) {
|
||||
api.use(['tinytest', 'ecmascript', 'facts-base']);
|
||||
api.addFiles(['facts_base.tests.js'], 'server');
|
||||
api.use(["tinytest", "ecmascript", "facts-base"]);
|
||||
api.addFiles(["facts_base.tests.js"], "server");
|
||||
});
|
||||
|
||||
@@ -17,10 +17,13 @@ export default defineConfig({
|
||||
text: "Guide",
|
||||
items: [
|
||||
{text: "Overview", link: "/"},
|
||||
{text: "Migration Strategy", link: "/guide/migration-strategy"},
|
||||
{text: "Frequently Asked Questions", link: "/frequently-asked-questions/"},
|
||||
{text: "Breaking Changes", link: "/breaking-changes/"},
|
||||
{text: "Meteor.call x Meteor.callAsync", link: "/breaking-changes/call-x-callAsync"},
|
||||
{text: "Upgrading packages", link: "/breaking-changes/upgrading-packages"},
|
||||
{text: "Package Replacements", link: "/guide/package-replacements"},
|
||||
{text: "Removing Fibers Patterns", link: "/guide/removing-fibers"},
|
||||
{text: "Publishing Packages", link: "/guide/publishing-packages"},
|
||||
{text: "Common Errors", link: "/guide/common-errors"},
|
||||
]
|
||||
|
||||
@@ -135,6 +135,10 @@ Starting with Meteor 3.1, Express has been updated to version 5. If you're upgra
|
||||
|
||||
:::
|
||||
|
||||
::: info
|
||||
You can start using the Express-based WebApp API while still on Meteor 2.x by installing [`harry97:webapp`](https://github.com/harryadel/harry97-webapp), a backport of Meteor 3's Express-based webapp for Meteor 2.17. This lets you migrate your middleware code before upgrading.
|
||||
:::
|
||||
|
||||
The `webapp` package now exports these new properties:
|
||||
|
||||
```ts
|
||||
|
||||
@@ -22,3 +22,133 @@ To resolve this issue, follow these steps:
|
||||
By reducing the package footprint and updating dependencies, you should be able to complete the migration without memory-related errors.
|
||||
|
||||
This error was lastly reported [here](https://forums.meteor.com/t/meteor-update-fails/62171).
|
||||
|
||||
## Unhandled Promise Rejections in Async Callbacks
|
||||
|
||||
**Why this happens:**
|
||||
|
||||
When converting synchronous callbacks to async (e.g., in cron jobs, collection hooks, or event handlers), unhandled promise rejections can crash the server. This often manifests as seemingly unrelated errors like WebSocket connection failures or silent startup crashes.
|
||||
|
||||
A common case is `quave:synced-cron` where an async cron callback rejects without being caught, bringing down the entire process.
|
||||
|
||||
**How to solve it:**
|
||||
|
||||
Ensure all async callbacks properly handle errors:
|
||||
|
||||
```js
|
||||
// Problem — unhandled rejection crashes the process
|
||||
SyncedCron.add({
|
||||
name: 'My Job',
|
||||
schedule(parser) { return parser.text('every 1 hour'); },
|
||||
async job() {
|
||||
await SomeCollection.updateAsync(/* ... */); // [!code error] if this throws, process crashes
|
||||
}
|
||||
});
|
||||
|
||||
// Solution — wrap in try/catch
|
||||
SyncedCron.add({
|
||||
name: 'My Job',
|
||||
schedule(parser) { return parser.text('every 1 hour'); },
|
||||
async job() {
|
||||
try { // [!code highlight]
|
||||
await SomeCollection.updateAsync(/* ... */);
|
||||
} catch (error) { // [!code highlight]
|
||||
console.error('Cron job failed:', error); // [!code highlight]
|
||||
} // [!code highlight]
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
If a package does not support async callbacks, consider lazy-loading it inside `Meteor.startup()` to ensure the environment is fully initialized first.
|
||||
|
||||
## SimpleSchema and Collection2 Changes
|
||||
|
||||
**Why this happens:**
|
||||
|
||||
`aldeed:collection2` v4 (the Meteor 3-compatible version) now bundles `aldeed:simple-schema` internally as an Atmosphere package. This replaces the previous setup where you installed the `simpl-schema` npm package separately. The npm `simpl-schema` v3+ actually dropped all Meteor support, so the Atmosphere package is a hard-fork that maintains Meteor-specific features like Tracker reactivity.
|
||||
|
||||
**Common issues and solutions:**
|
||||
|
||||
1. **Do not use the npm `simpl-schema` v3+ with Collection2 v4** — they conflict. Collection2 v4 bundles the compatible `aldeed:simple-schema` automatically:
|
||||
|
||||
```bash
|
||||
# Remove the npm package if present
|
||||
meteor npm remove simpl-schema # [!code highlight]
|
||||
# Collection2 v4 bundles aldeed:simple-schema — no separate install needed
|
||||
```
|
||||
|
||||
2. **Static vs. dynamic loading** — Collection2 v4 offers two import modes:
|
||||
|
||||
```js
|
||||
// Static loading (default) — loaded immediately, increases initial bundle size
|
||||
import 'meteor/aldeed:collection2'; // [!code highlight]
|
||||
|
||||
// Dynamic loading — deferred, reduces initial bundle size
|
||||
import 'meteor/aldeed:collection2/dynamic'; // [!code highlight]
|
||||
// You must explicitly call load() before using schemas
|
||||
await Collection2.load(); // [!code highlight]
|
||||
```
|
||||
|
||||
If your schemas aren't being applied or you get errors about missing schema methods, ensure you're using the right loading mode and calling `Collection2.load()` if using dynamic imports.
|
||||
|
||||
3. **Dot-notation fields need explicit parent objects** — schemas using nested fields like `'address.city'` may need the parent `address` object declared explicitly.
|
||||
|
||||
## Package Loading Order Issues
|
||||
|
||||
**Why this happens:**
|
||||
|
||||
Meteor 3's stricter module system changes the order in which packages and their side effects are loaded. Packages that relied on implicit loading order (e.g., a package that injects itself globally before another package reads it) may break.
|
||||
|
||||
**How to solve it:**
|
||||
|
||||
1. Use explicit `import` statements in your entry point files (`client/main.js`, `server/main.js`) to control load order
|
||||
2. If a package needs to run before another, import it earlier in your entry point
|
||||
3. For packages that register side effects (like template helpers or collection extensions), ensure they are imported before the code that depends on them
|
||||
|
||||
## `Meteor.bindEnvironment` Required for External Callbacks
|
||||
|
||||
**Why this happens:**
|
||||
|
||||
Code running outside of Meteor's async context (e.g., in Express middleware, third-party library callbacks, or raw Node.js event handlers) does not have access to Meteor's environment variables or DDP context. This was sometimes silently handled by Fibers but now requires explicit wrapping.
|
||||
|
||||
**How to solve it:**
|
||||
|
||||
Wrap external callbacks with `Meteor.bindEnvironment`:
|
||||
|
||||
```js
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import { WebApp } from 'meteor/webapp';
|
||||
|
||||
// Problem — no Meteor context in Express handler
|
||||
WebApp.handlers.use('/webhook', (req, res) => {
|
||||
const user = Meteor.user(); // [!code error] throws error — no Meteor context
|
||||
});
|
||||
|
||||
// Solution — wrap with bindEnvironment
|
||||
WebApp.handlers.use('/webhook', Meteor.bindEnvironment(async (req, res) => { // [!code highlight]
|
||||
const user = await Meteor.userAsync(); // [!code highlight] works
|
||||
res.send('OK');
|
||||
})); // [!code highlight]
|
||||
```
|
||||
|
||||
## Monkey-Patching Timing Issues
|
||||
|
||||
**Why this happens:**
|
||||
|
||||
Packages or application code that monkey-patches Meteor APIs (e.g., overriding `Meteor.publish`, wrapping collection methods) may fail if the target API isn't available yet when the patching code runs. Meteor 3's module loading changes can alter the timing of when code executes.
|
||||
|
||||
**How to solve it:**
|
||||
|
||||
Wrap monkey-patching code in `Meteor.startup()`:
|
||||
|
||||
```js
|
||||
// Problem — patching may run before the target API is ready
|
||||
Meteor.publish = patchedPublish(Meteor.publish); // [!code error]
|
||||
|
||||
// Solution — defer to startup
|
||||
Meteor.startup(() => { // [!code highlight]
|
||||
Meteor.publish = patchedPublish(Meteor.publish); // [!code highlight]
|
||||
}); // [!code highlight]
|
||||
```
|
||||
|
||||
For more complex cases, create a dedicated internal package that controls load order through `api.use()` dependencies in `package.js`.
|
||||
|
||||
156
v3-docs/v3-migration-docs/guide/migration-strategy.md
Normal file
156
v3-docs/v3-migration-docs/guide/migration-strategy.md
Normal file
@@ -0,0 +1,156 @@
|
||||
# Migration Strategy
|
||||
|
||||
This guide provides a recommended step-by-step order for migrating your Meteor 2.x application to Meteor 3.x. It is based on real-world migration experiences from projects like [WeKan](https://github.com/wekan/wekan/pull/6205), [Wework](https://github.com/nate-strauser/wework/pull/126), and several community migration reports.
|
||||
|
||||
::: tip
|
||||
You don't have to complete every step before moving to the next. But following this general order will help you avoid common pitfalls and reduce the number of issues you face at each stage.
|
||||
:::
|
||||
|
||||
## Step 1: Update to the Latest Meteor 2.x
|
||||
|
||||
Before jumping to Meteor 3, update your project to the latest Meteor 2.x release (2.16+). This gives you access to compatibility shims and warnings that make the transition smoother.
|
||||
|
||||
```bash
|
||||
meteor update
|
||||
```
|
||||
|
||||
Meteor 2.8+ introduced `*Async` methods alongside the existing sync ones, so you can start migrating your code incrementally while everything still works. See [Migrating to Async in v2](../migrating-to-async-in-v2/index.md) for details.
|
||||
|
||||
## Step 2: Audit and Replace Unmaintained Packages
|
||||
|
||||
This is often the most time-consuming step. Many Atmosphere packages are unmaintained and will not work with Meteor 3.
|
||||
|
||||
1. Review your `.meteor/packages` file
|
||||
2. For each package, check if a Meteor 3-compatible version exists on [Packosphere](https://packosphere.com/) or the [Meteor Community Packages](https://github.com/Meteor-Community-Packages) GitHub org
|
||||
3. Replace or remove packages that have no compatible version
|
||||
|
||||
See [Package Replacements](./package-replacements.md) for a table of common replacements.
|
||||
|
||||
::: tip
|
||||
Reduce your package footprint as much as possible before upgrading. Fewer packages means fewer migration issues and faster build times.
|
||||
:::
|
||||
|
||||
## Step 3: Identify Sync Code with `WARN_WHEN_USING_OLD_API`
|
||||
|
||||
Run your app with this environment variable to find all places where you're using the old synchronous API:
|
||||
|
||||
```bash
|
||||
WARN_WHEN_USING_OLD_API=true meteor run
|
||||
```
|
||||
|
||||
This will log warnings for every sync MongoDB method call on the server (e.g., `findOne`, `insert`, `update`), helping you identify what needs to change.
|
||||
|
||||
## Step 4: Restructure Entry Points
|
||||
|
||||
Meteor 3 works best with explicit entry points. If your project relies on Meteor's implicit file loading, now is the time to restructure.
|
||||
|
||||
1. Create explicit `client/main.js` and `server/main.js` files (if you don't already have them)
|
||||
2. Configure `mainModule` in your `package.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"meteor": {
|
||||
"mainModule": {
|
||||
"client": "client/main.js",
|
||||
"server": "server/main.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
3. Replace ambient globals with explicit imports:
|
||||
|
||||
```js
|
||||
// Before: relying on global availability
|
||||
Template.hello.helpers({ /* ... */ });
|
||||
|
||||
// After: explicit import
|
||||
import { Template } from 'meteor/templating';
|
||||
Template.hello.helpers({ /* ... */ });
|
||||
```
|
||||
|
||||
::: warning
|
||||
This restructuring step can be substantial for large applications. WeKan's migration involved splitting collection definitions (shared models vs. server-only hooks/methods) and rewriting their entire boot sequence.
|
||||
:::
|
||||
|
||||
### Prepare for Express Early with `harry97:webapp`
|
||||
|
||||
Meteor 3 replaces Connect with Express in the `webapp` package. If your app uses `WebApp.connectHandlers` or custom middleware, you can start writing Express-compatible code **while still on Meteor 2.x** by using the [`harry97:webapp`](https://github.com/harryadel/harry97-webapp) package — a backport of Meteor 3's Express-based webapp for Meteor 2.17.
|
||||
|
||||
```bash
|
||||
meteor add harry97:webapp
|
||||
```
|
||||
|
||||
This gives you access to the same Express API that Meteor 3 uses (`WebApp.handlers`, `WebApp.express`, etc.) with backward-compatible aliases for `WebApp.connectHandlers` and `WebApp.rawConnectHandlers`. When you eventually upgrade to Meteor 3, your middleware code will already be compatible — just remove `harry97:webapp` and the core `webapp` package takes over.
|
||||
|
||||
## Step 5: Convert Sync Code to Async
|
||||
|
||||
On the server, all MongoDB collection methods must use their `*Async` counterparts:
|
||||
|
||||
```js
|
||||
// Before
|
||||
const doc = MyCollection.findOne({ _id: id });
|
||||
MyCollection.insert({ name: 'test' });
|
||||
MyCollection.update({ _id: id }, { $set: { name: 'updated' } });
|
||||
|
||||
// After
|
||||
const doc = await MyCollection.findOneAsync({ _id: id });
|
||||
await MyCollection.insertAsync({ name: 'test' });
|
||||
await MyCollection.updateAsync({ _id: id }, { $set: { name: 'updated' } });
|
||||
```
|
||||
|
||||
Don't forget to also convert:
|
||||
- `Meteor.user()` → `await Meteor.userAsync()`
|
||||
- `Meteor.call()` → `await Meteor.callAsync()`
|
||||
- `Email.send()` → `await Email.sendAsync()`
|
||||
- `cursor.fetch()` → `await cursor.fetchAsync()`
|
||||
- `cursor.count()` → `await cursor.countAsync()`
|
||||
- `cursor.forEach()` → `await cursor.forEachAsync()`
|
||||
- `cursor.map()` → `await cursor.mapAsync()`
|
||||
- `createIndex()` → `await createIndexAsync()`
|
||||
|
||||
The [jscodeshift codemod](https://github.com/minhna/meteor-async-migration) can automate much of this work.
|
||||
|
||||
## Step 6: Replace Deprecated Patterns
|
||||
|
||||
Remove patterns that depend on Fibers or other removed APIs. See [Removing Fibers Patterns](./removing-fibers.md) for detailed before/after examples. Key replacements:
|
||||
|
||||
- `Meteor.wrapAsync()` → `util.promisify()` or manual Promises
|
||||
- `Promise.await()` → `await` in an `async` function
|
||||
- `HTTP.call()` → `await fetch()` (using the `meteor/fetch` core package)
|
||||
- `Npm.require('fibers')` → remove entirely
|
||||
|
||||
## Step 7: Run `meteor update` to 3.x
|
||||
|
||||
Once your code is async-ready and your packages are compatible:
|
||||
|
||||
```bash
|
||||
meteor update
|
||||
```
|
||||
|
||||
Then clean up:
|
||||
|
||||
```bash
|
||||
rm -rf node_modules package-lock.json
|
||||
meteor npm install
|
||||
```
|
||||
|
||||
If you encounter memory issues during the update, see [Common Errors](./common-errors.md#cannot-enlarge-memory-array).
|
||||
|
||||
## Step 8: Test and Iterate
|
||||
|
||||
After upgrading:
|
||||
|
||||
1. Run your app and check the server console for errors
|
||||
2. Test each feature systematically — async issues often manifest as silent failures or unexpected `undefined` values
|
||||
3. Pay special attention to:
|
||||
- Meteor methods and publications
|
||||
- Collection hooks and allow/deny callbacks
|
||||
- Cron jobs and background tasks
|
||||
- Third-party API integrations
|
||||
|
||||
::: tip
|
||||
Migrate one module at a time when possible. This lets you catch and fix errors incrementally instead of facing them all at once.
|
||||
:::
|
||||
|
||||
For real-world migration writeups, migration PRs, and more supporting links, see [Migration Reports and External Resources](../index.md#migration-reports-and-external-resources).
|
||||
86
v3-docs/v3-migration-docs/guide/package-replacements.md
Normal file
86
v3-docs/v3-migration-docs/guide/package-replacements.md
Normal file
@@ -0,0 +1,86 @@
|
||||
# Package Replacements
|
||||
|
||||
One of the biggest challenges in migrating to Meteor 3 is dealing with unmaintained Atmosphere packages. This page lists common packages that need replacement and their recommended alternatives.
|
||||
|
||||
::: tip
|
||||
Before replacing a package, check [Packosphere](https://packosphere.com/) — many packages have already been updated for Meteor 3 compatibility. Also check the [Meteor Community Packages](https://github.com/Meteor-Community-Packages) GitHub org and the [community tracking spreadsheet](https://docs.google.com/spreadsheets/d/1JbUZmJab3owZ9LV71Ubto32YX_QWQljRypJTOQupxL8/edit?usp=sharing).
|
||||
:::
|
||||
|
||||
## Replacement Strategy
|
||||
|
||||
When a package doesn't have a Meteor 3-compatible version:
|
||||
|
||||
1. **Check Packosphere** for an updated version or fork
|
||||
2. **Check the [Community Packages](https://github.com/Meteor-Community-Packages) org** — many packages have been transferred here for ongoing maintenance
|
||||
3. **Look for an npm replacement** — Meteor 3 has better npm integration, so npm packages are often viable
|
||||
4. **Inline the functionality** — if the package is small, consider copying the relevant code into your project
|
||||
5. **Fork and patch** — as a last resort, fork the package and make the minimum changes for Meteor 3 compatibility
|
||||
|
||||
## Routing
|
||||
|
||||
| Old Package | Replacement | Notes |
|
||||
|---|---|---|
|
||||
| `kadira:flow-router` | `ostrio:flow-router-extra` | Drop-in replacement with additional features |
|
||||
| `iron:router` | `ostrio:flow-router-extra` or `vlasky:galvanized-iron-router` | `galvanized-iron-router` is closest to iron:router's API |
|
||||
|
||||
## Data & Collections
|
||||
|
||||
| Old Package | Replacement | Notes |
|
||||
|---|---|---|
|
||||
| `aldeed:collection2` | `aldeed:collection2@4.0.0` | Updated for Meteor 3; now bundles `aldeed:simple-schema` internally — remove the `simpl-schema` npm package |
|
||||
| `aldeed:schema-index` | `communitypackages:schema-index` | Transferred to Community Packages |
|
||||
| `aldeed:schema-deny` | `communitypackages:schema-deny` | Transferred to Community Packages |
|
||||
| `konecty:mongo-counter` | Inline MongoDB `$inc` operations | Simple enough to implement directly |
|
||||
| `cottz:publish-relations` | `reywood:publish-composite` or [meteor-reactive-publish](https://github.com/nachocodoner/meteor-reactive-publish) | Both support reactive joins |
|
||||
|
||||
## Scheduling & Background Jobs
|
||||
|
||||
| Old Package | Replacement | Notes |
|
||||
|---|---|---|
|
||||
| `percolate:synced-cron` | `quave:synced-cron` | Community-maintained fork with async support |
|
||||
|
||||
## HTTP & APIs
|
||||
|
||||
| Old Package | Replacement | Notes |
|
||||
|---|---|---|
|
||||
| `http` (Meteor package) | `fetch` (`meteor/fetch`) | Core Meteor package; uses the standard `fetch` API |
|
||||
| `simple:json-routes` | `WebApp.handlers` (Express) | Meteor 3 uses Express — see [Breaking Changes](../breaking-changes/index.md#webapp-switches-to-express-5) |
|
||||
| Restivus | `WebApp.handlers` (Express) | Build REST endpoints directly with Express routes |
|
||||
|
||||
## Accounts & Auth
|
||||
|
||||
| Old Package | Replacement | Notes |
|
||||
|---|---|---|
|
||||
| `useraccounts:*` | `communitypackages:*` | Community-maintained alternatives |
|
||||
|
||||
## UI & Templates
|
||||
|
||||
| Old Package | Replacement | Notes |
|
||||
|---|---|---|
|
||||
| `mquandalle:jade` | Remove, convert to Spacebars/HTML | Jade template support was dropped |
|
||||
| `peerlibrary:blaze-components` | Native Blaze templates | Convert to standard `Template` patterns |
|
||||
| `meteorhacks:subs-manager` | Remove | Not needed with modern Meteor's subscription handling |
|
||||
|
||||
## Build Tools & CSS
|
||||
|
||||
| Old Package | Replacement | Notes |
|
||||
|---|---|---|
|
||||
| `fourseven:scss` | Remove (if using rspack on Meteor 3.4+) | rspack has native SCSS support |
|
||||
|
||||
::: info
|
||||
For projects on Meteor 3.4+ with rspack, many build-tool-related Atmosphere packages (SCSS, Less, etc.) are no longer needed as rspack handles these natively. See the [Meteor-Rspack integration guide](https://docs.meteor.com/about/modern-build-stack/rspack-bundler-integration.html) for setup details and supported features.
|
||||
:::
|
||||
|
||||
## Utilities
|
||||
|
||||
| Old Package | Replacement | Notes |
|
||||
|---|---|---|
|
||||
| `ongoworks:speakingurl` | `limax` (npm) | `npm install limax` |
|
||||
| `underscore` | Native JavaScript | `Array.map`, `Object.keys`, `Array.filter`, spread syntax, etc. |
|
||||
| `moment` | Native `Date`, `date-fns`, or `luxon` (npm) | `moment` is in maintenance mode |
|
||||
|
||||
## Community Resources
|
||||
|
||||
- [Community Package Migration Thread](https://forums.meteor.com/t/looking-for-help-migrating-packages-to-meteor-3-0/60985) — ongoing community discussion about package migration status
|
||||
- [Package Compatibility Spreadsheet](https://docs.google.com/spreadsheets/d/1JbUZmJab3owZ9LV71Ubto32YX_QWQljRypJTOQupxL8/edit?usp=sharing) — collaborative tracking of package compatibility
|
||||
- [Upgrading Packages Guide](../breaking-changes/upgrading-packages.md) — how to update your own packages for Meteor 3 compatibility
|
||||
186
v3-docs/v3-migration-docs/guide/removing-fibers.md
Normal file
186
v3-docs/v3-migration-docs/guide/removing-fibers.md
Normal file
@@ -0,0 +1,186 @@
|
||||
# Removing Fibers Patterns
|
||||
|
||||
Meteor 3 removes the Fibers dependency entirely. This page shows before/after examples for the most common Fibers-dependent patterns you'll encounter during migration.
|
||||
|
||||
For background on why Fibers was removed, see the [FAQ](../frequently-asked-questions/index.md#what-is-fibers).
|
||||
|
||||
## `Meteor.wrapAsync` → Promises
|
||||
|
||||
`Meteor.wrapAsync` no longer exists. Replace it with `util.promisify` or manual Promise wrapping.
|
||||
|
||||
```js
|
||||
// Before
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
|
||||
const syncFunction = Meteor.wrapAsync(someCallbackFunction); // [!code error]
|
||||
const result = syncFunction(arg1, arg2); // [!code error]
|
||||
|
||||
// After — using util.promisify
|
||||
import { promisify } from 'util';
|
||||
|
||||
const asyncFunction = promisify(someCallbackFunction);
|
||||
const result = await asyncFunction(arg1, arg2); // [!code highlight]
|
||||
```
|
||||
|
||||
If the callback doesn't follow the standard `(error, result)` pattern, wrap it manually:
|
||||
|
||||
```js
|
||||
// After — manual Promise wrapping
|
||||
function asyncFunction(arg1, arg2) {
|
||||
return new Promise((resolve, reject) => {
|
||||
someCallbackFunction(arg1, arg2, (error, result) => {
|
||||
if (error) reject(error);
|
||||
else resolve(result);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const result = await asyncFunction(arg1, arg2); // [!code highlight]
|
||||
```
|
||||
|
||||
## `Promise.await` → `async/await`
|
||||
|
||||
`Promise.await` was a Fibers-based synchronous wait. Replace with standard `await` inside an `async` function.
|
||||
|
||||
```js
|
||||
// Before
|
||||
const result = Promise.await(someAsyncOperation()); // [!code error]
|
||||
|
||||
// After
|
||||
const result = await someAsyncOperation(); // [!code highlight]
|
||||
```
|
||||
|
||||
::: warning
|
||||
The function containing `await` must be declared as `async`. This often means you need to make the calling function async too, which can cascade up the call chain.
|
||||
:::
|
||||
|
||||
## `Npm.require('fibers')` → Remove
|
||||
|
||||
Any direct usage of the `fibers` npm module must be removed entirely.
|
||||
|
||||
```js
|
||||
// Before
|
||||
const Fiber = Npm.require('fibers'); // [!code error]
|
||||
const Future = Npm.require('fibers/future'); // [!code error]
|
||||
|
||||
const future = new Future(); // [!code error]
|
||||
someCallback((err, result) => { // [!code error]
|
||||
if (err) future.throw(err); // [!code error]
|
||||
else future.return(result); // [!code error]
|
||||
}); // [!code error]
|
||||
const result = future.wait(); // [!code error]
|
||||
|
||||
// After
|
||||
const result = await new Promise((resolve, reject) => { // [!code highlight]
|
||||
someCallback((err, result) => { // [!code highlight]
|
||||
if (err) reject(err); // [!code highlight]
|
||||
else resolve(result); // [!code highlight]
|
||||
}); // [!code highlight]
|
||||
}); // [!code highlight]
|
||||
```
|
||||
|
||||
## `HTTP.call` → `fetch`
|
||||
|
||||
The old `HTTP` package is replaced by the `fetch` core Meteor package.
|
||||
|
||||
```js
|
||||
// Before
|
||||
import { HTTP } from 'meteor/http'; // [!code error]
|
||||
|
||||
const response = HTTP.call('GET', 'https://api.example.com/data', { // [!code error]
|
||||
headers: { Authorization: `Bearer ${token}` } // [!code error]
|
||||
}); // [!code error]
|
||||
const data = response.data; // [!code error]
|
||||
|
||||
// After
|
||||
import { fetch } from 'meteor/fetch'; // [!code highlight]
|
||||
|
||||
const response = await fetch('https://api.example.com/data', { // [!code highlight]
|
||||
headers: { Authorization: `Bearer ${token}` } // [!code highlight]
|
||||
}); // [!code highlight]
|
||||
const data = await response.json(); // [!code highlight]
|
||||
```
|
||||
|
||||
For POST requests:
|
||||
|
||||
```js
|
||||
// Before
|
||||
HTTP.call('POST', url, { // [!code error]
|
||||
data: { key: 'value' } // [!code error]
|
||||
}); // [!code error]
|
||||
|
||||
// After
|
||||
await fetch(url, { // [!code highlight]
|
||||
method: 'POST', // [!code highlight]
|
||||
headers: { 'Content-Type': 'application/json' }, // [!code highlight]
|
||||
body: JSON.stringify({ key: 'value' }) // [!code highlight]
|
||||
}); // [!code highlight]
|
||||
```
|
||||
|
||||
## `Email.send` → `Email.sendAsync`
|
||||
|
||||
```js
|
||||
// Before
|
||||
import { Email } from 'meteor/email';
|
||||
|
||||
Email.send({ // [!code error]
|
||||
to: 'user@example.com',
|
||||
from: 'noreply@example.com',
|
||||
subject: 'Hello',
|
||||
text: 'World'
|
||||
}); // [!code error]
|
||||
|
||||
// After
|
||||
await Email.sendAsync({ // [!code highlight]
|
||||
to: 'user@example.com',
|
||||
from: 'noreply@example.com',
|
||||
subject: 'Hello',
|
||||
text: 'World'
|
||||
}); // [!code highlight]
|
||||
```
|
||||
|
||||
## Synchronous `createIndex` → `createIndexAsync`
|
||||
|
||||
```js
|
||||
// Before
|
||||
MyCollection._ensureIndex({ email: 1 }); // [!code error]
|
||||
|
||||
// After
|
||||
await MyCollection.createIndexAsync({ email: 1 }); // [!code highlight]
|
||||
```
|
||||
|
||||
## Callback-Based Patterns → `async/await`
|
||||
|
||||
Many older Meteor patterns used callbacks or synchronous Fiber-based code. Convert these to `async/await`:
|
||||
|
||||
```js
|
||||
// Before — callback in Meteor.startup
|
||||
Meteor.startup(() => {
|
||||
const settings = Settings.findOne({ key: 'app' }); // [!code error]
|
||||
if (!settings) {
|
||||
Settings.insert({ key: 'app', value: defaults }); // [!code error]
|
||||
}
|
||||
});
|
||||
|
||||
// After
|
||||
Meteor.startup(async () => { // [!code highlight]
|
||||
const settings = await Settings.findOneAsync({ key: 'app' }); // [!code highlight]
|
||||
if (!settings) {
|
||||
await Settings.insertAsync({ key: 'app', value: defaults }); // [!code highlight]
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
```js
|
||||
// Before — synchronous publish
|
||||
Meteor.publish('userPosts', function () {
|
||||
const user = Meteor.users.findOne(this.userId); // [!code error]
|
||||
return Posts.find({ authorId: user._id }); // [!code error]
|
||||
});
|
||||
|
||||
// After
|
||||
Meteor.publish('userPosts', async function () { // [!code highlight]
|
||||
const user = await Meteor.users.findOneAsync(this.userId); // [!code highlight]
|
||||
return Posts.find({ authorId: user._id });
|
||||
});
|
||||
```
|
||||
@@ -55,12 +55,15 @@ Which will install the necessary packages using the latest Node.js version from
|
||||
|
||||
This guide covers the necessary topics for migrating your application from Meteor 2.x to Meteor 3.0, including:
|
||||
|
||||
- [Migration Strategy](./guide/migration-strategy.md), a recommended step-by-step migration order based on real-world experience.
|
||||
- [Frequently Asked Questions](./frequently-asked-questions/index.md), answers to common questions.
|
||||
- [Breaking Changes](./breaking-changes/index.md), an overview of the changes that will affect your application.
|
||||
|
||||
- [Meteor.call x Meteor.callAsync](./breaking-changes/call-x-callAsync.md), why should you change your methods to use `Async` methods.
|
||||
- [Upgrading packages](./breaking-changes/upgrading-packages.md), how to upgrade your packages to the be compatible with Meteor v3.
|
||||
|
||||
- [Package Replacements](./guide/package-replacements.md), common unmaintained packages and their Meteor 3-compatible alternatives.
|
||||
- [Removing Fibers Patterns](./guide/removing-fibers.md), before/after examples for replacing Fibers-dependent code.
|
||||
- [How async functions work and how to use them](./api/async-functions.md), a how-to guide in how to use async functions and helpers for Meteor.
|
||||
- [Renamed Functions](./api/renamed-functions.md), a list of functions that were renamed in Meteor v3.
|
||||
- [Removed Functions](./api/removed-functions.md), a list of functions that were removed in Meteor v3.
|
||||
@@ -69,15 +72,23 @@ This guide covers the necessary topics for migrating your application from Meteo
|
||||
- [Blaze in Meteor v3](./front-end/blaze.md), how to migrate your Blaze code to Meteor v3.
|
||||
|
||||
- [Migrating to Async in Meteor 2.x](migrating-to-async-in-v2/index.md), how can you migrate your application to Meteor v3 while in 2.x.
|
||||
- [Common Errors](./guide/common-errors.md), documented errors and solutions you may encounter during migration.
|
||||
|
||||
## External Resources
|
||||
|
||||
We are aware of these articles and guides to assist with your migration:
|
||||
## Migration Reports and External Resources
|
||||
|
||||
We are aware of these migration reports, articles, guides, and videos to assist with your migration:
|
||||
|
||||
- [Prepare your Meteor.js project for the big 3.0 release](https://dev.to/jankapunkt/prepare-your-meteorjs-project-for-the-big-30-release-14bf)
|
||||
- [Gradually upgrading a Meteor.js project to 3.0](https://dev.to/meteor/gradually-upgrading-a-meteorjs-project-to-30-5aj0)
|
||||
- [Meteor 3.0 Migration Guide, from Daniel](https://docs.google.com/document/d/1XxHE5MQaS0-85HQ-bkiXxmGlYi41ggkX3F-9Rjb9HhE/edit#heading=h.65xi3waq9bb)
|
||||
- [Illustreets Migration Guide, large SaaS migrated to 3.0](https://forums.meteor.com/t/large-saas-migrated-to-3-0/61113) & their how-to [post](https://forums.meteor.com/t/meteor-3-0-beta-6-is-out/61277/12)
|
||||
- [Atmosphere Migration from Meteor 2.x to Meteor 3.4 with Rspack](https://blog.galaxycloud.app/meteorjs-2-to-3-blaze-migration-rspack/)
|
||||
- [The Meteor 3.0 Migration: A Space Exploration Mission](https://dev.to/meteor/the-meteor-30-migration-a-space-exploration-mission-3gb5) — Collection2, collection-hooks, SCSS, Cordova migration experience
|
||||
- Dev Diary series by Harry Adel: [#24](https://harryadel.com/dev-diary-24/) (package audit & strategy), [#25](https://harryadel.com/dev-diary-25/) (auth packages & Fibers removal), [#26](https://harryadel.com/dev-diary-26/) (app restructuring & final migration)
|
||||
- [WeKan Meteor 3 Migration PR](https://github.com/wekan/wekan/pull/6205) — large Blaze app migration with 23 model files
|
||||
- [Wework Meteor 3.4 Migration PR](https://github.com/nate-strauser/wework/pull/126) — iron:router replacement, REST API migration
|
||||
- [Community Package Migration Thread](https://forums.meteor.com/t/looking-for-help-migrating-packages-to-meteor-3-0/60985) — ongoing community discussion and tracking
|
||||
- [Package Compatibility Spreadsheet](https://docs.google.com/spreadsheets/d/1JbUZmJab3owZ9LV71Ubto32YX_QWQljRypJTOQupxL8/edit?usp=sharing) — collaborative tracking of package compatibility
|
||||
|
||||
### Videos
|
||||
|
||||
|
||||
Reference in New Issue
Block a user