Skip to content

Commit cb8f969

Browse files
atljsatya164
andauthored
feat(crnl): store metadata with new projects (#551)
### Summary We need a way to track the project information since we are going to introduce a library upgrade helper. With this PR, when you create a new library using `create-react-native-library`, it will automatically save; 1. Version of `create-react-native-library` used to create the project 2. The library type (native module, native view, etc.) 3. The languages used (Kotlin, objective-c, etc.) into `create-react-native-library.json` file at the root. ### Test plan <!-- List the steps with which we can test this change. Provide screenshots if this changes anything visual. --> 1. Build and run `create-react-native-library` 2. Note down the options you have picked 3. Make sure the `create-react-native-library.json` file has been generated at the root of your library 4. Check the version, library type, and languages fields and make sure they are using correct data. --------- Co-authored-by: Satyajit Sahoo <[email protected]>
1 parent 48b0e9a commit cb8f969

File tree

2 files changed

+91
-49
lines changed

2 files changed

+91
-49
lines changed

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

Lines changed: 91 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import githubUsername from 'github-username';
1010
import prompts, { type PromptObject } from './utils/prompts';
1111
import generateExampleApp from './utils/generateExampleApp';
1212
import { spawn } from './utils/spawn';
13+
import { version } from '../package.json';
1314

1415
const FALLBACK_BOB_VERSION = '0.20.0';
1516

@@ -107,6 +108,7 @@ type ProjectType =
107108
| 'library';
108109

109110
type Answers = {
111+
name: string;
110112
slug: string;
111113
description: string;
112114
authorName: string;
@@ -117,6 +119,7 @@ type Answers = {
117119
type?: ProjectType;
118120
example?: boolean;
119121
reactNativeVersion?: string;
122+
local?: boolean;
120123
};
121124

122125
const LANGUAGE_CHOICES: {
@@ -265,7 +268,10 @@ const args: Record<ArgName, yargs.Options> = {
265268

266269
// FIXME: fix the type
267270
// eslint-disable-next-line @typescript-eslint/no-explicit-any
268-
async function create(argv: yargs.Arguments<any>) {
271+
async function create(_argv: yargs.Arguments<any>) {
272+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
273+
const { _, $0, ...argv } = _argv;
274+
269275
let local = false;
270276

271277
if (typeof argv.local === 'boolean') {
@@ -355,13 +361,11 @@ async function create(argv: yargs.Arguments<any>) {
355361

356362
const basename = path.basename(folder);
357363

358-
const questions: Record<
359-
ArgName,
360-
Omit<PromptObject<keyof Answers>, 'validate'> & {
361-
validate?: (value: string) => boolean | string;
362-
}
363-
> = {
364-
'slug': {
364+
const questions: (Omit<PromptObject<keyof Answers>, 'validate' | 'name'> & {
365+
validate?: (value: string) => boolean | string;
366+
name: string;
367+
})[] = [
368+
{
365369
type: 'text',
366370
name: 'slug',
367371
message: 'What is the name of the npm package?',
@@ -374,28 +378,28 @@ async function create(argv: yargs.Arguments<any>) {
374378
validateNpmPackage(input).validForNewPackages ||
375379
'Must be a valid npm package name',
376380
},
377-
'description': {
381+
{
378382
type: 'text',
379383
name: 'description',
380384
message: 'What is the description for the package?',
381385
validate: (input) => Boolean(input) || 'Cannot be empty',
382386
},
383-
'author-name': {
387+
{
384388
type: local ? null : 'text',
385389
name: 'authorName',
386390
message: 'What is the name of package author?',
387391
initial: name,
388392
validate: (input) => Boolean(input) || 'Cannot be empty',
389393
},
390-
'author-email': {
394+
{
391395
type: local ? null : 'text',
392396
name: 'authorEmail',
393397
message: 'What is the email address for the package author?',
394398
initial: email,
395399
validate: (input) =>
396400
/^\S+@\S+$/.test(input) || 'Must be a valid email address',
397401
},
398-
'author-url': {
402+
{
399403
type: local ? null : 'text',
400404
name: 'authorUrl',
401405
message: 'What is the URL for the package author?',
@@ -413,7 +417,7 @@ async function create(argv: yargs.Arguments<any>) {
413417
},
414418
validate: (input) => /^https?:\/\//.test(input) || 'Must be a valid URL',
415419
},
416-
'repo-url': {
420+
{
417421
type: local ? null : 'text',
418422
name: 'repoUrl',
419423
message: 'What is the URL for the repository?',
@@ -428,13 +432,13 @@ async function create(argv: yargs.Arguments<any>) {
428432
},
429433
validate: (input) => /^https?:\/\//.test(input) || 'Must be a valid URL',
430434
},
431-
'type': {
435+
{
432436
type: 'select',
433437
name: 'type',
434438
message: 'What type of library do you want to develop?',
435439
choices: TYPE_CHOICES,
436440
},
437-
'languages': {
441+
{
438442
type: 'select',
439443
name: 'languages',
440444
message: 'Which languages do you want to use?',
@@ -448,15 +452,15 @@ async function create(argv: yargs.Arguments<any>) {
448452
});
449453
},
450454
},
451-
};
455+
];
452456

453457
// Validate arguments passed to the CLI
454458
for (const [key, value] of Object.entries(argv)) {
455459
if (value == null) {
456460
continue;
457461
}
458462

459-
const question = questions[key as ArgName];
463+
const question = questions.find((q) => q.name === key);
460464

461465
if (question == null) {
462466
continue;
@@ -494,49 +498,37 @@ async function create(argv: yargs.Arguments<any>) {
494498
}
495499
}
496500

497-
const {
498-
slug,
499-
description,
500-
authorName,
501-
authorEmail,
502-
authorUrl,
503-
repoUrl,
504-
type = 'module-mixed',
505-
languages = type === 'library' ? 'js' : 'java-objc',
506-
example: hasExample,
507-
reactNativeVersion,
508-
} = {
501+
const answers = {
509502
...argv,
503+
local,
510504
...(await prompts(
511-
Object.entries(questions)
512-
.filter(([k, v]) => {
513-
// Skip 'with-recommended-options' question if type of language is passed
505+
questions
506+
.filter((question) => {
507+
// Skip questions which are passed as parameter and pass validation
514508
if (
515-
k === 'with-recommended-options' &&
516-
(argv.languages || argv.type)
509+
argv[question.name] != null &&
510+
question.validate?.(argv[question.name]) !== false
517511
) {
518512
return false;
519513
}
520514

521-
// Skip questions which are passed as parameter and pass validation
522-
if (argv[k] != null && v.validate?.(argv[k]) !== false) {
523-
return false;
524-
}
525-
526515
// Skip questions with a single choice
527-
if (Array.isArray(v.choices) && v.choices.length === 1) {
516+
if (
517+
Array.isArray(question.choices) &&
518+
question.choices.length === 1
519+
) {
528520
return false;
529521
}
530522

531523
return true;
532524
})
533-
.map(([, v]) => {
534-
const { type, choices } = v;
525+
.map((question) => {
526+
const { type, choices } = question;
535527

536528
// Skip dynamic questions with a single choice
537529
if (type === 'select' && typeof choices === 'function') {
538530
return {
539-
...v,
531+
...question,
540532
type: (prev, values, prompt) => {
541533
const result = choices(prev, { ...argv, ...values }, prompt);
542534

@@ -549,24 +541,37 @@ async function create(argv: yargs.Arguments<any>) {
549541
};
550542
}
551543

552-
return v;
544+
return question;
553545
})
554546
)),
555547
} as Answers;
556548

549+
const {
550+
slug,
551+
description,
552+
authorName,
553+
authorEmail,
554+
authorUrl,
555+
repoUrl,
556+
type = 'module-mixed',
557+
languages = type === 'library' ? 'js' : 'java-objc',
558+
example: hasExample,
559+
reactNativeVersion,
560+
} = answers;
561+
557562
// Get latest version of Bob from NPM
558-
let version: string;
563+
let bobVersion: string;
559564

560565
try {
561-
version = await Promise.race([
566+
bobVersion = await Promise.race([
562567
new Promise<string>((resolve) => {
563568
setTimeout(() => resolve(FALLBACK_BOB_VERSION), 1000);
564569
}),
565570
spawn('npm', ['view', 'react-native-builder-bob', 'dist-tags.latest']),
566571
]);
567572
} catch (e) {
568573
// Fallback to a known version if we couldn't fetch
569-
version = FALLBACK_BOB_VERSION;
574+
bobVersion = FALLBACK_BOB_VERSION;
570575
}
571576

572577
const moduleType = type.startsWith('view-') ? 'view' : 'module';
@@ -598,7 +603,7 @@ async function create(argv: yargs.Arguments<any>) {
598603

599604
const options = {
600605
bob: {
601-
version: version || FALLBACK_BOB_VERSION,
606+
version: bobVersion || FALLBACK_BOB_VERSION,
602607
},
603608
project: {
604609
slug,
@@ -795,6 +800,40 @@ async function create(argv: yargs.Arguments<any>) {
795800
}
796801
}
797802

803+
// Some of the passed args can already be derived from the generated package.json file.
804+
const ignoredAnswers: (keyof Answers)[] = [
805+
'name',
806+
'slug',
807+
'description',
808+
'authorName',
809+
'authorEmail',
810+
'authorUrl',
811+
'repoUrl',
812+
'example',
813+
'reactNativeVersion',
814+
'local',
815+
];
816+
817+
type AnswerEntries<T extends keyof Answers = keyof Answers> = [
818+
T,
819+
Answers[T],
820+
][];
821+
822+
const libraryMetadata = Object.fromEntries(
823+
(Object.entries(answers) as AnswerEntries).filter(
824+
([answer]) => !ignoredAnswers.includes(answer)
825+
)
826+
);
827+
libraryMetadata.version = version;
828+
829+
const libraryPackageJson = await fs.readJson(
830+
path.join(folder, 'package.json')
831+
);
832+
libraryPackageJson['create-react-native-library'] = libraryMetadata;
833+
await fs.writeJson(path.join(folder, 'package.json'), libraryPackageJson, {
834+
spaces: 2,
835+
});
836+
798837
spinner.succeed(
799838
`Project created successfully at ${kleur.yellow(
800839
path.relative(process.cwd(), folder)
@@ -916,4 +955,8 @@ yargs
916955

917956
process.exit(1);
918957
})
958+
.parserConfiguration({
959+
// don't pass kebab-case args to handler.
960+
'strip-dashed': true,
961+
})
919962
.strict().argv;

tsconfig.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
"react-native-builder-bob": ["./packages/react-native-builder-bob/src"]
99
},
1010
"outDir": "./typescript",
11-
"composite": true,
1211
"allowUnreachableCode": false,
1312
"allowUnusedLabels": false,
1413
"esModuleInterop": true,

0 commit comments

Comments
 (0)