From 8b3365f957795d2e02fc0669ac46036a5a23c720 Mon Sep 17 00:00:00 2001 From: "S.Gromov" Date: Wed, 15 Jul 2026 16:56:44 +0300 Subject: [PATCH] =?UTF-8?q?feat:=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D1=82=D1=8C=20=D0=BF=D0=BE=D0=B4=D0=B4=D0=B5=D1=80=D0=B6=D0=BA?= =?UTF-8?q?=D1=83=2020=20framework=20exact=20modes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Добавлены exact modes: - vue@vite и vue@webpack - nuxt@vite и nuxt@webpack - svelte@vite, svelte@webpack и sveltekit@vite - angular@application и angular@webpack - astro@vite - solid@vite, solid@webpack и solid-start@vite - preact@vite и preact@webpack - qwik@vite - lit@vite и lit@webpack - alpine@vite и alpine@webpack Для каждого mode реализованы изолированный adapter, нативный framework-компонент, declarations, manifest, CSS и внешний asset URL. Добавлены production integration-стенды с генерацией, typecheck, сборкой и Playwright-проверкой рендера спрайта и Viewer. Обновлены Viewer, RU/EN-гайды, README, technical reference и AI skills. Итоговая матрица включает 29 exact modes. Проверки: - 48 unit-тестов - 29 integration E2E-тестов --- AGENTS.md | 26 +- FEATURES.md | 10 +- MODES_CHECKLIST.md | 78 + README.md | 37 +- README_RU.md | 37 +- docs/en/README.md | 20 + docs/en/guides/README.md | 20 + docs/en/guides/alpine-vite.md | 91 + docs/en/guides/alpine-webpack.md | 103 + docs/en/guides/angular-application.md | 94 + docs/en/guides/angular-webpack.md | 93 + docs/en/guides/astro-vite.md | 92 + docs/en/guides/lit-vite.md | 86 + docs/en/guides/lit-webpack.md | 98 + docs/en/guides/next-app-turbopack.md | 8 +- docs/en/guides/next-app-webpack.md | 8 +- docs/en/guides/next-pages-turbopack.md | 8 +- docs/en/guides/next-pages-webpack.md | 8 +- docs/en/guides/nuxt-vite.md | 100 + docs/en/guides/nuxt-webpack.md | 113 + docs/en/guides/preact-vite.md | 75 + docs/en/guides/preact-webpack.md | 75 + docs/en/guides/qwik-vite.md | 82 + docs/en/guides/react-vite.md | 8 +- docs/en/guides/react-webpack.md | 8 +- docs/en/guides/solid-start-vite.md | 83 + docs/en/guides/solid-vite.md | 83 + docs/en/guides/solid-webpack.md | 75 + docs/en/guides/standalone-vite.md | 10 +- docs/en/guides/standalone-webpack.md | 10 +- docs/en/guides/standalone.md | 33 +- docs/en/guides/svelte-vite.md | 95 + docs/en/guides/svelte-webpack.md | 105 + docs/en/guides/sveltekit-vite.md | 91 + docs/en/guides/vue-vite.md | 105 + docs/en/guides/vue-webpack.md | 126 + docs/en/reference/technical.md | 46 +- docs/ru/README.md | 20 + docs/ru/guides/README.md | 20 + docs/ru/guides/alpine-vite.md | 91 + docs/ru/guides/alpine-webpack.md | 103 + docs/ru/guides/angular-application.md | 94 + docs/ru/guides/angular-webpack.md | 93 + docs/ru/guides/astro-vite.md | 92 + docs/ru/guides/lit-vite.md | 86 + docs/ru/guides/lit-webpack.md | 98 + docs/ru/guides/next-app-turbopack.md | 8 +- docs/ru/guides/next-app-webpack.md | 8 +- docs/ru/guides/next-pages-turbopack.md | 8 +- docs/ru/guides/next-pages-webpack.md | 8 +- docs/ru/guides/nuxt-vite.md | 100 + docs/ru/guides/nuxt-webpack.md | 113 + docs/ru/guides/preact-vite.md | 75 + docs/ru/guides/preact-webpack.md | 75 + docs/ru/guides/qwik-vite.md | 82 + docs/ru/guides/react-vite.md | 8 +- docs/ru/guides/react-webpack.md | 8 +- docs/ru/guides/solid-start-vite.md | 83 + docs/ru/guides/solid-vite.md | 83 + docs/ru/guides/solid-webpack.md | 75 + docs/ru/guides/standalone-vite.md | 10 +- docs/ru/guides/standalone-webpack.md | 10 +- docs/ru/guides/standalone.md | 35 +- docs/ru/guides/svelte-vite.md | 95 + docs/ru/guides/svelte-webpack.md | 105 + docs/ru/guides/sveltekit-vite.md | 91 + docs/ru/guides/vue-vite.md | 105 + docs/ru/guides/vue-webpack.md | 126 + docs/ru/reference/technical.md | 46 +- integration/README.md | 27 +- integration/apps/alpine-vite/index.html | 17 + integration/apps/alpine-vite/package.json | 20 + .../src/generated-contract.typecheck.ts | 22 + integration/apps/alpine-vite/src/main.js | 25 + .../apps/alpine-vite/src/sprite/.gitignore | 2 + .../apps/alpine-vite/src/sprite/index.d.ts | 1 + .../apps/alpine-vite/src/sprite/index.js | 1 + .../src/sprite/svg-sprite.config.json | 6 + integration/apps/alpine-vite/tsconfig.json | 12 + integration/apps/alpine-webpack/index.html | 16 + integration/apps/alpine-webpack/package.json | 23 + .../src/generated-contract.typecheck.ts | 22 + integration/apps/alpine-webpack/src/main.js | 25 + .../apps/alpine-webpack/src/sprite/.gitignore | 2 + .../apps/alpine-webpack/src/sprite/index.d.ts | 1 + .../apps/alpine-webpack/src/sprite/index.js | 1 + .../src/sprite/svg-sprite.config.json | 6 + integration/apps/alpine-webpack/tsconfig.json | 12 + .../apps/alpine-webpack/webpack.config.js | 26 + integration/apps/angular-webpack/package.json | 33 + .../apps/angular-webpack/src/index.html | 11 + integration/apps/angular-webpack/src/main.ts | 37 + .../angular-webpack/src/sprite/.gitignore | 2 + .../apps/angular-webpack/src/sprite/index.ts | 1 + .../src/sprite/svg-sprite.config.ts | 6 + .../apps/angular-webpack/src/type-probe.ts | 8 + .../apps/angular-webpack/tsconfig.json | 17 + .../apps/angular-webpack/webpack.config.js | 42 + integration/apps/angular/angular.json | 3 + integration/apps/angular/package.json | 2 +- integration/apps/angular/src/app.component.ts | 29 +- .../apps/angular/src/sprite/.gitignore | 2 + integration/apps/angular/src/sprite/index.ts | 1 + .../angular/src/sprite/svg-sprite.config.ts | 6 + integration/apps/angular/src/styles.css | 7 +- integration/apps/angular/src/type-probe.ts | 10 + .../apps/angular/svg-sprites.config.ts | 11 - integration/apps/angular/tsconfig.app.json | 2 +- integration/apps/angular/tsconfig.json | 2 - integration/apps/astro/package.json | 2 +- integration/apps/astro/src/pages/index.astro | 31 +- integration/apps/astro/src/sprite/.gitignore | 2 + integration/apps/astro/src/sprite/index.d.ts | 1 + integration/apps/astro/src/sprite/index.js | 1 + .../astro/src/sprite/svg-sprite.config.ts | 6 + integration/apps/astro/src/type-probe.ts | 11 + integration/apps/astro/svg-sprites.config.ts | 11 - integration/apps/lit-vite/index.html | 17 + integration/apps/lit-vite/package.json | 20 + .../src/generated-contract.typecheck.ts | 18 + integration/apps/lit-vite/src/main.js | 20 + .../apps/lit-vite/src/sprite/.gitignore | 2 + .../apps/lit-vite/src/sprite/index.d.ts | 1 + integration/apps/lit-vite/src/sprite/index.js | 1 + .../src/sprite/svg-sprite.config.json | 6 + integration/apps/lit-vite/tsconfig.json | 12 + integration/apps/lit-webpack/index.html | 16 + integration/apps/lit-webpack/package.json | 23 + .../src/generated-contract.typecheck.ts | 18 + integration/apps/lit-webpack/src/main.js | 20 + .../apps/lit-webpack/src/sprite/.gitignore | 2 + .../apps/lit-webpack/src/sprite/index.d.ts | 1 + .../apps/lit-webpack/src/sprite/index.js | 1 + .../src/sprite/svg-sprite.config.json | 6 + integration/apps/lit-webpack/tsconfig.json | 12 + .../apps/lit-webpack/webpack.config.js | 26 + integration/apps/nuxt-vite/app/app.vue | 37 + .../app/components/SpriteViewer.client.vue | 16 + .../app/generated-contract.typecheck.ts | 15 + integration/apps/nuxt-vite/nuxt.config.ts | 9 + integration/apps/nuxt-vite/package.json | 22 + .../apps/nuxt-vite/src/sprite/.gitignore | 2 + .../apps/nuxt-vite/src/sprite/index.d.ts | 1 + .../apps/nuxt-vite/src/sprite/index.js | 1 + .../nuxt-vite/src/sprite/svg-sprite.config.ts | 8 + integration/apps/nuxt-webpack/app/app.vue | 37 + .../app/components/SpriteViewer.client.vue | 16 + .../app/generated-contract.typecheck.ts | 15 + integration/apps/nuxt-webpack/nuxt.config.ts | 28 + integration/apps/nuxt-webpack/package.json | 25 + .../apps/nuxt-webpack/src/sprite/.gitignore | 2 + .../apps/nuxt-webpack/src/sprite/index.d.ts | 1 + .../apps/nuxt-webpack/src/sprite/index.js | 1 + .../src/sprite/svg-sprite.config.ts | 8 + integration/apps/preact-vite/index.html | 12 + integration/apps/preact-vite/package.json | 20 + .../src/generated-contract.typecheck.ts | 13 + integration/apps/preact-vite/src/main.js | 34 + .../apps/preact-vite/src/sprite/.gitignore | 2 + .../apps/preact-vite/src/sprite/index.d.ts | 1 + .../apps/preact-vite/src/sprite/index.js | 1 + .../src/sprite/svg-sprite.config.ts | 8 + integration/apps/preact-vite/src/style.css | 15 + integration/apps/preact-vite/tsconfig.json | 12 + integration/apps/preact-vite/vite.config.ts | 3 + integration/apps/preact-webpack/package.json | 25 + .../src/generated-contract.typecheck.ts | 13 + integration/apps/preact-webpack/src/main.js | 34 + .../apps/preact-webpack/src/sprite/.gitignore | 2 + .../apps/preact-webpack/src/sprite/index.d.ts | 1 + .../apps/preact-webpack/src/sprite/index.js | 1 + .../src/sprite/svg-sprite.config.ts | 8 + integration/apps/preact-webpack/src/style.css | 15 + integration/apps/preact-webpack/tsconfig.json | 12 + .../apps/preact-webpack/webpack.config.js | 40 + integration/apps/qwik-vite/index.html | 11 + integration/apps/qwik-vite/package.json | 20 + integration/apps/qwik-vite/src/entry.dev.jsx | 5 + integration/apps/qwik-vite/src/entry.ssr.jsx | 7 + .../src/generated-contract.typecheck.ts | 13 + integration/apps/qwik-vite/src/root.jsx | 32 + .../apps/qwik-vite/src/sprite/.gitignore | 2 + .../apps/qwik-vite/src/sprite/index.d.ts | 1 + .../apps/qwik-vite/src/sprite/index.js | 1 + .../qwik-vite/src/sprite/svg-sprite.config.ts | 8 + integration/apps/qwik-vite/src/style.css | 14 + integration/apps/qwik-vite/tsconfig.json | 14 + integration/apps/qwik-vite/vite.config.ts | 6 + .../build/client/_build/.vite/manifest.json | 73 + .../client/_build/assets/client-BDFkFOh8.js | 2 + .../client/_build/assets/client-BVONibDl.css | 1 + .../client/_build/assets/index-B1AR4FGy.js | 1 + .../client/_build/assets/index-BWUdTtCi.css | 1 + .../client/_build/assets/index-DgFw7Fi1.js | 2 + .../_build/assets/preload-helper-C_-y6sQU.js | 1 + .../client/_build/assets/sprite-DvsqBNon.svg | 3 + .../assets/svg-sprite.manifest-ByXNchEI.js | 1 + .../_build/assets/viewer-element-BO6fulPI.js | 602 ++ .../server-fns/_server/.vite/manifest.json | 79 + .../_server/assets/app-BVONibDl.css | 1 + .../server-fns/_server/assets/app-Bgr52ZUm.js | 1 + .../_server/assets/index-AIk5O37p.js | 1 + .../_server/assets/index-BWUdTtCi.css | 1 + .../_server/assets/server-fns-XxvbTHGR.js | 1 + .../_server/assets/sprite-DvsqBNon.svg | 3 + .../assets/svg-sprite.manifest-D0DHYqq2.js | 1 + .../_server/assets/viewer-element-BO6fulPI.js | 602 ++ .../.vinxi/build/server-fns/_server/index.js | 1 + .../build/server-fns/_server/server-fns.js | 1 + .../.vinxi/build/ssr/.vite/manifest.json | 62 + .../build/ssr/assets/index-BWUdTtCi.css | 1 + .../.vinxi/build/ssr/assets/index-CV0w8CD4.js | 1 + .../build/ssr/assets/sprite-DvsqBNon.svg | 3 + .../.vinxi/build/ssr/assets/ssr-BVONibDl.css | 1 + .../assets/svg-sprite.manifest-Di7XhmOO.js | 1 + .../ssr/assets/viewer-element-BO6fulPI.js | 602 ++ .../.vinxi/build/ssr/index.js | 1 + .../solid-start-vite/.vinxi/build/ssr/ssr.js | 1 + .../.vinxi/types/nitro-config.d.ts | 16 + .../.vinxi/types/nitro-imports.d.ts | 1 + .../.vinxi/types/nitro-routes.d.ts | 14 + .../solid-start-vite/.vinxi/types/nitro.d.ts | 3 + .../.vinxi/types/tsconfig.json | 38 + .../apps/solid-start-vite/app.config.ts | 3 + .../apps/solid-start-vite/package.json | 22 + integration/apps/solid-start-vite/src/app.jsx | 13 + .../solid-start-vite/src/entry-client.jsx | 3 + .../solid-start-vite/src/entry-server.jsx | 20 + .../src/generated-contract.typecheck.ts | 13 + .../solid-start-vite/src/routes/index.jsx | 31 + .../solid-start-vite/src/sprite/.gitignore | 2 + .../solid-start-vite/src/sprite/index.d.ts | 1 + .../apps/solid-start-vite/src/sprite/index.js | 1 + .../src/sprite/svg-sprite.config.ts | 8 + .../apps/solid-start-vite/src/style.css | 14 + .../apps/solid-start-vite/tsconfig.json | 15 + integration/apps/solid-vite/index.html | 2 +- integration/apps/solid-vite/package.json | 2 +- .../src/generated-contract.typecheck.ts | 13 + integration/apps/solid-vite/src/main.jsx | 35 + integration/apps/solid-vite/src/main.tsx | 21 - .../apps/solid-vite/src/sprite/.gitignore | 2 + .../apps/solid-vite/src/sprite/index.d.ts | 1 + .../apps/solid-vite/src/sprite/index.js | 1 + .../src/sprite/svg-sprite.config.ts | 8 + integration/apps/solid-vite/src/style.css | 5 +- .../apps/solid-vite/svg-sprites.config.ts | 11 - integration/apps/solid-webpack/package.json | 27 + .../src/generated-contract.typecheck.ts | 13 + integration/apps/solid-webpack/src/main.jsx | 36 + .../apps/solid-webpack/src/sprite/.gitignore | 2 + .../apps/solid-webpack/src/sprite/index.d.ts | 1 + .../apps/solid-webpack/src/sprite/index.js | 1 + .../src/sprite/svg-sprite.config.ts | 8 + integration/apps/solid-webpack/src/style.css | 15 + integration/apps/solid-webpack/tsconfig.json | 12 + .../apps/solid-webpack/webpack.config.js | 46 + integration/apps/svelte-vite/index.html | 2 +- integration/apps/svelte-vite/package.json | 2 +- integration/apps/svelte-vite/src/App.svelte | 37 +- integration/apps/svelte-vite/src/app.d.ts | 6 - integration/apps/svelte-vite/src/main.js | 10 + integration/apps/svelte-vite/src/main.ts | 7 - .../apps/svelte-vite/src/sprite/.gitignore | 2 + .../apps/svelte-vite/src/sprite/index.d.ts | 1 + .../apps/svelte-vite/src/sprite/index.js | 1 + .../src/sprite/svg-sprite.config.ts | 8 + .../apps/svelte-vite/src/type-probe.ts | 9 + .../apps/svelte-vite/svg-sprites.config.ts | 11 - integration/apps/svelte-vite/tsconfig.json | 4 +- integration/apps/svelte-webpack/package.json | 25 + .../apps/svelte-webpack/src/App.svelte | 49 + integration/apps/svelte-webpack/src/main.js | 10 + .../apps/svelte-webpack/src/sprite/.gitignore | 2 + .../apps/svelte-webpack/src/sprite/index.d.ts | 1 + .../apps/svelte-webpack/src/sprite/index.js | 1 + .../src/sprite/svg-sprite.config.ts | 8 + .../apps/svelte-webpack/src/type-probe.ts | 9 + integration/apps/svelte-webpack/tsconfig.json | 14 + .../apps/svelte-webpack/webpack.config.js | 40 + integration/apps/sveltekit/package.json | 4 +- .../apps/sveltekit/src/routes/+page.svelte | 43 +- .../apps/sveltekit/src/sprite/.gitignore | 2 + .../apps/sveltekit/src/sprite/index.d.ts | 1 + .../apps/sveltekit/src/sprite/index.js | 1 + .../sveltekit/src/sprite/svg-sprite.config.ts | 8 + integration/apps/sveltekit/src/type-probe.ts | 9 + .../apps/sveltekit/svg-sprites.config.ts | 11 - integration/apps/vue-vite/index.html | 2 +- integration/apps/vue-vite/package.json | 2 +- integration/apps/vue-vite/src/App.vue | 32 +- .../src/generated-contract.typecheck.ts | 9 + .../apps/vue-vite/src/{main.ts => main.js} | 0 .../apps/vue-vite/src/sprite/.gitignore | 2 + .../apps/vue-vite/src/sprite/index.d.ts | 1 + integration/apps/vue-vite/src/sprite/index.js | 1 + .../vue-vite/src/sprite/svg-sprite.config.ts | 8 + .../apps/vue-vite/svg-sprites.config.ts | 11 - integration/apps/vue-vite/vite.config.ts | 10 +- integration/apps/vue-webpack/package.json | 28 + integration/apps/vue-webpack/src/App.vue | 46 + .../src/generated-contract.typecheck.ts | 15 + integration/apps/vue-webpack/src/main.js | 5 + .../apps/vue-webpack/src/sprite/.gitignore | 2 + .../apps/vue-webpack/src/sprite/index.d.ts | 1 + .../apps/vue-webpack/src/sprite/index.js | 1 + .../src/sprite/svg-sprite.config.ts | 8 + integration/apps/vue-webpack/tsconfig.json | 13 + .../apps/vue-webpack/webpack.config.js | 60 + integration/package-lock.json | 6705 ++++++++++++++++- integration/scripts/apps.mjs | 32 +- integration/tests/apps.spec.ts | 16 +- package-lock.json | 15 +- package.json | 16 +- skills/svg-sprites/skill.config.mjs | 4 +- skills/svg-sprites/src/en/SKILL.md | 68 +- skills/svg-sprites/src/ru/SKILL.md | 68 +- src/cli/parse-args.ts | 20 + src/config.ts | 20 + src/core/mode-adapter.ts | 7 +- src/index.ts | 11 + src/mode-registry.ts | 40 + src/modes/alpine-vite/adapter.ts | 25 + src/modes/alpine-vite/output.ts | 272 + src/modes/alpine-webpack/adapter.ts | 25 + src/modes/alpine-webpack/output.ts | 273 + src/modes/angular-application/adapter.ts | 36 + src/modes/angular-application/output.ts | 242 + src/modes/angular-webpack/adapter.ts | 36 + src/modes/angular-webpack/output.ts | 230 + src/modes/astro-vite/adapter.ts | 36 + src/modes/astro-vite/output.ts | 238 + src/modes/lit-vite/adapter.ts | 25 + src/modes/lit-vite/output.ts | 258 + src/modes/lit-webpack/adapter.ts | 25 + src/modes/lit-webpack/output.ts | 259 + src/modes/nuxt-vite/adapter.ts | 25 + src/modes/nuxt-vite/output.ts | 241 + src/modes/nuxt-webpack/adapter.ts | 25 + src/modes/nuxt-webpack/output.ts | 241 + src/modes/preact-vite/adapter.ts | 25 + src/modes/preact-vite/output.ts | 188 + src/modes/preact-webpack/adapter.ts | 25 + src/modes/preact-webpack/output.ts | 189 + src/modes/qwik-vite/adapter.ts | 25 + src/modes/qwik-vite/output.ts | 192 + src/modes/solid-start-vite/adapter.ts | 25 + src/modes/solid-start-vite/output.ts | 197 + src/modes/solid-vite/adapter.ts | 25 + src/modes/solid-vite/output.ts | 197 + src/modes/solid-webpack/adapter.ts | 25 + src/modes/solid-webpack/output.ts | 198 + src/modes/svelte-vite/adapter.ts | 27 + src/modes/svelte-vite/output.ts | 229 + src/modes/svelte-webpack/adapter.ts | 27 + src/modes/svelte-webpack/output.ts | 229 + src/modes/sveltekit-vite/adapter.ts | 27 + src/modes/sveltekit-vite/output.ts | 229 + src/modes/vue-vite/adapter.ts | 27 + src/modes/vue-vite/output.ts | 301 + src/modes/vue-webpack/adapter.ts | 25 + src/modes/vue-webpack/output.ts | 242 + src/targets/types.ts | 34 +- src/viewer/code.ts | 49 +- src/viewer/element.ts | 2 +- src/viewer/types.ts | 19 +- test/angular-astro-mode-contracts.test.mjs | 162 + test/documentation-guides.test.mjs | 27 + test/lit-alpine-mode-contracts.test.mjs | 90 + test/preact-modes.test.mjs | 52 + test/qwik-vite-mode.test.mjs | 43 + test/solid-modes.test.mjs | 59 + test/svelte-modes.test.mjs | 69 + test/vue-nuxt-mode-family.test.mjs | 85 + test/vue-vite-mode.test.mjs | 62 + tsup.browser.config.ts | 14 + tsup.config.ts | 3 - tsup.viewer.config.ts | 3 +- 378 files changed, 21188 insertions(+), 650 deletions(-) create mode 100644 MODES_CHECKLIST.md create mode 100644 docs/en/guides/alpine-vite.md create mode 100644 docs/en/guides/alpine-webpack.md create mode 100644 docs/en/guides/angular-application.md create mode 100644 docs/en/guides/angular-webpack.md create mode 100644 docs/en/guides/astro-vite.md create mode 100644 docs/en/guides/lit-vite.md create mode 100644 docs/en/guides/lit-webpack.md create mode 100644 docs/en/guides/nuxt-vite.md create mode 100644 docs/en/guides/nuxt-webpack.md create mode 100644 docs/en/guides/preact-vite.md create mode 100644 docs/en/guides/preact-webpack.md create mode 100644 docs/en/guides/qwik-vite.md create mode 100644 docs/en/guides/solid-start-vite.md create mode 100644 docs/en/guides/solid-vite.md create mode 100644 docs/en/guides/solid-webpack.md create mode 100644 docs/en/guides/svelte-vite.md create mode 100644 docs/en/guides/svelte-webpack.md create mode 100644 docs/en/guides/sveltekit-vite.md create mode 100644 docs/en/guides/vue-vite.md create mode 100644 docs/en/guides/vue-webpack.md create mode 100644 docs/ru/guides/alpine-vite.md create mode 100644 docs/ru/guides/alpine-webpack.md create mode 100644 docs/ru/guides/angular-application.md create mode 100644 docs/ru/guides/angular-webpack.md create mode 100644 docs/ru/guides/astro-vite.md create mode 100644 docs/ru/guides/lit-vite.md create mode 100644 docs/ru/guides/lit-webpack.md create mode 100644 docs/ru/guides/nuxt-vite.md create mode 100644 docs/ru/guides/nuxt-webpack.md create mode 100644 docs/ru/guides/preact-vite.md create mode 100644 docs/ru/guides/preact-webpack.md create mode 100644 docs/ru/guides/qwik-vite.md create mode 100644 docs/ru/guides/solid-start-vite.md create mode 100644 docs/ru/guides/solid-vite.md create mode 100644 docs/ru/guides/solid-webpack.md create mode 100644 docs/ru/guides/svelte-vite.md create mode 100644 docs/ru/guides/svelte-webpack.md create mode 100644 docs/ru/guides/sveltekit-vite.md create mode 100644 docs/ru/guides/vue-vite.md create mode 100644 docs/ru/guides/vue-webpack.md create mode 100644 integration/apps/alpine-vite/index.html create mode 100644 integration/apps/alpine-vite/package.json create mode 100644 integration/apps/alpine-vite/src/generated-contract.typecheck.ts create mode 100644 integration/apps/alpine-vite/src/main.js create mode 100644 integration/apps/alpine-vite/src/sprite/.gitignore create mode 100644 integration/apps/alpine-vite/src/sprite/index.d.ts create mode 100644 integration/apps/alpine-vite/src/sprite/index.js create mode 100644 integration/apps/alpine-vite/src/sprite/svg-sprite.config.json create mode 100644 integration/apps/alpine-vite/tsconfig.json create mode 100644 integration/apps/alpine-webpack/index.html create mode 100644 integration/apps/alpine-webpack/package.json create mode 100644 integration/apps/alpine-webpack/src/generated-contract.typecheck.ts create mode 100644 integration/apps/alpine-webpack/src/main.js create mode 100644 integration/apps/alpine-webpack/src/sprite/.gitignore create mode 100644 integration/apps/alpine-webpack/src/sprite/index.d.ts create mode 100644 integration/apps/alpine-webpack/src/sprite/index.js create mode 100644 integration/apps/alpine-webpack/src/sprite/svg-sprite.config.json create mode 100644 integration/apps/alpine-webpack/tsconfig.json create mode 100644 integration/apps/alpine-webpack/webpack.config.js create mode 100644 integration/apps/angular-webpack/package.json create mode 100644 integration/apps/angular-webpack/src/index.html create mode 100644 integration/apps/angular-webpack/src/main.ts create mode 100644 integration/apps/angular-webpack/src/sprite/.gitignore create mode 100644 integration/apps/angular-webpack/src/sprite/index.ts create mode 100644 integration/apps/angular-webpack/src/sprite/svg-sprite.config.ts create mode 100644 integration/apps/angular-webpack/src/type-probe.ts create mode 100644 integration/apps/angular-webpack/tsconfig.json create mode 100644 integration/apps/angular-webpack/webpack.config.js create mode 100644 integration/apps/angular/src/sprite/.gitignore create mode 100644 integration/apps/angular/src/sprite/index.ts create mode 100644 integration/apps/angular/src/sprite/svg-sprite.config.ts create mode 100644 integration/apps/angular/src/type-probe.ts delete mode 100644 integration/apps/angular/svg-sprites.config.ts create mode 100644 integration/apps/astro/src/sprite/.gitignore create mode 100644 integration/apps/astro/src/sprite/index.d.ts create mode 100644 integration/apps/astro/src/sprite/index.js create mode 100644 integration/apps/astro/src/sprite/svg-sprite.config.ts create mode 100644 integration/apps/astro/src/type-probe.ts delete mode 100644 integration/apps/astro/svg-sprites.config.ts create mode 100644 integration/apps/lit-vite/index.html create mode 100644 integration/apps/lit-vite/package.json create mode 100644 integration/apps/lit-vite/src/generated-contract.typecheck.ts create mode 100644 integration/apps/lit-vite/src/main.js create mode 100644 integration/apps/lit-vite/src/sprite/.gitignore create mode 100644 integration/apps/lit-vite/src/sprite/index.d.ts create mode 100644 integration/apps/lit-vite/src/sprite/index.js create mode 100644 integration/apps/lit-vite/src/sprite/svg-sprite.config.json create mode 100644 integration/apps/lit-vite/tsconfig.json create mode 100644 integration/apps/lit-webpack/index.html create mode 100644 integration/apps/lit-webpack/package.json create mode 100644 integration/apps/lit-webpack/src/generated-contract.typecheck.ts create mode 100644 integration/apps/lit-webpack/src/main.js create mode 100644 integration/apps/lit-webpack/src/sprite/.gitignore create mode 100644 integration/apps/lit-webpack/src/sprite/index.d.ts create mode 100644 integration/apps/lit-webpack/src/sprite/index.js create mode 100644 integration/apps/lit-webpack/src/sprite/svg-sprite.config.json create mode 100644 integration/apps/lit-webpack/tsconfig.json create mode 100644 integration/apps/lit-webpack/webpack.config.js create mode 100644 integration/apps/nuxt-vite/app/app.vue create mode 100644 integration/apps/nuxt-vite/app/components/SpriteViewer.client.vue create mode 100644 integration/apps/nuxt-vite/app/generated-contract.typecheck.ts create mode 100644 integration/apps/nuxt-vite/nuxt.config.ts create mode 100644 integration/apps/nuxt-vite/package.json create mode 100644 integration/apps/nuxt-vite/src/sprite/.gitignore create mode 100644 integration/apps/nuxt-vite/src/sprite/index.d.ts create mode 100644 integration/apps/nuxt-vite/src/sprite/index.js create mode 100644 integration/apps/nuxt-vite/src/sprite/svg-sprite.config.ts create mode 100644 integration/apps/nuxt-webpack/app/app.vue create mode 100644 integration/apps/nuxt-webpack/app/components/SpriteViewer.client.vue create mode 100644 integration/apps/nuxt-webpack/app/generated-contract.typecheck.ts create mode 100644 integration/apps/nuxt-webpack/nuxt.config.ts create mode 100644 integration/apps/nuxt-webpack/package.json create mode 100644 integration/apps/nuxt-webpack/src/sprite/.gitignore create mode 100644 integration/apps/nuxt-webpack/src/sprite/index.d.ts create mode 100644 integration/apps/nuxt-webpack/src/sprite/index.js create mode 100644 integration/apps/nuxt-webpack/src/sprite/svg-sprite.config.ts create mode 100644 integration/apps/preact-vite/index.html create mode 100644 integration/apps/preact-vite/package.json create mode 100644 integration/apps/preact-vite/src/generated-contract.typecheck.ts create mode 100644 integration/apps/preact-vite/src/main.js create mode 100644 integration/apps/preact-vite/src/sprite/.gitignore create mode 100644 integration/apps/preact-vite/src/sprite/index.d.ts create mode 100644 integration/apps/preact-vite/src/sprite/index.js create mode 100644 integration/apps/preact-vite/src/sprite/svg-sprite.config.ts create mode 100644 integration/apps/preact-vite/src/style.css create mode 100644 integration/apps/preact-vite/tsconfig.json create mode 100644 integration/apps/preact-vite/vite.config.ts create mode 100644 integration/apps/preact-webpack/package.json create mode 100644 integration/apps/preact-webpack/src/generated-contract.typecheck.ts create mode 100644 integration/apps/preact-webpack/src/main.js create mode 100644 integration/apps/preact-webpack/src/sprite/.gitignore create mode 100644 integration/apps/preact-webpack/src/sprite/index.d.ts create mode 100644 integration/apps/preact-webpack/src/sprite/index.js create mode 100644 integration/apps/preact-webpack/src/sprite/svg-sprite.config.ts create mode 100644 integration/apps/preact-webpack/src/style.css create mode 100644 integration/apps/preact-webpack/tsconfig.json create mode 100644 integration/apps/preact-webpack/webpack.config.js create mode 100644 integration/apps/qwik-vite/index.html create mode 100644 integration/apps/qwik-vite/package.json create mode 100644 integration/apps/qwik-vite/src/entry.dev.jsx create mode 100644 integration/apps/qwik-vite/src/entry.ssr.jsx create mode 100644 integration/apps/qwik-vite/src/generated-contract.typecheck.ts create mode 100644 integration/apps/qwik-vite/src/root.jsx create mode 100644 integration/apps/qwik-vite/src/sprite/.gitignore create mode 100644 integration/apps/qwik-vite/src/sprite/index.d.ts create mode 100644 integration/apps/qwik-vite/src/sprite/index.js create mode 100644 integration/apps/qwik-vite/src/sprite/svg-sprite.config.ts create mode 100644 integration/apps/qwik-vite/src/style.css create mode 100644 integration/apps/qwik-vite/tsconfig.json create mode 100644 integration/apps/qwik-vite/vite.config.ts create mode 100644 integration/apps/solid-start-vite/.vinxi/build/client/_build/.vite/manifest.json create mode 100644 integration/apps/solid-start-vite/.vinxi/build/client/_build/assets/client-BDFkFOh8.js create mode 100644 integration/apps/solid-start-vite/.vinxi/build/client/_build/assets/client-BVONibDl.css create mode 100644 integration/apps/solid-start-vite/.vinxi/build/client/_build/assets/index-B1AR4FGy.js create mode 100644 integration/apps/solid-start-vite/.vinxi/build/client/_build/assets/index-BWUdTtCi.css create mode 100644 integration/apps/solid-start-vite/.vinxi/build/client/_build/assets/index-DgFw7Fi1.js create mode 100644 integration/apps/solid-start-vite/.vinxi/build/client/_build/assets/preload-helper-C_-y6sQU.js create mode 100644 integration/apps/solid-start-vite/.vinxi/build/client/_build/assets/sprite-DvsqBNon.svg create mode 100644 integration/apps/solid-start-vite/.vinxi/build/client/_build/assets/svg-sprite.manifest-ByXNchEI.js create mode 100644 integration/apps/solid-start-vite/.vinxi/build/client/_build/assets/viewer-element-BO6fulPI.js create mode 100644 integration/apps/solid-start-vite/.vinxi/build/server-fns/_server/.vite/manifest.json create mode 100644 integration/apps/solid-start-vite/.vinxi/build/server-fns/_server/assets/app-BVONibDl.css create mode 100644 integration/apps/solid-start-vite/.vinxi/build/server-fns/_server/assets/app-Bgr52ZUm.js create mode 100644 integration/apps/solid-start-vite/.vinxi/build/server-fns/_server/assets/index-AIk5O37p.js create mode 100644 integration/apps/solid-start-vite/.vinxi/build/server-fns/_server/assets/index-BWUdTtCi.css create mode 100644 integration/apps/solid-start-vite/.vinxi/build/server-fns/_server/assets/server-fns-XxvbTHGR.js create mode 100644 integration/apps/solid-start-vite/.vinxi/build/server-fns/_server/assets/sprite-DvsqBNon.svg create mode 100644 integration/apps/solid-start-vite/.vinxi/build/server-fns/_server/assets/svg-sprite.manifest-D0DHYqq2.js create mode 100644 integration/apps/solid-start-vite/.vinxi/build/server-fns/_server/assets/viewer-element-BO6fulPI.js create mode 100644 integration/apps/solid-start-vite/.vinxi/build/server-fns/_server/index.js create mode 100644 integration/apps/solid-start-vite/.vinxi/build/server-fns/_server/server-fns.js create mode 100644 integration/apps/solid-start-vite/.vinxi/build/ssr/.vite/manifest.json create mode 100644 integration/apps/solid-start-vite/.vinxi/build/ssr/assets/index-BWUdTtCi.css create mode 100644 integration/apps/solid-start-vite/.vinxi/build/ssr/assets/index-CV0w8CD4.js create mode 100644 integration/apps/solid-start-vite/.vinxi/build/ssr/assets/sprite-DvsqBNon.svg create mode 100644 integration/apps/solid-start-vite/.vinxi/build/ssr/assets/ssr-BVONibDl.css create mode 100644 integration/apps/solid-start-vite/.vinxi/build/ssr/assets/svg-sprite.manifest-Di7XhmOO.js create mode 100644 integration/apps/solid-start-vite/.vinxi/build/ssr/assets/viewer-element-BO6fulPI.js create mode 100644 integration/apps/solid-start-vite/.vinxi/build/ssr/index.js create mode 100644 integration/apps/solid-start-vite/.vinxi/build/ssr/ssr.js create mode 100644 integration/apps/solid-start-vite/.vinxi/types/nitro-config.d.ts create mode 100644 integration/apps/solid-start-vite/.vinxi/types/nitro-imports.d.ts create mode 100644 integration/apps/solid-start-vite/.vinxi/types/nitro-routes.d.ts create mode 100644 integration/apps/solid-start-vite/.vinxi/types/nitro.d.ts create mode 100644 integration/apps/solid-start-vite/.vinxi/types/tsconfig.json create mode 100644 integration/apps/solid-start-vite/app.config.ts create mode 100644 integration/apps/solid-start-vite/package.json create mode 100644 integration/apps/solid-start-vite/src/app.jsx create mode 100644 integration/apps/solid-start-vite/src/entry-client.jsx create mode 100644 integration/apps/solid-start-vite/src/entry-server.jsx create mode 100644 integration/apps/solid-start-vite/src/generated-contract.typecheck.ts create mode 100644 integration/apps/solid-start-vite/src/routes/index.jsx create mode 100644 integration/apps/solid-start-vite/src/sprite/.gitignore create mode 100644 integration/apps/solid-start-vite/src/sprite/index.d.ts create mode 100644 integration/apps/solid-start-vite/src/sprite/index.js create mode 100644 integration/apps/solid-start-vite/src/sprite/svg-sprite.config.ts create mode 100644 integration/apps/solid-start-vite/src/style.css create mode 100644 integration/apps/solid-start-vite/tsconfig.json create mode 100644 integration/apps/solid-vite/src/generated-contract.typecheck.ts create mode 100644 integration/apps/solid-vite/src/main.jsx delete mode 100644 integration/apps/solid-vite/src/main.tsx create mode 100644 integration/apps/solid-vite/src/sprite/.gitignore create mode 100644 integration/apps/solid-vite/src/sprite/index.d.ts create mode 100644 integration/apps/solid-vite/src/sprite/index.js create mode 100644 integration/apps/solid-vite/src/sprite/svg-sprite.config.ts delete mode 100644 integration/apps/solid-vite/svg-sprites.config.ts create mode 100644 integration/apps/solid-webpack/package.json create mode 100644 integration/apps/solid-webpack/src/generated-contract.typecheck.ts create mode 100644 integration/apps/solid-webpack/src/main.jsx create mode 100644 integration/apps/solid-webpack/src/sprite/.gitignore create mode 100644 integration/apps/solid-webpack/src/sprite/index.d.ts create mode 100644 integration/apps/solid-webpack/src/sprite/index.js create mode 100644 integration/apps/solid-webpack/src/sprite/svg-sprite.config.ts create mode 100644 integration/apps/solid-webpack/src/style.css create mode 100644 integration/apps/solid-webpack/tsconfig.json create mode 100644 integration/apps/solid-webpack/webpack.config.js delete mode 100644 integration/apps/svelte-vite/src/app.d.ts create mode 100644 integration/apps/svelte-vite/src/main.js delete mode 100644 integration/apps/svelte-vite/src/main.ts create mode 100644 integration/apps/svelte-vite/src/sprite/.gitignore create mode 100644 integration/apps/svelte-vite/src/sprite/index.d.ts create mode 100644 integration/apps/svelte-vite/src/sprite/index.js create mode 100644 integration/apps/svelte-vite/src/sprite/svg-sprite.config.ts create mode 100644 integration/apps/svelte-vite/src/type-probe.ts delete mode 100644 integration/apps/svelte-vite/svg-sprites.config.ts create mode 100644 integration/apps/svelte-webpack/package.json create mode 100644 integration/apps/svelte-webpack/src/App.svelte create mode 100644 integration/apps/svelte-webpack/src/main.js create mode 100644 integration/apps/svelte-webpack/src/sprite/.gitignore create mode 100644 integration/apps/svelte-webpack/src/sprite/index.d.ts create mode 100644 integration/apps/svelte-webpack/src/sprite/index.js create mode 100644 integration/apps/svelte-webpack/src/sprite/svg-sprite.config.ts create mode 100644 integration/apps/svelte-webpack/src/type-probe.ts create mode 100644 integration/apps/svelte-webpack/tsconfig.json create mode 100644 integration/apps/svelte-webpack/webpack.config.js create mode 100644 integration/apps/sveltekit/src/sprite/.gitignore create mode 100644 integration/apps/sveltekit/src/sprite/index.d.ts create mode 100644 integration/apps/sveltekit/src/sprite/index.js create mode 100644 integration/apps/sveltekit/src/sprite/svg-sprite.config.ts create mode 100644 integration/apps/sveltekit/src/type-probe.ts delete mode 100644 integration/apps/sveltekit/svg-sprites.config.ts create mode 100644 integration/apps/vue-vite/src/generated-contract.typecheck.ts rename integration/apps/vue-vite/src/{main.ts => main.js} (100%) create mode 100644 integration/apps/vue-vite/src/sprite/.gitignore create mode 100644 integration/apps/vue-vite/src/sprite/index.d.ts create mode 100644 integration/apps/vue-vite/src/sprite/index.js create mode 100644 integration/apps/vue-vite/src/sprite/svg-sprite.config.ts delete mode 100644 integration/apps/vue-vite/svg-sprites.config.ts create mode 100644 integration/apps/vue-webpack/package.json create mode 100644 integration/apps/vue-webpack/src/App.vue create mode 100644 integration/apps/vue-webpack/src/generated-contract.typecheck.ts create mode 100644 integration/apps/vue-webpack/src/main.js create mode 100644 integration/apps/vue-webpack/src/sprite/.gitignore create mode 100644 integration/apps/vue-webpack/src/sprite/index.d.ts create mode 100644 integration/apps/vue-webpack/src/sprite/index.js create mode 100644 integration/apps/vue-webpack/src/sprite/svg-sprite.config.ts create mode 100644 integration/apps/vue-webpack/tsconfig.json create mode 100644 integration/apps/vue-webpack/webpack.config.js create mode 100644 src/modes/alpine-vite/adapter.ts create mode 100644 src/modes/alpine-vite/output.ts create mode 100644 src/modes/alpine-webpack/adapter.ts create mode 100644 src/modes/alpine-webpack/output.ts create mode 100644 src/modes/angular-application/adapter.ts create mode 100644 src/modes/angular-application/output.ts create mode 100644 src/modes/angular-webpack/adapter.ts create mode 100644 src/modes/angular-webpack/output.ts create mode 100644 src/modes/astro-vite/adapter.ts create mode 100644 src/modes/astro-vite/output.ts create mode 100644 src/modes/lit-vite/adapter.ts create mode 100644 src/modes/lit-vite/output.ts create mode 100644 src/modes/lit-webpack/adapter.ts create mode 100644 src/modes/lit-webpack/output.ts create mode 100644 src/modes/nuxt-vite/adapter.ts create mode 100644 src/modes/nuxt-vite/output.ts create mode 100644 src/modes/nuxt-webpack/adapter.ts create mode 100644 src/modes/nuxt-webpack/output.ts create mode 100644 src/modes/preact-vite/adapter.ts create mode 100644 src/modes/preact-vite/output.ts create mode 100644 src/modes/preact-webpack/adapter.ts create mode 100644 src/modes/preact-webpack/output.ts create mode 100644 src/modes/qwik-vite/adapter.ts create mode 100644 src/modes/qwik-vite/output.ts create mode 100644 src/modes/solid-start-vite/adapter.ts create mode 100644 src/modes/solid-start-vite/output.ts create mode 100644 src/modes/solid-vite/adapter.ts create mode 100644 src/modes/solid-vite/output.ts create mode 100644 src/modes/solid-webpack/adapter.ts create mode 100644 src/modes/solid-webpack/output.ts create mode 100644 src/modes/svelte-vite/adapter.ts create mode 100644 src/modes/svelte-vite/output.ts create mode 100644 src/modes/svelte-webpack/adapter.ts create mode 100644 src/modes/svelte-webpack/output.ts create mode 100644 src/modes/sveltekit-vite/adapter.ts create mode 100644 src/modes/sveltekit-vite/output.ts create mode 100644 src/modes/vue-vite/adapter.ts create mode 100644 src/modes/vue-vite/output.ts create mode 100644 src/modes/vue-webpack/adapter.ts create mode 100644 src/modes/vue-webpack/output.ts create mode 100644 test/angular-astro-mode-contracts.test.mjs create mode 100644 test/lit-alpine-mode-contracts.test.mjs create mode 100644 test/preact-modes.test.mjs create mode 100644 test/qwik-vite-mode.test.mjs create mode 100644 test/solid-modes.test.mjs create mode 100644 test/svelte-modes.test.mjs create mode 100644 test/vue-nuxt-mode-family.test.mjs create mode 100644 test/vue-vite-mode.test.mjs create mode 100644 tsup.browser.config.ts diff --git a/AGENTS.md b/AGENTS.md index 2c4f586..8fa0545 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -13,7 +13,27 @@ - `standalone`; - `standalone@vite`; - `standalone@webpack`; -- будущие `vue@*` и другие modes. +- `vue@vite`; +- `vue@webpack`; +- `nuxt@vite`; +- `nuxt@webpack`; +- `svelte@vite`; +- `svelte@webpack`; +- `sveltekit@vite`; +- `angular@application`; +- `angular@webpack`; +- `astro@vite`; +- `solid@vite`; +- `solid@webpack`; +- `solid-start@vite`; +- `preact@vite`; +- `preact@webpack`; +- `qwik@vite`; +- `lit@vite`; +- `lit@webpack`; +- `alpine@vite`; +- `alpine@webpack`; +- будущие exact modes. Для каждого exact mode используется отдельный каталог `src/modes//`. Adapter самостоятельно определяет: @@ -59,7 +79,9 @@ Core не генерирует JavaScript, declarations, manifest source, CSS и Один config разрешается ровно в один mode и один output. Множественные modes не генерируются в один root; orchestration выполняется независимыми config/API/CLI вызовами. -Runtime генерируется как ESM JavaScript. Типизация добавляется отдельными `.d.ts`; TypeScript/TSX не используется как runtime output. +Каждый adapter генерирует один нативный для фреймворка runtime-контракт, совместимый со стандартными JavaScript- и TypeScript-конфигурациями этого фреймворка. Предпочтительный output — ESM JavaScript; если framework compiler требует собственный формат, допускаются framework-native контейнеры с JavaScript-синтаксисом (`.jsx`, `.svelte`, `.astro` и аналогичные). `.tsx` и TypeScript runtime запрещены, кроме frameworks вроде Angular, чей штатный production toolchain требует TypeScript. Типизация всегда добавляется отдельными `.d.ts`; отдельные JS- и TS-реализации одного компонента не создаются. + +Integration-стенд должен собирать runtime как JavaScript-потребитель без дополнительной TypeScript-настройки, когда framework это допускает. TypeScript-совместимость того же generated API проверяется отдельным type probe внутри стенда. Framework-native adapter нельзя подменять consumer-примером другого mode или Web Component facade. Core writer полностью владеет каталогом `.svg-sprite` и при каждой генерации заменяет его через временный каталог с rollback при ошибке. Корневым `.gitignore` writer владеет, когда exact-mode adapter запрашивает его через `OutputPlan`. Bare `standalone` не создаёт `.gitignore`; остальные modes создают. diff --git a/FEATURES.md b/FEATURES.md index d15f11c..3a8ac00 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -1,12 +1,12 @@ # Иконки без лишней цены для приложения -`@gromlab/svg-sprites` превращает SVG проекта в кешируемую, типизированную систему иконок для vanilla-приложений, React и Next.js. В коде остаются простые компоненты, а приложение получает преимущества спрайтов без сложной инфраструктуры. +`@gromlab/svg-sprites` превращает SVG проекта в кешируемую, типизированную систему иконок для современных web frameworks. В коде остаются нативные компоненты выбранного стека, а приложение получает преимущества спрайтов без сложной инфраструктуры. 1. **AI-friendly из коробки** `@gromlab/svg-sprites` сразу рассчитан на работу с AI-агентами. Подключите готовый skill и поручите агенту настройку, миграцию или диагностику без длинных инструкций и ручного изучения документации. -2. **Типизированный React-компонент с автокомплитом** +2. **Типизированный нативный компонент с автокомплитом** Каждый спрайт получает собственный готовый компонент. Prop `icon` формируется из реальных имён SVG, поэтому редактор показывает точный список доступных иконок, а TypeScript сразу обнаруживает опечатки. Не нужно вручную поддерживать компоненты, union-типы или реестр имён. @@ -40,11 +40,11 @@ 10. **SpriteViewer: все спрайты на одной debug-странице** - `SpriteViewer` рендерит все спрайты проекта в одном месте и показывает, какие иконки вошли в каждый набор и как они выглядят. Для каждой иконки видны созданные CSS-переменные и их fallback-цвета. Значения можно менять прямо в Viewer и сразу наблюдать результат. Здесь же доступны готовые примеры подключения через React, ``, `` и CSS. + `SpriteViewer` рендерит все спрайты проекта в одном месте и показывает, какие иконки вошли в каждый набор и как они выглядят. Для каждой иконки видны созданные CSS-переменные и их fallback-цвета. Значения можно менять прямо в Viewer и сразу наблюдать результат. Здесь же доступны готовые примеры для framework из manifest, ``, `` и CSS. -11. **Standalone, React и Next.js** +11. **29 exact modes** - Для vanilla-приложений с Vite или Webpack пакет генерирует нативный типизированный Web Component без runtime-зависимостей. Для React и Next.js создаётся React-компонент с поддержкой Vite, Webpack 5, App Router, Pages Router и Turbopack. + Изолированные adapters создают нативные компоненты для standalone, React, Next.js, Vue, Nuxt, Svelte, SvelteKit, Angular, Astro, Solid, SolidStart, Preact, Qwik, Lit и Alpine.js. Каждый exact mode учитывает свой Vite, Webpack, Turbopack или application-builder контракт. 12. **Чистый Git** diff --git a/MODES_CHECKLIST.md b/MODES_CHECKLIST.md new file mode 100644 index 0000000..ff0c2b1 --- /dev/null +++ b/MODES_CHECKLIST.md @@ -0,0 +1,78 @@ +# Exact Mode Checklist + +Mode отмечается поддержанным только после завершения полного вертикального среза: + +- реализован независимый adapter в `src/modes//`; +- добавлен один integration-стенд для exact mode; +- production build стенда успешно рендерит иконку из внешнего SVG-спрайта; +- Viewer на стенде загружает тот же спрайт и проходит адресный E2E; +- добавлены быстрые старты на русском и английском языках; +- ссылки на быстрые старты добавлены в `README_RU.md` и `README.md`. + +## Standalone + +- [x] `standalone` +- [x] `standalone@vite` +- [x] `standalone@webpack` + +## React + +- [x] `react@vite` +- [x] `react@webpack` + +## Next.js + +- [x] `next@app/turbopack` +- [x] `next@app/webpack` +- [x] `next@pages/turbopack` +- [x] `next@pages/webpack` + +## Vue + +- [x] `vue@vite` +- [x] `vue@webpack` + +## Nuxt + +- [x] `nuxt@vite` +- [x] `nuxt@webpack` + +## Svelte + +- [x] `svelte@vite` +- [x] `svelte@webpack` +- [x] `sveltekit@vite` + +## Angular + +- [x] `angular@application` +- [x] `angular@webpack` + +## Astro + +- [x] `astro@vite` + +## Solid + +- [x] `solid@vite` +- [x] `solid@webpack` +- [x] `solid-start@vite` + +## Preact + +- [x] `preact@vite` +- [x] `preact@webpack` + +## Qwik + +- [x] `qwik@vite` + +## Lit + +- [x] `lit@vite` +- [x] `lit@webpack` + +## Alpine.js + +- [x] `alpine@vite` +- [x] `alpine@webpack` diff --git a/README.md b/README.md index 22f9bcb..e1365cf 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ `@gromlab/svg-sprites` is an SVG sprite generator for modern web applications. It combines selected SVG icons into one or more external, cacheable sprites and prepares them for use in the UI. -For vanilla applications using Vite/Webpack, the package generates a native typed Web Component; for React and Next.js, it generates a React component. In every case, the SVG remains a separate cacheable asset. +Each exact mode generates a native typed component for its framework and bundler: Web Component, React, Vue, Svelte, Angular, Astro, Solid, Preact, Qwik, Lit, or Alpine.js. In every case, the SVG remains a separate cacheable asset. ## An SVG sprite as simple as a regular SVG icon @@ -205,26 +205,21 @@ Create themes, states, and hover effects without editing the SVG or making addit ## SpriteViewer: every sprite on one debug page -`SpriteViewer` renders all standalone, React, and Next.js project sprites in one place. One Web Component owns the visuals, while React uses a thin bridge to it. +`SpriteViewer` renders sprites from every supported exact mode in one place. One Web Component owns the visuals, while React also provides a thin bridge to it. For each icon, you can see the generated CSS variables and their fallback colors. Change the values directly in the Viewer and see the result immediately. -It also provides ready-to-use integration examples for: - -- React; -- ``; -- ``; -- CSS. +It also provides ready-to-use examples for the manifest's framework, ``, ``, and CSS. ![SpriteViewer](https://raw.githubusercontent.com/gromlab-ru/svg-sprites/master/preview-image.png) The Viewer is added only to an internal debug page and does not become part of the generated icon components. -With bare standalone, the application loads the Viewer as a browser script and HTML element; bundler modes use the npm entry, while React and Next.js import `SpriteViewer` from `@gromlab/svg-sprites/react`. +With bare standalone, the application loads the Viewer as a browser script and HTML element. Bundler and framework modes use the npm Web Component entry; React and Next.js may instead import the bridge from `@gromlab/svg-sprites/react`. -## Standalone, React, and Next.js +## 29 exact modes -The package generates low-level standalone sprites for static HTML, Vite, and Webpack 5, plus typed React components for React and Next.js. +The package provides isolated contracts for standalone, React, Next.js, Vue, Nuxt, Svelte, SvelteKit, Angular, Astro, Solid, SolidStart, Preact, Qwik, Lit, and Alpine.js across their supported Vite, Webpack, Turbopack, and application-builder variants. ## Clean Git history @@ -249,6 +244,26 @@ This README introduces the project's capabilities and demonstrates the primary u - [Standalone + Webpack 5](docs/en/guides/standalone-webpack.md) - [React + Vite](docs/en/guides/react-vite.md) - [React + Webpack 5](docs/en/guides/react-webpack.md) +- [Vue + Vite](docs/en/guides/vue-vite.md) +- [Vue + Webpack](docs/en/guides/vue-webpack.md) +- [Nuxt + Vite](docs/en/guides/nuxt-vite.md) +- [Nuxt + Webpack](docs/en/guides/nuxt-webpack.md) +- [Svelte + Vite](docs/en/guides/svelte-vite.md) +- [Svelte + Webpack](docs/en/guides/svelte-webpack.md) +- [SvelteKit + Vite](docs/en/guides/sveltekit-vite.md) +- [Angular application builder](docs/en/guides/angular-application.md) +- [Angular + Webpack](docs/en/guides/angular-webpack.md) +- [Astro + Vite](docs/en/guides/astro-vite.md) +- [Solid + Vite](docs/en/guides/solid-vite.md) +- [Solid + Webpack](docs/en/guides/solid-webpack.md) +- [SolidStart + Vite](docs/en/guides/solid-start-vite.md) +- [Preact + Vite](docs/en/guides/preact-vite.md) +- [Preact + Webpack](docs/en/guides/preact-webpack.md) +- [Qwik + Vite](docs/en/guides/qwik-vite.md) +- [Lit + Vite](docs/en/guides/lit-vite.md) +- [Lit + Webpack](docs/en/guides/lit-webpack.md) +- [Alpine.js + Vite](docs/en/guides/alpine-vite.md) +- [Alpine.js + Webpack](docs/en/guides/alpine-webpack.md) - [Next.js App Router + Turbopack](docs/en/guides/next-app-turbopack.md) - [Next.js App Router + Webpack](docs/en/guides/next-app-webpack.md) - [Next.js Pages Router + Turbopack](docs/en/guides/next-pages-turbopack.md) diff --git a/README_RU.md b/README_RU.md index 0fe049f..e0122c4 100644 --- a/README_RU.md +++ b/README_RU.md @@ -6,7 +6,7 @@ `@gromlab/svg-sprites` — генератор SVG-спрайтов для современных веб-приложений. Он собирает выбранные SVG-иконки в один или несколько внешних кешируемых спрайтов и подготавливает их для использования в интерфейсе. -Для vanilla-приложений с Vite/Webpack пакет создаёт нативный типизированный Web Component, а для React и Next.js — React-компонент. SVG во всех случаях остаётся отдельным кешируемым asset. +Каждый exact mode создаёт нативный типизированный компонент для своего framework и bundler: Web Component, React, Vue, Svelte, Angular, Astro, Solid, Preact, Qwik, Lit или Alpine.js. SVG во всех случаях остаётся отдельным кешируемым asset. ## SVG-спрайт так же прост, как обычная SVG-иконка @@ -205,26 +205,21 @@ JavaScript отвечает за интерфейс и поведение, а г ## SpriteViewer: все спрайты на одной debug-странице -`SpriteViewer` рендерит все standalone, React и Next.js спрайты проекта в одном месте. Один Web Component отвечает за визуал, а React использует тонкий bridge к нему. +`SpriteViewer` рендерит спрайты всех поддерживаемых exact modes в одном месте. Один Web Component отвечает за визуал, а для React также доступен тонкий bridge к нему. Для каждой иконки видны созданные CSS-переменные и их fallback-цвета. Значения можно менять прямо в Viewer и сразу наблюдать результат. -Здесь же доступны готовые примеры подключения через: - -- React; -- ``; -- ``; -- CSS. +Здесь же доступны готовые примеры для framework из manifest, ``, `` и CSS. ![SpriteViewer](https://raw.githubusercontent.com/gromlab-ru/svg-sprites/master/preview-image.png) Viewer подключается только к внутренней debug-странице и не становится частью generated-компонентов иконок. -Bare standalone подключает Viewer через browser script и HTML element; bundler modes используют npm entry, а React и Next.js импортируют `SpriteViewer` из `@gromlab/svg-sprites/react`. +Bare standalone подключает Viewer через browser script и HTML element. Bundler и framework modes используют npm entry Web Component; React и Next.js также могут импортировать bridge из `@gromlab/svg-sprites/react`. -## Standalone, React и Next.js +## 29 exact modes -Пакет генерирует низкоуровневые standalone-спрайты для static HTML, Vite и Webpack 5, а также типизированные React-компоненты для React и Next.js. +Пакет предоставляет изолированные контракты для standalone, React, Next.js, Vue, Nuxt, Svelte, SvelteKit, Angular, Astro, Solid, SolidStart, Preact, Qwik, Lit и Alpine.js в поддерживаемых вариантах Vite, Webpack, Turbopack и application builder. ## Чистый Git @@ -249,6 +244,26 @@ README знакомит с возможностями проекта и пока - [Standalone + Webpack 5](docs/ru/guides/standalone-webpack.md) - [React + Vite](docs/ru/guides/react-vite.md) - [React + Webpack 5](docs/ru/guides/react-webpack.md) +- [Vue + Vite](docs/ru/guides/vue-vite.md) +- [Vue + Webpack](docs/ru/guides/vue-webpack.md) +- [Nuxt + Vite](docs/ru/guides/nuxt-vite.md) +- [Nuxt + Webpack](docs/ru/guides/nuxt-webpack.md) +- [Svelte + Vite](docs/ru/guides/svelte-vite.md) +- [Svelte + Webpack](docs/ru/guides/svelte-webpack.md) +- [SvelteKit + Vite](docs/ru/guides/sveltekit-vite.md) +- [Angular application builder](docs/ru/guides/angular-application.md) +- [Angular + Webpack](docs/ru/guides/angular-webpack.md) +- [Astro + Vite](docs/ru/guides/astro-vite.md) +- [Solid + Vite](docs/ru/guides/solid-vite.md) +- [Solid + Webpack](docs/ru/guides/solid-webpack.md) +- [SolidStart + Vite](docs/ru/guides/solid-start-vite.md) +- [Preact + Vite](docs/ru/guides/preact-vite.md) +- [Preact + Webpack](docs/ru/guides/preact-webpack.md) +- [Qwik + Vite](docs/ru/guides/qwik-vite.md) +- [Lit + Vite](docs/ru/guides/lit-vite.md) +- [Lit + Webpack](docs/ru/guides/lit-webpack.md) +- [Alpine.js + Vite](docs/ru/guides/alpine-vite.md) +- [Alpine.js + Webpack](docs/ru/guides/alpine-webpack.md) - [Next.js App Router + Turbopack](docs/ru/guides/next-app-turbopack.md) - [Next.js App Router + Webpack](docs/ru/guides/next-app-webpack.md) - [Next.js Pages Router + Turbopack](docs/ru/guides/next-pages-turbopack.md) diff --git a/docs/en/README.md b/docs/en/README.md index 4adf85e..f319fb3 100644 --- a/docs/en/README.md +++ b/docs/en/README.md @@ -14,6 +14,26 @@ The common format for JSON, JavaScript, and TypeScript config files is described | Vanilla + Webpack 5 | `standalone@webpack` | [Standalone + Webpack](guides/standalone-webpack.md) | | React + Vite | `react@vite` | [React + Vite](guides/react-vite.md) | | React + Webpack 5 | `react@webpack` | [React + Webpack](guides/react-webpack.md) | +| Vue + Vite | `vue@vite` | [Vue + Vite](guides/vue-vite.md) | +| Vue + Webpack | `vue@webpack` | [Vue + Webpack](guides/vue-webpack.md) | +| Nuxt + Vite | `nuxt@vite` | [Nuxt + Vite](guides/nuxt-vite.md) | +| Nuxt + Webpack | `nuxt@webpack` | [Nuxt + Webpack](guides/nuxt-webpack.md) | +| Svelte + Vite | `svelte@vite` | [Svelte + Vite](guides/svelte-vite.md) | +| Svelte + Webpack | `svelte@webpack` | [Svelte + Webpack](guides/svelte-webpack.md) | +| SvelteKit + Vite | `sveltekit@vite` | [SvelteKit + Vite](guides/sveltekit-vite.md) | +| Angular application builder | `angular@application` | [Angular application builder](guides/angular-application.md) | +| Angular + Webpack | `angular@webpack` | [Angular + Webpack](guides/angular-webpack.md) | +| Astro + Vite | `astro@vite` | [Astro + Vite](guides/astro-vite.md) | +| Solid + Vite | `solid@vite` | [Solid + Vite](guides/solid-vite.md) | +| Solid + Webpack | `solid@webpack` | [Solid + Webpack](guides/solid-webpack.md) | +| SolidStart + Vite | `solid-start@vite` | [SolidStart + Vite](guides/solid-start-vite.md) | +| Preact + Vite | `preact@vite` | [Preact + Vite](guides/preact-vite.md) | +| Preact + Webpack | `preact@webpack` | [Preact + Webpack](guides/preact-webpack.md) | +| Qwik + Vite | `qwik@vite` | [Qwik + Vite](guides/qwik-vite.md) | +| Lit + Vite | `lit@vite` | [Lit + Vite](guides/lit-vite.md) | +| Lit + Webpack | `lit@webpack` | [Lit + Webpack](guides/lit-webpack.md) | +| Alpine.js + Vite | `alpine@vite` | [Alpine.js + Vite](guides/alpine-vite.md) | +| Alpine.js + Webpack | `alpine@webpack` | [Alpine.js + Webpack](guides/alpine-webpack.md) | | Next.js App Router + Turbopack | `next@app/turbopack` | [App Router + Turbopack](guides/next-app-turbopack.md) | | Next.js App Router + Webpack | `next@app/webpack` | [App Router + Webpack](guides/next-app-webpack.md) | | Next.js Pages Router + Turbopack | `next@pages/turbopack` | [Pages Router + Turbopack](guides/next-pages-turbopack.md) | diff --git a/docs/en/guides/README.md b/docs/en/guides/README.md index d832d85..3af8914 100644 --- a/docs/en/guides/README.md +++ b/docs/en/guides/README.md @@ -5,6 +5,26 @@ - `standalone@webpack`: [standalone with Webpack](standalone-webpack.md) - `react@vite`: [React with Vite](react-vite.md) - `react@webpack`: [React with Webpack](react-webpack.md) +- `vue@vite`: [Vue with Vite](vue-vite.md) +- `vue@webpack`: [Vue with Webpack](vue-webpack.md) +- `nuxt@vite`: [Nuxt with Vite](nuxt-vite.md) +- `nuxt@webpack`: [Nuxt with Webpack](nuxt-webpack.md) +- `svelte@vite`: [Svelte with Vite](svelte-vite.md) +- `svelte@webpack`: [Svelte with Webpack](svelte-webpack.md) +- `sveltekit@vite`: [SvelteKit with Vite](sveltekit-vite.md) +- `angular@application`: [Angular application builder](angular-application.md) +- `angular@webpack`: [Angular with Webpack](angular-webpack.md) +- `astro@vite`: [Astro with Vite](astro-vite.md) +- `solid@vite`: [Solid with Vite](solid-vite.md) +- `solid@webpack`: [Solid with Webpack](solid-webpack.md) +- `solid-start@vite`: [SolidStart with Vite](solid-start-vite.md) +- `preact@vite`: [Preact with Vite](preact-vite.md) +- `preact@webpack`: [Preact with Webpack](preact-webpack.md) +- `qwik@vite`: [Qwik with Vite](qwik-vite.md) +- `lit@vite`: [Lit with Vite](lit-vite.md) +- `lit@webpack`: [Lit with Webpack](lit-webpack.md) +- `alpine@vite`: [Alpine.js with Vite](alpine-vite.md) +- `alpine@webpack`: [Alpine.js with Webpack](alpine-webpack.md) - `next@app/turbopack`: [App Router with Turbopack](next-app-turbopack.md) - `next@app/webpack`: [App Router with Webpack](next-app-webpack.md) - `next@pages/turbopack`: [Pages Router with Turbopack](next-pages-turbopack.md) diff --git a/docs/en/guides/alpine-vite.md b/docs/en/guides/alpine-vite.md new file mode 100644 index 0000000..9e20b7d --- /dev/null +++ b/docs/en/guides/alpine-vite.md @@ -0,0 +1,91 @@ +# SVG Sprite for Alpine.js with Vite + +A quick guide to creating an SVG sprite in an Alpine.js application built with Vite. + +## Generate the sprite + +Choose a directory for the future SVG sprite, for example `assets/app-icons`, and create `svg-sprite.config.json` inside it. In `input`, specify the path to existing SVG files relative to the configuration file. There is no need to move or copy the icons. + +```json +{ + "mode": "alpine@vite", + "name": "app", + "input": "../svg-icons/**/*.svg" +} +``` + +The generator does not need to be added to the application dependencies: run it through `npx`. + +Add generation before development and production builds: + +```json +{ + "scripts": { + "sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json", + "predev": "npm run sprites", + "dev": "vite", + "prebuild": "npm run sprites", + "build": "vite build" + } +} +``` + +## Use the sprite + +The value `name: "app"` creates the Alpine plugin `appAlpinePlugin`, the `x-app-icon` directive, and the `$appIconHref` magic. + +Create `assets/app-icons/index.js`: + +```js +export * from './.svg-sprite/index.js' +``` + +Register the generated plugin before starting Alpine: + +```js +import Alpine from 'alpinejs' +import { appAlpinePlugin } from '../assets/app-icons/index.js' + +Alpine.plugin(appAlpinePlugin) +Alpine.start() +``` + +Use the reactive directive on an SVG element: + +```html + +``` + +The directive expression resolves to a source SVG file name without the extension. A monochrome icon inherits `color`; override multicolor icon layers with `--icon-color-N`. Vite loads the generated CSS and emits `sprite.svg` as a separate production asset. + +## Debug and preview + +Viewer displays all icons on one page and is only needed during development. Install it separately: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Add Viewer to a development page: + +```html + + +``` + +Create `src/svg-sprite-debug.js`: + +```js +import '@gromlab/svg-sprites/viewer/element' +import spriteManifest from '../assets/app-icons/.svg-sprite/svg-sprite.manifest.js' + +document.querySelector('gromlab-sprite-viewer').sources = [spriteManifest] +``` + +Run `npm run dev` and open the development page. Viewer is independent from the Alpine plugin. diff --git a/docs/en/guides/alpine-webpack.md b/docs/en/guides/alpine-webpack.md new file mode 100644 index 0000000..dccbd63 --- /dev/null +++ b/docs/en/guides/alpine-webpack.md @@ -0,0 +1,103 @@ +# SVG Sprite for Alpine.js with Webpack 5 + +A quick guide to creating an SVG sprite in an Alpine.js application built with Webpack 5. + +## Generate the sprite + +Choose a directory for the future SVG sprite, for example `assets/app-icons`, and create `svg-sprite.config.json` inside it. In `input`, specify the path to existing SVG files relative to the configuration file. There is no need to move or copy the icons. + +```json +{ + "mode": "alpine@webpack", + "name": "app", + "input": "../svg-icons/**/*.svg" +} +``` + +The generator does not need to be added to the application dependencies: run it through `npx`. + +Add generation before development and production builds: + +```json +{ + "scripts": { + "sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json", + "predev": "npm run sprites", + "dev": "webpack serve --mode development", + "prebuild": "npm run sprites", + "build": "webpack --mode production" + } +} +``` + +## Use the sprite + +The value `name: "app"` creates the Alpine plugin `appAlpinePlugin`, the `x-app-icon` directive, and the `$appIconHref` magic. + +Generated Alpine CSS is imported with the `?inline` query. Add an Asset Module rule to `webpack.config.js`: + +```js +export default { + module: { + rules: [ + { + test: /\.css$/, + resourceQuery: /inline/, + type: 'asset/source', + }, + ], + }, +} +``` + +Create `assets/app-icons/index.js`: + +```js +export * from './.svg-sprite/index.js' +``` + +Register the generated plugin before starting Alpine: + +```js +import Alpine from 'alpinejs' +import { appAlpinePlugin } from '../assets/app-icons/index.js' + +Alpine.plugin(appAlpinePlugin) +Alpine.start() +``` + +Use the reactive directive on an SVG element: + +```html + +``` + +The directive expression resolves to a source SVG file name without the extension. A monochrome icon inherits `color`; override multicolor icon layers with `--icon-color-N`. Webpack 5 emits `sprite.svg` as a separate production asset. + +## Debug and preview + +Viewer displays all icons on one page and is only needed during development. Install it separately: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Add Viewer to a development entry: + +```js +import '@gromlab/svg-sprites/viewer/element' +import spriteManifest from '../assets/app-icons/.svg-sprite/svg-sprite.manifest.js' + +const viewer = document.createElement('gromlab-sprite-viewer') +viewer.viewerTitle = 'Project icons' +viewer.sources = [spriteManifest] +document.body.append(viewer) +``` + +Include this entry only in development. Viewer is independent from the Alpine plugin. diff --git a/docs/en/guides/angular-application.md b/docs/en/guides/angular-application.md new file mode 100644 index 0000000..965415b --- /dev/null +++ b/docs/en/guides/angular-application.md @@ -0,0 +1,94 @@ +# SVG Sprite for Angular with the Application Builder + +A quick guide to creating an SVG sprite in an Angular application built with `@angular/build:application`. + +## Generate the sprite + +Choose a directory for the future SVG sprite, for example `assets/app-icons`, and create `svg-sprite.config.json` inside it. In `input`, specify the path to existing SVG files relative to the configuration file. There is no need to move or copy the icons. + +```json +{ + "mode": "angular@application", + "name": "app", + "input": "../svg-icons/**/*.svg" +} +``` + +The package does not need to be a project dependency. Run generation through `npx` before development and production builds: + +```json +{ + "scripts": { + "sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json", + "prestart": "npm run sprites", + "start": "ng serve", + "prebuild": "npm run sprites", + "build": "ng build" + } +} +``` + +The application builder emits imported SVG files when its file loader is enabled. Add this option to the build target in `angular.json`: + +```json +{ + "builder": "@angular/build:application", + "options": { + "loader": { ".svg": "file" } + } +} +``` + +## Use the sprite + +Create `assets/app-icons/index.ts`: + +```ts +export * from './.svg-sprite/index' +``` + +Import the generated standalone component. The value `name: "app"` creates `AppIcon` and the selector `app-icon`: + +```ts +import { Component } from '@angular/core' +import { AppIcon } from '../assets/app-icons' + +@Component({ + selector: 'app-root', + standalone: true, + imports: [AppIcon], + template: ` + + `, +}) +export class AppComponent {} +``` + +The `icon` input is typed from source file names. Monochrome icons inherit `color`; use `--icon-color-N` for individual colors. + +## Debug and preview + +Viewer is optional and only needed during development: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Import `@gromlab/svg-sprites/viewer/element`, add `CUSTOM_ELEMENTS_SCHEMA`, and place `` in the template. Load the generated manifest while omitting framework-only usage metadata: + +```ts +readonly viewerSources = [async () => { + const { default: manifest } = await import( + '../assets/app-icons/.svg-sprite/svg-sprite.manifest.js' + ) + const { usage: _usage, ...viewerManifest } = manifest + return viewerManifest +}] +``` + +The Viewer uses the same production sprite URL as `AppIcon`. diff --git a/docs/en/guides/angular-webpack.md b/docs/en/guides/angular-webpack.md new file mode 100644 index 0000000..1d30c6d --- /dev/null +++ b/docs/en/guides/angular-webpack.md @@ -0,0 +1,93 @@ +# SVG Sprite for Angular with Webpack + +A quick guide to creating an SVG sprite in an Angular application built by the Webpack-based Angular CLI browser builder. + +## Generate the sprite + +Choose a directory for the future SVG sprite, for example `assets/app-icons`, and create `svg-sprite.config.json` inside it. In `input`, specify the path to existing SVG files relative to the configuration file. There is no need to move or copy the icons. + +```json +{ + "mode": "angular@webpack", + "name": "app", + "input": "../svg-icons/**/*.svg" +} +``` + +This mode is for a workspace whose build target uses the official Webpack builder: + +```json +{ + "builder": "@angular-devkit/build-angular:browser" +} +``` + +The package does not need to be a project dependency. Generate the sprite through `npx` before each start and build: + +```json +{ + "scripts": { + "sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json", + "prestart": "npm run sprites", + "start": "ng serve", + "prebuild": "npm run sprites", + "build": "ng build" + } +} +``` + +Webpack resolves the generated `new URL(..., import.meta.url)` expression and emits `sprite.svg` as a production asset. + +## Use the sprite + +Create `assets/app-icons/index.ts`: + +```ts +export * from './.svg-sprite/index' +``` + +Import the generated standalone component. The value `name: "app"` creates `AppIcon` and the selector `app-icon`: + +```ts +import { Component } from '@angular/core' +import { AppIcon } from '../assets/app-icons' + +@Component({ + selector: 'app-root', + standalone: true, + imports: [AppIcon], + template: ` + + `, +}) +export class AppComponent {} +``` + +The `icon` input is typed from source file names. Monochrome icons inherit `color`; use `--icon-color-N` for individual colors. + +## Debug and preview + +Viewer is optional and only needed during development: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Import `@gromlab/svg-sprites/viewer/element`, add `CUSTOM_ELEMENTS_SCHEMA`, and place `` in the template: + +```ts +readonly viewerSources = [async () => { + const { default: manifest } = await import( + '../assets/app-icons/.svg-sprite/svg-sprite.manifest.js' + ) + const { usage: _usage, ...viewerManifest } = manifest + return viewerManifest +}] +``` + +The Viewer and `AppIcon` share the Webpack-emitted sprite URL. diff --git a/docs/en/guides/astro-vite.md b/docs/en/guides/astro-vite.md new file mode 100644 index 0000000..888aba8 --- /dev/null +++ b/docs/en/guides/astro-vite.md @@ -0,0 +1,92 @@ +# SVG Sprite for Astro with Vite + +A quick guide to creating an SVG sprite in an Astro application built with Vite. + +## Generate the sprite + +Choose a directory for the future SVG sprite, for example `assets/app-icons`, and create `svg-sprite.config.json` inside it. In `input`, specify the path to existing SVG files relative to the configuration file. There is no need to move or copy the icons. + +```json +{ + "mode": "astro@vite", + "name": "app", + "input": "../svg-icons/**/*.svg" +} +``` + +The package does not need to be a project dependency. Generate the sprite through `npx` before development and production builds: + +```json +{ + "scripts": { + "sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json", + "predev": "npm run sprites", + "dev": "astro dev", + "prebuild": "npm run sprites", + "build": "astro check && astro build" + } +} +``` + +## Use the sprite + +Create `assets/app-icons/index.js`: + +```js +export * from './.svg-sprite/index.js' +``` + +Create `assets/app-icons/index.d.ts` for the same typed API: + +```ts +export * from './.svg-sprite/index.js' +``` + +The value `name: "app"` creates the native Astro component `AppIcon`. Use it in a page: + +```astro +--- +import { AppIcon } from '../../assets/app-icons/index.js' +--- + + +``` + +The `icon` prop is typed from source file names. Vite emits `sprite.svg` from the component's static asset import. + +## Debug and preview + +Viewer is optional and only needed during development: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Add the Viewer to the page and connect the generated manifest in a client script: + +```astro + + + +``` + +The manifest retains Astro usage metadata while Viewer renders the same production sprite. diff --git a/docs/en/guides/lit-vite.md b/docs/en/guides/lit-vite.md new file mode 100644 index 0000000..05c357f --- /dev/null +++ b/docs/en/guides/lit-vite.md @@ -0,0 +1,86 @@ +# SVG Sprite for Lit with Vite + +A quick guide to creating an SVG sprite in a Lit application built with Vite. + +## Generate the sprite + +Choose a directory for the future SVG sprite, for example `assets/app-icons`, and create `svg-sprite.config.json` inside it. In `input`, specify the path to existing SVG files relative to the configuration file. There is no need to move or copy the icons. + +```json +{ + "mode": "lit@vite", + "name": "app", + "input": "../svg-icons/**/*.svg" +} +``` + +The generator does not need to be added to the application dependencies: run it through `npx`. + +Add generation before development and production builds: + +```json +{ + "scripts": { + "sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json", + "predev": "npm run sprites", + "dev": "vite", + "prebuild": "npm run sprites", + "build": "vite build" + } +} +``` + +## Use the sprite + +The value `name: "app"` creates the Lit class `AppIcon`, the `` tag, and the `defineAppIcon` registration function. + +Create `assets/app-icons/index.js`: + +```js +export * from './.svg-sprite/index.js' +``` + +Register the component before rendering it: + +```js +import { defineAppIcon } from '../assets/app-icons/index.js' + +defineAppIcon() + +document.querySelector('#app').innerHTML = ` + +` +``` + +The `icon` property accepts source SVG file names without the extension. A monochrome icon inherits `color`; override multicolor icon layers with `--icon-color-N`. Vite loads the component CSS and emits `sprite.svg` as a separate production asset. + +## Debug and preview + +Viewer displays all icons on one page and is only needed during development. Install it separately: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Add Viewer to a development page: + +```html + + +``` + +Create `src/svg-sprite-debug.js`: + +```js +import '@gromlab/svg-sprites/viewer/element' +import spriteManifest from '../assets/app-icons/.svg-sprite/svg-sprite.manifest.js' + +document.querySelector('gromlab-sprite-viewer').sources = [spriteManifest] +``` + +Run `npm run dev` and open the development page. Viewer is not required by `AppIcon`. diff --git a/docs/en/guides/lit-webpack.md b/docs/en/guides/lit-webpack.md new file mode 100644 index 0000000..1ce85bb --- /dev/null +++ b/docs/en/guides/lit-webpack.md @@ -0,0 +1,98 @@ +# SVG Sprite for Lit with Webpack 5 + +A quick guide to creating an SVG sprite in a Lit application built with Webpack 5. + +## Generate the sprite + +Choose a directory for the future SVG sprite, for example `assets/app-icons`, and create `svg-sprite.config.json` inside it. In `input`, specify the path to existing SVG files relative to the configuration file. There is no need to move or copy the icons. + +```json +{ + "mode": "lit@webpack", + "name": "app", + "input": "../svg-icons/**/*.svg" +} +``` + +The generator does not need to be added to the application dependencies: run it through `npx`. + +Add generation before development and production builds: + +```json +{ + "scripts": { + "sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json", + "predev": "npm run sprites", + "dev": "webpack serve --mode development", + "prebuild": "npm run sprites", + "build": "webpack --mode production" + } +} +``` + +## Use the sprite + +The value `name: "app"` creates the Lit class `AppIcon`, the `` tag, and the `defineAppIcon` registration function. + +Generated Lit CSS is imported with the `?inline` query. Add an Asset Module rule to `webpack.config.js`: + +```js +export default { + module: { + rules: [ + { + test: /\.css$/, + resourceQuery: /inline/, + type: 'asset/source', + }, + ], + }, +} +``` + +Create `assets/app-icons/index.js`: + +```js +export * from './.svg-sprite/index.js' +``` + +Register the component before rendering it: + +```js +import { defineAppIcon } from '../assets/app-icons/index.js' + +defineAppIcon() + +document.querySelector('#app').innerHTML = ` + +` +``` + +The `icon` property accepts source SVG file names without the extension. A monochrome icon inherits `color`; override multicolor icon layers with `--icon-color-N`. Webpack 5 emits `sprite.svg` as a separate production asset. + +## Debug and preview + +Viewer displays all icons on one page and is only needed during development. Install it separately: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Add Viewer to a development entry: + +```js +import '@gromlab/svg-sprites/viewer/element' +import spriteManifest from '../assets/app-icons/.svg-sprite/svg-sprite.manifest.js' + +const viewer = document.createElement('gromlab-sprite-viewer') +viewer.viewerTitle = 'Project icons' +viewer.sources = [spriteManifest] +document.body.append(viewer) +``` + +Include this entry only in development. Viewer is not required by `AppIcon`. diff --git a/docs/en/guides/next-app-turbopack.md b/docs/en/guides/next-app-turbopack.md index 964be0b..3917c65 100644 --- a/docs/en/guides/next-app-turbopack.md +++ b/docs/en/guides/next-app-turbopack.md @@ -4,9 +4,9 @@ A quick guide to creating an SVG sprite in a Next.js application using App Route ## Generate the sprite -Choose a folder for the sprite. This example uses `assets/app-icons`, with source SVG files, including the `check.svg` used below, in `assets/svg-icons`. +Choose a directory for the future SVG sprite, for example `assets/app-icons`, and create `svg-sprite.config.json` inside it. In `input`, specify the path to existing SVG files relative to the configuration file. There is no need to move or copy the icons. -Create `assets/app-icons/svg-sprite.config.json`: +Example configuration: ```json { @@ -16,7 +16,7 @@ Create `assets/app-icons/svg-sprite.config.json`: } ``` -The `input` path is relative to the config folder. +The package does not need to be a project dependency: generation runs through `npx`. Add generation commands to `package.json`. Generated files are excluded from Git by default, so `predev` and `prebuild` rebuild the sprite before every start and build: @@ -51,7 +51,7 @@ import { AppIcon } from '../assets/app-icons' export default function Page() { return ( +import { AppIcon } from '../assets/app-icons/index.js' + + + +``` + +`AppIcon` is SSR-safe and does not need a client-only wrapper. Vite emits `sprite.svg` as a separate production asset. + +## Debug and preview + +Viewer displays all icons on one page so you can check their rendering, change colors, and inspect the related CSS variables. It is only needed for development and is installed separately: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Create `app/components/SvgSpriteViewer.client.vue` so the browser-only Viewer is not evaluated during SSR: + +```vue + + + +``` + +Mark `gromlab-sprite-viewer` as a custom element in `nuxt.config.ts`: + +```ts +export default defineNuxtConfig({ + vue: { + compilerOptions: { + isCustomElement: (tag) => tag === 'gromlab-sprite-viewer', + }, + }, +}) +``` + +Render `` on your development page. Viewer is isolated from the generated `AppIcon` runtime. diff --git a/docs/en/guides/nuxt-webpack.md b/docs/en/guides/nuxt-webpack.md new file mode 100644 index 0000000..4b3ea60 --- /dev/null +++ b/docs/en/guides/nuxt-webpack.md @@ -0,0 +1,113 @@ +# SVG Sprite for Nuxt with Webpack + +A quick guide to creating an SVG sprite in a Nuxt application built with Webpack. + +## Generate the sprite + +Choose a directory for the future SVG sprite, for example `assets/app-icons`, and create `svg-sprite.config.json` inside it. In `input`, specify the path to existing SVG files relative to the configuration file. There is no need to move or copy the icons. + +Example configuration: + +```json +{ + "mode": "nuxt@webpack", + "name": "app", + "input": "../svg-icons/**/*.svg" +} +``` + +The package does not need to be a project dependency: generation runs through `npx`. + +Use the Nuxt Webpack builder in `nuxt.config.ts`: + +```bash +npm install --save-dev @nuxt/webpack-builder +``` + +```ts +export default defineNuxtConfig({ + builder: '@nuxt/webpack-builder', +}) +``` + +Add generation commands to `package.json`: + +```json +{ + "scripts": { + "sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json", + "predev": "npm run sprites", + "dev": "nuxt dev", + "prebuild": "npm run sprites", + "build": "nuxt build" + } +} +``` + +## Use the sprite + +The value `name: "app"` creates the Vue component `AppIcon`. Create `assets/app-icons/index.js`: + +```js +export * from './.svg-sprite/index.js' +``` + +Use the component in a Nuxt page or layout: + +```vue + + + +``` + +`AppIcon` is SSR-safe and does not need a client-only wrapper. Webpack emits `sprite.svg` as a separate production asset. + +## Debug and preview + +Viewer displays all icons on one page so you can check their rendering, change colors, and inspect the related CSS variables. It is only needed for development and is installed separately: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Create `app/components/SvgSpriteViewer.client.vue` so the browser-only Viewer is not evaluated during SSR: + +```vue + + + +``` + +Extend the existing `nuxt.config.ts` settings so Vue treats the Viewer as a custom element: + +```ts +export default defineNuxtConfig({ + builder: '@nuxt/webpack-builder', + vue: { + compilerOptions: { + isCustomElement: (tag) => tag === 'gromlab-sprite-viewer', + }, + }, +}) +``` + +Render `` on your development page. Viewer is isolated from the generated `AppIcon` runtime. diff --git a/docs/en/guides/preact-vite.md b/docs/en/guides/preact-vite.md new file mode 100644 index 0000000..7fd752b --- /dev/null +++ b/docs/en/guides/preact-vite.md @@ -0,0 +1,75 @@ +# SVG Sprite for Preact with Vite + +A quick guide to creating an SVG sprite in a Preact application built with Vite. + +## Generate the sprite + +Choose a directory for the future SVG sprite, for example `assets/app-icons`, and create `svg-sprite.config.json` inside it. In `input`, specify the path to existing SVG files relative to the configuration file. There is no need to move or copy the icons. + +```json +{ + "mode": "preact@vite", + "name": "app", + "input": "../svg-icons/**/*.svg" +} +``` + +The package does not need to be a project dependency. Run it through `npx` before development and production builds: + +```json +{ + "scripts": { + "sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json", + "predev": "npm run sprites", + "dev": "vite", + "prebuild": "npm run sprites", + "build": "tsc --noEmit && vite build" + } +} +``` + +## Use the sprite + +Create `assets/app-icons/index.js` and `assets/app-icons/index.d.ts` with the same export: + +```js +export * from './.svg-sprite/index.js' +``` + +Use the generated plain-JavaScript Preact component: + +```jsx +import { AppIcon } from '../assets/app-icons/index.js' + +export function SaveIcon() { + return ( + + ) +} +``` + +Vite automatically emits the imported `sprite.svg` as a production asset. + +## Debug and preview + +Install Viewer only for development: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Mount it from a debug entry: + +```js +import '@gromlab/svg-sprites/viewer/element' + +const viewer = document.createElement('gromlab-sprite-viewer') +viewer.sources = [() => import('../assets/app-icons/.svg-sprite/svg-sprite.manifest.js')] +document.body.append(viewer) +``` diff --git a/docs/en/guides/preact-webpack.md b/docs/en/guides/preact-webpack.md new file mode 100644 index 0000000..2fbd224 --- /dev/null +++ b/docs/en/guides/preact-webpack.md @@ -0,0 +1,75 @@ +# SVG Sprite for Preact with Webpack + +A quick guide to creating an SVG sprite in a Preact application built with Webpack 5. + +## Generate the sprite + +Choose a directory for the future SVG sprite, for example `assets/app-icons`, and create `svg-sprite.config.json` inside it. In `input`, specify the path to existing SVG files relative to the configuration file. There is no need to move or copy the icons. + +```json +{ + "mode": "preact@webpack", + "name": "app", + "input": "../svg-icons/**/*.svg" +} +``` + +The package does not need to be a project dependency. Run it through `npx` before Webpack starts or builds: + +```json +{ + "scripts": { + "sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json", + "predev": "npm run sprites", + "dev": "webpack serve --mode development", + "prebuild": "npm run sprites", + "build": "tsc --noEmit && webpack --mode production" + } +} +``` + +## Use the sprite + +Create `assets/app-icons/index.js` and `assets/app-icons/index.d.ts` with the same export: + +```js +export * from './.svg-sprite/index.js' +``` + +Use the generated Preact component: + +```jsx +import { AppIcon } from '../assets/app-icons/index.js' + +export function SaveIcon() { + return ( + + ) +} +``` + +Webpack Asset Modules emit `sprite.svg` from the generated `new URL(...)` expression. + +## Debug and preview + +Install Viewer only for development: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Mount it from a development-only entry: + +```js +import '@gromlab/svg-sprites/viewer/element' + +const viewer = document.createElement('gromlab-sprite-viewer') +viewer.sources = [() => import('../assets/app-icons/.svg-sprite/svg-sprite.manifest.js')] +document.body.append(viewer) +``` diff --git a/docs/en/guides/qwik-vite.md b/docs/en/guides/qwik-vite.md new file mode 100644 index 0000000..019f142 --- /dev/null +++ b/docs/en/guides/qwik-vite.md @@ -0,0 +1,82 @@ +# SVG Sprite for Qwik with Vite + +A quick guide to creating an SVG sprite in an SSR Qwik application built with Vite. + +## Generate the sprite + +Choose a directory for the future SVG sprite, for example `assets/app-icons`, and create `svg-sprite.config.json` inside it. In `input`, specify the path to existing SVG files relative to the configuration file. There is no need to move or copy the icons. + +```json +{ + "mode": "qwik@vite", + "name": "app", + "input": "../svg-icons/**/*.svg" +} +``` + +The package does not need to be a project dependency. Regenerate through `npx` before Vite starts or builds: + +```json +{ + "scripts": { + "sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json", + "predev": "npm run sprites", + "dev": "vite --mode ssr", + "prebuild": "npm run sprites", + "build": "tsc --noEmit && vite build" + } +} +``` + +## Use the sprite + +Create `assets/app-icons/index.ts`: + +```ts +export * from './.svg-sprite/index.js' +``` + +The generated component is a Qwik `component$` and is safe during SSR: + +```tsx +import { component$ } from '@builder.io/qwik' +import { AppIcon } from '../assets/app-icons' + +export default component$(() => ( + +)) +``` + +The component uses a static Vite asset import and does not access browser globals during SSR. + +## Debug and preview + +Viewer is browser-only, optional development tooling: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Load it from a visible task: + +```tsx +import { component$, useSignal, useVisibleTask$ } from '@builder.io/qwik' +import type { SpriteViewerElement } from '@gromlab/svg-sprites/viewer' + +export const IconViewer = component$(() => { + const host = useSignal() + useVisibleTask$(async () => { + await import('@gromlab/svg-sprites/viewer/element') + const viewer = document.createElement('gromlab-sprite-viewer') as SpriteViewerElement + viewer.sources = [() => import('../assets/app-icons/.svg-sprite/svg-sprite.manifest.js')] + host.value?.append(viewer) + }) + return
+}) +``` diff --git a/docs/en/guides/react-vite.md b/docs/en/guides/react-vite.md index d51cf11..992ba35 100644 --- a/docs/en/guides/react-vite.md +++ b/docs/en/guides/react-vite.md @@ -4,9 +4,9 @@ A quick guide to creating an SVG sprite in a React application built with Vite. ## Generate the sprite -Choose a folder for the sprite. This example uses `assets/app-icons`, with source SVG files, including the `check.svg` used below, in `assets/svg-icons`. +Choose a directory for the future SVG sprite, for example `assets/app-icons`, and create `svg-sprite.config.json` inside it. In `input`, specify the path to existing SVG files relative to the configuration file. There is no need to move or copy the icons. -Create `assets/app-icons/svg-sprite.config.json`: +Example configuration: ```json { @@ -16,7 +16,7 @@ Create `assets/app-icons/svg-sprite.config.json`: } ``` -The `input` path is relative to the config folder. +The package does not need to be a project dependency: generation runs through `npx`. Add generation commands to `package.json`. Generated files are excluded from Git by default, so `predev` and `prebuild` rebuild the sprite before every start and build: @@ -50,7 +50,7 @@ import { AppIcon } from '../assets/app-icons' export function SaveIcon() { return ( + ) +} +``` + +The component uses a static Vite asset import and does not access browser globals during SSR. + +## Debug and preview + +Viewer is browser-only, optional development tooling: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Load it from `onMount` so it is excluded from server rendering: + +```tsx +import { onMount } from 'solid-js' +import type { SpriteViewerElement } from '@gromlab/svg-sprites/viewer' + +export function IconViewer() { + let host!: HTMLDivElement + onMount(async () => { + await import('@gromlab/svg-sprites/viewer/element') + const viewer = document.createElement('gromlab-sprite-viewer') as SpriteViewerElement + viewer.sources = [() => import('../assets/app-icons/.svg-sprite/svg-sprite.manifest.js')] + host.append(viewer) + }) + return
+} +``` diff --git a/docs/en/guides/solid-vite.md b/docs/en/guides/solid-vite.md new file mode 100644 index 0000000..fa92600 --- /dev/null +++ b/docs/en/guides/solid-vite.md @@ -0,0 +1,83 @@ +# SVG Sprite for Solid with Vite + +A quick guide to creating an SVG sprite in a Solid application built with Vite. + +## Generate the sprite + +Choose a directory for the future SVG sprite, for example `assets/app-icons`, and create `svg-sprite.config.json` inside it. In `input`, specify the path to existing SVG files relative to the configuration file. There is no need to move or copy the icons. + +```json +{ + "mode": "solid@vite", + "name": "app", + "input": "../svg-icons/**/*.svg" +} +``` + +The package does not need to be a project dependency. Run it through `npx` and regenerate before development and production builds: + +```json +{ + "scripts": { + "sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json", + "predev": "npm run sprites", + "dev": "vite", + "prebuild": "npm run sprites", + "build": "tsc --noEmit && vite build" + } +} +``` + +## Use the sprite + +Create `assets/app-icons/index.ts`: + +```ts +export * from './.svg-sprite/index.js' +``` + +The name `app` creates the Solid component `AppIcon`: + +```tsx +import { AppIcon } from '../assets/app-icons' + +export function SaveIcon() { + return ( + + ) +} +``` + +Vite emits `sprite.svg` as a production asset. Monochrome icons inherit `color`; multicolor icons use `--icon-color-N`. + +## Debug and preview + +Viewer is optional and only needed during development: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Mount it from a debug component after the browser is ready: + +```tsx +import { onMount } from 'solid-js' +import type { SpriteViewerElement } from '@gromlab/svg-sprites/viewer' + +export function IconViewer() { + let host!: HTMLDivElement + onMount(async () => { + await import('@gromlab/svg-sprites/viewer/element') + const viewer = document.createElement('gromlab-sprite-viewer') as SpriteViewerElement + viewer.sources = [() => import('../assets/app-icons/.svg-sprite/svg-sprite.manifest.js')] + host.append(viewer) + }) + return
+} +``` diff --git a/docs/en/guides/solid-webpack.md b/docs/en/guides/solid-webpack.md new file mode 100644 index 0000000..be6cf8a --- /dev/null +++ b/docs/en/guides/solid-webpack.md @@ -0,0 +1,75 @@ +# SVG Sprite for Solid with Webpack + +A quick guide to creating an SVG sprite in a Solid application built with Webpack 5. + +## Generate the sprite + +Choose a directory for the future SVG sprite, for example `assets/app-icons`, and create `svg-sprite.config.json` inside it. In `input`, specify the path to existing SVG files relative to the configuration file. There is no need to move or copy the icons. + +```json +{ + "mode": "solid@webpack", + "name": "app", + "input": "../svg-icons/**/*.svg" +} +``` + +The package does not need to be a project dependency. Run it through `npx` before Webpack starts or builds: + +```json +{ + "scripts": { + "sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json", + "predev": "npm run sprites", + "dev": "webpack serve --mode development", + "prebuild": "npm run sprites", + "build": "tsc --noEmit && webpack --mode production" + } +} +``` + +## Use the sprite + +Create `assets/app-icons/index.js` and `assets/app-icons/index.d.ts` with the same export: + +```js +export * from './.svg-sprite/index.js' +``` + +Use the generated Solid component: + +```jsx +import { AppIcon } from '../assets/app-icons/index.js' + +export function SaveIcon() { + return ( + + ) +} +``` + +Webpack Asset Modules emit `sprite.svg` from the generated `new URL(...)`. Keep `.jsx` processing enabled for the generated Solid component. + +## Debug and preview + +Install Viewer only for development: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Add it to a development-only entry: + +```js +import '@gromlab/svg-sprites/viewer/element' + +const viewer = document.createElement('gromlab-sprite-viewer') +viewer.sources = [() => import('../assets/app-icons/.svg-sprite/svg-sprite.manifest.js')] +document.body.append(viewer) +``` diff --git a/docs/en/guides/standalone-vite.md b/docs/en/guides/standalone-vite.md index ab46219..22dce51 100644 --- a/docs/en/guides/standalone-vite.md +++ b/docs/en/guides/standalone-vite.md @@ -4,9 +4,9 @@ A quick guide to creating an SVG sprite in a Vite application without a framewor ## Generate the sprite -Choose a folder for the sprite. This example uses `assets/app-icons`, with source SVG files, including the `check.svg` used below, in `assets/svg-icons`. +Choose a directory for the future SVG sprite, for example `assets/app-icons`, and create `svg-sprite.config.json` inside it. In `input`, specify the path to existing SVG files relative to the configuration file. There is no need to move or copy the icons. -Create `assets/app-icons/svg-sprite.config.json`: +Example configuration: ```json { @@ -16,7 +16,7 @@ Create `assets/app-icons/svg-sprite.config.json`: } ``` -The `input` path is relative to the config folder. +The package does not need to be a project dependency: generation runs through `npx`. Add generation commands to `package.json`. Generated files are excluded from Git by default, so `predev` and `prebuild` rebuild the sprite before every start and build: @@ -54,10 +54,10 @@ defineAppIconElement() Use the icon in HTML: ```html - + ``` -The file `check.svg` is available as `icon="check"`. Set its size and colors with CSS: +The `icon` value is the source SVG filename without the extension. Set its size and colors with CSS: ```css app-icon { diff --git a/docs/en/guides/standalone-webpack.md b/docs/en/guides/standalone-webpack.md index 94d663d..a62cbb2 100644 --- a/docs/en/guides/standalone-webpack.md +++ b/docs/en/guides/standalone-webpack.md @@ -4,9 +4,9 @@ A quick guide to creating an SVG sprite in a Webpack 5 application without a fra ## Generate the sprite -Choose a folder for the sprite. This example uses `assets/app-icons`, with source SVG files, including the `check.svg` used below, in `assets/svg-icons`. +Choose a directory for the future SVG sprite, for example `assets/app-icons`, and create `svg-sprite.config.json` inside it. In `input`, specify the path to existing SVG files relative to the configuration file. There is no need to move or copy the icons. -Create `assets/app-icons/svg-sprite.config.json`: +Example configuration: ```json { @@ -16,7 +16,7 @@ Create `assets/app-icons/svg-sprite.config.json`: } ``` -The `input` path is relative to the config folder. +The package does not need to be a project dependency: generation runs through `npx`. Add generation commands to `package.json`. Generated files are excluded from Git by default, so `predev` and `prebuild` rebuild the sprite before every start and build: @@ -54,10 +54,10 @@ defineAppIconElement() Use the icon in HTML: ```html - + ``` -The file `check.svg` is available as `icon="check"`. Set its size and colors with CSS: +The `icon` value is the source SVG filename without the extension. Set its size and colors with CSS: ```css app-icon { diff --git a/docs/en/guides/standalone.md b/docs/en/guides/standalone.md index 47b715b..c06b9bc 100644 --- a/docs/en/guides/standalone.md +++ b/docs/en/guides/standalone.md @@ -6,32 +6,9 @@ Combine SVG icons into one file and use them on an HTML page. You do not need to install the package in your project. -This guide uses the following project structure: - -```text -/ -├── index.html -└── assets/ - ├── app-icons/ - └── svg-icons/ - ├── check.svg - └── warning.svg -``` - ### 1. Create the sprite config -Choose a folder for the sprite. This example uses `assets/app-icons`. Create `svg-sprite.config.json` inside it: - -```json -{ - "mode": "standalone", - "name": "icons" -} -``` - -### 2. Set the icon source - -`input` can point to a folder, an individual SVG file, or a glob pattern. To use multiple sources, provide an array with any combination of these values: +Choose a directory for the future SVG sprite, for example `assets/app-icons`, and create `svg-sprite.config.json` inside it. In `input`, specify the path to existing SVG files relative to the configuration file. There is no need to move or copy the icons. ```json { @@ -41,7 +18,7 @@ Choose a folder for the sprite. This example uses `assets/app-icons`. Create `sv } ``` -### 3. Generate the sprite +### 2. Generate the sprite Pass the config path to the command: @@ -62,7 +39,7 @@ assets/app-icons/.svg-sprite/ The `.svg-sprite` directory is created automatically and fully replaced on every generation. Do not edit its contents manually. -### 4. Use an icon +### 3. Use an icon In `index.html`, point to the generated `sprite.svg`. After `#`, add the icon file name without the `.svg` extension: @@ -72,12 +49,10 @@ In `index.html`, point to the generated `sprite.svg`. After `#`, add the icon fi height="24" aria-label="Done" > - + ``` -The icon from `check.svg` is available as `#check`. - ## Debug and preview `sprite.svg` is a technical file, not an icon gallery. Opening it does not provide a convenient view of the whole set. Gradients, masks, filters, and references to internal `id` values may also render with artifacts. diff --git a/docs/en/guides/svelte-vite.md b/docs/en/guides/svelte-vite.md new file mode 100644 index 0000000..1e43901 --- /dev/null +++ b/docs/en/guides/svelte-vite.md @@ -0,0 +1,95 @@ +# SVG Sprite for Svelte with Vite + +A quick guide to creating an SVG sprite in a Svelte application built with Vite. + +## Generate the sprite + +Choose a directory for the future SVG sprite, for example `assets/app-icons`, and create `svg-sprite.config.json` inside it. In `input`, specify the path to existing SVG files relative to the configuration file. There is no need to move or copy the icons. + +Example configuration: + +```json +{ + "mode": "svelte@vite", + "name": "app", + "input": "../svg-icons/**/*.svg" +} +``` + +The package does not need to be a project dependency: generation runs through `npx`. + +Add generation commands to `package.json`. Generated files are excluded from Git by default, so `predev` and `prebuild` rebuild the sprite before every start and build: + +```json +{ + "scripts": { + "sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json", + "predev": "npm run sprites", + "dev": "vite", + "prebuild": "npm run sprites", + "build": "vite build" + } +} +``` + +## Use the sprite + +The value `name: "app"` creates the Svelte component `AppIcon`. + +Create `assets/app-icons/index.js`: + +```js +export * from './.svg-sprite/index.js' +``` + +Use the component in your application: + +```svelte + + + +``` + +The `icon` prop accepts source SVG file names without the extension. A monochrome icon inherits `color`, while colors in a multicolor icon are overridden with `--icon-color-N`. + +Vite automatically includes the component styles and emits `sprite.svg` as a separate production asset. + +## Debug and preview + +Viewer displays all icons on one page so you can check their rendering, change colors, and inspect the related CSS variables. It is only needed for development and is installed separately: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Add Viewer to a development-only Svelte page or component: + +```svelte + + + +``` + +Run `npm run dev` and open the page containing Viewer. Do not import this development component from the production entry. diff --git a/docs/en/guides/svelte-webpack.md b/docs/en/guides/svelte-webpack.md new file mode 100644 index 0000000..13a859b --- /dev/null +++ b/docs/en/guides/svelte-webpack.md @@ -0,0 +1,105 @@ +# SVG Sprite for Svelte with Webpack 5 + +A quick guide to creating an SVG sprite in a Svelte application built with Webpack 5. + +## Generate the sprite + +Choose a directory for the future SVG sprite, for example `assets/app-icons`, and create `svg-sprite.config.json` inside it. In `input`, specify the path to existing SVG files relative to the configuration file. There is no need to move or copy the icons. + +Example configuration: + +```json +{ + "mode": "svelte@webpack", + "name": "app", + "input": "../svg-icons/**/*.svg" +} +``` + +The package does not need to be a project dependency: generation runs through `npx`. + +Add generation commands to `package.json`: + +```json +{ + "scripts": { + "sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json", + "predev": "npm run sprites", + "dev": "webpack serve --mode development", + "prebuild": "npm run sprites", + "build": "webpack --mode production" + } +} +``` + +## Use the sprite + +The value `name: "app"` creates the Svelte component `AppIcon`. + +Create `assets/app-icons/index.js`: + +```js +export * from './.svg-sprite/index.js' +``` + +Use the component in your application: + +```svelte + + + +``` + +The generated component is a native `.svelte` file. The normal `svelte-loader` rule must include `.svelte` files under `assets`: + +```js +{ + test: /\.svelte$/, + use: { + loader: 'svelte-loader', + options: { emitCss: false }, + }, +} +``` + +Webpack 5 processes the component's asset URL and emits `sprite.svg` as a separate production asset. + +## Debug and preview + +Viewer displays all icons on one page so you can check their rendering, change colors, and inspect the related CSS variables. It is only needed for development and is installed separately: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Add Viewer to a development-only Svelte component: + +```svelte + + + +``` + +Run `npm run dev` and open the page containing Viewer. Do not import this development component from the production entry. diff --git a/docs/en/guides/sveltekit-vite.md b/docs/en/guides/sveltekit-vite.md new file mode 100644 index 0000000..b0efe44 --- /dev/null +++ b/docs/en/guides/sveltekit-vite.md @@ -0,0 +1,91 @@ +# SVG Sprite for SvelteKit with Vite + +A quick guide to creating an SVG sprite in a SvelteKit application built with Vite. + +## Generate the sprite + +Choose a directory for the future SVG sprite, for example `assets/app-icons`, and create `svg-sprite.config.json` inside it. In `input`, specify the path to existing SVG files relative to the configuration file. There is no need to move or copy the icons. + +Example configuration: + +```json +{ + "mode": "sveltekit@vite", + "name": "app", + "input": "../svg-icons/**/*.svg" +} +``` + +The package does not need to be a project dependency: generation runs through `npx`. + +Add generation commands to `package.json`. Generated files are excluded from Git by default, so `predev` and `prebuild` rebuild the sprite before every start and build: + +```json +{ + "scripts": { + "sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json", + "predev": "npm run sprites", + "dev": "vite dev", + "prebuild": "npm run sprites", + "build": "vite build" + } +} +``` + +## Use the sprite + +The value `name: "app"` creates the SSR-safe Svelte component `AppIcon`. + +Create `assets/app-icons/index.js`: + +```js +export * from './.svg-sprite/index.js' +``` + +Use the component in `src/routes/+page.svelte`: + +```svelte + + + +``` + +The `icon` prop accepts source SVG file names without the extension. The component has no browser-only initialization, so the page can be rendered on the server. Vite emits `sprite.svg` as a separate production asset. + +## Debug and preview + +Viewer displays all icons on one page so you can check their rendering, change colors, and inspect the related CSS variables. It is only needed for development and is installed separately: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Create a development route such as `src/routes/svg-sprite/+page.svelte`. Load the custom element from an action so it is only registered in the browser: + +```svelte + + + +``` + +Run `npm run dev` and open `/svg-sprite`. The action does not run during SSR. diff --git a/docs/en/guides/vue-vite.md b/docs/en/guides/vue-vite.md new file mode 100644 index 0000000..152f14b --- /dev/null +++ b/docs/en/guides/vue-vite.md @@ -0,0 +1,105 @@ +# SVG Sprite for Vue with Vite + +A quick guide to creating an SVG sprite in a Vue application built with Vite. + +## Generate the sprite + +Choose a directory for the future SVG sprite, for example `assets/app-icons`, and create `svg-sprite.config.json` inside it. In `input`, specify the path to existing SVG files relative to the configuration file. There is no need to move or copy the icons. + +Example configuration: + +```json +{ + "mode": "vue@vite", + "name": "app", + "input": "../svg-icons/**/*.svg" +} +``` + +The package does not need to be a project dependency: generation runs through `npx`. + +Add generation commands to `package.json`. Generated files are excluded from Git by default, so `predev` and `prebuild` rebuild the sprite before every start and build: + +```json +{ + "scripts": { + "sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json", + "predev": "npm run sprites", + "dev": "vite", + "prebuild": "npm run sprites", + "build": "vue-tsc --noEmit && vite build" + } +} +``` + +## Use the sprite + +The value `name: "app"` creates the Vue component `AppIcon`. + +Create the entry point `assets/app-icons/index.ts`: + +```ts +export * from './.svg-sprite/index.js' +``` + +Use the component in your application: + +```vue + + + +``` + +The `icon` prop accepts source SVG file names without the extension. A monochrome icon inherits `color`, while colors in a multicolor icon are overridden with `--icon-color-N`. + +Vite automatically includes the component styles and adds `sprite.svg` to the production build. + +## Debug and preview + +Viewer displays all icons on one page so you can check their rendering, change colors, and inspect the related CSS variables. It is only needed for development and is installed separately: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Create `svg-sprite.html` in the project root: + +```html + + + + + Project icons + + + + + + +``` + +Create `src/svg-sprite-debug.ts`: + +```ts +import '@gromlab/svg-sprites/viewer/element' +import type { SpriteViewerElement } from '@gromlab/svg-sprites/viewer' +import spriteManifest from '../assets/app-icons/.svg-sprite/svg-sprite.manifest.js' + +const viewer = document.querySelector('gromlab-sprite-viewer')! +viewer.sources = [spriteManifest] +``` + +Run `npm run dev` and open `/svg-sprite.html`. + +Viewer is not required by `AppIcon` and is not loaded by the main application code. diff --git a/docs/en/guides/vue-webpack.md b/docs/en/guides/vue-webpack.md new file mode 100644 index 0000000..d37667e --- /dev/null +++ b/docs/en/guides/vue-webpack.md @@ -0,0 +1,126 @@ +# SVG Sprite for Vue with Webpack 5 + +A quick guide to creating an SVG sprite in a Vue application built with Webpack 5. + +## Generate the sprite + +Choose a directory for the future SVG sprite, for example `assets/app-icons`, and create `svg-sprite.config.json` inside it. In `input`, specify the path to existing SVG files relative to the configuration file. There is no need to move or copy the icons. + +Example configuration: + +```json +{ + "mode": "vue@webpack", + "name": "app", + "input": "../svg-icons/**/*.svg" +} +``` + +The package does not need to be a project dependency: generation runs through `npx`. + +Add generation commands to `package.json`. Generated files are excluded from Git by default, so `predev` and `prebuild` rebuild the sprite before every start and build: + +```json +{ + "scripts": { + "sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json", + "predev": "npm run sprites", + "dev": "webpack serve --mode development", + "prebuild": "npm run sprites", + "build": "webpack --mode production" + } +} +``` + +## Use the sprite + +The value `name: "app"` creates the Vue component `AppIcon`. Create `assets/app-icons/index.js`: + +```js +export * from './.svg-sprite/index.js' +``` + +Use the component in your application: + +```vue + + + +``` + +The `icon` prop accepts source SVG file names without the extension. A monochrome icon inherits `color`, while colors in a multicolor icon are overridden with `--icon-color-N`. + +The component uses CSS Modules. If the project does not process them yet, install `style-loader` and `css-loader`, then add a rule with a default export: + +```bash +npm install --save-dev style-loader css-loader +``` + +```js +{ + test: /\.module\.css$/i, + use: [ + 'style-loader', + { + loader: 'css-loader', + options: { modules: { namedExport: false } }, + }, + ], +} +``` + +Webpack 5 automatically adds `sprite.svg` to the production build. + +## Debug and preview + +Viewer displays all icons on one page so you can check their rendering, change colors, and inspect the related CSS variables. It is only needed for development and is installed separately: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Add Viewer to a development-only Vue component: + +```vue + + + +``` + +Configure Vue Loader to treat `gromlab-sprite-viewer` as a custom element: + +```js +{ + test: /\.vue$/, + loader: 'vue-loader', + options: { + compilerOptions: { + isCustomElement: (tag) => tag === 'gromlab-sprite-viewer', + }, + }, +} +``` + +Render the Viewer component on your development page. Viewer is not required by `AppIcon`. diff --git a/docs/en/reference/technical.md b/docs/en/reference/technical.md index 86822a6..158e394 100644 --- a/docs/en/reference/technical.md +++ b/docs/en/reference/technical.md @@ -15,6 +15,26 @@ Reference for the configuration, generated API, and behavior of `@gromlab/svg-sp - [Next.js App Router + Webpack](../guides/next-app-webpack.md) - [Next.js Pages Router + Turbopack](../guides/next-pages-turbopack.md) - [Next.js Pages Router + Webpack](../guides/next-pages-webpack.md) +- [Vue + Vite](../guides/vue-vite.md) +- [Vue + Webpack](../guides/vue-webpack.md) +- [Nuxt + Vite](../guides/nuxt-vite.md) +- [Nuxt + Webpack](../guides/nuxt-webpack.md) +- [Svelte + Vite](../guides/svelte-vite.md) +- [Svelte + Webpack](../guides/svelte-webpack.md) +- [SvelteKit + Vite](../guides/sveltekit-vite.md) +- [Angular application builder](../guides/angular-application.md) +- [Angular + Webpack](../guides/angular-webpack.md) +- [Astro + Vite](../guides/astro-vite.md) +- [Solid + Vite](../guides/solid-vite.md) +- [Solid + Webpack](../guides/solid-webpack.md) +- [SolidStart + Vite](../guides/solid-start-vite.md) +- [Preact + Vite](../guides/preact-vite.md) +- [Preact + Webpack](../guides/preact-webpack.md) +- [Qwik + Vite](../guides/qwik-vite.md) +- [Lit + Vite](../guides/lit-vite.md) +- [Lit + Webpack](../guides/lit-webpack.md) +- [Alpine.js + Vite](../guides/alpine-vite.md) +- [Alpine.js + Webpack](../guides/alpine-webpack.md) ## Requirements @@ -51,6 +71,26 @@ svg-sprites [options] | Standalone + Webpack 5 | `standalone@webpack` | | React + Vite | `react@vite` | | React + Webpack 5 | `react@webpack` | +| Vue + Vite | `vue@vite` | +| Vue + Webpack | `vue@webpack` | +| Nuxt + Vite | `nuxt@vite` | +| Nuxt + Webpack | `nuxt@webpack` | +| Svelte + Vite | `svelte@vite` | +| Svelte + Webpack | `svelte@webpack` | +| SvelteKit + Vite | `sveltekit@vite` | +| Angular application builder | `angular@application` | +| Angular + Webpack | `angular@webpack` | +| Astro + Vite | `astro@vite` | +| Solid + Vite | `solid@vite` | +| Solid + Webpack | `solid@webpack` | +| SolidStart + Vite | `solid-start@vite` | +| Preact + Vite | `preact@vite` | +| Preact + Webpack | `preact@webpack` | +| Qwik + Vite | `qwik@vite` | +| Lit + Vite | `lit@vite` | +| Lit + Webpack | `lit@webpack` | +| Alpine.js + Vite | `alpine@vite` | +| Alpine.js + Webpack | `alpine@webpack` | | Next.js App Router + Turbopack | `next@app/turbopack` | | Next.js App Router + Webpack 5 | `next@app/webpack` | | Next.js Pages Router + Turbopack | `next@pages/turbopack` | @@ -310,7 +350,7 @@ The component does not add accessibility semantics automatically. Pass appropria ## Multiple sprites -Each directory with a configuration creates an independent mode-specific contract. React and Next.js modes generate a React component and types, `standalone@vite` and `standalone@webpack` generate a Web Component and types, and bare `standalone` generates an SVG and JSON manifest: +Each directory with a configuration creates an independent mode-specific contract. Framework modes generate their native component and declarations, standalone bundler modes generate a Web Component and declarations, and bare `standalone` generates an SVG and JSON manifest: ```text app-icons → AppIcon → shared icons @@ -332,7 +372,7 @@ All current modes generate the `stack` format. ### Generated component -For React and Next.js, use the generated React component. It knows the internal IDs, constructs the URL, and provides a TypeScript API: +Use the generated native component documented by the selected exact-mode guide. It knows the internal IDs, constructs the URL, and provides a TypeScript API. React and Next.js use it like this: ```tsx @@ -570,7 +610,7 @@ export const IconsDebugPage = () => ( ) ``` -The Viewer displays groups, search, `viewBox`, CSS custom properties, and fallback colors. React/Next manifests get React, SVG, IMG, and CSS tabs; standalone manifests get SVG, IMG, and CSS. You can change color values in the interface and immediately inspect the result. +The Viewer displays groups, search, `viewBox`, CSS custom properties, and fallback colors. Framework manifests get their framework-specific tab plus SVG, IMG, and CSS; standalone manifests get SVG, IMG, and CSS. You can change color values in the interface and immediately inspect the result. ### Viewer theme diff --git a/docs/ru/README.md b/docs/ru/README.md index c68ddf5..820c6c7 100644 --- a/docs/ru/README.md +++ b/docs/ru/README.md @@ -14,6 +14,26 @@ | Vanilla + Webpack 5 | `standalone@webpack` | [Standalone + Webpack](guides/standalone-webpack.md) | | React + Vite | `react@vite` | [React + Vite](guides/react-vite.md) | | React + Webpack 5 | `react@webpack` | [React + Webpack](guides/react-webpack.md) | +| Vue + Vite | `vue@vite` | [Vue + Vite](guides/vue-vite.md) | +| Vue + Webpack | `vue@webpack` | [Vue + Webpack](guides/vue-webpack.md) | +| Nuxt + Vite | `nuxt@vite` | [Nuxt + Vite](guides/nuxt-vite.md) | +| Nuxt + Webpack | `nuxt@webpack` | [Nuxt + Webpack](guides/nuxt-webpack.md) | +| Svelte + Vite | `svelte@vite` | [Svelte + Vite](guides/svelte-vite.md) | +| Svelte + Webpack | `svelte@webpack` | [Svelte + Webpack](guides/svelte-webpack.md) | +| SvelteKit + Vite | `sveltekit@vite` | [SvelteKit + Vite](guides/sveltekit-vite.md) | +| Angular application builder | `angular@application` | [Angular application builder](guides/angular-application.md) | +| Angular + Webpack | `angular@webpack` | [Angular + Webpack](guides/angular-webpack.md) | +| Astro + Vite | `astro@vite` | [Astro + Vite](guides/astro-vite.md) | +| Solid + Vite | `solid@vite` | [Solid + Vite](guides/solid-vite.md) | +| Solid + Webpack | `solid@webpack` | [Solid + Webpack](guides/solid-webpack.md) | +| SolidStart + Vite | `solid-start@vite` | [SolidStart + Vite](guides/solid-start-vite.md) | +| Preact + Vite | `preact@vite` | [Preact + Vite](guides/preact-vite.md) | +| Preact + Webpack | `preact@webpack` | [Preact + Webpack](guides/preact-webpack.md) | +| Qwik + Vite | `qwik@vite` | [Qwik + Vite](guides/qwik-vite.md) | +| Lit + Vite | `lit@vite` | [Lit + Vite](guides/lit-vite.md) | +| Lit + Webpack | `lit@webpack` | [Lit + Webpack](guides/lit-webpack.md) | +| Alpine.js + Vite | `alpine@vite` | [Alpine.js + Vite](guides/alpine-vite.md) | +| Alpine.js + Webpack | `alpine@webpack` | [Alpine.js + Webpack](guides/alpine-webpack.md) | | Next.js App Router + Turbopack | `next@app/turbopack` | [App Router + Turbopack](guides/next-app-turbopack.md) | | Next.js App Router + Webpack | `next@app/webpack` | [App Router + Webpack](guides/next-app-webpack.md) | | Next.js Pages Router + Turbopack | `next@pages/turbopack` | [Pages Router + Turbopack](guides/next-pages-turbopack.md) | diff --git a/docs/ru/guides/README.md b/docs/ru/guides/README.md index e5788cc..4cf1a7c 100644 --- a/docs/ru/guides/README.md +++ b/docs/ru/guides/README.md @@ -5,6 +5,26 @@ - `standalone@webpack`: [standalone с Webpack](standalone-webpack.md) - `react@vite`: [React с Vite](react-vite.md) - `react@webpack`: [React с Webpack](react-webpack.md) +- `vue@vite`: [Vue с Vite](vue-vite.md) +- `vue@webpack`: [Vue с Webpack](vue-webpack.md) +- `nuxt@vite`: [Nuxt с Vite](nuxt-vite.md) +- `nuxt@webpack`: [Nuxt с Webpack](nuxt-webpack.md) +- `svelte@vite`: [Svelte с Vite](svelte-vite.md) +- `svelte@webpack`: [Svelte с Webpack](svelte-webpack.md) +- `sveltekit@vite`: [SvelteKit с Vite](sveltekit-vite.md) +- `angular@application`: [Angular application builder](angular-application.md) +- `angular@webpack`: [Angular с Webpack](angular-webpack.md) +- `astro@vite`: [Astro с Vite](astro-vite.md) +- `solid@vite`: [Solid с Vite](solid-vite.md) +- `solid@webpack`: [Solid с Webpack](solid-webpack.md) +- `solid-start@vite`: [SolidStart с Vite](solid-start-vite.md) +- `preact@vite`: [Preact с Vite](preact-vite.md) +- `preact@webpack`: [Preact с Webpack](preact-webpack.md) +- `qwik@vite`: [Qwik с Vite](qwik-vite.md) +- `lit@vite`: [Lit с Vite](lit-vite.md) +- `lit@webpack`: [Lit с Webpack](lit-webpack.md) +- `alpine@vite`: [Alpine.js с Vite](alpine-vite.md) +- `alpine@webpack`: [Alpine.js с Webpack](alpine-webpack.md) - `next@app/turbopack`: [App Router с Turbopack](next-app-turbopack.md) - `next@app/webpack`: [App Router с Webpack](next-app-webpack.md) - `next@pages/turbopack`: [Pages Router с Turbopack](next-pages-turbopack.md) diff --git a/docs/ru/guides/alpine-vite.md b/docs/ru/guides/alpine-vite.md new file mode 100644 index 0000000..c533a87 --- /dev/null +++ b/docs/ru/guides/alpine-vite.md @@ -0,0 +1,91 @@ +# SVG-спрайт для Alpine.js на Vite + +Инструкция по быстрому созданию SVG-спрайта в Alpine.js-приложении на Vite. + +## Генерация спрайта + +Выберите каталог для будущего SVG-спрайта, например `assets/app-icons`, и создайте в нём `svg-sprite.config.json`. В `input` укажите путь к существующим SVG относительно файла конфигурации. Перемещать или копировать иконки не требуется. + +```json +{ + "mode": "alpine@vite", + "name": "app", + "input": "../svg-icons/**/*.svg" +} +``` + +Генератор не нужно добавлять в зависимости приложения: запускайте его через `npx`. + +Добавьте генерацию перед запуском разработки и production-сборкой: + +```json +{ + "scripts": { + "sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json", + "predev": "npm run sprites", + "dev": "vite", + "prebuild": "npm run sprites", + "build": "vite build" + } +} +``` + +## Использование спрайта + +Значение `name: "app"` создаёт Alpine plugin `appAlpinePlugin`, директиву `x-app-icon` и magic `$appIconHref`. + +Создайте `assets/app-icons/index.js`: + +```js +export * from './.svg-sprite/index.js' +``` + +Зарегистрируйте generated plugin до запуска Alpine: + +```js +import Alpine from 'alpinejs' +import { appAlpinePlugin } from '../assets/app-icons/index.js' + +Alpine.plugin(appAlpinePlugin) +Alpine.start() +``` + +Используйте реактивную директиву на SVG-элементе: + +```html + +``` + +Выражение директивы возвращает имя исходного SVG без расширения. Монохромная иконка наследует `color`, а слои многоцветной иконки переопределяются через `--icon-color-N`. Vite подключает generated CSS и выпускает `sprite.svg` отдельным production asset. + +## Дебаг и превью + +Viewer показывает все иконки на одной странице и нужен только при разработке. Установите его отдельно: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Добавьте Viewer на development-страницу: + +```html + + +``` + +Создайте `src/svg-sprite-debug.js`: + +```js +import '@gromlab/svg-sprites/viewer/element' +import spriteManifest from '../assets/app-icons/.svg-sprite/svg-sprite.manifest.js' + +document.querySelector('gromlab-sprite-viewer').sources = [spriteManifest] +``` + +Запустите `npm run dev` и откройте development-страницу. Viewer не зависит от Alpine plugin. diff --git a/docs/ru/guides/alpine-webpack.md b/docs/ru/guides/alpine-webpack.md new file mode 100644 index 0000000..c05436d --- /dev/null +++ b/docs/ru/guides/alpine-webpack.md @@ -0,0 +1,103 @@ +# SVG-спрайт для Alpine.js на Webpack 5 + +Инструкция по быстрому созданию SVG-спрайта в Alpine.js-приложении на Webpack 5. + +## Генерация спрайта + +Выберите каталог для будущего SVG-спрайта, например `assets/app-icons`, и создайте в нём `svg-sprite.config.json`. В `input` укажите путь к существующим SVG относительно файла конфигурации. Перемещать или копировать иконки не требуется. + +```json +{ + "mode": "alpine@webpack", + "name": "app", + "input": "../svg-icons/**/*.svg" +} +``` + +Генератор не нужно добавлять в зависимости приложения: запускайте его через `npx`. + +Добавьте генерацию перед запуском разработки и production-сборкой: + +```json +{ + "scripts": { + "sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json", + "predev": "npm run sprites", + "dev": "webpack serve --mode development", + "prebuild": "npm run sprites", + "build": "webpack --mode production" + } +} +``` + +## Использование спрайта + +Значение `name: "app"` создаёт Alpine plugin `appAlpinePlugin`, директиву `x-app-icon` и magic `$appIconHref`. + +Generated CSS Alpine импортируется с query `?inline`. Добавьте правило Asset Modules в `webpack.config.js`: + +```js +export default { + module: { + rules: [ + { + test: /\.css$/, + resourceQuery: /inline/, + type: 'asset/source', + }, + ], + }, +} +``` + +Создайте `assets/app-icons/index.js`: + +```js +export * from './.svg-sprite/index.js' +``` + +Зарегистрируйте generated plugin до запуска Alpine: + +```js +import Alpine from 'alpinejs' +import { appAlpinePlugin } from '../assets/app-icons/index.js' + +Alpine.plugin(appAlpinePlugin) +Alpine.start() +``` + +Используйте реактивную директиву на SVG-элементе: + +```html + +``` + +Выражение директивы возвращает имя исходного SVG без расширения. Монохромная иконка наследует `color`, а слои многоцветной иконки переопределяются через `--icon-color-N`. Webpack 5 выпускает `sprite.svg` отдельным production asset. + +## Дебаг и превью + +Viewer показывает все иконки на одной странице и нужен только при разработке. Установите его отдельно: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Добавьте Viewer в development entry: + +```js +import '@gromlab/svg-sprites/viewer/element' +import spriteManifest from '../assets/app-icons/.svg-sprite/svg-sprite.manifest.js' + +const viewer = document.createElement('gromlab-sprite-viewer') +viewer.viewerTitle = 'Иконки проекта' +viewer.sources = [spriteManifest] +document.body.append(viewer) +``` + +Подключайте этот entry только при разработке. Viewer не зависит от Alpine plugin. diff --git a/docs/ru/guides/angular-application.md b/docs/ru/guides/angular-application.md new file mode 100644 index 0000000..16ac7d5 --- /dev/null +++ b/docs/ru/guides/angular-application.md @@ -0,0 +1,94 @@ +# SVG-спрайт для Angular с Application Builder + +Инструкция по быстрому созданию SVG-спрайта в Angular-приложении на `@angular/build:application`. + +## Генерация спрайта + +Выберите каталог для будущего SVG-спрайта, например `assets/app-icons`, и создайте в нём `svg-sprite.config.json`. В `input` укажите путь к существующим SVG относительно файла конфигурации. Перемещать или копировать иконки не требуется. + +```json +{ + "mode": "angular@application", + "name": "app", + "input": "../svg-icons/**/*.svg" +} +``` + +Пакет не нужно добавлять в зависимости проекта. Запускайте генерацию через `npx` перед разработкой и production-сборкой: + +```json +{ + "scripts": { + "sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json", + "prestart": "npm run sprites", + "start": "ng serve", + "prebuild": "npm run sprites", + "build": "ng build" + } +} +``` + +Application Builder выпускает импортированный SVG отдельным файлом при включённом file loader. Добавьте опцию в build target файла `angular.json`: + +```json +{ + "builder": "@angular/build:application", + "options": { + "loader": { ".svg": "file" } + } +} +``` + +## Использование спрайта + +Создайте `assets/app-icons/index.ts`: + +```ts +export * from './.svg-sprite/index' +``` + +Импортируйте сгенерированный standalone-компонент. Значение `name: "app"` создаёт `AppIcon` с селектором `app-icon`: + +```ts +import { Component } from '@angular/core' +import { AppIcon } from '../assets/app-icons' + +@Component({ + selector: 'app-root', + standalone: true, + imports: [AppIcon], + template: ` + + `, +}) +export class AppComponent {} +``` + +Input `icon` типизирован именами исходных файлов. Монохромные иконки наследуют `color`, отдельные цвета задаются через `--icon-color-N`. + +## Дебаг и превью + +Viewer необязателен и нужен только при разработке: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Импортируйте `@gromlab/svg-sprites/viewer/element`, добавьте `CUSTOM_ELEMENTS_SCHEMA` и поместите `` в шаблон. Загрузите generated manifest без framework-specific metadata: + +```ts +readonly viewerSources = [async () => { + const { default: manifest } = await import( + '../assets/app-icons/.svg-sprite/svg-sprite.manifest.js' + ) + const { usage: _usage, ...viewerManifest } = manifest + return viewerManifest +}] +``` + +Viewer использует тот же production URL спрайта, что и `AppIcon`. diff --git a/docs/ru/guides/angular-webpack.md b/docs/ru/guides/angular-webpack.md new file mode 100644 index 0000000..7be3843 --- /dev/null +++ b/docs/ru/guides/angular-webpack.md @@ -0,0 +1,93 @@ +# SVG-спрайт для Angular на Webpack + +Инструкция по быстрому созданию SVG-спрайта в Angular-приложении со штатным Webpack browser builder из Angular CLI. + +## Генерация спрайта + +Выберите каталог для будущего SVG-спрайта, например `assets/app-icons`, и создайте в нём `svg-sprite.config.json`. В `input` укажите путь к существующим SVG относительно файла конфигурации. Перемещать или копировать иконки не требуется. + +```json +{ + "mode": "angular@webpack", + "name": "app", + "input": "../svg-icons/**/*.svg" +} +``` + +Mode предназначен для workspace, где build target использует официальный Webpack builder: + +```json +{ + "builder": "@angular-devkit/build-angular:browser" +} +``` + +Пакет не нужно добавлять в зависимости проекта. Генерируйте спрайт через `npx` перед каждым запуском и сборкой: + +```json +{ + "scripts": { + "sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json", + "prestart": "npm run sprites", + "start": "ng serve", + "prebuild": "npm run sprites", + "build": "ng build" + } +} +``` + +Webpack разрешает generated-выражение `new URL(..., import.meta.url)` и выпускает `sprite.svg` как production asset. + +## Использование спрайта + +Создайте `assets/app-icons/index.ts`: + +```ts +export * from './.svg-sprite/index' +``` + +Импортируйте сгенерированный standalone-компонент. Значение `name: "app"` создаёт `AppIcon` с селектором `app-icon`: + +```ts +import { Component } from '@angular/core' +import { AppIcon } from '../assets/app-icons' + +@Component({ + selector: 'app-root', + standalone: true, + imports: [AppIcon], + template: ` + + `, +}) +export class AppComponent {} +``` + +Input `icon` типизирован именами исходных файлов. Монохромные иконки наследуют `color`, отдельные цвета задаются через `--icon-color-N`. + +## Дебаг и превью + +Viewer необязателен и нужен только при разработке: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Импортируйте `@gromlab/svg-sprites/viewer/element`, добавьте `CUSTOM_ELEMENTS_SCHEMA` и поместите `` в шаблон: + +```ts +readonly viewerSources = [async () => { + const { default: manifest } = await import( + '../assets/app-icons/.svg-sprite/svg-sprite.manifest.js' + ) + const { usage: _usage, ...viewerManifest } = manifest + return viewerManifest +}] +``` + +Viewer и `AppIcon` используют один выпущенный Webpack URL спрайта. diff --git a/docs/ru/guides/astro-vite.md b/docs/ru/guides/astro-vite.md new file mode 100644 index 0000000..34bd738 --- /dev/null +++ b/docs/ru/guides/astro-vite.md @@ -0,0 +1,92 @@ +# SVG-спрайт для Astro на Vite + +Инструкция по быстрому созданию SVG-спрайта в Astro-приложении на Vite. + +## Генерация спрайта + +Выберите каталог для будущего SVG-спрайта, например `assets/app-icons`, и создайте в нём `svg-sprite.config.json`. В `input` укажите путь к существующим SVG относительно файла конфигурации. Перемещать или копировать иконки не требуется. + +```json +{ + "mode": "astro@vite", + "name": "app", + "input": "../svg-icons/**/*.svg" +} +``` + +Пакет не нужно добавлять в зависимости проекта. Генерируйте спрайт через `npx` перед разработкой и production-сборкой: + +```json +{ + "scripts": { + "sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json", + "predev": "npm run sprites", + "dev": "astro dev", + "prebuild": "npm run sprites", + "build": "astro check && astro build" + } +} +``` + +## Использование спрайта + +Создайте `assets/app-icons/index.js`: + +```js +export * from './.svg-sprite/index.js' +``` + +Создайте `assets/app-icons/index.d.ts` для того же типизированного API: + +```ts +export * from './.svg-sprite/index.js' +``` + +Значение `name: "app"` создаёт нативный Astro-компонент `AppIcon`. Используйте его на странице: + +```astro +--- +import { AppIcon } from '../../assets/app-icons/index.js' +--- + + +``` + +Prop `icon` типизирован именами исходных файлов. Vite выпускает `sprite.svg` из статического asset import компонента. + +## Дебаг и превью + +Viewer необязателен и нужен только при разработке: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Добавьте Viewer на страницу и подключите generated manifest в клиентском скрипте: + +```astro + + + +``` + +Manifest сохраняет Astro usage metadata, а Viewer отображает тот же production-спрайт. diff --git a/docs/ru/guides/lit-vite.md b/docs/ru/guides/lit-vite.md new file mode 100644 index 0000000..83e0f10 --- /dev/null +++ b/docs/ru/guides/lit-vite.md @@ -0,0 +1,86 @@ +# SVG-спрайт для Lit на Vite + +Инструкция по быстрому созданию SVG-спрайта в Lit-приложении на Vite. + +## Генерация спрайта + +Выберите каталог для будущего SVG-спрайта, например `assets/app-icons`, и создайте в нём `svg-sprite.config.json`. В `input` укажите путь к существующим SVG относительно файла конфигурации. Перемещать или копировать иконки не требуется. + +```json +{ + "mode": "lit@vite", + "name": "app", + "input": "../svg-icons/**/*.svg" +} +``` + +Генератор не нужно добавлять в зависимости приложения: запускайте его через `npx`. + +Добавьте генерацию перед запуском разработки и production-сборкой: + +```json +{ + "scripts": { + "sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json", + "predev": "npm run sprites", + "dev": "vite", + "prebuild": "npm run sprites", + "build": "vite build" + } +} +``` + +## Использование спрайта + +Значение `name: "app"` создаёт Lit-класс `AppIcon`, тег `` и функцию регистрации `defineAppIcon`. + +Создайте `assets/app-icons/index.js`: + +```js +export * from './.svg-sprite/index.js' +``` + +Зарегистрируйте компонент перед его отображением: + +```js +import { defineAppIcon } from '../assets/app-icons/index.js' + +defineAppIcon() + +document.querySelector('#app').innerHTML = ` + +` +``` + +Свойство `icon` принимает имя исходного SVG без расширения. Монохромная иконка наследует `color`, а слои многоцветной иконки переопределяются через `--icon-color-N`. Vite подключает CSS компонента и выпускает `sprite.svg` отдельным production asset. + +## Дебаг и превью + +Viewer показывает все иконки на одной странице и нужен только при разработке. Установите его отдельно: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Добавьте Viewer на development-страницу: + +```html + + +``` + +Создайте `src/svg-sprite-debug.js`: + +```js +import '@gromlab/svg-sprites/viewer/element' +import spriteManifest from '../assets/app-icons/.svg-sprite/svg-sprite.manifest.js' + +document.querySelector('gromlab-sprite-viewer').sources = [spriteManifest] +``` + +Запустите `npm run dev` и откройте development-страницу. Viewer не требуется для работы `AppIcon`. diff --git a/docs/ru/guides/lit-webpack.md b/docs/ru/guides/lit-webpack.md new file mode 100644 index 0000000..36d7cb4 --- /dev/null +++ b/docs/ru/guides/lit-webpack.md @@ -0,0 +1,98 @@ +# SVG-спрайт для Lit на Webpack 5 + +Инструкция по быстрому созданию SVG-спрайта в Lit-приложении на Webpack 5. + +## Генерация спрайта + +Выберите каталог для будущего SVG-спрайта, например `assets/app-icons`, и создайте в нём `svg-sprite.config.json`. В `input` укажите путь к существующим SVG относительно файла конфигурации. Перемещать или копировать иконки не требуется. + +```json +{ + "mode": "lit@webpack", + "name": "app", + "input": "../svg-icons/**/*.svg" +} +``` + +Генератор не нужно добавлять в зависимости приложения: запускайте его через `npx`. + +Добавьте генерацию перед запуском разработки и production-сборкой: + +```json +{ + "scripts": { + "sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json", + "predev": "npm run sprites", + "dev": "webpack serve --mode development", + "prebuild": "npm run sprites", + "build": "webpack --mode production" + } +} +``` + +## Использование спрайта + +Значение `name: "app"` создаёт Lit-класс `AppIcon`, тег `` и функцию регистрации `defineAppIcon`. + +Generated CSS Lit импортируется с query `?inline`. Добавьте правило Asset Modules в `webpack.config.js`: + +```js +export default { + module: { + rules: [ + { + test: /\.css$/, + resourceQuery: /inline/, + type: 'asset/source', + }, + ], + }, +} +``` + +Создайте `assets/app-icons/index.js`: + +```js +export * from './.svg-sprite/index.js' +``` + +Зарегистрируйте компонент перед его отображением: + +```js +import { defineAppIcon } from '../assets/app-icons/index.js' + +defineAppIcon() + +document.querySelector('#app').innerHTML = ` + +` +``` + +Свойство `icon` принимает имя исходного SVG без расширения. Монохромная иконка наследует `color`, а слои многоцветной иконки переопределяются через `--icon-color-N`. Webpack 5 выпускает `sprite.svg` отдельным production asset. + +## Дебаг и превью + +Viewer показывает все иконки на одной странице и нужен только при разработке. Установите его отдельно: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Добавьте Viewer в development entry: + +```js +import '@gromlab/svg-sprites/viewer/element' +import spriteManifest from '../assets/app-icons/.svg-sprite/svg-sprite.manifest.js' + +const viewer = document.createElement('gromlab-sprite-viewer') +viewer.viewerTitle = 'Иконки проекта' +viewer.sources = [spriteManifest] +document.body.append(viewer) +``` + +Подключайте этот entry только при разработке. Viewer не требуется для работы `AppIcon`. diff --git a/docs/ru/guides/next-app-turbopack.md b/docs/ru/guides/next-app-turbopack.md index 5b6d7eb..4f586a2 100644 --- a/docs/ru/guides/next-app-turbopack.md +++ b/docs/ru/guides/next-app-turbopack.md @@ -4,9 +4,9 @@ ## Генерация спрайта -Выберите папку для спрайта. В примере используется `assets/app-icons`, а исходные SVG, включая используемый ниже `check.svg`, находятся в `assets/svg-icons`. +Выберите каталог для будущего SVG-спрайта, например `assets/app-icons`, и создайте в нём `svg-sprite.config.json`. В `input` укажите путь к существующим SVG относительно файла конфигурации. Перемещать или копировать иконки не требуется. -Создайте конфиг `assets/app-icons/svg-sprite.config.json`: +Пример конфига: ```json { @@ -16,7 +16,7 @@ } ``` -Путь в `input` считается от папки с конфигом. +Пакет не нужно добавлять в зависимости проекта: генерация запускается через `npx`. Добавьте команды генерации в `package.json`. Сгенерированные файлы по умолчанию исключены из Git, поэтому `predev` и `prebuild` пересобирают спрайт перед каждым запуском и сборкой: @@ -51,7 +51,7 @@ import { AppIcon } from '../assets/app-icons' export default function Page() { return ( +import { AppIcon } from '../assets/app-icons/index.js' + + + +``` + +`AppIcon` безопасен для SSR и не требует client-only обёртки. Vite выпускает `sprite.svg` отдельным production asset. + +## Дебаг и превью + +Viewer показывает все иконки на одной странице, позволяет проверить их отображение, изменить цвета и посмотреть связанные CSS-переменные. Он нужен только для разработки и устанавливается отдельно: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Создайте `app/components/SvgSpriteViewer.client.vue`, чтобы browser-only Viewer не выполнялся во время SSR: + +```vue + + + +``` + +Отметьте `gromlab-sprite-viewer` как custom element в `nuxt.config.ts`: + +```ts +export default defineNuxtConfig({ + vue: { + compilerOptions: { + isCustomElement: (tag) => tag === 'gromlab-sprite-viewer', + }, + }, +}) +``` + +Покажите `` на странице разработки. Viewer изолирован от generated runtime компонента `AppIcon`. diff --git a/docs/ru/guides/nuxt-webpack.md b/docs/ru/guides/nuxt-webpack.md new file mode 100644 index 0000000..31d79d3 --- /dev/null +++ b/docs/ru/guides/nuxt-webpack.md @@ -0,0 +1,113 @@ +# SVG-спрайт для Nuxt на Webpack + +Инструкция по быстрому созданию SVG-спрайта в Nuxt-приложении на Webpack. + +## Генерация спрайта + +Выберите каталог для будущего SVG-спрайта, например `assets/app-icons`, и создайте в нём `svg-sprite.config.json`. В `input` укажите путь к существующим SVG относительно файла конфигурации. Перемещать или копировать иконки не требуется. + +Пример конфига: + +```json +{ + "mode": "nuxt@webpack", + "name": "app", + "input": "../svg-icons/**/*.svg" +} +``` + +Пакет не нужно добавлять в зависимости проекта: генерация запускается через `npx`. + +Подключите Webpack builder Nuxt в `nuxt.config.ts`: + +```bash +npm install --save-dev @nuxt/webpack-builder +``` + +```ts +export default defineNuxtConfig({ + builder: '@nuxt/webpack-builder', +}) +``` + +Добавьте команды генерации в `package.json`: + +```json +{ + "scripts": { + "sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json", + "predev": "npm run sprites", + "dev": "nuxt dev", + "prebuild": "npm run sprites", + "build": "nuxt build" + } +} +``` + +## Использование спрайта + +Значение `name: "app"` создаёт Vue-компонент `AppIcon`. Создайте `assets/app-icons/index.js`: + +```js +export * from './.svg-sprite/index.js' +``` + +Используйте компонент на странице или в layout Nuxt: + +```vue + + + +``` + +`AppIcon` безопасен для SSR и не требует client-only обёртки. Webpack выпускает `sprite.svg` отдельным production asset. + +## Дебаг и превью + +Viewer показывает все иконки на одной странице, позволяет проверить их отображение, изменить цвета и посмотреть связанные CSS-переменные. Он нужен только для разработки и устанавливается отдельно: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Создайте `app/components/SvgSpriteViewer.client.vue`, чтобы browser-only Viewer не выполнялся во время SSR: + +```vue + + + +``` + +Дополните существующие настройки `nuxt.config.ts`, чтобы Vue считал Viewer custom element: + +```ts +export default defineNuxtConfig({ + builder: '@nuxt/webpack-builder', + vue: { + compilerOptions: { + isCustomElement: (tag) => tag === 'gromlab-sprite-viewer', + }, + }, +}) +``` + +Покажите `` на странице разработки. Viewer изолирован от generated runtime компонента `AppIcon`. diff --git a/docs/ru/guides/preact-vite.md b/docs/ru/guides/preact-vite.md new file mode 100644 index 0000000..26bea8b --- /dev/null +++ b/docs/ru/guides/preact-vite.md @@ -0,0 +1,75 @@ +# SVG-спрайт для Preact на Vite + +Инструкция по быстрому созданию SVG-спрайта в Preact-приложении на Vite. + +## Генерация спрайта + +Выберите каталог для будущего SVG-спрайта, например `assets/app-icons`, и создайте в нём `svg-sprite.config.json`. В `input` укажите путь к существующим SVG относительно файла конфигурации. Перемещать или копировать иконки не требуется. + +```json +{ + "mode": "preact@vite", + "name": "app", + "input": "../svg-icons/**/*.svg" +} +``` + +Пакет не нужно добавлять в зависимости проекта. Запускайте его через `npx` перед разработкой и production-сборкой: + +```json +{ + "scripts": { + "sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json", + "predev": "npm run sprites", + "dev": "vite", + "prebuild": "npm run sprites", + "build": "tsc --noEmit && vite build" + } +} +``` + +## Использование спрайта + +Создайте `assets/app-icons/index.js` и `assets/app-icons/index.d.ts` с одинаковым экспортом: + +```js +export * from './.svg-sprite/index.js' +``` + +Используйте сгенерированный Preact-компонент на plain JavaScript: + +```jsx +import { AppIcon } from '../assets/app-icons/index.js' + +export function SaveIcon() { + return ( + + ) +} +``` + +Vite автоматически выпускает импортированный `sprite.svg` как production asset. + +## Дебаг и превью + +Установите Viewer только для разработки: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Подключите его в отладочной entry: + +```js +import '@gromlab/svg-sprites/viewer/element' + +const viewer = document.createElement('gromlab-sprite-viewer') +viewer.sources = [() => import('../assets/app-icons/.svg-sprite/svg-sprite.manifest.js')] +document.body.append(viewer) +``` diff --git a/docs/ru/guides/preact-webpack.md b/docs/ru/guides/preact-webpack.md new file mode 100644 index 0000000..e16e9e6 --- /dev/null +++ b/docs/ru/guides/preact-webpack.md @@ -0,0 +1,75 @@ +# SVG-спрайт для Preact на Webpack + +Инструкция по быстрому созданию SVG-спрайта в Preact-приложении на Webpack 5. + +## Генерация спрайта + +Выберите каталог для будущего SVG-спрайта, например `assets/app-icons`, и создайте в нём `svg-sprite.config.json`. В `input` укажите путь к существующим SVG относительно файла конфигурации. Перемещать или копировать иконки не требуется. + +```json +{ + "mode": "preact@webpack", + "name": "app", + "input": "../svg-icons/**/*.svg" +} +``` + +Пакет не нужно добавлять в зависимости проекта. Запускайте его через `npx` перед запуском и сборкой Webpack: + +```json +{ + "scripts": { + "sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json", + "predev": "npm run sprites", + "dev": "webpack serve --mode development", + "prebuild": "npm run sprites", + "build": "tsc --noEmit && webpack --mode production" + } +} +``` + +## Использование спрайта + +Создайте `assets/app-icons/index.js` и `assets/app-icons/index.d.ts` с одинаковым экспортом: + +```js +export * from './.svg-sprite/index.js' +``` + +Используйте сгенерированный Preact-компонент: + +```jsx +import { AppIcon } from '../assets/app-icons/index.js' + +export function SaveIcon() { + return ( + + ) +} +``` + +Webpack Asset Modules выпускают `sprite.svg` из сгенерированного выражения `new URL(...)`. + +## Дебаг и превью + +Установите Viewer только для разработки: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Подключите его в отдельной development entry: + +```js +import '@gromlab/svg-sprites/viewer/element' + +const viewer = document.createElement('gromlab-sprite-viewer') +viewer.sources = [() => import('../assets/app-icons/.svg-sprite/svg-sprite.manifest.js')] +document.body.append(viewer) +``` diff --git a/docs/ru/guides/qwik-vite.md b/docs/ru/guides/qwik-vite.md new file mode 100644 index 0000000..76532cc --- /dev/null +++ b/docs/ru/guides/qwik-vite.md @@ -0,0 +1,82 @@ +# SVG-спрайт для Qwik на Vite + +Инструкция по быстрому созданию SVG-спрайта в SSR-приложении Qwik на Vite. + +## Генерация спрайта + +Выберите каталог для будущего SVG-спрайта, например `assets/app-icons`, и создайте в нём `svg-sprite.config.json`. В `input` укажите путь к существующим SVG относительно файла конфигурации. Перемещать или копировать иконки не требуется. + +```json +{ + "mode": "qwik@vite", + "name": "app", + "input": "../svg-icons/**/*.svg" +} +``` + +Пакет не нужно добавлять в зависимости проекта. Перегенерируйте спрайт через `npx` перед запуском и сборкой Vite: + +```json +{ + "scripts": { + "sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json", + "predev": "npm run sprites", + "dev": "vite --mode ssr", + "prebuild": "npm run sprites", + "build": "tsc --noEmit && vite build" + } +} +``` + +## Использование спрайта + +Создайте `assets/app-icons/index.ts`: + +```ts +export * from './.svg-sprite/index.js' +``` + +Сгенерированный компонент является Qwik `component$` и безопасен во время SSR: + +```tsx +import { component$ } from '@builder.io/qwik' +import { AppIcon } from '../assets/app-icons' + +export default component$(() => ( + +)) +``` + +Компонент использует статический Vite asset import и не обращается к browser globals во время SSR. + +## Дебаг и превью + +Viewer работает только в браузере и нужен лишь для разработки: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Загрузите его из visible task: + +```tsx +import { component$, useSignal, useVisibleTask$ } from '@builder.io/qwik' +import type { SpriteViewerElement } from '@gromlab/svg-sprites/viewer' + +export const IconViewer = component$(() => { + const host = useSignal() + useVisibleTask$(async () => { + await import('@gromlab/svg-sprites/viewer/element') + const viewer = document.createElement('gromlab-sprite-viewer') as SpriteViewerElement + viewer.sources = [() => import('../assets/app-icons/.svg-sprite/svg-sprite.manifest.js')] + host.value?.append(viewer) + }) + return
+}) +``` diff --git a/docs/ru/guides/react-vite.md b/docs/ru/guides/react-vite.md index 67503a0..1f7018c 100644 --- a/docs/ru/guides/react-vite.md +++ b/docs/ru/guides/react-vite.md @@ -4,9 +4,9 @@ ## Генерация спрайта -Выберите папку для спрайта. В примере используется `assets/app-icons`, а исходные SVG, включая используемый ниже `check.svg`, находятся в `assets/svg-icons`. +Выберите каталог для будущего SVG-спрайта, например `assets/app-icons`, и создайте в нём `svg-sprite.config.json`. В `input` укажите путь к существующим SVG относительно файла конфигурации. Перемещать или копировать иконки не требуется. -Создайте конфиг `assets/app-icons/svg-sprite.config.json`: +Пример конфига: ```json { @@ -16,7 +16,7 @@ } ``` -Путь в `input` считается от папки с конфигом. +Пакет не нужно добавлять в зависимости проекта: генерация запускается через `npx`. Добавьте команды генерации в `package.json`. Сгенерированные файлы по умолчанию исключены из Git, поэтому `predev` и `prebuild` пересобирают спрайт перед каждым запуском и сборкой: @@ -50,7 +50,7 @@ import { AppIcon } from '../assets/app-icons' export function SaveIcon() { return ( + ) +} +``` + +Компонент использует статический Vite asset import и не обращается к browser globals во время SSR. + +## Дебаг и превью + +Viewer работает только в браузере и нужен лишь для разработки: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Загрузите его из `onMount`, чтобы исключить из серверного рендера: + +```tsx +import { onMount } from 'solid-js' +import type { SpriteViewerElement } from '@gromlab/svg-sprites/viewer' + +export function IconViewer() { + let host!: HTMLDivElement + onMount(async () => { + await import('@gromlab/svg-sprites/viewer/element') + const viewer = document.createElement('gromlab-sprite-viewer') as SpriteViewerElement + viewer.sources = [() => import('../assets/app-icons/.svg-sprite/svg-sprite.manifest.js')] + host.append(viewer) + }) + return
+} +``` diff --git a/docs/ru/guides/solid-vite.md b/docs/ru/guides/solid-vite.md new file mode 100644 index 0000000..3d0227c --- /dev/null +++ b/docs/ru/guides/solid-vite.md @@ -0,0 +1,83 @@ +# SVG-спрайт для Solid на Vite + +Инструкция по быстрому созданию SVG-спрайта в Solid-приложении на Vite. + +## Генерация спрайта + +Выберите каталог для будущего SVG-спрайта, например `assets/app-icons`, и создайте в нём `svg-sprite.config.json`. В `input` укажите путь к существующим SVG относительно файла конфигурации. Перемещать или копировать иконки не требуется. + +```json +{ + "mode": "solid@vite", + "name": "app", + "input": "../svg-icons/**/*.svg" +} +``` + +Пакет не нужно добавлять в зависимости проекта. Запускайте его через `npx` перед разработкой и production-сборкой: + +```json +{ + "scripts": { + "sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json", + "predev": "npm run sprites", + "dev": "vite", + "prebuild": "npm run sprites", + "build": "tsc --noEmit && vite build" + } +} +``` + +## Использование спрайта + +Создайте `assets/app-icons/index.ts`: + +```ts +export * from './.svg-sprite/index.js' +``` + +Имя `app` создаёт Solid-компонент `AppIcon`: + +```tsx +import { AppIcon } from '../assets/app-icons' + +export function SaveIcon() { + return ( + + ) +} +``` + +Vite выпускает `sprite.svg` как production asset. Монохромные иконки наследуют `color`, многоцветные используют `--icon-color-N`. + +## Дебаг и превью + +Viewer нужен только во время разработки и устанавливается отдельно: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Подключите его в отладочном компоненте после запуска браузерного кода: + +```tsx +import { onMount } from 'solid-js' +import type { SpriteViewerElement } from '@gromlab/svg-sprites/viewer' + +export function IconViewer() { + let host!: HTMLDivElement + onMount(async () => { + await import('@gromlab/svg-sprites/viewer/element') + const viewer = document.createElement('gromlab-sprite-viewer') as SpriteViewerElement + viewer.sources = [() => import('../assets/app-icons/.svg-sprite/svg-sprite.manifest.js')] + host.append(viewer) + }) + return
+} +``` diff --git a/docs/ru/guides/solid-webpack.md b/docs/ru/guides/solid-webpack.md new file mode 100644 index 0000000..602bdae --- /dev/null +++ b/docs/ru/guides/solid-webpack.md @@ -0,0 +1,75 @@ +# SVG-спрайт для Solid на Webpack + +Инструкция по быстрому созданию SVG-спрайта в Solid-приложении на Webpack 5. + +## Генерация спрайта + +Выберите каталог для будущего SVG-спрайта, например `assets/app-icons`, и создайте в нём `svg-sprite.config.json`. В `input` укажите путь к существующим SVG относительно файла конфигурации. Перемещать или копировать иконки не требуется. + +```json +{ + "mode": "solid@webpack", + "name": "app", + "input": "../svg-icons/**/*.svg" +} +``` + +Пакет не нужно добавлять в зависимости проекта. Запускайте его через `npx` перед запуском и сборкой Webpack: + +```json +{ + "scripts": { + "sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json", + "predev": "npm run sprites", + "dev": "webpack serve --mode development", + "prebuild": "npm run sprites", + "build": "tsc --noEmit && webpack --mode production" + } +} +``` + +## Использование спрайта + +Создайте `assets/app-icons/index.js` и `assets/app-icons/index.d.ts` с одинаковым экспортом: + +```js +export * from './.svg-sprite/index.js' +``` + +Используйте сгенерированный Solid-компонент: + +```jsx +import { AppIcon } from '../assets/app-icons/index.js' + +export function SaveIcon() { + return ( + + ) +} +``` + +Webpack Asset Modules выпускают `sprite.svg` из сгенерированного `new URL(...)`. Обработка `.jsx` должна охватывать сгенерированный Solid-компонент. + +## Дебаг и превью + +Установите Viewer только для разработки: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Подключите его в отдельной development entry: + +```js +import '@gromlab/svg-sprites/viewer/element' + +const viewer = document.createElement('gromlab-sprite-viewer') +viewer.sources = [() => import('../assets/app-icons/.svg-sprite/svg-sprite.manifest.js')] +document.body.append(viewer) +``` diff --git a/docs/ru/guides/standalone-vite.md b/docs/ru/guides/standalone-vite.md index ed01987..dce8e9c 100644 --- a/docs/ru/guides/standalone-vite.md +++ b/docs/ru/guides/standalone-vite.md @@ -4,9 +4,9 @@ ## Генерация спрайта -Выберите папку для спрайта. В примере используется `assets/app-icons`, а исходные SVG, включая используемый ниже `check.svg`, находятся в `assets/svg-icons`. +Выберите каталог для будущего SVG-спрайта, например `assets/app-icons`, и создайте в нём `svg-sprite.config.json`. В `input` укажите путь к существующим SVG относительно файла конфигурации. Перемещать или копировать иконки не требуется. -Создайте конфиг `assets/app-icons/svg-sprite.config.json`: +Пример конфига: ```json { @@ -16,7 +16,7 @@ } ``` -Путь в `input` считается от папки с конфигом. +Пакет не нужно добавлять в зависимости проекта: генерация запускается через `npx`. Добавьте команды генерации в `package.json`. Сгенерированные файлы по умолчанию исключены из Git, поэтому `predev` и `prebuild` пересобирают спрайт перед каждым запуском и сборкой: @@ -54,10 +54,10 @@ defineAppIconElement() Используйте иконку в HTML: ```html - + ``` -Файл `check.svg` доступен как `icon="check"`. Размер и цвета настраиваются через CSS: +Значение `icon` — имя исходного SVG без расширения. Размер и цвета настраиваются через CSS: ```css app-icon { diff --git a/docs/ru/guides/standalone-webpack.md b/docs/ru/guides/standalone-webpack.md index ca13ba4..cb14be8 100644 --- a/docs/ru/guides/standalone-webpack.md +++ b/docs/ru/guides/standalone-webpack.md @@ -4,9 +4,9 @@ ## Генерация спрайта -Выберите папку для спрайта. В примере используется `assets/app-icons`, а исходные SVG, включая используемый ниже `check.svg`, находятся в `assets/svg-icons`. +Выберите каталог для будущего SVG-спрайта, например `assets/app-icons`, и создайте в нём `svg-sprite.config.json`. В `input` укажите путь к существующим SVG относительно файла конфигурации. Перемещать или копировать иконки не требуется. -Создайте конфиг `assets/app-icons/svg-sprite.config.json`: +Пример конфига: ```json { @@ -16,7 +16,7 @@ } ``` -Путь в `input` считается от папки с конфигом. +Пакет не нужно добавлять в зависимости проекта: генерация запускается через `npx`. Добавьте команды генерации в `package.json`. Сгенерированные файлы по умолчанию исключены из Git, поэтому `predev` и `prebuild` пересобирают спрайт перед каждым запуском и сборкой: @@ -54,10 +54,10 @@ defineAppIconElement() Используйте иконку в HTML: ```html - + ``` -Файл `check.svg` доступен как `icon="check"`. Размер и цвета настраиваются через CSS: +Значение `icon` — имя исходного SVG без расширения. Размер и цвета настраиваются через CSS: ```css app-icon { diff --git a/docs/ru/guides/standalone.md b/docs/ru/guides/standalone.md index bf36246..9b2ecf9 100644 --- a/docs/ru/guides/standalone.md +++ b/docs/ru/guides/standalone.md @@ -6,32 +6,9 @@ Устанавливать пакет в проект не нужно. -В руководстве используется следующая структура проекта: - -```text -/ -├── index.html -└── assets/ - ├── app-icons/ - └── svg-icons/ - ├── check.svg - └── warning.svg -``` - ### 1. Создайте конфиг спрайта -Выберите папку для спрайта. В этом примере используется `assets/app-icons`. Создайте в ней файл `svg-sprite.config.json`: - -```json -{ - "mode": "standalone", - "name": "icons" -} -``` - -### 2. Укажите источник иконок - -В `input` можно указать папку, отдельный SVG-файл или glob-шаблон. Для нескольких источников используйте массив с любой комбинацией этих значений: +Выберите каталог для будущего SVG-спрайта, например `assets/app-icons`, и создайте в нём `svg-sprite.config.json`. В `input` укажите путь к существующим SVG относительно файла конфигурации. Перемещать или копировать иконки не требуется. ```json { @@ -41,7 +18,7 @@ } ``` -### 3. Сгенерируйте спрайт +### 2. Сгенерируйте спрайт Передайте команде путь к конфигу: @@ -62,7 +39,7 @@ assets/app-icons/.svg-sprite/ Каталог `.svg-sprite` создаётся автоматически и полностью заменяется при каждой генерации. Не редактируйте его содержимое вручную. -### 4. Используйте иконку +### 3. Используйте иконку В `index.html` укажите путь к созданному `sprite.svg`. После `#` добавьте имя нужной иконки без расширения `.svg`: @@ -72,12 +49,10 @@ assets/app-icons/.svg-sprite/ height="24" aria-label="Готово" > - + ``` -Иконка из файла `check.svg` будет доступна как `#check`. - ## Дебаг и превью `sprite.svg` — технический файл, а не галерея иконок. При его открытии нельзя удобно просмотреть весь набор. Кроме того, градиенты, маски, фильтры и ссылки на внутренние `id` могут отображаться с артефактами. @@ -105,4 +80,4 @@ Viewer работает напрямую с файлами из `.svg-sprite`. > ``` -Viewer можно вынести в отдельный HTML-файл в корне сайта, предназначенный только для разработки и проверки иконок. \ No newline at end of file +Viewer можно вынести в отдельный HTML-файл в корне сайта, предназначенный только для разработки и проверки иконок. diff --git a/docs/ru/guides/svelte-vite.md b/docs/ru/guides/svelte-vite.md new file mode 100644 index 0000000..07ed062 --- /dev/null +++ b/docs/ru/guides/svelte-vite.md @@ -0,0 +1,95 @@ +# SVG-спрайт для Svelte на Vite + +Инструкция по быстрому созданию SVG-спрайта в Svelte-приложении на Vite. + +## Генерация спрайта + +Выберите каталог для будущего SVG-спрайта, например `assets/app-icons`, и создайте в нём `svg-sprite.config.json`. В `input` укажите путь к существующим SVG относительно файла конфигурации. Перемещать или копировать иконки не требуется. + +Пример конфига: + +```json +{ + "mode": "svelte@vite", + "name": "app", + "input": "../svg-icons/**/*.svg" +} +``` + +Пакет не нужно добавлять в зависимости проекта: генерация запускается через `npx`. + +Добавьте команды генерации в `package.json`. Сгенерированные файлы по умолчанию исключены из Git, поэтому `predev` и `prebuild` пересобирают спрайт перед каждым запуском и сборкой: + +```json +{ + "scripts": { + "sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json", + "predev": "npm run sprites", + "dev": "vite", + "prebuild": "npm run sprites", + "build": "vite build" + } +} +``` + +## Использование спрайта + +Значение `name: "app"` создаёт Svelte-компонент `AppIcon`. + +Создайте `assets/app-icons/index.js`: + +```js +export * from './.svg-sprite/index.js' +``` + +Используйте компонент в приложении: + +```svelte + + + +``` + +Свойство `icon` принимает имена исходных SVG без расширения. Монохромная иконка наследует `color`, а цвета многоцветной переопределяются через `--icon-color-N`. + +Vite сам подключает стили компонента и выпускает `sprite.svg` отдельным production asset. + +## Дебаг и превью + +Viewer показывает все иконки на одной странице, позволяет проверить их отображение, изменить цвета и посмотреть связанные CSS-переменные. Он нужен только для разработки и устанавливается отдельно: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Добавьте Viewer на страницу или в компонент, используемый только при разработке: + +```svelte + + + +``` + +Запустите `npm run dev` и откройте страницу с Viewer. Не импортируйте этот отладочный компонент из production entry. diff --git a/docs/ru/guides/svelte-webpack.md b/docs/ru/guides/svelte-webpack.md new file mode 100644 index 0000000..32ed502 --- /dev/null +++ b/docs/ru/guides/svelte-webpack.md @@ -0,0 +1,105 @@ +# SVG-спрайт для Svelte на Webpack 5 + +Инструкция по быстрому созданию SVG-спрайта в Svelte-приложении на Webpack 5. + +## Генерация спрайта + +Выберите каталог для будущего SVG-спрайта, например `assets/app-icons`, и создайте в нём `svg-sprite.config.json`. В `input` укажите путь к существующим SVG относительно файла конфигурации. Перемещать или копировать иконки не требуется. + +Пример конфига: + +```json +{ + "mode": "svelte@webpack", + "name": "app", + "input": "../svg-icons/**/*.svg" +} +``` + +Пакет не нужно добавлять в зависимости проекта: генерация запускается через `npx`. + +Добавьте команды генерации в `package.json`: + +```json +{ + "scripts": { + "sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json", + "predev": "npm run sprites", + "dev": "webpack serve --mode development", + "prebuild": "npm run sprites", + "build": "webpack --mode production" + } +} +``` + +## Использование спрайта + +Значение `name: "app"` создаёт Svelte-компонент `AppIcon`. + +Создайте `assets/app-icons/index.js`: + +```js +export * from './.svg-sprite/index.js' +``` + +Используйте компонент в приложении: + +```svelte + + + +``` + +Generated-компонент является нативным `.svelte`-файлом. Обычное правило `svelte-loader` должно обрабатывать `.svelte`-файлы в `assets`: + +```js +{ + test: /\.svelte$/, + use: { + loader: 'svelte-loader', + options: { emitCss: false }, + }, +} +``` + +Webpack 5 обрабатывает asset URL из компонента и выпускает `sprite.svg` отдельным production asset. + +## Дебаг и превью + +Viewer показывает все иконки на одной странице, позволяет проверить их отображение, изменить цвета и посмотреть связанные CSS-переменные. Он нужен только для разработки и устанавливается отдельно: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Добавьте Viewer в Svelte-компонент, используемый только при разработке: + +```svelte + + + +``` + +Запустите `npm run dev` и откройте страницу с Viewer. Не импортируйте этот отладочный компонент из production entry. diff --git a/docs/ru/guides/sveltekit-vite.md b/docs/ru/guides/sveltekit-vite.md new file mode 100644 index 0000000..ccb7815 --- /dev/null +++ b/docs/ru/guides/sveltekit-vite.md @@ -0,0 +1,91 @@ +# SVG-спрайт для SvelteKit на Vite + +Инструкция по быстрому созданию SVG-спрайта в SvelteKit-приложении на Vite. + +## Генерация спрайта + +Выберите каталог для будущего SVG-спрайта, например `assets/app-icons`, и создайте в нём `svg-sprite.config.json`. В `input` укажите путь к существующим SVG относительно файла конфигурации. Перемещать или копировать иконки не требуется. + +Пример конфига: + +```json +{ + "mode": "sveltekit@vite", + "name": "app", + "input": "../svg-icons/**/*.svg" +} +``` + +Пакет не нужно добавлять в зависимости проекта: генерация запускается через `npx`. + +Добавьте команды генерации в `package.json`. Сгенерированные файлы по умолчанию исключены из Git, поэтому `predev` и `prebuild` пересобирают спрайт перед каждым запуском и сборкой: + +```json +{ + "scripts": { + "sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json", + "predev": "npm run sprites", + "dev": "vite dev", + "prebuild": "npm run sprites", + "build": "vite build" + } +} +``` + +## Использование спрайта + +Значение `name: "app"` создаёт SSR-safe Svelte-компонент `AppIcon`. + +Создайте `assets/app-icons/index.js`: + +```js +export * from './.svg-sprite/index.js' +``` + +Используйте компонент в `src/routes/+page.svelte`: + +```svelte + + + +``` + +Свойство `icon` принимает имена исходных SVG без расширения. В компоненте нет browser-only инициализации, поэтому страница может рендериться на сервере. Vite выпускает `sprite.svg` отдельным production asset. + +## Дебаг и превью + +Viewer показывает все иконки на одной странице, позволяет проверить их отображение, изменить цвета и посмотреть связанные CSS-переменные. Он нужен только для разработки и устанавливается отдельно: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Создайте отладочный route, например `src/routes/svg-sprite/+page.svelte`. Загружайте custom element из action, чтобы регистрация выполнялась только в браузере: + +```svelte + + + +``` + +Запустите `npm run dev` и откройте `/svg-sprite`. Action не выполняется во время SSR. diff --git a/docs/ru/guides/vue-vite.md b/docs/ru/guides/vue-vite.md new file mode 100644 index 0000000..fd92974 --- /dev/null +++ b/docs/ru/guides/vue-vite.md @@ -0,0 +1,105 @@ +# SVG-спрайт для Vue на Vite + +Инструкция по быстрому созданию SVG-спрайта в Vue-приложении на Vite. + +## Генерация спрайта + +Выберите каталог для будущего SVG-спрайта, например `assets/app-icons`, и создайте в нём `svg-sprite.config.json`. В `input` укажите путь к существующим SVG относительно файла конфигурации. Перемещать или копировать иконки не требуется. + +Пример конфига: + +```json +{ + "mode": "vue@vite", + "name": "app", + "input": "../svg-icons/**/*.svg" +} +``` + +Пакет не нужно добавлять в зависимости проекта: генерация запускается через `npx`. + +Добавьте команды генерации в `package.json`. Сгенерированные файлы по умолчанию исключены из Git, поэтому `predev` и `prebuild` пересобирают спрайт перед каждым запуском и сборкой: + +```json +{ + "scripts": { + "sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json", + "predev": "npm run sprites", + "dev": "vite", + "prebuild": "npm run sprites", + "build": "vue-tsc --noEmit && vite build" + } +} +``` + +## Использование спрайта + +Значение `name: "app"` создаёт Vue-компонент `AppIcon`. + +Создайте точку входа `assets/app-icons/index.ts`: + +```ts +export * from './.svg-sprite/index.js' +``` + +Используйте компонент в приложении: + +```vue + + + +``` + +Свойство `icon` принимает имена исходных SVG без расширения. Монохромная иконка наследует `color`, а цвета многоцветной переопределяются через `--icon-color-N`. + +Vite сам подключает стили компонента и добавляет `sprite.svg` в итоговую сборку. + +## Дебаг и превью + +Viewer показывает все иконки на одной странице, позволяет проверить их отображение, изменить цвета и посмотреть связанные CSS-переменные. Он нужен только для разработки и устанавливается отдельно: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Создайте `svg-sprite.html` в корне проекта: + +```html + + + + + Иконки проекта + + + + + + +``` + +Создайте `src/svg-sprite-debug.ts`: + +```ts +import '@gromlab/svg-sprites/viewer/element' +import type { SpriteViewerElement } from '@gromlab/svg-sprites/viewer' +import spriteManifest from '../assets/app-icons/.svg-sprite/svg-sprite.manifest.js' + +const viewer = document.querySelector('gromlab-sprite-viewer')! +viewer.sources = [spriteManifest] +``` + +Запустите `npm run dev` и откройте `/svg-sprite.html`. + +Viewer не требуется для работы `AppIcon` и не подключается к основному коду приложения. diff --git a/docs/ru/guides/vue-webpack.md b/docs/ru/guides/vue-webpack.md new file mode 100644 index 0000000..3d7441c --- /dev/null +++ b/docs/ru/guides/vue-webpack.md @@ -0,0 +1,126 @@ +# SVG-спрайт для Vue на Webpack 5 + +Инструкция по быстрому созданию SVG-спрайта в Vue-приложении на Webpack 5. + +## Генерация спрайта + +Выберите каталог для будущего SVG-спрайта, например `assets/app-icons`, и создайте в нём `svg-sprite.config.json`. В `input` укажите путь к существующим SVG относительно файла конфигурации. Перемещать или копировать иконки не требуется. + +Пример конфига: + +```json +{ + "mode": "vue@webpack", + "name": "app", + "input": "../svg-icons/**/*.svg" +} +``` + +Пакет не нужно добавлять в зависимости проекта: генерация запускается через `npx`. + +Добавьте команды генерации в `package.json`. Сгенерированные файлы по умолчанию исключены из Git, поэтому `predev` и `prebuild` пересобирают спрайт перед каждым запуском и сборкой: + +```json +{ + "scripts": { + "sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json", + "predev": "npm run sprites", + "dev": "webpack serve --mode development", + "prebuild": "npm run sprites", + "build": "webpack --mode production" + } +} +``` + +## Использование спрайта + +Значение `name: "app"` создаёт Vue-компонент `AppIcon`. Создайте `assets/app-icons/index.js`: + +```js +export * from './.svg-sprite/index.js' +``` + +Используйте компонент в приложении: + +```vue + + + +``` + +Свойство `icon` принимает имена исходных SVG без расширения. Монохромная иконка наследует `color`, а цвета многоцветной переопределяются через `--icon-color-N`. + +Компонент использует CSS Modules. Если проект ещё не обрабатывает их, установите `style-loader` и `css-loader`, затем добавьте правило с default export: + +```bash +npm install --save-dev style-loader css-loader +``` + +```js +{ + test: /\.module\.css$/i, + use: [ + 'style-loader', + { + loader: 'css-loader', + options: { modules: { namedExport: false } }, + }, + ], +} +``` + +Webpack 5 сам добавляет `sprite.svg` в итоговую сборку. + +## Дебаг и превью + +Viewer показывает все иконки на одной странице, позволяет проверить их отображение, изменить цвета и посмотреть связанные CSS-переменные. Он нужен только для разработки и устанавливается отдельно: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Добавьте Viewer в Vue-компонент, подключаемый только при разработке: + +```vue + + + +``` + +Настройте Vue Loader так, чтобы `gromlab-sprite-viewer` считался custom element: + +```js +{ + test: /\.vue$/, + loader: 'vue-loader', + options: { + compilerOptions: { + isCustomElement: (tag) => tag === 'gromlab-sprite-viewer', + }, + }, +} +``` + +Покажите компонент Viewer на странице разработки. Viewer не требуется для работы `AppIcon`. diff --git a/docs/ru/reference/technical.md b/docs/ru/reference/technical.md index 2ccbc54..179d03e 100644 --- a/docs/ru/reference/technical.md +++ b/docs/ru/reference/technical.md @@ -15,6 +15,26 @@ - [Next.js App Router + Webpack](../guides/next-app-webpack.md) - [Next.js Pages Router + Turbopack](../guides/next-pages-turbopack.md) - [Next.js Pages Router + Webpack](../guides/next-pages-webpack.md) +- [Vue + Vite](../guides/vue-vite.md) +- [Vue + Webpack](../guides/vue-webpack.md) +- [Nuxt + Vite](../guides/nuxt-vite.md) +- [Nuxt + Webpack](../guides/nuxt-webpack.md) +- [Svelte + Vite](../guides/svelte-vite.md) +- [Svelte + Webpack](../guides/svelte-webpack.md) +- [SvelteKit + Vite](../guides/sveltekit-vite.md) +- [Angular application builder](../guides/angular-application.md) +- [Angular + Webpack](../guides/angular-webpack.md) +- [Astro + Vite](../guides/astro-vite.md) +- [Solid + Vite](../guides/solid-vite.md) +- [Solid + Webpack](../guides/solid-webpack.md) +- [SolidStart + Vite](../guides/solid-start-vite.md) +- [Preact + Vite](../guides/preact-vite.md) +- [Preact + Webpack](../guides/preact-webpack.md) +- [Qwik + Vite](../guides/qwik-vite.md) +- [Lit + Vite](../guides/lit-vite.md) +- [Lit + Webpack](../guides/lit-webpack.md) +- [Alpine.js + Vite](../guides/alpine-vite.md) +- [Alpine.js + Webpack](../guides/alpine-webpack.md) ## Требования @@ -51,6 +71,26 @@ svg-sprites [options] | Standalone + Webpack 5 | `standalone@webpack` | | React + Vite | `react@vite` | | React + Webpack 5 | `react@webpack` | +| Vue + Vite | `vue@vite` | +| Vue + Webpack | `vue@webpack` | +| Nuxt + Vite | `nuxt@vite` | +| Nuxt + Webpack | `nuxt@webpack` | +| Svelte + Vite | `svelte@vite` | +| Svelte + Webpack | `svelte@webpack` | +| SvelteKit + Vite | `sveltekit@vite` | +| Angular application builder | `angular@application` | +| Angular + Webpack | `angular@webpack` | +| Astro + Vite | `astro@vite` | +| Solid + Vite | `solid@vite` | +| Solid + Webpack | `solid@webpack` | +| SolidStart + Vite | `solid-start@vite` | +| Preact + Vite | `preact@vite` | +| Preact + Webpack | `preact@webpack` | +| Qwik + Vite | `qwik@vite` | +| Lit + Vite | `lit@vite` | +| Lit + Webpack | `lit@webpack` | +| Alpine.js + Vite | `alpine@vite` | +| Alpine.js + Webpack | `alpine@webpack` | | Next.js App Router + Turbopack | `next@app/turbopack` | | Next.js App Router + Webpack 5 | `next@app/webpack` | | Next.js Pages Router + Turbopack | `next@pages/turbopack` | @@ -309,7 +349,7 @@ folder open.svg → icon="folder open" → id="icon-" ## Множественные спрайты -Каждый каталог с конфигом создаёт независимый mode-specific контракт. React и Next.js создают React-компонент и типы, `standalone@vite` и `standalone@webpack` — Web Component и типы, а bare `standalone` — SVG и JSON manifest: +Каждый каталог с конфигом создаёт независимый mode-specific контракт. Framework modes создают нативный компонент и declarations, standalone bundler modes — Web Component и declarations, а bare `standalone` — SVG и JSON manifest: ```text app-icons → AppIcon → общие иконки @@ -331,7 +371,7 @@ editor-icons → EditorIcon → иконки редактора ### Generated-компонент -Для React и Next.js используйте generated React-компонент. Он знает внутренние ID, формирует URL и предоставляет TypeScript API: +Используйте generated native-компонент из guide выбранного exact mode. Он знает внутренние ID, формирует URL и предоставляет TypeScript API. Для React и Next.js это выглядит так: ```tsx @@ -569,7 +609,7 @@ export const IconsDebugPage = () => ( ) ``` -Viewer показывает группы, поиск, `viewBox`, CSS-переменные и fallback-цвета. React/Next manifests получают вкладки React, SVG, IMG и CSS; standalone manifests получают SVG, IMG и CSS. Цветовые значения можно менять в интерфейсе и сразу проверять результат. +Viewer показывает группы, поиск, `viewBox`, CSS-переменные и fallback-цвета. Framework manifests получают вкладку своего framework, а также SVG, IMG и CSS; standalone manifests получают SVG, IMG и CSS. Цветовые значения можно менять в интерфейсе и сразу проверять результат. ### Тема Viewer diff --git a/integration/README.md b/integration/README.md index 9b3c9a6..e581a1c 100644 --- a/integration/README.md +++ b/integration/README.md @@ -12,13 +12,32 @@ production build и отображения внешнего SVG-спрайта | `standalone-webpack` | `standalone@webpack` | Vanilla TypeScript + Webpack 5 | | `react-vite` | `react@vite` | React + Vite | | `react-webpack` | `react@webpack` | React + Webpack 5 | +| `vue-vite` | `vue@vite` | Vue + Vite | +| `vue-webpack` | `vue@webpack` | Vue + Webpack 5 | +| `nuxt-vite` | `nuxt@vite` | Nuxt + Vite | +| `nuxt-webpack` | `nuxt@webpack` | Nuxt + Webpack | +| `svelte-vite` | `svelte@vite` | Svelte + Vite | +| `svelte-webpack` | `svelte@webpack` | Svelte + Webpack | +| `sveltekit` | `sveltekit@vite` | SvelteKit + Vite | +| `angular` | `angular@application` | Angular application builder | +| `angular-webpack` | `angular@webpack` | Angular + Webpack | +| `astro` | `astro@vite` | Astro + Vite | +| `solid-vite` | `solid@vite` | Solid + Vite | +| `solid-webpack` | `solid@webpack` | Solid + Webpack | +| `solid-start-vite` | `solid-start@vite` | SolidStart + Vite | +| `preact-vite` | `preact@vite` | Preact + Vite | +| `preact-webpack` | `preact@webpack` | Preact + Webpack | +| `qwik-vite` | `qwik@vite` | Qwik + Vite | +| `lit-vite` | `lit@vite` | Lit + Vite | +| `lit-webpack` | `lit@webpack` | Lit + Webpack | +| `alpine-vite` | `alpine@vite` | Alpine.js + Vite | +| `alpine-webpack` | `alpine@webpack` | Alpine.js + Webpack | | `next-app-turbopack` | `next@app/turbopack` | App Router + Turbopack | | `next-app-webpack` | `next@app/webpack` | App Router + Webpack | | `next-pages-turbopack` | `next@pages/turbopack` | Pages Router + Turbopack | | `next-pages-webpack` | `next@pages/webpack` | Pages Router + Webpack | -В verify-матрицу входят только поддерживаемые exact modes. Fixtures остальных -frameworks сохранены как заготовки и будут подключаться после появления их adapters. +В verify-матрицу входят все exact modes из корневого `MODES_CHECKLIST.md`. ## Первый запуск @@ -46,10 +65,12 @@ npm run integration:test Для запуска команды только в одном fixture: ```bash -node integration/scripts/run-workspaces.mjs build react-vite +node scripts/run-workspaces.mjs build react-vite npm run dev --workspace @svg-sprites-fixtures/react-vite --prefix integration ``` +Первую команду запускайте из каталога `integration`. + Перед E2E production builds должны существовать. Тест запускает каждый server последовательно, проверяет внешний `href`, HTTP status и Content-Type спрайта, наличие symbol ID, отсутствие browser errors и зелёные пиксели отрисованной иконки. diff --git a/integration/apps/alpine-vite/index.html b/integration/apps/alpine-vite/index.html new file mode 100644 index 0000000..76e8239 --- /dev/null +++ b/integration/apps/alpine-vite/index.html @@ -0,0 +1,17 @@ + + + + + + Alpine Vite sprite fixture + + + +
+ + + diff --git a/integration/apps/alpine-vite/package.json b/integration/apps/alpine-vite/package.json new file mode 100644 index 0000000..a2e3dfa --- /dev/null +++ b/integration/apps/alpine-vite/package.json @@ -0,0 +1,20 @@ +{ + "name": "@svg-sprites-fixtures/alpine-vite", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "sprites": "svg-sprites src/sprite/svg-sprite.config.json", + "typecheck": "tsc --noEmit", + "build": "npm run sprites && npm run typecheck && vite build", + "dev": "npm run sprites && vite" + }, + "dependencies": { + "alpinejs": "3.15.12" + }, + "devDependencies": { + "@gromlab/svg-sprites": "file:../../..", + "typescript": "6.0.2", + "vite": "8.1.4" + } +} diff --git a/integration/apps/alpine-vite/src/generated-contract.typecheck.ts b/integration/apps/alpine-vite/src/generated-contract.typecheck.ts new file mode 100644 index 0000000..5df6ec7 --- /dev/null +++ b/integration/apps/alpine-vite/src/generated-contract.typecheck.ts @@ -0,0 +1,22 @@ +import { + iconsAlpinePlugin, + iconsIconDirective, + iconsIconMagic, + iconsIconNames, + type IconsIconName, +} from './sprite/index.js' + +declare const Alpine: { plugin(plugin: typeof iconsAlpinePlugin): void } + +const iconName: IconsIconName = iconsIconNames[0] +const directive: 'icons-icon' = iconsIconDirective +const magic: 'iconsIconHref' = iconsIconMagic + +Alpine.plugin(iconsAlpinePlugin) +void iconName +void directive +void magic + +// @ts-expect-error Generated icon names form a literal union. +const missingIcon: IconsIconName = 'missing' +void missingIcon diff --git a/integration/apps/alpine-vite/src/main.js b/integration/apps/alpine-vite/src/main.js new file mode 100644 index 0000000..c3da3d4 --- /dev/null +++ b/integration/apps/alpine-vite/src/main.js @@ -0,0 +1,25 @@ +import '@gromlab/svg-sprites/viewer/element' +import Alpine from 'alpinejs' +import spriteManifest from './sprite/.svg-sprite/svg-sprite.manifest.js' +import { iconsAlpinePlugin } from './sprite/index.js' + +Alpine.plugin(iconsAlpinePlugin) +window.Alpine = Alpine + +document.querySelector('#app').innerHTML = ` +
+

Alpine + Vite

+ + +
+` + +document.querySelector('gromlab-sprite-viewer').sources = [spriteManifest] +Alpine.start() diff --git a/integration/apps/alpine-vite/src/sprite/.gitignore b/integration/apps/alpine-vite/src/sprite/.gitignore new file mode 100644 index 0000000..7f3344e --- /dev/null +++ b/integration/apps/alpine-vite/src/sprite/.gitignore @@ -0,0 +1,2 @@ +# @generated by @gromlab/svg-sprites. Do not edit. +/.svg-sprite/ diff --git a/integration/apps/alpine-vite/src/sprite/index.d.ts b/integration/apps/alpine-vite/src/sprite/index.d.ts new file mode 100644 index 0000000..ceeead8 --- /dev/null +++ b/integration/apps/alpine-vite/src/sprite/index.d.ts @@ -0,0 +1 @@ +export * from './.svg-sprite/index.js' diff --git a/integration/apps/alpine-vite/src/sprite/index.js b/integration/apps/alpine-vite/src/sprite/index.js new file mode 100644 index 0000000..ceeead8 --- /dev/null +++ b/integration/apps/alpine-vite/src/sprite/index.js @@ -0,0 +1 @@ +export * from './.svg-sprite/index.js' diff --git a/integration/apps/alpine-vite/src/sprite/svg-sprite.config.json b/integration/apps/alpine-vite/src/sprite/svg-sprite.config.json new file mode 100644 index 0000000..ac49208 --- /dev/null +++ b/integration/apps/alpine-vite/src/sprite/svg-sprite.config.json @@ -0,0 +1,6 @@ +{ + "mode": "alpine@vite", + "name": "icons", + "input": "../../../../fixtures/icons/check.svg", + "generatedNotice": false +} diff --git a/integration/apps/alpine-vite/tsconfig.json b/integration/apps/alpine-vite/tsconfig.json new file mode 100644 index 0000000..e0507e9 --- /dev/null +++ b/integration/apps/alpine-vite/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "lib": ["ES2022", "DOM"], + "strict": true, + "noEmit": true, + "skipLibCheck": true + }, + "include": ["src/generated-contract.typecheck.ts"] +} diff --git a/integration/apps/alpine-webpack/index.html b/integration/apps/alpine-webpack/index.html new file mode 100644 index 0000000..417f26a --- /dev/null +++ b/integration/apps/alpine-webpack/index.html @@ -0,0 +1,16 @@ + + + + + + Alpine Webpack sprite fixture + + + +
+ + diff --git a/integration/apps/alpine-webpack/package.json b/integration/apps/alpine-webpack/package.json new file mode 100644 index 0000000..c6a363d --- /dev/null +++ b/integration/apps/alpine-webpack/package.json @@ -0,0 +1,23 @@ +{ + "name": "@svg-sprites-fixtures/alpine-webpack", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "sprites": "svg-sprites src/sprite/svg-sprite.config.json", + "typecheck": "tsc --noEmit", + "build": "npm run sprites && npm run typecheck && webpack --mode production", + "dev": "npm run sprites && webpack serve --mode development" + }, + "dependencies": { + "alpinejs": "3.15.12" + }, + "devDependencies": { + "@gromlab/svg-sprites": "file:../../..", + "html-webpack-plugin": "5.6.7", + "typescript": "6.0.2", + "webpack": "5.108.4", + "webpack-cli": "7.2.1", + "webpack-dev-server": "5.2.2" + } +} diff --git a/integration/apps/alpine-webpack/src/generated-contract.typecheck.ts b/integration/apps/alpine-webpack/src/generated-contract.typecheck.ts new file mode 100644 index 0000000..5df6ec7 --- /dev/null +++ b/integration/apps/alpine-webpack/src/generated-contract.typecheck.ts @@ -0,0 +1,22 @@ +import { + iconsAlpinePlugin, + iconsIconDirective, + iconsIconMagic, + iconsIconNames, + type IconsIconName, +} from './sprite/index.js' + +declare const Alpine: { plugin(plugin: typeof iconsAlpinePlugin): void } + +const iconName: IconsIconName = iconsIconNames[0] +const directive: 'icons-icon' = iconsIconDirective +const magic: 'iconsIconHref' = iconsIconMagic + +Alpine.plugin(iconsAlpinePlugin) +void iconName +void directive +void magic + +// @ts-expect-error Generated icon names form a literal union. +const missingIcon: IconsIconName = 'missing' +void missingIcon diff --git a/integration/apps/alpine-webpack/src/main.js b/integration/apps/alpine-webpack/src/main.js new file mode 100644 index 0000000..78a4e41 --- /dev/null +++ b/integration/apps/alpine-webpack/src/main.js @@ -0,0 +1,25 @@ +import '@gromlab/svg-sprites/viewer/element' +import Alpine from 'alpinejs' +import spriteManifest from './sprite/.svg-sprite/svg-sprite.manifest.js' +import { iconsAlpinePlugin } from './sprite/index.js' + +Alpine.plugin(iconsAlpinePlugin) +window.Alpine = Alpine + +document.querySelector('#app').innerHTML = ` +
+

Alpine + Webpack

+ + +
+` + +document.querySelector('gromlab-sprite-viewer').sources = [spriteManifest] +Alpine.start() diff --git a/integration/apps/alpine-webpack/src/sprite/.gitignore b/integration/apps/alpine-webpack/src/sprite/.gitignore new file mode 100644 index 0000000..7f3344e --- /dev/null +++ b/integration/apps/alpine-webpack/src/sprite/.gitignore @@ -0,0 +1,2 @@ +# @generated by @gromlab/svg-sprites. Do not edit. +/.svg-sprite/ diff --git a/integration/apps/alpine-webpack/src/sprite/index.d.ts b/integration/apps/alpine-webpack/src/sprite/index.d.ts new file mode 100644 index 0000000..ceeead8 --- /dev/null +++ b/integration/apps/alpine-webpack/src/sprite/index.d.ts @@ -0,0 +1 @@ +export * from './.svg-sprite/index.js' diff --git a/integration/apps/alpine-webpack/src/sprite/index.js b/integration/apps/alpine-webpack/src/sprite/index.js new file mode 100644 index 0000000..ceeead8 --- /dev/null +++ b/integration/apps/alpine-webpack/src/sprite/index.js @@ -0,0 +1 @@ +export * from './.svg-sprite/index.js' diff --git a/integration/apps/alpine-webpack/src/sprite/svg-sprite.config.json b/integration/apps/alpine-webpack/src/sprite/svg-sprite.config.json new file mode 100644 index 0000000..022defc --- /dev/null +++ b/integration/apps/alpine-webpack/src/sprite/svg-sprite.config.json @@ -0,0 +1,6 @@ +{ + "mode": "alpine@webpack", + "name": "icons", + "input": "../../../../fixtures/icons/check.svg", + "generatedNotice": false +} diff --git a/integration/apps/alpine-webpack/tsconfig.json b/integration/apps/alpine-webpack/tsconfig.json new file mode 100644 index 0000000..e0507e9 --- /dev/null +++ b/integration/apps/alpine-webpack/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "lib": ["ES2022", "DOM"], + "strict": true, + "noEmit": true, + "skipLibCheck": true + }, + "include": ["src/generated-contract.typecheck.ts"] +} diff --git a/integration/apps/alpine-webpack/webpack.config.js b/integration/apps/alpine-webpack/webpack.config.js new file mode 100644 index 0000000..c06f3f0 --- /dev/null +++ b/integration/apps/alpine-webpack/webpack.config.js @@ -0,0 +1,26 @@ +import path from 'node:path' +import { fileURLToPath } from 'node:url' + +import HtmlWebpackPlugin from 'html-webpack-plugin' + +const root = path.dirname(fileURLToPath(import.meta.url)) + +export default { + entry: './src/main.js', + output: { + path: path.join(root, 'dist'), + filename: 'assets/app.[contenthash].js', + assetModuleFilename: 'assets/[name].[contenthash][ext]', + clean: true, + }, + module: { + rules: [ + { + test: /\.css$/, + resourceQuery: /inline/, + type: 'asset/source', + }, + ], + }, + plugins: [new HtmlWebpackPlugin({ template: './index.html' })], +} diff --git a/integration/apps/angular-webpack/package.json b/integration/apps/angular-webpack/package.json new file mode 100644 index 0000000..891bb0b --- /dev/null +++ b/integration/apps/angular-webpack/package.json @@ -0,0 +1,33 @@ +{ + "name": "@svg-sprites-fixtures/angular-webpack", + "private": true, + "version": "0.0.0", + "scripts": { + "sprites": "svg-sprites src/sprite/svg-sprite.config.ts", + "typecheck": "ngc -p tsconfig.json --noEmit", + "build": "npm run sprites && webpack --mode production", + "dev": "npm run sprites && webpack serve --mode development" + }, + "dependencies": { + "@angular/common": "21.2.18", + "@angular/compiler": "21.2.18", + "@angular/core": "21.2.18", + "@angular/platform-browser": "21.2.18", + "rxjs": "7.8.2", + "tslib": "2.8.1", + "zone.js": "0.16.2" + }, + "devDependencies": { + "@angular/compiler-cli": "21.2.18", + "@babel/core": "7.28.6", + "@gromlab/svg-sprites": "file:../../..", + "@ngtools/webpack": "21.2.18", + "babel-loader": "10.0.0", + "html-webpack-plugin": "5.6.6", + "raw-loader": "4.0.2", + "typescript": "5.9.3", + "webpack": "5.105.4", + "webpack-cli": "6.0.1", + "webpack-dev-server": "5.2.3" + } +} diff --git a/integration/apps/angular-webpack/src/index.html b/integration/apps/angular-webpack/src/index.html new file mode 100644 index 0000000..3eecb77 --- /dev/null +++ b/integration/apps/angular-webpack/src/index.html @@ -0,0 +1,11 @@ + + + + + + Angular Webpack sprite fixture + + + + + diff --git a/integration/apps/angular-webpack/src/main.ts b/integration/apps/angular-webpack/src/main.ts new file mode 100644 index 0000000..a9fe7d0 --- /dev/null +++ b/integration/apps/angular-webpack/src/main.ts @@ -0,0 +1,37 @@ +import '@gromlab/svg-sprites/viewer/element' + +import { CUSTOM_ELEMENTS_SCHEMA, Component } from '@angular/core' +import { bootstrapApplication } from '@angular/platform-browser' + +import { IconsIcon } from './sprite' +import './type-probe' + +@Component({ + selector: 'app-root', + standalone: true, + imports: [IconsIcon], + schemas: [CUSTOM_ELEMENTS_SCHEMA], + template: ` +
+

Angular + Webpack

+ + +
+ `, +}) +class AppComponent { + readonly viewerSources = [ + () => import('./sprite/.svg-sprite/svg-sprite.manifest.js'), + ] +} + +bootstrapApplication(AppComponent).catch((error: unknown) => console.error(error)) diff --git a/integration/apps/angular-webpack/src/sprite/.gitignore b/integration/apps/angular-webpack/src/sprite/.gitignore new file mode 100644 index 0000000..7f3344e --- /dev/null +++ b/integration/apps/angular-webpack/src/sprite/.gitignore @@ -0,0 +1,2 @@ +# @generated by @gromlab/svg-sprites. Do not edit. +/.svg-sprite/ diff --git a/integration/apps/angular-webpack/src/sprite/index.ts b/integration/apps/angular-webpack/src/sprite/index.ts new file mode 100644 index 0000000..26b8ed7 --- /dev/null +++ b/integration/apps/angular-webpack/src/sprite/index.ts @@ -0,0 +1 @@ +export * from './.svg-sprite/index' diff --git a/integration/apps/angular-webpack/src/sprite/svg-sprite.config.ts b/integration/apps/angular-webpack/src/sprite/svg-sprite.config.ts new file mode 100644 index 0000000..4384bc5 --- /dev/null +++ b/integration/apps/angular-webpack/src/sprite/svg-sprite.config.ts @@ -0,0 +1,6 @@ +export default { + mode: 'angular@webpack', + name: 'icons', + input: '../../../../fixtures/icons/check.svg', + generatedNotice: false, +} diff --git a/integration/apps/angular-webpack/src/type-probe.ts b/integration/apps/angular-webpack/src/type-probe.ts new file mode 100644 index 0000000..32854b2 --- /dev/null +++ b/integration/apps/angular-webpack/src/type-probe.ts @@ -0,0 +1,8 @@ +import type { IconsIconName } from './sprite' + +const validIcon: IconsIconName = 'check' +void validIcon + +// @ts-expect-error generated icon names are a closed union +const invalidIcon: IconsIconName = 'missing' +void invalidIcon diff --git a/integration/apps/angular-webpack/tsconfig.json b/integration/apps/angular-webpack/tsconfig.json new file mode 100644 index 0000000..1a6e253 --- /dev/null +++ b/integration/apps/angular-webpack/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ES2022", + "moduleResolution": "bundler", + "lib": ["ES2022", "DOM"], + "strict": true, + "experimentalDecorators": true, + "useDefineForClassFields": false, + "skipLibCheck": true + }, + "angularCompilerOptions": { + "strictTemplates": true + }, + "files": ["src/main.ts", "src/type-probe.ts"], + "include": ["src/**/*.d.ts"] +} diff --git a/integration/apps/angular-webpack/webpack.config.js b/integration/apps/angular-webpack/webpack.config.js new file mode 100644 index 0000000..75ee46d --- /dev/null +++ b/integration/apps/angular-webpack/webpack.config.js @@ -0,0 +1,42 @@ +import path from 'node:path' +import { fileURLToPath } from 'node:url' + +import { AngularWebpackPlugin } from '@ngtools/webpack' +import linkerPlugin from '@angular/compiler-cli/linker/babel' +import HtmlWebpackPlugin from 'html-webpack-plugin' + +const rootDir = path.dirname(fileURLToPath(import.meta.url)) + +export default { + entry: path.join(rootDir, 'src/main.ts'), + output: { + path: path.join(rootDir, 'dist'), + filename: 'app.[contenthash].js', + clean: true, + assetModuleFilename: 'assets/[name].[contenthash][ext]', + }, + resolve: { extensions: ['.ts', '.js'] }, + module: { + rules: [ + { test: /\.ts$/, loader: '@ngtools/webpack' }, + { + test: /\.[cm]?js$/, + exclude: /@babel(?:\/|\\)runtime/, + use: { + loader: 'babel-loader', + options: { + compact: false, + plugins: [linkerPlugin], + }, + }, + }, + { test: /\.css$/, use: 'raw-loader' }, + { test: /\.svg$/, type: 'asset/resource' }, + ], + }, + plugins: [ + new AngularWebpackPlugin({ tsconfig: path.join(rootDir, 'tsconfig.json') }), + new HtmlWebpackPlugin({ template: path.join(rootDir, 'src/index.html') }), + ], + devServer: { host: '127.0.0.1', static: path.join(rootDir, 'dist') }, +} diff --git a/integration/apps/angular/angular.json b/integration/apps/angular/angular.json index 9aeba5a..3c0d90e 100644 --- a/integration/apps/angular/angular.json +++ b/integration/apps/angular/angular.json @@ -21,6 +21,9 @@ "input": "public" } ], + "loader": { + ".svg": "file" + }, "styles": ["src/styles.css"], "outputPath": "dist/angular" }, diff --git a/integration/apps/angular/package.json b/integration/apps/angular/package.json index 0a70727..972f7cc 100644 --- a/integration/apps/angular/package.json +++ b/integration/apps/angular/package.json @@ -3,7 +3,7 @@ "private": true, "version": "0.0.0", "scripts": { - "sprites": "svg-sprites --mode legacy .", + "sprites": "svg-sprites src/sprite/svg-sprite.config.ts", "typecheck": "ngc -p tsconfig.app.json --noEmit", "build": "npm run sprites && ng build --configuration production", "dev": "npm run sprites && ng serve" diff --git a/integration/apps/angular/src/app.component.ts b/integration/apps/angular/src/app.component.ts index e10b360..e70f220 100644 --- a/integration/apps/angular/src/app.component.ts +++ b/integration/apps/angular/src/app.component.ts @@ -1,20 +1,33 @@ -import { Component } from '@angular/core' +import '@gromlab/svg-sprites/viewer/element' + +import { CUSTOM_ELEMENTS_SCHEMA, Component } from '@angular/core' + +import { IconsIcon } from './sprite' @Component({ selector: 'app-root', standalone: true, + imports: [IconsIcon], + schemas: [CUSTOM_ELEMENTS_SCHEMA], template: `
-

Angular

- Angular application builder + - - + style="color: #16a34a; --icon-color-1: #16a34a" + /> +
`, }) -export class AppComponent {} +export class AppComponent { + readonly viewerSources = [ + () => import('./sprite/.svg-sprite/svg-sprite.manifest.js'), + ] +} diff --git a/integration/apps/angular/src/sprite/.gitignore b/integration/apps/angular/src/sprite/.gitignore new file mode 100644 index 0000000..7f3344e --- /dev/null +++ b/integration/apps/angular/src/sprite/.gitignore @@ -0,0 +1,2 @@ +# @generated by @gromlab/svg-sprites. Do not edit. +/.svg-sprite/ diff --git a/integration/apps/angular/src/sprite/index.ts b/integration/apps/angular/src/sprite/index.ts new file mode 100644 index 0000000..26b8ed7 --- /dev/null +++ b/integration/apps/angular/src/sprite/index.ts @@ -0,0 +1 @@ +export * from './.svg-sprite/index' diff --git a/integration/apps/angular/src/sprite/svg-sprite.config.ts b/integration/apps/angular/src/sprite/svg-sprite.config.ts new file mode 100644 index 0000000..1481e0e --- /dev/null +++ b/integration/apps/angular/src/sprite/svg-sprite.config.ts @@ -0,0 +1,6 @@ +export default { + mode: 'angular@application', + name: 'icons', + input: '../../../../fixtures/icons/check.svg', + generatedNotice: false, +} diff --git a/integration/apps/angular/src/styles.css b/integration/apps/angular/src/styles.css index 55d4dfd..5f94093 100644 --- a/integration/apps/angular/src/styles.css +++ b/integration/apps/angular/src/styles.css @@ -9,8 +9,13 @@ body { padding: 40px; } -[data-testid='icon'] { +icons-icon[data-testid='icon'] { width: 64px; height: 64px; color: #16a34a; } + +gromlab-sprite-viewer { + display: block; + margin-top: 32px; +} diff --git a/integration/apps/angular/src/type-probe.ts b/integration/apps/angular/src/type-probe.ts new file mode 100644 index 0000000..a50f31d --- /dev/null +++ b/integration/apps/angular/src/type-probe.ts @@ -0,0 +1,10 @@ +import type { IconsIconName } from './sprite' +import type { SpriteManifest } from './sprite/.svg-sprite/svg-sprite.manifest.js' + +const icon: IconsIconName = 'check' +const mode: SpriteManifest['mode'] = 'angular@application' + +// @ts-expect-error Unknown source file names are rejected by the generated union. +const unknownIcon: IconsIconName = 'missing' + +export { icon, mode, unknownIcon } diff --git a/integration/apps/angular/svg-sprites.config.ts b/integration/apps/angular/svg-sprites.config.ts deleted file mode 100644 index 24da7e0..0000000 --- a/integration/apps/angular/svg-sprites.config.ts +++ /dev/null @@ -1,11 +0,0 @@ -export default { - output: 'public/sprites', - preview: false, - sprites: [ - { - name: 'icons', - input: '../../fixtures/icons', - format: 'symbol', - }, - ], -} diff --git a/integration/apps/angular/tsconfig.app.json b/integration/apps/angular/tsconfig.app.json index 5b9d3c5..122a65a 100644 --- a/integration/apps/angular/tsconfig.app.json +++ b/integration/apps/angular/tsconfig.app.json @@ -4,6 +4,6 @@ "outDir": "./out-tsc/app", "types": [] }, - "files": ["src/main.ts"], + "files": ["src/main.ts", "src/type-probe.ts"], "include": ["src/**/*.d.ts"] } diff --git a/integration/apps/angular/tsconfig.json b/integration/apps/angular/tsconfig.json index 46004da..3637ff5 100644 --- a/integration/apps/angular/tsconfig.json +++ b/integration/apps/angular/tsconfig.json @@ -1,6 +1,5 @@ { "compilerOptions": { - "baseUrl": ".", "outDir": "./dist/out-tsc", "forceConsistentCasingInFileNames": true, "strict": true, @@ -10,7 +9,6 @@ "noFallthroughCasesInSwitch": true, "sourceMap": true, "declaration": false, - "downlevelIteration": true, "experimentalDecorators": true, "moduleResolution": "bundler", "importHelpers": true, diff --git a/integration/apps/astro/package.json b/integration/apps/astro/package.json index eb21793..ee8f215 100644 --- a/integration/apps/astro/package.json +++ b/integration/apps/astro/package.json @@ -4,7 +4,7 @@ "version": "0.0.0", "type": "module", "scripts": { - "sprites": "svg-sprites --mode legacy .", + "sprites": "svg-sprites src/sprite/svg-sprite.config.ts", "typecheck": "astro check", "build": "npm run sprites && astro check && astro build", "dev": "npm run sprites && astro dev" diff --git a/integration/apps/astro/src/pages/index.astro b/integration/apps/astro/src/pages/index.astro index a5b9b4b..3f6d296 100644 --- a/integration/apps/astro/src/pages/index.astro +++ b/integration/apps/astro/src/pages/index.astro @@ -1,4 +1,6 @@ --- +import { IconsIcon } from '../sprite/index.js' + const title = 'Astro sprite fixture' --- @@ -12,15 +14,29 @@ const title = 'Astro sprite fixture'

Astro

- - - + width="64" + height="64" + style="--icon-color-1: #16a34a" + /> +
+ @@ -41,4 +57,9 @@ const title = 'Astro sprite fixture' height: 64px; color: #16a34a; } + + gromlab-sprite-viewer { + display: block; + margin-top: 32px; + } diff --git a/integration/apps/astro/src/sprite/.gitignore b/integration/apps/astro/src/sprite/.gitignore new file mode 100644 index 0000000..7f3344e --- /dev/null +++ b/integration/apps/astro/src/sprite/.gitignore @@ -0,0 +1,2 @@ +# @generated by @gromlab/svg-sprites. Do not edit. +/.svg-sprite/ diff --git a/integration/apps/astro/src/sprite/index.d.ts b/integration/apps/astro/src/sprite/index.d.ts new file mode 100644 index 0000000..ceeead8 --- /dev/null +++ b/integration/apps/astro/src/sprite/index.d.ts @@ -0,0 +1 @@ +export * from './.svg-sprite/index.js' diff --git a/integration/apps/astro/src/sprite/index.js b/integration/apps/astro/src/sprite/index.js new file mode 100644 index 0000000..ceeead8 --- /dev/null +++ b/integration/apps/astro/src/sprite/index.js @@ -0,0 +1 @@ +export * from './.svg-sprite/index.js' diff --git a/integration/apps/astro/src/sprite/svg-sprite.config.ts b/integration/apps/astro/src/sprite/svg-sprite.config.ts new file mode 100644 index 0000000..7ae905f --- /dev/null +++ b/integration/apps/astro/src/sprite/svg-sprite.config.ts @@ -0,0 +1,6 @@ +export default { + mode: 'astro@vite', + name: 'icons', + input: '../../../../fixtures/icons/check.svg', + generatedNotice: false, +} diff --git a/integration/apps/astro/src/type-probe.ts b/integration/apps/astro/src/type-probe.ts new file mode 100644 index 0000000..fc24b32 --- /dev/null +++ b/integration/apps/astro/src/type-probe.ts @@ -0,0 +1,11 @@ +import type { IconsIconName, IconsIconProps } from './sprite/index.js' +import type { SpriteManifest } from './sprite/.svg-sprite/svg-sprite.manifest.js' + +const icon: IconsIconName = 'check' +const props: IconsIconProps = { icon, width: 24, 'aria-label': 'Check icon' } +const framework: SpriteManifest['usage']['framework'] = 'astro' + +// @ts-expect-error Unknown source file names are rejected by the generated union. +const unknownIcon: IconsIconName = 'missing' + +export { framework, icon, props, unknownIcon } diff --git a/integration/apps/astro/svg-sprites.config.ts b/integration/apps/astro/svg-sprites.config.ts deleted file mode 100644 index 24da7e0..0000000 --- a/integration/apps/astro/svg-sprites.config.ts +++ /dev/null @@ -1,11 +0,0 @@ -export default { - output: 'public/sprites', - preview: false, - sprites: [ - { - name: 'icons', - input: '../../fixtures/icons', - format: 'symbol', - }, - ], -} diff --git a/integration/apps/lit-vite/index.html b/integration/apps/lit-vite/index.html new file mode 100644 index 0000000..793d364 --- /dev/null +++ b/integration/apps/lit-vite/index.html @@ -0,0 +1,17 @@ + + + + + + Lit Vite sprite fixture + + + +
+ + + diff --git a/integration/apps/lit-vite/package.json b/integration/apps/lit-vite/package.json new file mode 100644 index 0000000..870359f --- /dev/null +++ b/integration/apps/lit-vite/package.json @@ -0,0 +1,20 @@ +{ + "name": "@svg-sprites-fixtures/lit-vite", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "sprites": "svg-sprites src/sprite/svg-sprite.config.json", + "typecheck": "tsc --noEmit", + "build": "npm run sprites && npm run typecheck && vite build", + "dev": "npm run sprites && vite" + }, + "dependencies": { + "lit": "3.3.3" + }, + "devDependencies": { + "@gromlab/svg-sprites": "file:../../..", + "typescript": "6.0.2", + "vite": "8.1.4" + } +} diff --git a/integration/apps/lit-vite/src/generated-contract.typecheck.ts b/integration/apps/lit-vite/src/generated-contract.typecheck.ts new file mode 100644 index 0000000..8204ef3 --- /dev/null +++ b/integration/apps/lit-vite/src/generated-contract.typecheck.ts @@ -0,0 +1,18 @@ +import { + defineIconsIcon, + IconsIcon, + iconsIconNames, + iconsIconTagName, + type IconsIconName, +} from './sprite/index.js' + +const iconName: IconsIconName = iconsIconNames[0] +const icon = new IconsIcon() +icon.icon = iconName + +const registeredIcon = document.createElement(iconsIconTagName) +registeredIcon.icon = 'check' +defineIconsIcon() + +// @ts-expect-error Generated icon names form a literal union. +registeredIcon.icon = 'missing' diff --git a/integration/apps/lit-vite/src/main.js b/integration/apps/lit-vite/src/main.js new file mode 100644 index 0000000..8244b67 --- /dev/null +++ b/integration/apps/lit-vite/src/main.js @@ -0,0 +1,20 @@ +import '@gromlab/svg-sprites/viewer/element' +import spriteManifest from './sprite/.svg-sprite/svg-sprite.manifest.js' +import { defineIconsIcon } from './sprite/index.js' + +defineIconsIcon() + +document.querySelector('#app').innerHTML = ` +

Lit + Vite

+ + +` + +document.querySelector('gromlab-sprite-viewer').sources = [spriteManifest] diff --git a/integration/apps/lit-vite/src/sprite/.gitignore b/integration/apps/lit-vite/src/sprite/.gitignore new file mode 100644 index 0000000..7f3344e --- /dev/null +++ b/integration/apps/lit-vite/src/sprite/.gitignore @@ -0,0 +1,2 @@ +# @generated by @gromlab/svg-sprites. Do not edit. +/.svg-sprite/ diff --git a/integration/apps/lit-vite/src/sprite/index.d.ts b/integration/apps/lit-vite/src/sprite/index.d.ts new file mode 100644 index 0000000..ceeead8 --- /dev/null +++ b/integration/apps/lit-vite/src/sprite/index.d.ts @@ -0,0 +1 @@ +export * from './.svg-sprite/index.js' diff --git a/integration/apps/lit-vite/src/sprite/index.js b/integration/apps/lit-vite/src/sprite/index.js new file mode 100644 index 0000000..ceeead8 --- /dev/null +++ b/integration/apps/lit-vite/src/sprite/index.js @@ -0,0 +1 @@ +export * from './.svg-sprite/index.js' diff --git a/integration/apps/lit-vite/src/sprite/svg-sprite.config.json b/integration/apps/lit-vite/src/sprite/svg-sprite.config.json new file mode 100644 index 0000000..11325da --- /dev/null +++ b/integration/apps/lit-vite/src/sprite/svg-sprite.config.json @@ -0,0 +1,6 @@ +{ + "mode": "lit@vite", + "name": "icons", + "input": "../../../../fixtures/icons/check.svg", + "generatedNotice": false +} diff --git a/integration/apps/lit-vite/tsconfig.json b/integration/apps/lit-vite/tsconfig.json new file mode 100644 index 0000000..e0507e9 --- /dev/null +++ b/integration/apps/lit-vite/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "lib": ["ES2022", "DOM"], + "strict": true, + "noEmit": true, + "skipLibCheck": true + }, + "include": ["src/generated-contract.typecheck.ts"] +} diff --git a/integration/apps/lit-webpack/index.html b/integration/apps/lit-webpack/index.html new file mode 100644 index 0000000..aec10c3 --- /dev/null +++ b/integration/apps/lit-webpack/index.html @@ -0,0 +1,16 @@ + + + + + + Lit Webpack sprite fixture + + + +
+ + diff --git a/integration/apps/lit-webpack/package.json b/integration/apps/lit-webpack/package.json new file mode 100644 index 0000000..81f5714 --- /dev/null +++ b/integration/apps/lit-webpack/package.json @@ -0,0 +1,23 @@ +{ + "name": "@svg-sprites-fixtures/lit-webpack", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "sprites": "svg-sprites src/sprite/svg-sprite.config.json", + "typecheck": "tsc --noEmit", + "build": "npm run sprites && npm run typecheck && webpack --mode production", + "dev": "npm run sprites && webpack serve --mode development" + }, + "dependencies": { + "lit": "3.3.3" + }, + "devDependencies": { + "@gromlab/svg-sprites": "file:../../..", + "html-webpack-plugin": "5.6.7", + "typescript": "6.0.2", + "webpack": "5.108.4", + "webpack-cli": "7.2.1", + "webpack-dev-server": "5.2.2" + } +} diff --git a/integration/apps/lit-webpack/src/generated-contract.typecheck.ts b/integration/apps/lit-webpack/src/generated-contract.typecheck.ts new file mode 100644 index 0000000..8204ef3 --- /dev/null +++ b/integration/apps/lit-webpack/src/generated-contract.typecheck.ts @@ -0,0 +1,18 @@ +import { + defineIconsIcon, + IconsIcon, + iconsIconNames, + iconsIconTagName, + type IconsIconName, +} from './sprite/index.js' + +const iconName: IconsIconName = iconsIconNames[0] +const icon = new IconsIcon() +icon.icon = iconName + +const registeredIcon = document.createElement(iconsIconTagName) +registeredIcon.icon = 'check' +defineIconsIcon() + +// @ts-expect-error Generated icon names form a literal union. +registeredIcon.icon = 'missing' diff --git a/integration/apps/lit-webpack/src/main.js b/integration/apps/lit-webpack/src/main.js new file mode 100644 index 0000000..85c6187 --- /dev/null +++ b/integration/apps/lit-webpack/src/main.js @@ -0,0 +1,20 @@ +import '@gromlab/svg-sprites/viewer/element' +import spriteManifest from './sprite/.svg-sprite/svg-sprite.manifest.js' +import { defineIconsIcon } from './sprite/index.js' + +defineIconsIcon() + +document.querySelector('#app').innerHTML = ` +

Lit + Webpack

+ + +` + +document.querySelector('gromlab-sprite-viewer').sources = [spriteManifest] diff --git a/integration/apps/lit-webpack/src/sprite/.gitignore b/integration/apps/lit-webpack/src/sprite/.gitignore new file mode 100644 index 0000000..7f3344e --- /dev/null +++ b/integration/apps/lit-webpack/src/sprite/.gitignore @@ -0,0 +1,2 @@ +# @generated by @gromlab/svg-sprites. Do not edit. +/.svg-sprite/ diff --git a/integration/apps/lit-webpack/src/sprite/index.d.ts b/integration/apps/lit-webpack/src/sprite/index.d.ts new file mode 100644 index 0000000..ceeead8 --- /dev/null +++ b/integration/apps/lit-webpack/src/sprite/index.d.ts @@ -0,0 +1 @@ +export * from './.svg-sprite/index.js' diff --git a/integration/apps/lit-webpack/src/sprite/index.js b/integration/apps/lit-webpack/src/sprite/index.js new file mode 100644 index 0000000..ceeead8 --- /dev/null +++ b/integration/apps/lit-webpack/src/sprite/index.js @@ -0,0 +1 @@ +export * from './.svg-sprite/index.js' diff --git a/integration/apps/lit-webpack/src/sprite/svg-sprite.config.json b/integration/apps/lit-webpack/src/sprite/svg-sprite.config.json new file mode 100644 index 0000000..0361df5 --- /dev/null +++ b/integration/apps/lit-webpack/src/sprite/svg-sprite.config.json @@ -0,0 +1,6 @@ +{ + "mode": "lit@webpack", + "name": "icons", + "input": "../../../../fixtures/icons/check.svg", + "generatedNotice": false +} diff --git a/integration/apps/lit-webpack/tsconfig.json b/integration/apps/lit-webpack/tsconfig.json new file mode 100644 index 0000000..e0507e9 --- /dev/null +++ b/integration/apps/lit-webpack/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "lib": ["ES2022", "DOM"], + "strict": true, + "noEmit": true, + "skipLibCheck": true + }, + "include": ["src/generated-contract.typecheck.ts"] +} diff --git a/integration/apps/lit-webpack/webpack.config.js b/integration/apps/lit-webpack/webpack.config.js new file mode 100644 index 0000000..c06f3f0 --- /dev/null +++ b/integration/apps/lit-webpack/webpack.config.js @@ -0,0 +1,26 @@ +import path from 'node:path' +import { fileURLToPath } from 'node:url' + +import HtmlWebpackPlugin from 'html-webpack-plugin' + +const root = path.dirname(fileURLToPath(import.meta.url)) + +export default { + entry: './src/main.js', + output: { + path: path.join(root, 'dist'), + filename: 'assets/app.[contenthash].js', + assetModuleFilename: 'assets/[name].[contenthash][ext]', + clean: true, + }, + module: { + rules: [ + { + test: /\.css$/, + resourceQuery: /inline/, + type: 'asset/source', + }, + ], + }, + plugins: [new HtmlWebpackPlugin({ template: './index.html' })], +} diff --git a/integration/apps/nuxt-vite/app/app.vue b/integration/apps/nuxt-vite/app/app.vue new file mode 100644 index 0000000..363da4a --- /dev/null +++ b/integration/apps/nuxt-vite/app/app.vue @@ -0,0 +1,37 @@ + + + + + diff --git a/integration/apps/nuxt-vite/app/components/SpriteViewer.client.vue b/integration/apps/nuxt-vite/app/components/SpriteViewer.client.vue new file mode 100644 index 0000000..dccc494 --- /dev/null +++ b/integration/apps/nuxt-vite/app/components/SpriteViewer.client.vue @@ -0,0 +1,16 @@ + + + diff --git a/integration/apps/nuxt-vite/app/generated-contract.typecheck.ts b/integration/apps/nuxt-vite/app/generated-contract.typecheck.ts new file mode 100644 index 0000000..08d2de8 --- /dev/null +++ b/integration/apps/nuxt-vite/app/generated-contract.typecheck.ts @@ -0,0 +1,15 @@ +import { IconsIcon } from '../src/sprite/.svg-sprite/index.js' +import type { IconsIconName, IconsIconProps } from '../src/sprite/.svg-sprite/index.js' + +const iconName: IconsIconName = 'check' +const iconProps: IconsIconProps = { + icon: iconName, + width: 64, + style: { '--icon-color-1': '#16a34a' }, +} + +void [IconsIcon, iconProps] + +// @ts-expect-error Generated icon names form a literal union. +const missingIcon: IconsIconName = 'missing' +void missingIcon diff --git a/integration/apps/nuxt-vite/nuxt.config.ts b/integration/apps/nuxt-vite/nuxt.config.ts new file mode 100644 index 0000000..4c4d63f --- /dev/null +++ b/integration/apps/nuxt-vite/nuxt.config.ts @@ -0,0 +1,9 @@ +export default defineNuxtConfig({ + compatibilityDate: '2026-07-01', + devtools: { enabled: false }, + vue: { + compilerOptions: { + isCustomElement: (tag) => tag === 'gromlab-sprite-viewer', + }, + }, +}) diff --git a/integration/apps/nuxt-vite/package.json b/integration/apps/nuxt-vite/package.json new file mode 100644 index 0000000..5b77384 --- /dev/null +++ b/integration/apps/nuxt-vite/package.json @@ -0,0 +1,22 @@ +{ + "name": "@svg-sprites-fixtures/nuxt-vite", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "sprites": "svg-sprites src/sprite/svg-sprite.config.ts", + "typecheck": "nuxt typecheck", + "build": "npm run sprites && nuxt build", + "dev": "npm run sprites && nuxt dev", + "start": "node .output/server/index.mjs" + }, + "dependencies": { + "nuxt": "4.3.1", + "vue": "3.5.39" + }, + "devDependencies": { + "@gromlab/svg-sprites": "file:../../..", + "typescript": "6.0.2", + "vue-tsc": "3.3.7" + } +} diff --git a/integration/apps/nuxt-vite/src/sprite/.gitignore b/integration/apps/nuxt-vite/src/sprite/.gitignore new file mode 100644 index 0000000..7f3344e --- /dev/null +++ b/integration/apps/nuxt-vite/src/sprite/.gitignore @@ -0,0 +1,2 @@ +# @generated by @gromlab/svg-sprites. Do not edit. +/.svg-sprite/ diff --git a/integration/apps/nuxt-vite/src/sprite/index.d.ts b/integration/apps/nuxt-vite/src/sprite/index.d.ts new file mode 100644 index 0000000..ceeead8 --- /dev/null +++ b/integration/apps/nuxt-vite/src/sprite/index.d.ts @@ -0,0 +1 @@ +export * from './.svg-sprite/index.js' diff --git a/integration/apps/nuxt-vite/src/sprite/index.js b/integration/apps/nuxt-vite/src/sprite/index.js new file mode 100644 index 0000000..ceeead8 --- /dev/null +++ b/integration/apps/nuxt-vite/src/sprite/index.js @@ -0,0 +1 @@ +export * from './.svg-sprite/index.js' diff --git a/integration/apps/nuxt-vite/src/sprite/svg-sprite.config.ts b/integration/apps/nuxt-vite/src/sprite/svg-sprite.config.ts new file mode 100644 index 0000000..c28161d --- /dev/null +++ b/integration/apps/nuxt-vite/src/sprite/svg-sprite.config.ts @@ -0,0 +1,8 @@ +import { defineSpriteConfig } from '@gromlab/svg-sprites' + +export default defineSpriteConfig({ + mode: 'nuxt@vite', + name: 'icons', + input: '../../../../fixtures/icons/check.svg', + generatedNotice: false, +}) diff --git a/integration/apps/nuxt-webpack/app/app.vue b/integration/apps/nuxt-webpack/app/app.vue new file mode 100644 index 0000000..b17d060 --- /dev/null +++ b/integration/apps/nuxt-webpack/app/app.vue @@ -0,0 +1,37 @@ + + + + + diff --git a/integration/apps/nuxt-webpack/app/components/SpriteViewer.client.vue b/integration/apps/nuxt-webpack/app/components/SpriteViewer.client.vue new file mode 100644 index 0000000..b8ec9b6 --- /dev/null +++ b/integration/apps/nuxt-webpack/app/components/SpriteViewer.client.vue @@ -0,0 +1,16 @@ + + + diff --git a/integration/apps/nuxt-webpack/app/generated-contract.typecheck.ts b/integration/apps/nuxt-webpack/app/generated-contract.typecheck.ts new file mode 100644 index 0000000..08d2de8 --- /dev/null +++ b/integration/apps/nuxt-webpack/app/generated-contract.typecheck.ts @@ -0,0 +1,15 @@ +import { IconsIcon } from '../src/sprite/.svg-sprite/index.js' +import type { IconsIconName, IconsIconProps } from '../src/sprite/.svg-sprite/index.js' + +const iconName: IconsIconName = 'check' +const iconProps: IconsIconProps = { + icon: iconName, + width: 64, + style: { '--icon-color-1': '#16a34a' }, +} + +void [IconsIcon, iconProps] + +// @ts-expect-error Generated icon names form a literal union. +const missingIcon: IconsIconName = 'missing' +void missingIcon diff --git a/integration/apps/nuxt-webpack/nuxt.config.ts b/integration/apps/nuxt-webpack/nuxt.config.ts new file mode 100644 index 0000000..fe79c75 --- /dev/null +++ b/integration/apps/nuxt-webpack/nuxt.config.ts @@ -0,0 +1,28 @@ +export default defineNuxtConfig({ + compatibilityDate: '2026-07-01', + devtools: { enabled: false }, + builder: 'webpack', + nitro: { + externals: { + inline: ['entities'], + }, + }, + hooks: { + 'webpack:config'(configs) { + for (const config of configs) { + const rules = config.module?.rules ?? [] + for (const rule of rules) { + if (rule && typeof rule === 'object' && rule.test instanceof RegExp && rule.test.test('icon.svg')) { + rule.exclude = rule.exclude ? [rule.exclude, /\.svg$/i] : /\.svg$/i + } + } + rules.unshift({ test: /\.svg$/i, type: 'asset/resource' }) + } + }, + }, + vue: { + compilerOptions: { + isCustomElement: (tag) => tag === 'gromlab-sprite-viewer', + }, + }, +}) diff --git a/integration/apps/nuxt-webpack/package.json b/integration/apps/nuxt-webpack/package.json new file mode 100644 index 0000000..7a74b54 --- /dev/null +++ b/integration/apps/nuxt-webpack/package.json @@ -0,0 +1,25 @@ +{ + "name": "@svg-sprites-fixtures/nuxt-webpack", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "sprites": "svg-sprites src/sprite/svg-sprite.config.ts", + "typecheck": "nuxt typecheck", + "build": "npm run sprites && nuxt build", + "dev": "npm run sprites && nuxt dev", + "start": "node .output/server/index.mjs" + }, + "dependencies": { + "entities": "7.0.1", + "nuxt": "4.3.1", + "vue": "3.5.39" + }, + "devDependencies": { + "@gromlab/svg-sprites": "file:../../..", + "@nuxt/webpack-builder": "4.3.1", + "postcss-loader": "8.2.1", + "typescript": "6.0.2", + "vue-tsc": "3.3.7" + } +} diff --git a/integration/apps/nuxt-webpack/src/sprite/.gitignore b/integration/apps/nuxt-webpack/src/sprite/.gitignore new file mode 100644 index 0000000..7f3344e --- /dev/null +++ b/integration/apps/nuxt-webpack/src/sprite/.gitignore @@ -0,0 +1,2 @@ +# @generated by @gromlab/svg-sprites. Do not edit. +/.svg-sprite/ diff --git a/integration/apps/nuxt-webpack/src/sprite/index.d.ts b/integration/apps/nuxt-webpack/src/sprite/index.d.ts new file mode 100644 index 0000000..ceeead8 --- /dev/null +++ b/integration/apps/nuxt-webpack/src/sprite/index.d.ts @@ -0,0 +1 @@ +export * from './.svg-sprite/index.js' diff --git a/integration/apps/nuxt-webpack/src/sprite/index.js b/integration/apps/nuxt-webpack/src/sprite/index.js new file mode 100644 index 0000000..ceeead8 --- /dev/null +++ b/integration/apps/nuxt-webpack/src/sprite/index.js @@ -0,0 +1 @@ +export * from './.svg-sprite/index.js' diff --git a/integration/apps/nuxt-webpack/src/sprite/svg-sprite.config.ts b/integration/apps/nuxt-webpack/src/sprite/svg-sprite.config.ts new file mode 100644 index 0000000..50f16be --- /dev/null +++ b/integration/apps/nuxt-webpack/src/sprite/svg-sprite.config.ts @@ -0,0 +1,8 @@ +import { defineSpriteConfig } from '@gromlab/svg-sprites' + +export default defineSpriteConfig({ + mode: 'nuxt@webpack', + name: 'icons', + input: '../../../../fixtures/icons/check.svg', + generatedNotice: false, +}) diff --git a/integration/apps/preact-vite/index.html b/integration/apps/preact-vite/index.html new file mode 100644 index 0000000..4e6d88a --- /dev/null +++ b/integration/apps/preact-vite/index.html @@ -0,0 +1,12 @@ + + + + + + Preact Vite sprite fixture + + +
+ + + diff --git a/integration/apps/preact-vite/package.json b/integration/apps/preact-vite/package.json new file mode 100644 index 0000000..191e53a --- /dev/null +++ b/integration/apps/preact-vite/package.json @@ -0,0 +1,20 @@ +{ + "name": "@svg-sprites-fixtures/preact-vite", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "sprites": "svg-sprites src/sprite/svg-sprite.config.ts", + "typecheck": "tsc --noEmit", + "build": "npm run sprites && npm run typecheck && vite build", + "dev": "npm run sprites && vite" + }, + "dependencies": { + "preact": "10.27.2" + }, + "devDependencies": { + "@gromlab/svg-sprites": "file:../../..", + "typescript": "6.0.2", + "vite": "8.1.4" + } +} diff --git a/integration/apps/preact-vite/src/generated-contract.typecheck.ts b/integration/apps/preact-vite/src/generated-contract.typecheck.ts new file mode 100644 index 0000000..5b59d88 --- /dev/null +++ b/integration/apps/preact-vite/src/generated-contract.typecheck.ts @@ -0,0 +1,13 @@ +import { IconsIcon, iconsIconNames } from './sprite/index.js' +import type { IconsIconName, IconsIconProps } from './sprite/index.js' + +const iconName: IconsIconName = 'check' +const props: IconsIconProps = { icon: iconName, width: 24 } + +void IconsIcon +void iconsIconNames +void props + +// @ts-expect-error Generated icon names form a literal union. +const missingIcon: IconsIconName = 'missing' +void missingIcon diff --git a/integration/apps/preact-vite/src/main.js b/integration/apps/preact-vite/src/main.js new file mode 100644 index 0000000..e7f70b7 --- /dev/null +++ b/integration/apps/preact-vite/src/main.js @@ -0,0 +1,34 @@ +import { h, render } from 'preact' + +import '@gromlab/svg-sprites/viewer/element' +import { IconsIcon } from './sprite/index.js' +import './style.css' + +const viewerSources = [ + () => import('./sprite/.svg-sprite/svg-sprite.manifest.js'), +] + +function App() { + const connectViewer = (viewer) => { + if (viewer) viewer.sources = viewerSources + } + + return h('main', null, + h('h1', null, 'Preact + Vite'), + h(IconsIcon, { + 'data-testid': 'icon', + 'data-app': 'preact-vite', + icon: 'check', + 'aria-label': 'Check icon', + width: 64, + height: 64, + style: { '--icon-color-1': '#16a34a' }, + }), + h('gromlab-sprite-viewer', { + ref: connectViewer, + 'viewer-title': 'Preact Vite Viewer', + }), + ) +} + +render(h(App), document.getElementById('root')) diff --git a/integration/apps/preact-vite/src/sprite/.gitignore b/integration/apps/preact-vite/src/sprite/.gitignore new file mode 100644 index 0000000..7f3344e --- /dev/null +++ b/integration/apps/preact-vite/src/sprite/.gitignore @@ -0,0 +1,2 @@ +# @generated by @gromlab/svg-sprites. Do not edit. +/.svg-sprite/ diff --git a/integration/apps/preact-vite/src/sprite/index.d.ts b/integration/apps/preact-vite/src/sprite/index.d.ts new file mode 100644 index 0000000..ceeead8 --- /dev/null +++ b/integration/apps/preact-vite/src/sprite/index.d.ts @@ -0,0 +1 @@ +export * from './.svg-sprite/index.js' diff --git a/integration/apps/preact-vite/src/sprite/index.js b/integration/apps/preact-vite/src/sprite/index.js new file mode 100644 index 0000000..ceeead8 --- /dev/null +++ b/integration/apps/preact-vite/src/sprite/index.js @@ -0,0 +1 @@ +export * from './.svg-sprite/index.js' diff --git a/integration/apps/preact-vite/src/sprite/svg-sprite.config.ts b/integration/apps/preact-vite/src/sprite/svg-sprite.config.ts new file mode 100644 index 0000000..e449761 --- /dev/null +++ b/integration/apps/preact-vite/src/sprite/svg-sprite.config.ts @@ -0,0 +1,8 @@ +import { defineSpriteConfig } from '@gromlab/svg-sprites' + +export default defineSpriteConfig({ + mode: 'preact@vite', + name: 'icons', + input: '../../../../fixtures/icons/check.svg', + generatedNotice: false, +}) diff --git a/integration/apps/preact-vite/src/style.css b/integration/apps/preact-vite/src/style.css new file mode 100644 index 0000000..5171e72 --- /dev/null +++ b/integration/apps/preact-vite/src/style.css @@ -0,0 +1,15 @@ +:root { + font-family: system-ui, sans-serif; + color: #172033; + background: #fff; +} + +body { + margin: 0; + padding: 40px; +} + +gromlab-sprite-viewer { + display: block; + margin-top: 32px; +} diff --git a/integration/apps/preact-vite/tsconfig.json b/integration/apps/preact-vite/tsconfig.json new file mode 100644 index 0000000..0d8dcf3 --- /dev/null +++ b/integration/apps/preact-vite/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "strict": true, + "noEmit": true, + "skipLibCheck": true + }, + "include": ["src/**/*.ts"] +} diff --git a/integration/apps/preact-vite/vite.config.ts b/integration/apps/preact-vite/vite.config.ts new file mode 100644 index 0000000..4f1b25a --- /dev/null +++ b/integration/apps/preact-vite/vite.config.ts @@ -0,0 +1,3 @@ +import { defineConfig } from 'vite' + +export default defineConfig({}) diff --git a/integration/apps/preact-webpack/package.json b/integration/apps/preact-webpack/package.json new file mode 100644 index 0000000..7ba31a7 --- /dev/null +++ b/integration/apps/preact-webpack/package.json @@ -0,0 +1,25 @@ +{ + "name": "@svg-sprites-fixtures/preact-webpack", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "sprites": "svg-sprites src/sprite/svg-sprite.config.ts", + "typecheck": "tsc --noEmit", + "build": "npm run sprites && npm run typecheck && webpack --mode production", + "dev": "npm run sprites && webpack serve --mode development" + }, + "dependencies": { + "preact": "10.27.2" + }, + "devDependencies": { + "@gromlab/svg-sprites": "file:../../..", + "css-loader": "7.1.4", + "html-webpack-plugin": "5.6.7", + "style-loader": "4.0.0", + "typescript": "6.0.2", + "webpack": "5.108.4", + "webpack-cli": "7.2.1", + "webpack-dev-server": "5.2.2" + } +} diff --git a/integration/apps/preact-webpack/src/generated-contract.typecheck.ts b/integration/apps/preact-webpack/src/generated-contract.typecheck.ts new file mode 100644 index 0000000..5b59d88 --- /dev/null +++ b/integration/apps/preact-webpack/src/generated-contract.typecheck.ts @@ -0,0 +1,13 @@ +import { IconsIcon, iconsIconNames } from './sprite/index.js' +import type { IconsIconName, IconsIconProps } from './sprite/index.js' + +const iconName: IconsIconName = 'check' +const props: IconsIconProps = { icon: iconName, width: 24 } + +void IconsIcon +void iconsIconNames +void props + +// @ts-expect-error Generated icon names form a literal union. +const missingIcon: IconsIconName = 'missing' +void missingIcon diff --git a/integration/apps/preact-webpack/src/main.js b/integration/apps/preact-webpack/src/main.js new file mode 100644 index 0000000..58b4de4 --- /dev/null +++ b/integration/apps/preact-webpack/src/main.js @@ -0,0 +1,34 @@ +import { h, render } from 'preact' + +import '@gromlab/svg-sprites/viewer/element' +import { IconsIcon } from './sprite/index.js' +import './style.css' + +const viewerSources = [ + () => import('./sprite/.svg-sprite/svg-sprite.manifest.js'), +] + +function App() { + const connectViewer = (viewer) => { + if (viewer) viewer.sources = viewerSources + } + + return h('main', null, + h('h1', null, 'Preact + Webpack'), + h(IconsIcon, { + 'data-testid': 'icon', + 'data-app': 'preact-webpack', + icon: 'check', + 'aria-label': 'Check icon', + width: 64, + height: 64, + style: { '--icon-color-1': '#16a34a' }, + }), + h('gromlab-sprite-viewer', { + ref: connectViewer, + 'viewer-title': 'Preact Webpack Viewer', + }), + ) +} + +render(h(App), document.getElementById('root')) diff --git a/integration/apps/preact-webpack/src/sprite/.gitignore b/integration/apps/preact-webpack/src/sprite/.gitignore new file mode 100644 index 0000000..7f3344e --- /dev/null +++ b/integration/apps/preact-webpack/src/sprite/.gitignore @@ -0,0 +1,2 @@ +# @generated by @gromlab/svg-sprites. Do not edit. +/.svg-sprite/ diff --git a/integration/apps/preact-webpack/src/sprite/index.d.ts b/integration/apps/preact-webpack/src/sprite/index.d.ts new file mode 100644 index 0000000..ceeead8 --- /dev/null +++ b/integration/apps/preact-webpack/src/sprite/index.d.ts @@ -0,0 +1 @@ +export * from './.svg-sprite/index.js' diff --git a/integration/apps/preact-webpack/src/sprite/index.js b/integration/apps/preact-webpack/src/sprite/index.js new file mode 100644 index 0000000..ceeead8 --- /dev/null +++ b/integration/apps/preact-webpack/src/sprite/index.js @@ -0,0 +1 @@ +export * from './.svg-sprite/index.js' diff --git a/integration/apps/preact-webpack/src/sprite/svg-sprite.config.ts b/integration/apps/preact-webpack/src/sprite/svg-sprite.config.ts new file mode 100644 index 0000000..ad5e507 --- /dev/null +++ b/integration/apps/preact-webpack/src/sprite/svg-sprite.config.ts @@ -0,0 +1,8 @@ +import { defineSpriteConfig } from '@gromlab/svg-sprites' + +export default defineSpriteConfig({ + mode: 'preact@webpack', + name: 'icons', + input: '../../../../fixtures/icons/check.svg', + generatedNotice: false, +}) diff --git a/integration/apps/preact-webpack/src/style.css b/integration/apps/preact-webpack/src/style.css new file mode 100644 index 0000000..5171e72 --- /dev/null +++ b/integration/apps/preact-webpack/src/style.css @@ -0,0 +1,15 @@ +:root { + font-family: system-ui, sans-serif; + color: #172033; + background: #fff; +} + +body { + margin: 0; + padding: 40px; +} + +gromlab-sprite-viewer { + display: block; + margin-top: 32px; +} diff --git a/integration/apps/preact-webpack/tsconfig.json b/integration/apps/preact-webpack/tsconfig.json new file mode 100644 index 0000000..0d8dcf3 --- /dev/null +++ b/integration/apps/preact-webpack/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "strict": true, + "noEmit": true, + "skipLibCheck": true + }, + "include": ["src/**/*.ts"] +} diff --git a/integration/apps/preact-webpack/webpack.config.js b/integration/apps/preact-webpack/webpack.config.js new file mode 100644 index 0000000..421de1a --- /dev/null +++ b/integration/apps/preact-webpack/webpack.config.js @@ -0,0 +1,40 @@ +import path from 'node:path' +import { fileURLToPath } from 'node:url' + +import HtmlWebpackPlugin from 'html-webpack-plugin' + +const root = path.dirname(fileURLToPath(import.meta.url)) + +export default { + entry: './src/main.js', + output: { + path: path.join(root, 'dist'), + filename: 'assets/app.[contenthash].js', + assetModuleFilename: 'assets/[name].[contenthash][ext]', + clean: true, + }, + resolve: { + extensions: ['.js'], + alias: { + preact: path.resolve(root, '../../node_modules/preact'), + }, + }, + module: { + rules: [ + { + test: /\.module\.css$/, + use: ['style-loader', { loader: 'css-loader', options: { modules: { namedExport: false } } }], + }, + { + test: /\.css$/, + exclude: /\.module\.css$/, + use: ['style-loader', 'css-loader'], + }, + ], + }, + plugins: [ + new HtmlWebpackPlugin({ + templateContent: 'Preact Webpack sprite fixture
', + }), + ], +} diff --git a/integration/apps/qwik-vite/index.html b/integration/apps/qwik-vite/index.html new file mode 100644 index 0000000..2d34953 --- /dev/null +++ b/integration/apps/qwik-vite/index.html @@ -0,0 +1,11 @@ + + + + + + Qwik Vite sprite fixture + + + + + diff --git a/integration/apps/qwik-vite/package.json b/integration/apps/qwik-vite/package.json new file mode 100644 index 0000000..cbdc8ef --- /dev/null +++ b/integration/apps/qwik-vite/package.json @@ -0,0 +1,20 @@ +{ + "name": "@svg-sprites-fixtures/qwik-vite", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "sprites": "svg-sprites src/sprite/svg-sprite.config.ts", + "typecheck": "tsc --noEmit", + "build": "npm run sprites && npm run typecheck && vite build", + "dev": "npm run sprites && vite --mode ssr" + }, + "dependencies": { + "@builder.io/qwik": "^1.17.0" + }, + "devDependencies": { + "@gromlab/svg-sprites": "file:../../..", + "typescript": "6.0.2", + "vite": "7.3.1" + } +} diff --git a/integration/apps/qwik-vite/src/entry.dev.jsx b/integration/apps/qwik-vite/src/entry.dev.jsx new file mode 100644 index 0000000..08cb7d4 --- /dev/null +++ b/integration/apps/qwik-vite/src/entry.dev.jsx @@ -0,0 +1,5 @@ +import { render } from '@builder.io/qwik' + +import Root from './root' + +export default render(document, ) diff --git a/integration/apps/qwik-vite/src/entry.ssr.jsx b/integration/apps/qwik-vite/src/entry.ssr.jsx new file mode 100644 index 0000000..d7d4e8f --- /dev/null +++ b/integration/apps/qwik-vite/src/entry.ssr.jsx @@ -0,0 +1,7 @@ +import { renderToStream } from '@builder.io/qwik/server' + +import Root from './root' + +export default function render(options) { + return renderToStream(, options) +} diff --git a/integration/apps/qwik-vite/src/generated-contract.typecheck.ts b/integration/apps/qwik-vite/src/generated-contract.typecheck.ts new file mode 100644 index 0000000..4c27e19 --- /dev/null +++ b/integration/apps/qwik-vite/src/generated-contract.typecheck.ts @@ -0,0 +1,13 @@ +import { IconsIcon, iconsIconNames } from './sprite' +import type { IconsIconName, IconsIconProps } from './sprite' + +const iconName: IconsIconName = 'check' +const props: IconsIconProps = { icon: iconName, width: 24 } + +void IconsIcon +void iconsIconNames +void props + +// @ts-expect-error Generated icon names form a literal union. +const missingIcon: IconsIconName = 'missing' +void missingIcon diff --git a/integration/apps/qwik-vite/src/root.jsx b/integration/apps/qwik-vite/src/root.jsx new file mode 100644 index 0000000..50ca18d --- /dev/null +++ b/integration/apps/qwik-vite/src/root.jsx @@ -0,0 +1,32 @@ +import { component$, useSignal, useVisibleTask$ } from '@builder.io/qwik' + +import { IconsIcon } from './sprite' +import './style.css' + +export default component$(() => { + const viewerHost = useSignal() + + useVisibleTask$(async () => { + await import('@gromlab/svg-sprites/viewer/element') + const viewer = document.createElement('gromlab-sprite-viewer') + viewer.viewerTitle = 'Qwik Vite Viewer' + viewer.sources = [() => import('./sprite/.svg-sprite/svg-sprite.manifest.js')] + viewerHost.value?.append(viewer) + }) + + return ( +
+

Qwik + Vite

+ +
+
+ ) +}) diff --git a/integration/apps/qwik-vite/src/sprite/.gitignore b/integration/apps/qwik-vite/src/sprite/.gitignore new file mode 100644 index 0000000..7f3344e --- /dev/null +++ b/integration/apps/qwik-vite/src/sprite/.gitignore @@ -0,0 +1,2 @@ +# @generated by @gromlab/svg-sprites. Do not edit. +/.svg-sprite/ diff --git a/integration/apps/qwik-vite/src/sprite/index.d.ts b/integration/apps/qwik-vite/src/sprite/index.d.ts new file mode 100644 index 0000000..ceeead8 --- /dev/null +++ b/integration/apps/qwik-vite/src/sprite/index.d.ts @@ -0,0 +1 @@ +export * from './.svg-sprite/index.js' diff --git a/integration/apps/qwik-vite/src/sprite/index.js b/integration/apps/qwik-vite/src/sprite/index.js new file mode 100644 index 0000000..411538d --- /dev/null +++ b/integration/apps/qwik-vite/src/sprite/index.js @@ -0,0 +1 @@ +export * from './.svg-sprite' diff --git a/integration/apps/qwik-vite/src/sprite/svg-sprite.config.ts b/integration/apps/qwik-vite/src/sprite/svg-sprite.config.ts new file mode 100644 index 0000000..9650f0d --- /dev/null +++ b/integration/apps/qwik-vite/src/sprite/svg-sprite.config.ts @@ -0,0 +1,8 @@ +import { defineSpriteConfig } from '@gromlab/svg-sprites' + +export default defineSpriteConfig({ + mode: 'qwik@vite', + name: 'icons', + input: '../../../../fixtures/icons/check.svg', + generatedNotice: false, +}) diff --git a/integration/apps/qwik-vite/src/style.css b/integration/apps/qwik-vite/src/style.css new file mode 100644 index 0000000..b52c67b --- /dev/null +++ b/integration/apps/qwik-vite/src/style.css @@ -0,0 +1,14 @@ +:root { + font-family: system-ui, sans-serif; + color: #172033; + background: #fff; +} + +body { + margin: 0; + padding: 40px; +} + +.viewer { + margin-top: 32px; +} diff --git a/integration/apps/qwik-vite/tsconfig.json b/integration/apps/qwik-vite/tsconfig.json new file mode 100644 index 0000000..b365b15 --- /dev/null +++ b/integration/apps/qwik-vite/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "jsx": "preserve", + "jsxImportSource": "@builder.io/qwik", + "strict": true, + "noEmit": true, + "skipLibCheck": true + }, + "include": ["src", "vite.config.ts"] +} diff --git a/integration/apps/qwik-vite/vite.config.ts b/integration/apps/qwik-vite/vite.config.ts new file mode 100644 index 0000000..5226831 --- /dev/null +++ b/integration/apps/qwik-vite/vite.config.ts @@ -0,0 +1,6 @@ +import { qwikVite } from '@builder.io/qwik/optimizer' +import { defineConfig } from 'vite' + +export default defineConfig({ + plugins: [qwikVite({ csr: true })], +}) diff --git a/integration/apps/solid-start-vite/.vinxi/build/client/_build/.vite/manifest.json b/integration/apps/solid-start-vite/.vinxi/build/client/_build/.vite/manifest.json new file mode 100644 index 0000000..eca3610 --- /dev/null +++ b/integration/apps/solid-start-vite/.vinxi/build/client/_build/.vite/manifest.json @@ -0,0 +1,73 @@ +{ + "../../../dist/viewer-element.js": { + "file": "assets/viewer-element-BO6fulPI.js", + "name": "viewer-element", + "src": "../../../dist/viewer-element.js", + "isDynamicEntry": true + }, + "_index-BWUdTtCi.css": { + "file": "assets/index-BWUdTtCi.css", + "src": "_index-BWUdTtCi.css" + }, + "_index-DgFw7Fi1.js": { + "file": "assets/index-DgFw7Fi1.js", + "name": "index", + "isDynamicEntry": true, + "imports": [ + "_preload-helper-C_-y6sQU.js" + ], + "dynamicImports": [ + "../../../dist/viewer-element.js", + "src/sprite/.svg-sprite/svg-sprite.manifest.js" + ], + "css": [ + "assets/index-BWUdTtCi.css" + ], + "assets": [ + "assets/sprite-DvsqBNon.svg" + ] + }, + "_preload-helper-C_-y6sQU.js": { + "file": "assets/preload-helper-C_-y6sQU.js", + "name": "preload-helper" + }, + "src/routes/index.jsx?pick=default&pick=$css": { + "file": "assets/index-B1AR4FGy.js", + "name": "index", + "src": "src/routes/index.jsx?pick=default&pick=$css", + "isEntry": true, + "imports": [ + "_preload-helper-C_-y6sQU.js", + "_index-DgFw7Fi1.js" + ] + }, + "src/sprite/.svg-sprite/sprite.svg": { + "file": "assets/sprite-DvsqBNon.svg", + "src": "src/sprite/.svg-sprite/sprite.svg" + }, + "src/sprite/.svg-sprite/svg-sprite.manifest.js": { + "file": "assets/svg-sprite.manifest-ByXNchEI.js", + "name": "svg-sprite.manifest", + "src": "src/sprite/.svg-sprite/svg-sprite.manifest.js", + "isDynamicEntry": true, + "imports": [ + "_index-DgFw7Fi1.js", + "_preload-helper-C_-y6sQU.js" + ] + }, + "virtual:$vinxi/handler/client": { + "file": "assets/client-BDFkFOh8.js", + "name": "client", + "src": "virtual:$vinxi/handler/client", + "isEntry": true, + "imports": [ + "_preload-helper-C_-y6sQU.js" + ], + "dynamicImports": [ + "src/routes/index.jsx?pick=default&pick=$css" + ], + "css": [ + "assets/client-BVONibDl.css" + ] + } +} \ No newline at end of file diff --git a/integration/apps/solid-start-vite/.vinxi/build/client/_build/assets/client-BDFkFOh8.js b/integration/apps/solid-start-vite/.vinxi/build/client/_build/assets/client-BDFkFOh8.js new file mode 100644 index 0000000..9e8eb67 --- /dev/null +++ b/integration/apps/solid-start-vite/.vinxi/build/client/_build/assets/client-BDFkFOh8.js @@ -0,0 +1,2 @@ +const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/index-B1AR4FGy.js","assets/preload-helper-C_-y6sQU.js","assets/index-DgFw7Fi1.js","assets/index-BWUdTtCi.css"])))=>i.map(i=>d[i]); +import{r as ie,c as A,g as le,a as O,b as Le,o as J,u as Ne,d as ce,e as ue,i as Te,s as Oe,f as Ie,h as ke,j as R,k as De,m as Ue,S as de,l as je,n as fe,p as z,q as Fe,t as I,w as j,x as F,y as M,z as he,A as D,B as Me,_ as We,C as Be,E as qe,D as ze}from"./preload-helper-C_-y6sQU.js";const H="Invariant Violation",{setPrototypeOf:He=function(e,t){return e.__proto__=t,e}}=Object;class Y extends Error{framesToPop=1;name=H;constructor(t=H){super(typeof t=="number"?`${H}: ${t} (see https://github.com/apollographql/invariant-packages)`:t),He(this,Y.prototype)}}function K(e,t){if(!e)throw new Y(t)}const Ke=/^[A-Za-z]:\//;function Ve(e=""){return e&&e.replace(/\\/g,"/").replace(Ke,t=>t.toUpperCase())}const Ze=/^[/\\]{2}/,Ge=/^[/\\](?![/\\])|^[/\\]{2}(?!\.)|^[A-Za-z]:[/\\]/,Xe=/^[A-Za-z]:$/,Je=function(e){if(e.length===0)return".";e=Ve(e);const t=e.match(Ze),n=G(e),r=e[e.length-1]==="/";return e=Ye(e,!n),e.length===0?n?"/":r?"./":".":(r&&(e+="/"),Xe.test(e)&&(e+="/"),t?n?`//${e}`:`//./${e}`:n&&!G(e)?`/${e}`:e)},pe=function(...e){if(e.length===0)return".";let t;for(const n of e)n&&n.length>0&&(t===void 0?t=n:t+=`/${n}`);return t===void 0?".":Je(t.replace(/\/\/+/g,"/"))};function Ye(e,t){let n="",r=0,a=-1,o=0,i=null;for(let s=0;s<=e.length;++s){if(s2){const c=n.lastIndexOf("/");c===-1?(n="",r=0):(n=n.slice(0,c),r=n.length-1-n.lastIndexOf("/")),a=s,o=0;continue}else if(n.length>0){n="",r=0,a=s,o=0;continue}}t&&(n+=n.length>0?"/..":"..",r=2)}else n.length>0?n+=`/${e.slice(a+1,s)}`:n=e.slice(a+1,s),r=s-a-1;a=s,o=0}else i==="."&&o!==-1?++o:o=-1}return n}const G=function(e){return Ge.test(e)};function Qe(e){return`virtual:${e}`}function et(e){return e.handler?.endsWith(".html")?G(e.handler)?e.handler:pe(e.root,e.handler):`$vinxi/handler/${e.name}`}const tt=new Proxy({},{get(e,t){return K(typeof t=="string","Bundler name should be a string"),{name:t,type:"client",handler:Qe(et({name:t})),baseURL:"/_build",chunks:new Proxy({},{get(n,r){K(typeof r=="string","Chunk expected");let a=pe("/_build",r+".mjs");return{import(){return import(a)},output:{path:a}}}}),inputs:new Proxy({},{get(n,r){K(typeof r=="string","Input must be string");let a=window.manifest[r].output;return{async import(){return import(a)},async assets(){return window.manifest[r].assets},output:{path:a}}}})}}});globalThis.MANIFEST=tt;function me(){let e=new Set;function t(a){return e.add(a),()=>e.delete(a)}let n=!1;function r(a,o){if(n)return!(n=!1);const i={to:a,options:o,defaultPrevented:!1,preventDefault:()=>i.defaultPrevented=!0};for(const s of e)s.listener({...i,from:s.location,retry:c=>{c&&(n=!0),s.navigate(a,{...o,resolve:!1})}});return!i.defaultPrevented}return{subscribe:t,confirm:r}}let X;function Q(){(!window.history.state||window.history.state._depth==null)&&window.history.replaceState({...window.history.state,_depth:window.history.length-1},""),X=window.history.state._depth}Q();function nt(e){return{...e,_depth:window.history.state&&window.history.state._depth}}function rt(e,t){let n=!1;return()=>{const r=X;Q();const a=r==null?null:X-r;if(n){n=!1;return}a&&t(a)?(n=!0,window.history.go(-a)):e()}}const at=/^(?:[a-z0-9]+:)?\/\//i,ot=/^\/+|(\/)\/+$/g,ge="http://sr";function k(e,t=!1){const n=e.replace(ot,"$1");return n?t||/^[?#]/.test(n)?n:"/"+n:""}function W(e,t,n){if(at.test(t))return;const r=k(e),a=n&&k(n);let o="";return!a||t.startsWith("/")?o=r:a.toLowerCase().indexOf(r.toLowerCase())!==0?o=r+a:o=a,(o||"/")+k(t,!o)}function st(e,t){return k(e).replace(/\/*(\*.*)?$/g,"")+k(t)}function we(e){const t={};return e.searchParams.forEach((n,r)=>{r in t?Array.isArray(t[r])?t[r].push(n):t[r]=[t[r],n]:t[r]=n}),t}function it(e,t,n){const[r,a]=e.split("/*",2),o=r.split("/").filter(Boolean),i=o.length;return s=>{const c=s.split("/").filter(Boolean),l=c.length-i;if(l<0||l>0&&a===void 0&&!t)return null;const u={path:i?"":"/",params:{}},w=h=>n===void 0?void 0:n[h];for(let h=0;hr===e;return t===void 0?!0:typeof t=="string"?n(t):typeof t=="function"?t(e):Array.isArray(t)?t.some(n):t instanceof RegExp?t.test(e):!1}function lt(e){const[t,n]=e.pattern.split("/*",2),r=t.split("/").filter(Boolean);return r.reduce((a,o)=>a+(o.startsWith(":")?2:3),r.length-(n===void 0?0:1))}function ye(e){const t=new Map,n=le();return new Proxy({},{get(r,a){return t.has(a)||ie(n,()=>t.set(a,A(()=>e()[a]))),t.get(a)()},getOwnPropertyDescriptor(){return{enumerable:!0,configurable:!0}},ownKeys(){return Reflect.ownKeys(e())},has(r,a){return a in e()}})}function ve(e){let t=/(\/?\:[^\/]+)\?/.exec(e);if(!t)return[e];let n=e.slice(0,t.index),r=e.slice(t.index+t[0].length);const a=[n,n+=t[1]];for(;t=/^(\/\:[^\/]+)\?/.exec(r);)a.push(n+=t[1]),r=r.slice(t[0].length);return ve(r).reduce((o,i)=>[...o,...a.map(s=>s+i)],[])}const ct=100,ut=ce(),Re=ce();function dt(e,t=""){const{component:n,preload:r,load:a,children:o,info:i}=e,s=!o||Array.isArray(o)&&!o.length,c={key:e,component:n,preload:r||a,info:i};return be(e.path).reduce((l,u)=>{for(const w of ve(u)){const h=st(t,w);let m=s?h:h.split("/*",1)[0];m=m.split("/").map(b=>b.startsWith(":")||b.startsWith("*")?b:encodeURIComponent(b)).join("/"),l.push({...c,originalPath:u,pattern:m,matcher:it(m,!s,e.matchFilters)})}return l},[])}function ft(e,t=0){return{routes:e,score:lt(e[e.length-1])*1e4-t,matcher(n){const r=[];for(let a=e.length-1;a>=0;a--){const o=e[a],i=o.matcher(n);if(!i)return null;r.unshift({...i,route:o})}return r}}}function be(e){return Array.isArray(e)?e:[e]}function Ee(e,t="",n=[],r=[]){const a=be(e);for(let o=0,i=a.length;oi.score-o.score)}function Z(e,t){for(let n=0,r=e.length;n{const w=e();try{return new URL(w,r)}catch{return console.error(`Invalid path ${w}`),u}},r,{equals:(u,w)=>u.href===w.href}),o=A(()=>a().pathname),i=A(()=>a().search,!0),s=A(()=>a().hash),c=()=>"",l=J(i,()=>we(a()));return{get pathname(){return o()},get search(){return i()},get hash(){return s()},get state(){return t()},get key(){return c()},query:n?n(l):ye(l)}}let S;function pt(){return S}function mt(e,t,n,r={}){const{signal:[a,o],utils:i={}}=e,s=i.parsePath||(p=>p),c=i.renderPath||(p=>p),l=i.beforeLeave||me(),u=W("",r.base||"");if(u===void 0)throw new Error(`${u} is not a valid base path`);u&&!a().value&&o({value:u,replace:!0,scroll:!1});const[w,h]=O(!1);let m;const b=(p,g)=>{g.value===d()&&g.state===v()||(m===void 0&&h(!0),S=p,m=g,Oe(()=>{m===g&&(f(m.value),y(m.state),ke(),C[1](E=>E.filter(x=>x.pending)))}).finally(()=>{m===g&&Ie(()=>{S=void 0,p==="navigate"&&xe(m),h(!1),m=void 0})}))},[d,f]=O(a().value),[v,y]=O(a().state),$=ht(d,v,i.queryWrapper),P=[],C=O([]),N=A(()=>typeof r.transformUrl=="function"?Z(t(),r.transformUrl($.pathname)):Z(t(),$.pathname)),ee=()=>{const p=N(),g={};for(let E=0;Eu,outlet:()=>null,resolvePath(p){return W(u,p)}};return Le(J(a,p=>b("native",p),{defer:!0})),{base:te,location:$,params:$e,isRouting:w,renderPath:c,parsePath:s,navigatorFactory:Se,matches:N,beforeLeave:l,preloadRoute:_e,singleFlight:r.singleFlight===void 0?!0:r.singleFlight,submissions:C};function Ce(p,g,E){ue(()=>{if(typeof g=="number"){g&&(i.go?i.go(g):console.warn("Router integration does not support relative routing"));return}const x=!g||g[0]==="?",{replace:B,resolve:_,scroll:q,state:T}={replace:!1,resolve:!x,scroll:!0,...E},U=_?p.resolvePath(g):W(x&&$.pathname||"",g);if(U===void 0)throw new Error(`Path '${g}' is not a routable path`);if(P.length>=ct)throw new Error("Too many redirects");const ne=d();(U!==ne||T!==v())&&(Te||l.confirm(U,E)&&(P.push({value:ne,replace:B,scroll:q,state:v()}),b("navigate",{value:U,state:T})))})}function Se(p){return p=p||Ne(Re)||te,(g,E)=>Ce(p,g,E)}function xe(p){const g=P[0];g&&(o({...p,replace:g.replace,scroll:g.scroll}),P.length=0)}function _e(p,g){const E=Z(t(),p.pathname),x=S;S="preload";for(let B in E){const{route:_,params:q}=E[B];_.component&&_.component.preload&&_.component.preload();const{preload:T}=_;g&&T&&ie(n(),()=>T({params:q,location:{pathname:p.pathname,search:p.search,hash:p.hash,query:we(p),state:null,key:""},intent:"preload"}))}S=x}}function gt(e,t,n,r){const{base:a,location:o,params:i}=e,{pattern:s,component:c,preload:l}=r().route,u=A(()=>r().path);c&&c.preload&&c.preload();const w=l?l({params:i,location:o,intent:S||"initial"}):void 0;return{parent:t,pattern:s,path:u,outlet:()=>c?R(c,{params:i,location:o,data:w,get children(){return n()}}):n(),resolvePath(m){return W(a.path(),m,u())}}}const wt=e=>t=>{const{base:n}=t,r=De(()=>t.children),a=A(()=>Ee(r(),t.base||""));let o;const i=mt(e,a,()=>o,{base:n,singleFlight:t.singleFlight,transformUrl:t.transformUrl});return e.create&&e.create(i),R(ut.Provider,{value:i,get children(){return R(yt,{routerState:i,get root(){return t.root},get preload(){return t.rootPreload||t.rootLoad},get children(){return[Ue(()=>(o=le())&&null),R(vt,{routerState:i,get branches(){return a()}})]}})}})};function yt(e){const t=e.routerState.location,n=e.routerState.params,r=A(()=>e.preload&&ue(()=>{e.preload({params:n,location:t,intent:pt()||"initial"})}));return R(de,{get when(){return e.root},keyed:!0,get fallback(){return e.children},children:a=>R(a,{params:n,location:t,get data(){return r()},get children(){return e.children}})})}function vt(e){const t=[];let n;const r=A(J(e.routerState.matches,(a,o,i)=>{let s=o&&a.length===o.length;const c=[];for(let l=0,u=a.length;l{t[l]=m,c[l]=gt(e.routerState,c[l-1]||e.routerState.base,re(()=>r()[l+1]),()=>{const b=e.routerState.matches();return b[l]??b[0]})}))}return t.splice(a.length).forEach(l=>l()),i&&s?i:(n=c[0],c)}));return re(()=>r()&&n)()}const re=e=>()=>R(de,{get when(){return e()},keyed:!0,children:t=>R(Re.Provider,{value:t,get children(){return t.outlet()}})});function Rt([e,t],n,r){return[e,r?a=>t(r(a)):t]}function bt(e){let t=!1;const n=a=>typeof a=="string"?{value:a}:a,r=Rt(O(n(e.get()),{equals:(a,o)=>a.value===o.value&&a.state===o.state}),void 0,a=>(!t&&e.set(a),z.registry&&!z.done&&(z.done=!0),a));return e.init&&fe(e.init((a=e.get())=>{t=!0,r[1](n(a)),t=!1})),wt({signal:r,create:e.create,utils:e.utils})}function Et(e,t,n){return e.addEventListener(t,n),()=>e.removeEventListener(t,n)}function Pt(e,t){const n=e&&document.getElementById(e);n?n.scrollIntoView():t&&window.scrollTo(0,0)}const At=new Map;function $t(e=!0,t=!1,n="/_server",r){return a=>{const o=a.base.path(),i=a.navigatorFactory(a.base);let s,c;function l(d){return d.namespaceURI==="http://www.w3.org/2000/svg"}function u(d){if(d.defaultPrevented||d.button!==0||d.metaKey||d.altKey||d.ctrlKey||d.shiftKey)return;const f=d.composedPath().find(N=>N instanceof Node&&N.nodeName.toUpperCase()==="A");if(!f||t&&!f.hasAttribute("link"))return;const v=l(f),y=v?f.href.baseVal:f.href;if((v?f.target.baseVal:f.target)||!y&&!f.hasAttribute("state"))return;const P=(f.getAttribute("rel")||"").split(/\s+/);if(f.hasAttribute("download")||P&&P.includes("external"))return;const C=v?new URL(y,document.baseURI):new URL(y);if(!(C.origin!==window.location.origin||o&&C.pathname&&!C.pathname.toLowerCase().startsWith(o.toLowerCase())))return[f,C]}function w(d){const f=u(d);if(!f)return;const[v,y]=f,$=a.parsePath(y.pathname+y.search+y.hash),P=v.getAttribute("state");d.preventDefault(),i($,{resolve:!1,replace:v.hasAttribute("replace"),scroll:!v.hasAttribute("noscroll"),state:P?JSON.parse(P):void 0})}function h(d){const f=u(d);if(!f)return;const[v,y]=f;r&&(y.pathname=r(y.pathname)),a.preloadRoute(y,v.getAttribute("preload")!=="false")}function m(d){clearTimeout(s);const f=u(d);if(!f)return c=null;const[v,y]=f;c!==v&&(r&&(y.pathname=r(y.pathname)),s=setTimeout(()=>{a.preloadRoute(y,v.getAttribute("preload")!=="false"),c=v},20))}function b(d){if(d.defaultPrevented)return;let f=d.submitter&&d.submitter.hasAttribute("formaction")?d.submitter.getAttribute("formaction"):d.target.getAttribute("action");if(!f)return;if(!f.startsWith("https://action/")){const y=new URL(f,ge);if(f=a.parsePath(y.pathname+y.search),!f.startsWith(n))return}if(d.target.method.toUpperCase()!=="POST")throw new Error("Only POST forms are supported for Actions");const v=At.get(f);if(v){d.preventDefault();const y=new FormData(d.target,d.submitter);v.call({r:a,f:d.target},d.target.enctype==="multipart/form-data"?y:new URLSearchParams(y))}}Fe(["click","submit"]),document.addEventListener("click",w),e&&(document.addEventListener("mousemove",m,{passive:!0}),document.addEventListener("focusin",h,{passive:!0}),document.addEventListener("touchstart",h,{passive:!0})),document.addEventListener("submit",b),fe(()=>{document.removeEventListener("click",w),e&&(document.removeEventListener("mousemove",m),document.removeEventListener("focusin",h),document.removeEventListener("touchstart",h)),document.removeEventListener("submit",b)})}}function Ct(e){const t=()=>{const r=window.location.pathname.replace(/^\/+/,"/")+window.location.search,a=window.history.state&&window.history.state._depth&&Object.keys(window.history.state).length===1?void 0:window.history.state;return{value:r+window.location.hash,state:a}},n=me();return bt({get:t,set({value:r,replace:a,scroll:o,state:i}){a?window.history.replaceState(nt(i),"",r):window.history.pushState(i,"",r),Pt(decodeURIComponent(window.location.hash.slice(1)),o),Q()},init:r=>Et(window,"popstate",rt(r,a=>{if(a)return!n.confirm(a);{const o=t();return!n.confirm(o.value,{state:o.state})}})),create:$t(e.preload,e.explicitLinks,e.actionBase,e.transformUrl),utils:{go:r=>window.history.go(r),beforeLeave:n}})(e)}function St(e){e.forEach(t=>{if(!t.attrs.href)return;let n=document.head.querySelector(`link[href="${t.attrs.href}"]`);n||(n=document.createElement("link"),n.setAttribute("rel","preload"),n.setAttribute("as","style"),n.setAttribute("href",t.attrs.href),document.head.appendChild(n))})}var xt=D(" \ No newline at end of file diff --git a/integration/apps/solid-start-vite/.vinxi/build/client/_build/assets/svg-sprite.manifest-ByXNchEI.js b/integration/apps/solid-start-vite/.vinxi/build/client/_build/assets/svg-sprite.manifest-ByXNchEI.js new file mode 100644 index 0000000..45c8d85 --- /dev/null +++ b/integration/apps/solid-start-vite/.vinxi/build/client/_build/assets/svg-sprite.manifest-ByXNchEI.js @@ -0,0 +1 @@ +import{s as o}from"./index-DgFw7Fi1.js";import"./preload-helper-C_-y6sQU.js";const s={schemaVersion:1,generator:"@gromlab/svg-sprites",name:"icons",usage:{framework:"solid-start",componentName:"IconsIcon",ssr:!0},mode:"solid-start@vite",target:"vite",format:"stack",iconCount:1,spriteUrl:o,icons:[{name:"check",id:"check",viewBox:"0 0 24 24",colors:[{variable:"--icon-color-1",fallback:"currentColor"}]}]};export{s as default,s as spriteManifest}; diff --git a/integration/apps/solid-start-vite/.vinxi/build/client/_build/assets/viewer-element-BO6fulPI.js b/integration/apps/solid-start-vite/.vinxi/build/client/_build/assets/viewer-element-BO6fulPI.js new file mode 100644 index 0000000..a96cb71 --- /dev/null +++ b/integration/apps/solid-start-vite/.vinxi/build/client/_build/assets/viewer-element-BO6fulPI.js @@ -0,0 +1,602 @@ +var Rt=Object.defineProperty,Lt=Object.getOwnPropertyNames,m=(e,t,r)=>function(){if(r)throw r[0];try{return e&&(t=(0,e[Lt(e)[0]])(e=0)),t}catch(o){throw r=[o],o}},it=(e,t)=>{for(var r in t)Rt(e,r,{get:t[r],enumerable:!0})},E,u,te=m({"node_modules/vanilla-colorful/lib/utils/math.js"(){E=(e,t=0,r=1)=>e>r?r:eMath.round(r*e)/r}}),ot,Y,st,Pe,Z,He,S,Ue,Ne,re=m({"node_modules/vanilla-colorful/lib/utils/convert.js"(){te(),ot=e=>Ne(Y(e)),Y=e=>(e[0]==="#"&&(e=e.substring(1)),e.length<6?{r:parseInt(e[0]+e[0],16),g:parseInt(e[1]+e[1],16),b:parseInt(e[2]+e[2],16),a:e.length===4?u(parseInt(e[3]+e[3],16)/255,2):1}:{r:parseInt(e.substring(0,2),16),g:parseInt(e.substring(2,4),16),b:parseInt(e.substring(4,6),16),a:e.length===8?u(parseInt(e.substring(6,8),16)/255,2):1}),st=e=>Ue(He(e)),Pe=({h:e,s:t,v:r,a:i})=>{const o=(200-t)*r/100;return{h:u(e),s:u(o>0&&o<200?t*r/100/(o<=100?o:200-o)*100:0),l:u(o/2),a:u(i,2)}},Z=e=>{const{h:t,s:r,l:i}=Pe(e);return`hsl(${t}, ${r}%, ${i}%)`},He=({h:e,s:t,v:r,a:i})=>{e=e/360*6,t=t/100,r=r/100;const o=Math.floor(e),s=r*(1-t),a=r*(1-(e-o)*t),n=r*(1-(1-e+o)*t),l=o%6;return{r:u([r,a,s,s,n,r][l]*255),g:u([n,r,r,a,s,s][l]*255),b:u([s,s,n,r,r,a][l]*255),a:u(i,2)}},S=e=>{const t=e.toString(16);return t.length<2?"0"+t:t},Ue=({r:e,g:t,b:r,a:i})=>{const o=i<1?S(u(i*255)):"";return"#"+S(e)+S(t)+S(r)+o},Ne=({r:e,g:t,b:r,a:i})=>{const o=Math.max(e,t,r),s=o-Math.min(e,t,r),a=s?o===e?(t-r)/s:o===t?2+(r-e)/s:4+(e-t)/s:0;return{h:u(60*(a<0?a+6:a)),s:u(o?s/o*100:0),v:u(o/255*100),a:i}}}}),$e,at,nt=m({"node_modules/vanilla-colorful/lib/utils/compare.js"(){re(),$e=(e,t)=>{if(e===t)return!0;for(const r in e)if(e[r]!==t[r])return!1;return!0},at=(e,t)=>e.toLowerCase()===t.toLowerCase()?!0:$e(Y(e),Y(t))}}),ae,ie,X,Ae=m({"node_modules/vanilla-colorful/lib/utils/dom.js"(){ae={},ie=e=>{let t=ae[e];return t||(t=document.createElement("template"),t.innerHTML=e,ae[e]=t),t},X=(e,t,r)=>{e.dispatchEvent(new CustomEvent(t,{bubbles:!0,detail:r}))}}}),w,z,De,ne,Re,Ee,lt=m({"node_modules/vanilla-colorful/lib/components/slider.js"(){Ae(),te(),w=!1,z=e=>"touches"in e,De=e=>w&&!z(e)?!1:(w||(w=z(e)),!0),ne=(e,t)=>{const r=z(t)?t.touches[0]:t,i=e.el.getBoundingClientRect();X(e.el,"move",e.getMove({x:E((r.pageX-(i.left+window.pageXOffset))/i.width),y:E((r.pageY-(i.top+window.pageYOffset))/i.height)}))},Re=(e,t)=>{const r=t.keyCode;r>40||e.xy&&r<37||r<33||(t.preventDefault(),X(e.el,"move",e.getMove({x:r===39?.01:r===37?-.01:r===34?.05:r===33?-.05:r===35?1:r===36?-1:0,y:r===40?.01:r===38?-.01:0},!0)))},Ee=class{constructor(e,t,r,i){const o=ie(`
`);e.appendChild(o.content.cloneNode(!0));const s=e.querySelector(`[part=${t}]`);s.addEventListener("mousedown",this),s.addEventListener("touchstart",this),s.addEventListener("keydown",this),this.el=s,this.xy=i,this.nodes=[s.firstChild,s]}set dragging(e){const t=e?document.addEventListener:document.removeEventListener;t(w?"touchmove":"mousemove",this),t(w?"touchend":"mouseup",this)}handleEvent(e){switch(e.type){case"mousedown":case"touchstart":if(e.preventDefault(),!De(e)||!w&&e.button!=0)return;this.el.focus(),ne(this,e),this.dragging=!0;break;case"mousemove":case"touchmove":e.preventDefault(),ne(this,e);break;case"mouseup":case"touchend":this.dragging=!1;break;case"keydown":Re(this,e);break}}style(e){e.forEach((t,r)=>{for(const i in t)this.nodes[r].style.setProperty(i,t[i])})}}}}),ct,jt=m({"node_modules/vanilla-colorful/lib/components/hue.js"(){lt(),re(),te(),ct=class extends Ee{constructor(e){super(e,"hue",'aria-label="Hue" aria-valuemin="0" aria-valuemax="360"',!1)}update({h:e}){this.h=e,this.style([{left:`${e/360*100}%`,color:Z({h:e,s:100,v:100,a:1})}]),this.el.setAttribute("aria-valuenow",`${u(e)}`)}getMove(e,t){return{h:t?E(this.h+e.x*360,0,360):360*e.x}}}}}),dt,zt=m({"node_modules/vanilla-colorful/lib/components/saturation.js"(){lt(),re(),te(),dt=class extends Ee{constructor(e){super(e,"saturation",'aria-label="Color"',!0)}update(e){this.hsva=e,this.style([{top:`${100-e.v}%`,left:`${e.s}%`,color:Z(e)},{"background-color":Z({h:e.h,s:100,v:100,a:1})}]),this.el.setAttribute("aria-valuetext",`Saturation ${u(e.s)}%, Brightness ${u(e.v)}%`)}getMove(e,t){return{s:t?E(this.hsva.s+e.x*100,0,100):e.x*100,v:t?E(this.hsva.v-e.y*100,0,100):Math.round(100-e.y*100)}}}}}),ht,Bt=m({"node_modules/vanilla-colorful/lib/styles/color-picker.js"(){ht=':host{display:flex;flex-direction:column;position:relative;width:200px;height:200px;user-select:none;-webkit-user-select:none;cursor:default}:host([hidden]){display:none!important}[role=slider]{position:relative;touch-action:none;user-select:none;-webkit-user-select:none;outline:0}[role=slider]:last-child{border-radius:0 0 8px 8px}[part$=pointer]{position:absolute;z-index:1;box-sizing:border-box;width:28px;height:28px;display:flex;place-content:center center;transform:translate(-50%,-50%);background-color:#fff;border:2px solid #fff;border-radius:50%;box-shadow:0 2px 4px rgba(0,0,0,.2)}[part$=pointer]::after{content:"";width:100%;height:100%;border-radius:inherit;background-color:currentColor}[role=slider]:focus [part$=pointer]{transform:translate(-50%,-50%) scale(1.1)}'}}),pt,It=m({"node_modules/vanilla-colorful/lib/styles/hue.js"(){pt="[part=hue]{flex:0 0 24px;background:linear-gradient(to right,red 0,#ff0 17%,#0f0 33%,#0ff 50%,#00f 67%,#f0f 83%,red 100%)}[part=hue-pointer]{top:50%;z-index:2}"}}),ut,Vt=m({"node_modules/vanilla-colorful/lib/styles/saturation.js"(){ut="[part=saturation]{flex-grow:1;border-color:transparent;border-bottom:12px solid #000;border-radius:8px 8px 0 0;background-image:linear-gradient(to top,#000,transparent),linear-gradient(to right,#fff,rgba(255,255,255,0));box-shadow:inset 0 0 0 1px rgba(0,0,0,.05)}[part=saturation-pointer]{z-index:3}"}}),T,B,le,I,ce,de,he,gt,qt=m({"node_modules/vanilla-colorful/lib/components/color-picker.js"(){nt(),Ae(),jt(),zt(),Bt(),It(),Vt(),T=Symbol("same"),B=Symbol("color"),le=Symbol("hsva"),I=Symbol("update"),ce=Symbol("parts"),de=Symbol("css"),he=Symbol("sliders"),gt=class extends HTMLElement{static get observedAttributes(){return["color"]}get[de](){return[ht,pt,ut]}get[he](){return[dt,ct]}get color(){return this[B]}set color(e){if(!this[T](e)){const t=this.colorModel.toHsva(e);this[I](t),this[B]=e}}constructor(){super();const e=ie(``),t=this.attachShadow({mode:"open"});t.appendChild(e.content.cloneNode(!0)),t.addEventListener("move",this),this[ce]=this[he].map(r=>new r(t))}connectedCallback(){if(this.hasOwnProperty("color")){const e=this.color;delete this.color,this.color=e}else this.color||(this.color=this.colorModel.defaultColor)}attributeChangedCallback(e,t,r){const i=this.colorModel.fromAttr(r);this[T](i)||(this.color=i)}handleEvent(e){const t=this[le],r={...t,...e.detail};this[I](r);let i;!$e(r,t)&&!this[T](i=this.colorModel.fromHsva(r))&&(this[B]=i,X(this,"color-changed",{value:i}))}[T](e){return this.color&&this.colorModel.equal(e,this.color)}[I](e){this[le]=e,this[ce].forEach(t=>t.update(e))}}}}),mt={};it(mt,{HexBase:()=>vt});var Le,vt,Ft=m({"node_modules/vanilla-colorful/lib/entrypoints/hex.js"(){qt(),re(),nt(),Le={defaultColor:"#000",toHsva:ot,fromHsva:({h:e,s:t,v:r})=>st({h:e,s:t,v:r,a:1}),equal:at,fromAttr:e=>e},vt=class extends gt{get colorModel(){return Le}}}}),je,G,Jt=m({"node_modules/vanilla-colorful/lib/utils/validate.js"(){je=/^#?([0-9A-F]{3,8})$/i,G=(e,t)=>{const r=je.exec(e),i=r?r[1].length:0;return i===3||i===6||!!t&&i===4||!!t&&i===8}}}),_t={};it(_t,{HexInputBase:()=>ft});var ze,pe,ue,M,ge,V,q,me,C,ft,Wt=m({"node_modules/vanilla-colorful/lib/entrypoints/hex-input.js"(){Jt(),Ae(),ze=ie(''),pe=(e,t)=>e.replace(/([^0-9A-F]+)/gi,"").substring(0,t?8:6),ue=Symbol("alpha"),M=Symbol("color"),ge=Symbol("saved"),V=Symbol("input"),q=Symbol("init"),me=Symbol("prefix"),C=Symbol("update"),ft=class extends HTMLElement{static get observedAttributes(){return["alpha","color","prefixed"]}get color(){return this[M]}set color(e){this[M]=e,this[C](e)}get alpha(){return this[ue]}set alpha(e){this[ue]=e,this.toggleAttribute("alpha",e);const t=this.color;t&&!G(t,e)&&(this.color=t.startsWith("#")?t.substring(0,t.length===5?4:7):t.substring(0,t.length===4?3:6))}get prefixed(){return this[me]}set prefixed(e){this[me]=e,this.toggleAttribute("prefixed",e),this[C](this.color)}constructor(){super();const e=this.attachShadow({mode:"open"});e.appendChild(ze.content.cloneNode(!0)),e.firstElementChild.addEventListener("slotchange",()=>this[q](e))}connectedCallback(){if(this[q](this.shadowRoot),this.hasOwnProperty("alpha")){const e=this.alpha;delete this.alpha,this.alpha=e}else this.alpha=this.hasAttribute("alpha");if(this.hasOwnProperty("prefixed")){const e=this.prefixed;delete this.prefixed,this.prefixed=e}else this.prefixed=this.hasAttribute("prefixed");if(this.hasOwnProperty("color")){const e=this.color;delete this.color,this.color=e}else this.color==null?this.color=this.getAttribute("color")||"":this[M]&&this[C](this[M])}handleEvent(e){const t=e.target,{value:r}=t;switch(e.type){case"input":const i=pe(r,this.alpha);this[ge]=this.color,(G(i,this.alpha)||r==="")&&(this.color=i,this.dispatchEvent(new CustomEvent("color-changed",{bubbles:!0,detail:{value:i?"#"+i:""}})));break;case"blur":r&&!G(r,this.alpha)&&(this.color=this[ge])}}attributeChangedCallback(e,t,r){e==="color"&&this.color!==r&&(this.color=r);const i=r!=null;e==="alpha"&&this.alpha!==i&&(this.alpha=i),e==="prefixed"&&this.prefixed!==i&&(this.prefixed=i)}[q](e){let t=this.querySelector("input");if(!t){let r;for(;r=this.firstChild;)r.remove();t=e.querySelector("input")}t.addEventListener("input",this),t.addEventListener("blur",this),this[V]=t,this[C](this.color)}[C](e){this[V]&&(this[V].value=e==null||e==""?"":(this.prefixed?"#":"")+pe(e,this.alpha))}}}}),K=globalThis,ke=K.ShadowRoot&&(K.ShadyCSS===void 0||K.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,bt=Symbol(),Be=new WeakMap,Gt=class{constructor(e,t,r){if(this._$cssResult$=!0,r!==bt)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=e,this.t=t}get styleSheet(){let e=this.o;const t=this.t;if(ke&&e===void 0){const r=t!==void 0&&t.length===1;r&&(e=Be.get(t)),e===void 0&&((this.o=e=new CSSStyleSheet).replaceSync(this.cssText),r&&Be.set(t,e))}return e}toString(){return this.cssText}},wt=e=>new Gt(typeof e=="string"?e:e+"",void 0,bt),Kt=(e,t)=>{if(ke)e.adoptedStyleSheets=t.map(r=>r instanceof CSSStyleSheet?r:r.styleSheet);else for(const r of t){const i=document.createElement("style"),o=K.litNonce;o!==void 0&&i.setAttribute("nonce",o),i.textContent=r.cssText,e.appendChild(i)}},Ie=ke?e=>e:e=>e instanceof CSSStyleSheet?(t=>{let r="";for(const i of t.cssRules)r+=i.cssText;return wt(r)})(e):e,{is:Yt,defineProperty:Zt,getOwnPropertyDescriptor:Xt,getOwnPropertyNames:Qt,getOwnPropertySymbols:er,getPrototypeOf:tr}=Object,oe=globalThis,Ve=oe.trustedTypes,rr=Ve?Ve.emptyScript:"",ir=oe.reactiveElementPolyfillSupport,H=(e,t)=>e,xe={toAttribute(e,t){switch(t){case Boolean:e=e?rr:null;break;case Object:case Array:e=e==null?e:JSON.stringify(e)}return e},fromAttribute(e,t){let r=e;switch(t){case Boolean:r=e!==null;break;case Number:r=e===null?null:Number(e);break;case Object:case Array:try{r=JSON.parse(e)}catch{r=null}}return r}},$t=(e,t)=>!Yt(e,t),qe={attribute:!0,type:String,converter:xe,reflect:!1,useDefault:!1,hasChanged:$t};Symbol.metadata??=Symbol("metadata"),oe.litPropertyMetadata??=new WeakMap;var A=class extends HTMLElement{static addInitializer(e){this._$Ei(),(this.l??=[]).push(e)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(e,t=qe){if(t.state&&(t.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(e)&&((t=Object.create(t)).wrapped=!0),this.elementProperties.set(e,t),!t.noAccessor){const r=Symbol(),i=this.getPropertyDescriptor(e,r,t);i!==void 0&&Zt(this.prototype,e,i)}}static getPropertyDescriptor(e,t,r){const{get:i,set:o}=Xt(this.prototype,e)??{get(){return this[t]},set(s){this[t]=s}};return{get:i,set(s){const a=i?.call(this);o?.call(this,s),this.requestUpdate(e,a,r)},configurable:!0,enumerable:!0}}static getPropertyOptions(e){return this.elementProperties.get(e)??qe}static _$Ei(){if(this.hasOwnProperty(H("elementProperties")))return;const e=tr(this);e.finalize(),e.l!==void 0&&(this.l=[...e.l]),this.elementProperties=new Map(e.elementProperties)}static finalize(){if(this.hasOwnProperty(H("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(H("properties"))){const t=this.properties,r=[...Qt(t),...er(t)];for(const i of r)this.createProperty(i,t[i])}const e=this[Symbol.metadata];if(e!==null){const t=litPropertyMetadata.get(e);if(t!==void 0)for(const[r,i]of t)this.elementProperties.set(r,i)}this._$Eh=new Map;for(const[t,r]of this.elementProperties){const i=this._$Eu(t,r);i!==void 0&&this._$Eh.set(i,t)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(e){const t=[];if(Array.isArray(e)){const r=new Set(e.flat(1/0).reverse());for(const i of r)t.unshift(Ie(i))}else e!==void 0&&t.push(Ie(e));return t}static _$Eu(e,t){const r=t.attribute;return r===!1?void 0:typeof r=="string"?r:typeof e=="string"?e.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(e=>this.enableUpdating=e),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(e=>e(this))}addController(e){(this._$EO??=new Set).add(e),this.renderRoot!==void 0&&this.isConnected&&e.hostConnected?.()}removeController(e){this._$EO?.delete(e)}_$E_(){const e=new Map,t=this.constructor.elementProperties;for(const r of t.keys())this.hasOwnProperty(r)&&(e.set(r,this[r]),delete this[r]);e.size>0&&(this._$Ep=e)}createRenderRoot(){const e=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return Kt(e,this.constructor.elementStyles),e}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(e=>e.hostConnected?.())}enableUpdating(e){}disconnectedCallback(){this._$EO?.forEach(e=>e.hostDisconnected?.())}attributeChangedCallback(e,t,r){this._$AK(e,r)}_$ET(e,t){const r=this.constructor.elementProperties.get(e),i=this.constructor._$Eu(e,r);if(i!==void 0&&r.reflect===!0){const o=(r.converter?.toAttribute!==void 0?r.converter:xe).toAttribute(t,r.type);this._$Em=e,o==null?this.removeAttribute(i):this.setAttribute(i,o),this._$Em=null}}_$AK(e,t){const r=this.constructor,i=r._$Eh.get(e);if(i!==void 0&&this._$Em!==i){const o=r.getPropertyOptions(i),s=typeof o.converter=="function"?{fromAttribute:o.converter}:o.converter?.fromAttribute!==void 0?o.converter:xe;this._$Em=i;const a=s.fromAttribute(t,o.type);this[i]=a??this._$Ej?.get(i)??a,this._$Em=null}}requestUpdate(e,t,r,i=!1,o){if(e!==void 0){const s=this.constructor;if(i===!1&&(o=this[e]),r??=s.getPropertyOptions(e),!((r.hasChanged??$t)(o,t)||r.useDefault&&r.reflect&&o===this._$Ej?.get(e)&&!this.hasAttribute(s._$Eu(e,r))))return;this.C(e,t,r)}this.isUpdatePending===!1&&(this._$ES=this._$EP())}C(e,t,{useDefault:r,reflect:i,wrapped:o},s){r&&!(this._$Ej??=new Map).has(e)&&(this._$Ej.set(e,s??t??this[e]),o!==!0||s!==void 0)||(this._$AL.has(e)||(this.hasUpdated||r||(t=void 0),this._$AL.set(e,t)),i===!0&&this._$Em!==e&&(this._$Eq??=new Set).add(e))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(t){Promise.reject(t)}const e=this.scheduleUpdate();return e!=null&&await e,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(const[i,o]of this._$Ep)this[i]=o;this._$Ep=void 0}const r=this.constructor.elementProperties;if(r.size>0)for(const[i,o]of r){const{wrapped:s}=o,a=this[i];s!==!0||this._$AL.has(i)||a===void 0||this.C(i,void 0,o,a)}}let e=!1;const t=this._$AL;try{e=this.shouldUpdate(t),e?(this.willUpdate(t),this._$EO?.forEach(r=>r.hostUpdate?.()),this.update(t)):this._$EM()}catch(r){throw e=!1,this._$EM(),r}e&&this._$AE(t)}willUpdate(e){}_$AE(e){this._$EO?.forEach(t=>t.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(e)),this.updated(e)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(e){return!0}update(e){this._$Eq&&=this._$Eq.forEach(t=>this._$ET(t,this[t])),this._$EM()}updated(e){}firstUpdated(e){}};A.elementStyles=[],A.shadowRootOptions={mode:"open"},A[H("elementProperties")]=new Map,A[H("finalized")]=new Map,ir?.({ReactiveElement:A}),(oe.reactiveElementVersions??=[]).push("2.1.2");var Se=globalThis,Fe=e=>e,Q=Se.trustedTypes,Je=Q?Q.createPolicy("lit-html",{createHTML:e=>e}):void 0,xt="$lit$",f=`lit$${Math.random().toFixed(9).slice(2)}$`,yt="?"+f,or=`<${yt}>`,y=document,N=()=>y.createComment(""),D=e=>e===null||typeof e!="object"&&typeof e!="function",Te=Array.isArray,sr=e=>Te(e)||typeof e?.[Symbol.iterator]=="function",ve=`[ +\f\r]`,O=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,We=/-->/g,Ge=/>/g,$=RegExp(`>|${ve}(?:([^\\s"'>=/]+)(${ve}*=${ve}*(?:[^ +\f\r"'\`<>=]|("|')|))|$)`,"g"),Ke=/'/g,Ye=/"/g,Ct=/^(?:script|style|textarea|title)$/i,ar=e=>(t,...r)=>({_$litType$:e,strings:t,values:r}),p=ar(1),b=Symbol.for("lit-noChange"),c=Symbol.for("lit-nothing"),Ze=new WeakMap,x=y.createTreeWalker(y,129);function At(e,t){if(!Te(e)||!e.hasOwnProperty("raw"))throw Error("invalid template strings array");return Je!==void 0?Je.createHTML(t):t}var nr=(e,t)=>{const r=e.length-1,i=[];let o,s=t===2?"":t===3?"":"",a=O;for(let n=0;n"?(a=o??O,h=-1):d[1]===void 0?h=-2:(h=a.lastIndex-d[2].length,g=d[1],a=d[3]===void 0?$:d[3]==='"'?Ye:Ke):a===Ye||a===Ke?a=$:a===We||a===Ge?a=O:(a=$,o=void 0);const _=a===$&&e[n+1].startsWith("/>")?" ":"";s+=a===O?l+or:h>=0?(i.push(g),l.slice(0,h)+xt+l.slice(h)+f+_):l+f+(h===-2?n:_)}return[At(e,s+(e[r]||"")+(t===2?"":t===3?"":"")),i]},ye=class Et{constructor({strings:t,_$litType$:r},i){let o;this.parts=[];let s=0,a=0;const n=t.length-1,l=this.parts,[g,d]=nr(t,r);if(this.el=Et.createElement(g,i),x.currentNode=this.el.content,r===2||r===3){const h=this.el.content.firstChild;h.replaceWith(...h.childNodes)}for(;(o=x.nextNode())!==null&&l.length0){o.textContent=Q?Q.emptyScript:"";for(let _=0;_2||r[0]!==""||r[1]!==""?(this._$AH=Array(r.length-1).fill(new String),this.strings=r):this._$AH=c}_$AI(e,t=this,r,i){const o=this.strings;let s=!1;if(o===void 0)e=k(this,e,t,0),s=!D(e)||e!==this._$AH&&e!==b,s&&(this._$AH=e);else{const a=e;let n,l;for(e=o[0],n=0;n{const i=r?.renderBefore??t;let o=i._$litPart$;if(o===void 0){const s=r?.renderBefore??null;i._$litPart$=o=new Me(t.insertBefore(N(),s),s,void 0,r??{})}return o._$AI(e),o},Oe=globalThis,U=class extends A{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){const e=super.createRenderRoot();return this.renderOptions.renderBefore??=e.firstChild,e}update(e){const t=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(e),this._$Do=gr(t,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return b}};U._$litElement$=!0,U.finalized=!0,Oe.litElementHydrateSupport?.({LitElement:U});var mr=Oe.litElementPolyfillSupport;mr?.({LitElement:U});(Oe.litElementVersions??=[]).push("4.2.2");var F=e=>e??c,St={ATTRIBUTE:1,CHILD:2},Tt=e=>(...t)=>({_$litDirective$:e,values:t}),Mt=class{constructor(e){}get _$AU(){return this._$AM._$AU}_$AT(e,t,r){this._$Ct=e,this._$AM=t,this._$Ci=r}_$AS(e,t){return this.update(e,t)}update(e,t){return this.render(...t)}},Ot="important",vr=" !"+Ot,_e=Tt(class extends Mt{constructor(e){if(super(e),e.type!==St.ATTRIBUTE||e.name!=="style"||e.strings?.length>2)throw Error("The `styleMap` directive must be used in the `style` attribute and must be the only part in the attribute.")}render(e){return Object.keys(e).reduce((t,r)=>{const i=e[r];return i==null?t:t+`${r=r.includes("-")?r:r.replace(/(?:^(webkit|moz|ms|o)|)(?=[A-Z])/g,"-$&").toLowerCase()}:${i};`},"")}update(e,[t]){const{style:r}=e.element;if(this.ft===void 0)return this.ft=new Set(Object.keys(t)),this.render(t);for(const i of this.ft)t[i]==null&&(this.ft.delete(i),i.includes("-")?r.removeProperty(i):r[i]=null);for(const i in t){const o=t[i];if(o!=null){this.ft.add(i);const s=typeof o=="string"&&o.endsWith(vr);i.includes("-")||s?r.setProperty(i,s?o.slice(0,-11):o,s?Ot:""):r[i]=o}}return b}}),Ce=class extends Mt{constructor(e){if(super(e),this.it=c,e.type!==St.CHILD)throw Error(this.constructor.directiveName+"() can only be used in child bindings")}render(e){if(e===c||e==null)return this._t=void 0,this.it=e;if(e===b)return e;if(typeof e!="string")throw Error(this.constructor.directiveName+"() called with a non-string value");if(e===this.it)return this._t;this.it=e;const t=[e];return t.raw=t,this._t={_$litType$:this.constructor.resultType,strings:t,values:[]}}};Ce.directiveName="unsafeHTML",Ce.resultType=1;var _r=Tt(Ce),fr=[{id:"svg",label:"SVG"},{id:"img",label:"IMG"},{id:"css",label:"CSS"}],br={react:"React",vue:"Vue",svelte:"Svelte",angular:"Angular",astro:"Astro",solid:"Solid","solid-start":"SolidStart",preact:"Preact",qwik:"Qwik",lit:"Lit",alpine:"Alpine"};function Pt(e){return e.usage?e.usage:e.componentName?{framework:"react",componentName:e.componentName}:null}function fe(e){const t=Pt(e),i=[...t?[{id:t.framework,label:br[t.framework]}]:[],...fr];return e.format==="stack"?i:i.filter(o=>o.id!=="img"&&o.id!=="css")}function wr(e){if(!e)return null;const t=e.trim().split(/[\s,]+/).map(Number);return t.length===4&&t.every(Number.isFinite)?`${t[2]} × ${t[3]}`:null}function R(e,t="#1a1a1a"){const r=e.trim().toLowerCase();if(r==="currentcolor")return R(t);const i=r.match(/^#([\da-f])([\da-f])([\da-f])(?:[\da-f])?$/i);if(i)return`#${i[1]}${i[1]}${i[2]}${i[2]}${i[3]}${i[3]}`;const o=r.match(/^#([\da-f]{6})(?:[\da-f]{2})?$/i);if(o)return`#${o[1]}`;const s=r.match(/^rgba?\(\s*(\d+)\s*[, ]\s*(\d+)\s*[, ]\s*(\d+)/);return s?`#${s.slice(1,4).map(n=>Math.min(255,Number(n)).toString(16).padStart(2,"0")).join("")}`:{black:"#000000",blue:"#0000ff",cyan:"#00ffff",gray:"#808080",green:"#008000",grey:"#808080",magenta:"#ff00ff",orange:"#ffa500",pink:"#ffc0cb",purple:"#800080",red:"#ff0000",white:"#ffffff",yellow:"#ffff00"}[r]??R(t===e?"#1a1a1a":t)}function $r(e){return Object.entries(e).map(([t,r])=>` ${JSON.stringify(t)}: ${JSON.stringify(r)},`)}function P(e){const t={"&":"&",'"':""","<":"<",">":">"};return e.replace(/[&"<>]/g,r=>t[r])}function xr(e){return e.replace(/\\/g,"\\\\").replace(/'/g,"\\'")}function yr(e){const{manifest:t,icon:r,tab:i,colorOverrides:o,cssColor:s}=e,a=`${t.spriteUrl}#${r.id}`,n=$r(o);if(!["svg","img","css"].includes(i)){const d=Pt(t);if(!d)throw new Error(`The ${i} code tab requires component metadata.`);if(d.framework==="alpine")return{code:``,language:"html"};if(d.framework==="lit")return{code:`<${d.tagName} icon=${JSON.stringify(r.name)}>`,language:"html"};if(d.framework==="angular")return{code:`<${d.selector} icon=${JSON.stringify(r.name)}>`,language:"html"};if(d.framework==="vue"){const h=Object.entries(o).map(([v,_])=>`${JSON.stringify(v)}: ${JSON.stringify(_)}`).join(", ");return{code:`<${d.componentName} icon=${JSON.stringify(r.name)}${h?` :style="{ ${P(h)} }"`:""} />`,language:"html"}}return n.length===0?{code:`<${d.componentName} icon=${JSON.stringify(r.name)} />`,language:"tsx"}:{code:[`<${d.componentName}`,` icon=${JSON.stringify(r.name)}`," style={{",...n," }}","/>"].join(` +`),language:"tsx"}}if(i==="svg"){const d=Object.entries(o).map(([h,v])=>`${h}: ${v}`).join("; ");return{code:` + +`,language:"html"}}if(i==="img")return{code:`${P(r.name)}`,language:"html"};const l=`icon-${r.name.replace(/[^a-zA-Z0-9_-]+/g,"-")||"sprite"}`,g=xr(a);return{code:[`.${l} {`," width: 24px;"," height: 24px;",` mask: url('${g}') no-repeat center / contain;`,` -webkit-mask: url('${g}') no-repeat center / contain;`,` background-color: ${s};`,"}"].join(` +`),language:"css"}}var Cr=/[&<>"]/g,Ar={"&":"&","<":"<",">":">",'"':"""},Er=[[//g,"comment"],[/<\/?[\w.-]+/g,"tag"],[/[\w-]+(?=\s*=)/g,"attr"],[/"[^"]*"|'[^']*'/g,"string"]],kr=[[/\/\*[\s\S]*?\*\//g,"comment"],[/\.[\w-]+/g,"selector"],[/[\w-]+(?=\s*:)/g,"property"],[/'[^']*'|"[^"]*"/g,"string"],[/#[\da-fA-F]{3,8}\b/g,"color"],[/\d+(?:\.\d+)?(?:px|em|rem|%)?/g,"number"]];function Sr(e,t){const r=t==="css"?kr:Er,i=[];let o=0;for(;o{const n=a.replace(Cr,l=>Ar[l]);return s?`${n}`:n}).join("")}var J=new WeakMap;function L(e){return e!==null&&typeof e=="object"}function Ht(e){return L(e)?typeof e.name=="string"&&typeof e.id=="string"&&(e.viewBox===null||typeof e.viewBox=="string")&&Array.isArray(e.colors):!1}function Ut(e){return L(e)?e.schemaVersion===1&&e.generator==="@gromlab/svg-sprites"&&typeof e.name=="string"&&typeof e.target=="string"&&(e.format==="stack"||e.format==="symbol")&&typeof e.iconCount=="number"&&typeof e.spriteUrl=="string"&&Array.isArray(e.icons)&&e.icons.every(Ht):!1}function Nt(e){return L(e)&&e.schemaVersion===1&&e.generator==="@gromlab/svg-sprites"&&typeof e.name=="string"&&typeof e.target=="string"&&(e.format==="stack"||e.format==="symbol")&&typeof e.iconCount=="number"&&Array.isArray(e.icons)&&e.icons.every(Ht)}function Tr(e){return!L(e)||Ut(e)||Nt(e)?e:e.default??e.spriteManifest??e}function ee(e,t){const r=Tr(e);if(Ut(r))return r;if(t&&Nt(r))return{...r,spriteUrl:t};throw new Error("The loaded source does not contain a valid SVG sprite manifest.")}function Xe(e){return Array.isArray(e)?e:Object.values(e)}function Dt(e){if(!L(e))return!1;const t=e;return typeof t.manifestUrl=="string"&&typeof t.spriteUrl=="string"}function Qe(e,t){return e.namet.name?1:0}async function Mr(e){const t=await fetch(e.manifestUrl);if(!t.ok)throw new Error(`Cannot load SVG sprite manifest: ${t.status} ${t.statusText}`);return ee(await t.json(),e.spriteUrl)}async function Or(e){if(Dt(e))return Mr(e);if(typeof e!="function")return ee(e);const t=J.get(e);if(t)return t;const r=Promise.resolve().then(e).then(i=>ee(i));return J.set(e,r),r.catch(()=>{J.get(e)===r&&J.delete(e)}),r}var Pr=` +:host { display: block; } +.gromlab-sprite-viewer { + --sv-bg: #f0f0f3; + --sv-text: #1a1a1a; + --sv-card: #ffffff; + --sv-card-hover: #eaeaed; + --sv-border: #d8d8d8; + --sv-accent: #3b82f6; + --sv-muted: #777777; + --sv-code: #f5f5f5; + --sv-checker-a: #e9e9e9; + --sv-checker-b: #ffffff; + --sv-danger: #b42332; + box-sizing: border-box; + min-height: 320px; + padding: 24px; + color: var(--sv-text); + color-scheme: light; + background: var(--sv-bg); + border-radius: 12px; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; +} +.gromlab-sprite-viewer[data-theme="dark"] { + --sv-bg: #1a1a1a; + --sv-text: #e5e5e5; + --sv-card: #2a2a2a; + --sv-card-hover: #333333; + --sv-border: #404040; + --sv-muted: #a3a3a3; + --sv-code: #242424; + --sv-checker-a: #333333; + --sv-checker-b: #2a2a2a; + --sv-danger: #ff9ba6; + color-scheme: dark; +} +@media (prefers-color-scheme: dark) { + .gromlab-sprite-viewer:not([data-theme]) { + --sv-bg: #1a1a1a; + --sv-text: #e5e5e5; + --sv-card: #2a2a2a; + --sv-card-hover: #333333; + --sv-border: #404040; + --sv-muted: #a3a3a3; + --sv-code: #242424; + --sv-checker-a: #333333; + --sv-checker-b: #2a2a2a; + --sv-danger: #ff9ba6; + color-scheme: dark; + } +} +.gromlab-sprite-viewer *, +.gromlab-sprite-viewer *::before, +.gromlab-sprite-viewer *::after { box-sizing: border-box; } +.gromlab-sprite-viewer button, +.gromlab-sprite-viewer input { font: inherit; } +.gromlab-sprite-viewer__header { + display: flex; + align-items: center; + gap: 16px; + flex-wrap: wrap; + margin-bottom: 24px; +} +.gromlab-sprite-viewer__title { margin: 0; font-size: 24px; line-height: 1.2; font-weight: 700; } +.gromlab-sprite-viewer__summary { color: var(--sv-muted); font-size: 13px; } +.gromlab-sprite-viewer__toolbar { display: flex; align-items: center; gap: 12px; margin-left: auto; } +.gromlab-sprite-viewer__search { + width: 220px; + height: 38px; + padding: 0 12px; + color: var(--sv-text); + background: var(--sv-card); + border: 1px solid var(--sv-border); + border-radius: 8px; + outline: none; + transition: border-color .15s, box-shadow .15s; +} +.gromlab-sprite-viewer__search:focus { border-color: var(--sv-accent); box-shadow: 0 0 0 3px rgba(59, 130, 246, .18); } +.gromlab-sprite-viewer__search::placeholder { color: var(--sv-muted); } +.gromlab-sprite-viewer__theme { + display: grid; + place-items: center; + width: 38px; + height: 38px; + padding: 0; + color: var(--sv-text); + background: var(--sv-card); + border: 1px solid var(--sv-border); + border-radius: 8px; + cursor: pointer; + transition: background .15s, border-color .15s; +} +.gromlab-sprite-viewer__theme:hover { background: var(--sv-card-hover); } +.gromlab-sprite-viewer__theme:focus-visible, +.gromlab-sprite-viewer__card:focus-visible, +.gromlab-sprite-viewer__close:focus-visible, +.gromlab-sprite-viewer__tab:focus-visible, +.gromlab-sprite-viewer__copy:focus-visible, +.gromlab-sprite-viewer__swatch:focus-visible { + outline: 2px solid var(--sv-accent); + outline-offset: 2px; +} +.gromlab-sprite-viewer__errors { + margin: 0 0 24px; + padding: 12px 14px; + color: var(--sv-danger); + background: rgba(180, 35, 50, .08); + border: 1px solid rgba(180, 35, 50, .28); + border-radius: 8px; + font-size: 12px; +} +.gromlab-sprite-viewer__group { margin-bottom: 40px; } +.gromlab-sprite-viewer__group-header { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 16px; } +.gromlab-sprite-viewer__group-title { margin: 0; font-size: 18px; line-height: 1.3; font-weight: 600; } +.gromlab-sprite-viewer__badge { + padding: 2px 8px; + color: #ffffff; + background: var(--sv-accent); + border-radius: 999px; + font-size: 11px; + line-height: 1.5; + font-weight: 600; + letter-spacing: .04em; + text-transform: uppercase; +} +.gromlab-sprite-viewer__group-count, +.gromlab-sprite-viewer__description { color: var(--sv-muted); font-size: 13px; font-weight: 400; } +.gromlab-sprite-viewer__description { flex-basis: 100%; margin: -2px 0 0; } +.gromlab-sprite-viewer__grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 12px; } +.gromlab-sprite-viewer__card { + display: flex; + min-width: 0; + padding: 16px 8px; + color: inherit; + text-align: center; + background: var(--sv-card); + border: 0; + border-radius: 8px; + cursor: pointer; + flex-direction: column; + align-items: center; + gap: 8px; + transition: background .15s, transform .15s; +} +.gromlab-sprite-viewer__card:hover { background: var(--sv-card-hover); transform: translateY(-1px); } +.gromlab-sprite-viewer__icon-wrap, +.gromlab-sprite-viewer__dialog-preview-canvas { + display: grid; + place-items: center; + background: conic-gradient( + var(--sv-checker-a) 25%, var(--sv-checker-b) 0 50%, + var(--sv-checker-a) 0 75%, var(--sv-checker-b) 0 + ); + background-size: 8px 8px; + border-radius: 4px; +} +.gromlab-sprite-viewer__icon-wrap { width: 128px; height: 128px; } +.gromlab-sprite-viewer__icon { display: block; width: 128px; height: 128px; color: var(--sv-text); overflow: visible; } +.gromlab-sprite-viewer__icon-name { + max-width: 100%; + overflow: hidden; + color: var(--sv-muted); + font-size: 13px; + line-height: 1.35; + text-overflow: ellipsis; + white-space: nowrap; +} +.gromlab-sprite-viewer__status { + padding: 40px 20px; + color: var(--sv-muted); + text-align: center; + background: var(--sv-card); + border: 1px dashed var(--sv-border); + border-radius: 8px; +} +.gromlab-sprite-viewer__footer { + display: flex; + align-items: center; + justify-content: center; + gap: 12px; + margin-top: 40px; + padding-top: 16px; + color: var(--sv-muted); + border-top: 1px solid var(--sv-border); + font-size: 13px; +} +.gromlab-sprite-viewer__footer a { color: var(--sv-accent); text-decoration: none; } +.gromlab-sprite-viewer__footer a:hover { text-decoration: underline; } +.gromlab-sprite-viewer__dialog { + width: min(560px, calc(100vw - 32px)); + max-width: none; + max-height: calc(100dvh - 48px); + padding: 0; + color: var(--sv-text); + background: transparent; + border: 0; + overflow: visible; +} +.gromlab-sprite-viewer__dialog::backdrop { background: rgba(0, 0, 0, .55); backdrop-filter: blur(2px); } +.gromlab-sprite-viewer__dialog-shell { + position: relative; + max-height: calc(100dvh - 48px); + padding: 24px; + overflow-y: auto; + color: var(--sv-text); + background: var(--sv-bg); + border: 1px solid var(--sv-border); + border-radius: 12px; + box-shadow: 0 24px 72px rgba(0, 0, 0, .28); +} +.gromlab-sprite-viewer__close { + position: absolute; + z-index: 2; + top: 12px; + right: 12px; + display: grid; + place-items: center; + width: 34px; + height: 34px; + padding: 0; + color: var(--sv-text); + background: var(--sv-bg); + border: 1px solid var(--sv-border); + border-radius: 8px; + cursor: pointer; +} +.gromlab-sprite-viewer__close:hover { background: var(--sv-card-hover); } +.gromlab-sprite-viewer__dialog-preview { display: flex; align-items: center; justify-content: center; padding: 24px; margin-bottom: 16px; background: var(--sv-card); border-radius: 8px; } +.gromlab-sprite-viewer__dialog-preview-canvas { width: 256px; height: 256px; } +.gromlab-sprite-viewer__dialog-icon, +.gromlab-sprite-viewer__dialog-img, +.gromlab-sprite-viewer__dialog-mask { display: block; width: 256px; height: 256px; } +.gromlab-sprite-viewer__dialog-icon { color: var(--sv-text); overflow: visible; } +.gromlab-sprite-viewer__dialog-img { object-fit: contain; } +.gromlab-sprite-viewer__dialog-heading { display: flex; justify-content: center; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 6px; } +.gromlab-sprite-viewer__dialog-title { margin: 0; font: 600 16px/1.3 ui-monospace, "SFMono-Regular", Consolas, monospace; } +.gromlab-sprite-viewer__viewbox { + padding: 2px 8px; + color: var(--sv-muted); + background: var(--sv-card); + border: 1px solid var(--sv-border); + border-radius: 999px; + font-size: 11px; + white-space: nowrap; +} +.gromlab-sprite-viewer__dialog-meta { margin: 0 0 20px; color: var(--sv-muted); text-align: center; font-size: 12px; } +.gromlab-sprite-viewer__colors { margin-bottom: 20px; } +.gromlab-sprite-viewer__hint { + margin: 0 0 12px; + padding: 8px 12px; + color: var(--sv-muted); + background: var(--sv-card); + border-left: 3px solid var(--sv-accent); + border-radius: 4px; + font-size: 12px; + line-height: 1.5; +} +.gromlab-sprite-viewer__colors-title { margin: 0 0 8px; color: var(--sv-muted); font-size: 12px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; } +.gromlab-sprite-viewer__color-row { position: relative; display: flex; align-items: center; gap: 8px; min-height: 32px; margin-bottom: 6px; } +.gromlab-sprite-viewer__swatch { width: 26px; height: 26px; flex: 0 0 auto; padding: 0; border: 1px solid var(--sv-border); border-radius: 4px; cursor: pointer; } +.gromlab-sprite-viewer__color-label { min-width: 0; overflow-wrap: anywhere; color: var(--sv-muted); font: 12px/1.4 ui-monospace, "SFMono-Regular", Consolas, monospace; } +.gromlab-sprite-viewer__color-popover { + position: absolute; + z-index: 5; + bottom: calc(100% + 6px); + left: 0; + width: 224px; + max-width: calc(100vw - 64px); + padding: 12px; + background: var(--sv-bg); + border: 1px solid var(--sv-border); + border-radius: 8px; + box-shadow: 0 8px 28px rgba(0, 0, 0, .22); +} +.gromlab-sprite-viewer__color-picker { display: flex; flex-direction: column; width: 198px; max-width: 100%; height: 160px; } +.gromlab-sprite-viewer__hex-input { display: block; width: 100%; margin-top: 8px; } +.gromlab-sprite-viewer__hex-input::part(input) { box-sizing: border-box; display: block; width: 100%; height: 32px; padding: 0 8px; color: var(--sv-text); text-align: center; background: var(--sv-card); border: 1px solid var(--sv-border); border-radius: 4px; outline: none; font: 12px/1 ui-monospace, "SFMono-Regular", Consolas, monospace; } +.gromlab-sprite-viewer__hex-input::part(input):focus { border-color: var(--sv-accent); } +.gromlab-sprite-viewer__tabs { display: flex; overflow-x: auto; margin-bottom: 12px; border-bottom: 1px solid var(--sv-border); } +.gromlab-sprite-viewer__tab { flex: 0 0 auto; padding: 8px 16px; color: var(--sv-muted); background: none; border: 0; border-bottom: 2px solid transparent; cursor: pointer; font-size: 12px; font-weight: 600; } +.gromlab-sprite-viewer__tab[aria-selected="true"] { color: var(--sv-accent); border-bottom-color: var(--sv-accent); } +.gromlab-sprite-viewer__code { position: relative; overflow: hidden; background: var(--sv-code); border-radius: 8px; } +.gromlab-sprite-viewer__code pre { min-height: 72px; margin: 0; padding: 16px 70px 16px 16px; overflow-x: auto; font: 12px/1.6 ui-monospace, "SFMono-Regular", Consolas, monospace; } +.gromlab-sprite-viewer__copy { position: absolute; top: 8px; right: 8px; padding: 4px 8px; color: var(--sv-muted); background: var(--sv-bg); border: 1px solid var(--sv-border); border-radius: 4px; cursor: pointer; font-size: 11px; } +.gromlab-sprite-viewer__copy:hover { color: var(--sv-text); } +.gromlab-sprite-viewer__code .hl-tag { color: #116329; } +.gromlab-sprite-viewer__code .hl-attr, +.gromlab-sprite-viewer__code .hl-number, +.gromlab-sprite-viewer__code .hl-property { color: #0550ae; } +.gromlab-sprite-viewer__code .hl-string, +.gromlab-sprite-viewer__code .hl-color { color: #0a3069; } +.gromlab-sprite-viewer__code .hl-comment { color: #8b949e; } +.gromlab-sprite-viewer__code .hl-punctuation, +.gromlab-sprite-viewer__code .hl-selector { color: #6639ba; } +.gromlab-sprite-viewer[data-theme="dark"] .gromlab-sprite-viewer__code .hl-tag { color: #7ee787; } +.gromlab-sprite-viewer[data-theme="dark"] .gromlab-sprite-viewer__code .hl-attr, +.gromlab-sprite-viewer[data-theme="dark"] .gromlab-sprite-viewer__code .hl-number, +.gromlab-sprite-viewer[data-theme="dark"] .gromlab-sprite-viewer__code .hl-property { color: #79c0ff; } +.gromlab-sprite-viewer[data-theme="dark"] .gromlab-sprite-viewer__code .hl-string, +.gromlab-sprite-viewer[data-theme="dark"] .gromlab-sprite-viewer__code .hl-color { color: #a5d6ff; } +.gromlab-sprite-viewer[data-theme="dark"] .gromlab-sprite-viewer__code .hl-punctuation, +.gromlab-sprite-viewer[data-theme="dark"] .gromlab-sprite-viewer__code .hl-selector { color: #d2a8ff; } +@media (prefers-color-scheme: dark) { + .gromlab-sprite-viewer:not([data-theme]) .gromlab-sprite-viewer__code .hl-tag { color: #7ee787; } + .gromlab-sprite-viewer:not([data-theme]) .gromlab-sprite-viewer__code .hl-attr, + .gromlab-sprite-viewer:not([data-theme]) .gromlab-sprite-viewer__code .hl-number, + .gromlab-sprite-viewer:not([data-theme]) .gromlab-sprite-viewer__code .hl-property { color: #79c0ff; } + .gromlab-sprite-viewer:not([data-theme]) .gromlab-sprite-viewer__code .hl-string, + .gromlab-sprite-viewer:not([data-theme]) .gromlab-sprite-viewer__code .hl-color { color: #a5d6ff; } + .gromlab-sprite-viewer:not([data-theme]) .gromlab-sprite-viewer__code .hl-punctuation, + .gromlab-sprite-viewer:not([data-theme]) .gromlab-sprite-viewer__code .hl-selector { color: #d2a8ff; } +} +@media (max-width: 640px) { + .gromlab-sprite-viewer { padding: 16px; border-radius: 8px; } + .gromlab-sprite-viewer__header { align-items: stretch; } + .gromlab-sprite-viewer__toolbar { width: 100%; margin-left: 0; } + .gromlab-sprite-viewer__search { width: auto; flex: 1; min-width: 0; } + .gromlab-sprite-viewer__grid { grid-template-columns: repeat(auto-fill, minmax(132px, 1fr)); gap: 8px; } + .gromlab-sprite-viewer__icon-wrap, + .gromlab-sprite-viewer__icon { width: 104px; height: 104px; } + .gromlab-sprite-viewer__dialog { width: calc(100vw - 20px); max-height: calc(100dvh - 20px); } + .gromlab-sprite-viewer__dialog-shell { max-height: calc(100dvh - 20px); padding: 16px; } + .gromlab-sprite-viewer__dialog-preview { padding: 16px; } + .gromlab-sprite-viewer__dialog-preview-canvas, + .gromlab-sprite-viewer__dialog-icon, + .gromlab-sprite-viewer__dialog-img, + .gromlab-sprite-viewer__dialog-mask { width: min(256px, calc(100vw - 86px)); height: min(256px, calc(100vw - 86px)); } +} +@media (prefers-reduced-motion: reduce) { + .gromlab-sprite-viewer *, + .gromlab-sprite-viewer *::before, + .gromlab-sprite-viewer *::after { scroll-behavior: auto !important; transition-duration: .01ms !important; } +} +`,et="gromlab-sprite-viewer",be="gromlab-hex-color-picker",we="gromlab-hex-input",W;function Hr(){return customElements.get(be)&&customElements.get(we)?Promise.resolve():W||(W=Promise.all([Promise.resolve().then(()=>(Ft(),mt)),Promise.resolve().then(()=>(Wt(),_t))]).then(([{HexBase:e},{HexInputBase:t}])=>{customElements.get(be)||customElements.define(be,class extends e{}),customElements.get(we)||customElements.define(we,class extends t{})}),W)}function tt(e,t){const r=e%100,i=e%10,o=r>=11&&r<=19?t[2]:i===1?t[0]:i>=2&&i<=4?t[1]:t[2];return`${e} ${o}`}function Ur(){return globalThis.matchMedia?.("(prefers-color-scheme: dark)").matches?"dark":"light"}function rt(e,t){return Object.fromEntries(e.colors.map(({variable:r,fallback:i})=>[r,R(i,t)]))}var Nr=class extends U{static properties={sources:{attribute:!1},viewerTitle:{attribute:"viewer-title"},colorTheme:{attribute:"color-theme"},themeControlled:{attribute:!1},showThemeToggle:{attribute:!1},manifestUrl:{attribute:"manifest-url"},spriteUrl:{attribute:"sprite-url"},_manifests:{state:!0},_errors:{state:!0},_loading:{state:!0},_query:{state:!0},_systemTheme:{state:!0},_selected:{state:!0},_activeTab:{state:!0},_colors:{state:!0},_colorOverrides:{state:!0},_cssColor:{state:!0},_cssColorOverridden:{state:!0},_openColorControl:{state:!0},_copied:{state:!0}};static styles=wt(Pr);_loadVersion=0;_copyTimeout;_media;constructor(){super(),this.sources=[],this.viewerTitle="SVG Sprites",this.colorTheme="auto",this.themeControlled=!1,this.showThemeToggle=!0,this._manifests=[],this._errors=[],this._loading=!1,this._query="",this._systemTheme=Ur(),this._selected=null,this._activeTab="svg",this._colors={},this._colorOverrides={},this._cssColor="#1a1a1a",this._cssColorOverridden=!1,this._openColorControl=null,this._copied=!1}_handleMediaChange=()=>{this._systemTheme=this._media?.matches?"dark":"light",this._syncThemeColors()};_handleDocumentPointerDown=e=>{if(!this._openColorControl)return;e.composedPath().some(r=>r instanceof HTMLElement&&(r.dataset.colorTrigger===this._openColorControl||r.dataset.colorPopover===this._openColorControl))||(this._openColorControl=null)};connectedCallback(){super.connectedCallback(),Hr(),this._media=globalThis.matchMedia?.("(prefers-color-scheme: dark)"),this._systemTheme=this._media?.matches?"dark":"light",this._media?.addEventListener("change",this._handleMediaChange),globalThis.document?.addEventListener("pointerdown",this._handleDocumentPointerDown,!0)}disconnectedCallback(){this._media?.removeEventListener("change",this._handleMediaChange),globalThis.document?.removeEventListener("pointerdown",this._handleDocumentPointerDown,!0),this._copyTimeout&&clearTimeout(this._copyTimeout),super.disconnectedCallback()}updated(e){(e.has("sources")||e.has("manifestUrl")||e.has("spriteUrl"))&&this._loadSources(),e.has("colorTheme")&&this._syncThemeColors()}get _resolvedColorTheme(){return this.colorTheme==="auto"?this._systemTheme:this.colorTheme}get _themeColor(){return this._resolvedColorTheme==="dark"?"#e5e5e5":"#1a1a1a"}_effectiveSources(){const e=Xe(this.sources);return e.length>0?e:this.manifestUrl&&this.spriteUrl?[{manifestUrl:this.manifestUrl,spriteUrl:this.spriteUrl}]:[]}async _loadSources(){const e=++this._loadVersion,t=this._effectiveSources(),r=[],i=[],o=[];this.manifestUrl&&!this.spriteUrl&&Xe(this.sources).length===0&&o.push("The sprite-url attribute is required with manifest-url.");for(const n of t){if(typeof n=="function"||Dt(n)){i.push(n);continue}try{r.push(ee(n))}catch(l){o.push(l instanceof Error?l.message:String(l))}}if(this._manifests=r.sort(Qe),this._errors=o,this._loading=i.length>0,this._selected=null,i.length===0)return;const s=await Promise.allSettled(i.map(Or));if(e!==this._loadVersion)return;const a=[];for(const n of s)n.status==="fulfilled"?a.push(n.value):o.push(n.reason instanceof Error?n.reason.message:String(n.reason));this._manifests=[...r,...a].sort(Qe),this._errors=[...o],this._loading=!1}_syncThemeColors(){this._selected&&(this._colors={...rt(this._selected.icon,this._themeColor),...this._colorOverrides},this._cssColorOverridden||(this._cssColor=this._themeColor))}_toggleTheme(){const e=this._resolvedColorTheme==="dark"?"light":"dark";this.themeControlled||(this.colorTheme=e),this.dispatchEvent(new CustomEvent("color-theme-change",{bubbles:!0,composed:!0,detail:{theme:e}}))}async _selectIcon(e,t){const r=`${e.name}:${e.spriteUrl}:${t.id}`;this._selected={key:r,manifest:e,icon:t},this._activeTab=fe(e)[0]?.id??"svg",this._colorOverrides={},this._colors=rt(t,this._themeColor),this._cssColor=this._themeColor,this._cssColorOverridden=!1,this._openColorControl=null,this._copied=!1,await this.updateComplete;const i=this.renderRoot.querySelector("dialog");i&&!i.open&&i.showModal()}_closeDialog(){const e=this.renderRoot.querySelector("dialog");e?.open&&e.close(),this._selected=null,this._openColorControl=null}_handleBackdropClick(e){if(!(e.currentTarget instanceof HTMLDialogElement)||e.target!==e.currentTarget)return;const t=e.currentTarget.getBoundingClientRect();(e.clientXt.right||e.clientYt.bottom)&&this._closeDialog()}_handleTabKeyDown(e,t){if(!this._selected)return;const r=fe(this._selected.manifest);let i=null;if(e.key==="ArrowRight"&&(i=(t+1)%r.length),e.key==="ArrowLeft"&&(i=(t-1+r.length)%r.length),e.key==="Home"&&(i=0),e.key==="End"&&(i=r.length-1),i===null)return;e.preventDefault(),this._activeTab=r[i].id;const o=this.renderRoot.querySelectorAll('[role="tab"]');this.updateComplete.then(()=>o[i]?.focus())}_handleColorChange(e,t){const r=R(t);this._colors={...this._colors,[e]:r},this._colorOverrides={...this._colorOverrides,[e]:r}}_handleCssColorChange(e){this._cssColor=R(e),this._cssColorOverridden=!0}async _copyCode(e){if(globalThis.navigator?.clipboard)try{await globalThis.navigator.clipboard.writeText(e),this._copied=!0,this._copyTimeout&&clearTimeout(this._copyTimeout),this._copyTimeout=setTimeout(()=>{this._copied=!1},1500)}catch{this._copied=!1}}_renderColorControl(e,t,r,i){const o=this._openColorControl===r,s=`color-${r.replace(/[^a-zA-Z0-9_-]/g,"-")}`;return p` +
+ + ${e} + ${o?p` + + `:c} +
+ `}_renderDialogPreview(e,t){const r=`${e.spriteUrl}#${t.id}`;return this._activeTab==="img"?p`${t.name}`:this._activeTab==="css"?p` + + `:p` + + + + `}_renderDialog(){if(!this._selected)return c;const{manifest:e,icon:t}=this._selected,r=fe(e),i=r.some(n=>n.id===this._activeTab)?this._activeTab:r[0].id,o=yr({manifest:e,icon:t,tab:i,colorOverrides:this._colorOverrides,cssColor:this._cssColor}),s=wr(t.viewBox),a=i!=="img"&&i!=="css";return p` + {n.preventDefault(),this._closeDialog()}} + @click=${this._handleBackdropClick} + > +
+ + +
+
+ ${this._renderDialogPreview(e,t)} +
+
+ +
+

${t.name}

+ ${s?p`${s}`:c} +
+

${e.name} · ${e.format} · ${e.target}

+ +
+ ${a&&t.colors.length>0?p` +

+ Цвета применяются к превью через CSS-переменные и попадут в пример кода. +

+

CSS Variables

+ ${t.colors.map(({variable:n,fallback:l})=>this._renderColorControl(`${n}: ${l}`,this._colors[n],n,g=>this._handleColorChange(n,g)))} + `:c} + ${a&&t.colors.length===0?p` +

У иконки нет настраиваемых цветовых переменных.

+ `:c} + ${i==="img"?p` +

+ IMG изолирует SVG: CSS-переменные и currentColor внутрь изображения не передаются. +

+ `:c} + ${i==="css"?p` +

+ CSS mask отображает иконку одним цветом через background-color. +

+ ${this._renderColorControl("background-color",this._cssColor,"background-color",n=>this._handleCssColorChange(n))} + `:c} +
+ +
+ ${r.map((n,l)=>p` + + `)} +
+ +
+
${_r(Sr(o.code,o.language))}
+ +
+
+
+ `}render(){const e=this._query.trim().toLowerCase(),t=this._manifests.map(o=>({manifest:o,icons:o.icons.filter(s=>e===""||s.name.toLowerCase().includes(e)||o.name.toLowerCase().includes(e))})).filter(o=>o.icons.length>0),r=this._manifests.reduce((o,s)=>o+s.iconCount,0),i=t.reduce((o,s)=>o+s.icons.length,0);return p` +
+
+

${this.viewerTitle}

+ + ${tt(this._manifests.length,["спрайт","спрайта","спрайтов"])} + · ${tt(r,["иконка","иконки","иконок"])} + ${e?` · найдено ${i}`:c} + +
+ {this._query=o.currentTarget.value}} + placeholder="Найти иконку" + aria-label="Поиск иконок" + > + ${this.showThemeToggle?p` + + `:c} +
+
+ + ${this._errors.length>0?p` + + `:c} + + ${t.map(({manifest:o,icons:s})=>p` +
+
+

${o.name}

+ ${o.format} + ${s.length} + ${o.description?p` +

${o.description}

+ `:c} +
+
+ ${s.map(a=>p` + + `)} +
+
+ `)} + + ${t.length===0&&(!this._loading||this._manifests.length>0)?p` +
+ ${this._manifests.length===0?"Спрайты не подключены":"Иконки не найдены"} +
+ `:c} + ${this._loading&&this._manifests.length===0?p` +
Загрузка спрайтов...
+ `:c} + + + + ${this._renderDialog()} +
+ `}};function Dr(){customElements.get(et)||customElements.define(et,Nr)}Dr();/*! Bundled license information: + +@lit/reactive-element/css-tag.js: + (** + * @license + * Copyright 2019 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + *) + +@lit/reactive-element/reactive-element.js: +lit-html/lit-html.js: +lit-element/lit-element.js: +lit-html/directive.js: +lit-html/directives/unsafe-html.js: + (** + * @license + * Copyright 2017 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + *) + +lit-html/is-server.js: + (** + * @license + * Copyright 2022 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + *) + +lit-html/directives/if-defined.js: +lit-html/directives/style-map.js: + (** + * @license + * Copyright 2018 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + *) +*/export{Dr as defineSpriteViewerElement}; diff --git a/integration/apps/solid-start-vite/.vinxi/build/server-fns/_server/.vite/manifest.json b/integration/apps/solid-start-vite/.vinxi/build/server-fns/_server/.vite/manifest.json new file mode 100644 index 0000000..df67611 --- /dev/null +++ b/integration/apps/solid-start-vite/.vinxi/build/server-fns/_server/.vite/manifest.json @@ -0,0 +1,79 @@ +{ + "../../../dist/viewer-element.js": { + "file": "assets/viewer-element-BO6fulPI.js", + "name": "viewer-element", + "src": "../../../dist/viewer-element.js", + "isDynamicEntry": true + }, + "_index-AIk5O37p.js": { + "file": "assets/index-AIk5O37p.js", + "name": "index", + "isDynamicEntry": true, + "dynamicImports": [ + "../../../dist/viewer-element.js", + "src/sprite/.svg-sprite/svg-sprite.manifest.js" + ], + "css": [ + "assets/index-BWUdTtCi.css" + ], + "assets": [ + "assets/sprite-DvsqBNon.svg" + ] + }, + "_index-BWUdTtCi.css": { + "file": "assets/index-BWUdTtCi.css", + "src": "_index-BWUdTtCi.css" + }, + "_server-fns-XxvbTHGR.js": { + "file": "assets/server-fns-XxvbTHGR.js", + "name": "server-fns", + "dynamicImports": [ + "src/routes/index.jsx?pick=default&pick=$css", + "src/routes/index.jsx?pick=default&pick=$css", + "src/app.jsx" + ] + }, + "src/app.jsx": { + "file": "assets/app-Bgr52ZUm.js", + "name": "app", + "src": "src/app.jsx", + "isDynamicEntry": true, + "imports": [ + "_server-fns-XxvbTHGR.js" + ], + "css": [ + "assets/app-BVONibDl.css" + ] + }, + "src/routes/index.jsx?pick=default&pick=$css": { + "file": "index.js", + "name": "index", + "src": "src/routes/index.jsx?pick=default&pick=$css", + "isEntry": true, + "imports": [ + "_index-AIk5O37p.js" + ] + }, + "src/sprite/.svg-sprite/sprite.svg": { + "file": "assets/sprite-DvsqBNon.svg", + "src": "src/sprite/.svg-sprite/sprite.svg" + }, + "src/sprite/.svg-sprite/svg-sprite.manifest.js": { + "file": "assets/svg-sprite.manifest-D0DHYqq2.js", + "name": "svg-sprite.manifest", + "src": "src/sprite/.svg-sprite/svg-sprite.manifest.js", + "isDynamicEntry": true, + "imports": [ + "_index-AIk5O37p.js" + ] + }, + "virtual:$vinxi/handler/server-fns": { + "file": "server-fns.js", + "name": "server-fns", + "src": "virtual:$vinxi/handler/server-fns", + "isEntry": true, + "imports": [ + "_server-fns-XxvbTHGR.js" + ] + } +} \ No newline at end of file diff --git a/integration/apps/solid-start-vite/.vinxi/build/server-fns/_server/assets/app-BVONibDl.css b/integration/apps/solid-start-vite/.vinxi/build/server-fns/_server/assets/app-BVONibDl.css new file mode 100644 index 0000000..b9cce31 --- /dev/null +++ b/integration/apps/solid-start-vite/.vinxi/build/server-fns/_server/assets/app-BVONibDl.css @@ -0,0 +1 @@ +:root{font-family:system-ui,sans-serif;color:#172033;background:#fff}body{margin:0;padding:40px}.viewer{margin-top:32px} diff --git a/integration/apps/solid-start-vite/.vinxi/build/server-fns/_server/assets/app-Bgr52ZUm.js b/integration/apps/solid-start-vite/.vinxi/build/server-fns/_server/assets/app-Bgr52ZUm.js new file mode 100644 index 0000000..387cccc --- /dev/null +++ b/integration/apps/solid-start-vite/.vinxi/build/server-fns/_server/assets/app-Bgr52ZUm.js @@ -0,0 +1 @@ +import{isServer as k,getRequestEvent as T,createComponent as S,delegateEvents as ye}from"solid-js/web";import{F as ve}from"./server-fns-XxvbTHGR.js";import{getOwner as Q,runWithOwner as Y,createMemo as L,createSignal as j,createRenderEffect as be,on as N,useContext as Re,createContext as Z,startTransition as Pe,resetErrorBoundaries as Le,batch as Se,untrack as ee,createComponent as Ae,children as Ee,Show as te,createRoot as Ce,onCleanup as ne,sharedConfig as $,Suspense as xe}from"solid-js";import"cookie-es";import"solid-js/web/storage";import"h3";import"unctx";import"node:async_hooks";import"radix3";import"seroval";import"seroval-plugins/web";function re(){let e=new Set;function t(n){return e.add(n),()=>e.delete(n)}let r=!1;function o(n,a){if(r)return!(r=!1);const s={to:n,options:a,defaultPrevented:!1,preventDefault:()=>s.defaultPrevented=!0};for(const i of e)i.listener({...s,from:i.location,retry:h=>{h&&(r=!0),i.navigate(n,{...a,resolve:!1})}});return!s.defaultPrevented}return{subscribe:t,confirm:o}}let M;function z(){(!window.history.state||window.history.state._depth==null)&&window.history.replaceState({...window.history.state,_depth:window.history.length-1},""),M=window.history.state._depth}k||z();function Oe(e){return{...e,_depth:window.history.state&&window.history.state._depth}}function Ue(e,t){let r=!1;return()=>{const o=M;z();const n=o==null?null:M-o;if(r){r=!1;return}n&&t(n)?(r=!0,window.history.go(-n)):e()}}const Fe=/^(?:[a-z0-9]+:)?\/\//i,ke=/^\/+|(\/)\/+$/g,oe="http://sr";function B(e,t=!1){const r=e.replace(ke,"$1");return r?t||/^[?#]/.test(r)?r:"/"+r:""}function D(e,t,r){if(Fe.test(t))return;const o=B(e),n=r&&B(r);let a="";return!n||t.startsWith("/")?a=o:n.toLowerCase().indexOf(o.toLowerCase())!==0?a=o+n:a=n,(a||"/")+B(t,!a)}function qe(e,t){return B(e).replace(/\/*(\*.*)?$/g,"")+B(t)}function ae(e){const t={};return e.searchParams.forEach((r,o)=>{o in t?Array.isArray(t[o])?t[o].push(r):t[o]=[t[o],r]:t[o]=r}),t}function je(e,t,r){const[o,n]=e.split("/*",2),a=o.split("/").filter(Boolean),s=a.length;return i=>{const h=i.split("/").filter(Boolean),c=h.length-s;if(c<0||c>0&&n===void 0&&!t)return null;const d={path:s?"":"/",params:{}},y=m=>r===void 0?void 0:r[m];for(let m=0;mo===e;return t===void 0?!0:typeof t=="string"?r(t):typeof t=="function"?t(e):Array.isArray(t)?t.some(r):t instanceof RegExp?t.test(e):!1}function Be(e){const[t,r]=e.pattern.split("/*",2),o=t.split("/").filter(Boolean);return o.reduce((n,a)=>n+(a.startsWith(":")?2:3),o.length-(r===void 0?0:1))}function se(e){const t=new Map,r=Q();return new Proxy({},{get(o,n){return t.has(n)||Y(r,()=>t.set(n,L(()=>e()[n]))),t.get(n)()},getOwnPropertyDescriptor(){return{enumerable:!0,configurable:!0}},ownKeys(){return Reflect.ownKeys(e())},has(o,n){return n in e()}})}function ie(e){let t=/(\/?\:[^\/]+)\?/.exec(e);if(!t)return[e];let r=e.slice(0,t.index),o=e.slice(t.index+t[0].length);const n=[r,r+=t[1]];for(;t=/^(\/\:[^\/]+)\?/.exec(o);)n.push(r+=t[1]),o=o.slice(t[0].length);return ie(o).reduce((a,s)=>[...a,...n.map(i=>i+s)],[])}const Ie=100,_e=Z(),ce=Z();function We(e,t=""){const{component:r,preload:o,load:n,children:a,info:s}=e,i=!a||Array.isArray(a)&&!a.length,h={key:e,component:r,preload:o||n,info:s};return ue(e.path).reduce((c,d)=>{for(const y of ie(d)){const m=qe(t,y);let g=i?m:m.split("/*",1)[0];g=g.split("/").map(b=>b.startsWith(":")||b.startsWith("*")?b:encodeURIComponent(b)).join("/"),c.push({...h,originalPath:d,pattern:g,matcher:je(g,!i,e.matchFilters)})}return c},[])}function De(e,t=0){return{routes:e,score:Be(e[e.length-1])*1e4-t,matcher(r){const o=[];for(let n=e.length-1;n>=0;n--){const a=e[n],s=a.matcher(r);if(!s)return null;o.unshift({...s,route:a})}return o}}}function ue(e){return Array.isArray(e)?e:[e]}function le(e,t="",r=[],o=[]){const n=ue(e);for(let a=0,s=n.length;as.score-a.score)}function I(e,t){for(let r=0,o=e.length;r{const y=e();try{return new URL(y,o)}catch{return console.error(`Invalid path ${y}`),d}},o,{equals:(d,y)=>d.href===y.href}),a=L(()=>n().pathname),s=L(()=>n().search,!0),i=L(()=>n().hash),h=()=>"",c=N(s,()=>ae(n()));return{get pathname(){return a()},get search(){return s()},get hash(){return i()},get state(){return t()},get key(){return h()},query:r?r(c):se(c)}}let C;function $e(){return C}function Ke(e,t,r,o={}){const{signal:[n,a],utils:s={}}=e,i=s.parsePath||(f=>f),h=s.renderPath||(f=>f),c=s.beforeLeave||re(),d=D("",o.base||"");if(d===void 0)throw new Error(`${d} is not a valid base path`);d&&!n().value&&a({value:d,replace:!0,scroll:!1});const[y,m]=j(!1);let g;const b=(f,p)=>{p.value===u()&&p.state===v()||(g===void 0&&m(!0),C=f,g=p,Pe(()=>{g===p&&(l(g.value),w(g.state),Le(),k||E[1](R=>R.filter(x=>x.pending)))}).finally(()=>{g===p&&Se(()=>{C=void 0,f==="navigate"&&pe(g),m(!1),g=void 0})}))},[u,l]=j(n().value),[v,w]=j(n().state),A=Te(u,v,s.queryWrapper),P=[],E=j(k?we():[]),q=L(()=>typeof o.transformUrl=="function"?I(t(),o.transformUrl(A.pathname)):I(t(),A.pathname)),H=()=>{const f=q(),p={};for(let R=0;Rd,outlet:()=>null,resolvePath(f){return D(d,f)}};return be(N(n,f=>b("native",f),{defer:!0})),{base:V,location:A,params:fe,isRouting:y,renderPath:h,parsePath:i,navigatorFactory:me,matches:q,beforeLeave:c,preloadRoute:ge,singleFlight:o.singleFlight===void 0?!0:o.singleFlight,submissions:E};function de(f,p,R){ee(()=>{if(typeof p=="number"){p&&(s.go?s.go(p):console.warn("Router integration does not support relative routing"));return}const x=!p||p[0]==="?",{replace:_,resolve:O,scroll:W,state:U}={replace:!1,resolve:!x,scroll:!0,...R},F=O?f.resolvePath(p):D(x&&A.pathname||"",p);if(F===void 0)throw new Error(`Path '${p}' is not a routable path`);if(P.length>=Ie)throw new Error("Too many redirects");const J=u();if(F!==J||U!==v())if(k){const X=T();X&&(X.response={status:302,headers:new Headers({Location:F})}),a({value:F,replace:_,scroll:W,state:U})}else c.confirm(F,R)&&(P.push({value:J,replace:_,scroll:W,state:v()}),b("navigate",{value:F,state:U}))})}function me(f){return f=f||Re(ce)||V,(p,R)=>de(f,p,R)}function pe(f){const p=P[0];p&&(a({...f,replace:p.replace,scroll:p.scroll}),P.length=0)}function ge(f,p){const R=I(t(),f.pathname),x=C;C="preload";for(let _ in R){const{route:O,params:W}=R[_];O.component&&O.component.preload&&O.component.preload();const{preload:U}=O;p&&U&&Y(r(),()=>U({params:W,location:{pathname:f.pathname,search:f.search,hash:f.hash,query:ae(f),state:null,key:""},intent:"preload"}))}C=x}function we(){const f=T();return f&&f.router&&f.router.submission?[f.router.submission]:[]}}function Me(e,t,r,o){const{base:n,location:a,params:s}=e,{pattern:i,component:h,preload:c}=o().route,d=L(()=>o().path);h&&h.preload&&h.preload();const y=c?c({params:s,location:a,intent:C||"initial"}):void 0;return{parent:t,pattern:i,path:d,outlet:()=>h?Ae(h,{params:s,location:a,data:y,get children(){return r()}}):r(),resolvePath(g){return D(n.path(),g,d())}}}const he=e=>t=>{const{base:r}=t,o=Ee(()=>t.children),n=L(()=>le(o(),t.base||""));let a;const s=Ke(e,n,()=>a,{base:r,singleFlight:t.singleFlight,transformUrl:t.transformUrl});return e.create&&e.create(s),S(_e.Provider,{value:s,get children(){return S(Ne,{routerState:s,get root(){return t.root},get preload(){return t.rootPreload||t.rootLoad},get children(){return[(a=Q())&&null,S(ze,{routerState:s,get branches(){return n()}})]}})}})};function Ne(e){const t=e.routerState.location,r=e.routerState.params,o=L(()=>e.preload&&ee(()=>{e.preload({params:r,location:t,intent:$e()||"initial"})}));return S(te,{get when(){return e.root},keyed:!0,get fallback(){return e.children},children:n=>S(n,{params:r,location:t,get data(){return o()},get children(){return e.children}})})}function ze(e){if(k){const n=T();if(n&&n.router&&n.router.dataOnly){He(n,e.routerState,e.branches);return}n&&((n.router||(n.router={})).matches||(n.router.matches=e.routerState.matches().map(({route:a,path:s,params:i})=>({path:a.originalPath,pattern:a.pattern,match:s,params:i,info:a.info}))))}const t=[];let r;const o=L(N(e.routerState.matches,(n,a,s)=>{let i=a&&n.length===a.length;const h=[];for(let c=0,d=n.length;c{t[c]=g,h[c]=Me(e.routerState,h[c-1]||e.routerState.base,G(()=>o()[c+1]),()=>{const b=e.routerState.matches();return b[c]??b[0]})}))}return t.splice(n.length).forEach(c=>c()),s&&i?s:(r=h[0],h)}));return G(()=>o()&&r)()}const G=e=>()=>S(te,{get when(){return e()},keyed:!0,children:t=>S(ce.Provider,{value:t,get children(){return t.outlet()}})});function He(e,t,r){const o=new URL(e.request.url),n=I(r,new URL(e.router.previousUrl||e.request.url).pathname),a=I(r,o.pathname);for(let s=0;st(o(n)):t]}function Je(e){let t=!1;const r=n=>typeof n=="string"?{value:n}:n,o=Ve(j(r(e.get()),{equals:(n,a)=>n.value===a.value&&n.state===a.state}),void 0,n=>(!t&&e.set(n),$.registry&&!$.done&&($.done=!0),n));return e.init&&ne(e.init((n=e.get())=>{t=!0,o[1](r(n)),t=!1})),he({signal:o,create:e.create,utils:e.utils})}function Xe(e,t,r){return e.addEventListener(t,r),()=>e.removeEventListener(t,r)}function Ge(e,t){const r=e&&document.getElementById(e);r?r.scrollIntoView():t&&window.scrollTo(0,0)}function Qe(e){const t=new URL(e);return t.pathname+t.search}function Ye(e){let t;const r={value:e.url||(t=T())&&Qe(t.request.url)||""};return he({signal:[()=>r,o=>Object.assign(r,o)]})(e)}const Ze=new Map;function et(e=!0,t=!1,r="/_server",o){return n=>{const a=n.base.path(),s=n.navigatorFactory(n.base);let i,h;function c(u){return u.namespaceURI==="http://www.w3.org/2000/svg"}function d(u){if(u.defaultPrevented||u.button!==0||u.metaKey||u.altKey||u.ctrlKey||u.shiftKey)return;const l=u.composedPath().find(q=>q instanceof Node&&q.nodeName.toUpperCase()==="A");if(!l||t&&!l.hasAttribute("link"))return;const v=c(l),w=v?l.href.baseVal:l.href;if((v?l.target.baseVal:l.target)||!w&&!l.hasAttribute("state"))return;const P=(l.getAttribute("rel")||"").split(/\s+/);if(l.hasAttribute("download")||P&&P.includes("external"))return;const E=v?new URL(w,document.baseURI):new URL(w);if(!(E.origin!==window.location.origin||a&&E.pathname&&!E.pathname.toLowerCase().startsWith(a.toLowerCase())))return[l,E]}function y(u){const l=d(u);if(!l)return;const[v,w]=l,A=n.parsePath(w.pathname+w.search+w.hash),P=v.getAttribute("state");u.preventDefault(),s(A,{resolve:!1,replace:v.hasAttribute("replace"),scroll:!v.hasAttribute("noscroll"),state:P?JSON.parse(P):void 0})}function m(u){const l=d(u);if(!l)return;const[v,w]=l;o&&(w.pathname=o(w.pathname)),n.preloadRoute(w,v.getAttribute("preload")!=="false")}function g(u){clearTimeout(i);const l=d(u);if(!l)return h=null;const[v,w]=l;h!==v&&(o&&(w.pathname=o(w.pathname)),i=setTimeout(()=>{n.preloadRoute(w,v.getAttribute("preload")!=="false"),h=v},20))}function b(u){if(u.defaultPrevented)return;let l=u.submitter&&u.submitter.hasAttribute("formaction")?u.submitter.getAttribute("formaction"):u.target.getAttribute("action");if(!l)return;if(!l.startsWith("https://action/")){const w=new URL(l,oe);if(l=n.parsePath(w.pathname+w.search),!l.startsWith(r))return}if(u.target.method.toUpperCase()!=="POST")throw new Error("Only POST forms are supported for Actions");const v=Ze.get(l);if(v){u.preventDefault();const w=new FormData(u.target,u.submitter);v.call({r:n,f:u.target},u.target.enctype==="multipart/form-data"?w:new URLSearchParams(w))}}ye(["click","submit"]),document.addEventListener("click",y),e&&(document.addEventListener("mousemove",g,{passive:!0}),document.addEventListener("focusin",m,{passive:!0}),document.addEventListener("touchstart",m,{passive:!0})),document.addEventListener("submit",b),ne(()=>{document.removeEventListener("click",y),e&&(document.removeEventListener("mousemove",g),document.removeEventListener("focusin",m),document.removeEventListener("touchstart",m)),document.removeEventListener("submit",b)})}}function tt(e){if(k)return Ye(e);const t=()=>{const o=window.location.pathname.replace(/^\/+/,"/")+window.location.search,n=window.history.state&&window.history.state._depth&&Object.keys(window.history.state).length===1?void 0:window.history.state;return{value:o+window.location.hash,state:n}},r=re();return Je({get:t,set({value:o,replace:n,scroll:a,state:s}){n?window.history.replaceState(Oe(s),"",o):window.history.pushState(s,"",o),Ge(decodeURIComponent(window.location.hash.slice(1)),a),z()},init:o=>Xe(window,"popstate",Ue(o,n=>{if(n)return!r.confirm(n);{const a=t();return!r.confirm(a.value,{state:a.state})}})),create:et(e.preload,e.explicitLinks,e.actionBase,e.transformUrl),utils:{go:o=>window.history.go(o),beforeLeave:r}})(e)}function dt(){return S(tt,{root:e=>S(xe,{get children(){return e.children}}),get children(){return S(ve,{})}})}export{dt as default}; diff --git a/integration/apps/solid-start-vite/.vinxi/build/server-fns/_server/assets/index-AIk5O37p.js b/integration/apps/solid-start-vite/.vinxi/build/server-fns/_server/assets/index-AIk5O37p.js new file mode 100644 index 0000000..d4d8677 --- /dev/null +++ b/integration/apps/solid-start-vite/.vinxi/build/server-fns/_server/assets/index-AIk5O37p.js @@ -0,0 +1 @@ +import{createComponent as p,ssrElement as n,mergeProps as i,ssr as r,ssrAttribute as c,escape as s,ssrHydrationKey as u}from"solid-js/web";import{splitProps as m,Show as d,onMount as v}from"solid-js";const w="_root_1egzq_3",h="_wrap_1egzq_9",l={root:w,wrap:h},g={check:"check"},f="/_server/assets/sprite-DvsqBNon.svg";var _=[""],k=[""];const y=t=>{const[e,o]=m(t,["icon","wrapped","class"]),a=()=>f+"#"+g[e.icon];return p(d,{get when(){return e.wrapped},get fallback(){return n("svg",i(o,{get class(){return[l.root,e.class].filter(Boolean).join(" ")}}),()=>r(k,c("href",s(a(),!0),!1)),!0)},get children(){return n("span",i(o,{get class(){return[l.wrap,e.class].filter(Boolean).join(" ")}}),()=>r(_,c("href",s(a(),!0),!1)),!0)}})};var S=["

SolidStart + Vite

",'
'];function H(){let t;return v(async()=>{await import("./viewer-element-BO6fulPI.js");const e=document.createElement("gromlab-sprite-viewer");e.viewerTitle="SolidStart Vite Viewer",e.sources=[()=>import("./svg-sprite.manifest-D0DHYqq2.js")],t.append(e)}),r(S,u(),s(p(y,{"data-testid":"icon","data-app":"solid-start-vite",icon:"check","aria-label":"Check icon",width:64,height:64,style:{"--icon-color-1":"#16a34a"}})))}export{H,f as s}; diff --git a/integration/apps/solid-start-vite/.vinxi/build/server-fns/_server/assets/index-BWUdTtCi.css b/integration/apps/solid-start-vite/.vinxi/build/server-fns/_server/assets/index-BWUdTtCi.css new file mode 100644 index 0000000..013ec25 --- /dev/null +++ b/integration/apps/solid-start-vite/.vinxi/build/server-fns/_server/assets/index-BWUdTtCi.css @@ -0,0 +1 @@ +._root_1egzq_3{transition-property:fill,stroke,color;transition-duration:.3s;transition-timing-function:ease}._wrap_1egzq_9{display:inline-flex}._wrap_1egzq_9 svg{width:100%;height:100%;transition-property:fill,stroke,color;transition-duration:.3s;transition-timing-function:ease} diff --git a/integration/apps/solid-start-vite/.vinxi/build/server-fns/_server/assets/server-fns-XxvbTHGR.js b/integration/apps/solid-start-vite/.vinxi/build/server-fns/_server/assets/server-fns-XxvbTHGR.js new file mode 100644 index 0000000..bd384e5 --- /dev/null +++ b/integration/apps/solid-start-vite/.vinxi/build/server-fns/_server/assets/server-fns-XxvbTHGR.js @@ -0,0 +1 @@ +import{parseSetCookie as G}from"cookie-es";import{lazy as K,createComponent as V,sharedConfig as I}from"solid-js";import{ssrElement as R,escape as Z,mergeProps as Q,ssr as Y,isServer as ee,getRequestEvent as te,renderToString as ne}from"solid-js/web";import{provideRequestEvent as O}from"solid-js/web/storage";import{H3Event as T,getRequestIP as re,parseCookies as se,getResponseStatus as oe,getResponseStatusText as ae,getCookie as ie,setCookie as ce,getResponseHeader as ue,setResponseHeader as le,removeResponseHeader as de,getResponseHeaders as pe,getRequestURL as fe,getRequestWebStream as he,setResponseStatus as ge,appendResponseHeader as me,setHeader as Re,eventHandler as ye}from"h3";import{getContext as Se}from"unctx";import{AsyncLocalStorage as we}from"node:async_hooks";import{createRouter as be}from"radix3";import{Feature as ve,fromJSON as $e,crossSerializeStream as Te,getCrossReferenceHeader as xe,toCrossJSONStream as Ee}from"seroval";import{AbortSignalPlugin as qe,CustomEventPlugin as He,DOMExceptionPlugin as Ae,EventPlugin as Ce,FormDataPlugin as ke,HeadersPlugin as Pe,ReadableStreamPlugin as Fe,RequestPlugin as Le,ResponsePlugin as Ie,URLSearchParamsPlugin as Oe,URLPlugin as Ue}from"seroval-plugins/web";function _e(e){let n;const t=_(e),s={duplex:"half",method:e.method,headers:e.headers};return e.node.req.body instanceof ArrayBuffer?new Request(t,{...s,body:e.node.req.body}):new Request(t,{...s,get body(){return n||(n=Ge(e),n)}})}function je(e){return e.web??={request:_e(e),url:_(e)},e.web.request}function Ne(){return Qe()}const U=Symbol("$HTTPEvent");function Me(e){return typeof e=="object"&&(e instanceof T||e?.[U]instanceof T||e?.__is_event__===!0)}function u(e){return function(...n){let t=n[0];if(Me(t))n[0]=t instanceof T||t.__is_event__?t:t[U];else{if(!globalThis.app.config.server.experimental?.asyncContext)throw new Error("AsyncLocalStorage was not enabled. Use the `server.experimental.asyncContext: true` option in your app configuration to enable it. Or, pass the instance of HTTPEvent that you have as the first argument to the function.");if(t=Ne(),!t)throw new Error("No HTTPEvent found in AsyncLocalStorage. Make sure you are using the function within the server runtime.");n.unshift(t)}return e(...n)}}const _=u(fe),De=u(re),S=u(ge),q=u(oe),We=u(ae),y=u(pe),H=u(ue),Be=u(le),j=u(me),ze=u(se),Je=u(ie),Xe=u(ce),h=u(Re),Ge=u(he),Ke=u(de),Ve=u(je);function Ze(){return Se("nitro-app",{asyncContext:!!globalThis.app.config.server.experimental?.asyncContext,AsyncLocalStorage:we})}function Qe(){return Ze().use().event}const b="Invariant Violation",{setPrototypeOf:Ye=function(e,n){return e.__proto__=n,e}}=Object;class x extends Error{framesToPop=1;name=b;constructor(n=b){super(typeof n=="number"?`${b}: ${n} (see https://github.com/apollographql/invariant-packages)`:n),Ye(this,x.prototype)}}function et(e,n){if(!e)throw new x(n)}const v="solidFetchEvent";function tt(e){return{request:Ve(e),response:ot(e),clientAddress:De(e),locals:{},nativeEvent:e}}function nt(e){return{...e}}function rt(e){if(!e.context[v]){const n=tt(e);e.context[v]=n}return e.context[v]}function A(e,n){for(const[t,s]of n.entries())j(e,t,s)}class st{event;constructor(n){this.event=n}get(n){const t=H(this.event,n);return Array.isArray(t)?t.join(", "):t||null}has(n){return this.get(n)!==null}set(n,t){return Be(this.event,n,t)}delete(n){return Ke(this.event,n)}append(n,t){j(this.event,n,t)}getSetCookie(){const n=H(this.event,"Set-Cookie");return Array.isArray(n)?n:[n]}forEach(n){return Object.entries(y(this.event)).forEach(([t,s])=>n(Array.isArray(s)?s.join(", "):s,t,this))}entries(){return Object.entries(y(this.event)).map(([n,t])=>[n,Array.isArray(t)?t.join(", "):t])[Symbol.iterator]()}keys(){return Object.keys(y(this.event))[Symbol.iterator]()}values(){return Object.values(y(this.event)).map(n=>Array.isArray(n)?n.join(", "):n)[Symbol.iterator]()}[Symbol.iterator](){return this.entries()[Symbol.iterator]()}}function ot(e){return{get status(){return q(e)},set status(n){S(e,n)},get statusText(){return We(e)},set statusText(n){S(e,q(e),n)},headers:new st(e)}}const N=[{page:!0,$component:{src:"src/routes/index.jsx?pick=default&pick=$css",build:()=>import("../index.js"),import:()=>import("../index.js")},path:"/",filePath:"/home/gromov/projects/my/svg-sprites/integration/apps/solid-start-vite/src/routes/index.jsx"}],at=it(N.filter(e=>e.page));function it(e){function n(t,s,o,a){const i=Object.values(t).find(c=>o.startsWith(c.id+"/"));return i?(n(i.children||(i.children=[]),s,o.slice(i.id.length)),t):(t.push({...s,id:o,path:o.replace(/\([^)/]+\)/g,"").replace(/\/+/g,"/")}),t)}return e.sort((t,s)=>t.path.length-s.path.length).reduce((t,s)=>n(t,s,s.path,s.path),[])}function ct(e){return e.$HEAD||e.$GET||e.$POST||e.$PUT||e.$PATCH||e.$DELETE}be({routes:N.reduce((e,n)=>{if(!ct(n))return e;let t=n.path.replace(/\([^)/]+\)/g,"").replace(/\/+/g,"/").replace(/\*([^/]*)/g,(s,o)=>`**:${o}`).split("/").map(s=>s.startsWith(":")||s.startsWith("*")?s:encodeURIComponent(s)).join("/");if(/:[^/]*\?/g.test(t))throw new Error(`Optional parameters are not supported in API routes: ${t}`);if(e[t])throw new Error(`Duplicate API routes for "${t}" found at "${e[t].route.path}" and "${n.path}"`);return e[t]={route:n},e},{})});function ut(e){e.forEach(n=>{if(!n.attrs.href)return;let t=document.head.querySelector(`link[href="${n.attrs.href}"]`);t||(t=document.createElement("link"),t.setAttribute("rel","preload"),t.setAttribute("as","style"),t.setAttribute("href",n.attrs.href),document.head.appendChild(t))})}var lt=" ";const dt={style:e=>R("style",e.attrs,()=>e.children,!0),link:e=>R("link",e.attrs,void 0,!0),script:e=>e.attrs.src?R("script",Q(()=>e.attrs,{get id(){return e.key}}),()=>Y(lt),!0):null,noscript:e=>R("noscript",e.attrs,()=>Z(e.children),!0)};function pt(e,n){let{tag:t,attrs:{key:s,...o}={key:void 0},children:a}=e;return dt[t]({attrs:{...o,nonce:n},key:s,children:a})}function ft(e,n,t,s="default"){return K(async()=>{{const a=(await e.import())[s],c=(await n.inputs?.[e.src].assets()).filter(l=>l.tag==="style"||l.attrs.rel==="stylesheet");return typeof window<"u"&&ut(c),{default:l=>[...c.map(g=>pt(g)),V(a,l)]}}})}function M(){function e(t){return{...t,...t.$$route?t.$$route.require().route:void 0,info:{...t.$$route?t.$$route.require().route.info:{},filesystem:!0},component:t.$component&&ft(t.$component,globalThis.MANIFEST.client,globalThis.MANIFEST.ssr),children:t.children?t.children.map(e):void 0}}return at.map(e)}let C;const Ft=ee?()=>te().routes:()=>C||(C=M());function ht(e){const n=Je(e.nativeEvent,"flash");if(n)try{let t=JSON.parse(n);if(!t||!t.result)return;const s=[...t.input.slice(0,-1),new Map(t.input[t.input.length-1])],o=t.error?new Error(t.result):t.result;return{input:s,url:t.url,pending:!1,result:t.thrown?void 0:o,error:t.thrown?o:void 0}}catch(t){console.error(t)}finally{Xe(e.nativeEvent,"flash","",{maxAge:0})}}async function gt(e){const n=globalThis.MANIFEST.client;return globalThis.MANIFEST.ssr,e.response.headers.set("Content-Type","text/html"),Object.assign(e,{manifest:await n.json(),assets:[...await n.inputs[n.handler].assets()],router:{submission:ht(e)},routes:M(),complete:!1,$islands:new Set})}const mt=new Set([301,302,303,307,308]);function Rt(e){return e.status&&mt.has(e.status)?e.status:302}const yt={},E=[qe,He,Ae,Ce,ke,Pe,Fe,Le,Ie,Oe,Ue],St=64,D=ve.RegExp;function W(e){const n=new TextEncoder().encode(e),t=n.length,s=t.toString(16),o="00000000".substring(0,8-s.length)+s,a=new TextEncoder().encode(`;0x${o};`),i=new Uint8Array(12+t);return i.set(a),i.set(n,12),i}function k(e,n){return new ReadableStream({start(t){Te(n,{scopeId:e,plugins:E,onSerialize(s,o){t.enqueue(W(o?`(${xe(e)},${s})`:s))},onDone(){t.close()},onError(s){t.error(s)}})}})}function wt(e){return new ReadableStream({start(n){Ee(e,{disabledFeatures:D,depthLimit:St,plugins:E,onParse(t){n.enqueue(W(JSON.stringify(t)))},onDone(){n.close()},onError(t){n.error(t)}})}})}async function P(e){return $e(JSON.parse(e),{plugins:E,disabledFeatures:D})}async function bt(e){const n=rt(e),t=n.request,s=t.headers.get("X-Server-Id"),o=t.headers.get("X-Server-Instance"),a=t.headers.has("X-Single-Flight"),i=new URL(t.url);let c,p;if(s)et(typeof s=="string","Invalid server function"),[c,p]=decodeURIComponent(s).split("#");else if(c=i.searchParams.get("id"),p=i.searchParams.get("name"),!c||!p)return new Response(null,{status:404});const l=yt[c];let g;if(!l)return new Response(null,{status:404});g=await l.importer();const B=g[l.functionName];let f=[];if(!o||e.method==="GET"){const r=i.searchParams.get("args");if(r){const d=await P(r);for(const m of d)f.push(m)}}if(e.method==="POST"){const r=t.headers.get("content-type"),d=e.node.req,m=d instanceof ReadableStream,z=d.body instanceof ReadableStream,J=m&&d.locked||z&&d.body.locked,X=m?d:d.body,w=J?t:new Request(t,{...t,body:X});t.headers.get("x-serialized")?f=await P(await w.text()):r?.startsWith("multipart/form-data")||r?.startsWith("application/x-www-form-urlencoded")?f.push(await w.formData()):r?.startsWith("application/json")&&(f=await w.json())}try{let r=await O(n,async()=>(I.context={event:n},n.locals.serverFunctionMeta={id:c+"#"+p},B(...f)));if(a&&o&&(r=await L(n,r)),r instanceof Response){if(r.headers&&r.headers.has("X-Content-Raw"))return r;o&&(r.headers&&A(e,r.headers),r.status&&(r.status<300||r.status>=400)&&S(e,r.status),r.customBody?r=await r.customBody():r.body==null&&(r=null))}if(!o)return F(r,t,f);return h(e,"x-serialized","true"),h(e,"content-type","text/javascript"),k(o,r);return wt(r)}catch(r){if(r instanceof Response)a&&o&&(r=await L(n,r)),r.headers&&A(e,r.headers),r.status&&(!o||r.status<300||r.status>=400)&&S(e,r.status),r.customBody?r=r.customBody():r.body==null&&(r=null),h(e,"X-Error","true");else if(o){const d=r instanceof Error?r.message:typeof r=="string"?r:"true";h(e,"X-Error",d.replace(/[\r\n]+/g,""))}else r=F(r,t,f,!0);return o?(h(e,"x-serialized","true"),h(e,"content-type","text/javascript"),k(o,r)):r}}function F(e,n,t,s){const o=new URL(n.url),a=e instanceof Error;let i=302,c;return e instanceof Response?(c=new Headers(e.headers),e.headers.has("Location")&&(c.set("Location",new URL(e.headers.get("Location"),o.origin+"").toString()),i=Rt(e))):c=new Headers({Location:new URL(n.headers.get("referer")).toString()}),e&&c.append("Set-Cookie",`flash=${encodeURIComponent(JSON.stringify({url:o.pathname+o.search,result:a?e.message:e,thrown:s,error:a,input:[...t.slice(0,-1),[...t[t.length-1].entries()]]}))}; Secure; HttpOnly;`),new Response(null,{status:i,headers:c})}let $;function vt(e){const n=new Headers(e.request.headers),t=ze(e.nativeEvent),s=e.response.headers.getSetCookie();n.delete("cookie");let o=!1;return e.nativeEvent.node?.req&&(o=!0,e.nativeEvent.node.req.headers.cookie=""),s.forEach(a=>{if(!a)return;const{maxAge:i,expires:c,name:p,value:l}=G(a);if(i!=null&&i<=0){delete t[p];return}if(c!=null&&c.getTime()<=Date.now()){delete t[p];return}t[p]=l}),Object.entries(t).forEach(([a,i])=>{n.append("cookie",`${a}=${i}`),o&&(e.nativeEvent.node.req.headers.cookie+=`${a}=${i};`)}),n}async function L(e,n){let t,s=new URL(e.request.headers.get("referer")).toString();n instanceof Response&&(n.headers.has("X-Revalidate")&&(t=n.headers.get("X-Revalidate").split(",")),n.headers.has("Location")&&(s=new URL(n.headers.get("Location"),new URL(e.request.url).origin+"").toString()));const o=nt(e);return o.request=new Request(s,{headers:vt(e)}),await O(o,async()=>{await gt(o),$||($=(await import("./app-Bgr52ZUm.js")).default),o.router.dataOnly=t||!0,o.router.previousUrl=e.request.headers.get("referer");try{ne(()=>{I.context.event=o,$()})}catch(c){console.log(c)}const a=o.router.data;if(!a)return n;let i=!1;for(const c in a)a[c]===void 0?delete a[c]:i=!0;return i&&(n instanceof Response?n.customBody&&(a._$value=n.customBody()):(a._$value=n,n=new Response(null,{status:200})),n.customBody=()=>a,n.headers.set("X-Single-Flight","true")),n})}const Lt=ye(bt);export{Ft as F,Lt as h}; diff --git a/integration/apps/solid-start-vite/.vinxi/build/server-fns/_server/assets/sprite-DvsqBNon.svg b/integration/apps/solid-start-vite/.vinxi/build/server-fns/_server/assets/sprite-DvsqBNon.svg new file mode 100644 index 0000000..bd5742e --- /dev/null +++ b/integration/apps/solid-start-vite/.vinxi/build/server-fns/_server/assets/sprite-DvsqBNon.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/integration/apps/solid-start-vite/.vinxi/build/server-fns/_server/assets/svg-sprite.manifest-D0DHYqq2.js b/integration/apps/solid-start-vite/.vinxi/build/server-fns/_server/assets/svg-sprite.manifest-D0DHYqq2.js new file mode 100644 index 0000000..00c28b5 --- /dev/null +++ b/integration/apps/solid-start-vite/.vinxi/build/server-fns/_server/assets/svg-sprite.manifest-D0DHYqq2.js @@ -0,0 +1 @@ +import{s as o}from"./index-AIk5O37p.js";import"solid-js/web";import"solid-js";const s={schemaVersion:1,generator:"@gromlab/svg-sprites",name:"icons",usage:{framework:"solid-start",componentName:"IconsIcon",ssr:!0},mode:"solid-start@vite",target:"vite",format:"stack",iconCount:1,spriteUrl:o,icons:[{name:"check",id:"check",viewBox:"0 0 24 24",colors:[{variable:"--icon-color-1",fallback:"currentColor"}]}]};export{s as default,s as spriteManifest}; diff --git a/integration/apps/solid-start-vite/.vinxi/build/server-fns/_server/assets/viewer-element-BO6fulPI.js b/integration/apps/solid-start-vite/.vinxi/build/server-fns/_server/assets/viewer-element-BO6fulPI.js new file mode 100644 index 0000000..a96cb71 --- /dev/null +++ b/integration/apps/solid-start-vite/.vinxi/build/server-fns/_server/assets/viewer-element-BO6fulPI.js @@ -0,0 +1,602 @@ +var Rt=Object.defineProperty,Lt=Object.getOwnPropertyNames,m=(e,t,r)=>function(){if(r)throw r[0];try{return e&&(t=(0,e[Lt(e)[0]])(e=0)),t}catch(o){throw r=[o],o}},it=(e,t)=>{for(var r in t)Rt(e,r,{get:t[r],enumerable:!0})},E,u,te=m({"node_modules/vanilla-colorful/lib/utils/math.js"(){E=(e,t=0,r=1)=>e>r?r:eMath.round(r*e)/r}}),ot,Y,st,Pe,Z,He,S,Ue,Ne,re=m({"node_modules/vanilla-colorful/lib/utils/convert.js"(){te(),ot=e=>Ne(Y(e)),Y=e=>(e[0]==="#"&&(e=e.substring(1)),e.length<6?{r:parseInt(e[0]+e[0],16),g:parseInt(e[1]+e[1],16),b:parseInt(e[2]+e[2],16),a:e.length===4?u(parseInt(e[3]+e[3],16)/255,2):1}:{r:parseInt(e.substring(0,2),16),g:parseInt(e.substring(2,4),16),b:parseInt(e.substring(4,6),16),a:e.length===8?u(parseInt(e.substring(6,8),16)/255,2):1}),st=e=>Ue(He(e)),Pe=({h:e,s:t,v:r,a:i})=>{const o=(200-t)*r/100;return{h:u(e),s:u(o>0&&o<200?t*r/100/(o<=100?o:200-o)*100:0),l:u(o/2),a:u(i,2)}},Z=e=>{const{h:t,s:r,l:i}=Pe(e);return`hsl(${t}, ${r}%, ${i}%)`},He=({h:e,s:t,v:r,a:i})=>{e=e/360*6,t=t/100,r=r/100;const o=Math.floor(e),s=r*(1-t),a=r*(1-(e-o)*t),n=r*(1-(1-e+o)*t),l=o%6;return{r:u([r,a,s,s,n,r][l]*255),g:u([n,r,r,a,s,s][l]*255),b:u([s,s,n,r,r,a][l]*255),a:u(i,2)}},S=e=>{const t=e.toString(16);return t.length<2?"0"+t:t},Ue=({r:e,g:t,b:r,a:i})=>{const o=i<1?S(u(i*255)):"";return"#"+S(e)+S(t)+S(r)+o},Ne=({r:e,g:t,b:r,a:i})=>{const o=Math.max(e,t,r),s=o-Math.min(e,t,r),a=s?o===e?(t-r)/s:o===t?2+(r-e)/s:4+(e-t)/s:0;return{h:u(60*(a<0?a+6:a)),s:u(o?s/o*100:0),v:u(o/255*100),a:i}}}}),$e,at,nt=m({"node_modules/vanilla-colorful/lib/utils/compare.js"(){re(),$e=(e,t)=>{if(e===t)return!0;for(const r in e)if(e[r]!==t[r])return!1;return!0},at=(e,t)=>e.toLowerCase()===t.toLowerCase()?!0:$e(Y(e),Y(t))}}),ae,ie,X,Ae=m({"node_modules/vanilla-colorful/lib/utils/dom.js"(){ae={},ie=e=>{let t=ae[e];return t||(t=document.createElement("template"),t.innerHTML=e,ae[e]=t),t},X=(e,t,r)=>{e.dispatchEvent(new CustomEvent(t,{bubbles:!0,detail:r}))}}}),w,z,De,ne,Re,Ee,lt=m({"node_modules/vanilla-colorful/lib/components/slider.js"(){Ae(),te(),w=!1,z=e=>"touches"in e,De=e=>w&&!z(e)?!1:(w||(w=z(e)),!0),ne=(e,t)=>{const r=z(t)?t.touches[0]:t,i=e.el.getBoundingClientRect();X(e.el,"move",e.getMove({x:E((r.pageX-(i.left+window.pageXOffset))/i.width),y:E((r.pageY-(i.top+window.pageYOffset))/i.height)}))},Re=(e,t)=>{const r=t.keyCode;r>40||e.xy&&r<37||r<33||(t.preventDefault(),X(e.el,"move",e.getMove({x:r===39?.01:r===37?-.01:r===34?.05:r===33?-.05:r===35?1:r===36?-1:0,y:r===40?.01:r===38?-.01:0},!0)))},Ee=class{constructor(e,t,r,i){const o=ie(`
`);e.appendChild(o.content.cloneNode(!0));const s=e.querySelector(`[part=${t}]`);s.addEventListener("mousedown",this),s.addEventListener("touchstart",this),s.addEventListener("keydown",this),this.el=s,this.xy=i,this.nodes=[s.firstChild,s]}set dragging(e){const t=e?document.addEventListener:document.removeEventListener;t(w?"touchmove":"mousemove",this),t(w?"touchend":"mouseup",this)}handleEvent(e){switch(e.type){case"mousedown":case"touchstart":if(e.preventDefault(),!De(e)||!w&&e.button!=0)return;this.el.focus(),ne(this,e),this.dragging=!0;break;case"mousemove":case"touchmove":e.preventDefault(),ne(this,e);break;case"mouseup":case"touchend":this.dragging=!1;break;case"keydown":Re(this,e);break}}style(e){e.forEach((t,r)=>{for(const i in t)this.nodes[r].style.setProperty(i,t[i])})}}}}),ct,jt=m({"node_modules/vanilla-colorful/lib/components/hue.js"(){lt(),re(),te(),ct=class extends Ee{constructor(e){super(e,"hue",'aria-label="Hue" aria-valuemin="0" aria-valuemax="360"',!1)}update({h:e}){this.h=e,this.style([{left:`${e/360*100}%`,color:Z({h:e,s:100,v:100,a:1})}]),this.el.setAttribute("aria-valuenow",`${u(e)}`)}getMove(e,t){return{h:t?E(this.h+e.x*360,0,360):360*e.x}}}}}),dt,zt=m({"node_modules/vanilla-colorful/lib/components/saturation.js"(){lt(),re(),te(),dt=class extends Ee{constructor(e){super(e,"saturation",'aria-label="Color"',!0)}update(e){this.hsva=e,this.style([{top:`${100-e.v}%`,left:`${e.s}%`,color:Z(e)},{"background-color":Z({h:e.h,s:100,v:100,a:1})}]),this.el.setAttribute("aria-valuetext",`Saturation ${u(e.s)}%, Brightness ${u(e.v)}%`)}getMove(e,t){return{s:t?E(this.hsva.s+e.x*100,0,100):e.x*100,v:t?E(this.hsva.v-e.y*100,0,100):Math.round(100-e.y*100)}}}}}),ht,Bt=m({"node_modules/vanilla-colorful/lib/styles/color-picker.js"(){ht=':host{display:flex;flex-direction:column;position:relative;width:200px;height:200px;user-select:none;-webkit-user-select:none;cursor:default}:host([hidden]){display:none!important}[role=slider]{position:relative;touch-action:none;user-select:none;-webkit-user-select:none;outline:0}[role=slider]:last-child{border-radius:0 0 8px 8px}[part$=pointer]{position:absolute;z-index:1;box-sizing:border-box;width:28px;height:28px;display:flex;place-content:center center;transform:translate(-50%,-50%);background-color:#fff;border:2px solid #fff;border-radius:50%;box-shadow:0 2px 4px rgba(0,0,0,.2)}[part$=pointer]::after{content:"";width:100%;height:100%;border-radius:inherit;background-color:currentColor}[role=slider]:focus [part$=pointer]{transform:translate(-50%,-50%) scale(1.1)}'}}),pt,It=m({"node_modules/vanilla-colorful/lib/styles/hue.js"(){pt="[part=hue]{flex:0 0 24px;background:linear-gradient(to right,red 0,#ff0 17%,#0f0 33%,#0ff 50%,#00f 67%,#f0f 83%,red 100%)}[part=hue-pointer]{top:50%;z-index:2}"}}),ut,Vt=m({"node_modules/vanilla-colorful/lib/styles/saturation.js"(){ut="[part=saturation]{flex-grow:1;border-color:transparent;border-bottom:12px solid #000;border-radius:8px 8px 0 0;background-image:linear-gradient(to top,#000,transparent),linear-gradient(to right,#fff,rgba(255,255,255,0));box-shadow:inset 0 0 0 1px rgba(0,0,0,.05)}[part=saturation-pointer]{z-index:3}"}}),T,B,le,I,ce,de,he,gt,qt=m({"node_modules/vanilla-colorful/lib/components/color-picker.js"(){nt(),Ae(),jt(),zt(),Bt(),It(),Vt(),T=Symbol("same"),B=Symbol("color"),le=Symbol("hsva"),I=Symbol("update"),ce=Symbol("parts"),de=Symbol("css"),he=Symbol("sliders"),gt=class extends HTMLElement{static get observedAttributes(){return["color"]}get[de](){return[ht,pt,ut]}get[he](){return[dt,ct]}get color(){return this[B]}set color(e){if(!this[T](e)){const t=this.colorModel.toHsva(e);this[I](t),this[B]=e}}constructor(){super();const e=ie(``),t=this.attachShadow({mode:"open"});t.appendChild(e.content.cloneNode(!0)),t.addEventListener("move",this),this[ce]=this[he].map(r=>new r(t))}connectedCallback(){if(this.hasOwnProperty("color")){const e=this.color;delete this.color,this.color=e}else this.color||(this.color=this.colorModel.defaultColor)}attributeChangedCallback(e,t,r){const i=this.colorModel.fromAttr(r);this[T](i)||(this.color=i)}handleEvent(e){const t=this[le],r={...t,...e.detail};this[I](r);let i;!$e(r,t)&&!this[T](i=this.colorModel.fromHsva(r))&&(this[B]=i,X(this,"color-changed",{value:i}))}[T](e){return this.color&&this.colorModel.equal(e,this.color)}[I](e){this[le]=e,this[ce].forEach(t=>t.update(e))}}}}),mt={};it(mt,{HexBase:()=>vt});var Le,vt,Ft=m({"node_modules/vanilla-colorful/lib/entrypoints/hex.js"(){qt(),re(),nt(),Le={defaultColor:"#000",toHsva:ot,fromHsva:({h:e,s:t,v:r})=>st({h:e,s:t,v:r,a:1}),equal:at,fromAttr:e=>e},vt=class extends gt{get colorModel(){return Le}}}}),je,G,Jt=m({"node_modules/vanilla-colorful/lib/utils/validate.js"(){je=/^#?([0-9A-F]{3,8})$/i,G=(e,t)=>{const r=je.exec(e),i=r?r[1].length:0;return i===3||i===6||!!t&&i===4||!!t&&i===8}}}),_t={};it(_t,{HexInputBase:()=>ft});var ze,pe,ue,M,ge,V,q,me,C,ft,Wt=m({"node_modules/vanilla-colorful/lib/entrypoints/hex-input.js"(){Jt(),Ae(),ze=ie(''),pe=(e,t)=>e.replace(/([^0-9A-F]+)/gi,"").substring(0,t?8:6),ue=Symbol("alpha"),M=Symbol("color"),ge=Symbol("saved"),V=Symbol("input"),q=Symbol("init"),me=Symbol("prefix"),C=Symbol("update"),ft=class extends HTMLElement{static get observedAttributes(){return["alpha","color","prefixed"]}get color(){return this[M]}set color(e){this[M]=e,this[C](e)}get alpha(){return this[ue]}set alpha(e){this[ue]=e,this.toggleAttribute("alpha",e);const t=this.color;t&&!G(t,e)&&(this.color=t.startsWith("#")?t.substring(0,t.length===5?4:7):t.substring(0,t.length===4?3:6))}get prefixed(){return this[me]}set prefixed(e){this[me]=e,this.toggleAttribute("prefixed",e),this[C](this.color)}constructor(){super();const e=this.attachShadow({mode:"open"});e.appendChild(ze.content.cloneNode(!0)),e.firstElementChild.addEventListener("slotchange",()=>this[q](e))}connectedCallback(){if(this[q](this.shadowRoot),this.hasOwnProperty("alpha")){const e=this.alpha;delete this.alpha,this.alpha=e}else this.alpha=this.hasAttribute("alpha");if(this.hasOwnProperty("prefixed")){const e=this.prefixed;delete this.prefixed,this.prefixed=e}else this.prefixed=this.hasAttribute("prefixed");if(this.hasOwnProperty("color")){const e=this.color;delete this.color,this.color=e}else this.color==null?this.color=this.getAttribute("color")||"":this[M]&&this[C](this[M])}handleEvent(e){const t=e.target,{value:r}=t;switch(e.type){case"input":const i=pe(r,this.alpha);this[ge]=this.color,(G(i,this.alpha)||r==="")&&(this.color=i,this.dispatchEvent(new CustomEvent("color-changed",{bubbles:!0,detail:{value:i?"#"+i:""}})));break;case"blur":r&&!G(r,this.alpha)&&(this.color=this[ge])}}attributeChangedCallback(e,t,r){e==="color"&&this.color!==r&&(this.color=r);const i=r!=null;e==="alpha"&&this.alpha!==i&&(this.alpha=i),e==="prefixed"&&this.prefixed!==i&&(this.prefixed=i)}[q](e){let t=this.querySelector("input");if(!t){let r;for(;r=this.firstChild;)r.remove();t=e.querySelector("input")}t.addEventListener("input",this),t.addEventListener("blur",this),this[V]=t,this[C](this.color)}[C](e){this[V]&&(this[V].value=e==null||e==""?"":(this.prefixed?"#":"")+pe(e,this.alpha))}}}}),K=globalThis,ke=K.ShadowRoot&&(K.ShadyCSS===void 0||K.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,bt=Symbol(),Be=new WeakMap,Gt=class{constructor(e,t,r){if(this._$cssResult$=!0,r!==bt)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=e,this.t=t}get styleSheet(){let e=this.o;const t=this.t;if(ke&&e===void 0){const r=t!==void 0&&t.length===1;r&&(e=Be.get(t)),e===void 0&&((this.o=e=new CSSStyleSheet).replaceSync(this.cssText),r&&Be.set(t,e))}return e}toString(){return this.cssText}},wt=e=>new Gt(typeof e=="string"?e:e+"",void 0,bt),Kt=(e,t)=>{if(ke)e.adoptedStyleSheets=t.map(r=>r instanceof CSSStyleSheet?r:r.styleSheet);else for(const r of t){const i=document.createElement("style"),o=K.litNonce;o!==void 0&&i.setAttribute("nonce",o),i.textContent=r.cssText,e.appendChild(i)}},Ie=ke?e=>e:e=>e instanceof CSSStyleSheet?(t=>{let r="";for(const i of t.cssRules)r+=i.cssText;return wt(r)})(e):e,{is:Yt,defineProperty:Zt,getOwnPropertyDescriptor:Xt,getOwnPropertyNames:Qt,getOwnPropertySymbols:er,getPrototypeOf:tr}=Object,oe=globalThis,Ve=oe.trustedTypes,rr=Ve?Ve.emptyScript:"",ir=oe.reactiveElementPolyfillSupport,H=(e,t)=>e,xe={toAttribute(e,t){switch(t){case Boolean:e=e?rr:null;break;case Object:case Array:e=e==null?e:JSON.stringify(e)}return e},fromAttribute(e,t){let r=e;switch(t){case Boolean:r=e!==null;break;case Number:r=e===null?null:Number(e);break;case Object:case Array:try{r=JSON.parse(e)}catch{r=null}}return r}},$t=(e,t)=>!Yt(e,t),qe={attribute:!0,type:String,converter:xe,reflect:!1,useDefault:!1,hasChanged:$t};Symbol.metadata??=Symbol("metadata"),oe.litPropertyMetadata??=new WeakMap;var A=class extends HTMLElement{static addInitializer(e){this._$Ei(),(this.l??=[]).push(e)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(e,t=qe){if(t.state&&(t.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(e)&&((t=Object.create(t)).wrapped=!0),this.elementProperties.set(e,t),!t.noAccessor){const r=Symbol(),i=this.getPropertyDescriptor(e,r,t);i!==void 0&&Zt(this.prototype,e,i)}}static getPropertyDescriptor(e,t,r){const{get:i,set:o}=Xt(this.prototype,e)??{get(){return this[t]},set(s){this[t]=s}};return{get:i,set(s){const a=i?.call(this);o?.call(this,s),this.requestUpdate(e,a,r)},configurable:!0,enumerable:!0}}static getPropertyOptions(e){return this.elementProperties.get(e)??qe}static _$Ei(){if(this.hasOwnProperty(H("elementProperties")))return;const e=tr(this);e.finalize(),e.l!==void 0&&(this.l=[...e.l]),this.elementProperties=new Map(e.elementProperties)}static finalize(){if(this.hasOwnProperty(H("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(H("properties"))){const t=this.properties,r=[...Qt(t),...er(t)];for(const i of r)this.createProperty(i,t[i])}const e=this[Symbol.metadata];if(e!==null){const t=litPropertyMetadata.get(e);if(t!==void 0)for(const[r,i]of t)this.elementProperties.set(r,i)}this._$Eh=new Map;for(const[t,r]of this.elementProperties){const i=this._$Eu(t,r);i!==void 0&&this._$Eh.set(i,t)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(e){const t=[];if(Array.isArray(e)){const r=new Set(e.flat(1/0).reverse());for(const i of r)t.unshift(Ie(i))}else e!==void 0&&t.push(Ie(e));return t}static _$Eu(e,t){const r=t.attribute;return r===!1?void 0:typeof r=="string"?r:typeof e=="string"?e.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(e=>this.enableUpdating=e),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(e=>e(this))}addController(e){(this._$EO??=new Set).add(e),this.renderRoot!==void 0&&this.isConnected&&e.hostConnected?.()}removeController(e){this._$EO?.delete(e)}_$E_(){const e=new Map,t=this.constructor.elementProperties;for(const r of t.keys())this.hasOwnProperty(r)&&(e.set(r,this[r]),delete this[r]);e.size>0&&(this._$Ep=e)}createRenderRoot(){const e=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return Kt(e,this.constructor.elementStyles),e}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(e=>e.hostConnected?.())}enableUpdating(e){}disconnectedCallback(){this._$EO?.forEach(e=>e.hostDisconnected?.())}attributeChangedCallback(e,t,r){this._$AK(e,r)}_$ET(e,t){const r=this.constructor.elementProperties.get(e),i=this.constructor._$Eu(e,r);if(i!==void 0&&r.reflect===!0){const o=(r.converter?.toAttribute!==void 0?r.converter:xe).toAttribute(t,r.type);this._$Em=e,o==null?this.removeAttribute(i):this.setAttribute(i,o),this._$Em=null}}_$AK(e,t){const r=this.constructor,i=r._$Eh.get(e);if(i!==void 0&&this._$Em!==i){const o=r.getPropertyOptions(i),s=typeof o.converter=="function"?{fromAttribute:o.converter}:o.converter?.fromAttribute!==void 0?o.converter:xe;this._$Em=i;const a=s.fromAttribute(t,o.type);this[i]=a??this._$Ej?.get(i)??a,this._$Em=null}}requestUpdate(e,t,r,i=!1,o){if(e!==void 0){const s=this.constructor;if(i===!1&&(o=this[e]),r??=s.getPropertyOptions(e),!((r.hasChanged??$t)(o,t)||r.useDefault&&r.reflect&&o===this._$Ej?.get(e)&&!this.hasAttribute(s._$Eu(e,r))))return;this.C(e,t,r)}this.isUpdatePending===!1&&(this._$ES=this._$EP())}C(e,t,{useDefault:r,reflect:i,wrapped:o},s){r&&!(this._$Ej??=new Map).has(e)&&(this._$Ej.set(e,s??t??this[e]),o!==!0||s!==void 0)||(this._$AL.has(e)||(this.hasUpdated||r||(t=void 0),this._$AL.set(e,t)),i===!0&&this._$Em!==e&&(this._$Eq??=new Set).add(e))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(t){Promise.reject(t)}const e=this.scheduleUpdate();return e!=null&&await e,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(const[i,o]of this._$Ep)this[i]=o;this._$Ep=void 0}const r=this.constructor.elementProperties;if(r.size>0)for(const[i,o]of r){const{wrapped:s}=o,a=this[i];s!==!0||this._$AL.has(i)||a===void 0||this.C(i,void 0,o,a)}}let e=!1;const t=this._$AL;try{e=this.shouldUpdate(t),e?(this.willUpdate(t),this._$EO?.forEach(r=>r.hostUpdate?.()),this.update(t)):this._$EM()}catch(r){throw e=!1,this._$EM(),r}e&&this._$AE(t)}willUpdate(e){}_$AE(e){this._$EO?.forEach(t=>t.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(e)),this.updated(e)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(e){return!0}update(e){this._$Eq&&=this._$Eq.forEach(t=>this._$ET(t,this[t])),this._$EM()}updated(e){}firstUpdated(e){}};A.elementStyles=[],A.shadowRootOptions={mode:"open"},A[H("elementProperties")]=new Map,A[H("finalized")]=new Map,ir?.({ReactiveElement:A}),(oe.reactiveElementVersions??=[]).push("2.1.2");var Se=globalThis,Fe=e=>e,Q=Se.trustedTypes,Je=Q?Q.createPolicy("lit-html",{createHTML:e=>e}):void 0,xt="$lit$",f=`lit$${Math.random().toFixed(9).slice(2)}$`,yt="?"+f,or=`<${yt}>`,y=document,N=()=>y.createComment(""),D=e=>e===null||typeof e!="object"&&typeof e!="function",Te=Array.isArray,sr=e=>Te(e)||typeof e?.[Symbol.iterator]=="function",ve=`[ +\f\r]`,O=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,We=/-->/g,Ge=/>/g,$=RegExp(`>|${ve}(?:([^\\s"'>=/]+)(${ve}*=${ve}*(?:[^ +\f\r"'\`<>=]|("|')|))|$)`,"g"),Ke=/'/g,Ye=/"/g,Ct=/^(?:script|style|textarea|title)$/i,ar=e=>(t,...r)=>({_$litType$:e,strings:t,values:r}),p=ar(1),b=Symbol.for("lit-noChange"),c=Symbol.for("lit-nothing"),Ze=new WeakMap,x=y.createTreeWalker(y,129);function At(e,t){if(!Te(e)||!e.hasOwnProperty("raw"))throw Error("invalid template strings array");return Je!==void 0?Je.createHTML(t):t}var nr=(e,t)=>{const r=e.length-1,i=[];let o,s=t===2?"":t===3?"":"",a=O;for(let n=0;n"?(a=o??O,h=-1):d[1]===void 0?h=-2:(h=a.lastIndex-d[2].length,g=d[1],a=d[3]===void 0?$:d[3]==='"'?Ye:Ke):a===Ye||a===Ke?a=$:a===We||a===Ge?a=O:(a=$,o=void 0);const _=a===$&&e[n+1].startsWith("/>")?" ":"";s+=a===O?l+or:h>=0?(i.push(g),l.slice(0,h)+xt+l.slice(h)+f+_):l+f+(h===-2?n:_)}return[At(e,s+(e[r]||"")+(t===2?"":t===3?"":"")),i]},ye=class Et{constructor({strings:t,_$litType$:r},i){let o;this.parts=[];let s=0,a=0;const n=t.length-1,l=this.parts,[g,d]=nr(t,r);if(this.el=Et.createElement(g,i),x.currentNode=this.el.content,r===2||r===3){const h=this.el.content.firstChild;h.replaceWith(...h.childNodes)}for(;(o=x.nextNode())!==null&&l.length0){o.textContent=Q?Q.emptyScript:"";for(let _=0;_2||r[0]!==""||r[1]!==""?(this._$AH=Array(r.length-1).fill(new String),this.strings=r):this._$AH=c}_$AI(e,t=this,r,i){const o=this.strings;let s=!1;if(o===void 0)e=k(this,e,t,0),s=!D(e)||e!==this._$AH&&e!==b,s&&(this._$AH=e);else{const a=e;let n,l;for(e=o[0],n=0;n{const i=r?.renderBefore??t;let o=i._$litPart$;if(o===void 0){const s=r?.renderBefore??null;i._$litPart$=o=new Me(t.insertBefore(N(),s),s,void 0,r??{})}return o._$AI(e),o},Oe=globalThis,U=class extends A{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){const e=super.createRenderRoot();return this.renderOptions.renderBefore??=e.firstChild,e}update(e){const t=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(e),this._$Do=gr(t,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return b}};U._$litElement$=!0,U.finalized=!0,Oe.litElementHydrateSupport?.({LitElement:U});var mr=Oe.litElementPolyfillSupport;mr?.({LitElement:U});(Oe.litElementVersions??=[]).push("4.2.2");var F=e=>e??c,St={ATTRIBUTE:1,CHILD:2},Tt=e=>(...t)=>({_$litDirective$:e,values:t}),Mt=class{constructor(e){}get _$AU(){return this._$AM._$AU}_$AT(e,t,r){this._$Ct=e,this._$AM=t,this._$Ci=r}_$AS(e,t){return this.update(e,t)}update(e,t){return this.render(...t)}},Ot="important",vr=" !"+Ot,_e=Tt(class extends Mt{constructor(e){if(super(e),e.type!==St.ATTRIBUTE||e.name!=="style"||e.strings?.length>2)throw Error("The `styleMap` directive must be used in the `style` attribute and must be the only part in the attribute.")}render(e){return Object.keys(e).reduce((t,r)=>{const i=e[r];return i==null?t:t+`${r=r.includes("-")?r:r.replace(/(?:^(webkit|moz|ms|o)|)(?=[A-Z])/g,"-$&").toLowerCase()}:${i};`},"")}update(e,[t]){const{style:r}=e.element;if(this.ft===void 0)return this.ft=new Set(Object.keys(t)),this.render(t);for(const i of this.ft)t[i]==null&&(this.ft.delete(i),i.includes("-")?r.removeProperty(i):r[i]=null);for(const i in t){const o=t[i];if(o!=null){this.ft.add(i);const s=typeof o=="string"&&o.endsWith(vr);i.includes("-")||s?r.setProperty(i,s?o.slice(0,-11):o,s?Ot:""):r[i]=o}}return b}}),Ce=class extends Mt{constructor(e){if(super(e),this.it=c,e.type!==St.CHILD)throw Error(this.constructor.directiveName+"() can only be used in child bindings")}render(e){if(e===c||e==null)return this._t=void 0,this.it=e;if(e===b)return e;if(typeof e!="string")throw Error(this.constructor.directiveName+"() called with a non-string value");if(e===this.it)return this._t;this.it=e;const t=[e];return t.raw=t,this._t={_$litType$:this.constructor.resultType,strings:t,values:[]}}};Ce.directiveName="unsafeHTML",Ce.resultType=1;var _r=Tt(Ce),fr=[{id:"svg",label:"SVG"},{id:"img",label:"IMG"},{id:"css",label:"CSS"}],br={react:"React",vue:"Vue",svelte:"Svelte",angular:"Angular",astro:"Astro",solid:"Solid","solid-start":"SolidStart",preact:"Preact",qwik:"Qwik",lit:"Lit",alpine:"Alpine"};function Pt(e){return e.usage?e.usage:e.componentName?{framework:"react",componentName:e.componentName}:null}function fe(e){const t=Pt(e),i=[...t?[{id:t.framework,label:br[t.framework]}]:[],...fr];return e.format==="stack"?i:i.filter(o=>o.id!=="img"&&o.id!=="css")}function wr(e){if(!e)return null;const t=e.trim().split(/[\s,]+/).map(Number);return t.length===4&&t.every(Number.isFinite)?`${t[2]} × ${t[3]}`:null}function R(e,t="#1a1a1a"){const r=e.trim().toLowerCase();if(r==="currentcolor")return R(t);const i=r.match(/^#([\da-f])([\da-f])([\da-f])(?:[\da-f])?$/i);if(i)return`#${i[1]}${i[1]}${i[2]}${i[2]}${i[3]}${i[3]}`;const o=r.match(/^#([\da-f]{6})(?:[\da-f]{2})?$/i);if(o)return`#${o[1]}`;const s=r.match(/^rgba?\(\s*(\d+)\s*[, ]\s*(\d+)\s*[, ]\s*(\d+)/);return s?`#${s.slice(1,4).map(n=>Math.min(255,Number(n)).toString(16).padStart(2,"0")).join("")}`:{black:"#000000",blue:"#0000ff",cyan:"#00ffff",gray:"#808080",green:"#008000",grey:"#808080",magenta:"#ff00ff",orange:"#ffa500",pink:"#ffc0cb",purple:"#800080",red:"#ff0000",white:"#ffffff",yellow:"#ffff00"}[r]??R(t===e?"#1a1a1a":t)}function $r(e){return Object.entries(e).map(([t,r])=>` ${JSON.stringify(t)}: ${JSON.stringify(r)},`)}function P(e){const t={"&":"&",'"':""","<":"<",">":">"};return e.replace(/[&"<>]/g,r=>t[r])}function xr(e){return e.replace(/\\/g,"\\\\").replace(/'/g,"\\'")}function yr(e){const{manifest:t,icon:r,tab:i,colorOverrides:o,cssColor:s}=e,a=`${t.spriteUrl}#${r.id}`,n=$r(o);if(!["svg","img","css"].includes(i)){const d=Pt(t);if(!d)throw new Error(`The ${i} code tab requires component metadata.`);if(d.framework==="alpine")return{code:``,language:"html"};if(d.framework==="lit")return{code:`<${d.tagName} icon=${JSON.stringify(r.name)}>`,language:"html"};if(d.framework==="angular")return{code:`<${d.selector} icon=${JSON.stringify(r.name)}>`,language:"html"};if(d.framework==="vue"){const h=Object.entries(o).map(([v,_])=>`${JSON.stringify(v)}: ${JSON.stringify(_)}`).join(", ");return{code:`<${d.componentName} icon=${JSON.stringify(r.name)}${h?` :style="{ ${P(h)} }"`:""} />`,language:"html"}}return n.length===0?{code:`<${d.componentName} icon=${JSON.stringify(r.name)} />`,language:"tsx"}:{code:[`<${d.componentName}`,` icon=${JSON.stringify(r.name)}`," style={{",...n," }}","/>"].join(` +`),language:"tsx"}}if(i==="svg"){const d=Object.entries(o).map(([h,v])=>`${h}: ${v}`).join("; ");return{code:` + +`,language:"html"}}if(i==="img")return{code:`${P(r.name)}`,language:"html"};const l=`icon-${r.name.replace(/[^a-zA-Z0-9_-]+/g,"-")||"sprite"}`,g=xr(a);return{code:[`.${l} {`," width: 24px;"," height: 24px;",` mask: url('${g}') no-repeat center / contain;`,` -webkit-mask: url('${g}') no-repeat center / contain;`,` background-color: ${s};`,"}"].join(` +`),language:"css"}}var Cr=/[&<>"]/g,Ar={"&":"&","<":"<",">":">",'"':"""},Er=[[//g,"comment"],[/<\/?[\w.-]+/g,"tag"],[/[\w-]+(?=\s*=)/g,"attr"],[/"[^"]*"|'[^']*'/g,"string"]],kr=[[/\/\*[\s\S]*?\*\//g,"comment"],[/\.[\w-]+/g,"selector"],[/[\w-]+(?=\s*:)/g,"property"],[/'[^']*'|"[^"]*"/g,"string"],[/#[\da-fA-F]{3,8}\b/g,"color"],[/\d+(?:\.\d+)?(?:px|em|rem|%)?/g,"number"]];function Sr(e,t){const r=t==="css"?kr:Er,i=[];let o=0;for(;o{const n=a.replace(Cr,l=>Ar[l]);return s?`${n}`:n}).join("")}var J=new WeakMap;function L(e){return e!==null&&typeof e=="object"}function Ht(e){return L(e)?typeof e.name=="string"&&typeof e.id=="string"&&(e.viewBox===null||typeof e.viewBox=="string")&&Array.isArray(e.colors):!1}function Ut(e){return L(e)?e.schemaVersion===1&&e.generator==="@gromlab/svg-sprites"&&typeof e.name=="string"&&typeof e.target=="string"&&(e.format==="stack"||e.format==="symbol")&&typeof e.iconCount=="number"&&typeof e.spriteUrl=="string"&&Array.isArray(e.icons)&&e.icons.every(Ht):!1}function Nt(e){return L(e)&&e.schemaVersion===1&&e.generator==="@gromlab/svg-sprites"&&typeof e.name=="string"&&typeof e.target=="string"&&(e.format==="stack"||e.format==="symbol")&&typeof e.iconCount=="number"&&Array.isArray(e.icons)&&e.icons.every(Ht)}function Tr(e){return!L(e)||Ut(e)||Nt(e)?e:e.default??e.spriteManifest??e}function ee(e,t){const r=Tr(e);if(Ut(r))return r;if(t&&Nt(r))return{...r,spriteUrl:t};throw new Error("The loaded source does not contain a valid SVG sprite manifest.")}function Xe(e){return Array.isArray(e)?e:Object.values(e)}function Dt(e){if(!L(e))return!1;const t=e;return typeof t.manifestUrl=="string"&&typeof t.spriteUrl=="string"}function Qe(e,t){return e.namet.name?1:0}async function Mr(e){const t=await fetch(e.manifestUrl);if(!t.ok)throw new Error(`Cannot load SVG sprite manifest: ${t.status} ${t.statusText}`);return ee(await t.json(),e.spriteUrl)}async function Or(e){if(Dt(e))return Mr(e);if(typeof e!="function")return ee(e);const t=J.get(e);if(t)return t;const r=Promise.resolve().then(e).then(i=>ee(i));return J.set(e,r),r.catch(()=>{J.get(e)===r&&J.delete(e)}),r}var Pr=` +:host { display: block; } +.gromlab-sprite-viewer { + --sv-bg: #f0f0f3; + --sv-text: #1a1a1a; + --sv-card: #ffffff; + --sv-card-hover: #eaeaed; + --sv-border: #d8d8d8; + --sv-accent: #3b82f6; + --sv-muted: #777777; + --sv-code: #f5f5f5; + --sv-checker-a: #e9e9e9; + --sv-checker-b: #ffffff; + --sv-danger: #b42332; + box-sizing: border-box; + min-height: 320px; + padding: 24px; + color: var(--sv-text); + color-scheme: light; + background: var(--sv-bg); + border-radius: 12px; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; +} +.gromlab-sprite-viewer[data-theme="dark"] { + --sv-bg: #1a1a1a; + --sv-text: #e5e5e5; + --sv-card: #2a2a2a; + --sv-card-hover: #333333; + --sv-border: #404040; + --sv-muted: #a3a3a3; + --sv-code: #242424; + --sv-checker-a: #333333; + --sv-checker-b: #2a2a2a; + --sv-danger: #ff9ba6; + color-scheme: dark; +} +@media (prefers-color-scheme: dark) { + .gromlab-sprite-viewer:not([data-theme]) { + --sv-bg: #1a1a1a; + --sv-text: #e5e5e5; + --sv-card: #2a2a2a; + --sv-card-hover: #333333; + --sv-border: #404040; + --sv-muted: #a3a3a3; + --sv-code: #242424; + --sv-checker-a: #333333; + --sv-checker-b: #2a2a2a; + --sv-danger: #ff9ba6; + color-scheme: dark; + } +} +.gromlab-sprite-viewer *, +.gromlab-sprite-viewer *::before, +.gromlab-sprite-viewer *::after { box-sizing: border-box; } +.gromlab-sprite-viewer button, +.gromlab-sprite-viewer input { font: inherit; } +.gromlab-sprite-viewer__header { + display: flex; + align-items: center; + gap: 16px; + flex-wrap: wrap; + margin-bottom: 24px; +} +.gromlab-sprite-viewer__title { margin: 0; font-size: 24px; line-height: 1.2; font-weight: 700; } +.gromlab-sprite-viewer__summary { color: var(--sv-muted); font-size: 13px; } +.gromlab-sprite-viewer__toolbar { display: flex; align-items: center; gap: 12px; margin-left: auto; } +.gromlab-sprite-viewer__search { + width: 220px; + height: 38px; + padding: 0 12px; + color: var(--sv-text); + background: var(--sv-card); + border: 1px solid var(--sv-border); + border-radius: 8px; + outline: none; + transition: border-color .15s, box-shadow .15s; +} +.gromlab-sprite-viewer__search:focus { border-color: var(--sv-accent); box-shadow: 0 0 0 3px rgba(59, 130, 246, .18); } +.gromlab-sprite-viewer__search::placeholder { color: var(--sv-muted); } +.gromlab-sprite-viewer__theme { + display: grid; + place-items: center; + width: 38px; + height: 38px; + padding: 0; + color: var(--sv-text); + background: var(--sv-card); + border: 1px solid var(--sv-border); + border-radius: 8px; + cursor: pointer; + transition: background .15s, border-color .15s; +} +.gromlab-sprite-viewer__theme:hover { background: var(--sv-card-hover); } +.gromlab-sprite-viewer__theme:focus-visible, +.gromlab-sprite-viewer__card:focus-visible, +.gromlab-sprite-viewer__close:focus-visible, +.gromlab-sprite-viewer__tab:focus-visible, +.gromlab-sprite-viewer__copy:focus-visible, +.gromlab-sprite-viewer__swatch:focus-visible { + outline: 2px solid var(--sv-accent); + outline-offset: 2px; +} +.gromlab-sprite-viewer__errors { + margin: 0 0 24px; + padding: 12px 14px; + color: var(--sv-danger); + background: rgba(180, 35, 50, .08); + border: 1px solid rgba(180, 35, 50, .28); + border-radius: 8px; + font-size: 12px; +} +.gromlab-sprite-viewer__group { margin-bottom: 40px; } +.gromlab-sprite-viewer__group-header { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 16px; } +.gromlab-sprite-viewer__group-title { margin: 0; font-size: 18px; line-height: 1.3; font-weight: 600; } +.gromlab-sprite-viewer__badge { + padding: 2px 8px; + color: #ffffff; + background: var(--sv-accent); + border-radius: 999px; + font-size: 11px; + line-height: 1.5; + font-weight: 600; + letter-spacing: .04em; + text-transform: uppercase; +} +.gromlab-sprite-viewer__group-count, +.gromlab-sprite-viewer__description { color: var(--sv-muted); font-size: 13px; font-weight: 400; } +.gromlab-sprite-viewer__description { flex-basis: 100%; margin: -2px 0 0; } +.gromlab-sprite-viewer__grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 12px; } +.gromlab-sprite-viewer__card { + display: flex; + min-width: 0; + padding: 16px 8px; + color: inherit; + text-align: center; + background: var(--sv-card); + border: 0; + border-radius: 8px; + cursor: pointer; + flex-direction: column; + align-items: center; + gap: 8px; + transition: background .15s, transform .15s; +} +.gromlab-sprite-viewer__card:hover { background: var(--sv-card-hover); transform: translateY(-1px); } +.gromlab-sprite-viewer__icon-wrap, +.gromlab-sprite-viewer__dialog-preview-canvas { + display: grid; + place-items: center; + background: conic-gradient( + var(--sv-checker-a) 25%, var(--sv-checker-b) 0 50%, + var(--sv-checker-a) 0 75%, var(--sv-checker-b) 0 + ); + background-size: 8px 8px; + border-radius: 4px; +} +.gromlab-sprite-viewer__icon-wrap { width: 128px; height: 128px; } +.gromlab-sprite-viewer__icon { display: block; width: 128px; height: 128px; color: var(--sv-text); overflow: visible; } +.gromlab-sprite-viewer__icon-name { + max-width: 100%; + overflow: hidden; + color: var(--sv-muted); + font-size: 13px; + line-height: 1.35; + text-overflow: ellipsis; + white-space: nowrap; +} +.gromlab-sprite-viewer__status { + padding: 40px 20px; + color: var(--sv-muted); + text-align: center; + background: var(--sv-card); + border: 1px dashed var(--sv-border); + border-radius: 8px; +} +.gromlab-sprite-viewer__footer { + display: flex; + align-items: center; + justify-content: center; + gap: 12px; + margin-top: 40px; + padding-top: 16px; + color: var(--sv-muted); + border-top: 1px solid var(--sv-border); + font-size: 13px; +} +.gromlab-sprite-viewer__footer a { color: var(--sv-accent); text-decoration: none; } +.gromlab-sprite-viewer__footer a:hover { text-decoration: underline; } +.gromlab-sprite-viewer__dialog { + width: min(560px, calc(100vw - 32px)); + max-width: none; + max-height: calc(100dvh - 48px); + padding: 0; + color: var(--sv-text); + background: transparent; + border: 0; + overflow: visible; +} +.gromlab-sprite-viewer__dialog::backdrop { background: rgba(0, 0, 0, .55); backdrop-filter: blur(2px); } +.gromlab-sprite-viewer__dialog-shell { + position: relative; + max-height: calc(100dvh - 48px); + padding: 24px; + overflow-y: auto; + color: var(--sv-text); + background: var(--sv-bg); + border: 1px solid var(--sv-border); + border-radius: 12px; + box-shadow: 0 24px 72px rgba(0, 0, 0, .28); +} +.gromlab-sprite-viewer__close { + position: absolute; + z-index: 2; + top: 12px; + right: 12px; + display: grid; + place-items: center; + width: 34px; + height: 34px; + padding: 0; + color: var(--sv-text); + background: var(--sv-bg); + border: 1px solid var(--sv-border); + border-radius: 8px; + cursor: pointer; +} +.gromlab-sprite-viewer__close:hover { background: var(--sv-card-hover); } +.gromlab-sprite-viewer__dialog-preview { display: flex; align-items: center; justify-content: center; padding: 24px; margin-bottom: 16px; background: var(--sv-card); border-radius: 8px; } +.gromlab-sprite-viewer__dialog-preview-canvas { width: 256px; height: 256px; } +.gromlab-sprite-viewer__dialog-icon, +.gromlab-sprite-viewer__dialog-img, +.gromlab-sprite-viewer__dialog-mask { display: block; width: 256px; height: 256px; } +.gromlab-sprite-viewer__dialog-icon { color: var(--sv-text); overflow: visible; } +.gromlab-sprite-viewer__dialog-img { object-fit: contain; } +.gromlab-sprite-viewer__dialog-heading { display: flex; justify-content: center; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 6px; } +.gromlab-sprite-viewer__dialog-title { margin: 0; font: 600 16px/1.3 ui-monospace, "SFMono-Regular", Consolas, monospace; } +.gromlab-sprite-viewer__viewbox { + padding: 2px 8px; + color: var(--sv-muted); + background: var(--sv-card); + border: 1px solid var(--sv-border); + border-radius: 999px; + font-size: 11px; + white-space: nowrap; +} +.gromlab-sprite-viewer__dialog-meta { margin: 0 0 20px; color: var(--sv-muted); text-align: center; font-size: 12px; } +.gromlab-sprite-viewer__colors { margin-bottom: 20px; } +.gromlab-sprite-viewer__hint { + margin: 0 0 12px; + padding: 8px 12px; + color: var(--sv-muted); + background: var(--sv-card); + border-left: 3px solid var(--sv-accent); + border-radius: 4px; + font-size: 12px; + line-height: 1.5; +} +.gromlab-sprite-viewer__colors-title { margin: 0 0 8px; color: var(--sv-muted); font-size: 12px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; } +.gromlab-sprite-viewer__color-row { position: relative; display: flex; align-items: center; gap: 8px; min-height: 32px; margin-bottom: 6px; } +.gromlab-sprite-viewer__swatch { width: 26px; height: 26px; flex: 0 0 auto; padding: 0; border: 1px solid var(--sv-border); border-radius: 4px; cursor: pointer; } +.gromlab-sprite-viewer__color-label { min-width: 0; overflow-wrap: anywhere; color: var(--sv-muted); font: 12px/1.4 ui-monospace, "SFMono-Regular", Consolas, monospace; } +.gromlab-sprite-viewer__color-popover { + position: absolute; + z-index: 5; + bottom: calc(100% + 6px); + left: 0; + width: 224px; + max-width: calc(100vw - 64px); + padding: 12px; + background: var(--sv-bg); + border: 1px solid var(--sv-border); + border-radius: 8px; + box-shadow: 0 8px 28px rgba(0, 0, 0, .22); +} +.gromlab-sprite-viewer__color-picker { display: flex; flex-direction: column; width: 198px; max-width: 100%; height: 160px; } +.gromlab-sprite-viewer__hex-input { display: block; width: 100%; margin-top: 8px; } +.gromlab-sprite-viewer__hex-input::part(input) { box-sizing: border-box; display: block; width: 100%; height: 32px; padding: 0 8px; color: var(--sv-text); text-align: center; background: var(--sv-card); border: 1px solid var(--sv-border); border-radius: 4px; outline: none; font: 12px/1 ui-monospace, "SFMono-Regular", Consolas, monospace; } +.gromlab-sprite-viewer__hex-input::part(input):focus { border-color: var(--sv-accent); } +.gromlab-sprite-viewer__tabs { display: flex; overflow-x: auto; margin-bottom: 12px; border-bottom: 1px solid var(--sv-border); } +.gromlab-sprite-viewer__tab { flex: 0 0 auto; padding: 8px 16px; color: var(--sv-muted); background: none; border: 0; border-bottom: 2px solid transparent; cursor: pointer; font-size: 12px; font-weight: 600; } +.gromlab-sprite-viewer__tab[aria-selected="true"] { color: var(--sv-accent); border-bottom-color: var(--sv-accent); } +.gromlab-sprite-viewer__code { position: relative; overflow: hidden; background: var(--sv-code); border-radius: 8px; } +.gromlab-sprite-viewer__code pre { min-height: 72px; margin: 0; padding: 16px 70px 16px 16px; overflow-x: auto; font: 12px/1.6 ui-monospace, "SFMono-Regular", Consolas, monospace; } +.gromlab-sprite-viewer__copy { position: absolute; top: 8px; right: 8px; padding: 4px 8px; color: var(--sv-muted); background: var(--sv-bg); border: 1px solid var(--sv-border); border-radius: 4px; cursor: pointer; font-size: 11px; } +.gromlab-sprite-viewer__copy:hover { color: var(--sv-text); } +.gromlab-sprite-viewer__code .hl-tag { color: #116329; } +.gromlab-sprite-viewer__code .hl-attr, +.gromlab-sprite-viewer__code .hl-number, +.gromlab-sprite-viewer__code .hl-property { color: #0550ae; } +.gromlab-sprite-viewer__code .hl-string, +.gromlab-sprite-viewer__code .hl-color { color: #0a3069; } +.gromlab-sprite-viewer__code .hl-comment { color: #8b949e; } +.gromlab-sprite-viewer__code .hl-punctuation, +.gromlab-sprite-viewer__code .hl-selector { color: #6639ba; } +.gromlab-sprite-viewer[data-theme="dark"] .gromlab-sprite-viewer__code .hl-tag { color: #7ee787; } +.gromlab-sprite-viewer[data-theme="dark"] .gromlab-sprite-viewer__code .hl-attr, +.gromlab-sprite-viewer[data-theme="dark"] .gromlab-sprite-viewer__code .hl-number, +.gromlab-sprite-viewer[data-theme="dark"] .gromlab-sprite-viewer__code .hl-property { color: #79c0ff; } +.gromlab-sprite-viewer[data-theme="dark"] .gromlab-sprite-viewer__code .hl-string, +.gromlab-sprite-viewer[data-theme="dark"] .gromlab-sprite-viewer__code .hl-color { color: #a5d6ff; } +.gromlab-sprite-viewer[data-theme="dark"] .gromlab-sprite-viewer__code .hl-punctuation, +.gromlab-sprite-viewer[data-theme="dark"] .gromlab-sprite-viewer__code .hl-selector { color: #d2a8ff; } +@media (prefers-color-scheme: dark) { + .gromlab-sprite-viewer:not([data-theme]) .gromlab-sprite-viewer__code .hl-tag { color: #7ee787; } + .gromlab-sprite-viewer:not([data-theme]) .gromlab-sprite-viewer__code .hl-attr, + .gromlab-sprite-viewer:not([data-theme]) .gromlab-sprite-viewer__code .hl-number, + .gromlab-sprite-viewer:not([data-theme]) .gromlab-sprite-viewer__code .hl-property { color: #79c0ff; } + .gromlab-sprite-viewer:not([data-theme]) .gromlab-sprite-viewer__code .hl-string, + .gromlab-sprite-viewer:not([data-theme]) .gromlab-sprite-viewer__code .hl-color { color: #a5d6ff; } + .gromlab-sprite-viewer:not([data-theme]) .gromlab-sprite-viewer__code .hl-punctuation, + .gromlab-sprite-viewer:not([data-theme]) .gromlab-sprite-viewer__code .hl-selector { color: #d2a8ff; } +} +@media (max-width: 640px) { + .gromlab-sprite-viewer { padding: 16px; border-radius: 8px; } + .gromlab-sprite-viewer__header { align-items: stretch; } + .gromlab-sprite-viewer__toolbar { width: 100%; margin-left: 0; } + .gromlab-sprite-viewer__search { width: auto; flex: 1; min-width: 0; } + .gromlab-sprite-viewer__grid { grid-template-columns: repeat(auto-fill, minmax(132px, 1fr)); gap: 8px; } + .gromlab-sprite-viewer__icon-wrap, + .gromlab-sprite-viewer__icon { width: 104px; height: 104px; } + .gromlab-sprite-viewer__dialog { width: calc(100vw - 20px); max-height: calc(100dvh - 20px); } + .gromlab-sprite-viewer__dialog-shell { max-height: calc(100dvh - 20px); padding: 16px; } + .gromlab-sprite-viewer__dialog-preview { padding: 16px; } + .gromlab-sprite-viewer__dialog-preview-canvas, + .gromlab-sprite-viewer__dialog-icon, + .gromlab-sprite-viewer__dialog-img, + .gromlab-sprite-viewer__dialog-mask { width: min(256px, calc(100vw - 86px)); height: min(256px, calc(100vw - 86px)); } +} +@media (prefers-reduced-motion: reduce) { + .gromlab-sprite-viewer *, + .gromlab-sprite-viewer *::before, + .gromlab-sprite-viewer *::after { scroll-behavior: auto !important; transition-duration: .01ms !important; } +} +`,et="gromlab-sprite-viewer",be="gromlab-hex-color-picker",we="gromlab-hex-input",W;function Hr(){return customElements.get(be)&&customElements.get(we)?Promise.resolve():W||(W=Promise.all([Promise.resolve().then(()=>(Ft(),mt)),Promise.resolve().then(()=>(Wt(),_t))]).then(([{HexBase:e},{HexInputBase:t}])=>{customElements.get(be)||customElements.define(be,class extends e{}),customElements.get(we)||customElements.define(we,class extends t{})}),W)}function tt(e,t){const r=e%100,i=e%10,o=r>=11&&r<=19?t[2]:i===1?t[0]:i>=2&&i<=4?t[1]:t[2];return`${e} ${o}`}function Ur(){return globalThis.matchMedia?.("(prefers-color-scheme: dark)").matches?"dark":"light"}function rt(e,t){return Object.fromEntries(e.colors.map(({variable:r,fallback:i})=>[r,R(i,t)]))}var Nr=class extends U{static properties={sources:{attribute:!1},viewerTitle:{attribute:"viewer-title"},colorTheme:{attribute:"color-theme"},themeControlled:{attribute:!1},showThemeToggle:{attribute:!1},manifestUrl:{attribute:"manifest-url"},spriteUrl:{attribute:"sprite-url"},_manifests:{state:!0},_errors:{state:!0},_loading:{state:!0},_query:{state:!0},_systemTheme:{state:!0},_selected:{state:!0},_activeTab:{state:!0},_colors:{state:!0},_colorOverrides:{state:!0},_cssColor:{state:!0},_cssColorOverridden:{state:!0},_openColorControl:{state:!0},_copied:{state:!0}};static styles=wt(Pr);_loadVersion=0;_copyTimeout;_media;constructor(){super(),this.sources=[],this.viewerTitle="SVG Sprites",this.colorTheme="auto",this.themeControlled=!1,this.showThemeToggle=!0,this._manifests=[],this._errors=[],this._loading=!1,this._query="",this._systemTheme=Ur(),this._selected=null,this._activeTab="svg",this._colors={},this._colorOverrides={},this._cssColor="#1a1a1a",this._cssColorOverridden=!1,this._openColorControl=null,this._copied=!1}_handleMediaChange=()=>{this._systemTheme=this._media?.matches?"dark":"light",this._syncThemeColors()};_handleDocumentPointerDown=e=>{if(!this._openColorControl)return;e.composedPath().some(r=>r instanceof HTMLElement&&(r.dataset.colorTrigger===this._openColorControl||r.dataset.colorPopover===this._openColorControl))||(this._openColorControl=null)};connectedCallback(){super.connectedCallback(),Hr(),this._media=globalThis.matchMedia?.("(prefers-color-scheme: dark)"),this._systemTheme=this._media?.matches?"dark":"light",this._media?.addEventListener("change",this._handleMediaChange),globalThis.document?.addEventListener("pointerdown",this._handleDocumentPointerDown,!0)}disconnectedCallback(){this._media?.removeEventListener("change",this._handleMediaChange),globalThis.document?.removeEventListener("pointerdown",this._handleDocumentPointerDown,!0),this._copyTimeout&&clearTimeout(this._copyTimeout),super.disconnectedCallback()}updated(e){(e.has("sources")||e.has("manifestUrl")||e.has("spriteUrl"))&&this._loadSources(),e.has("colorTheme")&&this._syncThemeColors()}get _resolvedColorTheme(){return this.colorTheme==="auto"?this._systemTheme:this.colorTheme}get _themeColor(){return this._resolvedColorTheme==="dark"?"#e5e5e5":"#1a1a1a"}_effectiveSources(){const e=Xe(this.sources);return e.length>0?e:this.manifestUrl&&this.spriteUrl?[{manifestUrl:this.manifestUrl,spriteUrl:this.spriteUrl}]:[]}async _loadSources(){const e=++this._loadVersion,t=this._effectiveSources(),r=[],i=[],o=[];this.manifestUrl&&!this.spriteUrl&&Xe(this.sources).length===0&&o.push("The sprite-url attribute is required with manifest-url.");for(const n of t){if(typeof n=="function"||Dt(n)){i.push(n);continue}try{r.push(ee(n))}catch(l){o.push(l instanceof Error?l.message:String(l))}}if(this._manifests=r.sort(Qe),this._errors=o,this._loading=i.length>0,this._selected=null,i.length===0)return;const s=await Promise.allSettled(i.map(Or));if(e!==this._loadVersion)return;const a=[];for(const n of s)n.status==="fulfilled"?a.push(n.value):o.push(n.reason instanceof Error?n.reason.message:String(n.reason));this._manifests=[...r,...a].sort(Qe),this._errors=[...o],this._loading=!1}_syncThemeColors(){this._selected&&(this._colors={...rt(this._selected.icon,this._themeColor),...this._colorOverrides},this._cssColorOverridden||(this._cssColor=this._themeColor))}_toggleTheme(){const e=this._resolvedColorTheme==="dark"?"light":"dark";this.themeControlled||(this.colorTheme=e),this.dispatchEvent(new CustomEvent("color-theme-change",{bubbles:!0,composed:!0,detail:{theme:e}}))}async _selectIcon(e,t){const r=`${e.name}:${e.spriteUrl}:${t.id}`;this._selected={key:r,manifest:e,icon:t},this._activeTab=fe(e)[0]?.id??"svg",this._colorOverrides={},this._colors=rt(t,this._themeColor),this._cssColor=this._themeColor,this._cssColorOverridden=!1,this._openColorControl=null,this._copied=!1,await this.updateComplete;const i=this.renderRoot.querySelector("dialog");i&&!i.open&&i.showModal()}_closeDialog(){const e=this.renderRoot.querySelector("dialog");e?.open&&e.close(),this._selected=null,this._openColorControl=null}_handleBackdropClick(e){if(!(e.currentTarget instanceof HTMLDialogElement)||e.target!==e.currentTarget)return;const t=e.currentTarget.getBoundingClientRect();(e.clientXt.right||e.clientYt.bottom)&&this._closeDialog()}_handleTabKeyDown(e,t){if(!this._selected)return;const r=fe(this._selected.manifest);let i=null;if(e.key==="ArrowRight"&&(i=(t+1)%r.length),e.key==="ArrowLeft"&&(i=(t-1+r.length)%r.length),e.key==="Home"&&(i=0),e.key==="End"&&(i=r.length-1),i===null)return;e.preventDefault(),this._activeTab=r[i].id;const o=this.renderRoot.querySelectorAll('[role="tab"]');this.updateComplete.then(()=>o[i]?.focus())}_handleColorChange(e,t){const r=R(t);this._colors={...this._colors,[e]:r},this._colorOverrides={...this._colorOverrides,[e]:r}}_handleCssColorChange(e){this._cssColor=R(e),this._cssColorOverridden=!0}async _copyCode(e){if(globalThis.navigator?.clipboard)try{await globalThis.navigator.clipboard.writeText(e),this._copied=!0,this._copyTimeout&&clearTimeout(this._copyTimeout),this._copyTimeout=setTimeout(()=>{this._copied=!1},1500)}catch{this._copied=!1}}_renderColorControl(e,t,r,i){const o=this._openColorControl===r,s=`color-${r.replace(/[^a-zA-Z0-9_-]/g,"-")}`;return p` +
+ + ${e} + ${o?p` + + `:c} +
+ `}_renderDialogPreview(e,t){const r=`${e.spriteUrl}#${t.id}`;return this._activeTab==="img"?p`${t.name}`:this._activeTab==="css"?p` + + `:p` + + + + `}_renderDialog(){if(!this._selected)return c;const{manifest:e,icon:t}=this._selected,r=fe(e),i=r.some(n=>n.id===this._activeTab)?this._activeTab:r[0].id,o=yr({manifest:e,icon:t,tab:i,colorOverrides:this._colorOverrides,cssColor:this._cssColor}),s=wr(t.viewBox),a=i!=="img"&&i!=="css";return p` + {n.preventDefault(),this._closeDialog()}} + @click=${this._handleBackdropClick} + > +
+ + +
+
+ ${this._renderDialogPreview(e,t)} +
+
+ +
+

${t.name}

+ ${s?p`${s}`:c} +
+

${e.name} · ${e.format} · ${e.target}

+ +
+ ${a&&t.colors.length>0?p` +

+ Цвета применяются к превью через CSS-переменные и попадут в пример кода. +

+

CSS Variables

+ ${t.colors.map(({variable:n,fallback:l})=>this._renderColorControl(`${n}: ${l}`,this._colors[n],n,g=>this._handleColorChange(n,g)))} + `:c} + ${a&&t.colors.length===0?p` +

У иконки нет настраиваемых цветовых переменных.

+ `:c} + ${i==="img"?p` +

+ IMG изолирует SVG: CSS-переменные и currentColor внутрь изображения не передаются. +

+ `:c} + ${i==="css"?p` +

+ CSS mask отображает иконку одним цветом через background-color. +

+ ${this._renderColorControl("background-color",this._cssColor,"background-color",n=>this._handleCssColorChange(n))} + `:c} +
+ +
+ ${r.map((n,l)=>p` + + `)} +
+ +
+
${_r(Sr(o.code,o.language))}
+ +
+
+
+ `}render(){const e=this._query.trim().toLowerCase(),t=this._manifests.map(o=>({manifest:o,icons:o.icons.filter(s=>e===""||s.name.toLowerCase().includes(e)||o.name.toLowerCase().includes(e))})).filter(o=>o.icons.length>0),r=this._manifests.reduce((o,s)=>o+s.iconCount,0),i=t.reduce((o,s)=>o+s.icons.length,0);return p` +
+
+

${this.viewerTitle}

+ + ${tt(this._manifests.length,["спрайт","спрайта","спрайтов"])} + · ${tt(r,["иконка","иконки","иконок"])} + ${e?` · найдено ${i}`:c} + +
+ {this._query=o.currentTarget.value}} + placeholder="Найти иконку" + aria-label="Поиск иконок" + > + ${this.showThemeToggle?p` + + `:c} +
+
+ + ${this._errors.length>0?p` + + `:c} + + ${t.map(({manifest:o,icons:s})=>p` +
+
+

${o.name}

+ ${o.format} + ${s.length} + ${o.description?p` +

${o.description}

+ `:c} +
+
+ ${s.map(a=>p` + + `)} +
+
+ `)} + + ${t.length===0&&(!this._loading||this._manifests.length>0)?p` +
+ ${this._manifests.length===0?"Спрайты не подключены":"Иконки не найдены"} +
+ `:c} + ${this._loading&&this._manifests.length===0?p` +
Загрузка спрайтов...
+ `:c} + + + + ${this._renderDialog()} +
+ `}};function Dr(){customElements.get(et)||customElements.define(et,Nr)}Dr();/*! Bundled license information: + +@lit/reactive-element/css-tag.js: + (** + * @license + * Copyright 2019 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + *) + +@lit/reactive-element/reactive-element.js: +lit-html/lit-html.js: +lit-element/lit-element.js: +lit-html/directive.js: +lit-html/directives/unsafe-html.js: + (** + * @license + * Copyright 2017 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + *) + +lit-html/is-server.js: + (** + * @license + * Copyright 2022 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + *) + +lit-html/directives/if-defined.js: +lit-html/directives/style-map.js: + (** + * @license + * Copyright 2018 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + *) +*/export{Dr as defineSpriteViewerElement}; diff --git a/integration/apps/solid-start-vite/.vinxi/build/server-fns/_server/index.js b/integration/apps/solid-start-vite/.vinxi/build/server-fns/_server/index.js new file mode 100644 index 0000000..4712d99 --- /dev/null +++ b/integration/apps/solid-start-vite/.vinxi/build/server-fns/_server/index.js @@ -0,0 +1 @@ +import"solid-js/web";import"solid-js";import{H as p}from"./assets/index-AIk5O37p.js";export{p as default}; diff --git a/integration/apps/solid-start-vite/.vinxi/build/server-fns/_server/server-fns.js b/integration/apps/solid-start-vite/.vinxi/build/server-fns/_server/server-fns.js new file mode 100644 index 0000000..852231a --- /dev/null +++ b/integration/apps/solid-start-vite/.vinxi/build/server-fns/_server/server-fns.js @@ -0,0 +1 @@ +import{h as s}from"./assets/server-fns-XxvbTHGR.js";import"cookie-es";import"solid-js";import"solid-js/web";import"solid-js/web/storage";import"h3";import"unctx";import"node:async_hooks";import"radix3";import"seroval";import"seroval-plugins/web";export{s as default}; diff --git a/integration/apps/solid-start-vite/.vinxi/build/ssr/.vite/manifest.json b/integration/apps/solid-start-vite/.vinxi/build/ssr/.vite/manifest.json new file mode 100644 index 0000000..200df6d --- /dev/null +++ b/integration/apps/solid-start-vite/.vinxi/build/ssr/.vite/manifest.json @@ -0,0 +1,62 @@ +{ + "../../../dist/viewer-element.js": { + "file": "assets/viewer-element-BO6fulPI.js", + "name": "viewer-element", + "src": "../../../dist/viewer-element.js", + "isDynamicEntry": true + }, + "_index-BWUdTtCi.css": { + "file": "assets/index-BWUdTtCi.css", + "src": "_index-BWUdTtCi.css" + }, + "_index-CV0w8CD4.js": { + "file": "assets/index-CV0w8CD4.js", + "name": "index", + "isDynamicEntry": true, + "dynamicImports": [ + "../../../dist/viewer-element.js", + "src/sprite/.svg-sprite/svg-sprite.manifest.js" + ], + "css": [ + "assets/index-BWUdTtCi.css" + ], + "assets": [ + "assets/sprite-DvsqBNon.svg" + ] + }, + "src/routes/index.jsx?pick=default&pick=$css": { + "file": "index.js", + "name": "index", + "src": "src/routes/index.jsx?pick=default&pick=$css", + "isEntry": true, + "imports": [ + "_index-CV0w8CD4.js" + ] + }, + "src/sprite/.svg-sprite/sprite.svg": { + "file": "assets/sprite-DvsqBNon.svg", + "src": "src/sprite/.svg-sprite/sprite.svg" + }, + "src/sprite/.svg-sprite/svg-sprite.manifest.js": { + "file": "assets/svg-sprite.manifest-Di7XhmOO.js", + "name": "svg-sprite.manifest", + "src": "src/sprite/.svg-sprite/svg-sprite.manifest.js", + "isDynamicEntry": true, + "imports": [ + "_index-CV0w8CD4.js" + ] + }, + "virtual:$vinxi/handler/ssr": { + "file": "ssr.js", + "name": "ssr", + "src": "virtual:$vinxi/handler/ssr", + "isEntry": true, + "dynamicImports": [ + "src/routes/index.jsx?pick=default&pick=$css", + "src/routes/index.jsx?pick=default&pick=$css" + ], + "css": [ + "assets/ssr-BVONibDl.css" + ] + } +} \ No newline at end of file diff --git a/integration/apps/solid-start-vite/.vinxi/build/ssr/assets/index-BWUdTtCi.css b/integration/apps/solid-start-vite/.vinxi/build/ssr/assets/index-BWUdTtCi.css new file mode 100644 index 0000000..013ec25 --- /dev/null +++ b/integration/apps/solid-start-vite/.vinxi/build/ssr/assets/index-BWUdTtCi.css @@ -0,0 +1 @@ +._root_1egzq_3{transition-property:fill,stroke,color;transition-duration:.3s;transition-timing-function:ease}._wrap_1egzq_9{display:inline-flex}._wrap_1egzq_9 svg{width:100%;height:100%;transition-property:fill,stroke,color;transition-duration:.3s;transition-timing-function:ease} diff --git a/integration/apps/solid-start-vite/.vinxi/build/ssr/assets/index-CV0w8CD4.js b/integration/apps/solid-start-vite/.vinxi/build/ssr/assets/index-CV0w8CD4.js new file mode 100644 index 0000000..d827752 --- /dev/null +++ b/integration/apps/solid-start-vite/.vinxi/build/ssr/assets/index-CV0w8CD4.js @@ -0,0 +1 @@ +import{createComponent as p,ssrElement as n,mergeProps as i,ssr as r,ssrAttribute as c,escape as s,ssrHydrationKey as u}from"solid-js/web";import{splitProps as m,Show as d,onMount as w}from"solid-js";const h="_root_1egzq_3",v="_wrap_1egzq_9",l={root:h,wrap:v},g={check:"check"},f="/_build/assets/sprite-DvsqBNon.svg";var _=[""],b=[""];const k=t=>{const[e,o]=m(t,["icon","wrapped","class"]),a=()=>f+"#"+g[e.icon];return p(d,{get when(){return e.wrapped},get fallback(){return n("svg",i(o,{get class(){return[l.root,e.class].filter(Boolean).join(" ")}}),()=>r(b,c("href",s(a(),!0),!1)),!0)},get children(){return n("span",i(o,{get class(){return[l.wrap,e.class].filter(Boolean).join(" ")}}),()=>r(_,c("href",s(a(),!0),!1)),!0)}})};var y=["

SolidStart + Vite

",'
'];function H(){let t;return w(async()=>{await import("./viewer-element-BO6fulPI.js");const e=document.createElement("gromlab-sprite-viewer");e.viewerTitle="SolidStart Vite Viewer",e.sources=[()=>import("./svg-sprite.manifest-Di7XhmOO.js")],t.append(e)}),r(y,u(),s(p(k,{"data-testid":"icon","data-app":"solid-start-vite",icon:"check","aria-label":"Check icon",width:64,height:64,style:{"--icon-color-1":"#16a34a"}})))}export{H,f as s}; diff --git a/integration/apps/solid-start-vite/.vinxi/build/ssr/assets/sprite-DvsqBNon.svg b/integration/apps/solid-start-vite/.vinxi/build/ssr/assets/sprite-DvsqBNon.svg new file mode 100644 index 0000000..bd5742e --- /dev/null +++ b/integration/apps/solid-start-vite/.vinxi/build/ssr/assets/sprite-DvsqBNon.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/integration/apps/solid-start-vite/.vinxi/build/ssr/assets/ssr-BVONibDl.css b/integration/apps/solid-start-vite/.vinxi/build/ssr/assets/ssr-BVONibDl.css new file mode 100644 index 0000000..b9cce31 --- /dev/null +++ b/integration/apps/solid-start-vite/.vinxi/build/ssr/assets/ssr-BVONibDl.css @@ -0,0 +1 @@ +:root{font-family:system-ui,sans-serif;color:#172033;background:#fff}body{margin:0;padding:40px}.viewer{margin-top:32px} diff --git a/integration/apps/solid-start-vite/.vinxi/build/ssr/assets/svg-sprite.manifest-Di7XhmOO.js b/integration/apps/solid-start-vite/.vinxi/build/ssr/assets/svg-sprite.manifest-Di7XhmOO.js new file mode 100644 index 0000000..3516d62 --- /dev/null +++ b/integration/apps/solid-start-vite/.vinxi/build/ssr/assets/svg-sprite.manifest-Di7XhmOO.js @@ -0,0 +1 @@ +import{s as o}from"./index-CV0w8CD4.js";import"solid-js/web";import"solid-js";const s={schemaVersion:1,generator:"@gromlab/svg-sprites",name:"icons",usage:{framework:"solid-start",componentName:"IconsIcon",ssr:!0},mode:"solid-start@vite",target:"vite",format:"stack",iconCount:1,spriteUrl:o,icons:[{name:"check",id:"check",viewBox:"0 0 24 24",colors:[{variable:"--icon-color-1",fallback:"currentColor"}]}]};export{s as default,s as spriteManifest}; diff --git a/integration/apps/solid-start-vite/.vinxi/build/ssr/assets/viewer-element-BO6fulPI.js b/integration/apps/solid-start-vite/.vinxi/build/ssr/assets/viewer-element-BO6fulPI.js new file mode 100644 index 0000000..a96cb71 --- /dev/null +++ b/integration/apps/solid-start-vite/.vinxi/build/ssr/assets/viewer-element-BO6fulPI.js @@ -0,0 +1,602 @@ +var Rt=Object.defineProperty,Lt=Object.getOwnPropertyNames,m=(e,t,r)=>function(){if(r)throw r[0];try{return e&&(t=(0,e[Lt(e)[0]])(e=0)),t}catch(o){throw r=[o],o}},it=(e,t)=>{for(var r in t)Rt(e,r,{get:t[r],enumerable:!0})},E,u,te=m({"node_modules/vanilla-colorful/lib/utils/math.js"(){E=(e,t=0,r=1)=>e>r?r:eMath.round(r*e)/r}}),ot,Y,st,Pe,Z,He,S,Ue,Ne,re=m({"node_modules/vanilla-colorful/lib/utils/convert.js"(){te(),ot=e=>Ne(Y(e)),Y=e=>(e[0]==="#"&&(e=e.substring(1)),e.length<6?{r:parseInt(e[0]+e[0],16),g:parseInt(e[1]+e[1],16),b:parseInt(e[2]+e[2],16),a:e.length===4?u(parseInt(e[3]+e[3],16)/255,2):1}:{r:parseInt(e.substring(0,2),16),g:parseInt(e.substring(2,4),16),b:parseInt(e.substring(4,6),16),a:e.length===8?u(parseInt(e.substring(6,8),16)/255,2):1}),st=e=>Ue(He(e)),Pe=({h:e,s:t,v:r,a:i})=>{const o=(200-t)*r/100;return{h:u(e),s:u(o>0&&o<200?t*r/100/(o<=100?o:200-o)*100:0),l:u(o/2),a:u(i,2)}},Z=e=>{const{h:t,s:r,l:i}=Pe(e);return`hsl(${t}, ${r}%, ${i}%)`},He=({h:e,s:t,v:r,a:i})=>{e=e/360*6,t=t/100,r=r/100;const o=Math.floor(e),s=r*(1-t),a=r*(1-(e-o)*t),n=r*(1-(1-e+o)*t),l=o%6;return{r:u([r,a,s,s,n,r][l]*255),g:u([n,r,r,a,s,s][l]*255),b:u([s,s,n,r,r,a][l]*255),a:u(i,2)}},S=e=>{const t=e.toString(16);return t.length<2?"0"+t:t},Ue=({r:e,g:t,b:r,a:i})=>{const o=i<1?S(u(i*255)):"";return"#"+S(e)+S(t)+S(r)+o},Ne=({r:e,g:t,b:r,a:i})=>{const o=Math.max(e,t,r),s=o-Math.min(e,t,r),a=s?o===e?(t-r)/s:o===t?2+(r-e)/s:4+(e-t)/s:0;return{h:u(60*(a<0?a+6:a)),s:u(o?s/o*100:0),v:u(o/255*100),a:i}}}}),$e,at,nt=m({"node_modules/vanilla-colorful/lib/utils/compare.js"(){re(),$e=(e,t)=>{if(e===t)return!0;for(const r in e)if(e[r]!==t[r])return!1;return!0},at=(e,t)=>e.toLowerCase()===t.toLowerCase()?!0:$e(Y(e),Y(t))}}),ae,ie,X,Ae=m({"node_modules/vanilla-colorful/lib/utils/dom.js"(){ae={},ie=e=>{let t=ae[e];return t||(t=document.createElement("template"),t.innerHTML=e,ae[e]=t),t},X=(e,t,r)=>{e.dispatchEvent(new CustomEvent(t,{bubbles:!0,detail:r}))}}}),w,z,De,ne,Re,Ee,lt=m({"node_modules/vanilla-colorful/lib/components/slider.js"(){Ae(),te(),w=!1,z=e=>"touches"in e,De=e=>w&&!z(e)?!1:(w||(w=z(e)),!0),ne=(e,t)=>{const r=z(t)?t.touches[0]:t,i=e.el.getBoundingClientRect();X(e.el,"move",e.getMove({x:E((r.pageX-(i.left+window.pageXOffset))/i.width),y:E((r.pageY-(i.top+window.pageYOffset))/i.height)}))},Re=(e,t)=>{const r=t.keyCode;r>40||e.xy&&r<37||r<33||(t.preventDefault(),X(e.el,"move",e.getMove({x:r===39?.01:r===37?-.01:r===34?.05:r===33?-.05:r===35?1:r===36?-1:0,y:r===40?.01:r===38?-.01:0},!0)))},Ee=class{constructor(e,t,r,i){const o=ie(`
`);e.appendChild(o.content.cloneNode(!0));const s=e.querySelector(`[part=${t}]`);s.addEventListener("mousedown",this),s.addEventListener("touchstart",this),s.addEventListener("keydown",this),this.el=s,this.xy=i,this.nodes=[s.firstChild,s]}set dragging(e){const t=e?document.addEventListener:document.removeEventListener;t(w?"touchmove":"mousemove",this),t(w?"touchend":"mouseup",this)}handleEvent(e){switch(e.type){case"mousedown":case"touchstart":if(e.preventDefault(),!De(e)||!w&&e.button!=0)return;this.el.focus(),ne(this,e),this.dragging=!0;break;case"mousemove":case"touchmove":e.preventDefault(),ne(this,e);break;case"mouseup":case"touchend":this.dragging=!1;break;case"keydown":Re(this,e);break}}style(e){e.forEach((t,r)=>{for(const i in t)this.nodes[r].style.setProperty(i,t[i])})}}}}),ct,jt=m({"node_modules/vanilla-colorful/lib/components/hue.js"(){lt(),re(),te(),ct=class extends Ee{constructor(e){super(e,"hue",'aria-label="Hue" aria-valuemin="0" aria-valuemax="360"',!1)}update({h:e}){this.h=e,this.style([{left:`${e/360*100}%`,color:Z({h:e,s:100,v:100,a:1})}]),this.el.setAttribute("aria-valuenow",`${u(e)}`)}getMove(e,t){return{h:t?E(this.h+e.x*360,0,360):360*e.x}}}}}),dt,zt=m({"node_modules/vanilla-colorful/lib/components/saturation.js"(){lt(),re(),te(),dt=class extends Ee{constructor(e){super(e,"saturation",'aria-label="Color"',!0)}update(e){this.hsva=e,this.style([{top:`${100-e.v}%`,left:`${e.s}%`,color:Z(e)},{"background-color":Z({h:e.h,s:100,v:100,a:1})}]),this.el.setAttribute("aria-valuetext",`Saturation ${u(e.s)}%, Brightness ${u(e.v)}%`)}getMove(e,t){return{s:t?E(this.hsva.s+e.x*100,0,100):e.x*100,v:t?E(this.hsva.v-e.y*100,0,100):Math.round(100-e.y*100)}}}}}),ht,Bt=m({"node_modules/vanilla-colorful/lib/styles/color-picker.js"(){ht=':host{display:flex;flex-direction:column;position:relative;width:200px;height:200px;user-select:none;-webkit-user-select:none;cursor:default}:host([hidden]){display:none!important}[role=slider]{position:relative;touch-action:none;user-select:none;-webkit-user-select:none;outline:0}[role=slider]:last-child{border-radius:0 0 8px 8px}[part$=pointer]{position:absolute;z-index:1;box-sizing:border-box;width:28px;height:28px;display:flex;place-content:center center;transform:translate(-50%,-50%);background-color:#fff;border:2px solid #fff;border-radius:50%;box-shadow:0 2px 4px rgba(0,0,0,.2)}[part$=pointer]::after{content:"";width:100%;height:100%;border-radius:inherit;background-color:currentColor}[role=slider]:focus [part$=pointer]{transform:translate(-50%,-50%) scale(1.1)}'}}),pt,It=m({"node_modules/vanilla-colorful/lib/styles/hue.js"(){pt="[part=hue]{flex:0 0 24px;background:linear-gradient(to right,red 0,#ff0 17%,#0f0 33%,#0ff 50%,#00f 67%,#f0f 83%,red 100%)}[part=hue-pointer]{top:50%;z-index:2}"}}),ut,Vt=m({"node_modules/vanilla-colorful/lib/styles/saturation.js"(){ut="[part=saturation]{flex-grow:1;border-color:transparent;border-bottom:12px solid #000;border-radius:8px 8px 0 0;background-image:linear-gradient(to top,#000,transparent),linear-gradient(to right,#fff,rgba(255,255,255,0));box-shadow:inset 0 0 0 1px rgba(0,0,0,.05)}[part=saturation-pointer]{z-index:3}"}}),T,B,le,I,ce,de,he,gt,qt=m({"node_modules/vanilla-colorful/lib/components/color-picker.js"(){nt(),Ae(),jt(),zt(),Bt(),It(),Vt(),T=Symbol("same"),B=Symbol("color"),le=Symbol("hsva"),I=Symbol("update"),ce=Symbol("parts"),de=Symbol("css"),he=Symbol("sliders"),gt=class extends HTMLElement{static get observedAttributes(){return["color"]}get[de](){return[ht,pt,ut]}get[he](){return[dt,ct]}get color(){return this[B]}set color(e){if(!this[T](e)){const t=this.colorModel.toHsva(e);this[I](t),this[B]=e}}constructor(){super();const e=ie(``),t=this.attachShadow({mode:"open"});t.appendChild(e.content.cloneNode(!0)),t.addEventListener("move",this),this[ce]=this[he].map(r=>new r(t))}connectedCallback(){if(this.hasOwnProperty("color")){const e=this.color;delete this.color,this.color=e}else this.color||(this.color=this.colorModel.defaultColor)}attributeChangedCallback(e,t,r){const i=this.colorModel.fromAttr(r);this[T](i)||(this.color=i)}handleEvent(e){const t=this[le],r={...t,...e.detail};this[I](r);let i;!$e(r,t)&&!this[T](i=this.colorModel.fromHsva(r))&&(this[B]=i,X(this,"color-changed",{value:i}))}[T](e){return this.color&&this.colorModel.equal(e,this.color)}[I](e){this[le]=e,this[ce].forEach(t=>t.update(e))}}}}),mt={};it(mt,{HexBase:()=>vt});var Le,vt,Ft=m({"node_modules/vanilla-colorful/lib/entrypoints/hex.js"(){qt(),re(),nt(),Le={defaultColor:"#000",toHsva:ot,fromHsva:({h:e,s:t,v:r})=>st({h:e,s:t,v:r,a:1}),equal:at,fromAttr:e=>e},vt=class extends gt{get colorModel(){return Le}}}}),je,G,Jt=m({"node_modules/vanilla-colorful/lib/utils/validate.js"(){je=/^#?([0-9A-F]{3,8})$/i,G=(e,t)=>{const r=je.exec(e),i=r?r[1].length:0;return i===3||i===6||!!t&&i===4||!!t&&i===8}}}),_t={};it(_t,{HexInputBase:()=>ft});var ze,pe,ue,M,ge,V,q,me,C,ft,Wt=m({"node_modules/vanilla-colorful/lib/entrypoints/hex-input.js"(){Jt(),Ae(),ze=ie(''),pe=(e,t)=>e.replace(/([^0-9A-F]+)/gi,"").substring(0,t?8:6),ue=Symbol("alpha"),M=Symbol("color"),ge=Symbol("saved"),V=Symbol("input"),q=Symbol("init"),me=Symbol("prefix"),C=Symbol("update"),ft=class extends HTMLElement{static get observedAttributes(){return["alpha","color","prefixed"]}get color(){return this[M]}set color(e){this[M]=e,this[C](e)}get alpha(){return this[ue]}set alpha(e){this[ue]=e,this.toggleAttribute("alpha",e);const t=this.color;t&&!G(t,e)&&(this.color=t.startsWith("#")?t.substring(0,t.length===5?4:7):t.substring(0,t.length===4?3:6))}get prefixed(){return this[me]}set prefixed(e){this[me]=e,this.toggleAttribute("prefixed",e),this[C](this.color)}constructor(){super();const e=this.attachShadow({mode:"open"});e.appendChild(ze.content.cloneNode(!0)),e.firstElementChild.addEventListener("slotchange",()=>this[q](e))}connectedCallback(){if(this[q](this.shadowRoot),this.hasOwnProperty("alpha")){const e=this.alpha;delete this.alpha,this.alpha=e}else this.alpha=this.hasAttribute("alpha");if(this.hasOwnProperty("prefixed")){const e=this.prefixed;delete this.prefixed,this.prefixed=e}else this.prefixed=this.hasAttribute("prefixed");if(this.hasOwnProperty("color")){const e=this.color;delete this.color,this.color=e}else this.color==null?this.color=this.getAttribute("color")||"":this[M]&&this[C](this[M])}handleEvent(e){const t=e.target,{value:r}=t;switch(e.type){case"input":const i=pe(r,this.alpha);this[ge]=this.color,(G(i,this.alpha)||r==="")&&(this.color=i,this.dispatchEvent(new CustomEvent("color-changed",{bubbles:!0,detail:{value:i?"#"+i:""}})));break;case"blur":r&&!G(r,this.alpha)&&(this.color=this[ge])}}attributeChangedCallback(e,t,r){e==="color"&&this.color!==r&&(this.color=r);const i=r!=null;e==="alpha"&&this.alpha!==i&&(this.alpha=i),e==="prefixed"&&this.prefixed!==i&&(this.prefixed=i)}[q](e){let t=this.querySelector("input");if(!t){let r;for(;r=this.firstChild;)r.remove();t=e.querySelector("input")}t.addEventListener("input",this),t.addEventListener("blur",this),this[V]=t,this[C](this.color)}[C](e){this[V]&&(this[V].value=e==null||e==""?"":(this.prefixed?"#":"")+pe(e,this.alpha))}}}}),K=globalThis,ke=K.ShadowRoot&&(K.ShadyCSS===void 0||K.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,bt=Symbol(),Be=new WeakMap,Gt=class{constructor(e,t,r){if(this._$cssResult$=!0,r!==bt)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=e,this.t=t}get styleSheet(){let e=this.o;const t=this.t;if(ke&&e===void 0){const r=t!==void 0&&t.length===1;r&&(e=Be.get(t)),e===void 0&&((this.o=e=new CSSStyleSheet).replaceSync(this.cssText),r&&Be.set(t,e))}return e}toString(){return this.cssText}},wt=e=>new Gt(typeof e=="string"?e:e+"",void 0,bt),Kt=(e,t)=>{if(ke)e.adoptedStyleSheets=t.map(r=>r instanceof CSSStyleSheet?r:r.styleSheet);else for(const r of t){const i=document.createElement("style"),o=K.litNonce;o!==void 0&&i.setAttribute("nonce",o),i.textContent=r.cssText,e.appendChild(i)}},Ie=ke?e=>e:e=>e instanceof CSSStyleSheet?(t=>{let r="";for(const i of t.cssRules)r+=i.cssText;return wt(r)})(e):e,{is:Yt,defineProperty:Zt,getOwnPropertyDescriptor:Xt,getOwnPropertyNames:Qt,getOwnPropertySymbols:er,getPrototypeOf:tr}=Object,oe=globalThis,Ve=oe.trustedTypes,rr=Ve?Ve.emptyScript:"",ir=oe.reactiveElementPolyfillSupport,H=(e,t)=>e,xe={toAttribute(e,t){switch(t){case Boolean:e=e?rr:null;break;case Object:case Array:e=e==null?e:JSON.stringify(e)}return e},fromAttribute(e,t){let r=e;switch(t){case Boolean:r=e!==null;break;case Number:r=e===null?null:Number(e);break;case Object:case Array:try{r=JSON.parse(e)}catch{r=null}}return r}},$t=(e,t)=>!Yt(e,t),qe={attribute:!0,type:String,converter:xe,reflect:!1,useDefault:!1,hasChanged:$t};Symbol.metadata??=Symbol("metadata"),oe.litPropertyMetadata??=new WeakMap;var A=class extends HTMLElement{static addInitializer(e){this._$Ei(),(this.l??=[]).push(e)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(e,t=qe){if(t.state&&(t.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(e)&&((t=Object.create(t)).wrapped=!0),this.elementProperties.set(e,t),!t.noAccessor){const r=Symbol(),i=this.getPropertyDescriptor(e,r,t);i!==void 0&&Zt(this.prototype,e,i)}}static getPropertyDescriptor(e,t,r){const{get:i,set:o}=Xt(this.prototype,e)??{get(){return this[t]},set(s){this[t]=s}};return{get:i,set(s){const a=i?.call(this);o?.call(this,s),this.requestUpdate(e,a,r)},configurable:!0,enumerable:!0}}static getPropertyOptions(e){return this.elementProperties.get(e)??qe}static _$Ei(){if(this.hasOwnProperty(H("elementProperties")))return;const e=tr(this);e.finalize(),e.l!==void 0&&(this.l=[...e.l]),this.elementProperties=new Map(e.elementProperties)}static finalize(){if(this.hasOwnProperty(H("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(H("properties"))){const t=this.properties,r=[...Qt(t),...er(t)];for(const i of r)this.createProperty(i,t[i])}const e=this[Symbol.metadata];if(e!==null){const t=litPropertyMetadata.get(e);if(t!==void 0)for(const[r,i]of t)this.elementProperties.set(r,i)}this._$Eh=new Map;for(const[t,r]of this.elementProperties){const i=this._$Eu(t,r);i!==void 0&&this._$Eh.set(i,t)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(e){const t=[];if(Array.isArray(e)){const r=new Set(e.flat(1/0).reverse());for(const i of r)t.unshift(Ie(i))}else e!==void 0&&t.push(Ie(e));return t}static _$Eu(e,t){const r=t.attribute;return r===!1?void 0:typeof r=="string"?r:typeof e=="string"?e.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(e=>this.enableUpdating=e),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(e=>e(this))}addController(e){(this._$EO??=new Set).add(e),this.renderRoot!==void 0&&this.isConnected&&e.hostConnected?.()}removeController(e){this._$EO?.delete(e)}_$E_(){const e=new Map,t=this.constructor.elementProperties;for(const r of t.keys())this.hasOwnProperty(r)&&(e.set(r,this[r]),delete this[r]);e.size>0&&(this._$Ep=e)}createRenderRoot(){const e=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return Kt(e,this.constructor.elementStyles),e}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(e=>e.hostConnected?.())}enableUpdating(e){}disconnectedCallback(){this._$EO?.forEach(e=>e.hostDisconnected?.())}attributeChangedCallback(e,t,r){this._$AK(e,r)}_$ET(e,t){const r=this.constructor.elementProperties.get(e),i=this.constructor._$Eu(e,r);if(i!==void 0&&r.reflect===!0){const o=(r.converter?.toAttribute!==void 0?r.converter:xe).toAttribute(t,r.type);this._$Em=e,o==null?this.removeAttribute(i):this.setAttribute(i,o),this._$Em=null}}_$AK(e,t){const r=this.constructor,i=r._$Eh.get(e);if(i!==void 0&&this._$Em!==i){const o=r.getPropertyOptions(i),s=typeof o.converter=="function"?{fromAttribute:o.converter}:o.converter?.fromAttribute!==void 0?o.converter:xe;this._$Em=i;const a=s.fromAttribute(t,o.type);this[i]=a??this._$Ej?.get(i)??a,this._$Em=null}}requestUpdate(e,t,r,i=!1,o){if(e!==void 0){const s=this.constructor;if(i===!1&&(o=this[e]),r??=s.getPropertyOptions(e),!((r.hasChanged??$t)(o,t)||r.useDefault&&r.reflect&&o===this._$Ej?.get(e)&&!this.hasAttribute(s._$Eu(e,r))))return;this.C(e,t,r)}this.isUpdatePending===!1&&(this._$ES=this._$EP())}C(e,t,{useDefault:r,reflect:i,wrapped:o},s){r&&!(this._$Ej??=new Map).has(e)&&(this._$Ej.set(e,s??t??this[e]),o!==!0||s!==void 0)||(this._$AL.has(e)||(this.hasUpdated||r||(t=void 0),this._$AL.set(e,t)),i===!0&&this._$Em!==e&&(this._$Eq??=new Set).add(e))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(t){Promise.reject(t)}const e=this.scheduleUpdate();return e!=null&&await e,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(const[i,o]of this._$Ep)this[i]=o;this._$Ep=void 0}const r=this.constructor.elementProperties;if(r.size>0)for(const[i,o]of r){const{wrapped:s}=o,a=this[i];s!==!0||this._$AL.has(i)||a===void 0||this.C(i,void 0,o,a)}}let e=!1;const t=this._$AL;try{e=this.shouldUpdate(t),e?(this.willUpdate(t),this._$EO?.forEach(r=>r.hostUpdate?.()),this.update(t)):this._$EM()}catch(r){throw e=!1,this._$EM(),r}e&&this._$AE(t)}willUpdate(e){}_$AE(e){this._$EO?.forEach(t=>t.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(e)),this.updated(e)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(e){return!0}update(e){this._$Eq&&=this._$Eq.forEach(t=>this._$ET(t,this[t])),this._$EM()}updated(e){}firstUpdated(e){}};A.elementStyles=[],A.shadowRootOptions={mode:"open"},A[H("elementProperties")]=new Map,A[H("finalized")]=new Map,ir?.({ReactiveElement:A}),(oe.reactiveElementVersions??=[]).push("2.1.2");var Se=globalThis,Fe=e=>e,Q=Se.trustedTypes,Je=Q?Q.createPolicy("lit-html",{createHTML:e=>e}):void 0,xt="$lit$",f=`lit$${Math.random().toFixed(9).slice(2)}$`,yt="?"+f,or=`<${yt}>`,y=document,N=()=>y.createComment(""),D=e=>e===null||typeof e!="object"&&typeof e!="function",Te=Array.isArray,sr=e=>Te(e)||typeof e?.[Symbol.iterator]=="function",ve=`[ +\f\r]`,O=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,We=/-->/g,Ge=/>/g,$=RegExp(`>|${ve}(?:([^\\s"'>=/]+)(${ve}*=${ve}*(?:[^ +\f\r"'\`<>=]|("|')|))|$)`,"g"),Ke=/'/g,Ye=/"/g,Ct=/^(?:script|style|textarea|title)$/i,ar=e=>(t,...r)=>({_$litType$:e,strings:t,values:r}),p=ar(1),b=Symbol.for("lit-noChange"),c=Symbol.for("lit-nothing"),Ze=new WeakMap,x=y.createTreeWalker(y,129);function At(e,t){if(!Te(e)||!e.hasOwnProperty("raw"))throw Error("invalid template strings array");return Je!==void 0?Je.createHTML(t):t}var nr=(e,t)=>{const r=e.length-1,i=[];let o,s=t===2?"":t===3?"":"",a=O;for(let n=0;n"?(a=o??O,h=-1):d[1]===void 0?h=-2:(h=a.lastIndex-d[2].length,g=d[1],a=d[3]===void 0?$:d[3]==='"'?Ye:Ke):a===Ye||a===Ke?a=$:a===We||a===Ge?a=O:(a=$,o=void 0);const _=a===$&&e[n+1].startsWith("/>")?" ":"";s+=a===O?l+or:h>=0?(i.push(g),l.slice(0,h)+xt+l.slice(h)+f+_):l+f+(h===-2?n:_)}return[At(e,s+(e[r]||"")+(t===2?"":t===3?"":"")),i]},ye=class Et{constructor({strings:t,_$litType$:r},i){let o;this.parts=[];let s=0,a=0;const n=t.length-1,l=this.parts,[g,d]=nr(t,r);if(this.el=Et.createElement(g,i),x.currentNode=this.el.content,r===2||r===3){const h=this.el.content.firstChild;h.replaceWith(...h.childNodes)}for(;(o=x.nextNode())!==null&&l.length0){o.textContent=Q?Q.emptyScript:"";for(let _=0;_2||r[0]!==""||r[1]!==""?(this._$AH=Array(r.length-1).fill(new String),this.strings=r):this._$AH=c}_$AI(e,t=this,r,i){const o=this.strings;let s=!1;if(o===void 0)e=k(this,e,t,0),s=!D(e)||e!==this._$AH&&e!==b,s&&(this._$AH=e);else{const a=e;let n,l;for(e=o[0],n=0;n{const i=r?.renderBefore??t;let o=i._$litPart$;if(o===void 0){const s=r?.renderBefore??null;i._$litPart$=o=new Me(t.insertBefore(N(),s),s,void 0,r??{})}return o._$AI(e),o},Oe=globalThis,U=class extends A{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){const e=super.createRenderRoot();return this.renderOptions.renderBefore??=e.firstChild,e}update(e){const t=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(e),this._$Do=gr(t,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return b}};U._$litElement$=!0,U.finalized=!0,Oe.litElementHydrateSupport?.({LitElement:U});var mr=Oe.litElementPolyfillSupport;mr?.({LitElement:U});(Oe.litElementVersions??=[]).push("4.2.2");var F=e=>e??c,St={ATTRIBUTE:1,CHILD:2},Tt=e=>(...t)=>({_$litDirective$:e,values:t}),Mt=class{constructor(e){}get _$AU(){return this._$AM._$AU}_$AT(e,t,r){this._$Ct=e,this._$AM=t,this._$Ci=r}_$AS(e,t){return this.update(e,t)}update(e,t){return this.render(...t)}},Ot="important",vr=" !"+Ot,_e=Tt(class extends Mt{constructor(e){if(super(e),e.type!==St.ATTRIBUTE||e.name!=="style"||e.strings?.length>2)throw Error("The `styleMap` directive must be used in the `style` attribute and must be the only part in the attribute.")}render(e){return Object.keys(e).reduce((t,r)=>{const i=e[r];return i==null?t:t+`${r=r.includes("-")?r:r.replace(/(?:^(webkit|moz|ms|o)|)(?=[A-Z])/g,"-$&").toLowerCase()}:${i};`},"")}update(e,[t]){const{style:r}=e.element;if(this.ft===void 0)return this.ft=new Set(Object.keys(t)),this.render(t);for(const i of this.ft)t[i]==null&&(this.ft.delete(i),i.includes("-")?r.removeProperty(i):r[i]=null);for(const i in t){const o=t[i];if(o!=null){this.ft.add(i);const s=typeof o=="string"&&o.endsWith(vr);i.includes("-")||s?r.setProperty(i,s?o.slice(0,-11):o,s?Ot:""):r[i]=o}}return b}}),Ce=class extends Mt{constructor(e){if(super(e),this.it=c,e.type!==St.CHILD)throw Error(this.constructor.directiveName+"() can only be used in child bindings")}render(e){if(e===c||e==null)return this._t=void 0,this.it=e;if(e===b)return e;if(typeof e!="string")throw Error(this.constructor.directiveName+"() called with a non-string value");if(e===this.it)return this._t;this.it=e;const t=[e];return t.raw=t,this._t={_$litType$:this.constructor.resultType,strings:t,values:[]}}};Ce.directiveName="unsafeHTML",Ce.resultType=1;var _r=Tt(Ce),fr=[{id:"svg",label:"SVG"},{id:"img",label:"IMG"},{id:"css",label:"CSS"}],br={react:"React",vue:"Vue",svelte:"Svelte",angular:"Angular",astro:"Astro",solid:"Solid","solid-start":"SolidStart",preact:"Preact",qwik:"Qwik",lit:"Lit",alpine:"Alpine"};function Pt(e){return e.usage?e.usage:e.componentName?{framework:"react",componentName:e.componentName}:null}function fe(e){const t=Pt(e),i=[...t?[{id:t.framework,label:br[t.framework]}]:[],...fr];return e.format==="stack"?i:i.filter(o=>o.id!=="img"&&o.id!=="css")}function wr(e){if(!e)return null;const t=e.trim().split(/[\s,]+/).map(Number);return t.length===4&&t.every(Number.isFinite)?`${t[2]} × ${t[3]}`:null}function R(e,t="#1a1a1a"){const r=e.trim().toLowerCase();if(r==="currentcolor")return R(t);const i=r.match(/^#([\da-f])([\da-f])([\da-f])(?:[\da-f])?$/i);if(i)return`#${i[1]}${i[1]}${i[2]}${i[2]}${i[3]}${i[3]}`;const o=r.match(/^#([\da-f]{6})(?:[\da-f]{2})?$/i);if(o)return`#${o[1]}`;const s=r.match(/^rgba?\(\s*(\d+)\s*[, ]\s*(\d+)\s*[, ]\s*(\d+)/);return s?`#${s.slice(1,4).map(n=>Math.min(255,Number(n)).toString(16).padStart(2,"0")).join("")}`:{black:"#000000",blue:"#0000ff",cyan:"#00ffff",gray:"#808080",green:"#008000",grey:"#808080",magenta:"#ff00ff",orange:"#ffa500",pink:"#ffc0cb",purple:"#800080",red:"#ff0000",white:"#ffffff",yellow:"#ffff00"}[r]??R(t===e?"#1a1a1a":t)}function $r(e){return Object.entries(e).map(([t,r])=>` ${JSON.stringify(t)}: ${JSON.stringify(r)},`)}function P(e){const t={"&":"&",'"':""","<":"<",">":">"};return e.replace(/[&"<>]/g,r=>t[r])}function xr(e){return e.replace(/\\/g,"\\\\").replace(/'/g,"\\'")}function yr(e){const{manifest:t,icon:r,tab:i,colorOverrides:o,cssColor:s}=e,a=`${t.spriteUrl}#${r.id}`,n=$r(o);if(!["svg","img","css"].includes(i)){const d=Pt(t);if(!d)throw new Error(`The ${i} code tab requires component metadata.`);if(d.framework==="alpine")return{code:``,language:"html"};if(d.framework==="lit")return{code:`<${d.tagName} icon=${JSON.stringify(r.name)}>`,language:"html"};if(d.framework==="angular")return{code:`<${d.selector} icon=${JSON.stringify(r.name)}>`,language:"html"};if(d.framework==="vue"){const h=Object.entries(o).map(([v,_])=>`${JSON.stringify(v)}: ${JSON.stringify(_)}`).join(", ");return{code:`<${d.componentName} icon=${JSON.stringify(r.name)}${h?` :style="{ ${P(h)} }"`:""} />`,language:"html"}}return n.length===0?{code:`<${d.componentName} icon=${JSON.stringify(r.name)} />`,language:"tsx"}:{code:[`<${d.componentName}`,` icon=${JSON.stringify(r.name)}`," style={{",...n," }}","/>"].join(` +`),language:"tsx"}}if(i==="svg"){const d=Object.entries(o).map(([h,v])=>`${h}: ${v}`).join("; ");return{code:` + +`,language:"html"}}if(i==="img")return{code:`${P(r.name)}`,language:"html"};const l=`icon-${r.name.replace(/[^a-zA-Z0-9_-]+/g,"-")||"sprite"}`,g=xr(a);return{code:[`.${l} {`," width: 24px;"," height: 24px;",` mask: url('${g}') no-repeat center / contain;`,` -webkit-mask: url('${g}') no-repeat center / contain;`,` background-color: ${s};`,"}"].join(` +`),language:"css"}}var Cr=/[&<>"]/g,Ar={"&":"&","<":"<",">":">",'"':"""},Er=[[//g,"comment"],[/<\/?[\w.-]+/g,"tag"],[/[\w-]+(?=\s*=)/g,"attr"],[/"[^"]*"|'[^']*'/g,"string"]],kr=[[/\/\*[\s\S]*?\*\//g,"comment"],[/\.[\w-]+/g,"selector"],[/[\w-]+(?=\s*:)/g,"property"],[/'[^']*'|"[^"]*"/g,"string"],[/#[\da-fA-F]{3,8}\b/g,"color"],[/\d+(?:\.\d+)?(?:px|em|rem|%)?/g,"number"]];function Sr(e,t){const r=t==="css"?kr:Er,i=[];let o=0;for(;o{const n=a.replace(Cr,l=>Ar[l]);return s?`${n}`:n}).join("")}var J=new WeakMap;function L(e){return e!==null&&typeof e=="object"}function Ht(e){return L(e)?typeof e.name=="string"&&typeof e.id=="string"&&(e.viewBox===null||typeof e.viewBox=="string")&&Array.isArray(e.colors):!1}function Ut(e){return L(e)?e.schemaVersion===1&&e.generator==="@gromlab/svg-sprites"&&typeof e.name=="string"&&typeof e.target=="string"&&(e.format==="stack"||e.format==="symbol")&&typeof e.iconCount=="number"&&typeof e.spriteUrl=="string"&&Array.isArray(e.icons)&&e.icons.every(Ht):!1}function Nt(e){return L(e)&&e.schemaVersion===1&&e.generator==="@gromlab/svg-sprites"&&typeof e.name=="string"&&typeof e.target=="string"&&(e.format==="stack"||e.format==="symbol")&&typeof e.iconCount=="number"&&Array.isArray(e.icons)&&e.icons.every(Ht)}function Tr(e){return!L(e)||Ut(e)||Nt(e)?e:e.default??e.spriteManifest??e}function ee(e,t){const r=Tr(e);if(Ut(r))return r;if(t&&Nt(r))return{...r,spriteUrl:t};throw new Error("The loaded source does not contain a valid SVG sprite manifest.")}function Xe(e){return Array.isArray(e)?e:Object.values(e)}function Dt(e){if(!L(e))return!1;const t=e;return typeof t.manifestUrl=="string"&&typeof t.spriteUrl=="string"}function Qe(e,t){return e.namet.name?1:0}async function Mr(e){const t=await fetch(e.manifestUrl);if(!t.ok)throw new Error(`Cannot load SVG sprite manifest: ${t.status} ${t.statusText}`);return ee(await t.json(),e.spriteUrl)}async function Or(e){if(Dt(e))return Mr(e);if(typeof e!="function")return ee(e);const t=J.get(e);if(t)return t;const r=Promise.resolve().then(e).then(i=>ee(i));return J.set(e,r),r.catch(()=>{J.get(e)===r&&J.delete(e)}),r}var Pr=` +:host { display: block; } +.gromlab-sprite-viewer { + --sv-bg: #f0f0f3; + --sv-text: #1a1a1a; + --sv-card: #ffffff; + --sv-card-hover: #eaeaed; + --sv-border: #d8d8d8; + --sv-accent: #3b82f6; + --sv-muted: #777777; + --sv-code: #f5f5f5; + --sv-checker-a: #e9e9e9; + --sv-checker-b: #ffffff; + --sv-danger: #b42332; + box-sizing: border-box; + min-height: 320px; + padding: 24px; + color: var(--sv-text); + color-scheme: light; + background: var(--sv-bg); + border-radius: 12px; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; +} +.gromlab-sprite-viewer[data-theme="dark"] { + --sv-bg: #1a1a1a; + --sv-text: #e5e5e5; + --sv-card: #2a2a2a; + --sv-card-hover: #333333; + --sv-border: #404040; + --sv-muted: #a3a3a3; + --sv-code: #242424; + --sv-checker-a: #333333; + --sv-checker-b: #2a2a2a; + --sv-danger: #ff9ba6; + color-scheme: dark; +} +@media (prefers-color-scheme: dark) { + .gromlab-sprite-viewer:not([data-theme]) { + --sv-bg: #1a1a1a; + --sv-text: #e5e5e5; + --sv-card: #2a2a2a; + --sv-card-hover: #333333; + --sv-border: #404040; + --sv-muted: #a3a3a3; + --sv-code: #242424; + --sv-checker-a: #333333; + --sv-checker-b: #2a2a2a; + --sv-danger: #ff9ba6; + color-scheme: dark; + } +} +.gromlab-sprite-viewer *, +.gromlab-sprite-viewer *::before, +.gromlab-sprite-viewer *::after { box-sizing: border-box; } +.gromlab-sprite-viewer button, +.gromlab-sprite-viewer input { font: inherit; } +.gromlab-sprite-viewer__header { + display: flex; + align-items: center; + gap: 16px; + flex-wrap: wrap; + margin-bottom: 24px; +} +.gromlab-sprite-viewer__title { margin: 0; font-size: 24px; line-height: 1.2; font-weight: 700; } +.gromlab-sprite-viewer__summary { color: var(--sv-muted); font-size: 13px; } +.gromlab-sprite-viewer__toolbar { display: flex; align-items: center; gap: 12px; margin-left: auto; } +.gromlab-sprite-viewer__search { + width: 220px; + height: 38px; + padding: 0 12px; + color: var(--sv-text); + background: var(--sv-card); + border: 1px solid var(--sv-border); + border-radius: 8px; + outline: none; + transition: border-color .15s, box-shadow .15s; +} +.gromlab-sprite-viewer__search:focus { border-color: var(--sv-accent); box-shadow: 0 0 0 3px rgba(59, 130, 246, .18); } +.gromlab-sprite-viewer__search::placeholder { color: var(--sv-muted); } +.gromlab-sprite-viewer__theme { + display: grid; + place-items: center; + width: 38px; + height: 38px; + padding: 0; + color: var(--sv-text); + background: var(--sv-card); + border: 1px solid var(--sv-border); + border-radius: 8px; + cursor: pointer; + transition: background .15s, border-color .15s; +} +.gromlab-sprite-viewer__theme:hover { background: var(--sv-card-hover); } +.gromlab-sprite-viewer__theme:focus-visible, +.gromlab-sprite-viewer__card:focus-visible, +.gromlab-sprite-viewer__close:focus-visible, +.gromlab-sprite-viewer__tab:focus-visible, +.gromlab-sprite-viewer__copy:focus-visible, +.gromlab-sprite-viewer__swatch:focus-visible { + outline: 2px solid var(--sv-accent); + outline-offset: 2px; +} +.gromlab-sprite-viewer__errors { + margin: 0 0 24px; + padding: 12px 14px; + color: var(--sv-danger); + background: rgba(180, 35, 50, .08); + border: 1px solid rgba(180, 35, 50, .28); + border-radius: 8px; + font-size: 12px; +} +.gromlab-sprite-viewer__group { margin-bottom: 40px; } +.gromlab-sprite-viewer__group-header { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 16px; } +.gromlab-sprite-viewer__group-title { margin: 0; font-size: 18px; line-height: 1.3; font-weight: 600; } +.gromlab-sprite-viewer__badge { + padding: 2px 8px; + color: #ffffff; + background: var(--sv-accent); + border-radius: 999px; + font-size: 11px; + line-height: 1.5; + font-weight: 600; + letter-spacing: .04em; + text-transform: uppercase; +} +.gromlab-sprite-viewer__group-count, +.gromlab-sprite-viewer__description { color: var(--sv-muted); font-size: 13px; font-weight: 400; } +.gromlab-sprite-viewer__description { flex-basis: 100%; margin: -2px 0 0; } +.gromlab-sprite-viewer__grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 12px; } +.gromlab-sprite-viewer__card { + display: flex; + min-width: 0; + padding: 16px 8px; + color: inherit; + text-align: center; + background: var(--sv-card); + border: 0; + border-radius: 8px; + cursor: pointer; + flex-direction: column; + align-items: center; + gap: 8px; + transition: background .15s, transform .15s; +} +.gromlab-sprite-viewer__card:hover { background: var(--sv-card-hover); transform: translateY(-1px); } +.gromlab-sprite-viewer__icon-wrap, +.gromlab-sprite-viewer__dialog-preview-canvas { + display: grid; + place-items: center; + background: conic-gradient( + var(--sv-checker-a) 25%, var(--sv-checker-b) 0 50%, + var(--sv-checker-a) 0 75%, var(--sv-checker-b) 0 + ); + background-size: 8px 8px; + border-radius: 4px; +} +.gromlab-sprite-viewer__icon-wrap { width: 128px; height: 128px; } +.gromlab-sprite-viewer__icon { display: block; width: 128px; height: 128px; color: var(--sv-text); overflow: visible; } +.gromlab-sprite-viewer__icon-name { + max-width: 100%; + overflow: hidden; + color: var(--sv-muted); + font-size: 13px; + line-height: 1.35; + text-overflow: ellipsis; + white-space: nowrap; +} +.gromlab-sprite-viewer__status { + padding: 40px 20px; + color: var(--sv-muted); + text-align: center; + background: var(--sv-card); + border: 1px dashed var(--sv-border); + border-radius: 8px; +} +.gromlab-sprite-viewer__footer { + display: flex; + align-items: center; + justify-content: center; + gap: 12px; + margin-top: 40px; + padding-top: 16px; + color: var(--sv-muted); + border-top: 1px solid var(--sv-border); + font-size: 13px; +} +.gromlab-sprite-viewer__footer a { color: var(--sv-accent); text-decoration: none; } +.gromlab-sprite-viewer__footer a:hover { text-decoration: underline; } +.gromlab-sprite-viewer__dialog { + width: min(560px, calc(100vw - 32px)); + max-width: none; + max-height: calc(100dvh - 48px); + padding: 0; + color: var(--sv-text); + background: transparent; + border: 0; + overflow: visible; +} +.gromlab-sprite-viewer__dialog::backdrop { background: rgba(0, 0, 0, .55); backdrop-filter: blur(2px); } +.gromlab-sprite-viewer__dialog-shell { + position: relative; + max-height: calc(100dvh - 48px); + padding: 24px; + overflow-y: auto; + color: var(--sv-text); + background: var(--sv-bg); + border: 1px solid var(--sv-border); + border-radius: 12px; + box-shadow: 0 24px 72px rgba(0, 0, 0, .28); +} +.gromlab-sprite-viewer__close { + position: absolute; + z-index: 2; + top: 12px; + right: 12px; + display: grid; + place-items: center; + width: 34px; + height: 34px; + padding: 0; + color: var(--sv-text); + background: var(--sv-bg); + border: 1px solid var(--sv-border); + border-radius: 8px; + cursor: pointer; +} +.gromlab-sprite-viewer__close:hover { background: var(--sv-card-hover); } +.gromlab-sprite-viewer__dialog-preview { display: flex; align-items: center; justify-content: center; padding: 24px; margin-bottom: 16px; background: var(--sv-card); border-radius: 8px; } +.gromlab-sprite-viewer__dialog-preview-canvas { width: 256px; height: 256px; } +.gromlab-sprite-viewer__dialog-icon, +.gromlab-sprite-viewer__dialog-img, +.gromlab-sprite-viewer__dialog-mask { display: block; width: 256px; height: 256px; } +.gromlab-sprite-viewer__dialog-icon { color: var(--sv-text); overflow: visible; } +.gromlab-sprite-viewer__dialog-img { object-fit: contain; } +.gromlab-sprite-viewer__dialog-heading { display: flex; justify-content: center; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 6px; } +.gromlab-sprite-viewer__dialog-title { margin: 0; font: 600 16px/1.3 ui-monospace, "SFMono-Regular", Consolas, monospace; } +.gromlab-sprite-viewer__viewbox { + padding: 2px 8px; + color: var(--sv-muted); + background: var(--sv-card); + border: 1px solid var(--sv-border); + border-radius: 999px; + font-size: 11px; + white-space: nowrap; +} +.gromlab-sprite-viewer__dialog-meta { margin: 0 0 20px; color: var(--sv-muted); text-align: center; font-size: 12px; } +.gromlab-sprite-viewer__colors { margin-bottom: 20px; } +.gromlab-sprite-viewer__hint { + margin: 0 0 12px; + padding: 8px 12px; + color: var(--sv-muted); + background: var(--sv-card); + border-left: 3px solid var(--sv-accent); + border-radius: 4px; + font-size: 12px; + line-height: 1.5; +} +.gromlab-sprite-viewer__colors-title { margin: 0 0 8px; color: var(--sv-muted); font-size: 12px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; } +.gromlab-sprite-viewer__color-row { position: relative; display: flex; align-items: center; gap: 8px; min-height: 32px; margin-bottom: 6px; } +.gromlab-sprite-viewer__swatch { width: 26px; height: 26px; flex: 0 0 auto; padding: 0; border: 1px solid var(--sv-border); border-radius: 4px; cursor: pointer; } +.gromlab-sprite-viewer__color-label { min-width: 0; overflow-wrap: anywhere; color: var(--sv-muted); font: 12px/1.4 ui-monospace, "SFMono-Regular", Consolas, monospace; } +.gromlab-sprite-viewer__color-popover { + position: absolute; + z-index: 5; + bottom: calc(100% + 6px); + left: 0; + width: 224px; + max-width: calc(100vw - 64px); + padding: 12px; + background: var(--sv-bg); + border: 1px solid var(--sv-border); + border-radius: 8px; + box-shadow: 0 8px 28px rgba(0, 0, 0, .22); +} +.gromlab-sprite-viewer__color-picker { display: flex; flex-direction: column; width: 198px; max-width: 100%; height: 160px; } +.gromlab-sprite-viewer__hex-input { display: block; width: 100%; margin-top: 8px; } +.gromlab-sprite-viewer__hex-input::part(input) { box-sizing: border-box; display: block; width: 100%; height: 32px; padding: 0 8px; color: var(--sv-text); text-align: center; background: var(--sv-card); border: 1px solid var(--sv-border); border-radius: 4px; outline: none; font: 12px/1 ui-monospace, "SFMono-Regular", Consolas, monospace; } +.gromlab-sprite-viewer__hex-input::part(input):focus { border-color: var(--sv-accent); } +.gromlab-sprite-viewer__tabs { display: flex; overflow-x: auto; margin-bottom: 12px; border-bottom: 1px solid var(--sv-border); } +.gromlab-sprite-viewer__tab { flex: 0 0 auto; padding: 8px 16px; color: var(--sv-muted); background: none; border: 0; border-bottom: 2px solid transparent; cursor: pointer; font-size: 12px; font-weight: 600; } +.gromlab-sprite-viewer__tab[aria-selected="true"] { color: var(--sv-accent); border-bottom-color: var(--sv-accent); } +.gromlab-sprite-viewer__code { position: relative; overflow: hidden; background: var(--sv-code); border-radius: 8px; } +.gromlab-sprite-viewer__code pre { min-height: 72px; margin: 0; padding: 16px 70px 16px 16px; overflow-x: auto; font: 12px/1.6 ui-monospace, "SFMono-Regular", Consolas, monospace; } +.gromlab-sprite-viewer__copy { position: absolute; top: 8px; right: 8px; padding: 4px 8px; color: var(--sv-muted); background: var(--sv-bg); border: 1px solid var(--sv-border); border-radius: 4px; cursor: pointer; font-size: 11px; } +.gromlab-sprite-viewer__copy:hover { color: var(--sv-text); } +.gromlab-sprite-viewer__code .hl-tag { color: #116329; } +.gromlab-sprite-viewer__code .hl-attr, +.gromlab-sprite-viewer__code .hl-number, +.gromlab-sprite-viewer__code .hl-property { color: #0550ae; } +.gromlab-sprite-viewer__code .hl-string, +.gromlab-sprite-viewer__code .hl-color { color: #0a3069; } +.gromlab-sprite-viewer__code .hl-comment { color: #8b949e; } +.gromlab-sprite-viewer__code .hl-punctuation, +.gromlab-sprite-viewer__code .hl-selector { color: #6639ba; } +.gromlab-sprite-viewer[data-theme="dark"] .gromlab-sprite-viewer__code .hl-tag { color: #7ee787; } +.gromlab-sprite-viewer[data-theme="dark"] .gromlab-sprite-viewer__code .hl-attr, +.gromlab-sprite-viewer[data-theme="dark"] .gromlab-sprite-viewer__code .hl-number, +.gromlab-sprite-viewer[data-theme="dark"] .gromlab-sprite-viewer__code .hl-property { color: #79c0ff; } +.gromlab-sprite-viewer[data-theme="dark"] .gromlab-sprite-viewer__code .hl-string, +.gromlab-sprite-viewer[data-theme="dark"] .gromlab-sprite-viewer__code .hl-color { color: #a5d6ff; } +.gromlab-sprite-viewer[data-theme="dark"] .gromlab-sprite-viewer__code .hl-punctuation, +.gromlab-sprite-viewer[data-theme="dark"] .gromlab-sprite-viewer__code .hl-selector { color: #d2a8ff; } +@media (prefers-color-scheme: dark) { + .gromlab-sprite-viewer:not([data-theme]) .gromlab-sprite-viewer__code .hl-tag { color: #7ee787; } + .gromlab-sprite-viewer:not([data-theme]) .gromlab-sprite-viewer__code .hl-attr, + .gromlab-sprite-viewer:not([data-theme]) .gromlab-sprite-viewer__code .hl-number, + .gromlab-sprite-viewer:not([data-theme]) .gromlab-sprite-viewer__code .hl-property { color: #79c0ff; } + .gromlab-sprite-viewer:not([data-theme]) .gromlab-sprite-viewer__code .hl-string, + .gromlab-sprite-viewer:not([data-theme]) .gromlab-sprite-viewer__code .hl-color { color: #a5d6ff; } + .gromlab-sprite-viewer:not([data-theme]) .gromlab-sprite-viewer__code .hl-punctuation, + .gromlab-sprite-viewer:not([data-theme]) .gromlab-sprite-viewer__code .hl-selector { color: #d2a8ff; } +} +@media (max-width: 640px) { + .gromlab-sprite-viewer { padding: 16px; border-radius: 8px; } + .gromlab-sprite-viewer__header { align-items: stretch; } + .gromlab-sprite-viewer__toolbar { width: 100%; margin-left: 0; } + .gromlab-sprite-viewer__search { width: auto; flex: 1; min-width: 0; } + .gromlab-sprite-viewer__grid { grid-template-columns: repeat(auto-fill, minmax(132px, 1fr)); gap: 8px; } + .gromlab-sprite-viewer__icon-wrap, + .gromlab-sprite-viewer__icon { width: 104px; height: 104px; } + .gromlab-sprite-viewer__dialog { width: calc(100vw - 20px); max-height: calc(100dvh - 20px); } + .gromlab-sprite-viewer__dialog-shell { max-height: calc(100dvh - 20px); padding: 16px; } + .gromlab-sprite-viewer__dialog-preview { padding: 16px; } + .gromlab-sprite-viewer__dialog-preview-canvas, + .gromlab-sprite-viewer__dialog-icon, + .gromlab-sprite-viewer__dialog-img, + .gromlab-sprite-viewer__dialog-mask { width: min(256px, calc(100vw - 86px)); height: min(256px, calc(100vw - 86px)); } +} +@media (prefers-reduced-motion: reduce) { + .gromlab-sprite-viewer *, + .gromlab-sprite-viewer *::before, + .gromlab-sprite-viewer *::after { scroll-behavior: auto !important; transition-duration: .01ms !important; } +} +`,et="gromlab-sprite-viewer",be="gromlab-hex-color-picker",we="gromlab-hex-input",W;function Hr(){return customElements.get(be)&&customElements.get(we)?Promise.resolve():W||(W=Promise.all([Promise.resolve().then(()=>(Ft(),mt)),Promise.resolve().then(()=>(Wt(),_t))]).then(([{HexBase:e},{HexInputBase:t}])=>{customElements.get(be)||customElements.define(be,class extends e{}),customElements.get(we)||customElements.define(we,class extends t{})}),W)}function tt(e,t){const r=e%100,i=e%10,o=r>=11&&r<=19?t[2]:i===1?t[0]:i>=2&&i<=4?t[1]:t[2];return`${e} ${o}`}function Ur(){return globalThis.matchMedia?.("(prefers-color-scheme: dark)").matches?"dark":"light"}function rt(e,t){return Object.fromEntries(e.colors.map(({variable:r,fallback:i})=>[r,R(i,t)]))}var Nr=class extends U{static properties={sources:{attribute:!1},viewerTitle:{attribute:"viewer-title"},colorTheme:{attribute:"color-theme"},themeControlled:{attribute:!1},showThemeToggle:{attribute:!1},manifestUrl:{attribute:"manifest-url"},spriteUrl:{attribute:"sprite-url"},_manifests:{state:!0},_errors:{state:!0},_loading:{state:!0},_query:{state:!0},_systemTheme:{state:!0},_selected:{state:!0},_activeTab:{state:!0},_colors:{state:!0},_colorOverrides:{state:!0},_cssColor:{state:!0},_cssColorOverridden:{state:!0},_openColorControl:{state:!0},_copied:{state:!0}};static styles=wt(Pr);_loadVersion=0;_copyTimeout;_media;constructor(){super(),this.sources=[],this.viewerTitle="SVG Sprites",this.colorTheme="auto",this.themeControlled=!1,this.showThemeToggle=!0,this._manifests=[],this._errors=[],this._loading=!1,this._query="",this._systemTheme=Ur(),this._selected=null,this._activeTab="svg",this._colors={},this._colorOverrides={},this._cssColor="#1a1a1a",this._cssColorOverridden=!1,this._openColorControl=null,this._copied=!1}_handleMediaChange=()=>{this._systemTheme=this._media?.matches?"dark":"light",this._syncThemeColors()};_handleDocumentPointerDown=e=>{if(!this._openColorControl)return;e.composedPath().some(r=>r instanceof HTMLElement&&(r.dataset.colorTrigger===this._openColorControl||r.dataset.colorPopover===this._openColorControl))||(this._openColorControl=null)};connectedCallback(){super.connectedCallback(),Hr(),this._media=globalThis.matchMedia?.("(prefers-color-scheme: dark)"),this._systemTheme=this._media?.matches?"dark":"light",this._media?.addEventListener("change",this._handleMediaChange),globalThis.document?.addEventListener("pointerdown",this._handleDocumentPointerDown,!0)}disconnectedCallback(){this._media?.removeEventListener("change",this._handleMediaChange),globalThis.document?.removeEventListener("pointerdown",this._handleDocumentPointerDown,!0),this._copyTimeout&&clearTimeout(this._copyTimeout),super.disconnectedCallback()}updated(e){(e.has("sources")||e.has("manifestUrl")||e.has("spriteUrl"))&&this._loadSources(),e.has("colorTheme")&&this._syncThemeColors()}get _resolvedColorTheme(){return this.colorTheme==="auto"?this._systemTheme:this.colorTheme}get _themeColor(){return this._resolvedColorTheme==="dark"?"#e5e5e5":"#1a1a1a"}_effectiveSources(){const e=Xe(this.sources);return e.length>0?e:this.manifestUrl&&this.spriteUrl?[{manifestUrl:this.manifestUrl,spriteUrl:this.spriteUrl}]:[]}async _loadSources(){const e=++this._loadVersion,t=this._effectiveSources(),r=[],i=[],o=[];this.manifestUrl&&!this.spriteUrl&&Xe(this.sources).length===0&&o.push("The sprite-url attribute is required with manifest-url.");for(const n of t){if(typeof n=="function"||Dt(n)){i.push(n);continue}try{r.push(ee(n))}catch(l){o.push(l instanceof Error?l.message:String(l))}}if(this._manifests=r.sort(Qe),this._errors=o,this._loading=i.length>0,this._selected=null,i.length===0)return;const s=await Promise.allSettled(i.map(Or));if(e!==this._loadVersion)return;const a=[];for(const n of s)n.status==="fulfilled"?a.push(n.value):o.push(n.reason instanceof Error?n.reason.message:String(n.reason));this._manifests=[...r,...a].sort(Qe),this._errors=[...o],this._loading=!1}_syncThemeColors(){this._selected&&(this._colors={...rt(this._selected.icon,this._themeColor),...this._colorOverrides},this._cssColorOverridden||(this._cssColor=this._themeColor))}_toggleTheme(){const e=this._resolvedColorTheme==="dark"?"light":"dark";this.themeControlled||(this.colorTheme=e),this.dispatchEvent(new CustomEvent("color-theme-change",{bubbles:!0,composed:!0,detail:{theme:e}}))}async _selectIcon(e,t){const r=`${e.name}:${e.spriteUrl}:${t.id}`;this._selected={key:r,manifest:e,icon:t},this._activeTab=fe(e)[0]?.id??"svg",this._colorOverrides={},this._colors=rt(t,this._themeColor),this._cssColor=this._themeColor,this._cssColorOverridden=!1,this._openColorControl=null,this._copied=!1,await this.updateComplete;const i=this.renderRoot.querySelector("dialog");i&&!i.open&&i.showModal()}_closeDialog(){const e=this.renderRoot.querySelector("dialog");e?.open&&e.close(),this._selected=null,this._openColorControl=null}_handleBackdropClick(e){if(!(e.currentTarget instanceof HTMLDialogElement)||e.target!==e.currentTarget)return;const t=e.currentTarget.getBoundingClientRect();(e.clientXt.right||e.clientYt.bottom)&&this._closeDialog()}_handleTabKeyDown(e,t){if(!this._selected)return;const r=fe(this._selected.manifest);let i=null;if(e.key==="ArrowRight"&&(i=(t+1)%r.length),e.key==="ArrowLeft"&&(i=(t-1+r.length)%r.length),e.key==="Home"&&(i=0),e.key==="End"&&(i=r.length-1),i===null)return;e.preventDefault(),this._activeTab=r[i].id;const o=this.renderRoot.querySelectorAll('[role="tab"]');this.updateComplete.then(()=>o[i]?.focus())}_handleColorChange(e,t){const r=R(t);this._colors={...this._colors,[e]:r},this._colorOverrides={...this._colorOverrides,[e]:r}}_handleCssColorChange(e){this._cssColor=R(e),this._cssColorOverridden=!0}async _copyCode(e){if(globalThis.navigator?.clipboard)try{await globalThis.navigator.clipboard.writeText(e),this._copied=!0,this._copyTimeout&&clearTimeout(this._copyTimeout),this._copyTimeout=setTimeout(()=>{this._copied=!1},1500)}catch{this._copied=!1}}_renderColorControl(e,t,r,i){const o=this._openColorControl===r,s=`color-${r.replace(/[^a-zA-Z0-9_-]/g,"-")}`;return p` +
+ + ${e} + ${o?p` + + `:c} +
+ `}_renderDialogPreview(e,t){const r=`${e.spriteUrl}#${t.id}`;return this._activeTab==="img"?p`${t.name}`:this._activeTab==="css"?p` + + `:p` + + + + `}_renderDialog(){if(!this._selected)return c;const{manifest:e,icon:t}=this._selected,r=fe(e),i=r.some(n=>n.id===this._activeTab)?this._activeTab:r[0].id,o=yr({manifest:e,icon:t,tab:i,colorOverrides:this._colorOverrides,cssColor:this._cssColor}),s=wr(t.viewBox),a=i!=="img"&&i!=="css";return p` + {n.preventDefault(),this._closeDialog()}} + @click=${this._handleBackdropClick} + > +
+ + +
+
+ ${this._renderDialogPreview(e,t)} +
+
+ +
+

${t.name}

+ ${s?p`${s}`:c} +
+

${e.name} · ${e.format} · ${e.target}

+ +
+ ${a&&t.colors.length>0?p` +

+ Цвета применяются к превью через CSS-переменные и попадут в пример кода. +

+

CSS Variables

+ ${t.colors.map(({variable:n,fallback:l})=>this._renderColorControl(`${n}: ${l}`,this._colors[n],n,g=>this._handleColorChange(n,g)))} + `:c} + ${a&&t.colors.length===0?p` +

У иконки нет настраиваемых цветовых переменных.

+ `:c} + ${i==="img"?p` +

+ IMG изолирует SVG: CSS-переменные и currentColor внутрь изображения не передаются. +

+ `:c} + ${i==="css"?p` +

+ CSS mask отображает иконку одним цветом через background-color. +

+ ${this._renderColorControl("background-color",this._cssColor,"background-color",n=>this._handleCssColorChange(n))} + `:c} +
+ +
+ ${r.map((n,l)=>p` + + `)} +
+ +
+
${_r(Sr(o.code,o.language))}
+ +
+
+
+ `}render(){const e=this._query.trim().toLowerCase(),t=this._manifests.map(o=>({manifest:o,icons:o.icons.filter(s=>e===""||s.name.toLowerCase().includes(e)||o.name.toLowerCase().includes(e))})).filter(o=>o.icons.length>0),r=this._manifests.reduce((o,s)=>o+s.iconCount,0),i=t.reduce((o,s)=>o+s.icons.length,0);return p` +
+
+

${this.viewerTitle}

+ + ${tt(this._manifests.length,["спрайт","спрайта","спрайтов"])} + · ${tt(r,["иконка","иконки","иконок"])} + ${e?` · найдено ${i}`:c} + +
+ {this._query=o.currentTarget.value}} + placeholder="Найти иконку" + aria-label="Поиск иконок" + > + ${this.showThemeToggle?p` + + `:c} +
+
+ + ${this._errors.length>0?p` + + `:c} + + ${t.map(({manifest:o,icons:s})=>p` +
+
+

${o.name}

+ ${o.format} + ${s.length} + ${o.description?p` +

${o.description}

+ `:c} +
+
+ ${s.map(a=>p` + + `)} +
+
+ `)} + + ${t.length===0&&(!this._loading||this._manifests.length>0)?p` +
+ ${this._manifests.length===0?"Спрайты не подключены":"Иконки не найдены"} +
+ `:c} + ${this._loading&&this._manifests.length===0?p` +
Загрузка спрайтов...
+ `:c} + + + + ${this._renderDialog()} +
+ `}};function Dr(){customElements.get(et)||customElements.define(et,Nr)}Dr();/*! Bundled license information: + +@lit/reactive-element/css-tag.js: + (** + * @license + * Copyright 2019 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + *) + +@lit/reactive-element/reactive-element.js: +lit-html/lit-html.js: +lit-element/lit-element.js: +lit-html/directive.js: +lit-html/directives/unsafe-html.js: + (** + * @license + * Copyright 2017 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + *) + +lit-html/is-server.js: + (** + * @license + * Copyright 2022 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + *) + +lit-html/directives/if-defined.js: +lit-html/directives/style-map.js: + (** + * @license + * Copyright 2018 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + *) +*/export{Dr as defineSpriteViewerElement}; diff --git a/integration/apps/solid-start-vite/.vinxi/build/ssr/index.js b/integration/apps/solid-start-vite/.vinxi/build/ssr/index.js new file mode 100644 index 0000000..6178f9f --- /dev/null +++ b/integration/apps/solid-start-vite/.vinxi/build/ssr/index.js @@ -0,0 +1 @@ +import"solid-js/web";import"solid-js";import{H as p}from"./assets/index-CV0w8CD4.js";export{p as default}; diff --git a/integration/apps/solid-start-vite/.vinxi/build/ssr/ssr.js b/integration/apps/solid-start-vite/.vinxi/build/ssr/ssr.js new file mode 100644 index 0000000..49bdf0d --- /dev/null +++ b/integration/apps/solid-start-vite/.vinxi/build/ssr/ssr.js @@ -0,0 +1 @@ +import{ssrElement as K,escape as T,mergeProps as Ke,ssr as $,isServer as x,getRequestEvent as k,renderToString as ze,renderToStream as Ge,createComponent as R,delegateEvents as Je,ssrHydrationKey as q,useAssets as Ve,NoHydration as ye,Hydration as Xe,ssrAttribute as z,HydrationScript as Ye}from"solid-js/web";import{lazy as Qe,createComponent as we,sharedConfig as U,getOwner as ve,runWithOwner as Re,createMemo as P,createSignal as M,createRenderEffect as Ze,on as ne,useContext as et,createContext as be,startTransition as tt,resetErrorBoundaries as nt,batch as rt,untrack as Se,children as st,Show as Ee,createRoot as ot,onCleanup as re,Suspense as at,catchError as it,ErrorBoundary as ct}from"solid-js";import{provideRequestEvent as ut}from"solid-js/web/storage";import{H3Event as Y,setResponseStatus as lt,setHeader as ht,getRequestIP as dt,getResponseStatus as ft,getResponseStatusText as pt,getResponseHeader as mt,setResponseHeader as gt,removeResponseHeader as yt,appendResponseHeader as wt,getResponseHeaders as vt,getRequestURL as Rt,getRequestWebStream as bt,sendRedirect as St,getCookie as Et,setCookie as At,eventHandler as $t}from"h3";import{getContext as Pt}from"unctx";import{AsyncLocalStorage as Tt}from"node:async_hooks";import{createRouter as xt}from"radix3";function Ct(e){let t;const n=$e(e),s={duplex:"half",method:e.method,headers:e.headers};return e.node.req.body instanceof ArrayBuffer?new Request(n,{...s,body:e.node.req.body}):new Request(n,{...s,get body(){return t||(t=Mt(e),t)}})}function Lt(e){return e.web??={request:Ct(e),url:$e(e)},e.web.request}function Ht(){return Bt()}const Ae=Symbol("$HTTPEvent");function qt(e){return typeof e=="object"&&(e instanceof Y||e?.[Ae]instanceof Y||e?.__is_event__===!0)}function S(e){return function(...t){let n=t[0];if(qt(n))t[0]=n instanceof Y||n.__is_event__?n:n[Ae];else{if(!globalThis.app.config.server.experimental?.asyncContext)throw new Error("AsyncLocalStorage was not enabled. Use the `server.experimental.asyncContext: true` option in your app configuration to enable it. Or, pass the instance of HTTPEvent that you have as the first argument to the function.");if(n=Ht(),!n)throw new Error("No HTTPEvent found in AsyncLocalStorage. Make sure you are using the function within the server runtime.");t.unshift(n)}return e(...t)}}const $e=S(Rt),kt=S(dt),Q=S(lt),ue=S(ft),Ot=S(pt),G=S(vt),le=S(mt),Ft=S(gt),_t=S(wt),he=S(St),It=S(Et),Ut=S(At),jt=S(ht),Mt=S(bt),Nt=S(yt),Wt=S(Lt);function Dt(){return Pt("nitro-app",{asyncContext:!!globalThis.app.config.server.experimental?.asyncContext,AsyncLocalStorage:Tt})}function Bt(){return Dt().use().event}const Pe=[{page:!0,$component:{src:"src/routes/index.jsx?pick=default&pick=$css",build:()=>import("./index.js"),import:()=>import("./index.js")},path:"/",filePath:"/home/gromov/projects/my/svg-sprites/integration/apps/solid-start-vite/src/routes/index.jsx"}],Kt=zt(Pe.filter(e=>e.page));function zt(e){function t(n,s,r,o){const a=Object.values(n).find(i=>r.startsWith(i.id+"/"));return a?(t(a.children||(a.children=[]),s,r.slice(a.id.length)),n):(n.push({...s,id:r,path:r.replace(/\([^)/]+\)/g,"").replace(/\/+/g,"/")}),n)}return e.sort((n,s)=>n.path.length-s.path.length).reduce((n,s)=>t(n,s,s.path,s.path),[])}function Gt(e,t){const n=Vt.lookup(e);if(n&&n.route){const s=n.route,r=t==="HEAD"?s.$HEAD||s.$GET:s[`$${t}`];if(r===void 0)return;const o=s.page===!0&&s.$component!==void 0;return{handler:r,params:n.params,isPage:o}}}function Jt(e){return e.$HEAD||e.$GET||e.$POST||e.$PUT||e.$PATCH||e.$DELETE}const Vt=xt({routes:Pe.reduce((e,t)=>{if(!Jt(t))return e;let n=t.path.replace(/\([^)/]+\)/g,"").replace(/\/+/g,"/").replace(/\*([^/]*)/g,(s,r)=>`**:${r}`).split("/").map(s=>s.startsWith(":")||s.startsWith("*")?s:encodeURIComponent(s)).join("/");if(/:[^/]*\?/g.test(n))throw new Error(`Optional parameters are not supported in API routes: ${n}`);if(e[n])throw new Error(`Duplicate API routes for "${n}" found at "${e[n].route.path}" and "${t.path}"`);return e[n]={route:t},e},{})}),V="solidFetchEvent";function Xt(e){return{request:Wt(e),response:Zt(e),clientAddress:kt(e),locals:{},nativeEvent:e}}function Yt(e){if(!e.context[V]){const t=Xt(e);e.context[V]=t}return e.context[V]}class Qt{event;constructor(t){this.event=t}get(t){const n=le(this.event,t);return Array.isArray(n)?n.join(", "):n||null}has(t){return this.get(t)!==null}set(t,n){return Ft(this.event,t,n)}delete(t){return Nt(this.event,t)}append(t,n){_t(this.event,t,n)}getSetCookie(){const t=le(this.event,"Set-Cookie");return Array.isArray(t)?t:[t]}forEach(t){return Object.entries(G(this.event)).forEach(([n,s])=>t(Array.isArray(s)?s.join(", "):s,n,this))}entries(){return Object.entries(G(this.event)).map(([t,n])=>[t,Array.isArray(n)?n.join(", "):n])[Symbol.iterator]()}keys(){return Object.keys(G(this.event))[Symbol.iterator]()}values(){return Object.values(G(this.event)).map(t=>Array.isArray(t)?t.join(", "):t)[Symbol.iterator]()}[Symbol.iterator](){return this.entries()[Symbol.iterator]()}}function Zt(e){return{get status(){return ue(e)},set status(t){Q(e,t)},get statusText(){return Ot(e)},set statusText(t){Q(e,ue(e),t)},headers:new Qt(e)}}function en(e){e.forEach(t=>{if(!t.attrs.href)return;let n=document.head.querySelector(`link[href="${t.attrs.href}"]`);n||(n=document.createElement("link"),n.setAttribute("rel","preload"),n.setAttribute("as","style"),n.setAttribute("href",t.attrs.href),document.head.appendChild(n))})}var tn=" ";const nn={style:e=>K("style",e.attrs,()=>e.children,!0),link:e=>K("link",e.attrs,void 0,!0),script:e=>e.attrs.src?K("script",Ke(()=>e.attrs,{get id(){return e.key}}),()=>$(tn),!0):null,noscript:e=>K("noscript",e.attrs,()=>T(e.children),!0)};function Z(e,t){let{tag:n,attrs:{key:s,...r}={key:void 0},children:o}=e;return nn[n]({attrs:{...r,nonce:t},key:s,children:o})}function rn(e,t,n,s="default"){return Qe(async()=>{{const o=(await e.import())[s],i=(await t.inputs?.[e.src].assets()).filter(u=>u.tag==="style"||u.attrs.rel==="stylesheet");return typeof window<"u"&&en(i),{default:u=>[...i.map(h=>Z(h)),we(o,u)]}}})}function Te(){function e(n){return{...n,...n.$$route?n.$$route.require().route:void 0,info:{...n.$$route?n.$$route.require().route.info:{},filesystem:!0},component:n.$component&&rn(n.$component,globalThis.MANIFEST.client,globalThis.MANIFEST.ssr),children:n.children?n.children.map(e):void 0}}return Kt.map(e)}let de;const sn=x?()=>k().routes:()=>de||(de=Te());function on(e){const t=It(e.nativeEvent,"flash");if(t)try{let n=JSON.parse(t);if(!n||!n.result)return;const s=[...n.input.slice(0,-1),new Map(n.input[n.input.length-1])],r=n.error?new Error(n.result):n.result;return{input:s,url:n.url,pending:!1,result:n.thrown?void 0:r,error:n.thrown?r:void 0}}catch(n){console.error(n)}finally{Ut(e.nativeEvent,"flash","",{maxAge:0})}}async function an(e){const t=globalThis.MANIFEST.client;return globalThis.MANIFEST.ssr,e.response.headers.set("Content-Type","text/html"),Object.assign(e,{manifest:await t.json(),assets:[...await t.inputs[t.handler].assets()],router:{submission:on(e)},routes:Te(),complete:!1,$islands:new Set})}const cn=new Set([301,302,303,307,308]);function ee(e){return e.status&&cn.has(e.status)?e.status:302}function un(e,t,n={},s){return $t({handler:r=>{const o=Yt(r);return ut(o,async()=>{const a=Gt(new URL(o.request.url).pathname,o.request.method);if(a){const d=await a.handler.import(),g=o.request.method==="HEAD"?d.HEAD||d.GET:d[o.request.method];o.params=a.params||{},U.context={event:o};const l=await g(o);if(l!==void 0)return l;if(o.request.method!=="GET")throw new Error(`API handler for ${o.request.method} "${o.request.url}" did not return a response.`);if(!a.isPage)return}const i=await t(o),c=typeof n=="function"?await n(i):{...n},u=c.mode||"stream";if(c.nonce&&(i.nonce=c.nonce),u==="sync"){const d=ze(()=>(U.context.event=i,e(i)),c);if(i.complete=!0,i.response&&i.response.headers.get("Location")){const g=ee(i.response);return he(r,i.response.headers.get("Location"),g)}return d}if(c.onCompleteAll){const d=c.onCompleteAll;c.onCompleteAll=g=>{pe(i)(g),d(g)}}else c.onCompleteAll=pe(i);if(c.onCompleteShell){const d=c.onCompleteShell;c.onCompleteShell=g=>{fe(i,r)(),d(g)}}else c.onCompleteShell=fe(i,r);const h=Ge(()=>(U.context.event=i,e(i)),c);if(i.response&&i.response.headers.get("Location")){const d=ee(i.response);return he(r,i.response.headers.get("Location"),d)}if(u==="async")return h;const{writable:w,readable:m}=new TransformStream;return h.pipeTo(w),m})}})}function fe(e,t){return()=>{if(e.response&&e.response.headers.get("Location")){const n=ee(e.response);Q(t,n),jt(t,"Location",e.response.headers.get("Location"))}}}function pe(e){return({write:t})=>{e.complete=!0;const n=e.response&&e.response.headers.get("Location");n&&t(` + diff --git a/integration/apps/solid-vite/package.json b/integration/apps/solid-vite/package.json index 8f6650d..30c845e 100644 --- a/integration/apps/solid-vite/package.json +++ b/integration/apps/solid-vite/package.json @@ -4,7 +4,7 @@ "version": "0.0.0", "type": "module", "scripts": { - "sprites": "svg-sprites --mode legacy .", + "sprites": "svg-sprites src/sprite/svg-sprite.config.ts", "typecheck": "tsc --noEmit", "build": "npm run sprites && tsc --noEmit && vite build", "dev": "npm run sprites && vite" diff --git a/integration/apps/solid-vite/src/generated-contract.typecheck.ts b/integration/apps/solid-vite/src/generated-contract.typecheck.ts new file mode 100644 index 0000000..4c27e19 --- /dev/null +++ b/integration/apps/solid-vite/src/generated-contract.typecheck.ts @@ -0,0 +1,13 @@ +import { IconsIcon, iconsIconNames } from './sprite' +import type { IconsIconName, IconsIconProps } from './sprite' + +const iconName: IconsIconName = 'check' +const props: IconsIconProps = { icon: iconName, width: 24 } + +void IconsIcon +void iconsIconNames +void props + +// @ts-expect-error Generated icon names form a literal union. +const missingIcon: IconsIconName = 'missing' +void missingIcon diff --git a/integration/apps/solid-vite/src/main.jsx b/integration/apps/solid-vite/src/main.jsx new file mode 100644 index 0000000..dbe30a8 --- /dev/null +++ b/integration/apps/solid-vite/src/main.jsx @@ -0,0 +1,35 @@ +import { onMount } from 'solid-js' +import { render } from 'solid-js/web' + +import { IconsIcon } from './sprite' +import './style.css' + +function App() { + let viewerHost + + onMount(async () => { + await import('@gromlab/svg-sprites/viewer/element') + const viewer = document.createElement('gromlab-sprite-viewer') + viewer.viewerTitle = 'Solid Vite Viewer' + viewer.sources = [() => import('./sprite/.svg-sprite/svg-sprite.manifest.js')] + viewerHost.append(viewer) + }) + + return ( +
+

Solid + Vite

+ +
+
+ ) +} + +render(() => , document.getElementById('root')) diff --git a/integration/apps/solid-vite/src/main.tsx b/integration/apps/solid-vite/src/main.tsx deleted file mode 100644 index d15a93c..0000000 --- a/integration/apps/solid-vite/src/main.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { render } from 'solid-js/web' - -import './style.css' - -function App() { - return ( -
-

Solid + Vite

- - - -
- ) -} - -render(() => , document.getElementById('root')!) diff --git a/integration/apps/solid-vite/src/sprite/.gitignore b/integration/apps/solid-vite/src/sprite/.gitignore new file mode 100644 index 0000000..7f3344e --- /dev/null +++ b/integration/apps/solid-vite/src/sprite/.gitignore @@ -0,0 +1,2 @@ +# @generated by @gromlab/svg-sprites. Do not edit. +/.svg-sprite/ diff --git a/integration/apps/solid-vite/src/sprite/index.d.ts b/integration/apps/solid-vite/src/sprite/index.d.ts new file mode 100644 index 0000000..ceeead8 --- /dev/null +++ b/integration/apps/solid-vite/src/sprite/index.d.ts @@ -0,0 +1 @@ +export * from './.svg-sprite/index.js' diff --git a/integration/apps/solid-vite/src/sprite/index.js b/integration/apps/solid-vite/src/sprite/index.js new file mode 100644 index 0000000..411538d --- /dev/null +++ b/integration/apps/solid-vite/src/sprite/index.js @@ -0,0 +1 @@ +export * from './.svg-sprite' diff --git a/integration/apps/solid-vite/src/sprite/svg-sprite.config.ts b/integration/apps/solid-vite/src/sprite/svg-sprite.config.ts new file mode 100644 index 0000000..608e50b --- /dev/null +++ b/integration/apps/solid-vite/src/sprite/svg-sprite.config.ts @@ -0,0 +1,8 @@ +import { defineSpriteConfig } from '@gromlab/svg-sprites' + +export default defineSpriteConfig({ + mode: 'solid@vite', + name: 'icons', + input: '../../../../fixtures/icons/check.svg', + generatedNotice: false, +}) diff --git a/integration/apps/solid-vite/src/style.css b/integration/apps/solid-vite/src/style.css index 55d4dfd..227e7fc 100644 --- a/integration/apps/solid-vite/src/style.css +++ b/integration/apps/solid-vite/src/style.css @@ -12,5 +12,8 @@ body { [data-testid='icon'] { width: 64px; height: 64px; - color: #16a34a; +} + +.viewer { + margin-top: 32px; } diff --git a/integration/apps/solid-vite/svg-sprites.config.ts b/integration/apps/solid-vite/svg-sprites.config.ts deleted file mode 100644 index 24da7e0..0000000 --- a/integration/apps/solid-vite/svg-sprites.config.ts +++ /dev/null @@ -1,11 +0,0 @@ -export default { - output: 'public/sprites', - preview: false, - sprites: [ - { - name: 'icons', - input: '../../fixtures/icons', - format: 'symbol', - }, - ], -} diff --git a/integration/apps/solid-webpack/package.json b/integration/apps/solid-webpack/package.json new file mode 100644 index 0000000..b9e441d --- /dev/null +++ b/integration/apps/solid-webpack/package.json @@ -0,0 +1,27 @@ +{ + "name": "@svg-sprites-fixtures/solid-webpack", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "sprites": "svg-sprites src/sprite/svg-sprite.config.ts", + "typecheck": "tsc --noEmit", + "build": "npm run sprites && npm run typecheck && webpack --mode production", + "dev": "npm run sprites && webpack serve --mode development" + }, + "dependencies": { + "solid-js": "1.9.14" + }, + "devDependencies": { + "@gromlab/svg-sprites": "file:../../..", + "babel-loader": "10.0.0", + "babel-preset-solid": "1.9.9", + "css-loader": "7.1.4", + "html-webpack-plugin": "5.6.7", + "style-loader": "4.0.0", + "typescript": "6.0.2", + "webpack": "5.108.4", + "webpack-cli": "7.2.1", + "webpack-dev-server": "5.2.2" + } +} diff --git a/integration/apps/solid-webpack/src/generated-contract.typecheck.ts b/integration/apps/solid-webpack/src/generated-contract.typecheck.ts new file mode 100644 index 0000000..5b59d88 --- /dev/null +++ b/integration/apps/solid-webpack/src/generated-contract.typecheck.ts @@ -0,0 +1,13 @@ +import { IconsIcon, iconsIconNames } from './sprite/index.js' +import type { IconsIconName, IconsIconProps } from './sprite/index.js' + +const iconName: IconsIconName = 'check' +const props: IconsIconProps = { icon: iconName, width: 24 } + +void IconsIcon +void iconsIconNames +void props + +// @ts-expect-error Generated icon names form a literal union. +const missingIcon: IconsIconName = 'missing' +void missingIcon diff --git a/integration/apps/solid-webpack/src/main.jsx b/integration/apps/solid-webpack/src/main.jsx new file mode 100644 index 0000000..325bebb --- /dev/null +++ b/integration/apps/solid-webpack/src/main.jsx @@ -0,0 +1,36 @@ +import { onMount } from 'solid-js' +import { render } from 'solid-js/web' + +import '@gromlab/svg-sprites/viewer/element' +import { IconsIcon } from './sprite/index.js' +import './style.css' + +const viewerSources = [ + () => import('./sprite/.svg-sprite/svg-sprite.manifest.js'), +] + +function App() { + let viewer + + onMount(() => { + viewer.sources = viewerSources + }) + + return ( +
+

Solid + Webpack

+ + +
+ ) +} + +render(() => , document.getElementById('root')) diff --git a/integration/apps/solid-webpack/src/sprite/.gitignore b/integration/apps/solid-webpack/src/sprite/.gitignore new file mode 100644 index 0000000..7f3344e --- /dev/null +++ b/integration/apps/solid-webpack/src/sprite/.gitignore @@ -0,0 +1,2 @@ +# @generated by @gromlab/svg-sprites. Do not edit. +/.svg-sprite/ diff --git a/integration/apps/solid-webpack/src/sprite/index.d.ts b/integration/apps/solid-webpack/src/sprite/index.d.ts new file mode 100644 index 0000000..ceeead8 --- /dev/null +++ b/integration/apps/solid-webpack/src/sprite/index.d.ts @@ -0,0 +1 @@ +export * from './.svg-sprite/index.js' diff --git a/integration/apps/solid-webpack/src/sprite/index.js b/integration/apps/solid-webpack/src/sprite/index.js new file mode 100644 index 0000000..ceeead8 --- /dev/null +++ b/integration/apps/solid-webpack/src/sprite/index.js @@ -0,0 +1 @@ +export * from './.svg-sprite/index.js' diff --git a/integration/apps/solid-webpack/src/sprite/svg-sprite.config.ts b/integration/apps/solid-webpack/src/sprite/svg-sprite.config.ts new file mode 100644 index 0000000..994addb --- /dev/null +++ b/integration/apps/solid-webpack/src/sprite/svg-sprite.config.ts @@ -0,0 +1,8 @@ +import { defineSpriteConfig } from '@gromlab/svg-sprites' + +export default defineSpriteConfig({ + mode: 'solid@webpack', + name: 'icons', + input: '../../../../fixtures/icons/check.svg', + generatedNotice: false, +}) diff --git a/integration/apps/solid-webpack/src/style.css b/integration/apps/solid-webpack/src/style.css new file mode 100644 index 0000000..5171e72 --- /dev/null +++ b/integration/apps/solid-webpack/src/style.css @@ -0,0 +1,15 @@ +:root { + font-family: system-ui, sans-serif; + color: #172033; + background: #fff; +} + +body { + margin: 0; + padding: 40px; +} + +gromlab-sprite-viewer { + display: block; + margin-top: 32px; +} diff --git a/integration/apps/solid-webpack/tsconfig.json b/integration/apps/solid-webpack/tsconfig.json new file mode 100644 index 0000000..0d8dcf3 --- /dev/null +++ b/integration/apps/solid-webpack/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "strict": true, + "noEmit": true, + "skipLibCheck": true + }, + "include": ["src/**/*.ts"] +} diff --git a/integration/apps/solid-webpack/webpack.config.js b/integration/apps/solid-webpack/webpack.config.js new file mode 100644 index 0000000..7dcb6c0 --- /dev/null +++ b/integration/apps/solid-webpack/webpack.config.js @@ -0,0 +1,46 @@ +import path from 'node:path' +import { fileURLToPath } from 'node:url' + +import HtmlWebpackPlugin from 'html-webpack-plugin' + +const root = path.dirname(fileURLToPath(import.meta.url)) + +export default { + entry: './src/main.jsx', + output: { + path: path.join(root, 'dist'), + filename: 'assets/app.[contenthash].js', + assetModuleFilename: 'assets/[name].[contenthash][ext]', + clean: true, + }, + resolve: { + extensions: ['.jsx', '.js'], + conditionNames: ['browser', 'import', 'require', 'default'], + }, + module: { + rules: [ + { + test: /\.jsx$/, + exclude: /node_modules/, + use: { + loader: 'babel-loader', + options: { presets: ['babel-preset-solid'] }, + }, + }, + { + test: /\.module\.css$/, + use: ['style-loader', { loader: 'css-loader', options: { modules: { namedExport: false } } }], + }, + { + test: /\.css$/, + exclude: /\.module\.css$/, + use: ['style-loader', 'css-loader'], + }, + ], + }, + plugins: [ + new HtmlWebpackPlugin({ + templateContent: 'Solid Webpack sprite fixture
', + }), + ], +} diff --git a/integration/apps/svelte-vite/index.html b/integration/apps/svelte-vite/index.html index 049c910..6c1d55e 100644 --- a/integration/apps/svelte-vite/index.html +++ b/integration/apps/svelte-vite/index.html @@ -7,6 +7,6 @@
- + diff --git a/integration/apps/svelte-vite/package.json b/integration/apps/svelte-vite/package.json index ed009b7..cbfe7ff 100644 --- a/integration/apps/svelte-vite/package.json +++ b/integration/apps/svelte-vite/package.json @@ -4,7 +4,7 @@ "version": "0.0.0", "type": "module", "scripts": { - "sprites": "svg-sprites --mode legacy .", + "sprites": "svg-sprites src/sprite/svg-sprite.config.ts", "typecheck": "svelte-check --tsconfig ./tsconfig.json", "build": "npm run sprites && svelte-check --tsconfig ./tsconfig.json && vite build", "dev": "npm run sprites && vite" diff --git a/integration/apps/svelte-vite/src/App.svelte b/integration/apps/svelte-vite/src/App.svelte index 1c6c801..62123f0 100644 --- a/integration/apps/svelte-vite/src/App.svelte +++ b/integration/apps/svelte-vite/src/App.svelte @@ -1,13 +1,33 @@ + +

Svelte + Vite

- - - + width="64" + height="64" + style="--icon-color-1: #16a34a" + /> +
diff --git a/integration/apps/svelte-vite/src/app.d.ts b/integration/apps/svelte-vite/src/app.d.ts deleted file mode 100644 index fc8b85a..0000000 --- a/integration/apps/svelte-vite/src/app.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -declare module '*.svelte' { - import type { Component } from 'svelte' - - const component: Component> - export default component -} diff --git a/integration/apps/svelte-vite/src/main.js b/integration/apps/svelte-vite/src/main.js new file mode 100644 index 0000000..5494ad8 --- /dev/null +++ b/integration/apps/svelte-vite/src/main.js @@ -0,0 +1,10 @@ +import { mount } from 'svelte' + +import App from './App.svelte' + +const target = document.getElementById('app') +if (!target) throw new Error('Svelte mount target was not found.') + +mount(App, { + target, +}) diff --git a/integration/apps/svelte-vite/src/main.ts b/integration/apps/svelte-vite/src/main.ts deleted file mode 100644 index 5307270..0000000 --- a/integration/apps/svelte-vite/src/main.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { mount } from 'svelte' - -import App from './App.svelte' - -mount(App, { - target: document.getElementById('app')!, -}) diff --git a/integration/apps/svelte-vite/src/sprite/.gitignore b/integration/apps/svelte-vite/src/sprite/.gitignore new file mode 100644 index 0000000..7f3344e --- /dev/null +++ b/integration/apps/svelte-vite/src/sprite/.gitignore @@ -0,0 +1,2 @@ +# @generated by @gromlab/svg-sprites. Do not edit. +/.svg-sprite/ diff --git a/integration/apps/svelte-vite/src/sprite/index.d.ts b/integration/apps/svelte-vite/src/sprite/index.d.ts new file mode 100644 index 0000000..ceeead8 --- /dev/null +++ b/integration/apps/svelte-vite/src/sprite/index.d.ts @@ -0,0 +1 @@ +export * from './.svg-sprite/index.js' diff --git a/integration/apps/svelte-vite/src/sprite/index.js b/integration/apps/svelte-vite/src/sprite/index.js new file mode 100644 index 0000000..ceeead8 --- /dev/null +++ b/integration/apps/svelte-vite/src/sprite/index.js @@ -0,0 +1 @@ +export * from './.svg-sprite/index.js' diff --git a/integration/apps/svelte-vite/src/sprite/svg-sprite.config.ts b/integration/apps/svelte-vite/src/sprite/svg-sprite.config.ts new file mode 100644 index 0000000..b60a5b1 --- /dev/null +++ b/integration/apps/svelte-vite/src/sprite/svg-sprite.config.ts @@ -0,0 +1,8 @@ +import { defineSpriteConfig } from '@gromlab/svg-sprites' + +export default defineSpriteConfig({ + mode: 'svelte@vite', + name: 'icons', + input: '../../../../fixtures/icons/check.svg', + generatedNotice: false, +}) diff --git a/integration/apps/svelte-vite/src/type-probe.ts b/integration/apps/svelte-vite/src/type-probe.ts new file mode 100644 index 0000000..1ab0cf8 --- /dev/null +++ b/integration/apps/svelte-vite/src/type-probe.ts @@ -0,0 +1,9 @@ +import type { ComponentProps } from 'svelte' +import { IconsIcon } from './sprite/index.js' +import type { IconsIconName, IconsIconProps } from './sprite/index.js' + +const icon: IconsIconName = 'check' +const inferred: ComponentProps = { icon, 'aria-label': 'Check icon' } +const declared: IconsIconProps = inferred + +void declared diff --git a/integration/apps/svelte-vite/svg-sprites.config.ts b/integration/apps/svelte-vite/svg-sprites.config.ts deleted file mode 100644 index 24da7e0..0000000 --- a/integration/apps/svelte-vite/svg-sprites.config.ts +++ /dev/null @@ -1,11 +0,0 @@ -export default { - output: 'public/sprites', - preview: false, - sprites: [ - { - name: 'icons', - input: '../../fixtures/icons', - format: 'symbol', - }, - ], -} diff --git a/integration/apps/svelte-vite/tsconfig.json b/integration/apps/svelte-vite/tsconfig.json index 1db664c..02c92e2 100644 --- a/integration/apps/svelte-vite/tsconfig.json +++ b/integration/apps/svelte-vite/tsconfig.json @@ -5,10 +5,12 @@ "moduleResolution": "Bundler", "lib": ["ES2022", "DOM", "DOM.Iterable"], "allowArbitraryExtensions": true, + "allowJs": true, + "checkJs": true, "isolatedModules": true, "strict": true, "noEmit": true, "skipLibCheck": true }, - "include": ["src/**/*.ts", "src/**/*.svelte"] + "include": ["src/**/*.js", "src/**/*.ts", "src/**/*.svelte"] } diff --git a/integration/apps/svelte-webpack/package.json b/integration/apps/svelte-webpack/package.json new file mode 100644 index 0000000..ed80ac2 --- /dev/null +++ b/integration/apps/svelte-webpack/package.json @@ -0,0 +1,25 @@ +{ + "name": "@svg-sprites-fixtures/svelte-webpack", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "sprites": "svg-sprites src/sprite/svg-sprite.config.ts", + "typecheck": "svelte-check --tsconfig ./tsconfig.json", + "build": "npm run sprites && svelte-check --tsconfig ./tsconfig.json && webpack --mode production", + "dev": "npm run sprites && webpack serve --mode development" + }, + "dependencies": { + "svelte": "5.56.4" + }, + "devDependencies": { + "@gromlab/svg-sprites": "file:../../..", + "html-webpack-plugin": "5.6.7", + "svelte-check": "4.7.2", + "svelte-loader": "3.2.4", + "typescript": "6.0.2", + "webpack": "5.108.4", + "webpack-cli": "7.2.1", + "webpack-dev-server": "5.2.2" + } +} diff --git a/integration/apps/svelte-webpack/src/App.svelte b/integration/apps/svelte-webpack/src/App.svelte new file mode 100644 index 0000000..bc08720 --- /dev/null +++ b/integration/apps/svelte-webpack/src/App.svelte @@ -0,0 +1,49 @@ + + +
+

Svelte + Webpack

+ + +
+ + diff --git a/integration/apps/svelte-webpack/src/main.js b/integration/apps/svelte-webpack/src/main.js new file mode 100644 index 0000000..5494ad8 --- /dev/null +++ b/integration/apps/svelte-webpack/src/main.js @@ -0,0 +1,10 @@ +import { mount } from 'svelte' + +import App from './App.svelte' + +const target = document.getElementById('app') +if (!target) throw new Error('Svelte mount target was not found.') + +mount(App, { + target, +}) diff --git a/integration/apps/svelte-webpack/src/sprite/.gitignore b/integration/apps/svelte-webpack/src/sprite/.gitignore new file mode 100644 index 0000000..7f3344e --- /dev/null +++ b/integration/apps/svelte-webpack/src/sprite/.gitignore @@ -0,0 +1,2 @@ +# @generated by @gromlab/svg-sprites. Do not edit. +/.svg-sprite/ diff --git a/integration/apps/svelte-webpack/src/sprite/index.d.ts b/integration/apps/svelte-webpack/src/sprite/index.d.ts new file mode 100644 index 0000000..ceeead8 --- /dev/null +++ b/integration/apps/svelte-webpack/src/sprite/index.d.ts @@ -0,0 +1 @@ +export * from './.svg-sprite/index.js' diff --git a/integration/apps/svelte-webpack/src/sprite/index.js b/integration/apps/svelte-webpack/src/sprite/index.js new file mode 100644 index 0000000..ceeead8 --- /dev/null +++ b/integration/apps/svelte-webpack/src/sprite/index.js @@ -0,0 +1 @@ +export * from './.svg-sprite/index.js' diff --git a/integration/apps/svelte-webpack/src/sprite/svg-sprite.config.ts b/integration/apps/svelte-webpack/src/sprite/svg-sprite.config.ts new file mode 100644 index 0000000..fc33568 --- /dev/null +++ b/integration/apps/svelte-webpack/src/sprite/svg-sprite.config.ts @@ -0,0 +1,8 @@ +import { defineSpriteConfig } from '@gromlab/svg-sprites' + +export default defineSpriteConfig({ + mode: 'svelte@webpack', + name: 'icons', + input: '../../../../fixtures/icons/check.svg', + generatedNotice: false, +}) diff --git a/integration/apps/svelte-webpack/src/type-probe.ts b/integration/apps/svelte-webpack/src/type-probe.ts new file mode 100644 index 0000000..04b87b9 --- /dev/null +++ b/integration/apps/svelte-webpack/src/type-probe.ts @@ -0,0 +1,9 @@ +import type { ComponentProps } from 'svelte' +import { IconsIcon } from './sprite/index.js' +import type { IconsIconName, IconsIconProps } from './sprite/index.js' + +const icon: IconsIconName = 'check' +const inferred: ComponentProps = { icon, width: 64, height: 64 } +const declared: IconsIconProps = inferred + +void declared diff --git a/integration/apps/svelte-webpack/tsconfig.json b/integration/apps/svelte-webpack/tsconfig.json new file mode 100644 index 0000000..3541adb --- /dev/null +++ b/integration/apps/svelte-webpack/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "allowJs": true, + "checkJs": true, + "strict": true, + "noEmit": true, + "skipLibCheck": true + }, + "include": ["src/**/*.js", "src/**/*.ts", "src/**/*.svelte"] +} diff --git a/integration/apps/svelte-webpack/webpack.config.js b/integration/apps/svelte-webpack/webpack.config.js new file mode 100644 index 0000000..eefd8fd --- /dev/null +++ b/integration/apps/svelte-webpack/webpack.config.js @@ -0,0 +1,40 @@ +import path from 'node:path' +import { fileURLToPath } from 'node:url' + +import HtmlWebpackPlugin from 'html-webpack-plugin' + +const root = path.dirname(fileURLToPath(import.meta.url)) + +export default { + entry: './src/main.js', + output: { + path: path.join(root, 'dist'), + filename: 'assets/app.[contenthash].js', + assetModuleFilename: 'assets/[name].[contenthash][ext]', + clean: true, + }, + resolve: { + extensions: ['.svelte', '.js'], + conditionNames: ['svelte', 'browser', '...'], + mainFields: ['svelte', 'browser', 'module', 'main'], + }, + module: { + rules: [ + { + test: /\.svelte$/, + use: { + loader: 'svelte-loader', + options: { + emitCss: false, + hotReload: false, + }, + }, + }, + ], + }, + plugins: [ + new HtmlWebpackPlugin({ + templateContent: 'Svelte Webpack sprite fixture
', + }), + ], +} diff --git a/integration/apps/sveltekit/package.json b/integration/apps/sveltekit/package.json index 7d10104..95403b6 100644 --- a/integration/apps/sveltekit/package.json +++ b/integration/apps/sveltekit/package.json @@ -4,9 +4,9 @@ "version": "0.0.0", "type": "module", "scripts": { - "sprites": "svg-sprites --mode legacy .", + "sprites": "svg-sprites src/sprite/svg-sprite.config.ts", "typecheck": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", - "build": "npm run sprites && svelte-kit sync && vite build", + "build": "npm run sprites && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json && vite build", "dev": "npm run sprites && vite dev", "start": "node build/index.js" }, diff --git a/integration/apps/sveltekit/src/routes/+page.svelte b/integration/apps/sveltekit/src/routes/+page.svelte index 4da36fb..ca33f38 100644 --- a/integration/apps/sveltekit/src/routes/+page.svelte +++ b/integration/apps/sveltekit/src/routes/+page.svelte @@ -1,17 +1,43 @@ + + SvelteKit sprite fixture

SvelteKit

- - - + width="64" + height="64" + style="--icon-color-1: #16a34a" + /> +
diff --git a/integration/apps/sveltekit/src/sprite/.gitignore b/integration/apps/sveltekit/src/sprite/.gitignore new file mode 100644 index 0000000..7f3344e --- /dev/null +++ b/integration/apps/sveltekit/src/sprite/.gitignore @@ -0,0 +1,2 @@ +# @generated by @gromlab/svg-sprites. Do not edit. +/.svg-sprite/ diff --git a/integration/apps/sveltekit/src/sprite/index.d.ts b/integration/apps/sveltekit/src/sprite/index.d.ts new file mode 100644 index 0000000..ceeead8 --- /dev/null +++ b/integration/apps/sveltekit/src/sprite/index.d.ts @@ -0,0 +1 @@ +export * from './.svg-sprite/index.js' diff --git a/integration/apps/sveltekit/src/sprite/index.js b/integration/apps/sveltekit/src/sprite/index.js new file mode 100644 index 0000000..ceeead8 --- /dev/null +++ b/integration/apps/sveltekit/src/sprite/index.js @@ -0,0 +1 @@ +export * from './.svg-sprite/index.js' diff --git a/integration/apps/sveltekit/src/sprite/svg-sprite.config.ts b/integration/apps/sveltekit/src/sprite/svg-sprite.config.ts new file mode 100644 index 0000000..1a889ba --- /dev/null +++ b/integration/apps/sveltekit/src/sprite/svg-sprite.config.ts @@ -0,0 +1,8 @@ +import { defineSpriteConfig } from '@gromlab/svg-sprites' + +export default defineSpriteConfig({ + mode: 'sveltekit@vite', + name: 'icons', + input: '../../../../fixtures/icons/check.svg', + generatedNotice: false, +}) diff --git a/integration/apps/sveltekit/src/type-probe.ts b/integration/apps/sveltekit/src/type-probe.ts new file mode 100644 index 0000000..df995a4 --- /dev/null +++ b/integration/apps/sveltekit/src/type-probe.ts @@ -0,0 +1,9 @@ +import type { ComponentProps } from 'svelte' +import { IconsIcon } from './sprite/index.js' +import type { IconsIconName, IconsIconProps } from './sprite/index.js' + +const icon: IconsIconName = 'check' +const inferred: ComponentProps = { icon, role: 'img' } +const declared: IconsIconProps = inferred + +void declared diff --git a/integration/apps/sveltekit/svg-sprites.config.ts b/integration/apps/sveltekit/svg-sprites.config.ts deleted file mode 100644 index dc80240..0000000 --- a/integration/apps/sveltekit/svg-sprites.config.ts +++ /dev/null @@ -1,11 +0,0 @@ -export default { - output: 'static/sprites', - preview: false, - sprites: [ - { - name: 'icons', - input: '../../fixtures/icons', - format: 'symbol', - }, - ], -} diff --git a/integration/apps/vue-vite/index.html b/integration/apps/vue-vite/index.html index ac2f7be..049a6a4 100644 --- a/integration/apps/vue-vite/index.html +++ b/integration/apps/vue-vite/index.html @@ -7,6 +7,6 @@
- + diff --git a/integration/apps/vue-vite/package.json b/integration/apps/vue-vite/package.json index 59ea81c..7eec46a 100644 --- a/integration/apps/vue-vite/package.json +++ b/integration/apps/vue-vite/package.json @@ -4,7 +4,7 @@ "version": "0.0.0", "type": "module", "scripts": { - "sprites": "svg-sprites --mode legacy .", + "sprites": "svg-sprites src/sprite/svg-sprite.config.ts", "typecheck": "vue-tsc --noEmit", "build": "npm run sprites && vue-tsc --noEmit && vite build", "dev": "npm run sprites && vite" diff --git a/integration/apps/vue-vite/src/App.vue b/integration/apps/vue-vite/src/App.vue index eb2129e..c1d0901 100644 --- a/integration/apps/vue-vite/src/App.vue +++ b/integration/apps/vue-vite/src/App.vue @@ -1,14 +1,29 @@ + + @@ -24,9 +39,8 @@ body { padding: 40px; } -[data-testid='icon'] { - width: 64px; - height: 64px; - color: #16a34a; +gromlab-sprite-viewer { + display: block; + margin-top: 32px; } diff --git a/integration/apps/vue-vite/src/generated-contract.typecheck.ts b/integration/apps/vue-vite/src/generated-contract.typecheck.ts new file mode 100644 index 0000000..878a1c3 --- /dev/null +++ b/integration/apps/vue-vite/src/generated-contract.typecheck.ts @@ -0,0 +1,9 @@ +import type { IconsIconName, IconsIconProps } from './sprite/.svg-sprite/index.js' + +const icon: IconsIconName = 'check' +const props: IconsIconProps = { icon, width: 24, height: 24 } +void props + +// @ts-expect-error generated icon names are a closed union +const invalidIcon: IconsIconName = 'missing' +void invalidIcon diff --git a/integration/apps/vue-vite/src/main.ts b/integration/apps/vue-vite/src/main.js similarity index 100% rename from integration/apps/vue-vite/src/main.ts rename to integration/apps/vue-vite/src/main.js diff --git a/integration/apps/vue-vite/src/sprite/.gitignore b/integration/apps/vue-vite/src/sprite/.gitignore new file mode 100644 index 0000000..7f3344e --- /dev/null +++ b/integration/apps/vue-vite/src/sprite/.gitignore @@ -0,0 +1,2 @@ +# @generated by @gromlab/svg-sprites. Do not edit. +/.svg-sprite/ diff --git a/integration/apps/vue-vite/src/sprite/index.d.ts b/integration/apps/vue-vite/src/sprite/index.d.ts new file mode 100644 index 0000000..ceeead8 --- /dev/null +++ b/integration/apps/vue-vite/src/sprite/index.d.ts @@ -0,0 +1 @@ +export * from './.svg-sprite/index.js' diff --git a/integration/apps/vue-vite/src/sprite/index.js b/integration/apps/vue-vite/src/sprite/index.js new file mode 100644 index 0000000..411538d --- /dev/null +++ b/integration/apps/vue-vite/src/sprite/index.js @@ -0,0 +1 @@ +export * from './.svg-sprite' diff --git a/integration/apps/vue-vite/src/sprite/svg-sprite.config.ts b/integration/apps/vue-vite/src/sprite/svg-sprite.config.ts new file mode 100644 index 0000000..a467135 --- /dev/null +++ b/integration/apps/vue-vite/src/sprite/svg-sprite.config.ts @@ -0,0 +1,8 @@ +import { defineSpriteConfig } from '@gromlab/svg-sprites' + +export default defineSpriteConfig({ + mode: 'vue@vite', + name: 'icons', + input: '../../../../fixtures/icons/check.svg', + generatedNotice: false, +}) diff --git a/integration/apps/vue-vite/svg-sprites.config.ts b/integration/apps/vue-vite/svg-sprites.config.ts deleted file mode 100644 index 24da7e0..0000000 --- a/integration/apps/vue-vite/svg-sprites.config.ts +++ /dev/null @@ -1,11 +0,0 @@ -export default { - output: 'public/sprites', - preview: false, - sprites: [ - { - name: 'icons', - input: '../../fixtures/icons', - format: 'symbol', - }, - ], -} diff --git a/integration/apps/vue-vite/vite.config.ts b/integration/apps/vue-vite/vite.config.ts index 544087a..3ac952e 100644 --- a/integration/apps/vue-vite/vite.config.ts +++ b/integration/apps/vue-vite/vite.config.ts @@ -2,5 +2,13 @@ import vue from '@vitejs/plugin-vue' import { defineConfig } from 'vite' export default defineConfig({ - plugins: [vue()], + plugins: [ + vue({ + template: { + compilerOptions: { + isCustomElement: (tag) => tag === 'gromlab-sprite-viewer', + }, + }, + }), + ], }) diff --git a/integration/apps/vue-webpack/package.json b/integration/apps/vue-webpack/package.json new file mode 100644 index 0000000..e688c43 --- /dev/null +++ b/integration/apps/vue-webpack/package.json @@ -0,0 +1,28 @@ +{ + "name": "@svg-sprites-fixtures/vue-webpack", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "sprites": "svg-sprites src/sprite/svg-sprite.config.ts", + "typecheck": "vue-tsc --noEmit", + "build": "npm run sprites && vue-tsc --noEmit && webpack --mode production", + "dev": "npm run sprites && webpack serve --mode development" + }, + "dependencies": { + "vue": "3.5.39" + }, + "devDependencies": { + "@gromlab/svg-sprites": "file:../../..", + "@vue/compiler-sfc": "3.5.39", + "css-loader": "7.1.4", + "html-webpack-plugin": "5.6.7", + "style-loader": "4.0.0", + "typescript": "6.0.2", + "vue-loader": "17.4.2", + "vue-tsc": "3.3.7", + "webpack": "5.108.4", + "webpack-cli": "7.2.1", + "webpack-dev-server": "5.2.2" + } +} diff --git a/integration/apps/vue-webpack/src/App.vue b/integration/apps/vue-webpack/src/App.vue new file mode 100644 index 0000000..b61e11e --- /dev/null +++ b/integration/apps/vue-webpack/src/App.vue @@ -0,0 +1,46 @@ + + + + + diff --git a/integration/apps/vue-webpack/src/generated-contract.typecheck.ts b/integration/apps/vue-webpack/src/generated-contract.typecheck.ts new file mode 100644 index 0000000..144d403 --- /dev/null +++ b/integration/apps/vue-webpack/src/generated-contract.typecheck.ts @@ -0,0 +1,15 @@ +import { IconsIcon } from './sprite/.svg-sprite/index.js' +import type { IconsIconName, IconsIconProps } from './sprite/.svg-sprite/index.js' + +const iconName: IconsIconName = 'check' +const iconProps: IconsIconProps = { + icon: iconName, + width: 64, + style: { '--icon-color-1': '#16a34a' }, +} + +void [IconsIcon, iconProps] + +// @ts-expect-error Generated icon names form a literal union. +const missingIcon: IconsIconName = 'missing' +void missingIcon diff --git a/integration/apps/vue-webpack/src/main.js b/integration/apps/vue-webpack/src/main.js new file mode 100644 index 0000000..912d54f --- /dev/null +++ b/integration/apps/vue-webpack/src/main.js @@ -0,0 +1,5 @@ +import { createApp } from 'vue' + +import App from './App.vue' + +createApp(App).mount('#app') diff --git a/integration/apps/vue-webpack/src/sprite/.gitignore b/integration/apps/vue-webpack/src/sprite/.gitignore new file mode 100644 index 0000000..7f3344e --- /dev/null +++ b/integration/apps/vue-webpack/src/sprite/.gitignore @@ -0,0 +1,2 @@ +# @generated by @gromlab/svg-sprites. Do not edit. +/.svg-sprite/ diff --git a/integration/apps/vue-webpack/src/sprite/index.d.ts b/integration/apps/vue-webpack/src/sprite/index.d.ts new file mode 100644 index 0000000..ceeead8 --- /dev/null +++ b/integration/apps/vue-webpack/src/sprite/index.d.ts @@ -0,0 +1 @@ +export * from './.svg-sprite/index.js' diff --git a/integration/apps/vue-webpack/src/sprite/index.js b/integration/apps/vue-webpack/src/sprite/index.js new file mode 100644 index 0000000..ceeead8 --- /dev/null +++ b/integration/apps/vue-webpack/src/sprite/index.js @@ -0,0 +1 @@ +export * from './.svg-sprite/index.js' diff --git a/integration/apps/vue-webpack/src/sprite/svg-sprite.config.ts b/integration/apps/vue-webpack/src/sprite/svg-sprite.config.ts new file mode 100644 index 0000000..24214ed --- /dev/null +++ b/integration/apps/vue-webpack/src/sprite/svg-sprite.config.ts @@ -0,0 +1,8 @@ +import { defineSpriteConfig } from '@gromlab/svg-sprites' + +export default defineSpriteConfig({ + mode: 'vue@webpack', + name: 'icons', + input: '../../../../fixtures/icons/check.svg', + generatedNotice: false, +}) diff --git a/integration/apps/vue-webpack/tsconfig.json b/integration/apps/vue-webpack/tsconfig.json new file mode 100644 index 0000000..d015a5b --- /dev/null +++ b/integration/apps/vue-webpack/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "allowJs": true, + "strict": true, + "noEmit": true, + "skipLibCheck": true + }, + "include": ["src/**/*.js", "src/**/*.ts", "src/**/*.vue"] +} diff --git a/integration/apps/vue-webpack/webpack.config.js b/integration/apps/vue-webpack/webpack.config.js new file mode 100644 index 0000000..d975ff1 --- /dev/null +++ b/integration/apps/vue-webpack/webpack.config.js @@ -0,0 +1,60 @@ +import path from 'node:path' +import { fileURLToPath } from 'node:url' + +import HtmlWebpackPlugin from 'html-webpack-plugin' +import webpack from 'webpack' +import { VueLoaderPlugin } from 'vue-loader' + +const root = path.dirname(fileURLToPath(import.meta.url)) + +export default { + entry: './src/main.js', + output: { + path: path.join(root, 'dist'), + filename: 'assets/app.[contenthash].js', + assetModuleFilename: 'assets/[name].[contenthash][ext]', + clean: true, + }, + resolve: { + extensions: ['.js', '.vue'], + }, + module: { + rules: [ + { + test: /\.vue$/, + loader: 'vue-loader', + options: { + compilerOptions: { + isCustomElement: (tag) => tag === 'gromlab-sprite-viewer', + }, + }, + }, + { + test: /\.module\.css$/, + use: [ + 'style-loader', + { + loader: 'css-loader', + options: { modules: { namedExport: false } }, + }, + ], + }, + { + test: /\.css$/, + exclude: /\.module\.css$/, + use: ['style-loader', 'css-loader'], + }, + ], + }, + plugins: [ + new VueLoaderPlugin(), + new webpack.DefinePlugin({ + __VUE_OPTIONS_API__: JSON.stringify(true), + __VUE_PROD_DEVTOOLS__: JSON.stringify(false), + __VUE_PROD_HYDRATION_MISMATCH_DETAILS__: JSON.stringify(false), + }), + new HtmlWebpackPlugin({ + templateContent: 'Vue Webpack sprite fixture
', + }), + ], +} diff --git a/integration/package-lock.json b/integration/package-lock.json index 52290d2..cd8253a 100644 --- a/integration/package-lock.json +++ b/integration/package-lock.json @@ -23,8 +23,10 @@ "dependencies": { "colorette": "^2.0.20", "jiti": "^2.6.1", + "lit": "^3.3.3", "svg-sprite": "^2.0.4", - "tinyglobby": "^0.2.17" + "tinyglobby": "^0.2.17", + "vanilla-colorful": "^0.7.2" }, "bin": { "svg-sprites": "dist/cli.js" @@ -35,12 +37,10 @@ "@types/react-dom": "^19.2.3", "@types/svg-sprite": "^0.0.39", "jsdom": "^26.1.0", - "lit": "^3.3.3", "react": "^19.2.5", "react-dom": "^19.2.5", "tsup": "^8.4.0", - "typescript": "^5.8.3", - "vanilla-colorful": "^0.7.2" + "typescript": "^5.8.3" }, "engines": { "node": ">=18" @@ -54,6 +54,33 @@ } } }, + "apps/alpine-vite": { + "name": "@svg-sprites-fixtures/alpine-vite", + "version": "0.0.0", + "dependencies": { + "alpinejs": "3.15.12" + }, + "devDependencies": { + "@gromlab/svg-sprites": "file:../../..", + "typescript": "6.0.2", + "vite": "8.1.4" + } + }, + "apps/alpine-webpack": { + "name": "@svg-sprites-fixtures/alpine-webpack", + "version": "0.0.0", + "dependencies": { + "alpinejs": "3.15.12" + }, + "devDependencies": { + "@gromlab/svg-sprites": "file:../../..", + "html-webpack-plugin": "5.6.7", + "typescript": "6.0.2", + "webpack": "5.108.4", + "webpack-cli": "7.2.1", + "webpack-dev-server": "5.2.2" + } + }, "apps/angular": { "name": "@svg-sprites-fixtures/angular", "version": "0.0.0", @@ -74,6 +101,854 @@ "typescript": "5.9.3" } }, + "apps/angular-webpack": { + "name": "@svg-sprites-fixtures/angular-webpack", + "version": "0.0.0", + "dependencies": { + "@angular/common": "21.2.18", + "@angular/compiler": "21.2.18", + "@angular/core": "21.2.18", + "@angular/platform-browser": "21.2.18", + "rxjs": "7.8.2", + "tslib": "2.8.1", + "zone.js": "0.16.2" + }, + "devDependencies": { + "@angular/compiler-cli": "21.2.18", + "@babel/core": "7.28.6", + "@gromlab/svg-sprites": "file:../../..", + "@ngtools/webpack": "21.2.18", + "babel-loader": "10.0.0", + "html-webpack-plugin": "5.6.6", + "raw-loader": "4.0.2", + "typescript": "5.9.3", + "webpack": "5.105.4", + "webpack-cli": "6.0.1", + "webpack-dev-server": "5.2.3" + } + }, + "apps/angular-webpack/node_modules/@babel/core": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.6.tgz", + "integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/generator": "^7.28.6", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "apps/angular-webpack/node_modules/@babel/core/node_modules/debug": { + "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" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "apps/angular-webpack/node_modules/@discoveryjs/json-ext": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.6.3.tgz", + "integrity": "sha512-4B4OijXeVNOPZlYA2oEwWOTkzyltLao+xbotHQeqN++Rv27Y6s818+n2Qkp8q+Fxhn0t/5lA5X1Mxktud8eayQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.17.0" + } + }, + "apps/angular-webpack/node_modules/@ngtools/webpack": { + "version": "21.2.18", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-21.2.18.tgz", + "integrity": "sha512-rWXbsI7G8eK4vJ4Rn+uQFlytn/GcSgriviT4KpyWwuBFI8/kBXWVsF3C65CADrVfIocHV3Gcu4WQv2ee3YoTVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "@angular/compiler-cli": "^21.0.0", + "typescript": ">=5.9 <6.0", + "webpack": "^5.54.0" + } + }, + "apps/angular-webpack/node_modules/@webpack-cli/configtest": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-3.0.1.tgz", + "integrity": "sha512-u8d0pJ5YFgneF/GuvEiDA61Tf1VDomHHYMjv/wc9XzYj7nopltpG96nXN5dJRstxZhcNpV1g+nT6CydO7pHbjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12.0" + }, + "peerDependencies": { + "webpack": "^5.82.0", + "webpack-cli": "6.x.x" + } + }, + "apps/angular-webpack/node_modules/@webpack-cli/info": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-3.0.1.tgz", + "integrity": "sha512-coEmDzc2u/ffMvuW9aCjoRzNSPDl/XLuhPdlFRpT9tZHmJ/039az33CE7uH+8s0uL1j5ZNtfdv0HkfaKRBGJsQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12.0" + }, + "peerDependencies": { + "webpack": "^5.82.0", + "webpack-cli": "6.x.x" + } + }, + "apps/angular-webpack/node_modules/@webpack-cli/serve": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-3.0.1.tgz", + "integrity": "sha512-sbgw03xQaCLiT6gcY/6u3qBDn01CWw/nbaXl3gTdTFuJJ75Gffv3E3DBpgvY2fkkrdS1fpjaXNOmJlnbtKauKg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12.0" + }, + "peerDependencies": { + "webpack": "^5.82.0", + "webpack-cli": "6.x.x" + }, + "peerDependenciesMeta": { + "webpack-dev-server": { + "optional": true + } + } + }, + "apps/angular-webpack/node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "apps/angular-webpack/node_modules/body-parser": { + "version": "1.20.6", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.6.tgz", + "integrity": "sha512-p5tAzS57i5MV9fZFDj9LeIiTZEufbSe2eDozP+ElheSUq1m74CRq1jI4mYNDdVs9vQztXFLuk/Gd6BWTdwRJ5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.15.1", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "apps/angular-webpack/node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "apps/angular-webpack/node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "apps/angular-webpack/node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "apps/angular-webpack/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "apps/angular-webpack/node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "apps/angular-webpack/node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", + "dev": true, + "license": "MIT" + }, + "apps/angular-webpack/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "apps/angular-webpack/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "apps/angular-webpack/node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "apps/angular-webpack/node_modules/express": { + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz", + "integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "~1.20.5", + "content-disposition": "~0.5.4", + "content-type": "~1.0.4", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "~0.1.12", + "proxy-addr": "~2.0.7", + "qs": "~6.15.1", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "~0.19.0", + "serve-static": "~1.16.2", + "setprototypeof": "1.2.0", + "statuses": "~2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "apps/angular-webpack/node_modules/finalhandler": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "statuses": "~2.0.2", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "apps/angular-webpack/node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "apps/angular-webpack/node_modules/html-webpack-plugin": { + "version": "5.6.6", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.6.tgz", + "integrity": "sha512-bLjW01UTrvoWTJQL5LsMRo1SypHW80FTm12OJRSnr3v6YHNhfe+1r0MYUZJMACxnCHURVnBWRwAsWs2yPU9Ezw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/html-minifier-terser": "^6.0.0", + "html-minifier-terser": "^6.0.2", + "lodash": "^4.17.21", + "pretty-error": "^4.0.0", + "tapable": "^2.0.0" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/html-webpack-plugin" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "webpack": "^5.20.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "apps/angular-webpack/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "apps/angular-webpack/node_modules/ipaddr.js": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.4.0.tgz", + "integrity": "sha512-9VGk3HGanVE6JoZXHiCpnGy5X0jYDnN4EA4lntFPj+1vIWlFhIylq2CrrCOJH9EAhc5CYhq18F2Av2tgoAPsYQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "apps/angular-webpack/node_modules/is-wsl": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", + "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "apps/angular-webpack/node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "apps/angular-webpack/node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "apps/angular-webpack/node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "apps/angular-webpack/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "apps/angular-webpack/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "apps/angular-webpack/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "apps/angular-webpack/node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "apps/angular-webpack/node_modules/open": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz", + "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "wsl-utils": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "apps/angular-webpack/node_modules/path-to-regexp": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", + "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", + "dev": true, + "license": "MIT" + }, + "apps/angular-webpack/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "apps/angular-webpack/node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "apps/angular-webpack/node_modules/raw-body": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "apps/angular-webpack/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "apps/angular-webpack/node_modules/selfsigned": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-5.5.0.tgz", + "integrity": "sha512-ftnu3TW4+3eBfLRFnDEkzGxSF/10BJBkaLJuBHZX0kiPS7bRdlpZGu6YGt4KngMkdTwJE6MbjavFpqHvqVt+Ew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/x509": "^1.14.2", + "pkijs": "^3.3.3" + }, + "engines": { + "node": ">=18" + } + }, + "apps/angular-webpack/node_modules/semver": { + "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" + } + }, + "apps/angular-webpack/node_modules/send": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.4.1", + "range-parser": "~1.2.1", + "statuses": "~2.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "apps/angular-webpack/node_modules/serve-static": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "~0.19.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "apps/angular-webpack/node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "apps/angular-webpack/node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "apps/angular-webpack/node_modules/webpack": { + "version": "5.105.4", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.4.tgz", + "integrity": "sha512-jTywjboN9aHxFlToqb0K0Zs9SbBoW4zRUlGzI2tYNxVYcEi/IPpn+Xi4ye5jTLvX2YeLuic/IvxNot+Q1jMoOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.8", + "@types/json-schema": "^7.0.15", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.16.0", + "acorn-import-phases": "^1.0.3", + "browserslist": "^4.28.1", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.20.0", + "es-module-lexer": "^2.0.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.3.1", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^4.3.3", + "tapable": "^2.3.0", + "terser-webpack-plugin": "^5.3.17", + "watchpack": "^2.5.1", + "webpack-sources": "^3.3.4" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "apps/angular-webpack/node_modules/webpack-cli": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-6.0.1.tgz", + "integrity": "sha512-MfwFQ6SfwinsUVi0rNJm7rHZ31GyTcpVE5pgVA3hwFRb7COD4TzjUUwhGWKfO50+xdc2MQPuEBBJoqIMGt3JDw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@discoveryjs/json-ext": "^0.6.1", + "@webpack-cli/configtest": "^3.0.1", + "@webpack-cli/info": "^3.0.1", + "@webpack-cli/serve": "^3.0.1", + "colorette": "^2.0.14", + "commander": "^12.1.0", + "cross-spawn": "^7.0.3", + "envinfo": "^7.14.0", + "fastest-levenshtein": "^1.0.12", + "import-local": "^3.0.2", + "interpret": "^3.1.1", + "rechoir": "^0.8.0", + "webpack-merge": "^6.0.1" + }, + "bin": { + "webpack-cli": "bin/cli.js" + }, + "engines": { + "node": ">=18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.82.0" + }, + "peerDependenciesMeta": { + "webpack-bundle-analyzer": { + "optional": true + }, + "webpack-dev-server": { + "optional": true + } + } + }, + "apps/angular-webpack/node_modules/webpack-dev-server": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-5.2.3.tgz", + "integrity": "sha512-9Gyu2F7+bg4Vv+pjbovuYDhHX+mqdqITykfzdM9UyKqKHlsE5aAjRhR+oOEfXW5vBeu8tarzlJFIZva4ZjAdrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/bonjour": "^3.5.13", + "@types/connect-history-api-fallback": "^1.5.4", + "@types/express": "^4.17.25", + "@types/express-serve-static-core": "^4.17.21", + "@types/serve-index": "^1.9.4", + "@types/serve-static": "^1.15.5", + "@types/sockjs": "^0.3.36", + "@types/ws": "^8.5.10", + "ansi-html-community": "^0.0.8", + "bonjour-service": "^1.2.1", + "chokidar": "^3.6.0", + "colorette": "^2.0.10", + "compression": "^1.8.1", + "connect-history-api-fallback": "^2.0.0", + "express": "^4.22.1", + "graceful-fs": "^4.2.6", + "http-proxy-middleware": "^2.0.9", + "ipaddr.js": "^2.1.0", + "launch-editor": "^2.6.1", + "open": "^10.0.3", + "p-retry": "^6.2.0", + "schema-utils": "^4.2.0", + "selfsigned": "^5.5.0", + "serve-index": "^1.9.1", + "sockjs": "^0.3.24", + "spdy": "^4.0.2", + "webpack-dev-middleware": "^7.4.2", + "ws": "^8.18.0" + }, + "bin": { + "webpack-dev-server": "bin/webpack-dev-server.js" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + }, + "webpack-cli": { + "optional": true + } + } + }, + "apps/angular-webpack/node_modules/wsl-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz", + "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "apps/angular/node_modules/@algolia/abtesting": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@algolia/abtesting/-/abtesting-1.14.1.tgz", @@ -484,162 +1359,6 @@ "yarn": ">= 1.13.0" } }, - "apps/angular/node_modules/@angular/common": { - "version": "21.2.18", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-21.2.18.tgz", - "integrity": "sha512-gZugZ8gX/KkACIZ3/ekoImLu5z8a0Iu9O5b84kh8e+++VUjmkmd19IygBsq/8/fF3vKf0UcIKcx3P6A/gb2DJw==", - "license": "MIT", - "dependencies": { - "tslib": "^2.3.0" - }, - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" - }, - "peerDependencies": { - "@angular/core": "21.2.18", - "rxjs": "^6.5.3 || ^7.4.0" - } - }, - "apps/angular/node_modules/@angular/compiler": { - "version": "21.2.18", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-21.2.18.tgz", - "integrity": "sha512-ccnDuKLuzIa0ayijR+alarsHNWIksuGV/lxGTZ0t6/0+B6J/RXupz6M2IO6ZHHEcg8r7pcrLCUBTyp3FoiRJrg==", - "license": "MIT", - "dependencies": { - "tslib": "^2.3.0" - }, - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" - } - }, - "apps/angular/node_modules/@angular/compiler-cli": { - "version": "21.2.18", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-21.2.18.tgz", - "integrity": "sha512-L5zbIp7YfTTB4I4xT33FgEBanzhppNejzZX0HJOEqKDyDL2jXq+flt83lE0XVuRJ6/zrG+UKj0B+y/CK6Ro7Wg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "7.29.7", - "@jridgewell/sourcemap-codec": "^1.4.14", - "chokidar": "^5.0.0", - "convert-source-map": "^1.5.1", - "reflect-metadata": "^0.2.0", - "semver": "^7.0.0", - "tslib": "^2.3.0", - "yargs": "^18.0.0" - }, - "bin": { - "ng-xi18n": "bundles/src/bin/ng_xi18n.js", - "ngc": "bundles/src/bin/ngc.js" - }, - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" - }, - "peerDependencies": { - "@angular/compiler": "21.2.18", - "typescript": ">=5.9 <6.1" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "apps/angular/node_modules/@angular/core": { - "version": "21.2.18", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-21.2.18.tgz", - "integrity": "sha512-AG4bb6GU0+qp+vVBjc/IhSPjgcRX8QsCb8jzN8dDyhnjsyuuG/LlIiU0l6n65BI9SGKE9m6TfsJ1ObkkAiE5KQ==", - "license": "MIT", - "dependencies": { - "tslib": "^2.3.0" - }, - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" - }, - "peerDependencies": { - "@angular/compiler": "21.2.18", - "rxjs": "^6.5.3 || ^7.4.0", - "zone.js": "~0.15.0 || ~0.16.0" - }, - "peerDependenciesMeta": { - "@angular/compiler": { - "optional": true - }, - "zone.js": { - "optional": true - } - } - }, - "apps/angular/node_modules/@angular/platform-browser": { - "version": "21.2.18", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-21.2.18.tgz", - "integrity": "sha512-zltF+3HrlgtZbYg8U98LhG0dyGm1aHT3Px8//9wjqi/TUY8UlHsYKByL197QtVWDBjf/dekzXdz5c+iyVtanLQ==", - "license": "MIT", - "dependencies": { - "tslib": "^2.3.0" - }, - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" - }, - "peerDependencies": { - "@angular/animations": "21.2.18", - "@angular/common": "21.2.18", - "@angular/core": "21.2.18" - }, - "peerDependenciesMeta": { - "@angular/animations": { - "optional": true - } - } - }, - "apps/angular/node_modules/@babel/core": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", - "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.7", - "@babel/generator": "^7.29.7", - "@babel/helper-compilation-targets": "^7.29.7", - "@babel/helper-module-transforms": "^7.29.7", - "@babel/helpers": "^7.29.7", - "@babel/parser": "^7.29.7", - "@babel/template": "^7.29.7", - "@babel/traverse": "^7.29.7", - "@babel/types": "^7.29.7", - "@jridgewell/remapping": "^2.3.5", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "apps/angular/node_modules/@babel/core/node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" - }, - "apps/angular/node_modules/@babel/core/node_modules/semver": { - "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" - } - }, "apps/angular/node_modules/@inquirer/ansi": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.2.tgz", @@ -1968,6 +2687,33 @@ "typescript": "6.0.2" } }, + "apps/lit-vite": { + "name": "@svg-sprites-fixtures/lit-vite", + "version": "0.0.0", + "dependencies": { + "lit": "3.3.3" + }, + "devDependencies": { + "@gromlab/svg-sprites": "file:../../..", + "typescript": "6.0.2", + "vite": "8.1.4" + } + }, + "apps/lit-webpack": { + "name": "@svg-sprites-fixtures/lit-webpack", + "version": "0.0.0", + "dependencies": { + "lit": "3.3.3" + }, + "devDependencies": { + "@gromlab/svg-sprites": "file:../../..", + "html-webpack-plugin": "5.6.7", + "typescript": "6.0.2", + "webpack": "5.108.4", + "webpack-cli": "7.2.1", + "webpack-dev-server": "5.2.2" + } + }, "apps/next-app": { "name": "@svg-sprites-fixtures/next-app", "version": "0.0.0", @@ -2079,6 +2825,712 @@ "vue-tsc": "3.3.7" } }, + "apps/nuxt-vite": { + "name": "@svg-sprites-fixtures/nuxt-vite", + "version": "0.0.0", + "dependencies": { + "nuxt": "4.3.1", + "vue": "3.5.39" + }, + "devDependencies": { + "@gromlab/svg-sprites": "file:../../..", + "typescript": "6.0.2", + "vue-tsc": "3.3.7" + } + }, + "apps/nuxt-webpack": { + "name": "@svg-sprites-fixtures/nuxt-webpack", + "version": "0.0.0", + "dependencies": { + "entities": "7.0.1", + "nuxt": "4.3.1", + "vue": "3.5.39" + }, + "devDependencies": { + "@gromlab/svg-sprites": "file:../../..", + "@nuxt/webpack-builder": "4.3.1", + "postcss-loader": "8.2.1", + "typescript": "6.0.2", + "vue-tsc": "3.3.7" + } + }, + "apps/nuxt-webpack/node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "apps/nuxt-webpack/node_modules/postcss-loader": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-8.2.1.tgz", + "integrity": "sha512-k98jtRzthjj3f76MYTs9JTpRqV1RaaMhEU0Lpw9OTmQZQdppg4B30VZ74BojuBHt3F4KyubHJoXCMUeM8Bqeow==", + "dev": true, + "license": "MIT", + "dependencies": { + "cosmiconfig": "^9.0.0", + "jiti": "^2.5.1", + "semver": "^7.6.2" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || ^1.0.0 || ^2.0.0-0", + "postcss": "^7.0.0 || ^8.0.1", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "apps/nuxt-webpack/node_modules/postcss-loader/node_modules/cosmiconfig": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.2.tgz", + "integrity": "sha512-gtTZxTDau1wL7Y7zifc2dd8jHSK/k6BTx/2Xp/BpdlAdnlYWFVt7qhJqgwi7637yRwRQ3qL4ZidbB4I8tA5VOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "apps/preact-vite": { + "name": "@svg-sprites-fixtures/preact-vite", + "version": "0.0.0", + "dependencies": { + "preact": "10.27.2" + }, + "devDependencies": { + "@gromlab/svg-sprites": "file:../../..", + "typescript": "6.0.2", + "vite": "8.1.4" + } + }, + "apps/preact-webpack": { + "name": "@svg-sprites-fixtures/preact-webpack", + "version": "0.0.0", + "dependencies": { + "preact": "10.27.2" + }, + "devDependencies": { + "@gromlab/svg-sprites": "file:../../..", + "css-loader": "7.1.4", + "html-webpack-plugin": "5.6.7", + "style-loader": "4.0.0", + "typescript": "6.0.2", + "webpack": "5.108.4", + "webpack-cli": "7.2.1", + "webpack-dev-server": "5.2.2" + } + }, + "apps/qwik-vite": { + "name": "@svg-sprites-fixtures/qwik-vite", + "version": "0.0.0", + "dependencies": { + "@builder.io/qwik": "^1.17.0" + }, + "devDependencies": { + "@gromlab/svg-sprites": "file:../../..", + "typescript": "6.0.2", + "vite": "7.3.1" + } + }, + "apps/qwik-vite/node_modules/@builder.io/qwik": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/@builder.io/qwik/-/qwik-1.20.0.tgz", + "integrity": "sha512-mPnENUgyOGYQW9UNGGeGP67hg8rI/hi4WQ0z/rM3PAgQl3UjfreBxyj1njxvSLAz5nei6yD20/W/eywVIxOCIw==", + "license": "MIT", + "dependencies": { + "csstype": "^3.1.3", + "launch-editor": "^2.11.1", + "rollup": "^4.59.0" + }, + "bin": { + "qwik": "qwik-cli.cjs" + }, + "engines": { + "node": ">=16.8.0 <18.0.0 || >=18.11" + }, + "peerDependencies": { + "vite": ">=5 <8" + } + }, + "apps/qwik-vite/node_modules/@esbuild/aix-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", + "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "apps/qwik-vite/node_modules/@esbuild/android-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz", + "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "apps/qwik-vite/node_modules/@esbuild/android-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz", + "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "apps/qwik-vite/node_modules/@esbuild/android-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz", + "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "apps/qwik-vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz", + "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "apps/qwik-vite/node_modules/@esbuild/darwin-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz", + "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "apps/qwik-vite/node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz", + "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "apps/qwik-vite/node_modules/@esbuild/freebsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz", + "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "apps/qwik-vite/node_modules/@esbuild/linux-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz", + "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "apps/qwik-vite/node_modules/@esbuild/linux-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz", + "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "apps/qwik-vite/node_modules/@esbuild/linux-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz", + "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "apps/qwik-vite/node_modules/@esbuild/linux-loong64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz", + "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "apps/qwik-vite/node_modules/@esbuild/linux-mips64el": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz", + "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "apps/qwik-vite/node_modules/@esbuild/linux-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz", + "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "apps/qwik-vite/node_modules/@esbuild/linux-riscv64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz", + "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "apps/qwik-vite/node_modules/@esbuild/linux-s390x": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz", + "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "apps/qwik-vite/node_modules/@esbuild/linux-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", + "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "apps/qwik-vite/node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz", + "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "apps/qwik-vite/node_modules/@esbuild/netbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz", + "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "apps/qwik-vite/node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz", + "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "apps/qwik-vite/node_modules/@esbuild/openbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz", + "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "apps/qwik-vite/node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz", + "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "apps/qwik-vite/node_modules/@esbuild/sunos-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz", + "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "apps/qwik-vite/node_modules/@esbuild/win32-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz", + "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "apps/qwik-vite/node_modules/@esbuild/win32-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz", + "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "apps/qwik-vite/node_modules/@esbuild/win32-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz", + "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "apps/qwik-vite/node_modules/esbuild": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz", + "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.7", + "@esbuild/android-arm": "0.27.7", + "@esbuild/android-arm64": "0.27.7", + "@esbuild/android-x64": "0.27.7", + "@esbuild/darwin-arm64": "0.27.7", + "@esbuild/darwin-x64": "0.27.7", + "@esbuild/freebsd-arm64": "0.27.7", + "@esbuild/freebsd-x64": "0.27.7", + "@esbuild/linux-arm": "0.27.7", + "@esbuild/linux-arm64": "0.27.7", + "@esbuild/linux-ia32": "0.27.7", + "@esbuild/linux-loong64": "0.27.7", + "@esbuild/linux-mips64el": "0.27.7", + "@esbuild/linux-ppc64": "0.27.7", + "@esbuild/linux-riscv64": "0.27.7", + "@esbuild/linux-s390x": "0.27.7", + "@esbuild/linux-x64": "0.27.7", + "@esbuild/netbsd-arm64": "0.27.7", + "@esbuild/netbsd-x64": "0.27.7", + "@esbuild/openbsd-arm64": "0.27.7", + "@esbuild/openbsd-x64": "0.27.7", + "@esbuild/openharmony-arm64": "0.27.7", + "@esbuild/sunos-x64": "0.27.7", + "@esbuild/win32-arm64": "0.27.7", + "@esbuild/win32-ia32": "0.27.7", + "@esbuild/win32-x64": "0.27.7" + } + }, + "apps/qwik-vite/node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "apps/qwik-vite/node_modules/vite": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", + "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, "apps/react-vite": { "name": "@svg-sprites-fixtures/react-vite", "version": "0.0.0", @@ -2116,6 +3568,20 @@ "webpack-dev-server": "5.2.2" } }, + "apps/solid-start-vite": { + "name": "@svg-sprites-fixtures/solid-start-vite", + "version": "0.0.0", + "dependencies": { + "@solidjs/router": "^0.15.0", + "@solidjs/start": "^1.1.0", + "solid-js": "1.9.14", + "vinxi": "^0.5.0" + }, + "devDependencies": { + "@gromlab/svg-sprites": "file:../../..", + "typescript": "6.0.2" + } + }, "apps/solid-vite": { "name": "@svg-sprites-fixtures/solid-vite", "version": "0.0.0", @@ -2129,6 +3595,44 @@ "vite-plugin-solid": "2.11.12" } }, + "apps/solid-webpack": { + "name": "@svg-sprites-fixtures/solid-webpack", + "version": "0.0.0", + "dependencies": { + "solid-js": "1.9.14" + }, + "devDependencies": { + "@gromlab/svg-sprites": "file:../../..", + "babel-loader": "10.0.0", + "babel-preset-solid": "1.9.9", + "css-loader": "7.1.4", + "html-webpack-plugin": "5.6.7", + "style-loader": "4.0.0", + "typescript": "6.0.2", + "webpack": "5.108.4", + "webpack-cli": "7.2.1", + "webpack-dev-server": "5.2.2" + } + }, + "apps/solid-webpack/node_modules/babel-preset-solid": { + "version": "1.9.9", + "resolved": "https://registry.npmjs.org/babel-preset-solid/-/babel-preset-solid-1.9.9.tgz", + "integrity": "sha512-pCnxWrciluXCeli/dj5PIEHgbNzim3evtTn12snjqqg8QZWJNMjH1AWIp4iG/tbVjqQ72aBEymMSagvmgxubXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "babel-plugin-jsx-dom-expressions": "^0.40.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "solid-js": "^1.9.8" + }, + "peerDependenciesMeta": { + "solid-js": { + "optional": true + } + } + }, "apps/standalone": { "name": "@svg-sprites-fixtures/standalone", "version": "0.0.0", @@ -2172,6 +3676,23 @@ "vite": "8.1.4" } }, + "apps/svelte-webpack": { + "name": "@svg-sprites-fixtures/svelte-webpack", + "version": "0.0.0", + "dependencies": { + "svelte": "5.56.4" + }, + "devDependencies": { + "@gromlab/svg-sprites": "file:../../..", + "html-webpack-plugin": "5.6.7", + "svelte-check": "4.7.2", + "svelte-loader": "3.2.4", + "typescript": "6.0.2", + "webpack": "5.108.4", + "webpack-cli": "7.2.1", + "webpack-dev-server": "5.2.2" + } + }, "apps/sveltekit": { "name": "@svg-sprites-fixtures/sveltekit", "version": "0.0.0", @@ -2202,6 +3723,26 @@ "vue-tsc": "3.3.7" } }, + "apps/vue-webpack": { + "name": "@svg-sprites-fixtures/vue-webpack", + "version": "0.0.0", + "dependencies": { + "vue": "3.5.39" + }, + "devDependencies": { + "@gromlab/svg-sprites": "file:../../..", + "@vue/compiler-sfc": "3.5.39", + "css-loader": "7.1.4", + "html-webpack-plugin": "5.6.7", + "style-loader": "4.0.0", + "typescript": "6.0.2", + "vue-loader": "17.4.2", + "vue-tsc": "3.3.7", + "webpack": "5.108.4", + "webpack-cli": "7.2.1", + "webpack-dev-server": "5.2.2" + } + }, "node_modules/@ampproject/remapping": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", @@ -2216,6 +3757,114 @@ "node": ">=6.0.0" } }, + "node_modules/@angular/common": { + "version": "21.2.18", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-21.2.18.tgz", + "integrity": "sha512-gZugZ8gX/KkACIZ3/ekoImLu5z8a0Iu9O5b84kh8e+++VUjmkmd19IygBsq/8/fF3vKf0UcIKcx3P6A/gb2DJw==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@angular/core": "21.2.18", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/compiler": { + "version": "21.2.18", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-21.2.18.tgz", + "integrity": "sha512-ccnDuKLuzIa0ayijR+alarsHNWIksuGV/lxGTZ0t6/0+B6J/RXupz6M2IO6ZHHEcg8r7pcrLCUBTyp3FoiRJrg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/@angular/compiler-cli": { + "version": "21.2.18", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-21.2.18.tgz", + "integrity": "sha512-L5zbIp7YfTTB4I4xT33FgEBanzhppNejzZX0HJOEqKDyDL2jXq+flt83lE0XVuRJ6/zrG+UKj0B+y/CK6Ro7Wg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "7.29.7", + "@jridgewell/sourcemap-codec": "^1.4.14", + "chokidar": "^5.0.0", + "convert-source-map": "^1.5.1", + "reflect-metadata": "^0.2.0", + "semver": "^7.0.0", + "tslib": "^2.3.0", + "yargs": "^18.0.0" + }, + "bin": { + "ng-xi18n": "bundles/src/bin/ng_xi18n.js", + "ngc": "bundles/src/bin/ngc.js" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@angular/compiler": "21.2.18", + "typescript": ">=5.9 <6.1" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@angular/core": { + "version": "21.2.18", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-21.2.18.tgz", + "integrity": "sha512-AG4bb6GU0+qp+vVBjc/IhSPjgcRX8QsCb8jzN8dDyhnjsyuuG/LlIiU0l6n65BI9SGKE9m6TfsJ1ObkkAiE5KQ==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@angular/compiler": "21.2.18", + "rxjs": "^6.5.3 || ^7.4.0", + "zone.js": "~0.15.0 || ~0.16.0" + }, + "peerDependenciesMeta": { + "@angular/compiler": { + "optional": true + }, + "zone.js": { + "optional": true + } + } + }, + "node_modules/@angular/platform-browser": { + "version": "21.2.18", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-21.2.18.tgz", + "integrity": "sha512-zltF+3HrlgtZbYg8U98LhG0dyGm1aHT3Px8//9wjqi/TUY8UlHsYKByL197QtVWDBjf/dekzXdz5c+iyVtanLQ==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@angular/animations": "21.2.18", + "@angular/common": "21.2.18", + "@angular/core": "21.2.18" + }, + "peerDependenciesMeta": { + "@angular/animations": { + "optional": true + } + } + }, "node_modules/@astrojs/check": { "version": "0.9.9", "resolved": "https://registry.npmjs.org/@astrojs/check/-/check-0.9.9.tgz", @@ -2705,20 +4354,20 @@ } }, "node_modules/@babel/core": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", - "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helpers": "^7.28.6", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/traverse": "^7.29.0", - "@babel/types": "^7.29.0", + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", @@ -3320,6 +4969,46 @@ "integrity": "sha512-3PxTH8itZzltK0U9jTwVVnjLXvnDYuq3m+QXsHkENxWiPRh4WaoLcs1SQjqgZ55kS+QyirpH5BVwzP2gMVG6EQ==", "license": "MIT" }, + "node_modules/@deno/shim-deno": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@deno/shim-deno/-/shim-deno-0.19.2.tgz", + "integrity": "sha512-q3VTHl44ad8T2Tw2SpeAvghdGOjlnLPDNO2cpOxwMrBE/PVas6geWpbpIgrM+czOCH0yejp0yi8OaTuB+NU40Q==", + "license": "MIT", + "dependencies": { + "@deno/shim-deno-test": "^0.5.0", + "which": "^4.0.0" + } + }, + "node_modules/@deno/shim-deno-test": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@deno/shim-deno-test/-/shim-deno-test-0.5.0.tgz", + "integrity": "sha512-4nMhecpGlPi0cSzT67L+Tm+GOJqvuk8gqHBziqcUQOarnuIax1z96/gJHCSIz2Z0zhxE6Rzwb3IZXPtFh51j+w==", + "license": "MIT" + }, + "node_modules/@deno/shim-deno/node_modules/isexe": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.5.tgz", + "integrity": "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@deno/shim-deno/node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, "node_modules/@discoveryjs/json-ext": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-1.1.0.tgz", @@ -4468,6 +6157,98 @@ "node": ">=8" } }, + "node_modules/@jest/pattern": { + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.4.0.tgz", + "integrity": "sha512-RAWn3+f9u8BsHijKJ71uHcFp6vmyEt6VvoWXkl6hKF3qVIuWNmudVjg12DlBPGup/frIl5UcUlH5HfEuvHpEXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-regex-util": "30.4.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/schemas": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.4.1.tgz", + "integrity": "sha512-i6b4qw5qnP8c5FEeBJg/uZQ4ddrkN6Ca8qISJh0pr7a5hfn3h3v5x60BEbOC7OYAGZNMs1LfFLwnW2CuK8F57Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.34.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/types": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.4.1.tgz", + "integrity": "sha512-f1x/vJXIfjOlEmejYpbkbgw1gOqpPECwMvMEtBqe47j7H2Hg8h8w3o3ikhSXq3MI15kg+oQ0exWO0uCtTNJLoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/pattern": "30.4.0", + "@jest/schemas": "30.4.1", + "@types/istanbul-lib-coverage": "^2.0.6", + "@types/istanbul-reports": "^3.0.4", + "@types/node": "*", + "@types/yargs": "^17.0.33", + "chalk": "^4.1.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/types/node_modules/ansi-styles": { + "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" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/types/node_modules/chalk": { + "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", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/types/node_modules/supports-color": { + "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" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", @@ -4976,6 +6757,21 @@ "dev": true, "license": "MIT" }, + "node_modules/@lit-labs/ssr-dom-shim": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.6.0.tgz", + "integrity": "sha512-VHb0ALPMTlgKjM6yIxxoQNnpKyUKLD04VzeQdsiXkMqkvYlAHxq9glGLmgbb889/1GsohSOAjvQYoiBppXFqrQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@lit/reactive-element": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-2.1.2.tgz", + "integrity": "sha512-pbCDiVMnne1lYUIaYNN5wrwQXDtHaYtg7YEFPeW+hws6U47WeFvISGUWekPGKWOP1ygrs0ef0o1VJMk1exos5A==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit-labs/ssr-dom-shim": "^1.5.0" + } + }, "node_modules/@mapbox/node-pre-gyp": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-2.0.3.tgz", @@ -5578,6 +7374,19 @@ "node": ">= 10" } }, + "node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -6179,6 +7988,152 @@ "node": "^20.17.0 || >=22.9.0" } }, + "node_modules/@nuxt/friendly-errors-webpack-plugin": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@nuxt/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-2.6.0.tgz", + "integrity": "sha512-3IZj6MXbzlvUxDncAxgBMLQwGPY/JlNhy2i+AGyOHCAReR5HcBxYjVRBvyaKM9R3s5k4OODYKeHAbrToZH/47w==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^2.4.2", + "consola": "^3.2.3", + "error-stack-parser": "^2.1.4", + "string-width": "^4.2.3" + }, + "engines": { + "node": ">=14.18.0", + "npm": ">=5.0.0" + }, + "peerDependencies": { + "webpack": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0" + } + }, + "node_modules/@nuxt/friendly-errors-webpack-plugin/node_modules/ansi-regex": { + "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" + } + }, + "node_modules/@nuxt/friendly-errors-webpack-plugin/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@nuxt/friendly-errors-webpack-plugin/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@nuxt/friendly-errors-webpack-plugin/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@nuxt/friendly-errors-webpack-plugin/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@nuxt/friendly-errors-webpack-plugin/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@nuxt/friendly-errors-webpack-plugin/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@nuxt/friendly-errors-webpack-plugin/node_modules/is-fullwidth-code-point": { + "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" + } + }, + "node_modules/@nuxt/friendly-errors-webpack-plugin/node_modules/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==", + "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/@nuxt/friendly-errors-webpack-plugin/node_modules/strip-ansi": { + "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/@nuxt/friendly-errors-webpack-plugin/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/@nuxt/kit": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/@nuxt/kit/-/kit-4.3.1.tgz", @@ -6880,6 +8835,291 @@ } } }, + "node_modules/@nuxt/webpack-builder": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@nuxt/webpack-builder/-/webpack-builder-4.3.1.tgz", + "integrity": "sha512-m6kmZnDY7tbY+/YF6hcT0yY/zeGmEyLO87l12mm2HYZxHr98+rbMUiQHkEvxBOkwCijkQ6DsB/p1its2jLcF6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nuxt/friendly-errors-webpack-plugin": "^2.6.0", + "@nuxt/kit": "4.3.1", + "@vue/compiler-sfc": "3.5.27", + "autoprefixer": "^10.4.24", + "css-loader": "^7.1.3", + "css-minimizer-webpack-plugin": "^7.0.4", + "cssnano": "^7.1.2", + "defu": "^6.1.4", + "esbuild-loader": "^4.4.2", + "escape-string-regexp": "^5.0.0", + "exsolve": "^1.0.8", + "file-loader": "^6.2.0", + "fork-ts-checker-webpack-plugin": "^9.1.0", + "jiti": "^2.6.1", + "knitwork": "^1.3.0", + "magic-string": "^0.30.21", + "memfs": "^4.56.10", + "mini-css-extract-plugin": "^2.10.0", + "mlly": "^1.8.0", + "ohash": "^2.0.11", + "pathe": "^2.0.3", + "pify": "^6.1.0", + "postcss": "^8.5.6", + "postcss-import": "^16.1.1", + "postcss-import-resolver": "^2.0.0", + "postcss-loader": "^8.2.0", + "postcss-url": "^10.1.3", + "pug-plain-loader": "^1.1.0", + "seroval": "^1.5.0", + "std-env": "^3.10.0", + "time-fix-plugin": "^2.0.7", + "tinyglobby": "^0.2.15", + "ufo": "^1.6.3", + "unenv": "^2.0.0-rc.24", + "unplugin": "^3.0.0", + "url-loader": "^4.1.1", + "vue-bundle-renderer": "^2.2.0", + "vue-loader": "^17.4.2", + "webpack": "^5.104.1", + "webpack-bundle-analyzer": "^5.2.0", + "webpack-dev-middleware": "^7.4.5", + "webpack-hot-middleware": "^2.26.1", + "webpackbar": "^7.0.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "nuxt": "4.3.1", + "vue": "^3.3.4" + } + }, + "node_modules/@nuxt/webpack-builder/node_modules/@vue/compiler-core": { + "version": "3.5.27", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.27.tgz", + "integrity": "sha512-gnSBQjZA+//qDZen+6a2EdHqJ68Z7uybrMf3SPjEGgG4dicklwDVmMC1AeIHxtLVPT7sn6sH1KOO+tS6gwOUeQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.5", + "@vue/shared": "3.5.27", + "entities": "^7.0.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@nuxt/webpack-builder/node_modules/@vue/compiler-dom": { + "version": "3.5.27", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.27.tgz", + "integrity": "sha512-oAFea8dZgCtVVVTEC7fv3T5CbZW9BxpFzGGxC79xakTr6ooeEqmRuvQydIiDAkglZEAd09LgVf1RoDnL54fu5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.27", + "@vue/shared": "3.5.27" + } + }, + "node_modules/@nuxt/webpack-builder/node_modules/@vue/compiler-sfc": { + "version": "3.5.27", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.27.tgz", + "integrity": "sha512-sHZu9QyDPeDmN/MRoshhggVOWE5WlGFStKFwu8G52swATgSny27hJRWteKDSUUzUH+wp+bmeNbhJnEAel/auUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.5", + "@vue/compiler-core": "3.5.27", + "@vue/compiler-dom": "3.5.27", + "@vue/compiler-ssr": "3.5.27", + "@vue/shared": "3.5.27", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.21", + "postcss": "^8.5.6", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@nuxt/webpack-builder/node_modules/@vue/compiler-ssr": { + "version": "3.5.27", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.27.tgz", + "integrity": "sha512-Sj7h+JHt512fV1cTxKlYhg7qxBvack+BGncSpH+8vnN+KN95iPIcqB5rsbblX40XorP+ilO7VIKlkuu3Xq2vjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.27", + "@vue/shared": "3.5.27" + } + }, + "node_modules/@nuxt/webpack-builder/node_modules/@vue/shared": { + "version": "3.5.27", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.27.tgz", + "integrity": "sha512-dXr/3CgqXsJkZ0n9F3I4elY8wM9jMJpP3pvRG52r6m0tu/MsAFIe6JpXVGeNMd/D9F4hQynWT8Rfuj0bdm9kFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@nuxt/webpack-builder/node_modules/ansis": { + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/ansis/-/ansis-3.17.0.tgz", + "integrity": "sha512-0qWUglt9JEqLFr3w1I1pbrChn1grhaiAR2ocX1PP/flRmxgtwTzPFFFnfIlD6aMOLQZgSuCRlidD70lvx8yhzg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + } + }, + "node_modules/@nuxt/webpack-builder/node_modules/cosmiconfig": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.2.tgz", + "integrity": "sha512-gtTZxTDau1wL7Y7zifc2dd8jHSK/k6BTx/2Xp/BpdlAdnlYWFVt7qhJqgwi7637yRwRQ3qL4ZidbB4I8tA5VOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@nuxt/webpack-builder/node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/@nuxt/webpack-builder/node_modules/postcss-loader": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-8.2.1.tgz", + "integrity": "sha512-k98jtRzthjj3f76MYTs9JTpRqV1RaaMhEU0Lpw9OTmQZQdppg4B30VZ74BojuBHt3F4KyubHJoXCMUeM8Bqeow==", + "dev": true, + "license": "MIT", + "dependencies": { + "cosmiconfig": "^9.0.0", + "jiti": "^2.5.1", + "semver": "^7.6.2" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || ^1.0.0 || ^2.0.0-0", + "postcss": "^7.0.0 || ^8.0.1", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/@nuxt/webpack-builder/node_modules/unplugin": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-3.3.0.tgz", + "integrity": "sha512-qa66K+crbfyE6JK10GjvbJeRrOsuC/JpbnHctfyp/i4oBTxWOzJfRZyDiOk1PtErMFRu8JhsU/wPvOdBNWe5Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "picomatch": "^4.0.4", + "webpack-virtual-modules": "^0.6.2" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "@farmfe/core": "*", + "@rspack/core": "*", + "bun-types-no-globals": "*", + "esbuild": "*", + "rolldown": "*", + "rollup": "*", + "unloader": "*", + "vite": "*", + "webpack": "*" + }, + "peerDependenciesMeta": { + "@farmfe/core": { + "optional": true + }, + "@rspack/core": { + "optional": true + }, + "bun-types-no-globals": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "rolldown": { + "optional": true + }, + "rollup": { + "optional": true + }, + "unloader": { + "optional": true + }, + "vite": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/@nuxt/webpack-builder/node_modules/webpackbar": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webpackbar/-/webpackbar-7.0.0.tgz", + "integrity": "sha512-aS9soqSO2iCHgqHoCrj4LbfGQUboDCYJPSFOAchEK+9psIjNrfSWW4Y0YEz67MKURNvMmfo0ycOg9d/+OOf9/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansis": "^3.2.0", + "consola": "^3.2.3", + "pretty-time": "^1.1.0", + "std-env": "^3.7.0" + }, + "engines": { + "node": ">=14.21.3" + }, + "peerDependencies": { + "@rspack/core": "*", + "webpack": "3 || 4 || 5" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, "node_modules/@oslojs/encoding": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@oslojs/encoding/-/encoding-1.1.0.tgz", @@ -8182,6 +10422,175 @@ "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", "license": "MIT" }, + "node_modules/@peculiar/asn1-cms": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-cms/-/asn1-cms-2.8.0.tgz", + "integrity": "sha512-NgekZOrSJFSBFLFoLfwePguAWAx7z1+f2TEsWFUMyiqqfntZ4+S/S5hzqME3q4pCA0iOsFKdwiQ35dwY24eVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.8.0", + "@peculiar/asn1-x509": "^2.8.0", + "@peculiar/asn1-x509-attr": "^2.8.0", + "asn1js": "^3.0.10", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-csr": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-csr/-/asn1-csr-2.8.0.tgz", + "integrity": "sha512-akbF8+uvleHs8sejNPQxwmVFuInAg6FMNHOwMILXfP518YfFJwdR3jr6oNUPOaEJfuEhn/vkNOCIT6ASUd4mbg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.8.0", + "@peculiar/asn1-x509": "^2.8.0", + "asn1js": "^3.0.10", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-ecc": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-ecc/-/asn1-ecc-2.8.0.tgz", + "integrity": "sha512-ohwlk+u9Rv2NOAY1c6MfHj45ATVF8R1DUN/WCgABiRtLi2ZftlZWZX7KvpAbU8v9xPcmoILfELeEABj/rn18AQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.8.0", + "@peculiar/asn1-x509": "^2.8.0", + "asn1js": "^3.0.10", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-pfx": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-pfx/-/asn1-pfx-2.8.0.tgz", + "integrity": "sha512-5yof1ytoB++RQtaFbqSUJ8pxDJtZT6vbVqZ8XoJ61ph7UjNVvfFwAilnCodqkNsAodpy13gDhoxZXw00pghnyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/asn1-cms": "^2.8.0", + "@peculiar/asn1-pkcs8": "^2.8.0", + "@peculiar/asn1-rsa": "^2.8.0", + "@peculiar/asn1-schema": "^2.8.0", + "asn1js": "^3.0.10", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-pkcs8": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs8/-/asn1-pkcs8-2.8.0.tgz", + "integrity": "sha512-qAKXtLpBEw9LqhKpjw3ajZSXlBur+ipW+y2ivVBQAG6F6qRx94yO+1ZR4mvw+YaCfKSaOzLeYEzsPaBp4SJELA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.8.0", + "@peculiar/asn1-x509": "^2.8.0", + "asn1js": "^3.0.10", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-pkcs9": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs9/-/asn1-pkcs9-2.8.0.tgz", + "integrity": "sha512-b5nDWCnkV60+cQ141D6sVVwK9nz64R5n3zSVnklGd+ECdkW2Ol3U1a6yYFlalpSOaD557yuJB64A+q42jG7lUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/asn1-cms": "^2.8.0", + "@peculiar/asn1-pfx": "^2.8.0", + "@peculiar/asn1-pkcs8": "^2.8.0", + "@peculiar/asn1-schema": "^2.8.0", + "@peculiar/asn1-x509": "^2.8.0", + "@peculiar/asn1-x509-attr": "^2.8.0", + "asn1js": "^3.0.10", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-rsa": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-rsa/-/asn1-rsa-2.8.0.tgz", + "integrity": "sha512-zHEUlCqB2mk7x2lxDwHHJy7hWZOPdGHVlsmITWKB5/PbQo61atbu9PJ/0r9dQNMwFzbKPXZ8uK8/91eUhRznSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.8.0", + "@peculiar/asn1-x509": "^2.8.0", + "asn1js": "^3.0.10", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-schema": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.8.0.tgz", + "integrity": "sha512-7YT0U/ze0tF2QOBbE15gKZwy5tvgGyLRiRHLzhlbOpf7BT032oBSd0haZqXn5W6l26WLlu3dyxzjM+2638/z2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/utils": "^2.0.2", + "asn1js": "^3.0.10", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-x509": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-x509/-/asn1-x509-2.8.0.tgz", + "integrity": "sha512-N0CMuhWUzsWEVq6F1q9X6+VKUnWzSW+cSVg+aPaGGwDdbFoFWTYgin5MHwXgpWd6y9COMBxnfy/Qc+Xc7F0Zwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.8.0", + "@peculiar/utils": "^2.0.2", + "asn1js": "^3.0.10", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-x509-attr": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-x509-attr/-/asn1-x509-attr-2.8.0.tgz", + "integrity": "sha512-tHjkfS/qhMnmrlB2J9NhflQlQ7In3khO3CfmVrriOlpTeErY9ZIKOso1hQ5JQiyrJ7ShvqVPk7E5fQmbclkSKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.8.0", + "@peculiar/asn1-x509": "^2.8.0", + "asn1js": "^3.0.10", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@peculiar/utils/-/utils-2.0.3.tgz", + "integrity": "sha512-+oL3HPFRIZ1St2K50lWCXiioIgSoxzz7R1J3uF6neO2yl1sgmpgY6XXJH4BdpoDkMWznQTeYF6oWNDZLCdQ4eQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/x509": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/@peculiar/x509/-/x509-1.14.3.tgz", + "integrity": "sha512-C2Xj8FZ0uHWeCXXqX5B4/gVFQmtSkiuOolzAgutjTfseNOHT3pUjljDZsTSxXFGgio54bCzVFqmEOUrIVk8RDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/asn1-cms": "^2.6.0", + "@peculiar/asn1-csr": "^2.6.0", + "@peculiar/asn1-ecc": "^2.6.0", + "@peculiar/asn1-pkcs9": "^2.6.0", + "@peculiar/asn1-rsa": "^2.6.0", + "@peculiar/asn1-schema": "^2.6.0", + "@peculiar/asn1-x509": "^2.6.0", + "pvtsutils": "^1.3.6", + "reflect-metadata": "^0.2.2", + "tslib": "^2.8.1", + "tsyringe": "^4.10.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -9216,6 +11625,13 @@ "@simple-git/args-pathspec": "^1.0.3" } }, + "node_modules/@sinclair/typebox": { + "version": "0.34.52", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.52.tgz", + "integrity": "sha512-XiMQh7qqVlxZzcVD+kkGMNGMzcTrDMLWI7S4x7z1MkCkbDPrekpZXEUK0eZqZFMuHQg2a2DZOcDIh9o5v3Gonw==", + "dev": true, + "license": "MIT" + }, "node_modules/@sindresorhus/is": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-7.2.0.tgz", @@ -9240,6 +11656,150 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@solidjs/router": { + "version": "0.15.4", + "resolved": "https://registry.npmjs.org/@solidjs/router/-/router-0.15.4.tgz", + "integrity": "sha512-WOpgg9a9T638cR+5FGbFi/IV4l2FpmBs1GpIMSPa0Ce9vyJN7Wts+X2PqMf9IYn0zUj2MlSJtm1gp7/HI/n5TQ==", + "license": "MIT", + "peerDependencies": { + "solid-js": "^1.8.6" + } + }, + "node_modules/@solidjs/start": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@solidjs/start/-/start-1.3.2.tgz", + "integrity": "sha512-tasDl3utVbtP0rr4InB3ntBIFV2upvEiFrOOCkRrAA3yBfjx9elpxnc94sJQXo65PNYdAAAkPIC6h93vLrtwHg==", + "license": "MIT", + "dependencies": { + "@tanstack/server-functions-plugin": "1.121.21", + "@vinxi/plugin-directives": "^0.5.0", + "@vinxi/server-components": "^0.5.0", + "cookie-es": "^2.0.0", + "defu": "^6.1.2", + "error-stack-parser": "^2.1.4", + "html-to-image": "^1.11.11", + "radix3": "^1.1.0", + "seroval": "^1.5.0", + "seroval-plugins": "^1.5.0", + "shiki": "^1.26.1", + "source-map-js": "^1.0.2", + "terracotta": "^1.0.4", + "tinyglobby": "^0.2.2", + "vite-plugin-solid": "^2.11.10" + }, + "peerDependencies": { + "vinxi": "^0.5.7" + } + }, + "node_modules/@solidjs/start/node_modules/@shikijs/core": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.29.2.tgz", + "integrity": "sha512-vju0lY9r27jJfOY4Z7+Rt/nIOjzJpZ3y+nYpqtUZInVoXQ/TJZcfGnNOGnKjFdVZb8qexiCuSlZRKcGfhhTTZQ==", + "license": "MIT", + "dependencies": { + "@shikijs/engine-javascript": "1.29.2", + "@shikijs/engine-oniguruma": "1.29.2", + "@shikijs/types": "1.29.2", + "@shikijs/vscode-textmate": "^10.0.1", + "@types/hast": "^3.0.4", + "hast-util-to-html": "^9.0.4" + } + }, + "node_modules/@solidjs/start/node_modules/@shikijs/engine-javascript": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-1.29.2.tgz", + "integrity": "sha512-iNEZv4IrLYPv64Q6k7EPpOCE/nuvGiKl7zxdq0WFuRPF5PAE9PRo2JGq/d8crLusM59BRemJ4eOqrFrC4wiQ+A==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "1.29.2", + "@shikijs/vscode-textmate": "^10.0.1", + "oniguruma-to-es": "^2.2.0" + } + }, + "node_modules/@solidjs/start/node_modules/@shikijs/engine-oniguruma": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-1.29.2.tgz", + "integrity": "sha512-7iiOx3SG8+g1MnlzZVDYiaeHe7Ez2Kf2HrJzdmGwkRisT7r4rak0e655AcM/tF9JG/kg5fMNYlLLKglbN7gBqA==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "1.29.2", + "@shikijs/vscode-textmate": "^10.0.1" + } + }, + "node_modules/@solidjs/start/node_modules/@shikijs/langs": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-1.29.2.tgz", + "integrity": "sha512-FIBA7N3LZ+223U7cJDUYd5shmciFQlYkFXlkKVaHsCPgfVLiO+e12FmQE6Tf9vuyEsFe3dIl8qGWKXgEHL9wmQ==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "1.29.2" + } + }, + "node_modules/@solidjs/start/node_modules/@shikijs/themes": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-1.29.2.tgz", + "integrity": "sha512-i9TNZlsq4uoyqSbluIcZkmPL9Bfi3djVxRnofUHwvx/h6SRW3cwgBC5SML7vsDcWyukY0eCzVN980rqP6qNl9g==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "1.29.2" + } + }, + "node_modules/@solidjs/start/node_modules/@shikijs/types": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-1.29.2.tgz", + "integrity": "sha512-VJjK0eIijTZf0QSTODEXCqinjBn0joAHQ+aPSBzrv4O2d/QSbsMw+ZeSRx03kV34Hy7NzUvV/7NqfYGRLrASmw==", + "license": "MIT", + "dependencies": { + "@shikijs/vscode-textmate": "^10.0.1", + "@types/hast": "^3.0.4" + } + }, + "node_modules/@solidjs/start/node_modules/oniguruma-to-es": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-2.3.0.tgz", + "integrity": "sha512-bwALDxriqfKGfUufKGGepCzu9x7nJQuoRoAFp4AnwehhC2crqrDIAP/uN2qdlsAvSMpeRC3+Yzhqc7hLmle5+g==", + "license": "MIT", + "dependencies": { + "emoji-regex-xs": "^1.0.0", + "regex": "^5.1.1", + "regex-recursion": "^5.1.1" + } + }, + "node_modules/@solidjs/start/node_modules/regex": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/regex/-/regex-5.1.1.tgz", + "integrity": "sha512-dN5I359AVGPnwzJm2jN1k0W9LPZ+ePvoOeVMMfqIMFz53sSwXkxaJoxr50ptnsC771lK95BnTrVSZxq0b9yCGw==", + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/@solidjs/start/node_modules/regex-recursion": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-5.1.1.tgz", + "integrity": "sha512-ae7SBCbzVNrIjgSbh7wMznPcQel1DNlDtzensnFxpiNpXt1U2ju/bHugH422r+4LAVS1FpW1YCwilmnNsjum9w==", + "license": "MIT", + "dependencies": { + "regex": "^5.1.1", + "regex-utilities": "^2.3.0" + } + }, + "node_modules/@solidjs/start/node_modules/shiki": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.29.2.tgz", + "integrity": "sha512-njXuliz/cP+67jU2hukkxCNuH1yUi4QfdZZY+sMr5PPrIyXSu5iTb/qYC4BiWWB0vZ+7TbdvYUCeL23zpwCfbg==", + "license": "MIT", + "dependencies": { + "@shikijs/core": "1.29.2", + "@shikijs/engine-javascript": "1.29.2", + "@shikijs/engine-oniguruma": "1.29.2", + "@shikijs/langs": "1.29.2", + "@shikijs/themes": "1.29.2", + "@shikijs/types": "1.29.2", + "@shikijs/vscode-textmate": "^10.0.1", + "@types/hast": "^3.0.4" + } + }, "node_modules/@speed-highlight/core": { "version": "1.2.17", "resolved": "https://registry.npmjs.org/@speed-highlight/core/-/core-1.2.17.tgz", @@ -9351,14 +11911,34 @@ "vite": "^8.0.0-beta.7 || ^8.0.0" } }, + "node_modules/@svg-sprites-fixtures/alpine-vite": { + "resolved": "apps/alpine-vite", + "link": true + }, + "node_modules/@svg-sprites-fixtures/alpine-webpack": { + "resolved": "apps/alpine-webpack", + "link": true + }, "node_modules/@svg-sprites-fixtures/angular": { "resolved": "apps/angular", "link": true }, + "node_modules/@svg-sprites-fixtures/angular-webpack": { + "resolved": "apps/angular-webpack", + "link": true + }, "node_modules/@svg-sprites-fixtures/astro": { "resolved": "apps/astro", "link": true }, + "node_modules/@svg-sprites-fixtures/lit-vite": { + "resolved": "apps/lit-vite", + "link": true + }, + "node_modules/@svg-sprites-fixtures/lit-webpack": { + "resolved": "apps/lit-webpack", + "link": true + }, "node_modules/@svg-sprites-fixtures/next-app-turbopack": { "resolved": "apps/next-app-turbopack", "link": true @@ -9379,6 +11959,26 @@ "resolved": "apps/nuxt", "link": true }, + "node_modules/@svg-sprites-fixtures/nuxt-vite": { + "resolved": "apps/nuxt-vite", + "link": true + }, + "node_modules/@svg-sprites-fixtures/nuxt-webpack": { + "resolved": "apps/nuxt-webpack", + "link": true + }, + "node_modules/@svg-sprites-fixtures/preact-vite": { + "resolved": "apps/preact-vite", + "link": true + }, + "node_modules/@svg-sprites-fixtures/preact-webpack": { + "resolved": "apps/preact-webpack", + "link": true + }, + "node_modules/@svg-sprites-fixtures/qwik-vite": { + "resolved": "apps/qwik-vite", + "link": true + }, "node_modules/@svg-sprites-fixtures/react-vite": { "resolved": "apps/react-vite", "link": true @@ -9387,10 +11987,18 @@ "resolved": "apps/react-webpack", "link": true }, + "node_modules/@svg-sprites-fixtures/solid-start-vite": { + "resolved": "apps/solid-start-vite", + "link": true + }, "node_modules/@svg-sprites-fixtures/solid-vite": { "resolved": "apps/solid-vite", "link": true }, + "node_modules/@svg-sprites-fixtures/solid-webpack": { + "resolved": "apps/solid-webpack", + "link": true + }, "node_modules/@svg-sprites-fixtures/standalone": { "resolved": "apps/standalone", "link": true @@ -9407,6 +12015,10 @@ "resolved": "apps/svelte-vite", "link": true }, + "node_modules/@svg-sprites-fixtures/svelte-webpack": { + "resolved": "apps/svelte-webpack", + "link": true + }, "node_modules/@svg-sprites-fixtures/sveltekit": { "resolved": "apps/sveltekit", "link": true @@ -9415,6 +12027,10 @@ "resolved": "apps/vue-vite", "link": true }, + "node_modules/@svg-sprites-fixtures/vue-webpack": { + "resolved": "apps/vue-webpack", + "link": true + }, "node_modules/@swc/helpers": { "version": "0.5.15", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", @@ -9424,6 +12040,107 @@ "tslib": "^2.8.0" } }, + "node_modules/@tanstack/directive-functions-plugin": { + "version": "1.121.21", + "resolved": "https://registry.npmjs.org/@tanstack/directive-functions-plugin/-/directive-functions-plugin-1.121.21.tgz", + "integrity": "sha512-B9z/HbF7gJBaRHieyX7f2uQ4LpLLAVAEutBZipH6w+CYD6RHRJvSVPzECGHF7icFhNWTiJQL2QR6K07s59yzEw==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "7.26.2", + "@babel/core": "^7.26.8", + "@babel/traverse": "^7.26.8", + "@babel/types": "^7.26.8", + "@tanstack/router-utils": "^1.121.21", + "babel-dead-code-elimination": "^1.0.10", + "tiny-invariant": "^1.3.3" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "vite": ">=6.0.0" + } + }, + "node_modules/@tanstack/directive-functions-plugin/node_modules/@babel/code-frame": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@tanstack/router-utils": { + "version": "1.162.2", + "resolved": "https://registry.npmjs.org/@tanstack/router-utils/-/router-utils-1.162.2.tgz", + "integrity": "sha512-hTWqJtqIFFdvuCl8WXNyrodp2L9zo2G37xKRrcVmVRWpAB2h+U1LuRAfS4tsFTiWOIoE/B+WDVFB8JpoEdw6jQ==", + "license": "MIT", + "dependencies": { + "@babel/generator": "^7.28.5", + "@babel/parser": "^7.28.5", + "@babel/types": "^7.28.5", + "ansis": "^4.1.0", + "babel-dead-code-elimination": "^1.0.12", + "diff": "^8.0.2", + "pathe": "^2.0.3", + "tinyglobby": "^0.2.15" + }, + "engines": { + "node": ">=20.19" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/server-functions-plugin": { + "version": "1.121.21", + "resolved": "https://registry.npmjs.org/@tanstack/server-functions-plugin/-/server-functions-plugin-1.121.21.tgz", + "integrity": "sha512-a05fzK+jBGacsSAc1vE8an7lpBh4H0PyIEcivtEyHLomgSeElAJxm9E2It/0nYRZ5Lh23m0okbhzJNaYWZpAOg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "7.26.2", + "@babel/core": "^7.26.8", + "@babel/plugin-syntax-jsx": "^7.25.9", + "@babel/plugin-syntax-typescript": "^7.25.9", + "@babel/template": "^7.26.8", + "@babel/traverse": "^7.26.8", + "@babel/types": "^7.26.8", + "@tanstack/directive-functions-plugin": "1.121.21", + "babel-dead-code-elimination": "^1.0.9", + "tiny-invariant": "^1.3.3" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/server-functions-plugin/node_modules/@babel/code-frame": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@tufjs/canonical-json": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-2.0.0.tgz", @@ -9462,7 +12179,6 @@ "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, "license": "MIT", "dependencies": { "@babel/parser": "^7.20.7", @@ -9476,7 +12192,6 @@ "version": "7.27.0", "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", - "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.0.0" @@ -9486,7 +12201,6 @@ "version": "7.4.4", "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, "license": "MIT", "dependencies": { "@babel/parser": "^7.1.0", @@ -9497,7 +12211,6 @@ "version": "7.28.0", "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", - "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.28.2" @@ -9524,6 +12237,12 @@ "@types/node": "*" } }, + "node_modules/@types/braces": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/braces/-/braces-3.0.5.tgz", + "integrity": "sha512-SQFof9H+LXeWNz8wDe7oN5zu7ket0qwMu5vZubW4GCJ8Kkeh6nBWUz87+KTz/G3Kqsrp0j/W253XJb3KMEeg3w==", + "license": "MIT" + }, "node_modules/@types/connect": { "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", @@ -9552,6 +12271,28 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/eslint": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", @@ -9626,6 +12367,33 @@ "@types/node": "*" } }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -9642,6 +12410,15 @@ "@types/unist": "*" } }, + "node_modules/@types/micromatch": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/@types/micromatch/-/micromatch-4.0.10.tgz", + "integrity": "sha512-5jOhFDElqr4DKTrTEbnW8DZ4Hz5LRUEmyrGpCMrD/NphYv3nUnaF08xmSLx1rGGnyEs/kFnhiw6dCgcDqMr5PQ==", + "license": "MIT", + "dependencies": { + "@types/braces": "*" + } + }, "node_modules/@types/mime": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", @@ -9800,6 +12577,23 @@ "@types/node": "*" } }, + "node_modules/@types/yargs": { + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@ungap/structured-clone": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.3.tgz", @@ -9854,6 +12648,145 @@ "node": ">=20" } }, + "node_modules/@vinxi/listhen": { + "version": "1.5.6", + "resolved": "https://registry.npmjs.org/@vinxi/listhen/-/listhen-1.5.6.tgz", + "integrity": "sha512-WSN1z931BtasZJlgPp704zJFnQFRg7yzSjkm3MzAWQYe4uXFXlFr1hc5Ac2zae5/HDOz5x1/zDM5Cb54vTCnWw==", + "license": "MIT", + "dependencies": { + "@parcel/watcher": "^2.3.0", + "@parcel/watcher-wasm": "2.3.0", + "citty": "^0.1.5", + "clipboardy": "^4.0.0", + "consola": "^3.2.3", + "defu": "^6.1.4", + "get-port-please": "^3.1.2", + "h3": "^1.10.0", + "http-shutdown": "^1.2.2", + "jiti": "^1.21.0", + "mlly": "^1.5.0", + "node-forge": "^1.3.1", + "pathe": "^1.1.2", + "std-env": "^3.7.0", + "ufo": "^1.3.2", + "untun": "^0.1.3", + "uqr": "^0.1.2" + }, + "bin": { + "listen": "bin/listhen.mjs", + "listhen": "bin/listhen.mjs" + } + }, + "node_modules/@vinxi/listhen/node_modules/@parcel/watcher-wasm": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-wasm/-/watcher-wasm-2.3.0.tgz", + "integrity": "sha512-ejBAX8H0ZGsD8lSICDNyMbSEtPMWgDL0WFCt/0z7hyf5v8Imz4rAM8xY379mBsECkq/Wdqa5WEDLqtjZ+6NxfA==", + "bundleDependencies": [ + "napi-wasm" + ], + "license": "MIT", + "dependencies": { + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "napi-wasm": "^1.1.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@vinxi/listhen/node_modules/@parcel/watcher-wasm/node_modules/napi-wasm": { + "version": "1.1.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@vinxi/listhen/node_modules/citty": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", + "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", + "license": "MIT", + "dependencies": { + "consola": "^3.2.3" + } + }, + "node_modules/@vinxi/listhen/node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/@vinxi/listhen/node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "license": "MIT" + }, + "node_modules/@vinxi/plugin-directives": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@vinxi/plugin-directives/-/plugin-directives-0.5.1.tgz", + "integrity": "sha512-pH/KIVBvBt7z7cXrUH/9uaqcdxjegFC7+zvkZkdOyWzs+kQD5KPf3cl8kC+5ayzXHT+OMlhGhyitytqN3cGmHg==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.23.5", + "acorn": "^8.10.0", + "acorn-jsx": "^5.3.2", + "acorn-loose": "^8.3.0", + "acorn-typescript": "^1.4.3", + "astring": "^1.8.6", + "magicast": "^0.2.10", + "recast": "^0.23.4", + "tslib": "^2.6.2" + }, + "peerDependencies": { + "vinxi": "^0.5.5" + } + }, + "node_modules/@vinxi/plugin-directives/node_modules/magicast": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.2.11.tgz", + "integrity": "sha512-6saXbRDA1HMkqbsvHOU6HBjCVgZT460qheRkLhJQHWAbhXoWESI3Kn/dGGXyKs15FFKR85jsUqFx2sMK0wy/5g==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.22.16", + "@babel/types": "^7.22.17", + "recast": "^0.23.4" + } + }, + "node_modules/@vinxi/server-components": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@vinxi/server-components/-/server-components-0.5.1.tgz", + "integrity": "sha512-0BsG95qac3dkhfdRZxqzqYWJE4NvPL7ILlV43B6K6ho1etXWB2e5b0IxsUAUbyqpqiXM7mSRivojuXjb2G4OsQ==", + "license": "MIT", + "dependencies": { + "@vinxi/plugin-directives": "0.5.1", + "acorn": "^8.10.0", + "acorn-loose": "^8.3.0", + "acorn-typescript": "^1.4.3", + "astring": "^1.8.6", + "magicast": "^0.2.10", + "recast": "^0.23.4" + }, + "peerDependencies": { + "vinxi": "^0.5.5" + } + }, + "node_modules/@vinxi/server-components/node_modules/magicast": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.2.11.tgz", + "integrity": "sha512-6saXbRDA1HMkqbsvHOU6HBjCVgZT460qheRkLhJQHWAbhXoWESI3Kn/dGGXyKs15FFKR85jsUqFx2sMK0wy/5g==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.22.16", + "@babel/types": "^7.22.17", + "recast": "^0.23.4" + } + }, "node_modules/@vitejs/plugin-react": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.1.tgz", @@ -10532,6 +13465,49 @@ "acorn": "^8.14.0" } }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-loose": { + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/acorn-loose/-/acorn-loose-8.5.2.tgz", + "integrity": "sha512-PPvV6g8UGMGgjrMu+n/f9E/tCSkNQ2Y97eFvuVdJfG11+xdIeDcLyNdC8SHcrHbRqkfwLASdplyR6B6sKM1U4A==", + "license": "MIT", + "dependencies": { + "acorn": "^8.15.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-typescript": { + "version": "1.4.13", + "resolved": "https://registry.npmjs.org/acorn-typescript/-/acorn-typescript-1.4.13.tgz", + "integrity": "sha512-xsc9Xv0xlVfwp2o7sQ+GCQ1PgbkdcpWdTzrwXxO3xDMTAywVS3oXVOcOHuRjAPkS4P9b+yc/qNF15460v+jp4Q==", + "license": "MIT", + "peerDependencies": { + "acorn": ">=8.9.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.5", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz", + "integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/ajv": { "version": "8.20.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", @@ -10611,6 +13587,30 @@ "integrity": "sha512-I8FjmltrfnDFoZedi5CG8DghVYNhzb/Ijluz7tCSJH0xpd0484Kowhbb1XDYOxfJpU1p5wnM2X54dA+IfGyD1g==", "license": "MIT" }, + "node_modules/alpinejs": { + "version": "3.15.12", + "resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.15.12.tgz", + "integrity": "sha512-nJvPAQVNPdZZ0NrExJ/kzQco3ijR8LwvCOadQecllESiqT4NyZ/57sN9V2XyvhlBGAbmlKYgeWZvYdKq99ij/Q==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "~3.1.1" + } + }, + "node_modules/alpinejs/node_modules/@vue/reactivity": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.1.5.tgz", + "integrity": "sha512-1tdfLmNjWG6t/CsPldh+foumYFo3cpyCHgBYQ34ylaMsJ+SNHQ1kApMIa8jN+i593zQuaw3AdWH0nJTARzCFhg==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.1.5" + } + }, + "node_modules/alpinejs/node_modules/@vue/shared": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.1.5.tgz", + "integrity": "sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA==", + "license": "MIT" + }, "node_modules/am-i-vibing": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/am-i-vibing/-/am-i-vibing-0.4.0.tgz", @@ -10623,6 +13623,59 @@ "am-i-vibing": "dist/cli.mjs" } }, + "node_modules/ansi-align": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", + "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", + "license": "ISC", + "dependencies": { + "string-width": "^4.1.0" + } + }, + "node_modules/ansi-align/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-align/node_modules/is-fullwidth-code-point": { + "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==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-align/node_modules/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", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-align/node_modules/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" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/ansi-escapes": { "version": "7.3.0", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.3.0.tgz", @@ -10853,6 +13906,37 @@ "dev": true, "license": "MIT" }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/asn1js": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.10.tgz", + "integrity": "sha512-S2s3aOytiKdFRdulw2qPE51MzjzVOisppcVv7jVFR+Kw0kxwvFrDcYA0h7Ndqbmj0HkMIXYWaoj7fli8kgx1eg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "pvtsutils": "^1.3.6", + "pvutils": "^1.1.5", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/assert-never": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/assert-never/-/assert-never-1.4.0.tgz", + "integrity": "sha512-5oJg84os6NMQNl27T9LnZkvvqzvAnHu03ShCnoj6bsJwS7L8AO4lf+C/XjK/nvzEqQB744moC6V128RucQd1jA==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/ast-kit": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/ast-kit/-/ast-kit-2.2.0.tgz", @@ -10869,6 +13953,18 @@ "url": "https://github.com/sponsors/sxzz" } }, + "node_modules/ast-types": { + "version": "0.16.1", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.16.1.tgz", + "integrity": "sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/ast-walker-scope": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/ast-walker-scope/-/ast-walker-scope-0.8.3.tgz", @@ -10885,6 +13981,15 @@ "url": "https://github.com/sponsors/sxzz" } }, + "node_modules/astring": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/astring/-/astring-1.9.0.tgz", + "integrity": "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==", + "license": "MIT", + "bin": { + "astring": "bin/astring" + } + }, "node_modules/astro": { "version": "7.0.7", "resolved": "https://registry.npmjs.org/astro/-/astro-7.0.7.tgz", @@ -11053,11 +14158,117 @@ } } }, + "node_modules/babel-dead-code-elimination": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/babel-dead-code-elimination/-/babel-dead-code-elimination-1.0.12.tgz", + "integrity": "sha512-GERT7L2TiYcYDtYk1IpD+ASAYXjKbLTDPhBtYj7X1NuRMDTMtAx9kyBenub1Ev41lo91OHCKdmP+egTDmfQ7Ig==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.23.7", + "@babel/parser": "^7.23.6", + "@babel/traverse": "^7.23.7", + "@babel/types": "^7.23.6" + } + }, + "node_modules/babel-loader": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-10.0.0.tgz", + "integrity": "sha512-z8jt+EdS61AMw22nSfoNJAZ0vrtmhPRVi6ghL3rCeRZI8cdNYFiV5xeV3HbE7rlZZNmGH8BVccwWt8/ED0QOHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^5.0.0" + }, + "engines": { + "node": "^18.20.0 || ^20.10.0 || >=22.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0", + "webpack": ">=5.61.0" + } + }, + "node_modules/babel-loader/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/babel-loader/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/babel-loader/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/babel-loader/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/babel-loader/node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/babel-plugin-jsx-dom-expressions": { "version": "0.40.7", "resolved": "https://registry.npmjs.org/babel-plugin-jsx-dom-expressions/-/babel-plugin-jsx-dom-expressions-0.40.7.tgz", "integrity": "sha512-/O6JWUmjv03OI9lL2ry9bUjpD5S3PclM55RRJEyCdcFZ5W2SEA/59d+l2hNsk3gI6kiWRdRPdOtqZmsQzFN1pQ==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-module-imports": "7.18.6", @@ -11074,7 +14285,6 @@ "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", - "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.18.6" @@ -11087,7 +14297,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", - "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=0.12" @@ -11100,7 +14309,6 @@ "version": "7.3.0", "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", - "dev": true, "license": "MIT", "dependencies": { "entities": "^6.0.0" @@ -11113,7 +14321,6 @@ "version": "1.9.12", "resolved": "https://registry.npmjs.org/babel-preset-solid/-/babel-preset-solid-1.9.12.tgz", "integrity": "sha512-LLqnuKVDlKpyBlMPcH6qEvs/wmS9a+NczppxJ3ryS/c0O5IiSFOIBQi9GzyiGDSbcJpx4Gr87jyFTos1MyEuWg==", - "dev": true, "license": "MIT", "dependencies": { "babel-plugin-jsx-dom-expressions": "^0.40.6" @@ -11128,6 +14335,20 @@ } } }, + "node_modules/babel-walk": { + "version": "3.0.0-canary-5", + "resolved": "https://registry.npmjs.org/babel-walk/-/babel-walk-3.0.0-canary-5.tgz", + "integrity": "sha512-GAwkz0AihzY5bkwIY5QDR+LvsRQgB/B+1foMPvi0FZPMl5fjD7ICiznUiBdLYMH1QYe6vqu4gWYytZOccLouFw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/types": "^7.9.6" + }, + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/bail": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", @@ -11266,6 +14487,16 @@ "dev": true, "license": "MIT" }, + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -11353,6 +14584,63 @@ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", "license": "ISC" }, + "node_modules/boxen": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-8.0.1.tgz", + "integrity": "sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==", + "license": "MIT", + "dependencies": { + "ansi-align": "^3.0.1", + "camelcase": "^8.0.0", + "chalk": "^5.3.0", + "cli-boxes": "^3.0.0", + "string-width": "^7.2.0", + "type-fest": "^4.21.0", + "widest-line": "^5.0.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boxen/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "license": "MIT" + }, + "node_modules/boxen/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boxen/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/brace-expansion": { "version": "5.0.7", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz", @@ -11474,6 +14762,16 @@ "node": ">= 0.8" } }, + "node_modules/bytestreamjs": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/bytestreamjs/-/bytestreamjs-2.0.1.tgz", + "integrity": "sha512-U1Z/ob71V/bXfVABvNr/Kumf5VyeQRBEm6Txb0PQ6S7V5GpBM3w4Cbqz/xPDicR5tN0uvDifng8C+5qECeGwyQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/c12": { "version": "3.3.4", "resolved": "https://registry.npmjs.org/c12/-/c12-3.3.4.tgz", @@ -11574,6 +14872,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/camel-case": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", @@ -11585,6 +14893,18 @@ "tslib": "^2.0.3" } }, + "node_modules/camelcase": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz", + "integrity": "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==", + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/caniuse-api": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", @@ -11631,7 +14951,6 @@ "version": "5.6.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", - "dev": true, "license": "MIT", "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" @@ -11660,6 +14979,17 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/character-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz", + "integrity": "sha512-+UqJQjFEFaTAs3bNsF2j2kEN1baG/zghZbdqoYEDxGZtJo9LBzl1A+m0D4n3qKx8N2FNv8/Xp6yV9mQmBuptaw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "is-regex": "^1.0.3" + } + }, "node_modules/chardet": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.2.0.tgz", @@ -11745,6 +15075,18 @@ "node": ">=0.10.0" } }, + "node_modules/cli-boxes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", + "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/cli-cursor": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", @@ -11807,6 +15149,38 @@ "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", "license": "MIT" }, + "node_modules/clipboardy": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-4.0.0.tgz", + "integrity": "sha512-5mOlNS0mhX0707P2I0aZ2V/cmHUEO/fL7VFLqszkhUsxt7RwnmrInf/eEQKlf5GzvYeHIjT+Ov1HRfNmymlG0w==", + "license": "MIT", + "dependencies": { + "execa": "^8.0.1", + "is-wsl": "^3.1.0", + "is64bit": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clipboardy/node_modules/is-wsl": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", + "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/cliui": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", @@ -11844,23 +15218,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/cliui/node_modules/wrap-ansi": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", - "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.2.1", - "string-width": "^7.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/clone-deep": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", @@ -12047,6 +15404,13 @@ "node": ">= 0.6" } }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, "node_modules/confbox": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.4.tgz", @@ -12072,6 +15436,18 @@ "node": "^14.18.0 || >=16.10.0" } }, + "node_modules/constantinople": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-4.0.1.tgz", + "integrity": "sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/parser": "^7.6.0", + "@babel/types": "^7.6.1" + } + }, "node_modules/content-disposition": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", @@ -12153,6 +15529,33 @@ "url": "https://opencollective.com/express" } }, + "node_modules/cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/crc-32": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", @@ -12268,6 +15671,78 @@ } } }, + "node_modules/css-minimizer-webpack-plugin": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-7.0.4.tgz", + "integrity": "sha512-2iACis+P8qdLj1tHcShtztkGhCNIRUajJj7iX0IM9a5FA0wXGwjV8Nf6+HsBjBfb4LO8TTAVoetBbM54V6f3+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "cssnano": "^7.0.4", + "jest-worker": "^30.0.5", + "postcss": "^8.4.40", + "schema-utils": "^4.2.0", + "serialize-javascript": "^6.0.2" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@parcel/css": { + "optional": true + }, + "@swc/css": { + "optional": true + }, + "clean-css": { + "optional": true + }, + "csso": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "lightningcss": { + "optional": true + } + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/jest-worker": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.4.1.tgz", + "integrity": "sha512-SHynN/q/QD++iNyvMdy+WMmbCGk8jIsNcRxycXbWubSOhvo6T+j2afcfUSl+3hYsiBebOTo0cT7c2H7CXugu1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@ungap/structured-clone": "^1.3.0", + "jest-util": "30.4.1", + "merge-stream": "^2.0.0", + "supports-color": "^8.1.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, "node_modules/css-select": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/css-select/-/css-select-6.0.0.tgz", @@ -12438,6 +15913,30 @@ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", "license": "MIT" }, + "node_modules/cuint": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/cuint/-/cuint-0.2.2.tgz", + "integrity": "sha512-d4ZVpCW31eWwCMe1YT3ur7mUDnTXbgwyzaL320DrcRT45rfjYxkt5QWLrmOJ+/UEAI2+fQgKe/fCjR8l4TpRgw==", + "dev": true, + "license": "MIT" + }, + "node_modules/dax-sh": { + "version": "0.43.2", + "resolved": "https://registry.npmjs.org/dax-sh/-/dax-sh-0.43.2.tgz", + "integrity": "sha512-uULa1sSIHgXKGCqJ/pA0zsnzbHlVnuq7g8O2fkHokWFNwEGIhh5lAJlxZa1POG5En5ba7AU4KcBAvGQWMMf8rg==", + "deprecated": "This package has moved to simply be 'dax' instead of 'dax-sh'", + "license": "MIT", + "dependencies": { + "@deno/shim-deno": "~0.19.0", + "undici-types": "^5.26" + } + }, + "node_modules/dax-sh/node_modules/undici-types": { + "version": "5.28.4", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.28.4.tgz", + "integrity": "sha512-3OeMF5Lyowe8VW0skf5qaIE7Or3yS9LS7fvMUI0gg4YxpIBVg0L8BxCmROw2CcYhSkpR68Epz7CGc8MPj94Uww==", + "license": "MIT" + }, "node_modules/db0": { "version": "0.3.4", "resolved": "https://registry.npmjs.org/db0/-/db0-0.3.4.tgz", @@ -12578,7 +16077,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.8", @@ -12642,6 +16140,14 @@ "node": ">=6" } }, + "node_modules/doctypes": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz", + "integrity": "sha512-LLBi6pEqS6Do3EKQ3J0NqHWV5hhb78Pi8vvESYwyOy2c31ZEZVdtitdzsQsKb7878PEERhzUk0ftqGhG6Mz+pQ==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/dom-converter": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", @@ -12816,6 +16322,22 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "license": "MIT" }, + "node_modules/emoji-regex-xs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex-xs/-/emoji-regex-xs-1.0.0.tgz", + "integrity": "sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==", + "license": "MIT" + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/encodeurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", @@ -12887,6 +16409,38 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "prr": "~1.0.1" + }, + "bin": { + "errno": "cli.js" + } + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/error-stack-parser": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", + "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", + "license": "MIT", + "dependencies": { + "stackframe": "^1.3.4" + } + }, "node_modules/error-stack-parser-es": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/error-stack-parser-es/-/error-stack-parser-es-1.0.5.tgz", @@ -12981,6 +16535,38 @@ "@esbuild/win32-x64": "0.28.1" } }, + "node_modules/esbuild-loader": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/esbuild-loader/-/esbuild-loader-4.5.0.tgz", + "integrity": "sha512-rVYe97IN1+VoealVHQ3STEPbfTm+vvPk7AZPrL53Pt6JUGBhHTnIqyaow7EveMFlmQ2A1bLp7q19sP4PppaCDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.28.1", + "get-tsconfig": "^4.10.1", + "loader-utils": "^2.0.4", + "webpack-sources": "^3.3.4" + }, + "funding": { + "url": "https://github.com/privatenumber/esbuild-loader?sponsor=1" + }, + "peerDependencies": { + "webpack": "^4.40.0 || ^5.0.0" + } + }, + "node_modules/esbuild-loader/node_modules/get-tsconfig": { + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.14.0.tgz", + "integrity": "sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -13028,6 +16614,19 @@ "integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==", "license": "MIT" }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/esrap": { "version": "2.2.13", "resolved": "https://registry.npmjs.org/esrap/-/esrap-2.2.13.tgz", @@ -13293,6 +16892,13 @@ "node": ">=8.6.0" } }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, "node_modules/fast-npm-meta": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/fast-npm-meta/-/fast-npm-meta-1.5.1.tgz", @@ -13346,6 +16952,16 @@ "fast-string-width": "^3.0.2" } }, + "node_modules/fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.9.1" + } + }, "node_modules/fastq": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", @@ -13385,6 +17001,80 @@ } } }, + "node_modules/file-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", + "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/file-loader/node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/file-loader/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/file-loader/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/file-loader/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", @@ -13462,7 +17152,6 @@ "version": "1.16.0", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", - "dev": true, "funding": [ { "type": "individual", @@ -13516,6 +17205,207 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/fork-ts-checker-webpack-plugin": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-9.1.0.tgz", + "integrity": "sha512-mpafl89VFPJmhnJ1ssH+8wmM2b50n+Rew5x42NeI2U78aRWgtkEtGmctp7iT16UjquJTjorEmIfESj3DxdW84Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.16.7", + "chalk": "^4.1.2", + "chokidar": "^4.0.1", + "cosmiconfig": "^8.2.0", + "deepmerge": "^4.2.2", + "fs-extra": "^10.0.0", + "memfs": "^3.4.1", + "minimatch": "^3.0.4", + "node-abort-controller": "^3.0.1", + "schema-utils": "^3.1.1", + "semver": "^7.3.5", + "tapable": "^2.2.1" + }, + "engines": { + "node": ">=14.21.3" + }, + "peerDependencies": { + "typescript": ">3.6.0", + "webpack": "^5.11.0" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/ansi-styles": { + "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" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/balanced-match": { + "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/fork-ts-checker-webpack-plugin/node_modules/brace-expansion": { + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/chalk": { + "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", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/memfs": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", + "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", + "dev": true, + "license": "Unlicense", + "dependencies": { + "fs-monkey": "^1.0.4" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/supports-color": { + "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" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -13548,6 +17438,21 @@ "node": ">= 0.8" } }, + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/fs-minipass": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz", @@ -13561,6 +17466,13 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/fs-monkey": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.1.0.tgz", + "integrity": "sha512-QMUezzXWII9EV5aTFXW1UBVUO77wYPpjqIF8/AviUCThNeSYZykpoTixUeaNNBwmCev0AMDWMAni+f8Hxb1IFw==", + "dev": true, + "license": "Unlicense" + }, "node_modules/fsevents": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", @@ -13904,6 +17816,30 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hash-sum": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-2.0.0.tgz", + "integrity": "sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==", + "dev": true, + "license": "MIT" + }, "node_modules/hasown": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", @@ -14051,7 +17987,6 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==", - "dev": true, "license": "MIT" }, "node_modules/html-escaper": { @@ -14082,6 +18017,12 @@ "node": ">=12" } }, + "node_modules/html-to-image": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/html-to-image/-/html-to-image-1.11.13.tgz", + "integrity": "sha512-cuOPoI7WApyhBElTTb9oqsawRvZ0rHhaHwghRLlTuffoD1B2aDemlCruLeZrUIIdvG7gs9xeELEPm6PhuASqrg==", + "license": "MIT" + }, "node_modules/html-void-elements": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", @@ -14202,7 +18143,6 @@ "version": "1.18.1", "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", - "dev": true, "license": "MIT", "dependencies": { "eventemitter3": "^4.0.0", @@ -14279,7 +18219,6 @@ "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "dev": true, "license": "MIT" }, "node_modules/http-shutdown": { @@ -14402,6 +18341,33 @@ "dev": true, "license": "MIT" }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/import-local": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", @@ -14566,6 +18532,13 @@ "url": "https://github.com/sponsors/brc-dd" } }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -14609,6 +18582,32 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-expression": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-4.0.0.tgz", + "integrity": "sha512-zMIXX63sxzG3XrkHkrAPvm/OVZVSCPNkwMHU8oTX7/U3AL78I0QXCEICXUM13BIa8TYGZ68PiTKfQz3yaTNr4A==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "acorn": "^7.1.1", + "object-assign": "^4.1.1" + } + }, + "node_modules/is-expression/node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -14801,6 +18800,26 @@ "@types/estree": "*" } }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-stream": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", @@ -14830,7 +18849,6 @@ "version": "4.1.16", "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz", "integrity": "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==", - "dev": true, "license": "MIT", "engines": { "node": ">=12.13" @@ -14866,6 +18884,21 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is64bit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is64bit/-/is64bit-2.0.0.tgz", + "integrity": "sha512-jv+8jaWCl0g2lSBkNSVXdzfBA0npK1HGC2KtWM9FumFRoGS94g3NbCCLVnCYHLjp4GrW2KZeeSTMo5ddtznmGw==", + "license": "MIT", + "dependencies": { + "system-architecture": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -14930,6 +18963,80 @@ "@pkgjs/parseargs": "^0.11.0" } }, + "node_modules/jest-regex-util": { + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.4.0.tgz", + "integrity": "sha512-mWlvLviKIgIQ8VCuM1xRdD0TWp3zlzionlmDBjuXVBs+VkmXq6FgW9T4Emr7oGz/Rk6feDCGyiugolcQEyp3mg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-util": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.4.1.tgz", + "integrity": "sha512-vjQb1sACEiv13DKJMDToJpzVW0joCsIQrmbg0fi7CyOOt+g9jTuQl2A216pWRBYhOVt53XbL/2LbMKg1BECWOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.4.1", + "@types/node": "*", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "graceful-fs": "^4.2.11", + "picomatch": "^4.0.3" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-util/node_modules/ansi-styles": { + "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" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-util/node_modules/chalk": { + "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", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-util/node_modules/supports-color": { + "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" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/jest-worker": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", @@ -14964,6 +19071,14 @@ "url": "https://github.com/sponsors/panva" } }, + "node_modules/js-stringify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz", + "integrity": "sha512-rtS5ATOo2Q5k1G+DADISilDA6lv79zIiwFd6CcjuIxGKLFm5C+RLImRscVap9k55i+MOZwgliw+NejvkLuGD5g==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -15046,6 +19161,19 @@ "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", "license": "MIT" }, + "node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, "node_modules/jsonparse": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", @@ -15056,6 +19184,26 @@ ], "license": "MIT" }, + "node_modules/jstransformer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz", + "integrity": "sha512-C9YK3Rf8q6VAPDCCU9fnqo3mAfOH6vUGnMcP4AQAYIEpWtfGLpwOTmZ+igtdK5y+VvI2n3CyYSzy4Qh34eq24A==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "is-promise": "^2.0.0", + "promise": "^7.0.1" + } + }, + "node_modules/jstransformer/node_modules/is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -15403,6 +19551,13 @@ "url": "https://github.com/sponsors/antonk52" } }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, "node_modules/listhen": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/listhen/-/listhen-1.10.0.tgz", @@ -15439,6 +19594,37 @@ "integrity": "sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==", "license": "MIT" }, + "node_modules/lit": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/lit/-/lit-3.3.3.tgz", + "integrity": "sha512-fycuvZg/hkpozL00lm1pEJH5nN/lr9ZXd6mJI2HSN4+Bzc+LDNdEApJ6HFbPkdFNHLvOplIIuJvxkS4XUxqirw==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit/reactive-element": "^2.1.0", + "lit-element": "^4.2.0", + "lit-html": "^3.3.0" + } + }, + "node_modules/lit-element": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-4.2.2.tgz", + "integrity": "sha512-aFKhNToWxoyhkNDmWZwEva2SlQia+jfG0fjIWV//YeTaWrVnOxD89dPKfigCUspXFmjzOEUQpOkejH5Ly6sG0w==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit-labs/ssr-dom-shim": "^1.5.0", + "@lit/reactive-element": "^2.1.0", + "lit-html": "^3.3.0" + } + }, + "node_modules/lit-html": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-3.3.3.tgz", + "integrity": "sha512-el8M6jK2o3RXBnrSHX3ZKrsN8zEV63pSExTO1wYJz7QndGYZ8353e2a5PPX+qHe2aGayfnchQmkAojaWAREOIA==", + "license": "BSD-3-Clause", + "dependencies": { + "@types/trusted-types": "^2.0.2" + } + }, "node_modules/loader-runner": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.2.tgz", @@ -15453,6 +19639,21 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, "node_modules/local-pkg": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-1.2.1.tgz", @@ -15544,13 +19745,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/log-update/node_modules/emoji-regex": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", - "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", - "dev": true, - "license": "MIT" - }, "node_modules/log-update/node_modules/slice-ansi": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz", @@ -15568,42 +19762,6 @@ "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, - "node_modules/log-update/node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-update/node_modules/wrap-ansi": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", - "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.2.1", - "string-width": "^7.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/lower-case": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", @@ -15682,6 +19840,32 @@ "source-map-js": "^1.2.1" } }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "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" + } + }, "node_modules/make-fetch-happen": { "version": "15.0.6", "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-15.0.6.tgz", @@ -15783,11 +19967,57 @@ "tslib": "2" } }, + "node_modules/memory-fs": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", + "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", + "dev": true, + "license": "MIT", + "dependencies": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + }, + "engines": { + "node": ">=4.3.0 <5.0.0 || >=5.10" + } + }, + "node_modules/memory-fs/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/memory-fs/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/memory-fs/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/merge-anything": { "version": "5.1.7", "resolved": "https://registry.npmjs.org/merge-anything/-/merge-anything-5.1.7.tgz", "integrity": "sha512-eRtbOb1N5iyH0tkQDAoQ4Ipsp/5qSR79Dzrz8hEPxRX10RWWR/iQXdoKmBSRCThY1Fh5EhISDtpSc93fpxUniQ==", - "dev": true, "license": "MIT", "dependencies": { "is-what": "^4.1.8" @@ -16016,6 +20246,27 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/mini-css-extract-plugin": { + "version": "2.10.2", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.10.2.tgz", + "integrity": "sha512-AOSS0IdEB95ayVkxn5oGzNQwqAi2J0Jb/kKm43t7H73s8+f5873g0yuj0PNvK4dO75mu5DHg4nlgp4k6Kga8eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "schema-utils": "^4.0.0", + "tapable": "^2.2.1" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, "node_modules/minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -16038,6 +20289,16 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/minimizer-webpack-plugin": { "version": "5.6.1", "resolved": "https://registry.npmjs.org/minimizer-webpack-plugin/-/minimizer-webpack-plugin-5.6.1.tgz", @@ -17261,6 +21522,13 @@ "tslib": "^2.0.3" } }, + "node_modules/node-abort-controller": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", + "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==", + "dev": true, + "license": "MIT" + }, "node_modules/node-addon-api": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", @@ -17927,6 +22195,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/opener": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", + "dev": true, + "license": "(WTFPL OR MIT)", + "bin": { + "opener": "bin/opener-bin.js" + } + }, "node_modules/ordered-binary": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/ordered-binary/-/ordered-binary-1.6.1.tgz", @@ -18220,6 +22498,45 @@ "tslib": "^2.0.3" } }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-json/node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, "node_modules/parse5": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz", @@ -18346,6 +22663,16 @@ "url": "https://opencollective.com/express" } }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/pathe": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", @@ -18382,6 +22709,19 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pify": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-6.1.0.tgz", + "integrity": "sha512-KocF8ve28eFjjuBKKGvzOBGzG8ew2OqOOSxTTZhirkzH7h3BI1vyzqlR0qbfcDBve1Yzo3FVlWUAtCRrbVN8Fw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/piscina": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/piscina/-/piscina-5.2.0.tgz", @@ -18429,6 +22769,24 @@ "pathe": "^2.0.3" } }, + "node_modules/pkijs": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/pkijs/-/pkijs-3.4.0.tgz", + "integrity": "sha512-emEcLuomt2j03vxD54giVB4SxTjnsqkU692xZOZXHDVoYyypEm+b3jpiTcc+Cf+myooc+/Ly0z01jqeNHVgJGw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@noble/hashes": "1.4.0", + "asn1js": "^3.0.6", + "bytestreamjs": "^2.0.1", + "pvtsutils": "^1.3.6", + "pvutils": "^1.1.3", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/playwright": { "version": "1.61.1", "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.61.1.tgz", @@ -18600,6 +22958,58 @@ "postcss": "^8.5.13" } }, + "node_modules/postcss-import": { + "version": "16.1.1", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-16.1.1.tgz", + "integrity": "sha512-2xVS1NCZAfjtVdvXiyegxzJ447GyqCeEI5V7ApgQVOWnros1p5lGNovJNapwPpMombyFBfqDwt7AD3n2l0KOfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-import-resolver": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-import-resolver/-/postcss-import-resolver-2.0.0.tgz", + "integrity": "sha512-y001XYgGvVwgxyxw9J1a5kqM/vtmIQGzx34g0A0Oy44MFcy/ZboZw1hu/iN3VYFjSTRzbvd7zZJJz0Kh0AGkTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "enhanced-resolve": "^4.1.1" + } + }, + "node_modules/postcss-import-resolver/node_modules/enhanced-resolve": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz", + "integrity": "sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.5.0", + "tapable": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-import-resolver/node_modules/tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/postcss-media-query-parser": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", @@ -19022,6 +23432,69 @@ "postcss": "^8.5.13" } }, + "node_modules/postcss-url": { + "version": "10.1.4", + "resolved": "https://registry.npmjs.org/postcss-url/-/postcss-url-10.1.4.tgz", + "integrity": "sha512-/oBzyLOHQvXvVr/7bzZOFD5lYTy1nomVE4aMA9eY5KQsHfWLDIzb86q8XoUsmrj2xKoGYMAvd884EzJEQzuXIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "make-dir": "~3.1.0", + "mime": "~2.5.2", + "minimatch": "^3.1.5", + "xxhashjs": "~0.2.2" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-url/node_modules/balanced-match": { + "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/postcss-url/node_modules/brace-expansion": { + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/postcss-url/node_modules/mime": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz", + "integrity": "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/postcss-url/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/postcss-value-parser": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", @@ -19040,6 +23513,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/preact": { + "version": "10.27.2", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.27.2.tgz", + "integrity": "sha512-5SYSgFKSyhCbk6SrXyMpqjb5+MQBgfvEKE/OC+PujcY34sOpqtr+0AZQtPYx5IA6VxynQ7rUPCtKzyovpj9Bpg==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, "node_modules/prettier": { "version": "3.9.5", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.5.tgz", @@ -19079,6 +23562,16 @@ "renderkid": "^3.0.0" } }, + "node_modules/pretty-time": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pretty-time/-/pretty-time-1.1.0.tgz", + "integrity": "sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/prismjs": { "version": "1.30.0", "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", @@ -19122,6 +23615,17 @@ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "license": "MIT" }, + "node_modules/promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "asap": "~2.0.3" + } + }, "node_modules/property-information": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.2.0.tgz", @@ -19146,6 +23650,232 @@ "node": ">= 0.10" } }, + "node_modules/prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", + "dev": true, + "license": "MIT" + }, + "node_modules/pug": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pug/-/pug-3.0.4.tgz", + "integrity": "sha512-kFfq5mMzrS7+wrl5pLJzZEzemx34OQ0w4SARfhy/3yxTlhbstsudDwJzhf1hP02yHzbjoVMSXUj/Sz6RNfMyXg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "pug-code-gen": "^3.0.4", + "pug-filters": "^4.0.0", + "pug-lexer": "^5.0.1", + "pug-linker": "^4.0.0", + "pug-load": "^3.0.0", + "pug-parser": "^6.0.0", + "pug-runtime": "^3.0.1", + "pug-strip-comments": "^2.0.0" + } + }, + "node_modules/pug-attrs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pug-attrs/-/pug-attrs-3.0.0.tgz", + "integrity": "sha512-azINV9dUtzPMFQktvTXciNAfAuVh/L/JCl0vtPCwvOA21uZrC08K/UnmrL+SXGEVc1FwzjW62+xw5S/uaLj6cA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "constantinople": "^4.0.1", + "js-stringify": "^1.0.2", + "pug-runtime": "^3.0.0" + } + }, + "node_modules/pug-code-gen": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pug-code-gen/-/pug-code-gen-3.0.4.tgz", + "integrity": "sha512-6okWYIKdasTyXICyEtvobmTZAVX57JkzgzIi4iRJlin8kmhG+Xry2dsus+Mun/nGCn6F2U49haHI5mkELXB14g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "constantinople": "^4.0.1", + "doctypes": "^1.1.0", + "js-stringify": "^1.0.2", + "pug-attrs": "^3.0.0", + "pug-error": "^2.1.0", + "pug-runtime": "^3.0.1", + "void-elements": "^3.1.0", + "with": "^7.0.0" + } + }, + "node_modules/pug-error": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pug-error/-/pug-error-2.1.0.tgz", + "integrity": "sha512-lv7sU9e5Jk8IeUheHata6/UThZ7RK2jnaaNztxfPYUY+VxZyk/ePVaNZ/vwmH8WqGvDz3LrNYt/+gA55NDg6Pg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/pug-filters": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pug-filters/-/pug-filters-4.0.0.tgz", + "integrity": "sha512-yeNFtq5Yxmfz0f9z2rMXGw/8/4i1cCFecw/Q7+D0V2DdtII5UvqE12VaZ2AY7ri6o5RNXiweGH79OCq+2RQU4A==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "constantinople": "^4.0.1", + "jstransformer": "1.0.0", + "pug-error": "^2.0.0", + "pug-walk": "^2.0.0", + "resolve": "^1.15.1" + } + }, + "node_modules/pug-lexer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pug-lexer/-/pug-lexer-5.0.1.tgz", + "integrity": "sha512-0I6C62+keXlZPZkOJeVam9aBLVP2EnbeDw3An+k0/QlqdwH6rv8284nko14Na7c0TtqtogfWXcRoFE4O4Ff20w==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "character-parser": "^2.2.0", + "is-expression": "^4.0.0", + "pug-error": "^2.0.0" + } + }, + "node_modules/pug-linker": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pug-linker/-/pug-linker-4.0.0.tgz", + "integrity": "sha512-gjD1yzp0yxbQqnzBAdlhbgoJL5qIFJw78juN1NpTLt/mfPJ5VgC4BvkoD3G23qKzJtIIXBbcCt6FioLSFLOHdw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "pug-error": "^2.0.0", + "pug-walk": "^2.0.0" + } + }, + "node_modules/pug-load": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pug-load/-/pug-load-3.0.0.tgz", + "integrity": "sha512-OCjTEnhLWZBvS4zni/WUMjH2YSUosnsmjGBB1An7CsKQarYSWQ0GCVyd4eQPMFJqZ8w9xgs01QdiZXKVjk92EQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "object-assign": "^4.1.1", + "pug-walk": "^2.0.0" + } + }, + "node_modules/pug-parser": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/pug-parser/-/pug-parser-6.0.0.tgz", + "integrity": "sha512-ukiYM/9cH6Cml+AOl5kETtM9NR3WulyVP2y4HOU45DyMim1IeP/OOiyEWRr6qk5I5klpsBnbuHpwKmTx6WURnw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "pug-error": "^2.0.0", + "token-stream": "1.0.0" + } + }, + "node_modules/pug-plain-loader": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pug-plain-loader/-/pug-plain-loader-1.1.0.tgz", + "integrity": "sha512-1nYgIJLaahRuHJHhzSPODV44aZfb00bO7kiJiMkke6Hj4SVZftuvx6shZ4BOokk50dJc2RSFqNUBOlus0dniFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "loader-utils": "^1.1.0" + }, + "peerDependencies": { + "pug": "^2.0.0 || ^3.0.0" + } + }, + "node_modules/pug-plain-loader/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/pug-plain-loader/node_modules/loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pug-runtime": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/pug-runtime/-/pug-runtime-3.0.1.tgz", + "integrity": "sha512-L50zbvrQ35TkpHwv0G6aLSuueDRwc/97XdY8kL3tOT0FmhgG7UypU3VztfV/LATAvmUfYi4wNxSajhSAeNN+Kg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/pug-strip-comments": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pug-strip-comments/-/pug-strip-comments-2.0.0.tgz", + "integrity": "sha512-zo8DsDpH7eTkPHCXFeAk1xZXJbyoTfdPlNR0bK7rpOMuhBYb0f5qUVCO1xlsitYd3w5FQTK7zpNVKb3rZoUrrQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "pug-error": "^2.0.0" + } + }, + "node_modules/pug-walk": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-2.0.0.tgz", + "integrity": "sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pvtsutils": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.3.6.tgz", + "integrity": "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.8.1" + } + }, + "node_modules/pvutils": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.1.5.tgz", + "integrity": "sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/qs": { "version": "6.15.3", "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz", @@ -19205,6 +23935,16 @@ "integrity": "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==", "license": "MIT" }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, "node_modules/range-parser": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.3.0.tgz", @@ -19234,6 +23974,80 @@ "node": ">= 0.10" } }, + "node_modules/raw-loader": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-4.0.2.tgz", + "integrity": "sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/raw-loader/node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/raw-loader/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/raw-loader/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/raw-loader/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/rc9": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/rc9/-/rc9-3.0.1.tgz", @@ -19265,6 +24079,26 @@ "react": "^19.2.5" } }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/read-cache/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/readable-stream": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", @@ -19330,6 +24164,31 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/recast": { + "version": "0.23.12", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.23.12.tgz", + "integrity": "sha512-dEWRjcINDu/F4l2dYx57ugBtD7HV9KXESyxhzw/MqWLeglJrsjJKqACPyUPg+6AF8mIgm+Zi0dZ3ACoIg+QtpA==", + "license": "MIT", + "dependencies": { + "ast-types": "^0.16.1", + "esprima": "~4.0.0", + "source-map": "~0.6.1", + "tiny-invariant": "^1.3.3", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/recast/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/rechoir": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", @@ -19587,7 +24446,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "dev": true, "license": "MIT" }, "node_modules/resolve": { @@ -20804,7 +25662,6 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/solid-refresh/-/solid-refresh-0.6.3.tgz", "integrity": "sha512-F3aPsX6hVw9ttm5LYlth8Q15x6MlI/J3Dn+o3EQyRTtTxidepSTwAYdozt01/YA+7ObcciagGEyXIopGZzQtbA==", - "dev": true, "license": "MIT", "dependencies": { "@babel/generator": "^7.23.6", @@ -20815,6 +25672,18 @@ "solid-js": "^1.3" } }, + "node_modules/solid-use": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/solid-use/-/solid-use-0.9.1.tgz", + "integrity": "sha512-UwvXDVPlrrbj/9ewG9ys5uL2IO4jSiwys2KPzK4zsnAcmEl7iDafZWW1Mo4BSEWOmQCGK6IvpmGHo1aou8iOFw==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "solid-js": "^1.7" + } + }, "node_modules/source-map": { "version": "0.7.6", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", @@ -20959,6 +25828,12 @@ "node": "^20.17.0 || >=22.9.0" } }, + "node_modules/stackframe": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", + "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==", + "license": "MIT" + }, "node_modules/standard-as-callback": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", @@ -21328,6 +26203,41 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/svelte-dev-helper": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/svelte-dev-helper/-/svelte-dev-helper-1.1.9.tgz", + "integrity": "sha512-oU+Xv7Dl4kRU2kdFjsoPLfJfnt5hUhsFUZtuzI3Ku/f2iAFZqBoEuXOqK3N9ngD4dxQOmN4OKWPHVi3NeAeAfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/svelte-hmr": { + "version": "0.14.12", + "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.14.12.tgz", + "integrity": "sha512-4QSW/VvXuqVcFZ+RhxiR8/newmwOCTlbYIezvkeN6302YFRE8cXy0naamHcjz8Y9Ce3ITTZtrHrIL0AGfyo61w==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^12.20 || ^14.13.1 || >= 16" + }, + "peerDependencies": { + "svelte": ">=3.19.0" + } + }, + "node_modules/svelte-loader": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/svelte-loader/-/svelte-loader-3.2.4.tgz", + "integrity": "sha512-e0HdDnkYH/MDx4/IfTSka5AOFg9yYJcPuoZJB5x0l60fkHjVjNvrrxr+rJegDG9J7ZymmdHt00/hdLw+QF299w==", + "dev": true, + "license": "MIT", + "dependencies": { + "loader-utils": "^2.0.4", + "svelte-dev-helper": "^1.1.9", + "svelte-hmr": "^0.14.2" + }, + "peerDependencies": { + "svelte": "^3.0.0 || ^4.0.0-next.0 || ^5.0.0-next.1" + } + }, "node_modules/svelte/node_modules/aria-query": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.1.tgz", @@ -21408,6 +26318,18 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/system-architecture": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/system-architecture/-/system-architecture-0.1.0.tgz", + "integrity": "sha512-ulAk51I9UVUyJgxlv9M6lFot2WP3e7t8Kz9+IS6D4rVba1tR9kON+Ey69f+1R4Q8cd45Lod6a4IcJIxnzGc/zA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/tagged-tag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/tagged-tag/-/tagged-tag-1.0.0.tgz", @@ -21480,6 +26402,21 @@ "streamx": "^2.12.5" } }, + "node_modules/terracotta": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/terracotta/-/terracotta-1.1.1.tgz", + "integrity": "sha512-Sa6wnvkGMFmPq8N/wQb2aKkIW2eXH0LJvUOEk6QZLBEjqy+LsbzAOpDuqEfOmfA/hexssE6eQve2i1IIOeOh4g==", + "license": "MIT", + "dependencies": { + "solid-use": "^0.9.1" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "solid-js": "^1.8" + } + }, "node_modules/terser": { "version": "5.49.0", "resolved": "https://registry.npmjs.org/terser/-/terser-5.49.0.tgz", @@ -21498,6 +26435,67 @@ "node": ">=10" } }, + "node_modules/terser-webpack-plugin": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.6.1.tgz", + "integrity": "sha512-201R5j+sJpK8nFWwKVyNfZot8FaJbLZDq5evriVzbV1wDtSXDjRUDRfJzHpAaxFDMEhsZL1QkeqM61wgsS3KaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "jest-worker": "^27.4.5", + "schema-utils": "^4.3.0", + "terser": "^5.31.1" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@minify-html/node": { + "optional": true + }, + "@swc/core": { + "optional": true + }, + "@swc/css": { + "optional": true + }, + "@swc/html": { + "optional": true + }, + "clean-css": { + "optional": true + }, + "cssnano": { + "optional": true + }, + "csso": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "html-minifier-terser": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "postcss": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, "node_modules/terser/node_modules/commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", @@ -21537,6 +26535,16 @@ "dev": true, "license": "MIT" }, + "node_modules/time-fix-plugin": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/time-fix-plugin/-/time-fix-plugin-2.0.7.tgz", + "integrity": "sha512-uVFet1LQToeUX0rTcSiYVYVoGuBpc8gP/2jnlUzuHMHe+gux6XLsNzxLUweabMwiUj5ejhoIMsUI55nVSEa/Vw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "webpack": ">=4.0.0" + } + }, "node_modules/tiny-inflate": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", @@ -21604,6 +26612,14 @@ "node": ">=0.6" } }, + "node_modules/token-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-1.0.0.tgz", + "integrity": "sha512-VSsyNPPW74RpHwR8Fc21uubwHY7wMDeJLys2IX5zJNih+OnAnaifKHo+1LHT7DAdloQ7apeaaWg8l7qnf/TnEg==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/totalist": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", @@ -21733,6 +26749,26 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, + "node_modules/tsyringe": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/tsyringe/-/tsyringe-4.10.0.tgz", + "integrity": "sha512-axr3IdNuVIxnaK5XGEUFTu3YmAQ6lllgrvqfEoR16g/HGnYY/6We4oWENtAnzK6/LpJ2ur9PAb80RBt7/U4ugw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^1.9.3" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/tsyringe/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true, + "license": "0BSD" + }, "node_modules/tuf-js": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-4.1.0.tgz", @@ -22060,6 +27096,16 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -22345,6 +27391,120 @@ "integrity": "sha512-0rjE8iEJe4YmT9TOhwsZtqCMRLc5DXZUI2UEYUUg63ikBkqqE5EYWaI0etFe/5KUcmcYwLih2RND1kq+hrUJXA==", "license": "MIT" }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/url-loader": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-4.1.1.tgz", + "integrity": "sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "loader-utils": "^2.0.0", + "mime-types": "^2.1.27", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "file-loader": "*", + "webpack": "^4.0.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "file-loader": { + "optional": true + } + } + }, + "node_modules/url-loader/node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/url-loader/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/url-loader/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/url-loader/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/url-loader/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/url-loader/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -22427,6 +27587,783 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/vinxi": { + "version": "0.5.11", + "resolved": "https://registry.npmjs.org/vinxi/-/vinxi-0.5.11.tgz", + "integrity": "sha512-82Qm+EG/b2PRFBvXBbz1lgWBGcd9totIL6SJhnrZYfakjloTVG9+5l6gfO6dbCCtztm5pqWFzLY0qpZ3H3ww/w==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.22.11", + "@babel/plugin-syntax-jsx": "^7.22.5", + "@babel/plugin-syntax-typescript": "^7.22.5", + "@types/micromatch": "^4.0.2", + "@vinxi/listhen": "^1.5.6", + "boxen": "^8.0.1", + "chokidar": "^4.0.3", + "citty": "^0.1.6", + "consola": "^3.4.2", + "crossws": "^0.3.4", + "dax-sh": "^0.43.0", + "defu": "^6.1.4", + "es-module-lexer": "^1.7.0", + "esbuild": "^0.25.3", + "get-port-please": "^3.1.2", + "h3": "1.15.3", + "hookable": "^5.5.3", + "http-proxy": "^1.18.1", + "micromatch": "^4.0.8", + "nitropack": "^2.11.10", + "node-fetch-native": "^1.6.6", + "path-to-regexp": "^6.2.1", + "pathe": "^1.1.1", + "radix3": "^1.1.2", + "resolve": "^1.22.10", + "serve-placeholder": "^2.0.1", + "serve-static": "^1.15.0", + "tinyglobby": "^0.2.14", + "ufo": "^1.6.1", + "unctx": "^2.4.1", + "unenv": "^1.10.0", + "unstorage": "^1.16.0", + "vite": "^6.4.1", + "zod": "^4.0.0" + }, + "bin": { + "vinxi": "bin/cli.mjs" + } + }, + "node_modules/vinxi/node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vinxi/node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vinxi/node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vinxi/node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vinxi/node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vinxi/node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vinxi/node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vinxi/node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vinxi/node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vinxi/node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vinxi/node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vinxi/node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vinxi/node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vinxi/node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vinxi/node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vinxi/node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vinxi/node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vinxi/node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vinxi/node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vinxi/node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vinxi/node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vinxi/node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vinxi/node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vinxi/node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vinxi/node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vinxi/node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vinxi/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/vinxi/node_modules/citty": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", + "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", + "license": "MIT", + "dependencies": { + "consola": "^3.2.3" + } + }, + "node_modules/vinxi/node_modules/cookie-es": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-1.2.3.tgz", + "integrity": "sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw==", + "license": "MIT" + }, + "node_modules/vinxi/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/vinxi/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/vinxi/node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "license": "MIT" + }, + "node_modules/vinxi/node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/vinxi/node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/vinxi/node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/vinxi/node_modules/h3": { + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/h3/-/h3-1.15.3.tgz", + "integrity": "sha512-z6GknHqyX0h9aQaTx22VZDf6QyZn+0Nh+Ym8O/u0SGSkyF5cuTJYKlc8MkzW3Nzf9LE1ivcpmYC3FUGpywhuUQ==", + "license": "MIT", + "dependencies": { + "cookie-es": "^1.2.2", + "crossws": "^0.3.4", + "defu": "^6.1.4", + "destr": "^2.0.5", + "iron-webcrypto": "^1.2.1", + "node-mock-http": "^1.0.0", + "radix3": "^1.1.2", + "ufo": "^1.6.1", + "uncrypto": "^0.1.3" + } + }, + "node_modules/vinxi/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/vinxi/node_modules/path-to-regexp": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", + "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", + "license": "MIT" + }, + "node_modules/vinxi/node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "license": "MIT" + }, + "node_modules/vinxi/node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/vinxi/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/vinxi/node_modules/send": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.4.1", + "range-parser": "~1.2.1", + "statuses": "~2.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/vinxi/node_modules/serve-static": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "~0.19.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/vinxi/node_modules/unenv": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/unenv/-/unenv-1.10.0.tgz", + "integrity": "sha512-wY5bskBQFL9n3Eca5XnhH6KbUo/tfvkwm9OpcdCvLaeA7piBNbavbOKJySEwQ1V0RH6HvNlSAFRTpvTqgKRQXQ==", + "license": "MIT", + "dependencies": { + "consola": "^3.2.3", + "defu": "^6.1.4", + "mime": "^3.0.0", + "node-fetch-native": "^1.6.4", + "pathe": "^1.1.2" + } + }, + "node_modules/vinxi/node_modules/unenv/node_modules/mime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/vinxi/node_modules/vite": { + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.3.tgz", + "integrity": "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==", + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, "node_modules/vite": { "version": "8.1.4", "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.4.tgz", @@ -22841,7 +28778,6 @@ "version": "2.11.12", "resolved": "https://registry.npmjs.org/vite-plugin-solid/-/vite-plugin-solid-2.11.12.tgz", "integrity": "sha512-FgjPcx2OwX9h6f28jli7A4bG7PP3te8uyakE5iqsmpq3Jqi1TWLgSroC9N6cMfGRU2zXsl4Q6ISvTr2VL0QHpA==", - "dev": true, "license": "MIT", "dependencies": { "@babel/core": "^7.23.3", @@ -22952,6 +28888,17 @@ } } }, + "node_modules/void-elements": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", + "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/volar-service-css": { "version": "0.0.71", "resolved": "https://registry.npmjs.org/volar-service-css/-/volar-service-css-0.0.71.tgz", @@ -23270,6 +29217,75 @@ "integrity": "sha512-RutnB7X8c5hjq39NceArgXg28WZtZpGc3+J16ljMiYnFhKvd8hITxSWQSQ5bvldxMDU6gG5mkxl1MTQLXckVSQ==", "license": "MIT" }, + "node_modules/vue-loader": { + "version": "17.4.2", + "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-17.4.2.tgz", + "integrity": "sha512-yTKOA4R/VN4jqjw4y5HrynFL8AK0Z3/Jt7eOJXEitsm0GMRHDBjCfCiuTiLP7OESvsZYo2pATCWhDqxC5ZrM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "hash-sum": "^2.0.0", + "watchpack": "^2.4.0" + }, + "peerDependencies": { + "webpack": "^4.1.0 || ^5.0.0-0" + }, + "peerDependenciesMeta": { + "@vue/compiler-sfc": { + "optional": true + }, + "vue": { + "optional": true + } + } + }, + "node_modules/vue-loader/node_modules/ansi-styles": { + "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" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/vue-loader/node_modules/chalk": { + "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", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/vue-loader/node_modules/supports-color": { + "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" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/vue-router": { "version": "4.6.4", "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.6.4.tgz", @@ -23386,6 +29402,51 @@ } } }, + "node_modules/webpack-bundle-analyzer": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-5.3.1.tgz", + "integrity": "sha512-wP2EusncRGL1tZyMHC/umLkjPdYMkTL9nPEKh8G8dkYCJ9TyF6xnXFqjhdqmv5J900irN/g0P5jMvLT22krEXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@discoveryjs/json-ext": "^0.6.3", + "acorn": "^8.0.4", + "acorn-walk": "^8.0.0", + "commander": "^14.0.2", + "escape-string-regexp": "^5.0.0", + "html-escaper": "^3.0.3", + "opener": "^1.5.2", + "picocolors": "^1.0.0", + "sirv": "^3.0.2", + "ws": "^8.19.0" + }, + "bin": { + "webpack-bundle-analyzer": "lib/bin/analyzer.js" + }, + "engines": { + "node": ">= 20.9.0" + } + }, + "node_modules/webpack-bundle-analyzer/node_modules/@discoveryjs/json-ext": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.6.3.tgz", + "integrity": "sha512-4B4OijXeVNOPZlYA2oEwWOTkzyltLao+xbotHQeqN++Rv27Y6s818+n2Qkp8q+Fxhn0t/5lA5X1Mxktud8eayQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.17.0" + } + }, + "node_modules/webpack-bundle-analyzer/node_modules/commander": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", + "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, "node_modules/webpack-cli": { "version": "7.2.1", "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-7.2.1.tgz", @@ -23990,6 +30051,41 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/webpack-hot-middleware": { + "version": "2.26.1", + "resolved": "https://registry.npmjs.org/webpack-hot-middleware/-/webpack-hot-middleware-2.26.1.tgz", + "integrity": "sha512-khZGfAeJx6I8K9zKohEWWYN6KDlVw2DHownoe+6Vtwj1LP9WFgegXnVMSkZ/dBEBtXFwrkkydsaPFlB7f8wU2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-html-community": "0.0.8", + "html-entities": "^2.1.0", + "strip-ansi": "^6.0.0" + } + }, + "node_modules/webpack-hot-middleware/node_modules/ansi-regex": { + "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" + } + }, + "node_modules/webpack-hot-middleware/node_modules/strip-ansi": { + "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/webpack-merge": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-6.0.1.tgz", @@ -24084,6 +30180,44 @@ "node": ">= 8" } }, + "node_modules/widest-line": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-5.0.0.tgz", + "integrity": "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==", + "license": "MIT", + "dependencies": { + "string-width": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/widest-line/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "license": "MIT" + }, + "node_modules/widest-line/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/wildcard": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", @@ -24091,6 +30225,40 @@ "dev": true, "license": "MIT" }, + "node_modules/with": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/with/-/with-7.0.2.tgz", + "integrity": "sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/parser": "^7.9.6", + "@babel/types": "^7.9.6", + "assert-never": "^1.2.1", + "babel-walk": "3.0.0-canary-5" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/wrap-ansi-cjs": { "name": "wrap-ansi", "version": "7.0.0", @@ -24168,6 +30336,29 @@ "node": ">=8" } }, + "node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "license": "MIT" + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -24233,6 +30424,16 @@ "integrity": "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA==", "license": "MIT" }, + "node_modules/xxhashjs": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/xxhashjs/-/xxhashjs-0.2.2.tgz", + "integrity": "sha512-AkTuIuVTET12tpsVIQo+ZU6f/qDmKuRUcjaqR+OIvm+aCBsZ95i7UVY5WJ9TMsSaZ0DA2WxoZ4acu0sPH+OKAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cuint": "^0.2.2" + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/integration/scripts/apps.mjs b/integration/scripts/apps.mjs index 601e0f1..40bb8b7 100644 --- a/integration/scripts/apps.mjs +++ b/integration/scripts/apps.mjs @@ -2,10 +2,30 @@ export const apps = [ { id: 'standalone', kind: 'static', output: 'dist', expectedSpritePath: '/app-icons/sprite.svg' }, { id: 'standalone-vite', kind: 'static', output: 'dist' }, { id: 'standalone-webpack', kind: 'static', output: 'dist' }, - { id: 'react-vite', kind: 'static', output: 'dist' }, - { id: 'react-webpack', kind: 'static', output: 'dist' }, - { id: 'next-app-turbopack', kind: 'next' }, - { id: 'next-app-webpack', kind: 'next' }, - { id: 'next-pages-turbopack', kind: 'next' }, - { id: 'next-pages-webpack', kind: 'next' }, + { id: 'react-vite', kind: 'static', output: 'dist', frameworkTab: 'React' }, + { id: 'react-webpack', kind: 'static', output: 'dist', frameworkTab: 'React' }, + { id: 'next-app-turbopack', kind: 'next', frameworkTab: 'React' }, + { id: 'next-app-webpack', kind: 'next', frameworkTab: 'React' }, + { id: 'next-pages-turbopack', kind: 'next', frameworkTab: 'React' }, + { id: 'next-pages-webpack', kind: 'next', frameworkTab: 'React' }, + { id: 'vue-vite', kind: 'static', output: 'dist', frameworkTab: 'Vue' }, + { id: 'vue-webpack', kind: 'static', output: 'dist', frameworkTab: 'Vue' }, + { id: 'nuxt-vite', kind: 'nuxt', frameworkTab: 'Vue' }, + { id: 'nuxt-webpack', kind: 'nuxt', frameworkTab: 'Vue' }, + { id: 'svelte-vite', kind: 'static', output: 'dist', frameworkTab: 'Svelte' }, + { id: 'svelte-webpack', kind: 'static', output: 'dist', frameworkTab: 'Svelte' }, + { id: 'sveltekit', kind: 'sveltekit', frameworkTab: 'Svelte' }, + { id: 'angular', kind: 'static', output: 'dist/angular/browser', frameworkTab: 'Angular' }, + { id: 'angular-webpack', kind: 'static', output: 'dist', frameworkTab: 'Angular' }, + { id: 'astro', kind: 'static', output: 'dist', frameworkTab: 'Astro' }, + { id: 'solid-vite', kind: 'static', output: 'dist', frameworkTab: 'Solid' }, + { id: 'solid-webpack', kind: 'static', output: 'dist', frameworkTab: 'Solid' }, + { id: 'solid-start-vite', kind: 'solid-start', frameworkTab: 'SolidStart' }, + { id: 'preact-vite', kind: 'static', output: 'dist', frameworkTab: 'Preact' }, + { id: 'preact-webpack', kind: 'static', output: 'dist', frameworkTab: 'Preact' }, + { id: 'qwik-vite', kind: 'static', output: 'dist', frameworkTab: 'Qwik' }, + { id: 'lit-vite', kind: 'static', output: 'dist', frameworkTab: 'Lit', shadowIcon: true }, + { id: 'lit-webpack', kind: 'static', output: 'dist', frameworkTab: 'Lit', shadowIcon: true }, + { id: 'alpine-vite', kind: 'static', output: 'dist', frameworkTab: 'Alpine' }, + { id: 'alpine-webpack', kind: 'static', output: 'dist', frameworkTab: 'Alpine' }, ] diff --git a/integration/tests/apps.spec.ts b/integration/tests/apps.spec.ts index ee4ea9b..a688966 100644 --- a/integration/tests/apps.spec.ts +++ b/integration/tests/apps.spec.ts @@ -40,6 +40,10 @@ function startServer(app: (typeof apps)[number], port: number): ChildProcess { cwd = appRoot command = [path.join(appRoot, 'build/index.js')] break + case 'solid-start': + cwd = appRoot + command = [path.join(appRoot, '.output/server/index.mjs')] + break default: command = [path.join(integrationRoot, 'scripts/serve-static.mjs'), path.join(appRoot, app.output), String(port)] } @@ -111,10 +115,12 @@ for (const app of apps) { await expect(icon).toBeVisible() await expect(icon).toHaveAttribute('data-app', app.id) - if (app.id === 'standalone-vite' || app.id === 'standalone-webpack') { + if (app.id === 'standalone-vite' || app.id === 'standalone-webpack' || ('shadowIcon' in app && app.shadowIcon)) { expect(await icon.evaluate((element) => element.tagName.toLowerCase())).toBe('icons-icon') expect(await icon.evaluate((element) => element.shadowRoot !== null)).toBe(true) - await expect(icon.locator('svg')).toHaveAttribute('viewBox', '0 0 24 24') + if (app.id.startsWith('standalone')) { + await expect(icon.locator('svg')).toHaveAttribute('viewBox', '0 0 24 24') + } } const href = await icon.locator('use').getAttribute('href') @@ -145,10 +151,10 @@ for (const app of apps) { const dialog = viewer.getByRole('dialog', { name: 'check' }) await expect(dialog).toBeVisible() await expect(dialog.getByRole('tab', { name: 'SVG' })).toBeVisible() - if (app.id.startsWith('standalone')) { - await expect(dialog.getByRole('tab', { name: 'React' })).toHaveCount(0) + if ('frameworkTab' in app) { + await expect(dialog.getByRole('tab', { name: app.frameworkTab })).toBeVisible() } else { - await expect(dialog.getByRole('tab', { name: 'React' })).toBeVisible() + await expect(dialog.getByRole('tab', { name: 'React' })).toHaveCount(0) } const colorSwatch = dialog.getByRole('button', { name: /Изменить цвет --icon-color-1/ }) diff --git a/package-lock.json b/package-lock.json index 1f1f65f..e41ff4a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,8 +11,10 @@ "dependencies": { "colorette": "^2.0.20", "jiti": "^2.6.1", + "lit": "^3.3.3", "svg-sprite": "^2.0.4", - "tinyglobby": "^0.2.17" + "tinyglobby": "^0.2.17", + "vanilla-colorful": "^0.7.2" }, "bin": { "svg-sprites": "dist/cli.js" @@ -23,12 +25,10 @@ "@types/react-dom": "^19.2.3", "@types/svg-sprite": "^0.0.39", "jsdom": "^26.1.0", - "lit": "^3.3.3", "react": "^19.2.5", "react-dom": "^19.2.5", "tsup": "^8.4.0", - "typescript": "^5.8.3", - "vanilla-colorful": "^0.7.2" + "typescript": "^5.8.3" }, "engines": { "node": ">=18" @@ -676,14 +676,12 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.6.0.tgz", "integrity": "sha512-VHb0ALPMTlgKjM6yIxxoQNnpKyUKLD04VzeQdsiXkMqkvYlAHxq9glGLmgbb889/1GsohSOAjvQYoiBppXFqrQ==", - "dev": true, "license": "BSD-3-Clause" }, "node_modules/@lit/reactive-element": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-2.1.2.tgz", "integrity": "sha512-pbCDiVMnne1lYUIaYNN5wrwQXDtHaYtg7YEFPeW+hws6U47WeFvISGUWekPGKWOP1ygrs0ef0o1VJMk1exos5A==", - "dev": true, "license": "BSD-3-Clause", "dependencies": { "@lit-labs/ssr-dom-shim": "^1.5.0" @@ -1330,7 +1328,6 @@ "version": "2.0.7", "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", - "dev": true, "license": "MIT" }, "node_modules/@types/vinyl": { @@ -2200,7 +2197,6 @@ "version": "3.3.3", "resolved": "https://registry.npmjs.org/lit/-/lit-3.3.3.tgz", "integrity": "sha512-fycuvZg/hkpozL00lm1pEJH5nN/lr9ZXd6mJI2HSN4+Bzc+LDNdEApJ6HFbPkdFNHLvOplIIuJvxkS4XUxqirw==", - "dev": true, "license": "BSD-3-Clause", "dependencies": { "@lit/reactive-element": "^2.1.0", @@ -2212,7 +2208,6 @@ "version": "4.2.2", "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-4.2.2.tgz", "integrity": "sha512-aFKhNToWxoyhkNDmWZwEva2SlQia+jfG0fjIWV//YeTaWrVnOxD89dPKfigCUspXFmjzOEUQpOkejH5Ly6sG0w==", - "dev": true, "license": "BSD-3-Clause", "dependencies": { "@lit-labs/ssr-dom-shim": "^1.5.0", @@ -2224,7 +2219,6 @@ "version": "3.3.3", "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-3.3.3.tgz", "integrity": "sha512-el8M6jK2o3RXBnrSHX3ZKrsN8zEV63pSExTO1wYJz7QndGYZ8353e2a5PPX+qHe2aGayfnchQmkAojaWAREOIA==", - "dev": true, "license": "BSD-3-Clause", "dependencies": { "@types/trusted-types": "^2.0.2" @@ -3103,7 +3097,6 @@ "version": "0.7.2", "resolved": "https://registry.npmjs.org/vanilla-colorful/-/vanilla-colorful-0.7.2.tgz", "integrity": "sha512-z2YZusTFC6KnLERx1cgoIRX2CjPRP0W75N+3CC6gbvdX5Ch47rZkEMGO2Xnf+IEmi3RiFLxS18gayMA27iU7Kg==", - "dev": true, "license": "MIT" }, "node_modules/vinyl": { diff --git a/package.json b/package.json index 08a61c5..f0ad9cb 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@gromlab/svg-sprites", "version": "1.1.5", - "description": "Generate SVG sprites and typed icon components for standalone, React, and Next.js", + "description": "Generate external SVG sprites and typed native icon components for modern web frameworks", "type": "module", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -56,7 +56,7 @@ ], "scripts": { "build": "npm run build:package", - "build:package": "tsup && tsup --config tsup.viewer.config.ts", + "build:package": "tsup && tsup --config tsup.browser.config.ts && tsup --config tsup.viewer.config.ts", "build:skill": "node skills/svg-sprites/build.mjs", "check:skill": "node skills/svg-sprites/build.mjs --check", "dev": "tsup --watch", @@ -75,7 +75,11 @@ "sprite", "svg-sprite", "icons", + "vite", + "webpack", "nextjs", + "vue", + "svelte", "typescript", "codegen" ], @@ -97,8 +101,10 @@ "dependencies": { "colorette": "^2.0.20", "jiti": "^2.6.1", + "lit": "^3.3.3", "svg-sprite": "^2.0.4", - "tinyglobby": "^0.2.17" + "tinyglobby": "^0.2.17", + "vanilla-colorful": "^0.7.2" }, "peerDependencies": { "react": ">=18 <20" @@ -118,11 +124,9 @@ "@types/react-dom": "^19.2.3", "@types/svg-sprite": "^0.0.39", "jsdom": "^26.1.0", - "lit": "^3.3.3", "react": "^19.2.5", "react-dom": "^19.2.5", "tsup": "^8.4.0", - "typescript": "^5.8.3", - "vanilla-colorful": "^0.7.2" + "typescript": "^5.8.3" } } diff --git a/skills/svg-sprites/skill.config.mjs b/skills/svg-sprites/skill.config.mjs index d3bc0dd..5ce4ac4 100644 --- a/skills/svg-sprites/skill.config.mjs +++ b/skills/svg-sprites/skill.config.mjs @@ -48,7 +48,7 @@ const russianDocumentation = [ export default [ { name: 'svg-sprites', - description: 'Use only when configuring, generating, or troubleshooting @gromlab/svg-sprites. Triggers: @gromlab/svg-sprites, svg-sprite.config.json, svg-sprite.config.ts, defineSpriteConfig, generateSprite, standalone, standalone@vite, standalone@webpack, react@vite, react@webpack, next@app, next@pages, SpriteConfig.input, --input, SpriteViewer, or --icon-color-N. Do NOT use for custom SVG sprites, favicons, raster images, icon fonts, choosing an icon set, or inline SVG without this package.', + description: 'Use only when configuring, generating, or troubleshooting @gromlab/svg-sprites. Triggers: @gromlab/svg-sprites, svg-sprite.config.json, defineSpriteConfig, generateSprite, exact modes for standalone, React, Next.js, Vue, Nuxt, Svelte, Angular, Astro, Solid, Preact, Qwik, Lit, or Alpine.js, SpriteConfig.input, --input, SpriteViewer, or --icon-color-N. Do NOT use for custom SVG sprites, favicons, raster images, icon fonts, choosing an icon set, or inline SVG without this package.', output: '../artifacts/svg-sprites', maxSkillBytes: 48_000, documents: documents('en'), @@ -56,7 +56,7 @@ export default [ }, { name: 'svg-sprites-ru', - description: 'Используй только при настройке, изменении или диагностике @gromlab/svg-sprites. Триггеры: @gromlab/svg-sprites, svg-sprite.config.json, svg-sprite.config.ts, defineSpriteConfig, generateSprite, standalone, standalone@vite, standalone@webpack, react@vite, react@webpack, next@app, next@pages, SpriteConfig.input, --input, SpriteViewer и --icon-color-N. НЕ используй для самописных SVG-спрайтов, inline SVG, favicon, растровых изображений, icon fonts или выбора библиотеки иконок.', + description: 'Используй только при настройке, изменении или диагностике @gromlab/svg-sprites. Триггеры: @gromlab/svg-sprites, svg-sprite.config.json, defineSpriteConfig, generateSprite, exact modes для standalone, React, Next.js, Vue, Nuxt, Svelte, Angular, Astro, Solid, Preact, Qwik, Lit или Alpine.js, SpriteConfig.input, --input, SpriteViewer и --icon-color-N. НЕ используй для самописных SVG-спрайтов, inline SVG, favicon, растровых изображений, icon fonts или выбора библиотеки иконок.', output: '../artifacts/svg-sprites-ru', maxSkillBytes: 48_000, documents: documents('ru'), diff --git a/skills/svg-sprites/src/en/SKILL.md b/skills/svg-sprites/src/en/SKILL.md index 2b83029..6aad1a4 100644 --- a/skills/svg-sprites/src/en/SKILL.md +++ b/skills/svg-sprites/src/en/SKILL.md @@ -2,15 +2,15 @@ ## What the package does -`@gromlab/svg-sprites` is a CLI generator that builds SVG sprites from user-provided SVG files. The package does not include its own icon set: it compiles project SVGs into an external sprite asset, creates a native typed Web Component for standalone bundler modes, and creates a React component for React/Next.js. +`@gromlab/svg-sprites` is a CLI generator that builds SVG sprites from user-provided SVG files. The package does not include its own icon set: it compiles project SVGs into an external sprite asset and creates a typed native component for the selected exact framework and bundler mode. The package supports multiple independent sprites in one project. Each explicitly selected config file or config-less directory describes one sprite and gets its own: - SVG asset; - mode-specific manifest data; -- icon name types and production entry `.svg-sprite/index.js` for bundler modes; +- icon name types and production entry `.svg-sprite/index.js` for every mode except bare `standalone`; +- an isolated framework-native component and declarations for framework modes; - a native Web Component with an explicit registration function for `standalone@vite`/`standalone@webpack`; -- a React component only for React/Next.js; - a deployment-neutral JSON manifest without a public URL for bare `standalone`. The project determines how many sprite directories exist and where they live. For example, `name: 'file-manager'` produces `FileManagerIcon`, `FileManagerIconName`, and `fileManagerIconNames`, while another directory with `name: 'navigation'` produces a separate `NavigationIcon`. These are examples of per-sprite APIs, not fixed package exports. @@ -28,6 +28,26 @@ Select exactly one supported mode key: | Standalone + Webpack 5 | `standalone@webpack` | | React + Vite | `react@vite` | | React + Webpack 5 | `react@webpack` | +| Vue + Vite | `vue@vite` | +| Vue + Webpack | `vue@webpack` | +| Nuxt + Vite | `nuxt@vite` | +| Nuxt + Webpack | `nuxt@webpack` | +| Svelte + Vite | `svelte@vite` | +| Svelte + Webpack | `svelte@webpack` | +| SvelteKit + Vite | `sveltekit@vite` | +| Angular application builder | `angular@application` | +| Angular + Webpack | `angular@webpack` | +| Astro + Vite | `astro@vite` | +| Solid + Vite | `solid@vite` | +| Solid + Webpack | `solid@webpack` | +| SolidStart + Vite | `solid-start@vite` | +| Preact + Vite | `preact@vite` | +| Preact + Webpack | `preact@webpack` | +| Qwik + Vite | `qwik@vite` | +| Lit + Vite | `lit@vite` | +| Lit + Webpack | `lit@webpack` | +| Alpine.js + Vite | `alpine@vite` | +| Alpine.js + Webpack | `alpine@webpack` | | Next.js App Router + Turbopack | `next@app/turbopack` | | Next.js App Router + Webpack 5 | `next@app/webpack` | | Next.js Pages Router + Turbopack | `next@pages/turbopack` | @@ -48,7 +68,7 @@ The CLI accepts exactly one path. A `.ts`, `.js`, or `.json` file loads that exa } ``` -Generation through `npx` does not add the package to the project. Do not use incomplete `react`, `next@app`, `next@pages`, or `standalone@` keys, or the removed `legacy` mode. Use bare `standalone` only when the application publishes the SVG itself; use the complete Vite/Webpack key otherwise. Create one command per config file or directory when the project has multiple sprites. +Generation through `npx` does not add the package to the project. Do not invent shortened or generic mode keys, and do not use the removed `legacy` mode. Select one complete key from the table. Use bare `standalone` only when the application publishes the SVG itself. Create one command per config file or directory when the project has multiple sprites. ## Inspecting the project @@ -56,7 +76,7 @@ Establish the project's actual contract before making changes: 1. Read the root `package.json`, lockfile, and workspace configuration; identify the framework, bundler, and existing commands. 2. Find config files, commands containing `svg-sprites`, and imports of generated components. Config names are arbitrary; use the explicit CLI path and object fields. -3. For React, determine whether the project uses Vite or Webpack 5 from its scripts and configuration. For Next.js, separately determine the App/Pages Router and the bundler used by the actual `dev`/`build` commands. +3. Determine the framework, router when applicable, and actual bundler from scripts and configuration. For Next.js, separately determine the App/Pages Router and the bundler used by the real `dev`/`build` commands. 4. Check existing `predev`, `prebuild`, `pretypecheck`, and orchestration scripts. Do not overwrite them. 5. For a new sprite, choose a target directory without imposing a particular application layer or architecture. 6. Check TypeScript and alias settings. Package subpath exports require TypeScript 5+ with `moduleResolution: 'bundler'`, `'node16'`, or `'nodenext'`. @@ -88,13 +108,13 @@ Work in this order: 7. If the application imports the sprite-module directory, create a user-owned `index.ts` next to `.svg-sprite`; do not place user files inside the generated directory. 8. Run the first generation before typecheck or application startup, then inspect the mode-specific output and the actual component import. -Do not add the Viewer automatically. Connect it only when requested or when visual verification of the set, colors, or complex SVGs is needed. Get the production isolation pattern from the exact guide: Vite, Webpack, App Router, and Pages Router use different boundaries. +Do not add the Viewer automatically. Connect it only when requested or when visual verification of the set, colors, or complex SVGs is needed. Get the production isolation pattern from the exact guide: frameworks, bundlers, and routers use different boundaries. Do not copy snippets between exact modes even when their APIs look similar. Asset URLs, generated files, CSS handling, router boundaries, and debug-tool setup differ. ## Generated directory contract -After generation, a React/Next.js directory has this structure: +For example, after generation a React/Next.js directory has this structure: ```text svg-sprite/ @@ -126,14 +146,14 @@ export * from './.svg-sprite/index.js' The generator owns the complete `.svg-sprite` directory and replaces it on every run. Never put user files inside it. The generator also owns `.gitignore` when the selected mode creates it. Bare `standalone` preserves a user-owned `.gitignore`, but removes a managed `.gitignore` left by another mode. Generated paths must not contain symlinks. -In React/Next modes, the internal `index.js` exports the component from `react/react-component.js` and the readonly name array; `index.d.ts` adds props/style types and the icon-name union. Standalone bundler modes export Web Component helpers and types without `react/`; bare `standalone` does not create a facade. Bundler-mode manifest declarations define their types locally and do not import the generator package. The manifest contains mode, target, icon list, and icon metadata for debug tools; bundler manifests also contain a URL and are not imported by the production component. +Every exact-mode adapter owns its facade, framework directory, native component runtime, declarations, manifest source, styles, and asset URL. React/Next use `react/`; other framework modes use their own generated contract documented by the matching guide. Standalone bundler modes export Web Component helpers and types; bare `standalone` does not create a facade. Manifest declarations define their types locally and do not import the generator package. In bundler modes, the sprite remains a separate asset and SVG path data is not embedded in JavaScript. The content hash depends on bundler settings. Bare `standalone` creates a fixed filename, and the application owns its public name and versioning: -- `react@vite` generates a static `sprite.svg?no-inline` import, preventing Vite from inlining it; +- Vite-based adapters use a mode-owned static asset import that keeps the sprite external; - `standalone@vite` uses the same Vite asset mechanism and exports an href helper plus a native Web Component without React; - `standalone@webpack` uses Webpack Asset Modules and exports the same mode-local Web Component without React; -- React Webpack 5 and all Next modes obtain the asset through `new URL(..., import.meta.url).href`, which must be processed by the selected bundler; +- Webpack-based adapters and all Next modes use their adapter-owned external asset mechanism, commonly `new URL(..., import.meta.url).href`; - a custom Webpack SVG loader must not intercept the generated `sprite.svg`; - in Next mode, the generated component does not contain `'use client'` and works in Server Components, SSR, and SSG; do not add a client boundary solely for an icon; - the Next build command and mode key must agree: Turbopack with `.../turbopack`, Webpack with `.../webpack`. @@ -156,7 +176,7 @@ defineFileManagerIconElement() The native element has no runtime dependencies, selects the generated ID and `viewBox`, obtains the URL through the bundler, and renders `` in Shadow DOM. Its `icon` property is typed with the exact name union, while plain HTML attribute values are validated only at runtime. It defaults to `1em × 1em`; resize the host with CSS. Bare `standalone` does not generate a Web Component. -In React/Next.js, the same `name: 'file-manager'` creates the `FileManagerIcon` React component. For `name: 'navigation'`, use the generated `NavigationIcon`. +In component modes, the same `name: 'file-manager'` creates a native `FileManagerIcon` component. Its syntax and props follow the exact-mode guide. For React/Next.js, `name: 'navigation'` creates `NavigationIcon`. Import the component from the root of its sprite directory. `width` and `height` are optional: ordinary CSS classes can control the size. @@ -201,7 +221,7 @@ Automatic replacement targets `fill`/`stroke` attributes and inline `style`. The `SpriteViewer` is optional. Install `@gromlab/svg-sprites` as a development dependency only when the project needs the Viewer. It accepts manifests or statically discoverable loaders and provides search, themes, colors, and examples, but production components do not depend on it. -Open the exact guide before connecting the Viewer. Vite uses a separate HTML entry, plain Webpack uses a development-only entry, Next.js uses a debug route, and the App Router additionally requires a separate Client Component boundary. Do not transfer setup between modes. +Open the exact guide before connecting the Viewer. Frameworks, bundlers, and routers require different debug entries or client boundaries. Do not transfer setup between modes. ## Verifying the result @@ -211,7 +231,7 @@ After changing a config or SVG, perform these required checks: 2. Inspect the output for the selected exact mode: - bare `standalone` creates `sprite.svg` and `svg-sprite.manifest.json`; - `standalone@vite` and `standalone@webpack` additionally create `index.*`, `icon-data.*`, and a JS manifest, but no `react/` directory; - - React and Next.js modes also create `react/react-component.js`, its declaration, and its CSS Module. + - framework modes also create their adapter-owned native component runtime, declaration, and styles. 3. For modes with a public facade, inspect `.svg-sprite/index.js`, the adjacent `index.d.ts`, the name list, and the actual import through the user-owned barrel. 4. Inspect the manifest: mode and target must match the selected adapter, and the icon list must match the source SVGs. In bundler modes the URL must use the mode-specific mechanism; the bare JSON manifest intentionally has no public `spriteUrl`. 5. Run the project's existing typecheck when the mode creates types or user-owned TypeScript changed. @@ -254,7 +274,7 @@ References are included in the built skill. Open only the documents relevant to ### Overview -- [Package README](./references/README.md) covers capabilities, the primary React/Next.js scenario, and documentation links. +- [Package README](./references/README.md) covers capabilities, the primary React/Next.js example, all supported families, and documentation links. ### Configuration @@ -267,6 +287,26 @@ References are included in the built skill. Open only the documents relevant to - [`standalone@webpack`](./references/docs/en/guides/standalone-webpack.md) covers a vanilla Webpack 5 application and the Web Component. - [`react@vite`](./references/docs/en/guides/react-vite.md) covers React with Vite. - [`react@webpack`](./references/docs/en/guides/react-webpack.md) covers React with Webpack 5. +- [`vue@vite`](./references/docs/en/guides/vue-vite.md) covers Vue with Vite. +- [`vue@webpack`](./references/docs/en/guides/vue-webpack.md) covers Vue with Webpack. +- [`nuxt@vite`](./references/docs/en/guides/nuxt-vite.md) covers Nuxt with Vite. +- [`nuxt@webpack`](./references/docs/en/guides/nuxt-webpack.md) covers Nuxt with Webpack. +- [`svelte@vite`](./references/docs/en/guides/svelte-vite.md) covers Svelte with Vite. +- [`svelte@webpack`](./references/docs/en/guides/svelte-webpack.md) covers Svelte with Webpack. +- [`sveltekit@vite`](./references/docs/en/guides/sveltekit-vite.md) covers SvelteKit with Vite. +- [`angular@application`](./references/docs/en/guides/angular-application.md) covers the Angular application builder. +- [`angular@webpack`](./references/docs/en/guides/angular-webpack.md) covers Angular with Webpack. +- [`astro@vite`](./references/docs/en/guides/astro-vite.md) covers Astro with Vite. +- [`solid@vite`](./references/docs/en/guides/solid-vite.md) covers Solid with Vite. +- [`solid@webpack`](./references/docs/en/guides/solid-webpack.md) covers Solid with Webpack. +- [`solid-start@vite`](./references/docs/en/guides/solid-start-vite.md) covers SolidStart with Vite. +- [`preact@vite`](./references/docs/en/guides/preact-vite.md) covers Preact with Vite. +- [`preact@webpack`](./references/docs/en/guides/preact-webpack.md) covers Preact with Webpack. +- [`qwik@vite`](./references/docs/en/guides/qwik-vite.md) covers Qwik with Vite. +- [`lit@vite`](./references/docs/en/guides/lit-vite.md) covers Lit with Vite. +- [`lit@webpack`](./references/docs/en/guides/lit-webpack.md) covers Lit with Webpack. +- [`alpine@vite`](./references/docs/en/guides/alpine-vite.md) covers Alpine.js with Vite. +- [`alpine@webpack`](./references/docs/en/guides/alpine-webpack.md) covers Alpine.js with Webpack. - [`next@app/turbopack`](./references/docs/en/guides/next-app-turbopack.md) covers the Next.js App Router with Turbopack. - [`next@app/webpack`](./references/docs/en/guides/next-app-webpack.md) covers the Next.js App Router with Webpack. - [`next@pages/turbopack`](./references/docs/en/guides/next-pages-turbopack.md) covers the Next.js Pages Router with Turbopack. diff --git a/skills/svg-sprites/src/ru/SKILL.md b/skills/svg-sprites/src/ru/SKILL.md index 955a3ff..48a93f6 100644 --- a/skills/svg-sprites/src/ru/SKILL.md +++ b/skills/svg-sprites/src/ru/SKILL.md @@ -2,15 +2,15 @@ ## Что делает пакет -`@gromlab/svg-sprites` — CLI-генератор SVG-спрайтов для пользовательских SVG-файлов. Пакет не содержит собственного набора иконок: он собирает SVG проекта во внешний sprite asset, создаёт нативный типизированный Web Component для standalone bundler modes и React-компонент для React/Next.js. +`@gromlab/svg-sprites` — CLI-генератор SVG-спрайтов для пользовательских SVG-файлов. Пакет не содержит собственного набора иконок: он собирает SVG проекта во внешний sprite asset и создаёт типизированный нативный компонент для выбранного exact framework/bundler mode. Пакет рассчитан на несколько независимых спрайтов в одном проекте. Каждый явно выбранный config-файл или config-less каталог описывает один спрайт и получает собственные: - SVG asset; - mode-specific manifest data; -- для bundler modes — типы имён и production entry `.svg-sprite/index.js`; +- для всех modes, кроме bare `standalone`, — типы имён и production entry `.svg-sprite/index.js`; +- для framework modes — изолированный нативный компонент и declarations; - для `standalone@vite`/`standalone@webpack` — нативный Web Component с явной функцией регистрации; -- только для React/Next.js — React-компонент; - для bare `standalone` — deployment-neutral JSON manifest без публичного URL. Количество и расположение каталогов определяет проект. Например, `name: 'file-manager'` создаёт `FileManagerIcon`, `FileManagerIconName` и `fileManagerIconNames`, а другой каталог с `name: 'navigation'` создаст отдельный `NavigationIcon`. Это примеры API отдельных спрайтов, а не фиксированные экспорты пакета. @@ -28,6 +28,26 @@ Generated production runtime и declarations не импортируют `@groml | Standalone + Webpack 5 | `standalone@webpack` | | React + Vite | `react@vite` | | React + Webpack 5 | `react@webpack` | +| Vue + Vite | `vue@vite` | +| Vue + Webpack | `vue@webpack` | +| Nuxt + Vite | `nuxt@vite` | +| Nuxt + Webpack | `nuxt@webpack` | +| Svelte + Vite | `svelte@vite` | +| Svelte + Webpack | `svelte@webpack` | +| SvelteKit + Vite | `sveltekit@vite` | +| Angular application builder | `angular@application` | +| Angular + Webpack | `angular@webpack` | +| Astro + Vite | `astro@vite` | +| Solid + Vite | `solid@vite` | +| Solid + Webpack | `solid@webpack` | +| SolidStart + Vite | `solid-start@vite` | +| Preact + Vite | `preact@vite` | +| Preact + Webpack | `preact@webpack` | +| Qwik + Vite | `qwik@vite` | +| Lit + Vite | `lit@vite` | +| Lit + Webpack | `lit@webpack` | +| Alpine.js + Vite | `alpine@vite` | +| Alpine.js + Webpack | `alpine@webpack` | | Next.js App Router + Turbopack | `next@app/turbopack` | | Next.js App Router + Webpack 5 | `next@app/webpack` | | Next.js Pages Router + Turbopack | `next@pages/turbopack` | @@ -48,7 +68,7 @@ CLI принимает ровно один путь. Путь к файлу `.ts } ``` -Генерация через `npx` не добавляет package в проект. Не используй неполные `react`, `next@app`, `next@pages`, `standalone@` или удалённый `legacy`. Bare `standalone` выбирай только когда приложение само публикует SVG; для Vite/Webpack используй соответствующий полный key. Для нескольких спрайтов создай отдельную команду для каждого config-файла или каталога. +Генерация через `npx` не добавляет package в проект. Не придумывай сокращённые или generic mode keys и не используй удалённый `legacy`: выбери один полный key из таблицы. Bare `standalone` выбирай только когда приложение само публикует SVG. Для нескольких спрайтов создай отдельную команду для каждого config-файла или каталога. ## Инспекция проекта @@ -56,7 +76,7 @@ CLI принимает ровно один путь. Путь к файлу `.ts 1. Прочитай корневой `package.json`, lock-файл и workspace-конфигурацию; определи framework, bundler и существующие команды. 2. Найди config-файлы, команды `svg-sprites` и импорты generated-компонентов. Имя конфига произвольное; ориентируйся на переданный CLI путь и поля объекта. -3. Для React определи Vite или Webpack 5 по scripts и конфигу. Для Next.js отдельно определи App/Pages Router и сборщик реальных `dev`/`build` команд. +3. Определи framework, router при его наличии и фактический bundler по scripts и конфигу. Для Next.js отдельно определи App/Pages Router и сборщик реальных `dev`/`build` команд. 4. Проверь существующие `predev`, `prebuild`, `pretypecheck` и агрегирующие scripts. Не перезаписывай их. 5. Для нового спрайта выбери целевой каталог, не навязывая конкретный слой или архитектуру приложения. 6. Проверь TypeScript и alias-настройки. Для package subpath exports нужен TypeScript 5+ с `moduleResolution: 'bundler'`, `'node16'` или `'nodenext'`. @@ -88,13 +108,13 @@ CLI принимает ровно один путь. Путь к файлу `.ts 7. Если приложение импортирует каталог sprite-модуля, создай пользовательский `index.ts` рядом с `.svg-sprite`; не помещай пользовательские файлы внутрь generated-каталога. 8. Выполни первую генерацию до typecheck или запуска приложения, затем проверь mode-specific output и фактический импорт компонента. -Не добавляй Viewer автоматически. Подключай его только по запросу пользователя или когда нужна визуальная проверка набора, цветов либо сложных SVG. Способ изоляции Viewer от production бери из exact guide: Vite, Webpack, App Router и Pages Router используют разные границы. +Не добавляй Viewer автоматически. Подключай его только по запросу пользователя или когда нужна визуальная проверка набора, цветов либо сложных SVG. Способ изоляции Viewer от production бери из exact guide: frameworks, bundlers и routers используют разные границы. Не копируй snippets между exact modes даже при похожем API. Различаются asset URL, generated-файлы, CSS handling, router boundary и способ подключения debug-инструментов. ## Контракт generated-каталога -После генерации React/Next-каталог имеет следующий вид: +Например, после генерации React/Next-каталог имеет следующий вид: ```text svg-sprite/ @@ -126,14 +146,14 @@ export * from './.svg-sprite/index.js' Генератор полностью владеет каталогом `.svg-sprite` и заменяет его при каждом запуске. Никогда не помещай туда пользовательские файлы. Генератор также владеет `.gitignore`, когда выбранный mode его создаёт. Bare `standalone` сохраняет пользовательский `.gitignore`, но удаляет управляемый `.gitignore`, оставшийся после другого mode. Generated-пути не должны содержать symlink. -В React/Next modes внутренний `index.js` экспортирует компонент из `react/react-component.js` и readonly-массив имён, а `index.d.ts` добавляет props/style-типы и union имени. Standalone bundler modes экспортируют Web Component helpers и типы без `react/`; bare `standalone` не создаёт facade. Manifest declarations bundler modes объявляют типы локально и не импортируют generator package. Manifest содержит mode, target, список и метаданные иконок для debug-инструментов; bundler manifest также содержит URL и не импортируется production-компонентом. +Каждый exact-mode adapter владеет facade, framework-каталогом, runtime нативного компонента, declarations, manifest source, styles и asset URL. React/Next используют `react/`; остальные framework modes используют собственный generated-контракт из соответствующего guide. Standalone bundler modes экспортируют Web Component helpers и типы, а bare `standalone` не создаёт facade. Manifest declarations объявляют типы локально и не импортируют generator package. В bundler modes спрайт остаётся отдельным asset, а SVG path-данные не встраиваются в JavaScript. Content hash зависит от настроек сборщика. Bare `standalone` создаёт файл с фиксированным именем, а приложение само определяет его публичное имя и версионирование: -- `react@vite` генерирует статический импорт `sprite.svg?no-inline`, запрещающий Vite inline; +- Vite-based adapters используют mode-owned static asset import, сохраняющий sprite внешним; - `standalone@vite` использует тот же Vite asset-механизм и экспортирует href helper и нативный Web Component без React; - `standalone@webpack` использует Webpack Asset Modules и экспортирует такой же mode-local Web Component без React; -- React Webpack 5 и все Next modes получают asset через `new URL(..., import.meta.url).href`, который должен обработать соответствующий сборщик; +- Webpack-based adapters и все Next modes используют adapter-owned механизм внешнего asset, обычно `new URL(..., import.meta.url).href`; - кастомный Webpack SVG loader не должен перехватывать generated `sprite.svg`; - в Next mode generated-компонент не содержит `'use client'` и работает в Server Components, SSR и SSG; не добавляй клиентскую границу только ради иконки; - команда сборки Next и mode key должны совпадать: Turbopack с `.../turbopack`, Webpack с `.../webpack`. @@ -156,7 +176,7 @@ defineFileManagerIconElement() Нативный элемент не имеет runtime-зависимостей, сам выбирает generated ID и `viewBox`, получает URL через bundler и рендерит `` в Shadow DOM. Его property `icon` типизирован точным union имён, но строковые HTML attributes проверяются только в runtime. Размер по умолчанию равен `1em × 1em`; меняй его через CSS на host. Bare `standalone` Web Component не генерирует. -В React/Next.js тот же `name: 'file-manager'` создаёт React-компонент `FileManagerIcon`. Для `name: 'navigation'` используй сгенерированный `NavigationIcon`. +В component modes тот же `name: 'file-manager'` создаёт нативный компонент `FileManagerIcon`; его синтаксис и props определяет exact-mode guide. В React/Next.js значение `name: 'navigation'` создаёт `NavigationIcon`. Импортируй компонент из корня соответствующего каталога спрайта. `width` и `height` не обязательны: размером можно управлять обычным CSS-классом. @@ -201,7 +221,7 @@ Generated-компонент не выбирает семантику за пр `SpriteViewer` необязателен. Установи `@gromlab/svg-sprites` как development dependency, только если проекту нужен Viewer. Он принимает manifests или статически обнаружимые loaders, показывает поиск, темы, цвета и примеры, но production-компоненты от него не зависят. -Перед подключением Viewer открой exact guide. Vite использует отдельную HTML entry, обычный Webpack — development-only entry, Next.js — debug route, а App Router дополнительно требует отдельную Client Component boundary. Не переноси способ подключения между modes. +Перед подключением Viewer открой exact guide. Frameworks, bundlers и routers требуют разных debug entries или client boundaries. Не переноси способ подключения между modes. ## Проверка результата @@ -211,7 +231,7 @@ Generated-компонент не выбирает семантику за пр 2. Проверь output выбранного exact mode: - bare `standalone` создаёт `sprite.svg` и `svg-sprite.manifest.json`; - `standalone@vite` и `standalone@webpack` дополнительно создают `index.*`, `icon-data.*` и JS manifest, но не каталог `react/`; - - React и Next.js modes также создают `react/react-component.js`, declaration и CSS Module. + - framework modes также создают adapter-owned runtime нативного компонента, declaration и styles. 3. Для modes с public facade проверь `.svg-sprite/index.js`, соседний `index.d.ts`, список имён и фактический импорт через пользовательский barrel. 4. Проверь manifest: mode и target должны соответствовать выбранному adapter, а список иконок — исходным SVG. В bundler modes URL должен формироваться mode-specific способом; bare JSON manifest намеренно не содержит публичного `spriteUrl`. 5. Запусти существующий typecheck проекта, если mode создаёт типы или изменился пользовательский TypeScript-код. @@ -254,7 +274,7 @@ References являются частью собранного skill. Откры ### Обзор -- [README пакета](./references/README_RU.md) — возможности, основной React/Next.js сценарий и ссылки на документацию. +- [README пакета](./references/README_RU.md) — возможности, основной React/Next.js пример, все поддерживаемые families и ссылки на документацию. ### Конфигурация @@ -267,6 +287,26 @@ References являются частью собранного skill. Откры - [`standalone@webpack`](./references/docs/ru/guides/standalone-webpack.md) — vanilla-приложение с Webpack 5 и Web Component. - [`react@vite`](./references/docs/ru/guides/react-vite.md) — React с Vite. - [`react@webpack`](./references/docs/ru/guides/react-webpack.md) — React с Webpack 5. +- [`vue@vite`](./references/docs/ru/guides/vue-vite.md) — Vue с Vite. +- [`vue@webpack`](./references/docs/ru/guides/vue-webpack.md) — Vue с Webpack. +- [`nuxt@vite`](./references/docs/ru/guides/nuxt-vite.md) — Nuxt с Vite. +- [`nuxt@webpack`](./references/docs/ru/guides/nuxt-webpack.md) — Nuxt с Webpack. +- [`svelte@vite`](./references/docs/ru/guides/svelte-vite.md) — Svelte с Vite. +- [`svelte@webpack`](./references/docs/ru/guides/svelte-webpack.md) — Svelte с Webpack. +- [`sveltekit@vite`](./references/docs/ru/guides/sveltekit-vite.md) — SvelteKit с Vite. +- [`angular@application`](./references/docs/ru/guides/angular-application.md) — Angular application builder. +- [`angular@webpack`](./references/docs/ru/guides/angular-webpack.md) — Angular с Webpack. +- [`astro@vite`](./references/docs/ru/guides/astro-vite.md) — Astro с Vite. +- [`solid@vite`](./references/docs/ru/guides/solid-vite.md) — Solid с Vite. +- [`solid@webpack`](./references/docs/ru/guides/solid-webpack.md) — Solid с Webpack. +- [`solid-start@vite`](./references/docs/ru/guides/solid-start-vite.md) — SolidStart с Vite. +- [`preact@vite`](./references/docs/ru/guides/preact-vite.md) — Preact с Vite. +- [`preact@webpack`](./references/docs/ru/guides/preact-webpack.md) — Preact с Webpack. +- [`qwik@vite`](./references/docs/ru/guides/qwik-vite.md) — Qwik с Vite. +- [`lit@vite`](./references/docs/ru/guides/lit-vite.md) — Lit с Vite. +- [`lit@webpack`](./references/docs/ru/guides/lit-webpack.md) — Lit с Webpack. +- [`alpine@vite`](./references/docs/ru/guides/alpine-vite.md) — Alpine.js с Vite. +- [`alpine@webpack`](./references/docs/ru/guides/alpine-webpack.md) — Alpine.js с Webpack. - [`next@app/turbopack`](./references/docs/ru/guides/next-app-turbopack.md) — Next.js App Router с Turbopack. - [`next@app/webpack`](./references/docs/ru/guides/next-app-webpack.md) — Next.js App Router с Webpack. - [`next@pages/turbopack`](./references/docs/ru/guides/next-pages-turbopack.md) — Next.js Pages Router с Turbopack. diff --git a/src/cli/parse-args.ts b/src/cli/parse-args.ts index c6b57be..ebe6855 100644 --- a/src/cli/parse-args.ts +++ b/src/cli/parse-args.ts @@ -16,6 +16,26 @@ export const CLI_USAGE = [ ' standalone@webpack', ' react@vite', ' react@webpack', + ' vue@vite', + ' vue@webpack', + ' nuxt@vite', + ' nuxt@webpack', + ' svelte@vite', + ' svelte@webpack', + ' sveltekit@vite', + ' angular@application', + ' angular@webpack', + ' astro@vite', + ' solid@vite', + ' solid@webpack', + ' solid-start@vite', + ' preact@vite', + ' preact@webpack', + ' qwik@vite', + ' lit@vite', + ' lit@webpack', + ' alpine@vite', + ' alpine@webpack', ' next@app/turbopack', ' next@app/webpack', ' next@pages/turbopack', diff --git a/src/config.ts b/src/config.ts index bbf3fc2..fba0ad0 100644 --- a/src/config.ts +++ b/src/config.ts @@ -21,6 +21,26 @@ const MODES = new Set([ 'standalone@webpack', 'react@vite', 'react@webpack', + 'vue@vite', + 'vue@webpack', + 'nuxt@vite', + 'nuxt@webpack', + 'svelte@vite', + 'svelte@webpack', + 'sveltekit@vite', + 'angular@application', + 'angular@webpack', + 'astro@vite', + 'solid@vite', + 'solid@webpack', + 'solid-start@vite', + 'preact@vite', + 'preact@webpack', + 'qwik@vite', + 'lit@vite', + 'lit@webpack', + 'alpine@vite', + 'alpine@webpack', 'next@app/turbopack', 'next@app/webpack', 'next@pages/turbopack', diff --git a/src/core/mode-adapter.ts b/src/core/mode-adapter.ts index 2da8014..bad6539 100644 --- a/src/core/mode-adapter.ts +++ b/src/core/mode-adapter.ts @@ -5,6 +5,7 @@ import type { ReactAssetTarget, StandaloneAssetTarget, SpriteMode, + SpriteAssetTarget, } from '../targets/types.js' import type { ResolvedSpriteConfig, SpriteFolder } from '../types.js' @@ -38,7 +39,11 @@ export type StandaloneModeResultMetadata = { readonly target: StandaloneAssetTarget } -export type ModeResultMetadata = ReactModeResultMetadata | NextModeResultMetadata | StandaloneModeResultMetadata +export type FrameworkModeResultMetadata = { + readonly target: SpriteAssetTarget +} + +export type ModeResultMetadata = FrameworkModeResultMetadata | NextModeResultMetadata export type OutputPlan = { readonly files: readonly GeneratedFile[] diff --git a/src/index.ts b/src/index.ts index 1e2c93f..6a6d3b5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -21,14 +21,25 @@ export type { NextAssetTarget, NextBundler, NextRouter, + AlpineSpriteMode, + AngularAssetTarget, + AngularSpriteMode, + AstroSpriteMode, + LitSpriteMode, + NuxtSpriteMode, + PreactSpriteMode, + QwikSpriteMode, ReactAssetTarget, ReactSpriteMode, + SolidSpriteMode, StandaloneAssetTarget, StandaloneSpriteMode, StaticAssetTarget, + SvelteSpriteMode, SpriteAssetTarget, SpriteMode, ViteAssetTarget, + VueSpriteMode, WebpackAssetTarget, } from './targets/types.js' export type { diff --git a/src/mode-registry.ts b/src/mode-registry.ts index f463b79..03f5cae 100644 --- a/src/mode-registry.ts +++ b/src/mode-registry.ts @@ -1,13 +1,33 @@ import type { ModeAdapter } from './core/mode-adapter.js' +import { alpineViteAdapter } from './modes/alpine-vite/adapter.js' +import { alpineWebpackAdapter } from './modes/alpine-webpack/adapter.js' +import { angularApplicationAdapter } from './modes/angular-application/adapter.js' +import { angularWebpackAdapter } from './modes/angular-webpack/adapter.js' +import { astroViteAdapter } from './modes/astro-vite/adapter.js' +import { litViteAdapter } from './modes/lit-vite/adapter.js' +import { litWebpackAdapter } from './modes/lit-webpack/adapter.js' import { nextAppTurbopackAdapter } from './modes/next-app-turbopack/adapter.js' import { nextAppWebpackAdapter } from './modes/next-app-webpack/adapter.js' import { nextPagesTurbopackAdapter } from './modes/next-pages-turbopack/adapter.js' import { nextPagesWebpackAdapter } from './modes/next-pages-webpack/adapter.js' +import { nuxtViteAdapter } from './modes/nuxt-vite/adapter.js' +import { nuxtWebpackAdapter } from './modes/nuxt-webpack/adapter.js' +import { preactViteAdapter } from './modes/preact-vite/adapter.js' +import { preactWebpackAdapter } from './modes/preact-webpack/adapter.js' +import { qwikViteAdapter } from './modes/qwik-vite/adapter.js' import { reactViteAdapter } from './modes/react-vite/adapter.js' import { reactWebpackAdapter } from './modes/react-webpack/adapter.js' import { standaloneAdapter } from './modes/standalone/adapter.js' import { standaloneViteAdapter } from './modes/standalone-vite/adapter.js' import { standaloneWebpackAdapter } from './modes/standalone-webpack/adapter.js' +import { solidStartViteAdapter } from './modes/solid-start-vite/adapter.js' +import { solidViteAdapter } from './modes/solid-vite/adapter.js' +import { solidWebpackAdapter } from './modes/solid-webpack/adapter.js' +import { svelteViteAdapter } from './modes/svelte-vite/adapter.js' +import { svelteWebpackAdapter } from './modes/svelte-webpack/adapter.js' +import { sveltekitViteAdapter } from './modes/sveltekit-vite/adapter.js' +import { vueViteAdapter } from './modes/vue-vite/adapter.js' +import { vueWebpackAdapter } from './modes/vue-webpack/adapter.js' import type { SpriteMode } from './targets/types.js' const modeRegistry: Record = { @@ -16,6 +36,26 @@ const modeRegistry: Record = { 'standalone@webpack': standaloneWebpackAdapter, 'react@vite': reactViteAdapter, 'react@webpack': reactWebpackAdapter, + 'vue@vite': vueViteAdapter, + 'vue@webpack': vueWebpackAdapter, + 'nuxt@vite': nuxtViteAdapter, + 'nuxt@webpack': nuxtWebpackAdapter, + 'svelte@vite': svelteViteAdapter, + 'svelte@webpack': svelteWebpackAdapter, + 'sveltekit@vite': sveltekitViteAdapter, + 'angular@application': angularApplicationAdapter, + 'angular@webpack': angularWebpackAdapter, + 'astro@vite': astroViteAdapter, + 'solid@vite': solidViteAdapter, + 'solid@webpack': solidWebpackAdapter, + 'solid-start@vite': solidStartViteAdapter, + 'preact@vite': preactViteAdapter, + 'preact@webpack': preactWebpackAdapter, + 'qwik@vite': qwikViteAdapter, + 'lit@vite': litViteAdapter, + 'lit@webpack': litWebpackAdapter, + 'alpine@vite': alpineViteAdapter, + 'alpine@webpack': alpineWebpackAdapter, 'next@app/turbopack': nextAppTurbopackAdapter, 'next@app/webpack': nextAppWebpackAdapter, 'next@pages/turbopack': nextPagesTurbopackAdapter, diff --git a/src/modes/alpine-vite/adapter.ts b/src/modes/alpine-vite/adapter.ts new file mode 100644 index 0000000..f6e6575 --- /dev/null +++ b/src/modes/alpine-vite/adapter.ts @@ -0,0 +1,25 @@ +import { compileSpriteContent } from '../../compiler.js' +import { createCompiledSpriteArtifact } from '../../core/compiled-artifact.js' +import type { ModeAdapter } from '../../core/mode-adapter.js' +import { generateOutputFiles } from './output.js' + +export const alpineViteAdapter: ModeAdapter<'alpine@vite'> = { + mode: 'alpine@vite', + async generate(context) { + const bytes = await compileSpriteContent(context.prepared.folder, context.config.transform, { + rootViewBox: false, + }) + const artifact = createCompiledSpriteArtifact(bytes, context.prepared.iconNames, 'stack') + + return { + files: generateOutputFiles(context.config, artifact), + paths: { + generatedDir: '.svg-sprite', + sprite: '.svg-sprite/sprite.svg', + manifest: '.svg-sprite/svg-sprite.manifest.js', + entry: '.svg-sprite/index.js', + }, + result: { target: 'vite' }, + } + }, +} diff --git a/src/modes/alpine-vite/output.ts b/src/modes/alpine-vite/output.ts new file mode 100644 index 0000000..2292ec6 --- /dev/null +++ b/src/modes/alpine-vite/output.ts @@ -0,0 +1,272 @@ +import path from 'node:path' +import type { CompiledSpriteArtifact } from '../../core/compiled-artifact.js' +import { GENERATED_MARKER } from '../../core/generated-markers.js' +import type { GeneratedFile } from '../../core/mode-adapter.js' +import type { ResolvedSpriteConfig } from '../../types.js' + +const MODE = 'alpine@vite' +const TARGET = 'vite' +const OUTPUT_DIR = '.svg-sprite' +const NOTICE = 'AUTOMATICALLY GENERATED FILE. Do not edit manually.' + +function pascal(value: string): string { + return value.split('-').map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join('') +} + +function camel(value: string): string { + return value.replace(/-([a-z0-9])/g, (_, char: string) => char.toUpperCase()) +} + +function header(enabled: boolean): string { + return enabled + ? `/*\n * ${NOTICE}\n * ${GENERATED_MARKER}.\n */` + : `/* ${GENERATED_MARKER}. Do not edit. */` +} + +function markedSvg(bytes: Uint8Array, enabled: boolean): string { + const marker = enabled ? `${NOTICE} ${GENERATED_MARKER}.` : `${GENERATED_MARKER}. Do not edit.` + const content = new TextDecoder().decode(bytes) + return content.startsWith(')\s*/, `$1\n\n`) + : `\n${content}` +} + +function description(config: ResolvedSpriteConfig): string[] { + const text = config.description ?? `Icon names for the "${config.name}" SVG sprite.` + return ['/**', ...text.split(/\r?\n/).map((line) => ` * ${line.replace(/\*\//g, '* /')}`), ' */'] +} + +function iconData(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const prefix = camel(config.name) + const names = artifact.icons.map((icon) => icon.name) + const ids = Object.fromEntries(artifact.icons.map((icon) => [icon.name, icon.id])) + return [ + header(config.generatedNotice), + '', + ...description(config), + `export const ${prefix}IconNames = ${JSON.stringify(names, null, 2)}`, + '', + `export const ${prefix}IconIds = ${JSON.stringify(ids, null, 2)}`, + '', + ].join('\n') +} + +function iconTypes(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const typeName = `${pascal(config.name)}IconName` + const names = `${camel(config.name)}IconNames` + const ids = `${camel(config.name)}IconIds` + return [ + header(config.generatedNotice), + '', + ...description(config), + `export declare const ${names}: readonly [`, + ...artifact.icons.map((icon) => ` ${JSON.stringify(icon.name)},`), + ']', + `export type ${typeName} = typeof ${names}[number]`, + `export declare const ${ids}: {`, + ...artifact.icons.map((icon) => ` readonly ${JSON.stringify(icon.name)}: ${JSON.stringify(icon.id)}`), + '}', + '', + ].join('\n') +} + +function integration(config: ResolvedSpriteConfig): string { + const prefix = camel(config.name) + const directive = `${config.name}-icon` + const magic = `${prefix}IconHref` + const className = `${config.name}-icon` + const styleId = `svg-sprites-${config.name}-alpine` + return [ + header(config.generatedNotice), + "import integrationStyles from './alpine-integration.css?inline'", + `import { ${prefix}IconIds } from '../icon-data.js'`, + "import spriteUrl from '../sprite.svg?no-inline'", + '', + `export const ${prefix}IconDirective = ${JSON.stringify(directive)}`, + `export const ${prefix}IconMagic = ${JSON.stringify(magic)}`, + '', + 'function iconHref(icon) {', + ` const id = ${prefix}IconIds[icon]`, + " return id ? spriteUrl + '#' + id : ''", + '}', + '', + 'function installStyles() {', + ` if (document.getElementById(${JSON.stringify(styleId)})) return`, + " const style = document.createElement('style')", + ` style.id = ${JSON.stringify(styleId)}`, + ' style.textContent = integrationStyles', + ' document.head.append(style)', + '}', + '', + `export const ${prefix}AlpinePlugin = (Alpine) => {`, + ' installStyles()', + ` Alpine.magic(${prefix}IconMagic, () => iconHref)`, + '', + ` Alpine.directive(${prefix}IconDirective, (element, { expression }, { effect, evaluateLater }) => {`, + " if (element.namespaceURI !== 'http://www.w3.org/2000/svg' || element.localName !== 'svg') {", + ` throw new Error('x-${directive} must be used on an element.')`, + ' }', + '', + ` element.classList.add(${JSON.stringify(className)})`, + " let use = Array.from(element.children).find((child) => child.localName === 'use' && child.hasAttribute('data-svg-sprites-icon'))", + ' if (!use) {', + " use = document.createElementNS('http://www.w3.org/2000/svg', 'use')", + " use.setAttribute('data-svg-sprites-icon', '')", + ' element.append(use)', + ' }', + '', + ' const evaluateIcon = evaluateLater(expression)', + ' effect(() => {', + ' evaluateIcon((icon) => {', + ' const href = iconHref(icon)', + " if (href) use.setAttribute('href', href)", + " else use.removeAttribute('href')", + ' })', + ' })', + ' })', + '}', + '', + ].join('\n') +} + +function integrationTypes(config: ResolvedSpriteConfig): string { + const pascalName = pascal(config.name) + const prefix = camel(config.name) + const typeName = `${pascalName}IconName` + return [ + header(config.generatedNotice), + '', + `export type ${pascalName}AlpineDirectiveUtilities = {`, + ' effect(callback: () => void): void', + ' evaluateLater(expression: string): (receiver: (value: unknown) => void) => void', + '}', + `export type ${pascalName}AlpineApi = {`, + ' magic(name: string, callback: () => unknown): unknown', + ` directive(name: string, callback: (element: Element, directive: { expression: string }, utilities: ${pascalName}AlpineDirectiveUtilities) => void): unknown`, + '}', + `export type ${pascalName}AlpinePluginType = (Alpine: ${pascalName}AlpineApi) => void`, + `export declare const ${prefix}IconDirective: ${JSON.stringify(`${config.name}-icon`)}`, + `export declare const ${prefix}IconMagic: ${JSON.stringify(`${prefix}IconHref`)}`, + `export declare const ${prefix}AlpinePlugin: ${pascalName}AlpinePluginType`, + `export type { ${typeName} } from '../icon-data.js'`, + '', + ].join('\n') +} + +function styles(config: ResolvedSpriteConfig): string { + const transition = config.transform.addTransition + ? [' transition-property: fill, stroke, color;', ' transition-duration: 0.3s;', ' transition-timing-function: ease;'] + : [] + return [ + header(config.generatedNotice), + '', + `.${config.name}-icon {`, + ' display: inline-block;', + ' width: 1em;', + ' height: 1em;', + ' vertical-align: middle;', + ...transition, + '}', + '', + ].join('\n') +} + +function index(config: ResolvedSpriteConfig): string { + const prefix = camel(config.name) + return [ + header(config.generatedNotice), + `export { ${prefix}AlpinePlugin, ${prefix}IconDirective, ${prefix}IconMagic } from './alpine/alpine-integration.js'`, + `export { ${prefix}IconNames } from './icon-data.js'`, + '', + ].join('\n') +} + +function indexTypes(config: ResolvedSpriteConfig): string { + const pascalName = pascal(config.name) + const prefix = camel(config.name) + return [ + header(config.generatedNotice), + `export { ${prefix}AlpinePlugin, ${prefix}IconDirective, ${prefix}IconMagic } from './alpine/alpine-integration.js'`, + `export type { ${pascalName}AlpineApi, ${pascalName}AlpineDirectiveUtilities, ${pascalName}AlpinePluginType } from './alpine/alpine-integration.js'`, + `export { ${prefix}IconNames } from './icon-data.js'`, + `export type { ${pascalName}IconName } from './icon-data.js'`, + '', + ].join('\n') +} + +function manifest(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const prefix = camel(config.name) + const data = { + schemaVersion: 1, + generator: '@gromlab/svg-sprites', + name: config.name, + ...(config.description === undefined ? {} : { description: config.description }), + usage: { + framework: 'alpine', + pluginName: `${prefix}AlpinePlugin`, + directive: `x-${config.name}-icon`, + magic: `$${prefix}IconHref`, + }, + mode: MODE, + target: TARGET, + format: artifact.format, + iconCount: artifact.icons.length, + spriteUrl: '__SPRITE_URL__', + icons: artifact.icons, + } + const source = JSON.stringify(data, null, 2).replace('"__SPRITE_URL__"', 'spriteUrl') + return [ + header(config.generatedNotice), + "import spriteUrl from './sprite.svg?no-inline'", + '', + `export const spriteManifest = ${source}`, + '', + 'export default spriteManifest', + '', + ].join('\n') +} + +function manifestTypes(config: ResolvedSpriteConfig): string { + return [ + header(config.generatedNotice), + '', + 'export type SpriteManifestColor = { variable: `--icon-color-${number}`; fallback: string }', + 'export type SpriteManifestIcon = {', + ' name: string', + ' id: string', + ' viewBox: string | null', + ' colors: readonly SpriteManifestColor[]', + '}', + 'export type SpriteManifest = {', + ' schemaVersion: 1', + " generator: '@gromlab/svg-sprites'", + ' name: string', + ' description?: string', + " usage: { framework: 'alpine'; pluginName: string; directive: string; magic: string }", + " mode: 'alpine@vite'", + " target: 'vite'", + " format: 'stack' | 'symbol'", + ' iconCount: number', + ' spriteUrl: string', + ' icons: readonly SpriteManifestIcon[]', + '}', + 'export declare const spriteManifest: SpriteManifest', + 'export default spriteManifest', + '', + ].join('\n') +} + +export function generateOutputFiles(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): GeneratedFile[] { + return [ + { path: path.posix.join(OUTPUT_DIR, 'index.js'), content: index(config) }, + { path: path.posix.join(OUTPUT_DIR, 'index.d.ts'), content: indexTypes(config) }, + { path: path.posix.join(OUTPUT_DIR, 'sprite.svg'), content: markedSvg(artifact.bytes, config.generatedNotice) }, + { path: path.posix.join(OUTPUT_DIR, 'icon-data.js'), content: iconData(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'icon-data.d.ts'), content: iconTypes(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'alpine', 'alpine-integration.js'), content: integration(config) }, + { path: path.posix.join(OUTPUT_DIR, 'alpine', 'alpine-integration.d.ts'), content: integrationTypes(config) }, + { path: path.posix.join(OUTPUT_DIR, 'alpine', 'alpine-integration.css'), content: styles(config) }, + { path: path.posix.join(OUTPUT_DIR, 'svg-sprite.manifest.js'), content: manifest(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'svg-sprite.manifest.d.ts'), content: manifestTypes(config) }, + ] +} diff --git a/src/modes/alpine-webpack/adapter.ts b/src/modes/alpine-webpack/adapter.ts new file mode 100644 index 0000000..b173fc3 --- /dev/null +++ b/src/modes/alpine-webpack/adapter.ts @@ -0,0 +1,25 @@ +import { compileSpriteContent } from '../../compiler.js' +import { createCompiledSpriteArtifact } from '../../core/compiled-artifact.js' +import type { ModeAdapter } from '../../core/mode-adapter.js' +import { generateOutputFiles } from './output.js' + +export const alpineWebpackAdapter: ModeAdapter<'alpine@webpack'> = { + mode: 'alpine@webpack', + async generate(context) { + const bytes = await compileSpriteContent(context.prepared.folder, context.config.transform, { + rootViewBox: false, + }) + const artifact = createCompiledSpriteArtifact(bytes, context.prepared.iconNames, 'stack') + + return { + files: generateOutputFiles(context.config, artifact), + paths: { + generatedDir: '.svg-sprite', + sprite: '.svg-sprite/sprite.svg', + manifest: '.svg-sprite/svg-sprite.manifest.js', + entry: '.svg-sprite/index.js', + }, + result: { target: 'webpack' }, + } + }, +} diff --git a/src/modes/alpine-webpack/output.ts b/src/modes/alpine-webpack/output.ts new file mode 100644 index 0000000..5604e24 --- /dev/null +++ b/src/modes/alpine-webpack/output.ts @@ -0,0 +1,273 @@ +import path from 'node:path' +import type { CompiledSpriteArtifact } from '../../core/compiled-artifact.js' +import { GENERATED_MARKER } from '../../core/generated-markers.js' +import type { GeneratedFile } from '../../core/mode-adapter.js' +import type { ResolvedSpriteConfig } from '../../types.js' + +const MODE = 'alpine@webpack' +const TARGET = 'webpack' +const OUTPUT_DIR = '.svg-sprite' +const NOTICE = 'AUTOMATICALLY GENERATED FILE. Do not edit manually.' + +function pascal(value: string): string { + return value.split('-').map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join('') +} + +function camel(value: string): string { + return value.replace(/-([a-z0-9])/g, (_, char: string) => char.toUpperCase()) +} + +function header(enabled: boolean): string { + return enabled + ? `/*\n * ${NOTICE}\n * ${GENERATED_MARKER}.\n */` + : `/* ${GENERATED_MARKER}. Do not edit. */` +} + +function markedSvg(bytes: Uint8Array, enabled: boolean): string { + const marker = enabled ? `${NOTICE} ${GENERATED_MARKER}.` : `${GENERATED_MARKER}. Do not edit.` + const content = new TextDecoder().decode(bytes) + return content.startsWith(')\s*/, `$1\n\n`) + : `\n${content}` +} + +function description(config: ResolvedSpriteConfig): string[] { + const text = config.description ?? `Icon names for the "${config.name}" SVG sprite.` + return ['/**', ...text.split(/\r?\n/).map((line) => ` * ${line.replace(/\*\//g, '* /')}`), ' */'] +} + +function iconData(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const prefix = camel(config.name) + const names = artifact.icons.map((icon) => icon.name) + const ids = Object.fromEntries(artifact.icons.map((icon) => [icon.name, icon.id])) + return [ + header(config.generatedNotice), + '', + ...description(config), + `export const ${prefix}IconNames = ${JSON.stringify(names, null, 2)}`, + '', + `export const ${prefix}IconIds = ${JSON.stringify(ids, null, 2)}`, + '', + ].join('\n') +} + +function iconTypes(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const typeName = `${pascal(config.name)}IconName` + const names = `${camel(config.name)}IconNames` + const ids = `${camel(config.name)}IconIds` + return [ + header(config.generatedNotice), + '', + ...description(config), + `export declare const ${names}: readonly [`, + ...artifact.icons.map((icon) => ` ${JSON.stringify(icon.name)},`), + ']', + `export type ${typeName} = typeof ${names}[number]`, + `export declare const ${ids}: {`, + ...artifact.icons.map((icon) => ` readonly ${JSON.stringify(icon.name)}: ${JSON.stringify(icon.id)}`), + '}', + '', + ].join('\n') +} + +function integration(config: ResolvedSpriteConfig): string { + const prefix = camel(config.name) + const directive = `${config.name}-icon` + const magic = `${prefix}IconHref` + const className = `${config.name}-icon` + const styleId = `svg-sprites-${config.name}-alpine` + return [ + header(config.generatedNotice), + "import integrationStyles from './alpine-integration.css?inline'", + `import { ${prefix}IconIds } from '../icon-data.js'`, + '', + "const spriteUrl = new URL('../sprite.svg', import.meta.url).href", + '', + `export const ${prefix}IconDirective = ${JSON.stringify(directive)}`, + `export const ${prefix}IconMagic = ${JSON.stringify(magic)}`, + '', + 'function iconHref(icon) {', + ` const id = ${prefix}IconIds[icon]`, + " return id ? spriteUrl + '#' + id : ''", + '}', + '', + 'function installStyles() {', + ` if (document.getElementById(${JSON.stringify(styleId)})) return`, + " const style = document.createElement('style')", + ` style.id = ${JSON.stringify(styleId)}`, + ' style.textContent = integrationStyles', + ' document.head.append(style)', + '}', + '', + `export const ${prefix}AlpinePlugin = (Alpine) => {`, + ' installStyles()', + ` Alpine.magic(${prefix}IconMagic, () => iconHref)`, + '', + ` Alpine.directive(${prefix}IconDirective, (element, { expression }, { effect, evaluateLater }) => {`, + " if (element.namespaceURI !== 'http://www.w3.org/2000/svg' || element.localName !== 'svg') {", + ` throw new Error('x-${directive} must be used on an element.')`, + ' }', + '', + ` element.classList.add(${JSON.stringify(className)})`, + " let use = Array.from(element.children).find((child) => child.localName === 'use' && child.hasAttribute('data-svg-sprites-icon'))", + ' if (!use) {', + " use = document.createElementNS('http://www.w3.org/2000/svg', 'use')", + " use.setAttribute('data-svg-sprites-icon', '')", + ' element.append(use)', + ' }', + '', + ' const evaluateIcon = evaluateLater(expression)', + ' effect(() => {', + ' evaluateIcon((icon) => {', + ' const href = iconHref(icon)', + " if (href) use.setAttribute('href', href)", + " else use.removeAttribute('href')", + ' })', + ' })', + ' })', + '}', + '', + ].join('\n') +} + +function integrationTypes(config: ResolvedSpriteConfig): string { + const pascalName = pascal(config.name) + const prefix = camel(config.name) + const typeName = `${pascalName}IconName` + return [ + header(config.generatedNotice), + '', + `export type ${pascalName}AlpineDirectiveUtilities = {`, + ' effect(callback: () => void): void', + ' evaluateLater(expression: string): (receiver: (value: unknown) => void) => void', + '}', + `export type ${pascalName}AlpineApi = {`, + ' magic(name: string, callback: () => unknown): unknown', + ` directive(name: string, callback: (element: Element, directive: { expression: string }, utilities: ${pascalName}AlpineDirectiveUtilities) => void): unknown`, + '}', + `export type ${pascalName}AlpinePluginType = (Alpine: ${pascalName}AlpineApi) => void`, + `export declare const ${prefix}IconDirective: ${JSON.stringify(`${config.name}-icon`)}`, + `export declare const ${prefix}IconMagic: ${JSON.stringify(`${prefix}IconHref`)}`, + `export declare const ${prefix}AlpinePlugin: ${pascalName}AlpinePluginType`, + `export type { ${typeName} } from '../icon-data.js'`, + '', + ].join('\n') +} + +function styles(config: ResolvedSpriteConfig): string { + const transition = config.transform.addTransition + ? [' transition-property: fill, stroke, color;', ' transition-duration: 0.3s;', ' transition-timing-function: ease;'] + : [] + return [ + header(config.generatedNotice), + '', + `.${config.name}-icon {`, + ' display: inline-block;', + ' width: 1em;', + ' height: 1em;', + ' vertical-align: middle;', + ...transition, + '}', + '', + ].join('\n') +} + +function index(config: ResolvedSpriteConfig): string { + const prefix = camel(config.name) + return [ + header(config.generatedNotice), + `export { ${prefix}AlpinePlugin, ${prefix}IconDirective, ${prefix}IconMagic } from './alpine/alpine-integration.js'`, + `export { ${prefix}IconNames } from './icon-data.js'`, + '', + ].join('\n') +} + +function indexTypes(config: ResolvedSpriteConfig): string { + const pascalName = pascal(config.name) + const prefix = camel(config.name) + return [ + header(config.generatedNotice), + `export { ${prefix}AlpinePlugin, ${prefix}IconDirective, ${prefix}IconMagic } from './alpine/alpine-integration.js'`, + `export type { ${pascalName}AlpineApi, ${pascalName}AlpineDirectiveUtilities, ${pascalName}AlpinePluginType } from './alpine/alpine-integration.js'`, + `export { ${prefix}IconNames } from './icon-data.js'`, + `export type { ${pascalName}IconName } from './icon-data.js'`, + '', + ].join('\n') +} + +function manifest(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const prefix = camel(config.name) + const data = { + schemaVersion: 1, + generator: '@gromlab/svg-sprites', + name: config.name, + ...(config.description === undefined ? {} : { description: config.description }), + usage: { + framework: 'alpine', + pluginName: `${prefix}AlpinePlugin`, + directive: `x-${config.name}-icon`, + magic: `$${prefix}IconHref`, + }, + mode: MODE, + target: TARGET, + format: artifact.format, + iconCount: artifact.icons.length, + spriteUrl: '__SPRITE_URL__', + icons: artifact.icons, + } + const source = JSON.stringify(data, null, 2).replace('"__SPRITE_URL__"', 'spriteUrl') + return [ + header(config.generatedNotice), + "const spriteUrl = new URL('./sprite.svg', import.meta.url).href", + '', + `export const spriteManifest = ${source}`, + '', + 'export default spriteManifest', + '', + ].join('\n') +} + +function manifestTypes(config: ResolvedSpriteConfig): string { + return [ + header(config.generatedNotice), + '', + 'export type SpriteManifestColor = { variable: `--icon-color-${number}`; fallback: string }', + 'export type SpriteManifestIcon = {', + ' name: string', + ' id: string', + ' viewBox: string | null', + ' colors: readonly SpriteManifestColor[]', + '}', + 'export type SpriteManifest = {', + ' schemaVersion: 1', + " generator: '@gromlab/svg-sprites'", + ' name: string', + ' description?: string', + " usage: { framework: 'alpine'; pluginName: string; directive: string; magic: string }", + " mode: 'alpine@webpack'", + " target: 'webpack'", + " format: 'stack' | 'symbol'", + ' iconCount: number', + ' spriteUrl: string', + ' icons: readonly SpriteManifestIcon[]', + '}', + 'export declare const spriteManifest: SpriteManifest', + 'export default spriteManifest', + '', + ].join('\n') +} + +export function generateOutputFiles(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): GeneratedFile[] { + return [ + { path: path.posix.join(OUTPUT_DIR, 'index.js'), content: index(config) }, + { path: path.posix.join(OUTPUT_DIR, 'index.d.ts'), content: indexTypes(config) }, + { path: path.posix.join(OUTPUT_DIR, 'sprite.svg'), content: markedSvg(artifact.bytes, config.generatedNotice) }, + { path: path.posix.join(OUTPUT_DIR, 'icon-data.js'), content: iconData(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'icon-data.d.ts'), content: iconTypes(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'alpine', 'alpine-integration.js'), content: integration(config) }, + { path: path.posix.join(OUTPUT_DIR, 'alpine', 'alpine-integration.d.ts'), content: integrationTypes(config) }, + { path: path.posix.join(OUTPUT_DIR, 'alpine', 'alpine-integration.css'), content: styles(config) }, + { path: path.posix.join(OUTPUT_DIR, 'svg-sprite.manifest.js'), content: manifest(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'svg-sprite.manifest.d.ts'), content: manifestTypes(config) }, + ] +} diff --git a/src/modes/angular-application/adapter.ts b/src/modes/angular-application/adapter.ts new file mode 100644 index 0000000..9cf4438 --- /dev/null +++ b/src/modes/angular-application/adapter.ts @@ -0,0 +1,36 @@ +import { compileSpriteContent } from '../../compiler.js' +import { createCompiledSpriteArtifact } from '../../core/compiled-artifact.js' +import type { ModeAdapterContext, OutputPlan } from '../../core/mode-adapter.js' +import { generateOutputFiles } from './output.js' + +type AngularApplicationOutputPlan = Omit & { + readonly result: { readonly target: 'application' } +} + +type AngularApplicationAdapter = { + readonly mode: 'angular@application' + generate(context: ModeAdapterContext): Promise +} + +export const angularApplicationAdapter: AngularApplicationAdapter = { + mode: 'angular@application', + async generate(context) { + const bytes = await compileSpriteContent( + context.prepared.folder, + context.config.transform, + { rootViewBox: false }, + ) + const artifact = createCompiledSpriteArtifact(bytes, context.prepared.iconNames, 'stack') + + return { + files: generateOutputFiles(context.config, artifact), + paths: { + generatedDir: '.svg-sprite', + sprite: '.svg-sprite/sprite.svg', + manifest: '.svg-sprite/svg-sprite.manifest.js', + entry: '.svg-sprite/index.ts', + }, + result: { target: 'application' }, + } + }, +} diff --git a/src/modes/angular-application/output.ts b/src/modes/angular-application/output.ts new file mode 100644 index 0000000..adfe284 --- /dev/null +++ b/src/modes/angular-application/output.ts @@ -0,0 +1,242 @@ +import path from 'node:path' +import type { CompiledSpriteArtifact } from '../../core/compiled-artifact.js' +import { GENERATED_MARKER } from '../../core/generated-markers.js' +import type { GeneratedFile } from '../../core/mode-adapter.js' +import type { ResolvedSpriteConfig } from '../../types.js' + +const MODE = 'angular@application' +const TARGET = 'application' +const OUTPUT_DIR = '.svg-sprite' +const NOTICE = 'АВТОМАТИЧЕСКИ СГЕНЕРИРОВАННЫЙ ФАЙЛ. Не редактируйте вручную.' + +function pascal(value: string): string { + return value.split('-').map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join('') +} + +function camel(value: string): string { + return value.replace(/-([a-z0-9])/g, (_, char: string) => char.toUpperCase()) +} + +function header(enabled: boolean): string { + return enabled + ? `/*\n * ${NOTICE}\n * ${GENERATED_MARKER}.\n */` + : `/* ${GENERATED_MARKER}. Do not edit. */` +} + +function svg(bytes: Uint8Array, enabled: boolean): string { + const marker = enabled ? `${NOTICE}\n ${GENERATED_MARKER}.` : `${GENERATED_MARKER}. Do not edit.` + const content = new TextDecoder().decode(bytes) + return content.startsWith(')\s*/, `$1\n\n`) + : `\n${content}` +} + +function description(config: ResolvedSpriteConfig): string[] { + const text = config.description ?? `Имена иконок SVG-спрайта «${config.name}».` + return ['/**', ...text.split(/\r?\n/).map((line) => ` * ${line.replace(/\*\//g, '* /')}`), ' */'] +} + +function iconData(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const prefix = camel(config.name) + return [ + header(config.generatedNotice), + '', + ...description(config), + `export const ${prefix}IconNames = ${JSON.stringify(artifact.icons.map((icon) => icon.name), null, 2)}`, + '', + `export const ${prefix}IconIds = ${JSON.stringify(Object.fromEntries(artifact.icons.map((icon) => [icon.name, icon.id])), null, 2)}`, + '', + ].join('\n') +} + +function iconDeclarations(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const componentName = `${pascal(config.name)}Icon` + const names = `${camel(config.name)}IconNames` + const ids = `${camel(config.name)}IconIds` + return [ + header(config.generatedNotice), + '', + ...description(config), + `export declare const ${names}: readonly [`, + ...artifact.icons.map((icon) => ` ${JSON.stringify(icon.name)},`), + ']', + `export type ${componentName}Name = typeof ${names}[number]`, + `export declare const ${ids}: {`, + ...artifact.icons.map((icon) => ` readonly ${JSON.stringify(icon.name)}: ${JSON.stringify(icon.id)}`), + '}', + '', + ].join('\n') +} + +function component(config: ResolvedSpriteConfig): string { + const componentName = `${pascal(config.name)}Icon` + const typeName = `${componentName}Name` + const ids = `${camel(config.name)}IconIds` + return [ + header(config.generatedNotice), + '/// ', + "import { ChangeDetectionStrategy, Component, input } from '@angular/core'", + `import type { ${typeName} } from '../icon-data.js'`, + `import { ${ids} } from '../icon-data.js'`, + "import spriteUrl from '../sprite.svg'", + '', + '@Component({', + ` selector: '${config.name}-icon',`, + ' standalone: true,', + " template: '',", + " styleUrl: './angular-component.css',", + ' changeDetection: ChangeDetectionStrategy.OnPush,', + '})', + `export class ${componentName} {`, + ` readonly icon = input.required<${typeName}>()`, + '', + ' get href(): string {', + ` return spriteUrl + '#' + ${ids}[this.icon()]`, + ' }', + '}', + '', + ].join('\n') +} + +function componentDeclarations(config: ResolvedSpriteConfig): string { + const componentName = `${pascal(config.name)}Icon` + const typeName = `${componentName}Name` + return [ + header(config.generatedNotice), + "import type * as i0 from '@angular/core'", + "import type { InputSignal } from '@angular/core'", + `import type { ${typeName} } from '../icon-data.js'`, + '', + `export declare class ${componentName} {`, + ` readonly icon: InputSignal<${typeName}>`, + ' get href(): string', + ` static ɵfac: i0.ɵɵFactoryDeclaration<${componentName}, never>`, + ` static ɵcmp: i0.ɵɵComponentDeclaration<${componentName}, '${config.name}-icon', never, { icon: { alias: 'icon'; required: true; isSignal: true } }, {}, never, never, true, never>`, + '}', + '', + ].join('\n') +} + +function index(config: ResolvedSpriteConfig): string { + return [ + header(config.generatedNotice), + `export { ${pascal(config.name)}Icon } from './angular/angular-component'`, + `export { ${camel(config.name)}IconNames } from './icon-data.js'`, + `export type { ${pascal(config.name)}IconName } from './icon-data.js'`, + '', + ].join('\n') +} + +function indexDeclarations(config: ResolvedSpriteConfig): string { + const componentName = `${pascal(config.name)}Icon` + return [ + header(config.generatedNotice), + `export { ${componentName} } from './angular/angular-component'`, + `export { ${camel(config.name)}IconNames } from './icon-data.js'`, + `export type { ${componentName}Name } from './icon-data.js'`, + '', + ].join('\n') +} + +function assetDeclarations(config: ResolvedSpriteConfig): string { + return [ + header(config.generatedNotice), + "declare module '*.svg' {", + ' const url: string', + ' export default url', + '}', + '', + ].join('\n') +} + +function styles(config: ResolvedSpriteConfig): string { + const transition = config.transform.addTransition + ? [' transition-property: fill, stroke, color;', ' transition-duration: 0.3s;', ' transition-timing-function: ease;'] + : [] + return [ + header(config.generatedNotice), + '', + ':host {', + ' display: inline-flex;', + '}', + '', + '.root {', + ' width: 100%;', + ' height: 100%;', + ...transition, + '}', + '', + ].join('\n') +} + +function manifest(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const data = { + schemaVersion: 1, + generator: '@gromlab/svg-sprites', + name: config.name, + ...(config.description === undefined ? {} : { description: config.description }), + usage: { + framework: 'angular', + componentName: `${pascal(config.name)}Icon`, + selector: `${config.name}-icon`, + }, + mode: MODE, + target: TARGET, + format: artifact.format, + iconCount: artifact.icons.length, + spriteUrl: '__SPRITE_URL__', + icons: artifact.icons, + } + const source = JSON.stringify(data, null, 2).replace('"__SPRITE_URL__"', 'spriteUrl') + return [ + header(config.generatedNotice), + "import spriteUrl from './sprite.svg'", + '', + `export const spriteManifest = ${source}`, + '', + 'export default spriteManifest', + '', + ].join('\n') +} + +function manifestDeclarations(config: ResolvedSpriteConfig): string { + return [ + header(config.generatedNotice), + '', + 'export type SpriteManifestColor = { variable: `--icon-color-${number}`; fallback: string }', + 'export type SpriteManifestIcon = { name: string; id: string; viewBox: string | null; colors: readonly SpriteManifestColor[] }', + 'export type SpriteManifest = {', + ' schemaVersion: 1', + " generator: '@gromlab/svg-sprites'", + ' name: string', + ' description?: string', + " usage: { framework: 'angular'; componentName: string; selector: string }", + " mode: 'angular@application'", + " target: 'application'", + " format: 'stack' | 'symbol'", + ' iconCount: number', + ' spriteUrl: string', + ' icons: readonly SpriteManifestIcon[]', + '}', + '', + 'export declare const spriteManifest: SpriteManifest', + 'export default spriteManifest', + '', + ].join('\n') +} + +export function generateOutputFiles(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): GeneratedFile[] { + return [ + { path: path.posix.join(OUTPUT_DIR, 'index.ts'), content: index(config) }, + { path: path.posix.join(OUTPUT_DIR, 'index.d.ts'), content: indexDeclarations(config) }, + { path: path.posix.join(OUTPUT_DIR, 'assets.d.ts'), content: assetDeclarations(config) }, + { path: path.posix.join(OUTPUT_DIR, 'sprite.svg'), content: svg(artifact.bytes, config.generatedNotice) }, + { path: path.posix.join(OUTPUT_DIR, 'icon-data.js'), content: iconData(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'icon-data.d.ts'), content: iconDeclarations(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'angular', 'angular-component.ts'), content: component(config) }, + { path: path.posix.join(OUTPUT_DIR, 'angular', 'angular-component.d.ts'), content: componentDeclarations(config) }, + { path: path.posix.join(OUTPUT_DIR, 'angular', 'angular-component.css'), content: styles(config) }, + { path: path.posix.join(OUTPUT_DIR, 'svg-sprite.manifest.js'), content: manifest(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'svg-sprite.manifest.d.ts'), content: manifestDeclarations(config) }, + ] +} diff --git a/src/modes/angular-webpack/adapter.ts b/src/modes/angular-webpack/adapter.ts new file mode 100644 index 0000000..216abca --- /dev/null +++ b/src/modes/angular-webpack/adapter.ts @@ -0,0 +1,36 @@ +import { compileSpriteContent } from '../../compiler.js' +import { createCompiledSpriteArtifact } from '../../core/compiled-artifact.js' +import type { ModeAdapterContext, OutputPlan } from '../../core/mode-adapter.js' +import { generateOutputFiles } from './output.js' + +type AngularWebpackOutputPlan = Omit & { + readonly result: { readonly target: 'webpack' } +} + +type AngularWebpackAdapter = { + readonly mode: 'angular@webpack' + generate(context: ModeAdapterContext): Promise +} + +export const angularWebpackAdapter: AngularWebpackAdapter = { + mode: 'angular@webpack', + async generate(context) { + const bytes = await compileSpriteContent( + context.prepared.folder, + context.config.transform, + { rootViewBox: false }, + ) + const artifact = createCompiledSpriteArtifact(bytes, context.prepared.iconNames, 'stack') + + return { + files: generateOutputFiles(context.config, artifact), + paths: { + generatedDir: '.svg-sprite', + sprite: '.svg-sprite/sprite.svg', + manifest: '.svg-sprite/svg-sprite.manifest.js', + entry: '.svg-sprite/index.ts', + }, + result: { target: 'webpack' }, + } + }, +} diff --git a/src/modes/angular-webpack/output.ts b/src/modes/angular-webpack/output.ts new file mode 100644 index 0000000..6423201 --- /dev/null +++ b/src/modes/angular-webpack/output.ts @@ -0,0 +1,230 @@ +import path from 'node:path' +import type { CompiledSpriteArtifact } from '../../core/compiled-artifact.js' +import { GENERATED_MARKER } from '../../core/generated-markers.js' +import type { GeneratedFile } from '../../core/mode-adapter.js' +import type { ResolvedSpriteConfig } from '../../types.js' + +const MODE = 'angular@webpack' +const TARGET = 'webpack' +const OUTPUT_DIR = '.svg-sprite' +const NOTICE = 'АВТОМАТИЧЕСКИ СГЕНЕРИРОВАННЫЙ ФАЙЛ. Не редактируйте вручную.' + +function pascal(value: string): string { + return value.split('-').map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join('') +} + +function camel(value: string): string { + return value.replace(/-([a-z0-9])/g, (_, char: string) => char.toUpperCase()) +} + +function header(enabled: boolean): string { + return enabled + ? `/*\n * ${NOTICE}\n * ${GENERATED_MARKER}.\n */` + : `/* ${GENERATED_MARKER}. Do not edit. */` +} + +function svg(bytes: Uint8Array, enabled: boolean): string { + const marker = enabled ? `${NOTICE}\n ${GENERATED_MARKER}.` : `${GENERATED_MARKER}. Do not edit.` + const content = new TextDecoder().decode(bytes) + return content.startsWith(')\s*/, `$1\n\n`) + : `\n${content}` +} + +function description(config: ResolvedSpriteConfig): string[] { + const text = config.description ?? `Имена иконок SVG-спрайта «${config.name}».` + return ['/**', ...text.split(/\r?\n/).map((line) => ` * ${line.replace(/\*\//g, '* /')}`), ' */'] +} + +function iconData(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const prefix = camel(config.name) + return [ + header(config.generatedNotice), + '', + ...description(config), + `export const ${prefix}IconNames = ${JSON.stringify(artifact.icons.map((icon) => icon.name), null, 2)}`, + '', + `export const ${prefix}IconIds = ${JSON.stringify(Object.fromEntries(artifact.icons.map((icon) => [icon.name, icon.id])), null, 2)}`, + '', + ].join('\n') +} + +function iconDeclarations(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const componentName = `${pascal(config.name)}Icon` + const names = `${camel(config.name)}IconNames` + const ids = `${camel(config.name)}IconIds` + return [ + header(config.generatedNotice), + '', + ...description(config), + `export declare const ${names}: readonly [`, + ...artifact.icons.map((icon) => ` ${JSON.stringify(icon.name)},`), + ']', + `export type ${componentName}Name = typeof ${names}[number]`, + `export declare const ${ids}: {`, + ...artifact.icons.map((icon) => ` readonly ${JSON.stringify(icon.name)}: ${JSON.stringify(icon.id)}`), + '}', + '', + ].join('\n') +} + +function component(config: ResolvedSpriteConfig): string { + const componentName = `${pascal(config.name)}Icon` + const typeName = `${componentName}Name` + const ids = `${camel(config.name)}IconIds` + return [ + header(config.generatedNotice), + "import { ChangeDetectionStrategy, Component, input } from '@angular/core'", + `import type { ${typeName} } from '../icon-data.js'`, + `import { ${ids} } from '../icon-data.js'`, + '', + "const spriteUrl = new URL('../sprite.svg', import.meta.url).href", + '', + '@Component({', + ` selector: '${config.name}-icon',`, + ' standalone: true,', + " template: '',", + " styleUrl: './angular-component.css',", + ' changeDetection: ChangeDetectionStrategy.OnPush,', + '})', + `export class ${componentName} {`, + ` readonly icon = input.required<${typeName}>()`, + '', + ' get href(): string {', + ` return spriteUrl + '#' + ${ids}[this.icon()]`, + ' }', + '}', + '', + ].join('\n') +} + +function componentDeclarations(config: ResolvedSpriteConfig): string { + const componentName = `${pascal(config.name)}Icon` + const typeName = `${componentName}Name` + return [ + header(config.generatedNotice), + "import type * as i0 from '@angular/core'", + "import type { InputSignal } from '@angular/core'", + `import type { ${typeName} } from '../icon-data.js'`, + '', + `export declare class ${componentName} {`, + ` readonly icon: InputSignal<${typeName}>`, + ' get href(): string', + ` static ɵfac: i0.ɵɵFactoryDeclaration<${componentName}, never>`, + ` static ɵcmp: i0.ɵɵComponentDeclaration<${componentName}, '${config.name}-icon', never, { icon: { alias: 'icon'; required: true; isSignal: true } }, {}, never, never, true, never>`, + '}', + '', + ].join('\n') +} + +function index(config: ResolvedSpriteConfig): string { + return [ + header(config.generatedNotice), + `export { ${pascal(config.name)}Icon } from './angular/angular-component'`, + `export { ${camel(config.name)}IconNames } from './icon-data.js'`, + `export type { ${pascal(config.name)}IconName } from './icon-data.js'`, + '', + ].join('\n') +} + +function indexDeclarations(config: ResolvedSpriteConfig): string { + const componentName = `${pascal(config.name)}Icon` + return [ + header(config.generatedNotice), + `export { ${componentName} } from './angular/angular-component'`, + `export { ${camel(config.name)}IconNames } from './icon-data.js'`, + `export type { ${componentName}Name } from './icon-data.js'`, + '', + ].join('\n') +} + +function styles(config: ResolvedSpriteConfig): string { + const transition = config.transform.addTransition + ? [' transition-property: fill, stroke, color;', ' transition-duration: 0.3s;', ' transition-timing-function: ease;'] + : [] + return [ + header(config.generatedNotice), + '', + ':host {', + ' display: inline-flex;', + '}', + '', + '.root {', + ' width: 100%;', + ' height: 100%;', + ...transition, + '}', + '', + ].join('\n') +} + +function manifest(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const data = { + schemaVersion: 1, + generator: '@gromlab/svg-sprites', + name: config.name, + ...(config.description === undefined ? {} : { description: config.description }), + usage: { + framework: 'angular', + componentName: `${pascal(config.name)}Icon`, + selector: `${config.name}-icon`, + }, + mode: MODE, + target: TARGET, + format: artifact.format, + iconCount: artifact.icons.length, + spriteUrl: '__SPRITE_URL__', + icons: artifact.icons, + } + const source = JSON.stringify(data, null, 2).replace('"__SPRITE_URL__"', 'spriteUrl') + return [ + header(config.generatedNotice), + "const spriteUrl = new URL('./sprite.svg', import.meta.url).href", + '', + `export const spriteManifest = ${source}`, + '', + 'export default spriteManifest', + '', + ].join('\n') +} + +function manifestDeclarations(config: ResolvedSpriteConfig): string { + return [ + header(config.generatedNotice), + '', + 'export type SpriteManifestColor = { variable: `--icon-color-${number}`; fallback: string }', + 'export type SpriteManifestIcon = { name: string; id: string; viewBox: string | null; colors: readonly SpriteManifestColor[] }', + 'export type SpriteManifest = {', + ' schemaVersion: 1', + " generator: '@gromlab/svg-sprites'", + ' name: string', + ' description?: string', + " usage: { framework: 'angular'; componentName: string; selector: string }", + " mode: 'angular@webpack'", + " target: 'webpack'", + " format: 'stack' | 'symbol'", + ' iconCount: number', + ' spriteUrl: string', + ' icons: readonly SpriteManifestIcon[]', + '}', + '', + 'export declare const spriteManifest: SpriteManifest', + 'export default spriteManifest', + '', + ].join('\n') +} + +export function generateOutputFiles(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): GeneratedFile[] { + return [ + { path: path.posix.join(OUTPUT_DIR, 'index.ts'), content: index(config) }, + { path: path.posix.join(OUTPUT_DIR, 'index.d.ts'), content: indexDeclarations(config) }, + { path: path.posix.join(OUTPUT_DIR, 'sprite.svg'), content: svg(artifact.bytes, config.generatedNotice) }, + { path: path.posix.join(OUTPUT_DIR, 'icon-data.js'), content: iconData(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'icon-data.d.ts'), content: iconDeclarations(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'angular', 'angular-component.ts'), content: component(config) }, + { path: path.posix.join(OUTPUT_DIR, 'angular', 'angular-component.d.ts'), content: componentDeclarations(config) }, + { path: path.posix.join(OUTPUT_DIR, 'angular', 'angular-component.css'), content: styles(config) }, + { path: path.posix.join(OUTPUT_DIR, 'svg-sprite.manifest.js'), content: manifest(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'svg-sprite.manifest.d.ts'), content: manifestDeclarations(config) }, + ] +} diff --git a/src/modes/astro-vite/adapter.ts b/src/modes/astro-vite/adapter.ts new file mode 100644 index 0000000..4d4a36f --- /dev/null +++ b/src/modes/astro-vite/adapter.ts @@ -0,0 +1,36 @@ +import { compileSpriteContent } from '../../compiler.js' +import { createCompiledSpriteArtifact } from '../../core/compiled-artifact.js' +import type { ModeAdapterContext, OutputPlan } from '../../core/mode-adapter.js' +import { generateOutputFiles } from './output.js' + +type AstroViteOutputPlan = Omit & { + readonly result: { readonly target: 'vite' } +} + +type AstroViteAdapter = { + readonly mode: 'astro@vite' + generate(context: ModeAdapterContext): Promise +} + +export const astroViteAdapter: AstroViteAdapter = { + mode: 'astro@vite', + async generate(context) { + const bytes = await compileSpriteContent( + context.prepared.folder, + context.config.transform, + { rootViewBox: false }, + ) + const artifact = createCompiledSpriteArtifact(bytes, context.prepared.iconNames, 'stack') + + return { + files: generateOutputFiles(context.config, artifact), + paths: { + generatedDir: '.svg-sprite', + sprite: '.svg-sprite/sprite.svg', + manifest: '.svg-sprite/svg-sprite.manifest.js', + entry: '.svg-sprite/index.js', + }, + result: { target: 'vite' }, + } + }, +} diff --git a/src/modes/astro-vite/output.ts b/src/modes/astro-vite/output.ts new file mode 100644 index 0000000..95ed5ca --- /dev/null +++ b/src/modes/astro-vite/output.ts @@ -0,0 +1,238 @@ +import path from 'node:path' +import type { CompiledSpriteArtifact } from '../../core/compiled-artifact.js' +import { GENERATED_MARKER } from '../../core/generated-markers.js' +import type { GeneratedFile } from '../../core/mode-adapter.js' +import type { ResolvedSpriteConfig } from '../../types.js' + +const MODE = 'astro@vite' +const TARGET = 'vite' +const OUTPUT_DIR = '.svg-sprite' +const NOTICE = 'АВТОМАТИЧЕСКИ СГЕНЕРИРОВАННЫЙ ФАЙЛ. Не редактируйте вручную.' + +function pascal(value: string): string { + return value.split('-').map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join('') +} + +function camel(value: string): string { + return value.replace(/-([a-z0-9])/g, (_, char: string) => char.toUpperCase()) +} + +function header(enabled: boolean): string { + return enabled + ? `/*\n * ${NOTICE}\n * ${GENERATED_MARKER}.\n */` + : `/* ${GENERATED_MARKER}. Do not edit. */` +} + +function astroHeader(enabled: boolean): string { + return enabled + ? `` + : `` +} + +function svg(bytes: Uint8Array, enabled: boolean): string { + const marker = enabled ? `${NOTICE}\n ${GENERATED_MARKER}.` : `${GENERATED_MARKER}. Do not edit.` + const content = new TextDecoder().decode(bytes) + return content.startsWith(')\s*/, `$1\n\n`) + : `\n${content}` +} + +function description(config: ResolvedSpriteConfig): string[] { + const text = config.description ?? `Имена иконок SVG-спрайта «${config.name}».` + return ['/**', ...text.split(/\r?\n/).map((line) => ` * ${line.replace(/\*\//g, '* /')}`), ' */'] +} + +function iconData(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const prefix = camel(config.name) + return [ + header(config.generatedNotice), + '', + ...description(config), + `export const ${prefix}IconNames = ${JSON.stringify(artifact.icons.map((icon) => icon.name), null, 2)}`, + '', + `export const ${prefix}IconIds = ${JSON.stringify(Object.fromEntries(artifact.icons.map((icon) => [icon.name, icon.id])), null, 2)}`, + '', + ].join('\n') +} + +function iconDeclarations(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const componentName = `${pascal(config.name)}Icon` + const names = `${camel(config.name)}IconNames` + const ids = `${camel(config.name)}IconIds` + return [ + header(config.generatedNotice), + '', + ...description(config), + `export declare const ${names}: readonly [`, + ...artifact.icons.map((icon) => ` ${JSON.stringify(icon.name)},`), + ']', + `export type ${componentName}Name = typeof ${names}[number]`, + `export declare const ${ids}: {`, + ...artifact.icons.map((icon) => ` readonly ${JSON.stringify(icon.name)}: ${JSON.stringify(icon.id)}`), + '}', + '', + ].join('\n') +} + +function component(config: ResolvedSpriteConfig): string { + const ids = `${camel(config.name)}IconIds` + const rootClass = `${config.name}-icon-root` + const wrapClass = `${config.name}-icon-wrap` + return [ + '---', + `import './astro-component.css'`, + `import { ${ids} } from '../icon-data.js'`, + "import spriteUrl from '../sprite.svg?no-inline'", + '', + 'const { icon, wrapped = false, class: className, ...attributes } = Astro.props', + `const href = spriteUrl + '#' + ${ids}[icon]`, + '---', + astroHeader(config.generatedNotice), + '{', + ' wrapped ? (', + ` `, + ' ', + ' ', + ' ) : (', + ` `, + ' ', + ' ', + ' )', + '}', + '', + ].join('\n') +} + +function componentDeclarations(config: ResolvedSpriteConfig): string { + const componentName = `${pascal(config.name)}Icon` + const typeName = `${componentName}Name` + return [ + header(config.generatedNotice), + "import type { HTMLAttributes } from 'astro/types'", + `import type { ${typeName} } from '../icon-data.js'`, + '', + `export type ${componentName}Style = HTMLAttributes<'svg'>['style']`, + `type ${componentName}BaseProps = { icon: ${typeName} }`, + `type ${componentName}SvgProps = ${componentName}BaseProps & { wrapped?: false } & HTMLAttributes<'svg'>`, + `type ${componentName}WrappedProps = ${componentName}BaseProps & { wrapped: true } & HTMLAttributes<'span'>`, + `export type ${componentName}Props = ${componentName}SvgProps | ${componentName}WrappedProps`, + `declare const ${componentName}: (props: ${componentName}Props) => any`, + `export default ${componentName}`, + '', + ].join('\n') +} + +function index(config: ResolvedSpriteConfig): string { + return [ + header(config.generatedNotice), + `export { default as ${pascal(config.name)}Icon } from './astro/astro-component.astro'`, + `export { ${camel(config.name)}IconNames } from './icon-data.js'`, + '', + ].join('\n') +} + +function indexDeclarations(config: ResolvedSpriteConfig): string { + const componentName = `${pascal(config.name)}Icon` + return [ + header(config.generatedNotice), + `export { default as ${componentName} } from './astro/astro-component.astro'`, + `export type { ${componentName}Props, ${componentName}Style } from './astro/astro-component.astro'`, + `export { ${camel(config.name)}IconNames } from './icon-data.js'`, + `export type { ${componentName}Name } from './icon-data.js'`, + '', + ].join('\n') +} + +function styles(config: ResolvedSpriteConfig): string { + const transition = config.transform.addTransition + ? [' transition-property: fill, stroke, color;', ' transition-duration: 0.3s;', ' transition-timing-function: ease;'] + : [] + return [ + header(config.generatedNotice), + '', + `.${config.name}-icon-root {`, + ...transition, + '}', + '', + `.${config.name}-icon-wrap {`, + ' display: inline-flex;', + '}', + '', + `.${config.name}-icon-wrap svg {`, + ' width: 100%;', + ' height: 100%;', + ...transition, + '}', + '', + ].join('\n') +} + +function manifest(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const data = { + schemaVersion: 1, + generator: '@gromlab/svg-sprites', + name: config.name, + ...(config.description === undefined ? {} : { description: config.description }), + usage: { + framework: 'astro', + componentName: `${pascal(config.name)}Icon`, + }, + mode: MODE, + target: TARGET, + format: artifact.format, + iconCount: artifact.icons.length, + spriteUrl: '__SPRITE_URL__', + icons: artifact.icons, + } + const source = JSON.stringify(data, null, 2).replace('"__SPRITE_URL__"', 'spriteUrl') + return [ + header(config.generatedNotice), + "import spriteUrl from './sprite.svg?no-inline'", + '', + `export const spriteManifest = ${source}`, + '', + 'export default spriteManifest', + '', + ].join('\n') +} + +function manifestDeclarations(config: ResolvedSpriteConfig): string { + return [ + header(config.generatedNotice), + '', + 'export type SpriteManifestColor = { variable: `--icon-color-${number}`; fallback: string }', + 'export type SpriteManifestIcon = { name: string; id: string; viewBox: string | null; colors: readonly SpriteManifestColor[] }', + 'export type SpriteManifest = {', + ' schemaVersion: 1', + " generator: '@gromlab/svg-sprites'", + ' name: string', + ' description?: string', + " usage: { framework: 'astro'; componentName: string }", + " mode: 'astro@vite'", + " target: 'vite'", + " format: 'stack' | 'symbol'", + ' iconCount: number', + ' spriteUrl: string', + ' icons: readonly SpriteManifestIcon[]', + '}', + '', + 'export declare const spriteManifest: SpriteManifest', + 'export default spriteManifest', + '', + ].join('\n') +} + +export function generateOutputFiles(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): GeneratedFile[] { + return [ + { path: path.posix.join(OUTPUT_DIR, 'index.js'), content: index(config) }, + { path: path.posix.join(OUTPUT_DIR, 'index.d.ts'), content: indexDeclarations(config) }, + { path: path.posix.join(OUTPUT_DIR, 'sprite.svg'), content: svg(artifact.bytes, config.generatedNotice) }, + { path: path.posix.join(OUTPUT_DIR, 'icon-data.js'), content: iconData(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'icon-data.d.ts'), content: iconDeclarations(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'astro', 'astro-component.astro'), content: component(config) }, + { path: path.posix.join(OUTPUT_DIR, 'astro', 'astro-component.astro.d.ts'), content: componentDeclarations(config) }, + { path: path.posix.join(OUTPUT_DIR, 'astro', 'astro-component.css'), content: styles(config) }, + { path: path.posix.join(OUTPUT_DIR, 'svg-sprite.manifest.js'), content: manifest(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'svg-sprite.manifest.d.ts'), content: manifestDeclarations(config) }, + ] +} diff --git a/src/modes/lit-vite/adapter.ts b/src/modes/lit-vite/adapter.ts new file mode 100644 index 0000000..fe392f9 --- /dev/null +++ b/src/modes/lit-vite/adapter.ts @@ -0,0 +1,25 @@ +import { compileSpriteContent } from '../../compiler.js' +import { createCompiledSpriteArtifact } from '../../core/compiled-artifact.js' +import type { ModeAdapter } from '../../core/mode-adapter.js' +import { generateOutputFiles } from './output.js' + +export const litViteAdapter: ModeAdapter<'lit@vite'> = { + mode: 'lit@vite', + async generate(context) { + const bytes = await compileSpriteContent(context.prepared.folder, context.config.transform, { + rootViewBox: false, + }) + const artifact = createCompiledSpriteArtifact(bytes, context.prepared.iconNames, 'stack') + + return { + files: generateOutputFiles(context.config, artifact), + paths: { + generatedDir: '.svg-sprite', + sprite: '.svg-sprite/sprite.svg', + manifest: '.svg-sprite/svg-sprite.manifest.js', + entry: '.svg-sprite/index.js', + }, + result: { target: 'vite' }, + } + }, +} diff --git a/src/modes/lit-vite/output.ts b/src/modes/lit-vite/output.ts new file mode 100644 index 0000000..dd40843 --- /dev/null +++ b/src/modes/lit-vite/output.ts @@ -0,0 +1,258 @@ +import path from 'node:path' +import type { CompiledSpriteArtifact } from '../../core/compiled-artifact.js' +import { GENERATED_MARKER } from '../../core/generated-markers.js' +import type { GeneratedFile } from '../../core/mode-adapter.js' +import type { ResolvedSpriteConfig } from '../../types.js' + +const MODE = 'lit@vite' +const TARGET = 'vite' +const OUTPUT_DIR = '.svg-sprite' +const NOTICE = 'AUTOMATICALLY GENERATED FILE. Do not edit manually.' + +function pascal(value: string): string { + return value.split('-').map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join('') +} + +function camel(value: string): string { + return value.replace(/-([a-z0-9])/g, (_, char: string) => char.toUpperCase()) +} + +function header(enabled: boolean): string { + return enabled + ? `/*\n * ${NOTICE}\n * ${GENERATED_MARKER}.\n */` + : `/* ${GENERATED_MARKER}. Do not edit. */` +} + +function markedSvg(bytes: Uint8Array, enabled: boolean): string { + const marker = enabled ? `${NOTICE} ${GENERATED_MARKER}.` : `${GENERATED_MARKER}. Do not edit.` + const content = new TextDecoder().decode(bytes) + return content.startsWith(')\s*/, `$1\n\n`) + : `\n${content}` +} + +function description(config: ResolvedSpriteConfig): string[] { + const text = config.description ?? `Icon names for the "${config.name}" SVG sprite.` + return ['/**', ...text.split(/\r?\n/).map((line) => ` * ${line.replace(/\*\//g, '* /')}`), ' */'] +} + +function iconData(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const prefix = camel(config.name) + const names = artifact.icons.map((icon) => icon.name) + const ids = Object.fromEntries(artifact.icons.map((icon) => [icon.name, icon.id])) + return [ + header(config.generatedNotice), + '', + ...description(config), + `export const ${prefix}IconNames = ${JSON.stringify(names, null, 2)}`, + '', + `export const ${prefix}IconIds = ${JSON.stringify(ids, null, 2)}`, + '', + ].join('\n') +} + +function iconTypes(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const component = `${pascal(config.name)}Icon` + const names = `${camel(config.name)}IconNames` + const ids = `${camel(config.name)}IconIds` + return [ + header(config.generatedNotice), + '', + ...description(config), + `export declare const ${names}: readonly [`, + ...artifact.icons.map((icon) => ` ${JSON.stringify(icon.name)},`), + ']', + `export type ${component}Name = typeof ${names}[number]`, + `export declare const ${ids}: {`, + ...artifact.icons.map((icon) => ` readonly ${JSON.stringify(icon.name)}: ${JSON.stringify(icon.id)}`), + '}', + '', + ].join('\n') +} + +function component(config: ResolvedSpriteConfig): string { + const className = `${pascal(config.name)}Icon` + const prefix = camel(config.name) + const tagName = `${config.name}-icon` + return [ + header(config.generatedNotice), + "import { LitElement, html, nothing, unsafeCSS } from 'lit'", + "import componentStyles from './lit-component.css?inline'", + `import { ${prefix}IconIds } from '../icon-data.js'`, + "import spriteUrl from '../sprite.svg?no-inline'", + '', + `export const ${prefix}IconTagName = ${JSON.stringify(tagName)}`, + '', + `export class ${className} extends LitElement {`, + ' static properties = {', + " icon: { type: String },", + ' }', + '', + ' static styles = unsafeCSS(componentStyles)', + '', + " icon = ''", + '', + ' render() {', + ` const id = ${prefix}IconIds[this.icon]`, + ' if (!id) return nothing', + '', + " const href = spriteUrl + '#' + id", + ' return html``', + ' }', + '}', + '', + `export function define${className}() {`, + ` if (!customElements.get(${prefix}IconTagName)) {`, + ` customElements.define(${prefix}IconTagName, ${className})`, + ' }', + '}', + '', + ].join('\n') +} + +function componentTypes(config: ResolvedSpriteConfig): string { + const className = `${pascal(config.name)}Icon` + const prefix = camel(config.name) + const tagName = `${config.name}-icon` + return [ + header(config.generatedNotice), + "import { LitElement } from 'lit'", + `import type { ${className}Name } from '../icon-data.js'`, + '', + `export declare const ${prefix}IconTagName: ${JSON.stringify(tagName)}`, + `export declare class ${className} extends LitElement {`, + ` icon: ${className}Name | ''`, + '}', + `export declare function define${className}(): void`, + '', + 'declare global {', + ' interface HTMLElementTagNameMap {', + ` ${JSON.stringify(tagName)}: ${className}`, + ' }', + '}', + '', + ].join('\n') +} + +function styles(config: ResolvedSpriteConfig): string { + const transition = config.transform.addTransition + ? [' transition-property: fill, stroke, color;', ' transition-duration: 0.3s;', ' transition-timing-function: ease;'] + : [] + return [ + header(config.generatedNotice), + '', + ':host {', + ' display: inline-flex;', + ' width: 1em;', + ' height: 1em;', + ' vertical-align: middle;', + '}', + '', + '.root {', + ' width: 100%;', + ' height: 100%;', + ...transition, + '}', + '', + ].join('\n') +} + +function index(config: ResolvedSpriteConfig): string { + const className = `${pascal(config.name)}Icon` + const prefix = camel(config.name) + return [ + header(config.generatedNotice), + `export { ${className}, define${className}, ${prefix}IconTagName } from './lit/lit-component.js'`, + `export { ${prefix}IconNames } from './icon-data.js'`, + '', + ].join('\n') +} + +function indexTypes(config: ResolvedSpriteConfig): string { + const className = `${pascal(config.name)}Icon` + const prefix = camel(config.name) + return [ + header(config.generatedNotice), + `export { ${className}, define${className}, ${prefix}IconTagName } from './lit/lit-component.js'`, + `export { ${prefix}IconNames } from './icon-data.js'`, + `export type { ${className}Name } from './icon-data.js'`, + '', + ].join('\n') +} + +function manifest(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const className = `${pascal(config.name)}Icon` + const data = { + schemaVersion: 1, + generator: '@gromlab/svg-sprites', + name: config.name, + ...(config.description === undefined ? {} : { description: config.description }), + usage: { + framework: 'lit', + componentName: className, + tagName: `${config.name}-icon`, + defineFunction: `define${className}`, + }, + mode: MODE, + target: TARGET, + format: artifact.format, + iconCount: artifact.icons.length, + spriteUrl: '__SPRITE_URL__', + icons: artifact.icons, + } + const source = JSON.stringify(data, null, 2).replace('"__SPRITE_URL__"', 'spriteUrl') + return [ + header(config.generatedNotice), + "import spriteUrl from './sprite.svg?no-inline'", + '', + `export const spriteManifest = ${source}`, + '', + 'export default spriteManifest', + '', + ].join('\n') +} + +function manifestTypes(config: ResolvedSpriteConfig): string { + return [ + header(config.generatedNotice), + '', + 'export type SpriteManifestColor = { variable: `--icon-color-${number}`; fallback: string }', + 'export type SpriteManifestIcon = {', + ' name: string', + ' id: string', + ' viewBox: string | null', + ' colors: readonly SpriteManifestColor[]', + '}', + 'export type SpriteManifest = {', + ' schemaVersion: 1', + " generator: '@gromlab/svg-sprites'", + ' name: string', + ' description?: string', + " usage: { framework: 'lit'; componentName: string; tagName: string; defineFunction: string }", + " mode: 'lit@vite'", + " target: 'vite'", + " format: 'stack' | 'symbol'", + ' iconCount: number', + ' spriteUrl: string', + ' icons: readonly SpriteManifestIcon[]', + '}', + 'export declare const spriteManifest: SpriteManifest', + 'export default spriteManifest', + '', + ].join('\n') +} + +export function generateOutputFiles(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): GeneratedFile[] { + return [ + { path: path.posix.join(OUTPUT_DIR, 'index.js'), content: index(config) }, + { path: path.posix.join(OUTPUT_DIR, 'index.d.ts'), content: indexTypes(config) }, + { path: path.posix.join(OUTPUT_DIR, 'sprite.svg'), content: markedSvg(artifact.bytes, config.generatedNotice) }, + { path: path.posix.join(OUTPUT_DIR, 'icon-data.js'), content: iconData(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'icon-data.d.ts'), content: iconTypes(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'lit', 'lit-component.js'), content: component(config) }, + { path: path.posix.join(OUTPUT_DIR, 'lit', 'lit-component.d.ts'), content: componentTypes(config) }, + { path: path.posix.join(OUTPUT_DIR, 'lit', 'lit-component.css'), content: styles(config) }, + { path: path.posix.join(OUTPUT_DIR, 'svg-sprite.manifest.js'), content: manifest(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'svg-sprite.manifest.d.ts'), content: manifestTypes(config) }, + ] +} diff --git a/src/modes/lit-webpack/adapter.ts b/src/modes/lit-webpack/adapter.ts new file mode 100644 index 0000000..329b293 --- /dev/null +++ b/src/modes/lit-webpack/adapter.ts @@ -0,0 +1,25 @@ +import { compileSpriteContent } from '../../compiler.js' +import { createCompiledSpriteArtifact } from '../../core/compiled-artifact.js' +import type { ModeAdapter } from '../../core/mode-adapter.js' +import { generateOutputFiles } from './output.js' + +export const litWebpackAdapter: ModeAdapter<'lit@webpack'> = { + mode: 'lit@webpack', + async generate(context) { + const bytes = await compileSpriteContent(context.prepared.folder, context.config.transform, { + rootViewBox: false, + }) + const artifact = createCompiledSpriteArtifact(bytes, context.prepared.iconNames, 'stack') + + return { + files: generateOutputFiles(context.config, artifact), + paths: { + generatedDir: '.svg-sprite', + sprite: '.svg-sprite/sprite.svg', + manifest: '.svg-sprite/svg-sprite.manifest.js', + entry: '.svg-sprite/index.js', + }, + result: { target: 'webpack' }, + } + }, +} diff --git a/src/modes/lit-webpack/output.ts b/src/modes/lit-webpack/output.ts new file mode 100644 index 0000000..b72f398 --- /dev/null +++ b/src/modes/lit-webpack/output.ts @@ -0,0 +1,259 @@ +import path from 'node:path' +import type { CompiledSpriteArtifact } from '../../core/compiled-artifact.js' +import { GENERATED_MARKER } from '../../core/generated-markers.js' +import type { GeneratedFile } from '../../core/mode-adapter.js' +import type { ResolvedSpriteConfig } from '../../types.js' + +const MODE = 'lit@webpack' +const TARGET = 'webpack' +const OUTPUT_DIR = '.svg-sprite' +const NOTICE = 'AUTOMATICALLY GENERATED FILE. Do not edit manually.' + +function pascal(value: string): string { + return value.split('-').map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join('') +} + +function camel(value: string): string { + return value.replace(/-([a-z0-9])/g, (_, char: string) => char.toUpperCase()) +} + +function header(enabled: boolean): string { + return enabled + ? `/*\n * ${NOTICE}\n * ${GENERATED_MARKER}.\n */` + : `/* ${GENERATED_MARKER}. Do not edit. */` +} + +function markedSvg(bytes: Uint8Array, enabled: boolean): string { + const marker = enabled ? `${NOTICE} ${GENERATED_MARKER}.` : `${GENERATED_MARKER}. Do not edit.` + const content = new TextDecoder().decode(bytes) + return content.startsWith(')\s*/, `$1\n\n`) + : `\n${content}` +} + +function description(config: ResolvedSpriteConfig): string[] { + const text = config.description ?? `Icon names for the "${config.name}" SVG sprite.` + return ['/**', ...text.split(/\r?\n/).map((line) => ` * ${line.replace(/\*\//g, '* /')}`), ' */'] +} + +function iconData(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const prefix = camel(config.name) + const names = artifact.icons.map((icon) => icon.name) + const ids = Object.fromEntries(artifact.icons.map((icon) => [icon.name, icon.id])) + return [ + header(config.generatedNotice), + '', + ...description(config), + `export const ${prefix}IconNames = ${JSON.stringify(names, null, 2)}`, + '', + `export const ${prefix}IconIds = ${JSON.stringify(ids, null, 2)}`, + '', + ].join('\n') +} + +function iconTypes(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const component = `${pascal(config.name)}Icon` + const names = `${camel(config.name)}IconNames` + const ids = `${camel(config.name)}IconIds` + return [ + header(config.generatedNotice), + '', + ...description(config), + `export declare const ${names}: readonly [`, + ...artifact.icons.map((icon) => ` ${JSON.stringify(icon.name)},`), + ']', + `export type ${component}Name = typeof ${names}[number]`, + `export declare const ${ids}: {`, + ...artifact.icons.map((icon) => ` readonly ${JSON.stringify(icon.name)}: ${JSON.stringify(icon.id)}`), + '}', + '', + ].join('\n') +} + +function component(config: ResolvedSpriteConfig): string { + const className = `${pascal(config.name)}Icon` + const prefix = camel(config.name) + const tagName = `${config.name}-icon` + return [ + header(config.generatedNotice), + "import { LitElement, html, nothing, unsafeCSS } from 'lit'", + "import componentStyles from './lit-component.css?inline'", + `import { ${prefix}IconIds } from '../icon-data.js'`, + '', + "const spriteUrl = new URL('../sprite.svg', import.meta.url).href", + '', + `export const ${prefix}IconTagName = ${JSON.stringify(tagName)}`, + '', + `export class ${className} extends LitElement {`, + ' static properties = {', + " icon: { type: String },", + ' }', + '', + ' static styles = unsafeCSS(componentStyles)', + '', + " icon = ''", + '', + ' render() {', + ` const id = ${prefix}IconIds[this.icon]`, + ' if (!id) return nothing', + '', + " const href = spriteUrl + '#' + id", + ' return html``', + ' }', + '}', + '', + `export function define${className}() {`, + ` if (!customElements.get(${prefix}IconTagName)) {`, + ` customElements.define(${prefix}IconTagName, ${className})`, + ' }', + '}', + '', + ].join('\n') +} + +function componentTypes(config: ResolvedSpriteConfig): string { + const className = `${pascal(config.name)}Icon` + const prefix = camel(config.name) + const tagName = `${config.name}-icon` + return [ + header(config.generatedNotice), + "import { LitElement } from 'lit'", + `import type { ${className}Name } from '../icon-data.js'`, + '', + `export declare const ${prefix}IconTagName: ${JSON.stringify(tagName)}`, + `export declare class ${className} extends LitElement {`, + ` icon: ${className}Name | ''`, + '}', + `export declare function define${className}(): void`, + '', + 'declare global {', + ' interface HTMLElementTagNameMap {', + ` ${JSON.stringify(tagName)}: ${className}`, + ' }', + '}', + '', + ].join('\n') +} + +function styles(config: ResolvedSpriteConfig): string { + const transition = config.transform.addTransition + ? [' transition-property: fill, stroke, color;', ' transition-duration: 0.3s;', ' transition-timing-function: ease;'] + : [] + return [ + header(config.generatedNotice), + '', + ':host {', + ' display: inline-flex;', + ' width: 1em;', + ' height: 1em;', + ' vertical-align: middle;', + '}', + '', + '.root {', + ' width: 100%;', + ' height: 100%;', + ...transition, + '}', + '', + ].join('\n') +} + +function index(config: ResolvedSpriteConfig): string { + const className = `${pascal(config.name)}Icon` + const prefix = camel(config.name) + return [ + header(config.generatedNotice), + `export { ${className}, define${className}, ${prefix}IconTagName } from './lit/lit-component.js'`, + `export { ${prefix}IconNames } from './icon-data.js'`, + '', + ].join('\n') +} + +function indexTypes(config: ResolvedSpriteConfig): string { + const className = `${pascal(config.name)}Icon` + const prefix = camel(config.name) + return [ + header(config.generatedNotice), + `export { ${className}, define${className}, ${prefix}IconTagName } from './lit/lit-component.js'`, + `export { ${prefix}IconNames } from './icon-data.js'`, + `export type { ${className}Name } from './icon-data.js'`, + '', + ].join('\n') +} + +function manifest(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const className = `${pascal(config.name)}Icon` + const data = { + schemaVersion: 1, + generator: '@gromlab/svg-sprites', + name: config.name, + ...(config.description === undefined ? {} : { description: config.description }), + usage: { + framework: 'lit', + componentName: className, + tagName: `${config.name}-icon`, + defineFunction: `define${className}`, + }, + mode: MODE, + target: TARGET, + format: artifact.format, + iconCount: artifact.icons.length, + spriteUrl: '__SPRITE_URL__', + icons: artifact.icons, + } + const source = JSON.stringify(data, null, 2).replace('"__SPRITE_URL__"', 'spriteUrl') + return [ + header(config.generatedNotice), + "const spriteUrl = new URL('./sprite.svg', import.meta.url).href", + '', + `export const spriteManifest = ${source}`, + '', + 'export default spriteManifest', + '', + ].join('\n') +} + +function manifestTypes(config: ResolvedSpriteConfig): string { + return [ + header(config.generatedNotice), + '', + 'export type SpriteManifestColor = { variable: `--icon-color-${number}`; fallback: string }', + 'export type SpriteManifestIcon = {', + ' name: string', + ' id: string', + ' viewBox: string | null', + ' colors: readonly SpriteManifestColor[]', + '}', + 'export type SpriteManifest = {', + ' schemaVersion: 1', + " generator: '@gromlab/svg-sprites'", + ' name: string', + ' description?: string', + " usage: { framework: 'lit'; componentName: string; tagName: string; defineFunction: string }", + " mode: 'lit@webpack'", + " target: 'webpack'", + " format: 'stack' | 'symbol'", + ' iconCount: number', + ' spriteUrl: string', + ' icons: readonly SpriteManifestIcon[]', + '}', + 'export declare const spriteManifest: SpriteManifest', + 'export default spriteManifest', + '', + ].join('\n') +} + +export function generateOutputFiles(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): GeneratedFile[] { + return [ + { path: path.posix.join(OUTPUT_DIR, 'index.js'), content: index(config) }, + { path: path.posix.join(OUTPUT_DIR, 'index.d.ts'), content: indexTypes(config) }, + { path: path.posix.join(OUTPUT_DIR, 'sprite.svg'), content: markedSvg(artifact.bytes, config.generatedNotice) }, + { path: path.posix.join(OUTPUT_DIR, 'icon-data.js'), content: iconData(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'icon-data.d.ts'), content: iconTypes(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'lit', 'lit-component.js'), content: component(config) }, + { path: path.posix.join(OUTPUT_DIR, 'lit', 'lit-component.d.ts'), content: componentTypes(config) }, + { path: path.posix.join(OUTPUT_DIR, 'lit', 'lit-component.css'), content: styles(config) }, + { path: path.posix.join(OUTPUT_DIR, 'svg-sprite.manifest.js'), content: manifest(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'svg-sprite.manifest.d.ts'), content: manifestTypes(config) }, + ] +} diff --git a/src/modes/nuxt-vite/adapter.ts b/src/modes/nuxt-vite/adapter.ts new file mode 100644 index 0000000..ca33e4b --- /dev/null +++ b/src/modes/nuxt-vite/adapter.ts @@ -0,0 +1,25 @@ +import { compileSpriteContent } from '../../compiler.js' +import { createCompiledSpriteArtifact } from '../../core/compiled-artifact.js' +import type { ModeAdapter } from '../../core/mode-adapter.js' +import { generateOutputFiles } from './output.js' + +export const nuxtViteAdapter: ModeAdapter<'nuxt@vite'> = { + mode: 'nuxt@vite', + async generate(context) { + const bytes = await compileSpriteContent(context.prepared.folder, context.config.transform, { + rootViewBox: false, + }) + const artifact = createCompiledSpriteArtifact(bytes, context.prepared.iconNames, 'stack') + + return { + files: generateOutputFiles(context.config, artifact), + paths: { + generatedDir: '.svg-sprite', + sprite: '.svg-sprite/sprite.svg', + manifest: '.svg-sprite/svg-sprite.manifest.js', + entry: '.svg-sprite/index.js', + }, + result: { target: 'vite' }, + } + }, +} diff --git a/src/modes/nuxt-vite/output.ts b/src/modes/nuxt-vite/output.ts new file mode 100644 index 0000000..675f17a --- /dev/null +++ b/src/modes/nuxt-vite/output.ts @@ -0,0 +1,241 @@ +import path from 'node:path' +import type { CompiledSpriteArtifact } from '../../core/compiled-artifact.js' +import { GENERATED_MARKER } from '../../core/generated-markers.js' +import type { GeneratedFile } from '../../core/mode-adapter.js' +import type { ResolvedSpriteConfig } from '../../types.js' + +const MODE = 'nuxt@vite' +const TARGET = 'vite' +const OUTPUT_DIR = '.svg-sprite' +const NOTICE = 'AUTOMATICALLY GENERATED FILE. Do not edit manually.' + +function pascal(value: string): string { + return value.split('-').map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join('') +} + +function camel(value: string): string { + return value.replace(/-([a-z0-9])/g, (_, char: string) => char.toUpperCase()) +} + +function header(enabled: boolean): string { + return enabled + ? `/*\n * ${NOTICE}\n * ${GENERATED_MARKER}.\n */` + : `/* ${GENERATED_MARKER}. Do not edit. */` +} + +function markedSvg(bytes: Uint8Array, enabled: boolean): string { + const marker = enabled ? `${NOTICE}\n ${GENERATED_MARKER}.` : `${GENERATED_MARKER}. Do not edit.` + const content = new TextDecoder().decode(bytes) + return content.startsWith(')\s*/, `$1\n\n`) + : `\n${content}` +} + +function docs(config: ResolvedSpriteConfig): string[] { + const text = config.description ?? `SVG sprite icon names for "${config.name}".` + return ['/**', ...text.split(/\r?\n/).map((line) => ` * ${line.replace(/\*\//g, '* /')}`), ' */'] +} + +function iconData(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const prefix = camel(config.name) + const names = artifact.icons.map((icon) => icon.name) + const ids = Object.fromEntries(artifact.icons.map((icon) => [icon.name, icon.id])) + return [ + header(config.generatedNotice), + '', + ...docs(config), + `export const ${prefix}IconNames = ${JSON.stringify(names, null, 2)}`, + '', + `export const ${prefix}IconIds = ${JSON.stringify(ids, null, 2)}`, + '', + ].join('\n') +} + +function component(config: ResolvedSpriteConfig): string { + const componentName = `${pascal(config.name)}Icon` + const ids = `${camel(config.name)}IconIds` + return [ + header(config.generatedNotice), + "import { defineComponent, h } from 'vue'", + "import styles from './vue-component.module.css'", + `import { ${ids} } from '../icon-data.js'`, + "import spriteUrl from '../sprite.svg?no-inline'", + '', + `export const ${componentName} = defineComponent({`, + ` name: '${componentName}',`, + ' inheritAttrs: false,', + ' props: {', + " icon: { type: String, required: true },", + " wrapped: { type: Boolean, default: false },", + ' },', + ' setup(props, { attrs }) {', + ' return () => {', + ` const href = spriteUrl + '#' + ${ids}[props.icon]`, + ' if (props.wrapped) {', + " return h('span', { ...attrs, class: [styles.wrap, attrs.class] }, [", + " h('svg', null, [h('use', { href })]),", + ' ])', + ' }', + " return h('svg', { ...attrs, class: [styles.root, attrs.class] }, [", + " h('use', { href }),", + ' ])', + ' }', + ' },', + '})', + '', + ].join('\n') +} + +function iconDeclarations(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const componentName = `${pascal(config.name)}Icon` + const names = `${camel(config.name)}IconNames` + const ids = `${camel(config.name)}IconIds` + return [ + header(config.generatedNotice), + '', + ...docs(config), + `export declare const ${names}: readonly [`, + ...artifact.icons.map((icon) => ` ${JSON.stringify(icon.name)},`), + ']', + `export type ${componentName}Name = typeof ${names}[number]`, + `export declare const ${ids}: {`, + ...artifact.icons.map((icon) => ` readonly ${JSON.stringify(icon.name)}: ${JSON.stringify(icon.id)}`), + '}', + '', + ].join('\n') +} + +function componentDeclarations(config: ResolvedSpriteConfig): string { + const componentName = `${pascal(config.name)}Icon` + return [ + header(config.generatedNotice), + "import type { CSSProperties, DefineComponent, HTMLAttributes, SVGAttributes } from 'vue'", + `import type { ${componentName}Name } from '../icon-data.js'`, + '', + `export type ${componentName}Style = CSSProperties & Partial>`, + `type ${componentName}BaseProps = { icon: ${componentName}Name }`, + `type ${componentName}SvgProps = ${componentName}BaseProps & { wrapped?: false } & Omit & { style?: ${componentName}Style }`, + `type ${componentName}WrappedProps = ${componentName}BaseProps & { wrapped: true } & Omit & { style?: ${componentName}Style }`, + `export type ${componentName}Props = ${componentName}SvgProps | ${componentName}WrappedProps`, + `export declare const ${componentName}: DefineComponent<${componentName}Props>`, + '', + ].join('\n') +} + +function index(config: ResolvedSpriteConfig): string { + return [ + header(config.generatedNotice), + `export { ${pascal(config.name)}Icon } from './vue/vue-component.js'`, + `export { ${camel(config.name)}IconNames } from './icon-data.js'`, + '', + ].join('\n') +} + +function indexDeclarations(config: ResolvedSpriteConfig): string { + const componentName = `${pascal(config.name)}Icon` + const names = `${camel(config.name)}IconNames` + return [ + header(config.generatedNotice), + `export { ${componentName} } from './vue/vue-component.js'`, + `export type { ${componentName}Props, ${componentName}Style } from './vue/vue-component.js'`, + `export { ${names} } from './icon-data.js'`, + `export type { ${componentName}Name } from './icon-data.js'`, + '', + ].join('\n') +} + +function styles(config: ResolvedSpriteConfig): string { + const transition = config.transform.addTransition + ? [' transition-property: fill, stroke, color;', ' transition-duration: 0.3s;', ' transition-timing-function: ease;'] + : [] + return [ + header(config.generatedNotice), + '', + '.root {', + ...transition, + '}', + '', + '.wrap {', + ' display: inline-flex;', + '}', + '', + '.wrap svg {', + ' width: 100%;', + ' height: 100%;', + ...transition, + '}', + '', + ].join('\n') +} + +function manifest(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const data = { + schemaVersion: 1, + generator: '@gromlab/svg-sprites', + name: config.name, + ...(config.description === undefined ? {} : { description: config.description }), + usage: { framework: 'vue', componentName: `${pascal(config.name)}Icon` }, + mode: MODE, + target: TARGET, + format: artifact.format, + iconCount: artifact.icons.length, + spriteUrl: '__SPRITE_URL__', + icons: artifact.icons, + } + const source = JSON.stringify(data, null, 2).replace('"__SPRITE_URL__"', 'spriteUrl') + return [ + header(config.generatedNotice), + "import spriteUrl from './sprite.svg?no-inline'", + '', + `export const spriteManifest = ${source}`, + '', + 'export default spriteManifest', + '', + ].join('\n') +} + +function manifestDeclarations(config: ResolvedSpriteConfig): string { + return [ + header(config.generatedNotice), + '', + 'export type SpriteManifestColor = { variable: `--icon-color-${number}`; fallback: string }', + 'export type SpriteManifestIcon = {', + ' name: string', + ' id: string', + ' viewBox: string | null', + ' colors: readonly SpriteManifestColor[]', + '}', + 'export type SpriteManifest = {', + ' schemaVersion: 1', + " generator: '@gromlab/svg-sprites'", + ' name: string', + ' description?: string', + " usage: { framework: 'vue'; componentName: string }", + " mode: 'nuxt@vite'", + " target: 'vite'", + " format: 'stack' | 'symbol'", + ' iconCount: number', + ' spriteUrl: string', + ' icons: readonly SpriteManifestIcon[]', + '}', + '', + 'export declare const spriteManifest: SpriteManifest', + 'export default spriteManifest', + '', + ].join('\n') +} + +export function generateOutputFiles(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): GeneratedFile[] { + return [ + { path: path.posix.join(OUTPUT_DIR, 'index.js'), content: index(config) }, + { path: path.posix.join(OUTPUT_DIR, 'index.d.ts'), content: indexDeclarations(config) }, + { path: path.posix.join(OUTPUT_DIR, 'sprite.svg'), content: markedSvg(artifact.bytes, config.generatedNotice) }, + { path: path.posix.join(OUTPUT_DIR, 'icon-data.js'), content: iconData(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'icon-data.d.ts'), content: iconDeclarations(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'vue', 'vue-component.js'), content: component(config) }, + { path: path.posix.join(OUTPUT_DIR, 'vue', 'vue-component.d.ts'), content: componentDeclarations(config) }, + { path: path.posix.join(OUTPUT_DIR, 'vue', 'vue-component.module.css'), content: styles(config) }, + { path: path.posix.join(OUTPUT_DIR, 'svg-sprite.manifest.js'), content: manifest(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'svg-sprite.manifest.d.ts'), content: manifestDeclarations(config) }, + ] +} diff --git a/src/modes/nuxt-webpack/adapter.ts b/src/modes/nuxt-webpack/adapter.ts new file mode 100644 index 0000000..4c6ffb7 --- /dev/null +++ b/src/modes/nuxt-webpack/adapter.ts @@ -0,0 +1,25 @@ +import { compileSpriteContent } from '../../compiler.js' +import { createCompiledSpriteArtifact } from '../../core/compiled-artifact.js' +import type { ModeAdapter } from '../../core/mode-adapter.js' +import { generateOutputFiles } from './output.js' + +export const nuxtWebpackAdapter: ModeAdapter<'nuxt@webpack'> = { + mode: 'nuxt@webpack', + async generate(context) { + const bytes = await compileSpriteContent(context.prepared.folder, context.config.transform, { + rootViewBox: false, + }) + const artifact = createCompiledSpriteArtifact(bytes, context.prepared.iconNames, 'stack') + + return { + files: generateOutputFiles(context.config, artifact), + paths: { + generatedDir: '.svg-sprite', + sprite: '.svg-sprite/sprite.svg', + manifest: '.svg-sprite/svg-sprite.manifest.js', + entry: '.svg-sprite/index.js', + }, + result: { target: 'webpack' }, + } + }, +} diff --git a/src/modes/nuxt-webpack/output.ts b/src/modes/nuxt-webpack/output.ts new file mode 100644 index 0000000..731fbae --- /dev/null +++ b/src/modes/nuxt-webpack/output.ts @@ -0,0 +1,241 @@ +import path from 'node:path' +import type { CompiledSpriteArtifact } from '../../core/compiled-artifact.js' +import { GENERATED_MARKER } from '../../core/generated-markers.js' +import type { GeneratedFile } from '../../core/mode-adapter.js' +import type { ResolvedSpriteConfig } from '../../types.js' + +const MODE = 'nuxt@webpack' +const TARGET = 'webpack' +const OUTPUT_DIR = '.svg-sprite' +const NOTICE = 'AUTOMATICALLY GENERATED FILE. Do not edit manually.' + +function pascal(value: string): string { + return value.split('-').map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join('') +} + +function camel(value: string): string { + return value.replace(/-([a-z0-9])/g, (_, char: string) => char.toUpperCase()) +} + +function header(enabled: boolean): string { + return enabled + ? `/*\n * ${NOTICE}\n * ${GENERATED_MARKER}.\n */` + : `/* ${GENERATED_MARKER}. Do not edit. */` +} + +function markedSvg(bytes: Uint8Array, enabled: boolean): string { + const marker = enabled ? `${NOTICE}\n ${GENERATED_MARKER}.` : `${GENERATED_MARKER}. Do not edit.` + const content = new TextDecoder().decode(bytes) + return content.startsWith(')\s*/, `$1\n\n`) + : `\n${content}` +} + +function docs(config: ResolvedSpriteConfig): string[] { + const text = config.description ?? `SVG sprite icon names for "${config.name}".` + return ['/**', ...text.split(/\r?\n/).map((line) => ` * ${line.replace(/\*\//g, '* /')}`), ' */'] +} + +function iconData(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const prefix = camel(config.name) + const names = artifact.icons.map((icon) => icon.name) + const ids = Object.fromEntries(artifact.icons.map((icon) => [icon.name, icon.id])) + return [ + header(config.generatedNotice), + '', + ...docs(config), + `export const ${prefix}IconNames = ${JSON.stringify(names, null, 2)}`, + '', + `export const ${prefix}IconIds = ${JSON.stringify(ids, null, 2)}`, + '', + ].join('\n') +} + +function component(config: ResolvedSpriteConfig): string { + const componentName = `${pascal(config.name)}Icon` + const ids = `${camel(config.name)}IconIds` + return [ + header(config.generatedNotice), + "import { defineComponent, h } from 'vue'", + "import styles from './vue-component.module.css'", + `import { ${ids} } from '../icon-data.js'`, + "import spriteUrl from '../sprite.svg'", + '', + `export const ${componentName} = defineComponent({`, + ` name: '${componentName}',`, + ' inheritAttrs: false,', + ' props: {', + " icon: { type: String, required: true },", + " wrapped: { type: Boolean, default: false },", + ' },', + ' setup(props, { attrs }) {', + ' return () => {', + ` const href = spriteUrl + '#' + ${ids}[props.icon]`, + ' if (props.wrapped) {', + " return h('span', { ...attrs, class: [styles.wrap, attrs.class] }, [", + " h('svg', null, [h('use', { href })]),", + ' ])', + ' }', + " return h('svg', { ...attrs, class: [styles.root, attrs.class] }, [", + " h('use', { href }),", + ' ])', + ' }', + ' },', + '})', + '', + ].join('\n') +} + +function iconDeclarations(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const componentName = `${pascal(config.name)}Icon` + const names = `${camel(config.name)}IconNames` + const ids = `${camel(config.name)}IconIds` + return [ + header(config.generatedNotice), + '', + ...docs(config), + `export declare const ${names}: readonly [`, + ...artifact.icons.map((icon) => ` ${JSON.stringify(icon.name)},`), + ']', + `export type ${componentName}Name = typeof ${names}[number]`, + `export declare const ${ids}: {`, + ...artifact.icons.map((icon) => ` readonly ${JSON.stringify(icon.name)}: ${JSON.stringify(icon.id)}`), + '}', + '', + ].join('\n') +} + +function componentDeclarations(config: ResolvedSpriteConfig): string { + const componentName = `${pascal(config.name)}Icon` + return [ + header(config.generatedNotice), + "import type { CSSProperties, DefineComponent, HTMLAttributes, SVGAttributes } from 'vue'", + `import type { ${componentName}Name } from '../icon-data.js'`, + '', + `export type ${componentName}Style = CSSProperties & Partial>`, + `type ${componentName}BaseProps = { icon: ${componentName}Name }`, + `type ${componentName}SvgProps = ${componentName}BaseProps & { wrapped?: false } & Omit & { style?: ${componentName}Style }`, + `type ${componentName}WrappedProps = ${componentName}BaseProps & { wrapped: true } & Omit & { style?: ${componentName}Style }`, + `export type ${componentName}Props = ${componentName}SvgProps | ${componentName}WrappedProps`, + `export declare const ${componentName}: DefineComponent<${componentName}Props>`, + '', + ].join('\n') +} + +function index(config: ResolvedSpriteConfig): string { + return [ + header(config.generatedNotice), + `export { ${pascal(config.name)}Icon } from './vue/vue-component.js'`, + `export { ${camel(config.name)}IconNames } from './icon-data.js'`, + '', + ].join('\n') +} + +function indexDeclarations(config: ResolvedSpriteConfig): string { + const componentName = `${pascal(config.name)}Icon` + const names = `${camel(config.name)}IconNames` + return [ + header(config.generatedNotice), + `export { ${componentName} } from './vue/vue-component.js'`, + `export type { ${componentName}Props, ${componentName}Style } from './vue/vue-component.js'`, + `export { ${names} } from './icon-data.js'`, + `export type { ${componentName}Name } from './icon-data.js'`, + '', + ].join('\n') +} + +function styles(config: ResolvedSpriteConfig): string { + const transition = config.transform.addTransition + ? [' transition-property: fill, stroke, color;', ' transition-duration: 0.3s;', ' transition-timing-function: ease;'] + : [] + return [ + header(config.generatedNotice), + '', + '.root {', + ...transition, + '}', + '', + '.wrap {', + ' display: inline-flex;', + '}', + '', + '.wrap svg {', + ' width: 100%;', + ' height: 100%;', + ...transition, + '}', + '', + ].join('\n') +} + +function manifest(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const data = { + schemaVersion: 1, + generator: '@gromlab/svg-sprites', + name: config.name, + ...(config.description === undefined ? {} : { description: config.description }), + usage: { framework: 'vue', componentName: `${pascal(config.name)}Icon` }, + mode: MODE, + target: TARGET, + format: artifact.format, + iconCount: artifact.icons.length, + spriteUrl: '__SPRITE_URL__', + icons: artifact.icons, + } + const source = JSON.stringify(data, null, 2).replace('"__SPRITE_URL__"', 'spriteUrl') + return [ + header(config.generatedNotice), + "import spriteUrl from './sprite.svg'", + '', + `export const spriteManifest = ${source}`, + '', + 'export default spriteManifest', + '', + ].join('\n') +} + +function manifestDeclarations(config: ResolvedSpriteConfig): string { + return [ + header(config.generatedNotice), + '', + 'export type SpriteManifestColor = { variable: `--icon-color-${number}`; fallback: string }', + 'export type SpriteManifestIcon = {', + ' name: string', + ' id: string', + ' viewBox: string | null', + ' colors: readonly SpriteManifestColor[]', + '}', + 'export type SpriteManifest = {', + ' schemaVersion: 1', + " generator: '@gromlab/svg-sprites'", + ' name: string', + ' description?: string', + " usage: { framework: 'vue'; componentName: string }", + " mode: 'nuxt@webpack'", + " target: 'webpack'", + " format: 'stack' | 'symbol'", + ' iconCount: number', + ' spriteUrl: string', + ' icons: readonly SpriteManifestIcon[]', + '}', + '', + 'export declare const spriteManifest: SpriteManifest', + 'export default spriteManifest', + '', + ].join('\n') +} + +export function generateOutputFiles(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): GeneratedFile[] { + return [ + { path: path.posix.join(OUTPUT_DIR, 'index.js'), content: index(config) }, + { path: path.posix.join(OUTPUT_DIR, 'index.d.ts'), content: indexDeclarations(config) }, + { path: path.posix.join(OUTPUT_DIR, 'sprite.svg'), content: markedSvg(artifact.bytes, config.generatedNotice) }, + { path: path.posix.join(OUTPUT_DIR, 'icon-data.js'), content: iconData(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'icon-data.d.ts'), content: iconDeclarations(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'vue', 'vue-component.js'), content: component(config) }, + { path: path.posix.join(OUTPUT_DIR, 'vue', 'vue-component.d.ts'), content: componentDeclarations(config) }, + { path: path.posix.join(OUTPUT_DIR, 'vue', 'vue-component.module.css'), content: styles(config) }, + { path: path.posix.join(OUTPUT_DIR, 'svg-sprite.manifest.js'), content: manifest(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'svg-sprite.manifest.d.ts'), content: manifestDeclarations(config) }, + ] +} diff --git a/src/modes/preact-vite/adapter.ts b/src/modes/preact-vite/adapter.ts new file mode 100644 index 0000000..e72529f --- /dev/null +++ b/src/modes/preact-vite/adapter.ts @@ -0,0 +1,25 @@ +import { compileSpriteContent } from '../../compiler.js' +import { createCompiledSpriteArtifact } from '../../core/compiled-artifact.js' +import type { ModeAdapter } from '../../core/mode-adapter.js' +import { generateOutputFiles } from './output.js' + +export const preactViteAdapter: ModeAdapter<'preact@vite'> = { + mode: 'preact@vite', + async generate(context) { + const bytes = await compileSpriteContent(context.prepared.folder, context.config.transform, { + rootViewBox: false, + }) + const artifact = createCompiledSpriteArtifact(bytes, context.prepared.iconNames, 'stack') + + return { + files: generateOutputFiles(context.config, artifact), + paths: { + generatedDir: '.svg-sprite', + sprite: '.svg-sprite/sprite.svg', + manifest: '.svg-sprite/svg-sprite.manifest.js', + entry: '.svg-sprite/index.js', + }, + result: { target: 'vite' }, + } + }, +} diff --git a/src/modes/preact-vite/output.ts b/src/modes/preact-vite/output.ts new file mode 100644 index 0000000..3401551 --- /dev/null +++ b/src/modes/preact-vite/output.ts @@ -0,0 +1,188 @@ +import path from 'node:path' +import type { CompiledSpriteArtifact } from '../../core/compiled-artifact.js' +import { GENERATED_MARKER } from '../../core/generated-markers.js' +import type { GeneratedFile } from '../../core/mode-adapter.js' +import type { ResolvedSpriteConfig } from '../../types.js' + +const OUTPUT_DIR = '.svg-sprite' + +function pascal(value: string): string { + return value.split('-').map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join('') +} + +function camel(value: string): string { + return value.replace(/-([a-z0-9])/g, (_, char: string) => char.toUpperCase()) +} + +function header(enabled: boolean): string { + return enabled + ? `/*\n * АВТОМАТИЧЕСКИ СГЕНЕРИРОВАННЫЙ ФАЙЛ. Не редактируйте вручную.\n * ${GENERATED_MARKER}.\n */` + : `/* ${GENERATED_MARKER}. Do not edit. */` +} + +function svg(bytes: Uint8Array, enabled: boolean): string { + const notice = enabled + ? `АВТОМАТИЧЕСКИ СГЕНЕРИРОВАННЫЙ ФАЙЛ. ${GENERATED_MARKER}.` + : `${GENERATED_MARKER}. Do not edit.` + const content = new TextDecoder().decode(bytes) + return content.startsWith(')\s*/, `$1\n\n`) + : `\n${content}` +} + +function description(config: ResolvedSpriteConfig): string[] { + const text = config.description ?? `Имена иконок SVG-спрайта «${config.name}».` + return ['/**', ...text.split(/\r?\n/).map((line) => ` * ${line.replace(/\*\//g, '* /')}`), ' */'] +} + +function iconData(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const name = camel(config.name) + return [ + header(config.generatedNotice), + '', + ...description(config), + `export const ${name}IconNames = ${JSON.stringify(artifact.icons.map((icon) => icon.name), null, 2)}`, + '', + `export const ${name}IconIds = ${JSON.stringify(Object.fromEntries(artifact.icons.map((icon) => [icon.name, icon.id])), null, 2)}`, + '', + ].join('\n') +} + +function iconTypes(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const component = `${pascal(config.name)}Icon` + const names = `${camel(config.name)}IconNames` + const ids = `${camel(config.name)}IconIds` + return [ + header(config.generatedNotice), + '', + ...description(config), + `export declare const ${names}: readonly [`, + ...artifact.icons.map((icon) => ` ${JSON.stringify(icon.name)},`), + ']', + `export type ${component}Name = typeof ${names}[number]`, + `export declare const ${ids}: {`, + ...artifact.icons.map((icon) => ` readonly ${JSON.stringify(icon.name)}: ${JSON.stringify(icon.id)}`), + '}', + '', + ].join('\n') +} + +function component(config: ResolvedSpriteConfig): string { + const componentName = `${pascal(config.name)}Icon` + const ids = `${camel(config.name)}IconIds` + return [ + header(config.generatedNotice), + "import { h } from 'preact'", + "import styles from './preact-component.module.css'", + `import { ${ids} } from '../icon-data.js'`, + "import spriteUrl from '../sprite.svg?no-inline'", + '', + `/** Иконка из SVG-спрайта «${config.name}». */`, + `export const ${componentName} = (props) => {`, + ' const { icon, wrapped, class: classValue, className, ...rest } = props', + ` const href = spriteUrl + '#' + ${ids}[icon]`, + ' const classes = [wrapped ? styles.wrap : styles.root, classValue, className].filter(Boolean).join(\' \')', + '', + ' if (wrapped) {', + " return h('span', { ...rest, class: classes }, h('svg', null, h('use', { href })))", + ' }', + " return h('svg', { ...rest, class: classes }, h('use', { href }))", + '}', + '', + ].join('\n') +} + +function componentTypes(config: ResolvedSpriteConfig): string { + const component = `${pascal(config.name)}Icon` + return [ + header(config.generatedNotice), + "import type { FunctionalComponent, JSX } from 'preact'", + `import type { ${component}Name } from '../icon-data.js'`, + '', + `export type ${component}Style = JSX.CSSProperties & Partial>`, + `type ${component}BaseProps = { icon: ${component}Name }`, + `type ${component}SvgProps = ${component}BaseProps & { wrapped?: false } & Omit, 'style'> & { style?: ${component}Style }`, + `type ${component}WrappedProps = ${component}BaseProps & { wrapped: true } & Omit, 'style'> & { style?: ${component}Style }`, + `export type ${component}Props = ${component}SvgProps | ${component}WrappedProps`, + `export declare const ${component}: FunctionalComponent<${component}Props>`, + '', + ].join('\n') +} + +function index(config: ResolvedSpriteConfig): string { + return [ + header(config.generatedNotice), + `export { ${pascal(config.name)}Icon } from './preact/preact-component.js'`, + `export { ${camel(config.name)}IconNames } from './icon-data.js'`, + '', + ].join('\n') +} + +function indexTypes(config: ResolvedSpriteConfig): string { + const component = `${pascal(config.name)}Icon` + return [ + header(config.generatedNotice), + `export { ${component} } from './preact/preact-component.js'`, + `export type { ${component}Props, ${component}Style } from './preact/preact-component.js'`, + `export { ${camel(config.name)}IconNames } from './icon-data.js'`, + `export type { ${component}Name } from './icon-data.js'`, + '', + ].join('\n') +} + +function styles(config: ResolvedSpriteConfig): string { + const transition = config.transform.addTransition + ? [' transition-property: fill, stroke, color;', ' transition-duration: 0.3s;', ' transition-timing-function: ease;'] + : [] + return [header(config.generatedNotice), '', '.root {', ...transition, '}', '', '.wrap {', ' display: inline-flex;', '}', '', '.wrap svg {', ' width: 100%;', ' height: 100%;', ...transition, '}', ''].join('\n') +} + +function manifest(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const data = { + schemaVersion: 1, + generator: '@gromlab/svg-sprites', + name: config.name, + ...(config.description === undefined ? {} : { description: config.description }), + usage: { framework: 'preact', componentName: `${pascal(config.name)}Icon` }, + mode: 'preact@vite', + target: 'vite', + format: artifact.format, + iconCount: artifact.icons.length, + spriteUrl: '__SPRITE_URL__', + icons: artifact.icons, + } + const source = JSON.stringify(data, null, 2).replace('"__SPRITE_URL__"', 'spriteUrl') + return [header(config.generatedNotice), "import spriteUrl from './sprite.svg?no-inline'", '', `export const spriteManifest = ${source}`, '', 'export default spriteManifest', ''].join('\n') +} + +function manifestTypes(config: ResolvedSpriteConfig): string { + return [ + header(config.generatedNotice), + 'export type SpriteManifestColor = { variable: `--icon-color-${number}`; fallback: string }', + 'export type SpriteManifestIcon = { name: string; id: string; viewBox: string | null; colors: readonly SpriteManifestColor[] }', + 'export type SpriteManifest = {', + " schemaVersion: 1; generator: '@gromlab/svg-sprites'; name: string; description?: string", + " usage: { framework: 'preact'; componentName: string }", + " mode: 'preact@vite'; target: 'vite'; format: 'stack' | 'symbol'", + ' iconCount: number; spriteUrl: string; icons: readonly SpriteManifestIcon[]', + '}', + 'export declare const spriteManifest: SpriteManifest', + 'export default spriteManifest', + '', + ].join('\n') +} + +export function generateOutputFiles(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): GeneratedFile[] { + return [ + { path: path.posix.join(OUTPUT_DIR, 'index.js'), content: index(config) }, + { path: path.posix.join(OUTPUT_DIR, 'index.d.ts'), content: indexTypes(config) }, + { path: path.posix.join(OUTPUT_DIR, 'sprite.svg'), content: svg(artifact.bytes, config.generatedNotice) }, + { path: path.posix.join(OUTPUT_DIR, 'icon-data.js'), content: iconData(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'icon-data.d.ts'), content: iconTypes(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'preact', 'preact-component.js'), content: component(config) }, + { path: path.posix.join(OUTPUT_DIR, 'preact', 'preact-component.d.ts'), content: componentTypes(config) }, + { path: path.posix.join(OUTPUT_DIR, 'preact', 'preact-component.module.css'), content: styles(config) }, + { path: path.posix.join(OUTPUT_DIR, 'svg-sprite.manifest.js'), content: manifest(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'svg-sprite.manifest.d.ts'), content: manifestTypes(config) }, + ] +} diff --git a/src/modes/preact-webpack/adapter.ts b/src/modes/preact-webpack/adapter.ts new file mode 100644 index 0000000..79a41be --- /dev/null +++ b/src/modes/preact-webpack/adapter.ts @@ -0,0 +1,25 @@ +import { compileSpriteContent } from '../../compiler.js' +import { createCompiledSpriteArtifact } from '../../core/compiled-artifact.js' +import type { ModeAdapter } from '../../core/mode-adapter.js' +import { generateOutputFiles } from './output.js' + +export const preactWebpackAdapter: ModeAdapter<'preact@webpack'> = { + mode: 'preact@webpack', + async generate(context) { + const bytes = await compileSpriteContent(context.prepared.folder, context.config.transform, { + rootViewBox: false, + }) + const artifact = createCompiledSpriteArtifact(bytes, context.prepared.iconNames, 'stack') + + return { + files: generateOutputFiles(context.config, artifact), + paths: { + generatedDir: '.svg-sprite', + sprite: '.svg-sprite/sprite.svg', + manifest: '.svg-sprite/svg-sprite.manifest.js', + entry: '.svg-sprite/index.js', + }, + result: { target: 'webpack' }, + } + }, +} diff --git a/src/modes/preact-webpack/output.ts b/src/modes/preact-webpack/output.ts new file mode 100644 index 0000000..1996f80 --- /dev/null +++ b/src/modes/preact-webpack/output.ts @@ -0,0 +1,189 @@ +import path from 'node:path' +import type { CompiledSpriteArtifact } from '../../core/compiled-artifact.js' +import { GENERATED_MARKER } from '../../core/generated-markers.js' +import type { GeneratedFile } from '../../core/mode-adapter.js' +import type { ResolvedSpriteConfig } from '../../types.js' + +const OUTPUT_DIR = '.svg-sprite' + +function pascal(value: string): string { + return value.split('-').map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join('') +} + +function camel(value: string): string { + return value.replace(/-([a-z0-9])/g, (_, char: string) => char.toUpperCase()) +} + +function header(enabled: boolean): string { + return enabled + ? `/*\n * АВТОМАТИЧЕСКИ СГЕНЕРИРОВАННЫЙ ФАЙЛ. Не редактируйте вручную.\n * ${GENERATED_MARKER}.\n */` + : `/* ${GENERATED_MARKER}. Do not edit. */` +} + +function svg(bytes: Uint8Array, enabled: boolean): string { + const notice = enabled + ? `АВТОМАТИЧЕСКИ СГЕНЕРИРОВАННЫЙ ФАЙЛ. ${GENERATED_MARKER}.` + : `${GENERATED_MARKER}. Do not edit.` + const content = new TextDecoder().decode(bytes) + return content.startsWith(')\s*/, `$1\n\n`) + : `\n${content}` +} + +function description(config: ResolvedSpriteConfig): string[] { + const text = config.description ?? `Имена иконок SVG-спрайта «${config.name}».` + return ['/**', ...text.split(/\r?\n/).map((line) => ` * ${line.replace(/\*\//g, '* /')}`), ' */'] +} + +function iconData(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const name = camel(config.name) + return [ + header(config.generatedNotice), + '', + ...description(config), + `export const ${name}IconNames = ${JSON.stringify(artifact.icons.map((icon) => icon.name), null, 2)}`, + '', + `export const ${name}IconIds = ${JSON.stringify(Object.fromEntries(artifact.icons.map((icon) => [icon.name, icon.id])), null, 2)}`, + '', + ].join('\n') +} + +function iconTypes(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const component = `${pascal(config.name)}Icon` + const names = `${camel(config.name)}IconNames` + const ids = `${camel(config.name)}IconIds` + return [ + header(config.generatedNotice), + '', + ...description(config), + `export declare const ${names}: readonly [`, + ...artifact.icons.map((icon) => ` ${JSON.stringify(icon.name)},`), + ']', + `export type ${component}Name = typeof ${names}[number]`, + `export declare const ${ids}: {`, + ...artifact.icons.map((icon) => ` readonly ${JSON.stringify(icon.name)}: ${JSON.stringify(icon.id)}`), + '}', + '', + ].join('\n') +} + +function component(config: ResolvedSpriteConfig): string { + const componentName = `${pascal(config.name)}Icon` + const ids = `${camel(config.name)}IconIds` + return [ + header(config.generatedNotice), + "import { h } from 'preact'", + "import styles from './preact-component.module.css'", + `import { ${ids} } from '../icon-data.js'`, + '', + "const spriteUrl = new URL('../sprite.svg', import.meta.url).href", + '', + `/** Иконка из SVG-спрайта «${config.name}». */`, + `export const ${componentName} = (props) => {`, + ' const { icon, wrapped, class: classValue, className, ...rest } = props', + ` const href = spriteUrl + '#' + ${ids}[icon]`, + ' const classes = [wrapped ? styles.wrap : styles.root, classValue, className].filter(Boolean).join(\' \')', + '', + ' if (wrapped) {', + " return h('span', { ...rest, class: classes }, h('svg', null, h('use', { href })))", + ' }', + " return h('svg', { ...rest, class: classes }, h('use', { href }))", + '}', + '', + ].join('\n') +} + +function componentTypes(config: ResolvedSpriteConfig): string { + const component = `${pascal(config.name)}Icon` + return [ + header(config.generatedNotice), + "import type { FunctionalComponent, JSX } from 'preact'", + `import type { ${component}Name } from '../icon-data.js'`, + '', + `export type ${component}Style = JSX.CSSProperties & Partial>`, + `type ${component}BaseProps = { icon: ${component}Name }`, + `type ${component}SvgProps = ${component}BaseProps & { wrapped?: false } & Omit, 'style'> & { style?: ${component}Style }`, + `type ${component}WrappedProps = ${component}BaseProps & { wrapped: true } & Omit, 'style'> & { style?: ${component}Style }`, + `export type ${component}Props = ${component}SvgProps | ${component}WrappedProps`, + `export declare const ${component}: FunctionalComponent<${component}Props>`, + '', + ].join('\n') +} + +function index(config: ResolvedSpriteConfig): string { + return [ + header(config.generatedNotice), + `export { ${pascal(config.name)}Icon } from './preact/preact-component.js'`, + `export { ${camel(config.name)}IconNames } from './icon-data.js'`, + '', + ].join('\n') +} + +function indexTypes(config: ResolvedSpriteConfig): string { + const component = `${pascal(config.name)}Icon` + return [ + header(config.generatedNotice), + `export { ${component} } from './preact/preact-component.js'`, + `export type { ${component}Props, ${component}Style } from './preact/preact-component.js'`, + `export { ${camel(config.name)}IconNames } from './icon-data.js'`, + `export type { ${component}Name } from './icon-data.js'`, + '', + ].join('\n') +} + +function styles(config: ResolvedSpriteConfig): string { + const transition = config.transform.addTransition + ? [' transition-property: fill, stroke, color;', ' transition-duration: 0.3s;', ' transition-timing-function: ease;'] + : [] + return [header(config.generatedNotice), '', '.root {', ...transition, '}', '', '.wrap {', ' display: inline-flex;', '}', '', '.wrap svg {', ' width: 100%;', ' height: 100%;', ...transition, '}', ''].join('\n') +} + +function manifest(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const data = { + schemaVersion: 1, + generator: '@gromlab/svg-sprites', + name: config.name, + ...(config.description === undefined ? {} : { description: config.description }), + usage: { framework: 'preact', componentName: `${pascal(config.name)}Icon` }, + mode: 'preact@webpack', + target: 'webpack', + format: artifact.format, + iconCount: artifact.icons.length, + spriteUrl: '__SPRITE_URL__', + icons: artifact.icons, + } + const source = JSON.stringify(data, null, 2).replace('"__SPRITE_URL__"', 'spriteUrl') + return [header(config.generatedNotice), "const spriteUrl = new URL('./sprite.svg', import.meta.url).href", '', `export const spriteManifest = ${source}`, '', 'export default spriteManifest', ''].join('\n') +} + +function manifestTypes(config: ResolvedSpriteConfig): string { + return [ + header(config.generatedNotice), + 'export type SpriteManifestColor = { variable: `--icon-color-${number}`; fallback: string }', + 'export type SpriteManifestIcon = { name: string; id: string; viewBox: string | null; colors: readonly SpriteManifestColor[] }', + 'export type SpriteManifest = {', + " schemaVersion: 1; generator: '@gromlab/svg-sprites'; name: string; description?: string", + " usage: { framework: 'preact'; componentName: string }", + " mode: 'preact@webpack'; target: 'webpack'; format: 'stack' | 'symbol'", + ' iconCount: number; spriteUrl: string; icons: readonly SpriteManifestIcon[]', + '}', + 'export declare const spriteManifest: SpriteManifest', + 'export default spriteManifest', + '', + ].join('\n') +} + +export function generateOutputFiles(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): GeneratedFile[] { + return [ + { path: path.posix.join(OUTPUT_DIR, 'index.js'), content: index(config) }, + { path: path.posix.join(OUTPUT_DIR, 'index.d.ts'), content: indexTypes(config) }, + { path: path.posix.join(OUTPUT_DIR, 'sprite.svg'), content: svg(artifact.bytes, config.generatedNotice) }, + { path: path.posix.join(OUTPUT_DIR, 'icon-data.js'), content: iconData(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'icon-data.d.ts'), content: iconTypes(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'preact', 'preact-component.js'), content: component(config) }, + { path: path.posix.join(OUTPUT_DIR, 'preact', 'preact-component.d.ts'), content: componentTypes(config) }, + { path: path.posix.join(OUTPUT_DIR, 'preact', 'preact-component.module.css'), content: styles(config) }, + { path: path.posix.join(OUTPUT_DIR, 'svg-sprite.manifest.js'), content: manifest(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'svg-sprite.manifest.d.ts'), content: manifestTypes(config) }, + ] +} diff --git a/src/modes/qwik-vite/adapter.ts b/src/modes/qwik-vite/adapter.ts new file mode 100644 index 0000000..0fd92a7 --- /dev/null +++ b/src/modes/qwik-vite/adapter.ts @@ -0,0 +1,25 @@ +import { compileSpriteContent } from '../../compiler.js' +import { createCompiledSpriteArtifact } from '../../core/compiled-artifact.js' +import type { ModeAdapter } from '../../core/mode-adapter.js' +import { generateOutputFiles } from './output.js' + +export const qwikViteAdapter: ModeAdapter<'qwik@vite'> = { + mode: 'qwik@vite', + async generate(context) { + const bytes = await compileSpriteContent(context.prepared.folder, context.config.transform, { + rootViewBox: false, + }) + const artifact = createCompiledSpriteArtifact(bytes, context.prepared.iconNames, 'stack') + + return { + files: generateOutputFiles(context.config, artifact), + paths: { + generatedDir: '.svg-sprite', + sprite: '.svg-sprite/sprite.svg', + manifest: '.svg-sprite/svg-sprite.manifest.js', + entry: '.svg-sprite/index.js', + }, + result: { target: 'vite' }, + } + }, +} diff --git a/src/modes/qwik-vite/output.ts b/src/modes/qwik-vite/output.ts new file mode 100644 index 0000000..c97f1b8 --- /dev/null +++ b/src/modes/qwik-vite/output.ts @@ -0,0 +1,192 @@ +import path from 'node:path' +import type { CompiledSpriteArtifact } from '../../core/compiled-artifact.js' +import { GENERATED_MARKER } from '../../core/generated-markers.js' +import type { GeneratedFile } from '../../core/mode-adapter.js' +import type { ResolvedSpriteConfig } from '../../types.js' + +const OUTPUT_DIR = '.svg-sprite' + +function pascal(value: string): string { + return value.split('-').map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join('') +} + +function camel(value: string): string { + return value.replace(/-([a-z0-9])/g, (_, char: string) => char.toUpperCase()) +} + +function header(enabled: boolean): string { + return enabled + ? `/*\n * АВТОМАТИЧЕСКИ СГЕНЕРИРОВАННЫЙ ФАЙЛ. Не редактируйте вручную.\n * ${GENERATED_MARKER}.\n */` + : `/* ${GENERATED_MARKER}. Do not edit. */` +} + +function svg(bytes: Uint8Array, enabled: boolean): string { + const notice = enabled + ? `АВТОМАТИЧЕСКИ СГЕНЕРИРОВАННЫЙ ФАЙЛ. ${GENERATED_MARKER}.` + : `${GENERATED_MARKER}. Do not edit.` + const content = new TextDecoder().decode(bytes) + return content.startsWith(')\s*/, `$1\n\n`) + : `\n${content}` +} + +function description(config: ResolvedSpriteConfig): string[] { + const text = config.description ?? `Имена иконок SVG-спрайта «${config.name}».` + return ['/**', ...text.split(/\r?\n/).map((line) => ` * ${line.replace(/\*\//g, '* /')}`), ' */'] +} + +function iconData(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const name = camel(config.name) + return [ + header(config.generatedNotice), + '', + ...description(config), + `export const ${name}IconNames = ${JSON.stringify(artifact.icons.map((icon) => icon.name), null, 2)}`, + '', + `export const ${name}IconIds = ${JSON.stringify(Object.fromEntries(artifact.icons.map((icon) => [icon.name, icon.id])), null, 2)}`, + '', + ].join('\n') +} + +function iconTypes(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const component = `${pascal(config.name)}Icon` + const names = `${camel(config.name)}IconNames` + const ids = `${camel(config.name)}IconIds` + return [ + header(config.generatedNotice), + '', + ...description(config), + `export declare const ${names}: readonly [`, + ...artifact.icons.map((icon) => ` ${JSON.stringify(icon.name)},`), + ']', + `export type ${component}Name = typeof ${names}[number]`, + `export declare const ${ids}: {`, + ...artifact.icons.map((icon) => ` readonly ${JSON.stringify(icon.name)}: ${JSON.stringify(icon.id)}`), + '}', + '', + ].join('\n') +} + +function component(config: ResolvedSpriteConfig): string { + const componentName = `${pascal(config.name)}Icon` + const ids = `${camel(config.name)}IconIds` + return [ + header(config.generatedNotice), + "import { component$ } from '@builder.io/qwik'", + "import styles from './qwik-component.module.css'", + `import { ${ids} } from '../icon-data.js'`, + "import spriteUrl from '../sprite.svg?no-inline'", + '', + `/** SSR-safe иконка из SVG-спрайта «${config.name}». */`, + `export const ${componentName} = component$((props) => {`, + ' const { icon, wrapped, class: classValue, ...rest } = props', + ` const href = spriteUrl + '#' + ${ids}[icon]`, + ' const classes = [wrapped ? styles.wrap : styles.root, classValue]', + '', + ' if (wrapped) {', + ' return (', + ' ', + ' ', + ' ', + ' )', + ' }', + ' return ', + '})', + '', + ].join('\n') +} + +function componentTypes(config: ResolvedSpriteConfig): string { + const component = `${pascal(config.name)}Icon` + return [ + header(config.generatedNotice), + "import type { Component, CSSProperties, QwikIntrinsicElements } from '@builder.io/qwik'", + `import type { ${component}Name } from '../icon-data.js'`, + '', + `export type ${component}Style = CSSProperties & Partial>`, + `type ${component}BaseProps = { icon: ${component}Name }`, + `type ${component}SvgProps = ${component}BaseProps & { wrapped?: false } & Omit & { style?: ${component}Style }`, + `type ${component}WrappedProps = ${component}BaseProps & { wrapped: true } & Omit & { style?: ${component}Style }`, + `export type ${component}Props = ${component}SvgProps | ${component}WrappedProps`, + `export declare const ${component}: Component<${component}Props>`, + '', + ].join('\n') +} + +function index(config: ResolvedSpriteConfig): string { + return [ + header(config.generatedNotice), + `export { ${pascal(config.name)}Icon } from './qwik/qwik-component.jsx'`, + `export { ${camel(config.name)}IconNames } from './icon-data.js'`, + '', + ].join('\n') +} + +function indexTypes(config: ResolvedSpriteConfig): string { + const component = `${pascal(config.name)}Icon` + return [ + header(config.generatedNotice), + `export { ${component} } from './qwik/qwik-component.jsx'`, + `export type { ${component}Props, ${component}Style } from './qwik/qwik-component.jsx'`, + `export { ${camel(config.name)}IconNames } from './icon-data.js'`, + `export type { ${component}Name } from './icon-data.js'`, + '', + ].join('\n') +} + +function styles(config: ResolvedSpriteConfig): string { + const transition = config.transform.addTransition + ? [' transition-property: fill, stroke, color;', ' transition-duration: 0.3s;', ' transition-timing-function: ease;'] + : [] + return [header(config.generatedNotice), '', '.root {', ...transition, '}', '', '.wrap {', ' display: inline-flex;', '}', '', '.wrap svg {', ' width: 100%;', ' height: 100%;', ...transition, '}', ''].join('\n') +} + +function manifest(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const data = { + schemaVersion: 1, + generator: '@gromlab/svg-sprites', + name: config.name, + ...(config.description === undefined ? {} : { description: config.description }), + usage: { framework: 'qwik', componentName: `${pascal(config.name)}Icon`, ssr: true }, + mode: 'qwik@vite', + target: 'vite', + format: artifact.format, + iconCount: artifact.icons.length, + spriteUrl: '__SPRITE_URL__', + icons: artifact.icons, + } + const source = JSON.stringify(data, null, 2).replace('"__SPRITE_URL__"', 'spriteUrl') + return [header(config.generatedNotice), "import spriteUrl from './sprite.svg?no-inline'", '', `export const spriteManifest = ${source}`, '', 'export default spriteManifest', ''].join('\n') +} + +function manifestTypes(config: ResolvedSpriteConfig): string { + return [ + header(config.generatedNotice), + 'export type SpriteManifestColor = { variable: `--icon-color-${number}`; fallback: string }', + 'export type SpriteManifestIcon = { name: string; id: string; viewBox: string | null; colors: readonly SpriteManifestColor[] }', + 'export type SpriteManifest = {', + " schemaVersion: 1; generator: '@gromlab/svg-sprites'; name: string; description?: string", + " usage: { framework: 'qwik'; componentName: string; ssr: true }", + " mode: 'qwik@vite'; target: 'vite'; format: 'stack' | 'symbol'", + ' iconCount: number; spriteUrl: string; icons: readonly SpriteManifestIcon[]', + '}', + 'export declare const spriteManifest: SpriteManifest', + 'export default spriteManifest', + '', + ].join('\n') +} + +export function generateOutputFiles(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): GeneratedFile[] { + return [ + { path: path.posix.join(OUTPUT_DIR, 'index.js'), content: index(config) }, + { path: path.posix.join(OUTPUT_DIR, 'index.d.ts'), content: indexTypes(config) }, + { path: path.posix.join(OUTPUT_DIR, 'sprite.svg'), content: svg(artifact.bytes, config.generatedNotice) }, + { path: path.posix.join(OUTPUT_DIR, 'icon-data.js'), content: iconData(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'icon-data.d.ts'), content: iconTypes(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'qwik', 'qwik-component.jsx'), content: component(config) }, + { path: path.posix.join(OUTPUT_DIR, 'qwik', 'qwik-component.d.ts'), content: componentTypes(config) }, + { path: path.posix.join(OUTPUT_DIR, 'qwik', 'qwik-component.module.css'), content: styles(config) }, + { path: path.posix.join(OUTPUT_DIR, 'svg-sprite.manifest.js'), content: manifest(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'svg-sprite.manifest.d.ts'), content: manifestTypes(config) }, + ] +} diff --git a/src/modes/solid-start-vite/adapter.ts b/src/modes/solid-start-vite/adapter.ts new file mode 100644 index 0000000..4f86e88 --- /dev/null +++ b/src/modes/solid-start-vite/adapter.ts @@ -0,0 +1,25 @@ +import { compileSpriteContent } from '../../compiler.js' +import { createCompiledSpriteArtifact } from '../../core/compiled-artifact.js' +import type { ModeAdapter } from '../../core/mode-adapter.js' +import { generateOutputFiles } from './output.js' + +export const solidStartViteAdapter: ModeAdapter<'solid-start@vite'> = { + mode: 'solid-start@vite', + async generate(context) { + const bytes = await compileSpriteContent(context.prepared.folder, context.config.transform, { + rootViewBox: false, + }) + const artifact = createCompiledSpriteArtifact(bytes, context.prepared.iconNames, 'stack') + + return { + files: generateOutputFiles(context.config, artifact), + paths: { + generatedDir: '.svg-sprite', + sprite: '.svg-sprite/sprite.svg', + manifest: '.svg-sprite/svg-sprite.manifest.js', + entry: '.svg-sprite/index.js', + }, + result: { target: 'vite' }, + } + }, +} diff --git a/src/modes/solid-start-vite/output.ts b/src/modes/solid-start-vite/output.ts new file mode 100644 index 0000000..bda0a44 --- /dev/null +++ b/src/modes/solid-start-vite/output.ts @@ -0,0 +1,197 @@ +import path from 'node:path' +import type { CompiledSpriteArtifact } from '../../core/compiled-artifact.js' +import { GENERATED_MARKER } from '../../core/generated-markers.js' +import type { GeneratedFile } from '../../core/mode-adapter.js' +import type { ResolvedSpriteConfig } from '../../types.js' + +const OUTPUT_DIR = '.svg-sprite' + +function pascal(value: string): string { + return value.split('-').map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join('') +} + +function camel(value: string): string { + return value.replace(/-([a-z0-9])/g, (_, char: string) => char.toUpperCase()) +} + +function header(enabled: boolean): string { + return enabled + ? `/*\n * АВТОМАТИЧЕСКИ СГЕНЕРИРОВАННЫЙ ФАЙЛ. Не редактируйте вручную.\n * ${GENERATED_MARKER}.\n */` + : `/* ${GENERATED_MARKER}. Do not edit. */` +} + +function svg(bytes: Uint8Array, enabled: boolean): string { + const notice = enabled + ? `АВТОМАТИЧЕСКИ СГЕНЕРИРОВАННЫЙ ФАЙЛ. ${GENERATED_MARKER}.` + : `${GENERATED_MARKER}. Do not edit.` + const content = new TextDecoder().decode(bytes) + return content.startsWith(')\s*/, `$1\n\n`) + : `\n${content}` +} + +function description(config: ResolvedSpriteConfig): string[] { + const text = config.description ?? `Имена иконок SVG-спрайта «${config.name}».` + return ['/**', ...text.split(/\r?\n/).map((line) => ` * ${line.replace(/\*\//g, '* /')}`), ' */'] +} + +function iconData(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const name = camel(config.name) + return [ + header(config.generatedNotice), + '', + ...description(config), + `export const ${name}IconNames = ${JSON.stringify(artifact.icons.map((icon) => icon.name), null, 2)}`, + '', + `export const ${name}IconIds = ${JSON.stringify(Object.fromEntries(artifact.icons.map((icon) => [icon.name, icon.id])), null, 2)}`, + '', + ].join('\n') +} + +function iconTypes(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const component = `${pascal(config.name)}Icon` + const names = `${camel(config.name)}IconNames` + const ids = `${camel(config.name)}IconIds` + return [ + header(config.generatedNotice), + '', + ...description(config), + `export declare const ${names}: readonly [`, + ...artifact.icons.map((icon) => ` ${JSON.stringify(icon.name)},`), + ']', + `export type ${component}Name = typeof ${names}[number]`, + `export declare const ${ids}: {`, + ...artifact.icons.map((icon) => ` readonly ${JSON.stringify(icon.name)}: ${JSON.stringify(icon.id)}`), + '}', + '', + ].join('\n') +} + +function component(config: ResolvedSpriteConfig): string { + const componentName = `${pascal(config.name)}Icon` + const ids = `${camel(config.name)}IconIds` + return [ + header(config.generatedNotice), + "import { Show, splitProps } from 'solid-js'", + "import styles from './solid-component.module.css'", + `import { ${ids} } from '../icon-data.js'`, + "import spriteUrl from '../sprite.svg?no-inline'", + '', + `/** SSR-safe иконка из SVG-спрайта «${config.name}». */`, + `export const ${componentName} = (props) => {`, + " const [local, rest] = splitProps(props, ['icon', 'wrapped', 'class'])", + ` const href = () => spriteUrl + '#' + ${ids}[local.icon]`, + '', + ' return (', + ' ', + ' ', + ' ', + ' }', + ' >', + ' ', + ' ', + ' ', + ' ', + ' )', + '}', + '', + ].join('\n') +} + +function componentTypes(config: ResolvedSpriteConfig): string { + const component = `${pascal(config.name)}Icon` + return [ + header(config.generatedNotice), + "import type { Component, JSX } from 'solid-js'", + `import type { ${component}Name } from '../icon-data.js'`, + '', + `export type ${component}Style = JSX.CSSProperties & Partial>`, + `type ${component}BaseProps = { icon: ${component}Name }`, + `type ${component}SvgProps = ${component}BaseProps & { wrapped?: false } & Omit, 'style'> & { style?: ${component}Style }`, + `type ${component}WrappedProps = ${component}BaseProps & { wrapped: true } & Omit, 'style'> & { style?: ${component}Style }`, + `export type ${component}Props = ${component}SvgProps | ${component}WrappedProps`, + `export declare const ${component}: Component<${component}Props>`, + '', + ].join('\n') +} + +function index(config: ResolvedSpriteConfig): string { + return [ + header(config.generatedNotice), + `export { ${pascal(config.name)}Icon } from './solid/solid-component.jsx'`, + `export { ${camel(config.name)}IconNames } from './icon-data.js'`, + '', + ].join('\n') +} + +function indexTypes(config: ResolvedSpriteConfig): string { + const component = `${pascal(config.name)}Icon` + return [ + header(config.generatedNotice), + `export { ${component} } from './solid/solid-component.jsx'`, + `export type { ${component}Props, ${component}Style } from './solid/solid-component.jsx'`, + `export { ${camel(config.name)}IconNames } from './icon-data.js'`, + `export type { ${component}Name } from './icon-data.js'`, + '', + ].join('\n') +} + +function styles(config: ResolvedSpriteConfig): string { + const transition = config.transform.addTransition + ? [' transition-property: fill, stroke, color;', ' transition-duration: 0.3s;', ' transition-timing-function: ease;'] + : [] + return [header(config.generatedNotice), '', '.root {', ...transition, '}', '', '.wrap {', ' display: inline-flex;', '}', '', '.wrap svg {', ' width: 100%;', ' height: 100%;', ...transition, '}', ''].join('\n') +} + +function manifest(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const data = { + schemaVersion: 1, + generator: '@gromlab/svg-sprites', + name: config.name, + ...(config.description === undefined ? {} : { description: config.description }), + usage: { framework: 'solid-start', componentName: `${pascal(config.name)}Icon`, ssr: true }, + mode: 'solid-start@vite', + target: 'vite', + format: artifact.format, + iconCount: artifact.icons.length, + spriteUrl: '__SPRITE_URL__', + icons: artifact.icons, + } + const source = JSON.stringify(data, null, 2).replace('"__SPRITE_URL__"', 'spriteUrl') + return [header(config.generatedNotice), "import spriteUrl from './sprite.svg?no-inline'", '', `export const spriteManifest = ${source}`, '', 'export default spriteManifest', ''].join('\n') +} + +function manifestTypes(config: ResolvedSpriteConfig): string { + return [ + header(config.generatedNotice), + 'export type SpriteManifestColor = { variable: `--icon-color-${number}`; fallback: string }', + 'export type SpriteManifestIcon = { name: string; id: string; viewBox: string | null; colors: readonly SpriteManifestColor[] }', + 'export type SpriteManifest = {', + " schemaVersion: 1; generator: '@gromlab/svg-sprites'; name: string; description?: string", + " usage: { framework: 'solid-start'; componentName: string; ssr: true }", + " mode: 'solid-start@vite'; target: 'vite'; format: 'stack' | 'symbol'", + ' iconCount: number; spriteUrl: string; icons: readonly SpriteManifestIcon[]', + '}', + 'export declare const spriteManifest: SpriteManifest', + 'export default spriteManifest', + '', + ].join('\n') +} + +export function generateOutputFiles(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): GeneratedFile[] { + return [ + { path: path.posix.join(OUTPUT_DIR, 'index.js'), content: index(config) }, + { path: path.posix.join(OUTPUT_DIR, 'index.d.ts'), content: indexTypes(config) }, + { path: path.posix.join(OUTPUT_DIR, 'sprite.svg'), content: svg(artifact.bytes, config.generatedNotice) }, + { path: path.posix.join(OUTPUT_DIR, 'icon-data.js'), content: iconData(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'icon-data.d.ts'), content: iconTypes(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'solid', 'solid-component.jsx'), content: component(config) }, + { path: path.posix.join(OUTPUT_DIR, 'solid', 'solid-component.d.ts'), content: componentTypes(config) }, + { path: path.posix.join(OUTPUT_DIR, 'solid', 'solid-component.module.css'), content: styles(config) }, + { path: path.posix.join(OUTPUT_DIR, 'svg-sprite.manifest.js'), content: manifest(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'svg-sprite.manifest.d.ts'), content: manifestTypes(config) }, + ] +} diff --git a/src/modes/solid-vite/adapter.ts b/src/modes/solid-vite/adapter.ts new file mode 100644 index 0000000..e8dd34e --- /dev/null +++ b/src/modes/solid-vite/adapter.ts @@ -0,0 +1,25 @@ +import { compileSpriteContent } from '../../compiler.js' +import { createCompiledSpriteArtifact } from '../../core/compiled-artifact.js' +import type { ModeAdapter } from '../../core/mode-adapter.js' +import { generateOutputFiles } from './output.js' + +export const solidViteAdapter: ModeAdapter<'solid@vite'> = { + mode: 'solid@vite', + async generate(context) { + const bytes = await compileSpriteContent(context.prepared.folder, context.config.transform, { + rootViewBox: false, + }) + const artifact = createCompiledSpriteArtifact(bytes, context.prepared.iconNames, 'stack') + + return { + files: generateOutputFiles(context.config, artifact), + paths: { + generatedDir: '.svg-sprite', + sprite: '.svg-sprite/sprite.svg', + manifest: '.svg-sprite/svg-sprite.manifest.js', + entry: '.svg-sprite/index.js', + }, + result: { target: 'vite' }, + } + }, +} diff --git a/src/modes/solid-vite/output.ts b/src/modes/solid-vite/output.ts new file mode 100644 index 0000000..ee7e4de --- /dev/null +++ b/src/modes/solid-vite/output.ts @@ -0,0 +1,197 @@ +import path from 'node:path' +import type { CompiledSpriteArtifact } from '../../core/compiled-artifact.js' +import { GENERATED_MARKER } from '../../core/generated-markers.js' +import type { GeneratedFile } from '../../core/mode-adapter.js' +import type { ResolvedSpriteConfig } from '../../types.js' + +const OUTPUT_DIR = '.svg-sprite' + +function pascal(value: string): string { + return value.split('-').map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join('') +} + +function camel(value: string): string { + return value.replace(/-([a-z0-9])/g, (_, char: string) => char.toUpperCase()) +} + +function header(enabled: boolean): string { + return enabled + ? `/*\n * АВТОМАТИЧЕСКИ СГЕНЕРИРОВАННЫЙ ФАЙЛ. Не редактируйте вручную.\n * ${GENERATED_MARKER}.\n */` + : `/* ${GENERATED_MARKER}. Do not edit. */` +} + +function svg(bytes: Uint8Array, enabled: boolean): string { + const notice = enabled + ? `АВТОМАТИЧЕСКИ СГЕНЕРИРОВАННЫЙ ФАЙЛ. ${GENERATED_MARKER}.` + : `${GENERATED_MARKER}. Do not edit.` + const content = new TextDecoder().decode(bytes) + return content.startsWith(')\s*/, `$1\n\n`) + : `\n${content}` +} + +function description(config: ResolvedSpriteConfig): string[] { + const text = config.description ?? `Имена иконок SVG-спрайта «${config.name}».` + return ['/**', ...text.split(/\r?\n/).map((line) => ` * ${line.replace(/\*\//g, '* /')}`), ' */'] +} + +function iconData(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const name = camel(config.name) + return [ + header(config.generatedNotice), + '', + ...description(config), + `export const ${name}IconNames = ${JSON.stringify(artifact.icons.map((icon) => icon.name), null, 2)}`, + '', + `export const ${name}IconIds = ${JSON.stringify(Object.fromEntries(artifact.icons.map((icon) => [icon.name, icon.id])), null, 2)}`, + '', + ].join('\n') +} + +function iconTypes(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const component = `${pascal(config.name)}Icon` + const names = `${camel(config.name)}IconNames` + const ids = `${camel(config.name)}IconIds` + return [ + header(config.generatedNotice), + '', + ...description(config), + `export declare const ${names}: readonly [`, + ...artifact.icons.map((icon) => ` ${JSON.stringify(icon.name)},`), + ']', + `export type ${component}Name = typeof ${names}[number]`, + `export declare const ${ids}: {`, + ...artifact.icons.map((icon) => ` readonly ${JSON.stringify(icon.name)}: ${JSON.stringify(icon.id)}`), + '}', + '', + ].join('\n') +} + +function component(config: ResolvedSpriteConfig): string { + const componentName = `${pascal(config.name)}Icon` + const ids = `${camel(config.name)}IconIds` + return [ + header(config.generatedNotice), + "import { Show, splitProps } from 'solid-js'", + "import styles from './solid-component.module.css'", + `import { ${ids} } from '../icon-data.js'`, + "import spriteUrl from '../sprite.svg?no-inline'", + '', + `/** Иконка из SVG-спрайта «${config.name}». */`, + `export const ${componentName} = (props) => {`, + " const [local, rest] = splitProps(props, ['icon', 'wrapped', 'class'])", + ` const href = () => spriteUrl + '#' + ${ids}[local.icon]`, + '', + ' return (', + ' ', + ' ', + ' ', + ' }', + ' >', + ' ', + ' ', + ' ', + ' ', + ' )', + '}', + '', + ].join('\n') +} + +function componentTypes(config: ResolvedSpriteConfig): string { + const component = `${pascal(config.name)}Icon` + return [ + header(config.generatedNotice), + "import type { Component, JSX } from 'solid-js'", + `import type { ${component}Name } from '../icon-data.js'`, + '', + `export type ${component}Style = JSX.CSSProperties & Partial>`, + `type ${component}BaseProps = { icon: ${component}Name }`, + `type ${component}SvgProps = ${component}BaseProps & { wrapped?: false } & Omit, 'style'> & { style?: ${component}Style }`, + `type ${component}WrappedProps = ${component}BaseProps & { wrapped: true } & Omit, 'style'> & { style?: ${component}Style }`, + `export type ${component}Props = ${component}SvgProps | ${component}WrappedProps`, + `export declare const ${component}: Component<${component}Props>`, + '', + ].join('\n') +} + +function index(config: ResolvedSpriteConfig): string { + return [ + header(config.generatedNotice), + `export { ${pascal(config.name)}Icon } from './solid/solid-component.jsx'`, + `export { ${camel(config.name)}IconNames } from './icon-data.js'`, + '', + ].join('\n') +} + +function indexTypes(config: ResolvedSpriteConfig): string { + const component = `${pascal(config.name)}Icon` + return [ + header(config.generatedNotice), + `export { ${component} } from './solid/solid-component.jsx'`, + `export type { ${component}Props, ${component}Style } from './solid/solid-component.jsx'`, + `export { ${camel(config.name)}IconNames } from './icon-data.js'`, + `export type { ${component}Name } from './icon-data.js'`, + '', + ].join('\n') +} + +function styles(config: ResolvedSpriteConfig): string { + const transition = config.transform.addTransition + ? [' transition-property: fill, stroke, color;', ' transition-duration: 0.3s;', ' transition-timing-function: ease;'] + : [] + return [header(config.generatedNotice), '', '.root {', ...transition, '}', '', '.wrap {', ' display: inline-flex;', '}', '', '.wrap svg {', ' width: 100%;', ' height: 100%;', ...transition, '}', ''].join('\n') +} + +function manifest(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const data = { + schemaVersion: 1, + generator: '@gromlab/svg-sprites', + name: config.name, + ...(config.description === undefined ? {} : { description: config.description }), + usage: { framework: 'solid', componentName: `${pascal(config.name)}Icon` }, + mode: 'solid@vite', + target: 'vite', + format: artifact.format, + iconCount: artifact.icons.length, + spriteUrl: '__SPRITE_URL__', + icons: artifact.icons, + } + const source = JSON.stringify(data, null, 2).replace('"__SPRITE_URL__"', 'spriteUrl') + return [header(config.generatedNotice), "import spriteUrl from './sprite.svg?no-inline'", '', `export const spriteManifest = ${source}`, '', 'export default spriteManifest', ''].join('\n') +} + +function manifestTypes(config: ResolvedSpriteConfig): string { + return [ + header(config.generatedNotice), + 'export type SpriteManifestColor = { variable: `--icon-color-${number}`; fallback: string }', + 'export type SpriteManifestIcon = { name: string; id: string; viewBox: string | null; colors: readonly SpriteManifestColor[] }', + 'export type SpriteManifest = {', + " schemaVersion: 1; generator: '@gromlab/svg-sprites'; name: string; description?: string", + " usage: { framework: 'solid'; componentName: string }", + " mode: 'solid@vite'; target: 'vite'; format: 'stack' | 'symbol'", + ' iconCount: number; spriteUrl: string; icons: readonly SpriteManifestIcon[]', + '}', + 'export declare const spriteManifest: SpriteManifest', + 'export default spriteManifest', + '', + ].join('\n') +} + +export function generateOutputFiles(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): GeneratedFile[] { + return [ + { path: path.posix.join(OUTPUT_DIR, 'index.js'), content: index(config) }, + { path: path.posix.join(OUTPUT_DIR, 'index.d.ts'), content: indexTypes(config) }, + { path: path.posix.join(OUTPUT_DIR, 'sprite.svg'), content: svg(artifact.bytes, config.generatedNotice) }, + { path: path.posix.join(OUTPUT_DIR, 'icon-data.js'), content: iconData(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'icon-data.d.ts'), content: iconTypes(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'solid', 'solid-component.jsx'), content: component(config) }, + { path: path.posix.join(OUTPUT_DIR, 'solid', 'solid-component.d.ts'), content: componentTypes(config) }, + { path: path.posix.join(OUTPUT_DIR, 'solid', 'solid-component.module.css'), content: styles(config) }, + { path: path.posix.join(OUTPUT_DIR, 'svg-sprite.manifest.js'), content: manifest(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'svg-sprite.manifest.d.ts'), content: manifestTypes(config) }, + ] +} diff --git a/src/modes/solid-webpack/adapter.ts b/src/modes/solid-webpack/adapter.ts new file mode 100644 index 0000000..5165482 --- /dev/null +++ b/src/modes/solid-webpack/adapter.ts @@ -0,0 +1,25 @@ +import { compileSpriteContent } from '../../compiler.js' +import { createCompiledSpriteArtifact } from '../../core/compiled-artifact.js' +import type { ModeAdapter } from '../../core/mode-adapter.js' +import { generateOutputFiles } from './output.js' + +export const solidWebpackAdapter: ModeAdapter<'solid@webpack'> = { + mode: 'solid@webpack', + async generate(context) { + const bytes = await compileSpriteContent(context.prepared.folder, context.config.transform, { + rootViewBox: false, + }) + const artifact = createCompiledSpriteArtifact(bytes, context.prepared.iconNames, 'stack') + + return { + files: generateOutputFiles(context.config, artifact), + paths: { + generatedDir: '.svg-sprite', + sprite: '.svg-sprite/sprite.svg', + manifest: '.svg-sprite/svg-sprite.manifest.js', + entry: '.svg-sprite/index.js', + }, + result: { target: 'webpack' }, + } + }, +} diff --git a/src/modes/solid-webpack/output.ts b/src/modes/solid-webpack/output.ts new file mode 100644 index 0000000..be1b1c4 --- /dev/null +++ b/src/modes/solid-webpack/output.ts @@ -0,0 +1,198 @@ +import path from 'node:path' +import type { CompiledSpriteArtifact } from '../../core/compiled-artifact.js' +import { GENERATED_MARKER } from '../../core/generated-markers.js' +import type { GeneratedFile } from '../../core/mode-adapter.js' +import type { ResolvedSpriteConfig } from '../../types.js' + +const OUTPUT_DIR = '.svg-sprite' + +function pascal(value: string): string { + return value.split('-').map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join('') +} + +function camel(value: string): string { + return value.replace(/-([a-z0-9])/g, (_, char: string) => char.toUpperCase()) +} + +function header(enabled: boolean): string { + return enabled + ? `/*\n * АВТОМАТИЧЕСКИ СГЕНЕРИРОВАННЫЙ ФАЙЛ. Не редактируйте вручную.\n * ${GENERATED_MARKER}.\n */` + : `/* ${GENERATED_MARKER}. Do not edit. */` +} + +function svg(bytes: Uint8Array, enabled: boolean): string { + const notice = enabled + ? `АВТОМАТИЧЕСКИ СГЕНЕРИРОВАННЫЙ ФАЙЛ. ${GENERATED_MARKER}.` + : `${GENERATED_MARKER}. Do not edit.` + const content = new TextDecoder().decode(bytes) + return content.startsWith(')\s*/, `$1\n\n`) + : `\n${content}` +} + +function description(config: ResolvedSpriteConfig): string[] { + const text = config.description ?? `Имена иконок SVG-спрайта «${config.name}».` + return ['/**', ...text.split(/\r?\n/).map((line) => ` * ${line.replace(/\*\//g, '* /')}`), ' */'] +} + +function iconData(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const name = camel(config.name) + return [ + header(config.generatedNotice), + '', + ...description(config), + `export const ${name}IconNames = ${JSON.stringify(artifact.icons.map((icon) => icon.name), null, 2)}`, + '', + `export const ${name}IconIds = ${JSON.stringify(Object.fromEntries(artifact.icons.map((icon) => [icon.name, icon.id])), null, 2)}`, + '', + ].join('\n') +} + +function iconTypes(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const component = `${pascal(config.name)}Icon` + const names = `${camel(config.name)}IconNames` + const ids = `${camel(config.name)}IconIds` + return [ + header(config.generatedNotice), + '', + ...description(config), + `export declare const ${names}: readonly [`, + ...artifact.icons.map((icon) => ` ${JSON.stringify(icon.name)},`), + ']', + `export type ${component}Name = typeof ${names}[number]`, + `export declare const ${ids}: {`, + ...artifact.icons.map((icon) => ` readonly ${JSON.stringify(icon.name)}: ${JSON.stringify(icon.id)}`), + '}', + '', + ].join('\n') +} + +function component(config: ResolvedSpriteConfig): string { + const componentName = `${pascal(config.name)}Icon` + const ids = `${camel(config.name)}IconIds` + return [ + header(config.generatedNotice), + "import { Show, splitProps } from 'solid-js'", + "import styles from './solid-component.module.css'", + `import { ${ids} } from '../icon-data.js'`, + '', + "const spriteUrl = new URL('../sprite.svg', import.meta.url).href", + '', + `/** Иконка из SVG-спрайта «${config.name}». */`, + `export const ${componentName} = (props) => {`, + " const [local, rest] = splitProps(props, ['icon', 'wrapped', 'class'])", + ` const href = () => spriteUrl + '#' + ${ids}[local.icon]`, + '', + ' return (', + ' ', + ' ', + ' ', + ' }', + ' >', + ' ', + ' ', + ' ', + ' ', + ' )', + '}', + '', + ].join('\n') +} + +function componentTypes(config: ResolvedSpriteConfig): string { + const component = `${pascal(config.name)}Icon` + return [ + header(config.generatedNotice), + "import type { Component, JSX } from 'solid-js'", + `import type { ${component}Name } from '../icon-data.js'`, + '', + `export type ${component}Style = JSX.CSSProperties & Partial>`, + `type ${component}BaseProps = { icon: ${component}Name }`, + `type ${component}SvgProps = ${component}BaseProps & { wrapped?: false } & Omit, 'style'> & { style?: ${component}Style }`, + `type ${component}WrappedProps = ${component}BaseProps & { wrapped: true } & Omit, 'style'> & { style?: ${component}Style }`, + `export type ${component}Props = ${component}SvgProps | ${component}WrappedProps`, + `export declare const ${component}: Component<${component}Props>`, + '', + ].join('\n') +} + +function index(config: ResolvedSpriteConfig): string { + return [ + header(config.generatedNotice), + `export { ${pascal(config.name)}Icon } from './solid/solid-component.jsx'`, + `export { ${camel(config.name)}IconNames } from './icon-data.js'`, + '', + ].join('\n') +} + +function indexTypes(config: ResolvedSpriteConfig): string { + const component = `${pascal(config.name)}Icon` + return [ + header(config.generatedNotice), + `export { ${component} } from './solid/solid-component.jsx'`, + `export type { ${component}Props, ${component}Style } from './solid/solid-component.jsx'`, + `export { ${camel(config.name)}IconNames } from './icon-data.js'`, + `export type { ${component}Name } from './icon-data.js'`, + '', + ].join('\n') +} + +function styles(config: ResolvedSpriteConfig): string { + const transition = config.transform.addTransition + ? [' transition-property: fill, stroke, color;', ' transition-duration: 0.3s;', ' transition-timing-function: ease;'] + : [] + return [header(config.generatedNotice), '', '.root {', ...transition, '}', '', '.wrap {', ' display: inline-flex;', '}', '', '.wrap svg {', ' width: 100%;', ' height: 100%;', ...transition, '}', ''].join('\n') +} + +function manifest(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const data = { + schemaVersion: 1, + generator: '@gromlab/svg-sprites', + name: config.name, + ...(config.description === undefined ? {} : { description: config.description }), + usage: { framework: 'solid', componentName: `${pascal(config.name)}Icon` }, + mode: 'solid@webpack', + target: 'webpack', + format: artifact.format, + iconCount: artifact.icons.length, + spriteUrl: '__SPRITE_URL__', + icons: artifact.icons, + } + const source = JSON.stringify(data, null, 2).replace('"__SPRITE_URL__"', 'spriteUrl') + return [header(config.generatedNotice), "const spriteUrl = new URL('./sprite.svg', import.meta.url).href", '', `export const spriteManifest = ${source}`, '', 'export default spriteManifest', ''].join('\n') +} + +function manifestTypes(config: ResolvedSpriteConfig): string { + return [ + header(config.generatedNotice), + 'export type SpriteManifestColor = { variable: `--icon-color-${number}`; fallback: string }', + 'export type SpriteManifestIcon = { name: string; id: string; viewBox: string | null; colors: readonly SpriteManifestColor[] }', + 'export type SpriteManifest = {', + " schemaVersion: 1; generator: '@gromlab/svg-sprites'; name: string; description?: string", + " usage: { framework: 'solid'; componentName: string }", + " mode: 'solid@webpack'; target: 'webpack'; format: 'stack' | 'symbol'", + ' iconCount: number; spriteUrl: string; icons: readonly SpriteManifestIcon[]', + '}', + 'export declare const spriteManifest: SpriteManifest', + 'export default spriteManifest', + '', + ].join('\n') +} + +export function generateOutputFiles(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): GeneratedFile[] { + return [ + { path: path.posix.join(OUTPUT_DIR, 'index.js'), content: index(config) }, + { path: path.posix.join(OUTPUT_DIR, 'index.d.ts'), content: indexTypes(config) }, + { path: path.posix.join(OUTPUT_DIR, 'sprite.svg'), content: svg(artifact.bytes, config.generatedNotice) }, + { path: path.posix.join(OUTPUT_DIR, 'icon-data.js'), content: iconData(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'icon-data.d.ts'), content: iconTypes(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'solid', 'solid-component.jsx'), content: component(config) }, + { path: path.posix.join(OUTPUT_DIR, 'solid', 'solid-component.d.ts'), content: componentTypes(config) }, + { path: path.posix.join(OUTPUT_DIR, 'solid', 'solid-component.module.css'), content: styles(config) }, + { path: path.posix.join(OUTPUT_DIR, 'svg-sprite.manifest.js'), content: manifest(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'svg-sprite.manifest.d.ts'), content: manifestTypes(config) }, + ] +} diff --git a/src/modes/svelte-vite/adapter.ts b/src/modes/svelte-vite/adapter.ts new file mode 100644 index 0000000..6661652 --- /dev/null +++ b/src/modes/svelte-vite/adapter.ts @@ -0,0 +1,27 @@ +import { compileSpriteContent } from '../../compiler.js' +import { createCompiledSpriteArtifact } from '../../core/compiled-artifact.js' +import type { ModeAdapter } from '../../core/mode-adapter.js' +import { generateOutputFiles } from './output.js' + +export const svelteViteAdapter: ModeAdapter<'svelte@vite'> = { + mode: 'svelte@vite', + async generate(context) { + const bytes = await compileSpriteContent( + context.prepared.folder, + context.config.transform, + { rootViewBox: false }, + ) + const artifact = createCompiledSpriteArtifact(bytes, context.prepared.iconNames, 'stack') + + return { + files: generateOutputFiles(context.config, artifact), + paths: { + generatedDir: '.svg-sprite', + sprite: '.svg-sprite/sprite.svg', + manifest: '.svg-sprite/svg-sprite.manifest.js', + entry: '.svg-sprite/index.js', + }, + result: { target: 'vite' }, + } + }, +} diff --git a/src/modes/svelte-vite/output.ts b/src/modes/svelte-vite/output.ts new file mode 100644 index 0000000..bd35c0c --- /dev/null +++ b/src/modes/svelte-vite/output.ts @@ -0,0 +1,229 @@ +import path from 'node:path' +import type { CompiledSpriteArtifact } from '../../core/compiled-artifact.js' +import { GENERATED_MARKER } from '../../core/generated-markers.js' +import type { GeneratedFile } from '../../core/mode-adapter.js' +import type { ResolvedSpriteConfig } from '../../types.js' + +const MODE = 'svelte@vite' +const TARGET = 'vite' +const OUTPUT_DIR = '.svg-sprite' +const NOTICE = 'АВТОМАТИЧЕСКИ СГЕНЕРИРОВАННЫЙ ФАЙЛ. Не редактируйте вручную.' + +function pascal(value: string): string { + return value.split('-').map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join('') +} + +function camel(value: string): string { + return value.replace(/-([a-z0-9])/g, (_, char: string) => char.toUpperCase()) +} + +function jsHeader(enabled: boolean): string { + return enabled + ? `/*\n * ${NOTICE}\n * ${GENERATED_MARKER}.\n */` + : `/* ${GENERATED_MARKER}. Do not edit. */` +} + +function markupHeader(enabled: boolean): string { + return enabled + ? `` + : `` +} + +function svg(bytes: Uint8Array, enabled: boolean): string { + const marker = enabled ? `${NOTICE}\n ${GENERATED_MARKER}.` : `${GENERATED_MARKER}. Do not edit.` + const content = new TextDecoder().decode(bytes) + return content.startsWith(')\s*/, `$1\n\n`) + : `\n${content}` +} + +function description(config: ResolvedSpriteConfig): string[] { + const text = config.description ?? `Имена иконок SVG-спрайта «${config.name}».` + return ['/**', ...text.split(/\r?\n/).map((line) => ` * ${line.replace(/\*\//g, '* /')}`), ' */'] +} + +function iconData(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const prefix = camel(config.name) + return [ + jsHeader(config.generatedNotice), + '', + ...description(config), + `export const ${prefix}IconNames = ${JSON.stringify(artifact.icons.map((icon) => icon.name), null, 2)}`, + '', + `export const ${prefix}IconIds = ${JSON.stringify(Object.fromEntries(artifact.icons.map((icon) => [icon.name, icon.id])), null, 2)}`, + '', + ].join('\n') +} + +function component(config: ResolvedSpriteConfig): string { + const ids = `${camel(config.name)}IconIds` + const transition = config.transform.addTransition + ? [' transition-property: fill, stroke, color;', ' transition-duration: 0.3s;', ' transition-timing-function: ease;'] + : [] + return [ + markupHeader(config.generatedNotice), + '', + '', + '{#if wrapped}', + ' ', + '{:else}', + ' ', + '{/if}', + '', + '', + '', + ].join('\n') +} + +function iconDeclarations(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const componentName = `${pascal(config.name)}Icon` + const names = `${camel(config.name)}IconNames` + const ids = `${camel(config.name)}IconIds` + return [ + jsHeader(config.generatedNotice), + '', + ...description(config), + `export declare const ${names}: readonly [`, + ...artifact.icons.map((icon) => ` ${JSON.stringify(icon.name)},`), + ']', + `export type ${componentName}Name = typeof ${names}[number]`, + `export declare const ${ids}: {`, + ...artifact.icons.map((icon) => ` readonly ${JSON.stringify(icon.name)}: ${JSON.stringify(icon.id)}`), + '}', + '', + ].join('\n') +} + +function componentDeclarations(config: ResolvedSpriteConfig): string { + const componentName = `${pascal(config.name)}Icon` + return [ + jsHeader(config.generatedNotice), + "import type { Component } from 'svelte'", + "import type { ClassValue, HTMLAttributes, SVGAttributes } from 'svelte/elements'", + `import type { ${componentName}Name } from '../icon-data.js'`, + '', + `export type ${componentName}Style = string`, + `type ${componentName}BaseProps = { icon: ${componentName}Name; class?: ClassValue; style?: ${componentName}Style }`, + `type ${componentName}SvgProps = ${componentName}BaseProps & { wrapped?: false } & Omit, 'class' | 'style'>`, + `type ${componentName}WrappedProps = ${componentName}BaseProps & { wrapped: true } & Omit, 'class' | 'style'>`, + `export type ${componentName}Props = ${componentName}SvgProps | ${componentName}WrappedProps`, + `declare const ${componentName}: Component<${componentName}Props>`, + `export default ${componentName}`, + '', + ].join('\n') +} + +function index(config: ResolvedSpriteConfig): string { + return [ + jsHeader(config.generatedNotice), + `export { default as ${pascal(config.name)}Icon } from './svelte/svelte-component.svelte'`, + `export { ${camel(config.name)}IconNames } from './icon-data.js'`, + '', + ].join('\n') +} + +function indexDeclarations(config: ResolvedSpriteConfig): string { + const componentName = `${pascal(config.name)}Icon` + return [ + jsHeader(config.generatedNotice), + `export { default as ${componentName} } from './svelte/svelte-component.svelte'`, + `export type { ${componentName}Props, ${componentName}Style } from './svelte/svelte-component.svelte'`, + `export { ${camel(config.name)}IconNames } from './icon-data.js'`, + `export type { ${componentName}Name } from './icon-data.js'`, + '', + ].join('\n') +} + +function manifest(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const data = { + schemaVersion: 1, + generator: '@gromlab/svg-sprites', + name: config.name, + ...(config.description === undefined ? {} : { description: config.description }), + usage: { framework: 'svelte', componentName: `${pascal(config.name)}Icon` }, + mode: MODE, + target: TARGET, + format: artifact.format, + iconCount: artifact.icons.length, + spriteUrl: '__SPRITE_URL__', + icons: artifact.icons, + } + const source = JSON.stringify(data, null, 2).replace('"__SPRITE_URL__"', 'spriteUrl') + return [ + jsHeader(config.generatedNotice), + "import spriteUrl from './sprite.svg?no-inline'", + '', + `export const spriteManifest = ${source}`, + '', + 'export default spriteManifest', + '', + ].join('\n') +} + +function manifestDeclarations(config: ResolvedSpriteConfig): string { + return [ + jsHeader(config.generatedNotice), + '', + 'export type SpriteManifestColor = {', + ' variable: `--icon-color-${number}`', + ' fallback: string', + '}', + 'export type SpriteManifestIcon = {', + ' name: string', + ' id: string', + ' viewBox: string | null', + ' colors: readonly SpriteManifestColor[]', + '}', + 'export type SpriteManifest = {', + ' schemaVersion: 1', + " generator: '@gromlab/svg-sprites'", + ' name: string', + ' description?: string', + " usage: { framework: 'svelte'; componentName: string }", + " mode: 'svelte@vite'", + " target: 'vite'", + " format: 'stack' | 'symbol'", + ' iconCount: number', + ' spriteUrl: string', + ' icons: readonly SpriteManifestIcon[]', + '}', + '', + 'export declare const spriteManifest: SpriteManifest', + 'export default spriteManifest', + '', + ].join('\n') +} + +export function generateOutputFiles(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): GeneratedFile[] { + return [ + { path: path.posix.join(OUTPUT_DIR, 'index.js'), content: index(config) }, + { path: path.posix.join(OUTPUT_DIR, 'index.d.ts'), content: indexDeclarations(config) }, + { path: path.posix.join(OUTPUT_DIR, 'sprite.svg'), content: svg(artifact.bytes, config.generatedNotice) }, + { path: path.posix.join(OUTPUT_DIR, 'icon-data.js'), content: iconData(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'icon-data.d.ts'), content: iconDeclarations(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'svelte', 'svelte-component.svelte'), content: component(config) }, + { path: path.posix.join(OUTPUT_DIR, 'svelte', 'svelte-component.svelte.d.ts'), content: componentDeclarations(config) }, + { path: path.posix.join(OUTPUT_DIR, 'svg-sprite.manifest.js'), content: manifest(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'svg-sprite.manifest.d.ts'), content: manifestDeclarations(config) }, + ] +} diff --git a/src/modes/svelte-webpack/adapter.ts b/src/modes/svelte-webpack/adapter.ts new file mode 100644 index 0000000..169ded1 --- /dev/null +++ b/src/modes/svelte-webpack/adapter.ts @@ -0,0 +1,27 @@ +import { compileSpriteContent } from '../../compiler.js' +import { createCompiledSpriteArtifact } from '../../core/compiled-artifact.js' +import type { ModeAdapter } from '../../core/mode-adapter.js' +import { generateOutputFiles } from './output.js' + +export const svelteWebpackAdapter: ModeAdapter<'svelte@webpack'> = { + mode: 'svelte@webpack', + async generate(context) { + const bytes = await compileSpriteContent( + context.prepared.folder, + context.config.transform, + { rootViewBox: false }, + ) + const artifact = createCompiledSpriteArtifact(bytes, context.prepared.iconNames, 'stack') + + return { + files: generateOutputFiles(context.config, artifact), + paths: { + generatedDir: '.svg-sprite', + sprite: '.svg-sprite/sprite.svg', + manifest: '.svg-sprite/svg-sprite.manifest.js', + entry: '.svg-sprite/index.js', + }, + result: { target: 'webpack' }, + } + }, +} diff --git a/src/modes/svelte-webpack/output.ts b/src/modes/svelte-webpack/output.ts new file mode 100644 index 0000000..ee5e9b5 --- /dev/null +++ b/src/modes/svelte-webpack/output.ts @@ -0,0 +1,229 @@ +import path from 'node:path' +import type { CompiledSpriteArtifact } from '../../core/compiled-artifact.js' +import { GENERATED_MARKER } from '../../core/generated-markers.js' +import type { GeneratedFile } from '../../core/mode-adapter.js' +import type { ResolvedSpriteConfig } from '../../types.js' + +const MODE = 'svelte@webpack' +const TARGET = 'webpack' +const OUTPUT_DIR = '.svg-sprite' +const NOTICE = 'АВТОМАТИЧЕСКИ СГЕНЕРИРОВАННЫЙ ФАЙЛ. Не редактируйте вручную.' + +function pascal(value: string): string { + return value.split('-').map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join('') +} + +function camel(value: string): string { + return value.replace(/-([a-z0-9])/g, (_, char: string) => char.toUpperCase()) +} + +function jsHeader(enabled: boolean): string { + return enabled + ? `/*\n * ${NOTICE}\n * ${GENERATED_MARKER}.\n */` + : `/* ${GENERATED_MARKER}. Do not edit. */` +} + +function markupHeader(enabled: boolean): string { + return enabled + ? `` + : `` +} + +function svg(bytes: Uint8Array, enabled: boolean): string { + const marker = enabled ? `${NOTICE}\n ${GENERATED_MARKER}.` : `${GENERATED_MARKER}. Do not edit.` + const content = new TextDecoder().decode(bytes) + return content.startsWith(')\s*/, `$1\n\n`) + : `\n${content}` +} + +function description(config: ResolvedSpriteConfig): string[] { + const text = config.description ?? `Имена иконок SVG-спрайта «${config.name}».` + return ['/**', ...text.split(/\r?\n/).map((line) => ` * ${line.replace(/\*\//g, '* /')}`), ' */'] +} + +function iconData(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const prefix = camel(config.name) + return [ + jsHeader(config.generatedNotice), + '', + ...description(config), + `export const ${prefix}IconNames = ${JSON.stringify(artifact.icons.map((icon) => icon.name), null, 2)}`, + '', + `export const ${prefix}IconIds = ${JSON.stringify(Object.fromEntries(artifact.icons.map((icon) => [icon.name, icon.id])), null, 2)}`, + '', + ].join('\n') +} + +function component(config: ResolvedSpriteConfig): string { + const ids = `${camel(config.name)}IconIds` + const transition = config.transform.addTransition + ? [' transition-property: fill, stroke, color;', ' transition-duration: 0.3s;', ' transition-timing-function: ease;'] + : [] + return [ + markupHeader(config.generatedNotice), + '', + '', + '{#if wrapped}', + ' ', + '{:else}', + ' ', + '{/if}', + '', + '', + '', + ].join('\n') +} + +function iconDeclarations(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const componentName = `${pascal(config.name)}Icon` + const names = `${camel(config.name)}IconNames` + const ids = `${camel(config.name)}IconIds` + return [ + jsHeader(config.generatedNotice), + '', + ...description(config), + `export declare const ${names}: readonly [`, + ...artifact.icons.map((icon) => ` ${JSON.stringify(icon.name)},`), + ']', + `export type ${componentName}Name = typeof ${names}[number]`, + `export declare const ${ids}: {`, + ...artifact.icons.map((icon) => ` readonly ${JSON.stringify(icon.name)}: ${JSON.stringify(icon.id)}`), + '}', + '', + ].join('\n') +} + +function componentDeclarations(config: ResolvedSpriteConfig): string { + const componentName = `${pascal(config.name)}Icon` + return [ + jsHeader(config.generatedNotice), + "import type { Component } from 'svelte'", + "import type { ClassValue, HTMLAttributes, SVGAttributes } from 'svelte/elements'", + `import type { ${componentName}Name } from '../icon-data.js'`, + '', + `export type ${componentName}Style = string`, + `type ${componentName}BaseProps = { icon: ${componentName}Name; class?: ClassValue; style?: ${componentName}Style }`, + `type ${componentName}SvgProps = ${componentName}BaseProps & { wrapped?: false } & Omit, 'class' | 'style'>`, + `type ${componentName}WrappedProps = ${componentName}BaseProps & { wrapped: true } & Omit, 'class' | 'style'>`, + `export type ${componentName}Props = ${componentName}SvgProps | ${componentName}WrappedProps`, + `declare const ${componentName}: Component<${componentName}Props>`, + `export default ${componentName}`, + '', + ].join('\n') +} + +function index(config: ResolvedSpriteConfig): string { + return [ + jsHeader(config.generatedNotice), + `export { default as ${pascal(config.name)}Icon } from './svelte/svelte-component.svelte'`, + `export { ${camel(config.name)}IconNames } from './icon-data.js'`, + '', + ].join('\n') +} + +function indexDeclarations(config: ResolvedSpriteConfig): string { + const componentName = `${pascal(config.name)}Icon` + return [ + jsHeader(config.generatedNotice), + `export { default as ${componentName} } from './svelte/svelte-component.svelte'`, + `export type { ${componentName}Props, ${componentName}Style } from './svelte/svelte-component.svelte'`, + `export { ${camel(config.name)}IconNames } from './icon-data.js'`, + `export type { ${componentName}Name } from './icon-data.js'`, + '', + ].join('\n') +} + +function manifest(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const data = { + schemaVersion: 1, + generator: '@gromlab/svg-sprites', + name: config.name, + ...(config.description === undefined ? {} : { description: config.description }), + usage: { framework: 'svelte', componentName: `${pascal(config.name)}Icon` }, + mode: MODE, + target: TARGET, + format: artifact.format, + iconCount: artifact.icons.length, + spriteUrl: '__SPRITE_URL__', + icons: artifact.icons, + } + const source = JSON.stringify(data, null, 2).replace('"__SPRITE_URL__"', 'spriteUrl') + return [ + jsHeader(config.generatedNotice), + "const spriteUrl = new URL('./sprite.svg', import.meta.url).href", + '', + `export const spriteManifest = ${source}`, + '', + 'export default spriteManifest', + '', + ].join('\n') +} + +function manifestDeclarations(config: ResolvedSpriteConfig): string { + return [ + jsHeader(config.generatedNotice), + '', + 'export type SpriteManifestColor = {', + ' variable: `--icon-color-${number}`', + ' fallback: string', + '}', + 'export type SpriteManifestIcon = {', + ' name: string', + ' id: string', + ' viewBox: string | null', + ' colors: readonly SpriteManifestColor[]', + '}', + 'export type SpriteManifest = {', + ' schemaVersion: 1', + " generator: '@gromlab/svg-sprites'", + ' name: string', + ' description?: string', + " usage: { framework: 'svelte'; componentName: string }", + " mode: 'svelte@webpack'", + " target: 'webpack'", + " format: 'stack' | 'symbol'", + ' iconCount: number', + ' spriteUrl: string', + ' icons: readonly SpriteManifestIcon[]', + '}', + '', + 'export declare const spriteManifest: SpriteManifest', + 'export default spriteManifest', + '', + ].join('\n') +} + +export function generateOutputFiles(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): GeneratedFile[] { + return [ + { path: path.posix.join(OUTPUT_DIR, 'index.js'), content: index(config) }, + { path: path.posix.join(OUTPUT_DIR, 'index.d.ts'), content: indexDeclarations(config) }, + { path: path.posix.join(OUTPUT_DIR, 'sprite.svg'), content: svg(artifact.bytes, config.generatedNotice) }, + { path: path.posix.join(OUTPUT_DIR, 'icon-data.js'), content: iconData(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'icon-data.d.ts'), content: iconDeclarations(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'svelte', 'svelte-component.svelte'), content: component(config) }, + { path: path.posix.join(OUTPUT_DIR, 'svelte', 'svelte-component.svelte.d.ts'), content: componentDeclarations(config) }, + { path: path.posix.join(OUTPUT_DIR, 'svg-sprite.manifest.js'), content: manifest(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'svg-sprite.manifest.d.ts'), content: manifestDeclarations(config) }, + ] +} diff --git a/src/modes/sveltekit-vite/adapter.ts b/src/modes/sveltekit-vite/adapter.ts new file mode 100644 index 0000000..dd5a0b6 --- /dev/null +++ b/src/modes/sveltekit-vite/adapter.ts @@ -0,0 +1,27 @@ +import { compileSpriteContent } from '../../compiler.js' +import { createCompiledSpriteArtifact } from '../../core/compiled-artifact.js' +import type { ModeAdapter } from '../../core/mode-adapter.js' +import { generateOutputFiles } from './output.js' + +export const sveltekitViteAdapter: ModeAdapter<'sveltekit@vite'> = { + mode: 'sveltekit@vite', + async generate(context) { + const bytes = await compileSpriteContent( + context.prepared.folder, + context.config.transform, + { rootViewBox: false }, + ) + const artifact = createCompiledSpriteArtifact(bytes, context.prepared.iconNames, 'stack') + + return { + files: generateOutputFiles(context.config, artifact), + paths: { + generatedDir: '.svg-sprite', + sprite: '.svg-sprite/sprite.svg', + manifest: '.svg-sprite/svg-sprite.manifest.js', + entry: '.svg-sprite/index.js', + }, + result: { target: 'vite' }, + } + }, +} diff --git a/src/modes/sveltekit-vite/output.ts b/src/modes/sveltekit-vite/output.ts new file mode 100644 index 0000000..114dbeb --- /dev/null +++ b/src/modes/sveltekit-vite/output.ts @@ -0,0 +1,229 @@ +import path from 'node:path' +import type { CompiledSpriteArtifact } from '../../core/compiled-artifact.js' +import { GENERATED_MARKER } from '../../core/generated-markers.js' +import type { GeneratedFile } from '../../core/mode-adapter.js' +import type { ResolvedSpriteConfig } from '../../types.js' + +const MODE = 'sveltekit@vite' +const TARGET = 'vite' +const OUTPUT_DIR = '.svg-sprite' +const NOTICE = 'АВТОМАТИЧЕСКИ СГЕНЕРИРОВАННЫЙ ФАЙЛ. Не редактируйте вручную.' + +function pascal(value: string): string { + return value.split('-').map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join('') +} + +function camel(value: string): string { + return value.replace(/-([a-z0-9])/g, (_, char: string) => char.toUpperCase()) +} + +function jsHeader(enabled: boolean): string { + return enabled + ? `/*\n * ${NOTICE}\n * ${GENERATED_MARKER}.\n */` + : `/* ${GENERATED_MARKER}. Do not edit. */` +} + +function markupHeader(enabled: boolean): string { + return enabled + ? `` + : `` +} + +function svg(bytes: Uint8Array, enabled: boolean): string { + const marker = enabled ? `${NOTICE}\n ${GENERATED_MARKER}.` : `${GENERATED_MARKER}. Do not edit.` + const content = new TextDecoder().decode(bytes) + return content.startsWith(')\s*/, `$1\n\n`) + : `\n${content}` +} + +function description(config: ResolvedSpriteConfig): string[] { + const text = config.description ?? `Имена иконок SVG-спрайта «${config.name}».` + return ['/**', ...text.split(/\r?\n/).map((line) => ` * ${line.replace(/\*\//g, '* /')}`), ' */'] +} + +function iconData(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const prefix = camel(config.name) + return [ + jsHeader(config.generatedNotice), + '', + ...description(config), + `export const ${prefix}IconNames = ${JSON.stringify(artifact.icons.map((icon) => icon.name), null, 2)}`, + '', + `export const ${prefix}IconIds = ${JSON.stringify(Object.fromEntries(artifact.icons.map((icon) => [icon.name, icon.id])), null, 2)}`, + '', + ].join('\n') +} + +function component(config: ResolvedSpriteConfig): string { + const ids = `${camel(config.name)}IconIds` + const transition = config.transform.addTransition + ? [' transition-property: fill, stroke, color;', ' transition-duration: 0.3s;', ' transition-timing-function: ease;'] + : [] + return [ + markupHeader(config.generatedNotice), + '', + '', + '{#if wrapped}', + ' ', + '{:else}', + ' ', + '{/if}', + '', + '', + '', + ].join('\n') +} + +function iconDeclarations(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const componentName = `${pascal(config.name)}Icon` + const names = `${camel(config.name)}IconNames` + const ids = `${camel(config.name)}IconIds` + return [ + jsHeader(config.generatedNotice), + '', + ...description(config), + `export declare const ${names}: readonly [`, + ...artifact.icons.map((icon) => ` ${JSON.stringify(icon.name)},`), + ']', + `export type ${componentName}Name = typeof ${names}[number]`, + `export declare const ${ids}: {`, + ...artifact.icons.map((icon) => ` readonly ${JSON.stringify(icon.name)}: ${JSON.stringify(icon.id)}`), + '}', + '', + ].join('\n') +} + +function componentDeclarations(config: ResolvedSpriteConfig): string { + const componentName = `${pascal(config.name)}Icon` + return [ + jsHeader(config.generatedNotice), + "import type { Component } from 'svelte'", + "import type { ClassValue, HTMLAttributes, SVGAttributes } from 'svelte/elements'", + `import type { ${componentName}Name } from '../icon-data.js'`, + '', + `export type ${componentName}Style = string`, + `type ${componentName}BaseProps = { icon: ${componentName}Name; class?: ClassValue; style?: ${componentName}Style }`, + `type ${componentName}SvgProps = ${componentName}BaseProps & { wrapped?: false } & Omit, 'class' | 'style'>`, + `type ${componentName}WrappedProps = ${componentName}BaseProps & { wrapped: true } & Omit, 'class' | 'style'>`, + `export type ${componentName}Props = ${componentName}SvgProps | ${componentName}WrappedProps`, + `declare const ${componentName}: Component<${componentName}Props>`, + `export default ${componentName}`, + '', + ].join('\n') +} + +function index(config: ResolvedSpriteConfig): string { + return [ + jsHeader(config.generatedNotice), + `export { default as ${pascal(config.name)}Icon } from './svelte/svelte-component.svelte'`, + `export { ${camel(config.name)}IconNames } from './icon-data.js'`, + '', + ].join('\n') +} + +function indexDeclarations(config: ResolvedSpriteConfig): string { + const componentName = `${pascal(config.name)}Icon` + return [ + jsHeader(config.generatedNotice), + `export { default as ${componentName} } from './svelte/svelte-component.svelte'`, + `export type { ${componentName}Props, ${componentName}Style } from './svelte/svelte-component.svelte'`, + `export { ${camel(config.name)}IconNames } from './icon-data.js'`, + `export type { ${componentName}Name } from './icon-data.js'`, + '', + ].join('\n') +} + +function manifest(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const data = { + schemaVersion: 1, + generator: '@gromlab/svg-sprites', + name: config.name, + ...(config.description === undefined ? {} : { description: config.description }), + usage: { framework: 'svelte', componentName: `${pascal(config.name)}Icon` }, + mode: MODE, + target: TARGET, + format: artifact.format, + iconCount: artifact.icons.length, + spriteUrl: '__SPRITE_URL__', + icons: artifact.icons, + } + const source = JSON.stringify(data, null, 2).replace('"__SPRITE_URL__"', 'spriteUrl') + return [ + jsHeader(config.generatedNotice), + "import spriteUrl from './sprite.svg?no-inline'", + '', + `export const spriteManifest = ${source}`, + '', + 'export default spriteManifest', + '', + ].join('\n') +} + +function manifestDeclarations(config: ResolvedSpriteConfig): string { + return [ + jsHeader(config.generatedNotice), + '', + 'export type SpriteManifestColor = {', + ' variable: `--icon-color-${number}`', + ' fallback: string', + '}', + 'export type SpriteManifestIcon = {', + ' name: string', + ' id: string', + ' viewBox: string | null', + ' colors: readonly SpriteManifestColor[]', + '}', + 'export type SpriteManifest = {', + ' schemaVersion: 1', + " generator: '@gromlab/svg-sprites'", + ' name: string', + ' description?: string', + " usage: { framework: 'svelte'; componentName: string }", + " mode: 'sveltekit@vite'", + " target: 'vite'", + " format: 'stack' | 'symbol'", + ' iconCount: number', + ' spriteUrl: string', + ' icons: readonly SpriteManifestIcon[]', + '}', + '', + 'export declare const spriteManifest: SpriteManifest', + 'export default spriteManifest', + '', + ].join('\n') +} + +export function generateOutputFiles(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): GeneratedFile[] { + return [ + { path: path.posix.join(OUTPUT_DIR, 'index.js'), content: index(config) }, + { path: path.posix.join(OUTPUT_DIR, 'index.d.ts'), content: indexDeclarations(config) }, + { path: path.posix.join(OUTPUT_DIR, 'sprite.svg'), content: svg(artifact.bytes, config.generatedNotice) }, + { path: path.posix.join(OUTPUT_DIR, 'icon-data.js'), content: iconData(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'icon-data.d.ts'), content: iconDeclarations(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'svelte', 'svelte-component.svelte'), content: component(config) }, + { path: path.posix.join(OUTPUT_DIR, 'svelte', 'svelte-component.svelte.d.ts'), content: componentDeclarations(config) }, + { path: path.posix.join(OUTPUT_DIR, 'svg-sprite.manifest.js'), content: manifest(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'svg-sprite.manifest.d.ts'), content: manifestDeclarations(config) }, + ] +} diff --git a/src/modes/vue-vite/adapter.ts b/src/modes/vue-vite/adapter.ts new file mode 100644 index 0000000..0e390cf --- /dev/null +++ b/src/modes/vue-vite/adapter.ts @@ -0,0 +1,27 @@ +import { compileSpriteContent } from '../../compiler.js' +import { createCompiledSpriteArtifact } from '../../core/compiled-artifact.js' +import type { ModeAdapter } from '../../core/mode-adapter.js' +import { generateOutputFiles } from './output.js' + +export const vueViteAdapter: ModeAdapter<'vue@vite'> = { + mode: 'vue@vite', + async generate(context) { + const bytes = await compileSpriteContent( + context.prepared.folder, + context.config.transform, + { rootViewBox: false }, + ) + const artifact = createCompiledSpriteArtifact(bytes, context.prepared.iconNames, 'stack') + + return { + files: generateOutputFiles(context.config, artifact), + paths: { + generatedDir: '.svg-sprite', + sprite: '.svg-sprite/sprite.svg', + manifest: '.svg-sprite/svg-sprite.manifest.js', + entry: '.svg-sprite/index.js', + }, + result: { target: 'vite' }, + } + }, +} diff --git a/src/modes/vue-vite/output.ts b/src/modes/vue-vite/output.ts new file mode 100644 index 0000000..a829212 --- /dev/null +++ b/src/modes/vue-vite/output.ts @@ -0,0 +1,301 @@ +import path from 'node:path' +import type { CompiledSpriteArtifact } from '../../core/compiled-artifact.js' +import { GENERATED_MARKER } from '../../core/generated-markers.js' +import type { GeneratedFile } from '../../core/mode-adapter.js' +import type { ResolvedSpriteConfig } from '../../types.js' + +const MODE = 'vue@vite' +const TARGET = 'vite' +const OUTPUT_DIR = '.svg-sprite' +const GENERATED_NOTICE = [ + '----------------------------------------------------------------------', + '## АВТОМАТИЧЕСКИ СГЕНЕРИРОВАННЫЙ ФАЙЛ ##', + '## ##', + '## Не редактируйте вручную: изменения будут перезаписаны. ##', + '## Для изменений перегенерируйте SVG-спрайт. ##', + '## ##', + '## Генератор: @gromlab/svg-sprites ##', + '## Репозиторий: https://github.com/gromlab-ru/svg-sprites ##', + '----------------------------------------------------------------------', +] + +function toPascalCase(value: string): string { + return value.split('-').map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join('') +} + +function toCamelCase(value: string): string { + return value.replace(/-([a-z0-9])/g, (_, char: string) => char.toUpperCase()) +} + +function blockHeader(enabled: boolean): string { + if (!enabled) return `/* ${GENERATED_MARKER}. Do not edit. */` + return ['/*', ...GENERATED_NOTICE.map((line) => ` * ${line}`), ' */'].join('\n') +} + +function svgHeader(enabled: boolean): string { + if (!enabled) return `` + return [''].join('\n') +} + +function formatSvg(content: string): string { + let depth = 0 + const lines = content.replace(/>\n<').split('\n') + const formatted = lines.map((line) => { + const trimmed = line.trim() + const tags = trimmed.match(/<[^>]+>/g) ?? [] + const startsWithClosingTag = trimmed.startsWith('')) { + depth += 1 + } + } + return formattedLine + }) + return `${formatted.join('\n')}\n` +} + +function markedSvg(bytes: Uint8Array, notice: boolean): string { + const svg = formatSvg(new TextDecoder().decode(bytes)) + const header = svgHeader(notice) + return svg.startsWith(')\s*/, `$1\n${header}\n`) + : `${header}\n${svg}` +} + +function descriptionComment(name: string, description?: string): string[] { + const text = description ?? `Имена иконок SVG-спрайта «${name}».` + return ['/**', ...text.split(/\r?\n/).map((line) => ` * ${line.replace(/\*\//g, '* /')}`), ' */'] +} + +function generateIconData(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const camelName = toCamelCase(config.name) + const names = artifact.icons.map((icon) => icon.name) + const ids = Object.fromEntries(artifact.icons.map((icon) => [icon.name, icon.id])) + return [ + blockHeader(config.generatedNotice), + '', + ...descriptionComment(config.name, config.description), + `export const ${camelName}IconNames = ${JSON.stringify(names, null, 2)}`, + '', + `export const ${camelName}IconIds = ${JSON.stringify(ids, null, 2)}`, + '', + ].join('\n') +} + +function generateComponent(config: ResolvedSpriteConfig): string { + const pascalName = toPascalCase(config.name) + const camelName = toCamelCase(config.name) + return [ + blockHeader(config.generatedNotice), + "import { defineComponent, h } from 'vue'", + "import styles from './vue-component.module.css'", + `import { ${camelName}IconIds } from '../icon-data.js'`, + "import spriteUrl from '../sprite.svg?no-inline'", + '', + `/** Иконка из SVG-спрайта «${config.name}». */`, + `export const ${pascalName}Icon = defineComponent({`, + ` name: '${pascalName}Icon',`, + ' inheritAttrs: false,', + ' props: {', + " icon: { type: String, required: true },", + " wrapped: { type: Boolean, default: false },", + ' },', + ' setup(props, { attrs }) {', + ' return () => {', + ` const href = spriteUrl + '#' + ${camelName}IconIds[props.icon]`, + '', + ' if (props.wrapped) {', + " return h('span', {", + ' ...attrs,', + ' class: [styles.wrap, attrs.class],', + " }, [h('svg', null, [h('use', { href })])])", + ' }', + '', + " return h('svg', {", + ' ...attrs,', + ' class: [styles.root, attrs.class],', + " }, [h('use', { href })])", + ' }', + ' },', + '})', + '', + ].join('\n') +} + +function generateIconDeclarations(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const pascalName = toPascalCase(config.name) + const camelName = toCamelCase(config.name) + const tuple = artifact.icons.map((icon) => ` ${JSON.stringify(icon.name)},`).join('\n') + const ids = artifact.icons.map((icon) => ` readonly ${JSON.stringify(icon.name)}: ${JSON.stringify(icon.id)}`).join('\n') + return [ + blockHeader(config.generatedNotice), + '', + ...descriptionComment(config.name, config.description), + `export declare const ${camelName}IconNames: readonly [`, + tuple, + ']', + '', + `export type ${pascalName}IconName = typeof ${camelName}IconNames[number]`, + `export declare const ${camelName}IconIds: {`, + ids, + '}', + '', + ].join('\n') +} + +function generateComponentDeclarations(config: ResolvedSpriteConfig): string { + const pascalName = toPascalCase(config.name) + return [ + blockHeader(config.generatedNotice), + "import type { CSSProperties, DefineComponent, HTMLAttributes, SVGAttributes } from 'vue'", + `import type { ${pascalName}IconName } from '../icon-data.js'`, + '', + `export type ${pascalName}IconStyle = CSSProperties & Partial>`, + '', + `type ${pascalName}IconBaseProps = { icon: ${pascalName}IconName }`, + `type ${pascalName}IconSvgProps = ${pascalName}IconBaseProps & { wrapped?: false } & Omit & { style?: ${pascalName}IconStyle }`, + `type ${pascalName}IconWrappedProps = ${pascalName}IconBaseProps & { wrapped: true } & Omit & { style?: ${pascalName}IconStyle }`, + `export type ${pascalName}IconProps = ${pascalName}IconSvgProps | ${pascalName}IconWrappedProps`, + '', + `export declare const ${pascalName}Icon: DefineComponent<${pascalName}IconProps>`, + '', + ].join('\n') +} + +function generateIndexDeclarations(config: ResolvedSpriteConfig): string { + const pascalName = toPascalCase(config.name) + const camelName = toCamelCase(config.name) + return [ + blockHeader(config.generatedNotice), + `export { ${pascalName}Icon } from './vue/vue-component.js'`, + `export type { ${pascalName}IconProps, ${pascalName}IconStyle } from './vue/vue-component.js'`, + `export { ${camelName}IconNames } from './icon-data.js'`, + `export type { ${pascalName}IconName } from './icon-data.js'`, + '', + ].join('\n') +} + +function generateIndex(config: ResolvedSpriteConfig): string { + const pascalName = toPascalCase(config.name) + const camelName = toCamelCase(config.name) + return [ + blockHeader(config.generatedNotice), + `export { ${pascalName}Icon } from './vue/vue-component.js'`, + `export { ${camelName}IconNames } from './icon-data.js'`, + '', + ].join('\n') +} + +function generateStyles(config: ResolvedSpriteConfig): string { + const transition = config.transform.addTransition + ? [' transition-property: fill, stroke, color;', ' transition-duration: 0.3s;', ' transition-timing-function: ease;'] + : [] + return [ + blockHeader(config.generatedNotice), + '', + '.root {', + ...transition, + '}', + '', + '.wrap {', + ' display: inline-flex;', + '}', + '', + '.wrap svg {', + ' width: 100%;', + ' height: 100%;', + ...transition, + '}', + '', + ].join('\n') +} + +function manifestData(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact) { + return { + schemaVersion: 1, + generator: '@gromlab/svg-sprites', + name: config.name, + ...(config.description === undefined ? {} : { description: config.description }), + usage: { + framework: 'vue', + componentName: `${toPascalCase(config.name)}Icon`, + }, + mode: MODE, + target: TARGET, + format: artifact.format, + iconCount: artifact.icons.length, + spriteUrl: '__SPRITE_URL__', + icons: artifact.icons, + } +} + +function generateManifest(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const serialized = JSON.stringify(manifestData(config, artifact), null, 2) + .replace('"__SPRITE_URL__"', 'spriteUrl') + return [ + blockHeader(config.generatedNotice), + "import spriteUrl from './sprite.svg?no-inline'", + '', + `export const spriteManifest = ${serialized}`, + '', + 'export default spriteManifest', + '', + ].join('\n') +} + +function generateManifestDeclarations(config: ResolvedSpriteConfig): string { + return [ + blockHeader(config.generatedNotice), + '', + 'export type SpriteManifestColor = {', + ' variable: `--icon-color-${number}`', + ' fallback: string', + '}', + 'export type SpriteManifestIcon = {', + ' name: string', + ' id: string', + ' viewBox: string | null', + ' colors: readonly SpriteManifestColor[]', + '}', + 'export type SpriteManifest = {', + ' schemaVersion: 1', + " generator: '@gromlab/svg-sprites'", + ' name: string', + ' description?: string', + " usage: { framework: 'vue'; componentName: string }", + " mode: 'vue@vite'", + " target: 'vite'", + " format: 'stack' | 'symbol'", + ' iconCount: number', + ' spriteUrl: string', + ' icons: readonly SpriteManifestIcon[]', + '}', + '', + 'export declare const spriteManifest: SpriteManifest', + 'export default spriteManifest', + '', + ].join('\n') +} + +export function generateOutputFiles( + config: ResolvedSpriteConfig, + artifact: CompiledSpriteArtifact, +): GeneratedFile[] { + return [ + { path: path.posix.join(OUTPUT_DIR, 'index.js'), content: generateIndex(config) }, + { path: path.posix.join(OUTPUT_DIR, 'index.d.ts'), content: generateIndexDeclarations(config) }, + { path: path.posix.join(OUTPUT_DIR, 'sprite.svg'), content: markedSvg(artifact.bytes, config.generatedNotice) }, + { path: path.posix.join(OUTPUT_DIR, 'icon-data.js'), content: generateIconData(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'icon-data.d.ts'), content: generateIconDeclarations(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'vue', 'vue-component.js'), content: generateComponent(config) }, + { path: path.posix.join(OUTPUT_DIR, 'vue', 'vue-component.d.ts'), content: generateComponentDeclarations(config) }, + { path: path.posix.join(OUTPUT_DIR, 'vue', 'vue-component.module.css'), content: generateStyles(config) }, + { path: path.posix.join(OUTPUT_DIR, 'svg-sprite.manifest.js'), content: generateManifest(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'svg-sprite.manifest.d.ts'), content: generateManifestDeclarations(config) }, + ] +} diff --git a/src/modes/vue-webpack/adapter.ts b/src/modes/vue-webpack/adapter.ts new file mode 100644 index 0000000..dc7c261 --- /dev/null +++ b/src/modes/vue-webpack/adapter.ts @@ -0,0 +1,25 @@ +import { compileSpriteContent } from '../../compiler.js' +import { createCompiledSpriteArtifact } from '../../core/compiled-artifact.js' +import type { ModeAdapter } from '../../core/mode-adapter.js' +import { generateOutputFiles } from './output.js' + +export const vueWebpackAdapter: ModeAdapter<'vue@webpack'> = { + mode: 'vue@webpack', + async generate(context) { + const bytes = await compileSpriteContent(context.prepared.folder, context.config.transform, { + rootViewBox: false, + }) + const artifact = createCompiledSpriteArtifact(bytes, context.prepared.iconNames, 'stack') + + return { + files: generateOutputFiles(context.config, artifact), + paths: { + generatedDir: '.svg-sprite', + sprite: '.svg-sprite/sprite.svg', + manifest: '.svg-sprite/svg-sprite.manifest.js', + entry: '.svg-sprite/index.js', + }, + result: { target: 'webpack' }, + } + }, +} diff --git a/src/modes/vue-webpack/output.ts b/src/modes/vue-webpack/output.ts new file mode 100644 index 0000000..06fe687 --- /dev/null +++ b/src/modes/vue-webpack/output.ts @@ -0,0 +1,242 @@ +import path from 'node:path' +import type { CompiledSpriteArtifact } from '../../core/compiled-artifact.js' +import { GENERATED_MARKER } from '../../core/generated-markers.js' +import type { GeneratedFile } from '../../core/mode-adapter.js' +import type { ResolvedSpriteConfig } from '../../types.js' + +const MODE = 'vue@webpack' +const TARGET = 'webpack' +const OUTPUT_DIR = '.svg-sprite' +const NOTICE = 'AUTOMATICALLY GENERATED FILE. Do not edit manually.' + +function pascal(value: string): string { + return value.split('-').map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join('') +} + +function camel(value: string): string { + return value.replace(/-([a-z0-9])/g, (_, char: string) => char.toUpperCase()) +} + +function header(enabled: boolean): string { + return enabled + ? `/*\n * ${NOTICE}\n * ${GENERATED_MARKER}.\n */` + : `/* ${GENERATED_MARKER}. Do not edit. */` +} + +function markedSvg(bytes: Uint8Array, enabled: boolean): string { + const marker = enabled ? `${NOTICE}\n ${GENERATED_MARKER}.` : `${GENERATED_MARKER}. Do not edit.` + const content = new TextDecoder().decode(bytes) + return content.startsWith(')\s*/, `$1\n\n`) + : `\n${content}` +} + +function docs(config: ResolvedSpriteConfig): string[] { + const text = config.description ?? `SVG sprite icon names for "${config.name}".` + return ['/**', ...text.split(/\r?\n/).map((line) => ` * ${line.replace(/\*\//g, '* /')}`), ' */'] +} + +function iconData(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const prefix = camel(config.name) + const names = artifact.icons.map((icon) => icon.name) + const ids = Object.fromEntries(artifact.icons.map((icon) => [icon.name, icon.id])) + return [ + header(config.generatedNotice), + '', + ...docs(config), + `export const ${prefix}IconNames = ${JSON.stringify(names, null, 2)}`, + '', + `export const ${prefix}IconIds = ${JSON.stringify(ids, null, 2)}`, + '', + ].join('\n') +} + +function component(config: ResolvedSpriteConfig): string { + const componentName = `${pascal(config.name)}Icon` + const ids = `${camel(config.name)}IconIds` + return [ + header(config.generatedNotice), + "import { defineComponent, h } from 'vue'", + "import styles from './vue-component.module.css'", + `import { ${ids} } from '../icon-data.js'`, + '', + "const spriteUrl = new URL('../sprite.svg', import.meta.url).href", + '', + `export const ${componentName} = defineComponent({`, + ` name: '${componentName}',`, + ' inheritAttrs: false,', + ' props: {', + " icon: { type: String, required: true },", + " wrapped: { type: Boolean, default: false },", + ' },', + ' setup(props, { attrs }) {', + ' return () => {', + ` const href = spriteUrl + '#' + ${ids}[props.icon]`, + ' if (props.wrapped) {', + " return h('span', { ...attrs, class: [styles.wrap, attrs.class] }, [", + " h('svg', null, [h('use', { href })]),", + ' ])', + ' }', + " return h('svg', { ...attrs, class: [styles.root, attrs.class] }, [", + " h('use', { href }),", + ' ])', + ' }', + ' },', + '})', + '', + ].join('\n') +} + +function iconDeclarations(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const componentName = `${pascal(config.name)}Icon` + const names = `${camel(config.name)}IconNames` + const ids = `${camel(config.name)}IconIds` + return [ + header(config.generatedNotice), + '', + ...docs(config), + `export declare const ${names}: readonly [`, + ...artifact.icons.map((icon) => ` ${JSON.stringify(icon.name)},`), + ']', + `export type ${componentName}Name = typeof ${names}[number]`, + `export declare const ${ids}: {`, + ...artifact.icons.map((icon) => ` readonly ${JSON.stringify(icon.name)}: ${JSON.stringify(icon.id)}`), + '}', + '', + ].join('\n') +} + +function componentDeclarations(config: ResolvedSpriteConfig): string { + const componentName = `${pascal(config.name)}Icon` + return [ + header(config.generatedNotice), + "import type { CSSProperties, DefineComponent, HTMLAttributes, SVGAttributes } from 'vue'", + `import type { ${componentName}Name } from '../icon-data.js'`, + '', + `export type ${componentName}Style = CSSProperties & Partial>`, + `type ${componentName}BaseProps = { icon: ${componentName}Name }`, + `type ${componentName}SvgProps = ${componentName}BaseProps & { wrapped?: false } & Omit & { style?: ${componentName}Style }`, + `type ${componentName}WrappedProps = ${componentName}BaseProps & { wrapped: true } & Omit & { style?: ${componentName}Style }`, + `export type ${componentName}Props = ${componentName}SvgProps | ${componentName}WrappedProps`, + `export declare const ${componentName}: DefineComponent<${componentName}Props>`, + '', + ].join('\n') +} + +function index(config: ResolvedSpriteConfig): string { + return [ + header(config.generatedNotice), + `export { ${pascal(config.name)}Icon } from './vue/vue-component.js'`, + `export { ${camel(config.name)}IconNames } from './icon-data.js'`, + '', + ].join('\n') +} + +function indexDeclarations(config: ResolvedSpriteConfig): string { + const componentName = `${pascal(config.name)}Icon` + const names = `${camel(config.name)}IconNames` + return [ + header(config.generatedNotice), + `export { ${componentName} } from './vue/vue-component.js'`, + `export type { ${componentName}Props, ${componentName}Style } from './vue/vue-component.js'`, + `export { ${names} } from './icon-data.js'`, + `export type { ${componentName}Name } from './icon-data.js'`, + '', + ].join('\n') +} + +function styles(config: ResolvedSpriteConfig): string { + const transition = config.transform.addTransition + ? [' transition-property: fill, stroke, color;', ' transition-duration: 0.3s;', ' transition-timing-function: ease;'] + : [] + return [ + header(config.generatedNotice), + '', + '.root {', + ...transition, + '}', + '', + '.wrap {', + ' display: inline-flex;', + '}', + '', + '.wrap svg {', + ' width: 100%;', + ' height: 100%;', + ...transition, + '}', + '', + ].join('\n') +} + +function manifest(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const data = { + schemaVersion: 1, + generator: '@gromlab/svg-sprites', + name: config.name, + ...(config.description === undefined ? {} : { description: config.description }), + usage: { framework: 'vue', componentName: `${pascal(config.name)}Icon` }, + mode: MODE, + target: TARGET, + format: artifact.format, + iconCount: artifact.icons.length, + spriteUrl: '__SPRITE_URL__', + icons: artifact.icons, + } + const source = JSON.stringify(data, null, 2).replace('"__SPRITE_URL__"', 'spriteUrl') + return [ + header(config.generatedNotice), + "const spriteUrl = new URL('./sprite.svg', import.meta.url).href", + '', + `export const spriteManifest = ${source}`, + '', + 'export default spriteManifest', + '', + ].join('\n') +} + +function manifestDeclarations(config: ResolvedSpriteConfig): string { + return [ + header(config.generatedNotice), + '', + 'export type SpriteManifestColor = { variable: `--icon-color-${number}`; fallback: string }', + 'export type SpriteManifestIcon = {', + ' name: string', + ' id: string', + ' viewBox: string | null', + ' colors: readonly SpriteManifestColor[]', + '}', + 'export type SpriteManifest = {', + ' schemaVersion: 1', + " generator: '@gromlab/svg-sprites'", + ' name: string', + ' description?: string', + " usage: { framework: 'vue'; componentName: string }", + " mode: 'vue@webpack'", + " target: 'webpack'", + " format: 'stack' | 'symbol'", + ' iconCount: number', + ' spriteUrl: string', + ' icons: readonly SpriteManifestIcon[]', + '}', + '', + 'export declare const spriteManifest: SpriteManifest', + 'export default spriteManifest', + '', + ].join('\n') +} + +export function generateOutputFiles(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): GeneratedFile[] { + return [ + { path: path.posix.join(OUTPUT_DIR, 'index.js'), content: index(config) }, + { path: path.posix.join(OUTPUT_DIR, 'index.d.ts'), content: indexDeclarations(config) }, + { path: path.posix.join(OUTPUT_DIR, 'sprite.svg'), content: markedSvg(artifact.bytes, config.generatedNotice) }, + { path: path.posix.join(OUTPUT_DIR, 'icon-data.js'), content: iconData(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'icon-data.d.ts'), content: iconDeclarations(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'vue', 'vue-component.js'), content: component(config) }, + { path: path.posix.join(OUTPUT_DIR, 'vue', 'vue-component.d.ts'), content: componentDeclarations(config) }, + { path: path.posix.join(OUTPUT_DIR, 'vue', 'vue-component.module.css'), content: styles(config) }, + { path: path.posix.join(OUTPUT_DIR, 'svg-sprite.manifest.js'), content: manifest(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'svg-sprite.manifest.d.ts'), content: manifestDeclarations(config) }, + ] +} diff --git a/src/targets/types.ts b/src/targets/types.ts index 054cfc2..e3d69cb 100644 --- a/src/targets/types.ts +++ b/src/targets/types.ts @@ -37,6 +37,23 @@ export type ReactAssetTarget = ViteAssetTarget | WebpackAssetTarget /** Полный ключ React mode, используемый конфигом, CLI и manifest. */ export type ReactSpriteMode = `react@${ReactAssetTarget}` +/** Среда сборки Vue sprite-модуля. */ +export type VueAssetTarget = ViteAssetTarget + +/** Полный ключ Vue mode, используемый конфигом, CLI и manifest. */ +export type VueSpriteMode = `vue@${ViteAssetTarget | WebpackAssetTarget}` + +export type NuxtSpriteMode = `nuxt@${ViteAssetTarget | WebpackAssetTarget}` +export type SvelteSpriteMode = `svelte@${ViteAssetTarget | WebpackAssetTarget}` | 'sveltekit@vite' +export type AngularAssetTarget = 'application' | WebpackAssetTarget +export type AngularSpriteMode = `angular@${AngularAssetTarget}` +export type AstroSpriteMode = 'astro@vite' +export type SolidSpriteMode = `solid@${ViteAssetTarget | WebpackAssetTarget}` | 'solid-start@vite' +export type PreactSpriteMode = `preact@${ViteAssetTarget | WebpackAssetTarget}` +export type QwikSpriteMode = 'qwik@vite' +export type LitSpriteMode = `lit@${ViteAssetTarget | WebpackAssetTarget}` +export type AlpineSpriteMode = `alpine@${ViteAssetTarget | WebpackAssetTarget}` + /** Среда standalone sprite-модуля. */ export type StandaloneAssetTarget = StaticAssetTarget | ViteAssetTarget | WebpackAssetTarget @@ -44,10 +61,23 @@ export type StandaloneAssetTarget = StaticAssetTarget | ViteAssetTarget | Webpac export type StandaloneSpriteMode = 'standalone' | `standalone@${ViteAssetTarget | WebpackAssetTarget}` /** Любая среда, для которой может быть сгенерирован React sprite-модуль. */ -export type SpriteAssetTarget = ReactAssetTarget | NextAssetTarget | StaticAssetTarget +export type SpriteAssetTarget = ReactAssetTarget | NextAssetTarget | StaticAssetTarget | AngularAssetTarget /** Режим генерации sprite-модуля. */ -export type SpriteMode = ReactSpriteMode | NextAssetTarget | StandaloneSpriteMode +export type SpriteMode = + | ReactSpriteMode + | VueSpriteMode + | NuxtSpriteMode + | SvelteSpriteMode + | AngularSpriteMode + | AstroSpriteMode + | SolidSpriteMode + | PreactSpriteMode + | QwikSpriteMode + | LitSpriteMode + | AlpineSpriteMode + | NextAssetTarget + | StandaloneSpriteMode /** Фрагменты кода, необходимые компоненту для получения URL SVG asset. */ export type SpriteAssetUrlCode = { diff --git a/src/viewer/code.ts b/src/viewer/code.ts index 890794b..778b220 100644 --- a/src/viewer/code.ts +++ b/src/viewer/code.ts @@ -1,6 +1,8 @@ import type { SpriteViewerManifest, SpriteViewerManifestIcon } from './types.js' -export type SpriteViewerTab = 'react' | 'vue' | 'svg' | 'img' | 'css' +import type { SpriteViewerManifestUsage } from './types.js' + +export type SpriteViewerTab = SpriteViewerManifestUsage['framework'] | 'svg' | 'img' | 'css' export type SpriteViewerCodeLanguage = 'tsx' | 'html' | 'css' const COMMON_TABS: ReadonlyArray<{ id: SpriteViewerTab; label: string }> = [ @@ -9,7 +11,21 @@ const COMMON_TABS: ReadonlyArray<{ id: SpriteViewerTab; label: string }> = [ { id: 'css', label: 'CSS' }, ] -function usageForManifest(manifest: SpriteViewerManifest): { framework: 'react' | 'vue'; componentName: string } | null { +const FRAMEWORK_LABELS: Record = { + react: 'React', + vue: 'Vue', + svelte: 'Svelte', + angular: 'Angular', + astro: 'Astro', + solid: 'Solid', + 'solid-start': 'SolidStart', + preact: 'Preact', + qwik: 'Qwik', + lit: 'Lit', + alpine: 'Alpine', +} + +function usageForManifest(manifest: SpriteViewerManifest): SpriteViewerManifestUsage | null { if (manifest.usage) return manifest.usage if (manifest.componentName) return { framework: 'react', componentName: manifest.componentName } return null @@ -18,12 +34,12 @@ function usageForManifest(manifest: SpriteViewerManifest): { framework: 'react' export function tabsForManifest(manifest: SpriteViewerManifest): ReadonlyArray<{ id: SpriteViewerTab; label: string }> { const usage = usageForManifest(manifest) const frameworkTab = usage - ? [{ id: usage.framework, label: usage.framework === 'react' ? 'React' : 'Vue' } as const] + ? [{ id: usage.framework, label: FRAMEWORK_LABELS[usage.framework] }] : [] const tabs = [...frameworkTab, ...COMMON_TABS] return manifest.format === 'stack' ? tabs - : tabs.filter((tab) => tab.id === 'react' || tab.id === 'vue' || tab.id === 'svg') + : tabs.filter((tab) => tab.id !== 'img' && tab.id !== 'css') } export function viewBoxSize(viewBox: string | null): string | null { @@ -96,11 +112,32 @@ export function generateViewerCode(options: { const href = `${manifest.spriteUrl}#${icon.id}` const overrides = styleLines(colorOverrides) - if (tab === 'react' || tab === 'vue') { + if (!['svg', 'img', 'css'].includes(tab)) { const usage = usageForManifest(manifest) if (!usage) throw new Error(`The ${tab} code tab requires component metadata.`) - if (tab === 'vue') { + if (usage.framework === 'alpine') { + return { + code: ``, + language: 'html', + } + } + + if (usage.framework === 'lit') { + return { + code: `<${usage.tagName} icon=${JSON.stringify(icon.name)}>`, + language: 'html', + } + } + + if (usage.framework === 'angular') { + return { + code: `<${usage.selector} icon=${JSON.stringify(icon.name)}>`, + language: 'html', + } + } + + if (usage.framework === 'vue') { const style = Object.entries(colorOverrides) .map(([variable, color]) => `${JSON.stringify(variable)}: ${JSON.stringify(color)}`) .join(', ') diff --git a/src/viewer/element.ts b/src/viewer/element.ts index e054f38..3b57570 100644 --- a/src/viewer/element.ts +++ b/src/viewer/element.ts @@ -437,7 +437,7 @@ class GromlabSpriteViewerElement extends LitElement { cssColor: this._cssColor, }) const dimensions = viewBoxSize(icon.viewBox) - const colorsVisible = activeTab === 'react' || activeTab === 'vue' || activeTab === 'svg' + const colorsVisible = activeTab !== 'img' && activeTab !== 'css' return html` ', + ) + + return { + rootDir, + config: { + mode, + name: 'app', + description: 'Application icons', + input: [iconsDir], + transform: { + removeSize: true, + replaceColors: true, + addTransition: true, + }, + generatedNotice: false, + }, + prepared: { + folder: { + name: 'app', + format: 'stack', + path: iconsDir, + files: [iconPath], + }, + iconNames: ['check'], + }, + } +} + +function filesByPath(plan) { + return new Map(plan.files.map((file) => [file.path, String(file.content)])) +} + +test('Angular application adapter emits a standalone component for the application builder', async () => { + const plan = await angularApplicationAdapter.generate(createContext('angular@application')) + const files = filesByPath(plan) + + assert.equal(angularApplicationAdapter.mode, 'angular@application') + assert.equal(plan.result.target, 'application') + assert.equal(plan.paths.entry, '.svg-sprite/index.ts') + assert.deepEqual([...files.keys()].sort(), [ + '.svg-sprite/angular/angular-component.css', + '.svg-sprite/angular/angular-component.d.ts', + '.svg-sprite/angular/angular-component.ts', + '.svg-sprite/assets.d.ts', + '.svg-sprite/icon-data.d.ts', + '.svg-sprite/icon-data.js', + '.svg-sprite/index.d.ts', + '.svg-sprite/index.ts', + '.svg-sprite/sprite.svg', + '.svg-sprite/svg-sprite.manifest.d.ts', + '.svg-sprite/svg-sprite.manifest.js', + ]) + + const component = files.get('.svg-sprite/angular/angular-component.ts') + const declarations = files.get('.svg-sprite/angular/angular-component.d.ts') + const manifest = files.get('.svg-sprite/svg-sprite.manifest.js') + assert.match(component, /@Component\(\{/) + assert.match(component, /standalone: true/) + assert.match(component, /export class AppIcon/) + assert.match(component, /input\.required\(\)/) + assert.match(component, /reference path="\.\.\/assets\.d\.ts"/) + assert.match(component, /import spriteUrl from '\.\.\/sprite\.svg'/) + assert.match(declarations, /InputSignal/) + assert.match(declarations, /ɵɵComponentDeclaration/) + assert.match(manifest, /import spriteUrl from '\.\/sprite\.svg'/) + assert.match(manifest, /"mode": "angular@application"/) + assert.match(manifest, /"framework": "angular"/) + assert.doesNotMatch(component, /customElements|HTMLElement|attachShadow/) +}) + +test('Angular Webpack adapter owns its Webpack asset contract', async () => { + const plan = await angularWebpackAdapter.generate(createContext('angular@webpack')) + const files = filesByPath(plan) + const component = files.get('.svg-sprite/angular/angular-component.ts') + const manifest = files.get('.svg-sprite/svg-sprite.manifest.js') + + assert.equal(angularWebpackAdapter.mode, 'angular@webpack') + assert.equal(plan.result.target, 'webpack') + assert.equal(files.has('.svg-sprite/assets.d.ts'), false) + assert.match(component, /new URL\('\.\.\/sprite\.svg', import\.meta\.url\)\.href/) + assert.match(manifest, /new URL\('\.\/sprite\.svg', import\.meta\.url\)\.href/) + assert.match(manifest, /"mode": "angular@webpack"/) + assert.doesNotMatch(component, /customElements|HTMLElement|attachShadow/) +}) + +test('Astro Vite adapter emits an Astro component with JavaScript frontmatter', async () => { + const plan = await astroViteAdapter.generate(createContext('astro@vite')) + const files = filesByPath(plan) + + assert.equal(astroViteAdapter.mode, 'astro@vite') + assert.equal(plan.result.target, 'vite') + assert.equal(plan.paths.entry, '.svg-sprite/index.js') + assert.deepEqual([...files.keys()].sort(), [ + '.svg-sprite/astro/astro-component.astro', + '.svg-sprite/astro/astro-component.astro.d.ts', + '.svg-sprite/astro/astro-component.css', + '.svg-sprite/icon-data.d.ts', + '.svg-sprite/icon-data.js', + '.svg-sprite/index.d.ts', + '.svg-sprite/index.js', + '.svg-sprite/sprite.svg', + '.svg-sprite/svg-sprite.manifest.d.ts', + '.svg-sprite/svg-sprite.manifest.js', + ]) + + const component = files.get('.svg-sprite/astro/astro-component.astro') + const declarations = files.get('.svg-sprite/astro/astro-component.astro.d.ts') + const manifest = files.get('.svg-sprite/svg-sprite.manifest.js') + assert.match(component, /^---\n/) + assert.match(component, /sprite\.svg\?no-inline/) + assert.match(component, /Astro\.props/) + assert.doesNotMatch(component, /lang=["']ts|interface Props|customElements|HTMLElement|attachShadow/) + assert.match(declarations, /HTMLAttributes<'svg'>/) + assert.match(declarations, /export type AppIconProps/) + assert.match(manifest, /sprite\.svg\?no-inline/) + assert.match(manifest, /"framework": "astro"/) + assert.match(manifest, /"componentName": "AppIcon"/) + assert.match(manifest, /"mode": "astro@vite"/) +}) + +test('framework-family adapters have no horizontal mode imports', () => { + for (const modeDirectory of ['angular-application', 'angular-webpack', 'astro-vite']) { + for (const fileName of ['adapter.ts', 'output.ts']) { + const source = fs.readFileSync( + path.resolve(import.meta.dirname, '..', 'src', 'modes', modeDirectory, fileName), + 'utf8', + ) + const imports = [...source.matchAll(/^import .* from ['"]([^'"]+)['"]$/gm)] + for (const [, specifier] of imports) { + if (specifier.startsWith('../')) assert.match(specifier, /^\.\.\/\.\.\//) + assert.doesNotMatch(specifier, /(?:^|\/)modes\//) + } + } + } +}) diff --git a/test/documentation-guides.test.mjs b/test/documentation-guides.test.mjs index 5792945..b253270 100644 --- a/test/documentation-guides.test.mjs +++ b/test/documentation-guides.test.mjs @@ -11,6 +11,26 @@ const guideModes = new Map([ ['standalone-webpack.md', 'standalone@webpack'], ['react-vite.md', 'react@vite'], ['react-webpack.md', 'react@webpack'], + ['vue-vite.md', 'vue@vite'], + ['vue-webpack.md', 'vue@webpack'], + ['nuxt-vite.md', 'nuxt@vite'], + ['nuxt-webpack.md', 'nuxt@webpack'], + ['svelte-vite.md', 'svelte@vite'], + ['svelte-webpack.md', 'svelte@webpack'], + ['sveltekit-vite.md', 'sveltekit@vite'], + ['angular-application.md', 'angular@application'], + ['angular-webpack.md', 'angular@webpack'], + ['astro-vite.md', 'astro@vite'], + ['solid-vite.md', 'solid@vite'], + ['solid-webpack.md', 'solid@webpack'], + ['solid-start-vite.md', 'solid-start@vite'], + ['preact-vite.md', 'preact@vite'], + ['preact-webpack.md', 'preact@webpack'], + ['qwik-vite.md', 'qwik@vite'], + ['lit-vite.md', 'lit@vite'], + ['lit-webpack.md', 'lit@webpack'], + ['alpine-vite.md', 'alpine@vite'], + ['alpine-webpack.md', 'alpine@webpack'], ['next-app-turbopack.md', 'next@app/turbopack'], ['next-app-webpack.md', 'next@app/webpack'], ['next-pages-turbopack.md', 'next@pages/turbopack'], @@ -35,6 +55,11 @@ const commandPatterns = { ru: /npx --yes @gromlab\/svg-sprites(?:\s|$)/, } +const spriteDirectoryPatterns = { + en: /Choose a directory for the future SVG sprite/, + ru: /Выберите каталог для будущего SVG-спрайта/, +} + function markdownFiles(directory) { return fs.readdirSync(directory, { withFileTypes: true }).flatMap((entry) => { const entryPath = path.join(directory, entry.name) @@ -67,10 +92,12 @@ test('exact-mode guides are reusable by docs and skills', () => { ] assert.deepEqual(headings, expectedHeadings) assert.match(source, commandPatterns[language]) + assert.match(source, spriteDirectoryPatterns[language]) if (mode !== 'standalone') { assert.match(source, /npm install --save-dev @gromlab\/svg-sprites/) } assert.doesNotMatch(source, /npx --yes[^\n]*@gromlab\/svg-sprites@/) + assert.doesNotMatch(source, /check\.svg|icon="check"|#check|iconName: 'check'/) const modePattern = new RegExp(`"mode": "${mode.replaceAll('/', '\\/')}"`) assert.match(source, modePattern) assert.doesNotMatch(source, /\]\([^)]+\)/, `${language}/${file} must not depend on its location`) diff --git a/test/lit-alpine-mode-contracts.test.mjs b/test/lit-alpine-mode-contracts.test.mjs new file mode 100644 index 0000000..3d539ae --- /dev/null +++ b/test/lit-alpine-mode-contracts.test.mjs @@ -0,0 +1,90 @@ +import assert from 'node:assert/strict' +import fs from 'node:fs' +import os from 'node:os' +import path from 'node:path' +import test from 'node:test' + +import { generateSprite, isSpriteMode } from '../dist/index.js' + +const CASES = [ + { mode: 'lit@vite', framework: 'lit', target: 'vite', folder: 'lit', runtime: 'lit-component.js' }, + { mode: 'lit@webpack', framework: 'lit', target: 'webpack', folder: 'lit', runtime: 'lit-component.js' }, + { mode: 'alpine@vite', framework: 'alpine', target: 'vite', folder: 'alpine', runtime: 'alpine-integration.js' }, + { mode: 'alpine@webpack', framework: 'alpine', target: 'webpack', folder: 'alpine', runtime: 'alpine-integration.js' }, +] + +test('keeps Lit and Alpine exact modes isolated', () => { + for (const { mode } of CASES) { + const modeDir = path.join(import.meta.dirname, '..', 'src', 'modes', mode.replace('@', '-')) + const source = ['adapter.ts', 'output.ts'] + .map((file) => fs.readFileSync(path.join(modeDir, file), 'utf8')) + .join('\n') + + assert.doesNotMatch(source, /from ['"][^'"]*\.\.\/(?:lit|alpine)-(?:vite|webpack)/) + assert.match(source, new RegExp(`const MODE = '${mode}'|mode: '${mode}'`)) + } +}) + +test('generates isolated Lit and Alpine exact-mode contracts', async (t) => { + for (const fixture of CASES) { + await t.test(fixture.mode, async () => { + const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), `svg-sprites-${fixture.framework}-`)) + const iconsDir = path.join(rootDir, 'icons') + fs.mkdirSync(iconsDir) + fs.writeFileSync( + path.join(iconsDir, 'check.svg'), + '', + ) + + assert.equal(isSpriteMode(fixture.mode), true) + const result = await generateSprite(rootDir, { + mode: fixture.mode, + name: 'app', + generatedNotice: false, + }) + const generatedDir = path.join(rootDir, '.svg-sprite') + const runtimePath = path.join(generatedDir, fixture.folder, fixture.runtime) + const runtime = fs.readFileSync(runtimePath, 'utf8') + const runtimeTypes = fs.readFileSync(runtimePath.replace(/\.js$/, '.d.ts'), 'utf8') + const manifest = fs.readFileSync(result.manifestPath, 'utf8') + const manifestTypes = fs.readFileSync(path.join(generatedDir, 'svg-sprite.manifest.d.ts'), 'utf8') + + assert.equal(result.mode, fixture.mode) + assert.equal(result.target, fixture.target) + assert.equal(result.iconCount, 1) + assert.deepEqual(fs.readdirSync(path.join(generatedDir, fixture.folder)).sort(), [ + `${fixture.runtime.replace(/\.js$/, '')}.css`, + `${fixture.runtime.replace(/\.js$/, '')}.d.ts`, + fixture.runtime, + ]) + assert.match(manifest, new RegExp(`"framework": "${fixture.framework}"`)) + assert.match(manifest, new RegExp(`"mode": "${fixture.mode}"`)) + assert.match(manifestTypes, new RegExp(`mode: '${fixture.mode}'`)) + + if (fixture.framework === 'lit') { + assert.match(runtime, /import \{ LitElement, html, nothing, unsafeCSS \} from 'lit'/) + assert.match(runtime, /export class AppIcon extends LitElement/) + assert.match(runtime, /export function defineAppIcon\(\)/) + assert.match(runtimeTypes, /export declare class AppIcon extends LitElement/) + assert.match(runtimeTypes, /"app-icon": AppIcon/) + assert.match(manifest, /"componentName": "AppIcon"/) + } else { + assert.match(runtime, /export const appAlpinePlugin = \(Alpine\) =>/) + assert.match(runtime, /Alpine\.directive\(appIconDirective/) + assert.match(runtime, /Alpine\.magic\(appIconMagic/) + assert.doesNotMatch(runtime, /customElements|extends HTMLElement|LitElement/) + assert.match(runtimeTypes, /export type AppAlpineApi/) + assert.match(runtimeTypes, /export declare const appAlpinePlugin: AppAlpinePluginType/) + assert.doesNotMatch(runtimeTypes, /from ['"]alpinejs['"]|import\(['"]alpinejs['"]\)/) + } + + if (fixture.target === 'vite') { + assert.match(runtime, /sprite\.svg\?no-inline/) + assert.match(manifest, /sprite\.svg\?no-inline/) + } else { + assert.match(runtime, /new URL\('\.\.\/sprite\.svg', import\.meta\.url\)\.href/) + assert.match(manifest, /new URL\('\.\/sprite\.svg', import\.meta\.url\)\.href/) + } + }) + } +}) diff --git a/test/preact-modes.test.mjs b/test/preact-modes.test.mjs new file mode 100644 index 0000000..7c95e27 --- /dev/null +++ b/test/preact-modes.test.mjs @@ -0,0 +1,52 @@ +import assert from 'node:assert/strict' +import fs from 'node:fs' +import os from 'node:os' +import path from 'node:path' +import test from 'node:test' + +import { generateSprite, isSpriteMode } from '../dist/index.js' + +const MODES = [ + { mode: 'preact@vite', target: 'vite', asset: /sprite\.svg\?no-inline/ }, + { mode: 'preact@webpack', target: 'webpack', asset: /new URL\('\.\.\/sprite\.svg', import\.meta\.url\)\.href/ }, +] + +function fixture() { + const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), 'svg-sprites-preact-')) + fs.mkdirSync(path.join(rootDir, 'icons')) + fs.writeFileSync( + path.join(rootDir, 'icons', 'check.svg'), + '', + ) + return rootDir +} + +test('generates every isolated Preact family contract', async () => { + for (const contract of MODES) { + assert.equal(isSpriteMode(contract.mode), true) + const result = await generateSprite(fixture(), { + mode: contract.mode, + name: 'app-icons', + generatedNotice: false, + }) + const component = fs.readFileSync(path.join(result.generatedDir, 'preact', 'preact-component.js'), 'utf8') + const componentTypes = fs.readFileSync(path.join(result.generatedDir, 'preact', 'preact-component.d.ts'), 'utf8') + const manifest = fs.readFileSync(result.manifestPath, 'utf8') + + assert.equal(result.mode, contract.mode) + assert.equal(result.target, contract.target) + assert.deepEqual(fs.readdirSync(path.join(result.generatedDir, 'preact')).sort(), [ + 'preact-component.d.ts', + 'preact-component.js', + 'preact-component.module.css', + ]) + assert.match(component, /import \{ h \} from 'preact'/) + assert.match(component, /export const AppIconsIcon/) + assert.match(component, contract.asset) + assert.match(componentTypes, /FunctionalComponent/) + assert.match(manifest, /"framework": "preact"/) + assert.match(manifest, new RegExp(`"mode": ${JSON.stringify(contract.mode)}`)) + assert.doesNotMatch(component, /customElements|HTMLElement|gromlab-sprite-viewer/) + assert.equal(fs.existsSync(path.join(result.generatedDir, 'preact', 'preact-component.tsx')), false) + } +}) diff --git a/test/qwik-vite-mode.test.mjs b/test/qwik-vite-mode.test.mjs new file mode 100644 index 0000000..4d7abf4 --- /dev/null +++ b/test/qwik-vite-mode.test.mjs @@ -0,0 +1,43 @@ +import assert from 'node:assert/strict' +import fs from 'node:fs' +import os from 'node:os' +import path from 'node:path' +import test from 'node:test' + +import { generateSprite, isSpriteMode } from '../dist/index.js' + +test('generates an isolated SSR-safe Qwik Vite contract', async () => { + const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), 'svg-sprites-qwik-')) + fs.mkdirSync(path.join(rootDir, 'icons')) + fs.writeFileSync( + path.join(rootDir, 'icons', 'check.svg'), + '', + ) + + assert.equal(isSpriteMode('qwik@vite'), true) + const result = await generateSprite(rootDir, { + mode: 'qwik@vite', + name: 'app-icons', + generatedNotice: false, + }) + const component = fs.readFileSync(path.join(result.generatedDir, 'qwik', 'qwik-component.jsx'), 'utf8') + const componentTypes = fs.readFileSync(path.join(result.generatedDir, 'qwik', 'qwik-component.d.ts'), 'utf8') + const manifest = fs.readFileSync(result.manifestPath, 'utf8') + + assert.equal(result.mode, 'qwik@vite') + assert.equal(result.target, 'vite') + assert.deepEqual(fs.readdirSync(path.join(result.generatedDir, 'qwik')).sort(), [ + 'qwik-component.d.ts', + 'qwik-component.jsx', + 'qwik-component.module.css', + ]) + assert.match(component, /import \{ component\$ \} from '@builder\.io\/qwik'/) + assert.match(component, /sprite\.svg\?no-inline/) + assert.match(component, /export const AppIconsIcon = component\$/) + assert.match(componentTypes, /Component/) + assert.match(manifest, /"framework": "qwik"/) + assert.match(manifest, /"ssr": true/) + assert.match(manifest, /"mode": "qwik@vite"/) + assert.doesNotMatch(component, /\b(?:window|document|navigator|customElements|HTMLElement)\b/) + assert.equal(fs.existsSync(path.join(result.generatedDir, 'qwik', 'qwik-component.tsx')), false) +}) diff --git a/test/solid-modes.test.mjs b/test/solid-modes.test.mjs new file mode 100644 index 0000000..5d5b11e --- /dev/null +++ b/test/solid-modes.test.mjs @@ -0,0 +1,59 @@ +import assert from 'node:assert/strict' +import fs from 'node:fs' +import os from 'node:os' +import path from 'node:path' +import test from 'node:test' + +import { generateSprite, isSpriteMode } from '../dist/index.js' + +const MODES = [ + { mode: 'solid@vite', target: 'vite', asset: /sprite\.svg\?no-inline/ }, + { mode: 'solid@webpack', target: 'webpack', asset: /new URL\('\.\.\/sprite\.svg', import\.meta\.url\)\.href/ }, + { mode: 'solid-start@vite', target: 'vite', asset: /sprite\.svg\?no-inline/, ssr: true }, +] + +function fixture() { + const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), 'svg-sprites-solid-')) + fs.mkdirSync(path.join(rootDir, 'icons')) + fs.writeFileSync( + path.join(rootDir, 'icons', 'check.svg'), + '', + ) + return rootDir +} + +test('generates every isolated Solid family contract', async () => { + for (const contract of MODES) { + assert.equal(isSpriteMode(contract.mode), true) + const result = await generateSprite(fixture(), { + mode: contract.mode, + name: 'app-icons', + generatedNotice: false, + }) + const componentPath = path.join(result.generatedDir, 'solid', 'solid-component.jsx') + const component = fs.readFileSync(componentPath, 'utf8') + const componentTypes = fs.readFileSync(path.join(result.generatedDir, 'solid', 'solid-component.d.ts'), 'utf8') + const manifest = fs.readFileSync(result.manifestPath, 'utf8') + + assert.equal(result.mode, contract.mode) + assert.equal(result.target, contract.target) + assert.deepEqual(fs.readdirSync(path.join(result.generatedDir, 'solid')).sort(), [ + 'solid-component.d.ts', + 'solid-component.jsx', + 'solid-component.module.css', + ]) + assert.match(component, /import \{ Show, splitProps \} from 'solid-js'/) + assert.match(component, /export const AppIconsIcon/) + assert.match(component, contract.asset) + assert.match(componentTypes, /Component/) + assert.match(manifest, /"framework": "solid(?:-start)?"/) + assert.match(manifest, new RegExp(`"mode": ${JSON.stringify(contract.mode)}`)) + assert.doesNotMatch(component, /customElements|HTMLElement|gromlab-sprite-viewer/) + assert.equal(fs.existsSync(path.join(result.generatedDir, 'solid', 'solid-component.tsx')), false) + + if (contract.ssr) { + assert.match(manifest, /"ssr": true/) + assert.doesNotMatch(component, /\b(?:window|document|navigator|customElements)\b/) + } + } +}) diff --git a/test/svelte-modes.test.mjs b/test/svelte-modes.test.mjs new file mode 100644 index 0000000..65dd1d2 --- /dev/null +++ b/test/svelte-modes.test.mjs @@ -0,0 +1,69 @@ +import assert from 'node:assert/strict' +import fs from 'node:fs' +import os from 'node:os' +import path from 'node:path' +import test from 'node:test' + +import { generateSprite, isSpriteMode } from '../dist/index.js' + +const GENERATED_FILES = [ + 'icon-data.d.ts', + 'icon-data.js', + 'index.d.ts', + 'index.js', + 'sprite.svg', + 'svelte', + 'svg-sprite.manifest.d.ts', + 'svg-sprite.manifest.js', +] + +test('generates isolated native Svelte contracts for every Svelte exact mode', async () => { + const modes = [ + { mode: 'svelte@vite', target: 'vite', url: /sprite\.svg\?no-inline/ }, + { mode: 'svelte@webpack', target: 'webpack', url: /new URL\('\.\.\/sprite\.svg', import\.meta\.url\)\.href/ }, + { mode: 'sveltekit@vite', target: 'vite', url: /sprite\.svg\?no-inline/ }, + ] + + for (const expected of modes) { + assert.equal(isSpriteMode(expected.mode), true) + const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), 'svg-sprites-svelte-')) + const iconsDir = path.join(rootDir, 'icons') + fs.mkdirSync(iconsDir) + fs.writeFileSync( + path.join(iconsDir, 'check.svg'), + '', + ) + + const result = await generateSprite(rootDir, { + mode: expected.mode, + name: 'app', + generatedNotice: false, + }) + const generatedDir = path.join(rootDir, '.svg-sprite') + const component = fs.readFileSync(path.join(generatedDir, 'svelte', 'svelte-component.svelte'), 'utf8') + const componentTypes = fs.readFileSync(path.join(generatedDir, 'svelte', 'svelte-component.svelte.d.ts'), 'utf8') + const manifest = fs.readFileSync(result.manifestPath, 'utf8') + const manifestTypes = fs.readFileSync(path.join(generatedDir, 'svg-sprite.manifest.d.ts'), 'utf8') + + assert.equal(result.mode, expected.mode) + assert.equal(result.target, expected.target) + assert.equal(result.iconCount, 1) + assert.deepEqual(fs.readdirSync(generatedDir).sort(), GENERATED_FILES) + assert.deepEqual(fs.readdirSync(path.join(generatedDir, 'svelte')).sort(), [ + 'svelte-component.svelte', + 'svelte-component.svelte.d.ts', + ]) + assert.match(component, /