diff --git a/.github/workflows/build-win-bundle.yml b/.github/workflows/build-win-bundle.yml index 2ba73fb5d0..ee72140db8 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 25ae724691..7e38ecf303 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 d6cc8a574c..06bcee0483 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 04b605600a..c0ce387cbf 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 2970d515a2..a44f70b368 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 d80d7793e6..e9333d4940 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 + + 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.all}:${deps.cef.env.CEF_PATH}"; + 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 03cb965eff..7bb7e2f254 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 5a1f419347..270bb870b2 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"); - }; - }; - - 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; }; - }; - - 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; - }; - - 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 + 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.cargo-watch - pkgs.cargo-nextest - pkgs.cargo-expand + pkgs = import inputs.nixpkgs { + inherit system; + overlays = [ (import inputs.rust-overlay) ]; + }; - # Linker - pkgs.mold + 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; }; + }; - # Profiling tools - pkgs.gnuplot - pkgs.samply - pkgs.cargo-flamegraph + deps = { + crane = lib.call ./deps/crane.nix; + cef = lib.call ./deps/cef.nix; + rustGPU = lib.call ./deps/rust-gpu.nix; + }; - # 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; + + # TODO: graphene-cli = lib.call ./pkgs/graphene-cli.nix; - default = graphite; - }; + tools = { + third-party-licenses = lib.call ./pkgs/tools/third-party-licenses.nix; + }; + } + ); - devShells.default = import ./dev.nix { - inherit - pkgs - deps - libs - tools - ; - }; + devShells = withArgs ( + { lib, ... }: + { + default = lib.call ./dev.nix; + } + ); - formatter = pkgs.nixfmt-tree; + 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 0000000000..85b53791f8 --- /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 815fd1e5e9..d50b00fccd 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 03d7146f74..fee6603119 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 46e2f90a29..727e8c9555 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 6ffb014496..bd757add80 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"; - }; - - postUnpack = '' - mkdir ./branding - cp -r ${branding}/* ./branding - ''; + 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"; + }; - 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 ""; + npmDeps = pkgs.importNpmLock { + npmRoot = "${info.src}/frontend"; + }; + npmRoot = "frontend"; + nativeBuildInputs = [ + pkgs.importNpmLock.npmConfigHook + pkgs.nodePackages.npm + ] + ++ common.nativeBuildInputs; + + 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 0000000000..9559603377 --- /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 6b08a3a2d2..6bf8977a92 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 4cba7a96af..6c8c121b88 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 e8cb48ace5..0000000000 --- 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 372c9ba742..2dcdd7e24e 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 0511588568..414e0a40c5 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 e4efc915c2..a62fe0b3f9 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 0d8e6b21ac..6294bb2115 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 6a7105d5c7..087c1fde8a 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 a059b6e5f0..701c8a0872 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 e90047f897..6bcc0670c1 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 82b070b1e3..0dd03171e7 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 7a48829a21..a585981ef7 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[]; -}; - -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", - }, +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; + + 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 - }); - - 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, + 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, }); - - 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 a31eea4256..9906a2afa5 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 4100c67ef4..05a9f9c8e9 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 046ff6ad99..23ae4fd5d3 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 30077176f8..c67322948e 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 08d03c8524..6ec3c3fe99 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 44bc1f9614..b49c6ca203 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 0000000000..db71f1e3f6 --- /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 0000000000..4a59967389 --- /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 0000000000..08ea588e05 --- /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 0000000000..49d6731dd4 --- /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 0000000000..94fe45d08d --- /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 0000000000..afa1b501f0 --- /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 0000000000..aac94f5cad --- /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 17c47708e8..3934df3a20 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