react-native-legal 1.8.1 → 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.
@@ -1,8 +1,8 @@
1
- import { type Types as SharedTypes } from '@callstack/licenses';
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: SharedTypes.ScanPackageOptionsFactory): void;
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, licenses_1.scanDependencies)(node_path_1.default.join(node_path_1.default.resolve(androidProjectPath, '..'), 'package.json'), scanOptionsFactory);
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 { type Types as SharedTypes } from '@callstack/licenses';
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: SharedTypes.ScanPackageOptionsFactory): void;
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, licenses_1.scanDependencies)(node_path_1.default.join(node_path_1.default.resolve(iosProjectPath, '..'), 'package.json'), scanOptionsFactory);
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 { type Types as SharedTypes, scanDependencies, writeAboutLibrariesNPMOutput } from '@callstack/licenses';
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(androidProjectPath: string, scanOptionsFactory: SharedTypes.ScanPackageOptionsFactory) {
18
- const licenses = scanDependencies(
19
- path.join(path.resolve(androidProjectPath, '..'), 'package.json'),
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
 
@@ -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 { type Types as SharedTypes, scanDependencies, writeLicensePlistNPMOutput } from '@callstack/licenses';
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(iosProjectPath: string, scanOptionsFactory: SharedTypes.ScanPackageOptionsFactory) {
15
- const licenses = scanDependencies(path.join(path.resolve(iosProjectPath, '..'), 'package.json'), scanOptionsFactory);
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.8.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",
@@ -63,7 +63,7 @@
63
63
  "react-native-builder-bob": "0.42.1"
64
64
  },
65
65
  "dependencies": {
66
- "@callstack/licenses": "^0.3.3",
66
+ "@callstack/licenses": "^1.0.0",
67
67
  "glob": "^13.0.0",
68
68
  "xcode": "^3.0.1",
69
69
  "xml2js": "^0.6.2"
@@ -1,5 +1,5 @@
1
1
  import { type ConfigPlugin } from 'expo/config-plugins';
2
- import type { PlatformPluginOptions } from '../types';
2
+ import type { PlatformPluginOptions } from '../../../plugin-utils/build/types';
3
3
  /**
4
4
  * Implementation of config plugin for Android setup
5
5
  *
@@ -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, licenses_1.scanDependencies)(node_path_1.default.join(exportedConfig.modRequest.projectRoot, 'package.json'), scanOptionsFactory);
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
  });
@@ -1,4 +1,3 @@
1
1
  import type { ConfigPlugin } from 'expo/config-plugins';
2
- import type { PluginScanOptions } from '../../plugin-utils/build/types';
3
- declare const _default: ConfigPlugin<PluginScanOptions>;
2
+ declare const _default: ConfigPlugin<Partial<import("../../plugin-utils/build/types").PluginScanOptions>>;
4
3
  export default _default;
@@ -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
- const { devDepsMode = 'none', includeOptionalDeps = true, transitiveDepsMode = 'all' } = options ?? {};
11
- const pluginScanOptions = { devDepsMode, includeOptionalDeps, transitiveDepsMode };
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,5 +1,5 @@
1
1
  import { type ConfigPlugin } from 'expo/config-plugins';
2
- import type { PlatformPluginOptions } from '../types';
2
+ import type { PlatformPluginOptions } from '../../../plugin-utils/build/types';
3
3
  /**
4
4
  * Implementation of config plugin for iOS setup
5
5
  *
@@ -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, licenses_1.scanDependencies)(node_path_1.default.join(exportedConfig.modRequest.projectRoot, 'package.json'), scanOptionsFactory);
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
  });
@@ -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 path from 'node:path';
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 type { PlatformPluginOptions } from '../types';
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> = (config, { scanOptionsFactory }) => {
18
+ export const withAndroidLegal: ConfigPlugin<PlatformPluginOptions> = (
19
+ config,
20
+ { scanOptionsFactory, dependencySource, additionalProjectRoots },
21
+ ) => {
20
22
  withAndroidManifest(config, async (exportedConfig) => {
21
- const licenses = scanDependencies(
22
- path.join(exportedConfig.modRequest.projectRoot, 'package.json'),
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;
@@ -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
- const { devDepsMode = 'none', includeOptionalDeps = true, transitiveDepsMode = 'all' } = options ?? {};
16
- const pluginScanOptions: PluginScanOptions = { devDepsMode, includeOptionalDeps, transitiveDepsMode };
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 path from 'node:path';
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 type { PlatformPluginOptions } from '../types';
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> = (config, { scanOptionsFactory }) => {
16
+ export const withIosLegal: ConfigPlugin<PlatformPluginOptions> = (
17
+ config,
18
+ { scanOptionsFactory, dependencySource, additionalProjectRoots },
19
+ ) => {
18
20
  withXcodeProject(config, async (exportedConfig) => {
19
- const licenses = scanDependencies(
20
- path.join(exportedConfig.modRequest.projectRoot, 'package.json'),
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;
@@ -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 { Types as SharedTypes } from '@callstack/licenses';
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 type { Types as SharedTypes } from '@callstack/licenses';
1
+ import path from 'node:path';
2
2
 
3
- import type { PluginScanOptions } from './types';
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
+ };
@@ -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
- default: 'all',
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
- /** @type {import('./plugin-utils/build/types').PluginScanOptions} */
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
- generateLegal(android?.sourceDir, ios?.sourceDir, { devDepsMode, includeOptionalDeps, transitiveDepsMode });
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
  ],