From e558c50de480e036760234eea09f66ac2dcb69e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Wed, 19 Mar 2025 13:44:46 +0100 Subject: [PATCH 01/63] adapt babel compile behavior to use SWC by default and fallback to babel --- meteor | 2 +- packages/autoupdate/autoupdate_server.js | 2 +- packages/babel-compiler/babel-compiler.js | 48 ++++++++++++++++++- packages/babel-compiler/package.js | 3 +- packages/boilerplate-generator/generator.js | 8 ++-- .../ddp-client/common/livedata_connection.js | 3 +- packages/ecmascript/runtime-tests.js | 4 +- packages/ejson/ejson.js | 2 +- packages/mongo/collection/methods_index.js | 8 ++-- scripts/dev-bundle-tool-package.js | 4 +- 10 files changed, 65 insertions(+), 19 deletions(-) diff --git a/meteor b/meteor index 745c3affbf..0c7f6125a9 100755 --- a/meteor +++ b/meteor @@ -1,6 +1,6 @@ #!/usr/bin/env bash -BUNDLE_VERSION=22.14.0.4 +BUNDLE_VERSION=22.14.0.5 # OS Check. Put here because here is where we download the precompiled diff --git a/packages/autoupdate/autoupdate_server.js b/packages/autoupdate/autoupdate_server.js index 7d20443c27..82af4c65e6 100644 --- a/packages/autoupdate/autoupdate_server.js +++ b/packages/autoupdate/autoupdate_server.js @@ -25,6 +25,7 @@ // The ID of each document is the client architecture, and the fields of // the document are the versions described above. +import { onMessage } from "meteor/inter-process-messaging"; import { ClientVersions } from "./client_versions.js"; export const Autoupdate = __meteor_runtime_config__.autoupdate = { @@ -152,7 +153,6 @@ function enqueueVersionsRefresh() { const setupListeners = () => { // Listen for messages pertaining to the client-refresh topic. - import { onMessage } from "meteor/inter-process-messaging"; onMessage("client-refresh", enqueueVersionsRefresh); // Another way to tell the process to refresh: send SIGHUP signal diff --git a/packages/babel-compiler/babel-compiler.js b/packages/babel-compiler/babel-compiler.js index 11cf63f923..3f2f2e22a7 100644 --- a/packages/babel-compiler/babel-compiler.js +++ b/packages/babel-compiler/babel-compiler.js @@ -1,5 +1,6 @@ var semver = Npm.require("semver"); var JSON5 = Npm.require("json5"); +var SWC = Npm.require("@swc/core"); /** * A compiler that can be instantiated with features and used inside @@ -145,7 +146,52 @@ BCp.processOneFileForTarget = function (inputFile, source) { try { var result = profile('Babel.compile', function () { - return Babel.compile(source, babelOptions, cacheOptions); + let compilation; + try { + const packagesSkipSwc = []; + const fileSkipSwc = ['webapp_server.js']; // top level await + + // Determine if SWC should be used based on package and file criteria. + const shouldUseSwc = + ['minimongo', 'random'].includes(packageName) || + (!packagesSkipSwc.includes(packageName) && + !fileSkipSwc.includes(inputFilePath)); + + if (shouldUseSwc) { + const isTypescriptSyntax = + inputFilePath.endsWith('.ts') || inputFilePath.endsWith('.tsx'); + const hasTSXSupport = inputFilePath.endsWith('.tsx'); + const hasJSXSupport = inputFilePath.endsWith('.jsx'); + + const transformed = SWC.transformSync(source, { + jsc: { + target: 'es2015', + parser: { + syntax: isTypescriptSyntax ? 'typescript' : 'ecmascript', + jsx: hasJSXSupport, + tsx: hasTSXSupport, + }, + }, + module: { type: 'commonjs' }, + minify: false, + sourceMaps: true, + }); + + compilation = { + code: transformed.code, + map: JSON.parse(transformed.map), + hash: toBeAdded.hash, + sourceType: 'module', + }; + } else { + compilation = Babel.compile(source, babelOptions, cacheOptions); + } + } catch (e) { + // If SWC fails, fall back to Babel + compilation = Babel.compile(source, babelOptions, cacheOptions); + } + + return compilation; }); } catch (e) { if (e.loc) { diff --git a/packages/babel-compiler/package.js b/packages/babel-compiler/package.js index 03822c789b..812b3e96af 100644 --- a/packages/babel-compiler/package.js +++ b/packages/babel-compiler/package.js @@ -7,7 +7,8 @@ Package.describe({ Npm.depends({ '@meteorjs/babel': '7.20.1', 'json5': '2.2.3', - 'semver': '7.6.3' + 'semver': '7.6.3', + "@swc/core": "1.11.11", }); Package.onUse(function (api) { diff --git a/packages/boilerplate-generator/generator.js b/packages/boilerplate-generator/generator.js index 0c4d8d426d..6b36f0adf3 100644 --- a/packages/boilerplate-generator/generator.js +++ b/packages/boilerplate-generator/generator.js @@ -1,8 +1,8 @@ import {readFileSync} from 'fs'; import { create as createStream } from "combined-stream2"; -import WebBrowserTemplate from './template-web.browser'; -import WebCordovaTemplate from './template-web.cordova'; +import { headTemplate as modernHeadTemplate, closeTemplate as modernCloseTemplate } from './template-web.browser'; +import { headTemplate as cordovaHeadTemplate, closeTemplate as cordovaCloseTemplate } from './template-web.cordova'; // Copied from webapp_server const readUtf8FileSync = filename => readFileSync(filename, 'utf8'); @@ -151,11 +151,11 @@ function getTemplate(arch) { const prefix = arch.split(".", 2).join("."); if (prefix === "web.browser") { - return WebBrowserTemplate; + return { headTemplate: modernHeadTemplate, closeTemplate: modernCloseTemplate }; } if (prefix === "web.cordova") { - return WebCordovaTemplate; + return { headTemplate: cordovaHeadTemplate, closeTemplate: cordovaCloseTemplate }; } throw new Error("Unsupported arch: " + arch); diff --git a/packages/ddp-client/common/livedata_connection.js b/packages/ddp-client/common/livedata_connection.js index abe1161d21..a568c7ca70 100644 --- a/packages/ddp-client/common/livedata_connection.js +++ b/packages/ddp-client/common/livedata_connection.js @@ -4,6 +4,7 @@ import { Tracker } from 'meteor/tracker'; import { EJSON } from 'meteor/ejson'; import { Random } from 'meteor/random'; import { MongoID } from 'meteor/mongo-id'; +import { ClientStream } from "meteor/socket-stream-client"; import { DDP } from './namespace.js'; import { MethodInvoker } from './method_invoker'; import { @@ -74,8 +75,6 @@ export class Connection { if (typeof url === 'object') { self._stream = url; } else { - import { ClientStream } from "meteor/socket-stream-client"; - self._stream = new ClientStream(url, { retry: options.retry, ConnectionError: DDP.ConnectionError, diff --git a/packages/ecmascript/runtime-tests.js b/packages/ecmascript/runtime-tests.js index a276d7e380..693116b7e0 100644 --- a/packages/ecmascript/runtime-tests.js +++ b/packages/ecmascript/runtime-tests.js @@ -1,3 +1,5 @@ +import { testExport as oyez } from './runtime-tests.js'; + const isNode8OrLater = Meteor.isServer && parseInt(process.versions.node) >= 8; Tinytest.add('ecmascript - runtime - template literals', test => { @@ -169,14 +171,12 @@ Tinytest.add('ecmascript - runtime - classes - properties', test => { static staticProp = 1234; check = self => { - import { testExport as oyez } from './runtime-tests.js'; test.equal(oyez, 'oyez'); test.isTrue(self === this); test.equal(this.property, 'property'); }; method() { - import { testExport as oyez } from './runtime-tests.js'; test.equal(oyez, 'oyez'); } } diff --git a/packages/ejson/ejson.js b/packages/ejson/ejson.js index e83d099279..76ef364651 100644 --- a/packages/ejson/ejson.js +++ b/packages/ejson/ejson.js @@ -9,6 +9,7 @@ import { isInfOrNaN, handleError, } from './utils'; +import canonicalStringify from './stringify'; /** * @namespace @@ -395,7 +396,6 @@ EJSON.stringify = handleError((item, options) => { let serialized; const json = EJSON.toJSONValue(item); if (options && (options.canonical || options.indent)) { - import canonicalStringify from './stringify'; serialized = canonicalStringify(json, options); } else { serialized = JSON.stringify(json); diff --git a/packages/mongo/collection/methods_index.js b/packages/mongo/collection/methods_index.js index 05d1186054..0731e7a5e1 100644 --- a/packages/mongo/collection/methods_index.js +++ b/packages/mongo/collection/methods_index.js @@ -1,3 +1,5 @@ +import { Log } from 'meteor/logging'; + export const IndexMethods = { // We'll actually design an index API later. For now, we just pass through to // Mongo's, but make it synchronous. @@ -21,8 +23,6 @@ export const IndexMethods = { if (self._collection.createIndexAsync) { await self._collection.createIndexAsync(index, options); } else { - import { Log } from 'meteor/logging'; - Log.debug(`ensureIndexAsync has been deprecated, please use the new 'createIndexAsync' instead${ options?.name ? `, index name: ${ options.name }` : `, index: ${ JSON.stringify(index) }` }`) await self._collection.ensureIndexAsync(index, options); } @@ -54,8 +54,6 @@ export const IndexMethods = { ) && Meteor.settings?.packages?.mongo?.reCreateIndexOnOptionMismatch ) { - import { Log } from 'meteor/logging'; - Log.info(`Re-creating index ${ index } for ${ self._name } due to options mismatch.`); await self._collection.dropIndexAsync(index); await self._collection.createIndexAsync(index, options); @@ -88,4 +86,4 @@ export const IndexMethods = { throw new Error('Can only call dropIndexAsync on server collections'); await self._collection.dropIndexAsync(index); }, -} \ No newline at end of file +} diff --git a/scripts/dev-bundle-tool-package.js b/scripts/dev-bundle-tool-package.js index 04aaa1029a..d57634cc0d 100644 --- a/scripts/dev-bundle-tool-package.js +++ b/scripts/dev-bundle-tool-package.js @@ -67,7 +67,9 @@ var packageJson = { "anser": "2.1.1", 'xmlbuilder2': '1.8.1', "ws": "7.4.5", - "open":"8.4.2" + "open":"8.4.2", + "@swc/core": "1.11.11", + "swc-to-babel": "4.0.0", } }; From 0a7c05e6ef8e56ec6226b3fdc0352341e3a1363e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Wed, 19 Mar 2025 14:41:54 +0100 Subject: [PATCH 02/63] clean --- packages/babel-compiler/babel-compiler.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/babel-compiler/babel-compiler.js b/packages/babel-compiler/babel-compiler.js index 3f2f2e22a7..322eaaf96c 100644 --- a/packages/babel-compiler/babel-compiler.js +++ b/packages/babel-compiler/babel-compiler.js @@ -153,8 +153,7 @@ BCp.processOneFileForTarget = function (inputFile, source) { // Determine if SWC should be used based on package and file criteria. const shouldUseSwc = - ['minimongo', 'random'].includes(packageName) || - (!packagesSkipSwc.includes(packageName) && + !packagesSkipSwc.includes(packageName) && !fileSkipSwc.includes(inputFilePath)); if (shouldUseSwc) { From f13d42cd9a33b3f69e1108a481d895bd9660c19f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Wed, 19 Mar 2025 15:02:23 +0100 Subject: [PATCH 03/63] clean --- packages/babel-compiler/babel-compiler.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/babel-compiler/babel-compiler.js b/packages/babel-compiler/babel-compiler.js index 322eaaf96c..f8d6217d54 100644 --- a/packages/babel-compiler/babel-compiler.js +++ b/packages/babel-compiler/babel-compiler.js @@ -154,7 +154,7 @@ BCp.processOneFileForTarget = function (inputFile, source) { // Determine if SWC should be used based on package and file criteria. const shouldUseSwc = !packagesSkipSwc.includes(packageName) && - !fileSkipSwc.includes(inputFilePath)); + !fileSkipSwc.includes(inputFilePath); if (shouldUseSwc) { const isTypescriptSyntax = From bdc2df5712d8ee5b9ec2c8ab0e938d7bfc934b1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Thu, 20 Mar 2025 14:48:29 +0100 Subject: [PATCH 04/63] resolve file.hash to properly resolve caching in some scenarios --- tools/isobuild/import-scanner.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/isobuild/import-scanner.ts b/tools/isobuild/import-scanner.ts index fe6d9639bc..c4e3b04c57 100644 --- a/tools/isobuild/import-scanner.ts +++ b/tools/isobuild/import-scanner.ts @@ -977,7 +977,7 @@ export default class ImportScanner { private async findImportedModuleIdentifiers( file: File, ): Promise> { - const fileHash = file.hash; + const fileHash = file.hash instanceof Promise ? await file.hash : file.hash; if (IMPORT_SCANNER_CACHE.has(fileHash)) { return IMPORT_SCANNER_CACHE.get(fileHash) as Record; } From b8472bf257d0b4c29ef3f0e98ca8ef2f6865dd9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Thu, 20 Mar 2025 15:04:20 +0100 Subject: [PATCH 05/63] set properly cache on import scanner --- tools/isobuild/import-scanner.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/isobuild/import-scanner.ts b/tools/isobuild/import-scanner.ts index c4e3b04c57..acec76b398 100644 --- a/tools/isobuild/import-scanner.ts +++ b/tools/isobuild/import-scanner.ts @@ -988,8 +988,8 @@ export default class ImportScanner { ); // there should always be file.hash, but better safe than sorry - if (file.hash) { - IMPORT_SCANNER_CACHE.set(file.hash, result); + if (fileHash) { + IMPORT_SCANNER_CACHE.set(fileHash, result); } return result; From d34c094bd82dc5ab3ecb30533c022e5f616e6d31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Sat, 22 Mar 2025 08:23:27 +0100 Subject: [PATCH 06/63] support acorn as a faster approach to parse code for the analyzer --- scripts/dev-bundle-tool-package.js | 2 +- tools/isobuild/js-analyze.js | 43 +++++++++++++++++++----------- 2 files changed, 29 insertions(+), 16 deletions(-) diff --git a/scripts/dev-bundle-tool-package.js b/scripts/dev-bundle-tool-package.js index d57634cc0d..60cc8d69fa 100644 --- a/scripts/dev-bundle-tool-package.js +++ b/scripts/dev-bundle-tool-package.js @@ -69,7 +69,7 @@ var packageJson = { "ws": "7.4.5", "open":"8.4.2", "@swc/core": "1.11.11", - "swc-to-babel": "4.0.0", + "acorn": "8.14.1", } }; diff --git a/tools/isobuild/js-analyze.js b/tools/isobuild/js-analyze.js index 35a929c083..9ed7b5fae3 100644 --- a/tools/isobuild/js-analyze.js +++ b/tools/isobuild/js-analyze.js @@ -4,6 +4,7 @@ import LRUCache from "lru-cache"; import { Profile } from '../tool-env/profile'; import Visitor from "@meteorjs/reify/lib/visitor.js"; import { findPossibleIndexes } from "@meteorjs/reify/lib/utils.js"; +import acorn from 'acorn'; const hasOwn = Object.prototype.hasOwnProperty; const objToStr = Object.prototype.toString @@ -15,7 +16,7 @@ function isRegExp(value) { var AST_CACHE = new LRUCache({ max: Math.pow(2, 12), length(ast) { - return ast.loc.end.line; + return ast.end; } }); @@ -28,20 +29,32 @@ function tryToParse(source, hash) { let ast; try { Profile.time('jsAnalyze.parse', () => { - ast = parse(source, { - strictMode: false, - sourceType: 'module', - allowImportExportEverywhere: true, - allowReturnOutsideFunction: true, - allowUndeclaredExports: true, - plugins: [ - // Only plugins for stage 3 features are enabled - // Enabling some plugins significantly affects parser performance - 'importAttributes', - 'explicitResourceManagement', - 'decorators' - ] - }); + try { + ast = acorn.parse(source, { + ecmaVersion: 'latest', + sourceType: 'script', + allowAwaitOutsideFunction: true, + allowImportExportEverywhere: true, + allowReturnOutsideFunction: true, + allowHashBang: true, + checkPrivateFields: false + }); + } catch (error) { + ast = parse(source, { + strictMode: false, + sourceType: 'module', + allowImportExportEverywhere: true, + allowReturnOutsideFunction: true, + allowUndeclaredExports: true, + plugins: [ + // Only plugins for stage 3 features are enabled + // Enabling some plugins significantly affects parser performance + 'importAttributes', + 'explicitResourceManagement', + 'decorators' + ] + }); + } }); } catch (e) { if (typeof e.loc === 'object') { From f257390ccad49fe86078a29a2b9cbe2137ccc2f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Sat, 22 Mar 2025 09:14:41 +0100 Subject: [PATCH 07/63] get locations to perserve proper cache --- tools/isobuild/js-analyze.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/isobuild/js-analyze.js b/tools/isobuild/js-analyze.js index 9ed7b5fae3..6e37ded2b1 100644 --- a/tools/isobuild/js-analyze.js +++ b/tools/isobuild/js-analyze.js @@ -16,7 +16,7 @@ function isRegExp(value) { var AST_CACHE = new LRUCache({ max: Math.pow(2, 12), length(ast) { - return ast.end; + return ast.loc.end.line; } }); @@ -37,7 +37,8 @@ function tryToParse(source, hash) { allowImportExportEverywhere: true, allowReturnOutsideFunction: true, allowHashBang: true, - checkPrivateFields: false + checkPrivateFields: false, + locations: true, }); } catch (error) { ast = parse(source, { From 94e6bd855efb7a46012be030f1bcec6ed1da77d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Sat, 22 Mar 2025 09:28:50 +0100 Subject: [PATCH 08/63] calculate and use lines when is possible to perserve cache length behavior and also keep less RAM usage --- tools/isobuild/js-analyze.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/isobuild/js-analyze.js b/tools/isobuild/js-analyze.js index 6e37ded2b1..e4f1c7f3a2 100644 --- a/tools/isobuild/js-analyze.js +++ b/tools/isobuild/js-analyze.js @@ -16,7 +16,7 @@ function isRegExp(value) { var AST_CACHE = new LRUCache({ max: Math.pow(2, 12), length(ast) { - return ast.loc.end.line; + return ast?.loc?.end?.line || ast?.lines || ast.end; } }); @@ -32,14 +32,14 @@ function tryToParse(source, hash) { try { ast = acorn.parse(source, { ecmaVersion: 'latest', - sourceType: 'script', + sourceType: 'module', allowAwaitOutsideFunction: true, allowImportExportEverywhere: true, allowReturnOutsideFunction: true, allowHashBang: true, checkPrivateFields: false, - locations: true, }); + Object.assign(ast, { lines: (source?.split('\n')?.length || 0) - 1 }); } catch (error) { ast = parse(source, { strictMode: false, From 51a32122de6c99e12c035c92b97c62c7c1d8b5a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Sat, 22 Mar 2025 09:29:42 +0100 Subject: [PATCH 09/63] perserve script sourceType --- tools/isobuild/js-analyze.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/isobuild/js-analyze.js b/tools/isobuild/js-analyze.js index e4f1c7f3a2..0944af885f 100644 --- a/tools/isobuild/js-analyze.js +++ b/tools/isobuild/js-analyze.js @@ -32,7 +32,7 @@ function tryToParse(source, hash) { try { ast = acorn.parse(source, { ecmaVersion: 'latest', - sourceType: 'module', + sourceType: 'script', allowAwaitOutsideFunction: true, allowImportExportEverywhere: true, allowReturnOutsideFunction: true, From 20930d21b54bbf72e20e7ad6a64bb6be95e4610f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Sat, 22 Mar 2025 09:35:34 +0100 Subject: [PATCH 10/63] better way to count lines --- tools/isobuild/js-analyze.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tools/isobuild/js-analyze.js b/tools/isobuild/js-analyze.js index 0944af885f..c66f5bb5dd 100644 --- a/tools/isobuild/js-analyze.js +++ b/tools/isobuild/js-analyze.js @@ -20,6 +20,14 @@ var AST_CACHE = new LRUCache({ } }); +function countLines(str) { + let count = 0; + for (let i = 0; i < str.length; i++) { + if (str[i] === '\n') count++; + } + return count; +} + // Like babel.parse, but annotates any thrown error with $ParseError = true. function tryToParse(source, hash) { if (hash && AST_CACHE.has(hash)) { @@ -39,7 +47,7 @@ function tryToParse(source, hash) { allowHashBang: true, checkPrivateFields: false, }); - Object.assign(ast, { lines: (source?.split('\n')?.length || 0) - 1 }); + Object.assign(ast, { lines: countLines(source) }); } catch (error) { ast = parse(source, { strictMode: false, From bd19fe02f8e5968c0e8ad8a279bb178ff8d5cf92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 25 Mar 2025 14:56:53 +0100 Subject: [PATCH 11/63] better approach to count lines --- tools/isobuild/js-analyze.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tools/isobuild/js-analyze.js b/tools/isobuild/js-analyze.js index c66f5bb5dd..8f011fff54 100644 --- a/tools/isobuild/js-analyze.js +++ b/tools/isobuild/js-analyze.js @@ -21,9 +21,11 @@ var AST_CACHE = new LRUCache({ }); function countLines(str) { + let lastIndex = str.indexOf('\n'); let count = 0; - for (let i = 0; i < str.length; i++) { - if (str[i] === '\n') count++; + while (lastIndex > -1) { + count += 1; + lastIndex = str.indexOf('\n', lastIndex + 1); } return count; } From 00780f91f457bbefd4c3f2ab307934fc109b5907 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 25 Mar 2025 15:39:18 +0100 Subject: [PATCH 12/63] use an estimation for cache length for better performance --- tools/isobuild/js-analyze.js | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/tools/isobuild/js-analyze.js b/tools/isobuild/js-analyze.js index 8f011fff54..2de961be63 100644 --- a/tools/isobuild/js-analyze.js +++ b/tools/isobuild/js-analyze.js @@ -16,20 +16,12 @@ function isRegExp(value) { var AST_CACHE = new LRUCache({ max: Math.pow(2, 12), length(ast) { - return ast?.loc?.end?.line || ast?.lines || ast.end; + // Estimate cached lines based on average length per character + const avgCharsPerLine = 40; + return Math.ceil(ast.end / avgCharsPerLine); } }); -function countLines(str) { - let lastIndex = str.indexOf('\n'); - let count = 0; - while (lastIndex > -1) { - count += 1; - lastIndex = str.indexOf('\n', lastIndex + 1); - } - return count; -} - // Like babel.parse, but annotates any thrown error with $ParseError = true. function tryToParse(source, hash) { if (hash && AST_CACHE.has(hash)) { @@ -49,7 +41,6 @@ function tryToParse(source, hash) { allowHashBang: true, checkPrivateFields: false, }); - Object.assign(ast, { lines: countLines(source) }); } catch (error) { ast = parse(source, { strictMode: false, From fbd71e9e6aea22f435198d14ed90bba5aba75440 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 25 Mar 2025 18:30:24 +0100 Subject: [PATCH 13/63] add caching as part of compilation --- packages/babel-compiler/babel-compiler.js | 69 ++++++++++++++++------- 1 file changed, 49 insertions(+), 20 deletions(-) diff --git a/packages/babel-compiler/babel-compiler.js b/packages/babel-compiler/babel-compiler.js index f8d6217d54..f9ea38f32f 100644 --- a/packages/babel-compiler/babel-compiler.js +++ b/packages/babel-compiler/babel-compiler.js @@ -1,6 +1,8 @@ var semver = Npm.require("semver"); var JSON5 = Npm.require("json5"); var SWC = Npm.require("@swc/core"); +var fs = Npm.require('fs'); +var path = Npm.require('path'); /** * A compiler that can be instantiated with features and used inside @@ -51,7 +53,9 @@ BCp.processFilesForTarget = function (inputFiles) { // Returns an object suitable for passing to inputFile.addJavaScript, or // null to indicate there was an error, and nothing should be added. BCp.processOneFileForTarget = function (inputFile, source) { + var self = this; // capture context this._babelrcCache = this._babelrcCache || Object.create(null); + this._transformCache = this._transformCache || Object.create(null); if (typeof source !== "string") { // Other compiler plugins can call processOneFileForTarget with a @@ -122,23 +126,18 @@ BCp.processOneFileForTarget = function (inputFile, source) { }, }; - this.inferTypeScriptConfig( - features, inputFile, cacheOptions.cacheDeps); + this.inferTypeScriptConfig(features, inputFile, cacheOptions.cacheDeps); var babelOptions = Babel.getDefaultOptions(features); babelOptions.caller = { name: "meteor", arch }; - this.inferExtraBabelOptions( - inputFile, - babelOptions, - cacheOptions.cacheDeps - ); + this.inferExtraBabelOptions(inputFile, babelOptions, cacheOptions.cacheDeps); babelOptions.sourceMaps = true; babelOptions.filename = babelOptions.sourceFileName = packageName - ? "packages/" + packageName + "/" + inputFilePath - : inputFilePath; + ? "packages/" + packageName + "/" + inputFilePath + : inputFilePath; if (this.modifyBabelConfig) { this.modifyBabelConfig(babelOptions, inputFile); @@ -146,19 +145,38 @@ BCp.processOneFileForTarget = function (inputFile, source) { try { var result = profile('Babel.compile', function () { - let compilation; + // Determine if SWC should be used based on package and file criteria. + const packagesSkipSwc = []; + const fileSkipSwc = ['webapp_server.js']; // top level await + const shouldUseSwc = !packagesSkipSwc.includes(packageName) && + !fileSkipSwc.includes(inputFilePath); + + // Create a cache key based on the source hash and the compiler used. + const cacheKey = `${toBeAdded.hash}-${shouldUseSwc ? 'swc' : 'babel'}`; + + // Check RAM cache + let compilation = self._transformCache[cacheKey]; + // Check file system cache if enabled + if (!compilation && self.cacheDirectory) { + const cacheFilePath = path.join(self.cacheDirectory, 'compilation-cache', cacheKey + '.json'); + if (fs.existsSync(cacheFilePath)) { + try { + compilation = JSON.parse(fs.readFileSync(cacheFilePath, 'utf8')); + self._transformCache[cacheKey] = compilation; + } catch (e) { + // If reading/parsing the cache fails, ignore and continue. + } + } + } + // Return cached result if found. + if (compilation) { + return compilation; + } + + // Perform compilation try { - const packagesSkipSwc = []; - const fileSkipSwc = ['webapp_server.js']; // top level await - - // Determine if SWC should be used based on package and file criteria. - const shouldUseSwc = - !packagesSkipSwc.includes(packageName) && - !fileSkipSwc.includes(inputFilePath); - if (shouldUseSwc) { - const isTypescriptSyntax = - inputFilePath.endsWith('.ts') || inputFilePath.endsWith('.tsx'); + const isTypescriptSyntax = inputFilePath.endsWith('.ts') || inputFilePath.endsWith('.tsx'); const hasTSXSupport = inputFilePath.endsWith('.tsx'); const hasJSXSupport = inputFilePath.endsWith('.jsx'); @@ -190,6 +208,17 @@ BCp.processOneFileForTarget = function (inputFile, source) { compilation = Babel.compile(source, babelOptions, cacheOptions); } + // Save result in caches + self._transformCache[cacheKey] = compilation; + if (self.cacheDirectory) { + const cacheFilePath = path.join(self.cacheDirectory, 'compilation-cache', cacheKey + '.json'); + try { + fs.mkdirSync(path.dirname(cacheFilePath), { recursive: true }); + fs.writeFileSync(cacheFilePath, JSON.stringify(compilation), 'utf8'); + } catch (e) { + // If file caching fails, ignore the error. + } + } return compilation; }); } catch (e) { From 49a639955bd27ad719148a4e1f1a039ac8ef6c23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Wed, 26 Mar 2025 15:08:58 +0100 Subject: [PATCH 14/63] apply cache only for swc as meteor-babel already describes a cache for babel --- packages/babel-compiler/babel-compiler.js | 73 ++++++++++++----------- 1 file changed, 38 insertions(+), 35 deletions(-) diff --git a/packages/babel-compiler/babel-compiler.js b/packages/babel-compiler/babel-compiler.js index f9ea38f32f..9d928d6bd8 100644 --- a/packages/babel-compiler/babel-compiler.js +++ b/packages/babel-compiler/babel-compiler.js @@ -55,7 +55,7 @@ BCp.processFilesForTarget = function (inputFiles) { BCp.processOneFileForTarget = function (inputFile, source) { var self = this; // capture context this._babelrcCache = this._babelrcCache || Object.create(null); - this._transformCache = this._transformCache || Object.create(null); + this._swcCache = this._swcCache || Object.create(null); if (typeof source !== "string") { // Other compiler plugins can call processOneFileForTarget with a @@ -151,35 +151,38 @@ BCp.processOneFileForTarget = function (inputFile, source) { const shouldUseSwc = !packagesSkipSwc.includes(packageName) && !fileSkipSwc.includes(inputFilePath); - // Create a cache key based on the source hash and the compiler used. - const cacheKey = `${toBeAdded.hash}-${shouldUseSwc ? 'swc' : 'babel'}`; - // Check RAM cache - let compilation = self._transformCache[cacheKey]; - // Check file system cache if enabled - if (!compilation && self.cacheDirectory) { - const cacheFilePath = path.join(self.cacheDirectory, 'compilation-cache', cacheKey + '.json'); - if (fs.existsSync(cacheFilePath)) { - try { - compilation = JSON.parse(fs.readFileSync(cacheFilePath, 'utf8')); - self._transformCache[cacheKey] = compilation; - } catch (e) { - // If reading/parsing the cache fails, ignore and continue. - } - } - } - // Return cached result if found. - if (compilation) { - return compilation; - } - - // Perform compilation + let compilation; try { if (shouldUseSwc) { + // Create a cache key based on the source hash and the compiler used. + const cacheKey = toBeAdded.hash; + const cacheContext = 'swc-cache'; + + // Check RAM cache + compilation = self._swcCache[cacheKey]; + // Check file system cache if enabled + if (!compilation && self.cacheDirectory) { + const cacheFilePath = path.join(self.cacheDirectory, cacheContext, cacheKey + '.json'); + if (fs.existsSync(cacheFilePath)) { + try { + compilation = JSON.parse(fs.readFileSync(cacheFilePath, 'utf8')); + self._swcCache[cacheKey] = compilation; + } catch (e) { + // If reading/parsing the cache fails, ignore and continue. + } + } + } + // Return cached result if found. + if (compilation) { + return compilation; + } + const isTypescriptSyntax = inputFilePath.endsWith('.ts') || inputFilePath.endsWith('.tsx'); const hasTSXSupport = inputFilePath.endsWith('.tsx'); const hasJSXSupport = inputFilePath.endsWith('.jsx'); + // Perform compilation const transformed = SWC.transformSync(source, { jsc: { target: 'es2015', @@ -200,6 +203,18 @@ BCp.processOneFileForTarget = function (inputFile, source) { hash: toBeAdded.hash, sourceType: 'module', }; + + // Save result in cache + self._swcCache[cacheKey] = compilation; + if (self.cacheDirectory) { + const cacheFilePath = path.join(self.cacheDirectory, cacheContext, cacheKey + '.json'); + try { + fs.mkdirSync(path.dirname(cacheFilePath), { recursive: true }); + fs.writeFileSync(cacheFilePath, JSON.stringify(compilation), 'utf8'); + } catch (e) { + // If file caching fails, ignore the error. + } + } } else { compilation = Babel.compile(source, babelOptions, cacheOptions); } @@ -207,18 +222,6 @@ BCp.processOneFileForTarget = function (inputFile, source) { // If SWC fails, fall back to Babel compilation = Babel.compile(source, babelOptions, cacheOptions); } - - // Save result in caches - self._transformCache[cacheKey] = compilation; - if (self.cacheDirectory) { - const cacheFilePath = path.join(self.cacheDirectory, 'compilation-cache', cacheKey + '.json'); - try { - fs.mkdirSync(path.dirname(cacheFilePath), { recursive: true }); - fs.writeFileSync(cacheFilePath, JSON.stringify(compilation), 'utf8'); - } catch (e) { - // If file caching fails, ignore the error. - } - } return compilation; }); } catch (e) { From c4e8fd4c4e628d82fae0b8e26b8db089dcde32bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Wed, 26 Mar 2025 15:19:50 +0100 Subject: [PATCH 15/63] better cache context --- packages/babel-compiler/babel-compiler.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/babel-compiler/babel-compiler.js b/packages/babel-compiler/babel-compiler.js index 9d928d6bd8..09d680c19e 100644 --- a/packages/babel-compiler/babel-compiler.js +++ b/packages/babel-compiler/babel-compiler.js @@ -157,7 +157,7 @@ BCp.processOneFileForTarget = function (inputFile, source) { if (shouldUseSwc) { // Create a cache key based on the source hash and the compiler used. const cacheKey = toBeAdded.hash; - const cacheContext = 'swc-cache'; + const cacheContext = '.swc-cache'; // Check RAM cache compilation = self._swcCache[cacheKey]; From c77611f57c63ea1bde03c618677234def26b48c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Wed, 26 Mar 2025 15:51:14 +0100 Subject: [PATCH 16/63] async write the cache --- packages/babel-compiler/babel-compiler.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/babel-compiler/babel-compiler.js b/packages/babel-compiler/babel-compiler.js index 09d680c19e..8181009167 100644 --- a/packages/babel-compiler/babel-compiler.js +++ b/packages/babel-compiler/babel-compiler.js @@ -209,8 +209,12 @@ BCp.processOneFileForTarget = function (inputFile, source) { if (self.cacheDirectory) { const cacheFilePath = path.join(self.cacheDirectory, cacheContext, cacheKey + '.json'); try { - fs.mkdirSync(path.dirname(cacheFilePath), { recursive: true }); - fs.writeFileSync(cacheFilePath, JSON.stringify(compilation), 'utf8'); + const writeFileCache = async () => { + await fs.promises.mkdir(path.dirname(cacheFilePath), { recursive: true }); + await fs.promises.writeFile(cacheFilePath, JSON.stringify(compilation), 'utf8'); + }; + // Asynchronously write the cache without blocking + writeFileCache(); } catch (e) { // If file caching fails, ignore the error. } From 09691e422ca149e91f83dd9173549302ef1190c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Wed, 26 Mar 2025 18:02:33 +0100 Subject: [PATCH 17/63] prefer more modern syntax on swc compilation --- packages/babel-compiler/babel-compiler.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/babel-compiler/babel-compiler.js b/packages/babel-compiler/babel-compiler.js index f8d6217d54..42aed0dd0f 100644 --- a/packages/babel-compiler/babel-compiler.js +++ b/packages/babel-compiler/babel-compiler.js @@ -164,7 +164,7 @@ BCp.processOneFileForTarget = function (inputFile, source) { const transformed = SWC.transformSync(source, { jsc: { - target: 'es2015', + target: 'es2022', parser: { syntax: isTypescriptSyntax ? 'typescript' : 'ecmascript', jsx: hasJSXSupport, From 5b99337802850a9db319824fc4bb9ca9ed286904 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Thu, 27 Mar 2025 15:37:37 +0100 Subject: [PATCH 18/63] Revert "prefer more modern syntax on swc compilation" This reverts commit 09691e422ca149e91f83dd9173549302ef1190c6. --- packages/babel-compiler/babel-compiler.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/babel-compiler/babel-compiler.js b/packages/babel-compiler/babel-compiler.js index 42aed0dd0f..f8d6217d54 100644 --- a/packages/babel-compiler/babel-compiler.js +++ b/packages/babel-compiler/babel-compiler.js @@ -164,7 +164,7 @@ BCp.processOneFileForTarget = function (inputFile, source) { const transformed = SWC.transformSync(source, { jsc: { - target: 'es2022', + target: 'es2015', parser: { syntax: isTypescriptSyntax ? 'typescript' : 'ecmascript', jsx: hasJSXSupport, From e1e44c15cb8a08078e951def3ae9c44de1fa540b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Thu, 27 Mar 2025 16:45:11 +0100 Subject: [PATCH 19/63] perserve Meteor's specifics: reify modules, nested imports and top-level await support --- packages/babel-compiler/babel-compiler.js | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/packages/babel-compiler/babel-compiler.js b/packages/babel-compiler/babel-compiler.js index f8d6217d54..1de5a197d8 100644 --- a/packages/babel-compiler/babel-compiler.js +++ b/packages/babel-compiler/babel-compiler.js @@ -1,6 +1,8 @@ var semver = Npm.require("semver"); var JSON5 = Npm.require("json5"); var SWC = Npm.require("@swc/core"); +const reifyCompile = Npm.require("@meteorjs/reify/lib/compiler").compile; +const reifyAcornTopLevelParse = Npm.require("@meteorjs/reify/lib/parsers/top-level").parse; /** * A compiler that can be instantiated with features and used inside @@ -149,7 +151,7 @@ BCp.processOneFileForTarget = function (inputFile, source) { let compilation; try { const packagesSkipSwc = []; - const fileSkipSwc = ['webapp_server.js']; // top level await + const fileSkipSwc = []; // top level await // Determine if SWC should be used based on package and file criteria. const shouldUseSwc = @@ -171,13 +173,25 @@ BCp.processOneFileForTarget = function (inputFile, source) { tsx: hasTSXSupport, }, }, - module: { type: 'commonjs' }, + module: { type: 'es6' }, minify: false, sourceMaps: true, }); + let content = transformed.code; + // Perserve Meteor's specifics: reify modules, nested imports and top-level await support. + const result = reifyCompile(content, { + parse: reifyAcornTopLevelParse, + generateLetDeclarations: false, + ast: false, + }); + if (!result.identical) { + identical = false; + content = result.code; + } + compilation = { - code: transformed.code, + code: content, map: JSON.parse(transformed.map), hash: toBeAdded.hash, sourceType: 'module', From 2ddc37e2123cb7dae5e362579d8adfc1a5ee01ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Thu, 27 Mar 2025 17:13:08 +0100 Subject: [PATCH 20/63] revert removal of nested imports as they are still compatible --- packages/autoupdate/autoupdate_server.js | 2 +- packages/ddp-client/common/livedata_connection.js | 3 ++- packages/ecmascript/runtime-tests.js | 4 ++-- packages/ejson/ejson.js | 2 +- packages/mongo/collection/methods_index.js | 6 ++++-- 5 files changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/autoupdate/autoupdate_server.js b/packages/autoupdate/autoupdate_server.js index 82af4c65e6..7d20443c27 100644 --- a/packages/autoupdate/autoupdate_server.js +++ b/packages/autoupdate/autoupdate_server.js @@ -25,7 +25,6 @@ // The ID of each document is the client architecture, and the fields of // the document are the versions described above. -import { onMessage } from "meteor/inter-process-messaging"; import { ClientVersions } from "./client_versions.js"; export const Autoupdate = __meteor_runtime_config__.autoupdate = { @@ -153,6 +152,7 @@ function enqueueVersionsRefresh() { const setupListeners = () => { // Listen for messages pertaining to the client-refresh topic. + import { onMessage } from "meteor/inter-process-messaging"; onMessage("client-refresh", enqueueVersionsRefresh); // Another way to tell the process to refresh: send SIGHUP signal diff --git a/packages/ddp-client/common/livedata_connection.js b/packages/ddp-client/common/livedata_connection.js index a568c7ca70..abe1161d21 100644 --- a/packages/ddp-client/common/livedata_connection.js +++ b/packages/ddp-client/common/livedata_connection.js @@ -4,7 +4,6 @@ import { Tracker } from 'meteor/tracker'; import { EJSON } from 'meteor/ejson'; import { Random } from 'meteor/random'; import { MongoID } from 'meteor/mongo-id'; -import { ClientStream } from "meteor/socket-stream-client"; import { DDP } from './namespace.js'; import { MethodInvoker } from './method_invoker'; import { @@ -75,6 +74,8 @@ export class Connection { if (typeof url === 'object') { self._stream = url; } else { + import { ClientStream } from "meteor/socket-stream-client"; + self._stream = new ClientStream(url, { retry: options.retry, ConnectionError: DDP.ConnectionError, diff --git a/packages/ecmascript/runtime-tests.js b/packages/ecmascript/runtime-tests.js index 693116b7e0..a276d7e380 100644 --- a/packages/ecmascript/runtime-tests.js +++ b/packages/ecmascript/runtime-tests.js @@ -1,5 +1,3 @@ -import { testExport as oyez } from './runtime-tests.js'; - const isNode8OrLater = Meteor.isServer && parseInt(process.versions.node) >= 8; Tinytest.add('ecmascript - runtime - template literals', test => { @@ -171,12 +169,14 @@ Tinytest.add('ecmascript - runtime - classes - properties', test => { static staticProp = 1234; check = self => { + import { testExport as oyez } from './runtime-tests.js'; test.equal(oyez, 'oyez'); test.isTrue(self === this); test.equal(this.property, 'property'); }; method() { + import { testExport as oyez } from './runtime-tests.js'; test.equal(oyez, 'oyez'); } } diff --git a/packages/ejson/ejson.js b/packages/ejson/ejson.js index 76ef364651..e83d099279 100644 --- a/packages/ejson/ejson.js +++ b/packages/ejson/ejson.js @@ -9,7 +9,6 @@ import { isInfOrNaN, handleError, } from './utils'; -import canonicalStringify from './stringify'; /** * @namespace @@ -396,6 +395,7 @@ EJSON.stringify = handleError((item, options) => { let serialized; const json = EJSON.toJSONValue(item); if (options && (options.canonical || options.indent)) { + import canonicalStringify from './stringify'; serialized = canonicalStringify(json, options); } else { serialized = JSON.stringify(json); diff --git a/packages/mongo/collection/methods_index.js b/packages/mongo/collection/methods_index.js index 0731e7a5e1..c516d7522b 100644 --- a/packages/mongo/collection/methods_index.js +++ b/packages/mongo/collection/methods_index.js @@ -1,5 +1,3 @@ -import { Log } from 'meteor/logging'; - export const IndexMethods = { // We'll actually design an index API later. For now, we just pass through to // Mongo's, but make it synchronous. @@ -23,6 +21,8 @@ export const IndexMethods = { if (self._collection.createIndexAsync) { await self._collection.createIndexAsync(index, options); } else { + import { Log } from 'meteor/logging'; + Log.debug(`ensureIndexAsync has been deprecated, please use the new 'createIndexAsync' instead${ options?.name ? `, index name: ${ options.name }` : `, index: ${ JSON.stringify(index) }` }`) await self._collection.ensureIndexAsync(index, options); } @@ -54,6 +54,8 @@ export const IndexMethods = { ) && Meteor.settings?.packages?.mongo?.reCreateIndexOnOptionMismatch ) { + import { Log } from 'meteor/logging'; + Log.info(`Re-creating index ${ index } for ${ self._name } due to options mismatch.`); await self._collection.dropIndexAsync(index); await self._collection.createIndexAsync(index, options); From 8e8801e2ef08b7a876d4a93e14bf873a178cddc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Fri, 28 Mar 2025 08:55:43 +0100 Subject: [PATCH 21/63] remove unrequired nested imports --- packages/ecmascript/runtime-tests.js | 4 ++-- packages/ejson/ejson.js | 2 +- packages/mongo/collection/methods_index.js | 6 ++---- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/packages/ecmascript/runtime-tests.js b/packages/ecmascript/runtime-tests.js index a276d7e380..693116b7e0 100644 --- a/packages/ecmascript/runtime-tests.js +++ b/packages/ecmascript/runtime-tests.js @@ -1,3 +1,5 @@ +import { testExport as oyez } from './runtime-tests.js'; + const isNode8OrLater = Meteor.isServer && parseInt(process.versions.node) >= 8; Tinytest.add('ecmascript - runtime - template literals', test => { @@ -169,14 +171,12 @@ Tinytest.add('ecmascript - runtime - classes - properties', test => { static staticProp = 1234; check = self => { - import { testExport as oyez } from './runtime-tests.js'; test.equal(oyez, 'oyez'); test.isTrue(self === this); test.equal(this.property, 'property'); }; method() { - import { testExport as oyez } from './runtime-tests.js'; test.equal(oyez, 'oyez'); } } diff --git a/packages/ejson/ejson.js b/packages/ejson/ejson.js index e83d099279..76ef364651 100644 --- a/packages/ejson/ejson.js +++ b/packages/ejson/ejson.js @@ -9,6 +9,7 @@ import { isInfOrNaN, handleError, } from './utils'; +import canonicalStringify from './stringify'; /** * @namespace @@ -395,7 +396,6 @@ EJSON.stringify = handleError((item, options) => { let serialized; const json = EJSON.toJSONValue(item); if (options && (options.canonical || options.indent)) { - import canonicalStringify from './stringify'; serialized = canonicalStringify(json, options); } else { serialized = JSON.stringify(json); diff --git a/packages/mongo/collection/methods_index.js b/packages/mongo/collection/methods_index.js index c516d7522b..0731e7a5e1 100644 --- a/packages/mongo/collection/methods_index.js +++ b/packages/mongo/collection/methods_index.js @@ -1,3 +1,5 @@ +import { Log } from 'meteor/logging'; + export const IndexMethods = { // We'll actually design an index API later. For now, we just pass through to // Mongo's, but make it synchronous. @@ -21,8 +23,6 @@ export const IndexMethods = { if (self._collection.createIndexAsync) { await self._collection.createIndexAsync(index, options); } else { - import { Log } from 'meteor/logging'; - Log.debug(`ensureIndexAsync has been deprecated, please use the new 'createIndexAsync' instead${ options?.name ? `, index name: ${ options.name }` : `, index: ${ JSON.stringify(index) }` }`) await self._collection.ensureIndexAsync(index, options); } @@ -54,8 +54,6 @@ export const IndexMethods = { ) && Meteor.settings?.packages?.mongo?.reCreateIndexOnOptionMismatch ) { - import { Log } from 'meteor/logging'; - Log.info(`Re-creating index ${ index } for ${ self._name } due to options mismatch.`); await self._collection.dropIndexAsync(index); await self._collection.createIndexAsync(index, options); From 74bdbd8d00f58995c0f606bdca1f3aa796f0074a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Fri, 28 Mar 2025 09:12:02 +0100 Subject: [PATCH 22/63] remove unrequired nested imports --- packages/autoupdate/autoupdate_server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/autoupdate/autoupdate_server.js b/packages/autoupdate/autoupdate_server.js index 7d20443c27..82af4c65e6 100644 --- a/packages/autoupdate/autoupdate_server.js +++ b/packages/autoupdate/autoupdate_server.js @@ -25,6 +25,7 @@ // The ID of each document is the client architecture, and the fields of // the document are the versions described above. +import { onMessage } from "meteor/inter-process-messaging"; import { ClientVersions } from "./client_versions.js"; export const Autoupdate = __meteor_runtime_config__.autoupdate = { @@ -152,7 +153,6 @@ function enqueueVersionsRefresh() { const setupListeners = () => { // Listen for messages pertaining to the client-refresh topic. - import { onMessage } from "meteor/inter-process-messaging"; onMessage("client-refresh", enqueueVersionsRefresh); // Another way to tell the process to refresh: send SIGHUP signal From a3b12b2eb611f1de3cedef929fe1d4238158486e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Fri, 28 Mar 2025 09:26:53 +0100 Subject: [PATCH 23/63] prefer require than nested import --- packages/ddp-client/common/livedata_connection.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ddp-client/common/livedata_connection.js b/packages/ddp-client/common/livedata_connection.js index abe1161d21..9755a8012a 100644 --- a/packages/ddp-client/common/livedata_connection.js +++ b/packages/ddp-client/common/livedata_connection.js @@ -74,7 +74,7 @@ export class Connection { if (typeof url === 'object') { self._stream = url; } else { - import { ClientStream } from "meteor/socket-stream-client"; + const { ClientStream } = require("meteor/socket-stream-client"); self._stream = new ClientStream(url, { retry: options.retry, From cf6dc9ca5a34d4f961225294c10391e2e4405dab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Fri, 28 Mar 2025 09:32:40 +0100 Subject: [PATCH 24/63] ensure incompatible files will directly parse with babel once detected --- packages/babel-compiler/babel-compiler.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/babel-compiler/babel-compiler.js b/packages/babel-compiler/babel-compiler.js index 8cf2c4245e..77271959d4 100644 --- a/packages/babel-compiler/babel-compiler.js +++ b/packages/babel-compiler/babel-compiler.js @@ -58,6 +58,7 @@ BCp.processOneFileForTarget = function (inputFile, source) { var self = this; // capture context this._babelrcCache = this._babelrcCache || Object.create(null); this._swcCache = this._swcCache || Object.create(null); + this._swcIncompatible = this._swcIncompatible || Object.create(null); if (typeof source !== "string") { // Other compiler plugins can call processOneFileForTarget with a @@ -150,8 +151,10 @@ BCp.processOneFileForTarget = function (inputFile, source) { // Determine if SWC should be used based on package and file criteria. const packagesSkipSwc = []; const fileSkipSwc = []; // top level await - const shouldUseSwc = !packagesSkipSwc.includes(packageName) && - !fileSkipSwc.includes(inputFilePath); + const shouldUseSwc = + !packagesSkipSwc.includes(packageName) && + !fileSkipSwc.includes(inputFilePath) && + !self._swcIncompatible[toBeAdded.hash]; // Check RAM cache let compilation; @@ -237,6 +240,7 @@ BCp.processOneFileForTarget = function (inputFile, source) { compilation = Babel.compile(source, babelOptions, cacheOptions); } } catch (e) { + self._swcIncompatible[toBeAdded.hash] = true; // If SWC fails, fall back to Babel compilation = Babel.compile(source, babelOptions, cacheOptions); } From 42b61a4ecc6f50e8d7160314a231967b2fa7394f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Fri, 28 Mar 2025 10:35:23 +0100 Subject: [PATCH 25/63] use latest acorn parser and proper reify options --- packages/babel-compiler/babel-compiler.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/packages/babel-compiler/babel-compiler.js b/packages/babel-compiler/babel-compiler.js index 77271959d4..d5f0263801 100644 --- a/packages/babel-compiler/babel-compiler.js +++ b/packages/babel-compiler/babel-compiler.js @@ -2,7 +2,7 @@ var semver = Npm.require("semver"); var JSON5 = Npm.require("json5"); var SWC = Npm.require("@swc/core"); const reifyCompile = Npm.require("@meteorjs/reify/lib/compiler").compile; -const reifyAcornTopLevelParse = Npm.require("@meteorjs/reify/lib/parsers/top-level").parse; +const reifyAcornParse = Npm.require("@meteorjs/reify/lib/parsers/acorn").parse; var fs = Npm.require('fs'); var path = Npm.require('path'); @@ -205,9 +205,21 @@ BCp.processOneFileForTarget = function (inputFile, source) { let content = transformed.code; // Perserve Meteor's specifics: reify modules, nested imports and top-level await support. const result = reifyCompile(content, { - parse: reifyAcornTopLevelParse, + parse: reifyAcornParse, generateLetDeclarations: false, ast: false, + // Enforce reify options for proper compatibility (TODO export getReifyOptions) + // https://github.com/meteor/meteor/blob/devel/npm-packages/meteor-babel/options.js#L19 + avoidModernSyntax: true, + enforceStrictMode: false, + dynamicImport: true, + ...features.topLevelAwait && { topLevelAwait: true }, + ...features.compileForShell && { moduleAlias: 'module' }, + ...(features.modernBrowsers || + features.nodeMajorVersion >= 8) && { + avoidModernSyntax: false, + generateLetDeclarations: true, + }, }); if (!result.identical) { identical = false; From 1d11de08d7afc39cd305f8234809f7d52788ccfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Fri, 28 Mar 2025 12:24:21 +0100 Subject: [PATCH 26/63] use reify acorn parser on static-assets server runtime --- tools/static-assets/server/runtime.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/static-assets/server/runtime.js b/tools/static-assets/server/runtime.js index 2b0f800940..9a1c6b15ef 100644 --- a/tools/static-assets/server/runtime.js +++ b/tools/static-assets/server/runtime.js @@ -47,7 +47,7 @@ module.exports = function enable ({ cachePath, createLoader = true } = {}) { }; const reifyVersion = require("@meteorjs/reify/package.json").version; - const reifyBabelParse = require("@meteorjs/reify/lib/parsers/babel").parse; + const reifyAcornParse = require("@meteorjs/reify/lib/parsers/acorn").parse; const reifyCompile = require("@meteorjs/reify/lib/compiler").compile; function compileContent (content) { @@ -55,7 +55,7 @@ module.exports = function enable ({ cachePath, createLoader = true } = {}) { try { const result = reifyCompile(content, { - parse: reifyBabelParse, + parse: reifyAcornParse, generateLetDeclarations: false, ast: false, }); From 94dd07b2d0826e9bf6ab6c3c083b4774641614ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Fri, 28 Mar 2025 12:27:39 +0100 Subject: [PATCH 27/63] use reify acorn parser on import scanner --- tools/isobuild/import-scanner.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/isobuild/import-scanner.ts b/tools/isobuild/import-scanner.ts index acec76b398..e05900b2f2 100644 --- a/tools/isobuild/import-scanner.ts +++ b/tools/isobuild/import-scanner.ts @@ -45,7 +45,7 @@ import { import { wrap } from "optimism"; const { compile: reifyCompile } = require("@meteorjs/reify/lib/compiler"); -const { parse: reifyBabelParse } = require("@meteorjs/reify/lib/parsers/babel"); +const { parse: reifyAcornParse } = require("@meteorjs/reify/lib/parsers/acorn"); import Resolver, { Resolution } from "./resolver"; import LRUCache from 'lru-cache'; @@ -88,7 +88,7 @@ const reifyCompileWithCache = Profile("reifyCompileWithCache", wrap(function ( const isLegacy = isLegacyArch(bundleArch); let result = reifyCompile(stripHashBang(source), { - parse: reifyBabelParse, + parse: reifyAcornParse, generateLetDeclarations: !isLegacy, avoidModernSyntax: isLegacy, enforceStrictMode: false, From ee23cb4e575a2765da82f602d47185b55b5f62bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 1 Apr 2025 19:38:58 +0200 Subject: [PATCH 28/63] ensure SWC.compile profile context and address feedback --- packages/babel-compiler/babel-compiler.js | 142 ++++++++++++---------- 1 file changed, 78 insertions(+), 64 deletions(-) diff --git a/packages/babel-compiler/babel-compiler.js b/packages/babel-compiler/babel-compiler.js index d5f0263801..d5e1f7437a 100644 --- a/packages/babel-compiler/babel-compiler.js +++ b/packages/babel-compiler/babel-compiler.js @@ -29,6 +29,67 @@ var isMeteorPre144 = semver.lt(process.version, "4.8.1"); var enableClientTLA = process.env.METEOR_ENABLE_CLIENT_TOP_LEVEL_AWAIT === 'true'; +function compileWithBabel(source, babelOptions, cacheOptions) { + return profile('Babel.compile', function () { + return Babel.compile(source, babelOptions, cacheOptions); + }); +} + +function compileWithSWC(source, { inputFilePath, hash, features, ...swcOptions }) { + return profile('SWC.compile', function () { + // Determine file extension based syntax. + const isTypescriptSyntax = inputFilePath.endsWith('.ts') || inputFilePath.endsWith('.tsx'); + const hasTSXSupport = inputFilePath.endsWith('.tsx'); + const hasJSXSupport = inputFilePath.endsWith('.jsx'); + + // Perform SWC transformation. + const transformed = SWC.transformSync(source, { + ...swcOptions, + jsc: { + target: 'es2015', + parser: { + syntax: isTypescriptSyntax ? 'typescript' : 'ecmascript', + jsx: hasJSXSupport, + tsx: hasTSXSupport, + }, + }, + module: { type: 'es6' }, + minify: false, + sourceMaps: true, + }); + + let content = transformed.code; + + // Preserve Meteor-specific features: reify modules, nested imports, and top-level await support. + const result = reifyCompile(content, { + parse: reifyAcornParse, + generateLetDeclarations: false, + ast: false, + // Enforce reify options for proper compatibility. + avoidModernSyntax: true, + enforceStrictMode: false, + dynamicImport: true, + ...features.topLevelAwait && { topLevelAwait: true }, + ...features.compileForShell && { moduleAlias: 'module' }, + ...(features.modernBrowsers || features.nodeMajorVersion >= 8) && { + avoidModernSyntax: false, + generateLetDeclarations: true, + }, + }); + if (!result.identical) { + content = result.code; + } + + return { + code: content, + map: JSON.parse(transformed.map), + hash, + sourceType: 'module', + }; + }); +} + + BCp.processFilesForTarget = function (inputFiles) { var compiler = this; @@ -147,16 +208,16 @@ BCp.processOneFileForTarget = function (inputFile, source) { } try { - var result = profile('Babel.compile', function () { - // Determine if SWC should be used based on package and file criteria. + var result = (function getTranspilerCompilation() { const packagesSkipSwc = []; const fileSkipSwc = []; // top level await + + // Determine if SWC should be used based on package and file criteria. const shouldUseSwc = !packagesSkipSwc.includes(packageName) && !fileSkipSwc.includes(inputFilePath) && - !self._swcIncompatible[toBeAdded.hash]; + !this._swcIncompatible[toBeAdded.hash]; - // Check RAM cache let compilation; try { if (shouldUseSwc) { @@ -165,14 +226,14 @@ BCp.processOneFileForTarget = function (inputFile, source) { const cacheContext = '.swc-cache'; // Check RAM cache - compilation = self._swcCache[cacheKey]; + compilation = this._swcCache[cacheKey]; // Check file system cache if enabled - if (!compilation && self.cacheDirectory) { - const cacheFilePath = path.join(self.cacheDirectory, cacheContext, cacheKey + '.json'); + if (!compilation && this.cacheDirectory) { + const cacheFilePath = path.join(this.cacheDirectory, cacheContext, cacheKey + '.json'); if (fs.existsSync(cacheFilePath)) { try { compilation = JSON.parse(fs.readFileSync(cacheFilePath, 'utf8')); - self._swcCache[cacheKey] = compilation; + this._swcCache[cacheKey] = compilation; } catch (e) { // If reading/parsing the cache fails, ignore and continue. } @@ -183,60 +244,12 @@ BCp.processOneFileForTarget = function (inputFile, source) { return compilation; } - const isTypescriptSyntax = inputFilePath.endsWith('.ts') || inputFilePath.endsWith('.tsx'); - const hasTSXSupport = inputFilePath.endsWith('.tsx'); - const hasJSXSupport = inputFilePath.endsWith('.jsx'); - - // Perform compilation - const transformed = SWC.transformSync(source, { - jsc: { - target: 'es2015', - parser: { - syntax: isTypescriptSyntax ? 'typescript' : 'ecmascript', - jsx: hasJSXSupport, - tsx: hasTSXSupport, - }, - }, - module: { type: 'es6' }, - minify: false, - sourceMaps: true, - }); - - let content = transformed.code; - // Perserve Meteor's specifics: reify modules, nested imports and top-level await support. - const result = reifyCompile(content, { - parse: reifyAcornParse, - generateLetDeclarations: false, - ast: false, - // Enforce reify options for proper compatibility (TODO export getReifyOptions) - // https://github.com/meteor/meteor/blob/devel/npm-packages/meteor-babel/options.js#L19 - avoidModernSyntax: true, - enforceStrictMode: false, - dynamicImport: true, - ...features.topLevelAwait && { topLevelAwait: true }, - ...features.compileForShell && { moduleAlias: 'module' }, - ...(features.modernBrowsers || - features.nodeMajorVersion >= 8) && { - avoidModernSyntax: false, - generateLetDeclarations: true, - }, - }); - if (!result.identical) { - identical = false; - content = result.code; - } - - compilation = { - code: content, - map: JSON.parse(transformed.map), - hash: toBeAdded.hash, - sourceType: 'module', - }; + compilation = compileWithSWC(source, { inputFilePath, hash: toBeAdded.hash, features }); // Save result in cache - self._swcCache[cacheKey] = compilation; - if (self.cacheDirectory) { - const cacheFilePath = path.join(self.cacheDirectory, cacheContext, cacheKey + '.json'); + this._swcCache[cacheKey] = compilation; + if (this.cacheDirectory) { + const cacheFilePath = path.join(this.cacheDirectory, cacheContext, cacheKey + '.json'); try { const writeFileCache = async () => { await fs.promises.mkdir(path.dirname(cacheFilePath), { recursive: true }); @@ -249,15 +262,16 @@ BCp.processOneFileForTarget = function (inputFile, source) { } } } else { - compilation = Babel.compile(source, babelOptions, cacheOptions); + compilation = compileWithBabel(source, babelOptions, cacheOptions); } } catch (e) { - self._swcIncompatible[toBeAdded.hash] = true; + this._swcIncompatible[toBeAdded.hash] = true; // If SWC fails, fall back to Babel - compilation = Babel.compile(source, babelOptions, cacheOptions); + compilation = compileWithBabel(source, babelOptions, cacheOptions); } + return compilation; - }); + }).call(this); } catch (e) { if (e.loc) { // Error is from @babel/parser. From 04c71f1a5b90fa17878c4bc2c5cf19232db29175 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 1 Apr 2025 19:40:25 +0200 Subject: [PATCH 29/63] prefer an arrow function --- packages/babel-compiler/babel-compiler.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/babel-compiler/babel-compiler.js b/packages/babel-compiler/babel-compiler.js index d5e1f7437a..f4df7343c9 100644 --- a/packages/babel-compiler/babel-compiler.js +++ b/packages/babel-compiler/babel-compiler.js @@ -208,7 +208,7 @@ BCp.processOneFileForTarget = function (inputFile, source) { } try { - var result = (function getTranspilerCompilation() { + var result = (() => { const packagesSkipSwc = []; const fileSkipSwc = []; // top level await @@ -271,7 +271,7 @@ BCp.processOneFileForTarget = function (inputFile, source) { } return compilation; - }).call(this); + })(); } catch (e) { if (e.loc) { // Error is from @babel/parser. From d137aea93c4815f7b399f20d8a63e050b6bb14a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 1 Apr 2025 19:51:11 +0200 Subject: [PATCH 30/63] refactor cache code --- packages/babel-compiler/babel-compiler.js | 82 +++++++++++++---------- 1 file changed, 46 insertions(+), 36 deletions(-) diff --git a/packages/babel-compiler/babel-compiler.js b/packages/babel-compiler/babel-compiler.js index f4df7343c9..54727ee938 100644 --- a/packages/babel-compiler/babel-compiler.js +++ b/packages/babel-compiler/babel-compiler.js @@ -35,7 +35,7 @@ function compileWithBabel(source, babelOptions, cacheOptions) { }); } -function compileWithSWC(source, { inputFilePath, hash, features, ...swcOptions }) { +function compileWithSwc(source, swcOptions, { inputFilePath, features }) { return profile('SWC.compile', function () { // Determine file extension based syntax. const isTypescriptSyntax = inputFilePath.endsWith('.ts') || inputFilePath.endsWith('.tsx'); @@ -83,7 +83,6 @@ function compileWithSWC(source, { inputFilePath, hash, features, ...swcOptions } return { code: content, map: JSON.parse(transformed.map), - hash, sourceType: 'module', }; }); @@ -221,46 +220,17 @@ BCp.processOneFileForTarget = function (inputFile, source) { let compilation; try { if (shouldUseSwc) { - // Create a cache key based on the source hash and the compiler used. + // Create a cache key based on the source hash and the compiler used const cacheKey = toBeAdded.hash; - const cacheContext = '.swc-cache'; - - // Check RAM cache - compilation = this._swcCache[cacheKey]; - // Check file system cache if enabled - if (!compilation && this.cacheDirectory) { - const cacheFilePath = path.join(this.cacheDirectory, cacheContext, cacheKey + '.json'); - if (fs.existsSync(cacheFilePath)) { - try { - compilation = JSON.parse(fs.readFileSync(cacheFilePath, 'utf8')); - this._swcCache[cacheKey] = compilation; - } catch (e) { - // If reading/parsing the cache fails, ignore and continue. - } - } - } + // Check cache + compilation = this.readFromSwcCache({ cacheKey }); // Return cached result if found. if (compilation) { return compilation; } - - compilation = compileWithSWC(source, { inputFilePath, hash: toBeAdded.hash, features }); - + compilation = compileWithSwc(source, {}, { inputFilePath, features }); // Save result in cache - this._swcCache[cacheKey] = compilation; - if (this.cacheDirectory) { - const cacheFilePath = path.join(this.cacheDirectory, cacheContext, cacheKey + '.json'); - try { - const writeFileCache = async () => { - await fs.promises.mkdir(path.dirname(cacheFilePath), { recursive: true }); - await fs.promises.writeFile(cacheFilePath, JSON.stringify(compilation), 'utf8'); - }; - // Asynchronously write the cache without blocking - writeFileCache(); - } catch (e) { - // If file caching fails, ignore the error. - } - } + this.writeToSwcCache({ cacheKey, compilation }); } else { compilation = compileWithBabel(source, babelOptions, cacheOptions); } @@ -689,3 +659,43 @@ function packageNameFromTopLevelModuleId(id) { } return parts[0]; } + +const SwcCacheContext = '.swc-cache'; + +BCp.readFromSwcCache = function({ cacheKey }) { + // Check in-memory cache. + let compilation = this._swcCache[cacheKey]; + // If not found, try file system cache if enabled. + if (!compilation && this.cacheDirectory) { + const cacheFilePath = path.join(this.cacheDirectory, SwcCacheContext, `${cacheKey}.json`); + if (fs.existsSync(cacheFilePath)) { + try { + compilation = JSON.parse(fs.readFileSync(cacheFilePath, 'utf8')); + // Save back to in-memory cache. + this._swcCache[cacheKey] = compilation; + } catch (err) { + // Ignore any errors reading/parsing the cache. + } + } + } + return compilation; +}; + +BCp.writeToSwcCache = function({ cacheKey, compilation }) { + // Save to in-memory cache. + this._swcCache[cacheKey] = compilation; + // If file system caching is enabled, write asynchronously. + if (this.cacheDirectory) { + const cacheFilePath = path.join(this.cacheDirectory, SwcCacheContext, `${cacheKey}.json`); + try { + const writeFileCache = async () => { + await fs.promises.mkdir(path.dirname(cacheFilePath), { recursive: true }); + await fs.promises.writeFile(cacheFilePath, JSON.stringify(compilation), 'utf8'); + }; + // Invoke without blocking the main flow. + writeFileCache(); + } catch (err) { + // If writing fails, ignore the error. + } + } +}; From b512040335ca918e27d611092a79dfaab428fc07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 1 Apr 2025 19:52:35 +0200 Subject: [PATCH 31/63] remove unrequired self --- packages/babel-compiler/babel-compiler.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/babel-compiler/babel-compiler.js b/packages/babel-compiler/babel-compiler.js index 54727ee938..35e3588df7 100644 --- a/packages/babel-compiler/babel-compiler.js +++ b/packages/babel-compiler/babel-compiler.js @@ -115,7 +115,6 @@ BCp.processFilesForTarget = function (inputFiles) { // Returns an object suitable for passing to inputFile.addJavaScript, or // null to indicate there was an error, and nothing should be added. BCp.processOneFileForTarget = function (inputFile, source) { - var self = this; // capture context this._babelrcCache = this._babelrcCache || Object.create(null); this._swcCache = this._swcCache || Object.create(null); this._swcIncompatible = this._swcIncompatible || Object.create(null); From 048f81126acf791e0c9f308bc83b94ce6aaa21e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Wed, 2 Apr 2025 17:41:31 +0200 Subject: [PATCH 32/63] implement config to enable SWC --- packages/babel-compiler/babel-compiler.js | 64 ++++++++++++++++++++--- 1 file changed, 58 insertions(+), 6 deletions(-) diff --git a/packages/babel-compiler/babel-compiler.js b/packages/babel-compiler/babel-compiler.js index 35e3588df7..7903502e4d 100644 --- a/packages/babel-compiler/babel-compiler.js +++ b/packages/babel-compiler/babel-compiler.js @@ -88,6 +88,53 @@ function compileWithSwc(source, swcOptions, { inputFilePath, features }) { }); } +function getMeteorAppDir() { + return process.cwd(); +} + +function getMeteorAppPackageJson() { + return JSON.parse( + fs.readFileSync(`${getMeteorAppDir()}/package.json`, 'utf-8'), + ); +} + +const _regexCache = new Map(); + +function isRegexLike(str) { + return /[.*+?^${}()|[\]\\]/.test(str); +} + +function isExcludedConfig(name, excludeList = []) { + return excludeList.some(rule => { + if (name === rule) return true; + if (isRegexLike(rule)) { + let regex = _regexCache.get(rule); + if (!regex) { + try { + regex = new RegExp(rule); + _regexCache.set(rule, regex); + } catch (err) { + console.warn(`Invalid regex in exclude list: "${rule}"`); + return false; + } + } + return regex.test(name); + } + + return false; + }); +} + +BCp.initializeMeteorAppConfig = function () { + const currentLastModifiedConfigTime = fs + .statSync(`${getMeteorAppDir()}/package.json`) + ?.mtime?.getTime(); + if (currentLastModifiedConfigTime !== this.lastModifiedConfigTime) { + this.lastModifiedConfigTime = currentLastModifiedConfigTime; + this.lastModifiedConfig = getMeteorAppPackageJson()?.meteor; + } + return this.lastModifiedConfig; +}; BCp.processFilesForTarget = function (inputFiles) { var compiler = this; @@ -95,6 +142,8 @@ BCp.processFilesForTarget = function (inputFiles) { // Reset this cache for each batch processed. this._babelrcCache = null; + this.initializeMeteorAppConfig(); + inputFiles.forEach(function (inputFile) { if (inputFile.supportsLazyCompilation) { inputFile.addJavaScript({ @@ -207,14 +256,17 @@ BCp.processOneFileForTarget = function (inputFile, source) { try { var result = (() => { - const packagesSkipSwc = []; - const fileSkipSwc = []; // top level await - + const isAppCode = packageName == null; + const config = this.lastModifiedConfig?.modernTranspiler; + const hasModernTranspiler = config != null; + const shouldSkipSwc = + !hasModernTranspiler || + (config.excludeApp === true && isAppCode) || + isExcludedConfig(packageName, config.excludePackages || []) || + (isAppCode && isExcludedConfig(inputFilePath, config.excludeFiles || [])); // Determine if SWC should be used based on package and file criteria. const shouldUseSwc = - !packagesSkipSwc.includes(packageName) && - !fileSkipSwc.includes(inputFilePath) && - !this._swcIncompatible[toBeAdded.hash]; + !shouldSkipSwc && !this._swcIncompatible[toBeAdded.hash]; let compilation; try { From cb68023bf2998dc2811eea3c6d692753ba27d86e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Wed, 2 Apr 2025 17:56:26 +0200 Subject: [PATCH 33/63] better naming --- packages/babel-compiler/babel-compiler.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/babel-compiler/babel-compiler.js b/packages/babel-compiler/babel-compiler.js index 7903502e4d..9d1c946fdd 100644 --- a/packages/babel-compiler/babel-compiler.js +++ b/packages/babel-compiler/babel-compiler.js @@ -262,8 +262,8 @@ BCp.processOneFileForTarget = function (inputFile, source) { const shouldSkipSwc = !hasModernTranspiler || (config.excludeApp === true && isAppCode) || - isExcludedConfig(packageName, config.excludePackages || []) || - (isAppCode && isExcludedConfig(inputFilePath, config.excludeFiles || [])); + (isAppCode && isExcludedConfig(inputFilePath, config.excludeAppFiles || [])) || + isExcludedConfig(packageName, config.excludePackages || []); // Determine if SWC should be used based on package and file criteria. const shouldUseSwc = !shouldSkipSwc && !this._swcIncompatible[toBeAdded.hash]; From fb2bae51979633adcd80f085f0566cbc30bbd86d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Fri, 4 Apr 2025 14:14:11 +0200 Subject: [PATCH 34/63] support a configuration to exclude by node_modules and simplify config --- packages/babel-compiler/babel-compiler.js | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/packages/babel-compiler/babel-compiler.js b/packages/babel-compiler/babel-compiler.js index 9d1c946fdd..d388e2b466 100644 --- a/packages/babel-compiler/babel-compiler.js +++ b/packages/babel-compiler/babel-compiler.js @@ -104,9 +104,11 @@ function isRegexLike(str) { return /[.*+?^${}()|[\]\\]/.test(str); } -function isExcludedConfig(name, excludeList = []) { +function isExcludedConfig(name, excludeList = [], startsWith) { + if (!name) return false; return excludeList.some(rule => { if (name === rule) return true; + if (startsWith && name.startsWith(rule)) return true; if (isRegexLike(rule)) { let regex = _regexCache.get(rule); if (!regex) { @@ -256,13 +258,26 @@ BCp.processOneFileForTarget = function (inputFile, source) { try { var result = (() => { - const isAppCode = packageName == null; + const isNodeModulesCode = packageName == null && inputFilePath.includes("node_modules/"); + const isAppCode = packageName == null && !isNodeModulesCode; const config = this.lastModifiedConfig?.modernTranspiler; const hasModernTranspiler = config != null; const shouldSkipSwc = !hasModernTranspiler || (config.excludeApp === true && isAppCode) || - (isAppCode && isExcludedConfig(inputFilePath, config.excludeAppFiles || [])) || + (config.excludeNodeModules === true && isNodeModulesCode) || + (isAppCode && + Array.isArray(config.excludeApp) && + isExcludedConfig(inputFilePath, config.excludeApp || [])) || + (isNodeModulesCode && + Array.isArray(config.excludeNodeModules) && + (isExcludedConfig(inputFilePath, config.excludeNodeModules || []) || + isExcludedConfig( + inputFilePath.replace('node_modules/', ''), + config.excludeNodeModules || [], + true, + )) + ) || isExcludedConfig(packageName, config.excludePackages || []); // Determine if SWC should be used based on package and file criteria. const shouldUseSwc = From 85e7f6e7207e2913002cc1ae0d5c8ee323abd5f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Fri, 4 Apr 2025 14:36:51 +0200 Subject: [PATCH 35/63] support a configuration to exclude by meteor package and its path --- packages/babel-compiler/babel-compiler.js | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/packages/babel-compiler/babel-compiler.js b/packages/babel-compiler/babel-compiler.js index d388e2b466..e280801181 100644 --- a/packages/babel-compiler/babel-compiler.js +++ b/packages/babel-compiler/babel-compiler.js @@ -105,7 +105,7 @@ function isRegexLike(str) { } function isExcludedConfig(name, excludeList = [], startsWith) { - if (!name) return false; + if (!name || !excludeList?.length) return false; return excludeList.some(rule => { if (name === rule) return true; if (startsWith && name.startsWith(rule)) return true; @@ -260,12 +260,14 @@ BCp.processOneFileForTarget = function (inputFile, source) { var result = (() => { const isNodeModulesCode = packageName == null && inputFilePath.includes("node_modules/"); const isAppCode = packageName == null && !isNodeModulesCode; + const isPackageCode = packageName != null; const config = this.lastModifiedConfig?.modernTranspiler; const hasModernTranspiler = config != null; const shouldSkipSwc = !hasModernTranspiler || - (config.excludeApp === true && isAppCode) || - (config.excludeNodeModules === true && isNodeModulesCode) || + (isAppCode && config.excludeApp === true) || + (isNodeModulesCode && config.excludeNodeModules === true) || + (isPackageCode && config.excludePackages === true) || (isAppCode && Array.isArray(config.excludeApp) && isExcludedConfig(inputFilePath, config.excludeApp || [])) || @@ -276,9 +278,14 @@ BCp.processOneFileForTarget = function (inputFile, source) { inputFilePath.replace('node_modules/', ''), config.excludeNodeModules || [], true, - )) - ) || - isExcludedConfig(packageName, config.excludePackages || []); + ))) || + (isPackageCode && + Array.isArray(config.excludePackages) && + (isExcludedConfig(packageName, config.excludePackages || []) || + isExcludedConfig( + `${packageName}/${inputFilePath}`, + config.excludePackages || [], + ))); // Determine if SWC should be used based on package and file criteria. const shouldUseSwc = !shouldSkipSwc && !this._swcIncompatible[toBeAdded.hash]; From cc1f6a4943a42606421bb29130e78e574ba51c9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Mon, 7 Apr 2025 15:31:34 +0200 Subject: [PATCH 36/63] add a verbose config to get insights of transpilation process --- packages/babel-compiler/babel-compiler.js | 99 ++++++++++++++++++++++- 1 file changed, 98 insertions(+), 1 deletion(-) diff --git a/packages/babel-compiler/babel-compiler.js b/packages/babel-compiler/babel-compiler.js index e280801181..0b3d9cc4a9 100644 --- a/packages/babel-compiler/babel-compiler.js +++ b/packages/babel-compiler/babel-compiler.js @@ -104,6 +104,62 @@ function isRegexLike(str) { return /[.*+?^${}()|[\]\\]/.test(str); } +function color(text, code) { + return `\x1b[${code}m${text}\x1b[0m`; +} + +function logTranspilation({ + packageName, + inputFilePath, + usedSwc, + cacheHit, + isNodeModulesCode, + errorMessage = '', + tip = '', +}) { + const transpiler = usedSwc ? 'SWC' : 'Babel'; + const transpilerColor = usedSwc ? 32 : 33; + const label = color('[Transpiler]', 36); + const transpilerPart = `${label} Used ${color( + transpiler, + transpilerColor, + )} for`; + const filePathPadded = `${ + packageName ? `${packageName}/` : '' + }${inputFilePath}`.padEnd(50); + let rawOrigin = ''; + if (packageName) { + rawOrigin = `(package)`; + } else { + rawOrigin = isNodeModulesCode ? '(node_modules)' : '(app)'; + } + const originPaddedRaw = rawOrigin.padEnd(35); + const originPaddedColored = packageName + ? originPaddedRaw + : isNodeModulesCode + ? color(originPaddedRaw, 90) + : color(originPaddedRaw, 35); + const cacheStatus = errorMessage + ? color('⚠️ Fallback', 33) + : usedSwc + ? cacheHit + ? color('🟢 Cache hit', 32) + : color('🔴 Cache miss', 31) + : ''; + console.log( + `${transpilerPart} ${filePathPadded}${originPaddedColored}${cacheStatus}`, + ); + if (errorMessage) { + console.log(); + console.log(` ↳ ${color('Error:', 31)} ${errorMessage}`); + if (tip) { + console.log(); + console.log(` ${color('💡 Tip:', 33)} ${tip}`); + } + console.log(); + } +} + function isExcludedConfig(name, excludeList = [], startsWith) { if (!name || !excludeList?.length) return false; return excludeList.some(rule => { @@ -292,6 +348,7 @@ BCp.processOneFileForTarget = function (inputFile, source) { let compilation; try { + let usedSwc = false; if (shouldUseSwc) { // Create a cache key based on the source hash and the compiler used const cacheKey = toBeAdded.hash; @@ -299,18 +356,58 @@ BCp.processOneFileForTarget = function (inputFile, source) { compilation = this.readFromSwcCache({ cacheKey }); // Return cached result if found. if (compilation) { + if (config?.verbose) { + logTranspilation({ + usedSwc: true, + inputFilePath, + packageName, + isNodeModulesCode, + cacheHit: true, + }); + } return compilation; } - compilation = compileWithSwc(source, {}, { inputFilePath, features }); + compilation = compileWithSwc( + source, + {}, + { inputFilePath, features }, + ); // Save result in cache this.writeToSwcCache({ cacheKey, compilation }); + usedSwc = true; } else { compilation = compileWithBabel(source, babelOptions, cacheOptions); + usedSwc = false; + } + + if (config?.verbose) { + logTranspilation({ + usedSwc, + inputFilePath, + packageName, + isNodeModulesCode, + cacheHit: false, + }); } } catch (e) { this._swcIncompatible[toBeAdded.hash] = true; // If SWC fails, fall back to Babel compilation = compileWithBabel(source, babelOptions, cacheOptions); + if (config?.verbose) { + logTranspilation({ + usedSwc: false, + inputFilePath, + packageName, + isNodeModulesCode, + cacheHit: false, + errorMessage: e?.message, + ...(e?.message?.includes( + 'cannot be used outside of module code', + ) && { + tip: 'Remove nested import to support SWC transpilation and improve speed', + }), + }); + } } return compilation; From 14202008ac648e135328715a235e7551f40d5796 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Mon, 7 Apr 2025 16:22:36 +0200 Subject: [PATCH 37/63] better tip on nested imports --- packages/babel-compiler/babel-compiler.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/babel-compiler/babel-compiler.js b/packages/babel-compiler/babel-compiler.js index 0b3d9cc4a9..6402f4dc99 100644 --- a/packages/babel-compiler/babel-compiler.js +++ b/packages/babel-compiler/babel-compiler.js @@ -404,7 +404,7 @@ BCp.processOneFileForTarget = function (inputFile, source) { ...(e?.message?.includes( 'cannot be used outside of module code', ) && { - tip: 'Remove nested import to support SWC transpilation and improve speed', + tip: 'Remove nested imports or replace them with require to support SWC and improve speed.', }), }); } From 681f59a20606b85b388b735547a6f71c5b7dc8cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Mon, 7 Apr 2025 17:29:57 +0200 Subject: [PATCH 38/63] support swcConfig and plugin support --- packages/babel-compiler/babel-compiler.js | 278 +++++++++++++--------- 1 file changed, 169 insertions(+), 109 deletions(-) diff --git a/packages/babel-compiler/babel-compiler.js b/packages/babel-compiler/babel-compiler.js index 6402f4dc99..526b1cb215 100644 --- a/packages/babel-compiler/babel-compiler.js +++ b/packages/babel-compiler/babel-compiler.js @@ -42,9 +42,7 @@ function compileWithSwc(source, swcOptions, { inputFilePath, features }) { const hasTSXSupport = inputFilePath.endsWith('.tsx'); const hasJSXSupport = inputFilePath.endsWith('.jsx'); - // Perform SWC transformation. - const transformed = SWC.transformSync(source, { - ...swcOptions, + const baseSwcConfig = { jsc: { target: 'es2015', parser: { @@ -56,7 +54,19 @@ function compileWithSwc(source, swcOptions, { inputFilePath, features }) { module: { type: 'es6' }, minify: false, sourceMaps: true, - }); + }; + const nextSwcConfig = + Object.keys(swcOptions)?.length > 0 + ? deepMerge(baseSwcConfig, swcOptions, [ + 'module.type', + 'jsc.parser.syntax', + 'jsc.parser.jsx', + 'jsc.parser.tsx', + ]) + : baseSwcConfig; + + // Perform SWC transformation. + const transformed = SWC.transformSync(source, nextSwcConfig); let content = transformed.code; @@ -88,110 +98,31 @@ function compileWithSwc(source, swcOptions, { inputFilePath, features }) { }); } -function getMeteorAppDir() { - return process.cwd(); -} - -function getMeteorAppPackageJson() { - return JSON.parse( - fs.readFileSync(`${getMeteorAppDir()}/package.json`, 'utf-8'), - ); -} - -const _regexCache = new Map(); - -function isRegexLike(str) { - return /[.*+?^${}()|[\]\\]/.test(str); -} - -function color(text, code) { - return `\x1b[${code}m${text}\x1b[0m`; -} - -function logTranspilation({ - packageName, - inputFilePath, - usedSwc, - cacheHit, - isNodeModulesCode, - errorMessage = '', - tip = '', -}) { - const transpiler = usedSwc ? 'SWC' : 'Babel'; - const transpilerColor = usedSwc ? 32 : 33; - const label = color('[Transpiler]', 36); - const transpilerPart = `${label} Used ${color( - transpiler, - transpilerColor, - )} for`; - const filePathPadded = `${ - packageName ? `${packageName}/` : '' - }${inputFilePath}`.padEnd(50); - let rawOrigin = ''; - if (packageName) { - rawOrigin = `(package)`; - } else { - rawOrigin = isNodeModulesCode ? '(node_modules)' : '(app)'; - } - const originPaddedRaw = rawOrigin.padEnd(35); - const originPaddedColored = packageName - ? originPaddedRaw - : isNodeModulesCode - ? color(originPaddedRaw, 90) - : color(originPaddedRaw, 35); - const cacheStatus = errorMessage - ? color('⚠️ Fallback', 33) - : usedSwc - ? cacheHit - ? color('🟢 Cache hit', 32) - : color('🔴 Cache miss', 31) - : ''; - console.log( - `${transpilerPart} ${filePathPadded}${originPaddedColored}${cacheStatus}`, - ); - if (errorMessage) { - console.log(); - console.log(` ↳ ${color('Error:', 31)} ${errorMessage}`); - if (tip) { - console.log(); - console.log(` ${color('💡 Tip:', 33)} ${tip}`); - } - console.log(); - } -} - -function isExcludedConfig(name, excludeList = [], startsWith) { - if (!name || !excludeList?.length) return false; - return excludeList.some(rule => { - if (name === rule) return true; - if (startsWith && name.startsWith(rule)) return true; - if (isRegexLike(rule)) { - let regex = _regexCache.get(rule); - if (!regex) { - try { - regex = new RegExp(rule); - _regexCache.set(rule, regex); - } catch (err) { - console.warn(`Invalid regex in exclude list: "${rule}"`); - return false; - } - } - return regex.test(name); - } - - return false; - }); -} - BCp.initializeMeteorAppConfig = function () { const currentLastModifiedConfigTime = fs .statSync(`${getMeteorAppDir()}/package.json`) ?.mtime?.getTime(); - if (currentLastModifiedConfigTime !== this.lastModifiedConfigTime) { - this.lastModifiedConfigTime = currentLastModifiedConfigTime; - this.lastModifiedConfig = getMeteorAppPackageJson()?.meteor; + if (currentLastModifiedConfigTime !== this.lastModifiedMeteorConfigTime) { + this.lastModifiedMeteorConfigTime = currentLastModifiedConfigTime; + this.lastModifiedMeteorConfig = getMeteorAppPackageJson()?.meteor; } - return this.lastModifiedConfig; + return this.lastModifiedMeteorConfig; +}; + +BCp.initializeMeteorAppSwcrc = function () { + if (!fs.existsSync(`${getMeteorAppDir()}/.swcrc`)) { + this.lastModifiedSwcConfig = {}; + return; + } + + const currentLastModifiedConfigTime = fs + .statSync(`${getMeteorAppDir()}/.swcrc`) + ?.mtime?.getTime(); + if (currentLastModifiedConfigTime !== this.lastModifiedSwcConfigTime) { + this.lastModifiedSwcConfigTime = currentLastModifiedConfigTime; + this.lastModifiedSwcConfig = getMeteorAppSwcrc(); + } + return this.lastModifiedSwcConfig; }; BCp.processFilesForTarget = function (inputFiles) { @@ -201,6 +132,7 @@ BCp.processFilesForTarget = function (inputFiles) { this._babelrcCache = null; this.initializeMeteorAppConfig(); + this.initializeMeteorAppSwcrc(); inputFiles.forEach(function (inputFile) { if (inputFile.supportsLazyCompilation) { @@ -317,7 +249,7 @@ BCp.processOneFileForTarget = function (inputFile, source) { const isNodeModulesCode = packageName == null && inputFilePath.includes("node_modules/"); const isAppCode = packageName == null && !isNodeModulesCode; const isPackageCode = packageName != null; - const config = this.lastModifiedConfig?.modernTranspiler; + const config = this.lastModifiedMeteorConfig?.modernTranspiler; const hasModernTranspiler = config != null; const shouldSkipSwc = !hasModernTranspiler || @@ -342,16 +274,16 @@ BCp.processOneFileForTarget = function (inputFile, source) { `${packageName}/${inputFilePath}`, config.excludePackages || [], ))); + + const cacheKey = `${toBeAdded.hash}${this.lastModifiedSwcConfigTime ||''}`; // Determine if SWC should be used based on package and file criteria. - const shouldUseSwc = - !shouldSkipSwc && !this._swcIncompatible[toBeAdded.hash]; + const shouldUseSwc = !shouldSkipSwc && !this._swcIncompatible[cacheKey]; let compilation; try { let usedSwc = false; if (shouldUseSwc) { // Create a cache key based on the source hash and the compiler used - const cacheKey = toBeAdded.hash; // Check cache compilation = this.readFromSwcCache({ cacheKey }); // Return cached result if found. @@ -369,7 +301,7 @@ BCp.processOneFileForTarget = function (inputFile, source) { } compilation = compileWithSwc( source, - {}, + this.lastModifiedSwcConfig, { inputFilePath, features }, ); // Save result in cache @@ -390,7 +322,7 @@ BCp.processOneFileForTarget = function (inputFile, source) { }); } } catch (e) { - this._swcIncompatible[toBeAdded.hash] = true; + this._swcIncompatible[cacheKey] = true; // If SWC fails, fall back to Babel compilation = compileWithBabel(source, babelOptions, cacheOptions); if (config?.verbose) { @@ -869,3 +801,131 @@ BCp.writeToSwcCache = function({ cacheKey, compilation }) { } } }; + +function getMeteorAppDir() { + return process.cwd(); +} + +function getMeteorAppPackageJson() { + return JSON.parse( + fs.readFileSync(`${getMeteorAppDir()}/package.json`, 'utf-8'), + ); +} + +function getMeteorAppSwcrc() { + try { + return JSON.parse(fs.readFileSync(`${getMeteorAppDir()}/.swcrc`, 'utf-8')); + } catch (e) { + console.error('Error parsing .swcrc file', e); + } +} + +const _regexCache = new Map(); + +function isRegexLike(str) { + return /[.*+?^${}()|[\]\\]/.test(str); +} + +function isExcludedConfig(name, excludeList = [], startsWith) { + if (!name || !excludeList?.length) return false; + return excludeList.some(rule => { + if (name === rule) return true; + if (startsWith && name.startsWith(rule)) return true; + if (isRegexLike(rule)) { + let regex = _regexCache.get(rule); + if (!regex) { + try { + regex = new RegExp(rule); + _regexCache.set(rule, regex); + } catch (err) { + console.warn(`Invalid regex in exclude list: "${rule}"`); + return false; + } + } + return regex.test(name); + } + + return false; + }); +} + +function color(text, code) { + return `\x1b[${code}m${text}\x1b[0m`; +} + +function logTranspilation({ + packageName, + inputFilePath, + usedSwc, + cacheHit, + isNodeModulesCode, + errorMessage = '', + tip = '', +}) { + const transpiler = usedSwc ? 'SWC' : 'Babel'; + const transpilerColor = usedSwc ? 32 : 33; + const label = color('[Transpiler]', 36); + const transpilerPart = `${label} Used ${color( + transpiler, + transpilerColor, + )} for`; + const filePathPadded = `${ + packageName ? `${packageName}/` : '' + }${inputFilePath}`.padEnd(50); + let rawOrigin = ''; + if (packageName) { + rawOrigin = `(package)`; + } else { + rawOrigin = isNodeModulesCode ? '(node_modules)' : '(app)'; + } + const originPaddedRaw = rawOrigin.padEnd(35); + const originPaddedColored = packageName + ? originPaddedRaw + : isNodeModulesCode + ? color(originPaddedRaw, 90) + : color(originPaddedRaw, 35); + const cacheStatus = errorMessage + ? color('⚠️ Fallback', 33) + : usedSwc + ? cacheHit + ? color('🟢 Cache hit', 32) + : color('🔴 Cache miss', 31) + : ''; + console.log( + `${transpilerPart} ${filePathPadded}${originPaddedColored}${cacheStatus}`, + ); + if (errorMessage) { + console.log(); + console.log(` ↳ ${color('Error:', 31)} ${errorMessage}`); + if (tip) { + console.log(); + console.log(` ${color('💡 Tip:', 33)} ${tip}`); + } + console.log(); + } +} + +function deepMerge(target, source, preservePaths, inPath = '') { + for (const key in source) { + const fullPath = inPath ? `${inPath}.${key}` : key; + + // Skip preserved paths + if (preservePaths.includes(fullPath)) continue; + + if ( + typeof source[key] === 'object' && + source[key] !== null && + !Array.isArray(source[key]) + ) { + target[key] = deepMerge( + target[key] || {}, + source[key], + preservePaths, + fullPath, + ); + } else { + target[key] = source[key]; + } + } + return target; +} From 630fb0ef233b1e658ffc7afbd114013e6ef5fb04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Mon, 7 Apr 2025 17:59:24 +0200 Subject: [PATCH 39/63] print configs in verbose modes --- packages/babel-compiler/babel-compiler.js | 52 +++++++++++++++++------ 1 file changed, 39 insertions(+), 13 deletions(-) diff --git a/packages/babel-compiler/babel-compiler.js b/packages/babel-compiler/babel-compiler.js index 526b1cb215..0d88ec35dd 100644 --- a/packages/babel-compiler/babel-compiler.js +++ b/packages/babel-compiler/babel-compiler.js @@ -98,31 +98,42 @@ function compileWithSwc(source, swcOptions, { inputFilePath, features }) { }); } +let lastModifiedMeteorConfig; +let lastModifiedMeteorConfigTime; BCp.initializeMeteorAppConfig = function () { const currentLastModifiedConfigTime = fs .statSync(`${getMeteorAppDir()}/package.json`) ?.mtime?.getTime(); - if (currentLastModifiedConfigTime !== this.lastModifiedMeteorConfigTime) { - this.lastModifiedMeteorConfigTime = currentLastModifiedConfigTime; - this.lastModifiedMeteorConfig = getMeteorAppPackageJson()?.meteor; + if (currentLastModifiedConfigTime !== lastModifiedMeteorConfigTime) { + lastModifiedMeteorConfigTime = currentLastModifiedConfigTime; + lastModifiedMeteorConfig = getMeteorAppPackageJson()?.meteor; + + if (lastModifiedMeteorConfig?.modernTranspiler?.verbose) { + logConfigBlock('Meteor Config', lastModifiedMeteorConfig); + } } - return this.lastModifiedMeteorConfig; + return lastModifiedMeteorConfig; }; +let lastModifiedSwcConfig; +let lastModifiedSwcConfigTime; BCp.initializeMeteorAppSwcrc = function () { if (!fs.existsSync(`${getMeteorAppDir()}/.swcrc`)) { - this.lastModifiedSwcConfig = {}; + lastModifiedSwcConfig = {}; return; } - const currentLastModifiedConfigTime = fs .statSync(`${getMeteorAppDir()}/.swcrc`) ?.mtime?.getTime(); - if (currentLastModifiedConfigTime !== this.lastModifiedSwcConfigTime) { - this.lastModifiedSwcConfigTime = currentLastModifiedConfigTime; - this.lastModifiedSwcConfig = getMeteorAppSwcrc(); + if (currentLastModifiedConfigTime !== lastModifiedSwcConfigTime) { + lastModifiedSwcConfigTime = currentLastModifiedConfigTime; + lastModifiedSwcConfig = getMeteorAppSwcrc(); + + if (lastModifiedMeteorConfig?.modernTranspiler?.verbose) { + logConfigBlock('SWC Config', lastModifiedSwcConfig); + } } - return this.lastModifiedSwcConfig; + return lastModifiedSwcConfig; }; BCp.processFilesForTarget = function (inputFiles) { @@ -249,7 +260,7 @@ BCp.processOneFileForTarget = function (inputFile, source) { const isNodeModulesCode = packageName == null && inputFilePath.includes("node_modules/"); const isAppCode = packageName == null && !isNodeModulesCode; const isPackageCode = packageName != null; - const config = this.lastModifiedMeteorConfig?.modernTranspiler; + const config = lastModifiedMeteorConfig?.modernTranspiler; const hasModernTranspiler = config != null; const shouldSkipSwc = !hasModernTranspiler || @@ -275,7 +286,7 @@ BCp.processOneFileForTarget = function (inputFile, source) { config.excludePackages || [], ))); - const cacheKey = `${toBeAdded.hash}${this.lastModifiedSwcConfigTime ||''}`; + const cacheKey = `${toBeAdded.hash}${lastModifiedSwcConfigTime ||''}`; // Determine if SWC should be used based on package and file criteria. const shouldUseSwc = !shouldSkipSwc && !this._swcIncompatible[cacheKey]; @@ -301,7 +312,7 @@ BCp.processOneFileForTarget = function (inputFile, source) { } compilation = compileWithSwc( source, - this.lastModifiedSwcConfig, + lastModifiedSwcConfig, { inputFilePath, features }, ); // Save result in cache @@ -905,6 +916,21 @@ function logTranspilation({ } } +function logConfigBlock(description, configObject) { + const label = color('[Config]', 36); + const descriptionColor = color(description, 90); + + console.log(`${label} ${descriptionColor}`); + + const configLines = JSON.stringify(configObject, null, 2) + .replace(/"([^"]+)":/g, '$1:') + .split('\n') + .map(line => ' ' + line); + + configLines.forEach(line => console.log(line)); + console.log(); +} + function deepMerge(target, source, preservePaths, inPath = '') { for (const key in source) { const fullPath = inPath ? `${inPath}.${key}` : key; From 51dbad059b9fe06dc5f22b2a122c9a1b7c0ffbe6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 8 Apr 2025 14:56:36 +0200 Subject: [PATCH 40/63] support SWC for legacy browsers --- packages/babel-compiler/babel-compiler.js | 53 ++++++++++++++++++++--- 1 file changed, 48 insertions(+), 5 deletions(-) diff --git a/packages/babel-compiler/babel-compiler.js b/packages/babel-compiler/babel-compiler.js index 0d88ec35dd..3694b56c26 100644 --- a/packages/babel-compiler/babel-compiler.js +++ b/packages/babel-compiler/babel-compiler.js @@ -35,16 +35,17 @@ function compileWithBabel(source, babelOptions, cacheOptions) { }); } -function compileWithSwc(source, swcOptions, { inputFilePath, features }) { +function compileWithSwc(source, swcOptions, { inputFilePath, features, arch }) { return profile('SWC.compile', function () { // Determine file extension based syntax. const isTypescriptSyntax = inputFilePath.endsWith('.ts') || inputFilePath.endsWith('.tsx'); const hasTSXSupport = inputFilePath.endsWith('.tsx'); const hasJSXSupport = inputFilePath.endsWith('.jsx'); + const isLegacyWebArch = arch.includes('legacy'); const baseSwcConfig = { jsc: { - target: 'es2015', + ...!isLegacyWebArch && { target: 'es2015' }, parser: { syntax: isTypescriptSyntax ? 'typescript' : 'ecmascript', jsx: hasJSXSupport, @@ -54,10 +55,14 @@ function compileWithSwc(source, swcOptions, { inputFilePath, features }) { module: { type: 'es6' }, minify: false, sourceMaps: true, + ...isLegacyWebArch && { + env: { targets: lastModifiedSwcLegacyConfig || {} }, + }, }; const nextSwcConfig = Object.keys(swcOptions)?.length > 0 ? deepMerge(baseSwcConfig, swcOptions, [ + 'env.targets', 'module.type', 'jsc.parser.syntax', 'jsc.parser.jsx', @@ -136,6 +141,16 @@ BCp.initializeMeteorAppSwcrc = function () { return lastModifiedSwcConfig; }; +let lastModifiedSwcLegacyConfig; +BCp.initializeMeteorAppLegacyConfig = function () { + const swcLegacyConfig = convertBabelTargetsForSwc(Babel.getMinimumModernBrowserVersions()); + if (lastModifiedMeteorConfig?.modernTranspiler?.verbose && !lastModifiedSwcLegacyConfig) { + logConfigBlock('SWC Legacy Config', swcLegacyConfig); + } + lastModifiedSwcLegacyConfig = swcLegacyConfig; + return lastModifiedSwcConfig; +}; + BCp.processFilesForTarget = function (inputFiles) { var compiler = this; @@ -144,6 +159,7 @@ BCp.processFilesForTarget = function (inputFiles) { this.initializeMeteorAppConfig(); this.initializeMeteorAppSwcrc(); + this.initializeMeteorAppLegacyConfig(); inputFiles.forEach(function (inputFile) { if (inputFile.supportsLazyCompilation) { @@ -260,6 +276,8 @@ BCp.processOneFileForTarget = function (inputFile, source) { const isNodeModulesCode = packageName == null && inputFilePath.includes("node_modules/"); const isAppCode = packageName == null && !isNodeModulesCode; const isPackageCode = packageName != null; + const isLegacyWebArch = arch.includes('legacy'); + const config = lastModifiedMeteorConfig?.modernTranspiler; const hasModernTranspiler = config != null; const shouldSkipSwc = @@ -267,6 +285,7 @@ BCp.processOneFileForTarget = function (inputFile, source) { (isAppCode && config.excludeApp === true) || (isNodeModulesCode && config.excludeNodeModules === true) || (isPackageCode && config.excludePackages === true) || + (isLegacyWebArch && config.excludeLegacy === true) || (isAppCode && Array.isArray(config.excludeApp) && isExcludedConfig(inputFilePath, config.excludeApp || [])) || @@ -286,7 +305,9 @@ BCp.processOneFileForTarget = function (inputFile, source) { config.excludePackages || [], ))); - const cacheKey = `${toBeAdded.hash}${lastModifiedSwcConfigTime ||''}`; + const cacheKey = `${toBeAdded.hash}${lastModifiedSwcConfigTime || ''}${ + isLegacyWebArch ? 'legacy' : '' + }`; // Determine if SWC should be used based on package and file criteria. const shouldUseSwc = !shouldSkipSwc && !this._swcIncompatible[cacheKey]; @@ -306,6 +327,7 @@ BCp.processOneFileForTarget = function (inputFile, source) { packageName, isNodeModulesCode, cacheHit: true, + arch, }); } return compilation; @@ -313,7 +335,7 @@ BCp.processOneFileForTarget = function (inputFile, source) { compilation = compileWithSwc( source, lastModifiedSwcConfig, - { inputFilePath, features }, + { inputFilePath, features, arch }, ); // Save result in cache this.writeToSwcCache({ cacheKey, compilation }); @@ -330,6 +352,7 @@ BCp.processOneFileForTarget = function (inputFile, source) { packageName, isNodeModulesCode, cacheHit: false, + arch, }); } } catch (e) { @@ -343,6 +366,7 @@ BCp.processOneFileForTarget = function (inputFile, source) { packageName, isNodeModulesCode, cacheHit: false, + arch, errorMessage: e?.message, ...(e?.message?.includes( 'cannot be used outside of module code', @@ -870,6 +894,7 @@ function logTranspilation({ usedSwc, cacheHit, isNodeModulesCode, + arch, errorMessage = '', tip = '', }) { @@ -902,8 +927,9 @@ function logTranspilation({ ? color('🟢 Cache hit', 32) : color('🔴 Cache miss', 31) : ''; + const archPart = arch ? color(` (${arch})`, 90) : ''; console.log( - `${transpilerPart} ${filePathPadded}${originPaddedColored}${cacheStatus}`, + `${transpilerPart} ${filePathPadded}${originPaddedColored}${cacheStatus}${archPart}`, ); if (errorMessage) { console.log(); @@ -955,3 +981,20 @@ function deepMerge(target, source, preservePaths, inPath = '') { } return target; } + +function convertBabelTargetsForSwc(babelTargets) { + const allowedEnvs = new Set([ + 'chrome', 'opera', 'edge', 'firefox', 'safari', + 'ie', 'ios', 'android', 'node', 'electron' + ]); + + const filteredTargets = {}; + for (const [env, version] of Object.entries(babelTargets)) { + if (allowedEnvs.has(env)) { + // Convert an array version (e.g., [10, 3]) into "10.3", otherwise convert to string. + filteredTargets[env] = Array.isArray(version) ? version.join('.') : version.toString(); + } + } + + return filteredTargets; +} From 40a3cedce9a66d37a2cb3f530b41c5adae6534fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 8 Apr 2025 16:01:45 +0200 Subject: [PATCH 41/63] clean --- packages/babel-compiler/babel-compiler.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/babel-compiler/babel-compiler.js b/packages/babel-compiler/babel-compiler.js index 3694b56c26..03176da2dd 100644 --- a/packages/babel-compiler/babel-compiler.js +++ b/packages/babel-compiler/babel-compiler.js @@ -305,9 +305,13 @@ BCp.processOneFileForTarget = function (inputFile, source) { config.excludePackages || [], ))); - const cacheKey = `${toBeAdded.hash}${lastModifiedSwcConfigTime || ''}${ - isLegacyWebArch ? 'legacy' : '' - }`; + const cacheKey = [ + toBeAdded.hash, + lastModifiedSwcConfigTime, + isLegacyWebArch ? 'legacy' : '', + ] + .filter(Boolean) + .join('-'); // Determine if SWC should be used based on package and file criteria. const shouldUseSwc = !shouldSkipSwc && !this._swcIncompatible[cacheKey]; From 9e9c1be76fd7505a7f4a111f80adce8dbe8889c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 8 Apr 2025 16:21:40 +0200 Subject: [PATCH 42/63] use a meteor-specific version for SWC --- packages/babel-compiler/babel-compiler.js | 2 +- packages/babel-compiler/package.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/babel-compiler/babel-compiler.js b/packages/babel-compiler/babel-compiler.js index 03176da2dd..18bd05f828 100644 --- a/packages/babel-compiler/babel-compiler.js +++ b/packages/babel-compiler/babel-compiler.js @@ -1,6 +1,6 @@ var semver = Npm.require("semver"); var JSON5 = Npm.require("json5"); -var SWC = Npm.require("@swc/core"); +var SWC = Npm.require("@meteorjs/swc-core"); const reifyCompile = Npm.require("@meteorjs/reify/lib/compiler").compile; const reifyAcornParse = Npm.require("@meteorjs/reify/lib/parsers/acorn").parse; var fs = Npm.require('fs'); diff --git a/packages/babel-compiler/package.js b/packages/babel-compiler/package.js index 812b3e96af..0e9c3c420d 100644 --- a/packages/babel-compiler/package.js +++ b/packages/babel-compiler/package.js @@ -8,7 +8,7 @@ Npm.depends({ '@meteorjs/babel': '7.20.1', 'json5': '2.2.3', 'semver': '7.6.3', - "@swc/core": "1.11.11", + "@meteorjs/swc-core": "1.1.2", }); Package.onUse(function (api) { From 252303441446260f4971ca0836f24064e7bf7b22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 8 Apr 2025 16:31:32 +0200 Subject: [PATCH 43/63] fix syntax --- packages/babel-compiler/babel-compiler.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/babel-compiler/babel-compiler.js b/packages/babel-compiler/babel-compiler.js index 18bd05f828..73a39d28b5 100644 --- a/packages/babel-compiler/babel-compiler.js +++ b/packages/babel-compiler/babel-compiler.js @@ -45,7 +45,7 @@ function compileWithSwc(source, swcOptions, { inputFilePath, features, arch }) { const isLegacyWebArch = arch.includes('legacy'); const baseSwcConfig = { jsc: { - ...!isLegacyWebArch && { target: 'es2015' }, + ...(!isLegacyWebArch && { target: 'es2015' }), parser: { syntax: isTypescriptSyntax ? 'typescript' : 'ecmascript', jsx: hasJSXSupport, @@ -55,9 +55,9 @@ function compileWithSwc(source, swcOptions, { inputFilePath, features, arch }) { module: { type: 'es6' }, minify: false, sourceMaps: true, - ...isLegacyWebArch && { + ...(isLegacyWebArch && { env: { targets: lastModifiedSwcLegacyConfig || {} }, - }, + }), }; const nextSwcConfig = Object.keys(swcOptions)?.length > 0 From c69e757bb7686f7b1b8df5ceaf6d37f9deaff4c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 8 Apr 2025 16:32:57 +0200 Subject: [PATCH 44/63] remove un-required @swc/core from the meteor bundler --- scripts/dev-bundle-tool-package.js | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/dev-bundle-tool-package.js b/scripts/dev-bundle-tool-package.js index 60cc8d69fa..2b92b9e5f6 100644 --- a/scripts/dev-bundle-tool-package.js +++ b/scripts/dev-bundle-tool-package.js @@ -68,7 +68,6 @@ var packageJson = { 'xmlbuilder2': '1.8.1', "ws": "7.4.5", "open":"8.4.2", - "@swc/core": "1.11.11", "acorn": "8.14.1", } }; From 9d9de24b322cca2845f41219062f889a2c27b354 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 8 Apr 2025 16:39:07 +0200 Subject: [PATCH 45/63] ignore server babel-compiler file from the check legacy syntax as node 22 is modern enough --- packages/babel-compiler/babel-compiler.js | 8 ++++---- scripts/admin/check-legacy-syntax/check-syntax.js | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/babel-compiler/babel-compiler.js b/packages/babel-compiler/babel-compiler.js index 73a39d28b5..c7e4354207 100644 --- a/packages/babel-compiler/babel-compiler.js +++ b/packages/babel-compiler/babel-compiler.js @@ -84,12 +84,12 @@ function compileWithSwc(source, swcOptions, { inputFilePath, features, arch }) { avoidModernSyntax: true, enforceStrictMode: false, dynamicImport: true, - ...features.topLevelAwait && { topLevelAwait: true }, - ...features.compileForShell && { moduleAlias: 'module' }, - ...(features.modernBrowsers || features.nodeMajorVersion >= 8) && { + ...(features.topLevelAwait && { topLevelAwait: true }), + ...(features.compileForShell && { moduleAlias: 'module' }), + ...((features.modernBrowsers || features.nodeMajorVersion >= 8) && { avoidModernSyntax: false, generateLetDeclarations: true, - }, + }), }); if (!result.identical) { content = result.code; diff --git a/scripts/admin/check-legacy-syntax/check-syntax.js b/scripts/admin/check-legacy-syntax/check-syntax.js index d5a1db490f..58b1ce1284 100644 --- a/scripts/admin/check-legacy-syntax/check-syntax.js +++ b/scripts/admin/check-legacy-syntax/check-syntax.js @@ -42,6 +42,7 @@ const packages = { autopublish: {}, "babel-compiler": { serverFiles: ["babel.js", "babel-compiler.js"], + ignoredFiles: ["babel-compiler.js"], }, "babel-runtime": {}, "browser-policy": {}, From 7bae9b5c91bca0b36b9de69a5edc94a07521508a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 8 Apr 2025 17:05:47 +0200 Subject: [PATCH 46/63] bump meteor bundle version --- meteor | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meteor b/meteor index 0c7f6125a9..70ccbf127d 100755 --- a/meteor +++ b/meteor @@ -1,6 +1,6 @@ #!/usr/bin/env bash -BUNDLE_VERSION=22.14.0.5 +BUNDLE_VERSION=22.14.0.6 # OS Check. Put here because here is where we download the precompiled From 5239dc5da9ba1894b6509f77eab3b70fd8abc7b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 8 Apr 2025 19:45:10 +0200 Subject: [PATCH 47/63] bump bundle version --- meteor | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meteor b/meteor index 70ccbf127d..07a1b72746 100755 --- a/meteor +++ b/meteor @@ -1,6 +1,6 @@ #!/usr/bin/env bash -BUNDLE_VERSION=22.14.0.6 +BUNDLE_VERSION=22.14.0.7 # OS Check. Put here because here is where we download the precompiled From f23c7dac5967a60652d64b929c85dd690c759371 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 8 Apr 2025 19:50:23 +0200 Subject: [PATCH 48/63] re-run checks From b8b59b0b7daec466762d37817d504993e74d1a34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Wed, 9 Apr 2025 10:39:38 +0200 Subject: [PATCH 49/63] re-run checks From 3a039e56f4d9c57501f4f4a4a2079605e4ec8926 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Wed, 9 Apr 2025 14:26:54 +0200 Subject: [PATCH 50/63] precheck on meteor config --- packages/babel-compiler/babel-compiler.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/babel-compiler/babel-compiler.js b/packages/babel-compiler/babel-compiler.js index c7e4354207..6f668817bd 100644 --- a/packages/babel-compiler/babel-compiler.js +++ b/packages/babel-compiler/babel-compiler.js @@ -106,6 +106,9 @@ function compileWithSwc(source, swcOptions, { inputFilePath, features, arch }) { let lastModifiedMeteorConfig; let lastModifiedMeteorConfigTime; BCp.initializeMeteorAppConfig = function () { + if (!fs.existsSync(`${getMeteorAppDir()}/package.json`)) { + return; + } const currentLastModifiedConfigTime = fs .statSync(`${getMeteorAppDir()}/package.json`) ?.mtime?.getTime(); From a5022137db1bb9cf7835e483f5ec5c548da965a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Wed, 9 Apr 2025 14:28:06 +0200 Subject: [PATCH 51/63] precheck on meteor config --- packages/babel-compiler/babel-compiler.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/babel-compiler/babel-compiler.js b/packages/babel-compiler/babel-compiler.js index 6f668817bd..6bf6c85fea 100644 --- a/packages/babel-compiler/babel-compiler.js +++ b/packages/babel-compiler/babel-compiler.js @@ -106,7 +106,7 @@ function compileWithSwc(source, swcOptions, { inputFilePath, features, arch }) { let lastModifiedMeteorConfig; let lastModifiedMeteorConfigTime; BCp.initializeMeteorAppConfig = function () { - if (!fs.existsSync(`${getMeteorAppDir()}/package.json`)) { + if (!lastModifiedMeteorConfig && !fs.existsSync(`${getMeteorAppDir()}/package.json`)) { return; } const currentLastModifiedConfigTime = fs @@ -126,7 +126,7 @@ BCp.initializeMeteorAppConfig = function () { let lastModifiedSwcConfig; let lastModifiedSwcConfigTime; BCp.initializeMeteorAppSwcrc = function () { - if (!fs.existsSync(`${getMeteorAppDir()}/.swcrc`)) { + if (!lastModifiedSwcConfig && !fs.existsSync(`${getMeteorAppDir()}/.swcrc`)) { lastModifiedSwcConfig = {}; return; } From 3a583d50503863f7ac39005ccad044429e556fcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Wed, 9 Apr 2025 15:04:22 +0200 Subject: [PATCH 52/63] fix ci --- packages/babel-compiler/babel-compiler.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/babel-compiler/babel-compiler.js b/packages/babel-compiler/babel-compiler.js index 6bf6c85fea..d323554201 100644 --- a/packages/babel-compiler/babel-compiler.js +++ b/packages/babel-compiler/babel-compiler.js @@ -35,7 +35,7 @@ function compileWithBabel(source, babelOptions, cacheOptions) { }); } -function compileWithSwc(source, swcOptions, { inputFilePath, features, arch }) { +function compileWithSwc(source, swcOptions = {}, { inputFilePath, features, arch }) { return profile('SWC.compile', function () { // Determine file extension based syntax. const isTypescriptSyntax = inputFilePath.endsWith('.ts') || inputFilePath.endsWith('.tsx'); @@ -127,7 +127,6 @@ let lastModifiedSwcConfig; let lastModifiedSwcConfigTime; BCp.initializeMeteorAppSwcrc = function () { if (!lastModifiedSwcConfig && !fs.existsSync(`${getMeteorAppDir()}/.swcrc`)) { - lastModifiedSwcConfig = {}; return; } const currentLastModifiedConfigTime = fs From badcdeea1a30cadc969e9d0c086eea7b20f34345 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Mon, 14 Apr 2025 14:55:33 +0200 Subject: [PATCH 53/63] re-run checks From c7c7b7bdcfd602354fa90e90592729c1d2bbcda6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Mon, 14 Apr 2025 15:51:59 +0200 Subject: [PATCH 54/63] re-run checks From c38491519e909a421abb83878104386ed0da9b5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Mon, 14 Apr 2025 18:30:41 +0200 Subject: [PATCH 55/63] add docs for SWC adoption --- v3-docs/docs/.vitepress/config.mts | 14 +++ v3-docs/docs/about/modern-build-stack.md | 13 ++ .../modern-build-stack/modern-bundler.md | 19 +++ .../modern-transpiler-swc.md | 113 ++++++++++++++++++ 4 files changed, 159 insertions(+) create mode 100644 v3-docs/docs/about/modern-build-stack.md create mode 100644 v3-docs/docs/about/modern-build-stack/modern-bundler.md create mode 100644 v3-docs/docs/about/modern-build-stack/modern-transpiler-swc.md diff --git a/v3-docs/docs/.vitepress/config.mts b/v3-docs/docs/.vitepress/config.mts index 83add47b6a..5e37b0ab0a 100644 --- a/v3-docs/docs/.vitepress/config.mts +++ b/v3-docs/docs/.vitepress/config.mts @@ -168,6 +168,20 @@ export default defineConfig({ text: "Cordova", link: "/about/cordova", }, + { + text: "Modern Build Stack", + link: "/about/modern-build-stack.md", + items: [ + { + text: "Modern Transpiler: SWC", + link: "/about/modern-build-stack/modern-transpiler-swc.md", + }, + { + text: "Modern Bundler", + link: "/about/modern-build-stack/modern-bundler.md", + }, + ] + }, ], collapsed: true, }, diff --git a/v3-docs/docs/about/modern-build-stack.md b/v3-docs/docs/about/modern-build-stack.md new file mode 100644 index 0000000000..6facbd687d --- /dev/null +++ b/v3-docs/docs/about/modern-build-stack.md @@ -0,0 +1,13 @@ +# Modern Build Stack + +The Meteor bundler is made up of several key components that enhance your experience both during development and when deploying to production. These include: + +- **Transpiler**: Responsible for converting each file into a syntax compatible across different browsers and runtime environments. +- **Bundler**: Handles discovering your app’s files and dependencies, including Meteor packages and core modules, then links them into production-ready bundles. It also applies optimizations to produce lighter builds and faster processes. +- **Dev Server**: During development, it watches for file changes, and supports fast feedback via HMR, bundle visualizers, debug tools, and more. At runtime, it provides a full-featured server environment with support for SSR and modern APIs powered by Express. + +To improve the development and deployment experience for all Meteor projects, we’re revamping each of these components with a focus on better performance, smarter tooling, and leaner bundle sizes: + +- **Modern Transpiler**: Meteor is adopting **SWC** as a faster alternative to Babel. +- **Modern Bundler**: A new bundler will handle only your app’s code, supporting tree-shaking, popular plugins, and better features for both development and production. Meanwhile, Meteor’s core bundler will continue handling Meteor-specific tasks, such as compiling Atmosphere packages, with optimized workflows. +- **Dev Server Enhancements**: The dev server remains a core part of Meteor, now with ongoing improvements in performance and developer features. diff --git a/v3-docs/docs/about/modern-build-stack/modern-bundler.md b/v3-docs/docs/about/modern-build-stack/modern-bundler.md new file mode 100644 index 0000000000..1c72489185 --- /dev/null +++ b/v3-docs/docs/about/modern-build-stack/modern-bundler.md @@ -0,0 +1,19 @@ +# Modern bundler + +Meteor handles watching and linking all project files into the final bundle. While we'd like to offload more of this to modern bundlers, we're still focused on keeping what's left in the Meteor context as fast as possible. + +Integration with a modern bundler is in progress for Meteor 3.4. Meanwhile, we've optimized existing processes for better performance. + +## Modern builds + +Starting with Meteor 3.3, new apps skip `web.browser.legacy` and `web.cordova` by default in development mode (unless developing for native). + +For existing apps, enable this by adding to `package.json`: + +```json +"meteor": { + "modernWebArchsOnly": true +} +``` + +This works like using `--exclude-archs web.browser.legacy,web.cordova` with `meteor run`. diff --git a/v3-docs/docs/about/modern-build-stack/modern-transpiler-swc.md b/v3-docs/docs/about/modern-build-stack/modern-transpiler-swc.md new file mode 100644 index 0000000000..006a5c15b3 --- /dev/null +++ b/v3-docs/docs/about/modern-build-stack/modern-transpiler-swc.md @@ -0,0 +1,113 @@ +# Modern transpiler: SWC + +Meteor has long used Babel, a mature and still widely adopted transpiler. However, it lags behind newer tools like SWC in terms of speed. SWC and others are not only faster but are growing in use and features, often surpassing Babel. + +Since transpilation is one of the slowest steps in development, Meteor now gives you the option to use SWC for your apps. + +## Enable SWC + +Add this to your app's `package.json`: + +```json +"meteor": { + "modernTranspiler": true +} +``` + +When starting your app for web or native, SWC will handle all files: your app, npm packages, and Atmosphere packages. This also applies to production builds. + +## Verbose transpilation process + +To analyze and improve transpilation, you can enable verbose output. Add this to `package.json`: + +```json +"meteor": { + "modernTranspiler": { + "verbose": true + } +} +``` + +This shows each file being processed, its context, cache usage, and whether it fell back to Babel due to incompatibilities. + +## Adapt your code to benefit from SWC + +If all your code uses SWC, you're good and can turn off verbosity. But if you see logs like: + +``` shell +[Transpiler] Used Babel for () Fallback + +``` + +There are a few things you can do. + +First, check the fallback details. It may show why SWC couldn’t handle the file. A common reason is nested imports, `import` statements inside a function. Moving them to the top level may fix it. These nested imports work via a Babel plugin specific to Meteor, which SWC doesn’t support. + +Other reasons might involve features tied to Babel plugins. If so, you’ll need to find a similar plugin for SWC. See the [SWC plugin list](https://plugins.swc.rs/versions/range/271). + +Second, you might choose to ignore the fallback if those files are fine with Babel. Even with SWC enabled, Meteor will continue using Babel for those files on future rebuilds. + +Third, you can exclude files or contexts from SWC. For example, if you're using `babel-plugin-react-compiler`, you can exclude your app code adding this to `package.json`: + +```json +"meteor": { + "modernTranspiler": { + "excludeApp": true + } +} +``` + +Or exclude only specific files like `.jsx`: + +```json +"meteor": { + "modernTranspiler": { + "excludeApp": ["\\.jsx"] + } +} +``` + +You can also use `excludePackages`, `excludeNodeModules`, and `excludeLegacy` for finer control. See the `modernTranspiler` config docs for more. + +When no alternatives exist, these settings let you still get most of SWC’s speed benefits by limiting fallback use. + +We expect most apps will benefit just by enabling `modernTranspiler: true`. Most Meteor packages should work right away, except ones using nested imports. Node modules will mostly work too, since they follow common standards. Most app code should also work unless it depends on Babel-specific behavior. + +> Remember to turn off verbosity when you're done with optimizations. + +## User custom .swcrc + +You can use .swcrc config in the root of your project to describe specific [SWC plugins](https://github.com/swc-project/plugins) there, that will be applied to compile the entire files of your project. + +## Config API + +- `modernTranspiler: [true|false]` + Enables or disables the use of the modern transpiler (SWC). If disabled, Babel will be used directly instead. + +- `modernTranspiler.excludeApp: [true|false] or [string[]]` + If true, the app’s own code (outside of Meteor core and packages) will continue using Babel. + Otherwise, a list of file paths or regex-like patterns within the app to exclude from SWC transpilation. + +- `modernTranspiler.excludeNodeModules: [true|false] or [string[]]` + If true, the app’s node_modules will continue using Babel. + Otherwise, a list of NPM packages names, file paths or regex-like patterns within the node_modules folder to exclude from SWC transpilation. + +- `modernTranspiler.excludePackages: [true|false] or [string[]]` + If true, the Meteor’s packages will continue using Babel. + Otherwise, a list of package names, file paths or regex-like patterns within the package to exclude from SWC transpilation. + +- `modernTranspiler.excludeLegacy: [true|false]` + If true, the Meteor’s bundle for legacy browsers will continue using Babel. + +- `modernTranspiler.verbose: [true|false]` + If true, the transpilation process for files is shown when running the app. This helps understand which transpiler is used for each file, what fallbacks are applied, and gives a chance to either exclude files to always use Babel or migrate fully to SWC. + +## Troubleshotting + +If you run into issues, try `meteor reset` or delete the `.meteor` folder. + +For help or to report issues, post on [GitHub](https://github.com/meteor/meteor/issues) or the [Meteor forums](https://forums.meteor.com). We’re focused on making Meteor faster and your feedback helps. + +You can compare performance before and after enabling `modernTranspiler` by running [`meteor profile`](../../cli/index.md#meteorprofile). Share your results to show progress to others. + +> **[Check out modern bundler options](./modern-bundler.md) to improve performance and access newer build features.** From a5fa1f8208f41cff5dfaca592ec28cfbe3dd28ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Mon, 14 Apr 2025 18:41:59 +0200 Subject: [PATCH 56/63] fix docs --- v3-docs/docs/about/modern-build-stack/modern-transpiler-swc.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v3-docs/docs/about/modern-build-stack/modern-transpiler-swc.md b/v3-docs/docs/about/modern-build-stack/modern-transpiler-swc.md index 006a5c15b3..64aff0fe24 100644 --- a/v3-docs/docs/about/modern-build-stack/modern-transpiler-swc.md +++ b/v3-docs/docs/about/modern-build-stack/modern-transpiler-swc.md @@ -104,7 +104,7 @@ You can use .swcrc config in the root of your project to describe specific [SWC ## Troubleshotting -If you run into issues, try `meteor reset` or delete the `.meteor` folder. +If you run into issues, try `meteor reset` or delete the `.meteor/local` folder in the project root. For help or to report issues, post on [GitHub](https://github.com/meteor/meteor/issues) or the [Meteor forums](https://forums.meteor.com). We’re focused on making Meteor faster and your feedback helps. From cc30ec881e2d38c72f024ac156a2e611bd7ca1e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Mon, 14 Apr 2025 19:02:40 +0200 Subject: [PATCH 57/63] fix docs --- .../docs/about/modern-build-stack/modern-transpiler-swc.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/v3-docs/docs/about/modern-build-stack/modern-transpiler-swc.md b/v3-docs/docs/about/modern-build-stack/modern-transpiler-swc.md index 64aff0fe24..66f7964a9d 100644 --- a/v3-docs/docs/about/modern-build-stack/modern-transpiler-swc.md +++ b/v3-docs/docs/about/modern-build-stack/modern-transpiler-swc.md @@ -67,7 +67,7 @@ Or exclude only specific files like `.jsx`: } ``` -You can also use `excludePackages`, `excludeNodeModules`, and `excludeLegacy` for finer control. See the `modernTranspiler` config docs for more. +You can also use `excludePackages`, `excludeNodeModules`, and `excludeLegacy` for finer control. See the [`modernTranspiler` config docs](#config-api) for more. When no alternatives exist, these settings let you still get most of SWC’s speed benefits by limiting fallback use. @@ -75,7 +75,7 @@ We expect most apps will benefit just by enabling `modernTranspiler: true`. Most > Remember to turn off verbosity when you're done with optimizations. -## User custom .swcrc +## Custom .swcrc You can use .swcrc config in the root of your project to describe specific [SWC plugins](https://github.com/swc-project/plugins) there, that will be applied to compile the entire files of your project. From c26bc46b1591647caf508b9c3db009919304a2ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Mon, 14 Apr 2025 19:05:24 +0200 Subject: [PATCH 58/63] fix docs --- v3-docs/docs/about/modern-build-stack/modern-transpiler-swc.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v3-docs/docs/about/modern-build-stack/modern-transpiler-swc.md b/v3-docs/docs/about/modern-build-stack/modern-transpiler-swc.md index 66f7964a9d..3a8930089a 100644 --- a/v3-docs/docs/about/modern-build-stack/modern-transpiler-swc.md +++ b/v3-docs/docs/about/modern-build-stack/modern-transpiler-swc.md @@ -1,6 +1,6 @@ # Modern transpiler: SWC -Meteor has long used Babel, a mature and still widely adopted transpiler. However, it lags behind newer tools like SWC in terms of speed. SWC and others are not only faster but are growing in use and features, often surpassing Babel. +Meteor has long used Babel, a mature and still widely adopted transpiler. However, it lags behind newer tools like SWC in terms of speed. SWC and others are not only faster but are growing in use and features, reaching parity with Babel. Since transpilation is one of the slowest steps in development, Meteor now gives you the option to use SWC for your apps. From fdc5a5b64b8f9f2c37d83bc36505468bad0b74a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Mon, 14 Apr 2025 21:04:49 +0200 Subject: [PATCH 59/63] fix docs --- v3-docs/docs/about/modern-build-stack.md | 13 +++++++++++++ .../docs/about/modern-build-stack/modern-bundler.md | 2 +- .../modern-build-stack/modern-transpiler-swc.md | 4 ++-- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/v3-docs/docs/about/modern-build-stack.md b/v3-docs/docs/about/modern-build-stack.md index 6facbd687d..433782a81d 100644 --- a/v3-docs/docs/about/modern-build-stack.md +++ b/v3-docs/docs/about/modern-build-stack.md @@ -11,3 +11,16 @@ To improve the development and deployment experience for all Meteor projects, we - **Modern Transpiler**: Meteor is adopting **SWC** as a faster alternative to Babel. - **Modern Bundler**: A new bundler will handle only your app’s code, supporting tree-shaking, popular plugins, and better features for both development and production. Meanwhile, Meteor’s core bundler will continue handling Meteor-specific tasks, such as compiling Atmosphere packages, with optimized workflows. - **Dev Server Enhancements**: The dev server remains a core part of Meteor, now with ongoing improvements in performance and developer features. + +## Quick start + +To start using the new build stack, add the following to your `package.json`: + +```json +"meteor": { + "modernWebArchsOnly": true, + "modernTranspiler": true +} +``` + +Learn more about these settings in the [Modern Transpiler](modern-build-stack/modern-transpiler.md) and [Modern Bundler](modern-build-stack/modern-bundler.md) guides. diff --git a/v3-docs/docs/about/modern-build-stack/modern-bundler.md b/v3-docs/docs/about/modern-build-stack/modern-bundler.md index 1c72489185..d5ebb5b55e 100644 --- a/v3-docs/docs/about/modern-build-stack/modern-bundler.md +++ b/v3-docs/docs/about/modern-build-stack/modern-bundler.md @@ -1,4 +1,4 @@ -# Modern bundler +# Modern Bundler Meteor handles watching and linking all project files into the final bundle. While we'd like to offload more of this to modern bundlers, we're still focused on keeping what's left in the Meteor context as fast as possible. diff --git a/v3-docs/docs/about/modern-build-stack/modern-transpiler-swc.md b/v3-docs/docs/about/modern-build-stack/modern-transpiler-swc.md index 3a8930089a..278da99499 100644 --- a/v3-docs/docs/about/modern-build-stack/modern-transpiler-swc.md +++ b/v3-docs/docs/about/modern-build-stack/modern-transpiler-swc.md @@ -1,4 +1,4 @@ -# Modern transpiler: SWC +# Modern Transpiler: SWC Meteor has long used Babel, a mature and still widely adopted transpiler. However, it lags behind newer tools like SWC in terms of speed. SWC and others are not only faster but are growing in use and features, reaching parity with Babel. @@ -47,7 +47,7 @@ Other reasons might involve features tied to Babel plugins. If so, you’ll need Second, you might choose to ignore the fallback if those files are fine with Babel. Even with SWC enabled, Meteor will continue using Babel for those files on future rebuilds. -Third, you can exclude files or contexts from SWC. For example, if you're using `babel-plugin-react-compiler`, you can exclude your app code adding this to `package.json`: +Third, you can exclude files or contexts from SWC. For example, if you're using `babel-plugin-react-compiler`, which SWC doesn't support yet, you can exclude your app code adding this to `package.json`: ```json "meteor": { From 39b8f65c3b6acb72c9de4c23e564e77ccc5bd89a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 15 Apr 2025 08:25:21 +0200 Subject: [PATCH 60/63] fix docs --- v3-docs/docs/about/modern-build-stack.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v3-docs/docs/about/modern-build-stack.md b/v3-docs/docs/about/modern-build-stack.md index 433782a81d..f76892371d 100644 --- a/v3-docs/docs/about/modern-build-stack.md +++ b/v3-docs/docs/about/modern-build-stack.md @@ -23,4 +23,4 @@ To start using the new build stack, add the following to your `package.json`: } ``` -Learn more about these settings in the [Modern Transpiler](modern-build-stack/modern-transpiler.md) and [Modern Bundler](modern-build-stack/modern-bundler.md) guides. +Learn more about these settings in the [Modern Transpiler](modern-build-stack/modern-transpiler-swc.md) and [Modern Bundler](modern-build-stack/modern-bundler.md) guides. From 041f194a84d6501ce8147c9eaa562698cc3ad3bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 15 Apr 2025 10:00:23 +0200 Subject: [PATCH 61/63] re-run checks From 3de4c410072ee1fbd22d588e79da80e8e96abea2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 15 Apr 2025 11:57:06 +0200 Subject: [PATCH 62/63] re-run checks From 9a229cd665e9e621a75eefedb428d8480b74c00a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Codo=C3=B1er?= Date: Tue, 15 Apr 2025 14:35:43 +0200 Subject: [PATCH 63/63] ensure new meteor apps have modernTranspiler enabled --- tools/static-assets/skel-apollo/package.json | 3 ++- tools/static-assets/skel-blaze/package.json | 3 ++- tools/static-assets/skel-chakra-ui/package.json | 3 ++- tools/static-assets/skel-minimal/package.json | 3 ++- tools/static-assets/skel-react/package.json | 3 ++- tools/static-assets/skel-solid/package.json | 3 ++- tools/static-assets/skel-svelte/package.json | 3 ++- tools/static-assets/skel-tailwind/package.json | 3 ++- tools/static-assets/skel-typescript/package.json | 3 ++- v3-docs/docs/about/modern-build-stack.md | 2 +- 10 files changed, 19 insertions(+), 10 deletions(-) diff --git a/tools/static-assets/skel-apollo/package.json b/tools/static-assets/skel-apollo/package.json index 3bb70f1781..5ecaafb90b 100644 --- a/tools/static-assets/skel-apollo/package.json +++ b/tools/static-assets/skel-apollo/package.json @@ -22,6 +22,7 @@ "server": "server/main.js" }, "testModule": "tests/main.js", - "modernWebArchsOnly": true + "modernWebArchsOnly": true, + "modernTranspiler": true } } diff --git a/tools/static-assets/skel-blaze/package.json b/tools/static-assets/skel-blaze/package.json index 21fba0da58..241f4001e8 100644 --- a/tools/static-assets/skel-blaze/package.json +++ b/tools/static-assets/skel-blaze/package.json @@ -18,6 +18,7 @@ "server": "server/main.js" }, "testModule": "tests/main.js", - "modernWebArchsOnly": true + "modernWebArchsOnly": true, + "modernTranspiler": true } } diff --git a/tools/static-assets/skel-chakra-ui/package.json b/tools/static-assets/skel-chakra-ui/package.json index 92bbdf24ad..561d051ac7 100644 --- a/tools/static-assets/skel-chakra-ui/package.json +++ b/tools/static-assets/skel-chakra-ui/package.json @@ -25,6 +25,7 @@ "server": "server/main.js" }, "testModule": "tests/main.js", - "modernWebArchsOnly": true + "modernWebArchsOnly": true, + "modernTranspiler": true } } diff --git a/tools/static-assets/skel-minimal/package.json b/tools/static-assets/skel-minimal/package.json index 73469db096..f0038698c7 100644 --- a/tools/static-assets/skel-minimal/package.json +++ b/tools/static-assets/skel-minimal/package.json @@ -17,6 +17,7 @@ "server": "server/main.js" }, "testModule": "tests/main.js", - "modernWebArchsOnly": true + "modernWebArchsOnly": true, + "modernTranspiler": true } } diff --git a/tools/static-assets/skel-react/package.json b/tools/static-assets/skel-react/package.json index 08d7e054e3..59d8f0c986 100644 --- a/tools/static-assets/skel-react/package.json +++ b/tools/static-assets/skel-react/package.json @@ -19,6 +19,7 @@ "server": "server/main.js" }, "testModule": "tests/main.js", - "modernWebArchsOnly": true + "modernWebArchsOnly": true, + "modernTranspiler": true } } diff --git a/tools/static-assets/skel-solid/package.json b/tools/static-assets/skel-solid/package.json index 6e8187cbeb..b24dd0ead4 100644 --- a/tools/static-assets/skel-solid/package.json +++ b/tools/static-assets/skel-solid/package.json @@ -19,7 +19,8 @@ "server": "server/entry-meteor.js" }, "testModule": "tests/main.js", - "modernWebArchsOnly": true + "modernWebArchsOnly": true, + "modernTranspiler": true }, "devDependencies": { "babel-preset-solid": "^1.8.15", diff --git a/tools/static-assets/skel-svelte/package.json b/tools/static-assets/skel-svelte/package.json index 7cf92dfce7..07c594351b 100644 --- a/tools/static-assets/skel-svelte/package.json +++ b/tools/static-assets/skel-svelte/package.json @@ -28,6 +28,7 @@ } }, "testModule": "tests/main.js", - "modernWebArchsOnly": true + "modernWebArchsOnly": true, + "modernTranspiler": true } } diff --git a/tools/static-assets/skel-tailwind/package.json b/tools/static-assets/skel-tailwind/package.json index 94934d4898..3ad26935c0 100644 --- a/tools/static-assets/skel-tailwind/package.json +++ b/tools/static-assets/skel-tailwind/package.json @@ -23,6 +23,7 @@ "server": "server/main.js" }, "testModule": "tests/main.js", - "modernWebArchsOnly": true + "modernWebArchsOnly": true, + "modernTranspiler": true } } diff --git a/tools/static-assets/skel-typescript/package.json b/tools/static-assets/skel-typescript/package.json index 03ff9ce6c7..a5a9a1e979 100644 --- a/tools/static-assets/skel-typescript/package.json +++ b/tools/static-assets/skel-typescript/package.json @@ -26,6 +26,7 @@ "server": "server/main.ts" }, "testModule": "tests/main.ts", - "modernWebArchsOnly": true + "modernWebArchsOnly": true, + "modernTranspiler": true } } diff --git a/v3-docs/docs/about/modern-build-stack.md b/v3-docs/docs/about/modern-build-stack.md index f76892371d..e16e814e24 100644 --- a/v3-docs/docs/about/modern-build-stack.md +++ b/v3-docs/docs/about/modern-build-stack.md @@ -14,7 +14,7 @@ To improve the development and deployment experience for all Meteor projects, we ## Quick start -To start using the new build stack, add the following to your `package.json`: +Start using the new build stack by creating a Meteor app, or add this to your `package.json` in an existing one: ```json "meteor": {