@companion-module/tools 3.0.2 → 3.1.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## [3.1.0](https://github.com/bitfocus/companion-module-tools/compare/v3.0.2...v3.1.0) (2026-08-28)
4
+
5
+
6
+ ### Features
7
+
8
+ * add node26 tsconfig ([bb73011](https://github.com/bitfocus/companion-module-tools/commit/bb7301104fd641dc3dfda0bd7abef4dc36870fb5))
9
+ * Replace license-file generation after esbuild migration ([#112](https://github.com/bitfocus/companion-module-tools/issues/112)) ([7819905](https://github.com/bitfocus/companion-module-tools/commit/7819905a47f3a357a4fe8e1b6ed117b617e3f2cc))
10
+ * warn about restricted module licenses ([#113](https://github.com/bitfocus/companion-module-tools/issues/113)) ([b1cc996](https://github.com/bitfocus/companion-module-tools/commit/b1cc996bc1201f796e0e1afa26f29822bfff260f))
11
+
12
+
3
13
  ## [3.0.2](https://github.com/bitfocus/companion-module-tools/compare/v3.0.1...v3.0.2) (2026-06-19)
4
14
 
5
15
 
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2026 Bitfocus AS
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
package/README.md CHANGED
@@ -14,6 +14,28 @@ More information on this command is available [on the wiki](https://github.com/b
14
14
 
15
15
  Generate the new format manifest from an old `package.json`.
16
16
 
17
+ ## Licensing
18
+
19
+ A module has two licenses, and they are not the same thing:
20
+
21
+ - `license` in `package.json` is the license of the code you wrote. This must be `MIT`, so module source always stays
22
+ portable and can be reused anywhere.
23
+ - `license` in `companion/manifest.json` is the license the packaged module is distributed under. A build bundles your
24
+ code together with its dependencies, so this has to be a license that whole blob can be shipped under.
25
+
26
+ Your own code stays MIT even when a dependency is copyleft. If you bundle a GPL-3.0 dependency, your source stays MIT
27
+ while the manifest declares `GPL-3.0-only`, because that is what the combined package must be distributed as. Anyone
28
+ reusing your source on its own still gets it under MIT.
29
+
30
+ The build and check commands validate every bundled and external dependency against the manifest license. Supported
31
+ values are `MIT` (recommended, it can use the most of npm), `GPL-2.0-only` and `GPL-3.0-only`. Dual licensing is not
32
+ supported, and a module which declares no license in its manifest falls back to the one in `package.json`.
33
+
34
+ Some older packages ship a license file but declare no license in their `package.json`, so there is nothing to check
35
+ against. Those are listed in `src/scripts/lib/known-package-licenses.ts` once their license has been confirmed from
36
+ what they publish, so open a PR there if you hit one. A license a package declares itself always wins over that list.
37
+
38
+ Pass `--ignore-license-rules` to report license problems as warnings instead of failing the command.
17
39
 
18
40
  ## Upgrading from v1.x to v2.0
19
41
 
@@ -21,8 +43,8 @@ v2.0 of this library includes some breaking changes to how eslint and prettier h
21
43
 
22
44
  This change was done for two reasons:
23
45
 
24
- 1) Very few modules use eslint, making this extra weight for them for no gain.
25
- 2) Recent versions of yarn do not expose these binaries in a way which is easily callable by modules, requiring tricks to be able to execute them.
46
+ 1. Very few modules use eslint, making this extra weight for them for no gain.
47
+ 2. Recent versions of yarn do not expose these binaries in a way which is easily callable by modules, requiring tricks to be able to execute them.
26
48
 
27
49
  To resolve this, you will need to do a `yarn add --dev eslint prettier` in your modules to install the dependencies, and update any scripts to remove the invocation hacks.
28
50
 
@@ -42,7 +64,7 @@ If using TypeScript, you should specify a `typescriptRoot`:
42
64
  import { generateEslintConfig } from '@companion-module/tools/eslint/config.mjs'
43
65
 
44
66
  export default generateEslintConfig({
45
- enableTypescript: true,
67
+ enableTypescript: true,
46
68
  })
47
69
  ```
48
70
 
@@ -2,18 +2,31 @@
2
2
  // The zx shebang doesn't resolve dependencies correctly
3
3
  import 'zx/globals';
4
4
  import { buildPackage } from './lib/build-util.js';
5
+ import { LicensePolicyError } from './lib/license-warning-util.js';
6
+ import { ignoreLicenseRules } from './lib/cli-options.js';
5
7
  if (process.platform === 'win32') {
6
8
  usePowerShell(); // to enable powershell
7
9
  }
8
10
  if (argv.help) {
9
- console.log('Usage: companion-module-build [--dev] [--prerelease]');
11
+ console.log('Usage: companion-module-build [--dev] [--prerelease] [--ignore-license-rules]');
10
12
  console.log('Builds the companion connection module');
11
13
  console.log(' --dev: Build in development mode. This will not minify the code, making it easier to debug.');
12
14
  console.log(' --prerelease: Build in prerelease mode. This gets added as metadata to the manifest');
13
15
  console.log(' --output <filename>: Output to a specific filename, without a file extension');
16
+ console.log(' --ignore-license-rules: Report license policy issues as warnings instead of failing');
14
17
  process.exit(0);
15
18
  }
16
19
  const { validateManifest } = await import('@companion-module/base/manifest').catch((e) => {
17
20
  throw new Error(`Failed to load @companion-module/base. Have you installed a compatible version?: ${e?.message ?? e}`);
18
21
  });
19
- await buildPackage('@companion-module/base', validateManifest, 'connection', '>=1.4.0 <3.0.0');
22
+ try {
23
+ await buildPackage('@companion-module/base', validateManifest, 'connection', '>=1.4.0 <3.0.0', {
24
+ ignoreLicenseRules: ignoreLicenseRules(argv),
25
+ });
26
+ }
27
+ catch (e) {
28
+ if (e instanceof LicensePolicyError)
29
+ process.exitCode = 1;
30
+ else
31
+ throw e;
32
+ }
@@ -2,16 +2,29 @@
2
2
  // The zx shebang doesn't resolve dependencies correctly
3
3
  import 'zx/globals';
4
4
  import { buildPackage } from './lib/build-util.js';
5
+ import { LicensePolicyError } from './lib/license-warning-util.js';
6
+ import { ignoreLicenseRules } from './lib/cli-options.js';
5
7
  if (process.platform === 'win32') {
6
8
  usePowerShell(); // to enable powershell
7
9
  }
8
10
  if (argv.help) {
9
- console.log('Usage: companion-module-build [--dev] [--prerelease]');
10
- console.log('Builds the companion connection module');
11
+ console.log('Usage: companion-surface-build [--dev] [--prerelease] [--ignore-license-rules]');
12
+ console.log('Builds the companion surface module');
11
13
  console.log(' --dev: Build in development mode. This will not minify the code, making it easier to debug.');
12
14
  console.log(' --prerelease: Build in prerelease mode. This gets added as metadata to the manifest');
13
15
  console.log(' --output <filename>: Output to a specific filename, without a file extension');
16
+ console.log(' --ignore-license-rules: Report license policy issues as warnings instead of failing');
14
17
  process.exit(0);
15
18
  }
16
19
  const { validateSurfaceManifest } = await import('@companion-surface/base');
17
- await buildPackage('@companion-surface/base', validateSurfaceManifest, 'surface', '>=1.0.0 <2.0.0');
20
+ try {
21
+ await buildPackage('@companion-surface/base', validateSurfaceManifest, 'surface', '>=1.0.0 <2.0.0', {
22
+ ignoreLicenseRules: ignoreLicenseRules(argv),
23
+ });
24
+ }
25
+ catch (e) {
26
+ if (e instanceof LicensePolicyError)
27
+ process.exitCode = 1;
28
+ else
29
+ throw e;
30
+ }
@@ -1,25 +1,37 @@
1
1
  #!/usr/bin/env node
2
2
  // The zx shebang doesn't resolve dependencies correctly
3
3
  import 'zx/globals';
4
- import path from 'path';
5
- import { findModuleDir, readUTF8File } from './lib/build-util.js';
4
+ import { createRequire } from 'node:module';
5
+ import { findModuleDir } from './lib/build-util.js';
6
+ import { checkPackage } from './lib/check-util.js';
7
+ import { LicensePolicyError } from './lib/license-warning-util.js';
8
+ import { ignoreLicenseRules } from './lib/cli-options.js';
6
9
  if (process.platform === 'win32') {
7
10
  usePowerShell(); // to enable powershell
8
11
  }
12
+ if (argv.help) {
13
+ console.log('Usage: companion-module-check [--ignore-license-rules]');
14
+ console.log('Checks the companion connection module');
15
+ console.log(' --ignore-license-rules: Report license policy issues as warnings instead of failing');
16
+ process.exit(0);
17
+ }
9
18
  const { validateManifest } = await import('@companion-module/base/manifest').catch((e) => {
10
19
  throw new Error(`Failed to load @companion-module/base. Have you installed a compatible version?: ${e?.message ?? e}`);
11
20
  });
12
- // const toolsDir = path.join(__dirname, '..')
13
- const toolsDir = await findModuleDir(import.meta.resolve('@companion-module/tools'));
14
- const frameworkDir = await findModuleDir(import.meta.resolve('@companion-module/base'));
21
+ const require = createRequire(import.meta.url);
22
+ const toolsDir = await findModuleDir(require.resolve('@companion-module/tools'));
23
+ const frameworkDir = await findModuleDir(require.resolve('@companion-module/base'));
15
24
  console.log(`Checking for: ${process.cwd()}`);
16
25
  console.log(`Tools path: ${toolsDir}`);
17
26
  console.log(`Framework path: ${frameworkDir}`);
18
- const manifestJson = JSON.parse(await readUTF8File(path.resolve('./companion/manifest.json')));
19
27
  try {
20
- validateManifest(manifestJson, false);
28
+ await checkPackage({ validateManifest, ignoreLicenseRules: ignoreLicenseRules(argv) });
21
29
  }
22
30
  catch (e) {
23
- console.error('Manifest validation failed', e);
24
- process.exit(1);
31
+ if (e instanceof LicensePolicyError)
32
+ process.exitCode = 1;
33
+ else {
34
+ console.error('Manifest validation failed', e);
35
+ process.exit(1);
36
+ }
25
37
  }
@@ -1,23 +1,38 @@
1
1
  #!/usr/bin/env node
2
2
  // The zx shebang doesn't resolve dependencies correctly
3
3
  import 'zx/globals';
4
- import path from 'path';
5
- import { validateSurfaceManifest } from '@companion-surface/base';
6
- import { findModuleDir, readUTF8File } from './lib/build-util.js';
4
+ import { createRequire } from 'node:module';
5
+ import { findModuleDir } from './lib/build-util.js';
6
+ import { checkPackage } from './lib/check-util.js';
7
+ import { LicensePolicyError } from './lib/license-warning-util.js';
8
+ import { ignoreLicenseRules } from './lib/cli-options.js';
7
9
  if (process.platform === 'win32') {
8
10
  usePowerShell(); // to enable powershell
9
11
  }
10
- // const toolsDir = path.join(__dirname, '..')
11
- const toolsDir = await findModuleDir(import.meta.resolve('@companion-module/tools'));
12
- const frameworkDir = await findModuleDir(import.meta.resolve('@companion-surface/base'));
12
+ if (argv.help) {
13
+ console.log('Usage: companion-surface-check [--ignore-license-rules]');
14
+ console.log('Checks the companion surface module');
15
+ console.log(' --ignore-license-rules: Report license policy issues as warnings instead of failing');
16
+ process.exit(0);
17
+ }
18
+ const { validateSurfaceManifest } = await import('@companion-surface/base');
19
+ const require = createRequire(import.meta.url);
20
+ const toolsDir = await findModuleDir(require.resolve('@companion-module/tools'));
21
+ const frameworkDir = await findModuleDir(require.resolve('@companion-surface/base'));
13
22
  console.log(`Checking for: ${process.cwd()}`);
14
23
  console.log(`Tools path: ${toolsDir}`);
15
24
  console.log(`Framework path: ${frameworkDir}`);
16
- const manifestJson = JSON.parse(await readUTF8File(path.resolve('./companion/manifest.json')));
17
25
  try {
18
- validateSurfaceManifest(manifestJson, false);
26
+ await checkPackage({
27
+ validateManifest: validateSurfaceManifest,
28
+ ignoreLicenseRules: ignoreLicenseRules(argv),
29
+ });
19
30
  }
20
31
  catch (e) {
21
- console.error('Manifest validation failed', e);
22
- process.exit(1);
32
+ if (e instanceof LicensePolicyError)
33
+ process.exitCode = 1;
34
+ else {
35
+ console.error('Manifest validation failed', e);
36
+ process.exit(1);
37
+ }
23
38
  }
@@ -1,6 +1,12 @@
1
1
  import 'zx/globals';
2
+ import type { ModuleType } from './license-warning-util.js';
3
+ import type { LegalInventory } from './license-util.js';
2
4
  export declare function readUTF8File(filePath: string): Promise<string>;
3
5
  export declare function findModuleDir(cwd: string): Promise<string>;
4
- type ModuleType = 'connection' | 'surface';
5
- export declare function buildPackage<M>(frameworkPackageName: string, validateManifest: (manifest: M, looseChecks: boolean) => void, moduleType: ModuleType, versionRange: string): Promise<void>;
6
- export {};
6
+ export declare function enforceLegalInventory(inventory: LegalInventory, options?: {
7
+ ignoreLicenseRules?: boolean;
8
+ stderr?: Pick<NodeJS.WriteStream, 'write' | 'isTTY'>;
9
+ }): void;
10
+ export declare function buildPackage<M>(frameworkPackageName: string, validateManifest: (manifest: M, looseChecks: boolean) => void, moduleType: ModuleType, versionRange: string, options?: {
11
+ ignoreLicenseRules?: boolean;
12
+ }): Promise<void>;
@@ -6,6 +6,10 @@ import * as tar from 'tar';
6
6
  import { createRequire } from 'module';
7
7
  import * as semver from 'semver';
8
8
  import * as esbuild from 'esbuild';
9
+ import { collectInstalledPackages, collectMetafilePackages, createLegalInventory, writeLegalArtifacts, } from './license-util.js';
10
+ import { createEsbuildOptions, loadModuleBuildDefinition } from './bundle-util.js';
11
+ import { resolveExternalDependencies } from './external-install-util.js';
12
+ import { enforceLicensePolicy } from './license-warning-util.js';
9
13
  function toSanitizedDirname(name) {
10
14
  return name.replace(/[^a-zA-Z0-9-\.]/g, '-').replace(/[-+]/g, '-');
11
15
  }
@@ -23,7 +27,10 @@ export async function findModuleDir(cwd) {
23
27
  }
24
28
  return path.dirname(pkgJsonPath);
25
29
  }
26
- export async function buildPackage(frameworkPackageName, validateManifest, moduleType, versionRange) {
30
+ export function enforceLegalInventory(inventory, options = {}) {
31
+ enforceLicensePolicy(inventory, options);
32
+ }
33
+ export async function buildPackage(frameworkPackageName, validateManifest, moduleType, versionRange, options = {}) {
27
34
  // const toolsDir = path.join(__dirname, '..')
28
35
  const moduleDir = process.cwd();
29
36
  const moduleRequire = createRequire(path.join(moduleDir, 'package.json'));
@@ -57,55 +64,27 @@ export async function buildPackage(frameworkPackageName, validateManifest, modul
57
64
  // create new
58
65
  await fs.mkdir(packageBaseDir, { recursive: true });
59
66
  const isDev = !!(argv.dev || argv.debug);
60
- // Load optional build config from the module directory
61
- let buildConfig = {};
62
- const buildConfigPath = path.resolve('build-config.cjs');
63
- if (fs.existsSync(buildConfigPath)) {
64
- buildConfig = require(buildConfigPath);
67
+ const buildDefinition = await loadModuleBuildDefinition(moduleDir);
68
+ const buildConfig = buildDefinition.buildConfig;
69
+ if (fs.existsSync(path.join(moduleDir, 'build-config.cjs')))
65
70
  console.log('Found additional build configuration');
66
- }
67
- // Flatten externals to a string array for esbuild
68
- const externalsRaw = Array.isArray(buildConfig.externals)
69
- ? buildConfig.externals
70
- : buildConfig.externals
71
- ? [buildConfig.externals]
72
- : [];
73
- // Build entry points map
74
- const entryPoints = {
75
- main: './' + srcPackageJson.main,
76
- ...buildConfig.additionalEntrypoints,
77
- };
71
+ const externalsRaw = buildDefinition.externals;
78
72
  // build the code
79
- const esbuildOptions = {
80
- entryPoints,
73
+ const esbuildOptions = createEsbuildOptions(buildDefinition, {
81
74
  outdir: path.resolve(moduleDir, packageBaseDir),
82
- bundle: true,
83
- platform: 'node',
84
- format: 'esm',
85
75
  minify: isDev ? false : !buildConfig.disableMinifier,
86
76
  sourcemap: isDev ? 'inline' : false,
87
- target: 'node22',
88
- external: externalsRaw,
89
- // When bundling to ESM, `require`, `__dirname`, and `__filename` are not defined.
90
- // Many CJS transitive dependencies call require() for Node built-ins (e.g. `require('events')`).
91
- // Inject a small header that recreates them with ESM-native APIs so they work at runtime.
92
- banner: {
93
- js: [
94
- `import { createRequire as __esbuild_createRequire } from 'module';`,
95
- `import { fileURLToPath as __esbuild_fileURLToPath } from 'url';`,
96
- `import { dirname as __esbuild_dirname } from 'path';`,
97
- `const require = __esbuild_createRequire(import.meta.url);`,
98
- `const __filename = __esbuild_fileURLToPath(import.meta.url);`,
99
- `const __dirname = __esbuild_dirname(__filename);`,
100
- ].join('\n'),
101
- },
102
- };
103
- await esbuild.build(esbuildOptions);
77
+ });
78
+ const buildResult = await esbuild.build(esbuildOptions);
79
+ if (!buildResult.metafile)
80
+ throw new Error('esbuild did not produce a metafile');
81
+ const metafilePackages = await collectMetafilePackages(moduleDir, buildResult.metafile);
104
82
  // copy in the metadata
105
83
  await fs.copy('companion', path.join(packageBaseDir, 'companion'));
106
84
  // Copy the manifest, overriding some properties
107
85
  manifestJson.runtime.entrypoint = '../main.js';
108
86
  manifestJson.version = srcPackageJson.version;
87
+ // Note: not syncing the license, as source and distrubution could be different licenses.
109
88
  manifestJson.runtime.api = 'nodejs-ipc';
110
89
  manifestJson.runtime.apiVersion = frameworkPackageJson.version;
111
90
  // Bake in the prerelease flag if using module-base which is new enough
@@ -132,15 +111,7 @@ export async function buildPackage(frameworkPackageName, validateManifest, modul
132
111
  };
133
112
  // Ensure that any externals are added as dependencies
134
113
  if (externalsRaw.length) {
135
- // Add any external dependencies with versions matching what is currently installed.
136
- // Externals are plain package-name strings when using esbuild.
137
- for (const external of externalsRaw) {
138
- const extPath = await findUp('package.json', { cwd: moduleRequire.resolve(external) });
139
- if (extPath) {
140
- const extJson = JSON.parse(await readUTF8File(extPath));
141
- packageJson.dependencies[extJson.name] = extJson.version;
142
- }
143
- }
114
+ Object.assign(packageJson.dependencies, await resolveExternalDependencies(moduleDir, externalsRaw));
144
115
  // Ensure node-gyp is excluded from the installed deps in the output package
145
116
  packageJson.resolutions = {
146
117
  'node-gyp': 'npm:empty-npm-package@1.0.0',
@@ -205,6 +176,16 @@ export async function buildPackage(frameworkPackageName, validateManifest, modul
205
176
  }
206
177
  }
207
178
  }
179
+ const shippedPackages = [...metafilePackages.packages];
180
+ if (Object.keys(packageJson.dependencies).length) {
181
+ shippedPackages.push(...(await collectInstalledPackages(path.join(packageBaseDir, 'node_modules'))));
182
+ }
183
+ const legalInventory = await createLegalInventory(shippedPackages);
184
+ for (const diagnostic of [...metafilePackages.diagnostics, ...legalInventory.diagnostics]) {
185
+ console.warn(`License inventory: ${diagnostic}`);
186
+ }
187
+ await writeLegalArtifacts(packageBaseDir, legalInventory);
188
+ enforceLegalInventory(legalInventory, options);
208
189
  // Create tgz of the build
209
190
  let tgzFile = toSanitizedDirname(`${manifestJson.id}-${manifestJson.version}`);
210
191
  if (typeof argv['output'] === 'string') {
@@ -0,0 +1,11 @@
1
+ import * as esbuild from 'esbuild';
2
+ import type { ModuleBuildConfig } from '../../build-config.js';
3
+ export interface ModuleBuildDefinition {
4
+ moduleDir: string;
5
+ packageJson: Record<string, unknown>;
6
+ buildConfig: ModuleBuildConfig;
7
+ entryPoints: Record<string, string>;
8
+ externals: string[];
9
+ }
10
+ export declare function loadModuleBuildDefinition(moduleDir: string): Promise<ModuleBuildDefinition>;
11
+ export declare function createEsbuildOptions(definition: ModuleBuildDefinition, overrides: Pick<esbuild.BuildOptions, 'outdir' | 'write' | 'minify' | 'sourcemap'>): esbuild.BuildOptions;
@@ -0,0 +1,49 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { readFile } from 'node:fs/promises';
3
+ import { createRequire } from 'node:module';
4
+ import path from 'node:path';
5
+ export async function loadModuleBuildDefinition(moduleDir) {
6
+ const moduleRequire = createRequire(path.join(moduleDir, 'package.json'));
7
+ const packageJson = JSON.parse(await readFile(path.join(moduleDir, 'package.json'), 'utf8'));
8
+ if (typeof packageJson.main !== 'string')
9
+ throw new Error('Missing main in package.json');
10
+ let buildConfig = {};
11
+ const buildConfigPath = path.join(moduleDir, 'build-config.cjs');
12
+ if (existsSync(buildConfigPath))
13
+ buildConfig = moduleRequire(buildConfigPath);
14
+ const externals = Array.isArray(buildConfig.externals)
15
+ ? buildConfig.externals
16
+ : buildConfig.externals
17
+ ? [buildConfig.externals]
18
+ : [];
19
+ return {
20
+ moduleDir,
21
+ packageJson,
22
+ buildConfig,
23
+ entryPoints: { main: `./${packageJson.main}`, ...buildConfig.additionalEntrypoints },
24
+ externals,
25
+ };
26
+ }
27
+ export function createEsbuildOptions(definition, overrides) {
28
+ return {
29
+ absWorkingDir: definition.moduleDir,
30
+ entryPoints: definition.entryPoints,
31
+ bundle: true,
32
+ platform: 'node',
33
+ format: 'esm',
34
+ target: 'node22',
35
+ external: definition.externals,
36
+ metafile: true,
37
+ ...overrides,
38
+ banner: {
39
+ js: [
40
+ `import { createRequire as __esbuild_createRequire } from 'module';`,
41
+ `import { fileURLToPath as __esbuild_fileURLToPath } from 'url';`,
42
+ `import { dirname as __esbuild_dirname } from 'path';`,
43
+ `const require = __esbuild_createRequire(import.meta.url);`,
44
+ `const __filename = __esbuild_fileURLToPath(import.meta.url);`,
45
+ `const __dirname = __esbuild_dirname(__filename);`,
46
+ ].join('\n'),
47
+ },
48
+ };
49
+ }
@@ -0,0 +1,6 @@
1
+ export declare function checkPackage<M>(options: {
2
+ validateManifest: (manifest: M, looseChecks: boolean) => void;
3
+ cwd?: string;
4
+ ignoreLicenseRules?: boolean;
5
+ stderr?: Pick<NodeJS.WriteStream, 'write' | 'isTTY'>;
6
+ }): Promise<void>;
@@ -0,0 +1,14 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { enforceLicensePolicy } from './license-warning-util.js';
4
+ import { analyzeShippedLegalInventory } from './license-util.js';
5
+ export async function checkPackage(options) {
6
+ const cwd = options.cwd ?? process.cwd();
7
+ const manifest = JSON.parse(await readFile(path.join(cwd, 'companion', 'manifest.json'), 'utf8'));
8
+ options.validateManifest(manifest, false);
9
+ const inventory = await analyzeShippedLegalInventory(cwd);
10
+ enforceLicensePolicy(inventory, {
11
+ ignoreLicenseRules: options.ignoreLicenseRules,
12
+ stderr: options.stderr,
13
+ });
14
+ }
@@ -0,0 +1 @@
1
+ export declare function ignoreLicenseRules(argv: Record<string, unknown>): boolean;
@@ -0,0 +1,4 @@
1
+ export function ignoreLicenseRules(argv) {
2
+ const value = argv['ignore-license-rules'];
3
+ return value === true || value === 'true';
4
+ }
@@ -0,0 +1,2 @@
1
+ export declare function resolveExternalDependencies(moduleDir: string, externals: string[]): Promise<Record<string, string>>;
2
+ export declare function withInstalledExternalTree<T>(dependencies: Record<string, string>, inspect: (nodeModulesDir: string) => Promise<T>): Promise<T>;
@@ -0,0 +1,51 @@
1
+ import { execFile } from 'node:child_process';
2
+ import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
3
+ import { createRequire } from 'node:module';
4
+ import { tmpdir } from 'node:os';
5
+ import path from 'node:path';
6
+ import { promisify } from 'node:util';
7
+ const execFileAsync = promisify(execFile);
8
+ async function findPackageRoot(filePath) {
9
+ let currentDir = path.dirname(filePath);
10
+ while (true) {
11
+ try {
12
+ await readFile(path.join(currentDir, 'package.json'));
13
+ return currentDir;
14
+ }
15
+ catch {
16
+ const parentDir = path.dirname(currentDir);
17
+ if (parentDir === currentDir)
18
+ throw new Error(`No package.json found for ${filePath}`);
19
+ currentDir = parentDir;
20
+ }
21
+ }
22
+ }
23
+ export async function resolveExternalDependencies(moduleDir, externals) {
24
+ const moduleRequire = createRequire(path.join(moduleDir, 'package.json'));
25
+ const dependencies = {};
26
+ for (const external of externals) {
27
+ const packageRoot = await findPackageRoot(moduleRequire.resolve(external));
28
+ const packageJson = JSON.parse(await readFile(path.join(packageRoot, 'package.json'), 'utf8'));
29
+ if (typeof packageJson.name !== 'string' || typeof packageJson.version !== 'string') {
30
+ throw new Error(`External ${external} has invalid package metadata`);
31
+ }
32
+ dependencies[packageJson.name] = packageJson.version;
33
+ }
34
+ return dependencies;
35
+ }
36
+ export async function withInstalledExternalTree(dependencies, inspect) {
37
+ const stagingDir = await mkdtemp(path.join(tmpdir(), 'companion-license-check-'));
38
+ try {
39
+ await writeFile(path.join(stagingDir, 'package.json'), JSON.stringify({
40
+ private: true,
41
+ dependencies,
42
+ resolutions: { 'node-gyp': 'npm:empty-npm-package@1.0.0' },
43
+ }));
44
+ await writeFile(path.join(stagingDir, 'yarn.lock'), '');
45
+ await execFileAsync('yarn', ['--cwd', stagingDir, 'install', '--no-immutable']);
46
+ return await inspect(path.join(stagingDir, 'node_modules'));
47
+ }
48
+ finally {
49
+ await rm(stagingDir, { recursive: true, force: true });
50
+ }
51
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Packages which ship no license declaration in their package.json, but whose license has been confirmed by reading
3
+ * what they publish. Only add an entry after checking that version yourself, and record where the license was found.
4
+ *
5
+ * Keys are exact `name@version`, as a later release can change license. A declared license always wins over this list,
6
+ * so an entry can never hide what a package says about itself.
7
+ */
8
+ export declare const KNOWN_PACKAGE_LICENSES: Record<string, string>;
9
+ export declare function knownPackageLicense(name: string | undefined, version: string | undefined): string | undefined;
10
+ /**
11
+ * Packages which declare a license that is not a valid SPDX expression, mapped to what the license text they publish
12
+ * actually is. Unlike KNOWN_PACKAGE_LICENSES this overrides what a package says about itself, so it is only consulted
13
+ * when the declaration cannot be parsed, and can never turn a real license into a more convenient one.
14
+ *
15
+ * Only add an entry after reading the license text shipped in that exact version, and record what identified it.
16
+ * Keys are exact `name@version`, so a later release declaring something different is unaffected.
17
+ */
18
+ export declare const CORRECTED_PACKAGE_LICENSES: Record<string, string>;
19
+ export declare function correctedPackageLicense(name: string | undefined, version: string | undefined): string | undefined;