create-expo-module 0.2.0 → 0.4.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/build/create-expo-module.js +81 -51
- package/build/create-expo-module.js.map +1 -1
- package/build/createExampleApp.d.ts +6 -0
- package/build/createExampleApp.js +125 -0
- package/build/createExampleApp.js.map +1 -0
- package/build/packageManager.d.ts +2 -0
- package/build/packageManager.js +15 -0
- package/build/packageManager.js.map +1 -0
- package/build/resolvePackageManager.d.ts +3 -0
- package/build/resolvePackageManager.js +36 -0
- package/build/resolvePackageManager.js.map +1 -0
- package/build/types.d.ts +36 -0
- package/build/types.js +3 -0
- package/build/types.js.map +1 -0
- package/build/utils.d.ts +3 -0
- package/build/utils.js +28 -0
- package/build/utils.js.map +1 -0
- package/package.json +7 -4
- package/src/create-expo-module.ts +101 -95
- package/src/createExampleApp.ts +153 -0
- package/src/packageManager.ts +14 -0
- package/src/resolvePackageManager.ts +34 -0
- package/src/types.ts +39 -0
- package/src/utils.ts +28 -0
|
@@ -4,12 +4,18 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const spawn_async_1 = __importDefault(require("@expo/spawn-async"));
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
7
8
|
const commander_1 = require("commander");
|
|
8
9
|
const download_tarball_1 = __importDefault(require("download-tarball"));
|
|
9
10
|
const ejs_1 = __importDefault(require("ejs"));
|
|
10
11
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
11
12
|
const path_1 = __importDefault(require("path"));
|
|
12
13
|
const prompts_1 = __importDefault(require("prompts"));
|
|
14
|
+
const validate_npm_package_name_1 = __importDefault(require("validate-npm-package-name"));
|
|
15
|
+
const createExampleApp_1 = require("./createExampleApp");
|
|
16
|
+
const packageManager_1 = require("./packageManager");
|
|
17
|
+
const resolvePackageManager_1 = require("./resolvePackageManager");
|
|
18
|
+
const utils_1 = require("./utils");
|
|
13
19
|
const packageJson = require('../package.json');
|
|
14
20
|
// `yarn run` may change the current working dir, then we should use `INIT_CWD` env.
|
|
15
21
|
const CWD = process.env.INIT_CWD || process.cwd();
|
|
@@ -24,28 +30,29 @@ const IGNORES_PATHS = ['.DS_Store', 'build', 'node_modules', 'package.json'];
|
|
|
24
30
|
*/
|
|
25
31
|
async function main(target, options) {
|
|
26
32
|
const targetDir = target ? path_1.default.join(CWD, target) : CWD;
|
|
27
|
-
options.target = targetDir;
|
|
28
33
|
await fs_extra_1.default.ensureDir(targetDir);
|
|
34
|
+
await confirmTargetDirAsync(targetDir);
|
|
35
|
+
options.target = targetDir;
|
|
29
36
|
const data = await askForSubstitutionDataAsync(targetDir, options);
|
|
30
|
-
const packageManager = await
|
|
37
|
+
const packageManager = await (0, resolvePackageManager_1.resolvePackageManager)();
|
|
31
38
|
const packagePath = options.source
|
|
32
39
|
? path_1.default.join(CWD, options.source)
|
|
33
40
|
: await downloadPackageAsync(targetDir);
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
await (0, utils_1.newStep)('Creating the module from template files', async (step) => {
|
|
42
|
+
await createModuleFromTemplate(packagePath, targetDir, data);
|
|
43
|
+
step.succeed('Created the module from template files');
|
|
44
|
+
});
|
|
45
|
+
await (0, utils_1.newStep)('Installing module dependencies', async (step) => {
|
|
46
|
+
await (0, packageManager_1.installDependencies)(packageManager, targetDir);
|
|
47
|
+
step.succeed('Installed module dependencies');
|
|
48
|
+
});
|
|
49
|
+
await (0, utils_1.newStep)('Compiling TypeScript files', async (step) => {
|
|
50
|
+
await (0, spawn_async_1.default)(packageManager, ['run', 'build'], {
|
|
51
|
+
cwd: targetDir,
|
|
52
|
+
stdio: 'ignore',
|
|
42
53
|
});
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
const template = await fs_extra_1.default.readFile(fromPath, { encoding: 'utf8' });
|
|
46
|
-
const renderedContent = ejs_1.default.render(template, data);
|
|
47
|
-
await fs_extra_1.default.outputFile(toPath, renderedContent, { encoding: 'utf8' });
|
|
48
|
-
}
|
|
54
|
+
step.succeed('Compiled TypeScript files');
|
|
55
|
+
});
|
|
49
56
|
if (!options.source) {
|
|
50
57
|
// Files in the downloaded tarball are wrapped in `package` dir.
|
|
51
58
|
// We should remove it after all.
|
|
@@ -57,8 +64,11 @@ async function main(target, options) {
|
|
|
57
64
|
if (!options.withChangelog) {
|
|
58
65
|
await fs_extra_1.default.remove(path_1.default.join(targetDir, 'CHANGELOG.md'));
|
|
59
66
|
}
|
|
60
|
-
|
|
61
|
-
|
|
67
|
+
if (options.example) {
|
|
68
|
+
// Create "example" folder
|
|
69
|
+
await (0, createExampleApp_1.createExampleApp)(data, targetDir, packageManager);
|
|
70
|
+
}
|
|
71
|
+
console.log();
|
|
62
72
|
console.log('✅ Successfully created Expo module');
|
|
63
73
|
}
|
|
64
74
|
/**
|
|
@@ -106,43 +116,34 @@ async function npmWhoamiAsync(targetDir) {
|
|
|
106
116
|
* Downloads the template from NPM registry.
|
|
107
117
|
*/
|
|
108
118
|
async function downloadPackageAsync(targetDir) {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Asks whether to use Yarn or npm as a dependency package manager.
|
|
119
|
-
*/
|
|
120
|
-
async function selectPackageManagerAsync() {
|
|
121
|
-
const { packageManager } = await (0, prompts_1.default)({
|
|
122
|
-
type: 'select',
|
|
123
|
-
name: 'packageManager',
|
|
124
|
-
message: 'Which package manager do you want to use to install dependencies?',
|
|
125
|
-
choices: [
|
|
126
|
-
{ title: 'yarn', value: 'yarn' },
|
|
127
|
-
{ title: 'npm', value: 'npm' },
|
|
128
|
-
],
|
|
119
|
+
return await (0, utils_1.newStep)('Downloading module template from npm', async (step) => {
|
|
120
|
+
const tarballUrl = await getNpmTarballUrl('expo-module-template');
|
|
121
|
+
await (0, download_tarball_1.default)({
|
|
122
|
+
url: tarballUrl,
|
|
123
|
+
dir: targetDir,
|
|
124
|
+
});
|
|
125
|
+
step.succeed('Downloaded module template from npm');
|
|
126
|
+
return path_1.default.join(targetDir, 'package');
|
|
129
127
|
});
|
|
130
|
-
return packageManager;
|
|
131
128
|
}
|
|
132
129
|
/**
|
|
133
|
-
*
|
|
130
|
+
* Creates the module based on the `ejs` template (e.g. `expo-module-template` package).
|
|
134
131
|
*/
|
|
135
|
-
async function
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
132
|
+
async function createModuleFromTemplate(templatePath, targetPath, data) {
|
|
133
|
+
const files = await getFilesAsync(templatePath);
|
|
134
|
+
// Iterate through all template files.
|
|
135
|
+
for (const file of files) {
|
|
136
|
+
const renderedRelativePath = ejs_1.default.render(file.replace(/^\$/, ''), data, {
|
|
137
|
+
openDelimiter: '{',
|
|
138
|
+
closeDelimiter: '}',
|
|
139
|
+
escape: (value) => value.replace('.', path_1.default.sep),
|
|
140
140
|
});
|
|
141
|
+
const fromPath = path_1.default.join(templatePath, file);
|
|
142
|
+
const toPath = path_1.default.join(targetPath, renderedRelativePath);
|
|
143
|
+
const template = await fs_extra_1.default.readFile(fromPath, { encoding: 'utf8' });
|
|
144
|
+
const renderedContent = ejs_1.default.render(template, data);
|
|
145
|
+
await fs_extra_1.default.outputFile(toPath, renderedContent, { encoding: 'utf8' });
|
|
141
146
|
}
|
|
142
|
-
console.log('📦 Installing dependencies...');
|
|
143
|
-
await run('install');
|
|
144
|
-
console.log('🛠 Compiling TypeScript files...');
|
|
145
|
-
await run('run', 'build');
|
|
146
147
|
}
|
|
147
148
|
/**
|
|
148
149
|
* Asks the user for some data necessary to render the template.
|
|
@@ -151,6 +152,7 @@ async function postActionsAsync(packageManager, targetDir) {
|
|
|
151
152
|
async function askForSubstitutionDataAsync(targetDir, options) {
|
|
152
153
|
var _a, _b;
|
|
153
154
|
const defaultPackageSlug = path_1.default.basename(targetDir);
|
|
155
|
+
const useDefaultSlug = options.target && (0, validate_npm_package_name_1.default)(defaultPackageSlug);
|
|
154
156
|
const defaultProjectName = defaultPackageSlug
|
|
155
157
|
.replace(/^./, (match) => match.toUpperCase())
|
|
156
158
|
.replace(/\W+(\w)/g, (_, p1) => p1.toUpperCase());
|
|
@@ -160,7 +162,8 @@ async function askForSubstitutionDataAsync(targetDir, options) {
|
|
|
160
162
|
name: 'slug',
|
|
161
163
|
message: 'What is the package slug?',
|
|
162
164
|
initial: defaultPackageSlug,
|
|
163
|
-
resolvedValue:
|
|
165
|
+
resolvedValue: useDefaultSlug ? defaultPackageSlug : null,
|
|
166
|
+
validate: (input) => (0, validate_npm_package_name_1.default)(input).validForNewPackages || 'Must be a valid npm package name',
|
|
164
167
|
},
|
|
165
168
|
{
|
|
166
169
|
type: 'text',
|
|
@@ -172,6 +175,7 @@ async function askForSubstitutionDataAsync(targetDir, options) {
|
|
|
172
175
|
type: 'text',
|
|
173
176
|
name: 'description',
|
|
174
177
|
message: 'How would you describe the module?',
|
|
178
|
+
validate: (input) => !!input || 'Cannot be empty',
|
|
175
179
|
},
|
|
176
180
|
{
|
|
177
181
|
type: 'text',
|
|
@@ -195,12 +199,17 @@ async function askForSubstitutionDataAsync(targetDir, options) {
|
|
|
195
199
|
type: 'text',
|
|
196
200
|
name: 'repo',
|
|
197
201
|
message: 'What is the repository URL?',
|
|
202
|
+
validate: (input) => /^https?:\/\//.test(input) || 'Must be a valid URL',
|
|
198
203
|
},
|
|
199
204
|
];
|
|
205
|
+
// Stop the process when the user cancels/exits the prompt.
|
|
206
|
+
const onCancel = () => {
|
|
207
|
+
process.exit(0);
|
|
208
|
+
};
|
|
200
209
|
const answers = {};
|
|
201
210
|
for (const query of promptQueries) {
|
|
202
211
|
const { name, resolvedValue } = query;
|
|
203
|
-
answers[name] = (_b = resolvedValue !== null && resolvedValue !== void 0 ? resolvedValue : options[name]) !== null && _b !== void 0 ? _b : (await (0, prompts_1.default)(query))[name];
|
|
212
|
+
answers[name] = (_b = resolvedValue !== null && resolvedValue !== void 0 ? resolvedValue : options[name]) !== null && _b !== void 0 ? _b : (await (0, prompts_1.default)(query, { onCancel }))[name];
|
|
204
213
|
}
|
|
205
214
|
const { slug, name, description, package: projectPackage, author, license, repo } = answers;
|
|
206
215
|
return {
|
|
@@ -216,6 +225,26 @@ async function askForSubstitutionDataAsync(targetDir, options) {
|
|
|
216
225
|
repo,
|
|
217
226
|
};
|
|
218
227
|
}
|
|
228
|
+
/**
|
|
229
|
+
* Checks whether the target directory is empty and if not, asks the user to confirm if he wants to continue.
|
|
230
|
+
*/
|
|
231
|
+
async function confirmTargetDirAsync(targetDir) {
|
|
232
|
+
const files = await fs_extra_1.default.readdir(targetDir);
|
|
233
|
+
if (files.length === 0) {
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
const { shouldContinue } = await (0, prompts_1.default)({
|
|
237
|
+
type: 'confirm',
|
|
238
|
+
name: 'shouldContinue',
|
|
239
|
+
message: `The target directory ${chalk_1.default.magenta(targetDir)} is not empty.\nDo you want to continue anyway?`,
|
|
240
|
+
initial: true,
|
|
241
|
+
}, {
|
|
242
|
+
onCancel: () => false,
|
|
243
|
+
});
|
|
244
|
+
if (!shouldContinue) {
|
|
245
|
+
process.exit(0);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
219
248
|
const program = new commander_1.Command();
|
|
220
249
|
program
|
|
221
250
|
.name(packageJson.name)
|
|
@@ -231,6 +260,7 @@ program
|
|
|
231
260
|
.option('-r, --repo <repo_url>', 'The URL to the repository.')
|
|
232
261
|
.option('--with-readme', 'Whether to include README.md file.', false)
|
|
233
262
|
.option('--with-changelog', 'Whether to include CHANGELOG.md file.', false)
|
|
263
|
+
.option('--no-example', 'Whether to skip creating the example app.', false)
|
|
234
264
|
.action(main);
|
|
235
265
|
program.parse(process.argv);
|
|
236
266
|
//# sourceMappingURL=create-expo-module.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-expo-module.js","sourceRoot":"","sources":["../src/create-expo-module.ts"],"names":[],"mappings":"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","sourcesContent":["import spawnAsync from '@expo/spawn-async';\nimport { Command } from 'commander';\nimport downloadTarball from 'download-tarball';\nimport ejs from 'ejs';\nimport fs from 'fs-extra';\nimport path from 'path';\nimport prompts, { PromptObject } from 'prompts';\n\nconst packageJson = require('../package.json');\n\n// `yarn run` may change the current working dir, then we should use `INIT_CWD` env.\nconst CWD = process.env.INIT_CWD || process.cwd();\n\n// Ignore some paths. Especially `package.json` as it is rendered\n// from `$package.json` file instead of the original one.\nconst IGNORES_PATHS = ['.DS_Store', 'build', 'node_modules', 'package.json'];\n\n/**\n * Possible command options.\n */\ntype CommandOptions = {\n target: string;\n source?: string;\n name?: string;\n description?: string;\n package?: string;\n author?: string;\n license?: string;\n repo?: string;\n withReadme: boolean;\n withChangelog: boolean;\n};\n\n/**\n * Represents an object that is passed to `ejs` when rendering the template.\n */\ntype SubstitutionData = {\n project: {\n slug: string;\n name: string;\n version: string;\n description: string;\n package: string;\n };\n author: string;\n license: string;\n repo: string;\n};\n\ntype CustomPromptObject = PromptObject & {\n name: string;\n resolvedValue?: string | null;\n};\n\ntype PackageManager = 'npm' | 'yarn';\n\n/**\n * The main function of the command.\n *\n * @param target Path to the directory where to create the module. Defaults to current working dir.\n * @param command An object from `commander`.\n */\nasync function main(target: string | undefined, options: CommandOptions) {\n const targetDir = target ? path.join(CWD, target) : CWD;\n\n options.target = targetDir;\n await fs.ensureDir(targetDir);\n\n const data = await askForSubstitutionDataAsync(targetDir, options);\n const packageManager = await selectPackageManagerAsync();\n const packagePath = options.source\n ? path.join(CWD, options.source)\n : await downloadPackageAsync(targetDir);\n const files = await getFilesAsync(packagePath);\n\n console.log('🎨 Creating Expo module from the template files...');\n\n // Iterate through all template files.\n for (const file of files) {\n const renderedRelativePath = ejs.render(file.replace(/^\\$/, ''), data, {\n openDelimiter: '{',\n closeDelimiter: '}',\n escape: (value: string) => value.replace('.', path.sep),\n });\n const fromPath = path.join(packagePath, file);\n const toPath = path.join(targetDir, renderedRelativePath);\n const template = await fs.readFile(fromPath, { encoding: 'utf8' });\n const renderedContent = ejs.render(template, data);\n\n await fs.outputFile(toPath, renderedContent, { encoding: 'utf8' });\n }\n\n if (!options.source) {\n // Files in the downloaded tarball are wrapped in `package` dir.\n // We should remove it after all.\n await fs.remove(packagePath);\n }\n if (!options.withReadme) {\n await fs.remove(path.join(targetDir, 'README.md'));\n }\n if (!options.withChangelog) {\n await fs.remove(path.join(targetDir, 'CHANGELOG.md'));\n }\n\n // Install dependencies and build\n await postActionsAsync(packageManager, targetDir);\n\n console.log('✅ Successfully created Expo module');\n}\n\n/**\n * Recursively scans for the files within the directory. Returned paths are relative to the `root` path.\n */\nasync function getFilesAsync(root: string, dir: string | null = null): Promise<string[]> {\n const files: string[] = [];\n const baseDir = dir ? path.join(root, dir) : root;\n\n for (const file of await fs.readdir(baseDir)) {\n const relativePath = dir ? path.join(dir, file) : file;\n\n if (IGNORES_PATHS.includes(relativePath) || IGNORES_PATHS.includes(file)) {\n continue;\n }\n\n const fullPath = path.join(baseDir, file);\n const stat = await fs.lstat(fullPath);\n\n if (stat.isDirectory()) {\n files.push(...(await getFilesAsync(root, relativePath)));\n } else {\n files.push(relativePath);\n }\n }\n return files;\n}\n\n/**\n * Asks NPM registry for the url to the tarball.\n */\nasync function getNpmTarballUrl(packageName: string, version: string = 'latest'): Promise<string> {\n const { stdout } = await spawnAsync('npm', ['view', `${packageName}@${version}`, 'dist.tarball']);\n return stdout.trim();\n}\n\n/**\n * Gets the username of currently logged in user. Used as a default in the prompt asking for the module author.\n */\nasync function npmWhoamiAsync(targetDir: string): Promise<string | null> {\n try {\n const { stdout } = await spawnAsync('npm', ['whoami'], { cwd: targetDir });\n return stdout.trim();\n } catch {\n return null;\n }\n}\n\n/**\n * Downloads the template from NPM registry.\n */\nasync function downloadPackageAsync(targetDir: string): Promise<string> {\n const tarballUrl = await getNpmTarballUrl('expo-module-template');\n\n console.log('⬇️ Downloading module template from npm...');\n\n await downloadTarball({\n url: tarballUrl,\n dir: targetDir,\n });\n return path.join(targetDir, 'package');\n}\n\n/**\n * Asks whether to use Yarn or npm as a dependency package manager.\n */\nasync function selectPackageManagerAsync(): Promise<PackageManager> {\n const { packageManager } = await prompts({\n type: 'select',\n name: 'packageManager',\n message: 'Which package manager do you want to use to install dependencies?',\n choices: [\n { title: 'yarn', value: 'yarn' },\n { title: 'npm', value: 'npm' },\n ],\n });\n return packageManager;\n}\n\n/**\n * Installs dependencies and builds TypeScript files.\n */\nasync function postActionsAsync(packageManager: PackageManager, targetDir: string) {\n async function run(...args: string[]) {\n await spawnAsync(packageManager, args, {\n cwd: targetDir,\n stdio: 'ignore',\n });\n }\n\n console.log('📦 Installing dependencies...');\n await run('install');\n\n console.log('🛠 Compiling TypeScript files...');\n await run('run', 'build');\n}\n\n/**\n * Asks the user for some data necessary to render the template.\n * Some values may already be provided by command options, the prompt is skipped in that case.\n */\nasync function askForSubstitutionDataAsync(\n targetDir: string,\n options: CommandOptions\n): Promise<SubstitutionData> {\n const defaultPackageSlug = path.basename(targetDir);\n const defaultProjectName = defaultPackageSlug\n .replace(/^./, (match) => match.toUpperCase())\n .replace(/\\W+(\\w)/g, (_, p1) => p1.toUpperCase());\n\n const promptQueries: CustomPromptObject[] = [\n {\n type: 'text',\n name: 'slug',\n message: 'What is the package slug?',\n initial: defaultPackageSlug,\n resolvedValue: options.target ? defaultPackageSlug : null,\n },\n {\n type: 'text',\n name: 'name',\n message: 'What is the project name?',\n initial: defaultProjectName,\n },\n {\n type: 'text',\n name: 'description',\n message: 'How would you describe the module?',\n },\n {\n type: 'text',\n name: 'package',\n message: 'What is the Android package name?',\n initial: `expo.modules.${defaultPackageSlug.replace(/\\W/g, '').toLowerCase()}`,\n },\n {\n type: 'text',\n name: 'author',\n message: 'Who is the author?',\n initial: (await npmWhoamiAsync(targetDir)) ?? '',\n },\n {\n type: 'text',\n name: 'license',\n message: 'What is the license?',\n initial: 'MIT',\n },\n {\n type: 'text',\n name: 'repo',\n message: 'What is the repository URL?',\n },\n ];\n\n const answers: Record<string, string> = {};\n for (const query of promptQueries) {\n const { name, resolvedValue } = query;\n answers[name] = resolvedValue ?? options[name] ?? (await prompts(query))[name];\n }\n\n const { slug, name, description, package: projectPackage, author, license, repo } = answers;\n\n return {\n project: {\n slug,\n name,\n version: '0.1.0',\n description,\n package: projectPackage,\n },\n author,\n license,\n repo,\n };\n}\n\nconst program = new Command();\n\nprogram\n .name(packageJson.name)\n .version(packageJson.version)\n .description(packageJson.description)\n .arguments('[target_dir]')\n .option(\n '-s, --source <source_dir>',\n 'Local path to the template. By default it downloads `expo-module-template` from NPM.'\n )\n .option('-n, --name <module_name>', 'Name of the native module.')\n .option('-d, --description <description>', 'Description of the module.')\n .option('-p, --package <package>', 'The Android package name.')\n .option('-a, --author <author>', 'The author name.')\n .option('-l, --license <license>', 'The license that the module is distributed with.')\n .option('-r, --repo <repo_url>', 'The URL to the repository.')\n .option('--with-readme', 'Whether to include README.md file.', false)\n .option('--with-changelog', 'Whether to include CHANGELOG.md file.', false)\n .action(main);\n\nprogram.parse(process.argv);\n"]}
|
|
1
|
+
{"version":3,"file":"create-expo-module.js","sourceRoot":"","sources":["../src/create-expo-module.ts"],"names":[],"mappings":"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","sourcesContent":["import spawnAsync from '@expo/spawn-async';\nimport chalk from 'chalk';\nimport { Command } from 'commander';\nimport downloadTarball from 'download-tarball';\nimport ejs from 'ejs';\nimport fs from 'fs-extra';\nimport path from 'path';\nimport prompts from 'prompts';\nimport validateNpmPackage from 'validate-npm-package-name';\n\nimport { createExampleApp } from './createExampleApp';\nimport { installDependencies } from './packageManager';\nimport { resolvePackageManager } from './resolvePackageManager';\nimport { CommandOptions, CustomPromptObject, SubstitutionData } from './types';\nimport { newStep } from './utils';\n\nconst packageJson = require('../package.json');\n\n// `yarn run` may change the current working dir, then we should use `INIT_CWD` env.\nconst CWD = process.env.INIT_CWD || process.cwd();\n\n// Ignore some paths. Especially `package.json` as it is rendered\n// from `$package.json` file instead of the original one.\nconst IGNORES_PATHS = ['.DS_Store', 'build', 'node_modules', 'package.json'];\n\n/**\n * The main function of the command.\n *\n * @param target Path to the directory where to create the module. Defaults to current working dir.\n * @param command An object from `commander`.\n */\nasync function main(target: string | undefined, options: CommandOptions) {\n const targetDir = target ? path.join(CWD, target) : CWD;\n\n await fs.ensureDir(targetDir);\n await confirmTargetDirAsync(targetDir);\n\n options.target = targetDir;\n\n const data = await askForSubstitutionDataAsync(targetDir, options);\n const packageManager = await resolvePackageManager();\n const packagePath = options.source\n ? path.join(CWD, options.source)\n : await downloadPackageAsync(targetDir);\n\n await newStep('Creating the module from template files', async (step) => {\n await createModuleFromTemplate(packagePath, targetDir, data);\n step.succeed('Created the module from template files');\n });\n\n await newStep('Installing module dependencies', async (step) => {\n await installDependencies(packageManager, targetDir);\n step.succeed('Installed module dependencies');\n });\n\n await newStep('Compiling TypeScript files', async (step) => {\n await spawnAsync(packageManager, ['run', 'build'], {\n cwd: targetDir,\n stdio: 'ignore',\n });\n step.succeed('Compiled TypeScript files');\n });\n\n if (!options.source) {\n // Files in the downloaded tarball are wrapped in `package` dir.\n // We should remove it after all.\n await fs.remove(packagePath);\n }\n if (!options.withReadme) {\n await fs.remove(path.join(targetDir, 'README.md'));\n }\n if (!options.withChangelog) {\n await fs.remove(path.join(targetDir, 'CHANGELOG.md'));\n }\n if (options.example) {\n // Create \"example\" folder\n await createExampleApp(data, targetDir, packageManager);\n }\n\n console.log();\n console.log('✅ Successfully created Expo module');\n}\n\n/**\n * Recursively scans for the files within the directory. Returned paths are relative to the `root` path.\n */\nasync function getFilesAsync(root: string, dir: string | null = null): Promise<string[]> {\n const files: string[] = [];\n const baseDir = dir ? path.join(root, dir) : root;\n\n for (const file of await fs.readdir(baseDir)) {\n const relativePath = dir ? path.join(dir, file) : file;\n\n if (IGNORES_PATHS.includes(relativePath) || IGNORES_PATHS.includes(file)) {\n continue;\n }\n\n const fullPath = path.join(baseDir, file);\n const stat = await fs.lstat(fullPath);\n\n if (stat.isDirectory()) {\n files.push(...(await getFilesAsync(root, relativePath)));\n } else {\n files.push(relativePath);\n }\n }\n return files;\n}\n\n/**\n * Asks NPM registry for the url to the tarball.\n */\nasync function getNpmTarballUrl(packageName: string, version: string = 'latest'): Promise<string> {\n const { stdout } = await spawnAsync('npm', ['view', `${packageName}@${version}`, 'dist.tarball']);\n return stdout.trim();\n}\n\n/**\n * Gets the username of currently logged in user. Used as a default in the prompt asking for the module author.\n */\nasync function npmWhoamiAsync(targetDir: string): Promise<string | null> {\n try {\n const { stdout } = await spawnAsync('npm', ['whoami'], { cwd: targetDir });\n return stdout.trim();\n } catch {\n return null;\n }\n}\n\n/**\n * Downloads the template from NPM registry.\n */\nasync function downloadPackageAsync(targetDir: string): Promise<string> {\n return await newStep('Downloading module template from npm', async (step) => {\n const tarballUrl = await getNpmTarballUrl('expo-module-template');\n\n await downloadTarball({\n url: tarballUrl,\n dir: targetDir,\n });\n\n step.succeed('Downloaded module template from npm');\n\n return path.join(targetDir, 'package');\n });\n}\n\n/**\n * Creates the module based on the `ejs` template (e.g. `expo-module-template` package).\n */\nasync function createModuleFromTemplate(\n templatePath: string,\n targetPath: string,\n data: SubstitutionData\n) {\n const files = await getFilesAsync(templatePath);\n\n // Iterate through all template files.\n for (const file of files) {\n const renderedRelativePath = ejs.render(file.replace(/^\\$/, ''), data, {\n openDelimiter: '{',\n closeDelimiter: '}',\n escape: (value: string) => value.replace('.', path.sep),\n });\n const fromPath = path.join(templatePath, file);\n const toPath = path.join(targetPath, renderedRelativePath);\n const template = await fs.readFile(fromPath, { encoding: 'utf8' });\n const renderedContent = ejs.render(template, data);\n\n await fs.outputFile(toPath, renderedContent, { encoding: 'utf8' });\n }\n}\n\n/**\n * Asks the user for some data necessary to render the template.\n * Some values may already be provided by command options, the prompt is skipped in that case.\n */\nasync function askForSubstitutionDataAsync(\n targetDir: string,\n options: CommandOptions\n): Promise<SubstitutionData> {\n const defaultPackageSlug = path.basename(targetDir);\n const useDefaultSlug = options.target && validateNpmPackage(defaultPackageSlug);\n const defaultProjectName = defaultPackageSlug\n .replace(/^./, (match) => match.toUpperCase())\n .replace(/\\W+(\\w)/g, (_, p1) => p1.toUpperCase());\n\n const promptQueries: CustomPromptObject[] = [\n {\n type: 'text',\n name: 'slug',\n message: 'What is the package slug?',\n initial: defaultPackageSlug,\n resolvedValue: useDefaultSlug ? defaultPackageSlug : null,\n validate: (input) =>\n validateNpmPackage(input).validForNewPackages || 'Must be a valid npm package name',\n },\n {\n type: 'text',\n name: 'name',\n message: 'What is the project name?',\n initial: defaultProjectName,\n },\n {\n type: 'text',\n name: 'description',\n message: 'How would you describe the module?',\n validate: (input) => !!input || 'Cannot be empty',\n },\n {\n type: 'text',\n name: 'package',\n message: 'What is the Android package name?',\n initial: `expo.modules.${defaultPackageSlug.replace(/\\W/g, '').toLowerCase()}`,\n },\n {\n type: 'text',\n name: 'author',\n message: 'Who is the author?',\n initial: (await npmWhoamiAsync(targetDir)) ?? '',\n },\n {\n type: 'text',\n name: 'license',\n message: 'What is the license?',\n initial: 'MIT',\n },\n {\n type: 'text',\n name: 'repo',\n message: 'What is the repository URL?',\n validate: (input) => /^https?:\\/\\//.test(input) || 'Must be a valid URL',\n },\n ];\n\n // Stop the process when the user cancels/exits the prompt.\n const onCancel = () => {\n process.exit(0);\n };\n\n const answers: Record<string, string> = {};\n for (const query of promptQueries) {\n const { name, resolvedValue } = query;\n answers[name] = resolvedValue ?? options[name] ?? (await prompts(query, { onCancel }))[name];\n }\n\n const { slug, name, description, package: projectPackage, author, license, repo } = answers;\n\n return {\n project: {\n slug,\n name,\n version: '0.1.0',\n description,\n package: projectPackage,\n },\n author,\n license,\n repo,\n };\n}\n\n/**\n * Checks whether the target directory is empty and if not, asks the user to confirm if he wants to continue.\n */\nasync function confirmTargetDirAsync(targetDir: string): Promise<void> {\n const files = await fs.readdir(targetDir);\n\n if (files.length === 0) {\n return;\n }\n const { shouldContinue } = await prompts(\n {\n type: 'confirm',\n name: 'shouldContinue',\n message: `The target directory ${chalk.magenta(\n targetDir\n )} is not empty.\\nDo you want to continue anyway?`,\n initial: true,\n },\n {\n onCancel: () => false,\n }\n );\n if (!shouldContinue) {\n process.exit(0);\n }\n}\n\nconst program = new Command();\n\nprogram\n .name(packageJson.name)\n .version(packageJson.version)\n .description(packageJson.description)\n .arguments('[target_dir]')\n .option(\n '-s, --source <source_dir>',\n 'Local path to the template. By default it downloads `expo-module-template` from NPM.'\n )\n .option('-n, --name <module_name>', 'Name of the native module.')\n .option('-d, --description <description>', 'Description of the module.')\n .option('-p, --package <package>', 'The Android package name.')\n .option('-a, --author <author>', 'The author name.')\n .option('-l, --license <license>', 'The license that the module is distributed with.')\n .option('-r, --repo <repo_url>', 'The URL to the repository.')\n .option('--with-readme', 'Whether to include README.md file.', false)\n .option('--with-changelog', 'Whether to include CHANGELOG.md file.', false)\n .option('--no-example', 'Whether to skip creating the example app.', false)\n .action(main);\n\nprogram.parse(process.argv);\n"]}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { PackageManagerName } from './resolvePackageManager';
|
|
2
|
+
import { SubstitutionData } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Initializes a new Expo project as an example app.
|
|
5
|
+
*/
|
|
6
|
+
export declare function createExampleApp(data: SubstitutionData, targetDir: string, packageManager: PackageManagerName): Promise<void>;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createExampleApp = void 0;
|
|
7
|
+
const spawn_async_1 = __importDefault(require("@expo/spawn-async"));
|
|
8
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const packageManager_1 = require("./packageManager");
|
|
11
|
+
const utils_1 = require("./utils");
|
|
12
|
+
// These dependencies will be removed from the example app (`expo init` adds them)
|
|
13
|
+
const DEPENDENCIES_TO_REMOVE = ['expo-status-bar', 'expo-splash-screen'];
|
|
14
|
+
/**
|
|
15
|
+
* Initializes a new Expo project as an example app.
|
|
16
|
+
*/
|
|
17
|
+
async function createExampleApp(data, targetDir, packageManager) {
|
|
18
|
+
const exampleProjectSlug = `${data.project.slug}-example`;
|
|
19
|
+
await (0, utils_1.newStep)('Initializing the example app', async (step) => {
|
|
20
|
+
await (0, spawn_async_1.default)('expo', ['init', exampleProjectSlug, '--template', 'expo-template-blank-typescript'], {
|
|
21
|
+
cwd: targetDir,
|
|
22
|
+
stdio: ['ignore', 'ignore', 'inherit'],
|
|
23
|
+
});
|
|
24
|
+
step.succeed('Initialized the example app');
|
|
25
|
+
});
|
|
26
|
+
// `expo init` creates a new folder with the same name as the project slug
|
|
27
|
+
const appTmpPath = path_1.default.join(targetDir, exampleProjectSlug);
|
|
28
|
+
// Path to the target example dir
|
|
29
|
+
const appTargetPath = path_1.default.join(targetDir, 'example');
|
|
30
|
+
await (0, utils_1.newStep)('Configuring the example app', async (step) => {
|
|
31
|
+
// "example" folder already exists and contains template files,
|
|
32
|
+
// that should replace these created by `expo init`.
|
|
33
|
+
await moveFiles(appTargetPath, appTmpPath);
|
|
34
|
+
// Cleanup the "example" dir
|
|
35
|
+
await fs_extra_1.default.rmdir(appTargetPath);
|
|
36
|
+
// Move the temporary example app to "example" dir
|
|
37
|
+
await fs_extra_1.default.rename(appTmpPath, appTargetPath);
|
|
38
|
+
await addMissingAppConfigFields(appTargetPath, data);
|
|
39
|
+
step.succeed('Configured the example app');
|
|
40
|
+
});
|
|
41
|
+
await prebuildExampleApp(appTargetPath);
|
|
42
|
+
await modifyPackageJson(appTargetPath);
|
|
43
|
+
await (0, utils_1.newStep)('Installing dependencies in the example app', async (step) => {
|
|
44
|
+
await (0, packageManager_1.installDependencies)(packageManager, appTargetPath);
|
|
45
|
+
await podInstall(appTargetPath);
|
|
46
|
+
step.succeed('Installed dependencies in the example app');
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
exports.createExampleApp = createExampleApp;
|
|
50
|
+
/**
|
|
51
|
+
* Copies files from one directory to another.
|
|
52
|
+
*/
|
|
53
|
+
async function moveFiles(fromPath, toPath) {
|
|
54
|
+
for (const file of await fs_extra_1.default.readdir(fromPath)) {
|
|
55
|
+
await fs_extra_1.default.move(path_1.default.join(fromPath, file), path_1.default.join(toPath, file), {
|
|
56
|
+
overwrite: true,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Adds missing configuration that are required to run `expo prebuild`.
|
|
62
|
+
*/
|
|
63
|
+
async function addMissingAppConfigFields(appPath, data) {
|
|
64
|
+
const appConfigPath = path_1.default.join(appPath, 'app.json');
|
|
65
|
+
const appConfig = await fs_extra_1.default.readJson(appConfigPath);
|
|
66
|
+
const appId = `${data.project.package}.example`;
|
|
67
|
+
// Android package name needs to be added to app.json
|
|
68
|
+
if (!appConfig.expo.android) {
|
|
69
|
+
appConfig.expo.android = {};
|
|
70
|
+
}
|
|
71
|
+
appConfig.expo.android.package = appId;
|
|
72
|
+
// Specify iOS bundle identifier
|
|
73
|
+
if (!appConfig.expo.ios) {
|
|
74
|
+
appConfig.expo.ios = {};
|
|
75
|
+
}
|
|
76
|
+
appConfig.expo.ios.bundleIdentifier = appId;
|
|
77
|
+
await fs_extra_1.default.writeJson(appConfigPath, appConfig, {
|
|
78
|
+
spaces: 2,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Applies necessary changes to **package.json** of the example app.
|
|
83
|
+
* It means setting the autolinking config and removing unnecessary dependencies.
|
|
84
|
+
*/
|
|
85
|
+
async function modifyPackageJson(appPath) {
|
|
86
|
+
const packageJsonPath = path_1.default.join(appPath, 'package.json');
|
|
87
|
+
const packageJson = await fs_extra_1.default.readJson(packageJsonPath);
|
|
88
|
+
if (!packageJson.expo) {
|
|
89
|
+
packageJson.expo = {};
|
|
90
|
+
}
|
|
91
|
+
// Set the native modules dir to the root folder,
|
|
92
|
+
// so that the autolinking can detect and link the module.
|
|
93
|
+
packageJson.expo.autolinking = {
|
|
94
|
+
nativeModulesDir: '..',
|
|
95
|
+
};
|
|
96
|
+
// Remove unnecessary dependencies
|
|
97
|
+
for (const dependencyToRemove of DEPENDENCIES_TO_REMOVE) {
|
|
98
|
+
delete packageJson.dependencies[dependencyToRemove];
|
|
99
|
+
}
|
|
100
|
+
await fs_extra_1.default.writeJson(packageJsonPath, packageJson, {
|
|
101
|
+
spaces: 2,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Runs `expo prebuild` in the example app.
|
|
106
|
+
*/
|
|
107
|
+
async function prebuildExampleApp(exampleAppPath) {
|
|
108
|
+
await (0, utils_1.newStep)('Prebuilding the example app', async (step) => {
|
|
109
|
+
await (0, spawn_async_1.default)('expo', ['prebuild', '--no-install'], {
|
|
110
|
+
cwd: exampleAppPath,
|
|
111
|
+
stdio: ['ignore', 'ignore', 'pipe'],
|
|
112
|
+
});
|
|
113
|
+
step.succeed('Prebuilt the example app');
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Runs `pod install` in the iOS project at the given path.
|
|
118
|
+
*/
|
|
119
|
+
async function podInstall(appPath) {
|
|
120
|
+
await (0, spawn_async_1.default)('pod', ['install'], {
|
|
121
|
+
cwd: path_1.default.join(appPath, 'ios'),
|
|
122
|
+
stdio: ['ignore', 'ignore', 'pipe'],
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=createExampleApp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createExampleApp.js","sourceRoot":"","sources":["../src/createExampleApp.ts"],"names":[],"mappings":"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","sourcesContent":["import spawnAsync from '@expo/spawn-async';\nimport fs from 'fs-extra';\nimport path from 'path';\n\nimport { installDependencies } from './packageManager';\nimport { PackageManagerName } from './resolvePackageManager';\nimport { SubstitutionData } from './types';\nimport { newStep } from './utils';\n\n// These dependencies will be removed from the example app (`expo init` adds them)\nconst DEPENDENCIES_TO_REMOVE = ['expo-status-bar', 'expo-splash-screen'];\n\n/**\n * Initializes a new Expo project as an example app.\n */\nexport async function createExampleApp(\n data: SubstitutionData,\n targetDir: string,\n packageManager: PackageManagerName\n): Promise<void> {\n const exampleProjectSlug = `${data.project.slug}-example`;\n\n await newStep('Initializing the example app', async (step) => {\n await spawnAsync(\n 'expo',\n ['init', exampleProjectSlug, '--template', 'expo-template-blank-typescript'],\n {\n cwd: targetDir,\n stdio: ['ignore', 'ignore', 'inherit'],\n }\n );\n step.succeed('Initialized the example app');\n });\n\n // `expo init` creates a new folder with the same name as the project slug\n const appTmpPath = path.join(targetDir, exampleProjectSlug);\n\n // Path to the target example dir\n const appTargetPath = path.join(targetDir, 'example');\n\n await newStep('Configuring the example app', async (step) => {\n // \"example\" folder already exists and contains template files,\n // that should replace these created by `expo init`.\n await moveFiles(appTargetPath, appTmpPath);\n\n // Cleanup the \"example\" dir\n await fs.rmdir(appTargetPath);\n\n // Move the temporary example app to \"example\" dir\n await fs.rename(appTmpPath, appTargetPath);\n\n await addMissingAppConfigFields(appTargetPath, data);\n\n step.succeed('Configured the example app');\n });\n\n await prebuildExampleApp(appTargetPath);\n\n await modifyPackageJson(appTargetPath);\n\n await newStep('Installing dependencies in the example app', async (step) => {\n await installDependencies(packageManager, appTargetPath);\n await podInstall(appTargetPath);\n step.succeed('Installed dependencies in the example app');\n });\n}\n\n/**\n * Copies files from one directory to another.\n */\nasync function moveFiles(fromPath: string, toPath: string): Promise<void> {\n for (const file of await fs.readdir(fromPath)) {\n await fs.move(path.join(fromPath, file), path.join(toPath, file), {\n overwrite: true,\n });\n }\n}\n\n/**\n * Adds missing configuration that are required to run `expo prebuild`.\n */\nasync function addMissingAppConfigFields(appPath: string, data: SubstitutionData): Promise<void> {\n const appConfigPath = path.join(appPath, 'app.json');\n const appConfig = await fs.readJson(appConfigPath);\n const appId = `${data.project.package}.example`;\n\n // Android package name needs to be added to app.json\n if (!appConfig.expo.android) {\n appConfig.expo.android = {};\n }\n appConfig.expo.android.package = appId;\n\n // Specify iOS bundle identifier\n if (!appConfig.expo.ios) {\n appConfig.expo.ios = {};\n }\n appConfig.expo.ios.bundleIdentifier = appId;\n\n await fs.writeJson(appConfigPath, appConfig, {\n spaces: 2,\n });\n}\n\n/**\n * Applies necessary changes to **package.json** of the example app.\n * It means setting the autolinking config and removing unnecessary dependencies.\n */\nasync function modifyPackageJson(appPath: string): Promise<void> {\n const packageJsonPath = path.join(appPath, 'package.json');\n const packageJson = await fs.readJson(packageJsonPath);\n\n if (!packageJson.expo) {\n packageJson.expo = {};\n }\n\n // Set the native modules dir to the root folder,\n // so that the autolinking can detect and link the module.\n packageJson.expo.autolinking = {\n nativeModulesDir: '..',\n };\n\n // Remove unnecessary dependencies\n for (const dependencyToRemove of DEPENDENCIES_TO_REMOVE) {\n delete packageJson.dependencies[dependencyToRemove];\n }\n\n await fs.writeJson(packageJsonPath, packageJson, {\n spaces: 2,\n });\n}\n\n/**\n * Runs `expo prebuild` in the example app.\n */\nasync function prebuildExampleApp(exampleAppPath: string): Promise<void> {\n await newStep('Prebuilding the example app', async (step) => {\n await spawnAsync('expo', ['prebuild', '--no-install'], {\n cwd: exampleAppPath,\n stdio: ['ignore', 'ignore', 'pipe'],\n });\n step.succeed('Prebuilt the example app');\n });\n}\n\n/**\n * Runs `pod install` in the iOS project at the given path.\n */\nasync function podInstall(appPath: string): Promise<void> {\n await spawnAsync('pod', ['install'], {\n cwd: path.join(appPath, 'ios'),\n stdio: ['ignore', 'ignore', 'pipe'],\n });\n}\n"]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.installDependencies = void 0;
|
|
7
|
+
const spawn_async_1 = __importDefault(require("@expo/spawn-async"));
|
|
8
|
+
async function installDependencies(packageManager, appPath, ...args) {
|
|
9
|
+
await (0, spawn_async_1.default)(packageManager, ['install', ...args], {
|
|
10
|
+
cwd: appPath,
|
|
11
|
+
stdio: 'ignore',
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
exports.installDependencies = installDependencies;
|
|
15
|
+
//# sourceMappingURL=packageManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"packageManager.js","sourceRoot":"","sources":["../src/packageManager.ts"],"names":[],"mappings":";;;;;;AAAA,oEAA2C;AAIpC,KAAK,UAAU,mBAAmB,CACvC,cAAkC,EAClC,OAAe,EACf,GAAG,IAAc;IAEjB,MAAM,IAAA,qBAAU,EAAC,cAAc,EAAE,CAAC,SAAS,EAAE,GAAG,IAAI,CAAC,EAAE;QACrD,GAAG,EAAE,OAAO;QACZ,KAAK,EAAE,QAAQ;KAChB,CAAC,CAAC;AACL,CAAC;AATD,kDASC","sourcesContent":["import spawnAsync from '@expo/spawn-async';\n\nimport { PackageManagerName } from './resolvePackageManager';\n\nexport async function installDependencies(\n packageManager: PackageManagerName,\n appPath: string,\n ...args: string[]\n) {\n await spawnAsync(packageManager, ['install', ...args], {\n cwd: appPath,\n stdio: 'ignore',\n });\n}\n"]}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolvePackageManager = void 0;
|
|
4
|
+
const child_process_1 = require("child_process");
|
|
5
|
+
/** Determine which package manager to use for installing dependencies based on how the process was started. */
|
|
6
|
+
function resolvePackageManager() {
|
|
7
|
+
// Attempt to detect if the user started the command using `yarn` or `pnpm`
|
|
8
|
+
const userAgent = process.env.npm_config_user_agent;
|
|
9
|
+
if (userAgent === null || userAgent === void 0 ? void 0 : userAgent.startsWith('yarn')) {
|
|
10
|
+
return 'yarn';
|
|
11
|
+
}
|
|
12
|
+
else if (userAgent === null || userAgent === void 0 ? void 0 : userAgent.startsWith('pnpm')) {
|
|
13
|
+
return 'pnpm';
|
|
14
|
+
}
|
|
15
|
+
else if (userAgent === null || userAgent === void 0 ? void 0 : userAgent.startsWith('npm')) {
|
|
16
|
+
return 'npm';
|
|
17
|
+
}
|
|
18
|
+
// Try availability
|
|
19
|
+
if (isPackageManagerAvailable('yarn')) {
|
|
20
|
+
return 'yarn';
|
|
21
|
+
}
|
|
22
|
+
else if (isPackageManagerAvailable('pnpm')) {
|
|
23
|
+
return 'pnpm';
|
|
24
|
+
}
|
|
25
|
+
return 'npm';
|
|
26
|
+
}
|
|
27
|
+
exports.resolvePackageManager = resolvePackageManager;
|
|
28
|
+
function isPackageManagerAvailable(manager) {
|
|
29
|
+
try {
|
|
30
|
+
(0, child_process_1.execSync)(`${manager} --version`, { stdio: 'ignore' });
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
catch { }
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=resolvePackageManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolvePackageManager.js","sourceRoot":"","sources":["../src/resolvePackageManager.ts"],"names":[],"mappings":";;;AAAA,iDAAyC;AAIzC,+GAA+G;AAC/G,SAAgB,qBAAqB;IACnC,2EAA2E;IAC3E,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC;IAEpD,IAAI,SAAS,aAAT,SAAS,uBAAT,SAAS,CAAE,UAAU,CAAC,MAAM,CAAC,EAAE;QACjC,OAAO,MAAM,CAAC;KACf;SAAM,IAAI,SAAS,aAAT,SAAS,uBAAT,SAAS,CAAE,UAAU,CAAC,MAAM,CAAC,EAAE;QACxC,OAAO,MAAM,CAAC;KACf;SAAM,IAAI,SAAS,aAAT,SAAS,uBAAT,SAAS,CAAE,UAAU,CAAC,KAAK,CAAC,EAAE;QACvC,OAAO,KAAK,CAAC;KACd;IAED,mBAAmB;IACnB,IAAI,yBAAyB,CAAC,MAAM,CAAC,EAAE;QACrC,OAAO,MAAM,CAAC;KACf;SAAM,IAAI,yBAAyB,CAAC,MAAM,CAAC,EAAE;QAC5C,OAAO,MAAM,CAAC;KACf;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AApBD,sDAoBC;AAED,SAAS,yBAAyB,CAAC,OAA2B;IAC5D,IAAI;QACF,IAAA,wBAAQ,EAAC,GAAG,OAAO,YAAY,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;QACtD,OAAO,IAAI,CAAC;KACb;IAAC,MAAM,GAAE;IACV,OAAO,KAAK,CAAC;AACf,CAAC","sourcesContent":["import { execSync } from 'child_process';\n\nexport type PackageManagerName = 'npm' | 'pnpm' | 'yarn';\n\n/** Determine which package manager to use for installing dependencies based on how the process was started. */\nexport function resolvePackageManager(): PackageManagerName {\n // Attempt to detect if the user started the command using `yarn` or `pnpm`\n const userAgent = process.env.npm_config_user_agent;\n\n if (userAgent?.startsWith('yarn')) {\n return 'yarn';\n } else if (userAgent?.startsWith('pnpm')) {\n return 'pnpm';\n } else if (userAgent?.startsWith('npm')) {\n return 'npm';\n }\n\n // Try availability\n if (isPackageManagerAvailable('yarn')) {\n return 'yarn';\n } else if (isPackageManagerAvailable('pnpm')) {\n return 'pnpm';\n }\n\n return 'npm';\n}\n\nfunction isPackageManagerAvailable(manager: PackageManagerName): boolean {\n try {\n execSync(`${manager} --version`, { stdio: 'ignore' });\n return true;\n } catch {}\n return false;\n}\n"]}
|
package/build/types.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { PromptObject } from 'prompts';
|
|
2
|
+
/**
|
|
3
|
+
* Possible command options.
|
|
4
|
+
*/
|
|
5
|
+
export declare type CommandOptions = {
|
|
6
|
+
target: string;
|
|
7
|
+
source?: string;
|
|
8
|
+
name?: string;
|
|
9
|
+
description?: string;
|
|
10
|
+
package?: string;
|
|
11
|
+
author?: string;
|
|
12
|
+
license?: string;
|
|
13
|
+
repo?: string;
|
|
14
|
+
withReadme: boolean;
|
|
15
|
+
withChangelog: boolean;
|
|
16
|
+
example: boolean;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Represents an object that is passed to `ejs` when rendering the template.
|
|
20
|
+
*/
|
|
21
|
+
export declare type SubstitutionData = {
|
|
22
|
+
project: {
|
|
23
|
+
slug: string;
|
|
24
|
+
name: string;
|
|
25
|
+
version: string;
|
|
26
|
+
description: string;
|
|
27
|
+
package: string;
|
|
28
|
+
};
|
|
29
|
+
author: string;
|
|
30
|
+
license: string;
|
|
31
|
+
repo: string;
|
|
32
|
+
};
|
|
33
|
+
export declare type CustomPromptObject = PromptObject & {
|
|
34
|
+
name: string;
|
|
35
|
+
resolvedValue?: string | null;
|
|
36
|
+
};
|
package/build/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"","sourcesContent":["import { PromptObject } from 'prompts';\n\n/**\n * Possible command options.\n */\nexport type CommandOptions = {\n target: string;\n source?: string;\n name?: string;\n description?: string;\n package?: string;\n author?: string;\n license?: string;\n repo?: string;\n withReadme: boolean;\n withChangelog: boolean;\n example: boolean;\n};\n\n/**\n * Represents an object that is passed to `ejs` when rendering the template.\n */\nexport type SubstitutionData = {\n project: {\n slug: string;\n name: string;\n version: string;\n description: string;\n package: string;\n };\n author: string;\n license: string;\n repo: string;\n};\n\nexport type CustomPromptObject = PromptObject & {\n name: string;\n resolvedValue?: string | null;\n};\n"]}
|
package/build/utils.d.ts
ADDED
package/build/utils.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.newStep = void 0;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const ora_1 = __importDefault(require("ora"));
|
|
9
|
+
async function newStep(title, action, options = {}) {
|
|
10
|
+
const disabled = process.env.CI || process.env.EXPO_DEBUG;
|
|
11
|
+
const step = (0, ora_1.default)({
|
|
12
|
+
text: chalk_1.default.bold(title),
|
|
13
|
+
isEnabled: !disabled,
|
|
14
|
+
stream: disabled ? process.stdout : process.stderr,
|
|
15
|
+
...options,
|
|
16
|
+
});
|
|
17
|
+
step.start();
|
|
18
|
+
try {
|
|
19
|
+
return await action(step);
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
step.fail();
|
|
23
|
+
console.error(error);
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
exports.newStep = newStep;
|
|
28
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":";;;;;;AAAA,kDAA0B;AAC1B,8CAAsB;AAIf,KAAK,UAAU,OAAO,CAC3B,KAAa,EACb,MAAmD,EACnD,UAAuB,EAAE;IAEzB,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,EAAE,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC;IAC1D,MAAM,IAAI,GAAG,IAAA,aAAG,EAAC;QACf,IAAI,EAAE,eAAK,CAAC,IAAI,CAAC,KAAK,CAAC;QACvB,SAAS,EAAE,CAAC,QAAQ;QACpB,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM;QAClD,GAAG,OAAO;KACX,CAAC,CAAC;IAEH,IAAI,CAAC,KAAK,EAAE,CAAC;IAEb,IAAI;QACF,OAAO,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC;KAC3B;IAAC,OAAO,KAAK,EAAE;QACd,IAAI,CAAC,IAAI,EAAE,CAAC;QACZ,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACrB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;KACjB;AACH,CAAC;AAtBD,0BAsBC","sourcesContent":["import chalk from 'chalk';\nimport ora from 'ora';\n\nexport type StepOptions = ora.Options;\n\nexport async function newStep<Result>(\n title: string,\n action: (step: ora.Ora) => Promise<Result> | Result,\n options: StepOptions = {}\n): Promise<Result> {\n const disabled = process.env.CI || process.env.EXPO_DEBUG;\n const step = ora({\n text: chalk.bold(title),\n isEnabled: !disabled,\n stream: disabled ? process.stdout : process.stderr,\n ...options,\n });\n\n step.start();\n\n try {\n return await action(step);\n } catch (error) {\n step.fail();\n console.error(error);\n process.exit(1);\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-expo-module",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "The script to create the Expo module",
|
|
5
5
|
"main": "build/create-expo-module.js",
|
|
6
6
|
"types": "build/create-expo-module.d.ts",
|
|
@@ -34,16 +34,19 @@
|
|
|
34
34
|
"homepage": "https://github.com/expo/expo/tree/main/packages/expo",
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@expo/spawn-async": "^1.5.0",
|
|
37
|
+
"chalk": "^4.1.2",
|
|
37
38
|
"commander": "^8.3.0",
|
|
38
39
|
"download-tarball": "^2.0.0",
|
|
39
|
-
"ejs": "^3.1.
|
|
40
|
+
"ejs": "^3.1.7",
|
|
40
41
|
"fs-extra": "^10.0.0",
|
|
41
|
-
"
|
|
42
|
+
"ora": "^5.4.1",
|
|
43
|
+
"prompts": "^2.4.2",
|
|
44
|
+
"validate-npm-package-name": "^4.0.0"
|
|
42
45
|
},
|
|
43
46
|
"devDependencies": {
|
|
44
47
|
"@types/ejs": "^3.1.0",
|
|
45
48
|
"@types/prompts": "^2.0.14",
|
|
46
49
|
"expo-module-scripts": "^2.0.0"
|
|
47
50
|
},
|
|
48
|
-
"gitHead": "
|
|
51
|
+
"gitHead": "c6678c65b68e45062d49a2deea8e822f69388278"
|
|
49
52
|
}
|
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
import spawnAsync from '@expo/spawn-async';
|
|
2
|
+
import chalk from 'chalk';
|
|
2
3
|
import { Command } from 'commander';
|
|
3
4
|
import downloadTarball from 'download-tarball';
|
|
4
5
|
import ejs from 'ejs';
|
|
5
6
|
import fs from 'fs-extra';
|
|
6
7
|
import path from 'path';
|
|
7
|
-
import prompts
|
|
8
|
+
import prompts from 'prompts';
|
|
9
|
+
import validateNpmPackage from 'validate-npm-package-name';
|
|
10
|
+
|
|
11
|
+
import { createExampleApp } from './createExampleApp';
|
|
12
|
+
import { installDependencies } from './packageManager';
|
|
13
|
+
import { resolvePackageManager } from './resolvePackageManager';
|
|
14
|
+
import { CommandOptions, CustomPromptObject, SubstitutionData } from './types';
|
|
15
|
+
import { newStep } from './utils';
|
|
8
16
|
|
|
9
17
|
const packageJson = require('../package.json');
|
|
10
18
|
|
|
@@ -15,45 +23,6 @@ const CWD = process.env.INIT_CWD || process.cwd();
|
|
|
15
23
|
// from `$package.json` file instead of the original one.
|
|
16
24
|
const IGNORES_PATHS = ['.DS_Store', 'build', 'node_modules', 'package.json'];
|
|
17
25
|
|
|
18
|
-
/**
|
|
19
|
-
* Possible command options.
|
|
20
|
-
*/
|
|
21
|
-
type CommandOptions = {
|
|
22
|
-
target: string;
|
|
23
|
-
source?: string;
|
|
24
|
-
name?: string;
|
|
25
|
-
description?: string;
|
|
26
|
-
package?: string;
|
|
27
|
-
author?: string;
|
|
28
|
-
license?: string;
|
|
29
|
-
repo?: string;
|
|
30
|
-
withReadme: boolean;
|
|
31
|
-
withChangelog: boolean;
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Represents an object that is passed to `ejs` when rendering the template.
|
|
36
|
-
*/
|
|
37
|
-
type SubstitutionData = {
|
|
38
|
-
project: {
|
|
39
|
-
slug: string;
|
|
40
|
-
name: string;
|
|
41
|
-
version: string;
|
|
42
|
-
description: string;
|
|
43
|
-
package: string;
|
|
44
|
-
};
|
|
45
|
-
author: string;
|
|
46
|
-
license: string;
|
|
47
|
-
repo: string;
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
type CustomPromptObject = PromptObject & {
|
|
51
|
-
name: string;
|
|
52
|
-
resolvedValue?: string | null;
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
type PackageManager = 'npm' | 'yarn';
|
|
56
|
-
|
|
57
26
|
/**
|
|
58
27
|
* The main function of the command.
|
|
59
28
|
*
|
|
@@ -63,32 +32,34 @@ type PackageManager = 'npm' | 'yarn';
|
|
|
63
32
|
async function main(target: string | undefined, options: CommandOptions) {
|
|
64
33
|
const targetDir = target ? path.join(CWD, target) : CWD;
|
|
65
34
|
|
|
66
|
-
options.target = targetDir;
|
|
67
35
|
await fs.ensureDir(targetDir);
|
|
36
|
+
await confirmTargetDirAsync(targetDir);
|
|
37
|
+
|
|
38
|
+
options.target = targetDir;
|
|
68
39
|
|
|
69
40
|
const data = await askForSubstitutionDataAsync(targetDir, options);
|
|
70
|
-
const packageManager = await
|
|
41
|
+
const packageManager = await resolvePackageManager();
|
|
71
42
|
const packagePath = options.source
|
|
72
43
|
? path.join(CWD, options.source)
|
|
73
44
|
: await downloadPackageAsync(targetDir);
|
|
74
|
-
const files = await getFilesAsync(packagePath);
|
|
75
45
|
|
|
76
|
-
|
|
46
|
+
await newStep('Creating the module from template files', async (step) => {
|
|
47
|
+
await createModuleFromTemplate(packagePath, targetDir, data);
|
|
48
|
+
step.succeed('Created the module from template files');
|
|
49
|
+
});
|
|
77
50
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
closeDelimiter: '}',
|
|
83
|
-
escape: (value: string) => value.replace('.', path.sep),
|
|
84
|
-
});
|
|
85
|
-
const fromPath = path.join(packagePath, file);
|
|
86
|
-
const toPath = path.join(targetDir, renderedRelativePath);
|
|
87
|
-
const template = await fs.readFile(fromPath, { encoding: 'utf8' });
|
|
88
|
-
const renderedContent = ejs.render(template, data);
|
|
51
|
+
await newStep('Installing module dependencies', async (step) => {
|
|
52
|
+
await installDependencies(packageManager, targetDir);
|
|
53
|
+
step.succeed('Installed module dependencies');
|
|
54
|
+
});
|
|
89
55
|
|
|
90
|
-
|
|
91
|
-
|
|
56
|
+
await newStep('Compiling TypeScript files', async (step) => {
|
|
57
|
+
await spawnAsync(packageManager, ['run', 'build'], {
|
|
58
|
+
cwd: targetDir,
|
|
59
|
+
stdio: 'ignore',
|
|
60
|
+
});
|
|
61
|
+
step.succeed('Compiled TypeScript files');
|
|
62
|
+
});
|
|
92
63
|
|
|
93
64
|
if (!options.source) {
|
|
94
65
|
// Files in the downloaded tarball are wrapped in `package` dir.
|
|
@@ -101,10 +72,12 @@ async function main(target: string | undefined, options: CommandOptions) {
|
|
|
101
72
|
if (!options.withChangelog) {
|
|
102
73
|
await fs.remove(path.join(targetDir, 'CHANGELOG.md'));
|
|
103
74
|
}
|
|
75
|
+
if (options.example) {
|
|
76
|
+
// Create "example" folder
|
|
77
|
+
await createExampleApp(data, targetDir, packageManager);
|
|
78
|
+
}
|
|
104
79
|
|
|
105
|
-
|
|
106
|
-
await postActionsAsync(packageManager, targetDir);
|
|
107
|
-
|
|
80
|
+
console.log();
|
|
108
81
|
console.log('✅ Successfully created Expo module');
|
|
109
82
|
}
|
|
110
83
|
|
|
@@ -158,49 +131,44 @@ async function npmWhoamiAsync(targetDir: string): Promise<string | null> {
|
|
|
158
131
|
* Downloads the template from NPM registry.
|
|
159
132
|
*/
|
|
160
133
|
async function downloadPackageAsync(targetDir: string): Promise<string> {
|
|
161
|
-
|
|
134
|
+
return await newStep('Downloading module template from npm', async (step) => {
|
|
135
|
+
const tarballUrl = await getNpmTarballUrl('expo-module-template');
|
|
162
136
|
|
|
163
|
-
|
|
137
|
+
await downloadTarball({
|
|
138
|
+
url: tarballUrl,
|
|
139
|
+
dir: targetDir,
|
|
140
|
+
});
|
|
164
141
|
|
|
165
|
-
|
|
166
|
-
url: tarballUrl,
|
|
167
|
-
dir: targetDir,
|
|
168
|
-
});
|
|
169
|
-
return path.join(targetDir, 'package');
|
|
170
|
-
}
|
|
142
|
+
step.succeed('Downloaded module template from npm');
|
|
171
143
|
|
|
172
|
-
|
|
173
|
-
* Asks whether to use Yarn or npm as a dependency package manager.
|
|
174
|
-
*/
|
|
175
|
-
async function selectPackageManagerAsync(): Promise<PackageManager> {
|
|
176
|
-
const { packageManager } = await prompts({
|
|
177
|
-
type: 'select',
|
|
178
|
-
name: 'packageManager',
|
|
179
|
-
message: 'Which package manager do you want to use to install dependencies?',
|
|
180
|
-
choices: [
|
|
181
|
-
{ title: 'yarn', value: 'yarn' },
|
|
182
|
-
{ title: 'npm', value: 'npm' },
|
|
183
|
-
],
|
|
144
|
+
return path.join(targetDir, 'package');
|
|
184
145
|
});
|
|
185
|
-
return packageManager;
|
|
186
146
|
}
|
|
187
147
|
|
|
188
148
|
/**
|
|
189
|
-
*
|
|
149
|
+
* Creates the module based on the `ejs` template (e.g. `expo-module-template` package).
|
|
190
150
|
*/
|
|
191
|
-
async function
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
}
|
|
151
|
+
async function createModuleFromTemplate(
|
|
152
|
+
templatePath: string,
|
|
153
|
+
targetPath: string,
|
|
154
|
+
data: SubstitutionData
|
|
155
|
+
) {
|
|
156
|
+
const files = await getFilesAsync(templatePath);
|
|
198
157
|
|
|
199
|
-
|
|
200
|
-
|
|
158
|
+
// Iterate through all template files.
|
|
159
|
+
for (const file of files) {
|
|
160
|
+
const renderedRelativePath = ejs.render(file.replace(/^\$/, ''), data, {
|
|
161
|
+
openDelimiter: '{',
|
|
162
|
+
closeDelimiter: '}',
|
|
163
|
+
escape: (value: string) => value.replace('.', path.sep),
|
|
164
|
+
});
|
|
165
|
+
const fromPath = path.join(templatePath, file);
|
|
166
|
+
const toPath = path.join(targetPath, renderedRelativePath);
|
|
167
|
+
const template = await fs.readFile(fromPath, { encoding: 'utf8' });
|
|
168
|
+
const renderedContent = ejs.render(template, data);
|
|
201
169
|
|
|
202
|
-
|
|
203
|
-
|
|
170
|
+
await fs.outputFile(toPath, renderedContent, { encoding: 'utf8' });
|
|
171
|
+
}
|
|
204
172
|
}
|
|
205
173
|
|
|
206
174
|
/**
|
|
@@ -212,6 +180,7 @@ async function askForSubstitutionDataAsync(
|
|
|
212
180
|
options: CommandOptions
|
|
213
181
|
): Promise<SubstitutionData> {
|
|
214
182
|
const defaultPackageSlug = path.basename(targetDir);
|
|
183
|
+
const useDefaultSlug = options.target && validateNpmPackage(defaultPackageSlug);
|
|
215
184
|
const defaultProjectName = defaultPackageSlug
|
|
216
185
|
.replace(/^./, (match) => match.toUpperCase())
|
|
217
186
|
.replace(/\W+(\w)/g, (_, p1) => p1.toUpperCase());
|
|
@@ -222,7 +191,9 @@ async function askForSubstitutionDataAsync(
|
|
|
222
191
|
name: 'slug',
|
|
223
192
|
message: 'What is the package slug?',
|
|
224
193
|
initial: defaultPackageSlug,
|
|
225
|
-
resolvedValue:
|
|
194
|
+
resolvedValue: useDefaultSlug ? defaultPackageSlug : null,
|
|
195
|
+
validate: (input) =>
|
|
196
|
+
validateNpmPackage(input).validForNewPackages || 'Must be a valid npm package name',
|
|
226
197
|
},
|
|
227
198
|
{
|
|
228
199
|
type: 'text',
|
|
@@ -234,6 +205,7 @@ async function askForSubstitutionDataAsync(
|
|
|
234
205
|
type: 'text',
|
|
235
206
|
name: 'description',
|
|
236
207
|
message: 'How would you describe the module?',
|
|
208
|
+
validate: (input) => !!input || 'Cannot be empty',
|
|
237
209
|
},
|
|
238
210
|
{
|
|
239
211
|
type: 'text',
|
|
@@ -257,13 +229,19 @@ async function askForSubstitutionDataAsync(
|
|
|
257
229
|
type: 'text',
|
|
258
230
|
name: 'repo',
|
|
259
231
|
message: 'What is the repository URL?',
|
|
232
|
+
validate: (input) => /^https?:\/\//.test(input) || 'Must be a valid URL',
|
|
260
233
|
},
|
|
261
234
|
];
|
|
262
235
|
|
|
236
|
+
// Stop the process when the user cancels/exits the prompt.
|
|
237
|
+
const onCancel = () => {
|
|
238
|
+
process.exit(0);
|
|
239
|
+
};
|
|
240
|
+
|
|
263
241
|
const answers: Record<string, string> = {};
|
|
264
242
|
for (const query of promptQueries) {
|
|
265
243
|
const { name, resolvedValue } = query;
|
|
266
|
-
answers[name] = resolvedValue ?? options[name] ?? (await prompts(query))[name];
|
|
244
|
+
answers[name] = resolvedValue ?? options[name] ?? (await prompts(query, { onCancel }))[name];
|
|
267
245
|
}
|
|
268
246
|
|
|
269
247
|
const { slug, name, description, package: projectPackage, author, license, repo } = answers;
|
|
@@ -282,6 +260,33 @@ async function askForSubstitutionDataAsync(
|
|
|
282
260
|
};
|
|
283
261
|
}
|
|
284
262
|
|
|
263
|
+
/**
|
|
264
|
+
* Checks whether the target directory is empty and if not, asks the user to confirm if he wants to continue.
|
|
265
|
+
*/
|
|
266
|
+
async function confirmTargetDirAsync(targetDir: string): Promise<void> {
|
|
267
|
+
const files = await fs.readdir(targetDir);
|
|
268
|
+
|
|
269
|
+
if (files.length === 0) {
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
const { shouldContinue } = await prompts(
|
|
273
|
+
{
|
|
274
|
+
type: 'confirm',
|
|
275
|
+
name: 'shouldContinue',
|
|
276
|
+
message: `The target directory ${chalk.magenta(
|
|
277
|
+
targetDir
|
|
278
|
+
)} is not empty.\nDo you want to continue anyway?`,
|
|
279
|
+
initial: true,
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
onCancel: () => false,
|
|
283
|
+
}
|
|
284
|
+
);
|
|
285
|
+
if (!shouldContinue) {
|
|
286
|
+
process.exit(0);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
285
290
|
const program = new Command();
|
|
286
291
|
|
|
287
292
|
program
|
|
@@ -301,6 +306,7 @@ program
|
|
|
301
306
|
.option('-r, --repo <repo_url>', 'The URL to the repository.')
|
|
302
307
|
.option('--with-readme', 'Whether to include README.md file.', false)
|
|
303
308
|
.option('--with-changelog', 'Whether to include CHANGELOG.md file.', false)
|
|
309
|
+
.option('--no-example', 'Whether to skip creating the example app.', false)
|
|
304
310
|
.action(main);
|
|
305
311
|
|
|
306
312
|
program.parse(process.argv);
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import spawnAsync from '@expo/spawn-async';
|
|
2
|
+
import fs from 'fs-extra';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
|
|
5
|
+
import { installDependencies } from './packageManager';
|
|
6
|
+
import { PackageManagerName } from './resolvePackageManager';
|
|
7
|
+
import { SubstitutionData } from './types';
|
|
8
|
+
import { newStep } from './utils';
|
|
9
|
+
|
|
10
|
+
// These dependencies will be removed from the example app (`expo init` adds them)
|
|
11
|
+
const DEPENDENCIES_TO_REMOVE = ['expo-status-bar', 'expo-splash-screen'];
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Initializes a new Expo project as an example app.
|
|
15
|
+
*/
|
|
16
|
+
export async function createExampleApp(
|
|
17
|
+
data: SubstitutionData,
|
|
18
|
+
targetDir: string,
|
|
19
|
+
packageManager: PackageManagerName
|
|
20
|
+
): Promise<void> {
|
|
21
|
+
const exampleProjectSlug = `${data.project.slug}-example`;
|
|
22
|
+
|
|
23
|
+
await newStep('Initializing the example app', async (step) => {
|
|
24
|
+
await spawnAsync(
|
|
25
|
+
'expo',
|
|
26
|
+
['init', exampleProjectSlug, '--template', 'expo-template-blank-typescript'],
|
|
27
|
+
{
|
|
28
|
+
cwd: targetDir,
|
|
29
|
+
stdio: ['ignore', 'ignore', 'inherit'],
|
|
30
|
+
}
|
|
31
|
+
);
|
|
32
|
+
step.succeed('Initialized the example app');
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// `expo init` creates a new folder with the same name as the project slug
|
|
36
|
+
const appTmpPath = path.join(targetDir, exampleProjectSlug);
|
|
37
|
+
|
|
38
|
+
// Path to the target example dir
|
|
39
|
+
const appTargetPath = path.join(targetDir, 'example');
|
|
40
|
+
|
|
41
|
+
await newStep('Configuring the example app', async (step) => {
|
|
42
|
+
// "example" folder already exists and contains template files,
|
|
43
|
+
// that should replace these created by `expo init`.
|
|
44
|
+
await moveFiles(appTargetPath, appTmpPath);
|
|
45
|
+
|
|
46
|
+
// Cleanup the "example" dir
|
|
47
|
+
await fs.rmdir(appTargetPath);
|
|
48
|
+
|
|
49
|
+
// Move the temporary example app to "example" dir
|
|
50
|
+
await fs.rename(appTmpPath, appTargetPath);
|
|
51
|
+
|
|
52
|
+
await addMissingAppConfigFields(appTargetPath, data);
|
|
53
|
+
|
|
54
|
+
step.succeed('Configured the example app');
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
await prebuildExampleApp(appTargetPath);
|
|
58
|
+
|
|
59
|
+
await modifyPackageJson(appTargetPath);
|
|
60
|
+
|
|
61
|
+
await newStep('Installing dependencies in the example app', async (step) => {
|
|
62
|
+
await installDependencies(packageManager, appTargetPath);
|
|
63
|
+
await podInstall(appTargetPath);
|
|
64
|
+
step.succeed('Installed dependencies in the example app');
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Copies files from one directory to another.
|
|
70
|
+
*/
|
|
71
|
+
async function moveFiles(fromPath: string, toPath: string): Promise<void> {
|
|
72
|
+
for (const file of await fs.readdir(fromPath)) {
|
|
73
|
+
await fs.move(path.join(fromPath, file), path.join(toPath, file), {
|
|
74
|
+
overwrite: true,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Adds missing configuration that are required to run `expo prebuild`.
|
|
81
|
+
*/
|
|
82
|
+
async function addMissingAppConfigFields(appPath: string, data: SubstitutionData): Promise<void> {
|
|
83
|
+
const appConfigPath = path.join(appPath, 'app.json');
|
|
84
|
+
const appConfig = await fs.readJson(appConfigPath);
|
|
85
|
+
const appId = `${data.project.package}.example`;
|
|
86
|
+
|
|
87
|
+
// Android package name needs to be added to app.json
|
|
88
|
+
if (!appConfig.expo.android) {
|
|
89
|
+
appConfig.expo.android = {};
|
|
90
|
+
}
|
|
91
|
+
appConfig.expo.android.package = appId;
|
|
92
|
+
|
|
93
|
+
// Specify iOS bundle identifier
|
|
94
|
+
if (!appConfig.expo.ios) {
|
|
95
|
+
appConfig.expo.ios = {};
|
|
96
|
+
}
|
|
97
|
+
appConfig.expo.ios.bundleIdentifier = appId;
|
|
98
|
+
|
|
99
|
+
await fs.writeJson(appConfigPath, appConfig, {
|
|
100
|
+
spaces: 2,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Applies necessary changes to **package.json** of the example app.
|
|
106
|
+
* It means setting the autolinking config and removing unnecessary dependencies.
|
|
107
|
+
*/
|
|
108
|
+
async function modifyPackageJson(appPath: string): Promise<void> {
|
|
109
|
+
const packageJsonPath = path.join(appPath, 'package.json');
|
|
110
|
+
const packageJson = await fs.readJson(packageJsonPath);
|
|
111
|
+
|
|
112
|
+
if (!packageJson.expo) {
|
|
113
|
+
packageJson.expo = {};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Set the native modules dir to the root folder,
|
|
117
|
+
// so that the autolinking can detect and link the module.
|
|
118
|
+
packageJson.expo.autolinking = {
|
|
119
|
+
nativeModulesDir: '..',
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
// Remove unnecessary dependencies
|
|
123
|
+
for (const dependencyToRemove of DEPENDENCIES_TO_REMOVE) {
|
|
124
|
+
delete packageJson.dependencies[dependencyToRemove];
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
await fs.writeJson(packageJsonPath, packageJson, {
|
|
128
|
+
spaces: 2,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Runs `expo prebuild` in the example app.
|
|
134
|
+
*/
|
|
135
|
+
async function prebuildExampleApp(exampleAppPath: string): Promise<void> {
|
|
136
|
+
await newStep('Prebuilding the example app', async (step) => {
|
|
137
|
+
await spawnAsync('expo', ['prebuild', '--no-install'], {
|
|
138
|
+
cwd: exampleAppPath,
|
|
139
|
+
stdio: ['ignore', 'ignore', 'pipe'],
|
|
140
|
+
});
|
|
141
|
+
step.succeed('Prebuilt the example app');
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Runs `pod install` in the iOS project at the given path.
|
|
147
|
+
*/
|
|
148
|
+
async function podInstall(appPath: string): Promise<void> {
|
|
149
|
+
await spawnAsync('pod', ['install'], {
|
|
150
|
+
cwd: path.join(appPath, 'ios'),
|
|
151
|
+
stdio: ['ignore', 'ignore', 'pipe'],
|
|
152
|
+
});
|
|
153
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import spawnAsync from '@expo/spawn-async';
|
|
2
|
+
|
|
3
|
+
import { PackageManagerName } from './resolvePackageManager';
|
|
4
|
+
|
|
5
|
+
export async function installDependencies(
|
|
6
|
+
packageManager: PackageManagerName,
|
|
7
|
+
appPath: string,
|
|
8
|
+
...args: string[]
|
|
9
|
+
) {
|
|
10
|
+
await spawnAsync(packageManager, ['install', ...args], {
|
|
11
|
+
cwd: appPath,
|
|
12
|
+
stdio: 'ignore',
|
|
13
|
+
});
|
|
14
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { execSync } from 'child_process';
|
|
2
|
+
|
|
3
|
+
export type PackageManagerName = 'npm' | 'pnpm' | 'yarn';
|
|
4
|
+
|
|
5
|
+
/** Determine which package manager to use for installing dependencies based on how the process was started. */
|
|
6
|
+
export function resolvePackageManager(): PackageManagerName {
|
|
7
|
+
// Attempt to detect if the user started the command using `yarn` or `pnpm`
|
|
8
|
+
const userAgent = process.env.npm_config_user_agent;
|
|
9
|
+
|
|
10
|
+
if (userAgent?.startsWith('yarn')) {
|
|
11
|
+
return 'yarn';
|
|
12
|
+
} else if (userAgent?.startsWith('pnpm')) {
|
|
13
|
+
return 'pnpm';
|
|
14
|
+
} else if (userAgent?.startsWith('npm')) {
|
|
15
|
+
return 'npm';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Try availability
|
|
19
|
+
if (isPackageManagerAvailable('yarn')) {
|
|
20
|
+
return 'yarn';
|
|
21
|
+
} else if (isPackageManagerAvailable('pnpm')) {
|
|
22
|
+
return 'pnpm';
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return 'npm';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function isPackageManagerAvailable(manager: PackageManagerName): boolean {
|
|
29
|
+
try {
|
|
30
|
+
execSync(`${manager} --version`, { stdio: 'ignore' });
|
|
31
|
+
return true;
|
|
32
|
+
} catch {}
|
|
33
|
+
return false;
|
|
34
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { PromptObject } from 'prompts';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Possible command options.
|
|
5
|
+
*/
|
|
6
|
+
export type CommandOptions = {
|
|
7
|
+
target: string;
|
|
8
|
+
source?: string;
|
|
9
|
+
name?: string;
|
|
10
|
+
description?: string;
|
|
11
|
+
package?: string;
|
|
12
|
+
author?: string;
|
|
13
|
+
license?: string;
|
|
14
|
+
repo?: string;
|
|
15
|
+
withReadme: boolean;
|
|
16
|
+
withChangelog: boolean;
|
|
17
|
+
example: boolean;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Represents an object that is passed to `ejs` when rendering the template.
|
|
22
|
+
*/
|
|
23
|
+
export type SubstitutionData = {
|
|
24
|
+
project: {
|
|
25
|
+
slug: string;
|
|
26
|
+
name: string;
|
|
27
|
+
version: string;
|
|
28
|
+
description: string;
|
|
29
|
+
package: string;
|
|
30
|
+
};
|
|
31
|
+
author: string;
|
|
32
|
+
license: string;
|
|
33
|
+
repo: string;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export type CustomPromptObject = PromptObject & {
|
|
37
|
+
name: string;
|
|
38
|
+
resolvedValue?: string | null;
|
|
39
|
+
};
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
|
|
4
|
+
export type StepOptions = ora.Options;
|
|
5
|
+
|
|
6
|
+
export async function newStep<Result>(
|
|
7
|
+
title: string,
|
|
8
|
+
action: (step: ora.Ora) => Promise<Result> | Result,
|
|
9
|
+
options: StepOptions = {}
|
|
10
|
+
): Promise<Result> {
|
|
11
|
+
const disabled = process.env.CI || process.env.EXPO_DEBUG;
|
|
12
|
+
const step = ora({
|
|
13
|
+
text: chalk.bold(title),
|
|
14
|
+
isEnabled: !disabled,
|
|
15
|
+
stream: disabled ? process.stdout : process.stderr,
|
|
16
|
+
...options,
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
step.start();
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
return await action(step);
|
|
23
|
+
} catch (error) {
|
|
24
|
+
step.fail();
|
|
25
|
+
console.error(error);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
}
|