@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.
Files changed (54) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +74 -0
  3. package/build/common.d.ts +71 -0
  4. package/build/common.js +308 -0
  5. package/build/index.d.ts +3 -0
  6. package/build/index.js +42 -0
  7. package/build/types/AboutLibrariesLibraryJsonPayload.d.ts +13 -0
  8. package/build/types/AboutLibrariesLibraryJsonPayload.js +2 -0
  9. package/build/types/AboutLibrariesLicenseJsonPayload.d.ts +6 -0
  10. package/build/types/AboutLibrariesLicenseJsonPayload.js +2 -0
  11. package/build/types/AboutLibrariesLikePackageInfo.d.ts +7 -0
  12. package/build/types/AboutLibrariesLikePackageInfo.js +2 -0
  13. package/build/types/AggregatedLicensesObj.d.ts +6 -0
  14. package/build/types/AggregatedLicensesObj.js +2 -0
  15. package/build/types/DependencyType.d.ts +3 -0
  16. package/build/types/DependencyType.js +2 -0
  17. package/build/types/LicenseObj.d.ts +24 -0
  18. package/build/types/LicenseObj.js +2 -0
  19. package/build/types/LicensePlistPayload.d.ts +7 -0
  20. package/build/types/LicensePlistPayload.js +2 -0
  21. package/build/types/ScanPackageCallContext.d.ts +25 -0
  22. package/build/types/ScanPackageCallContext.js +2 -0
  23. package/build/types/ScanPackageOptions.d.ts +24 -0
  24. package/build/types/ScanPackageOptions.js +2 -0
  25. package/build/types/index.d.ts +9 -0
  26. package/build/types/index.js +25 -0
  27. package/build/utils/index.d.ts +4 -0
  28. package/build/utils/index.js +40 -0
  29. package/build/utils/miscUtils.d.ts +6 -0
  30. package/build/utils/miscUtils.js +39 -0
  31. package/build/utils/packageUtils.d.ts +26 -0
  32. package/build/utils/packageUtils.js +100 -0
  33. package/build/utils/repositoryUtils.d.ts +1 -0
  34. package/build/utils/repositoryUtils.js +18 -0
  35. package/build/utils/yamlUtils.d.ts +4 -0
  36. package/build/utils/yamlUtils.js +64 -0
  37. package/package.json +67 -0
  38. package/src/common.ts +384 -0
  39. package/src/index.ts +3 -0
  40. package/src/types/AboutLibrariesLibraryJsonPayload.ts +10 -0
  41. package/src/types/AboutLibrariesLicenseJsonPayload.ts +6 -0
  42. package/src/types/AboutLibrariesLikePackageInfo.ts +8 -0
  43. package/src/types/AggregatedLicensesObj.ts +7 -0
  44. package/src/types/DependencyType.ts +3 -0
  45. package/src/types/LicenseObj.ts +34 -0
  46. package/src/types/LicensePlistPayload.ts +7 -0
  47. package/src/types/ScanPackageCallContext.ts +30 -0
  48. package/src/types/ScanPackageOptions.ts +29 -0
  49. package/src/types/index.ts +9 -0
  50. package/src/utils/index.ts +4 -0
  51. package/src/utils/miscUtils.ts +42 -0
  52. package/src/utils/packageUtils.ts +103 -0
  53. package/src/utils/repositoryUtils.ts +17 -0
  54. package/src/utils/yamlUtils.ts +73 -0
package/src/common.ts ADDED
@@ -0,0 +1,384 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+
4
+ import glob from 'glob';
5
+
6
+ import type {
7
+ AboutLibrariesLibraryJsonPayload,
8
+ AboutLibrariesLicenseJsonPayload,
9
+ AboutLibrariesLikePackageInfo,
10
+ AggregatedLicensesObj,
11
+ DependencyType,
12
+ LicensePlistPayload,
13
+ ScanPackageCallContext,
14
+ ScanPackageOptionsFactory,
15
+ } from './types';
16
+ import { PackageUtils, YamlUtils } from './utils';
17
+
18
+ type InternalScanGroupSpecifier = { packages: [depName: string, depVersion: string][]; dependencyType: DependencyType };
19
+
20
+ /**
21
+ * Scans a single package and its dependencies for license information
22
+ *
23
+ * @param packageName Name of the package to scan
24
+ * @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.
25
+ * @param processedPackages Set of already processed packages (avoids cycles)
26
+ * @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
27
+ * @param scanOptionsFactory Factory function to create scan options for dependencies; defaults to {@link PackageUtils.legacyDefaultScanPackageOptionsFactory}
28
+ * @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`
29
+ * @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`
30
+ */
31
+ function scanPackage(
32
+ packageName: string,
33
+ requiredVersion: string,
34
+ processedPackages: Set<string>,
35
+ result: AggregatedLicensesObj,
36
+ scanOptionsFactory: ScanPackageOptionsFactory = PackageUtils.legacyDefaultScanPackageOptionsFactory,
37
+ {
38
+ parentPackageRoot,
39
+ parentPackageName,
40
+ dependencyType,
41
+ parentPackageRequiredVersion,
42
+ parentPackageResolvedVersion,
43
+ }: ScanPackageCallContext,
44
+ ) {
45
+ const requiredVersionPackageKey = `${packageName}@${requiredVersion}`;
46
+
47
+ // Skip if already processed to avoid circular dependencies
48
+ if (processedPackages.has(requiredVersionPackageKey)) {
49
+ return;
50
+ }
51
+
52
+ // If the package is a file: dependency, warn about lack of support
53
+ if (requiredVersion.startsWith('file:')) {
54
+ console.warn(
55
+ `[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).`,
56
+ );
57
+ }
58
+
59
+ processedPackages.add(requiredVersionPackageKey);
60
+
61
+ try {
62
+ const localPackageJsonPath = PackageUtils.getPackageJsonPath(packageName, parentPackageRoot);
63
+
64
+ if (!localPackageJsonPath) {
65
+ // do not warn if the package is an optional dependency, it's normal it may not be installed
66
+ if (!dependencyType.toLowerCase().includes('optional')) {
67
+ console.warn(`[react-native-legal] skipping ${requiredVersionPackageKey} could not find package.json`);
68
+ }
69
+
70
+ return;
71
+ }
72
+
73
+ const localPackageJson = require(path.resolve(localPackageJsonPath));
74
+
75
+ if (localPackageJson.private !== true) {
76
+ const licenseFiles = glob.sync('LICEN{S,C}E{.md,}', {
77
+ cwd: path.dirname(localPackageJsonPath),
78
+ absolute: true,
79
+ nocase: true,
80
+ nodir: true,
81
+ ignore: '**/{__tests__,__fixtures__,__mocks__}/**',
82
+ });
83
+
84
+ const resolvedVersionPackageKey = `${packageName}@${localPackageJson.version}`;
85
+
86
+ result[resolvedVersionPackageKey] = {
87
+ name: packageName,
88
+ author: PackageUtils.parseAuthorField(localPackageJson),
89
+ content: licenseFiles?.[0] ? fs.readFileSync(licenseFiles[0], { encoding: 'utf-8' }) : undefined,
90
+ file: licenseFiles?.[0] ? licenseFiles[0] : undefined,
91
+ description: localPackageJson.description,
92
+ type: PackageUtils.parseLicenseField(localPackageJson),
93
+ url: PackageUtils.parseRepositoryFieldToUrl(localPackageJson),
94
+ version: localPackageJson.version,
95
+ requiredVersion,
96
+ parentPackageName,
97
+ parentPackageRequiredVersion,
98
+ parentPackageResolvedVersion,
99
+ dependencyType,
100
+ };
101
+ }
102
+
103
+ const dependencies: MaybeDependencyMapping = localPackageJson.dependencies;
104
+ const devDependencies: MaybeDependencyMapping = localPackageJson.devDependencies;
105
+ const optionalDependencies: MaybeDependencyMapping = localPackageJson.optionalDependencies;
106
+ const isWorkspacePackage = requiredVersion.startsWith('workspace:');
107
+
108
+ const scanOptions = scanOptionsFactory({
109
+ isRoot: false,
110
+ isWorkspacePackage,
111
+ });
112
+
113
+ // check if transitive dependencies should be scanned
114
+ if (!scanOptions.includeTransitiveDependencies) {
115
+ return;
116
+ }
117
+
118
+ // helper used for finding nested dependencies installed with different versions for a given package, see docstring of scanPackage
119
+ const currentPackageRoot = path.dirname(localPackageJsonPath);
120
+
121
+ for (const { dependencyType, packages } of [
122
+ {
123
+ dependencyType: 'transitiveDependency',
124
+ packages: dependencies ? Object.entries(dependencies) : [],
125
+ },
126
+ {
127
+ dependencyType: 'transitiveDevDependency',
128
+ packages: devDependencies && scanOptions.includeDevDependencies ? Object.entries(devDependencies) : [],
129
+ },
130
+ {
131
+ dependencyType: 'transitiveOptionalDependency',
132
+ packages:
133
+ optionalDependencies && scanOptions.includeOptionalDependencies ? Object.entries(optionalDependencies) : [],
134
+ },
135
+ ] as InternalScanGroupSpecifier[]) {
136
+ for (const [depName, depVersion] of packages) {
137
+ scanPackage(depName, depVersion as string, processedPackages, result, scanOptionsFactory, {
138
+ dependencyType,
139
+ parentPackageRoot: currentPackageRoot,
140
+ parentPackageName: packageName,
141
+ parentPackageRequiredVersion: requiredVersion,
142
+ parentPackageResolvedVersion: localPackageJson.version,
143
+ });
144
+ }
145
+ }
146
+ } catch (error) {
147
+ console.warn(`[react-native-legal] could not process package.json for ${packageName}`);
148
+ }
149
+ }
150
+
151
+ type DependencyMapping = Record<string, string>;
152
+ type MaybeDependencyMapping = DependencyMapping | undefined;
153
+
154
+ /**
155
+ * Scans `package.json` and searches for all packages under `dependencies` field. Supports monorepo projects.
156
+ *
157
+ * @param appPackageJsonPath Path to the `package.json` file of the application
158
+ * @param scanOptionsFactory Factory function to create scan options for dependencies; defaults to {@link PackageUtils.legacyDefaultScanPackageOptionsFactory}
159
+ * @returns Aggregated licenses object containing all scanned dependencies and their license information
160
+ */
161
+ export function scanDependencies(
162
+ appPackageJsonPath: string,
163
+ scanOptionsFactory: ScanPackageOptionsFactory = PackageUtils.legacyDefaultScanPackageOptionsFactory,
164
+ ): AggregatedLicensesObj {
165
+ const appPackageJson = require(path.resolve(appPackageJsonPath));
166
+ const dependencies: MaybeDependencyMapping = appPackageJson.dependencies;
167
+ const devDependencies: MaybeDependencyMapping = appPackageJson.devDependencies;
168
+ const optionalDependencies: MaybeDependencyMapping = appPackageJson.optionalDependencies;
169
+ const result: AggregatedLicensesObj = {};
170
+ const processedPackages = new Set<string>();
171
+
172
+ const rootScanOptions = scanOptionsFactory({ isRoot: true, isWorkspacePackage: false });
173
+
174
+ for (const { dependencyType, packages } of [
175
+ {
176
+ dependencyType: 'dependency',
177
+ packages: dependencies ? Object.entries(dependencies) : [],
178
+ },
179
+ {
180
+ dependencyType: 'devDependency',
181
+ packages: devDependencies && rootScanOptions.includeDevDependencies ? Object.entries(devDependencies) : [],
182
+ },
183
+ {
184
+ dependencyType: 'optionalDependency',
185
+ packages:
186
+ optionalDependencies && rootScanOptions.includeOptionalDependencies ? Object.entries(optionalDependencies) : [],
187
+ },
188
+ ] as InternalScanGroupSpecifier[]) {
189
+ for (const [depName, depVersion] of packages) {
190
+ scanPackage(depName, depVersion as string, processedPackages, result, scanOptionsFactory, {
191
+ dependencyType,
192
+ });
193
+ }
194
+ }
195
+
196
+ return result;
197
+ }
198
+
199
+ /**
200
+ * Generates LicensePlist-compatible metadata for NPM dependencies as a YAML string.
201
+ *
202
+ * To write a file directly, use `writeLicensePlistNPMOutput` function.
203
+ *
204
+ * @param licenses Scanned NPM licenses
205
+ * @param iosProjectPath Path to the iOS project directory
206
+ * @see {@link writeLicensePlistNPMOutput}
207
+ */
208
+ export function generateLicensePlistNPMOutput(licenses: AggregatedLicensesObj, iosProjectPath: string): string {
209
+ const renames: Record<string, string> = {};
210
+ const licenseEntries = Object.entries(licenses).map(([packageKey, licenseObj]) => {
211
+ const normalizedPackageNameWithVersion = PackageUtils.normalizePackageName(packageKey);
212
+
213
+ if (licenseObj.name !== normalizedPackageNameWithVersion) {
214
+ renames[normalizedPackageNameWithVersion] = licenseObj.name;
215
+ }
216
+
217
+ const relativeLicenseFile = licenseObj.file ? path.relative(iosProjectPath, licenseObj.file) : undefined;
218
+
219
+ return {
220
+ name: normalizedPackageNameWithVersion,
221
+ version: licenseObj.version,
222
+ ...(licenseObj.url && { source: licenseObj.url }),
223
+ ...(licenseObj.file
224
+ ? { file: relativeLicenseFile }
225
+ : { body: licenseObj.content ?? licenseObj.type ?? 'UNKNOWN' }),
226
+ } as LicensePlistPayload;
227
+ });
228
+
229
+ const yamlDoc = {
230
+ ...(Object.keys(renames).length > 0 && { rename: renames }),
231
+ manual: licenseEntries,
232
+ };
233
+
234
+ const yamlContent = [
235
+ '# BEGIN Generated NPM license entries',
236
+ YamlUtils.toYaml(yamlDoc),
237
+ '# END Generated NPM license entries',
238
+ ].join('\n');
239
+
240
+ return yamlContent;
241
+ }
242
+
243
+ /**
244
+ * Writes LicensePlist-compatible metadata for NPM dependencies to a file
245
+ *
246
+ * This will take scanned NPM licenses and produce following output inside iOS project's directory:
247
+ *
248
+ * ```
249
+ * | - ios
250
+ * | ---- myawesomeapp
251
+ * | ---- myawesomeapp.xcodeproj
252
+ * | ---- myawesomeapp.xcodeworkspace
253
+ * | ---- license_plist.yml <--- generated LicensePlist config with NPM dependencies
254
+ * | ---- Podfile
255
+ * | ---- Podfile.lock
256
+ * ```
257
+ *
258
+ * @param licenses Scanned NPM licenses
259
+ * @param iosProjectPath Path to the iOS project directory
260
+ * @param plistLikeOutput Optional pre-generated string output to use instead of generating it using `generateLicensePlistNPMOutput`
261
+ * @see {@link generateLicensePlistNPMOutput}
262
+ */
263
+ export function writeLicensePlistNPMOutput(
264
+ licenses: AggregatedLicensesObj,
265
+ iosProjectPath: string,
266
+ plistLikeOutput?: string,
267
+ ) {
268
+ if (!plistLikeOutput) {
269
+ plistLikeOutput = generateLicensePlistNPMOutput(licenses, iosProjectPath);
270
+ }
271
+
272
+ fs.writeFileSync(path.join(iosProjectPath, 'license_plist.yml'), plistLikeOutput, { encoding: 'utf-8' });
273
+ }
274
+
275
+ /**
276
+ * Generates AboutLibraries-compatible metadata for NPM dependencies
277
+ *
278
+ * This will take scanned NPM licenses and produce output that can be modified and/or written to the Android project files.
279
+ *
280
+ * @param licenses Scanned NPM licenses
281
+ * @returns Array of AboutLibrariesLikePackage objects, each representing a NPM dependency
282
+ * @see {@link writeAboutLibrariesNPMOutput}
283
+ */
284
+ export function generateAboutLibrariesNPMOutput(licenses: AggregatedLicensesObj): AboutLibrariesLikePackageInfo[] {
285
+ return Object.entries(licenses)
286
+ .map(([packageKey, licenseObj]) => {
287
+ return {
288
+ artifactVersion: licenseObj.version,
289
+ content: licenseObj.content ?? '',
290
+ description: licenseObj.description ?? '',
291
+ developers: [{ name: licenseObj.author ?? '', organisationUrl: '' }],
292
+ licenses: [PackageUtils.prepareAboutLibrariesLicenseField(licenseObj)],
293
+ name: licenseObj.name,
294
+ tag: '',
295
+ type: licenseObj.type,
296
+ uniqueId: PackageUtils.normalizePackageName(packageKey),
297
+ website: licenseObj.url,
298
+ };
299
+ })
300
+ .map((jsonPayload) => {
301
+ const libraryJsonPayload: AboutLibrariesLibraryJsonPayload = {
302
+ artifactVersion: jsonPayload.artifactVersion,
303
+ description: jsonPayload.description,
304
+ developers: jsonPayload.developers,
305
+ licenses: jsonPayload.licenses,
306
+ name: jsonPayload.name,
307
+ tag: jsonPayload.tag,
308
+ uniqueId: jsonPayload.uniqueId,
309
+ website: jsonPayload.website,
310
+ };
311
+ const licenseJsonPayload: AboutLibrariesLicenseJsonPayload = {
312
+ content: jsonPayload.content,
313
+ hash: jsonPayload.licenses[0],
314
+ name: jsonPayload.type ?? '',
315
+ url: '',
316
+ };
317
+
318
+ return {
319
+ normalizedPackageNameWithVersion: jsonPayload.uniqueId,
320
+ libraryJsonPayload,
321
+ licenseJsonPayload,
322
+ };
323
+ });
324
+ }
325
+
326
+ /**
327
+ * Generates AboutLibraries-compatible metadata for NPM dependencies
328
+ *
329
+ * This will take scanned NPM licenses and produce following output inside android project's directory:
330
+ *
331
+ * ```
332
+ * | - android
333
+ * | ---- app
334
+ * | ---- config <--- generated AboutLibraries config directory
335
+ * | ------- libraries <--- generated directory with JSON files list of NPM dependencies
336
+ * | ------- licenses <--- generated directory with JSON files list of used licenses
337
+ * | ---- build.gradle
338
+ * | ---- settings.gradle
339
+ * ```
340
+ *
341
+ * @param licenses Scanned NPM licenses
342
+ * @param androidProjectPath Path to the Android project directory
343
+ * @param aboutLibrariesLikeOutput Optional pre-generated output to use instead of generating it using `generateAboutLibrariesNPMOutput`
344
+ * @see {@link generateAboutLibrariesNPMOutput}
345
+ */
346
+ export function writeAboutLibrariesNPMOutput(
347
+ licenses: AggregatedLicensesObj,
348
+ androidProjectPath: string,
349
+ aboutLibrariesLikeOutput?: AboutLibrariesLikePackageInfo[],
350
+ ) {
351
+ const aboutLibrariesConfigDirPath = path.join(androidProjectPath, 'config');
352
+ const aboutLibrariesConfigLibrariesDirPath = path.join(aboutLibrariesConfigDirPath, 'libraries');
353
+ const aboutLibrariesConfigLicensesDirPath = path.join(aboutLibrariesConfigDirPath, 'licenses');
354
+
355
+ if (!fs.existsSync(aboutLibrariesConfigDirPath)) {
356
+ fs.mkdirSync(aboutLibrariesConfigDirPath);
357
+ }
358
+
359
+ if (!fs.existsSync(aboutLibrariesConfigLibrariesDirPath)) {
360
+ fs.mkdirSync(aboutLibrariesConfigLibrariesDirPath);
361
+ }
362
+
363
+ if (!fs.existsSync(aboutLibrariesConfigLicensesDirPath)) {
364
+ fs.mkdirSync(aboutLibrariesConfigLicensesDirPath);
365
+ }
366
+
367
+ if (!aboutLibrariesLikeOutput) {
368
+ aboutLibrariesLikeOutput = generateAboutLibrariesNPMOutput(licenses);
369
+ }
370
+
371
+ aboutLibrariesLikeOutput.forEach(({ normalizedPackageNameWithVersion, libraryJsonPayload, licenseJsonPayload }) => {
372
+ const libraryJsonFilePath = path.join(
373
+ aboutLibrariesConfigLibrariesDirPath,
374
+ `${normalizedPackageNameWithVersion}.json`,
375
+ );
376
+ const licenseJsonFilePath = path.join(aboutLibrariesConfigLicensesDirPath, `${licenseJsonPayload.hash}.json`);
377
+
378
+ fs.writeFileSync(libraryJsonFilePath, JSON.stringify(libraryJsonPayload));
379
+
380
+ if (!fs.existsSync(licenseJsonFilePath)) {
381
+ fs.writeFileSync(licenseJsonFilePath, JSON.stringify(licenseJsonPayload));
382
+ }
383
+ });
384
+ }
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ export * from './common';
2
+ export * as Types from './types';
3
+ export * from './utils';
@@ -0,0 +1,10 @@
1
+ export type AboutLibrariesLibraryJsonPayload = {
2
+ artifactVersion: string;
3
+ description: string;
4
+ developers: { name: string; organisationUrl: string }[];
5
+ licenses: string[];
6
+ name: string;
7
+ tag: string;
8
+ uniqueId: string;
9
+ website: string | undefined;
10
+ };
@@ -0,0 +1,6 @@
1
+ export type AboutLibrariesLicenseJsonPayload = {
2
+ content: string;
3
+ hash: string;
4
+ name: string;
5
+ url: string;
6
+ };
@@ -0,0 +1,8 @@
1
+ import type { AboutLibrariesLibraryJsonPayload } from './AboutLibrariesLibraryJsonPayload';
2
+ import type { AboutLibrariesLicenseJsonPayload } from './AboutLibrariesLicenseJsonPayload';
3
+
4
+ export type AboutLibrariesLikePackageInfo = {
5
+ normalizedPackageNameWithVersion: string;
6
+ libraryJsonPayload: AboutLibrariesLibraryJsonPayload;
7
+ licenseJsonPayload: AboutLibrariesLicenseJsonPayload;
8
+ };
@@ -0,0 +1,7 @@
1
+ import type { LicenseObj } from './LicenseObj';
2
+
3
+ /**
4
+ * Mapping from package keys in format `<package-name>/<package-version>` to license objects.
5
+ * @see {@link LicenseObj}
6
+ */
7
+ export type AggregatedLicensesObj = Record<string, LicenseObj>;
@@ -0,0 +1,3 @@
1
+ type BaseDependencyType = 'dependency' | 'devDependency' | 'optionalDependency' | 'peerDependency';
2
+
3
+ export type DependencyType = BaseDependencyType | `transitive${Capitalize<BaseDependencyType>}`;
@@ -0,0 +1,34 @@
1
+ import type { DependencyType } from './DependencyType';
2
+ import type { ScanPackageCallContext } from './ScanPackageCallContext';
3
+
4
+ export type LicenseObj = {
5
+ /** Package name */
6
+ name: string;
7
+
8
+ /** Package author */
9
+ author?: string;
10
+
11
+ /** Package license contents */
12
+ content?: string;
13
+
14
+ /** Package description */
15
+ description?: string;
16
+
17
+ /** License file path */
18
+ file?: string;
19
+
20
+ /** License type */
21
+ type?: string;
22
+
23
+ /** Package repository URL */
24
+ url?: string;
25
+
26
+ /** The resolved version that was actually installed */
27
+ version: string;
28
+
29
+ /** The source of how this package has been introduced to the dependency tree */
30
+ dependencyType: DependencyType;
31
+
32
+ /** The required version specified in package.json */
33
+ requiredVersion: string;
34
+ } & Pick<ScanPackageCallContext, 'parentPackageName' | 'parentPackageRequiredVersion' | 'parentPackageResolvedVersion'>;
@@ -0,0 +1,7 @@
1
+ export type LicensePlistPayload = {
2
+ name: string;
3
+ source?: string;
4
+ version: string;
5
+ body?: string;
6
+ file?: string;
7
+ };
@@ -0,0 +1,30 @@
1
+ import type { DependencyType } from './DependencyType';
2
+
3
+ export type ScanPackageCallContext = {
4
+ /** Information on what is the source of the dependency on the package to be scanned */
5
+ dependencyType: DependencyType;
6
+
7
+ /**
8
+ * The name of the package that introduced the package to be scanned as a dependency (of any type);
9
+ * undefined if the package to be scanned is a dependency of the root package
10
+ */
11
+ parentPackageName?: string;
12
+
13
+ /**
14
+ * The installation directory of the package that introduced the package to be scanned as a dependency (of any type);
15
+ * undefined if the package to be scanned is a dependency of the root package
16
+ */
17
+ parentPackageRoot?: string;
18
+
19
+ /**
20
+ * The required version, as specified in package.json
21
+ * of the package that introduced the package to be scanned as a dependency (of any type)
22
+ */
23
+ parentPackageRequiredVersion?: string;
24
+
25
+ /**
26
+ * The resolved (actually installed by the package manager) version
27
+ * of the package that introduced the package to be scanned as a dependency (of any type)
28
+ */
29
+ parentPackageResolvedVersion?: string;
30
+ };
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Scan options for controlling which dependencies of a package are scanned
3
+ */
4
+ export type ScanPackageOptions = {
5
+ /** Whether transitive dependencies should be scanned */
6
+ includeTransitiveDependencies: boolean;
7
+
8
+ /** Whether to include devDependencies in the scan; includeTransitiveDependencies option applies */
9
+ includeDevDependencies: boolean;
10
+
11
+ /** Whether to include optionalDependencies in the scan; includeTransitiveDependencies option applies */
12
+ includeOptionalDependencies: boolean;
13
+ };
14
+
15
+ /**
16
+ * Information about a single package to be scanned
17
+ */
18
+ export type ScanPackageOptionsFactoryPackageInfo = {
19
+ /** `true` if the analyzed package.json is the root of the scanned project */
20
+ isRoot: boolean;
21
+
22
+ /** `true` if the analyzed package.json is a dependency related to the project via a `workspace:...` dependency specifier */
23
+ isWorkspacePackage: boolean;
24
+ };
25
+
26
+ /**
27
+ * Factory to create a filter for scan options for dependencies of a given package
28
+ */
29
+ export type ScanPackageOptionsFactory = (packageInfo: ScanPackageOptionsFactoryPackageInfo) => ScanPackageOptions;
@@ -0,0 +1,9 @@
1
+ export * from './LicenseObj';
2
+ export * from './AggregatedLicensesObj';
3
+ export * from './AboutLibrariesLibraryJsonPayload';
4
+ export * from './AboutLibrariesLicenseJsonPayload';
5
+ export * from './LicensePlistPayload';
6
+ export * from './AboutLibrariesLikePackageInfo';
7
+ export * from './ScanPackageOptions';
8
+ export * from './ScanPackageCallContext';
9
+ export * from './DependencyType';
@@ -0,0 +1,4 @@
1
+ export * as PackageUtils from './packageUtils';
2
+ export * as MiscUtils from './miscUtils';
3
+ export * as RepositoryUtils from './repositoryUtils';
4
+ export * as YamlUtils from './yamlUtils';
@@ -0,0 +1,42 @@
1
+ import crypto from 'crypto';
2
+
3
+ export function sha512(text: string) {
4
+ return crypto.createHash('sha512').update(text).digest('hex');
5
+ }
6
+
7
+ export function compareObjects(a: unknown, b: unknown): boolean {
8
+ if (a == null || b == null || typeof a !== 'object' || typeof b !== 'object') {
9
+ return a === b;
10
+ }
11
+
12
+ const entriesA = Object.entries(a);
13
+ const entriesB = Object.entries(b);
14
+
15
+ return (
16
+ entriesA.length === entriesB.length &&
17
+ entriesA
18
+ .map(([keyA, valueA]) => {
19
+ const entry = entriesB.find(([keyB]) => keyA === keyB);
20
+
21
+ if (!entry) {
22
+ return valueA === entry;
23
+ }
24
+
25
+ const [, valueB] = entry;
26
+
27
+ return compareObjects(valueA, valueB);
28
+ })
29
+ .reduce((acc, curr) => acc && curr, true)
30
+ );
31
+ }
32
+
33
+ /**
34
+ * Makes a deep-check between array items and provided object, returns true if array has provided object.
35
+ */
36
+ export function arrayIncludesObject(array?: unknown[], object?: unknown) {
37
+ if (!Array.isArray(array)) {
38
+ return;
39
+ }
40
+
41
+ return array.map((item) => compareObjects(item, object)).reduce((acc, curr) => acc || curr, false);
42
+ }