create-deka-app 0.0.5 → 0.53.4

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/README.md CHANGED
@@ -32,6 +32,23 @@ everything non-interactively — no prompts:
32
32
  See [deka#1091](https://github.com/dekaruntime/deka/issues/1091) for the
33
33
  design history.
34
34
 
35
+ ## Versioning
36
+
37
+ **create-deka-app's version always equals the deka runtime version it
38
+ scaffolds.** `create-deka-app@0.53.4` pins `@dekaruntime/deka@0.53.4` — not
39
+ some other version, and never its own separate `0.0.x` release line. The
40
+ two publish together, from the same CI run (`.github/workflows/publish-runtime.yml`,
41
+ the `deka` family), so a given create-deka-app version always produces the
42
+ same project: no registry lookup needed to decide the pin.
43
+
44
+ The one exception is a create-deka-app release whose exact-matching
45
+ runtime build isn't published yet (or a scaffolder-only release cut via
46
+ `publish.yml`'s `v*` tag path, which has no runtime counterpart at all).
47
+ If installing the exact pin fails, create-deka-app falls back to the
48
+ latest published `@dekaruntime/deka` version, prints a clear warning, and
49
+ never leaves a nonexistent version pinned in the generated
50
+ `package.json`.
51
+
35
52
  ## Platforms
36
53
 
37
54
  | Platform | Status |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-deka-app",
3
- "version": "0.0.5",
3
+ "version": "0.53.4",
4
4
  "description": "Scaffold a new deka app",
5
5
  "bin": {
6
6
  "create-deka-app": "./index.js"
package/src/cli.js CHANGED
@@ -17,17 +17,15 @@ export function run({
17
17
  cwd = process.cwd(),
18
18
  log = console.log,
19
19
  error = console.error,
20
- // create-deka-app's own version. Not currently used by createApp -- the
21
- // runtime version it pins in the generated package.json is resolved
22
- // separately, from the npm registry, since create-deka-app's own 0.0.x
23
- // line and @dekaruntime/deka's release line are not in lockstep. Kept
24
- // here as the CLI's own version for whatever legitimately needs it
25
- // (e.g. a future `--version` flag).
20
+ // create-deka-app's own version. Under lockstep versioning this IS the
21
+ // @dekaruntime/deka version createApp pins in the generated
22
+ // package.json (see src/scaffold.js) -- create-deka-app@X.Y.Z always
23
+ // scaffolds @dekaruntime/deka@X.Y.Z.
26
24
  ownVersion = ownPackageJson.version,
27
25
  } = {}) {
28
26
  const targetArg = argv[0]
29
27
  try {
30
- return createApp({ targetArg, cwd, env, log })
28
+ return createApp({ targetArg, cwd, env, log, ownVersion })
31
29
  } catch (err) {
32
30
  if (err instanceof ScaffoldError) {
33
31
  error(err.message)
@@ -8,17 +8,11 @@
8
8
  // arm64". `npm_execpath` is a fallback for the rarer case where that header
9
9
  // is absent but the package manager's own launcher script is still visible
10
10
  // on the path it invoked us with.
11
- // `runScript` renders the command we tell the user to run for a
12
- // package.json script (currently always "dev"). It must work with zero
13
- // global installs, using only what the install step above already put on
14
- // disk -- so it is each package manager's own idiom for running a local
15
- // script, never a bare `deka ...` invocation (deka itself only lives in
16
- // this project's node_modules/.bin).
17
11
  const MANAGERS = {
18
- npm: { name: 'npm', install: ['npm', ['install']], runScript: (script) => `npm run ${script}` },
19
- pnpm: { name: 'pnpm', install: ['pnpm', ['install']], runScript: (script) => `pnpm ${script}` },
20
- yarn: { name: 'yarn', install: ['yarn', ['install']], runScript: (script) => `yarn ${script}` },
21
- bun: { name: 'bun', install: ['bun', ['install']], runScript: (script) => `bun run ${script}` },
12
+ npm: { name: 'npm', install: ['npm', ['install']] },
13
+ pnpm: { name: 'pnpm', install: ['pnpm', ['install']] },
14
+ yarn: { name: 'yarn', install: ['yarn', ['install']] },
15
+ bun: { name: 'bun', install: ['bun', ['install']] },
22
16
  }
23
17
 
24
18
  export function detectPackageManager(env = process.env) {
package/src/scaffold.js CHANGED
@@ -43,20 +43,25 @@ function stripDekaNextSteps(output) {
43
43
  return cutIndex === -1 ? String(output) : lines.slice(0, cutIndex).join('\n')
44
44
  }
45
45
 
46
- // deka init's own next-steps text always tells the user to run a bare
47
- // `deka ...` command, because that's correct advice for someone who ran
48
- // `deka init` by hand with deka already on their PATH. It is wrong advice
49
- // here: create-deka-app only ever installs deka into this project's own
50
- // node_modules/.bin, so a bare `deka` command fails for a user who has no
51
- // global install. We suppress deka's block (stripDekaNextSteps) and print
52
- // our own -- the `cd` line plus the detected package manager's own idiom
53
- // for running the "dev" script already sitting in package.json, which
54
- // works with nothing beyond what the install step just put on disk.
55
- function printNextSteps(log, dirName, pm) {
46
+ // deka init's own next-steps text always tells the user to run `deka dev`,
47
+ // and that command is correct as-is -- `deka` from this package is scoped
48
+ // to the project, so the copy installed into node_modules/.bin (and picked
49
+ // up via the project's package.json script) works with nothing beyond what
50
+ // the install step above already put on disk. We suppress deka's block
51
+ // (stripDekaNextSteps) only because it doesn't know it's being invoked
52
+ // from the parent directory here, so it can't tell the user they still
53
+ // need to `cd <dirName>` first -- not because its command is wrong. Our
54
+ // own block adds that missing `cd` and repeats `deka dev` verbatim,
55
+ // regardless of which package manager ran the install. (This reverts an
56
+ // earlier version of this function that substituted a package-manager
57
+ // idiom -- `npm run dev` / `pnpm dev` / etc. -- for `deka dev`, on the
58
+ // mistaken premise that a bare `deka dev` can't be run without a global
59
+ // install.)
60
+ function printNextSteps(log, dirName) {
56
61
  log('')
57
62
  log(' Next steps:')
58
63
  log(` cd ${dirName}`)
59
- log(` ${pm.runScript('dev')}`)
64
+ log(' deka dev')
60
65
  }
61
66
 
62
67
  export const RUNTIME_PACKAGE = '@dekaruntime/deka'
@@ -65,12 +70,10 @@ export const RUNTIME_PACKAGE = '@dekaruntime/deka'
65
70
  // project. Exported so the test suite can assert on it directly without
66
71
  // re-deriving the shape.
67
72
  //
68
- // `runtimeVersion` must be the @dekaruntime/deka version resolved at
69
- // scaffold time (see resolveRuntimeVersion below) -- never this package's
70
- // own version. create-deka-app has its own 0.0.x release line and
71
- // @dekaruntime/deka tracks deka's releases; the two are not in lockstep
72
- // and never will be, so passing this package's own version here pins a
73
- // version of the runtime that may not exist (see deka#1076-era bug).
73
+ // `runtimeVersion` is whatever create-deka-app decided to pin -- under
74
+ // lockstep versioning (see createApp below) that is this scaffolder's own
75
+ // version in the common case, or the registry-resolved fallback version
76
+ // when the exact match hasn't been published.
74
77
  export function buildPackageJson(dirName, runtimeVersion) {
75
78
  return {
76
79
  name: sanitizePackageName(dirName),
@@ -88,8 +91,12 @@ export function buildPackageJson(dirName, runtimeVersion) {
88
91
  }
89
92
 
90
93
  // Looks up the latest published @dekaruntime/deka version from the npm
91
- // registry so the generated package.json pins something that actually
92
- // exists, rather than assuming it matches create-deka-app's own version.
94
+ // registry. This is the FALLBACK path only -- under lockstep versioning,
95
+ // create-deka-app@X.Y.Z always scaffolds @dekaruntime/deka@X.Y.Z directly
96
+ // (see resolveRuntimeVersion below), with no registry call. This function
97
+ // is reached only when that exact pin failed to install, e.g. a
98
+ // create-deka-app release that published before its matching runtime
99
+ // build finished.
93
100
  //
94
101
  // Always shells out to `npm` for this lookup (not the detected package
95
102
  // manager) -- npm ships with every Node.js install, so it is available
@@ -100,7 +107,7 @@ export function buildPackageJson(dirName, runtimeVersion) {
100
107
  // Falls back to the "latest" dist-tag -- never to a version we already
101
108
  // know is wrong -- if the registry can't be reached (offline, registry
102
109
  // outage, etc), and says so via `log` so the fallback is visible.
103
- export function resolveRuntimeVersion({ spawn = spawnSync, cwd = process.cwd(), log = console.log } = {}) {
110
+ export function resolveLatestRuntimeVersion({ spawn = spawnSync, cwd = process.cwd(), log = console.log } = {}) {
104
111
  const result = spawn('npm', ['view', RUNTIME_PACKAGE, 'version'], { cwd, encoding: 'utf8' })
105
112
 
106
113
  const version =
@@ -117,6 +124,23 @@ export function resolveRuntimeVersion({ spawn = spawnSync, cwd = process.cwd(),
117
124
  return version
118
125
  }
119
126
 
127
+ // Decides which @dekaruntime/deka version to pin in the scaffolded
128
+ // project. `ownVersion` is create-deka-app's own version -- under
129
+ // lockstep versioning (README: "create-deka-app's version always equals
130
+ // the deka runtime version it scaffolds") that IS the runtime version, so
131
+ // this returns it directly with no network call: a given create-deka-app
132
+ // version always produces the same pin, reproducibly.
133
+ //
134
+ // This is exported only so the test suite can call it directly; createApp
135
+ // below is what actually decides whether the pin needs the fallback (that
136
+ // requires attempting the install, which this function does not do).
137
+ export function resolveRuntimeVersion({ ownVersion }) {
138
+ if (!ownVersion) {
139
+ throw new Error('resolveRuntimeVersion requires ownVersion')
140
+ }
141
+ return ownVersion
142
+ }
143
+
120
144
  /**
121
145
  * Orchestrates the scaffold. Everything the real `deka init` binary is
122
146
  * responsible for (writing app/, public/, deka.json, deka.lock, printing
@@ -134,11 +158,16 @@ export function createApp({
134
158
  arch = process.arch,
135
159
  spawn = spawnSync,
136
160
  log = console.log,
161
+ ownVersion,
137
162
  } = {}) {
138
163
  if (!targetArg) {
139
164
  throw new ScaffoldError(USAGE, 1)
140
165
  }
141
166
 
167
+ if (!ownVersion) {
168
+ throw new Error('createApp requires ownVersion (create-deka-app pins it as the runtime version)')
169
+ }
170
+
142
171
  if (!isSupportedPlatform(platform, arch)) {
143
172
  throw new ScaffoldError(
144
173
  `create-deka-app does not support ${platformKey(platform, arch)} yet.\n` +
@@ -164,16 +193,33 @@ export function createApp({
164
193
  mkdirSync(targetDir, { recursive: true })
165
194
  }
166
195
 
167
- const runtimeVersion = resolveRuntimeVersion({ spawn, cwd: targetDir, log })
196
+ // Lockstep versioning: create-deka-app@X.Y.Z always pins
197
+ // @dekaruntime/deka@X.Y.Z -- this is create-deka-app's own version, so
198
+ // deciding the pin needs no network call and is fully reproducible (a
199
+ // given create-deka-app version always produces the same project).
200
+ //
201
+ // The one case this doesn't cover is a create-deka-app release whose
202
+ // exact-matching runtime build isn't published yet (or never will be --
203
+ // e.g. a scaffolder-only patch released via publish.yml, see its header
204
+ // comment). That surfaces as the install below failing, and is handled
205
+ // as a fallback: re-resolve against the registry and retry once, never
206
+ // silently leaving a nonexistent version pinned in the final
207
+ // package.json (the ETARGET bug fixed in 0.0.3).
208
+ let runtimeVersion = resolveRuntimeVersion({ ownVersion })
168
209
 
169
- const pkg = buildPackageJson(path.basename(targetDir), runtimeVersion)
170
- writeFileSync(path.join(targetDir, 'package.json'), `${JSON.stringify(pkg, null, 2)}\n`)
210
+ const writePackageJson = (version) => {
211
+ const pkg = buildPackageJson(path.basename(targetDir), version)
212
+ writeFileSync(path.join(targetDir, 'package.json'), `${JSON.stringify(pkg, null, 2)}\n`)
213
+ }
214
+ writePackageJson(runtimeVersion)
171
215
 
172
216
  const pm = detectPackageManager(env)
173
217
  log(`> Using ${pm.name} to install @dekaruntime/deka...`)
174
218
 
175
219
  const [installCmd, installArgs] = pm.install
176
- const install = spawn(installCmd, installArgs, { cwd: targetDir, stdio: 'inherit' })
220
+ const runInstall = () => spawn(installCmd, installArgs, { cwd: targetDir, stdio: 'inherit' })
221
+
222
+ let install = runInstall()
177
223
 
178
224
  if (install.error) {
179
225
  throw new ScaffoldError(
@@ -181,12 +227,41 @@ export function createApp({
181
227
  `Make sure ${pm.name} is installed and on your PATH, then run it there yourself.`
182
228
  )
183
229
  }
230
+
184
231
  if (install.status !== 0) {
185
- throw new ScaffoldError(
186
- `"${pm.name} install" failed in ${targetDir} (exit code ${install.status}).\n` +
187
- 'Run it there yourself to see the full error.',
188
- install.status ?? 1
232
+ log(
233
+ `> "${pm.name} install" could not install ${RUNTIME_PACKAGE}@${runtimeVersion} (exit code ${install.status}). ` +
234
+ `This can happen when create-deka-app@${ownVersion} shipped before its matching runtime build did. ` +
235
+ 'Falling back to the latest published version instead.'
189
236
  )
237
+ const fallbackVersion = resolveLatestRuntimeVersion({ spawn, cwd: targetDir, log })
238
+ if (fallbackVersion === runtimeVersion) {
239
+ throw new ScaffoldError(
240
+ `"${pm.name} install" failed in ${targetDir} (exit code ${install.status}), ` +
241
+ `and the npm registry also reports ${RUNTIME_PACKAGE}@${fallbackVersion} as the latest version.\n` +
242
+ 'Run it there yourself to see the full error.',
243
+ install.status ?? 1
244
+ )
245
+ }
246
+
247
+ runtimeVersion = fallbackVersion
248
+ writePackageJson(runtimeVersion)
249
+ install = runInstall()
250
+
251
+ if (install.error) {
252
+ throw new ScaffoldError(
253
+ `Could not run "${pm.name} install" in ${targetDir}: ${install.error.message}\n` +
254
+ `Make sure ${pm.name} is installed and on your PATH, then run it there yourself.`
255
+ )
256
+ }
257
+ if (install.status !== 0) {
258
+ throw new ScaffoldError(
259
+ `"${pm.name} install" failed in ${targetDir} (exit code ${install.status}), ` +
260
+ `even after falling back to ${RUNTIME_PACKAGE}@${runtimeVersion}.\n` +
261
+ 'Run it there yourself to see the full error.',
262
+ install.status ?? 1
263
+ )
264
+ }
190
265
  }
191
266
 
192
267
  const dekaBin = dekaBinPath(targetDir, platform)
@@ -229,7 +304,7 @@ export function createApp({
229
304
 
230
305
  if (init.stdout) log(stripDekaNextSteps(init.stdout))
231
306
  if (init.stderr) log(stripDekaNextSteps(init.stderr))
232
- printNextSteps(log, dirName, pm)
307
+ printNextSteps(log, dirName)
233
308
 
234
309
  return 0
235
310
  }