@mr.dj2u/cli 0.1.11 → 0.1.13
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/bundles/claude-code/.claude-plugin/plugin.json +1 -1
- package/bundles/claude-code/.mcp.json +1 -1
- package/bundles/claude-code/commands/create-expo-super-stack.md +10 -7
- package/bundles/claude-code/skills/create-expo-super-stack/SKILL.md +10 -7
- package/bundles/claude-code/skills/super-stack-startup/SKILL.md +1 -1
- package/bundles/codex/.codex-plugin/plugin.json +1 -1
- package/bundles/codex/.mcp.json +1 -1
- package/bundles/codex/commands/create-expo-super-stack.md +10 -7
- package/bundles/codex/skills/super-stack-startup/SKILL.md +1 -1
- package/bundles/codex/skills/workflow-create-expo-super-stack/SKILL.md +10 -7
- package/bundles/vscode-copilot/.github/prompts/create-expo-super-stack.prompt.md +10 -7
- package/bundles/vscode-copilot/.github/skills/super-stack-startup/SKILL.md +1 -1
- package/bundles/vscode-copilot/.vscode/mcp.json +1 -1
- package/bundles/vscode-copilot/user/.copilot/skills/super-stack-startup/SKILL.md +1 -1
- package/bundles/vscode-copilot/user/.copilot/skills/workflow-create-expo-super-stack/SKILL.md +10 -7
- package/dist/cess-intake.d.ts +16 -1
- package/dist/cess-intake.d.ts.map +1 -1
- package/dist/cess-intake.js +677 -34
- package/dist/cess-intake.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +14 -4
- package/dist/cli.js.map +1 -1
- package/dist/commands/agent.d.ts.map +1 -1
- package/dist/commands/agent.js +3 -1
- package/dist/commands/agent.js.map +1 -1
- package/dist/commands/mcp-install.d.ts +3 -2
- package/dist/commands/mcp-install.d.ts.map +1 -1
- package/dist/commands/mcp-install.js +17 -44
- package/dist/commands/mcp-install.js.map +1 -1
- package/dist/commands/onboard.d.ts +9 -3
- package/dist/commands/onboard.d.ts.map +1 -1
- package/dist/commands/onboard.js +28 -10
- package/dist/commands/onboard.js.map +1 -1
- package/dist/commands/roadmap.d.ts +6 -0
- package/dist/commands/roadmap.d.ts.map +1 -0
- package/dist/commands/roadmap.js +54 -0
- package/dist/commands/roadmap.js.map +1 -0
- package/dist/project-memory.d.ts +8 -1
- package/dist/project-memory.d.ts.map +1 -1
- package/dist/project-memory.js +189 -93
- package/dist/project-memory.js.map +1 -1
- package/dist/roadmap.d.ts +71 -0
- package/dist/roadmap.d.ts.map +1 -0
- package/dist/roadmap.js +865 -0
- package/dist/roadmap.js.map +1 -0
- package/dist/stylist-theme.d.ts.map +1 -1
- package/dist/stylist-theme.js +1 -20
- package/dist/stylist-theme.js.map +1 -1
- package/package.json +7 -3
- package/templates/embedded-fonts.template.ts +72 -72
- package/templates/expo-sdk-56-screen-universal.template.tsx +709 -709
- package/templates/project/guidelines.md +4 -5
- package/templates/stylist-screen.template.tsx +3456 -3446
package/dist/cess-intake.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { AGENT_DERIVED_CORE_FLOWS, CUSTOM_BACKEND_EXPLANATION, DATA_NEED_OPTIONS, DATA_START_EXPLANATION, EAS_EXPLANATION, EAS_USE_OPTIONS, EXPO_SERVER_ADAPTER_EXPLANATION, OTHER_DATA_NEEDS, PLATFORM_OPTIONS, TEST_TO_MAIN_EXPLANATION, defaultOnboardPlan, deriveDeployedServer, formatDataNeedsSelection, } from './commands/onboard.js';
|
|
3
3
|
const DEFAULT_PROJECT_NAME = 'my-expo-app';
|
|
4
|
+
const DEFAULT_DISPLAY_APP_NAME = 'My Expo App';
|
|
4
5
|
const STACK_DEFAULTS = {
|
|
5
6
|
scriptLanguage: 'typescript',
|
|
6
7
|
packageManager: 'npm',
|
|
@@ -20,9 +21,9 @@ const CESS_QUESTIONS = [
|
|
|
20
21
|
},
|
|
21
22
|
{
|
|
22
23
|
id: 'appName',
|
|
23
|
-
prompt: 'What
|
|
24
|
+
prompt: 'What is the name of your app?',
|
|
24
25
|
kind: 'text',
|
|
25
|
-
defaultValue: (context) => context.
|
|
26
|
+
defaultValue: (context) => context.appDisplayName,
|
|
26
27
|
},
|
|
27
28
|
{
|
|
28
29
|
id: 'scriptLanguage',
|
|
@@ -112,12 +113,6 @@ const CESS_QUESTIONS = [
|
|
|
112
113
|
],
|
|
113
114
|
defaultValue: () => STACK_DEFAULTS.easSetup,
|
|
114
115
|
},
|
|
115
|
-
{
|
|
116
|
-
id: 'displayAppName',
|
|
117
|
-
prompt: 'What display app name should MDS use in project memory?',
|
|
118
|
-
kind: 'text',
|
|
119
|
-
defaultValue: (context) => context.onboardAnswers.appName,
|
|
120
|
-
},
|
|
121
116
|
{
|
|
122
117
|
id: 'audience',
|
|
123
118
|
prompt: 'Who is this app for?',
|
|
@@ -267,16 +262,6 @@ const CESS_QUESTIONS = [
|
|
|
267
262
|
],
|
|
268
263
|
defaultValue: (context) => context.onboardAnswers.includeCreateExpoComponents,
|
|
269
264
|
},
|
|
270
|
-
{
|
|
271
|
-
id: 'useLatestExpoSdk',
|
|
272
|
-
prompt: 'Use the latest Expo SDK even if Expo Go availability may lag?',
|
|
273
|
-
kind: 'single-select',
|
|
274
|
-
options: () => [
|
|
275
|
-
{ value: true, label: 'Yes', hint: 'Default' },
|
|
276
|
-
{ value: false, label: 'No' },
|
|
277
|
-
],
|
|
278
|
-
defaultValue: (context) => context.onboardAnswers.useLatestExpoSdk,
|
|
279
|
-
},
|
|
280
265
|
{
|
|
281
266
|
id: 'usesExpoUi',
|
|
282
267
|
prompt: 'Use Expo UI for native-feeling screens?',
|
|
@@ -363,22 +348,676 @@ const CESS_QUESTIONS = [
|
|
|
363
348
|
defaultValue: () => false,
|
|
364
349
|
},
|
|
365
350
|
];
|
|
351
|
+
const CESS_SNAPSHOT_START = '<!-- MDS_CESS_SNAPSHOT_START -->';
|
|
352
|
+
const CESS_SNAPSHOT_END = '<!-- MDS_CESS_SNAPSHOT_END -->';
|
|
353
|
+
export function extractCessInfoFromMarkdown(input) {
|
|
354
|
+
const infoMarkdown = normalizeLineEndings(input.infoMarkdown);
|
|
355
|
+
const sections = parseMarkdownSections(infoMarkdown);
|
|
356
|
+
const evidence = {};
|
|
357
|
+
const prefilledAnswers = {};
|
|
358
|
+
const ambiguousQuestionIds = new Set();
|
|
359
|
+
const usedSections = new Set();
|
|
360
|
+
const explicitAppName = normalizeText(input.appName);
|
|
361
|
+
let derivedDisplayName = explicitAppName;
|
|
362
|
+
let derivedFolderSlug = explicitAppName ? slugifyAppName(explicitAppName) : undefined;
|
|
363
|
+
const recordEvidence = (key, note) => {
|
|
364
|
+
evidence[key] = [...(evidence[key] ?? []), note];
|
|
365
|
+
};
|
|
366
|
+
const assignValue = (id, value, note) => {
|
|
367
|
+
const normalized = normalizeExtractedAnswerValue(id, value);
|
|
368
|
+
if (normalized === undefined) {
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
if (ambiguousQuestionIds.has(id)) {
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
const current = prefilledAnswers[id];
|
|
375
|
+
if (current !== undefined && !areAnswerValuesEquivalent(id, current, normalized)) {
|
|
376
|
+
delete prefilledAnswers[id];
|
|
377
|
+
ambiguousQuestionIds.add(id);
|
|
378
|
+
recordEvidence(id, `${note} (conflicts with earlier extracted value)`);
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
prefilledAnswers[id] = normalized;
|
|
382
|
+
recordEvidence(id, note);
|
|
383
|
+
};
|
|
384
|
+
const snapshot = parseCessSnapshot(infoMarkdown);
|
|
385
|
+
if (snapshot) {
|
|
386
|
+
recordEvidence('snapshot', 'Parsed machine-readable MDS snapshot block.');
|
|
387
|
+
if (!derivedDisplayName) {
|
|
388
|
+
derivedDisplayName = normalizeText(snapshot.displayAppName);
|
|
389
|
+
}
|
|
390
|
+
if (!derivedFolderSlug) {
|
|
391
|
+
derivedFolderSlug = normalizeText(snapshot.folderSlug) ?? slugifyAppName(snapshot.displayAppName);
|
|
392
|
+
}
|
|
393
|
+
const snapshotAnswers = normalizeCessIntakeAnswers(snapshot.answers);
|
|
394
|
+
for (const [key, value] of Object.entries(snapshotAnswers)) {
|
|
395
|
+
assignValue(key, value, 'MDS snapshot');
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
const title = extractProjectTitle(infoMarkdown);
|
|
399
|
+
if (!derivedDisplayName && title) {
|
|
400
|
+
derivedDisplayName = title;
|
|
401
|
+
recordEvidence('appName', `Derived app name from title: ${title}`);
|
|
402
|
+
}
|
|
403
|
+
if (!derivedFolderSlug && derivedDisplayName) {
|
|
404
|
+
derivedFolderSlug = slugifyAppName(derivedDisplayName);
|
|
405
|
+
}
|
|
406
|
+
const targetUsers = sections.get('Target Users');
|
|
407
|
+
if (targetUsers) {
|
|
408
|
+
usedSections.add('Target Users');
|
|
409
|
+
assignValue('audience', normalizeSectionText(targetUsers), 'Target Users section');
|
|
410
|
+
}
|
|
411
|
+
else {
|
|
412
|
+
const overview = sections.get('Overview');
|
|
413
|
+
const overviewAudience = extractAudienceFromOverview(overview);
|
|
414
|
+
if (overviewAudience) {
|
|
415
|
+
usedSections.add('Overview');
|
|
416
|
+
assignValue('audience', overviewAudience, 'Overview section');
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
const coreUserFlows = sections.get('Core User Flows');
|
|
420
|
+
if (coreUserFlows) {
|
|
421
|
+
usedSections.add('Core User Flows');
|
|
422
|
+
assignValue('coreFlows', normalizeListSection(coreUserFlows), 'Core User Flows section');
|
|
423
|
+
}
|
|
424
|
+
const mustIncludeScreens = sections.get('Must-Include Screens Or Flows');
|
|
425
|
+
if (mustIncludeScreens) {
|
|
426
|
+
usedSections.add('Must-Include Screens Or Flows');
|
|
427
|
+
assignValue('screens', normalizeListSection(mustIncludeScreens), 'Must-Include Screens Or Flows section');
|
|
428
|
+
}
|
|
429
|
+
const dataAndBackend = sections.get('Data And Backend');
|
|
430
|
+
if (dataAndBackend) {
|
|
431
|
+
usedSections.add('Data And Backend');
|
|
432
|
+
const inferredDataNeeds = inferDataNeedSelections(dataAndBackend);
|
|
433
|
+
if (inferredDataNeeds.length > 0) {
|
|
434
|
+
assignValue('dataNeedSelections', inferredDataNeeds, 'Data And Backend section');
|
|
435
|
+
}
|
|
436
|
+
const dataStart = inferDataStart(dataAndBackend);
|
|
437
|
+
if (dataStart) {
|
|
438
|
+
assignValue('dataStart', dataStart, 'Data And Backend section');
|
|
439
|
+
}
|
|
440
|
+
const authBackend = inferAuthBackend(dataAndBackend);
|
|
441
|
+
if (authBackend) {
|
|
442
|
+
assignValue('authBackend', authBackend, 'Data And Backend section');
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
const platforms = sections.get('Platforms');
|
|
446
|
+
if (platforms) {
|
|
447
|
+
usedSections.add('Platforms');
|
|
448
|
+
extractPlatformDecisions(platforms, assignValue);
|
|
449
|
+
}
|
|
450
|
+
const packageChoices = sections.get('Package Choices');
|
|
451
|
+
if (packageChoices) {
|
|
452
|
+
usedSections.add('Package Choices');
|
|
453
|
+
extractPackageChoices(packageChoices, assignValue);
|
|
454
|
+
}
|
|
455
|
+
const releaseStrategy = sections.get('Release Strategy');
|
|
456
|
+
if (releaseStrategy) {
|
|
457
|
+
usedSections.add('Release Strategy');
|
|
458
|
+
const deploymentTarget = extractBulletValue(releaseStrategy, 'Deployment plan');
|
|
459
|
+
if (deploymentTarget) {
|
|
460
|
+
assignValue('deploymentTarget', deploymentTarget, 'Release Strategy section');
|
|
461
|
+
}
|
|
462
|
+
const easUses = inferEasUses(extractBulletValue(releaseStrategy, 'EAS usage') ?? releaseStrategy);
|
|
463
|
+
if (easUses.length > 0) {
|
|
464
|
+
assignValue('easUses', easUses, 'Release Strategy section');
|
|
465
|
+
assignValue('easSetup', true, 'Release Strategy section');
|
|
466
|
+
}
|
|
467
|
+
const testToMain = parseBooleanValue(extractBulletValue(releaseStrategy, 'Test-to-main safeguards'));
|
|
468
|
+
if (typeof testToMain === 'boolean') {
|
|
469
|
+
assignValue('testToMainSafeguards', testToMain, 'Release Strategy section');
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
const techStackSection = sections.get('Tech Stack & MDS Onboarding');
|
|
473
|
+
if (techStackSection) {
|
|
474
|
+
usedSections.add('Tech Stack & MDS Onboarding');
|
|
475
|
+
extractTechStackDecisions(techStackSection, assignValue);
|
|
476
|
+
}
|
|
477
|
+
const onboardingDecisionsSection = sections.get('Onboarding Decisions');
|
|
478
|
+
if (onboardingDecisionsSection) {
|
|
479
|
+
usedSections.add('Onboarding Decisions');
|
|
480
|
+
extractOnboardingDecisionLines(onboardingDecisionsSection, assignValue);
|
|
481
|
+
}
|
|
482
|
+
if (derivedDisplayName) {
|
|
483
|
+
assignValue('displayAppName', derivedDisplayName, 'Derived app name');
|
|
484
|
+
}
|
|
485
|
+
const preservedNotes = [...sections.entries()]
|
|
486
|
+
.filter(([heading, body]) => !usedSections.has(heading) && normalizeSectionText(body))
|
|
487
|
+
.map(([heading, body]) => `## ${heading}\n\n${normalizeSectionText(body)}`);
|
|
488
|
+
const missingQuestionIds = validateCessGenerationReadiness({
|
|
489
|
+
parentDir: input.parentDir,
|
|
490
|
+
appName: derivedFolderSlug,
|
|
491
|
+
answers: prefilledAnswers,
|
|
492
|
+
cwd: input.cwd,
|
|
493
|
+
});
|
|
494
|
+
return {
|
|
495
|
+
prefilledAnswers,
|
|
496
|
+
derivedDisplayName,
|
|
497
|
+
derivedFolderSlug,
|
|
498
|
+
missingQuestionIds,
|
|
499
|
+
ambiguousQuestionIds: Array.from(ambiguousQuestionIds),
|
|
500
|
+
evidence,
|
|
501
|
+
preservedNotes,
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
function parseCessSnapshot(infoMarkdown) {
|
|
505
|
+
const match = new RegExp(`${escapeRegExp(CESS_SNAPSHOT_START)}([\\s\\S]*?)${escapeRegExp(CESS_SNAPSHOT_END)}`, 'u').exec(infoMarkdown);
|
|
506
|
+
if (!match?.[1]) {
|
|
507
|
+
return null;
|
|
508
|
+
}
|
|
509
|
+
const rawBlock = match[1].trim();
|
|
510
|
+
const jsonMatch = /```json\s*([\s\S]*?)```/u.exec(rawBlock);
|
|
511
|
+
const jsonText = (jsonMatch?.[1] ?? rawBlock).trim();
|
|
512
|
+
if (!jsonText) {
|
|
513
|
+
return null;
|
|
514
|
+
}
|
|
515
|
+
try {
|
|
516
|
+
const parsed = JSON.parse(jsonText);
|
|
517
|
+
const answers = typeof parsed.answers === 'object' && parsed.answers && !Array.isArray(parsed.answers)
|
|
518
|
+
? parsed.answers
|
|
519
|
+
: undefined;
|
|
520
|
+
return {
|
|
521
|
+
displayAppName: normalizeText(parsed.displayAppName),
|
|
522
|
+
folderSlug: normalizeText(parsed.folderSlug),
|
|
523
|
+
answers,
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
catch {
|
|
527
|
+
return null;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
function extractProjectTitle(infoMarkdown) {
|
|
531
|
+
const match = /^#\s+(.+?)\s*$/mu.exec(infoMarkdown);
|
|
532
|
+
if (!match?.[1]) {
|
|
533
|
+
return undefined;
|
|
534
|
+
}
|
|
535
|
+
return normalizeProjectTitle(match[1]);
|
|
536
|
+
}
|
|
537
|
+
function parseMarkdownSections(markdown) {
|
|
538
|
+
const sections = new Map();
|
|
539
|
+
const matches = [...markdown.matchAll(/^##\s+(.+?)\s*$/gmu)];
|
|
540
|
+
for (let index = 0; index < matches.length; index += 1) {
|
|
541
|
+
const match = matches[index];
|
|
542
|
+
const next = matches[index + 1];
|
|
543
|
+
if (!match) {
|
|
544
|
+
continue;
|
|
545
|
+
}
|
|
546
|
+
const heading = match?.[1]?.trim();
|
|
547
|
+
if (!heading || match.index === undefined) {
|
|
548
|
+
continue;
|
|
549
|
+
}
|
|
550
|
+
const start = match.index + match[0].length;
|
|
551
|
+
const end = next?.index ?? markdown.length;
|
|
552
|
+
sections.set(heading, markdown.slice(start, end).trim());
|
|
553
|
+
}
|
|
554
|
+
return sections;
|
|
555
|
+
}
|
|
556
|
+
function extractAudienceFromOverview(overview) {
|
|
557
|
+
const text = normalizeSectionText(overview);
|
|
558
|
+
if (!text) {
|
|
559
|
+
return undefined;
|
|
560
|
+
}
|
|
561
|
+
const match = /^Build an Expo app for\s+(.+?)[.]\s*$/iu.exec(text);
|
|
562
|
+
return normalizeText(match?.[1] ?? text);
|
|
563
|
+
}
|
|
564
|
+
function normalizeSectionText(value) {
|
|
565
|
+
const normalized = normalizeLineEndings(value ?? '')
|
|
566
|
+
.replace(/<!--[\s\S]*?-->/gu, '')
|
|
567
|
+
.replace(/^\s*>/gmu, '')
|
|
568
|
+
.trim();
|
|
569
|
+
return normalized.length > 0 ? normalized : undefined;
|
|
570
|
+
}
|
|
571
|
+
function normalizeListSection(value) {
|
|
572
|
+
const text = normalizeSectionText(value);
|
|
573
|
+
if (!text) {
|
|
574
|
+
return undefined;
|
|
575
|
+
}
|
|
576
|
+
const bulletLines = text
|
|
577
|
+
.split('\n')
|
|
578
|
+
.map((line) => line.trim())
|
|
579
|
+
.filter(Boolean)
|
|
580
|
+
.map((line) => line.replace(/^[-*]\s+/u, ''))
|
|
581
|
+
.filter((line) => !line.startsWith('# TodoForContext'));
|
|
582
|
+
return bulletLines.length > 0 ? bulletLines.join('\n') : text;
|
|
583
|
+
}
|
|
584
|
+
function inferDataNeedSelections(value) {
|
|
585
|
+
const normalized = value.toLowerCase();
|
|
586
|
+
const selected = new Set();
|
|
587
|
+
for (const option of DATA_NEED_OPTIONS) {
|
|
588
|
+
if (normalized.includes(option.toLowerCase())) {
|
|
589
|
+
selected.add(option);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
if (/\blocal\b|\bsqlite\b/u.test(normalized)) {
|
|
593
|
+
selected.add('Local UI/app state');
|
|
594
|
+
}
|
|
595
|
+
if (/\bauth\b|\buser account\b|\bsign in\b|\bsign-up\b|\bsign up\b/u.test(normalized)) {
|
|
596
|
+
selected.add('User accounts/authentication');
|
|
597
|
+
}
|
|
598
|
+
if (/\bdatabase\b|\bsupabase\b|\brecords\b/u.test(normalized)) {
|
|
599
|
+
selected.add('Backend database records');
|
|
600
|
+
}
|
|
601
|
+
if (/\bfile\b|\bimage\b|\bphoto\b|\bupload\b|\bstorage\b/u.test(normalized)) {
|
|
602
|
+
selected.add('File/image uploads or storage');
|
|
603
|
+
}
|
|
604
|
+
if (/\bapi\b|\bintegration\b/u.test(normalized)) {
|
|
605
|
+
selected.add('External APIs/integrations');
|
|
606
|
+
}
|
|
607
|
+
if (/\banalytics\b|\bevents\b/u.test(normalized)) {
|
|
608
|
+
selected.add('Analytics/events');
|
|
609
|
+
}
|
|
610
|
+
if (/\bpayments?\b|\bsubscription\b/u.test(normalized)) {
|
|
611
|
+
selected.add('Payments/subscriptions');
|
|
612
|
+
}
|
|
613
|
+
if (/\brealtime\b|\bcollaboration\b/u.test(normalized)) {
|
|
614
|
+
selected.add('Realtime/collaboration');
|
|
615
|
+
}
|
|
616
|
+
if (/\bpush\b|\bemail\b|\bnotification\b/u.test(normalized)) {
|
|
617
|
+
selected.add('Push/email notifications');
|
|
618
|
+
}
|
|
619
|
+
if (/\boffline\b|\bcache\b|\bsync\b/u.test(normalized)) {
|
|
620
|
+
selected.add('Offline sync/cache');
|
|
621
|
+
}
|
|
622
|
+
if (/\badmin\b|\bmoderation\b/u.test(normalized)) {
|
|
623
|
+
selected.add('Admin/moderation tools');
|
|
624
|
+
}
|
|
625
|
+
return Array.from(selected);
|
|
626
|
+
}
|
|
627
|
+
function inferDataStart(value) {
|
|
628
|
+
const normalized = value.toLowerCase();
|
|
629
|
+
if (normalized.includes('supabase')) {
|
|
630
|
+
return 'supabase';
|
|
631
|
+
}
|
|
632
|
+
if (normalized.includes('local dummy data') || normalized.includes('sqlite') || normalized.includes('local ui/app state')) {
|
|
633
|
+
return 'local';
|
|
634
|
+
}
|
|
635
|
+
return undefined;
|
|
636
|
+
}
|
|
637
|
+
function inferAuthBackend(value) {
|
|
638
|
+
const normalized = value.toLowerCase();
|
|
639
|
+
if (normalized.includes('supabase')) {
|
|
640
|
+
return 'supabase';
|
|
641
|
+
}
|
|
642
|
+
if (normalized.includes('firebase')) {
|
|
643
|
+
return 'firebase';
|
|
644
|
+
}
|
|
645
|
+
return undefined;
|
|
646
|
+
}
|
|
647
|
+
function extractPlatformDecisions(value, assignValue) {
|
|
648
|
+
const targetPlatforms = parsePlatformList(extractBulletValue(value, 'Target platforms'));
|
|
649
|
+
if (targetPlatforms.length > 0) {
|
|
650
|
+
assignValue('targetPlatforms', targetPlatforms, 'Platforms section');
|
|
651
|
+
}
|
|
652
|
+
const firstTargetPlatform = normalizeChoice(extractBulletValue(value, 'First MVP platform'), PLATFORM_OPTIONS);
|
|
653
|
+
if (firstTargetPlatform) {
|
|
654
|
+
assignValue('firstTargetPlatform', firstTargetPlatform, 'Platforms section');
|
|
655
|
+
}
|
|
656
|
+
const appDirectoryValue = extractBulletValue(value, 'Expo Router app directory');
|
|
657
|
+
if (appDirectoryValue?.includes('src/app')) {
|
|
658
|
+
assignValue('appDirectory', 'src', 'Platforms section');
|
|
659
|
+
}
|
|
660
|
+
else if (appDirectoryValue?.includes('app')) {
|
|
661
|
+
assignValue('appDirectory', 'root', 'Platforms section');
|
|
662
|
+
}
|
|
663
|
+
const organization = extractBulletValue(value, 'Platform-specific organization') ?? '';
|
|
664
|
+
if (organization.toLowerCase().includes('folder')) {
|
|
665
|
+
assignValue('platformStrategy', 'folders', 'Platforms section');
|
|
666
|
+
}
|
|
667
|
+
else if (organization.toLowerCase().includes('file')) {
|
|
668
|
+
assignValue('platformStrategy', 'files-only', 'Platforms section');
|
|
669
|
+
}
|
|
670
|
+
const layoutMode = extractBulletValue(value, 'Platform layout mode');
|
|
671
|
+
if (layoutMode?.toLowerCase().includes('platform-specific')) {
|
|
672
|
+
assignValue('platformLayouts', 'platform-specific', 'Platforms section');
|
|
673
|
+
}
|
|
674
|
+
else if (layoutMode?.toLowerCase().includes('shared')) {
|
|
675
|
+
assignValue('platformLayouts', 'shared', 'Platforms section');
|
|
676
|
+
}
|
|
677
|
+
const webOutput = normalizeChoice(extractBulletValue(value, 'Web output'), ['static', 'server', 'spa', 'none']);
|
|
678
|
+
if (webOutput) {
|
|
679
|
+
assignValue('webOutput', webOutput, 'Platforms section');
|
|
680
|
+
}
|
|
681
|
+
const deployedServer = (extractBulletValue(value, 'Deployed server') ?? '').toLowerCase();
|
|
682
|
+
if (deployedServer.includes('no deployed server') || deployedServer === 'none') {
|
|
683
|
+
assignValue('expoServerAdapter', 'none', 'Platforms section');
|
|
684
|
+
assignValue('customBackend', false, 'Platforms section');
|
|
685
|
+
}
|
|
686
|
+
else if (deployedServer.includes('eas')) {
|
|
687
|
+
assignValue('expoServerAdapter', 'eas', 'Platforms section');
|
|
688
|
+
}
|
|
689
|
+
else if (deployedServer.includes('express')) {
|
|
690
|
+
assignValue('expoServerAdapter', 'express', 'Platforms section');
|
|
691
|
+
}
|
|
692
|
+
else if (deployedServer.includes('bun')) {
|
|
693
|
+
assignValue('expoServerAdapter', 'bun', 'Platforms section');
|
|
694
|
+
}
|
|
695
|
+
else if (deployedServer.includes('custom')) {
|
|
696
|
+
assignValue('expoServerAdapter', 'other', 'Platforms section');
|
|
697
|
+
assignValue('customBackend', true, 'Platforms section');
|
|
698
|
+
}
|
|
699
|
+
const expoUi = parseBooleanValue(extractBulletValue(value, 'Expo UI'));
|
|
700
|
+
if (typeof expoUi === 'boolean') {
|
|
701
|
+
assignValue('usesExpoUi', expoUi, 'Platforms section');
|
|
702
|
+
}
|
|
703
|
+
const expoUiUniversal = parseBooleanValue(extractBulletValue(value, 'Expo UI Universal components'));
|
|
704
|
+
if (typeof expoUiUniversal === 'boolean') {
|
|
705
|
+
assignValue('usesExpoUiUniversalComponents', expoUiUniversal, 'Platforms section');
|
|
706
|
+
}
|
|
707
|
+
const expoNativeTabs = parseBooleanValue(extractBulletValue(value, 'Expo Native Tabs'));
|
|
708
|
+
if (typeof expoNativeTabs === 'boolean') {
|
|
709
|
+
assignValue('usesExpoNativeTabs', expoNativeTabs, 'Platforms section');
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
function extractPackageChoices(value, assignValue) {
|
|
713
|
+
const entries = normalizeListSection(value)
|
|
714
|
+
?.split('\n')
|
|
715
|
+
.map((item) => item.trim().toLowerCase())
|
|
716
|
+
.filter(Boolean) ?? [];
|
|
717
|
+
for (const entry of entries) {
|
|
718
|
+
if (entry === 'uniwind') {
|
|
719
|
+
assignValue('stylingSystem', 'uniwind', 'Package Choices section');
|
|
720
|
+
}
|
|
721
|
+
else if (entry === 'nativewind') {
|
|
722
|
+
assignValue('stylingSystem', 'nativewind', 'Package Choices section');
|
|
723
|
+
}
|
|
724
|
+
else if (entry === 'tamagui') {
|
|
725
|
+
assignValue('stylingSystem', 'tamagui', 'Package Choices section');
|
|
726
|
+
}
|
|
727
|
+
else if (entry === 'restyle') {
|
|
728
|
+
assignValue('stylingSystem', 'restyle', 'Package Choices section');
|
|
729
|
+
}
|
|
730
|
+
else if (entry === 'supabase') {
|
|
731
|
+
assignValue('authBackend', 'supabase', 'Package Choices section');
|
|
732
|
+
}
|
|
733
|
+
else if (entry === 'firebase') {
|
|
734
|
+
assignValue('authBackend', 'firebase', 'Package Choices section');
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
function extractTechStackDecisions(value, assignValue) {
|
|
739
|
+
const language = normalizeChoice(extractKeyValue(value, 'Language'), ['typescript', 'javascript']);
|
|
740
|
+
if (language) {
|
|
741
|
+
assignValue('scriptLanguage', language, 'Tech Stack & MDS Onboarding section');
|
|
742
|
+
}
|
|
743
|
+
const packageManager = normalizeChoice(extractKeyValue(value, 'Package manager'), ['npm', 'pnpm', 'yarn', 'bun']);
|
|
744
|
+
if (packageManager) {
|
|
745
|
+
assignValue('packageManager', packageManager, 'Tech Stack & MDS Onboarding section');
|
|
746
|
+
}
|
|
747
|
+
const routing = (extractKeyValue(value, 'Routing') ?? '').toLowerCase();
|
|
748
|
+
if (routing.includes('react navigation')) {
|
|
749
|
+
assignValue('navigationLibrary', 'react-navigation', 'Tech Stack & MDS Onboarding section');
|
|
750
|
+
if (routing.includes('tabs')) {
|
|
751
|
+
assignValue('reactNavigationLayout', 'tabs', 'Tech Stack & MDS Onboarding section');
|
|
752
|
+
}
|
|
753
|
+
else if (routing.includes('drawer')) {
|
|
754
|
+
assignValue('reactNavigationLayout', 'drawer', 'Tech Stack & MDS Onboarding section');
|
|
755
|
+
}
|
|
756
|
+
else {
|
|
757
|
+
assignValue('reactNavigationLayout', 'stack', 'Tech Stack & MDS Onboarding section');
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
else if (routing.includes('expo router')) {
|
|
761
|
+
assignValue('navigationLibrary', 'expo-router', 'Tech Stack & MDS Onboarding section');
|
|
762
|
+
}
|
|
763
|
+
const styling = (extractKeyValue(value, 'Styling') ?? '').toLowerCase();
|
|
764
|
+
if (styling.includes('uniwind')) {
|
|
765
|
+
assignValue('stylingSystem', 'uniwind', 'Tech Stack & MDS Onboarding section');
|
|
766
|
+
}
|
|
767
|
+
else if (styling.includes('nativewind')) {
|
|
768
|
+
assignValue('stylingSystem', 'nativewind', 'Tech Stack & MDS Onboarding section');
|
|
769
|
+
}
|
|
770
|
+
else if (styling.includes('tamagui')) {
|
|
771
|
+
assignValue('stylingSystem', 'tamagui', 'Tech Stack & MDS Onboarding section');
|
|
772
|
+
}
|
|
773
|
+
else if (styling.includes('restyle')) {
|
|
774
|
+
assignValue('stylingSystem', 'restyle', 'Tech Stack & MDS Onboarding section');
|
|
775
|
+
}
|
|
776
|
+
else if (styling.includes('stylesheet')) {
|
|
777
|
+
assignValue('stylingSystem', 'stylesheet', 'Tech Stack & MDS Onboarding section');
|
|
778
|
+
}
|
|
779
|
+
const stateManagement = (extractKeyValue(value, 'State management') ?? '').toLowerCase();
|
|
780
|
+
if (stateManagement.includes('zustand')) {
|
|
781
|
+
assignValue('stateManagement', 'zustand', 'Tech Stack & MDS Onboarding section');
|
|
782
|
+
}
|
|
783
|
+
else if (stateManagement.includes('none')) {
|
|
784
|
+
assignValue('stateManagement', 'none', 'Tech Stack & MDS Onboarding section');
|
|
785
|
+
}
|
|
786
|
+
const auth = inferAuthBackend(extractKeyValue(value, 'Auth') ?? '');
|
|
787
|
+
if (auth) {
|
|
788
|
+
assignValue('authBackend', auth, 'Tech Stack & MDS Onboarding section');
|
|
789
|
+
}
|
|
790
|
+
const distribution = extractKeyValue(value, 'Distribution');
|
|
791
|
+
if (distribution) {
|
|
792
|
+
assignValue('deploymentTarget', distribution, 'Tech Stack & MDS Onboarding section');
|
|
793
|
+
}
|
|
794
|
+
const easUses = inferEasUses(extractKeyValue(value, 'EAS') ?? '');
|
|
795
|
+
if (easUses.length > 0) {
|
|
796
|
+
assignValue('easUses', easUses, 'Tech Stack & MDS Onboarding section');
|
|
797
|
+
assignValue('easSetup', true, 'Tech Stack & MDS Onboarding section');
|
|
798
|
+
}
|
|
799
|
+
extractOnboardingDecisionLines(value, assignValue);
|
|
800
|
+
}
|
|
801
|
+
function extractOnboardingDecisionLines(value, assignValue) {
|
|
802
|
+
const lines = normalizeLineEndings(value)
|
|
803
|
+
.split('\n')
|
|
804
|
+
.map((line) => line.trim())
|
|
805
|
+
.filter(Boolean);
|
|
806
|
+
for (const line of lines) {
|
|
807
|
+
const rawKeyValue = /^-\s+(.+?):\s+(.+)$/u.exec(line);
|
|
808
|
+
const key = normalizeText(rawKeyValue?.[1]);
|
|
809
|
+
const rawValue = normalizeText(rawKeyValue?.[2]);
|
|
810
|
+
if (!key || !rawValue) {
|
|
811
|
+
continue;
|
|
812
|
+
}
|
|
813
|
+
const loweredKey = key.toLowerCase();
|
|
814
|
+
if (loweredKey === 'advanced package setup') {
|
|
815
|
+
continue;
|
|
816
|
+
}
|
|
817
|
+
if (loweredKey === 'create expo starter components') {
|
|
818
|
+
assignValue('includeCreateExpoComponents', parseBooleanValue(rawValue), 'Onboarding decisions');
|
|
819
|
+
}
|
|
820
|
+
else if (loweredKey === 'latest expo sdk preference') {
|
|
821
|
+
// Captured for human context only; SDK targeting is enforced by the generator.
|
|
822
|
+
continue;
|
|
823
|
+
}
|
|
824
|
+
else if (loweredKey === 'expo ui') {
|
|
825
|
+
assignValue('usesExpoUi', parseBooleanValue(rawValue), 'Onboarding decisions');
|
|
826
|
+
}
|
|
827
|
+
else if (loweredKey === 'expo ui universal components') {
|
|
828
|
+
assignValue('usesExpoUiUniversalComponents', parseBooleanValue(rawValue), 'Onboarding decisions');
|
|
829
|
+
}
|
|
830
|
+
else if (loweredKey === 'expo native tabs') {
|
|
831
|
+
assignValue('usesExpoNativeTabs', parseBooleanValue(rawValue), 'Onboarding decisions');
|
|
832
|
+
}
|
|
833
|
+
else if (loweredKey === 'target platforms') {
|
|
834
|
+
const targetPlatforms = parsePlatformList(rawValue);
|
|
835
|
+
if (targetPlatforms.length > 0) {
|
|
836
|
+
assignValue('targetPlatforms', targetPlatforms, 'Onboarding decisions');
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
else if (loweredKey === 'first mvp platform') {
|
|
840
|
+
assignValue('firstTargetPlatform', normalizeChoice(rawValue, PLATFORM_OPTIONS), 'Onboarding decisions');
|
|
841
|
+
}
|
|
842
|
+
else if (loweredKey === 'expo router app directory') {
|
|
843
|
+
assignValue('appDirectory', rawValue.includes('src/app') ? 'src' : 'root', 'Onboarding decisions');
|
|
844
|
+
}
|
|
845
|
+
else if (loweredKey === 'platform-specific organization') {
|
|
846
|
+
assignValue('platformStrategy', rawValue.toLowerCase().includes('folder') ? 'folders' : 'files-only', 'Onboarding decisions');
|
|
847
|
+
}
|
|
848
|
+
else if (loweredKey === 'platform layout mode') {
|
|
849
|
+
assignValue('platformLayouts', rawValue.toLowerCase().includes('platform-specific') ? 'platform-specific' : 'shared', 'Onboarding decisions');
|
|
850
|
+
}
|
|
851
|
+
else if (loweredKey === 'web output') {
|
|
852
|
+
assignValue('webOutput', normalizeChoice(rawValue, ['static', 'server', 'spa', 'none']), 'Onboarding decisions');
|
|
853
|
+
}
|
|
854
|
+
else if (loweredKey === 'deployed server') {
|
|
855
|
+
extractPlatformDecisions(`- Deployed server: ${rawValue}`, assignValue);
|
|
856
|
+
}
|
|
857
|
+
else if (loweredKey === 'eas usage') {
|
|
858
|
+
const easUses = inferEasUses(rawValue);
|
|
859
|
+
if (easUses.length > 0) {
|
|
860
|
+
assignValue('easUses', easUses, 'Onboarding decisions');
|
|
861
|
+
assignValue('easSetup', true, 'Onboarding decisions');
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
else if (loweredKey === 'data start') {
|
|
865
|
+
assignValue('dataStart', inferDataStart(rawValue), 'Onboarding decisions');
|
|
866
|
+
}
|
|
867
|
+
else if (loweredKey === 'test-to-main safeguards') {
|
|
868
|
+
assignValue('testToMainSafeguards', parseBooleanValue(rawValue), 'Onboarding decisions');
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
function extractBulletValue(value, label) {
|
|
873
|
+
const match = new RegExp(`^-\\s+${escapeRegExp(label)}:\\s+(.+)$`, 'imu').exec(value);
|
|
874
|
+
return normalizeText(match?.[1]);
|
|
875
|
+
}
|
|
876
|
+
function extractKeyValue(value, label) {
|
|
877
|
+
const match = new RegExp(`^-\\s+(?:\\*\\*)?${escapeRegExp(label)}(?:\\*\\*)?:\\s+(.+)$`, 'imu').exec(value);
|
|
878
|
+
return normalizeText(match?.[1]);
|
|
879
|
+
}
|
|
880
|
+
function inferEasUses(value) {
|
|
881
|
+
const normalized = value.toLowerCase();
|
|
882
|
+
return EAS_USE_OPTIONS.filter((item) => normalized.includes(item.toLowerCase()));
|
|
883
|
+
}
|
|
884
|
+
function parsePlatformList(value) {
|
|
885
|
+
const normalized = value
|
|
886
|
+
?.split(',')
|
|
887
|
+
.map((item) => item.replace(/[`]/gu, '').trim().toLowerCase())
|
|
888
|
+
.filter(Boolean) ?? [];
|
|
889
|
+
return normalized
|
|
890
|
+
.map((item) => {
|
|
891
|
+
if (item === 'ios' || item === 'android' || item === 'web' || item === 'apple-tv' || item === 'android-tv') {
|
|
892
|
+
return item;
|
|
893
|
+
}
|
|
894
|
+
return item.replace(/\s+/gu, '-');
|
|
895
|
+
})
|
|
896
|
+
.filter((item) => PLATFORM_OPTIONS.includes(item));
|
|
897
|
+
}
|
|
898
|
+
function parseBooleanValue(value) {
|
|
899
|
+
const normalized = value?.trim().toLowerCase();
|
|
900
|
+
if (!normalized) {
|
|
901
|
+
return undefined;
|
|
902
|
+
}
|
|
903
|
+
if (['yes', 'true', 'on'].includes(normalized)) {
|
|
904
|
+
return true;
|
|
905
|
+
}
|
|
906
|
+
if (['no', 'false', 'off'].includes(normalized)) {
|
|
907
|
+
return false;
|
|
908
|
+
}
|
|
909
|
+
return undefined;
|
|
910
|
+
}
|
|
911
|
+
function normalizeExtractedAnswerValue(id, value) {
|
|
912
|
+
switch (id) {
|
|
913
|
+
case 'confirmed':
|
|
914
|
+
case 'easSetup':
|
|
915
|
+
case 'customBackend':
|
|
916
|
+
case 'includeCreateExpoComponents':
|
|
917
|
+
case 'usesExpoUi':
|
|
918
|
+
case 'usesExpoUiUniversalComponents':
|
|
919
|
+
case 'usesExpoNativeTabs':
|
|
920
|
+
case 'guidelinesTemplate':
|
|
921
|
+
case 'testToMainSafeguards':
|
|
922
|
+
case 'saveDefaults':
|
|
923
|
+
return typeof value === 'boolean' ? value : undefined;
|
|
924
|
+
case 'dataNeedSelections':
|
|
925
|
+
case 'targetPlatforms':
|
|
926
|
+
case 'easUses':
|
|
927
|
+
return normalizeStringArray(value) ?? undefined;
|
|
928
|
+
case 'scriptLanguage':
|
|
929
|
+
return normalizeEnum(value, ['typescript', 'javascript']);
|
|
930
|
+
case 'packageManager':
|
|
931
|
+
return normalizeEnum(value, ['npm', 'pnpm', 'yarn', 'bun']);
|
|
932
|
+
case 'navigationLibrary':
|
|
933
|
+
return normalizeEnum(value, ['expo-router', 'react-navigation']);
|
|
934
|
+
case 'reactNavigationLayout':
|
|
935
|
+
return normalizeEnum(value, ['stack', 'tabs', 'drawer']);
|
|
936
|
+
case 'stylingSystem':
|
|
937
|
+
return normalizeEnum(value, ['uniwind', 'nativewind', 'tamagui', 'restyle', 'stylesheet']);
|
|
938
|
+
case 'stateManagement':
|
|
939
|
+
return normalizeEnum(value, ['zustand', 'none']);
|
|
940
|
+
case 'authBackend':
|
|
941
|
+
return normalizeEnum(value, ['none', 'supabase', 'firebase']);
|
|
942
|
+
case 'platformStrategy':
|
|
943
|
+
return normalizeEnum(value, ['folders', 'files-only']);
|
|
944
|
+
case 'appDirectory':
|
|
945
|
+
return normalizeEnum(value, ['src', 'root']);
|
|
946
|
+
case 'platformLayouts':
|
|
947
|
+
return normalizeEnum(value, ['shared', 'platform-specific']);
|
|
948
|
+
case 'webOutput':
|
|
949
|
+
return normalizeEnum(value, ['static', 'server', 'spa', 'none']);
|
|
950
|
+
case 'expoServerAdapter':
|
|
951
|
+
return normalizeEnum(value, ['eas', 'express', 'bun', 'other', 'none']);
|
|
952
|
+
case 'dataStart':
|
|
953
|
+
return normalizeEnum(value, ['local', 'supabase']);
|
|
954
|
+
case 'screens':
|
|
955
|
+
return normalizeOptionalDeferText(value);
|
|
956
|
+
default:
|
|
957
|
+
return normalizeText(value);
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
function areAnswerValuesEquivalent(id, left, right) {
|
|
961
|
+
if (Array.isArray(left) || Array.isArray(right)) {
|
|
962
|
+
return JSON.stringify(normalizeStringArray(left) ?? []) === JSON.stringify(normalizeStringArray(right) ?? []);
|
|
963
|
+
}
|
|
964
|
+
if (typeof left === 'boolean' || typeof right === 'boolean') {
|
|
965
|
+
return left === right;
|
|
966
|
+
}
|
|
967
|
+
return normalizeExtractedAnswerValue(id, left) === normalizeExtractedAnswerValue(id, right);
|
|
968
|
+
}
|
|
969
|
+
function normalizeProjectTitle(value) {
|
|
970
|
+
const trimmed = value.replace(/\s+project info$/iu, '').trim();
|
|
971
|
+
const normalized = normalizeText(trimmed);
|
|
972
|
+
return normalized ? displayNameFromProjectName(normalized) : undefined;
|
|
973
|
+
}
|
|
974
|
+
function normalizeLineEndings(value) {
|
|
975
|
+
return value.replace(/\r\n/gu, '\n');
|
|
976
|
+
}
|
|
977
|
+
function slugifyAppName(value) {
|
|
978
|
+
const normalized = normalizeText(value);
|
|
979
|
+
if (!normalized) {
|
|
980
|
+
return undefined;
|
|
981
|
+
}
|
|
982
|
+
const slug = normalized
|
|
983
|
+
.toLowerCase()
|
|
984
|
+
.replace(/[^a-z0-9]+/gu, '-')
|
|
985
|
+
.replace(/^-+|-+$/gu, '');
|
|
986
|
+
return slug || DEFAULT_PROJECT_NAME;
|
|
987
|
+
}
|
|
988
|
+
function displayNameFromProjectName(value) {
|
|
989
|
+
const normalized = normalizeText(value);
|
|
990
|
+
if (!normalized) {
|
|
991
|
+
return DEFAULT_DISPLAY_APP_NAME;
|
|
992
|
+
}
|
|
993
|
+
if (normalized.includes(' ')) {
|
|
994
|
+
return normalized;
|
|
995
|
+
}
|
|
996
|
+
return normalized
|
|
997
|
+
.replace(/[-_]+/gu, ' ')
|
|
998
|
+
.replace(/\b\w/gu, (match) => match.toUpperCase());
|
|
999
|
+
}
|
|
1000
|
+
function escapeRegExp(value) {
|
|
1001
|
+
return value.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&');
|
|
1002
|
+
}
|
|
366
1003
|
export function buildCessIntakeStep(input) {
|
|
367
1004
|
const cwd = input.cwd ? path.resolve(input.cwd) : process.cwd();
|
|
368
1005
|
const parentDirProvided = normalizeText(input.parentDir);
|
|
369
1006
|
const appNameProvided = normalizeText(input.appName);
|
|
370
1007
|
const parentDir = normalizeParentDir(input.parentDir, cwd);
|
|
371
1008
|
const appName = normalizeProjectName(input.appName);
|
|
1009
|
+
const appDisplayName = displayNameFromProjectName(input.appName);
|
|
372
1010
|
const currentAnswers = normalizeCessIntakeAnswers(input.answers);
|
|
373
1011
|
const resolvedPlan = resolveCessPlan({
|
|
374
1012
|
parentDir,
|
|
375
|
-
appName,
|
|
1013
|
+
appName: input.appName,
|
|
376
1014
|
answers: currentAnswers,
|
|
377
1015
|
cwd,
|
|
378
1016
|
});
|
|
379
1017
|
const context = {
|
|
380
1018
|
parentDir,
|
|
381
1019
|
appName,
|
|
1020
|
+
appDisplayName,
|
|
382
1021
|
currentAnswers,
|
|
383
1022
|
resolvedAnswers: resolvedPlan.answers,
|
|
384
1023
|
onboardAnswers: resolvedPlan.onboardAnswers,
|
|
@@ -438,11 +1077,12 @@ export function resolveCessPlan(input) {
|
|
|
438
1077
|
const cwd = input.cwd ? path.resolve(input.cwd) : process.cwd();
|
|
439
1078
|
const parentDir = normalizeParentDir(input.parentDir, cwd);
|
|
440
1079
|
const appName = normalizeProjectName(input.appName);
|
|
1080
|
+
const appDisplayName = displayNameFromProjectName(input.appName);
|
|
441
1081
|
const projectPath = path.resolve(parentDir, appName);
|
|
442
1082
|
const currentAnswers = normalizeCessIntakeAnswers(input.answers);
|
|
443
1083
|
const onboardArgv = buildOnboardArgvFromCess(parentDir, appName, currentAnswers);
|
|
444
1084
|
const onboardPlan = defaultOnboardPlan(onboardArgv, projectPath);
|
|
445
|
-
const answers = buildResolvedCessAnswers(currentAnswers, parentDir, appName, onboardPlan.answers);
|
|
1085
|
+
const answers = buildResolvedCessAnswers(currentAnswers, parentDir, appName, appDisplayName, onboardPlan.answers);
|
|
446
1086
|
const finalOnboardArgv = buildOnboardArgvFromCess(parentDir, appName, answers);
|
|
447
1087
|
const finalOnboardPlan = defaultOnboardPlan(finalOnboardArgv, projectPath);
|
|
448
1088
|
const createExpoStackFlags = buildCreateExpoStackFlags(answers);
|
|
@@ -476,6 +1116,7 @@ export function validateCessGenerationReadiness(input) {
|
|
|
476
1116
|
const nextQuestion = findNextQuestion({
|
|
477
1117
|
parentDir,
|
|
478
1118
|
appName,
|
|
1119
|
+
appDisplayName: displayNameFromProjectName(input.appName),
|
|
479
1120
|
currentAnswers,
|
|
480
1121
|
resolvedAnswers: resolvedPlan.answers,
|
|
481
1122
|
onboardAnswers: resolvedPlan.onboardAnswers,
|
|
@@ -535,7 +1176,6 @@ export function normalizeCessIntakeAnswers(answers) {
|
|
|
535
1176
|
normalized.customBackendEntry = normalizeText(answers.customBackendEntry);
|
|
536
1177
|
normalized.deploymentTarget = normalizeText(answers.deploymentTarget);
|
|
537
1178
|
normalized.includeCreateExpoComponents = normalizeBoolean(answers.includeCreateExpoComponents);
|
|
538
|
-
normalized.useLatestExpoSdk = normalizeBoolean(answers.useLatestExpoSdk);
|
|
539
1179
|
normalized.usesExpoUi = normalizeBoolean(answers.usesExpoUi);
|
|
540
1180
|
normalized.usesExpoUiUniversalComponents = normalizeBoolean(answers.usesExpoUiUniversalComponents);
|
|
541
1181
|
normalized.usesExpoNativeTabs = normalizeBoolean(answers.usesExpoNativeTabs);
|
|
@@ -614,12 +1254,6 @@ export function buildMdsFlags(appName, onboardAnswers, intakeAnswers) {
|
|
|
614
1254
|
else {
|
|
615
1255
|
flags.push('--mds-no-create-expo-components');
|
|
616
1256
|
}
|
|
617
|
-
if (onboardAnswers.useLatestExpoSdk) {
|
|
618
|
-
flags.push('--mds-latest-expo-sdk');
|
|
619
|
-
}
|
|
620
|
-
else {
|
|
621
|
-
flags.push('--mds-no-latest-expo-sdk');
|
|
622
|
-
}
|
|
623
1257
|
if (onboardAnswers.usesExpoUi) {
|
|
624
1258
|
flags.push('--mds-expo-ui');
|
|
625
1259
|
}
|
|
@@ -692,7 +1326,7 @@ export function buildCessSummaryLines(parentDir, appName, answers, onboardAnswer
|
|
|
692
1326
|
? 'web output: none'
|
|
693
1327
|
: `web output: ${onboardAnswers.webOutput}, deployed server: ${onboardAnswers.deployedServer}`;
|
|
694
1328
|
return [
|
|
695
|
-
`app: ${appName} at ${parentDir}`,
|
|
1329
|
+
`app: ${onboardAnswers.appName} (folder: ${appName}) at ${parentDir}`,
|
|
696
1330
|
`stack: ${stackLine}`,
|
|
697
1331
|
`audience: ${onboardAnswers.audience}`,
|
|
698
1332
|
`core flows: ${onboardAnswers.coreFlows}`,
|
|
@@ -701,7 +1335,7 @@ export function buildCessSummaryLines(parentDir, appName, answers, onboardAnswer
|
|
|
701
1335
|
`data start: ${onboardAnswers.dataStart}, test-to-main: ${onboardAnswers.testToMainSafeguards ? 'on' : 'off'}, guidelines template: ${answers.guidelinesTemplate === false ? 'off' : 'on'}, save defaults: ${answers.saveDefaults ? 'on' : 'off'}`,
|
|
702
1336
|
];
|
|
703
1337
|
}
|
|
704
|
-
function buildResolvedCessAnswers(currentAnswers, parentDir, appName, onboardAnswers) {
|
|
1338
|
+
function buildResolvedCessAnswers(currentAnswers, parentDir, appName, appDisplayName, onboardAnswers) {
|
|
705
1339
|
const targetPlatforms = currentAnswers.targetPlatforms ?? onboardAnswers.targetPlatforms;
|
|
706
1340
|
const usesExpoUi = currentAnswers.usesExpoUi ?? onboardAnswers.usesExpoUi;
|
|
707
1341
|
const screens = currentAnswers.screens === 'defer' ? '' : currentAnswers.screens;
|
|
@@ -715,7 +1349,7 @@ function buildResolvedCessAnswers(currentAnswers, parentDir, appName, onboardAns
|
|
|
715
1349
|
stateManagement: currentAnswers.stateManagement ?? STACK_DEFAULTS.stateManagement,
|
|
716
1350
|
authBackend: currentAnswers.authBackend ?? STACK_DEFAULTS.authBackend,
|
|
717
1351
|
easSetup: currentAnswers.easSetup ?? STACK_DEFAULTS.easSetup,
|
|
718
|
-
displayAppName: currentAnswers.displayAppName ?? onboardAnswers.appName ?? appName,
|
|
1352
|
+
displayAppName: currentAnswers.displayAppName ?? appDisplayName ?? onboardAnswers.appName ?? appName,
|
|
719
1353
|
audience: currentAnswers.audience ?? onboardAnswers.audience,
|
|
720
1354
|
coreFlows: currentAnswers.coreFlows ?? onboardAnswers.coreFlows ?? AGENT_DERIVED_CORE_FLOWS,
|
|
721
1355
|
screens,
|
|
@@ -732,7 +1366,6 @@ function buildResolvedCessAnswers(currentAnswers, parentDir, appName, onboardAns
|
|
|
732
1366
|
customBackendEntry: currentAnswers.customBackendEntry ?? onboardAnswers.customBackendEntry,
|
|
733
1367
|
deploymentTarget: currentAnswers.deploymentTarget ?? onboardAnswers.deploymentTarget,
|
|
734
1368
|
includeCreateExpoComponents: currentAnswers.includeCreateExpoComponents ?? onboardAnswers.includeCreateExpoComponents,
|
|
735
|
-
useLatestExpoSdk: currentAnswers.useLatestExpoSdk ?? onboardAnswers.useLatestExpoSdk,
|
|
736
1369
|
usesExpoUi,
|
|
737
1370
|
usesExpoUiUniversalComponents: currentAnswers.usesExpoUiUniversalComponents ??
|
|
738
1371
|
(usesExpoUi ? onboardAnswers.usesExpoUiUniversalComponents : false),
|
|
@@ -754,14 +1387,21 @@ function buildOnboardArgvFromCess(parentDir, appName, answers) {
|
|
|
754
1387
|
return {
|
|
755
1388
|
project: path.resolve(parentDir, appName),
|
|
756
1389
|
yes: true,
|
|
757
|
-
appName: normalizeText(answers.displayAppName) ?? appName,
|
|
1390
|
+
appName: normalizeText(answers.displayAppName) ?? displayNameFromProjectName(appName),
|
|
1391
|
+
generatorScriptLanguage: answers.scriptLanguage,
|
|
1392
|
+
generatorPackageManager: answers.packageManager,
|
|
1393
|
+
generatorNavigationLibrary: answers.navigationLibrary,
|
|
1394
|
+
generatorReactNavigationLayout: answers.reactNavigationLayout,
|
|
1395
|
+
generatorStylingSystem: answers.stylingSystem,
|
|
1396
|
+
generatorStateManagement: answers.stateManagement,
|
|
1397
|
+
generatorAuthBackend: answers.authBackend,
|
|
1398
|
+
generatorEasSetup: answers.easSetup,
|
|
758
1399
|
audience: normalizeText(answers.audience),
|
|
759
1400
|
coreFlows: normalizeText(answers.coreFlows),
|
|
760
1401
|
screens,
|
|
761
1402
|
dataNeeds,
|
|
762
1403
|
deploymentTarget: normalizeText(answers.deploymentTarget),
|
|
763
1404
|
createExpoComponents: answers.includeCreateExpoComponents,
|
|
764
|
-
latestExpoSdk: answers.useLatestExpoSdk,
|
|
765
1405
|
platforms: targetPlatforms,
|
|
766
1406
|
firstPlatform: normalizeText(answers.firstTargetPlatform),
|
|
767
1407
|
platformStrategy: answers.platformStrategy,
|
|
@@ -825,7 +1465,7 @@ function normalizeParentDir(parentDir, cwd) {
|
|
|
825
1465
|
return path.resolve(cwd, normalizeText(parentDir) ?? '.');
|
|
826
1466
|
}
|
|
827
1467
|
function normalizeProjectName(appName) {
|
|
828
|
-
return
|
|
1468
|
+
return slugifyAppName(appName) ?? DEFAULT_PROJECT_NAME;
|
|
829
1469
|
}
|
|
830
1470
|
function normalizeBoolean(value) {
|
|
831
1471
|
return typeof value === 'boolean' ? value : undefined;
|
|
@@ -833,6 +1473,9 @@ function normalizeBoolean(value) {
|
|
|
833
1473
|
function normalizeEnum(value, choices) {
|
|
834
1474
|
return typeof value === 'string' && choices.includes(value) ? value : undefined;
|
|
835
1475
|
}
|
|
1476
|
+
function normalizeChoice(value, choices) {
|
|
1477
|
+
return normalizeEnum(value, choices);
|
|
1478
|
+
}
|
|
836
1479
|
function normalizeText(value) {
|
|
837
1480
|
if (typeof value !== 'string') {
|
|
838
1481
|
return undefined;
|