@callstack/licenses 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +74 -0
- package/build/common.d.ts +71 -0
- package/build/common.js +308 -0
- package/build/index.d.ts +3 -0
- package/build/index.js +42 -0
- package/build/types/AboutLibrariesLibraryJsonPayload.d.ts +13 -0
- package/build/types/AboutLibrariesLibraryJsonPayload.js +2 -0
- package/build/types/AboutLibrariesLicenseJsonPayload.d.ts +6 -0
- package/build/types/AboutLibrariesLicenseJsonPayload.js +2 -0
- package/build/types/AboutLibrariesLikePackageInfo.d.ts +7 -0
- package/build/types/AboutLibrariesLikePackageInfo.js +2 -0
- package/build/types/AggregatedLicensesObj.d.ts +6 -0
- package/build/types/AggregatedLicensesObj.js +2 -0
- package/build/types/DependencyType.d.ts +3 -0
- package/build/types/DependencyType.js +2 -0
- package/build/types/LicenseObj.d.ts +24 -0
- package/build/types/LicenseObj.js +2 -0
- package/build/types/LicensePlistPayload.d.ts +7 -0
- package/build/types/LicensePlistPayload.js +2 -0
- package/build/types/ScanPackageCallContext.d.ts +25 -0
- package/build/types/ScanPackageCallContext.js +2 -0
- package/build/types/ScanPackageOptions.d.ts +24 -0
- package/build/types/ScanPackageOptions.js +2 -0
- package/build/types/index.d.ts +9 -0
- package/build/types/index.js +25 -0
- package/build/utils/index.d.ts +4 -0
- package/build/utils/index.js +40 -0
- package/build/utils/miscUtils.d.ts +6 -0
- package/build/utils/miscUtils.js +39 -0
- package/build/utils/packageUtils.d.ts +26 -0
- package/build/utils/packageUtils.js +100 -0
- package/build/utils/repositoryUtils.d.ts +1 -0
- package/build/utils/repositoryUtils.js +18 -0
- package/build/utils/yamlUtils.d.ts +4 -0
- package/build/utils/yamlUtils.js +64 -0
- package/package.json +67 -0
- package/src/common.ts +384 -0
- package/src/index.ts +3 -0
- package/src/types/AboutLibrariesLibraryJsonPayload.ts +10 -0
- package/src/types/AboutLibrariesLicenseJsonPayload.ts +6 -0
- package/src/types/AboutLibrariesLikePackageInfo.ts +8 -0
- package/src/types/AggregatedLicensesObj.ts +7 -0
- package/src/types/DependencyType.ts +3 -0
- package/src/types/LicenseObj.ts +34 -0
- package/src/types/LicensePlistPayload.ts +7 -0
- package/src/types/ScanPackageCallContext.ts +30 -0
- package/src/types/ScanPackageOptions.ts +29 -0
- package/src/types/index.ts +9 -0
- package/src/utils/index.ts +4 -0
- package/src/utils/miscUtils.ts +42 -0
- package/src/utils/packageUtils.ts +103 -0
- package/src/utils/repositoryUtils.ts +17 -0
- package/src/utils/yamlUtils.ts +73 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 Callstack
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# @callstack/licenses
|
|
2
|
+
|
|
3
|
+
## Features
|
|
4
|
+
|
|
5
|
+
- 🔍 Scan and aggregate license information from your project dependencies
|
|
6
|
+
- 📝 Generate license reports in a format of choice (JSON, Markdown, raw text, AboutLibraries-compatible JSON metadata)
|
|
7
|
+
- 🔄 Support for both direct and transitive dependencies
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install @callstack/licenses
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Programmatic Usage
|
|
16
|
+
|
|
17
|
+
You can use `@callstack/licenses` programmatically in your Node.js applications. Here's a basic example of how to use it:
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
import {
|
|
21
|
+
generateAboutLibrariesNPMOutput,
|
|
22
|
+
generateLicensePlistNPMOutput,
|
|
23
|
+
scanDependencies,
|
|
24
|
+
Types,
|
|
25
|
+
} from '@callstack/licenses';
|
|
26
|
+
import * as md from 'ts-markdown-builder';
|
|
27
|
+
|
|
28
|
+
// apart from dependencies, also include devDependencies, but only from the root package.json;
|
|
29
|
+
// also, include all transitive dependencies & optional dependencies
|
|
30
|
+
const optionsFactory: Types.ScanPackageOptionsFactory = ({ isRoot }) => ({
|
|
31
|
+
includeDevDependencies: isRoot,
|
|
32
|
+
includeTransitiveDependencies: true,
|
|
33
|
+
includeOptionalDependencies: true,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// scan dependencies of a package
|
|
37
|
+
const licenses = scanDependencies(packageJsonPath, optionsFactory);
|
|
38
|
+
|
|
39
|
+
// generate AboutLibraries-compatible JSON metadata
|
|
40
|
+
const aboutLibrariesCompatibleReport = generateAboutLibrariesNPMOutput(licenses);
|
|
41
|
+
|
|
42
|
+
// generate LicensePlist-compatible metadata
|
|
43
|
+
const licensePlistReport = generateLicensePlistNPMOutput(licenses, iosProjectPath);
|
|
44
|
+
|
|
45
|
+
// generate a Markdown report
|
|
46
|
+
const markdownString = md
|
|
47
|
+
.joinBlocks(
|
|
48
|
+
Object.entries(licenses)
|
|
49
|
+
.flatMap(([packageKey, { name: packageName, version, author, content, description, file, type, url }]) => [
|
|
50
|
+
md.heading(packageName, { level: 2 }),
|
|
51
|
+
'\n',
|
|
52
|
+
`Version: ${version}<br/>\n`,
|
|
53
|
+
url ? `URL: ${url}<br/>\n` : '',
|
|
54
|
+
author ? `Author: ${author}<br/>\n\n` : '',
|
|
55
|
+
content ?? '',
|
|
56
|
+
'\n',
|
|
57
|
+
description ? `Description: ${description}\n` : '',
|
|
58
|
+
file ? `\nFile: ${file}\n` : '',
|
|
59
|
+
type ? `Type: ${type}` : '',
|
|
60
|
+
'\n',
|
|
61
|
+
md.horizontalRule,
|
|
62
|
+
])
|
|
63
|
+
.join('\n'),
|
|
64
|
+
)
|
|
65
|
+
.toString();
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## API Documentation
|
|
69
|
+
|
|
70
|
+
The API documentation is published under: https://callstackincubator.github.io/react-native-legal/api/.
|
|
71
|
+
|
|
72
|
+
## Contributing
|
|
73
|
+
|
|
74
|
+
This package is consumed by other packages in the monorepo by its build outputs, so everytime it is modified, you need to rebuild the package. This can be done once by running `yarn build`, or by running `yarn dev` to run `tsc` in watch mode. All this is described in the [Contributing Guide](../../CONTRIBUTING.md).
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { AboutLibrariesLikePackageInfo, AggregatedLicensesObj, ScanPackageOptionsFactory } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Scans `package.json` and searches for all packages under `dependencies` field. Supports monorepo projects.
|
|
4
|
+
*
|
|
5
|
+
* @param appPackageJsonPath Path to the `package.json` file of the application
|
|
6
|
+
* @param scanOptionsFactory Factory function to create scan options for dependencies; defaults to {@link PackageUtils.legacyDefaultScanPackageOptionsFactory}
|
|
7
|
+
* @returns Aggregated licenses object containing all scanned dependencies and their license information
|
|
8
|
+
*/
|
|
9
|
+
export declare function scanDependencies(appPackageJsonPath: string, scanOptionsFactory?: ScanPackageOptionsFactory): AggregatedLicensesObj;
|
|
10
|
+
/**
|
|
11
|
+
* Generates LicensePlist-compatible metadata for NPM dependencies as a YAML string.
|
|
12
|
+
*
|
|
13
|
+
* To write a file directly, use `writeLicensePlistNPMOutput` function.
|
|
14
|
+
*
|
|
15
|
+
* @param licenses Scanned NPM licenses
|
|
16
|
+
* @param iosProjectPath Path to the iOS project directory
|
|
17
|
+
* @see {@link writeLicensePlistNPMOutput}
|
|
18
|
+
*/
|
|
19
|
+
export declare function generateLicensePlistNPMOutput(licenses: AggregatedLicensesObj, iosProjectPath: string): string;
|
|
20
|
+
/**
|
|
21
|
+
* Writes LicensePlist-compatible metadata for NPM dependencies to a file
|
|
22
|
+
*
|
|
23
|
+
* This will take scanned NPM licenses and produce following output inside iOS project's directory:
|
|
24
|
+
*
|
|
25
|
+
* ```
|
|
26
|
+
* | - ios
|
|
27
|
+
* | ---- myawesomeapp
|
|
28
|
+
* | ---- myawesomeapp.xcodeproj
|
|
29
|
+
* | ---- myawesomeapp.xcodeworkspace
|
|
30
|
+
* | ---- license_plist.yml <--- generated LicensePlist config with NPM dependencies
|
|
31
|
+
* | ---- Podfile
|
|
32
|
+
* | ---- Podfile.lock
|
|
33
|
+
* ```
|
|
34
|
+
*
|
|
35
|
+
* @param licenses Scanned NPM licenses
|
|
36
|
+
* @param iosProjectPath Path to the iOS project directory
|
|
37
|
+
* @param plistLikeOutput Optional pre-generated string output to use instead of generating it using `generateLicensePlistNPMOutput`
|
|
38
|
+
* @see {@link generateLicensePlistNPMOutput}
|
|
39
|
+
*/
|
|
40
|
+
export declare function writeLicensePlistNPMOutput(licenses: AggregatedLicensesObj, iosProjectPath: string, plistLikeOutput?: string): void;
|
|
41
|
+
/**
|
|
42
|
+
* Generates AboutLibraries-compatible metadata for NPM dependencies
|
|
43
|
+
*
|
|
44
|
+
* This will take scanned NPM licenses and produce output that can be modified and/or written to the Android project files.
|
|
45
|
+
*
|
|
46
|
+
* @param licenses Scanned NPM licenses
|
|
47
|
+
* @returns Array of AboutLibrariesLikePackage objects, each representing a NPM dependency
|
|
48
|
+
* @see {@link writeAboutLibrariesNPMOutput}
|
|
49
|
+
*/
|
|
50
|
+
export declare function generateAboutLibrariesNPMOutput(licenses: AggregatedLicensesObj): AboutLibrariesLikePackageInfo[];
|
|
51
|
+
/**
|
|
52
|
+
* Generates AboutLibraries-compatible metadata for NPM dependencies
|
|
53
|
+
*
|
|
54
|
+
* This will take scanned NPM licenses and produce following output inside android project's directory:
|
|
55
|
+
*
|
|
56
|
+
* ```
|
|
57
|
+
* | - android
|
|
58
|
+
* | ---- app
|
|
59
|
+
* | ---- config <--- generated AboutLibraries config directory
|
|
60
|
+
* | ------- libraries <--- generated directory with JSON files list of NPM dependencies
|
|
61
|
+
* | ------- licenses <--- generated directory with JSON files list of used licenses
|
|
62
|
+
* | ---- build.gradle
|
|
63
|
+
* | ---- settings.gradle
|
|
64
|
+
* ```
|
|
65
|
+
*
|
|
66
|
+
* @param licenses Scanned NPM licenses
|
|
67
|
+
* @param androidProjectPath Path to the Android project directory
|
|
68
|
+
* @param aboutLibrariesLikeOutput Optional pre-generated output to use instead of generating it using `generateAboutLibrariesNPMOutput`
|
|
69
|
+
* @see {@link generateAboutLibrariesNPMOutput}
|
|
70
|
+
*/
|
|
71
|
+
export declare function writeAboutLibrariesNPMOutput(licenses: AggregatedLicensesObj, androidProjectPath: string, aboutLibrariesLikeOutput?: AboutLibrariesLikePackageInfo[]): void;
|
package/build/common.js
ADDED
|
@@ -0,0 +1,308 @@
|
|
|
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.scanDependencies = scanDependencies;
|
|
7
|
+
exports.generateLicensePlistNPMOutput = generateLicensePlistNPMOutput;
|
|
8
|
+
exports.writeLicensePlistNPMOutput = writeLicensePlistNPMOutput;
|
|
9
|
+
exports.generateAboutLibrariesNPMOutput = generateAboutLibrariesNPMOutput;
|
|
10
|
+
exports.writeAboutLibrariesNPMOutput = writeAboutLibrariesNPMOutput;
|
|
11
|
+
const fs_1 = __importDefault(require("fs"));
|
|
12
|
+
const path_1 = __importDefault(require("path"));
|
|
13
|
+
const glob_1 = __importDefault(require("glob"));
|
|
14
|
+
const utils_1 = require("./utils");
|
|
15
|
+
/**
|
|
16
|
+
* Scans a single package and its dependencies for license information
|
|
17
|
+
*
|
|
18
|
+
* @param packageName Name of the package to scan
|
|
19
|
+
* @param requiredVersion Version of the package to scan; this is the version specifier from package.json, e.g. `^1.0.0`, `~2.3.4`, etc.
|
|
20
|
+
* @param processedPackages Set of already processed packages (avoids cycles)
|
|
21
|
+
* @param result Aggregated licenses object to store the results; the keys will be in the format of `packageName@version` where version is the resolved version of the package
|
|
22
|
+
* @param scanOptionsFactory Factory function to create scan options for dependencies; defaults to {@link PackageUtils.legacyDefaultScanPackageOptionsFactory}
|
|
23
|
+
* @param isOptionalDependency Whether the package is an optional dependency, in which case a warning will not be logged if the corresponding package.json is not found; defaults to `false`
|
|
24
|
+
* @param parentPackageRoot Optional path to the parent package root, has priority over default root to lock for dependencies in; used to discover different versions of the same package installed in nested node_modules, e.g. suppose `X@1`, `Y@1` where `Y@1` -> `X@2`; then, node_modules would have `X@1`, `Y@1` and `X@2` would be installed to `node_modules/Y/node_modules/X@2`
|
|
25
|
+
*/
|
|
26
|
+
function scanPackage(packageName, requiredVersion, processedPackages, result, scanOptionsFactory = utils_1.PackageUtils.legacyDefaultScanPackageOptionsFactory, { parentPackageRoot, parentPackageName, dependencyType, parentPackageRequiredVersion, parentPackageResolvedVersion, }) {
|
|
27
|
+
const requiredVersionPackageKey = `${packageName}@${requiredVersion}`;
|
|
28
|
+
// Skip if already processed to avoid circular dependencies
|
|
29
|
+
if (processedPackages.has(requiredVersionPackageKey)) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
// If the package is a file: dependency, warn about lack of support
|
|
33
|
+
if (requiredVersion.startsWith('file:')) {
|
|
34
|
+
console.warn(`[react-native-legal] ${packageName} (${requiredVersion}) is 'file:' dependency. Such packages are not supported yet (see https://callstackincubator.github.io/react-native-legal/docs/programmatic-usage.html#known-limitations).`);
|
|
35
|
+
}
|
|
36
|
+
processedPackages.add(requiredVersionPackageKey);
|
|
37
|
+
try {
|
|
38
|
+
const localPackageJsonPath = utils_1.PackageUtils.getPackageJsonPath(packageName, parentPackageRoot);
|
|
39
|
+
if (!localPackageJsonPath) {
|
|
40
|
+
// do not warn if the package is an optional dependency, it's normal it may not be installed
|
|
41
|
+
if (!dependencyType.toLowerCase().includes('optional')) {
|
|
42
|
+
console.warn(`[react-native-legal] skipping ${requiredVersionPackageKey} could not find package.json`);
|
|
43
|
+
}
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const localPackageJson = require(path_1.default.resolve(localPackageJsonPath));
|
|
47
|
+
if (localPackageJson.private !== true) {
|
|
48
|
+
const licenseFiles = glob_1.default.sync('LICEN{S,C}E{.md,}', {
|
|
49
|
+
cwd: path_1.default.dirname(localPackageJsonPath),
|
|
50
|
+
absolute: true,
|
|
51
|
+
nocase: true,
|
|
52
|
+
nodir: true,
|
|
53
|
+
ignore: '**/{__tests__,__fixtures__,__mocks__}/**',
|
|
54
|
+
});
|
|
55
|
+
const resolvedVersionPackageKey = `${packageName}@${localPackageJson.version}`;
|
|
56
|
+
result[resolvedVersionPackageKey] = {
|
|
57
|
+
name: packageName,
|
|
58
|
+
author: utils_1.PackageUtils.parseAuthorField(localPackageJson),
|
|
59
|
+
content: licenseFiles?.[0] ? fs_1.default.readFileSync(licenseFiles[0], { encoding: 'utf-8' }) : undefined,
|
|
60
|
+
file: licenseFiles?.[0] ? licenseFiles[0] : undefined,
|
|
61
|
+
description: localPackageJson.description,
|
|
62
|
+
type: utils_1.PackageUtils.parseLicenseField(localPackageJson),
|
|
63
|
+
url: utils_1.PackageUtils.parseRepositoryFieldToUrl(localPackageJson),
|
|
64
|
+
version: localPackageJson.version,
|
|
65
|
+
requiredVersion,
|
|
66
|
+
parentPackageName,
|
|
67
|
+
parentPackageRequiredVersion,
|
|
68
|
+
parentPackageResolvedVersion,
|
|
69
|
+
dependencyType,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
const dependencies = localPackageJson.dependencies;
|
|
73
|
+
const devDependencies = localPackageJson.devDependencies;
|
|
74
|
+
const optionalDependencies = localPackageJson.optionalDependencies;
|
|
75
|
+
const isWorkspacePackage = requiredVersion.startsWith('workspace:');
|
|
76
|
+
const scanOptions = scanOptionsFactory({
|
|
77
|
+
isRoot: false,
|
|
78
|
+
isWorkspacePackage,
|
|
79
|
+
});
|
|
80
|
+
// check if transitive dependencies should be scanned
|
|
81
|
+
if (!scanOptions.includeTransitiveDependencies) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
// helper used for finding nested dependencies installed with different versions for a given package, see docstring of scanPackage
|
|
85
|
+
const currentPackageRoot = path_1.default.dirname(localPackageJsonPath);
|
|
86
|
+
for (const { dependencyType, packages } of [
|
|
87
|
+
{
|
|
88
|
+
dependencyType: 'transitiveDependency',
|
|
89
|
+
packages: dependencies ? Object.entries(dependencies) : [],
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
dependencyType: 'transitiveDevDependency',
|
|
93
|
+
packages: devDependencies && scanOptions.includeDevDependencies ? Object.entries(devDependencies) : [],
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
dependencyType: 'transitiveOptionalDependency',
|
|
97
|
+
packages: optionalDependencies && scanOptions.includeOptionalDependencies ? Object.entries(optionalDependencies) : [],
|
|
98
|
+
},
|
|
99
|
+
]) {
|
|
100
|
+
for (const [depName, depVersion] of packages) {
|
|
101
|
+
scanPackage(depName, depVersion, processedPackages, result, scanOptionsFactory, {
|
|
102
|
+
dependencyType,
|
|
103
|
+
parentPackageRoot: currentPackageRoot,
|
|
104
|
+
parentPackageName: packageName,
|
|
105
|
+
parentPackageRequiredVersion: requiredVersion,
|
|
106
|
+
parentPackageResolvedVersion: localPackageJson.version,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
console.warn(`[react-native-legal] could not process package.json for ${packageName}`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Scans `package.json` and searches for all packages under `dependencies` field. Supports monorepo projects.
|
|
117
|
+
*
|
|
118
|
+
* @param appPackageJsonPath Path to the `package.json` file of the application
|
|
119
|
+
* @param scanOptionsFactory Factory function to create scan options for dependencies; defaults to {@link PackageUtils.legacyDefaultScanPackageOptionsFactory}
|
|
120
|
+
* @returns Aggregated licenses object containing all scanned dependencies and their license information
|
|
121
|
+
*/
|
|
122
|
+
function scanDependencies(appPackageJsonPath, scanOptionsFactory = utils_1.PackageUtils.legacyDefaultScanPackageOptionsFactory) {
|
|
123
|
+
const appPackageJson = require(path_1.default.resolve(appPackageJsonPath));
|
|
124
|
+
const dependencies = appPackageJson.dependencies;
|
|
125
|
+
const devDependencies = appPackageJson.devDependencies;
|
|
126
|
+
const optionalDependencies = appPackageJson.optionalDependencies;
|
|
127
|
+
const result = {};
|
|
128
|
+
const processedPackages = new Set();
|
|
129
|
+
const rootScanOptions = scanOptionsFactory({ isRoot: true, isWorkspacePackage: false });
|
|
130
|
+
for (const { dependencyType, packages } of [
|
|
131
|
+
{
|
|
132
|
+
dependencyType: 'dependency',
|
|
133
|
+
packages: dependencies ? Object.entries(dependencies) : [],
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
dependencyType: 'devDependency',
|
|
137
|
+
packages: devDependencies && rootScanOptions.includeDevDependencies ? Object.entries(devDependencies) : [],
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
dependencyType: 'optionalDependency',
|
|
141
|
+
packages: optionalDependencies && rootScanOptions.includeOptionalDependencies ? Object.entries(optionalDependencies) : [],
|
|
142
|
+
},
|
|
143
|
+
]) {
|
|
144
|
+
for (const [depName, depVersion] of packages) {
|
|
145
|
+
scanPackage(depName, depVersion, processedPackages, result, scanOptionsFactory, {
|
|
146
|
+
dependencyType,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return result;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Generates LicensePlist-compatible metadata for NPM dependencies as a YAML string.
|
|
154
|
+
*
|
|
155
|
+
* To write a file directly, use `writeLicensePlistNPMOutput` function.
|
|
156
|
+
*
|
|
157
|
+
* @param licenses Scanned NPM licenses
|
|
158
|
+
* @param iosProjectPath Path to the iOS project directory
|
|
159
|
+
* @see {@link writeLicensePlistNPMOutput}
|
|
160
|
+
*/
|
|
161
|
+
function generateLicensePlistNPMOutput(licenses, iosProjectPath) {
|
|
162
|
+
const renames = {};
|
|
163
|
+
const licenseEntries = Object.entries(licenses).map(([packageKey, licenseObj]) => {
|
|
164
|
+
const normalizedPackageNameWithVersion = utils_1.PackageUtils.normalizePackageName(packageKey);
|
|
165
|
+
if (licenseObj.name !== normalizedPackageNameWithVersion) {
|
|
166
|
+
renames[normalizedPackageNameWithVersion] = licenseObj.name;
|
|
167
|
+
}
|
|
168
|
+
const relativeLicenseFile = licenseObj.file ? path_1.default.relative(iosProjectPath, licenseObj.file) : undefined;
|
|
169
|
+
return {
|
|
170
|
+
name: normalizedPackageNameWithVersion,
|
|
171
|
+
version: licenseObj.version,
|
|
172
|
+
...(licenseObj.url && { source: licenseObj.url }),
|
|
173
|
+
...(licenseObj.file
|
|
174
|
+
? { file: relativeLicenseFile }
|
|
175
|
+
: { body: licenseObj.content ?? licenseObj.type ?? 'UNKNOWN' }),
|
|
176
|
+
};
|
|
177
|
+
});
|
|
178
|
+
const yamlDoc = {
|
|
179
|
+
...(Object.keys(renames).length > 0 && { rename: renames }),
|
|
180
|
+
manual: licenseEntries,
|
|
181
|
+
};
|
|
182
|
+
const yamlContent = [
|
|
183
|
+
'# BEGIN Generated NPM license entries',
|
|
184
|
+
utils_1.YamlUtils.toYaml(yamlDoc),
|
|
185
|
+
'# END Generated NPM license entries',
|
|
186
|
+
].join('\n');
|
|
187
|
+
return yamlContent;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Writes LicensePlist-compatible metadata for NPM dependencies to a file
|
|
191
|
+
*
|
|
192
|
+
* This will take scanned NPM licenses and produce following output inside iOS project's directory:
|
|
193
|
+
*
|
|
194
|
+
* ```
|
|
195
|
+
* | - ios
|
|
196
|
+
* | ---- myawesomeapp
|
|
197
|
+
* | ---- myawesomeapp.xcodeproj
|
|
198
|
+
* | ---- myawesomeapp.xcodeworkspace
|
|
199
|
+
* | ---- license_plist.yml <--- generated LicensePlist config with NPM dependencies
|
|
200
|
+
* | ---- Podfile
|
|
201
|
+
* | ---- Podfile.lock
|
|
202
|
+
* ```
|
|
203
|
+
*
|
|
204
|
+
* @param licenses Scanned NPM licenses
|
|
205
|
+
* @param iosProjectPath Path to the iOS project directory
|
|
206
|
+
* @param plistLikeOutput Optional pre-generated string output to use instead of generating it using `generateLicensePlistNPMOutput`
|
|
207
|
+
* @see {@link generateLicensePlistNPMOutput}
|
|
208
|
+
*/
|
|
209
|
+
function writeLicensePlistNPMOutput(licenses, iosProjectPath, plistLikeOutput) {
|
|
210
|
+
if (!plistLikeOutput) {
|
|
211
|
+
plistLikeOutput = generateLicensePlistNPMOutput(licenses, iosProjectPath);
|
|
212
|
+
}
|
|
213
|
+
fs_1.default.writeFileSync(path_1.default.join(iosProjectPath, 'license_plist.yml'), plistLikeOutput, { encoding: 'utf-8' });
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Generates AboutLibraries-compatible metadata for NPM dependencies
|
|
217
|
+
*
|
|
218
|
+
* This will take scanned NPM licenses and produce output that can be modified and/or written to the Android project files.
|
|
219
|
+
*
|
|
220
|
+
* @param licenses Scanned NPM licenses
|
|
221
|
+
* @returns Array of AboutLibrariesLikePackage objects, each representing a NPM dependency
|
|
222
|
+
* @see {@link writeAboutLibrariesNPMOutput}
|
|
223
|
+
*/
|
|
224
|
+
function generateAboutLibrariesNPMOutput(licenses) {
|
|
225
|
+
return Object.entries(licenses)
|
|
226
|
+
.map(([packageKey, licenseObj]) => {
|
|
227
|
+
return {
|
|
228
|
+
artifactVersion: licenseObj.version,
|
|
229
|
+
content: licenseObj.content ?? '',
|
|
230
|
+
description: licenseObj.description ?? '',
|
|
231
|
+
developers: [{ name: licenseObj.author ?? '', organisationUrl: '' }],
|
|
232
|
+
licenses: [utils_1.PackageUtils.prepareAboutLibrariesLicenseField(licenseObj)],
|
|
233
|
+
name: licenseObj.name,
|
|
234
|
+
tag: '',
|
|
235
|
+
type: licenseObj.type,
|
|
236
|
+
uniqueId: utils_1.PackageUtils.normalizePackageName(packageKey),
|
|
237
|
+
website: licenseObj.url,
|
|
238
|
+
};
|
|
239
|
+
})
|
|
240
|
+
.map((jsonPayload) => {
|
|
241
|
+
const libraryJsonPayload = {
|
|
242
|
+
artifactVersion: jsonPayload.artifactVersion,
|
|
243
|
+
description: jsonPayload.description,
|
|
244
|
+
developers: jsonPayload.developers,
|
|
245
|
+
licenses: jsonPayload.licenses,
|
|
246
|
+
name: jsonPayload.name,
|
|
247
|
+
tag: jsonPayload.tag,
|
|
248
|
+
uniqueId: jsonPayload.uniqueId,
|
|
249
|
+
website: jsonPayload.website,
|
|
250
|
+
};
|
|
251
|
+
const licenseJsonPayload = {
|
|
252
|
+
content: jsonPayload.content,
|
|
253
|
+
hash: jsonPayload.licenses[0],
|
|
254
|
+
name: jsonPayload.type ?? '',
|
|
255
|
+
url: '',
|
|
256
|
+
};
|
|
257
|
+
return {
|
|
258
|
+
normalizedPackageNameWithVersion: jsonPayload.uniqueId,
|
|
259
|
+
libraryJsonPayload,
|
|
260
|
+
licenseJsonPayload,
|
|
261
|
+
};
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Generates AboutLibraries-compatible metadata for NPM dependencies
|
|
266
|
+
*
|
|
267
|
+
* This will take scanned NPM licenses and produce following output inside android project's directory:
|
|
268
|
+
*
|
|
269
|
+
* ```
|
|
270
|
+
* | - android
|
|
271
|
+
* | ---- app
|
|
272
|
+
* | ---- config <--- generated AboutLibraries config directory
|
|
273
|
+
* | ------- libraries <--- generated directory with JSON files list of NPM dependencies
|
|
274
|
+
* | ------- licenses <--- generated directory with JSON files list of used licenses
|
|
275
|
+
* | ---- build.gradle
|
|
276
|
+
* | ---- settings.gradle
|
|
277
|
+
* ```
|
|
278
|
+
*
|
|
279
|
+
* @param licenses Scanned NPM licenses
|
|
280
|
+
* @param androidProjectPath Path to the Android project directory
|
|
281
|
+
* @param aboutLibrariesLikeOutput Optional pre-generated output to use instead of generating it using `generateAboutLibrariesNPMOutput`
|
|
282
|
+
* @see {@link generateAboutLibrariesNPMOutput}
|
|
283
|
+
*/
|
|
284
|
+
function writeAboutLibrariesNPMOutput(licenses, androidProjectPath, aboutLibrariesLikeOutput) {
|
|
285
|
+
const aboutLibrariesConfigDirPath = path_1.default.join(androidProjectPath, 'config');
|
|
286
|
+
const aboutLibrariesConfigLibrariesDirPath = path_1.default.join(aboutLibrariesConfigDirPath, 'libraries');
|
|
287
|
+
const aboutLibrariesConfigLicensesDirPath = path_1.default.join(aboutLibrariesConfigDirPath, 'licenses');
|
|
288
|
+
if (!fs_1.default.existsSync(aboutLibrariesConfigDirPath)) {
|
|
289
|
+
fs_1.default.mkdirSync(aboutLibrariesConfigDirPath);
|
|
290
|
+
}
|
|
291
|
+
if (!fs_1.default.existsSync(aboutLibrariesConfigLibrariesDirPath)) {
|
|
292
|
+
fs_1.default.mkdirSync(aboutLibrariesConfigLibrariesDirPath);
|
|
293
|
+
}
|
|
294
|
+
if (!fs_1.default.existsSync(aboutLibrariesConfigLicensesDirPath)) {
|
|
295
|
+
fs_1.default.mkdirSync(aboutLibrariesConfigLicensesDirPath);
|
|
296
|
+
}
|
|
297
|
+
if (!aboutLibrariesLikeOutput) {
|
|
298
|
+
aboutLibrariesLikeOutput = generateAboutLibrariesNPMOutput(licenses);
|
|
299
|
+
}
|
|
300
|
+
aboutLibrariesLikeOutput.forEach(({ normalizedPackageNameWithVersion, libraryJsonPayload, licenseJsonPayload }) => {
|
|
301
|
+
const libraryJsonFilePath = path_1.default.join(aboutLibrariesConfigLibrariesDirPath, `${normalizedPackageNameWithVersion}.json`);
|
|
302
|
+
const licenseJsonFilePath = path_1.default.join(aboutLibrariesConfigLicensesDirPath, `${licenseJsonPayload.hash}.json`);
|
|
303
|
+
fs_1.default.writeFileSync(libraryJsonFilePath, JSON.stringify(libraryJsonPayload));
|
|
304
|
+
if (!fs_1.default.existsSync(licenseJsonFilePath)) {
|
|
305
|
+
fs_1.default.writeFileSync(licenseJsonFilePath, JSON.stringify(licenseJsonPayload));
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
}
|
package/build/index.d.ts
ADDED
package/build/index.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
19
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
20
|
+
};
|
|
21
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
22
|
+
var ownKeys = function(o) {
|
|
23
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
24
|
+
var ar = [];
|
|
25
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
26
|
+
return ar;
|
|
27
|
+
};
|
|
28
|
+
return ownKeys(o);
|
|
29
|
+
};
|
|
30
|
+
return function (mod) {
|
|
31
|
+
if (mod && mod.__esModule) return mod;
|
|
32
|
+
var result = {};
|
|
33
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
34
|
+
__setModuleDefault(result, mod);
|
|
35
|
+
return result;
|
|
36
|
+
};
|
|
37
|
+
})();
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.Types = void 0;
|
|
40
|
+
__exportStar(require("./common"), exports);
|
|
41
|
+
exports.Types = __importStar(require("./types"));
|
|
42
|
+
__exportStar(require("./utils"), exports);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type AboutLibrariesLibraryJsonPayload = {
|
|
2
|
+
artifactVersion: string;
|
|
3
|
+
description: string;
|
|
4
|
+
developers: {
|
|
5
|
+
name: string;
|
|
6
|
+
organisationUrl: string;
|
|
7
|
+
}[];
|
|
8
|
+
licenses: string[];
|
|
9
|
+
name: string;
|
|
10
|
+
tag: string;
|
|
11
|
+
uniqueId: string;
|
|
12
|
+
website: string | undefined;
|
|
13
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { AboutLibrariesLibraryJsonPayload } from './AboutLibrariesLibraryJsonPayload';
|
|
2
|
+
import type { AboutLibrariesLicenseJsonPayload } from './AboutLibrariesLicenseJsonPayload';
|
|
3
|
+
export type AboutLibrariesLikePackageInfo = {
|
|
4
|
+
normalizedPackageNameWithVersion: string;
|
|
5
|
+
libraryJsonPayload: AboutLibrariesLibraryJsonPayload;
|
|
6
|
+
licenseJsonPayload: AboutLibrariesLicenseJsonPayload;
|
|
7
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { DependencyType } from './DependencyType';
|
|
2
|
+
import type { ScanPackageCallContext } from './ScanPackageCallContext';
|
|
3
|
+
export type LicenseObj = {
|
|
4
|
+
/** Package name */
|
|
5
|
+
name: string;
|
|
6
|
+
/** Package author */
|
|
7
|
+
author?: string;
|
|
8
|
+
/** Package license contents */
|
|
9
|
+
content?: string;
|
|
10
|
+
/** Package description */
|
|
11
|
+
description?: string;
|
|
12
|
+
/** License file path */
|
|
13
|
+
file?: string;
|
|
14
|
+
/** License type */
|
|
15
|
+
type?: string;
|
|
16
|
+
/** Package repository URL */
|
|
17
|
+
url?: string;
|
|
18
|
+
/** The resolved version that was actually installed */
|
|
19
|
+
version: string;
|
|
20
|
+
/** The source of how this package has been introduced to the dependency tree */
|
|
21
|
+
dependencyType: DependencyType;
|
|
22
|
+
/** The required version specified in package.json */
|
|
23
|
+
requiredVersion: string;
|
|
24
|
+
} & Pick<ScanPackageCallContext, 'parentPackageName' | 'parentPackageRequiredVersion' | 'parentPackageResolvedVersion'>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { DependencyType } from './DependencyType';
|
|
2
|
+
export type ScanPackageCallContext = {
|
|
3
|
+
/** Information on what is the source of the dependency on the package to be scanned */
|
|
4
|
+
dependencyType: DependencyType;
|
|
5
|
+
/**
|
|
6
|
+
* The name of the package that introduced the package to be scanned as a dependency (of any type);
|
|
7
|
+
* undefined if the package to be scanned is a dependency of the root package
|
|
8
|
+
*/
|
|
9
|
+
parentPackageName?: string;
|
|
10
|
+
/**
|
|
11
|
+
* The installation directory of the package that introduced the package to be scanned as a dependency (of any type);
|
|
12
|
+
* undefined if the package to be scanned is a dependency of the root package
|
|
13
|
+
*/
|
|
14
|
+
parentPackageRoot?: string;
|
|
15
|
+
/**
|
|
16
|
+
* The required version, as specified in package.json
|
|
17
|
+
* of the package that introduced the package to be scanned as a dependency (of any type)
|
|
18
|
+
*/
|
|
19
|
+
parentPackageRequiredVersion?: string;
|
|
20
|
+
/**
|
|
21
|
+
* The resolved (actually installed by the package manager) version
|
|
22
|
+
* of the package that introduced the package to be scanned as a dependency (of any type)
|
|
23
|
+
*/
|
|
24
|
+
parentPackageResolvedVersion?: string;
|
|
25
|
+
};
|