create-expo-module 0.3.0 → 0.3.1
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 +22 -13
- package/build/create-expo-module.js.map +1 -1
- package/build/createExampleApp.d.ts +6 -0
- package/build/createExampleApp.js +129 -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/types.d.ts +36 -0
- package/build/types.js +3 -0
- package/build/types.js.map +1 -0
- package/package.json +4 -3
- package/src/create-expo-module.ts +26 -53
- package/src/createExampleApp.ts +157 -0
- package/src/packageManager.ts +14 -0
- package/src/types.ts +39 -0
|
@@ -11,6 +11,9 @@ const ejs_1 = __importDefault(require("ejs"));
|
|
|
11
11
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
12
12
|
const path_1 = __importDefault(require("path"));
|
|
13
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");
|
|
14
17
|
const resolvePackageManager_1 = require("./resolvePackageManager");
|
|
15
18
|
const packageJson = require('../package.json');
|
|
16
19
|
// `yarn run` may change the current working dir, then we should use `INIT_CWD` env.
|
|
@@ -26,9 +29,9 @@ const IGNORES_PATHS = ['.DS_Store', 'build', 'node_modules', 'package.json'];
|
|
|
26
29
|
*/
|
|
27
30
|
async function main(target, options) {
|
|
28
31
|
const targetDir = target ? path_1.default.join(CWD, target) : CWD;
|
|
32
|
+
await fs_extra_1.default.ensureDir(targetDir);
|
|
29
33
|
await confirmTargetDirAsync(targetDir);
|
|
30
34
|
options.target = targetDir;
|
|
31
|
-
await fs_extra_1.default.ensureDir(targetDir);
|
|
32
35
|
const data = await askForSubstitutionDataAsync(targetDir, options);
|
|
33
36
|
const packageManager = await (0, resolvePackageManager_1.resolvePackageManager)();
|
|
34
37
|
const packagePath = options.source
|
|
@@ -49,6 +52,8 @@ async function main(target, options) {
|
|
|
49
52
|
const renderedContent = ejs_1.default.render(template, data);
|
|
50
53
|
await fs_extra_1.default.outputFile(toPath, renderedContent, { encoding: 'utf8' });
|
|
51
54
|
}
|
|
55
|
+
// Install dependencies and build
|
|
56
|
+
await postActionsAsync(packageManager, targetDir);
|
|
52
57
|
if (!options.source) {
|
|
53
58
|
// Files in the downloaded tarball are wrapped in `package` dir.
|
|
54
59
|
// We should remove it after all.
|
|
@@ -60,8 +65,10 @@ async function main(target, options) {
|
|
|
60
65
|
if (!options.withChangelog) {
|
|
61
66
|
await fs_extra_1.default.remove(path_1.default.join(targetDir, 'CHANGELOG.md'));
|
|
62
67
|
}
|
|
63
|
-
|
|
64
|
-
|
|
68
|
+
if (options.example) {
|
|
69
|
+
// Create "example" folder
|
|
70
|
+
await (0, createExampleApp_1.createExampleApp)(data, targetDir, packageManager);
|
|
71
|
+
}
|
|
65
72
|
console.log('✅ Successfully created Expo module');
|
|
66
73
|
}
|
|
67
74
|
/**
|
|
@@ -121,16 +128,13 @@ async function downloadPackageAsync(targetDir) {
|
|
|
121
128
|
* Installs dependencies and builds TypeScript files.
|
|
122
129
|
*/
|
|
123
130
|
async function postActionsAsync(packageManager, targetDir) {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
cwd: targetDir,
|
|
127
|
-
stdio: 'ignore',
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
console.log('📦 Installing dependencies...');
|
|
131
|
-
await run('install');
|
|
131
|
+
console.log('📦 Installing module dependencies...');
|
|
132
|
+
await (0, packageManager_1.installDependencies)(packageManager, targetDir);
|
|
132
133
|
console.log('🛠 Compiling TypeScript files...');
|
|
133
|
-
await
|
|
134
|
+
await (0, spawn_async_1.default)(packageManager, ['run', 'build'], {
|
|
135
|
+
cwd: targetDir,
|
|
136
|
+
stdio: 'ignore',
|
|
137
|
+
});
|
|
134
138
|
}
|
|
135
139
|
/**
|
|
136
140
|
* Asks the user for some data necessary to render the template.
|
|
@@ -139,6 +143,7 @@ async function postActionsAsync(packageManager, targetDir) {
|
|
|
139
143
|
async function askForSubstitutionDataAsync(targetDir, options) {
|
|
140
144
|
var _a, _b;
|
|
141
145
|
const defaultPackageSlug = path_1.default.basename(targetDir);
|
|
146
|
+
const useDefaultSlug = options.target && (0, validate_npm_package_name_1.default)(defaultPackageSlug);
|
|
142
147
|
const defaultProjectName = defaultPackageSlug
|
|
143
148
|
.replace(/^./, (match) => match.toUpperCase())
|
|
144
149
|
.replace(/\W+(\w)/g, (_, p1) => p1.toUpperCase());
|
|
@@ -148,7 +153,8 @@ async function askForSubstitutionDataAsync(targetDir, options) {
|
|
|
148
153
|
name: 'slug',
|
|
149
154
|
message: 'What is the package slug?',
|
|
150
155
|
initial: defaultPackageSlug,
|
|
151
|
-
resolvedValue:
|
|
156
|
+
resolvedValue: useDefaultSlug ? defaultPackageSlug : null,
|
|
157
|
+
validate: (input) => (0, validate_npm_package_name_1.default)(input).validForNewPackages || 'Must be a valid npm package name',
|
|
152
158
|
},
|
|
153
159
|
{
|
|
154
160
|
type: 'text',
|
|
@@ -160,6 +166,7 @@ async function askForSubstitutionDataAsync(targetDir, options) {
|
|
|
160
166
|
type: 'text',
|
|
161
167
|
name: 'description',
|
|
162
168
|
message: 'How would you describe the module?',
|
|
169
|
+
validate: (input) => !!input || 'Cannot be empty',
|
|
163
170
|
},
|
|
164
171
|
{
|
|
165
172
|
type: 'text',
|
|
@@ -183,6 +190,7 @@ async function askForSubstitutionDataAsync(targetDir, options) {
|
|
|
183
190
|
type: 'text',
|
|
184
191
|
name: 'repo',
|
|
185
192
|
message: 'What is the repository URL?',
|
|
193
|
+
validate: (input) => /^https?:\/\//.test(input) || 'Must be a valid URL',
|
|
186
194
|
},
|
|
187
195
|
];
|
|
188
196
|
// Stop the process when the user cancels/exits the prompt.
|
|
@@ -243,6 +251,7 @@ program
|
|
|
243
251
|
.option('-r, --repo <repo_url>', 'The URL to the repository.')
|
|
244
252
|
.option('--with-readme', 'Whether to include README.md file.', false)
|
|
245
253
|
.option('--with-changelog', 'Whether to include CHANGELOG.md file.', false)
|
|
254
|
+
.option('--no-example', 'Whether to skip creating the example app.', false)
|
|
246
255
|
.action(main);
|
|
247
256
|
program.parse(process.argv);
|
|
248
257
|
//# 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 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, { PromptObject } from 'prompts';\n\nimport { PackageManagerName, resolvePackageManager } from './resolvePackageManager';\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\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 confirmTargetDirAsync(targetDir);\n\n options.target = targetDir;\n await fs.ensureDir(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 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 * Installs dependencies and builds TypeScript files.\n */\nasync function postActionsAsync(packageManager: PackageManagerName, 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 // 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 .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 { PackageManagerName, resolvePackageManager } from './resolvePackageManager';\nimport { CommandOptions, CustomPromptObject, SubstitutionData } from './types';\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 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 // Install dependencies and build\n await postActionsAsync(packageManager, targetDir);\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('✅ 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 * Installs dependencies and builds TypeScript files.\n */\nasync function postActionsAsync(packageManager: PackageManagerName, targetDir: string) {\n console.log('📦 Installing module dependencies...');\n await installDependencies(packageManager, targetDir);\n\n console.log('🛠 Compiling TypeScript files...');\n await spawnAsync(packageManager, ['run', 'build'], {\n cwd: targetDir,\n stdio: 'ignore',\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,129 @@
|
|
|
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
|
+
// These dependencies will be removed from the example app (`expo init` adds them)
|
|
12
|
+
const DEPENDENCIES_TO_REMOVE = ['expo-status-bar', 'expo-splash-screen'];
|
|
13
|
+
/**
|
|
14
|
+
* Initializes a new Expo project as an example app.
|
|
15
|
+
*/
|
|
16
|
+
async function createExampleApp(data, targetDir, packageManager) {
|
|
17
|
+
console.log('🎭 Creating the example app...');
|
|
18
|
+
const exampleProjectSlug = `${data.project.slug}-example`;
|
|
19
|
+
await (0, spawn_async_1.default)('expo', ['init', exampleProjectSlug, '--template', 'expo-template-blank-typescript'], {
|
|
20
|
+
cwd: targetDir,
|
|
21
|
+
stdio: ['ignore', 'ignore', 'inherit'],
|
|
22
|
+
});
|
|
23
|
+
// `expo init` creates a new folder with the same name as the project slug
|
|
24
|
+
const appTmpPath = path_1.default.join(targetDir, exampleProjectSlug);
|
|
25
|
+
// Path to the target example dir
|
|
26
|
+
const appTargetPath = path_1.default.join(targetDir, 'example');
|
|
27
|
+
console.log('🛠 Configuring the example app...');
|
|
28
|
+
// "example" folder already exists and contains template files,
|
|
29
|
+
// that should replace these created by `expo init`.
|
|
30
|
+
await moveFiles(appTargetPath, appTmpPath);
|
|
31
|
+
// Cleanup the "example" dir
|
|
32
|
+
await fs_extra_1.default.rmdir(appTargetPath);
|
|
33
|
+
// Move the temporary example app to "example" dir
|
|
34
|
+
await fs_extra_1.default.rename(appTmpPath, appTargetPath);
|
|
35
|
+
await addMissingAppConfigFields(appTargetPath, data);
|
|
36
|
+
console.log('👷 Prebuilding the example app...');
|
|
37
|
+
await prebuildExampleApp(appTargetPath);
|
|
38
|
+
await modifyPackageJson(appTargetPath);
|
|
39
|
+
console.log('📦 Installing dependencies in the example app...');
|
|
40
|
+
await (0, packageManager_1.installDependencies)(packageManager, appTargetPath);
|
|
41
|
+
console.log('🥥 Installing iOS pods in the example app...');
|
|
42
|
+
await podInstall(appTargetPath);
|
|
43
|
+
}
|
|
44
|
+
exports.createExampleApp = createExampleApp;
|
|
45
|
+
/**
|
|
46
|
+
* Copies files from one directory to another.
|
|
47
|
+
*/
|
|
48
|
+
async function moveFiles(fromPath, toPath) {
|
|
49
|
+
for (const file of await fs_extra_1.default.readdir(fromPath)) {
|
|
50
|
+
await fs_extra_1.default.move(path_1.default.join(fromPath, file), path_1.default.join(toPath, file), {
|
|
51
|
+
overwrite: true,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Adds missing configuration that are required to run `expo prebuild`.
|
|
57
|
+
*/
|
|
58
|
+
async function addMissingAppConfigFields(appPath, data) {
|
|
59
|
+
const appConfigPath = path_1.default.join(appPath, 'app.json');
|
|
60
|
+
const appConfig = await fs_extra_1.default.readJson(appConfigPath);
|
|
61
|
+
const appId = `${data.project.package}.example`;
|
|
62
|
+
// Android package name needs to be added to app.json
|
|
63
|
+
if (!appConfig.expo.android) {
|
|
64
|
+
appConfig.expo.android = {};
|
|
65
|
+
}
|
|
66
|
+
appConfig.expo.android.package = appId;
|
|
67
|
+
// Specify iOS bundle identifier
|
|
68
|
+
if (!appConfig.expo.ios) {
|
|
69
|
+
appConfig.expo.ios = {};
|
|
70
|
+
}
|
|
71
|
+
appConfig.expo.ios.bundleIdentifier = appId;
|
|
72
|
+
await fs_extra_1.default.writeJson(appConfigPath, appConfig, {
|
|
73
|
+
spaces: 2,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Applies necessary changes to **package.json** of the example app.
|
|
78
|
+
* It means setting the autolinking config and removing unnecessary dependencies.
|
|
79
|
+
*/
|
|
80
|
+
async function modifyPackageJson(appPath) {
|
|
81
|
+
const packageJsonPath = path_1.default.join(appPath, 'package.json');
|
|
82
|
+
const packageJson = await fs_extra_1.default.readJson(packageJsonPath);
|
|
83
|
+
if (!packageJson.expo) {
|
|
84
|
+
packageJson.expo = {};
|
|
85
|
+
}
|
|
86
|
+
// Set the native modules dir to the root folder,
|
|
87
|
+
// so that the autolinking can detect and link the module.
|
|
88
|
+
packageJson.expo.autolinking = {
|
|
89
|
+
nativeModulesDir: '..',
|
|
90
|
+
};
|
|
91
|
+
// Remove unnecessary dependencies
|
|
92
|
+
for (const dependencyToRemove of DEPENDENCIES_TO_REMOVE) {
|
|
93
|
+
delete packageJson.dependencies[dependencyToRemove];
|
|
94
|
+
}
|
|
95
|
+
await fs_extra_1.default.writeJson(packageJsonPath, packageJson, {
|
|
96
|
+
spaces: 2,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Runs `expo prebuild` in the example app.
|
|
101
|
+
*/
|
|
102
|
+
async function prebuildExampleApp(exampleAppPath) {
|
|
103
|
+
try {
|
|
104
|
+
await (0, spawn_async_1.default)('expo', ['prebuild', '--no-install'], {
|
|
105
|
+
cwd: exampleAppPath,
|
|
106
|
+
stdio: ['ignore', 'ignore', 'pipe'],
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
console.error(error.stderr);
|
|
111
|
+
process.exit(1);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Runs `pod install` in the iOS project at the given path.
|
|
116
|
+
*/
|
|
117
|
+
async function podInstall(appPath) {
|
|
118
|
+
try {
|
|
119
|
+
await (0, spawn_async_1.default)('pod', ['install'], {
|
|
120
|
+
cwd: path_1.default.join(appPath, 'ios'),
|
|
121
|
+
stdio: ['ignore', 'ignore', 'pipe'],
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
console.error(error.stderr);
|
|
126
|
+
process.exit(1);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
//# 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';\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 console.log('🎭 Creating the example app...');\n\n const exampleProjectSlug = `${data.project.slug}-example`;\n\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\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 console.log('🛠 Configuring the example app...');\n\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 console.log('👷 Prebuilding the example app...');\n await prebuildExampleApp(appTargetPath);\n\n await modifyPackageJson(appTargetPath);\n\n console.log('📦 Installing dependencies in the example app...');\n await installDependencies(packageManager, appTargetPath);\n\n console.log('🥥 Installing iOS pods in the example app...');\n await podInstall(appTargetPath);\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 try {\n await spawnAsync('expo', ['prebuild', '--no-install'], {\n cwd: exampleAppPath,\n stdio: ['ignore', 'ignore', 'pipe'],\n });\n } catch (error: any) {\n console.error(error.stderr);\n process.exit(1);\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 try {\n await spawnAsync('pod', ['install'], {\n cwd: path.join(appPath, 'ios'),\n stdio: ['ignore', 'ignore', 'pipe'],\n });\n } catch (error: any) {\n console.error(error.stderr);\n process.exit(1);\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"]}
|
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-expo-module",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.1",
|
|
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",
|
|
@@ -39,12 +39,13 @@
|
|
|
39
39
|
"download-tarball": "^2.0.0",
|
|
40
40
|
"ejs": "^3.1.7",
|
|
41
41
|
"fs-extra": "^10.0.0",
|
|
42
|
-
"prompts": "^2.4.2"
|
|
42
|
+
"prompts": "^2.4.2",
|
|
43
|
+
"validate-npm-package-name": "^4.0.0"
|
|
43
44
|
},
|
|
44
45
|
"devDependencies": {
|
|
45
46
|
"@types/ejs": "^3.1.0",
|
|
46
47
|
"@types/prompts": "^2.0.14",
|
|
47
48
|
"expo-module-scripts": "^2.0.0"
|
|
48
49
|
},
|
|
49
|
-
"gitHead": "
|
|
50
|
+
"gitHead": "093d340c3fd67ff4375af3a471c7d044aee893c9"
|
|
50
51
|
}
|
|
@@ -5,9 +5,13 @@ import downloadTarball from 'download-tarball';
|
|
|
5
5
|
import ejs from 'ejs';
|
|
6
6
|
import fs from 'fs-extra';
|
|
7
7
|
import path from 'path';
|
|
8
|
-
import prompts
|
|
8
|
+
import prompts from 'prompts';
|
|
9
|
+
import validateNpmPackage from 'validate-npm-package-name';
|
|
9
10
|
|
|
11
|
+
import { createExampleApp } from './createExampleApp';
|
|
12
|
+
import { installDependencies } from './packageManager';
|
|
10
13
|
import { PackageManagerName, resolvePackageManager } from './resolvePackageManager';
|
|
14
|
+
import { CommandOptions, CustomPromptObject, SubstitutionData } from './types';
|
|
11
15
|
|
|
12
16
|
const packageJson = require('../package.json');
|
|
13
17
|
|
|
@@ -18,43 +22,6 @@ const CWD = process.env.INIT_CWD || process.cwd();
|
|
|
18
22
|
// from `$package.json` file instead of the original one.
|
|
19
23
|
const IGNORES_PATHS = ['.DS_Store', 'build', 'node_modules', 'package.json'];
|
|
20
24
|
|
|
21
|
-
/**
|
|
22
|
-
* Possible command options.
|
|
23
|
-
*/
|
|
24
|
-
type CommandOptions = {
|
|
25
|
-
target: string;
|
|
26
|
-
source?: string;
|
|
27
|
-
name?: string;
|
|
28
|
-
description?: string;
|
|
29
|
-
package?: string;
|
|
30
|
-
author?: string;
|
|
31
|
-
license?: string;
|
|
32
|
-
repo?: string;
|
|
33
|
-
withReadme: boolean;
|
|
34
|
-
withChangelog: boolean;
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Represents an object that is passed to `ejs` when rendering the template.
|
|
39
|
-
*/
|
|
40
|
-
type SubstitutionData = {
|
|
41
|
-
project: {
|
|
42
|
-
slug: string;
|
|
43
|
-
name: string;
|
|
44
|
-
version: string;
|
|
45
|
-
description: string;
|
|
46
|
-
package: string;
|
|
47
|
-
};
|
|
48
|
-
author: string;
|
|
49
|
-
license: string;
|
|
50
|
-
repo: string;
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
type CustomPromptObject = PromptObject & {
|
|
54
|
-
name: string;
|
|
55
|
-
resolvedValue?: string | null;
|
|
56
|
-
};
|
|
57
|
-
|
|
58
25
|
/**
|
|
59
26
|
* The main function of the command.
|
|
60
27
|
*
|
|
@@ -64,10 +31,10 @@ type CustomPromptObject = PromptObject & {
|
|
|
64
31
|
async function main(target: string | undefined, options: CommandOptions) {
|
|
65
32
|
const targetDir = target ? path.join(CWD, target) : CWD;
|
|
66
33
|
|
|
34
|
+
await fs.ensureDir(targetDir);
|
|
67
35
|
await confirmTargetDirAsync(targetDir);
|
|
68
36
|
|
|
69
37
|
options.target = targetDir;
|
|
70
|
-
await fs.ensureDir(targetDir);
|
|
71
38
|
|
|
72
39
|
const data = await askForSubstitutionDataAsync(targetDir, options);
|
|
73
40
|
const packageManager = await resolvePackageManager();
|
|
@@ -93,6 +60,9 @@ async function main(target: string | undefined, options: CommandOptions) {
|
|
|
93
60
|
await fs.outputFile(toPath, renderedContent, { encoding: 'utf8' });
|
|
94
61
|
}
|
|
95
62
|
|
|
63
|
+
// Install dependencies and build
|
|
64
|
+
await postActionsAsync(packageManager, targetDir);
|
|
65
|
+
|
|
96
66
|
if (!options.source) {
|
|
97
67
|
// Files in the downloaded tarball are wrapped in `package` dir.
|
|
98
68
|
// We should remove it after all.
|
|
@@ -104,9 +74,10 @@ async function main(target: string | undefined, options: CommandOptions) {
|
|
|
104
74
|
if (!options.withChangelog) {
|
|
105
75
|
await fs.remove(path.join(targetDir, 'CHANGELOG.md'));
|
|
106
76
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
77
|
+
if (options.example) {
|
|
78
|
+
// Create "example" folder
|
|
79
|
+
await createExampleApp(data, targetDir, packageManager);
|
|
80
|
+
}
|
|
110
81
|
|
|
111
82
|
console.log('✅ Successfully created Expo module');
|
|
112
83
|
}
|
|
@@ -176,18 +147,14 @@ async function downloadPackageAsync(targetDir: string): Promise<string> {
|
|
|
176
147
|
* Installs dependencies and builds TypeScript files.
|
|
177
148
|
*/
|
|
178
149
|
async function postActionsAsync(packageManager: PackageManagerName, targetDir: string) {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
cwd: targetDir,
|
|
182
|
-
stdio: 'ignore',
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
console.log('📦 Installing dependencies...');
|
|
187
|
-
await run('install');
|
|
150
|
+
console.log('📦 Installing module dependencies...');
|
|
151
|
+
await installDependencies(packageManager, targetDir);
|
|
188
152
|
|
|
189
153
|
console.log('🛠 Compiling TypeScript files...');
|
|
190
|
-
await
|
|
154
|
+
await spawnAsync(packageManager, ['run', 'build'], {
|
|
155
|
+
cwd: targetDir,
|
|
156
|
+
stdio: 'ignore',
|
|
157
|
+
});
|
|
191
158
|
}
|
|
192
159
|
|
|
193
160
|
/**
|
|
@@ -199,6 +166,7 @@ async function askForSubstitutionDataAsync(
|
|
|
199
166
|
options: CommandOptions
|
|
200
167
|
): Promise<SubstitutionData> {
|
|
201
168
|
const defaultPackageSlug = path.basename(targetDir);
|
|
169
|
+
const useDefaultSlug = options.target && validateNpmPackage(defaultPackageSlug);
|
|
202
170
|
const defaultProjectName = defaultPackageSlug
|
|
203
171
|
.replace(/^./, (match) => match.toUpperCase())
|
|
204
172
|
.replace(/\W+(\w)/g, (_, p1) => p1.toUpperCase());
|
|
@@ -209,7 +177,9 @@ async function askForSubstitutionDataAsync(
|
|
|
209
177
|
name: 'slug',
|
|
210
178
|
message: 'What is the package slug?',
|
|
211
179
|
initial: defaultPackageSlug,
|
|
212
|
-
resolvedValue:
|
|
180
|
+
resolvedValue: useDefaultSlug ? defaultPackageSlug : null,
|
|
181
|
+
validate: (input) =>
|
|
182
|
+
validateNpmPackage(input).validForNewPackages || 'Must be a valid npm package name',
|
|
213
183
|
},
|
|
214
184
|
{
|
|
215
185
|
type: 'text',
|
|
@@ -221,6 +191,7 @@ async function askForSubstitutionDataAsync(
|
|
|
221
191
|
type: 'text',
|
|
222
192
|
name: 'description',
|
|
223
193
|
message: 'How would you describe the module?',
|
|
194
|
+
validate: (input) => !!input || 'Cannot be empty',
|
|
224
195
|
},
|
|
225
196
|
{
|
|
226
197
|
type: 'text',
|
|
@@ -244,6 +215,7 @@ async function askForSubstitutionDataAsync(
|
|
|
244
215
|
type: 'text',
|
|
245
216
|
name: 'repo',
|
|
246
217
|
message: 'What is the repository URL?',
|
|
218
|
+
validate: (input) => /^https?:\/\//.test(input) || 'Must be a valid URL',
|
|
247
219
|
},
|
|
248
220
|
];
|
|
249
221
|
|
|
@@ -320,6 +292,7 @@ program
|
|
|
320
292
|
.option('-r, --repo <repo_url>', 'The URL to the repository.')
|
|
321
293
|
.option('--with-readme', 'Whether to include README.md file.', false)
|
|
322
294
|
.option('--with-changelog', 'Whether to include CHANGELOG.md file.', false)
|
|
295
|
+
.option('--no-example', 'Whether to skip creating the example app.', false)
|
|
323
296
|
.action(main);
|
|
324
297
|
|
|
325
298
|
program.parse(process.argv);
|
|
@@ -0,0 +1,157 @@
|
|
|
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
|
+
|
|
9
|
+
// These dependencies will be removed from the example app (`expo init` adds them)
|
|
10
|
+
const DEPENDENCIES_TO_REMOVE = ['expo-status-bar', 'expo-splash-screen'];
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Initializes a new Expo project as an example app.
|
|
14
|
+
*/
|
|
15
|
+
export async function createExampleApp(
|
|
16
|
+
data: SubstitutionData,
|
|
17
|
+
targetDir: string,
|
|
18
|
+
packageManager: PackageManagerName
|
|
19
|
+
): Promise<void> {
|
|
20
|
+
console.log('🎭 Creating the example app...');
|
|
21
|
+
|
|
22
|
+
const exampleProjectSlug = `${data.project.slug}-example`;
|
|
23
|
+
|
|
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
|
+
|
|
33
|
+
// `expo init` creates a new folder with the same name as the project slug
|
|
34
|
+
const appTmpPath = path.join(targetDir, exampleProjectSlug);
|
|
35
|
+
|
|
36
|
+
// Path to the target example dir
|
|
37
|
+
const appTargetPath = path.join(targetDir, 'example');
|
|
38
|
+
|
|
39
|
+
console.log('🛠 Configuring the example app...');
|
|
40
|
+
|
|
41
|
+
// "example" folder already exists and contains template files,
|
|
42
|
+
// that should replace these created by `expo init`.
|
|
43
|
+
await moveFiles(appTargetPath, appTmpPath);
|
|
44
|
+
|
|
45
|
+
// Cleanup the "example" dir
|
|
46
|
+
await fs.rmdir(appTargetPath);
|
|
47
|
+
|
|
48
|
+
// Move the temporary example app to "example" dir
|
|
49
|
+
await fs.rename(appTmpPath, appTargetPath);
|
|
50
|
+
|
|
51
|
+
await addMissingAppConfigFields(appTargetPath, data);
|
|
52
|
+
|
|
53
|
+
console.log('👷 Prebuilding the example app...');
|
|
54
|
+
await prebuildExampleApp(appTargetPath);
|
|
55
|
+
|
|
56
|
+
await modifyPackageJson(appTargetPath);
|
|
57
|
+
|
|
58
|
+
console.log('📦 Installing dependencies in the example app...');
|
|
59
|
+
await installDependencies(packageManager, appTargetPath);
|
|
60
|
+
|
|
61
|
+
console.log('🥥 Installing iOS pods in the example app...');
|
|
62
|
+
await podInstall(appTargetPath);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Copies files from one directory to another.
|
|
67
|
+
*/
|
|
68
|
+
async function moveFiles(fromPath: string, toPath: string): Promise<void> {
|
|
69
|
+
for (const file of await fs.readdir(fromPath)) {
|
|
70
|
+
await fs.move(path.join(fromPath, file), path.join(toPath, file), {
|
|
71
|
+
overwrite: true,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Adds missing configuration that are required to run `expo prebuild`.
|
|
78
|
+
*/
|
|
79
|
+
async function addMissingAppConfigFields(appPath: string, data: SubstitutionData): Promise<void> {
|
|
80
|
+
const appConfigPath = path.join(appPath, 'app.json');
|
|
81
|
+
const appConfig = await fs.readJson(appConfigPath);
|
|
82
|
+
const appId = `${data.project.package}.example`;
|
|
83
|
+
|
|
84
|
+
// Android package name needs to be added to app.json
|
|
85
|
+
if (!appConfig.expo.android) {
|
|
86
|
+
appConfig.expo.android = {};
|
|
87
|
+
}
|
|
88
|
+
appConfig.expo.android.package = appId;
|
|
89
|
+
|
|
90
|
+
// Specify iOS bundle identifier
|
|
91
|
+
if (!appConfig.expo.ios) {
|
|
92
|
+
appConfig.expo.ios = {};
|
|
93
|
+
}
|
|
94
|
+
appConfig.expo.ios.bundleIdentifier = appId;
|
|
95
|
+
|
|
96
|
+
await fs.writeJson(appConfigPath, appConfig, {
|
|
97
|
+
spaces: 2,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Applies necessary changes to **package.json** of the example app.
|
|
103
|
+
* It means setting the autolinking config and removing unnecessary dependencies.
|
|
104
|
+
*/
|
|
105
|
+
async function modifyPackageJson(appPath: string): Promise<void> {
|
|
106
|
+
const packageJsonPath = path.join(appPath, 'package.json');
|
|
107
|
+
const packageJson = await fs.readJson(packageJsonPath);
|
|
108
|
+
|
|
109
|
+
if (!packageJson.expo) {
|
|
110
|
+
packageJson.expo = {};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Set the native modules dir to the root folder,
|
|
114
|
+
// so that the autolinking can detect and link the module.
|
|
115
|
+
packageJson.expo.autolinking = {
|
|
116
|
+
nativeModulesDir: '..',
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
// Remove unnecessary dependencies
|
|
120
|
+
for (const dependencyToRemove of DEPENDENCIES_TO_REMOVE) {
|
|
121
|
+
delete packageJson.dependencies[dependencyToRemove];
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
await fs.writeJson(packageJsonPath, packageJson, {
|
|
125
|
+
spaces: 2,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Runs `expo prebuild` in the example app.
|
|
131
|
+
*/
|
|
132
|
+
async function prebuildExampleApp(exampleAppPath: string): Promise<void> {
|
|
133
|
+
try {
|
|
134
|
+
await spawnAsync('expo', ['prebuild', '--no-install'], {
|
|
135
|
+
cwd: exampleAppPath,
|
|
136
|
+
stdio: ['ignore', 'ignore', 'pipe'],
|
|
137
|
+
});
|
|
138
|
+
} catch (error: any) {
|
|
139
|
+
console.error(error.stderr);
|
|
140
|
+
process.exit(1);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Runs `pod install` in the iOS project at the given path.
|
|
146
|
+
*/
|
|
147
|
+
async function podInstall(appPath: string): Promise<void> {
|
|
148
|
+
try {
|
|
149
|
+
await spawnAsync('pod', ['install'], {
|
|
150
|
+
cwd: path.join(appPath, 'ios'),
|
|
151
|
+
stdio: ['ignore', 'ignore', 'pipe'],
|
|
152
|
+
});
|
|
153
|
+
} catch (error: any) {
|
|
154
|
+
console.error(error.stderr);
|
|
155
|
+
process.exit(1);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
@@ -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
|
+
}
|
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
|
+
};
|