1#!/usr/bin/env node
2'use strict';
3
4const fs = require('fs');
5const glob = require('glob');
6const path = require('path');
7
8function replaceAll(current, replacement, content) {
9 const regexp = new RegExp(escapeRegExp('${' + current + '}'), 'g');
10 return content.replace(regexp, replacement);
11}
12
13function escapeRegExp(string) {
14 return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
15}
16
17function removeUnapplicableSections(name, content) {
18 const opener = `<!--- remove for ${name} --->`;
19 const closer = `<!--- end remove for ${name} --->`;
20 let nextContent = content;
21
22 while (nextContent.includes(opener)) {
23 const openIndex = nextContent.indexOf(opener);
24 let closeIndex = nextContent.indexOf(closer);
25 if (!closeIndex) {
26 console.warn(`Can't find section closer for ${name}`);
27 closeIndex = openIndex + opener.length;
28 }
29 const section = nextContent.substring(openIndex, closeIndex + closer.length);
30 nextContent = nextContent.replace(section, '');
31 }
32 return nextContent;
33}
34
35function removeOptionals(content) {
36 const regexp = new RegExp(escapeRegExp('<!---') + '.*' + escapeRegExp('--->') + '\n', 'g');
37 return content.replace(regexp, '');
38}
39
40const DEFAULT_HOMEPAGE = 'https://docs.expo.dev/versions/latest/';
41
42function generateREADME() {
43 const template = path.join(__dirname, '..', 'templates', 'README.md');
44 let readme = fs.readFileSync(template, 'utf8');
45 const pkg = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'package.json'), 'utf8'));
46 const packageName = pkg.name;
47 const homepage = pkg.homepage || DEFAULT_HOMEPAGE;
48 const description = pkg.description || 'Put a description of your Unimodule here';
49
50 // This isn't really accurate, but these all behave the same as far as READMEs go
51 const isInterface = packageName.endsWith('-interface') || packageName.endsWith('-provider');
52
53 const isAndroid = fs.existsSync(path.join(process.cwd(), 'android'));
54 const isIOS = fs.existsSync(path.join(process.cwd(), 'ios'));
55
56 // Search in docs/versions/unversioned/sdk/*.md - can't find it? Ask what it should be and initialize for them
57 readme = replaceAll('packageName', packageName, readme);
58 readme = replaceAll('description', description, readme);
59
60 if (isIOS) {
61 const podspecs = glob.sync('{ios/**/,}*.podspec');
62 const podspecPath = podspecs[0];
63 const podName = (() => {
64 const parts = podspecPath.split('/');
65 return parts[parts.length - 1].replace('.podspec', '');
66 })();
67
68 readme = replaceAll('podName', podName, readme);
69 } else {
70 readme = removeUnapplicableSections('no-ios', readme);
71 }
72
73 if (isInterface) {
74 readme = removeUnapplicableSections('interfaces', readme);
75 } else {
76 let docName;
77 if (homepage === DEFAULT_HOMEPAGE || homepage.startsWith('https://github.com')) {
78 docName = packageName.replace('expo-', '');
79 console.warn(
80 `Tried to guess the docs homepage for ${packageName}; add it under the "homepage" entry in package.json`
81 );
82 } else {
83 docName = (() => {
84 const parts = homepage.split('/');
85 // Handle both with and without trailing slash
86 let name = parts[parts.length - 1];
87 if (name === '') {
88 name = parts[parts.length - 2];
89 }
90 return name;
91 })();
92 }
93 readme = replaceAll('docName', docName, readme);
94 if (isAndroid) {
95 const androidPackages = glob.sync('android/src/**/*Package.{java,kt}');
96 let androidPackage;
97 let androidPackagePath = 'expo.modules.yourModule.YourPackage';
98 let androidPackageName = 'YourPackage';
99 if (!androidPackages) {
100 console.warn(
101 `No Android package classes found, using: ${androidPackageName} - be sure to update this`
102 );
103 } else {
104 androidPackage = androidPackages[0];
105 if (androidPackages.length > 1) {
106 console.warn('Found multiple packages, which one should we use?');
107 androidPackage = androidPackages[0];
108 }
109
110 if (androidPackage) {
111 // From: android/src/main/java/expo/modules/localization/LocalizationPackage.java'
112 // To: expo.modules.localization.LocalizationPackage
113 androidPackagePath = androidPackage
114 .split('/java/')[1]
115 .replace(/\//g, '.')
116 .replace('.java', '');
117 androidPackageName = (() => {
118 const parts = androidPackagePath.split('.');
119 return parts[parts.length - 1];
120 })();
121
122 readme = replaceAll('androidPackageName', androidPackageName, readme);
123 } else {
124 readme = removeUnapplicableSections('no-package', readme);
125 }
126 }
127
128 readme = replaceAll('androidPackagePath', androidPackagePath, readme);
129 } else {
130 readme = removeUnapplicableSections('no-android', readme);
131 }
132 }
133 return removeOptionals(readme);
134}
135
136const readme = generateREADME();
137const readmePath = path.join(process.cwd(), 'README.md');
138if (fs.existsSync(readmePath)) {
139 console.log('expo-module-scripts: README.md exists, not updating');
140} else {
141 fs.writeFileSync(readmePath, readme, { encoding: 'utf8' });
142}
143