From da7437c023331229650668d7a5ca9f00a4a902eb Mon Sep 17 00:00:00 2001 From: Timon Date: Thu, 26 Feb 2026 11:12:28 +0000 Subject: [PATCH] Reimplement notice file generation for third-party licenses through Rust, now with CEF credits (#3808) --- .github/workflows/build-win-bundle.yml | 1 + .github/workflows/provide-shaders.yml | 2 +- .gitignore | 1 + .nix/deps/cef.nix | 4 +- .nix/deps/rust-gpu.nix | 2 +- .nix/dev.nix | 60 +- .nix/flake.lock | 34 - .nix/flake.nix | 197 ++---- .nix/pkgs/graphite-branding.nix | 20 + .nix/pkgs/graphite-bundle.nix | 13 +- .nix/pkgs/graphite-flatpak-manifest.nix | 6 +- ....nix => graphite-raster-nodes-shaders.nix} | 12 +- .nix/pkgs/graphite.nix | 120 ++-- .nix/pkgs/tools/third-party-licenses.nix | 31 + Cargo.lock | 263 +++++++- Cargo.toml | 4 + about.hbs | 27 - desktop/Cargo.toml | 3 +- desktop/src/app.rs | 13 + .../src/handle_desktop_wrapper_message.rs | 4 + .../wrapper/src/intercept_frontend_message.rs | 3 + desktop/wrapper/src/messages.rs | 4 + frontend/package-lock.json | 620 +++++++++++++++--- frontend/package.json | 16 +- frontend/src/state-providers/dialog.ts | 1 - frontend/vite.config.ts | 452 ++----------- node-graph/graph-craft/Cargo.toml | 1 + node-graph/interpreted-executor/Cargo.toml | 2 +- node-graph/libraries/wgpu-executor/Cargo.toml | 1 + node-graph/nodes/graphic/Cargo.toml | 1 + node-graph/preprocessor/Cargo.toml | 1 + package.json | 6 +- tools/third-party-licenses/.gitignore | 1 + tools/third-party-licenses/Cargo.toml | 19 + tools/third-party-licenses/build.rs | 15 + tools/third-party-licenses/src/cargo.rs | 106 +++ tools/third-party-licenses/src/cef.rs | 105 +++ tools/third-party-licenses/src/main.rs | 229 +++++++ tools/third-party-licenses/src/npm.rs | 93 +++ .../volunteer/guide/project-setup/_index.md | 13 +- 40 files changed, 1729 insertions(+), 777 deletions(-) create mode 100644 .nix/pkgs/graphite-branding.nix rename .nix/pkgs/{raster-nodes-shaders.nix => graphite-raster-nodes-shaders.nix} (78%) create mode 100644 .nix/pkgs/tools/third-party-licenses.nix delete mode 100644 about.hbs create mode 100644 tools/third-party-licenses/.gitignore create mode 100644 tools/third-party-licenses/Cargo.toml create mode 100644 tools/third-party-licenses/build.rs create mode 100644 tools/third-party-licenses/src/cargo.rs create mode 100644 tools/third-party-licenses/src/cef.rs create mode 100644 tools/third-party-licenses/src/main.rs create mode 100644 tools/third-party-licenses/src/npm.rs diff --git a/.github/workflows/build-win-bundle.yml b/.github/workflows/build-win-bundle.yml index 2ba73fb5..ee72140d 100644 --- a/.github/workflows/build-win-bundle.yml +++ b/.github/workflows/build-win-bundle.yml @@ -70,6 +70,7 @@ jobs: cargo binstall --no-confirm --force "wasm-bindgen-cli@$env:WASM_BINDGEN_CLI_VERSION" - name: Build Windows Bundle + shell: bash # `cargo-about` refuses to run in powershell env: CARGO_TERM_COLOR: always run: npm run build-desktop diff --git a/.github/workflows/provide-shaders.yml b/.github/workflows/provide-shaders.yml index 25ae7246..7e38ecf3 100644 --- a/.github/workflows/provide-shaders.yml +++ b/.github/workflows/provide-shaders.yml @@ -17,7 +17,7 @@ jobs: - uses: DeterminateSystems/magic-nix-cache-action@main - name: Build graphene raster nodes shaders - run: nix build .nix#raster-nodes-shaders && cp result raster_nodes_shaders_entrypoint.wgsl + run: nix build .nix#graphite-raster-nodes-shaders && cp result raster_nodes_shaders_entrypoint.wgsl - name: Upload graphene raster nodes shaders to artifacts repository run: | diff --git a/.gitignore b/.gitignore index d6cc8a57..06bcee04 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ branding/ target/ +third-party-licenses.txt* result/ .flatpak-builder/ *.spv diff --git a/.nix/deps/cef.nix b/.nix/deps/cef.nix index 04b60560..c0ce387c 100644 --- a/.nix/deps/cef.nix +++ b/.nix/deps/cef.nix @@ -1,4 +1,4 @@ -{ pkgs, inputs, ... }: +{ pkgs, ... }: let cefPath = pkgs.cef-binary.overrideAttrs (finalAttrs: { @@ -10,6 +10,8 @@ let mv ./Resources/* $out/ mv ./include $out/ + cat ./CREDITS.html | ${pkgs.xz}/bin/xz -9 -e -c > $out/CREDITS.html.xz + echo '${ builtins.toJSON { type = "minimal"; diff --git a/.nix/deps/rust-gpu.nix b/.nix/deps/rust-gpu.nix index 2970d515..a44f70b3 100644 --- a/.nix/deps/rust-gpu.nix +++ b/.nix/deps/rust-gpu.nix @@ -1,4 +1,4 @@ -{ pkgs, inputs, ... }: +{ pkgs, ... }: let extensions = [ diff --git a/.nix/dev.nix b/.nix/dev.nix index d80d7793..e9333d49 100644 --- a/.nix/dev.nix +++ b/.nix/dev.nix @@ -1,16 +1,58 @@ -{ - pkgs, - deps, - libs, - tools, - ... -}: +{ pkgs, deps, ... }: +let + libs = [ + pkgs.wayland + pkgs.vulkan-loader + pkgs.libGL + pkgs.openssl + pkgs.libraw + + # X11 Support + pkgs.libxkbcommon + pkgs.libXcursor + pkgs.libxcb + pkgs.libX11 + ]; +in pkgs.mkShell ( { - packages = tools.all ++ libs.all; + packages = libs ++ [ + pkgs.pkg-config - LD_LIBRARY_PATH = "${pkgs.lib.makeLibraryPath libs.all}:${deps.cef.env.CEF_PATH}"; + pkgs.lld + pkgs.nodejs + pkgs.nodePackages.npm + pkgs.binaryen + pkgs.wasm-bindgen-cli_0_2_100 + pkgs.wasm-pack + pkgs.cargo-about + + pkgs.rustc + pkgs.cargo + pkgs.rust-analyzer + pkgs.clippy + pkgs.rustfmt + + pkgs.git + + pkgs.cargo-watch + pkgs.cargo-nextest + pkgs.cargo-expand + + # Linker + pkgs.mold + + # Profiling tools + pkgs.gnuplot + pkgs.samply + pkgs.cargo-flamegraph + + # Plotting tools + pkgs.graphviz + ]; + + LD_LIBRARY_PATH = "${pkgs.lib.makeLibraryPath libs}:${deps.cef.env.CEF_PATH}"; XDG_DATA_DIRS = "${pkgs.gsettings-desktop-schemas}/share/gsettings-schemas/${pkgs.gsettings-desktop-schemas.name}:${pkgs.gtk3}/share/gsettings-schemas/${pkgs.gtk3.name}:$XDG_DATA_DIRS"; shellHook = '' diff --git a/.nix/flake.lock b/.nix/flake.lock index 03cb965e..7bb7e2f2 100644 --- a/.nix/flake.lock +++ b/.nix/flake.lock @@ -29,24 +29,6 @@ "url": "https://flakehub.com/f/edolstra/flake-compat/1.tar.gz" } }, - "flake-utils": { - "inputs": { - "systems": "systems" - }, - "locked": { - "lastModified": 1731533236, - "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, "nixpkgs": { "locked": { "lastModified": 1770197578, @@ -67,7 +49,6 @@ "inputs": { "crane": "crane", "flake-compat": "flake-compat", - "flake-utils": "flake-utils", "nixpkgs": "nixpkgs", "rust-overlay": "rust-overlay" } @@ -91,21 +72,6 @@ "repo": "rust-overlay", "type": "github" } - }, - "systems": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } } }, "root": "root", diff --git a/.nix/flake.nix b/.nix/flake.nix index 5a1f4193..270bb870 100644 --- a/.nix/flake.nix +++ b/.nix/flake.nix @@ -18,7 +18,6 @@ url = "github:oxalica/rust-overlay"; inputs.nixpkgs.follows = "nixpkgs"; }; - flake-utils.url = "github:numtide/flake-utils"; crane.url = "github:ipetkov/crane"; # This is used to provide a identical development shell at `shell.nix` for users that do not use flakes @@ -27,143 +26,85 @@ outputs = inputs: - inputs.flake-utils.lib.eachDefaultSystem ( - system: + ( let - info = { - pname = "graphite"; - version = "unstable"; - src = pkgs.lib.cleanSourceWith { - src = ./..; - filter = path: type: !(type == "directory" && builtins.baseNameOf path == ".nix"); - }; - }; + systems = [ + "x86_64-linux" + "aarch64-linux" + ]; + forAllSystems = f: inputs.nixpkgs.lib.genAttrs systems (system: f system); + args = + system: + ( + let + lib = inputs.nixpkgs.lib // { + call = p: import p args; + }; - pkgs = import inputs.nixpkgs { - inherit system; - overlays = [ (import inputs.rust-overlay) ]; - }; + pkgs = import inputs.nixpkgs { + inherit system; + overlays = [ (import inputs.rust-overlay) ]; + }; - deps = { - crane = import ./deps/crane.nix { inherit pkgs inputs; }; - cef = import ./deps/cef.nix { inherit pkgs inputs; }; - rustGPU = import ./deps/rust-gpu.nix { inherit pkgs inputs; }; - }; + info = { + pname = "graphite"; + version = "unstable"; + src = inputs.nixpkgs.lib.cleanSourceWith { + src = ./..; + filter = path: type: !(type == "directory" && builtins.baseNameOf path == ".nix"); + }; + cargoVendored = deps.crane.lib.vendorCargoDeps { inherit (info) src; }; + }; - libs = rec { - desktop = [ - pkgs.wayland - pkgs.openssl - pkgs.vulkan-loader - pkgs.libraw - pkgs.libGL - ]; - desktop-x11 = [ - pkgs.libxkbcommon - pkgs.xorg.libXcursor - pkgs.xorg.libxcb - pkgs.xorg.libX11 - ]; - desktop-all = desktop ++ desktop-x11; - all = desktop-all; - }; + deps = { + crane = lib.call ./deps/crane.nix; + cef = lib.call ./deps/cef.nix; + rustGPU = lib.call ./deps/rust-gpu.nix; + }; - tools = rec { - desktop = [ - pkgs.pkg-config - ]; - frontend = [ - pkgs.lld - pkgs.nodejs - pkgs.nodePackages.npm - pkgs.binaryen - pkgs.wasm-bindgen-cli_0_2_100 - pkgs.wasm-pack - pkgs.cargo-about - ]; - dev = [ - pkgs.rustc - pkgs.cargo - pkgs.rust-analyzer - pkgs.clippy - pkgs.rustfmt - - pkgs.git - - pkgs.cargo-watch - pkgs.cargo-nextest - pkgs.cargo-expand - - # Linker - pkgs.mold - - # Profiling tools - pkgs.gnuplot - pkgs.samply - pkgs.cargo-flamegraph - - # Plotting tools - pkgs.graphviz - ]; - all = desktop ++ frontend ++ dev; - }; + args = { + inherit system; + inherit (inputs) self; + inherit inputs; + inherit pkgs; + inherit lib; + inherit info; + inherit deps; + } + // inputs; + in + args + ); + withArgs = f: forAllSystems (system: f (args system)); in { - packages = rec { - graphiteWithArgs = - args: - (import ./pkgs/graphite.nix { - pkgs = pkgs // { - inherit raster-nodes-shaders; - }; - inherit - info - inputs - deps - libs - tools - ; - }) - args; - graphite = graphiteWithArgs { }; - graphite-dev = graphiteWithArgs { dev = true; }; - graphite-without-resources = graphiteWithArgs { embeddedResources = false; }; - graphite-without-resources-dev = graphiteWithArgs { - embeddedResources = false; - dev = true; - }; - graphite-bundle = import ./pkgs/graphite-bundle.nix { - inherit pkgs graphite; - }; - graphite-flatpak-manifest = import ./pkgs/graphite-flatpak-manifest.nix { - inherit pkgs; - archive = graphite-bundle.tar; - }; - #TODO: graphene-cli = import ./pkgs/graphene-cli.nix { inherit info pkgs inputs deps libs tools; }; - raster-nodes-shaders = import ./pkgs/raster-nodes-shaders.nix { - inherit - info - pkgs - inputs - deps - libs - tools - ; - }; + packages = withArgs ( + { lib, ... }: + rec { + default = graphite; + graphite = (lib.call ./pkgs/graphite.nix) { }; + graphite-dev = (lib.call ./pkgs/graphite.nix) { dev = true; }; + graphite-raster-nodes-shaders = lib.call ./pkgs/graphite-raster-nodes-shaders.nix; + graphite-branding = lib.call ./pkgs/graphite-branding.nix; + graphite-bundle = lib.call ./pkgs/graphite-bundle.nix; + graphite-flatpak-manifest = lib.call ./pkgs/graphite-flatpak-manifest.nix; - default = graphite; - }; + # TODO: graphene-cli = lib.call ./pkgs/graphene-cli.nix; - devShells.default = import ./dev.nix { - inherit - pkgs - deps - libs - tools - ; - }; + tools = { + third-party-licenses = lib.call ./pkgs/tools/third-party-licenses.nix; + }; + } + ); - formatter = pkgs.nixfmt-tree; + devShells = withArgs ( + { lib, ... }: + { + default = lib.call ./dev.nix; + } + ); + + formatter = withArgs ({ pkgs, ... }: pkgs.nixfmt-tree); } ); } diff --git a/.nix/pkgs/graphite-branding.nix b/.nix/pkgs/graphite-branding.nix new file mode 100644 index 00000000..85b53791 --- /dev/null +++ b/.nix/pkgs/graphite-branding.nix @@ -0,0 +1,20 @@ +{ info, pkgs, ... }: + +let + brandingTar = pkgs.fetchurl ( + let + lockContent = builtins.readFile "${info.src}/.branding"; + lines = builtins.filter (s: s != [ ]) (builtins.split "\n" lockContent); + url = builtins.elemAt lines 0; + hash = builtins.elemAt lines 1; + in + { + url = url; + sha256 = hash; + } + ); +in +pkgs.runCommand "${info.pname}-branding" { } '' + mkdir -p $out + tar -xvf ${brandingTar} -C $out --strip-components 1 +'' diff --git a/.nix/pkgs/graphite-bundle.nix b/.nix/pkgs/graphite-bundle.nix index 815fd1e5..d50b00fc 100644 --- a/.nix/pkgs/graphite-bundle.nix +++ b/.nix/pkgs/graphite-bundle.nix @@ -1,18 +1,19 @@ { pkgs, - graphite, + self, + system, + ... }: let bundle = { - pkgs, - graphite, archive ? false, compression ? null, - passthru ? {}, + passthru ? { }, }: ( let + graphite = self.packages.${system}.graphite; tar = if compression == null then archive else true; nameArchiveSuffix = if tar then ".tar" else ""; nameCompressionSuffix = if compression == null then "" else "." + compression; @@ -75,18 +76,14 @@ let ); in bundle { - inherit pkgs graphite; passthru = { tar = bundle { - inherit pkgs graphite; archive = true; passthru = { gz = bundle { - inherit pkgs graphite; compression = "gz"; }; xz = bundle { - inherit pkgs graphite; compression = "xz"; }; }; diff --git a/.nix/pkgs/graphite-flatpak-manifest.nix b/.nix/pkgs/graphite-flatpak-manifest.nix index 03d7146f..fee66031 100644 --- a/.nix/pkgs/graphite-flatpak-manifest.nix +++ b/.nix/pkgs/graphite-flatpak-manifest.nix @@ -1,6 +1,8 @@ { pkgs, - archive, + self, + system, + ... }: (pkgs.formats.json { }).generate "art.graphite.Graphite.json" { @@ -28,7 +30,7 @@ sources = [ { type = "archive"; - path = archive; + path = self.packages.${system}.graphite-bundle.tar; strip-components = 0; } ]; diff --git a/.nix/pkgs/raster-nodes-shaders.nix b/.nix/pkgs/graphite-raster-nodes-shaders.nix similarity index 78% rename from .nix/pkgs/raster-nodes-shaders.nix rename to .nix/pkgs/graphite-raster-nodes-shaders.nix index 46e2f90a..727e8c95 100644 --- a/.nix/pkgs/raster-nodes-shaders.nix +++ b/.nix/pkgs/graphite-raster-nodes-shaders.nix @@ -1,12 +1,4 @@ -{ - info, - pkgs, - inputs, - deps, - libs, - tools, - ... -}: +{ info, deps, ... }: (deps.crane.lib.overrideToolchain (_: deps.rustGPU.toolchain)).buildPackage { pname = "raster-nodes-shaders"; @@ -16,7 +8,7 @@ inherit (deps.crane.lib.findCargoFiles (deps.crane.lib.cleanCargoSource info.src)) cargoConfigs; cargoLockList = [ "${info.src}/Cargo.lock" - "${deps.rustGPU.toolchain.passthru.availableComponents.rust-src}/lib/rustlib/src/rust/library/Cargo.lock" + "${deps.rustGPU.toolchain.availableComponents.rust-src}/lib/rustlib/src/rust/library/Cargo.lock" ]; }; diff --git a/.nix/pkgs/graphite.nix b/.nix/pkgs/graphite.nix index 6ffb0144..bd757add 100644 --- a/.nix/pkgs/graphite.nix +++ b/.nix/pkgs/graphite.nix @@ -1,42 +1,36 @@ { info, pkgs, - inputs, + self, deps, - libs, - tools, + system, + lib, ... }: { - embeddedResources ? true, dev ? false, }: let - brandingTar = pkgs.fetchurl ( - let - lockContent = builtins.readFile "${info.src}/.branding"; - lines = builtins.filter (s: s != [ ]) (builtins.split "\n" lockContent); - url = builtins.elemAt lines 0; - hash = builtins.elemAt lines 1; - in - { - url = url; - sha256 = hash; - } - ); - branding = pkgs.runCommand "${info.pname}-branding" { } '' - mkdir -p $out - tar -xvf ${brandingTar} -C $out --strip-components 1 - ''; - cargoVendorDir = deps.crane.lib.vendorCargoDeps { inherit (info) src; }; + branding = self.packages.${system}.graphite-branding; + cargoVendorDir = deps.crane.lib.vendorCargoDeps { inherit (info) src; }; resourcesCommon = { pname = "${info.pname}-resources"; inherit (info) version src; inherit cargoVendorDir; strictDeps = true; - nativeBuildInputs = tools.frontend; + nativeBuildInputs = [ + pkgs.pkg-config + pkgs.lld + pkgs.nodejs + pkgs.nodePackages.npm + pkgs.binaryen + pkgs.wasm-bindgen-cli_0_2_100 + pkgs.wasm-pack + pkgs.cargo-about + ]; + buildInputs = [ pkgs.openssl ]; env.CARGO_PROFILE = if dev then "dev" else "release"; cargoExtraArgs = "--target wasm32-unknown-unknown -p graphite-wasm --no-default-features --features native"; doCheck = false; @@ -54,7 +48,11 @@ let npmConfigScript = "setup"; makeCacheWritable = true; - nativeBuildInputs = tools.frontend ++ [ pkgs.importNpmLock.npmConfigHook pkgs.removeReferencesTo ]; + nativeBuildInputs = [ + pkgs.importNpmLock.npmConfigHook + pkgs.removeReferencesTo + ] + ++ resourcesCommon.nativeBuildInputs; prePatch = '' mkdir branding @@ -80,18 +78,33 @@ let ''; } ); + libs = [ + pkgs.wayland + pkgs.vulkan-loader + pkgs.libGL + pkgs.openssl + pkgs.libraw + + # X11 Support + pkgs.libxkbcommon + pkgs.libXcursor + pkgs.libxcb + pkgs.libX11 + ]; common = { inherit (info) pname version src; inherit cargoVendorDir; strictDeps = true; - buildInputs = libs.desktop-all; - nativeBuildInputs = tools.desktop ++ [ pkgs.makeWrapper pkgs.removeReferencesTo ]; + buildInputs = libs; + nativeBuildInputs = [ + pkgs.pkg-config + pkgs.cargo-about + pkgs.removeReferencesTo + ]; env = deps.cef.env // { CARGO_PROFILE = if dev then "dev" else "release"; }; - cargoExtraArgs = "-p graphite-desktop${ - if embeddedResources then "" else " --no-default-features --features recommended" - }"; + cargoExtraArgs = "-p graphite-desktop"; doCheck = false; }; in @@ -101,31 +114,34 @@ deps.crane.lib.buildPackage ( // { cargoArtifacts = deps.crane.lib.buildDepsOnly common; - env = - common.env - // { - RASTER_NODES_SHADER_PATH = pkgs.raster-nodes-shaders; - } - // ( - if embeddedResources then - { - EMBEDDED_RESOURCES = resources; - } - else - { } - ) // { - GRAPHITE_GIT_COMMIT_HASH = inputs.self.rev or "unknown"; - GRAPHITE_GIT_COMMIT_DATE = inputs.self.lastModified or "unknown"; - }; + env = common.env // { + RASTER_NODES_SHADER_PATH = self.packages.${system}.graphite-raster-nodes-shaders; + EMBEDDED_RESOURCES = resources; + GRAPHITE_GIT_COMMIT_HASH = self.rev or "unknown"; + GRAPHITE_GIT_COMMIT_DATE = self.lastModified or "unknown"; + }; - postUnpack = '' - mkdir ./branding - cp -r ${branding}/* ./branding - ''; + npmDeps = pkgs.importNpmLock { + npmRoot = "${info.src}/frontend"; + }; + npmRoot = "frontend"; + nativeBuildInputs = [ + pkgs.importNpmLock.npmConfigHook + pkgs.nodePackages.npm + ] + ++ common.nativeBuildInputs; - preBuild = if inputs.self ? rev then '' - export GRAPHITE_GIT_COMMIT_DATE="$(date -u -d "@$GRAPHITE_GIT_COMMIT_DATE" +"%Y-%m-%dT%H:%M:%SZ")" - '' else ""; + preBuild = '' + ${lib.getExe self.packages.${system}.tools.third-party-licenses} + '' + + ( + if self ? rev then + '' + export GRAPHITE_GIT_COMMIT_DATE="$(date -u -d "@$GRAPHITE_GIT_COMMIT_DATE" +"%Y-%m-%dT%H:%M:%SZ")" + '' + else + "" + ); installPhase = '' mkdir -p $out/bin @@ -148,7 +164,7 @@ deps.crane.lib.buildPackage ( remove-references-to -t "${cargoVendorDir}" $out/bin/graphite patchelf \ - --set-rpath "${pkgs.lib.makeLibraryPath libs.desktop-all}:${deps.cef.env.CEF_PATH}" \ + --set-rpath "${pkgs.lib.makeLibraryPath libs}:${deps.cef.env.CEF_PATH}" \ --add-needed libGL.so \ $out/bin/graphite ''; diff --git a/.nix/pkgs/tools/third-party-licenses.nix b/.nix/pkgs/tools/third-party-licenses.nix new file mode 100644 index 00000000..95596033 --- /dev/null +++ b/.nix/pkgs/tools/third-party-licenses.nix @@ -0,0 +1,31 @@ +{ + info, + deps, + pkgs, + ... +}: + +let + cargoVendorDir = deps.crane.lib.vendorCargoDeps { inherit (info) src; }; + common = { + pname = "third-party-licenses"; + inherit (info) version src; + inherit cargoVendorDir; + nativeBuildInputs = [ pkgs.pkg-config ]; + buildInputs = [ pkgs.openssl ]; + strictDeps = true; + env = deps.cef.env // { + CARGO_PROFILE = "dev"; + }; + cargoExtraArgs = "-p third-party-licenses --features desktop"; + doCheck = false; + }; +in +deps.crane.lib.buildPackage ( + common + // { + inherit cargoVendorDir; + cargoArtifacts = deps.crane.lib.buildDepsOnly common; + meta.mainProgram = "third-party-licenses"; + } +) diff --git a/Cargo.lock b/Cargo.lock index 6b08a3a2..6bf8977a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1302,6 +1302,29 @@ dependencies = [ "typenum", ] +[[package]] +name = "cssparser" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dae61cf9c0abb83bd659dab65b7e4e38d8236824c85f0f804f173567bda257d2" +dependencies = [ + "cssparser-macros", + "dtoa-short", + "itoa", + "phf", + "smallvec", +] + +[[package]] +name = "cssparser-macros" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" +dependencies = [ + "quote", + "syn 2.0.106", +] + [[package]] name = "ctor" version = "0.2.9" @@ -1475,8 +1498,7 @@ checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" [[package]] name = "download-cef" version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d98178d9254efef0f69c1f584713d69c790ec00668cd98f783a5085fbefdbddc" +source = "git+https://github.com/timon-schelling/cef-rs.git?branch=graphite#8efeb241d1837447eccaee5d713a7c1ce331cd52" dependencies = [ "bzip2", "clap", @@ -1499,6 +1521,21 @@ dependencies = [ "serde", ] +[[package]] +name = "dtoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c3cf4824e2d5f025c7b531afcb2325364084a16806f6d47fbc1f5fbd9960590" + +[[package]] +name = "dtoa-short" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd1511a7b6a56299bd043a9c167a6d2bfb37bf84a6dfceaba651168adfb43c87" +dependencies = [ + "dtoa", +] + [[package]] name = "dunce" version = "1.0.5" @@ -1531,6 +1568,12 @@ dependencies = [ "graphite-editor", ] +[[package]] +name = "ego-tree" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2972feb8dffe7bc8c5463b1dacda1b0dfbed3710e50f977d965429692d74cd8" + [[package]] name = "either" version = "1.15.0" @@ -1911,6 +1954,16 @@ dependencies = [ "libc", ] +[[package]] +name = "futf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" +dependencies = [ + "mac", + "new_debug_unreachable", +] + [[package]] name = "futures" version = "0.3.31" @@ -2044,6 +2097,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "getopts" +version = "0.2.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe4fbac503b8d1f88e6676011885f34b7174f46e59956bba534ba83abded4df" +dependencies = [ + "unicode-width", +] + [[package]] name = "getrandom" version = "0.2.16" @@ -2365,6 +2427,7 @@ dependencies = [ "glam", "graphite-desktop-embedded-resources", "graphite-desktop-wrapper", + "lzma-rust2", "muda", "objc2 0.6.3", "objc2-app-kit 0.3.2", @@ -2611,6 +2674,16 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" +[[package]] +name = "html5ever" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6452c4751a24e1b99c3260d505eaeee76a050573e61f30ac2c924ddc7236f01e" +dependencies = [ + "log", + "markup5ever", +] + [[package]] name = "http" version = "1.3.1" @@ -3374,6 +3447,21 @@ dependencies = [ "num-traits", ] +[[package]] +name = "lzma-rust2" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47bb1e988e6fb779cf720ad431242d3f03167c1b3f2b1aae7f1a94b2495b36ae" +dependencies = [ + "sha2", +] + +[[package]] +name = "mac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" + [[package]] name = "malloc_buf" version = "0.0.6" @@ -3383,6 +3471,17 @@ dependencies = [ "libc", ] +[[package]] +name = "markup5ever" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c3294c4d74d0742910f8c7b466f44dda9eb2d5742c1e430138df290a1e8451c" +dependencies = [ + "log", + "tendril", + "web_atoms", +] + [[package]] name = "matchers" version = "0.2.0" @@ -4282,6 +4381,59 @@ dependencies = [ "indexmap", ] +[[package]] +name = "phf" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" +dependencies = [ + "phf_macros", + "phf_shared", + "serde", +] + +[[package]] +name = "phf_codegen" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49aa7f9d80421bca176ca8dbfebe668cc7a2684708594ec9f3c0db0805d5d6e1" +dependencies = [ + "phf_generator", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" +dependencies = [ + "fastrand", + "phf_shared", +] + +[[package]] +name = "phf_macros" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "phf_shared" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" +dependencies = [ + "siphasher", +] + [[package]] name = "pico-args" version = "0.5.0" @@ -4472,6 +4624,12 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + [[package]] name = "preprocessor" version = "0.1.0" @@ -5353,6 +5511,21 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "scraper" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93cecd86d6259499c844440546d02f55f3e17bd286e529e48d1f9f67e92315cb" +dependencies = [ + "cssparser", + "ego-tree", + "getopts", + "html5ever", + "precomputed-hash", + "selectors", + "tendril", +] + [[package]] name = "sctk-adwaita" version = "0.11.0" @@ -5389,6 +5562,25 @@ dependencies = [ "libc", ] +[[package]] +name = "selectors" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "feef350c36147532e1b79ea5c1f3791373e61cbd9a6a2615413b3807bb164fb7" +dependencies = [ + "bitflags 2.11.0", + "cssparser", + "derive_more", + "log", + "new_debug_unreachable", + "phf", + "phf_codegen", + "precomputed-hash", + "rustc-hash 2.1.1", + "servo_arc", + "smallvec", +] + [[package]] name = "semver" version = "1.0.26" @@ -5492,6 +5684,15 @@ dependencies = [ "serde", ] +[[package]] +name = "servo_arc" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "170fb83ab34de17dc69aa7c67482b22218ddb85da56546f9bd6b929e32a05930" +dependencies = [ + "stable_deref_trait", +] + [[package]] name = "sha1_smol" version = "1.0.1" @@ -5809,6 +6010,30 @@ dependencies = [ "float-cmp", ] +[[package]] +name = "string_cache" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a18596f8c785a729f2819c0f6a7eae6ebeebdfffbfe4214ae6b087f690e31901" +dependencies = [ + "new_debug_unreachable", + "parking_lot", + "phf_shared", + "precomputed-hash", +] + +[[package]] +name = "string_cache_codegen" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585635e46db231059f76c5849798146164652513eb9e8ab2685939dd90f29b69" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", +] + [[package]] name = "strsim" version = "0.11.1" @@ -5981,6 +6206,17 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "tendril" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" +dependencies = [ + "futf", + "mac", + "utf-8", +] + [[package]] name = "termcolor" version = "1.4.1" @@ -6005,6 +6241,17 @@ dependencies = [ "vector-types", ] +[[package]] +name = "third-party-licenses" +version = "0.0.0" +dependencies = [ + "cef-dll-sys", + "lzma-rust2", + "scraper", + "serde", + "serde_json", +] + [[package]] name = "thiserror" version = "1.0.69" @@ -6997,6 +7244,18 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web_atoms" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57a9779e9f04d2ac1ce317aee707aa2f6b773afba7b931222bff6983843b1576" +dependencies = [ + "phf", + "phf_codegen", + "string_cache", + "string_cache_codegen", +] + [[package]] name = "webpki-root-certs" version = "1.0.6" diff --git a/Cargo.toml b/Cargo.toml index 4cba7a96..6c8c121b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,6 +41,7 @@ members = [ "node-graph/preprocessor", "proc-macros", "tools/crate-hierarchy-viz", + "tools/third-party-licenses", "tools/editor-message-tree", "tools/node-docs", ] @@ -247,6 +248,8 @@ clap = "4.5" spirv-std = { git = "https://github.com/Firestar99/rust-gpu-new", rev = "c12f216121820580731440ee79ebc7403d6ea04f", features = ["bytemuck"] } cargo-gpu = { git = "https://github.com/Firestar99/cargo-gpu", rev = "3952a22d16edbd38689f3a876e417899f21e1fe7", default-features = false } qrcodegen = "1.8" +lzma-rust2 = { version = "0.16", default-features = false, features = ["std", "encoder", "optimization", "xz"] } +scraper = "0.25" [workspace.lints.rust] unexpected_cfgs = { level = "allow", check-cfg = ['cfg(target_arch, values("spirv"))'] } @@ -277,3 +280,4 @@ debug = true [patch.crates-io] # Force cargo to use only one version of the dpi crate (vendoring breaks without this) dpi = { git = "https://github.com/rust-windowing/winit.git" } +download-cef = { git = "https://github.com/timon-schelling/cef-rs.git", branch = "graphite" } diff --git a/about.hbs b/about.hbs deleted file mode 100644 index e8cb48ac..00000000 --- a/about.hbs +++ /dev/null @@ -1,27 +0,0 @@ -{{! -Be careful to prevent auto-formatting from breaking this file's indentation. -Replace this file with JSON output once this is resolved: https://github.com/EmbarkStudios/cargo-about/issues/73 - -The `GENERATED_BY_CARGO_ABOUT` prefix is a JS labeled statement -() -used so the reader of the generated file can verify the file does indeed start with that string, -while remaining valid JS for subsequent parsing. -}} -GENERATED_BY_CARGO_ABOUT: [ - {{#each licenses}} - { - licenseName: `{{name}}`, - licenseText: `{{text}}`, - packages: [ - {{#each used_by}} - { - name: `{{crate.name}}`, - version: `{{crate.version}}`, - author: `{{crate.authors}}`, - repository: `{{crate.repository}}`, - }, - {{/each}} - ], - }, - {{/each}} -] diff --git a/desktop/Cargo.toml b/desktop/Cargo.toml index 372c9ba7..2dcdd7e2 100644 --- a/desktop/Cargo.toml +++ b/desktop/Cargo.toml @@ -44,8 +44,9 @@ vello = { workspace = true } derivative = { workspace = true } rfd = { workspace = true } open = { workspace = true } -rand = { workspace = true, features = ["thread_rng"] } +lzma-rust2 = { workspace = true } serde = { workspace = true } +rand = { workspace = true, features = ["thread_rng"] } clap = { workspace = true, features = ["derive"] } fd-lock = "4.0.4" ctrlc = "3.5.1" diff --git a/desktop/src/app.rs b/desktop/src/app.rs index 05115885..414e0a40 100644 --- a/desktop/src/app.rs +++ b/desktop/src/app.rs @@ -1,6 +1,7 @@ use rand::Rng; use rfd::AsyncFileDialog; use std::fs; +use std::io::Read; use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc::{Receiver, Sender, SyncSender}; @@ -416,6 +417,18 @@ impl App { DesktopFrontendMessage::Restart => { self.exit(Some(ExitReason::Restart)); } + DesktopFrontendMessage::LoadThirdPartyLicenses => { + let compressed = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/third-party-licenses.txt.xz")); + let mut reader = lzma_rust2::XzReader::new(compressed.as_slice(), false); + let mut text = String::new(); + if let Err(e) = reader.read_to_string(&mut text) { + tracing::error!("Failed to decompress third-party licenses: {e}"); + return; + } + + let message = DesktopWrapperMessage::LoadThirdPartyLicenses { text }; + responses.push(message); + } } } diff --git a/desktop/wrapper/src/handle_desktop_wrapper_message.rs b/desktop/wrapper/src/handle_desktop_wrapper_message.rs index e4efc915..a62fe0b3 100644 --- a/desktop/wrapper/src/handle_desktop_wrapper_message.rs +++ b/desktop/wrapper/src/handle_desktop_wrapper_message.rs @@ -97,5 +97,9 @@ pub(super) fn handle_desktop_wrapper_message(dispatcher: &mut DesktopWrapperMess let message = AppWindowMessage::PointerLockMove { x, y }; dispatcher.queue_editor_message(message); } + DesktopWrapperMessage::LoadThirdPartyLicenses { text } => { + let message = DialogMessage::RequestLicensesThirdPartyDialogWithLicenseText { license_text: text }; + dispatcher.queue_editor_message(message); + } } } diff --git a/desktop/wrapper/src/intercept_frontend_message.rs b/desktop/wrapper/src/intercept_frontend_message.rs index 0d8e6b21..6294bb21 100644 --- a/desktop/wrapper/src/intercept_frontend_message.rs +++ b/desktop/wrapper/src/intercept_frontend_message.rs @@ -154,6 +154,9 @@ pub(super) fn intercept_frontend_message(dispatcher: &mut DesktopWrapperMessageD FrontendMessage::WindowRestart => { dispatcher.respond(DesktopFrontendMessage::Restart); } + FrontendMessage::TriggerDisplayThirdPartyLicensesDialog => { + dispatcher.respond(DesktopFrontendMessage::LoadThirdPartyLicenses); + } m => return Some(m), } None diff --git a/desktop/wrapper/src/messages.rs b/desktop/wrapper/src/messages.rs index 6a7105d5..087c1fde 100644 --- a/desktop/wrapper/src/messages.rs +++ b/desktop/wrapper/src/messages.rs @@ -75,6 +75,7 @@ pub enum DesktopFrontendMessage { WindowHideOthers, WindowShowAll, Restart, + LoadThirdPartyLicenses, } pub enum DesktopWrapperMessage { @@ -126,6 +127,9 @@ pub enum DesktopWrapperMessage { x: f64, y: f64, }, + LoadThirdPartyLicenses { + text: String, + }, } #[derive(Clone, serde::Serialize, serde::Deserialize, Debug)] diff --git a/frontend/package-lock.json b/frontend/package-lock.json index a059b6e5..701c8a08 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -9,6 +9,7 @@ "dependencies": { "class-transformer": "^0.5.1", "idb-keyval": "^6.2.2", + "license-checker-rseidelsohn": "^4.4.2", "reflect-metadata": "^0.2.2", "source-code-pro": "2.42.0", "source-sans-pro": "2.45.0" @@ -611,7 +612,6 @@ "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", @@ -627,7 +627,6 @@ "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@eslint/core": "^0.17.0" }, @@ -641,7 +640,6 @@ "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@types/json-schema": "^7.0.15" }, @@ -718,7 +716,6 @@ "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", "dev": true, "license": "Apache-2.0", - "peer": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } @@ -729,7 +726,6 @@ "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@eslint/core": "^0.17.0", "levn": "^0.4.1" @@ -744,7 +740,6 @@ "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@types/json-schema": "^7.0.15" }, @@ -758,7 +753,6 @@ "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", "dev": true, "license": "Apache-2.0", - "peer": true, "engines": { "node": ">=18.18.0" } @@ -769,7 +763,6 @@ "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.4.0" @@ -784,7 +777,6 @@ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, "license": "Apache-2.0", - "peer": true, "engines": { "node": ">=12.22" }, @@ -799,7 +791,6 @@ "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", "dev": true, "license": "Apache-2.0", - "peer": true, "engines": { "node": ">=18.18" }, @@ -808,6 +799,102 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/@isaacs/fs-minipass": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", @@ -922,6 +1009,30 @@ "node": ">= 8" } }, + "node_modules/@npmcli/fs": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.1.tgz", + "integrity": "sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==", + "license": "ISC", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/fs/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@parcel/watcher": { "version": "2.5.4", "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.4.tgz", @@ -1232,6 +1343,16 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, "node_modules/@pkgr/core": { "version": "0.2.9", "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", @@ -1618,6 +1739,7 @@ "integrity": "sha512-ou/d51QSdTyN26D7h6dSpusAKaZkAiGM55/AKYi+9AGZw7q85hElbjK3kEyzXHhLSnRISHOYzVge6x0jRZ7DXA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^5.0.0", "deepmerge": "^4.3.1", @@ -1717,6 +1839,7 @@ "integrity": "sha512-/rpCXHlCWeqClNBwUhDcusJxXYDjZTyE8v5oTO7WbL8eij2nKhUeU89/6xgjU7N4/Vh3He0BtyhJdQbDyhiXAw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~7.16.0" } @@ -1766,6 +1889,7 @@ "integrity": "sha512-nm3cvFN9SqZGXjmw5bZ6cGmvJSyJPn0wU9gHAZZHDnZl2wF9PhHv78Xf06E0MaNk4zLVHL8hb2/c32XvyJOLQg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.53.1", "@typescript-eslint/types": "8.53.1", @@ -2259,12 +2383,22 @@ "win32" ] }, + "node_modules/abbrev": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", + "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/acorn": { "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2316,7 +2450,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -2326,7 +2459,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -2383,7 +2515,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", "integrity": "sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -2534,7 +2665,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, "license": "MIT" }, "node_modules/base64-js": { @@ -2671,7 +2801,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", @@ -2688,7 +2817,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, "license": "MIT", "dependencies": { "has-flag": "^4.0.0" @@ -2758,7 +2886,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -2771,7 +2898,6 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, "license": "MIT" }, "node_modules/commenting": { @@ -2824,9 +2950,7 @@ "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, "license": "MIT", - "peer": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -2907,7 +3031,6 @@ "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -2926,8 +3049,7 @@ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/deepmerge": { "version": "4.3.1", @@ -3031,11 +3153,16 @@ "node": ">= 0.4" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, "license": "MIT" }, "node_modules/es-abstract": { @@ -3245,7 +3372,6 @@ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -3430,6 +3556,7 @@ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -3595,7 +3722,6 @@ "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@types/json-schema": "^7.0.15" }, @@ -3634,7 +3760,6 @@ "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", "dev": true, "license": "BSD-3-Clause", - "peer": true, "dependencies": { "estraverse": "^5.1.0" }, @@ -3741,8 +3866,7 @@ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/fastq": { "version": "1.20.1", @@ -3778,7 +3902,6 @@ "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "flat-cache": "^4.0.0" }, @@ -3805,7 +3928,6 @@ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -3823,7 +3945,6 @@ "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" @@ -3837,8 +3958,7 @@ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", "dev": true, - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/for-each": { "version": "0.3.5", @@ -3856,6 +3976,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -3875,7 +4011,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -4002,13 +4137,33 @@ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, + "node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "is-glob": "^4.0.3" }, @@ -4023,6 +4178,30 @@ "dev": true, "license": "BSD-2-Clause" }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/globals": { "version": "17.0.0", "resolved": "https://registry.npmjs.org/globals/-/globals-17.0.0.tgz", @@ -4070,7 +4249,7 @@ "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, + "devOptional": true, "license": "ISC" }, "node_modules/has-bigints": { @@ -4090,7 +4269,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -4158,7 +4336,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -4167,6 +4344,18 @@ "node": ">= 0.4" } }, + "node_modules/hosted-git-info": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-6.1.3.tgz", + "integrity": "sha512-HVJyzUrLIL1c0QmviVh5E8VGyUS7xCFPS6yydaVd1UegW+ibV/CohqTH9MkOLDp5o+rb82DMo77PTuc9F/8GKw==", + "license": "ISC", + "dependencies": { + "lru-cache": "^7.5.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/idb-keyval": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-6.2.2.tgz", @@ -4234,7 +4423,6 @@ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=0.8.19" } @@ -4365,7 +4553,6 @@ "version": "2.16.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "dev": true, "license": "MIT", "dependencies": { "hasown": "^2.0.2" @@ -4442,7 +4629,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -4700,9 +4886,22 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC", - "peer": true + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } }, "node_modules/js-yaml": { "version": "4.1.1", @@ -4722,8 +4921,16 @@ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.2.tgz", + "integrity": "sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==", "license": "MIT", - "peer": true + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } }, "node_modules/json-schema-traverse": { "version": "0.4.1", @@ -4737,8 +4944,7 @@ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/json5": { "version": "1.0.2", @@ -4759,7 +4965,6 @@ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "json-buffer": "3.0.1" } @@ -4777,7 +4982,6 @@ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -4786,6 +4990,44 @@ "node": ">= 0.8.0" } }, + "node_modules/license-checker-rseidelsohn": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/license-checker-rseidelsohn/-/license-checker-rseidelsohn-4.4.2.tgz", + "integrity": "sha512-Sf8WaJhd2vELvCne+frS9AXqnY/vv591s2/nZcJDwTnoNgltG4mAmoenffVb8L2YPRYbxARLyrHJBC38AVfpuA==", + "license": "BSD-3-Clause", + "dependencies": { + "chalk": "4.1.2", + "debug": "^4.3.4", + "lodash.clonedeep": "^4.5.0", + "mkdirp": "^1.0.4", + "nopt": "^7.2.0", + "read-installed-packages": "^2.0.1", + "semver": "^7.3.5", + "spdx-correct": "^3.1.1", + "spdx-expression-parse": "^3.0.1", + "spdx-satisfies": "^5.0.1", + "treeify": "^1.1.0" + }, + "bin": { + "license-checker-rseidelsohn": "bin/license-checker-rseidelsohn.js" + }, + "engines": { + "node": ">=18", + "npm": ">=8" + } + }, + "node_modules/license-checker-rseidelsohn/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/lilconfig": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", @@ -4809,7 +5051,6 @@ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "p-locate": "^5.0.0" }, @@ -4827,13 +5068,27 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "license": "ISC", + "engines": { + "node": ">=12" + } }, "node_modules/magic-string": { "version": "0.30.21", @@ -4905,7 +5160,6 @@ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6" } @@ -4951,7 +5205,6 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" @@ -4970,6 +5223,18 @@ "node": ">= 18" } }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/moment": { "version": "2.30.1", "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", @@ -4984,7 +5249,6 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, "license": "MIT" }, "node_modules/nanoid": { @@ -5037,6 +5301,57 @@ "license": "MIT", "optional": true }, + "node_modules/nopt": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", + "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", + "license": "ISC", + "dependencies": { + "abbrev": "^2.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/normalize-package-data": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-5.0.0.tgz", + "integrity": "sha512-h9iPVIfrVZ9wVYQnxFgtw1ugSvGEMOlyPWWtm8BMJhnwyEL/FLbYbTY3V3PpjI/BUK67n9PEWDu6eHzu1fB15Q==", + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^6.0.0", + "is-core-module": "^2.8.1", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/normalize-package-data/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm-normalize-package-bin": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz", + "integrity": "sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==", + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", @@ -5151,7 +5466,6 @@ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", @@ -5188,7 +5502,6 @@ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "yocto-queue": "^0.1.0" }, @@ -5205,7 +5518,6 @@ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "p-limit": "^3.0.2" }, @@ -5216,6 +5528,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, "node_modules/package-name-regex": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/package-name-regex/-/package-name-regex-2.0.6.tgz", @@ -5248,7 +5566,6 @@ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -5257,9 +5574,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -5271,6 +5586,28 @@ "dev": true, "license": "MIT" }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -5321,6 +5658,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -5444,7 +5782,6 @@ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">= 0.8.0" } @@ -5455,6 +5792,7 @@ "integrity": "sha512-yEPsovQfpxYfgWNhCfECjG5AQaO+K3dp6XERmOepyPDVqcJm+bjyCVO3pmU+nAPe0N5dDvekfGezt/EIiRe1TA==", "dev": true, "license": "MIT", + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -5530,6 +5868,41 @@ ], "license": "MIT" }, + "node_modules/read-installed-packages": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/read-installed-packages/-/read-installed-packages-2.0.1.tgz", + "integrity": "sha512-t+fJOFOYaZIjBpTVxiV8Mkt7yQyy4E6MSrrnt5FmPd4enYvpU/9DYGirDmN1XQwkfeuWIhM/iu0t2rm6iSr0CA==", + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^3.1.0", + "debug": "^4.3.4", + "read-package-json": "^6.0.0", + "semver": "2 || 3 || 4 || 5 || 6 || 7", + "slide": "~1.1.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.2" + } + }, + "node_modules/read-package-json": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-6.0.4.tgz", + "integrity": "sha512-AEtWXYfopBj2z5N5PbkAOeNHRPUg5q+Nen7QLxV8M2zJq1ym6/lCz3fYNTCXe19puu2d06jfHhrP7v/S2PtMMw==", + "deprecated": "This package is no longer supported. Please use @npmcli/package-json instead.", + "license": "ISC", + "dependencies": { + "glob": "^10.2.2", + "json-parse-even-better-errors": "^3.0.0", + "normalize-package-data": "^5.0.0", + "npm-normalize-package-bin": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/readdirp": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", @@ -5662,6 +6035,7 @@ "integrity": "sha512-wDv/Ht1BNHB4upNbK74s9usvl7hObDnvVzknxqY/E/O3X6rW1U1rV1aENEfJ54eFZDTNo7zv1f5N4edCluH7+A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/estree": "1.0.8" }, @@ -5819,6 +6193,7 @@ "integrity": "sha512-y5LWb0IlbO4e97Zr7c3mlpabcbBtS+ieiZ9iwDooShpFKWXf62zz5pEPdwrLYm+Bxn1fnbwFGzHuCLSA9tBmrw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "chokidar": "^4.0.0", "immutable": "^5.0.2", @@ -5838,7 +6213,6 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -5897,9 +6271,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "license": "MIT", - "peer": true, "dependencies": { "shebang-regex": "^3.0.0" }, @@ -5911,9 +6283,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -6007,6 +6377,27 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/slide": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", + "integrity": "sha512-NwrtjCg+lZoqhFU8fOwl4ay2ei8PaqCBOUV3/ektPY9trO1yQ1oXEfmHAhKArUVUr/hOHvy5f6AdP17dCM0zMw==", + "license": "ISC", + "engines": { + "node": "*" + } + }, "node_modules/source-code-pro": { "version": "2.42.0", "resolved": "https://registry.npmjs.org/source-code-pro/-/source-code-pro-2.42.0.tgz", @@ -6034,7 +6425,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/spdx-compare/-/spdx-compare-1.0.0.tgz", "integrity": "sha512-C1mDZOX0hnu0ep9dfmuoi03+eOdDoz2yvK79RxbcrVEG1NO1Ph35yW102DHWKN4pk80nwCgeMmSY5L25VE4D9A==", - "dev": true, "license": "MIT", "dependencies": { "array-find-index": "^1.0.2", @@ -6042,18 +6432,26 @@ "spdx-ranges": "^2.0.0" } }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, "node_modules/spdx-exceptions": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", - "dev": true, "license": "CC-BY-3.0" }, "node_modules/spdx-expression-parse": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, "license": "MIT", "dependencies": { "spdx-exceptions": "^2.1.0", @@ -6074,21 +6472,18 @@ "version": "3.0.22", "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.22.tgz", "integrity": "sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==", - "dev": true, "license": "CC0-1.0" }, "node_modules/spdx-ranges": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/spdx-ranges/-/spdx-ranges-2.1.1.tgz", "integrity": "sha512-mcdpQFV7UDAgLpXEE/jOMqvK4LBoO0uTQg0uvXUewmEFhpiZx5yJSZITHB8w1ZahKdhfZqP5GPEOKLyEq5p8XA==", - "dev": true, "license": "(MIT AND CC-BY-3.0)" }, "node_modules/spdx-satisfies": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/spdx-satisfies/-/spdx-satisfies-5.0.1.tgz", "integrity": "sha512-Nwor6W6gzFp8XX4neaKQ7ChV4wmpSh2sSDemMFSzHxpTw460jxFYeOn+jq4ybnSSw/5sc3pjka9MQPouksQNpw==", - "dev": true, "license": "MIT", "dependencies": { "spdx-compare": "^1.0.0", @@ -6124,7 +6519,21 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -6198,7 +6607,19 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -6265,6 +6686,7 @@ "integrity": "sha512-MhSWfWEpG5T57z0Oyfk9D1GhAz/KTZKZZlWtGEsy9zNk2fafpuU7sJQlXNSA8HtvwKxVC9XlDyl5YovXUXjjHA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", @@ -6445,6 +6867,15 @@ "tree-kill": "cli.js" } }, + "node_modules/treeify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/treeify/-/treeify-1.1.0.tgz", + "integrity": "sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, "node_modules/ts-api-utils": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", @@ -6464,6 +6895,7 @@ "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -6528,7 +6960,6 @@ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "prelude-ls": "^1.2.1" }, @@ -6620,6 +7051,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -6685,6 +7117,7 @@ "dev": true, "hasInstallScript": true, "license": "MIT", + "peer": true, "dependencies": { "napi-postinstall": "^0.3.0" }, @@ -6737,12 +7170,23 @@ "dev": true, "license": "MIT" }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, "node_modules/vite": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", @@ -6867,9 +7311,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "license": "ISC", - "peer": true, "dependencies": { "isexe": "^2.0.0" }, @@ -6975,7 +7417,6 @@ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -6998,6 +7439,24 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -7063,7 +7522,6 @@ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, diff --git a/frontend/package.json b/frontend/package.json index e90047f8..6bcc0670 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -21,8 +21,8 @@ "lint-fix": "eslint . --fix && tsc --noEmit", "---------- INTERNAL ----------": "", "setup": "node package-installer.js && node branding-installer.js", - "native:build-dev": "wasm-pack build ./wasm --dev --target=web --no-default-features --features native && vite build --mode dev", - "native:build-production": "wasm-pack build ./wasm --release --target=web --no-default-features --features native && vite build", + "native:build-dev": "wasm-pack build ./wasm --dev --target=web --no-default-features --features native && vite build --mode native", + "native:build-production": "wasm-pack build ./wasm --release --target=web --no-default-features --features native && vite build --mode native", "wasm:build-dev": "wasm-pack build ./wasm --dev --target=web", "wasm:build-profiling": "wasm-pack build ./wasm --profiling --target=web", "wasm:build-production": "wasm-pack build ./wasm --release --target=web", @@ -51,20 +51,20 @@ "eslint-plugin-prettier": "^5.5.5", "eslint-plugin-svelte": "^3.14.0", "globals": "^17.0.0", + "license-checker-rseidelsohn": "^4.4.2", "postcss": "^8.5.6", - "prettier": "^3.8.0", "prettier-plugin-svelte": "^3.4.1", + "prettier": "^3.8.0", "process": "^0.11.10", - "rollup-plugin-license": "^3.6.0", "sass": "^1.97.2", - "svelte": "5.47.1", "svelte-preprocess": "^6.0.3", + "svelte": "5.47.1", "tar": "^7.5.4", "ts-node": "^10.9.2", - "typescript": "^5.9.3", "typescript-eslint": "^8.53.1", - "vite": "^7.3.1", - "vite-multiple-assets": "2.2.6" + "typescript": "^5.9.3", + "vite-multiple-assets": "2.2.6", + "vite": "^7.3.1" }, "homepage": "https://graphite.art", "license": "Apache-2.0", diff --git a/frontend/src/state-providers/dialog.ts b/frontend/src/state-providers/dialog.ts index 82b070b1..0dd03171 100644 --- a/frontend/src/state-providers/dialog.ts +++ b/frontend/src/state-providers/dialog.ts @@ -81,7 +81,6 @@ export function createDialogState(editor: Editor) { editor.subscriptions.subscribeJsMessage(TriggerDisplayThirdPartyLicensesDialog, async () => { const BACKUP_URL = "https://editor.graphite.art/third-party-licenses.txt"; let licenseText = `Content was not able to load. Please check your network connection and try again.\n\nOr visit ${BACKUP_URL} for the license notices.`; - if (editor.handle.inDevelopmentMode()) licenseText = `Third-party licenses are not available in development builds.\n\nVisit ${BACKUP_URL} for the license notices.`; const response = await fetch("/third-party-licenses.txt"); if (response.ok && response.headers.get("Content-Type")?.includes("text/plain")) licenseText = await response.text(); diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 7a48829a..a585981e 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -1,14 +1,10 @@ -/* eslint-disable no-console */ - -import { spawnSync } from "child_process"; -import fs from "fs"; -import os from "os"; +import { execSync } from "child_process"; +import { readFileSync } from "fs"; import path from "path"; import { svelte } from "@sveltejs/vite-plugin-svelte"; -import rollupPluginLicense, { type Dependency } from "rollup-plugin-license"; import { sveltePreprocess } from "svelte-preprocess"; -import { defineConfig } from "vite"; +import { defineConfig, type PluginOption } from "vite"; import { DynamicPublicDirectory as viteMultipleAssets } from "vite-multiple-assets"; const projectRootDir = path.resolve(__dirname); @@ -16,36 +12,7 @@ const projectRootDir = path.resolve(__dirname); // https://vitejs.dev/config/ export default defineConfig(({ mode }) => { return { - plugins: [ - svelte({ - preprocess: [sveltePreprocess()], - onwarn(warning, defaultHandler) { - const suppressed = [ - "css-unused-selector", // NOTICE: Keep this list in sync with the list in `.vscode/settings.json` - "vite-plugin-svelte-css-no-scopable-elements", // NOTICE: Keep this list in sync with the list in `.vscode/settings.json` - "a11y-no-static-element-interactions", // NOTICE: Keep this list in sync with the list in `.vscode/settings.json` - "a11y-no-noninteractive-element-interactions", // NOTICE: Keep this list in sync with the list in `.vscode/settings.json` - "a11y-click-events-have-key-events", // NOTICE: Keep this list in sync with the list in `.vscode/settings.json` - "a11y_consider_explicit_label", // NOTICE: Keep this list in sync with the list in `.vscode/settings.json` - "a11y_click_events_have_key_events", // NOTICE: Keep this list in sync with the list in `.vscode/settings.json` - "a11y_no_noninteractive_element_interactions", // NOTICE: Keep this list in sync with the list in `.vscode/settings.json` - "a11y_no_static_element_interactions", // NOTICE: Keep this list in sync with the list in `.vscode/settings.json` - ]; - if (suppressed.includes(warning.code)) return; - - defaultHandler?.(warning); - }, - }), - viteMultipleAssets( - // Additional static asset directories besides `public/` - [ - { input: "../demo-artwork/**", output: "demo-artwork" }, - { input: "../branding/favicons/**", output: "" }, - ], - // Options where we set custom MIME types - { mimeTypes: { ".graphite": "application/json" } }, - ), - ], + plugins: plugins(mode), resolve: { alias: [ { find: /@branding\/(.*\.svg)/, replacement: path.resolve(projectRootDir, "../branding", "$1?raw") }, @@ -58,374 +25,63 @@ export default defineConfig(({ mode }) => { port: 8080, host: "0.0.0.0", }, - build: { - rollupOptions: { - plugins: - mode !== "dev" - ? [ - rollupPluginLicense({ - thirdParty: { - includePrivate: false, - multipleVersions: true, - allow: { - test: `(${getAcceptedLicenses()})`, - failOnUnlicensed: true, - failOnViolation: true, - }, - output: { - file: path.resolve(__dirname, "./dist/third-party-licenses.txt"), - template: formatThirdPartyLicenses, - }, - }, - }), - ] - : [], - }, - }, }; }); -type LicenseInfo = { - licenseName: string; - licenseText: string; - noticeText?: string; - packages: PackageInfo[]; -}; +function plugins(mode: string): PluginOption[] { + const plugins = [ + svelte({ + preprocess: [sveltePreprocess()], + onwarn(warning, defaultHandler) { + const suppressed = [ + "css-unused-selector", // NOTICE: Keep this list in sync with the list in `.vscode/settings.json` + "vite-plugin-svelte-css-no-scopable-elements", // NOTICE: Keep this list in sync with the list in `.vscode/settings.json` + "a11y-no-static-element-interactions", // NOTICE: Keep this list in sync with the list in `.vscode/settings.json` + "a11y-no-noninteractive-element-interactions", // NOTICE: Keep this list in sync with the list in `.vscode/settings.json` + "a11y-click-events-have-key-events", // NOTICE: Keep this list in sync with the list in `.vscode/settings.json` + "a11y_consider_explicit_label", // NOTICE: Keep this list in sync with the list in `.vscode/settings.json` + "a11y_click_events_have_key_events", // NOTICE: Keep this list in sync with the list in `.vscode/settings.json` + "a11y_no_noninteractive_element_interactions", // NOTICE: Keep this list in sync with the list in `.vscode/settings.json` + "a11y_no_static_element_interactions", // NOTICE: Keep this list in sync with the list in `.vscode/settings.json` + ]; + if (suppressed.includes(warning.code)) return; -type PackageInfo = { - name: string; - version: string; - author: string; - repository: string; -}; - -function formatThirdPartyLicenses(jsLicenses: Dependency[]): string { - // Generate the Rust license information. - const rustLicenses = generateRustLicenses(); - const additionalLicenses = generateAdditionalLicenses(); - - // Ensure we have the required license information to work with before proceeding. - if (rustLicenses.length === 0) { - // This is probably caused by `cargo about` not being installed. - console.error("Could not run `cargo about`, which is required to generate license information."); - console.error("To install cargo-about on your system, you can run `cargo install cargo-about`."); - console.error("License information is required in production builds. Aborting."); - - process.exit(1); - } - if (jsLicenses.length === 0) { - console.error("No JavaScript package licenses were found by `rollup-plugin-license`. Please investigate."); - console.error("License information is required in production builds. Aborting."); - - process.exit(1); - } - - let licenses = rustLicenses.concat(additionalLicenses); - - // SPECIAL CASE: Find then duplicate this license if one of its packages is `path-bool`, adding its notice text. - let foundLicensesIndex: number | undefined = undefined; - let foundPackagesIndex: number | undefined = undefined; - licenses.forEach((license, licenseIndex) => { - license.packages.forEach((pkg, pkgIndex) => { - if (pkg.name === "path-bool") { - foundLicensesIndex = licenseIndex; - foundPackagesIndex = pkgIndex; - } - }); - }); - if (foundLicensesIndex !== undefined && foundPackagesIndex !== undefined) { - const license = licenses[foundLicensesIndex]; - const pkg = license.packages[foundPackagesIndex]; - - license.packages = license.packages.filter((pkg) => pkg.name !== "path-bool"); - const noticeText = fs.readFileSync(path.resolve(__dirname, "../libraries/path-bool/NOTICE"), "utf8"); - - licenses.push({ - licenseName: license.licenseName, - licenseText: license.licenseText, - noticeText, - packages: [pkg], - }); - } - - // Extend the license list with the provided JS licenses. - jsLicenses.forEach((jsLicense) => { - const name = jsLicense.name || ""; - const version = jsLicense.version || ""; - const author = jsLicense.author?.text() || ""; - const licenseName = jsLicense.license || ""; - const licenseText = trimBlankLines(jsLicense.licenseText || ""); - const noticeText = trimBlankLines(jsLicense.noticeText || ""); - - let repository = jsLicense.repository || ""; - if (repository && typeof repository === "object") repository = repository.url; - - const matchedLicense = licenses.find( - (license) => license.licenseName === licenseName && trimBlankLines(license.licenseText || "") === licenseText && trimBlankLines(license.noticeText || "") === noticeText, - ); - - const pkg: PackageInfo = { name, version, author, repository }; - if (matchedLicense) matchedLicense.packages.push(pkg); - else licenses.push({ licenseName, licenseText, noticeText, packages: [pkg] }); - }); - - // Combine any license notices into the license text. - licenses.forEach((license, index) => { - if (license.noticeText) { - licenses[index].licenseText += "\n\n"; - licenses[index].licenseText += " _______________________________________\n"; - licenses[index].licenseText += "│ │\n"; - licenses[index].licenseText += "│ THE FOLLOWING NOTICE FILE IS INCLUDED │\n"; - licenses[index].licenseText += "│ │\n"; - licenses[index].licenseText += " ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾\n\n"; - licenses[index].licenseText += `${license.noticeText}\n`; - licenses[index].noticeText = undefined; - } - }); - - // De-duplicate any licenses with the same text by merging their lists of packages. - const licensesNormalizedWhitespace = licenses.map((license) => license.licenseText.replace(/[\n\s]+/g, " ").trim()); - licenses.forEach((currentLicense, currentLicenseIndex) => { - licenses.slice(0, currentLicenseIndex).forEach((comparisonLicense, comparisonLicenseIndex) => { - if (licensesNormalizedWhitespace[currentLicenseIndex] === licensesNormalizedWhitespace[comparisonLicenseIndex]) { - currentLicense.packages.push(...comparisonLicense.packages); - comparisonLicense.packages = []; - // After emptying the packages, the redundant license with no packages will be removed in the next step's `filter()`. - } - }); - }); - - // Filter out first-party internal Graphite crates. - licenses = licenses.filter((license) => { - license.packages = license.packages.filter( - (packageInfo) => - !(packageInfo.repository && packageInfo.repository.toLowerCase().includes("github.com/GraphiteEditor/Graphite".toLowerCase())) && - !( - packageInfo.author && - packageInfo.author.toLowerCase().includes("contact@graphite.art") && - // Exclude a comma which indicates multiple authors, which we need to not filter out - !packageInfo.author.toLowerCase().includes(",") - ), - ); - return license.packages.length > 0; - }); - - // Sort the licenses by the number of packages using the same license, and then alphabetically by license name. - licenses.sort((a, b) => a.licenseText.localeCompare(b.licenseText)); - licenses.sort((a, b) => a.licenseName.localeCompare(b.licenseName)); - licenses.sort((a, b) => b.packages.length - a.packages.length); - // Sort the individual packages using each license alphabetically. - licenses.forEach((license) => { - license.packages.sort((a, b) => a.name.localeCompare(b.name)); - }); - - // Prepare a header for the license notice. - let formattedLicenseNotice = ""; - formattedLicenseNotice += "▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐\n"; - formattedLicenseNotice += "▐▐ ▐▐\n"; - formattedLicenseNotice += "▐▐ GRAPHITE THIRD-PARTY SOFTWARE LICENSE NOTICES ▐▐\n"; - formattedLicenseNotice += "▐▐ ▐▐\n"; - formattedLicenseNotice += "▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐\n"; - - // Append a block for each license shared by multiple packages with identical license text. - licenses.forEach((license) => { - let packagesWithSameLicense = license.packages.map((packageInfo) => { - const { name, version, author, repository } = packageInfo; - - // Remove the `git+` or `git://` prefix and `.git` suffix. - let repo = repository; - if (repo.startsWith("git+")) repo = repo.slice("git+".length); - if (repo.startsWith("git://")) repo = repo.slice("git://".length); - if (repo.endsWith(".git")) repo = repo.slice(0, -".git".length); - if (repo.endsWith(".git#release")) repo = repo.slice(0, -".git#release".length); - - return `${name} ${version}${author ? ` - ${author}` : ""}${repo ? ` - ${repo}` : ""}`; - }); - const multi = packagesWithSameLicense.length !== 1; - const saysLicense = license.licenseName.toLowerCase().includes("license"); - const header = `The package${multi ? "s" : ""} listed here ${multi ? "are" : "is"} licensed under the terms of the ${license.licenseName}${saysLicense ? "" : " license"} printed beneath`; - const packagesLineLength = Math.max(header.length, ...packagesWithSameLicense.map((line) => line.length)); - packagesWithSameLicense = packagesWithSameLicense.map((line) => `│ ${line}${" ".repeat(packagesLineLength - line.length)} │`); - - formattedLicenseNotice += "\n"; - formattedLicenseNotice += ` ${"_".repeat(packagesLineLength + 2)}\n`; - formattedLicenseNotice += `│ ${" ".repeat(packagesLineLength)} │\n`; - formattedLicenseNotice += `│ ${header}${" ".repeat(packagesLineLength - header.length)} │\n`; - formattedLicenseNotice += `│${"_".repeat(packagesLineLength + 2)}│\n`; - formattedLicenseNotice += `${packagesWithSameLicense.join("\n")}\n`; - formattedLicenseNotice += ` ${"‾".repeat(packagesLineLength + 2)}\n`; - formattedLicenseNotice += `${license.licenseText}\n`; - }); - - formattedLicenseNotice += "\n"; - return formattedLicenseNotice; -} - -// Include additional licenses that aren't automatically generated by `cargo about` or `rollup-plugin-license`. -function generateAdditionalLicenses(): LicenseInfo[] { - const ADDITIONAL_LICENSES = [ - { - licenseName: "SIL Open Font License 1.1", - licenseTextPath: "node_modules/source-sans-pro/LICENSE.txt", - manifestPath: "node_modules/source-sans-pro/package.json", - }, - { - licenseName: "SIL Open Font License 1.1", - licenseTextPath: "node_modules/source-code-pro/LICENSE.md", - manifestPath: "node_modules/source-code-pro/package.json", - }, + defaultHandler?.(warning); + }, + }), + viteMultipleAssets( + // Additional static asset directories besides `public/` + [ + { input: "../demo-artwork/**", output: "demo-artwork" }, + { input: "../branding/favicons/**", output: "" }, + ], + // Options where we set custom MIME types + { mimeTypes: { ".graphite": "application/json" } }, + ), ]; - return ADDITIONAL_LICENSES.map(({ licenseName, licenseTextPath, manifestPath }) => { - const licenseText = (fs.existsSync(licenseTextPath) && fs.readFileSync(licenseTextPath, "utf8")) || ""; - - const manifestJSON = (fs.existsSync(manifestPath) && JSON.parse(fs.readFileSync(manifestPath, "utf8"))) || {}; - const name = manifestJSON.name || ""; - const version = manifestJSON.version || ""; - const author = manifestJSON.author.name || manifestJSON.author || ""; - const repository = manifestJSON.repository?.url || ""; - - return { - licenseName, - licenseText: trimBlankLines(licenseText), - packages: [{ name, version, author, repository }], - }; - }); -} - -function generateRustLicenses(): LicenseInfo[] { - // Log the starting status to the build output. - console.info("\n\nGenerating license information for Rust code\n"); - - try { - // Call `cargo about` in the terminal to generate the license information for Rust crates. - // The `about.hbs` file is written so it generates a valid JavaScript array expression which we evaluate below. - const { licenses, status, stderr } = (() => { - // On Windows, we have to write the output to a temporary file because of powershell's handling of stdout. - if (os.platform() === "win32") { - const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "graphite-licenses-")); - const licensesFile = path.join(tmpDir, "licenses.js"); - - const { status, stderr } = spawnSync("cargo", ["about", "generate", "about.hbs", "-o", licensesFile], { - cwd: path.join(__dirname, ".."), - encoding: "utf8", - shell: true, - windowsHide: true, // Hide the terminal on Windows + if (mode !== "native") { + plugins.push({ + name: "third-party-licenses", + buildStart() { + try { + execSync("cargo run -p third-party-licenses", { + stdio: "inherit", + }); + } catch (_e) { + this.error("Failed to generate third-party licenses"); + } + }, + generateBundle() { + const source = readFileSync(path.resolve(projectRootDir, "third-party-licenses.txt"), "utf-8"); + this.emitFile({ + type: "asset", + fileName: "third-party-licenses.txt", + source, }); - - const licenses = fs.existsSync(licensesFile) ? fs.readFileSync(licensesFile, "utf8") : ""; - - return { licenses, status, stderr }; - } else { - const { stdout, status, stderr } = spawnSync("cargo", ["about", "generate", "about.hbs"], { - cwd: path.join(__dirname, ".."), - encoding: "utf8", - shell: true, - }); - - return { licenses: stdout, status, stderr }; - } - })(); - - // If the command failed, print the error message and exit early. - if (status !== 0) { - // Cargo returns 101 when the subcommand (`about`) wasn't found, so we skip printing the below error message in that case. - if (status !== 101) { - console.error("cargo-about failed", status, stderr); - } - return []; - } - - // Make sure the output starts with this expected label, which lets us know the file generated with expected output. - // We don't want to eval an error message or something else, so we fail early if that happens. - if (!licenses.trim().startsWith("GENERATED_BY_CARGO_ABOUT:")) { - console.error("Unexpected output from cargo-about", licenses); - return []; - } - - // Convert the array JS syntax string into an actual JS array in memory. - // Security-wise, eval() isn't any worse than require(), but it's able to work without a temporary file. - // We call eval indirectly to avoid a warning as explained here: . - const indirectEval = eval; - const licensesArray = indirectEval(licenses) as LicenseInfo[]; - - // Remove the HTML character encoding caused by Handlebars. - const rustLicenses = (licensesArray || []).map( - (rustLicense): LicenseInfo => ({ - licenseName: htmlDecode(rustLicense.licenseName), - licenseText: trimBlankLines(htmlDecode(rustLicense.licenseText)), - packages: rustLicense.packages.map( - (packageInfo): PackageInfo => ({ - name: htmlDecode(packageInfo.name), - version: htmlDecode(packageInfo.version), - author: htmlDecode(packageInfo.author) - .replace(/\[(.*), \]/, "$1") - .replace("[]", ""), - repository: htmlDecode(packageInfo.repository), - }), - ), - }), - ); - - return rustLicenses; - } catch (_) { - return []; - } -} - -function htmlDecode(input: string): string { - if (!input) return input; - - const htmlEntities = { - nbsp: " ", - copy: "©", - reg: "®", - lt: "<", - gt: ">", - amp: "&", - apos: "'", - quot: `"`, - }; - - return input.replace(/&([^;]+);/g, (entity: string, entityCode: string) => { - const maybeEntity = Object.entries(htmlEntities).find(([key, _]) => key === entityCode); - if (maybeEntity) return maybeEntity[1]; - - let match; - if ((match = entityCode.match(/^#x([\da-fA-F]+)$/))) { - return String.fromCharCode(parseInt(match[1], 16)); - } - if ((match = entityCode.match(/^#(\d+)$/))) { - return String.fromCharCode(~~match[1]); - } - return entity; - }); -} - -function trimBlankLines(input: string): string { - let result = input.replace(/\r/g, ""); - - while (result.charAt(0) === "\r" || result.charAt(0) === "\n") { - result = result.slice(1); - } - while (result.slice(-1) === "\r" || result.slice(-1) === "\n") { - result = result.slice(0, -1); + }, + }); } - return result; -} - -function getAcceptedLicenses() { - const tomlContent = fs.readFileSync(path.resolve(__dirname, "../about.toml"), "utf8"); - - const licensesBlock = tomlContent?.match(/accepted\s*=\s*\[([^\]]*)\]/)?.[1] || ""; - - return licensesBlock - .split("\n") - .map((line) => line.replace(/#.*$/, "")) // Remove comments - .join("\n") - .split(",") - .map((license) => license.trim().replace(/"/g, "")) - .filter((license) => license.length > 0) - .join(" OR "); + return plugins; } diff --git a/node-graph/graph-craft/Cargo.toml b/node-graph/graph-craft/Cargo.toml index a31eea42..9906a2af 100644 --- a/node-graph/graph-craft/Cargo.toml +++ b/node-graph/graph-craft/Cargo.toml @@ -3,6 +3,7 @@ name = "graph-craft" version = "0.1.0" edition = "2024" license = "MIT OR Apache-2.0" +authors.workspace = true [features] default = ["dealloc_nodes", "wgpu"] diff --git a/node-graph/interpreted-executor/Cargo.toml b/node-graph/interpreted-executor/Cargo.toml index 4100c67e..05a9f9c8 100644 --- a/node-graph/interpreted-executor/Cargo.toml +++ b/node-graph/interpreted-executor/Cargo.toml @@ -3,6 +3,7 @@ name = "interpreted-executor" version = "0.1.0" edition = "2024" license = "MIT OR Apache-2.0" +authors.workspace = true [features] default = [] @@ -56,4 +57,3 @@ harness = false [[bench]] name = "run_cached_iai" harness = false - diff --git a/node-graph/libraries/wgpu-executor/Cargo.toml b/node-graph/libraries/wgpu-executor/Cargo.toml index 046ff6ad..23ae4fd5 100644 --- a/node-graph/libraries/wgpu-executor/Cargo.toml +++ b/node-graph/libraries/wgpu-executor/Cargo.toml @@ -3,6 +3,7 @@ name = "wgpu-executor" version = "0.1.0" edition = "2024" license = "MIT OR Apache-2.0" +authors.workspace = true [dependencies] # Local dependencies diff --git a/node-graph/nodes/graphic/Cargo.toml b/node-graph/nodes/graphic/Cargo.toml index 30077176..c6732294 100644 --- a/node-graph/nodes/graphic/Cargo.toml +++ b/node-graph/nodes/graphic/Cargo.toml @@ -3,6 +3,7 @@ name = "graphic-nodes" version = "0.1.0" edition = "2024" license = "MIT OR Apache-2.0" +authors.workspace = true [dependencies] # Local dependencies diff --git a/node-graph/preprocessor/Cargo.toml b/node-graph/preprocessor/Cargo.toml index 08d03c85..6ec3c3fe 100644 --- a/node-graph/preprocessor/Cargo.toml +++ b/node-graph/preprocessor/Cargo.toml @@ -3,6 +3,7 @@ name = "preprocessor" version = "0.1.0" edition = "2024" license = "MIT OR Apache-2.0" +authors.workspace = true [features] diff --git a/package.json b/package.json index 44bc1f96..b49c6ca2 100644 --- a/package.json +++ b/package.json @@ -4,15 +4,15 @@ "scripts": { "---------- DEV SERVER ----------": "", "start": "cd frontend && npm start", - "start-desktop": "cd frontend && npm run build-native-dev && cargo run -p graphite-desktop-bundle -- open", + "start-desktop": "cd frontend && npm run build-native-dev && cargo run -p third-party-licenses --features desktop && cargo run -p graphite-desktop-bundle -- open", "profiling": "cd frontend && npm run profiling", "production": "cd frontend && npm run production", "---------- BUILDS ----------": "", "build-dev": "cd frontend && npm run build-dev", "build-profiling": "cd frontend && npm run build-profiling", "build": "cd frontend && npm run build", - "build-desktop": "cd frontend && npm run build-native && cargo run -r -p graphite-desktop-bundle", - "build-desktop-dev": "cd frontend && npm run build-native-dev && cargo run -p graphite-desktop-bundle", + "build-desktop": "cd frontend && npm run build-native && cargo run -p third-party-licenses --features desktop && cargo run -r -p graphite-desktop-bundle", + "build-desktop-dev": "cd frontend && npm run build-native-dev && cargo run -p third-party-licenses --features desktop && cargo run -p graphite-desktop-bundle", "---------- UTILITIES ----------": "", "lint": "cd frontend && npm run lint", "lint-fix": "cd frontend && npm run lint-fix" diff --git a/tools/third-party-licenses/.gitignore b/tools/third-party-licenses/.gitignore new file mode 100644 index 00000000..db71f1e3 --- /dev/null +++ b/tools/third-party-licenses/.gitignore @@ -0,0 +1 @@ +*.hash diff --git a/tools/third-party-licenses/Cargo.toml b/tools/third-party-licenses/Cargo.toml new file mode 100644 index 00000000..4a599673 --- /dev/null +++ b/tools/third-party-licenses/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "third-party-licenses" +edition.workspace = true +version.workspace = true +license.workspace = true +authors.workspace = true + +[features] +desktop = ["dep:cef-dll-sys", "dep:scraper"] + +[dependencies] +# Workspace dependencies +serde = { workspace = true } +serde_json = { workspace = true } +lzma-rust2 = { workspace = true } + +# Optional workspace dependencies +cef-dll-sys = { workspace = true, optional = true } +scraper = { workspace = true, optional = true } diff --git a/tools/third-party-licenses/build.rs b/tools/third-party-licenses/build.rs new file mode 100644 index 00000000..08ea588e --- /dev/null +++ b/tools/third-party-licenses/build.rs @@ -0,0 +1,15 @@ +fn main() { + println!("cargo:rerun-if-changed=src"); + + println!("cargo:rerun-if-env-changed=DEP_CEF_DLL_WRAPPER_CEF_DIR"); + if let Ok(cef_dir) = std::env::var("DEP_CEF_DLL_WRAPPER_CEF_DIR") { + println!("cargo:rustc-env=CEF_PATH={cef_dir}"); + } + + let manifest_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); + if std::env::var("CARGO_FEATURE_DESKTOP").is_ok() { + let _ = std::fs::remove_file(manifest_dir.join("desktop.hash")); + } else { + let _ = std::fs::remove_file(manifest_dir.join("web.hash")); + } +} diff --git a/tools/third-party-licenses/src/cargo.rs b/tools/third-party-licenses/src/cargo.rs new file mode 100644 index 00000000..49d6731d --- /dev/null +++ b/tools/third-party-licenses/src/cargo.rs @@ -0,0 +1,106 @@ +use crate::{LicenceSource, LicenseEntry, Package}; +use serde::Deserialize; +use std::fs; +use std::hash::{Hash, Hasher}; +use std::path::PathBuf; +use std::process::{self, Command}; + +pub struct CargoLicenseSource {} + +impl CargoLicenseSource { + pub fn new() -> Self { + Self {} + } +} + +impl LicenceSource for CargoLicenseSource { + fn licenses(&self) -> Vec { + parse(run()) + } +} + +impl Hash for CargoLicenseSource { + fn hash(&self, state: &mut H) { + let lock_path = PathBuf::from(env!("CARGO_WORKSPACE_DIR")).join("Cargo.lock"); + fs::read_to_string(lock_path).unwrap().hash(state) + } +} + +#[derive(Deserialize)] +struct Output { + licenses: Vec, +} + +#[derive(Deserialize)] +struct License { + name: Option, + text: Option, + used_by: Vec, +} + +#[derive(Deserialize)] +struct UsedBy { + #[serde(rename = "crate")] + crate_info: Crate, +} + +#[derive(Deserialize)] +struct Crate { + name: Option, + version: Option, + authors: Option>, + repository: Option, +} + +fn parse(parsed: Output) -> Vec { + parsed + .licenses + .into_iter() + .map(|license| { + let packages = license + .used_by + .into_iter() + .map(|used| { + let name = used.crate_info.name.as_deref().unwrap_or_default(); + let version = used.crate_info.version.as_deref().unwrap_or_default(); + let display_name = if version.is_empty() { name.to_string() } else { format!("{name}@{version}") }; + + let repository = used.crate_info.repository.filter(|s| !s.is_empty()); + + Package { + name: display_name, + authors: used.crate_info.authors.unwrap_or_default(), + url: repository, + } + }) + .collect(); + + LicenseEntry { + name: license.name, + text: license.text.as_deref().unwrap_or_default().to_string(), + packages, + } + }) + .collect() +} + +fn run() -> Output { + let output = Command::new("cargo") + .args(["about", "generate", "--format", "json", "--frozen"]) + .current_dir(env!("CARGO_WORKSPACE_DIR")) + .output() + .unwrap_or_else(|e| { + eprintln!("Failed to run cargo about generate: {e}"); + process::exit(1) + }); + + if !output.status.success() { + eprintln!("cargo about generate failed:\n{}", String::from_utf8_lossy(&output.stderr)); + process::exit(1) + } + + serde_json::from_str(&String::from_utf8(output.stdout).expect("cargo about generate should return valid UTF-8")).unwrap_or_else(|e| { + eprintln!("Failed to parse cargo about generate JSON: {e}"); + process::exit(1) + }) +} diff --git a/tools/third-party-licenses/src/cef.rs b/tools/third-party-licenses/src/cef.rs new file mode 100644 index 00000000..94fe45d0 --- /dev/null +++ b/tools/third-party-licenses/src/cef.rs @@ -0,0 +1,105 @@ +use lzma_rust2::XzReader; +use scraper::{Html, Selector}; +use std::hash::Hash; +use std::io::Read; +use std::path::PathBuf; +use std::{fs, process}; + +use crate::{LicenceSource, LicenseEntry, Package}; + +pub struct CefLicenseSource; + +impl CefLicenseSource { + pub fn new() -> Self { + Self {} + } +} + +impl LicenceSource for CefLicenseSource { + fn licenses(&self) -> Vec { + let html = read(); + parse(&html) + } +} + +impl Hash for CefLicenseSource { + fn hash(&self, state: &mut H) { + read().hash(state) + } +} + +fn parse(html: &str) -> Vec { + let document = Html::parse_document(html); + + let product_sel = Selector::parse("div.product").unwrap(); + let title_sel = Selector::parse("span.title").unwrap(); + let homepage_sel = Selector::parse("span.homepage a").unwrap(); + let license_sel = Selector::parse("div.license pre").unwrap(); + + document + .select(&product_sel) + .filter_map(|product| { + let name: String = product.select(&title_sel).next().map(|el| el.text().collect()).unwrap_or_default(); + + if name.is_empty() { + return None; + } + + let homepage = product.select(&homepage_sel).next().and_then(|el| el.value().attr("href").map(String::from)); + + let license_text: String = product.select(&license_sel).next().map(|el| el.text().collect::()).unwrap_or_default().trim().to_string(); + + let pkg = Package { + name, + url: homepage, + authors: Vec::new(), + }; + + Some(LicenseEntry { + name: None, + text: license_text, + packages: vec![pkg], + }) + }) + .collect() +} + +fn read() -> String { + let cef_path = PathBuf::from(env!("CEF_PATH")); + let cef_credits = std::fs::read_dir(&cef_path) + .unwrap_or_else(|e| { + eprintln!("Failed to read CEF_PATH directory {}: {e}", cef_path.display()); + process::exit(1); + }) + .filter_map(|entry| entry.ok()) + .find(|entry| { + let name = entry.file_name(); + name.eq_ignore_ascii_case("credits.html") || name.eq_ignore_ascii_case("credits.html.xz") + }) + .map(|entry| entry.path()) + .unwrap_or_else(|| { + eprintln!("Could not find CREDITS.html or CREDITS.html.xz in {}", cef_path.display()); + process::exit(1); + }); + + let decompress_xz = cef_credits.extension().map(|ext| ext.eq_ignore_ascii_case("xz")).unwrap_or(false); + + if decompress_xz { + let file = fs::File::open(&cef_credits).unwrap_or_else(|e| { + eprintln!("Failed to open CEF credits file {}: {e}", cef_credits.display()); + process::exit(1); + }); + let mut reader = XzReader::new(file, false); + let mut html = String::new(); + reader.read_to_string(&mut html).unwrap_or_else(|e| { + eprintln!("Failed to decompress CEF credits file {}: {e}", cef_credits.display()); + process::exit(1); + }); + html + } else { + fs::read_to_string(&cef_credits).unwrap_or_else(|e| { + eprintln!("Failed to read CEF credits file {}: {e}", cef_credits.display()); + process::exit(1); + }) + } +} diff --git a/tools/third-party-licenses/src/main.rs b/tools/third-party-licenses/src/main.rs new file mode 100644 index 00000000..afa1b501 --- /dev/null +++ b/tools/third-party-licenses/src/main.rs @@ -0,0 +1,229 @@ +use std::collections::HashMap; +use std::hash::{DefaultHasher, Hash, Hasher}; +use std::path::PathBuf; +use std::{fs, process}; + +mod cargo; +#[cfg(feature = "desktop")] +mod cef; +mod npm; + +use crate::cargo::CargoLicenseSource; +#[cfg(feature = "desktop")] +use crate::cef::CefLicenseSource; +use crate::npm::NpmLicenseSource; + +pub trait LicenceSource: std::hash::Hash { + fn licenses(&self) -> Vec; +} + +pub struct LicenseEntry { + name: Option, + text: String, + packages: Vec, +} + +pub struct Package { + name: String, + authors: Vec, + url: Option, +} + +#[derive(Hash)] +struct Run<'a> { + output: &'a Vec, + cargo: &'a CargoLicenseSource, + npm: &'a NpmLicenseSource, + #[cfg(feature = "desktop")] + cef: &'a CefLicenseSource, +} + +fn main() { + let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + let workspace_dir = PathBuf::from(env!("CARGO_WORKSPACE_DIR")); + + #[cfg(feature = "desktop")] + let output_path = workspace_dir.join("desktop/third-party-licenses.txt.xz"); + #[cfg(not(feature = "desktop"))] + let output_path = workspace_dir.join("frontend/third-party-licenses.txt"); + + #[cfg(feature = "desktop")] + let current_hash_path = manifest_dir.join("desktop.hash"); + #[cfg(not(feature = "desktop"))] + let current_hash_path = manifest_dir.join("web.hash"); + + let cargo_source = CargoLicenseSource::new(); + let npm_source = NpmLicenseSource::new(workspace_dir.join("frontend")); + #[cfg(feature = "desktop")] + let cef_source = CefLicenseSource::new(); + + let mut run = Run { + cargo: &cargo_source, + npm: &npm_source, + #[cfg(feature = "desktop")] + cef: &cef_source, + output: &fs::read(&output_path).unwrap_or_default(), + }; + + let mut hasher = DefaultHasher::new(); + run.hash(&mut hasher); + let current_hash = format!("{:016x}", hasher.finish()); + + if current_hash == fs::read_to_string(¤t_hash_path).unwrap_or_default() { + eprintln!("No changes in licenses detected, skipping generation."); + return; + } + eprintln!("Changes in licenses detected, generating new license file."); + + let licenses = merge_filter_dedup_and_sort(vec![ + cargo_source.licenses(), + npm_source.licenses(), + #[cfg(feature = "desktop")] + cef_source.licenses(), + ]); + let formatted = format_credits(&licenses); + + #[cfg(feature = "desktop")] + let output = compress(&formatted); + #[cfg(not(feature = "desktop"))] + let output = formatted.as_bytes().to_vec(); + if let Some(parent) = output_path.parent() { + fs::create_dir_all(parent).unwrap_or_else(|e| { + eprintln!("Failed to create directory {}: {e}", parent.display()); + std::process::exit(1); + }); + } + fs::write(&output_path, &output).unwrap_or_else(|e| { + eprintln!("Failed to write {}: {e}", &output_path.display()); + std::process::exit(1); + }); + run.output = &output; + + let hash = { + let mut hasher = DefaultHasher::new(); + run.hash(&mut hasher); + format!("{:016x}", hasher.finish()) + }; + + fs::write(¤t_hash_path, hash).unwrap_or_else(|e| { + eprintln!("Failed to write hash file {}: {e}", current_hash_path.display()); + process::exit(1); + }); +} + +fn format_credits(licenses: &Vec) -> String { + let mut out = String::new(); + + out.push_str("▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐\n"); + out.push_str("▐▐ ▐▐\n"); + out.push_str("▐▐ GRAPHITE THIRD-PARTY SOFTWARE LICENSE NOTICES ▐▐\n"); + out.push_str("▐▐ ▐▐\n"); + out.push_str("▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐\n"); + + for license in licenses { + let package_lines: Vec = license + .packages + .iter() + .map(|pkg| match &pkg { + Package { name, authors, url: Some(url) } if !authors.is_empty() => format!("{} - [{}] - {}", name, authors.join(", "), url), + Package { name, authors: _, url: Some(url) } => format!("{} - {}", name, url), + Package { name, authors, url: None } if !authors.is_empty() => format!("{} - [{}]", name, authors.join(", ")), + _ => pkg.name.clone(), + }) + .collect(); + + let multi = package_lines.len() > 1; + + let header = format!( + "The package{} listed here {} licensed under the terms of the {} printed beneath", + if multi { "s" } else { "" }, + if multi { "are" } else { "is" }, + if let Some(license) = license.name.as_ref() { license.to_string() } else { "license".to_string() } + ); + + let max_len = std::iter::once(header.len()).chain(package_lines.iter().map(|l| l.chars().count())).max().unwrap_or(0); + + let padded_packages: Vec = package_lines + .iter() + .map(|line| { + let pad = max_len - line.chars().count(); + format!("│ {}{} │", line, " ".repeat(pad)) + }) + .collect(); + + out.push_str(&format!("\n {}\n", "_".repeat(max_len + 2))); + out.push_str(&format!("│ {} │\n", " ".repeat(max_len))); + out.push_str(&format!("│ {}{} │\n", header, " ".repeat(max_len - header.len()))); + out.push_str(&format!("│{}│\n", "_".repeat(max_len + 2))); + out.push_str(&padded_packages.join("\n")); + out.push('\n'); + out.push_str(&format!(" {}", "\u{203e}".repeat(max_len + 2))); + for line in license.text.lines() { + if line.is_empty() { + out.push('\n'); + continue; + } + out.push('\n'); + out.push_str(" "); + out.push_str(line); + } + out.truncate(out.trim_end().len()); + out.push('\n'); + } + + out +} + +fn merge_filter_dedup_and_sort(sources: Vec>) -> Vec { + let mut all = Vec::new(); + for source in sources { + all.extend(source); + } + filter(&mut all); + let mut all = dedup_by_licence_text(all); + all.sort_by(|a, b| b.packages.len().cmp(&a.packages.len()).then(a.text.len().cmp(&b.text.len()))); + all +} + +fn filter(licenses: &mut Vec) { + licenses.iter_mut().for_each(|l| { + l.packages.retain(|p| !(p.authors.len() == 1 && p.authors[0].contains("contact@graphite.art"))); + }); + licenses.retain(|l| !l.packages.is_empty()); +} + +fn dedup_by_licence_text(vec: Vec) -> Vec { + let mut map: HashMap = HashMap::new(); + + for entry in vec { + match map.entry(entry.text.clone()) { + std::collections::hash_map::Entry::Occupied(mut e) => { + e.get_mut().packages.extend(entry.packages); + } + std::collections::hash_map::Entry::Vacant(e) => { + e.insert(entry); + } + } + } + + map.into_values().collect() +} + +#[cfg(feature = "desktop")] +fn compress(content: &str) -> Vec { + use std::io::Write; + let mut buf = Vec::new(); + let mut writer = lzma_rust2::XzWriter::new(&mut buf, lzma_rust2::XzOptions::default()).unwrap_or_else(|e| { + eprintln!("Failed to create XZ writer: {e}"); + std::process::exit(1); + }); + writer.write_all(content.as_bytes()).unwrap_or_else(|e| { + eprintln!("Failed to write compressed credits: {e}"); + std::process::exit(1); + }); + writer.finish().unwrap_or_else(|e| { + eprintln!("Failed to finish XZ compression: {e}"); + std::process::exit(1); + }); + buf +} diff --git a/tools/third-party-licenses/src/npm.rs b/tools/third-party-licenses/src/npm.rs new file mode 100644 index 00000000..aac94f5c --- /dev/null +++ b/tools/third-party-licenses/src/npm.rs @@ -0,0 +1,93 @@ +use std::collections::HashMap; +use std::fs; +use std::path::PathBuf; +use std::process; +use std::process::Command; + +use crate::{LicenceSource, LicenseEntry, Package}; + +pub struct NpmLicenseSource { + dir: PathBuf, +} +impl NpmLicenseSource { + pub fn new(dir: PathBuf) -> Self { + Self { dir } + } +} + +impl LicenceSource for NpmLicenseSource { + fn licenses(&self) -> Vec { + parse(run(&self.dir)) + } +} + +impl std::hash::Hash for NpmLicenseSource { + fn hash(&self, state: &mut H) { + let lock_path = self.dir.join("package-lock.json"); + fs::read_to_string(lock_path).unwrap().hash(state) + } +} + +type Output = HashMap; + +#[derive(serde::Deserialize)] +struct NpmEntry { + licenses: Option, + repository: Option, + #[serde(rename = "licenseFile")] + license_file: Option, + publisher: Option, + email: Option, +} + +fn parse(parsed: Output) -> Vec { + parsed + .iter() + .map(|(name, entry)| { + let publisher_info = entry.publisher.as_ref().map(|p| { + let email_part = entry.email.as_ref().map(|e| format!(" <{}>", e)).unwrap_or_default(); + format!("{}{}", p, email_part) + }); + + let pkg = Package { + name: name.to_string(), + url: entry.repository.clone(), + authors: publisher_info.into_iter().collect(), + }; + + let license_text = entry.license_file.as_ref().and_then(|p| fs::read_to_string(p).ok()).map(|s| s.to_string()).unwrap_or_default(); + + LicenseEntry { + name: entry.licenses.clone(), + text: license_text, + packages: vec![pkg], + } + }) + .collect() +} + +fn run(dir: &std::path::Path) -> Output { + #[cfg(not(target_os = "windows"))] + let mut cmd = Command::new("npx"); + #[cfg(target_os = "windows")] + let mut cmd = Command::new("npx.cmd"); + cmd.args(["license-checker-rseidelsohn", "--production", "--json"]); + cmd.current_dir(dir); + + let output = cmd.output().unwrap_or_else(|e| { + eprintln!("Failed to run npx license-checker-rseidelsohn: {e}"); + process::exit(1); + }); + + if !output.status.success() { + eprintln!("npx license-checker-rseidelsohn failed:\n{}", String::from_utf8_lossy(&output.stderr)); + process::exit(1); + } + + let json_str = String::from_utf8(output.stdout).expect("Invalid UTF-8 from license-checker"); + + serde_json::from_str(&json_str).unwrap_or_else(|e| { + eprintln!("Failed to parse license-checker JSON: {e}"); + process::exit(1) + }) +} diff --git a/website/content/volunteer/guide/project-setup/_index.md b/website/content/volunteer/guide/project-setup/_index.md index 17c47708..3934df3a 100644 --- a/website/content/volunteer/guide/project-setup/_index.md +++ b/website/content/volunteer/guide/project-setup/_index.md @@ -19,9 +19,10 @@ Graphite is built with Rust and web technologies, which means you will need to i Next, install the dependencies required for development builds: ```sh -cargo install cargo-watch -cargo install wasm-pack cargo install -f wasm-bindgen-cli@0.2.100 +cargo install wasm-pack +cargo install cargo-watch +cargo install cargo-about ``` Regarding the last one: you'll likely get faster build times if you manually install that specific version of `wasm-bindgen-cli`. It is supposed to be installed automatically but a version mismatch causes it to reinstall every single recompilation. It may need to be manually updated periodically to match the version of the `wasm-bindgen` dependency in [`Cargo.toml`](https://github.com/GraphiteEditor/Graphite/blob/master/Cargo.toml). @@ -66,13 +67,7 @@ npm run production
Production build instructions: click here -You'll rarely need to compile your own production builds because our CI/CD system takes care of deployments. However, you can compile a production build with full optimizations by first installing the additional `cargo-about` dev dependency: - -```sh -cargo install cargo-about -``` - -And then running: +You'll rarely need to compile your own production builds because our CI/CD system takes care of deployments. However, you can compile a production build with full optimizations by running: ```sh npm run build