Skip to content

Commit 060c31f

Browse files
committed
[cli] users can save configurations
1 parent 940dc32 commit 060c31f

File tree

4 files changed

+110
-10
lines changed

4 files changed

+110
-10
lines changed

.changeset/chilly-sheep-reflect.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'create-expo-stack': minor
3+
---
4+
5+
users can save their favorite configurations

cli/src/utilities/configStorage.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { promises as fs } from 'fs';
2+
import path from 'path';
3+
import { CliResults } from '../types';
4+
import os from 'os';
5+
6+
const CONFIG_DIR = path.join(os.homedir(), '.create-expo-stack');
7+
const CONFIG_FILE = path.join(CONFIG_DIR, 'configurations.json');
8+
9+
interface AppConfig {
10+
name: string;
11+
cliResults: CliResults;
12+
}
13+
14+
const ensureConfigDirExists = async () => {
15+
try {
16+
await fs.mkdir(CONFIG_DIR, { recursive: true });
17+
} catch (error) {
18+
if (error.code !== 'EEXIST') {
19+
throw error;
20+
}
21+
}
22+
};
23+
24+
export const saveConfig = async (config: AppConfig): Promise<void> => {
25+
await ensureConfigDirExists();
26+
let configs = await loadConfigs();
27+
await fs.writeFile(CONFIG_FILE, JSON.stringify(configs, null, 2));
28+
};
29+
30+
export const loadConfigs = async (): Promise<AppConfig[]> => {
31+
try {
32+
const data = await fs.readFile(CONFIG_FILE, 'utf8');
33+
return JSON.parse(data);
34+
} catch (error) {
35+
if (error.code === 'ENOENT') {
36+
return [];
37+
}
38+
throw error;
39+
}
40+
};

cli/src/utilities/runCLI.ts

Lines changed: 64 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Toolbox } from 'gluegun/build/types/domain/toolbox';
2-
import { confirm, isCancel, cancel, multiselect, select } from '@clack/prompts';
2+
import { confirm, isCancel, cancel, multiselect, select, text } from '@clack/prompts';
33

44
import { defaultOptions } from '../constants';
55
import {
@@ -12,6 +12,7 @@ import {
1212
StylingSelect
1313
} from '../types';
1414
import { getDefaultPackageManagerVersion } from './getPackageManager';
15+
import { loadConfigs, saveConfig } from './configStorage';
1516

1617
const recommendedBunVersion = '1.0.14';
1718

@@ -30,6 +31,47 @@ export async function runCLI(toolbox: Toolbox, projectName: string): Promise<Cli
3031

3132
// Clear default packages
3233
cliResults.packages = [];
34+
35+
// Check whether the user has any saved create expo stack configurations
36+
const savedConfigs = await loadConfigs();
37+
38+
// If the user has saved configurations, ask if they would like to use them
39+
if (savedConfigs.length > 0) {
40+
const useSavedConfig = await confirm({
41+
message: 'Would you like to use a saved configuration?',
42+
initialValue: false
43+
});
44+
45+
if (isCancel(useSavedConfig)) {
46+
cancel('Cancelled... 👋');
47+
return process.exit(0);
48+
}
49+
50+
if (useSavedConfig) {
51+
const savedConfigSelect = await select({
52+
message: 'Which saved configuration would you like to use?',
53+
options: savedConfigs.map((config) => ({ value: config.name, label: config.name }))
54+
});
55+
56+
if (isCancel(savedConfigSelect)) {
57+
cancel('Cancelled... 👋');
58+
return process.exit(0);
59+
}
60+
61+
const selectedConfig = savedConfigs.find((config) => config.name === savedConfigSelect);
62+
63+
if (selectedConfig) {
64+
cliResults.packages = selectedConfig.cliResults.packages;
65+
cliResults.flags = selectedConfig.cliResults.flags;
66+
success(`Using saved configuration: ${selectedConfig.name}`);
67+
68+
return cliResults;
69+
} else {
70+
warning('Saved configuration not found, continuing with default configuration.');
71+
}
72+
}
73+
}
74+
3375
// Ask about TypeScript
3476
const shouldUseTypescript = await confirm({
3577
message: 'Would you like to use TypeScript with this project?',
@@ -327,18 +369,31 @@ export async function runCLI(toolbox: Toolbox, projectName: string): Promise<Cli
327369
} else {
328370
success(`No problem, skipping authentication for now.`);
329371
}
372+
}
330373

331-
const internationalizationSelect = await confirm({
332-
message: `What would you like to support internationalization?`,
333-
initialValue: false
374+
// Offer user ability to save configuration
375+
const shouldSaveConfig = await confirm({
376+
message: 'Would you like to save this configuration for future use?',
377+
initialValue: false
378+
});
379+
380+
if (isCancel(shouldSaveConfig)) {
381+
cancel('Cancelled... 👋');
382+
return process.exit(0);
383+
}
384+
385+
if (shouldSaveConfig) {
386+
const name = await text({
387+
message: 'What do you want to name this configuration?',
388+
placeholder: 'Default'
334389
});
335390

336-
if (internationalizationSelect) {
337-
cliResults.packages.push({ name: 'i18next', type: 'internationalization' });
338-
success(`You'll be using i18next for internationalization.`);
339-
} else {
340-
success(`No problem, skipping internationalization for now.`);
391+
if (isCancel(name)) {
392+
cancel('Cancelled... 👋');
393+
return process.exit(0);
341394
}
395+
396+
await saveConfig({ name, cliResults });
342397
}
343398

344399
return cliResults;

cli/src/utilities/validateProjectName.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export async function validateProjectName(
3131
}
3232

3333
if (shouldDeleteExistingProject) {
34-
s.start('Deleting existing project...');
34+
s.start('Deleting existing project. This may take a while...');
3535
await removeAsync(projectName);
3636
s.stop(`Deleted existing directory: ${projectName}`);
3737
return;

0 commit comments

Comments
 (0)