@jojomatik/nuxt-bundle 2.0.0-beta.3 → 2.0.0-beta.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -3,5 +3,41 @@
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
+
31
+ Use an object in `allowedLicenses` to allow a license only for specific dependencies:
32
+
33
+ ```ts
34
+ licenseModule: {
35
+ allowedLicenses: [
36
+ "MIT",
37
+ { "SEE LICENSE IN LICENSE.md": ["apexcharts", "vue-apexcharts"] },
38
+ ],
39
+ }
40
+ ```
41
+
6
42
  ## Licensing
7
43
  This project is licensed under the MIT License (MIT). See also [`LICENSE`](LICENSE).
@@ -0,0 +1,21 @@
1
+ import { CLicenseCard } from "@crystal-creations/crystal-components";
2
+
3
+ export default {
4
+ title: "Molecules/CLicenseCard",
5
+ component: CLicenseCard,
6
+ args: {
7
+ softwareInfo: {
8
+ name: "Example package",
9
+ version: "1.2.3",
10
+ author: "Example Author",
11
+ repositoryLink: "https://github.com/example/example-package",
12
+ notice: `MIT License
13
+
14
+ Copyright (c) 2026 Example Author
15
+
16
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the Software), to deal in the Software without restriction.`,
17
+ },
18
+ },
19
+ };
20
+
21
+ export const Default = {};
@@ -68,8 +68,8 @@ function softwareInfo() {
68
68
  // 1. Remove `[...]github.com` prefix with protocol etc., if present.
69
69
  // 2. Remove `.git` suffix, if present.
70
70
  const repositoryCoordinates = repositoryLink
71
- .replace(/.+?(github.com\/)/, "")
72
- .replace(".git", "");
71
+ .replace(/^(?:.*?github\.com\/)?/, "")
72
+ .replace(/\.git$/, "");
73
73
 
74
74
  // 3. Prepend `https://github.com/`.
75
75
  repositoryLink = "https://github.com/" + repositoryCoordinates;
@@ -0,0 +1,104 @@
1
+ import {
2
+ applyLicenseOverrides,
3
+ validateDependencyLicense,
4
+ type LicenseOverride,
5
+ } from "./license-validation.ts";
6
+
7
+ type AllowedLicense = string | Record<string, string[]>;
8
+
9
+ type LicenseModuleOptions = {
10
+ allowedLicenses: AllowedLicense[];
11
+ licenseOverrides?: Record<string, LicenseOverride>;
12
+ };
13
+
14
+ type LicenseLogger = {
15
+ error(message: string): void;
16
+ info(message: string): void;
17
+ };
18
+
19
+ type LicenseDependency = {
20
+ name: string;
21
+ license?: string | null;
22
+ };
23
+
24
+ const envKey = "ALLOWED_LICENCES";
25
+
26
+ export function createLicensePluginOptions(
27
+ options: LicenseModuleOptions,
28
+ logger: LicenseLogger,
29
+ ) {
30
+ return {
31
+ thirdParty: {
32
+ includePrivate: false,
33
+ allow: {
34
+ test: (dependency: LicenseDependency) => {
35
+ const allowedLicensesFromEnv = process.env[envKey]
36
+ ?.split(",")
37
+ .map((license) => license.trim());
38
+ const allowedLicenses =
39
+ allowedLicensesFromEnv ?? options.allowedLicenses;
40
+ const dependencyAllowedLicenses = allowedLicenses.flatMap(
41
+ (allowedLicense) => {
42
+ if (typeof allowedLicense === "string") return [allowedLicense];
43
+
44
+ return Object.entries(allowedLicense)
45
+ .filter(([, dependencies]) =>
46
+ dependencies.includes(dependency.name),
47
+ )
48
+ .map(([license]) => license);
49
+ },
50
+ );
51
+ const overrides = options.licenseOverrides ?? {};
52
+ const validation = validateDependencyLicense(
53
+ dependency,
54
+ overrides,
55
+ dependencyAllowedLicenses,
56
+ );
57
+
58
+ if (!validation.license) return false;
59
+
60
+ if (!validation.isAllowed) {
61
+ logger.error(
62
+ `Dependency "${dependency.name}" has a license (${validation.license}) that is not in the dependency-specific list of allowed licenses (${dependencyAllowedLicenses.join(", ")}).`,
63
+ );
64
+
65
+ const requiredLicenses = [...allowedLicenses, validation.license];
66
+ const configContent = JSON.stringify(
67
+ {
68
+ extends: "@jojomatik/nuxt-bundle",
69
+ licenseModule: {
70
+ allowedLicenses: requiredLicenses,
71
+ },
72
+ },
73
+ undefined,
74
+ 2,
75
+ );
76
+ const configExample =
77
+ "export default defineNuxtConfig(" + configContent + ");\n";
78
+
79
+ logger.info(
80
+ `You can specify allowed licenses in "nuxt.config.ts" environment variable:`,
81
+ );
82
+ console.log(configExample);
83
+
84
+ logger.info(
85
+ `You can also specify allowed licenses via "${envKey}" environment variable (ALLOWED_LICENCES=${requiredLicenses.join(",")}).`,
86
+ );
87
+ }
88
+
89
+ return validation.isAllowed;
90
+ },
91
+ failOnUnlicensed: true,
92
+ failOnViolation: true,
93
+ },
94
+ output: {
95
+ file: "public/dependencies.json",
96
+ template(dependencies: LicenseDependency[]) {
97
+ return JSON.stringify(
98
+ applyLicenseOverrides(dependencies, options.licenseOverrides ?? {}),
99
+ );
100
+ },
101
+ },
102
+ },
103
+ };
104
+ }
@@ -0,0 +1,44 @@
1
+ type LicenseDependency = {
2
+ name: string;
3
+ license?: string | null;
4
+ };
5
+
6
+ export type LicenseOverride =
7
+ string | ((license: string | undefined) => string | undefined);
8
+
9
+ function resolveLicense(
10
+ dependency: LicenseDependency,
11
+ licenseOverrides: Record<string, LicenseOverride>,
12
+ ) {
13
+ const originalLicense = dependency.license ?? undefined;
14
+ const override = licenseOverrides[dependency.name];
15
+
16
+ if (!Object.hasOwn(licenseOverrides, dependency.name)) {
17
+ return originalLicense;
18
+ }
19
+
20
+ return typeof override === "function" ? override(originalLicense) : override;
21
+ }
22
+
23
+ export function validateDependencyLicense(
24
+ dependency: LicenseDependency,
25
+ licenseOverrides: Record<string, LicenseOverride>,
26
+ allowedLicenses: string[],
27
+ ) {
28
+ const license = resolveLicense(dependency, licenseOverrides);
29
+
30
+ return {
31
+ license,
32
+ isAllowed: Boolean(license && allowedLicenses.includes(license)),
33
+ };
34
+ }
35
+
36
+ export function applyLicenseOverrides(
37
+ dependencies: LicenseDependency[],
38
+ licenseOverrides: Record<string, LicenseOverride>,
39
+ ) {
40
+ return dependencies.map((dependency) => ({
41
+ ...dependency,
42
+ license: resolveLicense(dependency, licenseOverrides),
43
+ }));
44
+ }
@@ -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/nuxt.config.ts CHANGED
@@ -25,6 +25,8 @@ const isDevServer =
25
25
  process.argv.some((arg) => arg.endsWith("nuxi")) &&
26
26
  process.argv.includes("dev");
27
27
 
28
+ const isStorybook = process.argv.some((arg) => arg.includes("storybook"));
29
+
28
30
  export default defineNuxtConfig({
29
31
  app: {
30
32
  pageTransition: { name: "page", mode: "out-in" },
@@ -41,6 +43,11 @@ export default defineNuxtConfig({
41
43
  href: "/assets/fonts/roboto/index.css",
42
44
  },
43
45
  // Favicon generated with https://realfavicongenerator.net/
46
+ {
47
+ rel: "icon",
48
+ type: "image/vnd.microsoft.icon",
49
+ href: "/assets/favicon/favicon.ico",
50
+ },
44
51
  {
45
52
  rel: "icon",
46
53
  type: "image/png",
@@ -82,7 +89,7 @@ export default defineNuxtConfig({
82
89
  "vuetify-nuxt-module",
83
90
  ],
84
91
  licenseModule: {
85
- allowedLicenses: ["MIT", "Apache-2.0"],
92
+ allowedLicenses: ["MIT", "Apache-2.0", "ISC", "OFL-1.1"],
86
93
  },
87
94
  i18n: {
88
95
  locales: getLocales(),
@@ -96,6 +103,10 @@ export default defineNuxtConfig({
96
103
  storybook: {
97
104
  enabled: !isDevServer,
98
105
  },
106
+ // Storybook's Nuxt preset disables experimental.appManifest, which nuxt-skew-protection requires.
107
+ skewProtection: {
108
+ enabled: !isStorybook,
109
+ },
99
110
  hooks: {
100
111
  "nitro:build:before": () => {
101
112
  const fontsDir = "public/assets/fonts/";
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "@jojomatik/nuxt-bundle",
3
- "version": "2.0.0-beta.3",
3
+ "version": "2.0.0-beta.30",
4
4
  "type": "module",
5
+ "packageManager": "yarn@1.22.22",
5
6
  "description": "A bundle of commonly used nuxt dependencies and configuration.",
6
7
  "keywords": [
7
8
  "nuxt",
@@ -11,62 +12,74 @@
11
12
  "configuration"
12
13
  ],
13
14
  "scripts": {
15
+ "prepare": "nuxi prepare",
14
16
  "dev": "nuxi dev",
15
17
  "build": "nuxi build",
16
18
  "start": "node .output/server/index.mjs",
19
+ "storybook": "storybook dev -p 6006 --disable-telemetry",
20
+ "build-storybook": "storybook build --disable-telemetry",
17
21
  "lint": "eslint . --max-warnings 0",
22
+ "test": "vitest run --coverage",
23
+ "test:release-notes": "node scripts/check-release-notes.ts",
18
24
  "semantic-release": "semantic-release"
19
25
  },
20
26
  "files": [
21
27
  "app",
28
+ "lib",
22
29
  "locales",
23
30
  "modules",
24
31
  "nuxt.config.ts",
25
32
  "package.json",
26
- "README.md"
33
+ "README.md",
34
+ "vuetify-options.ts"
27
35
  ],
28
36
  "main": "nuxt.config.ts",
29
37
  "dependencies": {
30
- "@crystal-creations/crystal-components": "1.0.0-beta.43",
31
- "@fontsource/roboto": "5.2.9",
38
+ "@crystal-creations/crystal-components": "1.0.0-beta.49",
39
+ "@fontsource/roboto": "5.3.0",
32
40
  "@mdi/js": "7.4.47",
33
- "@nuxt/eslint": "^1.12.1",
34
- "@nuxt/image": "2.0.0",
35
- "@nuxtjs/i18n": "10.2.1",
36
- "@nuxtjs/seo": "3.3.0",
37
- "@nuxtjs/storybook": "9.0.1",
41
+ "@nuxt/eslint": "1.17.0",
42
+ "@nuxt/image": "2.2.0",
43
+ "@nuxtjs/i18n": "10.6.0",
44
+ "@nuxtjs/seo": "6.0.1",
45
+ "@nuxtjs/storybook": "10.0.3",
38
46
  "@semantic-release/exec": "7.1.0",
39
- "@semantic-release/git": "10.0.1",
40
- "@storybook/addon-links": "9.1.17",
41
- "@storybook/addon-docs": "9.1.17",
42
- "@types/node": "24.10.4",
43
- "chromatic": "13.3.4",
44
- "conventional-changelog-conventionalcommits": "9.1.0",
47
+ "@semantic-release/git": "11.0.1",
48
+ "@storybook/addon-docs": "10.6.5",
49
+ "@storybook/addon-links": "10.6.5",
50
+ "@types/node": "26.6.5",
51
+ "chromatic": "18.13.3",
52
+ "conventional-changelog-conventionalcommits": "9.3.1",
45
53
  "eslint-config-prettier": "10.1.8",
46
- "eslint-plugin-prettier": "5.5.4",
47
- "eslint-plugin-storybook": "9.1.17",
48
- "eslint-plugin-vue": "10.6.2",
49
- "prettier": "3.7.4",
50
- "rollup": "4.55.1",
51
- "rollup-plugin-license": "3.6.0",
52
- "sass": "1.97.1",
53
- "semantic-release": "25.0.2",
54
- "storybook": "9.1.17",
55
- "typescript": "5.9.3",
56
- "vue-router": "4.6.4",
57
- "vuetify": "3.11.6",
58
- "vuetify-nuxt-module": "0.19.2"
54
+ "eslint-plugin-prettier": "5.5.7",
55
+ "eslint-plugin-storybook": "10.6.5",
56
+ "eslint-plugin-vue": "10.12.0",
57
+ "prettier": "3.9.10",
58
+ "rollup": "4.66.0",
59
+ "rollup-plugin-license": "3.7.1",
60
+ "sass": "1.106.1",
61
+ "semantic-release": "25.0.9",
62
+ "storybook": "10.6.5",
63
+ "typescript": "6.0.3",
64
+ "vue-router": "5.4.0",
65
+ "vuetify": "3.13.7",
66
+ "vuetify-nuxt-module": "1.0.0-rc.6"
59
67
  },
60
68
  "devDependencies": {
61
- "eslint": "9.39.2",
62
- "nuxt": "4.2.2",
63
- "vite": "7.3.0",
64
- "vue": "3.5.26"
69
+ "@vitejs/plugin-vue": "6.0.10",
70
+ "@vitest/coverage-v8": "5.0.5",
71
+ "@vue/test-utils": "2.5.1",
72
+ "eslint": "10.13.0",
73
+ "happy-dom": "21.0.4",
74
+ "nuxt": "4.6.3",
75
+ "vite": "8.3.5",
76
+ "vitest": "5.0.5",
77
+ "vue": "3.5.44"
65
78
  },
66
79
  "peerDependencies": {
67
- "eslint": "^9.39.2",
68
- "nuxt": "^4.2.2",
69
- "vite": "^7.3.0",
80
+ "eslint": "^9.39.2 || ^10.0.0",
81
+ "nuxt": "^4.6.1",
82
+ "vite": "^7.3.0 || ^8.0.0",
70
83
  "vue": "^3.5.26"
71
84
  },
72
85
  "author": {
@@ -0,0 +1,48 @@
1
+ import type { VOptions } from "vuetify-nuxt-module";
2
+
3
+ const vuetifyOptions: VOptions = {
4
+ theme: {
5
+ defaultTheme: "light",
6
+ themes: {
7
+ light: {
8
+ dark: false,
9
+ colors: {
10
+ background: "#EDEDED",
11
+ surface: "#FCFCFC",
12
+ "surface-variant": "#424242",
13
+ "on-surface-variant": "#EEEEEE",
14
+ primary: "#004c6d",
15
+ "primary-darken-1": "#3700B3",
16
+ secondary: "#037a7a",
17
+ "secondary-darken-1": "#018786",
18
+ error: "#B00020",
19
+ info: "#2196F3",
20
+ success: "#4CAF50",
21
+ warning: "#FB8C00",
22
+ },
23
+ variables: {},
24
+ },
25
+ dark: {
26
+ dark: true,
27
+ colors: {
28
+ background: "#121212",
29
+ surface: "#212121",
30
+ "surface-variant": "#BDBDBD",
31
+ "on-surface-variant": "#424242",
32
+ primary: "#0074a5",
33
+ secondary: "#039e9e",
34
+ error: "#CF6679",
35
+ info: "#2196F3",
36
+ success: "#4CAF50",
37
+ warning: "#FB8C00",
38
+ },
39
+ variables: {},
40
+ },
41
+ },
42
+ },
43
+ icons: {
44
+ defaultSet: "mdi-svg",
45
+ },
46
+ };
47
+
48
+ export default vuetifyOptions;