react-native-legal 1.8.0 → 1.9.0
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.
- package/android/gradle.properties +7 -0
- package/bare-plugin/build/android/androidCommand.d.ts +2 -2
- package/bare-plugin/build/android/androidCommand.js +9 -2
- package/bare-plugin/build/index.d.ts +1 -1
- package/bare-plugin/build/index.js +4 -3
- package/bare-plugin/build/ios/iosCommand.d.ts +2 -2
- package/bare-plugin/build/ios/iosCommand.js +9 -2
- package/bare-plugin/src/android/androidCommand.ts +14 -5
- package/bare-plugin/src/index.ts +4 -3
- package/bare-plugin/src/ios/iosCommand.ts +15 -3
- package/package.json +3 -2
- package/plugin/build/android/withAndroidLegal.d.ts +1 -1
- package/plugin/build/android/withAndroidLegal.js +9 -6
- package/plugin/build/index.d.ts +1 -2
- package/plugin/build/index.js +5 -4
- package/plugin/build/ios/withIosLegal.d.ts +1 -1
- package/plugin/build/ios/withIosLegal.js +9 -6
- package/plugin/build/types.d.ts +1 -5
- package/plugin/src/android/withAndroidLegal.ts +13 -8
- package/plugin/src/index.ts +7 -6
- package/plugin/src/ios/withIosLegal.ts +13 -8
- package/plugin/src/types.ts +1 -7
- package/plugin-utils/build/common.d.ts +24 -2
- package/plugin-utils/build/common.js +51 -0
- package/plugin-utils/build/metro.d.ts +17 -0
- package/plugin-utils/build/metro.js +61 -0
- package/plugin-utils/build/types.d.ts +9 -0
- package/plugin-utils/src/common.ts +73 -2
- package/plugin-utils/src/metro.ts +76 -0
- package/plugin-utils/src/types.ts +12 -0
- package/react-native.config.js +33 -6
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
ReactNativeLegal_androidGradlePluginVersion=8.12.0
|
|
2
|
+
ReactNativeLegal_composeBomVersion=2025.11.01
|
|
3
|
+
ReactNativeLegal_kotlinVersion=2.1.20
|
|
4
|
+
ReactNativeLegal_compileSdkVersion=36
|
|
5
|
+
ReactNativeLegal_minSdkVersion=21
|
|
6
|
+
ReactNativeLegal_targetSdkVersion=35
|
|
7
|
+
ReactNativeLegal_aboutLibrariesVersion=13.1.0
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import type { PlatformPluginOptions } from '../../../plugin-utils/build/types';
|
|
2
2
|
/**
|
|
3
3
|
* Implementation of bare plugin's Android/Android TV setup
|
|
4
4
|
*
|
|
5
5
|
* It scans the NPM dependencies, generates AboutLibraries-compatible metadata for them,
|
|
6
6
|
* installs & configures AboutLibraries Gradle plugin and adds Android Activity with a list of dependencies and their licenses
|
|
7
7
|
*/
|
|
8
|
-
export declare function androidCommand(androidProjectPath: string, scanOptionsFactory:
|
|
8
|
+
export declare function androidCommand(androidProjectPath: string, { scanOptionsFactory, dependencySource, additionalProjectRoots }: PlatformPluginOptions): Promise<void>;
|
|
@@ -7,6 +7,7 @@ exports.androidCommand = androidCommand;
|
|
|
7
7
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
8
|
const node_path_1 = __importDefault(require("node:path"));
|
|
9
9
|
const licenses_1 = require("@callstack/licenses");
|
|
10
|
+
const common_1 = require("../../../plugin-utils/build/common");
|
|
10
11
|
const addListActivity_1 = require("./addListActivity");
|
|
11
12
|
const addResourceKeepFile_1 = require("./addResourceKeepFile");
|
|
12
13
|
const applyAndConfigureAboutLibrariesPlugin_1 = require("./applyAndConfigureAboutLibrariesPlugin");
|
|
@@ -17,8 +18,14 @@ const declareAboutLibrariesPlugin_1 = require("./declareAboutLibrariesPlugin");
|
|
|
17
18
|
* It scans the NPM dependencies, generates AboutLibraries-compatible metadata for them,
|
|
18
19
|
* installs & configures AboutLibraries Gradle plugin and adds Android Activity with a list of dependencies and their licenses
|
|
19
20
|
*/
|
|
20
|
-
function androidCommand(androidProjectPath, scanOptionsFactory) {
|
|
21
|
-
const licenses = (0,
|
|
21
|
+
async function androidCommand(androidProjectPath, { scanOptionsFactory, dependencySource, additionalProjectRoots }) {
|
|
22
|
+
const licenses = await (0, common_1.scanLicenses)({
|
|
23
|
+
projectRoot: node_path_1.default.resolve(androidProjectPath, '..'),
|
|
24
|
+
platform: 'android',
|
|
25
|
+
dependencySource,
|
|
26
|
+
scanOptionsFactory,
|
|
27
|
+
additionalProjectRoots,
|
|
28
|
+
});
|
|
22
29
|
const aboutLibrariesConfigDirPath = node_path_1.default.join(androidProjectPath, 'config');
|
|
23
30
|
// Cleanup metadata in case `scanOptionsFactory` changed
|
|
24
31
|
node_fs_1.default.rmSync(aboutLibrariesConfigDirPath, { recursive: true, force: true });
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { PluginScanOptions } from '../../plugin-utils/build/types';
|
|
2
|
-
declare function generateLegal(androidProjectPath: string | undefined, iosProjectPath: string | undefined, pluginScanOptions: PluginScanOptions): void
|
|
2
|
+
declare function generateLegal(androidProjectPath: string | undefined, iosProjectPath: string | undefined, pluginScanOptions: PluginScanOptions): Promise<void>;
|
|
3
3
|
export default generateLegal;
|
|
@@ -3,13 +3,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const common_1 = require("../../plugin-utils/build/common");
|
|
4
4
|
const androidCommand_1 = require("./android/androidCommand");
|
|
5
5
|
const iosCommand_1 = require("./ios/iosCommand");
|
|
6
|
-
function generateLegal(androidProjectPath, iosProjectPath, pluginScanOptions) {
|
|
6
|
+
async function generateLegal(androidProjectPath, iosProjectPath, pluginScanOptions) {
|
|
7
7
|
const scanOptionsFactory = (0, common_1.createPluginScanOptionsFactory)(pluginScanOptions);
|
|
8
|
+
const { dependencySource, additionalProjectRoots } = pluginScanOptions;
|
|
8
9
|
if (androidProjectPath) {
|
|
9
|
-
(0, androidCommand_1.androidCommand)(androidProjectPath, scanOptionsFactory);
|
|
10
|
+
await (0, androidCommand_1.androidCommand)(androidProjectPath, { scanOptionsFactory, dependencySource, additionalProjectRoots });
|
|
10
11
|
}
|
|
11
12
|
if (iosProjectPath) {
|
|
12
|
-
(0, iosCommand_1.iosCommand)(iosProjectPath, scanOptionsFactory);
|
|
13
|
+
await (0, iosCommand_1.iosCommand)(iosProjectPath, { scanOptionsFactory, dependencySource, additionalProjectRoots });
|
|
13
14
|
}
|
|
14
15
|
}
|
|
15
16
|
exports.default = generateLegal;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import type { PlatformPluginOptions } from '../../../plugin-utils/build/types';
|
|
2
2
|
/**
|
|
3
3
|
* Implementation of bare plugin's iOS/tvOS setup
|
|
4
4
|
*
|
|
5
5
|
* It scans the NPM dependencies, generates LicensePlist-compatible metadata for them,
|
|
6
6
|
* configures Settings.bundle and registers a shell script generating LicensePlist metadata for iOS dependencies
|
|
7
7
|
*/
|
|
8
|
-
export declare function iosCommand(iosProjectPath: string, scanOptionsFactory:
|
|
8
|
+
export declare function iosCommand(iosProjectPath: string, { scanOptionsFactory, dependencySource, additionalProjectRoots }: PlatformPluginOptions): Promise<void>;
|
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.iosCommand = iosCommand;
|
|
7
7
|
const node_path_1 = __importDefault(require("node:path"));
|
|
8
8
|
const licenses_1 = require("@callstack/licenses");
|
|
9
|
+
const common_1 = require("../../../plugin-utils/build/common");
|
|
9
10
|
const addSettingsBundle_1 = require("./addSettingsBundle");
|
|
10
11
|
const registerLicensePlistBuildPhase_1 = require("./registerLicensePlistBuildPhase");
|
|
11
12
|
/**
|
|
@@ -14,8 +15,14 @@ const registerLicensePlistBuildPhase_1 = require("./registerLicensePlistBuildPha
|
|
|
14
15
|
* It scans the NPM dependencies, generates LicensePlist-compatible metadata for them,
|
|
15
16
|
* configures Settings.bundle and registers a shell script generating LicensePlist metadata for iOS dependencies
|
|
16
17
|
*/
|
|
17
|
-
function iosCommand(iosProjectPath, scanOptionsFactory) {
|
|
18
|
-
const licenses = (0,
|
|
18
|
+
async function iosCommand(iosProjectPath, { scanOptionsFactory, dependencySource, additionalProjectRoots }) {
|
|
19
|
+
const licenses = await (0, common_1.scanLicenses)({
|
|
20
|
+
projectRoot: node_path_1.default.resolve(iosProjectPath, '..'),
|
|
21
|
+
platform: 'ios',
|
|
22
|
+
dependencySource,
|
|
23
|
+
scanOptionsFactory,
|
|
24
|
+
additionalProjectRoots,
|
|
25
|
+
});
|
|
19
26
|
(0, licenses_1.writeLicensePlistNPMOutput)(licenses, iosProjectPath);
|
|
20
27
|
(0, addSettingsBundle_1.addSettingsBundle)(iosProjectPath);
|
|
21
28
|
(0, registerLicensePlistBuildPhase_1.registerLicensePlistBuildPhase)(iosProjectPath);
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { writeAboutLibrariesNPMOutput } from '@callstack/licenses';
|
|
5
|
+
|
|
6
|
+
import { scanLicenses } from '../../../plugin-utils/build/common';
|
|
7
|
+
import type { PlatformPluginOptions } from '../../../plugin-utils/build/types';
|
|
5
8
|
|
|
6
9
|
import { addListActivity } from './addListActivity';
|
|
7
10
|
import { addResourceKeepFile } from './addResourceKeepFile';
|
|
@@ -14,11 +17,17 @@ import { declareAboutLibrariesPlugin } from './declareAboutLibrariesPlugin';
|
|
|
14
17
|
* It scans the NPM dependencies, generates AboutLibraries-compatible metadata for them,
|
|
15
18
|
* installs & configures AboutLibraries Gradle plugin and adds Android Activity with a list of dependencies and their licenses
|
|
16
19
|
*/
|
|
17
|
-
export function androidCommand(
|
|
18
|
-
|
|
19
|
-
|
|
20
|
+
export async function androidCommand(
|
|
21
|
+
androidProjectPath: string,
|
|
22
|
+
{ scanOptionsFactory, dependencySource, additionalProjectRoots }: PlatformPluginOptions,
|
|
23
|
+
): Promise<void> {
|
|
24
|
+
const licenses = await scanLicenses({
|
|
25
|
+
projectRoot: path.resolve(androidProjectPath, '..'),
|
|
26
|
+
platform: 'android',
|
|
27
|
+
dependencySource,
|
|
20
28
|
scanOptionsFactory,
|
|
21
|
-
|
|
29
|
+
additionalProjectRoots,
|
|
30
|
+
});
|
|
22
31
|
|
|
23
32
|
const aboutLibrariesConfigDirPath = path.join(androidProjectPath, 'config');
|
|
24
33
|
|
package/bare-plugin/src/index.ts
CHANGED
|
@@ -4,19 +4,20 @@ import type { PluginScanOptions } from '../../plugin-utils/build/types';
|
|
|
4
4
|
import { androidCommand } from './android/androidCommand';
|
|
5
5
|
import { iosCommand } from './ios/iosCommand';
|
|
6
6
|
|
|
7
|
-
function generateLegal(
|
|
7
|
+
async function generateLegal(
|
|
8
8
|
androidProjectPath: string | undefined,
|
|
9
9
|
iosProjectPath: string | undefined,
|
|
10
10
|
pluginScanOptions: PluginScanOptions,
|
|
11
11
|
) {
|
|
12
12
|
const scanOptionsFactory = createPluginScanOptionsFactory(pluginScanOptions);
|
|
13
|
+
const { dependencySource, additionalProjectRoots } = pluginScanOptions;
|
|
13
14
|
|
|
14
15
|
if (androidProjectPath) {
|
|
15
|
-
androidCommand(androidProjectPath, scanOptionsFactory);
|
|
16
|
+
await androidCommand(androidProjectPath, { scanOptionsFactory, dependencySource, additionalProjectRoots });
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
if (iosProjectPath) {
|
|
19
|
-
iosCommand(iosProjectPath, scanOptionsFactory);
|
|
20
|
+
await iosCommand(iosProjectPath, { scanOptionsFactory, dependencySource, additionalProjectRoots });
|
|
20
21
|
}
|
|
21
22
|
}
|
|
22
23
|
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { writeLicensePlistNPMOutput } from '@callstack/licenses';
|
|
4
|
+
|
|
5
|
+
import { scanLicenses } from '../../../plugin-utils/build/common';
|
|
6
|
+
import type { PlatformPluginOptions } from '../../../plugin-utils/build/types';
|
|
4
7
|
|
|
5
8
|
import { addSettingsBundle } from './addSettingsBundle';
|
|
6
9
|
import { registerLicensePlistBuildPhase } from './registerLicensePlistBuildPhase';
|
|
@@ -11,8 +14,17 @@ import { registerLicensePlistBuildPhase } from './registerLicensePlistBuildPhase
|
|
|
11
14
|
* It scans the NPM dependencies, generates LicensePlist-compatible metadata for them,
|
|
12
15
|
* configures Settings.bundle and registers a shell script generating LicensePlist metadata for iOS dependencies
|
|
13
16
|
*/
|
|
14
|
-
export function iosCommand(
|
|
15
|
-
|
|
17
|
+
export async function iosCommand(
|
|
18
|
+
iosProjectPath: string,
|
|
19
|
+
{ scanOptionsFactory, dependencySource, additionalProjectRoots }: PlatformPluginOptions,
|
|
20
|
+
) {
|
|
21
|
+
const licenses = await scanLicenses({
|
|
22
|
+
projectRoot: path.resolve(iosProjectPath, '..'),
|
|
23
|
+
platform: 'ios',
|
|
24
|
+
dependencySource,
|
|
25
|
+
scanOptionsFactory,
|
|
26
|
+
additionalProjectRoots,
|
|
27
|
+
});
|
|
16
28
|
|
|
17
29
|
writeLicensePlistNPMOutput(licenses, iosProjectPath);
|
|
18
30
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-legal",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.0",
|
|
4
4
|
"description": "Acknowledge OSS libraries used in your React Native app",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react-native",
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
"files": [
|
|
32
32
|
"android/src",
|
|
33
33
|
"android/build.gradle",
|
|
34
|
+
"android/gradle.properties",
|
|
34
35
|
"bare-plugin",
|
|
35
36
|
"ios",
|
|
36
37
|
"lib",
|
|
@@ -62,7 +63,7 @@
|
|
|
62
63
|
"react-native-builder-bob": "0.42.1"
|
|
63
64
|
},
|
|
64
65
|
"dependencies": {
|
|
65
|
-
"@callstack/licenses": "^0.
|
|
66
|
+
"@callstack/licenses": "^1.0.0",
|
|
66
67
|
"glob": "^13.0.0",
|
|
67
68
|
"xcode": "^3.0.1",
|
|
68
69
|
"xml2js": "^0.6.2"
|
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.withAndroidLegal = void 0;
|
|
7
|
-
const node_path_1 = __importDefault(require("node:path"));
|
|
8
4
|
const licenses_1 = require("@callstack/licenses");
|
|
9
5
|
const config_plugins_1 = require("expo/config-plugins");
|
|
6
|
+
const common_1 = require("../../../plugin-utils/build/common");
|
|
10
7
|
const addListActivity_1 = require("./addListActivity");
|
|
11
8
|
const addResourceKeepFile_1 = require("./addResourceKeepFile");
|
|
12
9
|
const applyAndConfigureAboutLibrariesPlugin_1 = require("./applyAndConfigureAboutLibrariesPlugin");
|
|
@@ -17,9 +14,15 @@ const declareAboutLibrariesPlugin_1 = require("./declareAboutLibrariesPlugin");
|
|
|
17
14
|
* It scans the NPM dependencies, generates AboutLibraries-compatible metadata,
|
|
18
15
|
* installs & configures AboutLibraries Gradle plugin and adds Android Activity with a list of dependencies and their licenses
|
|
19
16
|
*/
|
|
20
|
-
const withAndroidLegal = (config, { scanOptionsFactory }) => {
|
|
17
|
+
const withAndroidLegal = (config, { scanOptionsFactory, dependencySource, additionalProjectRoots }) => {
|
|
21
18
|
(0, config_plugins_1.withAndroidManifest)(config, async (exportedConfig) => {
|
|
22
|
-
const licenses = (0,
|
|
19
|
+
const licenses = await (0, common_1.scanLicenses)({
|
|
20
|
+
projectRoot: exportedConfig.modRequest.projectRoot,
|
|
21
|
+
platform: 'android',
|
|
22
|
+
dependencySource,
|
|
23
|
+
scanOptionsFactory,
|
|
24
|
+
additionalProjectRoots,
|
|
25
|
+
});
|
|
23
26
|
(0, licenses_1.writeAboutLibrariesNPMOutput)(licenses, exportedConfig.modRequest.platformProjectRoot);
|
|
24
27
|
return exportedConfig;
|
|
25
28
|
});
|
package/plugin/build/index.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
1
|
import type { ConfigPlugin } from 'expo/config-plugins';
|
|
2
|
-
|
|
3
|
-
declare const _default: ConfigPlugin<PluginScanOptions>;
|
|
2
|
+
declare const _default: ConfigPlugin<Partial<import("../../plugin-utils/build/types").PluginScanOptions>>;
|
|
4
3
|
export default _default;
|
package/plugin/build/index.js
CHANGED
|
@@ -7,11 +7,12 @@ const withIosLegal_1 = require("./ios/withIosLegal");
|
|
|
7
7
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
8
8
|
const pak = require('react-native-legal/package.json');
|
|
9
9
|
const withReactNativeLegal = (config, options) => {
|
|
10
|
-
|
|
11
|
-
const pluginScanOptions =
|
|
10
|
+
// validates the raw options, before the defaults are assigned
|
|
11
|
+
const pluginScanOptions = (0, common_1.resolvePluginScanOptions)(options);
|
|
12
|
+
const { dependencySource, additionalProjectRoots } = pluginScanOptions;
|
|
12
13
|
const scanOptionsFactory = (0, common_1.createPluginScanOptionsFactory)(pluginScanOptions);
|
|
13
|
-
config = (0, withAndroidLegal_1.withAndroidLegal)(config, { scanOptionsFactory });
|
|
14
|
-
config = (0, withIosLegal_1.withIosLegal)(config, { scanOptionsFactory });
|
|
14
|
+
config = (0, withAndroidLegal_1.withAndroidLegal)(config, { scanOptionsFactory, dependencySource, additionalProjectRoots });
|
|
15
|
+
config = (0, withIosLegal_1.withIosLegal)(config, { scanOptionsFactory, dependencySource, additionalProjectRoots });
|
|
15
16
|
return config;
|
|
16
17
|
};
|
|
17
18
|
exports.default = (0, config_plugins_1.createRunOncePlugin)(withReactNativeLegal, pak.name, pak.version);
|
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.withIosLegal = void 0;
|
|
7
|
-
const node_path_1 = __importDefault(require("node:path"));
|
|
8
4
|
const licenses_1 = require("@callstack/licenses");
|
|
9
5
|
const config_plugins_1 = require("expo/config-plugins");
|
|
6
|
+
const common_1 = require("../../../plugin-utils/build/common");
|
|
10
7
|
const addSettingsBundle_1 = require("./addSettingsBundle");
|
|
11
8
|
const registerLicensePlistBuildPhase_1 = require("./registerLicensePlistBuildPhase");
|
|
12
9
|
/**
|
|
@@ -15,9 +12,15 @@ const registerLicensePlistBuildPhase_1 = require("./registerLicensePlistBuildPha
|
|
|
15
12
|
* It scans the NPM dependencies, generates LicensePlist-compatible metadata,
|
|
16
13
|
* configures Settings.bundle and registers a shell script generating LicensePlist metadata for iOS dependencies
|
|
17
14
|
*/
|
|
18
|
-
const withIosLegal = (config, { scanOptionsFactory }) => {
|
|
15
|
+
const withIosLegal = (config, { scanOptionsFactory, dependencySource, additionalProjectRoots }) => {
|
|
19
16
|
(0, config_plugins_1.withXcodeProject)(config, async (exportedConfig) => {
|
|
20
|
-
const licenses = (0,
|
|
17
|
+
const licenses = await (0, common_1.scanLicenses)({
|
|
18
|
+
projectRoot: exportedConfig.modRequest.projectRoot,
|
|
19
|
+
platform: 'ios',
|
|
20
|
+
dependencySource,
|
|
21
|
+
scanOptionsFactory,
|
|
22
|
+
additionalProjectRoots,
|
|
23
|
+
});
|
|
21
24
|
(0, licenses_1.writeLicensePlistNPMOutput)(licenses, exportedConfig.modRequest.platformProjectRoot);
|
|
22
25
|
return exportedConfig;
|
|
23
26
|
});
|
package/plugin/build/types.d.ts
CHANGED
|
@@ -1,6 +1,2 @@
|
|
|
1
|
-
import type { Types as SharedTypes } from '@callstack/licenses';
|
|
2
1
|
import type { PluginScanOptions } from '../../plugin-utils/build/types';
|
|
3
|
-
export type PluginOptions = PluginScanOptions
|
|
4
|
-
export type PlatformPluginOptions = {
|
|
5
|
-
scanOptionsFactory: SharedTypes.ScanPackageOptionsFactory;
|
|
6
|
-
};
|
|
2
|
+
export type PluginOptions = Partial<PluginScanOptions>;
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
import { scanDependencies, writeAboutLibrariesNPMOutput } from '@callstack/licenses';
|
|
1
|
+
import { writeAboutLibrariesNPMOutput } from '@callstack/licenses';
|
|
4
2
|
import { type ConfigPlugin, withAndroidManifest } from 'expo/config-plugins';
|
|
5
3
|
|
|
6
|
-
import
|
|
4
|
+
import { scanLicenses } from '../../../plugin-utils/build/common';
|
|
5
|
+
import type { PlatformPluginOptions } from '../../../plugin-utils/build/types';
|
|
7
6
|
|
|
8
7
|
import { addListActivity } from './addListActivity';
|
|
9
8
|
import { addResourceKeepFile } from './addResourceKeepFile';
|
|
@@ -16,12 +15,18 @@ import { declareAboutLibrariesPlugin } from './declareAboutLibrariesPlugin';
|
|
|
16
15
|
* It scans the NPM dependencies, generates AboutLibraries-compatible metadata,
|
|
17
16
|
* installs & configures AboutLibraries Gradle plugin and adds Android Activity with a list of dependencies and their licenses
|
|
18
17
|
*/
|
|
19
|
-
export const withAndroidLegal: ConfigPlugin<PlatformPluginOptions> = (
|
|
18
|
+
export const withAndroidLegal: ConfigPlugin<PlatformPluginOptions> = (
|
|
19
|
+
config,
|
|
20
|
+
{ scanOptionsFactory, dependencySource, additionalProjectRoots },
|
|
21
|
+
) => {
|
|
20
22
|
withAndroidManifest(config, async (exportedConfig) => {
|
|
21
|
-
const licenses =
|
|
22
|
-
|
|
23
|
+
const licenses = await scanLicenses({
|
|
24
|
+
projectRoot: exportedConfig.modRequest.projectRoot,
|
|
25
|
+
platform: 'android',
|
|
26
|
+
dependencySource,
|
|
23
27
|
scanOptionsFactory,
|
|
24
|
-
|
|
28
|
+
additionalProjectRoots,
|
|
29
|
+
});
|
|
25
30
|
|
|
26
31
|
writeAboutLibrariesNPMOutput(licenses, exportedConfig.modRequest.platformProjectRoot);
|
|
27
32
|
return exportedConfig;
|
package/plugin/src/index.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import type { ConfigPlugin } from 'expo/config-plugins';
|
|
2
2
|
import { createRunOncePlugin } from 'expo/config-plugins';
|
|
3
3
|
|
|
4
|
-
import { createPluginScanOptionsFactory } from '../../plugin-utils/build/common';
|
|
5
|
-
import type { PluginScanOptions } from '../../plugin-utils/build/types';
|
|
4
|
+
import { createPluginScanOptionsFactory, resolvePluginScanOptions } from '../../plugin-utils/build/common';
|
|
6
5
|
|
|
7
6
|
import { withAndroidLegal } from './android/withAndroidLegal';
|
|
8
7
|
import { withIosLegal } from './ios/withIosLegal';
|
|
@@ -12,12 +11,14 @@ import type { PluginOptions } from './types';
|
|
|
12
11
|
const pak = require('react-native-legal/package.json');
|
|
13
12
|
|
|
14
13
|
const withReactNativeLegal: ConfigPlugin<PluginOptions> = (config, options) => {
|
|
15
|
-
|
|
16
|
-
const pluginScanOptions
|
|
14
|
+
// validates the raw options, before the defaults are assigned
|
|
15
|
+
const pluginScanOptions = resolvePluginScanOptions(options);
|
|
16
|
+
const { dependencySource, additionalProjectRoots } = pluginScanOptions;
|
|
17
|
+
|
|
17
18
|
const scanOptionsFactory = createPluginScanOptionsFactory(pluginScanOptions);
|
|
18
19
|
|
|
19
|
-
config = withAndroidLegal(config, { scanOptionsFactory });
|
|
20
|
-
config = withIosLegal(config, { scanOptionsFactory });
|
|
20
|
+
config = withAndroidLegal(config, { scanOptionsFactory, dependencySource, additionalProjectRoots });
|
|
21
|
+
config = withIosLegal(config, { scanOptionsFactory, dependencySource, additionalProjectRoots });
|
|
21
22
|
|
|
22
23
|
return config;
|
|
23
24
|
};
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
import { scanDependencies, writeLicensePlistNPMOutput } from '@callstack/licenses';
|
|
1
|
+
import { writeLicensePlistNPMOutput } from '@callstack/licenses';
|
|
4
2
|
import { type ConfigPlugin, withXcodeProject } from 'expo/config-plugins';
|
|
5
3
|
|
|
6
|
-
import
|
|
4
|
+
import { scanLicenses } from '../../../plugin-utils/build/common';
|
|
5
|
+
import type { PlatformPluginOptions } from '../../../plugin-utils/build/types';
|
|
7
6
|
|
|
8
7
|
import { addSettingsBundle } from './addSettingsBundle';
|
|
9
8
|
import { registerLicensePlistBuildPhase } from './registerLicensePlistBuildPhase';
|
|
@@ -14,12 +13,18 @@ import { registerLicensePlistBuildPhase } from './registerLicensePlistBuildPhase
|
|
|
14
13
|
* It scans the NPM dependencies, generates LicensePlist-compatible metadata,
|
|
15
14
|
* configures Settings.bundle and registers a shell script generating LicensePlist metadata for iOS dependencies
|
|
16
15
|
*/
|
|
17
|
-
export const withIosLegal: ConfigPlugin<PlatformPluginOptions> = (
|
|
16
|
+
export const withIosLegal: ConfigPlugin<PlatformPluginOptions> = (
|
|
17
|
+
config,
|
|
18
|
+
{ scanOptionsFactory, dependencySource, additionalProjectRoots },
|
|
19
|
+
) => {
|
|
18
20
|
withXcodeProject(config, async (exportedConfig) => {
|
|
19
|
-
const licenses =
|
|
20
|
-
|
|
21
|
+
const licenses = await scanLicenses({
|
|
22
|
+
projectRoot: exportedConfig.modRequest.projectRoot,
|
|
23
|
+
platform: 'ios',
|
|
24
|
+
dependencySource,
|
|
21
25
|
scanOptionsFactory,
|
|
22
|
-
|
|
26
|
+
additionalProjectRoots,
|
|
27
|
+
});
|
|
23
28
|
|
|
24
29
|
writeLicensePlistNPMOutput(licenses, exportedConfig.modRequest.platformProjectRoot);
|
|
25
30
|
return exportedConfig;
|
package/plugin/src/types.ts
CHANGED
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
import type { Types as SharedTypes } from '@callstack/licenses';
|
|
2
|
-
|
|
3
1
|
import type { PluginScanOptions } from '../../plugin-utils/build/types';
|
|
4
2
|
|
|
5
|
-
export type PluginOptions = PluginScanOptions
|
|
6
|
-
|
|
7
|
-
export type PlatformPluginOptions = {
|
|
8
|
-
scanOptionsFactory: SharedTypes.ScanPackageOptionsFactory;
|
|
9
|
-
};
|
|
3
|
+
export type PluginOptions = Partial<PluginScanOptions>;
|
|
@@ -1,3 +1,25 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import type { PluginScanOptions } from './types';
|
|
1
|
+
import { type Types as SharedTypes } from '@callstack/licenses';
|
|
2
|
+
import type { DependencySource, PluginScanOptions } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Assigns defaults to the options provided by the user.
|
|
5
|
+
* Must be called with the raw options (before any defaults are assigned), because it validates
|
|
6
|
+
* that the options scanning `package.json` files are not combined with `dependencySource: 'metro'`, which ignores them.
|
|
7
|
+
*
|
|
8
|
+
* @throws When `dependencySource` is `'metro'` and any of the `package.json` scan options is provided
|
|
9
|
+
*/
|
|
10
|
+
export declare function resolvePluginScanOptions(options?: Partial<PluginScanOptions>): PluginScanOptions;
|
|
3
11
|
export declare function createPluginScanOptionsFactory(pluginScanOptions: PluginScanOptions): SharedTypes.ScanPackageOptionsFactory;
|
|
12
|
+
/**
|
|
13
|
+
* Collects license information of the app's NPM dependencies,
|
|
14
|
+
* using the method selected with `dependencySource`
|
|
15
|
+
*
|
|
16
|
+
* - `package-json` - scans dependencies declared in `package.json` (and their dependencies)
|
|
17
|
+
* - `metro` - takes packages included in the Metro dependency graph of the app for a given platform
|
|
18
|
+
*/
|
|
19
|
+
export declare function scanLicenses({ projectRoot, platform, dependencySource, scanOptionsFactory, additionalProjectRoots, }: {
|
|
20
|
+
projectRoot: string;
|
|
21
|
+
platform: 'ios' | 'android';
|
|
22
|
+
dependencySource: DependencySource;
|
|
23
|
+
scanOptionsFactory: SharedTypes.ScanPackageOptionsFactory;
|
|
24
|
+
additionalProjectRoots: readonly string[];
|
|
25
|
+
}): Promise<SharedTypes.AggregatedLicensesMapping>;
|
|
@@ -1,6 +1,43 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.resolvePluginScanOptions = resolvePluginScanOptions;
|
|
3
7
|
exports.createPluginScanOptionsFactory = createPluginScanOptionsFactory;
|
|
8
|
+
exports.scanLicenses = scanLicenses;
|
|
9
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
10
|
+
const licenses_1 = require("@callstack/licenses");
|
|
11
|
+
const metro_1 = require("./metro");
|
|
12
|
+
const PACKAGE_JSON_SCAN_OPTION_DEFAULTS = {
|
|
13
|
+
devDepsMode: 'none',
|
|
14
|
+
includeOptionalDeps: true,
|
|
15
|
+
transitiveDepsMode: 'all',
|
|
16
|
+
additionalProjectRoots: [],
|
|
17
|
+
};
|
|
18
|
+
const PACKAGE_JSON_SCAN_OPTION_NAMES = Object.keys(PACKAGE_JSON_SCAN_OPTION_DEFAULTS);
|
|
19
|
+
/**
|
|
20
|
+
* Assigns defaults to the options provided by the user.
|
|
21
|
+
* Must be called with the raw options (before any defaults are assigned), because it validates
|
|
22
|
+
* that the options scanning `package.json` files are not combined with `dependencySource: 'metro'`, which ignores them.
|
|
23
|
+
*
|
|
24
|
+
* @throws When `dependencySource` is `'metro'` and any of the `package.json` scan options is provided
|
|
25
|
+
*/
|
|
26
|
+
function resolvePluginScanOptions(options = {}) {
|
|
27
|
+
const { dependencySource = 'package-json' } = options;
|
|
28
|
+
if (dependencySource === 'metro') {
|
|
29
|
+
const ignoredOptions = PACKAGE_JSON_SCAN_OPTION_NAMES.filter((name) => options[name] !== undefined);
|
|
30
|
+
if (ignoredOptions.length > 0) {
|
|
31
|
+
throw new Error(`[react-native-legal] dependencySource: 'metro' cannot be combined with: ${ignoredOptions.join(', ')}. ` +
|
|
32
|
+
`These options only apply when dependencySource is 'package-json'.`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
...PACKAGE_JSON_SCAN_OPTION_DEFAULTS,
|
|
37
|
+
...Object.fromEntries(Object.entries(options).filter(([, value]) => value !== undefined)),
|
|
38
|
+
dependencySource,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
4
41
|
function createPluginScanOptionsFactory(pluginScanOptions) {
|
|
5
42
|
return function ({ isRoot, isWorkspacePackage }) {
|
|
6
43
|
let includeDevDependencies = false;
|
|
@@ -35,3 +72,17 @@ function createPluginScanOptionsFactory(pluginScanOptions) {
|
|
|
35
72
|
};
|
|
36
73
|
};
|
|
37
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* Collects license information of the app's NPM dependencies,
|
|
77
|
+
* using the method selected with `dependencySource`
|
|
78
|
+
*
|
|
79
|
+
* - `package-json` - scans dependencies declared in `package.json` (and their dependencies)
|
|
80
|
+
* - `metro` - takes packages included in the Metro dependency graph of the app for a given platform
|
|
81
|
+
*/
|
|
82
|
+
async function scanLicenses({ projectRoot, platform, dependencySource, scanOptionsFactory, additionalProjectRoots, }) {
|
|
83
|
+
if (dependencySource === 'metro') {
|
|
84
|
+
const packageRoots = await (0, metro_1.getMetroPackageRoots)(projectRoot, platform);
|
|
85
|
+
return (0, licenses_1.scanPackageRoots)(packageRoots);
|
|
86
|
+
}
|
|
87
|
+
return (0, licenses_1.scanDependencies)(node_path_1.default.join(projectRoot, 'package.json'), scanOptionsFactory, { additionalProjectRoots });
|
|
88
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves the entry file of the app, the same way it is resolved when bundling:
|
|
3
|
+
* `main` field of the app's `package.json` (either a relative path, e.g. `index.js`, or a module, e.g. `expo-router/entry`),
|
|
4
|
+
* falling back to `index` file in the project root
|
|
5
|
+
*
|
|
6
|
+
* @param projectRoot Root directory of the app (containing `package.json`)
|
|
7
|
+
*/
|
|
8
|
+
export declare function resolveEntryFile(projectRoot: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* Builds the Metro dependency graph of the app for a given platform
|
|
11
|
+
* and returns root directories of all packages included in the production bundle
|
|
12
|
+
* (the app's own package is not included)
|
|
13
|
+
*
|
|
14
|
+
* @param projectRoot Root directory of the app (containing `package.json` and `metro.config.js`)
|
|
15
|
+
* @param platform Platform for which the dependency graph should be built
|
|
16
|
+
*/
|
|
17
|
+
export declare function getMetroPackageRoots(projectRoot: string, platform: 'ios' | 'android'): Promise<string[]>;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.resolveEntryFile = resolveEntryFile;
|
|
7
|
+
exports.getMetroPackageRoots = getMetroPackageRoots;
|
|
8
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
9
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
10
|
+
const licenses_1 = require("@callstack/licenses");
|
|
11
|
+
/**
|
|
12
|
+
* Resolves the entry file of the app, the same way it is resolved when bundling:
|
|
13
|
+
* `main` field of the app's `package.json` (either a relative path, e.g. `index.js`, or a module, e.g. `expo-router/entry`),
|
|
14
|
+
* falling back to `index` file in the project root
|
|
15
|
+
*
|
|
16
|
+
* @param projectRoot Root directory of the app (containing `package.json`)
|
|
17
|
+
*/
|
|
18
|
+
function resolveEntryFile(projectRoot) {
|
|
19
|
+
const packageJson = JSON.parse(node_fs_1.default.readFileSync(node_path_1.default.join(projectRoot, 'package.json'), { encoding: 'utf-8' }));
|
|
20
|
+
const main = packageJson.main;
|
|
21
|
+
if (!main) {
|
|
22
|
+
return require.resolve(node_path_1.default.join(projectRoot, 'index'));
|
|
23
|
+
}
|
|
24
|
+
try {
|
|
25
|
+
return require.resolve(node_path_1.default.resolve(projectRoot, main));
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return require.resolve(main, { paths: [projectRoot] });
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Builds the Metro dependency graph of the app for a given platform
|
|
33
|
+
* and returns root directories of all packages included in the production bundle
|
|
34
|
+
* (the app's own package is not included)
|
|
35
|
+
*
|
|
36
|
+
* @param projectRoot Root directory of the app (containing `package.json` and `metro.config.js`)
|
|
37
|
+
* @param platform Platform for which the dependency graph should be built
|
|
38
|
+
*/
|
|
39
|
+
async function getMetroPackageRoots(projectRoot, platform) {
|
|
40
|
+
const metroPath = require.resolve('metro', { paths: [projectRoot] });
|
|
41
|
+
const { loadConfig, buildGraph } = require(metroPath);
|
|
42
|
+
const config = await loadConfig({ cwd: projectRoot });
|
|
43
|
+
const graph = await buildGraph(config, {
|
|
44
|
+
entries: [resolveEntryFile(projectRoot)],
|
|
45
|
+
platform,
|
|
46
|
+
dev: false,
|
|
47
|
+
minify: false,
|
|
48
|
+
});
|
|
49
|
+
// Metro reports real paths of the modules, so the project root has to be compared by its real path too
|
|
50
|
+
const appRoot = node_fs_1.default.realpathSync(projectRoot);
|
|
51
|
+
// many modules share a directory - look up the package root only once per directory
|
|
52
|
+
const moduleDirs = new Set([...graph.dependencies.keys()].map((modulePath) => node_path_1.default.dirname(modulePath)));
|
|
53
|
+
const packageRoots = new Set();
|
|
54
|
+
for (const moduleDir of moduleDirs) {
|
|
55
|
+
const packageRoot = (0, licenses_1.findPackageRoot)(moduleDir);
|
|
56
|
+
if (packageRoot && packageRoot !== appRoot) {
|
|
57
|
+
packageRoots.add(packageRoot);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return [...packageRoots];
|
|
61
|
+
}
|
|
@@ -1,5 +1,14 @@
|
|
|
1
|
+
import type { Types as SharedTypes } from '@callstack/licenses';
|
|
2
|
+
export type DependencySource = 'package-json' | 'metro';
|
|
1
3
|
export interface PluginScanOptions {
|
|
4
|
+
dependencySource: DependencySource;
|
|
2
5
|
devDepsMode: 'root-only' | 'none';
|
|
3
6
|
includeOptionalDeps: boolean;
|
|
4
7
|
transitiveDepsMode: 'all' | 'from-external-only' | 'from-workspace-only' | 'none';
|
|
8
|
+
additionalProjectRoots: readonly string[];
|
|
5
9
|
}
|
|
10
|
+
export type PlatformPluginOptions = {
|
|
11
|
+
scanOptionsFactory: SharedTypes.ScanPackageOptionsFactory;
|
|
12
|
+
dependencySource: DependencySource;
|
|
13
|
+
additionalProjectRoots: readonly string[];
|
|
14
|
+
};
|
|
@@ -1,6 +1,48 @@
|
|
|
1
|
-
import
|
|
1
|
+
import path from 'node:path';
|
|
2
2
|
|
|
3
|
-
import type
|
|
3
|
+
import { type Types as SharedTypes, scanDependencies, scanPackageRoots } from '@callstack/licenses';
|
|
4
|
+
|
|
5
|
+
import { getMetroPackageRoots } from './metro';
|
|
6
|
+
import type { DependencySource, PluginScanOptions } from './types';
|
|
7
|
+
|
|
8
|
+
const PACKAGE_JSON_SCAN_OPTION_DEFAULTS = {
|
|
9
|
+
devDepsMode: 'none',
|
|
10
|
+
includeOptionalDeps: true,
|
|
11
|
+
transitiveDepsMode: 'all',
|
|
12
|
+
additionalProjectRoots: [],
|
|
13
|
+
} as const satisfies Omit<PluginScanOptions, 'dependencySource'>;
|
|
14
|
+
|
|
15
|
+
const PACKAGE_JSON_SCAN_OPTION_NAMES = Object.keys(PACKAGE_JSON_SCAN_OPTION_DEFAULTS) as Array<
|
|
16
|
+
keyof typeof PACKAGE_JSON_SCAN_OPTION_DEFAULTS
|
|
17
|
+
>;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Assigns defaults to the options provided by the user.
|
|
21
|
+
* Must be called with the raw options (before any defaults are assigned), because it validates
|
|
22
|
+
* that the options scanning `package.json` files are not combined with `dependencySource: 'metro'`, which ignores them.
|
|
23
|
+
*
|
|
24
|
+
* @throws When `dependencySource` is `'metro'` and any of the `package.json` scan options is provided
|
|
25
|
+
*/
|
|
26
|
+
export function resolvePluginScanOptions(options: Partial<PluginScanOptions> = {}): PluginScanOptions {
|
|
27
|
+
const { dependencySource = 'package-json' } = options;
|
|
28
|
+
|
|
29
|
+
if (dependencySource === 'metro') {
|
|
30
|
+
const ignoredOptions = PACKAGE_JSON_SCAN_OPTION_NAMES.filter((name) => options[name] !== undefined);
|
|
31
|
+
|
|
32
|
+
if (ignoredOptions.length > 0) {
|
|
33
|
+
throw new Error(
|
|
34
|
+
`[react-native-legal] dependencySource: 'metro' cannot be combined with: ${ignoredOptions.join(', ')}. ` +
|
|
35
|
+
`These options only apply when dependencySource is 'package-json'.`,
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
...PACKAGE_JSON_SCAN_OPTION_DEFAULTS,
|
|
42
|
+
...Object.fromEntries(Object.entries(options).filter(([, value]) => value !== undefined)),
|
|
43
|
+
dependencySource,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
4
46
|
|
|
5
47
|
export function createPluginScanOptionsFactory(
|
|
6
48
|
pluginScanOptions: PluginScanOptions,
|
|
@@ -47,3 +89,32 @@ export function createPluginScanOptionsFactory(
|
|
|
47
89
|
};
|
|
48
90
|
};
|
|
49
91
|
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Collects license information of the app's NPM dependencies,
|
|
95
|
+
* using the method selected with `dependencySource`
|
|
96
|
+
*
|
|
97
|
+
* - `package-json` - scans dependencies declared in `package.json` (and their dependencies)
|
|
98
|
+
* - `metro` - takes packages included in the Metro dependency graph of the app for a given platform
|
|
99
|
+
*/
|
|
100
|
+
export async function scanLicenses({
|
|
101
|
+
projectRoot,
|
|
102
|
+
platform,
|
|
103
|
+
dependencySource,
|
|
104
|
+
scanOptionsFactory,
|
|
105
|
+
additionalProjectRoots,
|
|
106
|
+
}: {
|
|
107
|
+
projectRoot: string;
|
|
108
|
+
platform: 'ios' | 'android';
|
|
109
|
+
dependencySource: DependencySource;
|
|
110
|
+
scanOptionsFactory: SharedTypes.ScanPackageOptionsFactory;
|
|
111
|
+
additionalProjectRoots: readonly string[];
|
|
112
|
+
}): Promise<SharedTypes.AggregatedLicensesMapping> {
|
|
113
|
+
if (dependencySource === 'metro') {
|
|
114
|
+
const packageRoots = await getMetroPackageRoots(projectRoot, platform);
|
|
115
|
+
|
|
116
|
+
return scanPackageRoots(packageRoots);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return scanDependencies(path.join(projectRoot, 'package.json'), scanOptionsFactory, { additionalProjectRoots });
|
|
120
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
import { findPackageRoot } from '@callstack/licenses';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Minimal typing of the Metro APIs used below - Metro is not a dependency of this package,
|
|
8
|
+
* it is loaded from the app's project
|
|
9
|
+
*/
|
|
10
|
+
type MetroModule = {
|
|
11
|
+
loadConfig: (argv: { cwd: string }) => Promise<unknown>;
|
|
12
|
+
buildGraph: (
|
|
13
|
+
config: unknown,
|
|
14
|
+
options: { entries: string[]; platform: string; dev: boolean; minify: boolean },
|
|
15
|
+
) => Promise<{ dependencies: ReadonlyMap<string, unknown> }>;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Resolves the entry file of the app, the same way it is resolved when bundling:
|
|
20
|
+
* `main` field of the app's `package.json` (either a relative path, e.g. `index.js`, or a module, e.g. `expo-router/entry`),
|
|
21
|
+
* falling back to `index` file in the project root
|
|
22
|
+
*
|
|
23
|
+
* @param projectRoot Root directory of the app (containing `package.json`)
|
|
24
|
+
*/
|
|
25
|
+
export function resolveEntryFile(projectRoot: string): string {
|
|
26
|
+
const packageJson = JSON.parse(fs.readFileSync(path.join(projectRoot, 'package.json'), { encoding: 'utf-8' }));
|
|
27
|
+
const main: string | undefined = packageJson.main;
|
|
28
|
+
|
|
29
|
+
if (!main) {
|
|
30
|
+
return require.resolve(path.join(projectRoot, 'index'));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
return require.resolve(path.resolve(projectRoot, main));
|
|
35
|
+
} catch {
|
|
36
|
+
return require.resolve(main, { paths: [projectRoot] });
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Builds the Metro dependency graph of the app for a given platform
|
|
42
|
+
* and returns root directories of all packages included in the production bundle
|
|
43
|
+
* (the app's own package is not included)
|
|
44
|
+
*
|
|
45
|
+
* @param projectRoot Root directory of the app (containing `package.json` and `metro.config.js`)
|
|
46
|
+
* @param platform Platform for which the dependency graph should be built
|
|
47
|
+
*/
|
|
48
|
+
export async function getMetroPackageRoots(projectRoot: string, platform: 'ios' | 'android'): Promise<string[]> {
|
|
49
|
+
const metroPath = require.resolve('metro', { paths: [projectRoot] });
|
|
50
|
+
const { loadConfig, buildGraph }: MetroModule = require(metroPath);
|
|
51
|
+
|
|
52
|
+
const config = await loadConfig({ cwd: projectRoot });
|
|
53
|
+
|
|
54
|
+
const graph = await buildGraph(config, {
|
|
55
|
+
entries: [resolveEntryFile(projectRoot)],
|
|
56
|
+
platform,
|
|
57
|
+
dev: false,
|
|
58
|
+
minify: false,
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// Metro reports real paths of the modules, so the project root has to be compared by its real path too
|
|
62
|
+
const appRoot = fs.realpathSync(projectRoot);
|
|
63
|
+
// many modules share a directory - look up the package root only once per directory
|
|
64
|
+
const moduleDirs = new Set([...graph.dependencies.keys()].map((modulePath) => path.dirname(modulePath)));
|
|
65
|
+
const packageRoots = new Set<string>();
|
|
66
|
+
|
|
67
|
+
for (const moduleDir of moduleDirs) {
|
|
68
|
+
const packageRoot = findPackageRoot(moduleDir);
|
|
69
|
+
|
|
70
|
+
if (packageRoot && packageRoot !== appRoot) {
|
|
71
|
+
packageRoots.add(packageRoot);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return [...packageRoots];
|
|
76
|
+
}
|
|
@@ -1,5 +1,17 @@
|
|
|
1
|
+
import type { Types as SharedTypes } from '@callstack/licenses';
|
|
2
|
+
|
|
3
|
+
export type DependencySource = 'package-json' | 'metro';
|
|
4
|
+
|
|
1
5
|
export interface PluginScanOptions {
|
|
6
|
+
dependencySource: DependencySource;
|
|
2
7
|
devDepsMode: 'root-only' | 'none';
|
|
3
8
|
includeOptionalDeps: boolean;
|
|
4
9
|
transitiveDepsMode: 'all' | 'from-external-only' | 'from-workspace-only' | 'none';
|
|
10
|
+
additionalProjectRoots: readonly string[];
|
|
5
11
|
}
|
|
12
|
+
|
|
13
|
+
export type PlatformPluginOptions = {
|
|
14
|
+
scanOptionsFactory: SharedTypes.ScanPackageOptionsFactory;
|
|
15
|
+
dependencySource: DependencySource;
|
|
16
|
+
additionalProjectRoots: readonly string[];
|
|
17
|
+
};
|
package/react-native.config.js
CHANGED
|
@@ -15,14 +15,12 @@ module.exports = {
|
|
|
15
15
|
|
|
16
16
|
return 'none';
|
|
17
17
|
},
|
|
18
|
-
default: 'none',
|
|
19
18
|
},
|
|
20
19
|
{
|
|
21
20
|
name: '--od, --include-optional-deps [boolean]',
|
|
22
21
|
description:
|
|
23
22
|
'Whether to include optionalDependencies in the scan; includeTransitiveDependencies option applies',
|
|
24
23
|
parse: (val) => val !== 'false',
|
|
25
|
-
default: true,
|
|
26
24
|
},
|
|
27
25
|
{
|
|
28
26
|
name: '--tm, --transitive-deps-mode <string>',
|
|
@@ -34,15 +32,44 @@ module.exports = {
|
|
|
34
32
|
|
|
35
33
|
return 'all';
|
|
36
34
|
},
|
|
37
|
-
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: '--ds, --dependency-source <string>',
|
|
38
|
+
description:
|
|
39
|
+
'How the dependencies are determined: from package.json files or from the Metro dependency graph',
|
|
40
|
+
parse: (val) => {
|
|
41
|
+
if (val === 'metro') {
|
|
42
|
+
return val;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return 'package-json';
|
|
46
|
+
},
|
|
47
|
+
default: 'package-json',
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: '--apr, --additional-project-roots <paths>',
|
|
51
|
+
description:
|
|
52
|
+
'Comma-separated paths (relative to the app root) of other folders with a package.json whose dependencies should be listed too, e.g. monorepo workspaces the app does not depend on; can be repeated',
|
|
53
|
+
parse: (val, previous = []) => [...previous, ...val.split(',').map((rootPath) => rootPath.trim())],
|
|
38
54
|
},
|
|
39
55
|
],
|
|
40
56
|
func: ([], { project: { android, ios } }, args) => {
|
|
41
57
|
const generateLegal = require('./bare-plugin/build').default;
|
|
42
|
-
|
|
43
|
-
const { devDepsMode, includeOptionalDeps, transitiveDepsMode } = args;
|
|
58
|
+
const { resolvePluginScanOptions } = require('./plugin-utils/build/common');
|
|
59
|
+
const { devDepsMode, includeOptionalDeps, transitiveDepsMode, dependencySource, additionalProjectRoots } = args;
|
|
44
60
|
|
|
45
|
-
|
|
61
|
+
// the defaults are assigned here (not in the options above), so that the options explicitly passed by the user can be validated
|
|
62
|
+
return generateLegal(
|
|
63
|
+
android?.sourceDir,
|
|
64
|
+
ios?.sourceDir,
|
|
65
|
+
resolvePluginScanOptions({
|
|
66
|
+
devDepsMode,
|
|
67
|
+
includeOptionalDeps,
|
|
68
|
+
transitiveDepsMode,
|
|
69
|
+
dependencySource,
|
|
70
|
+
additionalProjectRoots,
|
|
71
|
+
}),
|
|
72
|
+
);
|
|
46
73
|
},
|
|
47
74
|
},
|
|
48
75
|
],
|