├── .astro
└── types.d.ts
├── .changeset
├── README.md
└── config.json
├── .editorconfig
├── .github
├── actions
│ └── install
│ │ └── action.yml
├── renovate.json5
└── workflows
│ ├── ci.yml
│ └── release.yml
├── .gitignore
├── .nvmrc
├── .prettierignore
├── .prettierrc.js
├── .vscode
└── settings.json
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── astro.config.ts
├── env.d.ts
├── eslint-plugin-import.d.ts
├── eslint.config.js
├── index.ts
├── package.json
├── pnpm-lock.yaml
├── src
├── assets
│ └── astro-full-logo-light.svg
├── components
│ ├── Story.astro
│ └── Svg
│ │ ├── Svg.astro
│ │ ├── overrideSvgAttributes.bench.ts
│ │ ├── overrideSvgAttributes.test.ts
│ │ └── overrideSvgAttributes.ts
├── env.d.ts
├── layouts
│ └── Layout.astro
├── pages
│ └── index.astro
└── types.ts
└── tsconfig.json
/.astro/types.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/.changeset/README.md:
--------------------------------------------------------------------------------
1 | # Changesets
2 |
3 | Hello and welcome! This folder has been automatically generated by
4 | `@changesets/cli`, a build tool that works with multi-package repos, or
5 | single-package repos to help you version and publish your code. You can find the
6 | full documentation for it
7 | [in our repository](https://github.com/changesets/changesets)
8 |
9 | We have a quick list of common questions to get you started engaging with this
10 | project in
11 | [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
12 |
--------------------------------------------------------------------------------
/.changeset/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://unpkg.com/@changesets/config@2.2.0/schema.json",
3 | "changelog": "@changesets/cli/changelog",
4 | "commit": false,
5 | "access": "public",
6 | "baseBranch": "main",
7 | "updateInternalDependencies": "patch"
8 | }
9 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = lf
6 | insert_final_newline = true
7 | indent_style = space
8 | indent_size = 2
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | trim_trailing_whitespace = false
13 |
14 | [*.yml]
15 | indent_size = 2
16 |
--------------------------------------------------------------------------------
/.github/actions/install/action.yml:
--------------------------------------------------------------------------------
1 | name: Install Tools & Dependencies
2 | description: Installs pnpm, Node.js & package dependencies
3 |
4 | runs:
5 | using: composite
6 | steps:
7 | - name: Setup pnpm
8 | uses: pnpm/action-setup@v4
9 |
10 | - name: Setup Node ${{ inputs.node-version }}
11 | uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
12 | with:
13 | node-version-file: ".nvmrc"
14 | cache: pnpm
15 |
16 | - name: Install dependencies
17 | run: pnpm install
18 | shell: bash
19 |
--------------------------------------------------------------------------------
/.github/renovate.json5:
--------------------------------------------------------------------------------
1 | {
2 | $schema: "https://docs.renovatebot.com/renovate-schema.json",
3 | extends: ["config:best-practices", "config:js-lib"],
4 | rangeStrategy: "update-lockfile",
5 | lockFileMaintenance: {
6 | enabled: true,
7 | automerge: true,
8 | },
9 | packageRules: [
10 | {
11 | groupName: "prettier and plugins",
12 | matchPackageNames: ["prettier"],
13 | },
14 | ],
15 | }
16 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 | on:
3 | push:
4 | branches:
5 | - main
6 | pull_request:
7 | branches:
8 | - main
9 | jobs:
10 | lint:
11 | name: Lint and typecheck
12 | runs-on: ubuntu-latest
13 | steps:
14 | - name: Checkout code
15 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
16 | - name: Install Tools & Dependencies
17 | uses: ./.github/actions/install
18 | - name: Check `*.astro` types
19 | run: pnpm check:astro
20 | - name: Check `*.ts` types
21 | run: pnpm check:ts
22 | - name: Lint astro and ts
23 | run: pnpm lint
24 | - name: Check formatting
25 | run: pnpm format:check
26 | test:
27 | name: Test
28 | runs-on: ubuntu-latest
29 | steps:
30 | - name: Checkout code
31 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
32 | - name: Install Tools & Dependencies
33 | uses: ./.github/actions/install
34 | - name: Test
35 | run: pnpm test
36 | build-docs:
37 | name: Smoke-Test Docs Site
38 | runs-on: ubuntu-latest
39 | steps:
40 | - name: Checkout code
41 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
42 | - name: Install Tools & Dependencies
43 | uses: ./.github/actions/install
44 | - name: Build the docs
45 | run: pnpm build
46 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 |
8 | defaults:
9 | run:
10 | shell: bash
11 |
12 | jobs:
13 | changelog:
14 | name: Changelog PR or Release
15 | if: ${{ github.repository_owner == 'jasikpark' }}
16 | runs-on: ubuntu-latest
17 | permissions:
18 | contents: write
19 | id-token: write
20 | pull-requests: write
21 | steps:
22 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
23 |
24 | - name: Install Tools & Dependencies
25 | uses: ./.github/actions/install
26 |
27 | - name: Create Release Pull Request or Publish
28 | id: changesets
29 | uses: changesets/action@v1
30 | with:
31 | # Note: pnpm install after versioning is necessary to refresh lockfile
32 | version: pnpm run version
33 | publish: pnpm exec changeset publish
34 | commit: "[ci] release"
35 | title: "[ci] release"
36 | env:
37 | # Needs access to push to main
38 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
39 | # Needs access to publish to npm
40 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
41 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ### Node ###
2 | # Logs
3 | logs
4 | *.log
5 | npm-debug.log*
6 | yarn-debug.log*
7 | yarn-error.log*
8 | lerna-debug.log*
9 | .pnpm-debug.log*
10 |
11 | # Diagnostic reports (https://nodejs.org/api/report.html)
12 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
13 |
14 | # Coverage directory used by tools like istanbul
15 | coverage
16 | *.lcov
17 |
18 |
19 | # Dependency directories
20 | node_modules/
21 |
22 | # TypeScript cache
23 | *.tsbuildinfo
24 |
25 | # Optional npm cache directory
26 | .npm
27 |
28 | # Optional eslint cache
29 | .eslintcache
30 |
31 | # Optional stylelint cache
32 | .stylelintcache
33 |
34 | # Optional REPL history
35 | .node_repl_history
36 |
37 | # Output of 'npm pack'
38 | *.tgz
39 |
40 | # Yarn Integrity file
41 | .yarn-integrity
42 |
43 | # dotenv environment variable files
44 | .env
45 | .env.development.local
46 | .env.test.local
47 | .env.production.local
48 | .env.local
49 |
50 | # parcel-bundler cache (https://parceljs.org/)
51 | .cache
52 | .parcel-cache
53 |
54 | # FuseBox cache
55 | .fusebox/
56 |
57 | # DynamoDB Local files
58 | .dynamodb/
59 |
60 | # TernJS port file
61 | .tern-port
62 |
63 | # Stores VSCode versions used for testing VSCode extensions
64 | .vscode-test
65 |
66 | # yarn v2
67 | .yarn/cache
68 | .yarn/unplugged
69 | .yarn/build-state.yml
70 | .yarn/install-state.gz
71 | .pnp.*
72 |
73 |
74 | dist/
75 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | 22.14.0
2 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | # Ignore artifacts:
2 | dist
3 | coverage
4 | pnpm-lock.yaml
5 |
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | /** @type {import("prettier").Config & { experimentalTernaries: true }} */
2 | export default {
3 | proseWrap: "preserve",
4 | plugins: ["prettier-plugin-astro"],
5 | overrides: [
6 | {
7 | files: "*.astro",
8 | options: {
9 | parser: "astro",
10 | },
11 | },
12 | ],
13 | experimentalTernaries: true,
14 | };
15 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "prettier.documentSelectors": ["**/*.astro"],
3 | "[astro]": {
4 | "editor.defaultFormatter": "esbenp.prettier-vscode"
5 | },
6 | "languageToolLinter.languageTool.ignoredWordsInWorkspace": ["astro", "vite"],
7 | "eslint.validate": [
8 | "javascript",
9 | "javascriptreact",
10 | "astro", // Enable .astro
11 | "typescript", // Enable .ts
12 | "typescriptreact" // Enable .tsx
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # @jasikpark/astro-svg-loader
2 |
3 | ## 0.3.1
4 |
5 | ### Patch Changes
6 |
7 | - 83f32ce: Remove extraneous dependency
8 |
9 | ## 0.3.0
10 |
11 | ### Minor Changes
12 |
13 | - 523a49e: Permit Astro 4.0 as a peer dep
14 |
15 | ## 0.2.0
16 |
17 | ### Minor Changes
18 |
19 | - f755968: Update to allow Astro v3 as a valid peer dep
20 |
21 | ### Patch Changes
22 |
23 | - afa45a0: Start publishing with provenance (https://docs.npmjs.com/generating-provenance-statements)
24 |
25 | ## 0.1.0
26 |
27 | ### Minor Changes
28 |
29 | - eb2f29a: Move to supporting astro v2 instead of astro v1
30 |
31 | ### Patch Changes
32 |
33 | - 57e5f36: Error on `alt` prop passed to SVG; `aria-hidden` or `aria-label`
34 | should be used instead.
35 |
36 | ## 0.0.2
37 |
38 | ### Patch Changes
39 |
40 | - 99b36fe: Update README installation instructions
41 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | We as members, contributors, and leaders pledge to make participation in our
6 | community a harassment-free experience for everyone, regardless of age, body
7 | size, visible or invisible disability, ethnicity, sex characteristics, gender
8 | identity and expression, level of experience, education, socio-economic status,
9 | nationality, personal appearance, race, religion, or sexual identity and
10 | orientation.
11 |
12 | We pledge to act and interact in ways that contribute to an open, welcoming,
13 | diverse, inclusive, and healthy community.
14 |
15 | ## Our Standards
16 |
17 | Examples of behavior that contributes to a positive environment for our
18 | community include:
19 |
20 | - Demonstrating empathy and kindness toward other people
21 | - Being respectful of differing opinions, viewpoints, and experiences
22 | - Giving and gracefully accepting constructive feedback
23 | - Accepting responsibility and apologizing to those affected by our mistakes,
24 | and learning from the experience
25 | - Focusing on what is best not just for us as individuals, but for the overall
26 | community
27 |
28 | Examples of unacceptable behavior include:
29 |
30 | - The use of sexualized language or imagery, and sexual attention or advances of
31 | any kind
32 | - Trolling, insulting or derogatory comments, and personal or political attacks
33 | - Public or private harassment
34 | - Publishing others' private information, such as a physical or email address,
35 | without their explicit permission
36 | - Other conduct which could reasonably be considered inappropriate in a
37 | professional setting
38 |
39 | ## Enforcement Responsibilities
40 |
41 | Community leaders are responsible for clarifying and enforcing our standards of
42 | acceptable behavior and will take appropriate and fair corrective action in
43 | response to any behavior that they deem inappropriate, threatening, offensive,
44 | or harmful.
45 |
46 | Community leaders have the right and responsibility to remove, edit, or reject
47 | comments, commits, code, wiki edits, issues, and other contributions that are
48 | not aligned to this Code of Conduct, and will communicate reasons for moderation
49 | decisions when appropriate.
50 |
51 | ## Scope
52 |
53 | This Code of Conduct applies within all community spaces, and also applies when
54 | an individual is officially representing the community in public spaces.
55 | Examples of representing our community include using an official e-mail address,
56 | posting via an official social media account, or acting as an appointed
57 | representative at an online or offline event.
58 |
59 | ## Enforcement
60 |
61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
62 | reported to the community leaders responsible for enforcement at
63 | calebjasik@jasik.xyz. All complaints will be reviewed and investigated promptly
64 | and fairly.
65 |
66 | All community leaders are obligated to respect the privacy and security of the
67 | reporter of any incident.
68 |
69 | ## Enforcement Guidelines
70 |
71 | Community leaders will follow these Community Impact Guidelines in determining
72 | the consequences for any action they deem in violation of this Code of Conduct:
73 |
74 | ### 1. Correction
75 |
76 | **Community Impact**: Use of inappropriate language or other behavior deemed
77 | unprofessional or unwelcome in the community.
78 |
79 | **Consequence**: A private, written warning from community leaders, providing
80 | clarity around the nature of the violation and an explanation of why the
81 | behavior was inappropriate. A public apology may be requested.
82 |
83 | ### 2. Warning
84 |
85 | **Community Impact**: A violation through a single incident or series of
86 | actions.
87 |
88 | **Consequence**: A warning with consequences for continued behavior. No
89 | interaction with the people involved, including unsolicited interaction with
90 | those enforcing the Code of Conduct, for a specified period of time. This
91 | includes avoiding interactions in community spaces as well as external channels
92 | like social media. Violating these terms may lead to a temporary or permanent
93 | ban.
94 |
95 | ### 3. Temporary Ban
96 |
97 | **Community Impact**: A serious violation of community standards, including
98 | sustained inappropriate behavior.
99 |
100 | **Consequence**: A temporary ban from any sort of interaction or public
101 | communication with the community for a specified period of time. No public or
102 | private interaction with the people involved, including unsolicited interaction
103 | with those enforcing the Code of Conduct, is allowed during this period.
104 | Violating these terms may lead to a permanent ban.
105 |
106 | ### 4. Permanent Ban
107 |
108 | **Community Impact**: Demonstrating a pattern of violation of community
109 | standards, including sustained inappropriate behavior, harassment of an
110 | individual, or aggression toward or disparagement of classes of individuals.
111 |
112 | **Consequence**: A permanent ban from any sort of public interaction within the
113 | community.
114 |
115 | ## Attribution
116 |
117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage],
118 | version 2.0, available at
119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
120 |
121 | Community Impact Guidelines were inspired by
122 | [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity).
123 |
124 | [homepage]: https://www.contributor-covenant.org
125 |
126 | For answers to common questions about this code of conduct, see the FAQ at
127 | https://www.contributor-covenant.org/faq. Translations are available at
128 | https://www.contributor-covenant.org/translations.
129 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Caleb Jasik
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # `@jasikpark/astro-svg-loader`
2 |
3 | [](https://app.fossa.com/projects/git%2Bgithub.com%2Fjasikpark%2Fastro-svg-loader?ref=badge_shield) [](https://fast-check.dev/)
4 |
5 | This package allows you to import SVGs as Astro components, using
6 | [@natemoo-re](https://github.com/natemoo-re)'s `ultrahtml` and vite's support
7 | for
8 | [raw imports](https://vitejs.dev/guide/assets.html#importing-asset-as-string) to
9 | do the trick.
10 |
11 | Taking inspiration from
12 | that holds that icons should always be inlined.
13 |
14 | Usage:
15 |
16 | Peer deps are `astro`
17 |
18 | ```
19 | npm add -D @jasikpark/astro-svg-loader
20 | ```
21 |
22 | ```
23 | yarn add -D @jasikpark/astro-svg-loader
24 | ```
25 |
26 | ```
27 | pnpm add -D @jasikpark/astro-svg-loader
28 | ```
29 |
30 | then in an Astro component:
31 |
32 | ```astro
33 | ---
34 | import Svg from "@jasikpark/astro-svg-loader";
35 | ---
36 |
37 |
41 | ```
42 |
43 | Be sure to use a raw import: `?raw`, or you'll import the URL rather than the
44 | SVG source.
45 |
46 | [](https://stackblitz.com/github/jasikpark/astro-svg-loader/)
47 |
48 | ## License
49 |
50 | [](https://app.fossa.com/projects/git%2Bgithub.com%2Fjasikpark%2Fastro-svg-loader?ref=badge_large)
51 |
52 | ## Inspiration
53 |
54 | - [astro-icon](https://github.com/natemoo-re/astro-icon)
55 | - [svgr](https://react-svgr.com/)
56 |
--------------------------------------------------------------------------------
/astro.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "astro/config";
2 |
3 | export default defineConfig({
4 | vite: {
5 | test: {
6 | sequence: {
7 | shuffle: true,
8 | },
9 | },
10 | },
11 | });
12 |
--------------------------------------------------------------------------------
/env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/eslint-plugin-import.d.ts:
--------------------------------------------------------------------------------
1 | declare module "eslint-plugin-import";
2 |
--------------------------------------------------------------------------------
/eslint.config.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 |
3 | import eslint from "@eslint/js";
4 | import eslintPluginAstro from "eslint-plugin-astro";
5 | import tseslint from "typescript-eslint";
6 | import { flatConfigs as importFlatConfigs } from "eslint-plugin-import";
7 |
8 | export default tseslint.config(
9 | {
10 | ignores: ["dist", ".astro/"],
11 | },
12 | eslint.configs.recommended,
13 | ...tseslint.configs.strictTypeChecked,
14 | ...tseslint.configs.stylisticTypeChecked,
15 | {
16 | languageOptions: {
17 | parserOptions: {
18 | projectService: true,
19 | // @ts-expect-error -- dirname is missing on the type :shrug:
20 | tsconfigRootDir: import.meta.dirname,
21 | },
22 | },
23 | rules: {
24 | "@typescript-eslint/consistent-type-definitions": ["error", "type"],
25 | },
26 | },
27 | importFlatConfigs.recommended,
28 | importFlatConfigs.typescript,
29 | {
30 | settings: {
31 | "import/resolver": {
32 | typescript: true,
33 | },
34 | },
35 | },
36 | ...eslintPluginAstro.configs["jsx-a11y-recommended"],
37 | );
38 |
--------------------------------------------------------------------------------
/index.ts:
--------------------------------------------------------------------------------
1 | // Do not write code directly here, instead use the `src` folder!
2 | // Then, use this file to export everything you want your user to access.
3 |
4 | // TS has issues validating that Astro components exist when imported.
5 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment
6 | // @ts-ignore
7 | import Svg from "./src/components/Svg/Svg.astro";
8 |
9 | export default Svg;
10 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@jasikpark/astro-svg-loader",
3 | "repository": {
4 | "url": "https://github.com/jasikpark/astro-svg-loader"
5 | },
6 | "license": "MIT",
7 | "version": "0.3.1",
8 | "type": "module",
9 | "exports": {
10 | ".": "./index.ts"
11 | },
12 | "files": [
13 | "src",
14 | "index.ts"
15 | ],
16 | "keywords": [
17 | "astro-component",
18 | "svg",
19 | "svg-loader"
20 | ],
21 | "scripts": {
22 | "preinstall": "npx only-allow pnpm",
23 | "dev": "astro dev",
24 | "build": "astro build",
25 | "preview": "astro preview",
26 | "format": "prettier -w .",
27 | "format:check": "prettier -c .",
28 | "test": "vitest --run",
29 | "test:watch": "vitest",
30 | "test:watch:ui": "vitest --ui",
31 | "lint": "eslint",
32 | "check:astro": "astro check",
33 | "check:ts": "tsc -p .",
34 | "check": "pnpm run '/^check:.*/'",
35 | "version": "changeset version && pnpm install --no-frozen-lockfile && pnpm format",
36 | "changeset": "changeset"
37 | },
38 | "devDependencies": {
39 | "@astrojs/check": "0.9.4",
40 | "@changesets/cli": "2.29.1",
41 | "@eslint/js": "9.24.0",
42 | "@ianvs/prettier-plugin-sort-imports": "4.4.1",
43 | "@types/eslint__js": "8.42.3",
44 | "@typescript-eslint/parser": "8.30.1",
45 | "@vitest/ui": "3.1.2",
46 | "astro": "4.16.18",
47 | "eslint": "8.57.1",
48 | "eslint-plugin-astro": "1.3.1",
49 | "eslint-plugin-import": "2.31.0",
50 | "eslint-plugin-jsx-a11y": "6.10.2",
51 | "fast-check": "4.1.1",
52 | "prettier": "3.5.3",
53 | "prettier-plugin-astro": "0.14.1",
54 | "shiki": "2.5.0",
55 | "typescript": "5.8.3",
56 | "typescript-eslint": "8.30.1",
57 | "vitest": "3.1.2"
58 | },
59 | "peerDependencies": {
60 | "astro": "^2.0.0 || ^3.0.0 || ^4.0.0"
61 | },
62 | "engines": {
63 | "node": ">18",
64 | "pnpm": ">=7.9.5"
65 | },
66 | "packageManager": "pnpm@10.8.1",
67 | "dependencies": {
68 | "ultrahtml": "1.5.3"
69 | },
70 | "publishConfig": {
71 | "provenance": true
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/assets/astro-full-logo-light.svg:
--------------------------------------------------------------------------------
1 |
10 |
--------------------------------------------------------------------------------
/src/components/Story.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import { Code } from "astro/components";
3 |
4 | type Props = {
5 | title: string;
6 | /** Provide a code example for the story */
7 | source: string;
8 | };
9 |
10 | const { title, source } = Astro.props as Props;
11 | ---
12 |
13 |
14 |
15 | {title}:
16 |
17 |
18 |
19 | {source ?
: null}
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
38 |
--------------------------------------------------------------------------------
/src/components/Svg/Svg.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import type { SVGAttributes } from "../../types";
3 | import { overrideSvgAttributes } from "./overrideSvgAttributes";
4 |
5 | // accepts SVG attributes which will override the ones in the original SVG
6 | export interface Props extends SVGAttributes {
7 | /** pass an `import("*.svg?raw")` to `Svg` for the svg file to use */
8 | src: Promise;
9 | }
10 |
11 | const { src, ...attributeOverrides } = Astro.props as Props;
12 |
13 | const SVG_NOT_FOUND = "Could not find an SVG at the provided `src`";
14 | const ALT_NOT_ALLOWED =
15 | "`alt` is not a valid prop for svg, perhaps you mean `aria-label` or `aria-hidden`?";
16 |
17 | const svgImport = await src;
18 |
19 | if (!svgImport) {
20 | throw new Error(SVG_NOT_FOUND);
21 | }
22 |
23 | const svgSource = svgImport.default;
24 |
25 | if (!svgSource) {
26 | throw new Error(SVG_NOT_FOUND);
27 | }
28 |
29 | if ("alt" in attributeOverrides) {
30 | throw new Error(ALT_NOT_ALLOWED);
31 | }
32 |
33 | if (!svgSource.trimStart().toLowerCase().startsWith("
42 |
--------------------------------------------------------------------------------
/src/components/Svg/overrideSvgAttributes.bench.ts:
--------------------------------------------------------------------------------
1 | import type { SVGAttributes } from "../../types.js";
2 | import { overrideSvgAttributes } from "./overrideSvgAttributes.js";
3 | import fc from "fast-check";
4 | import { parse } from "ultrahtml";
5 | import { bench, describe, expect } from "vitest";
6 |
7 | const svgArbitrary = fc
8 | .record({
9 | start: fc.mixedCase(fc.constantFrom("")),
12 | })
13 | .map(({ start, middle, end }) => `${start}${middle}${end}`);
14 |
15 | const SVGAttributesArbitrary = fc.record({
16 | height: fc.option(fc.integer()),
17 | width: fc.option(fc.integer()),
18 | "aria-hidden": fc.option(fc.boolean()),
19 | "aria-label": fc.option(fc.string()),
20 | viewBox: fc.option(fc.string()),
21 | fill: fc.option(fc.string()),
22 | xmlns: fc.option(fc.string()),
23 | });
24 |
25 | describe("overrideSvgAtrributes", () => {
26 | bench("should return the same output if no overrides are given", async () => {
27 | expect(await overrideSvgAttributes("")).toBe("");
28 | expect(await overrideSvgAttributes("