@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 +10 -0
- package/LICENSE +21 -0
- package/README.md +25 -3
- package/dist/scripts/build-connection.js +15 -2
- package/dist/scripts/build-surface.js +16 -3
- package/dist/scripts/check-connection.js +21 -9
- package/dist/scripts/check-surface.js +25 -10
- package/dist/scripts/lib/build-util.d.ts +9 -3
- package/dist/scripts/lib/build-util.js +30 -49
- package/dist/scripts/lib/bundle-util.d.ts +11 -0
- package/dist/scripts/lib/bundle-util.js +49 -0
- package/dist/scripts/lib/check-util.d.ts +6 -0
- package/dist/scripts/lib/check-util.js +14 -0
- package/dist/scripts/lib/cli-options.d.ts +1 -0
- package/dist/scripts/lib/cli-options.js +4 -0
- package/dist/scripts/lib/external-install-util.d.ts +2 -0
- package/dist/scripts/lib/external-install-util.js +51 -0
- package/dist/scripts/lib/known-package-licenses.d.ts +19 -0
- package/dist/scripts/lib/known-package-licenses.js +78 -0
- package/dist/scripts/lib/license-util.d.ts +55 -0
- package/dist/scripts/lib/license-util.js +427 -0
- package/dist/scripts/lib/license-warning-util.d.ts +27 -0
- package/dist/scripts/lib/license-warning-util.js +371 -0
- package/package.json +7 -3
- package/tsconfig/node22/recommended-esm.json +5 -20
- package/tsconfig/node22/recommended.json +5 -20
- package/tsconfig/node26/recommended.json +26 -0
- package/tsconfig/shared.json +24 -0
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
|
|
25
|
-
2
|
|
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
|
-
|
|
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
|
-
|
|
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-
|
|
10
|
-
console.log('Builds the companion
|
|
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
|
-
|
|
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
|
|
5
|
-
import { findModuleDir
|
|
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
|
-
|
|
13
|
-
const toolsDir = await findModuleDir(
|
|
14
|
-
const frameworkDir = await findModuleDir(
|
|
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
|
|
28
|
+
await checkPackage({ validateManifest, ignoreLicenseRules: ignoreLicenseRules(argv) });
|
|
21
29
|
}
|
|
22
30
|
catch (e) {
|
|
23
|
-
|
|
24
|
-
|
|
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
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
26
|
+
await checkPackage({
|
|
27
|
+
validateManifest: validateSurfaceManifest,
|
|
28
|
+
ignoreLicenseRules: ignoreLicenseRules(argv),
|
|
29
|
+
});
|
|
19
30
|
}
|
|
20
31
|
catch (e) {
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
|
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
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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
|
-
|
|
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,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,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;
|