diff --git a/plugin/README.md b/plugin/README.md index 2c25772..82e22d6 100644 --- a/plugin/README.md +++ b/plugin/README.md @@ -361,31 +361,57 @@ Available client types: > [!NOTE] > If your project also uses `vite/client` types, its `*.svg` declaration (which types SVGs as `string`) may conflict with the plugin's component types. -### Custom module declarations +### Typing an icon -TypeScript can't match two wildcards in a module specifier, so named sprite queries (`?sprite=name`) and combined query params (`?current-color&sprite=icons`) aren't covered by the built-in declarations. Add your own in a `.d.ts` file included by your tsconfig: +Each target exports its component type as `SvgComponent` from `@svg-jar/plugin/types/`. Use it to type a variable, prop or config field that holds an SVG import, instead of writing out the type yourself. ```ts -// svg.d.ts (React example) -declare module '*.svg?sprite=icons' { - import type { FC, SVGAttributes, ReactNode } from 'react'; +// sidebar.ts (Ember example) +import type { SvgComponent } from '@svg-jar/plugin/types/ember'; +import UsersIcon from './icons/users.svg'; - const Component: FC & { children?: ReactNode }>; - export default Component; +interface Section { + title: string; + icon: SvgComponent; } + +export const sections: Section[] = [{ title: 'Users', icon: UsersIcon }]; ``` -For the `dom` target: +```tsx +// React example +import type { SvgComponent } from '@svg-jar/plugin/types/react'; + +function NavLink({ href, icon: Icon, label }: { href: string; icon: SvgComponent; label: string }) { + return ( + + + ); +} +``` + +The `dom` target also exports `SvgOptions`, the options type its factory function accepts. + +Importing from `@svg-jar/plugin/types/` only gives you the types. The `*.svg` module declarations still come from the `client` entry in your tsconfig. + +### Custom module declarations + +TypeScript can't match two wildcards in a module specifier, so named sprite queries (`?sprite=name`) and combined query params (`?current-color&sprite=icons`) aren't covered by the built-in declarations. Add your own in a `.d.ts` file included by your tsconfig: ```ts +// svg.d.ts (Ember example) declare module '*.svg?sprite=icons' { - import type { SvgOptions } from '@svg-jar/plugin/runtime/dom'; + import type { SvgComponent } from '@svg-jar/plugin/types/ember'; - const component: (options?: SvgOptions) => SVGSVGElement; - export default component; + const Component: SvgComponent; + export default Component; } ``` +For other targets, change `ember` in the import path to your target, e.g. `@svg-jar/plugin/types/react`. + The `?file` query always exports a `string` URL regardless of target, so no custom declaration is needed for combined file queries. ## How it works diff --git a/plugin/client/dom-types.d.ts b/plugin/client/dom-types.d.ts new file mode 100644 index 0000000..07de11d --- /dev/null +++ b/plugin/client/dom-types.d.ts @@ -0,0 +1,23 @@ +// Named types for SVG imports handled by @svg-jar/plugin with the DOM target. +// +// import type { SvgComponent } from '@svg-jar/plugin/types/dom'; +// +// This file only exports types. To type the `*.svg` imports themselves, add +// "@svg-jar/plugin/client/dom" to "types" in your tsconfig.json. + +/** + * Options for the rendered SVG element. `title` and `desc` create child + * elements for accessibility. Any other property is set as an attribute on + * the `` element. + */ +export interface SvgOptions { + /** Creates a `` child element for accessibility. */ + title?: string; + /** Creates a `` child element for accessibility. */ + desc?: string; + /** Any other properties are set as attributes on the `` element. */ + [attr: string]: string | undefined; +} + +/** Factory function that creates a new SVG DOM element on each call. */ +export type SvgComponent = (options?: SvgOptions) => SVGSVGElement; diff --git a/plugin/client/dom.d.ts b/plugin/client/dom.d.ts index 70a3b04..10386aa 100644 --- a/plugin/client/dom.d.ts +++ b/plugin/client/dom.d.ts @@ -7,35 +7,35 @@ // "types": ["@svg-jar/plugin/client/dom"] // } // } - -interface SvgOptions { - /** Creates a `` child element for accessibility. */ - title?: string; - /** Creates a `` child element for accessibility. */ - desc?: string; - /** Any other properties are set as attributes on the `` element. */ - [attr: string]: string | undefined; -} +// +// The component type is exported by name from "@svg-jar/plugin/types/dom". declare module '*.svg' { - /** Factory function that creates a new SVG DOM element on each call. */ - const component: (options?: SvgOptions) => SVGSVGElement; - export default component; + import type { SvgComponent } from '@svg-jar/plugin/types/dom'; + + const Component: SvgComponent; + export default Component; } declare module '*.svg?current-color' { - const component: (options?: SvgOptions) => SVGSVGElement; - export default component; + import type { SvgComponent } from '@svg-jar/plugin/types/dom'; + + const Component: SvgComponent; + export default Component; } declare module '*.svg?skip-current-color' { - const component: (options?: SvgOptions) => SVGSVGElement; - export default component; + import type { SvgComponent } from '@svg-jar/plugin/types/dom'; + + const Component: SvgComponent; + export default Component; } declare module '*.svg?unsafe-inline' { - const component: (options?: SvgOptions) => SVGSVGElement; - export default component; + import type { SvgComponent } from '@svg-jar/plugin/types/dom'; + + const Component: SvgComponent; + export default Component; } declare module '*.svg?file' { diff --git a/plugin/client/ember-types.d.ts b/plugin/client/ember-types.d.ts new file mode 100644 index 0000000..b4c31b5 --- /dev/null +++ b/plugin/client/ember-types.d.ts @@ -0,0 +1,16 @@ +// Named types for SVG imports handled by @svg-jar/plugin with the Ember target. +// +// import type { SvgComponent } from '@svg-jar/plugin/types/ember'; +// +// This file only exports types. To type the `*.svg` imports themselves, add +// "@svg-jar/plugin/client/ember" to "types" in your tsconfig.json. + +import type { ComponentLike } from '@glint/template'; + +/** Ember component that renders an SVG import. */ +export type SvgComponent = ComponentLike<{ + Element: SVGSVGElement; + Blocks: { + default: []; + }; +}>; diff --git a/plugin/client/ember.d.ts b/plugin/client/ember.d.ts index 9771560..3a7ee55 100644 --- a/plugin/client/ember.d.ts +++ b/plugin/client/ember.d.ts @@ -7,52 +7,34 @@ // "types": ["@svg-jar/plugin/client/ember"] // } // } +// +// The component type is exported by name from "@svg-jar/plugin/types/ember". declare module '*.svg' { - import type { ComponentLike } from '@glint/template'; - - const Component: ComponentLike<{ - Element: SVGSVGElement; - Blocks: { - default: []; - }; - }>; + import type { SvgComponent } from '@svg-jar/plugin/types/ember'; + + const Component: SvgComponent; export default Component; } declare module '*.svg?current-color' { - import type { ComponentLike } from '@glint/template'; - - const Component: ComponentLike<{ - Element: SVGSVGElement; - Blocks: { - default: []; - }; - }>; + import type { SvgComponent } from '@svg-jar/plugin/types/ember'; + + const Component: SvgComponent; export default Component; } declare module '*.svg?skip-current-color' { - import type { ComponentLike } from '@glint/template'; - - const Component: ComponentLike<{ - Element: SVGSVGElement; - Blocks: { - default: []; - }; - }>; + import type { SvgComponent } from '@svg-jar/plugin/types/ember'; + + const Component: SvgComponent; export default Component; } declare module '*.svg?unsafe-inline' { - import type { ComponentLike } from '@glint/template'; - - const Component: ComponentLike<{ - Element: SVGSVGElement; - Blocks: { - default: []; - }; - }>; + import type { SvgComponent } from '@svg-jar/plugin/types/ember'; + + const Component: SvgComponent; export default Component; } diff --git a/plugin/client/preact-types.d.ts b/plugin/client/preact-types.d.ts new file mode 100644 index 0000000..a52eb29 --- /dev/null +++ b/plugin/client/preact-types.d.ts @@ -0,0 +1,11 @@ +// Named types for SVG imports handled by @svg-jar/plugin with the Preact target. +// +// import type { SvgComponent } from '@svg-jar/plugin/types/preact'; +// +// This file only exports types. To type the `*.svg` imports themselves, add +// "@svg-jar/plugin/client/preact" to "types" in your tsconfig.json. + +import type { FunctionComponent, JSX } from 'preact'; + +/** Preact component that renders an SVG import. */ +export type SvgComponent = FunctionComponent>; diff --git a/plugin/client/preact.d.ts b/plugin/client/preact.d.ts index f482a7d..00ef5a1 100644 --- a/plugin/client/preact.d.ts +++ b/plugin/client/preact.d.ts @@ -7,32 +7,34 @@ // "types": ["@svg-jar/plugin/client/preact"] // } // } +// +// The component type is exported by name from "@svg-jar/plugin/types/preact". declare module '*.svg' { - import type { FunctionComponent, JSX } from 'preact'; + import type { SvgComponent } from '@svg-jar/plugin/types/preact'; - const Component: FunctionComponent>; + const Component: SvgComponent; export default Component; } declare module '*.svg?current-color' { - import type { FunctionComponent, JSX } from 'preact'; + import type { SvgComponent } from '@svg-jar/plugin/types/preact'; - const Component: FunctionComponent>; + const Component: SvgComponent; export default Component; } declare module '*.svg?skip-current-color' { - import type { FunctionComponent, JSX } from 'preact'; + import type { SvgComponent } from '@svg-jar/plugin/types/preact'; - const Component: FunctionComponent>; + const Component: SvgComponent; export default Component; } declare module '*.svg?unsafe-inline' { - import type { FunctionComponent, JSX } from 'preact'; + import type { SvgComponent } from '@svg-jar/plugin/types/preact'; - const Component: FunctionComponent>; + const Component: SvgComponent; export default Component; } diff --git a/plugin/client/react-types.d.ts b/plugin/client/react-types.d.ts new file mode 100644 index 0000000..a64a49d --- /dev/null +++ b/plugin/client/react-types.d.ts @@ -0,0 +1,11 @@ +// Named types for SVG imports handled by @svg-jar/plugin with the React target. +// +// import type { SvgComponent } from '@svg-jar/plugin/types/react'; +// +// This file only exports types. To type the `*.svg` imports themselves, add +// "@svg-jar/plugin/client/react" to "types" in your tsconfig.json. + +import type { FC, SVGAttributes, ReactNode } from 'react'; + +/** React component that renders an SVG import. */ +export type SvgComponent = FC & { children?: ReactNode }>; diff --git a/plugin/client/react.d.ts b/plugin/client/react.d.ts index 61fb05a..c03c36c 100644 --- a/plugin/client/react.d.ts +++ b/plugin/client/react.d.ts @@ -7,32 +7,34 @@ // "types": ["@svg-jar/plugin/client/react"] // } // } +// +// The component type is exported by name from "@svg-jar/plugin/types/react". declare module '*.svg' { - import type { FC, SVGAttributes, ReactNode } from 'react'; + import type { SvgComponent } from '@svg-jar/plugin/types/react'; - const Component: FC & { children?: ReactNode }>; + const Component: SvgComponent; export default Component; } declare module '*.svg?current-color' { - import type { FC, SVGAttributes, ReactNode } from 'react'; + import type { SvgComponent } from '@svg-jar/plugin/types/react'; - const Component: FC & { children?: ReactNode }>; + const Component: SvgComponent; export default Component; } declare module '*.svg?skip-current-color' { - import type { FC, SVGAttributes, ReactNode } from 'react'; + import type { SvgComponent } from '@svg-jar/plugin/types/react'; - const Component: FC & { children?: ReactNode }>; + const Component: SvgComponent; export default Component; } declare module '*.svg?unsafe-inline' { - import type { FC, SVGAttributes, ReactNode } from 'react'; + import type { SvgComponent } from '@svg-jar/plugin/types/react'; - const Component: FC & { children?: ReactNode }>; + const Component: SvgComponent; export default Component; } diff --git a/plugin/client/solid-types.d.ts b/plugin/client/solid-types.d.ts new file mode 100644 index 0000000..6ef7b97 --- /dev/null +++ b/plugin/client/solid-types.d.ts @@ -0,0 +1,11 @@ +// Named types for SVG imports handled by @svg-jar/plugin with the Solid target. +// +// import type { SvgComponent } from '@svg-jar/plugin/types/solid'; +// +// This file only exports types. To type the `*.svg` imports themselves, add +// "@svg-jar/plugin/client/solid" to "types" in your tsconfig.json. + +import type { Component, JSX, ParentProps } from 'solid-js'; + +/** Solid component that renders an SVG import. */ +export type SvgComponent = Component>>; diff --git a/plugin/client/solid.d.ts b/plugin/client/solid.d.ts index 05f5695..df832ca 100644 --- a/plugin/client/solid.d.ts +++ b/plugin/client/solid.d.ts @@ -7,33 +7,35 @@ // "types": ["@svg-jar/plugin/client/solid"] // } // } +// +// The component type is exported by name from "@svg-jar/plugin/types/solid". declare module '*.svg' { - import type { Component, JSX, ParentProps } from 'solid-js'; + import type { SvgComponent } from '@svg-jar/plugin/types/solid'; - const component: Component>>; - export default component; + const Component: SvgComponent; + export default Component; } declare module '*.svg?current-color' { - import type { Component, JSX, ParentProps } from 'solid-js'; + import type { SvgComponent } from '@svg-jar/plugin/types/solid'; - const component: Component>>; - export default component; + const Component: SvgComponent; + export default Component; } declare module '*.svg?skip-current-color' { - import type { Component, JSX, ParentProps } from 'solid-js'; + import type { SvgComponent } from '@svg-jar/plugin/types/solid'; - const component: Component>>; - export default component; + const Component: SvgComponent; + export default Component; } declare module '*.svg?unsafe-inline' { - import type { Component, JSX, ParentProps } from 'solid-js'; + import type { SvgComponent } from '@svg-jar/plugin/types/solid'; - const component: Component>>; - export default component; + const Component: SvgComponent; + export default Component; } declare module '*.svg?file' { diff --git a/plugin/client/vue-types.d.ts b/plugin/client/vue-types.d.ts new file mode 100644 index 0000000..e862bf2 --- /dev/null +++ b/plugin/client/vue-types.d.ts @@ -0,0 +1,11 @@ +// Named types for SVG imports handled by @svg-jar/plugin with the Vue target. +// +// import type { SvgComponent } from '@svg-jar/plugin/types/vue'; +// +// This file only exports types. To type the `*.svg` imports themselves, add +// "@svg-jar/plugin/client/vue" to "types" in your tsconfig.json. + +import type { Component } from 'vue'; + +/** Vue component that renders an SVG import. */ +export type SvgComponent = Component; diff --git a/plugin/client/vue.d.ts b/plugin/client/vue.d.ts index 8c87002..50b4c04 100644 --- a/plugin/client/vue.d.ts +++ b/plugin/client/vue.d.ts @@ -7,33 +7,35 @@ // "types": ["@svg-jar/plugin/client/vue"] // } // } +// +// The component type is exported by name from "@svg-jar/plugin/types/vue". declare module '*.svg' { - import type { Component } from 'vue'; + import type { SvgComponent } from '@svg-jar/plugin/types/vue'; - const component: Component; - export default component; + const Component: SvgComponent; + export default Component; } declare module '*.svg?current-color' { - import type { Component } from 'vue'; + import type { SvgComponent } from '@svg-jar/plugin/types/vue'; - const component: Component; - export default component; + const Component: SvgComponent; + export default Component; } declare module '*.svg?skip-current-color' { - import type { Component } from 'vue'; + import type { SvgComponent } from '@svg-jar/plugin/types/vue'; - const component: Component; - export default component; + const Component: SvgComponent; + export default Component; } declare module '*.svg?unsafe-inline' { - import type { Component } from 'vue'; + import type { SvgComponent } from '@svg-jar/plugin/types/vue'; - const component: Component; - export default component; + const Component: SvgComponent; + export default Component; } declare module '*.svg?file' { diff --git a/plugin/client/web-component-types.d.ts b/plugin/client/web-component-types.d.ts new file mode 100644 index 0000000..73f974a --- /dev/null +++ b/plugin/client/web-component-types.d.ts @@ -0,0 +1,9 @@ +// Named types for SVG imports handled by @svg-jar/plugin with the web-component target. +// +// import type { SvgComponent } from '@svg-jar/plugin/types/web-component'; +// +// This file only exports types. To type the `*.svg` imports themselves, add +// "@svg-jar/plugin/client/web-component" to "types" in your tsconfig.json. + +/** Custom element class to register with customElements.define(). */ +export type SvgComponent = typeof HTMLElement; diff --git a/plugin/client/web-component.d.ts b/plugin/client/web-component.d.ts index bffd1c6..06cea02 100644 --- a/plugin/client/web-component.d.ts +++ b/plugin/client/web-component.d.ts @@ -7,26 +7,35 @@ // "types": ["@svg-jar/plugin/client/web-component"] // } // } +// +// The component type is exported by name from "@svg-jar/plugin/types/web-component". declare module '*.svg' { - /** Custom element class to register with customElements.define(). */ - const ElementClass: typeof HTMLElement; - export default ElementClass; + import type { SvgComponent } from '@svg-jar/plugin/types/web-component'; + + const Component: SvgComponent; + export default Component; } declare module '*.svg?current-color' { - const ElementClass: typeof HTMLElement; - export default ElementClass; + import type { SvgComponent } from '@svg-jar/plugin/types/web-component'; + + const Component: SvgComponent; + export default Component; } declare module '*.svg?skip-current-color' { - const ElementClass: typeof HTMLElement; - export default ElementClass; + import type { SvgComponent } from '@svg-jar/plugin/types/web-component'; + + const Component: SvgComponent; + export default Component; } declare module '*.svg?unsafe-inline' { - const ElementClass: typeof HTMLElement; - export default ElementClass; + import type { SvgComponent } from '@svg-jar/plugin/types/web-component'; + + const Component: SvgComponent; + export default Component; } declare module '*.svg?file' { diff --git a/plugin/package.json b/plugin/package.json index 42e27a9..939348b 100644 --- a/plugin/package.json +++ b/plugin/package.json @@ -71,6 +71,27 @@ "./client/web-component": { "types": "./client/web-component.d.ts" }, + "./types/dom": { + "types": "./client/dom-types.d.ts" + }, + "./types/ember": { + "types": "./client/ember-types.d.ts" + }, + "./types/react": { + "types": "./client/react-types.d.ts" + }, + "./types/preact": { + "types": "./client/preact-types.d.ts" + }, + "./types/vue": { + "types": "./client/vue-types.d.ts" + }, + "./types/solid": { + "types": "./client/solid-types.d.ts" + }, + "./types/web-component": { + "types": "./client/web-component-types.d.ts" + }, "./package.json": "./package.json" }, "main": "./dist/index.js", @@ -102,7 +123,9 @@ "unplugin": "^3.0.0" }, "devDependencies": { + "@glint/template": "^1.11.0", "@playwright/test": "^1.59.1", + "@types/react": "^19.2.14", "execa": "catalog:", "rolldown": "catalog:", "rollup": "catalog:", diff --git a/plugin/runtime/react.ts b/plugin/runtime/react.ts index 7cca905..6f49d77 100644 --- a/plugin/runtime/react.ts +++ b/plugin/runtime/react.ts @@ -1,6 +1,6 @@ -/* eslint-disable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-return */ -// @ts-expect-error react is a peer dependency provided by the consuming app -import { createElement, useCallback } from 'react'; +import { createElement, useCallback, type ReactElement, type ReactNode, type SVGProps } from 'react'; + +type SvgProps = SVGProps & { children?: ReactNode }; /** * Converts attribute names to the camelCase prop names React expects, @@ -36,18 +36,12 @@ export const createSvg: ( height: string | null, href: string, rootAttrs?: Record | null, -) => (props: unknown) => Record = /*#__NO_SIDE_EFFECTS__*/ ( - viewBox, - width, - height, - href, - rootAttrs, -) => { +) => (props: SvgProps) => ReactElement = /*#__NO_SIDE_EFFECTS__*/ (viewBox, width, height, href, rootAttrs) => { const useEl = createElement('use', { href }); const rootProps = toReactProps(rootAttrs); - return (props: unknown) => { - const { children, ...rest } = props as Record; + return (props: SvgProps) => { + const { children, ...rest } = props; return createElement( 'svg', { viewBox: viewBox ?? undefined, width: width ?? undefined, height: height ?? undefined, ...rootProps, ...rest }, @@ -78,17 +72,11 @@ export const createSvgInline: ( height: string | null, xml: string, rootAttrs?: Record | null, -) => (props: unknown) => Record = /*#__NO_SIDE_EFFECTS__*/ ( - viewBox, - width, - height, - xml, - rootAttrs, -) => { +) => (props: SvgProps) => ReactElement = /*#__NO_SIDE_EFFECTS__*/ (viewBox, width, height, xml, rootAttrs) => { const rootProps = toReactProps(rootAttrs); - return (props: unknown) => { - const { children, ...rest } = props as Record; + return (props: SvgProps) => { + const { children, ...rest } = props; const setMarkup = useCallback((node: SVGGElement | null) => { if (node) node.innerHTML = xml; }, []); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fafe367..052b947 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -62,9 +62,6 @@ importers: '@eksml/xml': specifier: ^0.2.0 version: 0.2.0 - '@glint/template': - specifier: '>=1.0.0' - version: 1.11.0 ember-source: specifier: '>=6.1.0' version: 6.10.1(@glimmer/component@2.1.1)(rsvp@4.8.5) @@ -90,9 +87,15 @@ importers: specifier: '>=3.0.0' version: 3.5.32(typescript@6.0.2) devDependencies: + '@glint/template': + specifier: ^1.11.0 + version: 1.11.0 '@playwright/test': specifier: ^1.59.1 version: 1.59.1 + '@types/react': + specifier: ^19.2.14 + version: 19.2.14 execa: specifier: 'catalog:' version: 9.6.1 @@ -178,6 +181,9 @@ importers: '@embroider/vite': specifier: 1.7.2 version: 1.7.2(@embroider/core@4.4.7(@glint/template@1.11.0))(@glint/template@1.11.0)(vite@7.3.2(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)) + '@glint/template': + specifier: ^1.11.0 + version: 1.11.0 '@rollup/plugin-babel': specifier: 6.1.0 version: 6.1.0(@babel/core@7.29.0)(@types/babel__core@7.20.5)(rollup@4.60.1) @@ -187,6 +193,9 @@ importers: babel-plugin-ember-template-compilation: specifier: 4.0.0 version: 4.0.0 + typescript: + specifier: 'catalog:' + version: 6.0.2 vite: specifier: 7.3.2 version: 7.3.2(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0) diff --git a/test-projects/rolldown/vanilla/src/svg-types.ts b/test-projects/rolldown/vanilla/src/svg-types.ts new file mode 100644 index 0000000..24b50b5 --- /dev/null +++ b/test-projects/rolldown/vanilla/src/svg-types.ts @@ -0,0 +1,14 @@ +// Type tests for the SVG import declarations. `pnpm typecheck` fails if an +// SVG import does not have the named `SvgComponent` type. + +import type { SvgComponent } from '@svg-jar/plugin/types/dom'; +import Arrow from './icons/arrow.svg'; +import Square from './icons/square.svg?unsafe-inline'; +import Circle from './icons/circle.svg?sprite=shapes'; + +export const arrow: SvgComponent = Arrow; +export const square: SvgComponent = Square; +export const circle: SvgComponent = Circle; + +// @ts-expect-error an SVG import is a component, so this fails unless the type is `any` +export const notANumber: number = Arrow; diff --git a/test-projects/rolldown/vanilla/src/types.d.ts b/test-projects/rolldown/vanilla/src/types.d.ts index b22f559..0d1e804 100644 --- a/test-projects/rolldown/vanilla/src/types.d.ts +++ b/test-projects/rolldown/vanilla/src/types.d.ts @@ -1,6 +1,6 @@ declare module '*.svg?sprite=shapes' { - import type { SvgOptions } from '@svg-jar/plugin/runtime/dom'; + import type { SvgComponent } from '@svg-jar/plugin/types/dom'; - const component: (options?: SvgOptions) => SVGSVGElement; - export default component; + const Component: SvgComponent; + export default Component; } diff --git a/test-projects/tsdown/internal-package/src/svg-types.ts b/test-projects/tsdown/internal-package/src/svg-types.ts new file mode 100644 index 0000000..57e656a --- /dev/null +++ b/test-projects/tsdown/internal-package/src/svg-types.ts @@ -0,0 +1,12 @@ +// Type tests for the SVG import declarations. `pnpm typecheck` fails if an +// SVG import does not have the named `SvgComponent` type. + +import type { SvgComponent } from '@svg-jar/plugin/types/dom'; +import Arrow from './icons/arrow.svg'; +import Square from './icons/square.svg?unsafe-inline'; + +export const arrow: SvgComponent = Arrow; +export const square: SvgComponent = Square; + +// @ts-expect-error an SVG import is a component, so this fails unless the type is `any` +export const notANumber: number = Arrow; diff --git a/test-projects/vite/ember/package.json b/test-projects/vite/ember/package.json index ddfe6dd..e5a041a 100644 --- a/test-projects/vite/ember/package.json +++ b/test-projects/vite/ember/package.json @@ -22,7 +22,8 @@ "scripts": { "build": "vite build", "dev": "vite", - "start": "vite" + "start": "vite", + "typecheck": "tsc --noEmit" }, "dependencies": { "@ember/test-waiters": "^4.1.1", @@ -40,9 +41,11 @@ "@babel/runtime": "7.29.2", "@embroider/core": "4.4.7", "@embroider/vite": "1.7.2", + "@glint/template": "^1.11.0", "@rollup/plugin-babel": "6.1.0", "@svg-jar/plugin": "workspace:*", "babel-plugin-ember-template-compilation": "4.0.0", + "typescript": "catalog:", "vite": "7.3.2" }, "engines": { diff --git a/test-projects/vite/ember/tsconfig.json b/test-projects/vite/ember/tsconfig.json new file mode 100644 index 0000000..c66dcd4 --- /dev/null +++ b/test-projects/vite/ember/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "bundler", + "lib": ["ES2022", "DOM"], + "types": ["@svg-jar/plugin/client/ember"], + "strict": true, + "noEmit": true + }, + "include": ["types"] +} diff --git a/test-projects/vite/ember/types/svg-sprites.d.ts b/test-projects/vite/ember/types/svg-sprites.d.ts new file mode 100644 index 0000000..a301ef8 --- /dev/null +++ b/test-projects/vite/ember/types/svg-sprites.d.ts @@ -0,0 +1,6 @@ +declare module '*.svg?sprite=shapes' { + import type { SvgComponent } from '@svg-jar/plugin/types/ember'; + + const Component: SvgComponent; + export default Component; +} diff --git a/test-projects/vite/ember/types/svg-types.ts b/test-projects/vite/ember/types/svg-types.ts new file mode 100644 index 0000000..10716f7 --- /dev/null +++ b/test-projects/vite/ember/types/svg-types.ts @@ -0,0 +1,14 @@ +// Type tests for the SVG import declarations. `pnpm typecheck` fails if an +// SVG import does not have the named `SvgComponent` type. + +import type { SvgComponent } from '@svg-jar/plugin/types/ember'; +import Arrow from '../app/icons/arrow.svg'; +import Square from '../app/icons/square.svg?unsafe-inline'; +import Circle from '../app/icons/circle.svg?sprite=shapes'; + +export const arrow: SvgComponent = Arrow; +export const square: SvgComponent = Square; +export const circle: SvgComponent = Circle; + +// @ts-expect-error an SVG import is a component, so this fails unless the type is `any` +export const notANumber: number = Arrow; diff --git a/test-projects/vite/preact/src/svg-types.ts b/test-projects/vite/preact/src/svg-types.ts new file mode 100644 index 0000000..386a034 --- /dev/null +++ b/test-projects/vite/preact/src/svg-types.ts @@ -0,0 +1,14 @@ +// Type tests for the SVG import declarations. `pnpm typecheck` fails if an +// SVG import does not have the named `SvgComponent` type. + +import type { SvgComponent } from '@svg-jar/plugin/types/preact'; +import Arrow from './icons/arrow.svg'; +import Square from './icons/square.svg?unsafe-inline'; +import Circle from './icons/circle.svg?sprite=shapes'; + +export const arrow: SvgComponent = Arrow; +export const square: SvgComponent = Square; +export const circle: SvgComponent = Circle; + +// @ts-expect-error an SVG import is a component, so this fails unless the type is `any` +export const notANumber: number = Arrow; diff --git a/test-projects/vite/preact/src/types.d.ts b/test-projects/vite/preact/src/types.d.ts index 0b7d44a..daa8b93 100644 --- a/test-projects/vite/preact/src/types.d.ts +++ b/test-projects/vite/preact/src/types.d.ts @@ -1,6 +1,6 @@ declare module '*.svg?sprite=shapes' { - import type { FunctionComponent, JSX } from 'preact'; + import type { SvgComponent } from '@svg-jar/plugin/types/preact'; - const Component: FunctionComponent>; + const Component: SvgComponent; export default Component; } diff --git a/test-projects/vite/react/src/svg-types.ts b/test-projects/vite/react/src/svg-types.ts new file mode 100644 index 0000000..a87fe1a --- /dev/null +++ b/test-projects/vite/react/src/svg-types.ts @@ -0,0 +1,14 @@ +// Type tests for the SVG import declarations. `pnpm typecheck` fails if an +// SVG import does not have the named `SvgComponent` type. + +import type { SvgComponent } from '@svg-jar/plugin/types/react'; +import Arrow from './icons/arrow.svg'; +import Square from './icons/square.svg?unsafe-inline'; +import Circle from './icons/circle.svg?sprite=shapes'; + +export const arrow: SvgComponent = Arrow; +export const square: SvgComponent = Square; +export const circle: SvgComponent = Circle; + +// @ts-expect-error an SVG import is a component, so this fails unless the type is `any` +export const notANumber: number = Arrow; diff --git a/test-projects/vite/react/src/types.d.ts b/test-projects/vite/react/src/types.d.ts index 3c473d2..d74a04d 100644 --- a/test-projects/vite/react/src/types.d.ts +++ b/test-projects/vite/react/src/types.d.ts @@ -1,6 +1,6 @@ declare module '*.svg?sprite=shapes' { - import type { FC, SVGAttributes, ReactNode } from 'react'; + import type { SvgComponent } from '@svg-jar/plugin/types/react'; - const Component: FC & { children?: ReactNode }>; + const Component: SvgComponent; export default Component; } diff --git a/test-projects/vite/solid/src/svg-types.ts b/test-projects/vite/solid/src/svg-types.ts new file mode 100644 index 0000000..80d8824 --- /dev/null +++ b/test-projects/vite/solid/src/svg-types.ts @@ -0,0 +1,14 @@ +// Type tests for the SVG import declarations. `pnpm typecheck` fails if an +// SVG import does not have the named `SvgComponent` type. + +import type { SvgComponent } from '@svg-jar/plugin/types/solid'; +import Arrow from './icons/arrow.svg'; +import Square from './icons/square.svg?unsafe-inline'; +import Circle from './icons/circle.svg?sprite=shapes'; + +export const arrow: SvgComponent = Arrow; +export const square: SvgComponent = Square; +export const circle: SvgComponent = Circle; + +// @ts-expect-error an SVG import is a component, so this fails unless the type is `any` +export const notANumber: number = Arrow; diff --git a/test-projects/vite/solid/src/types.d.ts b/test-projects/vite/solid/src/types.d.ts index 8e2dddd..4f5df84 100644 --- a/test-projects/vite/solid/src/types.d.ts +++ b/test-projects/vite/solid/src/types.d.ts @@ -1,6 +1,6 @@ declare module '*.svg?sprite=shapes' { - import type { Component, JSX, ParentProps } from 'solid-js'; + import type { SvgComponent } from '@svg-jar/plugin/types/solid'; - const component: Component>>; - export default component; + const Component: SvgComponent; + export default Component; } diff --git a/test-projects/vite/vanilla/src/svg-types.ts b/test-projects/vite/vanilla/src/svg-types.ts new file mode 100644 index 0000000..24b50b5 --- /dev/null +++ b/test-projects/vite/vanilla/src/svg-types.ts @@ -0,0 +1,14 @@ +// Type tests for the SVG import declarations. `pnpm typecheck` fails if an +// SVG import does not have the named `SvgComponent` type. + +import type { SvgComponent } from '@svg-jar/plugin/types/dom'; +import Arrow from './icons/arrow.svg'; +import Square from './icons/square.svg?unsafe-inline'; +import Circle from './icons/circle.svg?sprite=shapes'; + +export const arrow: SvgComponent = Arrow; +export const square: SvgComponent = Square; +export const circle: SvgComponent = Circle; + +// @ts-expect-error an SVG import is a component, so this fails unless the type is `any` +export const notANumber: number = Arrow; diff --git a/test-projects/vite/vanilla/src/types.d.ts b/test-projects/vite/vanilla/src/types.d.ts index c662068..330ca34 100644 --- a/test-projects/vite/vanilla/src/types.d.ts +++ b/test-projects/vite/vanilla/src/types.d.ts @@ -1,20 +1,20 @@ declare module '*.svg?sprite=shapes' { - import type { SvgOptions } from '@svg-jar/plugin/runtime/dom'; + import type { SvgComponent } from '@svg-jar/plugin/types/dom'; - const component: (options?: SvgOptions) => SVGSVGElement; - export default component; + const Component: SvgComponent; + export default Component; } declare module '*.svg?sprite=animated' { - import type { SvgOptions } from '@svg-jar/plugin/runtime/dom'; + import type { SvgComponent } from '@svg-jar/plugin/types/dom'; - const component: (options?: SvgOptions) => SVGSVGElement; - export default component; + const Component: SvgComponent; + export default Component; } declare module '*.svg?sprite=fonts' { - import type { SvgOptions } from '@svg-jar/plugin/runtime/dom'; + import type { SvgComponent } from '@svg-jar/plugin/types/dom'; - const component: (options?: SvgOptions) => SVGSVGElement; - export default component; + const Component: SvgComponent; + export default Component; } diff --git a/test-projects/vite/vue/src/svg-types.ts b/test-projects/vite/vue/src/svg-types.ts new file mode 100644 index 0000000..fa452de --- /dev/null +++ b/test-projects/vite/vue/src/svg-types.ts @@ -0,0 +1,14 @@ +// Type tests for the SVG import declarations. `pnpm typecheck` fails if an +// SVG import does not have the named `SvgComponent` type. + +import type { SvgComponent } from '@svg-jar/plugin/types/vue'; +import Arrow from './icons/arrow.svg'; +import Square from './icons/square.svg?unsafe-inline'; +import Circle from './icons/circle.svg?sprite=shapes'; + +export const arrow: SvgComponent = Arrow; +export const square: SvgComponent = Square; +export const circle: SvgComponent = Circle; + +// @ts-expect-error an SVG import is a component, so this fails unless the type is `any` +export const notANumber: number = Arrow; diff --git a/test-projects/vite/vue/src/types.d.ts b/test-projects/vite/vue/src/types.d.ts index a57a2ab..923cbdc 100644 --- a/test-projects/vite/vue/src/types.d.ts +++ b/test-projects/vite/vue/src/types.d.ts @@ -1,6 +1,6 @@ declare module '*.svg?sprite=shapes' { - import type { Component } from 'vue'; + import type { SvgComponent } from '@svg-jar/plugin/types/vue'; - const component: Component; - export default component; + const Component: SvgComponent; + export default Component; } diff --git a/test-projects/vite/web-component/src/svg-types.ts b/test-projects/vite/web-component/src/svg-types.ts new file mode 100644 index 0000000..0f5b11d --- /dev/null +++ b/test-projects/vite/web-component/src/svg-types.ts @@ -0,0 +1,14 @@ +// Type tests for the SVG import declarations. `pnpm typecheck` fails if an +// SVG import does not have the named `SvgComponent` type. + +import type { SvgComponent } from '@svg-jar/plugin/types/web-component'; +import Arrow from './icons/arrow.svg'; +import Square from './icons/square.svg?unsafe-inline'; +import Circle from './icons/circle.svg?sprite=shapes'; + +export const arrow: SvgComponent = Arrow; +export const square: SvgComponent = Square; +export const circle: SvgComponent = Circle; + +// @ts-expect-error an SVG import is a component, so this fails unless the type is `any` +export const notANumber: number = Arrow; diff --git a/test-projects/vite/web-component/src/types.d.ts b/test-projects/vite/web-component/src/types.d.ts index f2f6b3f..386dfed 100644 --- a/test-projects/vite/web-component/src/types.d.ts +++ b/test-projects/vite/web-component/src/types.d.ts @@ -1,4 +1,6 @@ declare module '*.svg?sprite=shapes' { - const ElementClass: typeof HTMLElement; - export default ElementClass; + import type { SvgComponent } from '@svg-jar/plugin/types/web-component'; + + const Component: SvgComponent; + export default Component; }