diff --git a/MAINTAINER.md b/MAINTAINER.md index a941f09..85888cf 100644 --- a/MAINTAINER.md +++ b/MAINTAINER.md @@ -23,33 +23,20 @@ PATCH: bugfix only. For `expo` test app -- `cd ..` -- `expo init expo-example -t blank` -- `cd expo-example` -- `yarn add file:../react-native-hcaptcha` -- `yarn add react-native-modal react-native-webview` -- `cp ../react-native-hcaptcha/Example.App.js App.js` -- `yarn android` +- `cd react-native-hcaptcha` +- `yarn example --expo +- `yarn android` or `npm run android` For `react-native` test app -- `cd ..` -- `react-native init rnexample` or `react-native init rnexample --version 0.63.4` for specific version -- `cd rnexample` -- `yarn add file:../react-native-hcaptcha` -- `yarn add react-native-modal react-native-webview` -- `cp ../react-native-hcaptcha/Example.App.js App.js` -- `yarn android` +- `cd react-native-hcaptcha` +- `yarn example` +- `yarn android` or `npm run android` For iOS instead the last step do: -- `pushd ios; pod install; popd` -- `yarn ios` - -To quickly update `react-native-hcaptcha` locally just run: - -`yarn upgrade file:../react-native-hcaptcha` - +- `pushd ios; env USE_HERMES=0 pod install; popd` +- `yarn ios` or `npm run ios` ### Known issues @@ -145,4 +132,27 @@ ERROR Invariant Violation: No callback found with cbID xxxxx and callID yyyyy Solution: delete `node_modules` in `react-native-hcaptcha`. -This issue is related to mismatched `react-native` versions in the test app vs. `react-native-hcaptcha` \ No newline at end of file +This issue is related to mismatched `react-native` versions in the test app vs. `react-native-hcaptcha` + +--- + +Problem: + +Xcode failed to build with: + +> ... +> Framework 'hermes' not found + +Solution: `env USE_HERMES=0 pod install` or add `:hermes_enabled => false` into `use_react_native!` call in `ios/Podfile` + +--- + +Problem: +``` +> yarn add file:../react-native-hcaptcha +Usage Error: The file:../react-native-hcaptcha string didn't match the required format (package-name@range). Did you perhaps forget to explicitly reference the package name? +``` + +Solution: `yarn add ../react-native-hcaptcha` + +Yarn 2.10.x and above doesn't require `file:` scheme prefix https://stackoverflow.com/questions/40102686/how-to-install-package-with-local-path-by-yarn-it-couldnt-find-package \ No newline at end of file diff --git a/__scripts__/generate-example.js b/__scripts__/generate-example.js new file mode 100644 index 0000000..1c6a8cf --- /dev/null +++ b/__scripts__/generate-example.js @@ -0,0 +1,168 @@ +#!/usr/bin/env node + +const { execSync, spawnSync } = require('child_process'); +const { platform } = require('os'); +const fs = require('fs'); +const path = require('path'); + +// Function to display help message +function showHelp() { + console.log(` +Usage: ${path.basename(process.argv[1])} [options] + +Options: + --expo Use expo-cli (default: false) + --template Specify the project template + --name Specify the project name (required) + -h, --help Show this help message +`); + process.exit(1); +} + +// Simple argument parser function +function parseArgs(args) { + const options = { + cliName: 'react-native', + projectName: 'react_native_hcaptcha_example', + projectRelativeProjectPath: '../react-native-hcaptcha-example', + packageManager: 'yarn', // npm is broken https://github.com/facebook/react-native/issues/29977 + projectTemplate: undefined, + frameworkVersion: undefined, + }; + + const argHandlers = { + '--expo': () => options.cliName = 'expo', + '--template': (value) => options.projectTemplate = value, + '--name': (value) => { + options.projectName = value.replace(/[^a-zA-Z0-9]/g, '_'); + options.projectRelativeProjectPath = path.join('..', value); + }, + '-h': showHelp, + '--help': showHelp + }; + + for (let i = 2; i < args.length; i++) { + const arg = args[i]; + + if (argHandlers[arg]) { + const handler = argHandlers[arg]; + if (typeof handler === 'function') { + const nextArg = args[i + 1]; + // Check if the next argument is not another flag + if (typeof handler === 'function' && (nextArg && !nextArg.startsWith('--'))) { + handler(nextArg); + i++; // Skip next argument as it is consumed + } else if (handler === showHelp) { + handler(); + } else { + console.error(`Error: ${arg} requires a value.`); + showHelp(); + } + } + } else { + console.error(`Unknown option: ${arg}`); + showHelp(); + } + } + + if (!options.projectName) { + console.error('Error: --name is required.'); + showHelp(); + } + + return options; +} + +function cleanupEnvPathNode() { + process.env.PATH = process.env.PATH.split(':') + .filter(dir => !dir.includes('node_modules/.bin')) + .join(':'); + console.log(`env=${JSON.stringify(process.env)}`); +} + +// Function to build the create command +function buildCreateCommand({ cliName, projectRelativeProjectPath, projectName, projectTemplate, packageManager, frameworkVersion }) { + let createCommand = [cliName, 'init', projectName, '--directory', projectRelativeProjectPath]; + + if (projectTemplate) { + createCommand.push('--template', projectTemplate); + } + + if (cliName === 'expo') { + createCommand.push(`--${packageManager}`); + } else if (cliName.includes('react-native')) { + createCommand.push('--pm', packageManager); + + if (frameworkVersion) { + createCommand.push('--version', frameworkVersion); + } + + createCommand.push('--skip-install'); + } else { + console.error('Error: unsupporte cliName'); + showHelp(); + } + + createCommand.push('--verbose'); + + return createCommand; +} + +// Function to check if hCaptcha is linked +function checkHcaptchaLinked() { + // + return false; +} + +// Main function that takes parsed arguments and runs the necessary setup +function main({ cliName, projectRelativeProjectPath, projectName, projectTemplate, packageManager, frameworkVersion }) { + cleanupEnvPathNode(); + + console.warn(`Warning! Example project will be generated in '${path.dirname(__dirname)}' directory`); + + // Build the command to initialize the project + const createCommand = buildCreateCommand({ cliName, projectRelativeProjectPath, projectName, projectTemplate, packageManager, frameworkVersion }); + + // Run the project initialization command + console.log(`Running command: ${createCommand}`); + execSync(createCommand.join(' '), { stdio: 'inherit', shell: true }); + + const projectPath = path.join(process.cwd(), projectRelativeProjectPath); + const packageManagerOptions = { stdio: 'inherit', cwd: projectPath }; + + // Copy App.js + fs.readdirSync(projectPath) + .filter(file => file.startsWith('App.')) + .forEach(file => fs.unlinkSync(path.join(projectPath, file))); + fs.copyFileSync('Example.App.js', `${projectPath}/App.js`); + + // Install dependencies + const isHcaptchaLinked = checkHcaptchaLinked(); + const mainPackage = '@hcaptcha/react-native-hcaptcha' + const mainPackagePath = `${path.dirname(projectRelativeProjectPath)}/react-native-hcaptcha` + const peerPackages = 'react-native-modal react-native-webview'; + + console.warn('Installing dependencies...'); + if (packageManager === 'yarn') { + execSync(`yarn add ${mainPackagePath}`, packageManagerOptions); + execSync(`yarn add ${peerPackages}`, packageManagerOptions); + } else { + execSync(`npm i --save ${mainPackagePath}`, packageManagerOptions); + execSync(`npm i --save ${peerPackages}`, packageManagerOptions); + } + + if (isHcaptchaLinked) { + execSync(`${packageManager} link ${mainPackage}`, packageManagerOptions); + } + + // Handle iOS-specific setup if running on macOS + if (platform() === 'darwin') { + const podOptions = { stdio: 'inherit', cwd: path.join(projectPath, 'ios'), env: { ...process.env, USE_HERMES: 0 } }; + execSync('bundle install', podOptions); + execSync('bundle exec pod install', podOptions); + } + + console.log('Setup complete.'); +} + +main(parseArgs(process.argv)); \ No newline at end of file diff --git a/package.json b/package.json index 2b9a7ff..9171d3e 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,8 @@ "description": "hCaptcha Library for React Native (both Android and iOS)", "main": "index.js", "scripts": { - "test": "jest" + "test": "jest", + "example": "node __scripts__/generate-example.js --name react-native-hcaptcha-example" }, "jest": { "preset": "react-native",