From fff3420edc343b61c384a8268f68bf0e7f73fc5d Mon Sep 17 00:00:00 2001 From: Elecmonkey Date: Sun, 2 Nov 2025 16:26:41 +0800 Subject: [PATCH] feat: add svelte template (#6) --- README.md | 1 + svelte/.gitignore | 4 ++ svelte/README.md | 35 ++++++++++++ svelte/package.json | 60 ++++++++++++++++++++ svelte/playground/index.html | 12 ++++ svelte/playground/src/App.svelte | 66 ++++++++++++++++++++++ svelte/playground/src/index.ts | 8 +++ svelte/playground/svelte.config.ts | 5 ++ svelte/playground/vite.config.ts | 7 +++ svelte/scripts/tsdown-plugin-svelte-dts.js | 32 +++++++++++ svelte/src/MyButton.svelte | 40 +++++++++++++ svelte/src/index.ts | 1 + svelte/svelte.config.ts | 5 ++ svelte/tests/index.test.ts | 50 ++++++++++++++++ svelte/tests/setup.ts | 1 + svelte/tsconfig.json | 21 +++++++ svelte/tsdown.config.ts | 16 ++++++ svelte/vitest.config.ts | 14 +++++ 18 files changed, 378 insertions(+) create mode 100644 svelte/.gitignore create mode 100644 svelte/README.md create mode 100644 svelte/package.json create mode 100644 svelte/playground/index.html create mode 100644 svelte/playground/src/App.svelte create mode 100644 svelte/playground/src/index.ts create mode 100644 svelte/playground/svelte.config.ts create mode 100644 svelte/playground/vite.config.ts create mode 100644 svelte/scripts/tsdown-plugin-svelte-dts.js create mode 100644 svelte/src/MyButton.svelte create mode 100644 svelte/src/index.ts create mode 100644 svelte/svelte.config.ts create mode 100644 svelte/tests/index.test.ts create mode 100644 svelte/tests/setup.ts create mode 100644 svelte/tsconfig.json create mode 100644 svelte/tsdown.config.ts create mode 100644 svelte/vitest.config.ts diff --git a/README.md b/README.md index c287d72..1c2f4a9 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ A collection of templates for [tsdown](https://tsdown.dev). - [Vue](./vue) - [React](./react) - [Solid](./solid) +- [Svelte](./svelte) ## License diff --git a/svelte/.gitignore b/svelte/.gitignore new file mode 100644 index 0000000..7535211 --- /dev/null +++ b/svelte/.gitignore @@ -0,0 +1,4 @@ +node_modules +dist +*.log +.DS_Store diff --git a/svelte/README.md b/svelte/README.md new file mode 100644 index 0000000..b432f99 --- /dev/null +++ b/svelte/README.md @@ -0,0 +1,35 @@ +# svelte-components-starter + +A starter for creating a Svelte component library. + +## Development + +- Install dependencies: + +```bash +npm install +``` + +- Run the unit tests: + +```bash +npm run test +``` + +- Build the library: + +```bash +npm run build +``` + +- Run the playground for development: + +```bash +npm run play +``` + +- Type checking: + +```bash +npm run typecheck +``` diff --git a/svelte/package.json b/svelte/package.json new file mode 100644 index 0000000..1ce5d11 --- /dev/null +++ b/svelte/package.json @@ -0,0 +1,60 @@ +{ + "name": "svelte-components-starter", + "version": "0.0.0", + "description": "A starter for creating a Svelte component library.", + "type": "module", + "license": "MIT", + "homepage": "https://github.com/author/library#readme", + "bugs": { + "url": "https://github.com/author/library/issues" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/author/library.git" + }, + "author": "Author Name ", + "files": [ + "dist" + ], + "main": "./dist/index.js", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": "./dist/index.js", + "./package.json": "./package.json" + }, + "publishConfig": { + "access": "public" + }, + "scripts": { + "build": "tsdown", + "dev": "tsdown --watch", + "play": "vite --config playground/vite.config.ts", + "test": "vitest", + "typecheck": "svelte-check --tsconfig ./tsconfig.json", + "release": "bumpp && pnpm publish", + "prepublishOnly": "pnpm run build" + }, + "peerDependencies": { + "svelte": "^5.0.0" + }, + "devDependencies": { + "@sveltejs/package": "^2.5.4", + "@sveltejs/vite-plugin-svelte": "^6.2.1", + "@testing-library/jest-dom": "^6.9.1", + "@testing-library/svelte": "^5.2.8", + "@tsconfig/svelte": "^5.0.5", + "@types/node": "^24.9.2", + "bumpp": "^10.3.1", + "jsdom": "^27.0.1", + "rollup-plugin-svelte": "^7.2.3", + "svelte": "^5.43.0", + "svelte-check": "^4.3.3", + "svelte-preprocess": "^6.0.3", + "svelte2tsx": "^0.7.45", + "tsdown": "0.15.12", + "typescript": "^5.9.3", + "vite": "npm:rolldown-vite@^7.1.20", + "vitest": "^4.0.5" + } +} diff --git a/svelte/playground/index.html b/svelte/playground/index.html new file mode 100644 index 0000000..a8ff365 --- /dev/null +++ b/svelte/playground/index.html @@ -0,0 +1,12 @@ + + + + + + Svelte Component Playground + + +
+ + + diff --git a/svelte/playground/src/App.svelte b/svelte/playground/src/App.svelte new file mode 100644 index 0000000..234977f --- /dev/null +++ b/svelte/playground/src/App.svelte @@ -0,0 +1,66 @@ + + +
+

Svelte Component Library

+ +
+

MyButton Component

+ +

Last count received: {lastCount}

+
+ +
+

Custom Label

+ console.log('Clicked', count)} /> +
+
+ + \ No newline at end of file diff --git a/svelte/playground/src/index.ts b/svelte/playground/src/index.ts new file mode 100644 index 0000000..928b6c5 --- /dev/null +++ b/svelte/playground/src/index.ts @@ -0,0 +1,8 @@ +import { mount } from 'svelte' +import App from './App.svelte' + +const app = mount(App, { + target: document.getElementById('app')!, +}) + +export default app diff --git a/svelte/playground/svelte.config.ts b/svelte/playground/svelte.config.ts new file mode 100644 index 0000000..09a1bf7 --- /dev/null +++ b/svelte/playground/svelte.config.ts @@ -0,0 +1,5 @@ +import { vitePreprocess } from '@sveltejs/vite-plugin-svelte' + +export default { + preprocess: vitePreprocess() +} diff --git a/svelte/playground/vite.config.ts b/svelte/playground/vite.config.ts new file mode 100644 index 0000000..5723c51 --- /dev/null +++ b/svelte/playground/vite.config.ts @@ -0,0 +1,7 @@ +import { svelte } from '@sveltejs/vite-plugin-svelte' +import { defineConfig } from 'vite' + +export default defineConfig({ + root: './playground', + plugins: [svelte()] +}) diff --git a/svelte/scripts/tsdown-plugin-svelte-dts.js b/svelte/scripts/tsdown-plugin-svelte-dts.js new file mode 100644 index 0000000..a0c8fec --- /dev/null +++ b/svelte/scripts/tsdown-plugin-svelte-dts.js @@ -0,0 +1,32 @@ +import { emitDts } from 'svelte2tsx' +import { resolve } from 'path' + +export function svelteDtsPlugin(options = {}) { + return { + name: 'tsdown-plugin-svelte-dts', + async closeBundle() { + const { + declarationDir = './dist', + libRoot = './src', + tsconfig = 'tsconfig.json', + svelteShimsPath = 'node_modules/svelte2tsx/svelte-shims-v4.d.ts', + } = options + + const declarationDirAbs = resolve(process.cwd(), declarationDir) + + try { + await emitDts({ + declarationDir: declarationDirAbs, + svelteShimsPath: resolve(process.cwd(), svelteShimsPath), + libRoot: resolve(process.cwd(), libRoot), + tsconfig: resolve(process.cwd(), tsconfig), + }) + + console.log('Svelte DTS generation complete') + } catch (error) { + console.error('❌ Svelte DTS generation failed:', error) + throw error + } + }, + } +} diff --git a/svelte/src/MyButton.svelte b/svelte/src/MyButton.svelte new file mode 100644 index 0000000..7e9cc13 --- /dev/null +++ b/svelte/src/MyButton.svelte @@ -0,0 +1,40 @@ + + + + + \ No newline at end of file diff --git a/svelte/src/index.ts b/svelte/src/index.ts new file mode 100644 index 0000000..923af3b --- /dev/null +++ b/svelte/src/index.ts @@ -0,0 +1 @@ +export { default as MyButton } from './MyButton.svelte' \ No newline at end of file diff --git a/svelte/svelte.config.ts b/svelte/svelte.config.ts new file mode 100644 index 0000000..09a1bf7 --- /dev/null +++ b/svelte/svelte.config.ts @@ -0,0 +1,5 @@ +import { vitePreprocess } from '@sveltejs/vite-plugin-svelte' + +export default { + preprocess: vitePreprocess() +} diff --git a/svelte/tests/index.test.ts b/svelte/tests/index.test.ts new file mode 100644 index 0000000..d7ea638 --- /dev/null +++ b/svelte/tests/index.test.ts @@ -0,0 +1,50 @@ +/// + +import { render, screen } from '@testing-library/svelte' +import { expect, test } from 'vitest' +import MyButton from '../src/MyButton.svelte' + +test('button renders with default label', () => { + render(MyButton) + + const buttonElement = screen.getByRole('button') + expect(buttonElement).toBeInTheDocument() + expect(buttonElement).toHaveTextContent('Click me: 0') +}) + +test('button renders with custom label', () => { + render(MyButton, { props: { label: 'Count' } }) + + const buttonElement = screen.getByRole('button') + expect(buttonElement).toHaveTextContent('Count: 0') +}) + +test('button increments count on click', async () => { + render(MyButton, { props: { label: 'Test' } }) + + const buttonElement = screen.getByRole('button') + expect(buttonElement).toHaveTextContent('Test: 0') + + await buttonElement.click() + expect(buttonElement).toHaveTextContent('Test: 1') + + await buttonElement.click() + expect(buttonElement).toHaveTextContent('Test: 2') +}) + +test('button calls onclick callback with count', async () => { + let receivedCount = 0 + const handleClick = (count: number) => { + receivedCount = count + } + + render(MyButton, { props: { onclick: handleClick } }) + + const buttonElement = screen.getByRole('button') + + await buttonElement.click() + expect(receivedCount).toBe(1) + + await buttonElement.click() + expect(receivedCount).toBe(2) +}) diff --git a/svelte/tests/setup.ts b/svelte/tests/setup.ts new file mode 100644 index 0000000..a9d0dd3 --- /dev/null +++ b/svelte/tests/setup.ts @@ -0,0 +1 @@ +import '@testing-library/jest-dom/vitest' diff --git a/svelte/tsconfig.json b/svelte/tsconfig.json new file mode 100644 index 0000000..e03f5c8 --- /dev/null +++ b/svelte/tsconfig.json @@ -0,0 +1,21 @@ +{ + "extends": "@tsconfig/svelte/tsconfig.json", + "compilerOptions": { + "target": "esnext", + "lib": ["es2023", "dom"], + "moduleDetection": "force", + "module": "preserve", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "types": ["node"], + "strict": true, + "noUnusedLocals": true, + "declaration": true, + "emitDeclarationOnly": true, + "esModuleInterop": true, + "isolatedModules": true, + "verbatimModuleSyntax": true, + "skipLibCheck": true + }, + "include": ["src"] +} diff --git a/svelte/tsdown.config.ts b/svelte/tsdown.config.ts new file mode 100644 index 0000000..d68d220 --- /dev/null +++ b/svelte/tsdown.config.ts @@ -0,0 +1,16 @@ +import svelte from 'rollup-plugin-svelte' +import { sveltePreprocess } from 'svelte-preprocess' +import { defineConfig } from 'tsdown' +import { svelteDtsPlugin } from './scripts/tsdown-plugin-svelte-dts.js' + +export default defineConfig({ + platform: 'neutral', + plugins: [ + svelte({ preprocess: sveltePreprocess() }), + svelteDtsPlugin({ + declarationDir: './dist', + libRoot: './src', + tsconfig: 'tsconfig.json', + }), + ] +}) diff --git a/svelte/vitest.config.ts b/svelte/vitest.config.ts new file mode 100644 index 0000000..cbda5b1 --- /dev/null +++ b/svelte/vitest.config.ts @@ -0,0 +1,14 @@ +import { svelte } from '@sveltejs/vite-plugin-svelte' +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + plugins: [svelte() as any], + test: { + environment: 'jsdom', + globals: true, + setupFiles: './tests/setup.ts', + }, + resolve: { + conditions: ['browser'], + }, +}) -- 2.51.2