@bun-native/cli 0.33.0 → 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.
@@ -3,50 +3,48 @@ import { mkdir, mkdtemp, rm } from 'node:fs/promises'
3
3
  import { join, resolve } from 'node:path'
4
4
  import { pathToFileURL } from 'node:url'
5
5
  import { parseArgs } from 'node:util'
6
- import { prebuiltHost } from './native/host.ts'
7
- import { createProject, readProject } from './native/project.ts'
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.
8
9
 
9
10
  const repository = resolve(import.meta.dir, '..')
10
- const usage = `Bun Native application tooling
11
+ const usage = (cli: string) => `Bun Native application tooling
11
12
 
12
- bun run native create apps/my-app [--framework solid|react] [--language typescript|javascript]
13
+ ${cli} create apps/my-app [--framework solid|react] [--language typescript|javascript]
13
14
  bun scripts/native.ts create /path/to/my-app --packages /path/to/package-set/manifest.json
14
- bun run native dev apps/my-app --host /absolute/path/to/bun-native-host|auto [--watch]
15
- bun run native build apps/my-app --host /absolute/path/to/bun-native-host|auto --host-profile dev|release [--outdir directory] [--bundle-id reverse.dns.id]
16
- bun run native check apps/my-app
17
- bun run native format apps/my-app [--check]
18
- bun run native lint apps/my-app
19
- bun run native doctor [apps/my-app] [--host /absolute/path/to/bun-native-host|auto]
20
- bun run native host install [--version x.y.z] [--force]
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]
21
22
 
22
- Projects use checkout dependencies unless --packages selects a standalone local package set.
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.
23
25
  Standalone scripts use their own installed CLI. Native hosts are supplied explicitly: an absolute path,
24
- or auto for this checkout version's released host (linux-x64, linux-arm64, darwin-arm64, darwin-x64).
26
+ or auto for this CLI version's released host (linux-x64, linux-arm64, darwin-arm64, darwin-x64).
25
27
  dev starts once; --watch remounts the app after edits and resets state. build produces unsigned same-platform AOT artifacts.
26
28
  check runs Bun's built-in type checker, formatter check and the pinned oxlint; format applies the formatter.
27
29
  lint runs only the pinned oxlint, never on vendored packages or generated output.
28
30
  doctor reports Bun, platform, host-header and project compatibility without launching anything.
29
31
  host install and --host auto download the unsigned release host once, check it against the release's
30
- SHA256SUMS and reuse it from .bun-native/hosts. No other command downloads a host; none runs Cargo.
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.
31
33
  See docs/developer-tooling.md.`
32
34
 
33
- async function resolveHost(value: string | undefined): Promise<string> {
34
- if (value !== 'auto') return prebuiltHost(value)
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
+ }
35
40
  const { installReleaseHost } = await import('./native/host-download.ts')
36
- return installReleaseHost({ version: await checkoutVersion(), cache: hostCache })
37
- }
38
-
39
- const hostCache = join(repository, '.bun-native', 'hosts')
40
-
41
- async function checkoutVersion(): Promise<string> {
42
- const manifest: { version?: unknown } = await Bun.file(join(repository, 'package.json')).json()
43
- if (typeof manifest.version !== 'string') throw new Error('The checkout has no workspace version')
44
- return manifest.version
41
+ return installReleaseHost({ version: releaseVersion(context), cache: context.hostCache })
45
42
  }
46
43
 
47
44
  async function main(args: string[]): Promise<void> {
45
+ const context = await cliContext(repository)
48
46
  if (args.length === 0 || (args.length === 1 && ['--help', '-h'].includes(args[0]!))) {
49
- console.log(usage)
47
+ console.log(usage(context.command))
50
48
  return
51
49
  }
52
50
  const [command, ...rest] = args
@@ -67,16 +65,25 @@ async function main(args: string[]): Promise<void> {
67
65
  throw new Error('--framework must be solid or react')
68
66
  if (values.language !== 'typescript' && values.language !== 'javascript')
69
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')
70
76
  const path = await createProject(
71
77
  repository,
72
78
  positionals[0]!,
73
79
  values.framework,
74
80
  values.language,
75
- values.packages
81
+ values.packages,
82
+ registry
76
83
  )
77
84
  console.log(`Created ${values.framework} ${values.language} project: ${path}`)
78
85
  console.log(
79
- values.packages
86
+ values.packages || registry
80
87
  ? 'Follow the generated README to install the pinned dependencies, then run check and dev with a prebuilt host.'
81
88
  : 'Run its check script, then dev with --host pointing to a prebuilt native host.'
82
89
  )
@@ -91,16 +98,20 @@ async function main(args: string[]): Promise<void> {
91
98
  })
92
99
  if (positionals.length !== 1)
93
100
  throw new Error(`${command} requires exactly one project directory`)
94
- const project = await readProject(repository, positionals[0]!)
101
+ const { readProject } = await import('./native/project.ts')
102
+ const project = await readProject(repository, positionals[0]!, { verification: 'stamp' })
95
103
  const { checkProject, formatProject } = await import('./native/quality.ts')
96
- const steps = []
97
- if (command === 'format') steps.push(formatProject(project, values.check === true))
98
- else {
99
- if (command === 'check') steps.push(...checkProject(project))
100
- // Only check and lint load the CLI's pinned oxlint; format never depends on it.
101
- const { lintProject } = await import('./native/lint.ts')
102
- steps.push(lintProject(project))
103
- }
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()
104
115
  for (const step of steps) {
105
116
  if (step.output) console.log(step.output)
106
117
  console.log(`${step.name}: ${step.exitCode === 0 ? 'passed' : 'failed'}`)
@@ -123,11 +134,11 @@ async function main(args: string[]): Promise<void> {
123
134
  options: { version: { type: 'string' }, force: { type: 'boolean' } },
124
135
  })
125
136
  if (positionals.length !== 1 || positionals[0] !== 'install')
126
- throw new Error('Usage: bun run native host install [--version x.y.z] [--force]')
137
+ throw new Error(`Usage: ${context.command} host install [--version x.y.z] [--force]`)
127
138
  const { installReleaseHost } = await import('./native/host-download.ts')
128
139
  const host = await installReleaseHost({
129
- version: values.version ?? (await checkoutVersion()),
130
- cache: hostCache,
140
+ version: releaseVersion(context, values.version),
141
+ cache: context.hostCache,
131
142
  force: values.force === true,
132
143
  })
133
144
  console.log(host)
@@ -142,7 +153,7 @@ async function main(args: string[]): Promise<void> {
142
153
  })
143
154
  if (positionals.length > 1) throw new Error('doctor accepts at most one project directory')
144
155
  const { diagnose } = await import('./native/doctor.ts')
145
- const host = values.host === 'auto' ? await resolveHost('auto') : values.host
156
+ const host = values.host === 'auto' ? await resolveHost(context, 'auto') : values.host
146
157
  const results = await diagnose(repository, { host, project: positionals[0] })
147
158
  for (const result of results)
148
159
  console.log(`${result.status.padEnd(7)} ${result.name}: ${result.detail}`)
@@ -150,7 +161,7 @@ async function main(args: string[]): Promise<void> {
150
161
  return
151
162
  }
152
163
  if (command !== 'dev' && command !== 'build')
153
- throw new Error(`Unknown command: ${command}\n${usage}`)
164
+ throw new Error(`Unknown command: ${command}\n${usage(context.command)}`)
154
165
  const options: Record<string, { type: 'string' | 'boolean' }> = { host: { type: 'string' } }
155
166
  if (command === 'build') {
156
167
  options['outdir'] = { type: 'string' }
@@ -164,8 +175,15 @@ async function main(args: string[]): Promise<void> {
164
175
  options,
165
176
  })
166
177
  if (positionals.length !== 1) throw new Error(`${command} requires exactly one project directory`)
167
- const host = await resolveHost(typeof values['host'] === 'string' ? values['host'] : undefined)
168
- const project = await readProject(repository, positionals[0]!)
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
+ })
169
187
  if (command === 'dev' && values.watch) {
170
188
  const { watchProject } = await import('./native/watch.ts')
171
189
  await watchProject(repository, project.directory, host)
@@ -5,7 +5,13 @@
5
5
  "revision": "b1007f1177fd402fae67387b8548cae5ae9b5707",
6
6
  "cliRevision": "1.4.4-canary.1+b1007f117",
7
7
  "source": "https://github.com/oven-sh/bun/tree/b1007f1177fd402fae67387b8548cae5ae9b5707",
8
- "compileArguments": ["compile", "--no-script", "--production"],
8
+ "compileArguments": [
9
+ "--no-install",
10
+ "compile",
11
+ "--no-script",
12
+ "--production",
13
+ "--explain=json"
14
+ ],
9
15
  "distribution": "npm",
10
16
  "packages": {
11
17
  "linux-x64": {
@@ -13,7 +19,11 @@
13
19
  "version": "1.4.4-canary.20261105.1+b1007f1",
14
20
  "url": "https://registry.npmjs.org/@oven/bun-linux-x64/-/bun-linux-x64-1.4.4-canary.20261105.1.tgz",
15
21
  "integrity": "sha512-eJqVFA3brmF4UPBynqjPbQEKvId1TvdThVuYUmtP4J/VVs42iKOd6M7mfp4DnRd4sOqY7FClDaPf04J4tun5Gw==",
16
- "unpackedSize": 140680988
22
+ "unpackedSize": 140680988,
23
+ "executable": {
24
+ "bytes": 140680448,
25
+ "sha256": "8f55345e8a878f2b4f9b233184ffa424fdd67667fa70c16b1f11a1ae8387e9ab"
26
+ }
17
27
  },
18
28
  "linux-arm64": {
19
29
  "name": "@oven/bun-linux-aarch64",
@@ -1,43 +0,0 @@
1
- import { mkdir, realpath, stat } from 'node:fs/promises'
2
- import { isAbsolute, resolve } from 'node:path'
3
- import { transformSolid } from './solid/transform.ts'
4
-
5
- /** Build-time JSX conversion only; the generated module is shared by dev and AOT. */
6
- export async function buildSolid(entry: string, outdir: string): Promise<string> {
7
- const source = await realpath(entry)
8
- if (!(await stat(source)).isFile() || !/\.[jt]sx?$/.test(source))
9
- throw new Error('Solid entry must be a TypeScript/JavaScript source file')
10
- const destination = resolve(outdir)
11
- await mkdir(destination, { recursive: true })
12
- const result = await Bun.build({
13
- entrypoints: [source],
14
- outdir: destination,
15
- naming: 'application.js',
16
- target: 'bun',
17
- format: 'esm',
18
- conditions: ['browser'],
19
- define: { 'process.env.NODE_ENV': '"production"' },
20
- sourcemap: 'external',
21
- plugins: [
22
- {
23
- name: 'solid-native-universal',
24
- setup(build) {
25
- build.onLoad({ filter: /\.[jt]sx$/ }, async ({ path }) => ({
26
- contents: transformSolid(await Bun.file(path).text(), path),
27
- loader: 'ts',
28
- }))
29
- },
30
- },
31
- ],
32
- })
33
- if (!result.success) throw new AggregateError(result.logs, 'Solid native build failed')
34
- return resolve(destination, 'application.js')
35
- }
36
-
37
- if (import.meta.main) {
38
- const entry = process.argv[2]
39
- const outdir = process.argv[3]
40
- if (!entry || !outdir || process.argv.length !== 4 || !isAbsolute(outdir))
41
- throw new Error('Usage: bun scripts/solid-build.ts ENTRY ABSOLUTE_OUTPUT_DIRECTORY')
42
- console.log(await buildSolid(entry, outdir))
43
- }