@tscircuit/eval 0.0.310 → 0.0.312

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.ts CHANGED
@@ -1,4 +1,3 @@
1
1
  export * from "./runner"
2
2
  export * from "./worker"
3
3
  export * from "./getPossibleEntrypointComponentPaths"
4
- export * from "./getPlatformConfig"
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tscircuit/eval",
3
3
  "main": "dist/lib/index.js",
4
- "version": "0.0.310",
4
+ "version": "0.0.312",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "build": "bun run build:lib && bun run build:webworker && bun run build:blob-url && bun run build:runner && bun run build:worker-wrapper",
@@ -0,0 +1,42 @@
1
+ import { test, expect } from "bun:test"
2
+ import { runTscircuitCode } from "lib/runner"
3
+
4
+ test(
5
+ "should support importing various npm packages",
6
+ async () => {
7
+ const circuitJson = await runTscircuitCode(
8
+ `
9
+ import _ from "lodash"
10
+ import { v4 as uuidv4 } from "uuid"
11
+ import dayjs from "dayjs"
12
+
13
+ export default () => {
14
+ // Test lodash
15
+ if (!_.isEqual({ a: 1 }, { a: 1 })) {
16
+ throw new Error("_.isEqual failed")
17
+ }
18
+
19
+ // Test uuid
20
+ const uuid = uuidv4()
21
+ if (typeof uuid !== "string" || uuid.length < 36) {
22
+ throw new Error("uuid.v4 failed to generate a valid uuid")
23
+ }
24
+
25
+ // Test dayjs
26
+ if (!dayjs().isValid()) {
27
+ throw new Error("dayjs().isValid() failed")
28
+ }
29
+
30
+ return <resistor name="R1" resistance="1k" />
31
+ }
32
+ `,
33
+ )
34
+
35
+ const resistor = circuitJson.find(
36
+ (element) => element.type === "source_component" && element.name === "R1",
37
+ )
38
+
39
+ expect(resistor).toBeDefined()
40
+ },
41
+ { timeout: 15000 },
42
+ )
@@ -6,6 +6,7 @@ import { importSnippet } from "./import-snippet"
6
6
  import { resolveFilePath } from "lib/runner/resolveFilePath"
7
7
  import { resolveNodeModule } from "lib/utils/resolve-node-module"
8
8
  import { importNodeModule } from "./import-node-module"
9
+ import { importNpmPackage } from "./import-npm-package"
9
10
  import Debug from "debug"
10
11
 
11
12
  const debug = Debug("tsci:eval:import-eval-path")
@@ -59,6 +60,10 @@ export async function importEvalPath(
59
60
  return importSnippet(importName, ctx, depth)
60
61
  }
61
62
 
63
+ if (!importName.startsWith(".") && !importName.startsWith("/")) {
64
+ return importNpmPackage(importName, ctx)
65
+ }
66
+
62
67
  throw new Error(
63
68
  `Unresolved import "${importName}" ${opts.cwd ? `from directory "${opts.cwd}"` : ""}`,
64
69
  )
@@ -0,0 +1,69 @@
1
+ import { evalCompiledJs } from "./eval-compiled-js"
2
+ import type { ExecutionContext } from "./execution-context"
3
+ import * as Babel from "@babel/standalone"
4
+ import { dirname } from "lib/utils/dirname"
5
+ import Debug from "debug"
6
+
7
+ const debug = Debug("tsci:eval:import-npm-package")
8
+
9
+ function extractPackagePathFromJSDelivr(url: string) {
10
+ const prefix = "https://cdn.jsdelivr.net/npm/"
11
+ if (url.startsWith(prefix)) {
12
+ return url.substring(prefix.length).replace(/\/\+esm$/, "")
13
+ }
14
+ return url
15
+ }
16
+
17
+ export async function importNpmPackage(
18
+ importName: string,
19
+ ctx: ExecutionContext,
20
+ ) {
21
+ debug(`importing npm package: ${importName}`)
22
+ const { preSuppliedImports } = ctx
23
+
24
+ if (preSuppliedImports[importName]) return
25
+
26
+ const npmCdnUrl = `https://cdn.jsdelivr.net/npm/${importName}/+esm`
27
+
28
+ let finalUrl: string | undefined
29
+ const { content, error } = await globalThis
30
+ .fetch(npmCdnUrl)
31
+ .then(async (res) => {
32
+ finalUrl = res.url
33
+ if (!res.ok)
34
+ throw new Error(
35
+ `Could not fetch "${importName}" from jsdelivr: ${res.statusText}`,
36
+ )
37
+ return { content: await res.text(), error: null }
38
+ })
39
+ .catch((e) => ({ error: e, content: null }))
40
+
41
+ if (error) {
42
+ console.error("Error fetching npm import", importName, error)
43
+ throw error
44
+ }
45
+
46
+ const transpiled = Babel.transform(content!, {
47
+ presets: ["react", "env"],
48
+ plugins: ["transform-modules-commonjs"],
49
+ filename: importName,
50
+ })
51
+
52
+ if (!transpiled.code) {
53
+ throw new Error(`Babel transpilation failed for ${importName}`)
54
+ }
55
+ try {
56
+ const finalImportName = extractPackagePathFromJSDelivr(finalUrl!)
57
+ const cwd = dirname(finalImportName)
58
+ const exports = evalCompiledJs(
59
+ transpiled.code!,
60
+ preSuppliedImports,
61
+ cwd,
62
+ ).exports
63
+ preSuppliedImports[importName] = exports
64
+ preSuppliedImports[finalImportName] = exports
65
+ preSuppliedImports[finalUrl!] = exports
66
+ } catch (e: any) {
67
+ throw new Error(`Eval npm package error for "${importName}": ${e.message}`)
68
+ }
69
+ }
@@ -1 +0,0 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" width="800" height="600" data-software-used-string="@tscircuit/core@0.0.706"><style></style><rect class="boundary" x="0" y="0" fill="#000" width="800" height="600"/><rect class="pcb-boundary" fill="none" stroke="#fff" stroke-width="0.3" x="69.204152249135" y="139.44636678200698" width="661.5916955017301" height="321.10726643598616"/><path class="pcb-board" d="M 69.204152249135 460.55363321799314 L 730.795847750865 460.55363321799314 L 730.795847750865 139.44636678200698 L 69.204152249135 139.44636678200698 Z" fill="none" stroke="rgba(255, 255, 255, 0.5)" stroke-width="6.920415224913495"/><path class="pcb-trace" stroke="rgb(200, 52, 52)" fill="none" d="M 503.11418685121106 300.00000000000006 L 296.88581314878894 300.00000000000006" stroke-width="10.380622837370241" stroke-linecap="round" stroke-linejoin="round" shape-rendering="crispEdges" data-layer="top"/><path class="pcb-trace" stroke="rgb(200, 52, 52)" fill="none" d="M 296.88581314878894 300.00000000000006 L 296.88581314878894 300.00000000000006" stroke-width="10.380622837370241" stroke-linecap="round" stroke-linejoin="round" shape-rendering="crispEdges" data-layer="top"/><rect class="pcb-pad" fill="rgb(200, 52, 52)" x="484.42906574394465" y="277.8546712802769" width="37.37024221453287" height="44.29065743944637" data-layer="top"/><rect class="pcb-pad" fill="rgb(200, 52, 52)" x="555.0173010380622" y="277.8546712802769" width="37.37024221453287" height="44.29065743944637" data-layer="top"/><rect class="pcb-pad" fill="rgb(200, 52, 52)" x="207.61245674740485" y="277.8546712802769" width="37.37024221453287" height="44.29065743944637" data-layer="top"/><rect class="pcb-pad" fill="rgb(200, 52, 52)" x="278.20069204152253" y="277.8546712802769" width="37.37024221453287" height="44.29065743944637" data-layer="top"/><path class="pcb-silkscreen pcb-silkscreen-top" d="M 573.7024221453287 250.17301038062288 L 470.5882352941177 250.17301038062288 L 470.5882352941177 349.82698961937723 L 573.7024221453287 349.82698961937723" fill="none" stroke="#f2eda1" stroke-width="6.920415224913495" stroke-linecap="round" stroke-linejoin="round" data-pcb-component-id="pcb_component_1" data-pcb-silkscreen-path-id="pcb_silkscreen_path_0"/><path class="pcb-silkscreen pcb-silkscreen-top" d="M 250.95910034602076 273.7024221453288 L 272.22429065743944 273.7024221453288" fill="none" stroke="#f2eda1" stroke-width="8.304498269896193" stroke-linecap="round" stroke-linejoin="round" data-pcb-component-id="pcb_component_0" data-pcb-silkscreen-path-id="pcb_silkscreen_path_1"/><path class="pcb-silkscreen pcb-silkscreen-top" d="M 250.95910034602076 326.2975778546713 L 272.22429065743944 326.2975778546713" fill="none" stroke="#f2eda1" stroke-width="8.304498269896193" stroke-linecap="round" stroke-linejoin="round" data-pcb-component-id="pcb_component_0" data-pcb-silkscreen-path-id="pcb_silkscreen_path_2"/><text x="0" y="0" dx="0" dy="0" fill="#f2eda1" font-family="Arial, sans-serif" font-size="27.68166089965398" text-anchor="middle" dominant-baseline="central" transform="matrix(1,0,0,1,538.4083044982699,215.5709342560554)" class="pcb-silkscreen-text pcb-silkscreen-top" data-pcb-silkscreen-text-id="pcb_component_1" stroke="none">C1</text><text x="0" y="0" dx="0" dy="0" fill="#f2eda1" font-family="Arial, sans-serif" font-size="101.73010380622837" text-anchor="middle" dominant-baseline="central" transform="matrix(1,0,0,1,261.5916955017301,219.03114186851218)" class="pcb-silkscreen-text pcb-silkscreen-top" data-pcb-silkscreen-text-id="pcb_component_0" stroke="none">R1</text></svg>
@@ -1,75 +0,0 @@
1
- import { CircuitRunner } from "lib/runner/CircuitRunner"
2
- import { getPlatformConfig } from "lib/getPlatformConfig"
3
- import { expect, test } from "bun:test"
4
- import { convertCircuitJsonToPcbSvg } from "circuit-to-svg"
5
-
6
- test("example19-service-like-browser-preview", async () => {
7
- // Simulate the service code pattern with platform config
8
- const worker = new CircuitRunner({
9
- platform: getPlatformConfig(), // This is the key - pass platform config
10
- })
11
-
12
- // Simulate user code that uses kicad footprints
13
- const userCode = `
14
- export default () => (
15
- <board>
16
- <resistor name="R1" resistance="1k" footprint="kicad:Resistor_SMD.pretty/R_0402_1005Metric" pcbX={-2} />
17
- <capacitor name="C1" capacitance="100uF" footprint="0402" pcbX={2} />
18
- <trace from=".R1 > .pin2" to=".C1 > .pin1" />
19
- </board>
20
- )
21
- `
22
-
23
- // Execute with fsMap similar to service code
24
- await worker.executeWithFsMap({
25
- fsMap: {
26
- "entrypoint.tsx": `
27
- import UserComponents from "./UserCode.tsx";
28
-
29
- const hasBoard = ${userCode.includes("<board").toString()};
30
-
31
- circuit.add(
32
- hasBoard ? (
33
- <UserComponents />
34
- ) : (
35
- <board>
36
- <UserComponents name="U1" />
37
- </board>
38
- )
39
- );
40
- `,
41
- "UserCode.tsx": userCode,
42
- },
43
- entrypoint: "entrypoint.tsx",
44
- })
45
-
46
- await worker.renderUntilSettled()
47
-
48
- const circuitJson = await worker.getCircuitJson()
49
-
50
- // Verify the circuit was created successfully
51
- expect(circuitJson).toBeDefined()
52
- expect(Array.isArray(circuitJson)).toBe(true)
53
- expect(circuitJson.length).toBeGreaterThan(0)
54
-
55
- // Check that we have the expected elements
56
- const pcb_trace = circuitJson.filter((el: any) => el.type === "pcb_trace")
57
- expect(pcb_trace).toBeDefined()
58
- expect(pcb_trace.length).toBe(1)
59
-
60
- // Check that kicad footprint was processed (should have footprint elements)
61
- const footprintElements = circuitJson.filter(
62
- (el: any) =>
63
- el.type === "pcb_silkscreen_text" ||
64
- el.type === "pcb_pad" ||
65
- el.type === "pcb_silkscreen_line",
66
- )
67
- expect(footprintElements.length).toBeGreaterThan(0)
68
-
69
- // Generate SVG to verify visual output
70
- expect(convertCircuitJsonToPcbSvg(circuitJson)).toMatchSvgSnapshot(
71
- import.meta.path,
72
- )
73
-
74
- await worker.kill()
75
- })