diff --git a/packages/babel-compiler/babel-compiler.js b/packages/babel-compiler/babel-compiler.js index a4cb28a7f7..1c49ac6711 100644 --- a/packages/babel-compiler/babel-compiler.js +++ b/packages/babel-compiler/babel-compiler.js @@ -13,9 +13,9 @@ var crypto = Npm.require('crypto'); * Plugin.registerCompiler * @param {Object} extraFeatures The same object that getDefaultOptions takes */ -BabelCompiler = function BabelCompiler(extraFeatures, modifyConfig) { +BabelCompiler = function BabelCompiler(extraFeatures, modifyBabelConfig) { this.extraFeatures = extraFeatures; - this.modifyConfig = modifyConfig; + this.modifyBabelConfig = modifyBabelConfig; this._babelrcCache = null; this._babelrcWarnings = Object.create(null); this.cacheDirectory = null; @@ -37,10 +37,42 @@ function compileWithBabel(source, babelOptions, cacheOptions) { }); } -function compileWithSwc(source, swcOptions = {}, { features }) { +function compileWithSwc(source, swcOptions = {}, { inputFilePath, filename, sourceFileName, 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: { + ...(!isLegacyWebArch && { target: 'es2015' }), + parser: { + syntax: isTypescriptSyntax ? 'typescript' : 'ecmascript', + jsx: hasJSXSupport, + tsx: hasTSXSupport, + }, + }, + module: { type: 'es6' }, + minify: false, + sourceMaps: true, + filename, + sourceFileName, + ...(isLegacyWebArch && { + env: { targets: lastModifiedSwcLegacyConfig || {} }, + }), + }; + const nextSwcConfig = + Object.keys(swcOptions)?.length > 0 + ? deepMerge(baseSwcConfig, swcOptions, [ + 'env.targets', + 'module.type', + ]) + : baseSwcConfig; + // Perform SWC transformation. - const transformed = SWC.transformSync(source, swcOptions); + const transformed = SWC.transformSync(source, nextSwcConfig); let content = transformed.code; @@ -272,10 +304,7 @@ BCp.processOneFileForTarget = function (inputFile, source) { }, }; - const filename = packageName - ? `packages/${packageName}/${inputFilePath}` - : inputFilePath; - + // Helper function to setup Babel options const setupBabelOptions = () => { this.inferTypeScriptConfig(features, inputFile, cacheOptions.cacheDeps); @@ -283,60 +312,26 @@ BCp.processOneFileForTarget = function (inputFile, source) { babelOptions.caller = { name: "meteor", arch }; babelOptions.sourceMaps = true; + const filename = packageName + ? `packages/${packageName}/${inputFilePath}` + : inputFilePath; babelOptions.filename = babelOptions.sourceFileName = filename; this.inferExtraBabelOptions(inputFile, babelOptions, cacheOptions.cacheDeps); - if (this.modifyConfig) { - this.modifyConfig(babelOptions, inputFile); + if (this.modifyBabelConfig) { + this.modifyBabelConfig(babelOptions, inputFile); } return babelOptions; }; - const setupSWCOptions = () => { - const isTypescriptSyntax = inputFilePath.endsWith('.ts') || inputFilePath.endsWith('.tsx'); - const hasTSXSupport = inputFilePath.endsWith('.tsx'); - const hasJSXSupport = inputFilePath.endsWith('.jsx'); - const isLegacyWebArch = arch.includes('legacy'); + // Define babelOptions at the outer scope so it's available for source map + var babelOptions; + const filename = packageName + ? `packages/${packageName}/${inputFilePath}` + : inputFilePath; - var swcOptions = { - jsc: { - ...(!isLegacyWebArch && { target: 'es2015' }), - parser: { - syntax: isTypescriptSyntax ? 'typescript' : 'ecmascript', - jsx: hasJSXSupport, - tsx: hasTSXSupport, - }, - }, - module: { type: 'es6' }, - minify: false, - sourceMaps: true, - filename, - sourceFileName: filename, - ...(isLegacyWebArch && { - env: { targets: lastModifiedSwcLegacyConfig || {} }, - }), - }; - - // Merge with app-level SWC config - if (lastModifiedSwcConfig) { - swcOptions = deepMerge(swcOptions, lastModifiedSwcConfig, [ - 'env.targets', - 'module.type', - ]); - } - - this.inferExtraSWCOptions(inputFile, swcOptions, cacheOptions.cacheDeps); - - if (!!this.extraFeatures?.swc && this.modifyConfig) { - this.modifyConfig(swcOptions, inputFile); - } - - return swcOptions; - }; - - var babelOptions = { filename }; try { var result = (() => { const isNodeModulesCode = packageName == null && inputFilePath.includes("node_modules/"); @@ -380,7 +375,7 @@ BCp.processOneFileForTarget = function (inputFile, source) { .join('-'); // Determine if SWC should be used based on package and file criteria. const shouldUseSwc = !shouldSkipSwc && !this._swcIncompatible[cacheKey]; - console.log("--> (babel-compiler.js-Line: 383)\n shouldUseSwc: ", shouldUseSwc); + let compilation; try { let usedSwc = false; @@ -400,18 +395,22 @@ BCp.processOneFileForTarget = function (inputFile, source) { arch, }); } + // Set babelOptions.filename for source map + babelOptions = { filename }; return compilation; } - const swcOptions = setupSWCOptions(); + const sourceFileName = filename; compilation = compileWithSwc( source, - swcOptions, - { features }, + lastModifiedSwcConfig, + { inputFilePath, features, arch, filename, sourceFileName }, ); // Save result in cache this.writeToSwcCache({ cacheKey, compilation }); usedSwc = true; + // Set babelOptions.filename for source map + babelOptions = { filename }; } else { // Set up Babel options only when compiling with Babel babelOptions = setupBabelOptions(); @@ -434,7 +433,9 @@ BCp.processOneFileForTarget = function (inputFile, source) { this._swcIncompatible[cacheKey] = true; // If SWC fails, fall back to Babel + // Set up Babel options for fallback babelOptions = setupBabelOptions(); + compilation = compileWithBabel(source, babelOptions, cacheOptions); if (config?.verbose) { logTranspilation({ @@ -565,15 +566,6 @@ BCp.inferExtraBabelOptions = function (inputFile, babelOptions, cacheDeps) { ); }; -BCp.inferExtraSWCOptions = function (inputFile, swcOptions, cacheDeps) { - if (! inputFile.require || - ! inputFile.findControlFile || - ! inputFile.readAndWatchFile) { - return false; - } - return this._inferFromSwcRc(inputFile, swcOptions, cacheDeps); -}; - BCp._inferFromBabelRc = function (inputFile, babelOptions, cacheDeps) { var babelrcPath = inputFile.findControlFile(".babelrc"); if (babelrcPath) { @@ -626,66 +618,6 @@ BCp._inferFromPackageJson = function (inputFile, babelOptions, cacheDeps) { } }; -BCp._inferFromSwcRc = function (inputFile, swcOptions, cacheDeps) { - var swcrcPath = inputFile.findControlFile(".swcrc"); - if (swcrcPath) { - if (! hasOwn.call(this._babelrcCache, swcrcPath)) { - try { - this._babelrcCache[swcrcPath] = { - controlFilePath: swcrcPath, - controlFileData: JSON.parse( - inputFile.readAndWatchFile(swcrcPath)), - deps: Object.create(null), - }; - } catch (e) { - if (e instanceof SyntaxError) { - e.message = ".swcrc is not a valid JSON file: " + e.message; - } - throw e; - } - } - - const cacheEntry = this._babelrcCache[swcrcPath]; - - if (this._inferHelperForSwc(inputFile, cacheEntry)) { - deepMerge(swcOptions, cacheEntry.controlFileData); - console.log("--> (babel-compiler.js-Line: 661)\n swcOptions: ", swcOptions); - Object.assign(cacheDeps, cacheEntry.deps); - return true; - } - } -}; - -BCp._inferHelperForSwc = function (inputFile, cacheEntry) { - if (! cacheEntry.controlFileData) { - return false; - } - - if (hasOwn.call(cacheEntry, "finalInferHelperForSwcResult")) { - // We've already run _inferHelperForSwc and populated - // cacheEntry.controlFileData, so we can return early here. - return cacheEntry.finalInferHelperForSwcResult; - } - - // First, ensure that the current file path is not excluded. - if (cacheEntry.controlFileData.exclude) { - const exclude = cacheEntry.controlFileData.exclude; - const path = inputFile.getPathInPackage(); - - if (exclude instanceof Array) { - for (let i = 0; i < exclude.length; ++i) { - if (path.match(exclude[i])) { - return cacheEntry.finalInferHelperForSwcResult = false; - } - } - } else if (path.match(exclude)) { - return cacheEntry.finalInferHelperForSwcResult = false; - } - } - - return cacheEntry.finalInferHelperForSwcResult = true; -}; - BCp._inferHelper = function (inputFile, cacheEntry) { if (! cacheEntry.controlFileData) { return false; @@ -1128,7 +1060,7 @@ function logConfigBlock(description, configObject) { console.log(); } -function deepMerge(target, source, preservePaths = [], inPath = '') { +function deepMerge(target, source, preservePaths, inPath = '') { for (const key in source) { const fullPath = inPath ? `${inPath}.${key}` : key; diff --git a/tools/tests/apps/modules-modern/packages/modules-test-plugin/.swcrc b/tools/tests/apps/modules-modern/packages/modules-test-plugin/.swcrc deleted file mode 100644 index 80dc832e86..0000000000 --- a/tools/tests/apps/modules-modern/packages/modules-test-plugin/.swcrc +++ /dev/null @@ -1,18 +0,0 @@ -{ - "jsc": { - "parser": { - "syntax": "ecmascript", - "jsx": true - }, - "transform": { - "legacyDecorator": true, - "decoratorMetadata": true - }, - "target": "es2015" - }, - "module": { - "type": "es6" - }, - "minify": false, - "sourceMaps": true -} diff --git a/tools/tests/apps/modules-modern/packages/modules-test-plugin/array.arson b/tools/tests/apps/modules-modern/packages/modules-test-plugin/array.arson deleted file mode 100644 index 2924cde2f9..0000000000 --- a/tools/tests/apps/modules-modern/packages/modules-test-plugin/array.arson +++ /dev/null @@ -1,4 +0,0 @@ -[ - [1, 0, -4], - "asdf" -] diff --git a/tools/tests/apps/modules-modern/packages/modules-test-plugin/modules-test-plugin.js b/tools/tests/apps/modules-modern/packages/modules-test-plugin/modules-test-plugin.js deleted file mode 100644 index 8781742a6c..0000000000 --- a/tools/tests/apps/modules-modern/packages/modules-test-plugin/modules-test-plugin.js +++ /dev/null @@ -1,23 +0,0 @@ -import { strictEqual } from "assert"; - -// SWC version doesn't use the Babel-specific oyez-transform.js plugin -// Instead, we'll just use a direct assertion that passes -strictEqual("ASDF", "ASDF"); - -// Test that the SWC configuration in .swcrc is correctly applied -// The legacyDecorator and decoratorMetadata options should be enabled -function testDecorator(target, key) { - target[key] = "decorated"; -} - -class TestClass { - @testDecorator - testProperty = "original"; -} - -const instance = new TestClass(); -// If the decorator is correctly applied, the property value should be "decorated" -strictEqual(instance.testProperty, "decorated"); - -export { default as one } from "./one"; -export { default as array } from "./array"; diff --git a/tools/tests/apps/modules-modern/packages/modules-test-plugin/one.arson b/tools/tests/apps/modules-modern/packages/modules-test-plugin/one.arson deleted file mode 100644 index 7405358da2..0000000000 --- a/tools/tests/apps/modules-modern/packages/modules-test-plugin/one.arson +++ /dev/null @@ -1,8 +0,0 @@ -[ - { - "string": 1, - "number": 2 - }, - "one", - 1 -] diff --git a/tools/tests/apps/modules-modern/packages/modules-test-plugin/package.js b/tools/tests/apps/modules-modern/packages/modules-test-plugin/package.js deleted file mode 100644 index 6cd69488a6..0000000000 --- a/tools/tests/apps/modules-modern/packages/modules-test-plugin/package.js +++ /dev/null @@ -1,29 +0,0 @@ -Package.describe({ - name: "modules-test-plugin", - version: "0.0.1", - summary: "", - // By default, Meteor will default to using README.md for documentation. - // To avoid submitting documentation, set this field to null. - documentation: null -}); - -Package.registerBuildPlugin({ - name: "compile-arson", - use: ["ecmascript"], - sources: ["plugin.js"], - npmDependencies: { - // Only keeping the necessary dependencies for SWC - "ts-invariant": "0.4.1" - } -}); - -Npm.depends({ - arson: "0.2.3", - "vue-template-compiler": "2.5.16" -}); - -Package.onUse(function(api) { - api.use("ecmascript"); - api.use("isobuild:compiler-plugin@1.0.0") - api.mainModule("modules-test-plugin.js"); -}); diff --git a/tools/tests/apps/modules-modern/packages/modules-test-plugin/plugin.js b/tools/tests/apps/modules-modern/packages/modules-test-plugin/plugin.js deleted file mode 100644 index 21d35b3065..0000000000 --- a/tools/tests/apps/modules-modern/packages/modules-test-plugin/plugin.js +++ /dev/null @@ -1,57 +0,0 @@ -import { invariant } from "ts-invariant"; - -invariant( - typeof process.versions.node === "string", - "Meteor plugins should only run in Node.js", -); - -invariant( - require.resolve("ts-invariant"), - "/node_modules/meteor/meteor-test-plugin/node_modules/ts-invariant/lib/invariant.js", -); - -Plugin.registerCompiler({ - extensions: ["arson"] -}, () => new ArsonCompiler); - -class ArsonCompiler { - // Simple property for the compiler name - expectedName = "compile-arson"; - - processFilesForTarget(inputFiles) { - invariant(this.expectedName === "compile-arson", this.expectedName); - invariant(inputFiles.length > 0); - - let vueCheckCount = 0; - - inputFiles.forEach(file => { - const arson = file.require("arson"); - let encoded = file.getContentsAsString(); - const decoded = arson.decode(encoded); - decoded.self = decoded; - encoded = arson.encode(decoded); - - file.addJavaScript({ - path: file.getPathInPackage() + ".js", - data: [ - 'module.exportDefault(require("arson").decode(', - " " + JSON.stringify(encoded), - "));", - "" - ].join("\n"), - hash: file.getSourceHash() - }); - - if (file.getPackageName() === "modules-test-plugin") { - const vueCompilerId = file.resolve("vue-template-compiler"); - // Make sure resolution does not use the "browser" field of - // vue-template-compiler/package.json. - const base = vueCompilerId.split("/").pop(); - invariant(base === "index.js", base); - ++vueCheckCount; - } - }); - - invariant(vueCheckCount > 0); - } -}