uniweb 1.0.2 → 1.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/package.json +4 -4
- package/partials/agents.md +2 -1
- package/src/backend/foundation-bring-along.js +17 -7
- package/src/commands/add.js +119 -69
- package/src/commands/build.js +64 -138
- package/src/commands/clone.js +15 -3
- package/src/commands/dev.js +57 -0
- package/src/commands/doctor.js +48 -44
- package/src/commands/rename.js +11 -26
- package/src/commands/update.js +19 -5
- package/src/framework-index.json +16 -16
- package/src/utils/config.js +98 -90
- package/src/utils/discover.js +17 -20
- package/src/utils/placement.js +55 -0
- package/src/utils/scaffold.js +1 -1
- package/src/utils/workspace-packages.js +431 -0
- package/src/utils/workspace.js +10 -71
- package/src/versions.js +5 -3
package/src/framework-index.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"generatedAt": "2026-10-
|
|
3
|
+
"generatedAt": "2026-10-13T20:52:47.988Z",
|
|
4
4
|
"packages": {
|
|
5
5
|
"@uniweb/api": {
|
|
6
|
-
"version": "1.0
|
|
6
|
+
"version": "1.1.0"
|
|
7
7
|
},
|
|
8
8
|
"@uniweb/build": {
|
|
9
|
-
"version": "1.
|
|
9
|
+
"version": "1.1.1"
|
|
10
10
|
},
|
|
11
11
|
"@uniweb/content-reader": {
|
|
12
12
|
"version": "1.2.8"
|
|
@@ -15,28 +15,28 @@
|
|
|
15
15
|
"version": "1.0.1"
|
|
16
16
|
},
|
|
17
17
|
"@uniweb/core": {
|
|
18
|
-
"version": "1.0
|
|
18
|
+
"version": "1.1.0"
|
|
19
19
|
},
|
|
20
20
|
"@uniweb/frame-bridge": {
|
|
21
|
-
"version": "1.0.
|
|
21
|
+
"version": "1.0.2"
|
|
22
22
|
},
|
|
23
23
|
"@uniweb/icons": {
|
|
24
|
-
"version": "1.0.
|
|
24
|
+
"version": "1.0.2"
|
|
25
25
|
},
|
|
26
26
|
"@uniweb/kit": {
|
|
27
|
-
"version": "1.0
|
|
27
|
+
"version": "1.1.0"
|
|
28
28
|
},
|
|
29
29
|
"@uniweb/loom": {
|
|
30
|
-
"version": "1.0
|
|
30
|
+
"version": "1.1.0"
|
|
31
31
|
},
|
|
32
32
|
"@uniweb/press": {
|
|
33
|
-
"version": "1.0
|
|
33
|
+
"version": "1.1.0"
|
|
34
34
|
},
|
|
35
35
|
"@uniweb/projections": {
|
|
36
|
-
"version": "1.0
|
|
36
|
+
"version": "1.1.0"
|
|
37
37
|
},
|
|
38
38
|
"@uniweb/runtime": {
|
|
39
|
-
"version": "1.0
|
|
39
|
+
"version": "1.1.0"
|
|
40
40
|
},
|
|
41
41
|
"@uniweb/scene": {
|
|
42
42
|
"version": "1.0.1"
|
|
@@ -45,22 +45,22 @@
|
|
|
45
45
|
"version": "1.0.1"
|
|
46
46
|
},
|
|
47
47
|
"@uniweb/scholar": {
|
|
48
|
-
"version": "1.0
|
|
48
|
+
"version": "1.1.0"
|
|
49
49
|
},
|
|
50
50
|
"@uniweb/semantic-parser": {
|
|
51
51
|
"version": "1.5.1"
|
|
52
52
|
},
|
|
53
53
|
"@uniweb/snapshot": {
|
|
54
|
-
"version": "1.0
|
|
54
|
+
"version": "1.1.0"
|
|
55
55
|
},
|
|
56
56
|
"@uniweb/templates": {
|
|
57
|
-
"version": "1.0
|
|
57
|
+
"version": "1.1.0"
|
|
58
58
|
},
|
|
59
59
|
"@uniweb/theming": {
|
|
60
|
-
"version": "1.0
|
|
60
|
+
"version": "1.1.0"
|
|
61
61
|
},
|
|
62
62
|
"@uniweb/unipress": {
|
|
63
|
-
"version": "1.0
|
|
63
|
+
"version": "1.1.0"
|
|
64
64
|
}
|
|
65
65
|
},
|
|
66
66
|
"templates": {
|
package/src/utils/config.js
CHANGED
|
@@ -18,10 +18,16 @@ import { existsSync, readFileSync } from 'node:fs'
|
|
|
18
18
|
import { readFile, writeFile } from 'node:fs/promises'
|
|
19
19
|
import { join } from 'node:path'
|
|
20
20
|
import { homedir } from 'node:os'
|
|
21
|
-
import yaml from 'js-yaml'
|
|
22
21
|
import { filterCmd } from './pm.js'
|
|
23
22
|
import { writeJsonPreservingStyleAsync } from './json-file.js'
|
|
24
23
|
import { loggedInOriginOf, readSessionFileSync } from './session-file.js'
|
|
24
|
+
import {
|
|
25
|
+
WORKSPACE_FILE,
|
|
26
|
+
readWorkspacePatterns,
|
|
27
|
+
patternsCover,
|
|
28
|
+
editWorkspaceFilePackages,
|
|
29
|
+
renderWorkspaceFile
|
|
30
|
+
} from './workspace-packages.js'
|
|
25
31
|
|
|
26
32
|
// ── Backend origin ─────────────────────────────────────────────
|
|
27
33
|
|
|
@@ -214,59 +220,88 @@ export const DEFAULT_BACKEND_ORIGIN = 'https://uniweb.app'
|
|
|
214
220
|
/**
|
|
215
221
|
* Read workspace package globs.
|
|
216
222
|
* Tries pnpm-workspace.yaml first, falls back to package.json workspaces.
|
|
223
|
+
* The folders they name: `workspacePackages` (./workspace-packages.js).
|
|
217
224
|
* @param {string} rootDir - Workspace root directory
|
|
218
225
|
* @returns {Promise<{packages: string[]}>}
|
|
219
226
|
*/
|
|
220
227
|
export async function readWorkspaceConfig(rootDir) {
|
|
221
|
-
|
|
222
|
-
const configPath = join(rootDir, 'pnpm-workspace.yaml')
|
|
223
|
-
if (existsSync(configPath)) {
|
|
224
|
-
const content = await readFile(configPath, 'utf-8')
|
|
225
|
-
const config = yaml.load(content)
|
|
226
|
-
return { packages: config?.packages || [] }
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// Fall back to package.json workspaces
|
|
230
|
-
const pkg = await readRootPackageJson(rootDir)
|
|
231
|
-
if (Array.isArray(pkg.workspaces)) {
|
|
232
|
-
return { packages: pkg.workspaces }
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
return { packages: [] }
|
|
228
|
+
return { packages: readWorkspacePatterns(rootDir).patterns }
|
|
236
229
|
}
|
|
237
230
|
|
|
238
231
|
/**
|
|
239
|
-
*
|
|
232
|
+
* Make the folder at `relPath` a workspace package.
|
|
233
|
+
*
|
|
234
|
+
* Nothing is written for a folder a pattern already covers — it is a package
|
|
235
|
+
* already. ⛔ Until 2026-10-13 its own path was added beside the pattern, and
|
|
236
|
+
* discovery listed it twice. Otherwise `entry` — the folder itself, unless a
|
|
237
|
+
* pattern is given for it — is added to each manifest that does not cover it:
|
|
238
|
+
*
|
|
239
|
+
* - pnpm-workspace.yaml's `packages:` list, edited in place: comments and every
|
|
240
|
+
* other key stay as they were. ⛔ Until 2026-10-13 the file was rewritten from
|
|
241
|
+
* its `packages` alone, deleting `onlyBuiltDependencies` and `allowBuilds` with
|
|
242
|
+
* the comments that explain them. A workspace without the file (a blank one, at
|
|
243
|
+
* its first `add`) gets it from the workspace template, as `create` writes it.
|
|
244
|
+
* - package.json's `workspaces`, which npm and yarn read.
|
|
245
|
+
*
|
|
240
246
|
* @param {string} rootDir - Workspace root directory
|
|
241
|
-
* @param {
|
|
247
|
+
* @param {string} relPath - The package's folder, workspace-relative
|
|
248
|
+
* @param {{ entry?: string }} [options] - `entry`: a pattern to list for it instead
|
|
242
249
|
*/
|
|
243
|
-
export async function
|
|
244
|
-
const
|
|
245
|
-
const
|
|
246
|
-
|
|
250
|
+
export async function registerWorkspacePackage(rootDir, relPath, { entry = relPath } = {}) {
|
|
251
|
+
const { source, patterns } = readWorkspacePatterns(rootDir)
|
|
252
|
+
const ymlPath = join(rootDir, WORKSPACE_FILE)
|
|
253
|
+
if (!patternsCover(patterns, relPath)) {
|
|
254
|
+
if (source === WORKSPACE_FILE) {
|
|
255
|
+
const text = await readFile(ymlPath, 'utf-8')
|
|
256
|
+
await writeFile(ymlPath, editedWorkspaceFile(text, (list) => [...list, entry], entry))
|
|
257
|
+
} else {
|
|
258
|
+
await writeFile(ymlPath, renderWorkspaceFile([...patterns, entry]))
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const pkg = await readRootPackageJson(rootDir)
|
|
263
|
+
const workspaces = Array.isArray(pkg.workspaces) ? pkg.workspaces : []
|
|
264
|
+
if (!patternsCover(workspaces, relPath)) {
|
|
265
|
+
pkg.workspaces = [...workspaces, entry]
|
|
266
|
+
await writeRootPackageJson(rootDir, pkg)
|
|
267
|
+
}
|
|
247
268
|
}
|
|
248
269
|
|
|
249
270
|
/**
|
|
250
|
-
*
|
|
251
|
-
*
|
|
252
|
-
*
|
|
271
|
+
* A package folder moved from `oldPath` to `newPath`: rename its entry in each
|
|
272
|
+
* manifest that lists it by path — a pattern is left as written — then register
|
|
273
|
+
* the new path, in case it has moved out from under the pattern that covered it.
|
|
274
|
+
*
|
|
275
|
+
* @param {string} rootDir
|
|
276
|
+
* @param {string} oldPath
|
|
277
|
+
* @param {string} newPath
|
|
253
278
|
*/
|
|
254
|
-
export async function
|
|
255
|
-
const
|
|
256
|
-
if (
|
|
257
|
-
|
|
258
|
-
await
|
|
279
|
+
export async function moveWorkspacePackage(rootDir, oldPath, newPath) {
|
|
280
|
+
const { source, patterns } = readWorkspacePatterns(rootDir)
|
|
281
|
+
if (source === WORKSPACE_FILE && patterns.includes(oldPath)) {
|
|
282
|
+
const ymlPath = join(rootDir, WORKSPACE_FILE)
|
|
283
|
+
const text = await readFile(ymlPath, 'utf-8')
|
|
284
|
+
const rename = (list) => list.map((p) => (p === oldPath ? newPath : p))
|
|
285
|
+
await writeFile(ymlPath, editedWorkspaceFile(text, rename, newPath))
|
|
259
286
|
}
|
|
260
|
-
|
|
261
|
-
// Sync workspaces array in package.json (for npm compatibility)
|
|
262
287
|
const pkg = await readRootPackageJson(rootDir)
|
|
263
|
-
if (
|
|
264
|
-
pkg.workspaces =
|
|
265
|
-
}
|
|
266
|
-
if (!pkg.workspaces.includes(glob)) {
|
|
267
|
-
pkg.workspaces.push(glob)
|
|
288
|
+
if (Array.isArray(pkg.workspaces) && pkg.workspaces.includes(oldPath)) {
|
|
289
|
+
pkg.workspaces = pkg.workspaces.map((p) => (p === oldPath ? newPath : p))
|
|
268
290
|
await writeRootPackageJson(rootDir, pkg)
|
|
269
291
|
}
|
|
292
|
+
await registerWorkspacePackage(rootDir, newPath)
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/** The edited file, or an error that says what to add by hand — the file is never rewritten. */
|
|
296
|
+
function editedWorkspaceFile(text, edit, entry) {
|
|
297
|
+
const next = editWorkspaceFilePackages(text, edit)
|
|
298
|
+
if (next == null) {
|
|
299
|
+
throw new Error(
|
|
300
|
+
`Could not edit the packages: list in ${WORKSPACE_FILE} in place, and it is not rewritten ` +
|
|
301
|
+
`whole. Add "${entry}" to it by hand.`
|
|
302
|
+
)
|
|
303
|
+
}
|
|
304
|
+
return next
|
|
270
305
|
}
|
|
271
306
|
|
|
272
307
|
/**
|
|
@@ -352,66 +387,39 @@ export function computeRootScripts(sites, pm = 'pnpm') {
|
|
|
352
387
|
*/
|
|
353
388
|
export async function updateRootScripts(rootDir, sites, pm = 'pnpm') {
|
|
354
389
|
const pkg = await readRootPackageJson(rootDir)
|
|
355
|
-
const
|
|
390
|
+
const unique = sites.filter((s, i) => sites.findIndex((o) => o.path === s.path) === i)
|
|
391
|
+
const newScripts = computeRootScripts(unique, pm)
|
|
392
|
+
const scripts = { ...(pkg.scripts || {}) }
|
|
356
393
|
|
|
357
394
|
// If we're adding a second site, rename existing dev/preview to dev:{firstName}
|
|
358
|
-
if (
|
|
359
|
-
const firstName =
|
|
395
|
+
if (unique.length === 2 && scripts.dev) {
|
|
396
|
+
const firstName = unique[0].name
|
|
360
397
|
// Only rename if the existing dev script references the first site's name
|
|
361
|
-
if (
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
pkg.scripts = { ...pkg.scripts, ...newScripts }
|
|
368
|
-
await writeRootPackageJson(rootDir, pkg)
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
// Resolve a workspace glob pattern to actual directories
|
|
372
|
-
export async function resolveGlob(rootDir, pattern) {
|
|
373
|
-
const clean = pattern.replace(/^["']|["']$/g, '')
|
|
374
|
-
|
|
375
|
-
if (clean.endsWith('/*')) {
|
|
376
|
-
// Pattern like "foundations/*" - list subdirectories
|
|
377
|
-
const baseDir = clean.slice(0, -2)
|
|
378
|
-
const fullPath = join(rootDir, baseDir)
|
|
379
|
-
if (!existsSync(fullPath)) return []
|
|
380
|
-
try {
|
|
381
|
-
const { readdirSync } = await import('node:fs')
|
|
382
|
-
const entries = readdirSync(fullPath, { withFileTypes: true })
|
|
383
|
-
return entries
|
|
384
|
-
.filter((e) => e.isDirectory() && !e.name.startsWith('.'))
|
|
385
|
-
.map((e) => join(baseDir, e.name))
|
|
386
|
-
} catch {
|
|
387
|
-
return []
|
|
398
|
+
if (scripts.dev.includes(firstName)) {
|
|
399
|
+
scripts[`dev:${firstName}`] = scripts.dev
|
|
400
|
+
scripts[`preview:${firstName}`] = scripts.preview
|
|
388
401
|
}
|
|
389
402
|
}
|
|
390
403
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
e.isDirectory() &&
|
|
401
|
-
!e.name.startsWith('.') &&
|
|
402
|
-
e.name !== 'node_modules'
|
|
403
|
-
)
|
|
404
|
-
.filter((e) => existsSync(join(rootDir, e.name, suffix)))
|
|
405
|
-
.map((e) => join(e.name, suffix))
|
|
406
|
-
} catch {
|
|
407
|
-
return []
|
|
408
|
-
}
|
|
404
|
+
// ⭐ The per-site scripts this CLI writes are regenerated from the sites there are
|
|
405
|
+
// now, and one it wrote for a site that is gone — renamed, moved, removed — goes.
|
|
406
|
+
// A script whose command is not one the CLI writes is the user's, and stays.
|
|
407
|
+
// ⛔ Until 2026-10-13 they were only ever added: after `rename site blog journal`,
|
|
408
|
+
// `dev:blog` still ran `uniweb dev blog`, a site that no longer existed.
|
|
409
|
+
for (const [key, value] of Object.entries(scripts)) {
|
|
410
|
+
if (key in newScripts) continue
|
|
411
|
+
const m = /^(dev|preview):(.+)$/.exec(key)
|
|
412
|
+
if (m && cliSiteScripts(m[1], m[2]).includes(value)) delete scripts[key]
|
|
409
413
|
}
|
|
410
414
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
}
|
|
415
|
+
pkg.scripts = { ...scripts, ...newScripts }
|
|
416
|
+
await writeRootPackageJson(rootDir, pkg)
|
|
417
|
+
}
|
|
415
418
|
|
|
416
|
-
|
|
419
|
+
/** Every command this CLI has written for the `dev:<site>` / `preview:<site>` script. */
|
|
420
|
+
function cliSiteScripts(kind, site) {
|
|
421
|
+
return kind === 'dev'
|
|
422
|
+
? [`uniweb dev ${site}`, filterCmd('pnpm', site, 'dev'), filterCmd('npm', site, 'dev')]
|
|
423
|
+
: [filterCmd('pnpm', site, 'preview'), filterCmd('npm', site, 'preview')]
|
|
417
424
|
}
|
|
425
|
+
|
package/src/utils/discover.js
CHANGED
|
@@ -24,7 +24,7 @@ import { existsSync } from 'node:fs'
|
|
|
24
24
|
import { readFile } from 'node:fs/promises'
|
|
25
25
|
import { join } from 'node:path'
|
|
26
26
|
import { classifyPackage } from '@uniweb/build'
|
|
27
|
-
import {
|
|
27
|
+
import { workspacePackages } from './workspace-packages.js'
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
30
|
* Discover foundations in the workspace.
|
|
@@ -45,30 +45,27 @@ export async function discoverSites(rootDir) {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
async function discoverByKind(rootDir, kind) {
|
|
48
|
-
const { packages } = await readWorkspaceConfig(rootDir)
|
|
49
48
|
const out = []
|
|
50
49
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
if (classifyPackage(fullPath) !== kind) continue
|
|
50
|
+
// The workspace's packages as pnpm reads its patterns — each folder once.
|
|
51
|
+
for (const dir of workspacePackages(rootDir)) {
|
|
52
|
+
const fullPath = join(rootDir, dir)
|
|
53
|
+
if (classifyPackage(fullPath) !== kind) continue
|
|
56
54
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
55
|
+
// Read package.json for the package name. Synthesize one from
|
|
56
|
+
// the directory if it's missing or malformed — we still want
|
|
57
|
+
// the package to surface in pickers.
|
|
58
|
+
const pkgPath = join(fullPath, 'package.json')
|
|
59
|
+
let name = dir.split('/').pop()
|
|
60
|
+
if (existsSync(pkgPath)) {
|
|
61
|
+
try {
|
|
62
|
+
const pkg = JSON.parse(await readFile(pkgPath, 'utf-8'))
|
|
63
|
+
if (pkg.name) name = pkg.name
|
|
64
|
+
} catch {
|
|
65
|
+
// keep directory-derived name
|
|
69
66
|
}
|
|
70
|
-
out.push({ name, path: dir })
|
|
71
67
|
}
|
|
68
|
+
out.push({ name, path: dir })
|
|
72
69
|
}
|
|
73
70
|
|
|
74
71
|
return out
|
package/src/utils/placement.js
CHANGED
|
@@ -96,6 +96,12 @@ export const EXTENSION_KIND = {
|
|
|
96
96
|
* @returns {{ relativePath: string, packageName: string }}
|
|
97
97
|
*/
|
|
98
98
|
export function resolvePlacement(rootDir, name, opts, kind) {
|
|
99
|
+
// 0. `--project` names the folder itself (rule 4), so a name or a `--path` beside it
|
|
100
|
+
// names a second one: refused, never ignored (`projectConflictLines`).
|
|
101
|
+
if (opts.project && (name || opts.path)) {
|
|
102
|
+
return { projectConflict: true, relativePath: null, packageName: null }
|
|
103
|
+
}
|
|
104
|
+
|
|
99
105
|
// 1. --path is a PARENT directory. The folder is `<path>/<name>` if a
|
|
100
106
|
// name was given, or `<path>` itself if not (the path's last segment
|
|
101
107
|
// is then taken as the package name).
|
|
@@ -149,3 +155,52 @@ export function resolvePlacement(rootDir, name, opts, kind) {
|
|
|
149
155
|
packageName: kind.defaultPkg
|
|
150
156
|
}
|
|
151
157
|
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* What to say when `--project` came with a name or a `--path` — the first line an
|
|
161
|
+
* error, the rest the commands that do what was meant.
|
|
162
|
+
*
|
|
163
|
+
* `--project <p>` is the co-located shorthand: it places a project's one foundation
|
|
164
|
+
* at `<p>/src/` and its one site at `<p>/site/`, and names nothing else. A name beside
|
|
165
|
+
* it asks for a different folder, which naming the folder says plainly.
|
|
166
|
+
* ⛔ Until 2026-10-13 the name won without a word: `add site fitness --project
|
|
167
|
+
* memberships` created `fitness/` at the workspace root.
|
|
168
|
+
*
|
|
169
|
+
* @param {'foundation'|'site'|'extension'} kind
|
|
170
|
+
* @param {string|null} name - the name given, if any
|
|
171
|
+
* @param {{ project: string, path?: string }} opts
|
|
172
|
+
* @param {string} command - how to run the verb: `uniweb add site`, `uniweb clone <uuid>`
|
|
173
|
+
* @returns {string[]}
|
|
174
|
+
*/
|
|
175
|
+
export function projectConflictLines(kind, name, opts, command) {
|
|
176
|
+
const p = opts.project
|
|
177
|
+
if (!name) {
|
|
178
|
+
return [
|
|
179
|
+
`--project and --path both name the folder — use one.`,
|
|
180
|
+
` ${command} --project ${p} ${kind === 'site' ? `${p}/site/` : `${p}/src/`}`,
|
|
181
|
+
` ${command} --path ${opts.path}`
|
|
182
|
+
]
|
|
183
|
+
}
|
|
184
|
+
if (kind === 'extension') {
|
|
185
|
+
return [
|
|
186
|
+
`--project places a project's foundation or site; an extension goes in the folder you name.`,
|
|
187
|
+
` ${command} ${p}/extensions/${name}`
|
|
188
|
+
]
|
|
189
|
+
}
|
|
190
|
+
if (kind === 'site') {
|
|
191
|
+
return [
|
|
192
|
+
`--project ${p} places the project's one site, at ${p}/site/, and takes no name.`,
|
|
193
|
+
`For a site named "${name}" in ${p}, name its folder — a project with several sites keeps them in ${p}/sites/:`,
|
|
194
|
+
` ${command} ${p}/sites/${name}`,
|
|
195
|
+
`Or the project's one site:`,
|
|
196
|
+
` ${command} --project ${p}`
|
|
197
|
+
]
|
|
198
|
+
}
|
|
199
|
+
return [
|
|
200
|
+
`--project ${p} places the project's foundation, at ${p}/src/, and takes no name.`,
|
|
201
|
+
`For a foundation named "${name}" in ${p}, name its folder:`,
|
|
202
|
+
` ${command} ${p}/${name}`,
|
|
203
|
+
`Or the project's foundation:`,
|
|
204
|
+
` ${command} --project ${p}`
|
|
205
|
+
]
|
|
206
|
+
}
|
package/src/utils/scaffold.js
CHANGED
|
@@ -37,7 +37,7 @@ export async function scaffoldWorkspace(targetDir, context, options = {}) {
|
|
|
37
37
|
registerVersions(getVersionsForTemplates())
|
|
38
38
|
|
|
39
39
|
// Skip pnpm-workspace.yaml for blank workspaces (no packages yet).
|
|
40
|
-
// The `add` command creates it on demand
|
|
40
|
+
// The `add` command creates it on demand (registerWorkspacePackage), from this template.
|
|
41
41
|
// Without this, pnpm fails with ERR_PNPM_NO_IMPORTER_MANIFEST_FOUND
|
|
42
42
|
// because an empty packages: [] makes pnpm search parent directories.
|
|
43
43
|
const skip = context.workspaceGlobs?.length ? [] : ['pnpm-workspace.yaml']
|