#!/usr/bin/env node 'use strict'; const fs = require('fs'); const glob = require('glob'); const path = require('path'); function replaceAll(current, replacement, content) { const regexp = new RegExp(escapeRegExp('${' + current + '}'), 'g'); return content.replace(regexp, replacement); } function escapeRegExp(string) { return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } function removeUnapplicableSections(name, content) { const opener = ``; const closer = ``; let nextContent = content; while (nextContent.includes(opener)) { const openIndex = nextContent.indexOf(opener); let closeIndex = nextContent.indexOf(closer); if (!closeIndex) { console.warn(`Can't find section closer for ${name}`); closeIndex = openIndex + opener.length; } const section = nextContent.substring(openIndex, closeIndex + closer.length); nextContent = nextContent.replace(section, ''); } return nextContent; } function removeOptionals(content) { const regexp = new RegExp(escapeRegExp('') + '\n', 'g'); return content.replace(regexp, ''); } const DEFAULT_HOMEPAGE = 'https://docs.expo.dev/versions/latest/'; function generateREADME() { const template = path.join(__dirname, '..', 'templates', 'README.md'); let readme = fs.readFileSync(template, 'utf8'); const pkg = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'package.json'), 'utf8')); const packageName = pkg.name; const homepage = pkg.homepage || DEFAULT_HOMEPAGE; const description = pkg.description || 'Put a description of your Unimodule here'; // This isn't really accurate, but these all behave the same as far as READMEs go const isInterface = packageName.endsWith('-interface') || packageName.endsWith('-provider'); const isAndroid = fs.existsSync(path.join(process.cwd(), 'android')); const isIOS = fs.existsSync(path.join(process.cwd(), 'ios')); // Search in docs/versions/unversioned/sdk/*.md - can't find it? Ask what it should be and initialize for them readme = replaceAll('packageName', packageName, readme); readme = replaceAll('description', description, readme); if (isIOS) { const podspecs = glob.sync('{ios/**/,}*.podspec'); const podspecPath = podspecs[0]; const podName = (() => { const parts = podspecPath.split('/'); return parts[parts.length - 1].replace('.podspec', ''); })(); readme = replaceAll('podName', podName, readme); } else { readme = removeUnapplicableSections('no-ios', readme); } if (isInterface) { readme = removeUnapplicableSections('interfaces', readme); } else { let docName; if (homepage === DEFAULT_HOMEPAGE || homepage.startsWith('https://github.com')) { docName = packageName.replace('expo-', ''); console.warn( `Tried to guess the docs homepage for ${packageName}; add it under the "homepage" entry in package.json` ); } else { docName = (() => { const parts = homepage.split('/'); // Handle both with and without trailing slash let name = parts[parts.length - 1]; if (name === '') { name = parts[parts.length - 2]; } return name; })(); } readme = replaceAll('docName', docName, readme); if (isAndroid) { const androidPackages = glob.sync('android/src/**/*Package.{java,kt}'); let androidPackage; let androidPackagePath = 'expo.modules.yourModule.YourPackage'; let androidPackageName = 'YourPackage'; if (!androidPackages) { console.warn( `No Android package classes found, using: ${androidPackageName} - be sure to update this` ); } else { androidPackage = androidPackages[0]; if (androidPackages.length > 1) { console.warn('Found multiple packages, which one should we use?'); androidPackage = androidPackages[0]; } if (androidPackage) { // From: android/src/main/java/expo/modules/localization/LocalizationPackage.java' // To: expo.modules.localization.LocalizationPackage androidPackagePath = androidPackage .split('/java/')[1] .replace(/\//g, '.') .replace('.java', ''); androidPackageName = (() => { const parts = androidPackagePath.split('.'); return parts[parts.length - 1]; })(); readme = replaceAll('androidPackageName', androidPackageName, readme); } else { readme = removeUnapplicableSections('no-package', readme); } } readme = replaceAll('androidPackagePath', androidPackagePath, readme); } else { readme = removeUnapplicableSections('no-android', readme); } } return removeOptionals(readme); } const readme = generateREADME(); const readmePath = path.join(process.cwd(), 'README.md'); if (fs.existsSync(readmePath)) { console.log('expo-module-scripts: README.md exists, not updating'); } else { fs.writeFileSync(readmePath, readme, { encoding: 'utf8' }); }