@jojomatik/nuxt-bundle 2.0.0-beta.6 → 2.0.0-beta.7
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 +25 -0
- package/lib/__tests__/license-validation.test.ts +116 -0
- package/lib/license-plugin-options.ts +91 -0
- package/lib/license-validation.ts +45 -0
- package/modules/license-module.ts +2 -73
- package/package.json +14 -9
package/README.md
CHANGED
|
@@ -3,5 +3,30 @@
|
|
|
3
3
|
|
|
4
4
|
A bundle of commonly used nuxt dependencies and configuration.
|
|
5
5
|
|
|
6
|
+
## Usage
|
|
7
|
+
Extend your Nuxt configuration with `@jojomatik/nuxt-bundle` to use its shared dependencies and configuration. Dependency licenses are validated during builds.
|
|
8
|
+
|
|
9
|
+
Use `licenseModule.licenseOverrides` to provide license metadata for dependencies whose manifests omit it. Overrides are still checked against `licenseModule.allowedLicenses`:
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
export default defineNuxtConfig({
|
|
13
|
+
extends: "@jojomatik/nuxt-bundle",
|
|
14
|
+
licenseModule: {
|
|
15
|
+
licenseOverrides: {
|
|
16
|
+
"socket.io-client": "MIT",
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Dependencies without an override still fail validation when their license metadata is missing.
|
|
23
|
+
An override can also be a function that receives the license from the package metadata. For example, to supply MIT only when the license is missing:
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
licenseOverrides: {
|
|
27
|
+
"socket.io-client": (license) => license || "MIT",
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
6
31
|
## Licensing
|
|
7
32
|
This project is licensed under the MIT License (MIT). See also [`LICENSE`](LICENSE).
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { test } from "node:test";
|
|
3
|
+
import { createLicensePluginOptions } from "../license-plugin-options.ts";
|
|
4
|
+
import {
|
|
5
|
+
applyLicenseOverrides,
|
|
6
|
+
validateDependencyLicense,
|
|
7
|
+
} from "../license-validation.ts";
|
|
8
|
+
|
|
9
|
+
test("applies a package license override before validation", () => {
|
|
10
|
+
assert.deepEqual(
|
|
11
|
+
validateDependencyLicense(
|
|
12
|
+
{ name: "socket.io-client" },
|
|
13
|
+
{ "socket.io-client": "MIT" },
|
|
14
|
+
["MIT"],
|
|
15
|
+
),
|
|
16
|
+
{ license: "MIT", isAllowed: true },
|
|
17
|
+
);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test("rejects missing license metadata without an override", () => {
|
|
21
|
+
assert.deepEqual(
|
|
22
|
+
validateDependencyLicense({ name: "unlicensed-package" }, {}, ["MIT"]),
|
|
23
|
+
{ license: undefined, isAllowed: false },
|
|
24
|
+
);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test("still rejects an overridden license outside the allow-list", () => {
|
|
28
|
+
assert.deepEqual(
|
|
29
|
+
validateDependencyLicense(
|
|
30
|
+
{ name: "socket.io-client" },
|
|
31
|
+
{ "socket.io-client": "MIT" },
|
|
32
|
+
["Apache-2.0"],
|
|
33
|
+
),
|
|
34
|
+
{ license: "MIT", isAllowed: false },
|
|
35
|
+
);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test("applies overrides to generated metadata without mutating dependencies", () => {
|
|
39
|
+
const dependency = { name: "socket.io-client" };
|
|
40
|
+
|
|
41
|
+
assert.deepEqual(
|
|
42
|
+
applyLicenseOverrides([dependency], { "socket.io-client": "MIT" }),
|
|
43
|
+
[{ name: "socket.io-client", license: "MIT" }],
|
|
44
|
+
);
|
|
45
|
+
assert.equal(dependency.license, undefined);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test("allows an override callback to supply a license only when missing", () => {
|
|
49
|
+
const licenseOverrides = {
|
|
50
|
+
"socket.io-client": (license: string | undefined) => license || "MIT",
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
assert.deepEqual(
|
|
54
|
+
validateDependencyLicense({ name: "socket.io-client" }, licenseOverrides, [
|
|
55
|
+
"MIT",
|
|
56
|
+
]),
|
|
57
|
+
{ license: "MIT", isAllowed: true },
|
|
58
|
+
);
|
|
59
|
+
assert.deepEqual(
|
|
60
|
+
validateDependencyLicense(
|
|
61
|
+
{ name: "socket.io-client", license: "GPL-3.0" },
|
|
62
|
+
licenseOverrides,
|
|
63
|
+
["MIT"],
|
|
64
|
+
),
|
|
65
|
+
{ license: "GPL-3.0", isAllowed: false },
|
|
66
|
+
);
|
|
67
|
+
assert.deepEqual(
|
|
68
|
+
applyLicenseOverrides(
|
|
69
|
+
[{ name: "socket.io-client", license: "GPL-3.0" }],
|
|
70
|
+
licenseOverrides,
|
|
71
|
+
),
|
|
72
|
+
[{ name: "socket.io-client", license: "GPL-3.0" }],
|
|
73
|
+
);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test("normalizes null license metadata before invoking an override callback", () => {
|
|
77
|
+
let callbackLicense: string | undefined = "not called";
|
|
78
|
+
const licenseOverrides = {
|
|
79
|
+
"socket.io-client": (license: string | undefined) => {
|
|
80
|
+
callbackLicense = license;
|
|
81
|
+
return license || "MIT";
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
assert.deepEqual(
|
|
86
|
+
validateDependencyLicense(
|
|
87
|
+
{ name: "socket.io-client", license: null },
|
|
88
|
+
licenseOverrides,
|
|
89
|
+
["MIT"],
|
|
90
|
+
),
|
|
91
|
+
{ license: "MIT", isAllowed: true },
|
|
92
|
+
);
|
|
93
|
+
assert.equal(callbackLicense, undefined);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test("applies license overrides in configured plugin callbacks", () => {
|
|
97
|
+
const pluginOptions = createLicensePluginOptions(
|
|
98
|
+
{
|
|
99
|
+
allowedLicenses: ["MIT"],
|
|
100
|
+
licenseOverrides: {
|
|
101
|
+
"socket.io-client": (license) => license ?? "MIT",
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
error: assert.fail,
|
|
106
|
+
info: assert.fail,
|
|
107
|
+
},
|
|
108
|
+
);
|
|
109
|
+
const dependency = { name: "socket.io-client", license: null };
|
|
110
|
+
|
|
111
|
+
assert.equal(pluginOptions.thirdParty.allow.test(dependency), true);
|
|
112
|
+
assert.deepEqual(
|
|
113
|
+
JSON.parse(pluginOptions.thirdParty.output.template([dependency])),
|
|
114
|
+
[{ name: "socket.io-client", license: "MIT" }],
|
|
115
|
+
);
|
|
116
|
+
});
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import {
|
|
2
|
+
applyLicenseOverrides,
|
|
3
|
+
validateDependencyLicense,
|
|
4
|
+
type LicenseOverride,
|
|
5
|
+
} from "./license-validation.ts";
|
|
6
|
+
|
|
7
|
+
type LicenseModuleOptions = {
|
|
8
|
+
allowedLicenses: string[];
|
|
9
|
+
licenseOverrides?: Record<string, LicenseOverride>;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
type LicenseLogger = {
|
|
13
|
+
error(message: string): void;
|
|
14
|
+
info(message: string): void;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
type LicenseDependency = {
|
|
18
|
+
name: string;
|
|
19
|
+
license?: string | null;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const envKey = "ALLOWED_LICENCES";
|
|
23
|
+
|
|
24
|
+
export function createLicensePluginOptions(
|
|
25
|
+
options: LicenseModuleOptions,
|
|
26
|
+
logger: LicenseLogger,
|
|
27
|
+
) {
|
|
28
|
+
return {
|
|
29
|
+
thirdParty: {
|
|
30
|
+
includePrivate: false,
|
|
31
|
+
allow: {
|
|
32
|
+
test: (dependency: LicenseDependency) => {
|
|
33
|
+
const allowedLicensesFromEnv = process.env[envKey]
|
|
34
|
+
?.split(",")
|
|
35
|
+
.map((license) => license.trim());
|
|
36
|
+
const allowedLicenses =
|
|
37
|
+
allowedLicensesFromEnv ?? options.allowedLicenses;
|
|
38
|
+
const overrides = options.licenseOverrides ?? {};
|
|
39
|
+
const validation = validateDependencyLicense(
|
|
40
|
+
dependency,
|
|
41
|
+
overrides,
|
|
42
|
+
allowedLicenses,
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
if (!validation.license) return false;
|
|
46
|
+
|
|
47
|
+
if (!validation.isAllowed) {
|
|
48
|
+
logger.error(
|
|
49
|
+
`Dependency "${dependency.name}" has a license (${validation.license}) that is not the list of allowed licenses (${allowedLicenses.join(", ")}).`,
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
const requiredLicenses = [...allowedLicenses, validation.license];
|
|
53
|
+
const configContent = JSON.stringify(
|
|
54
|
+
{
|
|
55
|
+
extends: "@jojomatik/nuxt-bundle",
|
|
56
|
+
licenseModule: {
|
|
57
|
+
allowedLicenses: requiredLicenses,
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
undefined,
|
|
61
|
+
2,
|
|
62
|
+
);
|
|
63
|
+
const configExample =
|
|
64
|
+
"export default defineNuxtConfig(" + configContent + ");\n";
|
|
65
|
+
|
|
66
|
+
logger.info(
|
|
67
|
+
`You can specify allowed licenses in "nuxt.config.ts" environment variable:`,
|
|
68
|
+
);
|
|
69
|
+
console.log(configExample);
|
|
70
|
+
|
|
71
|
+
logger.info(
|
|
72
|
+
`You can also specify allowed licenses via "${envKey}" environment variable (ALLOWED_LICENCES=${requiredLicenses.join(",")}).`,
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return validation.isAllowed;
|
|
77
|
+
},
|
|
78
|
+
failOnUnlicensed: true,
|
|
79
|
+
failOnViolation: true,
|
|
80
|
+
},
|
|
81
|
+
output: {
|
|
82
|
+
file: "public/dependencies.json",
|
|
83
|
+
template(dependencies: LicenseDependency[]) {
|
|
84
|
+
return JSON.stringify(
|
|
85
|
+
applyLicenseOverrides(dependencies, options.licenseOverrides ?? {}),
|
|
86
|
+
);
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
type LicenseDependency = {
|
|
2
|
+
name: string;
|
|
3
|
+
license?: string | null;
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
export type LicenseOverride =
|
|
7
|
+
| string
|
|
8
|
+
| ((license: string | undefined) => string | undefined);
|
|
9
|
+
|
|
10
|
+
function resolveLicense(
|
|
11
|
+
dependency: LicenseDependency,
|
|
12
|
+
licenseOverrides: Record<string, LicenseOverride>,
|
|
13
|
+
) {
|
|
14
|
+
const originalLicense = dependency.license ?? undefined;
|
|
15
|
+
const override = licenseOverrides[dependency.name];
|
|
16
|
+
|
|
17
|
+
if (!Object.hasOwn(licenseOverrides, dependency.name)) {
|
|
18
|
+
return originalLicense;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return typeof override === "function" ? override(originalLicense) : override;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function validateDependencyLicense(
|
|
25
|
+
dependency: LicenseDependency,
|
|
26
|
+
licenseOverrides: Record<string, LicenseOverride>,
|
|
27
|
+
allowedLicenses: string[],
|
|
28
|
+
) {
|
|
29
|
+
const license = resolveLicense(dependency, licenseOverrides);
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
license,
|
|
33
|
+
isAllowed: Boolean(license && allowedLicenses.includes(license)),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function applyLicenseOverrides(
|
|
38
|
+
dependencies: LicenseDependency[],
|
|
39
|
+
licenseOverrides: Record<string, LicenseOverride>,
|
|
40
|
+
) {
|
|
41
|
+
return dependencies.map((dependency) => ({
|
|
42
|
+
...dependency,
|
|
43
|
+
license: resolveLicense(dependency, licenseOverrides),
|
|
44
|
+
}));
|
|
45
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { addVitePlugin, defineNuxtModule, useLogger } from "nuxt/kit";
|
|
2
2
|
import license from "rollup-plugin-license";
|
|
3
|
+
import { createLicensePluginOptions } from "../lib/license-plugin-options";
|
|
3
4
|
|
|
4
5
|
const moduleKey = "@jojomatik/nuxt-bundle/license-module";
|
|
5
|
-
const envKey = "ALLOWED_LICENCES";
|
|
6
6
|
|
|
7
7
|
const logger = useLogger(moduleKey);
|
|
8
8
|
|
|
@@ -12,77 +12,6 @@ export default defineNuxtModule({
|
|
|
12
12
|
configKey: "licenseModule",
|
|
13
13
|
},
|
|
14
14
|
setup(options) {
|
|
15
|
-
addVitePlugin(
|
|
16
|
-
license({
|
|
17
|
-
thirdParty: {
|
|
18
|
-
includePrivate: false,
|
|
19
|
-
allow: {
|
|
20
|
-
test: (dependency) => {
|
|
21
|
-
// Return false for unlicensed dependencies.
|
|
22
|
-
if (!dependency.license) return false;
|
|
23
|
-
|
|
24
|
-
// Load allowed licenses from environment-variable
|
|
25
|
-
const allowedLicensesFromEnv = process.env[envKey]
|
|
26
|
-
?.split(",")
|
|
27
|
-
.map((license) => license.trim());
|
|
28
|
-
|
|
29
|
-
// Load allowed licenses from module options
|
|
30
|
-
// Allow MIT and Apache-2.0 licenses by default
|
|
31
|
-
const allowedLicenses =
|
|
32
|
-
allowedLicensesFromEnv ?? options.allowedLicenses;
|
|
33
|
-
|
|
34
|
-
const isLicenseAllowed = allowedLicenses.includes(
|
|
35
|
-
dependency.license,
|
|
36
|
-
);
|
|
37
|
-
|
|
38
|
-
if (!isLicenseAllowed) {
|
|
39
|
-
logger.error(
|
|
40
|
-
`Dependency "${dependency.name}" has a license (${dependency.license}) that is not the list of allowed licenses (${allowedLicenses.join(", ")}).`,
|
|
41
|
-
);
|
|
42
|
-
|
|
43
|
-
const requiredLicenses = [
|
|
44
|
-
...allowedLicenses,
|
|
45
|
-
dependency.license,
|
|
46
|
-
];
|
|
47
|
-
|
|
48
|
-
const configContent = JSON.stringify(
|
|
49
|
-
{
|
|
50
|
-
extends: "@jojomatik/nuxt-bundle",
|
|
51
|
-
licenseModule: {
|
|
52
|
-
allowedLicenses: requiredLicenses,
|
|
53
|
-
},
|
|
54
|
-
},
|
|
55
|
-
undefined,
|
|
56
|
-
2,
|
|
57
|
-
);
|
|
58
|
-
|
|
59
|
-
const configExample =
|
|
60
|
-
"export default defineNuxtConfig(" + configContent + ");\n";
|
|
61
|
-
|
|
62
|
-
logger.info(
|
|
63
|
-
`You can specify allowed licenses in "nuxt.config.ts" environment variable:`,
|
|
64
|
-
);
|
|
65
|
-
console.log(configExample);
|
|
66
|
-
|
|
67
|
-
logger.info(
|
|
68
|
-
`You can also specify allowed licenses via "${envKey}" environment variable (ALLOWED_LICENCES=${requiredLicenses.join(",")}).`,
|
|
69
|
-
);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
return isLicenseAllowed;
|
|
73
|
-
},
|
|
74
|
-
failOnUnlicensed: true,
|
|
75
|
-
failOnViolation: true,
|
|
76
|
-
},
|
|
77
|
-
output: {
|
|
78
|
-
// Output file into public directory which is included in the build output.
|
|
79
|
-
file: "public/dependencies.json",
|
|
80
|
-
template(dependencies) {
|
|
81
|
-
return JSON.stringify(dependencies);
|
|
82
|
-
},
|
|
83
|
-
},
|
|
84
|
-
},
|
|
85
|
-
}),
|
|
86
|
-
);
|
|
15
|
+
addVitePlugin(license(createLicensePluginOptions(options, logger)));
|
|
87
16
|
},
|
|
88
17
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jojomatik/nuxt-bundle",
|
|
3
|
-
"version": "2.0.0-beta.
|
|
3
|
+
"version": "2.0.0-beta.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A bundle of commonly used nuxt dependencies and configuration.",
|
|
6
6
|
"keywords": [
|
|
@@ -15,10 +15,12 @@
|
|
|
15
15
|
"build": "nuxi build",
|
|
16
16
|
"start": "node .output/server/index.mjs",
|
|
17
17
|
"lint": "eslint . --max-warnings 0",
|
|
18
|
+
"test": "node --experimental-strip-types --test lib/__tests__/*.test.ts",
|
|
18
19
|
"semantic-release": "semantic-release"
|
|
19
20
|
},
|
|
20
21
|
"files": [
|
|
21
22
|
"app",
|
|
23
|
+
"lib",
|
|
22
24
|
"locales",
|
|
23
25
|
"modules",
|
|
24
26
|
"nuxt.config.ts",
|
|
@@ -28,40 +30,40 @@
|
|
|
28
30
|
],
|
|
29
31
|
"main": "nuxt.config.ts",
|
|
30
32
|
"dependencies": {
|
|
31
|
-
"@crystal-creations/crystal-components": "1.0.0-beta.
|
|
33
|
+
"@crystal-creations/crystal-components": "1.0.0-beta.49",
|
|
32
34
|
"@fontsource/roboto": "5.2.9",
|
|
33
35
|
"@mdi/js": "7.4.47",
|
|
34
36
|
"@nuxt/eslint": "1.12.1",
|
|
35
37
|
"@nuxt/image": "2.0.0",
|
|
36
38
|
"@nuxtjs/i18n": "10.2.1",
|
|
37
|
-
"@nuxtjs/seo": "
|
|
39
|
+
"@nuxtjs/seo": "5.3.16",
|
|
38
40
|
"@nuxtjs/storybook": "9.0.1",
|
|
39
41
|
"@semantic-release/exec": "7.1.0",
|
|
40
42
|
"@semantic-release/git": "10.0.1",
|
|
41
43
|
"@storybook/addon-links": "9.1.17",
|
|
42
44
|
"@storybook/addon-docs": "9.1.17",
|
|
43
45
|
"@types/node": "24.10.4",
|
|
44
|
-
"chromatic": "13.3.
|
|
46
|
+
"chromatic": "13.3.5",
|
|
45
47
|
"conventional-changelog-conventionalcommits": "9.1.0",
|
|
46
48
|
"eslint-config-prettier": "10.1.8",
|
|
47
49
|
"eslint-plugin-prettier": "5.5.4",
|
|
48
50
|
"eslint-plugin-storybook": "9.1.17",
|
|
49
51
|
"eslint-plugin-vue": "10.6.2",
|
|
50
52
|
"prettier": "3.7.4",
|
|
51
|
-
"rollup": "4.
|
|
53
|
+
"rollup": "4.59.0",
|
|
52
54
|
"rollup-plugin-license": "3.6.0",
|
|
53
55
|
"sass": "1.97.1",
|
|
54
56
|
"semantic-release": "25.0.2",
|
|
55
|
-
"storybook": "9.1.
|
|
57
|
+
"storybook": "9.1.19",
|
|
56
58
|
"typescript": "5.9.3",
|
|
57
59
|
"vue-router": "4.6.4",
|
|
58
60
|
"vuetify": "3.11.6",
|
|
59
|
-
"vuetify-nuxt-module": "0.19.
|
|
61
|
+
"vuetify-nuxt-module": "0.19.5"
|
|
60
62
|
},
|
|
61
63
|
"devDependencies": {
|
|
62
64
|
"eslint": "9.39.2",
|
|
63
|
-
"nuxt": "4.
|
|
64
|
-
"vite": "7.3.
|
|
65
|
+
"nuxt": "4.5.1",
|
|
66
|
+
"vite": "7.3.5",
|
|
65
67
|
"vue": "3.5.26"
|
|
66
68
|
},
|
|
67
69
|
"peerDependencies": {
|
|
@@ -86,5 +88,8 @@
|
|
|
86
88
|
},
|
|
87
89
|
"publishConfig": {
|
|
88
90
|
"access": "public"
|
|
91
|
+
},
|
|
92
|
+
"resolutions": {
|
|
93
|
+
"jiti": "2.6.1"
|
|
89
94
|
}
|
|
90
95
|
}
|