uniweb 0.9.0 → 0.9.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.
Files changed (2) hide show
  1. package/package.json +5 -5
  2. package/src/versions.js +136 -35
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uniweb",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
4
4
  "description": "Create structured Vite + React sites with content/code separation",
5
5
  "type": "module",
6
6
  "bin": {
@@ -41,13 +41,13 @@
41
41
  "js-yaml": "^4.1.0",
42
42
  "prompts": "^2.4.2",
43
43
  "tar": "^7.0.0",
44
- "@uniweb/core": "0.6.0",
45
- "@uniweb/runtime": "0.7.0",
46
- "@uniweb/kit": "0.8.0"
44
+ "@uniweb/core": "0.6.1",
45
+ "@uniweb/runtime": "0.7.1",
46
+ "@uniweb/kit": "0.8.1"
47
47
  },
48
48
  "peerDependencies": {
49
49
  "@uniweb/content-reader": "1.1.4",
50
- "@uniweb/build": "0.9.0",
50
+ "@uniweb/build": "0.9.1",
51
51
  "@uniweb/semantic-parser": "1.1.9"
52
52
  },
53
53
  "peerDependenciesMeta": {
package/src/versions.js CHANGED
@@ -1,11 +1,37 @@
1
1
  /**
2
2
  * Version resolution utility
3
3
  *
4
- * Reads package versions from the CLI's own dependencies to ensure
5
- * generated projects use compatible versions.
4
+ * Produces npm-compatible version specs for every `@uniweb/*` package the
5
+ * scaffolder might reference when materializing a new project. The caller
6
+ * (templates/processor.js via the `{{version}}` Handlebars helper) never
7
+ * needs to know whether the CLI is running locally from a pnpm workspace
8
+ * or as an npm-installed binary — both code paths feed through here.
9
+ *
10
+ * ## How versions are resolved
11
+ *
12
+ * 1. Start from the CLI's own `package.json` dependencies. When the CLI
13
+ * was installed via npm, pnpm has already resolved every `workspace:*`
14
+ * spec into a concrete version like `^0.9.1`, so this step is
15
+ * usually enough.
16
+ * 2. For any dep that is still `workspace:*` (i.e. the CLI is running
17
+ * *from* the pnpm workspace — local dev, sandbox script, tests), fall
18
+ * back to reading the real `package.json` of each sibling package
19
+ * under `framework/<name>/` and using `^<that version>`. This is the
20
+ * path that keeps local sandboxes and templates aligned with whatever
21
+ * was last bumped in the monorepo.
22
+ * 3. Scan `framework/*` to catch any additional `@uniweb/*` package that
23
+ * wasn't explicitly listed as a CLI dep (press, loom, scholar, etc.).
24
+ * New packages get picked up by the scaffolder automatically.
25
+ *
26
+ * The previous implementation shipped a hardcoded fallback table and
27
+ * nothing kept it in sync with reality — see the audit note in
28
+ * `framework/CLAUDE.md` under "Publishing". Every workspace-based scaffold
29
+ * silently pinned against years-old versions. That table is gone. If this
30
+ * function ever fails to resolve a package, it returns `^0.0.0` rather
31
+ * than a stale best-guess, so the breakage is loud.
6
32
  */
7
33
 
8
- import { readFileSync } from 'node:fs'
34
+ import { readFileSync, readdirSync, statSync } from 'node:fs'
9
35
  import { dirname, join } from 'node:path'
10
36
  import { fileURLToPath } from 'node:url'
11
37
 
@@ -32,37 +58,104 @@ export function getCliVersion() {
32
58
  }
33
59
 
34
60
  /**
35
- * Extract version number from version spec (e.g., "^0.1.4" -> "0.1.4")
61
+ * Locate the framework/ directory on disk. When the CLI is running from
62
+ * the pnpm workspace, this resolves to `<workspace>/framework/`. When the
63
+ * CLI is installed from npm, this directory won't exist — callers must
64
+ * handle that (return null) so the function doesn't pretend to know more
65
+ * than it does.
36
66
  */
37
- function extractVersion(spec) {
38
- if (!spec) return null
39
- // Remove ^, ~, >=, etc. prefixes
40
- return spec.replace(/^[\^~>=<]+/, '')
67
+ function getFrameworkRoot() {
68
+ const candidate = join(__dirname, '..', '..')
69
+ try {
70
+ if (statSync(candidate).isDirectory()) {
71
+ return candidate
72
+ }
73
+ } catch {}
74
+ return null
41
75
  }
42
76
 
43
77
  /**
44
- * Resolve a version spec to an npm-compatible version
45
- * Handles workspace:* and other pnpm-specific protocols
46
- *
47
- * @param {string} spec - Version spec (e.g., "workspace:*", "^0.1.0")
48
- * @param {string} fallback - Fallback version if spec is not resolvable
49
- * @returns {string} npm-compatible version spec
78
+ * Read the current on-disk version of a specific `@uniweb/*` package by
79
+ * looking up `framework/<last-segment>/package.json`. Returns a caret
80
+ * range string like `^0.6.0`, or null if the package isn't present on
81
+ * disk (i.e. the CLI is running from npm, not from the workspace).
50
82
  */
51
- function resolveVersionSpec(spec, fallback) {
52
- if (!spec) return fallback
53
- // workspace:* is pnpm-specific, use fallback for npm compatibility
54
- if (spec.startsWith('workspace:')) return fallback
83
+ function readWorkspaceVersion(packageName) {
84
+ const root = getFrameworkRoot()
85
+ if (!root) return null
86
+ const shortName = packageName.startsWith('@uniweb/')
87
+ ? packageName.slice('@uniweb/'.length)
88
+ : packageName
89
+ const pkgPath = join(root, shortName, 'package.json')
90
+ try {
91
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'))
92
+ if (pkg.name === packageName && pkg.version) {
93
+ return `^${pkg.version}`
94
+ }
95
+ } catch {}
96
+ return null
97
+ }
98
+
99
+ /**
100
+ * Enumerate every `@uniweb/*` package under `framework/*` and return a
101
+ * map of `{ name: '^version' }`. Used to seed the resolved-versions
102
+ * cache so that packages not explicitly listed in the CLI's own deps
103
+ * (press, loom, scholar, schemas, etc.) still have a version available
104
+ * to templates that reference them.
105
+ */
106
+ function discoverWorkspacePackages() {
107
+ const root = getFrameworkRoot()
108
+ if (!root) return {}
109
+ const found = {}
110
+ let entries
111
+ try {
112
+ entries = readdirSync(root, { withFileTypes: true })
113
+ } catch {
114
+ return {}
115
+ }
116
+ for (const entry of entries) {
117
+ if (!entry.isDirectory()) continue
118
+ if (entry.name.startsWith('.') || entry.name.startsWith('_')) continue
119
+ const pkgPath = join(root, entry.name, 'package.json')
120
+ try {
121
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'))
122
+ if (pkg.name && pkg.version && pkg.name.startsWith('@uniweb/')) {
123
+ found[pkg.name] = `^${pkg.version}`
124
+ }
125
+ } catch {}
126
+ }
127
+ return found
128
+ }
129
+
130
+ /**
131
+ * Resolve a single version spec to something npm can install. A concrete
132
+ * spec like `^0.9.0` passes through untouched; `workspace:*` is replaced
133
+ * by the on-disk version of the named package; anything still unresolved
134
+ * returns null so the caller can fall back.
135
+ */
136
+ function resolveVersionSpec(spec, packageName) {
137
+ if (!spec) return null
138
+ if (spec.startsWith('workspace:')) {
139
+ return readWorkspaceVersion(packageName)
140
+ }
55
141
  return spec
56
142
  }
57
143
 
58
144
  /**
59
- * Get resolved versions for @uniweb/* packages
145
+ * Get resolved versions for @uniweb/* packages.
60
146
  *
61
- * Returns versions that should be used in generated projects,
62
- * based on the CLI's own dependencies.
147
+ * Priority (highest first):
148
+ * 1. A concrete version spec already in the CLI's own `package.json`
149
+ * (the state after an npm publish: `workspace:*` is resolved to a
150
+ * real version).
151
+ * 2. The on-disk version of the matching workspace package (the state
152
+ * during local development).
153
+ * 3. Discovery fallback — every `@uniweb/*` package found under
154
+ * `framework/*`, for packages that aren't in the CLI's own deps.
63
155
  *
64
- * Note: In development (pnpm workspace), versions may be "workspace:*"
65
- * which we convert to npm-compatible fallback versions.
156
+ * The return shape is stable across both paths: a map of package names to
157
+ * npm-compatible version specs, plus the CLI's own version under the key
158
+ * `uniweb`.
66
159
  *
67
160
  * @returns {Object} Map of package names to version specs
68
161
  */
@@ -72,20 +165,28 @@ export function getResolvedVersions() {
72
165
  const pkg = getCliPackageJson()
73
166
  const deps = { ...pkg.dependencies, ...pkg.devDependencies, ...pkg.peerDependencies }
74
167
 
75
- // All @uniweb/* packages are now direct dependencies of the CLI.
76
- // When publishing with pnpm, workspace:* gets resolved to actual versions.
77
- // Fallbacks are only used during local development.
78
- resolvedVersions = {
79
- '@uniweb/build': resolveVersionSpec(deps['@uniweb/build'], '^0.1.12'),
80
- '@uniweb/core': resolveVersionSpec(deps['@uniweb/core'], '^0.1.6'),
81
- '@uniweb/kit': resolveVersionSpec(deps['@uniweb/kit'], '^0.1.4'),
82
- '@uniweb/runtime': resolveVersionSpec(deps['@uniweb/runtime'], '^0.2.3'),
83
- '@uniweb/templates': resolveVersionSpec(deps['@uniweb/templates'], '^0.1.6'),
84
-
85
- // CLI itself (use current version)
86
- 'uniweb': `^${pkg.version}`,
168
+ // Seed from the CLI's own deps (the authoritative set when installed from npm).
169
+ const result = {}
170
+ for (const [name, spec] of Object.entries(deps)) {
171
+ if (!name.startsWith('@uniweb/')) continue
172
+ const resolved = resolveVersionSpec(spec, name)
173
+ if (resolved) result[name] = resolved
87
174
  }
88
175
 
176
+ // Merge in anything under framework/* that the CLI didn't list explicitly.
177
+ // A newly-added package (e.g. @uniweb/press) becomes reachable via
178
+ // {{version "@uniweb/press"}} without touching the CLI's package.json.
179
+ const discovered = discoverWorkspacePackages()
180
+ for (const [name, version] of Object.entries(discovered)) {
181
+ if (!result[name]) result[name] = version
182
+ }
183
+
184
+ // CLI itself. Caret on the current version — templates referencing
185
+ // `{{version "uniweb"}}` pick up whatever patch/minor ships in the
186
+ // same publish cycle as the template.
187
+ result['uniweb'] = `^${pkg.version}`
188
+
189
+ resolvedVersions = result
89
190
  return resolvedVersions
90
191
  }
91
192