@dfosco/storyboard 1.0.0-beta.3 → 1.0.0-beta.4
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 +1 -1
- package/scaffold/backstage/tsconfig.json +25 -0
- package/scaffold/backstage/vite.config.js +36 -5
- package/src/core/cli/dev.js +7 -6
- package/src/core/paths/resolvePaths.js +7 -36
- package/src/core/paths/resolvePaths.test.js +0 -23
- package/src/core/scaffold/scaffold-integration.test.js +16 -0
package/package.json
CHANGED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"useDefineForClassFields": true,
|
|
5
|
+
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
|
6
|
+
"module": "ESNext",
|
|
7
|
+
"skipLibCheck": true,
|
|
8
|
+
"moduleResolution": "bundler",
|
|
9
|
+
"allowImportingTsExtensions": true,
|
|
10
|
+
"isolatedModules": true,
|
|
11
|
+
"moduleDetection": "force",
|
|
12
|
+
"noEmit": true,
|
|
13
|
+
"jsx": "react-jsx",
|
|
14
|
+
"strict": false,
|
|
15
|
+
"noUnusedLocals": false,
|
|
16
|
+
"noUnusedParameters": false,
|
|
17
|
+
"noFallthroughCasesInSwitch": true,
|
|
18
|
+
"allowJs": true,
|
|
19
|
+
"baseUrl": ".",
|
|
20
|
+
"paths": {
|
|
21
|
+
"@/*": ["../*"]
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"include": ["src", "../components", "../prototypes", "../templates"]
|
|
25
|
+
}
|
|
@@ -26,18 +26,44 @@ import postcssGlobalData from '@csstools/postcss-global-data'
|
|
|
26
26
|
import postcssPresetEnv from 'postcss-preset-env'
|
|
27
27
|
import browsers from '@github/browserslist-config'
|
|
28
28
|
import { globSync } from 'glob'
|
|
29
|
-
import { readFileSync } from 'fs'
|
|
29
|
+
import { copyFileSync, existsSync, readFileSync } from 'fs'
|
|
30
30
|
|
|
31
31
|
const __dirname = path.dirname(new URL(import.meta.url).pathname)
|
|
32
32
|
|
|
33
33
|
// The content root is the parent of this `.backstage` Vite root.
|
|
34
34
|
const contentRoot = path.resolve(__dirname, '..')
|
|
35
35
|
|
|
36
|
+
// Mirror the canonical `storyboard.config.json` (kept at the CONTENT root) into
|
|
37
|
+
// this system root so the library bootstrap's Vite-root-relative imports
|
|
38
|
+
// (`import storyboardConfig from '../../storyboard.config.json'` in
|
|
39
|
+
// src/library/{mount,index,home,prototypes-entry}.jsx) resolve.
|
|
40
|
+
//
|
|
41
|
+
// This runs at Vite config-eval time — before Rollup resolves any module — so
|
|
42
|
+
// EVERY Vite invocation is self-sufficient: `storyboard dev`, and (crucially) a
|
|
43
|
+
// DIRECT `vite build` / `vite preview` used by CI, the deploy/preview workflows,
|
|
44
|
+
// and build-site.mjs. The mirror copy inside `.backstage/` is gitignored, so on
|
|
45
|
+
// a fresh clone it doesn't exist until something creates it; doing it here is
|
|
46
|
+
// what lets a clean CI build resolve the config import. The content-root copy
|
|
47
|
+
// remains the single source of truth the user edits. Legacy layout never loads
|
|
48
|
+
// this config (it ships a different vite.config.js where system === content).
|
|
49
|
+
function mirrorContentConfig() {
|
|
50
|
+
const src = path.join(contentRoot, 'storyboard.config.json')
|
|
51
|
+
const dest = path.join(__dirname, 'storyboard.config.json')
|
|
52
|
+
if (!existsSync(src)) return
|
|
53
|
+
try {
|
|
54
|
+
copyFileSync(src, dest)
|
|
55
|
+
} catch {
|
|
56
|
+
// Best-effort: a stale/missing mirror surfaces as a clear resolution
|
|
57
|
+
// error downstream; never crash config evaluation over the copy itself.
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
mirrorContentConfig()
|
|
62
|
+
|
|
36
63
|
// Additional content directories declared by the repo via the `content.dirs`
|
|
37
64
|
// array in storyboard.config.json. These live alongside the role folders at
|
|
38
65
|
// the content root and are watched for add/remove (full-reload) and warmed up
|
|
39
|
-
// like the built-in role folders.
|
|
40
|
-
// into this system root (see core/paths/resolvePaths.js#mirrorSystemConfig).
|
|
66
|
+
// like the built-in role folders. Reads the mirror created just above.
|
|
41
67
|
function readExtraContentDirs() {
|
|
42
68
|
try {
|
|
43
69
|
const cfg = JSON.parse(readFileSync(path.join(__dirname, 'storyboard.config.json'), 'utf-8'))
|
|
@@ -67,8 +93,13 @@ export default defineConfig(() => {
|
|
|
67
93
|
base,
|
|
68
94
|
resolve: {
|
|
69
95
|
alias: {
|
|
70
|
-
//
|
|
71
|
-
|
|
96
|
+
// `@` is the author-facing content alias (`@/components/...`,
|
|
97
|
+
// `@/templates/...`). In the legacy layout `@` and `@content` both
|
|
98
|
+
// resolve to `./src` (where content lives); in the backstage layout
|
|
99
|
+
// content lives at the content root, so `@` points there too. System
|
|
100
|
+
// source under `.backstage/src` is loaded via `/src/...` Vite-absolute
|
|
101
|
+
// paths (see index.html) and the `@dfosco/storyboard` package, never `@`.
|
|
102
|
+
'@': contentRoot,
|
|
72
103
|
// `@content` is the role-folder base for browser content globs
|
|
73
104
|
// (`@content/prototypes/**`). In the backstage layout it points at
|
|
74
105
|
// the content root, so the globs reach the sibling content folders
|
package/src/core/cli/dev.js
CHANGED
|
@@ -20,7 +20,7 @@ import { readFileSync, existsSync } from 'node:fs'
|
|
|
20
20
|
import { detectWorktreeName, getPort, releasePort } from '../worktree/port.js'
|
|
21
21
|
import { startFileWatcher } from '../file-watcher/watcher.js'
|
|
22
22
|
import { compactAll } from '../canvas/compact.js'
|
|
23
|
-
import { findSystemRoot,
|
|
23
|
+
import { findSystemRoot, ensureContentNodeModulesLink, ensureLinkedDepsOverlay, ensureContentSystemLinks } from '../paths/resolvePaths.js'
|
|
24
24
|
import { parseFlags } from './flags.js'
|
|
25
25
|
import { setupNeeded, writeUserState, getInstalledStoryboardVersion } from './userState.js'
|
|
26
26
|
import { dim, magenta, bold } from './intro.js'
|
|
@@ -177,12 +177,13 @@ async function main() {
|
|
|
177
177
|
// the content root.
|
|
178
178
|
const viteCwd = systemRoot
|
|
179
179
|
|
|
180
|
-
//
|
|
181
|
-
//
|
|
182
|
-
//
|
|
183
|
-
//
|
|
180
|
+
// Ensure the content-root `node_modules` symlink exists so content above the
|
|
181
|
+
// Vite root can resolve its bare imports. The `storyboard.config.json` mirror
|
|
182
|
+
// into `.backstage/` is handled at Vite config-eval time (see
|
|
183
|
+
// scaffold/backstage/vite.config.js#mirrorContentConfig) so EVERY Vite
|
|
184
|
+
// invocation — `storyboard dev` and direct `vite build`/`vite preview` in
|
|
185
|
+
// CI — stays self-sufficient. All no-ops in the legacy layout.
|
|
184
186
|
if (mode === 'backstage') {
|
|
185
|
-
mirrorSystemConfig(layout)
|
|
186
187
|
ensureContentNodeModulesLink(layout)
|
|
187
188
|
// When the library is linked from a sibling monorepo (local development),
|
|
188
189
|
// its hoisted deps aren't reachable from the Vite root. Overlay them so
|
|
@@ -40,7 +40,6 @@
|
|
|
40
40
|
|
|
41
41
|
import { existsSync, lstatSync, readlinkSync, symlinkSync, unlinkSync, readdirSync, realpathSync, mkdirSync, readFileSync, appendFileSync } from 'node:fs'
|
|
42
42
|
import { basename, dirname, join, relative, resolve } from 'node:path'
|
|
43
|
-
import { copyFileSync } from 'node:fs'
|
|
44
43
|
import process from 'node:process'
|
|
45
44
|
|
|
46
45
|
export const SYSTEM_DIR = '.backstage'
|
|
@@ -230,41 +229,13 @@ export function contentBase(root, ...segments) {
|
|
|
230
229
|
return segments.length ? join(base, ...segments) : base
|
|
231
230
|
}
|
|
232
231
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
* - `import.meta.glob('/storyboard.config.json')` (mountIsolationApp — rooted
|
|
241
|
-
* at the Vite root, i.e. the system root)
|
|
242
|
-
*
|
|
243
|
-
* In the **legacy** layout the system root and content root are the same
|
|
244
|
-
* directory, so this is a no-op (the config is already where those references
|
|
245
|
-
* resolve). In the **backstage** layout it copies
|
|
246
|
-
* `<contentRoot>/storyboard.config.json` → `<systemRoot>/storyboard.config.json`.
|
|
247
|
-
*
|
|
248
|
-
* The mirror is a generated artifact owned by the launcher/CLI — it lives
|
|
249
|
-
* inside `.backstage/` and is gitignored. The content-root copy remains the
|
|
250
|
-
* single source of truth the user edits.
|
|
251
|
-
*
|
|
252
|
-
* @param {{ mode: 'backstage'|'legacy', systemRoot: string, contentRoot: string }} loc
|
|
253
|
-
* @returns {boolean} true if a mirror copy was written.
|
|
254
|
-
*/
|
|
255
|
-
export function mirrorSystemConfig(loc) {
|
|
256
|
-
const { mode, systemRoot, contentRoot } = loc
|
|
257
|
-
if (mode !== 'backstage') return false
|
|
258
|
-
const src = join(contentRoot, 'storyboard.config.json')
|
|
259
|
-
const dest = join(systemRoot, 'storyboard.config.json')
|
|
260
|
-
if (!existsSync(src)) return false
|
|
261
|
-
try {
|
|
262
|
-
copyFileSync(src, dest)
|
|
263
|
-
return true
|
|
264
|
-
} catch {
|
|
265
|
-
return false
|
|
266
|
-
}
|
|
267
|
-
}
|
|
232
|
+
// NOTE: The `storyboard.config.json` mirror (copying the canonical content-root
|
|
233
|
+
// config into the system root so the library bootstrap's Vite-root-relative
|
|
234
|
+
// imports resolve) lives in the backstage Vite config itself
|
|
235
|
+
// (scaffold/backstage/vite.config.js#mirrorContentConfig). Running it at Vite
|
|
236
|
+
// config-eval time makes EVERY Vite invocation self-sufficient — `storyboard
|
|
237
|
+
// dev` and direct `vite build`/`vite preview` in CI alike — instead of only the
|
|
238
|
+
// `storyboard dev` launcher path.
|
|
268
239
|
|
|
269
240
|
/**
|
|
270
241
|
* Ensure a `node_modules` symlink exists at the CONTENT root pointing into the
|
|
@@ -9,7 +9,6 @@ import {
|
|
|
9
9
|
contentDir,
|
|
10
10
|
contentBase,
|
|
11
11
|
extraContentDirs,
|
|
12
|
-
mirrorSystemConfig,
|
|
13
12
|
ensureContentNodeModulesLink,
|
|
14
13
|
ensureLinkedDepsOverlay,
|
|
15
14
|
ensureContentSystemLinks,
|
|
@@ -183,28 +182,6 @@ describe('core/paths/resolvePaths', () => {
|
|
|
183
182
|
})
|
|
184
183
|
})
|
|
185
184
|
|
|
186
|
-
describe('mirrorSystemConfig', () => {
|
|
187
|
-
it('is a no-op in the legacy layout', () => {
|
|
188
|
-
writeFileSync(join(tmp, 'storyboard.config.json'), '{"port":1234}')
|
|
189
|
-
expect(mirrorSystemConfig(findSystemRoot(tmp))).toBe(false)
|
|
190
|
-
// No mirror is created (legacy systemRoot === contentRoot, nothing to copy).
|
|
191
|
-
expect(readFileSync(join(tmp, 'storyboard.config.json'), 'utf8')).toBe('{"port":1234}')
|
|
192
|
-
})
|
|
193
|
-
|
|
194
|
-
it('copies content-root config into the system root in the backstage layout', () => {
|
|
195
|
-
mkdirSync(join(tmp, SYSTEM_DIR))
|
|
196
|
-
writeFileSync(join(tmp, 'storyboard.config.json'), '{"port":4321}')
|
|
197
|
-
const loc = findSystemRoot(tmp)
|
|
198
|
-
expect(mirrorSystemConfig(loc)).toBe(true)
|
|
199
|
-
expect(readFileSync(join(tmp, SYSTEM_DIR, 'storyboard.config.json'), 'utf8')).toBe('{"port":4321}')
|
|
200
|
-
})
|
|
201
|
-
|
|
202
|
-
it('returns false when there is no content-root config to mirror', () => {
|
|
203
|
-
mkdirSync(join(tmp, SYSTEM_DIR))
|
|
204
|
-
expect(mirrorSystemConfig(findSystemRoot(tmp))).toBe(false)
|
|
205
|
-
})
|
|
206
|
-
})
|
|
207
|
-
|
|
208
185
|
describe('ensureContentNodeModulesLink', () => {
|
|
209
186
|
it('is a no-op in the legacy layout', () => {
|
|
210
187
|
expect(ensureContentNodeModulesLink(findSystemRoot(tmp))).toBe(false)
|
|
@@ -340,6 +340,22 @@ describe('storyboard-scaffold integration — backstage layout', () => {
|
|
|
340
340
|
// vitest.config.js has no scaffold/backstage/ variant → canonical is used.
|
|
341
341
|
expect(fs.existsSync(path.join(backstage, 'vitest.config.js'))).toBe(true)
|
|
342
342
|
}, 20000)
|
|
343
|
+
|
|
344
|
+
it('aliases `@` to the content root so author imports (@/components/...) resolve', () => {
|
|
345
|
+
runScaffolder(backstage)
|
|
346
|
+
const vite = fs.readFileSync(path.join(backstage, 'vite.config.js'), 'utf-8')
|
|
347
|
+
// In the backstage layout content lives ABOVE the Vite root, so the author
|
|
348
|
+
// `@` alias must point at the content root — not `.backstage/src` (which
|
|
349
|
+
// would 500/resolve-fail every `@/components/...` import).
|
|
350
|
+
expect(vite).toContain("'@': contentRoot,")
|
|
351
|
+
expect(vite).not.toContain("'@': path.resolve(__dirname, './src')")
|
|
352
|
+
}, 20000)
|
|
353
|
+
|
|
354
|
+
it('writes the backstage tsconfig variant mapping @/* to the content root', () => {
|
|
355
|
+
runScaffolder(backstage)
|
|
356
|
+
const ts = JSON.parse(fs.readFileSync(path.join(backstage, 'tsconfig.json'), 'utf-8'))
|
|
357
|
+
expect(ts.compilerOptions.paths['@/*']).toEqual(['../*'])
|
|
358
|
+
}, 20000)
|
|
343
359
|
})
|
|
344
360
|
|
|
345
361
|
// ---------------------------------------------------------------------------
|