@callstack/licenses 0.3.0 → 0.3.2

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.
@@ -166,6 +166,7 @@ function scanDependencies(appPackageJsonPath, scanOptionsFactory = utils_2.Packa
166
166
  }
167
167
  return result;
168
168
  }
169
+ const PODSPEC_NAME_REGEX = /(Pod::Spec\.new\s+do\s+\|(?<specref>.*)\|).*(\k<specref>\.name\s+=\s+(?<quote>["'])(?<specname>[a-zA-Z0-9_\-@]+)\k<quote>)/;
169
170
  /**
170
171
  * Generates LicensePlist-compatible metadata for NPM dependencies as a YAML string.
171
172
  *
@@ -190,9 +191,41 @@ function generateLicensePlistNPMOutput(licenses, iosProjectPath) {
190
191
  ...(license.file ? { file: relativeLicenseFile } : { body: license.content ?? license.type ?? 'UNKNOWN' }),
191
192
  };
192
193
  });
194
+ const excludedEntries = Object.values(licenses).reduce((acc, license) => {
195
+ const packageName = license.name;
196
+ const localPackageJsonPath = utils_2.PackageUtils.getPackageJsonPath(packageName);
197
+ if (!localPackageJsonPath) {
198
+ return acc;
199
+ }
200
+ const podspecFiles = glob_1.default.sync('**.podspec', {
201
+ cwd: node_path_1.default.dirname(localPackageJsonPath),
202
+ absolute: true,
203
+ nocase: true,
204
+ nodir: true,
205
+ ignore: '**/{__tests__,__fixtures__,__mocks__}/**',
206
+ });
207
+ if (!podspecFiles) {
208
+ return acc;
209
+ }
210
+ const podspecEntries = podspecFiles.map((podspecFile) => {
211
+ const podspecContent = node_fs_1.default.readFileSync(podspecFile, { encoding: 'utf-8' });
212
+ const podspecContentShrinked = podspecContent.replace(/\n/g, ' ');
213
+ const { specname } = PODSPEC_NAME_REGEX.exec(podspecContentShrinked)?.groups ?? {};
214
+ return { name: specname ?? packageName };
215
+ });
216
+ return [...acc, ...podspecEntries];
217
+ }, []);
193
218
  const yamlDoc = {
194
219
  ...(Object.keys(renames).length > 0 && { rename: renames }),
195
220
  manual: licenseEntries,
221
+ /**
222
+ * Adding excludes to prevent duplicates - NPM packages with iOS Podspecs are already
223
+ * manually included in the `licenseEntries`. However License Plist is automatically including all
224
+ * the entries detected via Pods-<project-name>-acknowledgements.plist, which also
225
+ * includes the NPM package with iOS Podspecs - let's filter them out,
226
+ * so that only the manual NPM entries are displayed
227
+ */
228
+ exclude: excludedEntries,
196
229
  };
197
230
  const yamlContent = [
198
231
  '# BEGIN Generated NPM license entries',
@@ -62,7 +62,13 @@ function prepareAboutLibrariesLicenseField(license) {
62
62
  if (!license.type) {
63
63
  return '';
64
64
  }
65
- return `${license.type}_${(0, miscUtils_1.sha512)(license.content ?? license.type)}`;
65
+ // The returned value is used as a filename under `android/config/licenses/`.
66
+ // Legacy/compound SPDX expressions like `MIT/X11` or `(MIT OR Apache-2.0)` would
67
+ // otherwise produce paths containing `/`, `(` or spaces, which causes the writer
68
+ // to either fail with ENOENT (when a `/` is interpreted as a subdirectory) or
69
+ // to produce names that are invalid on some filesystems.
70
+ const sanitizedType = license.type.replace(/[^A-Za-z0-9._-]/g, '_');
71
+ return `${sanitizedType}_${(0, miscUtils_1.sha512)(license.content ?? sanitizedType)}`;
66
72
  }
67
73
  function parseAuthorField(json) {
68
74
  if (typeof json.author === 'object' && typeof json.author.name === 'string') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@callstack/licenses",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "API package that exposes functionalities for programmatic scanning of licenses in Node.js projects.",
5
5
  "browser": "./build/index.js",
6
6
  "main": "./build/node/index.js",
@@ -215,6 +215,9 @@ export function scanDependencies(
215
215
  return result;
216
216
  }
217
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
+
218
221
  /**
219
222
  * Generates LicensePlist-compatible metadata for NPM dependencies as a YAML string.
220
223
  *
@@ -243,9 +246,48 @@ export function generateLicensePlistNPMOutput(licenses: AggregatedLicensesMappin
243
246
  } as LicensePlistPayload;
244
247
  });
245
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
+
246
280
  const yamlDoc = {
247
281
  ...(Object.keys(renames).length > 0 && { rename: renames }),
248
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,
249
291
  };
250
292
 
251
293
  const yamlContent = [
@@ -59,7 +59,14 @@ export function prepareAboutLibrariesLicenseField(license: License) {
59
59
  return '';
60
60
  }
61
61
 
62
- return `${license.type}_${sha512(license.content ?? license.type)}`;
62
+ // The returned value is used as a filename under `android/config/licenses/`.
63
+ // Legacy/compound SPDX expressions like `MIT/X11` or `(MIT OR Apache-2.0)` would
64
+ // otherwise produce paths containing `/`, `(` or spaces, which causes the writer
65
+ // to either fail with ENOENT (when a `/` is interpreted as a subdirectory) or
66
+ // to produce names that are invalid on some filesystems.
67
+ const sanitizedType = license.type.replace(/[^A-Za-z0-9._-]/g, '_');
68
+
69
+ return `${sanitizedType}_${sha512(license.content ?? sanitizedType)}`;
63
70
  }
64
71
 
65
72
  export function parseAuthorField(json: { author: string | { name: string } }) {