From c2236b7eb2858d2eb68d799e6105f1f2a1727279 Mon Sep 17 00:00:00 2001 From: Danilo Woznica Date: Fri, 7 Apr 2023 15:23:25 +0000 Subject: [PATCH 1/6] Update 3 files --- .../src/clients/node/client.utils.ts | 3 + .../src/clients/node/taskManager.test.ts | 124 ++++++++++++ .../src/clients/node/taskManager.ts | 178 ++++++++++++++++++ 3 files changed, 305 insertions(+) create mode 100644 sandpack-client/src/clients/node/taskManager.test.ts create mode 100644 sandpack-client/src/clients/node/taskManager.ts diff --git a/sandpack-client/src/clients/node/client.utils.ts b/sandpack-client/src/clients/node/client.utils.ts index 7d9dc09b0..c24bb254b 100644 --- a/sandpack-client/src/clients/node/client.utils.ts +++ b/sandpack-client/src/clients/node/client.utils.ts @@ -6,6 +6,8 @@ import { invariant } from "outvariant"; import type { SandpackBundlerFiles } from "../.."; import { createError } from "../.."; +import { tokenize } from "./taskManager"; + let counter = 0; export function generateRandomId() { @@ -72,6 +74,7 @@ export const findStartScriptPackageJson = ( const script = possibleKeys[index]; const candidate = scripts[script]; + console.log(tokenize(candidate)); const env = candidate .match(/(\w+=\w+;)*\w+=\w+/g) diff --git a/sandpack-client/src/clients/node/taskManager.test.ts b/sandpack-client/src/clients/node/taskManager.test.ts new file mode 100644 index 000000000..844086675 --- /dev/null +++ b/sandpack-client/src/clients/node/taskManager.test.ts @@ -0,0 +1,124 @@ +import { tokenize } from "./taskManager"; + +describe(tokenize, () => { + it("parses environment variables", () => { + const input = tokenize("FOO=1 tsc -p"); + const output = [ + { type: "EnvVar", value: { FOO: "1" } }, + { type: "Command", value: "tsc" }, + { type: "Argument", value: "-p" }, + ]; + + expect(input).toBe(output); + }); + + it("parses multiples envs environment variables", () => { + const input = tokenize("FOO=1 BAZ=bla tsc -p"); + const output = [ + { type: "EnvVar", value: { FOO: "1", BAZ: "bla" } }, + { type: "Command", value: "tsc" }, + { type: "Argument", value: "-p" }, + ]; + + expect(input).toBe(output); + }); + + it("parses command and argument", () => { + const input = tokenize("tsc -p"); + const output = [ + { type: "Command", value: "tsc" }, + { type: "Argument", value: "-p" }, + ]; + + expect(input).toBe(output); + }); + + it("parses two commands", () => { + const input = tokenize("tsc && node"); + const output = [ + { type: "Command", value: "tsc" }, + { type: "AND" }, + { type: "Command", value: "node" }, + ]; + + expect(input).toBe(output); + }); + + it("parses two commands", () => { + const input = tokenize("tsc -p . && node index.js"); + const output = [ + { type: "Command", value: "tsc" }, + { type: "Argument", value: "-p" }, + { type: "Command", value: "." }, + { type: "AND" }, + { type: "Command", value: "node" }, + { type: "Command", value: "index.js" }, + ]; + + expect(input).toBe(output); + }); + + it("parses multiple arguments", () => { + const input = tokenize("tsc --foo -- --foo"); + const output = [ + { type: "Command", value: "tsc" }, + { type: "Argument", value: "--foo" }, + { type: "Argument", value: "--" }, + { type: "Argument", value: "--foo" }, + ]; + + expect(input).toBe(output); + }); + + it("parses pipe and string commands", () => { + const input = tokenize(`echo "Hello World" | wc -w`); + const output = [ + { type: "Command", value: "echo" }, + { type: "String", value: '"Hello World"' }, + { type: "PIPE" }, + { type: "Command", value: "wc" }, + { type: "Argument", value: "-w" }, + ]; + + expect(input).toBe(output); + }); + + it("parses escaped characters", () => { + const input = tokenize(`echo "Hello | World" | wc -w`); + const output = [ + { type: "Command", value: "echo" }, + { type: "String", value: '"Hello | World"' }, + { type: "PIPE" }, + { type: "Command", value: "wc" }, + { type: "Argument", value: "-w" }, + ]; + + expect(input).toBe(output); + }); + + it("parses escaped characters", () => { + const input = tokenize(`echo "Hello | World" | wc -w`); + const output = [ + { type: "Command", value: "echo" }, + { type: "String", value: '"Hello | World"' }, + { type: "PIPE" }, + { type: "Command", value: "wc" }, + { type: "Argument", value: "-w" }, + ]; + + expect(input).toBe(output); + }); + + it("parses or", () => { + const input = tokenize(`echo "Hello | World" || wc -w`); + const output = [ + { type: "Command", value: "echo" }, + { type: "String", value: '"Hello | World"' }, + { type: "OR" }, + { type: "Command", value: "wc" }, + { type: "Argument", value: "-w" }, + ]; + + expect(input).toBe(output); + }); +}); diff --git a/sandpack-client/src/clients/node/taskManager.ts b/sandpack-client/src/clients/node/taskManager.ts new file mode 100644 index 000000000..36bcc1697 --- /dev/null +++ b/sandpack-client/src/clients/node/taskManager.ts @@ -0,0 +1,178 @@ +function isCommand(char: string) { + return /[a-zA-Z.]/.test(char); +} + +function isAlpha(char: string) { + return /[a-zA-Z]/.test(char); +} + +function isWhitespace(char: string) { + return /\s/.test(char); +} + +function isOperator(char: string) { + return /[&|]/.test(char); +} + +function isArgument(char: string) { + return /-/.test(char); +} + +function isString(char: string) { + return /["']/.test(char); +} + +function isEnvVar(char: string) { + return isAlpha(char) && char === char.toUpperCase(); +} + +enum TokenType { + OR = "OR", + AND = "AND", + PIPE = "PIPE", + Command = "Command", + Argument = "Argument", + String = "String", + EnvVar = "EnvVar", +} + +type Token = + | { type: TokenType } + | { + type: TokenType.Command | TokenType.Argument | TokenType.String; + value: string; + } + | { + type: TokenType.EnvVar; + value: Record; + }; + +const operators = new Map([ + ["&&", { type: TokenType.AND }], + ["||", { type: TokenType.OR }], + ["|", { type: TokenType.PIPE }], + ["-", { type: TokenType.Argument }], +]); + +export function tokenize(input: string): Token[] { + let current = 0; + const tokens = []; + + function parseCommand(): Token { + let value = ""; + while (isCommand(input[current]) && current < input.length) { + value += input[current]; + current++; + } + + return { type: TokenType.Command, value }; + } + + function parseOperator(): Token { + let value = ""; + while (isOperator(input[current]) && current < input.length) { + value += input[current]; + current++; + } + + return operators.get(value)!; + } + + function parseArgument(): Token { + let value = ""; + while ( + (isArgument(input[current]) || isAlpha(input[current])) && + current < input.length + ) { + value += input[current]; + current++; + } + + return { type: TokenType.Argument, value }; + } + + function parseString(): Token { + const openCloseQuote = input[current]; + + let value = input[current]; + current++; + + while (input[current] !== openCloseQuote && current < input.length) { + value += input[current]; + current++; + } + + value += input[current]; + current++; + + return { type: TokenType.String, value }; + } + + function parseEnvVars(): Token { + const value: Record = {}; + + const parseSingleEnv = () => { + let key = ""; + let pair = ""; + + while (input[current] !== "=" && current < input.length) { + key += input[current]; + current++; + } + + // Skip equal + if (input[current] === "=") { + current++; + } + + while (input[current] !== " " && current < input.length) { + pair += input[current]; + current++; + } + + value[key] = pair; + }; + + while (isEnvVar(input[current]) && current < input.length) { + parseSingleEnv(); + + current++; + } + + return { type: TokenType.EnvVar, value }; + } + + while (current < input.length) { + const currentChar = input[current]; + + if (isWhitespace(currentChar)) { + current++; + continue; + } + + switch (true) { + case isEnvVar(currentChar): + tokens.push(parseEnvVars()); + break; + + case isCommand(currentChar): + tokens.push(parseCommand()); + break; + case isOperator(currentChar): + tokens.push(parseOperator()); + break; + case isArgument(currentChar): + tokens.push(parseArgument()); + break; + + case isString(currentChar): + tokens.push(parseString()); + break; + + default: + throw new Error(`Unknown character: ${currentChar}`); + } + } + + return tokens; +} From ba13f8e0e5948a6b5fe1ef9dc5d896f6642219e2 Mon Sep 17 00:00:00 2001 From: Danilo Woznica Date: Fri, 7 Apr 2023 15:35:52 +0000 Subject: [PATCH 2/6] Update 3 files --- .../src/clients/node/client.utils.ts | 42 ++++++++++++------- .../src/clients/node/taskManager.test.ts | 20 ++++----- .../src/clients/node/taskManager.ts | 4 +- 3 files changed, 38 insertions(+), 28 deletions(-) diff --git a/sandpack-client/src/clients/node/client.utils.ts b/sandpack-client/src/clients/node/client.utils.ts index c24bb254b..7960db120 100644 --- a/sandpack-client/src/clients/node/client.utils.ts +++ b/sandpack-client/src/clients/node/client.utils.ts @@ -6,7 +6,7 @@ import { invariant } from "outvariant"; import type { SandpackBundlerFiles } from "../.."; import { createError } from "../.."; -import { tokenize } from "./taskManager"; +import { tokenize, TokenType } from "./taskManager"; let counter = 0; @@ -74,21 +74,31 @@ export const findStartScriptPackageJson = ( const script = possibleKeys[index]; const candidate = scripts[script]; - console.log(tokenize(candidate)); - - const env = candidate - .match(/(\w+=\w+;)*\w+=\w+/g) - // eslint-disable-next-line @typescript-eslint/no-explicit-any - ?.reduce((acc, curr) => { - const [key, value] = curr.split("="); - acc[key] = value; - return acc; - }, {}); - - const [command, ...args] = candidate - .replace(/(\w+=\w+;)*\w+=\w+/g, "") - .trim() - .split(" "); + + let env = {}; + let command = ""; + const args: string[] = []; + + tokenize(candidate).forEach((item) => { + const commandNotFoundYet = command === ""; + + if (item.type === TokenType.EnvVar) { + env = item.value; + } + + if (item.type === TokenType.Command && commandNotFoundYet) { + command = item.value; + } + + if ( + item.type === TokenType.Argument || + (!commandNotFoundYet && item.type === TokenType.Command) + ) { + args.push(item.value); + } + + // TODO: support TokenType.AND, TokenType.OR, TokenType.PIPE + }); return [command, args, { env }]; } diff --git a/sandpack-client/src/clients/node/taskManager.test.ts b/sandpack-client/src/clients/node/taskManager.test.ts index 844086675..fa595de81 100644 --- a/sandpack-client/src/clients/node/taskManager.test.ts +++ b/sandpack-client/src/clients/node/taskManager.test.ts @@ -9,7 +9,7 @@ describe(tokenize, () => { { type: "Argument", value: "-p" }, ]; - expect(input).toBe(output); + expect(input).toEqual(output); }); it("parses multiples envs environment variables", () => { @@ -20,7 +20,7 @@ describe(tokenize, () => { { type: "Argument", value: "-p" }, ]; - expect(input).toBe(output); + expect(input).toEqual(output); }); it("parses command and argument", () => { @@ -30,7 +30,7 @@ describe(tokenize, () => { { type: "Argument", value: "-p" }, ]; - expect(input).toBe(output); + expect(input).toEqual(output); }); it("parses two commands", () => { @@ -41,7 +41,7 @@ describe(tokenize, () => { { type: "Command", value: "node" }, ]; - expect(input).toBe(output); + expect(input).toEqual(output); }); it("parses two commands", () => { @@ -55,7 +55,7 @@ describe(tokenize, () => { { type: "Command", value: "index.js" }, ]; - expect(input).toBe(output); + expect(input).toEqual(output); }); it("parses multiple arguments", () => { @@ -67,7 +67,7 @@ describe(tokenize, () => { { type: "Argument", value: "--foo" }, ]; - expect(input).toBe(output); + expect(input).toEqual(output); }); it("parses pipe and string commands", () => { @@ -80,7 +80,7 @@ describe(tokenize, () => { { type: "Argument", value: "-w" }, ]; - expect(input).toBe(output); + expect(input).toEqual(output); }); it("parses escaped characters", () => { @@ -93,7 +93,7 @@ describe(tokenize, () => { { type: "Argument", value: "-w" }, ]; - expect(input).toBe(output); + expect(input).toEqual(output); }); it("parses escaped characters", () => { @@ -106,7 +106,7 @@ describe(tokenize, () => { { type: "Argument", value: "-w" }, ]; - expect(input).toBe(output); + expect(input).toEqual(output); }); it("parses or", () => { @@ -119,6 +119,6 @@ describe(tokenize, () => { { type: "Argument", value: "-w" }, ]; - expect(input).toBe(output); + expect(input).toEqual(output); }); }); diff --git a/sandpack-client/src/clients/node/taskManager.ts b/sandpack-client/src/clients/node/taskManager.ts index 36bcc1697..77fd14f0f 100644 --- a/sandpack-client/src/clients/node/taskManager.ts +++ b/sandpack-client/src/clients/node/taskManager.ts @@ -26,7 +26,7 @@ function isEnvVar(char: string) { return isAlpha(char) && char === char.toUpperCase(); } -enum TokenType { +export enum TokenType { OR = "OR", AND = "AND", PIPE = "PIPE", @@ -37,7 +37,7 @@ enum TokenType { } type Token = - | { type: TokenType } + | { type: TokenType.OR | TokenType.AND | TokenType.PIPE } | { type: TokenType.Command | TokenType.Argument | TokenType.String; value: string; From 65908f18ab7b53eee795360356fbcfa3d924496d Mon Sep 17 00:00:00 2001 From: Danilo Woznica Date: Fri, 7 Apr 2023 15:36:42 +0000 Subject: [PATCH 3/6] Update client.utils.ts --- sandpack-client/src/clients/node/client.utils.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/sandpack-client/src/clients/node/client.utils.ts b/sandpack-client/src/clients/node/client.utils.ts index 7960db120..f7f330278 100644 --- a/sandpack-client/src/clients/node/client.utils.ts +++ b/sandpack-client/src/clients/node/client.utils.ts @@ -54,6 +54,7 @@ export const findStartScriptPackageJson = ( packageJson: string ): [string, string[], ShellCommandOptions] => { let scripts: Record = {}; + // TODO: support postinstall const possibleKeys = ["dev", "start"]; try { From 3819b7376b3bc2bd8ee978b2d44bb9ba0dc13bb8 Mon Sep 17 00:00:00 2001 From: Danilo Woznica Date: Fri, 7 Apr 2023 21:53:17 +0000 Subject: [PATCH 4/6] Update 3 files --- .../src/clients/node/client.utils.ts | 44 ++++++++++++------- sandpack-client/src/clients/node/index.ts | 39 ++++++++++++---- .../src/templates/node/vite-react-ts.ts | 6 ++- 3 files changed, 65 insertions(+), 24 deletions(-) diff --git a/sandpack-client/src/clients/node/client.utils.ts b/sandpack-client/src/clients/node/client.utils.ts index f7f330278..1bb676961 100644 --- a/sandpack-client/src/clients/node/client.utils.ts +++ b/sandpack-client/src/clients/node/client.utils.ts @@ -47,12 +47,12 @@ export const fromBundlerFilesToFS = ( ); }; +type Command = [string, string[], ShellCommandOptions]; + /** * Figure out which script it must run to start a server */ -export const findStartScriptPackageJson = ( - packageJson: string -): [string, string[], ShellCommandOptions] => { +export const findStartScriptPackageJson = (packageJson: string): Command[] => { let scripts: Record = {}; // TODO: support postinstall const possibleKeys = ["dev", "start"]; @@ -73,35 +73,49 @@ export const findStartScriptPackageJson = ( for (let index = 0; index < possibleKeys.length; index++) { if (possibleKeys[index] in scripts) { const script = possibleKeys[index]; - const candidate = scripts[script]; + const listOfCommands: Command[] = []; + const commandTokens = tokenize(candidate); + let env = {}; let command = ""; - const args: string[] = []; + let args: string[] = []; - tokenize(candidate).forEach((item) => { + commandTokens.forEach((token, tokenIndex) => { const commandNotFoundYet = command === ""; - if (item.type === TokenType.EnvVar) { - env = item.value; + if (token.type === TokenType.EnvVar) { + env = token.value; + } + + if (token.type === TokenType.Command && commandNotFoundYet) { + command = token.value; } - if (item.type === TokenType.Command && commandNotFoundYet) { - command = item.value; + if ( + token.type === TokenType.Argument || + (!commandNotFoundYet && token.type === TokenType.Command) + ) { + args.push(token.value); } if ( - item.type === TokenType.Argument || - (!commandNotFoundYet && item.type === TokenType.Command) + token.type === TokenType.AND || + tokenIndex === commandTokens.length - 1 ) { - args.push(item.value); + const nodeboxCommand: Command = [command, args, { env }]; + listOfCommands.push(nodeboxCommand); + + env = {}; + command = ""; + args = []; } - // TODO: support TokenType.AND, TokenType.OR, TokenType.PIPE + // TODO: support TokenType.OR, TokenType.PIPE }); - return [command, args, { env }]; + return listOfCommands; } } diff --git a/sandpack-client/src/clients/node/index.ts b/sandpack-client/src/clients/node/index.ts index 7eb94ca86..320241a23 100644 --- a/sandpack-client/src/clients/node/index.ts +++ b/sandpack-client/src/clients/node/index.ts @@ -7,8 +7,8 @@ import type { FilesMap, ShellProcess, FSWatchEvent, + ShellInfo, } from "@codesandbox/nodebox"; -import type { ShellCommandOptions } from "@codesandbox/nodebox/build/modules/shell"; import type { ClientOptions, @@ -41,7 +41,6 @@ export class SandpackNode extends SandpackClient { private emulatorIframe!: HTMLIFrameElement; private emulator!: Nodebox; private emulatorShellProcess: ShellProcess | undefined; - private emulatorCommand: [string, string[], ShellCommandOptions] | undefined; private iframePreviewUrl: string | undefined; private _modulesCache = new Map(); private messageChannelId = generateRandomId(); @@ -127,7 +126,7 @@ export class SandpackNode extends SandpackClient { ): Promise<{ id: string }> { const packageJsonContent = readBuffer(files["/package.json"]); - this.emulatorCommand = findStartScriptPackageJson(packageJsonContent); + const emulatorCommand = findStartScriptPackageJson(packageJsonContent); this.emulatorShellProcess = this.emulator.shell.create(); // Shell listeners @@ -140,6 +139,8 @@ export class SandpackNode extends SandpackClient { }); }); + let globalIndexScript = 0; + await this.emulatorShellProcess.on("progress", (data) => { if ( data.state === "command_running" || @@ -148,10 +149,10 @@ export class SandpackNode extends SandpackClient { this.dispatch({ type: "shell/progress", data: { - ...data, + state: "command_running", command: [ - this.emulatorCommand?.[0], - this.emulatorCommand?.[1].join(" "), + emulatorCommand?.[globalIndexScript][0], + emulatorCommand?.[globalIndexScript][1].join(" "), ].join(" "), }, }); @@ -170,7 +171,29 @@ export class SandpackNode extends SandpackClient { this.dispatch({ type: "stdout", payload: { data, type: "err" } }); }); - return await this.emulatorShellProcess.runCommand(...this.emulatorCommand); + let shellId: ShellInfo; + + for ( + let indexScript = 0; + indexScript < emulatorCommand.length; + indexScript++ + ) { + if (this.emulatorShellProcess.id) { + await this.emulatorShellProcess.kill(); + } + + globalIndexScript = indexScript; + + shellId = await this.emulatorShellProcess.runCommand( + ...emulatorCommand[indexScript] + ); + + await new Promise((resolve) => { + setTimeout(resolve, 1000); + }); + } + + return shellId; } private async createPreviewURLFromId(id: string): Promise { @@ -360,7 +383,7 @@ export class SandpackNode extends SandpackClient { */ public async restartShellProcess(): Promise { - if (this.emulatorShellProcess && this.emulatorCommand) { + if (this.emulatorShellProcess) { // 1. Set the loading state and clean the URL this.dispatch({ type: "start", firstLoad: true }); diff --git a/sandpack-react/src/templates/node/vite-react-ts.ts b/sandpack-react/src/templates/node/vite-react-ts.ts index a8433f73f..692953287 100644 --- a/sandpack-react/src/templates/node/vite-react-ts.ts +++ b/sandpack-react/src/templates/node/vite-react-ts.ts @@ -84,11 +84,15 @@ root.render( 2 ), }, + "/foo.js": { + code: `const fs = require('fs'); +fs.writeFile("fileeee.ts", "", console.error)`, + }, "/package.json": { code: JSON.stringify( { scripts: { - dev: "vite --force", + dev: "tsc -p tsconfig.json && node foo.js && vite --force", build: "tsc && vite build", preview: "vite preview", }, From eefb60d037bdf640ef4c1f8495ba328fdab50c27 Mon Sep 17 00:00:00 2001 From: Danilo Woznica Date: Fri, 7 Apr 2023 21:54:18 +0000 Subject: [PATCH 5/6] Update client.utils.ts --- sandpack-client/src/clients/node/client.utils.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/sandpack-client/src/clients/node/client.utils.ts b/sandpack-client/src/clients/node/client.utils.ts index 1bb676961..003f5ea81 100644 --- a/sandpack-client/src/clients/node/client.utils.ts +++ b/sandpack-client/src/clients/node/client.utils.ts @@ -107,7 +107,6 @@ export const findStartScriptPackageJson = (packageJson: string): Command[] => { const nodeboxCommand: Command = [command, args, { env }]; listOfCommands.push(nodeboxCommand); - env = {}; command = ""; args = []; } From 58fc40933752a3333cb6e552915df0e5abac4e93 Mon Sep 17 00:00:00 2001 From: Danilo Woznica Date: Mon, 10 Apr 2023 12:06:35 +0000 Subject: [PATCH 6/6] Update 3 files --- sandpack-client/src/clients/node/index.ts | 39 +++++++++++-------- .../src/clients/node/taskManager.ts | 2 +- .../src/templates/node/vite-react-ts.ts | 5 ++- 3 files changed, 28 insertions(+), 18 deletions(-) diff --git a/sandpack-client/src/clients/node/index.ts b/sandpack-client/src/clients/node/index.ts index 320241a23..3bc76ec85 100644 --- a/sandpack-client/src/clients/node/index.ts +++ b/sandpack-client/src/clients/node/index.ts @@ -129,16 +129,6 @@ export class SandpackNode extends SandpackClient { const emulatorCommand = findStartScriptPackageJson(packageJsonContent); this.emulatorShellProcess = this.emulator.shell.create(); - // Shell listeners - await this.emulatorShellProcess.on("exit", (exitCode) => { - this.dispatch({ - type: "action", - action: "notification", - notificationType: "error", - title: createError(`Error: process.exit(${exitCode}) called.`), - }); - }); - let globalIndexScript = 0; await this.emulatorShellProcess.on("progress", (data) => { @@ -171,6 +161,17 @@ export class SandpackNode extends SandpackClient { this.dispatch({ type: "stdout", payload: { data, type: "err" } }); }); + await this.emulatorShellProcess.on("exit", (exitCode) => { + if (globalIndexScript === emulatorCommand.length - 1) { + this.dispatch({ + type: "action", + action: "notification", + notificationType: "error", + title: createError(`Error: process.exit(${exitCode}) called.`), + }); + } + }); + let shellId: ShellInfo; for ( @@ -178,18 +179,24 @@ export class SandpackNode extends SandpackClient { indexScript < emulatorCommand.length; indexScript++ ) { - if (this.emulatorShellProcess.id) { - await this.emulatorShellProcess.kill(); - } - globalIndexScript = indexScript; shellId = await this.emulatorShellProcess.runCommand( ...emulatorCommand[indexScript] ); - await new Promise((resolve) => { - setTimeout(resolve, 1000); + await new Promise(async (resolve) => { + await this.emulatorShellProcess?.on("exit", async () => { + if ( + this.emulatorShellProcess?.id && + this.emulatorShellProcess?.state === "running" + ) { + console.log(this.emulatorShellProcess); + await this.emulatorShellProcess.kill(); + } + + resolve(undefined); + }); }); } diff --git a/sandpack-client/src/clients/node/taskManager.ts b/sandpack-client/src/clients/node/taskManager.ts index 77fd14f0f..6a52ef39b 100644 --- a/sandpack-client/src/clients/node/taskManager.ts +++ b/sandpack-client/src/clients/node/taskManager.ts @@ -68,7 +68,7 @@ export function tokenize(input: string): Token[] { return { type: TokenType.Command, value }; } - function parseOperator(): Token { + function parseOperator(): { type: TokenType } { let value = ""; while (isOperator(input[current]) && current < input.length) { value += input[current]; diff --git a/sandpack-react/src/templates/node/vite-react-ts.ts b/sandpack-react/src/templates/node/vite-react-ts.ts index 692953287..311419f36 100644 --- a/sandpack-react/src/templates/node/vite-react-ts.ts +++ b/sandpack-react/src/templates/node/vite-react-ts.ts @@ -86,7 +86,10 @@ root.render( }, "/foo.js": { code: `const fs = require('fs'); -fs.writeFile("fileeee.ts", "", console.error)`, +fs.writeFile("fileeee.ts", "", console.error) + +process.exit() +`, }, "/package.json": { code: JSON.stringify(