@mr.dj2u/cli 0.1.15 → 0.1.16

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.
@@ -45,26 +45,22 @@ const DEFAULT_GUIDELINES_TEMPLATE_PATH = path.join(PACKAGE_ROOT, 'templates', 'p
45
45
  const STYLIST_SCREEN_TEMPLATE_PATH = path.join(PACKAGE_ROOT, 'templates', 'stylist-screen.template.tsx');
46
46
  const EMBEDDED_FONTS_TEMPLATE_PATH = path.join(PACKAGE_ROOT, 'templates', 'embedded-fonts.template.ts');
47
47
  const EXPO_SDK_56_SCREEN_UNIVERSAL_TEMPLATE_PATH = path.join(PACKAGE_ROOT, 'templates', 'expo-sdk-56-screen-universal.template.tsx');
48
- const CESS_SNAPSHOT_START = '<!-- MDS_CESS_SNAPSHOT_START -->';
49
- const CESS_SNAPSHOT_END = '<!-- MDS_CESS_SNAPSHOT_END -->';
50
48
  const INFO_HEADINGS = [
49
+ 'App Name',
51
50
  'Overview',
52
51
  'Target Users',
52
+ 'Problem this app solves',
53
53
  'Product Goals',
54
54
  'Non-Goals',
55
- 'Core Features',
56
- 'Core User Flows',
57
- 'Must-Include Screens Or Flows',
58
- 'Data And Backend',
55
+ 'First User Flow',
56
+ 'Core Flows and Features',
57
+ 'Screens',
59
58
  'Platforms',
60
- 'Package Choices',
61
59
  'Monetization Strategy',
62
60
  'Team Context',
63
- 'Release Strategy',
64
- 'Questions To Revisit',
65
- 'Open Questions',
66
- 'Resources',
67
- 'Tech Stack & MDS Onboarding',
61
+ 'Later Scope & Possibilities',
62
+ 'Research, Notes, and References',
63
+ 'Tech Stack & CESS Onboarding',
68
64
  ];
69
65
  const STYLE_HEADINGS = [
70
66
  'Visual Direction',
@@ -217,9 +213,15 @@ export async function scaffoldRichBoilerplate(projectPath, answers, force, optio
217
213
  export function renderInfo(projectPath, answers, existingInfo) {
218
214
  const importedNotes = renderImportedNotes(existingInfo, INFO_HEADINGS);
219
215
  const hasConcreteCoreFlows = !isGenericCoreFlowsText(answers.coreFlows);
216
+ const firstFlow = hasConcreteCoreFlows
217
+ ? extractFirstNonEmptyLine(answers.coreFlows)
218
+ : '# TodoForContext(optional): Describe the first real end-to-end user flow the MVP should support.';
220
219
  return [
221
220
  `# ${answers.appName} Project Info`,
222
221
  '',
222
+ '## App Name',
223
+ answers.appName,
224
+ '',
223
225
  '## Overview',
224
226
  '',
225
227
  `Build an Expo app for ${answers.audience}.`,
@@ -228,6 +230,9 @@ export function renderInfo(projectPath, answers, existingInfo) {
228
230
  '',
229
231
  answers.audience,
230
232
  '',
233
+ '## Problem this app solves',
234
+ '# TodoForContext(optional): Explain the user problem or pain this app exists to solve.',
235
+ '',
231
236
  '## Product Goals',
232
237
  '',
233
238
  '# TodoForContext(optional): Add the business/product outcomes that would make this app successful.',
@@ -236,49 +241,26 @@ export function renderInfo(projectPath, answers, existingInfo) {
236
241
  '',
237
242
  '# TodoForContext(optional): Add anything this app should intentionally avoid for the MVP.',
238
243
  '',
239
- '## Core Features',
244
+ '## First User Flow',
240
245
  '',
241
- hasConcreteCoreFlows
242
- ? `Derived from the first planned flows: ${answers.coreFlows}`
243
- : '# TodoForContext(optional): List the first core features the MVP should deliver.',
246
+ firstFlow,
244
247
  '',
245
- '## Core User Flows',
248
+ '## Core Flows and Features',
246
249
  '',
247
250
  hasConcreteCoreFlows
248
251
  ? answers.coreFlows
249
- : '# TodoForContext(optional): Describe the first real end-to-end user flow the MVP should support.',
252
+ : '# TodoForContext(optional): List the first core flows and features the MVP should deliver.',
250
253
  '',
251
- '## Must-Include Screens Or Flows',
254
+ '## Screens',
252
255
  '',
253
256
  answers.screens?.trim()
254
257
  ? answers.screens
255
- : '# TodoForContext(optional): List any known screens or flows that must be included in planning and implementation.',
256
- '',
257
- '## Data And Backend',
258
- '',
259
- answers.dataNeeds,
260
- '',
261
- `Starting mode: ${formatDataStart(answers.dataStart)}.`,
258
+ : '# TodoForContext(optional): List any known screens that must be included in planning and implementation.',
262
259
  '',
263
260
  '## Platforms',
264
261
  '',
265
262
  `- Target platforms: ${answers.targetPlatforms.join(', ') || 'none selected'}`,
266
263
  `- First MVP platform: ${answers.firstTargetPlatform}`,
267
- `- Expo Router app directory: ${formatAppDirectory(answers.appDirectory)}`,
268
- `- Platform-specific organization: ${formatPlatformStrategy(answers.platformFileStrategy)}`,
269
- `- Platform layout mode: ${formatPlatformLayoutMode(answers.platformLayoutMode)}`,
270
- `- Web output: ${answers.webOutput}`,
271
- `- Deployed server: ${formatServerChoice(answers.deployedServer)}`,
272
- `- Expo UI: ${formatBoolean(answers.usesExpoUi)}`,
273
- `- Expo UI Universal components: ${formatBoolean(answers.usesExpoUiUniversalComponents)}`,
274
- `- Expo Native Tabs: ${formatBoolean(answers.usesExpoNativeTabs)}`,
275
- '',
276
- '## Package Choices',
277
- '',
278
- answers.defaults.map((item) => `- ${item}`).join('\n'),
279
- '',
280
- '- Software Mansion core examples are included for Reanimated/Worklets, Gesture Handler, Screens, SVG, and Keyboard Controller.',
281
- '- Prune package examples and dependencies after reviewing the exposition pages.',
282
264
  '',
283
265
  '## Monetization Strategy',
284
266
  '',
@@ -288,58 +270,50 @@ export function renderInfo(projectPath, answers, existingInfo) {
288
270
  '',
289
271
  '# TodoForContext(optional): Add team size, roles, delegated responsibilities, stakeholders, and client contacts if useful.',
290
272
  '',
291
- '## Release Strategy',
292
- '',
293
- `- Deployment plan: ${answers.deploymentTarget}`,
294
- `- EAS usage: ${answers.easUses.length > 0 ? answers.easUses.join(', ') : 'not planned yet'}`,
295
- `- Test-to-main safeguards: ${formatBoolean(answers.testToMainSafeguards)}`,
296
- '',
297
- '## Questions To Revisit',
273
+ '## Later Scope & Possibilities',
298
274
  '',
275
+ '# TodoForContext(optional): Add future ideas or enhancements outside the first MVP.',
299
276
  '',
300
- '## Resources',
277
+ '## Research, Notes, and References',
301
278
  '',
302
279
  `- Source project: ${projectPath}`,
303
280
  '- # TodoForContext(optional): Add designs, repos, docs, client notes, analytics, credentials process, or research links.',
304
281
  '',
305
282
  ...importedNotes,
306
283
  '',
307
- '## Tech Stack & MDS Onboarding',
284
+ '# Tech Stack & CESS Onboarding',
308
285
  '',
309
- '> Quick-reference stack summary for agents and collaborators. Fill in or correct any items marked below.',
286
+ `- TypeScript: ${formatYesNo(answers.generatorScriptLanguage !== 'javascript')}`,
287
+ `- Package Manager: ${answers.generatorPackageManager ?? 'npm'}`,
288
+ `- Navigation: ${formatGeneratorNavigation(answers.generatorNavigationLibrary)}`,
289
+ `- Type of Navigation: ${formatGeneratorNavigationType(answers.generatorReactNavigationLayout)}`,
290
+ `- Expo Router app directory: ${formatAppDirectory(answers.appDirectory)}`,
291
+ `- Platform-specific organization: ${formatPlatformStrategy(answers.platformFileStrategy)}`,
292
+ `- Platform layout mode: ${formatPlatformLayoutMode(answers.platformLayoutMode)}`,
293
+ `- Web output: ${answers.webOutput}`,
310
294
  '',
311
- CESS_SNAPSHOT_START,
312
- '```json',
313
- JSON.stringify(buildCessSnapshot(answers), null, 2),
314
- '```',
315
- CESS_SNAPSHOT_END,
316
- '',
317
- `- **App:** ${answers.appName} — ${answers.audience}`,
318
- `- **Language:** ${formatGeneratorLanguage(answers.generatorScriptLanguage)}`,
319
- `- **Package manager:** ${formatGeneratorPackageManager(answers.generatorPackageManager)}`,
320
- `- **Routing:** ${formatGeneratorRouting(answers)}`,
321
- `- **Styling:** ${formatStyleStack(answers)}`,
322
- `- **State management:** ${formatGeneratorStateManagement(answers.generatorStateManagement)}`,
323
- `- **Auth:** ${formatAuthSummary(answers)}`,
324
- `- **Data:** ${formatDataStart(answers.dataStart)}`,
325
- `- **Platforms:** ${answers.targetPlatforms.join(', ') || 'none selected'}, first MVP target: ${answers.firstTargetPlatform}`,
326
- `- **Code organization:** ${formatCodeOrg(answers)}`,
327
- `- **Deployed server:** ${formatServerAdapterSummary(answers)}`,
328
- `- **Distribution:** ${answers.deploymentTarget}`,
329
- `- **EAS:** ${answers.easUses.length > 0 ? answers.easUses.join(', ') : 'not planned yet'}`,
330
- '',
331
- '### MDS Onboarding Decisions',
332
- '',
333
- `- Advanced package setup: ${formatBoolean(answers.advancedPackageSetup)}`,
334
- `- Create Expo starter components: ${formatBoolean(answers.includeCreateExpoComponents)}`,
335
- `- EAS starter selected during generation: ${formatBoolean(answers.generatorEasSetup ?? answers.easUses.length > 0)}`,
336
- `- MDS guidelines template: yes`,
337
- `- Expo UI: ${formatBoolean(answers.usesExpoUi)}`,
338
- `- Expo UI Universal components: ${formatBoolean(answers.usesExpoUiUniversalComponents)}`,
339
- `- Expo Native Tabs: ${formatBoolean(answers.usesExpoNativeTabs)}`,
340
- `- Test-to-main safeguards: ${formatBoolean(answers.testToMainSafeguards)}`,
341
- `- Data start: ${formatDataStart(answers.dataStart)}`,
342
- `- Defaults selected: ${answers.defaults.join(', ')}`,
295
+ `- Style Library: ${formatCessStyleLibrary(answers)}`,
296
+ '- Which NativeWindUI components: All',
297
+ `- Components from create-expo-app: ${formatYesNo(answers.includeCreateExpoComponents)}`,
298
+ `- Expo UI: ${formatYesNo(answers.usesExpoUi)}`,
299
+ `- Expo UI Universal components: ${formatYesNo(answers.usesExpoUiUniversalComponents)}`,
300
+ `- Expo Native Tabs: ${formatYesNo(answers.usesExpoNativeTabs)}`,
301
+ '',
302
+ '- Which Software Mansion packages: All',
303
+ `- State management library: ${formatGeneratorStateManagement(answers.generatorStateManagement)}`,
304
+ `- Auth: ${formatGeneratorAuth(answers.generatorAuthBackend)}`,
305
+ `- Data Categories: ${answers.dataNeeds}`,
306
+ `- Starting Data mode: ${formatDataStart(answers.dataStart)}.`,
307
+ '',
308
+ '- Internationalization: None',
309
+ '- Analytics: None',
310
+ `- EAS: ${formatYesNo(answers.generatorEasSetup ?? answers.easUses.length > 0)}`,
311
+ `- EAS Usage: ${answers.easUses.length > 0 ? answers.easUses.join(', ') : 'not planned yet'}`,
312
+ `- Deployed server: ${formatServerChoice(answers.deployedServer)}`,
313
+ `- Initial Deployment plan: ${answers.deploymentTarget}`,
314
+ '',
315
+ `- Start with MDS project guidelines template: ${formatYesNo(true)}`,
316
+ `- Use test-to-main safeguards: ${formatYesNo(answers.testToMainSafeguards)}`,
343
317
  '',
344
318
  ].join('\n');
345
319
  }
@@ -880,22 +854,6 @@ function formatServerChoice(value) {
880
854
  function formatDataStart(value) {
881
855
  return value === 'supabase' ? 'Supabase from the start' : 'local dummy data with Expo SQLite';
882
856
  }
883
- function formatServerAdapterSummary(answers) {
884
- if (answers.webOutput === 'none')
885
- return 'none (native-only)';
886
- switch (answers.expoServerAdapter) {
887
- case 'eas':
888
- return 'EAS hosting';
889
- case 'express':
890
- return 'Express adapter (node server.js, port 3000)';
891
- case 'bun':
892
- return 'Bun adapter (node server.js)';
893
- case 'other':
894
- return 'custom (not yet specified)';
895
- default:
896
- return formatServerChoice(answers.deployedServer);
897
- }
898
- }
899
857
  function deriveServeProdScript(answers) {
900
858
  if (answers.expoServerAdapter === 'express' || answers.expoServerAdapter === 'bun') {
901
859
  return 'node server.js';
@@ -908,105 +866,53 @@ function deriveServeProdFreshScript(answers) {
908
866
  }
909
867
  return `${MDS_NPX_COMMAND} free-port 8081 && npm run build:web && npx expo serve`;
910
868
  }
911
- function formatStyleStack(answers) {
912
- if (answers.defaults.includes('uniwind')) {
913
- return 'Uniwind / Tailwind CSS v4';
914
- }
915
- if (answers.defaults.includes('nativewindui')) {
916
- return 'NativeWindUI / NativeWind';
869
+ function extractFirstNonEmptyLine(value) {
870
+ return (value
871
+ .split(/\r?\n/u)
872
+ .map((line) => line.replace(/^[-*]\s+/u, '').trim())
873
+ .find(Boolean) ?? value.trim());
874
+ }
875
+ function formatYesNo(value) {
876
+ return value ? 'Yes' : 'No';
877
+ }
878
+ function formatGeneratorNavigation(value) {
879
+ return value === 'react-navigation' ? 'React Navigation' : 'Expo Router';
880
+ }
881
+ function formatGeneratorNavigationType(value) {
882
+ if (value === 'tabs') {
883
+ return 'Tabs';
917
884
  }
918
- if (answers.defaults.includes('nativewind')) {
919
- return 'NativeWind / Tailwind CSS';
885
+ if (value === 'drawer') {
886
+ return 'Drawer + Tabs';
920
887
  }
921
- if (answers.defaults.includes('unistyles')) {
922
- return 'Unistyles';
888
+ return 'Stack';
889
+ }
890
+ function formatCessStyleLibrary(answers) {
891
+ if (answers.generatorStylingSystem === 'nativewindui' || answers.defaults.includes('nativewindui')) {
892
+ return 'NativeWindUI';
923
893
  }
924
- if (answers.defaults.includes('restyle')) {
925
- return 'Shopify Restyle';
894
+ if (answers.generatorStylingSystem === 'nativewind' || answers.defaults.includes('nativewind')) {
895
+ return 'NativeWind';
926
896
  }
927
- if (answers.defaults.includes('tamagui')) {
897
+ if (answers.generatorStylingSystem === 'tamagui' || answers.defaults.includes('tamagui')) {
928
898
  return 'Tamagui';
929
899
  }
930
- return 'standard React Native StyleSheet';
931
- }
932
- function formatAuthSummary(answers) {
933
- if (answers.dataStart === 'supabase' || answers.defaults.includes('supabase')) {
934
- return 'Supabase auth (available via supabase-js)';
900
+ if (answers.generatorStylingSystem === 'restyle' || answers.defaults.includes('restyle')) {
901
+ return 'Restyle';
935
902
  }
936
- return 'no auth planned yet';
937
- }
938
- function formatCodeOrg(answers) {
939
- const parts = [
940
- formatPlatformStrategy(answers.platformFileStrategy),
941
- `${formatAppDirectory(answers.appDirectory)} routes`,
942
- formatPlatformLayoutMode(answers.platformLayoutMode),
943
- ];
944
- if (answers.webOutput === 'none') {
945
- parts.push('no web');
946
- }
947
- else {
948
- parts.push(`web: ${answers.webOutput}`);
903
+ if (answers.generatorStylingSystem === 'stylesheet') {
904
+ return 'StyleSheet';
949
905
  }
950
- return parts.join(', ');
906
+ return 'Uniwind';
951
907
  }
952
- function buildCessSnapshot(answers) {
953
- return {
954
- version: 1,
955
- displayAppName: answers.appName,
956
- folderSlug: toKebabCase(answers.appName),
957
- answers: {
958
- scriptLanguage: answers.generatorScriptLanguage,
959
- packageManager: answers.generatorPackageManager,
960
- navigationLibrary: answers.generatorNavigationLibrary,
961
- reactNavigationLayout: answers.generatorReactNavigationLayout,
962
- stylingSystem: answers.generatorStylingSystem,
963
- stateManagement: answers.generatorStateManagement,
964
- authBackend: answers.generatorAuthBackend,
965
- easSetup: answers.generatorEasSetup,
966
- displayAppName: answers.appName,
967
- audience: answers.audience,
968
- coreFlows: answers.coreFlows,
969
- screens: answers.screens,
970
- targetPlatforms: answers.targetPlatforms,
971
- firstTargetPlatform: answers.firstTargetPlatform,
972
- platformStrategy: answers.platformFileStrategy,
973
- appDirectory: answers.appDirectory,
974
- platformLayouts: answers.platformLayoutMode,
975
- webOutput: answers.webOutput,
976
- expoServerAdapter: answers.expoServerAdapter,
977
- customBackend: answers.customBackend,
978
- customBackendEntry: answers.customBackendEntry,
979
- deploymentTarget: answers.deploymentTarget,
980
- includeCreateExpoComponents: answers.includeCreateExpoComponents,
981
- usesExpoUi: answers.usesExpoUi,
982
- usesExpoUiUniversalComponents: answers.usesExpoUiUniversalComponents,
983
- usesExpoNativeTabs: answers.usesExpoNativeTabs,
984
- easUses: answers.easUses,
985
- guidelinesTemplate: true,
986
- dataStart: answers.dataStart,
987
- testToMainSafeguards: answers.testToMainSafeguards,
988
- },
989
- };
990
- }
991
- function formatGeneratorLanguage(value) {
992
- return value === 'javascript' ? 'JavaScript' : value === 'typescript' ? 'TypeScript' : '# TodoForContext(optional): TypeScript / JavaScript';
993
- }
994
- function formatGeneratorPackageManager(value) {
995
- return value ?? '# TodoForContext(optional): pnpm / npm / yarn / bun';
996
- }
997
- function formatGeneratorRouting(answers) {
998
- if (answers.generatorNavigationLibrary === 'react-navigation') {
999
- const layout = answers.generatorReactNavigationLayout === 'tabs'
1000
- ? 'Tabs'
1001
- : answers.generatorReactNavigationLayout === 'drawer'
1002
- ? 'Drawer + Tabs'
1003
- : 'Stack';
1004
- return `React Navigation (${layout})`;
908
+ function formatGeneratorAuth(value) {
909
+ if (value === 'supabase') {
910
+ return 'Supabase';
1005
911
  }
1006
- if (answers.generatorNavigationLibrary === 'expo-router') {
1007
- return `Expo Router (${formatAppDirectory(answers.appDirectory)})`;
912
+ if (value === 'firebase') {
913
+ return 'Firebase';
1008
914
  }
1009
- return `# TodoForContext(optional): Expo Router / React Navigation (${formatAppDirectory(answers.appDirectory)})`;
915
+ return 'None';
1010
916
  }
1011
917
  function formatGeneratorStateManagement(value) {
1012
918
  if (value === 'zustand') {
@@ -1017,13 +923,6 @@ function formatGeneratorStateManagement(value) {
1017
923
  }
1018
924
  return '# TodoForContext(optional): Zustand / Jotai / React context / none';
1019
925
  }
1020
- function toKebabCase(value) {
1021
- return value
1022
- .trim()
1023
- .toLowerCase()
1024
- .replace(/[^a-z0-9]+/gu, '-')
1025
- .replace(/^-+|-+$/gu, '') || 'expo-app';
1026
- }
1027
926
  function hasThinOnboardingAnswers(answers) {
1028
927
  const genericValues = new Set([
1029
928
  'Expo app users',