UNPKG

create-react-native-module

Version:

Tool to create a React Native library module or view module with a single command

269 lines (239 loc) 8.86 kB
const outdent = require('outdent'); module.exports = [{ // only needed in case of `exampleFileLinkage: true`: name: ({ exampleFileLinkage }) => exampleFileLinkage ? 'scripts/examples_postinstall.js' : undefined, content: ({ exampleName }) => `#!/usr/bin/env node /* * Using libraries within examples and linking them within packages.json like: * "react-native-library-name": "file:../" * will cause problems with the metro bundler if the example will run via * \`react-native run-[ios|android]\`. This will result in an error as the metro * bundler will find multiple versions for the same module while resolving it. * The reason for that is that if the library is installed it also copies in the * example folder itself as well as the node_modules folder of the library * although their are defined in .npmignore and should be ignored in theory. * * This postinstall script removes the node_modules folder as well as all * entries from the libraries .npmignore file within the examples node_modules * folder after the library was installed. This should resolve the metro * bundler issue mentioned above. * * It is expected this scripts lives in the libraries root folder within a * scripts folder. As first parameter the relative path to the libraries * folder within the example's node_modules folder may be provided. * This script will determine the path from this project's package.json file * if no such relative path is provided. * An example's package.json entry could look like: * "postinstall": "node ../scripts/examples_postinstall.js node_modules/react-native-library-name/" */ 'use strict'; const fs = require('fs'); const path = require('path'); /// Delete all files and directories for the given path const removeFileDirectoryRecursively = fileDirPath => { // Remove file if (!fs.lstatSync(fileDirPath).isDirectory()) { fs.unlinkSync(fileDirPath); return; } // Go down the directory an remove each file / directory recursively fs.readdirSync(fileDirPath).forEach(entry => { const entryPath = path.join(fileDirPath, entry); removeFileDirectoryRecursively(entryPath); }); fs.rmdirSync(fileDirPath); }; /// Remove ${exampleName}/node_modules/react-native-library-name/node_modules directory const removeLibraryNodeModulesPath = (libraryNodeModulesPath) => { const nodeModulesPath = path.resolve(libraryNodeModulesPath, 'node_modules') if (!fs.existsSync(nodeModulesPath)) { console.log(\`No node_modules path found at \${nodeModulesPath}. Skipping delete.\`) return; } console.log(\`Deleting: \${nodeModulesPath}\`) try { removeFileDirectoryRecursively(nodeModulesPath); console.log(\`Successfully deleted: \${nodeModulesPath}\`) } catch (err) { console.log(\`Error deleting \${nodeModulesPath}: \${err.message}\`); } }; /// Remove all entries from the .npmignore within ${exampleName}/node_modules/react-native-library-name/ const removeLibraryNpmIgnorePaths = (npmIgnorePath, libraryNodeModulesPath) => { if (!fs.existsSync(npmIgnorePath)) { console.log(\`No .npmignore path found at \${npmIgnorePath}. Skipping deleting content.\`); return; } fs.readFileSync(npmIgnorePath, 'utf8').split(/\\r?\\n/).forEach(entry => { if (entry.length === 0) { return } const npmIgnoreLibraryNodeModulesEntryPath = path.resolve(libraryNodeModulesPath, entry); if (!fs.existsSync(npmIgnoreLibraryNodeModulesEntryPath)) { return; } console.log(\`Deleting: \${npmIgnoreLibraryNodeModulesEntryPath}\`) try { removeFileDirectoryRecursively(npmIgnoreLibraryNodeModulesEntryPath); console.log(\`Successfully deleted: \${npmIgnoreLibraryNodeModulesEntryPath}\`) } catch (err) { console.log(\`Error deleting \${npmIgnoreLibraryNodeModulesEntryPath}: \${err.message}\`); } }); }; // Main start sweeping process (() => { // Read out dir of example project const exampleDir = process.cwd(); console.log(\`Starting postinstall cleanup for \${exampleDir}\`); // Resolve the React Native library's path within the example's node_modules directory const libraryNodeModulesPath = process.argv.length > 2 ? path.resolve(exampleDir, process.argv[2]) : path.resolve(exampleDir, 'node_modules', require('../package.json').name); console.log(\`Removing unwanted artifacts for \${libraryNodeModulesPath}\`); removeLibraryNodeModulesPath(libraryNodeModulesPath); const npmIgnorePath = path.resolve(__dirname, '../.npmignore'); removeLibraryNpmIgnorePaths(npmIgnorePath, libraryNodeModulesPath); })(); ` }, { // metro.config.js workarounds needed in case of `exampleFileLinkage: false`: name: ({ exampleName, exampleFileLinkage }) => exampleFileLinkage ? undefined : `${exampleName}/metro.config.js`, content: ({ packageName, exampleName }) => `// metro.config.js // // with multiple workarounds for this issue with symlinks: // https://github.com/facebook/metro/issues/1 // // with thanks to @johnryan (<https://github.com/johnryan>) // for the pointers to multiple workaround solutions here: // https://github.com/facebook/metro/issues/1#issuecomment-541642857 // // see also this discussion: // https://github.com/brodybits/create-react-native-module/issues/232 const path = require('path') module.exports = { // workaround for an issue with symlinks encountered starting with // metro@0.55 / React Native 0.61 // (not needed with React Native 0.60 / metro@0.54) resolver: { extraNodeModules: new Proxy( {}, { get: (_, name) => path.resolve('.', 'node_modules', name) } ) }, // quick workaround for another issue with symlinks watchFolders: [path.resolve('.'), path.resolve('..')] } `, }, { name: ({ exampleName, writeExamplePodfile }) => writeExamplePodfile ? `${exampleName}/ios/Podfile` : undefined, content: ({ packageName, exampleName }) => outdent({ trimTrailingNewline: false })` platform :ios, '10.0' target '${exampleName}' do rn_path = '../node_modules/react-native' pod 'yoga', path: "#{rn_path}/ReactCommon/yoga/yoga.podspec" pod 'DoubleConversion', :podspec => "#{rn_path}/third-party-podspecs/DoubleConversion.podspec" pod 'Folly', :podspec => "#{rn_path}/third-party-podspecs/Folly.podspec" pod 'glog', :podspec => "#{rn_path}/third-party-podspecs/GLog.podspec" pod 'React', path: rn_path, subspecs: [ 'Core', 'CxxBridge', 'RCTAnimation', 'RCTActionSheet', 'RCTImage', 'RCTLinkingIOS', 'RCTNetwork', 'RCTSettings', 'RCTText', 'RCTVibration', 'RCTWebSocket', 'RCTPushNotification', 'RCTCameraRoll', 'RCTSettings', 'RCTBlob', 'RCTGeolocation', 'DevSupport' ] pod '${packageName}', :path => '../../${packageName}.podspec' end `, }, { name: ({ exampleName }) => `${exampleName}/App.js`, content: ({ packageName, objectClassName, isView }) => `/** * Sample React Native App * * adapted from App.js generated by the following command: * * react-native init example * * https://github.com/facebook/react-native */ import React, { Component } from 'react'; import { Platform, StyleSheet, Text, View } from 'react-native'; import ${objectClassName} from '${packageName}';` + (!isView ? ` export default class App extends Component<{}> { state = { status: 'starting', message: '--' }; componentDidMount() { ${objectClassName}.sampleMethod('Testing', 123, (message) => { this.setState({ status: 'native callback received', message }); }); } render() { return ( <View style={styles.container}> <Text style={styles.welcome}>☆${objectClassName} example☆</Text> <Text style={styles.instructions}>STATUS: {this.state.status}</Text> <Text style={styles.welcome}>☆NATIVE CALLBACK MESSAGE☆</Text> <Text style={styles.instructions}>{this.state.message}</Text> </View> ); } }` : ` export default class App extends Component<{}> { render() { return ( <View style={styles.container}> <Text style={styles.welcome}>☆${objectClassName} example☆</Text> <Text style={styles.instructions}>STATUS: loaded</Text> <Text style={styles.welcome}>☆☆☆</Text> <${objectClassName} /> </View> ); } }`) + ` const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#F5FCFF', }, welcome: { fontSize: 20, textAlign: 'center', margin: 10, }, instructions: { textAlign: 'center', color: '#333333', marginBottom: 5, }, }); ` }];