Skip to content

Commit 86352aa

Browse files
committed
refactor: improve types
1 parent eebf3cb commit 86352aa

File tree

3 files changed

+57
-26
lines changed

3 files changed

+57
-26
lines changed

packages/create-react-native-library/src/index.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ import { prompt } from './utils/prompt';
3434
import { resolveNpmPackageVersion } from './utils/resolveNpmPackageVersion';
3535

3636
type Args = Partial<Answers> & {
37-
name?: string;
3837
$0: string;
3938
[key: string]: unknown;
4039
};
@@ -95,7 +94,10 @@ async function create(_argv: Args) {
9594

9695
await fs.mkdirp(folder);
9796

98-
if (answers.reactNativeVersion !== SUPPORTED_REACT_NATIVE_VERSION) {
97+
if (
98+
answers.reactNativeVersion != null &&
99+
answers.reactNativeVersion !== SUPPORTED_REACT_NATIVE_VERSION
100+
) {
99101
printUsedRNVersion(answers.reactNativeVersion, config);
100102
}
101103

packages/create-react-native-library/src/input.ts

+32-18
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ export const acceptedArgs = {
146146

147147
export type ExampleApp = 'none' | 'test-app' | 'expo' | 'vanilla';
148148

149-
export type Answers = {
149+
type PromptAnswers = {
150150
directory: string;
151151
slug: string;
152152
description: string;
@@ -157,8 +157,12 @@ export type Answers = {
157157
languages: ProjectLanguages;
158158
type: ProjectType;
159159
example: ExampleApp;
160-
reactNativeVersion: string;
161-
local?: boolean;
160+
local: boolean;
161+
};
162+
163+
export type Answers = PromptAnswers & {
164+
name?: string;
165+
reactNativeVersion?: string;
162166
interactive?: boolean;
163167
};
164168

@@ -178,7 +182,7 @@ export async function createQuestions({
178182
// Ignore error
179183
}
180184

181-
const questions: Question<keyof Answers>[] = [
185+
const questions: Question<keyof PromptAnswers>[] = [
182186
{
183187
type:
184188
local == null &&
@@ -194,7 +198,7 @@ export async function createQuestions({
194198
type: (_, answers) => (name && !(answers.local ?? local) ? null : 'text'),
195199
name: 'directory',
196200
message: `Where do you want to create the library?`,
197-
initial: (_: string, answers: Answers) => {
201+
initial: (_, answers) => {
198202
if ((answers.local ?? local) && name && !name?.includes('/')) {
199203
return `modules/${name}`;
200204
}
@@ -207,7 +211,7 @@ export async function createQuestions({
207211
}
208212

209213
if (fs.pathExistsSync(path.join(process.cwd(), input))) {
210-
return 'Folder already exists';
214+
return 'Directory already exists';
211215
}
212216

213217
return true;
@@ -218,8 +222,10 @@ export async function createQuestions({
218222
type: 'text',
219223
name: 'slug',
220224
message: 'What is the name of the npm package?',
221-
initial: (_: string, answers: Answers) => {
222-
const basename = path.basename(answers.directory ?? name ?? '');
225+
initial: (_, answers) => {
226+
const basename = path.basename(
227+
typeof answers.directory === 'string' ? answers.directory : name ?? ''
228+
);
223229

224230
if (validateNpmPackage(basename).validForNewPackages) {
225231
if (/^(@|react-native)/.test(basename)) {
@@ -260,8 +266,11 @@ export async function createQuestions({
260266
type: (_, answers) => (answers.local ?? local ? null : 'text'),
261267
name: 'authorUrl',
262268
message: 'What is the URL for the package author?',
263-
// @ts-expect-error this is supported, but types are wrong
264-
initial: async (_: string, answers: Answers) => {
269+
initial: async (_, answers) => {
270+
if (typeof answers.authorEmail !== 'string') {
271+
return '';
272+
}
273+
265274
try {
266275
const username = await githubUsername(answers.authorEmail);
267276

@@ -270,16 +279,20 @@ export async function createQuestions({
270279
// Ignore error
271280
}
272281

273-
return undefined;
282+
return '';
274283
},
275284
validate: (input) => /^https?:\/\//.test(input) || 'Must be a valid URL',
276285
},
277286
{
278287
type: (_, answers) => (answers.local ?? local ? null : 'text'),
279288
name: 'repoUrl',
280289
message: 'What is the URL for the repository?',
281-
initial: (_: string, answers: Answers) => {
282-
if (/^https?:\/\/github.com\/[^/]+/.test(answers.authorUrl)) {
290+
initial: (_, answers) => {
291+
if (
292+
typeof answers.authorUrl === 'string' &&
293+
typeof answers.slug === 'string' &&
294+
/^https?:\/\/github.com\/[^/]+/.test(answers.authorUrl)
295+
) {
283296
return `${answers.authorUrl}/${answers.slug
284297
.replace(/^@/, '')
285298
.replace(/\//g, '-')}`;
@@ -301,7 +314,8 @@ export async function createQuestions({
301314
message: 'Which languages do you want to use?',
302315
choices: (_, values) => {
303316
return LANGUAGE_CHOICES.filter((choice) => {
304-
if (choice.types) {
317+
if (choice.types && typeof values.type === 'string') {
318+
// @ts-expect-error `includes doesn't support checking arbitrary types
305319
return choice.types.includes(values.type);
306320
}
307321

@@ -339,9 +353,9 @@ export async function createQuestions({
339353
return questions;
340354
}
341355

342-
export function createMetadata(answers: Answers) {
356+
export function createMetadata(answers: Partial<PromptAnswers>) {
343357
// Some of the passed args can already be derived from the generated package.json file.
344-
const ignoredAnswers: (keyof Answers | 'name')[] = [
358+
const ignoredAnswers: (keyof Answers)[] = [
345359
'name',
346360
'directory',
347361
'slug',
@@ -356,9 +370,9 @@ export function createMetadata(answers: Answers) {
356370
'interactive',
357371
];
358372

359-
type AnswerEntries<T extends keyof Answers = keyof Answers> = [
373+
type AnswerEntries<T extends keyof PromptAnswers = keyof PromptAnswers> = [
360374
T,
361-
Answers[T],
375+
PromptAnswers[T],
362376
][];
363377

364378
const libraryMetadata = Object.fromEntries(

packages/create-react-native-library/src/utils/prompt.ts

+21-6
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,36 @@
1-
import prompts, { type Answers, type InitialReturnValue } from 'prompts';
21
import kleur from 'kleur';
2+
import prompts, {
3+
type Answers,
4+
type InitialReturnValue,
5+
type PromptType,
6+
} from 'prompts';
37

48
type Choice = {
59
title: string;
610
value: string;
711
description?: string;
812
};
913

10-
export type Question<T extends string> = Omit<
11-
prompts.PromptObject<T>,
12-
'validate' | 'name' | 'choices'
13-
> & {
14+
export type Question<T extends string> = {
1415
name: T;
16+
type:
17+
| PromptType
18+
| null
19+
| ((
20+
prev: unknown,
21+
values: Partial<Record<T, unknown>>
22+
) => PromptType | null);
23+
message: string;
1524
validate?: (value: string) => boolean | string;
1625
choices?:
1726
| Choice[]
18-
| ((prev: unknown, values: Partial<Answers<T>>) => Choice[]);
27+
| ((prev: unknown, values: Partial<Record<T, unknown>>) => Choice[]);
28+
initial?:
29+
| InitialReturnValue
30+
| ((
31+
prev: unknown,
32+
values: Partial<Record<T, unknown>>
33+
) => InitialReturnValue | Promise<InitialReturnValue>);
1934
default?: InitialReturnValue;
2035
};
2136

0 commit comments

Comments
 (0)