@lexibase/create-rdx 1.1.0 → 1.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 CHANGED
@@ -41,6 +41,12 @@ the runtime. Follow the commands printed by the generator.
41
41
 
42
42
  ## Recovery
43
43
 
44
+ In a pnpm workspace, installation uses the workspace's existing dependencies and
45
+ policy. Before installation and runtime preparation, the creator consolidates
46
+ duplicate exact-version approvals for RDX in `minimumReleaseAgeExclude`. Only
47
+ versions already listed are combined; the age threshold, strict mode, unrelated
48
+ package rules, comments and other workspace settings are preserved.
49
+
44
50
  If installation or preparation fails, the command exits with an error and keeps
45
51
  the generated files. Fix the reported issue, enter the created folder and retry
46
52
  installation/development using the same package manager. Do not rerun creation
package/bin/index.js CHANGED
@@ -8,6 +8,7 @@ import fs from 'fs-extra'
8
8
  import prompts from 'prompts'
9
9
 
10
10
  import { parseArguments, validateProjectName } from './options.mjs'
11
+ import { normalizePnpmRdxApprovals } from './pnpm-policy.mjs'
11
12
 
12
13
  function getPackageManager() {
13
14
  const userAgent = process.env.npm_config_user_agent || ''
@@ -113,6 +114,16 @@ async function main() {
113
114
  })
114
115
  await fs.ensureDir(path.join(targetDir, 'public'))
115
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
+
116
127
  const gitignoreTemplatePath = path.join(targetDir, 'gitignore.template')
117
128
  await fs.move(gitignoreTemplatePath, path.join(targetDir, '.gitignore'), {
118
129
  overwrite: true,
@@ -139,7 +150,11 @@ async function main() {
139
150
  if (!skipInstall) {
140
151
  try {
141
152
  console.log(`\nInstalling dependencies with ${packageManager}...\n`)
153
+ if (packageManager === 'pnpm')
154
+ await normalizePnpmRdxApprovals(targetDir)
142
155
  await run(packageManager, ['install'], targetDir)
156
+ if (packageManager === 'pnpm')
157
+ await normalizePnpmRdxApprovals(targetDir)
143
158
  const prepareArgs =
144
159
  packageManager === 'npm'
145
160
  ? ['exec', '--', 'rdx', 'prepare']
@@ -0,0 +1,115 @@
1
+ import { randomUUID } from 'node:crypto'
2
+ import { lstat, readFile, rename, rm, writeFile } from 'node:fs/promises'
3
+ import path from 'node:path'
4
+
5
+ import { isScalar, isSeq, parseDocument } from 'yaml'
6
+
7
+ const rdxPackages = new Set([
8
+ '@lexibase/rdx',
9
+ '@lexibase/rdx-core',
10
+ '@lexibase/rdx-next',
11
+ '@lexibase/rdx-types',
12
+ '@lexibase/rdx-ui',
13
+ '@lexibase/rdx-versioning',
14
+ '@lexibase/create-rdx',
15
+ ])
16
+ const version =
17
+ /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[\da-z.-]+)?(?:\+[\da-z.-]+)?$/i
18
+
19
+ async function workspaceFile(directory) {
20
+ let current = path.resolve(directory)
21
+ while (true) {
22
+ const filename = path.join(current, 'pnpm-workspace.yaml')
23
+ try {
24
+ const info = await lstat(filename)
25
+ if (!info.isFile() || info.isSymbolicLink()) return undefined
26
+ return filename
27
+ } catch (error) {
28
+ if (error.code !== 'ENOENT') throw error
29
+ }
30
+ const parent = path.dirname(current)
31
+ if (parent === current) return undefined
32
+ current = parent
33
+ }
34
+ }
35
+
36
+ function exactRule(value) {
37
+ if (typeof value !== 'string') return undefined
38
+ const at = value.indexOf('@', 1)
39
+ if (at < 0 || !rdxPackages.has(value.slice(0, at))) return undefined
40
+ const versions = value
41
+ .slice(at + 1)
42
+ .split('||')
43
+ .map((item) => item.trim())
44
+ if (!versions.length || !versions.every((item) => version.test(item)))
45
+ return undefined
46
+ return { name: value.slice(0, at), versions }
47
+ }
48
+
49
+ /** Merge only exact RDX approvals already recorded by the user or pnpm. */
50
+ export async function normalizePnpmRdxApprovals(directory) {
51
+ const filename = await workspaceFile(directory)
52
+ if (!filename) return false
53
+ const original = await readFile(filename, 'utf8')
54
+ const document = parseDocument(original)
55
+ if (document.errors.length)
56
+ throw new Error(`Cannot read ${filename}: ${document.errors[0].message}`)
57
+ const approvals = document.get('minimumReleaseAgeExclude', true)
58
+ if (approvals === undefined) return false
59
+ // Leave aliases and non-literal policy definitions to pnpm itself.
60
+ if (
61
+ !isSeq(approvals) ||
62
+ approvals.items.some(
63
+ (item) => !isScalar(item) || typeof item.value !== 'string'
64
+ )
65
+ )
66
+ return false
67
+
68
+ const groups = new Map()
69
+ const removed = new Set()
70
+ for (const node of approvals.items) {
71
+ const rule = exactRule(node.value)
72
+ if (!rule) continue
73
+ const first = groups.get(rule.name)
74
+ if (!first) {
75
+ groups.set(rule.name, { node, versions: new Set(rule.versions) })
76
+ continue
77
+ }
78
+ for (const value of rule.versions) first.versions.add(value)
79
+ first.node.value = `${rule.name}@${[...first.versions].join(' || ')}`
80
+ // Retain comments attached to approvals that are folded into the first rule.
81
+ if (node.commentBefore)
82
+ first.node.commentBefore = [first.node.commentBefore, node.commentBefore]
83
+ .filter(Boolean)
84
+ .join('\n')
85
+ if (node.comment)
86
+ first.node.comment = [first.node.comment, node.comment]
87
+ .filter(Boolean)
88
+ .join('\n')
89
+ removed.add(node)
90
+ }
91
+ if (!removed.size) return false
92
+ approvals.items = approvals.items.filter((node) => !removed.has(node))
93
+ let updated = document.toString({ lineWidth: 0 })
94
+ if (original.includes('\r\n')) updated = updated.replaceAll('\n', '\r\n')
95
+ const temporary = path.join(
96
+ path.dirname(filename),
97
+ `.rdx-policy-${randomUUID()}.tmp`
98
+ )
99
+ try {
100
+ const info = await lstat(filename)
101
+ await writeFile(temporary, updated, { flag: 'wx', mode: info.mode })
102
+ if (
103
+ info.isSymbolicLink() ||
104
+ (await readFile(filename, 'utf8')) !== original
105
+ )
106
+ throw new Error(
107
+ `The pnpm policy changed during project creation: ${filename}. Retry without concurrent installs.`
108
+ )
109
+ await rename(temporary, filename)
110
+ } finally {
111
+ await rm(temporary, { force: true })
112
+ }
113
+ console.log(`Consolidated existing RDX version approvals in ${filename}.`)
114
+ return true
115
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lexibase/create-rdx",
3
- "version": "1.1.0",
3
+ "version": "1.2.1",
4
4
  "description": "Create a new RDX documentation project.",
5
5
  "license": "MIT",
6
6
  "bin": {
@@ -30,7 +30,8 @@
30
30
  "dependencies": {
31
31
  "cross-spawn": "^7.0.6",
32
32
  "fs-extra": "^11.1.1",
33
- "prompts": "^2.4.2"
33
+ "prompts": "^2.4.2",
34
+ "yaml": "^2.9.1"
34
35
  },
35
36
  "scripts": {
36
37
  "test": "node --test tests/*.test.mjs"
@@ -9,7 +9,7 @@
9
9
  "start": "rdx start"
10
10
  },
11
11
  "dependencies": {
12
- "@lexibase/rdx": "1.1.0",
12
+ "@lexibase/rdx": "1.2.0",
13
13
  "next": "^16.4.2",
14
14
  "react": "19.3.1",
15
15
  "react-dom": "19.3.1"
@@ -2,10 +2,12 @@ import { defineConfig } from '@lexibase/rdx'
2
2
 
3
3
  export default defineConfig({
4
4
  description: 'Documentation built with RDX.',
5
- // SEO and link previews. Paths starting with '/' refer to files in public/.
5
+ // Site identity, SEO and link previews. '/' paths refer to files in public/.
6
6
  metadata: {
7
7
  // The single site name: browser titles, social previews and default branding.
8
8
  applicationName: 'My documentation',
9
+ // Footer starting year. Change it to an earlier founding year if needed.
10
+ since: new Date().getFullYear(),
9
11
  // Set metadataBase to your public site URL to resolve relative share images.
10
12
  // Optional examples below: uncomment only after adding the referenced files.
11
13
  // icons: {