From 27d00bd3dcc411a23ba1c2a32e1dac481f4808de Mon Sep 17 00:00:00 2001 From: Aakash Date: Thu, 26 Dec 2024 13:03:37 +0530 Subject: [PATCH 1/9] Working for Windows --- packages/api/exec.mts | 211 +++++++++++++++++++++++---- packages/api/server/channels/app.mts | 1 - packages/api/tsserver/tsservers.mts | 9 +- 3 files changed, 193 insertions(+), 28 deletions(-) diff --git a/packages/api/exec.mts b/packages/api/exec.mts index ba87e58b..9666c1c7 100644 --- a/packages/api/exec.mts +++ b/packages/api/exec.mts @@ -1,3 +1,4 @@ +/* eslint-disable turbo/no-undeclared-env-vars */ import Path from 'node:path'; import { spawn } from 'node:child_process'; @@ -5,6 +6,9 @@ interface NodeError extends Error { code?: string; } +/** + * Base type for execution requests containing common properties + */ export type BaseExecRequestType = { cwd: string; stdout: (data: Buffer) => void; @@ -13,20 +17,33 @@ export type BaseExecRequestType = { onError?: (err: NodeError) => void; }; +/** + * Type for Node.js execution requests + */ export type NodeRequestType = BaseExecRequestType & { env: NodeJS.ProcessEnv; entry: string; }; +/** + * Type for NPM installation requests + */ export type NPMInstallRequestType = BaseExecRequestType & { packages?: Array; args?: Array; }; +/** + * Type for NPX execution requests + */ type NpxRequestType = BaseExecRequestType & { args: Array; + env?: NodeJS.ProcessEnv; }; +/** + * Type for spawn call requests + */ type SpawnCallRequestType = { cwd: string; env: NodeJS.ProcessEnv; @@ -38,9 +55,58 @@ type SpawnCallRequestType = { onError?: (err: NodeError) => void; }; +/** + * General spawn call utility to execute a command with arguments. + * Enhanced with Windows-specific handling and improved error management. + */ export function spawnCall(options: SpawnCallRequestType) { const { cwd, env, command, args, stdout, stderr, onExit, onError } = options; - const child = spawn(command, args, { cwd: cwd, env: env }); + + // Handle Windows-specific path resolution + let finalCommand = command; + let finalArgs = args; + + if (process.platform === 'win32') { + // Check if the command ends with .cmd + if (command.endsWith('.cmd')) { + finalCommand = Path.join(process.env.SystemRoot || 'C:\\Windows', 'System32', 'cmd.exe'); + finalArgs = ['/c', command, ...args]; + } else if (Path.isAbsolute(command) && !command.includes('System32')) { + // For absolute paths that aren't System32 commands, still use cmd.exe + finalCommand = Path.join(process.env.SystemRoot || 'C:\\Windows', 'System32', 'cmd.exe'); + finalArgs = ['/c', command, ...args]; + } + } + + // Ensure the environment PATH is properly set + const finalEnv = { ...env }; + if (process.platform === 'win32') { + const additionalPaths = [ + Path.dirname(command), // Add the directory of the command + 'C:\\Program Files\\nodejs', + 'C:\\Program Files (x86)\\nodejs', + Path.join(process.env.APPDATA || '', 'npm'), + Path.join(process.env.LOCALAPPDATA || '', 'npm') + ]; + + const existingPath = finalEnv.PATH || ''; + const newPaths = additionalPaths + .filter(path => !existingPath.includes(path)) + .join(';'); + + finalEnv.PATH = newPaths + (existingPath ? ';' + existingPath : ''); + } + + console.log(`Executing command: ${finalCommand}`); + console.log(`With args:`, finalArgs); + console.log(`In directory: ${cwd}`); + + const child = spawn(finalCommand, finalArgs, { + cwd: cwd, + env: finalEnv, + windowsVerbatimArguments: true, + shell: process.platform === 'win32' + }); child.stdout.on('data', stdout); child.stderr.on('data', stderr); @@ -66,14 +132,13 @@ export function spawnCall(options: SpawnCallRequestType) { * Example: * * node({ - * cwd: '/Users/ben/.srcbook/30v2av4eee17m59dg2c29758to', - * env: {FOO_ENV_VAR: 'foooooooo'}, - * entry: '/Users/ben/.srcbook/30v2av4eee17m59dg2c29758to/src/foo.js', + * cwd: '/path/to/project', + * env: {FOO_ENV_VAR: 'value'}, + * entry: '/path/to/file.js', * stdout(data) {console.log(data.toString('utf8'))}, * stderr(data) {console.error(data.toString('utf8'))}, * onExit(code) {console.log(`Exit code: ${code}`)} * }); - * */ export function node(options: NodeRequestType) { const { cwd, env, entry, stdout, stderr, onExit } = options; @@ -95,22 +160,24 @@ export function node(options: NodeRequestType) { * Example: * * tsx({ - * cwd: '/Users/ben/.srcbook/30v2av4eee17m59dg2c29758to', - * env: {FOO_ENV_VAR: 'foooooooo'}, - * entry: '/Users/ben/.srcbook/30v2av4eee17m59dg2c29758to/src/foo.ts', + * cwd: '/path/to/project', + * env: {FOO_ENV_VAR: 'value'}, + * entry: '/path/to/file.ts', * stdout(data) {console.log(data.toString('utf8'))}, * stderr(data) {console.error(data.toString('utf8'))}, * onExit(code) {console.log(`Exit code: ${code}`)} * }); - * */ export function tsx(options: NodeRequestType) { const { cwd, env, entry, stdout, stderr, onExit } = options; - // We are making an assumption about `tsx` being the tool of choice - // for running TypeScript, as well as where it's located on the file system. + // Get the correct tsx executable path for the platform + const tsxExecutable = process.platform === 'win32' + ? Path.join(cwd, 'node_modules', '.bin', 'tsx.cmd') + : Path.join(cwd, 'node_modules', '.bin', 'tsx'); + return spawnCall({ - command: Path.join(cwd, 'node_modules', '.bin', 'tsx'), + command: tsxExecutable, cwd, args: [entry], stdout, @@ -126,7 +193,7 @@ export function tsx(options: NodeRequestType) { * Install all packages: * * npmInstall({ - * cwd: '/Users/ben/.srcbook/foo', + * cwd: '/path/to/project', * stdout(data) {console.log(data.toString('utf8'))}, * stderr(data) {console.error(data.toString('utf8'))}, * onExit(code) {console.log(`Exit code: ${code}`)} @@ -135,38 +202,130 @@ export function tsx(options: NodeRequestType) { * Install a specific package: * * npmInstall({ - * cwd: '/Users/ben/.srcbook/foo', - * package: 'marked', + * cwd: '/path/to/project', + * packages: ['lodash'], * stdout(data) {console.log(data.toString('utf8'))}, * stderr(data) {console.error(data.toString('utf8'))}, * onExit(code) {console.log(`Exit code: ${code}`)} * }); - * */ export function npmInstall(options: NPMInstallRequestType) { - const { cwd, stdout, stderr, onExit } = options; - const args = options.packages - ? ['install', '--include=dev', ...(options.args || []), ...options.packages] - : ['install', '--include=dev', ...(options.args || [])]; + const { cwd, stdout, stderr, onExit, packages, args } = options; + + // Get the npm executable path based on platform + const npmCommand = process.platform === 'win32' + ? Path.join(process.env.SystemRoot || 'C:\\Windows', 'System32', 'cmd.exe') + : 'npm'; + + // Construct the arguments differently for Windows + const installArgs = process.platform === 'win32' + ? [ + '/c', + 'npm', + 'install', + '--include=dev', + ...(args || []), + ...(packages || []) + ] + : [ + 'install', + '--include=dev', + ...(args || []), + ...(packages || []) + ]; + + // Set up the environment variables + const env = { ...process.env }; + + // Ensure PATH includes necessary directories for Windows + if (process.platform === 'win32') { + const additionalPaths = [ + 'C:\\Program Files\\nodejs', + 'C:\\Program Files (x86)\\nodejs', + Path.join(process.env.APPDATA || '', 'npm'), + Path.join(process.env.LOCALAPPDATA || '', 'npm') + ]; + + const existingPath = env.PATH || ''; + const newPaths = additionalPaths + .filter(path => !existingPath.includes(path)) + .join(';'); + + env.PATH = newPaths + (existingPath ? ';' + existingPath : ''); + } return spawnCall({ - command: 'npm', + command: npmCommand, cwd, - args, + args: installArgs, stdout, stderr, onExit, - env: process.env, + env, }); } /** * Run vite. + * + * Example: + * + * vite({ + * cwd: '/path/to/project', + * args: ['build'], + * stdout(data) {console.log(data.toString('utf8'))}, + * stderr(data) {console.error(data.toString('utf8'))}, + * onExit(code) {console.log(`Exit code: ${code}`)} + * }); */ export function vite(options: NpxRequestType) { + const { cwd, args = [], env = process.env } = options; + + if (process.platform === 'win32') { + const npmCommand = Path.join(process.env.SystemRoot || 'C:\\Windows', 'System32', 'cmd.exe'); + + // Construct a command that will work in Windows + const fullArgs = [ + '/c', + 'npm', + 'exec', // Use 'exec' instead of npx + '--yes', // Auto-approve any needed installations + '--', // Separator for the command + 'vite', // The command to run + ...(args || []) // Any additional arguments + ]; + + console.log(`Running vite with npm exec`); + console.log(`In directory: ${cwd}`); + console.log(`Command: ${npmCommand}`); + console.log(`Arguments:`, fullArgs); + + return spawnCall({ + ...options, + command: npmCommand, + args: fullArgs, + env: { + ...env, + FORCE_COLOR: '1', + // Add necessary paths + PATH: [ + Path.join(cwd, 'node_modules', '.bin'), + Path.join(process.env.APPDATA || '', 'npm'), + env.PATH || '' + ].join(Path.delimiter) + } + }); + } + + // Non-Windows platforms + const viteExecutable = Path.join(cwd, 'node_modules', '.bin', 'vite'); + return spawnCall({ ...options, - command: Path.join(options.cwd, 'node_modules', '.bin', 'vite'), - env: process.env, + command: viteExecutable, + env: { + ...env, + FORCE_COLOR: '1' + } }); -} +} \ No newline at end of file diff --git a/packages/api/server/channels/app.mts b/packages/api/server/channels/app.mts index 1d860270..0287d7df 100644 --- a/packages/api/server/channels/app.mts +++ b/packages/api/server/channels/app.mts @@ -113,7 +113,6 @@ async function previewStart( }, onExit: (code) => { deleteAppProcess(app.externalId, 'vite:server'); - wss.broadcast(`app:${app.externalId}`, 'preview:status', { url: null, status: 'stopped', diff --git a/packages/api/tsserver/tsservers.mts b/packages/api/tsserver/tsservers.mts index 9e86625d..9d6b6db9 100644 --- a/packages/api/tsserver/tsservers.mts +++ b/packages/api/tsserver/tsservers.mts @@ -44,8 +44,15 @@ export class TsServers { // created, the dependencies are not installed and thus this will // shut down immediately. Make sure that we handle this case after // package.json has finished installing its deps. - const child = spawn('npx', ['tsserver'], { + + const npxCmd = process.platform === 'win32' ? 'npx.cmd' : 'npx'; + + console.log(`Spawning tsserver with ${npxCmd} in ${options.cwd}`); + + + const child = spawn(npxCmd, ['tsserver'], { cwd: options.cwd, + shell: process.platform === 'win32' }); const server = new TsServer(child); From 72ad7a3de95d3b38b3f850f8d8e777c6c576deaf Mon Sep 17 00:00:00 2001 From: Aakash Date: Thu, 26 Dec 2024 13:31:55 +0530 Subject: [PATCH 2/9] Build error --- packages/web/tailwind.config.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/web/tailwind.config.js b/packages/web/tailwind.config.js index ceaaa635..7504b465 100644 --- a/packages/web/tailwind.config.js +++ b/packages/web/tailwind.config.js @@ -1,6 +1,6 @@ /** @type {import('tailwindcss').Config} */ -const plugin = require('tailwindcss/plugin'); -const path = require('path'); +import plugin from 'tailwindcss/plugin'; +import path from 'path'; module.exports = { darkMode: ['class'], From 1709789135741f8896c0898d0e257774bedcd125 Mon Sep 17 00:00:00 2001 From: Aakash Date: Thu, 26 Dec 2024 13:35:02 +0530 Subject: [PATCH 3/9] Prettier done --- packages/api/exec.mts | 81 ++++++++++++----------------- packages/api/tsserver/tsservers.mts | 5 +- 2 files changed, 36 insertions(+), 50 deletions(-) diff --git a/packages/api/exec.mts b/packages/api/exec.mts index 9666c1c7..744c1f35 100644 --- a/packages/api/exec.mts +++ b/packages/api/exec.mts @@ -86,13 +86,11 @@ export function spawnCall(options: SpawnCallRequestType) { 'C:\\Program Files\\nodejs', 'C:\\Program Files (x86)\\nodejs', Path.join(process.env.APPDATA || '', 'npm'), - Path.join(process.env.LOCALAPPDATA || '', 'npm') + Path.join(process.env.LOCALAPPDATA || '', 'npm'), ]; const existingPath = finalEnv.PATH || ''; - const newPaths = additionalPaths - .filter(path => !existingPath.includes(path)) - .join(';'); + const newPaths = additionalPaths.filter((path) => !existingPath.includes(path)).join(';'); finalEnv.PATH = newPaths + (existingPath ? ';' + existingPath : ''); } @@ -101,11 +99,11 @@ export function spawnCall(options: SpawnCallRequestType) { console.log(`With args:`, finalArgs); console.log(`In directory: ${cwd}`); - const child = spawn(finalCommand, finalArgs, { - cwd: cwd, + const child = spawn(finalCommand, finalArgs, { + cwd: cwd, env: finalEnv, windowsVerbatimArguments: true, - shell: process.platform === 'win32' + shell: process.platform === 'win32', }); child.stdout.on('data', stdout); @@ -172,9 +170,10 @@ export function tsx(options: NodeRequestType) { const { cwd, env, entry, stdout, stderr, onExit } = options; // Get the correct tsx executable path for the platform - const tsxExecutable = process.platform === 'win32' - ? Path.join(cwd, 'node_modules', '.bin', 'tsx.cmd') - : Path.join(cwd, 'node_modules', '.bin', 'tsx'); + const tsxExecutable = + process.platform === 'win32' + ? Path.join(cwd, 'node_modules', '.bin', 'tsx.cmd') + : Path.join(cwd, 'node_modules', '.bin', 'tsx'); return spawnCall({ command: tsxExecutable, @@ -213,43 +212,31 @@ export function npmInstall(options: NPMInstallRequestType) { const { cwd, stdout, stderr, onExit, packages, args } = options; // Get the npm executable path based on platform - const npmCommand = process.platform === 'win32' - ? Path.join(process.env.SystemRoot || 'C:\\Windows', 'System32', 'cmd.exe') - : 'npm'; + const npmCommand = + process.platform === 'win32' + ? Path.join(process.env.SystemRoot || 'C:\\Windows', 'System32', 'cmd.exe') + : 'npm'; // Construct the arguments differently for Windows - const installArgs = process.platform === 'win32' - ? [ - '/c', - 'npm', - 'install', - '--include=dev', - ...(args || []), - ...(packages || []) - ] - : [ - 'install', - '--include=dev', - ...(args || []), - ...(packages || []) - ]; + const installArgs = + process.platform === 'win32' + ? ['/c', 'npm', 'install', '--include=dev', ...(args || []), ...(packages || [])] + : ['install', '--include=dev', ...(args || []), ...(packages || [])]; // Set up the environment variables const env = { ...process.env }; - + // Ensure PATH includes necessary directories for Windows if (process.platform === 'win32') { const additionalPaths = [ 'C:\\Program Files\\nodejs', 'C:\\Program Files (x86)\\nodejs', Path.join(process.env.APPDATA || '', 'npm'), - Path.join(process.env.LOCALAPPDATA || '', 'npm') + Path.join(process.env.LOCALAPPDATA || '', 'npm'), ]; const existingPath = env.PATH || ''; - const newPaths = additionalPaths - .filter(path => !existingPath.includes(path)) - .join(';'); + const newPaths = additionalPaths.filter((path) => !existingPath.includes(path)).join(';'); env.PATH = newPaths + (existingPath ? ';' + existingPath : ''); } @@ -280,19 +267,19 @@ export function npmInstall(options: NPMInstallRequestType) { */ export function vite(options: NpxRequestType) { const { cwd, args = [], env = process.env } = options; - + if (process.platform === 'win32') { const npmCommand = Path.join(process.env.SystemRoot || 'C:\\Windows', 'System32', 'cmd.exe'); - + // Construct a command that will work in Windows const fullArgs = [ '/c', 'npm', - 'exec', // Use 'exec' instead of npx + 'exec', // Use 'exec' instead of npx '--yes', // Auto-approve any needed installations - '--', // Separator for the command - 'vite', // The command to run - ...(args || []) // Any additional arguments + '--', // Separator for the command + 'vite', // The command to run + ...(args || []), // Any additional arguments ]; console.log(`Running vite with npm exec`); @@ -311,21 +298,21 @@ export function vite(options: NpxRequestType) { PATH: [ Path.join(cwd, 'node_modules', '.bin'), Path.join(process.env.APPDATA || '', 'npm'), - env.PATH || '' - ].join(Path.delimiter) - } + env.PATH || '', + ].join(Path.delimiter), + }, }); } - + // Non-Windows platforms const viteExecutable = Path.join(cwd, 'node_modules', '.bin', 'vite'); - + return spawnCall({ ...options, command: viteExecutable, env: { ...env, - FORCE_COLOR: '1' - } + FORCE_COLOR: '1', + }, }); -} \ No newline at end of file +} diff --git a/packages/api/tsserver/tsservers.mts b/packages/api/tsserver/tsservers.mts index 9d6b6db9..edede43a 100644 --- a/packages/api/tsserver/tsservers.mts +++ b/packages/api/tsserver/tsservers.mts @@ -44,15 +44,14 @@ export class TsServers { // created, the dependencies are not installed and thus this will // shut down immediately. Make sure that we handle this case after // package.json has finished installing its deps. - + const npxCmd = process.platform === 'win32' ? 'npx.cmd' : 'npx'; console.log(`Spawning tsserver with ${npxCmd} in ${options.cwd}`); - const child = spawn(npxCmd, ['tsserver'], { cwd: options.cwd, - shell: process.platform === 'win32' + shell: process.platform === 'win32', }); const server = new TsServer(child); From 4d870d54625e36d3f295a31f4807ab6c3d56c941 Mon Sep 17 00:00:00 2001 From: Aakash Date: Thu, 26 Dec 2024 23:19:25 +0530 Subject: [PATCH 4/9] Removed Debugging Console and remoced unnecessary comments --- packages/api/exec.mts | 62 +++++-------------------------------------- 1 file changed, 6 insertions(+), 56 deletions(-) diff --git a/packages/api/exec.mts b/packages/api/exec.mts index 744c1f35..355aca72 100644 --- a/packages/api/exec.mts +++ b/packages/api/exec.mts @@ -6,9 +6,6 @@ interface NodeError extends Error { code?: string; } -/** - * Base type for execution requests containing common properties - */ export type BaseExecRequestType = { cwd: string; stdout: (data: Buffer) => void; @@ -17,33 +14,21 @@ export type BaseExecRequestType = { onError?: (err: NodeError) => void; }; -/** - * Type for Node.js execution requests - */ export type NodeRequestType = BaseExecRequestType & { env: NodeJS.ProcessEnv; entry: string; }; -/** - * Type for NPM installation requests - */ export type NPMInstallRequestType = BaseExecRequestType & { packages?: Array; args?: Array; }; -/** - * Type for NPX execution requests - */ type NpxRequestType = BaseExecRequestType & { args: Array; env?: NodeJS.ProcessEnv; }; -/** - * Type for spawn call requests - */ type SpawnCallRequestType = { cwd: string; env: NodeJS.ProcessEnv; @@ -55,34 +40,26 @@ type SpawnCallRequestType = { onError?: (err: NodeError) => void; }; -/** - * General spawn call utility to execute a command with arguments. - * Enhanced with Windows-specific handling and improved error management. - */ export function spawnCall(options: SpawnCallRequestType) { const { cwd, env, command, args, stdout, stderr, onExit, onError } = options; - // Handle Windows-specific path resolution let finalCommand = command; let finalArgs = args; if (process.platform === 'win32') { - // Check if the command ends with .cmd if (command.endsWith('.cmd')) { finalCommand = Path.join(process.env.SystemRoot || 'C:\\Windows', 'System32', 'cmd.exe'); finalArgs = ['/c', command, ...args]; } else if (Path.isAbsolute(command) && !command.includes('System32')) { - // For absolute paths that aren't System32 commands, still use cmd.exe finalCommand = Path.join(process.env.SystemRoot || 'C:\\Windows', 'System32', 'cmd.exe'); finalArgs = ['/c', command, ...args]; } } - // Ensure the environment PATH is properly set const finalEnv = { ...env }; if (process.platform === 'win32') { const additionalPaths = [ - Path.dirname(command), // Add the directory of the command + Path.dirname(command), 'C:\\Program Files\\nodejs', 'C:\\Program Files (x86)\\nodejs', Path.join(process.env.APPDATA || '', 'npm'), @@ -95,10 +72,6 @@ export function spawnCall(options: SpawnCallRequestType) { finalEnv.PATH = newPaths + (existingPath ? ';' + existingPath : ''); } - console.log(`Executing command: ${finalCommand}`); - console.log(`With args:`, finalArgs); - console.log(`In directory: ${cwd}`); - const child = spawn(finalCommand, finalArgs, { cwd: cwd, env: finalEnv, @@ -169,7 +142,6 @@ export function node(options: NodeRequestType) { export function tsx(options: NodeRequestType) { const { cwd, env, entry, stdout, stderr, onExit } = options; - // Get the correct tsx executable path for the platform const tsxExecutable = process.platform === 'win32' ? Path.join(cwd, 'node_modules', '.bin', 'tsx.cmd') @@ -211,22 +183,18 @@ export function tsx(options: NodeRequestType) { export function npmInstall(options: NPMInstallRequestType) { const { cwd, stdout, stderr, onExit, packages, args } = options; - // Get the npm executable path based on platform const npmCommand = process.platform === 'win32' ? Path.join(process.env.SystemRoot || 'C:\\Windows', 'System32', 'cmd.exe') : 'npm'; - // Construct the arguments differently for Windows const installArgs = process.platform === 'win32' ? ['/c', 'npm', 'install', '--include=dev', ...(args || []), ...(packages || [])] : ['install', '--include=dev', ...(args || []), ...(packages || [])]; - // Set up the environment variables const env = { ...process.env }; - // Ensure PATH includes necessary directories for Windows if (process.platform === 'win32') { const additionalPaths = [ 'C:\\Program Files\\nodejs', @@ -254,16 +222,6 @@ export function npmInstall(options: NPMInstallRequestType) { /** * Run vite. - * - * Example: - * - * vite({ - * cwd: '/path/to/project', - * args: ['build'], - * stdout(data) {console.log(data.toString('utf8'))}, - * stderr(data) {console.error(data.toString('utf8'))}, - * onExit(code) {console.log(`Exit code: ${code}`)} - * }); */ export function vite(options: NpxRequestType) { const { cwd, args = [], env = process.env } = options; @@ -271,22 +229,16 @@ export function vite(options: NpxRequestType) { if (process.platform === 'win32') { const npmCommand = Path.join(process.env.SystemRoot || 'C:\\Windows', 'System32', 'cmd.exe'); - // Construct a command that will work in Windows const fullArgs = [ '/c', 'npm', - 'exec', // Use 'exec' instead of npx - '--yes', // Auto-approve any needed installations - '--', // Separator for the command - 'vite', // The command to run - ...(args || []), // Any additional arguments + 'exec', + '--yes', + '--', + 'vite', + ...(args || []), ]; - console.log(`Running vite with npm exec`); - console.log(`In directory: ${cwd}`); - console.log(`Command: ${npmCommand}`); - console.log(`Arguments:`, fullArgs); - return spawnCall({ ...options, command: npmCommand, @@ -294,7 +246,6 @@ export function vite(options: NpxRequestType) { env: { ...env, FORCE_COLOR: '1', - // Add necessary paths PATH: [ Path.join(cwd, 'node_modules', '.bin'), Path.join(process.env.APPDATA || '', 'npm'), @@ -304,7 +255,6 @@ export function vite(options: NpxRequestType) { }); } - // Non-Windows platforms const viteExecutable = Path.join(cwd, 'node_modules', '.bin', 'vite'); return spawnCall({ From aa864e7ffd44105f0f91306d2119a0802b6201dc Mon Sep 17 00:00:00 2001 From: Aakash Date: Thu, 26 Dec 2024 23:23:25 +0530 Subject: [PATCH 5/9] Build error Resolved --- packages/api/exec.mts | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/packages/api/exec.mts b/packages/api/exec.mts index 355aca72..de690b40 100644 --- a/packages/api/exec.mts +++ b/packages/api/exec.mts @@ -229,15 +229,7 @@ export function vite(options: NpxRequestType) { if (process.platform === 'win32') { const npmCommand = Path.join(process.env.SystemRoot || 'C:\\Windows', 'System32', 'cmd.exe'); - const fullArgs = [ - '/c', - 'npm', - 'exec', - '--yes', - '--', - 'vite', - ...(args || []), - ]; + const fullArgs = ['/c', 'npm', 'exec', '--yes', '--', 'vite', ...(args || [])]; return spawnCall({ ...options, From 8eb78971909b49396efeb733854968183e356519 Mon Sep 17 00:00:00 2001 From: Aakash Date: Thu, 26 Dec 2024 23:53:38 +0530 Subject: [PATCH 6/9] Cleaned code with Singleton pattern by using where node --- packages/api/exec.mts | 190 +++++++++++++----------------------------- 1 file changed, 59 insertions(+), 131 deletions(-) diff --git a/packages/api/exec.mts b/packages/api/exec.mts index de690b40..b97e4217 100644 --- a/packages/api/exec.mts +++ b/packages/api/exec.mts @@ -1,6 +1,6 @@ /* eslint-disable turbo/no-undeclared-env-vars */ import Path from 'node:path'; -import { spawn } from 'node:child_process'; +import { spawn, execSync } from 'node:child_process'; interface NodeError extends Error { code?: string; @@ -40,59 +40,51 @@ type SpawnCallRequestType = { onError?: (err: NodeError) => void; }; -export function spawnCall(options: SpawnCallRequestType) { - const { cwd, env, command, args, stdout, stderr, onExit, onError } = options; - - let finalCommand = command; - let finalArgs = args; +class ExecutableResolver { + private static instance: ExecutableResolver; + private cachedPaths: Map = new Map(); - if (process.platform === 'win32') { - if (command.endsWith('.cmd')) { - finalCommand = Path.join(process.env.SystemRoot || 'C:\\Windows', 'System32', 'cmd.exe'); - finalArgs = ['/c', command, ...args]; - } else if (Path.isAbsolute(command) && !command.includes('System32')) { - finalCommand = Path.join(process.env.SystemRoot || 'C:\\Windows', 'System32', 'cmd.exe'); - finalArgs = ['/c', command, ...args]; + static getInstance(): ExecutableResolver { + if (!this.instance) { + this.instance = new ExecutableResolver(); } + return this.instance; } - const finalEnv = { ...env }; - if (process.platform === 'win32') { - const additionalPaths = [ - Path.dirname(command), - 'C:\\Program Files\\nodejs', - 'C:\\Program Files (x86)\\nodejs', - Path.join(process.env.APPDATA || '', 'npm'), - Path.join(process.env.LOCALAPPDATA || '', 'npm'), - ]; - - const existingPath = finalEnv.PATH || ''; - const newPaths = additionalPaths.filter((path) => !existingPath.includes(path)).join(';'); + private findExecutablePath(command: string): string { + try { + if (process.platform === 'win32') { + const paths = execSync(`where ${command}`).toString().trim().split('\n').map(p => p.trim()); + return paths.find(p => p.includes('Program Files')) ?? paths[0] ?? command; + } + return execSync(`which ${command}`).toString().trim(); + } catch { + return command; + } + } - finalEnv.PATH = newPaths + (existingPath ? ';' + existingPath : ''); + getPath(command: string): string { + if (!this.cachedPaths.has(command)) { + this.cachedPaths.set(command, this.findExecutablePath(command)); + } + return this.cachedPaths.get(command)!; } +} + +export function spawnCall(options: SpawnCallRequestType) { + const { cwd, env, command, args, stdout, stderr, onExit, onError } = options; - const child = spawn(finalCommand, finalArgs, { - cwd: cwd, - env: finalEnv, - windowsVerbatimArguments: true, + const child = spawn(command, args, { + cwd, + env, + windowsVerbatimArguments: process.platform === 'win32', shell: process.platform === 'win32', }); child.stdout.on('data', stdout); child.stderr.on('data', stderr); - - child.on('error', (err) => { - if (onError) { - onError(err); - } else { - console.error(err); - } - }); - - child.on('exit', (code, signal) => { - onExit(code, signal); - }); + child.on('error', onError || console.error); + child.on('exit', onExit); return child; } @@ -111,19 +103,14 @@ export function spawnCall(options: SpawnCallRequestType) { * onExit(code) {console.log(`Exit code: ${code}`)} * }); */ -export function node(options: NodeRequestType) { - const { cwd, env, entry, stdout, stderr, onExit } = options; - - return spawnCall({ - command: 'node', +export const node = ({ cwd, env, entry, ...rest }: NodeRequestType) => + spawnCall({ + command: ExecutableResolver.getInstance().getPath('node'), cwd, args: [entry], - stdout, - stderr, - onExit, env: { ...process.env, ...env }, + ...rest, }); -} /** * Execute a TypeScript file using tsx. @@ -139,24 +126,16 @@ export function node(options: NodeRequestType) { * onExit(code) {console.log(`Exit code: ${code}`)} * }); */ -export function tsx(options: NodeRequestType) { - const { cwd, env, entry, stdout, stderr, onExit } = options; - - const tsxExecutable = - process.platform === 'win32' +export const tsx = ({ cwd, env, entry, ...rest }: NodeRequestType) => + spawnCall({ + command: process.platform === 'win32' ? Path.join(cwd, 'node_modules', '.bin', 'tsx.cmd') - : Path.join(cwd, 'node_modules', '.bin', 'tsx'); - - return spawnCall({ - command: tsxExecutable, + : Path.join(cwd, 'node_modules', '.bin', 'tsx'), cwd, args: [entry], - stdout, - stderr, - onExit, env: { ...process.env, ...env }, + ...rest, }); -} /** * Run npm install. @@ -180,81 +159,30 @@ export function tsx(options: NodeRequestType) { * onExit(code) {console.log(`Exit code: ${code}`)} * }); */ -export function npmInstall(options: NPMInstallRequestType) { - const { cwd, stdout, stderr, onExit, packages, args } = options; - - const npmCommand = - process.platform === 'win32' - ? Path.join(process.env.SystemRoot || 'C:\\Windows', 'System32', 'cmd.exe') - : 'npm'; - - const installArgs = - process.platform === 'win32' - ? ['/c', 'npm', 'install', '--include=dev', ...(args || []), ...(packages || [])] - : ['install', '--include=dev', ...(args || []), ...(packages || [])]; - - const env = { ...process.env }; - - if (process.platform === 'win32') { - const additionalPaths = [ - 'C:\\Program Files\\nodejs', - 'C:\\Program Files (x86)\\nodejs', - Path.join(process.env.APPDATA || '', 'npm'), - Path.join(process.env.LOCALAPPDATA || '', 'npm'), - ]; - - const existingPath = env.PATH || ''; - const newPaths = additionalPaths.filter((path) => !existingPath.includes(path)).join(';'); - - env.PATH = newPaths + (existingPath ? ';' + existingPath : ''); - } - - return spawnCall({ - command: npmCommand, +export const npmInstall = ({ cwd, packages = [], args = [], ...rest }: NPMInstallRequestType) => + spawnCall({ + command: 'npm', cwd, - args: installArgs, - stdout, - stderr, - onExit, - env, + args: ['install', '--include=dev', ...args, ...packages], + env: process.env, + ...rest, }); -} /** * Run vite. */ -export function vite(options: NpxRequestType) { - const { cwd, args = [], env = process.env } = options; - - if (process.platform === 'win32') { - const npmCommand = Path.join(process.env.SystemRoot || 'C:\\Windows', 'System32', 'cmd.exe'); - - const fullArgs = ['/c', 'npm', 'exec', '--yes', '--', 'vite', ...(args || [])]; - - return spawnCall({ - ...options, - command: npmCommand, - args: fullArgs, - env: { - ...env, - FORCE_COLOR: '1', - PATH: [ - Path.join(cwd, 'node_modules', '.bin'), - Path.join(process.env.APPDATA || '', 'npm'), - env.PATH || '', - ].join(Path.delimiter), - }, - }); - } - - const viteExecutable = Path.join(cwd, 'node_modules', '.bin', 'vite'); - - return spawnCall({ - ...options, - command: viteExecutable, +export const vite = ({ cwd, args = [], env = process.env, ...rest }: NpxRequestType) => + spawnCall({ + command: process.platform === 'win32' + ? 'npx.cmd' + : Path.join(cwd, 'node_modules', '.bin', 'vite'), + cwd, + args: process.platform === 'win32' + ? ['vite', ...args] + : args, env: { ...env, FORCE_COLOR: '1', }, + ...rest, }); -} From a44dfafeafe47cd59a82d5c440bf067b7de18f3b Mon Sep 17 00:00:00 2001 From: Aakash Date: Thu, 26 Dec 2024 23:55:56 +0530 Subject: [PATCH 7/9] build error --- packages/api/exec.mts | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/packages/api/exec.mts b/packages/api/exec.mts index b97e4217..6211f701 100644 --- a/packages/api/exec.mts +++ b/packages/api/exec.mts @@ -54,8 +54,12 @@ class ExecutableResolver { private findExecutablePath(command: string): string { try { if (process.platform === 'win32') { - const paths = execSync(`where ${command}`).toString().trim().split('\n').map(p => p.trim()); - return paths.find(p => p.includes('Program Files')) ?? paths[0] ?? command; + const paths = execSync(`where ${command}`) + .toString() + .trim() + .split('\n') + .map((p) => p.trim()); + return paths.find((p) => p.includes('Program Files')) ?? paths[0] ?? command; } return execSync(`which ${command}`).toString().trim(); } catch { @@ -128,9 +132,10 @@ export const node = ({ cwd, env, entry, ...rest }: NodeRequestType) => */ export const tsx = ({ cwd, env, entry, ...rest }: NodeRequestType) => spawnCall({ - command: process.platform === 'win32' - ? Path.join(cwd, 'node_modules', '.bin', 'tsx.cmd') - : Path.join(cwd, 'node_modules', '.bin', 'tsx'), + command: + process.platform === 'win32' + ? Path.join(cwd, 'node_modules', '.bin', 'tsx.cmd') + : Path.join(cwd, 'node_modules', '.bin', 'tsx'), cwd, args: [entry], env: { ...process.env, ...env }, @@ -173,13 +178,10 @@ export const npmInstall = ({ cwd, packages = [], args = [], ...rest }: NPMInstal */ export const vite = ({ cwd, args = [], env = process.env, ...rest }: NpxRequestType) => spawnCall({ - command: process.platform === 'win32' - ? 'npx.cmd' - : Path.join(cwd, 'node_modules', '.bin', 'vite'), + command: + process.platform === 'win32' ? 'npx.cmd' : Path.join(cwd, 'node_modules', '.bin', 'vite'), cwd, - args: process.platform === 'win32' - ? ['vite', ...args] - : args, + args: process.platform === 'win32' ? ['vite', ...args] : args, env: { ...env, FORCE_COLOR: '1', From ad43030ec66b04eeea017c147c8988ebe4d696aa Mon Sep 17 00:00:00 2001 From: Aakash Amod Rajput <126223325+aakash-a-dev@users.noreply.github.com> Date: Fri, 27 Dec 2024 23:53:41 +0530 Subject: [PATCH 8/9] Removed Debug log --- packages/api/tsserver/tsservers.mts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/api/tsserver/tsservers.mts b/packages/api/tsserver/tsservers.mts index edede43a..dcc51041 100644 --- a/packages/api/tsserver/tsservers.mts +++ b/packages/api/tsserver/tsservers.mts @@ -47,8 +47,6 @@ export class TsServers { const npxCmd = process.platform === 'win32' ? 'npx.cmd' : 'npx'; - console.log(`Spawning tsserver with ${npxCmd} in ${options.cwd}`); - const child = spawn(npxCmd, ['tsserver'], { cwd: options.cwd, shell: process.platform === 'win32', From 9277dc8e2175be7a00a865bdc3631ca117a709fa Mon Sep 17 00:00:00 2001 From: Aakash Date: Wed, 22 Jan 2025 23:24:18 +0530 Subject: [PATCH 9/9] Updated as per comment --- packages/api/exec.mts | 68 +++++++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 35 deletions(-) diff --git a/packages/api/exec.mts b/packages/api/exec.mts index 6211f701..73b39fba 100644 --- a/packages/api/exec.mts +++ b/packages/api/exec.mts @@ -1,6 +1,6 @@ /* eslint-disable turbo/no-undeclared-env-vars */ import Path from 'node:path'; -import { spawn, execSync } from 'node:child_process'; +import { spawn } from 'node:child_process'; interface NodeError extends Error { code?: string; @@ -40,49 +40,47 @@ type SpawnCallRequestType = { onError?: (err: NodeError) => void; }; -class ExecutableResolver { - private static instance: ExecutableResolver; - private cachedPaths: Map = new Map(); - - static getInstance(): ExecutableResolver { - if (!this.instance) { - this.instance = new ExecutableResolver(); - } - return this.instance; +/** + * Main spawnCall function that routes to platform-specific implementations. + */ +export function spawnCall(options: SpawnCallRequestType) { + if (process.platform === 'win32') { + return spawnCallWindows(options); + } else { + return spawnCallUnix(options); } +} - private findExecutablePath(command: string): string { - try { - if (process.platform === 'win32') { - const paths = execSync(`where ${command}`) - .toString() - .trim() - .split('\n') - .map((p) => p.trim()); - return paths.find((p) => p.includes('Program Files')) ?? paths[0] ?? command; - } - return execSync(`which ${command}`).toString().trim(); - } catch { - return command; - } - } +/** + * Unix-specific implementation of spawnCall. + */ +function spawnCallUnix(options: SpawnCallRequestType) { + const { cwd, env, command, args, stdout, stderr, onExit, onError } = options; - getPath(command: string): string { - if (!this.cachedPaths.has(command)) { - this.cachedPaths.set(command, this.findExecutablePath(command)); - } - return this.cachedPaths.get(command)!; - } + const child = spawn(command, args, { + cwd, + env, + }); + + child.stdout.on('data', stdout); + child.stderr.on('data', stderr); + child.on('error', onError || console.error); + child.on('exit', onExit); + + return child; } -export function spawnCall(options: SpawnCallRequestType) { +/** + * Windows-specific implementation of spawnCall. + */ +function spawnCallWindows(options: SpawnCallRequestType) { const { cwd, env, command, args, stdout, stderr, onExit, onError } = options; const child = spawn(command, args, { cwd, env, - windowsVerbatimArguments: process.platform === 'win32', - shell: process.platform === 'win32', + windowsVerbatimArguments: true, + shell: true, }); child.stdout.on('data', stdout); @@ -109,7 +107,7 @@ export function spawnCall(options: SpawnCallRequestType) { */ export const node = ({ cwd, env, entry, ...rest }: NodeRequestType) => spawnCall({ - command: ExecutableResolver.getInstance().getPath('node'), + command: 'node', cwd, args: [entry], env: { ...process.env, ...env },