@soederpop/luca 0.0.36 → 0.1.0

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.
@@ -1,7 +1,7 @@
1
1
  import { setBuildTimeData, setContainerBuildTimeData } from './index.js';
2
2
 
3
3
  // Auto-generated introspection registry data
4
- // Generated at: 2026-03-28T00:36:29.848Z
4
+ // Generated at: 2026-03-28T05:59:16.310Z
5
5
 
6
6
  setBuildTimeData('features.containerLink', {
7
7
  "id": "features.containerLink",
@@ -24,7 +24,7 @@ import { basename, parse, relative, resolve, join } from "path";
24
24
  import "./features/disk-cache";
25
25
  import "./features/content-db";
26
26
  import "./features/downloader";
27
- import "./features/esbuild";
27
+ import "./features/transpiler";
28
28
  import "./features/file-manager";
29
29
  import "./features/fs";
30
30
  import "./features/git";
@@ -69,7 +69,7 @@ import type { ChildProcess } from "./features/proc";
69
69
  import type { DiskCache } from "./features/disk-cache";
70
70
  import type { ContentDb } from "./features/content-db";
71
71
  import type { Downloader } from "./features/downloader";
72
- import type { ESBuild } from "./features/esbuild";
72
+ import type { Transpiler } from "./features/transpiler";
73
73
  import type { FileManager } from "./features/file-manager";
74
74
  import type { FS } from "./features/fs";
75
75
  import type { Git } from "./features/git";
@@ -147,6 +147,7 @@ export {
147
147
  type SemanticSearch,
148
148
  type Dns,
149
149
  type Redis,
150
+ type Transpiler,
150
151
  };
151
152
 
152
153
  export type { FeatureOptions };
@@ -184,7 +185,7 @@ export interface NodeFeatures extends AvailableFeatures {
184
185
  packageFinder: typeof PackageFinder;
185
186
  repl: typeof Repl;
186
187
  yaml: typeof YAML;
187
- esbuild: typeof ESBuild;
188
+ transpiler: typeof Transpiler;
188
189
  diskCache: typeof DiskCache;
189
190
  vault: typeof Vault;
190
191
  jsonTree: typeof JsonTree;
@@ -233,7 +234,7 @@ export interface NodeContainerState extends ContainerState {
233
234
  * Server-side container for Node.js and Bun environments. Extends the base Container with
234
235
  * file system access, process management, git integration, and other server-side capabilities.
235
236
  *
236
- * Auto-enables core features on construction: fs, proc, git, grep, os, networking, ui, vm, esbuild, helpers.
237
+ * Auto-enables core features on construction: fs, proc, git, grep, os, networking, ui, vm, transpiler, helpers.
237
238
  * Also attaches Client, Server, Command, Endpoint, and Selector helper types, providing
238
239
  * `container.client()`, `container.server()`, `container.command()`, etc. factory methods.
239
240
  *
@@ -274,7 +275,7 @@ export class NodeContainer<
274
275
  yamlTree?: YamlTree;
275
276
  packageFinder?: PackageFinder;
276
277
  repl?: Repl;
277
- esbuild?: ESBuild;
278
+ transpiler?: Transpiler;
278
279
  diskCache?: DiskCache;
279
280
  vault?: Vault;
280
281
  python?: Python;
@@ -309,7 +310,7 @@ export class NodeContainer<
309
310
  this.feature("networking", { enable: true });
310
311
  this.feature("ui", { enable: true });
311
312
  this.feature("vm", { enable: true, context: {} });
312
- this.feature("esbuild", { enable: true });
313
+ this.feature("transpiler", { enable: true });
313
314
  this.feature("helpers", { enable: true });
314
315
 
315
316
  const enable = castArray(this.options.enable)
@@ -0,0 +1,111 @@
1
+ import { Feature } from '../feature.js'
2
+ import { FeatureStateSchema, FeatureOptionsSchema } from '../../schemas/base.js'
3
+
4
+ export interface TransformOptions {
5
+ loader?: 'ts' | 'tsx' | 'jsx' | 'js'
6
+ format?: 'esm' | 'cjs'
7
+ minify?: boolean
8
+ }
9
+
10
+ export interface TransformResult {
11
+ code: string
12
+ map: string
13
+ warnings: any[]
14
+ }
15
+
16
+ /**
17
+ * Convert ESM import/export statements to CJS require/module.exports
18
+ * so the code can run in a vm context that provides `require`.
19
+ */
20
+ function esmToCjs(code: string): string {
21
+ return code
22
+ // import Foo, { bar, baz } from 'x' → const Foo = require('x').default ?? require('x'); const { bar, baz } = require('x')
23
+ .replace(/^import\s+(\w+)\s*,\s*\{([^}]+)\}\s+from\s+(['"][^'"]+['"])\s*;?$/gm,
24
+ 'const $1 = require($3).default ?? require($3); const {$2} = require($3);')
25
+ // import { a, b } from 'x' → const { a, b } = require('x')
26
+ .replace(/^import\s+\{([^}]+)\}\s+from\s+(['"][^'"]+['"])\s*;?$/gm,
27
+ 'const {$1} = require($2);')
28
+ // import x from 'y' → const x = require('y').default ?? require('y')
29
+ .replace(/^import\s+(\w+)\s+from\s+(['"][^'"]+['"])\s*;?$/gm,
30
+ 'const $1 = require($2).default ?? require($2);')
31
+ // import * as x from 'y' → const x = require('y')
32
+ .replace(/^import\s+\*\s+as\s+(\w+)\s+from\s+(['"][^'"]+['"])\s*;?$/gm,
33
+ 'const $1 = require($2);')
34
+ // import 'y' → require('y')
35
+ .replace(/^import\s+(['"][^'"]+['"])\s*;?$/gm,
36
+ 'require($1);')
37
+ // export default → module.exports.default =
38
+ .replace(/^export\s+default\s+/gm, 'module.exports.default = ')
39
+ // export { a, b } from 'x' → Object.assign(module.exports, require('x')) (re-exports)
40
+ .replace(/^export\s+\{[^}]*\}\s+from\s+(['"][^'"]+['"])\s*;?$/gm,
41
+ 'Object.assign(module.exports, require($1));')
42
+ // export { ... } → strip (vars already in scope)
43
+ .replace(/^export\s+\{[^}]*\}\s*;?$/gm, '')
44
+ // export const/let/var → const/let/var
45
+ .replace(/^export\s+(const|let|var)\s+/gm, '$1 ')
46
+ // export function → function (keep declaration, strip export keyword)
47
+ .replace(/^export\s+(function|class)\s+/gm, '$1 ')
48
+ // export async function → async function
49
+ .replace(/^export\s+(async\s+function)\s+/gm, '$1 ')
50
+ }
51
+
52
+ /**
53
+ * Transpile TypeScript, TSX, and JSX to JavaScript at runtime using Bun's
54
+ * built-in transpiler. Compile code strings on the fly without touching the
55
+ * filesystem or spawning external processes.
56
+ *
57
+ * @example
58
+ * ```typescript
59
+ * const transpiler = container.feature('transpiler')
60
+ * const result = transpiler.transformSync('const x: number = 1')
61
+ * console.log(result.code) // 'const x = 1;\n'
62
+ * ```
63
+ */
64
+ export class Transpiler extends Feature {
65
+ static override shortcut = 'features.transpiler' as const
66
+ static override stateSchema = FeatureStateSchema
67
+ static override optionsSchema = FeatureOptionsSchema
68
+ static { Feature.register(this, 'transpiler') }
69
+
70
+ /**
71
+ * Transform code synchronously
72
+ * @param code - The code to transform
73
+ * @param options - Transform options (loader, format, minify)
74
+ * @returns The transformed code as { code, map, warnings }
75
+ */
76
+ transformSync(code: string, options: TransformOptions = {}): TransformResult {
77
+ const loader = options.loader || 'ts'
78
+ const format = options.format || 'esm'
79
+
80
+ const transpiler = new Bun.Transpiler({ loader })
81
+ let result = transpiler.transformSync(code)
82
+
83
+ if (format === 'cjs') {
84
+ result = esmToCjs(result)
85
+ }
86
+
87
+ return { code: result, map: '', warnings: [] }
88
+ }
89
+
90
+ /**
91
+ * Transform code asynchronously
92
+ * @param code - The code to transform
93
+ * @param options - Transform options (loader, format, minify)
94
+ * @returns The transformed code as { code, map, warnings }
95
+ */
96
+ async transform(code: string, options: TransformOptions = {}): Promise<TransformResult> {
97
+ const loader = options.loader || 'ts'
98
+ const format = options.format || 'esm'
99
+
100
+ const transpiler = new Bun.Transpiler({ loader })
101
+ let result = await transpiler.transform(code)
102
+
103
+ if (format === 'cjs') {
104
+ result = esmToCjs(result)
105
+ }
106
+
107
+ return { code: result, map: '', warnings: [] }
108
+ }
109
+ }
110
+
111
+ export default Transpiler
@@ -390,7 +390,7 @@ export class VM<
390
390
  if (!fs.exists(filePath)) return {}
391
391
 
392
392
  const raw = fs.readFile(filePath)
393
- const { code } = this.container.feature('esbuild').transformSync(raw, { format: 'cjs' })
393
+ const { code } = this.container.feature('transpiler').transformSync(raw, { format: 'cjs' })
394
394
 
395
395
  const { context } = this.performSync(code, {
396
396
  require: this.createRequireFor(filePath),
@@ -1,5 +1,5 @@
1
1
  // Auto-generated Python bridge script
2
- // Generated at: 2026-03-28T00:36:32.420Z
2
+ // Generated at: 2026-03-28T05:59:19.077Z
3
3
  // Source: src/python/bridge.py
4
4
  //
5
5
  // Do not edit manually. Run: luca build-python-bridge
@@ -1,5 +1,5 @@
1
1
  // Auto-generated scaffold and MCP readme content
2
- // Generated at: 2026-03-28T00:36:30.792Z
2
+ // Generated at: 2026-03-28T05:59:17.268Z
3
3
  // Source: docs/scaffolds/*.md, docs/examples/assistant/, and docs/mcp/readme.md
4
4
  //
5
5
  // Do not edit manually. Run: luca build-scaffolds
@@ -618,27 +618,27 @@ describe('Integration: Proc + FS for script execution', () => {
618
618
  })
619
619
  })
620
620
 
621
- describe('Integration: ESBuild + FS for code transformation', () => {
621
+ describe('Integration: Transpiler + FS for code transformation', () => {
622
622
  it('transforms typescript to javascript', async () => {
623
623
  const c = new NodeContainer({ cwd: testDir })
624
- const esbuild = c.feature('esbuild', { enable: true })
624
+ const transpiler = c.feature('transpiler', { enable: true })
625
625
 
626
626
  const tsCode = `
627
627
  const greet = (name: string): string => 'Hello, ' + name + '!'
628
628
  export default greet
629
629
  `
630
630
 
631
- const result = await esbuild.transform(tsCode, { loader: 'ts' })
631
+ const result = await transpiler.transform(tsCode, { loader: 'ts' })
632
632
  expect(result.code).toContain('greet')
633
633
  expect(result.code).not.toContain(': string')
634
634
  })
635
635
 
636
636
  it('reads ts file from disk, transforms it, writes js output', async () => {
637
637
  const c = new NodeContainer({ cwd: testDir })
638
- const esbuild = c.feature('esbuild', { enable: true })
638
+ const transpiler = c.feature('transpiler', { enable: true })
639
639
 
640
640
  const tsSource = c.fs.readFile(join(testDir, 'src', 'utils.ts'))
641
- const result = await esbuild.transform(tsSource, { loader: 'ts' })
641
+ const result = await transpiler.transform(tsSource, { loader: 'ts' })
642
642
 
643
643
  const outPath = join(testDir, 'src', 'utils.js')
644
644
  await c.fs.writeFileAsync(outPath, result.code)
@@ -1,79 +0,0 @@
1
- import * as esbuild from 'esbuild-wasm'
2
- import { Feature } from '../feature.js'
3
- import { FeatureStateSchema, FeatureOptionsSchema } from '../../schemas/base.js'
4
-
5
- /**
6
- * A Feature for compiling typescript / esm modules, etc to JavaScript
7
- * that the container can run at runtime. Uses esbuild for fast, reliable
8
- * TypeScript/ESM transformation with full format support (esm, cjs, iife).
9
- *
10
- * @example
11
- * ```typescript
12
- * const esbuild = container.feature('esbuild')
13
- * const result = esbuild.transformSync('const x: number = 1')
14
- * console.log(result.code) // 'const x = 1;\n'
15
- * ```
16
- */
17
- export class ESBuild extends Feature {
18
- static override shortcut = 'features.esbuild' as const
19
- static override stateSchema = FeatureStateSchema
20
- static override optionsSchema = FeatureOptionsSchema
21
- static { Feature.register(this, 'esbuild') }
22
-
23
- /**
24
- /**
25
- * Transform code synchronously
26
- * @param code - The code to transform
27
- * @param options - The options to pass to esbuild
28
- * @returns The transformed code
29
- */
30
- transformSync(code: string, options?: esbuild.TransformOptions): esbuild.TransformResult {
31
- return esbuild.transformSync(code, {
32
- loader: 'ts',
33
- format: 'esm',
34
- target: 'esnext',
35
- sourcemap: false,
36
- minify: false,
37
- ...options
38
- })
39
- }
40
-
41
- /**
42
- * Transform code asynchronously
43
- * @param code - The code to transform
44
- * @param options - The options to pass to esbuild
45
- * @returns The transformed code
46
- */
47
- async transform(code: string, options?: esbuild.TransformOptions): Promise<esbuild.TransformResult> {
48
- return esbuild.transform(code, {
49
- loader: 'ts',
50
- format: 'esm',
51
- target: 'esnext',
52
- sourcemap: false,
53
- minify: false,
54
- ...options
55
- })
56
- }
57
-
58
- /**
59
- * Bundle one or more entry points, resolving imports and requires into a single output.
60
- * Supports Node platform by default so require() and Node builtins are handled.
61
- * Returns in-memory output files unless write is enabled in options.
62
- * @param entryPoints - File paths to bundle from
63
- * @param options - esbuild BuildOptions overrides
64
- * @returns The build result with outputFiles when write is false
65
- */
66
- async bundle(entryPoints: string[], options?: esbuild.BuildOptions): Promise<esbuild.BuildResult> {
67
- return esbuild.build({
68
- entryPoints,
69
- bundle: true,
70
- platform: 'node',
71
- format: 'esm',
72
- target: 'esnext',
73
- write: false,
74
- ...options
75
- })
76
- }
77
- }
78
-
79
- export default ESBuild