From e1c0066a7986b9415f179fcc882337c9bd96a8f5 Mon Sep 17 00:00:00 2001 From: szymonrybczak Date: Wed, 14 Feb 2024 14:31:44 +0100 Subject: [PATCH 1/7] feat: add `add-platform` command --- docs/init.md | 18 +- packages/cli/package.json | 2 + .../src/commands/addPlatform/addPlatform.ts | 208 ++++++++++++++++++ .../cli/src/commands/addPlatform/index.ts | 23 ++ packages/cli/src/commands/index.ts | 2 + .../cli/src/commands/init/editTemplate.ts | 19 +- packages/cli/src/commands/init/git.ts | 11 + packages/cli/src/commands/init/init.ts | 13 +- packages/cli/src/commands/init/template.ts | 14 +- 9 files changed, 286 insertions(+), 24 deletions(-) create mode 100644 packages/cli/src/commands/addPlatform/addPlatform.ts create mode 100644 packages/cli/src/commands/addPlatform/index.ts diff --git a/docs/init.md b/docs/init.md index 59a936366..45877e32c 100644 --- a/docs/init.md +++ b/docs/init.md @@ -73,6 +73,8 @@ module.exports = { // Path to script, which will be executed after initialization process, but before installing all the dependencies specified in the template. This script runs as a shell script but you can change that (e.g. to Node) by using a shebang (see example custom template). postInitScript: './script.js', + // We're also using `template.config.js` when adding new platforms to existing project in `add-platform` command. Thanks to value passed to `platformName` we know which folder we should copy to the project. + platformName: 'visionos', }; ``` @@ -91,12 +93,16 @@ new Promise((resolve) => { spinner.start(); // do something resolve(); -}).then(() => { - spinner.succeed(); -}).catch(() => { - spinner.fail(); - throw new Error('Something went wrong during the post init script execution'); -}); +}) + .then(() => { + spinner.succeed(); + }) + .catch(() => { + spinner.fail(); + throw new Error( + 'Something went wrong during the post init script execution', + ); + }); ``` You can find example custom template [here](https://github.com/Esemesek/react-native-new-template). diff --git a/packages/cli/package.json b/packages/cli/package.json index 24d15f165..67ce20824 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -38,6 +38,7 @@ "find-up": "^5.0.0", "fs-extra": "^8.1.0", "graceful-fs": "^4.1.3", + "npm-registry-fetch": "^16.1.0", "prompts": "^2.4.2", "semver": "^7.5.2" }, @@ -45,6 +46,7 @@ "@types/fs-extra": "^8.1.0", "@types/graceful-fs": "^4.1.3", "@types/hapi__joi": "^17.1.6", + "@types/npm-registry-fetch": "^8.0.7", "@types/prompts": "^2.4.4", "@types/semver": "^6.0.2", "slash": "^3.0.0", diff --git a/packages/cli/src/commands/addPlatform/addPlatform.ts b/packages/cli/src/commands/addPlatform/addPlatform.ts new file mode 100644 index 000000000..2da3a7142 --- /dev/null +++ b/packages/cli/src/commands/addPlatform/addPlatform.ts @@ -0,0 +1,208 @@ +import { + CLIError, + getLoader, + logger, + prompt, +} from '@react-native-community/cli-tools'; +import {Config} from '@react-native-community/cli-types'; +import {join} from 'path'; +import {readFileSync} from 'fs'; +import chalk from 'chalk'; +import {install, PackageManager} from './../../tools/packageManager'; +import npmFetch from 'npm-registry-fetch'; +import semver from 'semver'; +import {checkGitInstallation, isGitTreeDirty} from '../init/git'; +import { + changePlaceholderInTemplate, + getTemplateName, +} from '../init/editTemplate'; +import { + copyTemplate, + executePostInitScript, + getTemplateConfig, + installTemplatePackage, +} from '../init/template'; +import {tmpdir} from 'os'; +import {mkdtempSync} from 'graceful-fs'; + +type Options = { + packageName: string; + version: string; + pm: PackageManager; + title: string; +}; + +const NPM_REGISTRY_URL = 'http://registry.npmjs.org'; // TODO: Support local registry + +const getAppName = async (root: string) => { + logger.log(`Reading ${chalk.cyan('name')} from package.json…`); + const pkgJsonPath = join(root, 'package.json'); + + if (!pkgJsonPath) { + throw new CLIError(`Unable to find package.json inside ${root}`); + } + + let {name} = JSON.parse(readFileSync(pkgJsonPath, 'utf8')); + + if (!name) { + const appJson = join(root, 'app.json'); + if (appJson) { + logger.log(`Reading ${chalk.cyan('name')} from app.json…`); + name = JSON.parse(readFileSync(appJson, 'utf8')).name; + } + + if (!name) { + throw new CLIError('Please specify name in package.json or app.json.'); + } + } + + return name; +}; + +const getPackageMatchingVersion = async ( + packageName: string, + version: string, +) => { + const npmResponse = await npmFetch.json(packageName, { + registry: NPM_REGISTRY_URL, + }); + + if ('dist-tags' in npmResponse) { + const distTags = npmResponse['dist-tags'] as Record; + if (version in distTags) { + return distTags[version]; + } + } + + if ('versions' in npmResponse) { + const versions = Object.keys( + npmResponse.versions as Record, + ); + if (versions.length > 0) { + const candidates = versions + .filter((v) => semver.satisfies(v, version)) + .sort(semver.rcompare); + + if (candidates.length > 0) { + return candidates[0]; + } + } + } + + throw new Error( + `Cannot find matching version of ${packageName} to react-native${version}, please provide version manually with --version flag.`, + ); +}; + +async function addPlatform( + [packageName]: string[], + {root, reactNativeVersion}: Config, + {version, pm, title}: Options, +) { + if (!packageName) { + throw new CLIError('Please provide package name e.g. react-native-macos'); + } + + const isGitAvailable = await checkGitInstallation(); + + if (isGitAvailable) { + const dirty = await isGitTreeDirty(root); + + if (dirty) { + logger.warn( + 'Your git tree is dirty. We recommend committing or stashing changes first.', + ); + const {proceed} = await prompt({ + type: 'confirm', + name: 'proceed', + message: 'Would you like to proceed?', + }); + + if (!proceed) { + return; + } + + logger.info('Proceeding with the installation'); + } + } + + const projectName = await getAppName(root); + + const matchingVersion = await getPackageMatchingVersion( + packageName, + version ?? reactNativeVersion, + ); + + logger.log( + `Found matching version ${chalk.cyan(matchingVersion)} for ${chalk.cyan( + packageName, + )}`, + ); + + const loader = getLoader({ + text: `Installing ${packageName}@${matchingVersion}`, + }); + + loader.start(); + + try { + await install([`${packageName}@${matchingVersion}`], { + packageManager: pm, + silent: true, + root, + }); + loader.succeed(); + } catch (e) { + loader.fail(); + throw e; + } + + loader.start('Copying template files'); + const templateSourceDir = mkdtempSync(join(tmpdir(), 'rncli-init-template-')); + await installTemplatePackage( + `${packageName}@${matchingVersion}`, + templateSourceDir, + pm, + ); + + const templateName = getTemplateName(templateSourceDir); + const templateConfig = getTemplateConfig(templateName, templateSourceDir); + + if (!templateConfig.platformName) { + throw new CLIError( + `Template ${templateName} is missing platformName in its template.config.js`, + ); + } + + await copyTemplate( + templateName, + templateConfig.templateDir, + templateSourceDir, + templateConfig.platformName, + ); + + loader.succeed(); + loader.start('Processing template'); + + await changePlaceholderInTemplate({ + projectName, + projectTitle: title, + placeholderName: templateConfig.placeholderName, + placeholderTitle: templateConfig.titlePlaceholder, + projectPath: join(root, templateConfig.platformName), + }); + + loader.succeed(); + + const {postInitScript} = templateConfig; + if (postInitScript) { + logger.debug('Executing post init script '); + await executePostInitScript( + templateName, + postInitScript, + templateSourceDir, + ); + } +} + +export default addPlatform; diff --git a/packages/cli/src/commands/addPlatform/index.ts b/packages/cli/src/commands/addPlatform/index.ts new file mode 100644 index 000000000..c8de652f1 --- /dev/null +++ b/packages/cli/src/commands/addPlatform/index.ts @@ -0,0 +1,23 @@ +import addPlatform from './addPlatform'; + +export default { + func: addPlatform, + name: 'add-platform [packageName]', + description: 'Add new platform to your React Native project.', + options: [ + { + name: '--version ', + description: 'Pass version of the platform to be added to the project.', + }, + { + name: '--pm ', + description: + 'Use specific package manager to initialize the project. Available options: `yarn`, `npm`, `bun`. Default: `yarn`', + default: 'yarn', + }, + { + name: '--title ', + description: 'Uses a custom app title name for application', + }, + ], +}; diff --git a/packages/cli/src/commands/index.ts b/packages/cli/src/commands/index.ts index 306fd5449..6a70b0f27 100644 --- a/packages/cli/src/commands/index.ts +++ b/packages/cli/src/commands/index.ts @@ -3,11 +3,13 @@ import {commands as cleanCommands} from '@react-native-community/cli-clean'; import {commands as doctorCommands} from '@react-native-community/cli-doctor'; import {commands as configCommands} from '@react-native-community/cli-config'; import init from './init'; +import addPlatform from './addPlatform'; export const projectCommands = [ ...configCommands, cleanCommands.clean, doctorCommands.info, + addPlatform, ] as Command[]; export const detachedCommands = [ diff --git a/packages/cli/src/commands/init/editTemplate.ts b/packages/cli/src/commands/init/editTemplate.ts index d3bb39f23..b16b401e6 100644 --- a/packages/cli/src/commands/init/editTemplate.ts +++ b/packages/cli/src/commands/init/editTemplate.ts @@ -13,6 +13,7 @@ interface PlaceholderConfig { placeholderTitle?: string; projectTitle?: string; packageName?: string; + projectPath?: string; } /** @@ -145,11 +146,12 @@ export async function replacePlaceholderWithPackageName({ placeholderName, placeholderTitle, packageName, + projectPath, }: Omit, 'projectTitle'>) { validatePackageName(packageName); const cleanPackageName = packageName.replace(/[^\p{L}\p{N}.]+/gu, ''); - for (const filePath of walk(process.cwd()).reverse()) { + for (const filePath of walk(projectPath).reverse()) { if (shouldIgnoreFile(filePath)) { continue; } @@ -232,6 +234,7 @@ export async function changePlaceholderInTemplate({ placeholderTitle = DEFAULT_TITLE_PLACEHOLDER, projectTitle = projectName, packageName, + projectPath = process.cwd(), }: PlaceholderConfig) { logger.debug(`Changing ${placeholderName} for ${projectName} in template`); @@ -242,12 +245,13 @@ export async function changePlaceholderInTemplate({ placeholderName, placeholderTitle, packageName, + projectPath, }); } catch (error) { throw new CLIError((error as Error).message); } } else { - for (const filePath of walk(process.cwd()).reverse()) { + for (const filePath of walk(projectPath).reverse()) { if (shouldIgnoreFile(filePath)) { continue; } @@ -269,3 +273,14 @@ export async function changePlaceholderInTemplate({ } } } + +export function getTemplateName(cwd: string) { + // We use package manager to infer the name of the template module for us. + // That's why we get it from temporary package.json, where the name is the + // first and only dependency (hence 0). + const name = Object.keys( + JSON.parse(fs.readFileSync(path.join(cwd, './package.json'), 'utf8')) + .dependencies, + )[0]; + return name; +} diff --git a/packages/cli/src/commands/init/git.ts b/packages/cli/src/commands/init/git.ts index 10eee2411..00058dde9 100644 --- a/packages/cli/src/commands/init/git.ts +++ b/packages/cli/src/commands/init/git.ts @@ -68,3 +68,14 @@ export const createGitRepository = async (folder: string) => { ); } }; + +export const isGitTreeDirty = async (folder: string) => { + try { + const {stdout} = await execa('git', ['status', '--porcelain'], { + cwd: folder, + }); + return stdout !== ''; + } catch { + return false; + } +}; diff --git a/packages/cli/src/commands/init/init.ts b/packages/cli/src/commands/init/init.ts index f4da03370..e906945ea 100644 --- a/packages/cli/src/commands/init/init.ts +++ b/packages/cli/src/commands/init/init.ts @@ -19,7 +19,7 @@ import { copyTemplate, executePostInitScript, } from './template'; -import {changePlaceholderInTemplate} from './editTemplate'; +import {changePlaceholderInTemplate, getTemplateName} from './editTemplate'; import * as PackageManager from '../../tools/packageManager'; import banner from './banner'; import TemplateAndVersionError from './errors/TemplateAndVersionError'; @@ -181,17 +181,6 @@ async function setProjectDirectory( return process.cwd(); } -function getTemplateName(cwd: string) { - // We use package manager to infer the name of the template module for us. - // That's why we get it from temporary package.json, where the name is the - // first and only dependency (hence 0). - const name = Object.keys( - JSON.parse(fs.readFileSync(path.join(cwd, './package.json'), 'utf8')) - .dependencies, - )[0]; - return name; -} - //set cache to empty string to prevent installing cocoapods on freshly created project function setEmptyHashForCachedDependencies(projectName: string) { cacheManager.set( diff --git a/packages/cli/src/commands/init/template.ts b/packages/cli/src/commands/init/template.ts index ee2adb537..4253bb3c3 100644 --- a/packages/cli/src/commands/init/template.ts +++ b/packages/cli/src/commands/init/template.ts @@ -1,5 +1,5 @@ import execa from 'execa'; -import path from 'path'; +import path, {join} from 'path'; import {logger, CLIError} from '@react-native-community/cli-tools'; import * as PackageManager from '../../tools/packageManager'; import copyFiles from '../../tools/copyFiles'; @@ -14,6 +14,7 @@ export type TemplateConfig = { templateDir: string; postInitScript?: string; titlePlaceholder?: string; + platformName: string; }; export async function installTemplatePackage( @@ -91,6 +92,7 @@ export async function copyTemplate( templateName: string, templateDir: string, templateSourceDir: string, + platformName: string = '', ) { const templatePath = path.resolve( templateSourceDir, @@ -101,9 +103,13 @@ export async function copyTemplate( logger.debug(`Copying template from ${templatePath}`); let regexStr = path.resolve(templatePath, 'node_modules'); - await copyFiles(templatePath, process.cwd(), { - exclude: [new RegExp(replacePathSepForRegex(regexStr))], - }); + await copyFiles( + join(templatePath, platformName), + join(process.cwd(), platformName), + { + exclude: [new RegExp(replacePathSepForRegex(regexStr))], + }, + ); } export function executePostInitScript( From bbc91404313f69ba0d4a134550d43727c1eca986 Mon Sep 17 00:00:00 2001 From: szymonrybczak Date: Wed, 14 Feb 2024 16:09:52 +0100 Subject: [PATCH 2/7] feat: support multiple platforms --- .../src/commands/addPlatform/addPlatform.ts | 34 +++++++++++-------- .../cli/src/commands/init/editTemplate.ts | 2 +- packages/cli/src/commands/init/template.ts | 14 +++----- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/packages/cli/src/commands/addPlatform/addPlatform.ts b/packages/cli/src/commands/addPlatform/addPlatform.ts index 2da3a7142..b86dc79a9 100644 --- a/packages/cli/src/commands/addPlatform/addPlatform.ts +++ b/packages/cli/src/commands/addPlatform/addPlatform.ts @@ -168,29 +168,33 @@ async function addPlatform( const templateName = getTemplateName(templateSourceDir); const templateConfig = getTemplateConfig(templateName, templateSourceDir); - if (!templateConfig.platformName) { + if (!templateConfig.platforms) { throw new CLIError( - `Template ${templateName} is missing platformName in its template.config.js`, + `Template ${templateName} is missing "platforms" in its "template.config.js"`, ); } - await copyTemplate( - templateName, - templateConfig.templateDir, - templateSourceDir, - templateConfig.platformName, - ); + for (const platform of templateConfig.platforms) { + await copyTemplate( + templateName, + templateConfig.templateDir, + templateSourceDir, + platform, + ); + } loader.succeed(); loader.start('Processing template'); - await changePlaceholderInTemplate({ - projectName, - projectTitle: title, - placeholderName: templateConfig.placeholderName, - placeholderTitle: templateConfig.titlePlaceholder, - projectPath: join(root, templateConfig.platformName), - }); + for (const platform of templateConfig.platforms) { + await changePlaceholderInTemplate({ + projectName, + projectTitle: title, + placeholderName: templateConfig.placeholderName, + placeholderTitle: templateConfig.titlePlaceholder, + projectPath: join(root, platform), + }); + } loader.succeed(); diff --git a/packages/cli/src/commands/init/editTemplate.ts b/packages/cli/src/commands/init/editTemplate.ts index b16b401e6..e90d4f688 100644 --- a/packages/cli/src/commands/init/editTemplate.ts +++ b/packages/cli/src/commands/init/editTemplate.ts @@ -146,7 +146,7 @@ export async function replacePlaceholderWithPackageName({ placeholderName, placeholderTitle, packageName, - projectPath, + projectPath = process.cwd(), }: Omit, 'projectTitle'>) { validatePackageName(packageName); const cleanPackageName = packageName.replace(/[^\p{L}\p{N}.]+/gu, ''); diff --git a/packages/cli/src/commands/init/template.ts b/packages/cli/src/commands/init/template.ts index 4253bb3c3..1fd39ad16 100644 --- a/packages/cli/src/commands/init/template.ts +++ b/packages/cli/src/commands/init/template.ts @@ -14,7 +14,7 @@ export type TemplateConfig = { templateDir: string; postInitScript?: string; titlePlaceholder?: string; - platformName: string; + platforms?: string[]; }; export async function installTemplatePackage( @@ -92,7 +92,7 @@ export async function copyTemplate( templateName: string, templateDir: string, templateSourceDir: string, - platformName: string = '', + platform: string = '', ) { const templatePath = path.resolve( templateSourceDir, @@ -103,13 +103,9 @@ export async function copyTemplate( logger.debug(`Copying template from ${templatePath}`); let regexStr = path.resolve(templatePath, 'node_modules'); - await copyFiles( - join(templatePath, platformName), - join(process.cwd(), platformName), - { - exclude: [new RegExp(replacePathSepForRegex(regexStr))], - }, - ); + await copyFiles(join(templatePath, platform), join(process.cwd(), platform), { + exclude: [new RegExp(replacePathSepForRegex(regexStr))], + }); } export function executePostInitScript( From 428d3bd12d0c4ff083a1457989e8d7dee2e459a4 Mon Sep 17 00:00:00 2001 From: szymonrybczak Date: Fri, 16 Feb 2024 15:43:21 +0100 Subject: [PATCH 3/7] feat: make sure platform isn't already initalized --- packages/cli/src/commands/addPlatform/addPlatform.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/cli/src/commands/addPlatform/addPlatform.ts b/packages/cli/src/commands/addPlatform/addPlatform.ts index b86dc79a9..c3c9fdbe6 100644 --- a/packages/cli/src/commands/addPlatform/addPlatform.ts +++ b/packages/cli/src/commands/addPlatform/addPlatform.ts @@ -24,6 +24,7 @@ import { } from '../init/template'; import {tmpdir} from 'os'; import {mkdtempSync} from 'graceful-fs'; +import {existsSync} from 'fs'; type Options = { packageName: string; @@ -175,6 +176,15 @@ async function addPlatform( } for (const platform of templateConfig.platforms) { + if (existsSync(join(root, platform))) { + throw new CLIError( + `Platform ${platform} already exists in the project. Directory ${join( + root, + platform, + )} is not empty.`, + ); + } + await copyTemplate( templateName, templateConfig.templateDir, From 4827e7f78435a123cd745c36f37364473d84a282 Mon Sep 17 00:00:00 2001 From: szymonrybczak Date: Sun, 25 Feb 2024 13:27:23 +0100 Subject: [PATCH 4/7] chore: add try-catch blocks --- .../cli/src/commands/addPlatform/addPlatform.ts | 14 ++++++++++++-- packages/cli/src/commands/init/editTemplate.ts | 16 ++++++++++++---- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/packages/cli/src/commands/addPlatform/addPlatform.ts b/packages/cli/src/commands/addPlatform/addPlatform.ts index c3c9fdbe6..a296fbfad 100644 --- a/packages/cli/src/commands/addPlatform/addPlatform.ts +++ b/packages/cli/src/commands/addPlatform/addPlatform.ts @@ -43,13 +43,23 @@ const getAppName = async (root: string) => { throw new CLIError(`Unable to find package.json inside ${root}`); } - let {name} = JSON.parse(readFileSync(pkgJsonPath, 'utf8')); + let name; + + try { + name = JSON.parse(readFileSync(pkgJsonPath, 'utf8')); + } catch (e) { + throw new CLIError(`Failed to read ${pkgJsonPath} file.`, e as Error); + } if (!name) { const appJson = join(root, 'app.json'); if (appJson) { logger.log(`Reading ${chalk.cyan('name')} from app.json…`); - name = JSON.parse(readFileSync(appJson, 'utf8')).name; + try { + name = JSON.parse(readFileSync(appJson, 'utf8')).name; + } catch (e) { + throw new CLIError(`Failed to read ${pkgJsonPath} file.`, e as Error); + } } if (!name) { diff --git a/packages/cli/src/commands/init/editTemplate.ts b/packages/cli/src/commands/init/editTemplate.ts index e90d4f688..9692a4387 100644 --- a/packages/cli/src/commands/init/editTemplate.ts +++ b/packages/cli/src/commands/init/editTemplate.ts @@ -278,9 +278,17 @@ export function getTemplateName(cwd: string) { // We use package manager to infer the name of the template module for us. // That's why we get it from temporary package.json, where the name is the // first and only dependency (hence 0). - const name = Object.keys( - JSON.parse(fs.readFileSync(path.join(cwd, './package.json'), 'utf8')) - .dependencies, - )[0]; + let name; + try { + name = Object.keys( + JSON.parse(fs.readFileSync(path.join(cwd, './package.json'), 'utf8')) + .dependencies, + )[0]; + } catch { + throw new CLIError( + 'Failed to read template name from package.json. Please make sure that the template you are using has a valid package.json file.', + ); + } + return name; } From 7fbb12dfcc0fb1136e7b78263fb9f3beea26c8c3 Mon Sep 17 00:00:00 2001 From: szymonrybczak Date: Wed, 28 Aug 2024 15:36:56 +0200 Subject: [PATCH 5/7] fix: map package name to template name --- .../src/commands/addPlatform/addPlatform.ts | 55 ++++++++++++++----- 1 file changed, 41 insertions(+), 14 deletions(-) diff --git a/packages/cli/src/commands/addPlatform/addPlatform.ts b/packages/cli/src/commands/addPlatform/addPlatform.ts index a296fbfad..324e3a195 100644 --- a/packages/cli/src/commands/addPlatform/addPlatform.ts +++ b/packages/cli/src/commands/addPlatform/addPlatform.ts @@ -12,10 +12,7 @@ import {install, PackageManager} from './../../tools/packageManager'; import npmFetch from 'npm-registry-fetch'; import semver from 'semver'; import {checkGitInstallation, isGitTreeDirty} from '../init/git'; -import { - changePlaceholderInTemplate, - getTemplateName, -} from '../init/editTemplate'; +import {changePlaceholderInTemplate} from '../init/editTemplate'; import { copyTemplate, executePostInitScript, @@ -46,7 +43,7 @@ const getAppName = async (root: string) => { let name; try { - name = JSON.parse(readFileSync(pkgJsonPath, 'utf8')); + name = JSON.parse(readFileSync(pkgJsonPath, 'utf8')).name; } catch (e) { throw new CLIError(`Failed to read ${pkgJsonPath} file.`, e as Error); } @@ -105,6 +102,19 @@ const getPackageMatchingVersion = async ( ); }; +// From React Native 0.75 template is not longer inside `react-native` core, +// so we need to map package name (fork) to template name + +const getTemplateNameFromPackageName = (packageName: string) => { + switch (packageName) { + case '@callstack/react-native-visionos': + case 'react-native-visionos': + return '@callstack/visionos-template'; + default: + return packageName; + } +}; + async function addPlatform( [packageName]: string[], {root, reactNativeVersion}: Config, @@ -114,6 +124,7 @@ async function addPlatform( throw new CLIError('Please provide package name e.g. react-native-macos'); } + const templateName = getTemplateNameFromPackageName(packageName); const isGitAvailable = await checkGitInstallation(); if (isGitAvailable) { @@ -163,20 +174,35 @@ async function addPlatform( root, }); loader.succeed(); - } catch (e) { + } catch (error) { loader.fail(); - throw e; + throw new CLIError( + `Failed to install package ${packageName}@${matchingVersion}`, + (error as Error).message, + ); } - loader.start('Copying template files'); + loader.start(`Installing template packages from ${templateName}@0${version}`); + const templateSourceDir = mkdtempSync(join(tmpdir(), 'rncli-init-template-')); - await installTemplatePackage( - `${packageName}@${matchingVersion}`, - templateSourceDir, - pm, - ); - const templateName = getTemplateName(templateSourceDir); + try { + await installTemplatePackage( + `${templateName}@0${version}`, + templateSourceDir, + pm, + ); + loader.succeed(); + } catch (error) { + loader.fail(); + throw new CLIError( + `Failed to install template packages from ${templateName}@0${version}`, + (error as Error).message, + ); + } + + loader.start('Copying template files'); + const templateConfig = getTemplateConfig(templateName, templateSourceDir); if (!templateConfig.platforms) { @@ -187,6 +213,7 @@ async function addPlatform( for (const platform of templateConfig.platforms) { if (existsSync(join(root, platform))) { + loader.fail(); throw new CLIError( `Platform ${platform} already exists in the project. Directory ${join( root, From 609aaa44d24388965a6737fb5a3a3c448331b158 Mon Sep 17 00:00:00 2001 From: szymonrybczak Date: Thu, 29 Aug 2024 10:43:27 +0200 Subject: [PATCH 6/7] fix: do not hardcode npm registry --- packages/cli/src/commands/addPlatform/addPlatform.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/commands/addPlatform/addPlatform.ts b/packages/cli/src/commands/addPlatform/addPlatform.ts index 324e3a195..0f9c4eaf3 100644 --- a/packages/cli/src/commands/addPlatform/addPlatform.ts +++ b/packages/cli/src/commands/addPlatform/addPlatform.ts @@ -22,6 +22,7 @@ import { import {tmpdir} from 'os'; import {mkdtempSync} from 'graceful-fs'; import {existsSync} from 'fs'; +import {getNpmRegistryUrl} from '../../tools/npm'; type Options = { packageName: string; @@ -30,7 +31,7 @@ type Options = { title: string; }; -const NPM_REGISTRY_URL = 'http://registry.npmjs.org'; // TODO: Support local registry +const NPM_REGISTRY_URL = getNpmRegistryUrl(); const getAppName = async (root: string) => { logger.log(`Reading ${chalk.cyan('name')} from package.json…`); From 26ec2e1530ff063121bbcf9f400da8b75d7be913 Mon Sep 17 00:00:00 2001 From: szymonrybczak Date: Thu, 29 Aug 2024 10:52:46 +0200 Subject: [PATCH 7/7] fix: install the same version of template as package --- packages/cli/src/commands/addPlatform/addPlatform.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/cli/src/commands/addPlatform/addPlatform.ts b/packages/cli/src/commands/addPlatform/addPlatform.ts index 0f9c4eaf3..5488e3640 100644 --- a/packages/cli/src/commands/addPlatform/addPlatform.ts +++ b/packages/cli/src/commands/addPlatform/addPlatform.ts @@ -183,13 +183,15 @@ async function addPlatform( ); } - loader.start(`Installing template packages from ${templateName}@0${version}`); + loader.start( + `Installing template packages from ${templateName}@0${matchingVersion}`, + ); const templateSourceDir = mkdtempSync(join(tmpdir(), 'rncli-init-template-')); try { await installTemplatePackage( - `${templateName}@0${version}`, + `${templateName}@0${matchingVersion}`, templateSourceDir, pm, ); @@ -197,7 +199,7 @@ async function addPlatform( } catch (error) { loader.fail(); throw new CLIError( - `Failed to install template packages from ${templateName}@0${version}`, + `Failed to install template packages from ${templateName}@0${matchingVersion}`, (error as Error).message, ); }