spine-framework 0.1.45 → 0.1.47
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.
|
@@ -28,9 +28,10 @@
|
|
|
28
28
|
*/
|
|
29
29
|
|
|
30
30
|
import type { Command } from 'commander'
|
|
31
|
-
import { existsSync, readFileSync, readdirSync } from 'fs'
|
|
31
|
+
import { existsSync, readFileSync, readdirSync, cpSync, mkdirSync } from 'fs'
|
|
32
32
|
import { resolve, dirname } from 'path'
|
|
33
33
|
import { fileURLToPath } from 'url'
|
|
34
|
+
import { execSync } from 'child_process'
|
|
34
35
|
import { adminDb } from '../../functions/_shared/index.ts'
|
|
35
36
|
|
|
36
37
|
const __filename = fileURLToPath(import.meta.url)
|
|
@@ -286,14 +287,102 @@ async function recordInstallation(appSlug: string, accountId: string | null, dry
|
|
|
286
287
|
}
|
|
287
288
|
}
|
|
288
289
|
|
|
290
|
+
function isNpmPackage(slug: string): boolean {
|
|
291
|
+
return slug.includes('/') || slug.startsWith('spine-framework-') || slug.startsWith('@')
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function resolveSlugFromPackage(pkg: string): string {
|
|
295
|
+
const manifest = resolve(PROJECT_ROOT, `node_modules/${pkg}/spine-manifest.json`)
|
|
296
|
+
if (existsSync(manifest)) {
|
|
297
|
+
try {
|
|
298
|
+
return JSON.parse(readFileSync(manifest, 'utf8')).slug
|
|
299
|
+
} catch { /* fall through */ }
|
|
300
|
+
}
|
|
301
|
+
const pkgJson = resolve(PROJECT_ROOT, `node_modules/${pkg}/package.json`)
|
|
302
|
+
if (existsSync(pkgJson)) {
|
|
303
|
+
try {
|
|
304
|
+
const meta = JSON.parse(readFileSync(pkgJson, 'utf8'))
|
|
305
|
+
if (meta.spine?.slug) return meta.spine.slug
|
|
306
|
+
} catch { /* fall through */ }
|
|
307
|
+
}
|
|
308
|
+
return pkg.replace(/^spine-framework-/, '').replace(/^@[^/]+\//, '')
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
async function checkPrerequisites(_slug: string): Promise<void> {
|
|
312
|
+
// Apps are independently installable — no hardcoded prereqs.
|
|
313
|
+
// Future: read prereqs from the app's manifest.json if needed.
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function copyAppFiles(pkg: string, slug: string): void {
|
|
317
|
+
const pkgDir = resolve(PROJECT_ROOT, `node_modules/${pkg}`)
|
|
318
|
+
const appDirs = ['pages', 'components', 'seed', 'api']
|
|
319
|
+
const appFiles = ['index.tsx', 'manifest.json', 'package.json']
|
|
320
|
+
const targetAppDir = resolve(PROJECT_ROOT, `custom/apps/${slug}`)
|
|
321
|
+
const targetFunctionsDir = resolve(PROJECT_ROOT, 'custom/functions')
|
|
322
|
+
|
|
323
|
+
mkdirSync(targetAppDir, { recursive: true })
|
|
324
|
+
mkdirSync(targetFunctionsDir, { recursive: true })
|
|
325
|
+
|
|
326
|
+
for (const file of appFiles) {
|
|
327
|
+
const src = resolve(pkgDir, file)
|
|
328
|
+
if (existsSync(src)) {
|
|
329
|
+
cpSync(src, resolve(targetAppDir, file), { force: true })
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
for (const dir of appDirs) {
|
|
334
|
+
const src = resolve(pkgDir, dir)
|
|
335
|
+
if (existsSync(src)) {
|
|
336
|
+
cpSync(src, resolve(targetAppDir, dir), { recursive: true, force: true })
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const functionsDir = resolve(pkgDir, 'functions')
|
|
341
|
+
if (existsSync(functionsDir)) {
|
|
342
|
+
cpSync(functionsDir, targetFunctionsDir, { recursive: true, force: true })
|
|
343
|
+
const count = readdirSync(targetFunctionsDir).filter(f => f.endsWith('.ts')).length
|
|
344
|
+
console.log(` ✓ Copied functions (${count} files) → custom/functions/`)
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
console.log(` ✓ Ejected app files → custom/apps/${slug}/`)
|
|
348
|
+
}
|
|
349
|
+
|
|
289
350
|
async function installApp(slug: string, options: InstallOptions): Promise<void> {
|
|
290
351
|
console.log(`\n📦 Installing app '${slug}'...\n`)
|
|
291
352
|
|
|
292
|
-
|
|
293
|
-
|
|
353
|
+
let resolvedSlug = slug
|
|
354
|
+
let packageName: string | null = null
|
|
355
|
+
|
|
356
|
+
if (isNpmPackage(slug)) {
|
|
357
|
+
packageName = slug
|
|
358
|
+
console.log(`📥 Step 1: Installing npm package...`)
|
|
359
|
+
const pkgDir = resolve(PROJECT_ROOT, `node_modules/${slug}`)
|
|
360
|
+
if (!existsSync(pkgDir)) {
|
|
361
|
+
try {
|
|
362
|
+
console.log(` Running: npm install ${slug}`)
|
|
363
|
+
execSync(`npm install ${slug}`, { cwd: PROJECT_ROOT, stdio: 'inherit' })
|
|
364
|
+
console.log(` ✓ Package installed`)
|
|
365
|
+
} catch (err: any) {
|
|
366
|
+
console.error(` ❌ npm install failed: ${err.message}`)
|
|
367
|
+
process.exit(1)
|
|
368
|
+
}
|
|
369
|
+
} else {
|
|
370
|
+
console.log(` ⏭️ Package already in node_modules`)
|
|
371
|
+
}
|
|
372
|
+
resolvedSlug = resolveSlugFromPackage(slug)
|
|
373
|
+
console.log(` ✓ App slug: ${resolvedSlug}`)
|
|
374
|
+
|
|
375
|
+
console.log(`\n📁 Step 2: Ejecting app files...`)
|
|
376
|
+
copyAppFiles(slug, resolvedSlug)
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// Check prerequisites (e.g. portal requires cortex)
|
|
380
|
+
await checkPrerequisites(resolvedSlug)
|
|
381
|
+
|
|
382
|
+
const appDir = resolve(PROJECT_ROOT, `custom/apps/${resolvedSlug}`)
|
|
294
383
|
if (!existsSync(appDir)) {
|
|
295
|
-
console.error(` ❌ App directory not found: custom/apps/${
|
|
296
|
-
console.error(` Install the npm package first or create the app with 'spine-framework create-app ${
|
|
384
|
+
console.error(` ❌ App directory not found: custom/apps/${resolvedSlug}`)
|
|
385
|
+
console.error(` Install the npm package first or create the app with 'spine-framework create-app ${resolvedSlug}'`)
|
|
297
386
|
process.exit(1)
|
|
298
387
|
}
|
|
299
388
|
|
|
@@ -313,7 +402,7 @@ async function installApp(slug: string, options: InstallOptions): Promise<void>
|
|
|
313
402
|
}
|
|
314
403
|
|
|
315
404
|
// 3. Ensure app record in DB
|
|
316
|
-
const appId = await ensureAppRecord(
|
|
405
|
+
const appId = await ensureAppRecord(resolvedSlug, appName)
|
|
317
406
|
|
|
318
407
|
// 4. Process seed files
|
|
319
408
|
const seedDir = resolve(appDir, 'seed')
|
|
@@ -364,12 +453,12 @@ async function installApp(slug: string, options: InstallOptions): Promise<void>
|
|
|
364
453
|
|
|
365
454
|
// 5. Record installation
|
|
366
455
|
console.log(`\n📝 Recording installation...`)
|
|
367
|
-
await recordInstallation(
|
|
456
|
+
await recordInstallation(resolvedSlug, options.account || null, options.dryRun)
|
|
368
457
|
|
|
369
458
|
console.log(`\n✅ App '${appName}' installed successfully!`)
|
|
370
459
|
console.log(`\n Next steps:`)
|
|
371
460
|
console.log(` 1. npm run assemble && netlify dev`)
|
|
372
|
-
console.log(` 2. Navigate to /${
|
|
461
|
+
console.log(` 2. Navigate to /${resolvedSlug} in your browser`)
|
|
373
462
|
}
|
|
374
463
|
|
|
375
464
|
export function registerInstallAppCommands(program: Command) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"install-app.d.ts","sourceRoot":"","sources":["../../../.framework/cli/commands/install-app.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"install-app.d.ts","sourceRoot":"","sources":["../../../.framework/cli/commands/install-app.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAkbxC,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,OAAO,QAgB1D"}
|