@lexibase/create-rdx 4.1.0 → 4.2.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.
- package/README.md +11 -0
- package/bin/index.js +101 -64
- package/bin/installed-runtime.mjs +32 -0
- package/bin/pnpm-progress.mjs +193 -0
- package/bin/progress.mjs +26 -0
- package/package.json +1 -1
- package/templates/default/README.md +2 -1
- package/templates/default/docs/init/intro.mdx +1 -1
- package/templates/default/package.json +1 -1
- package/templates/default/rdx.config.ts +2 -1
package/README.md
CHANGED
|
@@ -39,6 +39,17 @@ npm create @lexibase/rdx@latest my-docs -- --skip-install
|
|
|
39
39
|
`.rdx/`. Otherwise the creator uses the invoking package manager and prepares
|
|
40
40
|
the runtime. Follow the commands printed by the generator.
|
|
41
41
|
|
|
42
|
+
The creator reports file creation, dependency installation, and runtime preparation
|
|
43
|
+
as separate stages, with elapsed time and periodic activity messages during long
|
|
44
|
+
operations. During pnpm installation, RDX shows a rotating activity indicator and
|
|
45
|
+
a bar based on the number of packages linked. Resolution, cache and download
|
|
46
|
+
counts come from pnpm's structured logs; the bar waits for a known package total.
|
|
47
|
+
Warnings, errors and lifecycle output remain visible. Logs without a terminal use
|
|
48
|
+
plain status lines instead of animation. npm and Yarn retain their native output.
|
|
49
|
+
npm and pnpm projects
|
|
50
|
+
prepare using their installed RDX executable directly; Yarn provides its project
|
|
51
|
+
execution environment for Plug'n'Play compatibility.
|
|
52
|
+
|
|
42
53
|
## Recovery
|
|
43
54
|
|
|
44
55
|
In a pnpm workspace, installation uses the workspace's existing dependencies and
|
package/bin/index.js
CHANGED
|
@@ -9,6 +9,9 @@ import prompts from 'prompts'
|
|
|
9
9
|
|
|
10
10
|
import { parseArguments, validateProjectName } from './options.mjs'
|
|
11
11
|
import { normalizePnpmRdxApprovals } from './pnpm-policy.mjs'
|
|
12
|
+
import { installedRuntime } from './installed-runtime.mjs'
|
|
13
|
+
import { runStep } from './progress.mjs'
|
|
14
|
+
import { createPnpmProgress } from './pnpm-progress.mjs'
|
|
12
15
|
|
|
13
16
|
function getPackageManager() {
|
|
14
17
|
const userAgent = process.env.npm_config_user_agent || ''
|
|
@@ -19,7 +22,7 @@ function getPackageManager() {
|
|
|
19
22
|
return 'npm'
|
|
20
23
|
}
|
|
21
24
|
|
|
22
|
-
function run(command, args, cwd) {
|
|
25
|
+
function run(command, args, cwd, label = `${command} ${args.join(' ')}`) {
|
|
23
26
|
return new Promise((resolve, reject) => {
|
|
24
27
|
// Reuse the Node entry point when available. Otherwise cross-spawn
|
|
25
28
|
// resolves Windows command shims and escapes their arguments.
|
|
@@ -30,15 +33,29 @@ function run(command, args, cwd) {
|
|
|
30
33
|
/\.[cm]?js$/i.test(packageManagerEntry)
|
|
31
34
|
const executable = useNodeEntry ? process.execPath : command
|
|
32
35
|
const commandArgs = useNodeEntry ? [packageManagerEntry, ...args] : args
|
|
36
|
+
const progress =
|
|
37
|
+
command === 'pnpm' && args[0] === 'install'
|
|
38
|
+
? createPnpmProgress()
|
|
39
|
+
: undefined
|
|
40
|
+
if (progress) commandArgs.push('--reporter=ndjson')
|
|
33
41
|
const child = spawn(executable, commandArgs, {
|
|
34
42
|
cwd,
|
|
35
|
-
stdio: 'inherit',
|
|
43
|
+
stdio: progress ? ['inherit', 'pipe', 'pipe'] : 'inherit',
|
|
44
|
+
windowsHide: true,
|
|
36
45
|
})
|
|
37
46
|
|
|
38
|
-
|
|
39
|
-
|
|
47
|
+
if (progress) {
|
|
48
|
+
child.stdout.setEncoding('utf8').on('data', progress.stdout)
|
|
49
|
+
child.stderr.setEncoding('utf8').on('data', progress.stderr)
|
|
50
|
+
}
|
|
51
|
+
child.on('error', (error) => {
|
|
52
|
+
progress?.finish(false)
|
|
53
|
+
reject(error)
|
|
54
|
+
})
|
|
55
|
+
child.on('close', (code) => {
|
|
56
|
+
progress?.finish(code === 0)
|
|
40
57
|
if (code === 0) resolve()
|
|
41
|
-
else reject(new Error(`${
|
|
58
|
+
else reject(new Error(`${label} exited with ${code}`))
|
|
42
59
|
})
|
|
43
60
|
})
|
|
44
61
|
}
|
|
@@ -93,73 +110,93 @@ async function main() {
|
|
|
93
110
|
if (relativeTarget.startsWith('..') || path.isAbsolute(relativeTarget)) {
|
|
94
111
|
throw new Error('Project must be created inside the current directory.')
|
|
95
112
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
113
|
+
await runStep(
|
|
114
|
+
`[1/${skipInstall ? 1 : 3}] Creating project files...`,
|
|
115
|
+
async () => {
|
|
116
|
+
try {
|
|
117
|
+
await fs.mkdir(targetDir)
|
|
118
|
+
} catch (error) {
|
|
119
|
+
if (error.code === 'EEXIST')
|
|
120
|
+
throw new Error(
|
|
121
|
+
`The path "${projectName}" already exists. Choose another name; existing files are never overwritten.`
|
|
122
|
+
)
|
|
123
|
+
throw error
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const currentFile = fileURLToPath(import.meta.url)
|
|
127
|
+
const templateDir = path.resolve(
|
|
128
|
+
path.dirname(currentFile),
|
|
129
|
+
'../templates/default'
|
|
130
|
+
)
|
|
131
|
+
await fs.copy(templateDir, targetDir, {
|
|
132
|
+
errorOnExist: true,
|
|
133
|
+
overwrite: false,
|
|
134
|
+
})
|
|
135
|
+
await fs.ensureDir(path.join(targetDir, 'public'))
|
|
136
|
+
|
|
137
|
+
const configPath = path.join(targetDir, 'rdx.config.ts')
|
|
138
|
+
const config = await fs.readFile(configPath, 'utf8')
|
|
139
|
+
await fs.writeFile(
|
|
140
|
+
configPath,
|
|
141
|
+
config.replace(
|
|
142
|
+
'since: new Date().getFullYear(),',
|
|
143
|
+
`since: ${new Date().getFullYear()},`
|
|
144
|
+
)
|
|
102
145
|
)
|
|
103
|
-
throw error
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const currentFile = fileURLToPath(import.meta.url)
|
|
107
|
-
const templateDir = path.resolve(
|
|
108
|
-
path.dirname(currentFile),
|
|
109
|
-
'../templates/default'
|
|
110
|
-
)
|
|
111
|
-
await fs.copy(templateDir, targetDir, {
|
|
112
|
-
errorOnExist: true,
|
|
113
|
-
overwrite: false,
|
|
114
|
-
})
|
|
115
|
-
await fs.ensureDir(path.join(targetDir, 'public'))
|
|
116
|
-
|
|
117
|
-
const configPath = path.join(targetDir, 'rdx.config.ts')
|
|
118
|
-
const config = await fs.readFile(configPath, 'utf8')
|
|
119
|
-
await fs.writeFile(
|
|
120
|
-
configPath,
|
|
121
|
-
config.replace(
|
|
122
|
-
'since: new Date().getFullYear(),',
|
|
123
|
-
`since: ${new Date().getFullYear()},`
|
|
124
|
-
)
|
|
125
|
-
)
|
|
126
146
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
147
|
+
const gitignoreTemplatePath = path.join(targetDir, 'gitignore.template')
|
|
148
|
+
await fs.move(gitignoreTemplatePath, path.join(targetDir, '.gitignore'), {
|
|
149
|
+
overwrite: true,
|
|
150
|
+
})
|
|
151
|
+
await fs.remove(path.join(targetDir, 'gitignore'))
|
|
152
|
+
await fs.remove(path.join(targetDir, '.npmignore'))
|
|
153
|
+
|
|
154
|
+
const pkgPath = path.join(targetDir, 'package.json')
|
|
155
|
+
const pkg = await fs.readJson(pkgPath)
|
|
156
|
+
pkg.name = projectName.toLowerCase()
|
|
157
|
+
await fs.writeJson(pkgPath, pkg, { spaces: 2 })
|
|
158
|
+
|
|
159
|
+
const readmePath = path.join(targetDir, 'README.md')
|
|
160
|
+
const readme = await fs.readFile(readmePath, 'utf8')
|
|
161
|
+
await fs.writeFile(
|
|
162
|
+
readmePath,
|
|
163
|
+
readme
|
|
164
|
+
.replaceAll('{{RDX_PROJECT_NAME}}', projectName)
|
|
165
|
+
.replaceAll('__RDX_PROJECT_NAME__', projectName)
|
|
166
|
+
)
|
|
167
|
+
}
|
|
146
168
|
)
|
|
147
169
|
|
|
148
170
|
const packageManager = getPackageManager()
|
|
149
171
|
|
|
150
172
|
if (!skipInstall) {
|
|
151
173
|
try {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
174
|
+
await runStep(
|
|
175
|
+
`[2/3] Installing dependencies with ${packageManager}...`,
|
|
176
|
+
async () => {
|
|
177
|
+
if (packageManager === 'pnpm')
|
|
178
|
+
await normalizePnpmRdxApprovals(targetDir)
|
|
179
|
+
await run(packageManager, ['install'], targetDir)
|
|
180
|
+
if (packageManager === 'pnpm')
|
|
181
|
+
await normalizePnpmRdxApprovals(targetDir)
|
|
182
|
+
},
|
|
183
|
+
{ intervalMs: packageManager === 'pnpm' ? 0 : 15_000 }
|
|
184
|
+
)
|
|
185
|
+
await runStep('[3/3] Preparing RDX...', async () => {
|
|
186
|
+
if (packageManager === 'yarn') {
|
|
187
|
+
// Yarn provides the project's Plug'n'Play loader when needed.
|
|
188
|
+
await run(packageManager, ['exec', 'rdx', 'prepare'], targetDir)
|
|
189
|
+
} else {
|
|
190
|
+
// Avoid another package-manager startup and automatic install check.
|
|
191
|
+
const executable = await installedRuntime(targetDir)
|
|
192
|
+
await run(
|
|
193
|
+
process.execPath,
|
|
194
|
+
[executable, 'prepare'],
|
|
195
|
+
targetDir,
|
|
196
|
+
'rdx prepare'
|
|
197
|
+
)
|
|
198
|
+
}
|
|
199
|
+
})
|
|
163
200
|
} catch (error) {
|
|
164
201
|
const runner = packageManager === 'npm' ? 'npm run' : packageManager
|
|
165
202
|
throw new Error(
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { createRequire } from 'node:module'
|
|
2
|
+
import { readFile, stat } from 'node:fs/promises'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
|
|
5
|
+
/** Resolve the framework installed in the new project, including hoisted workspaces. */
|
|
6
|
+
export async function installedRuntime(directory) {
|
|
7
|
+
const require = createRequire(path.join(directory, 'package.json'))
|
|
8
|
+
let filename
|
|
9
|
+
try {
|
|
10
|
+
filename = require.resolve('@lexibase/rdx/package.json')
|
|
11
|
+
} catch {
|
|
12
|
+
throw new Error(
|
|
13
|
+
'Could not find @lexibase/rdx in the new project after installation.'
|
|
14
|
+
)
|
|
15
|
+
}
|
|
16
|
+
const manifest = JSON.parse(await readFile(filename, 'utf8'))
|
|
17
|
+
const entry =
|
|
18
|
+
typeof manifest.bin === 'string' ? manifest.bin : manifest.bin?.rdx
|
|
19
|
+
if (typeof entry !== 'string' || !entry)
|
|
20
|
+
throw new Error(
|
|
21
|
+
'The installed @lexibase/rdx package does not provide its rdx executable.'
|
|
22
|
+
)
|
|
23
|
+
const executable = path.resolve(path.dirname(filename), entry)
|
|
24
|
+
try {
|
|
25
|
+
if (!(await stat(executable)).isFile()) throw new Error()
|
|
26
|
+
} catch {
|
|
27
|
+
throw new Error(
|
|
28
|
+
'The installed RDX executable is missing. Run the package manager install command again.'
|
|
29
|
+
)
|
|
30
|
+
}
|
|
31
|
+
return executable
|
|
32
|
+
}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { performance } from 'node:perf_hooks'
|
|
2
|
+
import process from 'node:process'
|
|
3
|
+
|
|
4
|
+
/** Adapt pnpm's NDJSON logs; the bar measures package linking, not elapsed time. */
|
|
5
|
+
export function createPnpmProgress({
|
|
6
|
+
output = process.stdout,
|
|
7
|
+
errors = process.stderr,
|
|
8
|
+
interactive = Boolean(output.isTTY) && process.env.TERM !== 'dumb',
|
|
9
|
+
intervalMs = 100,
|
|
10
|
+
heartbeatMs = 15_000,
|
|
11
|
+
now = () => performance.now(),
|
|
12
|
+
} = {}) {
|
|
13
|
+
const started = now()
|
|
14
|
+
const counts = { resolved: 0, cached: 0, downloaded: 0, linked: 0 }
|
|
15
|
+
const statuses = {
|
|
16
|
+
resolved: 'resolved',
|
|
17
|
+
found_in_store: 'cached',
|
|
18
|
+
fetched: 'downloaded',
|
|
19
|
+
imported: 'linked',
|
|
20
|
+
}
|
|
21
|
+
const totals = new Map()
|
|
22
|
+
const seen = new Set()
|
|
23
|
+
let phase = 'Resolving dependencies'
|
|
24
|
+
let buffer = ''
|
|
25
|
+
let frame = 0
|
|
26
|
+
let finished = false
|
|
27
|
+
let promptOpen = false
|
|
28
|
+
let previousPhase
|
|
29
|
+
const elapsed = () => `${((now() - started) / 1000).toFixed(1)}s`
|
|
30
|
+
const clear = () => {
|
|
31
|
+
if (interactive) output.write('\r\x1b[2K')
|
|
32
|
+
}
|
|
33
|
+
function render() {
|
|
34
|
+
const total = totals.size
|
|
35
|
+
? [...totals.values()].reduce((sum, value) => sum + value, 0)
|
|
36
|
+
: undefined
|
|
37
|
+
const width = output.columns || 100
|
|
38
|
+
const cells = width < 100 ? 10 : 16
|
|
39
|
+
const filled =
|
|
40
|
+
total > 0
|
|
41
|
+
? Math.min(cells, Math.floor((counts.linked / total) * cells))
|
|
42
|
+
: 0
|
|
43
|
+
const bar = `[${'#'.repeat(filled)}${'-'.repeat(cells - filled)}]`
|
|
44
|
+
const packages =
|
|
45
|
+
total === undefined ? `${counts.linked}/?` : `${counts.linked}/${total}`
|
|
46
|
+
const detail = `${width >= 110 ? `resolved ${counts.resolved}, ` : ''}cached ${counts.cached}, downloaded ${counts.downloaded}`
|
|
47
|
+
const shortPhase = phase.split(' ')[0]
|
|
48
|
+
const text =
|
|
49
|
+
width < 70
|
|
50
|
+
? `${packages} packages | ${elapsed()}`
|
|
51
|
+
: `${shortPhase} ${packages} | ${elapsed()} | ${detail}`
|
|
52
|
+
return `${['|', '/', '-', '\\'][frame++ % 4]} ${bar} ${text}`.slice(
|
|
53
|
+
0,
|
|
54
|
+
Math.max(1, width - 1)
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
function draw(force = false) {
|
|
58
|
+
if (finished || promptOpen) return
|
|
59
|
+
if (interactive) {
|
|
60
|
+
// pnpm emits many debug records per package. Paint on the timer instead
|
|
61
|
+
// of turning each record into terminal writes during large installations.
|
|
62
|
+
if (!force && intervalMs > 0 && previousPhase === phase) return
|
|
63
|
+
clear()
|
|
64
|
+
output.write(render())
|
|
65
|
+
} else if (force || previousPhase !== phase) {
|
|
66
|
+
output.write(
|
|
67
|
+
`[RDX] ${phase}: resolved ${counts.resolved}, cached ${counts.cached}, downloaded ${counts.downloaded}, linked ${counts.linked} (${elapsed()}).\n`
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
previousPhase = phase
|
|
71
|
+
}
|
|
72
|
+
function message(text, stream = output) {
|
|
73
|
+
if (!promptOpen) clear()
|
|
74
|
+
stream.write(text)
|
|
75
|
+
// Keep prompts without a newline visible and leave the cursor after them.
|
|
76
|
+
promptOpen = !text.endsWith('\n')
|
|
77
|
+
if (text.endsWith('\n')) draw(true)
|
|
78
|
+
}
|
|
79
|
+
function record(line) {
|
|
80
|
+
if (!line.trim()) {
|
|
81
|
+
if (promptOpen) message('\n')
|
|
82
|
+
return
|
|
83
|
+
}
|
|
84
|
+
let event
|
|
85
|
+
try {
|
|
86
|
+
event = JSON.parse(line)
|
|
87
|
+
} catch {
|
|
88
|
+
message(line + '\n')
|
|
89
|
+
return
|
|
90
|
+
}
|
|
91
|
+
if (
|
|
92
|
+
!event ||
|
|
93
|
+
typeof event !== 'object' ||
|
|
94
|
+
typeof event.name !== 'string' ||
|
|
95
|
+
!(event.name === 'pnpm' || event.name.startsWith('pnpm:'))
|
|
96
|
+
) {
|
|
97
|
+
message(line + '\n')
|
|
98
|
+
return
|
|
99
|
+
}
|
|
100
|
+
if (promptOpen) {
|
|
101
|
+
output.write('\n')
|
|
102
|
+
promptOpen = false
|
|
103
|
+
}
|
|
104
|
+
if (
|
|
105
|
+
event.name === 'pnpm:progress' &&
|
|
106
|
+
Object.hasOwn(statuses, event.status)
|
|
107
|
+
) {
|
|
108
|
+
const key = statuses[event.status]
|
|
109
|
+
const id = event.to || event.packageId
|
|
110
|
+
const identity = id && JSON.stringify([event.requester, event.status, id])
|
|
111
|
+
if (!identity || !seen.has(identity)) {
|
|
112
|
+
counts[key]++
|
|
113
|
+
if (identity) seen.add(identity)
|
|
114
|
+
}
|
|
115
|
+
if (key === 'linked') phase = 'Linking packages'
|
|
116
|
+
} else if (
|
|
117
|
+
event.name === 'pnpm:fetching-progress' &&
|
|
118
|
+
event.status === 'started' &&
|
|
119
|
+
event.size >= 1024 * 1024
|
|
120
|
+
) {
|
|
121
|
+
message(
|
|
122
|
+
`[RDX] Downloading ${event.packageId} (${(event.size / 1024 / 1024).toFixed(1)} MB).\n`
|
|
123
|
+
)
|
|
124
|
+
} else if (event.name === 'pnpm:stats' && Number.isFinite(event.added)) {
|
|
125
|
+
totals.set(event.prefix || '', Math.max(0, event.added))
|
|
126
|
+
} else if (event.name === 'pnpm:stage') {
|
|
127
|
+
if (event.stage === 'importing_started') phase = 'Linking packages'
|
|
128
|
+
if (event.stage === 'importing_done') phase = 'Finalizing installation'
|
|
129
|
+
} else if (event.name === 'pnpm:lifecycle' && event.line) {
|
|
130
|
+
message(`[pnpm:${event.stage || 'script'}] ${event.line}\n`)
|
|
131
|
+
} else if (event.level === 'warn' || event.level === 'error') {
|
|
132
|
+
const error = event.err || event
|
|
133
|
+
const text = [
|
|
134
|
+
error.code,
|
|
135
|
+
error.message || event.message,
|
|
136
|
+
error.hint,
|
|
137
|
+
error.pkgsStack && JSON.stringify(error.pkgsStack),
|
|
138
|
+
]
|
|
139
|
+
.filter(Boolean)
|
|
140
|
+
.join('\n')
|
|
141
|
+
message(`[pnpm] ${text || JSON.stringify(event)}\n`, errors)
|
|
142
|
+
} else if (event.name === 'pnpm:peer-dependency-issues') {
|
|
143
|
+
message(
|
|
144
|
+
`[pnpm] Peer dependency issues:\n${JSON.stringify(event.issuesByProjects || event, null, 2)}\n`,
|
|
145
|
+
errors
|
|
146
|
+
)
|
|
147
|
+
} else if (event.level === 'info' && event.message) {
|
|
148
|
+
message(`[pnpm] ${event.message}\n`)
|
|
149
|
+
}
|
|
150
|
+
draw()
|
|
151
|
+
}
|
|
152
|
+
draw(true)
|
|
153
|
+
const timer =
|
|
154
|
+
intervalMs > 0
|
|
155
|
+
? setInterval(() => draw(true), interactive ? intervalMs : heartbeatMs)
|
|
156
|
+
: undefined
|
|
157
|
+
timer?.unref()
|
|
158
|
+
return {
|
|
159
|
+
stdout(chunk) {
|
|
160
|
+
buffer += chunk
|
|
161
|
+
let end
|
|
162
|
+
while ((end = buffer.indexOf('\n')) !== -1) {
|
|
163
|
+
const line = buffer.slice(0, end).replace(/\r$/, '')
|
|
164
|
+
buffer = buffer.slice(end + 1)
|
|
165
|
+
record(line)
|
|
166
|
+
}
|
|
167
|
+
if (buffer && !buffer.trimStart().startsWith('{')) {
|
|
168
|
+
message(buffer)
|
|
169
|
+
buffer = ''
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
stderr(chunk) {
|
|
173
|
+
message(chunk, errors)
|
|
174
|
+
},
|
|
175
|
+
finish(success) {
|
|
176
|
+
if (finished) return
|
|
177
|
+
clearInterval(timer)
|
|
178
|
+
if (buffer) {
|
|
179
|
+
record(buffer)
|
|
180
|
+
buffer = ''
|
|
181
|
+
}
|
|
182
|
+
finished = true
|
|
183
|
+
if (promptOpen) {
|
|
184
|
+
output.write('\n')
|
|
185
|
+
promptOpen = false
|
|
186
|
+
}
|
|
187
|
+
clear()
|
|
188
|
+
output.write(
|
|
189
|
+
`[RDX] ${success ? 'Dependencies installed' : 'Installation stopped'}: ${counts.linked} linked, ${counts.cached} cached, ${counts.downloaded} downloaded (${elapsed()}).\n`
|
|
190
|
+
)
|
|
191
|
+
},
|
|
192
|
+
}
|
|
193
|
+
}
|
package/bin/progress.mjs
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { performance } from 'node:perf_hooks'
|
|
2
|
+
|
|
3
|
+
/** Show real stage timings while preserving the installer's own terminal output. */
|
|
4
|
+
export async function runStep(
|
|
5
|
+
label,
|
|
6
|
+
operation,
|
|
7
|
+
{ log = console.log, intervalMs = 15_000 } = {}
|
|
8
|
+
) {
|
|
9
|
+
const started = performance.now()
|
|
10
|
+
const elapsed = () => ((performance.now() - started) / 1000).toFixed(1)
|
|
11
|
+
log(`\n${label}`)
|
|
12
|
+
const timer =
|
|
13
|
+
intervalMs > 0
|
|
14
|
+
? setInterval(() => {
|
|
15
|
+
log(`[RDX] ${label} (${elapsed()}s elapsed)`)
|
|
16
|
+
}, intervalMs)
|
|
17
|
+
: undefined
|
|
18
|
+
timer?.unref()
|
|
19
|
+
try {
|
|
20
|
+
const result = await operation()
|
|
21
|
+
log(`[RDX] Completed in ${elapsed()}s.`)
|
|
22
|
+
return result
|
|
23
|
+
} finally {
|
|
24
|
+
clearInterval(timer)
|
|
25
|
+
}
|
|
26
|
+
}
|
package/package.json
CHANGED
|
@@ -51,7 +51,8 @@ public/images/rdx.svg → /images/rdx.svg
|
|
|
51
51
|
public/downloads/example.txt → /downloads/example.txt
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
Write pages as `.md` or `.mdx` inside `docs/`. Both formats support RDX's
|
|
55
|
+
Markdown components. Use site-root URLs for assets:
|
|
55
56
|
|
|
56
57
|
```md
|
|
57
58
|

|
|
@@ -6,5 +6,5 @@ link_position: 1
|
|
|
6
6
|
|
|
7
7
|
# Welcome to your documentation
|
|
8
8
|
|
|
9
|
-
RDX turns folders and `.mdx` files into a documentation site. Start by editing
|
|
9
|
+
RDX turns folders and `.md` or `.mdx` files into a documentation site. Start by editing
|
|
10
10
|
this page or adding new pages inside `docs/`.
|
|
@@ -32,7 +32,8 @@ export default defineConfig({
|
|
|
32
32
|
// Optional navbar text does not change metadata.applicationName.
|
|
33
33
|
navbar: {
|
|
34
34
|
title: '', // Text beside the logo; empty or omitted shows only the logo.
|
|
35
|
-
// logo:
|
|
35
|
+
// logo: '/images/logo.png', // File in public/images/; PNG, SVG and other image formats work.
|
|
36
|
+
// Or import your components/logo.tsx and set logo: Logo.
|
|
36
37
|
actions: {
|
|
37
38
|
github: {
|
|
38
39
|
visible: true,
|