mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
Merge branch 'devel' into dev-bundle-24.0.2.0
This commit is contained in:
190
.github/scripts/inactive-issues.js
vendored
Normal file
190
.github/scripts/inactive-issues.js
vendored
Normal file
@@ -0,0 +1,190 @@
|
||||
module.exports = async ({ github, context }) => {
|
||||
const daysToComment = 60;
|
||||
const daysToLabel = 90;
|
||||
const now = new Date();
|
||||
const idleTimeComment = daysToComment * 24 * 60 * 60 * 1000; // 60 days in milliseconds
|
||||
const idleTimeLabel = daysToLabel * 24 * 60 * 60 * 1000; // 90 days in milliseconds
|
||||
|
||||
// Function to fetch issues until we find recently updated ones
|
||||
async function fetchAllIssues() {
|
||||
let allIssues = [];
|
||||
let page = 1;
|
||||
let hasNextPage = true;
|
||||
const now = new Date();
|
||||
const minInactivity = idleTimeComment; // 60 days in milliseconds
|
||||
|
||||
while (hasNextPage) {
|
||||
const response = await github.rest.issues.listForRepo({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
state: 'open',
|
||||
per_page: 100,
|
||||
page: page,
|
||||
sort: 'updated',
|
||||
direction: 'asc' // Oldest updated first
|
||||
});
|
||||
|
||||
// Check if the most recently updated issue on this page is too recent
|
||||
let recentIssueFound = false;
|
||||
if (response.data.length > 0) {
|
||||
// Check the last issue on the page (most recently updated)
|
||||
const lastIssue = response.data[response.data.length - 1];
|
||||
const lastIssueUpdatedAt = new Date(lastIssue.updated_at);
|
||||
const timeSinceLastIssueUpdate = now.getTime() - lastIssueUpdatedAt.getTime();
|
||||
|
||||
if (timeSinceLastIssueUpdate < minInactivity) {
|
||||
// This page already has issues that are too recent, filter them out
|
||||
const filteredIssues = response.data.filter(issue => {
|
||||
const issueUpdatedAt = new Date(issue.updated_at);
|
||||
const timeSinceUpdate = now.getTime() - issueUpdatedAt.getTime();
|
||||
return timeSinceUpdate >= minInactivity;
|
||||
});
|
||||
|
||||
allIssues = allIssues.concat(filteredIssues);
|
||||
recentIssueFound = true;
|
||||
hasNextPage = false;
|
||||
} else {
|
||||
// All issues on this page are old enough, keep them all
|
||||
allIssues = allIssues.concat(response.data);
|
||||
}
|
||||
}
|
||||
|
||||
// Stop if we found recent issues or reached the end of pagination
|
||||
if (recentIssueFound) {
|
||||
hasNextPage = false;
|
||||
} else if (response.data.length < 100) {
|
||||
hasNextPage = false;
|
||||
} else {
|
||||
page++;
|
||||
// Small delay to avoid hitting rate limits
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
}
|
||||
}
|
||||
|
||||
return allIssues;
|
||||
}
|
||||
|
||||
// Fetch all issues
|
||||
const allIssues = await fetchAllIssues();
|
||||
|
||||
let processedCount = 0;
|
||||
let commentedCount = 0;
|
||||
let labeledCount = 0;
|
||||
|
||||
for (const issue of allIssues) {
|
||||
processedCount++;
|
||||
|
||||
// Skip pull requests
|
||||
if (issue.pull_request) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip issues that already have the idle label
|
||||
if (issue.labels.some(label => label.name === 'idle')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get latest comment or update date
|
||||
const issueUpdatedAt = new Date(issue.updated_at);
|
||||
const timeSinceUpdate = now.getTime() - issueUpdatedAt.getTime();
|
||||
|
||||
// Handle 60-day idle issues (comment)
|
||||
if (timeSinceUpdate > idleTimeComment && timeSinceUpdate <= idleTimeLabel) {
|
||||
// Check if bot already commented to avoid duplicate comments
|
||||
const comments = await github.rest.issues.listComments({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
per_page: 100
|
||||
});
|
||||
|
||||
// Check if there's a recent bot comment
|
||||
const botCommented = comments.data.some(comment => {
|
||||
const commentDate = new Date(comment.created_at);
|
||||
const timeSinceComment = now.getTime() - commentDate.getTime();
|
||||
const isBot = comment.user.login === 'github-actions[bot]';
|
||||
const isRecent = timeSinceComment < idleTimeComment;
|
||||
const hasRightContent = comment.body.includes('Is this issue still relevant?');
|
||||
|
||||
return isBot && isRecent && hasRightContent;
|
||||
});
|
||||
|
||||
if (!botCommented) {
|
||||
try {
|
||||
const result = await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
body: `👋 @${issue.user.login} This issue has been open for 60 days with no activity. Is this issue still relevant? If there is no response or activity within the next 30 days, this issue will be labeled as \`idle\`.`
|
||||
});
|
||||
commentedCount++;
|
||||
} catch (error) {
|
||||
// Add retry logic
|
||||
try {
|
||||
// Wait for 5 seconds before retrying
|
||||
await new Promise(resolve => setTimeout(resolve, 5000));
|
||||
|
||||
const retryResult = await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
body: `👋 @${issue.user.login} This issue has been open for 60 days with no activity. Is this issue still relevant? If there is no response or activity within the next 30 days, this issue will be labeled as \`idle\`.`
|
||||
});
|
||||
commentedCount++;
|
||||
} catch (retryError) {
|
||||
// Failed retry, continue with other issues
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle 90-day idle issues (add label)
|
||||
else if (timeSinceUpdate > idleTimeLabel) {
|
||||
// Check if the issue has the idle label
|
||||
if (!issue.labels.some(label => label.name === 'idle')) {
|
||||
try {
|
||||
// Add the label
|
||||
const labelResult = await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
labels: ['idle']
|
||||
});
|
||||
|
||||
// Add a comment when labeling as idle
|
||||
const commentResult = await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
body: `This issue has been automatically labeled as \`idle\` due to 90 days of inactivity. If this issue is still relevant, please comment to reactivate it.`
|
||||
});
|
||||
labeledCount++;
|
||||
} catch (error) {
|
||||
// Add retry logic with exponential backoff
|
||||
try {
|
||||
// Wait for 5 seconds before retrying
|
||||
await new Promise(resolve => setTimeout(resolve, 5000));
|
||||
|
||||
const retryLabelResult = await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
labels: ['idle']
|
||||
});
|
||||
|
||||
// Retry adding comment
|
||||
const retryCommentResult = await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
body: `This issue has been automatically labeled as \`idle\` due to 90 days of inactivity. If this issue is still relevant, please comment to reactivate it.`
|
||||
});
|
||||
labeledCount++;
|
||||
} catch (retryError) {
|
||||
// Continue with other issues if retry fails
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
24
.github/workflows/inactive-issues.yml
vendored
Normal file
24
.github/workflows/inactive-issues.yml
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
name: Inactive Issues Management
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# “At 01:00 on Saturday.”
|
||||
- cron: '0 1 * * 6'
|
||||
# Allows to run this workflow manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
manage-inactive-issues:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Manage inactive issues
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
script: |
|
||||
const script = require('./.github/scripts/inactive-issues.js')
|
||||
await script({github, context})
|
||||
@@ -19,7 +19,7 @@ To report an issue in one of the projects listed below, please email code-of-con
|
||||
The Code of Conduct panel is a moderation team that handle code of conduct issues. The makeup of this team is as follows:
|
||||
|
||||
* CEO at Meteor Software - Frederico Maia Arantes
|
||||
* Software Engineer at Meteor Software - Denilson Silva
|
||||
* CTO at Meteor Software - Henrique Albert
|
||||
* CEO at High Impact Tech - Alim S. Gafar
|
||||
|
||||
Members of the CoCP team will be added for a 1-year term and will be re-confirmed on a yearly basis.
|
||||
|
||||
@@ -21,7 +21,7 @@ There are many ways to contribute to the Meteor Project. Here’s a list of tech
|
||||
|
||||
There are also several ways to contribute to the Meteor Project outside of GitHub, like organizing or speaking at [Meetups](https://forums.meteor.com/c/meetups) and events and helping to moderate our [forums](https://forums.meteor.com/).
|
||||
|
||||
If you can think of any changes to the project, [documentation](https://github.com/meteor/meteor/tree/devel/docs), or [guide](https://github.com/meteor/meteor/tree/devel/guide) that would improve the contributor experience, let us know by opening an issue!
|
||||
If you can think of any changes to the project, [documentation](https://github.com/meteor/meteor/tree/devel/v3-docs), or [guide](https://github.com/meteor/meteor/tree/devel/guide) that would improve the contributor experience, let us know by opening an issue!
|
||||
|
||||
### Finding work
|
||||
|
||||
@@ -43,15 +43,14 @@ Reviewers are members of the community who help with Pull Requests reviews.
|
||||
|
||||
Current Reviewers:
|
||||
- [meteor](https://github.com/meteor/meteor)
|
||||
- [@denihs](https://github.com/denihs)
|
||||
- [@fredmaiaarantes](https://github.com/fredmaiaarantes)
|
||||
- [@henriquealbert](https://github.com/henriquealbert)
|
||||
- [@aquinoit](https://github.com/aquinoit)
|
||||
- [@Grubba27](https://github.com/Grubba27)
|
||||
- [@filipenevola](https://github.com/filipenevola)
|
||||
- [@italojs](https://github.com/italojs)
|
||||
- [@nachocodoner](https://github.com/nachocodoner)
|
||||
- [@StorytellerCZ](https://github.com/StorytellerCZ)
|
||||
- [@zodern](https://github.com/zodern)
|
||||
- [@CaptainN](https://github.com/CaptainN)
|
||||
- [@radekmie](https://github.com/radekmie)
|
||||
|
||||
#### Core Committer
|
||||
@@ -59,16 +58,15 @@ Current Reviewers:
|
||||
The contributors with commit access to meteor/meteor are employees of Meteor Software LP or community members who have distinguished themselves in other contribution areas or members of partner companies. If you want to become a core committer, please start writing PRs.
|
||||
|
||||
Current Core Team:
|
||||
- [@denihs](https://github.com/denihs)
|
||||
- [@zodern](https://github.com/zodern)
|
||||
- [@filipenevola](https://github.com/filipenevola)
|
||||
- [@fredmaiaarantes](https://github.com/fredmaiaarantes)
|
||||
- [@henriquealbert](https://github.com/henriquealbert)
|
||||
- [@Grubba27](https://github.com/Grubba27)
|
||||
- [meteor](https://github.com/meteor/meteor)
|
||||
- [@fredmaiaarantes](https://github.com/fredmaiaarantes)
|
||||
- [@henriquealbert](https://github.com/henriquealbert)
|
||||
- [@Grubba27](https://github.com/Grubba27)
|
||||
- [@italojs](https://github.com/italojs)
|
||||
- [@nachocodoner](https://github.com/nachocodoner)
|
||||
- [@StorytellerCZ](https://github.com/StorytellerCZ)
|
||||
- [@CaptainN](https://github.com/CaptainN)
|
||||
- [@zodern](https://github.com/zodern)
|
||||
- [@radekmie](https://github.com/radekmie)
|
||||
- [@matheusccastroo](https://github.com/matheusccastroo)
|
||||
|
||||
### Tracking project work
|
||||
|
||||
|
||||
@@ -83,9 +83,9 @@ meteor
|
||||
|
||||
**Building an application with Meteor?**
|
||||
|
||||
* Deploy on [Galaxy](https://www.meteor.com/cloud)
|
||||
* Deploy on [Galaxy](https://galaxycloud.app)
|
||||
* Discuss on [Forums](https://forums.meteor.com/)
|
||||
* Join the Meteor Discord by clicking this [invite link](https://discord.gg/hZkTCaVjmT).
|
||||
* Join the [Meteor Discord](https://discord.gg/hZkTCaVjmT)
|
||||
|
||||
|
||||
Interested in helping or contributing to Meteor? These resources will help:
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
[//]: # (go to meteor/docs/generators/changelog/docs)
|
||||
|
||||
## v3.3.0, 2025-05-15
|
||||
## v3.3.0, 2025-06-11
|
||||
|
||||
### Highlights
|
||||
|
||||
@@ -23,11 +23,11 @@
|
||||
- Fix user agent detection and oplog collection filtering
|
||||
- Refine type definitions for Meteor methods and SSR's ServerSink
|
||||
- Allow opting out of usage stats with `DO_NOT_TRACK`
|
||||
- Update Node to 22.15.1 and Express to 5.1.0
|
||||
- Update Node to 22.16.0 and Express to 5.1.0
|
||||
|
||||
All Merged PRs@[GitHub PRs 3.3](https://github.com/meteor/meteor/pulls?q=is%3Apr+is%3Amerged+base%3Arelease-3.3)
|
||||
|
||||
React Packages Changelog: [react-meteor-data@4.0.0-beta.0](https://github.com/meteor/react-packages/blob/fb73eeb89ff59664a7a01769fa1c2c880e72a3e5/packages/react-meteor-data/CHANGELOG.md#v400-beta0-xxx)
|
||||
React Packages Changelog: [react-meteor-data@4.0.0](https://github.com/meteor/react-packages/tree/master/packages/react-meteor-data/CHANGELOG.md#v400-2025-06-11)
|
||||
|
||||
#### Breaking Changes
|
||||
|
||||
@@ -36,7 +36,7 @@ React Packages Changelog: [react-meteor-data@4.0.0-beta.0](https://github.com/me
|
||||
- Set `METEOR_WATCH_FORCE_POLLING=true` to enable polling.
|
||||
- Set `METEOR_WATCH_POLLING_INTERVAL_MS=1000` to adjust the interval.
|
||||
|
||||
- `react-meteor-data@4.0.0-beta.0`
|
||||
- `react-meteor-data@4.0.0`
|
||||
- Independent from the core, only applies if upgraded manually.
|
||||
- useFind describes no deps by default [PR#431](https://github.com/meteor/react-packages/pull/431)
|
||||
|
||||
@@ -51,13 +51,13 @@ React Packages Changelog: [react-meteor-data@4.0.0-beta.0](https://github.com/me
|
||||
Please run the following command to update your project:
|
||||
|
||||
```bash
|
||||
meteor update --release 3.3-beta.1
|
||||
meteor update --release 3.3
|
||||
```
|
||||
|
||||
To apply react-meteor-data changes:
|
||||
|
||||
```bash
|
||||
meteor add react-meteor-data@4.0.0-beta.0
|
||||
meteor add react-meteor-data@4.0.0
|
||||
```
|
||||
|
||||
**Add this to your `package.json` to enable the new modern build stack:**
|
||||
@@ -70,6 +70,14 @@ meteor add react-meteor-data@4.0.0-beta.0
|
||||
|
||||
> These settings are on by default for new apps.
|
||||
|
||||
On activate `modern` your app will be updated to use SWC transpiler. It will automatically fallback to Babel if your code can't be transpiled wit SWC.
|
||||
|
||||
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
|
||||
|
||||
- accounts-base@3.1.1
|
||||
@@ -81,10 +89,13 @@ meteor add react-meteor-data@4.0.0-beta.0
|
||||
- ecmascript@0.16.11
|
||||
- ejson@1.1.5
|
||||
- meteor@2.1.1
|
||||
- minifier-js@3.0.2
|
||||
- modern-browsers@0.2.2
|
||||
- mongo@2.1.2
|
||||
- server-render@0.4.3
|
||||
- socket-stream-client@0.6.1
|
||||
- standard-minifier-js@3.1.0
|
||||
- typescript@5.6.4
|
||||
- webapp@2.0.7
|
||||
- meteor-tool@3.3.0
|
||||
|
||||
@@ -106,9 +117,11 @@ meteor add react-meteor-data@4.0.0-beta.0
|
||||
- [@PedroMarianoAlmeida](https://github.com/PedroMarianoAlmeida)
|
||||
- [@harryadel](https://github.com/harryadel)
|
||||
- [@ericm546](https://github.com/ericm546)
|
||||
- [@StorytellerCZ](https://github.com/StorytellerCZ)
|
||||
|
||||
✨✨✨
|
||||
|
||||
|
||||
## v3.0.1, 2024-07-16
|
||||
|
||||
### Highlights
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
|
||||
const METEOR_LATEST_VERSION = '3.2';
|
||||
const METEOR_LATEST_VERSION = '3.3';
|
||||
const sudoUser = process.env.SUDO_USER || '';
|
||||
function isRoot() {
|
||||
return process.getuid && process.getuid() === 0;
|
||||
|
||||
4
npm-packages/meteor-installer/package-lock.json
generated
4
npm-packages/meteor-installer/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "meteor",
|
||||
"version": "3.2.0",
|
||||
"version": "3.3.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "meteor",
|
||||
"version": "3.2.0",
|
||||
"version": "3.3.0",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "meteor",
|
||||
"version": "3.2.0",
|
||||
"version": "3.3.0",
|
||||
"description": "Install Meteor",
|
||||
"main": "install.js",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Package.describe({
|
||||
summary: "A user account system",
|
||||
version: "3.1.1-beta330.1",
|
||||
version: "3.1.1",
|
||||
});
|
||||
|
||||
Package.onUse((api) => {
|
||||
|
||||
@@ -5,7 +5,7 @@ Package.describe({
|
||||
// 2.2.x in the future. The version was also bumped to 2.0.0 temporarily
|
||||
// during the Meteor 1.5.1 release process, so versions 2.0.0-beta.2
|
||||
// through -beta.5 and -rc.0 have already been published.
|
||||
version: "3.2.0-beta330.1",
|
||||
version: "3.2.0",
|
||||
});
|
||||
|
||||
Npm.depends({
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Package.describe({
|
||||
summary: 'Update the client when new client code is available',
|
||||
version: '2.0.1-beta330.1',
|
||||
version: '2.0.1',
|
||||
});
|
||||
|
||||
Package.onUse(function(api) {
|
||||
|
||||
@@ -5,15 +5,17 @@ const reifyCompile = Npm.require("@meteorjs/reify/lib/compiler").compile;
|
||||
const reifyAcornParse = Npm.require("@meteorjs/reify/lib/parsers/acorn").parse;
|
||||
var fs = Npm.require('fs');
|
||||
var path = Npm.require('path');
|
||||
var vm = Npm.require('vm');
|
||||
var crypto = Npm.require('crypto');
|
||||
|
||||
/**
|
||||
* A compiler that can be instantiated with features and used inside
|
||||
* Plugin.registerCompiler
|
||||
* @param {Object} extraFeatures The same object that getDefaultOptions takes
|
||||
*/
|
||||
BabelCompiler = function BabelCompiler(extraFeatures, modifyBabelConfig) {
|
||||
BabelCompiler = function BabelCompiler(extraFeatures, modifyConfig) {
|
||||
this.extraFeatures = extraFeatures;
|
||||
this.modifyBabelConfig = modifyBabelConfig;
|
||||
this.modifyConfig = modifyConfig;
|
||||
this._babelrcCache = null;
|
||||
this._babelrcWarnings = Object.create(null);
|
||||
this.cacheDirectory = null;
|
||||
@@ -23,6 +25,17 @@ var BCp = BabelCompiler.prototype;
|
||||
var excludedFileExtensionPattern = /\.(es5|min)\.js$/i;
|
||||
var hasOwn = Object.prototype.hasOwnProperty;
|
||||
|
||||
// Check if verbose mode is enabled either in the provided config or in extraFeatures
|
||||
BCp.isVerbose = function(config) {
|
||||
if (config?.modern?.transpiler?.verbose) {
|
||||
return true;
|
||||
}
|
||||
if (config?.verbose) {
|
||||
return true;
|
||||
}
|
||||
return !!this.extraFeatures?.verbose;
|
||||
};
|
||||
|
||||
// There's no way to tell the current Meteor version, but we can infer
|
||||
// whether it's Meteor 1.4.4 or earlier by checking the Node version.
|
||||
var isMeteorPre144 = semver.lt(process.version, "4.8.1");
|
||||
@@ -35,42 +48,10 @@ function compileWithBabel(source, babelOptions, cacheOptions) {
|
||||
});
|
||||
}
|
||||
|
||||
function compileWithSwc(source, swcOptions = {}, { inputFilePath, filename, sourceFileName, features, arch }) {
|
||||
function compileWithSwc(source, swcOptions = {}, { features }) {
|
||||
return profile('SWC.compile', function () {
|
||||
// Determine file extension based syntax.
|
||||
const isTypescriptSyntax = inputFilePath.endsWith('.ts') || inputFilePath.endsWith('.tsx');
|
||||
const hasTSXSupport = inputFilePath.endsWith('.tsx');
|
||||
const hasJSXSupport = inputFilePath.endsWith('.jsx');
|
||||
|
||||
const isLegacyWebArch = arch.includes('legacy');
|
||||
const baseSwcConfig = {
|
||||
jsc: {
|
||||
...(!isLegacyWebArch && { target: 'es2015' }),
|
||||
parser: {
|
||||
syntax: isTypescriptSyntax ? 'typescript' : 'ecmascript',
|
||||
jsx: hasJSXSupport,
|
||||
tsx: hasTSXSupport,
|
||||
},
|
||||
},
|
||||
module: { type: 'es6' },
|
||||
minify: false,
|
||||
sourceMaps: true,
|
||||
filename,
|
||||
sourceFileName,
|
||||
...(isLegacyWebArch && {
|
||||
env: { targets: lastModifiedSwcLegacyConfig || {} },
|
||||
}),
|
||||
};
|
||||
const nextSwcConfig =
|
||||
Object.keys(swcOptions)?.length > 0
|
||||
? deepMerge(baseSwcConfig, swcOptions, [
|
||||
'env.targets',
|
||||
'module.type',
|
||||
])
|
||||
: baseSwcConfig;
|
||||
|
||||
// Perform SWC transformation.
|
||||
const transformed = SWC.transformSync(source, nextSwcConfig);
|
||||
const transformed = SWC.transformSync(source, swcOptions);
|
||||
|
||||
let content = transformed.code;
|
||||
|
||||
@@ -135,7 +116,7 @@ BCp.initializeMeteorAppConfig = function () {
|
||||
modern: normalizeModern(modernForced || lastModifiedMeteorConfig?.modern),
|
||||
} : {};
|
||||
|
||||
if (lastModifiedMeteorConfig?.modern?.transpiler?.verbose) {
|
||||
if (this.isVerbose(lastModifiedMeteorConfig)) {
|
||||
logConfigBlock('Meteor Config', lastModifiedMeteorConfig);
|
||||
}
|
||||
}
|
||||
@@ -145,22 +126,39 @@ BCp.initializeMeteorAppConfig = function () {
|
||||
let lastModifiedSwcConfig;
|
||||
let lastModifiedSwcConfigTime;
|
||||
BCp.initializeMeteorAppSwcrc = function () {
|
||||
if (!lastModifiedSwcConfig && !fs.existsSync(`${getMeteorAppDir()}/.swcrc`)) {
|
||||
const hasSwcRc = fs.existsSync(`${getMeteorAppDir()}/.swcrc`);
|
||||
const hasSwcJs = !hasSwcRc && fs.existsSync(`${getMeteorAppDir()}/swc.config.js`);
|
||||
if (!lastModifiedSwcConfig && !hasSwcRc && !hasSwcJs) {
|
||||
return;
|
||||
}
|
||||
const currentLastModifiedConfigTime = fs
|
||||
.statSync(`${getMeteorAppDir()}/.swcrc`)
|
||||
?.mtime?.getTime();
|
||||
const swcFile = hasSwcJs ? 'swc.config.js' : '.swcrc';
|
||||
const filePath = `${getMeteorAppDir()}/${swcFile}`;
|
||||
const fileStats = fs.statSync(filePath);
|
||||
const fileModTime = fileStats?.mtime?.getTime();
|
||||
|
||||
let currentLastModifiedConfigTime;
|
||||
if (hasSwcJs) {
|
||||
// For dynamic JS files, first get the resolved configuration
|
||||
const resolvedConfig = lastModifiedSwcConfig || getMeteorAppSwcrc(swcFile);
|
||||
// Calculate a hash of the resolved configuration to detect changes
|
||||
const contentHash = crypto
|
||||
.createHash('sha256')
|
||||
.update(JSON.stringify(resolvedConfig))
|
||||
.digest('hex');
|
||||
// Combine file modification time and content hash to create a unique identifier
|
||||
currentLastModifiedConfigTime = `${fileModTime}-${contentHash}`;
|
||||
// Store the resolved configuration
|
||||
lastModifiedSwcConfig = resolvedConfig;
|
||||
} else {
|
||||
// For static JSON files, just use the file modification time
|
||||
currentLastModifiedConfigTime = fileModTime;
|
||||
}
|
||||
|
||||
if (currentLastModifiedConfigTime !== lastModifiedSwcConfigTime) {
|
||||
lastModifiedSwcConfigTime = currentLastModifiedConfigTime;
|
||||
lastModifiedSwcConfig = getMeteorAppSwcrc();
|
||||
lastModifiedSwcConfig = getMeteorAppSwcrc(swcFile);
|
||||
|
||||
// Resolve custom baseUrl to an absolute path pointing to the project root
|
||||
if (lastModifiedSwcConfig.jsc && lastModifiedSwcConfig.jsc.baseUrl) {
|
||||
lastModifiedSwcConfig.jsc.baseUrl = path.resolve(process.cwd(), lastModifiedSwcConfig.jsc.baseUrl);
|
||||
}
|
||||
|
||||
if (lastModifiedMeteorConfig?.modern?.transpiler?.verbose) {
|
||||
if (this.isVerbose(lastModifiedMeteorConfig)) {
|
||||
logConfigBlock('SWC Config', lastModifiedSwcConfig);
|
||||
}
|
||||
}
|
||||
@@ -170,7 +168,7 @@ BCp.initializeMeteorAppSwcrc = function () {
|
||||
let lastModifiedSwcLegacyConfig;
|
||||
BCp.initializeMeteorAppLegacyConfig = function () {
|
||||
const swcLegacyConfig = convertBabelTargetsForSwc(Babel.getMinimumModernBrowserVersions());
|
||||
if (lastModifiedMeteorConfig?.modern?.transpiler?.verbose && !lastModifiedSwcLegacyConfig) {
|
||||
if (this.isVerbose(lastModifiedMeteorConfig) && !lastModifiedSwcLegacyConfig) {
|
||||
logConfigBlock('SWC Legacy Config', swcLegacyConfig);
|
||||
}
|
||||
lastModifiedSwcLegacyConfig = swcLegacyConfig;
|
||||
@@ -280,23 +278,76 @@ BCp.processOneFileForTarget = function (inputFile, source) {
|
||||
},
|
||||
};
|
||||
|
||||
this.inferTypeScriptConfig(features, inputFile, cacheOptions.cacheDeps);
|
||||
|
||||
var babelOptions = Babel.getDefaultOptions(features);
|
||||
babelOptions.caller = { name: "meteor", arch };
|
||||
|
||||
this.inferExtraBabelOptions(inputFile, babelOptions, cacheOptions.cacheDeps);
|
||||
|
||||
babelOptions.sourceMaps = true;
|
||||
const filename = packageName
|
||||
? `packages/${packageName}/${inputFilePath}`
|
||||
: inputFilePath;
|
||||
babelOptions.filename = babelOptions.sourceFileName = filename;
|
||||
? `packages/${packageName}/${inputFilePath}`
|
||||
: inputFilePath;
|
||||
|
||||
if (this.modifyBabelConfig) {
|
||||
this.modifyBabelConfig(babelOptions, inputFile);
|
||||
}
|
||||
const setupBabelOptions = () => {
|
||||
this.inferTypeScriptConfig(features, inputFile, cacheOptions.cacheDeps);
|
||||
|
||||
var babelOptions = Babel.getDefaultOptions(features);
|
||||
babelOptions.caller = { name: "meteor", arch };
|
||||
|
||||
babelOptions.sourceMaps = true;
|
||||
babelOptions.filename = babelOptions.sourceFileName = filename;
|
||||
|
||||
this.inferExtraBabelOptions(inputFile, babelOptions, cacheOptions.cacheDeps);
|
||||
|
||||
if (this.modifyConfig) {
|
||||
this.modifyConfig(babelOptions, inputFile);
|
||||
}
|
||||
|
||||
return babelOptions;
|
||||
};
|
||||
|
||||
const setupSWCOptions = () => {
|
||||
const isTypescriptSyntax = inputFilePath.endsWith('.ts') || inputFilePath.endsWith('.tsx');
|
||||
const hasTSXSupport = inputFilePath.endsWith('.tsx');
|
||||
const hasJSXSupport = inputFilePath.endsWith('.jsx');
|
||||
const isLegacyWebArch = arch.includes('legacy');
|
||||
|
||||
var swcOptions = {
|
||||
jsc: {
|
||||
...(!isLegacyWebArch && { target: 'es2015' }),
|
||||
parser: {
|
||||
syntax: isTypescriptSyntax ? 'typescript' : 'ecmascript',
|
||||
jsx: hasJSXSupport,
|
||||
tsx: hasTSXSupport,
|
||||
},
|
||||
},
|
||||
module: { type: 'es6' },
|
||||
minify: false,
|
||||
sourceMaps: true,
|
||||
filename,
|
||||
sourceFileName: filename,
|
||||
...(isLegacyWebArch && {
|
||||
env: { targets: lastModifiedSwcLegacyConfig || {} },
|
||||
}),
|
||||
};
|
||||
|
||||
// Merge with app-level SWC config
|
||||
if (lastModifiedSwcConfig) {
|
||||
swcOptions = deepMerge(swcOptions, lastModifiedSwcConfig, [
|
||||
'env.targets',
|
||||
'module.type',
|
||||
]);
|
||||
}
|
||||
|
||||
this.inferExtraSWCOptions(inputFile, swcOptions, cacheOptions.cacheDeps);
|
||||
|
||||
if (!!this.extraFeatures?.swc && this.modifyConfig) {
|
||||
this.modifyConfig(swcOptions, inputFile);
|
||||
}
|
||||
|
||||
// Resolve custom baseUrl to an absolute path pointing to the project root
|
||||
if (swcOptions.jsc && swcOptions.jsc.baseUrl) {
|
||||
swcOptions.jsc.baseUrl = path.resolve(process.cwd(), swcOptions.jsc.baseUrl);
|
||||
}
|
||||
|
||||
return swcOptions;
|
||||
};
|
||||
|
||||
var babelOptions = { filename };
|
||||
try {
|
||||
var result = (() => {
|
||||
const isNodeModulesCode = packageName == null && inputFilePath.includes("node_modules/");
|
||||
@@ -305,7 +356,7 @@ BCp.processOneFileForTarget = function (inputFile, source) {
|
||||
const isLegacyWebArch = arch.includes('legacy');
|
||||
|
||||
const config = lastModifiedMeteorConfig?.modern?.transpiler;
|
||||
const hasModernTranspiler = lastModifiedMeteorConfig?.modern?.transpiler !== false;
|
||||
const hasModernTranspiler = config != null && config !== false;
|
||||
const shouldSkipSwc =
|
||||
!hasModernTranspiler ||
|
||||
(isAppCode && config?.excludeApp === true) ||
|
||||
@@ -339,8 +390,9 @@ BCp.processOneFileForTarget = function (inputFile, source) {
|
||||
.filter(Boolean)
|
||||
.join('-');
|
||||
// Determine if SWC should be used based on package and file criteria.
|
||||
const shouldUseSwc = !shouldSkipSwc && !this._swcIncompatible[cacheKey];
|
||||
|
||||
const shouldUseSwc =
|
||||
(!shouldSkipSwc || this.extraFeatures?.swc) &&
|
||||
!this._swcIncompatible[cacheKey];
|
||||
let compilation;
|
||||
try {
|
||||
let usedSwc = false;
|
||||
@@ -350,7 +402,7 @@ BCp.processOneFileForTarget = function (inputFile, source) {
|
||||
compilation = this.readFromSwcCache({ cacheKey });
|
||||
// Return cached result if found.
|
||||
if (compilation) {
|
||||
if (config?.verbose) {
|
||||
if (this.isVerbose(config)) {
|
||||
logTranspilation({
|
||||
usedSwc: true,
|
||||
inputFilePath,
|
||||
@@ -363,21 +415,24 @@ BCp.processOneFileForTarget = function (inputFile, source) {
|
||||
return compilation;
|
||||
}
|
||||
|
||||
const sourceFileName = filename;
|
||||
const swcOptions = setupSWCOptions();
|
||||
compilation = compileWithSwc(
|
||||
source,
|
||||
lastModifiedSwcConfig,
|
||||
{ inputFilePath, features, arch, filename, sourceFileName },
|
||||
swcOptions,
|
||||
{ features },
|
||||
);
|
||||
// Save result in cache
|
||||
this.writeToSwcCache({ cacheKey, compilation });
|
||||
usedSwc = true;
|
||||
} else {
|
||||
// Set up Babel options only when compiling with Babel
|
||||
babelOptions = setupBabelOptions();
|
||||
|
||||
compilation = compileWithBabel(source, babelOptions, cacheOptions);
|
||||
usedSwc = false;
|
||||
}
|
||||
|
||||
if (config?.verbose) {
|
||||
if (this.isVerbose(config)) {
|
||||
logTranspilation({
|
||||
usedSwc,
|
||||
inputFilePath,
|
||||
@@ -390,8 +445,10 @@ BCp.processOneFileForTarget = function (inputFile, source) {
|
||||
} catch (e) {
|
||||
this._swcIncompatible[cacheKey] = true;
|
||||
// If SWC fails, fall back to Babel
|
||||
|
||||
babelOptions = setupBabelOptions();
|
||||
compilation = compileWithBabel(source, babelOptions, cacheOptions);
|
||||
if (config?.verbose) {
|
||||
if (this.isVerbose(config)) {
|
||||
logTranspilation({
|
||||
usedSwc: false,
|
||||
inputFilePath,
|
||||
@@ -520,6 +577,15 @@ BCp.inferExtraBabelOptions = function (inputFile, babelOptions, cacheDeps) {
|
||||
);
|
||||
};
|
||||
|
||||
BCp.inferExtraSWCOptions = function (inputFile, swcOptions, cacheDeps) {
|
||||
if (! inputFile.require ||
|
||||
! inputFile.findControlFile ||
|
||||
! inputFile.readAndWatchFile) {
|
||||
return false;
|
||||
}
|
||||
return this._inferFromSwcRc(inputFile, swcOptions, cacheDeps);
|
||||
};
|
||||
|
||||
BCp._inferFromBabelRc = function (inputFile, babelOptions, cacheDeps) {
|
||||
var babelrcPath = inputFile.findControlFile(".babelrc");
|
||||
if (babelrcPath) {
|
||||
@@ -572,6 +638,65 @@ BCp._inferFromPackageJson = function (inputFile, babelOptions, cacheDeps) {
|
||||
}
|
||||
};
|
||||
|
||||
BCp._inferFromSwcRc = function (inputFile, swcOptions, cacheDeps) {
|
||||
var swcrcPath = inputFile.findControlFile(".swcrc");
|
||||
if (swcrcPath) {
|
||||
if (! hasOwn.call(this._babelrcCache, swcrcPath)) {
|
||||
try {
|
||||
this._babelrcCache[swcrcPath] = {
|
||||
controlFilePath: swcrcPath,
|
||||
controlFileData: JSON.parse(
|
||||
inputFile.readAndWatchFile(swcrcPath)),
|
||||
deps: Object.create(null),
|
||||
};
|
||||
} catch (e) {
|
||||
if (e instanceof SyntaxError) {
|
||||
e.message = ".swcrc is not a valid JSON file: " + e.message;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
const cacheEntry = this._babelrcCache[swcrcPath];
|
||||
|
||||
if (this._inferHelperForSwc(inputFile, cacheEntry)) {
|
||||
deepMerge(swcOptions, cacheEntry.controlFileData);
|
||||
Object.assign(cacheDeps, cacheEntry.deps);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
BCp._inferHelperForSwc = function (inputFile, cacheEntry) {
|
||||
if (! cacheEntry.controlFileData) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hasOwn.call(cacheEntry, "finalInferHelperForSwcResult")) {
|
||||
// We've already run _inferHelperForSwc and populated
|
||||
// cacheEntry.controlFileData, so we can return early here.
|
||||
return cacheEntry.finalInferHelperForSwcResult;
|
||||
}
|
||||
|
||||
// First, ensure that the current file path is not excluded.
|
||||
if (cacheEntry.controlFileData.exclude) {
|
||||
const exclude = cacheEntry.controlFileData.exclude;
|
||||
const path = inputFile.getPathInPackage();
|
||||
|
||||
if (exclude instanceof Array) {
|
||||
for (let i = 0; i < exclude.length; ++i) {
|
||||
if (path.match(exclude[i])) {
|
||||
return cacheEntry.finalInferHelperForSwcResult = false;
|
||||
}
|
||||
}
|
||||
} else if (path.match(exclude)) {
|
||||
return cacheEntry.finalInferHelperForSwcResult = false;
|
||||
}
|
||||
}
|
||||
|
||||
return cacheEntry.finalInferHelperForSwcResult = true;
|
||||
};
|
||||
|
||||
BCp._inferHelper = function (inputFile, cacheEntry) {
|
||||
if (! cacheEntry.controlFileData) {
|
||||
return false;
|
||||
@@ -879,11 +1004,34 @@ function getMeteorAppPackageJson() {
|
||||
);
|
||||
}
|
||||
|
||||
function getMeteorAppSwcrc() {
|
||||
function getMeteorAppSwcrc(file = '.swcrc') {
|
||||
try {
|
||||
return JSON.parse(fs.readFileSync(`${getMeteorAppDir()}/.swcrc`, 'utf-8'));
|
||||
const filePath = `${getMeteorAppDir()}/${file}`;
|
||||
if (file.endsWith('.js')) {
|
||||
let content = fs.readFileSync(filePath, 'utf-8');
|
||||
// Check if the content uses ES module syntax (export default)
|
||||
if (content.includes('export default')) {
|
||||
// Transform ES module syntax to CommonJS
|
||||
content = content.replace(/export\s+default\s+/, 'module.exports = ');
|
||||
}
|
||||
const script = new vm.Script(`
|
||||
(function() {
|
||||
const module = {};
|
||||
module.exports = {};
|
||||
(function(exports, module) {
|
||||
${content}
|
||||
})(module.exports, module);
|
||||
return module.exports;
|
||||
})()
|
||||
`);
|
||||
const context = vm.createContext({ process });
|
||||
return script.runInContext(context);
|
||||
} else {
|
||||
// For .swcrc and other JSON files, parse as JSON
|
||||
return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error parsing .swcrc file', e);
|
||||
console.error(`Error parsing ${file} file`, e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -991,7 +1139,7 @@ function logConfigBlock(description, configObject) {
|
||||
console.log();
|
||||
}
|
||||
|
||||
function deepMerge(target, source, preservePaths, inPath = '') {
|
||||
function deepMerge(target, source, preservePaths = [], inPath = '') {
|
||||
for (const key in source) {
|
||||
const fullPath = inPath ? `${inPath}.${key}` : key;
|
||||
|
||||
@@ -1032,3 +1180,18 @@ function convertBabelTargetsForSwc(babelTargets) {
|
||||
|
||||
return filteredTargets;
|
||||
}
|
||||
|
||||
/**
|
||||
* A compiler that extends BabelCompiler but always uses SWC
|
||||
* @param {Object} extraFeatures Additional features to pass to BabelCompiler
|
||||
* @param {Function} modifyConfig Function to modify the configuration
|
||||
*/
|
||||
SwcCompiler = function SwcCompiler(extraFeatures, modifyConfig) {
|
||||
extraFeatures = extraFeatures || {};
|
||||
extraFeatures.swc = true;
|
||||
BabelCompiler.call(this, extraFeatures, modifyConfig);
|
||||
};
|
||||
|
||||
// Inherit from BabelCompiler
|
||||
SwcCompiler.prototype = Object.create(BabelCompiler.prototype);
|
||||
SwcCompiler.prototype.constructor = SwcCompiler;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Package.describe({
|
||||
name: "babel-compiler",
|
||||
summary: "Parser/transpiler for ECMAScript 2015+ syntax",
|
||||
version: '7.12.0-beta330.1',
|
||||
version: '7.12.0',
|
||||
});
|
||||
|
||||
Npm.depends({
|
||||
@@ -23,4 +23,5 @@ Package.onUse(function (api) {
|
||||
|
||||
api.export('Babel', 'server');
|
||||
api.export('BabelCompiler', 'server');
|
||||
api.export('SwcCompiler', 'server');
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Package.describe({
|
||||
summary: "Generates the boilerplate html from program's manifest",
|
||||
version: '2.0.1-beta330.1',
|
||||
version: '2.0.1',
|
||||
});
|
||||
|
||||
Npm.depends({
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Package.describe({
|
||||
summary: "Meteor's latency-compensated distributed data client",
|
||||
version: "3.1.1-beta330.1",
|
||||
version: "3.1.1",
|
||||
documentation: null,
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Package.describe({
|
||||
name: 'ecmascript',
|
||||
version: '0.16.11-beta330.1',
|
||||
version: '0.16.11',
|
||||
summary: 'Compiler plugin that supports ES2015+ in all .js files',
|
||||
documentation: 'README.md',
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Package.describe({
|
||||
summary: 'Extended and Extensible JSON library',
|
||||
version: '1.1.5-beta330.1',
|
||||
version: '1.1.5',
|
||||
});
|
||||
|
||||
Package.onUse(function onUse(api) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Package.describe({
|
||||
summary: "The Meteor command-line tool",
|
||||
version: "3.3.0-beta.1",
|
||||
version: "3.3.0",
|
||||
});
|
||||
|
||||
Package.includeTool();
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Package.describe({
|
||||
summary: "Core Meteor environment",
|
||||
version: '2.1.1-beta330.1',
|
||||
version: '2.1.1',
|
||||
});
|
||||
|
||||
Package.registerBuildPlugin({
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Package.describe({
|
||||
summary: "JavaScript minifier",
|
||||
version: '3.0.1',
|
||||
version: '3.0.2',
|
||||
});
|
||||
|
||||
Npm.depends({
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Package.describe({
|
||||
name: 'modern-browsers',
|
||||
version: '0.2.2-beta330.1',
|
||||
version: '0.2.2',
|
||||
summary:
|
||||
'API for defining the boundary between modern and legacy ' +
|
||||
'JavaScript clients',
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
validateCollectionName
|
||||
} from './collection_utils';
|
||||
import { ReplicationMethods } from './methods_replication';
|
||||
import { watchChangeStream } from './watch_change_stream';
|
||||
|
||||
/**
|
||||
* @summary Namespace for MongoDB-related items
|
||||
@@ -263,6 +264,10 @@ Mongo.Collection.ObjectID = Mongo.ObjectID;
|
||||
*/
|
||||
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 });
|
||||
|
||||
|
||||
31
packages/mongo/collection/watch_change_stream.js
Normal file
31
packages/mongo/collection/watch_change_stream.js
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* @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);
|
||||
}
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
Package.describe({
|
||||
summary: "Adaptor for using MongoDB and Minimongo over DDP",
|
||||
version: "2.1.2-beta330.1",
|
||||
version: "2.1.2",
|
||||
});
|
||||
|
||||
Npm.depends({
|
||||
|
||||
@@ -3,7 +3,7 @@ Package.describe({
|
||||
summary: 'Compiler for CoffeeScript code, supporting the coffeescript package',
|
||||
// This version of NPM `coffeescript` module, with _1, _2 etc.
|
||||
// If you change this, make sure to also update ../coffeescript/package.js to match.
|
||||
version: '2.4.2'
|
||||
version: '2.4.2',
|
||||
});
|
||||
|
||||
Npm.depends({
|
||||
|
||||
@@ -6,12 +6,12 @@ Package.describe({
|
||||
// so bumping the version of this package will be how they get newer versions
|
||||
// of `coffeescript-compiler`. If you change this, make sure to also update
|
||||
// ../coffeescript-compiler/package.js to match.
|
||||
version: '2.7.2'
|
||||
version: '2.7.3',
|
||||
});
|
||||
|
||||
Package.registerBuildPlugin({
|
||||
name: 'compile-coffeescript',
|
||||
use: ['caching-compiler@2.0.0-rc300.2', 'ecmascript@0.16.9-rc300.2', 'coffeescript-compiler@2.4.1'],
|
||||
use: ['caching-compiler@2.0.1', 'ecmascript@0.16.11', 'coffeescript-compiler@2.4.2'],
|
||||
sources: ['compile-coffeescript.js'],
|
||||
npmDependencies: {
|
||||
// A breaking change was introduced in @babel/runtime@7.0.0-beta.56
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Package.describe({
|
||||
name: "server-render",
|
||||
version: '0.4.3-beta330.1',
|
||||
version: '0.4.3',
|
||||
summary: "Generic support for server-side rendering in Meteor apps",
|
||||
documentation: "README.md"
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Package.describe({
|
||||
name: "socket-stream-client",
|
||||
version: '0.6.1-beta330.1',
|
||||
version: '0.6.1',
|
||||
summary: "Provides the ClientStream abstraction used by ddp-client",
|
||||
documentation: "README.md"
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Package.describe({
|
||||
name: 'standard-minifier-js',
|
||||
version: '3.1.0-beta330.1',
|
||||
version: '3.1.0',
|
||||
summary: 'Standard javascript minifiers used with Meteor apps by default.',
|
||||
documentation: 'README.md',
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Package.describe({
|
||||
name: 'typescript',
|
||||
version: '5.6.3',
|
||||
version: '5.6.4',
|
||||
summary:
|
||||
'Compiler plugin that compiles TypeScript and ECMAScript in .ts and .tsx files',
|
||||
documentation: 'README.md',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Package.describe({
|
||||
summary: "Serves a Meteor app over HTTP",
|
||||
version: "2.0.7-beta330.1",
|
||||
version: "2.0.7",
|
||||
});
|
||||
|
||||
Npm.depends({
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"track": "METEOR",
|
||||
"version": "3.3-beta.1",
|
||||
"version": "3.3-rc.0",
|
||||
"recommended": false,
|
||||
"official": false,
|
||||
"description": "Meteor experimental release"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"track": "METEOR",
|
||||
"version": "3.2.2",
|
||||
"version": "3.3",
|
||||
"recommended": false,
|
||||
"official": true,
|
||||
"description": "The Official Meteor Distribution"
|
||||
|
||||
@@ -301,6 +301,9 @@ async function ensureWatchRoot(dirPath: string): Promise<void> {
|
||||
osDirPath,
|
||||
(err, events) => {
|
||||
if (err) {
|
||||
if (/Events were dropped/.test(err.message)) {
|
||||
return;
|
||||
}
|
||||
console.error(`Parcel watcher error on ${osDirPath}:`, err);
|
||||
// Only disable native watching for critical errors (like ENOSPC).
|
||||
// @ts-ignore
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"jsc": {
|
||||
"parser": {
|
||||
"syntax": "ecmascript",
|
||||
"jsx": true
|
||||
},
|
||||
"transform": {
|
||||
"legacyDecorator": true,
|
||||
"decoratorMetadata": true
|
||||
},
|
||||
"target": "es2015"
|
||||
},
|
||||
"module": {
|
||||
"type": "es6"
|
||||
},
|
||||
"minify": false,
|
||||
"sourceMaps": true
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
[
|
||||
[1, 0, -4],
|
||||
"asdf"
|
||||
]
|
||||
@@ -0,0 +1,19 @@
|
||||
import { strictEqual } from "assert";
|
||||
s
|
||||
// Test that the SWC configuration in .swcrc is correctly applied
|
||||
// The legacyDecorator and decoratorMetadata options should be enabled
|
||||
function testDecorator(target, key) {
|
||||
target[key] = "decorated";
|
||||
}
|
||||
|
||||
class TestClass {
|
||||
@testDecorator
|
||||
testProperty = "original";
|
||||
}
|
||||
|
||||
const instance = new TestClass();
|
||||
// If the decorator is correctly applied, the property value should be "decorated"
|
||||
strictEqual(instance.testProperty, "decorated");
|
||||
|
||||
export { default as one } from "./one";
|
||||
export { default as array } from "./array";
|
||||
@@ -0,0 +1,8 @@
|
||||
[
|
||||
{
|
||||
"string": 1,
|
||||
"number": 2
|
||||
},
|
||||
"one",
|
||||
1
|
||||
]
|
||||
@@ -0,0 +1,29 @@
|
||||
Package.describe({
|
||||
name: "modules-test-plugin",
|
||||
version: "0.0.1",
|
||||
summary: "",
|
||||
// By default, Meteor will default to using README.md for documentation.
|
||||
// To avoid submitting documentation, set this field to null.
|
||||
documentation: null
|
||||
});
|
||||
|
||||
Package.registerBuildPlugin({
|
||||
name: "compile-arson",
|
||||
use: ["ecmascript"],
|
||||
sources: ["plugin.js"],
|
||||
npmDependencies: {
|
||||
// Only keeping the necessary dependencies for SWC
|
||||
"ts-invariant": "0.4.1"
|
||||
}
|
||||
});
|
||||
|
||||
Npm.depends({
|
||||
arson: "0.2.3",
|
||||
"vue-template-compiler": "2.5.16"
|
||||
});
|
||||
|
||||
Package.onUse(function(api) {
|
||||
api.use("ecmascript");
|
||||
api.use("isobuild:compiler-plugin@1.0.0")
|
||||
api.mainModule("modules-test-plugin.js");
|
||||
});
|
||||
@@ -0,0 +1,57 @@
|
||||
import { invariant } from "ts-invariant";
|
||||
|
||||
invariant(
|
||||
typeof process.versions.node === "string",
|
||||
"Meteor plugins should only run in Node.js",
|
||||
);
|
||||
|
||||
invariant(
|
||||
require.resolve("ts-invariant"),
|
||||
"/node_modules/meteor/meteor-test-plugin/node_modules/ts-invariant/lib/invariant.js",
|
||||
);
|
||||
|
||||
Plugin.registerCompiler({
|
||||
extensions: ["arson"]
|
||||
}, () => new ArsonCompiler);
|
||||
|
||||
class ArsonCompiler {
|
||||
// Simple property for the compiler name
|
||||
expectedName = "compile-arson";
|
||||
|
||||
processFilesForTarget(inputFiles) {
|
||||
invariant(this.expectedName === "compile-arson", this.expectedName);
|
||||
invariant(inputFiles.length > 0);
|
||||
|
||||
let vueCheckCount = 0;
|
||||
|
||||
inputFiles.forEach(file => {
|
||||
const arson = file.require("arson");
|
||||
let encoded = file.getContentsAsString();
|
||||
const decoded = arson.decode(encoded);
|
||||
decoded.self = decoded;
|
||||
encoded = arson.encode(decoded);
|
||||
|
||||
file.addJavaScript({
|
||||
path: file.getPathInPackage() + ".js",
|
||||
data: [
|
||||
'module.exportDefault(require("arson").decode(',
|
||||
" " + JSON.stringify(encoded),
|
||||
"));",
|
||||
""
|
||||
].join("\n"),
|
||||
hash: file.getSourceHash()
|
||||
});
|
||||
|
||||
if (file.getPackageName() === "modules-test-plugin") {
|
||||
const vueCompilerId = file.resolve("vue-template-compiler");
|
||||
// Make sure resolution does not use the "browser" field of
|
||||
// vue-template-compiler/package.json.
|
||||
const base = vueCompilerId.split("/").pop();
|
||||
invariant(base === "index.js", base);
|
||||
++vueCheckCount;
|
||||
}
|
||||
});
|
||||
|
||||
invariant(vueCheckCount > 0);
|
||||
}
|
||||
}
|
||||
@@ -382,6 +382,114 @@ selftest.define("compiler plugin caching - local plugin", async function () {
|
||||
await run.stop();
|
||||
});
|
||||
|
||||
// Tests that SwcCompiler properly applies SWC compilation on JS files
|
||||
selftest.define("compiler plugin caching - local plugin with SwcCompiler", async function () {
|
||||
var s = new Sandbox({ fakeMongo: true });
|
||||
await s.init();
|
||||
|
||||
process.env.METEOR_DISABLE_COLORS = true;
|
||||
|
||||
// Create a new app based on local-compiler-plugin
|
||||
await s.createApp("myapp", "local-compiler-plugin");
|
||||
s.cd("myapp");
|
||||
|
||||
// Create a JavaScript file to test SWC compilation
|
||||
s.write("test.js", "const message = 'Hello from SWC'; console.log(message);");
|
||||
|
||||
// Modify the local plugin to use SwcCompiler for JS files
|
||||
s.write('packages/local-plugin/plugin.js', `
|
||||
var fs = Plugin.fs;
|
||||
var path = Plugin.path;
|
||||
|
||||
// Import SwcCompiler from babel-compiler package
|
||||
var SwcCompiler = Package['babel-compiler'].SwcCompiler;
|
||||
|
||||
// Register compiler for .js files using SwcCompiler
|
||||
Plugin.registerCompiler({
|
||||
extensions: ['js'],
|
||||
archMatching: 'os'
|
||||
}, function () {
|
||||
return new SwcJsCompiler();
|
||||
});
|
||||
|
||||
// SwcCompiler for JS files
|
||||
var SwcJsCompiler = function () {
|
||||
var self = this;
|
||||
self.runCount = 0;
|
||||
self.diskCache = null;
|
||||
|
||||
// Create an instance of the SwcCompiler with swc: true
|
||||
self.compiler = new SwcCompiler({ verbose: true });
|
||||
};
|
||||
SwcJsCompiler.prototype.processFilesForTarget = function (inputFiles) {
|
||||
var self = this;
|
||||
|
||||
// Use the SwcCompiler to process the files
|
||||
self.compiler.processFilesForTarget(inputFiles);
|
||||
|
||||
console.log("SwcJsCompiler invocation", ++self.runCount);
|
||||
if (self.diskCache) {
|
||||
fs.writeFileSync(self.diskCache, self.runCount + '\\n');
|
||||
}
|
||||
};
|
||||
SwcJsCompiler.prototype.setDiskCacheDirectory = function (diskCacheDir) {
|
||||
var self = this;
|
||||
self.diskCache = path.join(diskCacheDir, 'swc-cache');
|
||||
|
||||
// Pass the disk cache directory to the SwcCompiler
|
||||
if (self.compiler && self.compiler.setDiskCacheDirectory) {
|
||||
self.compiler.setDiskCacheDirectory(diskCacheDir);
|
||||
}
|
||||
|
||||
try {
|
||||
var data = fs.readFileSync(self.diskCache, 'utf8');
|
||||
} catch (e) {
|
||||
if (e.code !== 'ENOENT')
|
||||
throw e;
|
||||
return;
|
||||
}
|
||||
self.runCount = parseInt(data, 10);
|
||||
};
|
||||
`);
|
||||
|
||||
// Update package.js to use babel-compiler
|
||||
s.write('packages/local-plugin/package.js', `
|
||||
Package.registerBuildPlugin({
|
||||
name: "compileWithSwc",
|
||||
sources: ['plugin.js'],
|
||||
use: ['babel-compiler']
|
||||
});
|
||||
|
||||
Package.onUse(function (api) {
|
||||
api.use('isobuild:compiler-plugin@1.0.0');
|
||||
api.use('babel-compiler');
|
||||
});
|
||||
`);
|
||||
|
||||
var run = await startRun(s);
|
||||
|
||||
// The SwcJsCompiler gets used
|
||||
await run.match("SwcJsCompiler invocation 1", false, true);
|
||||
|
||||
// Verify that SWC compilation is being applied
|
||||
// This is indicated by the SWC verbose log message from babel-compiler.js
|
||||
await run.match(/\[Transpiler] Used SWC.*\(app\)/, false, true);
|
||||
|
||||
// Modify the JS file to test recompilation
|
||||
s.write("test.js", "const message = 'Updated SWC message'; console.log(message);");
|
||||
// SwcJsCompiler gets reused
|
||||
await run.match("SwcJsCompiler invocation 2", false, true);
|
||||
|
||||
// Restart meteor to test disk cache
|
||||
await run.stop();
|
||||
run = await startRun(s);
|
||||
|
||||
// Disk cache gets us up to 3 for SwcJsCompiler
|
||||
await run.match("SwcJsCompiler invocation 3", false, true);
|
||||
|
||||
await run.stop();
|
||||
});
|
||||
|
||||
// Test error on duplicate compiler plugins.
|
||||
selftest.define("compiler plugins - duplicate extension", async () => {
|
||||
const s = new Sandbox({ fakeMongo: true });
|
||||
|
||||
@@ -59,7 +59,7 @@ selftest.define("modern build stack", async function () {
|
||||
await s.cd("modern");
|
||||
|
||||
s.set("METEOR_PROFILE", "0");
|
||||
|
||||
|
||||
await writeModernConfig(s, true);
|
||||
|
||||
const run = s.run();
|
||||
@@ -204,6 +204,11 @@ selftest.define("modern build stack - transpiler boolean-like options", async fu
|
||||
run.waitSecs(waitToStart);
|
||||
await run.match("App running at");
|
||||
|
||||
/* check verbose logs */
|
||||
await run.match(/SWC 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);
|
||||
@@ -252,6 +257,11 @@ selftest.define("modern build stack - transpiler string-like options", async fun
|
||||
run.waitSecs(waitToStart);
|
||||
await run.match("App running at");
|
||||
|
||||
/* check verbose logs */
|
||||
await run.match(/SWC 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);
|
||||
@@ -297,6 +307,12 @@ async function writeSwcrcConfig(s, config) {
|
||||
s.write(".swcrc", JSON.stringify(json, null, 2) + "\n");
|
||||
}
|
||||
|
||||
async function writeSwcConfigJs(s, config) {
|
||||
// Create a JavaScript file that exports the configuration
|
||||
const jsContent = `module.exports = ${JSON.stringify(config, null, 2)};`;
|
||||
s.write("swc.config.js", jsContent);
|
||||
}
|
||||
|
||||
selftest.define("modern build stack - transpiler custom .swcrc", async function () {
|
||||
const currentMeteorModern = process.env.METEOR_MODERN;
|
||||
process.env.METEOR_MODERN = '';
|
||||
@@ -328,6 +344,50 @@ selftest.define("modern build stack - transpiler custom .swcrc", async function
|
||||
process.env.METEOR_MODERN = currentMeteorModern;
|
||||
});
|
||||
|
||||
selftest.define("modern build stack - transpiler custom swc.config.js", async function () {
|
||||
const currentMeteorModern = process.env.METEOR_MODERN;
|
||||
process.env.METEOR_MODERN = '';
|
||||
|
||||
const s = new Sandbox();
|
||||
await s.init();
|
||||
|
||||
await s.createApp("modern", "modern");
|
||||
await s.cd("modern");
|
||||
|
||||
// Remove the .swcrc file to ensure we're using swc.config.js
|
||||
s.unlink(".swcrc");
|
||||
|
||||
// Write the swc.config.js file with the same configuration
|
||||
await writeSwcConfigJs(s, {
|
||||
jsc: {
|
||||
baseUrl: "./",
|
||||
paths: {
|
||||
"@swcAlias/*": ["swcAlias/*"]
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
await writeConfig(s, {
|
||||
modern: true,
|
||||
mainModule: {
|
||||
client: 'client/main.js',
|
||||
server: 'server/alias.js',
|
||||
},
|
||||
});
|
||||
|
||||
const run = s.run();
|
||||
|
||||
run.waitSecs(waitToStart);
|
||||
await run.match("App running at");
|
||||
|
||||
/* custom swc.config.js and alias resolution */
|
||||
await run.match(/alias resolved/, false, true);
|
||||
|
||||
await run.stop();
|
||||
|
||||
process.env.METEOR_MODERN = currentMeteorModern;
|
||||
});
|
||||
|
||||
selftest.define("modern build stack - transpiler files", async function () {
|
||||
process.env.METEOR_MODERN = 'true';
|
||||
const s = new Sandbox();
|
||||
@@ -442,7 +502,7 @@ selftest.define("modern build stack - test swc minifier", async function () {
|
||||
|
||||
await s.createApp(appName, "modern");
|
||||
await s.cd(appName);
|
||||
|
||||
|
||||
await writeConfig(s, {
|
||||
modern: true,
|
||||
mainModule: {
|
||||
@@ -452,7 +512,7 @@ selftest.define("modern build stack - test swc minifier", async function () {
|
||||
});
|
||||
|
||||
s.set("NODE_INSPECTOR_IPC", "1");
|
||||
|
||||
|
||||
await writeModernConfig(s, {
|
||||
minifier: true
|
||||
});
|
||||
|
||||
@@ -34,6 +34,16 @@ By default, `"modern": true` enables all build stack upgrades. To opt out of web
|
||||
}
|
||||
```
|
||||
|
||||
This setting doesn’t affect production; legacy builds are still included by default on Meteor apps.
|
||||
|
||||
To exclude legacy builds in production, add "modern" to the `.meteor/platforms` file.
|
||||
|
||||
```sh
|
||||
server
|
||||
browser
|
||||
modern
|
||||
```
|
||||
|
||||
## Minifier
|
||||
|
||||
:::info
|
||||
|
||||
@@ -92,11 +92,11 @@ Or exclude only specific files like `.jsx`:
|
||||
}
|
||||
```
|
||||
|
||||
You can also use `excludePackages`, `excludeNodeModules`, and `excludeLegacy` for finer control. See the [`modernTranspiler` config docs](#config-api) for more.
|
||||
You can also use `excludePackages`, `excludeNodeModules`, and `excludeLegacy` for finer control. See the [`modern.transpiler` config docs](#config-api) for more.
|
||||
|
||||
When no plugin exists, these settings let you still get most of SWC’s speed benefits by limiting fallback use.
|
||||
|
||||
Most apps will benefit just by enabling `modernTranspiler: true`. Most Meteor packages should work right away, except ones using nested imports. Node modules will mostly work too, since they follow common standards. Most app code should also work unless it depends on Babel-specific behavior.
|
||||
Most apps will benefit just by enabling `modern: true`. Most Meteor packages should work right away, except ones using nested imports. Node modules will mostly work too, since they follow common standards. Most app code should also work unless it depends on Babel-specific behavior.
|
||||
|
||||
> Remember to turn off verbosity when you're done with optimizations.
|
||||
|
||||
@@ -110,7 +110,7 @@ You can also configure other options using the `.swcrc` format. One common case
|
||||
{
|
||||
"jsc": {
|
||||
"parser": {
|
||||
"syntax": "emcascript",
|
||||
"syntax": "ecmascript",
|
||||
"jsx": true
|
||||
}
|
||||
}
|
||||
@@ -121,6 +121,10 @@ You can also configure other options using the `.swcrc` format. One common case
|
||||
|
||||
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.
|
||||
|
||||
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).
|
||||
|
||||
## Config API
|
||||
|
||||
- `modern.transpiler: [true|false]` - Default: `true`
|
||||
@@ -192,6 +196,62 @@ To use the same aliases in SWC, add them to your [.swcrc](#custom-swcrc):
|
||||
|
||||
This enables you to use `@ui/components` instead of `./ui/components` in your imports.
|
||||
|
||||
You can use `swc.config.js` to define different aliases based on an environment variable.
|
||||
|
||||
``` js
|
||||
var mode = process.env.MODE_ENV;
|
||||
|
||||
var userAliases = {
|
||||
"@ui/*": ["user/*"],
|
||||
};
|
||||
|
||||
var adminAliases = {
|
||||
"@ui/*": ["admin/*"],
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
jsc: {
|
||||
baseUrl: "./",
|
||||
paths: mode === "USER" ? userAliases : adminAliases,
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
:::warning
|
||||
SWC only resolves aliases to imports, not `require` calls.
|
||||
:::
|
||||
|
||||
- Imports
|
||||
|
||||
Binding imports inject a module to use.
|
||||
|
||||
``` javascript
|
||||
// Binding imports
|
||||
import Button from "@ui/button";
|
||||
import { Button } from "@ui/button";
|
||||
```
|
||||
|
||||
Side-effect imports run the module’s code.
|
||||
|
||||
``` javascript
|
||||
// Side effect import
|
||||
import "@ui/button";
|
||||
```
|
||||
|
||||
- Require calls
|
||||
|
||||
Can import values or run the module’s code.
|
||||
|
||||
``` javascript
|
||||
const { Button } = require("@ui/button");
|
||||
|
||||
require("@ui/button");
|
||||
```
|
||||
|
||||
SWC resolve aliases for imports correctly, but require calls won’t. For require calls, use an import or a relative path.
|
||||
|
||||
SWC has no [module-resolver plugin like Babel’s](https://www.npmjs.com/package/babel-plugin-module-resolver) yet, which could affect require calls in the future.
|
||||
|
||||
### 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.
|
||||
@@ -216,6 +276,6 @@ If you run into issues, try `meteor reset` or delete the `.meteor/local` folder
|
||||
|
||||
For help or to report issues, post on [GitHub](https://github.com/meteor/meteor/issues) or the [Meteor forums](https://forums.meteor.com). We’re focused on making Meteor faster and your feedback helps.
|
||||
|
||||
You can compare performance before and after enabling `modernTranspiler` by running [`meteor profile`](../../cli/index.md#meteorprofile). Share your results to show progress to others.
|
||||
You can compare performance before and after enabling `modern` by running [`meteor profile`](../../cli/index.md#meteorprofile). Share your results to show progress to others.
|
||||
|
||||
> **[Check out modern bundler options](bundler.md) to improve performance and access newer build features.**
|
||||
|
||||
@@ -4,7 +4,7 @@ Describes the high-level features and actions for the Meteor project in the near
|
||||
|
||||
## Introduction
|
||||
|
||||
**Last updated: March 31th, 2025.**
|
||||
**Last updated: June 16, 2025.**
|
||||
|
||||
The description of many items includes sentences and ideas from Meteor community members.
|
||||
|
||||
@@ -34,26 +34,42 @@ Contributors are encouraged to focus their efforts on work that aligns with the
|
||||
**Goal:** Add a command([meteor profile](/cli/#meteorprofile)) to measure if our changes are actually making our builds faster and smaller.
|
||||
|
||||
|
||||
#### Phase 2: External Transpiler Integration & Bundler Improvements
|
||||
#### Phase 2: External Transpiler Integration
|
||||
|
||||
**Target Release:** 3.3 ⏳
|
||||
**Target Release:** 3.3 ✅
|
||||
|
||||
**Goal:** For this phase we want:
|
||||
- Improve our current bundler performance, via optimizations so that any meteor user can get benefits from it; And an external bundler could get
|
||||
the same benefits.
|
||||
- To have an external transpiler working with Meteor and producing a bundle that is smaller or faster than the current Meteor bundle.
|
||||
|
||||
|
||||
#### Phase 3: HMR Improvements
|
||||
|
||||
**Target Release:** 3.3.x ⏳
|
||||
**Target Release:** 3.3 ✅
|
||||
|
||||
**Goal:** Improve the HMR performance, so that it is faster and more reliable on what needs to be changed.
|
||||
|
||||
#### Phase 4: Build Process Optimization
|
||||
#### Phase 4: Bundler Improvements & feedback
|
||||
|
||||
**Target Release:** 3.3.x ⏳
|
||||
|
||||
**Goal:** Improve the build size and make meteor use less resources for building, decreasing even more build and rebuild time.
|
||||
- Expanding compatibility and updates based on the feedback from the community, so that we can have a better experience with our new build tools, in this case SWC
|
||||
|
||||
#### Phase 5: External Bundler integration
|
||||
|
||||
**Target Release:** 3.4 ⏳
|
||||
|
||||
**Goal:** And an external bundler (like RSPack, ESBuild, or Rollup) working with Meteor and producing a bundle that is smaller or faster than the current Meteor bundle.
|
||||
- This will also allow Meteor to have features like tree-shaking, code-splitting, and other optimizations that will make our apps leaner and faster.
|
||||
|
||||
#### Phase 6: Build Process Optimization
|
||||
|
||||
**Target Release:** 3.4.x ⏳
|
||||
|
||||
**Goal:** Improve the build size and make meteor use less resources for building, decreasing even more build and rebuild time.
|
||||
- Expanding compatibility and updates based on the feedback from the community, so that we can have a better experience with our new build tools
|
||||
|
||||
|
||||
#### Documentation Strategy
|
||||
|
||||
@@ -939,219 +939,784 @@ For now, you cannot run this while a development server is running. Quit all run
|
||||
:::
|
||||
|
||||
|
||||
## meteor build {meteorbuild}
|
||||
## meteor build {#meteorbuild}
|
||||
|
||||
Package this project up for deployment. The output is a directory with several
|
||||
build artifacts:
|
||||
Package your project for deployment.
|
||||
|
||||
<ul><li>a tarball (.tar.gz) that includes everything necessary to run the application
|
||||
server (see the <code>README</code> in the tarball for details). Using the
|
||||
`--directory` option will produce a `bundle` directory instead of the tarball.</li>
|
||||
<li>an unsigned <code>apk</code> bundle and a project source if Android is targeted as a
|
||||
mobile platform</li>
|
||||
<li>a directory with an Xcode project source if iOS is targeted as a mobile
|
||||
platform</li></ul>
|
||||
|
||||
You can use the application server bundle to host a Meteor application on your
|
||||
own server, instead of deploying to Galaxy. You will have to deal
|
||||
with logging, monitoring, backups, load-balancing, etc, all of which we handle
|
||||
for you if you use Galaxy.
|
||||
|
||||
The unsigned `apk` bundle and the outputted Xcode project can be used to deploy
|
||||
your mobile apps to Android Play Store and Apple App Store.
|
||||
|
||||
By default, your application is bundled for your current architecture.
|
||||
This may cause difficulties if your app contains binary code due to,
|
||||
for example, npm packages. You can try to override that behavior
|
||||
with the `--architecture` flag.
|
||||
|
||||
You can set optional data for the initial value of `Meteor.settings`
|
||||
in your mobile application with the `--mobile-settings` flag. A new value for
|
||||
`Meteor.settings` can be set later by the server as part of hot code push.
|
||||
|
||||
You can also specify which platforms you want to build with the `--platforms` flag.
|
||||
Examples: `--platforms=android`, `--platforms=ios`, `--platforms=web.browser`.
|
||||
|
||||
## meteor lint {meteorlint}
|
||||
|
||||
Run through the whole build process for the app and run all linters the app
|
||||
uses. Outputs all build errors or linting warnings to the standard output.
|
||||
|
||||
|
||||
## meteor search {meteorsearch}
|
||||
|
||||
Searches for Meteor packages and releases, whose names contain the specified
|
||||
regular expression.
|
||||
|
||||
|
||||
## meteor show {meteorshow}
|
||||
|
||||
Shows more information about a specific package or release: name, summary, the
|
||||
usernames of its maintainers, and, if specified, its homepage and git URL.
|
||||
|
||||
Get information on meteor recommended releases:
|
||||
```bash
|
||||
meteor build <output-path> [options]
|
||||
```
|
||||
|
||||
### Output Artifacts
|
||||
|
||||
The command produces deployment-ready artifacts for all platforms in your project:
|
||||
|
||||
- **Server Bundle**: A tarball containing everything needed to run the application server
|
||||
- **Android Package**: AAB/APK bundle and Android project source (if Android platform is added)
|
||||
- **iOS Package**: Xcode project source (if iOS platform is added)
|
||||
|
||||
::: tip Self-Hosting
|
||||
You can use the server bundle to host a Meteor application on your own infrastructure instead of Galaxy. Note that you'll need to handle logging, monitoring, backups, and load-balancing yourself.
|
||||
:::
|
||||
|
||||
### Options
|
||||
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| `--debug` | Build in debug mode (don't minify, preserve source maps) |
|
||||
| `--directory` | Output a directory instead of a tarball (existing output location will be deleted first) |
|
||||
| `--server-only` | Skip building mobile apps but still build the 'web.cordova' client target for hot code push |
|
||||
| `--mobile-settings <file>` | Set the initial value of `Meteor.settings` in mobile apps |
|
||||
| `--server <url>` | Location where mobile builds connect to the Meteor server (defaults to localhost:3000) |
|
||||
| `--architecture <arch>` | Build for a different architecture than your development machine |
|
||||
| `--allow-incompatible-update` | Allow packages to be upgraded/downgraded to potentially incompatible versions |
|
||||
| `--platforms <platforms>` | Build only for specified platforms (when available) |
|
||||
| `--packageType <type>` | Choose between `apk` or `bundle` for Android builds (defaults to `bundle`) |
|
||||
|
||||
::: details Available Architectures
|
||||
Valid architectures include:
|
||||
- `os.osx.x86_64`
|
||||
- `os.linux.x86_64`
|
||||
- `os.linux.x86_32`
|
||||
- `os.windows.x86_32`
|
||||
- `os.windows.x86_64`
|
||||
|
||||
This option selects the architecture of binary-dependent Atmosphere packages. If your project doesn't use Atmosphere packages with binary dependencies, `--architecture` has no effect.
|
||||
:::
|
||||
|
||||
### Examples
|
||||
|
||||
```bash
|
||||
# Basic build
|
||||
meteor build ../build
|
||||
|
||||
# Output a directory instead of a tarball
|
||||
meteor build ../build --directory
|
||||
|
||||
# Debug build (unminified)
|
||||
meteor build ../build --debug
|
||||
|
||||
# Build only the server (skip mobile apps)
|
||||
meteor build ../build --server-only
|
||||
|
||||
# Build for specific platforms
|
||||
meteor build ../build --platforms=android,ios
|
||||
|
||||
# Set server location for mobile apps
|
||||
meteor build ../build --server=https://example.com:443
|
||||
|
||||
# Build for a different architecture
|
||||
meteor build ../build --architecture=os.linux.x86_64
|
||||
|
||||
# Specify Android package type
|
||||
meteor build ../build --packageType=apk
|
||||
```
|
||||
|
||||
## meteor lint {#meteorlint}
|
||||
|
||||
Run linters on your Meteor application code.
|
||||
|
||||
```bash
|
||||
meteor lint [options]
|
||||
```
|
||||
|
||||
### Description
|
||||
|
||||
This command:
|
||||
- Performs a complete build of your application
|
||||
- Runs all configured linters
|
||||
- Outputs build errors and linting warnings to standard output
|
||||
|
||||
::: tip CI Integration
|
||||
The `meteor lint` command is particularly useful for continuous integration environments to catch code quality issues before deployment.
|
||||
:::
|
||||
|
||||
### Options
|
||||
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| `--allow-incompatible-update` | Allow packages to be upgraded or downgraded to potentially incompatible versions if required to satisfy all package version constraints |
|
||||
|
||||
### Example Usage
|
||||
|
||||
```bash
|
||||
# Basic usage
|
||||
meteor lint
|
||||
|
||||
# Allow incompatible package updates during linting
|
||||
meteor lint --allow-incompatible-update
|
||||
```
|
||||
|
||||
::: warning
|
||||
Linting errors will prevent your application from being built successfully. Fixing these errors is required for deployment.
|
||||
:::
|
||||
|
||||
|
||||
## meteor search {#meteorsearch}
|
||||
|
||||
Search for Meteor packages and releases.
|
||||
|
||||
```bash
|
||||
meteor search <regex> [options]
|
||||
```
|
||||
|
||||
### Description
|
||||
|
||||
Searches through the Meteor package and release database for items whose names match the specified regular expression.
|
||||
|
||||
::: info Default Behavior
|
||||
By default, the search will not show:
|
||||
- Packages without official versions (e.g., those with only prereleases)
|
||||
- Packages known to be incompatible with Meteor 0.9.0 and later due to migration issues
|
||||
:::
|
||||
|
||||
### Options
|
||||
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| `--maintainer <username>` | Filter results by authorized maintainer |
|
||||
| `--show-all` | Show all matches, including prereleases and incompatible packages |
|
||||
| `--ejson` | Display more detailed output in EJSON format |
|
||||
|
||||
### Examples
|
||||
|
||||
```bash
|
||||
# Search for all packages related to "auth"
|
||||
meteor search auth
|
||||
|
||||
# Search for packages maintained by a specific user
|
||||
meteor search mongo --maintainer meteor
|
||||
|
||||
# Show all matching packages, including prereleases
|
||||
meteor search bootstrap --show-all
|
||||
|
||||
# Get detailed output in EJSON format
|
||||
meteor search react --ejson
|
||||
```
|
||||
|
||||
::: tip Advanced Searching
|
||||
You can use regular expressions for more powerful searches:
|
||||
```bash
|
||||
# Packages that start with "react-"
|
||||
meteor search "^react-"
|
||||
|
||||
# Packages that end with "router"
|
||||
meteor search "router$"
|
||||
```
|
||||
:::
|
||||
|
||||
|
||||
## meteor show {#meteorshow}
|
||||
|
||||
Display detailed information about packages and releases.
|
||||
|
||||
```bash
|
||||
meteor show <name> [options]
|
||||
meteor show <name@version> [options]
|
||||
meteor show [options]
|
||||
```
|
||||
|
||||
### Description
|
||||
|
||||
Shows detailed information about a specific package or release, including:
|
||||
- Name and summary
|
||||
- Available versions
|
||||
- Maintainers
|
||||
- Homepage and git URL (if specified)
|
||||
- Exports and other package metadata
|
||||
|
||||
::: tip
|
||||
This works on both local packages built from source and remote packages stored on the server.
|
||||
:::
|
||||
|
||||
### Common Usage
|
||||
|
||||
#### View Package Information
|
||||
|
||||
```bash
|
||||
# Show information about a package
|
||||
meteor show jam:easy-schema
|
||||
|
||||
# Show information about a specific version
|
||||
meteor show jam:easy-schema@1.7.0
|
||||
|
||||
# Show information about the local version
|
||||
meteor show jam:easy-schema@local
|
||||
```
|
||||
|
||||
#### View Meteor Releases
|
||||
|
||||
```bash
|
||||
# Show recommended Meteor releases
|
||||
meteor show METEOR
|
||||
|
||||
# Show all Meteor releases (including intermediate ones)
|
||||
meteor show METEOR --show-all
|
||||
```
|
||||
|
||||
Get information on all meteor releases (including intermediate releases)"
|
||||
### Options
|
||||
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| `--show-all` | Show hidden versions, experimental releases, and incompatible packages |
|
||||
| `--ejson` | Display more detailed output in EJSON format |
|
||||
|
||||
### Examples
|
||||
|
||||
```bash
|
||||
meteor show --show-all METEOR
|
||||
# Running from a package directory shows info for that package
|
||||
cd ~/my-package
|
||||
meteor show
|
||||
|
||||
# View detailed EJSON output
|
||||
meteor show react-meteor-data --ejson
|
||||
```
|
||||
|
||||
::: info Default Behavior
|
||||
By default, Meteor:
|
||||
- Shows no more than five versions
|
||||
- Hides experimental release versions
|
||||
- Hides packages incompatible with Meteor 0.9.0 and later
|
||||
:::
|
||||
|
||||
::: details Version Selection
|
||||
For version-specific information (like exports), Meteor will use:
|
||||
1. The local version, if available
|
||||
2. The latest official version, if no local version exists
|
||||
:::
|
||||
|
||||
|
||||
## meteor publish {#meteorpublish}
|
||||
|
||||
Publish a package to Atmosphere (Meteor package server).
|
||||
|
||||
```bash
|
||||
meteor publish [options]
|
||||
meteor publish --update
|
||||
```
|
||||
|
||||
### Description
|
||||
|
||||
Publishes a new version of a local package to Atmosphere. Must be run from the package directory.
|
||||
|
||||
::: warning Package Naming Convention
|
||||
Published package names must begin with the maintainer's Meteor Developer Account username and a colon, like `username:package-name`.
|
||||
:::
|
||||
|
||||
### Common Operations
|
||||
|
||||
#### Publish a New Package
|
||||
|
||||
```bash
|
||||
cd my-package
|
||||
meteor publish --create
|
||||
```
|
||||
|
||||
#### Update an Existing Package
|
||||
|
||||
```bash
|
||||
cd my-package
|
||||
meteor publish
|
||||
```
|
||||
|
||||
#### Update Package Metadata
|
||||
|
||||
Update README, description, or other metadata without changing the code:
|
||||
|
||||
```bash
|
||||
cd my-package
|
||||
meteor publish --update
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| `--create` | Publish a new package for the first time |
|
||||
| `--update` | Update metadata of a previously published version (README, git URL, description, etc.) |
|
||||
| `--allow-incompatible-update` | Allow dependencies to be upgraded/downgraded to potentially incompatible versions |
|
||||
| `--no-lint` | Skip linting the package and its local dependencies before publishing |
|
||||
|
||||
### Architecture-Specific Packages
|
||||
|
||||
For packages with binary components:
|
||||
- Regular `publish` will only upload the build for your current architecture
|
||||
- Use `meteor publish-for-arch` from a different machine to upload builds for other architectures
|
||||
|
||||
::: details Package Publication Process
|
||||
When you publish a package:
|
||||
1. Meteor reads version information from `package.js`
|
||||
2. Builds the package
|
||||
3. Sends both source code and built version to the package server
|
||||
4. Marks you as the sole maintainer (use `meteor admin maintainers` to modify)
|
||||
:::
|
||||
|
||||
### Examples
|
||||
|
||||
```bash
|
||||
# Publish a new package
|
||||
meteor publish --create
|
||||
|
||||
# Update an existing package
|
||||
meteor publish
|
||||
|
||||
# Update metadata only
|
||||
meteor publish --update
|
||||
|
||||
# Publish without linting
|
||||
meteor publish --no-lint
|
||||
```
|
||||
|
||||
::: tip
|
||||
Use `meteor show` to preview how your package information will appear in the package server.
|
||||
:::
|
||||
|
||||
## meteor publish-for-arch {#meteorpublishforarch}
|
||||
|
||||
Publish architecture-specific builds of a package.
|
||||
|
||||
```bash
|
||||
meteor publish-for-arch packageName@version
|
||||
```
|
||||
|
||||
### Description
|
||||
|
||||
Creates and publishes a build of an existing package version for a different architecture than the one initially published.
|
||||
|
||||
::: info Architecture Support
|
||||
Meteor currently supports the following architectures:
|
||||
- 32-bit Linux
|
||||
- 64-bit Linux (used by Galaxy servers)
|
||||
- 64-bit macOS
|
||||
:::
|
||||
|
||||
### Use Case
|
||||
|
||||
When a package contains platform-specific components (like npm modules with native code), running `meteor publish` only creates a build for your current architecture. To make your package usable on other architectures, you need to run `publish-for-arch` from machines with those architectures.
|
||||
|
||||
### How It Works
|
||||
|
||||
1. Run the command on a machine with the target architecture
|
||||
2. Meteor downloads your package's source and dependencies from the package server
|
||||
3. Builds the package for the current architecture
|
||||
4. Uploads the architecture-specific build to the package server
|
||||
|
||||
::: tip No Source Required
|
||||
You don't need to have a copy of your package's source code to run this command. Meteor automatically downloads everything needed from the package server.
|
||||
:::
|
||||
|
||||
### Example Workflow
|
||||
|
||||
Imagine you've published a package with binary components from a Mac:
|
||||
|
||||
```bash
|
||||
# On your Mac
|
||||
cd my-binary-package
|
||||
meteor publish --create
|
||||
```
|
||||
|
||||
To make it available for Linux users:
|
||||
|
||||
```bash
|
||||
# Later, on a 64-bit Linux machine
|
||||
meteor publish-for-arch username:my-binary-package@1.0.0
|
||||
```
|
||||
|
||||
|
||||
## meteor publish {meteorpublish}
|
||||
## meteor publish-release {#meteorpublishrelease}
|
||||
|
||||
Publishes your package. To publish, you must `cd` into the package directory, log
|
||||
in with your Meteor Developer Account and run `meteor publish`. By convention,
|
||||
published package names must begin with the maintainer's Meteor Developer
|
||||
Account username and a colon, like so: `iron:router`.
|
||||
Publish a new Meteor release.
|
||||
|
||||
To publish a package for the first time, use `meteor publish --create`.
|
||||
```bash
|
||||
meteor publish-release <path-to-json-config> [options]
|
||||
```
|
||||
|
||||
Sometimes packages may contain binary code specific to an architecture (for
|
||||
example, they may use an npm package). In that case, running publish will only
|
||||
upload the build to the architecture that you were using to publish it. You can
|
||||
use `publish-for-arch` to upload a build to a different architecture from a
|
||||
different machine.
|
||||
### Description
|
||||
|
||||
If you have already published a package but need to update it's metadata
|
||||
(the content of `Package.describe`) or the README you can actually achieve this
|
||||
via `meteor publish --update`.
|
||||
Publishes a new release of Meteor based on a JSON configuration file. This allows you to create custom Meteor releases or release tracks.
|
||||
|
||||
## meteor publish-for-arch {meteorpublishforarch}
|
||||
::: info Release Tracks
|
||||
Meteor releases are divided into tracks:
|
||||
- Only Meteor Software can publish to the default Meteor track
|
||||
- Anyone can create and publish to their own custom tracks
|
||||
- Users won't switch tracks when running `meteor update` unless specified
|
||||
:::
|
||||
|
||||
Publishes a build of an existing package version from a different architecture.
|
||||
### Configuration File Format
|
||||
|
||||
Some packages contain code specific to an architecture. Running `publish` by
|
||||
itself, will upload the build to the architecture that you were using to
|
||||
publish. You need to run `publish-for-arch` from a different architecture to
|
||||
upload a different build.
|
||||
The JSON configuration file must contain:
|
||||
|
||||
For example, let's say you published name:cool-binary-blob from a Mac. If you
|
||||
want people to be able to use cool-binary-blob from Linux, you should log into a
|
||||
Linux machine and then run
|
||||
`meteor publish-for-arch name:cool-binary-blob@version`. It will notice that you
|
||||
are on a linux machine, and that there is no Linux-compatible build for your package
|
||||
and publish one.
|
||||
```json
|
||||
{
|
||||
"track": "TRACK_NAME", // Release track (e.g., "METEOR")
|
||||
"version": "VERSION", // Version number (e.g., "2.8.0")
|
||||
"recommended": true|false, // Is this a recommended release?
|
||||
"description": "DESCRIPTION", // Brief description of the release
|
||||
"tool": "PACKAGE@VERSION", // The meteor tool package and version
|
||||
"packages": { // Specific package versions for this release
|
||||
"package1": "version",
|
||||
"package2": "version"
|
||||
},
|
||||
"patchFrom": ["VERSION1", "VERSION2"] // Optional: releases this patches
|
||||
}
|
||||
```
|
||||
|
||||
Currently, the supported architectures for Meteor are 32-bit Linux, 64-bit Linux
|
||||
and Mac OS. Galaxy's servers run 64-bit Linux.
|
||||
::: warning Prerequisites
|
||||
You must publish all package versions to the package server before you can specify them in a release.
|
||||
:::
|
||||
|
||||
### Options
|
||||
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| `--create-track` | Create and publish a new release track |
|
||||
|
||||
### Recommended Flag
|
||||
|
||||
- Set `recommended: true` for stable releases (e.g., METEOR@3.2.2)
|
||||
- Set `recommended: false` for release candidates, experimental releases, etc.
|
||||
|
||||
### Patch Releases
|
||||
|
||||
Use the `patchFrom` field to specify a patch release:
|
||||
- Lists releases this new release patches
|
||||
- Automatically unrecommends the releases specified in `patchFrom`
|
||||
|
||||
### Examples
|
||||
|
||||
#### Publishing a New Release Track
|
||||
|
||||
```bash
|
||||
meteor publish-release my-release-config.json --create-track
|
||||
```
|
||||
|
||||
#### Publishing a New Release
|
||||
|
||||
```bash
|
||||
meteor publish-release meteor-3.3.0.json
|
||||
```
|
||||
|
||||
#### Sample Configuration File
|
||||
|
||||
```json
|
||||
{
|
||||
"track": "MYCORP",
|
||||
"version": "1.0.0",
|
||||
"recommended": true,
|
||||
"description": "MyCompany's custom Meteor release",
|
||||
"tool": "meteor-tool@2.8.0",
|
||||
"packages": {
|
||||
"accounts-base": "2.2.5",
|
||||
"mongo": "1.15.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
::: tip Custom Tool Forks
|
||||
This system allows forks of the meteor tool to be published as packages, letting users switch to custom tool implementations by changing to the corresponding release.
|
||||
:::
|
||||
|
||||
|
||||
## meteor publish-release {meteorpublishrelease}
|
||||
## meteor test-packages {#meteortestpackages}
|
||||
|
||||
Publishes a release of Meteor. Takes in a JSON configuration file.
|
||||
Run tests for Meteor packages.
|
||||
|
||||
Meteor releases are divided into tracks. While only MDG members can publish to
|
||||
the default Meteor track, anyone can create a track of their own and publish to
|
||||
it. Running `meteor update` without specifying the `--release` option will not
|
||||
cause the user to switch tracks.
|
||||
```bash
|
||||
meteor test-packages [options] [package...]
|
||||
```
|
||||
|
||||
To publish to a release track for the first time, use the `--create-track` flag.
|
||||
### Description
|
||||
|
||||
The JSON configuration file must contain the name of the release track
|
||||
(`track`), the release version (`version`), various metadata, the packages
|
||||
specified by the release as mapped to versions (`packages`), and the package &
|
||||
version of the Meteor command-line tool (`tool`). Note that this means that
|
||||
forks of the meteor tool can be published as packages and people can use them by
|
||||
switching to a corresponding release. For more information, run
|
||||
`meteor help publish-release`.
|
||||
Runs unit tests for one or more packages. Test results appear in a browser dashboard that updates whenever relevant source files are modified.
|
||||
|
||||
::: tip Package Specification
|
||||
Packages can be specified by:
|
||||
- **Name**: Resolved using the standard package search algorithm
|
||||
- **Path**: Any argument containing a '/' is loaded from that directory path
|
||||
:::
|
||||
|
||||
## meteor test-packages {meteortestpackages}
|
||||
If no packages are specified, all available packages will be tested.
|
||||
|
||||
Test Meteor packages, either by name, or by directory. Not specifying an
|
||||
argument will run tests for all local packages. The results are displayed in an
|
||||
app that runs at `localhost:3000` by default. If you need to, you can pass the
|
||||
`--settings` and `--port` arguments.
|
||||
### Options
|
||||
|
||||
If you want to filter the tests by name, you can use `--filter` or `-f`
|
||||
followed by the name of the test you want to run, it supports regex's.
|
||||
| Option | Description |
|
||||
|-------------------------------|-----------------------------------------------------------------|
|
||||
| `--port`, `-p <port>` | Port to listen on (default: 3000). Also uses ports N+1 and N+2 |
|
||||
| `--open`, `-o` | Opens a browser window when the app starts |
|
||||
| `--inspect[-brk][=<port>]` | Enable server-side debugging (default port: 9229) |
|
||||
| `--settings`, `-s <file>` | Set optional data for Meteor.settings on the server |
|
||||
| `--production` | Simulate production mode (minify and bundle CSS, JS files) |
|
||||
| `--driver-package <package>` | Test driver package to use (e.g., `meteortesting:mocha`) |
|
||||
| `--filter`, `-f` | Filter the tests by name |
|
||||
| `--verbose` | Print all output from build logs |
|
||||
| `--no-lint` | Skip running linters on every test app rebuild |
|
||||
| `--extra-packages <packages>` | Run with additional packages (comma separated) |
|
||||
| `--test-app-path <path>` | Set directory for temporary test app (default: system temp dir) |
|
||||
|
||||
```sh
|
||||
#### Mobile Testing Options
|
||||
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| `--ios`, `--android` | Run tests in an emulator |
|
||||
| `--ios-device`, `--android-device` | Run tests on a connected device |
|
||||
| `--mobile-server <url>` | Server location for mobile builds (default: local IP and port) |
|
||||
| `--cordova-server-port <port>` | Local port where Cordova will serve content |
|
||||
|
||||
### Examples
|
||||
|
||||
#### Test specific packages by name
|
||||
|
||||
```bash
|
||||
meteor test-packages accounts-base accounts-password
|
||||
```
|
||||
|
||||
#### Test a package by path
|
||||
|
||||
```bash
|
||||
meteor test-packages ./packages/my-package
|
||||
```
|
||||
|
||||
#### Test with custom settings
|
||||
|
||||
```bash
|
||||
meteor test-packages --settings settings.json
|
||||
```
|
||||
|
||||
#### Test with Mocha test driver
|
||||
|
||||
```bash
|
||||
meteor test-packages --driver-package meteortesting:mocha
|
||||
```
|
||||
|
||||
#### Test with filter
|
||||
|
||||
```bash
|
||||
meteor test-packages --filter myTestName
|
||||
```
|
||||
|
||||
this command will run only the tests that have `myTestName` in their name.
|
||||
|
||||
Alternatively, you can use the `TINYTEST_FILTER` environment variable to filter:
|
||||
|
||||
```sh
|
||||
```bash
|
||||
TINYTEST_FILTER=myTestName meteor test-packages
|
||||
```
|
||||
Has the same effect as the previous command.
|
||||
|
||||
## meteor admin {meteoradmin}
|
||||
#### Test on mobile device
|
||||
|
||||
Catch-all for miscellaneous commands that require authorization to use.
|
||||
```bash
|
||||
meteor test-packages --ios-device
|
||||
```
|
||||
|
||||
Some example uses of `meteor admin` include adding and removing package
|
||||
maintainers and setting a homepage for a package. It also includes various
|
||||
helpful functions for managing a Meteor release. Run `meteor help admin` for
|
||||
more information.
|
||||
## meteor admin {#meteoradmin}
|
||||
|
||||
## meteor shell {meteorshell}
|
||||
Administrative commands for official Meteor services.
|
||||
|
||||
When `meteor shell` is executed in an application directory where a server
|
||||
is already running, it connects to the server and starts an interactive
|
||||
shell for evaluating server-side code.
|
||||
```bash
|
||||
meteor admin <command> [args]
|
||||
```
|
||||
|
||||
Multiple shells can be attached to the same server. If no server is
|
||||
currently available, `meteor shell` will keep trying to connect until it
|
||||
succeeds.
|
||||
::: warning Authorization Required
|
||||
These commands require authorization to use.
|
||||
:::
|
||||
|
||||
Exiting the shell does not terminate the server. If the server restarts
|
||||
because a change was made in server code, or a fatal exception was
|
||||
encountered, the shell will restart along with the server. This behavior
|
||||
can be simulated by typing `.reload` in the shell.
|
||||
### Available Commands
|
||||
|
||||
The shell supports tab completion for global variables like `Meteor`,
|
||||
`Mongo`, and `Package`. Try typing `Meteor.is` and then pressing tab.
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `maintainers` | View or change package maintainers |
|
||||
| `recommend-release` | Recommend a previously published release |
|
||||
| `change-homepage` | Change the homepage URL of a package |
|
||||
| `list-organizations` | List the organizations of which you are a member |
|
||||
| `members` | View or change the members of an organization |
|
||||
| `get-machine` | Open an SSH shell to a machine in the Meteor build farm |
|
||||
|
||||
The shell maintains a persistent history across sessions. Previously-run
|
||||
commands can be accessed by pressing the up arrow.
|
||||
### Usage Examples
|
||||
|
||||
## meteor npm {meteornpm}
|
||||
```bash
|
||||
# View or change package maintainers
|
||||
meteor admin maintainers packagename [add/remove] [username]
|
||||
|
||||
The `meteor npm` command calls the
|
||||
[`npm`](https://docs.npmjs.com/getting-started/what-is-npm) version bundled
|
||||
with Meteor itself.
|
||||
# Change a package homepage
|
||||
meteor admin change-homepage packagename [url]
|
||||
|
||||
Additional parameters can be passed in the same way as the `npm` command
|
||||
(e.g. `meteor npm rebuild`, `meteor npm ls`, etc.) and the
|
||||
[npm documentation](https://docs.npmjs.com/) should be consulted for the
|
||||
full list of commands and for a better understanding of their usage.
|
||||
# List your organizations
|
||||
meteor admin list-organizations
|
||||
|
||||
For example, executing `meteor npm install lodash --save` would install `lodash`
|
||||
from npm to your `node_modules` directory and save its usage in your
|
||||
[`package.json`](https://docs.npmjs.com/files/package.json) file.
|
||||
# Manage organization members
|
||||
meteor admin members organization-name [add/remove] [username]
|
||||
```
|
||||
|
||||
Using the `meteor npm ...` commands in place of traditional `npm ...` commands
|
||||
is particularly important when using Node.js modules that have binary
|
||||
dependencies that make native C calls (like [`bcrypt`](https://www.npmjs.com/package/bcrypt))
|
||||
because doing so ensures that they are built using the same libraries.
|
||||
::: tip Detailed Help
|
||||
For more information on any admin command, run:
|
||||
```bash
|
||||
meteor help admin <command>
|
||||
```
|
||||
:::
|
||||
|
||||
Additionally, this access to the npm that comes with Meteor avoids the need to
|
||||
download and install npm separately.
|
||||
## meteor shell {#meteorshell}
|
||||
|
||||
## meteor node {meteornode}
|
||||
Start an interactive JavaScript shell for evaluating server-side code.
|
||||
|
||||
The `meteor node` command calls the
|
||||
[`node`](https://nodejs.org) version bundled with Meteor itself.
|
||||
```bash
|
||||
meteor shell
|
||||
```
|
||||
|
||||
> This is not to be confused with [`meteor shell`](#meteor-shell), which provides
|
||||
> an almost identical experience but also gives you access to the "server" context
|
||||
> of a Meteor application. Typically, `meteor shell` will be preferred.
|
||||
### Description
|
||||
|
||||
Additional parameters can be passed in the same way as the `node` command, and
|
||||
the [Node.js documentation](https://nodejs.org/dist/latest-v4.x/docs/api/cli.html)
|
||||
should be consulted for the full list of commands and for a better understanding
|
||||
of their usage.
|
||||
The `meteor shell` command connects to a running Meteor server and provides an interactive JavaScript REPL (Read-Eval-Print Loop) for executing server-side code.
|
||||
|
||||
For example, executing `meteor node` will enter the Node.js
|
||||
[Read-Eval-Print-Loop (REPL)](https://nodejs.org/dist/latest-v4.x/docs/api/repl.html)
|
||||
interface and allow you to interactively run JavaScript and see the results.
|
||||
::: tip Connection Behavior
|
||||
- Requires a running Meteor server in the application directory
|
||||
- If no server is available, it will keep trying to connect until successful
|
||||
- Multiple shells can be attached to the same server simultaneously
|
||||
:::
|
||||
|
||||
Executing `meteor node -e "console.log(process.versions)"` would
|
||||
run `console.log(process.versions)` in the version of `node` bundled with Meteor.
|
||||
### Features
|
||||
|
||||
#### Server Integration
|
||||
|
||||
- Exiting the shell does not terminate the server
|
||||
- If the server restarts (due to code changes or errors), the shell will automatically restart with it
|
||||
- You can manually trigger a reload by typing `.reload` in the shell
|
||||
|
||||
#### Developer Experience
|
||||
|
||||
| Feature | Description |
|
||||
|---------|-------------|
|
||||
| **Tab Completion** | Built-in tab completion for global variables like `Meteor`, `Mongo`, and `Package` |
|
||||
| **Persistent History** | Command history is maintained across sessions |
|
||||
| **Command Recall** | Access previously-run commands using the up arrow key |
|
||||
|
||||
### Example Usage
|
||||
|
||||
```bash
|
||||
# Start a Meteor server in one terminal
|
||||
meteor run
|
||||
|
||||
# Connect a shell in another terminal
|
||||
meteor shell
|
||||
|
||||
# Now you can run server-side code interactively:
|
||||
> Meteor.users.find().count()
|
||||
> Package.mongo.Mongo.Collection.prototype
|
||||
> Meteor.isServer
|
||||
true
|
||||
> .reload # Manually restart the shell
|
||||
```
|
||||
|
||||
::: details Advanced Example
|
||||
```js
|
||||
// Query the database
|
||||
> db = Package.mongo.MongoInternals.defaultRemoteCollectionDriver().mongo.db
|
||||
> db.collection('users').find().toArray()
|
||||
|
||||
// Access Meteor settings
|
||||
> Meteor.settings.public
|
||||
|
||||
// Inspect publications
|
||||
> Object.keys(Meteor.server.publish_handlers)
|
||||
```
|
||||
:::
|
||||
|
||||
## meteor npm {#meteornpm}
|
||||
|
||||
Run npm commands using Meteor's bundled npm version.
|
||||
|
||||
```bash
|
||||
meteor npm <command> [args...]
|
||||
```
|
||||
|
||||
### Description
|
||||
|
||||
The `meteor npm` command executes [npm](https://docs.npmjs.com/) commands using the version bundled with Meteor itself.
|
||||
|
||||
::: tip Benefits of Using Meteor's npm
|
||||
1. Ensures compatibility with Meteor's Node.js version
|
||||
2. Crucial for packages with native dependencies (like `bcrypt`)
|
||||
3. No need to install npm separately
|
||||
4. Consistent behavior across development environments
|
||||
:::
|
||||
|
||||
### Common Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `meteor npm install` | Install all dependencies listed in `package.json` |
|
||||
| `meteor npm install <package> --save` | Install and save a package as a dependency |
|
||||
| `meteor npm install <package> --save-dev` | Install and save a package as a development dependency |
|
||||
| `meteor npm update` | Update all packages to their latest allowed versions |
|
||||
| `meteor npm ls` | List installed packages |
|
||||
| `meteor npm rebuild` | Rebuild packages that have native dependencies |
|
||||
|
||||
### Examples
|
||||
|
||||
```bash
|
||||
# Install a package and save to dependencies
|
||||
meteor npm install lodash --save
|
||||
|
||||
# Install packages from package.json
|
||||
meteor npm install
|
||||
|
||||
# Run an npm script defined in package.json
|
||||
meteor npm run start
|
||||
|
||||
# View package information
|
||||
meteor npm info react
|
||||
```
|
||||
|
||||
::: warning Native Dependencies
|
||||
Using `meteor npm` instead of regular `npm` is especially important when working with packages that have binary dependencies making native C calls (like `bcrypt`). This ensures they're built with the same libraries used by Meteor.
|
||||
:::
|
||||
|
||||
## meteor node {#meteornode}
|
||||
|
||||
Run Node.js commands using Meteor's bundled Node.js version.
|
||||
|
||||
```bash
|
||||
meteor node [options] [script.js] [arguments]
|
||||
```
|
||||
|
||||
::: info Alternative
|
||||
Consider using [`meteor shell`](#meteorshell) instead, which provides similar functionality plus access to your Meteor application's server context.
|
||||
:::
|
||||
|
||||
### Description
|
||||
|
||||
The `meteor node` command runs [Node.js](https://nodejs.org/) using the version bundled with Meteor itself.
|
||||
|
||||
### Common Uses
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `meteor node` | Start an interactive Node.js REPL |
|
||||
| `meteor node script.js` | Execute a JavaScript file |
|
||||
| `meteor node -e "<code>"` | Execute a line of JavaScript |
|
||||
| `meteor node --version` | Show Node.js version |
|
||||
|
||||
### Examples
|
||||
|
||||
```bash
|
||||
# Start an interactive REPL
|
||||
meteor node
|
||||
|
||||
# Execute inline JavaScript
|
||||
meteor node -e "console.log(process.versions)"
|
||||
|
||||
# Run a script with arguments
|
||||
meteor node scripts/migrate.js --force
|
||||
|
||||
# Check installed Node.js version
|
||||
meteor node --version
|
||||
```
|
||||
|
||||
::: details Running a Simple Script
|
||||
Create `hello.js`:
|
||||
```js
|
||||
console.log('Hello from Node.js version', process.version);
|
||||
console.log('Arguments:', process.argv.slice(2));
|
||||
```
|
||||
|
||||
Run it:
|
||||
```bash
|
||||
meteor node hello.js arg1 arg2
|
||||
```
|
||||
:::
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
## v3.3.0, 2025-05-15
|
||||
## v3.3.0, 2025-06-11
|
||||
|
||||
### Highlights
|
||||
|
||||
@@ -13,11 +13,11 @@
|
||||
- Fix user agent detection and oplog collection filtering
|
||||
- Refine type definitions for Meteor methods and SSR's ServerSink
|
||||
- Allow opting out of usage stats with `DO_NOT_TRACK`
|
||||
- Update Node to 22.15.1 and Express to 5.1.0
|
||||
- Update Node to 22.16.0 and Express to 5.1.0
|
||||
|
||||
All Merged PRs@[GitHub PRs 3.3](https://github.com/meteor/meteor/pulls?q=is%3Apr+is%3Amerged+base%3Arelease-3.3)
|
||||
|
||||
React Packages Changelog: [react-meteor-data@4.0.0-beta.0](https://github.com/meteor/react-packages/blob/fb73eeb89ff59664a7a01769fa1c2c880e72a3e5/packages/react-meteor-data/CHANGELOG.md#v400-beta0-xxx)
|
||||
React Packages Changelog: [react-meteor-data@4.0.0](https://github.com/meteor/react-packages/tree/master/packages/react-meteor-data/CHANGELOG.md#v400-2025-06-11)
|
||||
|
||||
#### Breaking Changes
|
||||
|
||||
@@ -26,7 +26,7 @@ React Packages Changelog: [react-meteor-data@4.0.0-beta.0](https://github.com/me
|
||||
- Set `METEOR_WATCH_FORCE_POLLING=true` to enable polling.
|
||||
- Set `METEOR_WATCH_POLLING_INTERVAL_MS=1000` to adjust the interval.
|
||||
|
||||
- `react-meteor-data@4.0.0-beta.0`
|
||||
- `react-meteor-data@4.0.0`
|
||||
- Independent from the core, only applies if upgraded manually.
|
||||
- useFind describes no deps by default [PR#431](https://github.com/meteor/react-packages/pull/431)
|
||||
|
||||
@@ -41,13 +41,13 @@ React Packages Changelog: [react-meteor-data@4.0.0-beta.0](https://github.com/me
|
||||
Please run the following command to update your project:
|
||||
|
||||
```bash
|
||||
meteor update --release 3.3-beta.1
|
||||
meteor update --release 3.3
|
||||
```
|
||||
|
||||
To apply react-meteor-data changes:
|
||||
|
||||
```bash
|
||||
meteor add react-meteor-data@4.0.0-beta.0
|
||||
meteor add react-meteor-data@4.0.0
|
||||
```
|
||||
|
||||
**Add this to your `package.json` to enable the new modern build stack:**
|
||||
@@ -60,6 +60,14 @@ meteor add react-meteor-data@4.0.0-beta.0
|
||||
|
||||
> These settings are on by default for new apps.
|
||||
|
||||
On activate `modern` your app will be updated to use SWC transpiler. It will automatically fallback to Babel if your code can't be transpiled wit SWC.
|
||||
|
||||
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
|
||||
|
||||
- accounts-base@3.1.1
|
||||
@@ -71,10 +79,13 @@ meteor add react-meteor-data@4.0.0-beta.0
|
||||
- ecmascript@0.16.11
|
||||
- ejson@1.1.5
|
||||
- meteor@2.1.1
|
||||
- minifier-js@3.0.2
|
||||
- modern-browsers@0.2.2
|
||||
- mongo@2.1.2
|
||||
- server-render@0.4.3
|
||||
- socket-stream-client@0.6.1
|
||||
- standard-minifier-js@3.1.0
|
||||
- typescript@5.6.4
|
||||
- webapp@2.0.7
|
||||
- meteor-tool@3.3.0
|
||||
|
||||
@@ -96,5 +107,6 @@ meteor add react-meteor-data@4.0.0-beta.0
|
||||
- [@PedroMarianoAlmeida](https://github.com/PedroMarianoAlmeida)
|
||||
- [@harryadel](https://github.com/harryadel)
|
||||
- [@ericm546](https://github.com/ericm546)
|
||||
- [@StorytellerCZ](https://github.com/StorytellerCZ)
|
||||
|
||||
✨✨✨
|
||||
|
||||
@@ -10,7 +10,118 @@ This is a complete history of changes for Meteor releases.
|
||||
|
||||
[//]: # (go to meteor/docs/generators/changelog/docs)
|
||||
|
||||
## v3.3.0, 2025-06-11
|
||||
|
||||
### Highlights
|
||||
|
||||
- Support SWC transpiler and minifier for faster dev and builds [PR#13657](https://github.com/meteor/meteor/pull/13657), [PR#13715](https://github.com/meteor/meteor/pull/13715)
|
||||
- Switch to `@parcel/watcher` for improved native file watching [PR#13699](https://github.com/meteor/meteor/pull/13699), [#13707](https://github.com/meteor/meteor/pull/13707)
|
||||
- Default to modern architecture, skip legacy processing [PR#13665](https://github.com/meteor/meteor/pull/13665), [PR#13698](https://github.com/meteor/meteor/pull/13698)
|
||||
- Optimize SQLite for faster startup and better performance [PR#13702](https://github.com/meteor/meteor/pull/13702)
|
||||
- Support CPU profiling in Meteor 3 bundler [PR#13650](https://github.com/meteor/meteor/pull/13650)
|
||||
- Improve `meteor profile`: show rebuild steps and total, support `--build` [PR#16](https://github.com/meteor/performance/pull/16), [PR#13694](https://github.com/meteor/meteor/pull/13694)
|
||||
- Improve `useFind` and `useSubscribe` React hooks
|
||||
- Add `replaceEmailAsync` helper to Accounts [PR#13677](https://github.com/meteor/meteor/pull/13677)
|
||||
- Fix user agent detection and oplog collection filtering
|
||||
- Refine type definitions for Meteor methods and SSR's ServerSink
|
||||
- Allow opting out of usage stats with `DO_NOT_TRACK`
|
||||
- Update Node to 22.16.0 and Express to 5.1.0
|
||||
|
||||
All Merged PRs@[GitHub PRs 3.3](https://github.com/meteor/meteor/pulls?q=is%3Apr+is%3Amerged+base%3Arelease-3.3)
|
||||
|
||||
React Packages Changelog: [react-meteor-data@4.0.0](https://github.com/meteor/react-packages/tree/master/packages/react-meteor-data/CHANGELOG.md#v400-2025-06-11)
|
||||
|
||||
#### Breaking Changes
|
||||
|
||||
- File watching strategy switched to `@parcel/watcher`
|
||||
- Most setups should be fine, but if issues appear, like when using WSL with host, volumes, or remote setups—switch to polling.
|
||||
- Set `METEOR_WATCH_FORCE_POLLING=true` to enable polling.
|
||||
- Set `METEOR_WATCH_POLLING_INTERVAL_MS=1000` to adjust the interval.
|
||||
|
||||
- `react-meteor-data@4.0.0`
|
||||
- Independent from the core, only applies if upgraded manually.
|
||||
- useFind describes no deps by default [PR#431](https://github.com/meteor/react-packages/pull/431)
|
||||
|
||||
#### Internal API changes
|
||||
|
||||
- `express@5.1.0` - Depends on Meteor’s `webapp` package.
|
||||
- Deprecates non-native promise usage [#154](https://github.com/pillarjs/router/pull/154)
|
||||
- Use `async/await` or `Promise.resolve` when defining endpoints to avoid deprecation warnings.
|
||||
|
||||
#### Migration Steps
|
||||
|
||||
Please run the following command to update your project:
|
||||
|
||||
```bash
|
||||
meteor update --release 3.3
|
||||
```
|
||||
|
||||
To apply react-meteor-data changes:
|
||||
|
||||
```bash
|
||||
meteor add react-meteor-data@4.0.0
|
||||
```
|
||||
|
||||
**Add this to your `package.json` to enable the new modern build stack:**
|
||||
|
||||
```json
|
||||
"meteor": {
|
||||
"modern": true
|
||||
}
|
||||
```
|
||||
|
||||
> These settings are on by default for new apps.
|
||||
|
||||
On activate `modern` your app will be updated to use SWC transpiler. It will automatically fallback to Babel if your code can't be transpiled wit SWC.
|
||||
|
||||
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
|
||||
|
||||
- accounts-base@3.1.1
|
||||
- accounts-password@3.2.0
|
||||
- autoupdate@2.0.1
|
||||
- babel-compiler@7.12.0
|
||||
- boilerplate-generator@2.0.1
|
||||
- ddp-client@3.1.1
|
||||
- ecmascript@0.16.11
|
||||
- ejson@1.1.5
|
||||
- meteor@2.1.1
|
||||
- minifier-js@3.0.2
|
||||
- modern-browsers@0.2.2
|
||||
- mongo@2.1.2
|
||||
- server-render@0.4.3
|
||||
- socket-stream-client@0.6.1
|
||||
- standard-minifier-js@3.1.0
|
||||
- typescript@5.6.4
|
||||
- webapp@2.0.7
|
||||
- meteor-tool@3.3.0
|
||||
|
||||
#### Bumped NPM Packages
|
||||
|
||||
- meteor-node-stubs@1.2.17
|
||||
|
||||
#### Special thanks to
|
||||
|
||||
✨✨✨
|
||||
|
||||
- [@nachocodoner](https://github.com/nachocodoner)
|
||||
- [@italojs](https://github.com/italojs)
|
||||
- [@Grubba27](https://github.com/Grubba27)
|
||||
- [@zodern](https://github.com/zodern)
|
||||
- [@9Morello](https://github.com/9Morello)
|
||||
- [@welkinwong](https://github.com/welkinwong)
|
||||
- [@Poyoman39](https://github.com/Poyoman39)
|
||||
- [@PedroMarianoAlmeida](https://github.com/PedroMarianoAlmeida)
|
||||
- [@harryadel](https://github.com/harryadel)
|
||||
- [@ericm546](https://github.com/ericm546)
|
||||
- [@StorytellerCZ](https://github.com/StorytellerCZ)
|
||||
|
||||
✨✨✨
|
||||
|
||||
## v3.2.2, 2025-05-02
|
||||
|
||||
|
||||
Reference in New Issue
Block a user