@bun-native/cli 0.0.0-stage → 0.33.1

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.
Files changed (36) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +4 -2
  3. package/bun-native-source.json +151 -0
  4. package/package.json +63 -4
  5. package/src/.oxfmtrc.json +20 -0
  6. package/src/.oxlintrc.json +12 -0
  7. package/src/experiments/aot-gpui/compile.ts +44 -0
  8. package/src/experiments/aot-gpui/inspect-runtime.ts +304 -0
  9. package/src/experiments/aot-gpui/inspect.ts +66 -0
  10. package/src/scripts/native/artifact.ts +73 -0
  11. package/src/scripts/native/build.ts +231 -0
  12. package/src/scripts/native/bundle.ts +121 -0
  13. package/src/scripts/native/consumer-files.ts +46 -0
  14. package/src/scripts/native/context.ts +95 -0
  15. package/src/scripts/native/doctor.ts +69 -0
  16. package/src/scripts/native/host-download.ts +179 -0
  17. package/src/scripts/native/host-header.ts +33 -0
  18. package/src/scripts/native/host.ts +22 -0
  19. package/src/scripts/native/inputs.ts +95 -0
  20. package/src/scripts/native/lint.ts +43 -0
  21. package/src/scripts/native/package-archive.ts +94 -0
  22. package/src/scripts/native/package-lock.ts +220 -0
  23. package/src/scripts/native/package-set.ts +417 -0
  24. package/src/scripts/native/package-stamp.ts +209 -0
  25. package/src/scripts/native/paths.ts +20 -0
  26. package/src/scripts/native/project.ts +271 -0
  27. package/src/scripts/native/quality.ts +76 -0
  28. package/src/scripts/native/registry-project.ts +90 -0
  29. package/src/scripts/native/session.ts +299 -0
  30. package/src/scripts/native/standalone.ts +64 -0
  31. package/src/scripts/native/ustar.ts +29 -0
  32. package/src/scripts/native/watch.ts +104 -0
  33. package/src/scripts/native.ts +237 -0
  34. package/src/scripts/solid/transform.ts +16 -0
  35. package/src/toolchain.json +74 -0
  36. package/src/tsconfig.json +45 -0
@@ -0,0 +1,237 @@
1
+ #!/usr/bin/env bun
2
+ import { mkdir, mkdtemp, rm } from 'node:fs/promises'
3
+ import { join, resolve } from 'node:path'
4
+ import { pathToFileURL } from 'node:url'
5
+ import { parseArgs } from 'node:util'
6
+ import { cliContext, releaseVersion, type CliContext } from './native/context.ts'
7
+ // Command modules load lazily: --help and the light commands never pay for the
8
+ // package-set, archive and hashing modules they do not use.
9
+
10
+ const repository = resolve(import.meta.dir, '..')
11
+ const usage = (cli: string) => `Bun Native application tooling
12
+
13
+ ${cli} create apps/my-app [--framework solid|react] [--language typescript|javascript]
14
+ bun scripts/native.ts create /path/to/my-app --packages /path/to/package-set/manifest.json
15
+ ${cli} dev apps/my-app --host /absolute/path/to/bun-native-host|auto [--watch]
16
+ ${cli} build apps/my-app --host /absolute/path/to/bun-native-host|auto --host-profile dev|release [--outdir directory] [--bundle-id reverse.dns.id]
17
+ ${cli} check apps/my-app
18
+ ${cli} format apps/my-app [--check]
19
+ ${cli} lint apps/my-app
20
+ ${cli} doctor [apps/my-app] [--host /absolute/path/to/bun-native-host|auto]
21
+ ${cli} host install [--version x.y.z] [--force]
22
+
23
+ In a checkout, projects use checkout dependencies; an npm-installed CLI creates projects on its own
24
+ @bun-native release. --packages selects a standalone local package set instead.
25
+ Standalone scripts use their own installed CLI. Native hosts are supplied explicitly: an absolute path,
26
+ or auto for this CLI version's released host (linux-x64, linux-arm64, darwin-arm64, darwin-x64).
27
+ dev starts once; --watch remounts the app after edits and resets state. build produces unsigned same-platform AOT artifacts.
28
+ check runs Bun's built-in type checker, formatter check and the pinned oxlint; format applies the formatter.
29
+ lint runs only the pinned oxlint, never on vendored packages or generated output.
30
+ doctor reports Bun, platform, host-header and project compatibility without launching anything.
31
+ host install and --host auto download the unsigned release host once, check it against the release's
32
+ SHA256SUMS and reuse it from the checkout's .bun-native/hosts or the user cache. No other command downloads a host; none runs Cargo.
33
+ See docs/developer-tooling.md.`
34
+
35
+ async function resolveHost(context: CliContext, value: string | undefined): Promise<string> {
36
+ if (value !== 'auto') {
37
+ const { prebuiltHost } = await import('./native/host.ts')
38
+ return prebuiltHost(value)
39
+ }
40
+ const { installReleaseHost } = await import('./native/host-download.ts')
41
+ return installReleaseHost({ version: releaseVersion(context), cache: context.hostCache })
42
+ }
43
+
44
+ async function main(args: string[]): Promise<void> {
45
+ const context = await cliContext(repository)
46
+ if (args.length === 0 || (args.length === 1 && ['--help', '-h'].includes(args[0]!))) {
47
+ console.log(usage(context.command))
48
+ return
49
+ }
50
+ const [command, ...rest] = args
51
+ if (command === 'create') {
52
+ const { values, positionals } = parseArgs({
53
+ args: rest,
54
+ allowPositionals: true,
55
+ strict: true,
56
+ options: {
57
+ framework: { type: 'string', default: 'solid' },
58
+ language: { type: 'string', default: 'typescript' },
59
+ packages: { type: 'string' },
60
+ },
61
+ })
62
+ if (positionals.length !== 1)
63
+ throw new Error('create requires exactly one new project directory')
64
+ if (values.framework !== 'solid' && values.framework !== 'react')
65
+ throw new Error('--framework must be solid or react')
66
+ if (values.language !== 'typescript' && values.language !== 'javascript')
67
+ throw new Error('--language must be typescript or javascript')
68
+ // An npm-installed CLI creates a project on the same registry release.
69
+ const registry =
70
+ values.packages === undefined && context.mode === 'package'
71
+ ? (await (await import('./native/registry-project.ts')).installedTemplates(repository))[
72
+ values.framework
73
+ ]
74
+ : undefined
75
+ const { createProject } = await import('./native/project.ts')
76
+ const path = await createProject(
77
+ repository,
78
+ positionals[0]!,
79
+ values.framework,
80
+ values.language,
81
+ values.packages,
82
+ registry
83
+ )
84
+ console.log(`Created ${values.framework} ${values.language} project: ${path}`)
85
+ console.log(
86
+ values.packages || registry
87
+ ? 'Follow the generated README to install the pinned dependencies, then run check and dev with a prebuilt host.'
88
+ : 'Run its check script, then dev with --host pointing to a prebuilt native host.'
89
+ )
90
+ return
91
+ }
92
+ if (command === 'check' || command === 'format' || command === 'lint') {
93
+ const { values, positionals } = parseArgs({
94
+ args: rest,
95
+ allowPositionals: true,
96
+ strict: true,
97
+ options: command === 'format' ? { check: { type: 'boolean' } } : {},
98
+ })
99
+ if (positionals.length !== 1)
100
+ throw new Error(`${command} requires exactly one project directory`)
101
+ const { readProject } = await import('./native/project.ts')
102
+ const project = await readProject(repository, positionals[0]!, { verification: 'stamp' })
103
+ const { checkProject, formatProject } = await import('./native/quality.ts')
104
+ // The steps run concurrently; their reports print in this fixed order.
105
+ const steps =
106
+ command === 'format'
107
+ ? [await formatProject(project, values.check === true)]
108
+ : (
109
+ await Promise.all([
110
+ command === 'check' ? checkProject(project) : [],
111
+ // Only check and lint load the CLI's pinned oxlint; format never depends on it.
112
+ import('./native/lint.ts').then(({ lintProject }) => lintProject(project)),
113
+ ])
114
+ ).flat()
115
+ for (const step of steps) {
116
+ if (step.output) console.log(step.output)
117
+ console.log(`${step.name}: ${step.exitCode === 0 ? 'passed' : 'failed'}`)
118
+ }
119
+ if (steps.some((step) => step.exitCode !== 0)) {
120
+ const formatting = steps.find((step) => step.name === 'format')
121
+ if (formatting && formatting.exitCode !== 0 && (command === 'check' || values.check === true))
122
+ console.error(
123
+ 'Apply formatting with the project format script or the native format command.'
124
+ )
125
+ process.exitCode = 1
126
+ }
127
+ return
128
+ }
129
+ if (command === 'host') {
130
+ const { values, positionals } = parseArgs({
131
+ args: rest,
132
+ allowPositionals: true,
133
+ strict: true,
134
+ options: { version: { type: 'string' }, force: { type: 'boolean' } },
135
+ })
136
+ if (positionals.length !== 1 || positionals[0] !== 'install')
137
+ throw new Error(`Usage: ${context.command} host install [--version x.y.z] [--force]`)
138
+ const { installReleaseHost } = await import('./native/host-download.ts')
139
+ const host = await installReleaseHost({
140
+ version: releaseVersion(context, values.version),
141
+ cache: context.hostCache,
142
+ force: values.force === true,
143
+ })
144
+ console.log(host)
145
+ return
146
+ }
147
+ if (command === 'doctor') {
148
+ const { values, positionals } = parseArgs({
149
+ args: rest,
150
+ allowPositionals: true,
151
+ strict: true,
152
+ options: { host: { type: 'string' } },
153
+ })
154
+ if (positionals.length > 1) throw new Error('doctor accepts at most one project directory')
155
+ const { diagnose } = await import('./native/doctor.ts')
156
+ const host = values.host === 'auto' ? await resolveHost(context, 'auto') : values.host
157
+ const results = await diagnose(repository, { host, project: positionals[0] })
158
+ for (const result of results)
159
+ console.log(`${result.status.padEnd(7)} ${result.name}: ${result.detail}`)
160
+ if (results.some((result) => result.status === 'failed')) process.exitCode = 1
161
+ return
162
+ }
163
+ if (command !== 'dev' && command !== 'build')
164
+ throw new Error(`Unknown command: ${command}\n${usage(context.command)}`)
165
+ const options: Record<string, { type: 'string' | 'boolean' }> = { host: { type: 'string' } }
166
+ if (command === 'build') {
167
+ options['outdir'] = { type: 'string' }
168
+ options['host-profile'] = { type: 'string' }
169
+ options['bundle-id'] = { type: 'string' }
170
+ } else options['watch'] = { type: 'boolean' }
171
+ const { values, positionals } = parseArgs({
172
+ args: rest,
173
+ allowPositionals: true,
174
+ strict: true,
175
+ options,
176
+ })
177
+ if (positionals.length !== 1) throw new Error(`${command} requires exactly one project directory`)
178
+ const host = await resolveHost(
179
+ context,
180
+ typeof values['host'] === 'string' ? values['host'] : undefined
181
+ )
182
+ const { readProject } = await import('./native/project.ts')
183
+ // A build always re-verifies the package set it ships; dev may reuse the stamp.
184
+ const project = await readProject(repository, positionals[0]!, {
185
+ verification: command === 'build' ? 'full' : 'stamp',
186
+ })
187
+ if (command === 'dev' && values.watch) {
188
+ const { watchProject } = await import('./native/watch.ts')
189
+ await watchProject(repository, project.directory, host)
190
+ return
191
+ }
192
+ const { buildProject, bundleProject } = await import('./native/build.ts')
193
+ if (command === 'build') {
194
+ const profile = values['host-profile']
195
+ if (profile !== 'dev' && profile !== 'release')
196
+ throw new Error(
197
+ 'build requires --host-profile dev or release (declared provenance, not a verified build profile)'
198
+ )
199
+ const outdir = resolve(
200
+ typeof values.outdir === 'string' ? values.outdir : join(project.directory, 'dist')
201
+ )
202
+ const bundleIdentifier = values['bundle-id']
203
+ await buildProject(
204
+ project,
205
+ host,
206
+ outdir,
207
+ profile,
208
+ typeof bundleIdentifier === 'string' ? { bundleIdentifier } : {}
209
+ )
210
+ console.log(`Built unsigned AOT application and supplied host in ${outdir}`)
211
+ return
212
+ }
213
+ const cache = join(project.directory, '.bun-native')
214
+ await mkdir(cache, { recursive: true })
215
+ const directory = await mkdtemp(join(cache, 'dev-'))
216
+ const originalHost = process.env['BUN_NATIVE_HOST']
217
+ try {
218
+ const entry = await bundleProject(project, directory)
219
+ process.env['BUN_NATIVE_HOST'] = host
220
+ console.log(`Starting ${project.framework} application with prebuilt host ${host}`)
221
+ await import(pathToFileURL(entry).href)
222
+ } finally {
223
+ if (originalHost === undefined) delete process.env['BUN_NATIVE_HOST']
224
+ else process.env['BUN_NATIVE_HOST'] = originalHost
225
+ await rm(directory, { recursive: true, force: true })
226
+ }
227
+ }
228
+
229
+ if (import.meta.main) {
230
+ try {
231
+ await main(process.argv.slice(2))
232
+ } catch (error) {
233
+ console.error(error instanceof Error ? error.message : String(error))
234
+ if (error instanceof AggregateError) for (const detail of error.errors) console.error(detail)
235
+ process.exitCode = 1
236
+ }
237
+ }
@@ -0,0 +1,16 @@
1
+ import { transformSync } from '@babel/core'
2
+ import solid from 'babel-preset-solid'
3
+
4
+ /** Official Solid universal transform; project/Babel configuration cannot select DOM output. */
5
+ export function transformSolid(source: string, filename: string): string {
6
+ const result = transformSync(source, {
7
+ filename,
8
+ babelrc: false,
9
+ configFile: false,
10
+ sourceMaps: false,
11
+ parserOpts: { plugins: ['typescript', 'jsx'] },
12
+ presets: [[solid, { generate: 'universal', moduleName: '@bun-native/solid', dev: false }]],
13
+ })
14
+ if (!result?.code) throw new Error('Solid JSX transform produced no application code')
15
+ return result.code
16
+ }
@@ -0,0 +1,74 @@
1
+ {
2
+ "schemaVersion": 2,
3
+ "bun": {
4
+ "version": "1.4.4-canary.1",
5
+ "revision": "b1007f1177fd402fae67387b8548cae5ae9b5707",
6
+ "cliRevision": "1.4.4-canary.1+b1007f117",
7
+ "source": "https://github.com/oven-sh/bun/tree/b1007f1177fd402fae67387b8548cae5ae9b5707",
8
+ "compileArguments": [
9
+ "--no-install",
10
+ "compile",
11
+ "--no-script",
12
+ "--production",
13
+ "--explain=json"
14
+ ],
15
+ "distribution": "npm",
16
+ "packages": {
17
+ "linux-x64": {
18
+ "name": "@oven/bun-linux-x64",
19
+ "version": "1.4.4-canary.20261105.1+b1007f1",
20
+ "url": "https://registry.npmjs.org/@oven/bun-linux-x64/-/bun-linux-x64-1.4.4-canary.20261105.1.tgz",
21
+ "integrity": "sha512-eJqVFA3brmF4UPBynqjPbQEKvId1TvdThVuYUmtP4J/VVs42iKOd6M7mfp4DnRd4sOqY7FClDaPf04J4tun5Gw==",
22
+ "unpackedSize": 140680988,
23
+ "executable": {
24
+ "bytes": 140680448,
25
+ "sha256": "8f55345e8a878f2b4f9b233184ffa424fdd67667fa70c16b1f11a1ae8387e9ab"
26
+ }
27
+ },
28
+ "linux-arm64": {
29
+ "name": "@oven/bun-linux-aarch64",
30
+ "version": "1.4.4-canary.20261105.1+b1007f1",
31
+ "url": "https://registry.npmjs.org/@oven/bun-linux-aarch64/-/bun-linux-aarch64-1.4.4-canary.20261105.1.tgz",
32
+ "integrity": "sha512-6ASK2HEbe9/DK7KUXVjRifcQUNY6Cykz/JP8Z1uoNOMlUlh7NeC5pWJA3qWsaWvB/sy3Ozl0fUNNaT9xt31ebw==",
33
+ "unpackedSize": 142145540
34
+ },
35
+ "darwin-x64": {
36
+ "name": "@oven/bun-darwin-x64",
37
+ "version": "1.4.4-canary.20261105.1+b1007f1",
38
+ "url": "https://registry.npmjs.org/@oven/bun-darwin-x64/-/bun-darwin-x64-1.4.4-canary.20261105.1.tgz",
39
+ "integrity": "sha512-QJLJxX78hR/rZTY/j/yQJ2Baf/x4m74HzDG5Z3ePGWM9f8gCIbVFd17KHEvH7F4VbrGZF+sLAyH/WXzVMkGuvA==",
40
+ "unpackedSize": 134455089
41
+ },
42
+ "darwin-arm64": {
43
+ "name": "@oven/bun-darwin-aarch64",
44
+ "version": "1.4.4-canary.20261105.1+b1007f1",
45
+ "url": "https://registry.npmjs.org/@oven/bun-darwin-aarch64/-/bun-darwin-aarch64-1.4.4-canary.20261105.1.tgz",
46
+ "integrity": "sha512-qEQgT/3BrHUFchVvCzEMIw/SrQTR+9waTTQo/FLdyOt5OiFUdTlKrHDjIS9w9XMK+b6UdA6iVJs+B2VVnWv6iw==",
47
+ "unpackedSize": 123685464
48
+ },
49
+ "win32-x64": {
50
+ "name": "@oven/bun-windows-x64",
51
+ "version": "1.4.4-canary.20261105.1+b1007f1",
52
+ "url": "https://registry.npmjs.org/@oven/bun-windows-x64/-/bun-windows-x64-1.4.4-canary.20261105.1.tgz",
53
+ "integrity": "sha512-cy0DX5IP29NyjhNvPyOYqlsThwJlaKOp/3YnT2W9CojTzgQm9leZHmNQEIu/Kbip4M3LTEWd6gtVL73HvCoR6Q==",
54
+ "unpackedSize": 149885955
55
+ },
56
+ "win32-arm64": {
57
+ "name": "@oven/bun-windows-aarch64",
58
+ "version": "1.4.4-canary.20261105.1+b1007f1",
59
+ "url": "https://registry.npmjs.org/@oven/bun-windows-aarch64/-/bun-windows-aarch64-1.4.4-canary.20261105.1.tgz",
60
+ "integrity": "sha512-CP7pwT7C7LzhiTBnPPzMpA9DQgP2fZBLuCweSJStZTuu3ig3qzwI3gvqZcInPD7ZtTLZ6oSXGCkSqBMlMpgJBw==",
61
+ "unpackedSize": 133801483
62
+ }
63
+ }
64
+ },
65
+ "rust": {
66
+ "version": "1.98.1",
67
+ "components": ["rustfmt", "clippy"]
68
+ },
69
+ "gpui": {
70
+ "repository": "https://github.com/zed-industries/zed",
71
+ "revision": "ba63a022c9a22ca66d68ef2a8f0bb9b5b0a9984d",
72
+ "version": "0.2.2"
73
+ }
74
+ }
@@ -0,0 +1,45 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2024",
4
+ "lib": ["ES2024"],
5
+ "module": "Preserve",
6
+ "moduleResolution": "Bundler",
7
+ "allowImportingTsExtensions": true,
8
+ "allowJs": true,
9
+ "checkJs": true,
10
+ "verbatimModuleSyntax": true,
11
+ "strict": true,
12
+ "noUncheckedIndexedAccess": true,
13
+ "exactOptionalPropertyTypes": true,
14
+ "useUnknownInCatchVariables": true,
15
+ "noImplicitOverride": true,
16
+ "noFallthroughCasesInSwitch": true,
17
+ "noUnusedLocals": true,
18
+ "noUnusedParameters": true,
19
+ "skipLibCheck": true,
20
+ "noEmit": true,
21
+ "types": ["bun"],
22
+ "jsx": "preserve",
23
+ "jsxImportSource": "@bun-native/solid"
24
+ },
25
+ "include": [
26
+ "packages/**/*.ts",
27
+ "scripts/**/*.ts",
28
+ "scripts/**/*.mjs",
29
+ "tests/**/*.ts",
30
+ "examples/**/*.ts",
31
+ "benchmarks/**/*.ts",
32
+ "tests/native/javascript.js",
33
+ "experiments/**/*.ts",
34
+ "packages/**/*.tsx",
35
+ "examples/**/*.tsx",
36
+ "tests/**/*.tsx",
37
+ "benchmarks/**/*.tsx"
38
+ ],
39
+ "exclude": [
40
+ "**/dist/**",
41
+ "**/node_modules/**",
42
+ "tests/fixtures/**",
43
+ "benchmarks/electrobun/app/**"
44
+ ]
45
+ }