From 8381cd88ab3fa79c877c259607dcbb5828d10df2 Mon Sep 17 00:00:00 2001 From: Mish <10400064+mishushakov@users.noreply.github.com> Date: Fri, 25 Apr 2025 18:10:38 +0200 Subject: [PATCH 1/9] added typescript support via SWC --- template/.ts.swcrc | 15 +++++++++++++++ template/Dockerfile | 11 +++++++++-- template/server/contexts.py | 10 +++++++++- template/server/messaging.py | 19 +++++++++++++++++++ template/test.Dockerfile | 11 +++++++++-- 5 files changed, 61 insertions(+), 5 deletions(-) create mode 100644 template/.ts.swcrc diff --git a/template/.ts.swcrc b/template/.ts.swcrc new file mode 100644 index 00000000..cc07d97d --- /dev/null +++ b/template/.ts.swcrc @@ -0,0 +1,15 @@ +{ + "$schema": "https://swc.rs/schema.json", + "jsc": { + "parser": { + "syntax": "typescript" + } + }, + "module": { + "type": "commonjs" + }, + "env": { + "targets": "node 22" + }, + "isModule": false +} diff --git a/template/Dockerfile b/template/Dockerfile index b023150d..42bd906a 100644 --- a/template/Dockerfile +++ b/template/Dockerfile @@ -1,7 +1,11 @@ FROM python:3.10.14 RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y --no-install-recommends \ - build-essential curl git util-linux jq sudo nodejs npm fonts-noto-cjk + build-essential curl git util-linux jq sudo fonts-noto-cjk + +# Install Node.js 20.x from NodeSource +RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && \ + apt-get install -y nodejs ENV PIP_DEFAULT_TIMEOUT=100 \ PIP_DISABLE_PIP_VERSION_CHECK=1 \ @@ -24,10 +28,13 @@ RUN R -e "install.packages('IRkernel', repos='https://cloud.r-project.org')" RUN R -e "IRkernel::installspec(user = FALSE, name = 'r', displayname = 'R')" # Javascript Kernel -RUN npm install -g node-gyp RUN npm install -g --unsafe-perm ijavascript RUN ijsinstall --install=global +## TypeScript support +RUN npm install -g @swc/cli @swc/core +COPY .ts.swcrc /root/.ts.swcrc + # Deno Kernel COPY --from=denoland/deno:bin-2.0.4 /deno /usr/bin/deno RUN chmod +x /usr/bin/deno diff --git a/template/server/contexts.py b/template/server/contexts.py index 3af317d3..d078dc6e 100644 --- a/template/server/contexts.py +++ b/template/server/contexts.py @@ -11,6 +11,11 @@ logger = logging.Logger(__name__) +def get_kernel_for_language(language: str) -> str: + if language == "typescript": + return "javascript" + + return language def normalize_language(language: Optional[str]) -> str: if not language: @@ -21,13 +26,16 @@ def normalize_language(language: Optional[str]) -> str: if language == "js": return "javascript" + if language == "ts": + return "typescript" + return language async def create_context(client, websockets: dict, language: str, cwd: str) -> Context: data = { "path": str(uuid.uuid4()), - "kernel": {"name": language}, + "kernel": {"name": get_kernel_for_language(language)}, "type": "notebook", "name": str(uuid.uuid4()), } diff --git a/template/server/messaging.py b/template/server/messaging.py index 4f537aa8..c51c1ed3 100644 --- a/template/server/messaging.py +++ b/template/server/messaging.py @@ -3,6 +3,7 @@ import logging import uuid import asyncio +import subprocess from asyncio import Queue from envs import get_envs @@ -27,6 +28,7 @@ logger = logging.getLogger(__name__) +compile_typescript_cmd = "/usr/lib/node_modules/@swc/cli/bin/swc.js --config-file /root/.ts.swcrc --filename index.ts" class Execution: def __init__(self, in_background: bool = False): @@ -199,6 +201,23 @@ async def execute( + code ) + if self.language == "typescript": + logger.info("Compiling TypeScript: %s", code) + + # call swc to compile the typescript code + compile_result = subprocess.run(compile_typescript_cmd.split(), input=code.encode(), capture_output=True) + + if compile_result.returncode != 0: + logger.error("Error during TypeScript compilation: %s", compile_result.stderr.decode()) + yield Error( + name="TypeScriptCompilerError", + value=compile_result.stderr.decode(), + traceback="", + ) + return + + code = compile_result.stdout.decode() + logger.info(code) request = self._get_execute_request(message_id, code, False) diff --git a/template/test.Dockerfile b/template/test.Dockerfile index f3647911..984feb90 100644 --- a/template/test.Dockerfile +++ b/template/test.Dockerfile @@ -5,7 +5,11 @@ COPY --from=eclipse-temurin:11-jdk $JAVA_HOME $JAVA_HOME ENV PATH="${JAVA_HOME}/bin:${PATH}" RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y --no-install-recommends \ - build-essential curl git util-linux jq sudo nodejs npm fonts-noto-cjk + build-essential curl git util-linux jq sudo fonts-noto-cjk + +# Install Node.js 20.x from NodeSource +RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && \ + apt-get install -y nodejs ENV PIP_DEFAULT_TIMEOUT=100 \ PIP_DISABLE_PIP_VERSION_CHECK=1 \ @@ -19,10 +23,13 @@ COPY ./template/requirements.txt requirements.txt RUN pip install --no-cache-dir -r requirements.txt && ipython kernel install --name "python3" --user # Javascript Kernel -RUN npm install -g node-gyp RUN npm install -g --unsafe-perm ijavascript RUN ijsinstall --install=global +## TypeScript support +RUN npm install -g @swc/cli @swc/core +COPY ./template/.ts.swcrc /root/.ts.swcrc + # Deno Kernel COPY --from=denoland/deno:bin-2.0.4 /deno /usr/bin/deno RUN chmod +x /usr/bin/deno From 644c657cd7053e915780ff85d7e20d7219db2eb2 Mon Sep 17 00:00:00 2001 From: Mish <10400064+mishushakov@users.noreply.github.com> Date: Fri, 25 Apr 2025 18:17:04 +0200 Subject: [PATCH 2/9] added ts tests --- js/tests/defaultKernels.test.ts | 5 +++++ python/tests/async/test_async_default_kernels.py | 6 ++++++ python/tests/sync/test_default_kernels.py | 6 ++++++ 3 files changed, 17 insertions(+) diff --git a/js/tests/defaultKernels.test.ts b/js/tests/defaultKernels.test.ts index 8a7b7f65..b5a47944 100644 --- a/js/tests/defaultKernels.test.ts +++ b/js/tests/defaultKernels.test.ts @@ -6,3 +6,8 @@ sandboxTest('test js kernel', async ({ sandbox }) => { const output = await sandbox.runCode('console.log("Hello World!")', { language: 'js' }) expect(output.logs.stdout).toEqual(['Hello World!\n']) }) + +sandboxTest('test ts kernel', async ({ sandbox }) => { + const output = await sandbox.runCode('const message: string = "Hello World!"; console.log(message)', { language: 'ts' }) + expect(output.logs.stdout).toEqual(['Hello World!\n']) +}) diff --git a/python/tests/async/test_async_default_kernels.py b/python/tests/async/test_async_default_kernels.py index a632bda6..619571b5 100644 --- a/python/tests/async/test_async_default_kernels.py +++ b/python/tests/async/test_async_default_kernels.py @@ -6,3 +6,9 @@ async def test_js_kernel(async_sandbox: AsyncSandbox): "console.log('Hello, World!')", language="js" ) assert execution.logs.stdout == ["Hello, World!\n"] + +async def test_ts_kernel(async_sandbox: AsyncSandbox): + execution = await async_sandbox.run_code( + "const message: string = 'Hello, World!'; console.log(message);", language="ts" + ) + assert execution.logs.stdout == ["Hello, World!\n"] diff --git a/python/tests/sync/test_default_kernels.py b/python/tests/sync/test_default_kernels.py index d0daf820..0c8c25c7 100644 --- a/python/tests/sync/test_default_kernels.py +++ b/python/tests/sync/test_default_kernels.py @@ -18,3 +18,9 @@ def test_r_kernel(sandbox: Sandbox): def test_java_kernel(sandbox: Sandbox): execution = sandbox.run_code('System.out.println("Hello, World!")', language="java") assert execution.logs.stdout[0] == "Hello, World!" + + +@pytest.mark.skip_debug() +def test_ts_kernel(sandbox: Sandbox): + execution = sandbox.run_code("const message: string = 'Hello, World!'; console.log(message)", language="ts") + assert execution.logs.stdout == ["Hello, World!\n"] From 5c0db794572ddfb9eaade66bf074bac7f69ca1f6 Mon Sep 17 00:00:00 2001 From: Mish <10400064+mishushakov@users.noreply.github.com> Date: Fri, 25 Apr 2025 18:24:04 +0200 Subject: [PATCH 3/9] changed target node version --- template/.ts.swcrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/template/.ts.swcrc b/template/.ts.swcrc index cc07d97d..cf7cb8d3 100644 --- a/template/.ts.swcrc +++ b/template/.ts.swcrc @@ -9,7 +9,7 @@ "type": "commonjs" }, "env": { - "targets": "node 22" + "targets": "node 20" }, "isModule": false } From abac1f736ee122e84d15b1fa46597392a73444d1 Mon Sep 17 00:00:00 2001 From: Mish <10400064+mishushakov@users.noreply.github.com> Date: Wed, 30 Apr 2025 17:22:13 +0200 Subject: [PATCH 4/9] minor nits by @0div --- template/Dockerfile | 2 +- template/server/messaging.py | 27 +++++++++++++++++---------- template/test.Dockerfile | 4 ++-- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/template/Dockerfile b/template/Dockerfile index 42bd906a..36272ef0 100644 --- a/template/Dockerfile +++ b/template/Dockerfile @@ -31,7 +31,7 @@ RUN R -e "IRkernel::installspec(user = FALSE, name = 'r', displayname = 'R')" RUN npm install -g --unsafe-perm ijavascript RUN ijsinstall --install=global -## TypeScript support +## TypeScript compiler RUN npm install -g @swc/cli @swc/core COPY .ts.swcrc /root/.ts.swcrc diff --git a/template/server/messaging.py b/template/server/messaging.py index c51c1ed3..d2894d92 100644 --- a/template/server/messaging.py +++ b/template/server/messaging.py @@ -28,8 +28,6 @@ logger = logging.getLogger(__name__) -compile_typescript_cmd = "/usr/lib/node_modules/@swc/cli/bin/swc.js --config-file /root/.ts.swcrc --filename index.ts" - class Execution: def __init__(self, in_background: bool = False): self.queue = Queue[ @@ -204,20 +202,29 @@ async def execute( if self.language == "typescript": logger.info("Compiling TypeScript: %s", code) - # call swc to compile the typescript code - compile_result = subprocess.run(compile_typescript_cmd.split(), input=code.encode(), capture_output=True) - - if compile_result.returncode != 0: - logger.error("Error during TypeScript compilation: %s", compile_result.stderr.decode()) + # call SWC to compile the typescript code + try: + compile_result = subprocess.run("swc --config-file .ts.swcrc --filename index.ts".split(), input=code.encode(), capture_output=True) + + if compile_result.returncode != 0: + logger.error("Error during TypeScript compilation: %s", compile_result.stderr.decode()) + yield Error( + name="TypeScriptCompilerError", + value=compile_result.stderr.decode(), + traceback="", + ) + return + + code = compile_result.stdout.decode() + except Exception as e: + logger.error("Error starting SWC process: %s", e) yield Error( name="TypeScriptCompilerError", - value=compile_result.stderr.decode(), + value=str(e), traceback="", ) return - code = compile_result.stdout.decode() - logger.info(code) request = self._get_execute_request(message_id, code, False) diff --git a/template/test.Dockerfile b/template/test.Dockerfile index 984feb90..f9302081 100644 --- a/template/test.Dockerfile +++ b/template/test.Dockerfile @@ -26,9 +26,9 @@ RUN pip install --no-cache-dir -r requirements.txt && ipython kernel install --n RUN npm install -g --unsafe-perm ijavascript RUN ijsinstall --install=global -## TypeScript support +## TypeScript compiler RUN npm install -g @swc/cli @swc/core -COPY ./template/.ts.swcrc /root/.ts.swcrc +COPY ./template/.ts.swcrc $SERVER_PATH/.ts.swcrc # Deno Kernel COPY --from=denoland/deno:bin-2.0.4 /deno /usr/bin/deno From 9f44d34eb716f9776d3f7b673afae4b18ecc3a4f Mon Sep 17 00:00:00 2001 From: Mish <10400064+mishushakov@users.noreply.github.com> Date: Wed, 30 Apr 2025 17:34:56 +0200 Subject: [PATCH 5/9] added test for typescript kernel failure --- js/tests/defaultKernels.test.ts | 16 ++++++++++++++-- python/tests/async/test_async_default_kernels.py | 7 +++++++ python/tests/sync/test_default_kernels.py | 6 ++++++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/js/tests/defaultKernels.test.ts b/js/tests/defaultKernels.test.ts index b5a47944..5750644a 100644 --- a/js/tests/defaultKernels.test.ts +++ b/js/tests/defaultKernels.test.ts @@ -3,11 +3,23 @@ import { expect } from 'vitest' import { sandboxTest } from './setup' sandboxTest('test js kernel', async ({ sandbox }) => { - const output = await sandbox.runCode('console.log("Hello World!")', { language: 'js' }) + const output = await sandbox.runCode('console.log("Hello World!")', { + language: 'js', + }) expect(output.logs.stdout).toEqual(['Hello World!\n']) }) sandboxTest('test ts kernel', async ({ sandbox }) => { - const output = await sandbox.runCode('const message: string = "Hello World!"; console.log(message)', { language: 'ts' }) + const output = await sandbox.runCode( + 'const message: string = "Hello World!"; console.log(message)', + { language: 'ts' } + ) expect(output.logs.stdout).toEqual(['Hello World!\n']) }) + +sandboxTest('test ts kernel errors', async ({ sandbox }) => { + const output = await sandbox.runCode('import x from "module";', { + language: 'typescript', + }) + expect(output.error?.name).toEqual('TypeScriptCompilerError') +}) diff --git a/python/tests/async/test_async_default_kernels.py b/python/tests/async/test_async_default_kernels.py index 619571b5..72153022 100644 --- a/python/tests/async/test_async_default_kernels.py +++ b/python/tests/async/test_async_default_kernels.py @@ -12,3 +12,10 @@ async def test_ts_kernel(async_sandbox: AsyncSandbox): "const message: string = 'Hello, World!'; console.log(message);", language="ts" ) assert execution.logs.stdout == ["Hello, World!\n"] + +async def test_ts_kernel_errors(async_sandbox: AsyncSandbox): + execution = await async_sandbox.run_code( + "import x from 'module';", language="ts" + ) + assert execution.error is not None + assert execution.error.name == "TypeScriptCompilerError" diff --git a/python/tests/sync/test_default_kernels.py b/python/tests/sync/test_default_kernels.py index 0c8c25c7..0695defd 100644 --- a/python/tests/sync/test_default_kernels.py +++ b/python/tests/sync/test_default_kernels.py @@ -24,3 +24,9 @@ def test_java_kernel(sandbox: Sandbox): def test_ts_kernel(sandbox: Sandbox): execution = sandbox.run_code("const message: string = 'Hello, World!'; console.log(message)", language="ts") assert execution.logs.stdout == ["Hello, World!\n"] + + +def test_ts_kernel_errors(sandbox: Sandbox): + execution = sandbox.run_code("import x from 'module';", language="ts") + assert execution.error is not None + assert execution.error.name == "TypeScriptCompilerError" From bcd23c31d5770ec39abb8c053e37fa529168eed6 Mon Sep 17 00:00:00 2001 From: Mish <10400064+mishushakov@users.noreply.github.com> Date: Fri, 2 May 2025 11:29:56 +0200 Subject: [PATCH 6/9] changed swc path in prod Dockerfile --- template/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/template/Dockerfile b/template/Dockerfile index 36272ef0..199a5cb0 100644 --- a/template/Dockerfile +++ b/template/Dockerfile @@ -33,7 +33,7 @@ RUN ijsinstall --install=global ## TypeScript compiler RUN npm install -g @swc/cli @swc/core -COPY .ts.swcrc /root/.ts.swcrc +COPY .ts.swcrc $SERVER_PATH/.ts.swcrc # Deno Kernel COPY --from=denoland/deno:bin-2.0.4 /deno /usr/bin/deno From 9ef394f9b5c86145749c83a57673cc883af4a9a0 Mon Sep 17 00:00:00 2001 From: Mish <10400064+mishushakov@users.noreply.github.com> Date: Fri, 2 May 2025 11:35:13 +0200 Subject: [PATCH 7/9] added changeset --- .changeset/tiny-adults-breathe.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/tiny-adults-breathe.md diff --git a/.changeset/tiny-adults-breathe.md b/.changeset/tiny-adults-breathe.md new file mode 100644 index 00000000..38bb961f --- /dev/null +++ b/.changeset/tiny-adults-breathe.md @@ -0,0 +1,5 @@ +--- +'@e2b/code-interpreter-template': patch +--- + +added typescript support From 9ee90241833b5392e21c5d4e32102a0e7da0201c Mon Sep 17 00:00:00 2001 From: Mish <10400064+mishushakov@users.noreply.github.com> Date: Fri, 2 May 2025 13:58:55 +0200 Subject: [PATCH 8/9] skip test here so that the template can be built --- js/tests/defaultKernels.test.ts | 15 --------------- python/tests/async/test_async_default_kernels.py | 13 ------------- python/tests/sync/test_default_kernels.py | 12 ------------ 3 files changed, 40 deletions(-) diff --git a/js/tests/defaultKernels.test.ts b/js/tests/defaultKernels.test.ts index 5750644a..d8120267 100644 --- a/js/tests/defaultKernels.test.ts +++ b/js/tests/defaultKernels.test.ts @@ -8,18 +8,3 @@ sandboxTest('test js kernel', async ({ sandbox }) => { }) expect(output.logs.stdout).toEqual(['Hello World!\n']) }) - -sandboxTest('test ts kernel', async ({ sandbox }) => { - const output = await sandbox.runCode( - 'const message: string = "Hello World!"; console.log(message)', - { language: 'ts' } - ) - expect(output.logs.stdout).toEqual(['Hello World!\n']) -}) - -sandboxTest('test ts kernel errors', async ({ sandbox }) => { - const output = await sandbox.runCode('import x from "module";', { - language: 'typescript', - }) - expect(output.error?.name).toEqual('TypeScriptCompilerError') -}) diff --git a/python/tests/async/test_async_default_kernels.py b/python/tests/async/test_async_default_kernels.py index 72153022..a632bda6 100644 --- a/python/tests/async/test_async_default_kernels.py +++ b/python/tests/async/test_async_default_kernels.py @@ -6,16 +6,3 @@ async def test_js_kernel(async_sandbox: AsyncSandbox): "console.log('Hello, World!')", language="js" ) assert execution.logs.stdout == ["Hello, World!\n"] - -async def test_ts_kernel(async_sandbox: AsyncSandbox): - execution = await async_sandbox.run_code( - "const message: string = 'Hello, World!'; console.log(message);", language="ts" - ) - assert execution.logs.stdout == ["Hello, World!\n"] - -async def test_ts_kernel_errors(async_sandbox: AsyncSandbox): - execution = await async_sandbox.run_code( - "import x from 'module';", language="ts" - ) - assert execution.error is not None - assert execution.error.name == "TypeScriptCompilerError" diff --git a/python/tests/sync/test_default_kernels.py b/python/tests/sync/test_default_kernels.py index 0695defd..d0daf820 100644 --- a/python/tests/sync/test_default_kernels.py +++ b/python/tests/sync/test_default_kernels.py @@ -18,15 +18,3 @@ def test_r_kernel(sandbox: Sandbox): def test_java_kernel(sandbox: Sandbox): execution = sandbox.run_code('System.out.println("Hello, World!")', language="java") assert execution.logs.stdout[0] == "Hello, World!" - - -@pytest.mark.skip_debug() -def test_ts_kernel(sandbox: Sandbox): - execution = sandbox.run_code("const message: string = 'Hello, World!'; console.log(message)", language="ts") - assert execution.logs.stdout == ["Hello, World!\n"] - - -def test_ts_kernel_errors(sandbox: Sandbox): - execution = sandbox.run_code("import x from 'module';", language="ts") - assert execution.error is not None - assert execution.error.name == "TypeScriptCompilerError" From df71c98fde69d844980b29dfeabf320629052944 Mon Sep 17 00:00:00 2001 From: Mish <10400064+mishushakov@users.noreply.github.com> Date: Fri, 2 May 2025 14:25:18 +0200 Subject: [PATCH 9/9] added tests --- js/tests/defaultKernels.test.ts | 15 +++++++++++++++ python/tests/async/test_async_default_kernels.py | 13 +++++++++++++ python/tests/sync/test_default_kernels.py | 12 ++++++++++++ 3 files changed, 40 insertions(+) diff --git a/js/tests/defaultKernels.test.ts b/js/tests/defaultKernels.test.ts index d8120267..5750644a 100644 --- a/js/tests/defaultKernels.test.ts +++ b/js/tests/defaultKernels.test.ts @@ -8,3 +8,18 @@ sandboxTest('test js kernel', async ({ sandbox }) => { }) expect(output.logs.stdout).toEqual(['Hello World!\n']) }) + +sandboxTest('test ts kernel', async ({ sandbox }) => { + const output = await sandbox.runCode( + 'const message: string = "Hello World!"; console.log(message)', + { language: 'ts' } + ) + expect(output.logs.stdout).toEqual(['Hello World!\n']) +}) + +sandboxTest('test ts kernel errors', async ({ sandbox }) => { + const output = await sandbox.runCode('import x from "module";', { + language: 'typescript', + }) + expect(output.error?.name).toEqual('TypeScriptCompilerError') +}) diff --git a/python/tests/async/test_async_default_kernels.py b/python/tests/async/test_async_default_kernels.py index a632bda6..72153022 100644 --- a/python/tests/async/test_async_default_kernels.py +++ b/python/tests/async/test_async_default_kernels.py @@ -6,3 +6,16 @@ async def test_js_kernel(async_sandbox: AsyncSandbox): "console.log('Hello, World!')", language="js" ) assert execution.logs.stdout == ["Hello, World!\n"] + +async def test_ts_kernel(async_sandbox: AsyncSandbox): + execution = await async_sandbox.run_code( + "const message: string = 'Hello, World!'; console.log(message);", language="ts" + ) + assert execution.logs.stdout == ["Hello, World!\n"] + +async def test_ts_kernel_errors(async_sandbox: AsyncSandbox): + execution = await async_sandbox.run_code( + "import x from 'module';", language="ts" + ) + assert execution.error is not None + assert execution.error.name == "TypeScriptCompilerError" diff --git a/python/tests/sync/test_default_kernels.py b/python/tests/sync/test_default_kernels.py index d0daf820..0695defd 100644 --- a/python/tests/sync/test_default_kernels.py +++ b/python/tests/sync/test_default_kernels.py @@ -18,3 +18,15 @@ def test_r_kernel(sandbox: Sandbox): def test_java_kernel(sandbox: Sandbox): execution = sandbox.run_code('System.out.println("Hello, World!")', language="java") assert execution.logs.stdout[0] == "Hello, World!" + + +@pytest.mark.skip_debug() +def test_ts_kernel(sandbox: Sandbox): + execution = sandbox.run_code("const message: string = 'Hello, World!'; console.log(message)", language="ts") + assert execution.logs.stdout == ["Hello, World!\n"] + + +def test_ts_kernel_errors(sandbox: Sandbox): + execution = sandbox.run_code("import x from 'module';", language="ts") + assert execution.error is not None + assert execution.error.name == "TypeScriptCompilerError"