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 +17 -0
- package/package.json +1 -1
- package/src/cli.js +5 -7
- package/src/package-manager.js +4 -10
- package/src/scaffold.js +104 -29
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
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.
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
//
|
|
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)
|
package/src/package-manager.js
CHANGED
|
@@ -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']]
|
|
19
|
-
pnpm: { name: 'pnpm', install: ['pnpm', ['install']]
|
|
20
|
-
yarn: { name: 'yarn', install: ['yarn', ['install']]
|
|
21
|
-
bun: { name: 'bun', install: ['bun', ['install']]
|
|
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
|
|
47
|
-
//
|
|
48
|
-
//
|
|
49
|
-
//
|
|
50
|
-
//
|
|
51
|
-
//
|
|
52
|
-
//
|
|
53
|
-
//
|
|
54
|
-
//
|
|
55
|
-
|
|
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(
|
|
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`
|
|
69
|
-
//
|
|
70
|
-
//
|
|
71
|
-
//
|
|
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
|
|
92
|
-
//
|
|
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
|
|
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
|
-
|
|
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
|
|
170
|
-
|
|
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
|
|
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
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
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
|
|
307
|
+
printNextSteps(log, dirName)
|
|
233
308
|
|
|
234
309
|
return 0
|
|
235
310
|
}
|