create-react-native-library 0.44.3 → 0.45.0

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.
Files changed (60) hide show
  1. package/lib/{utils → exampleApp}/addCodegenBuildScript.js +8 -2
  2. package/lib/exampleApp/addCodegenBuildScript.js.map +1 -0
  3. package/lib/exampleApp/dependencies.js +32 -0
  4. package/lib/exampleApp/dependencies.js.map +1 -0
  5. package/lib/{utils → exampleApp}/generateExampleApp.js +2 -2
  6. package/lib/exampleApp/generateExampleApp.js.map +1 -0
  7. package/lib/index.js +91 -624
  8. package/lib/index.js.map +1 -1
  9. package/lib/inform.js +91 -0
  10. package/lib/inform.js.map +1 -0
  11. package/lib/input.js +266 -0
  12. package/lib/input.js.map +1 -0
  13. package/lib/template.js +175 -0
  14. package/lib/template.js.map +1 -0
  15. package/lib/utils/assert.js +59 -0
  16. package/lib/utils/assert.js.map +1 -0
  17. package/lib/utils/initialCommit.js +34 -0
  18. package/lib/utils/initialCommit.js.map +1 -0
  19. package/lib/utils/promiseWithFallback.js +25 -0
  20. package/lib/utils/promiseWithFallback.js.map +1 -0
  21. package/package.json +2 -2
  22. package/templates/common/$package.json +2 -2
  23. package/templates/common/CONTRIBUTING.md +0 -25
  24. package/templates/common/babel.config.js +1 -3
  25. package/templates/common/tsconfig.json +1 -0
  26. package/templates/common-local/$package.json +1 -1
  27. package/templates/example-module-legacy/example/src/App.tsx +25 -0
  28. package/templates/example-module-new/example/src/App.tsx +20 -0
  29. package/templates/example-view/example/src/App.tsx +23 -0
  30. package/templates/js-library/src/index.tsx +2 -2
  31. package/templates/kotlin-library-new/android/src/main/java/com/{%- project.package_dir %}/{%- project.name %}Package.kt +2 -2
  32. package/templates/native-common/android/build.gradle +3 -19
  33. package/templates/objc-library/ios/{%- project.name %}.h +4 -4
  34. package/templates/objc-library/ios/{%- project.name %}.mm +1 -6
  35. package/templates/objc-view-new/ios/{%- project.name %}View.h +0 -3
  36. package/templates/objc-view-new/ios/{%- project.name %}View.mm +6 -6
  37. package/lib/utils/addCodegenBuildScript.js.map +0 -1
  38. package/lib/utils/generateExampleApp.js.map +0 -1
  39. package/templates/common-example/example/src/App.tsx +0 -57
  40. package/templates/kotlin-library-mixed/android/src/main/java/com/{%- project.package_dir %}/{%- project.name %}Module.kt +0 -24
  41. package/templates/kotlin-library-mixed/android/src/main/java/com/{%- project.package_dir %}/{%- project.name %}Package.kt +0 -35
  42. package/templates/kotlin-library-mixed/android/src/newarch/{%- project.name %}Spec.kt +0 -7
  43. package/templates/kotlin-library-mixed/android/src/oldarch/{%- project.name %}Spec.kt +0 -11
  44. package/templates/kotlin-view-mixed/android/src/main/java/com/{%- project.package_dir %}/{%- project.name %}Package.kt +0 -19
  45. package/templates/kotlin-view-mixed/android/src/main/java/com/{%- project.package_dir %}/{%- project.name %}View.kt +0 -15
  46. package/templates/kotlin-view-mixed/android/src/main/java/com/{%- project.package_dir %}/{%- project.name %}ViewManager.kt +0 -28
  47. package/templates/kotlin-view-mixed/android/src/newarch/{%- project.name %}ViewManagerSpec.kt +0 -21
  48. package/templates/kotlin-view-mixed/android/src/oldarch/{%- project.name %}ViewManagerSpec.kt +0 -9
  49. package/templates/native-library-mixed/react-native.config.js +0 -16
  50. package/templates/native-library-mixed/src/Native{%- project.name %}.ts +0 -8
  51. package/templates/native-library-mixed/src/index.tsx +0 -29
  52. package/templates/native-view-mixed/react-native.config.js +0 -16
  53. package/templates/native-view-mixed/src/index.tsx +0 -2
  54. package/templates/native-view-mixed/src/{%- project.name %}ViewNativeComponent.ts +0 -8
  55. package/templates/objc-view-mixed/ios/Utils.h +0 -8
  56. package/templates/objc-view-mixed/ios/Utils.m +0 -26
  57. package/templates/objc-view-mixed/ios/{%- project.name %}View.h +0 -17
  58. package/templates/objc-view-mixed/ios/{%- project.name %}View.mm +0 -60
  59. package/templates/objc-view-mixed/ios/{%- project.name %}ViewManager.mm +0 -23
  60. /package/templates/{common-example → example-common}/example/metro.config.js +0 -0
package/lib/index.js CHANGED
@@ -1,183 +1,28 @@
1
1
  "use strict";
2
2
 
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.assertOptions = assertOptions;
7
3
  var _path = _interopRequireDefault(require("path"));
8
4
  var _fsExtra = _interopRequireDefault(require("fs-extra"));
9
- var _ejs = _interopRequireDefault(require("ejs"));
10
- var _dedent = _interopRequireDefault(require("dedent"));
11
5
  var _kleur = _interopRequireDefault(require("kleur"));
12
6
  var _yargs = _interopRequireDefault(require("yargs"));
13
7
  var _ora = _interopRequireDefault(require("ora"));
14
- var _nodeAssert = _interopRequireDefault(require("node:assert"));
15
- var _validateNpmPackageName = _interopRequireDefault(require("validate-npm-package-name"));
16
- var _githubUsername = _interopRequireDefault(require("github-username"));
17
8
  var _prompts = _interopRequireDefault(require("./utils/prompts"));
18
- var _generateExampleApp = _interopRequireDefault(require("./utils/generateExampleApp"));
19
- var _spawn = require("./utils/spawn");
20
- var _package = require("../package.json");
21
- var _addCodegenBuildScript = require("./utils/addCodegenBuildScript");
9
+ var _generateExampleApp = _interopRequireDefault(require("./exampleApp/generateExampleApp"));
10
+ var _addCodegenBuildScript = require("./exampleApp/addCodegenBuildScript");
11
+ var _initialCommit = require("./utils/initialCommit");
12
+ var _assert = require("./utils/assert");
13
+ var _promiseWithFallback = require("./utils/promiseWithFallback");
14
+ var _template = require("./template");
15
+ var _input = require("./input");
16
+ var _dependencies = require("./exampleApp/dependencies");
17
+ var _inform = require("./inform");
22
18
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
23
- const FALLBACK_BOB_VERSION = '0.29.0';
24
- const BINARIES = [/(gradlew|\.(jar|keystore|png|jpg|gif))$/, /\$\.yarn(?![a-z])/];
25
- const COMMON_FILES = _path.default.resolve(__dirname, '../templates/common');
26
- const COMMON_EXAMPLE_FILES = _path.default.resolve(__dirname, '../templates/common-example');
27
- const COMMON_LOCAL_FILES = _path.default.resolve(__dirname, '../templates/common-local');
28
- const JS_FILES = _path.default.resolve(__dirname, '../templates/js-library');
29
- const EXPO_FILES = _path.default.resolve(__dirname, '../templates/expo-library');
30
- const CPP_FILES = _path.default.resolve(__dirname, '../templates/cpp-library');
31
- const NATIVE_COMMON_FILES = _path.default.resolve(__dirname, '../templates/native-common');
32
- const NATIVE_COMMON_EXAMPLE_FILES = _path.default.resolve(__dirname, '../templates/native-common-example');
33
- const NATIVE_FILES = {
34
- module_legacy: _path.default.resolve(__dirname, '../templates/native-library-legacy'),
35
- module_new: _path.default.resolve(__dirname, '../templates/native-library-new'),
36
- module_mixed: _path.default.resolve(__dirname, '../templates/native-library-mixed'),
37
- view_legacy: _path.default.resolve(__dirname, '../templates/native-view-legacy'),
38
- view_mixed: _path.default.resolve(__dirname, '../templates/native-view-mixed'),
39
- view_new: _path.default.resolve(__dirname, '../templates/native-view-new')
40
- };
41
- const OBJC_FILES = {
42
- module_common: _path.default.resolve(__dirname, '../templates/objc-library'),
43
- view_legacy: _path.default.resolve(__dirname, '../templates/objc-view-legacy'),
44
- view_mixed: _path.default.resolve(__dirname, '../templates/objc-view-mixed'),
45
- view_new: _path.default.resolve(__dirname, '../templates/objc-view-new')
46
- };
47
- const KOTLIN_FILES = {
48
- module_legacy: _path.default.resolve(__dirname, '../templates/kotlin-library-legacy'),
49
- module_new: _path.default.resolve(__dirname, '../templates/kotlin-library-new'),
50
- module_mixed: _path.default.resolve(__dirname, '../templates/kotlin-library-mixed'),
51
- view_legacy: _path.default.resolve(__dirname, '../templates/kotlin-view-legacy'),
52
- view_mixed: _path.default.resolve(__dirname, '../templates/kotlin-view-mixed'),
53
- view_new: _path.default.resolve(__dirname, '../templates/kotlin-view-new')
54
- };
55
- const SWIFT_FILES = {
56
- module_legacy: _path.default.resolve(__dirname, '../templates/swift-library-legacy'),
57
- view_legacy: _path.default.resolve(__dirname, '../templates/swift-view-legacy')
58
- };
59
- const LANGUAGE_CHOICES = [{
60
- title: 'Kotlin & Objective-C',
61
- value: 'kotlin-objc',
62
- types: ['module-legacy', 'module-new', 'module-mixed', 'view-mixed', 'view-new', 'view-legacy']
63
- }, {
64
- title: 'Kotlin & Swift',
65
- value: 'kotlin-swift',
66
- types: ['module-legacy', 'view-legacy']
67
- }, {
68
- title: 'C++ for Android & iOS',
69
- value: 'cpp',
70
- types: ['module-legacy', 'module-mixed', 'module-new']
71
- }, {
72
- title: 'JavaScript for Android, iOS & Web',
73
- value: 'js',
74
- types: ['library']
75
- }];
76
- const EXAMPLE_CHOICES = [{
77
- title: 'Vanilla',
78
- value: 'vanilla',
79
- description: "provides access to app's native code",
80
- disabled: false
81
- }, {
82
- title: 'Test app',
83
- value: 'test-app',
84
- description: "app's native code is abstracted away",
85
- // The test app is disabled for now until proper
86
- // Codegen spec shipping is implemented
87
- disabled: !process.env.CRNL_ENABLE_TEST_APP
88
- }, {
89
- title: 'Expo',
90
- value: 'expo',
91
- description: 'managed expo project with web support',
92
- disabled: false
93
- }].filter(choice => !choice.disabled);
94
- const NEWARCH_DESCRIPTION = 'requires new arch (experimental)';
95
- const BACKCOMPAT_DESCRIPTION = 'supports new arch (experimental)';
96
- const TYPE_CHOICES = [{
97
- title: 'JavaScript library',
98
- value: 'library',
99
- description: 'supports Expo Go and Web'
100
- }, {
101
- title: 'Native module',
102
- value: 'module-legacy',
103
- description: 'bridge for native APIs to JS'
104
- }, {
105
- title: 'Native view',
106
- value: 'view-legacy',
107
- description: 'bridge for native views to JS'
108
- }, {
109
- title: 'Turbo module with backward compat',
110
- value: 'module-mixed',
111
- description: BACKCOMPAT_DESCRIPTION
112
- }, {
113
- title: 'Turbo module',
114
- value: 'module-new',
115
- description: NEWARCH_DESCRIPTION
116
- }, {
117
- title: 'Fabric view with backward compat',
118
- value: 'view-mixed',
119
- description: BACKCOMPAT_DESCRIPTION
120
- }, {
121
- title: 'Fabric view',
122
- value: 'view-new',
123
- description: NEWARCH_DESCRIPTION
124
- }];
125
- const args = {
126
- 'slug': {
127
- description: 'Name of the npm package',
128
- type: 'string'
129
- },
130
- 'description': {
131
- description: 'Description of the npm package',
132
- type: 'string'
133
- },
134
- 'author-name': {
135
- description: 'Name of the package author',
136
- type: 'string'
137
- },
138
- 'author-email': {
139
- description: 'Email address of the package author',
140
- type: 'string'
141
- },
142
- 'author-url': {
143
- description: 'URL for the package author',
144
- type: 'string'
145
- },
146
- 'repo-url': {
147
- description: 'URL for the repository',
148
- type: 'string'
149
- },
150
- 'languages': {
151
- description: 'Languages you want to use',
152
- choices: LANGUAGE_CHOICES.map(({
153
- value
154
- }) => value)
155
- },
156
- 'type': {
157
- description: 'Type of library you want to develop',
158
- choices: TYPE_CHOICES.map(({
159
- value
160
- }) => value)
161
- },
162
- 'react-native-version': {
163
- description: 'Version of React Native to use, uses latest if not specified',
164
- type: 'string'
165
- },
166
- 'local': {
167
- description: 'Whether to create a local library',
168
- type: 'boolean'
169
- },
170
- 'example': {
171
- description: 'Type of the example app to create',
172
- type: 'string',
173
- choices: EXAMPLE_CHOICES.map(({
174
- value
175
- }) => value)
176
- }
177
- };
178
-
179
- // FIXME: fix the type
180
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
19
+ const FALLBACK_BOB_VERSION = '0.32.0';
20
+ _yargs.default.command('$0 [name]', 'create a react native library', _input.acceptedArgs,
21
+ // @ts-expect-error Some types are still incompatible
22
+ create).demandCommand().recommendCommands().fail(_inform.printErrorHelp).parserConfiguration({
23
+ // don't pass kebab-case args to handler.
24
+ 'strip-dashed': true
25
+ }).strict().argv;
181
26
  async function create(_argv) {
182
27
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
183
28
  const {
@@ -187,7 +32,77 @@ async function create(_argv) {
187
32
  } = _argv;
188
33
 
189
34
  // Prefetch bob version in background while asking questions
190
- const bobVersionPromise = (0, _spawn.spawn)('npm', ['view', 'react-native-builder-bob', 'dist-tags.latest']);
35
+ const resolveBobVersion = (0, _promiseWithFallback.resolveBobVersionWithFallback)(FALLBACK_BOB_VERSION);
36
+ const local = await promptLocalLibrary(argv);
37
+ const folder = await promptPath(argv, local);
38
+ await (0, _assert.assertNpxExists)();
39
+ const basename = _path.default.basename(folder);
40
+ const {
41
+ questions,
42
+ singleChoiceAnswers
43
+ } = await (0, _input.createQuestions)({
44
+ basename,
45
+ local,
46
+ argv
47
+ });
48
+ (0, _assert.assertUserInput)(questions, argv);
49
+ const promptAnswers = await (0, _prompts.default)(questions);
50
+ const answers = {
51
+ ...argv,
52
+ local,
53
+ ...singleChoiceAnswers,
54
+ ...promptAnswers
55
+ };
56
+ (0, _assert.assertUserInput)(questions, answers);
57
+ const bobVersion = await resolveBobVersion();
58
+ const config = (0, _template.generateTemplateConfiguration)({
59
+ bobVersion,
60
+ basename,
61
+ answers
62
+ });
63
+ await _fsExtra.default.mkdirp(folder);
64
+ if (answers.reactNativeVersion != null) {
65
+ (0, _inform.printUsedRNVersion)(answers.reactNativeVersion, config);
66
+ }
67
+ const spinner = (0, _ora.default)().start();
68
+ if (config.example !== 'none') {
69
+ spinner.text = 'Generating example app';
70
+ await (0, _generateExampleApp.default)({
71
+ type: config.example,
72
+ dest: folder,
73
+ arch: config.project.arch,
74
+ project: config.project,
75
+ bobVersion,
76
+ reactNativeVersion: answers.reactNativeVersion
77
+ });
78
+ }
79
+ spinner.text = 'Copying files';
80
+ await (0, _template.applyTemplates)(answers, config, folder);
81
+ const rootPackageJson = await _fsExtra.default.readJson(_path.default.join(folder, 'package.json'));
82
+ if (config.example !== 'none') {
83
+ const {
84
+ devDependencies
85
+ } = await (0, _dependencies.getDependencyVersionsFromExampleApp)(folder, config.example);
86
+ rootPackageJson.devDependencies = rootPackageJson.devDependencies ? {
87
+ ...rootPackageJson.devDependencies,
88
+ ...devDependencies
89
+ } : devDependencies;
90
+ }
91
+ if (config.example === 'vanilla' && config.project.arch !== 'legacy') {
92
+ (0, _addCodegenBuildScript.addCodegenBuildScript)(folder);
93
+ }
94
+ const libraryMetadata = (0, _input.createMetadata)(answers);
95
+ rootPackageJson['create-react-native-library'] = libraryMetadata;
96
+ await _fsExtra.default.writeJson(_path.default.join(folder, 'package.json'), rootPackageJson, {
97
+ spaces: 2
98
+ });
99
+ if (!local) {
100
+ await (0, _initialCommit.createInitialGitCommit)(folder);
101
+ }
102
+ spinner.succeed(`Project created successfully at ${_kleur.default.yellow(_path.default.relative(process.cwd(), folder))}!\n`);
103
+ await (0, _inform.printNextSteps)(local, folder, config);
104
+ }
105
+ async function promptLocalLibrary(argv) {
191
106
  let local = false;
192
107
  if (typeof argv.local === 'boolean') {
193
108
  local = argv.local;
@@ -204,6 +119,9 @@ async function create(_argv) {
204
119
  local = answers.local;
205
120
  }
206
121
  }
122
+ return local;
123
+ }
124
+ async function promptPath(argv, local) {
207
125
  let folder;
208
126
  if (argv.name && !local) {
209
127
  folder = _path.default.join(process.cwd(), argv.name);
@@ -229,457 +147,6 @@ async function create(_argv) {
229
147
  console.log(`A folder already exists at ${_kleur.default.blue(folder)}! Please specify another folder name or delete the existing one.`);
230
148
  process.exit(1);
231
149
  }
232
- try {
233
- await (0, _spawn.spawn)('npx', ['--help']);
234
- } catch (error) {
235
- // @ts-expect-error: TS doesn't know about `code`
236
- if (error != null && error.code === 'ENOENT') {
237
- console.log(`Couldn't find ${_kleur.default.blue('npx')}! Please install it by running ${_kleur.default.blue('npm install -g npx')}`);
238
- process.exit(1);
239
- } else {
240
- throw error;
241
- }
242
- }
243
- let name, email;
244
- try {
245
- name = await (0, _spawn.spawn)('git', ['config', '--get', 'user.name']);
246
- email = await (0, _spawn.spawn)('git', ['config', '--get', 'user.email']);
247
- } catch (e) {
248
- // Ignore error
249
- }
250
- const basename = _path.default.basename(folder);
251
- const questions = [{
252
- type: 'text',
253
- name: 'slug',
254
- message: 'What is the name of the npm package?',
255
- initial: (0, _validateNpmPackageName.default)(basename).validForNewPackages ? /^(@|react-native)/.test(basename) ? basename : `react-native-${basename}` : undefined,
256
- validate: input => (0, _validateNpmPackageName.default)(input).validForNewPackages || 'Must be a valid npm package name'
257
- }, {
258
- type: 'text',
259
- name: 'description',
260
- message: 'What is the description for the package?',
261
- validate: input => Boolean(input) || 'Cannot be empty'
262
- }, {
263
- type: local ? null : 'text',
264
- name: 'authorName',
265
- message: 'What is the name of package author?',
266
- initial: name,
267
- validate: input => Boolean(input) || 'Cannot be empty'
268
- }, {
269
- type: local ? null : 'text',
270
- name: 'authorEmail',
271
- message: 'What is the email address for the package author?',
272
- initial: email,
273
- validate: input => /^\S+@\S+$/.test(input) || 'Must be a valid email address'
274
- }, {
275
- type: local ? null : 'text',
276
- name: 'authorUrl',
277
- message: 'What is the URL for the package author?',
278
- // @ts-expect-error this is supported, but types are wrong
279
- initial: async previous => {
280
- try {
281
- const username = await (0, _githubUsername.default)(previous);
282
- return `https://github.com/${username}`;
283
- } catch (e) {
284
- // Ignore error
285
- }
286
- return undefined;
287
- },
288
- validate: input => /^https?:\/\//.test(input) || 'Must be a valid URL'
289
- }, {
290
- type: local ? null : 'text',
291
- name: 'repoUrl',
292
- message: 'What is the URL for the repository?',
293
- initial: (_, answers) => {
294
- if (/^https?:\/\/github.com\/[^/]+/.test(answers.authorUrl)) {
295
- return `${answers.authorUrl}/${answers.slug.replace(/^@/, '').replace(/\//g, '-')}`;
296
- }
297
- return '';
298
- },
299
- validate: input => /^https?:\/\//.test(input) || 'Must be a valid URL'
300
- }, {
301
- type: 'select',
302
- name: 'type',
303
- message: 'What type of library do you want to develop?',
304
- choices: TYPE_CHOICES
305
- }, {
306
- type: 'select',
307
- name: 'languages',
308
- message: 'Which languages do you want to use?',
309
- choices: (_, values) => {
310
- return LANGUAGE_CHOICES.filter(choice => {
311
- if (choice.types) {
312
- return choice.types.includes(values.type);
313
- }
314
- return true;
315
- });
316
- }
317
- }];
318
- if (!local) {
319
- questions.push({
320
- type: 'select',
321
- name: 'example',
322
- message: 'What type of example app do you want to create?',
323
- choices: (_, values) => {
324
- return EXAMPLE_CHOICES.filter(choice => {
325
- if (values.type) {
326
- return values.type === 'library' ? choice.value === 'expo' : choice.value !== 'expo';
327
- }
328
- return true;
329
- });
330
- }
331
- });
332
- }
333
- assertOptions(questions, argv);
334
- const singleChoiceAnswers = {};
335
- const finalQuestions = [];
336
- for (const question of questions) {
337
- // Skip questions which are passed as parameter and pass validation
338
- if (argv[question.name] != null && question.validate?.(argv[question.name]) !== false) {
339
- continue;
340
- }
341
-
342
- // Don't prompt questions with a single choice
343
- if (Array.isArray(question.choices) && question.choices.length === 1) {
344
- const onlyChoice = question.choices[0];
345
- singleChoiceAnswers[question.name] = onlyChoice.value;
346
- continue;
347
- }
348
- const {
349
- type,
350
- choices
351
- } = question;
352
-
353
- // Don't prompt dynamic questions with a single choice
354
- if (type === 'select' && typeof choices === 'function') {
355
- question.type = (prev, values, prompt) => {
356
- const dynamicChoices = choices(prev, {
357
- ...argv,
358
- ...values
359
- }, prompt);
360
- if (dynamicChoices && dynamicChoices.length === 1) {
361
- const onlyChoice = dynamicChoices[0];
362
- singleChoiceAnswers[question.name] = onlyChoice.value;
363
- return null;
364
- }
365
- return type;
366
- };
367
- }
368
- finalQuestions.push(question);
369
- }
370
- const promptAnswers = await (0, _prompts.default)(finalQuestions);
371
- const answers = {
372
- ...argv,
373
- local,
374
- ...singleChoiceAnswers,
375
- ...promptAnswers
376
- };
377
- assertOptions(questions, answers);
378
- const {
379
- slug,
380
- description,
381
- authorName,
382
- authorEmail,
383
- authorUrl,
384
- repoUrl,
385
- type = 'module-mixed',
386
- languages = type === 'library' ? 'js' : 'kotlin-objc',
387
- example = local ? 'none' : type === 'library' ? 'expo' : 'vanilla',
388
- reactNativeVersion
389
- } = answers;
390
-
391
- // Get latest version of Bob from NPM
392
- let bobVersion;
393
- try {
394
- bobVersion = await Promise.race([new Promise(resolve => {
395
- setTimeout(() => resolve(FALLBACK_BOB_VERSION), 1000);
396
- }), bobVersionPromise]);
397
- } catch (e) {
398
- // Fallback to a known version if we couldn't fetch
399
- bobVersion = FALLBACK_BOB_VERSION;
400
- }
401
- const moduleType = type.startsWith('view-') ? 'view' : 'module';
402
- const arch = type === 'module-new' || type === 'view-new' ? 'new' : type === 'module-mixed' || type === 'view-mixed' ? 'mixed' : 'legacy';
403
- const project = slug.replace(/^(react-native-|@[^/]+\/)/, '');
404
- let namespace;
405
- if (slug.startsWith('@') && slug.includes('/')) {
406
- namespace = slug.split('/')[0]?.replace(/[^a-z0-9]/g, '').toLowerCase();
407
- }
408
-
409
- // Create a package identifier with specified namespace when possible
410
- const pack = `${namespace ? `${namespace}.` : ''}${project.replace(/[^a-z0-9]/g, '').toLowerCase()}`;
411
- const options = {
412
- bob: {
413
- version: bobVersion || FALLBACK_BOB_VERSION
414
- },
415
- project: {
416
- slug,
417
- description,
418
- name: /^[A-Z]/.test(basename) && /^[a-z0-9]+$/i.test(basename) ?
419
- // If the project name is already in PascalCase, use it as-is
420
- basename :
421
- // Otherwise, convert it to PascalCase and remove any non-alphanumeric characters
422
- `${project.charAt(0).toUpperCase()}${project.replace(/[^a-z0-9](\w)/g, (_, $1) => $1.toUpperCase()).slice(1)}`,
423
- package: pack,
424
- package_dir: pack.replace(/\./g, '/'),
425
- package_cpp: pack.replace(/\./g, '_'),
426
- identifier: slug.replace(/[^a-z0-9]+/g, '-').replace(/^-/, ''),
427
- native: languages !== 'js',
428
- arch,
429
- cpp: languages === 'cpp',
430
- swift: languages === 'kotlin-swift',
431
- view: moduleType === 'view',
432
- module: moduleType === 'module'
433
- },
434
- author: {
435
- name: authorName,
436
- email: authorEmail,
437
- url: authorUrl
438
- },
439
- repo: repoUrl,
440
- example,
441
- year: new Date().getFullYear()
442
- };
443
- const copyDir = async (source, dest) => {
444
- await _fsExtra.default.mkdirp(dest);
445
- const files = await _fsExtra.default.readdir(source);
446
- for (const f of files) {
447
- const target = _path.default.join(dest, _ejs.default.render(f.replace(/^\$/, ''), options, {
448
- openDelimiter: '{',
449
- closeDelimiter: '}'
450
- }));
451
- const file = _path.default.join(source, f);
452
- const stats = await _fsExtra.default.stat(file);
453
- if (stats.isDirectory()) {
454
- await copyDir(file, target);
455
- } else if (!BINARIES.some(r => r.test(file))) {
456
- const content = await _fsExtra.default.readFile(file, 'utf8');
457
- await _fsExtra.default.writeFile(target, _ejs.default.render(content, options));
458
- } else {
459
- await _fsExtra.default.copyFile(file, target);
460
- }
461
- }
462
- };
463
- await _fsExtra.default.mkdirp(folder);
464
- if (reactNativeVersion != null) {
465
- if (example === 'vanilla') {
466
- console.log(`${_kleur.default.blue('ℹ')} Using ${_kleur.default.cyan(`react-native@${reactNativeVersion}`)} for the example`);
467
- } else {
468
- console.warn(`${_kleur.default.yellow('⚠')} Ignoring --react-native-version for unsupported example type: ${_kleur.default.cyan(example)}`);
469
- }
470
- }
471
- const spinner = (0, _ora.default)().start();
472
- if (example !== 'none') {
473
- spinner.text = 'Generating example app';
474
- await (0, _generateExampleApp.default)({
475
- type: example,
476
- dest: folder,
477
- arch,
478
- project: options.project,
479
- bobVersion,
480
- reactNativeVersion
481
- });
482
- }
483
- spinner.text = 'Copying files';
484
- if (local) {
485
- await copyDir(COMMON_LOCAL_FILES, folder);
486
- } else {
487
- await copyDir(COMMON_FILES, folder);
488
- if (example !== 'none') {
489
- await copyDir(COMMON_EXAMPLE_FILES, folder);
490
- }
491
- }
492
- if (languages === 'js') {
493
- await copyDir(JS_FILES, folder);
494
- await copyDir(EXPO_FILES, folder);
495
- } else {
496
- await copyDir(NATIVE_COMMON_FILES, folder);
497
- if (example !== 'none') {
498
- await copyDir(NATIVE_COMMON_EXAMPLE_FILES, folder);
499
- }
500
- if (moduleType === 'module') {
501
- await copyDir(NATIVE_FILES[`${moduleType}_${arch}`], folder);
502
- } else {
503
- await copyDir(NATIVE_FILES[`${moduleType}_${arch}`], folder);
504
- }
505
- if (options.project.swift) {
506
- await copyDir(SWIFT_FILES[`${moduleType}_legacy`], folder);
507
- } else {
508
- if (moduleType === 'module') {
509
- await copyDir(OBJC_FILES[`${moduleType}_common`], folder);
510
- } else {
511
- await copyDir(OBJC_FILES[`view_${arch}`], folder);
512
- }
513
- }
514
- const templateType = `${moduleType}_${arch}`;
515
- await copyDir(KOTLIN_FILES[templateType], folder);
516
- if (options.project.cpp) {
517
- await copyDir(CPP_FILES, folder);
518
- await _fsExtra.default.remove(_path.default.join(folder, 'ios', `${options.project.name}.m`));
519
- }
520
- }
521
- const rootPackageJson = await _fsExtra.default.readJson(_path.default.join(folder, 'package.json'));
522
- if (example !== 'none') {
523
- // Set `react` and `react-native` versions of root `package.json` from example `package.json`
524
- const examplePackageJson = await _fsExtra.default.readJSON(_path.default.join(folder, 'example', 'package.json'));
525
- if (examplePackageJson.dependencies?.react && examplePackageJson.dependencies?.['react-native']) {
526
- rootPackageJson.devDependencies = rootPackageJson.devDependencies || {};
527
- rootPackageJson.devDependencies.react = examplePackageJson.dependencies.react;
528
- rootPackageJson.devDependencies['react-native'] = examplePackageJson.dependencies['react-native'];
529
- }
530
- if (example === 'vanilla') {
531
- // React Native doesn't provide the community CLI as a dependency.
532
- // We have to get read the version from the example app and put to the root package json
533
- const exampleCommunityCLIVersion = examplePackageJson.devDependencies['@react-native-community/cli'];
534
- (0, _nodeAssert.default)(exampleCommunityCLIVersion !== undefined, "The generated example app doesn't have community CLI installed");
535
- rootPackageJson.devDependencies = rootPackageJson.devDependencies || {};
536
- rootPackageJson.devDependencies['@react-native-community/cli'] = exampleCommunityCLIVersion;
537
- if (arch !== 'legacy') {
538
- (0, _addCodegenBuildScript.addCodegenBuildScript)(folder);
539
- }
540
- }
541
- }
542
-
543
- // Some of the passed args can already be derived from the generated package.json file.
544
- const ignoredAnswers = ['name', 'slug', 'description', 'authorName', 'authorEmail', 'authorUrl', 'repoUrl', 'example', 'reactNativeVersion', 'local'];
545
- const libraryMetadata = Object.fromEntries(Object.entries(answers).filter(([answer]) => !ignoredAnswers.includes(answer)));
546
- libraryMetadata.version = _package.version;
547
- rootPackageJson['create-react-native-library'] = libraryMetadata;
548
- await _fsExtra.default.writeJson(_path.default.join(folder, 'package.json'), rootPackageJson, {
549
- spaces: 2
550
- });
551
- if (!local) {
552
- let isInGitRepo = false;
553
- try {
554
- isInGitRepo = (await (0, _spawn.spawn)('git', ['rev-parse', '--is-inside-work-tree'])) === 'true';
555
- } catch (e) {
556
- // Ignore error
557
- }
558
- if (!isInGitRepo) {
559
- try {
560
- await (0, _spawn.spawn)('git', ['init'], {
561
- cwd: folder
562
- });
563
- await (0, _spawn.spawn)('git', ['branch', '-M', 'main'], {
564
- cwd: folder
565
- });
566
- await (0, _spawn.spawn)('git', ['add', '.'], {
567
- cwd: folder
568
- });
569
- await (0, _spawn.spawn)('git', ['commit', '-m', 'chore: initial commit'], {
570
- cwd: folder
571
- });
572
- } catch (e) {
573
- // Ignore error
574
- }
575
- }
576
- }
577
- spinner.succeed(`Project created successfully at ${_kleur.default.yellow(_path.default.relative(process.cwd(), folder))}!\n`);
578
- if (local) {
579
- let linked;
580
- const packageManager = (await _fsExtra.default.pathExists(_path.default.join(process.cwd(), 'yarn.lock'))) ? 'yarn' : 'npm';
581
- const packageJsonPath = _path.default.join(process.cwd(), 'package.json');
582
- if (await _fsExtra.default.pathExists(packageJsonPath)) {
583
- const packageJson = await _fsExtra.default.readJSON(packageJsonPath);
584
- const isReactNativeProject = Boolean(packageJson.dependencies?.['react-native']);
585
- if (isReactNativeProject) {
586
- packageJson.dependencies = packageJson.dependencies || {};
587
- packageJson.dependencies[slug] = packageManager === 'yarn' ? `link:./${_path.default.relative(process.cwd(), folder)}` : `file:./${_path.default.relative(process.cwd(), folder)}`;
588
- await _fsExtra.default.writeJSON(packageJsonPath, packageJson, {
589
- spaces: 2
590
- });
591
- linked = true;
592
- }
593
- }
594
- console.log((0, _dedent.default)(`
595
- ${_kleur.default.magenta(`${_kleur.default.bold('Get started')} with the project`)}${_kleur.default.gray(':')}
596
-
597
- ${(linked ? `- Run ${_kleur.default.blue(`${packageManager} install`)} to link the library\n` : `- Link the library at ${_kleur.default.blue(_path.default.relative(process.cwd(), folder))} based on your project setup'\n`) + `- Run ${_kleur.default.blue('pod install --project-directory=ios')} to install dependencies with CocoaPods\n` + `- Run ${_kleur.default.blue('npx react-native run-android')} or ${_kleur.default.blue('npx react-native run-ios')} to build and run the app\n` + `- Import from ${_kleur.default.blue(slug)} and use it in your app.`}
598
-
599
- ${_kleur.default.yellow(`Good luck!`)}
600
- `));
601
- } else {
602
- const platforms = {
603
- ios: {
604
- name: 'iOS',
605
- color: 'cyan'
606
- },
607
- android: {
608
- name: 'Android',
609
- color: 'green'
610
- },
611
- ...(example === 'expo' ? {
612
- web: {
613
- name: 'Web',
614
- color: 'blue'
615
- }
616
- } : null)
617
- };
618
- console.log((0, _dedent.default)(`
619
- ${_kleur.default.magenta(`${_kleur.default.bold('Get started')} with the project`)}${_kleur.default.gray(':')}
620
-
621
- ${_kleur.default.gray('$')} yarn
622
- ${Object.entries(platforms).map(([script, {
623
- name,
624
- color
625
- }]) => `
626
- ${_kleur.default[color](`Run the example app on ${_kleur.default.bold(name)}`)}${_kleur.default.gray(':')}
627
-
628
- ${_kleur.default.gray('$')} yarn example ${script}`).join('\n')}
629
-
630
- ${_kleur.default.yellow(`See ${_kleur.default.bold('CONTRIBUTING.md')} for more details. Good luck!`)}
631
- `));
632
- }
633
- }
634
- _yargs.default.command('$0 [name]', 'create a react native library', args, create).demandCommand().recommendCommands().fail((message, error) => {
635
- console.log('\n');
636
- if (error) {
637
- console.log(_kleur.default.red(error.message));
638
- throw error;
639
- }
640
- if (message) {
641
- console.log(_kleur.default.red(message));
642
- } else {
643
- console.log(_kleur.default.red(`An unknown error occurred. See '--help' for usage guide.`));
644
- }
645
- process.exit(1);
646
- }).parserConfiguration({
647
- // don't pass kebab-case args to handler.
648
- 'strip-dashed': true
649
- }).strict().argv;
650
-
651
- /**
652
- * Makes sure the answers are in expected form and ends the process with error if they are not
653
- */
654
- function assertOptions(questions, answers) {
655
- for (const [key, value] of Object.entries(answers)) {
656
- if (value == null) {
657
- continue;
658
- }
659
- const question = questions.find(q => q.name === key);
660
- if (question == null) {
661
- continue;
662
- }
663
- let valid = question.validate ? question.validate(String(value)) : true;
664
-
665
- // We also need to guard against invalid choices
666
- // If we don't already have a validation message to provide a better error
667
- if (typeof valid !== 'string' && 'choices' in question) {
668
- const choices = typeof question.choices === 'function' ? question.choices(undefined,
669
- // @ts-expect-error: it complains about optional values, but it should be fine
670
- answers, question) : question.choices;
671
- if (choices && !choices.some(choice => choice.value === value)) {
672
- valid = `Supported values are - ${choices.map(c => _kleur.default.green(c.value))}`;
673
- }
674
- }
675
- if (valid !== true) {
676
- let message = `Invalid value ${_kleur.default.red(String(value))} passed for ${_kleur.default.blue(key)}`;
677
- if (typeof valid === 'string') {
678
- message += `: ${valid}`;
679
- }
680
- console.log(message);
681
- process.exit(1);
682
- }
683
- }
150
+ return folder;
684
151
  }
685
152
  //# sourceMappingURL=index.js.map