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.
- package/lib/{utils → exampleApp}/addCodegenBuildScript.js +8 -2
- package/lib/exampleApp/addCodegenBuildScript.js.map +1 -0
- package/lib/exampleApp/dependencies.js +32 -0
- package/lib/exampleApp/dependencies.js.map +1 -0
- package/lib/{utils → exampleApp}/generateExampleApp.js +2 -2
- package/lib/exampleApp/generateExampleApp.js.map +1 -0
- package/lib/index.js +91 -624
- package/lib/index.js.map +1 -1
- package/lib/inform.js +91 -0
- package/lib/inform.js.map +1 -0
- package/lib/input.js +266 -0
- package/lib/input.js.map +1 -0
- package/lib/template.js +175 -0
- package/lib/template.js.map +1 -0
- package/lib/utils/assert.js +59 -0
- package/lib/utils/assert.js.map +1 -0
- package/lib/utils/initialCommit.js +34 -0
- package/lib/utils/initialCommit.js.map +1 -0
- package/lib/utils/promiseWithFallback.js +25 -0
- package/lib/utils/promiseWithFallback.js.map +1 -0
- package/package.json +2 -2
- package/templates/common/$package.json +2 -2
- package/templates/common/CONTRIBUTING.md +0 -25
- package/templates/common/babel.config.js +1 -3
- package/templates/common/tsconfig.json +1 -0
- package/templates/common-local/$package.json +1 -1
- package/templates/example-module-legacy/example/src/App.tsx +25 -0
- package/templates/example-module-new/example/src/App.tsx +20 -0
- package/templates/example-view/example/src/App.tsx +23 -0
- package/templates/js-library/src/index.tsx +2 -2
- package/templates/kotlin-library-new/android/src/main/java/com/{%- project.package_dir %}/{%- project.name %}Package.kt +2 -2
- package/templates/native-common/android/build.gradle +3 -19
- package/templates/objc-library/ios/{%- project.name %}.h +4 -4
- package/templates/objc-library/ios/{%- project.name %}.mm +1 -6
- package/templates/objc-view-new/ios/{%- project.name %}View.h +0 -3
- package/templates/objc-view-new/ios/{%- project.name %}View.mm +6 -6
- package/lib/utils/addCodegenBuildScript.js.map +0 -1
- package/lib/utils/generateExampleApp.js.map +0 -1
- package/templates/common-example/example/src/App.tsx +0 -57
- package/templates/kotlin-library-mixed/android/src/main/java/com/{%- project.package_dir %}/{%- project.name %}Module.kt +0 -24
- package/templates/kotlin-library-mixed/android/src/main/java/com/{%- project.package_dir %}/{%- project.name %}Package.kt +0 -35
- package/templates/kotlin-library-mixed/android/src/newarch/{%- project.name %}Spec.kt +0 -7
- package/templates/kotlin-library-mixed/android/src/oldarch/{%- project.name %}Spec.kt +0 -11
- package/templates/kotlin-view-mixed/android/src/main/java/com/{%- project.package_dir %}/{%- project.name %}Package.kt +0 -19
- package/templates/kotlin-view-mixed/android/src/main/java/com/{%- project.package_dir %}/{%- project.name %}View.kt +0 -15
- package/templates/kotlin-view-mixed/android/src/main/java/com/{%- project.package_dir %}/{%- project.name %}ViewManager.kt +0 -28
- package/templates/kotlin-view-mixed/android/src/newarch/{%- project.name %}ViewManagerSpec.kt +0 -21
- package/templates/kotlin-view-mixed/android/src/oldarch/{%- project.name %}ViewManagerSpec.kt +0 -9
- package/templates/native-library-mixed/react-native.config.js +0 -16
- package/templates/native-library-mixed/src/Native{%- project.name %}.ts +0 -8
- package/templates/native-library-mixed/src/index.tsx +0 -29
- package/templates/native-view-mixed/react-native.config.js +0 -16
- package/templates/native-view-mixed/src/index.tsx +0 -2
- package/templates/native-view-mixed/src/{%- project.name %}ViewNativeComponent.ts +0 -8
- package/templates/objc-view-mixed/ios/Utils.h +0 -8
- package/templates/objc-view-mixed/ios/Utils.m +0 -26
- package/templates/objc-view-mixed/ios/{%- project.name %}View.h +0 -17
- package/templates/objc-view-mixed/ios/{%- project.name %}View.mm +0 -60
- package/templates/objc-view-mixed/ios/{%- project.name %}ViewManager.mm +0 -23
- /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("./
|
|
19
|
-
var
|
|
20
|
-
var
|
|
21
|
-
var
|
|
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.
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
|
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
|
-
|
|
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
|