@companion-module/tools 3.1.0 → 3.2.0

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.
@@ -0,0 +1,376 @@
1
+ import { createRequire as _createRequire } from "module";
2
+ const __require = _createRequire(import.meta.url);
3
+ const parse = __require("spdx-expression-parse");
4
+ import { correctedPackageLicense } from './inventory.js';
5
+ export class LicensePolicyError extends Error {
6
+ issues;
7
+ constructor(issues) {
8
+ super(`License validation failed with ${issues.length} error${issues.length === 1 ? '' : 's'}.`);
9
+ this.issues = issues;
10
+ this.name = 'LicensePolicyError';
11
+ }
12
+ }
13
+ /** Permissive licenses which may be shipped under any of the supported project licenses */
14
+ const PERMISSIVE_DEPENDENCY_LICENSES = [
15
+ 'MIT',
16
+ 'MIT-0',
17
+ 'ISC',
18
+ 'BSD-2-Clause',
19
+ 'BSD-3-Clause',
20
+ '0BSD',
21
+ 'CC0-1.0',
22
+ 'Unlicense',
23
+ 'BlueOak-1.0.0',
24
+ 'Python-2.0',
25
+ 'Zlib',
26
+ 'BSL-1.0',
27
+ // Copyleft, but only over the font itself: section 2 permits bundling and redistributing the font with any
28
+ // software, and the reciprocity in sections 3 to 5 only ever reaches a modified version of the font
29
+ 'OFL-1.1',
30
+ // Not an SPDX identifier, SPDX has none for a public domain dedication. Recording it as CC0-1.0 or Unlicense
31
+ // would name a specific legal instrument the package never used, and public domain carries no obligation at all.
32
+ 'Public-Domain',
33
+ ];
34
+ const GPL2_DEPENDENCY_LICENSES = [
35
+ 'GPL-2.0-only',
36
+ 'GPL-2.0-or-later',
37
+ 'GPL-2.0', // Deprecated SPDX id for GPL-2.0-only, still widely declared on npm
38
+ ];
39
+ const GPL3_DEPENDENCY_LICENSES = [
40
+ 'GPL-3.0-only',
41
+ 'GPL-3.0-or-later',
42
+ 'GPL-3.0', // Deprecated SPDX id for GPL-3.0-only, still widely declared on npm
43
+ ];
44
+ // The LGPL families split by which GPL version their relicensing clause can reach. LGPL-2.x section 3 offers "version
45
+ // 2 of the ordinary GPL or any later version", so it suits either GPL policy, while LGPL-3.0 only reaches GPL-3.0.
46
+ const LGPL2_DEPENDENCY_LICENSES = [
47
+ 'LGPL-2.0-only',
48
+ 'LGPL-2.0-or-later',
49
+ 'LGPL-2.0', // Deprecated SPDX id for LGPL-2.0-only, still widely declared on npm
50
+ 'LGPL-2.1-only',
51
+ 'LGPL-2.1-or-later',
52
+ 'LGPL-2.1', // Deprecated SPDX id for LGPL-2.1-only
53
+ ];
54
+ const LGPL3_DEPENDENCY_LICENSES = [
55
+ 'LGPL-3.0-only',
56
+ 'LGPL-3.0-or-later',
57
+ 'LGPL-3.0', // Deprecated SPDX id for LGPL-3.0-only
58
+ ];
59
+ // Each policy pins the combined work to a single GPL version, so no mix of allowed dependencies can end up
60
+ // unlicensable. Offering GPL-2.0-or-later would break that, a GPL-2.0-only and a GPL-3.0-only dependency could then
61
+ // both be accepted while no version satisfies both.
62
+ const PROJECT_LICENSE_POLICIES = {
63
+ MIT: {
64
+ allowedDependencyLicenses: new Set([
65
+ ...PERMISSIVE_DEPENDENCY_LICENSES,
66
+ 'Apache-2.0',
67
+ 'CC-BY-3.0',
68
+ 'CC-BY-4.0',
69
+ 'MPL-2.0', // File level copyleft, allows bundling if source is available (links bundled LICENSE)
70
+ ]),
71
+ // An MIT bundle cannot offer the relinking static linking would require, but the LGPL is written to let a
72
+ // work under any terms use the library as a separate one, so every LGPL version is fine as an external
73
+ externalOnlyLicenses: new Set([...LGPL2_DEPENDENCY_LICENSES, ...LGPL3_DEPENDENCY_LICENSES]),
74
+ },
75
+ // Apache-2.0 and the CC-BY licenses are deliberately absent, they cannot be combined with GPL-2.0-only
76
+ 'GPL-2.0-only': {
77
+ allowedDependencyLicenses: new Set([
78
+ ...PERMISSIVE_DEPENDENCY_LICENSES,
79
+ ...GPL2_DEPENDENCY_LICENSES,
80
+ // Section 3 relicenses these to GPL-2.0, so the bundle stays distributable under this policy
81
+ ...LGPL2_DEPENDENCY_LICENSES,
82
+ 'MPL-2.0',
83
+ ]),
84
+ // LGPL-3.0 is absent entirely rather than external only. It is GPL-3.0 plus permissions, so it carries terms
85
+ // GPL-2.0 section 6 forbids adding, and linking still forms a combined work whichever way it is shipped.
86
+ externalOnlyLicenses: new Set(),
87
+ },
88
+ 'GPL-3.0-only': {
89
+ allowedDependencyLicenses: new Set([
90
+ ...PERMISSIVE_DEPENDENCY_LICENSES,
91
+ ...GPL3_DEPENDENCY_LICENSES,
92
+ 'GPL-2.0-or-later', // GPL-2.0-only is absent, only the "or later" form can be taken to GPL-3.0
93
+ // Both families reach GPL-3.0, the 2.x one through its "or any later version" offer
94
+ ...LGPL2_DEPENDENCY_LICENSES,
95
+ ...LGPL3_DEPENDENCY_LICENSES,
96
+ 'Apache-2.0',
97
+ 'MPL-2.0',
98
+ ]),
99
+ externalOnlyLicenses: new Set(),
100
+ },
101
+ };
102
+ export const SUPPORTED_PROJECT_LICENSES = Object.keys(PROJECT_LICENSE_POLICIES);
103
+ /** Policy applied to dependencies while the project itself has no usable license declaration */
104
+ const FALLBACK_PROJECT_LICENSE = 'MIT';
105
+ /** Suggested to authors, as the one which can use the most of npm and be reused most freely */
106
+ const RECOMMENDED_PROJECT_LICENSE = 'MIT';
107
+ function isProjectLicense(declaration) {
108
+ return SUPPORTED_PROJECT_LICENSES.includes(declaration);
109
+ }
110
+ function orList(items) {
111
+ const remaining = [...items];
112
+ const last = remaining.pop();
113
+ return remaining.length ? `${remaining.join(', ')} or ${last}` : `${last}`;
114
+ }
115
+ function supportedProjectLicenseAdvice() {
116
+ const alternatives = SUPPORTED_PROJECT_LICENSES.filter((license) => license !== RECOMMENDED_PROJECT_LICENSE);
117
+ return `We recommend ${RECOMMENDED_PROJECT_LICENSE} for the widest compatibility, but also accept ${orList(alternatives)} when necessary.`;
118
+ }
119
+ function evaluate(node, allowedLicenses) {
120
+ if ('license' in node) {
121
+ const license = node.plus ? `${node.license}-or-later` : node.license; // "GPL-2.0+" is deprecated for "GPL-2.0-or-later"
122
+ return {
123
+ allowed: !node.exception && allowedLicenses.has(license),
124
+ incompatibleAnd: false,
125
+ };
126
+ }
127
+ const left = evaluate(node.left, allowedLicenses);
128
+ const right = evaluate(node.right, allowedLicenses);
129
+ if (node.conjunction === 'or') {
130
+ const allowed = left.allowed || right.allowed;
131
+ return {
132
+ allowed,
133
+ incompatibleAnd: !allowed && (left.incompatibleAnd || right.incompatibleAnd),
134
+ };
135
+ }
136
+ return {
137
+ allowed: left.allowed && right.allowed,
138
+ incompatibleAnd: left.allowed !== right.allowed || left.incompatibleAnd || right.incompatibleAnd,
139
+ };
140
+ }
141
+ /**
142
+ * spdx-expression-parse rejects the whole expression when any leaf is not a valid SPDX identifier, so a legacy
143
+ * declaration like "(MIT OR GPL)" never reaches the OR logic at all. Re-read those keeping only the boolean
144
+ * structure, so an unrecognised identifier becomes a license which is simply never allowed. OR then still offers a
145
+ * real choice, and AND still requires every branch, so nothing unknown can be accepted on its own.
146
+ */
147
+ function parseLenient(declaration) {
148
+ const tokens = declaration.match(/\(|\)|[^\s()]+/g);
149
+ if (!tokens)
150
+ return undefined;
151
+ let index = 0;
152
+ const keyword = (token, word) => token !== undefined && token.toUpperCase() === word;
153
+ function parseExpression() {
154
+ let node = parseTerm();
155
+ if (!node)
156
+ return undefined;
157
+ while (keyword(tokens[index], 'OR')) {
158
+ index++;
159
+ const right = parseTerm();
160
+ if (!right)
161
+ return undefined;
162
+ node = { left: node, conjunction: 'or', right };
163
+ }
164
+ return node;
165
+ }
166
+ function parseTerm() {
167
+ let node = parseFactor();
168
+ if (!node)
169
+ return undefined;
170
+ while (keyword(tokens[index], 'AND')) {
171
+ index++;
172
+ const right = parseFactor();
173
+ if (!right)
174
+ return undefined;
175
+ node = { left: node, conjunction: 'and', right };
176
+ }
177
+ return node;
178
+ }
179
+ function parseFactor() {
180
+ const token = tokens[index];
181
+ if (token === undefined || token === ')')
182
+ return undefined;
183
+ if (token === '(') {
184
+ index++;
185
+ const node = parseExpression();
186
+ if (!node || tokens[index] !== ')')
187
+ return undefined;
188
+ index++;
189
+ return node;
190
+ }
191
+ if (keyword(token, 'AND') || keyword(token, 'OR') || keyword(token, 'WITH'))
192
+ return undefined;
193
+ index++;
194
+ const plus = token.endsWith('+');
195
+ const node = { license: plus ? token.slice(0, -1) : token };
196
+ if (plus)
197
+ node.plus = true;
198
+ if (keyword(tokens[index], 'WITH')) {
199
+ index++;
200
+ if (tokens[index] === undefined)
201
+ return undefined;
202
+ node.exception = tokens[index];
203
+ index++;
204
+ }
205
+ return node;
206
+ }
207
+ const node = parseExpression();
208
+ return node && index === tokens.length ? node : undefined;
209
+ }
210
+ function parseDeclaration(declaration) {
211
+ try {
212
+ return { node: parse(declaration), strict: true };
213
+ }
214
+ catch {
215
+ const node = parseLenient(declaration);
216
+ return node ? { node, strict: false } : undefined;
217
+ }
218
+ }
219
+ function normalizedDeclaration(packageInfo) {
220
+ return packageInfo.declaredLicense?.trim() ?? '';
221
+ }
222
+ function packageLabel(packageInfo) {
223
+ return `${packageInfo.name}${packageInfo.version ? `@${packageInfo.version}` : ''}`;
224
+ }
225
+ function displayDeclaration(declaration) {
226
+ return declaration.replace(/[\u0000-\u001f\u007f]/g, (character) => {
227
+ switch (character) {
228
+ case '\r':
229
+ return '\\r';
230
+ case '\n':
231
+ return '\\n';
232
+ case '\t':
233
+ return '\\t';
234
+ default:
235
+ return `\\u${character.codePointAt(0).toString(16).padStart(4, '0')}`;
236
+ }
237
+ });
238
+ }
239
+ function ambiguitySuffix(evaluation) {
240
+ return evaluation.incompatibleAnd
241
+ ? ' Both licenses may apply, making this declaration ambiguous and incompatible. Ask package author to use OR if either license may be chosen, or clarify package licensing.'
242
+ : '';
243
+ }
244
+ function packageSort(a, b) {
245
+ if (a.kind === 'project' && b.kind !== 'project')
246
+ return -1;
247
+ if (a.kind !== 'project' && b.kind === 'project')
248
+ return 1;
249
+ return (a.name.localeCompare(b.name) ||
250
+ (a.version ?? '').localeCompare(b.version ?? '') ||
251
+ normalizedDeclaration(a).localeCompare(normalizedDeclaration(b)) ||
252
+ a.kind.localeCompare(b.kind));
253
+ }
254
+ function issue(packageInfo, message) {
255
+ return {
256
+ packageName: packageInfo.name,
257
+ packageVersion: packageInfo.version,
258
+ packageKind: packageInfo.kind,
259
+ message,
260
+ };
261
+ }
262
+ /** Resolve the license the built artifact is distributed under, which selects the policy applied to its dependencies */
263
+ export function resolveProjectLicense(inventory) {
264
+ const projectPackage = inventory.packages.find((packageInfo) => packageInfo.kind === 'project');
265
+ const declaration = projectPackage ? normalizedDeclaration(projectPackage) : '';
266
+ return { declaration, license: isProjectLicense(declaration) ? declaration : undefined };
267
+ }
268
+ function createSourceLicenseIssue(packageInfo, descriptor) {
269
+ const required = descriptor.requiredSourceLicense;
270
+ if (!required)
271
+ return undefined; // This project draws no distinction between its source and distribution licenses
272
+ const sourceLicense = packageInfo.sourceLicense?.trim() ?? '';
273
+ if (sourceLicense === required)
274
+ return undefined;
275
+ if (!sourceLicense) {
276
+ return issue(packageInfo, `Your ${descriptor.subject} source does not declare a license in package.json, but the Companion project requires ${descriptor.subject} source to be ${required} so it stays portable.`);
277
+ }
278
+ return issue(packageInfo, `Your ${descriptor.subject} source is licensed as ${displayDeclaration(sourceLicense)} in package.json, but the Companion project requires ${descriptor.subject} source to be ${required} so it stays portable. Declare the license the ${descriptor.subject} is distributed under in ${descriptor.distributionLicenseLocation} instead.`);
279
+ }
280
+ function createDistributionLicenseIssue(packageInfo, descriptor) {
281
+ const declaration = normalizedDeclaration(packageInfo);
282
+ if (!declaration) {
283
+ return issue(packageInfo, `Your ${descriptor.subject} does not declare a license in ${descriptor.distributionLicenseLocation}. ${supportedProjectLicenseAdvice()} Talk to us if you have a reason to use a different license.`);
284
+ }
285
+ if (isProjectLicense(declaration))
286
+ return undefined;
287
+ let reason = 'which is not supported';
288
+ try {
289
+ // A declaration with a conjunction is dual licensing, which a project must pick a single license from
290
+ if (!('license' in parse(declaration)))
291
+ reason = 'and dual licensing is not supported';
292
+ }
293
+ catch {
294
+ // Report the declaration as unsupported, even when SPDX parsing fails.
295
+ }
296
+ return issue(packageInfo, `Your ${descriptor.subject} is published under ${displayDeclaration(declaration)}, ${reason}. ${supportedProjectLicenseAdvice()} Talk to us if you have a reason to use a different license.`);
297
+ }
298
+ /** The distribution and source licenses may be declared in different files, and are reported independently */
299
+ function createProjectIssues(packageInfo, descriptor) {
300
+ return [
301
+ createDistributionLicenseIssue(packageInfo, descriptor),
302
+ createSourceLicenseIssue(packageInfo, descriptor),
303
+ ].filter((projectIssue) => projectIssue !== undefined);
304
+ }
305
+ export function createLicensePolicyIssues(inventory, descriptor, overrides) {
306
+ const result = [];
307
+ const seen = new Set();
308
+ const projectLicense = resolveProjectLicense(inventory).license ?? FALLBACK_PROJECT_LICENSE;
309
+ const policy = PROJECT_LICENSE_POLICIES[projectLicense];
310
+ const bundledLicenses = policy.allowedDependencyLicenses;
311
+ // Identical to the bundled set where the policy has nothing which only works as an external, or where the project
312
+ // has no way to externalise, so that no issue is ever reported as fixable in a way which would not in fact help
313
+ const externalLicenses = policy.externalOnlyLicenses.size && descriptor.externaliseAdvice
314
+ ? new Set([...bundledLicenses, ...policy.externalOnlyLicenses])
315
+ : bundledLicenses;
316
+ for (const packageInfo of [...inventory.packages].sort(packageSort)) {
317
+ const declaration = normalizedDeclaration(packageInfo);
318
+ const identity = packageInfo.kind === 'project'
319
+ ? 'project'
320
+ : `dependency:${packageInfo.name}@${packageInfo.version ?? ''}:${declaration}`;
321
+ if (seen.has(identity))
322
+ continue;
323
+ seen.add(identity);
324
+ if (packageInfo.kind === 'project') {
325
+ result.push(...createProjectIssues(packageInfo, descriptor));
326
+ continue;
327
+ }
328
+ if (!declaration) {
329
+ result.push(issue(packageInfo, `Dependency ${packageLabel(packageInfo)} has no declared license.`));
330
+ continue;
331
+ }
332
+ let parsed = parseDeclaration(declaration);
333
+ // Only ever replaces a declaration which is not valid SPDX, so a correction can never launder a real license
334
+ if (!parsed || !parsed.strict) {
335
+ const corrected = correctedPackageLicense(overrides, packageInfo.name, packageInfo.version);
336
+ if (corrected)
337
+ parsed = parseDeclaration(corrected) ?? parsed;
338
+ }
339
+ if (!parsed) {
340
+ result.push(issue(packageInfo, `Dependency ${packageLabel(packageInfo)} has unparseable license declaration ${displayDeclaration(declaration)}.`));
341
+ continue;
342
+ }
343
+ const allowedLicenses = packageInfo.kind === 'external' ? externalLicenses : bundledLicenses;
344
+ const evaluation = evaluate(parsed.node, allowedLicenses);
345
+ if (evaluation.allowed)
346
+ continue;
347
+ // Only reachable for a bundled package, externals are already evaluated against the wider set
348
+ if (descriptor.externaliseAdvice && evaluate(parsed.node, externalLicenses).allowed) {
349
+ result.push(issue(packageInfo, `Dependency ${packageLabel(packageInfo)} has license declaration ${displayDeclaration(declaration)} which may only be shipped as an external dependency, not built into the bundle. ${descriptor.externaliseAdvice}`));
350
+ continue;
351
+ }
352
+ if (!parsed.strict) {
353
+ // Reaching here means no branch we could recognise was allowed, so the unrecognised ones are the problem
354
+ result.push(issue(packageInfo, `Dependency ${packageLabel(packageInfo)} has license declaration ${displayDeclaration(declaration)} which is not a valid SPDX identifier, so it cannot be checked against the ${projectLicense} license policy. Ask the package author to declare a specific SPDX license.`));
355
+ continue;
356
+ }
357
+ result.push(issue(packageInfo, `Dependency ${packageLabel(packageInfo)} has license declaration ${displayDeclaration(declaration)} which is not compatible with the ${projectLicense} license policy.${ambiguitySuffix(evaluation)}`));
358
+ }
359
+ return result;
360
+ }
361
+ export function enforceLicensePolicy(inventory, descriptor, overrides, options = {}) {
362
+ const issues = createLicensePolicyIssues(inventory, descriptor, overrides);
363
+ if (issues.length === 0)
364
+ return;
365
+ const stderr = options.stderr ?? process.stderr;
366
+ const prefix = options.ignoreLicenseRules ? 'LICENSE WARNING' : 'LICENSE ERROR';
367
+ for (const policyIssue of issues)
368
+ stderr.write(`${prefix}: ${policyIssue.message}\n`);
369
+ if (options.ignoreLicenseRules) {
370
+ stderr.write(`License validation ignored ${issues.length} error${issues.length === 1 ? '' : 's'} because --ignore-license-rules was provided.\n`);
371
+ return;
372
+ }
373
+ stderr.write(`License validation failed with ${issues.length} error${issues.length === 1 ? '' : 's'}.\n`);
374
+ stderr.write(`${descriptor.helpMessage}\n`);
375
+ throw new LicensePolicyError(issues);
376
+ }
@@ -3,17 +3,19 @@
3
3
  import 'zx/globals';
4
4
  import { buildPackage } from './lib/build-util.js';
5
5
  import { LicensePolicyError } from './lib/license-warning-util.js';
6
- import { ignoreLicenseRules } from './lib/cli-options.js';
6
+ import { PrebuildCoverageError } from './lib/prebuild-check-util.js';
7
+ import { ignoreLicenseRules, ignorePrebuildRules } from './lib/cli-options.js';
7
8
  if (process.platform === 'win32') {
8
9
  usePowerShell(); // to enable powershell
9
10
  }
10
11
  if (argv.help) {
11
- console.log('Usage: companion-module-build [--dev] [--prerelease] [--ignore-license-rules]');
12
+ console.log('Usage: companion-module-build [--dev] [--prerelease] [--ignore-license-rules] [--ignore-prebuild-rules]');
12
13
  console.log('Builds the companion connection module');
13
14
  console.log(' --dev: Build in development mode. This will not minify the code, making it easier to debug.');
14
15
  console.log(' --prerelease: Build in prerelease mode. This gets added as metadata to the manifest');
15
16
  console.log(' --output <filename>: Output to a specific filename, without a file extension');
16
17
  console.log(' --ignore-license-rules: Report license policy issues as warnings instead of failing');
18
+ console.log(' --ignore-prebuild-rules: Report native libraries missing binaries for a required platform as warnings instead of failing');
17
19
  process.exit(0);
18
20
  }
19
21
  const { validateManifest } = await import('@companion-module/base/manifest').catch((e) => {
@@ -22,10 +24,11 @@ const { validateManifest } = await import('@companion-module/base/manifest').cat
22
24
  try {
23
25
  await buildPackage('@companion-module/base', validateManifest, 'connection', '>=1.4.0 <3.0.0', {
24
26
  ignoreLicenseRules: ignoreLicenseRules(argv),
27
+ ignorePrebuildRules: ignorePrebuildRules(argv),
25
28
  });
26
29
  }
27
30
  catch (e) {
28
- if (e instanceof LicensePolicyError)
31
+ if (e instanceof LicensePolicyError || e instanceof PrebuildCoverageError)
29
32
  process.exitCode = 1;
30
33
  else
31
34
  throw e;
@@ -3,27 +3,30 @@
3
3
  import 'zx/globals';
4
4
  import { buildPackage } from './lib/build-util.js';
5
5
  import { LicensePolicyError } from './lib/license-warning-util.js';
6
- import { ignoreLicenseRules } from './lib/cli-options.js';
6
+ import { PrebuildCoverageError } from './lib/prebuild-check-util.js';
7
+ import { ignoreLicenseRules, ignorePrebuildRules } from './lib/cli-options.js';
7
8
  if (process.platform === 'win32') {
8
9
  usePowerShell(); // to enable powershell
9
10
  }
10
11
  if (argv.help) {
11
- console.log('Usage: companion-surface-build [--dev] [--prerelease] [--ignore-license-rules]');
12
+ console.log('Usage: companion-surface-build [--dev] [--prerelease] [--ignore-license-rules] [--ignore-prebuild-rules]');
12
13
  console.log('Builds the companion surface module');
13
14
  console.log(' --dev: Build in development mode. This will not minify the code, making it easier to debug.');
14
15
  console.log(' --prerelease: Build in prerelease mode. This gets added as metadata to the manifest');
15
16
  console.log(' --output <filename>: Output to a specific filename, without a file extension');
16
17
  console.log(' --ignore-license-rules: Report license policy issues as warnings instead of failing');
18
+ console.log(' --ignore-prebuild-rules: Report native libraries missing binaries for a required platform as warnings instead of failing');
17
19
  process.exit(0);
18
20
  }
19
21
  const { validateSurfaceManifest } = await import('@companion-surface/base');
20
22
  try {
21
23
  await buildPackage('@companion-surface/base', validateSurfaceManifest, 'surface', '>=1.0.0 <2.0.0', {
22
24
  ignoreLicenseRules: ignoreLicenseRules(argv),
25
+ ignorePrebuildRules: ignorePrebuildRules(argv),
23
26
  });
24
27
  }
25
28
  catch (e) {
26
- if (e instanceof LicensePolicyError)
29
+ if (e instanceof LicensePolicyError || e instanceof PrebuildCoverageError)
27
30
  process.exitCode = 1;
28
31
  else
29
32
  throw e;
@@ -9,4 +9,5 @@ export declare function enforceLegalInventory(inventory: LegalInventory, options
9
9
  }): void;
10
10
  export declare function buildPackage<M>(frameworkPackageName: string, validateManifest: (manifest: M, looseChecks: boolean) => void, moduleType: ModuleType, versionRange: string, options?: {
11
11
  ignoreLicenseRules?: boolean;
12
+ ignorePrebuildRules?: boolean;
12
13
  }): Promise<void>;
@@ -10,6 +10,7 @@ import { collectInstalledPackages, collectMetafilePackages, createLegalInventory
10
10
  import { createEsbuildOptions, loadModuleBuildDefinition } from './bundle-util.js';
11
11
  import { resolveExternalDependencies } from './external-install-util.js';
12
12
  import { enforceLicensePolicy } from './license-warning-util.js';
13
+ import { checkPrebuildCoverage, enforcePrebuildCoverage } from './prebuild-check-util.js';
13
14
  function toSanitizedDirname(name) {
14
15
  return name.replace(/[^a-zA-Z0-9-\.]/g, '-').replace(/[-+]/g, '-');
15
16
  }
@@ -176,6 +177,9 @@ export async function buildPackage(frameworkPackageName, validateManifest, modul
176
177
  }
177
178
  }
178
179
  }
180
+ // Ensure every native library has binaries for all the platforms we need to support
181
+ const prebuildReport = await checkPrebuildCoverage(packageBaseDir);
182
+ enforcePrebuildCoverage(prebuildReport, { ignorePrebuildRules: options.ignorePrebuildRules });
179
183
  const shippedPackages = [...metafilePackages.packages];
180
184
  if (Object.keys(packageJson.dependencies).length) {
181
185
  shippedPackages.push(...(await collectInstalledPackages(path.join(packageBaseDir, 'node_modules'))));
@@ -185,7 +189,7 @@ export async function buildPackage(frameworkPackageName, validateManifest, modul
185
189
  console.warn(`License inventory: ${diagnostic}`);
186
190
  }
187
191
  await writeLegalArtifacts(packageBaseDir, legalInventory);
188
- enforceLegalInventory(legalInventory, options);
192
+ enforceLegalInventory(legalInventory, { ignoreLicenseRules: options.ignoreLicenseRules });
189
193
  // Create tgz of the build
190
194
  let tgzFile = toSanitizedDirname(`${manifestJson.id}-${manifestJson.version}`);
191
195
  if (typeof argv['output'] === 'string') {
@@ -1 +1,2 @@
1
1
  export declare function ignoreLicenseRules(argv: Record<string, unknown>): boolean;
2
+ export declare function ignorePrebuildRules(argv: Record<string, unknown>): boolean;
@@ -2,3 +2,7 @@ export function ignoreLicenseRules(argv) {
2
2
  const value = argv['ignore-license-rules'];
3
3
  return value === true || value === 'true';
4
4
  }
5
+ export function ignorePrebuildRules(argv) {
6
+ const value = argv['ignore-prebuild-rules'];
7
+ return value === true || value === 'true';
8
+ }
@@ -1,3 +1,4 @@
1
+ import type { LicenseOverrides } from '../../license/index.js';
1
2
  /**
2
3
  * Packages which ship no license declaration in their package.json, but whose license has been confirmed by reading
3
4
  * what they publish. Only add an entry after checking that version yourself, and record where the license was found.
@@ -6,7 +7,6 @@
6
7
  * so an entry can never hide what a package says about itself.
7
8
  */
8
9
  export declare const KNOWN_PACKAGE_LICENSES: Record<string, string>;
9
- export declare function knownPackageLicense(name: string | undefined, version: string | undefined): string | undefined;
10
10
  /**
11
11
  * Packages which declare a license that is not a valid SPDX expression, mapped to what the license text they publish
12
12
  * actually is. Unlike KNOWN_PACKAGE_LICENSES this overrides what a package says about itself, so it is only consulted
@@ -16,4 +16,5 @@ export declare function knownPackageLicense(name: string | undefined, version: s
16
16
  * Keys are exact `name@version`, so a later release declaring something different is unaffected.
17
17
  */
18
18
  export declare const CORRECTED_PACKAGE_LICENSES: Record<string, string>;
19
- export declare function correctedPackageLicense(name: string | undefined, version: string | undefined): string | undefined;
19
+ /** The overrides the module tooling applies. Other consumers of the engine carry their own. */
20
+ export declare const MODULE_LICENSE_OVERRIDES: LicenseOverrides;
@@ -41,11 +41,6 @@ export const KNOWN_PACKAGE_LICENSES = {
41
41
  // license, "MIT License", in the published tarball, https://github.com/fabiospampinato/stubborn-fs
42
42
  'stubborn-fs@1.2.5': 'MIT',
43
43
  };
44
- export function knownPackageLicense(name, version) {
45
- if (!name || !version)
46
- return undefined;
47
- return KNOWN_PACKAGE_LICENSES[`${name}@${version}`];
48
- }
49
44
  /**
50
45
  * Packages which declare a license that is not a valid SPDX expression, mapped to what the license text they publish
51
46
  * actually is. Unlike KNOWN_PACKAGE_LICENSES this overrides what a package says about itself, so it is only consulted
@@ -71,8 +66,8 @@ export const CORRECTED_PACKAGE_LICENSES = {
71
66
  // clause forbidding use of the Dojo Foundation's name to endorse derived products, https://github.com/kriszyp/json-schema
72
67
  'json-schema@0.2.3': '(AFL-2.1 OR BSD-3-Clause)',
73
68
  };
74
- export function correctedPackageLicense(name, version) {
75
- if (!name || !version)
76
- return undefined;
77
- return CORRECTED_PACKAGE_LICENSES[`${name}@${version}`];
78
- }
69
+ /** The overrides the module tooling applies. Other consumers of the engine carry their own. */
70
+ export const MODULE_LICENSE_OVERRIDES = {
71
+ knownPackageLicenses: KNOWN_PACKAGE_LICENSES,
72
+ correctedPackageLicenses: CORRECTED_PACKAGE_LICENSES,
73
+ };
@@ -1,55 +1,16 @@
1
1
  import * as esbuild from 'esbuild';
2
- export type ShippedPackageKind = 'project' | 'bundled' | 'external';
3
- export interface ShippedPackage {
4
- kind: ShippedPackageKind;
5
- name: string;
6
- version?: string;
7
- declaredLicense?: string;
8
- /** Only for the project, the package.json license of its own source, which declaredLicense distributes it under */
9
- sourceLicense?: string;
10
- /** Where recipients can obtain the source of this package, as required by MPL-2.0 and the GPL family */
11
- repositoryUrl?: string;
12
- packageRoot: string;
13
- contributingPaths: Set<string>;
14
- }
15
- export interface MetafilePackageCollection {
16
- packages: ShippedPackage[];
17
- diagnostics: string[];
18
- }
19
- export interface LegalText {
20
- role: 'license' | 'notice' | 'source-comment';
21
- filename: string;
22
- content: string;
23
- sha256: string;
24
- }
25
- export interface ShippedPackageLegalRecord extends ShippedPackage {
26
- legalTexts: LegalText[];
27
- }
28
- export interface PackageLegalMaterial {
29
- package: ShippedPackageLegalRecord;
30
- diagnostics: string[];
31
- }
32
- export interface LegalInventory {
33
- packages: ShippedPackageLegalRecord[];
34
- diagnostics: string[];
35
- }
36
- type PackageJson = {
37
- name?: string;
38
- version?: string;
39
- license?: unknown;
40
- licenses?: unknown;
41
- repository?: unknown;
42
- homepage?: unknown;
43
- };
44
- export declare function normalizeRepositoryUrl(packageJson: PackageJson): string | undefined;
45
- export declare function normalizeInventoryPath(inputPath: string, ownerRoot: string): string;
2
+ import { type LegalInventory, type PackageCollection, type ProjectIdentity, type ShippedPackage } from '../../license/index.js';
3
+ export { NO_LICENSE_OVERRIDES, collectPackageLegalMaterial, collectPackagesFromInputPaths, createLegalInventory, getContributingInputs, normalizeInventoryPath, normalizeLegalText, normalizeRepositoryUrl, renderLicenseFile, renderNoticeFile, writeLegalArtifacts, } from '../../license/index.js';
4
+ export type { LegalInventory, LegalText, LicenseOverrides, PackageLegalMaterial, ProjectIdentity, ShippedPackage, ShippedPackageKind, ShippedPackageLegalRecord, } from '../../license/index.js';
5
+ export type { PackageCollection, PackageCollection as MetafilePackageCollection } from '../../license/index.js';
6
+ /**
7
+ * How a module identifies itself to the engine. The manifest license is what the packaged module is distributed as,
8
+ * which is what its dependencies must fit. package.json only licenses the module's own source, so it is the fallback
9
+ * for modules not declaring the other.
10
+ */
11
+ export declare function moduleProjectIdentity(moduleDir: string): Promise<ProjectIdentity>;
12
+ /** Scans a dependency tree installed alongside a module, applying the module tooling's own license overrides */
46
13
  export declare function collectInstalledPackages(nodeModulesDir: string): Promise<ShippedPackage[]>;
47
- export declare function normalizeLegalText(content: string): string;
48
- export declare function collectPackageLegalMaterial(packageInfo: ShippedPackage): Promise<PackageLegalMaterial>;
49
- export declare function renderLicenseFile(inventory: LegalInventory): string;
50
- export declare function renderNoticeFile(inventory: LegalInventory): string | undefined;
51
- export declare function writeLegalArtifacts(outputDir: string, inventory: LegalInventory): Promise<void>;
52
- export declare function createLegalInventory(packages: ShippedPackage[]): Promise<LegalInventory>;
14
+ /** Attributes the inputs of a module's esbuild metafile, applying the module tooling's own license overrides */
15
+ export declare function collectMetafilePackages(moduleDir: string, metafile: esbuild.Metafile): Promise<PackageCollection>;
53
16
  export declare function analyzeShippedLegalInventory(moduleDir: string): Promise<LegalInventory>;
54
- export declare function collectMetafilePackages(moduleDir: string, metafile: esbuild.Metafile): Promise<MetafilePackageCollection>;
55
- export {};