create-expo-module 0.5.13 → 0.6.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.
@@ -8,6 +8,7 @@ const chalk_1 = __importDefault(require("chalk"));
8
8
  const commander_1 = require("commander");
9
9
  const download_tarball_1 = __importDefault(require("download-tarball"));
10
10
  const ejs_1 = __importDefault(require("ejs"));
11
+ const find_up_1 = __importDefault(require("find-up"));
11
12
  const fs_extra_1 = __importDefault(require("fs-extra"));
12
13
  const getenv_1 = require("getenv");
13
14
  const path_1 = __importDefault(require("path"));
@@ -36,6 +37,16 @@ const IGNORES_PATHS = [
36
37
  ];
37
38
  // Url to the documentation on Expo Modules
38
39
  const DOCS_URL = 'https://docs.expo.dev/modules';
40
+ const FYI_LOCAL_DIR = 'https://expo.fyi/expo-module-local-autolinking.md';
41
+ async function getCorrectLocalDirectory(targetOrSlug) {
42
+ const packageJsonPath = await (0, find_up_1.default)('package.json', { cwd: CWD });
43
+ if (!packageJsonPath) {
44
+ console.log(chalk_1.default.red.bold('⚠️ This command should be run inside your Expo project when run with the --local flag.'));
45
+ console.log(chalk_1.default.red('For native modules to autolink correctly, you need to place them in the `modules` directory in the root of the project.'));
46
+ return null;
47
+ }
48
+ return path_1.default.join(packageJsonPath, '..', 'modules', targetOrSlug);
49
+ }
39
50
  /**
40
51
  * The main function of the command.
41
52
  *
@@ -43,69 +54,89 @@ const DOCS_URL = 'https://docs.expo.dev/modules';
43
54
  * @param command An object from `commander`.
44
55
  */
45
56
  async function main(target, options) {
46
- const slug = await askForPackageSlugAsync(target);
47
- const targetDir = path_1.default.join(CWD, target || slug);
57
+ if (options.local) {
58
+ console.log();
59
+ console.log(`${chalk_1.default.gray('The local module will be created in the ')}${chalk_1.default.gray.bold.italic('modules')} ${chalk_1.default.gray('directory in the root of your project. Learn more: ')}${chalk_1.default.gray.bold(FYI_LOCAL_DIR)}`);
60
+ console.log();
61
+ }
62
+ const slug = await askForPackageSlugAsync(target, options.local);
63
+ const targetDir = options.local
64
+ ? await getCorrectLocalDirectory(target || slug)
65
+ : path_1.default.join(CWD, target || slug);
66
+ if (!targetDir) {
67
+ return;
68
+ }
48
69
  await fs_extra_1.default.ensureDir(targetDir);
49
70
  await confirmTargetDirAsync(targetDir);
50
71
  options.target = targetDir;
51
- const data = await askForSubstitutionDataAsync(slug);
72
+ const data = await askForSubstitutionDataAsync(slug, options.local);
52
73
  // Make one line break between prompts and progress logs
53
74
  console.log();
54
75
  const packageManager = await (0, resolvePackageManager_1.resolvePackageManager)();
55
76
  const packagePath = options.source
56
77
  ? path_1.default.join(CWD, options.source)
57
- : await downloadPackageAsync(targetDir);
78
+ : await downloadPackageAsync(targetDir, options.local);
58
79
  (0, telemetry_1.logEventAsync)((0, telemetry_1.eventCreateExpoModule)(packageManager, options));
59
80
  await (0, utils_1.newStep)('Creating the module from template files', async (step) => {
60
81
  await createModuleFromTemplate(packagePath, targetDir, data);
61
82
  step.succeed('Created the module from template files');
62
83
  });
63
- await (0, utils_1.newStep)('Installing module dependencies', async (step) => {
64
- await (0, packageManager_1.installDependencies)(packageManager, targetDir);
65
- step.succeed('Installed module dependencies');
66
- });
67
- await (0, utils_1.newStep)('Compiling TypeScript files', async (step) => {
68
- await (0, spawn_async_1.default)(packageManager, ['run', 'build'], {
69
- cwd: targetDir,
70
- stdio: 'ignore',
84
+ if (!options.local) {
85
+ await (0, utils_1.newStep)('Installing module dependencies', async (step) => {
86
+ await (0, packageManager_1.installDependencies)(packageManager, targetDir);
87
+ step.succeed('Installed module dependencies');
71
88
  });
72
- step.succeed('Compiled TypeScript files');
73
- });
89
+ await (0, utils_1.newStep)('Compiling TypeScript files', async (step) => {
90
+ await (0, spawn_async_1.default)(packageManager, ['run', 'build'], {
91
+ cwd: targetDir,
92
+ stdio: 'ignore',
93
+ });
94
+ step.succeed('Compiled TypeScript files');
95
+ });
96
+ }
74
97
  if (!options.source) {
75
98
  // Files in the downloaded tarball are wrapped in `package` dir.
76
99
  // We should remove it after all.
77
100
  await fs_extra_1.default.remove(packagePath);
78
101
  }
79
- if (!options.withReadme) {
80
- await fs_extra_1.default.remove(path_1.default.join(targetDir, 'README.md'));
81
- }
82
- if (!options.withChangelog) {
83
- await fs_extra_1.default.remove(path_1.default.join(targetDir, 'CHANGELOG.md'));
84
- }
85
- if (options.example) {
86
- // Create "example" folder
87
- await (0, createExampleApp_1.createExampleApp)(data, targetDir, packageManager);
88
- }
89
- await (0, utils_1.newStep)('Creating an empty Git repository', async (step) => {
90
- try {
91
- const result = await createGitRepositoryAsync(targetDir);
92
- if (result) {
93
- step.succeed('Created an empty Git repository');
94
- }
95
- else if (result === null) {
96
- step.succeed('Skipped creating an empty Git repository, already within a Git repository');
97
- }
98
- else if (result === false) {
99
- step.warn('Could not create an empty Git repository, see debug logs with EXPO_DEBUG=true');
100
- }
102
+ if (!options.local && data.type !== 'local') {
103
+ if (!options.withReadme) {
104
+ await fs_extra_1.default.remove(path_1.default.join(targetDir, 'README.md'));
101
105
  }
102
- catch (e) {
103
- step.fail(e.toString());
106
+ if (!options.withChangelog) {
107
+ await fs_extra_1.default.remove(path_1.default.join(targetDir, 'CHANGELOG.md'));
104
108
  }
105
- });
109
+ if (options.example) {
110
+ // Create "example" folder
111
+ await (0, createExampleApp_1.createExampleApp)(data, targetDir, packageManager);
112
+ }
113
+ await (0, utils_1.newStep)('Creating an empty Git repository', async (step) => {
114
+ try {
115
+ const result = await createGitRepositoryAsync(targetDir);
116
+ if (result) {
117
+ step.succeed('Created an empty Git repository');
118
+ }
119
+ else if (result === null) {
120
+ step.succeed('Skipped creating an empty Git repository, already within a Git repository');
121
+ }
122
+ else if (result === false) {
123
+ step.warn('Could not create an empty Git repository, see debug logs with EXPO_DEBUG=true');
124
+ }
125
+ }
126
+ catch (e) {
127
+ step.fail(e.toString());
128
+ }
129
+ });
130
+ }
106
131
  console.log();
107
- console.log('✅ Successfully created Expo module');
108
- printFurtherInstructions(targetDir, packageManager, options.example);
132
+ if (options.local) {
133
+ console.log(`✅ Successfully created Expo module in ${chalk_1.default.bold.italic(`modules/${slug}`)}`);
134
+ printFurtherLocalInstructions(slug, data.project.moduleName);
135
+ }
136
+ else {
137
+ console.log('✅ Successfully created Expo module');
138
+ printFurtherInstructions(targetDir, packageManager, options.example);
139
+ }
109
140
  }
110
141
  /**
111
142
  * Recursively scans for the files within the directory. Returned paths are relative to the `root` path.
@@ -140,9 +171,9 @@ async function getNpmTarballUrl(packageName, version = 'latest') {
140
171
  /**
141
172
  * Downloads the template from NPM registry.
142
173
  */
143
- async function downloadPackageAsync(targetDir) {
174
+ async function downloadPackageAsync(targetDir, isLocal = false) {
144
175
  return await (0, utils_1.newStep)('Downloading module template from npm', async (step) => {
145
- const tarballUrl = await getNpmTarballUrl('expo-module-template', EXPO_BETA ? 'next' : 'latest');
176
+ const tarballUrl = await getNpmTarballUrl(isLocal ? 'expo-module-template-local' : 'expo-module-template', EXPO_BETA ? 'next' : 'latest');
146
177
  await (0, download_tarball_1.default)({
147
178
  url: tarballUrl,
148
179
  dir: targetDir,
@@ -206,8 +237,8 @@ async function createGitRepositoryAsync(targetDir) {
206
237
  /**
207
238
  * Asks the user for the package slug (npm package name).
208
239
  */
209
- async function askForPackageSlugAsync(customTargetPath) {
210
- const { slug } = await (0, prompts_1.default)((0, prompts_2.getSlugPrompt)(customTargetPath), {
240
+ async function askForPackageSlugAsync(customTargetPath, isLocal = false) {
241
+ const { slug } = await (0, prompts_1.default)((isLocal ? prompts_2.getLocalFolderNamePrompt : prompts_2.getSlugPrompt)(customTargetPath), {
211
242
  onCancel: () => process.exit(0),
212
243
  });
213
244
  return slug;
@@ -216,13 +247,27 @@ async function askForPackageSlugAsync(customTargetPath) {
216
247
  * Asks the user for some data necessary to render the template.
217
248
  * Some values may already be provided by command options, the prompt is skipped in that case.
218
249
  */
219
- async function askForSubstitutionDataAsync(slug) {
220
- const promptQueries = await (0, prompts_2.getSubstitutionDataPrompts)(slug);
250
+ async function askForSubstitutionDataAsync(slug, isLocal = false) {
251
+ const promptQueries = await (isLocal
252
+ ? prompts_2.getLocalSubstitutionDataPrompts
253
+ : prompts_2.getSubstitutionDataPrompts)(slug);
221
254
  // Stop the process when the user cancels/exits the prompt.
222
255
  const onCancel = () => {
223
256
  process.exit(0);
224
257
  };
225
258
  const { name, description, package: projectPackage, authorName, authorEmail, authorUrl, repo, } = await (0, prompts_1.default)(promptQueries, { onCancel });
259
+ if (isLocal) {
260
+ return {
261
+ project: {
262
+ slug,
263
+ name,
264
+ package: projectPackage,
265
+ moduleName: handleSuffix(name, 'Module'),
266
+ viewName: handleSuffix(name, 'View'),
267
+ },
268
+ type: 'local',
269
+ };
270
+ }
226
271
  return {
227
272
  project: {
228
273
  slug,
@@ -236,6 +281,7 @@ async function askForSubstitutionDataAsync(slug) {
236
281
  author: `${authorName} <${authorEmail}> (${authorUrl})`,
237
282
  license: 'MIT',
238
283
  repo,
284
+ type: 'remote',
239
285
  };
240
286
  }
241
287
  /**
@@ -273,7 +319,16 @@ function printFurtherInstructions(targetDir, packageManager, includesExample) {
273
319
  commands.forEach((command) => console.log(chalk_1.default.gray('>'), chalk_1.default.bold(command)));
274
320
  console.log();
275
321
  }
276
- console.log(`Visit ${chalk_1.default.blue.bold(DOCS_URL)} for the documentation on Expo Modules APIs`);
322
+ console.log(`Learn more on Expo Modules APIs: ${chalk_1.default.blue.bold(DOCS_URL)}`);
323
+ }
324
+ function printFurtherLocalInstructions(slug, name) {
325
+ console.log();
326
+ console.log(`You can now import this module inside your application.`);
327
+ console.log(`For example, you can add this line to your App.js or App.tsx file:`);
328
+ console.log(`${chalk_1.default.gray.italic(`import { hello } from './modules/${slug}';`)}`);
329
+ console.log();
330
+ console.log(`Learn more on Expo Modules APIs: ${chalk_1.default.blue.bold(DOCS_URL)}`);
331
+ console.log(chalk_1.default.yellow(`Remember you need to rebuild your development client or reinstall pods to see the changes.`));
277
332
  }
278
333
  const program = new commander_1.Command();
279
334
  program
@@ -285,6 +340,7 @@ program
285
340
  .option('--with-readme', 'Whether to include README.md file.', false)
286
341
  .option('--with-changelog', 'Whether to include CHANGELOG.md file.', false)
287
342
  .option('--no-example', 'Whether to skip creating the example app.', false)
343
+ .option('--local', 'Whether to create a local module in the current project, skipping installing node_modules and creating the example directory.', false)
288
344
  .action(main);
289
345
  program
290
346
  .hook('postAction', async () => {
@@ -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 { boolish } from 'getenv';\nimport path from 'path';\nimport prompts from 'prompts';\n\nimport { createExampleApp } from './createExampleApp';\nimport { installDependencies } from './packageManager';\nimport { getSlugPrompt, getSubstitutionDataPrompts } from './prompts';\nimport {\n formatRunCommand,\n PackageManagerName,\n resolvePackageManager,\n} from './resolvePackageManager';\nimport { eventCreateExpoModule, getTelemetryClient, logEventAsync } from './telemetry';\nimport { CommandOptions, SubstitutionData } from './types';\nimport { newStep } from './utils';\n\nconst debug = require('debug')('create-expo-module:main') as typeof console.log;\nconst packageJson = require('../package.json');\n\n// Opt in to using beta versions\nconst EXPO_BETA = boolish('EXPO_BETA', false);\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 = [\n '.DS_Store',\n 'build',\n 'node_modules',\n 'package.json',\n '.npmignore',\n '.gitignore',\n];\n\n// Url to the documentation on Expo Modules\nconst DOCS_URL = 'https://docs.expo.dev/modules';\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 slug = await askForPackageSlugAsync(target);\n const targetDir = path.join(CWD, target || slug);\n\n await fs.ensureDir(targetDir);\n await confirmTargetDirAsync(targetDir);\n\n options.target = targetDir;\n\n const data = await askForSubstitutionDataAsync(slug);\n\n // Make one line break between prompts and progress logs\n console.log();\n\n const packageManager = await resolvePackageManager();\n const packagePath = options.source\n ? path.join(CWD, options.source)\n : await downloadPackageAsync(targetDir);\n\n logEventAsync(eventCreateExpoModule(packageManager, options));\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 await newStep('Creating an empty Git repository', async (step) => {\n try {\n const result = await createGitRepositoryAsync(targetDir);\n if (result) {\n step.succeed('Created an empty Git repository');\n } else if (result === null) {\n step.succeed('Skipped creating an empty Git repository, already within a Git repository');\n } else if (result === false) {\n step.warn('Could not create an empty Git repository, see debug logs with EXPO_DEBUG=true');\n }\n } catch (e: any) {\n step.fail(e.toString());\n }\n });\n\n console.log();\n console.log('✅ Successfully created Expo module');\n\n printFurtherInstructions(targetDir, packageManager, options.example);\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 debug(`Using module template ${chalk.bold(packageName)}@${chalk.bold(version)}`);\n const { stdout } = await spawnAsync('npm', ['view', `${packageName}@${version}`, 'dist.tarball']);\n return stdout.trim();\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(\n 'expo-module-template',\n EXPO_BETA ? 'next' : 'latest'\n );\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\nfunction handleSuffix(name: string, suffix: string): string {\n if (name.endsWith(suffix)) {\n return name;\n }\n return `${name}${suffix}`;\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(/\\./g, 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\nasync function createGitRepositoryAsync(targetDir: string) {\n // Check if we are inside a git repository already\n try {\n await spawnAsync('git', ['rev-parse', '--is-inside-work-tree'], {\n stdio: 'ignore',\n cwd: targetDir,\n });\n debug(chalk.dim('New project is already inside of a Git repo, skipping git init.'));\n return null;\n } catch (e: any) {\n if (e.errno === 'ENOENT') {\n debug(chalk.dim('Unable to initialize Git repo. `git` not in $PATH.'));\n return false;\n }\n }\n\n // Create a new git repository\n await spawnAsync('git', ['init'], { stdio: 'ignore', cwd: targetDir });\n await spawnAsync('git', ['add', '-A'], { stdio: 'ignore', cwd: targetDir });\n\n const commitMsg = `Initial commit\\n\\nGenerated by ${packageJson.name} ${packageJson.version}.`;\n await spawnAsync('git', ['commit', '-m', commitMsg], {\n stdio: 'ignore',\n cwd: targetDir,\n });\n\n debug(chalk.dim('Initialized a Git repository.'));\n return true;\n}\n\n/**\n * Asks the user for the package slug (npm package name).\n */\nasync function askForPackageSlugAsync(customTargetPath?: string): Promise<string> {\n const { slug } = await prompts(getSlugPrompt(customTargetPath), {\n onCancel: () => process.exit(0),\n });\n return slug;\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(slug: string): Promise<SubstitutionData> {\n const promptQueries = await getSubstitutionDataPrompts(slug);\n\n // Stop the process when the user cancels/exits the prompt.\n const onCancel = () => {\n process.exit(0);\n };\n\n const {\n name,\n description,\n package: projectPackage,\n authorName,\n authorEmail,\n authorUrl,\n repo,\n } = await prompts(promptQueries, { onCancel });\n\n return {\n project: {\n slug,\n name,\n version: '0.1.0',\n description,\n package: projectPackage,\n moduleName: handleSuffix(name, 'Module'),\n viewName: handleSuffix(name, 'View'),\n },\n author: `${authorName} <${authorEmail}> (${authorUrl})`,\n license: 'MIT',\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, do 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\n/**\n * Prints how the user can follow up once the script finishes creating the module.\n */\nfunction printFurtherInstructions(\n targetDir: string,\n packageManager: PackageManagerName,\n includesExample: boolean\n) {\n if (includesExample) {\n const commands = [\n `cd ${path.relative(CWD, targetDir)}`,\n formatRunCommand(packageManager, 'open:ios'),\n formatRunCommand(packageManager, 'open:android'),\n ];\n\n console.log();\n console.log(\n 'To start developing your module, navigate to the directory and open iOS and Android projects of the example app'\n );\n commands.forEach((command) => console.log(chalk.gray('>'), chalk.bold(command)));\n console.log();\n }\n console.log(`Visit ${chalk.blue.bold(DOCS_URL)} for the documentation on Expo Modules APIs`);\n}\n\nconst program = new Command();\n\nprogram\n .name(packageJson.name)\n .version(packageJson.version)\n .description(packageJson.description)\n .arguments('[path]')\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('--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\n .hook('postAction', async () => {\n await getTelemetryClient().flush?.();\n })\n .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 findUp from 'find-up';\nimport fs from 'fs-extra';\nimport { boolish } from 'getenv';\nimport path from 'path';\nimport prompts from 'prompts';\n\nimport { createExampleApp } from './createExampleApp';\nimport { installDependencies } from './packageManager';\nimport {\n getLocalFolderNamePrompt,\n getLocalSubstitutionDataPrompts,\n getSlugPrompt,\n getSubstitutionDataPrompts,\n} from './prompts';\nimport {\n formatRunCommand,\n PackageManagerName,\n resolvePackageManager,\n} from './resolvePackageManager';\nimport { eventCreateExpoModule, getTelemetryClient, logEventAsync } from './telemetry';\nimport { CommandOptions, LocalSubstitutionData, SubstitutionData } from './types';\nimport { newStep } from './utils';\n\nconst debug = require('debug')('create-expo-module:main') as typeof console.log;\nconst packageJson = require('../package.json');\n\n// Opt in to using beta versions\nconst EXPO_BETA = boolish('EXPO_BETA', false);\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 = [\n '.DS_Store',\n 'build',\n 'node_modules',\n 'package.json',\n '.npmignore',\n '.gitignore',\n];\n\n// Url to the documentation on Expo Modules\nconst DOCS_URL = 'https://docs.expo.dev/modules';\n\nconst FYI_LOCAL_DIR = 'https://expo.fyi/expo-module-local-autolinking.md';\n\nasync function getCorrectLocalDirectory(targetOrSlug: string) {\n const packageJsonPath = await findUp('package.json', { cwd: CWD });\n if (!packageJsonPath) {\n console.log(\n chalk.red.bold(\n '⚠️ This command should be run inside your Expo project when run with the --local flag.'\n )\n );\n console.log(\n chalk.red(\n 'For native modules to autolink correctly, you need to place them in the `modules` directory in the root of the project.'\n )\n );\n return null;\n }\n return path.join(packageJsonPath, '..', 'modules', targetOrSlug);\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 if (options.local) {\n console.log();\n console.log(\n `${chalk.gray('The local module will be created in the ')}${chalk.gray.bold.italic(\n 'modules'\n )} ${chalk.gray('directory in the root of your project. Learn more: ')}${chalk.gray.bold(\n FYI_LOCAL_DIR\n )}`\n );\n console.log();\n }\n const slug = await askForPackageSlugAsync(target, options.local);\n const targetDir = options.local\n ? await getCorrectLocalDirectory(target || slug)\n : path.join(CWD, target || slug);\n\n if (!targetDir) {\n return;\n }\n await fs.ensureDir(targetDir);\n await confirmTargetDirAsync(targetDir);\n\n options.target = targetDir;\n\n const data = await askForSubstitutionDataAsync(slug, options.local);\n\n // Make one line break between prompts and progress logs\n console.log();\n\n const packageManager = await resolvePackageManager();\n const packagePath = options.source\n ? path.join(CWD, options.source)\n : await downloadPackageAsync(targetDir, options.local);\n\n logEventAsync(eventCreateExpoModule(packageManager, options));\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 if (!options.local) {\n await newStep('Installing module dependencies', async (step) => {\n await installDependencies(packageManager, targetDir);\n step.succeed('Installed module dependencies');\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\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.local && data.type !== 'local') {\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 await newStep('Creating an empty Git repository', async (step) => {\n try {\n const result = await createGitRepositoryAsync(targetDir);\n if (result) {\n step.succeed('Created an empty Git repository');\n } else if (result === null) {\n step.succeed('Skipped creating an empty Git repository, already within a Git repository');\n } else if (result === false) {\n step.warn(\n 'Could not create an empty Git repository, see debug logs with EXPO_DEBUG=true'\n );\n }\n } catch (e: any) {\n step.fail(e.toString());\n }\n });\n }\n\n console.log();\n if (options.local) {\n console.log(`✅ Successfully created Expo module in ${chalk.bold.italic(`modules/${slug}`)}`);\n printFurtherLocalInstructions(slug, data.project.moduleName);\n } else {\n console.log('✅ Successfully created Expo module');\n printFurtherInstructions(targetDir, packageManager, options.example);\n }\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 debug(`Using module template ${chalk.bold(packageName)}@${chalk.bold(version)}`);\n const { stdout } = await spawnAsync('npm', ['view', `${packageName}@${version}`, 'dist.tarball']);\n return stdout.trim();\n}\n\n/**\n * Downloads the template from NPM registry.\n */\nasync function downloadPackageAsync(targetDir: string, isLocal = false): Promise<string> {\n return await newStep('Downloading module template from npm', async (step) => {\n const tarballUrl = await getNpmTarballUrl(\n isLocal ? 'expo-module-template-local' : 'expo-module-template',\n EXPO_BETA ? 'next' : 'latest'\n );\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\nfunction handleSuffix(name: string, suffix: string): string {\n if (name.endsWith(suffix)) {\n return name;\n }\n return `${name}${suffix}`;\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 | LocalSubstitutionData\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(/\\./g, 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\nasync function createGitRepositoryAsync(targetDir: string) {\n // Check if we are inside a git repository already\n try {\n await spawnAsync('git', ['rev-parse', '--is-inside-work-tree'], {\n stdio: 'ignore',\n cwd: targetDir,\n });\n debug(chalk.dim('New project is already inside of a Git repo, skipping git init.'));\n return null;\n } catch (e: any) {\n if (e.errno === 'ENOENT') {\n debug(chalk.dim('Unable to initialize Git repo. `git` not in $PATH.'));\n return false;\n }\n }\n\n // Create a new git repository\n await spawnAsync('git', ['init'], { stdio: 'ignore', cwd: targetDir });\n await spawnAsync('git', ['add', '-A'], { stdio: 'ignore', cwd: targetDir });\n\n const commitMsg = `Initial commit\\n\\nGenerated by ${packageJson.name} ${packageJson.version}.`;\n await spawnAsync('git', ['commit', '-m', commitMsg], {\n stdio: 'ignore',\n cwd: targetDir,\n });\n\n debug(chalk.dim('Initialized a Git repository.'));\n return true;\n}\n\n/**\n * Asks the user for the package slug (npm package name).\n */\nasync function askForPackageSlugAsync(customTargetPath?: string, isLocal = false): Promise<string> {\n const { slug } = await prompts(\n (isLocal ? getLocalFolderNamePrompt : getSlugPrompt)(customTargetPath),\n {\n onCancel: () => process.exit(0),\n }\n );\n return slug;\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 slug: string,\n isLocal = false\n): Promise<SubstitutionData | LocalSubstitutionData> {\n const promptQueries = await (isLocal\n ? getLocalSubstitutionDataPrompts\n : getSubstitutionDataPrompts)(slug);\n\n // Stop the process when the user cancels/exits the prompt.\n const onCancel = () => {\n process.exit(0);\n };\n\n const {\n name,\n description,\n package: projectPackage,\n authorName,\n authorEmail,\n authorUrl,\n repo,\n } = await prompts(promptQueries, { onCancel });\n\n if (isLocal) {\n return {\n project: {\n slug,\n name,\n package: projectPackage,\n moduleName: handleSuffix(name, 'Module'),\n viewName: handleSuffix(name, 'View'),\n },\n type: 'local',\n };\n }\n\n return {\n project: {\n slug,\n name,\n version: '0.1.0',\n description,\n package: projectPackage,\n moduleName: handleSuffix(name, 'Module'),\n viewName: handleSuffix(name, 'View'),\n },\n author: `${authorName} <${authorEmail}> (${authorUrl})`,\n license: 'MIT',\n repo,\n type: 'remote',\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, do 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\n/**\n * Prints how the user can follow up once the script finishes creating the module.\n */\nfunction printFurtherInstructions(\n targetDir: string,\n packageManager: PackageManagerName,\n includesExample: boolean\n) {\n if (includesExample) {\n const commands = [\n `cd ${path.relative(CWD, targetDir)}`,\n formatRunCommand(packageManager, 'open:ios'),\n formatRunCommand(packageManager, 'open:android'),\n ];\n\n console.log();\n console.log(\n 'To start developing your module, navigate to the directory and open iOS and Android projects of the example app'\n );\n commands.forEach((command) => console.log(chalk.gray('>'), chalk.bold(command)));\n console.log();\n }\n console.log(`Learn more on Expo Modules APIs: ${chalk.blue.bold(DOCS_URL)}`);\n}\n\nfunction printFurtherLocalInstructions(slug: string, name: string) {\n console.log();\n console.log(`You can now import this module inside your application.`);\n console.log(`For example, you can add this line to your App.js or App.tsx file:`);\n console.log(`${chalk.gray.italic(`import { hello } from './modules/${slug}';`)}`);\n console.log();\n console.log(`Learn more on Expo Modules APIs: ${chalk.blue.bold(DOCS_URL)}`);\n console.log(\n chalk.yellow(\n `Remember you need to rebuild your development client or reinstall pods to see the changes.`\n )\n );\n}\n\nconst program = new Command();\n\nprogram\n .name(packageJson.name)\n .version(packageJson.version)\n .description(packageJson.description)\n .arguments('[path]')\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('--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 .option(\n '--local',\n 'Whether to create a local module in the current project, skipping installing node_modules and creating the example directory.',\n false\n )\n .action(main);\n\nprogram\n .hook('postAction', async () => {\n await getTelemetryClient().flush?.();\n })\n .parse(process.argv);\n"]}
@@ -78,7 +78,7 @@ async function moveFiles(fromPath, toPath) {
78
78
  }
79
79
  }
80
80
  /**
81
- * Adds missing configuration that are required to run `expo prebuild`.
81
+ * Adds missing configuration that are required to run `npx expo prebuild`.
82
82
  */
83
83
  async function addMissingAppConfigFields(appPath, data) {
84
84
  const appConfigPath = path_1.default.join(appPath, 'app.json');
@@ -1 +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 getenv from 'getenv';\nimport os from 'os';\nimport path from 'path';\n\nimport { installDependencies } from './packageManager';\nimport { PackageManagerName } from './resolvePackageManager';\nimport { SubstitutionData } from './types';\nimport { newStep } from './utils';\n\nconst debug = require('debug')('create-expo-module:createExampleApp') as typeof console.log;\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'];\nconst EXPO_BETA = getenv.boolish('EXPO_BETA', false);\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 // Package name for the example app\n const exampleProjectSlug = `${data.project.slug}-example`;\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 if (!(await fs.pathExists(appTargetPath))) {\n // The template doesn't include the example app, so just skip this phase\n return;\n }\n\n await newStep('Initializing the example app', async (step) => {\n const templateVersion = EXPO_BETA ? 'next' : 'latest';\n const template = `expo-template-blank-typescript@${templateVersion}`;\n debug(`Using example template: ${template}`);\n await spawnAsync(\n packageManager,\n ['create', 'expo-app', '--', exampleProjectSlug, '--template', template, '--yes'],\n {\n cwd: targetDir,\n stdio: 'ignore',\n }\n );\n step.succeed('Initialized the example app');\n });\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 // Clean up the \".git\" from example app\n // note, this directory has contents, rmdir will throw\n await fs.remove(path.join(appTmpPath, '.git'));\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 if (os.platform() === 'darwin') {\n await podInstall(appTargetPath);\n step.succeed('Installed dependencies in the example app');\n } else {\n step.succeed('Installed dependencies in the example app (skipped installing CocoaPods)');\n }\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 `npx 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('npx', ['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"]}
1
+ {"version":3,"file":"createExampleApp.js","sourceRoot":"","sources":["../src/createExampleApp.ts"],"names":[],"mappings":";;;;;;AAAA,oEAA2C;AAC3C,wDAA0B;AAC1B,oDAA4B;AAC5B,4CAAoB;AACpB,gDAAwB;AAExB,qDAAuD;AAGvD,mCAAkC;AAElC,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,qCAAqC,CAAuB,CAAC;AAE5F,kFAAkF;AAClF,MAAM,sBAAsB,GAAG,CAAC,iBAAiB,EAAE,oBAAoB,CAAC,CAAC;AACzE,MAAM,SAAS,GAAG,gBAAM,CAAC,OAAO,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;AAErD;;GAEG;AACI,KAAK,UAAU,gBAAgB,CACpC,IAAsB,EACtB,SAAiB,EACjB,cAAkC;IAElC,mCAAmC;IACnC,MAAM,kBAAkB,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,UAAU,CAAC;IAE1D,0EAA0E;IAC1E,MAAM,UAAU,GAAG,cAAI,CAAC,IAAI,CAAC,SAAS,EAAE,kBAAkB,CAAC,CAAC;IAE5D,iCAAiC;IACjC,MAAM,aAAa,GAAG,cAAI,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;IAEtD,IAAI,CAAC,CAAC,MAAM,kBAAE,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC,EAAE;QACzC,wEAAwE;QACxE,OAAO;KACR;IAED,MAAM,IAAA,eAAO,EAAC,8BAA8B,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;QAC3D,MAAM,eAAe,GAAG,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC;QACtD,MAAM,QAAQ,GAAG,kCAAkC,eAAe,EAAE,CAAC;QACrE,KAAK,CAAC,2BAA2B,QAAQ,EAAE,CAAC,CAAC;QAC7C,MAAM,IAAA,qBAAU,EACd,cAAc,EACd,CAAC,QAAQ,EAAE,UAAU,EAAE,IAAI,EAAE,kBAAkB,EAAE,YAAY,EAAE,QAAQ,EAAE,OAAO,CAAC,EACjF;YACE,GAAG,EAAE,SAAS;YACd,KAAK,EAAE,QAAQ;SAChB,CACF,CAAC;QACF,IAAI,CAAC,OAAO,CAAC,6BAA6B,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,MAAM,IAAA,eAAO,EAAC,6BAA6B,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;QAC1D,+DAA+D;QAC/D,oDAAoD;QACpD,MAAM,SAAS,CAAC,aAAa,EAAE,UAAU,CAAC,CAAC;QAE3C,4BAA4B;QAC5B,MAAM,kBAAE,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;QAE9B,uCAAuC;QACvC,sDAAsD;QACtD,MAAM,kBAAE,CAAC,MAAM,CAAC,cAAI,CAAC,IAAI,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC;QAE/C,kDAAkD;QAClD,MAAM,kBAAE,CAAC,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;QAE3C,MAAM,yBAAyB,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;QAErD,IAAI,CAAC,OAAO,CAAC,4BAA4B,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,MAAM,kBAAkB,CAAC,aAAa,CAAC,CAAC;IAExC,MAAM,iBAAiB,CAAC,aAAa,CAAC,CAAC;IAEvC,MAAM,IAAA,eAAO,EAAC,4CAA4C,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;QACzE,MAAM,IAAA,oCAAmB,EAAC,cAAc,EAAE,aAAa,CAAC,CAAC;QACzD,IAAI,YAAE,CAAC,QAAQ,EAAE,KAAK,QAAQ,EAAE;YAC9B,MAAM,UAAU,CAAC,aAAa,CAAC,CAAC;YAChC,IAAI,CAAC,OAAO,CAAC,2CAA2C,CAAC,CAAC;SAC3D;aAAM;YACL,IAAI,CAAC,OAAO,CAAC,0EAA0E,CAAC,CAAC;SAC1F;IACH,CAAC,CAAC,CAAC;AACL,CAAC;AAnED,4CAmEC;AAED;;GAEG;AACH,KAAK,UAAU,SAAS,CAAC,QAAgB,EAAE,MAAc;IACvD,KAAK,MAAM,IAAI,IAAI,MAAM,kBAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE;QAC7C,MAAM,kBAAE,CAAC,IAAI,CAAC,cAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,cAAI,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE;YAChE,SAAS,EAAE,IAAI;SAChB,CAAC,CAAC;KACJ;AACH,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,yBAAyB,CAAC,OAAe,EAAE,IAAsB;IAC9E,MAAM,aAAa,GAAG,cAAI,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;IACrD,MAAM,SAAS,GAAG,MAAM,kBAAE,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;IACnD,MAAM,KAAK,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,UAAU,CAAC;IAEhD,qDAAqD;IACrD,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE;QAC3B,SAAS,CAAC,IAAI,CAAC,OAAO,GAAG,EAAE,CAAC;KAC7B;IACD,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,GAAG,KAAK,CAAC;IAEvC,gCAAgC;IAChC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,EAAE;QACvB,SAAS,CAAC,IAAI,CAAC,GAAG,GAAG,EAAE,CAAC;KACzB;IACD,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,gBAAgB,GAAG,KAAK,CAAC;IAE5C,MAAM,kBAAE,CAAC,SAAS,CAAC,aAAa,EAAE,SAAS,EAAE;QAC3C,MAAM,EAAE,CAAC;KACV,CAAC,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,iBAAiB,CAAC,OAAe;IAC9C,MAAM,eAAe,GAAG,cAAI,CAAC,IAAI,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC;IAC3D,MAAM,WAAW,GAAG,MAAM,kBAAE,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC;IAEvD,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE;QACrB,WAAW,CAAC,IAAI,GAAG,EAAE,CAAC;KACvB;IAED,iDAAiD;IACjD,0DAA0D;IAC1D,WAAW,CAAC,IAAI,CAAC,WAAW,GAAG;QAC7B,gBAAgB,EAAE,IAAI;KACvB,CAAC;IAEF,kCAAkC;IAClC,KAAK,MAAM,kBAAkB,IAAI,sBAAsB,EAAE;QACvD,OAAO,WAAW,CAAC,YAAY,CAAC,kBAAkB,CAAC,CAAC;KACrD;IAED,MAAM,kBAAE,CAAC,SAAS,CAAC,eAAe,EAAE,WAAW,EAAE;QAC/C,MAAM,EAAE,CAAC;KACV,CAAC,CAAC;AACL,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,kBAAkB,CAAC,cAAsB;IACtD,MAAM,IAAA,eAAO,EAAC,6BAA6B,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;QAC1D,MAAM,IAAA,qBAAU,EAAC,KAAK,EAAE,CAAC,MAAM,EAAE,UAAU,EAAE,cAAc,CAAC,EAAE;YAC5D,GAAG,EAAE,cAAc;YACnB,KAAK,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,CAAC;SACpC,CAAC,CAAC;QACH,IAAI,CAAC,OAAO,CAAC,0BAA0B,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,UAAU,CAAC,OAAe;IACvC,MAAM,IAAA,qBAAU,EAAC,KAAK,EAAE,CAAC,SAAS,CAAC,EAAE;QACnC,GAAG,EAAE,cAAI,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC;QAC9B,KAAK,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,CAAC;KACpC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import spawnAsync from '@expo/spawn-async';\nimport fs from 'fs-extra';\nimport getenv from 'getenv';\nimport os from 'os';\nimport path from 'path';\n\nimport { installDependencies } from './packageManager';\nimport { PackageManagerName } from './resolvePackageManager';\nimport { SubstitutionData } from './types';\nimport { newStep } from './utils';\n\nconst debug = require('debug')('create-expo-module:createExampleApp') as typeof console.log;\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'];\nconst EXPO_BETA = getenv.boolish('EXPO_BETA', false);\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 // Package name for the example app\n const exampleProjectSlug = `${data.project.slug}-example`;\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 if (!(await fs.pathExists(appTargetPath))) {\n // The template doesn't include the example app, so just skip this phase\n return;\n }\n\n await newStep('Initializing the example app', async (step) => {\n const templateVersion = EXPO_BETA ? 'next' : 'latest';\n const template = `expo-template-blank-typescript@${templateVersion}`;\n debug(`Using example template: ${template}`);\n await spawnAsync(\n packageManager,\n ['create', 'expo-app', '--', exampleProjectSlug, '--template', template, '--yes'],\n {\n cwd: targetDir,\n stdio: 'ignore',\n }\n );\n step.succeed('Initialized the example app');\n });\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 // Clean up the \".git\" from example app\n // note, this directory has contents, rmdir will throw\n await fs.remove(path.join(appTmpPath, '.git'));\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 if (os.platform() === 'darwin') {\n await podInstall(appTargetPath);\n step.succeed('Installed dependencies in the example app');\n } else {\n step.succeed('Installed dependencies in the example app (skipped installing CocoaPods)');\n }\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 `npx 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 `npx 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('npx', ['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"]}
@@ -1,3 +1,5 @@
1
1
  import { PromptObject } from 'prompts';
2
2
  export declare function getSlugPrompt(customTargetPath?: string | null): PromptObject<string>;
3
+ export declare function getLocalFolderNamePrompt(customTargetPath?: string | null): PromptObject<string>;
3
4
  export declare function getSubstitutionDataPrompts(slug: string): Promise<PromptObject<string>[]>;
5
+ export declare function getLocalSubstitutionDataPrompts(slug: string): Promise<PromptObject<string>[]>;
package/build/prompts.js CHANGED
@@ -3,15 +3,18 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.getSubstitutionDataPrompts = exports.getSlugPrompt = void 0;
6
+ exports.getLocalSubstitutionDataPrompts = exports.getSubstitutionDataPrompts = exports.getLocalFolderNamePrompt = exports.getSlugPrompt = void 0;
7
7
  const path_1 = __importDefault(require("path"));
8
8
  const validate_npm_package_name_1 = __importDefault(require("validate-npm-package-name"));
9
9
  const utils_1 = require("./utils");
10
- function getSlugPrompt(customTargetPath) {
10
+ function getInitialName(customTargetPath) {
11
11
  const targetBasename = customTargetPath && path_1.default.basename(customTargetPath);
12
- const initial = targetBasename && (0, validate_npm_package_name_1.default)(targetBasename).validForNewPackages
12
+ return targetBasename && (0, validate_npm_package_name_1.default)(targetBasename).validForNewPackages
13
13
  ? targetBasename
14
14
  : 'my-module';
15
+ }
16
+ function getSlugPrompt(customTargetPath) {
17
+ const initial = getInitialName(customTargetPath);
15
18
  return {
16
19
  type: 'text',
17
20
  name: 'slug',
@@ -21,6 +24,17 @@ function getSlugPrompt(customTargetPath) {
21
24
  };
22
25
  }
23
26
  exports.getSlugPrompt = getSlugPrompt;
27
+ function getLocalFolderNamePrompt(customTargetPath) {
28
+ const initial = getInitialName(customTargetPath);
29
+ return {
30
+ type: 'text',
31
+ name: 'slug',
32
+ message: 'What is the name of the local module?',
33
+ initial,
34
+ validate: (input) => (0, validate_npm_package_name_1.default)(input).validForNewPackages || 'Must be a valid npm package name',
35
+ };
36
+ }
37
+ exports.getLocalFolderNamePrompt = getLocalFolderNamePrompt;
24
38
  async function getSubstitutionDataPrompts(slug) {
25
39
  return [
26
40
  {
@@ -84,4 +98,34 @@ async function getSubstitutionDataPrompts(slug) {
84
98
  ];
85
99
  }
86
100
  exports.getSubstitutionDataPrompts = getSubstitutionDataPrompts;
101
+ async function getLocalSubstitutionDataPrompts(slug) {
102
+ return [
103
+ {
104
+ type: 'text',
105
+ name: 'name',
106
+ message: 'What is the native module name?',
107
+ initial: () => {
108
+ return slug
109
+ .replace(/^@/, '')
110
+ .replace(/^./, (match) => match.toUpperCase())
111
+ .replace(/\W+(\w)/g, (_, p1) => p1.toUpperCase());
112
+ },
113
+ validate: (input) => !!input || 'The native module name cannot be empty',
114
+ },
115
+ {
116
+ type: 'text',
117
+ name: 'package',
118
+ message: 'What is the Android package name?',
119
+ initial: () => {
120
+ const namespace = slug
121
+ .replace(/\W/g, '')
122
+ .replace(/^(expo|reactnative)/, '')
123
+ .toLowerCase();
124
+ return `expo.modules.${namespace}`;
125
+ },
126
+ validate: (input) => !!input || 'The Android package name cannot be empty',
127
+ },
128
+ ];
129
+ }
130
+ exports.getLocalSubstitutionDataPrompts = getLocalSubstitutionDataPrompts;
87
131
  //# sourceMappingURL=prompts.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"prompts.js","sourceRoot":"","sources":["../src/prompts.ts"],"names":[],"mappings":";;;;;;AAAA,gDAAwB;AAExB,0FAA2D;AAE3D,mCAA0F;AAE1F,SAAgB,aAAa,CAAC,gBAAgC;IAC5D,MAAM,cAAc,GAAG,gBAAgB,IAAI,cAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAC;IAC3E,MAAM,OAAO,GACX,cAAc,IAAI,IAAA,mCAAkB,EAAC,cAAc,CAAC,CAAC,mBAAmB;QACtE,CAAC,CAAC,cAAc;QAChB,CAAC,CAAC,WAAW,CAAC;IAElB,OAAO;QACL,IAAI,EAAE,MAAM;QACZ,IAAI,EAAE,MAAM;QACZ,OAAO,EAAE,sCAAsC;QAC/C,OAAO;QACP,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE,CAClB,IAAA,mCAAkB,EAAC,KAAK,CAAC,CAAC,mBAAmB,IAAI,kCAAkC;KACtF,CAAC;AACJ,CAAC;AAfD,sCAeC;AAEM,KAAK,UAAU,0BAA0B,CAAC,IAAY;IAC3D,OAAO;QACL;YACE,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,MAAM;YACZ,OAAO,EAAE,iCAAiC;YAC1C,OAAO,EAAE,GAAG,EAAE;gBACZ,OAAO,IAAI;qBACR,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC;qBACjB,OAAO,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;qBAC7C,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;YACtD,CAAC;YACD,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,wCAAwC;SACzE;QACD;YACE,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,aAAa;YACnB,OAAO,EAAE,oCAAoC;YAC7C,OAAO,EAAE,eAAe;YACxB,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,iCAAiC;SAClE;QACD;YACE,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,SAAS;YACf,OAAO,EAAE,mCAAmC;YAC5C,OAAO,EAAE,GAAG,EAAE;gBACZ,MAAM,SAAS,GAAG,IAAI;qBACnB,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;qBAClB,OAAO,CAAC,qBAAqB,EAAE,EAAE,CAAC;qBAClC,WAAW,EAAE,CAAC;gBACjB,OAAO,gBAAgB,SAAS,EAAE,CAAC;YACrC,CAAC;YACD,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,0CAA0C;SAC3E;QACD;YACE,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,YAAY;YAClB,OAAO,EAAE,yCAAyC;YAClD,OAAO,EAAE,MAAM,IAAA,kBAAU,GAAE;YAC3B,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,iBAAiB;SAClD;QACD;YACE,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,aAAa;YACnB,OAAO,EAAE,0CAA0C;YACnD,OAAO,EAAE,MAAM,IAAA,uBAAe,GAAE;SACjC;QACD;YACE,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,WAAW;YACjB,OAAO,EAAE,iDAAiD;YAC1D,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,OAAwB,EAAE,EAAE,CAC7C,MAAM,IAAA,4BAAoB,EAAC,OAAO,CAAC,WAAW,CAAC;SAClD;QACD;YACE,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,MAAM;YACZ,OAAO,EAAE,qCAAqC;YAC9C,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,OAAwB,EAAE,EAAE,CAAC,MAAM,IAAA,oBAAY,EAAC,OAAO,CAAC,SAAS,EAAE,IAAI,CAAC;YAC3F,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,qBAAqB;SACzE;KACF,CAAC;AACJ,CAAC;AA9DD,gEA8DC","sourcesContent":["import path from 'path';\nimport { Answers, PromptObject } from 'prompts';\nimport validateNpmPackage from 'validate-npm-package-name';\n\nimport { findGitHubEmail, findGitHubProfileUrl, findMyName, guessRepoUrl } from './utils';\n\nexport function getSlugPrompt(customTargetPath?: string | null): PromptObject<string> {\n const targetBasename = customTargetPath && path.basename(customTargetPath);\n const initial =\n targetBasename && validateNpmPackage(targetBasename).validForNewPackages\n ? targetBasename\n : 'my-module';\n\n return {\n type: 'text',\n name: 'slug',\n message: 'What is the name of the npm package?',\n initial,\n validate: (input) =>\n validateNpmPackage(input).validForNewPackages || 'Must be a valid npm package name',\n };\n}\n\nexport async function getSubstitutionDataPrompts(slug: string): Promise<PromptObject<string>[]> {\n return [\n {\n type: 'text',\n name: 'name',\n message: 'What is the native module name?',\n initial: () => {\n return slug\n .replace(/^@/, '')\n .replace(/^./, (match) => match.toUpperCase())\n .replace(/\\W+(\\w)/g, (_, p1) => p1.toUpperCase());\n },\n validate: (input) => !!input || 'The native module name cannot be empty',\n },\n {\n type: 'text',\n name: 'description',\n message: 'How would you describe the module?',\n initial: 'My new module',\n validate: (input) => !!input || 'The description cannot be empty',\n },\n {\n type: 'text',\n name: 'package',\n message: 'What is the Android package name?',\n initial: () => {\n const namespace = slug\n .replace(/\\W/g, '')\n .replace(/^(expo|reactnative)/, '')\n .toLowerCase();\n return `expo.modules.${namespace}`;\n },\n validate: (input) => !!input || 'The Android package name cannot be empty',\n },\n {\n type: 'text',\n name: 'authorName',\n message: 'What is the name of the package author?',\n initial: await findMyName(),\n validate: (input) => !!input || 'Cannot be empty',\n },\n {\n type: 'text',\n name: 'authorEmail',\n message: 'What is the email address of the author?',\n initial: await findGitHubEmail(),\n },\n {\n type: 'text',\n name: 'authorUrl',\n message: \"What is the URL to the author's GitHub profile?\",\n initial: async (_, answers: Answers<string>) =>\n await findGitHubProfileUrl(answers.authorEmail),\n },\n {\n type: 'text',\n name: 'repo',\n message: 'What is the URL for the repository?',\n initial: async (_, answers: Answers<string>) => await guessRepoUrl(answers.authorUrl, slug),\n validate: (input) => /^https?:\\/\\//.test(input) || 'Must be a valid URL',\n },\n ];\n}\n"]}
1
+ {"version":3,"file":"prompts.js","sourceRoot":"","sources":["../src/prompts.ts"],"names":[],"mappings":"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","sourcesContent":["import path from 'path';\nimport { Answers, PromptObject } from 'prompts';\nimport validateNpmPackage from 'validate-npm-package-name';\n\nimport { findGitHubEmail, findGitHubProfileUrl, findMyName, guessRepoUrl } from './utils';\n\nfunction getInitialName(customTargetPath?: string | null): string {\n const targetBasename = customTargetPath && path.basename(customTargetPath);\n return targetBasename && validateNpmPackage(targetBasename).validForNewPackages\n ? targetBasename\n : 'my-module';\n}\n\nexport function getSlugPrompt(customTargetPath?: string | null): PromptObject<string> {\n const initial = getInitialName(customTargetPath);\n return {\n type: 'text',\n name: 'slug',\n message: 'What is the name of the npm package?',\n initial,\n validate: (input) =>\n validateNpmPackage(input).validForNewPackages || 'Must be a valid npm package name',\n };\n}\n\nexport function getLocalFolderNamePrompt(customTargetPath?: string | null): PromptObject<string> {\n const initial = getInitialName(customTargetPath);\n\n return {\n type: 'text',\n name: 'slug',\n message: 'What is the name of the local module?',\n initial,\n validate: (input) =>\n validateNpmPackage(input).validForNewPackages || 'Must be a valid npm package name',\n };\n}\n\nexport async function getSubstitutionDataPrompts(slug: string): Promise<PromptObject<string>[]> {\n return [\n {\n type: 'text',\n name: 'name',\n message: 'What is the native module name?',\n initial: () => {\n return slug\n .replace(/^@/, '')\n .replace(/^./, (match) => match.toUpperCase())\n .replace(/\\W+(\\w)/g, (_, p1) => p1.toUpperCase());\n },\n validate: (input) => !!input || 'The native module name cannot be empty',\n },\n {\n type: 'text',\n name: 'description',\n message: 'How would you describe the module?',\n initial: 'My new module',\n validate: (input) => !!input || 'The description cannot be empty',\n },\n {\n type: 'text',\n name: 'package',\n message: 'What is the Android package name?',\n initial: () => {\n const namespace = slug\n .replace(/\\W/g, '')\n .replace(/^(expo|reactnative)/, '')\n .toLowerCase();\n return `expo.modules.${namespace}`;\n },\n validate: (input) => !!input || 'The Android package name cannot be empty',\n },\n {\n type: 'text',\n name: 'authorName',\n message: 'What is the name of the package author?',\n initial: await findMyName(),\n validate: (input) => !!input || 'Cannot be empty',\n },\n {\n type: 'text',\n name: 'authorEmail',\n message: 'What is the email address of the author?',\n initial: await findGitHubEmail(),\n },\n {\n type: 'text',\n name: 'authorUrl',\n message: \"What is the URL to the author's GitHub profile?\",\n initial: async (_, answers: Answers<string>) =>\n await findGitHubProfileUrl(answers.authorEmail),\n },\n {\n type: 'text',\n name: 'repo',\n message: 'What is the URL for the repository?',\n initial: async (_, answers: Answers<string>) => await guessRepoUrl(answers.authorUrl, slug),\n validate: (input) => /^https?:\\/\\//.test(input) || 'Must be a valid URL',\n },\n ];\n}\n\nexport async function getLocalSubstitutionDataPrompts(\n slug: string\n): Promise<PromptObject<string>[]> {\n return [\n {\n type: 'text',\n name: 'name',\n message: 'What is the native module name?',\n initial: () => {\n return slug\n .replace(/^@/, '')\n .replace(/^./, (match) => match.toUpperCase())\n .replace(/\\W+(\\w)/g, (_, p1) => p1.toUpperCase());\n },\n validate: (input) => !!input || 'The native module name cannot be empty',\n },\n {\n type: 'text',\n name: 'package',\n message: 'What is the Android package name?',\n initial: () => {\n const namespace = slug\n .replace(/\\W/g, '')\n .replace(/^(expo|reactnative)/, '')\n .toLowerCase();\n return `expo.modules.${namespace}`;\n },\n validate: (input) => !!input || 'The Android package name cannot be empty',\n },\n ];\n}\n"]}
package/build/types.d.ts CHANGED
@@ -8,6 +8,7 @@ export type CommandOptions = {
8
8
  withReadme: boolean;
9
9
  withChangelog: boolean;
10
10
  example: boolean;
11
+ local: boolean;
11
12
  };
12
13
  /**
13
14
  * Represents an object that is passed to `ejs` when rendering the template.
@@ -25,6 +26,17 @@ export type SubstitutionData = {
25
26
  author: string;
26
27
  license: string;
27
28
  repo: string;
29
+ type: 'remote';
30
+ };
31
+ export type LocalSubstitutionData = {
32
+ project: {
33
+ slug: string;
34
+ name: string;
35
+ package: string;
36
+ moduleName: string;
37
+ viewName: string;
38
+ };
39
+ type: 'local';
28
40
  };
29
41
  export type CustomPromptObject = PromptObject & {
30
42
  name: string;
@@ -1 +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 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 moduleName: string;\n viewName: 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\nexport type Answers = Record<string, string>;\n"]}
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 withReadme: boolean;\n withChangelog: boolean;\n example: boolean;\n local: 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 moduleName: string;\n viewName: string;\n };\n author: string;\n license: string;\n repo: string;\n type: 'remote';\n};\n\nexport type LocalSubstitutionData = {\n project: {\n slug: string;\n name: string;\n package: string;\n moduleName: string;\n viewName: string;\n };\n type: 'local';\n};\n\nexport type CustomPromptObject = PromptObject & {\n name: string;\n resolvedValue?: string | null;\n};\n\nexport type Answers = Record<string, string>;\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-expo-module",
3
- "version": "0.5.13",
3
+ "version": "0.6.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",
@@ -43,6 +43,7 @@
43
43
  "debug": "^4.3.4",
44
44
  "download-tarball": "^2.0.0",
45
45
  "ejs": "^3.1.7",
46
+ "find-up": "^5.0.0",
46
47
  "fs-extra": "^10.0.0",
47
48
  "getenv": "^1.0.0",
48
49
  "github-username": "^6.0.0",
@@ -53,8 +54,9 @@
53
54
  "devDependencies": {
54
55
  "@tsconfig/node14": "^1.0.3",
55
56
  "@types/ejs": "^3.1.0",
57
+ "@types/find-up": "^4.0.0",
56
58
  "@types/prompts": "^2.0.14",
57
59
  "expo-module-scripts": "^3.0.0"
58
60
  },
59
- "gitHead": "53fff7109a6b5615240a055b04c0732713706c40"
61
+ "gitHead": "f7c329e4d16626a57899371c8d34b2607f6ab613"
60
62
  }
@@ -3,6 +3,7 @@ import chalk from 'chalk';
3
3
  import { Command } from 'commander';
4
4
  import downloadTarball from 'download-tarball';
5
5
  import ejs from 'ejs';
6
+ import findUp from 'find-up';
6
7
  import fs from 'fs-extra';
7
8
  import { boolish } from 'getenv';
8
9
  import path from 'path';
@@ -10,14 +11,19 @@ import prompts from 'prompts';
10
11
 
11
12
  import { createExampleApp } from './createExampleApp';
12
13
  import { installDependencies } from './packageManager';
13
- import { getSlugPrompt, getSubstitutionDataPrompts } from './prompts';
14
+ import {
15
+ getLocalFolderNamePrompt,
16
+ getLocalSubstitutionDataPrompts,
17
+ getSlugPrompt,
18
+ getSubstitutionDataPrompts,
19
+ } from './prompts';
14
20
  import {
15
21
  formatRunCommand,
16
22
  PackageManagerName,
17
23
  resolvePackageManager,
18
24
  } from './resolvePackageManager';
19
25
  import { eventCreateExpoModule, getTelemetryClient, logEventAsync } from './telemetry';
20
- import { CommandOptions, SubstitutionData } from './types';
26
+ import { CommandOptions, LocalSubstitutionData, SubstitutionData } from './types';
21
27
  import { newStep } from './utils';
22
28
 
23
29
  const debug = require('debug')('create-expo-module:main') as typeof console.log;
@@ -43,6 +49,26 @@ const IGNORES_PATHS = [
43
49
  // Url to the documentation on Expo Modules
44
50
  const DOCS_URL = 'https://docs.expo.dev/modules';
45
51
 
52
+ const FYI_LOCAL_DIR = 'https://expo.fyi/expo-module-local-autolinking.md';
53
+
54
+ async function getCorrectLocalDirectory(targetOrSlug: string) {
55
+ const packageJsonPath = await findUp('package.json', { cwd: CWD });
56
+ if (!packageJsonPath) {
57
+ console.log(
58
+ chalk.red.bold(
59
+ '⚠️ This command should be run inside your Expo project when run with the --local flag.'
60
+ )
61
+ );
62
+ console.log(
63
+ chalk.red(
64
+ 'For native modules to autolink correctly, you need to place them in the `modules` directory in the root of the project.'
65
+ )
66
+ );
67
+ return null;
68
+ }
69
+ return path.join(packageJsonPath, '..', 'modules', targetOrSlug);
70
+ }
71
+
46
72
  /**
47
73
  * The main function of the command.
48
74
  *
@@ -50,15 +76,31 @@ const DOCS_URL = 'https://docs.expo.dev/modules';
50
76
  * @param command An object from `commander`.
51
77
  */
52
78
  async function main(target: string | undefined, options: CommandOptions) {
53
- const slug = await askForPackageSlugAsync(target);
54
- const targetDir = path.join(CWD, target || slug);
79
+ if (options.local) {
80
+ console.log();
81
+ console.log(
82
+ `${chalk.gray('The local module will be created in the ')}${chalk.gray.bold.italic(
83
+ 'modules'
84
+ )} ${chalk.gray('directory in the root of your project. Learn more: ')}${chalk.gray.bold(
85
+ FYI_LOCAL_DIR
86
+ )}`
87
+ );
88
+ console.log();
89
+ }
90
+ const slug = await askForPackageSlugAsync(target, options.local);
91
+ const targetDir = options.local
92
+ ? await getCorrectLocalDirectory(target || slug)
93
+ : path.join(CWD, target || slug);
55
94
 
95
+ if (!targetDir) {
96
+ return;
97
+ }
56
98
  await fs.ensureDir(targetDir);
57
99
  await confirmTargetDirAsync(targetDir);
58
100
 
59
101
  options.target = targetDir;
60
102
 
61
- const data = await askForSubstitutionDataAsync(slug);
103
+ const data = await askForSubstitutionDataAsync(slug, options.local);
62
104
 
63
105
  // Make one line break between prompts and progress logs
64
106
  console.log();
@@ -66,7 +108,7 @@ async function main(target: string | undefined, options: CommandOptions) {
66
108
  const packageManager = await resolvePackageManager();
67
109
  const packagePath = options.source
68
110
  ? path.join(CWD, options.source)
69
- : await downloadPackageAsync(targetDir);
111
+ : await downloadPackageAsync(targetDir, options.local);
70
112
 
71
113
  logEventAsync(eventCreateExpoModule(packageManager, options));
72
114
 
@@ -74,55 +116,63 @@ async function main(target: string | undefined, options: CommandOptions) {
74
116
  await createModuleFromTemplate(packagePath, targetDir, data);
75
117
  step.succeed('Created the module from template files');
76
118
  });
77
-
78
- await newStep('Installing module dependencies', async (step) => {
79
- await installDependencies(packageManager, targetDir);
80
- step.succeed('Installed module dependencies');
81
- });
82
-
83
- await newStep('Compiling TypeScript files', async (step) => {
84
- await spawnAsync(packageManager, ['run', 'build'], {
85
- cwd: targetDir,
86
- stdio: 'ignore',
119
+ if (!options.local) {
120
+ await newStep('Installing module dependencies', async (step) => {
121
+ await installDependencies(packageManager, targetDir);
122
+ step.succeed('Installed module dependencies');
87
123
  });
88
- step.succeed('Compiled TypeScript files');
89
- });
124
+ await newStep('Compiling TypeScript files', async (step) => {
125
+ await spawnAsync(packageManager, ['run', 'build'], {
126
+ cwd: targetDir,
127
+ stdio: 'ignore',
128
+ });
129
+ step.succeed('Compiled TypeScript files');
130
+ });
131
+ }
90
132
 
91
133
  if (!options.source) {
92
134
  // Files in the downloaded tarball are wrapped in `package` dir.
93
135
  // We should remove it after all.
94
136
  await fs.remove(packagePath);
95
137
  }
96
- if (!options.withReadme) {
97
- await fs.remove(path.join(targetDir, 'README.md'));
98
- }
99
- if (!options.withChangelog) {
100
- await fs.remove(path.join(targetDir, 'CHANGELOG.md'));
101
- }
102
- if (options.example) {
103
- // Create "example" folder
104
- await createExampleApp(data, targetDir, packageManager);
105
- }
138
+ if (!options.local && data.type !== 'local') {
139
+ if (!options.withReadme) {
140
+ await fs.remove(path.join(targetDir, 'README.md'));
141
+ }
142
+ if (!options.withChangelog) {
143
+ await fs.remove(path.join(targetDir, 'CHANGELOG.md'));
144
+ }
145
+ if (options.example) {
146
+ // Create "example" folder
147
+ await createExampleApp(data, targetDir, packageManager);
148
+ }
106
149
 
107
- await newStep('Creating an empty Git repository', async (step) => {
108
- try {
109
- const result = await createGitRepositoryAsync(targetDir);
110
- if (result) {
111
- step.succeed('Created an empty Git repository');
112
- } else if (result === null) {
113
- step.succeed('Skipped creating an empty Git repository, already within a Git repository');
114
- } else if (result === false) {
115
- step.warn('Could not create an empty Git repository, see debug logs with EXPO_DEBUG=true');
150
+ await newStep('Creating an empty Git repository', async (step) => {
151
+ try {
152
+ const result = await createGitRepositoryAsync(targetDir);
153
+ if (result) {
154
+ step.succeed('Created an empty Git repository');
155
+ } else if (result === null) {
156
+ step.succeed('Skipped creating an empty Git repository, already within a Git repository');
157
+ } else if (result === false) {
158
+ step.warn(
159
+ 'Could not create an empty Git repository, see debug logs with EXPO_DEBUG=true'
160
+ );
161
+ }
162
+ } catch (e: any) {
163
+ step.fail(e.toString());
116
164
  }
117
- } catch (e: any) {
118
- step.fail(e.toString());
119
- }
120
- });
165
+ });
166
+ }
121
167
 
122
168
  console.log();
123
- console.log('✅ Successfully created Expo module');
124
-
125
- printFurtherInstructions(targetDir, packageManager, options.example);
169
+ if (options.local) {
170
+ console.log(`✅ Successfully created Expo module in ${chalk.bold.italic(`modules/${slug}`)}`);
171
+ printFurtherLocalInstructions(slug, data.project.moduleName);
172
+ } else {
173
+ console.log('✅ Successfully created Expo module');
174
+ printFurtherInstructions(targetDir, packageManager, options.example);
175
+ }
126
176
  }
127
177
 
128
178
  /**
@@ -163,10 +213,10 @@ async function getNpmTarballUrl(packageName: string, version: string = 'latest')
163
213
  /**
164
214
  * Downloads the template from NPM registry.
165
215
  */
166
- async function downloadPackageAsync(targetDir: string): Promise<string> {
216
+ async function downloadPackageAsync(targetDir: string, isLocal = false): Promise<string> {
167
217
  return await newStep('Downloading module template from npm', async (step) => {
168
218
  const tarballUrl = await getNpmTarballUrl(
169
- 'expo-module-template',
219
+ isLocal ? 'expo-module-template-local' : 'expo-module-template',
170
220
  EXPO_BETA ? 'next' : 'latest'
171
221
  );
172
222
 
@@ -194,7 +244,7 @@ function handleSuffix(name: string, suffix: string): string {
194
244
  async function createModuleFromTemplate(
195
245
  templatePath: string,
196
246
  targetPath: string,
197
- data: SubstitutionData
247
+ data: SubstitutionData | LocalSubstitutionData
198
248
  ) {
199
249
  const files = await getFilesAsync(templatePath);
200
250
 
@@ -247,10 +297,13 @@ async function createGitRepositoryAsync(targetDir: string) {
247
297
  /**
248
298
  * Asks the user for the package slug (npm package name).
249
299
  */
250
- async function askForPackageSlugAsync(customTargetPath?: string): Promise<string> {
251
- const { slug } = await prompts(getSlugPrompt(customTargetPath), {
252
- onCancel: () => process.exit(0),
253
- });
300
+ async function askForPackageSlugAsync(customTargetPath?: string, isLocal = false): Promise<string> {
301
+ const { slug } = await prompts(
302
+ (isLocal ? getLocalFolderNamePrompt : getSlugPrompt)(customTargetPath),
303
+ {
304
+ onCancel: () => process.exit(0),
305
+ }
306
+ );
254
307
  return slug;
255
308
  }
256
309
 
@@ -258,8 +311,13 @@ async function askForPackageSlugAsync(customTargetPath?: string): Promise<string
258
311
  * Asks the user for some data necessary to render the template.
259
312
  * Some values may already be provided by command options, the prompt is skipped in that case.
260
313
  */
261
- async function askForSubstitutionDataAsync(slug: string): Promise<SubstitutionData> {
262
- const promptQueries = await getSubstitutionDataPrompts(slug);
314
+ async function askForSubstitutionDataAsync(
315
+ slug: string,
316
+ isLocal = false
317
+ ): Promise<SubstitutionData | LocalSubstitutionData> {
318
+ const promptQueries = await (isLocal
319
+ ? getLocalSubstitutionDataPrompts
320
+ : getSubstitutionDataPrompts)(slug);
263
321
 
264
322
  // Stop the process when the user cancels/exits the prompt.
265
323
  const onCancel = () => {
@@ -276,6 +334,19 @@ async function askForSubstitutionDataAsync(slug: string): Promise<SubstitutionDa
276
334
  repo,
277
335
  } = await prompts(promptQueries, { onCancel });
278
336
 
337
+ if (isLocal) {
338
+ return {
339
+ project: {
340
+ slug,
341
+ name,
342
+ package: projectPackage,
343
+ moduleName: handleSuffix(name, 'Module'),
344
+ viewName: handleSuffix(name, 'View'),
345
+ },
346
+ type: 'local',
347
+ };
348
+ }
349
+
279
350
  return {
280
351
  project: {
281
352
  slug,
@@ -289,6 +360,7 @@ async function askForSubstitutionDataAsync(slug: string): Promise<SubstitutionDa
289
360
  author: `${authorName} <${authorEmail}> (${authorUrl})`,
290
361
  license: 'MIT',
291
362
  repo,
363
+ type: 'remote',
292
364
  };
293
365
  }
294
366
 
@@ -341,7 +413,21 @@ function printFurtherInstructions(
341
413
  commands.forEach((command) => console.log(chalk.gray('>'), chalk.bold(command)));
342
414
  console.log();
343
415
  }
344
- console.log(`Visit ${chalk.blue.bold(DOCS_URL)} for the documentation on Expo Modules APIs`);
416
+ console.log(`Learn more on Expo Modules APIs: ${chalk.blue.bold(DOCS_URL)}`);
417
+ }
418
+
419
+ function printFurtherLocalInstructions(slug: string, name: string) {
420
+ console.log();
421
+ console.log(`You can now import this module inside your application.`);
422
+ console.log(`For example, you can add this line to your App.js or App.tsx file:`);
423
+ console.log(`${chalk.gray.italic(`import { hello } from './modules/${slug}';`)}`);
424
+ console.log();
425
+ console.log(`Learn more on Expo Modules APIs: ${chalk.blue.bold(DOCS_URL)}`);
426
+ console.log(
427
+ chalk.yellow(
428
+ `Remember you need to rebuild your development client or reinstall pods to see the changes.`
429
+ )
430
+ );
345
431
  }
346
432
 
347
433
  const program = new Command();
@@ -358,6 +444,11 @@ program
358
444
  .option('--with-readme', 'Whether to include README.md file.', false)
359
445
  .option('--with-changelog', 'Whether to include CHANGELOG.md file.', false)
360
446
  .option('--no-example', 'Whether to skip creating the example app.', false)
447
+ .option(
448
+ '--local',
449
+ 'Whether to create a local module in the current project, skipping installing node_modules and creating the example directory.',
450
+ false
451
+ )
361
452
  .action(main);
362
453
 
363
454
  program
@@ -99,7 +99,7 @@ async function moveFiles(fromPath: string, toPath: string): Promise<void> {
99
99
  }
100
100
 
101
101
  /**
102
- * Adds missing configuration that are required to run `expo prebuild`.
102
+ * Adds missing configuration that are required to run `npx expo prebuild`.
103
103
  */
104
104
  async function addMissingAppConfigFields(appPath: string, data: SubstitutionData): Promise<void> {
105
105
  const appConfigPath = path.join(appPath, 'app.json');
package/src/prompts.ts CHANGED
@@ -4,13 +4,15 @@ import validateNpmPackage from 'validate-npm-package-name';
4
4
 
5
5
  import { findGitHubEmail, findGitHubProfileUrl, findMyName, guessRepoUrl } from './utils';
6
6
 
7
- export function getSlugPrompt(customTargetPath?: string | null): PromptObject<string> {
7
+ function getInitialName(customTargetPath?: string | null): string {
8
8
  const targetBasename = customTargetPath && path.basename(customTargetPath);
9
- const initial =
10
- targetBasename && validateNpmPackage(targetBasename).validForNewPackages
11
- ? targetBasename
12
- : 'my-module';
9
+ return targetBasename && validateNpmPackage(targetBasename).validForNewPackages
10
+ ? targetBasename
11
+ : 'my-module';
12
+ }
13
13
 
14
+ export function getSlugPrompt(customTargetPath?: string | null): PromptObject<string> {
15
+ const initial = getInitialName(customTargetPath);
14
16
  return {
15
17
  type: 'text',
16
18
  name: 'slug',
@@ -21,6 +23,19 @@ export function getSlugPrompt(customTargetPath?: string | null): PromptObject<st
21
23
  };
22
24
  }
23
25
 
26
+ export function getLocalFolderNamePrompt(customTargetPath?: string | null): PromptObject<string> {
27
+ const initial = getInitialName(customTargetPath);
28
+
29
+ return {
30
+ type: 'text',
31
+ name: 'slug',
32
+ message: 'What is the name of the local module?',
33
+ initial,
34
+ validate: (input) =>
35
+ validateNpmPackage(input).validForNewPackages || 'Must be a valid npm package name',
36
+ };
37
+ }
38
+
24
39
  export async function getSubstitutionDataPrompts(slug: string): Promise<PromptObject<string>[]> {
25
40
  return [
26
41
  {
@@ -84,3 +99,35 @@ export async function getSubstitutionDataPrompts(slug: string): Promise<PromptOb
84
99
  },
85
100
  ];
86
101
  }
102
+
103
+ export async function getLocalSubstitutionDataPrompts(
104
+ slug: string
105
+ ): Promise<PromptObject<string>[]> {
106
+ return [
107
+ {
108
+ type: 'text',
109
+ name: 'name',
110
+ message: 'What is the native module name?',
111
+ initial: () => {
112
+ return slug
113
+ .replace(/^@/, '')
114
+ .replace(/^./, (match) => match.toUpperCase())
115
+ .replace(/\W+(\w)/g, (_, p1) => p1.toUpperCase());
116
+ },
117
+ validate: (input) => !!input || 'The native module name cannot be empty',
118
+ },
119
+ {
120
+ type: 'text',
121
+ name: 'package',
122
+ message: 'What is the Android package name?',
123
+ initial: () => {
124
+ const namespace = slug
125
+ .replace(/\W/g, '')
126
+ .replace(/^(expo|reactnative)/, '')
127
+ .toLowerCase();
128
+ return `expo.modules.${namespace}`;
129
+ },
130
+ validate: (input) => !!input || 'The Android package name cannot be empty',
131
+ },
132
+ ];
133
+ }
package/src/types.ts CHANGED
@@ -9,6 +9,7 @@ export type CommandOptions = {
9
9
  withReadme: boolean;
10
10
  withChangelog: boolean;
11
11
  example: boolean;
12
+ local: boolean;
12
13
  };
13
14
 
14
15
  /**
@@ -27,6 +28,18 @@ export type SubstitutionData = {
27
28
  author: string;
28
29
  license: string;
29
30
  repo: string;
31
+ type: 'remote';
32
+ };
33
+
34
+ export type LocalSubstitutionData = {
35
+ project: {
36
+ slug: string;
37
+ name: string;
38
+ package: string;
39
+ moduleName: string;
40
+ viewName: string;
41
+ };
42
+ type: 'local';
30
43
  };
31
44
 
32
45
  export type CustomPromptObject = PromptObject & {