@dfosco/storyboard-react 4.0.0-beta.2 → 4.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 +3 -3
- package/src/vite/data-plugin.js +105 -11
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dfosco/storyboard-react",
|
|
3
|
-
"version": "4.0.0-beta.
|
|
3
|
+
"version": "4.0.0-beta.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"dependencies": {
|
|
6
|
-
"@dfosco/storyboard-core": "4.0.0-beta.
|
|
7
|
-
"@dfosco/tiny-canvas": "4.0.0-beta.
|
|
6
|
+
"@dfosco/storyboard-core": "4.0.0-beta.4",
|
|
7
|
+
"@dfosco/tiny-canvas": "4.0.0-beta.4",
|
|
8
8
|
"@neodrag/react": "^2.3.1",
|
|
9
9
|
"glob": "^11.0.0",
|
|
10
10
|
"jsonc-parser": "^3.3.1"
|
package/src/vite/data-plugin.js
CHANGED
|
@@ -155,11 +155,99 @@ function getLastModified(root, dirPath) {
|
|
|
155
155
|
}
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
+
/**
|
|
159
|
+
* Batch-fetch git metadata (author + lastModified) for multiple files in a
|
|
160
|
+
* single subprocess, avoiding per-file git overhead during startup.
|
|
161
|
+
*
|
|
162
|
+
* Returns a Map<absPath, { gitAuthor: string|null, lastModified: string|null }>
|
|
163
|
+
*/
|
|
164
|
+
function batchGitMetadata(root, filePaths) {
|
|
165
|
+
const result = new Map()
|
|
166
|
+
if (filePaths.length === 0) return result
|
|
167
|
+
|
|
168
|
+
// Initialize all entries
|
|
169
|
+
for (const fp of filePaths) {
|
|
170
|
+
result.set(fp, { gitAuthor: null, lastModified: null })
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
try {
|
|
174
|
+
// Batch lastModified: one git log call with all paths
|
|
175
|
+
// git log -1 gives the most recent commit touching any of these paths,
|
|
176
|
+
// but we need per-path data. Use --name-only to correlate.
|
|
177
|
+
// For efficiency, use a single git log with --format and --name-only
|
|
178
|
+
// that outputs one record per commit touching these files.
|
|
179
|
+
const allDirs = [...new Set(filePaths.map(fp => path.dirname(fp)))]
|
|
180
|
+
const dirsArg = allDirs.map(d => `"${d}"`).join(' ')
|
|
181
|
+
|
|
182
|
+
// Get lastModified per directory in one call using git log --format
|
|
183
|
+
// We output "MARKER<sep>dir<sep>date" per commit, then take the latest per dir.
|
|
184
|
+
const logResult = execSync(
|
|
185
|
+
`git log --format="%aI" --name-only -- ${dirsArg}`,
|
|
186
|
+
{ cwd: root, encoding: 'utf-8', timeout: 10000, maxBuffer: 1024 * 1024 },
|
|
187
|
+
).trim()
|
|
188
|
+
|
|
189
|
+
if (logResult) {
|
|
190
|
+
// Parse: alternating date lines and filename lines separated by blank lines
|
|
191
|
+
const blocks = logResult.split('\n\n')
|
|
192
|
+
const dirDates = new Map() // dir → most recent date
|
|
193
|
+
for (const block of blocks) {
|
|
194
|
+
const lines = block.split('\n').filter(Boolean)
|
|
195
|
+
if (lines.length < 2) continue
|
|
196
|
+
const date = lines[0]
|
|
197
|
+
for (let li = 1; li < lines.length; li++) {
|
|
198
|
+
const fileLine = lines[li].trim()
|
|
199
|
+
if (!fileLine) continue
|
|
200
|
+
const dir = path.dirname(path.resolve(root, fileLine))
|
|
201
|
+
if (!dirDates.has(dir)) {
|
|
202
|
+
dirDates.set(dir, date)
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
for (const fp of filePaths) {
|
|
207
|
+
const dir = path.dirname(fp)
|
|
208
|
+
const entry = result.get(fp)
|
|
209
|
+
if (dirDates.has(dir) && entry) {
|
|
210
|
+
entry.lastModified = dirDates.get(dir)
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
} catch { /* git not available or failed — leave nulls */ }
|
|
215
|
+
|
|
216
|
+
// Batch gitAuthor: use git log for each file's creation author.
|
|
217
|
+
// Unfortunately --follow --diff-filter=A doesn't combine well with multiple
|
|
218
|
+
// paths, so batch them in a single shell invocation using a for loop.
|
|
219
|
+
try {
|
|
220
|
+
const relPaths = filePaths.map(fp => path.relative(root, fp))
|
|
221
|
+
// Build a shell script that outputs "PATH<tab>AUTHOR" per file
|
|
222
|
+
const cmds = relPaths.map(rp =>
|
|
223
|
+
`echo -n "${rp}\\t"; git log --follow --diff-filter=A --format="%aN" -- "${rp}" | tail -1`
|
|
224
|
+
).join('; ')
|
|
225
|
+
const authorResult = execSync(cmds, {
|
|
226
|
+
cwd: root, encoding: 'utf-8', timeout: 10000, shell: true, maxBuffer: 1024 * 1024,
|
|
227
|
+
}).trim()
|
|
228
|
+
|
|
229
|
+
if (authorResult) {
|
|
230
|
+
for (const line of authorResult.split('\n')) {
|
|
231
|
+
const tabIdx = line.indexOf('\t')
|
|
232
|
+
if (tabIdx < 0) continue
|
|
233
|
+
const relPath = line.slice(0, tabIdx)
|
|
234
|
+
const author = line.slice(tabIdx + 1).trim()
|
|
235
|
+
if (!author) continue
|
|
236
|
+
const absPath2 = path.resolve(root, relPath)
|
|
237
|
+
const entry = result.get(absPath2)
|
|
238
|
+
if (entry) entry.gitAuthor = author
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
} catch { /* git not available */ }
|
|
242
|
+
|
|
243
|
+
return result
|
|
244
|
+
}
|
|
245
|
+
|
|
158
246
|
/**
|
|
159
247
|
* Scan the repo for all data files, validate uniqueness, return the index.
|
|
160
248
|
*/
|
|
161
249
|
function buildIndex(root) {
|
|
162
|
-
const ignore = ['node_modules/**', 'dist/**', '.git/**']
|
|
250
|
+
const ignore = ['node_modules/**', 'dist/**', '.git/**', '.worktrees/**', 'public/**']
|
|
163
251
|
const files = globSync(GLOB_PATTERN, { cwd: root, ignore, absolute: false })
|
|
164
252
|
const canvasFiles = globSync(CANVAS_GLOB_PATTERN, { cwd: root, ignore, absolute: false })
|
|
165
253
|
|
|
@@ -339,6 +427,13 @@ function generateModule({ index, protoFolders, flowRoutes, canvasRoutes }, root)
|
|
|
339
427
|
const resolvedFlowRoutes = {} // flow name → resolved route (for multi-flow logging)
|
|
340
428
|
let i = 0
|
|
341
429
|
|
|
430
|
+
// Batch-fetch git metadata for all prototype + canvas files in 1-2 subprocesses
|
|
431
|
+
const gitPaths = [
|
|
432
|
+
...Object.values(index.prototype || {}),
|
|
433
|
+
...Object.values(index.canvas || {}),
|
|
434
|
+
]
|
|
435
|
+
const gitMeta = batchGitMetadata(root, gitPaths)
|
|
436
|
+
|
|
342
437
|
for (const suffix of INDEX_KEYS) {
|
|
343
438
|
for (const [name, absPath] of Object.entries(index[suffix])) {
|
|
344
439
|
const varName = `_d${i++}`
|
|
@@ -349,18 +444,17 @@ function generateModule({ index, protoFolders, flowRoutes, canvasRoutes }, root)
|
|
|
349
444
|
|
|
350
445
|
// Auto-fill gitAuthor for prototype metadata from git history
|
|
351
446
|
if (suffix === 'prototype' && parsed && !parsed.gitAuthor) {
|
|
352
|
-
const
|
|
353
|
-
if (gitAuthor) {
|
|
354
|
-
parsed = { ...parsed, gitAuthor }
|
|
447
|
+
const meta = gitMeta.get(absPath)
|
|
448
|
+
if (meta?.gitAuthor) {
|
|
449
|
+
parsed = { ...parsed, gitAuthor: meta.gitAuthor }
|
|
355
450
|
}
|
|
356
451
|
}
|
|
357
452
|
|
|
358
453
|
// Auto-fill lastModified from git history for prototypes
|
|
359
454
|
if (suffix === 'prototype' && parsed) {
|
|
360
|
-
const
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
parsed = { ...parsed, lastModified }
|
|
455
|
+
const meta = gitMeta.get(absPath)
|
|
456
|
+
if (meta?.lastModified) {
|
|
457
|
+
parsed = { ...parsed, lastModified: meta.lastModified }
|
|
364
458
|
}
|
|
365
459
|
}
|
|
366
460
|
|
|
@@ -399,9 +493,9 @@ function generateModule({ index, protoFolders, flowRoutes, canvasRoutes }, root)
|
|
|
399
493
|
|
|
400
494
|
// Auto-fill gitAuthor for canvas metadata from git history
|
|
401
495
|
if (suffix === 'canvas' && parsed && !parsed.gitAuthor) {
|
|
402
|
-
const
|
|
403
|
-
if (gitAuthor) {
|
|
404
|
-
parsed = { ...parsed, gitAuthor }
|
|
496
|
+
const meta = gitMeta.get(absPath)
|
|
497
|
+
if (meta?.gitAuthor) {
|
|
498
|
+
parsed = { ...parsed, gitAuthor: meta.gitAuthor }
|
|
405
499
|
}
|
|
406
500
|
}
|
|
407
501
|
|