react-native-legal 1.0.0-next.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +113 -0
  3. package/ReactNativeLegal.podspec +46 -0
  4. package/android/build.gradle +75 -0
  5. package/android/react-native-helpers.gradle +45 -0
  6. package/android/src/main/java/com/reactnativelegal/ReactNativeLegalActivity.kt +98 -0
  7. package/android/src/main/java/com/reactnativelegal/ReactNativeLegalModuleImpl.kt +19 -0
  8. package/android/src/main/java/com/reactnativelegal/ReactNativeLegalTurboPackage.kt +38 -0
  9. package/android/src/main/res/layout/activity_licenses.xml +28 -0
  10. package/android/src/main/res/values/styles.xml +7 -0
  11. package/android/src/main/res/values-v29/styles.xml +8 -0
  12. package/android/src/newarch/java/com/reactnativelegal/ReactNativeLegalModule.kt +17 -0
  13. package/android/src/oldarch/java/com/reactnativelegal/ReactNativeLegalModule.kt +23 -0
  14. package/app.plugin.js +1 -0
  15. package/bare-plugin/build/android/addListActivity.d.ts +4 -0
  16. package/bare-plugin/build/android/addListActivity.js +22 -0
  17. package/bare-plugin/build/android/androidCommand.d.ts +7 -0
  18. package/bare-plugin/build/android/androidCommand.js +25 -0
  19. package/bare-plugin/build/android/applyAndConfigureAboutLibrariesPlugin.d.ts +6 -0
  20. package/bare-plugin/build/android/applyAndConfigureAboutLibrariesPlugin.js +27 -0
  21. package/bare-plugin/build/android/declareAboutLibrariesPlugin.d.ts +6 -0
  22. package/bare-plugin/build/android/declareAboutLibrariesPlugin.js +27 -0
  23. package/bare-plugin/build/android/utils.d.ts +14 -0
  24. package/bare-plugin/build/android/utils.js +37 -0
  25. package/bare-plugin/build/index.d.ts +2 -0
  26. package/bare-plugin/build/index.js +9 -0
  27. package/bare-plugin/build/ios/addSettingsBundle.d.ts +4 -0
  28. package/bare-plugin/build/ios/addSettingsBundle.js +27 -0
  29. package/bare-plugin/build/ios/iosCommand.d.ts +7 -0
  30. package/bare-plugin/build/ios/iosCommand.js +23 -0
  31. package/bare-plugin/build/ios/registerLicensePlistBuildPhase.d.ts +7 -0
  32. package/bare-plugin/build/ios/registerLicensePlistBuildPhase.js +24 -0
  33. package/bare-plugin/build/ios/utils.d.ts +33 -0
  34. package/bare-plugin/build/ios/utils.js +98 -0
  35. package/bare-plugin/src/android/addListActivity.ts +22 -0
  36. package/bare-plugin/src/android/androidCommand.ts +23 -0
  37. package/bare-plugin/src/android/applyAndConfigureAboutLibrariesPlugin.ts +21 -0
  38. package/bare-plugin/src/android/declareAboutLibrariesPlugin.ts +21 -0
  39. package/bare-plugin/src/android/utils.ts +34 -0
  40. package/bare-plugin/src/index.ts +9 -0
  41. package/bare-plugin/src/ios/addSettingsBundle.ts +27 -0
  42. package/bare-plugin/src/ios/iosCommand.ts +21 -0
  43. package/bare-plugin/src/ios/registerLicensePlistBuildPhase.ts +22 -0
  44. package/bare-plugin/src/ios/utils.ts +120 -0
  45. package/bare-plugin/tsconfig.json +20 -0
  46. package/ios/ReactNativeLegal-Bridging-Header.h +1 -0
  47. package/ios/ReactNativeLegalDetailViewController.swift +156 -0
  48. package/ios/ReactNativeLegalLicenseMetadata.swift +4 -0
  49. package/ios/ReactNativeLegalModule.h +7 -0
  50. package/ios/ReactNativeLegalModule.mm +29 -0
  51. package/ios/ReactNativeLegalModuleImpl.swift +78 -0
  52. package/ios/ReactNativeLegalTableViewController.swift +72 -0
  53. package/lib/commonjs/NativeReactNativeLegal.js +10 -0
  54. package/lib/commonjs/NativeReactNativeLegal.js.map +1 -0
  55. package/lib/commonjs/ReactNativeLegal.android.js +18 -0
  56. package/lib/commonjs/ReactNativeLegal.android.js.map +1 -0
  57. package/lib/commonjs/ReactNativeLegal.ios.js +18 -0
  58. package/lib/commonjs/ReactNativeLegal.ios.js.map +1 -0
  59. package/lib/commonjs/ReactNativeLegal.js +13 -0
  60. package/lib/commonjs/ReactNativeLegal.js.map +1 -0
  61. package/lib/commonjs/index.js +13 -0
  62. package/lib/commonjs/index.js.map +1 -0
  63. package/lib/module/NativeReactNativeLegal.js +3 -0
  64. package/lib/module/NativeReactNativeLegal.js.map +1 -0
  65. package/lib/module/ReactNativeLegal.android.js +10 -0
  66. package/lib/module/ReactNativeLegal.android.js.map +1 -0
  67. package/lib/module/ReactNativeLegal.ios.js +10 -0
  68. package/lib/module/ReactNativeLegal.ios.js.map +1 -0
  69. package/lib/module/ReactNativeLegal.js +6 -0
  70. package/lib/module/ReactNativeLegal.js.map +1 -0
  71. package/lib/module/index.js +2 -0
  72. package/lib/module/index.js.map +1 -0
  73. package/lib/typescript/NativeReactNativeLegal.d.ts +7 -0
  74. package/lib/typescript/NativeReactNativeLegal.d.ts.map +1 -0
  75. package/lib/typescript/ReactNativeLegal.android.d.ts +4 -0
  76. package/lib/typescript/ReactNativeLegal.android.d.ts.map +1 -0
  77. package/lib/typescript/ReactNativeLegal.d.ts +4 -0
  78. package/lib/typescript/ReactNativeLegal.d.ts.map +1 -0
  79. package/lib/typescript/ReactNativeLegal.ios.d.ts +4 -0
  80. package/lib/typescript/ReactNativeLegal.ios.d.ts.map +1 -0
  81. package/lib/typescript/index.d.ts +2 -0
  82. package/lib/typescript/index.d.ts.map +1 -0
  83. package/package.json +97 -0
  84. package/plugin/build/android/addListActivity.d.ts +5 -0
  85. package/plugin/build/android/addListActivity.js +17 -0
  86. package/plugin/build/android/applyAndConfigureAboutLibrariesPlugin.d.ts +7 -0
  87. package/plugin/build/android/applyAndConfigureAboutLibrariesPlugin.js +22 -0
  88. package/plugin/build/android/declareAboutLibrariesPlugin.d.ts +7 -0
  89. package/plugin/build/android/declareAboutLibrariesPlugin.js +22 -0
  90. package/plugin/build/android/withAndroidLegal.d.ts +8 -0
  91. package/plugin/build/android/withAndroidLegal.js +30 -0
  92. package/plugin/build/index.d.ts +3 -0
  93. package/plugin/build/index.js +11 -0
  94. package/plugin/build/ios/addSettingsBundle.d.ts +5 -0
  95. package/plugin/build/ios/addSettingsBundle.js +28 -0
  96. package/plugin/build/ios/registerLicensePlistBuildPhase.d.ts +8 -0
  97. package/plugin/build/ios/registerLicensePlistBuildPhase.js +23 -0
  98. package/plugin/build/ios/withIosLegal.d.ts +8 -0
  99. package/plugin/build/ios/withIosLegal.js +28 -0
  100. package/plugin/jest.config.js +1 -0
  101. package/plugin/src/android/addListActivity.ts +19 -0
  102. package/plugin/src/android/applyAndConfigureAboutLibrariesPlugin.ts +23 -0
  103. package/plugin/src/android/declareAboutLibrariesPlugin.ts +21 -0
  104. package/plugin/src/android/withAndroidLegal.ts +28 -0
  105. package/plugin/src/index.ts +14 -0
  106. package/plugin/src/ios/addSettingsBundle.ts +29 -0
  107. package/plugin/src/ios/registerLicensePlistBuildPhase.ts +24 -0
  108. package/plugin/src/ios/withIosLegal.ts +26 -0
  109. package/plugin/tsconfig.json +9 -0
  110. package/plugin-utils/build/android.d.ts +12 -0
  111. package/plugin-utils/build/android.js +91 -0
  112. package/plugin-utils/build/common.d.ts +45 -0
  113. package/plugin-utils/build/common.js +312 -0
  114. package/plugin-utils/build/ios.d.ts +11 -0
  115. package/plugin-utils/build/ios.js +86 -0
  116. package/plugin-utils/src/android.ts +99 -0
  117. package/plugin-utils/src/common.ts +398 -0
  118. package/plugin-utils/src/ios.ts +102 -0
  119. package/plugin-utils/tsconfig.json +20 -0
  120. package/react-native.config.js +13 -0
  121. package/src/NativeReactNativeLegal.ts +8 -0
  122. package/src/ReactNativeLegal.android.ts +10 -0
  123. package/src/ReactNativeLegal.ios.ts +10 -0
  124. package/src/ReactNativeLegal.ts +5 -0
  125. package/src/index.ts +1 -0
@@ -0,0 +1,398 @@
1
+ import crypto from 'crypto';
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+
5
+ const glob = require('glob');
6
+
7
+ type LicenseObj = {
8
+ author?: string;
9
+ content?: string;
10
+ description?: string;
11
+ type?: string;
12
+ url?: string;
13
+ version: string;
14
+ };
15
+
16
+ type AboutLibrariesLibraryJsonPayload = {
17
+ artifactVersion: string;
18
+ description: string;
19
+ developers: { name: string; organisationUrl: string }[];
20
+ licenses: string[];
21
+ name: string;
22
+ tag: string;
23
+ uniqueId: string;
24
+ };
25
+
26
+ type AboutLibrariesLicenseJsonPayload = {
27
+ content: string;
28
+ hash: string;
29
+ name: string;
30
+ url: string;
31
+ };
32
+
33
+ type LicensePlistPayload = {
34
+ body: string;
35
+ name: string;
36
+ source?: string;
37
+ version: string;
38
+ };
39
+
40
+ function compareObjects(a: unknown, b: unknown): boolean {
41
+ if (a == null || b == null || typeof a !== 'object' || typeof b !== 'object') {
42
+ return a === b;
43
+ }
44
+
45
+ const entriesA = Object.entries(a);
46
+ const entriesB = Object.entries(b);
47
+
48
+ return (
49
+ entriesA.length === entriesB.length &&
50
+ entriesA
51
+ .map(([keyA, valueA]) => {
52
+ const entry = entriesB.find(([keyB]) => keyA === keyB);
53
+
54
+ if (!entry) {
55
+ return valueA === entry;
56
+ }
57
+
58
+ const [, valueB] = entry;
59
+
60
+ return compareObjects(valueA, valueB);
61
+ })
62
+ .reduce((acc, curr) => acc && curr, true)
63
+ );
64
+ }
65
+
66
+ /**
67
+ * Makes a deep-check between array items and provided object, returns true if array has provided object.
68
+ */
69
+ export function arrayIncludesObject(array?: unknown[], object?: unknown) {
70
+ return array?.map((item) => compareObjects(item, object)).reduce((acc, curr) => acc || curr, false);
71
+ }
72
+
73
+ /**
74
+ * Scans `package.json` and searches for all packages under `dependencies` field. Supports monorepo projects.
75
+ */
76
+ export function scanDependencies(appPackageJsonPath: string) {
77
+ const appPackageJson = require(path.resolve(appPackageJsonPath));
78
+ const dependencies: Record<string, string> = appPackageJson.dependencies;
79
+
80
+ return Object.keys(dependencies).reduce(
81
+ (acc, dependency) => {
82
+ try {
83
+ const localPackageJsonPath = getPackageJsonPath(dependency);
84
+
85
+ if (!localPackageJsonPath) {
86
+ console.warn(`[react-native-legal] skipping ${dependency} could not find package.json`);
87
+ return acc;
88
+ }
89
+
90
+ const localPackageJson = require(path.resolve(localPackageJsonPath));
91
+ const licenseFiles = glob.sync('LICEN{S,C}E{.md,}', {
92
+ cwd: path.dirname(localPackageJsonPath),
93
+ absolute: true,
94
+ nocase: true,
95
+ nodir: true,
96
+ ignore: '**/{__tests__,__fixtures__,__mocks__}/**',
97
+ });
98
+
99
+ acc[dependency] = {
100
+ author: parseAuthorField(localPackageJson),
101
+ content: licenseFiles?.[0] ? fs.readFileSync(licenseFiles[0], { encoding: 'utf-8' }) : undefined,
102
+ description: localPackageJson.description,
103
+ type: parseLicenseField(localPackageJson),
104
+ url: parseRepositoryFieldToUrl(localPackageJson),
105
+ version: localPackageJson.version,
106
+ };
107
+ } catch (error) {
108
+ console.warn(`[react-native-legal] could not process package.json for ${dependency}`);
109
+ }
110
+
111
+ return acc;
112
+ },
113
+ {} as Record<string, LicenseObj>,
114
+ );
115
+ }
116
+
117
+ function needsQuoting(value: string) {
118
+ return (
119
+ value === '' || // empty string
120
+ /^[#:>|-]/.test(value) || // starts with special char
121
+ /^['"{}[\],&*#?|<>=!%@`]/.test(value) || // starts with indicator chars
122
+ /^[\s]|[\s]$/.test(value) || // has leading/trailing whitespace
123
+ /^[\d.+-]/.test(value) || // looks like a number/bool/null
124
+ /[\n"'\\\s]/.test(value) || // contains newlines, quotes, backslash, or spaces
125
+ /^(true|false|yes|no|null|on|off)$/i.test(value) // is a YAML keyword
126
+ );
127
+ }
128
+
129
+ function formatYamlKey(key: string) {
130
+ return /[@/_.]/.test(key) ? `"${key}"` : key;
131
+ }
132
+
133
+ function formatYamlValue(value: string, indent: number) {
134
+ if (value.includes('\n')) {
135
+ const indentedValue = value
136
+ .split('\n')
137
+ .map((line) => `${' '.repeat(indent)}${line}`)
138
+ .join('\n');
139
+
140
+ // Return the block indicator on the same line as the content
141
+ return `|${indentedValue ? '\n' + indentedValue : ''}`;
142
+ }
143
+
144
+ if (needsQuoting(value)) {
145
+ if (value.includes("'") && !value.includes('"')) {
146
+ return `"${value.replace(/["\\]/g, '\\$&')}"`;
147
+ }
148
+
149
+ return `'${value.replace(/'/g, "''")}'`;
150
+ }
151
+
152
+ return value;
153
+ }
154
+
155
+ function toYaml(obj: unknown, indent = 0): string {
156
+ const spaces = ' '.repeat(indent);
157
+
158
+ if (obj == null) return '';
159
+
160
+ if (Array.isArray(obj)) {
161
+ return obj.map((item) => `${spaces}- ${toYaml(item, indent + 2).trimStart()}`).join('\n');
162
+ }
163
+
164
+ if (typeof obj === 'object') {
165
+ return Object.entries(obj)
166
+ .filter(([, v]) => v != null)
167
+ .map(([key, value]) => {
168
+ const formattedKey = formatYamlKey(key);
169
+ const formattedValue = toYaml(value, indent + 2);
170
+
171
+ if (Array.isArray(value)) {
172
+ return `${spaces}${formattedKey}:\n${formattedValue}`;
173
+ }
174
+
175
+ if (typeof value === 'object' && value !== null) {
176
+ return `${spaces}${formattedKey}:\n${formattedValue}`;
177
+ }
178
+
179
+ if (typeof value === 'string' && value.includes('\n')) {
180
+ return `${spaces}${formattedKey}: ${formattedValue}`;
181
+ }
182
+
183
+ return `${spaces}${formattedKey}: ${formattedValue}`;
184
+ })
185
+ .join('\n');
186
+ }
187
+
188
+ return typeof obj === 'string' ? formatYamlValue(obj, indent) : String(obj);
189
+ }
190
+
191
+ /**
192
+ * Generates LicensePlist-compatible metadata for NPM dependencies
193
+ *
194
+ * This will take scanned NPM licenses and produce following output inside iOS project's directory:
195
+ *
196
+ * | - ios
197
+ * | ---- myawesomeapp
198
+ * | ---- myawesomeapp.xcodeproj
199
+ * | ---- myawesomeapp.xcodeworkspace
200
+ * | ---- license_plist.yml <--- generated LicensePlist config with NPM dependencies
201
+ * | ---- Podfile
202
+ * | ---- Podfile.lock
203
+ */
204
+ export function generateLicensePlistNPMOutput(licenses: Record<string, LicenseObj>, iosProjectPath: string) {
205
+ const renames: Record<string, string> = {};
206
+ const licenseEntries = Object.entries(licenses).map(([dependency, licenseObj]) => {
207
+ const normalizedName = normalizePackageName(dependency);
208
+
209
+ if (dependency !== normalizedName) {
210
+ renames[normalizedName] = dependency;
211
+ }
212
+
213
+ return {
214
+ name: normalizedName,
215
+ version: licenseObj.version,
216
+ ...(licenseObj.url && { source: licenseObj.url }),
217
+ body: licenseObj.content ?? licenseObj.type ?? 'UNKNOWN',
218
+ } as LicensePlistPayload;
219
+ });
220
+
221
+ const yamlDoc = {
222
+ ...(Object.keys(renames).length > 0 && { rename: renames }),
223
+ manual: licenseEntries,
224
+ };
225
+
226
+ const yamlContent = [
227
+ '# BEGIN Generated NPM license entries',
228
+ toYaml(yamlDoc),
229
+ '# END Generated NPM license entries',
230
+ ].join('\n');
231
+
232
+ fs.writeFileSync(path.join(iosProjectPath, 'license_plist.yml'), yamlContent, { encoding: 'utf-8' });
233
+ }
234
+
235
+ /**
236
+ * Generates AboutLibraries-compatible metadata for NPM dependencies
237
+ *
238
+ * This will take scanned NPM licenses and produce following output inside android project's directory:
239
+ *
240
+ * | - android
241
+ * | ---- app
242
+ * | ---- config <--- generated AboutLibraries config directory
243
+ * | ------- libraries <--- generated directory with JSON files list of NPM dependencies
244
+ * | ------- licenses <--- generated directory with JSON files list of used licenses
245
+ * | ---- build.gradle
246
+ * | ---- settings.gradle
247
+ */
248
+ export function generateAboutLibrariesNPMOutput(licenses: Record<string, LicenseObj>, androidProjectPath: string) {
249
+ const aboutLibrariesConfigDirPath = path.join(androidProjectPath, 'config');
250
+ const aboutLibrariesConfigLibrariesDirPath = path.join(aboutLibrariesConfigDirPath, 'libraries');
251
+ const aboutLibrariesConfigLicensesDirPath = path.join(aboutLibrariesConfigDirPath, 'licenses');
252
+
253
+ if (!fs.existsSync(aboutLibrariesConfigDirPath)) {
254
+ fs.mkdirSync(aboutLibrariesConfigDirPath);
255
+ }
256
+
257
+ if (!fs.existsSync(aboutLibrariesConfigLibrariesDirPath)) {
258
+ fs.mkdirSync(aboutLibrariesConfigLibrariesDirPath);
259
+ }
260
+
261
+ if (!fs.existsSync(aboutLibrariesConfigLicensesDirPath)) {
262
+ fs.mkdirSync(aboutLibrariesConfigLicensesDirPath);
263
+ }
264
+
265
+ Object.entries(licenses)
266
+ .map(([dependency, licenseObj]) => {
267
+ return {
268
+ artifactVersion: licenseObj.version,
269
+ content: licenseObj.content ?? '',
270
+ description: licenseObj.description ?? '',
271
+ developers: [{ name: licenseObj.author ?? '', organisationUrl: '' }],
272
+ licenses: [prepareAboutLibrariesLicenseField(licenseObj)],
273
+ name: dependency,
274
+ tag: '',
275
+ type: licenseObj.type,
276
+ uniqueId: normalizePackageName(dependency),
277
+ };
278
+ })
279
+ .map((jsonPayload) => {
280
+ const libraryJsonPayload: AboutLibrariesLibraryJsonPayload = {
281
+ artifactVersion: jsonPayload.artifactVersion,
282
+ description: jsonPayload.description,
283
+ developers: jsonPayload.developers,
284
+ licenses: jsonPayload.licenses,
285
+ name: jsonPayload.name,
286
+ tag: jsonPayload.tag,
287
+ uniqueId: jsonPayload.uniqueId,
288
+ };
289
+ const licenseJsonPayload: AboutLibrariesLicenseJsonPayload = {
290
+ content: jsonPayload.content,
291
+ hash: jsonPayload.licenses[0],
292
+ name: jsonPayload.type ?? '',
293
+ url: '',
294
+ };
295
+ const libraryJsonFilePath = path.join(
296
+ aboutLibrariesConfigLibrariesDirPath,
297
+ `${normalizePackageName(jsonPayload.name)}.json`,
298
+ );
299
+ const licenseJsonFilePath = path.join(aboutLibrariesConfigLicensesDirPath, `${licenseJsonPayload.hash}.json`);
300
+
301
+ fs.writeFileSync(libraryJsonFilePath, JSON.stringify(libraryJsonPayload));
302
+
303
+ if (!fs.existsSync(licenseJsonFilePath)) {
304
+ fs.writeFileSync(licenseJsonFilePath, JSON.stringify(licenseJsonPayload));
305
+ }
306
+ });
307
+ }
308
+
309
+ function prepareAboutLibrariesLicenseField(license: LicenseObj) {
310
+ if (!license.type) {
311
+ return '';
312
+ }
313
+
314
+ return `${license.type}_${sha512(license.content ?? license.type)}`;
315
+ }
316
+
317
+ function sha512(text: string) {
318
+ return crypto.createHash('sha512').update(text).digest('hex');
319
+ }
320
+
321
+ function parseAuthorField(json: { author: string | { name: string } }) {
322
+ if (typeof json.author === 'object' && typeof json.author.name === 'string') {
323
+ return json.author.name;
324
+ }
325
+
326
+ if (typeof json.author === 'string') {
327
+ return json.author;
328
+ }
329
+ }
330
+
331
+ function parseLicenseField(json: { license: string | { type: string } }) {
332
+ if (typeof json.license === 'object' && typeof json.license.type === 'string') {
333
+ return json.license.type;
334
+ }
335
+
336
+ if (typeof json.license === 'string') {
337
+ return json.license;
338
+ }
339
+ }
340
+
341
+ function parseRepositoryFieldToUrl(json: { repository: string | { url?: string } }) {
342
+ if (typeof json.repository === 'object' && typeof json.repository.url === 'string') {
343
+ return normalizeRepositoryUrl(json.repository.url);
344
+ }
345
+
346
+ if (typeof json.repository === 'string') {
347
+ return normalizeRepositoryUrl(json.repository);
348
+ }
349
+ }
350
+
351
+ function normalizeRepositoryUrl(url: string) {
352
+ return url
353
+ .replace('git+ssh://git@', 'git://')
354
+ .replace('.git', '')
355
+ .replace('git+https://github.com', 'https://github.com')
356
+ .replace('.git', '')
357
+ .replace('git://github.com', 'https://github.com')
358
+ .replace('.git', '')
359
+ .replace('git@github.com:', 'https://github.com/')
360
+ .replace('.git', '')
361
+ .replace('github:', 'https://github.com/')
362
+ .replace('.git', '');
363
+ }
364
+
365
+ function getPackageJsonPath(dependency: string) {
366
+ try {
367
+ return require.resolve(`${dependency}/package.json`);
368
+ } catch (error) {
369
+ return resolvePackageJsonFromEntry(dependency);
370
+ }
371
+ }
372
+
373
+ function resolvePackageJsonFromEntry(dependency: string) {
374
+ try {
375
+ const entryPath = require.resolve(dependency);
376
+ const packageDir = findPackageRoot(entryPath);
377
+
378
+ if (!packageDir) return null;
379
+
380
+ const packageJsonPath = path.join(packageDir, 'package.json');
381
+
382
+ return fs.existsSync(packageJsonPath) ? packageJsonPath : null;
383
+ } catch {
384
+ return null;
385
+ }
386
+ }
387
+
388
+ function findPackageRoot(entryPath: string) {
389
+ let currentDir = path.dirname(entryPath);
390
+ while (currentDir !== path.dirname(currentDir)) {
391
+ if (fs.existsSync(path.join(currentDir, 'package.json'))) return currentDir;
392
+ currentDir = path.dirname(currentDir);
393
+ }
394
+ }
395
+
396
+ function normalizePackageName(packageName: string): string {
397
+ return packageName.replace('/', '_');
398
+ }
@@ -0,0 +1,102 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+
4
+ import type { XcodeProject } from 'xcode';
5
+
6
+ /**
7
+ * Creates a Settings.bundle from a template and invokes a callback responsible for linking new file to the iOS project
8
+ */
9
+ export function addSettingsBundleUtil(
10
+ iosProjectPath: string,
11
+ addResourceFileToGroup: ({ settingsBundleFilename }: { settingsBundleFilename: string }) => void,
12
+ ) {
13
+ const settingsBundleFilename = 'Settings.bundle';
14
+ const settingsBundleRootPlistFilename = 'Root.plist';
15
+ const settingsBundlePath = path.join(iosProjectPath, settingsBundleFilename);
16
+ const settingsBundleRootPlistPath = path.join(settingsBundlePath, settingsBundleRootPlistFilename);
17
+
18
+ if (!fs.existsSync(settingsBundleRootPlistPath)) {
19
+ // Create `Settings.bundle` directory
20
+ if (!fs.existsSync(settingsBundlePath)) {
21
+ fs.mkdirSync(settingsBundlePath, { recursive: true });
22
+ }
23
+
24
+ // Create `Settings.bundle/Root.plist`
25
+ fs.writeFileSync(settingsBundleRootPlistPath, SETTINGS_BUNDLE_ROOT_PLIST_CONTENT);
26
+
27
+ // Link `Settings.bundle` to the resources
28
+ addResourceFileToGroup({ settingsBundleFilename });
29
+ console.log('Settings.bundle/Root.plist - ADDED');
30
+ } else {
31
+ console.log('Settings.bundle/Root.plist already present - SKIP');
32
+ }
33
+ }
34
+
35
+ /**
36
+ * Creates a shell script build phase (if needed) and links it to native targets build phases
37
+ */
38
+ export function registerLicensePlistBuildPhaseUtil(
39
+ projectTargetId: string,
40
+ pbxproj: XcodeProject, // Xcode Pbxproj
41
+ ) {
42
+ const nativeTargetSection = pbxproj.pbxNativeTargetSection();
43
+ const nativeTarget = nativeTargetSection[projectTargetId];
44
+
45
+ if (pbxproj.buildPhase(GENERATE_LICENSE_PLIST_BUILD_PHASE_COMMENT, projectTargetId)) {
46
+ console.log(`LicensePlist buildPhase already added in "${nativeTarget.name}" - SKIP`);
47
+ return pbxproj;
48
+ }
49
+
50
+ /**
51
+ * The build phase will generate licenses metadata using `LicensePlist` library and store it inside `Settings.bundle`.
52
+ *
53
+ * This will return an object with the uuid of the newly created build phase and its associated comment.
54
+ */
55
+ const newBuildPhase = pbxproj.addBuildPhase(
56
+ [],
57
+ 'PBXShellScriptBuildPhase',
58
+ GENERATE_LICENSE_PLIST_BUILD_PHASE_COMMENT,
59
+ projectTargetId,
60
+ {
61
+ shellPath: '/bin/sh',
62
+ shellScript: '${PODS_ROOT}/LicensePlist/license-plist --output-path ./Settings.bundle',
63
+ },
64
+ );
65
+ /**
66
+ * In order to correctly link all metadata from `Settings.bundle` to the app
67
+ * the newly created build phase has to be invoked before `Copy Bundle Resources` build phase.
68
+ *
69
+ * To make sure that happens, let's put newly created build phase as a first in the sequence.
70
+ * It can be done by overriding `PBXNativeTarget["buildPhases"]` array.
71
+ */
72
+ const newBuildPhasesInNativeTarget = [
73
+ { value: newBuildPhase.uuid, comment: GENERATE_LICENSE_PLIST_BUILD_PHASE_COMMENT },
74
+ ].concat(nativeTarget.buildPhases.filter(({ value }: { value: string }) => value !== newBuildPhase.uuid));
75
+
76
+ nativeTarget.buildPhases = newBuildPhasesInNativeTarget;
77
+
78
+ console.log(`LicensePlist buildPhase in nativeTarget "${nativeTarget.name}" - ADDED`);
79
+
80
+ return pbxproj;
81
+ }
82
+
83
+ const GENERATE_LICENSE_PLIST_BUILD_PHASE_COMMENT = 'Generate licenses with LicensePlist';
84
+ const SETTINGS_BUNDLE_ROOT_PLIST_CONTENT = `<?xml version="1.0" encoding="UTF-8"?>
85
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
86
+ <plist version="1.0">
87
+ <dict>
88
+ <key>StringsTable</key>
89
+ <string>Root</string>
90
+ <key>PreferenceSpecifiers</key>
91
+ <array>
92
+ <dict>
93
+ <key>Type</key>
94
+ <string>PSChildPaneSpecifier</string>
95
+ <key>Title</key>
96
+ <string>Licenses</string>
97
+ <key>File</key>
98
+ <string>com.mono0926.LicensePlist</string>
99
+ </dict>
100
+ </array>
101
+ </dict>
102
+ </plist>`;
@@ -0,0 +1,20 @@
1
+ {
2
+ "compilerOptions": {
3
+ "declaration": true,
4
+ "lib": ["es2020"],
5
+ "module": "commonjs",
6
+ "target": "es2020",
7
+
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "moduleResolution": "node",
13
+
14
+ "outDir": "build",
15
+ "rootDir": "src",
16
+ "typeRoots": ["../types-definitions", "../../../node_modules/@types"]
17
+ },
18
+ "include": ["./src"],
19
+ "exclude": ["**/__mocks__/*", "**/__tests__/*"]
20
+ }
@@ -0,0 +1,13 @@
1
+ module.exports = {
2
+ commands: [
3
+ {
4
+ name: 'legal-generate',
5
+ description: 'Set up all native boilerplate for OSS licenses notice',
6
+ func: ([], { project: { android, ios } }, {}) => {
7
+ const generateLegal = require('./bare-plugin/build').default;
8
+
9
+ generateLegal(android.sourceDir, ios.sourceDir);
10
+ },
11
+ },
12
+ ],
13
+ };
@@ -0,0 +1,8 @@
1
+ import type { TurboModule } from 'react-native';
2
+ import { TurboModuleRegistry } from 'react-native';
3
+
4
+ export interface Spec extends TurboModule {
5
+ launchLicenseListScreen(licenseHeaderText: string): void;
6
+ }
7
+
8
+ export default TurboModuleRegistry.getEnforcing<Spec>('ReactNativeLegalModule');
@@ -0,0 +1,10 @@
1
+ import NativeReactNativeLegal from './NativeReactNativeLegal';
2
+
3
+ export const ReactNativeLegal = {
4
+ launchLicenseListScreen: (licenseHeaderText?: string) => {
5
+ /**
6
+ * On Android, the licenses list is displayed as a custom activity
7
+ */
8
+ NativeReactNativeLegal.launchLicenseListScreen(licenseHeaderText ?? 'OSS Licenses');
9
+ },
10
+ };
@@ -0,0 +1,10 @@
1
+ import NativeReactNativeLegal from './NativeReactNativeLegal';
2
+
3
+ export const ReactNativeLegal = {
4
+ launchLicenseListScreen: (licenseHeaderText?: string) => {
5
+ /**
6
+ * On iOS, the licenses list is displayed as a custom table view controller
7
+ */
8
+ NativeReactNativeLegal.launchLicenseListScreen(licenseHeaderText ?? 'OSS Licenses');
9
+ },
10
+ };
@@ -0,0 +1,5 @@
1
+ export const ReactNativeLegal = {
2
+ launchLicenseListScreen: (_licenseHeaderText?: string) => {
3
+ //
4
+ },
5
+ };
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export { ReactNativeLegal } from './ReactNativeLegal';