@lexibase/create-rdx 4.1.0 → 4.2.0

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
@@ -39,6 +39,12 @@ 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. Package-manager output and prompts stay visible. npm and pnpm projects
45
+ prepare using their installed RDX executable directly; Yarn provides its project
46
+ execution environment for Plug'n'Play compatibility.
47
+
42
48
  ## Recovery
43
49
 
44
50
  In a pnpm workspace, installation uses the workspace's existing dependencies and
package/bin/index.js CHANGED
@@ -9,6 +9,8 @@ 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'
12
14
 
13
15
  function getPackageManager() {
14
16
  const userAgent = process.env.npm_config_user_agent || ''
@@ -19,7 +21,7 @@ function getPackageManager() {
19
21
  return 'npm'
20
22
  }
21
23
 
22
- function run(command, args, cwd) {
24
+ function run(command, args, cwd, label = `${command} ${args.join(' ')}`) {
23
25
  return new Promise((resolve, reject) => {
24
26
  // Reuse the Node entry point when available. Otherwise cross-spawn
25
27
  // resolves Windows command shims and escapes their arguments.
@@ -33,12 +35,13 @@ function run(command, args, cwd) {
33
35
  const child = spawn(executable, commandArgs, {
34
36
  cwd,
35
37
  stdio: 'inherit',
38
+ windowsHide: true,
36
39
  })
37
40
 
38
41
  child.on('error', reject)
39
42
  child.on('exit', (code) => {
40
43
  if (code === 0) resolve()
41
- else reject(new Error(`${command} ${args.join(' ')} exited with ${code}`))
44
+ else reject(new Error(`${label} exited with ${code}`))
42
45
  })
43
46
  })
44
47
  }
@@ -93,73 +96,92 @@ async function main() {
93
96
  if (relativeTarget.startsWith('..') || path.isAbsolute(relativeTarget)) {
94
97
  throw new Error('Project must be created inside the current directory.')
95
98
  }
96
- try {
97
- await fs.mkdir(targetDir)
98
- } catch (error) {
99
- if (error.code === 'EEXIST')
100
- throw new Error(
101
- `The path "${projectName}" already exists. Choose another name; existing files are never overwritten.`
99
+ await runStep(
100
+ `[1/${skipInstall ? 1 : 3}] Creating project files...`,
101
+ async () => {
102
+ try {
103
+ await fs.mkdir(targetDir)
104
+ } catch (error) {
105
+ if (error.code === 'EEXIST')
106
+ throw new Error(
107
+ `The path "${projectName}" already exists. Choose another name; existing files are never overwritten.`
108
+ )
109
+ throw error
110
+ }
111
+
112
+ const currentFile = fileURLToPath(import.meta.url)
113
+ const templateDir = path.resolve(
114
+ path.dirname(currentFile),
115
+ '../templates/default'
116
+ )
117
+ await fs.copy(templateDir, targetDir, {
118
+ errorOnExist: true,
119
+ overwrite: false,
120
+ })
121
+ await fs.ensureDir(path.join(targetDir, 'public'))
122
+
123
+ const configPath = path.join(targetDir, 'rdx.config.ts')
124
+ const config = await fs.readFile(configPath, 'utf8')
125
+ await fs.writeFile(
126
+ configPath,
127
+ config.replace(
128
+ 'since: new Date().getFullYear(),',
129
+ `since: ${new Date().getFullYear()},`
130
+ )
102
131
  )
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
132
 
127
- const gitignoreTemplatePath = path.join(targetDir, 'gitignore.template')
128
- await fs.move(gitignoreTemplatePath, path.join(targetDir, '.gitignore'), {
129
- overwrite: true,
130
- })
131
- await fs.remove(path.join(targetDir, 'gitignore'))
132
- await fs.remove(path.join(targetDir, '.npmignore'))
133
-
134
- const pkgPath = path.join(targetDir, 'package.json')
135
- const pkg = await fs.readJson(pkgPath)
136
- pkg.name = projectName.toLowerCase()
137
- await fs.writeJson(pkgPath, pkg, { spaces: 2 })
138
-
139
- const readmePath = path.join(targetDir, 'README.md')
140
- const readme = await fs.readFile(readmePath, 'utf8')
141
- await fs.writeFile(
142
- readmePath,
143
- readme
144
- .replaceAll('{{RDX_PROJECT_NAME}}', projectName)
145
- .replaceAll('__RDX_PROJECT_NAME__', projectName)
133
+ const gitignoreTemplatePath = path.join(targetDir, 'gitignore.template')
134
+ await fs.move(gitignoreTemplatePath, path.join(targetDir, '.gitignore'), {
135
+ overwrite: true,
136
+ })
137
+ await fs.remove(path.join(targetDir, 'gitignore'))
138
+ await fs.remove(path.join(targetDir, '.npmignore'))
139
+
140
+ const pkgPath = path.join(targetDir, 'package.json')
141
+ const pkg = await fs.readJson(pkgPath)
142
+ pkg.name = projectName.toLowerCase()
143
+ await fs.writeJson(pkgPath, pkg, { spaces: 2 })
144
+
145
+ const readmePath = path.join(targetDir, 'README.md')
146
+ const readme = await fs.readFile(readmePath, 'utf8')
147
+ await fs.writeFile(
148
+ readmePath,
149
+ readme
150
+ .replaceAll('{{RDX_PROJECT_NAME}}', projectName)
151
+ .replaceAll('__RDX_PROJECT_NAME__', projectName)
152
+ )
153
+ }
146
154
  )
147
155
 
148
156
  const packageManager = getPackageManager()
149
157
 
150
158
  if (!skipInstall) {
151
159
  try {
152
- console.log(`\nInstalling dependencies with ${packageManager}...\n`)
153
- if (packageManager === 'pnpm')
154
- await normalizePnpmRdxApprovals(targetDir)
155
- await run(packageManager, ['install'], targetDir)
156
- if (packageManager === 'pnpm')
157
- await normalizePnpmRdxApprovals(targetDir)
158
- const prepareArgs =
159
- packageManager === 'npm'
160
- ? ['exec', '--', 'rdx', 'prepare']
161
- : ['exec', 'rdx', 'prepare']
162
- await run(packageManager, prepareArgs, targetDir)
160
+ await runStep(
161
+ `[2/3] Installing dependencies with ${packageManager}...`,
162
+ async () => {
163
+ if (packageManager === 'pnpm')
164
+ await normalizePnpmRdxApprovals(targetDir)
165
+ await run(packageManager, ['install'], targetDir)
166
+ if (packageManager === 'pnpm')
167
+ await normalizePnpmRdxApprovals(targetDir)
168
+ }
169
+ )
170
+ await runStep('[3/3] Preparing RDX...', async () => {
171
+ if (packageManager === 'yarn') {
172
+ // Yarn provides the project's Plug'n'Play loader when needed.
173
+ await run(packageManager, ['exec', 'rdx', 'prepare'], targetDir)
174
+ } else {
175
+ // Avoid another package-manager startup and automatic install check.
176
+ const executable = await installedRuntime(targetDir)
177
+ await run(
178
+ process.execPath,
179
+ [executable, 'prepare'],
180
+ targetDir,
181
+ 'rdx prepare'
182
+ )
183
+ }
184
+ })
163
185
  } catch (error) {
164
186
  const runner = packageManager === 'npm' ? 'npm run' : packageManager
165
187
  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,23 @@
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 = setInterval(() => {
13
+ log(`[RDX] ${label} (${elapsed()}s elapsed)`)
14
+ }, intervalMs)
15
+ timer.unref()
16
+ try {
17
+ const result = await operation()
18
+ log(`[RDX] Completed in ${elapsed()}s.`)
19
+ return result
20
+ } finally {
21
+ clearInterval(timer)
22
+ }
23
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lexibase/create-rdx",
3
- "version": "4.1.0",
3
+ "version": "4.2.0",
4
4
  "description": "Create a new RDX documentation project.",
5
5
  "license": "MIT",
6
6
  "bin": {
@@ -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
- Use site-root URLs in MDX:
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
  ![RDX logo](/images/rdx.svg)
@@ -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/`.
@@ -9,7 +9,7 @@
9
9
  "start": "rdx start"
10
10
  },
11
11
  "dependencies": {
12
- "@lexibase/rdx": "4.1.0",
12
+ "@lexibase/rdx": "4.2.0",
13
13
  "next": "^16.4.2",
14
14
  "react": "19.3.1",
15
15
  "react-dom": "19.3.1"
@@ -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: Logo, // Import a React component that accepts className, not an image path.
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,