create-deka-app 0.0.3 → 0.0.5
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/package.json +1 -1
- package/src/cli.js +7 -1
- package/src/package-manager.js +10 -4
- package/src/scaffold.js +96 -5
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -17,11 +17,17 @@ 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
26
|
ownVersion = ownPackageJson.version,
|
|
21
27
|
} = {}) {
|
|
22
28
|
const targetArg = argv[0]
|
|
23
29
|
try {
|
|
24
|
-
return createApp({ targetArg, cwd, env,
|
|
30
|
+
return createApp({ targetArg, cwd, env, log })
|
|
25
31
|
} catch (err) {
|
|
26
32
|
if (err instanceof ScaffoldError) {
|
|
27
33
|
error(err.message)
|
package/src/package-manager.js
CHANGED
|
@@ -8,11 +8,17 @@
|
|
|
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).
|
|
11
17
|
const MANAGERS = {
|
|
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']] },
|
|
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}` },
|
|
16
22
|
}
|
|
17
23
|
|
|
18
24
|
export function detectPackageManager(env = process.env) {
|
package/src/scaffold.js
CHANGED
|
@@ -29,10 +29,49 @@ function dekaBinPath(targetDir, platform) {
|
|
|
29
29
|
return path.join(targetDir, 'node_modules', '.bin', platform === 'win32' ? 'deka.cmd' : 'deka')
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
// `deka init`'s own "next steps" block always starts with a line beginning
|
|
33
|
+
// "[init]" (verified against the real binary: `[init] DekaScript app
|
|
34
|
+
// ready` followed by 1-2 indented command lines). Everything from that
|
|
35
|
+
// line onward is deka's suggestion for what to run next, and we always
|
|
36
|
+
// replace it with our own -- see printNextSteps below for why. Everything
|
|
37
|
+
// before it (the banner, the per-file `[create] ...` lines) is left
|
|
38
|
+
// intact so the user still sees the scaffold happen.
|
|
39
|
+
function stripDekaNextSteps(output) {
|
|
40
|
+
if (!output) return ''
|
|
41
|
+
const lines = String(output).split(/\r?\n/)
|
|
42
|
+
const cutIndex = lines.findIndex((line) => line.startsWith('[init]'))
|
|
43
|
+
return cutIndex === -1 ? String(output) : lines.slice(0, cutIndex).join('\n')
|
|
44
|
+
}
|
|
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) {
|
|
56
|
+
log('')
|
|
57
|
+
log(' Next steps:')
|
|
58
|
+
log(` cd ${dirName}`)
|
|
59
|
+
log(` ${pm.runScript('dev')}`)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export const RUNTIME_PACKAGE = '@dekaruntime/deka'
|
|
63
|
+
|
|
32
64
|
// Builds the package.json this package writes into every scaffolded
|
|
33
65
|
// project. Exported so the test suite can assert on it directly without
|
|
34
66
|
// re-deriving the shape.
|
|
35
|
-
|
|
67
|
+
//
|
|
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).
|
|
74
|
+
export function buildPackageJson(dirName, runtimeVersion) {
|
|
36
75
|
return {
|
|
37
76
|
name: sanitizePackageName(dirName),
|
|
38
77
|
private: true,
|
|
@@ -43,11 +82,41 @@ export function buildPackageJson(dirName, ownVersion) {
|
|
|
43
82
|
start: 'deka start',
|
|
44
83
|
},
|
|
45
84
|
devDependencies: {
|
|
46
|
-
|
|
85
|
+
[RUNTIME_PACKAGE]: runtimeVersion,
|
|
47
86
|
},
|
|
48
87
|
}
|
|
49
88
|
}
|
|
50
89
|
|
|
90
|
+
// 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.
|
|
93
|
+
//
|
|
94
|
+
// Always shells out to `npm` for this lookup (not the detected package
|
|
95
|
+
// manager) -- npm ships with every Node.js install, so it is available
|
|
96
|
+
// regardless of whether the user ran this via npx, pnpm create, yarn
|
|
97
|
+
// create or bun create, and `npm view` is a read-only registry query with
|
|
98
|
+
// no project-local side effects.
|
|
99
|
+
//
|
|
100
|
+
// Falls back to the "latest" dist-tag -- never to a version we already
|
|
101
|
+
// know is wrong -- if the registry can't be reached (offline, registry
|
|
102
|
+
// outage, etc), and says so via `log` so the fallback is visible.
|
|
103
|
+
export function resolveRuntimeVersion({ spawn = spawnSync, cwd = process.cwd(), log = console.log } = {}) {
|
|
104
|
+
const result = spawn('npm', ['view', RUNTIME_PACKAGE, 'version'], { cwd, encoding: 'utf8' })
|
|
105
|
+
|
|
106
|
+
const version =
|
|
107
|
+
result && !result.error && result.status === 0 ? String(result.stdout || '').trim() : ''
|
|
108
|
+
|
|
109
|
+
if (!version) {
|
|
110
|
+
log(
|
|
111
|
+
`> Could not resolve the latest ${RUNTIME_PACKAGE} version from the npm registry ` +
|
|
112
|
+
'(offline, or the registry is unreachable); pinning "latest" instead.'
|
|
113
|
+
)
|
|
114
|
+
return 'latest'
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return version
|
|
118
|
+
}
|
|
119
|
+
|
|
51
120
|
/**
|
|
52
121
|
* Orchestrates the scaffold. Everything the real `deka init` binary is
|
|
53
122
|
* responsible for (writing app/, public/, deka.json, deka.lock, printing
|
|
@@ -61,7 +130,6 @@ export function createApp({
|
|
|
61
130
|
targetArg,
|
|
62
131
|
cwd = process.cwd(),
|
|
63
132
|
env = process.env,
|
|
64
|
-
ownVersion,
|
|
65
133
|
platform = process.platform,
|
|
66
134
|
arch = process.arch,
|
|
67
135
|
spawn = spawnSync,
|
|
@@ -96,7 +164,9 @@ export function createApp({
|
|
|
96
164
|
mkdirSync(targetDir, { recursive: true })
|
|
97
165
|
}
|
|
98
166
|
|
|
99
|
-
const
|
|
167
|
+
const runtimeVersion = resolveRuntimeVersion({ spawn, cwd: targetDir, log })
|
|
168
|
+
|
|
169
|
+
const pkg = buildPackageJson(path.basename(targetDir), runtimeVersion)
|
|
100
170
|
writeFileSync(path.join(targetDir, 'package.json'), `${JSON.stringify(pkg, null, 2)}\n`)
|
|
101
171
|
|
|
102
172
|
const pm = detectPackageManager(env)
|
|
@@ -128,17 +198,38 @@ export function createApp({
|
|
|
128
198
|
)
|
|
129
199
|
}
|
|
130
200
|
|
|
131
|
-
|
|
201
|
+
// Invoked as `deka init <dirName>` from the *parent* directory -- the
|
|
202
|
+
// same shape as someone typing `deka init myapp` by hand -- rather than
|
|
203
|
+
// `deka init` with cwd already set to targetDir. deka init never
|
|
204
|
+
// overwrites a file that already exists (package.json, written above,
|
|
205
|
+
// survives untouched), and this shape is what makes deka's own output
|
|
206
|
+
// reference the right directory name if any of it leaks through.
|
|
207
|
+
const dirName = path.basename(targetDir)
|
|
208
|
+
const parentDir = path.dirname(targetDir)
|
|
209
|
+
const init = spawn(dekaBin, ['init', dirName], {
|
|
210
|
+
cwd: parentDir,
|
|
211
|
+
stdio: ['inherit', 'pipe', 'pipe'],
|
|
212
|
+
encoding: 'utf8',
|
|
213
|
+
})
|
|
132
214
|
|
|
133
215
|
if (init.error) {
|
|
134
216
|
throw new ScaffoldError(`Could not run "deka init" in ${targetDir}: ${init.error.message}`)
|
|
135
217
|
}
|
|
136
218
|
if (init.status !== 0) {
|
|
219
|
+
// Something went wrong -- show everything deka printed, unfiltered,
|
|
220
|
+
// so the real error is visible. Filtering only ever happens below, on
|
|
221
|
+
// the success path, where we know exactly what we're throwing away.
|
|
222
|
+
if (init.stdout) log(String(init.stdout))
|
|
223
|
+
if (init.stderr) log(String(init.stderr))
|
|
137
224
|
throw new ScaffoldError(
|
|
138
225
|
`"deka init" failed in ${targetDir} (exit code ${init.status}).`,
|
|
139
226
|
init.status ?? 1
|
|
140
227
|
)
|
|
141
228
|
}
|
|
142
229
|
|
|
230
|
+
if (init.stdout) log(stripDekaNextSteps(init.stdout))
|
|
231
|
+
if (init.stderr) log(stripDekaNextSteps(init.stderr))
|
|
232
|
+
printNextSteps(log, dirName, pm)
|
|
233
|
+
|
|
143
234
|
return 0
|
|
144
235
|
}
|