Merge branch 'release-3.3.1' into modern-bundler-integration

This commit is contained in:
Nacho Codoñer
2025-07-23 14:16:53 +02:00
92 changed files with 1820 additions and 690 deletions

View File

@@ -79,7 +79,7 @@ run_save_node_bin: &run_save_node_bin
build_machine_environment:
&build_machine_environment # Specify that we want an actual machine (ala Circle 1.0), not a Docker image.
docker:
- image: meteor/circleci:2024.09.11-android-34-node-20
- image: meteor/circleci:2025.07.8-android-35-node-22
resource_class: large
environment:
# This multiplier scales the waitSecs for selftests.
@@ -756,7 +756,7 @@ jobs:
Docs:
docker:
# This Node version should match that in the meteor/docs CircleCI config.
- image: meteor/circleci:2024.09.11-android-34-node-20
- image: meteor/circleci:2025.07.8-android-35-node-22
resource_class: large
environment:
CHECKOUT_METEOR_DOCS: /home/circleci/test_docs

192
.github/labeler.yml vendored
View File

@@ -1,124 +1,178 @@
Project:Accounts:Password:
- packages/accounts-password/**/*
- changed-files:
- any-glob-to-any-file: packages/accounts-password/**/*
Project:Accounts:UI:
- packages/meteor-developer-config-ui/**/*
- packages/github-config-ui/**/*
- packages/google-config-ui/**/*
- packages/twitter-config-ui/**/*
- packages/facebook-config-ui/**/*
- packages/accounts-ui/**/*
- packages/accounts-ui-unstyled/**/*
- changed-files:
- any-glob-to-any-file:
- packages/meteor-developer-config-ui/**/*
- packages/github-config-ui/**/*
- packages/google-config-ui/**/*
- packages/twitter-config-ui/**/*
- packages/facebook-config-ui/**/*
- packages/accounts-ui/**/*
- packages/accounts-ui-unstyled/**/*
Project:CSS:
- packages/non-core/less/**/*
- packages/minifier-css/**/*
- packages/standard-minifier-css/**/*
- changed-files:
- any-glob-to-any-file:
- packages/non-core/less/**/*
- packages/minifier-css/**/*
- packages/standard-minifier-css/**/*
Project:DDP:
- packages/ddp-common/**/*
- packages/ddp-rate-limiter/**/*
- packages/ddp-server/**/*
- packages/ddp-client/**/*
- packages/ddp/**/*
- packages/socket-stream-client/**/*
- changed-files:
- any-glob-to-any-file:
- packages/ddp-common/**/*
- packages/ddp-rate-limiter/**/*
- packages/ddp-server/**/*
- packages/ddp-client/**/*
- packages/ddp/**/*
- packages/socket-stream-client/**/*
Project:EJSON:
- packages/ejson/**/*
- changed-files:
- any-glob-to-any-file: packages/ejson/**/*
Project:HMR:
- packages/hot-code-push/**/*
- packages/hot-module-replacement/**/*
- changed-files:
- any-glob-to-any-file:
- packages/hot-code-push/**/*
- packages/hot-module-replacement/**/*
Project:Isobuild:Minifiers:
- packages/minifier-css/**/*
- packages/minifier-js/**/*
- packages/standard-minifier-js/**/*
- packages/standard-minifier-css/**/*
- packages/standard-minifiers/**/*
- changed-files:
- any-glob-to-any-file:
- packages/minifier-css/**/*
- packages/minifier-js/**/*
- packages/standard-minifier-js/**/*
- packages/standard-minifier-css/**/*
- packages/standard-minifiers/**/*
Project:Isobuild:
- tools/isobuild/**/*
- changed-files:
- any-glob-to-any-file:
- tools/isobuild/**/*
Project:JS Environment:Typescript:
- packages/typescript/**/*
- changed-files:
- any-glob-to-any-file:
- packages/typescript/**/*
Project:JS Environment:
- packages/babel-compiler/**/*
- packages/babel-runtime/**/*
- packages/ecmascript/**/*
- packages/ecmascript-runtime/**/*
- packages/ecmascript-runtime-client/**/*
- packages/ecmascript-runtime-server/**/*
- packages/es5-shim/**/*
- packages/jshint/**/*
- changed-files:
- any-glob-to-any-file:
- packages/babel-compiler/**/*
- packages/babel-runtime/**/*
- packages/ecmascript/**/*
- packages/ecmascript-runtime/**/*
- packages/ecmascript-runtime-client/**/*
- packages/ecmascript-runtime-server/**/*
- packages/es5-shim/**/*
- packages/jshint/**/*
Project:Livequery:
- packages/livedata/**/*
- changed-files:
- any-glob-to-any-file:
- packages/livedata/**/*
Project:Minimongo:
- packages/minimongo
- changed-files:
- any-glob-to-any-file:
- packages/minimongo
Project:Mobile:
- tools/cordova/**/*
- packages/launch-screen/**/*
- packages/mobile-experience/**/*
- packages/mobile-status-bar/**/*
- changed-files:
- any-glob-to-any-file:
- tools/cordova/**/*
- packages/launch-screen/**/*
- packages/mobile-experience/**/*
- packages/mobile-status-bar/**/*
Project:Mongo Driver:
- packages/mongo/**/*
- packages/mongo-dev-server/**/*
- packages/mongo-id/**/*
- packages/mongo-livedata/**/*
- packages/disable-oplog/**/*
- packages/non-core/mongo-decimal/**/*
- changed-files:
- any-glob-to-any-file:
- packages/mongo/**/*
- packages/mongo-dev-server/**/*
- packages/mongo-id/**/*
- packages/mongo-livedata/**/*
- packages/disable-oplog/**/*
- packages/non-core/mongo-decimal/**/*
Project:NPM:
- npm-packages/**/*
- changed-files:
- any-glob-to-any-file:
- npm-packages/**/*
Project:Release Process:
- scripts/**/*
- changed-files:
- any-glob-to-any-file:
- scripts/**/*
Project:Tool:
- tools/**/*
- packages/meteor-tool/**/*
- changed-files:
- any-glob-to-any-file:
- tools/**/*
- packages/meteor-tool/**/*
Project:Tool:Shell:
- tools/console/**/*
- changed-files:
- any-glob-to-any-file:
- tools/console/**/*
Project:Utilities:Email:
- packages/email/**/*
- changed-files:
- any-glob-to-any-file:
- packages/email/**/*
Project:Utilities:HTTP:
- packages/deprecated/http/**/*
- packages/fetch/**/*
- packages/url/**/*
- changed-files:
- any-glob-to-any-file:
- packages/deprecated/http/**/*
- packages/fetch/**/*
- packages/url/**/*
Project:Webapp:
- packages/webapp/**/*
- packages/webapp-hashing/**/*
- changed-files:
- any-glob-to-any-file:
- packages/webapp/**/*
- packages/webapp-hashing/**/*
Project:Windows:
- scripts/windows/**/*
- changed-files:
- any-glob-to-any-file:
- scripts/windows/**/*
Project:Webapp:Browser Policy:
- packages/browser-policy/**/*
- packages/browser-policy-common/**/*
- packages/browser-policy-content/**/*
- packages/browser-policy-framing/**/*
- changed-files:
- any-glob-to-any-file:
- packages/browser-policy/**/*
- packages/browser-policy-common/**/*
- packages/browser-policy-content/**/*
- packages/browser-policy-framing/**/*
Project:Examples:
- tools/cli/example-repositories.js
- changed-files:
- any-glob-to-any-file:
- tools/cli/example-repositories.js
Project:Dynamic Import:
- packages/dynamic-import/**/*
- changed-files:
- any-glob-to-any-file:
- packages/dynamic-import/**/*
Project:Docs:
- docs/**/*
- v3-docs/**/*
- changed-files:
- any-glob-to-any-file:
- docs/**/*
- v3-docs/**/*
Project:Guide:
- guide/**/*
- changed-files:
- any-glob-to-any-file:
- guide/**/*
github_actions:
- ./github/**/*
- changed-files:
- any-glob-to-any-file:
- ./github/**/*

View File

@@ -17,6 +17,6 @@ jobs:
label:
runs-on: ubuntu-latest
steps:
- uses: actions/labeler@v4
- uses: actions/labeler@v5
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"

View File

@@ -4,7 +4,7 @@ dist: jammy
sudo: required
services: xvfb
node_js:
- "22.16.0"
- "22.17.0"
cache:
directories:
- ".meteor"

View File

@@ -151,7 +151,8 @@ password-based users or from an external service login flow. `options` may come
from an untrusted client so make sure to validate any values you read from
it. The `user` argument is created on the server and contains a
proposed user object with all the automatically generated fields
required for the user to log in, including the `_id`.
required for the user to log in, including a temporary `_id` (the final _id is
generated upon document insertion and not available in this function).
The function should return the user document (either the one passed in or a
newly-created object) with whatever modifications are desired. The returned

2
meteor
View File

@@ -1,6 +1,6 @@
#!/usr/bin/env bash
BUNDLE_VERSION=22.16.0.1
BUNDLE_VERSION=22.17.1.1
# OS Check. Put here because here is where we download the precompiled
# bundles that are arch specific.

View File

@@ -1 +0,0 @@
/node_modules

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,7 @@
"name": "meteor-node-stubs",
"author": "Ben Newman <ben@meteor.com>",
"description": "Stub implementations of Node built-in modules, a la Browserify",
"version": "1.2.13",
"version": "1.2.21",
"main": "index.js",
"license": "MIT",
"homepage": "https://github.com/meteor/meteor/blob/devel/npm-packages/meteor-node-stubs/README.md",

View File

@@ -689,7 +689,7 @@ export class AccountsClient extends AccountsCommon {
/**
* @summary Register a function to call when a reset password link is clicked
* in an email sent by
* [`Accounts.sendResetPasswordEmail`](#accounts_sendresetpasswordemail).
* [`Accounts.sendResetPasswordEmail`](#Accounts-sendResetPasswordEmail).
* This function should be called in top-level code, not inside
* `Meteor.startup()`.
* @memberof! Accounts
@@ -697,7 +697,7 @@ export class AccountsClient extends AccountsCommon {
* @param {Function} callback The function to call. It is given two arguments:
*
* 1. `token`: A password reset token that can be passed to
* [`Accounts.resetPassword`](#accounts_resetpassword).
* [`Accounts.resetPassword`](#Accounts-resetPassword).
* 2. `done`: A function to call when the password reset UI flow is complete. The normal
* login process is suspended until this function is called, so that the
* password for user A can be reset even if user B was logged in.
@@ -715,7 +715,7 @@ export class AccountsClient extends AccountsCommon {
/**
* @summary Register a function to call when an email verification link is
* clicked in an email sent by
* [`Accounts.sendVerificationEmail`](#accounts_sendverificationemail).
* [`Accounts.sendVerificationEmail`](#Accounts-sendVerificationEmail).
* This function should be called in top-level code, not inside
* `Meteor.startup()`.
* @memberof! Accounts
@@ -723,7 +723,7 @@ export class AccountsClient extends AccountsCommon {
* @param {Function} callback The function to call. It is given two arguments:
*
* 1. `token`: An email verification token that can be passed to
* [`Accounts.verifyEmail`](#accounts_verifyemail).
* [`Accounts.verifyEmail`](#Accounts-verifyEmail).
* 2. `done`: A function to call when the email verification UI flow is complete.
* The normal login process is suspended until this function is called, so
* that the user can be notified that they are verifying their email before
@@ -742,7 +742,7 @@ export class AccountsClient extends AccountsCommon {
/**
* @summary Register a function to call when an account enrollment link is
* clicked in an email sent by
* [`Accounts.sendEnrollmentEmail`](#accounts_sendenrollmentemail).
* [`Accounts.sendEnrollmentEmail`](#Accounts-sendEnrollmentEmail).
* This function should be called in top-level code, not inside
* `Meteor.startup()`.
* @memberof! Accounts
@@ -750,7 +750,7 @@ export class AccountsClient extends AccountsCommon {
* @param {Function} callback The function to call. It is given two arguments:
*
* 1. `token`: A password reset token that can be passed to
* [`Accounts.resetPassword`](#accounts_resetpassword) to give the newly
* [`Accounts.resetPassword`](#Accounts-resetPassword) to give the newly
* enrolled account a password.
* 2. `done`: A function to call when the enrollment UI flow is complete.
* The normal login process is suspended until this function is called, so that

View File

@@ -34,6 +34,9 @@ BCp.isVerbose = function(config = getMeteorConfig()) {
if (config?.modern?.transpiler?.verbose) {
return true;
}
if (config?.modern?.verbose) {
return true;
}
if (config?.verbose) {
return true;
}
@@ -109,7 +112,9 @@ BCp.initializeMeteorAppSwcrc = function () {
let currentLastModifiedConfigTime;
if (hasSwcJs) {
// For dynamic JS files, first get the resolved configuration
const resolvedConfig = lastModifiedSwcConfig || getMeteorAppSwcrc(swcFile);
const resolvedConfig = lastModifiedSwcConfigTime?.includes(`${fileModTime}`)
? lastModifiedSwcConfig || getMeteorAppSwcrc(swcFile)
: getMeteorAppSwcrc(swcFile);
// Calculate a hash of the resolved configuration to detect changes
const contentHash = crypto
.createHash('sha256')
@@ -129,8 +134,10 @@ BCp.initializeMeteorAppSwcrc = function () {
lastModifiedSwcConfig = getMeteorAppSwcrc(swcFile);
if (this.isVerbose()) {
logConfigBlock('SWC Config', lastModifiedSwcConfig);
logConfigBlock('SWC Custom Config', lastModifiedSwcConfig);
}
this._swcIncompatible = {};
}
return lastModifiedSwcConfig;
};
@@ -145,6 +152,42 @@ BCp.initializeMeteorAppLegacyConfig = function () {
return lastModifiedSwcConfig;
};
// Helper function to check if @swc/helpers is available
function hasSwcHelpers() {
return fs.existsSync(`${getMeteorAppDir()}/node_modules/@swc/helpers`);
}
// Helper function to log friendly messages about SWC helpers
function logSwcHelpersStatus(isAvailable) {
const label = color('[SWC Helpers]', 36);
if (isAvailable) {
// Green message for when helpers are available
console.log(`${label} ${color('✓ @swc/helpers is available in your project!', 32)}`);
console.log(` ${color('Benefits:', 32)}`);
console.log(` ${color('• Smaller bundle size: External helpers reduce code duplication', 32)}`);
console.log(` ${color('• Faster loads: less code to parse on first download', 32)}`);
console.log(` ${color('• Optional caching: separate vendor chunk can be cached by browsers', 32)}`);
} else {
// Yellow message for when helpers are not available
console.log(`${label} ${color('⚠ @swc/helpers is not available in your project', 33)}`);
console.log(` ${color('Suggestion:', 33)}`);
console.log(` ${color('• Add @swc/helpers to your project:', 33)}`);
console.log(` ${color('meteor npm install --save @swc/helpers', 33)}`);
console.log(` ${color('• This will reduce bundle size and improve performance', 33)}`);
}
console.log();
}
let hasSwcHelpersAvailable = false;
BCp.initializeMeteorAppSwcHelpersAvailable = function () {
hasSwcHelpersAvailable = hasSwcHelpers();
if (this.isVerbose()) {
logSwcHelpersStatus(hasSwcHelpersAvailable);
}
return hasSwcHelpersAvailable;
};
BCp.processFilesForTarget = function (inputFiles) {
var compiler = this;
@@ -154,6 +197,7 @@ BCp.processFilesForTarget = function (inputFiles) {
this.initializeMeteorAppConfig();
this.initializeMeteorAppSwcrc();
this.initializeMeteorAppLegacyConfig();
this.initializeMeteorAppSwcHelpersAvailable();
inputFiles.forEach(function (inputFile) {
if (inputFile.supportsLazyCompilation) {
@@ -284,6 +328,11 @@ BCp.processOneFileForTarget = function (inputFile, source) {
jsx: hasJSXSupport,
tsx: hasTSXSupport,
},
...(hasSwcHelpersAvailable &&
(packageName == null ||
!['modules-runtime'].includes(packageName)) && {
externalHelpers: true,
}),
},
module: { type: 'es6' },
minify: false,
@@ -356,6 +405,7 @@ BCp.processOneFileForTarget = function (inputFile, source) {
toBeAdded.hash,
lastModifiedSwcConfigTime,
isLegacyWebArch ? 'legacy' : '',
hasSwcHelpersAvailable,
]
.filter(Boolean)
.join('-');

View File

@@ -1,14 +1,14 @@
Package.describe({
name: "babel-compiler",
summary: "Parser/transpiler for ECMAScript 2015+ syntax",
version: '7.12.0',
version: '7.12.1-rc331.2',
});
Npm.depends({
'@meteorjs/babel': '7.20.1',
'json5': '2.2.3',
'semver': '7.6.3',
"@meteorjs/swc-core": "1.1.3",
"@meteorjs/swc-core": "1.12.14",
});
Package.onUse(function (api) {

View File

@@ -124,20 +124,6 @@ export class Hook {
}
}
async forEachAsync(iterator) {
const ids = Object.keys(this.callbacks);
for (let i = 0; i < ids.length; ++i) {
const id = ids[i];
// check to see if the callback was removed during iteration
if (hasOwn.call(this.callbacks, id)) {
const callback = this.callbacks[id];
if (!await iterator(callback)) {
break;
}
}
}
}
/**
* For each registered callback, call the passed iterator function with the callback.
*

View File

@@ -1,6 +1,6 @@
Package.describe({
summary: "Register callbacks on a hook",
version: '1.6.0',
version: '1.6.1-rc331.2',
});
Package.onUse(function (api) {

View File

@@ -1,6 +1,6 @@
Package.describe({
name: 'ecmascript',
version: '0.16.11',
version: '0.16.12-rc331.2',
summary: 'Compiler plugin that supports ES2015+ in all .js files',
documentation: 'README.md',
});

View File

@@ -1,6 +1,6 @@
Package.describe({
summary: "The Meteor command-line tool",
version: "3.3.0",
version: "3.3.1-rc.2",
});
Package.includeTool();

View File

@@ -1,6 +1,6 @@
Package.describe({
summary: "JavaScript minifier",
version: '3.0.2',
version: '3.0.3-rc331.2',
});
Npm.depends({

View File

@@ -2,6 +2,7 @@ import LocalCollection from './local_collection.js';
export const hasOwn = Object.prototype.hasOwnProperty;
export class MiniMongoQueryError extends Error {}
// Each element selector contains:
// - compileElementSelector, a function with args:
// - operand - the "right hand side" of the operator
@@ -24,7 +25,7 @@ export const ELEMENT_OPERATORS = {
if (!(Array.isArray(operand) && operand.length === 2
&& typeof operand[0] === 'number'
&& typeof operand[1] === 'number')) {
throw Error('argument to $mod must be an array of two numbers');
throw new MiniMongoQueryError('argument to $mod must be an array of two numbers');
}
// XXX could require to be ints or round or something
@@ -38,7 +39,7 @@ export const ELEMENT_OPERATORS = {
$in: {
compileElementSelector(operand) {
if (!Array.isArray(operand)) {
throw Error('$in needs an array');
throw new MiniMongoQueryError('$in needs an array');
}
const elementMatchers = operand.map(option => {
@@ -47,7 +48,7 @@ export const ELEMENT_OPERATORS = {
}
if (isOperatorObject(option)) {
throw Error('cannot nest $ under $in');
throw new MiniMongoQueryError('cannot nest $ under $in');
}
return equalityElementMatcher(option);
@@ -74,7 +75,7 @@ export const ELEMENT_OPERATORS = {
// does.
operand = 0;
} else if (typeof operand !== 'number') {
throw Error('$size needs a number');
throw new MiniMongoQueryError('$size needs a number');
}
return value => Array.isArray(value) && value.length === operand;
@@ -112,16 +113,16 @@ export const ELEMENT_OPERATORS = {
'maxKey': 127,
};
if (!hasOwn.call(operandAliasMap, operand)) {
throw Error(`unknown string alias for $type: ${operand}`);
throw new MiniMongoQueryError(`unknown string alias for $type: ${operand}`);
}
operand = operandAliasMap[operand];
} else if (typeof operand === 'number') {
if (operand === 0 || operand < -1
|| (operand > 19 && operand !== 127)) {
throw Error(`Invalid numerical $type code: ${operand}`);
throw new MiniMongoQueryError(`Invalid numerical $type code: ${operand}`);
}
} else {
throw Error('argument to $type is not a number or a string');
throw new MiniMongoQueryError('argument to $type is not a number or a string');
}
return value => (
@@ -168,7 +169,7 @@ export const ELEMENT_OPERATORS = {
$regex: {
compileElementSelector(operand, valueSelector) {
if (!(typeof operand === 'string' || operand instanceof RegExp)) {
throw Error('$regex has to be a string or RegExp');
throw new MiniMongoQueryError('$regex has to be a string or RegExp');
}
let regexp;
@@ -180,7 +181,7 @@ export const ELEMENT_OPERATORS = {
// ones (eg, Mongo supports x and s). Ideally we would implement x and s
// by transforming the regexp, but not today...
if (/[^gim]/.test(valueSelector.$options)) {
throw new Error('Only the i, m, and g regexp options are supported');
throw new MiniMongoQueryError('Only the i, m, and g regexp options are supported');
}
const source = operand instanceof RegExp ? operand.source : operand;
@@ -198,7 +199,7 @@ export const ELEMENT_OPERATORS = {
dontExpandLeafArrays: true,
compileElementSelector(operand, valueSelector, matcher) {
if (!LocalCollection._isPlainObject(operand)) {
throw Error('$elemMatch need an object');
throw new MiniMongoQueryError('$elemMatch need an object');
}
const isDocMatcher = !isOperatorObject(
@@ -353,7 +354,7 @@ const VALUE_OPERATORS = {
// $options just provides options for $regex; its logic is inside $regex
$options(operand, valueSelector) {
if (!hasOwn.call(valueSelector, '$regex')) {
throw Error('$options needs a $regex');
throw new MiniMongoQueryError('$options needs a $regex');
}
return everythingMatcher;
@@ -361,14 +362,14 @@ const VALUE_OPERATORS = {
// $maxDistance is basically an argument to $near
$maxDistance(operand, valueSelector) {
if (!valueSelector.$near) {
throw Error('$maxDistance needs a $near');
throw new MiniMongoQueryError('$maxDistance needs a $near');
}
return everythingMatcher;
},
$all(operand, valueSelector, matcher) {
if (!Array.isArray(operand)) {
throw Error('$all requires array');
throw new MiniMongoQueryError('$all requires array');
}
// Not sure why, but this seems to be what MongoDB does.
@@ -379,7 +380,7 @@ const VALUE_OPERATORS = {
const branchedMatchers = operand.map(criterion => {
// XXX handle $all/$elemMatch combination
if (isOperatorObject(criterion)) {
throw Error('no $ expressions in $all');
throw new MiniMongoQueryError('no $ expressions in $all');
}
// This is always a regexp or equality selector.
@@ -392,7 +393,7 @@ const VALUE_OPERATORS = {
},
$near(operand, valueSelector, matcher, isRoot) {
if (!isRoot) {
throw Error('$near can\'t be inside another $ operator');
throw new MiniMongoQueryError('$near can\'t be inside another $ operator');
}
matcher._hasGeoQuery = true;
@@ -433,7 +434,7 @@ const VALUE_OPERATORS = {
maxDistance = valueSelector.$maxDistance;
if (!isIndexable(operand)) {
throw Error('$near argument must be coordinate pair or GeoJSON');
throw new MiniMongoQueryError('$near argument must be coordinate pair or GeoJSON');
}
point = pointToArray(operand);
@@ -549,12 +550,12 @@ const andBranchedMatchers = andSomeMatchers;
function compileArrayOfDocumentSelectors(selectors, matcher, inElemMatch) {
if (!Array.isArray(selectors) || selectors.length === 0) {
throw Error('$and/$or/$nor must be nonempty array');
throw new MiniMongoQueryError('$and/$or/$nor must be nonempty array');
}
return selectors.map(subSelector => {
if (!LocalCollection._isPlainObject(subSelector)) {
throw Error('$or/$and/$nor entries need to be full objects');
throw new MiniMongoQueryError('$or/$and/$nor entries need to be full objects');
}
return compileDocumentSelector(subSelector, matcher, {inElemMatch});
@@ -576,7 +577,7 @@ export function compileDocumentSelector(docSelector, matcher, options = {}) {
// Outer operators are either logical operators (they recurse back into
// this function), or $where.
if (!hasOwn.call(LOGICAL_OPERATORS, key)) {
throw new Error(`Unrecognized logical operator: ${key}`);
throw new MiniMongoQueryError(`Unrecognized logical operator: ${key}`);
}
matcher._isSimple = false;
@@ -682,7 +683,7 @@ function distanceCoordinatePairs(a, b) {
// for equality with that thing.
export function equalityElementMatcher(elementSelector) {
if (isOperatorObject(elementSelector)) {
throw Error('Can\'t create equalityValueSelector for operator object');
throw new MiniMongoQueryError('Can\'t create equalityValueSelector for operator object');
}
// Special-case: null and undefined are equal (if you got undefined in there
@@ -759,7 +760,7 @@ function getOperandBitmask(operand, selector) {
}
// bad operand
throw Error(
throw new MiniMongoQueryError(
`operand to ${selector} must be a numeric bitmask (representable as a ` +
'non-negative 32-bit signed integer), a bindata bitmask or an array with ' +
'bit positions (non-negative integers)'
@@ -813,12 +814,11 @@ function insertIntoDocument(document, key, value) {
(existingKey.length > key.length && existingKey.indexOf(`${key}.`) === 0) ||
(key.length > existingKey.length && key.indexOf(`${existingKey}.`) === 0)
) {
throw new Error(
`cannot infer query fields to set, both paths '${existingKey}' and ` +
`'${key}' are matched`
throw new MiniMongoQueryError(
`cannot infer query fields to set, both paths '${existingKey}' and '${key}' are matched`
);
} else if (existingKey === key) {
throw new Error(
throw new MiniMongoQueryError(
`cannot infer query fields to set, path '${key}' is matched twice`
);
}
@@ -863,7 +863,7 @@ export function isOperatorObject(valueSelector, inconsistentOK) {
theseAreOperators = thisIsOperator;
} else if (theseAreOperators !== thisIsOperator) {
if (!inconsistentOK) {
throw new Error(
throw new MiniMongoQueryError(
`Inconsistent operator: ${JSON.stringify(valueSelector)}`
);
}
@@ -1132,7 +1132,7 @@ function operatorBranchedMatcher(valueSelector, matcher, isRoot) {
);
}
throw new Error(`Unrecognized operator: ${operator}`);
throw new MiniMongoQueryError(`Unrecognized operator: ${operator}`);
});
return andBranchedMatchers(operatorMatchers);
@@ -1232,7 +1232,7 @@ function populateDocumentWithObject(document, key, value) {
// Literal (possibly empty) object ( or empty object )
// Don't allow mixing '$'-prefixed with non-'$'-prefixed fields
if (keys.length !== unprefixedKeys.length) {
throw new Error(`unknown operator: ${unprefixedKeys[0]}`);
throw new MiniMongoQueryError(`unknown operator: ${unprefixedKeys[0]}`);
}
validateObject(value, key);

View File

@@ -1,6 +1,6 @@
Package.describe({
summary: "Meteor's client-side datastore: a port of MongoDB to Javascript",
version: "2.0.2",
version: "2.0.3-rc331.2",
});
Package.onUse((api) => {

View File

@@ -1,4 +1,12 @@
export declare function isModern(
browser: { name: string, major: number, minor?: number, patch?: number }
): boolean;
export declare function setMinimumBrowserVersions(
versions: Record<string, number | number[]>,
source: string
source?: string
): void;
export declare function getMinimumBrowserVersions(): Record<string, Record<string, number | number[]>>;
export declare function calculateHashOfMinimumVersions(): string;

View File

@@ -1,6 +1,6 @@
Package.describe({
name: 'modern-browsers',
version: '0.2.2',
version: '0.2.3-rc331.2',
summary:
'API for defining the boundary between modern and legacy ' +
'JavaScript clients',

View File

@@ -8,6 +8,8 @@ import { replaceMongoAtomWithMeteor, replaceTypes } from './mongo_common';
* This is an internal implementation detail and is created lazily by the main Cursor class.
*/
export class AsynchronousCursor {
_closing = false;
_pendingNext = null;
constructor(dbCursor, cursorDescription, options) {
this._dbCursor = dbCursor;
this._cursorDescription = cursorDescription;
@@ -36,10 +38,19 @@ export class AsynchronousCursor {
// Returns a Promise for the next object from the underlying cursor (before
// the Mongo->Meteor type replacement).
async _rawNextObjectPromise() {
if (this._closing) {
// Prevent next() after close is called
return null;
}
try {
return this._dbCursor.next();
this._pendingNext = this._dbCursor.next();
const result = await this._pendingNext;
this._pendingNext = null;
return result;
} catch (e) {
console.error(e);
} finally {
this._pendingNext = null;
}
}
@@ -74,24 +85,24 @@ export class AsynchronousCursor {
// _nextObjectPromise) or rejected if the cursor doesn't return within
// timeoutMS ms.
_nextObjectPromiseWithTimeout(timeoutMS) {
if (!timeoutMS) {
return this._nextObjectPromise();
}
const nextObjectPromise = this._nextObjectPromise();
const timeoutErr = new Error('Client-side timeout waiting for next object');
const timeoutPromise = new Promise((resolve, reject) => {
setTimeout(() => {
reject(timeoutErr);
if (!timeoutMS) {
return nextObjectPromise;
}
const timeoutPromise = new Promise(resolve => {
// On timeout, close the cursor.
const timeoutId = setTimeout(() => {
resolve(this.close());
}, timeoutMS);
});
return Promise.race([nextObjectPromise, timeoutPromise])
.catch((err) => {
if (err === timeoutErr) {
this.close();
return;
}
throw err;
// If the `_nextObjectPromise` returned first, cancel the timeout.
nextObjectPromise.finally(() => {
clearTimeout(timeoutId);
});
});
return Promise.race([nextObjectPromise, timeoutPromise]);
}
async forEach(callback, thisArg) {
@@ -123,7 +134,16 @@ export class AsynchronousCursor {
}
// Mostly usable for tailable cursors.
close() {
async close() {
this._closing = true;
// If there's a pending next(), wait for it to finish or abort
if (this._pendingNext) {
try {
await this._pendingNext;
} catch (e) {
// ignore
}
}
this._dbCursor.close();
}

View File

@@ -3,7 +3,8 @@ import { AsyncMethods } from './methods_async';
import { SyncMethods } from './methods_sync';
import { IndexMethods } from './methods_index';
import {
ID_GENERATORS, normalizeOptions,
ID_GENERATORS,
normalizeOptions,
setupAutopublish,
setupConnection,
setupDriver,
@@ -11,7 +12,6 @@ import {
validateCollectionName
} from './collection_utils';
import { ReplicationMethods } from './methods_replication';
import { watchChangeStream } from './watch_change_stream';
/**
* @summary Namespace for MongoDB-related items
@@ -267,7 +267,3 @@ Meteor.Collection = Mongo.Collection;
// Allow deny stuff is now in the allow-deny package
Object.assign(Mongo.Collection.prototype, AllowDeny.CollectionPrototype);
// Só agora que Mongo.Collection existe, adicionamos o método ao prototype
Object.assign(Mongo.Collection.prototype, { watchChangeStream });

View File

@@ -88,12 +88,17 @@ export function normalizeOptions(options) {
options.connection = options.manager;
}
const cleanedOptions = Object.fromEntries(
Object.entries(options || {}).filter(([_, v]) => v !== undefined),
);
// 2) Spread defaults first, then only the defined overrides
return {
connection: undefined,
idGeneration: 'STRING',
transform: null,
_driver: undefined,
_preventAutopublish: false,
...options,
...cleanedOptions,
};
}

View File

@@ -1,31 +0,0 @@
/**
* @summary Watches the MongoDB collection using Change Streams.
* @locus Server
* @memberof Mongo.Collection
* @instance
* @param {Array} [pipeline] Optional aggregation pipeline to filter Change Stream events.
* @param {Object} [options] Optional settings for the Change Stream.
* @returns {ChangeStream} The MongoDB ChangeStream instance.
* @throws {Error} If called on a client/minimongo collection.
*
* @example
* const changeStream = MyCollection.watchChangeStream([
* { $match: { 'operationType': 'insert' } }
* ]);
* changeStream.on('change', (change) => {
* console.log('Change detected:', change);
* });
*/
export function watchChangeStream(pipeline = [], options = {}) {
// Only available on server
if (typeof Package === 'undefined' || !this.rawCollection) {
throw new Error('watchChangeStream is only available on server collections');
}
const raw = this.rawCollection();
if (!raw.watch) {
throw new Error('Underlying collection does not support watch (Change Streams)');
}
console.log('[watchChangeStream] Chamando raw.watch() com pipeline:', JSON.stringify(pipeline, null, 2), 'e options:', JSON.stringify(options, null, 2));
return raw.watch(pipeline, options);
}

View File

@@ -1,5 +1,6 @@
import { Meteor } from 'meteor/meteor';
import { CLIENT_ONLY_METHODS, getAsyncMethodName } from 'meteor/minimongo/constants';
import { MiniMongoQueryError } from 'meteor/minimongo/common';
import path from 'path';
import { AsynchronousCursor } from './asynchronous_cursor';
import { Cursor } from './cursor';
@@ -886,6 +887,9 @@ Object.assign(MongoConnection.prototype, {
} catch (e) {
// XXX make all compilation errors MinimongoError or something
// so that this doesn't ignore unrelated exceptions
if (e instanceof MiniMongoQueryError) {
throw e;
}
return false;
}
},

View File

@@ -9,7 +9,7 @@
Package.describe({
summary: "Adaptor for using MongoDB and Minimongo over DDP",
version: "2.1.2",
version: "2.1.3-rc331.2",
});
Npm.depends({

View File

@@ -485,3 +485,58 @@ Meteor.isServer && Tinytest.addAsync('collection - simple add', async function(t
test.equal((await collection.findOneAsync(id)).a, 2);
await collection.removeAsync({});
});
Tinytest.addAsync('collection - default idGeneration when not provided', async function(test) {
if (!Meteor.isServer) {
return;
}
// Create a collection without specifying idGeneration option
var collectionName = 'defaultIdGeneration' + test.id;
var collection = new Mongo.Collection(collectionName, { idGeneration: undefined });
// Insert a document
var id = await collection.insertAsync({a: 1});
// Verify that the _id is a string
test.isTrue(typeof id === 'string', 'Document _id should be a string when no idGeneration option is provided');
test.isFalse(id instanceof Mongo.ObjectID, 'Document _id should not be a Mongo.ObjectID when no idGeneration option is provided');
// Clean up
await collection.removeAsync({});
});
Tinytest.addAsync('collection - compare default idGeneration with explicit idGeneration', async function(test) {
if (!Meteor.isServer) {
return;
}
// Create a collection without specifying idGeneration option
var defaultCollectionName = 'defaultIdGeneration2' + test.id;
var defaultCollection = new Mongo.Collection(defaultCollectionName, { idGeneration: undefined });
// Create a collection with explicit STRING idGeneration
var stringCollectionName = 'stringIdGeneration' + test.id;
var stringCollection = new Mongo.Collection(stringCollectionName, { idGeneration: 'STRING' });
// Create a collection with MONGO idGeneration
var mongoCollectionName = 'mongoIdGeneration' + test.id;
var mongoCollection = new Mongo.Collection(mongoCollectionName, { idGeneration: 'MONGO' });
// Insert documents
var defaultId = await defaultCollection.insertAsync({a: 1});
var stringId = await stringCollection.insertAsync({a: 1});
var mongoId = await mongoCollection.insertAsync({a: 1});
// Verify default behaves like STRING
test.isTrue(typeof defaultId === 'string', 'Default idGeneration should produce string IDs');
test.isTrue(typeof stringId === 'string', 'STRING idGeneration should produce string IDs');
test.isFalse(defaultId instanceof Mongo.ObjectID, 'Default idGeneration should not produce Mongo.ObjectID');
test.isFalse(stringId instanceof Mongo.ObjectID, 'STRING idGeneration should not produce Mongo.ObjectID');
// Verify MONGO produces ObjectIDs
test.isTrue(mongoId instanceof Mongo.ObjectID, 'MONGO idGeneration should produce Mongo.ObjectID');
// Clean up
await defaultCollection.removeAsync({});
await stringCollection.removeAsync({});
await mongoCollection.removeAsync({});
});

View File

@@ -4298,9 +4298,10 @@ Tinytest.addAsync(
await Collection.updateAsync({ _id: 'a' }, { $set: { num: 1 } });
await Collection.updateAsync({ _id: 'b' }, { $set: { num: 2 } });
if(Meteor.isClient) Meteor._sleepForMs(100); // wait for async operations to complete
items = await Collection.find().fetchAsync();
itemIds = items.map(_item => _item.num);
test.equal(itemIds, [1, 2]);
await Collection.removeAsync({ _id: 'a' });

View File

@@ -1,3 +1,5 @@
import { MiniMongoQueryError } from 'meteor/minimongo/common';
var randomId = Random.id();
var OplogCollection = new Mongo.Collection("oplog-" + randomId);
@@ -9,11 +11,16 @@ Tinytest.addAsync('mongo-livedata - oplog - cursorSupported', async function(
var supported = async function(expected, selector, options) {
var cursor = OplogCollection.find(selector, options);
var handle = await cursor.observeChanges({ added: function() {} });
// If there's no oplog at all, we shouldn't ever use it.
if (!oplogEnabled) expected = false;
test.equal(!!handle._multiplexer._observeDriver._usesOplog, expected);
handle.stop();
try {
var handle = await cursor.observeChanges({ added: function() {} });
// If there's no oplog at all, we shouldn't ever use it.
if (!oplogEnabled) expected = false;
test.equal(!!handle._multiplexer._observeDriver._usesOplog, !!expected);
handle.stop();
} catch(e){
if (e instanceof MiniMongoQueryError) return test.isFalse(expected);
else test.fail(e.message);
}
};
await supported(true, 'asdf');

View File

@@ -0,0 +1,4 @@
# npm-mongo-legacy
[Source code of released version](https://github.com/meteor/meteor/tree/devel/packages/npm-mongo-legacy)
***

3
packages/npm-mongo-legacy/index.d.ts vendored Normal file
View File

@@ -0,0 +1,3 @@
import * as NpmModuleMongodb from 'mongodb';
declare const NpmModuleMongodbVersion: string;
export { NpmModuleMongodb, NpmModuleMongodbVersion };

View File

@@ -0,0 +1,18 @@
// This has been moved out of the `mongo` package so it can be used by the tool
// via isopacket, without having to also load ddp-server.
Package.describe({
summary: "Wrapper around the mongo npm package (legacy)",
version: "6.9.0-rc331.2",
documentation: null,
});
Npm.depends({
mongodb: "6.9.0",
});
Package.onUse(function (api) {
api.addFiles("wrapper.js", "server");
api.export(["NpmModuleMongodb", "NpmModuleMongodbVersion"], "server");
api.addAssets("index.d.ts", "server");
});

View File

@@ -0,0 +1,11 @@
const oldNoDeprecationValue = process.noDeprecation;
try {
// Silence deprecation warnings introduced in a patch update to mongodb:
// https://github.com/meteor/meteor/pull/9942#discussion_r218564879
process.noDeprecation = true;
NpmModuleMongodb = Npm.require('mongodb');
} finally {
process.noDeprecation = oldNoDeprecationValue;
}
NpmModuleMongodbVersion = Npm.require('mongodb/package.json').version;

View File

@@ -3,12 +3,12 @@
Package.describe({
summary: "Wrapper around the mongo npm package",
version: "6.10.2",
version: "6.16.0-rc331.2",
documentation: null,
});
Npm.depends({
mongodb: "6.9.0"
mongodb: "6.16.0"
});
Package.onUse(function (api) {

View File

@@ -1,11 +1,49 @@
const { MongoClient, MongoCompatibilityError } = Npm.require('mongodb');
function connect(client) {
return client.connect()
.catch(error => {
if (error.cause instanceof MongoCompatibilityError && error.message.includes('maximum wire version')) {
console.warn(`[DEPRECATION] Legacy MongoDB version detected, using mongo-legacy package: ${error.message}
Warning: MongoDB versions <= 3.6 are deprecated. Some Meteor features may not work properly with this version.
It is recommended to use MongoDB >= 4.`);
if (!Package['npm-mongo-legacy']) {
throw new Error('Please, install npm-mongo-legacy package to use this version of MongoDB running "meteor add npm-mongo-legacy", then move the listed package inside .meteor/packages to the top.');
}
return false
}
})
}
if (process.env.MONGO_URL && (/^mongodb(\+srv)?:\/\//.test(process.env.MONGO_URL))) {
try {
connect(new MongoClient(process.env.MONGO_URL, {
tls: true,
tlsAllowInvalidCertificates: true,
})).then(client => {
if (client) client.close();
});
} catch (e) {
console.warn('Invalid MongoDB connection string in MONGO_URL:', process.env.MONGO_URL);
}
}
const useLegacyMongo = !!Package['npm-mongo-legacy']
const oldNoDeprecationValue = process.noDeprecation;
useLegacyMongo && console.log('WARN: npm-mongo-legacy package detected, using package for mongo <= 3.6');
try {
// Silence deprecation warnings introduced in a patch update to mongodb:
// https://github.com/meteor/meteor/pull/9942#discussion_r218564879
process.noDeprecation = true;
NpmModuleMongodb = Npm.require('mongodb');
NpmModuleMongodb = useLegacyMongo
? Package['npm-mongo-legacy'].NpmModuleMongodb
: Npm.require('mongodb');
} finally {
process.noDeprecation = oldNoDeprecationValue;
}
NpmModuleMongodbVersion = Npm.require('mongodb/package.json').version;
NpmModuleMongodbVersion = useLegacyMongo
? Package['npm-mongo-legacy'].NpmModuleMongodbVersion
: Npm.require('mongodb/package.json').version;

View File

@@ -1,6 +1,6 @@
Package.describe({
name: 'standard-minifier-js',
version: '3.1.0',
version: '3.1.1-rc331.2',
summary: 'Standard javascript minifiers used with Meteor apps by default.',
documentation: 'README.md',
});
@@ -12,7 +12,7 @@ Package.registerBuildPlugin({
'ecmascript'
],
npmDependencies: {
'@meteorjs/swc-core': '1.1.3',
'@meteorjs/swc-core': '1.12.14',
'acorn': '8.10.0',
"@babel/runtime": "7.18.9",
'@babel/parser': '7.22.7',

View File

@@ -1,6 +1,6 @@
Package.describe({
summary: "Tiny testing framework",
version: '1.3.1',
version: '1.3.2-rc331.2',
});
Npm.depends({

View File

@@ -74,7 +74,7 @@ export class TestCaseResults {
var frame = stack[i];
// Heuristic: use the OUTERMOST line which is in a :tests.js
// file (this is less likely to be a test helper function).
const fileName = frame.getFileName();
const fileName = frame?.getFileName ? frame.getFileName() : null;
if (fileName && fileName.match(/:tests\.js/)) {
doc.filename = fileName;
doc.line = frame.getLineNumber();

View File

@@ -1,6 +1,6 @@
Package.describe({
name: 'typescript',
version: '5.6.4',
version: '5.6.5-rc331.2',
summary:
'Compiler plugin that compiles TypeScript and ECMAScript in .ts and .tsx files',
documentation: 'README.md',

View File

@@ -1,6 +1,6 @@
{
"track": "METEOR",
"version": "3.3-rc.0",
"version": "3.3.1-rc.2",
"recommended": false,
"official": false,
"description": "Meteor experimental release"

View File

@@ -5,7 +5,7 @@ set -u
UNAME=$(uname)
ARCH=$(uname -m)
NODE_VERSION=22.16.0
NODE_VERSION=22.17.1
MONGO_VERSION_64BIT=7.0.16
MONGO_VERSION_32BIT=3.2.22
NPM_VERSION=10.9.2

View File

@@ -1203,71 +1203,85 @@ main.registerCommand({
toIgnore.push(/(\.html|\.js|\.css)/);
}
try {
// Prototype option should use local skeleton.
// Maybe we should use a different skeleton for prototype
if (options.prototype) throw new Error("Using prototype option");
// if using the release option we should use the default skeleton
// using it as it was before 2.x
if (release.explicit) throw new Error("Using release option");
const copyFromLocalSkeleton = async () => {
await files.cp_r(
skeletonPath,
appPath,
{
transformFilename: function (f) {
return transform(f);
},
transformContents: function (contents, f) {
// check if this app is just for prototyping if it is then we need to add autopublish and insecure in the packages file
if (/packages/.test(f)) {
const prototypePackages = () =>
"autopublish # Publish all data to the clients (for prototyping)\n" +
"insecure # Allow all DB writes from clients (for prototyping)";
await setupExampleByURL(`https://github.com/meteor/skel-${skeleton}`);
} catch (e) {
// XXX: if there is the need to add more options maybe we should have a better abstraction for this if-else
if (options.prototype) {
return Buffer.from(
contents.toString().replace(/~prototype~/g, prototypePackages())
);
} else {
return Buffer.from(contents.toString().replace(/~prototype~/g, ""));
}
}
if (/(\.html|\.[jt]sx?|\.css)/.test(f)) {
return Buffer.from(transform(contents.toString()));
} else {
return contents;
}
},
ignore: toIgnore,
preserveSymlinks: true,
}
);
};
if (
e.message !== "Using prototype option" &&
e.message !== "Using release option"
) {
// something has happened while creating the app using git clone
Console.error(
`Something has happened while creating your app using git clone.
// Check if the local skeleton path exists
const skeletonPath = files.pathJoin(
__dirnameConverted,
"..",
"static-assets",
`skel-${skeleton}`
);
const useLocalSkeleton = files.exists(skeletonPath) ||
options.prototype ||
release.explicit;
if (useLocalSkeleton) {
// Use local skeleton
await copyFromLocalSkeleton();
} else {
try {
// Prototype option should use local skeleton.
// Maybe we should use a different skeleton for prototype
if (options.prototype) throw new Error("Using prototype option");
// if using the release option we should use the default skeleton
// using it as it was before 2.x
if (release.explicit) throw new Error("Using release option");
// If local skeleton doesn't exist, use setupExampleByURL
await setupExampleByURL(`https://github.com/meteor/skel-${skeleton}`);
} catch (e) {
if (
e.message !== "Using prototype option" &&
e.message !== "Using release option"
) {
// something has happened while creating the app using git clone
Console.error(
`Something has happened while creating your app using git clone.
Will use cached version of skeletons.
Error message: `,
e.message
);
e.message
);
}
// For prototype or release options, use local skeleton
await copyFromLocalSkeleton();
}
// TODO: decide if this should stay here or not.
await files.cp_r(
files.pathJoin(
__dirnameConverted,
"..",
"static-assets",
`skel-${skeleton}`
),
appPath,
{
transformFilename: function (f) {
return transform(f);
},
transformContents: function (contents, f) {
// check if this app is just for prototyping if it is then we need to add autopublish and insecure in the packages file
if (/packages/.test(f)) {
const prototypePackages = () =>
"autopublish # Publish all data to the clients (for prototyping)\n" +
"insecure # Allow all DB writes from clients (for prototyping)";
// XXX: if there is the need to add more options maybe we should have a better abstraction for this if-else
if (options.prototype) {
return Buffer.from(
contents.toString().replace(/~prototype~/g, prototypePackages())
);
} else {
return Buffer.from(contents.toString().replace(/~prototype~/g, ""));
}
}
if (/(\.html|\.[jt]sx?|\.css)/.test(f)) {
return Buffer.from(transform(contents.toString()));
} else {
return contents;
}
},
ignore: toIgnore,
preserveSymlinks: true,
}
);
await setupMessages();
}
await setupMessages();
Console.info("");
});
@@ -3412,20 +3426,58 @@ const setupBenchmarkSuite = async (profilingPath) => {
if (await files.exists(profilingPath)) {
return;
}
// Check git availability and version
const [okGitVersion, errGitVersion] = await bash`git --version`;
if (errGitVersion) throw new Error("git is not installed");
const parsedGitVersion = semver.coerce(okGitVersion.match(/\d+\.\d+\.\d+/)[0] || '')?.version;
const checkInvalidGitVersion = parsedGitVersion == null || semver.lt(parsedGitVersion, '2.25.0');
if (checkInvalidGitVersion) {
const parsedGitVersion = semver.coerce(okGitVersion.match(/\d+\.\d+\.\d+/)?.[0] || '')?.version;
if (!parsedGitVersion || semver.lt(parsedGitVersion, '2.25.0')) {
throw new Error("git version is too old. Please upgrade to at least 2.25");
}
// Set GIT_TERMINAL_PROMPT=0 to disable prompting
// Check tar availability
const [okTar, errTar] = await bash`tar --version`;
const hasTar = !errTar;
// Disable interactive git prompts
process.env.GIT_TERMINAL_PROMPT = 0;
const repoUrl = "https://github.com/meteor/performance";
const branch = "v3.3.0";
let tarFailed = false;
// If tar is available, prefer tar-based extraction
if (hasTar) {
const tempDir = "/tmp/meteor-performance-benchmark-suite";
const tarCommand = [
`rm -rf ${tempDir}`,
`git clone --no-checkout --depth 1 --filter=tree:0 --sparse --progress --branch ${branch} --single-branch ${repoUrl} ${tempDir}`,
`cd ${tempDir}`,
`git sparse-checkout init --cone`,
`git sparse-checkout set scripts`,
`git checkout ${branch}`,
`mkdir -p ${profilingPath}/scripts`,
`tar -czf /tmp/scripts.tar.gz -C ./scripts .`,
`tar -xzf /tmp/scripts.tar.gz -C ${profilingPath}/scripts`,
`rm -rf ${tempDir}`,
`rm -f /tmp/scripts.tar.gz`
].join(" && ");
const [okTarClone, errTarClone] = await bash`${tarCommand}`;
if (!errTarClone) {
Console.info("Meteor profiling suite cloned to: " + Console.path(profilingPath));
return;
} else {
Console.warn("Tar-based cloning failed. Will attempt standard git clone...");
tarFailed = errTarClone;
}
} else {
Console.warn("Tar not available. Will use standard git clone...");
}
// Fallback to plain git clone
const gitCommand = [
`mkdir -p ${profilingPath}`,
`git clone --no-checkout --depth 1 --filter=tree:0 --sparse --progress --branch ${branch} --single-branch ${repoUrl} ${profilingPath}`,
@@ -3433,18 +3485,22 @@ const setupBenchmarkSuite = async (profilingPath) => {
`git sparse-checkout init --cone`,
`git sparse-checkout set scripts`,
`git checkout ${branch}`,
`find ${profilingPath} -maxdepth 1 -type f -delete`,
`find ${profilingPath} -maxdepth 1 -type f -delete`
].join(" && ");
const [, errClone] = await bash`${gitCommand}`;
const errorMessage = errClone && typeof errClone === "string" ? errClone : errClone?.message;
if (errorMessage && errorMessage.includes("Cloning into")) {
throw new Error("error cloning benchmark");
const [okClone, errClone] = await bash`${gitCommand}`;
if (errClone) {
let combinedMessage = "Git clone failed.";
if (tarFailed) {
combinedMessage = `Tar-based cloning also failed:\n${tarFailed}\n\nGit fallback failed:\n${errClone}`;
}
throw new Error(combinedMessage);
}
// remove .git folder from the example
// Remove .git folder if present
await files.rm_recursive_async(files.pathJoin(profilingPath, ".git"));
Console.info(
"Meteor profiling suite cloned to: " + Console.path(profilingPath),
);
Console.info("Meteor profiling suite cloned to: " + Console.path(profilingPath));
};
async function doBenchmarkCommand(options) {

View File

@@ -492,7 +492,7 @@ Note that you must have mongosh installed to use this option.
Options:
--url, -U return a Mongo database URL
--verbose, -v to show the errors that have occurred while connecting to the
database
database
Currently, this feature can only be used when developing locally.
The opened Mongo shell connects to the current project's local
@@ -816,7 +816,16 @@ Usage: meteor admin <command> [args]
Rarely used commands for administering official Meteor services.
Commands:
{{commands}}
make-bootstrap-tarballs
recommend-release
change-homepage
set-unmigrated
set-banners
list-organizations
members
set-latest-readme
get-machine
See 'meteor help admin <command>' for details on an admin command.
@@ -875,12 +884,12 @@ for replacing the names, we offer $$PascalName$$, $$camelName$$, $$name$$.
This is a MeteorJS project command.
Options:
--help Show help.
--path The path to the folder where the files will be generated. Default is the current folder.
--templatePath Path to the template file check https://docs.meteor.com/commandline.html#meteorgenerate-templating for more info.
--replaceFn Replace function to replace the names in the template. Check https://docs.meteor.com/commandline.html#meteorgenerate-templating for more info.
--methods Generate methods.
--publications Generate publications.
--help Show help.
--path The path to the folder where the files will be generated. Default is the current folder.
--templatePath Path to the template file check https://docs.meteor.com/commandline.html#meteorgenerate-templating for more info.
--replaceFn Replace function to replace the names in the template. Check https://docs.meteor.com/commandline.html#meteorgenerate-templating for more info.
--methods Generate methods.
--publications Generate publications.
>>> publish-release

View File

@@ -13,11 +13,11 @@ export const CORDOVA_ARCH = "web.cordova";
export const CORDOVA_PLATFORMS = ['ios', 'android'];
const CORDOVA_ANDROID_VERSION = "13.0.0";
const CORDOVA_ANDROID_VERSION = "14.0.1";
export const CORDOVA_DEV_BUNDLE_VERSIONS = {
'cordova-lib': '12.0.1',
'cordova-common': '5.0.0',
'cordova-lib': '12.0.2',
'cordova-common': '5.0.1',
'cordova-create': '2.0.0',
'cordova-registry-mapper': '1.1.15',
'cordova-android': CORDOVA_ANDROID_VERSION,

View File

@@ -46,6 +46,7 @@ import {
import { wrap } from "optimism";
const { compile: reifyCompile } = require("@meteorjs/reify/lib/compiler");
const { parse: reifyAcornParse } = require("@meteorjs/reify/lib/parsers/acorn");
const { parse: reifyBabelParse } = require("@meteorjs/reify/lib/parsers/babel");
import Resolver, { Resolution } from "./resolver";
import LRUCache from 'lru-cache';
@@ -87,14 +88,32 @@ const reifyCompileWithCache = Profile("reifyCompileWithCache", wrap(function (
}
const isLegacy = isLegacyArch(bundleArch);
let result = reifyCompile(stripHashBang(source), {
parse: reifyAcornParse,
const reifyOptions = {
generateLetDeclarations: !isLegacy,
avoidModernSyntax: isLegacy,
enforceStrictMode: false,
dynamicImport: true,
ast: false,
}).code;
};
let result;
try {
// First attempt: use Acorn
result = reifyCompile(stripHashBang(source), {
...reifyOptions,
parse: reifyAcornParse,
}).code;
} catch (acornError) {
// Fallback: use Babel parser
// acorn may throw SyntaxError due to the lack of support for
// some features, but babel should still be able to parse the file
// For example, acorn dont support JSX, only with acorn-jsx,
// but it isnt included in Reify.
result = reifyCompile(stripHashBang(source), {
...reifyOptions,
parse: reifyBabelParse,
}).code;
}
if (cacheFilePath) {
Promise.resolve().then(

View File

@@ -1234,7 +1234,16 @@ Object.assign(exports.ProjectConstraintsFile.prototype, {
constraint: constraintToAdd,
trailingSpaceAndComment: ''
};
self._constraintLines.push(lineRecord);
if (constraintToAdd.package === 'npm-mongo-legacy') {
const mongoIdx = self._constraintLines.findIndex(lr => lr.constraint && lr.constraint.package === 'mongo');
if (mongoIdx > -1) {
self._constraintLines.splice(mongoIdx, 0, lineRecord);
} else {
self._constraintLines.push(lineRecord);
}
} else {
self._constraintLines.push(lineRecord);
}
self._constraintMap[constraintToAdd.package] = lineRecord;
self._modified = true;
return;
@@ -1878,18 +1887,14 @@ export class MeteorConfig {
// General utility for querying the "meteor" section of package.json.
// TODO Implement an API for setting these values?
get(...keys) {
let config = this._ensureInitialized();
let filteredConfig = keys.length ? {} : config;
if (config) {
keys.every(key => {
if (config && _.has(config, key)) {
filteredConfig = config[key];
return true;
}
return false;
});
return filteredConfig;
}
const config = this._ensureInitialized();
if (!config) return undefined;
return keys.reduce((cur, key) => {
return (cur != null && _.has(cur, key))
? cur[key]
: undefined;
}, config);
}
getNodeModulesToRecompileByArch() {

View File

@@ -48,13 +48,15 @@ module.exports = function enable ({ cachePath, createLoader = true } = {}) {
const reifyVersion = require("@meteorjs/reify/package.json").version;
const reifyAcornParse = require("@meteorjs/reify/lib/parsers/acorn").parse;
const reifyBabelParse = require("@meteorjs/reify/lib/parsers/babel").parse;
const reifyCompile = require("@meteorjs/reify/lib/compiler").compile;
function compileContent (content) {
let identical = true;
let result;
try {
const result = reifyCompile(content, {
result = reifyCompile(content, {
parse: reifyAcornParse,
generateLetDeclarations: false,
ast: false,
@@ -63,9 +65,20 @@ module.exports = function enable ({ cachePath, createLoader = true } = {}) {
identical = false;
content = result.code;
}
} finally {
return { content, identical };
} catch (acornError) {
// Fallback: Babel
result = reifyCompile(content, {
parse: reifyBabelParse,
generateLetDeclarations: false,
ast: false,
});
if (!result.identical) {
identical = false;
content = result.code;
}
}
return { content, identical };
}
const _compile = Mp._compile;

View File

@@ -11,6 +11,7 @@
"@apollo/client": "^3.9.2",
"@apollo/server": "^4.10.0",
"@babel/runtime": "^7.23.9",
"@swc/helpers": "^0.5.17",
"graphql": "^16.8.1",
"meteor-node-stubs": "^1.2.12",
"react": "^18.2.0",

View File

@@ -9,6 +9,7 @@
},
"dependencies": {
"@babel/runtime": "^7.23.5",
"@swc/helpers": "^0.5.17",
"jquery": "^3.7.1",
"meteor-node-stubs": "^1.2.12"
},

View File

@@ -9,6 +9,7 @@
},
"dependencies": {
"@babel/runtime": "^7.23.5",
"@swc/helpers": "^0.5.17",
"@chakra-ui/icons": "^1.1.7",
"@chakra-ui/react": "^1.8.8",
"@emotion/react": "^11.9.3",

View File

@@ -7,10 +7,18 @@
},
"dependencies": {
"@babel/runtime": "^7.23.5",
"@swc/helpers": "^0.5.17",
"jquery": "^3.7.1",
"meteor-node-stubs": "^1.2.12"
},
"devDependencies": {
"chai": "^4.2.0"
},
"meteor": {
"mainModule": {
"client": "client/main.js",
"server": "server/main.js"
},
"modern": true
}
}

View File

@@ -9,6 +9,7 @@
},
"dependencies": {
"@babel/runtime": "^7.23.5",
"@swc/helpers": "^0.5.17",
"meteor-node-stubs": "^1.2.12"
},
"meteor": {

View File

@@ -9,6 +9,7 @@
},
"dependencies": {
"@babel/runtime": "^7.23.5",
"@swc/helpers": "^0.5.17",
"meteor-node-stubs": "^1.2.12",
"react": "^18.2.0",
"react-dom": "^18.2.0"

View File

@@ -0,0 +1,14 @@
/**
* Entrypoint for the Meteor client
*
* Generally, this file can be left empty. Vite will add imports for
* lazy-loaded Meteor packages to this file to ensure they aren't omitted from
* the final production bundle.
*
* Use ./main.js as the primary entrypoint for your client code to take full
* advantage of Vite's plugin and build system.
*
* This can also be a good place to put code that you don't want Vite to
* process, for example, if you run into a compatibility issue or need to use
* nested imports which Vite doesn't support.
*/

View File

@@ -0,0 +1 @@
import '../imports/ui/main';

View File

@@ -1 +0,0 @@
// main entry point is in imports/ui/main.jsx

View File

@@ -2,6 +2,7 @@
import { render } from 'solid-js/web';
import { App } from './App';
import { Meteor } from "meteor/meteor";
import './main.css';
Meteor.startup(() => {
render(() => <App/>, document.getElementById('root'));

View File

@@ -9,6 +9,7 @@
},
"dependencies": {
"@babel/runtime": "^7.23.9",
"@swc/helpers": "^0.5.17",
"meteor-node-stubs": "^1.2.12",
"picocolors": "^1.1.1",
"solid-js": "^1.9.4"

View File

@@ -0,0 +1,12 @@
/**
* Entrypoint for the Meteor server
* Generally, this file can be left empty. Vite will add imports for your app's
* server bundle here during both development and production build.
*
* Use ./main.js as the primary entrypoint for your app to take full advantage
* of Vite's plugin and build system.
*
* This can also be a good place to put code that you don't want Vite to
* process, for example, if you run into a compatibility issue or need to use
* nested imports.
*/

View File

@@ -8,7 +8,7 @@ export default defineConfig({
solidPlugin(),
solidSvg({ defaultExport: 'component' }),
meteor({
clientEntry: 'imports/ui/main.jsx',
clientEntry: 'client/main.js',
serverEntry: 'server/main.js',
enableExperimentalFeatures: true,
stubValidation: {

View File

@@ -9,6 +9,7 @@
},
"dependencies": {
"@babel/runtime": "^7.23.5",
"@swc/helpers": "^0.5.17",
"meteor-node-stubs": "^1.2.12",
"svelte": "^3.59.2"
},

View File

@@ -9,6 +9,7 @@
},
"dependencies": {
"@babel/runtime": "^7.23.5",
"@swc/helpers": "^0.5.17",
"autoprefixer": "^10.4.4",
"meteor-node-stubs": "^1.2.12",
"postcss": "^8.4.12",

View File

@@ -9,6 +9,7 @@
},
"dependencies": {
"@babel/runtime": "^7.23.5",
"@swc/helpers": "^0.5.17",
"meteor-node-stubs": "^1.2.12",
"react": "^18.2.0",
"react-dom": "^18.2.0"

View File

@@ -0,0 +1,14 @@
/**
* Entrypoint for the Meteor client
*
* Generally, this file can be left empty. Vite will add imports for
* lazy-loaded Meteor packages to this file to ensure they aren't omitted from
* the final production bundle.
*
* Use ./main.js as the primary entrypoint for your client code to take full
* advantage of Vite's plugin and build system.
*
* This can also be a good place to put code that you don't want Vite to
* process, for example, if you run into a compatibility issue or need to use
* nested imports which Vite doesn't support.
*/

View File

@@ -1,3 +0,0 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

View File

@@ -1 +1 @@
// main entry point is in imports/ui/main.jsx
import '../imports/ui/main'

View File

@@ -1,5 +1,5 @@
<script setup>
import AppMenu from './AppMenu.vue'
import AppMenu from './components/AppMenu.vue'
</script>
<template>

View File

@@ -0,0 +1 @@
@import "tailwindcss";

View File

@@ -2,6 +2,7 @@ import { Meteor } from 'meteor/meteor'
import { createApp } from 'vue'
import { VueMeteor } from 'vue-meteor-tracker'
import './main.css'
import App from './App.vue'
import { router } from './router'

View File

@@ -1,6 +1,6 @@
import { createRouter, createWebHistory } from 'vue-router'
import Home from './Home.vue'
import About from './About.vue'
import Home from './views/Home.vue'
import About from './views/About.vue'
export const router = createRouter({
history: createWebHistory(),

View File

@@ -1,6 +1,6 @@
<script setup>
import Hello from './Hello.vue'
import Info from './Info.vue'
import Hello from '../components/Hello.vue'
import Info from '../components/Info.vue'
</script>
<template>

View File

@@ -10,12 +10,14 @@
},
"dependencies": {
"@babel/runtime": "^7.23.5",
"@swc/helpers": "^0.5.17",
"meteor-node-stubs": "^1.2.12",
"vue": "^3.3.9",
"vue-meteor-tracker": "^3.0.0-beta.7",
"vue-router": "^4.2.5"
},
"meteor": {
"modern": true,
"mainModule": {
"client": "client/entry-meteor.js",
"server": "server/entry-meteor.js"
@@ -24,11 +26,10 @@
},
"devDependencies": {
"@types/meteor": "^2.9.7",
"@tailwindcss/vite": "^4.1.11",
"@vitejs/plugin-vue": "^5.2.1",
"autoprefixer": "^10.4.16",
"meteor-vite": "^3.2.1",
"postcss": "^8.4.31",
"tailwindcss": "^3.3.5",
"tailwindcss": "^4.1.11",
"vite": "^6.0.11"
}
}

View File

@@ -1,6 +0,0 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

View File

@@ -0,0 +1,12 @@
/**
* Entrypoint for the Meteor server
* Generally, this file can be left empty. Vite will add imports for your app's
* server bundle here during both development and production build.
*
* Use ./main.js as the primary entrypoint for your app to take full advantage
* of Vite's plugin and build system.
*
* This can also be a good place to put code that you don't want Vite to
* process, for example, if you run into a compatibility issue or need to use
* nested imports.
*/

View File

@@ -1,8 +0,0 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ['./imports/ui/**/*.{vue,js,ts,jsx,tsx}', './client/*.html'],
theme: {
extend: {},
},
plugins: [],
}

View File

@@ -1,12 +1,14 @@
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import tailwindcss from '@tailwindcss/vite';
import { defineConfig } from 'vite';
import { meteor } from 'meteor-vite/plugin';
export default defineConfig({
plugins: [
vue(),
tailwindcss(),
meteor({
clientEntry: 'imports/ui/main.js',
clientEntry: 'client/main.js',
serverEntry: 'server/main.js',
enableExperimentalFeatures: true,
stubValidation: {

View File

@@ -199,3 +199,71 @@ selftest.define("testModule", async function () {
await run.stop();
});
async function writeModernConfig(s, run, modernConfig, errorPattern) {
const json = JSON.parse(s.read("package.json"));
json.meteor = {
// Make sure the tests.js module is always loaded eagerly.
testModule: "tests.js"
};
if (typeof modernConfig === "undefined") {
delete json.meteor.modern;
} else {
json.meteor.modern = modernConfig;
}
s.write("package.json", JSON.stringify(json, null, 2) + "\n");
run.waitSecs(10);
if (errorPattern instanceof RegExp) {
await run.match(errorPattern);
} else {
run.forbid(" 0 passing ");
await run.match("SERVER FAILURES: 0");
await run.match("CLIENT FAILURES: 0");
}
}
selftest.define("modernConfig", async function () {
const s = new Sandbox();
await s.init();
await s.createApp("app-config-modernConfig", "app-config");
await s.cd("app-config-modernConfig");
// For meteortesting:mocha to work we must set test broswer driver
// See https://github.com/meteortesting/meteor-mocha
s.set("TEST_BROWSER_DRIVER", "puppeteer");
const run = s.run(
"test",
"--full-app",
"--driver-package", "meteortesting:mocha"
);
run.waitSecs(60);
await run.match("App running at");
function check(modernConfig) {
return writeModernConfig(s, run, modernConfig);
}
// Test with modern disabled
await check(false);
// Test with modern enabled
await check(true);
// Test with combined options
await check({
transpiler: true,
watcher: true,
webArchOnly: true,
minifier: true,
});
await run.stop();
});

View File

@@ -6,6 +6,7 @@
},
"dependencies": {
"@babel/runtime": "^7.23.5",
"config": "file:../config-package",
"meteor-node-stubs": "^1.2.12",
"react": "^18.3.1"
},

View File

@@ -38,7 +38,7 @@ selftest.define("help", async function () {
var checkSubcommandList = async function (run) {
await run.read("Usage: meteor admin <command>");
await run.match("Commands:");
await run.match(/recommend-release\s*Recommend a previously published/);
await run.match(/\s*recommend-release\s*/);
await run.expectExit(0);
};
await checkCommandHelp(s.run("create", "--help"));

View File

@@ -188,9 +188,35 @@ selftest.define("modern build stack - transpiler boolean-like options", async fu
const s = new Sandbox();
await s.init();
s.mkdir("config-package");
s.cd("config-package");
s.write(
"package.json",
JSON.stringify({
name: "config",
version: "1.2.3",
"private": true,
main: "index.js"
}, null, 2) + "\n"
);
s.write(
"index.js",
"exports.id = module.id;\n"
);
s.cd(s.home);
await s.createApp("modern", "modern");
await s.cd("modern");
s.append(
"server/main.js",
`if (require('config')) {
console.log('Loaded NPM package "config"', require('config').id);
}`);
process.env.METEOR_DISABLE_COLORS = true;
await writeModernConfig(s, {
@@ -204,14 +230,18 @@ selftest.define("modern build stack - transpiler boolean-like options", async fu
run.waitSecs(waitToStart);
await run.match("App running at");
/* check appended NPM package require */
await run.match(/Loaded NPM package "config"/, false, true);
/* check verbose logs */
await run.match(/SWC Config/, false, true);
await run.match(/SWC Custom Config/, false, true);
await run.match(/SWC Legacy Config/, false, true);
await run.match(/Meteor Config/, false, true);
/* check transpiler options */
await run.match(/\[Transpiler] Used SWC.*\(app\)/, false, true);
await run.match(/\[Transpiler] Used SWC.*\(package\)/, false, true);
run.forbid(/\[Transpiler] Used SWC.*\(node_modules\)/, false, true);
await writeModernConfig(s, {
transpiler: {
@@ -229,6 +259,20 @@ selftest.define("modern build stack - transpiler boolean-like options", async fu
});
await run.match(/\[Transpiler] Used Babel.*\(package\)/, false, true);
await writeConfig(s, {
modern: {
transpiler: {
verbose: true,
},
},
nodeModules: {
recompile: {
config: true,
},
},
});
await run.match(/\[Transpiler] Used SWC.*\(node_modules\)/, false, true);
await run.stop();
process.env.METEOR_MODERN = currentMeteorModern;
@@ -241,9 +285,34 @@ selftest.define("modern build stack - transpiler string-like options", async fun
const s = new Sandbox();
await s.init();
s.mkdir("config-package");
s.cd("config-package");
s.write(
"package.json",
JSON.stringify({
name: "config",
version: "1.2.3",
"private": true,
main: "index.js"
}, null, 2) + "\n"
);
s.write(
"index.js",
"exports.id = module.id;\n"
);
s.cd(s.home);
await s.createApp("modern", "modern");
await s.cd("modern");
s.append(
"server/main.js",
`import { id } from 'config';
console.log('Loaded NPM package "config"', require('config').id);`);
process.env.METEOR_DISABLE_COLORS = true;
await writeModernConfig(s, {
@@ -257,14 +326,18 @@ selftest.define("modern build stack - transpiler string-like options", async fun
run.waitSecs(waitToStart);
await run.match("App running at");
/* check appended NPM package imported */
await run.match(/Loaded NPM package "config"/, false, true);
/* check verbose logs */
await run.match(/SWC Config/, false, true);
await run.match(/SWC Custom Config/, false, true);
await run.match(/SWC Legacy Config/, false, true);
await run.match(/Meteor Config/, false, true);
/* check transpiler options */
await run.match(/\[Transpiler] Used SWC.*\(app\)/, false, true);
await run.match(/\[Transpiler] Used SWC.*\(package\)/, false, true);
run.forbid(/\[Transpiler] Used SWC.*\(node_modules\)/, false, true);
await writeModernConfig(s, {
transpiler: {
@@ -282,6 +355,20 @@ selftest.define("modern build stack - transpiler string-like options", async fun
});
await run.match(/\[Transpiler] Used Babel.*\(package\)/, false, true);
await writeConfig(s, {
modern: {
transpiler: {
verbose: true,
},
},
nodeModules: {
recompile: {
config: true,
},
},
});
await run.match(/\[Transpiler] Used SWC.*\(node_modules\)/, false, true);
await run.stop();
process.env.METEOR_MODERN = currentMeteorModern;

View File

@@ -73,7 +73,7 @@ $env:PATH += ";%JAVA_HOME%\bin"
For Android builds, you will need the Android SDK. You can install it via [Android Studio](https://developer.android.com/studio).
Once Android Studio is installed, go to **SDK Manager** and install the required SDK packages. The minimum required version is Android SDK 34. Install the `Android SDK Command-line Tools (latest)` as well.
Once Android Studio is installed, go to **SDK Manager** and install the required SDK packages. The minimum required version is Android SDK 35. Install the `Android SDK Command-line Tools (latest)` as well.
Ensure `ANDROID_HOME` environment variable is set by adding it to `~/.bashrc` or `~/.zshrc` :

View File

@@ -100,30 +100,41 @@ Most apps will benefit just by enabling `modern: true`. Most Meteor packages sho
> Remember to turn off verbosity when you're done with optimizations.
## Externalize SWC Helpers
By default, SWC inlines transformation helpers (e.g. _extends, _objectSpread) into every file that uses them. While this ensures compatibility out of the box, it can lead to duplicated code across your bundles increasing bundle size.
To centralize these helpers and keep your client builds lean, you can add the `@swc/helpers` in your app project.
``` bash
meteor npm install --save @swc/helpers
```
> This package is installed by default for new apps.
Meteors build tool comes pre-configured to externalize SWC helpers for you, no extra setup or .swcrc tweaks are needed. As soon as you install @swc/helpers, Meteors SWC pipeline will automatically emit imports for shared helper functions rather than inlining them, ensuring your app ships each helper just once.
## Custom .swcrc
You can use `.swcrc` config in the root of your project to describe specific [SWC plugins](https://github.com/swc-project/plugins) there, that will be applied to compile the entire files of your project.
You can also configure other options using the `.swcrc` format. One common case is when your project uses `.js` files for React code. React typically uses `.jsx` for components. We still recommend following that convention for compatibility, but if you prefer `.js`, you can provide a custom `.swcrc` like this:
``` json
{
"jsc": {
"parser": {
"syntax": "ecmascript",
"jsx": true
}
}
}
```
> You can also configure it for TypeScript, make sure to set `"syntax": "typescript"` and `"tsx": true` instead.
This overrides Meteor's internal SWC config to apply your settings, ensuring SWC processes `.js` or `.ts` files with React components without falling back to Babel.
You can also configure other options using the `.swcrc` format. For custom SWC configs, see the [SWC configuration API](https://swc.rs/docs/configuration/compilation).
Use `swc.config.js` in your project root for dynamic configuration. Meteor will import and apply the SWC config automatically. This lets you choose a config based on environment variables or other runtime factors.
Explore additional custom SWC configs, including ["Import Aliases"](#import-aliases) and ["React Runtime"](#react-runtime).
You can also review these migration topics that use custom `.swcrc` configs:
- [Import Aliases](#import-aliases)
- [JSX Syntax in JS files](#jsx-syntax-in-js-files)
- [React Runtime](#react-runtime)
- [Transform Imports](#transform-imports)
- [Private Properties](#private-properties)
:::warning
The standard name for the SWC configuration file is [`.swcrc`](https://swc.rs/docs/configuration/compilation).
Using as an extension, such as `config.swcrc`, wont work.
:::
## Config API
@@ -252,11 +263,32 @@ SWC resolve aliases for imports correctly, but require calls wont. For requir
SWC has no [module-resolver plugin like Babels](https://www.npmjs.com/package/babel-plugin-module-resolver) yet, which could affect require calls in the future.
### JSX Syntax in JS files
When migrating your app to use SWC, Meteor SWC falls back to Babel if you include JSX in `.js` files, since JSX is only recognized in `.jsx` files.
To enable JSX in `.js` files, create a [`.swcrc`](#custom-swcrc) file with this config:
``` json
{
"jsc": {
"parser": {
"syntax": "ecmascript",
"jsx": true
}
}
}
```
> For TypeScript, set "syntax": "typescript" and "tsx": true instead.
This overrides Meteors internal SWC config so SWC handles `.js` and `.ts` files with React components instead of falling back to Babel.
### React Runtime
Meteor Babel lets you skip importing React in your files by using the [`@babel/plugin-transform-react-jsx`](https://www.npmjs.com/package/@babel/plugin-transform-react-jsx) runtime config.
To use the same config in SWC, add it to your [.swcrc](#custom-swcrc):
To use the same config in SWC, add it to your [`.swcrc`](#custom-swcrc):
```json
{
@@ -270,6 +302,79 @@ To use the same config in SWC, add it to your [.swcrc](#custom-swcrc):
}
```
### Transform Imports
You might have used Meteor Babel with the [`babel-plugin-transform-imports`](https://www.npmjs.com/package/babel-plugin-transform-imports) plugin to rewrite imports in your app.
SWC offers a similar plugin: [`@swc/plugin-transform-imports`](https://www.npmjs.com/package/@swc/plugin-transform-imports).
To switch to SWC, install the plugin:
```bash
meteor npm install -D @swc/plugin-transform-imports
```
and add it to your [`.swcrc`](#custom-swcrc):
```json
{
"jsc": {
"experimental": {
"plugins": [
[
"@swc/plugin-transform-imports",
{
"lodash": {
"transform": "lodash/{{member}}",
"preventFullImport": true
}
}
]
]
}
}
}
```
This tells SWC to replace, for example,
``` javascript
import { map } from "lodash"
```
with
``` javascript
import map from "lodash/map"
```
avoiding full-package imports and reducing bundle size.
You can use advanced import transformations. [See the test suite for examples.](https://github.com/swc-project/plugins/blob/main/packages/transform-imports/__tests__/wasm.test.ts#L12-L63)
### Private Properties
SWC supports many of the most modern JS systax features, including private class properties, which Meteor Babel doesnt.
Just by enabling SWC, Meteor will parse properly code like:
``` javascript
class ClassWithPrivate {
#privateField;
#privateFieldWithInitializer = 42;
#privateMethod() {}
static #privateStaticField;
static #privateStaticFieldWithInitializer = 42;
static #privateStaticMethod() {}
}
```
You can opt-out of [private properties in SWC options with "privateMethod" setting](https://swc.rs/docs/configuration/compilation#ecmascript) with the [`.swcrc`](#custom-swcrc) file.
## Troubleshotting
If you run into issues, try `meteor reset` or delete the `.meteor/local` folder in the project root.

View File

@@ -533,7 +533,8 @@ password-based users or from an external service login flow. `options` may come
from an untrusted client so make sure to validate any values you read from
it. The `user` argument is created on the server and contains a
proposed user object with all the automatically generated fields
required for the user to log in, including the `_id`.
required for the user to log in, including a temporary `_id` (the final _id is
generated upon document insertion and not available in this function).
The function should return the user document (either the one passed in or a
newly-created object) with whatever modifications are desired. The returned

View File

@@ -914,6 +914,7 @@ const handle = await cursor.observeChangesAsync({
setTimeout(() => handle.stop(), 5000);
```
<ApiBox name="Mongo.getCollection" />
<ApiBox name="Mongo.ObjectID" />

View File

@@ -0,0 +1,116 @@
## v3.3.1, no-date
### Highlights
- **MongoDB Driver Upgrades**
- Upgraded core MongoDB driver to `6.16.0` to address latest issues reported [#13710](https://github.com/meteor/meteor/pull/13710)
- Introduced `npm-mongo-legacy` to maintain compatibility with MongoDB 3.6 via `mongodb@6.9.0` [#13736](https://github.com/meteor/meteor/pull/13736)
- Mitigated a cursor leak issue by synchronizing `next()` and `close()` operations [#13786](https://github.com/meteor/meteor/pull/13786)
- **Improved SWC integration**
- Fixed edge cases in config cache invalidation [#13809](https://github.com/meteor/meteor/pull/13809)
- Ensured `@swc/helpers` is consistently used for better bundle size and performance [#13820](https://github.com/meteor/meteor/pull/13820)
- Updated to SWC `1.12.14` [#13851](https://github.com/meteor/meteor/pull/13851)
- **Tooling and Build System**
- Fixed regression affecting rebuild behavior [#13810](https://github.com/meteor/meteor/pull/13810)
- Addressed issues getting performance profiles in mounted volumes [#13827](https://github.com/meteor/meteor/pull/13827)
- Fallback to Babel parser when Acorn fails to parse source code [#13844](https://github.com/meteor/meteor/pull/13844)
- **Mobile Support**
- Upgraded Cordova platform to version 14 [#13837](https://github.com/meteor/meteor/pull/13837)
- **Developer Experience**
- Added TypeScript types for `isModern` and `getMinimumBrowserVersions` functions [#13704](https://github.com/meteor/meteor/pull/13704)
- Enhanced CLI help output and documented admin commands [#13826](https://github.com/meteor/meteor/pull/13826)
- **Vite Tooling**
- Updated official Meteor + Vite skeletons [#13835](https://github.com/meteor/meteor/pull/13835)
- **Runtime & Dependencies**
- Updated to Node.js `22.17.1` [#13853](https://github.com/meteor/meteor/pull/13853)
- Bumped `meteor-node-stubs` to `1.2.21` [#13825](https://github.com/meteor/meteor/pull/13825)
All Merged PRs@[GitHub PRs 3.3.1](https://github.com/meteor/meteor/pulls?q=is%3Apr+is%3Amerged+base%3Arelease-3.3.1)
#### Breaking Changes
##### MongoDB Driver Upgrades
If you're using MongoDB 3.6 or earlier, install the new legacy package:
```bash
meteor add npm-mongo-legacy
```
This will pin the MongoDB driver to 6.9.0 for compatibility.
If youre on MongoDB 4+, the default [MongoDB driver 6.16.0](https://github.com/mongodb/node-mongodb-native/releases/tag/v6.16.0) is applied automatically.
Please migrate your database as soon as possible to MongoDB 5 onward, as [MongoDB driver 6.17.0](https://github.com/mongodb/node-mongodb-native/releases/tag/v6.17.0) will drop MongoDB 4 support. Well keep offering `npm-mongo-legacy` so you can keep getting Meteor updates with your existing MongoDB legacy version.
##### Cordova Upgrade
The Cordova platform has been upgraded to version 14. Refer to the [Cordova Changelog](https://cordova.apache.org/announcements/2025/03/26/cordova-android-14.0.0.html) for more details on the changes and migration steps.
#### Internal API changes
N/A
#### Migration Steps
Please run the following command to update your project:
```bash
meteor update --release 3.3.1
```
---
While this is a patch release, Meteor 3.3, a recent minor update, introduced a modern build stack thats now the default for new apps. Heres how you can migrate to it.
**Add this to your `package.json` to enable the new modern build stack:**
```json
"meteor": {
"modern": true
}
```
Check the docs for help with the SWC migration, especially if your project uses many Babel plugins.
[Modern Transpiler: SWC docs](https://docs.meteor.com/about/modern-build-stack/transpiler-swc.html)
If you find any issues, please report them to the [Meteor issues tracker](https://github.com/meteor/meteor).
#### Bumped Meteor Packages
- babel-compiler@7.12.1-rc331.2
- callback-hook@1.6.1-rc331.2
- ecmascript@0.16.12-rc331.2
- minifier-js@3.0.3-rc331.2
- minimongo@2.0.3-rc331.2
- modern-browsers@0.2.3-rc331.2
- mongo@2.1.3-rc331.2
- npm-mongo-legacy@6.9.0-rc331.2
- npm-mongo@6.16.0-rc331.2
- standard-minifier-js@3.1.1-rc331.2
- tinytest@1.3.2-rc331.2
- typescript@5.6.5-rc331.2
- meteor-tool@3.3.1-rc.2
#### Bumped NPM Packages
- meteor-node-stubs@1.2.21
#### Special thanks to
✨✨✨
- [@nachocodoner](https://github.com/nachocodoner)
- [@italojs](https://github.com/italojs)
- [@StorytellerCZ](https://github.com/StorytellerCZ)
- [@JorgenVatle](https://github.com/JorgenVatle)
- [@welkinwong](https://github.com/welkinwong)
- [@Saksham-Goel1107](https://github.com/Saksham-Goel1107)
✨✨✨

View File

@@ -1,6 +1,6 @@
---
meteor_version: 3.3
node_version: 22.16.0
meteor_version: 3.3.1
node_version: 22.17.1
npm_version: 10.9.2
---
# Meteor 3.0 Migration Guide