From fa1d2f3d712e258248fd8bb0d03c7b259c72f179 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2024 10:22:14 +0000 Subject: [PATCH 001/166] Bump actions/setup-node from 2 to 4 Bumps [actions/setup-node](https://github.com/actions/setup-node) from 2 to 4. - [Release notes](https://github.com/actions/setup-node/releases) - [Commits](https://github.com/actions/setup-node/compare/v2...v4) --- updated-dependencies: - dependency-name: actions/setup-node dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/meteor-selftest-windows.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/meteor-selftest-windows.yml b/.github/workflows/meteor-selftest-windows.yml index eed1b2b1b8..ab94b2f973 100644 --- a/.github/workflows/meteor-selftest-windows.yml +++ b/.github/workflows/meteor-selftest-windows.yml @@ -29,7 +29,7 @@ jobs: uses: actions/checkout@v4 - name: Setup Node.js - uses: actions/setup-node@v2 + uses: actions/setup-node@v4 with: node-version: 20.x From d26472ef6475776fd1399567e35b11eaebdc1fc4 Mon Sep 17 00:00:00 2001 From: Reina128 <114252439+Reina128@users.noreply.github.com> Date: Fri, 9 Aug 2024 11:36:32 -0700 Subject: [PATCH 002/166] simplified the utils.js code --- tools/utils/utils.js | 33 +++++---------------------------- 1 file changed, 5 insertions(+), 28 deletions(-) diff --git a/tools/utils/utils.js b/tools/utils/utils.js index 8ccd4f5b5a..fa137beaf5 100644 --- a/tools/utils/utils.js +++ b/tools/utils/utils.js @@ -34,11 +34,7 @@ exports.parseUrl = function (str, defaults) { } var hasScheme = exports.hasScheme(str); - if (! hasScheme) { - str = "http://" + str; - } - - var parsed = url.parse(str); + const parsed = url.parse(hasScheme ? str : `http://${str}`); // for consistency remove colon at the end of protocol parsed.protocol = parsed.protocol.replace(/\:$/, ''); @@ -59,10 +55,7 @@ exports.parseUrl = function (str, defaults) { exports.formatUrl = function (options) { // For consistency with `Meteor.absoluteUrl`, add a trailing slash to make // this a valid URL - if (!options.pathname) - options.pathname = "/"; - - return url.format(options); + return url.format({ ...options, pathname: options.pathname || "/" }); }; exports.ipAddress = function () { @@ -88,11 +81,6 @@ ${addressEntries.map(entry => entry.address).join(', ')}`); return addressEntries[0].address; }; -exports.hasScheme = function (str) { - return !! str.match(/^[A-Za-z][A-Za-z0-9+-\.]*\:\/\//); -}; - - exports.hasScheme = function (str) { return !! str.match(/^[A-Za-z][A-Za-z0-9+-\.]*\:\/\//); }; @@ -105,20 +93,8 @@ exports.isIPv4Address = function (str) { // Prints a package list in a nice format. // Input is an array of objects with keys 'name' and 'description'. exports.printPackageList = function (items, options) { - options = options || {}; - - var rows = _.map(items, function (item) { - var name = item.name; - var description = item.description || 'No description'; - return [name, description]; - }); - - var alphaSort = function (row) { - return row[0]; - }; - rows = _.sortBy(rows, alphaSort); - - var Console = require('../console/console.js').Console; + const rows = _.sortBy(items.map(item => [item.name, item.description || 'No description']), row => row[0]); + const Console = require('../console/console.js').Console; return Console.printTwoColumns(rows, options); }; @@ -773,3 +749,4 @@ export function isEmacs() { emacsDetected = !! (process.env.EMACS === "t" || process.env.INSIDE_EMACS); return emacsDetected; } + From d6a7cf2bf1dc80ca3d8bb945848176fd6a30740c Mon Sep 17 00:00:00 2001 From: Caio Date: Sat, 2 Nov 2024 17:24:40 -0300 Subject: [PATCH 003/166] Fix accounts-passwordless tests failing due to an unexpected configuration default --- packages/accounts-passwordless/server_tests.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/accounts-passwordless/server_tests.js b/packages/accounts-passwordless/server_tests.js index 768023b952..d15af99bca 100644 --- a/packages/accounts-passwordless/server_tests.js +++ b/packages/accounts-passwordless/server_tests.js @@ -4,6 +4,10 @@ import { SHA256 } from 'meteor/sha'; const USER_TOKEN = '123ABC'; +// The test suite for accounts-passwordless includes testing whether it gets the right error messages from the server. +// So, we need this disabled, otherwise those tests incorrectly fail when you run them. +Accounts._options.ambiguousErrorMessages = false; + const getData = ({ createdAt }) => { const userId = Random.id(); const email = `${userId}@meteorapp.com`; From 89cc3db9a11188a23769a6ac4bef14d25cfebbb1 Mon Sep 17 00:00:00 2001 From: 9Morello Date: Wed, 20 Nov 2024 17:06:02 -0300 Subject: [PATCH 004/166] move side effect to inside the first test, to prevent it from affecting other running test suits --- packages/accounts-passwordless/server_tests.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/accounts-passwordless/server_tests.js b/packages/accounts-passwordless/server_tests.js index d15af99bca..9855e1c020 100644 --- a/packages/accounts-passwordless/server_tests.js +++ b/packages/accounts-passwordless/server_tests.js @@ -4,10 +4,6 @@ import { SHA256 } from 'meteor/sha'; const USER_TOKEN = '123ABC'; -// The test suite for accounts-passwordless includes testing whether it gets the right error messages from the server. -// So, we need this disabled, otherwise those tests incorrectly fail when you run them. -Accounts._options.ambiguousErrorMessages = false; - const getData = ({ createdAt }) => { const userId = Random.id(); const email = `${userId}@meteorapp.com`; @@ -32,6 +28,9 @@ const getData = ({ createdAt }) => { }; Tinytest.add('passwordless - time expired', test => { + // The test suite for accounts-passwordless includes testing whether it gets the right error messages from the server. + // So, we need this disabled, otherwise those tests incorrectly fail when you run them. + Accounts._options.ambiguousErrorMessages = false; const createdAt = new Date('July 17, 2022 13:00:00'); const currentDate = new Date('July 17, 2022 14:01:00'); From fde56215ec4cdabb8cda86c724659ef6c2dd4393 Mon Sep 17 00:00:00 2001 From: graemepyle Date: Mon, 1 Sep 2025 22:55:49 +0400 Subject: [PATCH 005/166] Use concurrency-safe iteration in async methods --- packages/minimongo/local_collection.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/minimongo/local_collection.js b/packages/minimongo/local_collection.js index 590a6e9658..1ee5ad7a78 100644 --- a/packages/minimongo/local_collection.js +++ b/packages/minimongo/local_collection.js @@ -176,7 +176,7 @@ export default class LocalCollection { const queriesToRecompute = []; // trigger live queries that match - for (const qid of Object.keys(this.queries)) { + for (const qid in this.queries) { const query = this.queries[qid]; if (query.dirty) { @@ -827,7 +827,7 @@ export default class LocalCollection { LocalCollection._modify(doc, mod, {arrayIndices}); const recomputeQids = {}; - for (const qid of Object.keys(this.queries)) { + for (const qid in this.queries) { const query = this.queries[qid]; if (query.dirty) { From 8ba72268a6dfb6a5d8c35a353ce3c25d7bba1555 Mon Sep 17 00:00:00 2001 From: PARITOSH DEY <140790221+paritoshdey-dev@users.noreply.github.com> Date: Tue, 23 Sep 2025 23:32:13 +0530 Subject: [PATCH 006/166] Fix: make ParamTable always render so Chrome search can detect text (#13937) --- .../docs/components/helpers/ParamTable.vue | 76 ++++++++++--------- 1 file changed, 42 insertions(+), 34 deletions(-) diff --git a/v3-docs/docs/components/helpers/ParamTable.vue b/v3-docs/docs/components/helpers/ParamTable.vue index c3d1cdf9ed..b17f6a8142 100644 --- a/v3-docs/docs/components/helpers/ParamTable.vue +++ b/v3-docs/docs/components/helpers/ParamTable.vue @@ -1,14 +1,14 @@ diff --git a/packages/meetup-config-ui/meetup_configure.html b/packages/meetup-config-ui/meetup_configure.html index 5433697dbd..a0a6b36d82 100644 --- a/packages/meetup-config-ui/meetup_configure.html +++ b/packages/meetup-config-ui/meetup_configure.html @@ -4,20 +4,25 @@

  1. - Visit http://www.meetup.com/meetup_api/oauth_consumers/create/ + Visit https://www.meetup.com/api/oauth/list/ and sign in.
  2. - Click on "Create New Consumer". + Click "Create new client".
  3. - Set the Consumer name to the name of your application. + Set the "Client name" to the name of your application.
  4. - Optionally set the Application Website to the URL of your - website. You can leave this blank. + Set the "Application Website" to your site URL.
  5. - Set the Redirect URI to: {{siteUrl}} (Do not append a path to this URL.) + Set the Redirect URI to: {{siteUrl}} (Do not append a path to this URL.) +
  6. +
  7. + Fill out all the other required fields. +
  8. +
  9. + Click "Create" and note down your "Key" (Client ID) and "Secret" (Client Secret).
diff --git a/packages/meteor-developer-config-ui/meteor_developer_configure.html b/packages/meteor-developer-config-ui/meteor_developer_configure.html index 6d34d867e6..592a11b8be 100644 --- a/packages/meteor-developer-config-ui/meteor_developer_configure.html +++ b/packages/meteor-developer-config-ui/meteor_developer_configure.html @@ -4,15 +4,17 @@ Follow these steps:

    -
  1. Visit https://www.meteor.com/account-settings and sign in. +
  2. + Visit https://beta.galaxycloud.app/ and sign in.
  3. -
  4. Click "NEW APPLICATION" in the "Meteor Account Services" section - and give your app a name.
  5. -
  6. Add - - {{siteUrl}}_oauth/meteor-developer - - as the Redirect URL. +
  7. + Go to "Settings" -> "Authorized Domains" and "Add New Domain". +
  8. +
  9. + Set the "OAuth Redirect URL" to: {{siteUrl}}_oauth/meteor-developer +
  10. +
  11. + Click "Create" and note down your "Client ID" and "Client Secret".
diff --git a/packages/twitter-config-ui/twitter_configure.html b/packages/twitter-config-ui/twitter_configure.html index 85b2f3c2d1..b3fb304d65 100644 --- a/packages/twitter-config-ui/twitter_configure.html +++ b/packages/twitter-config-ui/twitter_configure.html @@ -4,22 +4,28 @@

  1. - Visit https://developer.twitter.com/en/portal/projects/new + Visit https://developer.x.com/en/portal/dashboard and sign in.
  2. - Select "Add project". + Create a new project and app (or select an existing one).
  3. - Save the API keys. + In your app settings, click on "Set up" under "User authentication settings".
  4. - Once you create your project, click "Set up" under "User authentication settings" + Enable "OAuth 1.0a" (required for Meteor).
  5. - Set Callback URI to: {{siteUrl}}_oauth/twitter + Set "Callback URI / Redirect URL" to: {{siteUrl}}_oauth/twitter
  6. - Set Website to: {{siteUrl}} + Set "Website URL" to: {{siteUrl}} +
  7. +
  8. + Click "Save". +
  9. +
  10. + Go to the "Keys and tokens" tab and note down your "API Key" (Consumer Key) and "API Key Secret" (Consumer Secret).
diff --git a/packages/weibo-config-ui/weibo_configure.html b/packages/weibo-config-ui/weibo_configure.html index e48ff8e4e9..b956b8e407 100644 --- a/packages/weibo-config-ui/weibo_configure.html +++ b/packages/weibo-config-ui/weibo_configure.html @@ -1,25 +1,31 @@ From f5e3e4c72737a54286dd57b932d9100379a2d384 Mon Sep 17 00:00:00 2001 From: bratelefant Date: Wed, 19 Nov 2025 08:41:49 +0100 Subject: [PATCH 008/166] fix: update getUsersInRoleAsync to handle assignment options correctly, adressing issue #14013 --- packages/roles/roles_common_async.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/roles/roles_common_async.js b/packages/roles/roles_common_async.js index dbd46d5b71..ee76160625 100644 --- a/packages/roles/roles_common_async.js +++ b/packages/roles/roles_common_async.js @@ -1052,8 +1052,11 @@ Object.assign(Roles, { * @return {Promise} Cursor of users in roles. */ getUsersInRoleAsync: async function (roles, options, queryOptions) { + const assignmentOptions = { ...options } + delete assignmentOptions.queryOptions + const ids = ( - await Roles.getUserAssignmentsForRole(roles, options).fetchAsync() + await Roles.getUserAssignmentsForRole(roles, assignmentOptions).fetchAsync() ).map((a) => a.user._id) return Meteor.users.find( From 4c8000fd99e87f9038a89c8305fef5f433224951 Mon Sep 17 00:00:00 2001 From: bratelefant Date: Wed, 19 Nov 2025 08:54:06 +0100 Subject: [PATCH 009/166] bump version for roles package to 1.0.2 --- packages/roles/package.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/roles/package.js b/packages/roles/package.js index dbcc7ac855..d0943ce407 100644 --- a/packages/roles/package.js +++ b/packages/roles/package.js @@ -2,7 +2,7 @@ Package.describe({ summary: "Authorization package for Meteor", - version: "1.0.1", + version: "1.0.2", name: "roles", documentation: null, }); From 6a659b11af37120044a12aef56e2e36b840e3389 Mon Sep 17 00:00:00 2001 From: bratelefant Date: Wed, 19 Nov 2025 10:15:27 +0100 Subject: [PATCH 010/166] normalize options in getUsersInRoleAsync --- packages/roles/roles_common_async.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/roles/roles_common_async.js b/packages/roles/roles_common_async.js index ee76160625..8b9babb9f4 100644 --- a/packages/roles/roles_common_async.js +++ b/packages/roles/roles_common_async.js @@ -1052,6 +1052,8 @@ Object.assign(Roles, { * @return {Promise} Cursor of users in roles. */ getUsersInRoleAsync: async function (roles, options, queryOptions) { + options = Roles._normalizeOptions(options) + const assignmentOptions = { ...options } delete assignmentOptions.queryOptions From 2271cb5b007558d6bd84f06e1cab75b98dcf03a7 Mon Sep 17 00:00:00 2001 From: bratelefant Date: Wed, 19 Nov 2025 10:23:49 +0100 Subject: [PATCH 011/166] add test for retrieving users in role with mongo query options --- packages/roles/tests/serverAsync.js | 32 +++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/packages/roles/tests/serverAsync.js b/packages/roles/tests/serverAsync.js index 1e844550fc..043af5fdf1 100644 --- a/packages/roles/tests/serverAsync.js +++ b/packages/roles/tests/serverAsync.js @@ -1596,6 +1596,38 @@ Tinytest.addAsync( } ); + +Tinytest.addAsync( + "roles -can get all users in role by scope and passes through mongo query arguments only to the users collection when included in the options", + async function (test) { + await clearData(); + await Roles.createRoleAsync("admin"); + await Roles.createRoleAsync("user"); + + await Roles.addUsersToRolesAsync( + [users.eve, users.joe], + ["admin", "user"], + "scope1" + ); + await Roles.addUsersToRolesAsync( + [users.bob, users.joe], + ["admin"], + "scope2" + ); + + const cursor = await Roles.getUsersInRoleAsync("admin", { scope: "scope1", queryOptions: { + fields: { _id: 1, username: 1 }, + limit: 1, + }}); + const results = await cursor.fetchAsync(); + + test.equal(1, results.length); + test.isTrue(hasProp(results[0], "_id")); + test.isTrue(hasProp(results[0], "username")); + } +); + + Tinytest.addAsync( "roles -can use Roles.GLOBAL_SCOPE to assign blanket roles", async function (test) { From 79ddb7517e86c053610bc7cf038b19e9fe725f59 Mon Sep 17 00:00:00 2001 From: italo jose Date: Fri, 21 Nov 2025 12:32:49 -0300 Subject: [PATCH 012/166] observeChanges tests: register expect resolvers before inserts to avoid race --- packages/mongo/tests/observe_changes_tests.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mongo/tests/observe_changes_tests.js b/packages/mongo/tests/observe_changes_tests.js index 8dd50eed5d..ac7a671cac 100644 --- a/packages/mongo/tests/observe_changes_tests.js +++ b/packages/mongo/tests/observe_changes_tests.js @@ -519,8 +519,8 @@ if (Meteor.isServer) { const [resolver1, promise1] = getPromiseAndResolver(); const [resolver2, promise2] = getPromiseAndResolver(); - await self.insert({x: 2, y: 3}); self.expects.push(resolver1, resolver2); + await self.insert({x: 2, y: 3}); await self.insert({x: 3, y: 7}); // filtered out by the query await self.insert({x: 4}); // Expect two added calls to happen. From d9f1123a4c059b2be0ff1919dc630ef11febd8c1 Mon Sep 17 00:00:00 2001 From: italo jose Date: Tue, 25 Nov 2025 11:00:55 -0300 Subject: [PATCH 013/166] Add travis-compat GitHub Actions workflow to run Travis parity tests - Add .github/workflows/travis-compat.yml - Trigger on push to ci/removing-travis - Runs on ubuntu-22.04 with Node 22.17 and C++ toolchain g++-12 - Restores caches (.npm, .meteor, .babel-cache, dev_bundle, chromium), installs system/npm deps and runs packages/test-in-console/run.sh --- .github/workflows/test-packages.yml | 53 +++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 .github/workflows/test-packages.yml diff --git a/.github/workflows/test-packages.yml b/.github/workflows/test-packages.yml new file mode 100644 index 0000000000..776c3b6c55 --- /dev/null +++ b/.github/workflows/test-packages.yml @@ -0,0 +1,53 @@ +name: Meteor test packages + +on: + push: + branches: + - ci/removing-travis + +jobs: + travis-compat: + runs-on: ubuntu-22.04 + concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + timeout-minutes: 90 + env: + CXX: g++-12 + phantom: false + PUPPETEER_DOWNLOAD_PATH: /home/runner/.npm/chromium + TEST_PACKAGES_EXCLUDE: stylus + METEOR_MODERN: true + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: 22.17.0 + + - name: Restore caches + uses: actions/cache@v4 + with: + path: | + ~/.npm + .meteor + .babel-cache + dev_bundle + /home/runner/.npm/chromium + key: ${{ runner.os }}-node-22.17-${{ hashFiles('meteor', '**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node-22.17- + + - name: Install system dependencies + run: | + sudo apt-get update + sudo apt-get install -y g++-12 libnss3 + + - name: Install npm dependencies + run: npm install + + - name: Run test-in-console suite (Travis parity) + run: ./packages/test-in-console/run.sh From 70570e76f178113ceec510cc144efa129916b637 Mon Sep 17 00:00:00 2001 From: italo jose Date: Tue, 25 Nov 2025 15:15:11 -0300 Subject: [PATCH 014/166] Install Puppeteer in test-packages workflow, add jquery to test-in-console, and export METEOR_NO_DEPRECATION earlier in run.sh --- .github/workflows/test-packages.yml | 3 +++ packages/test-in-console/package.js | 1 + packages/test-in-console/run.sh | 1 + 3 files changed, 5 insertions(+) diff --git a/.github/workflows/test-packages.yml b/.github/workflows/test-packages.yml index 776c3b6c55..ce65726e6d 100644 --- a/.github/workflows/test-packages.yml +++ b/.github/workflows/test-packages.yml @@ -49,5 +49,8 @@ jobs: - name: Install npm dependencies run: npm install + - name: Install Puppeteer + run: ./meteor npm install -g puppeteer@23.6.0 + - name: Run test-in-console suite (Travis parity) run: ./packages/test-in-console/run.sh diff --git a/packages/test-in-console/package.js b/packages/test-in-console/package.js index ebf1b6eff9..fe96041fd6 100644 --- a/packages/test-in-console/package.js +++ b/packages/test-in-console/package.js @@ -6,6 +6,7 @@ Package.describe({ Package.onUse(function(api) { api.use(['tinytest', 'random', 'ejson', 'check', 'ecmascript']); api.use('fetch', 'server'); + api.use('jquery', 'client'); api.export('TEST_STATUS', 'client'); diff --git a/packages/test-in-console/run.sh b/packages/test-in-console/run.sh index 5a88e9065d..5bdfb466eb 100755 --- a/packages/test-in-console/run.sh +++ b/packages/test-in-console/run.sh @@ -15,6 +15,7 @@ export PATH=$METEOR_HOME:$PATH export URL='http://127.0.0.1:4096/' export METEOR_PACKAGE_DIRS='packages/deprecated' +export METEOR_NO_DEPRECATION=true exec 3< <(./meteor test-packages --driver-package test-in-console -p 4096 --exclude ${TEST_PACKAGES_EXCLUDE:-''} $1) EXEC_PID=$! From 7a53730fbd78d857dc791a4f0f49e18e4617ede4 Mon Sep 17 00:00:00 2001 From: italo jose Date: Tue, 25 Nov 2025 17:44:59 -0300 Subject: [PATCH 015/166] Detect writable group dynamically in webapp socket tests and skip when none found Add getGroupNameForGid and getWritableGroupName helpers to resolve a writable group from /etc/group, the current user's gid/groups, or fallbacks (TRAVIS/staff/root). Use the resolved group for UNIX_SOCKET_GROUP in socket_file_tests and skip the group-specific test when no writable group can be determined. --- packages/webapp/socket_file_tests.js | 52 +++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/packages/webapp/socket_file_tests.js b/packages/webapp/socket_file_tests.js index 2dbde1927f..143cb8ca4d 100644 --- a/packages/webapp/socket_file_tests.js +++ b/packages/webapp/socket_file_tests.js @@ -26,6 +26,49 @@ const isMacOS = () => { return platform() === 'darwin'; }; +const getGroupNameForGid = (gid) => { + try { + const data = readFileSync('/etc/group', 'utf8'); + const line = data + .trim() + .split('\n') + .find((groupLine) => { + const [, , groupGid] = groupLine.trim().split(':'); + return Number(groupGid) === gid; + }); + + if (!line) return null; + const [name] = line.trim().split(':'); + return name || null; + } catch { + return null; + } +}; + +const getWritableGroupName = () => { + const { gid, uid } = userInfo(); + const gidsToTry = new Set(); + + if (typeof gid === 'number') { + gidsToTry.add(gid); + } + + if (typeof process.getgroups === 'function') { + process.getgroups().forEach((groupId) => gidsToTry.add(groupId)); + } + + for (const groupId of gidsToTry) { + const groupName = getGroupNameForGid(groupId); + if (groupName) { + return groupName; + } + } + + if (Boolean(process.env.TRAVIS)) return 'travis'; + if (isMacOS()) return 'staff'; + return uid === 0 ? 'root' : null; +}; + const removeTestSocketFile = () => { try { unlinkSync(testSocketFile); @@ -131,9 +174,16 @@ testAsyncMulti( }, async (test) => { // use UNIX_SOCKET_PATH and UNIX_SOCKET_GROUP + const groupToUse = getWritableGroupName(); + + if (!groupToUse) { + // Skip when no writable group could be determined for the current user. + test.isTrue(true); + return; + } + const { httpServer, server } = prepareServer(); - const groupToUse = Boolean(process.env.TRAVIS) && 'travis' || (isMacOS() ? 'staff' : 'root'); process.env.UNIX_SOCKET_PATH = testSocketFile; process.env.UNIX_SOCKET_GROUP = groupToUse; process.env.UNIX_SOCKET_PERMISSIONS = '777'; From 3d6e40b19bc82036c21b49c17383d64bcc9dad92 Mon Sep 17 00:00:00 2001 From: italo jose Date: Wed, 26 Nov 2025 14:15:43 -0300 Subject: [PATCH 016/166] Rename workflow job, remove Puppeteer install, add TINYTEST_FILTER, and add email debug logs - Rename GitHub Actions job travis-compat -> test-packages - Add TINYTEST_FILTER="email" to test-packages workflow env - Remove global Puppeteer install step and drop "Travis parity" label from run step - Add console.log debug output in Email.sendAsync to show MAIL_URL env, settings, and Meteor.isProduction before the production check --- .github/workflows/test-packages.yml | 8 +++----- packages/email/email.js | 3 +++ 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test-packages.yml b/.github/workflows/test-packages.yml index ce65726e6d..682f220d78 100644 --- a/.github/workflows/test-packages.yml +++ b/.github/workflows/test-packages.yml @@ -6,7 +6,7 @@ on: - ci/removing-travis jobs: - travis-compat: + test-packages: runs-on: ubuntu-22.04 concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -18,6 +18,7 @@ jobs: PUPPETEER_DOWNLOAD_PATH: /home/runner/.npm/chromium TEST_PACKAGES_EXCLUDE: stylus METEOR_MODERN: true + TINYTEST_FILTER: "email" steps: - name: Checkout repository @@ -49,8 +50,5 @@ jobs: - name: Install npm dependencies run: npm install - - name: Install Puppeteer - run: ./meteor npm install -g puppeteer@23.6.0 - - - name: Run test-in-console suite (Travis parity) + - name: Run test-in-console suite run: ./packages/test-in-console/run.sh diff --git a/packages/email/email.js b/packages/email/email.js index a17b11fcf3..9a64ad3f23 100644 --- a/packages/email/email.js +++ b/packages/email/email.js @@ -251,6 +251,9 @@ Email.sendAsync = async function (options) { const mailUrlEnv = process.env.MAIL_URL; const mailUrlSettings = Meteor.settings.packages?.email; + console.log('MAIL_URL env:', mailUrlEnv); + console.log('MAIL_URL settings:', mailUrlSettings); + console.log('Meteor.isProduction:', Meteor.isProduction); if (Meteor.isProduction && !mailUrlEnv && !mailUrlSettings) { // This check is mostly necessary when using the flag --production when running locally. // And it works as a reminder to properly set the mail URL when running locally. From 77da1ca98bbe7985ffc427b8ac79688ac764c625 Mon Sep 17 00:00:00 2001 From: italo jose Date: Wed, 26 Nov 2025 14:33:58 -0300 Subject: [PATCH 017/166] Remove console.log debug output and include MAIL_URL, settings, and production flag in missing-mail error message --- packages/email/email.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/email/email.js b/packages/email/email.js index 9a64ad3f23..76f8e22001 100644 --- a/packages/email/email.js +++ b/packages/email/email.js @@ -251,14 +251,14 @@ Email.sendAsync = async function (options) { const mailUrlEnv = process.env.MAIL_URL; const mailUrlSettings = Meteor.settings.packages?.email; - console.log('MAIL_URL env:', mailUrlEnv); - console.log('MAIL_URL settings:', mailUrlSettings); - console.log('Meteor.isProduction:', Meteor.isProduction); if (Meteor.isProduction && !mailUrlEnv && !mailUrlSettings) { // This check is mostly necessary when using the flag --production when running locally. // And it works as a reminder to properly set the mail URL when running locally. throw new Error( - 'You have not provided a mail URL. You can provide it by using the environment variable MAIL_URL or your settings. You can read more about it here: https://docs.meteor.com/api/email.html.' + `You have not provided a mail URL. You can provide it by using the environment variable MAIL_URL or your settings. You can read more about it here: https://docs.meteor.com/api/email.html. + MAIL_URL env: ${mailUrlEnv} + MAIL_URL settings: ${JSON.stringify(mailUrlSettings)} + Meteor.isProduction: ${Meteor.isProduction}` ); } From 8641b0833499c5d29939bbbedf54fead09e913d8 Mon Sep 17 00:00:00 2001 From: italo jose Date: Wed, 26 Nov 2025 15:14:05 -0300 Subject: [PATCH 018/166] Unquote TINYTEST_FILTER value and add NODE_ENV: CI to test-packages workflow --- .github/workflows/test-packages.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-packages.yml b/.github/workflows/test-packages.yml index 682f220d78..df87444dbd 100644 --- a/.github/workflows/test-packages.yml +++ b/.github/workflows/test-packages.yml @@ -18,7 +18,8 @@ jobs: PUPPETEER_DOWNLOAD_PATH: /home/runner/.npm/chromium TEST_PACKAGES_EXCLUDE: stylus METEOR_MODERN: true - TINYTEST_FILTER: "email" + TINYTEST_FILTER: email + NODE_ENV: CI steps: - name: Checkout repository From 016b6e8aca534c21c042beccf4786d167186854d Mon Sep 17 00:00:00 2001 From: italo jose Date: Wed, 26 Nov 2025 18:19:43 -0300 Subject: [PATCH 019/166] Remove MAIL_URL, settings and production flag from missing-mail error message Previously the thrown error included the MAIL_URL environment value, package settings JSON, and Meteor.isProduction flag. That could leak sensitive configuration into logs. Replace it with the original concise guidance and link to the docs. --- packages/email/email.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/email/email.js b/packages/email/email.js index 76f8e22001..a17b11fcf3 100644 --- a/packages/email/email.js +++ b/packages/email/email.js @@ -255,10 +255,7 @@ Email.sendAsync = async function (options) { // This check is mostly necessary when using the flag --production when running locally. // And it works as a reminder to properly set the mail URL when running locally. throw new Error( - `You have not provided a mail URL. You can provide it by using the environment variable MAIL_URL or your settings. You can read more about it here: https://docs.meteor.com/api/email.html. - MAIL_URL env: ${mailUrlEnv} - MAIL_URL settings: ${JSON.stringify(mailUrlSettings)} - Meteor.isProduction: ${Meteor.isProduction}` + 'You have not provided a mail URL. You can provide it by using the environment variable MAIL_URL or your settings. You can read more about it here: https://docs.meteor.com/api/email.html.' ); } From 1aba25288ae97b65b7c19d772c477d64f7a95d61 Mon Sep 17 00:00:00 2001 From: italo jose Date: Wed, 26 Nov 2025 18:39:06 -0300 Subject: [PATCH 020/166] Remove TINYTEST_FILTER from test-packages workflow --- .github/workflows/test-packages.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/test-packages.yml b/.github/workflows/test-packages.yml index df87444dbd..33b828ce45 100644 --- a/.github/workflows/test-packages.yml +++ b/.github/workflows/test-packages.yml @@ -18,7 +18,6 @@ jobs: PUPPETEER_DOWNLOAD_PATH: /home/runner/.npm/chromium TEST_PACKAGES_EXCLUDE: stylus METEOR_MODERN: true - TINYTEST_FILTER: email NODE_ENV: CI steps: From 1a694755e5f82f631dbe3be20d290eaf45777506 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Italo=20Jos=C3=A9?= Date: Thu, 27 Nov 2025 10:37:56 -0300 Subject: [PATCH 021/166] Disable ambiguous error messages in password tests (#14027) Set Accounts._options.ambiguousErrorMessages = false in password_tests.js so tests receive specific, deterministic error reasons instead of ambiguous messages. --- packages/accounts-password/password_tests.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/accounts-password/password_tests.js b/packages/accounts-password/password_tests.js index 49f94544a0..5df402d74d 100644 --- a/packages/accounts-password/password_tests.js +++ b/packages/accounts-password/password_tests.js @@ -1,4 +1,6 @@ Accounts._connectionCloseDelayMsForTests = 1000; +Accounts._options.ambiguousErrorMessages = false; + const makeTestConnAsync = (test) => new Promise((resolve, reject) => { From ea67c7824924c64edc7d505ebfcff6d1611f03cc Mon Sep 17 00:00:00 2001 From: Christian Wahle <59534030+bratelefant@users.noreply.github.com> Date: Tue, 2 Dec 2025 18:26:31 +0100 Subject: [PATCH 022/166] Update packages/roles/roles_common_async.js Co-authored-by: Gabriel Grubba <70247653+Grubba27@users.noreply.github.com> --- packages/roles/roles_common_async.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/roles/roles_common_async.js b/packages/roles/roles_common_async.js index 8b9babb9f4..1d5aa19a0f 100644 --- a/packages/roles/roles_common_async.js +++ b/packages/roles/roles_common_async.js @@ -1055,7 +1055,7 @@ Object.assign(Roles, { options = Roles._normalizeOptions(options) const assignmentOptions = { ...options } - delete assignmentOptions.queryOptions + assignmentOptions.queryOptions = undefined const ids = ( await Roles.getUserAssignmentsForRole(roles, assignmentOptions).fetchAsync() From ef0cbbdb171fb47c695ff8211780b3a633e21ea6 Mon Sep 17 00:00:00 2001 From: italo jose Date: Fri, 21 Nov 2025 12:35:27 -0300 Subject: [PATCH 023/166] Remove redundant await when returning promises Remove unnecessary `await` keywords in several server methods and test helpers to avoid extra microtask scheduling and simplify code: - packages/accounts-2fa/2fa-server.js: has2faEnabled - packages/accounts-base/accounts_server.js: login/attempt/token helper returns - packages/accounts-base/accounts_tests_setup.js: forceEnableUser2fa return - packages/accounts-password/email_tests_setup.js: createUserOnServer return --- packages/accounts-2fa/2fa-server.js | 2 +- packages/accounts-base/accounts_server.js | 6 +++--- packages/accounts-base/accounts_tests_setup.js | 2 +- packages/accounts-password/email_tests_setup.js | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/accounts-2fa/2fa-server.js b/packages/accounts-2fa/2fa-server.js index 801233d294..673ce77c15 100644 --- a/packages/accounts-2fa/2fa-server.js +++ b/packages/accounts-2fa/2fa-server.js @@ -123,7 +123,7 @@ Meteor.methods({ ); }, async has2faEnabled() { - return await Accounts._is2faEnabledForUser(); + return Accounts._is2faEnabledForUser(); }, }); diff --git a/packages/accounts-base/accounts_server.js b/packages/accounts-base/accounts_server.js index 64f0975579..4d8a0c3d5f 100644 --- a/packages/accounts-base/accounts_server.js +++ b/packages/accounts-base/accounts_server.js @@ -531,7 +531,7 @@ export class AccountsServer extends AccountsCommon { type, fn ) { - return await this._attemptLogin( + return this._attemptLogin( methodInvocation, methodName, methodArgs, @@ -678,7 +678,7 @@ export class AccountsServer extends AccountsCommon { const result = await accounts._runLoginHandlers(this, options); //console.log({result}); - return await accounts._attemptLogin(this, "login", arguments, result); + return accounts._attemptLogin(this, "login", arguments, result); }; methods.logout = async function () { @@ -731,7 +731,7 @@ export class AccountsServer extends AccountsCommon { const newStampedToken = accounts._generateStampedLoginToken(); newStampedToken.when = currentStampedToken.when; await accounts._insertLoginToken(this.userId, newStampedToken); - return await accounts._loginUser(this, this.userId, newStampedToken); + return accounts._loginUser(this, this.userId, newStampedToken); }; // Removes all tokens except the token associated with the current diff --git a/packages/accounts-base/accounts_tests_setup.js b/packages/accounts-base/accounts_tests_setup.js index 0e211828c0..7f8cf2a40d 100644 --- a/packages/accounts-base/accounts_tests_setup.js +++ b/packages/accounts-base/accounts_tests_setup.js @@ -32,7 +32,7 @@ Meteor.methods({ }, } ); - return await getTokenFromSecret({ selector, secret }); + return getTokenFromSecret({ selector, secret }); }, getTokenFromSecret, }); diff --git a/packages/accounts-password/email_tests_setup.js b/packages/accounts-password/email_tests_setup.js index fe393fb663..577f4555fb 100644 --- a/packages/accounts-password/email_tests_setup.js +++ b/packages/accounts-password/email_tests_setup.js @@ -55,7 +55,7 @@ Meteor.methods( check(email, String); const userId = await Accounts.createUser({ email }); await Accounts.sendEnrollmentEmail(userId); - return await Meteor.users.findOneAsync(userId); + return Meteor.users.findOneAsync(userId); } } ); From 75bc1e8d5eaf449104c4ffd362b03d27e6d9e3fd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Dec 2025 16:35:34 -0300 Subject: [PATCH 024/166] Merge pull request #13784 from meteor/dependabot/github_actions/actions/github-script-7 Bump actions/github-script from 6 to 7 --- .github/workflows/inactive-issues.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/inactive-issues.yml b/.github/workflows/inactive-issues.yml index 2d8cba7a3f..1ea6d88be6 100644 --- a/.github/workflows/inactive-issues.yml +++ b/.github/workflows/inactive-issues.yml @@ -17,7 +17,7 @@ jobs: uses: actions/checkout@v3 - name: Manage inactive issues - uses: actions/github-script@v6 + uses: actions/github-script@v7 with: script: | const script = require('./.github/scripts/inactive-issues.js') From 5fd859bb8826e9db6d34cb171a6d33695da7c075 Mon Sep 17 00:00:00 2001 From: italo jose Date: Fri, 5 Dec 2025 06:53:26 -0300 Subject: [PATCH 025/166] socket file tests: fail when no writable group could be determined instead of no-op --- packages/webapp/socket_file_tests.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/webapp/socket_file_tests.js b/packages/webapp/socket_file_tests.js index 143cb8ca4d..856fe7329e 100644 --- a/packages/webapp/socket_file_tests.js +++ b/packages/webapp/socket_file_tests.js @@ -178,7 +178,8 @@ testAsyncMulti( if (!groupToUse) { // Skip when no writable group could be determined for the current user. - test.isTrue(true); + // test.isTrue(true); + test.fail(`fail test: no writable group could be determined for the current user.`); return; } From 7930ecfd6fdb831818146a3527960016a7c85126 Mon Sep 17 00:00:00 2001 From: italo jose Date: Fri, 5 Dec 2025 09:17:13 -0300 Subject: [PATCH 026/166] remove this commit --- packages/mongo/tests/observe_changes_tests.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mongo/tests/observe_changes_tests.js b/packages/mongo/tests/observe_changes_tests.js index 8dd50eed5d..ac7a671cac 100644 --- a/packages/mongo/tests/observe_changes_tests.js +++ b/packages/mongo/tests/observe_changes_tests.js @@ -519,8 +519,8 @@ if (Meteor.isServer) { const [resolver1, promise1] = getPromiseAndResolver(); const [resolver2, promise2] = getPromiseAndResolver(); - await self.insert({x: 2, y: 3}); self.expects.push(resolver1, resolver2); + await self.insert({x: 2, y: 3}); await self.insert({x: 3, y: 7}); // filtered out by the query await self.insert({x: 4}); // Expect two added calls to happen. From ee3dfb66e273a3cc67520d331185bbed637c3a1b Mon Sep 17 00:00:00 2001 From: italo jose Date: Mon, 8 Dec 2025 17:00:44 -0300 Subject: [PATCH 027/166] test-packages: run workflow on pull_request and rename to "Package tests" --- .github/workflows/test-packages.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test-packages.yml b/.github/workflows/test-packages.yml index 33b828ce45..29b8c48646 100644 --- a/.github/workflows/test-packages.yml +++ b/.github/workflows/test-packages.yml @@ -1,9 +1,7 @@ -name: Meteor test packages +name: Package tests on: - push: - branches: - - ci/removing-travis + pull_request: jobs: test-packages: From 86be8517db9fcb9e5ca56c0be3115cec224aaeb3 Mon Sep 17 00:00:00 2001 From: zodern Date: Wed, 7 Feb 2024 11:01:57 -0600 Subject: [PATCH 028/166] Fix error generating xunit output --- packages/test-in-console/driver.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/test-in-console/driver.js b/packages/test-in-console/driver.js index 4f66667d0d..a4533f1256 100644 --- a/packages/test-in-console/driver.js +++ b/packages/test-in-console/driver.js @@ -210,7 +210,9 @@ runTests = function () { // Also log xUnit output xunit(''); - resultSet.forEach(function (result, name) { + Object.keys(resultSet).forEach(function (name) { + let result = resultSet[name]; + var classname = result.testPath.join('.').replace(/ /g, '-') + (result.server ? "-server" : "-client"); var name = result.test.replace(/ /g, '-') + (result.server ? "-server" : "-client"); var time = ""; From c93b6ff7022c6e5855f26740ca83ad34dccd25a7 Mon Sep 17 00:00:00 2001 From: carraes Date: Tue, 7 Oct 2025 22:06:36 -0300 Subject: [PATCH 029/166] feat: add support for dots in minimongo key names Minimongo now allows dots (.) in MongoDB key names, aligning with modern MongoDB support. Fixes #13552 --- packages/minimongo/local_collection.js | 7 +- packages/minimongo/minimongo_tests_client.js | 75 +++++++++++++++----- 2 files changed, 62 insertions(+), 20 deletions(-) diff --git a/packages/minimongo/local_collection.js b/packages/minimongo/local_collection.js index e9f1473867..a532337674 100644 --- a/packages/minimongo/local_collection.js +++ b/packages/minimongo/local_collection.js @@ -2293,11 +2293,12 @@ const NO_CREATE_MODIFIERS = { }; // Make sure field names do not contain Mongo restricted -// characters ('.', '$', '\0'). +// characters ('$', '\0') or invalid dot usage (leading/trailing/consecutive '.'). // https://docs.mongodb.com/manual/reference/limits/#Restrictions-on-Field-Names const invalidCharMsg = { $: 'start with \'$\'', - '.': 'contain \'.\'', + '.': 'start or end with \'.\'', + '..': 'contain consecutive dots', '\0': 'contain null bytes' }; @@ -2313,7 +2314,7 @@ function assertHasValidFieldNames(doc) { function assertIsValidFieldName(key) { let match; - if (typeof key === 'string' && (match = key.match(/^\$|\.|\0/))) { + if (typeof key === 'string' && (match = key.match(/^\$|^\.|\.\.|\.$|^\.$|\0/))) { throw MinimongoError(`Key ${key} must not ${invalidCharMsg[match[0]]}`); } } diff --git a/packages/minimongo/minimongo_tests_client.js b/packages/minimongo/minimongo_tests_client.js index c1405552a5..a0c5525bf6 100644 --- a/packages/minimongo/minimongo_tests_client.js +++ b/packages/minimongo/minimongo_tests_client.js @@ -2496,12 +2496,13 @@ Tinytest.addAsync('minimongo - modify', async test => { await modify({a: 12}, {}, {}); // tested against mongodb await modify({a: 12}, {a: 13}, {a: 13}); await modify({a: 12, b: 99}, {a: 13}, {a: 13}); + await modify({a: 12}, {b: {'a.b': 13}}, {b: {'a.b': 13}}); + await modify({_id: 1, a: 1}, {_id: 1, 'a.b': 2}, {_id: 1, 'a.b': 2}); await exception({a: 12}, {a: 13, $set: {b: 13}}); await exception({a: 12}, {$set: {b: 13}, a: 13}); await exception({a: 12}, {$a: 13}); // invalid operator await exception({a: 12}, {b: {$a: 13}}); - await exception({a: 12}, {b: {'a.b': 13}}); await exception({a: 12}, {b: {'\0a': 13}}); // keys @@ -2740,11 +2741,11 @@ Tinytest.addAsync('minimongo - modify', async test => { await exception({_id: 1}, {$set: {_id: 4}}); await modify({_id: 4}, {$set: {_id: 4}}, {_id: 4}); // not-changing _id is not bad // restricted field names + await modify({a: {}}, {$set: {a: {'a.b': 1}}}, {a: {'a.b': 1}}); await exception({a: {}}, {$set: {a: {$a: 1}}}); await exception({ a: {} }, { $set: { a: { c: [{ b: { $a: 1 } }] } } }); await exception({a: {}}, {$set: {a: {'\0a': 1}}}); - await exception({a: {}}, {$set: {a: {'a.b': 1}}}); // $unset await modify({}, {$unset: {a: 1}}, {}); @@ -2822,8 +2823,8 @@ Tinytest.addAsync('minimongo - modify', async test => { await exception({}, {$push: {'\0a': 1}}); await exception({}, {$push: {a: {$a: 1}}}); await exception({}, {$push: {a: {$each: [{$a: 1}]}}}); - await exception({}, {$push: {a: {$each: [{'a.b': 1}]}}}); await exception({}, {$push: {a: {$each: [{'\0a': 1}]}}}); + await modify({}, {$push: {a: {$each: [{'a.b': 1}]}}}, {a: [{'a.b': 1}]}); await modify({}, {$push: {a: {$each: [{'': 1}]}}}, {a: [ { '': 1 } ]}); await modify({}, {$push: {a: {$each: [{' ': 1}]}}}, {a: [ { ' ': 1 } ]}); await exception({}, {$push: {a: {$each: [{'.': 1}]}}}); @@ -2857,7 +2858,7 @@ Tinytest.addAsync('minimongo - modify', async test => { await modify({a: {}}, {$pushAll: {'a.x': []}}, {a: {x: []}}); await exception({a: [1]}, {$pushAll: {a: [{$a: 1}]}}); await exception({a: [1]}, {$pushAll: {a: [{'\0a': 1}]}}); - await exception({a: [1]}, {$pushAll: {a: [{'a.b': 1}]}}); + await modify({a: [1]}, {$pushAll: {a: [{'a.b': 1}]}}, {a: [1, {'a.b': 1}]}); // $addToSet await modify({}, {$addToSet: {a: 1}}, {a: [1]}); @@ -2883,14 +2884,16 @@ Tinytest.addAsync('minimongo - modify', async test => { // invalid field names await exception({}, {$addToSet: {a: {$b: 1}}}); - await exception({}, {$addToSet: {a: {'a.b': 1}}}); + await modify({}, {$addToSet: {a: {'a.b': 1}}}, {a: [{'a.b': 1}]}); await exception({}, {$addToSet: {a: {'a.': 1}}}); await exception({}, {$addToSet: {a: {'\u0000a': 1}}}); await exception({a: [1, 2]}, {$addToSet: {a: {$each: [3, 1, {$a: 1}]}}}); await exception({a: [1, 2]}, {$addToSet: {a: {$each: [3, 1, {'\0a': 1}]}}}); await exception({a: [1, 2]}, {$addToSet: {a: {$each: [3, 1, [{$a: 1}]]}}}); - await exception({a: [1, 2]}, {$addToSet: {a: {$each: [3, 1, [{b: {c: [{a: 1}, {'d.s': 2}]}}]]}}}); - await exception({a: [1, 2]}, {$addToSet: {a: {b: [3, 1, [{b: {c: [{a: 1}, {'d.s': 2}]}}]]}}}); + await modify({a: [1, 2]}, {$addToSet: {a: {$each: [3, 1, [{b: {c: [{a: 1}, {'d.s': 2}]}}]]}}}, + {a: [1, 2, 3, [{b: {c: [{a: 1}, {'d.s': 2}]}}]]}); + await modify({a: [1, 2]}, {$addToSet: {a: {b: [3, 1, [{b: {c: [{a: 1}, {'d.s': 2}]}}]]}}}, + {a: [1, 2, {b: [3, 1, [{b: {c: [{a: 1}, {'d.s': 2}]}}]]}]}); // $each is first element and thus an operator await modify({a: [1, 2]}, {$addToSet: {a: {$each: [3, 1, 4], b: 12}}}, {a: [ 1, 2, 3, 4 ]}); // this should fail because $each is now a field name (not first in object) and thus invalid field name with $ @@ -2983,7 +2986,7 @@ Tinytest.addAsync('minimongo - modify', async test => { await upsertException({a: 0}, {$setOnInsert: {'\0a': 12}}); await upsert({a: 0}, {$setOnInsert: {b: {a: 1}}}, {a: 0, b: {a: 1}}); await upsertException({a: 0}, {$setOnInsert: {b: {$a: 1}}}); - await upsertException({a: 0}, {$setOnInsert: {b: {'a.b': 1}}}); + await upsert({a: 0}, {$setOnInsert: {b: {'a.b': 1}}}, {a: 0, b: {'a.b': 1}}); await upsertException({a: 0}, {$setOnInsert: {b: {'\0a': 1}}}); // Test for https://github.com/meteor/meteor/issues/8775. @@ -3919,7 +3922,7 @@ Tinytest.add('minimongo - reactive skip/limit count while updating', test => { }); // Makes sure inserts cannot be performed using field names that have -// Mongo restricted characters in them ('.', '$', '\0'): +// Mongo restricted characters in them ('$', '\0'): // https://docs.mongodb.com/manual/reference/limits/#Restrictions-on-Field-Names Tinytest.add('minimongo - cannot insert using invalid field names', test => { const collection = new LocalCollection(); @@ -3930,18 +3933,13 @@ Tinytest.add('minimongo - cannot insert using invalid field names', test => { // Quick test to make sure field values with dots are allowed collection.insert({ a: 'b.c' }); - // Verify top level dot-field inserts are prohibited - ['a.b', '.b', 'a.', 'a.b.c'].forEach((field) => { + // Verify invalid dot patterns are rejected: leading dot, trailing dot, consecutive dots + ['.b', 'a.', '.', 'a..b', '. ', ' .', '...', 'a...b'].forEach((field) => { test.throws(() => { collection.insert({ [field]: 'c' }); - }, `Key ${field} must not contain '.'`); + }, `Key ${field}`); }); - // Verify nested dot-field inserts are prohibited - test.throws(() => { - collection.insert({ a: { b: { 'c.d': 'e' } } }); - }, "Key c.d must not contain '.'"); - // Verify field names starting with $ are prohibited test.throws(() => { collection.insert({ $a: 'b' }); @@ -3965,6 +3963,49 @@ Tinytest.add('minimongo - cannot insert using invalid field names', test => { }, 'Key \0c must not contain null bytes'); }); +// Verify that valid dotted field names are allowed (MongoDB 3.6+) +Tinytest.add('minimongo - can insert using valid dotted field names', test => { + const collection = new LocalCollection(); + + // Verify dotted field names work + ['a.b', 'a.b.c'].forEach((field) => { + const id = collection.insert({ [field]: 'd' }); + const doc = collection.findOne(id); + test.equal(doc[field], 'd', `Field ${field} should be allowed`); + collection.remove(id); + }); + + // Verify dotted fields in nested objects work + const id2 = collection.insert({ + nested: { + 'a.b': 'c', + 'a.b.c': 'd' + } + }); + const doc2 = collection.findOne(id2); + test.equal(doc2.nested['a.b'], 'c'); + test.equal(doc2.nested['a.b.c'], 'd'); + + // Verify update operations work with dotted field names in values + const id3 = collection.insert({ a: 'b' }); + collection.update(id3, { $set: { b: { 'a.b': 'c' } } }); + const doc3 = collection.findOne(id3); + test.equal(doc3.b['a.b'], 'c'); + + // Verify distinction: path semantics vs literal dotted keys + const id4 = collection.insert({ x: {} }); + // This uses path semantics - creates nested structure + collection.update(id4, { $set: { 'x.y': 1 } }); + const doc4a = collection.findOne(id4); + test.equal(doc4a.x.y, 1, 'Path semantics should create nested structure'); + + // This uses literal key - dot is part of the key name + collection.update(id4, { $set: { z: { 'x.y': 2 } } }); + const doc4b = collection.findOne(id4); + test.equal(doc4b.z['x.y'], 2, 'Literal key should store dot as part of name'); + test.equal(doc4b.z.x, undefined, 'Literal key should not create nesting'); +}); + // Makes sure $set's cannot be performed using null bytes // https://docs.mongodb.com/manual/reference/limits/#Restrictions-on-Field-Names Tinytest.add('minimongo - cannot $set with null bytes', test => { From 7db44d90d1ad7414c6efe8ab8c532ada5e7db3bc Mon Sep 17 00:00:00 2001 From: mjaruzel Date: Mon, 27 May 2024 15:39:54 +0200 Subject: [PATCH 030/166] fix: displaying google maps in mobile application --- packages/ecmascript-runtime-client/legacy.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/ecmascript-runtime-client/legacy.js b/packages/ecmascript-runtime-client/legacy.js index 20a6d8fb3a..3d482c5438 100644 --- a/packages/ecmascript-runtime-client/legacy.js +++ b/packages/ecmascript-runtime-client/legacy.js @@ -1,8 +1,7 @@ try { - Symbol = exports.Symbol = require("core-js/es/symbol"); - Map = exports.Map = require("core-js/es/map"); - Set = exports.Set = require("core-js/es/set"); - + Symbol = exports.Symbol = global.Symbol || require("core-js/es/symbol"); + Map = exports.Map = global.Map || require("core-js/es/map"); + Set = exports.Set = global.Set || require("core-js/es/set"); } catch (e) { throw new Error([ "The core-js npm package could not be found in your node_modules ", From d96657d34359368ae0f0210aa8378936e3feab50 Mon Sep 17 00:00:00 2001 From: Gabriel Grubba Date: Mon, 8 Dec 2025 11:00:22 -0300 Subject: [PATCH 031/166] FEATURE: Remove `import React from 'react';` from skels With our SWC changes, we can both remove this import line and add an example of swc configuration --- tools/static-assets/skel-react/.swcrc | 9 +++++++++ tools/static-assets/skel-react/client/main.jsx | 1 - tools/static-assets/skel-react/imports/ui/App.jsx | 1 - tools/static-assets/skel-react/imports/ui/Hello.jsx | 2 +- tools/static-assets/skel-react/imports/ui/Info.jsx | 1 - tools/static-assets/skel-typescript/.swcrc | 9 +++++++++ tools/static-assets/skel-typescript/client/main.tsx | 1 - tools/static-assets/skel-typescript/imports/ui/App.tsx | 1 - tools/static-assets/skel-typescript/imports/ui/Hello.tsx | 2 +- tools/static-assets/skel-typescript/imports/ui/Info.tsx | 1 - 10 files changed, 20 insertions(+), 8 deletions(-) create mode 100644 tools/static-assets/skel-react/.swcrc create mode 100644 tools/static-assets/skel-typescript/.swcrc diff --git a/tools/static-assets/skel-react/.swcrc b/tools/static-assets/skel-react/.swcrc new file mode 100644 index 0000000000..e0a05f1803 --- /dev/null +++ b/tools/static-assets/skel-react/.swcrc @@ -0,0 +1,9 @@ +{ + "jsc": { + "transform": { + "react": { + "runtime": "automatic" + } + } + } +} diff --git a/tools/static-assets/skel-react/client/main.jsx b/tools/static-assets/skel-react/client/main.jsx index d2e380f93c..c7e0c1f05e 100644 --- a/tools/static-assets/skel-react/client/main.jsx +++ b/tools/static-assets/skel-react/client/main.jsx @@ -1,4 +1,3 @@ -import React from 'react'; import { createRoot } from 'react-dom/client'; import { Meteor } from 'meteor/meteor'; import { App } from '/imports/ui/App'; diff --git a/tools/static-assets/skel-react/imports/ui/App.jsx b/tools/static-assets/skel-react/imports/ui/App.jsx index 6f7340caf9..eeeee2161a 100644 --- a/tools/static-assets/skel-react/imports/ui/App.jsx +++ b/tools/static-assets/skel-react/imports/ui/App.jsx @@ -1,4 +1,3 @@ -import React from 'react'; import { Hello } from './Hello.jsx'; import { Info } from './Info.jsx'; diff --git a/tools/static-assets/skel-react/imports/ui/Hello.jsx b/tools/static-assets/skel-react/imports/ui/Hello.jsx index 15e0f185ac..527d5af607 100644 --- a/tools/static-assets/skel-react/imports/ui/Hello.jsx +++ b/tools/static-assets/skel-react/imports/ui/Hello.jsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import { useState } from 'react'; export const Hello = () => { const [counter, setCounter] = useState(0); diff --git a/tools/static-assets/skel-react/imports/ui/Info.jsx b/tools/static-assets/skel-react/imports/ui/Info.jsx index a9a7a45cfe..7154a4776d 100644 --- a/tools/static-assets/skel-react/imports/ui/Info.jsx +++ b/tools/static-assets/skel-react/imports/ui/Info.jsx @@ -1,4 +1,3 @@ -import React from 'react'; import { useFind, useSubscribe } from 'meteor/react-meteor-data'; import { LinksCollection } from '../api/links'; diff --git a/tools/static-assets/skel-typescript/.swcrc b/tools/static-assets/skel-typescript/.swcrc new file mode 100644 index 0000000000..bbc8887edb --- /dev/null +++ b/tools/static-assets/skel-typescript/.swcrc @@ -0,0 +1,9 @@ +{ + "jsc": { + "transform": { + "react": { + "runtime": "automatic" + } + } + } +} \ No newline at end of file diff --git a/tools/static-assets/skel-typescript/client/main.tsx b/tools/static-assets/skel-typescript/client/main.tsx index 523141b528..57653a16f7 100644 --- a/tools/static-assets/skel-typescript/client/main.tsx +++ b/tools/static-assets/skel-typescript/client/main.tsx @@ -1,4 +1,3 @@ -import React from 'react'; import { createRoot } from 'react-dom/client'; import { Meteor } from 'meteor/meteor'; import { App } from '/imports/ui/App'; diff --git a/tools/static-assets/skel-typescript/imports/ui/App.tsx b/tools/static-assets/skel-typescript/imports/ui/App.tsx index d354e1b352..5956933766 100644 --- a/tools/static-assets/skel-typescript/imports/ui/App.tsx +++ b/tools/static-assets/skel-typescript/imports/ui/App.tsx @@ -1,4 +1,3 @@ -import React from 'react'; import { Hello } from './Hello'; import { Info } from './Info'; diff --git a/tools/static-assets/skel-typescript/imports/ui/Hello.tsx b/tools/static-assets/skel-typescript/imports/ui/Hello.tsx index 15e0f185ac..527d5af607 100644 --- a/tools/static-assets/skel-typescript/imports/ui/Hello.tsx +++ b/tools/static-assets/skel-typescript/imports/ui/Hello.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import { useState } from 'react'; export const Hello = () => { const [counter, setCounter] = useState(0); diff --git a/tools/static-assets/skel-typescript/imports/ui/Info.tsx b/tools/static-assets/skel-typescript/imports/ui/Info.tsx index 23cb8f07a3..809fbc6716 100644 --- a/tools/static-assets/skel-typescript/imports/ui/Info.tsx +++ b/tools/static-assets/skel-typescript/imports/ui/Info.tsx @@ -1,4 +1,3 @@ -import React from "react"; import { useFind, useSubscribe } from "meteor/react-meteor-data"; import { LinksCollection, Link } from "../api/links"; From d7afa91df3a4c2a9a5fbbd82a96f8135e8cef6da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Mon, 1 Dec 2025 17:35:45 +0100 Subject: [PATCH 032/166] update caching strategy in meteor-selftest-windows workflow --- .github/workflows/meteor-selftest-windows.yml | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/.github/workflows/meteor-selftest-windows.yml b/.github/workflows/meteor-selftest-windows.yml index ccde1a2cb1..72476c2055 100644 --- a/.github/workflows/meteor-selftest-windows.yml +++ b/.github/workflows/meteor-selftest-windows.yml @@ -40,6 +40,21 @@ jobs: with: node-version: 22.x + - name: Cache dependencies + uses: actions/cache@v4 + with: + path: | + dev_bundle + .babel-cache + .meteor + ~\.npm + node_modules + tools\modern-tests\node_modules + packages\**\.npm + key: ${{ runner.os }}-meteor-${{ hashFiles('**/package-lock.json', 'meteor') }} + restore-keys: | + ${{ runner.os }}-meteor- + - name: Install dependencies shell: pwsh run: | @@ -51,12 +66,3 @@ jobs: run: | $env:PATH = "C:\Program Files\7-Zip;$env:PATH" .\scripts\windows\ci\test.ps1 - - - name: Cache dependencies - uses: actions/cache@v4 - with: - path: | - .\dev_bundle - .\.babel-cache - .\.meteor - key: ${{ runner.os }}-meteor-${{ hashFiles('**/package-lock.json') }} From a24a361be566c36a985a0508e2244a8989d4cb93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 2 Dec 2025 10:24:11 +0100 Subject: [PATCH 033/166] use Windows tar instead of Git tar in workflow --- .github/workflows/meteor-selftest-windows.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/meteor-selftest-windows.yml b/.github/workflows/meteor-selftest-windows.yml index 72476c2055..12d21805d0 100644 --- a/.github/workflows/meteor-selftest-windows.yml +++ b/.github/workflows/meteor-selftest-windows.yml @@ -40,6 +40,11 @@ jobs: with: node-version: 22.x + - name: Prefer Windows tar over Git tar + shell: pwsh + run: | + "C:\Windows\System32" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + - name: Cache dependencies uses: actions/cache@v4 with: From 526019f0bc2f7daaed4d13c168581c68b2f4d487 Mon Sep 17 00:00:00 2001 From: Frederico Maia Date: Wed, 20 Nov 2024 12:38:15 -0300 Subject: [PATCH 034/166] Fix CLI and ecmascript links and add leonardoventurini:scss as the recommended scss package. --- guide/source/build-tool.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/guide/source/build-tool.md b/guide/source/build-tool.md index 46820815bd..1bb7d892f3 100644 --- a/guide/source/build-tool.md +++ b/guide/source/build-tool.md @@ -4,7 +4,7 @@ description: How to use Meteor's build system to compile your app. discourseTopicId: 19669 --- -The Meteor build system is the actual command line tool that you get when you install Meteor. You run it by typing the `meteor` command in your terminal, possibly followed by a set of arguments. Read the [docs about the command line tool](https://docs.meteor.com/commandline.html) or type `meteor help` in your terminal to learn about all of the commands. +The Meteor build system is the actual command line tool that you get when you install Meteor. You run it by typing the `meteor` command in your terminal, possibly followed by a set of arguments. Read the [docs about the command line tool](https://docs.meteor.com/cli/) or type `meteor help` in your terminal to learn about all of the commands.

What does it do?

@@ -16,7 +16,7 @@ After executing the `meteor` command to start the build tool you should leave it

Compiles files with build plugins

-The main function of the Meteor build tool is to run "build plugins". These plugins define different parts of your app build process. Meteor puts heavy emphasis on reducing or removing build configuration files, so you won't see any large build process config files like you would in Gulp or Webpack. The Meteor build process is configured almost entirely through adding and removing packages to your app and putting files in specially named directories. For example, to get all of the newest stable ES2015 JavaScript features in your app, you add the [`ecmascript` package](http://docs.meteor.com/#/full/ecmascript). This package provides support for ES2015 modules, which gives you even more fine grained control over file load order using ES2015 `import` and `export`. As new Meteor releases add new features to this package you get them for free. +The main function of the Meteor build tool is to run "build plugins". These plugins define different parts of your app build process. Meteor puts heavy emphasis on reducing or removing build configuration files, so you won't see any large build process config files like you would in Gulp or Webpack. The Meteor build process is configured almost entirely through adding and removing packages to your app and putting files in specially named directories. For example, to get all of the newest stable ES2015 JavaScript features in your app, you add the [`ecmascript` package](https://docs.meteor.com/packages/ecmascript.html). This package provides support for ES2015 modules, which gives you even more fine grained control over file load order using ES2015 `import` and `export`. As new Meteor releases add new features to this package you get them for free.

Controlling which files to build

@@ -224,7 +224,7 @@ For more examples and details on importing styles and using `@imports` with pack

Sass

-The best Sass build plugin for Meteor is [`fourseven:scss`](https://atmospherejs.com/fourseven/scss). +The best Sass build plugin for Meteor is [`leonardoventurini:scss`](https://atmospherejs.com/leonardoventurini/scss). An alternative to the previous recommended [`fourseven:scss`](https://atmospherejs.com/fourseven/scss) package.

Less

From f4b7873e3c8801548f4781abb33f290118548343 Mon Sep 17 00:00:00 2001 From: dupontbertrand Date: Tue, 8 Oct 2024 22:13:46 +0200 Subject: [PATCH 035/166] Update docs for upsert in ServiceConfiguration --- v3-docs/docs/api/accounts.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v3-docs/docs/api/accounts.md b/v3-docs/docs/api/accounts.md index 3a1cb7b3c5..233eca7454 100644 --- a/v3-docs/docs/api/accounts.md +++ b/v3-docs/docs/api/accounts.md @@ -296,7 +296,7 @@ Then, inside the server of your app (this example is for the Weebo service), imp ```js import { ServiceConfiguration } from "meteor/service-configuration"; -ServiceConfiguration.configurations.upsert( +ServiceConfiguration.configurations.upsertAsync( { service: "weibo" }, { $set: { From 0d652f4dd62ffff24e1762a0dbf4109388f7968b Mon Sep 17 00:00:00 2001 From: Jacob Gilbert Date: Thu, 24 Oct 2024 01:18:28 -0500 Subject: [PATCH 036/166] Update 3.forms-and-events.md In the instructions to add onSubmit and onChange to TaskForm.jsx there was an unused import for TaskCollections. Removing this had no effect on the useability of the code in this step. --- v3-docs/docs/tutorials/react/3.forms-and-events.md | 1 - 1 file changed, 1 deletion(-) diff --git a/v3-docs/docs/tutorials/react/3.forms-and-events.md b/v3-docs/docs/tutorials/react/3.forms-and-events.md index 8480c006ff..01ab1096f8 100644 --- a/v3-docs/docs/tutorials/react/3.forms-and-events.md +++ b/v3-docs/docs/tutorials/react/3.forms-and-events.md @@ -139,7 +139,6 @@ As you can see you are using the `useState` React Hook to store the `value` of y ```js [imports/ui/TaskForm.jsx] import React, { useState } from "react"; -import { TasksCollection } from "/imports/api/TasksCollection"; export const TaskForm = () => { const [text, setText] = useState(""); From f59201bc68f4dafb27635c62727d97502c4ba739 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Fri, 5 Dec 2025 14:09:54 +0100 Subject: [PATCH 037/166] re-run checks From f8ed28e6be2e0a524e0d7ebaf602f0e264a8f2fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Fri, 5 Dec 2025 15:37:37 +0100 Subject: [PATCH 038/166] re-run checks From df803cd58b411004085c7f73ce5bdaace930d9c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Fri, 5 Dec 2025 17:07:02 +0100 Subject: [PATCH 039/166] clean --- .github/workflows/meteor-selftest-windows.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/meteor-selftest-windows.yml b/.github/workflows/meteor-selftest-windows.yml index 12d21805d0..72476c2055 100644 --- a/.github/workflows/meteor-selftest-windows.yml +++ b/.github/workflows/meteor-selftest-windows.yml @@ -40,11 +40,6 @@ jobs: with: node-version: 22.x - - name: Prefer Windows tar over Git tar - shell: pwsh - run: | - "C:\Windows\System32" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append - - name: Cache dependencies uses: actions/cache@v4 with: From a807cb76138c7f103ee6ae91c91c53441ddbd161 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Fri, 5 Dec 2025 17:14:08 +0100 Subject: [PATCH 040/166] disable submodule update failure check in install.ps1 --- scripts/windows/ci/install.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/windows/ci/install.ps1 b/scripts/windows/ci/install.ps1 index 3624e4c11d..609381dea5 100644 --- a/scripts/windows/ci/install.ps1 +++ b/scripts/windows/ci/install.ps1 @@ -15,9 +15,9 @@ Write-Host "Updating submodules recursively..." -ForegroundColor Magenta # Appveyor suggests -q flag for 'git submodule...' https://goo.gl/4TFAHm & git.exe -C "$dirCheckout" submodule -q update --init --recursive -If ($LASTEXITCODE -ne 0) { - throw "Updating submodules failed." -} +# If ($LASTEXITCODE -ne 0) { +# throw "Updating submodules failed." +# } # The `meteor npm install` subcommand should work & "$meteorBat" npm install From 450679bf661d2a58ed0b69ccff42b798d436e31c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Mon, 8 Dec 2025 12:22:57 +0100 Subject: [PATCH 041/166] temp disable clean to verify cache speed --- .github/workflows/meteor-selftest-windows.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/meteor-selftest-windows.yml b/.github/workflows/meteor-selftest-windows.yml index 72476c2055..480272df30 100644 --- a/.github/workflows/meteor-selftest-windows.yml +++ b/.github/workflows/meteor-selftest-windows.yml @@ -28,10 +28,6 @@ jobs: cancel-in-progress: true steps: - - name: cleanup - shell: powershell - run: Remove-Item -Recurse -Force ${{ github.workspace }}\* - - name: Checkout code uses: actions/checkout@v4 From 9309790b24632b47de485fdc272cab674dbea98f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Mon, 8 Dec 2025 12:43:40 +0100 Subject: [PATCH 042/166] simplify cache paths in Windows self-test workflow --- .github/workflows/meteor-selftest-windows.yml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/.github/workflows/meteor-selftest-windows.yml b/.github/workflows/meteor-selftest-windows.yml index 480272df30..139d0fb9b4 100644 --- a/.github/workflows/meteor-selftest-windows.yml +++ b/.github/workflows/meteor-selftest-windows.yml @@ -40,13 +40,12 @@ jobs: uses: actions/cache@v4 with: path: | - dev_bundle - .babel-cache - .meteor - ~\.npm + dev_bundle/ + .babel-cache/ + .meteor/ + ~/.npm node_modules - tools\modern-tests\node_modules - packages\**\.npm + packages/**/.npm key: ${{ runner.os }}-meteor-${{ hashFiles('**/package-lock.json', 'meteor') }} restore-keys: | ${{ runner.os }}-meteor- From c9086753a1fe7e658211a4d8d63ab93af74c8977 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Mon, 8 Dec 2025 12:47:35 +0100 Subject: [PATCH 043/166] simplify cache paths in Windows self-test workflow --- .github/workflows/meteor-selftest-windows.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/meteor-selftest-windows.yml b/.github/workflows/meteor-selftest-windows.yml index 139d0fb9b4..b21824fdd5 100644 --- a/.github/workflows/meteor-selftest-windows.yml +++ b/.github/workflows/meteor-selftest-windows.yml @@ -44,7 +44,7 @@ jobs: .babel-cache/ .meteor/ ~/.npm - node_modules + node_modules/ packages/**/.npm key: ${{ runner.os }}-meteor-${{ hashFiles('**/package-lock.json', 'meteor') }} restore-keys: | From fef3b65db5b568a07978d3653044732b396f02ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Mon, 8 Dec 2025 15:50:48 +0100 Subject: [PATCH 044/166] skip temporary --get-ready --- scripts/windows/ci/install.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/windows/ci/install.ps1 b/scripts/windows/ci/install.ps1 index 609381dea5..0b58a341b7 100644 --- a/scripts/windows/ci/install.ps1 +++ b/scripts/windows/ci/install.ps1 @@ -35,7 +35,7 @@ while ($attempt -gt 0 -and -not $success) { # By redirecting error to host, we avoid a shocking/false error color, # since --get-ready and --version can print (anything) to STDERR and # PowerShell will interpret that as something being terribly wrong. - & "$meteorBat" --get-ready + # & "$meteorBat" --get-ready If ($LASTEXITCODE -eq 0) { $success = $true From ce872db2a5ff60ad78a2ddc752bba7c9ce243350 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 9 Dec 2025 10:21:20 +0100 Subject: [PATCH 045/166] refactor CI install script to adapt for CI environment --- scripts/windows/ci/install.ps1 | 45 ++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/scripts/windows/ci/install.ps1 b/scripts/windows/ci/install.ps1 index 0b58a341b7..07cccb0c50 100644 --- a/scripts/windows/ci/install.ps1 +++ b/scripts/windows/ci/install.ps1 @@ -4,6 +4,9 @@ If ($env:PLATFORM -Match '^x86|x64$') { $env:PLATFORM = "windows_${env:PLATFORM}" } +# Check if we're running in a CI environment +$isCI = $env:GITHUB_ACTIONS -eq "true" + $dirCheckout = (Get-Item $PSScriptRoot).parent.parent.parent.FullName $meteorBat = Join-Path $dirCheckout 'meteor.bat' @@ -15,9 +18,10 @@ Write-Host "Updating submodules recursively..." -ForegroundColor Magenta # Appveyor suggests -q flag for 'git submodule...' https://goo.gl/4TFAHm & git.exe -C "$dirCheckout" submodule -q update --init --recursive -# If ($LASTEXITCODE -ne 0) { -# throw "Updating submodules failed." -# } +# Only throw locally +If ($LASTEXITCODE -ne 0 -and -not $isCI) { + throw "Updating submodules failed." +} # The `meteor npm install` subcommand should work & "$meteorBat" npm install @@ -25,25 +29,28 @@ If ($LASTEXITCODE -ne 0) { throw "'meteor npm install' failed." } +# Only `meteor --get-ready` get-ready locally to have better control on CI # The `meteor --get-ready` command is susceptible to EPERM errors, so # we attempt it three times. -$attempt = 3 -$success = $false -while ($attempt -gt 0 -and -not $success) { +If (-not $isCI) { + $attempt = 3 + $success = $false + while ($attempt -gt 0 -and -not $success) { - Write-Host "Running 'meteor --get-ready'..." -ForegroundColor Magenta - # By redirecting error to host, we avoid a shocking/false error color, - # since --get-ready and --version can print (anything) to STDERR and - # PowerShell will interpret that as something being terribly wrong. - # & "$meteorBat" --get-ready + Write-Host "Running 'meteor --get-ready'..." -ForegroundColor Magenta + # By redirecting error to host, we avoid a shocking/false error color, + # since --get-ready and --version can print (anything) to STDERR and + # PowerShell will interpret that as something being terribly wrong. + & "$meteorBat" --get-ready - If ($LASTEXITCODE -eq 0) { - $success = $true - } else { - $attempt-- + If ($LASTEXITCODE -eq 0) { + $success = $true + } else { + $attempt-- + } + } + + If ($LASTEXITCODE -ne 0) { + throw "Running .\meteor --get-ready failed three times." } } - -If ($LASTEXITCODE -ne 0) { - throw "Running .\meteor --get-ready failed three times." -} From 3f9e31ddacfbd2b637944dfc37029cf99d0708a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 9 Dec 2025 10:21:31 +0100 Subject: [PATCH 046/166] update workflow name to 'Windows Selftest' --- .github/workflows/meteor-selftest-windows.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/meteor-selftest-windows.yml b/.github/workflows/meteor-selftest-windows.yml index b21824fdd5..f69bf252bc 100644 --- a/.github/workflows/meteor-selftest-windows.yml +++ b/.github/workflows/meteor-selftest-windows.yml @@ -1,4 +1,4 @@ -name: Meteor Selftest Windows +name: Windows Selftest on: pull_request: From e45365096f2890b0a07b4dd867408620483187ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 9 Dec 2025 11:04:22 +0100 Subject: [PATCH 047/166] refine Windows self-test workflow configuration --- .github/workflows/meteor-selftest-windows.yml | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/.github/workflows/meteor-selftest-windows.yml b/.github/workflows/meteor-selftest-windows.yml index f69bf252bc..4f5f000fdf 100644 --- a/.github/workflows/meteor-selftest-windows.yml +++ b/.github/workflows/meteor-selftest-windows.yml @@ -6,10 +6,20 @@ on: - opened - reopened - synchronize + paths: + - 'meteor' + - 'meteor.bat' + - 'tools/**' + - 'packages/babel-compiler/**' + - 'packages/dynamic-import/**' + - 'packages/meteor/**' + - 'packages/meteor-tool/**' + - '.github/workflows/windows-selftest.yml' + push: branches: - devel - - 2.x.x + - release-** env: METEOR_PRETTY_OUTPUT: 0 @@ -37,6 +47,7 @@ jobs: node-version: 22.x - name: Cache dependencies + id: meteor-cache uses: actions/cache@v4 with: path: | @@ -50,6 +61,14 @@ jobs: restore-keys: | ${{ runner.os }}-meteor- + # 👇 Run ONLY when the cache was NOT restored! + - name: Prepare Meteor (cache miss) + if: steps.meteor-cache.outputs.cache-hit != 'true' + shell: pwsh + run: | + $env:PATH = "C:\Program Files\7-Zip;$env:PATH" + .\meteor.bat --get-ready + - name: Install dependencies shell: pwsh run: | From 65157381a65edcbed57105b29fd8f35ca8b3c87b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 9 Dec 2025 11:21:19 +0100 Subject: [PATCH 048/166] rename workflow file and update path references --- .../{meteor-selftest-windows.yml => windows-selftest.yml} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename .github/workflows/{meteor-selftest-windows.yml => windows-selftest.yml} (97%) diff --git a/.github/workflows/meteor-selftest-windows.yml b/.github/workflows/windows-selftest.yml similarity index 97% rename from .github/workflows/meteor-selftest-windows.yml rename to .github/workflows/windows-selftest.yml index 4f5f000fdf..c40662af77 100644 --- a/.github/workflows/meteor-selftest-windows.yml +++ b/.github/workflows/windows-selftest.yml @@ -14,7 +14,7 @@ on: - 'packages/dynamic-import/**' - 'packages/meteor/**' - 'packages/meteor-tool/**' - - '.github/workflows/windows-selftest.yml' + - 'windows-selftest.yml' push: branches: From bdeffd28a4dff16b694ef809a0fca31719e90753 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 9 Dec 2025 11:23:07 +0100 Subject: [PATCH 049/166] fix path for windows-selftest.yml in workflow triggers --- .github/workflows/windows-selftest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/windows-selftest.yml b/.github/workflows/windows-selftest.yml index c40662af77..4f5f000fdf 100644 --- a/.github/workflows/windows-selftest.yml +++ b/.github/workflows/windows-selftest.yml @@ -14,7 +14,7 @@ on: - 'packages/dynamic-import/**' - 'packages/meteor/**' - 'packages/meteor-tool/**' - - 'windows-selftest.yml' + - '.github/workflows/windows-selftest.yml' push: branches: From 8c87b75ac686bc9b34241ba4f8ec6f13324b392f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 9 Dec 2025 11:28:30 +0100 Subject: [PATCH 050/166] update cache key in Windows CI workflow --- .github/workflows/windows-selftest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/windows-selftest.yml b/.github/workflows/windows-selftest.yml index 4f5f000fdf..f2d44258dc 100644 --- a/.github/workflows/windows-selftest.yml +++ b/.github/workflows/windows-selftest.yml @@ -57,7 +57,7 @@ jobs: ~/.npm node_modules/ packages/**/.npm - key: ${{ runner.os }}-meteor-${{ hashFiles('**/package-lock.json', 'meteor') }} + key: ${{ runner.os }}-meteor-${{ hashFiles('**/package-lock.json', 'meteor', 'meteor.bat') }} restore-keys: | ${{ runner.os }}-meteor- From a856fc6e419694b4d2a90420260b7f3a9c6a1057 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 9 Dec 2025 13:53:13 +0100 Subject: [PATCH 051/166] refactor dependency installation steps in Windows CI workflow --- .github/workflows/windows-selftest.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/windows-selftest.yml b/.github/workflows/windows-selftest.yml index f2d44258dc..5e6436d9d8 100644 --- a/.github/workflows/windows-selftest.yml +++ b/.github/workflows/windows-selftest.yml @@ -61,7 +61,13 @@ jobs: restore-keys: | ${{ runner.os }}-meteor- - # 👇 Run ONLY when the cache was NOT restored! + - name: Install dependencies + shell: pwsh + run: | + $env:PATH = "C:\Program Files\7-Zip;$env:PATH" + .\scripts\windows\ci\install.ps1 + + # Run ONLY when the cache was NOT restored - name: Prepare Meteor (cache miss) if: steps.meteor-cache.outputs.cache-hit != 'true' shell: pwsh @@ -69,12 +75,6 @@ jobs: $env:PATH = "C:\Program Files\7-Zip;$env:PATH" .\meteor.bat --get-ready - - name: Install dependencies - shell: pwsh - run: | - $env:PATH = "C:\Program Files\7-Zip;$env:PATH" - .\scripts\windows\ci\install.ps1 - - name: Run tests shell: pwsh run: | From 7faea7aed6b301d1b1283da37ef2723fe3fe656e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 9 Dec 2025 16:23:14 +0100 Subject: [PATCH 052/166] remove wildcard branch trigger from Windows CI workflow --- .github/workflows/windows-selftest.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/windows-selftest.yml b/.github/workflows/windows-selftest.yml index 5e6436d9d8..49b917a0f9 100644 --- a/.github/workflows/windows-selftest.yml +++ b/.github/workflows/windows-selftest.yml @@ -19,7 +19,6 @@ on: push: branches: - devel - - release-** env: METEOR_PRETTY_OUTPUT: 0 From a4a469811da21844274e13250a6f073eec6241ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Wed, 10 Dec 2025 13:52:08 +0100 Subject: [PATCH 053/166] re-run checks From 04439de2b6560c1b861c96138d2227899f57ab45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Wed, 10 Dec 2025 17:07:36 +0100 Subject: [PATCH 054/166] simplify cache key by removing package-lock.json hash --- .github/workflows/windows-selftest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/windows-selftest.yml b/.github/workflows/windows-selftest.yml index 49b917a0f9..7fdfc9e296 100644 --- a/.github/workflows/windows-selftest.yml +++ b/.github/workflows/windows-selftest.yml @@ -56,7 +56,7 @@ jobs: ~/.npm node_modules/ packages/**/.npm - key: ${{ runner.os }}-meteor-${{ hashFiles('**/package-lock.json', 'meteor', 'meteor.bat') }} + key: ${{ runner.os }}-meteor-${{ hashFiles('meteor', 'meteor.bat') }} restore-keys: | ${{ runner.os }}-meteor- From 934cb87dcc624b6fd01eb1ba895f5da5793138a4 Mon Sep 17 00:00:00 2001 From: Graeme Pyle Date: Thu, 30 Nov 2023 12:56:27 +0400 Subject: [PATCH 055/166] Add instructions for enabling back gesture on iOS --- guide/source/cordova.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/guide/source/cordova.md b/guide/source/cordova.md index 0cc34b552b..0d7b1966a8 100644 --- a/guide/source/cordova.md +++ b/guide/source/cordova.md @@ -695,3 +695,9 @@ From this point on, the process for submitting the app to the Play Store is the Because Crosswalk bundles native code for Chromium, you will end up with APKs for both ARM and x86. You can find the generated APKs in the `/android/project/build/outputs/apk` directory. You will have to sign and `zipalign` both APKs. You will also have to submit both to the Play Store, see [submitting multiple APKs](http://developer.android.com/google/play/publishing/multiple-apks.html) for more information. + +

Other tips

+ +The back gesture is disabled by default on iOS, but it can be enabled at runtime like this: + +```window.WkWebView.allowsBackForwardNavigationGestures(true);``` From 4d2520f5d569060235a89d4c4dd8023ddb08b43a Mon Sep 17 00:00:00 2001 From: Vlad Lasky Date: Fri, 12 Dec 2025 12:12:13 +1100 Subject: [PATCH 056/166] Fix flaky async publish cursor test on page reload The test was failing when the browser page was reloaded during test runs because the subscription ready callback could fire before the added messages arrived. This can happen when a previous test run was interrupted and the server is still processing the old session. The fix adds a polling mechanism to wait for data to arrive before asserting, with a 5-second timeout. --- .../ddp-server/livedata_server_async_tests.js | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/packages/ddp-server/livedata_server_async_tests.js b/packages/ddp-server/livedata_server_async_tests.js index 4ca4ca0864..3606b66044 100644 --- a/packages/ddp-server/livedata_server_async_tests.js +++ b/packages/ddp-server/livedata_server_async_tests.js @@ -168,9 +168,24 @@ Tinytest.addAsync('livedata server - async publish cursor', function( connection: clientConn, }); clientConn.subscribe('asyncPublishCursor', async () => { - const actual = await remoteCollection.find().fetch(); - test.equal(actual[0].name, 'async'); - onComplete(); + // Wait for data to arrive - the subscription is ready but data may still be in transit + // This can happen when a previous test run was interrupted (page reload) and the + // server is still processing the old session's grace period + let attempts = 0; + const maxAttempts = 50; // 5 seconds max wait + const checkData = async () => { + const actual = await remoteCollection.find().fetch(); + if (actual.length > 0) { + test.equal(actual[0].name, 'async'); + onComplete(); + } else if (attempts++ < maxAttempts) { + setTimeout(checkData, 100); + } else { + test.fail('Timed out waiting for data in async publish cursor test'); + onComplete(); + } + }; + await checkData(); }); }); }); From 5837919fe7bf763f1465e472aeaab5086bcf3cbb Mon Sep 17 00:00:00 2001 From: shanky Date: Tue, 9 Dec 2025 11:29:51 +0530 Subject: [PATCH 057/166] fix: handle Watchman RootResolveError for symlinked packages Fixes #13883 --- tools/fs/safe-watcher.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/tools/fs/safe-watcher.ts b/tools/fs/safe-watcher.ts index 07fe605fb7..eb49afbdcd 100644 --- a/tools/fs/safe-watcher.ts +++ b/tools/fs/safe-watcher.ts @@ -305,9 +305,13 @@ async function ensureWatchRoot(dirPath: string): Promise { if (/Events were dropped/.test(err.message)) { return; } + if (/RootResolveError/.test(err.message) || /failed to resolve root/.test(err.message)) { + console.warn(`Parcel watcher root resolve error on ${osDirPath}, ignoring: ${err.message}`); + ignoredWatchRoots.add(dirPath); + watchRoots.delete(dirPath); + return; + } console.error(`Parcel watcher error on ${osDirPath}:`, err); - // Only disable native watching for critical errors (like ENOSPC). - // @ts-ignore if (err.code === "ENOSPC" || err.errno === require("constants").ENOSPC) { fallbackToPolling(); } @@ -333,9 +337,11 @@ async function ensureWatchRoot(dirPath: string): Promise { (e.code === "ENOTDIR" || /Not a directory/.test(e.message) || e.code === "EBADF" || - /Bad file descriptor/.test(e.message)) + /Bad file descriptor/.test(e.message) || + /RootResolveError/.test(e.message) || + /failed to resolve root/.test(e.message)) ) { - console.warn(`Skipping watcher for ${osDirPath}: not a directory`); + console.warn(`Skipping watcher for ${osDirPath}: ${e.message || 'not watchable'}`); ignoredWatchRoots.add(dirPath); } else { console.error(`Failed to start watcher for ${osDirPath}:`, e); From 0179e82b9e0b1e1590f7d29c47cf16a8b1ad0134 Mon Sep 17 00:00:00 2001 From: Jan Dvorak Date: Wed, 17 Dec 2025 20:27:59 +0100 Subject: [PATCH 058/166] Icon for IntelliJ IDEs --- .gitignore | 1 + .idea/icon.svg | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100755 .idea/icon.svg diff --git a/.gitignore b/.gitignore index 4742e13056..36de1287c6 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ node_modules \#*\# .\#* .idea +!.idea/icon.svg *.iml *.sublime-project *.sublime-workspace diff --git a/.idea/icon.svg b/.idea/icon.svg new file mode 100755 index 0000000000..16ecae9b10 --- /dev/null +++ b/.idea/icon.svg @@ -0,0 +1,21 @@ + + + + + image/svg+xml + + + + + + + + + + + + + + + + \ No newline at end of file From 2a6d28d44cdcbe0818c2972fb62c914c7226afeb Mon Sep 17 00:00:00 2001 From: Jan Dvorak Date: Wed, 17 Dec 2025 21:28:38 +0100 Subject: [PATCH 059/166] Do not throw error on forgotPassword when ambiguous message set #11336 --- packages/accounts-password/password_server.js | 1 + packages/accounts-password/password_tests.js | 5 +- packages/tinytest/README.md | 10 ++- packages/tinytest/tinytest.js | 70 +++++++++++++++++-- 4 files changed, 78 insertions(+), 8 deletions(-) diff --git a/packages/accounts-password/password_server.js b/packages/accounts-password/password_server.js index 151f9c8d5f..60f68f14b5 100644 --- a/packages/accounts-password/password_server.js +++ b/packages/accounts-password/password_server.js @@ -513,6 +513,7 @@ Meteor.methods({forgotPassword: async options => { const user = await Accounts.findUserByEmail(options.email, { fields: { emails: 1 } }); if (!user) { + if (Accounts._options.ambiguousErrorMessages) return; Accounts._handleError("User not found"); } diff --git a/packages/accounts-password/password_tests.js b/packages/accounts-password/password_tests.js index 49f94544a0..9bdd79381a 100644 --- a/packages/accounts-password/password_tests.js +++ b/packages/accounts-password/password_tests.js @@ -1415,9 +1415,8 @@ if (Meteor.isServer) (() => { ); Accounts._options.ambiguousErrorMessages = true; - await test.throwsAsync( - async () => await Meteor.callAsync('forgotPassword', wrongOptions), - 'Something went wrong. Please check your credentials' + await test.doesNotThrowsAsync( + async () => await Meteor.callAsync("forgotPassword", wrongOptions) ); Accounts._options.ambiguousErrorMessages = false; diff --git a/packages/tinytest/README.md b/packages/tinytest/README.md index d93c5f1222..514ae883a7 100644 --- a/packages/tinytest/README.md +++ b/packages/tinytest/README.md @@ -272,7 +272,13 @@ EXPERIMENTAL way to compare two strings that results in a nicer display in the t ### Assertions without optional fail messages -`test.throws(func, expected);` +`test.throws(func, expected[, message]);` + +`test.throwsAsync(func, expected[, message]);` + +`test.doesNotThrows(func[, failureMessage]);` + +`test.doesNotThrowsAsync(func[, failureMessage]);` `expected` can be: @@ -281,6 +287,8 @@ EXPERIMENTAL way to compare two strings that results in a nicer display in the t - `regexp`: pass if the exception message passes the regexp. - `function`: call the function as a predicate with the exception. +`doesNotThrows` and `doesNotThrowsAsync` assert that the function does not throw. If the function throws, the assertion fails. The optional `failureMessage` is only used to annotate the failure. + Note: Node's `assert.throws` also accepts a constructor to test whether the error is of the expected class. But since JavaScript can't distinguish between constructors and plain functions and Node's `assert.throws` also accepts a predicate function, if the error fails the `instanceof` test with the constructor then the constructor is then treated as a predicate and called (!) The upshot is, if you want to test whether an error is of a particular class, use a predicate function. diff --git a/packages/tinytest/tinytest.js b/packages/tinytest/tinytest.js index b66097aafb..f8c29bab74 100644 --- a/packages/tinytest/tinytest.js +++ b/packages/tinytest/tinytest.js @@ -244,6 +244,19 @@ export class TestCaseResults { // The upshot is, if you want to test whether an error is of a // particular class, use a predicate function. // + /** + * Assert that `f` throws. + * + * `expected` can be: + * - undefined: accept any exception. + * - string: pass if the string is a substring of the exception message. + * - regexp: pass if the exception message passes the regexp. + * - function: call the function as a predicate with the exception. + * + * @param {Function} f + * @param {*} expected + * @param {String} message + */ throws(f, expected, message) { let actual; const predicate = this._guessPredicate(expected); @@ -258,10 +271,34 @@ export class TestCaseResults { } /** - * Same as throw, but accepts an async function as a parameter. - * @param f - * @param expected - * @param message + * Assert that `f` does not throw. + * @param {Function} f + * @param {String} failureMessage + */ + doesNotThrows(f, failureMessage) { + let actual; + + try { + f(); + } catch (exception) { + actual = exception; + } + + if (!actual) { + this.ok(); + } else { + this.fail({ + type: "throws", + message: ("threw an error unexpectedly: " + actual.message) + (failureMessage ? ": " + failureMessage : ""), + }); + } + } + + /** + * Same as `throws`, but accepts an async function as a parameter. + * @param {Function} f + * @param {*} expected + * @param {String} message * @returns {Promise} */ async throwsAsync(f, expected, message) { @@ -276,6 +313,31 @@ export class TestCaseResults { this._assertActual(actual, predicate, message); } + /** + * Same as `doesNotThrows`, but accepts an async function as a parameter. + * @param {Function} f + * @param {String} failureMessage + * @returns {Promise} + */ + async doesNotThrowsAsync(f, failureMessage) { + let actual; + + try { + await f(); + } catch (exception) { + actual = exception; + } + + if (!actual) { + this.ok(); + } else { + this.fail({ + type: "throws", + message: ("threw an error unexpectedly: " + actual.message) + (failureMessage ? ": " + failureMessage : ""), + }); + } + } + isTrue(v, msg) { if (v) this.ok(); From d037fe6b6ed69e269d16d3838b6bc189aadda104 Mon Sep 17 00:00:00 2001 From: calm329 Date: Mon, 5 Jan 2026 05:14:24 -0800 Subject: [PATCH 060/166] fix(accounts-password): fix operator precedence bug in passwordValidator --- packages/accounts-password/password_server.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/accounts-password/password_server.js b/packages/accounts-password/password_server.js index 151f9c8d5f..aa7175e595 100644 --- a/packages/accounts-password/password_server.js +++ b/packages/accounts-password/password_server.js @@ -295,7 +295,7 @@ const NonEmptyString = Match.Where(x => { }); const passwordValidator = Match.OneOf( - Match.Where(str => Match.test(str, String) && str.length <= Meteor.settings?.packages?.accounts?.passwordMaxLength || 256), { + Match.Where(str => Match.test(str, String) && str.length <= (Meteor.settings?.packages?.accounts?.passwordMaxLength || 256)), { digest: Match.Where(str => Match.test(str, String) && str.length === 64), algorithm: Match.OneOf('sha-256') } @@ -478,7 +478,7 @@ Meteor.methods( Accounts.setPasswordAsync = async (userId, newPlaintextPassword, options) => { check(userId, String); - check(newPlaintextPassword, Match.Where(str => Match.test(str, String) && str.length <= Meteor.settings?.packages?.accounts?.passwordMaxLength || 256)); + check(newPlaintextPassword, Match.Where(str => Match.test(str, String) && str.length <= (Meteor.settings?.packages?.accounts?.passwordMaxLength || 256))); check(options, Match.Maybe({ logout: Boolean })); options = { logout: true, ...options }; From e896c7bccf4fbb5d8a4be2117e26b727bb6d23d2 Mon Sep 17 00:00:00 2001 From: calm329 Date: Tue, 13 Jan 2026 06:48:33 -0800 Subject: [PATCH 061/166] Trigger CI From 1be8c39a3bced1c22168d19e30f1bf0343b7f03f Mon Sep 17 00:00:00 2001 From: thenileshmishra Date: Mon, 26 Jan 2026 23:09:36 +0530 Subject: [PATCH 062/166] fix(typescript): enable skipLibCheck to avoid broken third-party d.ts failures --- tools/static-assets/skel-typescript/tsconfig.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/static-assets/skel-typescript/tsconfig.json b/tools/static-assets/skel-typescript/tsconfig.json index 5806f496d5..9d9742a7c5 100644 --- a/tools/static-assets/skel-typescript/tsconfig.json +++ b/tools/static-assets/skel-typescript/tsconfig.json @@ -36,7 +36,9 @@ "resolveJsonModule": true, "types": ["node", "mocha"], "esModuleInterop": true, - "preserveSymlinks": true + "preserveSymlinks": true, + "skipLibCheck": true + }, "exclude": [ "./.meteor/**", From e46bd8a3abbe543f8f819010336707d3229866bb Mon Sep 17 00:00:00 2001 From: Drew Fisher Date: Wed, 28 Jan 2026 17:02:52 -0800 Subject: [PATCH 063/166] fix(typescript): constrain extension types to Document These functions were introduced to mongo.d.ts without constraint in 8ca07cc0d3d3051a6da9b4b432096ec1a039e3ee, which was included in Meteor 3.4 but not 3.3. When this type definition is used (i.e. through zodern-types), typescript will throw errors like: ``` .meteor/local/types/node_modules/package-types/mongo/package/os/packages/mongo/mongo.d.ts:125:63 - error TS2344: Type 'T' does not satisfy the constraint 'Document'. 125 addExtension(extension: (this: Collection, name: string | null, options?: CollectionOptions) => void): void; ~ .meteor/local/types/node_modules/package-types/mongo/package/os/packages/mongo/mongo.d.ts:125:18 125 addExtension(extension: (this: Collection, name: string | null, options?: CollectionOptions) => void): void; ~~~~~~~ This type parameter might need an `extends NpmModuleMongodb.BSON.Document` constraint. ``` The types should require that the type parameter to Collection extends the mongo document type, so we add the additional suggested constraints, which then satisfies the typechecker. --- packages/mongo/mongo.d.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/mongo/mongo.d.ts b/packages/mongo/mongo.d.ts index 0d499ee588..662fee5e72 100644 --- a/packages/mongo/mongo.d.ts +++ b/packages/mongo/mongo.d.ts @@ -122,14 +122,14 @@ export namespace Mongo { * Add a constructor extension function that runs when collections are created. * @param extension Extension function called with (name, options) and 'this' bound to collection instance */ - addExtension(extension: (this: Collection, name: string | null, options?: CollectionOptions) => void): void; + addExtension(extension: (this: Collection, name: string | null, options?: CollectionOptions) => void): void; /** * Add a prototype method to all collection instances. * @param name The name of the method to add * @param method The method function, bound to the collection instance */ - addPrototypeMethod(name: string, method: (this: Collection, ...args: any[]) => any): void; + addPrototypeMethod(name: string, method: (this: Collection, ...args: any[]) => any): void; /** * Add a static method to the Mongo.Collection constructor. @@ -573,14 +573,14 @@ export namespace Mongo { * Add a constructor extension function that runs when collections are created. * @param extension Extension function called with (name, options) and 'this' bound to collection instance */ - addExtension(extension: (this: Collection, name: string | null, options?: CollectionOptions) => void): void; + addExtension(extension: (this: Collection, name: string | null, options?: CollectionOptions) => void): void; /** * Add a prototype method to all collection instances. * @param name The name of the method to add * @param method The method function, bound to the collection instance */ - addPrototypeMethod(name: string, method: (this: Collection, ...args: any[]) => any): void; + addPrototypeMethod(name: string, method: (this: Collection, ...args: any[]) => any): void; /** * Add a static method to the Mongo.Collection constructor. From 1f69f21ab261b128e17884be5ce26aae2d61401a Mon Sep 17 00:00:00 2001 From: Abhyshek Bhalaji Date: Sun, 12 Oct 2025 13:01:02 +0530 Subject: [PATCH 064/166] removed _processOneDataMessage function --- .../ddp-client/common/livedata_connection.js | 21 ------------------- 1 file changed, 21 deletions(-) diff --git a/packages/ddp-client/common/livedata_connection.js b/packages/ddp-client/common/livedata_connection.js index 9755a8012a..03385b7b68 100644 --- a/packages/ddp-client/common/livedata_connection.js +++ b/packages/ddp-client/common/livedata_connection.js @@ -1120,27 +1120,6 @@ export class Connection { return Object.values(invokers).some((invoker) => !!invoker.sentMessage); } - async _processOneDataMessage(msg, updates) { - const messageType = msg.msg; - - // msg is one of ['added', 'changed', 'removed', 'ready', 'updated'] - if (messageType === 'added') { - await this._process_added(msg, updates); - } else if (messageType === 'changed') { - this._process_changed(msg, updates); - } else if (messageType === 'removed') { - this._process_removed(msg, updates); - } else if (messageType === 'ready') { - this._process_ready(msg, updates); - } else if (messageType === 'updated') { - this._process_updated(msg, updates); - } else if (messageType === 'nosub') { - // ignore this - } else { - Meteor._debug('discarding unknown livedata data message type', msg); - } - } - _prepareBuffersToFlush() { const self = this; if (self._bufferedWritesFlushHandle) { From 2c89d08dff77d7d3ceff7a982e3ee9df021af7fb Mon Sep 17 00:00:00 2001 From: Welkin Wong Date: Thu, 5 Feb 2026 15:11:07 +0800 Subject: [PATCH 065/166] fix(build): fix cordova platforms detection to avoid modern/legacy --- tools/project-context.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tools/project-context.js b/tools/project-context.js index 83633ec6a1..d8e85fe45b 100644 --- a/tools/project-context.js +++ b/tools/project-context.js @@ -1470,8 +1470,7 @@ Object.assign(exports.PlatformList.prototype, { getCordovaPlatforms: function () { var self = this; - return _.difference(self._platforms, - exports.PlatformList.DEFAULT_PLATFORMS); + return _.intersection(self._platforms, ['ios', 'android']); }, usesCordova: function () { From a8a4cb5c48385031131dc5b81b19431a3cc6e8c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Mon, 9 Feb 2026 16:58:26 +0100 Subject: [PATCH 066/166] add changelog for v3.4.1 release --- .../generators/changelog/versions/3.4.1.md | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 v3-docs/docs/generators/changelog/versions/3.4.1.md diff --git a/v3-docs/docs/generators/changelog/versions/3.4.1.md b/v3-docs/docs/generators/changelog/versions/3.4.1.md new file mode 100644 index 0000000000..f3de8ed027 --- /dev/null +++ b/v3-docs/docs/generators/changelog/versions/3.4.1.md @@ -0,0 +1,69 @@ +## v3.4.1, 2026-02-09 + +### Highlights + +- Add support for dots in minimongo key names, [PR#13975](https://github.com/meteor/meteor/pull/13975) +- Fix operator precedence bug in `passwordValidator` that could reject valid passwords, [PR#14075](https://github.com/meteor/meteor/pull/14075) +- Do not throw error on `forgotPassword` when `ambiguousErrorMessages` is set, [PR#14060](https://github.com/meteor/meteor/pull/14060) +- Fix Watchman `RootResolveError` for symlinked packages, [PR#14045](https://github.com/meteor/meteor/pull/14045) +- Fix `getUsersInRoleAsync` to handle assignment options correctly, [PR#14014](https://github.com/meteor/meteor/pull/14014) +- Use concurrency-safe iteration in Minimongo async methods, [PR#13927](https://github.com/meteor/meteor/pull/13927) +- Constrain TypeScript extension types to `Document` in collection extensions, [PR#14105](https://github.com/meteor/meteor/pull/14105) +- Remove redundant `await` on returned promises in accounts-base, [PR#14017](https://github.com/meteor/meteor/pull/14017) +- Remove `import React from 'react'` from project skeletons, [PR#14041](https://github.com/meteor/meteor/pull/14041) +- Fix displaying Google Maps in mobile applications by preserving native globals in legacy polyfills +- Add IntelliJ IDEs icon, [PR#14059](https://github.com/meteor/meteor/pull/14059) +- Remove unused `_processOneDataMessage` from DDP client +- Update OAuth configuration instructions, [PR#13947](https://github.com/meteor/meteor/pull/13947) +- Fix bugs with test-in-console, [PR#13000](https://github.com/meteor/meteor/pull/13000) + +All Merged PRs@[GitHub PRs 3.4.1](https://github.com/meteor/meteor/pulls?q=is%3Apr+is%3Amerged+base%3Arelease-3.4.1) + +#### Breaking Changes + +N/A + +#### Internal API changes + +- Removed unused `_processOneDataMessage` method from DDP client `Connection` class + +#### Migration Steps + +Please run the following command to update your project: + +```bash +meteor update --release 3.4.1 +``` + +--- + +If you find any issues, please report them to the [Meteor issues tracker](https://github.com/meteor/meteor). + +#### Bumped Meteor Packages + +- roles@1.0.2 + +#### Bumped NPM Packages + +N/A + +#### Special thanks to + +✨✨✨ + +- [@nachocodoner](https://github.com/nachocodoner) +- [@italojs](https://github.com/italojs) +- [@Grubba27](https://github.com/Grubba27) +- [@zarvox](https://github.com/zarvox) +- [@calm329](https://github.com/calm329) +- [@StorytellerCZ](https://github.com/StorytellerCZ) +- [@sanki92](https://github.com/sanki92) +- [@bratelefant](https://github.com/bratelefant) +- [@carlosarraes](https://github.com/carlosarraes) +- [@graemian](https://github.com/graemian) +- [@9Morello](https://github.com/9Morello) +- [@ReinaT5678](https://github.com/ReinaT5678) +- [@zodern](https://github.com/zodern) +- [@paritoshdey-dev](https://github.com/paritoshdey-dev) + +✨✨✨ From 15dc12b075c30b4a4ea1c4817de768a3aa19a91d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Wed, 11 Feb 2026 16:21:49 +0100 Subject: [PATCH 067/166] add TOOL_NODE_FLAGS inheritance for rspack processes and add tests --- packages/rspack/lib/processes.js | 7 +- packages/tools-core/lib/meteor.js | 33 ++++ packages/tools-core/package.js | 19 ++ packages/tools-core/tests/meteor_tests.js | 213 ++++++++++++++++++++++ v3-docs/docs/cli/environment-variables.md | 15 +- 5 files changed, 283 insertions(+), 4 deletions(-) create mode 100644 packages/tools-core/tests/meteor_tests.js diff --git a/packages/rspack/lib/processes.js b/packages/rspack/lib/processes.js index d97d731257..929c33333e 100644 --- a/packages/rspack/lib/processes.js +++ b/packages/rspack/lib/processes.js @@ -33,6 +33,7 @@ const { isMeteorAppConfigModernVerbose, isMeteorBundleVisualizerProject, getMeteorAppPort, + inheritMeteorToolNodeFlags, } = require('meteor/tools-core/lib/meteor'); const { @@ -327,7 +328,7 @@ export function startRspackClientServe(options = {}) { command, args, { cwd: appDir, - env: { ...process.env, ...envs }, + env: inheritMeteorToolNodeFlags({ ...process.env, ...envs }), onStdout: (data) => { logInfo(`[Rspack Client] ${data}`); if (onCompile && data.trim().includes("compiled")) { @@ -390,7 +391,7 @@ export function startRspackServerWatch(options = {}) { command, args, { cwd: appDir, - env: { ...process.env, ...envs }, + env: inheritMeteorToolNodeFlags({ ...process.env, ...envs }), onStdout: (data) => { logInfo(`[Rspack Server] ${data}`); if (onCompile && data.trim().includes("compiled")) { @@ -457,7 +458,7 @@ export async function runRspackBuild({ isClient, isServer, isTest, isTestModule, args, { cwd: appDir, - env: { ...process.env, ...envs }, + env: inheritMeteorToolNodeFlags({ ...process.env, ...envs }), onStdout: (data) => { logInfo(`[Rspack ${label} ${endpoint}] ${data}`); if (onCompile && data.trim().includes("compiled")) { diff --git a/packages/tools-core/lib/meteor.js b/packages/tools-core/lib/meteor.js index 1556a1ddde..18bea04de4 100644 --- a/packages/tools-core/lib/meteor.js +++ b/packages/tools-core/lib/meteor.js @@ -487,3 +487,36 @@ export function getMeteorEnvPackageDirs() { ...(packageDirsFromEnvVar('PACKAGE_DIRS', ':')), ]; } + +/** + * Spreads Meteor's TOOL_NODE_FLAGS to NODE_OPTIONS for proper inheritance + * of Meteor-specific tool environment process variables. + * Only spreads if TOOL_NODE_FLAGS_INHERIT is truthy (enabled by default). + * @param {Object} env - The current environment variables + * @returns {Object} The updated environment variables with NODE_OPTIONS + */ +export function inheritMeteorToolNodeFlags(env = {}) { + const toolFlags = env.TOOL_NODE_FLAGS; + if (!toolFlags) { + return env; + } + + // Check if spreading is enabled (default: true) + // Only disable if TOOL_NODE_FLAGS_INHERIT is explicitly set to a falsy value + // Treat "0" as falsy for this specific case + const shouldSpread = env.TOOL_NODE_FLAGS_INHERIT !== undefined + ? (env.TOOL_NODE_FLAGS_INHERIT !== "0" && !!env.TOOL_NODE_FLAGS_INHERIT) + : true; + + if (!shouldSpread) { + return env; + } + + return { + ...env, + NODE_OPTIONS: [toolFlags, env.NODE_OPTIONS] + .filter(Boolean) + .map(s => s.trim()) + .join(' '), + }; +} diff --git a/packages/tools-core/package.js b/packages/tools-core/package.js index 71a14de26c..341e554947 100644 --- a/packages/tools-core/package.js +++ b/packages/tools-core/package.js @@ -9,3 +9,22 @@ Package.onUse(function (api) { api.mainModule('tools-core.js', 'server'); }); + +Package.onTest(function (api) { + api.use(['ecmascript', 'tinytest']); + api.use('tools-core'); + + // Add test files for each lib/ module + // This structure allows easy addition of tests for other lib/ categories + api.addFiles([ + 'tests/meteor_tests.js', + // Add more test files here as needed: + // 'tests/process_tests.js', + // 'tests/npm_tests.js', + // 'tests/git_tests.js', + // 'tests/global-state_tests.js', + // 'tests/ignore_tests.js', + // 'tests/log_tests.js', + // 'tests/string_tests.js', + ], 'server'); +}); diff --git a/packages/tools-core/tests/meteor_tests.js b/packages/tools-core/tests/meteor_tests.js new file mode 100644 index 0000000000..2615172da9 --- /dev/null +++ b/packages/tools-core/tests/meteor_tests.js @@ -0,0 +1,213 @@ +import { inheritMeteorToolNodeFlags } from "../lib/meteor.js"; + +Tinytest.add( + "tools-core - inheritMeteorToolNodeFlags - no TOOL_NODE_FLAGS", + function (test) { + const env = { + NODE_OPTIONS: "--inspect", + TOOL_NODE_FLAGS_INHERIT: "true", + }; + const result = inheritMeteorToolNodeFlags(env); + + test.equal( + result, + env, + "Should return input unchanged when no TOOL_NODE_FLAGS" + ); + } +); + +Tinytest.add( + "tools-core - inheritMeteorToolNodeFlags - default behavior (inherit enabled)", + function (test) { + const env = { + TOOL_NODE_FLAGS: "--max-old-space-size=4096", + NODE_OPTIONS: "--inspect", + }; + const result = inheritMeteorToolNodeFlags(env); + + test.equal( + result.TOOL_NODE_FLAGS, + "--max-old-space-size=4096", + "TOOL_NODE_FLAGS should be preserved" + ); + test.equal( + result.NODE_OPTIONS, + "--max-old-space-size=4096 --inspect", + "NODE_OPTIONS should contain both flags" + ); + } +); + +Tinytest.add( + "tools-core - inheritMeteorToolNodeFlags - inherit explicitly enabled", + function (test) { + const env = { + TOOL_NODE_FLAGS: "--max-old-space-size=4096", + NODE_OPTIONS: "--inspect", + TOOL_NODE_FLAGS_INHERIT: "true", + }; + const result = inheritMeteorToolNodeFlags(env); + + test.equal( + result.NODE_OPTIONS, + "--max-old-space-size=4096 --inspect", + "NODE_OPTIONS should contain both flags when explicitly enabled" + ); + } +); + +Tinytest.add( + "tools-core - inheritMeteorToolNodeFlags - inherit explicitly enabled with truthy value", + function (test) { + const env = { + TOOL_NODE_FLAGS: "--max-old-space-size=4096", + NODE_OPTIONS: "--inspect", + TOOL_NODE_FLAGS_INHERIT: "1", + }; + const result = inheritMeteorToolNodeFlags(env); + + test.equal( + result.NODE_OPTIONS, + "--max-old-space-size=4096 --inspect", + "NODE_OPTIONS should contain both flags with truthy string" + ); + } +); + +Tinytest.add( + "tools-core - inheritMeteorToolNodeFlags - inherit disabled with empty string", + function (test) { + const env = { + TOOL_NODE_FLAGS: "--max-old-space-size=4096", + NODE_OPTIONS: "--inspect", + TOOL_NODE_FLAGS_INHERIT: "", + }; + const result = inheritMeteorToolNodeFlags(env); + + test.equal( + result.NODE_OPTIONS, + "--inspect", + "NODE_OPTIONS should remain unchanged when inherit disabled with empty string" + ); + test.equal( + result.TOOL_NODE_FLAGS, + "--max-old-space-size=4096", + "TOOL_NODE_FLAGS should be preserved" + ); + } +); + +Tinytest.add( + "tools-core - inheritMeteorToolNodeFlags - inherit disabled with false", + function (test) { + const env = { + TOOL_NODE_FLAGS: "--max-old-space-size=4096", + NODE_OPTIONS: "--inspect", + TOOL_NODE_FLAGS_INHERIT: false, + }; + const result = inheritMeteorToolNodeFlags(env); + + test.equal( + result.NODE_OPTIONS, + "--inspect", + "NODE_OPTIONS should remain unchanged when inherit disabled with false" + ); + } +); + +Tinytest.add( + "tools-core - inheritMeteorToolNodeFlags - inherit disabled with zero", + function (test) { + const env = { + TOOL_NODE_FLAGS: "--max-old-space-size=4096", + NODE_OPTIONS: "--inspect", + TOOL_NODE_FLAGS_INHERIT: "0", + }; + const result = inheritMeteorToolNodeFlags(env); + console.log("--> (meteor_tests.js-Line: 128)\n result: ", result); + + test.equal( + result.NODE_OPTIONS, + "--inspect", + 'NODE_OPTIONS should remain unchanged when inherit disabled with "0"' + ); + } +); + +Tinytest.add( + "tools-core - inheritMeteorToolNodeFlags - no existing NODE_OPTIONS", + function (test) { + const env = { + TOOL_NODE_FLAGS: "--max-old-space-size=4096", + }; + const result = inheritMeteorToolNodeFlags(env); + + test.equal( + result.NODE_OPTIONS, + "--max-old-space-size=4096", + "NODE_OPTIONS should be set to TOOL_NODE_FLAGS when no existing NODE_OPTIONS" + ); + } +); + +Tinytest.add( + "tools-core - inheritMeteorToolNodeFlags - whitespace handling", + function (test) { + const env = { + TOOL_NODE_FLAGS: " --max-old-space-size=4096 ", + NODE_OPTIONS: " --inspect ", + }; + const result = inheritMeteorToolNodeFlags(env); + + test.equal( + result.NODE_OPTIONS, + "--max-old-space-size=4096 --inspect", + "Should handle whitespace correctly" + ); + } +); + +Tinytest.add( + "tools-core - inheritMeteorToolNodeFlags - multiple flags", + function (test) { + const env = { + TOOL_NODE_FLAGS: "--max-old-space-size=4096 --expose-gc", + NODE_OPTIONS: "--inspect --trace-warnings", + }; + const result = inheritMeteorToolNodeFlags(env); + + test.equal( + result.NODE_OPTIONS, + "--max-old-space-size=4096 --expose-gc --inspect --trace-warnings", + "Should handle multiple flags correctly" + ); + } +); + +Tinytest.add( + "tools-core - inheritMeteorToolNodeFlags - empty environment", + function (test) { + const env = {}; + const result = inheritMeteorToolNodeFlags(env); + + test.equal( + result, + env, + "Should return input unchanged for empty environment" + ); + } +); + +Tinytest.add( + "tools-core - inheritMeteorToolNodeFlags - undefined environment", + function (test) { + const result = inheritMeteorToolNodeFlags(); + + test.equal( + Object.keys(result).length, + 0, + "Should return empty object for undefined input" + ); + } +); diff --git a/v3-docs/docs/cli/environment-variables.md b/v3-docs/docs/cli/environment-variables.md index b67bc28d91..35a9635d49 100644 --- a/v3-docs/docs/cli/environment-variables.md +++ b/v3-docs/docs/cli/environment-variables.md @@ -146,9 +146,22 @@ Used to generate URLs to your application by, among others, the accounts package ## TOOL_NODE_FLAGS (_development, production_) -Used to pass flags/variables to Node inside Meteor build. For example you can use this to pass a link to icu data: `TOOL_NODE_FLAGS="--icu-data-dir=node_modules/full-icu"` +Used to pass Node.js flags that Meteor will inherit and spread to other tool processes like Rspack. For example, to increase memory limits for all tools: `TOOL_NODE_FLAGS="--max-old-space-size=4096"` + +By default, these flags are automatically spread to `NODE_OPTIONS` so that tools like Rspack inherit them. This behavior can be controlled using [`TOOL_NODE_FLAGS_INHERIT`](#tool-node-flags-spread). + For full list of available flags see the [Node documentation](https://nodejs.org/dist/latest-v12.x/docs/api/cli.html). +## TOOL_NODE_FLAGS_INHERIT +(_development, production_) + +Controls whether `TOOL_NODE_FLAGS` are prepended to `NODE_OPTIONS`. Enabled by default. + +```bash +# Disable inheritance - Rspack won't inherit the heap limit +TOOL_NODE_FLAGS_INHERIT=0 TOOL_NODE_FLAGS="--max-old-space-size=4096" meteor run +``` + ## UNIX_SOCKET_GROUP (_production_) From c2079ffa23529ed41ea9125d84f08698ff7362d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Wed, 11 Feb 2026 16:46:26 +0100 Subject: [PATCH 068/166] remove commented-out test file references --- packages/tools-core/package.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/packages/tools-core/package.js b/packages/tools-core/package.js index 341e554947..c12ebf8b89 100644 --- a/packages/tools-core/package.js +++ b/packages/tools-core/package.js @@ -18,13 +18,5 @@ Package.onTest(function (api) { // This structure allows easy addition of tests for other lib/ categories api.addFiles([ 'tests/meteor_tests.js', - // Add more test files here as needed: - // 'tests/process_tests.js', - // 'tests/npm_tests.js', - // 'tests/git_tests.js', - // 'tests/global-state_tests.js', - // 'tests/ignore_tests.js', - // 'tests/log_tests.js', - // 'tests/string_tests.js', ], 'server'); }); From fad37e7ea6036c51da12dc6bf3d4c9ed7cada4f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Wed, 11 Feb 2026 17:19:55 +0100 Subject: [PATCH 069/166] update memory configuration guidelines for Meteor 3.4.1+ --- .../rspack-bundler-integration.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/v3-docs/docs/about/modern-build-stack/rspack-bundler-integration.md b/v3-docs/docs/about/modern-build-stack/rspack-bundler-integration.md index a4c0b56c33..35720ceb4c 100644 --- a/v3-docs/docs/about/modern-build-stack/rspack-bundler-integration.md +++ b/v3-docs/docs/about/modern-build-stack/rspack-bundler-integration.md @@ -804,9 +804,13 @@ You can compare performance before and after enabling `modern` by running [`mete Large apps are more likely to hit memory limits during Meteor-Rspack builds, but this can also happen on smaller projects depending on the number of dependencies, cache size, and available system memory. If you experience crashes or out-of-memory errors, it's likely that the Rspack child process is running out of heap memory. -A common first reaction is to set [`TOOL_NODE_FLAGS`](../../cli/environment-variables.md#tool-node-flags)` (`TOOL_NODE_FLAGS="--max-old-space-size=8192"`), but this flag is mainly for the Meteor tool's own Node.js process at startup. Rspack runs as a spawned child process and may not inherit it. +Starting from Meteor 3.4.1, you can use [`TOOL_NODE_FLAGS`](../../cli/environment-variables.md#tool-node-flags) to set memory limits that will be automatically inherited by Rspack and other tool processes: -Instead, use the standard `NODE_OPTIONS` environment variable, which Node.js propagates to child processes: +```bash +TOOL_NODE_FLAGS="--max-old-space-size=16384" meteor run +``` + +For Meteor 3.4, you should use the standard `NODE_OPTIONS` environment variable, which Node.js propagates to child processes: ```bash NODE_OPTIONS="--max-old-space-size=16384" meteor run @@ -814,10 +818,6 @@ NODE_OPTIONS="--max-old-space-size=16384" meteor run This raises the heap limit for the Rspack process and should reduce how often memory-related crashes occur. Adjust the value according to your machine's available memory. -:::info -For the Meteor 3.4.x series, as `NODE_OPTIONS` is confirmed to help, one option being considered is to automatically inherit memory settings from `TOOL_NODE_FLAGS` into the spawned Rspack process. -::: - Another approach is to disable Rspack's persistent cache, which is enabled by default and can be memory-intensive. See the [Cache](#cache) migration topic to disable it: ```js @@ -828,7 +828,7 @@ module.exports = defineConfig(Meteor => ({ })); ``` -You can combine both solutions: raise the heap limit with `NODE_OPTIONS` and disable persistent cache to reduce overall memory pressure. +You can combine both solutions: raise the heap limit with `TOOL_NODE_FLAGS` (3.4.1+) or `NODE_OPTIONS` (3.4) and disable persistent cache to reduce overall memory pressure. Rspack itself has reported plans to optimize persistent cache and overall RAM consumption in [Rspack 2.0](https://rspack.rs/misc/planning/roadmap), which should improve memory behavior in future Meteor-Rspack releases. From af0e1fafa613f71c7f22d0248778b6910e3e4616 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Fri, 13 Feb 2026 17:49:32 +0100 Subject: [PATCH 070/166] ensure full app tests run the app in the same instance as the tests --- npm-packages/meteor-rspack/rspack.config.js | 6 --- packages/rspack/lib/build-context.js | 52 +++++++++++++++------ packages/rspack/lib/config.js | 4 ++ packages/rspack/rspack_plugin.js | 21 --------- 4 files changed, 42 insertions(+), 41 deletions(-) diff --git a/npm-packages/meteor-rspack/rspack.config.js b/npm-packages/meteor-rspack/rspack.config.js index 09138424a7..0e05e47f8f 100644 --- a/npm-packages/meteor-rspack/rspack.config.js +++ b/npm-packages/meteor-rspack/rspack.config.js @@ -230,8 +230,6 @@ module.exports = async function (inMeteor = {}, argv = {}) { const projectConfigPath = Meteor.projectConfigPath || path.resolve(projectDir, 'rspack.config.js'); const configPath = Meteor.configPath; const testEntry = Meteor.testEntry; - const testClientEntry = Meteor.testClientEntry; - const testServerEntry = Meteor.testServerEntry; const isTypescriptEnabled = Meteor.isTypescriptEnabled || false; const isJsxEnabled = @@ -451,8 +449,6 @@ module.exports = async function (inMeteor = {}, argv = {}) { }) : isTest && testEntry ? path.resolve(process.cwd(), testEntry) - : isTest && testClientEntry - ? path.resolve(process.cwd(), testClientEntry) : path.resolve(process.cwd(), buildContext, entryPath); const clientNameConfig = `[${(isTest && 'test-') || ''}client-rspack]`; // Base client config @@ -570,8 +566,6 @@ module.exports = async function (inMeteor = {}, argv = {}) { }) : isTest && testEntry ? path.resolve(process.cwd(), testEntry) - : isTest && testServerEntry - ? path.resolve(process.cwd(), testServerEntry) : path.resolve(projectDir, buildContext, entryPath); const serverNameConfig = `[${(isTest && 'test-') || ''}server-rspack]`; // Base server config diff --git a/packages/rspack/lib/build-context.js b/packages/rspack/lib/build-context.js index 765f841f41..61fbab3e98 100644 --- a/packages/rspack/lib/build-context.js +++ b/packages/rspack/lib/build-context.js @@ -21,6 +21,7 @@ const { isMeteorAppBuild, isMeteorBlazeProject, isMeteorAppNative, + isMeteorAppTestFullApp, } = require('meteor/tools-core/lib/meteor'); const { @@ -129,11 +130,14 @@ export function ensureModuleFilesExist() { const testClientFiles = { entryFile: initialEntrypoints.testClient || '', outputFile: getBuildFilePath({ isTest: true, isTestModule, isClient: true, role: FILE_ROLE.output, onlyFilename: true }), + mainEntryFile: mainClientFiles.entryFile, }; const testServerFiles = { entryFile: initialEntrypoints.testServer || '', outputFile: getBuildFilePath({ isTest: true, isTestModule, isServer: true, role: FILE_ROLE.output, onlyFilename: true }), + mainEntryFile: mainServerFiles.entryFile, }; + const isTestFullApp = isMeteorAppTestFullApp(); const moduleFiles = { /* Main module files for client and server */ @@ -150,18 +154,18 @@ export function ensureModuleFilesExist() { [getBuildFilePath({ isMain: true, isServer: true, ...env, role: FILE_ROLE.output })]: getBuildFileContent({ isMain: true, isServer: true, ...env, role: FILE_ROLE.output, ...mainServerFiles }), /* Test module files when test module, test module files for client and server are present or eager discovery */ - [getBuildFilePath({ isTest: true, isTestModule, isClient: true, ...commandRole })]: - getBuildFileContent({ isTest: true, isTestModule, isClient: true, ...commandRole, ...testClientFiles }), - [getBuildFilePath({ isTest: true, isTestModule, isClient: true, role: FILE_ROLE.entry })]: - getBuildFileContent({ isTest: true, isTestModule, isClient: true, role: FILE_ROLE.entry, ...testClientFiles }), - [getBuildFilePath({ isTest: true, isTestModule, isClient: true, role: FILE_ROLE.output })]: - getBuildFileContent({ isTest: true, isTestModule, isClient: true, role: FILE_ROLE.output, ...testClientFiles }), - [getBuildFilePath({ isTest: true, isTestModule, isServer: true, ...commandRole })]: - getBuildFileContent({ isTest: true, isTestModule, isServer: true, ...commandRole, ...testServerFiles }), - [getBuildFilePath({ isTest: true, isTestModule, isServer: true, role: FILE_ROLE.entry })]: - getBuildFileContent({ isTest: true, isTestModule, isServer: true, role: FILE_ROLE.entry, ...testServerFiles }), - [getBuildFilePath({ isTest: true, isTestModule, isServer: true, role: FILE_ROLE.output })]: - getBuildFileContent({ isTest: true, isTestModule, isServer: true, role: FILE_ROLE.output, ...testServerFiles }), + [getBuildFilePath({ isTest: true, isTestFullApp, isTestModule, isClient: true, ...commandRole })]: + getBuildFileContent({ isTest: true, isTestFullApp, isTestModule, isClient: true, ...commandRole, ...testClientFiles }), + [getBuildFilePath({ isTest: true, isTestFullApp, isTestModule, isClient: true, role: FILE_ROLE.entry })]: + getBuildFileContent({ isTest: true, isTestFullApp, isTestModule, isClient: true, role: FILE_ROLE.entry, ...testClientFiles }), + [getBuildFilePath({ isTest: true, isTestFullApp, isTestModule, isClient: true, role: FILE_ROLE.output })]: + getBuildFileContent({ isTest: true, isTestFullApp, isTestModule, isClient: true, role: FILE_ROLE.output, ...testClientFiles }), + [getBuildFilePath({ isTest: true, isTestFullApp, isTestModule, isServer: true, ...commandRole })]: + getBuildFileContent({ isTest: true, isTestFullApp, isTestModule, isServer: true, ...commandRole, ...testServerFiles }), + [getBuildFilePath({ isTest: true, isTestFullApp, isTestModule, isServer: true, role: FILE_ROLE.entry })]: + getBuildFileContent({ isTest: true, isTestFullApp, isTestModule, isServer: true, role: FILE_ROLE.entry, ...testServerFiles }), + [getBuildFilePath({ isTest: true, isTestFullApp, isTestModule, isServer: true, role: FILE_ROLE.output })]: + getBuildFileContent({ isTest: true, isTestFullApp, isTestModule, isServer: true, role: FILE_ROLE.output, ...testServerFiles }), }; Object.entries(moduleFiles).forEach(([filename, defaultContent]) => { @@ -418,9 +422,29 @@ if (module.hot) { * @returns {string} The import content */ function getImportContent(config, side, role) { - if (config?.entryFile && role === FILE_ROLE.entry) { - return `/* Link to 🔌 Meteor ${capitalizeFirstLetter(side)} Entry */ + if (role === FILE_ROLE.entry) { + if (config?.isTest) { + return `${ + config?.isTestFullApp && config?.mainEntryFile + ? `/* Link to 🔌 Meteor ${capitalizeFirstLetter( + side + )} Main Entry (--full-app mode) */ +import '../../${config.mainEntryFile}';` + : "" + } +${ + config?.entryFile + ? ` +/* Link to 🔌 Meteor ${capitalizeFirstLetter(side)} Test Entry */ +import '../../${config.entryFile}';` + : "" +}`; + } + + if (config?.entryFile) { + return `/* Link to 🔌 Meteor ${capitalizeFirstLetter(side)} Entry */ import '../../${config?.entryFile}';`; + } } if (config?.outputFile && diff --git a/packages/rspack/lib/config.js b/packages/rspack/lib/config.js index 23ef8005a3..442f41d7d4 100644 --- a/packages/rspack/lib/config.js +++ b/packages/rspack/lib/config.js @@ -342,9 +342,13 @@ export function configureMeteorForRspack() { mainClient: `${RSPACK_BUILD_CONTEXT}/${mainClientModule}`, mainServer: `${RSPACK_BUILD_CONTEXT}/${mainServerModule}`, ...((isTestModule && { + mainClient: `${RSPACK_BUILD_CONTEXT}/${testClientModule}`, + mainServer: `${RSPACK_BUILD_CONTEXT}/${testServerModule}`, testClient: `${RSPACK_BUILD_CONTEXT}/${testClientModule}`, testServer: `${RSPACK_BUILD_CONTEXT}/${testServerModule}`, }) || { + mainClient: `${RSPACK_BUILD_CONTEXT}/${testClientModule}`, + mainServer: `${RSPACK_BUILD_CONTEXT}/${testServerModule}`, testClient: `${RSPACK_BUILD_CONTEXT}/${testClientModule}`, testServer: `${RSPACK_BUILD_CONTEXT}/${testServerModule}`, }), diff --git a/packages/rspack/rspack_plugin.js b/packages/rspack/rspack_plugin.js index e852a22a6c..f456e7cf43 100644 --- a/packages/rspack/rspack_plugin.js +++ b/packages/rspack/rspack_plugin.js @@ -264,27 +264,6 @@ if (isMeteorAppRun() || isMeteorAppBuild() || isMeteorAppTest()) { onCompileServer, } = setupCompilationTracking(); - // When run test for full app, run Rspack app server as well - // isTestLike ensures the app runtime environment inherit test envs - if (isMeteorAppTestFullApp()) { - await runRspackBuild({ - isTest: false, - isTestLike: true, - isServer: true, - isClient: false, - }); - - if (isMeteorAppTestWatch()) { - runRspackBuild({ - isServer: true, - isClient: false, - isTest: false, - isTestLike: true, - watch: true, - }); - } - } - // When testModule is specified for client or server, run Rspack considering those files if (initialEntrypoints?.testClient || initialEntrypoints?.testServer) { runRspackBuild({ From 0aac0a68e3e5796b7f169c5b56877a583d6a1e6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Fri, 13 Feb 2026 17:57:20 +0100 Subject: [PATCH 071/166] update @meteorjs/rspack to version 1.0.0-beta.1 --- npm-packages/meteor-rspack/package-lock.json | 4 ++-- npm-packages/meteor-rspack/package.json | 2 +- packages/rspack/lib/constants.js | 2 +- tools/modern-tests/apps/vue/package.json | 2 +- tools/static-assets/skel-angular/package.json | 2 +- tools/static-assets/skel-apollo/package.json | 2 +- tools/static-assets/skel-babel/package.json | 2 +- tools/static-assets/skel-blaze/package.json | 2 +- tools/static-assets/skel-chakra-ui/package.json | 2 +- tools/static-assets/skel-coffeescript/package.json | 2 +- tools/static-assets/skel-full/package.json | 2 +- tools/static-assets/skel-react/package.json | 2 +- tools/static-assets/skel-solid/package.json | 2 +- tools/static-assets/skel-svelte/package.json | 2 +- tools/static-assets/skel-tailwind/package.json | 2 +- tools/static-assets/skel-typescript/package.json | 2 +- tools/static-assets/skel-vue/package.json | 2 +- 17 files changed, 18 insertions(+), 18 deletions(-) diff --git a/npm-packages/meteor-rspack/package-lock.json b/npm-packages/meteor-rspack/package-lock.json index 7ac96ff574..9008a74422 100644 --- a/npm-packages/meteor-rspack/package-lock.json +++ b/npm-packages/meteor-rspack/package-lock.json @@ -1,12 +1,12 @@ { "name": "@meteorjs/rspack", - "version": "1.0.0", + "version": "1.1.0-beta.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@meteorjs/rspack", - "version": "1.0.0", + "version": "1.1.0-beta.1", "license": "ISC", "dependencies": { "fast-deep-equal": "^3.1.3", diff --git a/npm-packages/meteor-rspack/package.json b/npm-packages/meteor-rspack/package.json index 92c294f62c..de166bafa1 100644 --- a/npm-packages/meteor-rspack/package.json +++ b/npm-packages/meteor-rspack/package.json @@ -1,6 +1,6 @@ { "name": "@meteorjs/rspack", - "version": "1.0.0", + "version": "1.1.0-beta.1", "description": "Configuration logic for using Rspack in Meteor projects", "main": "index.js", "type": "commonjs", diff --git a/packages/rspack/lib/constants.js b/packages/rspack/lib/constants.js index 3e5a1e222f..9ca9e761f6 100644 --- a/packages/rspack/lib/constants.js +++ b/packages/rspack/lib/constants.js @@ -5,7 +5,7 @@ export const DEFAULT_RSPACK_VERSION = '1.7.1'; -export const DEFAULT_METEOR_RSPACK_VERSION = '1.0.0'; +export const DEFAULT_METEOR_RSPACK_VERSION = '1.1.0-beta.1'; export const DEFAULT_METEOR_RSPACK_REACT_HMR_VERSION = '1.4.3'; diff --git a/tools/modern-tests/apps/vue/package.json b/tools/modern-tests/apps/vue/package.json index 48cea77380..4d13d28fd1 100644 --- a/tools/modern-tests/apps/vue/package.json +++ b/tools/modern-tests/apps/vue/package.json @@ -17,7 +17,7 @@ "vue-router": "^4.2.5" }, "devDependencies": { - "@meteorjs/rspack": "^1.0.0", + "@meteorjs/rspack": "^1.0.0-beta.1", "@rspack/cli": "^1.4.8", "@rspack/core": "^1.4.8", "@tailwindcss/postcss": "^4.1.12", diff --git a/tools/static-assets/skel-angular/package.json b/tools/static-assets/skel-angular/package.json index a6a7078018..1140972385 100644 --- a/tools/static-assets/skel-angular/package.json +++ b/tools/static-assets/skel-angular/package.json @@ -22,7 +22,7 @@ }, "devDependencies": { "@angular/compiler-cli": "^20.0.0", - "@meteorjs/rspack": "^1.0.0", + "@meteorjs/rspack": "^1.0.0-beta.1", "@nx/angular-rspack": "^21.1.0", "@rsdoctor/rspack-plugin": "^1.2.3", "@rspack/cli": "^1.7.1", diff --git a/tools/static-assets/skel-apollo/package.json b/tools/static-assets/skel-apollo/package.json index 8ff4dcf675..50c58e0f22 100644 --- a/tools/static-assets/skel-apollo/package.json +++ b/tools/static-assets/skel-apollo/package.json @@ -20,7 +20,7 @@ "devDependencies": { "@graphql-tools/webpack-loader": "^7.0.0", "@rsdoctor/rspack-plugin": "^1.2.3", - "@meteorjs/rspack": "^1.0.0", + "@meteorjs/rspack": "^1.0.0-beta.1", "@rspack/cli": "^1.7.1", "@rspack/core": "^1.7.1", "@rspack/plugin-react-refresh": "^1.4.3", diff --git a/tools/static-assets/skel-babel/package.json b/tools/static-assets/skel-babel/package.json index 303e6037b6..647024e2c8 100644 --- a/tools/static-assets/skel-babel/package.json +++ b/tools/static-assets/skel-babel/package.json @@ -17,7 +17,7 @@ "devDependencies": { "@babel/preset-env": "^7.28.3", "@babel/preset-react": "^7.23.3", - "@meteorjs/rspack": "^1.0.0", + "@meteorjs/rspack": "^1.0.0-beta.1", "@rsdoctor/rspack-plugin": "^1.2.3", "@rspack/cli": "^1.7.1", "@rspack/core": "^1.7.1", diff --git a/tools/static-assets/skel-blaze/package.json b/tools/static-assets/skel-blaze/package.json index 8b59baee14..4d1b0a785e 100644 --- a/tools/static-assets/skel-blaze/package.json +++ b/tools/static-assets/skel-blaze/package.json @@ -14,7 +14,7 @@ "meteor-node-stubs": "^1.2.12" }, "devDependencies": { - "@meteorjs/rspack": "^1.0.0", + "@meteorjs/rspack": "^1.0.0-beta.1", "@rsdoctor/rspack-plugin": "^1.2.3", "@rspack/cli": "^1.7.1", "@rspack/core": "^1.7.1", diff --git a/tools/static-assets/skel-chakra-ui/package.json b/tools/static-assets/skel-chakra-ui/package.json index 121244d19f..6e3e3f7c8d 100644 --- a/tools/static-assets/skel-chakra-ui/package.json +++ b/tools/static-assets/skel-chakra-ui/package.json @@ -21,7 +21,7 @@ "react-dom": "^18.2.0" }, "devDependencies": { - "@meteorjs/rspack": "^1.0.0", + "@meteorjs/rspack": "^1.0.0-beta.1", "@rsdoctor/rspack-plugin": "^1.2.3", "@rspack/cli": "^1.7.1", "@rspack/core": "^1.7.1", diff --git a/tools/static-assets/skel-coffeescript/package.json b/tools/static-assets/skel-coffeescript/package.json index 0b265c46a6..af84b38f91 100644 --- a/tools/static-assets/skel-coffeescript/package.json +++ b/tools/static-assets/skel-coffeescript/package.json @@ -15,7 +15,7 @@ "react-dom": "^18.2.0" }, "devDependencies": { - "@meteorjs/rspack": "^1.0.0", + "@meteorjs/rspack": "^1.0.0-beta.1", "@rsdoctor/rspack-plugin": "^1.2.3", "@rspack/cli": "^1.7.1", "@rspack/core": "^1.7.1", diff --git a/tools/static-assets/skel-full/package.json b/tools/static-assets/skel-full/package.json index b98f4d8c4e..8c77c7dbb5 100644 --- a/tools/static-assets/skel-full/package.json +++ b/tools/static-assets/skel-full/package.json @@ -12,7 +12,7 @@ "meteor-node-stubs": "^1.2.12" }, "devDependencies": { - "@meteorjs/rspack": "^1.0.0", + "@meteorjs/rspack": "^1.0.0-beta.1", "@rsdoctor/rspack-plugin": "^1.2.3", "@rspack/cli": "^1.7.1", "@rspack/core": "^1.7.1", diff --git a/tools/static-assets/skel-react/package.json b/tools/static-assets/skel-react/package.json index 52c87bbea6..77d0137aaf 100644 --- a/tools/static-assets/skel-react/package.json +++ b/tools/static-assets/skel-react/package.json @@ -15,7 +15,7 @@ "react-dom": "^18.2.0" }, "devDependencies": { - "@meteorjs/rspack": "^1.0.0", + "@meteorjs/rspack": "^1.0.0-beta.1", "@rsdoctor/rspack-plugin": "^1.2.3", "@rspack/cli": "^1.7.1", "@rspack/core": "^1.7.1", diff --git a/tools/static-assets/skel-solid/package.json b/tools/static-assets/skel-solid/package.json index c6e22545f4..6e2cc4ba61 100644 --- a/tools/static-assets/skel-solid/package.json +++ b/tools/static-assets/skel-solid/package.json @@ -14,7 +14,7 @@ "picocolors": "^1.1.1" }, "devDependencies": { - "@meteorjs/rspack": "^1.0.0", + "@meteorjs/rspack": "^1.0.0-beta.1", "@rsdoctor/rspack-plugin": "^1.2.3", "@rspack/cli": "^1.7.1", "@rspack/core": "^1.7.1", diff --git a/tools/static-assets/skel-svelte/package.json b/tools/static-assets/skel-svelte/package.json index 41a8beb0a7..937c185741 100644 --- a/tools/static-assets/skel-svelte/package.json +++ b/tools/static-assets/skel-svelte/package.json @@ -13,7 +13,7 @@ "meteor-node-stubs": "^1.2.12" }, "devDependencies": { - "@meteorjs/rspack": "^1.0.0", + "@meteorjs/rspack": "^1.0.0-beta.1", "@rsdoctor/rspack-plugin": "^1.2.3", "@rspack/cli": "^1.7.1", "@rspack/core": "^1.7.1", diff --git a/tools/static-assets/skel-tailwind/package.json b/tools/static-assets/skel-tailwind/package.json index ce8ed74b51..482a0bcab0 100644 --- a/tools/static-assets/skel-tailwind/package.json +++ b/tools/static-assets/skel-tailwind/package.json @@ -16,7 +16,7 @@ "react-dom": "^17.0.2" }, "devDependencies": { - "@meteorjs/rspack": "^1.0.0", + "@meteorjs/rspack": "^1.0.0-beta.1", "@rsdoctor/rspack-plugin": "^1.2.3", "@rspack/cli": "^1.7.1", "@rspack/core": "^1.7.1", diff --git a/tools/static-assets/skel-typescript/package.json b/tools/static-assets/skel-typescript/package.json index 456a6a990a..af8e9e10c5 100644 --- a/tools/static-assets/skel-typescript/package.json +++ b/tools/static-assets/skel-typescript/package.json @@ -15,7 +15,7 @@ "react-dom": "^18.2.0" }, "devDependencies": { - "@meteorjs/rspack": "^1.0.0", + "@meteorjs/rspack": "^1.0.0-beta.1", "@rsdoctor/rspack-plugin": "^1.2.3", "@rspack/cli": "^1.7.1", "@rspack/core": "^1.7.1", diff --git a/tools/static-assets/skel-vue/package.json b/tools/static-assets/skel-vue/package.json index 4686b5ed7e..0f4debcbdd 100644 --- a/tools/static-assets/skel-vue/package.json +++ b/tools/static-assets/skel-vue/package.json @@ -17,7 +17,7 @@ "vue-router": "^4.2.5" }, "devDependencies": { - "@meteorjs/rspack": "^1.0.0", + "@meteorjs/rspack": "^1.0.0-beta.1", "@rsdoctor/rspack-plugin": "^1.2.3", "@rspack/cli": "^1.7.1", "@rspack/core": "^1.7.1", From 508539a0984f0b295111f7e0bfc200ec3a9d0e89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Mon, 16 Feb 2026 12:04:50 +0100 Subject: [PATCH 072/166] remove redundant mainClient and mainServer default values from rspack config --- packages/rspack/lib/config.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/rspack/lib/config.js b/packages/rspack/lib/config.js index 442f41d7d4..05d4c7d8a1 100644 --- a/packages/rspack/lib/config.js +++ b/packages/rspack/lib/config.js @@ -347,8 +347,6 @@ export function configureMeteorForRspack() { testClient: `${RSPACK_BUILD_CONTEXT}/${testClientModule}`, testServer: `${RSPACK_BUILD_CONTEXT}/${testServerModule}`, }) || { - mainClient: `${RSPACK_BUILD_CONTEXT}/${testClientModule}`, - mainServer: `${RSPACK_BUILD_CONTEXT}/${testServerModule}`, testClient: `${RSPACK_BUILD_CONTEXT}/${testClientModule}`, testServer: `${RSPACK_BUILD_CONTEXT}/${testServerModule}`, }), From 74bee0a97dd2b7b06360071929beaed1d6140bf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Mon, 16 Feb 2026 12:32:45 +0100 Subject: [PATCH 073/166] ensure `isMeteorAppTestFullApp` logic sets correct entrypoints in rspack config --- packages/rspack/lib/config.js | 12 +++++++++--- packages/rspack/rspack_plugin.js | 1 - 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/rspack/lib/config.js b/packages/rspack/lib/config.js index 05d4c7d8a1..7785925229 100644 --- a/packages/rspack/lib/config.js +++ b/packages/rspack/lib/config.js @@ -17,6 +17,7 @@ const { isMeteorAppBuild, isMeteorAppDebug, isMeteorAppTest, + isMeteorAppTestFullApp, isMeteorAppConfigModernVerbose, isMeteorBlazeProject, isMeteorLessProject, @@ -338,12 +339,10 @@ export function configureMeteorForRspack() { isServer: true, }); - const appEntrypoints = { + let appEntrypoints = { mainClient: `${RSPACK_BUILD_CONTEXT}/${mainClientModule}`, mainServer: `${RSPACK_BUILD_CONTEXT}/${mainServerModule}`, ...((isTestModule && { - mainClient: `${RSPACK_BUILD_CONTEXT}/${testClientModule}`, - mainServer: `${RSPACK_BUILD_CONTEXT}/${testServerModule}`, testClient: `${RSPACK_BUILD_CONTEXT}/${testClientModule}`, testServer: `${RSPACK_BUILD_CONTEXT}/${testServerModule}`, }) || { @@ -351,6 +350,13 @@ export function configureMeteorForRspack() { testServer: `${RSPACK_BUILD_CONTEXT}/${testServerModule}`, }), }; + if (isMeteorAppTestFullApp()) { + appEntrypoints = { + ...appEntrypoints, + mainClient: `${RSPACK_BUILD_CONTEXT}/${testClientModule}`, + mainServer: `${RSPACK_BUILD_CONTEXT}/${testServerModule}`, + }; + } // Set entry points in environment variables if they exist setMeteorAppEntrypoints(appEntrypoints); diff --git a/packages/rspack/rspack_plugin.js b/packages/rspack/rspack_plugin.js index f456e7cf43..26f63680b6 100644 --- a/packages/rspack/rspack_plugin.js +++ b/packages/rspack/rspack_plugin.js @@ -67,7 +67,6 @@ const { getMeteorAppEntrypoints, isMeteorAppTest, isMeteorAppTestWatch, - isMeteorAppTestFullApp, isMeteorAppDevelopment, isMeteorAppProduction, isMeteorAppDebug, From ec57a45f7a292157a64f94d7074db07fac4e6d20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Mon, 16 Feb 2026 16:02:45 +0100 Subject: [PATCH 074/166] ensure `globalImportPath` support in eager test file generation and update entry logic for client and server in rspack config --- npm-packages/meteor-rspack/lib/test.js | 25 +++++++++++--------- npm-packages/meteor-rspack/rspack.config.js | 26 ++++++++++++++++----- 2 files changed, 34 insertions(+), 17 deletions(-) diff --git a/npm-packages/meteor-rspack/lib/test.js b/npm-packages/meteor-rspack/lib/test.js index 3c2644b337..e35c7e17dc 100644 --- a/npm-packages/meteor-rspack/lib/test.js +++ b/npm-packages/meteor-rspack/lib/test.js @@ -13,13 +13,14 @@ const { createIgnoreRegex, createIgnoreGlobConfig } = require("./ignore.js"); * @returns {string} The path to the generated file */ const generateEagerTestFile = ({ - isAppTest, - projectDir, - buildContext, - ignoreEntries: inIgnoreEntries = [], - prefix: inPrefix = '', - extraEntry, - }) => { + isAppTest, + projectDir, + buildContext, + ignoreEntries: inIgnoreEntries = [], + prefix: inPrefix = '', + extraEntry, + globalImportPath, +}) => { const distDir = path.resolve(projectDir, ".meteor/local/test"); if (!fs.existsSync(distDir)) { fs.mkdirSync(distDir, { recursive: true }); @@ -49,7 +50,9 @@ const generateEagerTestFile = ({ ? "/\\.app-(?:test|spec)s?\\.[^.]+$/" : "/\\.(?:test|spec)s?\\.[^.]+$/"; - const content = `{ + const content = `${ + globalImportPath ? `import '${globalImportPath}';\n\n` : '' + }{ const ctx = import.meta.webpackContext('/', { recursive: true, regExp: ${regExp}, @@ -60,14 +63,14 @@ const generateEagerTestFile = ({ ${ extraEntry ? `const extra = import.meta.webpackContext('${path.dirname( - extraEntry - )}', { + extraEntry + )}', { recursive: false, regExp: ${new RegExp(`${path.basename(extraEntry)}$`).toString()}, mode: 'eager', }); extra.keys().forEach(extra);` - : '' + : "" } }`; diff --git a/npm-packages/meteor-rspack/rspack.config.js b/npm-packages/meteor-rspack/rspack.config.js index 0e05e47f8f..f24536ef4c 100644 --- a/npm-packages/meteor-rspack/rspack.config.js +++ b/npm-packages/meteor-rspack/rspack.config.js @@ -429,7 +429,7 @@ module.exports = async function (inMeteor = {}, argv = {}) { const lazyCompilationConfig = { lazyCompilation: false }; const clientEntry = - isTest && isTestEager && isTestFullApp + isClient && isTest && isTestEager && isTestFullApp ? generateEagerTestFile({ isAppTest: true, projectDir, @@ -437,8 +437,9 @@ module.exports = async function (inMeteor = {}, argv = {}) { ignoreEntries: [...meteorIgnoreEntries, "**/server/**"], prefix: "client", extraEntry: path.resolve(process.cwd(), Meteor.mainClientEntry), + globalImportPath: path.resolve(projectDir, buildContext, entryPath), }) - : isTest && isTestEager + : isClient && isTest && isTestEager ? generateEagerTestFile({ isAppTest: false, isClient: true, @@ -446,10 +447,16 @@ module.exports = async function (inMeteor = {}, argv = {}) { buildContext, ignoreEntries: [...meteorIgnoreEntries, "**/server/**"], prefix: "client", + globalImportPath: path.resolve(projectDir, buildContext, entryPath), }) - : isTest && testEntry + : isClient && isTest && testEntry ? path.resolve(process.cwd(), testEntry) : path.resolve(process.cwd(), buildContext, entryPath); + console.log( + "--> (rspack.config.js-Line: 431)\n clientEntry: ", + clientEntry, + entryPath + ); const clientNameConfig = `[${(isTest && 'test-') || ''}client-rspack]`; // Base client config let clientConfig = { @@ -548,26 +555,33 @@ module.exports = async function (inMeteor = {}, argv = {}) { }; const serverEntry = - isTest && isTestEager && isTestFullApp + isServer && isTest && isTestEager && isTestFullApp ? generateEagerTestFile({ isAppTest: true, projectDir, buildContext, ignoreEntries: [...meteorIgnoreEntries, "**/client/**"], prefix: "server", + globalImportPath: path.resolve(projectDir, buildContext, entryPath), }) - : isTest && isTestEager + : isServer && isTest && isTestEager ? generateEagerTestFile({ isAppTest: false, projectDir, buildContext, ignoreEntries: [...meteorIgnoreEntries, "**/client/**"], prefix: "server", + globalImportPath: path.resolve(projectDir, buildContext, entryPath), }) - : isTest && testEntry + : isServer && isTest && testEntry ? path.resolve(process.cwd(), testEntry) : path.resolve(projectDir, buildContext, entryPath); const serverNameConfig = `[${(isTest && 'test-') || ''}server-rspack]`; + console.log( + "--> (rspack.config.js-Line: 576)\n serverEntry: ", + serverEntry, + entryPath + ); // Base server config let serverConfig = { name: serverNameConfig, From 1fd34ce41c2de4cdb5e5a47572e517e64b5b5c15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Mon, 16 Feb 2026 16:04:44 +0100 Subject: [PATCH 075/166] update @meteorjs/rspack to version 1.1.0-beta.2 --- npm-packages/meteor-rspack/package-lock.json | 4 ++-- npm-packages/meteor-rspack/package.json | 2 +- packages/rspack/lib/constants.js | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/npm-packages/meteor-rspack/package-lock.json b/npm-packages/meteor-rspack/package-lock.json index 9008a74422..cf2ece90f6 100644 --- a/npm-packages/meteor-rspack/package-lock.json +++ b/npm-packages/meteor-rspack/package-lock.json @@ -1,12 +1,12 @@ { "name": "@meteorjs/rspack", - "version": "1.1.0-beta.1", + "version": "1.1.0-beta.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@meteorjs/rspack", - "version": "1.1.0-beta.1", + "version": "1.1.0-beta.2", "license": "ISC", "dependencies": { "fast-deep-equal": "^3.1.3", diff --git a/npm-packages/meteor-rspack/package.json b/npm-packages/meteor-rspack/package.json index de166bafa1..1306b93e26 100644 --- a/npm-packages/meteor-rspack/package.json +++ b/npm-packages/meteor-rspack/package.json @@ -1,6 +1,6 @@ { "name": "@meteorjs/rspack", - "version": "1.1.0-beta.1", + "version": "1.1.0-beta.2", "description": "Configuration logic for using Rspack in Meteor projects", "main": "index.js", "type": "commonjs", diff --git a/packages/rspack/lib/constants.js b/packages/rspack/lib/constants.js index 9ca9e761f6..3a16c20419 100644 --- a/packages/rspack/lib/constants.js +++ b/packages/rspack/lib/constants.js @@ -5,7 +5,7 @@ export const DEFAULT_RSPACK_VERSION = '1.7.1'; -export const DEFAULT_METEOR_RSPACK_VERSION = '1.1.0-beta.1'; +export const DEFAULT_METEOR_RSPACK_VERSION = '1.1.0-beta.2'; export const DEFAULT_METEOR_RSPACK_REACT_HMR_VERSION = '1.4.3'; From 1a3ce2b7f6d0d77df8c8b9162b5c973215706ef9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Mon, 16 Feb 2026 17:10:54 +0100 Subject: [PATCH 076/166] re-run checks From b4c99699ba421cc0d0086a3df3971c1d6627750b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 17 Feb 2026 17:10:56 +0100 Subject: [PATCH 077/166] add support for `meteorIgnoreEntries` to refine file exclusion logic in test generation and adjust rspack configuration accordingly --- npm-packages/meteor-rspack/lib/test.js | 24 +++++++++++++++++---- npm-packages/meteor-rspack/rspack.config.js | 12 +++++++---- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/npm-packages/meteor-rspack/lib/test.js b/npm-packages/meteor-rspack/lib/test.js index e35c7e17dc..2cade65b7e 100644 --- a/npm-packages/meteor-rspack/lib/test.js +++ b/npm-packages/meteor-rspack/lib/test.js @@ -9,6 +9,7 @@ const { createIgnoreRegex, createIgnoreGlobConfig } = require("./ignore.js"); * @param {string} options.projectDir - The project directory * @param {string} options.buildContext - The build context * @param {string[]} options.ignoreEntries - Array of ignore patterns + * @param {string[]} options.meteorIgnoreEntries - Array of meteor ignore patterns * @param {string} options.extraEntry - Extra entry to load * @returns {string} The path to the generated file */ @@ -17,6 +18,7 @@ const generateEagerTestFile = ({ projectDir, buildContext, ignoreEntries: inIgnoreEntries = [], + meteorIgnoreEntries: inMeteorIgnoreEntries = [], prefix: inPrefix = '', extraEntry, globalImportPath, @@ -41,6 +43,15 @@ const generateEagerTestFile = ({ createIgnoreGlobConfig(ignoreEntries) ); + const excludeMeteorIgnoreRegex = createIgnoreRegex( + createIgnoreGlobConfig(inMeteorIgnoreEntries) + ); + console.log( + "--> (test.js-Line: 41)\n createIgnoreGlobConfig(ignoreEntries): ", + createIgnoreGlobConfig(ignoreEntries), + excludeFoldersRegex.toString() + ); + const prefix = (inPrefix && `${inPrefix}-`) || ""; const filename = isAppTest ? `${prefix}eager-app-tests.mjs` @@ -51,15 +62,20 @@ const generateEagerTestFile = ({ : "/\\.(?:test|spec)s?\\.[^.]+$/"; const content = `${ - globalImportPath ? `import '${globalImportPath}';\n\n` : '' - }{ - const ctx = import.meta.webpackContext('/', { + globalImportPath ? `import '${globalImportPath}';\n\n` : "" + } + const MeteorIgnoreRegex = ${excludeMeteorIgnoreRegex.toString()}; + { + const ctx = import.meta.webpackContext('${projectDir}', { recursive: true, regExp: ${regExp}, exclude: ${excludeFoldersRegex.toString()}, mode: 'eager', }); - ctx.keys().forEach(ctx); + ctx.keys().filter((k) => { + // Only exclude based on *relative* path segments. + return !MeteorIgnoreRegex.test(rel); + }).forEach(ctx); ${ extraEntry ? `const extra = import.meta.webpackContext('${path.dirname( diff --git a/npm-packages/meteor-rspack/rspack.config.js b/npm-packages/meteor-rspack/rspack.config.js index f24536ef4c..0bbb3a0eb9 100644 --- a/npm-packages/meteor-rspack/rspack.config.js +++ b/npm-packages/meteor-rspack/rspack.config.js @@ -434,7 +434,8 @@ module.exports = async function (inMeteor = {}, argv = {}) { isAppTest: true, projectDir, buildContext, - ignoreEntries: [...meteorIgnoreEntries, "**/server/**"], + ignoreEntries: ["**/server/**"], + meteorIgnoreEntries, prefix: "client", extraEntry: path.resolve(process.cwd(), Meteor.mainClientEntry), globalImportPath: path.resolve(projectDir, buildContext, entryPath), @@ -445,7 +446,8 @@ module.exports = async function (inMeteor = {}, argv = {}) { isClient: true, projectDir, buildContext, - ignoreEntries: [...meteorIgnoreEntries, "**/server/**"], + ignoreEntries: ["**/server/**"], + meteorIgnoreEntries, prefix: "client", globalImportPath: path.resolve(projectDir, buildContext, entryPath), }) @@ -560,7 +562,8 @@ module.exports = async function (inMeteor = {}, argv = {}) { isAppTest: true, projectDir, buildContext, - ignoreEntries: [...meteorIgnoreEntries, "**/client/**"], + ignoreEntries: ["**/client/**"], + meteorIgnoreEntries, prefix: "server", globalImportPath: path.resolve(projectDir, buildContext, entryPath), }) @@ -569,7 +572,8 @@ module.exports = async function (inMeteor = {}, argv = {}) { isAppTest: false, projectDir, buildContext, - ignoreEntries: [...meteorIgnoreEntries, "**/client/**"], + ignoreEntries: ["**/client/**"], + meteorIgnoreEntries, prefix: "server", globalImportPath: path.resolve(projectDir, buildContext, entryPath), }) From b92aa93ca87433bb3025a282f7e73a4ae1d466be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 17 Feb 2026 17:15:47 +0100 Subject: [PATCH 078/166] refine relative path handling in test file filtering logic --- npm-packages/meteor-rspack/lib/test.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/npm-packages/meteor-rspack/lib/test.js b/npm-packages/meteor-rspack/lib/test.js index 2cade65b7e..db4cfb9a86 100644 --- a/npm-packages/meteor-rspack/lib/test.js +++ b/npm-packages/meteor-rspack/lib/test.js @@ -73,6 +73,9 @@ const generateEagerTestFile = ({ mode: 'eager', }); ctx.keys().filter((k) => { + // Make the check strictly relative to the context root. + // If k is absolute and starts with root, strip it; if it's already relative, leave it. + const rel = k.startsWith('${projectDir}') ? k.slice(${projectDir.length}) : k.replace(/^\\.\\//, ''); // Only exclude based on *relative* path segments. return !MeteorIgnoreRegex.test(rel); }).forEach(ctx); From 5902a86aece8b8b9efc3e5b891da2814145ae200 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 17 Feb 2026 17:16:52 +0100 Subject: [PATCH 079/166] remove debug logs and refine relative path handling in test file exclusion logic --- npm-packages/meteor-rspack/lib/test.js | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/npm-packages/meteor-rspack/lib/test.js b/npm-packages/meteor-rspack/lib/test.js index db4cfb9a86..46537314ab 100644 --- a/npm-packages/meteor-rspack/lib/test.js +++ b/npm-packages/meteor-rspack/lib/test.js @@ -42,15 +42,10 @@ const generateEagerTestFile = ({ const excludeFoldersRegex = createIgnoreRegex( createIgnoreGlobConfig(ignoreEntries) ); - + // Create regex from meteor ignore entries const excludeMeteorIgnoreRegex = createIgnoreRegex( createIgnoreGlobConfig(inMeteorIgnoreEntries) ); - console.log( - "--> (test.js-Line: 41)\n createIgnoreGlobConfig(ignoreEntries): ", - createIgnoreGlobConfig(ignoreEntries), - excludeFoldersRegex.toString() - ); const prefix = (inPrefix && `${inPrefix}-`) || ""; const filename = isAppTest @@ -75,7 +70,9 @@ const generateEagerTestFile = ({ ctx.keys().filter((k) => { // Make the check strictly relative to the context root. // If k is absolute and starts with root, strip it; if it's already relative, leave it. - const rel = k.startsWith('${projectDir}') ? k.slice(${projectDir.length}) : k.replace(/^\\.\\//, ''); + const rel = k.startsWith('${projectDir}') ? k.slice(${ + projectDir.length + }) : k.replace(/^\\.\\//, ''); // Only exclude based on *relative* path segments. return !MeteorIgnoreRegex.test(rel); }).forEach(ctx); From 8aed659ce6ee41805cd135a32e7a35e712e8b64e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 17 Feb 2026 17:38:10 +0100 Subject: [PATCH 080/166] add test coverage on the regression fixed --- npm-packages/meteor-rspack/package-lock.json | 4 ++-- npm-packages/meteor-rspack/package.json | 2 +- npm-packages/meteor-rspack/rspack.config.js | 10 ---------- packages/rspack/lib/constants.js | 2 +- tools/modern-tests/apps/react-router/.meteorignore | 1 + .../apps/react-router/react-router/server.app-test.js | 1 + 6 files changed, 6 insertions(+), 14 deletions(-) create mode 100644 tools/modern-tests/apps/react-router/.meteorignore create mode 100644 tools/modern-tests/apps/react-router/react-router/server.app-test.js diff --git a/npm-packages/meteor-rspack/package-lock.json b/npm-packages/meteor-rspack/package-lock.json index cf2ece90f6..e4d92c823d 100644 --- a/npm-packages/meteor-rspack/package-lock.json +++ b/npm-packages/meteor-rspack/package-lock.json @@ -1,12 +1,12 @@ { "name": "@meteorjs/rspack", - "version": "1.1.0-beta.2", + "version": "1.1.0-beta.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@meteorjs/rspack", - "version": "1.1.0-beta.2", + "version": "1.1.0-beta.4", "license": "ISC", "dependencies": { "fast-deep-equal": "^3.1.3", diff --git a/npm-packages/meteor-rspack/package.json b/npm-packages/meteor-rspack/package.json index 1306b93e26..ace6b6ee48 100644 --- a/npm-packages/meteor-rspack/package.json +++ b/npm-packages/meteor-rspack/package.json @@ -1,6 +1,6 @@ { "name": "@meteorjs/rspack", - "version": "1.1.0-beta.2", + "version": "1.1.0-beta.4", "description": "Configuration logic for using Rspack in Meteor projects", "main": "index.js", "type": "commonjs", diff --git a/npm-packages/meteor-rspack/rspack.config.js b/npm-packages/meteor-rspack/rspack.config.js index 0bbb3a0eb9..4e92ad24d4 100644 --- a/npm-packages/meteor-rspack/rspack.config.js +++ b/npm-packages/meteor-rspack/rspack.config.js @@ -454,11 +454,6 @@ module.exports = async function (inMeteor = {}, argv = {}) { : isClient && isTest && testEntry ? path.resolve(process.cwd(), testEntry) : path.resolve(process.cwd(), buildContext, entryPath); - console.log( - "--> (rspack.config.js-Line: 431)\n clientEntry: ", - clientEntry, - entryPath - ); const clientNameConfig = `[${(isTest && 'test-') || ''}client-rspack]`; // Base client config let clientConfig = { @@ -581,11 +576,6 @@ module.exports = async function (inMeteor = {}, argv = {}) { ? path.resolve(process.cwd(), testEntry) : path.resolve(projectDir, buildContext, entryPath); const serverNameConfig = `[${(isTest && 'test-') || ''}server-rspack]`; - console.log( - "--> (rspack.config.js-Line: 576)\n serverEntry: ", - serverEntry, - entryPath - ); // Base server config let serverConfig = { name: serverNameConfig, diff --git a/packages/rspack/lib/constants.js b/packages/rspack/lib/constants.js index 3a16c20419..91e577810c 100644 --- a/packages/rspack/lib/constants.js +++ b/packages/rspack/lib/constants.js @@ -5,7 +5,7 @@ export const DEFAULT_RSPACK_VERSION = '1.7.1'; -export const DEFAULT_METEOR_RSPACK_VERSION = '1.1.0-beta.2'; +export const DEFAULT_METEOR_RSPACK_VERSION = '1.1.0-beta.4'; export const DEFAULT_METEOR_RSPACK_REACT_HMR_VERSION = '1.4.3'; diff --git a/tools/modern-tests/apps/react-router/.meteorignore b/tools/modern-tests/apps/react-router/.meteorignore new file mode 100644 index 0000000000..b6cff6faa1 --- /dev/null +++ b/tools/modern-tests/apps/react-router/.meteorignore @@ -0,0 +1 @@ +react-router* diff --git a/tools/modern-tests/apps/react-router/react-router/server.app-test.js b/tools/modern-tests/apps/react-router/react-router/server.app-test.js new file mode 100644 index 0000000000..609d8d8cad --- /dev/null +++ b/tools/modern-tests/apps/react-router/react-router/server.app-test.js @@ -0,0 +1 @@ +throw new Error("test should be ignored by eager test loading"); From 4391d4959abe070bcc50e5e87773aaad2251efc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 17 Feb 2026 18:17:56 +0100 Subject: [PATCH 081/166] update @meteorjs/rspack to 1.1.0-beta.5 and refine test file exclusion logic --- npm-packages/meteor-rspack/lib/ignore.js | 2 +- npm-packages/meteor-rspack/lib/test.js | 26 ++++++++++--------- npm-packages/meteor-rspack/package-lock.json | 4 +-- npm-packages/meteor-rspack/package.json | 2 +- packages/rspack/lib/constants.js | 2 +- .../react-router-wxyz/ignore.app-test.js | 5 ++++ .../react-router/server.app-test.js | 1 - 7 files changed, 24 insertions(+), 18 deletions(-) create mode 100644 tools/modern-tests/apps/react-router/react-router-wxyz/ignore.app-test.js delete mode 100644 tools/modern-tests/apps/react-router/react-router/server.app-test.js diff --git a/npm-packages/meteor-rspack/lib/ignore.js b/npm-packages/meteor-rspack/lib/ignore.js index 17058cce19..3294c28f5c 100644 --- a/npm-packages/meteor-rspack/lib/ignore.js +++ b/npm-packages/meteor-rspack/lib/ignore.js @@ -117,7 +117,7 @@ function createIgnoreRegex(globPatterns) { // For absolute paths, we don't want to force the pattern to match from the beginning // but we still want to ensure it matches to the end of the path segment - regexPattern = '(?:^|/)' + regexPattern + '$'; + regexPattern = '(?:^|/)' + regexPattern + (pattern.endsWith('*') ? '' : '$'); return regexPattern; }).filter(pattern => pattern !== null); diff --git a/npm-packages/meteor-rspack/lib/test.js b/npm-packages/meteor-rspack/lib/test.js index 46537314ab..432551af0d 100644 --- a/npm-packages/meteor-rspack/lib/test.js +++ b/npm-packages/meteor-rspack/lib/test.js @@ -43,9 +43,9 @@ const generateEagerTestFile = ({ createIgnoreGlobConfig(ignoreEntries) ); // Create regex from meteor ignore entries - const excludeMeteorIgnoreRegex = createIgnoreRegex( - createIgnoreGlobConfig(inMeteorIgnoreEntries) - ); + const excludeMeteorIgnoreRegex = inMeteorIgnoreEntries.length > 0 + ? createIgnoreRegex(createIgnoreGlobConfig(inMeteorIgnoreEntries)) + : null; const prefix = (inPrefix && `${inPrefix}-`) || ""; const filename = isAppTest @@ -58,9 +58,12 @@ const generateEagerTestFile = ({ const content = `${ globalImportPath ? `import '${globalImportPath}';\n\n` : "" + }${ + excludeMeteorIgnoreRegex + ? `const MeteorIgnoreRegex = ${excludeMeteorIgnoreRegex.toString()};` + : "" } - const MeteorIgnoreRegex = ${excludeMeteorIgnoreRegex.toString()}; - { +{ const ctx = import.meta.webpackContext('${projectDir}', { recursive: true, regExp: ${regExp}, @@ -68,13 +71,12 @@ const generateEagerTestFile = ({ mode: 'eager', }); ctx.keys().filter((k) => { - // Make the check strictly relative to the context root. - // If k is absolute and starts with root, strip it; if it's already relative, leave it. - const rel = k.startsWith('${projectDir}') ? k.slice(${ - projectDir.length - }) : k.replace(/^\\.\\//, ''); - // Only exclude based on *relative* path segments. - return !MeteorIgnoreRegex.test(rel); + ${ + excludeMeteorIgnoreRegex + ? `// Only exclude based on *relative* path segments. + return !MeteorIgnoreRegex.test(k);` + : "return true;" + } }).forEach(ctx); ${ extraEntry diff --git a/npm-packages/meteor-rspack/package-lock.json b/npm-packages/meteor-rspack/package-lock.json index e4d92c823d..1213343e3e 100644 --- a/npm-packages/meteor-rspack/package-lock.json +++ b/npm-packages/meteor-rspack/package-lock.json @@ -1,12 +1,12 @@ { "name": "@meteorjs/rspack", - "version": "1.1.0-beta.4", + "version": "1.1.0-beta.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@meteorjs/rspack", - "version": "1.1.0-beta.4", + "version": "1.1.0-beta.5", "license": "ISC", "dependencies": { "fast-deep-equal": "^3.1.3", diff --git a/npm-packages/meteor-rspack/package.json b/npm-packages/meteor-rspack/package.json index ace6b6ee48..6978169c53 100644 --- a/npm-packages/meteor-rspack/package.json +++ b/npm-packages/meteor-rspack/package.json @@ -1,6 +1,6 @@ { "name": "@meteorjs/rspack", - "version": "1.1.0-beta.4", + "version": "1.1.0-beta.5", "description": "Configuration logic for using Rspack in Meteor projects", "main": "index.js", "type": "commonjs", diff --git a/packages/rspack/lib/constants.js b/packages/rspack/lib/constants.js index 91e577810c..3ceefd78fd 100644 --- a/packages/rspack/lib/constants.js +++ b/packages/rspack/lib/constants.js @@ -5,7 +5,7 @@ export const DEFAULT_RSPACK_VERSION = '1.7.1'; -export const DEFAULT_METEOR_RSPACK_VERSION = '1.1.0-beta.4'; +export const DEFAULT_METEOR_RSPACK_VERSION = '1.1.0-beta.5'; export const DEFAULT_METEOR_RSPACK_REACT_HMR_VERSION = '1.4.3'; diff --git a/tools/modern-tests/apps/react-router/react-router-wxyz/ignore.app-test.js b/tools/modern-tests/apps/react-router/react-router-wxyz/ignore.app-test.js new file mode 100644 index 0000000000..dfa87e115b --- /dev/null +++ b/tools/modern-tests/apps/react-router/react-router-wxyz/ignore.app-test.js @@ -0,0 +1,5 @@ +describe("Ignore", () => { + it("should not run as ignored", () => { + throw new Error("test should be ignored by eager test loading"); + }); +}); diff --git a/tools/modern-tests/apps/react-router/react-router/server.app-test.js b/tools/modern-tests/apps/react-router/react-router/server.app-test.js deleted file mode 100644 index 609d8d8cad..0000000000 --- a/tools/modern-tests/apps/react-router/react-router/server.app-test.js +++ /dev/null @@ -1 +0,0 @@ -throw new Error("test should be ignored by eager test loading"); From a1eb3c237a74aa4572206b28ddfd8e2b87f8d9ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 17 Feb 2026 18:43:38 +0100 Subject: [PATCH 082/166] refine test file exclusion logic by expanding ignore patterns and updating regex handling --- npm-packages/meteor-rspack/lib/ignore.js | 2 +- npm-packages/meteor-rspack/package-lock.json | 4 ++-- npm-packages/meteor-rspack/package.json | 2 +- packages/rspack/lib/constants.js | 2 +- tools/modern-tests/apps/react-router/.meteorignore | 5 +++++ .../apps/react-router/file-to-ignore.app-test.js | 5 +++++ .../apps/react-router/folder-to-ignore/ignore.app-test.js | 5 +++++ .../apps/react-router/prefix-test/ignore.app-test.js | 5 +++++ .../apps/react-router/react-router-wxyz/ignore.app-test.js | 2 +- .../apps/react-router/some-test-suffix/ignore.app-test.js | 5 +++++ .../react-router/some/nested/glob-ignore/ignore.app-test.js | 5 +++++ 11 files changed, 36 insertions(+), 6 deletions(-) create mode 100644 tools/modern-tests/apps/react-router/file-to-ignore.app-test.js create mode 100644 tools/modern-tests/apps/react-router/folder-to-ignore/ignore.app-test.js create mode 100644 tools/modern-tests/apps/react-router/prefix-test/ignore.app-test.js create mode 100644 tools/modern-tests/apps/react-router/some-test-suffix/ignore.app-test.js create mode 100644 tools/modern-tests/apps/react-router/some/nested/glob-ignore/ignore.app-test.js diff --git a/npm-packages/meteor-rspack/lib/ignore.js b/npm-packages/meteor-rspack/lib/ignore.js index 3294c28f5c..7abc3ba959 100644 --- a/npm-packages/meteor-rspack/lib/ignore.js +++ b/npm-packages/meteor-rspack/lib/ignore.js @@ -117,7 +117,7 @@ function createIgnoreRegex(globPatterns) { // For absolute paths, we don't want to force the pattern to match from the beginning // but we still want to ensure it matches to the end of the path segment - regexPattern = '(?:^|/)' + regexPattern + (pattern.endsWith('*') ? '' : '$'); + regexPattern = '(?:^|/)' + regexPattern; return regexPattern; }).filter(pattern => pattern !== null); diff --git a/npm-packages/meteor-rspack/package-lock.json b/npm-packages/meteor-rspack/package-lock.json index 1213343e3e..4b2c4e1836 100644 --- a/npm-packages/meteor-rspack/package-lock.json +++ b/npm-packages/meteor-rspack/package-lock.json @@ -1,12 +1,12 @@ { "name": "@meteorjs/rspack", - "version": "1.1.0-beta.5", + "version": "1.1.0-beta.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@meteorjs/rspack", - "version": "1.1.0-beta.5", + "version": "1.1.0-beta.6", "license": "ISC", "dependencies": { "fast-deep-equal": "^3.1.3", diff --git a/npm-packages/meteor-rspack/package.json b/npm-packages/meteor-rspack/package.json index 6978169c53..71b4960bb7 100644 --- a/npm-packages/meteor-rspack/package.json +++ b/npm-packages/meteor-rspack/package.json @@ -1,6 +1,6 @@ { "name": "@meteorjs/rspack", - "version": "1.1.0-beta.5", + "version": "1.1.0-beta.6", "description": "Configuration logic for using Rspack in Meteor projects", "main": "index.js", "type": "commonjs", diff --git a/packages/rspack/lib/constants.js b/packages/rspack/lib/constants.js index 3ceefd78fd..c9073b740d 100644 --- a/packages/rspack/lib/constants.js +++ b/packages/rspack/lib/constants.js @@ -5,7 +5,7 @@ export const DEFAULT_RSPACK_VERSION = '1.7.1'; -export const DEFAULT_METEOR_RSPACK_VERSION = '1.1.0-beta.5'; +export const DEFAULT_METEOR_RSPACK_VERSION = '1.1.0-beta.6'; export const DEFAULT_METEOR_RSPACK_REACT_HMR_VERSION = '1.4.3'; diff --git a/tools/modern-tests/apps/react-router/.meteorignore b/tools/modern-tests/apps/react-router/.meteorignore index b6cff6faa1..6b53260afa 100644 --- a/tools/modern-tests/apps/react-router/.meteorignore +++ b/tools/modern-tests/apps/react-router/.meteorignore @@ -1 +1,6 @@ react-router* +folder-to-ignore/ +file-to-ignore.app-test.js +**/glob-ignore/*.app-test.js +prefix-* +*-suffix diff --git a/tools/modern-tests/apps/react-router/file-to-ignore.app-test.js b/tools/modern-tests/apps/react-router/file-to-ignore.app-test.js new file mode 100644 index 0000000000..2fca97e5e9 --- /dev/null +++ b/tools/modern-tests/apps/react-router/file-to-ignore.app-test.js @@ -0,0 +1,5 @@ +describe("file-to-ignore.app-test.js pattern", () => { + it("should not run as ignored", () => { + throw new Error("test should be ignored by eager test loading"); + }); +}); diff --git a/tools/modern-tests/apps/react-router/folder-to-ignore/ignore.app-test.js b/tools/modern-tests/apps/react-router/folder-to-ignore/ignore.app-test.js new file mode 100644 index 0000000000..f0c849ec65 --- /dev/null +++ b/tools/modern-tests/apps/react-router/folder-to-ignore/ignore.app-test.js @@ -0,0 +1,5 @@ +describe("folder-to-ignore/ pattern", () => { + it("should not run as ignored", () => { + throw new Error("test should be ignored by eager test loading"); + }); +}); diff --git a/tools/modern-tests/apps/react-router/prefix-test/ignore.app-test.js b/tools/modern-tests/apps/react-router/prefix-test/ignore.app-test.js new file mode 100644 index 0000000000..faeb6ad5ec --- /dev/null +++ b/tools/modern-tests/apps/react-router/prefix-test/ignore.app-test.js @@ -0,0 +1,5 @@ +describe("prefix-* pattern", () => { + it("should not run as ignored", () => { + throw new Error("test should be ignored by eager test loading"); + }); +}); diff --git a/tools/modern-tests/apps/react-router/react-router-wxyz/ignore.app-test.js b/tools/modern-tests/apps/react-router/react-router-wxyz/ignore.app-test.js index dfa87e115b..6252f64b5b 100644 --- a/tools/modern-tests/apps/react-router/react-router-wxyz/ignore.app-test.js +++ b/tools/modern-tests/apps/react-router/react-router-wxyz/ignore.app-test.js @@ -1,4 +1,4 @@ -describe("Ignore", () => { +describe("react-router* pattern", () => { it("should not run as ignored", () => { throw new Error("test should be ignored by eager test loading"); }); diff --git a/tools/modern-tests/apps/react-router/some-test-suffix/ignore.app-test.js b/tools/modern-tests/apps/react-router/some-test-suffix/ignore.app-test.js new file mode 100644 index 0000000000..22830f7edc --- /dev/null +++ b/tools/modern-tests/apps/react-router/some-test-suffix/ignore.app-test.js @@ -0,0 +1,5 @@ +describe("*-suffix pattern", () => { + it("should not run as ignored", () => { + throw new Error("test should be ignored by eager test loading"); + }); +}); diff --git a/tools/modern-tests/apps/react-router/some/nested/glob-ignore/ignore.app-test.js b/tools/modern-tests/apps/react-router/some/nested/glob-ignore/ignore.app-test.js new file mode 100644 index 0000000000..6b220fd38f --- /dev/null +++ b/tools/modern-tests/apps/react-router/some/nested/glob-ignore/ignore.app-test.js @@ -0,0 +1,5 @@ +describe("**/glob-ignore/*.app-test.js pattern", () => { + it("should not run as ignored", () => { + throw new Error("test should be ignored by eager test loading"); + }); +}); From 00d1e6f485c42591d0f0d11dadefda0c98c5e803 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Wed, 18 Feb 2026 16:55:21 +0100 Subject: [PATCH 083/166] expand and refine test exclusion patterns by updating `.meteorignore` and test file structure --- npm-packages/meteor-rspack/lib/test.js | 1 + .../apps/react-router/.meteorignore | 22 ++++++++++++++----- .../react-router/file-to-ignored.app-test.js | 5 +++++ ...th-multiple-extensions-ignored.app-test.js | 5 +++++ .../ignore.app-test.js | 2 +- .../apps/react-router/important.app-test.js | 7 ++++++ .../integration/test-ignored.app-test.js | 5 +++++ .../ignore.app-test.js | 2 +- .../ignore.app-test.js | 2 +- .../ignore.app-test.js | 2 +- .../nested/glob-ignore/ignore.app-test.js | 5 ----- .../nested/glob-ignored/ignore.app-test.js | 5 +++++ .../some/unit/test-ignored.app-test.js | 5 +++++ .../specific-file-ignored.app-test.js | 5 +++++ ...st.js => test-example-ignored.app-test.js} | 2 +- .../test.temp-ignored.app-test.js | 5 +++++ 16 files changed, 64 insertions(+), 16 deletions(-) create mode 100644 tools/modern-tests/apps/react-router/file-to-ignored.app-test.js create mode 100644 tools/modern-tests/apps/react-router/file-with-multiple-extensions-ignored.app-test.js rename tools/modern-tests/apps/react-router/{folder-to-ignore => folder-to-ignored}/ignore.app-test.js (72%) create mode 100644 tools/modern-tests/apps/react-router/important.app-test.js create mode 100644 tools/modern-tests/apps/react-router/integration/test-ignored.app-test.js rename tools/modern-tests/apps/react-router/{some-test-suffix => prefix-test-ignored}/ignore.app-test.js (72%) rename tools/modern-tests/apps/react-router/{react-router-wxyz => react-router-wxyz-ignored}/ignore.app-test.js (70%) rename tools/modern-tests/apps/react-router/{prefix-test => some-test-ignored-suffix}/ignore.app-test.js (72%) delete mode 100644 tools/modern-tests/apps/react-router/some/nested/glob-ignore/ignore.app-test.js create mode 100644 tools/modern-tests/apps/react-router/some/nested/glob-ignored/ignore.app-test.js create mode 100644 tools/modern-tests/apps/react-router/some/unit/test-ignored.app-test.js create mode 100644 tools/modern-tests/apps/react-router/specific-file-ignored.app-test.js rename tools/modern-tests/apps/react-router/{file-to-ignore.app-test.js => test-example-ignored.app-test.js} (68%) create mode 100644 tools/modern-tests/apps/react-router/test.temp-ignored.app-test.js diff --git a/npm-packages/meteor-rspack/lib/test.js b/npm-packages/meteor-rspack/lib/test.js index 432551af0d..6ea5a04cf4 100644 --- a/npm-packages/meteor-rspack/lib/test.js +++ b/npm-packages/meteor-rspack/lib/test.js @@ -42,6 +42,7 @@ const generateEagerTestFile = ({ const excludeFoldersRegex = createIgnoreRegex( createIgnoreGlobConfig(ignoreEntries) ); + console.log("inMeteorIgnoreEntries", inMeteorIgnoreEntries); // Create regex from meteor ignore entries const excludeMeteorIgnoreRegex = inMeteorIgnoreEntries.length > 0 ? createIgnoreRegex(createIgnoreGlobConfig(inMeteorIgnoreEntries)) diff --git a/tools/modern-tests/apps/react-router/.meteorignore b/tools/modern-tests/apps/react-router/.meteorignore index 6b53260afa..e8c59338fc 100644 --- a/tools/modern-tests/apps/react-router/.meteorignore +++ b/tools/modern-tests/apps/react-router/.meteorignore @@ -1,6 +1,16 @@ -react-router* -folder-to-ignore/ -file-to-ignore.app-test.js -**/glob-ignore/*.app-test.js -prefix-* -*-suffix +# Folder combination patterns +react-router*-ignored +folder-to-ignored/ +file-to-ignored.app-test.js +**/glob-ignored/*.app-test.js +prefix-*-ignored +*-ignored-suffix + +# File combination patterns +specific-file-ignored.app-test.js +*.temp-ignored.app-test.js +**/unit/*.app-test.js +integration/*-ignored.app-test.js +!important.app-test.js +test-*-ignored.app-test.js +file-with-multiple-extensions-ignored.app-test.js diff --git a/tools/modern-tests/apps/react-router/file-to-ignored.app-test.js b/tools/modern-tests/apps/react-router/file-to-ignored.app-test.js new file mode 100644 index 0000000000..c4d16204ae --- /dev/null +++ b/tools/modern-tests/apps/react-router/file-to-ignored.app-test.js @@ -0,0 +1,5 @@ +describe("file-to-ignored.app-test.js pattern", () => { + it("should not run as ignored", () => { + throw new Error("test should be ignored by eager test loading"); + }); +}); diff --git a/tools/modern-tests/apps/react-router/file-with-multiple-extensions-ignored.app-test.js b/tools/modern-tests/apps/react-router/file-with-multiple-extensions-ignored.app-test.js new file mode 100644 index 0000000000..08e2e26dd3 --- /dev/null +++ b/tools/modern-tests/apps/react-router/file-with-multiple-extensions-ignored.app-test.js @@ -0,0 +1,5 @@ +describe("file-with-multiple-extensions-ignored.app-test.js pattern", () => { + it("should not run as ignored", () => { + throw new Error("test should be ignored by eager test loading"); + }); +}); diff --git a/tools/modern-tests/apps/react-router/folder-to-ignore/ignore.app-test.js b/tools/modern-tests/apps/react-router/folder-to-ignored/ignore.app-test.js similarity index 72% rename from tools/modern-tests/apps/react-router/folder-to-ignore/ignore.app-test.js rename to tools/modern-tests/apps/react-router/folder-to-ignored/ignore.app-test.js index f0c849ec65..27968893ac 100644 --- a/tools/modern-tests/apps/react-router/folder-to-ignore/ignore.app-test.js +++ b/tools/modern-tests/apps/react-router/folder-to-ignored/ignore.app-test.js @@ -1,4 +1,4 @@ -describe("folder-to-ignore/ pattern", () => { +describe("folder-to-ignored/ pattern", () => { it("should not run as ignored", () => { throw new Error("test should be ignored by eager test loading"); }); diff --git a/tools/modern-tests/apps/react-router/important.app-test.js b/tools/modern-tests/apps/react-router/important.app-test.js new file mode 100644 index 0000000000..a9cb841908 --- /dev/null +++ b/tools/modern-tests/apps/react-router/important.app-test.js @@ -0,0 +1,7 @@ +import assert from "assert"; + +describe("!important.app-test.js pattern", () => { + it("should run as it's not ignored (negation)", () => { + assert(true, "should run"); + }); +}); diff --git a/tools/modern-tests/apps/react-router/integration/test-ignored.app-test.js b/tools/modern-tests/apps/react-router/integration/test-ignored.app-test.js new file mode 100644 index 0000000000..23dcb26b19 --- /dev/null +++ b/tools/modern-tests/apps/react-router/integration/test-ignored.app-test.js @@ -0,0 +1,5 @@ +describe("integration/*-ignored.app-test.js pattern", () => { + it("should not run as ignored", () => { + throw new Error("test should be ignored by eager test loading"); + }); +}); diff --git a/tools/modern-tests/apps/react-router/some-test-suffix/ignore.app-test.js b/tools/modern-tests/apps/react-router/prefix-test-ignored/ignore.app-test.js similarity index 72% rename from tools/modern-tests/apps/react-router/some-test-suffix/ignore.app-test.js rename to tools/modern-tests/apps/react-router/prefix-test-ignored/ignore.app-test.js index 22830f7edc..cbe835f668 100644 --- a/tools/modern-tests/apps/react-router/some-test-suffix/ignore.app-test.js +++ b/tools/modern-tests/apps/react-router/prefix-test-ignored/ignore.app-test.js @@ -1,4 +1,4 @@ -describe("*-suffix pattern", () => { +describe("prefix-*-ignored pattern", () => { it("should not run as ignored", () => { throw new Error("test should be ignored by eager test loading"); }); diff --git a/tools/modern-tests/apps/react-router/react-router-wxyz/ignore.app-test.js b/tools/modern-tests/apps/react-router/react-router-wxyz-ignored/ignore.app-test.js similarity index 70% rename from tools/modern-tests/apps/react-router/react-router-wxyz/ignore.app-test.js rename to tools/modern-tests/apps/react-router/react-router-wxyz-ignored/ignore.app-test.js index 6252f64b5b..bf67b299e7 100644 --- a/tools/modern-tests/apps/react-router/react-router-wxyz/ignore.app-test.js +++ b/tools/modern-tests/apps/react-router/react-router-wxyz-ignored/ignore.app-test.js @@ -1,4 +1,4 @@ -describe("react-router* pattern", () => { +describe("react-router*-ignored pattern", () => { it("should not run as ignored", () => { throw new Error("test should be ignored by eager test loading"); }); diff --git a/tools/modern-tests/apps/react-router/prefix-test/ignore.app-test.js b/tools/modern-tests/apps/react-router/some-test-ignored-suffix/ignore.app-test.js similarity index 72% rename from tools/modern-tests/apps/react-router/prefix-test/ignore.app-test.js rename to tools/modern-tests/apps/react-router/some-test-ignored-suffix/ignore.app-test.js index faeb6ad5ec..30282297ed 100644 --- a/tools/modern-tests/apps/react-router/prefix-test/ignore.app-test.js +++ b/tools/modern-tests/apps/react-router/some-test-ignored-suffix/ignore.app-test.js @@ -1,4 +1,4 @@ -describe("prefix-* pattern", () => { +describe("*-ignored-suffix pattern", () => { it("should not run as ignored", () => { throw new Error("test should be ignored by eager test loading"); }); diff --git a/tools/modern-tests/apps/react-router/some/nested/glob-ignore/ignore.app-test.js b/tools/modern-tests/apps/react-router/some/nested/glob-ignore/ignore.app-test.js deleted file mode 100644 index 6b220fd38f..0000000000 --- a/tools/modern-tests/apps/react-router/some/nested/glob-ignore/ignore.app-test.js +++ /dev/null @@ -1,5 +0,0 @@ -describe("**/glob-ignore/*.app-test.js pattern", () => { - it("should not run as ignored", () => { - throw new Error("test should be ignored by eager test loading"); - }); -}); diff --git a/tools/modern-tests/apps/react-router/some/nested/glob-ignored/ignore.app-test.js b/tools/modern-tests/apps/react-router/some/nested/glob-ignored/ignore.app-test.js new file mode 100644 index 0000000000..a17df041dc --- /dev/null +++ b/tools/modern-tests/apps/react-router/some/nested/glob-ignored/ignore.app-test.js @@ -0,0 +1,5 @@ +describe("**/glob-ignored/*.app-test.js pattern", () => { + it("should not run as ignored", () => { + throw new Error("test should be ignored by eager test loading"); + }); +}); diff --git a/tools/modern-tests/apps/react-router/some/unit/test-ignored.app-test.js b/tools/modern-tests/apps/react-router/some/unit/test-ignored.app-test.js new file mode 100644 index 0000000000..08de39fdcc --- /dev/null +++ b/tools/modern-tests/apps/react-router/some/unit/test-ignored.app-test.js @@ -0,0 +1,5 @@ +describe("**/unit/*.app-test.js pattern", () => { + it("should not run as ignored", () => { + throw new Error("test should be ignored by eager test loading"); + }); +}); diff --git a/tools/modern-tests/apps/react-router/specific-file-ignored.app-test.js b/tools/modern-tests/apps/react-router/specific-file-ignored.app-test.js new file mode 100644 index 0000000000..17a1203f93 --- /dev/null +++ b/tools/modern-tests/apps/react-router/specific-file-ignored.app-test.js @@ -0,0 +1,5 @@ +describe("specific-file-ignored.app-test.js pattern", () => { + it("should not run as ignored", () => { + throw new Error("test should be ignored by eager test loading"); + }); +}); diff --git a/tools/modern-tests/apps/react-router/file-to-ignore.app-test.js b/tools/modern-tests/apps/react-router/test-example-ignored.app-test.js similarity index 68% rename from tools/modern-tests/apps/react-router/file-to-ignore.app-test.js rename to tools/modern-tests/apps/react-router/test-example-ignored.app-test.js index 2fca97e5e9..4e872e6c8b 100644 --- a/tools/modern-tests/apps/react-router/file-to-ignore.app-test.js +++ b/tools/modern-tests/apps/react-router/test-example-ignored.app-test.js @@ -1,4 +1,4 @@ -describe("file-to-ignore.app-test.js pattern", () => { +describe("test-*-ignored.app-test.js pattern", () => { it("should not run as ignored", () => { throw new Error("test should be ignored by eager test loading"); }); diff --git a/tools/modern-tests/apps/react-router/test.temp-ignored.app-test.js b/tools/modern-tests/apps/react-router/test.temp-ignored.app-test.js new file mode 100644 index 0000000000..d7dd5b6748 --- /dev/null +++ b/tools/modern-tests/apps/react-router/test.temp-ignored.app-test.js @@ -0,0 +1,5 @@ +describe("*.temp-ignored.app-test.js pattern", () => { + it("should not run as ignored", () => { + throw new Error("test should be ignored by eager test loading"); + }); +}); From 0bd094dea4ee3645ad02acece0cfd8f546d75340 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Wed, 18 Feb 2026 17:10:25 +0100 Subject: [PATCH 084/166] expand test file exclusion --- .../apps/react-router/.meteorignore | 20 +++++++++---------- ...th-multiple-extensions-ignored.app-test.js | 5 ----- .../ignored}/file-to-ignored.app-test.js | 2 +- .../folder-to-ignored/ignore.app-test.js | 2 +- .../integration}/test-ignored.app-test.js | 2 +- .../prefix-test-ignored}/ignore.app-test.js | 2 +- .../ignore.app-test.js | 2 +- .../ignore.app-test.js | 2 +- .../nested/glob-ignored/ignore.app-test.js | 5 +++++ .../some/unit}/test-ignored.app-test.js | 2 +- .../specific-file-ignored.app-test.js | 2 +- .../ignored}/test-example-ignored.app-test.js | 2 +- .../ignored}/test.temp-ignored.app-test.js | 2 +- 13 files changed, 25 insertions(+), 25 deletions(-) delete mode 100644 tools/modern-tests/apps/react-router/file-with-multiple-extensions-ignored.app-test.js rename tools/modern-tests/apps/react-router/{ => tests/ignored}/file-to-ignored.app-test.js (63%) rename tools/modern-tests/apps/react-router/{ => tests/ignored}/folder-to-ignored/ignore.app-test.js (66%) rename tools/modern-tests/apps/react-router/{some/unit => tests/ignored/integration}/test-ignored.app-test.js (61%) rename tools/modern-tests/apps/react-router/{some/nested/glob-ignored => tests/ignored/prefix-test-ignored}/ignore.app-test.js (67%) rename tools/modern-tests/apps/react-router/{some-test-ignored-suffix => tests/ignored/react-router-wxyz-ignored}/ignore.app-test.js (65%) rename tools/modern-tests/apps/react-router/{prefix-test-ignored => tests/ignored/some-test-ignored-suffix}/ignore.app-test.js (67%) create mode 100644 tools/modern-tests/apps/react-router/tests/ignored/some/nested/glob-ignored/ignore.app-test.js rename tools/modern-tests/apps/react-router/{integration => tests/ignored/some/unit}/test-ignored.app-test.js (61%) rename tools/modern-tests/apps/react-router/{ => tests/ignored}/specific-file-ignored.app-test.js (61%) rename tools/modern-tests/apps/react-router/{ => tests/ignored}/test-example-ignored.app-test.js (63%) rename tools/modern-tests/apps/react-router/{ => tests/ignored}/test.temp-ignored.app-test.js (63%) diff --git a/tools/modern-tests/apps/react-router/.meteorignore b/tools/modern-tests/apps/react-router/.meteorignore index e8c59338fc..269a4e9cc4 100644 --- a/tools/modern-tests/apps/react-router/.meteorignore +++ b/tools/modern-tests/apps/react-router/.meteorignore @@ -1,16 +1,16 @@ # Folder combination patterns -react-router*-ignored -folder-to-ignored/ -file-to-ignored.app-test.js +react-router* +tests/ignored/react-router*-ignored +tests/ignored/folder-to-ignored/ +tests/ignored/file-to-ignored.app-test.js **/glob-ignored/*.app-test.js -prefix-*-ignored -*-ignored-suffix +tests/ignored/prefix-*-ignored +tests/ignored/*-ignored-suffix # File combination patterns -specific-file-ignored.app-test.js -*.temp-ignored.app-test.js +tests/ignored/specific-file-ignored.app-test.js +tests/ignored/*.temp-ignored.app-test.js **/unit/*.app-test.js -integration/*-ignored.app-test.js +tests/ignored/integration/*-ignored.app-test.js +tests/ignored/test-*-ignored.app-test.js !important.app-test.js -test-*-ignored.app-test.js -file-with-multiple-extensions-ignored.app-test.js diff --git a/tools/modern-tests/apps/react-router/file-with-multiple-extensions-ignored.app-test.js b/tools/modern-tests/apps/react-router/file-with-multiple-extensions-ignored.app-test.js deleted file mode 100644 index 08e2e26dd3..0000000000 --- a/tools/modern-tests/apps/react-router/file-with-multiple-extensions-ignored.app-test.js +++ /dev/null @@ -1,5 +0,0 @@ -describe("file-with-multiple-extensions-ignored.app-test.js pattern", () => { - it("should not run as ignored", () => { - throw new Error("test should be ignored by eager test loading"); - }); -}); diff --git a/tools/modern-tests/apps/react-router/file-to-ignored.app-test.js b/tools/modern-tests/apps/react-router/tests/ignored/file-to-ignored.app-test.js similarity index 63% rename from tools/modern-tests/apps/react-router/file-to-ignored.app-test.js rename to tools/modern-tests/apps/react-router/tests/ignored/file-to-ignored.app-test.js index c4d16204ae..c67d43d5d9 100644 --- a/tools/modern-tests/apps/react-router/file-to-ignored.app-test.js +++ b/tools/modern-tests/apps/react-router/tests/ignored/file-to-ignored.app-test.js @@ -1,4 +1,4 @@ -describe("file-to-ignored.app-test.js pattern", () => { +describe("tests/ignored/file-to-ignored.app-test.js pattern", () => { it("should not run as ignored", () => { throw new Error("test should be ignored by eager test loading"); }); diff --git a/tools/modern-tests/apps/react-router/folder-to-ignored/ignore.app-test.js b/tools/modern-tests/apps/react-router/tests/ignored/folder-to-ignored/ignore.app-test.js similarity index 66% rename from tools/modern-tests/apps/react-router/folder-to-ignored/ignore.app-test.js rename to tools/modern-tests/apps/react-router/tests/ignored/folder-to-ignored/ignore.app-test.js index 27968893ac..5878c51755 100644 --- a/tools/modern-tests/apps/react-router/folder-to-ignored/ignore.app-test.js +++ b/tools/modern-tests/apps/react-router/tests/ignored/folder-to-ignored/ignore.app-test.js @@ -1,4 +1,4 @@ -describe("folder-to-ignored/ pattern", () => { +describe("tests/ignored/folder-to-ignored/ pattern", () => { it("should not run as ignored", () => { throw new Error("test should be ignored by eager test loading"); }); diff --git a/tools/modern-tests/apps/react-router/some/unit/test-ignored.app-test.js b/tools/modern-tests/apps/react-router/tests/ignored/integration/test-ignored.app-test.js similarity index 61% rename from tools/modern-tests/apps/react-router/some/unit/test-ignored.app-test.js rename to tools/modern-tests/apps/react-router/tests/ignored/integration/test-ignored.app-test.js index 08de39fdcc..6c85dda4f6 100644 --- a/tools/modern-tests/apps/react-router/some/unit/test-ignored.app-test.js +++ b/tools/modern-tests/apps/react-router/tests/ignored/integration/test-ignored.app-test.js @@ -1,4 +1,4 @@ -describe("**/unit/*.app-test.js pattern", () => { +describe("tests/ignored/integration/*-ignored.app-test.js pattern", () => { it("should not run as ignored", () => { throw new Error("test should be ignored by eager test loading"); }); diff --git a/tools/modern-tests/apps/react-router/some/nested/glob-ignored/ignore.app-test.js b/tools/modern-tests/apps/react-router/tests/ignored/prefix-test-ignored/ignore.app-test.js similarity index 67% rename from tools/modern-tests/apps/react-router/some/nested/glob-ignored/ignore.app-test.js rename to tools/modern-tests/apps/react-router/tests/ignored/prefix-test-ignored/ignore.app-test.js index a17df041dc..a8a1c30c5a 100644 --- a/tools/modern-tests/apps/react-router/some/nested/glob-ignored/ignore.app-test.js +++ b/tools/modern-tests/apps/react-router/tests/ignored/prefix-test-ignored/ignore.app-test.js @@ -1,4 +1,4 @@ -describe("**/glob-ignored/*.app-test.js pattern", () => { +describe("tests/ignored/prefix-*-ignored pattern", () => { it("should not run as ignored", () => { throw new Error("test should be ignored by eager test loading"); }); diff --git a/tools/modern-tests/apps/react-router/some-test-ignored-suffix/ignore.app-test.js b/tools/modern-tests/apps/react-router/tests/ignored/react-router-wxyz-ignored/ignore.app-test.js similarity index 65% rename from tools/modern-tests/apps/react-router/some-test-ignored-suffix/ignore.app-test.js rename to tools/modern-tests/apps/react-router/tests/ignored/react-router-wxyz-ignored/ignore.app-test.js index 30282297ed..217570f7b1 100644 --- a/tools/modern-tests/apps/react-router/some-test-ignored-suffix/ignore.app-test.js +++ b/tools/modern-tests/apps/react-router/tests/ignored/react-router-wxyz-ignored/ignore.app-test.js @@ -1,4 +1,4 @@ -describe("*-ignored-suffix pattern", () => { +describe("tests/ignored/react-router*-ignored pattern", () => { it("should not run as ignored", () => { throw new Error("test should be ignored by eager test loading"); }); diff --git a/tools/modern-tests/apps/react-router/prefix-test-ignored/ignore.app-test.js b/tools/modern-tests/apps/react-router/tests/ignored/some-test-ignored-suffix/ignore.app-test.js similarity index 67% rename from tools/modern-tests/apps/react-router/prefix-test-ignored/ignore.app-test.js rename to tools/modern-tests/apps/react-router/tests/ignored/some-test-ignored-suffix/ignore.app-test.js index cbe835f668..ad7167d7c5 100644 --- a/tools/modern-tests/apps/react-router/prefix-test-ignored/ignore.app-test.js +++ b/tools/modern-tests/apps/react-router/tests/ignored/some-test-ignored-suffix/ignore.app-test.js @@ -1,4 +1,4 @@ -describe("prefix-*-ignored pattern", () => { +describe("tests/ignored/*-ignored-suffix pattern", () => { it("should not run as ignored", () => { throw new Error("test should be ignored by eager test loading"); }); diff --git a/tools/modern-tests/apps/react-router/tests/ignored/some/nested/glob-ignored/ignore.app-test.js b/tools/modern-tests/apps/react-router/tests/ignored/some/nested/glob-ignored/ignore.app-test.js new file mode 100644 index 0000000000..403124d0b5 --- /dev/null +++ b/tools/modern-tests/apps/react-router/tests/ignored/some/nested/glob-ignored/ignore.app-test.js @@ -0,0 +1,5 @@ +describe("tests/ignored/some/nested/glob-ignored/ignore.app-test.js pattern", () => { + it("should not run as ignored", () => { + throw new Error("test should be ignored by eager test loading"); + }); +}); diff --git a/tools/modern-tests/apps/react-router/integration/test-ignored.app-test.js b/tools/modern-tests/apps/react-router/tests/ignored/some/unit/test-ignored.app-test.js similarity index 61% rename from tools/modern-tests/apps/react-router/integration/test-ignored.app-test.js rename to tools/modern-tests/apps/react-router/tests/ignored/some/unit/test-ignored.app-test.js index 23dcb26b19..3d8cab2346 100644 --- a/tools/modern-tests/apps/react-router/integration/test-ignored.app-test.js +++ b/tools/modern-tests/apps/react-router/tests/ignored/some/unit/test-ignored.app-test.js @@ -1,4 +1,4 @@ -describe("integration/*-ignored.app-test.js pattern", () => { +describe("tests/ignored/some/unit/test-ignored.app-test.js pattern", () => { it("should not run as ignored", () => { throw new Error("test should be ignored by eager test loading"); }); diff --git a/tools/modern-tests/apps/react-router/specific-file-ignored.app-test.js b/tools/modern-tests/apps/react-router/tests/ignored/specific-file-ignored.app-test.js similarity index 61% rename from tools/modern-tests/apps/react-router/specific-file-ignored.app-test.js rename to tools/modern-tests/apps/react-router/tests/ignored/specific-file-ignored.app-test.js index 17a1203f93..4eda02edf5 100644 --- a/tools/modern-tests/apps/react-router/specific-file-ignored.app-test.js +++ b/tools/modern-tests/apps/react-router/tests/ignored/specific-file-ignored.app-test.js @@ -1,4 +1,4 @@ -describe("specific-file-ignored.app-test.js pattern", () => { +describe("tests/ignored/specific-file-ignored.app-test.js pattern", () => { it("should not run as ignored", () => { throw new Error("test should be ignored by eager test loading"); }); diff --git a/tools/modern-tests/apps/react-router/test-example-ignored.app-test.js b/tools/modern-tests/apps/react-router/tests/ignored/test-example-ignored.app-test.js similarity index 63% rename from tools/modern-tests/apps/react-router/test-example-ignored.app-test.js rename to tools/modern-tests/apps/react-router/tests/ignored/test-example-ignored.app-test.js index 4e872e6c8b..9fdbbda403 100644 --- a/tools/modern-tests/apps/react-router/test-example-ignored.app-test.js +++ b/tools/modern-tests/apps/react-router/tests/ignored/test-example-ignored.app-test.js @@ -1,4 +1,4 @@ -describe("test-*-ignored.app-test.js pattern", () => { +describe("tests/ignored/test-*-ignored.app-test.js pattern", () => { it("should not run as ignored", () => { throw new Error("test should be ignored by eager test loading"); }); diff --git a/tools/modern-tests/apps/react-router/test.temp-ignored.app-test.js b/tools/modern-tests/apps/react-router/tests/ignored/test.temp-ignored.app-test.js similarity index 63% rename from tools/modern-tests/apps/react-router/test.temp-ignored.app-test.js rename to tools/modern-tests/apps/react-router/tests/ignored/test.temp-ignored.app-test.js index d7dd5b6748..72d4d3d0e1 100644 --- a/tools/modern-tests/apps/react-router/test.temp-ignored.app-test.js +++ b/tools/modern-tests/apps/react-router/tests/ignored/test.temp-ignored.app-test.js @@ -1,4 +1,4 @@ -describe("*.temp-ignored.app-test.js pattern", () => { +describe("tests/ignored/*.temp-ignored.app-test.js pattern", () => { it("should not run as ignored", () => { throw new Error("test should be ignored by eager test loading"); }); From 437da4db3740d1bcc3af3e195dd4c139e1e01e0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Wed, 18 Feb 2026 17:18:18 +0100 Subject: [PATCH 085/166] clean --- npm-packages/meteor-rspack/rspack.config.js | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/npm-packages/meteor-rspack/rspack.config.js b/npm-packages/meteor-rspack/rspack.config.js index f24536ef4c..2c7db4b8f8 100644 --- a/npm-packages/meteor-rspack/rspack.config.js +++ b/npm-packages/meteor-rspack/rspack.config.js @@ -452,11 +452,6 @@ module.exports = async function (inMeteor = {}, argv = {}) { : isClient && isTest && testEntry ? path.resolve(process.cwd(), testEntry) : path.resolve(process.cwd(), buildContext, entryPath); - console.log( - "--> (rspack.config.js-Line: 431)\n clientEntry: ", - clientEntry, - entryPath - ); const clientNameConfig = `[${(isTest && 'test-') || ''}client-rspack]`; // Base client config let clientConfig = { @@ -577,11 +572,6 @@ module.exports = async function (inMeteor = {}, argv = {}) { ? path.resolve(process.cwd(), testEntry) : path.resolve(projectDir, buildContext, entryPath); const serverNameConfig = `[${(isTest && 'test-') || ''}server-rspack]`; - console.log( - "--> (rspack.config.js-Line: 576)\n serverEntry: ", - serverEntry, - entryPath - ); // Base server config let serverConfig = { name: serverNameConfig, From 64ab79671b5fa4f9bebd56ac352e7a6eecae098a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Wed, 18 Feb 2026 17:28:00 +0100 Subject: [PATCH 086/166] set `TOOL_NODE_FLAGS` in e2e-tests workflow to prevent memory issues --- .github/workflows/e2e-tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index de695c209d..764cb20b16 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -18,6 +18,7 @@ concurrency: cancel-in-progress: true env: + TOOL_NODE_FLAGS: "--max_old_space_size=12288" NODE_OPTIONS: "--max_old_space_size=12288" jobs: From d57e1499340174bf6583251ae9cabef4c111afdf Mon Sep 17 00:00:00 2001 From: shanky Date: Mon, 16 Feb 2026 20:14:06 +0530 Subject: [PATCH 087/166] fix: Add support for swc.config.ts in Meteor 3.4 --- npm-packages/meteor-rspack/lib/swc.js | 35 ++++++++++++++++----- npm-packages/meteor-rspack/rspack.config.js | 3 ++ 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/npm-packages/meteor-rspack/lib/swc.js b/npm-packages/meteor-rspack/lib/swc.js index 8b43703059..61296d24d8 100644 --- a/npm-packages/meteor-rspack/lib/swc.js +++ b/npm-packages/meteor-rspack/lib/swc.js @@ -9,12 +9,32 @@ const vm = require('vm'); function getMeteorAppSwcrc(file = '.swcrc') { try { const filePath = `${process.cwd()}/${file}`; - if (file.endsWith('.js')) { + if (file.endsWith('.js') || file.endsWith('.ts')) { 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 = '); + + if (file.endsWith('.ts')) { + try { + const esbuild = require('esbuild'); + const result = esbuild.transformSync(content, { + loader: 'ts', + format: 'cjs', + target: 'node14', + }); + content = result.code; + } catch (esbuildError) { + content = content + .replace(/import\s+type\s+.*?from\s+['"][^'"]+['"];?/g, '') + .replace(/import\s+.*?from\s+['"][^'"]+['"];?/g, '') + .replace(/import\s+['"][^'"]+['"];?/g, '') + .replace(/export\s+default\s+/, 'module.exports = ') + .replace(/export\s+/g, '') + .replace(/:\s*\w+(\[\])?(\s*=)/g, '$2') + .replace(/\(([^)]*?):\s*\w+(\[\])?\)/g, '($1)') + .replace(/\):\s*\w+(\[\])?\s*\{/g, ') {') + .replace(/interface\s+\w+\s*\{[^}]*\}/g, '') + .replace(/type\s+\w+\s*=\s*[^;]+;/g, '') + .replace(/as\s+\w+(\[\])?/g, ''); + } } const script = new vm.Script(` (function() { @@ -45,12 +65,13 @@ function getMeteorAppSwcrc(file = '.swcrc') { function getMeteorAppSwcConfig() { const hasSwcRc = fs.existsSync(`${process.cwd()}/.swcrc`); const hasSwcJs = !hasSwcRc && fs.existsSync(`${process.cwd()}/swc.config.js`); + const hasSwcTs = !hasSwcRc && !hasSwcJs && fs.existsSync(`${process.cwd()}/swc.config.ts`); - if (!hasSwcRc && !hasSwcJs) { + if (!hasSwcRc && !hasSwcJs && !hasSwcTs) { return undefined; } - const swcFile = hasSwcJs ? 'swc.config.js' : '.swcrc'; + const swcFile = hasSwcTs ? 'swc.config.ts' : hasSwcJs ? 'swc.config.js' : '.swcrc'; const config = getMeteorAppSwcrc(swcFile); // Set baseUrl to process.cwd() if it exists diff --git a/npm-packages/meteor-rspack/rspack.config.js b/npm-packages/meteor-rspack/rspack.config.js index 09138424a7..20da463411 100644 --- a/npm-packages/meteor-rspack/rspack.config.js +++ b/npm-packages/meteor-rspack/rspack.config.js @@ -52,6 +52,8 @@ function createCacheStrategy(mode, side, { projectConfigPath, configPath } = {}) const hasSwcrcConfig = fs.existsSync(swcrcPath); const swcJsPath = path.join(process.cwd(), 'swc.config.js'); const hasSwcJsConfig = fs.existsSync(swcJsPath); + const swcTsPath = path.join(process.cwd(), 'swc.config.ts'); + const hasSwcTsConfig = fs.existsSync(swcTsPath); const postcssConfigPath = path.join(process.cwd(), 'postcss.config.js'); const hasPostcssConfig = fs.existsSync(postcssConfigPath); const packageLockPath = path.join(process.cwd(), 'package-lock.json'); @@ -68,6 +70,7 @@ function createCacheStrategy(mode, side, { projectConfigPath, configPath } = {}) ...(hasBabelJsConfig ? [babelJsConfig] : []), ...(hasSwcrcConfig ? [swcrcPath] : []), ...(hasSwcJsConfig ? [swcJsPath] : []), + ...(hasSwcTsConfig ? [swcTsPath] : []), ...(hasPostcssConfig ? [postcssConfigPath] : []), ...(hasPackageLock ? [packageLockPath] : []), ...(hasYarnLock ? [yarnLockPath] : []), From 134b28d6879d3bc82c370b99b7a76e61688f3134 Mon Sep 17 00:00:00 2001 From: shanky Date: Wed, 18 Feb 2026 23:07:21 +0530 Subject: [PATCH 088/166] fix: Use @swc/core and add babel-compiler support for swc.config.ts --- npm-packages/meteor-rspack/lib/swc.js | 22 ++++++++---- packages/babel-compiler/babel-compiler.js | 41 ++++++++++++++++++++--- 2 files changed, 53 insertions(+), 10 deletions(-) diff --git a/npm-packages/meteor-rspack/lib/swc.js b/npm-packages/meteor-rspack/lib/swc.js index 61296d24d8..6a3a3ea869 100644 --- a/npm-packages/meteor-rspack/lib/swc.js +++ b/npm-packages/meteor-rspack/lib/swc.js @@ -14,14 +14,20 @@ function getMeteorAppSwcrc(file = '.swcrc') { if (file.endsWith('.ts')) { try { - const esbuild = require('esbuild'); - const result = esbuild.transformSync(content, { - loader: 'ts', - format: 'cjs', - target: 'node14', + const swc = require('@swc/core'); + const result = swc.transformSync(content, { + jsc: { + parser: { + syntax: 'typescript', + }, + target: 'es2015', + }, + module: { + type: 'commonjs', + }, }); content = result.code; - } catch (esbuildError) { + } catch (swcError) { content = content .replace(/import\s+type\s+.*?from\s+['"][^'"]+['"];?/g, '') .replace(/import\s+.*?from\s+['"][^'"]+['"];?/g, '') @@ -36,6 +42,10 @@ function getMeteorAppSwcrc(file = '.swcrc') { .replace(/as\s+\w+(\[\])?/g, ''); } } + + if (content.includes('export default')) { + content = content.replace(/export\s+default\s+/, 'module.exports = '); + } const script = new vm.Script(` (function() { const module = {}; diff --git a/packages/babel-compiler/babel-compiler.js b/packages/babel-compiler/babel-compiler.js index 0de0637df2..43324e11e5 100644 --- a/packages/babel-compiler/babel-compiler.js +++ b/packages/babel-compiler/babel-compiler.js @@ -101,16 +101,17 @@ let lastModifiedSwcConfigTime; BCp.initializeMeteorAppSwcrc = function () { const hasSwcRc = fs.existsSync(`${getMeteorAppDir()}/.swcrc`); const hasSwcJs = !hasSwcRc && fs.existsSync(`${getMeteorAppDir()}/swc.config.js`); - if (!lastModifiedSwcConfig && !hasSwcRc && !hasSwcJs) { + const hasSwcTs = !hasSwcRc && !hasSwcJs && fs.existsSync(`${getMeteorAppDir()}/swc.config.ts`); + if (!lastModifiedSwcConfig && !hasSwcRc && !hasSwcJs && !hasSwcTs) { return; } - const swcFile = hasSwcJs ? 'swc.config.js' : '.swcrc'; + const swcFile = hasSwcTs ? 'swc.config.ts' : hasSwcJs ? 'swc.config.js' : '.swcrc'; const filePath = `${getMeteorAppDir()}/${swcFile}`; const fileStats = fs.statSync(filePath); const fileModTime = fileStats?.mtime?.getTime(); let currentLastModifiedConfigTime; - if (hasSwcJs) { + if (hasSwcJs || hasSwcTs) { // For dynamic JS files, first get the resolved configuration const resolvedConfig = lastModifiedSwcConfigTime?.includes(`${fileModTime}`) ? lastModifiedSwcConfig || getMeteorAppSwcrc(swcFile) @@ -1073,8 +1074,40 @@ function getMeteorAppPackageJson() { function getMeteorAppSwcrc(file = '.swcrc') { try { const filePath = `${getMeteorAppDir()}/${file}`; - if (file.endsWith('.js')) { + if (file.endsWith('.js') || file.endsWith('.ts')) { let content = fs.readFileSync(filePath, 'utf-8'); + + if (file.endsWith('.ts')) { + try { + const swc = require('@meteorjs/swc-core'); + const result = swc.transformSync(content, { + jsc: { + parser: { + syntax: 'typescript', + }, + target: 'es2015', + }, + module: { + type: 'commonjs', + }, + }); + content = result.code; + } catch (swcError) { + content = content + .replace(/import\s+type\s+.*?from\s+['"][^'"]+['"];?/g, '') + .replace(/import\s+.*?from\s+['"][^'"]+['"];?/g, '') + .replace(/import\s+['"][^'"]+['"];?/g, '') + .replace(/export\s+default\s+/, 'module.exports = ') + .replace(/export\s+/g, '') + .replace(/:\s*\w+(\[\])?(\s*=)/g, '$2') + .replace(/\(([^)]*?):\s*\w+(\[\])?\)/g, '($1)') + .replace(/\):\s*\w+(\[\])?\s*\{/g, ') {') + .replace(/interface\s+\w+\s*\{[^}]*\}/g, '') + .replace(/type\s+\w+\s*=\s*[^;]+;/g, '') + .replace(/as\s+\w+(\[\])?/g, ''); + } + } + // Check if the content uses ES module syntax (export default) if (content.includes('export default')) { // Transform ES module syntax to CommonJS From a76af19af4b6e713b85aa24f6f348671144ee38d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Fri, 20 Feb 2026 14:12:58 +0100 Subject: [PATCH 089/166] improve logging on Meteor-Rspack integration --- npm-packages/meteor-rspack/package-lock.json | 4 +- npm-packages/meteor-rspack/package.json | 2 +- .../plugins/MeteorRspackOutputPlugin.js | 42 +++++ npm-packages/meteor-rspack/rspack.config.js | 23 +++ packages/rspack/lib/constants.js | 2 +- packages/rspack/lib/logging.js | 98 +++++++++++ packages/rspack/lib/processes.js | 152 ++++++++++++++---- packages/tools-core/lib/log.js | 8 + packages/tools-core/lib/meteor.js | 8 + 9 files changed, 302 insertions(+), 37 deletions(-) create mode 100644 npm-packages/meteor-rspack/plugins/MeteorRspackOutputPlugin.js create mode 100644 packages/rspack/lib/logging.js diff --git a/npm-packages/meteor-rspack/package-lock.json b/npm-packages/meteor-rspack/package-lock.json index 4b2c4e1836..8a456ecb69 100644 --- a/npm-packages/meteor-rspack/package-lock.json +++ b/npm-packages/meteor-rspack/package-lock.json @@ -1,12 +1,12 @@ { "name": "@meteorjs/rspack", - "version": "1.1.0-beta.6", + "version": "1.1.0-beta.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@meteorjs/rspack", - "version": "1.1.0-beta.6", + "version": "1.1.0-beta.7", "license": "ISC", "dependencies": { "fast-deep-equal": "^3.1.3", diff --git a/npm-packages/meteor-rspack/package.json b/npm-packages/meteor-rspack/package.json index 71b4960bb7..a7f816bd95 100644 --- a/npm-packages/meteor-rspack/package.json +++ b/npm-packages/meteor-rspack/package.json @@ -1,6 +1,6 @@ { "name": "@meteorjs/rspack", - "version": "1.1.0-beta.6", + "version": "1.1.0-beta.7", "description": "Configuration logic for using Rspack in Meteor projects", "main": "index.js", "type": "commonjs", diff --git a/npm-packages/meteor-rspack/plugins/MeteorRspackOutputPlugin.js b/npm-packages/meteor-rspack/plugins/MeteorRspackOutputPlugin.js new file mode 100644 index 0000000000..840e7b8631 --- /dev/null +++ b/npm-packages/meteor-rspack/plugins/MeteorRspackOutputPlugin.js @@ -0,0 +1,42 @@ +// MeteorRspackOutputPlugin.js +// +// This plugin outputs a JSON stringified with a tag delimiter each time +// a new Rspack compilation happens. The JSON content is configurable +// via plugin instantiation options. + +class MeteorRspackOutputPlugin { + constructor(options = {}) { + this.pluginName = 'MeteorRspackOutputPlugin'; + this.options = options; + // The data to be output as JSON, can be a static object or a function + this.getData = + typeof options.getData === 'function' + ? options.getData + : () => options.data || {}; + } + + apply(compiler) { + const { devServer } = compiler.options; + + let devServerUrl; + if (devServer) { + const protocol = devServer.server?.type === "https" ? "https" : "http"; + const host = + devServer.host && devServer.host !== "0.0.0.0" + ? devServer.host + : "localhost"; + const port = devServer.port ?? 8080; + devServerUrl = `${protocol}://${host}:${port}`; + } + + // Hook into the 'done' event which fires after each compilation completes + compiler.hooks.done.tap(this.pluginName, stats => { + const data = { ...(this.getData(stats) || {}), devServerUrl }; + const jsonString = JSON.stringify(data); + const output = `[Meteor-Rspack]${jsonString}[/Meteor-Rspack]`; + console.log(output); + }); + } +} + +module.exports = { MeteorRspackOutputPlugin }; diff --git a/npm-packages/meteor-rspack/rspack.config.js b/npm-packages/meteor-rspack/rspack.config.js index 4e92ad24d4..311a48bb38 100644 --- a/npm-packages/meteor-rspack/rspack.config.js +++ b/npm-packages/meteor-rspack/rspack.config.js @@ -10,6 +10,7 @@ const { getMeteorAppSwcConfig } = require('./lib/swc.js'); const HtmlRspackPlugin = require('./plugins/HtmlRspackPlugin.js'); const { RequireExternalsPlugin } = require('./plugins/RequireExtenalsPlugin.js'); const { AssetExternalsPlugin } = require('./plugins/AssetExternalsPlugin.js'); +const { MeteorRspackOutputPlugin } = require('./plugins/MeteorRspackOutputPlugin.js'); const { generateEagerTestFile } = require("./lib/test.js"); const { getMeteorIgnoreEntries, createIgnoreGlobConfig } = require("./lib/ignore"); const { mergeMeteorRspackFragments } = require("./lib/meteorRspackConfigFactory.js"); @@ -427,6 +428,7 @@ module.exports = async function (inMeteor = {}, argv = {}) { : []; // Not supported in Meteor yet (Rspack 1.7+ is enabled by default) const lazyCompilationConfig = { lazyCompilation: false }; + const loggingConfig = { infrastructureLogging: { level: "none" } }; const clientEntry = isClient && isTest && isTestEager && isTestFullApp @@ -549,6 +551,7 @@ module.exports = async function (inMeteor = {}, argv = {}) { }), ...merge(cacheStrategy, { experiments: { css: true } }), ...lazyCompilationConfig, + ...loggingConfig, }; const serverEntry = @@ -639,6 +642,7 @@ module.exports = async function (inMeteor = {}, argv = {}) { ...((isDevEnvironment || (isTest && !isTestEager) || isNative) && cacheStrategy), ...lazyCompilationConfig, + ...loggingConfig, }; // Helper function to load and process config files @@ -698,6 +702,8 @@ module.exports = async function (inMeteor = {}, argv = {}) { // Load and apply project-level overrides for the selected build // Check if we're in a Meteor package directory by looking at the path const isMeteorPackageConfig = projectDir.includes('/packages/rspack'); + // Track if user config overrides stats and infrastructureLogging + let statsOverrided = false; if (fs.existsSync(projectConfigPath) && !isMeteorPackageConfig) { // Check if there's a .mjs or .cjs version of the config file const mjsConfigPath = projectConfigPath.replace(/\.js$/, '.mjs'); @@ -718,7 +724,11 @@ module.exports = async function (inMeteor = {}, argv = {}) { isAngularEnabled ); + // Track if user config overrides stats if (nextUserConfig) { + if (nextUserConfig.stats != null) { + statsOverrided = true; + } if (Meteor.isClient) { clientConfig = mergeSplitOverlap(clientConfig, nextUserConfig); } @@ -809,5 +819,18 @@ module.exports = async function (inMeteor = {}, argv = {}) { ); } + // Add MeteorRspackOutputPlugin as the last plugin to output compilation info + const meteorRspackOutputPlugin = new MeteorRspackOutputPlugin({ + getData: (stats) => ({ + name: config.name, + mode: config.mode, + hasErrors: stats.hasErrors(), + hasWarnings: stats.hasWarnings(), + timestamp: Date.now(), + statsOverrided, + }), + }); + config.plugins = [meteorRspackOutputPlugin, ...(config.plugins || [])]; + return [config]; } diff --git a/packages/rspack/lib/constants.js b/packages/rspack/lib/constants.js index c9073b740d..379eb1c51a 100644 --- a/packages/rspack/lib/constants.js +++ b/packages/rspack/lib/constants.js @@ -5,7 +5,7 @@ export const DEFAULT_RSPACK_VERSION = '1.7.1'; -export const DEFAULT_METEOR_RSPACK_VERSION = '1.1.0-beta.6'; +export const DEFAULT_METEOR_RSPACK_VERSION = '1.1.0-beta.7'; export const DEFAULT_METEOR_RSPACK_REACT_HMR_VERSION = '1.4.3'; diff --git a/packages/rspack/lib/logging.js b/packages/rspack/lib/logging.js new file mode 100644 index 0000000000..02d7d3cf08 --- /dev/null +++ b/packages/rspack/lib/logging.js @@ -0,0 +1,98 @@ +/** + * @module logging + * @description Functions for logging Rspack processes + */ + +const { logRaw } = require("meteor/tools-core/lib/log"); + +const { + isMeteorAppConfigModernVerbose, + isMeteorAppProfile, +} = require("meteor/tools-core/lib/meteor"); + +/** + * Checks if the logs should be verbose for Rspack processes. + * @returns {boolean} True if profiling or verbose mode is enabled, false otherwise. + */ +export function shouldLogVerbose() { + return isMeteorAppProfile() || isMeteorAppConfigModernVerbose(); +} + +/** + * Strips the leading label line (e.g. "[server-rspack]:\n") from Rspack output. + * @param {string} output - The raw output from an Rspack process + * @returns {string} The output without the leading label line, trimmed + */ +export function stripRspackLabel(output) { + return output.replace(/^\[.*?]:\s*\n/, "").trim(); +} + +/** + * Parses and extracts [Meteor-Rspack]{}[/Meteor-Rspack] content from data. + * Returns the cleaned data (without the tag content) and the parsed JSON config. + * @param {string} data - The raw data that may contain Meteor-Rspack tags + * @returns {{ cleanedData: string, config: Object|null }} Object with cleaned data and parsed config + */ +export function parseMeteorRspackOutput(data) { + const tagRegex = /\[Meteor-Rspack\](.*?)\[\/Meteor-Rspack\]/g; + let config = null; + let match; + + // Find all matches and parse the last one (in case of multiple) + while ((match = tagRegex.exec(data)) !== null) { + try { + config = JSON.parse(match[1]); + } catch (e) { + // If JSON parsing fails, keep config as null + config = null; + } + } + + // Remove all [Meteor-Rspack]...[/Meteor-Rspack] tags from the data + const cleanedData = data.replace(tagRegex, "").trim(); + + return { cleanedData, config }; +} + +const compilationCount = {}; +let hmrServerLogged = false; + +/** + * Logs "=> Started Rspack HMR server at " if devServerUrl exists in config. + * Only logs once per session. + * @param {Object|null} config - The parsed config from MeteorRspackOutputPlugin + */ +export function logHmrServerStarted(config) { + if (hmrServerLogged) return; + if (!config?.devServerUrl) return; + hmrServerLogged = true; + logRaw(`=> Started Rspack HMR server at: ${config.devServerUrl}`); +} + +/** + * Logs a friendly Meteor-style message with the raw Rspack output appended. + * Strips the leading label and logs as: + * "=> Compiled your client app compiled successfully in 342 ms" + * Adds a leading newline from the second compilation onwards per target. + * @param {string} output - The raw stdout line from an Rspack process + * @param {string} target - The build target label (e.g. "client", "server") + * @param {boolean} statsOverrided - If true, skip cleaning and use \n separator + */ +export function logCompilationOutput(output, target, statsOverrided = false) { + let cleaned; + let separator; + if (statsOverrided) { + cleaned = stripRspackLabel(output); + separator = "\n"; + } else { + cleaned = stripRspackLabel(output) + .replace(/^.*\[.*?]\s*/g, "") + .trim() + .replace(/\s*compiled\s*/g, ""); + separator = cleaned.includes("\n") ? ":\n" : " "; + } + compilationCount[target] = (compilationCount[target] || 0) + 1; + const prefix = + target === "server" && compilationCount[target] > 1 ? "\n=>" : "=>"; + logRaw(`${prefix} Compiled Rspack ${target} app${separator}${cleaned}`); +} diff --git a/packages/rspack/lib/processes.js b/packages/rspack/lib/processes.js index d97d731257..873ab946b2 100644 --- a/packages/rspack/lib/processes.js +++ b/packages/rspack/lib/processes.js @@ -3,8 +3,8 @@ * @description Functions for managing Rspack processes */ -import fs from 'fs'; -import path from 'path'; +import fs from "fs"; +import path from "path"; const { spawnProcess, @@ -15,6 +15,7 @@ const { const { logError, logInfo, + logRaw, } = require('meteor/tools-core/lib/log'); const { @@ -58,6 +59,14 @@ const { getBuildFileContent, } = require('./build-context'); +import { + logCompilationOutput, + logHmrServerStarted, + parseMeteorRspackOutput, + shouldLogVerbose, + stripRspackLabel, +} from "./logging"; + /** * Calculates the devServerPort based on process.env.PORT * Base port is 8077, and we add the sum of the digits of process.env.PORT @@ -329,35 +338,62 @@ export function startRspackClientServe(options = {}) { cwd: appDir, env: { ...process.env, ...envs }, onStdout: (data) => { - logInfo(`[Rspack Client] ${data}`); - if (onCompile && data.trim().includes("compiled")) { - onCompile(data); + const { cleanedData, config } = parseMeteorRspackOutput(data); + logHmrServerStarted(config); + onCompile(cleanedData, config); + if (!cleanedData) return; + if (shouldLogVerbose()) { + logInfo(`[Rspack Client] ${cleanedData}`); + } else { + logCompilationOutput(cleanedData, 'client', config?.statsOverrided); } }, onStderr: (data) => { + const { cleanedData } = parseMeteorRspackOutput(data); + if (!cleanedData) return; // Check if this is an EADDRINUSE error in development mode (which we want to completely ignore) - if (isMeteorAppDevelopment() && data.includes('EADDRINUSE')) { - logError(`[Rspack Client Error] ${data}`); + if (isMeteorAppDevelopment() && cleanedData.includes('EADDRINUSE')) { + if (shouldLogVerbose()) { + logError(`[Rspack Client Error] ${cleanedData}`); + } else { + logError(stripRspackLabel(cleanedData)); + } return; } // Check if this is actually an informational message (like webpack-dev-server messages) - if (data.includes('Loopback:') || data.includes('Project is running at:')) { - logInfo(`[Rspack Client] ${data}`); + if (cleanedData.includes('Loopback:') || cleanedData.includes('Project is running at:')) { + if (shouldLogVerbose()) { + logInfo(`[Rspack Client] ${cleanedData}`); + } else { + logRaw(stripRspackLabel(cleanedData)); + } } else { // Check if this is the "npm error could not determine executable to run" error - if (data.includes('npm error could not determine executable to run')) { + if (cleanedData.includes('npm error could not determine executable to run')) { const errorMsg = '[Rspack Client Error] Try running "meteor npm install" to ensure rspack is available'; - logError(errorMsg); + if (shouldLogVerbose()) { + logError(errorMsg); + } else { + logError('Try running "meteor npm install" to ensure rspack is available'); + } throw new Error(errorMsg); } - logError(`[Rspack Client Error] ${data}`); + if (shouldLogVerbose()) { + logError(`[Rspack Client Error] ${cleanedData}`); + } else { + logError(stripRspackLabel(cleanedData)); + } } }, onError: (err) => { const errorMsg = `Rspack Error: ${err.message}`; - logError(errorMsg); + if (shouldLogVerbose()) { + logError(errorMsg); + } else { + logError(err.message); + } throw new Error(errorMsg); - } + }, }); // Store the new process in global state @@ -392,28 +428,50 @@ export function startRspackServerWatch(options = {}) { cwd: appDir, env: { ...process.env, ...envs }, onStdout: (data) => { - logInfo(`[Rspack Server] ${data}`); - if (onCompile && data.trim().includes("compiled")) { - onCompile(data); + const { cleanedData, config } = parseMeteorRspackOutput(data); + onCompile(cleanedData, config); + if (!cleanedData) return; + if (shouldLogVerbose()) { + logInfo(`[Rspack Server] ${cleanedData}`); + } else { + logCompilationOutput(cleanedData, 'server', config?.statsOverrided); } }, onStderr: (data) => { + const { cleanedData } = parseMeteorRspackOutput(data); + if (!cleanedData) return; // Check if this is actually an informational message (like webpack-dev-server messages) - if (data.includes('Project is running at:')) { - logInfo(`[Rspack Server] ${data}`); + if (cleanedData.includes('Project is running at:')) { + if (shouldLogVerbose()) { + logInfo(`[Rspack Server] ${cleanedData}`); + } else { + logRaw(stripRspackLabel(cleanedData)); + } } else { // Check if this is the "npm error could not determine executable to run" error - if (data.includes('npm error could not determine executable to run')) { + if (cleanedData.includes('npm error could not determine executable to run')) { const errorMsg = '[Rspack Server Error] Try running "meteor npm install" to ensure rspack is available'; - logError(errorMsg); + if (shouldLogVerbose()) { + logError(errorMsg); + } else { + logError('Try running "meteor npm install" to ensure rspack is available'); + } throw new Error(errorMsg); } - logError(`[Rspack Server Error] ${data}`); + if (shouldLogVerbose()) { + logError(`[Rspack Server Error] ${cleanedData}`); + } else { + logError(stripRspackLabel(cleanedData)); + } } }, onError: (err) => { const errorMsg = `Rspack Error: ${err.message}`; - logError(errorMsg); + if (shouldLogVerbose()) { + logError(errorMsg); + } else { + logError(err.message); + } throw new Error(errorMsg); } }); @@ -459,23 +517,43 @@ export async function runRspackBuild({ isClient, isServer, isTest, isTestModule, cwd: appDir, env: { ...process.env, ...envs }, onStdout: (data) => { - logInfo(`[Rspack ${label} ${endpoint}] ${data}`); - if (onCompile && data.trim().includes("compiled")) { - onCompile(data); + const { cleanedData, config } = parseMeteorRspackOutput(data); + if (onCompile) { + onCompile(cleanedData, config); + } + if (!cleanedData) return; + if (shouldLogVerbose()) { + logInfo(`[Rspack ${label} ${endpoint}] ${cleanedData}`); + } else { + logCompilationOutput(cleanedData, endpoint.toLowerCase(), config?.statsOverrided); } }, onStderr: (data) => { + const { cleanedData } = parseMeteorRspackOutput(data); + if (!cleanedData) return; // Check if this is actually an informational message (like webpack-dev-server messages) - if (data.includes('Project is running at:')) { - logInfo(`[Rspack ${label} ${endpoint}] ${data}`); + if (cleanedData.includes('Project is running at:')) { + if (shouldLogVerbose()) { + logInfo(`[Rspack ${label} ${endpoint}] ${cleanedData}`); + } else { + logRaw(stripRspackLabel(cleanedData)); + } } else { // Check if this is the "npm error could not determine executable to run" error - if (data.includes('npm error could not determine executable to run')) { + if (cleanedData.includes('npm error could not determine executable to run')) { const errorMsg = `[Rspack ${label} Error ${endpoint}] Try running "meteor npm install" to ensure rspack is available`; - logError(errorMsg); + if (shouldLogVerbose()) { + logError(errorMsg); + } else { + logError(`Try running "meteor npm install" to ensure rspack is available`); + } throw new Error(errorMsg); } - logError(`[Rspack ${label} Error ${endpoint}] ${data}`); + if (shouldLogVerbose()) { + logError(`[Rspack ${label} Error ${endpoint}] ${cleanedData}`); + } else { + logError(stripRspackLabel(cleanedData)); + } } }, onExit: (code) => { @@ -483,12 +561,20 @@ export async function runRspackBuild({ isClient, isServer, isTest, isTestModule, resolve(); } else { const error = new Error(`Rspack ${label} failed in ${endpoint} with exit code ${code}`); - logError(error.message); + if (shouldLogVerbose()) { + logError(error.message); + } else { + logError(`Rspack ${label} failed with exit code ${code}`); + } reject(error); } }, onError: (err) => { - logError(`Rspack ${label} ${endpoint} error: ${err.message}`); + if (shouldLogVerbose()) { + logError(`Rspack ${label} ${endpoint} error: ${err.message}`); + } else { + logError(err.message); + } reject(err); } }); diff --git a/packages/tools-core/lib/log.js b/packages/tools-core/lib/log.js index 80e3f2a9af..b8120011de 100644 --- a/packages/tools-core/lib/log.js +++ b/packages/tools-core/lib/log.js @@ -34,6 +34,14 @@ export function logInfo(message) { console.log(`${colors.purple}${message}${colors.reset}`); } +/** + * Log a raw message without any color + * @param {string} message - The message to log + */ +export function logRaw(message) { + console.log(message); +} + /** * Log a success message in green * @param {string} message - The message to log diff --git a/packages/tools-core/lib/meteor.js b/packages/tools-core/lib/meteor.js index 1556a1ddde..61eb8a5fbb 100644 --- a/packages/tools-core/lib/meteor.js +++ b/packages/tools-core/lib/meteor.js @@ -292,6 +292,14 @@ export function isMeteorAppDebug() { ); } +/** + * Checks if the Meteor application is running with METEOR_PROFILE enabled. + * @returns {boolean} True if METEOR_PROFILE is set, false otherwise. + */ +export function isMeteorAppProfile() { + return !!process.env.METEOR_PROFILE; +} + /** * Sets a custom script URL for the Meteor application in the environment variable. * @param {string} scriptUrl - The URL of the custom script. From 70dd6999750b3c6570c5f57ebf0e9c6a6cd6fdf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Fri, 20 Feb 2026 15:36:28 +0100 Subject: [PATCH 090/166] refine logging utilities and improve dependency installation messages --- packages/rspack/lib/dependencies.js | 31 ++++++++--------------- packages/tools-core/lib/log.js | 39 +++++++++++++++++++++-------- 2 files changed, 38 insertions(+), 32 deletions(-) diff --git a/packages/rspack/lib/dependencies.js b/packages/rspack/lib/dependencies.js index 20ebca4271..81195fb7cc 100644 --- a/packages/rspack/lib/dependencies.js +++ b/packages/rspack/lib/dependencies.js @@ -75,14 +75,11 @@ async function ensureDependenciesInstalled(dependencies, globalStateKey, package let regularDepsStrings = []; // Display a header for the installation process - logProgress(`┌─────────────────────────────────────────────────`); - logProgress(`│ ${packageName} Dependencies Installation`); - logProgress(`└─────────────────────────────────────────────────`); + logProgress(`=> 📦 ${packageName} Dependencies`); // Show what dependencies will be installed - logInfo(`The following ${packageName} dependencies need to be installed:`); dependencyStrings.forEach(dep => { - logInfo(` • ${dep}`); + logInfo(` • ${dep}`); }); // Check if this is a Yarn project @@ -95,7 +92,7 @@ async function ensureDependenciesInstalled(dependencies, globalStateKey, package // Log progress for dev dependencies logProgress( - `🔧 Installing ${devDepsToInstall.length} dev dependenc${ + `=> 🔧 Installing ${devDepsToInstall.length} dev dependenc${ devDepsToInstall.length === 1 ? "y" : "ies" }...` ); @@ -114,7 +111,7 @@ async function ensureDependenciesInstalled(dependencies, globalStateKey, package // Log progress for regular dependencies logProgress( - `🔧 Installing ${regularDepsToInstall.length} dependenc${ + `=> 🔧 Installing ${regularDepsToInstall.length} dependenc${ regularDepsToInstall.length === 1 ? "y" : "ies" }...` ); @@ -131,22 +128,20 @@ async function ensureDependenciesInstalled(dependencies, globalStateKey, package if (!success) { const isYarnProj = process.env.YARN_ENABLED === 'true'; - logError(`\n┌─────────────────────────────────────────────────`); - logError(`│ ❌ ${packageName} Installation Failed`); - logError(`└─────────────────────────────────────────────────`); + logError(`=> ❌ Failed to install ${packageName}`); if (!devDepsSuccess && devDepsStrings.length > 0) { const devInstallCommand = isYarnProj ? `yarn add --dev ${devDepsStrings.join(' ').trim()}` : `meteor npm install -D ${devDepsStrings.join(' ').trim()}`; - logError(`For dev dependencies, run: ${devInstallCommand}`); + logError(` For dev dependencies, run: ${devInstallCommand}`); } if (!regularDepsSuccess && regularDepsStrings.length > 0) { const regularInstallCommand = isYarnProj ? `yarn add ${regularDepsStrings.join(' ').trim()}` : `meteor npm install ${regularDepsStrings.join(' ').trim()}`; - logError(`For regular dependencies, run: ${regularInstallCommand}`); + logError(` For regular dependencies, run: ${regularInstallCommand}`); } const allFailedDeps = []; @@ -158,20 +153,14 @@ async function ensureDependenciesInstalled(dependencies, globalStateKey, package ); } - logSuccess(`✅ ${packageName} dependencies installed`); + logSuccess(`=> ✅ Installed ${packageName} dependencies`); if (isMeteorAppUpdate()) { const isYarnProj = process.env.YARN_ENABLED === 'true'; const installCommand = isYarnProj ? 'yarn install' : 'npm install'; - logInfo(`\n┌───────────────────────────────────────────────────────────────────────┐`); - logInfo(`│ 🔔 IMPORTANT: Project Stability Reminder │`); - logInfo(`├───────────────────────────────────────────────────────────────────────┤`); - logInfo(`│ After the Meteor update finishes, please run \`${installCommand}\` in your │`); - logInfo(`│ project directory. │`); - logInfo(`│ │`); - logInfo(`│ This helps keep your dependencies correct and your project stable. │`); - logInfo(`└───────────────────────────────────────────────────────────────────────┘`); + logInfo(`=> 🔔 Remember: Run \`${installCommand}\` after the Meteor update finishes.`); + logInfo(` This helps keep your dependencies correct and your project stable.`); } } diff --git a/packages/tools-core/lib/log.js b/packages/tools-core/lib/log.js index b8120011de..4550c18449 100644 --- a/packages/tools-core/lib/log.js +++ b/packages/tools-core/lib/log.js @@ -1,21 +1,38 @@ // Check if colors should be disabled const shouldDisableColors = !!process.env.METEOR_DISABLE_COLORS; +// Minimum message length for consistent log formatting +const MIN_MESSAGE_LENGTH = 50; + // ANSI color codes const colors = { - reset: shouldDisableColors ? '' : '\x1b[0m', - blue: shouldDisableColors ? '' : '\x1b[34m', - red: shouldDisableColors ? '' : '\x1b[31m', - purple: shouldDisableColors ? '' : '\x1b[35m', - green: shouldDisableColors ? '' : '\x1b[32m' + reset: shouldDisableColors ? "" : "\x1b[0m", + blue: shouldDisableColors ? "" : "\x1b[34m", + red: shouldDisableColors ? "" : "\x1b[31m", + purple: shouldDisableColors ? "" : "\x1b[35m", + green: shouldDisableColors ? "" : "\x1b[32m", + cyan: shouldDisableColors ? "" : "\x1b[36m", }; +/** + * Pad a message to ensure it has a minimum length + * @param {string} message - The message to pad + * @param {number} minLength - The minimum length (default: MIN_MESSAGE_LENGTH) + * @returns {string} The padded message + */ +export function padMessage(message, minLength = MIN_MESSAGE_LENGTH) { + if (message.length >= minLength) { + return message; + } + return message.padEnd(minLength); +} + /** * Log a progress message in blue * @param {string} message - The message to log */ export function logProgress(message) { - console.log(`${colors.blue}${message}${colors.reset}`); + console.log(`${colors.blue}${padMessage(message)}${colors.reset}`); } /** @@ -23,15 +40,15 @@ export function logProgress(message) { * @param {string} message - The message to log */ export function logError(message) { - console.error(`${colors.red}${message}${colors.reset}`); + console.error(`${colors.red}${padMessage(message)}${colors.reset}`); } /** - * Log an info message in purple + * Log an info message in cyan * @param {string} message - The message to log */ export function logInfo(message) { - console.log(`${colors.purple}${message}${colors.reset}`); + console.log(`${colors.cyan}${padMessage(message)}${colors.reset}`); } /** @@ -39,7 +56,7 @@ export function logInfo(message) { * @param {string} message - The message to log */ export function logRaw(message) { - console.log(message); + console.log(padMessage(message)); } /** @@ -47,5 +64,5 @@ export function logRaw(message) { * @param {string} message - The message to log */ export function logSuccess(message) { - console.log(`${colors.green}${message}${colors.reset}`); + console.log(`${colors.green}${padMessage(message)}${colors.reset}`); } From 41836353db7ef85ef1e418717cdd8887b50c91aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Fri, 20 Feb 2026 17:33:50 +0100 Subject: [PATCH 091/166] refine logging and adjust devserver output handling --- .../meteor-rspack/lib/meteorRspackHelpers.js | 7 +++++ npm-packages/meteor-rspack/package-lock.json | 4 +-- npm-packages/meteor-rspack/package.json | 2 +- .../plugins/MeteorRspackOutputPlugin.js | 28 ++++++++----------- npm-packages/meteor-rspack/rspack.config.js | 14 ++++++++-- packages/rspack/lib/constants.js | 2 +- packages/rspack/lib/logging.js | 4 +-- packages/rspack/lib/processes.js | 23 ++++++++++++--- packages/tools-core/lib/log.js | 11 ++++++++ tools/isobuild/isopack.js | 4 +++ tools/modern-tests/test-helpers.js | 18 ++++++------ tools/runners/run-all.js | 2 +- tools/runners/run-log.js | 3 ++ tools/tests/run.js | 6 ++-- tools/tests/server-restart-port.js | 2 +- tools/tests/test-modes.js | 6 ++-- 16 files changed, 90 insertions(+), 46 deletions(-) diff --git a/npm-packages/meteor-rspack/lib/meteorRspackHelpers.js b/npm-packages/meteor-rspack/lib/meteorRspackHelpers.js index 1b77f977cb..e862b5c9ec 100644 --- a/npm-packages/meteor-rspack/lib/meteorRspackHelpers.js +++ b/npm-packages/meteor-rspack/lib/meteorRspackHelpers.js @@ -202,6 +202,12 @@ function disablePlugins(config, matchers) { return config; } +function outputMeteorRspack(data) { + const jsonString = JSON.stringify(data); + const output = `[Meteor-Rspack]${jsonString}[/Meteor-Rspack]`; + console.log(output); +} + module.exports = { compileWithMeteor, compileWithRspack, @@ -210,4 +216,5 @@ module.exports = { extendSwcConfig, makeWebNodeBuiltinsAlias, disablePlugins, + outputMeteorRspack, }; diff --git a/npm-packages/meteor-rspack/package-lock.json b/npm-packages/meteor-rspack/package-lock.json index 8a456ecb69..e114f89e32 100644 --- a/npm-packages/meteor-rspack/package-lock.json +++ b/npm-packages/meteor-rspack/package-lock.json @@ -1,12 +1,12 @@ { "name": "@meteorjs/rspack", - "version": "1.1.0-beta.7", + "version": "1.1.0-beta.8", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@meteorjs/rspack", - "version": "1.1.0-beta.7", + "version": "1.1.0-beta.8", "license": "ISC", "dependencies": { "fast-deep-equal": "^3.1.3", diff --git a/npm-packages/meteor-rspack/package.json b/npm-packages/meteor-rspack/package.json index a7f816bd95..37805b41db 100644 --- a/npm-packages/meteor-rspack/package.json +++ b/npm-packages/meteor-rspack/package.json @@ -1,6 +1,6 @@ { "name": "@meteorjs/rspack", - "version": "1.1.0-beta.7", + "version": "1.1.0-beta.8", "description": "Configuration logic for using Rspack in Meteor projects", "main": "index.js", "type": "commonjs", diff --git a/npm-packages/meteor-rspack/plugins/MeteorRspackOutputPlugin.js b/npm-packages/meteor-rspack/plugins/MeteorRspackOutputPlugin.js index 840e7b8631..a4494ae49e 100644 --- a/npm-packages/meteor-rspack/plugins/MeteorRspackOutputPlugin.js +++ b/npm-packages/meteor-rspack/plugins/MeteorRspackOutputPlugin.js @@ -4,10 +4,13 @@ // a new Rspack compilation happens. The JSON content is configurable // via plugin instantiation options. +const { outputMeteorRspack } = require('../lib/meteorRspackHelpers'); + class MeteorRspackOutputPlugin { constructor(options = {}) { this.pluginName = 'MeteorRspackOutputPlugin'; this.options = options; + this.compilationCount = 0; // The data to be output as JSON, can be a static object or a function this.getData = typeof options.getData === 'function' @@ -16,25 +19,16 @@ class MeteorRspackOutputPlugin { } apply(compiler) { - const { devServer } = compiler.options; - - let devServerUrl; - if (devServer) { - const protocol = devServer.server?.type === "https" ? "https" : "http"; - const host = - devServer.host && devServer.host !== "0.0.0.0" - ? devServer.host - : "localhost"; - const port = devServer.port ?? 8080; - devServerUrl = `${protocol}://${host}:${port}`; - } - // Hook into the 'done' event which fires after each compilation completes compiler.hooks.done.tap(this.pluginName, stats => { - const data = { ...(this.getData(stats) || {}), devServerUrl }; - const jsonString = JSON.stringify(data); - const output = `[Meteor-Rspack]${jsonString}[/Meteor-Rspack]`; - console.log(output); + this.compilationCount++; + const data = { + ...(this.getData(stats, { + compilationCount: this.compilationCount, + isRebuild: this.compilationCount > 1, + }) || {}), + }; + outputMeteorRspack(data); }); } } diff --git a/npm-packages/meteor-rspack/rspack.config.js b/npm-packages/meteor-rspack/rspack.config.js index 311a48bb38..fe065cc37c 100644 --- a/npm-packages/meteor-rspack/rspack.config.js +++ b/npm-packages/meteor-rspack/rspack.config.js @@ -22,6 +22,7 @@ const { extendSwcConfig, makeWebNodeBuiltinsAlias, disablePlugins, + outputMeteorRspack, } = require('./lib/meteorRspackHelpers.js'); const { prepareMeteorRspackConfig } = require("./lib/meteorRspackConfigFactory"); @@ -428,7 +429,7 @@ module.exports = async function (inMeteor = {}, argv = {}) { : []; // Not supported in Meteor yet (Rspack 1.7+ is enabled by default) const lazyCompilationConfig = { lazyCompilation: false }; - const loggingConfig = { infrastructureLogging: { level: "none" } }; + const loggingConfig = { stats: 'none', infrastructureLogging: { level: "none" } }; const clientEntry = isClient && isTest && isTestEager && isTestFullApp @@ -547,6 +548,13 @@ module.exports = async function (inMeteor = {}, argv = {}) { writeToDisk: filePath => /\.(html)$/.test(filePath) && !filePath.includes('.hot-update.'), }, + onListening(devServer) { + if (!devServer) return; + const { host, port } = devServer.options; + const protocol = devServer.options.server?.type === "https" ? "https" : "http"; + const devServerUrl = `${protocol}://${host || "localhost"}:${port}`; + outputMeteorRspack({ devServerUrl }); + }, }, }), ...merge(cacheStrategy, { experiments: { css: true } }), @@ -821,13 +829,15 @@ module.exports = async function (inMeteor = {}, argv = {}) { // Add MeteorRspackOutputPlugin as the last plugin to output compilation info const meteorRspackOutputPlugin = new MeteorRspackOutputPlugin({ - getData: (stats) => ({ + getData: (stats, { isRebuild, compilationCount }) => ({ name: config.name, mode: config.mode, hasErrors: stats.hasErrors(), hasWarnings: stats.hasWarnings(), timestamp: Date.now(), statsOverrided, + compilationCount, + isRebuild, }), }); config.plugins = [meteorRspackOutputPlugin, ...(config.plugins || [])]; diff --git a/packages/rspack/lib/constants.js b/packages/rspack/lib/constants.js index 379eb1c51a..d406dae5f8 100644 --- a/packages/rspack/lib/constants.js +++ b/packages/rspack/lib/constants.js @@ -5,7 +5,7 @@ export const DEFAULT_RSPACK_VERSION = '1.7.1'; -export const DEFAULT_METEOR_RSPACK_VERSION = '1.1.0-beta.7'; +export const DEFAULT_METEOR_RSPACK_VERSION = '1.1.0-beta.8'; export const DEFAULT_METEOR_RSPACK_REACT_HMR_VERSION = '1.4.3'; diff --git a/packages/rspack/lib/logging.js b/packages/rspack/lib/logging.js index 02d7d3cf08..560eeda50b 100644 --- a/packages/rspack/lib/logging.js +++ b/packages/rspack/lib/logging.js @@ -66,7 +66,7 @@ export function logHmrServerStarted(config) { if (hmrServerLogged) return; if (!config?.devServerUrl) return; hmrServerLogged = true; - logRaw(`=> Started Rspack HMR server at: ${config.devServerUrl}`); + logRaw(`=> Started Rspack HMR server at ${config.devServerUrl}/`); } /** @@ -93,6 +93,6 @@ export function logCompilationOutput(output, target, statsOverrided = false) { } compilationCount[target] = (compilationCount[target] || 0) + 1; const prefix = - target === "server" && compilationCount[target] > 1 ? "\n=>" : "=>"; + compilationCount[target] > 1 ? "\n=>" : "=>"; logRaw(`${prefix} Compiled Rspack ${target} app${separator}${cleaned}`); } diff --git a/packages/rspack/lib/processes.js b/packages/rspack/lib/processes.js index 873ab946b2..078adb633c 100644 --- a/packages/rspack/lib/processes.js +++ b/packages/rspack/lib/processes.js @@ -16,7 +16,8 @@ const { logError, logInfo, logRaw, -} = require('meteor/tools-core/lib/log'); + getRunLog, +} = require("meteor/tools-core/lib/log"); const { getMeteorAppDir, @@ -339,8 +340,20 @@ export function startRspackClientServe(options = {}) { env: { ...process.env, ...envs }, onStdout: (data) => { const { cleanedData, config } = parseMeteorRspackOutput(data); - logHmrServerStarted(config); - onCompile(cleanedData, config); + if (config && !!config?.devServerUrl) { + logHmrServerStarted(config); + } + if (config && (config?.compilationCount || 0) > 0) { + onCompile(cleanedData, config); + + if ( + config?.name?.includes("client") && + !config?.hasErrors && + config?.isRebuild + ) { + getRunLog()?.logClientRestart(); + } + } if (!cleanedData) return; if (shouldLogVerbose()) { logInfo(`[Rspack Client] ${cleanedData}`); @@ -429,7 +442,9 @@ export function startRspackServerWatch(options = {}) { env: { ...process.env, ...envs }, onStdout: (data) => { const { cleanedData, config } = parseMeteorRspackOutput(data); - onCompile(cleanedData, config); + if (config && (config?.compilationCount || 0) > 0) { + onCompile(cleanedData, config); + } if (!cleanedData) return; if (shouldLogVerbose()) { logInfo(`[Rspack Server] ${cleanedData}`); diff --git a/packages/tools-core/lib/log.js b/packages/tools-core/lib/log.js index 4550c18449..93f8d46602 100644 --- a/packages/tools-core/lib/log.js +++ b/packages/tools-core/lib/log.js @@ -66,3 +66,14 @@ export function logRaw(message) { export function logSuccess(message) { console.log(`${colors.green}${padMessage(message)}${colors.reset}`); } + +/** + * Get the runLogInstance from the Plugin object if it exists + * @returns {Object|undefined} The runLogInstance or undefined + */ +export function getRunLog() { + if (typeof Plugin !== 'undefined') { + return Plugin.runLogInstance; + } + return undefined; +} diff --git a/tools/isobuild/isopack.js b/tools/isobuild/isopack.js index 7bfb88b57e..91f62b9faf 100644 --- a/tools/isobuild/isopack.js +++ b/tools/isobuild/isopack.js @@ -23,6 +23,7 @@ import { requestGarbageCollection } from "../utils/gc.js"; import { Unibuild } from "./unibuild.js"; import rspackHelpers from "../tool-env/rspack"; import { getCurrentNodeBinDir, getDevBundle } from "../fs/files"; +import { runLogInstance } from "../runners/run-log"; var rejectBadPath = function (p) { if (p.match(/\.\./)) { @@ -530,6 +531,9 @@ Object.assign(Isopack.prototype, { // Share the rspackHelpers as part of plugin API rspackHelpers, + // Share the runLogInstance as part of plugin API + runLogInstance, + // 'extension' is a file extension without the separation dot // (eg 'js', 'coffee', 'coffee.md') // diff --git a/tools/modern-tests/test-helpers.js b/tools/modern-tests/test-helpers.js index 598112eb42..e0cf6ba813 100644 --- a/tools/modern-tests/test-helpers.js +++ b/tools/modern-tests/test-helpers.js @@ -207,7 +207,7 @@ export function testMeteorRspackBundler(options) { // Run the Meteor app to install Rspack const result = await runMeteorApp(tempDir, port, { - waitForOutput: "=> App running at:", + waitForOutput: "=> App running at", isMonorepo }); meteorProcess = result.meteorProcess; @@ -249,7 +249,7 @@ export function testMeteorRspackBundler(options) { test(`"meteor run" / should run and rebuild the app with Rspack`, async () => { // Run the Meteor app and wait for "restarted at" output const result = await runMeteorApp(tempDir, port, { - waitForOutput: "=> App running at:", + waitForOutput: "=> App running at", isMonorepo }); meteorProcess = result.meteorProcess; @@ -337,7 +337,7 @@ export function testMeteorRspackBundler(options) { test(`"meteor run --production" / should run and rebuild the app with Rspack in production`, async () => { // Run the Meteor app and wait for "restarted at" output const result = await runMeteorApp(tempDir, port, { - waitForOutput: "=> App running at:", + waitForOutput: "=> App running at", commandOptions: ['--production'], isMonorepo }); @@ -431,7 +431,7 @@ export function testMeteorRspackBundler(options) { test(`"meteor run --extra-packages bundle-visualizer --production" / should run with bundle-visualizer in production mode`, async () => { // Run the Meteor app with bundle-visualizer in production mode const result = await runMeteorApp(tempDir, port, { - waitForOutput: "=> App running at:", + waitForOutput: "=> App running at", commandOptions: ['--extra-packages', 'bundle-visualizer', '--production'], isMonorepo }); @@ -486,7 +486,7 @@ export function testMeteorRspackBundler(options) { test(`"meteor test${testFullApp ? ' --full-app' : ''}" / should run tests with Rspack`, async () => { const result = await runMeteorTests(tempDir, port, { - waitForOutput: "=> App running at:", + waitForOutput: "=> App running at", commandOptions: testFullApp ? ['--full-app'] : [], checkTestResults: false, isMonorepo @@ -561,7 +561,7 @@ export function testMeteorRspackBundler(options) { test(`"meteor test${testFullApp ? ' --full-app' : ''} --once" / should run tests once with Rspack`, async () => { // Test the app with Rspack once const result = await runMeteorTests(tempDir, port, { - waitForOutput: "=> App running at:", + waitForOutput: "=> App running at", commandOptions: testFullApp ? ['--full-app', '--once'] : ['--once'], checkTestResults: true, isMonorepo @@ -778,7 +778,7 @@ export function testMeteorSkeleton(options) { test(`"meteor run" / should run the ${skeletonName} app`, async () => { // Run the newly created app const result = await runMeteorApp(tempDir, port, { - waitForOutput: "=> App running at:" + waitForOutput: "=> App running at" }); meteorProcess = result.meteorProcess; @@ -811,7 +811,7 @@ export function testMeteorSkeleton(options) { test(`"meteor run --production" / should run the ${skeletonName} app in production mode`, async () => { // Run the app in production mode const result = await runMeteorApp(tempDir, port, { - waitForOutput: "=> App running at:", + waitForOutput: "=> App running at", commandOptions: ["--production"] }); meteorProcess = result.meteorProcess; @@ -855,7 +855,7 @@ export function testMeteorSkeleton(options) { // Run tests once for the app const result = await runMeteorTests(tempDir, port, { - waitForOutput: "=> App running at:", + waitForOutput: "=> App running at", commandOptions: ["--once"], checkTestResults: true }); diff --git a/tools/runners/run-all.js b/tools/runners/run-all.js index 594e20589c..b2b2567b48 100644 --- a/tools/runners/run-all.js +++ b/tools/runners/run-all.js @@ -218,7 +218,7 @@ class Runner { { arrow: true } ); } else { - runLog.log("App running at: " + self.rootUrl, { arrow: true }); + runLog.log("App running at " + self.rootUrl, { arrow: true }); } if (process.platform === "win32") { diff --git a/tools/runners/run-log.js b/tools/runners/run-log.js index 51efd8a4b3..52915814d6 100644 --- a/tools/runners/run-log.js +++ b/tools/runners/run-log.js @@ -225,3 +225,6 @@ var runLogInstance = new RunLog; function (method) { exports[method] = runLogInstance[method].bind(runLogInstance); }); + +// Export the singleton instance for use in plugins +exports.runLogInstance = runLogInstance; diff --git a/tools/tests/run.js b/tools/tests/run.js index 0093676d50..e5c7ffeab6 100644 --- a/tools/tests/run.js +++ b/tools/tests/run.js @@ -463,12 +463,12 @@ selftest.define("'meteor run --port' accepts/rejects proper values", async funct run = s.run("run", "--port", "3500"); run.waitSecs(30); - await run.match('App running at: http://localhost:3500/'); + await run.match('App running at http://localhost:3500/'); await run.stop(); run = s.run("run", "--port", "127.0.0.1:3500"); run.waitSecs(30); - await run.match('App running at: http://127.0.0.1:3500/'); + await run.match('App running at http://127.0.0.1:3500/'); await run.stop(); }); @@ -491,7 +491,7 @@ selftest.define("update package during run", async function () { var runRun = s.run(); runRun.waitSecs(3); - await runRun.match("App running at:"); + await runRun.match("App running at"); var updateRun = s.run("update", "glasser:package-for-selftest"); await updateRun.match( diff --git a/tools/tests/server-restart-port.js b/tools/tests/server-restart-port.js index 47a7e3a49c..7a02aea685 100644 --- a/tools/tests/server-restart-port.js +++ b/tools/tests/server-restart-port.js @@ -23,5 +23,5 @@ async function testHelper(server) { (match, n) => `module.id, ${ ++n }`, )); - await run.match("Meteor server restarted at: http://localhost:21000/"); + await run.match("Meteor server restarted at http://localhost:21000/"); } diff --git a/tools/tests/test-modes.js b/tools/tests/test-modes.js index f06eccebeb..eca226e461 100644 --- a/tools/tests/test-modes.js +++ b/tools/tests/test-modes.js @@ -20,17 +20,17 @@ selftest.define("'meteor test --port' accepts/rejects proper values", async func run = s.run("test", "--port", "3700", "--driver-package", "tmeasday:acceptance-test-driver"); run.waitSecs(30); - await run.match('App running at: http://localhost:3700/'); + await run.match('App running at http://localhost:3700/'); await run.stop(); run = s.run("test", "--port", "127.0.0.1:3700", "--driver-package", "tmeasday:acceptance-test-driver"); run.waitSecs(30); - await run.match('App running at: http://127.0.0.1:3700/'); + await run.match('App running at http://127.0.0.1:3700/'); await run.stop(); run = s.run("test", "--port", "[::]:3700", "--driver-package", "tmeasday:acceptance-test-driver"); run.waitSecs(30); - await run.match('App running at: http://[::]:3700/'); + await run.match('App running at http://[::]:3700/'); await run.stop(); }); From dd5ea37b4c0270957ada1f2dd2e6833d04a1d9f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Fri, 20 Feb 2026 17:43:40 +0100 Subject: [PATCH 092/166] add `isProfile` support and adjust logging configuration --- npm-packages/meteor-rspack/package-lock.json | 4 +- npm-packages/meteor-rspack/package.json | 2 +- npm-packages/meteor-rspack/rspack.config.js | 5 +- packages/rspack/lib/constants.js | 2 +- packages/rspack/lib/processes.js | 124 ++++++++++++------- 5 files changed, 86 insertions(+), 51 deletions(-) diff --git a/npm-packages/meteor-rspack/package-lock.json b/npm-packages/meteor-rspack/package-lock.json index e114f89e32..1954368e14 100644 --- a/npm-packages/meteor-rspack/package-lock.json +++ b/npm-packages/meteor-rspack/package-lock.json @@ -1,12 +1,12 @@ { "name": "@meteorjs/rspack", - "version": "1.1.0-beta.8", + "version": "1.1.0-beta.9", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@meteorjs/rspack", - "version": "1.1.0-beta.8", + "version": "1.1.0-beta.9", "license": "ISC", "dependencies": { "fast-deep-equal": "^3.1.3", diff --git a/npm-packages/meteor-rspack/package.json b/npm-packages/meteor-rspack/package.json index 37805b41db..e07aeabe79 100644 --- a/npm-packages/meteor-rspack/package.json +++ b/npm-packages/meteor-rspack/package.json @@ -1,6 +1,6 @@ { "name": "@meteorjs/rspack", - "version": "1.1.0-beta.8", + "version": "1.1.0-beta.9", "description": "Configuration logic for using Rspack in Meteor projects", "main": "index.js", "type": "commonjs", diff --git a/npm-packages/meteor-rspack/rspack.config.js b/npm-packages/meteor-rspack/rspack.config.js index fe065cc37c..9b90a14b1e 100644 --- a/npm-packages/meteor-rspack/rspack.config.js +++ b/npm-packages/meteor-rspack/rspack.config.js @@ -227,6 +227,7 @@ module.exports = async function (inMeteor = {}, argv = {}) { const isTestLike = !!Meteor.isTestLike; const swcExternalHelpers = !!Meteor.swcExternalHelpers; const isNative = !!Meteor.isNative; + const isProfile = !!Meteor.isProfile; const mode = isProd ? 'production' : 'development'; const projectDir = process.cwd(); const projectConfigPath = Meteor.projectConfigPath || path.resolve(projectDir, 'rspack.config.js'); @@ -429,7 +430,9 @@ module.exports = async function (inMeteor = {}, argv = {}) { : []; // Not supported in Meteor yet (Rspack 1.7+ is enabled by default) const lazyCompilationConfig = { lazyCompilation: false }; - const loggingConfig = { stats: 'none', infrastructureLogging: { level: "none" } }; + const loggingConfig = isProfile + ? {} + : { stats: 'none', infrastructureLogging: { level: 'none' } }; const clientEntry = isClient && isTest && isTestEager && isTestFullApp diff --git a/packages/rspack/lib/constants.js b/packages/rspack/lib/constants.js index d406dae5f8..e56de1b201 100644 --- a/packages/rspack/lib/constants.js +++ b/packages/rspack/lib/constants.js @@ -5,7 +5,7 @@ export const DEFAULT_RSPACK_VERSION = '1.7.1'; -export const DEFAULT_METEOR_RSPACK_VERSION = '1.1.0-beta.8'; +export const DEFAULT_METEOR_RSPACK_VERSION = '1.1.0-beta.9'; export const DEFAULT_METEOR_RSPACK_REACT_HMR_VERSION = '1.4.3'; diff --git a/packages/rspack/lib/processes.js b/packages/rspack/lib/processes.js index 078adb633c..7d00909aa2 100644 --- a/packages/rspack/lib/processes.js +++ b/packages/rspack/lib/processes.js @@ -67,6 +67,7 @@ import { shouldLogVerbose, stripRspackLabel, } from "./logging"; +import { isMeteorAppProfile } from "../../tools-core/lib/meteor"; /** * Calculates the devServerPort based on process.env.PORT @@ -231,29 +232,52 @@ export function getRspackEnv({ isClient, isServer, isTest: inIsTest, isTestLike: const isBlazeHotEnabled = isMeteorBlazeHotProject(); const isBundleVisualizerEnabled = isMeteorBundleVisualizerProject(); + const isProfile = isMeteorAppProfile(); + const swcExternalHelpers = checkNpmDependencyExists('@swc/helpers'); const configPath = getConfigFilePath(); const projectConfigPath = getCustomConfigFilePath(); const pairs = [ - ['isDevelopment', isMeteorAppDevelopment()], - ['isProduction', isMeteorAppProduction()], - ['isDebug', isMeteorAppDebug()], - ['isVerbose', isMeteorAppConfigModernVerbose()], - ['isTest', isTest], - ...(isTestLike ? [['isTestLike', isTestLike || isTest]] : []), - ...(isTestLike && isTestFullApp && [['isTestFullApp', isTestFullApp]] || []), - ...(isTestLike && isTestModule && [['isTestModule', isTestModule]] || []), - ...(isTestLike && isTestEager && [['isTestEager', isTestEager]] || []), - ['isRun', isMeteorAppRun()], - ['isBuild', isMeteorAppBuild()], - ['isNative', isMeteorAppNative()], - ['isClient', isClient], - ['isServer', isServer], - ['entryPath', getBuildFilePath({ ...module, ...env, ...side, isTestModule, role: FILE_ROLE.entry }) ], - ['outputPath', getBuildFilePath({ ...module, ...env, ...side, isTestModule, role: FILE_ROLE.output }) ], - ['outputFilename', + ["isDevelopment", isMeteorAppDevelopment()], + ["isProduction", isMeteorAppProduction()], + ["isDebug", isMeteorAppDebug()], + ["isVerbose", isMeteorAppConfigModernVerbose()], + ...((isProfile && [["isProfile", isMeteorAppProfile()]]) || []), + ["isTest", isTest], + ...(isTestLike ? [["isTestLike", isTestLike || isTest]] : []), + ...((isTestLike && isTestFullApp && [["isTestFullApp", isTestFullApp]]) || + []), + ...((isTestLike && isTestModule && [["isTestModule", isTestModule]]) || []), + ...((isTestLike && isTestEager && [["isTestEager", isTestEager]]) || []), + ["isRun", isMeteorAppRun()], + ["isBuild", isMeteorAppBuild()], + ["isNative", isMeteorAppNative()], + ["isClient", isClient], + ["isServer", isServer], + [ + "entryPath", + getBuildFilePath({ + ...module, + ...env, + ...side, + isTestModule, + role: FILE_ROLE.entry, + }), + ], + [ + "outputPath", + getBuildFilePath({ + ...module, + ...env, + ...side, + isTestModule, + role: FILE_ROLE.output, + }), + ], + [ + "outputFilename", getBuildFilePath({ ...env, ...side, @@ -262,41 +286,49 @@ export function getRspackEnv({ isClient, isServer, isTest: inIsTest, isTestLike: onlyFilename: true, }), ], - ['runPath', getBuildFilePath({ ...module, ...env, ...side, ...commandRole }) ], - ['buildContext', RSPACK_BUILD_CONTEXT], - ['chunksContext', RSPACK_CHUNKS_CONTEXT], - ['assetsContext', RSPACK_ASSETS_CONTEXT], - ['devServerPort', process.env.RSPACK_DEVSERVER_PORT], - ['projectConfigPath', projectConfigPath], - ['configPath', configPath], + [ + "runPath", + getBuildFilePath({ ...module, ...env, ...side, ...commandRole }), + ], + ["buildContext", RSPACK_BUILD_CONTEXT], + ["chunksContext", RSPACK_CHUNKS_CONTEXT], + ["assetsContext", RSPACK_ASSETS_CONTEXT], + ["devServerPort", process.env.RSPACK_DEVSERVER_PORT], + ["projectConfigPath", projectConfigPath], + ["configPath", configPath], ...((isTest && initialEntrypoints.testClient && initialEntrypoints.testServer && [ - ['testClientEntry', initialEntrypoints.testClient], - ['testServerEntry', initialEntrypoints.testServer], + ["testClientEntry", initialEntrypoints.testClient], + ["testServerEntry", initialEntrypoints.testServer], ]) || (isTest && initialEntrypoints.testModule && [ - ['testEntry', initialEntrypoints.testModule], - ]) || [ - ['mainClientEntry', initialEntrypoints.mainClient], - ['mainClientHtmlEntry', initialEntrypoints.mainClientHtml], - ['mainServerEntry', initialEntrypoints.mainServer], - ]), - ...(swcExternalHelpers && [['swcExternalHelpers', swcExternalHelpers]] || []), - ...(isReactEnabled && [['isReactEnabled', isReactEnabled]] || []), - ...(isBlazeEnabled && [['isBlazeEnabled', isBlazeEnabled]] || []), - ...(isBlazeHotEnabled && [['isBlazeHotEnabled', isBlazeHotEnabled]] || []), - ...(isTypescriptEnabled && [['isTypescriptEnabled', isTypescriptEnabled]] || []), - ...(isAngularEnabled && [['isAngularEnabled', isAngularEnabled]] || []), - ...(isTsxEnabled && [['isTsxEnabled', isTsxEnabled]] || []), - ...(isJsxEnabled && [['isJsxEnabled', isJsxEnabled]] || []), - ...(isBundleVisualizerEnabled && [ - ['isBundleVisualizerEnabled', isBundleVisualizerEnabled], - ['rsdoctorClientPort', process.env.RSDOCTOR_CLIENT_PORT], - ['rsdoctorServerPort', process.env.RSDOCTOR_SERVER_PORT], - ] || []), - + ["testEntry", initialEntrypoints.testModule], + ]) || [ + ["mainClientEntry", initialEntrypoints.mainClient], + ["mainClientHtmlEntry", initialEntrypoints.mainClientHtml], + ["mainServerEntry", initialEntrypoints.mainServer], + ]), + ...((swcExternalHelpers && [["swcExternalHelpers", swcExternalHelpers]]) || + []), + ...((isReactEnabled && [["isReactEnabled", isReactEnabled]]) || []), + ...((isBlazeEnabled && [["isBlazeEnabled", isBlazeEnabled]]) || []), + ...((isBlazeHotEnabled && [["isBlazeHotEnabled", isBlazeHotEnabled]]) || + []), + ...((isTypescriptEnabled && [ + ["isTypescriptEnabled", isTypescriptEnabled], + ]) || + []), + ...((isAngularEnabled && [["isAngularEnabled", isAngularEnabled]]) || []), + ...((isTsxEnabled && [["isTsxEnabled", isTsxEnabled]]) || []), + ...((isJsxEnabled && [["isJsxEnabled", isJsxEnabled]]) || []), + ...((isBundleVisualizerEnabled && [ + ["isBundleVisualizerEnabled", isBundleVisualizerEnabled], + ["rsdoctorClientPort", process.env.RSDOCTOR_CLIENT_PORT], + ["rsdoctorServerPort", process.env.RSDOCTOR_SERVER_PORT], + ]) || + []), ].filter(Boolean); // Create environment variables object with bannerOutput From 9ead928f5fc2457f0d272b47d8a1ce84986e0d50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 24 Feb 2026 09:16:08 +0100 Subject: [PATCH 093/166] adjust logging defaults to include verbose --- npm-packages/meteor-rspack/package-lock.json | 4 ++-- npm-packages/meteor-rspack/package.json | 2 +- npm-packages/meteor-rspack/rspack.config.js | 6 ++++-- packages/rspack/lib/constants.js | 2 +- packages/rspack/lib/logging.js | 6 ++++-- packages/tools-core/lib/log.js | 2 +- 6 files changed, 13 insertions(+), 9 deletions(-) diff --git a/npm-packages/meteor-rspack/package-lock.json b/npm-packages/meteor-rspack/package-lock.json index 1954368e14..a6a08c2f4b 100644 --- a/npm-packages/meteor-rspack/package-lock.json +++ b/npm-packages/meteor-rspack/package-lock.json @@ -1,12 +1,12 @@ { "name": "@meteorjs/rspack", - "version": "1.1.0-beta.9", + "version": "1.1.0-beta.10", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@meteorjs/rspack", - "version": "1.1.0-beta.9", + "version": "1.1.0-beta.10", "license": "ISC", "dependencies": { "fast-deep-equal": "^3.1.3", diff --git a/npm-packages/meteor-rspack/package.json b/npm-packages/meteor-rspack/package.json index e07aeabe79..f180c96a7d 100644 --- a/npm-packages/meteor-rspack/package.json +++ b/npm-packages/meteor-rspack/package.json @@ -1,6 +1,6 @@ { "name": "@meteorjs/rspack", - "version": "1.1.0-beta.9", + "version": "1.1.0-beta.10", "description": "Configuration logic for using Rspack in Meteor projects", "main": "index.js", "type": "commonjs", diff --git a/npm-packages/meteor-rspack/rspack.config.js b/npm-packages/meteor-rspack/rspack.config.js index 9b90a14b1e..b2b2bcbeea 100644 --- a/npm-packages/meteor-rspack/rspack.config.js +++ b/npm-packages/meteor-rspack/rspack.config.js @@ -228,6 +228,7 @@ module.exports = async function (inMeteor = {}, argv = {}) { const swcExternalHelpers = !!Meteor.swcExternalHelpers; const isNative = !!Meteor.isNative; const isProfile = !!Meteor.isProfile; + const isVerbose = !!Meteor.isVerbose; const mode = isProd ? 'production' : 'development'; const projectDir = process.cwd(); const projectConfigPath = Meteor.projectConfigPath || path.resolve(projectDir, 'rspack.config.js'); @@ -430,9 +431,10 @@ module.exports = async function (inMeteor = {}, argv = {}) { : []; // Not supported in Meteor yet (Rspack 1.7+ is enabled by default) const lazyCompilationConfig = { lazyCompilation: false }; - const loggingConfig = isProfile + const shouldLogVerbose = isProfile || isVerbose; + const loggingConfig = shouldLogVerbose ? {} - : { stats: 'none', infrastructureLogging: { level: 'none' } }; + : { stats: "errors-warnings", infrastructureLogging: { level: "warn" } }; const clientEntry = isClient && isTest && isTestEager && isTestFullApp diff --git a/packages/rspack/lib/constants.js b/packages/rspack/lib/constants.js index e56de1b201..0b99653fe9 100644 --- a/packages/rspack/lib/constants.js +++ b/packages/rspack/lib/constants.js @@ -5,7 +5,7 @@ export const DEFAULT_RSPACK_VERSION = '1.7.1'; -export const DEFAULT_METEOR_RSPACK_VERSION = '1.1.0-beta.9'; +export const DEFAULT_METEOR_RSPACK_VERSION = '1.1.0-beta.10'; export const DEFAULT_METEOR_RSPACK_REACT_HMR_VERSION = '1.4.3'; diff --git a/packages/rspack/lib/logging.js b/packages/rspack/lib/logging.js index 560eeda50b..6c938843d5 100644 --- a/packages/rspack/lib/logging.js +++ b/packages/rspack/lib/logging.js @@ -81,6 +81,7 @@ export function logHmrServerStarted(config) { export function logCompilationOutput(output, target, statsOverrided = false) { let cleaned; let separator; + // Logs original Rspack logging when stats overrided by user if (statsOverrided) { cleaned = stripRspackLabel(output); separator = "\n"; @@ -90,9 +91,10 @@ export function logCompilationOutput(output, target, statsOverrided = false) { .trim() .replace(/\s*compiled\s*/g, ""); separator = cleaned.includes("\n") ? ":\n" : " "; + // Ignore successful logs on default Meteor-Rspack logging + if (/\s*successfully\s*/g.test(cleaned)) return; } compilationCount[target] = (compilationCount[target] || 0) + 1; - const prefix = - compilationCount[target] > 1 ? "\n=>" : "=>"; + const prefix = compilationCount[target] > 1 ? "\n=>" : "=>"; logRaw(`${prefix} Compiled Rspack ${target} app${separator}${cleaned}`); } diff --git a/packages/tools-core/lib/log.js b/packages/tools-core/lib/log.js index 93f8d46602..6225d0da06 100644 --- a/packages/tools-core/lib/log.js +++ b/packages/tools-core/lib/log.js @@ -2,7 +2,7 @@ const shouldDisableColors = !!process.env.METEOR_DISABLE_COLORS; // Minimum message length for consistent log formatting -const MIN_MESSAGE_LENGTH = 50; +const MIN_MESSAGE_LENGTH = 80; // ANSI color codes const colors = { From a5404b1e6181d403b0307b8c91aec6d7194c8c97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 24 Feb 2026 10:06:37 +0100 Subject: [PATCH 094/166] remove colon from restart log message --- tools/runners/run-log.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/runners/run-log.js b/tools/runners/run-log.js index 52915814d6..f533b1787a 100644 --- a/tools/runners/run-log.js +++ b/tools/runners/run-log.js @@ -158,7 +158,7 @@ Object.assign(RunLog.prototype, { self.consecutiveRestartMessages = 1; } - var message = "=> Meteor server restarted at: " + options.rootUrl; + var message = "=> Meteor server restarted at " + options.rootUrl; if (self.consecutiveRestartMessages > 1) { message += " (x" + self.consecutiveRestartMessages + ")"; } From dab79e1e0e0045d5db40a11cd898a41d8a070746 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 24 Feb 2026 12:28:21 +0100 Subject: [PATCH 095/166] update Meteor-Rspack documentation with detailed logging configuration and verbose mode instructions --- .../rspack-bundler-integration.md | 51 ++++++++++++++----- 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/v3-docs/docs/about/modern-build-stack/rspack-bundler-integration.md b/v3-docs/docs/about/modern-build-stack/rspack-bundler-integration.md index a4c0b56c33..6d124131b3 100644 --- a/v3-docs/docs/about/modern-build-stack/rspack-bundler-integration.md +++ b/v3-docs/docs/about/modern-build-stack/rspack-bundler-integration.md @@ -162,16 +162,47 @@ You can use flags to control the final configuration based on the environment. T Some configurations in the Rspack config are reserved for the Meteor-Rspack setup to work, such as Rspack options inside the `entry` and `output` objects. These will trigger warnings if modified. All other settings can be overridden, giving you the flexibility to make any setup compatible with the modern bundler. -If you want to see the final Rspack config applying your overrides, you can enable verbose mode in the modern build stack. +If you want to see the final Rspack config applying your overrides, you can enable [verbose mode](#enable-verbose-mode) in the modern build stack. + +## Logging + +Starting with Meteor 3.4.1, the log output for the default Meteor-Rspack app is simplified to stay as close as possible to Meteor's native experience. By default, logs are less verbose and only show essential information like server restarts and client modifications. + +If there are any warnings or errors, Rspack logs will be shown with their own style and colors. + +### Enable Verbose Mode + +If you need more details about Meteor and Rspack processes, you can enable verbose mode in your `package.json`: ```json -"meteor": { - "modern": { - "verbose": true +{ + "meteor": { + "modern": { + "verbose": true + } } } ``` +### Advanced Rspack Logging + +For even deeper insights into the Rspack compilation process, you can configure [`stats`](https://rspack.rs/config/stats#stats) and [`infrastructureLogging`](https://rspack.rs/config/infrastructure-logging#infrastructurelogging) directly in your `rspack.config.js`. + +- **`stats`**: Controls what bundle information is displayed on each compilation. +- **`infrastructureLogging`**: Controls Rspack infrastructure logs, including HMR verbosity in both the terminal and the browser. To enable detailed logs for updates and serving client code changes, set `infrastructureLogging.level` to `'info'` or higher (it is not enabled by default). + +```javascript +module.exports = defineConfig(Meteor => { + return { + stats: 'detailed', // or other Rspack stats options + infrastructureLogging: { + level: 'info', + }, + // ... rest of your config + }; +}); +``` + ## Migration Topics ### Entry Points @@ -232,15 +263,7 @@ if (condition) { For background, see: [Why nested import](https://github.com/benjamn/reify/blob/main/WHY_NEST_IMPORTS.md). -To use Rspack, migrate your nested imports to a standard form. To identify and fix nested imports in your project, [use verbose mode in Meteor 3.3’s modern transpiler](./meteor-bundler-optimizations.md#optimize-swc-and-handle-fallbacks). Enable it with: - -```json -"meteor": { - "modern": { - "verbose": true - } -} -``` +To use Rspack, migrate your nested imports to a standard form. To identify and fix nested imports in your project, use [verbose mode](#enable-verbose-mode) to see which files are failing. When you run your app, `[Transpiler]` logs will show each file. Focus on `(app)` files that fail with messages like: @@ -671,7 +694,7 @@ Meteor cache remains active and continues to handle Atmosphere packages and inte This Rspack cache is enabled by default in persistent mode. If you [encounter issues](https://github.com/web-infra-dev/rspack/issues/11804) or prefer to disable it, you can do so in your `rspack.config.js` using the helper: -```json +```javascript const { defineConfig } = require('@meteorjs/rspack'); const { rspack } = require('@rspack/core'); const NodePolyfillPlugin = require('node-polyfill-webpack-plugin'); From bfff1a7797e69bc6b312aa10b377d4d347794ed4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 24 Feb 2026 12:34:43 +0100 Subject: [PATCH 096/166] update Meteor-Rspack documentation with detailed logging configuration and verbose mode instructions --- .../docs/about/modern-build-stack/rspack-bundler-integration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v3-docs/docs/about/modern-build-stack/rspack-bundler-integration.md b/v3-docs/docs/about/modern-build-stack/rspack-bundler-integration.md index 6d124131b3..6143054120 100644 --- a/v3-docs/docs/about/modern-build-stack/rspack-bundler-integration.md +++ b/v3-docs/docs/about/modern-build-stack/rspack-bundler-integration.md @@ -168,7 +168,7 @@ If you want to see the final Rspack config applying your overrides, you can enab Starting with Meteor 3.4.1, the log output for the default Meteor-Rspack app is simplified to stay as close as possible to Meteor's native experience. By default, logs are less verbose and only show essential information like server restarts and client modifications. -If there are any warnings or errors, Rspack logs will be shown with their own style and colors. +If there are any compilation warnings or errors, Rspack logs will be shown with their own style and colors. ### Enable Verbose Mode From 8b284db434d85802b964301d83887fc8ba5f27ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 24 Feb 2026 12:51:35 +0100 Subject: [PATCH 097/166] add support for `METEOR_LOCAL_DIR` to isolate Rspack build contexts for multiple app instances --- npm-packages/meteor-rspack/rspack.config.js | 18 +++++++++++++++--- packages/rspack/lib/constants.js | 12 +++++++++--- .../rspack-bundler-integration.md | 16 ++++++++++++++++ v3-docs/docs/cli/environment-variables.md | 14 ++++++++++++++ 4 files changed, 54 insertions(+), 6 deletions(-) diff --git a/npm-packages/meteor-rspack/rspack.config.js b/npm-packages/meteor-rspack/rspack.config.js index b2b2bcbeea..0f119ac35b 100644 --- a/npm-packages/meteor-rspack/rspack.config.js +++ b/npm-packages/meteor-rspack/rspack.config.js @@ -263,9 +263,21 @@ module.exports = async function (inMeteor = {}, argv = {}) { const serverOutputDir = path.resolve(projectDir, 'private'); // Determine context for bundles and assets - const buildContext = Meteor.buildContext || '_build'; - const assetsContext = Meteor.assetsContext || 'build-assets'; - const chunksContext = Meteor.chunksContext || 'build-chunks'; + const meteorLocalDirName = process.env.METEOR_LOCAL_DIR + ? path.basename(process.env.METEOR_LOCAL_DIR.replace(/\\/g, '/')) + : ''; + const buildContext = + Meteor.buildContext || + process.env.RSPACK_BUILD_CONTEXT || + `_build${(meteorLocalDirName && `-${meteorLocalDirName}`) || ''}`; + const assetsContext = + Meteor.assetsContext || + process.env.RSPACK_ASSETS_CONTEXT || + `build-assets${(meteorLocalDirName && `-${meteorLocalDirName}`) || ''}`; + const chunksContext = + Meteor.chunksContext || + process.env.RSPACK_CHUNKS_CONTEXT || + `build-chunks${(meteorLocalDirName && `-${meteorLocalDirName}`) || ''}`; // Determine build output and pass to Meteor const buildOutputDir = path.resolve(projectDir, buildContext, outputDir); diff --git a/packages/rspack/lib/constants.js b/packages/rspack/lib/constants.js index 0b99653fe9..3d202b749b 100644 --- a/packages/rspack/lib/constants.js +++ b/packages/rspack/lib/constants.js @@ -3,6 +3,8 @@ * @description Constants and global state keys for Rspack plugin */ +import path from 'path'; + export const DEFAULT_RSPACK_VERSION = '1.7.1'; export const DEFAULT_METEOR_RSPACK_VERSION = '1.1.0-beta.10'; @@ -46,6 +48,10 @@ export const GLOBAL_STATE_KEYS = { const meteorConfig = typeof Plugin !== 'undefined' ? Plugin?.getMeteorConfig() : null; +const meteorLocalDirName = process.env.METEOR_LOCAL_DIR + ? path.basename(process.env.METEOR_LOCAL_DIR.replace(/\\/g, '/')) + : ''; + /** * Directory name for Rspack build context * Can be overridden with RSPACK_BUILD_CONTEXT environment variable @@ -54,7 +60,7 @@ const meteorConfig = typeof Plugin !== 'undefined' ? Plugin?.getMeteorConfig() : export const RSPACK_BUILD_CONTEXT = meteorConfig?.buildContext || process.env.RSPACK_BUILD_CONTEXT || - '_build'; + `_build${(meteorLocalDirName && `-${meteorLocalDirName}`) || ''}`; process.env.RSPACK_BUILD_CONTEXT = RSPACK_BUILD_CONTEXT; @@ -66,7 +72,7 @@ process.env.RSPACK_BUILD_CONTEXT = RSPACK_BUILD_CONTEXT; export const RSPACK_ASSETS_CONTEXT = meteorConfig?.assetsContext || process.env.RSPACK_ASSETS_CONTEXT || - 'build-assets'; + `build-assets${(meteorLocalDirName && `-${meteorLocalDirName}`) || ''}`; process.env.RSPACK_ASSETS_CONTEXT = RSPACK_ASSETS_CONTEXT; @@ -78,7 +84,7 @@ process.env.RSPACK_ASSETS_CONTEXT = RSPACK_ASSETS_CONTEXT; export const RSPACK_CHUNKS_CONTEXT = meteorConfig?.chunksContext || process.env.RSPACK_CHUNKS_CONTEXT || - 'build-chunks'; + `build-chunks${(meteorLocalDirName && `-${meteorLocalDirName}`) || ''}`; process.env.RSPACK_CHUNKS_CONTEXT = RSPACK_CHUNKS_CONTEXT; diff --git a/v3-docs/docs/about/modern-build-stack/rspack-bundler-integration.md b/v3-docs/docs/about/modern-build-stack/rspack-bundler-integration.md index 6143054120..0b418710de 100644 --- a/v3-docs/docs/about/modern-build-stack/rspack-bundler-integration.md +++ b/v3-docs/docs/about/modern-build-stack/rspack-bundler-integration.md @@ -782,6 +782,22 @@ module.exports = defineConfig(Meteor => ({ })); ``` +### Running Multiple Instances + +By default, Meteor and Rspack use fixed directories for their build caches (`.meteor/local` and `_build`). If you try to run multiple instances of the same app simultaneously, they may conflict by attempting to write to the same folders. + +To run multiple instances, you can use the `METEOR_LOCAL_DIR` environment variable to specify a unique local directory for each instance. When this variable is set, the Meteor-Rspack integration automatically extracts the directory name and uses it as a suffix for Rspack's build contexts (`_build`, `build-chunks`, and `build-assets`), ensuring complete isolation between instances. + +```bash +# Instance 1 +PORT=3000 METEOR_LOCAL_DIR=.meteor/local-1 meteor run + +# Instance 2 +PORT=3001 METEOR_LOCAL_DIR=.meteor/local-2 meteor run +``` + +For more details on how this variable affects Rspack, see the [`METEOR_LOCAL_DIR`](../../cli/environment-variables.md#meteor_local_dir) documentation. + ## Benefits Meteor–Rspack integration sends your app code to Rspack to use modern bundler features. Meteor then uses Rspack’s output to handle Meteor-specific tasks (like Atmosphere package compilation) and create the final bundle. diff --git a/v3-docs/docs/cli/environment-variables.md b/v3-docs/docs/cli/environment-variables.md index b67bc28d91..99745b0c22 100644 --- a/v3-docs/docs/cli/environment-variables.md +++ b/v3-docs/docs/cli/environment-variables.md @@ -95,6 +95,20 @@ This way each command only processes the files it actually needs, reducing build `METEOR_IGNORE` is automatically set when using the [Rspack bundler integration](../about/modern-build-stack/rspack-bundler-integration.md). Since Rspack handles the client and server app bundling, Meteor's bundler should only worry about what it strictly needs for the Meteor-Rspack integration. By using `METEOR_IGNORE` to exclude folders and dependencies that Rspack already manages or that are irrelevant to Meteor's side of the build, you ensure the most speed is gained from the Rspack delegation. ::: +## METEOR_LOCAL_DIR +(_development_) + +This environment variable allows you to change the location of the `.meteor/local` directory, which Meteor uses to store its build cache and other local state. This is useful for running multiple instances of the same app with different local states or for redirecting the local directory to a different drive or path. + +When using the [Rspack bundler integration](../about/modern-build-stack/rspack-bundler-integration.md), `METEOR_LOCAL_DIR` also influences the Rspack build context. It extracts the name of the folder represented in the path and appends it as a suffix to the following Rspack constants: +- `RSPACK_BUILD_CONTEXT` +- `RSPACK_CHUNKS_CONTEXT` +- `RSPACK_ASSETS_CONTEXT` + +For example, if `METEOR_LOCAL_DIR` is set to `/path/to/.meteor/local-custom`, Rspack will use `_build-local-custom`, `build-chunks-local-custom`, and `build-assets-local-custom` as its context directories, ensuring that build artifacts remain isolated for that specific local environment. + +For more information, see the [Running Multiple Instances](../about/modern-build-stack/rspack-bundler-integration.md#running-multiple-instances) section in the Rspack documentation. + ## METEOR_PROFILE (_development_) From d9b11d45560a5a9373057124b4d2221c0d69c3a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 24 Feb 2026 13:02:20 +0100 Subject: [PATCH 098/166] add `buildContext` support to cache strategy for improved Rspack context isolation --- npm-packages/meteor-rspack/rspack.config.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/npm-packages/meteor-rspack/rspack.config.js b/npm-packages/meteor-rspack/rspack.config.js index 0f119ac35b..a2602dd121 100644 --- a/npm-packages/meteor-rspack/rspack.config.js +++ b/npm-packages/meteor-rspack/rspack.config.js @@ -42,7 +42,11 @@ function safeRequire(moduleName) { } // Persistent filesystem cache strategy -function createCacheStrategy(mode, side, { projectConfigPath, configPath } = {}) { +function createCacheStrategy( + mode, + side, + { projectConfigPath, configPath, buildContext } = {}, +) { // Check for configuration files const tsconfigPath = path.join(process.cwd(), 'tsconfig.json'); const hasTsconfig = fs.existsSync(tsconfigPath); @@ -83,7 +87,9 @@ function createCacheStrategy(mode, side, { projectConfigPath, configPath } = {}) type: "persistent", storage: { type: "filesystem", - directory: `node_modules/.cache/rspack${(side && `/${side}`) || ""}`, + directory: `node_modules/.cache/rspack${ + (buildContext && `-${buildContext}`) || '' + }${(side && `/${side}`) || ''}`, }, ...(buildDependencies.length > 0 && { buildDependencies: buildDependencies, @@ -286,7 +292,7 @@ module.exports = async function (inMeteor = {}, argv = {}) { const cacheStrategy = createCacheStrategy( mode, (Meteor.isClient && 'client') || 'server', - { projectConfigPath, configPath } + { projectConfigPath, configPath, buildContext } ); // Expose Meteor's helpers to expand Rspack configs From a6fc727bbe45b29864d4f9f85b8c101c7f1ada34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 24 Feb 2026 15:18:59 +0100 Subject: [PATCH 099/166] ensure test scenario of METEOR_LOCAL_DIR --- tools/modern-tests/helpers.js | 9 +++++++-- tools/modern-tests/react.test.js | 25 +++++++++++++++++++++++++ tools/modern-tests/test-helpers.js | 11 ++++++++--- 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/tools/modern-tests/helpers.js b/tools/modern-tests/helpers.js index cbacc9e7d0..0c877d8a86 100644 --- a/tools/modern-tests/helpers.js +++ b/tools/modern-tests/helpers.js @@ -109,7 +109,8 @@ export async function runMeteorApp(tempDir, port, options = {}) { args, appDir, { - captureOutput + captureOutput, + env: options.env } ); @@ -216,10 +217,14 @@ async function killSingleProcessByPort(port) { export async function runMeteorCommand(command, args = [], cwd, options = {}) { console.log(`Running Meteor command: ${command} ${args.join(' ')}...`); - const { captureOutput = false, checkExitCode = false, execaOptions: extraExecaOptions = {} } = options; + const { captureOutput = false, checkExitCode = false, execaOptions: extraExecaOptions = {}, env = {} } = options; const execaOptions = { cwd, + env: { + ...process.env, + ...env + }, ...extraExecaOptions }; diff --git a/tools/modern-tests/react.test.js b/tools/modern-tests/react.test.js index 896d587af3..ae90626c0c 100644 --- a/tools/modern-tests/react.test.js +++ b/tools/modern-tests/react.test.js @@ -120,6 +120,31 @@ describe('React App Bundling /', () => { }, } })); + + describe('Meteor+Rspack Bundler / (Custom METEOR_LOCAL_DIR)', testMeteorRspackBundler({ + appName: 'react', + port: 3103, + filePaths: { + client: 'client/main.jsx', + server: 'server/main.js', + test: 'tests/main.js' + }, + configFile: 'rspack.config.cjs', + buildDir: '_build-local-custom', + env: { METEOR_LOCAL_DIR: '.meteor/local-custom' }, + customAssertions: { + afterRun: async ({ tempDir }) => { + const appDir = tempDir; // testMeteorRspackBundler uses tempDir as appDir if not monorepo + + const localDir = path.join(appDir, ".meteor", 'local-custom'); + const buildDir = path.join(appDir, '_build-local-custom'); + const cacheDir = path.join(appDir, 'node_modules', '.cache', 'rspack-_build-local-custom'); + + expect(await fs.pathExists(buildDir)).toBe(true); + expect(await fs.pathExists(cacheDir)).toBe(true); + } + } + })); }); /** diff --git a/tools/modern-tests/test-helpers.js b/tools/modern-tests/test-helpers.js index e0cf6ba813..245f861d38 100644 --- a/tools/modern-tests/test-helpers.js +++ b/tools/modern-tests/test-helpers.js @@ -168,6 +168,8 @@ export function testMeteorRspackBundler(options) { buildDir = '_build', // Rspack config file (default: 'rspack.config.js') configFile = 'rspack.config.js', + // Custom environment variables + env = {}, } = options; return () => { @@ -208,7 +210,8 @@ export function testMeteorRspackBundler(options) { // Run the Meteor app to install Rspack const result = await runMeteorApp(tempDir, port, { waitForOutput: "=> App running at", - isMonorepo + isMonorepo, + env }); meteorProcess = result.meteorProcess; @@ -250,7 +253,8 @@ export function testMeteorRspackBundler(options) { // Run the Meteor app and wait for "restarted at" output const result = await runMeteorApp(tempDir, port, { waitForOutput: "=> App running at", - isMonorepo + isMonorepo, + env }); meteorProcess = result.meteorProcess; @@ -339,7 +343,8 @@ export function testMeteorRspackBundler(options) { const result = await runMeteorApp(tempDir, port, { waitForOutput: "=> App running at", commandOptions: ['--production'], - isMonorepo + isMonorepo, + env }); meteorProcess = result.meteorProcess; From e6b48a257aa6a58bc172194880b58a444a84014a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 24 Feb 2026 15:44:41 +0100 Subject: [PATCH 100/166] consolidate and extend custom assertions for Meteor+Rspack bundler tests --- tools/modern-tests/react.test.js | 173 +++++++++++++++++-------------- 1 file changed, 94 insertions(+), 79 deletions(-) diff --git a/tools/modern-tests/react.test.js b/tools/modern-tests/react.test.js index ae90626c0c..2e73a365e8 100644 --- a/tools/modern-tests/react.test.js +++ b/tools/modern-tests/react.test.js @@ -64,87 +64,102 @@ describe('React App Bundling /', () => { }); }); - describe('Meteor+Rspack Bundler /', testMeteorRspackBundler({ - appName: 'react', - port: 3102, - filePaths: { - client: 'client/main.jsx', - server: 'server/main.js', - test: 'tests/main.js' - }, - configFile: 'rspack.config.cjs', - customAssertions: { - afterRun: async ({ result }) => { - await waitForReactEnvs(result.outputLines, { isJsxEnabled: true }); - - // Check if images exist and return 200 status code - await assertImagesExistAndLoad(); - - // Check custom plugin is disabled with Meteor.disablePlugins - await waitForMeteorOutput(result.outputLines, /.*CustomConsoleLogPlugin.*/, { negate: true }); + describe( + "Meteor+Rspack Bundler /", + testMeteorRspackBundler({ + appName: "react", + port: 3102, + filePaths: { + client: "client/main.jsx", + server: "server/main.js", + test: "tests/main.js", }, - afterRunRebuildClient: async ({ allConsoleLogs }) => { - // Check for HMR output as enabled by default - await waitForMeteorOutput(allConsoleLogs, /.*HMR.*Updated modules:.*/); + configFile: "rspack.config.cjs", + buildDir: "_build-local-custom", + env: { METEOR_LOCAL_DIR: ".meteor/local-custom" }, + customAssertions: { + afterRun: async ({ result, tempDir }) => { + const appDir = tempDir; // testMeteorRspackBundler uses tempDir as appDir if not monorepo + + const localDir = path.join(appDir, ".meteor", "local-custom"); + const buildDir = path.join(appDir, "_build-local-custom"); + + expect(await fs.pathExists(buildDir)).toBe(true); + expect(await fs.pathExists(localDir)).toBe(true); + + await waitForReactEnvs(result.outputLines, { isJsxEnabled: true }); + + // Check if images exist and return 200 status code + await assertImagesExistAndLoad(); + + // Check custom plugin is disabled with Meteor.disablePlugins + await waitForMeteorOutput( + result.outputLines, + /.*CustomConsoleLogPlugin.*/, + { negate: true } + ); + }, + afterRunRebuildClient: async ({ allConsoleLogs }) => { + // Check for HMR output as enabled by default + await waitForMeteorOutput( + allConsoleLogs, + /.*HMR.*Updated modules:.*/ + ); + }, + afterRunProduction: async ({ result }) => { + await waitForReactEnvs(result.outputLines, { isJsxEnabled: true }); + + // Check if images exist and return 200 status code + await assertImagesExistAndLoad(); + + // Check custom plugin is disabled with Meteor.disablePlugins + await waitForMeteorOutput( + result.outputLines, + /.*CustomConsoleLogPlugin.*/, + { negate: true } + ); + }, + afterRunProductionRebuildClient: async ({ allConsoleLogs }) => { + // Check for HMR to not be enabled in production-like mode + await waitForMeteorOutput( + allConsoleLogs, + /.*HMR.*Updated modules:*/, + { negate: true } + ); + }, + afterTest: async ({ result }) => { + await waitForReactEnvs(result.outputLines); + + // Check custom plugin is disabled with Meteor.disablePlugins + await waitForMeteorOutput( + result.outputLines, + /.*CustomConsoleLogPlugin.*/, + { negate: true } + ); + }, + afterTestOnce: async ({ result }) => { + await waitForReactEnvs(result.outputLines); + + // Check custom plugin is disabled with Meteor.disablePlugins + await waitForMeteorOutput( + result.outputLines, + /.*CustomConsoleLogPlugin.*/, + { negate: true } + ); + }, + afterBuild: async ({ result }) => { + await waitForReactEnvs(result.outputLines, { isJsxEnabled: true }); + + // Check custom plugin is disabled with Meteor.disablePlugins + await waitForMeteorOutput( + result.outputLines, + /.*CustomConsoleLogPlugin.*/, + { negate: true } + ); + }, }, - afterRunProduction: async ({ result }) => { - await waitForReactEnvs(result.outputLines, { isJsxEnabled: true }); - - // Check if images exist and return 200 status code - await assertImagesExistAndLoad(); - - // Check custom plugin is disabled with Meteor.disablePlugins - await waitForMeteorOutput(result.outputLines, /.*CustomConsoleLogPlugin.*/, { negate: true }); - }, - afterRunProductionRebuildClient: async ({ allConsoleLogs }) => { - // Check for HMR to not be enabled in production-like mode - await waitForMeteorOutput(allConsoleLogs, /.*HMR.*Updated modules:*/, { negate: true }); - }, - afterTest: async ({ result }) => { - await waitForReactEnvs(result.outputLines); - - // Check custom plugin is disabled with Meteor.disablePlugins - await waitForMeteorOutput(result.outputLines, /.*CustomConsoleLogPlugin.*/, { negate: true }); - }, - afterTestOnce: async ({ result }) => { - await waitForReactEnvs(result.outputLines); - - // Check custom plugin is disabled with Meteor.disablePlugins - await waitForMeteorOutput(result.outputLines, /.*CustomConsoleLogPlugin.*/, { negate: true }); - }, - afterBuild: async ({ result }) => { - await waitForReactEnvs(result.outputLines, { isJsxEnabled: true }); - - // Check custom plugin is disabled with Meteor.disablePlugins - await waitForMeteorOutput(result.outputLines, /.*CustomConsoleLogPlugin.*/, { negate: true }); - }, - } - })); - - describe('Meteor+Rspack Bundler / (Custom METEOR_LOCAL_DIR)', testMeteorRspackBundler({ - appName: 'react', - port: 3103, - filePaths: { - client: 'client/main.jsx', - server: 'server/main.js', - test: 'tests/main.js' - }, - configFile: 'rspack.config.cjs', - buildDir: '_build-local-custom', - env: { METEOR_LOCAL_DIR: '.meteor/local-custom' }, - customAssertions: { - afterRun: async ({ tempDir }) => { - const appDir = tempDir; // testMeteorRspackBundler uses tempDir as appDir if not monorepo - - const localDir = path.join(appDir, ".meteor", 'local-custom'); - const buildDir = path.join(appDir, '_build-local-custom'); - const cacheDir = path.join(appDir, 'node_modules', '.cache', 'rspack-_build-local-custom'); - - expect(await fs.pathExists(buildDir)).toBe(true); - expect(await fs.pathExists(cacheDir)).toBe(true); - } - } - })); + }) + ); }); /** From 7dda78a55b9fcd4a53ca6e0158a03bf797b809fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 24 Feb 2026 16:16:44 +0100 Subject: [PATCH 101/166] improve log levels and improve error messaging --- packages/tools-core/lib/git.js | 24 ++++++------------------ packages/tools-core/lib/meteor.js | 5 +++-- packages/tools-core/lib/process.js | 3 ++- 3 files changed, 11 insertions(+), 21 deletions(-) diff --git a/packages/tools-core/lib/git.js b/packages/tools-core/lib/git.js index 71afcdb0e0..51021ce95b 100644 --- a/packages/tools-core/lib/git.js +++ b/packages/tools-core/lib/git.js @@ -1,6 +1,6 @@ import fs from 'fs'; import path from 'path'; -import { logProgress, logSuccess, logInfo, logError } from './log'; +import { logError, logProgress, logSuccess } from './log'; /** * Checks if the given directory is a git repository @@ -45,7 +45,7 @@ export function ensureGitignoreExists(dir, initialEntries = []) { fs.writeFileSync(gitignorePath, content, 'utf8'); return true; } catch (error) { - console.error(`Error creating .gitignore file: ${error.message}`); + logError(`=> Failed to create .gitignore: ${error.message}`); return false; } } @@ -71,7 +71,7 @@ export function getMissingGitignoreEntries(dir, entries) { return entries.filter(entry => !lines.includes(entry)); } catch (error) { - console.error(`Error reading .gitignore file: ${error.message}`); + logError(`=> Failed to read .gitignore: ${error.message}`); return entries; } } @@ -96,16 +96,7 @@ export function addGitignoreEntries(dir, entries, context = '') { return true; // All entries already exist } - // Display a header for the gitignore entries addition - logProgress(`┌─────────────────────────────────────────────────`); - logProgress(`│ Adding Gitignore Entries${context ? ` for ${context}` : ''}`); - logProgress(`└─────────────────────────────────────────────────`); - - // Show what entries will be added - logInfo(`The following entries will be added to .gitignore:`); - missingEntries.forEach(entry => { - logInfo(` • ${entry}`); - }); + logProgress(`=> Adding gitignore entries${context ? ` for ${context}` : ''}: ${missingEntries.join(', ')}`); try { const gitignorePath = path.join(dir, '.gitignore'); @@ -126,13 +117,10 @@ export function addGitignoreEntries(dir, entries, context = '') { content += missingEntries.join('\n') + '\n'; fs.writeFileSync(gitignorePath, content, 'utf8'); - logSuccess(`✅ Gitignore entries${context ? ` for ${context}` : ''} added`); + logSuccess(`=> Added gitignore entries${context ? ` for ${context}` : ''}`); return true; } catch (error) { - logError(`\n┌─────────────────────────────────────────────────`); - logError(`│ ❌ Failed to Add Gitignore Entries${context ? ` for ${context}` : ''}`); - logError(`└─────────────────────────────────────────────────`); - logError(`Error: ${error.message}`); + logError(`=> Failed to add gitignore entries${context ? ` for ${context}` : ''}: ${error.message}`); return false; } } diff --git a/packages/tools-core/lib/meteor.js b/packages/tools-core/lib/meteor.js index 61eb8a5fbb..7cce0973e2 100644 --- a/packages/tools-core/lib/meteor.js +++ b/packages/tools-core/lib/meteor.js @@ -1,5 +1,6 @@ const fs = require('fs'); const path = require('path'); +const { logError } = require('./log'); /** * Returns the current working directory of the Meteor application. @@ -391,11 +392,11 @@ export function getMeteorAppFilesAndFolders(options = {}) { } } catch (error) { // Skip items that can't be accessed - console.error(`Error accessing ${itemPath}: ${error.message}`); + logError(`=> Failed to access ${itemPath}: ${error.message}`); } } } catch (error) { - console.error(`Error reading directory ${dirPath}: ${error.message}`); + logError(`=> Failed to read directory ${dirPath}: ${error.message}`); } return result; diff --git a/packages/tools-core/lib/process.js b/packages/tools-core/lib/process.js index 88db5b7cc6..211870eee7 100644 --- a/packages/tools-core/lib/process.js +++ b/packages/tools-core/lib/process.js @@ -1,5 +1,6 @@ const { spawn } = require('child_process'); const net = require('net'); +const { logError } = require('./log'); /** * Spawns a new OS process with the given command and arguments. @@ -55,7 +56,7 @@ export function spawnProcess(command, args, options = {}) { proc.on('error', (err) => { proc.isRunning = false; if (options.onError) options.onError(err); - else console.error(`Process error: ${err.message}`); + else logError(`=> Process error: ${err.message}`); }); // This happens sometimes when we write to stdin after the app From 69e5ca61154f4a12c22a1923e364dc48fca2dbb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 24 Feb 2026 16:50:09 +0100 Subject: [PATCH 102/166] add `tools-core` package check to fix an issue --- packages/rspack/rspack_server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rspack/rspack_server.js b/packages/rspack/rspack_server.js index 53598b8de5..97ca990878 100644 --- a/packages/rspack/rspack_server.js +++ b/packages/rspack/rspack_server.js @@ -28,7 +28,7 @@ const RSPACK_ASSETS_REGEX = new RegExp( `^\/${rspackAssetsContext}\/(.+)$`, ); -if (Meteor.isDevelopment) { +if (global?.Package?.['tools-core'] != null && Meteor.isDevelopment) { const { shuffleString } = require('meteor/tools-core/lib/string'); const { createProxyMiddleware } = require('http-proxy-middleware'); From 25322059bfa029dfe611afc9ce9320d9bf74d050 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Wed, 25 Feb 2026 16:17:54 +0100 Subject: [PATCH 103/166] add support for server-only app bundling and improve flexibility in modern tests --- .github/workflows/e2e-tests.yml | 2 +- packages/rspack/lib/build-context.js | 92 ++++++++++ packages/rspack/lib/config.js | 4 +- packages/rspack/rspack_plugin.js | 72 +++++--- .../apps/server-only/.meteor/.gitignore | 1 + .../modern-tests/apps/server-only/.meteor/.id | 7 + .../apps/server-only/.meteor/packages | 20 +++ .../apps/server-only/.meteor/platforms | 2 + .../apps/server-only/.meteor/release | 1 + .../apps/server-only/.meteor/versions | 66 +++++++ .../apps/server-only/package.json | 17 ++ .../apps/server-only/rspack.config.js | 5 + .../apps/server-only/server/main.js | 1 + tools/modern-tests/helpers.js | 12 +- tools/modern-tests/server-only.test.js | 27 +++ tools/modern-tests/skeleton.test.js | 32 ++-- tools/modern-tests/test-helpers.js | 170 ++++++++++-------- 17 files changed, 412 insertions(+), 119 deletions(-) create mode 100644 tools/modern-tests/apps/server-only/.meteor/.gitignore create mode 100644 tools/modern-tests/apps/server-only/.meteor/.id create mode 100644 tools/modern-tests/apps/server-only/.meteor/packages create mode 100644 tools/modern-tests/apps/server-only/.meteor/platforms create mode 100644 tools/modern-tests/apps/server-only/.meteor/release create mode 100644 tools/modern-tests/apps/server-only/.meteor/versions create mode 100644 tools/modern-tests/apps/server-only/package.json create mode 100644 tools/modern-tests/apps/server-only/rspack.config.js create mode 100644 tools/modern-tests/apps/server-only/server/main.js create mode 100644 tools/modern-tests/server-only.test.js diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index de695c209d..51e9198997 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -32,8 +32,8 @@ jobs: - Babel - Blaze - Coffeescript - - Library - Monorepo + - Other - React - R.Router - Solid diff --git a/packages/rspack/lib/build-context.js b/packages/rspack/lib/build-context.js index 765f841f41..73f7367d79 100644 --- a/packages/rspack/lib/build-context.js +++ b/packages/rspack/lib/build-context.js @@ -280,6 +280,20 @@ function getBanner(config, side, env, module, role) { if (module === 'test') { // Test file banners if (role === FILE_ROLE.entry) { + if (!config?.entryFile) { + return `/** +* @file ${side}-entry.js +* @description No code generated +* -------------------------------------------------------------------------- +* ⚡ Rspack Test ${sideDisplay} Entry (${envDisplay}) +* -------------------------------------------------------------------------- +* • [■ ${side}-entry.js ] ──▶ [ ${side}-rspack.js ] ──▶ [ ${side}-meteor.js ] +* +* This file is empty because \`meteor.testModule${side === 'test' ? '' : `.${side}`}\` is not set in package.json. +* +${AUTO_GENERATED_WARNING} +*/`; + } // For test mode, if side is client or server, include it in the title const testType = side === 'test' ? 'Test' : `Test ${sideDisplay}`; return `/** @@ -299,6 +313,20 @@ ${AUTO_GENERATED_WARNING} } if (role === FILE_ROLE.output) { + if (!config?.entryFile) { + return `/** +* @file ${side}-rspack.js +* @description No code generated +* -------------------------------------------------------------------------- +* ⚡ Rspack Test ${sideDisplay} App (${envDisplay}) +* -------------------------------------------------------------------------- +* • [ ${side}-entry.js ] ──▶ [■ ${side}-rspack.js ] ──▶ [ ${side}-meteor.js ] +* +* This file is empty because \`meteor.testModule${side === 'test' ? '' : `.${side}`}\` is not set in package.json. +* +${AUTO_GENERATED_WARNING} +*/`; + } // For test mode, if side is client or server, include it in the title const testType = side === 'test' ? 'Test' : `Test ${sideDisplay}`; return `/** @@ -318,6 +346,20 @@ ${AUTO_GENERATED_WARNING} } if (role === FILE_ROLE.run || role === FILE_ROLE.build) { + if (!config?.entryFile) { + return `/** +* @file ${side}-meteor.js +* @description No code generated +* -------------------------------------------------------------------------- +* ☄️ Meteor Test ${sideDisplay} App (${envDisplay}) +* -------------------------------------------------------------------------- +* • [ ${side}-entry.js ] ──▶ [ ${side}-rspack.js ] ──▶ [■ ${side}-meteor.js ] +* +* This file is empty because \`meteor.testModule${side === 'test' ? '' : `.${side}`}\` is not set in package.json. +* +${AUTO_GENERATED_WARNING} +*/`; + } // For test mode, if side is client or server, include it in the title const testType = side === 'test' ? 'Test' : `Test ${sideDisplay}`; return `/** @@ -341,6 +383,20 @@ ${AUTO_GENERATED_WARNING} // For main modules (not test mode), use the new templates // Entry files if (role === FILE_ROLE.entry) { + if (!config?.entryFile) { + return `/** +* @file ${side}-entry.js +* @description No code generated +* -------------------------------------------------------------------------- +* 🔌 Rspack ${sideDisplay} Entry (${envDisplay}) +* -------------------------------------------------------------------------- +* • [■ ${side}-entry.js ] ──▶ [ ${side}-rspack.js ] ──▶ [ ${side}-meteor.js ] +* +* This file is empty because \`meteor.mainModule.${side}\` is not set in package.json. +* +${AUTO_GENERATED_WARNING} +*/`; + } return `/** * @file ${side}-entry.js * @description Entry point for Rspack build process @@ -360,6 +416,20 @@ ${AUTO_GENERATED_WARNING} // Rspack output files if (role === FILE_ROLE.output) { + if (!config?.entryFile) { + return `/** +* @file ${side}-rspack.js +* @description No code generated +* -------------------------------------------------------------------------- +* ⚡ Rspack ${sideDisplay} App (${envDisplay}) +* -------------------------------------------------------------------------- +* • [ ${side}-entry.js ] ──▶ [■ ${side}-rspack.js ] ──▶ [ ${side}-meteor.js ] +* +* This file is empty because \`meteor.mainModule.${side}\` is not set in package.json. +* +${AUTO_GENERATED_WARNING} +*/`; + } return `/** * @file ${side}-rspack.js * @description Bundled output generated by Rspack @@ -379,6 +449,20 @@ ${AUTO_GENERATED_WARNING} // Meteor files (run or build role) if (role === FILE_ROLE.run || role === FILE_ROLE.build) { + if (!config?.entryFile) { + return `/** +* @file ${side}-meteor.js +* @description No code generated +* -------------------------------------------------------------------------- +* ☄️ Meteor ${sideDisplay} App (${envDisplay}) +* -------------------------------------------------------------------------- +* • [ ${side}-entry.js ] ──▶ [ ${side}-rspack.js ] ──▶ [■ ${side}-meteor.js ] +* +* This file is empty because \`meteor.mainModule.${side}\` is not set in package.json. +* +${AUTO_GENERATED_WARNING} +*/`; + } return `/** * @file ${side}-meteor.js * @description Meteor runtime file that imports the Rspack bundle @@ -404,6 +488,10 @@ ${AUTO_GENERATED_WARNING} * @returns {string} The HMR code or empty string */ function getHmrCode(config, role) { + if (!config?.entryFile) { + return ''; + } + if (role === FILE_ROLE.entry && config?.isClient && !config?.isTest) { return `/* Enables HMR */ if (module.hot) { @@ -418,6 +506,10 @@ if (module.hot) { * @returns {string} The import content */ function getImportContent(config, side, role) { + if (!config?.entryFile) { + return ''; + } + if (config?.entryFile && role === FILE_ROLE.entry) { return `/* Link to 🔌 Meteor ${capitalizeFirstLetter(side)} Entry */ import '../../${config?.entryFile}';`; diff --git a/packages/rspack/lib/config.js b/packages/rspack/lib/config.js index 23ef8005a3..e1be83eab1 100644 --- a/packages/rspack/lib/config.js +++ b/packages/rspack/lib/config.js @@ -154,7 +154,9 @@ export function configureMeteorForRspack() { const initialEntrypointContexts = [ initialEntrypoints.mainClient, initialEntrypoints.mainServer, - ].map(entrypoint => path.dirname(entrypoint)); + ] + .filter(Boolean) + .map(entrypoint => path.dirname(entrypoint)); const includedDirs = ['public', 'private', '.meteor', RSPACK_BUILD_CONTEXT]; const ignoredDirs = projectRootFilesAndFolders.directories.filter( dir => !includedDirs.includes(dir), diff --git a/packages/rspack/rspack_plugin.js b/packages/rspack/rspack_plugin.js index e852a22a6c..9498039e82 100644 --- a/packages/rspack/rspack_plugin.js +++ b/packages/rspack/rspack_plugin.js @@ -89,12 +89,12 @@ const { } = require('meteor/tools-core/lib/npm'); const { hasMeteorAppConfigAutoInstallDeps } = require("../tools-core/lib/meteor"); +// Get entry points from Meteor configuration +const initialEntrypoints = getMeteorInitialAppEntrypoints(); if (isMeteorAppRun() || isMeteorAppBuild() || isMeteorAppTest() || isMeteorAppUpdate()) { - // Get entry points from Meteor configuration - const initialEntrypoints = getMeteorInitialAppEntrypoints(); // Check if mainClient and mainServer exist - if (!initialEntrypoints.mainClient || !initialEntrypoints.mainServer) { + if (!initialEntrypoints.mainServer) { logError(`\n┌─────────────────────────────────────────────────`); logError(`│ ❌ Missing Required Entry Points`); logError(`└─────────────────────────────────────────────────`); @@ -230,25 +230,43 @@ if (isMeteorAppRun() || isMeteorAppBuild() || isMeteorAppTest()) { // For 'run' command, start Rspack in appropriate modes with distinct callbacks if (isMeteorAppDevelopment() && !isMeteorAppNative()) { - startRspackClientServe({ onCompile: onCompileClient }); - startRspackServerWatch({ onCompile: onCompileServer }); + if (initialEntrypoints.mainClient) { + startRspackClientServe({ onCompile: onCompileClient }); + } + if (initialEntrypoints.mainServer) { + startRspackServerWatch({ onCompile: onCompileServer }); + } } else if (isMeteorAppProduction() || isMeteorAppNative()) { - runRspackBuild({ - isClient: true, - isServer: false, - watch: true, - onCompile: onCompileClient, - }); - runRspackBuild({ - isServer: true, - isClient: false, - watch: true, - onCompile: onCompileServer, - }); + if (initialEntrypoints.mainClient) { + runRspackBuild({ + isClient: true, + isServer: false, + watch: true, + onCompile: onCompileClient, + }); + } + if (initialEntrypoints.mainServer) { + runRspackBuild({ + isServer: true, + isClient: false, + watch: true, + onCompile: onCompileServer, + }); + } } // Wait for first compilation to complete - await waitForFirstCompilation(clientFirstCompile, serverFirstCompile, clientFirstCompilePromise, serverFirstCompilePromise); + const waitTarget = + initialEntrypoints?.testClient && initialEntrypoints?.testServer + ? "both" + : "server"; + await waitForFirstCompilation( + clientFirstCompile, + serverFirstCompile, + clientFirstCompilePromise, + serverFirstCompilePromise, + { target: waitTarget }, + ); // When running `meteor test` command } else if (isMeteorAppTest()) { @@ -306,7 +324,12 @@ if (isMeteorAppRun() || isMeteorAppBuild() || isMeteorAppTest()) { }); // Wait for first compilation to complete - await waitForFirstCompilation(clientFirstCompile, serverFirstCompile, clientFirstCompilePromise, serverFirstCompilePromise); + await waitForFirstCompilation( + clientFirstCompile, + serverFirstCompile, + clientFirstCompilePromise, + serverFirstCompilePromise + ); // When testModule is specified as a single file or not specified } else { @@ -335,10 +358,13 @@ if (isMeteorAppRun() || isMeteorAppBuild() || isMeteorAppTest()) { } else if (isMeteorAppBuild()) { // For 'build' command, run Rspack build without watch mode // Run client and server builds in parallel and wait for both to complete - await Promise.all([ - runRspackBuild({ isClient: true, isServer: false }), - runRspackBuild({ isServer: true, isClient: false }), - ]); + const targetsToBuild = [ + initialEntrypoints?.mainClient && + runRspackBuild({ isClient: true, isServer: false }), + initialEntrypoints?.mainServer && + runRspackBuild({ isServer: true, isClient: false }), + ].filter(Boolean); + await Promise.all(targetsToBuild); } } catch (error) { logError(`Rspack plugin error: ${error.message}`); diff --git a/tools/modern-tests/apps/server-only/.meteor/.gitignore b/tools/modern-tests/apps/server-only/.meteor/.gitignore new file mode 100644 index 0000000000..4083037423 --- /dev/null +++ b/tools/modern-tests/apps/server-only/.meteor/.gitignore @@ -0,0 +1 @@ +local diff --git a/tools/modern-tests/apps/server-only/.meteor/.id b/tools/modern-tests/apps/server-only/.meteor/.id new file mode 100644 index 0000000000..33386e121f --- /dev/null +++ b/tools/modern-tests/apps/server-only/.meteor/.id @@ -0,0 +1,7 @@ +# This file contains a token that is unique to your project. +# Check it into your repository along with the rest of this directory. +# It can be used for purposes such as: +# - ensuring you don't accidentally deploy one app on top of another +# - providing package authors with aggregated statistics + +l2d6s2mpmf9d.hefjva6hrzxq diff --git a/tools/modern-tests/apps/server-only/.meteor/packages b/tools/modern-tests/apps/server-only/.meteor/packages new file mode 100644 index 0000000000..0743c87631 --- /dev/null +++ b/tools/modern-tests/apps/server-only/.meteor/packages @@ -0,0 +1,20 @@ +# Meteor packages used by this project, one per line. +# Check this file (and the other files in this directory) into your repository. +# +# 'meteor add' and 'meteor remove' will edit this file for you, +# but you can also edit it by hand. + +meteor-base@1.5.2 # Packages every Meteor app needs to have +mobile-experience@1.1.2 # Packages for a great mobile UX +mongo@2.2.0 # The database Meteor supports right now +static-html@1.5.0 # Define static page content in .html files +reactive-var@1.0.13 # Reactive variable for tracker +tracker@1.3.4 # Meteor's client-side reactive programming library + +standard-minifier-css@1.10.0 # CSS minifier run for production mode +standard-minifier-js@3.2.0 # JS minifier run for production mode +es5-shim@4.8.1 # ECMAScript 5 compatibility for older browsers +ecmascript@0.17.0 # Enable ECMAScript2015+ syntax in app code +typescript@5.9.3 # Enable TypeScript syntax in .ts and .tsx modules +shell-server@0.7.0 # Server-side component of the `meteor shell` command +rspack diff --git a/tools/modern-tests/apps/server-only/.meteor/platforms b/tools/modern-tests/apps/server-only/.meteor/platforms new file mode 100644 index 0000000000..efeba1b50c --- /dev/null +++ b/tools/modern-tests/apps/server-only/.meteor/platforms @@ -0,0 +1,2 @@ +server +browser diff --git a/tools/modern-tests/apps/server-only/.meteor/release b/tools/modern-tests/apps/server-only/.meteor/release new file mode 100644 index 0000000000..621e94f0ec --- /dev/null +++ b/tools/modern-tests/apps/server-only/.meteor/release @@ -0,0 +1 @@ +none diff --git a/tools/modern-tests/apps/server-only/.meteor/versions b/tools/modern-tests/apps/server-only/.meteor/versions new file mode 100644 index 0000000000..7e24fc06e4 --- /dev/null +++ b/tools/modern-tests/apps/server-only/.meteor/versions @@ -0,0 +1,66 @@ +allow-deny@2.1.0 +autoupdate@2.0.1 +babel-compiler@7.13.0 +babel-runtime@1.5.2 +base64@1.0.13 +binary-heap@1.0.12 +boilerplate-generator@2.1.0 +caching-compiler@2.0.1 +callback-hook@1.6.1 +check@1.5.0 +core-runtime@1.0.0 +ddp@1.4.2 +ddp-client@3.1.1 +ddp-common@1.4.4 +ddp-server@3.1.2 +diff-sequence@1.1.3 +dynamic-import@0.7.4 +ecmascript@0.17.0 +ecmascript-runtime@0.8.3 +ecmascript-runtime-client@0.12.3 +ecmascript-runtime-server@0.11.1 +ejson@1.1.5 +es5-shim@4.8.1 +facts-base@1.0.2 +fetch@0.1.6 +geojson-utils@1.0.12 +hot-code-push@1.0.5 +id-map@1.2.0 +inter-process-messaging@0.1.2 +launch-screen@2.0.1 +logging@1.3.6 +meteor@2.2.0 +meteor-base@1.5.2 +minifier-css@2.0.1 +minifier-js@3.1.0 +minimongo@2.0.5 +mobile-experience@1.1.2 +mobile-status-bar@1.1.1 +modern-browsers@0.2.3 +modules@0.20.3 +modules-runtime@0.13.2 +mongo@2.2.0 +mongo-decimal@0.2.0 +mongo-dev-server@1.1.1 +mongo-id@1.0.9 +npm-mongo@6.16.1 +ordered-dict@1.2.0 +promise@1.0.0 +random@1.2.2 +react-fast-refresh@0.3.0 +reactive-var@1.0.13 +reload@1.3.2 +retry@1.1.1 +routepolicy@1.1.2 +rspack@1.0.0 +shell-server@0.7.0 +socket-stream-client@0.6.1 +standard-minifier-css@1.10.0 +standard-minifier-js@3.2.0 +static-html@1.5.0 +static-html-tools@1.0.0 +tools-core@1.0.0 +tracker@1.3.4 +typescript@5.9.3 +webapp@2.1.0 +webapp-hashing@1.1.2 diff --git a/tools/modern-tests/apps/server-only/package.json b/tools/modern-tests/apps/server-only/package.json new file mode 100644 index 0000000000..2b9704d1f7 --- /dev/null +++ b/tools/modern-tests/apps/server-only/package.json @@ -0,0 +1,17 @@ +{ + "name": "server-only", + "private": true, + "scripts": { + "start": "meteor run" + }, + "dependencies": { + "@babel/runtime": "^7.23.5", + "meteor-node-stubs": "^1.2.12" + }, + "meteor": { + "mainModule": { + "server": "server/main.js" + }, + "modern": true + } +} diff --git a/tools/modern-tests/apps/server-only/rspack.config.js b/tools/modern-tests/apps/server-only/rspack.config.js new file mode 100644 index 0000000000..a6434b4e45 --- /dev/null +++ b/tools/modern-tests/apps/server-only/rspack.config.js @@ -0,0 +1,5 @@ +const { defineConfig } = require('@meteorjs/rspack'); + +module.exports = defineConfig(() => { + return {}; +}); diff --git a/tools/modern-tests/apps/server-only/server/main.js b/tools/modern-tests/apps/server-only/server/main.js new file mode 100644 index 0000000000..f24ca7f09f --- /dev/null +++ b/tools/modern-tests/apps/server-only/server/main.js @@ -0,0 +1 @@ +console.log('server/main.js loaded'); \ No newline at end of file diff --git a/tools/modern-tests/helpers.js b/tools/modern-tests/helpers.js index cbacc9e7d0..756475c96e 100644 --- a/tools/modern-tests/helpers.js +++ b/tools/modern-tests/helpers.js @@ -123,11 +123,13 @@ export async function runMeteorApp(tempDir, port, options = {}) { } // Wait for server to be up - console.log(`Waiting for app to be available on port ${port}...`); - await waitOn({ - resources: [`http-get://localhost:${port}`], - timeout: 90000 - }); + if (!options.skipWaitOn) { + console.log(`Waiting for app to be available on port ${port}...`); + await waitOn({ + resources: [`http-get://localhost:${port}`], + timeout: 90000 + }); + } return { meteorProcess, outputLines }; } diff --git a/tools/modern-tests/server-only.test.js b/tools/modern-tests/server-only.test.js new file mode 100644 index 0000000000..d08d1a192c --- /dev/null +++ b/tools/modern-tests/server-only.test.js @@ -0,0 +1,27 @@ +import { + waitForMeteorOutput, +} from "./helpers"; +import { testMeteorRspackBundler } from './test-helpers'; + +describe("Other / Server-only App Bundling /", () => { + describe( + "Meteor+Rspack Bundler /", + testMeteorRspackBundler({ + appName: "server-only", + port: 3123, + skipClient: true, + filePaths: { + server: "server/main.js", + test: "tests/main.js", + }, + customAssertions: { + afterRun: async ({ result }) => { + await waitForMeteorOutput( + result.outputLines, + "server/main.js loaded" + ); + }, + }, + }) + ); +}); diff --git a/tools/modern-tests/skeleton.test.js b/tools/modern-tests/skeleton.test.js index 6b8ed7be1c..eacac1d553 100644 --- a/tools/modern-tests/skeleton.test.js +++ b/tools/modern-tests/skeleton.test.js @@ -93,16 +93,16 @@ describe('Meteor Skeletons /', () => { ); describe( - 'Full Library Skeleton /', + "Other / Full Skeleton /", testMeteorSkeleton({ - skeletonName: 'full', + skeletonName: "full", port: 3204, filePaths: { - client: 'client/main.js', - server: 'server/main.js', - test: 'imports/api/links/methods.tests.js', + client: "client/main.js", + server: "server/main.js", + test: "imports/api/links/methods.tests.js", }, - }), + }) ); describe( @@ -150,30 +150,30 @@ describe('Meteor Skeletons /', () => { ); describe( - 'Tailwind Library Skeleton /', + "Other / Tailwind Skeleton /", testMeteorSkeleton({ - skeletonName: 'tailwind', + skeletonName: "tailwind", port: 3208, filePaths: { - client: 'client/main.tsx', - server: 'server/main.ts', - test: 'tests/main.ts', + client: "client/main.tsx", + server: "server/main.ts", + test: "tests/main.ts", }, customAssertions: { afterRun: async () => { // Verify Tailwind styles for ".bg-gray-100" element - await assertStyles('.bg-gray-100', { - ['background-color']: 'oklch(0.967 0.003 264.542)', + await assertStyles(".bg-gray-100", { + ["background-color"]: "oklch(0.967 0.003 264.542)", }); }, afterRunProduction: async () => { // Verify Tailwind styles for ".bg-gray-100" element - await assertStyles('.bg-gray-100', { - ['background-color']: 'lab(96.1596 -0.0823438 -1.13575)', + await assertStyles(".bg-gray-100", { + ["background-color"]: "lab(96.1596 -0.0823438 -1.13575)", }); }, }, - }), + }) ); describe( diff --git a/tools/modern-tests/test-helpers.js b/tools/modern-tests/test-helpers.js index 598112eb42..5e0422e6d5 100644 --- a/tools/modern-tests/test-helpers.js +++ b/tools/modern-tests/test-helpers.js @@ -168,6 +168,8 @@ export function testMeteorRspackBundler(options) { buildDir = '_build', // Rspack config file (default: 'rspack.config.js') configFile = 'rspack.config.js', + // Whether to skip client-specific assertions + skipClient = false, } = options; return () => { @@ -207,8 +209,9 @@ export function testMeteorRspackBundler(options) { // Run the Meteor app to install Rspack const result = await runMeteorApp(tempDir, port, { - waitForOutput: "=> App running at:", - isMonorepo + waitForOutput: "=> App running at", + isMonorepo, + skipWaitOn: skipClient }); meteorProcess = result.meteorProcess; @@ -249,8 +252,9 @@ export function testMeteorRspackBundler(options) { test(`"meteor run" / should run and rebuild the app with Rspack`, async () => { // Run the Meteor app and wait for "restarted at" output const result = await runMeteorApp(tempDir, port, { - waitForOutput: "=> App running at:", - isMonorepo + waitForOutput: "=> App running at", + isMonorepo, + skipWaitOn: skipClient }); meteorProcess = result.meteorProcess; @@ -258,24 +262,28 @@ export function testMeteorRspackBundler(options) { await wait(WAIT_ON); // Assert that the app files exists - await assertFileExist(appDir, `${buildDir}/main-dev/client-entry.js`); - await assertFileExist(appDir, `${buildDir}/main-dev/client-rspack.js`); - await assertFileExist(appDir, `${buildDir}/main-dev/client-meteor.js`); + if (!skipClient) { + await assertFileExist(appDir, `${buildDir}/main-dev/client-entry.js`); + await assertFileExist(appDir, `${buildDir}/main-dev/client-rspack.js`); + await assertFileExist(appDir, `${buildDir}/main-dev/client-meteor.js`); + } await assertFileExist(appDir, `${buildDir}/main-dev/server-entry.js`); await assertFileExist(appDir, `${buildDir}/main-dev/server-rspack.js`); await assertFileExist(appDir, `${buildDir}/main-dev/server-meteor.js`); - // Assert that the Meteor app is running correctly - await assertMeteorReactApp(port, { title: appName }); + if (!skipClient) { + // Assert that the Meteor app is running correctly + await assertMeteorReactApp(port, { title: appName }); - // Assert that the app is using Rspack - await assertRspackScriptTag(port, true); + // Assert that the app is using Rspack + await assertRspackScriptTag(port, true); - // Assert that the body has the expected CSS styles - await assertBodyStyles({ - 'padding': '10px', - 'font-family': 'sans-serif' - }); + // Assert that the body has the expected CSS styles + await assertBodyStyles({ + 'padding': '10px', + 'font-family': 'sans-serif' + }); + } // Run custom assertions if provided if (customAssertions && customAssertions.afterRun) { @@ -283,20 +291,22 @@ export function testMeteorRspackBundler(options) { } // Update the client code - await appendFileContent(tempDir, filePaths.client, { - content: customUpdates.devClient(customMessages.devClient), - }); - const consoleLogs = await waitForPlaywrightConsole(customMessages.devClient, { returnAllLogs: true }); - - // Run custom assertions if provided - if (customAssertions && customAssertions.afterRunRebuildClient) { - await customAssertions.afterRunRebuildClient({ - tempDir, - port, - meteorProcess, - result, - allConsoleLogs: consoleLogs.allLogs + if (!skipClient) { + await appendFileContent(tempDir, filePaths.client, { + content: customUpdates.devClient(customMessages.devClient), }); + const consoleLogs = await waitForPlaywrightConsole(customMessages.devClient, { returnAllLogs: true }); + + // Run custom assertions if provided + if (customAssertions && customAssertions.afterRunRebuildClient) { + await customAssertions.afterRunRebuildClient({ + tempDir, + port, + meteorProcess, + result, + allConsoleLogs: consoleLogs.allLogs + }); + } } // Update the server code @@ -337,9 +347,10 @@ export function testMeteorRspackBundler(options) { test(`"meteor run --production" / should run and rebuild the app with Rspack in production`, async () => { // Run the Meteor app and wait for "restarted at" output const result = await runMeteorApp(tempDir, port, { - waitForOutput: "=> App running at:", + waitForOutput: "=> App running at", commandOptions: ['--production'], - isMonorepo + isMonorepo, + skipWaitOn: skipClient }); meteorProcess = result.meteorProcess; @@ -347,27 +358,34 @@ export function testMeteorRspackBundler(options) { await wait(WAIT_ON); // Assert that the app files exists - await assertFileExist(appDir, `${buildDir}/main-prod/client-entry.js`); - await assertFileExist(appDir, `${buildDir}/main-prod/client-rspack.js`); - await assertFileExist(appDir, `${buildDir}/main-prod/client-meteor.js`); + if (!skipClient) { + await assertFileExist(appDir, `${buildDir}/main-prod/client-entry.js`); + await assertFileExist(appDir, `${buildDir}/main-prod/client-rspack.js`); + await assertFileExist(appDir, `${buildDir}/main-prod/client-meteor.js`); + } await assertFileExist(appDir, `${buildDir}/main-prod/server-entry.js`); await assertFileExist(appDir, `${buildDir}/main-prod/server-rspack.js`); await assertFileExist(appDir, `${buildDir}/main-prod/server-meteor.js`); - await assertFileExist(appDir, `${buildDir}/main-prod/index.html`); + + if (!skipClient) { + await assertFileExist(appDir, `${buildDir}/main-prod/index.html`); + } await assertFileExist(tempDir, filePaths.server); - // Assert that the Meteor app is running correctly - await assertMeteorReactApp(port, { title: appName }); + if (!skipClient) { + // Assert that the Meteor app is running correctly + await assertMeteorReactApp(port, { title: appName }); - // Assert that the app is using Rspack - await assertRspackScriptTag(port, false); + // Assert that the app is using Rspack + await assertRspackScriptTag(port, false); - // Assert that the body has the expected CSS styles - await assertBodyStyles({ - 'padding': '10px', - 'font-family': 'sans-serif' - }); + // Assert that the body has the expected CSS styles + await assertBodyStyles({ + 'padding': '10px', + 'font-family': 'sans-serif' + }); + } // Run custom assertions if provided if (customAssertions && customAssertions.afterRunProduction) { @@ -375,20 +393,22 @@ export function testMeteorRspackBundler(options) { } // Update the client code - await appendFileContent(tempDir, filePaths.client, { - content: customUpdates.prodClient(customMessages.prodClient), - }); - const consoleLogs = await waitForPlaywrightConsole(customMessages.prodClient, { returnAllLogs: true }); - - // Run custom assertions if provided - if (customAssertions && customAssertions.afterRunProductionRebuildClient) { - await customAssertions.afterRunProductionRebuildClient({ - tempDir, - port, - meteorProcess, - result, - allConsoleLogs: consoleLogs.allLogs + if (!skipClient) { + await appendFileContent(tempDir, filePaths.client, { + content: customUpdates.prodClient(customMessages.prodClient), }); + const consoleLogs = await waitForPlaywrightConsole(customMessages.prodClient, { returnAllLogs: true }); + + // Run custom assertions if provided + if (customAssertions && customAssertions.afterRunProductionRebuildClient) { + await customAssertions.afterRunProductionRebuildClient({ + tempDir, + port, + meteorProcess, + result, + allConsoleLogs: consoleLogs.allLogs + }); + } } // Update the server code @@ -431,7 +451,7 @@ export function testMeteorRspackBundler(options) { test(`"meteor run --extra-packages bundle-visualizer --production" / should run with bundle-visualizer in production mode`, async () => { // Run the Meteor app with bundle-visualizer in production mode const result = await runMeteorApp(tempDir, port, { - waitForOutput: "=> App running at:", + waitForOutput: "=> App running at", commandOptions: ['--extra-packages', 'bundle-visualizer', '--production'], isMonorepo }); @@ -486,7 +506,7 @@ export function testMeteorRspackBundler(options) { test(`"meteor test${testFullApp ? ' --full-app' : ''}" / should run tests with Rspack`, async () => { const result = await runMeteorTests(tempDir, port, { - waitForOutput: "=> App running at:", + waitForOutput: "=> App running at", commandOptions: testFullApp ? ['--full-app'] : [], checkTestResults: false, isMonorepo @@ -499,9 +519,11 @@ export function testMeteorRspackBundler(options) { const isTestModule = filePaths.test && !filePaths.testClient && !filePaths.testServer; // Assert that the app files exists - await assertFileExist(appDir, `${buildDir}/test/client-entry.js`); - await assertFileExist(appDir, `${buildDir}/test/client-rspack.js`); - await assertFileExist(appDir, `${buildDir}/test/client-meteor.js`); + if (!skipClient) { + await assertFileExist(appDir, `${buildDir}/test/client-entry.js`); + await assertFileExist(appDir, `${buildDir}/test/client-rspack.js`); + await assertFileExist(appDir, `${buildDir}/test/client-meteor.js`); + } await assertFileExist(appDir, `${buildDir}/test/server-entry.js`); await assertFileExist(appDir, `${buildDir}/test/server-rspack.js`); await assertFileExist(appDir, `${buildDir}/test/server-meteor.js`); @@ -518,13 +540,15 @@ export function testMeteorRspackBundler(options) { }); await waitForMeteorOutput(result.outputLines, customMessages.test); } else { - await appendFileContent(tempDir, filePaths.testClient, { - content: customUpdates.test(customMessages.testClient), - }); - await waitForMeteorOutput( - result.outputLines, - customMessages.testClient - ); + if (!skipClient) { + await appendFileContent(tempDir, filePaths.testClient, { + content: customUpdates.test(customMessages.testClient), + }); + await waitForMeteorOutput( + result.outputLines, + customMessages.testClient + ); + } await appendFileContent(tempDir, filePaths.testServer, { content: customUpdates.test(customMessages.testServer), @@ -561,7 +585,7 @@ export function testMeteorRspackBundler(options) { test(`"meteor test${testFullApp ? ' --full-app' : ''} --once" / should run tests once with Rspack`, async () => { // Test the app with Rspack once const result = await runMeteorTests(tempDir, port, { - waitForOutput: "=> App running at:", + waitForOutput: "=> App running at", commandOptions: testFullApp ? ['--full-app', '--once'] : ['--once'], checkTestResults: true, isMonorepo @@ -778,7 +802,7 @@ export function testMeteorSkeleton(options) { test(`"meteor run" / should run the ${skeletonName} app`, async () => { // Run the newly created app const result = await runMeteorApp(tempDir, port, { - waitForOutput: "=> App running at:" + waitForOutput: "=> App running at" }); meteorProcess = result.meteorProcess; @@ -811,7 +835,7 @@ export function testMeteorSkeleton(options) { test(`"meteor run --production" / should run the ${skeletonName} app in production mode`, async () => { // Run the app in production mode const result = await runMeteorApp(tempDir, port, { - waitForOutput: "=> App running at:", + waitForOutput: "=> App running at", commandOptions: ["--production"] }); meteorProcess = result.meteorProcess; @@ -855,7 +879,7 @@ export function testMeteorSkeleton(options) { // Run tests once for the app const result = await runMeteorTests(tempDir, port, { - waitForOutput: "=> App running at:", + waitForOutput: "=> App running at", commandOptions: ["--once"], checkTestResults: true }); From ca843d7635714b25cc8ebb004b5d06c5772eb7a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Wed, 25 Feb 2026 17:26:06 +0100 Subject: [PATCH 104/166] clean --- tools/modern-tests/server-only.test.js | 12 +++++----- tools/modern-tests/skeleton.test.js | 32 +++++++++++++------------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/tools/modern-tests/server-only.test.js b/tools/modern-tests/server-only.test.js index 9a30a9111d..e31b430eb9 100644 --- a/tools/modern-tests/server-only.test.js +++ b/tools/modern-tests/server-only.test.js @@ -1,24 +1,24 @@ import { waitForMeteorOutput, -} from "./helpers"; +} from './helpers'; import { testMeteorRspackBundler } from './test-helpers'; -describe("Other / Server-only App Bundling /", () => { +describe('Other / Server-only App Bundling /', () => { describe( - "Meteor+Rspack Bundler /", + 'Meteor+Rspack Bundler /', testMeteorRspackBundler({ - appName: "server-only", + appName: 'server-only', port: 3123, skipClient: true, filePaths: { - server: "server/main.js", + server: 'server/main.js', test: null, }, customAssertions: { afterRun: async ({ result }) => { await waitForMeteorOutput( result.outputLines, - "server/main.js loaded" + 'server/main.js loaded' ); }, }, diff --git a/tools/modern-tests/skeleton.test.js b/tools/modern-tests/skeleton.test.js index eacac1d553..cec2d0da2e 100644 --- a/tools/modern-tests/skeleton.test.js +++ b/tools/modern-tests/skeleton.test.js @@ -93,14 +93,14 @@ describe('Meteor Skeletons /', () => { ); describe( - "Other / Full Skeleton /", + 'Other / Full Skeleton /', testMeteorSkeleton({ - skeletonName: "full", + skeletonName: 'full', port: 3204, filePaths: { - client: "client/main.js", - server: "server/main.js", - test: "imports/api/links/methods.tests.js", + client: 'client/main.js', + server: 'server/main.js', + test: 'imports/api/links/methods.tests.js', }, }) ); @@ -150,26 +150,26 @@ describe('Meteor Skeletons /', () => { ); describe( - "Other / Tailwind Skeleton /", + 'Other / Tailwind Skeleton /', testMeteorSkeleton({ - skeletonName: "tailwind", + skeletonName: 'tailwind', port: 3208, filePaths: { - client: "client/main.tsx", - server: "server/main.ts", - test: "tests/main.ts", + client: 'client/main.tsx', + server: 'server/main.ts', + test: 'tests/main.ts', }, customAssertions: { afterRun: async () => { - // Verify Tailwind styles for ".bg-gray-100" element - await assertStyles(".bg-gray-100", { - ["background-color"]: "oklch(0.967 0.003 264.542)", + // Verify Tailwind styles for '.bg-gray-100' element + await assertStyles('.bg-gray-100', { + ['background-color']: 'oklch(0.967 0.003 264.542)', }); }, afterRunProduction: async () => { - // Verify Tailwind styles for ".bg-gray-100" element - await assertStyles(".bg-gray-100", { - ["background-color"]: "lab(96.1596 -0.0823438 -1.13575)", + // Verify Tailwind styles for '.bg-gray-100' element + await assertStyles('.bg-gray-100', { + ['background-color']: 'lab(96.1596 -0.0823438 -1.13575)', }); }, }, From 950270cd48f94f0c349c39ba6fbf285e73e7cc5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Wed, 25 Feb 2026 18:28:19 +0100 Subject: [PATCH 105/166] add null checks for entrypoints in Rspack plugin logic --- packages/rspack/rspack_plugin.js | 13 +++++++------ tools/modern-tests/package-lock.json | 2 +- tools/modern-tests/package.json | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/packages/rspack/rspack_plugin.js b/packages/rspack/rspack_plugin.js index ee31fc7068..502f49ae82 100644 --- a/packages/rspack/rspack_plugin.js +++ b/packages/rspack/rspack_plugin.js @@ -89,11 +89,12 @@ const { const { hasMeteorAppConfigAutoInstallDeps } = require("../tools-core/lib/meteor"); // Get entry points from Meteor configuration -const initialEntrypoints = getMeteorInitialAppEntrypoints(); +let initialEntrypoints; if (isMeteorAppRun() || isMeteorAppBuild() || isMeteorAppTest() || isMeteorAppUpdate()) { + initialEntrypoints = getMeteorInitialAppEntrypoints(); // Check if mainClient and mainServer exist - if (!initialEntrypoints.mainServer) { + if (!initialEntrypoints?.mainServer) { logError(`\n┌─────────────────────────────────────────────────`); logError(`│ ❌ Missing Required Entry Points`); logError(`└─────────────────────────────────────────────────`); @@ -229,14 +230,14 @@ if (isMeteorAppRun() || isMeteorAppBuild() || isMeteorAppTest()) { // For 'run' command, start Rspack in appropriate modes with distinct callbacks if (isMeteorAppDevelopment() && !isMeteorAppNative()) { - if (initialEntrypoints.mainClient) { + if (initialEntrypoints?.mainClient) { startRspackClientServe({ onCompile: onCompileClient }); } - if (initialEntrypoints.mainServer) { + if (initialEntrypoints?.mainServer) { startRspackServerWatch({ onCompile: onCompileServer }); } } else if (isMeteorAppProduction() || isMeteorAppNative()) { - if (initialEntrypoints.mainClient) { + if (initialEntrypoints?.mainClient) { runRspackBuild({ isClient: true, isServer: false, @@ -244,7 +245,7 @@ if (isMeteorAppRun() || isMeteorAppBuild() || isMeteorAppTest()) { onCompile: onCompileClient, }); } - if (initialEntrypoints.mainServer) { + if (initialEntrypoints?.mainServer) { runRspackBuild({ isServer: true, isClient: false, diff --git a/tools/modern-tests/package-lock.json b/tools/modern-tests/package-lock.json index 79fbbe6ad8..b37ab8042f 100644 --- a/tools/modern-tests/package-lock.json +++ b/tools/modern-tests/package-lock.json @@ -15,7 +15,7 @@ "fs-extra": "^11.3.1", "jest": "^29.0.0", "jest-playwright-preset": "^3.0.1", - "playwright": "1.58.0", + "playwright": "^1.58.0", "wait-on": "^7.0.0" } }, diff --git a/tools/modern-tests/package.json b/tools/modern-tests/package.json index b8f8fbefea..25934c8c5d 100644 --- a/tools/modern-tests/package.json +++ b/tools/modern-tests/package.json @@ -13,7 +13,7 @@ "fs-extra": "^11.3.1", "jest": "^29.0.0", "jest-playwright-preset": "^3.0.1", - "playwright": "1.58.0", + "playwright": "^1.58.0", "wait-on": "^7.0.0" } } From 91800c5818f2787c897726f52d650e820e290d52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Wed, 25 Feb 2026 18:32:11 +0100 Subject: [PATCH 106/166] add support for `testClient` option in modern test helpers --- tools/modern-tests/test-helpers.js | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/modern-tests/test-helpers.js b/tools/modern-tests/test-helpers.js index c7aa4f7301..c3c0e5dc90 100644 --- a/tools/modern-tests/test-helpers.js +++ b/tools/modern-tests/test-helpers.js @@ -892,6 +892,7 @@ export function testMeteorSkeleton(options) { waitForOutput: "=> App running at", commandOptions: ["--once"], checkTestResults: true, + testClient: true, }); // Wait for a margin From 4641cc39e68393e65bfd97266501b25f0d657760 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Wed, 25 Feb 2026 19:01:21 +0100 Subject: [PATCH 107/166] add support for `skipClient` and `skipTestClient` options in modern test helpers --- tools/modern-tests/server-only.test.js | 2 +- tools/modern-tests/test-helpers.js | 16 +++++++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/tools/modern-tests/server-only.test.js b/tools/modern-tests/server-only.test.js index e31b430eb9..53eca14d53 100644 --- a/tools/modern-tests/server-only.test.js +++ b/tools/modern-tests/server-only.test.js @@ -10,9 +10,9 @@ describe('Other / Server-only App Bundling /', () => { appName: 'server-only', port: 3123, skipClient: true, + skipTestClient: true, filePaths: { server: 'server/main.js', - test: null, }, customAssertions: { afterRun: async ({ result }) => { diff --git a/tools/modern-tests/test-helpers.js b/tools/modern-tests/test-helpers.js index c3c0e5dc90..d6e1ceeaac 100644 --- a/tools/modern-tests/test-helpers.js +++ b/tools/modern-tests/test-helpers.js @@ -119,6 +119,8 @@ export function testMeteorBundler(options) { * @param {boolean} options.verbose - Whether to enable verbose output (default: true) * @param {boolean} options.testFullApp - Whether to run tests with the --full-app flag (default: false) * @param {boolean} options.testBundleVisualizer - Whether to run tests with bundle-visualizer in production mode (default: false) + * @param {boolean} options.skipClient - Whether to skip client-specific assertions (default: false) + * @param {boolean} options.skipTestClient - Whether to skip client-side tests (default: false) * @param {string[]} options.checkBundleFilePaths - Array of file paths to check for existence in the bundle * @param {Function} options.beforeAllBehavior - Additional behavior to run in beforeAll * @param {Function} options.afterAllBehavior - Additional behavior to run in afterAll @@ -170,6 +172,8 @@ export function testMeteorRspackBundler(options) { configFile = 'rspack.config.js', // Whether to skip client-specific assertions skipClient = false, + // Whether to skip client-side tests + skipTestClient = false, } = options; return () => { @@ -506,13 +510,13 @@ export function testMeteorRspackBundler(options) { test(`"meteor test${testFullApp ? ' --full-app' : ''}" / should run tests with Rspack`, async () => { const result = await runMeteorTests(tempDir, port, { - waitForOutput: !filePaths.testClient + waitForOutput: skipTestClient ? 'TEST_CLIENT=0' : '=> App running at', commandOptions: testFullApp ? ["--full-app"] : [], checkTestResults: false, isMonorepo, - testClient: !!filePaths.testClient, + testClient: !skipTestClient, }); meteorProcess = result.meteorProcess; @@ -592,13 +596,13 @@ export function testMeteorRspackBundler(options) { test(`"meteor test${testFullApp ? ' --full-app' : ''} --once" / should run tests once with Rspack`, async () => { // Test the app with Rspack once const result = await runMeteorTests(tempDir, port, { - waitForOutput: !filePaths.testClient + waitForOutput: skipTestClient ? 'TEST_CLIENT=0' : '=> App running at', commandOptions: testFullApp ? ['--full-app', '--once'] : ['--once'], checkTestResults: true, isMonorepo, - testClient: !!filePaths.testClient, + testClient: !skipTestClient, }); // Wait for a margin @@ -731,6 +735,7 @@ export function testMeteorRspackBundler(options) { * @param {Function} options.customAssertions.afterRunProduction - Custom assertions to run after running the app in production mode * @param {Function} options.customAssertions.afterTestOnce - Custom assertions to run after running tests once * @param {Function} options.customAssertions.afterBuild - Custom assertions to run after building the app + * @param {boolean} options.skipTestClient - Whether to skip client-side tests (default: false) * @param {string[]} options.checkBundleFilePaths - Array of file paths to check for existence in the bundle * @param {Function} options.beforeAllBehavior - Additional behavior to run in beforeAll * @param {Function} options.afterAllBehavior - Additional behavior to run in afterAll @@ -749,6 +754,7 @@ export function testMeteorSkeleton(options) { customAssertions = {}, checkBodyStyles = true, bodyStyles, + skipTestClient = false, checkBundleFilePaths = [], beforeAllBehavior, afterAllBehavior, @@ -892,7 +898,7 @@ export function testMeteorSkeleton(options) { waitForOutput: "=> App running at", commandOptions: ["--once"], checkTestResults: true, - testClient: true, + testClient: !skipTestClient, }); // Wait for a margin From 357fada8b8c8571321b2fd11e9944358d9c753d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Wed, 25 Feb 2026 19:10:47 +0100 Subject: [PATCH 108/166] add support for `isTest` flag in `getImportContent` function --- packages/rspack/lib/build-context.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/rspack/lib/build-context.js b/packages/rspack/lib/build-context.js index fa7189a7a1..5d3fe1f381 100644 --- a/packages/rspack/lib/build-context.js +++ b/packages/rspack/lib/build-context.js @@ -510,8 +510,8 @@ if (module.hot) { * @returns {string} The import content */ function getImportContent(config, side, role) { - if (!config?.entryFile) { - return ''; + if (!config?.entryFile && !config?.isTest) { + return ""; } if (role === FILE_ROLE.entry) { From a41e322e981f399945c526eaaecf000f6bb5bed0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Wed, 25 Feb 2026 19:14:18 +0100 Subject: [PATCH 109/166] add support for `isTest` flag in `getHmrCode` function --- packages/rspack/lib/build-context.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/rspack/lib/build-context.js b/packages/rspack/lib/build-context.js index 5d3fe1f381..38d561b295 100644 --- a/packages/rspack/lib/build-context.js +++ b/packages/rspack/lib/build-context.js @@ -492,7 +492,7 @@ ${AUTO_GENERATED_WARNING} * @returns {string} The HMR code or empty string */ function getHmrCode(config, role) { - if (!config?.entryFile) { + if (!config?.entryFile && !config?.isTest) { return ''; } @@ -511,7 +511,7 @@ if (module.hot) { */ function getImportContent(config, side, role) { if (!config?.entryFile && !config?.isTest) { - return ""; + return ''; } if (role === FILE_ROLE.entry) { From f64f422ac20ba3ec7e6b2a0374b8acb74fc3b5f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Wed, 25 Feb 2026 19:41:59 +0100 Subject: [PATCH 110/166] add support for `checkAppTitle` and `checkBodyStyles` options in modern test helpers and extend `bare skeleton` test --- tools/modern-tests/helpers.js | 2 +- tools/modern-tests/skeleton.test.js | 11 +++++++++++ tools/modern-tests/test-helpers.js | 14 +++++++++++--- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/tools/modern-tests/helpers.js b/tools/modern-tests/helpers.js index e15ff9ef95..4e04dc0568 100644 --- a/tools/modern-tests/helpers.js +++ b/tools/modern-tests/helpers.js @@ -181,7 +181,7 @@ async function killSingleProcessByPort(port) { // Different commands based on OS const command = process.platform === 'win32' ? `FOR /F "tokens=5" %a in ('netstat -ano ^| find "LISTENING" ^| find ":${port}"') do taskkill /F /PID %a` - : `lsof -i :${port} -t | xargs -r kill -9`; + : `lsof -i :${port} -t | grep -v ^${process.pid}$ | xargs -r kill -9`; console.log(`Killing process on port ${port}...`); try { diff --git a/tools/modern-tests/skeleton.test.js b/tools/modern-tests/skeleton.test.js index cec2d0da2e..bfc46155b7 100644 --- a/tools/modern-tests/skeleton.test.js +++ b/tools/modern-tests/skeleton.test.js @@ -52,6 +52,17 @@ describe('Meteor Skeletons /', () => { }), ); + describe( + "Other / Bare Skeleton /", + testMeteorSkeleton({ + skeletonName: "bare", + port: 3219, + checkAppTitle: false, + checkBodyStyles: false, + skipTestClient: true, + }) + ); + describe( 'Blaze Skeleton /', testMeteorSkeleton({ diff --git a/tools/modern-tests/test-helpers.js b/tools/modern-tests/test-helpers.js index d6e1ceeaac..28fd443219 100644 --- a/tools/modern-tests/test-helpers.js +++ b/tools/modern-tests/test-helpers.js @@ -735,6 +735,9 @@ export function testMeteorRspackBundler(options) { * @param {Function} options.customAssertions.afterRunProduction - Custom assertions to run after running the app in production mode * @param {Function} options.customAssertions.afterTestOnce - Custom assertions to run after running tests once * @param {Function} options.customAssertions.afterBuild - Custom assertions to run after building the app + * @param {boolean} options.checkBodyStyles - Whether to check the body styles (default: true) + * @param {boolean} options.checkAppTitle - Whether to check the Meteor app title (default: true) + * @param {Object} options.bodyStyles - Expected CSS styles for the body * @param {boolean} options.skipTestClient - Whether to skip client-side tests (default: false) * @param {string[]} options.checkBundleFilePaths - Array of file paths to check for existence in the bundle * @param {Function} options.beforeAllBehavior - Additional behavior to run in beforeAll @@ -753,6 +756,7 @@ export function testMeteorSkeleton(options) { }, customAssertions = {}, checkBodyStyles = true, + checkAppTitle = true, bodyStyles, skipTestClient = false, checkBundleFilePaths = [], @@ -826,7 +830,9 @@ export function testMeteorSkeleton(options) { await wait(WAIT_ON); // Assert that the Meteor app is running correctly - await assertMeteorApp(port, { title }); + if (checkAppTitle) { + await assertMeteorApp(port, { title }); + } if (checkBodyStyles) { // Assert that the body has the expected CSS styles @@ -860,7 +866,9 @@ export function testMeteorSkeleton(options) { await wait(WAIT_ON); // Assert that the Meteor app is running correctly - await assertMeteorApp(port, { title }); + if (checkAppTitle) { + await assertMeteorApp(port, { title }); + } if (checkBodyStyles) { // Assert that the body has the expected CSS styles @@ -895,7 +903,7 @@ export function testMeteorSkeleton(options) { // Run tests once for the app const result = await runMeteorTests(tempDir, port, { - waitForOutput: "=> App running at", + waitForOutput: skipTestClient ? "TEST_CLIENT=0" : "=> App running at", commandOptions: ["--once"], checkTestResults: true, testClient: !skipTestClient, From 998470028b94d65a6c28e72b1cc98770ad1e63d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Thu, 26 Feb 2026 14:53:29 +0100 Subject: [PATCH 111/166] update `isMeteorAppDevelopment` and `isMeteorAppProduction` to respect `NODE_ENV` --- packages/tools-core/lib/meteor.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/tools-core/lib/meteor.js b/packages/tools-core/lib/meteor.js index 1556a1ddde..8bed53df00 100644 --- a/packages/tools-core/lib/meteor.js +++ b/packages/tools-core/lib/meteor.js @@ -267,6 +267,9 @@ export function isMeteorAppNative() { * @returns {boolean} True if the application is in development mode, false otherwise. */ export function isMeteorAppDevelopment() { + if (process.env.NODE_ENV) { + return process.env.NODE_ENV !== 'production'; + } return Package.meteor?.Meteor.isDevelopment && !isMeteorAppBuild(); } @@ -275,6 +278,9 @@ export function isMeteorAppDevelopment() { * @returns {boolean} True if the application is in production mode, false otherwise. */ export function isMeteorAppProduction() { + if (process.env.NODE_ENV) { + return process.env.NODE_ENV === 'production'; + } return Package.meteor?.Meteor.isProduction || isMeteorAppBuild(); } From dcdffe613f7189a4e5f0c59140ed6953485796bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Thu, 26 Feb 2026 15:23:55 +0100 Subject: [PATCH 112/166] mdularize Meteor-Rspack configuration to NODE_ENV and rspack mode handling --- .../lib/meteorRspackConfigHelpers.js | 121 ++++++ npm-packages/meteor-rspack/rspack.config.js | 369 +++++++----------- 2 files changed, 269 insertions(+), 221 deletions(-) create mode 100644 npm-packages/meteor-rspack/lib/meteorRspackConfigHelpers.js diff --git a/npm-packages/meteor-rspack/lib/meteorRspackConfigHelpers.js b/npm-packages/meteor-rspack/lib/meteorRspackConfigHelpers.js new file mode 100644 index 0000000000..8cac1656c3 --- /dev/null +++ b/npm-packages/meteor-rspack/lib/meteorRspackConfigHelpers.js @@ -0,0 +1,121 @@ +const path = require('path'); +const fs = require('fs'); +const { cleanOmittedPaths } = require("./mergeRulesSplitOverlap.js"); +const { mergeMeteorRspackFragments } = require("./meteorRspackConfigFactory.js"); + +// Helper function to load and process config files +async function loadAndProcessConfig(configPath, configType, Meteor, argv, disableWarnings) { + try { + // Load the config file + let config; + if (path.extname(configPath) === '.mjs') { + // For ESM modules, we need to use dynamic import + const fileUrl = `file://${configPath}`; + const module = await import(fileUrl); + config = module.default || module; + } else { + // For CommonJS modules, we can use require + config = require(configPath)?.default || require(configPath); + } + + // Process the config + const rawConfig = typeof config === 'function' ? config(Meteor, argv) : config; + const resolvedConfig = await Promise.resolve(rawConfig); + const userConfig = resolvedConfig && '0' in resolvedConfig ? resolvedConfig[0] : resolvedConfig; + + // Define omitted paths and warning function + const omitPaths = [ + "name", + "target", + "entry", + "output.path", + "output.filename", + ...(Meteor.isServer ? ["optimization.splitChunks", "optimization.runtimeChunk"] : []), + ].filter(Boolean); + + const warningFn = path => { + if (disableWarnings) return; + console.warn( + `[${configType}] Ignored custom "${path}" — reserved for Meteor-Rspack integration.`, + ); + }; + + // Clean omitted paths and merge Meteor Rspack fragments + let nextConfig = cleanOmittedPaths(userConfig, { + omitPaths, + warningFn, + }); + nextConfig = mergeMeteorRspackFragments(nextConfig); + + return nextConfig; + } catch (error) { + console.error(`Error loading ${configType} from ${configPath}:`, error); + if (configType === 'rspack.config.js') { + throw error; // Only rethrow for project config + } + return null; + } +} + +/** + * Loads both the user's Rspack configuration and its potential override. + * + * @param {string|undefined} projectConfigPath + * @param {object} Meteor + * @param {object} argv + * @returns {Promise<{ nextUserConfig: object|null, nextOverrideConfig: object|null }>} + */ +async function loadUserAndOverrideConfig(projectConfigPath, Meteor, argv) { + let nextUserConfig = null; + let nextOverrideConfig = null; + + const projectDir = process.cwd(); + const isMeteorPackageConfig = projectDir.includes("/packages/rspack"); + + if (projectConfigPath) { + const configDir = path.dirname(projectConfigPath); + const configFileName = path.basename(projectConfigPath); + const configExt = path.extname(configFileName); + const configNameWithoutExt = configFileName.replace(configExt, ''); + const configNameFull = `${configNameWithoutExt}.override${configExt}`; + const overrideConfigPath = path.join(configDir, configNameFull); + + if (fs.existsSync(overrideConfigPath)) { + nextOverrideConfig = await loadAndProcessConfig( + overrideConfigPath, + configNameFull, + Meteor, + argv, + Meteor.isAngularEnabled + ); + } + + if (fs.existsSync(projectConfigPath) && !isMeteorPackageConfig) { + // Check if there's a .mjs or .cjs version of the config file + const mjsConfigPath = projectConfigPath.replace(/\.js$/, '.mjs'); + const cjsConfigPath = projectConfigPath.replace(/\.js$/, '.cjs'); + + let projectConfigPathToUse = projectConfigPath; + if (fs.existsSync(mjsConfigPath)) { + projectConfigPathToUse = mjsConfigPath; + } else if (fs.existsSync(cjsConfigPath)) { + projectConfigPathToUse = cjsConfigPath; + } + + nextUserConfig = await loadAndProcessConfig( + projectConfigPathToUse, + 'rspack.config.js', + Meteor, + argv, + Meteor.isAngularEnabled + ); + } + } + + return { nextUserConfig, nextOverrideConfig }; +} + +module.exports = { + loadAndProcessConfig, + loadUserAndOverrideConfig, +}; diff --git a/npm-packages/meteor-rspack/rspack.config.js b/npm-packages/meteor-rspack/rspack.config.js index 09138424a7..672d453d13 100644 --- a/npm-packages/meteor-rspack/rspack.config.js +++ b/npm-packages/meteor-rspack/rspack.config.js @@ -12,7 +12,6 @@ const { RequireExternalsPlugin } = require('./plugins/RequireExtenalsPlugin.js') const { AssetExternalsPlugin } = require('./plugins/AssetExternalsPlugin.js'); const { generateEagerTestFile } = require("./lib/test.js"); const { getMeteorIgnoreEntries, createIgnoreGlobConfig } = require("./lib/ignore"); -const { mergeMeteorRspackFragments } = require("./lib/meteorRspackConfigFactory.js"); const { compileWithMeteor, compileWithRspack, @@ -22,6 +21,7 @@ const { makeWebNodeBuiltinsAlias, disablePlugins, } = require('./lib/meteorRspackHelpers.js'); +const { loadUserAndOverrideConfig } = require('./lib/meteorRspackConfigHelpers.js'); const { prepareMeteorRspackConfig } = require("./lib/meteorRspackConfigFactory"); // Safe require that doesn't throw if the module isn't found @@ -204,15 +204,42 @@ module.exports = async function (inMeteor = {}, argv = {}) { const Meteor = { ...inMeteor }; // Convert string boolean values to actual booleans for (const key in Meteor) { - if (Meteor[key] === 'true' || Meteor[key] === true) { + if (Meteor[key] === "true" || Meteor[key] === true) { Meteor[key] = true; - } else if (Meteor[key] === 'false' || Meteor[key] === false) { + } else if (Meteor[key] === "false" || Meteor[key] === false) { Meteor[key] = false; } } - const isProd = !!Meteor.isProduction || argv.mode === 'production'; - const isDev = !!Meteor.isDevelopment || !isProd; + const isTestLike = !!Meteor.isTestLike; + const swcExternalHelpers = !!Meteor.swcExternalHelpers; + const isNative = !!Meteor.isNative; + + const projectDir = process.cwd(); + const projectConfigPath = + Meteor.projectConfigPath || path.resolve(projectDir, "rspack.config.js"); + + // Load and apply project-level overrides for the selected build + const { nextUserConfig, nextOverrideConfig } = + await loadUserAndOverrideConfig(projectConfigPath, Meteor, argv); + + // Determine the mode + const getModeFromConfig = () => { + if (nextOverrideConfig?.mode) return nextOverrideConfig.mode; + if (nextUserConfig?.mode) return nextUserConfig.mode; + if (argv.mode) return argv.mode; + if (Meteor.isProduction) return "production"; + if (Meteor.isDevelopment) return "development"; + return null; + }; + + const currentMode = getModeFromConfig(); + const isProd = currentMode + ? currentMode === "production" + : !!Meteor.isProduction; + const isDev = currentMode + ? currentMode === "development" + : !!Meteor.isDevelopment || !isProd; const isTest = !!Meteor.isTest; const isClient = !!Meteor.isClient; const isServer = !!Meteor.isServer; @@ -222,12 +249,8 @@ module.exports = async function (inMeteor = {}, argv = {}) { const isTestModule = !!Meteor.isTestModule; const isTestEager = !!Meteor.isTestEager; const isTestFullApp = !!Meteor.isTestFullApp; - const isTestLike = !!Meteor.isTestLike; - const swcExternalHelpers = !!Meteor.swcExternalHelpers; - const isNative = !!Meteor.isNative; - const mode = isProd ? 'production' : 'development'; - const projectDir = process.cwd(); - const projectConfigPath = Meteor.projectConfigPath || path.resolve(projectDir, 'rspack.config.js'); + + const mode = isProd ? "production" : "development"; const configPath = Meteor.configPath; const testEntry = Meteor.testEntry; const testClientEntry = Meteor.testClientEntry; @@ -242,28 +265,18 @@ module.exports = async function (inMeteor = {}, argv = {}) { const isAngularEnabled = Meteor.isAngularEnabled || false; // Determine entry points - const entryPath = Meteor.entryPath; + const entryPath = Meteor.entryPath || ""; // Determine output points const outputPath = Meteor.outputPath; - const outputDir = path.dirname(Meteor.outputPath || ''); + const outputDir = path.dirname(Meteor.outputPath || ""); const outputFilename = Meteor.outputFilename; - // Determine run point - const runPath = Meteor.runPath; - - // Determine banner - const bannerOutput = JSON.parse(Meteor.bannerOutput || process.env.RSPACK_BANNER || '""'); - - // Determine output directories - const clientOutputDir = path.resolve(projectDir, 'public'); - const serverOutputDir = path.resolve(projectDir, 'private'); - // Determine context for bundles and assets - const buildContext = Meteor.buildContext || '_build'; - const assetsContext = Meteor.assetsContext || 'build-assets'; - const chunksContext = Meteor.chunksContext || 'build-chunks'; + const buildContext = Meteor.buildContext || "_build"; + const assetsContext = Meteor.assetsContext || "build-assets"; + const chunksContext = Meteor.chunksContext || "build-chunks"; // Determine build output and pass to Meteor const buildOutputDir = path.resolve(projectDir, buildContext, outputDir); @@ -271,27 +284,38 @@ module.exports = async function (inMeteor = {}, argv = {}) { const cacheStrategy = createCacheStrategy( mode, - (Meteor.isClient && 'client') || 'server', + (Meteor.isClient && "client") || "server", { projectConfigPath, configPath } ); + // Determine run point + const runPath = Meteor.runPath || ""; + + // Determine banner + const bannerOutput = JSON.parse( + Meteor.bannerOutput || process.env.RSPACK_BANNER || '""' + ); + + // Determine output directories + const clientOutputDir = path.resolve(projectDir, "public"); + const serverOutputDir = path.resolve(projectDir, "private"); + // Expose Meteor's helpers to expand Rspack configs - Meteor.compileWithMeteor = deps => compileWithMeteor(deps); + Meteor.compileWithMeteor = (deps) => compileWithMeteor(deps); Meteor.compileWithRspack = (deps, options = {}) => compileWithRspack(deps, { options: mergeSplitOverlap(Meteor.swcConfigOptions, options), }); - Meteor.setCache = enabled => - setCache( - !!enabled, - enabled === 'memory' ? undefined : cacheStrategy - ); + Meteor.setCache = (enabled) => + setCache(!!enabled, enabled === "memory" ? undefined : cacheStrategy); Meteor.splitVendorChunk = () => splitVendorChunk(); - Meteor.extendSwcConfig = (customSwcConfig) => extendSwcConfig(customSwcConfig); + Meteor.extendSwcConfig = (customSwcConfig) => + extendSwcConfig(customSwcConfig); Meteor.extendConfig = (...configs) => mergeSplitOverlap(...configs); - Meteor.disablePlugins = matchers => prepareMeteorRspackConfig({ - disablePlugins: matchers, - }); + Meteor.disablePlugins = (matchers) => + prepareMeteorRspackConfig({ + disablePlugins: matchers, + }); // Add HtmlRspackPlugin function to Meteor Meteor.HtmlRspackPlugin = (options = {}) => { @@ -330,16 +354,13 @@ module.exports = async function (inMeteor = {}, argv = {}) { // Set default watch options const watchOptions = { ignored: [ - ...createIgnoreGlobConfig([ - ...meteorIgnoreEntries, - ...additionalEntries, - ]), + ...createIgnoreGlobConfig([...meteorIgnoreEntries, ...additionalEntries]), ], }; if (Meteor.isDebug || Meteor.isVerbose) { - console.log('[i] Rspack mode:', mode); - console.log('[i] Meteor flags:', Meteor); + console.log("[i] Rspack mode:", mode); + console.log("[i] Meteor flags:", Meteor); } const enableSwcExternalHelpers = !isServer && swcExternalHelpers; @@ -363,34 +384,34 @@ module.exports = async function (inMeteor = {}, argv = {}) { ...(isServer ? [/^bcrypt$/] : []), ]; const alias = { - '/': path.resolve(process.cwd()), + "/": path.resolve(process.cwd()), }; const fallback = { ...(isClient && makeWebNodeBuiltinsAlias()), }; const extensions = [ - '.ts', - '.tsx', - '.mts', - '.cts', - '.js', - '.jsx', - '.mjs', - '.cjs', - '.json', - '.wasm', + ".ts", + ".tsx", + ".mts", + ".cts", + ".js", + ".jsx", + ".mjs", + ".cjs", + ".json", + ".wasm", ]; const extraRules = []; const reactRefreshModule = isReactEnabled - ? safeRequire('@rspack/plugin-react-refresh') + ? safeRequire("@rspack/plugin-react-refresh") : null; const requireExternalsPlugin = new RequireExternalsPlugin({ filePath: path.join(buildContext, runPath), ...(Meteor.isBlazeEnabled && { externals: /\.html$/, - isEagerImport: module => module.endsWith('.html'), + isEagerImport: (module) => module.endsWith(".html"), ...(isProd && { lastImports: [`./${outputFilename}`], }), @@ -400,25 +421,26 @@ module.exports = async function (inMeteor = {}, argv = {}) { // Handle assets const assetExternalsPlugin = new AssetExternalsPlugin(); - const assetModuleFilename = _fileInfo => { + const assetModuleFilename = (_fileInfo) => { const filename = _fileInfo.filename; - const isPublic = filename.startsWith('/') || filename.startsWith('public'); + const isPublic = filename.startsWith("/") || filename.startsWith("public"); if (isPublic) return `[name][ext][query]`; return `${assetsContext}/[hash][ext][query]`; }; const rsdoctorModule = isBundleVisualizerEnabled - ? safeRequire('@rsdoctor/rspack-plugin') + ? safeRequire("@rsdoctor/rspack-plugin") : null; - const doctorPluginConfig = isRun && isBundleVisualizerEnabled && rsdoctorModule?.RsdoctorRspackPlugin - ? [ - new rsdoctorModule.RsdoctorRspackPlugin({ - port: isClient - ? (parseInt(Meteor.rsdoctorClientPort || '8888', 10)) - : (parseInt(Meteor.rsdoctorServerPort || '8889', 10)), - }), - ] - : []; + const doctorPluginConfig = + isRun && isBundleVisualizerEnabled && rsdoctorModule?.RsdoctorRspackPlugin + ? [ + new rsdoctorModule.RsdoctorRspackPlugin({ + port: isClient + ? parseInt(Meteor.rsdoctorClientPort || "8888", 10) + : parseInt(Meteor.rsdoctorServerPort || "8889", 10), + }), + ] + : []; const bannerPluginConfig = !isBuild ? [ new BannerPlugin({ @@ -454,11 +476,11 @@ module.exports = async function (inMeteor = {}, argv = {}) { : isTest && testClientEntry ? path.resolve(process.cwd(), testClientEntry) : path.resolve(process.cwd(), buildContext, entryPath); - const clientNameConfig = `[${(isTest && 'test-') || ''}client-rspack]`; + const clientNameConfig = `[${(isTest && "test-") || ""}client-rspack]`; // Base client config let clientConfig = { name: clientNameConfig, - target: 'web', + target: "web", mode, entry: clientEntry, output: { @@ -467,7 +489,7 @@ module.exports = async function (inMeteor = {}, argv = {}) { const chunkName = _module.chunk?.name; const isMainChunk = !chunkName || chunkName === "main"; const chunkSuffix = `${chunksContext}/[id]${ - isProd ? '.[chunkhash]' : '' + isProd ? ".[chunkhash]" : "" }.js`; if (isDevEnvironment) { if (isMainChunk) return outputFilename; @@ -476,21 +498,21 @@ module.exports = async function (inMeteor = {}, argv = {}) { if (isMainChunk) return `../${buildContext}/${outputPath}`; return chunkSuffix; }, - libraryTarget: 'commonjs2', - publicPath: '/', - chunkFilename: `${chunksContext}/[id]${isProd ? '.[chunkhash]' : ''}.js`, + libraryTarget: "commonjs2", + publicPath: "/", + chunkFilename: `${chunksContext}/[id]${isProd ? ".[chunkhash]" : ""}.js`, assetModuleFilename, cssFilename: `${chunksContext}/[name]${ - isProd ? '.[contenthash]' : '' + isProd ? ".[contenthash]" : "" }.css`, cssChunkFilename: `${chunksContext}/[id]${ - isProd ? '.[contenthash]' : '' + isProd ? ".[contenthash]" : "" }.css`, ...(isProd && { clean: { keep: keepOutsideBuild() } }), }, optimization: { usedExports: true, - splitChunks: { chunks: 'async' }, + splitChunks: { chunks: "async" }, }, module: { rules: [ @@ -499,7 +521,7 @@ module.exports = async function (inMeteor = {}, argv = {}) { ? [ { test: /\.html$/i, - loader: 'ignore-loader', + loader: "ignore-loader", }, ] : []), @@ -517,33 +539,36 @@ module.exports = async function (inMeteor = {}, argv = {}) { assetExternalsPlugin, ].filter(Boolean), new DefinePlugin({ - 'Meteor.isClient': JSON.stringify(true), - 'Meteor.isServer': JSON.stringify(false), - 'Meteor.isTest': JSON.stringify(isTestLike && !isTestFullApp), - 'Meteor.isAppTest': JSON.stringify(isTestLike && isTestFullApp), - 'Meteor.isDevelopment': JSON.stringify(isDev), - 'Meteor.isProduction': JSON.stringify(isProd), + "Meteor.isClient": JSON.stringify(true), + "Meteor.isServer": JSON.stringify(false), + "Meteor.isTest": JSON.stringify(isTestLike && !isTestFullApp), + "Meteor.isAppTest": JSON.stringify(isTestLike && isTestFullApp), + "Meteor.isDevelopment": JSON.stringify(isDev), + "Meteor.isProduction": JSON.stringify(isProd), }), ...bannerPluginConfig, Meteor.HtmlRspackPlugin(), ...doctorPluginConfig, new NormalModuleReplacementPlugin(/^node:(.*)$/, (res) => { - res.request = res.request.replace(/^node:/, ''); + res.request = res.request.replace(/^node:/, ""); }), ], watchOptions, - devtool: isDevEnvironment || isNative || isTest ? 'source-map' : 'hidden-source-map', + devtool: + isDevEnvironment || isNative || isTest + ? "source-map" + : "hidden-source-map", ...(isDevEnvironment && { devServer: { ...createRemoteDevServerConfig(), - static: { directory: clientOutputDir, publicPath: '/__rspack__/' }, + static: { directory: clientOutputDir, publicPath: "/__rspack__/" }, hot: true, liveReload: true, ...(Meteor.isBlazeEnabled && { hot: false }), port: Meteor.devServerPort || 8080, devMiddleware: { - writeToDisk: filePath => - /\.(html)$/.test(filePath) && !filePath.includes('.hot-update.'), + writeToDisk: (filePath) => + /\.(html)$/.test(filePath) && !filePath.includes(".hot-update."), }, }, }), @@ -573,18 +598,18 @@ module.exports = async function (inMeteor = {}, argv = {}) { : isTest && testServerEntry ? path.resolve(process.cwd(), testServerEntry) : path.resolve(projectDir, buildContext, entryPath); - const serverNameConfig = `[${(isTest && 'test-') || ''}server-rspack]`; + const serverNameConfig = `[${(isTest && "test-") || ""}server-rspack]`; // Base server config let serverConfig = { name: serverNameConfig, - target: 'node', + target: "node", mode, entry: serverEntry, output: { path: serverOutputDir, filename: () => `../${buildContext}/${outputPath}`, - libraryTarget: 'commonjs2', - chunkFilename: `${chunksContext}/[id]${isProd ? '.[chunkhash]' : ''}.js`, + libraryTarget: "commonjs2", + chunkFilename: `${chunksContext}/[id]${isProd ? ".[chunkhash]" : ""}.js`, assetModuleFilename, ...(isProd && { clean: { keep: keepOutsideBuild() } }), }, @@ -598,15 +623,15 @@ module.exports = async function (inMeteor = {}, argv = {}) { parser: { javascript: { // Dynamic imports on the server are treated as bundled in the same chunk - dynamicImportMode: 'eager', + dynamicImportMode: "eager", }, }, }, resolve: { extensions, alias, - modules: ['node_modules', path.resolve(projectDir)], - conditionNames: ['import', 'require', 'node', 'default'], + modules: ["node_modules", path.resolve(projectDir)], + conditionNames: ["import", "require", "node", "default"], }, externals, externalsPresets: { node: true }, @@ -614,18 +639,18 @@ module.exports = async function (inMeteor = {}, argv = {}) { new DefinePlugin( isTest && (isTestModule || isTestEager) ? { - 'Meteor.isTest': JSON.stringify(isTest && !isTestFullApp), - 'Meteor.isAppTest': JSON.stringify(isTest && isTestFullApp), - 'Meteor.isDevelopment': JSON.stringify(isDev), + "Meteor.isTest": JSON.stringify(isTest && !isTestFullApp), + "Meteor.isAppTest": JSON.stringify(isTest && isTestFullApp), + "Meteor.isDevelopment": JSON.stringify(isDev), } : { - 'Meteor.isClient': JSON.stringify(false), - 'Meteor.isServer': JSON.stringify(true), - 'Meteor.isTest': JSON.stringify(isTestLike && !isTestFullApp), - 'Meteor.isAppTest': JSON.stringify(isTestLike && isTestFullApp), - 'Meteor.isDevelopment': JSON.stringify(isDev), - 'Meteor.isProduction': JSON.stringify(isProd), - }, + "Meteor.isClient": JSON.stringify(false), + "Meteor.isServer": JSON.stringify(true), + "Meteor.isTest": JSON.stringify(isTestLike && !isTestFullApp), + "Meteor.isAppTest": JSON.stringify(isTestLike && isTestFullApp), + "Meteor.isDevelopment": JSON.stringify(isDev), + "Meteor.isProduction": JSON.stringify(isProd), + } ), ...bannerPluginConfig, requireExternalsPlugin, @@ -633,99 +658,15 @@ module.exports = async function (inMeteor = {}, argv = {}) { ...doctorPluginConfig, ], watchOptions, - devtool: isDevEnvironment || isNative || isTest ? 'source-map' : 'hidden-source-map', + devtool: + isDevEnvironment || isNative || isTest + ? "source-map" + : "hidden-source-map", ...((isDevEnvironment || (isTest && !isTestEager) || isNative) && cacheStrategy), ...lazyCompilationConfig, }; - // Helper function to load and process config files - async function loadAndProcessConfig(configPath, configType, Meteor, argv, isAngularEnabled) { - try { - // Load the config file - let config; - if (path.extname(configPath) === '.mjs') { - // For ESM modules, we need to use dynamic import - const fileUrl = `file://${configPath}`; - const module = await import(fileUrl); - config = module.default || module; - } else { - // For CommonJS modules, we can use require - config = require(configPath)?.default || require(configPath); - } - - // Process the config - const rawConfig = typeof config === 'function' ? config(Meteor, argv) : config; - const resolvedConfig = await Promise.resolve(rawConfig); - const userConfig = resolvedConfig && '0' in resolvedConfig ? resolvedConfig[0] : resolvedConfig; - - // Define omitted paths and warning function - const omitPaths = [ - "name", - "target", - "entry", - "output.path", - "output.filename", - ...(Meteor.isServer ? ["optimization.splitChunks", "optimization.runtimeChunk"] : []), - ].filter(Boolean); - - const warningFn = path => { - if (isAngularEnabled) return; - console.warn( - `[${configType}] Ignored custom "${path}" — reserved for Meteor-Rspack integration.`, - ); - }; - - // Clean omitted paths and merge Meteor Rspack fragments - let nextConfig = cleanOmittedPaths(userConfig, { - omitPaths, - warningFn, - }); - nextConfig = mergeMeteorRspackFragments(nextConfig); - - return nextConfig; - } catch (error) { - console.error(`Error loading ${configType} from ${configPath}:`, error); - if (configType === 'rspack.config.js') { - throw error; // Only rethrow for project config - } - return null; - } - } - - // Load and apply project-level overrides for the selected build - // Check if we're in a Meteor package directory by looking at the path - const isMeteorPackageConfig = projectDir.includes('/packages/rspack'); - if (fs.existsSync(projectConfigPath) && !isMeteorPackageConfig) { - // Check if there's a .mjs or .cjs version of the config file - const mjsConfigPath = projectConfigPath.replace(/\.js$/, '.mjs'); - const cjsConfigPath = projectConfigPath.replace(/\.js$/, '.cjs'); - - let projectConfigPathToUse = projectConfigPath; - if (fs.existsSync(mjsConfigPath)) { - projectConfigPathToUse = mjsConfigPath; - } else if (fs.existsSync(cjsConfigPath)) { - projectConfigPathToUse = cjsConfigPath; - } - - const nextUserConfig = await loadAndProcessConfig( - projectConfigPathToUse, - 'rspack.config.js', - Meteor, - argv, - isAngularEnabled - ); - - if (nextUserConfig) { - if (Meteor.isClient) { - clientConfig = mergeSplitOverlap(clientConfig, nextUserConfig); - } - if (Meteor.isServer) { - serverConfig = mergeSplitOverlap(serverConfig, nextUserConfig); - } - } - } - // Establish Angular overrides to ensure proper integration const angularExpandConfig = isAngularEnabled ? { @@ -763,29 +704,12 @@ module.exports = async function (inMeteor = {}, argv = {}) { ); config = mergeSplitOverlap(config, testClientExpandConfig); - // Check for override config file (extra file to override everything) - if (projectConfigPath) { - const configDir = path.dirname(projectConfigPath); - const configFileName = path.basename(projectConfigPath); - const configExt = path.extname(configFileName); - const configNameWithoutExt = configFileName.replace(configExt, ''); - const configNameFull = `${configNameWithoutExt}.override${configExt}`; - const overrideConfigPath = path.join(configDir, configNameFull); + if (nextUserConfig) { + config = mergeSplitOverlap(config, nextUserConfig); + } - if (fs.existsSync(overrideConfigPath)) { - const nextOverrideConfig = await loadAndProcessConfig( - overrideConfigPath, - configNameFull, - Meteor, - argv, - isAngularEnabled - ); - - if (nextOverrideConfig) { - // Apply override config as the last step - config = mergeSplitOverlap(config, nextOverrideConfig); - } - } + if (nextOverrideConfig) { + config = mergeSplitOverlap(config, nextOverrideConfig); } const shouldDisablePlugins = config?.disablePlugins != null; @@ -795,15 +719,18 @@ module.exports = async function (inMeteor = {}, argv = {}) { } if (Meteor.isDebug || Meteor.isVerbose) { - console.log('Config:', inspect(config, { depth: null, colors: true })); + console.log("Config:", inspect(config, { depth: null, colors: true })); } // Check if lazyCompilation is enabled and warn the user - if (config.lazyCompilation === true || typeof config.lazyCompilation === 'object') { + if ( + config.lazyCompilation === true || + typeof config.lazyCompilation === "object" + ) { console.warn( - '\n⚠️ Warning: lazyCompilation may not work correctly in the current Meteor-Rspack integration.\n' + - ' This feature will be evaluated for support in future Meteor versions.\n' + - ' If you encounter any issues, please disable it in your rspack config.\n', + "\n⚠️ Warning: lazyCompilation may not work correctly in the current Meteor-Rspack integration.\n" + + " This feature will be evaluated for support in future Meteor versions.\n" + + " If you encounter any issues, please disable it in your rspack config.\n" ); } From 8cfc7110fd996015026c464b9f2e46d6c2191e3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Thu, 26 Feb 2026 15:48:53 +0100 Subject: [PATCH 113/166] add NODE_ENV print step to e2e-tests workflow --- .github/workflows/e2e-tests.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index 764cb20b16..b255359a5a 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -68,6 +68,9 @@ jobs: with: node-version: 22.x + - name: Print NODE_ENV + run: echo "NODE_ENV is ${{ env.NODE_ENV }}" + - name: Install deps run: npm install From d92a10b7f0c6e03b57f3345178fd3684bebcd2f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Thu, 26 Feb 2026 15:51:53 +0100 Subject: [PATCH 114/166] add NODE_ENV print step to e2e-tests workflow --- tools/modern-tests/jest.setup.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/modern-tests/jest.setup.js b/tools/modern-tests/jest.setup.js index 1f0cf6e1fa..493ed6cb4a 100644 --- a/tools/modern-tests/jest.setup.js +++ b/tools/modern-tests/jest.setup.js @@ -12,6 +12,8 @@ process.env.RSPACK_DEVSERVER_PORT = '8080'; process.env.RSDOCTOR_CLIENT_PORT = '8888'; process.env.RSDOCTOR_SERVER_PORT = '8889'; +console.log('NODE_ENV is', process.env.NODE_ENV); + // This runs before each test beforeEach(() => { const name = expect.getState().currentTestName; From 7a4b165cbeebde02b7f6834ba72b2a0ac730d992 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Thu, 26 Feb 2026 16:46:54 +0100 Subject: [PATCH 115/166] update @meteorjs/rspack to version 1.1.0-beta.21 and refactor mode/config handling --- npm-packages/meteor-rspack/package-lock.json | 4 +- npm-packages/meteor-rspack/package.json | 2 +- npm-packages/meteor-rspack/rspack.config.js | 148 +++++++++++-------- packages/rspack/lib/constants.js | 2 +- tools/modern-tests/test-helpers.js | 21 +-- 5 files changed, 105 insertions(+), 72 deletions(-) diff --git a/npm-packages/meteor-rspack/package-lock.json b/npm-packages/meteor-rspack/package-lock.json index ee52cbc9e7..cb1fe97611 100644 --- a/npm-packages/meteor-rspack/package-lock.json +++ b/npm-packages/meteor-rspack/package-lock.json @@ -1,12 +1,12 @@ { "name": "@meteorjs/rspack", - "version": "1.1.0-beta.20", + "version": "1.1.0-beta.21", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@meteorjs/rspack", - "version": "1.1.0-beta.20", + "version": "1.1.0-beta.21", "license": "ISC", "dependencies": { "fast-deep-equal": "^3.1.3", diff --git a/npm-packages/meteor-rspack/package.json b/npm-packages/meteor-rspack/package.json index 33dfee7ce1..5599e2309f 100644 --- a/npm-packages/meteor-rspack/package.json +++ b/npm-packages/meteor-rspack/package.json @@ -1,6 +1,6 @@ { "name": "@meteorjs/rspack", - "version": "1.1.0-beta.20", + "version": "1.1.0-beta.21", "description": "Configuration logic for using Rspack in Meteor projects", "main": "index.js", "type": "commonjs", diff --git a/npm-packages/meteor-rspack/rspack.config.js b/npm-packages/meteor-rspack/rspack.config.js index cd97b6f38a..c5def81284 100644 --- a/npm-packages/meteor-rspack/rspack.config.js +++ b/npm-packages/meteor-rspack/rspack.config.js @@ -219,27 +219,24 @@ module.exports = async function (inMeteor = {}, argv = {}) { const projectConfigPath = Meteor.projectConfigPath || path.resolve(projectDir, "rspack.config.js"); - // Load and apply project-level overrides for the selected build - const { nextUserConfig, nextOverrideConfig } = - await loadUserAndOverrideConfig(projectConfigPath, Meteor, argv); + // Compute build paths before loading user config (needed by Meteor helpers below) + const buildContext = Meteor.buildContext || "_build"; + const outputPath = Meteor.outputPath; + const outputDir = path.dirname(Meteor.outputPath || ""); + Meteor.buildOutputDir = path.resolve(projectDir, buildContext, outputDir); - // Determine the mode - const getModeFromConfig = () => { - if (nextOverrideConfig?.mode) return nextOverrideConfig.mode; - if (nextUserConfig?.mode) return nextUserConfig.mode; + // Defined here so it can be called both before and after the first config load; + // without loaded configs it falls through to argv/Meteor flags. + const getModeFromConfig = (userConfig = undefined, overrideConfig = undefined) => { + if (overrideConfig?.mode) return overrideConfig.mode; + if (userConfig?.mode) return userConfig.mode; if (argv.mode) return argv.mode; if (Meteor.isProduction) return "production"; if (Meteor.isDevelopment) return "development"; return null; }; - const currentMode = getModeFromConfig(); - const isProd = currentMode - ? currentMode === "production" - : !!Meteor.isProduction; - const isDev = currentMode - ? currentMode === "development" - : !!Meteor.isDevelopment || !isProd; + // Meteor flags derived purely from input; independent of loaded user/override configs const isTest = !!Meteor.isTest; const isClient = !!Meteor.isClient; const isServer = !!Meteor.isServer; @@ -249,54 +246,37 @@ module.exports = async function (inMeteor = {}, argv = {}) { const isTestModule = !!Meteor.isTestModule; const isTestEager = !!Meteor.isTestEager; const isTestFullApp = !!Meteor.isTestFullApp; - - const mode = isProd ? "production" : "development"; - const configPath = Meteor.configPath; - const testEntry = Meteor.testEntry; - const isTypescriptEnabled = Meteor.isTypescriptEnabled || false; - const isJsxEnabled = - Meteor.isJsxEnabled || (!isTypescriptEnabled && isReactEnabled) || false; - const isTsxEnabled = - Meteor.isTsxEnabled || (isTypescriptEnabled && isReactEnabled) || false; + const isJsxEnabled = Meteor.isJsxEnabled || (!isTypescriptEnabled && isReactEnabled) || false; + const isTsxEnabled = Meteor.isTsxEnabled || (isTypescriptEnabled && isReactEnabled) || false; const isBundleVisualizerEnabled = Meteor.isBundleVisualizerEnabled || false; const isAngularEnabled = Meteor.isAngularEnabled || false; + const enableSwcExternalHelpers = !isServer && swcExternalHelpers; - // Determine entry points - const entryPath = Meteor.entryPath || ""; + // Initial mode before user/override configs are loaded + const initialCurrentMode = getModeFromConfig(); + const initialIsProd = initialCurrentMode ? initialCurrentMode === "production" : !!Meteor.isProduction; + const initialIsDev = initialCurrentMode ? initialCurrentMode === "development" : !!Meteor.isDevelopment || !initialIsProd; + const initialMode = initialIsProd ? "production" : "development"; - // Determine output points - const outputPath = Meteor.outputPath; - const outputDir = path.dirname(Meteor.outputPath || ""); - - const outputFilename = Meteor.outputFilename; - - // Determine context for bundles and assets - const buildContext = Meteor.buildContext || "_build"; - const assetsContext = Meteor.assetsContext || "build-assets"; - const chunksContext = Meteor.chunksContext || "build-chunks"; - - // Determine build output and pass to Meteor - const buildOutputDir = path.resolve(projectDir, buildContext, outputDir); - Meteor.buildOutputDir = buildOutputDir; - - const cacheStrategy = createCacheStrategy( - mode, - (Meteor.isClient && "client") || "server", - { projectConfigPath, configPath } + // Initialized with pre-load values so helpers work during the first config load; + // reassigned after load once mode is fully resolved. + let cacheStrategy = createCacheStrategy( + initialMode, + (isClient && "client") || "server", + { projectConfigPath, configPath: Meteor.configPath } ); - - // Determine run point - const runPath = Meteor.runPath || ""; - - // Determine banner - const bannerOutput = JSON.parse( - Meteor.bannerOutput || process.env.RSPACK_BANNER || '""' - ); - - // Determine output directories - const clientOutputDir = path.resolve(projectDir, "public"); - const serverOutputDir = path.resolve(projectDir, "private"); + let swcConfigRule = createSwcConfig({ + isTypescriptEnabled, + isReactEnabled, + isJsxEnabled, + isTsxEnabled, + externalHelpers: enableSwcExternalHelpers, + isDevEnvironment: isRun && initialIsDev && !isTest && !isNative, + isClient, + isAngularEnabled, + }); + Meteor.swcConfigOptions = swcConfigRule.options; // Expose Meteor's helpers to expand Rspack configs Meteor.compileWithMeteor = (deps) => compileWithMeteor(deps); @@ -337,6 +317,51 @@ module.exports = async function (inMeteor = {}, argv = {}) { }); }; + // First pass: resolve user/override configs early so mode overrides (e.g. "production") + // are available before computing isProd/isDev and the rest of the build flags. + let { nextUserConfig, nextOverrideConfig } = + await loadUserAndOverrideConfig(projectConfigPath, Meteor, argv); + + // Determine the final mode with loaded configs + const currentMode = getModeFromConfig(nextUserConfig, nextOverrideConfig); + const isProd = currentMode + ? currentMode === "production" + : !!Meteor.isProduction; + const isDev = currentMode + ? currentMode === "development" + : !!Meteor.isDevelopment || !isProd; + const mode = isProd ? "production" : "development"; + const configPath = Meteor.configPath; + const testEntry = Meteor.testEntry; + + // Determine entry points + const entryPath = Meteor.entryPath || ""; + + // Determine output points + const outputFilename = Meteor.outputFilename; + + // Determine context for bundles and assets + const assetsContext = Meteor.assetsContext || "build-assets"; + const chunksContext = Meteor.chunksContext || "build-chunks"; + + cacheStrategy = createCacheStrategy( + mode, + (Meteor.isClient && "client") || "server", + { projectConfigPath, configPath } + ); + + // Determine run point + const runPath = Meteor.runPath || ""; + + // Determine banner + const bannerOutput = JSON.parse( + Meteor.bannerOutput || process.env.RSPACK_BANNER || '""' + ); + + // Determine output directories + const clientOutputDir = path.resolve(projectDir, "public"); + const serverOutputDir = path.resolve(projectDir, "private"); + // Get Meteor ignore entries const meteorIgnoreEntries = getMeteorIgnoreEntries(projectDir); @@ -361,9 +386,8 @@ module.exports = async function (inMeteor = {}, argv = {}) { console.log("[i] Meteor flags:", Meteor); } - const enableSwcExternalHelpers = !isServer && swcExternalHelpers; const isDevEnvironment = isRun && isDev && !isTest && !isNative; - const swcConfigRule = createSwcConfig({ + swcConfigRule = createSwcConfig({ isTypescriptEnabled, isReactEnabled, isJsxEnabled, @@ -373,7 +397,6 @@ module.exports = async function (inMeteor = {}, argv = {}) { isClient, isAngularEnabled, }); - // Expose swc config to use in custom configs Meteor.swcConfigOptions = swcConfigRule.options; const externals = [ @@ -696,6 +719,13 @@ module.exports = async function (inMeteor = {}, argv = {}) { } : {}; + // Second pass: re-run only when a mode override was detected, so the user config + // can depend on fully-computed Meteor flags and helpers (swcConfigOptions, buildOutputDir, etc.). + if (nextUserConfig?.mode || nextOverrideConfig?.mode) { + ({ nextUserConfig, nextOverrideConfig } = + await loadUserAndOverrideConfig(projectConfigPath, Meteor, argv)); + } + let config = mergeSplitOverlap( isClient ? clientConfig : serverConfig, angularExpandConfig diff --git a/packages/rspack/lib/constants.js b/packages/rspack/lib/constants.js index 54af63026d..725d7ac488 100644 --- a/packages/rspack/lib/constants.js +++ b/packages/rspack/lib/constants.js @@ -5,7 +5,7 @@ export const DEFAULT_RSPACK_VERSION = '1.7.1'; -export const DEFAULT_METEOR_RSPACK_VERSION = '1.1.0-beta.20'; +export const DEFAULT_METEOR_RSPACK_VERSION = '1.1.0-beta.21'; export const DEFAULT_METEOR_RSPACK_REACT_HMR_VERSION = '1.4.3'; diff --git a/tools/modern-tests/test-helpers.js b/tools/modern-tests/test-helpers.js index 598112eb42..4b5cd5b021 100644 --- a/tools/modern-tests/test-helpers.js +++ b/tools/modern-tests/test-helpers.js @@ -30,6 +30,9 @@ import path from "path"; import execa from "execa"; import waitOn from "wait-on"; +// Clear NODE_ENV so meteor commands don't inherit any value from the test runner environment +process.env.NODE_ENV = ''; + const isCI = process.env.GITHUB_ACTIONS === "true"; const WAIT_ON = isCI ? 2000 : 500; @@ -207,7 +210,7 @@ export function testMeteorRspackBundler(options) { // Run the Meteor app to install Rspack const result = await runMeteorApp(tempDir, port, { - waitForOutput: "=> App running at:", + waitForOutput: "=> App running at", isMonorepo }); meteorProcess = result.meteorProcess; @@ -249,7 +252,7 @@ export function testMeteorRspackBundler(options) { test(`"meteor run" / should run and rebuild the app with Rspack`, async () => { // Run the Meteor app and wait for "restarted at" output const result = await runMeteorApp(tempDir, port, { - waitForOutput: "=> App running at:", + waitForOutput: "=> App running at", isMonorepo }); meteorProcess = result.meteorProcess; @@ -337,7 +340,7 @@ export function testMeteorRspackBundler(options) { test(`"meteor run --production" / should run and rebuild the app with Rspack in production`, async () => { // Run the Meteor app and wait for "restarted at" output const result = await runMeteorApp(tempDir, port, { - waitForOutput: "=> App running at:", + waitForOutput: "=> App running at", commandOptions: ['--production'], isMonorepo }); @@ -431,7 +434,7 @@ export function testMeteorRspackBundler(options) { test(`"meteor run --extra-packages bundle-visualizer --production" / should run with bundle-visualizer in production mode`, async () => { // Run the Meteor app with bundle-visualizer in production mode const result = await runMeteorApp(tempDir, port, { - waitForOutput: "=> App running at:", + waitForOutput: "=> App running at", commandOptions: ['--extra-packages', 'bundle-visualizer', '--production'], isMonorepo }); @@ -486,7 +489,7 @@ export function testMeteorRspackBundler(options) { test(`"meteor test${testFullApp ? ' --full-app' : ''}" / should run tests with Rspack`, async () => { const result = await runMeteorTests(tempDir, port, { - waitForOutput: "=> App running at:", + waitForOutput: "=> App running at", commandOptions: testFullApp ? ['--full-app'] : [], checkTestResults: false, isMonorepo @@ -561,7 +564,7 @@ export function testMeteorRspackBundler(options) { test(`"meteor test${testFullApp ? ' --full-app' : ''} --once" / should run tests once with Rspack`, async () => { // Test the app with Rspack once const result = await runMeteorTests(tempDir, port, { - waitForOutput: "=> App running at:", + waitForOutput: "=> App running at", commandOptions: testFullApp ? ['--full-app', '--once'] : ['--once'], checkTestResults: true, isMonorepo @@ -778,7 +781,7 @@ export function testMeteorSkeleton(options) { test(`"meteor run" / should run the ${skeletonName} app`, async () => { // Run the newly created app const result = await runMeteorApp(tempDir, port, { - waitForOutput: "=> App running at:" + waitForOutput: "=> App running at" }); meteorProcess = result.meteorProcess; @@ -811,7 +814,7 @@ export function testMeteorSkeleton(options) { test(`"meteor run --production" / should run the ${skeletonName} app in production mode`, async () => { // Run the app in production mode const result = await runMeteorApp(tempDir, port, { - waitForOutput: "=> App running at:", + waitForOutput: "=> App running at", commandOptions: ["--production"] }); meteorProcess = result.meteorProcess; @@ -855,7 +858,7 @@ export function testMeteorSkeleton(options) { // Run tests once for the app const result = await runMeteorTests(tempDir, port, { - waitForOutput: "=> App running at:", + waitForOutput: "=> App running at", commandOptions: ["--once"], checkTestResults: true }); From 852a8cbb91825ca6485c92830026233b217a8789 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Thu, 26 Feb 2026 17:00:24 +0100 Subject: [PATCH 116/166] remove NODE_ENV logging from e2e-tests workflow and jest setup --- .github/workflows/e2e-tests.yml | 3 --- tools/modern-tests/jest.setup.js | 2 -- 2 files changed, 5 deletions(-) diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index b255359a5a..764cb20b16 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -68,9 +68,6 @@ jobs: with: node-version: 22.x - - name: Print NODE_ENV - run: echo "NODE_ENV is ${{ env.NODE_ENV }}" - - name: Install deps run: npm install diff --git a/tools/modern-tests/jest.setup.js b/tools/modern-tests/jest.setup.js index 493ed6cb4a..1f0cf6e1fa 100644 --- a/tools/modern-tests/jest.setup.js +++ b/tools/modern-tests/jest.setup.js @@ -12,8 +12,6 @@ process.env.RSPACK_DEVSERVER_PORT = '8080'; process.env.RSDOCTOR_CLIENT_PORT = '8888'; process.env.RSDOCTOR_SERVER_PORT = '8889'; -console.log('NODE_ENV is', process.env.NODE_ENV); - // This runs before each test beforeEach(() => { const name = expect.getState().currentTestName; From d2b7849c25c50f8b84cc5ea181b86200e1df22c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Thu, 26 Feb 2026 18:45:02 +0100 Subject: [PATCH 117/166] link local meteor-rspack in tests and refactor configuration handling --- npm-packages/meteor-rspack/rspack.config.js | 72 ++++++++++++--------- tools/modern-tests/jest.setup.js | 3 + tools/modern-tests/test-helpers.js | 29 ++++++++- 3 files changed, 70 insertions(+), 34 deletions(-) diff --git a/npm-packages/meteor-rspack/rspack.config.js b/npm-packages/meteor-rspack/rspack.config.js index c5def81284..381fbcc6d6 100644 --- a/npm-packages/meteor-rspack/rspack.config.js +++ b/npm-packages/meteor-rspack/rspack.config.js @@ -214,6 +214,7 @@ module.exports = async function (inMeteor = {}, argv = {}) { const isTestLike = !!Meteor.isTestLike; const swcExternalHelpers = !!Meteor.swcExternalHelpers; const isNative = !!Meteor.isNative; + const devServerPort = Meteor.devServerPort || 8080; const projectDir = process.cwd(); const projectConfigPath = @@ -225,17 +226,6 @@ module.exports = async function (inMeteor = {}, argv = {}) { const outputDir = path.dirname(Meteor.outputPath || ""); Meteor.buildOutputDir = path.resolve(projectDir, buildContext, outputDir); - // Defined here so it can be called both before and after the first config load; - // without loaded configs it falls through to argv/Meteor flags. - const getModeFromConfig = (userConfig = undefined, overrideConfig = undefined) => { - if (overrideConfig?.mode) return overrideConfig.mode; - if (userConfig?.mode) return userConfig.mode; - if (argv.mode) return argv.mode; - if (Meteor.isProduction) return "production"; - if (Meteor.isDevelopment) return "development"; - return null; - }; - // Meteor flags derived purely from input; independent of loaded user/override configs const isTest = !!Meteor.isTest; const isClient = !!Meteor.isClient; @@ -247,16 +237,33 @@ module.exports = async function (inMeteor = {}, argv = {}) { const isTestEager = !!Meteor.isTestEager; const isTestFullApp = !!Meteor.isTestFullApp; const isTypescriptEnabled = Meteor.isTypescriptEnabled || false; - const isJsxEnabled = Meteor.isJsxEnabled || (!isTypescriptEnabled && isReactEnabled) || false; - const isTsxEnabled = Meteor.isTsxEnabled || (isTypescriptEnabled && isReactEnabled) || false; + const isJsxEnabled = + Meteor.isJsxEnabled || (!isTypescriptEnabled && isReactEnabled) || false; + const isTsxEnabled = + Meteor.isTsxEnabled || (isTypescriptEnabled && isReactEnabled) || false; const isBundleVisualizerEnabled = Meteor.isBundleVisualizerEnabled || false; const isAngularEnabled = Meteor.isAngularEnabled || false; const enableSwcExternalHelpers = !isServer && swcExternalHelpers; + // Defined here so it can be called both before and after the first config load; + // without loaded configs it falls through to argv/Meteor flags. + const getModeFromConfig = (userConfig, overrideConfig) => { + if (overrideConfig?.mode) return overrideConfig.mode; + if (userConfig?.mode) return userConfig.mode; + if (argv.mode) return argv.mode; + if (Meteor.isProduction) return "production"; + if (Meteor.isDevelopment) return "development"; + return null; + }; + // Initial mode before user/override configs are loaded const initialCurrentMode = getModeFromConfig(); - const initialIsProd = initialCurrentMode ? initialCurrentMode === "production" : !!Meteor.isProduction; - const initialIsDev = initialCurrentMode ? initialCurrentMode === "development" : !!Meteor.isDevelopment || !initialIsProd; + const initialIsProd = initialCurrentMode + ? initialCurrentMode === "production" + : !!Meteor.isProduction; + const initialIsDev = initialCurrentMode + ? initialCurrentMode === "development" + : !!Meteor.isDevelopment || !initialIsProd; const initialMode = initialIsProd ? "production" : "development"; // Initialized with pre-load values so helpers work during the first config load; @@ -319,8 +326,10 @@ module.exports = async function (inMeteor = {}, argv = {}) { // First pass: resolve user/override configs early so mode overrides (e.g. "production") // are available before computing isProd/isDev and the rest of the build flags. - let { nextUserConfig, nextOverrideConfig } = - await loadUserAndOverrideConfig(projectConfigPath, Meteor, argv); + // Skipped for Angular since it manages its own mode via the second pass. + let { nextUserConfig, nextOverrideConfig } = isAngularEnabled + ? {} + : await loadUserAndOverrideConfig(projectConfigPath, Meteor, argv); // Determine the final mode with loaded configs const currentMode = getModeFromConfig(nextUserConfig, nextOverrideConfig); @@ -586,7 +595,7 @@ module.exports = async function (inMeteor = {}, argv = {}) { hot: true, liveReload: true, ...(Meteor.isBlazeEnabled && { hot: false }), - port: Meteor.devServerPort || 8080, + port: devServerPort, devMiddleware: { writeToDisk: (filePath) => /\.(html)$/.test(filePath) && !filePath.includes(".hot-update."), @@ -692,7 +701,7 @@ module.exports = async function (inMeteor = {}, argv = {}) { const angularExpandConfig = isAngularEnabled ? { mode: isProd ? "production" : "development", - devServer: { port: Meteor.devServerPort }, + devServer: { port: devServerPort }, stats: { preset: "normal" }, infrastructureLogging: { level: "info" }, ...(isProd && isClient && { output: { module: false } }), @@ -719,23 +728,24 @@ module.exports = async function (inMeteor = {}, argv = {}) { } : {}; + // Second pass: re-run only when a mode override was detected, so the user config // can depend on fully-computed Meteor flags and helpers (swcConfigOptions, buildOutputDir, etc.). - if (nextUserConfig?.mode || nextOverrideConfig?.mode) { - ({ nextUserConfig, nextOverrideConfig } = - await loadUserAndOverrideConfig(projectConfigPath, Meteor, argv)); + if (nextUserConfig?.mode || nextOverrideConfig?.mode || isAngularEnabled) { + ({ nextUserConfig, nextOverrideConfig } = await loadUserAndOverrideConfig( + projectConfigPath, + Meteor, + argv + )); } - - let config = mergeSplitOverlap( - isClient ? clientConfig : serverConfig, - angularExpandConfig - ); - config = mergeSplitOverlap(config, testClientExpandConfig); - + let config = isClient ? clientConfig : serverConfig; if (nextUserConfig) { config = mergeSplitOverlap(config, nextUserConfig); } + config = mergeSplitOverlap(config, angularExpandConfig); + config = mergeSplitOverlap(config, testClientExpandConfig); + if (nextOverrideConfig) { config = mergeSplitOverlap(config, nextOverrideConfig); } @@ -746,9 +756,9 @@ module.exports = async function (inMeteor = {}, argv = {}) { delete config.disablePlugins; } - if (Meteor.isDebug || Meteor.isVerbose) { + // if (Meteor.isDebug || Meteor.isVerbose) { console.log("Config:", inspect(config, { depth: null, colors: true })); - } + // } // Check if lazyCompilation is enabled and warn the user if ( diff --git a/tools/modern-tests/jest.setup.js b/tools/modern-tests/jest.setup.js index 1f0cf6e1fa..8b3f830a1f 100644 --- a/tools/modern-tests/jest.setup.js +++ b/tools/modern-tests/jest.setup.js @@ -7,6 +7,9 @@ if (isCI) { console.log('Set 2 retries on Jest level'); } +// Clear NODE_ENV so meteor commands don't inherit any value from the test runner environment +process.env.NODE_ENV = ''; + // Set fixed ports for all tests process.env.RSPACK_DEVSERVER_PORT = '8080'; process.env.RSDOCTOR_CLIENT_PORT = '8888'; diff --git a/tools/modern-tests/test-helpers.js b/tools/modern-tests/test-helpers.js index 4b5cd5b021..4d0f408b23 100644 --- a/tools/modern-tests/test-helpers.js +++ b/tools/modern-tests/test-helpers.js @@ -30,13 +30,25 @@ import path from "path"; import execa from "execa"; import waitOn from "wait-on"; -// Clear NODE_ENV so meteor commands don't inherit any value from the test runner environment -process.env.NODE_ENV = ''; - const isCI = process.env.GITHUB_ACTIONS === "true"; +// Link local npm-packages/meteor-rspack so tests run against the latest dev version. +// Set NPM_LINK_RSPACK=false to disable. +const npmLinkLocalRspack = process.env.NPM_LINK_RSPACK !== 'false'; const WAIT_ON = isCI ? 2000 : 500; +async function linkLocalRspack(appDir) { + if (!npmLinkLocalRspack) return; + const repoRoot = path.resolve(process.cwd(), '..', '..'); + const rspackPackageDir = path.join(repoRoot, 'npm-packages', 'meteor-rspack'); + const rspackPkg = JSON.parse(await fs.readFile(path.join(rspackPackageDir, 'package.json'), 'utf8')); + console.log(`Installing ${rspackPkg.name}@${rspackPkg.version} in the app...`); + await execa('npm', ['install', `${rspackPkg.name}@${rspackPkg.version}`, '--save'], { cwd: appDir }); + console.log(`Linking local meteor-rspack from ${rspackPackageDir}...`); + await execa('npm', ['link', rspackPackageDir], { cwd: appDir }); + console.log('Local meteor-rspack linked successfully.'); +} + /** * Helper function to set up and run tests for the Meteor Bundler * @param {Object} options - Options for the test @@ -65,6 +77,9 @@ export function testMeteorBundler(options) { // Setup the Meteor app tempDir = (await setupMeteorApp(appName))?.tempDir; + + // Link local meteor-rspack so the app picks up the latest dev version + await linkLocalRspack(tempDir); }); afterAll(async () => { @@ -200,6 +215,10 @@ export function testMeteorRspackBundler(options) { // Add Rspack package appDir = isMonorepo ? path.join(tempDir, 'app') : tempDir; + + // Link local meteor-rspack so the app picks up the latest dev version + await linkLocalRspack(appDir); + await runMeteorCommand('add', ['rspack'], appDir, { checkExitCode: true }); // Set meteor.modern.verbose to true @@ -772,6 +791,9 @@ export function testMeteorSkeleton(options) { const packageJsonExists = await fs.pathExists(packageJsonPath); expect(packageJsonExists).toBe(true); + // Link local meteor-rspack so the app picks up the latest dev version + await linkLocalRspack(tempDir); + // Run custom assertions if provided if (customAssertions.afterCreate) { await customAssertions.afterCreate({ tempDir, packageJsonPath }); @@ -855,6 +877,7 @@ export function testMeteorSkeleton(options) { stdio: "inherit", shell: true }); + await linkLocalRspack(tempDir); // Run tests once for the app const result = await runMeteorTests(tempDir, port, { From e0413bcb33b7f508e47a8bca08719029154e87c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Fri, 27 Feb 2026 15:24:49 +0100 Subject: [PATCH 118/166] install @rspack/core and @rspack/cli based on DEFAULT_RSPACK_VERSION in tests --- tools/modern-tests/test-helpers.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tools/modern-tests/test-helpers.js b/tools/modern-tests/test-helpers.js index 4d0f408b23..fbd1353cca 100644 --- a/tools/modern-tests/test-helpers.js +++ b/tools/modern-tests/test-helpers.js @@ -42,6 +42,24 @@ async function linkLocalRspack(appDir) { const repoRoot = path.resolve(process.cwd(), '..', '..'); const rspackPackageDir = path.join(repoRoot, 'npm-packages', 'meteor-rspack'); const rspackPkg = JSON.parse(await fs.readFile(path.join(rspackPackageDir, 'package.json'), 'utf8')); + const constantsPath = path.join(repoRoot, 'packages', 'rspack', 'lib', 'constants.js'); + const constantsContent = await fs.readFile(constantsPath, 'utf8'); + const rspackVersionMatch = constantsContent.match(/DEFAULT_RSPACK_VERSION\s*=\s*['"]([^'"]+)['"]/); + const rspackVersion = rspackVersionMatch?.[1]; + if (rspackVersion) { + console.log(`Installing @rspack/core@${rspackVersion} and @rspack/cli@${rspackVersion}...`); + await execa( + "npm", + [ + "install", + `@rspack/core@${rspackVersion}`, + `@rspack/cli@${rspackVersion}`, + "--no-save", + "--no-package-lock", + ], + { cwd: rspackPackageDir } + ); + } console.log(`Installing ${rspackPkg.name}@${rspackPkg.version} in the app...`); await execa('npm', ['install', `${rspackPkg.name}@${rspackPkg.version}`, '--save'], { cwd: appDir }); console.log(`Linking local meteor-rspack from ${rspackPackageDir}...`); From 871029991f686733f60a69483ea4d037e158559a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Fri, 27 Feb 2026 16:22:21 +0100 Subject: [PATCH 119/166] add custom `NODE_ENV` support for e2e test phases and extend environment variable handling --- tools/modern-tests/babel.test.js | 35 +++++++++++++++++-- tools/modern-tests/helpers.js | 26 +++++++------- tools/modern-tests/test-helpers.js | 55 ++++++++++++++++++++---------- 3 files changed, 84 insertions(+), 32 deletions(-) diff --git a/tools/modern-tests/babel.test.js b/tools/modern-tests/babel.test.js index a414d7bbb0..4565410d1f 100644 --- a/tools/modern-tests/babel.test.js +++ b/tools/modern-tests/babel.test.js @@ -7,15 +7,27 @@ describe('Babel App Bundling /', () => { describe('Meteor+Rspack Bundler /', testMeteorRspackBundler({ appName: 'babel', port: 3122, - filePaths: { - client: 'client/main.jsx', + filePaths: { + client: 'client/main.jsx', server: 'server/main.js', test: 'tests/main.js' }, configFile: 'rspack.config.mjs', + skipEnvCheck: true, + // Test custom NODE_ENV compilation + env: { + meteorRun: { NODE_ENV: 'development' }, + meteorRunProduction: { NODE_ENV: 'production' }, + meteorTest: { NODE_ENV: 'development' }, + meteorTestOnce: { NODE_ENV: 'test' }, + meteorBuild: { NODE_ENV: 'development' }, + }, customAssertions: { afterRun: async ({ result }) => { await assertFileExtensionModuleRules(result.outputLines); + await waitForMeteorOutput(result.outputLines, /\[i\] Rspack mode: development/); + await waitForMeteorOutput(result.outputLines, /[^ ]*Meteor.isDevelopment[^ ]*: [^ ]*true[^ ]*/); + await waitForMeteorOutput(result.outputLines, /[^ ]*Meteor.isProduction[^ ]*: [^ ]*false[^ ]*/); }, afterRunRebuildClient: async ({ allConsoleLogs }) => { // Check for HMR output as enabled by default @@ -23,6 +35,15 @@ describe('Babel App Bundling /', () => { }, afterRunProduction: async ({ result }) => { await assertFileExtensionModuleRules(result.outputLines); + await waitForMeteorOutput(result.outputLines, /\[i\] Rspack mode: production/); + await waitForMeteorOutput( + result.outputLines, + /[^ ]*Meteor.isDevelopment[^ ]*: [^ ]*false[^ ]*/ + ); + await waitForMeteorOutput( + result.outputLines, + /[^ ]*Meteor.isProduction[^ ]*: [^ ]*true[^ ]*/ + ); }, afterRunProductionRebuildClient: async ({ allConsoleLogs }) => { // Check for HMR to not be enabled in production-like mode @@ -30,12 +51,22 @@ describe('Babel App Bundling /', () => { }, afterTest: async ({ result }) => { await assertFileExtensionModuleRules(result.outputLines); + await waitForMeteorOutput(result.outputLines, /\[i\] Rspack mode: development/); + await waitForMeteorOutput(result.outputLines, /[^ ]*Meteor.isDevelopment[^ ]*: [^ ]*true[^ ]*/); + await waitForMeteorOutput(result.outputLines, /[^ ]*Meteor.isProduction[^ ]*: [^ ]*false[^ ]*/); }, afterTestOnce: async ({ result }) => { await assertFileExtensionModuleRules(result.outputLines); + await waitForMeteorOutput(result.outputLines, /\[i\] Rspack mode: development/); + await waitForMeteorOutput(result.outputLines, /[^ ]*Meteor.isDevelopment[^ ]*: [^ ]*true[^ ]*/); + await waitForMeteorOutput(result.outputLines, /[^ ]*Meteor.isProduction[^ ]*: [^ ]*false[^ ]*/); }, afterBuild: async ({ result }) => { await assertFileExtensionModuleRules(result.outputLines); + // Force development mode on build + await waitForMeteorOutput(result.outputLines, /\[i\] Rspack mode: development/); + await waitForMeteorOutput(result.outputLines, /[^ ]*Meteor.isDevelopment[^ ]*: [^ ]*true[^ ]*/); + await waitForMeteorOutput(result.outputLines, /[^ ]*Meteor.isProduction[^ ]*: [^ ]*false[^ ]*/); }, } })); diff --git a/tools/modern-tests/helpers.js b/tools/modern-tests/helpers.js index cbacc9e7d0..43ab01394f 100644 --- a/tools/modern-tests/helpers.js +++ b/tools/modern-tests/helpers.js @@ -86,7 +86,7 @@ export async function setupMeteorApp(appName, options = {}) { * @returns {Object} - The meteor process and output lines */ export async function runMeteorApp(tempDir, port, options = {}) { - const { isMonorepo = false } = options; + const { isMonorepo = false, env = {} } = options; // Start Meteor CLI in dev mode console.log(`Starting Meteor app on port ${port}...`); @@ -105,11 +105,12 @@ export async function runMeteorApp(tempDir, port, options = {}) { // Run the meteor command const { meteorProcess, outputLines } = await runMeteorCommand( - 'run', - args, + 'run', + args, appDir, { - captureOutput + captureOutput, + execaOptions: { env: { ...process.env, ...env } } } ); @@ -528,7 +529,7 @@ export async function appendFileContent(tempDir, filePath, options = {}) { * @returns {Object} - The meteor process and output lines */ export async function runMeteorTests(tempDir, port, options = {}) { - const { isMonorepo = false } = options; + const { isMonorepo = false, env = {} } = options; // Start Meteor tests console.log(`Starting Meteor tests on port ${port}...`); @@ -547,14 +548,15 @@ export async function runMeteorTests(tempDir, port, options = {}) { // Run the meteor test command const { meteorProcess, outputLines, processResult } = await runMeteorCommand( - 'test', - args, + 'test', + args, appDir, { execaOptions: { env: { ...process.env, - TEST_BROWSER_DRIVER: 'playwright' + TEST_BROWSER_DRIVER: 'playwright', + ...env } }, captureOutput, @@ -705,7 +707,7 @@ export async function waitForPlaywrightConsole(pattern, options = {}) { * @returns {Object} - The build output directory and the meteor process result */ export async function buildMeteorApp(tempDir, options = {}) { - const { isMonorepo = false } = options; + const { isMonorepo = false, env = {} } = options; // Create a unique temporary directory for the build output const randomSuffix = Math.random().toString(36).substring(2, 10); @@ -729,11 +731,11 @@ export async function buildMeteorApp(tempDir, options = {}) { // Run the meteor build command with automatic exit code checking const result = await runMeteorCommand( - 'build', - args, + 'build', + args, appDir, { - execaOptions: options.execaOptions || {}, + execaOptions: { ...(options.execaOptions || {}), env: { ...process.env, ...(options.execaOptions?.env || {}), ...env } }, captureOutput: options.captureOutput !== undefined ? options.captureOutput : true, checkExitCode: true // Automatically check exit code } diff --git a/tools/modern-tests/test-helpers.js b/tools/modern-tests/test-helpers.js index fbd1353cca..f7714b1c80 100644 --- a/tools/modern-tests/test-helpers.js +++ b/tools/modern-tests/test-helpers.js @@ -46,6 +46,8 @@ async function linkLocalRspack(appDir) { const constantsContent = await fs.readFile(constantsPath, 'utf8'); const rspackVersionMatch = constantsContent.match(/DEFAULT_RSPACK_VERSION\s*=\s*['"]([^'"]+)['"]/); const rspackVersion = rspackVersionMatch?.[1]; + console.log(`Running npm install in ${rspackPackageDir}...`); + await execa('npm', ['install'], { cwd: rspackPackageDir }); if (rspackVersion) { console.log(`Installing @rspack/core@${rspackVersion} and @rspack/cli@${rspackVersion}...`); await execa( @@ -78,7 +80,7 @@ async function linkLocalRspack(appDir) { * @returns {Function} - Jest test function */ export function testMeteorBundler(options) { - const { appName, port, customAssertions, beforeAllBehavior, afterAllBehavior } = options; + const { appName, port, customAssertions, beforeAllBehavior, afterAllBehavior, env = {} } = options; return () => { let meteorProcess; @@ -117,7 +119,7 @@ export function testMeteorBundler(options) { test(`"meteor run" / should start the app`, async () => { // Run the Meteor app - meteorProcess = (await runMeteorApp(tempDir, port))?.meteorProcess; + meteorProcess = (await runMeteorApp(tempDir, port, { env: env.meteorRun }))?.meteorProcess; // Assert that the Meteor app is running correctly await assertMeteorReactApp(port, { title: appName }); @@ -204,6 +206,10 @@ export function testMeteorRspackBundler(options) { buildDir = '_build', // Rspack config file (default: 'rspack.config.js') configFile = 'rspack.config.js', + // Per-phase env vars: { meteorRun, meteorRunProduction, meteorTest, meteorTestOnce, meteorBuild } + env = {}, + // Skip isDevelopment/isProduction/isRun/isTest/isBuild verbose output checks + skipEnvCheck = false, } = options; return () => { @@ -248,7 +254,8 @@ export function testMeteorRspackBundler(options) { // Run the Meteor app to install Rspack const result = await runMeteorApp(tempDir, port, { waitForOutput: "=> App running at", - isMonorepo + isMonorepo, + env: env.meteorRun }); meteorProcess = result.meteorProcess; @@ -290,7 +297,8 @@ export function testMeteorRspackBundler(options) { // Run the Meteor app and wait for "restarted at" output const result = await runMeteorApp(tempDir, port, { waitForOutput: "=> App running at", - isMonorepo + isMonorepo, + env: env.meteorRun }); meteorProcess = result.meteorProcess; @@ -353,7 +361,7 @@ export function testMeteorRspackBundler(options) { await customAssertions.afterRunRebuildServer({ tempDir, port, meteorProcess, result }); } - if (verbose) { + if (verbose && !skipEnvCheck) { await waitForMeteorOutput( result.outputLines, /.*isDevelopment:.*true.*/ @@ -379,7 +387,8 @@ export function testMeteorRspackBundler(options) { const result = await runMeteorApp(tempDir, port, { waitForOutput: "=> App running at", commandOptions: ['--production'], - isMonorepo + isMonorepo, + env: env.meteorRunProduction }); meteorProcess = result.meteorProcess; @@ -445,7 +454,7 @@ export function testMeteorRspackBundler(options) { await customAssertions.afterRunProductionRebuildServer({ tempDir, port, meteorProcess, result }); } - if (verbose) { + if (verbose && !skipEnvCheck) { await waitForMeteorOutput( result.outputLines, /.*isProduction:.*true.*/ @@ -473,7 +482,8 @@ export function testMeteorRspackBundler(options) { const result = await runMeteorApp(tempDir, port, { waitForOutput: "=> App running at", commandOptions: ['--extra-packages', 'bundle-visualizer', '--production'], - isMonorepo + isMonorepo, + env: env.meteorRunProduction }); meteorProcess = result.meteorProcess; @@ -529,7 +539,8 @@ export function testMeteorRspackBundler(options) { waitForOutput: "=> App running at", commandOptions: testFullApp ? ['--full-app'] : [], checkTestResults: false, - isMonorepo + isMonorepo, + env: env.meteorTest }); meteorProcess = result.meteorProcess; @@ -575,7 +586,7 @@ export function testMeteorRspackBundler(options) { ); } - if (verbose) { + if (verbose && !skipEnvCheck) { await waitForMeteorOutput( result.outputLines, /.*isDevelopment:.*true.*/ @@ -604,7 +615,8 @@ export function testMeteorRspackBundler(options) { waitForOutput: "=> App running at", commandOptions: testFullApp ? ['--full-app', '--once'] : ['--once'], checkTestResults: true, - isMonorepo + isMonorepo, + env: env.meteorTestOnce }); // Wait for a margin @@ -618,7 +630,7 @@ export function testMeteorRspackBundler(options) { await assertFileExist(appDir, `${buildDir}/test/server-rspack.js`); await assertFileExist(appDir, `${buildDir}/test/server-meteor.js`); - if (verbose) { + if (verbose && !skipEnvCheck) { await waitForMeteorOutput( result.outputLines, /.*isDevelopment:.*true.*/ @@ -643,13 +655,14 @@ export function testMeteorRspackBundler(options) { const { buildOutputDir, processResult: result } = await buildMeteorApp(tempDir, { commandOptions: ['--directory'], captureOutput: true, - isMonorepo + isMonorepo, + env: env.meteorBuild }); // Wait for a margin await wait(WAIT_ON); - if (verbose) { + if (verbose && !skipEnvCheck) { await waitForMeteorOutput( result.outputLines, /.*isProduction:.*true.*/ @@ -758,6 +771,8 @@ export function testMeteorSkeleton(options) { checkBundleFilePaths = [], beforeAllBehavior, afterAllBehavior, + // Per-phase env vars: { meteorRun, meteorRunProduction, meteorTest, meteorBuild } + env = {}, } = options; return () => { @@ -821,7 +836,8 @@ export function testMeteorSkeleton(options) { test(`"meteor run" / should run the ${skeletonName} app`, async () => { // Run the newly created app const result = await runMeteorApp(tempDir, port, { - waitForOutput: "=> App running at" + waitForOutput: "=> App running at", + env: env.meteorRun }); meteorProcess = result.meteorProcess; @@ -855,7 +871,8 @@ export function testMeteorSkeleton(options) { // Run the app in production mode const result = await runMeteorApp(tempDir, port, { waitForOutput: "=> App running at", - commandOptions: ["--production"] + commandOptions: ["--production"], + env: env.meteorRunProduction }); meteorProcess = result.meteorProcess; @@ -901,7 +918,8 @@ export function testMeteorSkeleton(options) { const result = await runMeteorTests(tempDir, port, { waitForOutput: "=> App running at", commandOptions: ["--once"], - checkTestResults: true + checkTestResults: true, + env: env.meteorTest }); // Wait for a margin @@ -920,7 +938,8 @@ export function testMeteorSkeleton(options) { // Build the app const { buildOutputDir, processResult: result } = await buildMeteorApp(tempDir, { commandOptions: ["--directory"], - captureOutput: true + captureOutput: true, + env: env.meteorBuild }); // Wait for a margin From cfb8664542920e19c9933e9d2663a614f19792dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Fri, 27 Feb 2026 16:32:01 +0100 Subject: [PATCH 120/166] install ignore-loader in tests and remove redundant npm install step --- tools/modern-tests/test-helpers.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/modern-tests/test-helpers.js b/tools/modern-tests/test-helpers.js index f7714b1c80..fd93ae3ed3 100644 --- a/tools/modern-tests/test-helpers.js +++ b/tools/modern-tests/test-helpers.js @@ -46,8 +46,6 @@ async function linkLocalRspack(appDir) { const constantsContent = await fs.readFile(constantsPath, 'utf8'); const rspackVersionMatch = constantsContent.match(/DEFAULT_RSPACK_VERSION\s*=\s*['"]([^'"]+)['"]/); const rspackVersion = rspackVersionMatch?.[1]; - console.log(`Running npm install in ${rspackPackageDir}...`); - await execa('npm', ['install'], { cwd: rspackPackageDir }); if (rspackVersion) { console.log(`Installing @rspack/core@${rspackVersion} and @rspack/cli@${rspackVersion}...`); await execa( @@ -62,6 +60,8 @@ async function linkLocalRspack(appDir) { { cwd: rspackPackageDir } ); } + console.log(`Installing ignore-loader in the app...`); + await execa('npm', ['install', 'ignore-loader', '--save'], { cwd: appDir }); console.log(`Installing ${rspackPkg.name}@${rspackPkg.version} in the app...`); await execa('npm', ['install', `${rspackPkg.name}@${rspackPkg.version}`, '--save'], { cwd: appDir }); console.log(`Linking local meteor-rspack from ${rspackPackageDir}...`); From d167ac1e43d9d057a65ba6402ba1899806ac39c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Fri, 27 Feb 2026 17:40:25 +0100 Subject: [PATCH 121/166] add portable build support for Meteor environments and update related tests --- npm-packages/meteor-rspack/index.d.ts | 6 ++++ .../meteor-rspack/lib/meteorRspackHelpers.js | 14 ++++++++ npm-packages/meteor-rspack/rspack.config.js | 24 ++++++++++--- tools/cli/commands-packages.js | 2 +- .../apps/typescript/rspack.config.ts | 1 + tools/modern-tests/test-helpers.js | 17 ++++++--- tools/modern-tests/typescript.test.js | 35 ++++++++++++++++++- 7 files changed, 87 insertions(+), 12 deletions(-) diff --git a/npm-packages/meteor-rspack/index.d.ts b/npm-packages/meteor-rspack/index.d.ts index 896b9703df..449e6d1a85 100644 --- a/npm-packages/meteor-rspack/index.d.ts +++ b/npm-packages/meteor-rspack/index.d.ts @@ -75,6 +75,12 @@ type MeteorEnv = Record & { disablePlugins: ( matchers: string | RegExp | ((plugin: any, index: number) => boolean) | Array boolean)> ) => Record; + /** + * Omit `Meteor.isDevelopment` and `Meteor.isProduction` from the DefinePlugin so + * the bundle is not tied to a specific Meteor environment (portable / isomorphic builds). + * @returns A config fragment with `meteor.enablePortableBuild: true` + */ + enablePortableBuild: () => Record; } export type ConfigFactory = ( diff --git a/npm-packages/meteor-rspack/lib/meteorRspackHelpers.js b/npm-packages/meteor-rspack/lib/meteorRspackHelpers.js index 1b77f977cb..a0bae11704 100644 --- a/npm-packages/meteor-rspack/lib/meteorRspackHelpers.js +++ b/npm-packages/meteor-rspack/lib/meteorRspackHelpers.js @@ -152,6 +152,19 @@ function extendSwcConfig(swcConfig) { }); } +/** + * Signal that `Meteor.isDevelopment` and `Meteor.isProduction` should be omitted + * from DefinePlugin, making the bundle portable across Meteor environments. + * Usage: return Meteor.enablePortableBuild() in your rspack.config.js + * + * @returns {Record} config fragment with `meteor.enablePortableBuild: true` + */ +function enablePortableBuild() { + return prepareMeteorRspackConfig({ + "meteor.enablePortableBuild": true, + }); +} + /** * Remove plugins from a Rspack config by name, RegExp, predicate, or array of them. * When using a function predicate, it receives both the plugin and its index in the plugins array. @@ -210,4 +223,5 @@ module.exports = { extendSwcConfig, makeWebNodeBuiltinsAlias, disablePlugins, + enablePortableBuild, }; diff --git a/npm-packages/meteor-rspack/rspack.config.js b/npm-packages/meteor-rspack/rspack.config.js index 381fbcc6d6..3b7662962d 100644 --- a/npm-packages/meteor-rspack/rspack.config.js +++ b/npm-packages/meteor-rspack/rspack.config.js @@ -20,6 +20,7 @@ const { extendSwcConfig, makeWebNodeBuiltinsAlias, disablePlugins, + enablePortableBuild, } = require('./lib/meteorRspackHelpers.js'); const { loadUserAndOverrideConfig } = require('./lib/meteorRspackConfigHelpers.js'); const { prepareMeteorRspackConfig } = require("./lib/meteorRspackConfigFactory"); @@ -301,6 +302,7 @@ module.exports = async function (inMeteor = {}, argv = {}) { prepareMeteorRspackConfig({ disablePlugins: matchers, }); + Meteor.enablePortableBuild = () => enablePortableBuild(); // Add HtmlRspackPlugin function to Meteor Meteor.HtmlRspackPlugin = (options = {}) => { @@ -340,6 +342,10 @@ module.exports = async function (inMeteor = {}, argv = {}) { ? currentMode === "development" : !!Meteor.isDevelopment || !isProd; const mode = isProd ? "production" : "development"; + const isPortableBuild = !!( + nextUserConfig?.["meteor.enablePortableBuild"] || + nextOverrideConfig?.["meteor.enablePortableBuild"] + ); const configPath = Meteor.configPath; const testEntry = Meteor.testEntry; @@ -573,8 +579,10 @@ module.exports = async function (inMeteor = {}, argv = {}) { "Meteor.isServer": JSON.stringify(false), "Meteor.isTest": JSON.stringify(isTestLike && !isTestFullApp), "Meteor.isAppTest": JSON.stringify(isTestLike && isTestFullApp), - "Meteor.isDevelopment": JSON.stringify(isDev), - "Meteor.isProduction": JSON.stringify(isProd), + ...(!isPortableBuild && { + "Meteor.isDevelopment": JSON.stringify(isDev), + "Meteor.isProduction": JSON.stringify(isProd), + }), }), ...bannerPluginConfig, Meteor.HtmlRspackPlugin(), @@ -671,15 +679,19 @@ module.exports = async function (inMeteor = {}, argv = {}) { ? { "Meteor.isTest": JSON.stringify(isTest && !isTestFullApp), "Meteor.isAppTest": JSON.stringify(isTest && isTestFullApp), - "Meteor.isDevelopment": JSON.stringify(isDev), + ...(!isPortableBuild && { + "Meteor.isDevelopment": JSON.stringify(isDev), + }), } : { "Meteor.isClient": JSON.stringify(false), "Meteor.isServer": JSON.stringify(true), "Meteor.isTest": JSON.stringify(isTestLike && !isTestFullApp), "Meteor.isAppTest": JSON.stringify(isTestLike && isTestFullApp), - "Meteor.isDevelopment": JSON.stringify(isDev), - "Meteor.isProduction": JSON.stringify(isProd), + ...(!isPortableBuild && { + "Meteor.isDevelopment": JSON.stringify(isDev), + "Meteor.isProduction": JSON.stringify(isProd), + }), } ), ...bannerPluginConfig, @@ -756,6 +768,8 @@ module.exports = async function (inMeteor = {}, argv = {}) { delete config.disablePlugins; } + delete config["meteor.enablePortableBuild"]; + // if (Meteor.isDebug || Meteor.isVerbose) { console.log("Config:", inspect(config, { depth: null, colors: true })); // } diff --git a/tools/cli/commands-packages.js b/tools/cli/commands-packages.js index 02f896676f..395c26ba36 100644 --- a/tools/cli/commands-packages.js +++ b/tools/cli/commands-packages.js @@ -1832,7 +1832,7 @@ main.registerCommand({ } // Compile the app to resolve NPM dependencies bump coming from plugins - if (!files.inCheckout() && options["npm"]) { + if (options["npm"]) { await compileMeteorApp(options); return 0; } diff --git a/tools/modern-tests/apps/typescript/rspack.config.ts b/tools/modern-tests/apps/typescript/rspack.config.ts index 35ec5ffdad..f4be271f64 100644 --- a/tools/modern-tests/apps/typescript/rspack.config.ts +++ b/tools/modern-tests/apps/typescript/rspack.config.ts @@ -16,6 +16,7 @@ const require = createRequire(import.meta.url); */ export default defineConfig(Meteor => { return { + ...Meteor.enablePortableBuild(), ...Meteor.extendSwcConfig({ jsc: { baseUrl: process.cwd(), diff --git a/tools/modern-tests/test-helpers.js b/tools/modern-tests/test-helpers.js index fd93ae3ed3..5cd13786b7 100644 --- a/tools/modern-tests/test-helpers.js +++ b/tools/modern-tests/test-helpers.js @@ -40,8 +40,15 @@ const WAIT_ON = isCI ? 2000 : 500; async function linkLocalRspack(appDir) { if (!npmLinkLocalRspack) return; const repoRoot = path.resolve(process.cwd(), '..', '..'); + + const meteorBin = path.join(repoRoot, "meteor"); + console.log(`Running meteor update --npm in ${appDir}...`); + (await execa(meteorBin, ["update", "--npm"], { + cwd: appDir, + stdio: "inherit", + })); + const rspackPackageDir = path.join(repoRoot, 'npm-packages', 'meteor-rspack'); - const rspackPkg = JSON.parse(await fs.readFile(path.join(rspackPackageDir, 'package.json'), 'utf8')); const constantsPath = path.join(repoRoot, 'packages', 'rspack', 'lib', 'constants.js'); const constantsContent = await fs.readFile(constantsPath, 'utf8'); const rspackVersionMatch = constantsContent.match(/DEFAULT_RSPACK_VERSION\s*=\s*['"]([^'"]+)['"]/); @@ -62,8 +69,6 @@ async function linkLocalRspack(appDir) { } console.log(`Installing ignore-loader in the app...`); await execa('npm', ['install', 'ignore-loader', '--save'], { cwd: appDir }); - console.log(`Installing ${rspackPkg.name}@${rspackPkg.version} in the app...`); - await execa('npm', ['install', `${rspackPkg.name}@${rspackPkg.version}`, '--save'], { cwd: appDir }); console.log(`Linking local meteor-rspack from ${rspackPackageDir}...`); await execa('npm', ['link', rspackPackageDir], { cwd: appDir }); console.log('Local meteor-rspack linked successfully.'); @@ -240,11 +245,13 @@ export function testMeteorRspackBundler(options) { // Add Rspack package appDir = isMonorepo ? path.join(tempDir, 'app') : tempDir; + await runMeteorCommand("add", ["rspack"], appDir, { + checkExitCode: true, + }); + // Link local meteor-rspack so the app picks up the latest dev version await linkLocalRspack(appDir); - await runMeteorCommand('add', ['rspack'], appDir, { checkExitCode: true }); - // Set meteor.modern.verbose to true if (verbose) { await execa('npm', ['pkg', 'delete', 'meteor.modern'], { cwd: appDir }); diff --git a/tools/modern-tests/typescript.test.js b/tools/modern-tests/typescript.test.js index 8814237c39..de6bd3d4f3 100644 --- a/tools/modern-tests/typescript.test.js +++ b/tools/modern-tests/typescript.test.js @@ -40,7 +40,18 @@ describe('TypeScript App Bundling /', () => { }); await waitForTypeScriptEnvs(result.outputLines, { isTsxEnabled: true }); await waitForTypeScriptErrorFree(result.outputLines); - await assertFileExist(tempDir, '.meteor/local/types'); + await assertFileExist(tempDir, ".meteor/local/types"); + // Portable build: Meteor.isDevelopment and Meteor.isProduction must not be defined + await waitForMeteorOutput( + result.outputLines, + /[^ ]*Meteor.isDevelopment[^ ]*: [^ ]*false[^ ]*/, + { negate: true } + ); + await waitForMeteorOutput( + result.outputLines, + /[^ ]*Meteor.isProduction[^ ]*: [^ ]*true[^ ]*/, + { negate: true } + ); }, afterRunRebuildClient: async ({ allConsoleLogs }) => { // Check for HMR output as enabled by default @@ -52,6 +63,17 @@ describe('TypeScript App Bundling /', () => { 'white-space': 'break-spaces', }); await waitForTypeScriptEnvs(result.outputLines, { isTsxEnabled: true }); + // Portable build: Meteor.isDevelopment and Meteor.isProduction must not be defined + await waitForMeteorOutput( + result.outputLines, + /[^ ]*Meteor.isDevelopment[^ ]*: [^ ]*false[^ ]*/, + { negate: true } + ); + await waitForMeteorOutput( + result.outputLines, + /[^ ]*Meteor.isProduction[^ ]*: [^ ]*true[^ ]*/, + { negate: true } + ); }, afterRunProductionRebuildClient: async ({ allConsoleLogs }) => { // Check for HMR to not be enabled in production-like mode @@ -65,6 +87,17 @@ describe('TypeScript App Bundling /', () => { }, afterBuild: async ({ result }) => { await waitForTypeScriptEnvs(result.outputLines, { isTsxEnabled: true }); + // Portable build: Meteor.isDevelopment and Meteor.isProduction must not be defined + await waitForMeteorOutput( + result.outputLines, + /[^ ]*Meteor.isDevelopment[^ ]*: [^ ]*false[^ ]*/, + { negate: true } + ); + await waitForMeteorOutput( + result.outputLines, + /[^ ]*Meteor.isProduction[^ ]*: [^ ]*true[^ ]*/, + { negate: true } + ); }, } })); From e5c351a233769212d16db229e913ba271ad0030e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Fri, 27 Feb 2026 18:47:53 +0100 Subject: [PATCH 122/166] update @meteorjs/rspack to version 1.1.0-beta.22 --- npm-packages/meteor-rspack/package-lock.json | 4 ++-- npm-packages/meteor-rspack/package.json | 2 +- packages/rspack/lib/constants.js | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/npm-packages/meteor-rspack/package-lock.json b/npm-packages/meteor-rspack/package-lock.json index cb1fe97611..b047d25e20 100644 --- a/npm-packages/meteor-rspack/package-lock.json +++ b/npm-packages/meteor-rspack/package-lock.json @@ -1,12 +1,12 @@ { "name": "@meteorjs/rspack", - "version": "1.1.0-beta.21", + "version": "1.1.0-beta.22", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@meteorjs/rspack", - "version": "1.1.0-beta.21", + "version": "1.1.0-beta.22", "license": "ISC", "dependencies": { "fast-deep-equal": "^3.1.3", diff --git a/npm-packages/meteor-rspack/package.json b/npm-packages/meteor-rspack/package.json index 5599e2309f..5941adc281 100644 --- a/npm-packages/meteor-rspack/package.json +++ b/npm-packages/meteor-rspack/package.json @@ -1,6 +1,6 @@ { "name": "@meteorjs/rspack", - "version": "1.1.0-beta.21", + "version": "1.1.0-beta.22", "description": "Configuration logic for using Rspack in Meteor projects", "main": "index.js", "type": "commonjs", diff --git a/packages/rspack/lib/constants.js b/packages/rspack/lib/constants.js index 725d7ac488..b13862eda8 100644 --- a/packages/rspack/lib/constants.js +++ b/packages/rspack/lib/constants.js @@ -5,7 +5,7 @@ export const DEFAULT_RSPACK_VERSION = '1.7.1'; -export const DEFAULT_METEOR_RSPACK_VERSION = '1.1.0-beta.21'; +export const DEFAULT_METEOR_RSPACK_VERSION = '1.1.0-beta.22'; export const DEFAULT_METEOR_RSPACK_REACT_HMR_VERSION = '1.4.3'; From 2002681d9459ed1813372d3df7ca6fb863937b6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Fri, 27 Feb 2026 18:50:55 +0100 Subject: [PATCH 123/166] set `NPM_LINK_RSPACK=false` for release branches in e2e-tests workflow --- .github/workflows/e2e-tests.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index 764cb20b16..90db692a49 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -74,6 +74,13 @@ jobs: - name: Install test deps run: npm run install:modern + - name: Set NPM_LINK_RSPACK=false for release branches + run: | + echo "Current branch: ${{ github.head_ref || github.ref_name }}" + if [[ "${{ github.head_ref || github.ref_name }}" == release-* ]]; then + echo "NPM_LINK_RSPACK=false" >> $GITHUB_ENV + fi + - name: Prepare Meteor run: ./meteor --get-ready From 5d7f035a479585e50125aa3db3a10752044c39cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Mon, 2 Mar 2026 17:15:40 +0100 Subject: [PATCH 124/166] set `NPM_LINK_RSPACK=false` for release branches in e2e-tests workflow --- packages/rspack/lib/build-context.js | 1 + tools/modern-tests/react.test.js | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/rspack/lib/build-context.js b/packages/rspack/lib/build-context.js index 38d561b295..668f68da89 100644 --- a/packages/rspack/lib/build-context.js +++ b/packages/rspack/lib/build-context.js @@ -89,6 +89,7 @@ export function ensureRspackBuildContextExists() { `*/${RSPACK_ASSETS_CONTEXT}`, `*/${RSPACK_CHUNKS_CONTEXT}`, RSPACK_DOCTOR_CONTEXT, + ...(process.env.METEOR_LOCAL_DIR ? [process.env.METEOR_LOCAL_DIR] : []), ], 'Meteor Modern-Tools build context directories', ); diff --git a/tools/modern-tests/react.test.js b/tools/modern-tests/react.test.js index 2e73a365e8..f10ececa9c 100644 --- a/tools/modern-tests/react.test.js +++ b/tools/modern-tests/react.test.js @@ -9,7 +9,7 @@ import { import { testMeteorBundler, testMeteorRspackBundler } from './test-helpers'; import fs from 'fs-extra'; import path from 'path'; -import { assertMeteorReactApp, assertConsoleEval } from "./assertions"; +import { assertMeteorReactApp, assertConsoleEval, assertFileExist } from "./assertions"; describe('React App Bundling /', () => { @@ -87,6 +87,8 @@ describe('React App Bundling /', () => { expect(await fs.pathExists(buildDir)).toBe(true); expect(await fs.pathExists(localDir)).toBe(true); + await assertFileExist(appDir, '.gitignore', { content: '.meteor/local-custom' }); + await waitForReactEnvs(result.outputLines, { isJsxEnabled: true }); // Check if images exist and return 200 status code From ce33c82044ccf8688cbad5198eedd870419f7ded Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Mon, 2 Mar 2026 17:19:19 +0100 Subject: [PATCH 125/166] handle custom `METEOR_LOCAL_DIR` in build context and `.gitignore` entries --- packages/rspack/lib/build-context.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/rspack/lib/build-context.js b/packages/rspack/lib/build-context.js index 668f68da89..755368614f 100644 --- a/packages/rspack/lib/build-context.js +++ b/packages/rspack/lib/build-context.js @@ -89,11 +89,18 @@ export function ensureRspackBuildContextExists() { `*/${RSPACK_ASSETS_CONTEXT}`, `*/${RSPACK_CHUNKS_CONTEXT}`, RSPACK_DOCTOR_CONTEXT, - ...(process.env.METEOR_LOCAL_DIR ? [process.env.METEOR_LOCAL_DIR] : []), ], 'Meteor Modern-Tools build context directories', ); + if (process.env.METEOR_LOCAL_DIR) { + addGitignoreEntries( + appDir, + [process.env.METEOR_LOCAL_DIR], + 'Meteor custom local directory (METEOR_LOCAL_DIR)', + ); + } + return buildContextPath; } From bde46471cb0c9e7c89c6b2f4055e720eea880b94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Mon, 2 Mar 2026 17:32:41 +0100 Subject: [PATCH 126/166] refactor `.gitignore` entries management for build context directories --- packages/rspack/lib/build-context.js | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/packages/rspack/lib/build-context.js b/packages/rspack/lib/build-context.js index 755368614f..5ff8a7d08c 100644 --- a/packages/rspack/lib/build-context.js +++ b/packages/rspack/lib/build-context.js @@ -82,25 +82,28 @@ export function ensureRspackBuildContextExists() { } } - addGitignoreEntries( - appDir, - [ - RSPACK_BUILD_CONTEXT, - `*/${RSPACK_ASSETS_CONTEXT}`, - `*/${RSPACK_CHUNKS_CONTEXT}`, - RSPACK_DOCTOR_CONTEXT, - ], - 'Meteor Modern-Tools build context directories', - ); + const commonBuildEntries = [ + RSPACK_BUILD_CONTEXT, + `*/${RSPACK_ASSETS_CONTEXT}`, + `*/${RSPACK_CHUNKS_CONTEXT}`, + RSPACK_DOCTOR_CONTEXT, + ]; if (process.env.METEOR_LOCAL_DIR) { addGitignoreEntries( appDir, - [process.env.METEOR_LOCAL_DIR], - 'Meteor custom local directory (METEOR_LOCAL_DIR)', + [process.env.METEOR_LOCAL_DIR, ...commonBuildEntries], + "Meteor custom local directory (METEOR_LOCAL_DIR)" ); + return buildContextPath; } + addGitignoreEntries( + appDir, + commonBuildEntries, + "Meteor Modern-Tools build context directories" + ); + return buildContextPath; } From 3ad007a916e0bd08af27dd2e45c7146e3ff04776 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Mon, 2 Mar 2026 18:00:48 +0100 Subject: [PATCH 127/166] bump `@meteorjs/rspack` version to `1.1.0-beta.30` in constants and package files --- npm-packages/meteor-rspack/package-lock.json | 4 ++-- npm-packages/meteor-rspack/package.json | 2 +- packages/rspack/lib/constants.js | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/npm-packages/meteor-rspack/package-lock.json b/npm-packages/meteor-rspack/package-lock.json index a6a08c2f4b..0919f2882e 100644 --- a/npm-packages/meteor-rspack/package-lock.json +++ b/npm-packages/meteor-rspack/package-lock.json @@ -1,12 +1,12 @@ { "name": "@meteorjs/rspack", - "version": "1.1.0-beta.10", + "version": "1.1.0-beta.30", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@meteorjs/rspack", - "version": "1.1.0-beta.10", + "version": "1.1.0-beta.30", "license": "ISC", "dependencies": { "fast-deep-equal": "^3.1.3", diff --git a/npm-packages/meteor-rspack/package.json b/npm-packages/meteor-rspack/package.json index f180c96a7d..3d530801ac 100644 --- a/npm-packages/meteor-rspack/package.json +++ b/npm-packages/meteor-rspack/package.json @@ -1,6 +1,6 @@ { "name": "@meteorjs/rspack", - "version": "1.1.0-beta.10", + "version": "1.1.0-beta.30", "description": "Configuration logic for using Rspack in Meteor projects", "main": "index.js", "type": "commonjs", diff --git a/packages/rspack/lib/constants.js b/packages/rspack/lib/constants.js index 3d202b749b..718ca52301 100644 --- a/packages/rspack/lib/constants.js +++ b/packages/rspack/lib/constants.js @@ -7,7 +7,7 @@ import path from 'path'; export const DEFAULT_RSPACK_VERSION = '1.7.1'; -export const DEFAULT_METEOR_RSPACK_VERSION = '1.1.0-beta.10'; +export const DEFAULT_METEOR_RSPACK_VERSION = '1.1.0-beta.30'; export const DEFAULT_METEOR_RSPACK_REACT_HMR_VERSION = '1.4.3'; From 879e79404edf05a7230c5d56e3ee907016919803 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 3 Mar 2026 11:40:46 +0100 Subject: [PATCH 128/166] add script for creating Meteor test apps with customization options --- package.json | 3 +- tools/modern-tests/scripts/create-app.js | 444 +++++++++++++++++++++++ 2 files changed, 446 insertions(+), 1 deletion(-) create mode 100644 tools/modern-tests/scripts/create-app.js diff --git a/package.json b/package.json index 849ccda288..99ca49b7b0 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,8 @@ "scripts": { "install:modern": "cd tools/modern-tests && npm install && npx playwright install --with-deps chromium chromium-headless-shell", "test:idle-bot": "node --test .github/scripts/__tests__/inactive-issues.test.js", - "test:modern": "cd tools/modern-tests && npm test -- " + "test:modern": "cd tools/modern-tests && npm test -- ", + "create-app:modern": "cd tools/modern-tests && node scripts/create-app.js" }, "jshintConfig": { "esversion": 11 diff --git a/tools/modern-tests/scripts/create-app.js b/tools/modern-tests/scripts/create-app.js new file mode 100644 index 0000000000..ccff48f0ca --- /dev/null +++ b/tools/modern-tests/scripts/create-app.js @@ -0,0 +1,444 @@ +#!/usr/bin/env node + +/** + * Script to create a Meteor test app for manual testing without automatic cleanup. + * + * Sources apps from: + * - tools/modern-tests/apps/ (use --app flag) + * - meteor create -- (use --skeleton flag) + * + * Usage: + * npm run create-app:modern -- --app react + * npm run create-app:modern -- --app react --output ./dist/my-react-app + * npm run create-app:modern -- --app monorepo --monorepo + * npm run create-app:modern -- --skeleton react + * npm run create-app:modern -- --skeleton react --output ./my-apps/custom-name + */ + +const path = require('path'); +const fs = require('fs-extra'); +const execa = require('execa'); + +const REPO_ROOT = path.resolve(__dirname, '../../..'); +const METEOR_EXECUTABLE = path.join(REPO_ROOT, 'meteor'); +const MODERN_TESTS_DIR = path.join(__dirname, '..'); +const APPS_DIR = path.join(MODERN_TESTS_DIR, 'apps'); +const DEFAULT_OUTPUT_DIR = path.join(REPO_ROOT, 'dist'); + +function parseArgs(argv) { + const args = { monorepo: false }; + for (let i = 0; i < argv.length; i++) { + if (argv[i] === '--app') { + args.app = argv[++i]; + } else if (argv[i] === '--skeleton') { + args.skeleton = argv[++i]; + } else if (argv[i] === '--output') { + args.output = argv[++i]; + } else if (argv[i] === '--monorepo') { + args.monorepo = true; + } else if (argv[i] === '--force' || argv[i] === '-f') { + args.force = true; + } else if (argv[i] === '--help' || argv[i] === '-h') { + args.help = true; + } + } + return args; +} + +function printHelp() { + const availableApps = fs.existsSync(APPS_DIR) + ? fs.readdirSync(APPS_DIR).join(', ') + : '(none found)'; + + console.log(` +Usage: npm run create-app:modern -- [options] + +Options: + --app Copy an existing app from tools/modern-tests/apps/ + --skeleton Create a new app via "meteor create --" + --output Full destination path for the app (default: ./dist/) + --monorepo Treat the app as a monorepo (runs npm install at both root and app/ levels) + --force, -f Remove the destination directory if it already exists before creating the app + --help, -h Show this help message + +Available apps: ${availableApps} + +Examples: + npm run create-app:modern -- --app react + npm run create-app:modern -- --app react --output ./dist/my-react-app + npm run create-app:modern -- --app monorepo --monorepo + npm run create-app:modern -- --skeleton react + npm run create-app:modern -- --skeleton react --output ./my-apps/custom-name +`); +} + +/** + * Find a test-helper function call block (e.g., testMeteorSkeleton({ skeletonName: 'react', ... })) + * that contains a matching name key/value, and return the content of its options object. + */ +function findTestHelperBlock(content, fnName, nameKey, nameValue) { + let searchStart = 0; + + while (searchStart < content.length) { + const fnIdx = content.indexOf(fnName + '(', searchStart); + if (fnIdx === -1) return null; + + const braceIdx = content.indexOf('{', fnIdx + fnName.length); + if (braceIdx === -1) return null; + + // Find matching closing brace + let depth = 0; + let endIdx = -1; + for (let i = braceIdx; i < content.length; i++) { + if (content[i] === '{') depth++; + else if (content[i] === '}') { + depth--; + if (depth === 0) { + endIdx = i; + break; + } + } + } + + if (endIdx !== -1) { + const block = content.substring(braceIdx, endIdx + 1); + const namePattern = new RegExp(`${nameKey}:\\s*['"]${nameValue}['"]`); + if (namePattern.test(block)) { + return block; + } + } + + searchStart = endIdx !== -1 ? endIdx + 1 : fnIdx + 1; + } + + return null; +} + +/** + * Parse environment variable patterns from a code string. + * Matches: + * - process.env.KEY = 'value' (non-empty string values only) + * - env: { KEY: 'value', ... } + * Only includes envs that have an actual non-empty value. + */ +function parseEnvVars(code) { + const envVars = {}; + + // Pattern 1: process.env.KEY = 'value' or "value" (non-empty values only) + const processEnvRegex = /process\.env\.(\w+)\s*=\s*['"]([^'"]+)['"]/g; + let match; + while ((match = processEnvRegex.exec(code)) !== null) { + envVars[match[1]] = match[2]; + } + + // Pattern 2: env: { KEY: 'value', ... } + const envObjRegex = /\benv:\s*\{([^}]+)\}/g; + while ((match = envObjRegex.exec(code)) !== null) { + const envContent = match[1]; + const kvRegex = /(\w+)\s*:\s*['"]([^'"]+)['"]/g; + let kvMatch; + while ((kvMatch = kvRegex.exec(envContent)) !== null) { + envVars[kvMatch[1]] = kvMatch[2]; + } + } + + return envVars; +} + +/** + * Read the corresponding test file for an app or skeleton and extract + * environment variables that the tests set (so the manually created app + * behaves the same way). + * + * For --app : reads tools/modern-tests/.test.js (whole file) + * For --skeleton : reads tools/modern-tests/skeleton.test.js and + * scopes to the testMeteorSkeleton({ skeletonName: '' }) block. + */ +function extractEnvVarsFromTestFile(sourceName, isApp) { + const testFile = isApp + ? path.join(MODERN_TESTS_DIR, `${sourceName}.test.js`) + : path.join(MODERN_TESTS_DIR, 'skeleton.test.js'); + + if (!fs.existsSync(testFile)) return {}; + + const content = fs.readFileSync(testFile, 'utf8'); + let scope; + + if (isApp) { + scope = content; + } else { + scope = findTestHelperBlock(content, 'testMeteorSkeleton', 'skeletonName', sourceName); + if (!scope) return {}; + } + + return parseEnvVars(scope); +} + +/** + * Build a shell env prefix string from an env vars object. + * e.g., { METEOR_LOCAL_DIR: '.meteor/local-custom' } => "METEOR_LOCAL_DIR=.meteor/local-custom" + */ +function buildEnvPrefix(envVars) { + const entries = Object.entries(envVars); + if (entries.length === 0) return ''; + return entries.map(([key, value]) => `${key}=${value}`).join(' '); +} + +/** + * Replace bare `meteor` command occurrences in a script string with the full + * checkout path. Matches `meteor` only as a standalone command word: + * - not preceded by `/` or a word character (avoids already-resolved paths + * and things like `something-meteor`) + * - not followed by a word character (avoids `meteor-node-stubs` etc.) + */ +function rewriteMeteorCmd(scriptValue, meteorExecutable) { + return scriptValue.replace(/(? { + const inv = n === 'start' ? 'npm start' : `npm run ${n}`; + return Math.max(max, inv.length); + }, 0); + + console.log(''); + console.log('─────────────────────────────────────────────────────'); + console.log(` App ready at: ${destDir}`); + console.log('─────────────────────────────────────────────────────'); + console.log(''); + console.log(` cd ${destDir}`); + console.log(''); + console.log(' Run commands (meteor checkout binary):'); + console.log(` ${m} run`); + console.log(` ${m} run --production`); + + if (hasTestModule) { + console.log(` ${m} test --driver-package meteortesting:mocha`); + console.log(` ${m} test --once --driver-package meteortesting:mocha`); + if (hasClient) { + console.log(` ${m} test --full-app --driver-package meteortesting:mocha`); + console.log(` ${m} test --full-app --once --driver-package meteortesting:mocha`); + } + } + + console.log(` ${m} build ./_build --directory`); + console.log(''); + console.log(' npm scripts (run from the app directory):'); + + for (const [name, cmd] of Object.entries(scripts)) { + const invocation = name === 'start' ? 'npm start' : `npm run ${name}`; + const padding = ' '.repeat(maxLen - invocation.length); + console.log(` ${invocation}${padding} # ${cmd}`); + } + + console.log('─────────────────────────────────────────────────────'); + console.log(''); +} + +async function setupFromApp(appName, destDir, { isMonorepo = false, force = false } = {}) { + const sourceDir = path.join(APPS_DIR, appName); + + if (!fs.existsSync(sourceDir)) { + const available = fs.readdirSync(APPS_DIR).join(', '); + throw new Error( + `App '${appName}' not found in tools/modern-tests/apps/\nAvailable apps: ${available}` + ); + } + + if (fs.existsSync(destDir)) { + if (force) { + console.log(`Removing existing destination: ${destDir}...`); + await fs.remove(destDir); + } else { + console.error(`Error: destination already exists: ${destDir}`); + console.error('Remove it first or use --force to replace it.'); + process.exit(1); + } + } + + await fs.ensureDir(path.dirname(destDir)); + + console.log(`Copying app '${appName}' to ${destDir}...`); + await fs.copy(sourceDir, destDir, { + dereference: true, + preserveTimestamps: true, + overwrite: true, + }); + console.log('Copy complete.'); + + const appPackageJsonPath = isMonorepo + ? path.join(destDir, 'app', 'package.json') + : path.join(destDir, 'package.json'); + + const envVars = extractEnvVarsFromTestFile(appName, true); + + if (fs.existsSync(appPackageJsonPath)) { + console.log('Injecting npm scripts into package.json...'); + if (Object.keys(envVars).length > 0) { + console.log(' env from test file:', Object.entries(envVars).map(([k, v]) => `${k}=${v}`).join(' ')); + } + await injectNpmScripts(appPackageJsonPath, envVars); + } + + const meteorAppDir = isMonorepo ? path.join(destDir, 'app') : destDir; + + console.log('Adding rspack package...'); + await execa(METEOR_EXECUTABLE, ['add', 'rspack'], { + cwd: meteorAppDir, + stdio: 'inherit', + }); + + if (isMonorepo) { + console.log('Running npm install at root level...'); + await execa.command('npm install', { cwd: destDir, stdio: 'inherit', shell: true }); + console.log('Running npm install at app level...'); + await execa.command('npm install', { + cwd: path.join(destDir, 'app'), + stdio: 'inherit', + shell: true, + }); + } else { + console.log('Running npm install...'); + await execa.command('npm install', { cwd: destDir, stdio: 'inherit', shell: true }); + } + + return { destDir, appPackageJsonPath }; +} + +async function setupFromSkeleton(skeletonName, destDir, { force = false } = {}) { + if (fs.existsSync(destDir)) { + if (force) { + console.log(`Removing existing destination: ${destDir}...`); + await fs.remove(destDir); + } else { + console.error(`Error: destination already exists: ${destDir}`); + console.error('Remove it first or use --force to replace it.'); + process.exit(1); + } + } + + const parentDir = path.dirname(destDir); + const appDirName = path.basename(destDir); + + await fs.ensureDir(parentDir); + + console.log(`Creating Meteor app '${appDirName}' via "meteor create --${skeletonName}"...`); + await execa(METEOR_EXECUTABLE, ['create', `--${skeletonName}`, appDirName], { + cwd: parentDir, + stdio: 'inherit', + }); + + console.log('Adding rspack package...'); + await execa(METEOR_EXECUTABLE, ['add', 'rspack'], { + cwd: destDir, + stdio: 'inherit', + }); + + const appPackageJsonPath = path.join(destDir, 'package.json'); + + const envVars = extractEnvVarsFromTestFile(skeletonName, false); + + if (fs.existsSync(appPackageJsonPath)) { + console.log('Injecting npm scripts into package.json...'); + if (Object.keys(envVars).length > 0) { + console.log(' env from test file:', Object.entries(envVars).map(([k, v]) => `${k}=${v}`).join(' ')); + } + await injectNpmScripts(appPackageJsonPath, envVars); + } + + return { destDir, appPackageJsonPath }; +} + +async function main() { + const args = parseArgs(process.argv.slice(2)); + + if (args.help) { + printHelp(); + process.exit(0); + } + + if (!args.app && !args.skeleton) { + console.error('Error: you must provide --app or --skeleton '); + printHelp(); + process.exit(1); + } + + if (args.app && args.skeleton) { + console.error('Error: --app and --skeleton are mutually exclusive'); + process.exit(1); + } + + const sourceName = args.app || args.skeleton; + + // --output is the full destination path; if omitted, default to ./dist/ + const destDir = args.output + ? path.resolve(REPO_ROOT, args.output) + : path.join(DEFAULT_OUTPUT_DIR, sourceName); + + let result; + if (args.app) { + result = await setupFromApp(args.app, destDir, { isMonorepo: args.monorepo, force: args.force }); + } else { + result = await setupFromSkeleton(args.skeleton, destDir, { force: args.force }); + } + + printCommandSummary(result.destDir, result.appPackageJsonPath); +} + +main().catch(err => { + console.error('Error:', err.message); + process.exit(1); +}); From 80087a972cbc9221e058cced3292c14f99613981 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 3 Mar 2026 12:29:02 +0100 Subject: [PATCH 129/166] enhance `create-app` script with ANSI colorized logs and structured output --- tools/modern-tests/scripts/create-app.js | 125 ++++++++++++++--------- 1 file changed, 74 insertions(+), 51 deletions(-) diff --git a/tools/modern-tests/scripts/create-app.js b/tools/modern-tests/scripts/create-app.js index ccff48f0ca..3c00d3f6e2 100644 --- a/tools/modern-tests/scripts/create-app.js +++ b/tools/modern-tests/scripts/create-app.js @@ -25,6 +25,27 @@ const MODERN_TESTS_DIR = path.join(__dirname, '..'); const APPS_DIR = path.join(MODERN_TESTS_DIR, 'apps'); const DEFAULT_OUTPUT_DIR = path.join(REPO_ROOT, 'dist'); +// ANSI color helpers +const c = { + reset: '\x1b[0m', + bold: '\x1b[1m', + dim: '\x1b[2m', + green: '\x1b[32m', + yellow: '\x1b[33m', + blue: '\x1b[34m', + cyan: '\x1b[36m', + red: '\x1b[31m', + magenta: '\x1b[35m', +}; +const log = { + step: (msg) => console.log(`${c.cyan}>${c.reset} ${msg}`), + success: (msg) => console.log(`${c.green}>${c.reset} ${msg}`), + info: (msg) => console.log(`${c.blue}>${c.reset} ${msg}`), + warn: (msg) => console.log(`${c.yellow}>${c.reset} ${msg}`), + error: (msg) => console.error(`${c.red}>${c.reset} ${msg}`), + detail: (msg) => console.log(` ${c.dim}${msg}${c.reset}`), +}; + function parseArgs(argv) { const args = { monorepo: false }; for (let i = 0; i < argv.length; i++) { @@ -51,24 +72,24 @@ function printHelp() { : '(none found)'; console.log(` -Usage: npm run create-app:modern -- [options] +${c.bold}Usage:${c.reset} npm run create-app:modern -- [options] -Options: - --app Copy an existing app from tools/modern-tests/apps/ - --skeleton Create a new app via "meteor create --" - --output Full destination path for the app (default: ./dist/) - --monorepo Treat the app as a monorepo (runs npm install at both root and app/ levels) - --force, -f Remove the destination directory if it already exists before creating the app - --help, -h Show this help message +${c.bold}Options:${c.reset} + ${c.cyan}--app${c.reset} Copy an existing app from tools/modern-tests/apps/ + ${c.cyan}--skeleton${c.reset} Create a new app via "meteor create --" + ${c.cyan}--output${c.reset} Full destination path for the app (default: ./dist/) + ${c.cyan}--monorepo${c.reset} Treat the app as a monorepo (runs npm install at both root and app/ levels) + ${c.cyan}--force${c.reset}, ${c.cyan}-f${c.reset} Remove the destination directory if it already exists before creating the app + ${c.cyan}--help${c.reset}, ${c.cyan}-h${c.reset} Show this help message -Available apps: ${availableApps} +${c.bold}Available apps:${c.reset} ${c.green}${availableApps}${c.reset} -Examples: - npm run create-app:modern -- --app react - npm run create-app:modern -- --app react --output ./dist/my-react-app - npm run create-app:modern -- --app monorepo --monorepo - npm run create-app:modern -- --skeleton react - npm run create-app:modern -- --skeleton react --output ./my-apps/custom-name +${c.bold}Examples:${c.reset} + ${c.dim}npm run create-app:modern -- --app react${c.reset} + ${c.dim}npm run create-app:modern -- --app react --output ./dist/my-react-app${c.reset} + ${c.dim}npm run create-app:modern -- --app monorepo --monorepo${c.reset} + ${c.dim}npm run create-app:modern -- --skeleton react${c.reset} + ${c.dim}npm run create-app:modern -- --skeleton react --output ./my-apps/custom-name${c.reset} `); } @@ -254,37 +275,39 @@ function printCommandSummary(destDir, appPackageJsonPath) { return Math.max(max, inv.length); }, 0); + const line = `${c.dim}${'─'.repeat(55)}${c.reset}`; + console.log(''); - console.log('─────────────────────────────────────────────────────'); - console.log(` App ready at: ${destDir}`); - console.log('─────────────────────────────────────────────────────'); + console.log(line); + console.log(` ${c.green}${c.bold}App ready at:${c.reset} ${c.cyan}${destDir}${c.reset}`); + console.log(line); console.log(''); - console.log(` cd ${destDir}`); + console.log(` ${c.yellow}cd ${destDir}${c.reset}`); console.log(''); - console.log(' Run commands (meteor checkout binary):'); - console.log(` ${m} run`); - console.log(` ${m} run --production`); + console.log(` ${c.bold}Run commands${c.reset} ${c.dim}(meteor checkout binary):${c.reset}`); + console.log(` ${c.dim}${m} run${c.reset}`); + console.log(` ${c.dim}${m} run --production${c.reset}`); if (hasTestModule) { - console.log(` ${m} test --driver-package meteortesting:mocha`); - console.log(` ${m} test --once --driver-package meteortesting:mocha`); + console.log(` ${c.dim}${m} test --driver-package meteortesting:mocha${c.reset}`); + console.log(` ${c.dim}${m} test --once --driver-package meteortesting:mocha${c.reset}`); if (hasClient) { - console.log(` ${m} test --full-app --driver-package meteortesting:mocha`); - console.log(` ${m} test --full-app --once --driver-package meteortesting:mocha`); + console.log(` ${c.dim}${m} test --full-app --driver-package meteortesting:mocha${c.reset}`); + console.log(` ${c.dim}${m} test --full-app --once --driver-package meteortesting:mocha${c.reset}`); } } - console.log(` ${m} build ./_build --directory`); + console.log(` ${c.dim}${m} build ./_build --directory${c.reset}`); console.log(''); - console.log(' npm scripts (run from the app directory):'); + console.log(` ${c.bold}npm scripts${c.reset} ${c.dim}(run from the app directory):${c.reset}`); for (const [name, cmd] of Object.entries(scripts)) { const invocation = name === 'start' ? 'npm start' : `npm run ${name}`; const padding = ' '.repeat(maxLen - invocation.length); - console.log(` ${invocation}${padding} # ${cmd}`); + console.log(` ${c.cyan}${invocation}${c.reset}${padding} ${c.dim}# ${cmd}${c.reset}`); } - console.log('─────────────────────────────────────────────────────'); + console.log(line); console.log(''); } @@ -300,24 +323,24 @@ async function setupFromApp(appName, destDir, { isMonorepo = false, force = fals if (fs.existsSync(destDir)) { if (force) { - console.log(`Removing existing destination: ${destDir}...`); + log.warn(`Removing existing destination: ${c.cyan}${destDir}${c.reset}`); await fs.remove(destDir); } else { - console.error(`Error: destination already exists: ${destDir}`); - console.error('Remove it first or use --force to replace it.'); + log.error(`Destination already exists: ${c.cyan}${destDir}${c.reset}`); + log.detail('Remove it first or use --force to replace it.'); process.exit(1); } } await fs.ensureDir(path.dirname(destDir)); - console.log(`Copying app '${appName}' to ${destDir}...`); + log.step(`Copying app ${c.bold}${appName}${c.reset} to ${c.cyan}${destDir}${c.reset}`); await fs.copy(sourceDir, destDir, { dereference: true, preserveTimestamps: true, overwrite: true, }); - console.log('Copy complete.'); + log.success('Copy complete.'); const appPackageJsonPath = isMonorepo ? path.join(destDir, 'app', 'package.json') @@ -326,32 +349,32 @@ async function setupFromApp(appName, destDir, { isMonorepo = false, force = fals const envVars = extractEnvVarsFromTestFile(appName, true); if (fs.existsSync(appPackageJsonPath)) { - console.log('Injecting npm scripts into package.json...'); + log.step('Injecting npm scripts into package.json...'); if (Object.keys(envVars).length > 0) { - console.log(' env from test file:', Object.entries(envVars).map(([k, v]) => `${k}=${v}`).join(' ')); + log.detail(`env from test file: ${c.magenta}${Object.entries(envVars).map(([k, v]) => `${k}=${v}`).join(' ')}${c.reset}`); } await injectNpmScripts(appPackageJsonPath, envVars); } const meteorAppDir = isMonorepo ? path.join(destDir, 'app') : destDir; - console.log('Adding rspack package...'); + log.step('Adding rspack package...'); await execa(METEOR_EXECUTABLE, ['add', 'rspack'], { cwd: meteorAppDir, stdio: 'inherit', }); if (isMonorepo) { - console.log('Running npm install at root level...'); + log.step('Running npm install at root level...'); await execa.command('npm install', { cwd: destDir, stdio: 'inherit', shell: true }); - console.log('Running npm install at app level...'); + log.step('Running npm install at app level...'); await execa.command('npm install', { cwd: path.join(destDir, 'app'), stdio: 'inherit', shell: true, }); } else { - console.log('Running npm install...'); + log.step('Running npm install...'); await execa.command('npm install', { cwd: destDir, stdio: 'inherit', shell: true }); } @@ -361,11 +384,11 @@ async function setupFromApp(appName, destDir, { isMonorepo = false, force = fals async function setupFromSkeleton(skeletonName, destDir, { force = false } = {}) { if (fs.existsSync(destDir)) { if (force) { - console.log(`Removing existing destination: ${destDir}...`); + log.warn(`Removing existing destination: ${c.cyan}${destDir}${c.reset}`); await fs.remove(destDir); } else { - console.error(`Error: destination already exists: ${destDir}`); - console.error('Remove it first or use --force to replace it.'); + log.error(`Destination already exists: ${c.cyan}${destDir}${c.reset}`); + log.detail('Remove it first or use --force to replace it.'); process.exit(1); } } @@ -375,13 +398,13 @@ async function setupFromSkeleton(skeletonName, destDir, { force = false } = {}) await fs.ensureDir(parentDir); - console.log(`Creating Meteor app '${appDirName}' via "meteor create --${skeletonName}"...`); + log.step(`Creating Meteor app ${c.bold}${appDirName}${c.reset} via ${c.dim}meteor create --${skeletonName}${c.reset}`); await execa(METEOR_EXECUTABLE, ['create', `--${skeletonName}`, appDirName], { cwd: parentDir, stdio: 'inherit', }); - console.log('Adding rspack package...'); + log.step('Adding rspack package...'); await execa(METEOR_EXECUTABLE, ['add', 'rspack'], { cwd: destDir, stdio: 'inherit', @@ -392,9 +415,9 @@ async function setupFromSkeleton(skeletonName, destDir, { force = false } = {}) const envVars = extractEnvVarsFromTestFile(skeletonName, false); if (fs.existsSync(appPackageJsonPath)) { - console.log('Injecting npm scripts into package.json...'); + log.step('Injecting npm scripts into package.json...'); if (Object.keys(envVars).length > 0) { - console.log(' env from test file:', Object.entries(envVars).map(([k, v]) => `${k}=${v}`).join(' ')); + log.detail(`env from test file: ${c.magenta}${Object.entries(envVars).map(([k, v]) => `${k}=${v}`).join(' ')}${c.reset}`); } await injectNpmScripts(appPackageJsonPath, envVars); } @@ -411,13 +434,13 @@ async function main() { } if (!args.app && !args.skeleton) { - console.error('Error: you must provide --app or --skeleton '); + log.error('You must provide --app or --skeleton '); printHelp(); process.exit(1); } if (args.app && args.skeleton) { - console.error('Error: --app and --skeleton are mutually exclusive'); + log.error('--app and --skeleton are mutually exclusive'); process.exit(1); } @@ -439,6 +462,6 @@ async function main() { } main().catch(err => { - console.error('Error:', err.message); + log.error(err.message); process.exit(1); }); From d0eed87993172abb33bbef2bdc61429508353fc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 3 Mar 2026 12:32:08 +0100 Subject: [PATCH 130/166] use `meteor npm` and `meteor update --npm` in `create-app` script to manage dependencies --- tools/cli/commands-packages.js | 2 +- tools/modern-tests/scripts/create-app.js | 28 ++++++++++++++++++------ 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/tools/cli/commands-packages.js b/tools/cli/commands-packages.js index 02f896676f..395c26ba36 100644 --- a/tools/cli/commands-packages.js +++ b/tools/cli/commands-packages.js @@ -1832,7 +1832,7 @@ main.registerCommand({ } // Compile the app to resolve NPM dependencies bump coming from plugins - if (!files.inCheckout() && options["npm"]) { + if (options["npm"]) { await compileMeteorApp(options); return 0; } diff --git a/tools/modern-tests/scripts/create-app.js b/tools/modern-tests/scripts/create-app.js index 3c00d3f6e2..eb3a9abf07 100644 --- a/tools/modern-tests/scripts/create-app.js +++ b/tools/modern-tests/scripts/create-app.js @@ -364,18 +364,23 @@ async function setupFromApp(appName, destDir, { isMonorepo = false, force = fals stdio: 'inherit', }); + log.step('Updating Meteor npm dependencies...'); + await execa(METEOR_EXECUTABLE, ['update', '--npm'], { + cwd: meteorAppDir, + stdio: 'inherit', + }); + if (isMonorepo) { - log.step('Running npm install at root level...'); - await execa.command('npm install', { cwd: destDir, stdio: 'inherit', shell: true }); - log.step('Running npm install at app level...'); - await execa.command('npm install', { + log.step('Running meteor npm install at root level...'); + await execa(METEOR_EXECUTABLE, ['npm', 'install'], { cwd: destDir, stdio: 'inherit' }); + log.step('Running meteor npm install at app level...'); + await execa(METEOR_EXECUTABLE, ['npm', 'install'], { cwd: path.join(destDir, 'app'), stdio: 'inherit', - shell: true, }); } else { - log.step('Running npm install...'); - await execa.command('npm install', { cwd: destDir, stdio: 'inherit', shell: true }); + log.step('Running meteor npm install...'); + await execa(METEOR_EXECUTABLE, ['npm', 'install'], { cwd: destDir, stdio: 'inherit' }); } return { destDir, appPackageJsonPath }; @@ -410,6 +415,15 @@ async function setupFromSkeleton(skeletonName, destDir, { force = false } = {}) stdio: 'inherit', }); + log.step('Updating Meteor npm dependencies...'); + await execa(METEOR_EXECUTABLE, ['update', '--npm'], { + cwd: destDir, + stdio: 'inherit', + }); + + log.step('Running meteor npm install...'); + await execa(METEOR_EXECUTABLE, ['npm', 'install'], { cwd: destDir, stdio: 'inherit' }); + const appPackageJsonPath = path.join(destDir, 'package.json'); const envVars = extractEnvVarsFromTestFile(skeletonName, false); From 008bd95351aca580a0fc5db4e2bb196b3789c463 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 3 Mar 2026 13:06:29 +0100 Subject: [PATCH 131/166] rename modern references on tests to e2e, like ./tools/e2e-tests/apps, create-app:e2e and etc --- .github/skills/testing/SKILL.md | 12 +++--- .github/workflows/e2e-tests.yml | 8 ++-- AGENTS.md | 2 +- package.json | 6 +-- .../apps/babel/.babelrc | 0 .../apps/babel/.gitignore | 0 .../apps/babel/.meteor/.gitignore | 0 .../apps/babel/.meteor/.id | 0 .../apps/babel/.meteor/packages | 0 .../apps/babel/.meteor/platforms | 0 .../apps/babel/.meteor/release | 0 .../apps/babel/.meteor/versions | 0 .../apps/babel/.swcrc | 0 .../apps/babel/client/main.css | 0 .../apps/babel/client/main.html | 0 .../apps/babel/client/main.jsx | 0 .../apps/babel/imports/api/links.js | 0 .../apps/babel/imports/apollo/schema.graphql | 0 .../apps/babel/imports/ui/App.jsx | 0 .../apps/babel/imports/ui/Hello.jsx | 0 .../apps/babel/imports/ui/Info.jsx | 0 .../apps/babel/package.json | 0 .../apps/babel/rspack.config.mjs | 0 .../apps/babel/server/apollo.js | 0 .../apps/babel/server/main.js | 0 .../apps/babel/tests/main.js | 0 .../apps/blaze/.gitignore | 0 .../apps/blaze/.meteor/.gitignore | 0 .../apps/blaze/.meteor/.id | 0 .../apps/blaze/.meteor/packages | 0 .../apps/blaze/.meteor/platforms | 0 .../apps/blaze/.meteor/release | 0 .../apps/blaze/.meteor/versions | 0 .../apps/blaze/client/main.css | 0 .../apps/blaze/client/main.html | 0 .../apps/blaze/client/main.js | 0 .../apps/blaze/package.json | 0 .../apps/blaze/server/main.js | 0 .../apps/blaze/tests/main.js | 0 .../apps/coffeescript/.gitignore | 0 .../apps/coffeescript/.meteor/.gitignore | 0 .../apps/coffeescript/.meteor/.id | 0 .../apps/coffeescript/.meteor/packages | 0 .../apps/coffeescript/.meteor/platforms | 0 .../apps/coffeescript/.meteor/release | 0 .../apps/coffeescript/.meteor/versions | 0 .../apps/coffeescript/client/main.coffee | 0 .../apps/coffeescript/client/main.css | 0 .../apps/coffeescript/client/main.html | 0 .../coffeescript/imports/api/links.coffee | 0 .../apps/coffeescript/imports/ui/App.coffee | 0 .../apps/coffeescript/imports/ui/Hello.coffee | 0 .../apps/coffeescript/imports/ui/Info.coffee | 0 .../apps/coffeescript/package.json | 0 .../apps/coffeescript/rspack.config.js | 0 .../apps/coffeescript/server/main.coffee | 0 .../apps/coffeescript/tests/main.coffee | 0 .../apps/full-blaze/.gitignore | 0 .../apps/full-blaze/.meteor/.gitignore | 0 .../apps/full-blaze/.meteor/.id | 0 .../apps/full-blaze/.meteor/packages | 0 .../apps/full-blaze/.meteor/platforms | 0 .../apps/full-blaze/.meteor/release | 0 .../apps/full-blaze/.meteor/versions | 0 .../apps/full-blaze/client/head.html | 0 .../apps/full-blaze/client/main.js | 0 .../apps/full-blaze/client/main.less | 0 .../full-blaze/imports/api/links/links.js | 0 .../imports/api/links/links.tests.js | 0 .../full-blaze/imports/api/links/methods.js | 0 .../imports/api/links/methods.tests.js | 0 .../imports/api/links/server/publications.js | 0 .../full-blaze/imports/startup/both/index.js | 0 .../imports/startup/client/index.js | 0 .../imports/startup/client/routes.js | 0 .../imports/startup/server/fixtures.js | 0 .../imports/startup/server/index.js | 0 .../imports/startup/server/register-api.js | 0 .../imports/ui/components/hello/hello.html | 0 .../imports/ui/components/hello/hello.js | 0 .../imports/ui/components/info/info.html | 0 .../imports/ui/components/info/info.js | 0 .../imports/ui/layouts/body/body.html | 0 .../imports/ui/layouts/body/body.js | 0 .../imports/ui/pages/home/home.html | 0 .../full-blaze/imports/ui/pages/home/home.js | 0 .../imports/ui/pages/not-found/not-found.html | 0 .../imports/ui/pages/not-found/not-found.js | 0 .../imports/ui/stylesheets/not-found.less | 0 .../apps/full-blaze/package.json | 0 .../apps/full-blaze/private/README.md | 0 .../apps/full-blaze/public/img/404.svg | 0 .../apps/full-blaze/public/img/bg-footer.svg | 0 .../apps/full-blaze/rspack.config.js | 0 .../apps/full-blaze/server/main.js | 0 .../apps/full-blaze/swc.config.js | 0 .../apps/full-blaze/tests/main.js | 0 .../apps/monorepo/.npmrc | 0 .../apps/monorepo/app/.meteor/packages | 0 .../apps/monorepo/app/.meteor/platforms | 0 .../apps/monorepo/app/.meteor/release | 0 .../apps/monorepo/app/.meteorignore | 0 .../apps/monorepo/app/.swcrc | 0 .../apps/monorepo/app/client/client.test.js | 0 .../apps/monorepo/app/client/main.css | 0 .../apps/monorepo/app/client/main.html | 0 .../apps/monorepo/app/client/main.jsx | 0 .../apps/monorepo/app/ignored/ignore.test.js | 0 .../apps/monorepo/app/imports/api/links.js | 0 .../monorepo/app/imports/emails/TestEmail.jsx | 0 .../apps/monorepo/app/imports/ui/App.jsx | 0 .../apps/monorepo/app/imports/ui/Hello.jsx | 0 .../apps/monorepo/app/imports/ui/Info.jsx | 0 .../apps/monorepo/app/package.json | 0 .../app/plugins/CustomConsoleLogPlugin.js | 0 .../apps/monorepo/app/public/1x1.png | Bin .../apps/monorepo/app/public/docs/text.md | 0 .../apps/monorepo/app/public/images/1x1.png | Bin .../apps/monorepo/app/rspack.config.cjs | 0 .../monorepo/app/rspack.config.override.cjs | 0 .../apps/monorepo/app/server/main.js | 0 .../apps/monorepo/app/server/server.test.js | 0 .../monorepo/app/tests/ignore-test.test.js | 0 .../app/tests/ignored/ignore-nested.test.js | 0 .../apps/monorepo/app/tests/main.test.js | 0 .../apps/monorepo/package.json | 0 .../apps/react-router/.gitignore | 0 .../apps/react-router/.meteor/.gitignore | 0 .../apps/react-router/.meteor/.id | 0 .../apps/react-router/.meteor/packages | 0 .../apps/react-router/.meteor/platforms | 0 .../apps/react-router/.meteor/release | 0 .../apps/react-router/.meteor/versions | 0 .../apps/react-router/.meteorignore | 0 .../apps/react-router/babel.config.js | 0 .../react-router/client/client.app-test.js | 0 .../apps/react-router/client/main.css | 0 .../apps/react-router/client/main.html | 0 .../apps/react-router/client/main.jsx | 0 .../apps/react-router/important.app-test.js | 0 .../apps/react-router/imports/api/links.js | 0 .../react-router/imports/helpers/alias.js | 0 .../apps/react-router/imports/ui/App.jsx | 0 .../apps/react-router/imports/ui/Global.less | 0 .../apps/react-router/imports/ui/Hello.jsx | 0 .../apps/react-router/imports/ui/Home.jsx | 0 .../apps/react-router/imports/ui/Info.jsx | 0 .../apps/react-router/imports/ui/NotFound.jsx | 0 .../custom-package/custom-package.js | 0 .../my-packages/custom-package/package.js | 0 .../apps/react-router/package.json | 0 .../default-package/default-package.js | 0 .../packages/default-package/package.js | 0 .../plugins/CustomConsoleLogPlugin.js | 0 .../apps/react-router/public/1x1.png | Bin .../apps/react-router/public/docs/text.md | 0 .../apps/react-router/public/images/1x1.png | Bin .../ignore.app-test.js | 0 .../apps/react-router/rspack.config.js | 0 .../react-router/rspack.config.override.js | 0 .../server/browser-tests/browser.app-test.js | 0 .../apps/react-router/server/main.js | 0 .../server/resolve-extensions/first.jsx | 0 .../server/resolve-extensions/first.tsx | 0 .../react-router/server/server.app-test.js | 0 .../apps/react-router/server/ts/helpers.ts | 0 .../apps/react-router/styles/module.css | 0 .../tests/ignored/file-to-ignored.app-test.js | 0 .../folder-to-ignored/ignore.app-test.js | 0 .../integration/test-ignored.app-test.js | 0 .../prefix-test-ignored/ignore.app-test.js | 0 .../ignore.app-test.js | 0 .../ignore.app-test.js | 0 .../nested/glob-ignored/ignore.app-test.js | 0 .../some/unit/test-ignored.app-test.js | 0 .../ignored/specific-file-ignored.app-test.js | 0 .../ignored/test-example-ignored.app-test.js | 0 .../ignored/test.temp-ignored.app-test.js | 0 .../apps/react-router/tests/main.app-test.js | 0 .../apps/react/.gitignore | 0 .../apps/react/.meteor/.gitignore | 0 .../apps/react/.meteor/.id | 0 .../apps/react/.meteor/packages | 0 .../apps/react/.meteor/platforms | 0 .../apps/react/.meteor/release | 0 .../apps/react/.meteor/versions | 0 .../apps/react/client/main.html | 0 .../apps/react/client/main.jsx | 0 .../apps/react/imports/api/links.js | 0 .../apps/react/imports/ui/App.jsx | 0 .../apps/react/imports/ui/Hello.jsx | 0 .../apps/react/imports/ui/Info.jsx | 0 .../apps/react/imports/ui/images/1x1-js.png | Bin .../apps/react/imports/ui/main.css | 0 .../apps/react/package.json | 0 .../react/plugins/CustomConsoleLogPlugin.js | 0 .../apps/react/public/1x1-css.png | Bin .../apps/react/public/1x1-public.jpg | Bin .../apps/react/rspack.config.cjs | 0 .../apps/react/server/main.js | 0 .../apps/react/tests/main.js | 0 .../apps/server-only/.meteor/.gitignore | 0 .../apps/server-only/.meteor/.id | 0 .../apps/server-only/.meteor/packages | 0 .../apps/server-only/.meteor/platforms | 0 .../apps/server-only/.meteor/release | 0 .../apps/server-only/.meteor/versions | 0 .../apps/server-only/package.json | 0 .../apps/server-only/rspack.config.js | 0 .../apps/server-only/server/main.js | 0 .../apps/solid/.gitignore | 0 .../apps/solid/.meteor/.gitignore | 0 .../apps/solid/.meteor/.id | 0 .../apps/solid/.meteor/packages | 0 .../apps/solid/.meteor/platforms | 0 .../apps/solid/.meteor/release | 0 .../apps/solid/.meteor/versions | 0 .../apps/solid/client/main.html | 0 .../apps/solid/client/main.js | 0 .../apps/solid/imports/api/links.js | 0 .../apps/solid/imports/ui/App.jsx | 0 .../apps/solid/imports/ui/Hello.jsx | 0 .../apps/solid/imports/ui/Info.jsx | 0 .../apps/solid/imports/ui/main.css | 0 .../apps/solid/imports/ui/main.jsx | 0 .../apps/solid/package.json | 0 .../apps/solid/rspack.config.js | 0 .../apps/solid/server/main.js | 0 .../apps/solid/tests/main.js | 0 .../apps/svelte/.gitignore | 0 .../apps/svelte/.meteor/.gitignore | 0 .../apps/svelte/.meteor/.id | 0 .../apps/svelte/.meteor/packages | 0 .../apps/svelte/.meteor/platforms | 0 .../apps/svelte/.meteor/release | 0 .../apps/svelte/.meteor/versions | 0 .../apps/svelte/client/main.css | 0 .../apps/svelte/client/main.html | 0 .../apps/svelte/client/main.js | 0 .../apps/svelte/imports/api/links.js | 0 .../apps/svelte/imports/ui/App.svelte | 0 .../apps/svelte/package.json | 0 .../apps/svelte/rspack.config.js | 0 .../apps/svelte/server/main.js | 0 .../apps/svelte/tests/main.js | 0 .../apps/svelte/tsconfig.json | 0 .../apps/typescript/.gitignore | 0 .../apps/typescript/.meteor/.gitignore | 0 .../apps/typescript/.meteor/.id | 0 .../apps/typescript/.meteor/packages | 0 .../apps/typescript/.meteor/platforms | 0 .../apps/typescript/.meteor/release | 0 .../apps/typescript/.meteor/versions | 0 .../apps/typescript/client/main.html | 0 .../apps/typescript/client/main.scss | 0 .../apps/typescript/client/main.tsx | 0 .../apps/typescript/imports/api/links.ts | 0 .../apps/typescript/imports/ui/App.tsx | 0 .../apps/typescript/imports/ui/Global.scss | 0 .../apps/typescript/imports/ui/Hello.tsx | 0 .../apps/typescript/imports/ui/Info.tsx | 0 .../apps/typescript/package.json | 0 .../apps/typescript/rspack.config.ts | 0 .../apps/typescript/server/main.ts | 0 .../apps/typescript/tests/client.ts | 0 .../apps/typescript/tests/server.ts | 0 .../apps/typescript/tsconfig.json | 0 .../apps/vue/.gitignore | 0 .../apps/vue/.meteor/.gitignore | 0 .../apps/vue/.meteor/.id | 0 .../apps/vue/.meteor/packages | 0 .../apps/vue/.meteor/platforms | 0 .../apps/vue/.meteor/release | 0 .../apps/vue/.meteor/versions | 0 .../apps/vue/.meteorignore | 0 .../apps/vue/client/main.css | 0 .../apps/vue/client/main.html | 0 .../apps/vue/client/main.js | 0 .../apps/vue/client/meteor.css | 0 .../apps/vue/imports/api/links.js | 0 .../apps/vue/imports/ui/App.vue | 0 .../vue/imports/ui/components/AppMenu.vue | 0 .../apps/vue/imports/ui/components/Hello.vue | 0 .../apps/vue/imports/ui/components/Info.vue | 0 .../apps/vue/imports/ui/main.css | 0 .../apps/vue/imports/ui/main.js | 0 .../apps/vue/imports/ui/router.js | 0 .../apps/vue/imports/ui/views/About.vue | 0 .../apps/vue/imports/ui/views/Home.vue | 0 .../apps/vue/package.json | 0 .../apps/vue/postcss.config.js | 0 .../apps/vue/rspack.config.js | 0 .../apps/vue/server/entry-meteor.js | 0 .../apps/vue/server/main.js | 0 .../apps/vue/tests/main.js | 0 .../{modern-tests => e2e-tests}/assertions.js | 0 .../babel.config.js | 0 .../{modern-tests => e2e-tests}/babel.test.js | 0 .../{modern-tests => e2e-tests}/blaze.test.js | 0 .../coffeescript.test.js | 0 .../full-blaze.test.js | 0 tools/{modern-tests => e2e-tests}/helpers.js | 0 .../jest.config.js | 0 .../{modern-tests => e2e-tests}/jest.setup.js | 0 .../monorepo.test.js | 0 .../package-lock.json | 4 +- .../{modern-tests => e2e-tests}/package.json | 2 +- .../react-router.test.js | 0 .../{modern-tests => e2e-tests}/react.test.js | 0 .../scripts/create-app.js | 40 +++++++++--------- .../server-only.test.js | 0 .../skeleton.test.js | 0 .../{modern-tests => e2e-tests}/solid.test.js | 0 .../svelte.test.js | 0 .../test-helpers.js | 0 .../typescript.test.js | 0 tools/{modern-tests => e2e-tests}/vue.test.js | 0 317 files changed, 37 insertions(+), 37 deletions(-) rename tools/{modern-tests => e2e-tests}/apps/babel/.babelrc (100%) rename tools/{modern-tests => e2e-tests}/apps/babel/.gitignore (100%) rename tools/{modern-tests => e2e-tests}/apps/babel/.meteor/.gitignore (100%) rename tools/{modern-tests => e2e-tests}/apps/babel/.meteor/.id (100%) rename tools/{modern-tests => e2e-tests}/apps/babel/.meteor/packages (100%) rename tools/{modern-tests => e2e-tests}/apps/babel/.meteor/platforms (100%) rename tools/{modern-tests => e2e-tests}/apps/babel/.meteor/release (100%) rename tools/{modern-tests => e2e-tests}/apps/babel/.meteor/versions (100%) rename tools/{modern-tests => e2e-tests}/apps/babel/.swcrc (100%) rename tools/{modern-tests => e2e-tests}/apps/babel/client/main.css (100%) rename tools/{modern-tests => e2e-tests}/apps/babel/client/main.html (100%) rename tools/{modern-tests => e2e-tests}/apps/babel/client/main.jsx (100%) rename tools/{modern-tests => e2e-tests}/apps/babel/imports/api/links.js (100%) rename tools/{modern-tests => e2e-tests}/apps/babel/imports/apollo/schema.graphql (100%) rename tools/{modern-tests => e2e-tests}/apps/babel/imports/ui/App.jsx (100%) rename tools/{modern-tests => e2e-tests}/apps/babel/imports/ui/Hello.jsx (100%) rename tools/{modern-tests => e2e-tests}/apps/babel/imports/ui/Info.jsx (100%) rename tools/{modern-tests => e2e-tests}/apps/babel/package.json (100%) rename tools/{modern-tests => e2e-tests}/apps/babel/rspack.config.mjs (100%) rename tools/{modern-tests => e2e-tests}/apps/babel/server/apollo.js (100%) rename tools/{modern-tests => e2e-tests}/apps/babel/server/main.js (100%) rename tools/{modern-tests => e2e-tests}/apps/babel/tests/main.js (100%) rename tools/{modern-tests => e2e-tests}/apps/blaze/.gitignore (100%) rename tools/{modern-tests => e2e-tests}/apps/blaze/.meteor/.gitignore (100%) rename tools/{modern-tests => e2e-tests}/apps/blaze/.meteor/.id (100%) rename tools/{modern-tests => e2e-tests}/apps/blaze/.meteor/packages (100%) rename tools/{modern-tests => e2e-tests}/apps/blaze/.meteor/platforms (100%) rename tools/{modern-tests => e2e-tests}/apps/blaze/.meteor/release (100%) rename tools/{modern-tests => e2e-tests}/apps/blaze/.meteor/versions (100%) rename tools/{modern-tests => e2e-tests}/apps/blaze/client/main.css (100%) rename tools/{modern-tests => e2e-tests}/apps/blaze/client/main.html (100%) rename tools/{modern-tests => e2e-tests}/apps/blaze/client/main.js (100%) rename tools/{modern-tests => e2e-tests}/apps/blaze/package.json (100%) rename tools/{modern-tests => e2e-tests}/apps/blaze/server/main.js (100%) rename tools/{modern-tests => e2e-tests}/apps/blaze/tests/main.js (100%) rename tools/{modern-tests => e2e-tests}/apps/coffeescript/.gitignore (100%) rename tools/{modern-tests => e2e-tests}/apps/coffeescript/.meteor/.gitignore (100%) rename tools/{modern-tests => e2e-tests}/apps/coffeescript/.meteor/.id (100%) rename tools/{modern-tests => e2e-tests}/apps/coffeescript/.meteor/packages (100%) rename tools/{modern-tests => e2e-tests}/apps/coffeescript/.meteor/platforms (100%) rename tools/{modern-tests => e2e-tests}/apps/coffeescript/.meteor/release (100%) rename tools/{modern-tests => e2e-tests}/apps/coffeescript/.meteor/versions (100%) rename tools/{modern-tests => e2e-tests}/apps/coffeescript/client/main.coffee (100%) rename tools/{modern-tests => e2e-tests}/apps/coffeescript/client/main.css (100%) rename tools/{modern-tests => e2e-tests}/apps/coffeescript/client/main.html (100%) rename tools/{modern-tests => e2e-tests}/apps/coffeescript/imports/api/links.coffee (100%) rename tools/{modern-tests => e2e-tests}/apps/coffeescript/imports/ui/App.coffee (100%) rename tools/{modern-tests => e2e-tests}/apps/coffeescript/imports/ui/Hello.coffee (100%) rename tools/{modern-tests => e2e-tests}/apps/coffeescript/imports/ui/Info.coffee (100%) rename tools/{modern-tests => e2e-tests}/apps/coffeescript/package.json (100%) rename tools/{modern-tests => e2e-tests}/apps/coffeescript/rspack.config.js (100%) rename tools/{modern-tests => e2e-tests}/apps/coffeescript/server/main.coffee (100%) rename tools/{modern-tests => e2e-tests}/apps/coffeescript/tests/main.coffee (100%) rename tools/{modern-tests => e2e-tests}/apps/full-blaze/.gitignore (100%) rename tools/{modern-tests => e2e-tests}/apps/full-blaze/.meteor/.gitignore (100%) rename tools/{modern-tests => e2e-tests}/apps/full-blaze/.meteor/.id (100%) rename tools/{modern-tests => e2e-tests}/apps/full-blaze/.meteor/packages (100%) rename tools/{modern-tests => e2e-tests}/apps/full-blaze/.meteor/platforms (100%) rename tools/{modern-tests => e2e-tests}/apps/full-blaze/.meteor/release (100%) rename tools/{modern-tests => e2e-tests}/apps/full-blaze/.meteor/versions (100%) rename tools/{modern-tests => e2e-tests}/apps/full-blaze/client/head.html (100%) rename tools/{modern-tests => e2e-tests}/apps/full-blaze/client/main.js (100%) rename tools/{modern-tests => e2e-tests}/apps/full-blaze/client/main.less (100%) rename tools/{modern-tests => e2e-tests}/apps/full-blaze/imports/api/links/links.js (100%) rename tools/{modern-tests => e2e-tests}/apps/full-blaze/imports/api/links/links.tests.js (100%) rename tools/{modern-tests => e2e-tests}/apps/full-blaze/imports/api/links/methods.js (100%) rename tools/{modern-tests => e2e-tests}/apps/full-blaze/imports/api/links/methods.tests.js (100%) rename tools/{modern-tests => e2e-tests}/apps/full-blaze/imports/api/links/server/publications.js (100%) rename tools/{modern-tests => e2e-tests}/apps/full-blaze/imports/startup/both/index.js (100%) rename tools/{modern-tests => e2e-tests}/apps/full-blaze/imports/startup/client/index.js (100%) rename tools/{modern-tests => e2e-tests}/apps/full-blaze/imports/startup/client/routes.js (100%) rename tools/{modern-tests => e2e-tests}/apps/full-blaze/imports/startup/server/fixtures.js (100%) rename tools/{modern-tests => e2e-tests}/apps/full-blaze/imports/startup/server/index.js (100%) rename tools/{modern-tests => e2e-tests}/apps/full-blaze/imports/startup/server/register-api.js (100%) rename tools/{modern-tests => e2e-tests}/apps/full-blaze/imports/ui/components/hello/hello.html (100%) rename tools/{modern-tests => e2e-tests}/apps/full-blaze/imports/ui/components/hello/hello.js (100%) rename tools/{modern-tests => e2e-tests}/apps/full-blaze/imports/ui/components/info/info.html (100%) rename tools/{modern-tests => e2e-tests}/apps/full-blaze/imports/ui/components/info/info.js (100%) rename tools/{modern-tests => e2e-tests}/apps/full-blaze/imports/ui/layouts/body/body.html (100%) rename tools/{modern-tests => e2e-tests}/apps/full-blaze/imports/ui/layouts/body/body.js (100%) rename tools/{modern-tests => e2e-tests}/apps/full-blaze/imports/ui/pages/home/home.html (100%) rename tools/{modern-tests => e2e-tests}/apps/full-blaze/imports/ui/pages/home/home.js (100%) rename tools/{modern-tests => e2e-tests}/apps/full-blaze/imports/ui/pages/not-found/not-found.html (100%) rename tools/{modern-tests => e2e-tests}/apps/full-blaze/imports/ui/pages/not-found/not-found.js (100%) rename tools/{modern-tests => e2e-tests}/apps/full-blaze/imports/ui/stylesheets/not-found.less (100%) rename tools/{modern-tests => e2e-tests}/apps/full-blaze/package.json (100%) rename tools/{modern-tests => e2e-tests}/apps/full-blaze/private/README.md (100%) rename tools/{modern-tests => e2e-tests}/apps/full-blaze/public/img/404.svg (100%) rename tools/{modern-tests => e2e-tests}/apps/full-blaze/public/img/bg-footer.svg (100%) rename tools/{modern-tests => e2e-tests}/apps/full-blaze/rspack.config.js (100%) rename tools/{modern-tests => e2e-tests}/apps/full-blaze/server/main.js (100%) rename tools/{modern-tests => e2e-tests}/apps/full-blaze/swc.config.js (100%) rename tools/{modern-tests => e2e-tests}/apps/full-blaze/tests/main.js (100%) rename tools/{modern-tests => e2e-tests}/apps/monorepo/.npmrc (100%) rename tools/{modern-tests => e2e-tests}/apps/monorepo/app/.meteor/packages (100%) rename tools/{modern-tests => e2e-tests}/apps/monorepo/app/.meteor/platforms (100%) rename tools/{modern-tests => e2e-tests}/apps/monorepo/app/.meteor/release (100%) rename tools/{modern-tests => e2e-tests}/apps/monorepo/app/.meteorignore (100%) rename tools/{modern-tests => e2e-tests}/apps/monorepo/app/.swcrc (100%) rename tools/{modern-tests => e2e-tests}/apps/monorepo/app/client/client.test.js (100%) rename tools/{modern-tests => e2e-tests}/apps/monorepo/app/client/main.css (100%) rename tools/{modern-tests => e2e-tests}/apps/monorepo/app/client/main.html (100%) rename tools/{modern-tests => e2e-tests}/apps/monorepo/app/client/main.jsx (100%) rename tools/{modern-tests => e2e-tests}/apps/monorepo/app/ignored/ignore.test.js (100%) rename tools/{modern-tests => e2e-tests}/apps/monorepo/app/imports/api/links.js (100%) rename tools/{modern-tests => e2e-tests}/apps/monorepo/app/imports/emails/TestEmail.jsx (100%) rename tools/{modern-tests => e2e-tests}/apps/monorepo/app/imports/ui/App.jsx (100%) rename tools/{modern-tests => e2e-tests}/apps/monorepo/app/imports/ui/Hello.jsx (100%) rename tools/{modern-tests => e2e-tests}/apps/monorepo/app/imports/ui/Info.jsx (100%) rename tools/{modern-tests => e2e-tests}/apps/monorepo/app/package.json (100%) rename tools/{modern-tests => e2e-tests}/apps/monorepo/app/plugins/CustomConsoleLogPlugin.js (100%) rename tools/{modern-tests => e2e-tests}/apps/monorepo/app/public/1x1.png (100%) rename tools/{modern-tests => e2e-tests}/apps/monorepo/app/public/docs/text.md (100%) rename tools/{modern-tests => e2e-tests}/apps/monorepo/app/public/images/1x1.png (100%) rename tools/{modern-tests => e2e-tests}/apps/monorepo/app/rspack.config.cjs (100%) rename tools/{modern-tests => e2e-tests}/apps/monorepo/app/rspack.config.override.cjs (100%) rename tools/{modern-tests => e2e-tests}/apps/monorepo/app/server/main.js (100%) rename tools/{modern-tests => e2e-tests}/apps/monorepo/app/server/server.test.js (100%) rename tools/{modern-tests => e2e-tests}/apps/monorepo/app/tests/ignore-test.test.js (100%) rename tools/{modern-tests => e2e-tests}/apps/monorepo/app/tests/ignored/ignore-nested.test.js (100%) rename tools/{modern-tests => e2e-tests}/apps/monorepo/app/tests/main.test.js (100%) rename tools/{modern-tests => e2e-tests}/apps/monorepo/package.json (100%) rename tools/{modern-tests => e2e-tests}/apps/react-router/.gitignore (100%) rename tools/{modern-tests => e2e-tests}/apps/react-router/.meteor/.gitignore (100%) rename tools/{modern-tests => e2e-tests}/apps/react-router/.meteor/.id (100%) rename tools/{modern-tests => e2e-tests}/apps/react-router/.meteor/packages (100%) rename tools/{modern-tests => e2e-tests}/apps/react-router/.meteor/platforms (100%) rename tools/{modern-tests => e2e-tests}/apps/react-router/.meteor/release (100%) rename tools/{modern-tests => e2e-tests}/apps/react-router/.meteor/versions (100%) rename tools/{modern-tests => e2e-tests}/apps/react-router/.meteorignore (100%) rename tools/{modern-tests => e2e-tests}/apps/react-router/babel.config.js (100%) rename tools/{modern-tests => e2e-tests}/apps/react-router/client/client.app-test.js (100%) rename tools/{modern-tests => e2e-tests}/apps/react-router/client/main.css (100%) rename tools/{modern-tests => e2e-tests}/apps/react-router/client/main.html (100%) rename tools/{modern-tests => e2e-tests}/apps/react-router/client/main.jsx (100%) rename tools/{modern-tests => e2e-tests}/apps/react-router/important.app-test.js (100%) rename tools/{modern-tests => e2e-tests}/apps/react-router/imports/api/links.js (100%) rename tools/{modern-tests => e2e-tests}/apps/react-router/imports/helpers/alias.js (100%) rename tools/{modern-tests => e2e-tests}/apps/react-router/imports/ui/App.jsx (100%) rename tools/{modern-tests => e2e-tests}/apps/react-router/imports/ui/Global.less (100%) rename tools/{modern-tests => e2e-tests}/apps/react-router/imports/ui/Hello.jsx (100%) rename tools/{modern-tests => e2e-tests}/apps/react-router/imports/ui/Home.jsx (100%) rename tools/{modern-tests => e2e-tests}/apps/react-router/imports/ui/Info.jsx (100%) rename tools/{modern-tests => e2e-tests}/apps/react-router/imports/ui/NotFound.jsx (100%) rename tools/{modern-tests => e2e-tests}/apps/react-router/my-packages/custom-package/custom-package.js (100%) rename tools/{modern-tests => e2e-tests}/apps/react-router/my-packages/custom-package/package.js (100%) rename tools/{modern-tests => e2e-tests}/apps/react-router/package.json (100%) rename tools/{modern-tests => e2e-tests}/apps/react-router/packages/default-package/default-package.js (100%) rename tools/{modern-tests => e2e-tests}/apps/react-router/packages/default-package/package.js (100%) rename tools/{modern-tests => e2e-tests}/apps/react-router/plugins/CustomConsoleLogPlugin.js (100%) rename tools/{modern-tests => e2e-tests}/apps/react-router/public/1x1.png (100%) rename tools/{modern-tests => e2e-tests}/apps/react-router/public/docs/text.md (100%) rename tools/{modern-tests => e2e-tests}/apps/react-router/public/images/1x1.png (100%) rename tools/{modern-tests => e2e-tests}/apps/react-router/react-router-wxyz-ignored/ignore.app-test.js (100%) rename tools/{modern-tests => e2e-tests}/apps/react-router/rspack.config.js (100%) rename tools/{modern-tests => e2e-tests}/apps/react-router/rspack.config.override.js (100%) rename tools/{modern-tests => e2e-tests}/apps/react-router/server/browser-tests/browser.app-test.js (100%) rename tools/{modern-tests => e2e-tests}/apps/react-router/server/main.js (100%) rename tools/{modern-tests => e2e-tests}/apps/react-router/server/resolve-extensions/first.jsx (100%) rename tools/{modern-tests => e2e-tests}/apps/react-router/server/resolve-extensions/first.tsx (100%) rename tools/{modern-tests => e2e-tests}/apps/react-router/server/server.app-test.js (100%) rename tools/{modern-tests => e2e-tests}/apps/react-router/server/ts/helpers.ts (100%) rename tools/{modern-tests => e2e-tests}/apps/react-router/styles/module.css (100%) rename tools/{modern-tests => e2e-tests}/apps/react-router/tests/ignored/file-to-ignored.app-test.js (100%) rename tools/{modern-tests => e2e-tests}/apps/react-router/tests/ignored/folder-to-ignored/ignore.app-test.js (100%) rename tools/{modern-tests => e2e-tests}/apps/react-router/tests/ignored/integration/test-ignored.app-test.js (100%) rename tools/{modern-tests => e2e-tests}/apps/react-router/tests/ignored/prefix-test-ignored/ignore.app-test.js (100%) rename tools/{modern-tests => e2e-tests}/apps/react-router/tests/ignored/react-router-wxyz-ignored/ignore.app-test.js (100%) rename tools/{modern-tests => e2e-tests}/apps/react-router/tests/ignored/some-test-ignored-suffix/ignore.app-test.js (100%) rename tools/{modern-tests => e2e-tests}/apps/react-router/tests/ignored/some/nested/glob-ignored/ignore.app-test.js (100%) rename tools/{modern-tests => e2e-tests}/apps/react-router/tests/ignored/some/unit/test-ignored.app-test.js (100%) rename tools/{modern-tests => e2e-tests}/apps/react-router/tests/ignored/specific-file-ignored.app-test.js (100%) rename tools/{modern-tests => e2e-tests}/apps/react-router/tests/ignored/test-example-ignored.app-test.js (100%) rename tools/{modern-tests => e2e-tests}/apps/react-router/tests/ignored/test.temp-ignored.app-test.js (100%) rename tools/{modern-tests => e2e-tests}/apps/react-router/tests/main.app-test.js (100%) rename tools/{modern-tests => e2e-tests}/apps/react/.gitignore (100%) rename tools/{modern-tests => e2e-tests}/apps/react/.meteor/.gitignore (100%) rename tools/{modern-tests => e2e-tests}/apps/react/.meteor/.id (100%) rename tools/{modern-tests => e2e-tests}/apps/react/.meteor/packages (100%) rename tools/{modern-tests => e2e-tests}/apps/react/.meteor/platforms (100%) rename tools/{modern-tests => e2e-tests}/apps/react/.meteor/release (100%) rename tools/{modern-tests => e2e-tests}/apps/react/.meteor/versions (100%) rename tools/{modern-tests => e2e-tests}/apps/react/client/main.html (100%) rename tools/{modern-tests => e2e-tests}/apps/react/client/main.jsx (100%) rename tools/{modern-tests => e2e-tests}/apps/react/imports/api/links.js (100%) rename tools/{modern-tests => e2e-tests}/apps/react/imports/ui/App.jsx (100%) rename tools/{modern-tests => e2e-tests}/apps/react/imports/ui/Hello.jsx (100%) rename tools/{modern-tests => e2e-tests}/apps/react/imports/ui/Info.jsx (100%) rename tools/{modern-tests => e2e-tests}/apps/react/imports/ui/images/1x1-js.png (100%) rename tools/{modern-tests => e2e-tests}/apps/react/imports/ui/main.css (100%) rename tools/{modern-tests => e2e-tests}/apps/react/package.json (100%) rename tools/{modern-tests => e2e-tests}/apps/react/plugins/CustomConsoleLogPlugin.js (100%) rename tools/{modern-tests => e2e-tests}/apps/react/public/1x1-css.png (100%) rename tools/{modern-tests => e2e-tests}/apps/react/public/1x1-public.jpg (100%) rename tools/{modern-tests => e2e-tests}/apps/react/rspack.config.cjs (100%) rename tools/{modern-tests => e2e-tests}/apps/react/server/main.js (100%) rename tools/{modern-tests => e2e-tests}/apps/react/tests/main.js (100%) rename tools/{modern-tests => e2e-tests}/apps/server-only/.meteor/.gitignore (100%) rename tools/{modern-tests => e2e-tests}/apps/server-only/.meteor/.id (100%) rename tools/{modern-tests => e2e-tests}/apps/server-only/.meteor/packages (100%) rename tools/{modern-tests => e2e-tests}/apps/server-only/.meteor/platforms (100%) rename tools/{modern-tests => e2e-tests}/apps/server-only/.meteor/release (100%) rename tools/{modern-tests => e2e-tests}/apps/server-only/.meteor/versions (100%) rename tools/{modern-tests => e2e-tests}/apps/server-only/package.json (100%) rename tools/{modern-tests => e2e-tests}/apps/server-only/rspack.config.js (100%) rename tools/{modern-tests => e2e-tests}/apps/server-only/server/main.js (100%) rename tools/{modern-tests => e2e-tests}/apps/solid/.gitignore (100%) rename tools/{modern-tests => e2e-tests}/apps/solid/.meteor/.gitignore (100%) rename tools/{modern-tests => e2e-tests}/apps/solid/.meteor/.id (100%) rename tools/{modern-tests => e2e-tests}/apps/solid/.meteor/packages (100%) rename tools/{modern-tests => e2e-tests}/apps/solid/.meteor/platforms (100%) rename tools/{modern-tests => e2e-tests}/apps/solid/.meteor/release (100%) rename tools/{modern-tests => e2e-tests}/apps/solid/.meteor/versions (100%) rename tools/{modern-tests => e2e-tests}/apps/solid/client/main.html (100%) rename tools/{modern-tests => e2e-tests}/apps/solid/client/main.js (100%) rename tools/{modern-tests => e2e-tests}/apps/solid/imports/api/links.js (100%) rename tools/{modern-tests => e2e-tests}/apps/solid/imports/ui/App.jsx (100%) rename tools/{modern-tests => e2e-tests}/apps/solid/imports/ui/Hello.jsx (100%) rename tools/{modern-tests => e2e-tests}/apps/solid/imports/ui/Info.jsx (100%) rename tools/{modern-tests => e2e-tests}/apps/solid/imports/ui/main.css (100%) rename tools/{modern-tests => e2e-tests}/apps/solid/imports/ui/main.jsx (100%) rename tools/{modern-tests => e2e-tests}/apps/solid/package.json (100%) rename tools/{modern-tests => e2e-tests}/apps/solid/rspack.config.js (100%) rename tools/{modern-tests => e2e-tests}/apps/solid/server/main.js (100%) rename tools/{modern-tests => e2e-tests}/apps/solid/tests/main.js (100%) rename tools/{modern-tests => e2e-tests}/apps/svelte/.gitignore (100%) rename tools/{modern-tests => e2e-tests}/apps/svelte/.meteor/.gitignore (100%) rename tools/{modern-tests => e2e-tests}/apps/svelte/.meteor/.id (100%) rename tools/{modern-tests => e2e-tests}/apps/svelte/.meteor/packages (100%) rename tools/{modern-tests => e2e-tests}/apps/svelte/.meteor/platforms (100%) rename tools/{modern-tests => e2e-tests}/apps/svelte/.meteor/release (100%) rename tools/{modern-tests => e2e-tests}/apps/svelte/.meteor/versions (100%) rename tools/{modern-tests => e2e-tests}/apps/svelte/client/main.css (100%) rename tools/{modern-tests => e2e-tests}/apps/svelte/client/main.html (100%) rename tools/{modern-tests => e2e-tests}/apps/svelte/client/main.js (100%) rename tools/{modern-tests => e2e-tests}/apps/svelte/imports/api/links.js (100%) rename tools/{modern-tests => e2e-tests}/apps/svelte/imports/ui/App.svelte (100%) rename tools/{modern-tests => e2e-tests}/apps/svelte/package.json (100%) rename tools/{modern-tests => e2e-tests}/apps/svelte/rspack.config.js (100%) rename tools/{modern-tests => e2e-tests}/apps/svelte/server/main.js (100%) rename tools/{modern-tests => e2e-tests}/apps/svelte/tests/main.js (100%) rename tools/{modern-tests => e2e-tests}/apps/svelte/tsconfig.json (100%) rename tools/{modern-tests => e2e-tests}/apps/typescript/.gitignore (100%) rename tools/{modern-tests => e2e-tests}/apps/typescript/.meteor/.gitignore (100%) rename tools/{modern-tests => e2e-tests}/apps/typescript/.meteor/.id (100%) rename tools/{modern-tests => e2e-tests}/apps/typescript/.meteor/packages (100%) rename tools/{modern-tests => e2e-tests}/apps/typescript/.meteor/platforms (100%) rename tools/{modern-tests => e2e-tests}/apps/typescript/.meteor/release (100%) rename tools/{modern-tests => e2e-tests}/apps/typescript/.meteor/versions (100%) rename tools/{modern-tests => e2e-tests}/apps/typescript/client/main.html (100%) rename tools/{modern-tests => e2e-tests}/apps/typescript/client/main.scss (100%) rename tools/{modern-tests => e2e-tests}/apps/typescript/client/main.tsx (100%) rename tools/{modern-tests => e2e-tests}/apps/typescript/imports/api/links.ts (100%) rename tools/{modern-tests => e2e-tests}/apps/typescript/imports/ui/App.tsx (100%) rename tools/{modern-tests => e2e-tests}/apps/typescript/imports/ui/Global.scss (100%) rename tools/{modern-tests => e2e-tests}/apps/typescript/imports/ui/Hello.tsx (100%) rename tools/{modern-tests => e2e-tests}/apps/typescript/imports/ui/Info.tsx (100%) rename tools/{modern-tests => e2e-tests}/apps/typescript/package.json (100%) rename tools/{modern-tests => e2e-tests}/apps/typescript/rspack.config.ts (100%) rename tools/{modern-tests => e2e-tests}/apps/typescript/server/main.ts (100%) rename tools/{modern-tests => e2e-tests}/apps/typescript/tests/client.ts (100%) rename tools/{modern-tests => e2e-tests}/apps/typescript/tests/server.ts (100%) rename tools/{modern-tests => e2e-tests}/apps/typescript/tsconfig.json (100%) rename tools/{modern-tests => e2e-tests}/apps/vue/.gitignore (100%) rename tools/{modern-tests => e2e-tests}/apps/vue/.meteor/.gitignore (100%) rename tools/{modern-tests => e2e-tests}/apps/vue/.meteor/.id (100%) rename tools/{modern-tests => e2e-tests}/apps/vue/.meteor/packages (100%) rename tools/{modern-tests => e2e-tests}/apps/vue/.meteor/platforms (100%) rename tools/{modern-tests => e2e-tests}/apps/vue/.meteor/release (100%) rename tools/{modern-tests => e2e-tests}/apps/vue/.meteor/versions (100%) rename tools/{modern-tests => e2e-tests}/apps/vue/.meteorignore (100%) rename tools/{modern-tests => e2e-tests}/apps/vue/client/main.css (100%) rename tools/{modern-tests => e2e-tests}/apps/vue/client/main.html (100%) rename tools/{modern-tests => e2e-tests}/apps/vue/client/main.js (100%) rename tools/{modern-tests => e2e-tests}/apps/vue/client/meteor.css (100%) rename tools/{modern-tests => e2e-tests}/apps/vue/imports/api/links.js (100%) rename tools/{modern-tests => e2e-tests}/apps/vue/imports/ui/App.vue (100%) rename tools/{modern-tests => e2e-tests}/apps/vue/imports/ui/components/AppMenu.vue (100%) rename tools/{modern-tests => e2e-tests}/apps/vue/imports/ui/components/Hello.vue (100%) rename tools/{modern-tests => e2e-tests}/apps/vue/imports/ui/components/Info.vue (100%) rename tools/{modern-tests => e2e-tests}/apps/vue/imports/ui/main.css (100%) rename tools/{modern-tests => e2e-tests}/apps/vue/imports/ui/main.js (100%) rename tools/{modern-tests => e2e-tests}/apps/vue/imports/ui/router.js (100%) rename tools/{modern-tests => e2e-tests}/apps/vue/imports/ui/views/About.vue (100%) rename tools/{modern-tests => e2e-tests}/apps/vue/imports/ui/views/Home.vue (100%) rename tools/{modern-tests => e2e-tests}/apps/vue/package.json (100%) rename tools/{modern-tests => e2e-tests}/apps/vue/postcss.config.js (100%) rename tools/{modern-tests => e2e-tests}/apps/vue/rspack.config.js (100%) rename tools/{modern-tests => e2e-tests}/apps/vue/server/entry-meteor.js (100%) rename tools/{modern-tests => e2e-tests}/apps/vue/server/main.js (100%) rename tools/{modern-tests => e2e-tests}/apps/vue/tests/main.js (100%) rename tools/{modern-tests => e2e-tests}/assertions.js (100%) rename tools/{modern-tests => e2e-tests}/babel.config.js (100%) rename tools/{modern-tests => e2e-tests}/babel.test.js (100%) rename tools/{modern-tests => e2e-tests}/blaze.test.js (100%) rename tools/{modern-tests => e2e-tests}/coffeescript.test.js (100%) rename tools/{modern-tests => e2e-tests}/full-blaze.test.js (100%) rename tools/{modern-tests => e2e-tests}/helpers.js (100%) rename tools/{modern-tests => e2e-tests}/jest.config.js (100%) rename tools/{modern-tests => e2e-tests}/jest.setup.js (100%) rename tools/{modern-tests => e2e-tests}/monorepo.test.js (100%) rename tools/{modern-tests => e2e-tests}/package-lock.json (99%) rename tools/{modern-tests => e2e-tests}/package.json (92%) rename tools/{modern-tests => e2e-tests}/react-router.test.js (100%) rename tools/{modern-tests => e2e-tests}/react.test.js (100%) rename tools/{modern-tests => e2e-tests}/scripts/create-app.js (92%) rename tools/{modern-tests => e2e-tests}/server-only.test.js (100%) rename tools/{modern-tests => e2e-tests}/skeleton.test.js (100%) rename tools/{modern-tests => e2e-tests}/solid.test.js (100%) rename tools/{modern-tests => e2e-tests}/svelte.test.js (100%) rename tools/{modern-tests => e2e-tests}/test-helpers.js (100%) rename tools/{modern-tests => e2e-tests}/typescript.test.js (100%) rename tools/{modern-tests => e2e-tests}/vue.test.js (100%) diff --git a/.github/skills/testing/SKILL.md b/.github/skills/testing/SKILL.md index 160ba85c97..8d5d0ddbe4 100644 --- a/.github/skills/testing/SKILL.md +++ b/.github/skills/testing/SKILL.md @@ -25,15 +25,15 @@ TINYTEST_FILTER="collection" ./meteor test-packages # Filter specific tests # Package tests in console (headless via Puppeteer) PUPPETEER_DOWNLOAD_PATH=~/.npm/chromium ./packages/test-in-console/run.sh -# Modern E2E tests (Jest + Playwright) -npm run install:modern # Install dependencies -npm run test:modern # Run all E2E tests -npm run test:modern -- -t="React" # Run specific test +# E2E tests (Jest + Playwright) +npm run install:e2e # Install dependencies +npm run test:e2e # Run all E2E tests +npm run test:e2e -- -t="React" # Run specific test ``` -## Modern E2E Tests (`tools/modern-tests/`) +## E2E Tests (`tools/e2e-tests/`) -Jest + Playwright suite for verifying modern bundler integrations (rspack). Tests cover framework skeletons and build scenarios. +Jest + Playwright suite for verifying bundler integrations (rspack). Tests cover framework skeletons and build scenarios. **Test apps:** `apps/{react,vue,svelte,solid,blaze,typescript,babel,coffeescript,monorepo}` diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index 20e569d8c2..78e2225e5f 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -4,7 +4,7 @@ on: pull_request: paths: - 'meteor' - - 'tools/modern-tests/**' + - 'tools/e2e-tests/**' - 'packages/rspack/**' - 'packages/tools-core/**' - 'packages/babel-compiler/**' @@ -52,7 +52,7 @@ jobs: path: | ~/.npm node_modules - tools/modern-tests/node_modules + tools/e2e-tests/node_modules packages/**/.npm .meteor dev_bundle @@ -72,7 +72,7 @@ jobs: run: npm install - name: Install test deps - run: npm run install:modern + run: npm run install:e2e - name: Prepare Meteor run: ./meteor --get-ready @@ -84,4 +84,4 @@ jobs: retry_on: error timeout_minutes: 15 retry_wait_seconds: 90 - command: npm run test:modern -- -t="${{ matrix.category }}" + command: npm run test:e2e -- -t="${{ matrix.category }}" diff --git a/AGENTS.md b/AGENTS.md index 9cac088bee..db127840d8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -9,7 +9,7 @@ Full-stack JavaScript platform for modern web and mobile applications. ./meteor create my-app # Create app ./meteor self-test # CLI tests ./meteor test-packages ./packages/ # Package tests -npm run test:modern # E2E tests (Jest + Playwright) +npm run test:e2e # E2E tests (Jest + Playwright) ``` ## Structure diff --git a/package.json b/package.json index 99ca49b7b0..3c120bc71b 100644 --- a/package.json +++ b/package.json @@ -35,10 +35,10 @@ "typescript": "^5.4.5" }, "scripts": { - "install:modern": "cd tools/modern-tests && npm install && npx playwright install --with-deps chromium chromium-headless-shell", + "install:e2e": "cd tools/e2e-tests && npm install && npx playwright install --with-deps chromium chromium-headless-shell", "test:idle-bot": "node --test .github/scripts/__tests__/inactive-issues.test.js", - "test:modern": "cd tools/modern-tests && npm test -- ", - "create-app:modern": "cd tools/modern-tests && node scripts/create-app.js" + "test:e2e": "cd tools/e2e-tests && npm test -- ", + "create-app:e2e": "cd tools/e2e-tests && node scripts/create-app.js" }, "jshintConfig": { "esversion": 11 diff --git a/tools/modern-tests/apps/babel/.babelrc b/tools/e2e-tests/apps/babel/.babelrc similarity index 100% rename from tools/modern-tests/apps/babel/.babelrc rename to tools/e2e-tests/apps/babel/.babelrc diff --git a/tools/modern-tests/apps/babel/.gitignore b/tools/e2e-tests/apps/babel/.gitignore similarity index 100% rename from tools/modern-tests/apps/babel/.gitignore rename to tools/e2e-tests/apps/babel/.gitignore diff --git a/tools/modern-tests/apps/babel/.meteor/.gitignore b/tools/e2e-tests/apps/babel/.meteor/.gitignore similarity index 100% rename from tools/modern-tests/apps/babel/.meteor/.gitignore rename to tools/e2e-tests/apps/babel/.meteor/.gitignore diff --git a/tools/modern-tests/apps/babel/.meteor/.id b/tools/e2e-tests/apps/babel/.meteor/.id similarity index 100% rename from tools/modern-tests/apps/babel/.meteor/.id rename to tools/e2e-tests/apps/babel/.meteor/.id diff --git a/tools/modern-tests/apps/babel/.meteor/packages b/tools/e2e-tests/apps/babel/.meteor/packages similarity index 100% rename from tools/modern-tests/apps/babel/.meteor/packages rename to tools/e2e-tests/apps/babel/.meteor/packages diff --git a/tools/modern-tests/apps/babel/.meteor/platforms b/tools/e2e-tests/apps/babel/.meteor/platforms similarity index 100% rename from tools/modern-tests/apps/babel/.meteor/platforms rename to tools/e2e-tests/apps/babel/.meteor/platforms diff --git a/tools/modern-tests/apps/babel/.meteor/release b/tools/e2e-tests/apps/babel/.meteor/release similarity index 100% rename from tools/modern-tests/apps/babel/.meteor/release rename to tools/e2e-tests/apps/babel/.meteor/release diff --git a/tools/modern-tests/apps/babel/.meteor/versions b/tools/e2e-tests/apps/babel/.meteor/versions similarity index 100% rename from tools/modern-tests/apps/babel/.meteor/versions rename to tools/e2e-tests/apps/babel/.meteor/versions diff --git a/tools/modern-tests/apps/babel/.swcrc b/tools/e2e-tests/apps/babel/.swcrc similarity index 100% rename from tools/modern-tests/apps/babel/.swcrc rename to tools/e2e-tests/apps/babel/.swcrc diff --git a/tools/modern-tests/apps/babel/client/main.css b/tools/e2e-tests/apps/babel/client/main.css similarity index 100% rename from tools/modern-tests/apps/babel/client/main.css rename to tools/e2e-tests/apps/babel/client/main.css diff --git a/tools/modern-tests/apps/babel/client/main.html b/tools/e2e-tests/apps/babel/client/main.html similarity index 100% rename from tools/modern-tests/apps/babel/client/main.html rename to tools/e2e-tests/apps/babel/client/main.html diff --git a/tools/modern-tests/apps/babel/client/main.jsx b/tools/e2e-tests/apps/babel/client/main.jsx similarity index 100% rename from tools/modern-tests/apps/babel/client/main.jsx rename to tools/e2e-tests/apps/babel/client/main.jsx diff --git a/tools/modern-tests/apps/babel/imports/api/links.js b/tools/e2e-tests/apps/babel/imports/api/links.js similarity index 100% rename from tools/modern-tests/apps/babel/imports/api/links.js rename to tools/e2e-tests/apps/babel/imports/api/links.js diff --git a/tools/modern-tests/apps/babel/imports/apollo/schema.graphql b/tools/e2e-tests/apps/babel/imports/apollo/schema.graphql similarity index 100% rename from tools/modern-tests/apps/babel/imports/apollo/schema.graphql rename to tools/e2e-tests/apps/babel/imports/apollo/schema.graphql diff --git a/tools/modern-tests/apps/babel/imports/ui/App.jsx b/tools/e2e-tests/apps/babel/imports/ui/App.jsx similarity index 100% rename from tools/modern-tests/apps/babel/imports/ui/App.jsx rename to tools/e2e-tests/apps/babel/imports/ui/App.jsx diff --git a/tools/modern-tests/apps/babel/imports/ui/Hello.jsx b/tools/e2e-tests/apps/babel/imports/ui/Hello.jsx similarity index 100% rename from tools/modern-tests/apps/babel/imports/ui/Hello.jsx rename to tools/e2e-tests/apps/babel/imports/ui/Hello.jsx diff --git a/tools/modern-tests/apps/babel/imports/ui/Info.jsx b/tools/e2e-tests/apps/babel/imports/ui/Info.jsx similarity index 100% rename from tools/modern-tests/apps/babel/imports/ui/Info.jsx rename to tools/e2e-tests/apps/babel/imports/ui/Info.jsx diff --git a/tools/modern-tests/apps/babel/package.json b/tools/e2e-tests/apps/babel/package.json similarity index 100% rename from tools/modern-tests/apps/babel/package.json rename to tools/e2e-tests/apps/babel/package.json diff --git a/tools/modern-tests/apps/babel/rspack.config.mjs b/tools/e2e-tests/apps/babel/rspack.config.mjs similarity index 100% rename from tools/modern-tests/apps/babel/rspack.config.mjs rename to tools/e2e-tests/apps/babel/rspack.config.mjs diff --git a/tools/modern-tests/apps/babel/server/apollo.js b/tools/e2e-tests/apps/babel/server/apollo.js similarity index 100% rename from tools/modern-tests/apps/babel/server/apollo.js rename to tools/e2e-tests/apps/babel/server/apollo.js diff --git a/tools/modern-tests/apps/babel/server/main.js b/tools/e2e-tests/apps/babel/server/main.js similarity index 100% rename from tools/modern-tests/apps/babel/server/main.js rename to tools/e2e-tests/apps/babel/server/main.js diff --git a/tools/modern-tests/apps/babel/tests/main.js b/tools/e2e-tests/apps/babel/tests/main.js similarity index 100% rename from tools/modern-tests/apps/babel/tests/main.js rename to tools/e2e-tests/apps/babel/tests/main.js diff --git a/tools/modern-tests/apps/blaze/.gitignore b/tools/e2e-tests/apps/blaze/.gitignore similarity index 100% rename from tools/modern-tests/apps/blaze/.gitignore rename to tools/e2e-tests/apps/blaze/.gitignore diff --git a/tools/modern-tests/apps/blaze/.meteor/.gitignore b/tools/e2e-tests/apps/blaze/.meteor/.gitignore similarity index 100% rename from tools/modern-tests/apps/blaze/.meteor/.gitignore rename to tools/e2e-tests/apps/blaze/.meteor/.gitignore diff --git a/tools/modern-tests/apps/blaze/.meteor/.id b/tools/e2e-tests/apps/blaze/.meteor/.id similarity index 100% rename from tools/modern-tests/apps/blaze/.meteor/.id rename to tools/e2e-tests/apps/blaze/.meteor/.id diff --git a/tools/modern-tests/apps/blaze/.meteor/packages b/tools/e2e-tests/apps/blaze/.meteor/packages similarity index 100% rename from tools/modern-tests/apps/blaze/.meteor/packages rename to tools/e2e-tests/apps/blaze/.meteor/packages diff --git a/tools/modern-tests/apps/blaze/.meteor/platforms b/tools/e2e-tests/apps/blaze/.meteor/platforms similarity index 100% rename from tools/modern-tests/apps/blaze/.meteor/platforms rename to tools/e2e-tests/apps/blaze/.meteor/platforms diff --git a/tools/modern-tests/apps/blaze/.meteor/release b/tools/e2e-tests/apps/blaze/.meteor/release similarity index 100% rename from tools/modern-tests/apps/blaze/.meteor/release rename to tools/e2e-tests/apps/blaze/.meteor/release diff --git a/tools/modern-tests/apps/blaze/.meteor/versions b/tools/e2e-tests/apps/blaze/.meteor/versions similarity index 100% rename from tools/modern-tests/apps/blaze/.meteor/versions rename to tools/e2e-tests/apps/blaze/.meteor/versions diff --git a/tools/modern-tests/apps/blaze/client/main.css b/tools/e2e-tests/apps/blaze/client/main.css similarity index 100% rename from tools/modern-tests/apps/blaze/client/main.css rename to tools/e2e-tests/apps/blaze/client/main.css diff --git a/tools/modern-tests/apps/blaze/client/main.html b/tools/e2e-tests/apps/blaze/client/main.html similarity index 100% rename from tools/modern-tests/apps/blaze/client/main.html rename to tools/e2e-tests/apps/blaze/client/main.html diff --git a/tools/modern-tests/apps/blaze/client/main.js b/tools/e2e-tests/apps/blaze/client/main.js similarity index 100% rename from tools/modern-tests/apps/blaze/client/main.js rename to tools/e2e-tests/apps/blaze/client/main.js diff --git a/tools/modern-tests/apps/blaze/package.json b/tools/e2e-tests/apps/blaze/package.json similarity index 100% rename from tools/modern-tests/apps/blaze/package.json rename to tools/e2e-tests/apps/blaze/package.json diff --git a/tools/modern-tests/apps/blaze/server/main.js b/tools/e2e-tests/apps/blaze/server/main.js similarity index 100% rename from tools/modern-tests/apps/blaze/server/main.js rename to tools/e2e-tests/apps/blaze/server/main.js diff --git a/tools/modern-tests/apps/blaze/tests/main.js b/tools/e2e-tests/apps/blaze/tests/main.js similarity index 100% rename from tools/modern-tests/apps/blaze/tests/main.js rename to tools/e2e-tests/apps/blaze/tests/main.js diff --git a/tools/modern-tests/apps/coffeescript/.gitignore b/tools/e2e-tests/apps/coffeescript/.gitignore similarity index 100% rename from tools/modern-tests/apps/coffeescript/.gitignore rename to tools/e2e-tests/apps/coffeescript/.gitignore diff --git a/tools/modern-tests/apps/coffeescript/.meteor/.gitignore b/tools/e2e-tests/apps/coffeescript/.meteor/.gitignore similarity index 100% rename from tools/modern-tests/apps/coffeescript/.meteor/.gitignore rename to tools/e2e-tests/apps/coffeescript/.meteor/.gitignore diff --git a/tools/modern-tests/apps/coffeescript/.meteor/.id b/tools/e2e-tests/apps/coffeescript/.meteor/.id similarity index 100% rename from tools/modern-tests/apps/coffeescript/.meteor/.id rename to tools/e2e-tests/apps/coffeescript/.meteor/.id diff --git a/tools/modern-tests/apps/coffeescript/.meteor/packages b/tools/e2e-tests/apps/coffeescript/.meteor/packages similarity index 100% rename from tools/modern-tests/apps/coffeescript/.meteor/packages rename to tools/e2e-tests/apps/coffeescript/.meteor/packages diff --git a/tools/modern-tests/apps/coffeescript/.meteor/platforms b/tools/e2e-tests/apps/coffeescript/.meteor/platforms similarity index 100% rename from tools/modern-tests/apps/coffeescript/.meteor/platforms rename to tools/e2e-tests/apps/coffeescript/.meteor/platforms diff --git a/tools/modern-tests/apps/coffeescript/.meteor/release b/tools/e2e-tests/apps/coffeescript/.meteor/release similarity index 100% rename from tools/modern-tests/apps/coffeescript/.meteor/release rename to tools/e2e-tests/apps/coffeescript/.meteor/release diff --git a/tools/modern-tests/apps/coffeescript/.meteor/versions b/tools/e2e-tests/apps/coffeescript/.meteor/versions similarity index 100% rename from tools/modern-tests/apps/coffeescript/.meteor/versions rename to tools/e2e-tests/apps/coffeescript/.meteor/versions diff --git a/tools/modern-tests/apps/coffeescript/client/main.coffee b/tools/e2e-tests/apps/coffeescript/client/main.coffee similarity index 100% rename from tools/modern-tests/apps/coffeescript/client/main.coffee rename to tools/e2e-tests/apps/coffeescript/client/main.coffee diff --git a/tools/modern-tests/apps/coffeescript/client/main.css b/tools/e2e-tests/apps/coffeescript/client/main.css similarity index 100% rename from tools/modern-tests/apps/coffeescript/client/main.css rename to tools/e2e-tests/apps/coffeescript/client/main.css diff --git a/tools/modern-tests/apps/coffeescript/client/main.html b/tools/e2e-tests/apps/coffeescript/client/main.html similarity index 100% rename from tools/modern-tests/apps/coffeescript/client/main.html rename to tools/e2e-tests/apps/coffeescript/client/main.html diff --git a/tools/modern-tests/apps/coffeescript/imports/api/links.coffee b/tools/e2e-tests/apps/coffeescript/imports/api/links.coffee similarity index 100% rename from tools/modern-tests/apps/coffeescript/imports/api/links.coffee rename to tools/e2e-tests/apps/coffeescript/imports/api/links.coffee diff --git a/tools/modern-tests/apps/coffeescript/imports/ui/App.coffee b/tools/e2e-tests/apps/coffeescript/imports/ui/App.coffee similarity index 100% rename from tools/modern-tests/apps/coffeescript/imports/ui/App.coffee rename to tools/e2e-tests/apps/coffeescript/imports/ui/App.coffee diff --git a/tools/modern-tests/apps/coffeescript/imports/ui/Hello.coffee b/tools/e2e-tests/apps/coffeescript/imports/ui/Hello.coffee similarity index 100% rename from tools/modern-tests/apps/coffeescript/imports/ui/Hello.coffee rename to tools/e2e-tests/apps/coffeescript/imports/ui/Hello.coffee diff --git a/tools/modern-tests/apps/coffeescript/imports/ui/Info.coffee b/tools/e2e-tests/apps/coffeescript/imports/ui/Info.coffee similarity index 100% rename from tools/modern-tests/apps/coffeescript/imports/ui/Info.coffee rename to tools/e2e-tests/apps/coffeescript/imports/ui/Info.coffee diff --git a/tools/modern-tests/apps/coffeescript/package.json b/tools/e2e-tests/apps/coffeescript/package.json similarity index 100% rename from tools/modern-tests/apps/coffeescript/package.json rename to tools/e2e-tests/apps/coffeescript/package.json diff --git a/tools/modern-tests/apps/coffeescript/rspack.config.js b/tools/e2e-tests/apps/coffeescript/rspack.config.js similarity index 100% rename from tools/modern-tests/apps/coffeescript/rspack.config.js rename to tools/e2e-tests/apps/coffeescript/rspack.config.js diff --git a/tools/modern-tests/apps/coffeescript/server/main.coffee b/tools/e2e-tests/apps/coffeescript/server/main.coffee similarity index 100% rename from tools/modern-tests/apps/coffeescript/server/main.coffee rename to tools/e2e-tests/apps/coffeescript/server/main.coffee diff --git a/tools/modern-tests/apps/coffeescript/tests/main.coffee b/tools/e2e-tests/apps/coffeescript/tests/main.coffee similarity index 100% rename from tools/modern-tests/apps/coffeescript/tests/main.coffee rename to tools/e2e-tests/apps/coffeescript/tests/main.coffee diff --git a/tools/modern-tests/apps/full-blaze/.gitignore b/tools/e2e-tests/apps/full-blaze/.gitignore similarity index 100% rename from tools/modern-tests/apps/full-blaze/.gitignore rename to tools/e2e-tests/apps/full-blaze/.gitignore diff --git a/tools/modern-tests/apps/full-blaze/.meteor/.gitignore b/tools/e2e-tests/apps/full-blaze/.meteor/.gitignore similarity index 100% rename from tools/modern-tests/apps/full-blaze/.meteor/.gitignore rename to tools/e2e-tests/apps/full-blaze/.meteor/.gitignore diff --git a/tools/modern-tests/apps/full-blaze/.meteor/.id b/tools/e2e-tests/apps/full-blaze/.meteor/.id similarity index 100% rename from tools/modern-tests/apps/full-blaze/.meteor/.id rename to tools/e2e-tests/apps/full-blaze/.meteor/.id diff --git a/tools/modern-tests/apps/full-blaze/.meteor/packages b/tools/e2e-tests/apps/full-blaze/.meteor/packages similarity index 100% rename from tools/modern-tests/apps/full-blaze/.meteor/packages rename to tools/e2e-tests/apps/full-blaze/.meteor/packages diff --git a/tools/modern-tests/apps/full-blaze/.meteor/platforms b/tools/e2e-tests/apps/full-blaze/.meteor/platforms similarity index 100% rename from tools/modern-tests/apps/full-blaze/.meteor/platforms rename to tools/e2e-tests/apps/full-blaze/.meteor/platforms diff --git a/tools/modern-tests/apps/full-blaze/.meteor/release b/tools/e2e-tests/apps/full-blaze/.meteor/release similarity index 100% rename from tools/modern-tests/apps/full-blaze/.meteor/release rename to tools/e2e-tests/apps/full-blaze/.meteor/release diff --git a/tools/modern-tests/apps/full-blaze/.meteor/versions b/tools/e2e-tests/apps/full-blaze/.meteor/versions similarity index 100% rename from tools/modern-tests/apps/full-blaze/.meteor/versions rename to tools/e2e-tests/apps/full-blaze/.meteor/versions diff --git a/tools/modern-tests/apps/full-blaze/client/head.html b/tools/e2e-tests/apps/full-blaze/client/head.html similarity index 100% rename from tools/modern-tests/apps/full-blaze/client/head.html rename to tools/e2e-tests/apps/full-blaze/client/head.html diff --git a/tools/modern-tests/apps/full-blaze/client/main.js b/tools/e2e-tests/apps/full-blaze/client/main.js similarity index 100% rename from tools/modern-tests/apps/full-blaze/client/main.js rename to tools/e2e-tests/apps/full-blaze/client/main.js diff --git a/tools/modern-tests/apps/full-blaze/client/main.less b/tools/e2e-tests/apps/full-blaze/client/main.less similarity index 100% rename from tools/modern-tests/apps/full-blaze/client/main.less rename to tools/e2e-tests/apps/full-blaze/client/main.less diff --git a/tools/modern-tests/apps/full-blaze/imports/api/links/links.js b/tools/e2e-tests/apps/full-blaze/imports/api/links/links.js similarity index 100% rename from tools/modern-tests/apps/full-blaze/imports/api/links/links.js rename to tools/e2e-tests/apps/full-blaze/imports/api/links/links.js diff --git a/tools/modern-tests/apps/full-blaze/imports/api/links/links.tests.js b/tools/e2e-tests/apps/full-blaze/imports/api/links/links.tests.js similarity index 100% rename from tools/modern-tests/apps/full-blaze/imports/api/links/links.tests.js rename to tools/e2e-tests/apps/full-blaze/imports/api/links/links.tests.js diff --git a/tools/modern-tests/apps/full-blaze/imports/api/links/methods.js b/tools/e2e-tests/apps/full-blaze/imports/api/links/methods.js similarity index 100% rename from tools/modern-tests/apps/full-blaze/imports/api/links/methods.js rename to tools/e2e-tests/apps/full-blaze/imports/api/links/methods.js diff --git a/tools/modern-tests/apps/full-blaze/imports/api/links/methods.tests.js b/tools/e2e-tests/apps/full-blaze/imports/api/links/methods.tests.js similarity index 100% rename from tools/modern-tests/apps/full-blaze/imports/api/links/methods.tests.js rename to tools/e2e-tests/apps/full-blaze/imports/api/links/methods.tests.js diff --git a/tools/modern-tests/apps/full-blaze/imports/api/links/server/publications.js b/tools/e2e-tests/apps/full-blaze/imports/api/links/server/publications.js similarity index 100% rename from tools/modern-tests/apps/full-blaze/imports/api/links/server/publications.js rename to tools/e2e-tests/apps/full-blaze/imports/api/links/server/publications.js diff --git a/tools/modern-tests/apps/full-blaze/imports/startup/both/index.js b/tools/e2e-tests/apps/full-blaze/imports/startup/both/index.js similarity index 100% rename from tools/modern-tests/apps/full-blaze/imports/startup/both/index.js rename to tools/e2e-tests/apps/full-blaze/imports/startup/both/index.js diff --git a/tools/modern-tests/apps/full-blaze/imports/startup/client/index.js b/tools/e2e-tests/apps/full-blaze/imports/startup/client/index.js similarity index 100% rename from tools/modern-tests/apps/full-blaze/imports/startup/client/index.js rename to tools/e2e-tests/apps/full-blaze/imports/startup/client/index.js diff --git a/tools/modern-tests/apps/full-blaze/imports/startup/client/routes.js b/tools/e2e-tests/apps/full-blaze/imports/startup/client/routes.js similarity index 100% rename from tools/modern-tests/apps/full-blaze/imports/startup/client/routes.js rename to tools/e2e-tests/apps/full-blaze/imports/startup/client/routes.js diff --git a/tools/modern-tests/apps/full-blaze/imports/startup/server/fixtures.js b/tools/e2e-tests/apps/full-blaze/imports/startup/server/fixtures.js similarity index 100% rename from tools/modern-tests/apps/full-blaze/imports/startup/server/fixtures.js rename to tools/e2e-tests/apps/full-blaze/imports/startup/server/fixtures.js diff --git a/tools/modern-tests/apps/full-blaze/imports/startup/server/index.js b/tools/e2e-tests/apps/full-blaze/imports/startup/server/index.js similarity index 100% rename from tools/modern-tests/apps/full-blaze/imports/startup/server/index.js rename to tools/e2e-tests/apps/full-blaze/imports/startup/server/index.js diff --git a/tools/modern-tests/apps/full-blaze/imports/startup/server/register-api.js b/tools/e2e-tests/apps/full-blaze/imports/startup/server/register-api.js similarity index 100% rename from tools/modern-tests/apps/full-blaze/imports/startup/server/register-api.js rename to tools/e2e-tests/apps/full-blaze/imports/startup/server/register-api.js diff --git a/tools/modern-tests/apps/full-blaze/imports/ui/components/hello/hello.html b/tools/e2e-tests/apps/full-blaze/imports/ui/components/hello/hello.html similarity index 100% rename from tools/modern-tests/apps/full-blaze/imports/ui/components/hello/hello.html rename to tools/e2e-tests/apps/full-blaze/imports/ui/components/hello/hello.html diff --git a/tools/modern-tests/apps/full-blaze/imports/ui/components/hello/hello.js b/tools/e2e-tests/apps/full-blaze/imports/ui/components/hello/hello.js similarity index 100% rename from tools/modern-tests/apps/full-blaze/imports/ui/components/hello/hello.js rename to tools/e2e-tests/apps/full-blaze/imports/ui/components/hello/hello.js diff --git a/tools/modern-tests/apps/full-blaze/imports/ui/components/info/info.html b/tools/e2e-tests/apps/full-blaze/imports/ui/components/info/info.html similarity index 100% rename from tools/modern-tests/apps/full-blaze/imports/ui/components/info/info.html rename to tools/e2e-tests/apps/full-blaze/imports/ui/components/info/info.html diff --git a/tools/modern-tests/apps/full-blaze/imports/ui/components/info/info.js b/tools/e2e-tests/apps/full-blaze/imports/ui/components/info/info.js similarity index 100% rename from tools/modern-tests/apps/full-blaze/imports/ui/components/info/info.js rename to tools/e2e-tests/apps/full-blaze/imports/ui/components/info/info.js diff --git a/tools/modern-tests/apps/full-blaze/imports/ui/layouts/body/body.html b/tools/e2e-tests/apps/full-blaze/imports/ui/layouts/body/body.html similarity index 100% rename from tools/modern-tests/apps/full-blaze/imports/ui/layouts/body/body.html rename to tools/e2e-tests/apps/full-blaze/imports/ui/layouts/body/body.html diff --git a/tools/modern-tests/apps/full-blaze/imports/ui/layouts/body/body.js b/tools/e2e-tests/apps/full-blaze/imports/ui/layouts/body/body.js similarity index 100% rename from tools/modern-tests/apps/full-blaze/imports/ui/layouts/body/body.js rename to tools/e2e-tests/apps/full-blaze/imports/ui/layouts/body/body.js diff --git a/tools/modern-tests/apps/full-blaze/imports/ui/pages/home/home.html b/tools/e2e-tests/apps/full-blaze/imports/ui/pages/home/home.html similarity index 100% rename from tools/modern-tests/apps/full-blaze/imports/ui/pages/home/home.html rename to tools/e2e-tests/apps/full-blaze/imports/ui/pages/home/home.html diff --git a/tools/modern-tests/apps/full-blaze/imports/ui/pages/home/home.js b/tools/e2e-tests/apps/full-blaze/imports/ui/pages/home/home.js similarity index 100% rename from tools/modern-tests/apps/full-blaze/imports/ui/pages/home/home.js rename to tools/e2e-tests/apps/full-blaze/imports/ui/pages/home/home.js diff --git a/tools/modern-tests/apps/full-blaze/imports/ui/pages/not-found/not-found.html b/tools/e2e-tests/apps/full-blaze/imports/ui/pages/not-found/not-found.html similarity index 100% rename from tools/modern-tests/apps/full-blaze/imports/ui/pages/not-found/not-found.html rename to tools/e2e-tests/apps/full-blaze/imports/ui/pages/not-found/not-found.html diff --git a/tools/modern-tests/apps/full-blaze/imports/ui/pages/not-found/not-found.js b/tools/e2e-tests/apps/full-blaze/imports/ui/pages/not-found/not-found.js similarity index 100% rename from tools/modern-tests/apps/full-blaze/imports/ui/pages/not-found/not-found.js rename to tools/e2e-tests/apps/full-blaze/imports/ui/pages/not-found/not-found.js diff --git a/tools/modern-tests/apps/full-blaze/imports/ui/stylesheets/not-found.less b/tools/e2e-tests/apps/full-blaze/imports/ui/stylesheets/not-found.less similarity index 100% rename from tools/modern-tests/apps/full-blaze/imports/ui/stylesheets/not-found.less rename to tools/e2e-tests/apps/full-blaze/imports/ui/stylesheets/not-found.less diff --git a/tools/modern-tests/apps/full-blaze/package.json b/tools/e2e-tests/apps/full-blaze/package.json similarity index 100% rename from tools/modern-tests/apps/full-blaze/package.json rename to tools/e2e-tests/apps/full-blaze/package.json diff --git a/tools/modern-tests/apps/full-blaze/private/README.md b/tools/e2e-tests/apps/full-blaze/private/README.md similarity index 100% rename from tools/modern-tests/apps/full-blaze/private/README.md rename to tools/e2e-tests/apps/full-blaze/private/README.md diff --git a/tools/modern-tests/apps/full-blaze/public/img/404.svg b/tools/e2e-tests/apps/full-blaze/public/img/404.svg similarity index 100% rename from tools/modern-tests/apps/full-blaze/public/img/404.svg rename to tools/e2e-tests/apps/full-blaze/public/img/404.svg diff --git a/tools/modern-tests/apps/full-blaze/public/img/bg-footer.svg b/tools/e2e-tests/apps/full-blaze/public/img/bg-footer.svg similarity index 100% rename from tools/modern-tests/apps/full-blaze/public/img/bg-footer.svg rename to tools/e2e-tests/apps/full-blaze/public/img/bg-footer.svg diff --git a/tools/modern-tests/apps/full-blaze/rspack.config.js b/tools/e2e-tests/apps/full-blaze/rspack.config.js similarity index 100% rename from tools/modern-tests/apps/full-blaze/rspack.config.js rename to tools/e2e-tests/apps/full-blaze/rspack.config.js diff --git a/tools/modern-tests/apps/full-blaze/server/main.js b/tools/e2e-tests/apps/full-blaze/server/main.js similarity index 100% rename from tools/modern-tests/apps/full-blaze/server/main.js rename to tools/e2e-tests/apps/full-blaze/server/main.js diff --git a/tools/modern-tests/apps/full-blaze/swc.config.js b/tools/e2e-tests/apps/full-blaze/swc.config.js similarity index 100% rename from tools/modern-tests/apps/full-blaze/swc.config.js rename to tools/e2e-tests/apps/full-blaze/swc.config.js diff --git a/tools/modern-tests/apps/full-blaze/tests/main.js b/tools/e2e-tests/apps/full-blaze/tests/main.js similarity index 100% rename from tools/modern-tests/apps/full-blaze/tests/main.js rename to tools/e2e-tests/apps/full-blaze/tests/main.js diff --git a/tools/modern-tests/apps/monorepo/.npmrc b/tools/e2e-tests/apps/monorepo/.npmrc similarity index 100% rename from tools/modern-tests/apps/monorepo/.npmrc rename to tools/e2e-tests/apps/monorepo/.npmrc diff --git a/tools/modern-tests/apps/monorepo/app/.meteor/packages b/tools/e2e-tests/apps/monorepo/app/.meteor/packages similarity index 100% rename from tools/modern-tests/apps/monorepo/app/.meteor/packages rename to tools/e2e-tests/apps/monorepo/app/.meteor/packages diff --git a/tools/modern-tests/apps/monorepo/app/.meteor/platforms b/tools/e2e-tests/apps/monorepo/app/.meteor/platforms similarity index 100% rename from tools/modern-tests/apps/monorepo/app/.meteor/platforms rename to tools/e2e-tests/apps/monorepo/app/.meteor/platforms diff --git a/tools/modern-tests/apps/monorepo/app/.meteor/release b/tools/e2e-tests/apps/monorepo/app/.meteor/release similarity index 100% rename from tools/modern-tests/apps/monorepo/app/.meteor/release rename to tools/e2e-tests/apps/monorepo/app/.meteor/release diff --git a/tools/modern-tests/apps/monorepo/app/.meteorignore b/tools/e2e-tests/apps/monorepo/app/.meteorignore similarity index 100% rename from tools/modern-tests/apps/monorepo/app/.meteorignore rename to tools/e2e-tests/apps/monorepo/app/.meteorignore diff --git a/tools/modern-tests/apps/monorepo/app/.swcrc b/tools/e2e-tests/apps/monorepo/app/.swcrc similarity index 100% rename from tools/modern-tests/apps/monorepo/app/.swcrc rename to tools/e2e-tests/apps/monorepo/app/.swcrc diff --git a/tools/modern-tests/apps/monorepo/app/client/client.test.js b/tools/e2e-tests/apps/monorepo/app/client/client.test.js similarity index 100% rename from tools/modern-tests/apps/monorepo/app/client/client.test.js rename to tools/e2e-tests/apps/monorepo/app/client/client.test.js diff --git a/tools/modern-tests/apps/monorepo/app/client/main.css b/tools/e2e-tests/apps/monorepo/app/client/main.css similarity index 100% rename from tools/modern-tests/apps/monorepo/app/client/main.css rename to tools/e2e-tests/apps/monorepo/app/client/main.css diff --git a/tools/modern-tests/apps/monorepo/app/client/main.html b/tools/e2e-tests/apps/monorepo/app/client/main.html similarity index 100% rename from tools/modern-tests/apps/monorepo/app/client/main.html rename to tools/e2e-tests/apps/monorepo/app/client/main.html diff --git a/tools/modern-tests/apps/monorepo/app/client/main.jsx b/tools/e2e-tests/apps/monorepo/app/client/main.jsx similarity index 100% rename from tools/modern-tests/apps/monorepo/app/client/main.jsx rename to tools/e2e-tests/apps/monorepo/app/client/main.jsx diff --git a/tools/modern-tests/apps/monorepo/app/ignored/ignore.test.js b/tools/e2e-tests/apps/monorepo/app/ignored/ignore.test.js similarity index 100% rename from tools/modern-tests/apps/monorepo/app/ignored/ignore.test.js rename to tools/e2e-tests/apps/monorepo/app/ignored/ignore.test.js diff --git a/tools/modern-tests/apps/monorepo/app/imports/api/links.js b/tools/e2e-tests/apps/monorepo/app/imports/api/links.js similarity index 100% rename from tools/modern-tests/apps/monorepo/app/imports/api/links.js rename to tools/e2e-tests/apps/monorepo/app/imports/api/links.js diff --git a/tools/modern-tests/apps/monorepo/app/imports/emails/TestEmail.jsx b/tools/e2e-tests/apps/monorepo/app/imports/emails/TestEmail.jsx similarity index 100% rename from tools/modern-tests/apps/monorepo/app/imports/emails/TestEmail.jsx rename to tools/e2e-tests/apps/monorepo/app/imports/emails/TestEmail.jsx diff --git a/tools/modern-tests/apps/monorepo/app/imports/ui/App.jsx b/tools/e2e-tests/apps/monorepo/app/imports/ui/App.jsx similarity index 100% rename from tools/modern-tests/apps/monorepo/app/imports/ui/App.jsx rename to tools/e2e-tests/apps/monorepo/app/imports/ui/App.jsx diff --git a/tools/modern-tests/apps/monorepo/app/imports/ui/Hello.jsx b/tools/e2e-tests/apps/monorepo/app/imports/ui/Hello.jsx similarity index 100% rename from tools/modern-tests/apps/monorepo/app/imports/ui/Hello.jsx rename to tools/e2e-tests/apps/monorepo/app/imports/ui/Hello.jsx diff --git a/tools/modern-tests/apps/monorepo/app/imports/ui/Info.jsx b/tools/e2e-tests/apps/monorepo/app/imports/ui/Info.jsx similarity index 100% rename from tools/modern-tests/apps/monorepo/app/imports/ui/Info.jsx rename to tools/e2e-tests/apps/monorepo/app/imports/ui/Info.jsx diff --git a/tools/modern-tests/apps/monorepo/app/package.json b/tools/e2e-tests/apps/monorepo/app/package.json similarity index 100% rename from tools/modern-tests/apps/monorepo/app/package.json rename to tools/e2e-tests/apps/monorepo/app/package.json diff --git a/tools/modern-tests/apps/monorepo/app/plugins/CustomConsoleLogPlugin.js b/tools/e2e-tests/apps/monorepo/app/plugins/CustomConsoleLogPlugin.js similarity index 100% rename from tools/modern-tests/apps/monorepo/app/plugins/CustomConsoleLogPlugin.js rename to tools/e2e-tests/apps/monorepo/app/plugins/CustomConsoleLogPlugin.js diff --git a/tools/modern-tests/apps/monorepo/app/public/1x1.png b/tools/e2e-tests/apps/monorepo/app/public/1x1.png similarity index 100% rename from tools/modern-tests/apps/monorepo/app/public/1x1.png rename to tools/e2e-tests/apps/monorepo/app/public/1x1.png diff --git a/tools/modern-tests/apps/monorepo/app/public/docs/text.md b/tools/e2e-tests/apps/monorepo/app/public/docs/text.md similarity index 100% rename from tools/modern-tests/apps/monorepo/app/public/docs/text.md rename to tools/e2e-tests/apps/monorepo/app/public/docs/text.md diff --git a/tools/modern-tests/apps/monorepo/app/public/images/1x1.png b/tools/e2e-tests/apps/monorepo/app/public/images/1x1.png similarity index 100% rename from tools/modern-tests/apps/monorepo/app/public/images/1x1.png rename to tools/e2e-tests/apps/monorepo/app/public/images/1x1.png diff --git a/tools/modern-tests/apps/monorepo/app/rspack.config.cjs b/tools/e2e-tests/apps/monorepo/app/rspack.config.cjs similarity index 100% rename from tools/modern-tests/apps/monorepo/app/rspack.config.cjs rename to tools/e2e-tests/apps/monorepo/app/rspack.config.cjs diff --git a/tools/modern-tests/apps/monorepo/app/rspack.config.override.cjs b/tools/e2e-tests/apps/monorepo/app/rspack.config.override.cjs similarity index 100% rename from tools/modern-tests/apps/monorepo/app/rspack.config.override.cjs rename to tools/e2e-tests/apps/monorepo/app/rspack.config.override.cjs diff --git a/tools/modern-tests/apps/monorepo/app/server/main.js b/tools/e2e-tests/apps/monorepo/app/server/main.js similarity index 100% rename from tools/modern-tests/apps/monorepo/app/server/main.js rename to tools/e2e-tests/apps/monorepo/app/server/main.js diff --git a/tools/modern-tests/apps/monorepo/app/server/server.test.js b/tools/e2e-tests/apps/monorepo/app/server/server.test.js similarity index 100% rename from tools/modern-tests/apps/monorepo/app/server/server.test.js rename to tools/e2e-tests/apps/monorepo/app/server/server.test.js diff --git a/tools/modern-tests/apps/monorepo/app/tests/ignore-test.test.js b/tools/e2e-tests/apps/monorepo/app/tests/ignore-test.test.js similarity index 100% rename from tools/modern-tests/apps/monorepo/app/tests/ignore-test.test.js rename to tools/e2e-tests/apps/monorepo/app/tests/ignore-test.test.js diff --git a/tools/modern-tests/apps/monorepo/app/tests/ignored/ignore-nested.test.js b/tools/e2e-tests/apps/monorepo/app/tests/ignored/ignore-nested.test.js similarity index 100% rename from tools/modern-tests/apps/monorepo/app/tests/ignored/ignore-nested.test.js rename to tools/e2e-tests/apps/monorepo/app/tests/ignored/ignore-nested.test.js diff --git a/tools/modern-tests/apps/monorepo/app/tests/main.test.js b/tools/e2e-tests/apps/monorepo/app/tests/main.test.js similarity index 100% rename from tools/modern-tests/apps/monorepo/app/tests/main.test.js rename to tools/e2e-tests/apps/monorepo/app/tests/main.test.js diff --git a/tools/modern-tests/apps/monorepo/package.json b/tools/e2e-tests/apps/monorepo/package.json similarity index 100% rename from tools/modern-tests/apps/monorepo/package.json rename to tools/e2e-tests/apps/monorepo/package.json diff --git a/tools/modern-tests/apps/react-router/.gitignore b/tools/e2e-tests/apps/react-router/.gitignore similarity index 100% rename from tools/modern-tests/apps/react-router/.gitignore rename to tools/e2e-tests/apps/react-router/.gitignore diff --git a/tools/modern-tests/apps/react-router/.meteor/.gitignore b/tools/e2e-tests/apps/react-router/.meteor/.gitignore similarity index 100% rename from tools/modern-tests/apps/react-router/.meteor/.gitignore rename to tools/e2e-tests/apps/react-router/.meteor/.gitignore diff --git a/tools/modern-tests/apps/react-router/.meteor/.id b/tools/e2e-tests/apps/react-router/.meteor/.id similarity index 100% rename from tools/modern-tests/apps/react-router/.meteor/.id rename to tools/e2e-tests/apps/react-router/.meteor/.id diff --git a/tools/modern-tests/apps/react-router/.meteor/packages b/tools/e2e-tests/apps/react-router/.meteor/packages similarity index 100% rename from tools/modern-tests/apps/react-router/.meteor/packages rename to tools/e2e-tests/apps/react-router/.meteor/packages diff --git a/tools/modern-tests/apps/react-router/.meteor/platforms b/tools/e2e-tests/apps/react-router/.meteor/platforms similarity index 100% rename from tools/modern-tests/apps/react-router/.meteor/platforms rename to tools/e2e-tests/apps/react-router/.meteor/platforms diff --git a/tools/modern-tests/apps/react-router/.meteor/release b/tools/e2e-tests/apps/react-router/.meteor/release similarity index 100% rename from tools/modern-tests/apps/react-router/.meteor/release rename to tools/e2e-tests/apps/react-router/.meteor/release diff --git a/tools/modern-tests/apps/react-router/.meteor/versions b/tools/e2e-tests/apps/react-router/.meteor/versions similarity index 100% rename from tools/modern-tests/apps/react-router/.meteor/versions rename to tools/e2e-tests/apps/react-router/.meteor/versions diff --git a/tools/modern-tests/apps/react-router/.meteorignore b/tools/e2e-tests/apps/react-router/.meteorignore similarity index 100% rename from tools/modern-tests/apps/react-router/.meteorignore rename to tools/e2e-tests/apps/react-router/.meteorignore diff --git a/tools/modern-tests/apps/react-router/babel.config.js b/tools/e2e-tests/apps/react-router/babel.config.js similarity index 100% rename from tools/modern-tests/apps/react-router/babel.config.js rename to tools/e2e-tests/apps/react-router/babel.config.js diff --git a/tools/modern-tests/apps/react-router/client/client.app-test.js b/tools/e2e-tests/apps/react-router/client/client.app-test.js similarity index 100% rename from tools/modern-tests/apps/react-router/client/client.app-test.js rename to tools/e2e-tests/apps/react-router/client/client.app-test.js diff --git a/tools/modern-tests/apps/react-router/client/main.css b/tools/e2e-tests/apps/react-router/client/main.css similarity index 100% rename from tools/modern-tests/apps/react-router/client/main.css rename to tools/e2e-tests/apps/react-router/client/main.css diff --git a/tools/modern-tests/apps/react-router/client/main.html b/tools/e2e-tests/apps/react-router/client/main.html similarity index 100% rename from tools/modern-tests/apps/react-router/client/main.html rename to tools/e2e-tests/apps/react-router/client/main.html diff --git a/tools/modern-tests/apps/react-router/client/main.jsx b/tools/e2e-tests/apps/react-router/client/main.jsx similarity index 100% rename from tools/modern-tests/apps/react-router/client/main.jsx rename to tools/e2e-tests/apps/react-router/client/main.jsx diff --git a/tools/modern-tests/apps/react-router/important.app-test.js b/tools/e2e-tests/apps/react-router/important.app-test.js similarity index 100% rename from tools/modern-tests/apps/react-router/important.app-test.js rename to tools/e2e-tests/apps/react-router/important.app-test.js diff --git a/tools/modern-tests/apps/react-router/imports/api/links.js b/tools/e2e-tests/apps/react-router/imports/api/links.js similarity index 100% rename from tools/modern-tests/apps/react-router/imports/api/links.js rename to tools/e2e-tests/apps/react-router/imports/api/links.js diff --git a/tools/modern-tests/apps/react-router/imports/helpers/alias.js b/tools/e2e-tests/apps/react-router/imports/helpers/alias.js similarity index 100% rename from tools/modern-tests/apps/react-router/imports/helpers/alias.js rename to tools/e2e-tests/apps/react-router/imports/helpers/alias.js diff --git a/tools/modern-tests/apps/react-router/imports/ui/App.jsx b/tools/e2e-tests/apps/react-router/imports/ui/App.jsx similarity index 100% rename from tools/modern-tests/apps/react-router/imports/ui/App.jsx rename to tools/e2e-tests/apps/react-router/imports/ui/App.jsx diff --git a/tools/modern-tests/apps/react-router/imports/ui/Global.less b/tools/e2e-tests/apps/react-router/imports/ui/Global.less similarity index 100% rename from tools/modern-tests/apps/react-router/imports/ui/Global.less rename to tools/e2e-tests/apps/react-router/imports/ui/Global.less diff --git a/tools/modern-tests/apps/react-router/imports/ui/Hello.jsx b/tools/e2e-tests/apps/react-router/imports/ui/Hello.jsx similarity index 100% rename from tools/modern-tests/apps/react-router/imports/ui/Hello.jsx rename to tools/e2e-tests/apps/react-router/imports/ui/Hello.jsx diff --git a/tools/modern-tests/apps/react-router/imports/ui/Home.jsx b/tools/e2e-tests/apps/react-router/imports/ui/Home.jsx similarity index 100% rename from tools/modern-tests/apps/react-router/imports/ui/Home.jsx rename to tools/e2e-tests/apps/react-router/imports/ui/Home.jsx diff --git a/tools/modern-tests/apps/react-router/imports/ui/Info.jsx b/tools/e2e-tests/apps/react-router/imports/ui/Info.jsx similarity index 100% rename from tools/modern-tests/apps/react-router/imports/ui/Info.jsx rename to tools/e2e-tests/apps/react-router/imports/ui/Info.jsx diff --git a/tools/modern-tests/apps/react-router/imports/ui/NotFound.jsx b/tools/e2e-tests/apps/react-router/imports/ui/NotFound.jsx similarity index 100% rename from tools/modern-tests/apps/react-router/imports/ui/NotFound.jsx rename to tools/e2e-tests/apps/react-router/imports/ui/NotFound.jsx diff --git a/tools/modern-tests/apps/react-router/my-packages/custom-package/custom-package.js b/tools/e2e-tests/apps/react-router/my-packages/custom-package/custom-package.js similarity index 100% rename from tools/modern-tests/apps/react-router/my-packages/custom-package/custom-package.js rename to tools/e2e-tests/apps/react-router/my-packages/custom-package/custom-package.js diff --git a/tools/modern-tests/apps/react-router/my-packages/custom-package/package.js b/tools/e2e-tests/apps/react-router/my-packages/custom-package/package.js similarity index 100% rename from tools/modern-tests/apps/react-router/my-packages/custom-package/package.js rename to tools/e2e-tests/apps/react-router/my-packages/custom-package/package.js diff --git a/tools/modern-tests/apps/react-router/package.json b/tools/e2e-tests/apps/react-router/package.json similarity index 100% rename from tools/modern-tests/apps/react-router/package.json rename to tools/e2e-tests/apps/react-router/package.json diff --git a/tools/modern-tests/apps/react-router/packages/default-package/default-package.js b/tools/e2e-tests/apps/react-router/packages/default-package/default-package.js similarity index 100% rename from tools/modern-tests/apps/react-router/packages/default-package/default-package.js rename to tools/e2e-tests/apps/react-router/packages/default-package/default-package.js diff --git a/tools/modern-tests/apps/react-router/packages/default-package/package.js b/tools/e2e-tests/apps/react-router/packages/default-package/package.js similarity index 100% rename from tools/modern-tests/apps/react-router/packages/default-package/package.js rename to tools/e2e-tests/apps/react-router/packages/default-package/package.js diff --git a/tools/modern-tests/apps/react-router/plugins/CustomConsoleLogPlugin.js b/tools/e2e-tests/apps/react-router/plugins/CustomConsoleLogPlugin.js similarity index 100% rename from tools/modern-tests/apps/react-router/plugins/CustomConsoleLogPlugin.js rename to tools/e2e-tests/apps/react-router/plugins/CustomConsoleLogPlugin.js diff --git a/tools/modern-tests/apps/react-router/public/1x1.png b/tools/e2e-tests/apps/react-router/public/1x1.png similarity index 100% rename from tools/modern-tests/apps/react-router/public/1x1.png rename to tools/e2e-tests/apps/react-router/public/1x1.png diff --git a/tools/modern-tests/apps/react-router/public/docs/text.md b/tools/e2e-tests/apps/react-router/public/docs/text.md similarity index 100% rename from tools/modern-tests/apps/react-router/public/docs/text.md rename to tools/e2e-tests/apps/react-router/public/docs/text.md diff --git a/tools/modern-tests/apps/react-router/public/images/1x1.png b/tools/e2e-tests/apps/react-router/public/images/1x1.png similarity index 100% rename from tools/modern-tests/apps/react-router/public/images/1x1.png rename to tools/e2e-tests/apps/react-router/public/images/1x1.png diff --git a/tools/modern-tests/apps/react-router/react-router-wxyz-ignored/ignore.app-test.js b/tools/e2e-tests/apps/react-router/react-router-wxyz-ignored/ignore.app-test.js similarity index 100% rename from tools/modern-tests/apps/react-router/react-router-wxyz-ignored/ignore.app-test.js rename to tools/e2e-tests/apps/react-router/react-router-wxyz-ignored/ignore.app-test.js diff --git a/tools/modern-tests/apps/react-router/rspack.config.js b/tools/e2e-tests/apps/react-router/rspack.config.js similarity index 100% rename from tools/modern-tests/apps/react-router/rspack.config.js rename to tools/e2e-tests/apps/react-router/rspack.config.js diff --git a/tools/modern-tests/apps/react-router/rspack.config.override.js b/tools/e2e-tests/apps/react-router/rspack.config.override.js similarity index 100% rename from tools/modern-tests/apps/react-router/rspack.config.override.js rename to tools/e2e-tests/apps/react-router/rspack.config.override.js diff --git a/tools/modern-tests/apps/react-router/server/browser-tests/browser.app-test.js b/tools/e2e-tests/apps/react-router/server/browser-tests/browser.app-test.js similarity index 100% rename from tools/modern-tests/apps/react-router/server/browser-tests/browser.app-test.js rename to tools/e2e-tests/apps/react-router/server/browser-tests/browser.app-test.js diff --git a/tools/modern-tests/apps/react-router/server/main.js b/tools/e2e-tests/apps/react-router/server/main.js similarity index 100% rename from tools/modern-tests/apps/react-router/server/main.js rename to tools/e2e-tests/apps/react-router/server/main.js diff --git a/tools/modern-tests/apps/react-router/server/resolve-extensions/first.jsx b/tools/e2e-tests/apps/react-router/server/resolve-extensions/first.jsx similarity index 100% rename from tools/modern-tests/apps/react-router/server/resolve-extensions/first.jsx rename to tools/e2e-tests/apps/react-router/server/resolve-extensions/first.jsx diff --git a/tools/modern-tests/apps/react-router/server/resolve-extensions/first.tsx b/tools/e2e-tests/apps/react-router/server/resolve-extensions/first.tsx similarity index 100% rename from tools/modern-tests/apps/react-router/server/resolve-extensions/first.tsx rename to tools/e2e-tests/apps/react-router/server/resolve-extensions/first.tsx diff --git a/tools/modern-tests/apps/react-router/server/server.app-test.js b/tools/e2e-tests/apps/react-router/server/server.app-test.js similarity index 100% rename from tools/modern-tests/apps/react-router/server/server.app-test.js rename to tools/e2e-tests/apps/react-router/server/server.app-test.js diff --git a/tools/modern-tests/apps/react-router/server/ts/helpers.ts b/tools/e2e-tests/apps/react-router/server/ts/helpers.ts similarity index 100% rename from tools/modern-tests/apps/react-router/server/ts/helpers.ts rename to tools/e2e-tests/apps/react-router/server/ts/helpers.ts diff --git a/tools/modern-tests/apps/react-router/styles/module.css b/tools/e2e-tests/apps/react-router/styles/module.css similarity index 100% rename from tools/modern-tests/apps/react-router/styles/module.css rename to tools/e2e-tests/apps/react-router/styles/module.css diff --git a/tools/modern-tests/apps/react-router/tests/ignored/file-to-ignored.app-test.js b/tools/e2e-tests/apps/react-router/tests/ignored/file-to-ignored.app-test.js similarity index 100% rename from tools/modern-tests/apps/react-router/tests/ignored/file-to-ignored.app-test.js rename to tools/e2e-tests/apps/react-router/tests/ignored/file-to-ignored.app-test.js diff --git a/tools/modern-tests/apps/react-router/tests/ignored/folder-to-ignored/ignore.app-test.js b/tools/e2e-tests/apps/react-router/tests/ignored/folder-to-ignored/ignore.app-test.js similarity index 100% rename from tools/modern-tests/apps/react-router/tests/ignored/folder-to-ignored/ignore.app-test.js rename to tools/e2e-tests/apps/react-router/tests/ignored/folder-to-ignored/ignore.app-test.js diff --git a/tools/modern-tests/apps/react-router/tests/ignored/integration/test-ignored.app-test.js b/tools/e2e-tests/apps/react-router/tests/ignored/integration/test-ignored.app-test.js similarity index 100% rename from tools/modern-tests/apps/react-router/tests/ignored/integration/test-ignored.app-test.js rename to tools/e2e-tests/apps/react-router/tests/ignored/integration/test-ignored.app-test.js diff --git a/tools/modern-tests/apps/react-router/tests/ignored/prefix-test-ignored/ignore.app-test.js b/tools/e2e-tests/apps/react-router/tests/ignored/prefix-test-ignored/ignore.app-test.js similarity index 100% rename from tools/modern-tests/apps/react-router/tests/ignored/prefix-test-ignored/ignore.app-test.js rename to tools/e2e-tests/apps/react-router/tests/ignored/prefix-test-ignored/ignore.app-test.js diff --git a/tools/modern-tests/apps/react-router/tests/ignored/react-router-wxyz-ignored/ignore.app-test.js b/tools/e2e-tests/apps/react-router/tests/ignored/react-router-wxyz-ignored/ignore.app-test.js similarity index 100% rename from tools/modern-tests/apps/react-router/tests/ignored/react-router-wxyz-ignored/ignore.app-test.js rename to tools/e2e-tests/apps/react-router/tests/ignored/react-router-wxyz-ignored/ignore.app-test.js diff --git a/tools/modern-tests/apps/react-router/tests/ignored/some-test-ignored-suffix/ignore.app-test.js b/tools/e2e-tests/apps/react-router/tests/ignored/some-test-ignored-suffix/ignore.app-test.js similarity index 100% rename from tools/modern-tests/apps/react-router/tests/ignored/some-test-ignored-suffix/ignore.app-test.js rename to tools/e2e-tests/apps/react-router/tests/ignored/some-test-ignored-suffix/ignore.app-test.js diff --git a/tools/modern-tests/apps/react-router/tests/ignored/some/nested/glob-ignored/ignore.app-test.js b/tools/e2e-tests/apps/react-router/tests/ignored/some/nested/glob-ignored/ignore.app-test.js similarity index 100% rename from tools/modern-tests/apps/react-router/tests/ignored/some/nested/glob-ignored/ignore.app-test.js rename to tools/e2e-tests/apps/react-router/tests/ignored/some/nested/glob-ignored/ignore.app-test.js diff --git a/tools/modern-tests/apps/react-router/tests/ignored/some/unit/test-ignored.app-test.js b/tools/e2e-tests/apps/react-router/tests/ignored/some/unit/test-ignored.app-test.js similarity index 100% rename from tools/modern-tests/apps/react-router/tests/ignored/some/unit/test-ignored.app-test.js rename to tools/e2e-tests/apps/react-router/tests/ignored/some/unit/test-ignored.app-test.js diff --git a/tools/modern-tests/apps/react-router/tests/ignored/specific-file-ignored.app-test.js b/tools/e2e-tests/apps/react-router/tests/ignored/specific-file-ignored.app-test.js similarity index 100% rename from tools/modern-tests/apps/react-router/tests/ignored/specific-file-ignored.app-test.js rename to tools/e2e-tests/apps/react-router/tests/ignored/specific-file-ignored.app-test.js diff --git a/tools/modern-tests/apps/react-router/tests/ignored/test-example-ignored.app-test.js b/tools/e2e-tests/apps/react-router/tests/ignored/test-example-ignored.app-test.js similarity index 100% rename from tools/modern-tests/apps/react-router/tests/ignored/test-example-ignored.app-test.js rename to tools/e2e-tests/apps/react-router/tests/ignored/test-example-ignored.app-test.js diff --git a/tools/modern-tests/apps/react-router/tests/ignored/test.temp-ignored.app-test.js b/tools/e2e-tests/apps/react-router/tests/ignored/test.temp-ignored.app-test.js similarity index 100% rename from tools/modern-tests/apps/react-router/tests/ignored/test.temp-ignored.app-test.js rename to tools/e2e-tests/apps/react-router/tests/ignored/test.temp-ignored.app-test.js diff --git a/tools/modern-tests/apps/react-router/tests/main.app-test.js b/tools/e2e-tests/apps/react-router/tests/main.app-test.js similarity index 100% rename from tools/modern-tests/apps/react-router/tests/main.app-test.js rename to tools/e2e-tests/apps/react-router/tests/main.app-test.js diff --git a/tools/modern-tests/apps/react/.gitignore b/tools/e2e-tests/apps/react/.gitignore similarity index 100% rename from tools/modern-tests/apps/react/.gitignore rename to tools/e2e-tests/apps/react/.gitignore diff --git a/tools/modern-tests/apps/react/.meteor/.gitignore b/tools/e2e-tests/apps/react/.meteor/.gitignore similarity index 100% rename from tools/modern-tests/apps/react/.meteor/.gitignore rename to tools/e2e-tests/apps/react/.meteor/.gitignore diff --git a/tools/modern-tests/apps/react/.meteor/.id b/tools/e2e-tests/apps/react/.meteor/.id similarity index 100% rename from tools/modern-tests/apps/react/.meteor/.id rename to tools/e2e-tests/apps/react/.meteor/.id diff --git a/tools/modern-tests/apps/react/.meteor/packages b/tools/e2e-tests/apps/react/.meteor/packages similarity index 100% rename from tools/modern-tests/apps/react/.meteor/packages rename to tools/e2e-tests/apps/react/.meteor/packages diff --git a/tools/modern-tests/apps/react/.meteor/platforms b/tools/e2e-tests/apps/react/.meteor/platforms similarity index 100% rename from tools/modern-tests/apps/react/.meteor/platforms rename to tools/e2e-tests/apps/react/.meteor/platforms diff --git a/tools/modern-tests/apps/react/.meteor/release b/tools/e2e-tests/apps/react/.meteor/release similarity index 100% rename from tools/modern-tests/apps/react/.meteor/release rename to tools/e2e-tests/apps/react/.meteor/release diff --git a/tools/modern-tests/apps/react/.meteor/versions b/tools/e2e-tests/apps/react/.meteor/versions similarity index 100% rename from tools/modern-tests/apps/react/.meteor/versions rename to tools/e2e-tests/apps/react/.meteor/versions diff --git a/tools/modern-tests/apps/react/client/main.html b/tools/e2e-tests/apps/react/client/main.html similarity index 100% rename from tools/modern-tests/apps/react/client/main.html rename to tools/e2e-tests/apps/react/client/main.html diff --git a/tools/modern-tests/apps/react/client/main.jsx b/tools/e2e-tests/apps/react/client/main.jsx similarity index 100% rename from tools/modern-tests/apps/react/client/main.jsx rename to tools/e2e-tests/apps/react/client/main.jsx diff --git a/tools/modern-tests/apps/react/imports/api/links.js b/tools/e2e-tests/apps/react/imports/api/links.js similarity index 100% rename from tools/modern-tests/apps/react/imports/api/links.js rename to tools/e2e-tests/apps/react/imports/api/links.js diff --git a/tools/modern-tests/apps/react/imports/ui/App.jsx b/tools/e2e-tests/apps/react/imports/ui/App.jsx similarity index 100% rename from tools/modern-tests/apps/react/imports/ui/App.jsx rename to tools/e2e-tests/apps/react/imports/ui/App.jsx diff --git a/tools/modern-tests/apps/react/imports/ui/Hello.jsx b/tools/e2e-tests/apps/react/imports/ui/Hello.jsx similarity index 100% rename from tools/modern-tests/apps/react/imports/ui/Hello.jsx rename to tools/e2e-tests/apps/react/imports/ui/Hello.jsx diff --git a/tools/modern-tests/apps/react/imports/ui/Info.jsx b/tools/e2e-tests/apps/react/imports/ui/Info.jsx similarity index 100% rename from tools/modern-tests/apps/react/imports/ui/Info.jsx rename to tools/e2e-tests/apps/react/imports/ui/Info.jsx diff --git a/tools/modern-tests/apps/react/imports/ui/images/1x1-js.png b/tools/e2e-tests/apps/react/imports/ui/images/1x1-js.png similarity index 100% rename from tools/modern-tests/apps/react/imports/ui/images/1x1-js.png rename to tools/e2e-tests/apps/react/imports/ui/images/1x1-js.png diff --git a/tools/modern-tests/apps/react/imports/ui/main.css b/tools/e2e-tests/apps/react/imports/ui/main.css similarity index 100% rename from tools/modern-tests/apps/react/imports/ui/main.css rename to tools/e2e-tests/apps/react/imports/ui/main.css diff --git a/tools/modern-tests/apps/react/package.json b/tools/e2e-tests/apps/react/package.json similarity index 100% rename from tools/modern-tests/apps/react/package.json rename to tools/e2e-tests/apps/react/package.json diff --git a/tools/modern-tests/apps/react/plugins/CustomConsoleLogPlugin.js b/tools/e2e-tests/apps/react/plugins/CustomConsoleLogPlugin.js similarity index 100% rename from tools/modern-tests/apps/react/plugins/CustomConsoleLogPlugin.js rename to tools/e2e-tests/apps/react/plugins/CustomConsoleLogPlugin.js diff --git a/tools/modern-tests/apps/react/public/1x1-css.png b/tools/e2e-tests/apps/react/public/1x1-css.png similarity index 100% rename from tools/modern-tests/apps/react/public/1x1-css.png rename to tools/e2e-tests/apps/react/public/1x1-css.png diff --git a/tools/modern-tests/apps/react/public/1x1-public.jpg b/tools/e2e-tests/apps/react/public/1x1-public.jpg similarity index 100% rename from tools/modern-tests/apps/react/public/1x1-public.jpg rename to tools/e2e-tests/apps/react/public/1x1-public.jpg diff --git a/tools/modern-tests/apps/react/rspack.config.cjs b/tools/e2e-tests/apps/react/rspack.config.cjs similarity index 100% rename from tools/modern-tests/apps/react/rspack.config.cjs rename to tools/e2e-tests/apps/react/rspack.config.cjs diff --git a/tools/modern-tests/apps/react/server/main.js b/tools/e2e-tests/apps/react/server/main.js similarity index 100% rename from tools/modern-tests/apps/react/server/main.js rename to tools/e2e-tests/apps/react/server/main.js diff --git a/tools/modern-tests/apps/react/tests/main.js b/tools/e2e-tests/apps/react/tests/main.js similarity index 100% rename from tools/modern-tests/apps/react/tests/main.js rename to tools/e2e-tests/apps/react/tests/main.js diff --git a/tools/modern-tests/apps/server-only/.meteor/.gitignore b/tools/e2e-tests/apps/server-only/.meteor/.gitignore similarity index 100% rename from tools/modern-tests/apps/server-only/.meteor/.gitignore rename to tools/e2e-tests/apps/server-only/.meteor/.gitignore diff --git a/tools/modern-tests/apps/server-only/.meteor/.id b/tools/e2e-tests/apps/server-only/.meteor/.id similarity index 100% rename from tools/modern-tests/apps/server-only/.meteor/.id rename to tools/e2e-tests/apps/server-only/.meteor/.id diff --git a/tools/modern-tests/apps/server-only/.meteor/packages b/tools/e2e-tests/apps/server-only/.meteor/packages similarity index 100% rename from tools/modern-tests/apps/server-only/.meteor/packages rename to tools/e2e-tests/apps/server-only/.meteor/packages diff --git a/tools/modern-tests/apps/server-only/.meteor/platforms b/tools/e2e-tests/apps/server-only/.meteor/platforms similarity index 100% rename from tools/modern-tests/apps/server-only/.meteor/platforms rename to tools/e2e-tests/apps/server-only/.meteor/platforms diff --git a/tools/modern-tests/apps/server-only/.meteor/release b/tools/e2e-tests/apps/server-only/.meteor/release similarity index 100% rename from tools/modern-tests/apps/server-only/.meteor/release rename to tools/e2e-tests/apps/server-only/.meteor/release diff --git a/tools/modern-tests/apps/server-only/.meteor/versions b/tools/e2e-tests/apps/server-only/.meteor/versions similarity index 100% rename from tools/modern-tests/apps/server-only/.meteor/versions rename to tools/e2e-tests/apps/server-only/.meteor/versions diff --git a/tools/modern-tests/apps/server-only/package.json b/tools/e2e-tests/apps/server-only/package.json similarity index 100% rename from tools/modern-tests/apps/server-only/package.json rename to tools/e2e-tests/apps/server-only/package.json diff --git a/tools/modern-tests/apps/server-only/rspack.config.js b/tools/e2e-tests/apps/server-only/rspack.config.js similarity index 100% rename from tools/modern-tests/apps/server-only/rspack.config.js rename to tools/e2e-tests/apps/server-only/rspack.config.js diff --git a/tools/modern-tests/apps/server-only/server/main.js b/tools/e2e-tests/apps/server-only/server/main.js similarity index 100% rename from tools/modern-tests/apps/server-only/server/main.js rename to tools/e2e-tests/apps/server-only/server/main.js diff --git a/tools/modern-tests/apps/solid/.gitignore b/tools/e2e-tests/apps/solid/.gitignore similarity index 100% rename from tools/modern-tests/apps/solid/.gitignore rename to tools/e2e-tests/apps/solid/.gitignore diff --git a/tools/modern-tests/apps/solid/.meteor/.gitignore b/tools/e2e-tests/apps/solid/.meteor/.gitignore similarity index 100% rename from tools/modern-tests/apps/solid/.meteor/.gitignore rename to tools/e2e-tests/apps/solid/.meteor/.gitignore diff --git a/tools/modern-tests/apps/solid/.meteor/.id b/tools/e2e-tests/apps/solid/.meteor/.id similarity index 100% rename from tools/modern-tests/apps/solid/.meteor/.id rename to tools/e2e-tests/apps/solid/.meteor/.id diff --git a/tools/modern-tests/apps/solid/.meteor/packages b/tools/e2e-tests/apps/solid/.meteor/packages similarity index 100% rename from tools/modern-tests/apps/solid/.meteor/packages rename to tools/e2e-tests/apps/solid/.meteor/packages diff --git a/tools/modern-tests/apps/solid/.meteor/platforms b/tools/e2e-tests/apps/solid/.meteor/platforms similarity index 100% rename from tools/modern-tests/apps/solid/.meteor/platforms rename to tools/e2e-tests/apps/solid/.meteor/platforms diff --git a/tools/modern-tests/apps/solid/.meteor/release b/tools/e2e-tests/apps/solid/.meteor/release similarity index 100% rename from tools/modern-tests/apps/solid/.meteor/release rename to tools/e2e-tests/apps/solid/.meteor/release diff --git a/tools/modern-tests/apps/solid/.meteor/versions b/tools/e2e-tests/apps/solid/.meteor/versions similarity index 100% rename from tools/modern-tests/apps/solid/.meteor/versions rename to tools/e2e-tests/apps/solid/.meteor/versions diff --git a/tools/modern-tests/apps/solid/client/main.html b/tools/e2e-tests/apps/solid/client/main.html similarity index 100% rename from tools/modern-tests/apps/solid/client/main.html rename to tools/e2e-tests/apps/solid/client/main.html diff --git a/tools/modern-tests/apps/solid/client/main.js b/tools/e2e-tests/apps/solid/client/main.js similarity index 100% rename from tools/modern-tests/apps/solid/client/main.js rename to tools/e2e-tests/apps/solid/client/main.js diff --git a/tools/modern-tests/apps/solid/imports/api/links.js b/tools/e2e-tests/apps/solid/imports/api/links.js similarity index 100% rename from tools/modern-tests/apps/solid/imports/api/links.js rename to tools/e2e-tests/apps/solid/imports/api/links.js diff --git a/tools/modern-tests/apps/solid/imports/ui/App.jsx b/tools/e2e-tests/apps/solid/imports/ui/App.jsx similarity index 100% rename from tools/modern-tests/apps/solid/imports/ui/App.jsx rename to tools/e2e-tests/apps/solid/imports/ui/App.jsx diff --git a/tools/modern-tests/apps/solid/imports/ui/Hello.jsx b/tools/e2e-tests/apps/solid/imports/ui/Hello.jsx similarity index 100% rename from tools/modern-tests/apps/solid/imports/ui/Hello.jsx rename to tools/e2e-tests/apps/solid/imports/ui/Hello.jsx diff --git a/tools/modern-tests/apps/solid/imports/ui/Info.jsx b/tools/e2e-tests/apps/solid/imports/ui/Info.jsx similarity index 100% rename from tools/modern-tests/apps/solid/imports/ui/Info.jsx rename to tools/e2e-tests/apps/solid/imports/ui/Info.jsx diff --git a/tools/modern-tests/apps/solid/imports/ui/main.css b/tools/e2e-tests/apps/solid/imports/ui/main.css similarity index 100% rename from tools/modern-tests/apps/solid/imports/ui/main.css rename to tools/e2e-tests/apps/solid/imports/ui/main.css diff --git a/tools/modern-tests/apps/solid/imports/ui/main.jsx b/tools/e2e-tests/apps/solid/imports/ui/main.jsx similarity index 100% rename from tools/modern-tests/apps/solid/imports/ui/main.jsx rename to tools/e2e-tests/apps/solid/imports/ui/main.jsx diff --git a/tools/modern-tests/apps/solid/package.json b/tools/e2e-tests/apps/solid/package.json similarity index 100% rename from tools/modern-tests/apps/solid/package.json rename to tools/e2e-tests/apps/solid/package.json diff --git a/tools/modern-tests/apps/solid/rspack.config.js b/tools/e2e-tests/apps/solid/rspack.config.js similarity index 100% rename from tools/modern-tests/apps/solid/rspack.config.js rename to tools/e2e-tests/apps/solid/rspack.config.js diff --git a/tools/modern-tests/apps/solid/server/main.js b/tools/e2e-tests/apps/solid/server/main.js similarity index 100% rename from tools/modern-tests/apps/solid/server/main.js rename to tools/e2e-tests/apps/solid/server/main.js diff --git a/tools/modern-tests/apps/solid/tests/main.js b/tools/e2e-tests/apps/solid/tests/main.js similarity index 100% rename from tools/modern-tests/apps/solid/tests/main.js rename to tools/e2e-tests/apps/solid/tests/main.js diff --git a/tools/modern-tests/apps/svelte/.gitignore b/tools/e2e-tests/apps/svelte/.gitignore similarity index 100% rename from tools/modern-tests/apps/svelte/.gitignore rename to tools/e2e-tests/apps/svelte/.gitignore diff --git a/tools/modern-tests/apps/svelte/.meteor/.gitignore b/tools/e2e-tests/apps/svelte/.meteor/.gitignore similarity index 100% rename from tools/modern-tests/apps/svelte/.meteor/.gitignore rename to tools/e2e-tests/apps/svelte/.meteor/.gitignore diff --git a/tools/modern-tests/apps/svelte/.meteor/.id b/tools/e2e-tests/apps/svelte/.meteor/.id similarity index 100% rename from tools/modern-tests/apps/svelte/.meteor/.id rename to tools/e2e-tests/apps/svelte/.meteor/.id diff --git a/tools/modern-tests/apps/svelte/.meteor/packages b/tools/e2e-tests/apps/svelte/.meteor/packages similarity index 100% rename from tools/modern-tests/apps/svelte/.meteor/packages rename to tools/e2e-tests/apps/svelte/.meteor/packages diff --git a/tools/modern-tests/apps/svelte/.meteor/platforms b/tools/e2e-tests/apps/svelte/.meteor/platforms similarity index 100% rename from tools/modern-tests/apps/svelte/.meteor/platforms rename to tools/e2e-tests/apps/svelte/.meteor/platforms diff --git a/tools/modern-tests/apps/svelte/.meteor/release b/tools/e2e-tests/apps/svelte/.meteor/release similarity index 100% rename from tools/modern-tests/apps/svelte/.meteor/release rename to tools/e2e-tests/apps/svelte/.meteor/release diff --git a/tools/modern-tests/apps/svelte/.meteor/versions b/tools/e2e-tests/apps/svelte/.meteor/versions similarity index 100% rename from tools/modern-tests/apps/svelte/.meteor/versions rename to tools/e2e-tests/apps/svelte/.meteor/versions diff --git a/tools/modern-tests/apps/svelte/client/main.css b/tools/e2e-tests/apps/svelte/client/main.css similarity index 100% rename from tools/modern-tests/apps/svelte/client/main.css rename to tools/e2e-tests/apps/svelte/client/main.css diff --git a/tools/modern-tests/apps/svelte/client/main.html b/tools/e2e-tests/apps/svelte/client/main.html similarity index 100% rename from tools/modern-tests/apps/svelte/client/main.html rename to tools/e2e-tests/apps/svelte/client/main.html diff --git a/tools/modern-tests/apps/svelte/client/main.js b/tools/e2e-tests/apps/svelte/client/main.js similarity index 100% rename from tools/modern-tests/apps/svelte/client/main.js rename to tools/e2e-tests/apps/svelte/client/main.js diff --git a/tools/modern-tests/apps/svelte/imports/api/links.js b/tools/e2e-tests/apps/svelte/imports/api/links.js similarity index 100% rename from tools/modern-tests/apps/svelte/imports/api/links.js rename to tools/e2e-tests/apps/svelte/imports/api/links.js diff --git a/tools/modern-tests/apps/svelte/imports/ui/App.svelte b/tools/e2e-tests/apps/svelte/imports/ui/App.svelte similarity index 100% rename from tools/modern-tests/apps/svelte/imports/ui/App.svelte rename to tools/e2e-tests/apps/svelte/imports/ui/App.svelte diff --git a/tools/modern-tests/apps/svelte/package.json b/tools/e2e-tests/apps/svelte/package.json similarity index 100% rename from tools/modern-tests/apps/svelte/package.json rename to tools/e2e-tests/apps/svelte/package.json diff --git a/tools/modern-tests/apps/svelte/rspack.config.js b/tools/e2e-tests/apps/svelte/rspack.config.js similarity index 100% rename from tools/modern-tests/apps/svelte/rspack.config.js rename to tools/e2e-tests/apps/svelte/rspack.config.js diff --git a/tools/modern-tests/apps/svelte/server/main.js b/tools/e2e-tests/apps/svelte/server/main.js similarity index 100% rename from tools/modern-tests/apps/svelte/server/main.js rename to tools/e2e-tests/apps/svelte/server/main.js diff --git a/tools/modern-tests/apps/svelte/tests/main.js b/tools/e2e-tests/apps/svelte/tests/main.js similarity index 100% rename from tools/modern-tests/apps/svelte/tests/main.js rename to tools/e2e-tests/apps/svelte/tests/main.js diff --git a/tools/modern-tests/apps/svelte/tsconfig.json b/tools/e2e-tests/apps/svelte/tsconfig.json similarity index 100% rename from tools/modern-tests/apps/svelte/tsconfig.json rename to tools/e2e-tests/apps/svelte/tsconfig.json diff --git a/tools/modern-tests/apps/typescript/.gitignore b/tools/e2e-tests/apps/typescript/.gitignore similarity index 100% rename from tools/modern-tests/apps/typescript/.gitignore rename to tools/e2e-tests/apps/typescript/.gitignore diff --git a/tools/modern-tests/apps/typescript/.meteor/.gitignore b/tools/e2e-tests/apps/typescript/.meteor/.gitignore similarity index 100% rename from tools/modern-tests/apps/typescript/.meteor/.gitignore rename to tools/e2e-tests/apps/typescript/.meteor/.gitignore diff --git a/tools/modern-tests/apps/typescript/.meteor/.id b/tools/e2e-tests/apps/typescript/.meteor/.id similarity index 100% rename from tools/modern-tests/apps/typescript/.meteor/.id rename to tools/e2e-tests/apps/typescript/.meteor/.id diff --git a/tools/modern-tests/apps/typescript/.meteor/packages b/tools/e2e-tests/apps/typescript/.meteor/packages similarity index 100% rename from tools/modern-tests/apps/typescript/.meteor/packages rename to tools/e2e-tests/apps/typescript/.meteor/packages diff --git a/tools/modern-tests/apps/typescript/.meteor/platforms b/tools/e2e-tests/apps/typescript/.meteor/platforms similarity index 100% rename from tools/modern-tests/apps/typescript/.meteor/platforms rename to tools/e2e-tests/apps/typescript/.meteor/platforms diff --git a/tools/modern-tests/apps/typescript/.meteor/release b/tools/e2e-tests/apps/typescript/.meteor/release similarity index 100% rename from tools/modern-tests/apps/typescript/.meteor/release rename to tools/e2e-tests/apps/typescript/.meteor/release diff --git a/tools/modern-tests/apps/typescript/.meteor/versions b/tools/e2e-tests/apps/typescript/.meteor/versions similarity index 100% rename from tools/modern-tests/apps/typescript/.meteor/versions rename to tools/e2e-tests/apps/typescript/.meteor/versions diff --git a/tools/modern-tests/apps/typescript/client/main.html b/tools/e2e-tests/apps/typescript/client/main.html similarity index 100% rename from tools/modern-tests/apps/typescript/client/main.html rename to tools/e2e-tests/apps/typescript/client/main.html diff --git a/tools/modern-tests/apps/typescript/client/main.scss b/tools/e2e-tests/apps/typescript/client/main.scss similarity index 100% rename from tools/modern-tests/apps/typescript/client/main.scss rename to tools/e2e-tests/apps/typescript/client/main.scss diff --git a/tools/modern-tests/apps/typescript/client/main.tsx b/tools/e2e-tests/apps/typescript/client/main.tsx similarity index 100% rename from tools/modern-tests/apps/typescript/client/main.tsx rename to tools/e2e-tests/apps/typescript/client/main.tsx diff --git a/tools/modern-tests/apps/typescript/imports/api/links.ts b/tools/e2e-tests/apps/typescript/imports/api/links.ts similarity index 100% rename from tools/modern-tests/apps/typescript/imports/api/links.ts rename to tools/e2e-tests/apps/typescript/imports/api/links.ts diff --git a/tools/modern-tests/apps/typescript/imports/ui/App.tsx b/tools/e2e-tests/apps/typescript/imports/ui/App.tsx similarity index 100% rename from tools/modern-tests/apps/typescript/imports/ui/App.tsx rename to tools/e2e-tests/apps/typescript/imports/ui/App.tsx diff --git a/tools/modern-tests/apps/typescript/imports/ui/Global.scss b/tools/e2e-tests/apps/typescript/imports/ui/Global.scss similarity index 100% rename from tools/modern-tests/apps/typescript/imports/ui/Global.scss rename to tools/e2e-tests/apps/typescript/imports/ui/Global.scss diff --git a/tools/modern-tests/apps/typescript/imports/ui/Hello.tsx b/tools/e2e-tests/apps/typescript/imports/ui/Hello.tsx similarity index 100% rename from tools/modern-tests/apps/typescript/imports/ui/Hello.tsx rename to tools/e2e-tests/apps/typescript/imports/ui/Hello.tsx diff --git a/tools/modern-tests/apps/typescript/imports/ui/Info.tsx b/tools/e2e-tests/apps/typescript/imports/ui/Info.tsx similarity index 100% rename from tools/modern-tests/apps/typescript/imports/ui/Info.tsx rename to tools/e2e-tests/apps/typescript/imports/ui/Info.tsx diff --git a/tools/modern-tests/apps/typescript/package.json b/tools/e2e-tests/apps/typescript/package.json similarity index 100% rename from tools/modern-tests/apps/typescript/package.json rename to tools/e2e-tests/apps/typescript/package.json diff --git a/tools/modern-tests/apps/typescript/rspack.config.ts b/tools/e2e-tests/apps/typescript/rspack.config.ts similarity index 100% rename from tools/modern-tests/apps/typescript/rspack.config.ts rename to tools/e2e-tests/apps/typescript/rspack.config.ts diff --git a/tools/modern-tests/apps/typescript/server/main.ts b/tools/e2e-tests/apps/typescript/server/main.ts similarity index 100% rename from tools/modern-tests/apps/typescript/server/main.ts rename to tools/e2e-tests/apps/typescript/server/main.ts diff --git a/tools/modern-tests/apps/typescript/tests/client.ts b/tools/e2e-tests/apps/typescript/tests/client.ts similarity index 100% rename from tools/modern-tests/apps/typescript/tests/client.ts rename to tools/e2e-tests/apps/typescript/tests/client.ts diff --git a/tools/modern-tests/apps/typescript/tests/server.ts b/tools/e2e-tests/apps/typescript/tests/server.ts similarity index 100% rename from tools/modern-tests/apps/typescript/tests/server.ts rename to tools/e2e-tests/apps/typescript/tests/server.ts diff --git a/tools/modern-tests/apps/typescript/tsconfig.json b/tools/e2e-tests/apps/typescript/tsconfig.json similarity index 100% rename from tools/modern-tests/apps/typescript/tsconfig.json rename to tools/e2e-tests/apps/typescript/tsconfig.json diff --git a/tools/modern-tests/apps/vue/.gitignore b/tools/e2e-tests/apps/vue/.gitignore similarity index 100% rename from tools/modern-tests/apps/vue/.gitignore rename to tools/e2e-tests/apps/vue/.gitignore diff --git a/tools/modern-tests/apps/vue/.meteor/.gitignore b/tools/e2e-tests/apps/vue/.meteor/.gitignore similarity index 100% rename from tools/modern-tests/apps/vue/.meteor/.gitignore rename to tools/e2e-tests/apps/vue/.meteor/.gitignore diff --git a/tools/modern-tests/apps/vue/.meteor/.id b/tools/e2e-tests/apps/vue/.meteor/.id similarity index 100% rename from tools/modern-tests/apps/vue/.meteor/.id rename to tools/e2e-tests/apps/vue/.meteor/.id diff --git a/tools/modern-tests/apps/vue/.meteor/packages b/tools/e2e-tests/apps/vue/.meteor/packages similarity index 100% rename from tools/modern-tests/apps/vue/.meteor/packages rename to tools/e2e-tests/apps/vue/.meteor/packages diff --git a/tools/modern-tests/apps/vue/.meteor/platforms b/tools/e2e-tests/apps/vue/.meteor/platforms similarity index 100% rename from tools/modern-tests/apps/vue/.meteor/platforms rename to tools/e2e-tests/apps/vue/.meteor/platforms diff --git a/tools/modern-tests/apps/vue/.meteor/release b/tools/e2e-tests/apps/vue/.meteor/release similarity index 100% rename from tools/modern-tests/apps/vue/.meteor/release rename to tools/e2e-tests/apps/vue/.meteor/release diff --git a/tools/modern-tests/apps/vue/.meteor/versions b/tools/e2e-tests/apps/vue/.meteor/versions similarity index 100% rename from tools/modern-tests/apps/vue/.meteor/versions rename to tools/e2e-tests/apps/vue/.meteor/versions diff --git a/tools/modern-tests/apps/vue/.meteorignore b/tools/e2e-tests/apps/vue/.meteorignore similarity index 100% rename from tools/modern-tests/apps/vue/.meteorignore rename to tools/e2e-tests/apps/vue/.meteorignore diff --git a/tools/modern-tests/apps/vue/client/main.css b/tools/e2e-tests/apps/vue/client/main.css similarity index 100% rename from tools/modern-tests/apps/vue/client/main.css rename to tools/e2e-tests/apps/vue/client/main.css diff --git a/tools/modern-tests/apps/vue/client/main.html b/tools/e2e-tests/apps/vue/client/main.html similarity index 100% rename from tools/modern-tests/apps/vue/client/main.html rename to tools/e2e-tests/apps/vue/client/main.html diff --git a/tools/modern-tests/apps/vue/client/main.js b/tools/e2e-tests/apps/vue/client/main.js similarity index 100% rename from tools/modern-tests/apps/vue/client/main.js rename to tools/e2e-tests/apps/vue/client/main.js diff --git a/tools/modern-tests/apps/vue/client/meteor.css b/tools/e2e-tests/apps/vue/client/meteor.css similarity index 100% rename from tools/modern-tests/apps/vue/client/meteor.css rename to tools/e2e-tests/apps/vue/client/meteor.css diff --git a/tools/modern-tests/apps/vue/imports/api/links.js b/tools/e2e-tests/apps/vue/imports/api/links.js similarity index 100% rename from tools/modern-tests/apps/vue/imports/api/links.js rename to tools/e2e-tests/apps/vue/imports/api/links.js diff --git a/tools/modern-tests/apps/vue/imports/ui/App.vue b/tools/e2e-tests/apps/vue/imports/ui/App.vue similarity index 100% rename from tools/modern-tests/apps/vue/imports/ui/App.vue rename to tools/e2e-tests/apps/vue/imports/ui/App.vue diff --git a/tools/modern-tests/apps/vue/imports/ui/components/AppMenu.vue b/tools/e2e-tests/apps/vue/imports/ui/components/AppMenu.vue similarity index 100% rename from tools/modern-tests/apps/vue/imports/ui/components/AppMenu.vue rename to tools/e2e-tests/apps/vue/imports/ui/components/AppMenu.vue diff --git a/tools/modern-tests/apps/vue/imports/ui/components/Hello.vue b/tools/e2e-tests/apps/vue/imports/ui/components/Hello.vue similarity index 100% rename from tools/modern-tests/apps/vue/imports/ui/components/Hello.vue rename to tools/e2e-tests/apps/vue/imports/ui/components/Hello.vue diff --git a/tools/modern-tests/apps/vue/imports/ui/components/Info.vue b/tools/e2e-tests/apps/vue/imports/ui/components/Info.vue similarity index 100% rename from tools/modern-tests/apps/vue/imports/ui/components/Info.vue rename to tools/e2e-tests/apps/vue/imports/ui/components/Info.vue diff --git a/tools/modern-tests/apps/vue/imports/ui/main.css b/tools/e2e-tests/apps/vue/imports/ui/main.css similarity index 100% rename from tools/modern-tests/apps/vue/imports/ui/main.css rename to tools/e2e-tests/apps/vue/imports/ui/main.css diff --git a/tools/modern-tests/apps/vue/imports/ui/main.js b/tools/e2e-tests/apps/vue/imports/ui/main.js similarity index 100% rename from tools/modern-tests/apps/vue/imports/ui/main.js rename to tools/e2e-tests/apps/vue/imports/ui/main.js diff --git a/tools/modern-tests/apps/vue/imports/ui/router.js b/tools/e2e-tests/apps/vue/imports/ui/router.js similarity index 100% rename from tools/modern-tests/apps/vue/imports/ui/router.js rename to tools/e2e-tests/apps/vue/imports/ui/router.js diff --git a/tools/modern-tests/apps/vue/imports/ui/views/About.vue b/tools/e2e-tests/apps/vue/imports/ui/views/About.vue similarity index 100% rename from tools/modern-tests/apps/vue/imports/ui/views/About.vue rename to tools/e2e-tests/apps/vue/imports/ui/views/About.vue diff --git a/tools/modern-tests/apps/vue/imports/ui/views/Home.vue b/tools/e2e-tests/apps/vue/imports/ui/views/Home.vue similarity index 100% rename from tools/modern-tests/apps/vue/imports/ui/views/Home.vue rename to tools/e2e-tests/apps/vue/imports/ui/views/Home.vue diff --git a/tools/modern-tests/apps/vue/package.json b/tools/e2e-tests/apps/vue/package.json similarity index 100% rename from tools/modern-tests/apps/vue/package.json rename to tools/e2e-tests/apps/vue/package.json diff --git a/tools/modern-tests/apps/vue/postcss.config.js b/tools/e2e-tests/apps/vue/postcss.config.js similarity index 100% rename from tools/modern-tests/apps/vue/postcss.config.js rename to tools/e2e-tests/apps/vue/postcss.config.js diff --git a/tools/modern-tests/apps/vue/rspack.config.js b/tools/e2e-tests/apps/vue/rspack.config.js similarity index 100% rename from tools/modern-tests/apps/vue/rspack.config.js rename to tools/e2e-tests/apps/vue/rspack.config.js diff --git a/tools/modern-tests/apps/vue/server/entry-meteor.js b/tools/e2e-tests/apps/vue/server/entry-meteor.js similarity index 100% rename from tools/modern-tests/apps/vue/server/entry-meteor.js rename to tools/e2e-tests/apps/vue/server/entry-meteor.js diff --git a/tools/modern-tests/apps/vue/server/main.js b/tools/e2e-tests/apps/vue/server/main.js similarity index 100% rename from tools/modern-tests/apps/vue/server/main.js rename to tools/e2e-tests/apps/vue/server/main.js diff --git a/tools/modern-tests/apps/vue/tests/main.js b/tools/e2e-tests/apps/vue/tests/main.js similarity index 100% rename from tools/modern-tests/apps/vue/tests/main.js rename to tools/e2e-tests/apps/vue/tests/main.js diff --git a/tools/modern-tests/assertions.js b/tools/e2e-tests/assertions.js similarity index 100% rename from tools/modern-tests/assertions.js rename to tools/e2e-tests/assertions.js diff --git a/tools/modern-tests/babel.config.js b/tools/e2e-tests/babel.config.js similarity index 100% rename from tools/modern-tests/babel.config.js rename to tools/e2e-tests/babel.config.js diff --git a/tools/modern-tests/babel.test.js b/tools/e2e-tests/babel.test.js similarity index 100% rename from tools/modern-tests/babel.test.js rename to tools/e2e-tests/babel.test.js diff --git a/tools/modern-tests/blaze.test.js b/tools/e2e-tests/blaze.test.js similarity index 100% rename from tools/modern-tests/blaze.test.js rename to tools/e2e-tests/blaze.test.js diff --git a/tools/modern-tests/coffeescript.test.js b/tools/e2e-tests/coffeescript.test.js similarity index 100% rename from tools/modern-tests/coffeescript.test.js rename to tools/e2e-tests/coffeescript.test.js diff --git a/tools/modern-tests/full-blaze.test.js b/tools/e2e-tests/full-blaze.test.js similarity index 100% rename from tools/modern-tests/full-blaze.test.js rename to tools/e2e-tests/full-blaze.test.js diff --git a/tools/modern-tests/helpers.js b/tools/e2e-tests/helpers.js similarity index 100% rename from tools/modern-tests/helpers.js rename to tools/e2e-tests/helpers.js diff --git a/tools/modern-tests/jest.config.js b/tools/e2e-tests/jest.config.js similarity index 100% rename from tools/modern-tests/jest.config.js rename to tools/e2e-tests/jest.config.js diff --git a/tools/modern-tests/jest.setup.js b/tools/e2e-tests/jest.setup.js similarity index 100% rename from tools/modern-tests/jest.setup.js rename to tools/e2e-tests/jest.setup.js diff --git a/tools/modern-tests/monorepo.test.js b/tools/e2e-tests/monorepo.test.js similarity index 100% rename from tools/modern-tests/monorepo.test.js rename to tools/e2e-tests/monorepo.test.js diff --git a/tools/modern-tests/package-lock.json b/tools/e2e-tests/package-lock.json similarity index 99% rename from tools/modern-tests/package-lock.json rename to tools/e2e-tests/package-lock.json index b37ab8042f..1596084af7 100644 --- a/tools/modern-tests/package-lock.json +++ b/tools/e2e-tests/package-lock.json @@ -1,11 +1,11 @@ { - "name": "meteor-modern-tests", + "name": "meteor-e2e-tests", "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "meteor-modern-tests", + "name": "meteor-e2e-tests", "version": "1.0.0", "devDependencies": { "@babel/preset-env": "^7.21.3", diff --git a/tools/modern-tests/package.json b/tools/e2e-tests/package.json similarity index 92% rename from tools/modern-tests/package.json rename to tools/e2e-tests/package.json index 25934c8c5d..1f8dddc8c0 100644 --- a/tools/modern-tests/package.json +++ b/tools/e2e-tests/package.json @@ -1,5 +1,5 @@ { - "name": "meteor-modern-tests", + "name": "meteor-e2e-tests", "version": "1.0.0", "description": "Modern tests for Meteor", "scripts": { diff --git a/tools/modern-tests/react-router.test.js b/tools/e2e-tests/react-router.test.js similarity index 100% rename from tools/modern-tests/react-router.test.js rename to tools/e2e-tests/react-router.test.js diff --git a/tools/modern-tests/react.test.js b/tools/e2e-tests/react.test.js similarity index 100% rename from tools/modern-tests/react.test.js rename to tools/e2e-tests/react.test.js diff --git a/tools/modern-tests/scripts/create-app.js b/tools/e2e-tests/scripts/create-app.js similarity index 92% rename from tools/modern-tests/scripts/create-app.js rename to tools/e2e-tests/scripts/create-app.js index eb3a9abf07..5ffaa5952b 100644 --- a/tools/modern-tests/scripts/create-app.js +++ b/tools/e2e-tests/scripts/create-app.js @@ -4,15 +4,15 @@ * Script to create a Meteor test app for manual testing without automatic cleanup. * * Sources apps from: - * - tools/modern-tests/apps/ (use --app flag) + * - tools/e2e-tests/apps/ (use --app flag) * - meteor create -- (use --skeleton flag) * * Usage: - * npm run create-app:modern -- --app react - * npm run create-app:modern -- --app react --output ./dist/my-react-app - * npm run create-app:modern -- --app monorepo --monorepo - * npm run create-app:modern -- --skeleton react - * npm run create-app:modern -- --skeleton react --output ./my-apps/custom-name + * npm run create-app:e2e -- --app react + * npm run create-app:e2e -- --app react --output ./dist/my-react-app + * npm run create-app:e2e -- --app monorepo --monorepo + * npm run create-app:e2e -- --skeleton react + * npm run create-app:e2e -- --skeleton react --output ./my-apps/custom-name */ const path = require('path'); @@ -21,8 +21,8 @@ const execa = require('execa'); const REPO_ROOT = path.resolve(__dirname, '../../..'); const METEOR_EXECUTABLE = path.join(REPO_ROOT, 'meteor'); -const MODERN_TESTS_DIR = path.join(__dirname, '..'); -const APPS_DIR = path.join(MODERN_TESTS_DIR, 'apps'); +const E2E_TESTS_DIR = path.join(__dirname, '..'); +const APPS_DIR = path.join(E2E_TESTS_DIR, 'apps'); const DEFAULT_OUTPUT_DIR = path.join(REPO_ROOT, 'dist'); // ANSI color helpers @@ -72,10 +72,10 @@ function printHelp() { : '(none found)'; console.log(` -${c.bold}Usage:${c.reset} npm run create-app:modern -- [options] +${c.bold}Usage:${c.reset} npm run create-app:e2e -- [options] ${c.bold}Options:${c.reset} - ${c.cyan}--app${c.reset} Copy an existing app from tools/modern-tests/apps/ + ${c.cyan}--app${c.reset} Copy an existing app from tools/e2e-tests/apps/ ${c.cyan}--skeleton${c.reset} Create a new app via "meteor create --" ${c.cyan}--output${c.reset} Full destination path for the app (default: ./dist/) ${c.cyan}--monorepo${c.reset} Treat the app as a monorepo (runs npm install at both root and app/ levels) @@ -85,11 +85,11 @@ ${c.bold}Options:${c.reset} ${c.bold}Available apps:${c.reset} ${c.green}${availableApps}${c.reset} ${c.bold}Examples:${c.reset} - ${c.dim}npm run create-app:modern -- --app react${c.reset} - ${c.dim}npm run create-app:modern -- --app react --output ./dist/my-react-app${c.reset} - ${c.dim}npm run create-app:modern -- --app monorepo --monorepo${c.reset} - ${c.dim}npm run create-app:modern -- --skeleton react${c.reset} - ${c.dim}npm run create-app:modern -- --skeleton react --output ./my-apps/custom-name${c.reset} + ${c.dim}npm run create-app:e2e -- --app react${c.reset} + ${c.dim}npm run create-app:e2e -- --app react --output ./dist/my-react-app${c.reset} + ${c.dim}npm run create-app:e2e -- --app monorepo --monorepo${c.reset} + ${c.dim}npm run create-app:e2e -- --skeleton react${c.reset} + ${c.dim}npm run create-app:e2e -- --skeleton react --output ./my-apps/custom-name${c.reset} `); } @@ -171,14 +171,14 @@ function parseEnvVars(code) { * environment variables that the tests set (so the manually created app * behaves the same way). * - * For --app : reads tools/modern-tests/.test.js (whole file) - * For --skeleton : reads tools/modern-tests/skeleton.test.js and + * For --app : reads tools/e2e-tests/.test.js (whole file) + * For --skeleton : reads tools/e2e-tests/skeleton.test.js and * scopes to the testMeteorSkeleton({ skeletonName: '' }) block. */ function extractEnvVarsFromTestFile(sourceName, isApp) { const testFile = isApp - ? path.join(MODERN_TESTS_DIR, `${sourceName}.test.js`) - : path.join(MODERN_TESTS_DIR, 'skeleton.test.js'); + ? path.join(E2E_TESTS_DIR, `${sourceName}.test.js`) + : path.join(E2E_TESTS_DIR, 'skeleton.test.js'); if (!fs.existsSync(testFile)) return {}; @@ -317,7 +317,7 @@ async function setupFromApp(appName, destDir, { isMonorepo = false, force = fals if (!fs.existsSync(sourceDir)) { const available = fs.readdirSync(APPS_DIR).join(', '); throw new Error( - `App '${appName}' not found in tools/modern-tests/apps/\nAvailable apps: ${available}` + `App '${appName}' not found in tools/e2e-tests/apps/\nAvailable apps: ${available}` ); } diff --git a/tools/modern-tests/server-only.test.js b/tools/e2e-tests/server-only.test.js similarity index 100% rename from tools/modern-tests/server-only.test.js rename to tools/e2e-tests/server-only.test.js diff --git a/tools/modern-tests/skeleton.test.js b/tools/e2e-tests/skeleton.test.js similarity index 100% rename from tools/modern-tests/skeleton.test.js rename to tools/e2e-tests/skeleton.test.js diff --git a/tools/modern-tests/solid.test.js b/tools/e2e-tests/solid.test.js similarity index 100% rename from tools/modern-tests/solid.test.js rename to tools/e2e-tests/solid.test.js diff --git a/tools/modern-tests/svelte.test.js b/tools/e2e-tests/svelte.test.js similarity index 100% rename from tools/modern-tests/svelte.test.js rename to tools/e2e-tests/svelte.test.js diff --git a/tools/modern-tests/test-helpers.js b/tools/e2e-tests/test-helpers.js similarity index 100% rename from tools/modern-tests/test-helpers.js rename to tools/e2e-tests/test-helpers.js diff --git a/tools/modern-tests/typescript.test.js b/tools/e2e-tests/typescript.test.js similarity index 100% rename from tools/modern-tests/typescript.test.js rename to tools/e2e-tests/typescript.test.js diff --git a/tools/modern-tests/vue.test.js b/tools/e2e-tests/vue.test.js similarity index 100% rename from tools/modern-tests/vue.test.js rename to tools/e2e-tests/vue.test.js From 24b36475d357156780eba5e5bdb76dc570ed8792 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 3 Mar 2026 14:53:15 +0100 Subject: [PATCH 132/166] add e2e-coverage skill and documentation for maintaining E2E test coverage report --- .github/skills/e2e-coverage/SKILL.md | 50 +++++ AGENTS.md | 1 + CLAUDE.md | 1 + dev/modern-tools/rspack/E2E_COVERAGE.md | 272 ++++++++++++++++++++++++ 4 files changed, 324 insertions(+) create mode 100644 .github/skills/e2e-coverage/SKILL.md create mode 100644 dev/modern-tools/rspack/E2E_COVERAGE.md diff --git a/.github/skills/e2e-coverage/SKILL.md b/.github/skills/e2e-coverage/SKILL.md new file mode 100644 index 0000000000..c43b00ba2e --- /dev/null +++ b/.github/skills/e2e-coverage/SKILL.md @@ -0,0 +1,50 @@ +--- +name: e2e-coverage +description: Use when adding, modifying, or reviewing E2E test apps/skeletons to keep the test coverage report up to date. +--- + +# E2E Test Coverage Report + +Guidelines for maintaining `dev/modern-tools/rspack/E2E_COVERAGE.md` — a single-page report of what every E2E app and skeleton tests. + +## When to Update + +| Trigger | Action | +|---------|--------| +| New app added to `apps/` | Add a subsection under **Apps** with a coverage table | +| New skeleton added to `skeleton.test.js` | Add a row to the **Skeletons** table | +| New npm package imported for compatibility testing | Add an entry under **NPM Package Compatibility** with the package name, file, and reason | +| New custom assertion added to a test file | Add a row to that app's coverage table | +| New feature tested across multiple apps | Add a row to the **Feature Coverage Matrix** | +| App or skeleton removed | Remove its entries from all sections | + +## Report Structure + +The report has five sections, in this order: + +1. **Test Lifecycle** — the phases every app/skeleton goes through (init, run, prod, test, test once, build) and what default assertions apply +2. **Apps** — one subsection per `apps//` with a short description and a `| What is covered | Phase |` table +3. **Skeletons** — single table with one row per skeleton (`| Skeleton | Port | Language | Extra coverage |`) +4. **NPM Package Compatibility** — grouped by app, each entry has the package name, file path, and why it's included (ESM-only, native bindings, subpath exports, etc.) +5. **Feature Coverage Matrix** — cross-reference table (`| Feature | Apps | Skeletons |`) showing where each capability is tested + +## How to Gather Information + +For each app or skeleton, check these sources: + +| Source | What to look for | +|--------|-----------------| +| `.test.js` | Test helper used, options (`env`, `configFile`, `buildDir`, `testFullApp`, `checkBundleFilePaths`), all `customAssertions` callbacks and what they assert | +| `skeleton.test.js` | The `testMeteorSkeleton({ skeletonName: '' })` block for that skeleton | +| `apps//server/main.js` | npm imports with comments explaining why (ESM-only, native bindings, etc.) | +| `apps//imports/` | Shared code with special imports (`node:` protocol, JSX packages) | +| `apps//rspack.config.*` | Custom config features (`compileWithRspack`, `compileWithMeteor`, `disablePlugins`, custom rules) | +| `apps//package.json` | Dependencies that exist solely for compatibility testing | + +## Writing Guidelines + +- Keep descriptions short — one line per table row +- Use the phase names from the lifecycle table: Init, Run, Prod, Test, Build, All +- For npm packages, always state the **reason** (what module format issue it validates) +- Don't duplicate info between the per-app table and the feature matrix — the app table has detail, the matrix has the cross-reference +- When an env var is set in a test file, note it as `All (env prefix)` in the phase column diff --git a/AGENTS.md b/AGENTS.md index db127840d8..b7a4303ae5 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -41,6 +41,7 @@ Load these for detailed context on specific topics: | [testing](.github/skills/testing/SKILL.md) | Writing tests, debugging failures, test infrastructure | | [packages](.github/skills/packages/SKILL.md) | Finding packages by feature, understanding dependencies | | [modern-tools](.github/skills/modern-tools/SKILL.md) | tools-core utilities, rspack, modern integrations | +| [e2e-coverage](.github/skills/e2e-coverage/SKILL.md) | Updating the E2E test coverage report when apps/skeletons change | | [ai-context](.github/skills/ai-context/SKILL.md) | Creating, updating, or maintaining AI documentation files | ## Package Domains diff --git a/CLAUDE.md b/CLAUDE.md index cfdb92933f..87a39b6cd2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -11,4 +11,5 @@ Load these for detailed context on specific topics: | [testing](.github/skills/testing/SKILL.md) | Writing tests, debugging failures, test infrastructure | | [packages](.github/skills/packages/SKILL.md) | Finding packages by feature, understanding dependencies | | [modern-tools](.github/skills/modern-tools/SKILL.md) | tools-core utilities, rspack, modern integrations | +| [e2e-coverage](.github/skills/e2e-coverage/SKILL.md) | Updating the E2E test coverage report when apps/skeletons change | | [ai-context](.github/skills/ai-context/SKILL.md) | Creating, updating, or maintaining AI documentation files | diff --git a/dev/modern-tools/rspack/E2E_COVERAGE.md b/dev/modern-tools/rspack/E2E_COVERAGE.md new file mode 100644 index 0000000000..4fc5473720 --- /dev/null +++ b/dev/modern-tools/rspack/E2E_COVERAGE.md @@ -0,0 +1,272 @@ +# E2E Test Coverage + +> To update this report, follow the [e2e-coverage skill](/.github/skills/e2e-coverage/SKILL.md). + +End-to-end tests using Jest + Playwright that verify Meteor apps with the Rspack bundler across frameworks, build modes, and features. + +## Test Lifecycle + +Every app and skeleton goes through these phases (unless skipped): + +| Phase | What it does | +|-------|-------------| +| **Init** | Copies app, installs deps, adds rspack, generates config | +| **Run (dev)** | `meteor run` — asserts build artifacts, app loads, client/server hot rebuild | +| **Run (prod)** | `meteor run --production` — same checks in production mode | +| **Test** | `meteor test` — runs mocha test driver, verifies test rebuild | +| **Test once** | `meteor test --once` — runs tests to completion, checks exit code | +| **Build** | `meteor build` — verifies bundle structure (main.js, programs/server, web.browser, web.browser.legacy) | + +Default assertions on every run phase: build artifacts exist, page title matches, body styles render, `__rspack__` script tag is present. + +--- + +## Apps + +Each app lives in `apps//` and has a matching `.test.js`. + +### react + +Core React integration with custom Meteor local directory. + +| What is covered | Phase | +|----------------|-------| +| Custom `METEOR_LOCAL_DIR` (`.meteor/local-custom`) | All (env prefix) | +| Custom build dir (`_build-local-custom`) created | Run | +| `.gitignore` updated with custom local dir | Run | +| React + JSX environment detection | Run, Prod, Test, Build | +| Image assets load (generated + public + background) | Run, Prod | +| `Meteor.disablePlugins` suppresses rspack plugins | Run, Prod, Test, Build | +| Custom rspack config (`rspack.config.cjs`) | All | +| HMR works in dev, disabled in prod | Run, Prod | + +### react-router + +Full-featured React Router app with custom packages, Less, and advanced rspack config. + +| What is covered | Phase | +|----------------|-------| +| `METEOR_PACKAGE_DIRS` custom packages dir | All (env prefix) | +| `babel-plugin-react-compiler` integration | Init, Prod, Build | +| Compiler output cached in dev (babel.config.js) | Run | +| 404 page routing (renders "Page Not Found") | Run, Prod | +| Less stylesheet support (`white-space: break-spaces`) | Run, Prod | +| Meteor modules config styles (`align-content: center`) | Run, Prod | +| Custom HTML meta tags (`theme-color`) | Run, Prod | +| Default + custom package loading | Run | +| `resolve.extensions` loading (`.jsx`) | Run | +| `rspack.config.override.js` custom plugin loading | Run, Test, Build | +| React + TSX environment detection | Run, Prod, Test, Build | +| Full-app test mode (`--full-app`) | Test | +| Static assets in bundle (png, md) | Build | +| HMR works in dev, disabled in prod | Run, Prod | + +### blaze + +Blaze templating engine integration. + +| What is covered | Phase | +|----------------|-------| +| Blaze environment detection (`isBlazeEnabled`) | Run, Prod, Test, Build | +| HMR disabled (incompatible with Blaze) | Run, Prod | + +### full-blaze + +Full Blaze app (with `imports/` structure for tests). + +| What is covered | Phase | +|----------------|-------| +| Blaze environment detection | Run, Prod, Test, Build | +| `imports/api/` test path structure | Test | +| HMR disabled (incompatible with Blaze) | Run, Prod | + +### typescript + +TypeScript with SCSS, type checking, and `.ts` rspack config. + +| What is covered | Phase | +|----------------|-------| +| TypeScript rspack config (`rspack.config.ts`) | All | +| Custom build dir (`build`) | All | +| SCSS styles support (`white-space: break-spaces`) | Run, Prod | +| TypeScript + TSX environment detection | Run, Prod, Test, Build | +| `TsCheckerRspackPlugin` type checking (no errors) | Run | +| `.meteor/local/types` directory generated | Run | +| Separate client/server test files | Test | +| CI: removes TsCheckerRspackPlugin (resource limits) | Init | +| HMR works in dev, disabled in prod | Run, Prod | + +### babel + +Babel transpilation with custom module rules and `.mjs` rspack config. + +| What is covered | Phase | +|----------------|-------| +| Custom rspack config (`rspack.config.mjs`) | All | +| Module rules for `.js`/`.jsx` files | Run, Prod, Test, Build | +| Module rules for `.tsx`/`.ts`/`.mts`/`.cts`/`.mjs`/`.cjs` | Run, Prod, Test, Build | +| Module rules for `.graphql`/`.gql` files | Run, Prod, Test, Build | +| Default rules negated (custom rules override) | Run, Prod, Test, Build | +| HMR works in dev, disabled in prod | Run, Prod | + +### coffeescript + +CoffeeScript language support. + +| What is covered | Phase | +|----------------|-------| +| `.coffee` file compilation (client + server + test) | All | +| CoffeeScript-specific conditional syntax | Run, Prod | +| HMR works in dev, disabled in prod | Run, Prod | + +### vue + +Vue.js framework with Tailwind CSS. + +| What is covered | Phase | +|----------------|-------| +| Vue single-file components | All | +| Tailwind CSS styles (`.p-8` padding) | Run, Prod | +| HMR works in dev, disabled in prod | Run, Prod | + +### solid + +SolidJS framework integration. + +| What is covered | Phase | +|----------------|-------| +| SolidJS compilation and rendering | All | +| HMR works in dev, disabled in prod | Run, Prod | + +### svelte + +Svelte framework integration. + +| What is covered | Phase | +|----------------|-------| +| Svelte compilation and rendering | All | +| HMR works in dev, disabled in prod | Run, Prod | + +### monorepo + +Monorepo structure with app in subdirectory. + +| What is covered | Phase | +|----------------|-------| +| Monorepo layout (`app/` subdirectory) | All | +| Custom rspack config (`rspack.config.cjs`) | All | +| `rspack.config.override.cjs` custom plugin loading | Run, Test, Build | +| Static assets in bundle (png, md) | Build | +| HMR works in dev, disabled in prod | Run, Prod | + +### server-only + +Server-only app (no client entry point). + +| What is covered | Phase | +|----------------|-------| +| No client bundle (client skipped) | All | +| No client tests (test client skipped) | Test | +| Server entry loads (`server/main.js loaded`) | Run | + +--- + +## Skeletons + +Tested via `skeleton.test.js` using `meteor create --`. Each skeleton verifies: app creation, dev run, production run, test once, and build. + +| Skeleton | Port | Language | Extra coverage | +|----------|------|----------|----------------| +| angular | 3213 | TypeScript | | +| apollo | 3201 | JSX | | +| babel | 3212 | JSX | | +| bare | 3219 | JS | No title/style checks, no client tests | +| blaze | 3202 | JS | | +| chakra-ui | 3203 | JSX | No body style checks (custom UI library) | +| coffeescript | 3211 | CoffeeScript | | +| full | 3204 | JS | `imports/api/` test structure | +| react | 3205 | JSX | Custom body styles (Inter font, padding) | +| solid | 3206 | JS | | +| svelte | 3207 | JS | | +| tailwind | 3208 | TypeScript | Tailwind `bg-gray-100` styles (dev + prod color formats) | +| typescript | 3209 | TypeScript | CI: removes TsCheckerRspackPlugin | +| vue | 3210 | JS | | + +--- + +## NPM Package Compatibility + +Several apps import specific npm packages to verify that Meteor + Rspack handles different module formats and edge cases without errors. The app boots successfully only if these imports resolve correctly. + +### react-router (`apps/react-router/server/main.js`) + +| Package | Reason | +|---------|--------| +| `s3mini` | ESM-only package (no CJS fallback) | +| `@modelcontextprotocol/sdk/client/streamableHttp.js` | ESM subpath export (deep path into ESM package) | +| `bcrypt` | Native Node.js bindings (compiled C++ addon) | +| `puppeteer` | Large ESM-compatible package with complex dependency tree (`server/browser-tests/browser.app-test.js`) | + +### monorepo (`apps/monorepo/app/`) + +| Package | File | Reason | +|---------|------|--------| +| `pino` + `pino-pretty` | `server/main.js` | ESM-first logger; `pino-pretty` uses `thread-stream` which has worker file resolution issues — needs `Meteor.compileWithMeteor(["thread-stream"])` in rspack config | +| `grubba-rpc` | `server/main.js` | Untranspiled npm dependency — needs `Meteor.compileWithRspack(["grubba-rpc"])` to compile it through rspack | +| `node:buffer` | `imports/api/links.js` | Node.js built-in via `node:` protocol in shared client/server code — must be ignored on client without errors | +| `@react-email/components` | `imports/emails/TestEmail.jsx` | JSX-heavy ESM package with many subpath exports | + +### babel (`apps/babel/server/apollo.js`) + +| Package | Reason | +|---------|--------| +| `@apollo/server` | ESM-first GraphQL server | +| `@apollo/server/express4` | ESM subpath export (middleware from deep path) | +| `graphql` | Peer dependency, dual CJS/ESM package | + +### typescript (`apps/typescript/rspack.config.ts`) + +| Package | Reason | +|---------|--------| +| `node:module` (`createRequire`) | Node.js built-in in a `.ts` config file — tests CJS interop via `createRequire(import.meta.url)` in an ESM context | + +--- + +## Feature Coverage Matrix + +Where each feature is tested across apps and skeletons. + +| Feature | Apps | Skeletons | +|---------|------|-----------| +| HMR (dev) | react, react-router, babel, coffeescript, vue, solid, svelte, monorepo, typescript | | +| HMR disabled (prod) | all apps with HMR | | +| HMR incompatible | blaze, full-blaze | | +| Custom rspack config | react (.cjs), react-router, babel (.mjs), monorepo (.cjs), typescript (.ts) | | +| Config override file | react-router, monorepo | | +| Custom build dir | react, typescript | | +| Custom env vars | react (METEOR_LOCAL_DIR), react-router (METEOR_PACKAGE_DIRS) | | +| Static asset bundling | react-router, monorepo | | +| Less styles | react-router | | +| SCSS styles | typescript | | +| Tailwind CSS | vue | tailwind | +| Image asset loading | react | | +| 404 routing | react-router | | +| Meta tags | react-router | | +| Babel compiler plugin | react-router | | +| TypeScript type checking | typescript | | +| Meteor.disablePlugins | react | | +| Custom package dirs | react-router | | +| CoffeeScript compilation | coffeescript | coffeescript | +| Server-only (no client) | server-only | | +| Monorepo layout | monorepo | | +| Full-app test mode | react-router | | +| Module rules override | babel | | +| Skeleton creation | | all 14 skeletons | +| Body style assertions | | react, tailwind (custom); most others (default) | +| Custom .gitignore entries | react | | +| ESM-only packages | react-router, monorepo, babel | | +| ESM subpath exports | react-router, babel | | +| Native bindings (C++ addon) | react-router | | +| `node:` protocol imports | monorepo, typescript | | +| Untranspiled npm deps (`compileWithRspack`) | monorepo | | +| Worker resolution (`compileWithMeteor`) | monorepo | | From 9d9914c9e3249964e9ed4f92600bb62782b62156 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 3 Mar 2026 14:59:29 +0100 Subject: [PATCH 133/166] clarify e2e test infrastructure locations in documentation --- dev/modern-tools/rspack/E2E_COVERAGE.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dev/modern-tools/rspack/E2E_COVERAGE.md b/dev/modern-tools/rspack/E2E_COVERAGE.md index 4fc5473720..9330e45e56 100644 --- a/dev/modern-tools/rspack/E2E_COVERAGE.md +++ b/dev/modern-tools/rspack/E2E_COVERAGE.md @@ -4,6 +4,8 @@ End-to-end tests using Jest + Playwright that verify Meteor apps with the Rspack bundler across frameworks, build modes, and features. +Test infrastructure lives in `tools/e2e-tests/`, with app fixtures in `tools/e2e-tests/apps/` and matching test files at `tools/e2e-tests/.test.js`. + ## Test Lifecycle Every app and skeleton goes through these phases (unless skipped): From 70096df87a204eab8246cb81b60c3b1cc8695720 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 3 Mar 2026 15:11:27 +0100 Subject: [PATCH 134/166] force refetch of submodules in Windows self-tests --- .github/workflows/windows-selftest.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/windows-selftest.yml b/.github/workflows/windows-selftest.yml index 696cad4691..97a22ad4cb 100644 --- a/.github/workflows/windows-selftest.yml +++ b/.github/workflows/windows-selftest.yml @@ -64,6 +64,12 @@ jobs: restore-keys: | ${{ runner.os }}-meteor- + - name: Reset submodules (force refetch) + shell: pwsh + run: | + git submodule deinit -f --all + if (Test-Path ".git/modules") { Remove-Item -Recurse -Force ".git/modules" } + - name: Install dependencies shell: pwsh run: | From 77e31b7d684fa94efd4a54fe6340b9725886ea08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 3 Mar 2026 15:21:23 +0100 Subject: [PATCH 135/166] handle environment variable injection correctly in `create-app` script and propagate to all relevant commands --- tools/e2e-tests/scripts/create-app.js | 41 ++++++++++++++++----------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/tools/e2e-tests/scripts/create-app.js b/tools/e2e-tests/scripts/create-app.js index 5ffaa5952b..42d21ec4fb 100644 --- a/tools/e2e-tests/scripts/create-app.js +++ b/tools/e2e-tests/scripts/create-app.js @@ -347,40 +347,44 @@ async function setupFromApp(appName, destDir, { isMonorepo = false, force = fals : path.join(destDir, 'package.json'); const envVars = extractEnvVarsFromTestFile(appName, true); - - if (fs.existsSync(appPackageJsonPath)) { - log.step('Injecting npm scripts into package.json...'); - if (Object.keys(envVars).length > 0) { - log.detail(`env from test file: ${c.magenta}${Object.entries(envVars).map(([k, v]) => `${k}=${v}`).join(' ')}${c.reset}`); - } - await injectNpmScripts(appPackageJsonPath, envVars); + if (Object.keys(envVars).length > 0) { + log.detail(`env from test file: ${c.magenta}${Object.entries(envVars).map(([k, v]) => `${k}=${v}`).join(' ')}${c.reset}`); } const meteorAppDir = isMonorepo ? path.join(destDir, 'app') : destDir; + const execEnv = Object.keys(envVars).length > 0 ? { env: { ...process.env, ...envVars } } : {}; log.step('Adding rspack package...'); await execa(METEOR_EXECUTABLE, ['add', 'rspack'], { cwd: meteorAppDir, stdio: 'inherit', + ...execEnv, }); log.step('Updating Meteor npm dependencies...'); await execa(METEOR_EXECUTABLE, ['update', '--npm'], { cwd: meteorAppDir, stdio: 'inherit', + ...execEnv, }); if (isMonorepo) { log.step('Running meteor npm install at root level...'); - await execa(METEOR_EXECUTABLE, ['npm', 'install'], { cwd: destDir, stdio: 'inherit' }); + await execa(METEOR_EXECUTABLE, ['npm', 'install'], { cwd: destDir, stdio: 'inherit', ...execEnv }); log.step('Running meteor npm install at app level...'); await execa(METEOR_EXECUTABLE, ['npm', 'install'], { cwd: path.join(destDir, 'app'), stdio: 'inherit', + ...execEnv, }); } else { log.step('Running meteor npm install...'); - await execa(METEOR_EXECUTABLE, ['npm', 'install'], { cwd: destDir, stdio: 'inherit' }); + await execa(METEOR_EXECUTABLE, ['npm', 'install'], { cwd: destDir, stdio: 'inherit', ...execEnv }); + } + + if (fs.existsSync(appPackageJsonPath)) { + log.step('Injecting npm scripts into package.json...'); + await injectNpmScripts(appPackageJsonPath, envVars); } return { destDir, appPackageJsonPath }; @@ -409,30 +413,33 @@ async function setupFromSkeleton(skeletonName, destDir, { force = false } = {}) stdio: 'inherit', }); + const appPackageJsonPath = path.join(destDir, 'package.json'); + + const envVars = extractEnvVarsFromTestFile(skeletonName, false); + if (Object.keys(envVars).length > 0) { + log.detail(`env from test file: ${c.magenta}${Object.entries(envVars).map(([k, v]) => `${k}=${v}`).join(' ')}${c.reset}`); + } + const execEnv = Object.keys(envVars).length > 0 ? { env: { ...process.env, ...envVars } } : {}; + log.step('Adding rspack package...'); await execa(METEOR_EXECUTABLE, ['add', 'rspack'], { cwd: destDir, stdio: 'inherit', + ...execEnv, }); log.step('Updating Meteor npm dependencies...'); await execa(METEOR_EXECUTABLE, ['update', '--npm'], { cwd: destDir, stdio: 'inherit', + ...execEnv, }); log.step('Running meteor npm install...'); - await execa(METEOR_EXECUTABLE, ['npm', 'install'], { cwd: destDir, stdio: 'inherit' }); - - const appPackageJsonPath = path.join(destDir, 'package.json'); - - const envVars = extractEnvVarsFromTestFile(skeletonName, false); + await execa(METEOR_EXECUTABLE, ['npm', 'install'], { cwd: destDir, stdio: 'inherit', ...execEnv }); if (fs.existsSync(appPackageJsonPath)) { log.step('Injecting npm scripts into package.json...'); - if (Object.keys(envVars).length > 0) { - log.detail(`env from test file: ${c.magenta}${Object.entries(envVars).map(([k, v]) => `${k}=${v}`).join(' ')}${c.reset}`); - } await injectNpmScripts(appPackageJsonPath, envVars); } From ff500c221c6e98dfff98caec337dbd75c05d69ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 3 Mar 2026 15:26:09 +0100 Subject: [PATCH 136/166] prepend envs to rewritten `meteor` commands in `create-app` script --- tools/e2e-tests/scripts/create-app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/e2e-tests/scripts/create-app.js b/tools/e2e-tests/scripts/create-app.js index 42d21ec4fb..5b877a3999 100644 --- a/tools/e2e-tests/scripts/create-app.js +++ b/tools/e2e-tests/scripts/create-app.js @@ -234,7 +234,7 @@ async function injectNpmScripts(packageJsonPath, envVars = {}) { // Rewrite ALL existing scripts: replace bare `meteor` with the checkout path const scripts = {}; for (const [key, value] of Object.entries(pkg.scripts || {})) { - scripts[key] = rewriteMeteorCmd(value, m); + scripts[key] = `${p}${rewriteMeteorCmd(value, m)}`; } // Add/overwrite canonical scripts using the checkout path From 5811234780342c6dfdfd29d3bbd517210ba5bebb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 3 Mar 2026 16:14:44 +0100 Subject: [PATCH 137/166] add `reset` script to `create-app` scripts for e2e tests --- tools/e2e-tests/scripts/create-app.js | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/e2e-tests/scripts/create-app.js b/tools/e2e-tests/scripts/create-app.js index 5b877a3999..2e534c9e45 100644 --- a/tools/e2e-tests/scripts/create-app.js +++ b/tools/e2e-tests/scripts/create-app.js @@ -242,6 +242,7 @@ async function injectNpmScripts(packageJsonPath, envVars = {}) { scripts['start:prod'] = `${p}${m} run --production`; scripts['build'] = `${p}${m} build ./_build --directory`; scripts['visualize'] = `${p}${m} run --production --extra-packages bundle-visualizer`; + scripts['reset'] = `${p}${m} reset`; if (hasTestModule) { scripts['test'] = `${p}${m} test --once --driver-package meteortesting:mocha`; From 9757003aa7be971f9a261eb6af268216613b1c61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 3 Mar 2026 17:01:39 +0100 Subject: [PATCH 138/166] add `modern` configuration with verbose logging to `meteor` in `create-app` script --- tools/e2e-tests/scripts/create-app.js | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/e2e-tests/scripts/create-app.js b/tools/e2e-tests/scripts/create-app.js index 2e534c9e45..14315eb75c 100644 --- a/tools/e2e-tests/scripts/create-app.js +++ b/tools/e2e-tests/scripts/create-app.js @@ -255,6 +255,7 @@ async function injectNpmScripts(packageJsonPath, envVars = {}) { } pkg.scripts = scripts; + pkg.meteor = { ...meteorConfig, modern: { verbose: true } }; await fs.writeJson(packageJsonPath, pkg, { spaces: 2 }); } From 7371d4933732709fde38504f29f07df053bad6b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 3 Mar 2026 17:02:08 +0100 Subject: [PATCH 139/166] simplify cache directory path generation in `rspack.config.js` --- npm-packages/meteor-rspack/rspack.config.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/npm-packages/meteor-rspack/rspack.config.js b/npm-packages/meteor-rspack/rspack.config.js index a2602dd121..3ce3012a91 100644 --- a/npm-packages/meteor-rspack/rspack.config.js +++ b/npm-packages/meteor-rspack/rspack.config.js @@ -87,9 +87,9 @@ function createCacheStrategy( type: "persistent", storage: { type: "filesystem", - directory: `node_modules/.cache/rspack${ - (buildContext && `-${buildContext}`) || '' - }${(side && `/${side}`) || ''}`, + directory: `node_modules/.cache/rspack/${ + [buildContext, side].filter(Boolean).join('-') || 'default' + }`, }, ...(buildDependencies.length > 0 && { buildDependencies: buildDependencies, From 88a50262eed221dc73bf6df68bf3d816bb77c85b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 3 Mar 2026 17:07:10 +0100 Subject: [PATCH 140/166] ensure `onCompile` is invoked only when `config` is valid and `compilationCount` is greater than 0 --- packages/rspack/lib/processes.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/rspack/lib/processes.js b/packages/rspack/lib/processes.js index c3036251b6..d453c5144f 100644 --- a/packages/rspack/lib/processes.js +++ b/packages/rspack/lib/processes.js @@ -376,7 +376,7 @@ export function startRspackClientServe(options = {}) { if (config && !!config?.devServerUrl) { logHmrServerStarted(config); } - if (config && (config?.compilationCount || 0) > 0) { + if (onCompile && config && (config?.compilationCount || 0) > 0) { onCompile(cleanedData, config); if ( @@ -475,7 +475,7 @@ export function startRspackServerWatch(options = {}) { env: inheritMeteorToolNodeFlags({ ...process.env, ...envs }), onStdout: (data) => { const { cleanedData, config } = parseMeteorRspackOutput(data); - if (config && (config?.compilationCount || 0) > 0) { + if (onCompile && config && (config?.compilationCount || 0) > 0) { onCompile(cleanedData, config); } if (!cleanedData) return; @@ -566,7 +566,7 @@ export async function runRspackBuild({ isClient, isServer, isTest, isTestModule, env: inheritMeteorToolNodeFlags({ ...process.env, ...envs }), onStdout: (data) => { const { cleanedData, config } = parseMeteorRspackOutput(data); - if (onCompile) { + if (onCompile && config && (config?.compilationCount || 0) > 0) { onCompile(cleanedData, config); } if (!cleanedData) return; From 29d6d2f7fd81c72e146e5b5fcf7470c9ae79f1ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 3 Mar 2026 17:07:10 +0100 Subject: [PATCH 141/166] ensure `onCompile` is invoked only when `config` is valid and `compilationCount` is greater than 0 --- packages/rspack/lib/processes.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/rspack/lib/processes.js b/packages/rspack/lib/processes.js index c3036251b6..d453c5144f 100644 --- a/packages/rspack/lib/processes.js +++ b/packages/rspack/lib/processes.js @@ -376,7 +376,7 @@ export function startRspackClientServe(options = {}) { if (config && !!config?.devServerUrl) { logHmrServerStarted(config); } - if (config && (config?.compilationCount || 0) > 0) { + if (onCompile && config && (config?.compilationCount || 0) > 0) { onCompile(cleanedData, config); if ( @@ -475,7 +475,7 @@ export function startRspackServerWatch(options = {}) { env: inheritMeteorToolNodeFlags({ ...process.env, ...envs }), onStdout: (data) => { const { cleanedData, config } = parseMeteorRspackOutput(data); - if (config && (config?.compilationCount || 0) > 0) { + if (onCompile && config && (config?.compilationCount || 0) > 0) { onCompile(cleanedData, config); } if (!cleanedData) return; @@ -566,7 +566,7 @@ export async function runRspackBuild({ isClient, isServer, isTest, isTestModule, env: inheritMeteorToolNodeFlags({ ...process.env, ...envs }), onStdout: (data) => { const { cleanedData, config } = parseMeteorRspackOutput(data); - if (onCompile) { + if (onCompile && config && (config?.compilationCount || 0) > 0) { onCompile(cleanedData, config); } if (!cleanedData) return; From d6d705cfd15585f880adeed818fa0b7148860305 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 3 Mar 2026 17:02:08 +0100 Subject: [PATCH 142/166] simplify cache directory path generation in `rspack.config.js` --- npm-packages/meteor-rspack/rspack.config.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/npm-packages/meteor-rspack/rspack.config.js b/npm-packages/meteor-rspack/rspack.config.js index a2602dd121..3ce3012a91 100644 --- a/npm-packages/meteor-rspack/rspack.config.js +++ b/npm-packages/meteor-rspack/rspack.config.js @@ -87,9 +87,9 @@ function createCacheStrategy( type: "persistent", storage: { type: "filesystem", - directory: `node_modules/.cache/rspack${ - (buildContext && `-${buildContext}`) || '' - }${(side && `/${side}`) || ''}`, + directory: `node_modules/.cache/rspack/${ + [buildContext, side].filter(Boolean).join('-') || 'default' + }`, }, ...(buildDependencies.length > 0 && { buildDependencies: buildDependencies, From 8cf87eafad0e0911ccca68288ec39a541767c1ce Mon Sep 17 00:00:00 2001 From: shanky Date: Wed, 4 Mar 2026 00:26:48 +0530 Subject: [PATCH 143/166] fix: remove module CJS transform to prevent exports.default wrapping --- npm-packages/meteor-rspack/lib/swc.js | 7 +++---- packages/babel-compiler/babel-compiler.js | 9 ++++----- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/npm-packages/meteor-rspack/lib/swc.js b/npm-packages/meteor-rspack/lib/swc.js index 6a3a3ea869..e113ad1626 100644 --- a/npm-packages/meteor-rspack/lib/swc.js +++ b/npm-packages/meteor-rspack/lib/swc.js @@ -22,9 +22,6 @@ function getMeteorAppSwcrc(file = '.swcrc') { }, target: 'es2015', }, - module: { - type: 'commonjs', - }, }); content = result.code; } catch (swcError) { @@ -57,7 +54,9 @@ function getMeteorAppSwcrc(file = '.swcrc') { })() `); const context = vm.createContext({ process }); - return script.runInContext(context); + const result = script.runInContext(context); + // Handle CJS interop wrapper (e.g. { __esModule: true, default: config }) + return result && result.__esModule && result.default ? result.default : result; } else { // For .swcrc and other JSON files, parse as JSON return JSON.parse(fs.readFileSync(filePath, 'utf-8')); diff --git a/packages/babel-compiler/babel-compiler.js b/packages/babel-compiler/babel-compiler.js index 43324e11e5..2fdb836de0 100644 --- a/packages/babel-compiler/babel-compiler.js +++ b/packages/babel-compiler/babel-compiler.js @@ -112,7 +112,7 @@ BCp.initializeMeteorAppSwcrc = function () { let currentLastModifiedConfigTime; if (hasSwcJs || hasSwcTs) { - // For dynamic JS files, first get the resolved configuration + // For dynamic JS/TS files, first get the resolved configuration const resolvedConfig = lastModifiedSwcConfigTime?.includes(`${fileModTime}`) ? lastModifiedSwcConfig || getMeteorAppSwcrc(swcFile) : getMeteorAppSwcrc(swcFile); @@ -1087,9 +1087,6 @@ function getMeteorAppSwcrc(file = '.swcrc') { }, target: 'es2015', }, - module: { - type: 'commonjs', - }, }); content = result.code; } catch (swcError) { @@ -1124,7 +1121,9 @@ function getMeteorAppSwcrc(file = '.swcrc') { })() `); const context = vm.createContext({ process }); - return script.runInContext(context); + const result = script.runInContext(context); + // Handle CJS interop wrapper (e.g. { __esModule: true, default: config }) + return result && result.__esModule && result.default ? result.default : result; } else { // For .swcrc and other JSON files, parse as JSON return JSON.parse(fs.readFileSync(filePath, 'utf-8')); From cee7e411156303ba8d80dafb26a6cf8233c50322 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Wed, 4 Mar 2026 17:28:50 +0100 Subject: [PATCH 144/166] fix merge issues --- npm-packages/meteor-rspack/rspack.config.js | 52 ++++++++++++--------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/npm-packages/meteor-rspack/rspack.config.js b/npm-packages/meteor-rspack/rspack.config.js index f1bf215f3e..0bfb343543 100644 --- a/npm-packages/meteor-rspack/rspack.config.js +++ b/npm-packages/meteor-rspack/rspack.config.js @@ -229,8 +229,24 @@ module.exports = async function (inMeteor = {}, argv = {}) { const projectConfigPath = Meteor.projectConfigPath || path.resolve(projectDir, "rspack.config.js"); + // Determine context for bundles and assets + const meteorLocalDirName = process.env.METEOR_LOCAL_DIR + ? path.basename(process.env.METEOR_LOCAL_DIR.replace(/\\/g, "/")) + : ""; + const buildContext = + Meteor.buildContext || + process.env.RSPACK_BUILD_CONTEXT || + `_build${(meteorLocalDirName && `-${meteorLocalDirName}`) || ""}`; + const assetsContext = + Meteor.assetsContext || + process.env.RSPACK_ASSETS_CONTEXT || + `build-assets${(meteorLocalDirName && `-${meteorLocalDirName}`) || ""}`; + const chunksContext = + Meteor.chunksContext || + process.env.RSPACK_CHUNKS_CONTEXT || + `build-chunks${(meteorLocalDirName && `-${meteorLocalDirName}`) || ""}`; + // Compute build paths before loading user config (needed by Meteor helpers below) - const buildContext = Meteor.buildContext || "_build"; const outputPath = Meteor.outputPath; const outputDir = path.dirname(Meteor.outputPath || ""); Meteor.buildOutputDir = path.resolve(projectDir, buildContext, outputDir); @@ -280,29 +296,12 @@ module.exports = async function (inMeteor = {}, argv = {}) { : !!Meteor.isDevelopment || !initialIsProd; const initialMode = initialIsProd ? "production" : "development"; - // Determine context for bundles and assets - const meteorLocalDirName = process.env.METEOR_LOCAL_DIR - ? path.basename(process.env.METEOR_LOCAL_DIR.replace(/\\/g, '/')) - : ''; - const buildContext = - Meteor.buildContext || - process.env.RSPACK_BUILD_CONTEXT || - `_build${(meteorLocalDirName && `-${meteorLocalDirName}`) || ''}`; - const assetsContext = - Meteor.assetsContext || - process.env.RSPACK_ASSETS_CONTEXT || - `build-assets${(meteorLocalDirName && `-${meteorLocalDirName}`) || ''}`; - const chunksContext = - Meteor.chunksContext || - process.env.RSPACK_CHUNKS_CONTEXT || - `build-chunks${(meteorLocalDirName && `-${meteorLocalDirName}`) || ''}`; - // Initialized with pre-load values so helpers work during the first config load; // reassigned after load once mode is fully resolved. let cacheStrategy = createCacheStrategy( initialMode, - (Meteor.isClient && 'client') || 'server', - { projectConfigPath, configPath, buildContext } + (Meteor.isClient && "client") || "server", + { projectConfigPath, configPath, buildContext } ); let swcConfigRule = createSwcConfig({ isTypescriptEnabled, @@ -641,7 +640,8 @@ module.exports = async function (inMeteor = {}, argv = {}) { onListening(devServer) { if (!devServer) return; const { host, port } = devServer.options; - const protocol = devServer.options.server?.type === "https" ? "https" : "http"; + const protocol = + devServer.options.server?.type === "https" ? "https" : "http"; const devServerUrl = `${protocol}://${host || "localhost"}:${port}`; outputMeteorRspack({ devServerUrl }); }, @@ -781,7 +781,6 @@ module.exports = async function (inMeteor = {}, argv = {}) { } : {}; - // Second pass: re-run only when a mode override was detected, so the user config // can depend on fully-computed Meteor flags and helpers (swcConfigOptions, buildOutputDir, etc.). if (nextUserConfig?.mode || nextOverrideConfig?.mode || isAngularEnabled) { @@ -791,9 +790,13 @@ module.exports = async function (inMeteor = {}, argv = {}) { argv )); } + let statsOverrided = false; let config = isClient ? clientConfig : serverConfig; if (nextUserConfig) { config = mergeSplitOverlap(config, nextUserConfig); + if (nextUserConfig.stats != null) { + statsOverrided = true; + } } config = mergeSplitOverlap(config, angularExpandConfig); @@ -801,6 +804,9 @@ module.exports = async function (inMeteor = {}, argv = {}) { if (nextOverrideConfig) { config = mergeSplitOverlap(config, nextOverrideConfig); + if (nextOverrideConfig.stats != null) { + statsOverrided = true; + } } const shouldDisablePlugins = config?.disablePlugins != null; @@ -812,7 +818,7 @@ module.exports = async function (inMeteor = {}, argv = {}) { delete config["meteor.enablePortableBuild"]; // if (Meteor.isDebug || Meteor.isVerbose) { - console.log("Config:", inspect(config, { depth: null, colors: true })); + console.log("Config:", inspect(config, { depth: null, colors: true })); // } // Check if lazyCompilation is enabled and warn the user From 81982483a98afcaf7edaba062fea91990595d259 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Wed, 4 Mar 2026 17:38:30 +0100 Subject: [PATCH 145/166] document `enablePortableBuild` usage in Rspack integration docs --- .../rspack-bundler-integration.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/v3-docs/docs/about/modern-build-stack/rspack-bundler-integration.md b/v3-docs/docs/about/modern-build-stack/rspack-bundler-integration.md index 50d68f1055..35a34f1295 100644 --- a/v3-docs/docs/about/modern-build-stack/rspack-bundler-integration.md +++ b/v3-docs/docs/about/modern-build-stack/rspack-bundler-integration.md @@ -159,6 +159,7 @@ You can use flags to control the final configuration based on the environment. T | `splitVendorChunk` | function | Splits vendor libraries so they are automatically served from a separate chunk | | `extendSwcConfig` | function | Extends the [SWC loader configuration](https://rspack.rs/guide/features/builtin-swc-loader#options) to apply only to the app code | | `extendConfig` | function | Extends the config by applying merged object configs | +| `enablePortableBuild` | function | Omits `Meteor.isDevelopment` and `Meteor.isProduction` from the bundle, making it portable across environments | Some configurations in the Rspack config are reserved for the Meteor-Rspack setup to work, such as Rspack options inside the `entry` and `output` objects. These will trigger warnings if modified. All other settings can be overridden, giving you the flexibility to make any setup compatible with the modern bundler. @@ -782,6 +783,24 @@ module.exports = defineConfig(Meteor => ({ })); ``` +### Portable Build + +By default, Meteor-Rspack replaces `Meteor.isDevelopment` and `Meteor.isProduction` with static values at build time. This follows modern bundler conventions where `mode: "production"` enables aggressive dead-code elimination, any code inside `if (Meteor.isDevelopment) { ... }` blocks is stripped entirely from production builds. + +This is the recommended default. It produces smaller, more secure bundles by ensuring development-only code never ships to production. `meteor build` benefits directly from this, as the final output is as lean as possible. + +If you need a single build that works across environments (for example, building once and deploying to both staging and production without rebuilding), you can opt in to portable builds. This omits `Meteor.isDevelopment` and `Meteor.isProduction` from compile-time replacement, keeping them as runtime checks instead. + +```js +const { defineConfig } = require('@meteorjs/rspack'); + +module.exports = defineConfig(Meteor => ({ + ...Meteor.enablePortableBuild(), +})); +``` + +Note that this trades build optimization for portability — dead-code elimination for development/production branches will no longer apply, resulting in larger bundles. Other flags like `Meteor.isClient`, `Meteor.isServer`, and `Meteor.isTest` are always replaced at build time, since they depend on the build target. + ### Running Multiple Instances By default, Meteor and Rspack use fixed directories for their build caches (`.meteor/local` and `_build`). If you try to run multiple instances of the same app simultaneously, they may conflict by attempting to write to the same folders. From f5d1c047bbfcda343c26776418395ca72b4483ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Wed, 4 Mar 2026 18:26:42 +0100 Subject: [PATCH 146/166] add meteor reset E2E test coverage --- tools/cli/commands.js | 51 +++++++++----- tools/modern-tests/assertions.js | 37 +++++++++++ tools/modern-tests/test-helpers.js | 103 +++++++++++++++++++++++++++++ tools/tool-env/rspack.js | 47 +++++++++---- 4 files changed, 211 insertions(+), 27 deletions(-) diff --git a/tools/cli/commands.js b/tools/cli/commands.js index 6c26667dd7..88716546f4 100644 --- a/tools/cli/commands.js +++ b/tools/cli/commands.js @@ -1865,6 +1865,15 @@ main.registerCommand({ "MONGO_URL will NOT be reset."); } + // Always clean the default .meteor/local directory to prevent regressions. + // When METEOR_LOCAL_DIR is set, also clean the custom local directory. + const defaultLocalRelative = files.pathJoin('.meteor', 'local'); + const customLocalRelative = process.env.METEOR_LOCAL_DIR || null; + const localDirs = [defaultLocalRelative]; + if (customLocalRelative && customLocalRelative !== defaultLocalRelative) { + localDirs.push(customLocalRelative); + } + const resetMeteorNpmCachePromise = options['skip-cache'] ? Promise.resolve() : files.rm_recursive_async( files.pathJoin(options.appDir, "node_modules", ".cache", "meteor") ); @@ -1879,19 +1888,23 @@ main.registerCommand({ // XXX detect the case where Meteor is running the app, but // MONGO_URL was set, so we don't see a Mongo process var findMongoPort = require('../runners/run-mongo.js').findMongoPort; - var isRunning = !! await findMongoPort(files.pathJoin(options.appDir, ".meteor", "local", "db")); - if (isRunning) { - Console.error("reset: Meteor is running."); - Console.error(); - Console.error( - "This command does not work while Meteor is running your application.", - "Exit the running Meteor development server."); - return 1; + // Check all local dirs for a running Mongo instance + for (const localRelative of localDirs) { + const localDir = files.pathResolve(options.appDir, localRelative); + var isRunning = !! await findMongoPort(files.pathJoin(localDir, "db")); + if (isRunning) { + Console.error("reset: Meteor is running."); + Console.error(); + Console.error( + "This command does not work while Meteor is running your application.", + "Exit the running Meteor development server."); + return 1; + } } await Promise.all([ - files.rm_recursive_async( - files.pathJoin(options.appDir, ".meteor", "local") + ...localDirs.map((rel) => + files.rm_recursive_async(files.pathResolve(options.appDir, rel)) ), resetMeteorNpmCachePromise, ...resetRspackPromises, @@ -1901,11 +1914,19 @@ main.registerCommand({ return; } - var allExceptDb = files.getPathsInDir(files.pathJoin('.meteor', 'local'), { - cwd: options.appDir, - maxDepth: 1, - }).filter(function (path) { - return !path.includes('.meteor/local/db'); + // Collect all paths inside each local dir except db + var allExceptDb = localDirs.flatMap((rel) => { + try { + return files.getPathsInDir(rel, { + cwd: options.appDir, + maxDepth: 1, + }).filter(function (p) { + return !p.includes('/db'); + }); + } catch (e) { + // Directory may not exist (e.g. default dir when only custom is used) + return []; + } }); var allRemovePromises = [ diff --git a/tools/modern-tests/assertions.js b/tools/modern-tests/assertions.js index 4b873f6d9e..437bdec437 100644 --- a/tools/modern-tests/assertions.js +++ b/tools/modern-tests/assertions.js @@ -145,6 +145,43 @@ export async function assertFileExist(tempDir, filePath, options = {}) { await checkFile(); } +/** + * Helper function to assert that a path does NOT exist + * Retries until the path is gone or the timeout is exceeded + * @param {string} basePath - Base directory path + * @param {string} relPath - Relative path from basePath to check + * @param {Object} options - Additional options + * @param {number} options.timeout - Maximum time to wait in milliseconds (default: 5000) + * @param {number} options.checkInterval - Interval between checks in milliseconds (default: 100) + * @returns {Promise} + */ +export async function assertPathNotExist(basePath, relPath, options = {}) { + const { timeout = 5000, checkInterval = 100 } = options; + const fullPath = path.join(basePath, relPath); + const startTime = Date.now(); + + const check = async () => { + const exists = await fs.pathExists(fullPath); + if (exists && Date.now() - startTime < timeout) { + await new Promise(r => setTimeout(r, checkInterval)); + return check(); + } + if (exists) { + const stat = await fs.stat(fullPath); + const isDir = stat.isDirectory(); + let contents = ''; + if (isDir) { + const entries = await fs.readdir(fullPath); + contents = ` (contains: ${entries.join(', ')})`; + } + console.error(`assertPathNotExist FAILED: ${relPath} still exists at ${fullPath} [${isDir ? 'dir' : 'file'}, ${stat.size} bytes]${contents}`); + } + expect(exists).toBe(false); + }; + + await check(); +} + /** * Helper function to evaluate JavaScript code in the browser console and assert the result * @param {string} code - JavaScript code to evaluate in the browser console diff --git a/tools/modern-tests/test-helpers.js b/tools/modern-tests/test-helpers.js index d9ea68a623..02984c2008 100644 --- a/tools/modern-tests/test-helpers.js +++ b/tools/modern-tests/test-helpers.js @@ -23,6 +23,7 @@ import { assertFileExist, assertMeteorApp, assertMeteorReactApp, + assertPathNotExist, assertRspackScriptTag } from "./assertions"; import fs from "fs-extra"; @@ -776,6 +777,57 @@ export function testMeteorRspackBundler(options) { await cleanupTempDir(buildOutputDir); } }); + + test(`"meteor reset" / should clear all caches and build artifacts`, async () => { + // Derive METEOR_LOCAL_DIR-aware paths for assertions + const resetEnv = { ...env, ...(env.meteorReset || {}) }; + const meteorLocalDirEnv = resetEnv.METEOR_LOCAL_DIR; + const meteorLocalDirName = meteorLocalDirEnv + ? path.basename(meteorLocalDirEnv.replace(/\\/g, '/')) + : ''; + const localDirSuffix = meteorLocalDirName ? `-${meteorLocalDirName}` : ''; + + // Verify build artifacts exist from previous tests + await assertFileExist(appDir, buildDir); + await assertFileExist(appDir, 'node_modules/.cache/rspack'); + + // Run meteor reset + await runMeteorCommand("reset", [], appDir, { + checkExitCode: true, + env: resetEnv, + }); + + // Verify Rspack build artifacts removed (always check defaults) + await assertPathNotExist(appDir, buildDir); + await assertPathNotExist(appDir, 'node_modules/.cache/rspack'); + await assertPathNotExist(appDir, '_build'); + await assertPathNotExist(appDir, 'public/build-assets'); + await assertPathNotExist(appDir, 'public/build-chunks'); + + // When METEOR_LOCAL_DIR is set, also verify suffixed paths are cleaned + if (localDirSuffix) { + await assertPathNotExist(appDir, `_build${localDirSuffix}`); + await assertPathNotExist(appDir, `public/build-assets${localDirSuffix}`); + await assertPathNotExist(appDir, `public/build-chunks${localDirSuffix}`); + } + + // Verify default .meteor/local caches are always cleaned + await assertPathNotExist(appDir, '.meteor/local/build'); + await assertPathNotExist(appDir, '.meteor/local/bundler-cache'); + await assertPathNotExist(appDir, '.meteor/local/plugin-cache'); + + // When METEOR_LOCAL_DIR is set, also verify custom local dir is cleaned + if (meteorLocalDirEnv && meteorLocalDirEnv !== '.meteor/local') { + await assertPathNotExist(appDir, `${meteorLocalDirEnv}/build`); + await assertPathNotExist(appDir, `${meteorLocalDirEnv}/bundler-cache`); + await assertPathNotExist(appDir, `${meteorLocalDirEnv}/plugin-cache`); + } + + // Run custom assertions if provided + if (customAssertions && customAssertions.afterReset) { + await customAssertions.afterReset({ tempDir, appDir }); + } + }); }; } @@ -1047,5 +1099,56 @@ export function testMeteorSkeleton(options) { await cleanupTempDir(buildOutputDir); } }); + + test(`"meteor reset" / should clear all caches and build artifacts`, async () => { + // Derive METEOR_LOCAL_DIR-aware paths for assertions + const resetEnv = { ...env, ...(env.meteorReset || {}) }; + const meteorLocalDirEnv = resetEnv.METEOR_LOCAL_DIR; + const meteorLocalDirName = meteorLocalDirEnv + ? path.basename(meteorLocalDirEnv.replace(/\\/g, '/')) + : ''; + const localDirSuffix = meteorLocalDirName ? `-${meteorLocalDirName}` : ''; + + // Verify build artifacts exist from previous tests + await assertFileExist(tempDir, '_build'); + await assertFileExist(tempDir, 'node_modules/.cache/rspack'); + + // Run meteor reset + await runMeteorCommand('reset', [], tempDir, { + checkExitCode: true, + env: resetEnv, + }); + + // Verify Rspack build artifacts removed (always check defaults) + await assertPathNotExist(tempDir, '_build'); + await assertPathNotExist(tempDir, 'node_modules/.cache/rspack'); + await assertPathNotExist(tempDir, 'node_modules/.cache/meteor'); + await assertPathNotExist(tempDir, 'public/build-assets'); + await assertPathNotExist(tempDir, 'public/build-chunks'); + + // When METEOR_LOCAL_DIR is set, also verify suffixed paths are cleaned + if (localDirSuffix) { + await assertPathNotExist(tempDir, `_build${localDirSuffix}`); + await assertPathNotExist(tempDir, `public/build-assets${localDirSuffix}`); + await assertPathNotExist(tempDir, `public/build-chunks${localDirSuffix}`); + } + + // Verify default .meteor/local caches are always cleaned + await assertPathNotExist(tempDir, '.meteor/local/build'); + await assertPathNotExist(tempDir, '.meteor/local/bundler-cache'); + await assertPathNotExist(tempDir, '.meteor/local/plugin-cache'); + + // When METEOR_LOCAL_DIR is set, also verify custom local dir is cleaned + if (meteorLocalDirEnv && meteorLocalDirEnv !== '.meteor/local') { + await assertPathNotExist(tempDir, `${meteorLocalDirEnv}/build`); + await assertPathNotExist(tempDir, `${meteorLocalDirEnv}/bundler-cache`); + await assertPathNotExist(tempDir, `${meteorLocalDirEnv}/plugin-cache`); + } + + // Run custom assertions if provided + if (customAssertions.afterReset) { + await customAssertions.afterReset({ tempDir }); + } + }); }; } diff --git a/tools/tool-env/rspack.js b/tools/tool-env/rspack.js index 766ed44ec0..21749393d8 100644 --- a/tools/tool-env/rspack.js +++ b/tools/tool-env/rspack.js @@ -1,17 +1,25 @@ // Helper functions for Rspack integration const files = require('../fs/files'); +const path = require('path'); const { getMeteorConfig } = require("./meteor-config"); const config = getMeteorConfig(); +// Derive the METEOR_LOCAL_DIR suffix the same way packages/rspack/lib/constants.js does, +// so reset cleans the correct directories when running multiple instances. +const meteorLocalDirName = process.env.METEOR_LOCAL_DIR + ? path.basename(process.env.METEOR_LOCAL_DIR.replace(/\\/g, '/')) + : ''; +const localDirSuffix = meteorLocalDirName ? `-${meteorLocalDirName}` : ''; + // Get the build context from environment variable or use default "_build" -const rspackBuildContext = config?.buildContext || process.env.RSPACK_BUILD_CONTEXT || "_build"; +const rspackBuildContext = config?.buildContext || process.env.RSPACK_BUILD_CONTEXT || `_build${localDirSuffix}`; // Get the assets context from environment variable or use default "build-assets" -const rspackAssetsContext = config?.assetsContext || process.env.RSPACK_ASSETS_CONTEXT || "build-assets"; +const rspackAssetsContext = config?.assetsContext || process.env.RSPACK_ASSETS_CONTEXT || `build-assets${localDirSuffix}`; // Get the bundles context from environment variable or use default "build-chunks" -const rspackChunksContext = config?.chunksContext || process.env.RSPACK_CHUNKS_CONTEXT || "build-chunks"; +const rspackChunksContext = config?.chunksContext || process.env.RSPACK_CHUNKS_CONTEXT || `build-chunks${localDirSuffix}`; // Cache the regex pattern for performance const rspackFilePattern = new RegExp(`^${rspackBuildContext}\\/.*\\/[^\\/]*-rspack\\.js$`); @@ -35,16 +43,31 @@ exports.getRspackResourcesContexts = function() { ]; }; -// Function to get the rspack app contexts +// Function to get the rspack app contexts for cleanup. +// Always includes the default paths (_build, build-assets, build-chunks) to +// prevent regressions, plus suffixed paths when METEOR_LOCAL_DIR is set. exports.getRspackAppContexts = function(appDir) { - const rspackResourcesContexts = exports.getRspackResourcesContexts(); - return [ + const contexts = [ files.pathJoin(appDir, "node_modules", ".cache", "rspack"), - files.pathJoin(appDir, rspackBuildContext), - ...rspackResourcesContexts.reduce((arr, context) => [ - ...arr, - files.pathJoin(appDir, `public/${context}`), - files.pathJoin(appDir, `public/${context}`) - ], []) ]; + + // Always include defaults + const defaults = ['_build', 'build-assets', 'build-chunks']; + for (const name of defaults) { + contexts.push(files.pathJoin(appDir, name)); + contexts.push(files.pathJoin(appDir, `public/${name}`)); + contexts.push(files.pathJoin(appDir, `private/${name}`)); + } + + // When METEOR_LOCAL_DIR is set, also include suffixed paths + if (localDirSuffix) { + const suffixed = defaults.map(name => `${name}${localDirSuffix}`); + for (const name of suffixed) { + contexts.push(files.pathJoin(appDir, name)); + contexts.push(files.pathJoin(appDir, `public/${name}`)); + contexts.push(files.pathJoin(appDir, `private/${name}`)); + } + } + + return contexts; }; From 55630694211e081c4f5f6abf23c661a7f3cc0724 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Thu, 5 Mar 2026 16:39:46 +0100 Subject: [PATCH 147/166] add skipBuildCacheCheck option to test helpers and update `skeleton.test.js` to use this option. --- tools/modern-tests/skeleton.test.js | 1 + tools/modern-tests/test-helpers.js | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/tools/modern-tests/skeleton.test.js b/tools/modern-tests/skeleton.test.js index bfc46155b7..f880eb0192 100644 --- a/tools/modern-tests/skeleton.test.js +++ b/tools/modern-tests/skeleton.test.js @@ -60,6 +60,7 @@ describe('Meteor Skeletons /', () => { checkAppTitle: false, checkBodyStyles: false, skipTestClient: true, + skipBuildCacheCheck: true, }) ); diff --git a/tools/modern-tests/test-helpers.js b/tools/modern-tests/test-helpers.js index 02984c2008..acc7a79259 100644 --- a/tools/modern-tests/test-helpers.js +++ b/tools/modern-tests/test-helpers.js @@ -876,6 +876,8 @@ export function testMeteorSkeleton(options) { afterAllBehavior, // Per-phase env vars: { meteorRun, meteorRunProduction, meteorTest, meteorBuild } env = {}, + // Bare skeleton may not create build artifacts (e.g. _build, node_modules/.cache/rspack) + skipBuildCacheCheck = false, } = options; return () => { @@ -1110,8 +1112,10 @@ export function testMeteorSkeleton(options) { const localDirSuffix = meteorLocalDirName ? `-${meteorLocalDirName}` : ''; // Verify build artifacts exist from previous tests - await assertFileExist(tempDir, '_build'); - await assertFileExist(tempDir, 'node_modules/.cache/rspack'); + if (!skipBuildCacheCheck) { + await assertFileExist(tempDir, "_build"); + await assertFileExist(tempDir, "node_modules/.cache/rspack"); + } // Run meteor reset await runMeteorCommand('reset', [], tempDir, { From dd52fe3937af558f72f623466f7eeff9e677c067 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Thu, 5 Mar 2026 16:55:02 +0100 Subject: [PATCH 148/166] resolve custom Rspack context paths from `package.json` --- tools/modern-tests/test-helpers.js | 33 ++++++++++++++++++++-- tools/modern-tests/typescript.test.js | 2 ++ tools/tool-env/rspack.js | 40 ++++++++++++++++++++------- 3 files changed, 63 insertions(+), 12 deletions(-) diff --git a/tools/modern-tests/test-helpers.js b/tools/modern-tests/test-helpers.js index acc7a79259..2261d0d8ab 100644 --- a/tools/modern-tests/test-helpers.js +++ b/tools/modern-tests/test-helpers.js @@ -212,6 +212,10 @@ export function testMeteorRspackBundler(options) { afterAllBehavior, // Build directory (default: '_build') buildDir = '_build', + // Assets context directory (default: 'build-assets') + assetsContext = 'build-assets', + // Chunks context directory (default: 'build-chunks') + chunksContext = 'build-chunks', // Rspack config file (default: 'rspack.config.js') configFile = 'rspack.config.js', // Custom environment variables @@ -797,15 +801,26 @@ export function testMeteorRspackBundler(options) { env: resetEnv, }); - // Verify Rspack build artifacts removed (always check defaults) + // Verify Rspack build artifacts removed await assertPathNotExist(appDir, buildDir); await assertPathNotExist(appDir, 'node_modules/.cache/rspack'); + await assertPathNotExist(appDir, assetsContext); + await assertPathNotExist(appDir, chunksContext); + await assertPathNotExist(appDir, `public/${assetsContext}`); + await assertPathNotExist(appDir, `public/${chunksContext}`); + + // Also verify defaults are cleaned to prevent regressions await assertPathNotExist(appDir, '_build'); await assertPathNotExist(appDir, 'public/build-assets'); await assertPathNotExist(appDir, 'public/build-chunks'); // When METEOR_LOCAL_DIR is set, also verify suffixed paths are cleaned if (localDirSuffix) { + await assertPathNotExist(appDir, `${buildDir}${localDirSuffix}`); + await assertPathNotExist(appDir, `${assetsContext}${localDirSuffix}`); + await assertPathNotExist(appDir, `${chunksContext}${localDirSuffix}`); + await assertPathNotExist(appDir, `public/${assetsContext}${localDirSuffix}`); + await assertPathNotExist(appDir, `public/${chunksContext}${localDirSuffix}`); await assertPathNotExist(appDir, `_build${localDirSuffix}`); await assertPathNotExist(appDir, `public/build-assets${localDirSuffix}`); await assertPathNotExist(appDir, `public/build-chunks${localDirSuffix}`); @@ -878,6 +893,10 @@ export function testMeteorSkeleton(options) { env = {}, // Bare skeleton may not create build artifacts (e.g. _build, node_modules/.cache/rspack) skipBuildCacheCheck = false, + // Assets context directory (default: 'build-assets') + assetsContext = 'build-assets', + // Chunks context directory (default: 'build-chunks') + chunksContext = 'build-chunks', } = options; return () => { @@ -1123,16 +1142,26 @@ export function testMeteorSkeleton(options) { env: resetEnv, }); - // Verify Rspack build artifacts removed (always check defaults) + // Verify Rspack build artifacts removed await assertPathNotExist(tempDir, '_build'); await assertPathNotExist(tempDir, 'node_modules/.cache/rspack'); await assertPathNotExist(tempDir, 'node_modules/.cache/meteor'); + await assertPathNotExist(tempDir, assetsContext); + await assertPathNotExist(tempDir, chunksContext); + await assertPathNotExist(tempDir, `public/${assetsContext}`); + await assertPathNotExist(tempDir, `public/${chunksContext}`); + + // Also verify defaults are cleaned to prevent regressions await assertPathNotExist(tempDir, 'public/build-assets'); await assertPathNotExist(tempDir, 'public/build-chunks'); // When METEOR_LOCAL_DIR is set, also verify suffixed paths are cleaned if (localDirSuffix) { await assertPathNotExist(tempDir, `_build${localDirSuffix}`); + await assertPathNotExist(tempDir, `${assetsContext}${localDirSuffix}`); + await assertPathNotExist(tempDir, `${chunksContext}${localDirSuffix}`); + await assertPathNotExist(tempDir, `public/${assetsContext}${localDirSuffix}`); + await assertPathNotExist(tempDir, `public/${chunksContext}${localDirSuffix}`); await assertPathNotExist(tempDir, `public/build-assets${localDirSuffix}`); await assertPathNotExist(tempDir, `public/build-chunks${localDirSuffix}`); } diff --git a/tools/modern-tests/typescript.test.js b/tools/modern-tests/typescript.test.js index de6bd3d4f3..7be3de5bed 100644 --- a/tools/modern-tests/typescript.test.js +++ b/tools/modern-tests/typescript.test.js @@ -19,6 +19,8 @@ describe('TypeScript App Bundling /', () => { testServer: 'tests/server.ts', }, buildDir: 'build', + assetsContext: 'assets', + chunksContext: 'chunks', configFile: 'rspack.config.ts', customAssertions: { afterCreate({ tempDir }) { diff --git a/tools/tool-env/rspack.js b/tools/tool-env/rspack.js index 21749393d8..be7794dfba 100644 --- a/tools/tool-env/rspack.js +++ b/tools/tool-env/rspack.js @@ -44,16 +44,36 @@ exports.getRspackResourcesContexts = function() { }; // Function to get the rspack app contexts for cleanup. -// Always includes the default paths (_build, build-assets, build-chunks) to -// prevent regressions, plus suffixed paths when METEOR_LOCAL_DIR is set. +// Reads the app's package.json meteor config to resolve custom context names, +// and always includes the default paths to prevent regressions. exports.getRspackAppContexts = function(appDir) { + let appConfig = null; + try { + const pkgPath = files.pathJoin(appDir, 'package.json'); + if (files.exists(pkgPath)) { + const pkg = JSON.parse(files.readFile(pkgPath, 'utf8')); + appConfig = pkg?.meteor || null; + } + } catch (e) { + // Fall back to defaults if package.json can't be read + } + + const appBuildContext = appConfig?.buildContext || process.env.RSPACK_BUILD_CONTEXT || `_build${localDirSuffix}`; + const appAssetsContext = appConfig?.assetsContext || process.env.RSPACK_ASSETS_CONTEXT || `build-assets${localDirSuffix}`; + const appChunksContext = appConfig?.chunksContext || process.env.RSPACK_CHUNKS_CONTEXT || `build-chunks${localDirSuffix}`; + const contexts = [ files.pathJoin(appDir, "node_modules", ".cache", "rspack"), ]; - // Always include defaults - const defaults = ['_build', 'build-assets', 'build-chunks']; - for (const name of defaults) { + // Collect unique context names (configured + defaults to prevent regressions) + const allNames = new Set([ + appBuildContext, '_build', + appAssetsContext, 'build-assets', + appChunksContext, 'build-chunks', + ]); + + for (const name of allNames) { contexts.push(files.pathJoin(appDir, name)); contexts.push(files.pathJoin(appDir, `public/${name}`)); contexts.push(files.pathJoin(appDir, `private/${name}`)); @@ -61,11 +81,11 @@ exports.getRspackAppContexts = function(appDir) { // When METEOR_LOCAL_DIR is set, also include suffixed paths if (localDirSuffix) { - const suffixed = defaults.map(name => `${name}${localDirSuffix}`); - for (const name of suffixed) { - contexts.push(files.pathJoin(appDir, name)); - contexts.push(files.pathJoin(appDir, `public/${name}`)); - contexts.push(files.pathJoin(appDir, `private/${name}`)); + for (const name of allNames) { + const suffixed = `${name}${localDirSuffix}`; + contexts.push(files.pathJoin(appDir, suffixed)); + contexts.push(files.pathJoin(appDir, `public/${suffixed}`)); + contexts.push(files.pathJoin(appDir, `private/${suffixed}`)); } } From 651b87ece83c28af024089fd64834e774f109717 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Thu, 5 Mar 2026 18:16:10 +0100 Subject: [PATCH 149/166] extract `linkLocalRspack` logic into a reusable module and integrate it across test helpers and scripts. --- tools/e2e-tests/scripts/create-app.js | 17 ++---- tools/e2e-tests/scripts/link-rspack.js | 77 ++++++++++++++++++++++++++ tools/e2e-tests/test-helpers.js | 36 +----------- 3 files changed, 85 insertions(+), 45 deletions(-) create mode 100644 tools/e2e-tests/scripts/link-rspack.js diff --git a/tools/e2e-tests/scripts/create-app.js b/tools/e2e-tests/scripts/create-app.js index 14315eb75c..fc11504356 100644 --- a/tools/e2e-tests/scripts/create-app.js +++ b/tools/e2e-tests/scripts/create-app.js @@ -18,6 +18,7 @@ const path = require('path'); const fs = require('fs-extra'); const execa = require('execa'); +const { linkLocalRspack } = require('./link-rspack'); const REPO_ROOT = path.resolve(__dirname, '../../..'); const METEOR_EXECUTABLE = path.join(REPO_ROOT, 'meteor'); @@ -363,12 +364,8 @@ async function setupFromApp(appName, destDir, { isMonorepo = false, force = fals ...execEnv, }); - log.step('Updating Meteor npm dependencies...'); - await execa(METEOR_EXECUTABLE, ['update', '--npm'], { - cwd: meteorAppDir, - stdio: 'inherit', - ...execEnv, - }); + log.step('Linking local @meteorjs/rspack...'); + await linkLocalRspack(meteorAppDir, { env: envVars }); if (isMonorepo) { log.step('Running meteor npm install at root level...'); @@ -430,12 +427,8 @@ async function setupFromSkeleton(skeletonName, destDir, { force = false } = {}) ...execEnv, }); - log.step('Updating Meteor npm dependencies...'); - await execa(METEOR_EXECUTABLE, ['update', '--npm'], { - cwd: destDir, - stdio: 'inherit', - ...execEnv, - }); + log.step('Linking local @meteorjs/rspack...'); + await linkLocalRspack(destDir, { env: envVars }); log.step('Running meteor npm install...'); await execa(METEOR_EXECUTABLE, ['npm', 'install'], { cwd: destDir, stdio: 'inherit', ...execEnv }); diff --git a/tools/e2e-tests/scripts/link-rspack.js b/tools/e2e-tests/scripts/link-rspack.js new file mode 100644 index 0000000000..5e8b6bbb97 --- /dev/null +++ b/tools/e2e-tests/scripts/link-rspack.js @@ -0,0 +1,77 @@ +#!/usr/bin/env node + +/** + * Links the local npm-packages/meteor-rspack into a Meteor app so it runs + * against the latest dev version. + * + * Steps: + * 1. Run `meteor update --npm` in the app + * 2. Install the matching @rspack/core and @rspack/cli versions into the + * local meteor-rspack package (read from packages/rspack/lib/constants.js) + * 3. Install `ignore-loader` in the app + * 4. `npm link` the local meteor-rspack into the app + * + */ + +const path = require('path'); +const fs = require('fs'); +const execa = require('execa'); + +const REPO_ROOT = path.resolve(__dirname, '..', '..', '..'); +const METEOR_EXECUTABLE = path.join(REPO_ROOT, 'meteor'); +const RSPACK_PACKAGE_DIR = path.join(REPO_ROOT, 'npm-packages', 'meteor-rspack'); +const CONSTANTS_PATH = path.join(REPO_ROOT, 'packages', 'rspack', 'lib', 'constants.js'); + +async function linkLocalRspack(appDir, { env } = {}) { + const execOpts = env ? { env: { ...process.env, ...env } } : {}; + + console.log(`Running meteor update --npm in ${appDir}...`); + await execa(METEOR_EXECUTABLE, ['update', '--npm'], { + cwd: appDir, + stdio: 'inherit', + ...execOpts, + }); + + const constantsContent = fs.readFileSync(CONSTANTS_PATH, 'utf8'); + const rspackVersionMatch = constantsContent.match( + /DEFAULT_RSPACK_VERSION\s*=\s*['"]([^'"]+)['"]/ + ); + const rspackVersion = rspackVersionMatch?.[1]; + if (rspackVersion) { + console.log(`Installing @rspack/core@${rspackVersion} and @rspack/cli@${rspackVersion}...`); + await execa( + 'npm', + [ + 'install', + `@rspack/core@${rspackVersion}`, + `@rspack/cli@${rspackVersion}`, + '--no-save', + '--no-package-lock', + ], + { cwd: RSPACK_PACKAGE_DIR } + ); + } + + console.log('Installing ignore-loader in the app...'); + await execa('npm', ['install', 'ignore-loader', '--save'], { cwd: appDir }); + + console.log(`Linking local meteor-rspack from ${RSPACK_PACKAGE_DIR}...`); + await execa('npm', ['link', RSPACK_PACKAGE_DIR], { cwd: appDir }); + + console.log('Local meteor-rspack linked successfully.'); +} + +module.exports = { linkLocalRspack, REPO_ROOT, METEOR_EXECUTABLE, RSPACK_PACKAGE_DIR }; + +// CLI mode +if (require.main === module) { + const appDir = process.argv[2]; + if (!appDir) { + console.error('Usage: node link-rspack.js '); + process.exit(1); + } + linkLocalRspack(path.resolve(appDir)).catch(err => { + console.error(err.message); + process.exit(1); + }); +} diff --git a/tools/e2e-tests/test-helpers.js b/tools/e2e-tests/test-helpers.js index 2261d0d8ab..f1728472d5 100644 --- a/tools/e2e-tests/test-helpers.js +++ b/tools/e2e-tests/test-helpers.js @@ -38,41 +38,11 @@ const npmLinkLocalRspack = process.env.NPM_LINK_RSPACK !== 'false'; const WAIT_ON = isCI ? 2000 : 500; +const { linkLocalRspack: _linkLocalRspack } = require('./scripts/link-rspack'); + async function linkLocalRspack(appDir) { if (!npmLinkLocalRspack) return; - const repoRoot = path.resolve(process.cwd(), '..', '..'); - - const meteorBin = path.join(repoRoot, "meteor"); - console.log(`Running meteor update --npm in ${appDir}...`); - (await execa(meteorBin, ["update", "--npm"], { - cwd: appDir, - stdio: "inherit", - })); - - const rspackPackageDir = path.join(repoRoot, 'npm-packages', 'meteor-rspack'); - const constantsPath = path.join(repoRoot, 'packages', 'rspack', 'lib', 'constants.js'); - const constantsContent = await fs.readFile(constantsPath, 'utf8'); - const rspackVersionMatch = constantsContent.match(/DEFAULT_RSPACK_VERSION\s*=\s*['"]([^'"]+)['"]/); - const rspackVersion = rspackVersionMatch?.[1]; - if (rspackVersion) { - console.log(`Installing @rspack/core@${rspackVersion} and @rspack/cli@${rspackVersion}...`); - await execa( - "npm", - [ - "install", - `@rspack/core@${rspackVersion}`, - `@rspack/cli@${rspackVersion}`, - "--no-save", - "--no-package-lock", - ], - { cwd: rspackPackageDir } - ); - } - console.log(`Installing ignore-loader in the app...`); - await execa('npm', ['install', 'ignore-loader', '--save'], { cwd: appDir }); - console.log(`Linking local meteor-rspack from ${rspackPackageDir}...`); - await execa('npm', ['link', rspackPackageDir], { cwd: appDir }); - console.log('Local meteor-rspack linked successfully.'); + await _linkLocalRspack(appDir); } /** From 85daa9c7d7f788f8a8f8123e97fc08a3eac47e80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Mon, 9 Mar 2026 13:50:43 +0100 Subject: [PATCH 150/166] update package-lock.json --- tools/modern-tests/package-lock.json | 1888 ++++++-------------------- 1 file changed, 411 insertions(+), 1477 deletions(-) diff --git a/tools/modern-tests/package-lock.json b/tools/modern-tests/package-lock.json index b37ab8042f..5a3d6d6fb7 100644 --- a/tools/modern-tests/package-lock.json +++ b/tools/modern-tests/package-lock.json @@ -8,14 +8,16 @@ "name": "meteor-modern-tests", "version": "1.0.0", "devDependencies": { - "@babel/preset-env": "^7.21.3", - "babel-jest": "^29.0.0", + "@swc/core": "^1.15.18", + "@swc/jest": "^0.2.39", "cheerio": "^1.0.0-rc.12", "execa": "^5.1.1", "fs-extra": "^11.3.1", "jest": "^29.0.0", "jest-playwright-preset": "^3.0.1", "playwright": "^1.58.0", + "semver": "^7.7.4", + "underscore": "^1.13.8", "wait-on": "^7.0.0" } }, @@ -89,6 +91,16 @@ "url": "https://opencollective.com/babel" } }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/@babel/generator": { "version": "7.28.3", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", @@ -106,19 +118,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", - "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.27.3" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-compilation-targets": { "version": "7.27.2", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", @@ -136,61 +135,14 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.3.tgz", - "integrity": "sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg==", + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-member-expression-to-functions": "^7.27.1", - "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/helper-replace-supers": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/traverse": "^7.28.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.27.1.tgz", - "integrity": "sha512-uVDC72XVf8UbrH5qQTc18Agb8emwjTiZrQE11Nv3CuBEZmVvTwwE9CBUEvHku06gQCAyYf8Nv6ja1IN+6LMbxQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "regexpu-core": "^6.2.0", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.5.tgz", - "integrity": "sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-plugin-utils": "^7.27.1", - "debug": "^4.4.1", - "lodash.debounce": "^4.0.8", - "resolve": "^1.22.10" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + "license": "ISC", + "bin": { + "semver": "bin/semver.js" } }, "node_modules/@babel/helper-globals": { @@ -203,20 +155,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz", - "integrity": "sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-module-imports": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", @@ -249,19 +187,6 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", - "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-plugin-utils": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", @@ -272,56 +197,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz", - "integrity": "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-wrap-function": "^7.27.1", - "@babel/traverse": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-replace-supers": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz", - "integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.27.1", - "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/traverse": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", - "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-string-parser": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", @@ -352,21 +227,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-wrap-function": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.28.3.tgz", - "integrity": "sha512-zdf983tNfLZFletc0RRXYrHrucBEg95NIFMkn6K9dbeMYnsgHaSBGcQqdsCSStG2PYwRre0Qc2NNSCXbG+xc6g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.27.2", - "@babel/traverse": "^7.28.3", - "@babel/types": "^7.28.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helpers": { "version": "7.28.3", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.3.tgz", @@ -397,103 +257,6 @@ "node": ">=6.0.0" } }, - "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.27.1.tgz", - "integrity": "sha512-QPG3C9cCVRQLxAVwmefEmwdTanECuUBMQZ/ym5kiw3XKCGA7qkuQLcjWWHcrD/GKbn/WmJwaezfuuAOcyKlRPA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.27.1.tgz", - "integrity": "sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.27.1.tgz", - "integrity": "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz", - "integrity": "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/plugin-transform-optional-chaining": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.13.0" - } - }, - "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.28.3.tgz", - "integrity": "sha512-b6YTX108evsvE4YgWyQ921ZAFFQm3Bn+CA3+ZXlNVnPhx+UfsVURoPjfGAPCjBgrqo30yX/C2nZGX96DxvR9Iw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.28.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-proposal-private-property-in-object": { - "version": "7.21.0-placeholder-for-preset-env.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", - "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-syntax-async-generators": { "version": "7.8.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", @@ -549,22 +312,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.27.1.tgz", - "integrity": "sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-syntax-import-attributes": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", @@ -749,979 +496,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-unicode-sets-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", - "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz", - "integrity": "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.28.0.tgz", - "integrity": "sha512-BEOdvX4+M765icNPZeidyADIvQ1m1gmunXufXxvRESy/jNNyfovIqUyE7MVgGBjWktCoJlzvFA1To2O4ymIO3Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-remap-async-to-generator": "^7.27.1", - "@babel/traverse": "^7.28.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.27.1.tgz", - "integrity": "sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-remap-async-to-generator": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz", - "integrity": "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.0.tgz", - "integrity": "sha512-gKKnwjpdx5sER/wl0WN0efUBFzF/56YZO0RJrSYP4CljXnP31ByY7fol89AzomdlLNzI36AvOTmYHsnZTCkq8Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.27.1.tgz", - "integrity": "sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.3.tgz", - "integrity": "sha512-LtPXlBbRoc4Njl/oh1CeD/3jC+atytbnf/UqLoqTDcEYGUPj022+rvfkbDYieUrSj3CaV4yHDByPE+T2HwfsJg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.28.3", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.12.0" - } - }, - "node_modules/@babel/plugin-transform-classes": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.3.tgz", - "integrity": "sha512-DoEWC5SuxuARF2KdKmGUq3ghfPMO6ZzR12Dnp5gubwbeWJo4dbNWXJPVlwvh4Zlq6Z7YVvL8VFxeSOJgjsx4Sg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-globals": "^7.28.0", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-replace-supers": "^7.27.1", - "@babel/traverse": "^7.28.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.27.1.tgz", - "integrity": "sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/template": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.0.tgz", - "integrity": "sha512-v1nrSMBiKcodhsyJ4Gf+Z0U/yawmJDBOTpEB3mcQY52r9RIyPneGyAS/yM6seP/8I+mWI3elOMtT5dB8GJVs+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.28.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.27.1.tgz", - "integrity": "sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.27.1.tgz", - "integrity": "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.27.1.tgz", - "integrity": "sha512-hkGcueTEzuhB30B3eJCbCYeCaaEQOmQR0AdvzpD4LoN0GXMWzzGSuRrxR2xTnCrvNbVwK9N6/jQ92GSLfiZWoQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-dynamic-import": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.27.1.tgz", - "integrity": "sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-explicit-resource-management": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.0.tgz", - "integrity": "sha512-K8nhUcn3f6iB+P3gwCv/no7OdzOZQcKchW6N389V6PD8NUWKZHzndOd9sPDVbMoBsbmjMqlB4L9fm+fEFNVlwQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/plugin-transform-destructuring": "^7.28.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.27.1.tgz", - "integrity": "sha512-uspvXnhHvGKf2r4VVtBpeFnuDWsJLQ6MF6lGJLC89jBR1uoVeqM416AZtTuhTezOfgHicpJQmoD5YUakO/YmXQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.27.1.tgz", - "integrity": "sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-for-of": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz", - "integrity": "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-function-name": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz", - "integrity": "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-compilation-targets": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-json-strings": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.27.1.tgz", - "integrity": "sha512-6WVLVJiTjqcQauBhn1LkICsR2H+zm62I3h9faTDKt1qP4jn2o72tSvqMwtGFKGTpojce0gJs+76eZ2uCHRZh0Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-literals": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz", - "integrity": "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.27.1.tgz", - "integrity": "sha512-SJvDs5dXxiae4FbSL1aBJlG4wvl594N6YEVVn9e3JGulwioy6z3oPjx/sQBO3Y4NwUu5HNix6KJ3wBZoewcdbw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz", - "integrity": "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.27.1.tgz", - "integrity": "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz", - "integrity": "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.27.1.tgz", - "integrity": "sha512-w5N1XzsRbc0PQStASMksmUeqECuzKuTJer7kFagK8AXgpCMkeDMO5S+aaFb7A51ZYDF7XI34qsTX+fkHiIm5yA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.27.1.tgz", - "integrity": "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.27.1.tgz", - "integrity": "sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-new-target": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.27.1.tgz", - "integrity": "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.27.1.tgz", - "integrity": "sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.27.1.tgz", - "integrity": "sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.0.tgz", - "integrity": "sha512-9VNGikXxzu5eCiQjdE4IZn8sb9q7Xsk5EXLDBKUYg1e/Tve8/05+KJEtcxGxAgCY5t/BpKQM+JEL/yT4tvgiUA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/plugin-transform-destructuring": "^7.28.0", - "@babel/plugin-transform-parameters": "^7.27.7", - "@babel/traverse": "^7.28.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-object-super": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz", - "integrity": "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-replace-supers": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.27.1.tgz", - "integrity": "sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.27.1.tgz", - "integrity": "sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-parameters": { - "version": "7.27.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz", - "integrity": "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.27.1.tgz", - "integrity": "sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.27.1.tgz", - "integrity": "sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-create-class-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz", - "integrity": "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.3.tgz", - "integrity": "sha512-K3/M/a4+ESb5LEldjQb+XSrpY0nF+ZBFlTCbSnKaYAMfD8v33O6PMs4uYnOk19HlcsI8WMu3McdFPTiQHF/1/A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-regexp-modifiers": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.27.1.tgz", - "integrity": "sha512-TtEciroaiODtXvLZv4rmfMhkCv8jx3wgKpL68PuiPh2M4fvz5jhsA7697N1gMvkvr/JTF13DrFYyEbY9U7cVPA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.27.1.tgz", - "integrity": "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz", - "integrity": "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-spread": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.27.1.tgz", - "integrity": "sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.27.1.tgz", - "integrity": "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz", - "integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.1.tgz", - "integrity": "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz", - "integrity": "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-property-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.27.1.tgz", - "integrity": "sha512-uW20S39PnaTImxp39O5qFlHLS9LJEmANjMG7SxIhap8rCHqu0Ik+tLEPX5DKmHn6CsWQ7j3lix2tFOa5YtL12Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz", - "integrity": "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.27.1.tgz", - "integrity": "sha512-EtkOujbc4cgvb0mlpQefi4NTPBzhSIevblFevACNLUspmrALgmEBdL/XfnyyITfd8fKBZrZys92zOWcik7j9Tw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/preset-env": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.28.3.tgz", - "integrity": "sha512-ROiDcM+GbYVPYBOeCR6uBXKkQpBExLl8k9HO1ygXEyds39j+vCCsjmj7S8GOniZQlEs81QlkdJZe76IpLSiqpg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.28.0", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-validator-option": "^7.27.1", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.27.1", - "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.28.3", - "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", - "@babel/plugin-syntax-import-assertions": "^7.27.1", - "@babel/plugin-syntax-import-attributes": "^7.27.1", - "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.27.1", - "@babel/plugin-transform-async-generator-functions": "^7.28.0", - "@babel/plugin-transform-async-to-generator": "^7.27.1", - "@babel/plugin-transform-block-scoped-functions": "^7.27.1", - "@babel/plugin-transform-block-scoping": "^7.28.0", - "@babel/plugin-transform-class-properties": "^7.27.1", - "@babel/plugin-transform-class-static-block": "^7.28.3", - "@babel/plugin-transform-classes": "^7.28.3", - "@babel/plugin-transform-computed-properties": "^7.27.1", - "@babel/plugin-transform-destructuring": "^7.28.0", - "@babel/plugin-transform-dotall-regex": "^7.27.1", - "@babel/plugin-transform-duplicate-keys": "^7.27.1", - "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.27.1", - "@babel/plugin-transform-dynamic-import": "^7.27.1", - "@babel/plugin-transform-explicit-resource-management": "^7.28.0", - "@babel/plugin-transform-exponentiation-operator": "^7.27.1", - "@babel/plugin-transform-export-namespace-from": "^7.27.1", - "@babel/plugin-transform-for-of": "^7.27.1", - "@babel/plugin-transform-function-name": "^7.27.1", - "@babel/plugin-transform-json-strings": "^7.27.1", - "@babel/plugin-transform-literals": "^7.27.1", - "@babel/plugin-transform-logical-assignment-operators": "^7.27.1", - "@babel/plugin-transform-member-expression-literals": "^7.27.1", - "@babel/plugin-transform-modules-amd": "^7.27.1", - "@babel/plugin-transform-modules-commonjs": "^7.27.1", - "@babel/plugin-transform-modules-systemjs": "^7.27.1", - "@babel/plugin-transform-modules-umd": "^7.27.1", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.27.1", - "@babel/plugin-transform-new-target": "^7.27.1", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.27.1", - "@babel/plugin-transform-numeric-separator": "^7.27.1", - "@babel/plugin-transform-object-rest-spread": "^7.28.0", - "@babel/plugin-transform-object-super": "^7.27.1", - "@babel/plugin-transform-optional-catch-binding": "^7.27.1", - "@babel/plugin-transform-optional-chaining": "^7.27.1", - "@babel/plugin-transform-parameters": "^7.27.7", - "@babel/plugin-transform-private-methods": "^7.27.1", - "@babel/plugin-transform-private-property-in-object": "^7.27.1", - "@babel/plugin-transform-property-literals": "^7.27.1", - "@babel/plugin-transform-regenerator": "^7.28.3", - "@babel/plugin-transform-regexp-modifiers": "^7.27.1", - "@babel/plugin-transform-reserved-words": "^7.27.1", - "@babel/plugin-transform-shorthand-properties": "^7.27.1", - "@babel/plugin-transform-spread": "^7.27.1", - "@babel/plugin-transform-sticky-regex": "^7.27.1", - "@babel/plugin-transform-template-literals": "^7.27.1", - "@babel/plugin-transform-typeof-symbol": "^7.27.1", - "@babel/plugin-transform-unicode-escapes": "^7.27.1", - "@babel/plugin-transform-unicode-property-regex": "^7.27.1", - "@babel/plugin-transform-unicode-regex": "^7.27.1", - "@babel/plugin-transform-unicode-sets-regex": "^7.27.1", - "@babel/preset-modules": "0.1.6-no-external-plugins", - "babel-plugin-polyfill-corejs2": "^0.4.14", - "babel-plugin-polyfill-corejs3": "^0.13.0", - "babel-plugin-polyfill-regenerator": "^0.6.5", - "core-js-compat": "^3.43.0", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/preset-modules": { - "version": "0.1.6-no-external-plugins", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", - "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" - } - }, "node_modules/@babel/template": { "version": "7.27.2", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", @@ -1887,6 +661,58 @@ } } }, + "node_modules/@jest/create-cache-key-function": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/create-cache-key-function/-/create-cache-key-function-30.2.0.tgz", + "integrity": "sha512-44F4l4Enf+MirJN8X/NhdGkl71k5rBYiwdVlo4HxOwbu0sHV8QKrGEedb1VUU4K3W7fBKE0HGfbn7eZm0Ti3zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/create-cache-key-function/node_modules/@jest/schemas": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", + "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.34.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/create-cache-key-function/node_modules/@jest/types": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", + "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/pattern": "30.0.1", + "@jest/schemas": "30.0.5", + "@types/istanbul-lib-coverage": "^2.0.6", + "@types/istanbul-reports": "^3.0.4", + "@types/node": "*", + "@types/yargs": "^17.0.33", + "chalk": "^4.1.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/create-cache-key-function/node_modules/@sinclair/typebox": { + "version": "0.34.48", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz", + "integrity": "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==", + "dev": true, + "license": "MIT" + }, "node_modules/@jest/environment": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", @@ -1964,6 +790,30 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/@jest/pattern": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.0.1.tgz", + "integrity": "sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-regex-util": "30.0.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/pattern/node_modules/jest-regex-util": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.0.1.tgz", + "integrity": "sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, "node_modules/@jest/reporters": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", @@ -2025,19 +875,6 @@ "node": ">=10" } }, - "node_modules/@jest/reporters/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@jest/schemas": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", @@ -2233,6 +1070,250 @@ "@sinonjs/commons": "^3.0.0" } }, + "node_modules/@swc/core": { + "version": "1.15.18", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.15.18.tgz", + "integrity": "sha512-z87aF9GphWp//fnkRsqvtY+inMVPgYW3zSlXH1kJFvRT5H/wiAn+G32qW5l3oEk63KSF1x3Ov0BfHCObAmT8RA==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@swc/counter": "^0.1.3", + "@swc/types": "^0.1.25" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" + }, + "optionalDependencies": { + "@swc/core-darwin-arm64": "1.15.18", + "@swc/core-darwin-x64": "1.15.18", + "@swc/core-linux-arm-gnueabihf": "1.15.18", + "@swc/core-linux-arm64-gnu": "1.15.18", + "@swc/core-linux-arm64-musl": "1.15.18", + "@swc/core-linux-x64-gnu": "1.15.18", + "@swc/core-linux-x64-musl": "1.15.18", + "@swc/core-win32-arm64-msvc": "1.15.18", + "@swc/core-win32-ia32-msvc": "1.15.18", + "@swc/core-win32-x64-msvc": "1.15.18" + }, + "peerDependencies": { + "@swc/helpers": ">=0.5.17" + }, + "peerDependenciesMeta": { + "@swc/helpers": { + "optional": true + } + } + }, + "node_modules/@swc/core-darwin-arm64": { + "version": "1.15.18", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.15.18.tgz", + "integrity": "sha512-+mIv7uBuSaywN3C9LNuWaX1jJJ3SKfiJuE6Lr3bd+/1Iv8oMU7oLBjYMluX1UrEPzwN2qCdY6Io0yVicABoCwQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-darwin-x64": { + "version": "1.15.18", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.15.18.tgz", + "integrity": "sha512-wZle0eaQhnzxWX5V/2kEOI6Z9vl/lTFEC6V4EWcn+5pDjhemCpQv9e/TDJ0GIoiClX8EDWRvuZwh+Z3dhL1NAg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.15.18", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.15.18.tgz", + "integrity": "sha512-ao61HGXVqrJFHAcPtF4/DegmwEkVCo4HApnotLU8ognfmU8x589z7+tcf3hU+qBiU1WOXV5fQX6W9Nzs6hjxDw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.15.18", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.15.18.tgz", + "integrity": "sha512-3xnctOBLIq3kj8PxOCgPrGjBLP/kNOddr6f5gukYt/1IZxsITQaU9TDyjeX6jG+FiCIHjCuWuffsyQDL5Ew1bg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-musl": { + "version": "1.15.18", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.15.18.tgz", + "integrity": "sha512-0a+Lix+FSSHBSBOA0XznCcHo5/1nA6oLLjcnocvzXeqtdjnPb+SvchItHI+lfeiuj1sClYPDvPMLSLyXFaiIKw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-gnu": { + "version": "1.15.18", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.15.18.tgz", + "integrity": "sha512-wG9J8vReUlpaHz4KOD/5UE1AUgirimU4UFT9oZmupUDEofxJKYb1mTA/DrMj0s78bkBiNI+7Fo2EgPuvOJfuAA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-musl": { + "version": "1.15.18", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.15.18.tgz", + "integrity": "sha512-4nwbVvCphKzicwNWRmvD5iBaZj8JYsRGa4xOxJmOyHlMDpsvvJ2OR2cODlvWyGFH6BYL1MfIAK3qph3hp0Az6g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.15.18", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.15.18.tgz", + "integrity": "sha512-zk0RYO+LjiBCat2RTMHzAWaMky0cra9loH4oRrLKLLNuL+jarxKLFDA8xTZWEkCPLjUTwlRN7d28eDLLMgtUcQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.15.18", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.15.18.tgz", + "integrity": "sha512-yVuTrZ0RccD5+PEkpcLOBAuPbYBXS6rslENvIXfvJGXSdX5QGi1ehC4BjAMl5FkKLiam4kJECUI0l7Hq7T1vwg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-x64-msvc": { + "version": "1.15.18", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.15.18.tgz", + "integrity": "sha512-7NRmE4hmUQNCbYU3Hn9Tz57mK9Qq4c97ZS+YlamlK6qG9Fb5g/BB3gPDe0iLlJkns/sYv2VWSkm8c3NmbEGjbg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@swc/jest": { + "version": "0.2.39", + "resolved": "https://registry.npmjs.org/@swc/jest/-/jest-0.2.39.tgz", + "integrity": "sha512-eyokjOwYd0Q8RnMHri+8/FS1HIrIUKK/sRrFp8c1dThUOfNeCWbLmBP1P5VsKdvmkd25JaH+OKYwEYiAYg9YAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/create-cache-key-function": "^30.0.0", + "@swc/counter": "^0.1.3", + "jsonc-parser": "^3.2.0" + }, + "engines": { + "npm": ">= 7.0.0" + }, + "peerDependencies": { + "@swc/core": "*" + } + }, + "node_modules/@swc/types": { + "version": "0.1.25", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.25.tgz", + "integrity": "sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@swc/counter": "^0.1.3" + } + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -2533,48 +1614,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.4.14", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.14.tgz", - "integrity": "sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.27.7", - "@babel/helper-define-polyfill-provider": "^0.6.5", - "semver": "^6.3.1" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.13.0.tgz", - "integrity": "sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.5", - "core-js-compat": "^3.43.0" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.5.tgz", - "integrity": "sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.5" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, "node_modules/babel-preset-current-node-syntax": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", @@ -2739,6 +1778,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/caching-transform/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/caching-transform/node_modules/write-file-atomic": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", @@ -3008,20 +2057,6 @@ "dev": true, "license": "MIT" }, - "node_modules/core-js-compat": { - "version": "3.45.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.45.1.tgz", - "integrity": "sha512-tqTt5T4PzsMIZ430XGviK4vzYSoeNJ6CXODi6c/voxOT6IZqBht5/EKaSNnYiEjjRYxjVz7DQIsOsY0XNi8PIA==", - "dev": true, - "license": "MIT", - "dependencies": { - "browserslist": "^4.25.3" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, "node_modules/create-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", @@ -3430,16 +2465,6 @@ "node": ">=4" } }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -3574,6 +2599,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/find-cache-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/find-file-up": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/find-file-up/-/find-file-up-0.1.3.tgz", @@ -4278,6 +3313,16 @@ "node": ">=8" } }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/istanbul-lib-processinfo": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.3.tgz", @@ -4912,19 +3957,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/jest-util": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", @@ -5094,6 +4126,13 @@ "node": ">=6" } }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true, + "license": "MIT" + }, "node_modules/jsonfile": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", @@ -5154,13 +4193,6 @@ "dev": true, "license": "MIT" }, - "node_modules/lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", - "dev": true, - "license": "MIT" - }, "node_modules/lodash.flattendeep": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", @@ -5208,19 +4240,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/make-dir/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/makeerror": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", @@ -5488,6 +4507,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/nyc/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/nyc/node_modules/wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", @@ -5962,77 +4991,6 @@ "dev": true, "license": "MIT" }, - "node_modules/regenerate": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", - "dev": true, - "license": "MIT" - }, - "node_modules/regenerate-unicode-properties": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", - "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", - "dev": true, - "license": "MIT", - "dependencies": { - "regenerate": "^1.4.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regexpu-core": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz", - "integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==", - "dev": true, - "license": "MIT", - "dependencies": { - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.2.0", - "regjsgen": "^0.8.0", - "regjsparser": "^0.12.0", - "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regjsgen": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/regjsparser": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz", - "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "jsesc": "~3.0.2" - }, - "bin": { - "regjsparser": "bin/parser" - } - }, - "node_modules/regjsparser/node_modules/jsesc": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", - "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/release-zalgo": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", @@ -6166,13 +5124,16 @@ "license": "MIT" }, "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/set-blocking": { @@ -6294,6 +5255,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/spawn-wrap/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/spawnd": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/spawnd/-/spawnd-5.0.0.tgz", @@ -6513,6 +5484,13 @@ "is-typedarray": "^1.0.0" } }, + "node_modules/underscore": { + "version": "1.13.8", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.8.tgz", + "integrity": "sha512-DXtD3ZtEQzc7M8m4cXotyHR+FAS18C64asBYY5vqZexfYryNNnDc02W4hKg3rdQuqOYas1jkseX0+nZXjTXnvQ==", + "dev": true, + "license": "MIT" + }, "node_modules/undici": { "version": "7.15.0", "resolved": "https://registry.npmjs.org/undici/-/undici-7.15.0.tgz", @@ -6530,50 +5508,6 @@ "dev": true, "license": "MIT" }, - "node_modules/unicode-canonical-property-names-ecmascript": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", - "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", - "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "unicode-canonical-property-names-ecmascript": "^2.0.0", - "unicode-property-aliases-ecmascript": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-value-ecmascript": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", - "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-property-aliases-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", - "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/universalify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", From aa8403ae2e1eac6b60ed90020606ffabedaeaf5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Mon, 9 Mar 2026 14:13:43 +0100 Subject: [PATCH 151/166] rename `tools/modern-tests` to `tools/e2e-tests` and update references --- .github/skills/testing/SKILL.md | 2 +- .github/workflows/e2e-tests.yml | 4 ++-- DEVELOPMENT.md | 4 ++-- package.json | 4 ++-- tools/unit-tests/jest.config.js | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/skills/testing/SKILL.md b/.github/skills/testing/SKILL.md index 160ba85c97..3f829e665e 100644 --- a/.github/skills/testing/SKILL.md +++ b/.github/skills/testing/SKILL.md @@ -31,7 +31,7 @@ npm run test:modern # Run all E2E tests npm run test:modern -- -t="React" # Run specific test ``` -## Modern E2E Tests (`tools/modern-tests/`) +## Modern E2E Tests (`tools/e2e-tests/`) Jest + Playwright suite for verifying modern bundler integrations (rspack). Tests cover framework skeletons and build scenarios. diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index 7bd60570ae..ed3d711da6 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -4,7 +4,7 @@ on: pull_request: paths: - 'meteor' - - 'tools/modern-tests/**' + - 'tools/e2e-tests/**' - 'packages/rspack/**' - 'packages/tools-core/**' - 'packages/babel-compiler/**' @@ -52,7 +52,7 @@ jobs: path: | ~/.npm node_modules - tools/modern-tests/node_modules + tools/e2e-tests/node_modules packages/**/.npm .meteor dev_bundle diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 58eda23621..d14f9c746b 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -148,7 +148,7 @@ Place test files next to the module they test using the `*.test.js` naming conve ### E2E tests (Jest + Playwright) -End-to-end tests in `tools/modern-tests/` validate that Meteor skeletons and bundler integrations work correctly. They create real Meteor apps, start dev servers, and assert behavior in a headless Chromium browser. +End-to-end tests in `tools/e2e-tests/` validate that Meteor skeletons and bundler integrations work correctly. They create real Meteor apps, start dev servers, and assert behavior in a headless Chromium browser. ```sh # Install dependencies (first time) @@ -161,7 +161,7 @@ npm run test:e2e npm run test:e2e -- -t="React" ``` -Each test has a corresponding app fixture in `tools/modern-tests/apps/`. See that directory for examples when adding new E2E tests. +Each test has a corresponding app fixture in `tools/e2e-tests/apps/`. See that directory for examples when adding new E2E tests. ### Self-tests (Meteor tool) diff --git a/package.json b/package.json index 9c096fcd0a..99d103e75f 100644 --- a/package.json +++ b/package.json @@ -38,8 +38,8 @@ "install:unit": "cd tools/unit-tests && npm install", "test:unit": "cd tools/unit-tests && npm test", "test:idle-bot": "node --test .github/scripts/__tests__/inactive-issues.test.js", - "install:e2e": "cd tools/modern-tests && npm install && npx playwright install --with-deps chromium chromium-headless-shell", - "test:e2e": "cd tools/modern-tests && npm test -- " + "install:e2e": "cd tools/e2e-tests && npm install && npx playwright install --with-deps chromium chromium-headless-shell", + "test:e2e": "cd tools/e2e-tests && npm test -- " }, "jshintConfig": { "esversion": 11 diff --git a/tools/unit-tests/jest.config.js b/tools/unit-tests/jest.config.js index a21cc6c896..f78071ebc9 100644 --- a/tools/unit-tests/jest.config.js +++ b/tools/unit-tests/jest.config.js @@ -10,13 +10,13 @@ module.exports = { ], testPathIgnorePatterns: [ "/node_modules/", - "/tools/modern-tests/", + "/tools/e2e-tests/", "/tools/tests/", "/packages/", "/.github/", ], modulePathIgnorePatterns: [ - "/tools/modern-tests/", + "/tools/e2e-tests/", "/tools/tests/", "/tools/static-assets/", "/npm-packages/", From 64d7070e8568c5bc13e87ebbdeef6f2eace92249 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Mon, 9 Mar 2026 15:49:07 +0100 Subject: [PATCH 152/166] update `E2E_COVERAGE.md` with expanded rspack coverage scenarios and `meteor reset` documentation --- dev/modern-tools/rspack/E2E_COVERAGE.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/dev/modern-tools/rspack/E2E_COVERAGE.md b/dev/modern-tools/rspack/E2E_COVERAGE.md index 9330e45e56..8b1a8c20fc 100644 --- a/dev/modern-tools/rspack/E2E_COVERAGE.md +++ b/dev/modern-tools/rspack/E2E_COVERAGE.md @@ -18,6 +18,7 @@ Every app and skeleton goes through these phases (unless skipped): | **Test** | `meteor test` — runs mocha test driver, verifies test rebuild | | **Test once** | `meteor test --once` — runs tests to completion, checks exit code | | **Build** | `meteor build` — verifies bundle structure (main.js, programs/server, web.browser, web.browser.legacy) | +| **Reset** | `meteor reset` — clears rspack build artifacts, caches, asset/chunk context dirs, and `.meteor/local` subdirectories | Default assertions on every run phase: build artifacts exist, page title matches, body styles render, `__rspack__` script tag is present. @@ -90,8 +91,10 @@ TypeScript with SCSS, type checking, and `.ts` rspack config. |----------------|-------| | TypeScript rspack config (`rspack.config.ts`) | All | | Custom build dir (`build`) | All | +| Custom asset/chunk context dirs (`assets`, `chunks`) | All | | SCSS styles support (`white-space: break-spaces`) | Run, Prod | | TypeScript + TSX environment detection | Run, Prod, Test, Build | +| Portable build (Meteor.isDevelopment/isProduction not defined) | Run, Prod, Build | | `TsCheckerRspackPlugin` type checking (no errors) | Run | | `.meteor/local/types` directory generated | Run | | Separate client/server test files | Test | @@ -105,6 +108,9 @@ Babel transpilation with custom module rules and `.mjs` rspack config. | What is covered | Phase | |----------------|-------| | Custom rspack config (`rspack.config.mjs`) | All | +| Custom `NODE_ENV` compilation per phase | All (env prefix) | +| Rspack mode assertion (development/production) | Run, Prod, Test, Build | +| `Meteor.isDevelopment`/`Meteor.isProduction` defines | Run, Prod, Test, Build | | Module rules for `.js`/`.jsx` files | Run, Prod, Test, Build | | Module rules for `.tsx`/`.ts`/`.mts`/`.cts`/`.mjs`/`.cjs` | Run, Prod, Test, Build | | Module rules for `.graphql`/`.gql` files | Run, Prod, Test, Build | @@ -175,14 +181,14 @@ Server-only app (no client entry point). ## Skeletons -Tested via `skeleton.test.js` using `meteor create --`. Each skeleton verifies: app creation, dev run, production run, test once, and build. +Tested via `skeleton.test.js` using `meteor create --`. Each skeleton verifies: app creation, dev run, production run, test once, build, and reset. | Skeleton | Port | Language | Extra coverage | |----------|------|----------|----------------| | angular | 3213 | TypeScript | | | apollo | 3201 | JSX | | | babel | 3212 | JSX | | -| bare | 3219 | JS | No title/style checks, no client tests | +| bare | 3219 | JS | No title/style checks, no client tests, skip build cache check | | blaze | 3202 | JS | | | chakra-ui | 3203 | JSX | No body style checks (custom UI library) | | coffeescript | 3211 | CoffeeScript | | @@ -246,6 +252,7 @@ Where each feature is tested across apps and skeletons. | Custom rspack config | react (.cjs), react-router, babel (.mjs), monorepo (.cjs), typescript (.ts) | | | Config override file | react-router, monorepo | | | Custom build dir | react, typescript | | +| Custom asset/chunk context dirs | typescript | | | Custom env vars | react (METEOR_LOCAL_DIR), react-router (METEOR_PACKAGE_DIRS) | | | Static asset bundling | react-router, monorepo | | | Less styles | react-router | | @@ -263,6 +270,9 @@ Where each feature is tested across apps and skeletons. | Monorepo layout | monorepo | | | Full-app test mode | react-router | | | Module rules override | babel | | +| Custom NODE_ENV compilation | babel | | +| Portable build (no isDev/isProd defines) | typescript | | +| `meteor reset` cleanup | all apps | all skeletons | | Skeleton creation | | all 14 skeletons | | Body style assertions | | react, tailwind (custom); most others (default) | | Custom .gitignore entries | react | | From 4aa0ff8cb4855d0d192828a23a113b1e7c49e76b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Mon, 9 Mar 2026 16:52:35 +0100 Subject: [PATCH 153/166] ensure consistent POSIX-style paths across modules --- npm-packages/meteor-rspack/lib/test.js | 12 ++++++++---- .../meteor-rspack/plugins/RequireExtenalsPlugin.js | 14 ++++++++++---- packages/tools-core/lib/meteor.js | 11 ++++++++--- 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/npm-packages/meteor-rspack/lib/test.js b/npm-packages/meteor-rspack/lib/test.js index 6ea5a04cf4..1780d114bb 100644 --- a/npm-packages/meteor-rspack/lib/test.js +++ b/npm-packages/meteor-rspack/lib/test.js @@ -2,6 +2,10 @@ const fs = require('fs'); const path = require('path'); const { createIgnoreRegex, createIgnoreGlobConfig } = require("./ignore.js"); +// Normalize a path to always use forward slashes (POSIX style). +// Module identifiers in bundled JS must use '/' regardless of OS. +const toPosix = (p) => p.replace(/\\/g, '/'); + /** * Generates eager test files dynamically * @param {Object} options - Options for generating the test file @@ -58,14 +62,14 @@ const generateEagerTestFile = ({ : "/\\.(?:test|spec)s?\\.[^.]+$/"; const content = `${ - globalImportPath ? `import '${globalImportPath}';\n\n` : "" + globalImportPath ? `import '${toPosix(globalImportPath)}';\n\n` : "" }${ excludeMeteorIgnoreRegex ? `const MeteorIgnoreRegex = ${excludeMeteorIgnoreRegex.toString()};` : "" } { - const ctx = import.meta.webpackContext('${projectDir}', { + const ctx = import.meta.webpackContext('${toPosix(projectDir)}', { recursive: true, regExp: ${regExp}, exclude: ${excludeFoldersRegex.toString()}, @@ -81,9 +85,9 @@ const generateEagerTestFile = ({ }).forEach(ctx); ${ extraEntry - ? `const extra = import.meta.webpackContext('${path.dirname( + ? `const extra = import.meta.webpackContext('${toPosix(path.dirname( extraEntry - )}', { + ))}', { recursive: false, regExp: ${new RegExp(`${path.basename(extraEntry)}$`).toString()}, mode: 'eager', diff --git a/npm-packages/meteor-rspack/plugins/RequireExtenalsPlugin.js b/npm-packages/meteor-rspack/plugins/RequireExtenalsPlugin.js index fb400a4382..51cf0958fd 100644 --- a/npm-packages/meteor-rspack/plugins/RequireExtenalsPlugin.js +++ b/npm-packages/meteor-rspack/plugins/RequireExtenalsPlugin.js @@ -10,6 +10,10 @@ const fs = require('fs'); const path = require('path'); +// Normalize a path to always use forward slashes (POSIX style). +// Module identifiers in bundled JS must use '/' regardless of OS. +const toPosix = (p) => p.replace(/\\/g, '/'); + class RequireExternalsPlugin { constructor({ filePath, @@ -46,7 +50,7 @@ class RequireExternalsPlugin { // Prepare paths this.filePath = path.resolve(process.cwd(), filePath); this.backRoot = '../'.repeat( - filePath.replace(/^\.?\/+/, '').split('/').length - 1 + filePath.replace(/^\.?[/\\]+/, '').split(/[/\\]/).length - 1 ); // Initialize funcCount based on existing helpers in the file @@ -96,14 +100,16 @@ class RequireExternalsPlugin { pkg && (path.isAbsolute(pkg) || pkg.startsWith('./') || + pkg.startsWith('.\\') || pkg.startsWith('../') || + pkg.startsWith('..\\') || !!depInfo.ext) ) { const module = this.externalsMeta.get(pkg); if (module) { - return `${this.backRoot}${module.relativeRequest}`; + return `${this.backRoot}${toPosix(module.relativeRequest)}`; } - return `${this.backRoot}${name}`; + return `${this.backRoot}${toPosix(name)}`; } return pkg; @@ -132,7 +138,7 @@ class RequireExternalsPlugin { this.externalsMeta.set(externalRequest, { originalRequest: request, externalRequest, - relativeRequest: path.join(relContext, request), + relativeRequest: toPosix(path.join(relContext, request)), }); // tell Rspack "don't bundle this, import it at runtime" diff --git a/packages/tools-core/lib/meteor.js b/packages/tools-core/lib/meteor.js index c753d16aac..c603101f29 100644 --- a/packages/tools-core/lib/meteor.js +++ b/packages/tools-core/lib/meteor.js @@ -1,6 +1,11 @@ const fs = require('fs'); const path = require('path'); -const { logError } = require('./log'); + +const { logError } = require("./log"); + +// Normalize a path to always use forward slashes (POSIX style). +// Module identifiers must use '/' regardless of OS. +const toPosix = (p) => p.replace(/\\/g, '/'); /** * Returns the current working directory of the Meteor application. @@ -107,13 +112,13 @@ export function getMeteorInitialAppEntrypoints() { ); if (fs.existsSync(htmlPath)) { - mainClientHtml = path.join(clientDir, `${clientBasename}.html`); + mainClientHtml = toPosix(path.join(clientDir, `${clientBasename}.html`)); } else { // Find first html in entry folder const files = fs.readdirSync(path.join(getMeteorAppDir(), clientDir)); const htmlFile = files.find((file) => path.extname(file) === ".html"); if (htmlFile) { - mainClientHtml = path.join(clientDir, htmlFile); + mainClientHtml = toPosix(path.join(clientDir, htmlFile)); } } } From 68698cca33e4957aec226c72e5fa7e73e8b2d2f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Fri, 13 Mar 2026 14:46:49 +0100 Subject: [PATCH 154/166] add `RSPACK_NATIVE` flag to disable dev HMR proxy in native mode --- packages/rspack/rspack_plugin.js | 5 +++++ packages/rspack/rspack_server.js | 6 +++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/rspack/rspack_plugin.js b/packages/rspack/rspack_plugin.js index 502f49ae82..73c6a6eb5c 100644 --- a/packages/rspack/rspack_plugin.js +++ b/packages/rspack/rspack_plugin.js @@ -177,6 +177,11 @@ if (isMeteorAppRun() || isMeteorAppBuild() || isMeteorAppTest()) { // Configure Meteor settings for Rspack configureMeteorForRspack(); + // Set native mode flag so the server module can skip dev proxy setup + if (isMeteorAppNative()) { + process.env.RSPACK_NATIVE = 'true'; + } + // Calculate and set the devServerPort at boot if (!process.env.RSPACK_DEVSERVER_PORT) { process.env.RSPACK_DEVSERVER_PORT = calculateDevServerPort(); diff --git a/packages/rspack/rspack_server.js b/packages/rspack/rspack_server.js index 97ca990878..9c55294738 100644 --- a/packages/rspack/rspack_server.js +++ b/packages/rspack/rspack_server.js @@ -28,7 +28,11 @@ const RSPACK_ASSETS_REGEX = new RegExp( `^\/${rspackAssetsContext}\/(.+)$`, ); -if (global?.Package?.['tools-core'] != null && Meteor.isDevelopment) { +const shouldEnableDevHMRProxy = + global?.Package?.["tools-core"] != null && + Meteor.isDevelopment && + !process.env.RSPACK_NATIVE; +if (shouldEnableDevHMRProxy) { const { shuffleString } = require('meteor/tools-core/lib/string'); const { createProxyMiddleware } = require('http-proxy-middleware'); From aea76aff0da774989ef5be33154b03c179950920 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Fri, 13 Mar 2026 17:31:10 +0100 Subject: [PATCH 155/166] add check for `isMeteorAppNative` to disable dev HMR proxy in native apps --- packages/rspack/lib/config.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/rspack/lib/config.js b/packages/rspack/lib/config.js index 5fab01f4d4..5771d25fb2 100644 --- a/packages/rspack/lib/config.js +++ b/packages/rspack/lib/config.js @@ -15,6 +15,7 @@ const { isMeteorAppDevelopment, isMeteorAppRun, isMeteorAppBuild, + isMeteorAppNative, isMeteorAppDebug, isMeteorAppTest, isMeteorAppTestFullApp, @@ -370,7 +371,7 @@ export function configureMeteorForRspack() { ensureModuleFilesExist(); // Write content to module files - if (isMeteorAppRun() && isMeteorAppDevelopment()) { + if (isMeteorAppRun() && isMeteorAppDevelopment() && !isMeteorAppNative()) { const customScriptUrl = `/__rspack__/${getBuildFilePath({ ...env, isMain: true, From 396af247f335d0815416071d50316a6604bd2c95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Fri, 13 Mar 2026 17:50:06 +0100 Subject: [PATCH 156/166] refactor TypeScript template self-test: convert to async and clean up legacy code. --- tools/tests/typescript.js | 56 ++++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/tools/tests/typescript.js b/tools/tests/typescript.js index 2828cc6a2b..6add91d12b 100644 --- a/tools/tests/typescript.js +++ b/tools/tests/typescript.js @@ -1,27 +1,29 @@ -// var selftest = require('../tool-testing/selftest.js'); -// var Sandbox = selftest.Sandbox; -// -// selftest.define("typescript template works", function () { -// const s = new Sandbox; -// -// let run = s.run("create", "--typescript", "typescript"); -// -// run.waitSecs(60); -// run.match("Created a new Meteor app in 'typescript'."); -// run.match("To run your new app"); -// -// s.cd("typescript"); -// -// run = s.run("npm", "install"); -// run.expectExit(0); -// -// run = s.run("lint"); -// run.waitSecs(60); -// run.match("[zodern:types] Exiting \"meteor lint\" early"); -// run.expectExit(0); -// -// run = s.run("npx", "tsc"); -// run.waitSecs(60); -// run.expectEnd(); -// run.expectExit(0); -// }); +var selftest = require('../tool-testing/selftest.js'); +var Sandbox = selftest.Sandbox; +console.log("--> (typescript.js-Line: 3)\n Sandbox: ", Sandbox); + +selftest.define("typescript template works", async function () { + const s = new Sandbox; + await s.init(); + + let run = s.run("create", "--typescript", "typescript"); + + run.waitSecs(60); + await run.match("Created a new Meteor app in 'typescript'."); + await run.match("To run your new app"); + + s.cd("typescript"); + + run = s.run("npm", "install"); + run.expectExit(0); + + run = s.run("lint"); + run.waitSecs(60); + await run.match("[zodern:types] Exiting \"meteor lint\" early"); + run.expectExit(0); + + run = s.run("npx", "tsc"); + run.waitSecs(60); + run.expectEnd(); + run.expectExit(0); +}); From 1bd0fd4f6a10a0d7bd231262e160c2a347b15641 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Fri, 13 Mar 2026 17:52:07 +0100 Subject: [PATCH 157/166] refactor TypeScript template self-test: update to use `await` syntax --- tools/tests/typescript.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/tests/typescript.js b/tools/tests/typescript.js index 6add91d12b..a6d34a4c2f 100644 --- a/tools/tests/typescript.js +++ b/tools/tests/typescript.js @@ -3,7 +3,7 @@ var Sandbox = selftest.Sandbox; console.log("--> (typescript.js-Line: 3)\n Sandbox: ", Sandbox); selftest.define("typescript template works", async function () { - const s = new Sandbox; + const s = new Sandbox(); await s.init(); let run = s.run("create", "--typescript", "typescript"); @@ -15,15 +15,15 @@ selftest.define("typescript template works", async function () { s.cd("typescript"); run = s.run("npm", "install"); - run.expectExit(0); + await run.expectExit(0); run = s.run("lint"); run.waitSecs(60); - await run.match("[zodern:types] Exiting \"meteor lint\" early"); - run.expectExit(0); + await run.match('[zodern:types] Exiting "meteor lint" early'); + await run.expectExit(0); run = s.run("npx", "tsc"); run.waitSecs(60); - run.expectEnd(); - run.expectExit(0); + await run.expectEnd(); + await run.expectExit(0); }); From fe44650406e9e24eb0b267e560d9d66ebdf7eb6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Fri, 13 Mar 2026 18:02:23 +0100 Subject: [PATCH 158/166] remove trailing breakline --- tools/static-assets/skel-typescript/tsconfig.json | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/static-assets/skel-typescript/tsconfig.json b/tools/static-assets/skel-typescript/tsconfig.json index deb12b4a50..aff98434d9 100644 --- a/tools/static-assets/skel-typescript/tsconfig.json +++ b/tools/static-assets/skel-typescript/tsconfig.json @@ -38,7 +38,6 @@ "esModuleInterop": true, "preserveSymlinks": true, "skipLibCheck": true - }, "exclude": [ "./.meteor/**", From 7538fd13914268841b5ae32bfa84554f3818b6e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Fri, 13 Mar 2026 18:05:22 +0100 Subject: [PATCH 159/166] remove debug `console.log` from typescript.js --- tools/tests/typescript.js | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/tests/typescript.js b/tools/tests/typescript.js index a6d34a4c2f..7933ee5265 100644 --- a/tools/tests/typescript.js +++ b/tools/tests/typescript.js @@ -1,6 +1,5 @@ var selftest = require('../tool-testing/selftest.js'); var Sandbox = selftest.Sandbox; -console.log("--> (typescript.js-Line: 3)\n Sandbox: ", Sandbox); selftest.define("typescript template works", async function () { const s = new Sandbox(); From 753b22e6dfb71c8e28fadbfabc9dad35b2a5c5e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 17 Mar 2026 17:27:44 +0100 Subject: [PATCH 160/166] add support for `swc.config.ts` in docs and improve SWC configuration handling --- npm-packages/meteor-rspack/index.d.ts | 13 +++++- .../meteor-rspack/lib/meteorRspackHelpers.js | 36 +++++++++++++-- npm-packages/meteor-rspack/rspack.config.js | 11 +++-- .../e2e-tests/apps/typescript/client/main.tsx | 1 - .../apps/typescript/imports/ui/App.tsx | 1 - .../apps/typescript/imports/ui/Hello.tsx | 2 +- .../apps/typescript/imports/ui/Info.tsx | 1 - tools/e2e-tests/apps/typescript/package.json | 1 + tools/e2e-tests/apps/typescript/swc.config.ts | 13 ++++++ .../meteor-bundler-optimizations.md | 4 +- .../rspack-bundler-integration.md | 46 +++++++++++++++++-- 11 files changed, 112 insertions(+), 17 deletions(-) create mode 100644 tools/e2e-tests/apps/typescript/swc.config.ts diff --git a/npm-packages/meteor-rspack/index.d.ts b/npm-packages/meteor-rspack/index.d.ts index 449e6d1a85..20a17a1e7d 100644 --- a/npm-packages/meteor-rspack/index.d.ts +++ b/npm-packages/meteor-rspack/index.d.ts @@ -57,10 +57,21 @@ type MeteorEnv = Record & { */ splitVendorChunk: () => Record; /** - * Extend Rspack SWC loader config. + * Extend the SWC loader config by smart-merging custom options on top of + * Meteor's defaults. Only the properties you specify are overridden; + * everything else is preserved. + * @param swcConfig - SWC loader options to merge with defaults * @returns A config object with SWC loader config */ extendSwcConfig: (swcConfig: SwcLoaderOptions) => Record; + /** + * Replace the SWC loader config entirely, discarding Meteor's defaults. + * Use this when you need full control over SWC options and don't want any + * automatic merging with Meteor's built-in configuration. + * @param swcConfig - Complete SWC loader options (replaces defaults) + * @returns A config object with SWC loader config + */ + replaceSwcConfig: (swcConfig: SwcLoaderOptions) => Record; /** * Extend Rspack configs. * @returns A config object with merged configs diff --git a/npm-packages/meteor-rspack/lib/meteorRspackHelpers.js b/npm-packages/meteor-rspack/lib/meteorRspackHelpers.js index d287773000..2fbbac3027 100644 --- a/npm-packages/meteor-rspack/lib/meteorRspackHelpers.js +++ b/npm-packages/meteor-rspack/lib/meteorRspackHelpers.js @@ -132,10 +132,14 @@ function splitVendorChunk() { } /** - * Extend SWC loader config - * Usage: extendSwcConfig() + * Extend SWC loader config by smart-merging custom options on top of Meteor's + * defaults (via `mergeSplitOverlap`). Only the properties you specify are + * overridden; everything else is preserved. * - * @returns {Record} `{ meteorRspackConfigX: { optimization: { ... } } }` + * Usage: Meteor.extendSwcConfig({ jsc: { parser: { decorators: true } } }) + * + * @param {object} swcConfig - SWC loader options to merge with defaults + * @returns {Record} config fragment for spreading into rspack config */ function extendSwcConfig(swcConfig) { return prepareMeteorRspackConfig({ @@ -152,6 +156,31 @@ function extendSwcConfig(swcConfig) { }); } +/** + * Replace the SWC loader config entirely, discarding Meteor's defaults. + * Use this when you need full control over SWC options and don't want any + * automatic merging with Meteor's built-in configuration. + * + * Usage: Meteor.replaceSwcConfig({ jsc: { parser: { syntax: 'typescript' }, target: 'es2020' } }) + * + * @param {object} swcConfig - Complete SWC loader options (replaces defaults) + * @returns {Record} config fragment for spreading into rspack config + */ +function replaceSwcConfig(swcConfig) { + return prepareMeteorRspackConfig({ + module: { + rules: [ + { + test: /\.(?:[mc]?js|jsx|[mc]?ts|tsx)$/i, + exclude: /node_modules|\.meteor\/local/, + loader: 'builtin:swc-loader', + options: swcConfig, + }, + ], + }, + }); +} + /** * Signal that `Meteor.isDevelopment` and `Meteor.isProduction` should be omitted * from DefinePlugin, making the bundle portable across Meteor environments. @@ -227,6 +256,7 @@ module.exports = { setCache, splitVendorChunk, extendSwcConfig, + replaceSwcConfig, makeWebNodeBuiltinsAlias, disablePlugins, outputMeteorRspack, diff --git a/npm-packages/meteor-rspack/rspack.config.js b/npm-packages/meteor-rspack/rspack.config.js index 0bfb343543..55c2bfbad9 100644 --- a/npm-packages/meteor-rspack/rspack.config.js +++ b/npm-packages/meteor-rspack/rspack.config.js @@ -19,6 +19,7 @@ const { setCache, splitVendorChunk, extendSwcConfig, + replaceSwcConfig, makeWebNodeBuiltinsAlias, disablePlugins, outputMeteorRspack, @@ -325,7 +326,11 @@ module.exports = async function (inMeteor = {}, argv = {}) { setCache(!!enabled, enabled === "memory" ? undefined : cacheStrategy); Meteor.splitVendorChunk = () => splitVendorChunk(); Meteor.extendSwcConfig = (customSwcConfig) => - extendSwcConfig(customSwcConfig); + extendSwcConfig( + mergeSplitOverlap(Meteor.swcConfigOptions, customSwcConfig) + ); + Meteor.replaceSwcConfig = (customSwcConfig) => + replaceSwcConfig(customSwcConfig); Meteor.extendConfig = (...configs) => mergeSplitOverlap(...configs); Meteor.disablePlugins = (matchers) => prepareMeteorRspackConfig({ @@ -817,9 +822,9 @@ module.exports = async function (inMeteor = {}, argv = {}) { delete config["meteor.enablePortableBuild"]; - // if (Meteor.isDebug || Meteor.isVerbose) { + if (Meteor.isDebug || Meteor.isVerbose) { console.log("Config:", inspect(config, { depth: null, colors: true })); - // } + } // Check if lazyCompilation is enabled and warn the user if ( diff --git a/tools/e2e-tests/apps/typescript/client/main.tsx b/tools/e2e-tests/apps/typescript/client/main.tsx index e576e1b803..4eb40f49d1 100644 --- a/tools/e2e-tests/apps/typescript/client/main.tsx +++ b/tools/e2e-tests/apps/typescript/client/main.tsx @@ -1,4 +1,3 @@ -import React from 'react'; import { createRoot } from 'react-dom/client'; import { Meteor } from 'meteor/meteor'; import { App } from '@ui/App'; diff --git a/tools/e2e-tests/apps/typescript/imports/ui/App.tsx b/tools/e2e-tests/apps/typescript/imports/ui/App.tsx index 52f71448cc..94679c5547 100644 --- a/tools/e2e-tests/apps/typescript/imports/ui/App.tsx +++ b/tools/e2e-tests/apps/typescript/imports/ui/App.tsx @@ -1,4 +1,3 @@ -import React from 'react'; import './Global.scss'; import { Hello } from './Hello'; import { Info } from './Info'; diff --git a/tools/e2e-tests/apps/typescript/imports/ui/Hello.tsx b/tools/e2e-tests/apps/typescript/imports/ui/Hello.tsx index 15e0f185ac..527d5af607 100644 --- a/tools/e2e-tests/apps/typescript/imports/ui/Hello.tsx +++ b/tools/e2e-tests/apps/typescript/imports/ui/Hello.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import { useState } from 'react'; export const Hello = () => { const [counter, setCounter] = useState(0); diff --git a/tools/e2e-tests/apps/typescript/imports/ui/Info.tsx b/tools/e2e-tests/apps/typescript/imports/ui/Info.tsx index 23cb8f07a3..809fbc6716 100644 --- a/tools/e2e-tests/apps/typescript/imports/ui/Info.tsx +++ b/tools/e2e-tests/apps/typescript/imports/ui/Info.tsx @@ -1,4 +1,3 @@ -import React from "react"; import { useFind, useSubscribe } from "meteor/react-meteor-data"; import { LinksCollection, Link } from "../api/links"; diff --git a/tools/e2e-tests/apps/typescript/package.json b/tools/e2e-tests/apps/typescript/package.json index d1ac310541..f0359b6094 100644 --- a/tools/e2e-tests/apps/typescript/package.json +++ b/tools/e2e-tests/apps/typescript/package.json @@ -15,6 +15,7 @@ "react-dom": "^18.2.0" }, "devDependencies": { + "@swc/core": "^1.15.18", "@types/meteor": "^2.9.9", "@types/mocha": "^8.2.3", "@types/node": "^22.10.6", diff --git a/tools/e2e-tests/apps/typescript/swc.config.ts b/tools/e2e-tests/apps/typescript/swc.config.ts new file mode 100644 index 0000000000..699a33a40d --- /dev/null +++ b/tools/e2e-tests/apps/typescript/swc.config.ts @@ -0,0 +1,13 @@ +import type { Config } from "@swc/core"; + +const config: Config = { + jsc: { + transform: { + react: { + runtime: "automatic", + }, + }, + }, +}; + +export default config; diff --git a/v3-docs/docs/about/modern-build-stack/meteor-bundler-optimizations.md b/v3-docs/docs/about/modern-build-stack/meteor-bundler-optimizations.md index 6c76240f18..6c3d84cb53 100644 --- a/v3-docs/docs/about/modern-build-stack/meteor-bundler-optimizations.md +++ b/v3-docs/docs/about/modern-build-stack/meteor-bundler-optimizations.md @@ -165,7 +165,9 @@ You can use `.swcrc` config in the root of your project to describe specific [SW You can also configure other options using the `.swcrc` format. For custom SWC configs, see the [SWC configuration API](https://swc.rs/docs/configuration/compilation). -Use `swc.config.js` in your project root for dynamic configuration. Meteor will import and apply the SWC config automatically. This lets you choose a config based on environment variables or other runtime factors. +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. If you prefer TypeScript, `swc.config.ts` is also supported, Meteor will transpile and load it automatically. + +Meteor checks for config files in this order: `.swcrc` > `swc.config.js` > `swc.config.ts`. Only the first one found is used. You can also review these migration topics that use custom `.swcrc` configs: diff --git a/v3-docs/docs/about/modern-build-stack/rspack-bundler-integration.md b/v3-docs/docs/about/modern-build-stack/rspack-bundler-integration.md index 35a34f1295..f053475cf8 100644 --- a/v3-docs/docs/about/modern-build-stack/rspack-bundler-integration.md +++ b/v3-docs/docs/about/modern-build-stack/rspack-bundler-integration.md @@ -157,7 +157,8 @@ You can use flags to control the final configuration based on the environment. T | `compileWithRspack` | function | Forces given npm deps ([Condition](https://rspack.rs/config/module#condition)[]) to be compiled by Rspack | | `setCache` | function | Enables or disables cache. Accepts true (persistent, default), false, or 'memory' | | `splitVendorChunk` | function | Splits vendor libraries so they are automatically served from a separate chunk | -| `extendSwcConfig` | function | Extends the [SWC loader configuration](https://rspack.rs/guide/features/builtin-swc-loader#options) to apply only to the app code | +| `extendSwcConfig` | function | Smart-merges custom options into Meteor's default [SWC loader configuration](https://rspack.rs/guide/features/builtin-swc-loader#options), applying only to app code | +| `replaceSwcConfig` | function | Replaces Meteor's default [SWC loader configuration](https://rspack.rs/guide/features/builtin-swc-loader#options) entirely with the provided options, applying only to app code | | `extendConfig` | function | Extends the config by applying merged object configs | | `enablePortableBuild` | function | Omits `Meteor.isDevelopment` and `Meteor.isProduction` from the bundle, making it portable across environments | @@ -628,27 +629,62 @@ module.exports = defineConfig(Meteor => ({ This is a quick configuration for split chunks all within `node_modules` as a `vendor` chunk, if you need more control you can use the [official Rspack split chunks integration guide](https://rspack.rs/guide/optimization/code-splitting#splitchunksplugin). -### Extending SWC config +### Customizing SWC config Rspack uses the SWC configuration to transpile your app code. By default, it inherits any settings from the `.swcrc` file, which also [impacts how Meteor transpiles core and package code](meteor-bundler-optimizations.md#custom-swcrc). -If you want a configuration to apply only to your app code, you can extend the SWC setup using the `Meteor.extendSwcConfig` helper: +If you want a configuration to apply only to your app code (not Meteor packages), two helpers are available: + +#### `Meteor.extendSwcConfig` - smart merge (recommended) + +Merges your custom options on top of Meteor's defaults using a deep merge strategy (the same used by `Meteor.extendConfig`). Only the properties you specify are overridden; everything else (parser settings, React refresh, external helpers, etc) is preserved. ```js const { defineConfig } = require('@meteorjs/rspack'); module.exports = defineConfig(Meteor => ({ - // Extend SWC config + // Add decorator support while keeping all Meteor defaults ...Meteor.extendSwcConfig({ jsc: { parser: { - syntax: 'typescript', + decorators: true, }, }, }), })); ``` +#### `Meteor.replaceSwcConfig` - full replacement + +Discards Meteor's defaults entirely and uses the provided config as-is. Use this when you need complete control over SWC and the smart merge doesn't fit your use case. + +```js +const { defineConfig } = require('@meteorjs/rspack'); + +module.exports = defineConfig(Meteor => ({ + // Full SWC config — no Meteor defaults applied + ...Meteor.replaceSwcConfig({ + jsc: { + parser: { + syntax: 'typescript', + tsx: true, + decorators: true, + }, + target: 'es2020', + transform: { + react: { + runtime: 'automatic', + }, + }, + }, + }), +})); +``` + +:::warning +When using `replaceSwcConfig`, you are responsible for providing all necessary SWC options. Features like React refresh, external helpers and parser defaults that Meteor configures (`Meteor.swcConfigOptions`) will not be applied unless you include them yourself. +::: + ### Interop for Default Imports Meteor originally handled default imports from CommonJS modules automatically. This allowed you to write: From d307c9c1ebcb1dce94fce439595a0bc18590e0ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 17 Mar 2026 17:41:35 +0100 Subject: [PATCH 161/166] update `E2E_COVERAGE.md` to document `swc.config.ts` and extended TypeScript SWC scenarios --- dev/modern-tools/rspack/E2E_COVERAGE.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/dev/modern-tools/rspack/E2E_COVERAGE.md b/dev/modern-tools/rspack/E2E_COVERAGE.md index 8b1a8c20fc..5c3fc81315 100644 --- a/dev/modern-tools/rspack/E2E_COVERAGE.md +++ b/dev/modern-tools/rspack/E2E_COVERAGE.md @@ -85,16 +85,19 @@ Full Blaze app (with `imports/` structure for tests). ### typescript -TypeScript with SCSS, type checking, and `.ts` rspack config. +TypeScript with SCSS, type checking, `.ts` rspack config, and `.ts` SWC config. | What is covered | Phase | |----------------|-------| | TypeScript rspack config (`rspack.config.ts`) | All | +| TypeScript SWC config (`swc.config.ts`) with automatic JSX runtime | All | +| `@swc/core` used to transpile `.ts` config at load time | All | | Custom build dir (`build`) | All | | Custom asset/chunk context dirs (`assets`, `chunks`) | All | | SCSS styles support (`white-space: break-spaces`) | Run, Prod | | TypeScript + TSX environment detection | Run, Prod, Test, Build | | Portable build (Meteor.isDevelopment/isProduction not defined) | Run, Prod, Build | +| `Meteor.extendSwcConfig` with path aliases (`@ui/*`, `@api/*`) | All | | `TsCheckerRspackPlugin` type checking (no errors) | Run | | `.meteor/local/types` directory generated | Run | | Separate client/server test files | Test | @@ -232,11 +235,12 @@ Several apps import specific npm packages to verify that Meteor + Rspack handles | `@apollo/server/express4` | ESM subpath export (middleware from deep path) | | `graphql` | Peer dependency, dual CJS/ESM package | -### typescript (`apps/typescript/rspack.config.ts`) +### typescript (`apps/typescript/rspack.config.ts`, `apps/typescript/swc.config.ts`) | Package | Reason | |---------|--------| | `node:module` (`createRequire`) | Node.js built-in in a `.ts` config file — tests CJS interop via `createRequire(import.meta.url)` in an ESM context | +| `@swc/core` | Used at load time to transpile `swc.config.ts` from TypeScript before applying the SWC configuration | --- @@ -250,6 +254,7 @@ Where each feature is tested across apps and skeletons. | HMR disabled (prod) | all apps with HMR | | | HMR incompatible | blaze, full-blaze | | | Custom rspack config | react (.cjs), react-router, babel (.mjs), monorepo (.cjs), typescript (.ts) | | +| Custom SWC config (.ts) | typescript | | | Config override file | react-router, monorepo | | | Custom build dir | react, typescript | | | Custom asset/chunk context dirs | typescript | | @@ -272,6 +277,7 @@ Where each feature is tested across apps and skeletons. | Module rules override | babel | | | Custom NODE_ENV compilation | babel | | | Portable build (no isDev/isProd defines) | typescript | | +| `Meteor.extendSwcConfig` (path aliases) | typescript | | | `meteor reset` cleanup | all apps | all skeletons | | Skeleton creation | | all 14 skeletons | | Body style assertions | | react, tailwind (custom); most others (default) | From 7a29dd9aa0279a596c24f1e4bc02f823aa63eca0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 17 Mar 2026 17:43:24 +0100 Subject: [PATCH 162/166] update `E2E_COVERAGE.md` to clarify `@swc/core` usage as type-only import in `swc.config.ts` --- dev/modern-tools/rspack/E2E_COVERAGE.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/modern-tools/rspack/E2E_COVERAGE.md b/dev/modern-tools/rspack/E2E_COVERAGE.md index 5c3fc81315..1199c73a50 100644 --- a/dev/modern-tools/rspack/E2E_COVERAGE.md +++ b/dev/modern-tools/rspack/E2E_COVERAGE.md @@ -91,7 +91,7 @@ TypeScript with SCSS, type checking, `.ts` rspack config, and `.ts` SWC config. |----------------|-------| | TypeScript rspack config (`rspack.config.ts`) | All | | TypeScript SWC config (`swc.config.ts`) with automatic JSX runtime | All | -| `@swc/core` used to transpile `.ts` config at load time | All | +| `@swc/core` type-only import for SWC config typings | All | | Custom build dir (`build`) | All | | Custom asset/chunk context dirs (`assets`, `chunks`) | All | | SCSS styles support (`white-space: break-spaces`) | Run, Prod | @@ -240,7 +240,7 @@ Several apps import specific npm packages to verify that Meteor + Rspack handles | Package | Reason | |---------|--------| | `node:module` (`createRequire`) | Node.js built-in in a `.ts` config file — tests CJS interop via `createRequire(import.meta.url)` in an ESM context | -| `@swc/core` | Used at load time to transpile `swc.config.ts` from TypeScript before applying the SWC configuration | +| `@swc/core` | Type-only import (`import type { Config }`) — provides typings for `swc.config.ts`, stripped at compile time | --- From 79f1c05d6de5626a4803341e12ec82596eaefea9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Thu, 19 Mar 2026 10:16:06 +0100 Subject: [PATCH 163/166] re-run checks From f61bcbe4fd51627ce64a43f56d8fb3b29dc48711 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Thu, 19 Mar 2026 11:34:02 +0100 Subject: [PATCH 164/166] improve error handling in E2E test assertion for missing files --- tools/e2e-tests/assertions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/e2e-tests/assertions.js b/tools/e2e-tests/assertions.js index 437bdec437..5abaad4daf 100644 --- a/tools/e2e-tests/assertions.js +++ b/tools/e2e-tests/assertions.js @@ -102,7 +102,7 @@ export async function assertFileExist(tempDir, filePath, options = {}) { return checkFile(); } // If we've exceeded the timeout, fail the test - expect(fileExists).toBe(true); + throw new Error(`Expected file to exist but it was not found: ${fullPath}`); return false; } From 54f4b478f313dc500810d3d44417ab797a81ea50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Thu, 19 Mar 2026 11:57:13 +0100 Subject: [PATCH 165/166] [Rspack][CI] Add warning for NPM_LINK_RSPACK in tests and CI logs --- .github/workflows/e2e-tests.yml | 1 + tools/e2e-tests/test-helpers.js | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index ed3d711da6..ce29d07dfb 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -79,6 +79,7 @@ jobs: echo "Current branch: ${{ github.head_ref || github.ref_name }}" if [[ "${{ github.head_ref || github.ref_name }}" == release-* ]]; then echo "NPM_LINK_RSPACK=false" >> $GITHUB_ENV + echo "::warning::NPM_LINK_RSPACK=false on release branch. E2E tests will install @meteorjs/rspack from npm — make sure the latest version is published or tests may fail." fi - name: Prepare Meteor diff --git a/tools/e2e-tests/test-helpers.js b/tools/e2e-tests/test-helpers.js index f1728472d5..52c11201f3 100644 --- a/tools/e2e-tests/test-helpers.js +++ b/tools/e2e-tests/test-helpers.js @@ -35,6 +35,12 @@ const isCI = process.env.GITHUB_ACTIONS === "true"; // Link local npm-packages/meteor-rspack so tests run against the latest dev version. // Set NPM_LINK_RSPACK=false to disable. const npmLinkLocalRspack = process.env.NPM_LINK_RSPACK !== 'false'; +if (!npmLinkLocalRspack) { + console.warn( + '\x1b[33m⚠ NPM_LINK_RSPACK=false — tests will install @meteorjs/rspack from npm.\n' + + ' If CI fails, ensure the latest @meteorjs/rspack version has been published.\x1b[0m' + ); +} const WAIT_ON = isCI ? 2000 : 500; From 366bf354a25acc9670085441dc9bbdefebeffa93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Thu, 19 Mar 2026 12:11:11 +0100 Subject: [PATCH 166/166] bump `@meteorjs/rspack` to version `1.1.0-beta.31` in all templates and constants --- npm-packages/meteor-rspack/package-lock.json | 4 ++-- npm-packages/meteor-rspack/package.json | 2 +- packages/rspack/lib/constants.js | 2 +- tools/e2e-tests/apps/solid/package.json | 2 +- tools/e2e-tests/apps/svelte/package.json | 2 +- tools/e2e-tests/apps/vue/package.json | 2 +- tools/static-assets/skel-angular/package.json | 2 +- tools/static-assets/skel-apollo/package.json | 2 +- tools/static-assets/skel-babel/package.json | 2 +- tools/static-assets/skel-blaze/package.json | 2 +- tools/static-assets/skel-chakra-ui/package.json | 2 +- tools/static-assets/skel-coffeescript/package.json | 2 +- tools/static-assets/skel-full/package.json | 2 +- tools/static-assets/skel-react/package.json | 2 +- tools/static-assets/skel-solid/package.json | 2 +- tools/static-assets/skel-svelte/package.json | 2 +- tools/static-assets/skel-tailwind/package.json | 2 +- tools/static-assets/skel-typescript/package.json | 2 +- tools/static-assets/skel-vue/package.json | 2 +- 19 files changed, 20 insertions(+), 20 deletions(-) diff --git a/npm-packages/meteor-rspack/package-lock.json b/npm-packages/meteor-rspack/package-lock.json index 0919f2882e..7792758d69 100644 --- a/npm-packages/meteor-rspack/package-lock.json +++ b/npm-packages/meteor-rspack/package-lock.json @@ -1,12 +1,12 @@ { "name": "@meteorjs/rspack", - "version": "1.1.0-beta.30", + "version": "1.1.0-beta.31", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@meteorjs/rspack", - "version": "1.1.0-beta.30", + "version": "1.1.0-beta.31", "license": "ISC", "dependencies": { "fast-deep-equal": "^3.1.3", diff --git a/npm-packages/meteor-rspack/package.json b/npm-packages/meteor-rspack/package.json index 3d530801ac..2bcbb679c2 100644 --- a/npm-packages/meteor-rspack/package.json +++ b/npm-packages/meteor-rspack/package.json @@ -1,6 +1,6 @@ { "name": "@meteorjs/rspack", - "version": "1.1.0-beta.30", + "version": "1.1.0-beta.31", "description": "Configuration logic for using Rspack in Meteor projects", "main": "index.js", "type": "commonjs", diff --git a/packages/rspack/lib/constants.js b/packages/rspack/lib/constants.js index 718ca52301..1341f54ce7 100644 --- a/packages/rspack/lib/constants.js +++ b/packages/rspack/lib/constants.js @@ -7,7 +7,7 @@ import path from 'path'; export const DEFAULT_RSPACK_VERSION = '1.7.1'; -export const DEFAULT_METEOR_RSPACK_VERSION = '1.1.0-beta.30'; +export const DEFAULT_METEOR_RSPACK_VERSION = '1.1.0-beta.31'; export const DEFAULT_METEOR_RSPACK_REACT_HMR_VERSION = '1.4.3'; diff --git a/tools/e2e-tests/apps/solid/package.json b/tools/e2e-tests/apps/solid/package.json index 104f5258ef..5ce93de638 100644 --- a/tools/e2e-tests/apps/solid/package.json +++ b/tools/e2e-tests/apps/solid/package.json @@ -22,7 +22,7 @@ "modern": true }, "devDependencies": { - "@meteorjs/rspack": "^0.0.29", + "@meteorjs/rspack": "^1.1.0-beta.31", "@rspack/cli": "^1.4.8", "@rspack/core": "^1.4.8", "babel-loader": "10.0.0", diff --git a/tools/e2e-tests/apps/svelte/package.json b/tools/e2e-tests/apps/svelte/package.json index a9873d4aa5..cba0d4e7a0 100644 --- a/tools/e2e-tests/apps/svelte/package.json +++ b/tools/e2e-tests/apps/svelte/package.json @@ -13,7 +13,7 @@ "meteor-node-stubs": "^1.2.12" }, "devDependencies": { - "@meteorjs/rspack": "^0.0.28", + "@meteorjs/rspack": "^1.1.0-beta.31", "@rspack/cli": "^1.4.8", "@rspack/core": "^1.4.8", "playwright": "1.58.0", diff --git a/tools/e2e-tests/apps/vue/package.json b/tools/e2e-tests/apps/vue/package.json index 4d13d28fd1..31c39838ad 100644 --- a/tools/e2e-tests/apps/vue/package.json +++ b/tools/e2e-tests/apps/vue/package.json @@ -17,7 +17,7 @@ "vue-router": "^4.2.5" }, "devDependencies": { - "@meteorjs/rspack": "^1.0.0-beta.1", + "@meteorjs/rspack": "^1.1.0-beta.31", "@rspack/cli": "^1.4.8", "@rspack/core": "^1.4.8", "@tailwindcss/postcss": "^4.1.12", diff --git a/tools/static-assets/skel-angular/package.json b/tools/static-assets/skel-angular/package.json index 1140972385..fa3105b024 100644 --- a/tools/static-assets/skel-angular/package.json +++ b/tools/static-assets/skel-angular/package.json @@ -22,7 +22,7 @@ }, "devDependencies": { "@angular/compiler-cli": "^20.0.0", - "@meteorjs/rspack": "^1.0.0-beta.1", + "@meteorjs/rspack": "^1.1.0-beta.31", "@nx/angular-rspack": "^21.1.0", "@rsdoctor/rspack-plugin": "^1.2.3", "@rspack/cli": "^1.7.1", diff --git a/tools/static-assets/skel-apollo/package.json b/tools/static-assets/skel-apollo/package.json index 50c58e0f22..5fd18f2131 100644 --- a/tools/static-assets/skel-apollo/package.json +++ b/tools/static-assets/skel-apollo/package.json @@ -20,7 +20,7 @@ "devDependencies": { "@graphql-tools/webpack-loader": "^7.0.0", "@rsdoctor/rspack-plugin": "^1.2.3", - "@meteorjs/rspack": "^1.0.0-beta.1", + "@meteorjs/rspack": "^1.1.0-beta.31", "@rspack/cli": "^1.7.1", "@rspack/core": "^1.7.1", "@rspack/plugin-react-refresh": "^1.4.3", diff --git a/tools/static-assets/skel-babel/package.json b/tools/static-assets/skel-babel/package.json index 647024e2c8..8c9c45753f 100644 --- a/tools/static-assets/skel-babel/package.json +++ b/tools/static-assets/skel-babel/package.json @@ -17,7 +17,7 @@ "devDependencies": { "@babel/preset-env": "^7.28.3", "@babel/preset-react": "^7.23.3", - "@meteorjs/rspack": "^1.0.0-beta.1", + "@meteorjs/rspack": "^1.1.0-beta.31", "@rsdoctor/rspack-plugin": "^1.2.3", "@rspack/cli": "^1.7.1", "@rspack/core": "^1.7.1", diff --git a/tools/static-assets/skel-blaze/package.json b/tools/static-assets/skel-blaze/package.json index 4d1b0a785e..6dd838aaf6 100644 --- a/tools/static-assets/skel-blaze/package.json +++ b/tools/static-assets/skel-blaze/package.json @@ -14,7 +14,7 @@ "meteor-node-stubs": "^1.2.12" }, "devDependencies": { - "@meteorjs/rspack": "^1.0.0-beta.1", + "@meteorjs/rspack": "^1.1.0-beta.31", "@rsdoctor/rspack-plugin": "^1.2.3", "@rspack/cli": "^1.7.1", "@rspack/core": "^1.7.1", diff --git a/tools/static-assets/skel-chakra-ui/package.json b/tools/static-assets/skel-chakra-ui/package.json index 6e3e3f7c8d..a30ef9f180 100644 --- a/tools/static-assets/skel-chakra-ui/package.json +++ b/tools/static-assets/skel-chakra-ui/package.json @@ -21,7 +21,7 @@ "react-dom": "^18.2.0" }, "devDependencies": { - "@meteorjs/rspack": "^1.0.0-beta.1", + "@meteorjs/rspack": "^1.1.0-beta.31", "@rsdoctor/rspack-plugin": "^1.2.3", "@rspack/cli": "^1.7.1", "@rspack/core": "^1.7.1", diff --git a/tools/static-assets/skel-coffeescript/package.json b/tools/static-assets/skel-coffeescript/package.json index af84b38f91..6f2ebbce93 100644 --- a/tools/static-assets/skel-coffeescript/package.json +++ b/tools/static-assets/skel-coffeescript/package.json @@ -15,7 +15,7 @@ "react-dom": "^18.2.0" }, "devDependencies": { - "@meteorjs/rspack": "^1.0.0-beta.1", + "@meteorjs/rspack": "^1.1.0-beta.31", "@rsdoctor/rspack-plugin": "^1.2.3", "@rspack/cli": "^1.7.1", "@rspack/core": "^1.7.1", diff --git a/tools/static-assets/skel-full/package.json b/tools/static-assets/skel-full/package.json index 8c77c7dbb5..e7f8bb952e 100644 --- a/tools/static-assets/skel-full/package.json +++ b/tools/static-assets/skel-full/package.json @@ -12,7 +12,7 @@ "meteor-node-stubs": "^1.2.12" }, "devDependencies": { - "@meteorjs/rspack": "^1.0.0-beta.1", + "@meteorjs/rspack": "^1.1.0-beta.31", "@rsdoctor/rspack-plugin": "^1.2.3", "@rspack/cli": "^1.7.1", "@rspack/core": "^1.7.1", diff --git a/tools/static-assets/skel-react/package.json b/tools/static-assets/skel-react/package.json index 77d0137aaf..1091b1727a 100644 --- a/tools/static-assets/skel-react/package.json +++ b/tools/static-assets/skel-react/package.json @@ -15,7 +15,7 @@ "react-dom": "^18.2.0" }, "devDependencies": { - "@meteorjs/rspack": "^1.0.0-beta.1", + "@meteorjs/rspack": "^1.1.0-beta.31", "@rsdoctor/rspack-plugin": "^1.2.3", "@rspack/cli": "^1.7.1", "@rspack/core": "^1.7.1", diff --git a/tools/static-assets/skel-solid/package.json b/tools/static-assets/skel-solid/package.json index 6e2cc4ba61..d14262c517 100644 --- a/tools/static-assets/skel-solid/package.json +++ b/tools/static-assets/skel-solid/package.json @@ -14,7 +14,7 @@ "picocolors": "^1.1.1" }, "devDependencies": { - "@meteorjs/rspack": "^1.0.0-beta.1", + "@meteorjs/rspack": "^1.1.0-beta.31", "@rsdoctor/rspack-plugin": "^1.2.3", "@rspack/cli": "^1.7.1", "@rspack/core": "^1.7.1", diff --git a/tools/static-assets/skel-svelte/package.json b/tools/static-assets/skel-svelte/package.json index 937c185741..d864cf7c23 100644 --- a/tools/static-assets/skel-svelte/package.json +++ b/tools/static-assets/skel-svelte/package.json @@ -13,7 +13,7 @@ "meteor-node-stubs": "^1.2.12" }, "devDependencies": { - "@meteorjs/rspack": "^1.0.0-beta.1", + "@meteorjs/rspack": "^1.1.0-beta.31", "@rsdoctor/rspack-plugin": "^1.2.3", "@rspack/cli": "^1.7.1", "@rspack/core": "^1.7.1", diff --git a/tools/static-assets/skel-tailwind/package.json b/tools/static-assets/skel-tailwind/package.json index 482a0bcab0..79935caba4 100644 --- a/tools/static-assets/skel-tailwind/package.json +++ b/tools/static-assets/skel-tailwind/package.json @@ -16,7 +16,7 @@ "react-dom": "^17.0.2" }, "devDependencies": { - "@meteorjs/rspack": "^1.0.0-beta.1", + "@meteorjs/rspack": "^1.1.0-beta.31", "@rsdoctor/rspack-plugin": "^1.2.3", "@rspack/cli": "^1.7.1", "@rspack/core": "^1.7.1", diff --git a/tools/static-assets/skel-typescript/package.json b/tools/static-assets/skel-typescript/package.json index af8e9e10c5..59017498e0 100644 --- a/tools/static-assets/skel-typescript/package.json +++ b/tools/static-assets/skel-typescript/package.json @@ -15,7 +15,7 @@ "react-dom": "^18.2.0" }, "devDependencies": { - "@meteorjs/rspack": "^1.0.0-beta.1", + "@meteorjs/rspack": "^1.1.0-beta.31", "@rsdoctor/rspack-plugin": "^1.2.3", "@rspack/cli": "^1.7.1", "@rspack/core": "^1.7.1", diff --git a/tools/static-assets/skel-vue/package.json b/tools/static-assets/skel-vue/package.json index 0f4debcbdd..a156645d98 100644 --- a/tools/static-assets/skel-vue/package.json +++ b/tools/static-assets/skel-vue/package.json @@ -17,7 +17,7 @@ "vue-router": "^4.2.5" }, "devDependencies": { - "@meteorjs/rspack": "^1.0.0-beta.1", + "@meteorjs/rspack": "^1.1.0-beta.31", "@rsdoctor/rspack-plugin": "^1.2.3", "@rspack/cli": "^1.7.1", "@rspack/core": "^1.7.1",