diff --git a/packages/rspack/lib/constants.js b/packages/rspack/lib/constants.js index 998fddf5e0..5349ed18e6 100644 --- a/packages/rspack/lib/constants.js +++ b/packages/rspack/lib/constants.js @@ -36,6 +36,7 @@ export const GLOBAL_STATE_KEYS = { RSPACK_REACT_INSTALLATION_CHECKED: 'rspack.rspackReactInstallationChecked', RSPACK_DOCTOR_INSTALLATION_CHECKED: 'rspack.rspackDoctorInstallationChecked', REACT_CHECKED: 'rspack.reactChecked', + TYPESCRIPT_CHECKED: 'rspack.typescriptChecked', INITIAL_ENTRYPONTS: 'meteor.initialEntrypoints', CLIENT_FIRST_COMPILE: 'rspack.clientFirstCompile', SERVER_FIRST_COMPILE: 'rspack.serverFirstCompile', diff --git a/packages/rspack/lib/dependencies.js b/packages/rspack/lib/dependencies.js index feb67ef3a3..df9bdebf11 100644 --- a/packages/rspack/lib/dependencies.js +++ b/packages/rspack/lib/dependencies.js @@ -211,3 +211,31 @@ export async function ensureRspackDoctorInstalled() { 'Rspack Doctor' ); } + +/** + * Checks if TypeScript is installed and sets global state accordingly + * Sets global state and environment variables based on TypeScript detection + * @returns {boolean} Whether TypeScript is installed + */ +export function checkTypescriptInstalled() { + // Skip if already checked + if (getGlobalState(GLOBAL_STATE_KEYS.TYPESCRIPT_CHECKED, false)) { + return; + } + + const appDir = getMeteorAppDir(); + // Check if TypeScript is a dependency in the project + const isTypescriptInstalled = checkNpmDependencyExists('typescript', { cwd: appDir }); + + if (isTypescriptInstalled) { + // Set environment variable to indicate TypeScript is enabled + process.env.METEOR_TYPESCRIPT_ENABLED = 'true'; + } else { + process.env.METEOR_TYPESCRIPT_ENABLED = 'false'; + } + + // Mark as checked + setGlobalState(GLOBAL_STATE_KEYS.TYPESCRIPT_CHECKED, true); + + return isTypescriptInstalled; +} diff --git a/packages/rspack/lib/processes.js b/packages/rspack/lib/processes.js index fbd4d1c787..ebc6efa96b 100644 --- a/packages/rspack/lib/processes.js +++ b/packages/rspack/lib/processes.js @@ -3,7 +3,7 @@ * @description Functions for managing Rspack processes */ import { checkNpmDependencyExists, getNpxCommand } from 'meteor/tools-core/lib/npm'; -import { getMeteorAppPort } from 'meteor/tools-core/lib/meteor'; +import { getMeteorAppPort, isMeteorTypescriptProject } from 'meteor/tools-core/lib/meteor'; /** * Calculates the devServerPort based on process.env.PORT @@ -136,11 +136,14 @@ export function getRspackEnv({ isClient, isServer, isTest: inIsTest }) { const entryKey = `${isTest && isTestModule ? 'test' : 'main'}${isClient ? 'Client' : 'Server'}`; const inputFilePath = isTest && isTestModule ? initialEntrypoints.testModule : initialEntrypoints[entryKey]; - const isTypescriptEnabled = inputFilePath?.endsWith('.ts') || inputFilePath?.endsWith('.tsx'); - const isTsxEnabled = inputFilePath?.endsWith('.tsx'); - const isJsxEnabled = inputFilePath?.endsWith('.jsx'); + const isTypescriptEnabled = process.env.METEOR_TYPESCRIPT_ENABLED === 'true' || + inputFilePath?.endsWith('.ts') || + inputFilePath?.endsWith('.tsx'); + + const isReactEnabled = process.env.METEOR_REACT_ENABLED === 'true'; + const isTsxEnabled = isTypescriptEnabled && (inputFilePath?.endsWith('.tsx') || isReactEnabled); + const isJsxEnabled = !isTypescriptEnabled && (inputFilePath?.endsWith('.jsx') || isReactEnabled); - const isReactEnabled = !!process.env.METEOR_REACT_ENABLED; const isBlazeEnabled = isMeteorBlazeProject(); const isBlazeHotEnabled = isMeteorBlazeHotProject(); const isBundleVisualizerEnabled = isMeteorBundleVisualizerProject(); diff --git a/packages/rspack/rspack_plugin.js b/packages/rspack/rspack_plugin.js index 4296cf98a3..5892feb615 100644 --- a/packages/rspack/rspack_plugin.js +++ b/packages/rspack/rspack_plugin.js @@ -22,6 +22,7 @@ const { const { ensureRspackInstalled, checkReactInstalled, + checkTypescriptInstalled, ensureRspackReactInstalled, } = require('./lib/dependencies'); @@ -114,13 +115,19 @@ if (isMeteorAppRun() || isMeteorAppBuild() || isMeteorAppTest()) { if (hasMeteorAppConfigAutoInstallDeps()) { // Ensure Rspack is installed await ensureRspackInstalled(); + } - // Check if Rspack React is installed - if (checkReactInstalled()) { + // Check if Rspack React is installed + if (checkReactInstalled()) { + // Auto install deps (by default enabled) + if (hasMeteorAppConfigAutoInstallDeps()) { await ensureRspackReactInstalled(); } } + // Check if TypeScript is installed + checkTypescriptInstalled(); + // Ensure the Rspack build context directory exists ensureRspackBuildContextExists(); diff --git a/packages/tools-core/lib/meteor.js b/packages/tools-core/lib/meteor.js index 9cb98c6acb..0d69ba2283 100644 --- a/packages/tools-core/lib/meteor.js +++ b/packages/tools-core/lib/meteor.js @@ -421,6 +421,14 @@ export function isMeteorBundleVisualizerProject() { return getMeteorAppPackages().includes('bundle-visualizer'); } +/** + * Checks if the Meteor application is a Typescript project. + * @returns {boolean} True if the application is a Typescript project, false otherwise. + */ +export function isMeteorTypescriptProject() { + return getMeteorAppPackages().includes('typescript'); +} + /** * Checks if the current Meteor command is 'test-packages'. * @returns {boolean} True if the current command is 'test-packages', false otherwise. diff --git a/tools/modern-tests/apps/react-router/package.json b/tools/modern-tests/apps/react-router/package.json index 9e3c78d80b..753fec1e49 100644 --- a/tools/modern-tests/apps/react-router/package.json +++ b/tools/modern-tests/apps/react-router/package.json @@ -26,7 +26,8 @@ "babel-loader": "^9.1.3", "less": "^4.4.0", "less-loader": "^12.3.0", - "playwright": "^1.54.2" + "playwright": "^1.54.2", + "typescript": "^5.9.2" }, "meteor": { "mainModule": { diff --git a/tools/modern-tests/apps/react-router/server/main.js b/tools/modern-tests/apps/react-router/server/main.js index cdb3e6ccfd..4f036e6af4 100644 --- a/tools/modern-tests/apps/react-router/server/main.js +++ b/tools/modern-tests/apps/react-router/server/main.js @@ -5,8 +5,10 @@ import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/ import '@helper/alias'; import ReactAlias from '@react/alias'; import './resolve-extensions/first'; +import { TypescriptEnabled } from './ts/helpers'; console.log('@react/alias loaded', ReactAlias.version); +console.log('TypescriptEnabled', TypescriptEnabled); async function insertLink({ title, url }) { await LinksCollection.insertAsync({ title, url, createdAt: new Date() }); diff --git a/tools/modern-tests/apps/react-router/server/ts/helpers.ts b/tools/modern-tests/apps/react-router/server/ts/helpers.ts new file mode 100644 index 0000000000..67a82d6f6c --- /dev/null +++ b/tools/modern-tests/apps/react-router/server/ts/helpers.ts @@ -0,0 +1 @@ +export const TypescriptEnabled: boolean = true; diff --git a/tools/modern-tests/assertions.js b/tools/modern-tests/assertions.js index 5fe8813962..cf31f15aaa 100644 --- a/tools/modern-tests/assertions.js +++ b/tools/modern-tests/assertions.js @@ -167,7 +167,6 @@ export async function assertConsoleEval(code, expectedResult, options = {}) { try { // Evaluate the code in the browser context const result = await page.evaluate(code); - console.log("--> (assertions.js-Line: 170)\n result: ", result); if (exactMatch) { // Check for exact match diff --git a/tools/modern-tests/react-router.test.js b/tools/modern-tests/react-router.test.js index 1941fa7b85..0cc7c98dc8 100644 --- a/tools/modern-tests/react-router.test.js +++ b/tools/modern-tests/react-router.test.js @@ -20,7 +20,7 @@ describe('ReactRouter App Bundling /', () => { }, customAssertions: { afterRun: async ({ result, port }) => { - await waitForReactEnvs(result.outputLines, { isJsxEnabled: true }); + await waitForReactEnvs(result.outputLines, { isTsxEnabled: true }); await waitForMeteorOutput(result.outputLines, /.*babel-plugin-react-compiler.*/); await assert404Page(port); // Less styles support @@ -47,7 +47,7 @@ describe('ReactRouter App Bundling /', () => { await waitForMeteorOutput(allConsoleLogs, /.*HMR.*Updated modules:.*/); }, afterRunProduction: async ({ result, port }) => { - await waitForReactEnvs(result.outputLines, { isJsxEnabled: true }); + await waitForReactEnvs(result.outputLines, { isTsxEnabled: true }); await waitForMeteorOutput(result.outputLines, /.*babel-plugin-react-compiler.*/); await assert404Page(port, { isProductionMode: true }); // Less styles support @@ -74,7 +74,7 @@ describe('ReactRouter App Bundling /', () => { await waitForReactEnvs(result.outputLines); }, afterBuild: async ({ result }) => { - await waitForReactEnvs(result.outputLines, { isJsxEnabled: true }); + await waitForReactEnvs(result.outputLines, { isTsxEnabled: true }); await waitForMeteorOutput(result.outputLines, /.*babel-plugin-react-compiler.*/); }, } @@ -95,10 +95,10 @@ export async function waitForReactEnvs(outputLines, options = {}) { /.*isReactEnabled:.*true.*/, options ); - if (options.isJsxEnabled) { + if (options.isTsxEnabled) { await waitForMeteorOutput( outputLines, - /.*isJsxEnabled:.*true.*/, + /.*isTsxEnabled:.*true.*/, options ); }