vouchington-tooling 0.19.1 → 0.19.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.
package/README.md
CHANGED
|
@@ -328,7 +328,10 @@ or derive a configuration or documentation package spec from that manifest inste
|
|
|
328
328
|
|
|
329
329
|
`dependency-license-policy` keeps legal policy in the consumer. `collectPnpmLicenseReport` creates
|
|
330
330
|
an isolated, script-free temporary workspace and store, expands pnpm's supported architectures to
|
|
331
|
-
every `os`, `cpu`, and `libc` selector represented in the lockfile,
|
|
331
|
+
every `os`, `cpu`, and `libc` selector represented in the lockfile, drops every `engines` constraint
|
|
332
|
+
from the audit copy of the lockfile, and validates the JSON report. Dropping `engines` keeps
|
|
333
|
+
`pnpm fetch` from skipping optional packages that exclude the running Node.js; pnpm 12's `fetch`
|
|
334
|
+
ignores `force`, so their licenses would otherwise report as Unknown.
|
|
332
335
|
Pass explicit denied SPDX IDs and prefixes, exact aliases, and justified allowlist scopes to
|
|
333
336
|
`evaluatePnpmLicenseReport`. Unknown, malformed, and custom SPDX references fail closed. Allowlist
|
|
334
337
|
scopes are either intentionally global or an exact package-name set; the library returns structured
|
|
@@ -26,7 +26,10 @@ export function collectPnpmLicenseReport(repoRoot, options = {}) {
|
|
|
26
26
|
const auditWorkspace = prepareWorkspace(repoRoot, readFile(lockfilePath, 'utf8'), readFile(workspacePath, 'utf8'));
|
|
27
27
|
try {
|
|
28
28
|
const storeConfig = `--config.store-dir=${join(auditWorkspace.cwd, '.pnpm-store')}`;
|
|
29
|
-
const fetchResult = execute('pnpm', [storeConfig, '
|
|
29
|
+
const fetchResult = execute('pnpm', [storeConfig, 'fetch', '--ignore-scripts'], {
|
|
30
|
+
cwd: auditWorkspace.cwd,
|
|
31
|
+
encoding: 'utf8',
|
|
32
|
+
});
|
|
30
33
|
assertCommandSucceeded('pnpm fetch', fetchResult);
|
|
31
34
|
const result = execute('pnpm', [storeConfig, 'licenses', 'list', '--json'], {
|
|
32
35
|
cwd: auditWorkspace.cwd,
|
|
@@ -2,8 +2,21 @@ export interface PnpmLicenseAuditWorkspace {
|
|
|
2
2
|
readonly cleanup: () => void;
|
|
3
3
|
readonly cwd: string;
|
|
4
4
|
}
|
|
5
|
-
|
|
5
|
+
/** The rendered `pnpm-lock.yaml` and `pnpm-workspace.yaml` of a license audit workspace. */
|
|
6
|
+
export interface PnpmLicenseAuditFiles {
|
|
7
|
+
readonly lockfile: string;
|
|
8
|
+
readonly workspace: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Renders the audit copies of `pnpm-lock.yaml` and `pnpm-workspace.yaml` so `pnpm fetch` downloads
|
|
12
|
+
* every package in the lockfile, including optional ones that don't match the host.
|
|
13
|
+
*
|
|
14
|
+
* The workspace supports every `os`, `cpu`, and `libc` in the lockfile. The lockfile drops every
|
|
15
|
+
* `engines` constraint because pnpm 12's `fetch` ignores `force` and skips an optional package whose
|
|
16
|
+
* `engines` exclude the running Node.js, which would leave that package's license Unknown.
|
|
17
|
+
*/
|
|
18
|
+
export declare function renderPnpmLicenseAuditFiles(lockfileSource: string, workspaceSource: string, paths: {
|
|
6
19
|
lockfile: string;
|
|
7
20
|
workspace: string;
|
|
8
|
-
}):
|
|
21
|
+
}): PnpmLicenseAuditFiles;
|
|
9
22
|
export declare function preparePnpmLicenseAuditWorkspace(repoRoot: string, lockfileSource: string, workspaceSource: string): PnpmLicenseAuditWorkspace;
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import { copyFileSync, existsSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import { tmpdir } from 'node:os';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
|
-
import {
|
|
4
|
+
import { isMap, parseDocument, stringify as stringifyYaml } from 'yaml';
|
|
5
5
|
const PLATFORM_KEYS = ['os', 'cpu', 'libc'];
|
|
6
|
-
function
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}
|
|
11
|
-
catch (error) {
|
|
6
|
+
function parseYamlDocument(source, path) {
|
|
7
|
+
const document = parseDocument(source);
|
|
8
|
+
const [error] = document.errors;
|
|
9
|
+
if (error)
|
|
12
10
|
throw new Error(`failed to parse ${path}: ${String(error)}`, { cause: error });
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
return document;
|
|
12
|
+
}
|
|
13
|
+
function assertYamlObject(value, path) {
|
|
14
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
15
15
|
throw new Error(`expected ${path} to contain a YAML object`);
|
|
16
16
|
}
|
|
17
|
-
return
|
|
17
|
+
return value;
|
|
18
18
|
}
|
|
19
19
|
function getStringList(value, path) {
|
|
20
20
|
if (typeof value === 'string')
|
|
@@ -24,13 +24,7 @@ function getStringList(value, path) {
|
|
|
24
24
|
}
|
|
25
25
|
return value;
|
|
26
26
|
}
|
|
27
|
-
|
|
28
|
-
const lockfile = parseYamlObject(lockfileSource, paths.lockfile);
|
|
29
|
-
const workspace = parseYamlObject(workspaceSource, paths.workspace);
|
|
30
|
-
const packages = lockfile.packages;
|
|
31
|
-
if (typeof packages !== 'object' || packages === null || Array.isArray(packages)) {
|
|
32
|
-
throw new Error(`expected ${paths.lockfile} to contain a packages object`);
|
|
33
|
-
}
|
|
27
|
+
function collectSupportedArchitectures(packages, lockfilePath) {
|
|
34
28
|
const supportedArchitectures = {
|
|
35
29
|
cpu: ['current'],
|
|
36
30
|
libc: ['current'],
|
|
@@ -44,27 +38,52 @@ export function renderPnpmLicenseAuditWorkspace(lockfileSource, workspaceSource,
|
|
|
44
38
|
const value = snapshot[key];
|
|
45
39
|
if (value === undefined)
|
|
46
40
|
continue;
|
|
47
|
-
for (const platform of getStringList(value, `${
|
|
41
|
+
for (const platform of getStringList(value, `${lockfilePath} packages.*.${key}`)) {
|
|
48
42
|
values.add(platform);
|
|
49
43
|
}
|
|
50
44
|
}
|
|
51
45
|
supportedArchitectures[key].push(...[...values].filter((value) => value !== 'current').sort());
|
|
52
46
|
}
|
|
53
|
-
return
|
|
47
|
+
return supportedArchitectures;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Renders the audit copies of `pnpm-lock.yaml` and `pnpm-workspace.yaml` so `pnpm fetch` downloads
|
|
51
|
+
* every package in the lockfile, including optional ones that don't match the host.
|
|
52
|
+
*
|
|
53
|
+
* The workspace supports every `os`, `cpu`, and `libc` in the lockfile. The lockfile drops every
|
|
54
|
+
* `engines` constraint because pnpm 12's `fetch` ignores `force` and skips an optional package whose
|
|
55
|
+
* `engines` exclude the running Node.js, which would leave that package's license Unknown.
|
|
56
|
+
*/
|
|
57
|
+
export function renderPnpmLicenseAuditFiles(lockfileSource, workspaceSource, paths) {
|
|
58
|
+
const lockfileDocument = parseYamlDocument(lockfileSource, paths.lockfile);
|
|
59
|
+
const lockfile = assertYamlObject(lockfileDocument.toJS(), paths.lockfile);
|
|
60
|
+
const workspace = assertYamlObject(parseYamlDocument(workspaceSource, paths.workspace).toJS(), paths.workspace);
|
|
61
|
+
const packageNodes = lockfileDocument.get('packages');
|
|
62
|
+
if (!isMap(packageNodes)) {
|
|
63
|
+
throw new Error(`expected ${paths.lockfile} to contain a packages object`);
|
|
64
|
+
}
|
|
65
|
+
const supportedArchitectures = collectSupportedArchitectures(lockfile.packages, paths.lockfile);
|
|
66
|
+
for (const { value } of packageNodes.items)
|
|
67
|
+
if (isMap(value))
|
|
68
|
+
value.delete('engines');
|
|
69
|
+
return {
|
|
70
|
+
lockfile: lockfileDocument.toString({ lineWidth: 0 }),
|
|
71
|
+
workspace: stringifyYaml({ ...workspace, packages: [], supportedArchitectures }),
|
|
72
|
+
};
|
|
54
73
|
}
|
|
55
74
|
export function preparePnpmLicenseAuditWorkspace(repoRoot, lockfileSource, workspaceSource) {
|
|
56
75
|
const auditRoot = mkdtempSync(join(tmpdir(), 'dependency-license-audit-'));
|
|
57
76
|
try {
|
|
58
|
-
|
|
59
|
-
copyFileSync(join(repoRoot, filename), join(auditRoot, filename));
|
|
60
|
-
}
|
|
77
|
+
copyFileSync(join(repoRoot, 'package.json'), join(auditRoot, 'package.json'));
|
|
61
78
|
const npmrc = join(repoRoot, '.npmrc');
|
|
62
79
|
if (existsSync(npmrc))
|
|
63
80
|
copyFileSync(npmrc, join(auditRoot, '.npmrc'));
|
|
64
|
-
|
|
81
|
+
const files = renderPnpmLicenseAuditFiles(lockfileSource, workspaceSource, {
|
|
65
82
|
lockfile: join(repoRoot, 'pnpm-lock.yaml'),
|
|
66
83
|
workspace: join(repoRoot, 'pnpm-workspace.yaml'),
|
|
67
|
-
})
|
|
84
|
+
});
|
|
85
|
+
writeFileSync(join(auditRoot, 'pnpm-lock.yaml'), files.lockfile, 'utf8');
|
|
86
|
+
writeFileSync(join(auditRoot, 'pnpm-workspace.yaml'), files.workspace, 'utf8');
|
|
68
87
|
}
|
|
69
88
|
catch (error) {
|
|
70
89
|
rmSync(auditRoot, { force: true, recursive: true });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vouchington-tooling",
|
|
3
|
-
"version": "0.19.
|
|
3
|
+
"version": "0.19.2",
|
|
4
4
|
"description": "Vouchington CLI and extractable tooling libraries.",
|
|
5
5
|
"homepage": "https://github.com/vouchington/vouchington-tooling/tree/main/packages/vouchington-tooling#readme",
|
|
6
6
|
"bugs": {
|