@callstack/licenses 0.2.2 → 0.3.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 (61) hide show
  1. package/README.md +1 -1
  2. package/build/constants/index.d.ts +1 -0
  3. package/build/constants/index.js +17 -0
  4. package/build/constants/licenses.d.ts +7 -0
  5. package/build/constants/licenses.js +53 -0
  6. package/build/index.d.ts +2 -1
  7. package/build/index.js +5 -4
  8. package/build/licenses/LicenseCategory.d.ts +6 -0
  9. package/build/licenses/LicenseCategory.js +10 -0
  10. package/build/licenses/descriptions.d.ts +16 -0
  11. package/build/licenses/descriptions.js +53 -0
  12. package/build/licenses/index.d.ts +3 -0
  13. package/build/licenses/index.js +19 -0
  14. package/build/licenses/licenseAnalysis.d.ts +15 -0
  15. package/build/licenses/licenseAnalysis.js +65 -0
  16. package/build/{common.d.ts → node/common.d.ts} +6 -6
  17. package/build/{common.js → node/common.js} +108 -60
  18. package/build/node/index.d.ts +3 -0
  19. package/build/node/index.js +19 -0
  20. package/build/node/utils/index.d.ts +2 -0
  21. package/build/node/utils/index.js +38 -0
  22. package/build/{utils → node/utils}/packageUtils.d.ts +2 -2
  23. package/build/{utils → node/utils}/packageUtils.js +1 -1
  24. package/build/types/AggregatedLicensesMapping.d.ts +6 -0
  25. package/build/types/{LicenseObj.d.ts → License.d.ts} +8 -2
  26. package/build/types/LicenseAnalysisResult.d.ts +5 -0
  27. package/build/types/LicenseAnalysisResult.js +2 -0
  28. package/build/types/LicenseStats.d.ts +28 -0
  29. package/build/types/LicenseStats.js +2 -0
  30. package/build/types/ParentPackageInfo.d.ts +14 -0
  31. package/build/types/ParentPackageInfo.js +2 -0
  32. package/build/types/index.d.ts +5 -2
  33. package/build/types/index.js +5 -2
  34. package/build/utils/index.d.ts +0 -2
  35. package/build/utils/index.js +1 -3
  36. package/package.json +20 -7
  37. package/src/constants/index.ts +1 -0
  38. package/src/constants/licenses.ts +58 -0
  39. package/src/index.ts +2 -1
  40. package/src/licenses/LicenseCategory.ts +6 -0
  41. package/src/licenses/descriptions.ts +56 -0
  42. package/src/licenses/index.ts +3 -0
  43. package/src/licenses/licenseAnalysis.ts +78 -0
  44. package/src/{common.ts → node/common.ts} +104 -45
  45. package/src/node/index.ts +3 -0
  46. package/src/node/utils/index.ts +2 -0
  47. package/src/{utils → node/utils}/packageUtils.ts +3 -3
  48. package/src/types/AggregatedLicensesMapping.ts +7 -0
  49. package/src/types/{LicenseObj.ts → License.ts} +9 -2
  50. package/src/types/LicenseAnalysisResult.ts +7 -0
  51. package/src/types/LicenseStats.ts +33 -0
  52. package/src/types/ParentPackageInfo.ts +16 -0
  53. package/src/types/index.ts +5 -2
  54. package/src/utils/index.ts +0 -2
  55. package/build/types/AggregatedLicensesObj.d.ts +0 -6
  56. package/src/types/AggregatedLicensesObj.ts +0 -7
  57. /package/build/{utils → node/utils}/miscUtils.d.ts +0 -0
  58. /package/build/{utils → node/utils}/miscUtils.js +0 -0
  59. /package/build/types/{AggregatedLicensesObj.js → AggregatedLicensesMapping.js} +0 -0
  60. /package/build/types/{LicenseObj.js → License.js} +0 -0
  61. /package/src/{utils → node/utils}/miscUtils.ts +0 -0
@@ -1,5 +1,5 @@
1
- import fs from 'fs';
2
- import path from 'path';
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
3
 
4
4
  import glob from 'glob';
5
5
 
@@ -7,13 +7,16 @@ import type {
7
7
  AboutLibrariesLibraryJsonPayload,
8
8
  AboutLibrariesLicenseJsonPayload,
9
9
  AboutLibrariesLikePackageInfo,
10
- AggregatedLicensesObj,
10
+ AggregatedLicensesMapping,
11
11
  DependencyType,
12
12
  LicensePlistPayload,
13
+ ParentPackageInfo,
13
14
  ScanPackageCallContext,
14
15
  ScanPackageOptionsFactory,
15
- } from './types';
16
- import { PackageUtils, YamlUtils } from './utils';
16
+ } from '../types';
17
+ import { YamlUtils } from '../utils';
18
+
19
+ import { PackageUtils } from './utils';
17
20
 
18
21
  type InternalScanGroupSpecifier = { packages: [depName: string, depVersion: string][]; dependencyType: DependencyType };
19
22
 
@@ -32,7 +35,7 @@ function scanPackage(
32
35
  packageName: string,
33
36
  requiredVersion: string,
34
37
  processedPackages: Set<string>,
35
- result: AggregatedLicensesObj,
38
+ result: AggregatedLicensesMapping,
36
39
  scanOptionsFactory: ScanPackageOptionsFactory = PackageUtils.legacyDefaultScanPackageOptionsFactory,
37
40
  {
38
41
  parentPackageRoot,
@@ -83,21 +86,37 @@ function scanPackage(
83
86
 
84
87
  const resolvedVersionPackageKey = `${packageName}@${localPackageJson.version}`;
85
88
 
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
- };
89
+ let parentPackageInfo: ParentPackageInfo | undefined;
90
+ if (parentPackageName && parentPackageRequiredVersion && parentPackageResolvedVersion) {
91
+ parentPackageInfo = {
92
+ name: parentPackageName,
93
+ requiredVersion: parentPackageRequiredVersion,
94
+ resolvedVersion: parentPackageResolvedVersion,
95
+ };
96
+ }
97
+
98
+ if (parentPackageInfo && resolvedVersionPackageKey in result) {
99
+ result[resolvedVersionPackageKey].parentPackages = [
100
+ ...(result[resolvedVersionPackageKey].parentPackages ?? []),
101
+ parentPackageInfo,
102
+ ];
103
+ } else {
104
+ result[resolvedVersionPackageKey] = {
105
+ name: packageName,
106
+ author: PackageUtils.parseAuthorField(localPackageJson),
107
+ content: licenseFiles?.[0] ? fs.readFileSync(licenseFiles[0], { encoding: 'utf-8' }) : undefined,
108
+ file: licenseFiles?.[0] ? licenseFiles[0] : undefined,
109
+ description: localPackageJson.description,
110
+ type: PackageUtils.parseLicenseField(localPackageJson),
111
+ url: PackageUtils.parseRepositoryFieldToUrl(localPackageJson),
112
+ version: localPackageJson.version,
113
+ requiredVersion,
114
+ parentPackages: parentPackageInfo ? [parentPackageInfo] : [],
115
+ parentPackageRequiredVersion,
116
+ parentPackageResolvedVersion,
117
+ dependencyType,
118
+ };
119
+ }
101
120
  }
102
121
 
103
122
  const dependencies: MaybeDependencyMapping = localPackageJson.dependencies;
@@ -161,12 +180,12 @@ type MaybeDependencyMapping = DependencyMapping | undefined;
161
180
  export function scanDependencies(
162
181
  appPackageJsonPath: string,
163
182
  scanOptionsFactory: ScanPackageOptionsFactory = PackageUtils.legacyDefaultScanPackageOptionsFactory,
164
- ): AggregatedLicensesObj {
183
+ ): AggregatedLicensesMapping {
165
184
  const appPackageJson = require(path.resolve(appPackageJsonPath));
166
185
  const dependencies: MaybeDependencyMapping = appPackageJson.dependencies;
167
186
  const devDependencies: MaybeDependencyMapping = appPackageJson.devDependencies;
168
187
  const optionalDependencies: MaybeDependencyMapping = appPackageJson.optionalDependencies;
169
- const result: AggregatedLicensesObj = {};
188
+ const result: AggregatedLicensesMapping = {};
170
189
  const processedPackages = new Set<string>();
171
190
 
172
191
  const rootScanOptions = scanOptionsFactory({ isRoot: true, isWorkspacePackage: false });
@@ -196,6 +215,9 @@ export function scanDependencies(
196
215
  return result;
197
216
  }
198
217
 
218
+ const PODSPEC_NAME_REGEX =
219
+ /(Pod::Spec\.new\s+do\s+\|(?<specref>.*)\|).*(\k<specref>\.name\s+=\s+(?<quote>["'])(?<specname>[a-zA-Z0-9_\-@]+)\k<quote>)/;
220
+
199
221
  /**
200
222
  * Generates LicensePlist-compatible metadata for NPM dependencies as a YAML string.
201
223
  *
@@ -205,30 +227,67 @@ export function scanDependencies(
205
227
  * @param iosProjectPath Path to the iOS project directory
206
228
  * @see {@link writeLicensePlistNPMOutput}
207
229
  */
208
- export function generateLicensePlistNPMOutput(licenses: AggregatedLicensesObj, iosProjectPath: string): string {
230
+ export function generateLicensePlistNPMOutput(licenses: AggregatedLicensesMapping, iosProjectPath: string): string {
209
231
  const renames: Record<string, string> = {};
210
- const licenseEntries = Object.entries(licenses).map(([packageKey, licenseObj]) => {
232
+ const licenseEntries = Object.entries(licenses).map(([packageKey, license]) => {
211
233
  const normalizedPackageNameWithVersion = PackageUtils.normalizePackageName(packageKey);
212
234
 
213
- if (licenseObj.name !== normalizedPackageNameWithVersion) {
214
- renames[normalizedPackageNameWithVersion] = licenseObj.name;
235
+ if (license.name !== normalizedPackageNameWithVersion) {
236
+ renames[normalizedPackageNameWithVersion] = license.name;
215
237
  }
216
238
 
217
- const relativeLicenseFile = licenseObj.file ? path.relative(iosProjectPath, licenseObj.file) : undefined;
239
+ const relativeLicenseFile = license.file ? path.relative(iosProjectPath, license.file) : undefined;
218
240
 
219
241
  return {
220
242
  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' }),
243
+ version: license.version,
244
+ ...(license.url && { source: license.url }),
245
+ ...(license.file ? { file: relativeLicenseFile } : { body: license.content ?? license.type ?? 'UNKNOWN' }),
226
246
  } as LicensePlistPayload;
227
247
  });
228
248
 
249
+ const excludedEntries = Object.values(licenses).reduce<Array<{ name: string }>>((acc, license) => {
250
+ const packageName = license.name;
251
+ const localPackageJsonPath = PackageUtils.getPackageJsonPath(packageName);
252
+
253
+ if (!localPackageJsonPath) {
254
+ return acc;
255
+ }
256
+
257
+ const podspecFiles = glob.sync('**.podspec', {
258
+ cwd: path.dirname(localPackageJsonPath),
259
+ absolute: true,
260
+ nocase: true,
261
+ nodir: true,
262
+ ignore: '**/{__tests__,__fixtures__,__mocks__}/**',
263
+ });
264
+
265
+ if (!podspecFiles) {
266
+ return acc;
267
+ }
268
+
269
+ const podspecEntries = podspecFiles.map((podspecFile) => {
270
+ const podspecContent = fs.readFileSync(podspecFile, { encoding: 'utf-8' });
271
+ const podspecContentShrinked = podspecContent.replace(/\n/g, ' ');
272
+ const { specname } = PODSPEC_NAME_REGEX.exec(podspecContentShrinked)?.groups ?? {};
273
+
274
+ return { name: specname ?? packageName };
275
+ });
276
+
277
+ return [...acc, ...podspecEntries];
278
+ }, []);
279
+
229
280
  const yamlDoc = {
230
281
  ...(Object.keys(renames).length > 0 && { rename: renames }),
231
282
  manual: licenseEntries,
283
+ /**
284
+ * Adding excludes to prevent duplicates - NPM packages with iOS Podspecs are already
285
+ * manually included in the `licenseEntries`. However License Plist is automatically including all
286
+ * the entries detected via Pods-<project-name>-acknowledgements.plist, which also
287
+ * includes the NPM package with iOS Podspecs - let's filter them out,
288
+ * so that only the manual NPM entries are displayed
289
+ */
290
+ exclude: excludedEntries,
232
291
  };
233
292
 
234
293
  const yamlContent = [
@@ -261,7 +320,7 @@ export function generateLicensePlistNPMOutput(licenses: AggregatedLicensesObj, i
261
320
  * @see {@link generateLicensePlistNPMOutput}
262
321
  */
263
322
  export function writeLicensePlistNPMOutput(
264
- licenses: AggregatedLicensesObj,
323
+ licenses: AggregatedLicensesMapping,
265
324
  iosProjectPath: string,
266
325
  plistLikeOutput?: string,
267
326
  ) {
@@ -281,20 +340,20 @@ export function writeLicensePlistNPMOutput(
281
340
  * @returns Array of AboutLibrariesLikePackage objects, each representing a NPM dependency
282
341
  * @see {@link writeAboutLibrariesNPMOutput}
283
342
  */
284
- export function generateAboutLibrariesNPMOutput(licenses: AggregatedLicensesObj): AboutLibrariesLikePackageInfo[] {
343
+ export function generateAboutLibrariesNPMOutput(licenses: AggregatedLicensesMapping): AboutLibrariesLikePackageInfo[] {
285
344
  return Object.entries(licenses)
286
- .map(([packageKey, licenseObj]) => {
345
+ .map(([packageKey, license]) => {
287
346
  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,
347
+ artifactVersion: license.version,
348
+ content: license.content ?? '',
349
+ description: license.description ?? '',
350
+ developers: [{ name: license.author ?? '', organisationUrl: '' }],
351
+ licenses: [PackageUtils.prepareAboutLibrariesLicenseField(license)],
352
+ name: license.name,
294
353
  tag: '',
295
- type: licenseObj.type,
354
+ type: license.type,
296
355
  uniqueId: PackageUtils.normalizePackageName(packageKey),
297
- website: licenseObj.url,
356
+ website: license.url,
298
357
  };
299
358
  })
300
359
  .map((jsonPayload) => {
@@ -344,7 +403,7 @@ export function generateAboutLibrariesNPMOutput(licenses: AggregatedLicensesObj)
344
403
  * @see {@link generateAboutLibrariesNPMOutput}
345
404
  */
346
405
  export function writeAboutLibrariesNPMOutput(
347
- licenses: AggregatedLicensesObj,
406
+ licenses: AggregatedLicensesMapping,
348
407
  androidProjectPath: string,
349
408
  aboutLibrariesLikeOutput?: AboutLibrariesLikePackageInfo[],
350
409
  ) {
@@ -0,0 +1,3 @@
1
+ export * from './common';
2
+ export * from './utils';
3
+ export * from '../';
@@ -0,0 +1,2 @@
1
+ export * as PackageUtils from './packageUtils';
2
+ export * as MiscUtils from './miscUtils';
@@ -1,10 +1,10 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
3
 
4
- import type { LicenseObj, ScanPackageOptionsFactory } from '../types';
4
+ import type { License, ScanPackageOptionsFactory } from '../../types';
5
+ import { normalizeRepositoryUrl } from '../../utils/repositoryUtils';
5
6
 
6
7
  import { sha512 } from './miscUtils';
7
- import { normalizeRepositoryUrl } from './repositoryUtils';
8
8
 
9
9
  export function getPackageJsonPath(dependency: string, root?: string) {
10
10
  const rootsToSearch = [
@@ -54,7 +54,7 @@ export function normalizePackageName(packageName: string): string {
54
54
  return packageName.replace('/', '_');
55
55
  }
56
56
 
57
- export function prepareAboutLibrariesLicenseField(license: LicenseObj) {
57
+ export function prepareAboutLibrariesLicenseField(license: License) {
58
58
  if (!license.type) {
59
59
  return '';
60
60
  }
@@ -0,0 +1,7 @@
1
+ import type { License } from './License';
2
+
3
+ /**
4
+ * Mapping from package keys in format `<package-name>@<package-version>` to license objects.
5
+ * @see {@link License}
6
+ */
7
+ export type AggregatedLicensesMapping = Record<string, License>;
@@ -1,7 +1,8 @@
1
1
  import type { DependencyType } from './DependencyType';
2
+ import type { ParentPackageInfo } from './ParentPackageInfo';
2
3
  import type { ScanPackageCallContext } from './ScanPackageCallContext';
3
4
 
4
- export type LicenseObj = {
5
+ export type License = {
5
6
  /** Package name */
6
7
  name: string;
7
8
 
@@ -31,4 +32,10 @@ export type LicenseObj = {
31
32
 
32
33
  /** The required version specified in package.json */
33
34
  requiredVersion: string;
34
- } & Pick<ScanPackageCallContext, 'parentPackageName' | 'parentPackageRequiredVersion' | 'parentPackageResolvedVersion'>;
35
+
36
+ /**
37
+ * The name(s) of the package(s) that introduced the package as a dependency (of any type);
38
+ * empty array if the package is a dependency of the root package
39
+ */
40
+ parentPackages: ParentPackageInfo[];
41
+ } & Pick<ScanPackageCallContext, 'parentPackageRequiredVersion' | 'parentPackageResolvedVersion'>;
@@ -0,0 +1,7 @@
1
+ import type { LicenseCategory } from '../licenses/LicenseCategory';
2
+
3
+ import type { LicenseStats } from './LicenseStats';
4
+
5
+ export interface LicenseAnalysisResult extends LicenseStats {
6
+ categorizedLicenses: Record<string, LicenseCategory>;
7
+ }
@@ -0,0 +1,33 @@
1
+ import type { LicenseCategory } from '../licenses/LicenseCategory';
2
+
3
+ export interface LicenseStats {
4
+ /**
5
+ * Total number of packages
6
+ */
7
+ total: number;
8
+
9
+ /**
10
+ * Mapping of license category to count of packages in that category
11
+ */
12
+ byCategory: Record<LicenseCategory, number>;
13
+
14
+ /**
15
+ * Mapping of license name to count of packages with that license
16
+ */
17
+ byLicense: Record<string, number>;
18
+
19
+ /**
20
+ * Information about the presence of different license categories
21
+ */
22
+ categoriesPresence: {
23
+ hasAnyUnknown: boolean;
24
+ hasAnyWeakCopyleft: boolean;
25
+ hasAnyStrongCopyleft: boolean;
26
+ hasAllPermissive: boolean;
27
+ };
28
+
29
+ /**
30
+ * Description of the graph state based on the license categories
31
+ */
32
+ description: string;
33
+ }
@@ -0,0 +1,16 @@
1
+ export type ParentPackageInfo = {
2
+ /**
3
+ * The name of the package
4
+ */
5
+ name: string;
6
+
7
+ /**
8
+ * The required version, as specified in package.json
9
+ */
10
+ requiredVersion: string;
11
+
12
+ /**
13
+ * The resolved version, as specified in package.json
14
+ */
15
+ resolvedVersion: string;
16
+ };
@@ -1,5 +1,5 @@
1
- export * from './LicenseObj';
2
- export * from './AggregatedLicensesObj';
1
+ export * from './License';
2
+ export * from './AggregatedLicensesMapping';
3
3
  export * from './AboutLibrariesLibraryJsonPayload';
4
4
  export * from './AboutLibrariesLicenseJsonPayload';
5
5
  export * from './LicensePlistPayload';
@@ -7,3 +7,6 @@ export * from './AboutLibrariesLikePackageInfo';
7
7
  export * from './ScanPackageOptions';
8
8
  export * from './ScanPackageCallContext';
9
9
  export * from './DependencyType';
10
+ export * from './ParentPackageInfo';
11
+ export * from './LicenseAnalysisResult';
12
+ export * from './LicenseStats';
@@ -1,4 +1,2 @@
1
- export * as PackageUtils from './packageUtils';
2
- export * as MiscUtils from './miscUtils';
3
1
  export * as RepositoryUtils from './repositoryUtils';
4
2
  export * as YamlUtils from './yamlUtils';
@@ -1,6 +0,0 @@
1
- import type { LicenseObj } from './LicenseObj';
2
- /**
3
- * Mapping from package keys in format `<package-name>/<package-version>` to license objects.
4
- * @see {@link LicenseObj}
5
- */
6
- export type AggregatedLicensesObj = Record<string, LicenseObj>;
@@ -1,7 +0,0 @@
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>;
File without changes
File without changes
File without changes
File without changes