add Coffeescript dependency check and installation logic

This commit is contained in:
Nacho Codoñer
2025-07-18 14:13:48 +02:00
parent 472472a777
commit c46681f4dc
6 changed files with 144 additions and 22 deletions

View File

@@ -71,6 +71,22 @@ function createSwcConfig({ isRun, isTypescriptEnabled, isJsxEnabled, isTsxEnable
};
}
// Coffeescript rule
function createCoffeescriptConfig({ swcConfig }) {
return {
test: /\.coffee$/,
use: [
{
loader: 'swc-loader',
options: swcConfig,
},
{
loader: 'coffee-loader',
},
],
};
}
// Watch options shared across both builds
const watchOptions = {
ignored: ['**/.meteor/local/**', '**/dist/**'],
@@ -102,8 +118,12 @@ export default function (inMeteor = {}, argv = {}) {
const mode = isProd ? 'production' : 'development';
const isTypescriptEnabled = Meteor.isTypescriptEnabled || false;
const isJsxEnabled = Meteor.isJsxEnabled || false;
const isTsxEnabled = Meteor.isTsxEnabled || false;
const isJsxEnabled =
Meteor.isJsxEnabled || (!isTypescriptEnabled && isReactEnabled) || false;
const isTsxEnabled =
Meteor.isTsxEnabled || (isTypescriptEnabled && isReactEnabled) || false;
const isCoffeescriptEnabled = Meteor.isCoffeescriptEnabled || false;
// Determine entry points
const entryPath = Meteor.entryPath;
@@ -132,7 +152,7 @@ export default function (inMeteor = {}, argv = {}) {
console.log('[i] Meteor flags:', Meteor);
}
const swcConfig = createSwcConfig({
const swcConfigRule = createSwcConfig({
isRun,
isTypescriptEnabled,
isJsxEnabled,
@@ -142,7 +162,11 @@ export default function (inMeteor = {}, argv = {}) {
/^meteor.*/,
...(isReactEnabled ? [/^react$/, /^react-dom$/] : [])
];
const alias = {
'/': path.resolve(process.cwd()),
};
const extensions = [
...(isCoffeescriptEnabled ? ['.coffee'] : []),
'.ts',
'.tsx',
'.js',
@@ -152,9 +176,11 @@ export default function (inMeteor = {}, argv = {}) {
'.json',
'.wasm',
];
const alias = {
'/': path.resolve(process.cwd()),
};
const extraRules = [
...(isCoffeescriptEnabled
? [createCoffeescriptConfig({ swcConfig: swcConfigRule?.options })]
: []),
];
// Base client config
let clientConfig = {
@@ -177,7 +203,7 @@ export default function (inMeteor = {}, argv = {}) {
},
module: {
rules: [
swcConfig,
swcConfigRule,
...(Meteor.isBlazeEnabled
? [
{
@@ -186,6 +212,7 @@ export default function (inMeteor = {}, argv = {}) {
},
]
: []),
...extraRules,
],
},
resolve: { extensions, alias },
@@ -256,7 +283,7 @@ export default function (inMeteor = {}, argv = {}) {
},
optimization: { usedExports: true },
module: {
rules: [swcConfig],
rules: [swcConfigRule, ...extraRules],
},
resolve: {
extensions,

View File

@@ -3,24 +3,18 @@
* @description Constants and global state keys for RSPack plugin
*/
/**
* Default RSPack version to install if not found
* @constant {string}
*/
export const DEFAULT_RSPACK_VERSION = '1.4.8';
/**
* Default Meteor RSPack version to install if not found
* @constant {string}
*/
export const DEFAULT_METEOR_RSPACK_VERSION = '0.0.4';
/**
* Default RSPack React HMRversion to install if not found
* @constant {string}
*/
export const DEFAULT_METEOR_RSPACK_REACT_HMR_VERSION = '1.4.3';
export const DEFAULT_METEOR_RSPACK_COFFEESCRIPT_VERSION = '2.7.0';
export const DEFAULT_METEOR_RSPACK_COFFEE_LOADER_VERSION = '5.0.0';
export const DEFAULT_METEOR_RSPACK_SWC_LOADER_VERSION = '0.2.6';
/**
* Global state keys used for storing and retrieving state across the application
* @constant {Object}
@@ -37,6 +31,8 @@ export const GLOBAL_STATE_KEYS = {
SERVER_PROCESS: 'rspack.serverProcess',
RSPACK_INSTALLATION_CHECKED: 'rspack.rspackInstallationChecked',
RSPACK_REACT_INSTALLATION_CHECKED: 'rspack.rspackReactInstallationChecked',
COFFEESCRIPT_CHECKED: 'rspack.coffeescriptChecked',
RSPACK_COFFEESCRIPT_INSTALLATION_CHECKED: 'rspack.rspackCoffeescriptInstallationChecked',
REACT_CHECKED: 'rspack.reactChecked',
INITIAL_ENTRYPONTS: 'meteor.initialEntrypoints',
CLIENT_FIRST_COMPILE: 'rspack.clientFirstCompile',

View File

@@ -11,7 +11,10 @@ const {
logProgress,
logSuccess,
} = require('meteor/tools-core/lib/log');
const { getMeteorAppDir } = require('meteor/tools-core/lib/meteor');
const {
getMeteorAppDir,
isMeteorCoffeescriptProject,
} = require('meteor/tools-core/lib/meteor');
const {
checkNpmDependencyExists,
installNpmDependency,
@@ -154,3 +157,86 @@ export async function ensureRSPackReactInstalled() {
// Mark as checked
setGlobalState(GLOBAL_STATE_KEYS.RSPACK_REACT_INSTALLATION_CHECKED, true);
}
/**
* Checks if Coffeescript is installed and sets global state accordingly
* Sets global state and environment variables based on Coffeescript detection
* @returns {Promise<void>} A promise that resolves when the check is complete
*/
export async function checkCoffeescriptInstalled() {
// Skip if already checked
if (getGlobalState(GLOBAL_STATE_KEYS.COFFEESCRIPT_CHECKED, false)) {
return;
}
const appDir = getMeteorAppDir();
const isCoffescriptInstalled =
checkNpmDependencyExists('coffeescript', { cwd: appDir }) ||
isMeteorCoffeescriptProject();
if (isCoffescriptInstalled) {
// Set environment variable to indicate React is enabled
process.env.METEOR_COFFEESCRIPT_ENABLED = 'true';
} else {
process.env.METEOR_COFFEESCRIPT_ENABLED = 'false';
}
// Mark as checked
setGlobalState(GLOBAL_STATE_KEYS.COFFEESCRIPT_CHECKED, true);
return isCoffescriptInstalled;
}
export async function ensureRSPackCoffeescriptInstalled() {
// Skip if already checked
if (getGlobalState(GLOBAL_STATE_KEYS.RSPACK_COFFEESCRIPT_INSTALLATION_CHECKED, false)) {
return;
}
const appDir = getMeteorAppDir();
const isRSPackCoffeescriptInstalled =
checkNpmDependencyExists('coffeescript', { cwd: appDir }) &&
checkNpmDependencyVersion('coffeescript', {
cwd: appDir,
versionRequirement: DEFAULT_METEOR_RSPACK_COFFEESCRIPT_VERSION,
semverCondition: 'gte',
}) &&
checkNpmDependencyExists('coffee-loader', { cwd: appDir }) &&
checkNpmDependencyVersion('coffee-loader', {
cwd: appDir,
versionRequirement: DEFAULT_METEOR_RSPACK_COFFEE_LOADER_VERSION,
semverCondition: 'gte',
}) &&
checkNpmDependencyExists('swc-loader', { cwd: appDir }) &&
checkNpmDependencyVersion('swc-loader', {
cwd: appDir,
versionRequirement: DEFAULT_METEOR_RSPACK_SWC_LOADER_VERSION,
semverCondition: 'gte',
});
const rspackCoffeescriptDependencies = [
`coffeescript@${DEFAULT_METEOR_RSPACK_COFFEESCRIPT_VERSION}`,
`coffee-loader@${DEFAULT_METEOR_RSPACK_COFFEE_LOADER_VERSION}`,
`swc-loader@${DEFAULT_METEOR_RSPACK_SWC_LOADER_VERSION}`,
];
if (!isRSPackCoffeescriptInstalled) {
logProgress(
`RSPack Coffeescript not found. Installing ${joinWithAnd(rspackCoffeescriptDependencies)}...`,
);
const success = await installNpmDependency(rspackCoffeescriptDependencies, {
cwd: appDir,
dev: true,
});
if (!success) {
throw new Error(
`Failed to install RSPack Coffeescript. Please install it manually with: meteor npm install -D ${joinWithAnd(rspackCoffeescriptDependencies)}`
);
}
logSuccess('RSPack Coffeescript installed successfully.');
}
// Mark as checked
setGlobalState(GLOBAL_STATE_KEYS.RSPACK_COFFEESCRIPT_INSTALLATION_CHECKED, true);
}

View File

@@ -112,6 +112,7 @@ export function getRSPackEnv({ isClient, isServer }) {
['isTypescriptEnabled', isTypescriptEnabled],
['isTsxEnabled', isTsxEnabled],
['isJsxEnabled', isJsxEnabled],
['isCoffeescriptEnabled', process.env.METEOR_COFFEESCRIPT_ENABLED],
];
return pairs.flatMap(([key, val]) => [
'--env',

View File

@@ -21,7 +21,7 @@ const {
const {
ensureRSPackInstalled,
checkReactInstalled, ensureRSPackReactInstalled,
checkReactInstalled, ensureRSPackReactInstalled, checkCoffeescriptInstalled,
} = require('./lib/dependencies');
const {
@@ -72,6 +72,10 @@ try {
await ensureRSPackReactInstalled();
}
if (checkCoffeescriptInstalled()) {
await checkCoffeescriptInstalled();
}
// Ensure the RSPack build context directory exists
ensureRSPackBuildContextExists();

View File

@@ -285,3 +285,11 @@ export function isMeteorBlazeProject() {
export function isMeteorBlazeHotProject() {
return getMeteorAppPackages().includes('blaze-hot');
}
/**
* Checks if the Meteor application is a Coffeescript project.
* @returns {boolean}
*/
export function isMeteorCoffeescriptProject() {
return getMeteorAppPackages().includes('coffeescript');
}