Skip to content

Commit 79f4d63

Browse files
committed
feat(#10): initial basic web support
1 parent 1e37d65 commit 79f4d63

7 files changed

Lines changed: 277 additions & 3 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ project.xcworkspace
5050
build/
5151
packages/*/lib/
5252
packages/*/build/
53+
.rnlegal/
5354

5455
# Expo example
5556
examples/expo-example/android

examples/expo-example/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import '@expo/metro-runtime';
12
import { registerRootComponent } from 'expo';
23

34
import App from './App';

examples/expo-example/package.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,22 @@
77
"expo": "expo",
88
"android": "expo run:android",
99
"ios": "expo run:ios",
10-
"web": "expo start --web",
10+
"web": "react-native-legal && expo start --web",
11+
"react-native-legal": "react-native-legal",
1112
"typecheck": "tsc --noEmit"
1213
},
1314
"dependencies": {
15+
"@expo/metro-runtime": "~4.0.1",
1416
"expo": "~52.0.36",
1517
"expo-build-properties": "~0.13.2",
1618
"expo-splash-screen": "~0.29.22",
1719
"expo-status-bar": "~2.0.1",
1820
"react": "18.3.1",
21+
"react-dom": "18.3.1",
1922
"react-native": "0.76.7",
2023
"react-native-legal": "workspace:*",
21-
"react-native-legal-common-example-ui": "workspace:*"
24+
"react-native-legal-common-example-ui": "workspace:*",
25+
"react-native-web": "~0.19.13"
2226
},
2327
"devDependencies": {
2428
"@babel/core": "^7.25.2",
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#!/usr/bin/env node
2+
const fs = require('node:fs');
3+
const path = require('node:path');
4+
5+
const { scanDependencies } = require('@callstack/licenses');
6+
7+
const repoRootPath = path.resolve(process.cwd());
8+
const packageJsonPath = path.join(repoRootPath, 'package.json');
9+
10+
if (!fs.existsSync(packageJsonPath)) {
11+
console.error(`package.json not found at ${packageJsonPath}`);
12+
process.exit(1);
13+
}
14+
15+
const licenses = scanDependencies(packageJsonPath);
16+
17+
const payload = Object.entries(licenses)
18+
.map(([packageKey, licenseObj]) => {
19+
return {
20+
name: licenseObj.name,
21+
version: licenseObj.version,
22+
content: licenseObj.content ?? licenseObj.type ?? 'UNKNOWN',
23+
packageKey,
24+
...(licenseObj.url && { source: licenseObj.url }),
25+
};
26+
})
27+
.toSorted((first, second) => {
28+
if (!first.version || !second.version) {
29+
return first.name > second.name;
30+
}
31+
32+
if (first.name !== second.name) {
33+
return first.name > second.name ? 1 : -1;
34+
}
35+
36+
const [firstMajor, firstMinor, firstPatch] = first.version.split('.').filter(Boolean);
37+
const [secondMajor, secondMinor, secondPatch] = second.version.split('.').filter(Boolean);
38+
39+
return `${first.name}.${firstMajor.padStart(10, '0')}.${(firstMinor ?? '0').padStart(10, '0')}.${(
40+
firstPatch ?? '0'
41+
).padStart(10, '0')}` >
42+
`${second.name}.${secondMajor.padStart(10, '0')}.${(secondMinor ?? '0').padStart(10, '0')}.${(
43+
secondPatch ?? '0'
44+
).padStart(10, '0')}`
45+
? 1
46+
: -1;
47+
});
48+
49+
const rnLegalConfigPath = path.join(__dirname, '..', '.rnlegal');
50+
51+
if (!fs.existsSync(rnLegalConfigPath)) {
52+
fs.mkdirSync(rnLegalConfigPath);
53+
}
54+
55+
const rnLegalConfigLibrariesPath = path.join(rnLegalConfigPath, 'libraries.json');
56+
57+
fs.writeFileSync(rnLegalConfigLibrariesPath, JSON.stringify(payload), { encoding: 'utf-8' });
58+
59+
console.log(`✅ Output written to ${rnLegalConfigLibrariesPath}`);

packages/react-native-legal/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"main": "lib/commonjs/index",
1313
"module": "lib/module/index",
1414
"types": "lib/typescript/index.d.ts",
15+
"bin": "./bin/index.js",
1516
"react-native": "src/index",
1617
"source": "src/index",
1718
"repository": {
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import type { LibrariesResult } from './NativeReactNativeLegal';
2+
3+
export const ReactNativeLegal = {
4+
getLibrariesAsync: () => {
5+
const payload = require(`react-native-legal/.rnlegal/libraries.json`);
6+
7+
return Promise.resolve<LibrariesResult>({
8+
data: payload.map((library: any) => ({
9+
id: library.packageKey,
10+
name: library.packageKey,
11+
licenses: [{ licenseContent: library.content }],
12+
})),
13+
});
14+
},
15+
launchLicenseListScreen: (licenseHeaderText?: string) => {
16+
const payload = require(`react-native-legal/.rnlegal/libraries.json`);
17+
18+
const main = document.createElement('main');
19+
20+
main.style.display = 'flex';
21+
main.style.alignSelf = 'stretch';
22+
main.style.flex = '1';
23+
main.style.flexDirection = 'column';
24+
main.style.overflow = 'scroll';
25+
main.style.width = '100%';
26+
27+
const closeBtn = document.createElement('button');
28+
29+
closeBtn.innerText = 'Close';
30+
31+
const headerContainer = document.createElement('header');
32+
33+
headerContainer.style.display = 'flex';
34+
headerContainer.style.flexDirection = 'row';
35+
headerContainer.style.alignItems = 'center';
36+
headerContainer.style.justifyContent = 'space-between';
37+
headerContainer.appendChild(closeBtn);
38+
39+
main.appendChild(headerContainer);
40+
41+
if (licenseHeaderText) {
42+
const header = document.createElement('h1');
43+
44+
header.innerText = licenseHeaderText;
45+
46+
headerContainer.insertBefore(header, closeBtn);
47+
} else {
48+
headerContainer.style.flexDirection = 'row-reverse';
49+
}
50+
51+
payload.forEach((library: any) => {
52+
const summary = document.createElement('summary');
53+
54+
summary.style.fontSize = '20px';
55+
summary.style.margin = '10px 0px';
56+
summary.innerText = library.packageKey;
57+
58+
const content = document.createElement('p');
59+
60+
content.innerText = library.content;
61+
62+
const details = document.createElement('details');
63+
64+
details.appendChild(summary);
65+
details.appendChild(content);
66+
67+
main.appendChild(details);
68+
});
69+
70+
const dialog = document.createElement('dialog');
71+
72+
dialog.id = 'react-native-legal-dialog';
73+
dialog.style.height = '90%';
74+
dialog.style.width = '90%';
75+
dialog.appendChild(main);
76+
77+
document.querySelector('body')?.appendChild(dialog);
78+
79+
closeBtn.addEventListener(
80+
'click',
81+
() => {
82+
document.querySelector('body')?.removeChild(dialog);
83+
},
84+
{ once: true },
85+
);
86+
dialog.addEventListener(
87+
'close',
88+
() => {
89+
document.querySelector('body')?.removeChild(dialog);
90+
},
91+
{ once: true },
92+
);
93+
dialog.showModal();
94+
},
95+
};

0 commit comments

Comments
 (0)