diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 161b910..0000000 --- a/Dockerfile +++ /dev/null @@ -1,15 +0,0 @@ -FROM node:24-alpine - -WORKDIR /app - -# 1. 启用 pnpm -RUN corepack enable && corepack prepare pnpm@latest --activate - -# 2. 复制 workspace 配置 -COPY pnpm-workspace.yaml pnpm-lock.yaml package.json ./ - -# 3. 安装所有依赖 -RUN pnpm install --frozen-lockfile - -# 4. 复制源码 -COPY . . diff --git a/apps/vite-project/src/App.tsx b/apps/vite-project/src/App.tsx deleted file mode 100644 index a66b5ef..0000000 --- a/apps/vite-project/src/App.tsx +++ /dev/null @@ -1,122 +0,0 @@ -import { useState } from 'react' -import reactLogo from './assets/react.svg' -import viteLogo from './assets/vite.svg' -import heroImg from './assets/hero.png' -import './App.css' - -function App() { - const [count, setCount] = useState(0) - - return ( - <> -
-
- - React logo - Vite logo -
-
-

Get started

-

- Edit src/App.tsx and save to test HMR -

-
- -
- -
- -
-
- -

Documentation

-

Your questions, answered

- -
-
- -

Connect with us

-

Join the Vite community

- -
-
- -
-
- - ) -} - -export default App diff --git a/apps/vite-project/src/common/CommonProps.ts b/apps/vite-project/src/common/CommonProps.ts deleted file mode 100644 index c78338a..0000000 --- a/apps/vite-project/src/common/CommonProps.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { type CSSProperties, type ReactNode } from "react"; - -export type CommonProps = { - className?: string; - style?: CSSProperties; - children?: ReactNode; - disabled?: boolean; - key?: string; -}; diff --git a/apps/vite-project/src/common/InnerWrapper.tsx b/apps/vite-project/src/common/InnerWrapper.tsx deleted file mode 100644 index d05b9c0..0000000 --- a/apps/vite-project/src/common/InnerWrapper.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import { type ReactNode } from "react"; - -export const InnerWrapper = ({ children }: { children: ReactNode }) => { - return ( -
- {children} -
- ); -}; diff --git a/apps/vite-project/src/gallery/ButtonGallery.tsx b/apps/vite-project/src/gallery/ButtonGallery.tsx deleted file mode 100644 index c71d01e..0000000 --- a/apps/vite-project/src/gallery/ButtonGallery.tsx +++ /dev/null @@ -1,251 +0,0 @@ -import { Button, DownloadSvg } from "@defgov/ui-web-tw"; -import { InnerWrapper } from "../common/InnerWrapper"; -import { OuterWrapper } from "../common/OuterWrapper"; - -export const ButtonGallery = () => { - return ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {/* ------------------------------------- */} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ); -}; diff --git a/apps/vite-project/src/main.tsx b/apps/vite-project/src/main.tsx deleted file mode 100644 index 4aff025..0000000 --- a/apps/vite-project/src/main.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import { StrictMode } from 'react' -import { createRoot } from 'react-dom/client' -import App from './App.tsx' - -createRoot(document.getElementById('root')!).render( - - - , -) diff --git a/apps/vite-project/src/types/css.d.ts b/apps/vite-project/src/types/css.d.ts deleted file mode 100644 index d535de2..0000000 --- a/apps/vite-project/src/types/css.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -declare module '*.css' { - const content: { [className: string]: string }; - export default content; -} \ No newline at end of file diff --git a/apps/vite-project/src/types/env.d.ts b/apps/vite-project/src/types/env.d.ts deleted file mode 100644 index 7ba6254..0000000 --- a/apps/vite-project/src/types/env.d.ts +++ /dev/null @@ -1,51 +0,0 @@ -/// - -declare module "*.svg" { - import * as React from "react"; - export const ReactComponent: React.FC>; - const src: string; - export default src; -} - -declare module "*.png" { - const src: string; - export default src; -} - -declare module "*.jpg" { - const src: string; - export default src; -} - -declare module "*.jpeg" { - const src: string; - export default src; -} - -declare module "*.gif" { - const src: string; - export default src; -} - -declare module "*.webp" { - const src: string; - export default src; -} - -interface ImportMetaEnv { - readonly MODE: "development" | "production" | "test"; - readonly BASE_URL: string; - readonly PROD: boolean; - readonly DEV: boolean; - readonly SSR: boolean; - - // ===== 业务环境变量 ===== - readonly VITE_API_BASE: string; - readonly VITE_UPLOAD_URL?: string; - readonly VITE_ENABLE_MOCK?: "true" | "false"; - readonly VITE_SENTRY_DSN?: string; -} - -interface ImportMeta { - readonly env: ImportMetaEnv; -} diff --git a/apps/vite-project/vite.config.ts b/apps/vite-project/vite.config.ts deleted file mode 100644 index 174c135..0000000 --- a/apps/vite-project/vite.config.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { defineConfig } from "vite"; -import react from "@vitejs/plugin-react"; - -// https://vite.dev/config/ -export default defineConfig({ - plugins: [react()], - - build: { - rolldownOptions: { }, - }, -}); diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index f8fa0c9..0000000 --- a/docker-compose.yml +++ /dev/null @@ -1,10 +0,0 @@ -services: - dev: - image: node:24-alpine - working_dir: /app - volumes: - - .:/app - - /app/node_modules - ports: - - "3000:3000" - command: sh diff --git a/package.json b/package.json index d82bb25..c304568 100644 --- a/package.json +++ b/package.json @@ -3,8 +3,8 @@ "private": true, "type": "module", "scripts": { - "build": "turbo run build", - "dev": "turbo run dev" + "dev": "turbo run dev", + "build": "turbo run build" }, "devDependencies": { "turbo": "^2.8.0" @@ -12,5 +12,5 @@ "engines": { "node": ">=20" }, - "packageManager": "pnpm@11.0.9+sha512.34ce82e6780233cf9cad8685029a8f81d2e06196c5a9bad98879f7424940c6817c4e4524fb7d38b8553ceed48b9758b8ebaf1abd3600c232c4c8cf7366086f38" + "packageManager": "pnpm@11.5.2+sha512.71c631e382066efc25625d5cf029075de07b61b37f6e27350fbd84b1bda5864c8c1967adc280776b45c30a715c0359a3be08fef42d5bb09e2b99029979692916" } diff --git a/packages/bookmark-sync/manifest.json b/packages/bookmark-sync/manifest.json deleted file mode 100644 index ba8b7d5..0000000 --- a/packages/bookmark-sync/manifest.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "@defgov/bookmark-sync", - "version": "1.0", - "manifest_version": 3, - "permissions": ["bookmarks", "storage"], - "chrome_url_overrides": { - "newtab": "newtab.html" - }, - "host_permissions": [""], - "background": { - "service_worker": "controller.js" - }, - "browser_specific_settings": { - "gecko": { - "id": "bookmark-sync@example.com", - "strict_min_version": "109.0" - } - } -} diff --git a/packages/bookmark-sync/package.json b/packages/bookmark-sync/package.json deleted file mode 100644 index 63310ae..0000000 --- a/packages/bookmark-sync/package.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "defgov-bookmark-sync", - "version": "0.0.0", - "private": true, - "dependencies": { - "typescript": "^6.0.3" - }, - "devDependencies": { - "@types/chrome": "^0.1.40", - "@types/firefox-webext-browser": "^143.0.0", - "@types/node": "^25.6.0", - "@types/webextension-polyfill": "^0.12.5", - "webextension-polyfill": "^0.12.0" - } -} diff --git a/packages/bookmark-sync/src/controller.ts b/packages/bookmark-sync/src/controller.ts deleted file mode 100644 index 4e22eee..0000000 --- a/packages/bookmark-sync/src/controller.ts +++ /dev/null @@ -1,79 +0,0 @@ -import browser from "webextension-polyfill"; - -export class Controller { - /** - * 书签栏 (Bookmarks Bar) 固定ID为 "1" - * - * 其他书签 (Other Bookmarks) 固定ID为 "2" - * - * 移动设备书签 (Mobile Bookmarks) 固定ID为 "3" - */ - rootFolder: string; - - constructor(rootFolder: string = "1") { - this.rootFolder = rootFolder; - } - - init() {} - - setRootFolder(rootFolder: string) { - this.rootFolder = rootFolder; - } - - async createBookmark(parentId: string, title: string, url?: string) { - try { - await browser.bookmarks.create({ - parentId: parentId, - title: title, - url: url, - type: "bookmark", - }); - } catch (error) { - console.error("❌ 创建书签失败:", error); - } - } - - async createFolder(parentId: string, title: string, url?: string) { - try { - await browser.bookmarks.create({ - parentId: parentId, - title: title, - url: url, - type: "folder", - }); - } catch (error) { - console.error(error); - } - } - - async createSeparator(parentId: string) { - try { - await browser.bookmarks.create({ - parentId: parentId, - type: "separator", - }); - } catch (error) { - console.error(error); - } - } - - async deleteNode(id: string) { - const results = await browser.bookmarks.get(id); - if (results[0].type === "bookmark" || results[0].type === "separator") { - deleteBookmarkOrSeparator(id); - } else if (results[0].type === "folder") { - deleteFolder(id); - } - } - - async moveNode(id: string, destinationId: string, index: number) { - try { - await browser.bookmarks.move(id, { - parentId: destinationId, - index: index, - }); - } catch (error) { - console.error(error); - } - } -} diff --git a/packages/bookmark-sync/src/custom-bookmark-bar/newtab.html b/packages/bookmark-sync/src/custom-bookmark-bar/newtab.html deleted file mode 100644 index 5c4e904..0000000 --- a/packages/bookmark-sync/src/custom-bookmark-bar/newtab.html +++ /dev/null @@ -1,47 +0,0 @@ - - - - - 我的自定义书签 - - - -

我的书签管理器

- - - - -
- - - - diff --git a/packages/bookmark-sync/src/custom-bookmark-bar/popup.ts b/packages/bookmark-sync/src/custom-bookmark-bar/popup.ts deleted file mode 100644 index 9db016f..0000000 --- a/packages/bookmark-sync/src/custom-bookmark-bar/popup.ts +++ /dev/null @@ -1,76 +0,0 @@ -// 递归提取某个节点下的所有书签 URL(防止文件夹嵌套) -function extractUrls( - bookmarkNode: chrome.bookmarks.BookmarkTreeNode[], -): string[] { - let urls: string[] = []; - for (const node of bookmarkNode) { - if (node.url) { - urls.push(node.url); - } else if (node.children) { - urls = urls.concat(extractUrls(node.children)); - } - } - return urls; -} - -// 渲染指定 ID 文件夹下的书签 -async function renderBookmarksByFolderId(folderId: string) { - try { - const results = await chrome.bookmarks.getSubTree(folderId); - const folderNode = results[0]; - - if (!folderNode || !folderNode.children) return; - - const container = document.getElementById("bookmark-container"); - if (container) container.innerHTML = ""; - - // 遍历直接子节点 - folderNode.children.forEach((bookmark) => { - if (bookmark.url) { - const link = document.createElement("a"); - link.className = "bookmark-item"; - link.href = bookmark.url; - link.textContent = bookmark.title || bookmark.url; - // 自动获取网站 favicon 图标 - const faviconUrl = `https://www.google.com/s2/favicons?domain=${new URL(bookmark.url).hostname}&sz=32`; - link.innerHTML = `${bookmark.title}`; - container?.appendChild(link); - } - }); - } catch (error) { - console.error("获取书签失败:", error); - } -} - -// 初始化:自动获取所有一级文件夹并填充到下拉框 -async function initFolderSelector() { - const results = await chrome.bookmarks.getTree(); - const bookmarkBar = results[0].children?.[0]; // ID为1的书签栏 - const selector = document.getElementById( - "folder-selector", - ) as HTMLSelectElement; - - if (bookmarkBar && bookmarkBar.children) { - bookmarkBar.children.forEach((folder) => { - // 只把文件夹加到下拉框里 - if (!folder.url && folder.id !== "1") { - const option = document.createElement("option"); - option.value = folder.id || ""; - option.textContent = folder.title || "未命名文件夹"; - selector.appendChild(option); - } - }); - } - - // 绑定切换事件 - selector.addEventListener("change", (e) => { - const targetId = (e.target as HTMLSelectElement).value; - renderBookmarksByFolderId(targetId); - }); - - // 默认渲染书签栏 - renderBookmarksByFolderId("1"); -} - -// 页面加载完成后启动 -document.addEventListener("DOMContentLoaded", initFolderSelector); diff --git a/packages/bookmark-sync/src/types.ts b/packages/bookmark-sync/src/types.ts deleted file mode 100644 index d9aa602..0000000 --- a/packages/bookmark-sync/src/types.ts +++ /dev/null @@ -1,5 +0,0 @@ -import browser from "webextension-polyfill"; - -export type Account = { id: string; email: string; password: string }; - -export type BookmarkTreeNode = browser.Bookmarks.BookmarkTreeNode; diff --git a/packages/bookmark-sync/src/utils.ts b/packages/bookmark-sync/src/utils.ts deleted file mode 100644 index bf369ae..0000000 --- a/packages/bookmark-sync/src/utils.ts +++ /dev/null @@ -1,20 +0,0 @@ -async function getLastIndex(parentId: string) { - const children = await browser.bookmarks.getChildren(parentId); - return children.length > 0 ? children[children.length - 1].index : 0; -} - -async function deleteBookmarkOrSeparator(id: string) { - try { - await browser.bookmarks.remove(id); - } catch (error) { - console.error(error); - } -} - -async function deleteFolder(id: string) { - try { - await browser.bookmarks.removeTree(id); - } catch (error) { - console.error(error); - } -} diff --git a/packages/bookmark-sync/tsconfig.json b/packages/bookmark-sync/tsconfig.json deleted file mode 100644 index d5ec425..0000000 --- a/packages/bookmark-sync/tsconfig.json +++ /dev/null @@ -1,118 +0,0 @@ -{ - // tsconfig.lib.json - // 直接复制本文件内容到子项目的 tsconfig.json 即可,不要用 entends 继承本文件 - "extends": "../../tsconfig.base.json", - "compilerOptions": { - /** - * Browser api,需要加 "DOM","DOM.Iterable" - * Node api,需要加 "ES2025",始终使用带版本号的最新版本 - * NextJs api,属于同构,server 端会预处理 DOM,计算url,三个都需要 "ES2025", "DOM", "DOM.Iterable" - */ - "lib": ["ES2025", "DOM", "DOM.Iterable"], - - /** - * React JSX 编译模式 - * - 使用 React 17+ 新 JSX Transform - * - 不需要手动 import React - */ - "jsx": "react-jsx", - - /** - * 编译输出目录 - * - tsc / tsc -b 都会用到 - */ - "outDir": "./dist", - - /** - * 源码根目录 - * - 确保 dist 结构与 src 一致 - * - 对 declaration 路径至关重要 - */ - "rootDir": "./src", - - /** - * 生成 .d.ts 类型声明文件 - * - 组件库 / npm 包发布必需 - * - 对应用项目无害,仅影响类型输出 - */ - "declaration": true, - - /** - * 只做类型检查,不生成 JS 输出 - * - 适用于 Vite / Next / Nuxt 等 bundler 场景 - * - 防止 tsc 与构建工具重复 emit - */ - "noEmit": true, - - /** - * 强制单文件可独立编译 - * - 适配 esbuild / SWC / bundler 编译模型 - * - 禁止依赖跨文件类型推断(enum / namespace 等) - */ - "isolatedModules": true, - - /** - * 允许在 import 中显式使用 .ts / .tsx 后缀 - * - 兼容 Node ESM / bundler 对文件扩展名的严格要求 - * - 避免 `import './foo'` 在 TS + ESM 下歧义 - */ - "allowImportingTsExtensions": true - }, - /** - * 参与类型检查和编译的文件 - * - 只扫描 src - * - 其它目录通过 exclude 排除 - */ - "include": ["src", "scripts"], - - /** - * 明确排除非源码内容 - * - 避免污染类型系统 - * - 防止误入 dist / test / config - * - 保证发布包干净 - */ - "exclude": [ - "scripts", - "node_modules", - "dist", - - // ---------- build / cache ---------- - ".turbo/**/*", - ".cache/**/*", - ".vite/**/*", - - // ---------- 配置文件 ---------- - "vite.config.ts", - "*.config.ts", - "*.config.js", - "tsconfig.*.json", - - // ---------- 测试相关 ---------- - "__tests__/**/*", - "test/**/*", - "tests/**/*", - "**/*.test.ts", - "**/*.test.tsx", - "**/*.spec.ts", - "**/*.spec.tsx", - - // ---------- Storybook ---------- - ".storybook/**/*", - "stories/**/*", - - // ---------- 示例 / 脚本 ---------- - "example/**/*", - "examples/**/*", - "scripts/**/*", - - // ---------- 环境与静态资源 ---------- - ".env", - ".env.*", - "public/**/*", - - // ---------- 文档 ---------- - "docs/**/*", - "README.md", - "LICENSE" - ] -} diff --git a/packages/css/package.json b/packages/css/package.json new file mode 100644 index 0000000..983bd59 --- /dev/null +++ b/packages/css/package.json @@ -0,0 +1,44 @@ +{ + "name": "@dg/css", + "version": "0.0.0", + "private": true, + "type": "module", + "sideEffects": [ + "*.css" + ], + "module": "./dist/index.es.js", + "main": "./dist/index.cjs.js", + "types": "./dist/index.d.ts", + "style": "./dist/index.css", + "exports": { + ".": { + "import": "./dist/index.es.js", + "require": "./dist/index.cjs.js", + "types": "./dist/index.d.ts" + }, + "./index.css": "./dist/index.css" + }, + "files": [ + "dist" + ], + "scripts": { + "gen-index": "ts-node scripts/gen-index.ts", + "gen-dts": "tsc -p tsconfig.build.json", + "dev": "pnpm gen-index && vite build --watch", + "build": "pnpm gen-index && vite build && pnpm gen-dts" + }, + "devDependencies": { + "@types/node": "^25.6.0", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.1", + "tinyglobby": "^0.2.16", + "ts-node": "^10.9.2", + "typescript": "^6.0.3", + "vite": "^8.0.10" + }, + "peerDependencies": { + "react": "^19", + "react-dom": "^19" + } +} diff --git a/packages/css/scripts/gen-index.ts b/packages/css/scripts/gen-index.ts new file mode 100644 index 0000000..c685295 --- /dev/null +++ b/packages/css/scripts/gen-index.ts @@ -0,0 +1,254 @@ +import * as fs from "fs"; +import * as path from "path"; + +/** + * 默认测试 / story / example / fixture / type 文件匹配规则 + * 仅用于“识别”,不直接决定是否排除 + */ +const DEFAULT_TEST_FILE_PATTERNS: RegExp[] = [ + // ========================= + // Test / Spec + // ========================= + /\.(test|spec)\.(ts|tsx|js|jsx)$/, + /\.(test|spec)\.(ts|tsx|js|jsx)\?.*$/, // query param safe + + // ========================= + // E2E / Playwright / Cypress + // ========================= + /\.(cy|playwright|e2e)\.(ts|tsx|js|jsx)$/, + + // ========================= + // Storybook + // ========================= + /\.(story|stories)\.(ts|tsx|js|jsx|mdx)$/, + + // ========================= + // Snapshot / Mock / Fixture + // ========================= + /\.snap$/, + /\.mock\.(ts|tsx|js|jsx)$/, + /\.fixture\.(ts|tsx|js|jsx)$/, + + // ========================= + // Type declarations + // ========================= + /\.d\.ts$/, + + // ========================= + // Examples / Demos / Docs + // ========================= + /\.(example|demo|docs)\.(ts|tsx|js|jsx)$/, +]; + +/** + * 测试 / 辅助目录(目录级排除) + */ +const DEFAULT_TEST_DIR_PATTERNS: RegExp[] = [ + /[/\\](__tests__|tests|test|spec|__mocks__|__fixtures__|__snapshots__)[\/\\]/, + /[/\\](cypress|playwright|e2e|__e2e__|__playwright__)[\/\\]/, + /[/\\](story|stories|\.storybook)[\/\\]/, + /[/\\](types|type|typings|interfaces)[\/\\]/, + /[/\\](examples|example|demo|demos|docs)[\/\\]/, +]; + +type Config = { + outputDir: string; + outputFile: string; + scanDirs: string[]; + preamble?: string[]; + importPrefix?: string; + entryFilePatterns?: RegExp[]; + barrelFirstMode?: boolean; + includeFilePatterns?: RegExp[]; + excludeFilePatterns?: RegExp[]; + excludeDirPatterns?: RegExp[]; +}; + +const tsConfig: Config = { + outputDir: "src", + outputFile: "index.ts", + scanDirs: ["src"], + importPrefix: "export * from", + barrelFirstMode: true, + preamble: ["import './index.css';"], + entryFilePatterns: [/^index\.(js|ts|jsx|tsx)$/], + includeFilePatterns: [/\.(js|ts|jsx|tsx)$/], + excludeFilePatterns: DEFAULT_TEST_FILE_PATTERNS, + excludeDirPatterns: DEFAULT_TEST_DIR_PATTERNS, +}; + +const cssConfig: Config = { + outputDir: "src", + outputFile: "index.css", + scanDirs: ["src"], + importPrefix: "@import", + barrelFirstMode: false, + entryFilePatterns: [/^index\.(css)$/], + includeFilePatterns: [/\.(css)$/], + excludeFilePatterns: DEFAULT_TEST_FILE_PATTERNS, + excludeDirPatterns: DEFAULT_TEST_DIR_PATTERNS, +}; + +function isEntryFile(fileName: string, config: Config): boolean { + const regExps = config.entryFilePatterns; + + if (!regExps || regExps.length === 0) return false; + + return regExps.some((regExp) => regExp.test(fileName)); +} + +function isIncludeFile(fileName: string, config: Config): boolean { + const regExps = config.includeFilePatterns; + + if (!regExps || regExps.length === 0) return false; + + return regExps.some((regExp) => regExp.test(fileName)); +} + +function isExcludeFile(fileName: string, config: Config): boolean { + const regExps = config.excludeFilePatterns; + + if (!regExps || regExps.length === 0) return false; + + return regExps.some((regExp) => regExp.test(fileName)); +} + +function isExcludeDir(filePath: string, config: Config): boolean { + const regExps = config.excludeDirPatterns; + + if (!regExps || regExps.length === 0) return false; + + return regExps.some((regExp) => regExp.test(filePath)); +} + +function isValidDir(filePath: string, config: Config): boolean { + if (isExcludeDir(filePath, config)) { + return false; + } + return true; +} + +function isValidFile(fileName: string, config: Config): boolean { + if (isIncludeFile(fileName, config) && !isExcludeFile(fileName, config)) { + return true; + } + + return false; +} + +const buildExportStatement = (filePath: string, config: Config) => { + // 文件夹(不是文件)与目标文件的相对路径 + let exportFilePath = path.relative(config.outputDir, filePath); + + // 去除扩展名,并且转化成正斜杠 + const exportFilePathWithoutExt = path + .join( + path.dirname(exportFilePath), + path.basename(exportFilePath, path.extname(exportFilePath)), + ) + .replace(/\\/g, "/"); + + // 加上 ./ 前缀 + return `${config.importPrefix} './${exportFilePathWithoutExt}';`; +}; + +function isOutputEntry(filePath: string, config: Config) { + const outputFilePath = path.resolve(config.outputDir, config.outputFile); + const targetFilePath = path.resolve(filePath); + + return outputFilePath === targetFilePath; +} + +function generateExports(dirPath: string, config: Config): string[] { + const fileNames = fs.readdirSync(dirPath); + const exports: string[] = []; + + // ========================= + // 逻辑分支:Barrel First Mode + // ========================= + if (config.barrelFirstMode) { + // 查找当前目录有没有 index.ts + const entryFileName = fileNames.find((fileName) => { + const filePath = path.join(dirPath, fileName); + return fs.statSync(filePath).isFile() && isEntryFile(fileName, config); + }); + // 如果有 + if (entryFileName) { + const entryFilePath = path.join(dirPath, entryFileName); + + // 如果是 outputFile 自身,则跳过 + if (isOutputEntry(entryFilePath, config)) { + console.log(`⏭️ 跳过自身入口文件: ${entryFilePath}`); + } else { + exports.push(buildExportStatement(entryFilePath, config)); + } + } + } + + // ========================= + // 逻辑分支:index.ts 不存在,则继续正常遍历与递归 + // ========================= + fileNames.forEach((fileName) => { + const filePath = path.join(dirPath, fileName); + const stat = fs.statSync(filePath); + + // 情况1:是文件,且通过了校验 + if ( + stat.isFile() && + isValidFile(fileName, config) && + !isEntryFile(fileName, config) + ) { + exports.push(buildExportStatement(filePath, config)); + } + // 情况2:是文件夹,且通过了校验,递归扫描子文件夹 + else if (stat.isDirectory() && isValidDir(filePath, config)) { + const subExports = generateExports(filePath, config); + exports.push(...subExports); + } + }); + + return exports; +} + +function genIndexFile(config: Config) { + // 确保输出目录存在,如果不存在就递归创建 + if (!fs.existsSync(config.outputDir)) { + fs.mkdirSync(config.outputDir, { recursive: true }); + } + + const allExports: string[] = []; + + // 遍历所有需要扫描的根目录 + config.scanDirs.forEach((scanDir) => { + // 只有当目录真实存在时才进行扫描 + if (fs.existsSync(scanDir)) { + const exports = generateExports(scanDir, config); + allExports.push(...exports); + } else { + console.warn(`⚠️ 警告:扫描目录不存在,已跳过 -> ${scanDir}`); + } + }); + + // 拼接最终的文件内容:前言 + 导出语句(使用 Set 自动去重) + const fileContent = [ + ...(config.preamble ?? []), + ...Array.from(new Set(allExports)), + ].join("\n"); + + // 将内容写入到最终的 index.ts 文件中 + const outputFilePath = path.join(config.outputDir, config.outputFile); + fs.writeFileSync(outputFilePath, fileContent, "utf-8"); + + console.log(`✨ 成功生成入口文件: ${outputFilePath}`); +} + +// ================= 脚本执行入口 ================= +try { + console.log("🚀 开始扫描并生成入口文件..."); + genIndexFile(tsConfig); + genIndexFile(cssConfig); + console.log("✅ 脚本执行完毕!"); +} catch (error) { + console.error("❌ 脚本执行失败:", error); + process.exit(1); // 如果报错,让进程以非 0 状态码退出 +} diff --git a/packages/css/src/index.css b/packages/css/src/index.css new file mode 100644 index 0000000..afdf9b2 --- /dev/null +++ b/packages/css/src/index.css @@ -0,0 +1,36 @@ +@import './styles/base/root'; +@import './styles/utility/size-insensitive/align-content'; +@import './styles/utility/size-insensitive/align-items'; +@import './styles/utility/size-insensitive/align-self'; +@import './styles/utility/size-insensitive/animation'; +@import './styles/utility/size-insensitive/box-decoration'; +@import './styles/utility/size-insensitive/box-inside'; +@import './styles/utility/size-insensitive/box-sizing'; +@import './styles/utility/size-insensitive/brand'; +@import './styles/utility/size-insensitive/break-after'; +@import './styles/utility/size-insensitive/break-before'; +@import './styles/utility/size-insensitive/cursor'; +@import './styles/utility/size-insensitive/display'; +@import './styles/utility/size-insensitive/drop-shadow'; +@import './styles/utility/size-insensitive/flex-direction'; +@import './styles/utility/size-insensitive/flex-wrap'; +@import './styles/utility/size-insensitive/font-family'; +@import './styles/utility/size-insensitive/justify-content'; +@import './styles/utility/size-insensitive/justify-items'; +@import './styles/utility/size-insensitive/justify-self'; +@import './styles/utility/size-insensitive/overflow'; +@import './styles/utility/size-insensitive/overscroll-behavior'; +@import './styles/utility/size-insensitive/position'; +@import './styles/utility/size-insensitive/screen-reader'; +@import './styles/utility/size-insensitive/theme'; +@import './styles/utility/size-insensitive/user-select'; +@import './styles/utility/size-insensitive/variant'; +@import './styles/utility/size-insensitive/z-index'; +@import './styles/utility/size-sensitive/border-radius'; +@import './styles/utility/size-sensitive/font-size'; +@import './styles/utility/size-sensitive/font-weight'; +@import './styles/utility/size-sensitive/gap'; +@import './styles/utility/size-sensitive/height'; +@import './styles/utility/size-sensitive/margin'; +@import './styles/utility/size-sensitive/padding'; +@import './styles/utility/size-sensitive/width'; \ No newline at end of file diff --git a/packages/css/src/index.ts b/packages/css/src/index.ts new file mode 100644 index 0000000..141acb2 --- /dev/null +++ b/packages/css/src/index.ts @@ -0,0 +1,7 @@ +import './index.css'; +export * from './recipes/brandRecipe'; +export * from './recipes/inlineSizeRecipe'; +export * from './recipes/itemSizeRecipe'; +export * from './recipes/variantRecipe'; +export * from './styles/utils/cpm'; +export * from './styles/utils/cvr'; \ No newline at end of file diff --git a/packages/css/src/recipes/brandRecipe.ts b/packages/css/src/recipes/brandRecipe.ts new file mode 100644 index 0000000..2595b99 --- /dev/null +++ b/packages/css/src/recipes/brandRecipe.ts @@ -0,0 +1,13 @@ +import { cvr } from "../styles/utils/cvr"; + +export const brandRecipe = cvr({ + variants: { + brand: { + success: "brand--success", + danger: "brand--danger", + info: "brand--info", + warning: "brand--warning", + emphasize: "brand--emphasize", + }, + }, +}); diff --git a/packages/css/src/recipes/inlineSizeRecipe.ts b/packages/css/src/recipes/inlineSizeRecipe.ts new file mode 100644 index 0000000..2b06111 --- /dev/null +++ b/packages/css/src/recipes/inlineSizeRecipe.ts @@ -0,0 +1,15 @@ +import { cvr } from "../styles/utils/cvr"; + +export const inlineSizeRecipe = cvr({ + base: "display--flex flex--nowrap justify--center items--center", + variants: { + size: { + xs: "text--xs h--inline-xs gap--xs", + sm: "text--sm h--inline-sm gap--sm", + md: "text--md h--inline-md gap--md", + lg: "text--lg h--inline-lg gap--lg", + xl: "text--xl h--inline-xl gap--xl", + "2xl": "text--2xl h--inline-2xl gap--2xl", + }, + }, +}); diff --git a/packages/css/src/recipes/itemSizeRecipe.ts b/packages/css/src/recipes/itemSizeRecipe.ts new file mode 100644 index 0000000..161b03a --- /dev/null +++ b/packages/css/src/recipes/itemSizeRecipe.ts @@ -0,0 +1,46 @@ +import { cvr } from "../styles/utils/cvr"; + +export const itemSizeRecipe = cvr({ + base: "select--none display--flex flex--nowrap justify--center items--center", + variants: { + size: { + xs: "text--xs h--item-xs gap--xs px--xs", + sm: "text--sm h--item-sm gap-sm px--sm", + md: "text--md h--item-md gap-md px--md", + lg: "text--lg h--item-lg gap-lg px--lg", + xl: "text--xl h--item-xl gap-xl px--xl", + "2xl": "text--2xl h--item-2xl gap--2xl px--2xl", + }, + shape: { + square: "rounded--none", + rounded: "", + circle: "rounded--full", + }, + iconOnly: { + true: "px--none", + false: "", + }, + }, + compoundVariants: [ + { iconOnly: false, size: "xs", class: "px--xs" }, + { iconOnly: false, size: "sm", class: "px--sm" }, + { iconOnly: false, size: "md", class: "px--md" }, + { iconOnly: false, size: "lg", class: "px--lg" }, + { iconOnly: false, size: "xl", class: "px--xl" }, + { iconOnly: false, size: "2xl", class: "px--2xl" }, + // ================================================== + { shape: "rounded", size: "xs", class: "rounded--sm" }, + { shape: "rounded", size: "sm", class: "rounded--md" }, + { shape: "rounded", size: "md", class: "rounded--lg" }, + { shape: "rounded", size: "lg", class: "rounded--xl" }, + { shape: "rounded", size: "xl", class: "rounded--2xl" }, + { shape: "rounded", size: "2xl", class: "rounded--3xl" }, + // ================================================== + { iconOnly: true, size: "xs", class: "w--item-xs" }, + { iconOnly: true, size: "sm", class: "w--item-sm" }, + { iconOnly: true, size: "md", class: "w--item-md" }, + { iconOnly: true, size: "lg", class: "w--item-lg" }, + { iconOnly: true, size: "xl", class: "w--item-xl" }, + { iconOnly: true, size: "2xl", class: "w--item-2xl" }, + ], +}); diff --git a/packages/css/src/recipes/variantRecipe.ts b/packages/css/src/recipes/variantRecipe.ts new file mode 100644 index 0000000..52c3ee5 --- /dev/null +++ b/packages/css/src/recipes/variantRecipe.ts @@ -0,0 +1,58 @@ +import { cvr } from "../styles/utils/cvr"; + +export const variantRecipe = cvr({ + variants: { + variant: { + filled: "", + outline: "", + subtle: "", + ghost: "", + }, + disabled: { + true: "", + false: "", + }, + }, + compoundVariants: [ + { + disabled: false, + variant: "filled", + class: "variant--filled", + }, + { + disabled: false, + variant: "outline", + class: "variant--outline", + }, + { + disabled: false, + variant: "subtle", + class: "variant--subtle", + }, + { + disabled: false, + variant: "ghost", + class: "variant--ghost", + }, + { + disabled: true, + variant: "filled", + class: "variant--filled-disabled", + }, + { + disabled: true, + variant: "outline", + class: "variant--outline-disabled", + }, + { + disabled: true, + variant: "subtle", + class: "variant--subtle-disabled", + }, + { + disabled: true, + variant: "ghost", + class: "variant--ghost-disabled", + }, + ], +}); diff --git a/packages/ui-web/src/styles/base/root.css b/packages/css/src/styles/base/root.css similarity index 98% rename from packages/ui-web/src/styles/base/root.css rename to packages/css/src/styles/base/root.css index 4f36dba..ee4215a 100644 --- a/packages/ui-web/src/styles/base/root.css +++ b/packages/css/src/styles/base/root.css @@ -1,3 +1,5 @@ +@layer utility; + :root { --color-red-50: oklch(97.1% 0.013 17.38); --color-red-100: oklch(93.6% 0.032 17.717); @@ -317,12 +319,12 @@ --warning-bg-low-hover: var(--color-yellow-200); --warning-bg-low-active: var(--color-yellow-300); - --default-bg: var(--color-neutral-600); - --default-bg-hover: var(--color-neutral-500); - --default-bg-active: var(--color-neutral-400); - --default-bg-low: var(--color-neutral-100); - --default-bg-low-hover: var(--color-neutral-200); - --default-bg-low-active: var(--color-neutral-300); + --emphasize-bg: var(--color-neutral-600); + --emphasize-bg-hover: var(--color-neutral-500); + --emphasize-bg-active: var(--color-neutral-400); + --emphasize-bg-low: var(--color-neutral-100); + --emphasize-bg-low-hover: var(--color-neutral-200); + --emphasize-bg-low-active: var(--color-neutral-300); --disabled-fg: var(--color-gray-400); --disabled-bg: var(--color-gray-100); diff --git a/packages/ui-web/src/styles/utility/size-insensitive/align-content.css b/packages/css/src/styles/utility/size-insensitive/align-content.css similarity index 100% rename from packages/ui-web/src/styles/utility/size-insensitive/align-content.css rename to packages/css/src/styles/utility/size-insensitive/align-content.css diff --git a/packages/ui-web/src/styles/utility/size-insensitive/align-items.css b/packages/css/src/styles/utility/size-insensitive/align-items.css similarity index 100% rename from packages/ui-web/src/styles/utility/size-insensitive/align-items.css rename to packages/css/src/styles/utility/size-insensitive/align-items.css diff --git a/packages/ui-web/src/styles/utility/size-insensitive/align-self.css b/packages/css/src/styles/utility/size-insensitive/align-self.css similarity index 100% rename from packages/ui-web/src/styles/utility/size-insensitive/align-self.css rename to packages/css/src/styles/utility/size-insensitive/align-self.css diff --git a/packages/ui-web/src/styles/utility/size-insensitive/animation.css b/packages/css/src/styles/utility/size-insensitive/animation.css similarity index 100% rename from packages/ui-web/src/styles/utility/size-insensitive/animation.css rename to packages/css/src/styles/utility/size-insensitive/animation.css diff --git a/packages/ui-web/src/styles/utility/size-insensitive/box-decoration.css b/packages/css/src/styles/utility/size-insensitive/box-decoration.css similarity index 100% rename from packages/ui-web/src/styles/utility/size-insensitive/box-decoration.css rename to packages/css/src/styles/utility/size-insensitive/box-decoration.css diff --git a/packages/ui-web/src/styles/utility/size-insensitive/box-inside.css b/packages/css/src/styles/utility/size-insensitive/box-inside.css similarity index 100% rename from packages/ui-web/src/styles/utility/size-insensitive/box-inside.css rename to packages/css/src/styles/utility/size-insensitive/box-inside.css diff --git a/packages/ui-web/src/styles/utility/size-insensitive/box-sizing.css b/packages/css/src/styles/utility/size-insensitive/box-sizing.css similarity index 100% rename from packages/ui-web/src/styles/utility/size-insensitive/box-sizing.css rename to packages/css/src/styles/utility/size-insensitive/box-sizing.css diff --git a/packages/ui-web/src/styles/utility/size-insensitive/brand.css b/packages/css/src/styles/utility/size-insensitive/brand.css similarity index 79% rename from packages/ui-web/src/styles/utility/size-insensitive/brand.css rename to packages/css/src/styles/utility/size-insensitive/brand.css index b34b0fe..cad8578 100644 --- a/packages/ui-web/src/styles/utility/size-insensitive/brand.css +++ b/packages/css/src/styles/utility/size-insensitive/brand.css @@ -7,6 +7,7 @@ --brand-bg-low-hover: var(--info-bg-low-hover); --brand-bg-low-active: var(--info-bg-low-active); } + .brand--danger { --brand-bg: var(--danger-bg); --brand-bg-hover: var(--danger-bg-hover); @@ -15,6 +16,7 @@ --brand-bg-low-hover: var(--danger-bg-low-hover); --brand-bg-low-active: var(--danger-bg-low-active); } + .brand--success { --brand-bg: var(--success-bg); --brand-bg-hover: var(--success-bg-hover); @@ -23,6 +25,7 @@ --brand-bg-low-hover: var(--success-bg-low-hover); --brand-bg-low-active: var(--success-bg-low-active); } + .brand--warning { --brand-bg: var(--warning-bg); --brand-bg-hover: var(--warning-bg-hover); @@ -31,12 +34,13 @@ --brand-bg-low-hover: var(--warning-bg-low-hover); --brand-bg-low-active: var(--warning-bg-low-active); } - .brand--default { - --brand-bg: var(--default-bg); - --brand-bg-hover: var(--default-bg-hover); - --brand-bg-active: var(--default-bg-active); - --brand-bg-low: var(--default-bg-low); - --brand-bg-low-hover: var(--default-bg-low-hover); - --brand-bg-low-active: var(--default-bg-low-active); + + .brand--emphasize { + --brand-bg: var(--emphasize-bg); + --brand-bg-hover: var(--emphasize-bg-hover); + --brand-bg-active: var(--emphasize-bg-active); + --brand-bg-low: var(--emphasize-bg-low); + --brand-bg-low-hover: var(--emphasize-bg-low-hover); + --brand-bg-low-active: var(--emphasize-bg-low-active); } } diff --git a/packages/ui-web/src/styles/utility/size-insensitive/break-after.css b/packages/css/src/styles/utility/size-insensitive/break-after.css similarity index 100% rename from packages/ui-web/src/styles/utility/size-insensitive/break-after.css rename to packages/css/src/styles/utility/size-insensitive/break-after.css diff --git a/packages/ui-web/src/styles/utility/size-insensitive/break-before.css b/packages/css/src/styles/utility/size-insensitive/break-before.css similarity index 100% rename from packages/ui-web/src/styles/utility/size-insensitive/break-before.css rename to packages/css/src/styles/utility/size-insensitive/break-before.css diff --git a/packages/ui-web/src/styles/utility/size-insensitive/cursor.css b/packages/css/src/styles/utility/size-insensitive/cursor.css similarity index 100% rename from packages/ui-web/src/styles/utility/size-insensitive/cursor.css rename to packages/css/src/styles/utility/size-insensitive/cursor.css diff --git a/packages/ui-web/src/styles/utility/size-insensitive/display.css b/packages/css/src/styles/utility/size-insensitive/display.css similarity index 100% rename from packages/ui-web/src/styles/utility/size-insensitive/display.css rename to packages/css/src/styles/utility/size-insensitive/display.css diff --git a/packages/ui-web/src/styles/utility/size-insensitive/drop-shadow.css b/packages/css/src/styles/utility/size-insensitive/drop-shadow.css similarity index 100% rename from packages/ui-web/src/styles/utility/size-insensitive/drop-shadow.css rename to packages/css/src/styles/utility/size-insensitive/drop-shadow.css diff --git a/packages/ui-web/src/styles/utility/size-insensitive/flex-direction.css b/packages/css/src/styles/utility/size-insensitive/flex-direction.css similarity index 100% rename from packages/ui-web/src/styles/utility/size-insensitive/flex-direction.css rename to packages/css/src/styles/utility/size-insensitive/flex-direction.css diff --git a/packages/ui-web/src/styles/utility/size-insensitive/flex-wrap.css b/packages/css/src/styles/utility/size-insensitive/flex-wrap.css similarity index 100% rename from packages/ui-web/src/styles/utility/size-insensitive/flex-wrap.css rename to packages/css/src/styles/utility/size-insensitive/flex-wrap.css diff --git a/packages/ui-web/src/styles/utility/size-insensitive/font-family.css b/packages/css/src/styles/utility/size-insensitive/font-family.css similarity index 100% rename from packages/ui-web/src/styles/utility/size-insensitive/font-family.css rename to packages/css/src/styles/utility/size-insensitive/font-family.css diff --git a/packages/ui-web/src/styles/utility/size-insensitive/justify-content.css b/packages/css/src/styles/utility/size-insensitive/justify-content.css similarity index 100% rename from packages/ui-web/src/styles/utility/size-insensitive/justify-content.css rename to packages/css/src/styles/utility/size-insensitive/justify-content.css diff --git a/packages/ui-web/src/styles/utility/size-insensitive/justify-items.css b/packages/css/src/styles/utility/size-insensitive/justify-items.css similarity index 100% rename from packages/ui-web/src/styles/utility/size-insensitive/justify-items.css rename to packages/css/src/styles/utility/size-insensitive/justify-items.css diff --git a/packages/ui-web/src/styles/utility/size-insensitive/justify-self.css b/packages/css/src/styles/utility/size-insensitive/justify-self.css similarity index 100% rename from packages/ui-web/src/styles/utility/size-insensitive/justify-self.css rename to packages/css/src/styles/utility/size-insensitive/justify-self.css diff --git a/packages/ui-web/src/styles/utility/size-insensitive/overflow.css b/packages/css/src/styles/utility/size-insensitive/overflow.css similarity index 100% rename from packages/ui-web/src/styles/utility/size-insensitive/overflow.css rename to packages/css/src/styles/utility/size-insensitive/overflow.css diff --git a/packages/ui-web/src/styles/utility/size-insensitive/overscroll-behavior.css b/packages/css/src/styles/utility/size-insensitive/overscroll-behavior.css similarity index 73% rename from packages/ui-web/src/styles/utility/size-insensitive/overscroll-behavior.css rename to packages/css/src/styles/utility/size-insensitive/overscroll-behavior.css index f9fbacc..d22a37b 100644 --- a/packages/ui-web/src/styles/utility/size-insensitive/overscroll-behavior.css +++ b/packages/css/src/styles/utility/size-insensitive/overscroll-behavior.css @@ -11,27 +11,27 @@ overscroll-behavior: none; } - .overscroll--x--auto { + .overscroll--x-auto { overscroll-behavior-x: auto; } - .overscroll--x--contain { + .overscroll--x-contain { overscroll-behavior-x: contain; } - .overscroll--x--none { + .overscroll--x-none { overscroll-behavior-x: none; } - .overscroll--y--auto { + .overscroll--y-auto { overscroll-behavior-y: auto; } - .overscroll--y--contain { + .overscroll--y-contain { overscroll-behavior-y: contain; } - .overscroll--y--none { + .overscroll--y-none { overscroll-behavior-y: none; } -} +} \ No newline at end of file diff --git a/packages/css/src/styles/utility/size-insensitive/position.css b/packages/css/src/styles/utility/size-insensitive/position.css new file mode 100644 index 0000000..786d32e --- /dev/null +++ b/packages/css/src/styles/utility/size-insensitive/position.css @@ -0,0 +1,19 @@ +.position--static { + position: static; +} + +.position--fixed { + position: fixed; +} + +.position--absolute { + position: absolute; +} + +.position--relative { + position: relative; +} + +.position--sticky { + position: sticky; +} \ No newline at end of file diff --git a/packages/ui-web/src/styles/utility/size-insensitive/screen-reader.css b/packages/css/src/styles/utility/size-insensitive/screen-reader.css similarity index 100% rename from packages/ui-web/src/styles/utility/size-insensitive/screen-reader.css rename to packages/css/src/styles/utility/size-insensitive/screen-reader.css diff --git a/packages/ui-web/src/styles/utility/size-insensitive/theme.css b/packages/css/src/styles/utility/size-insensitive/theme.css similarity index 100% rename from packages/ui-web/src/styles/utility/size-insensitive/theme.css rename to packages/css/src/styles/utility/size-insensitive/theme.css diff --git a/packages/ui-web/src/styles/utility/size-insensitive/user-select.css b/packages/css/src/styles/utility/size-insensitive/user-select.css similarity index 100% rename from packages/ui-web/src/styles/utility/size-insensitive/user-select.css rename to packages/css/src/styles/utility/size-insensitive/user-select.css diff --git a/packages/css/src/styles/utility/size-insensitive/variant.css b/packages/css/src/styles/utility/size-insensitive/variant.css new file mode 100644 index 0000000..55705d9 --- /dev/null +++ b/packages/css/src/styles/utility/size-insensitive/variant.css @@ -0,0 +1,206 @@ +@layer utility { + .variant--filled { + --filled-fg: var(--color-white); + --filled-fg-hover: var(--color-white); + --filled-fg-active: var(--color-white); + --filled-bg: var(--brand-bg); + --filled-bg-hover: var(--brand-bg-hover); + --filled-bg-active: var(--brand-bg-active); + --filled-border-color: var(--color-transparent); + + color: var(--filled-fg); + background-color: var(--filled-bg); + border-color: var(--filled-border-color); + + &:hover { + background-color: var(--filled-bg-hover); + color: var(--filled-fg-hover); + } + + &:active { + background-color: var(--filled-bg-active); + color: var(--filled-fg-active); + } + + &:focus-visible { + background-color: var(--filled-bg-hover); + color: var(--filled-fg-hover); + } + } + + .variant--outline { + --outline-fg: var(--brand-bg); + --outline-fg-hover: var(--brand-bg); + --outline-fg-active: var(--brand-bg); + --outline-bg: var(--color-transparent); + --outline-bg-hover: var(--brand-bg-low); + --outline-bg-active: var(--brand-bg-low-hover); + --outline-border-color: var(--brand-bg); + + color: var(--outline-fg); + background-color: var(--outline-bg); + border: solid 1px; + border-color: var(--outline-border-color); + + &:hover { + background-color: var(--outline-bg-hover); + color: var(--outline-fg-hover); + } + + &:active { + background-color: var(--outline-bg-active); + color: var(--outline-fg-active); + } + + &:focus-visible { + background-color: var(--outline-bg-hover); + color: var(--outline-fg-hover); + } + } + + .variant--subtle { + --subtle-fg: var(--brand-bg); + --subtle-fg-hover: var(--brand-bg); + --subtle-fg-active: var(--brand-bg); + --subtle-bg: var(--brand-bg-low); + --subtle-bg-hover: var(--brand-bg-low-hover); + --subtle-bg-active: var(--brand-bg-low-active); + --subtle-border-color: var(--color-transparent); + + color: var(--subtle-fg); + background-color: var(--subtle-bg); + border-color: var(--subtle-border-color); + + &:hover { + background-color: var(--subtle-bg-hover); + color: var(--subtle-fg-hover); + } + + &:active { + background-color: var(--subtle-bg-active); + color: var(--subtle-fg-active); + } + + &:focus-visible { + background-color: var(--subtle-bg-hover); + color: var(--subtle-fg-hover); + } + } + + .variant--ghost { + --ghost-fg: var(--brand-bg); + --ghost-fg-hover: var(--brand-bg); + --ghost-fg-active: var(--brand-bg); + --ghost-bg: var(--color-transparent); + --ghost-bg-hover: var(--brand-bg-low); + --ghost-bg-active: var(--brand-bg-low-hover); + --ghost-border-color: var(--color-transparent); + + color: var(--ghost-fg); + background-color: var(--ghost-bg); + border-color: var(--ghost-border-color); + + &:hover { + background-color: var(--ghost-bg-hover); + color: var(--ghost-fg-hover); + } + + &:active { + background-color: var(--ghost-bg-active); + color: var(--ghost-fg-active); + } + + &:focus-visible { + background-color: var(--ghost-bg-hover); + color: var(--ghost-fg-hover); + } + } + + .variant--filled-disabled { + color: var(--disabled-fg); + background-color: var(--disabled-bg); + border-color: var(--disabled-border-color); + filter: grayscale(100%); + + &:hover { + color: var(--disabled-fg); + background-color: var(--disabled-bg); + border-color: var(--disabled-border-color); + filter: grayscale(100%); + } + + &:active { + color: var(--disabled-fg); + background-color: var(--disabled-bg); + border-color: var(--disabled-border-color); + filter: grayscale(100%); + } + + &:focus-visible { + color: var(--disabled-fg); + background-color: var(--disabled-bg); + border-color: var(--disabled-border-color); + filter: grayscale(100%); + } + } + + .variant--outline-disabled { + color: var(--disabled-fg); + background-color: var(--disabled-bg); + border: solid 1px; + border-color: var(--disabled-border-color); + filter: grayscale(100%); + + &:hover { + color: var(--disabled-fg); + background-color: var(--disabled-bg); + border: solid 1px; + border-color: var(--disabled-border-color); + filter: grayscale(100%); + } + + &:active { + color: var(--disabled-fg); + background-color: var(--disabled-bg); + border: solid 1px; + border-color: var(--disabled-border-color); + filter: grayscale(100%); + } + + &:focus-visible { + color: var(--disabled-fg); + background-color: var(--disabled-bg); + border: solid 1px; + border-color: var(--disabled-border-color); + filter: grayscale(100%); + } + } + + .variant--ghost-disabled { + color: var(--ghost-fg); + background-color: var(--ghost-bg); + border-color: var(--ghost-border-color); + filter: grayscale(50%); + + &:hover { + color: var(--ghost-fg); + background-color: var(--ghost-bg); + border-color: var(--ghost-border-color); + filter: grayscale(50%); + } + + &:active { + color: var(--ghost-fg); + background-color: var(--ghost-bg); + border-color: var(--ghost-border-color); + filter: grayscale(50%); + } + + &:focus-visible { + color: var(--ghost-fg); + background-color: var(--ghost-bg); + border-color: var(--ghost-border-color); + filter: grayscale(50%); + } + } +} diff --git a/packages/ui-web/src/styles/utility/size-insensitive/z-index.css b/packages/css/src/styles/utility/size-insensitive/z-index.css similarity index 84% rename from packages/ui-web/src/styles/utility/size-insensitive/z-index.css rename to packages/css/src/styles/utility/size-insensitive/z-index.css index d42e57c..7e9895e 100644 --- a/packages/ui-web/src/styles/utility/size-insensitive/z-index.css +++ b/packages/css/src/styles/utility/size-insensitive/z-index.css @@ -1,8 +1,10 @@ @layer utility { + /* 自动 & 零 */ .z--auto { z-index: auto; } + .z--0 { z-index: 0; } @@ -11,33 +13,41 @@ .z--10 { z-index: 10; } + .z--20 { z-index: 20; } + .z--30 { z-index: 30; } + .z--40 { z-index: 40; } + .z--50 { z-index: 50; } /* 负值(negative) */ - .z--n--10 { + .z--n-10 { z-index: -10; } - .z--n--20 { + + .z--n-20 { z-index: -20; } - .z--n--30 { + + .z--n-30 { z-index: -30; } - .z--n--40 { + + .z--n-40 { z-index: -40; } - .z--n--50 { + + .z--n-50 { z-index: -50; } -} +} \ No newline at end of file diff --git a/packages/ui-web/src/styles/utility/size-sensitive/border-radius.css b/packages/css/src/styles/utility/size-sensitive/border-radius.css similarity index 100% rename from packages/ui-web/src/styles/utility/size-sensitive/border-radius.css rename to packages/css/src/styles/utility/size-sensitive/border-radius.css diff --git a/packages/ui-web/src/styles/utility/size-sensitive/font-size.css b/packages/css/src/styles/utility/size-sensitive/font-size.css similarity index 100% rename from packages/ui-web/src/styles/utility/size-sensitive/font-size.css rename to packages/css/src/styles/utility/size-sensitive/font-size.css diff --git a/packages/ui-web/src/styles/utility/size-sensitive/font-weight.css b/packages/css/src/styles/utility/size-sensitive/font-weight.css similarity index 100% rename from packages/ui-web/src/styles/utility/size-sensitive/font-weight.css rename to packages/css/src/styles/utility/size-sensitive/font-weight.css diff --git a/packages/ui-web/src/styles/utility/size-sensitive/gap.css b/packages/css/src/styles/utility/size-sensitive/gap.css similarity index 100% rename from packages/ui-web/src/styles/utility/size-sensitive/gap.css rename to packages/css/src/styles/utility/size-sensitive/gap.css diff --git a/packages/ui-web/src/styles/utility/size-sensitive/height.css b/packages/css/src/styles/utility/size-sensitive/height.css similarity index 81% rename from packages/ui-web/src/styles/utility/size-sensitive/height.css rename to packages/css/src/styles/utility/size-sensitive/height.css index 2067c22..e51ee28 100644 --- a/packages/ui-web/src/styles/utility/size-sensitive/height.css +++ b/packages/css/src/styles/utility/size-sensitive/height.css @@ -1,47 +1,58 @@ @layer utility { - .h--item--xs { + .h--item-xs { /* 24px minimum touch size for text line */ height: var(--height-item-xs); } - .h--item--sm { + + .h--item-sm { /* 28px save space for most used size */ height: var(--height-item-sm); } - .h--item--md { + + .h--item-md { /* 34px most used size */ height: var(--height-item-md); } - .h--item--lg { + + .h--item-lg { /* 44px maximum touch size without waste */ height: var(--height-item-lg); } - .h--item--xl { + + .h--item-xl { /* 56px section-header bar */ height: var(--height-item-xl); } - .h--item--2xl { + + .h--item-2xl { /* 64px navigation bar */ height: var(--height-item-2xl); } + /* -------------- */ - .h--inline--xs { + .h--inline-xs { /* 16px */ height: calc(var(--font-size-xs) * var(--line-height-xs)); } - .h--inline--sm { + + .h--inline-sm { /* 20px */ height: calc(var(--font-size-sm) * var(--line-height-sm)); } - .h--inline--md { + + .h--inline-md { height: calc(var(--font-size-md) * var(--line-height-md)); } - .h--inline--lg { + + .h--inline-lg { height: calc(var(--font-size-lg) * var(--line-height-lg)); } - .h--inline--xl { + + .h--inline-xl { height: calc(var(--font-size-xl) * var(--line-height-xl)); } - .h--inline--2xl { + + .h--inline-2xl { height: calc(var(--font-size-2xl) * var(--line-height-2xl)); } -} +} \ No newline at end of file diff --git a/packages/css/src/styles/utility/size-sensitive/margin.css b/packages/css/src/styles/utility/size-sensitive/margin.css new file mode 100644 index 0000000..cc5cc7a --- /dev/null +++ b/packages/css/src/styles/utility/size-sensitive/margin.css @@ -0,0 +1,59 @@ +@layer utility { + .my--none { + margin-block: 0; + } + + .my--xs { + margin-block: var(--margin-xs); + } + + .my--sm { + margin-block: var(--margin-sm); + } + + .my--md { + margin-block: var(--margin-md); + } + + .my--lg { + margin-block: var(--margin-lg); + } + + .my--xl { + margin-block: var(--margin-xl); + } + + .my--2xl { + margin-block: var(--margin-2xl); + } + + /* ======================== */ + + .mx--none { + margin-inline: 0; + } + + .mx--xs { + margin-inline: var(--margin-xs); + } + + .mx--sm { + margin-inline: var(--margin-sm); + } + + .mx--md { + margin-inline: var(--margin-md); + } + + .mx--lg { + margin-inline: var(--margin-lg); + } + + .mx--xl { + margin-inline: var(--margin-xl); + } + + .mx--2xl { + margin-inline: var(--margin-2xl); + } +} \ No newline at end of file diff --git a/packages/ui-web/src/styles/utility/size-sensitive/padding-short.css b/packages/css/src/styles/utility/size-sensitive/padding.css similarity index 63% rename from packages/ui-web/src/styles/utility/size-sensitive/padding-short.css rename to packages/css/src/styles/utility/size-sensitive/padding.css index 73e0fb0..43d80a3 100644 --- a/packages/ui-web/src/styles/utility/size-sensitive/padding-short.css +++ b/packages/css/src/styles/utility/size-sensitive/padding.css @@ -2,48 +2,90 @@ .px--none { padding-inline: 0px; } + .px--xs { /* 4px, 24px - 16px = 8px */ padding-inline: var(--padding-xs); } + .px--sm { /* 4px, 28px - 20px = 8px */ padding-inline: var(--padding-sm); } + .px--md { /* 10px */ padding-inline: var(--padding-md); } + .px--lg { /* 16px */ padding-inline: var(--padding-lg); } + .px--xl { padding-inline: var(--padding-xl); } + .px--2xl { padding-inline: var(--padding-2xl); } + /* ------------------------ */ .py--none { padding-block: 0px; } + .py--xs { padding-block: var(--padding-xs); } + .py--sm { padding-block: var(--padding-sm); } + .py--md { padding-block: var(--padding-md); } + .py--lg { padding-block: var(--padding-lg); } + .py--xl { padding-block: var(--padding-xl); } + .py--2xl { padding-block: var(--padding-2xl); } -} + + /* ========================== */ + .px--long-xs { + /* 6px */ + padding-inline: var(--padding-long-xs); + } + + .px--long-sm { + /* 8px */ + padding-inline: var(--padding-long-sm); + } + + .px--long-md { + /* 10px */ + padding-inline: var(--padding-long-md); + } + + .px--long-lg { + /* 16px */ + padding-inline: var(--padding-long-lg); + } + + .px--long-xl { + padding-inline: var(--padding-long-xl); + } + + .px--long-2xl { + padding-inline: var(--padding-long-2xl); + } +} \ No newline at end of file diff --git a/packages/ui-web/src/styles/utility/size-sensitive/width.css b/packages/css/src/styles/utility/size-sensitive/width.css similarity index 84% rename from packages/ui-web/src/styles/utility/size-sensitive/width.css rename to packages/css/src/styles/utility/size-sensitive/width.css index 788dc24..c90e8d3 100644 --- a/packages/ui-web/src/styles/utility/size-sensitive/width.css +++ b/packages/css/src/styles/utility/size-sensitive/width.css @@ -1,45 +1,56 @@ @layer utility { - .w--item--xs { + .w--item-xs { /* 24px minimum touch size for text line */ width: var(--height-item-xs); } - .w--item--sm { + + .w--item-sm { /* 30px save space for most used size */ width: var(--height-item-sm); } - .w--item--md { + + .w--item-md { /* 34px most used size */ width: var(--height-item-lg); } + .w--item-lg { /* 44px maximum touch size without waste */ width: var(--height-item-lg); } - .w--item--xl { + + .w--item-xl { /* 56px section-header bar */ width: var(--height-item-xl); } + .w--item--2xl { /* 64px navigation bar */ width: var(--height-item-xl); } + /* ---------------------------------------------------- */ - .w--inline--xs { + .w--inline-xs { width: calc(var(--font-size-xs) * var(--line-height-xs)); } - .w--inline--sm { + + .w--inline-sm { width: calc(var(--font-size-sm) * var(--line-height-sm)); } - .w--inline--md { + + .w--inline-md { width: calc(var(--font-size-md) * var(--line-height-md)); } - .w--inline--lg { + + .w--inline-lg { width: calc(var(--font-size-lg) * var(--line-height-lg)); } - .w--inline--xl { + + .w--inline-xl { width: calc(var(--font-size-xl) * var(--line-height-xl)); } - .w--inline--xl { + + .w--inline-xl { width: calc(var(--font-size-2xl) * var(--line-height-2xl)); } -} +} \ No newline at end of file diff --git a/packages/css/src/styles/utils/cpm.ts b/packages/css/src/styles/utils/cpm.ts new file mode 100644 index 0000000..c20f0e2 --- /dev/null +++ b/packages/css/src/styles/utils/cpm.ts @@ -0,0 +1,27 @@ +export function cpm(...classes: (string[] | string)[]) { + const map = new Map(); + + function dealClass(cls: string) { + // 提取前缀和后缀 + const prifix = cls.split("--")[0]; + + // 使用set覆盖(自动新建),而不是push + map.set(prifix, cls); + } + + classes.forEach((item) => { + // 如果是 string 直接处理 + if (typeof item === "string") { + dealClass(item); + } else { + // 否则肯定是 string[] + item.forEach((i) => { + dealClass(i); + }); + } + }); + + const resultString = Array.from(map.values()).join(" "); + + return resultString; +} diff --git a/packages/ui-web/src/styles/utils/cvr.ts b/packages/css/src/styles/utils/cvr.ts similarity index 100% rename from packages/ui-web/src/styles/utils/cvr.ts rename to packages/css/src/styles/utils/cvr.ts diff --git a/packages/ui-web/src/types/env.d.ts b/packages/css/src/types/env.d.ts similarity index 100% rename from packages/ui-web/src/types/env.d.ts rename to packages/css/src/types/env.d.ts diff --git a/packages/css/tsconfig.build.json b/packages/css/tsconfig.build.json new file mode 100644 index 0000000..a5bcc27 --- /dev/null +++ b/packages/css/tsconfig.build.json @@ -0,0 +1,41 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": false, + "emitDeclarationOnly": true, + "declaration": true, + "rootDir": "./src", + "outDir": "./dist", + "declarationDir": "./dist" + }, + "include": ["src"], + "exclude": [ + "node_modules", + "dist", + ".turbo/**/*", + ".cache/**/*", + ".vite/**/*", + "vite.config.ts", + "*.config.ts", + "*.config.js", + "tsconfig.*.json", + "__tests__/**/*", + "test/**/*", + "tests/**/*", + "**/*.test.ts", + "**/*.test.tsx", + "**/*.spec.ts", + "**/*.spec.tsx", + ".storybook/**/*", + "stories/**/*", + "example/**/*", + "examples/**/*", + "scripts/**/*", + ".env", + ".env.*", + "public/**/*", + "docs/**/*", + "README.md", + "LICENSE" + ] +} diff --git a/packages/ui-web/tsconfig.json b/packages/css/tsconfig.json similarity index 94% rename from packages/ui-web/tsconfig.json rename to packages/css/tsconfig.json index f79e3c5..fcbe200 100644 --- a/packages/ui-web/tsconfig.json +++ b/packages/css/tsconfig.json @@ -14,8 +14,7 @@ "verbatimModuleSyntax": true, "moduleDetection": "force", "noEmit": true, - "jsx": "react-jsx", - + "jsx": "react-jsx" }, "include": ["src", "scripts", "vite.config.ts"] } diff --git a/packages/css/vite.config.ts b/packages/css/vite.config.ts new file mode 100644 index 0000000..ea4923d --- /dev/null +++ b/packages/css/vite.config.ts @@ -0,0 +1,39 @@ +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; +import path from "path"; + +export default defineConfig({ + plugins: [react()], + + build: { + cssMinify: false, + + lib: { + entry: path.resolve(import.meta.dirname, "src/index.ts"), + formats: ["es", "cjs"], + fileName: (format) => `index.${format}.js`, + }, + + rolldownOptions: { + external: ["react", "react-dom", "react/jsx-runtime"], + output: { + // 强制将生成的 CSS 命名为 index.css + assetFileNames: (assetInfo) => { + if (assetInfo.name && assetInfo.name.endsWith(".css")) { + return "index.css"; + } + return "[name].[hash][extname]"; + }, + globals: { + react: "React", + "react-dom": "ReactDOM", + }, + }, + }, + + emptyOutDir: true, + sourcemap: true, + cssCodeSplit: false, + outDir: "dist", + }, +}); diff --git a/packages/ui-web/package.json b/packages/ui-react/package.json similarity index 92% rename from packages/ui-web/package.json rename to packages/ui-react/package.json index 1fd1e67..d0cbc0a 100644 --- a/packages/ui-web/package.json +++ b/packages/ui-react/package.json @@ -1,5 +1,5 @@ { - "name": "@defgov/ui-web", + "name": "@dg/ui-react", "version": "0.0.0", "private": true, "type": "module", @@ -24,9 +24,11 @@ "scripts": { "gen-index": "ts-node scripts/gen-index.ts", "gen-dts": "tsc -p tsconfig.build.json", - "dev": "pnpm gen-index && vite", "build": "pnpm gen-index && vite build && pnpm gen-dts" }, + "dependencies": { + "@dg/css": "workspace:*" + }, "devDependencies": { "@types/node": "^25.6.0", "@vitejs/plugin-react": "^6.0.1", diff --git a/packages/ui-react/scripts/gen-index.ts b/packages/ui-react/scripts/gen-index.ts new file mode 100644 index 0000000..c685295 --- /dev/null +++ b/packages/ui-react/scripts/gen-index.ts @@ -0,0 +1,254 @@ +import * as fs from "fs"; +import * as path from "path"; + +/** + * 默认测试 / story / example / fixture / type 文件匹配规则 + * 仅用于“识别”,不直接决定是否排除 + */ +const DEFAULT_TEST_FILE_PATTERNS: RegExp[] = [ + // ========================= + // Test / Spec + // ========================= + /\.(test|spec)\.(ts|tsx|js|jsx)$/, + /\.(test|spec)\.(ts|tsx|js|jsx)\?.*$/, // query param safe + + // ========================= + // E2E / Playwright / Cypress + // ========================= + /\.(cy|playwright|e2e)\.(ts|tsx|js|jsx)$/, + + // ========================= + // Storybook + // ========================= + /\.(story|stories)\.(ts|tsx|js|jsx|mdx)$/, + + // ========================= + // Snapshot / Mock / Fixture + // ========================= + /\.snap$/, + /\.mock\.(ts|tsx|js|jsx)$/, + /\.fixture\.(ts|tsx|js|jsx)$/, + + // ========================= + // Type declarations + // ========================= + /\.d\.ts$/, + + // ========================= + // Examples / Demos / Docs + // ========================= + /\.(example|demo|docs)\.(ts|tsx|js|jsx)$/, +]; + +/** + * 测试 / 辅助目录(目录级排除) + */ +const DEFAULT_TEST_DIR_PATTERNS: RegExp[] = [ + /[/\\](__tests__|tests|test|spec|__mocks__|__fixtures__|__snapshots__)[\/\\]/, + /[/\\](cypress|playwright|e2e|__e2e__|__playwright__)[\/\\]/, + /[/\\](story|stories|\.storybook)[\/\\]/, + /[/\\](types|type|typings|interfaces)[\/\\]/, + /[/\\](examples|example|demo|demos|docs)[\/\\]/, +]; + +type Config = { + outputDir: string; + outputFile: string; + scanDirs: string[]; + preamble?: string[]; + importPrefix?: string; + entryFilePatterns?: RegExp[]; + barrelFirstMode?: boolean; + includeFilePatterns?: RegExp[]; + excludeFilePatterns?: RegExp[]; + excludeDirPatterns?: RegExp[]; +}; + +const tsConfig: Config = { + outputDir: "src", + outputFile: "index.ts", + scanDirs: ["src"], + importPrefix: "export * from", + barrelFirstMode: true, + preamble: ["import './index.css';"], + entryFilePatterns: [/^index\.(js|ts|jsx|tsx)$/], + includeFilePatterns: [/\.(js|ts|jsx|tsx)$/], + excludeFilePatterns: DEFAULT_TEST_FILE_PATTERNS, + excludeDirPatterns: DEFAULT_TEST_DIR_PATTERNS, +}; + +const cssConfig: Config = { + outputDir: "src", + outputFile: "index.css", + scanDirs: ["src"], + importPrefix: "@import", + barrelFirstMode: false, + entryFilePatterns: [/^index\.(css)$/], + includeFilePatterns: [/\.(css)$/], + excludeFilePatterns: DEFAULT_TEST_FILE_PATTERNS, + excludeDirPatterns: DEFAULT_TEST_DIR_PATTERNS, +}; + +function isEntryFile(fileName: string, config: Config): boolean { + const regExps = config.entryFilePatterns; + + if (!regExps || regExps.length === 0) return false; + + return regExps.some((regExp) => regExp.test(fileName)); +} + +function isIncludeFile(fileName: string, config: Config): boolean { + const regExps = config.includeFilePatterns; + + if (!regExps || regExps.length === 0) return false; + + return regExps.some((regExp) => regExp.test(fileName)); +} + +function isExcludeFile(fileName: string, config: Config): boolean { + const regExps = config.excludeFilePatterns; + + if (!regExps || regExps.length === 0) return false; + + return regExps.some((regExp) => regExp.test(fileName)); +} + +function isExcludeDir(filePath: string, config: Config): boolean { + const regExps = config.excludeDirPatterns; + + if (!regExps || regExps.length === 0) return false; + + return regExps.some((regExp) => regExp.test(filePath)); +} + +function isValidDir(filePath: string, config: Config): boolean { + if (isExcludeDir(filePath, config)) { + return false; + } + return true; +} + +function isValidFile(fileName: string, config: Config): boolean { + if (isIncludeFile(fileName, config) && !isExcludeFile(fileName, config)) { + return true; + } + + return false; +} + +const buildExportStatement = (filePath: string, config: Config) => { + // 文件夹(不是文件)与目标文件的相对路径 + let exportFilePath = path.relative(config.outputDir, filePath); + + // 去除扩展名,并且转化成正斜杠 + const exportFilePathWithoutExt = path + .join( + path.dirname(exportFilePath), + path.basename(exportFilePath, path.extname(exportFilePath)), + ) + .replace(/\\/g, "/"); + + // 加上 ./ 前缀 + return `${config.importPrefix} './${exportFilePathWithoutExt}';`; +}; + +function isOutputEntry(filePath: string, config: Config) { + const outputFilePath = path.resolve(config.outputDir, config.outputFile); + const targetFilePath = path.resolve(filePath); + + return outputFilePath === targetFilePath; +} + +function generateExports(dirPath: string, config: Config): string[] { + const fileNames = fs.readdirSync(dirPath); + const exports: string[] = []; + + // ========================= + // 逻辑分支:Barrel First Mode + // ========================= + if (config.barrelFirstMode) { + // 查找当前目录有没有 index.ts + const entryFileName = fileNames.find((fileName) => { + const filePath = path.join(dirPath, fileName); + return fs.statSync(filePath).isFile() && isEntryFile(fileName, config); + }); + // 如果有 + if (entryFileName) { + const entryFilePath = path.join(dirPath, entryFileName); + + // 如果是 outputFile 自身,则跳过 + if (isOutputEntry(entryFilePath, config)) { + console.log(`⏭️ 跳过自身入口文件: ${entryFilePath}`); + } else { + exports.push(buildExportStatement(entryFilePath, config)); + } + } + } + + // ========================= + // 逻辑分支:index.ts 不存在,则继续正常遍历与递归 + // ========================= + fileNames.forEach((fileName) => { + const filePath = path.join(dirPath, fileName); + const stat = fs.statSync(filePath); + + // 情况1:是文件,且通过了校验 + if ( + stat.isFile() && + isValidFile(fileName, config) && + !isEntryFile(fileName, config) + ) { + exports.push(buildExportStatement(filePath, config)); + } + // 情况2:是文件夹,且通过了校验,递归扫描子文件夹 + else if (stat.isDirectory() && isValidDir(filePath, config)) { + const subExports = generateExports(filePath, config); + exports.push(...subExports); + } + }); + + return exports; +} + +function genIndexFile(config: Config) { + // 确保输出目录存在,如果不存在就递归创建 + if (!fs.existsSync(config.outputDir)) { + fs.mkdirSync(config.outputDir, { recursive: true }); + } + + const allExports: string[] = []; + + // 遍历所有需要扫描的根目录 + config.scanDirs.forEach((scanDir) => { + // 只有当目录真实存在时才进行扫描 + if (fs.existsSync(scanDir)) { + const exports = generateExports(scanDir, config); + allExports.push(...exports); + } else { + console.warn(`⚠️ 警告:扫描目录不存在,已跳过 -> ${scanDir}`); + } + }); + + // 拼接最终的文件内容:前言 + 导出语句(使用 Set 自动去重) + const fileContent = [ + ...(config.preamble ?? []), + ...Array.from(new Set(allExports)), + ].join("\n"); + + // 将内容写入到最终的 index.ts 文件中 + const outputFilePath = path.join(config.outputDir, config.outputFile); + fs.writeFileSync(outputFilePath, fileContent, "utf-8"); + + console.log(`✨ 成功生成入口文件: ${outputFilePath}`); +} + +// ================= 脚本执行入口 ================= +try { + console.log("🚀 开始扫描并生成入口文件..."); + genIndexFile(tsConfig); + genIndexFile(cssConfig); + console.log("✅ 脚本执行完毕!"); +} catch (error) { + console.error("❌ 脚本执行失败:", error); + process.exit(1); // 如果报错,让进程以非 0 状态码退出 +} diff --git a/packages/ui-react/src/assets/svg/BoldSvg.tsx b/packages/ui-react/src/assets/svg/BoldSvg.tsx new file mode 100644 index 0000000..3f61f26 --- /dev/null +++ b/packages/ui-react/src/assets/svg/BoldSvg.tsx @@ -0,0 +1,12 @@ +export const BoldSvg = (props: React.SVGProps) => ( + + + +); diff --git a/packages/ui-react/src/assets/svg/CheckIndicatorSvg.tsx b/packages/ui-react/src/assets/svg/CheckIndicatorSvg.tsx new file mode 100644 index 0000000..6b63f79 --- /dev/null +++ b/packages/ui-react/src/assets/svg/CheckIndicatorSvg.tsx @@ -0,0 +1,8 @@ +export const CheckIndicatorSvg = (props: React.SVGProps) => ( + + + +); diff --git a/packages/ui-react/src/assets/svg/ChevronRightSvg.tsx b/packages/ui-react/src/assets/svg/ChevronRightSvg.tsx new file mode 100644 index 0000000..612809c --- /dev/null +++ b/packages/ui-react/src/assets/svg/ChevronRightSvg.tsx @@ -0,0 +1,12 @@ +export const ChevronRightSvg = (props: React.SVGProps) => ( + + + +); diff --git a/packages/ui-react/src/assets/svg/CutSvg.tsx b/packages/ui-react/src/assets/svg/CutSvg.tsx new file mode 100644 index 0000000..3b953fc --- /dev/null +++ b/packages/ui-react/src/assets/svg/CutSvg.tsx @@ -0,0 +1,8 @@ +export const CutSvg = (props: React.SVGProps) => ( + + + +); diff --git a/packages/ui-react/src/assets/svg/DownloadSvg.tsx b/packages/ui-react/src/assets/svg/DownloadSvg.tsx new file mode 100644 index 0000000..ce6a0ca --- /dev/null +++ b/packages/ui-react/src/assets/svg/DownloadSvg.tsx @@ -0,0 +1,8 @@ +export const DownloadSvg = (props: React.SVGProps) => ( + + + +); diff --git a/packages/ui-react/src/assets/svg/FileSvg.tsx b/packages/ui-react/src/assets/svg/FileSvg.tsx new file mode 100644 index 0000000..1edb89f --- /dev/null +++ b/packages/ui-react/src/assets/svg/FileSvg.tsx @@ -0,0 +1,14 @@ +export const FileSvg = (props: React.SVGProps) => ( + + + + + + +); diff --git a/packages/ui-react/src/assets/svg/KeySvg.tsx b/packages/ui-react/src/assets/svg/KeySvg.tsx new file mode 100644 index 0000000..8136e7e --- /dev/null +++ b/packages/ui-react/src/assets/svg/KeySvg.tsx @@ -0,0 +1,8 @@ +export const KeySvg = (props: React.SVGProps) => ( + + + +); diff --git a/packages/ui-react/src/assets/svg/MeshSvg.tsx b/packages/ui-react/src/assets/svg/MeshSvg.tsx new file mode 100644 index 0000000..3ecb96b --- /dev/null +++ b/packages/ui-react/src/assets/svg/MeshSvg.tsx @@ -0,0 +1,14 @@ +export const MeshSvg = (props: React.SVGProps) => ( + + + + + + +); diff --git a/packages/ui-react/src/assets/svg/MoonSvg.tsx b/packages/ui-react/src/assets/svg/MoonSvg.tsx new file mode 100644 index 0000000..5304a65 --- /dev/null +++ b/packages/ui-react/src/assets/svg/MoonSvg.tsx @@ -0,0 +1,12 @@ +export const MoonSvg = (props: React.SVGProps) => ( + + + +); diff --git a/packages/ui-react/src/assets/svg/PasteSvg.tsx b/packages/ui-react/src/assets/svg/PasteSvg.tsx new file mode 100644 index 0000000..244848e --- /dev/null +++ b/packages/ui-react/src/assets/svg/PasteSvg.tsx @@ -0,0 +1,8 @@ +export const PasteSvg = (props: React.SVGProps) => ( + + + +); diff --git a/packages/ui-react/src/assets/svg/Ruler.tsx b/packages/ui-react/src/assets/svg/Ruler.tsx new file mode 100644 index 0000000..7f59fdc --- /dev/null +++ b/packages/ui-react/src/assets/svg/Ruler.tsx @@ -0,0 +1,12 @@ +export const RulerSvg = (props: React.SVGProps) => ( + + + +); diff --git a/packages/ui-react/src/assets/svg/SearchSvg.tsx b/packages/ui-react/src/assets/svg/SearchSvg.tsx new file mode 100644 index 0000000..fc05409 --- /dev/null +++ b/packages/ui-react/src/assets/svg/SearchSvg.tsx @@ -0,0 +1,8 @@ +export const SearchSvg = (props: React.SVGProps) => ( + + + +); diff --git a/packages/ui-react/src/assets/svg/SettingSvg.tsx b/packages/ui-react/src/assets/svg/SettingSvg.tsx new file mode 100644 index 0000000..c4baf2e --- /dev/null +++ b/packages/ui-react/src/assets/svg/SettingSvg.tsx @@ -0,0 +1,9 @@ +export const SettingSvg = (props: React.SVGProps) => ( + + + +); diff --git a/packages/ui-react/src/assets/svg/SpinnerSvg.tsx b/packages/ui-react/src/assets/svg/SpinnerSvg.tsx new file mode 100644 index 0000000..61cc1f6 --- /dev/null +++ b/packages/ui-react/src/assets/svg/SpinnerSvg.tsx @@ -0,0 +1,23 @@ +export const SpinnerSvg = (props: React.SVGProps) => { + return ( + + + + + + + ); +}; diff --git a/packages/ui-react/src/assets/svg/SunSvg.tsx b/packages/ui-react/src/assets/svg/SunSvg.tsx new file mode 100644 index 0000000..e977265 --- /dev/null +++ b/packages/ui-react/src/assets/svg/SunSvg.tsx @@ -0,0 +1,12 @@ +export const SunSvg = (props: React.SVGProps) => ( + + + +); diff --git a/packages/ui-react/src/assets/svg/UserSvg.tsx b/packages/ui-react/src/assets/svg/UserSvg.tsx new file mode 100644 index 0000000..ec51b40 --- /dev/null +++ b/packages/ui-react/src/assets/svg/UserSvg.tsx @@ -0,0 +1,14 @@ +export const UserSvg = (props: React.SVGProps) => { + return ( + + + + ); +}; diff --git a/packages/ui-react/src/assets/svg/VolumeHighSvg.tsx b/packages/ui-react/src/assets/svg/VolumeHighSvg.tsx new file mode 100644 index 0000000..5920a0c --- /dev/null +++ b/packages/ui-react/src/assets/svg/VolumeHighSvg.tsx @@ -0,0 +1,12 @@ +export const VolumeHighSvg = (props: React.SVGProps) => ( + + + +); diff --git a/packages/ui-react/src/assets/svg/VolumeLowSvg.tsx b/packages/ui-react/src/assets/svg/VolumeLowSvg.tsx new file mode 100644 index 0000000..818b4c2 --- /dev/null +++ b/packages/ui-react/src/assets/svg/VolumeLowSvg.tsx @@ -0,0 +1,12 @@ +export const VolumeLowSvg = (props: React.SVGProps) => ( + + + +); diff --git a/packages/ui-react/src/assets/svg/VolumeMuteSvg.tsx b/packages/ui-react/src/assets/svg/VolumeMuteSvg.tsx new file mode 100644 index 0000000..abaff84 --- /dev/null +++ b/packages/ui-react/src/assets/svg/VolumeMuteSvg.tsx @@ -0,0 +1,12 @@ +export const VolumeMuteSvg = (props: React.SVGProps) => ( + + + +); diff --git a/packages/ui-react/src/componnets/button/ButtonIcon.tsx b/packages/ui-react/src/componnets/button/ButtonIcon.tsx new file mode 100644 index 0000000..3483b6d --- /dev/null +++ b/packages/ui-react/src/componnets/button/ButtonIcon.tsx @@ -0,0 +1,19 @@ +import { useEffect } from "react"; +import { Slot } from "../../utils/Slot"; +import { useButtonContext } from "./common/ButtonContext"; + +export const ButtonIcon = (props: any) => { + const { children, ...rest } = props; + + const { slotRegistry } = useButtonContext(); + + useEffect(() => { + slotRegistry.setSlots?.((prev) => ({ + ...prev, + icon: {children}, + })); + }, [children, slotRegistry]); + + return null; +}; +ButtonIcon.displayName = "ButtonIcon"; diff --git a/packages/ui-react/src/componnets/button/ButtonLoading.tsx b/packages/ui-react/src/componnets/button/ButtonLoading.tsx new file mode 100644 index 0000000..ada35e1 --- /dev/null +++ b/packages/ui-react/src/componnets/button/ButtonLoading.tsx @@ -0,0 +1,25 @@ +import { useEffect } from "react"; +import type { ReactNode } from "react"; +import { useButtonContext } from "./common/ButtonContext"; +import { Slot } from "../../utils/Slot"; + + +type ButtonLoadingProps = { + children: ReactNode; +}; + +export const ButtonLoading = (props: ButtonLoadingProps) => { + const { children, ...rest } = props; + const { slotRegistry } = useButtonContext(); + + useEffect(() => { + slotRegistry.setSlots?.(prev => ({ + ...prev, + loading: {children}, + })); + }, [children, slotRegistry]); + + return null; +}; + +ButtonLoading.displayName = "ButtonLoading"; \ No newline at end of file diff --git a/packages/ui-react/src/componnets/button/ButtonRoot.tsx b/packages/ui-react/src/componnets/button/ButtonRoot.tsx new file mode 100644 index 0000000..29be6f9 --- /dev/null +++ b/packages/ui-react/src/componnets/button/ButtonRoot.tsx @@ -0,0 +1,60 @@ +import { type ComponentPropsWithoutRef, forwardRef } from "react"; +import { ButtonContext, type ButtonSlot } from "./common/ButtonContext"; +import { useSlotRegistry } from "../../utils/useSlotRegistry"; +import { mergeProps } from "../../utils/mergeProps"; +import { useDefaultedProps } from "../../utils/useDefaultedProps"; +import { brandRecipe, cpm, itemSizeRecipe, variantRecipe } from "@dg/css"; + +export type ButtonRootOwnProps = { + size?: "xs" | "sm" | "md" | "lg"; + variant?: "filled" | "outline" | "subtle" | "ghost"; + shape?: "rounded" | "square" | "circle"; + brand?: "success" | "danger" | "info" | "warning" | "emphasize"; + iconOnly?: boolean; + hideIcon?: boolean; + loading?: boolean; + disabled?: boolean; +}; + +export type ButtonRootPrimitiveProps = Omit< + ComponentPropsWithoutRef<"button">, + keyof ButtonRootOwnProps +>; + +export type ButtonRootProps = ButtonRootOwnProps & ButtonRootPrimitiveProps; + +export const ButtonRoot = forwardRef( + (props, ref) => { + const defaultedProps = useDefaultedProps(props, { + size: "md", + variant: "filled", + shape: "rounded", + brand: "info", + iconOnly: false, + disabled: false, + }); + const { size, variant, shape, brand, iconOnly, disabled, children } = + defaultedProps; + + const clx = cpm( + itemSizeRecipe({ size, shape, iconOnly }), + variantRecipe({ variant, disabled }), + brandRecipe({ brand }), + ); + + const mergedProps = mergeProps({ className: clx }, defaultedProps); + + const slotRegistry = useSlotRegistry(); + + return ( + + + + ); + }, +); +ButtonRoot.displayName = "Button.Root"; diff --git a/packages/ui-react/src/componnets/button/common/ButtonContext.ts b/packages/ui-react/src/componnets/button/common/ButtonContext.ts new file mode 100644 index 0000000..dd1e25d --- /dev/null +++ b/packages/ui-react/src/componnets/button/common/ButtonContext.ts @@ -0,0 +1,26 @@ +// ButtonContext.ts +import { createContext, useContext, type ReactNode } from "react"; + +export type ButtonSlot = { + icon?: ReactNode; + loading?: ReactNode; +}; + +type ButtonContextValue = { + slotRegistry: { + slots?: ButtonSlot; + setSlots?: React.Dispatch>; + }; +}; + +export const ButtonContext = createContext( + undefined, +); + +export const useButtonContext = () => { + const ctx = useContext(ButtonContext); + if (!ctx) { + throw new Error("Button 子组件必须在 Button.Root 内使用"); + } + return ctx; +}; diff --git a/packages/ui-web/src/componnets/button/index.ts b/packages/ui-react/src/componnets/button/index.ts similarity index 50% rename from packages/ui-web/src/componnets/button/index.ts rename to packages/ui-react/src/componnets/button/index.ts index 2ab94cd..e32665e 100644 --- a/packages/ui-web/src/componnets/button/index.ts +++ b/packages/ui-react/src/componnets/button/index.ts @@ -2,8 +2,8 @@ import { ButtonIcon } from "./ButtonIcon"; import { ButtonLoading } from "./ButtonLoading"; import { ButtonRoot } from "./ButtonRoot"; -export namespace Button { - export const Root = ButtonRoot; - export const Icon = ButtonIcon; - export const Loading = ButtonLoading; -} +export const Button = { + Root: ButtonRoot, + Icon: ButtonIcon, + Loading: ButtonLoading, +} \ No newline at end of file diff --git a/packages/ui-react/src/index.css b/packages/ui-react/src/index.css new file mode 100644 index 0000000..e69de29 diff --git a/packages/ui-react/src/index.ts b/packages/ui-react/src/index.ts new file mode 100644 index 0000000..e7ccd3d --- /dev/null +++ b/packages/ui-react/src/index.ts @@ -0,0 +1,29 @@ +import './index.css'; +export * from './assets/svg/BoldSvg'; +export * from './assets/svg/CheckIndicatorSvg'; +export * from './assets/svg/ChevronRightSvg'; +export * from './assets/svg/CutSvg'; +export * from './assets/svg/DownloadSvg'; +export * from './assets/svg/FileSvg'; +export * from './assets/svg/KeySvg'; +export * from './assets/svg/MeshSvg'; +export * from './assets/svg/MoonSvg'; +export * from './assets/svg/PasteSvg'; +export * from './assets/svg/Ruler'; +export * from './assets/svg/SearchSvg'; +export * from './assets/svg/SettingSvg'; +export * from './assets/svg/SpinnerSvg'; +export * from './assets/svg/SunSvg'; +export * from './assets/svg/UserSvg'; +export * from './assets/svg/VolumeHighSvg'; +export * from './assets/svg/VolumeLowSvg'; +export * from './assets/svg/VolumeMuteSvg'; +export * from './componnets/button/index'; +export * from './componnets/button/ButtonIcon'; +export * from './componnets/button/ButtonLoading'; +export * from './componnets/button/ButtonRoot'; +export * from './componnets/button/common/ButtonContext'; +export * from './utils/mergeProps'; +export * from './utils/Slot'; +export * from './utils/useDefaultedProps'; +export * from './utils/useSlotRegistry'; \ No newline at end of file diff --git a/packages/ui-react/src/types/env.d.ts b/packages/ui-react/src/types/env.d.ts new file mode 100644 index 0000000..31058d4 --- /dev/null +++ b/packages/ui-react/src/types/env.d.ts @@ -0,0 +1,4 @@ +declare module "*.css" { + const content: string; + export default content; +} diff --git a/packages/ui-web/src/utils/Slot.tsx b/packages/ui-react/src/utils/Slot.tsx similarity index 100% rename from packages/ui-web/src/utils/Slot.tsx rename to packages/ui-react/src/utils/Slot.tsx diff --git a/packages/ui-react/src/utils/mergeProps.ts b/packages/ui-react/src/utils/mergeProps.ts new file mode 100644 index 0000000..ddecc39 --- /dev/null +++ b/packages/ui-react/src/utils/mergeProps.ts @@ -0,0 +1,107 @@ +type AnyProps = Record; + +function defaultClassMergeFn(...classes: string[]): string { + return classes + .map((s) => s.trim()) + .filter(Boolean) + .join(" ") + .trim(); +} + +// 工具类型 +type UnionToIntersection = + (U extends any ? (k: U) => void : never) extends + (k: infer I) => void ? I : never; + +// 泛型入口 +export function mergeProps< + T extends AnyProps[] +>( + ...args: [...T] +): UnionToIntersection; + +export function mergeProps< + T extends AnyProps[] +>( + options: { classMergeFn: (...cls: string[]) => string }, + ...args: [...T] +): UnionToIntersection; + +export function mergeProps( + arg1: any, + ...rest: any[] +): any { + let options = { + classMergeFn: defaultClassMergeFn, + }; + + let propsList: AnyProps[]; + + if ( + arg1 && + typeof arg1 === "object" && + !Array.isArray(arg1) && + "classMergeFn" in arg1 && + typeof arg1.classMergeFn === "function" + ) { + options = arg1; + propsList = rest; + } else { + propsList = [arg1, ...rest]; + } + + const result: AnyProps = {}; + const eventHandlers = new Map(); + const refs: any[] = []; + + for (const props of propsList) { + if (!props) continue; + + if (props.className) { + result.className = options.classMergeFn( + result.className ?? "", + props.className, + ); + } + + if (props.style) { + result.style = { ...(result.style ?? {}), ...props.style }; + } + + if (props.ref) { + refs.push(props.ref); + } + + for (const key of Object.keys(props)) { + if (key.startsWith("on") && typeof props[key] === "function") { + if (!eventHandlers.has(key)) { + eventHandlers.set(key, []); + } + eventHandlers.get(key)!.push(props[key]); + } else if (key !== "ref" && key !== "className" && key !== "style") { + result[key] = props[key]; + } + } + } + + // refs + if (refs.length === 1) { + result.ref = refs[0]; + } else if (refs.length > 1) { + result.ref = (node: any) => { + refs.forEach((ref) => { + if (typeof ref === "function") ref(node); + else if (ref) ref.current = node; + }); + }; + } + + // events + eventHandlers.forEach((handlers, key) => { + result[key] = (...args: any[]) => { + handlers.forEach((fn) => fn(...args)); + }; + }); + + return result; +} \ No newline at end of file diff --git a/packages/ui-react/src/utils/useDefaultedProps.ts b/packages/ui-react/src/utils/useDefaultedProps.ts new file mode 100644 index 0000000..f8bdefa --- /dev/null +++ b/packages/ui-react/src/utils/useDefaultedProps.ts @@ -0,0 +1,10 @@ +import { useMemo } from "react"; + +export function useDefaultedProps< + P extends Record, + D extends Partial

, +>(props: P, defaults: D): P & D { + return useMemo(() => { + return { ...defaults, ...props }; + }, [props, defaults]); +} diff --git a/packages/ui-react/src/utils/useSlotRegistry.ts b/packages/ui-react/src/utils/useSlotRegistry.ts new file mode 100644 index 0000000..93871f7 --- /dev/null +++ b/packages/ui-react/src/utils/useSlotRegistry.ts @@ -0,0 +1,8 @@ +// useButtonSlots.ts +import { useState } from "react"; + +export function useSlotRegistry() { + const [slots, setSlots] = useState() + + return { slots, setSlots } +} \ No newline at end of file diff --git a/packages/ui-web/tsconfig.build.json b/packages/ui-react/tsconfig.build.json similarity index 95% rename from packages/ui-web/tsconfig.build.json rename to packages/ui-react/tsconfig.build.json index 4581f3b..e9fcb7d 100644 --- a/packages/ui-web/tsconfig.build.json +++ b/packages/ui-react/tsconfig.build.json @@ -10,6 +10,8 @@ }, "include": ["src"], "exclude": [ + "src/App.tsx", + "src/main.tsx", "node_modules", "dist", ".turbo/**/*", diff --git a/packages/ui-react/tsconfig.json b/packages/ui-react/tsconfig.json new file mode 100644 index 0000000..7b84f30 --- /dev/null +++ b/packages/ui-react/tsconfig.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "target": "es2025", + "lib": [ + "ES2025", + "DOM", + "DOM.Iterable" + ], + "module": "ESNext", + "types": [ + "node", + "vite/client" + ], + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + }, + "include": [ + "src", + "scripts", + "vite.config.ts", + ] +} \ No newline at end of file diff --git a/packages/ui-react/vite.config.ts b/packages/ui-react/vite.config.ts new file mode 100644 index 0000000..ea4923d --- /dev/null +++ b/packages/ui-react/vite.config.ts @@ -0,0 +1,39 @@ +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; +import path from "path"; + +export default defineConfig({ + plugins: [react()], + + build: { + cssMinify: false, + + lib: { + entry: path.resolve(import.meta.dirname, "src/index.ts"), + formats: ["es", "cjs"], + fileName: (format) => `index.${format}.js`, + }, + + rolldownOptions: { + external: ["react", "react-dom", "react/jsx-runtime"], + output: { + // 强制将生成的 CSS 命名为 index.css + assetFileNames: (assetInfo) => { + if (assetInfo.name && assetInfo.name.endsWith(".css")) { + return "index.css"; + } + return "[name].[hash][extname]"; + }, + globals: { + react: "React", + "react-dom": "ReactDOM", + }, + }, + }, + + emptyOutDir: true, + sourcemap: true, + cssCodeSplit: false, + outDir: "dist", + }, +}); diff --git a/packages/ui-web-tw/package.json b/packages/ui-web-tw/package.json index ea245bd..f2b7727 100644 --- a/packages/ui-web-tw/package.json +++ b/packages/ui-web-tw/package.json @@ -24,7 +24,6 @@ "scripts": { "gen-index": "ts-node scripts/gen-index.ts", "gen-dts": "tsc -p tsconfig.build.json", - "dev": "pnpm gen-index && vite build --watch", "build": "pnpm gen-index && vite build && pnpm gen-dts" }, "devDependencies": { diff --git a/packages/ui-web-tw/src/index.css b/packages/ui-web-tw/src/index.css index 6416da1..a461c50 100644 --- a/packages/ui-web-tw/src/index.css +++ b/packages/ui-web-tw/src/index.css @@ -1,16 +1 @@ -@import "tailwindcss"; -@import './component/accordion/Accordion.css'; -@import './component/avatar/Avatar.css'; -@import './component/checkbox/Checkbox.css'; -@import './component/tooltip/Tooltip.css'; -@import './styles/theme/global.css'; -@import './styles/utility/brand.css'; -@import './styles/utility/font.css'; -@import './styles/utility/gap.css'; -@import './styles/utility/height.css'; -@import './styles/utility/loading.css'; -@import './styles/utility/margin.css'; -@import './styles/utility/padding.css'; -@import './styles/utility/skin.css'; -@import './styles/utility/variant.css'; -@import './styles/utility/width.css'; \ No newline at end of file +@import "tailwindcss"; \ No newline at end of file diff --git a/packages/ui-web-tw/src/index.ts b/packages/ui-web-tw/src/index.ts index f08a879..7505b6f 100644 --- a/packages/ui-web-tw/src/index.ts +++ b/packages/ui-web-tw/src/index.ts @@ -1,44 +1 @@ -import './index.css'; - -export * from './assets/svg/BoldSvg'; -export * from './assets/svg/CheckIndicatorSvg'; -export * from './assets/svg/ChevronRightSvg'; -export * from './assets/svg/CutSvg'; -export * from './assets/svg/DownloadSvg'; -export * from './assets/svg/FileSvg'; -export * from './assets/svg/KeySvg'; -export * from './assets/svg/MeshSvg'; -export * from './assets/svg/MoonSvg'; -export * from './assets/svg/PasteSvg'; -export * from './assets/svg/Ruler'; -export * from './assets/svg/SearchSvg'; -export * from './assets/svg/SettingSvg'; -export * from './assets/svg/SpinnerSvg'; -export * from './assets/svg/SunSvg'; -export * from './assets/svg/UserSvg'; -export * from './assets/svg/VolumeHighSvg'; -export * from './assets/svg/VolumeLowSvg'; -export * from './assets/svg/VolumeMuteSvg'; -export * from './common/Box'; -export * from './common/CommonProps'; -export * from './common/Slot'; -export * from './component/accordion/Accordion'; -export * from './component/accordion/AccordionItem'; -export * from './component/accordion/AccordionPanel'; -export * from './component/accordion/AccordionTrigger'; -export * from './component/avatar/Avatar'; -export * from './component/button/Button'; -export * from './component/checkbox/Checkbox'; -export * from './component/icon/Icon'; -export * from './component/theme/Theme'; -export * from './component/theme/ThemeContext'; -export * from './component/theme/useTheme'; -export * from './component/tooltip/Tooltip'; -export * from './component/tooltip/TooltipPopup'; -export * from './component/tooltip/TooltipTrigger'; -export * from './component/username-field/UsernameField'; -export * from './styles/recipe/IinlineSize.recipe'; -export * from './styles/recipe/ItemSize.recipe'; -export * from './styles/recipe/brand.recipe'; -export * from './styles/recipe/variant.recipe'; -export * from '../../css/utils/clspm'; \ No newline at end of file +import './index.css' \ No newline at end of file diff --git a/packages/ui-web/playground/App.tsx b/packages/ui-web/playground/App.tsx deleted file mode 100644 index 03ffc26..0000000 --- a/packages/ui-web/playground/App.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import { useState } from "react"; - -function App() { - return

; -} - -export default App; diff --git a/packages/ui-web/playground/index.html b/packages/ui-web/playground/index.html deleted file mode 100644 index a3ed15d..0000000 --- a/packages/ui-web/playground/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - vite-project - - -
- - - diff --git a/packages/ui-web/playground/main.tsx b/packages/ui-web/playground/main.tsx deleted file mode 100644 index 4aff025..0000000 --- a/packages/ui-web/playground/main.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import { StrictMode } from 'react' -import { createRoot } from 'react-dom/client' -import App from './App.tsx' - -createRoot(document.getElementById('root')!).render( - - - , -) diff --git a/packages/ui-web/scripts/gen-index.ts b/packages/ui-web/scripts/gen-index.ts deleted file mode 100644 index 64433db..0000000 --- a/packages/ui-web/scripts/gen-index.ts +++ /dev/null @@ -1,147 +0,0 @@ -import fs from "fs"; -import path from "path"; -// 1. 引入 tinyglobby -import { globSync } from "tinyglobby"; - -interface Config { - outputDir: string; - outputFilenameWithExt: string; - scanDirs: string[]; - importPrefix: string; - predefineStatements: string[]; - includeExtensions: string[]; - excludeDirs: string[]; - excludeFileExtensions: string[]; - excludePatterns: RegExp[]; -} - -const cssConfig: Config = { - outputDir: "src", - outputFilenameWithExt: "index.css", - scanDirs: ["src"], - importPrefix: "@import", - predefineStatements: [], - includeExtensions: [".css"], - excludeDirs: ["__tests__", "tests", "story", "stories", "types"], - excludeFileExtensions: [], - excludePatterns: [/^index\.(css)$/, /\.(test|spec)\./, /\.(story|stories)\./], -}; - -const tsConfig: Config = { - outputDir: "src", - outputFilenameWithExt: "index.ts", - scanDirs: ["src"], - importPrefix: "export * from", - predefineStatements: ["import './index.css'"], - includeExtensions: [".ts", "tsx", "js", "jsx"], - excludeDirs: ["__tests__", "tests", "story", "stories", "types"], - excludeFileExtensions: [".d.ts"], - excludePatterns: [ - /^index\.(ts|tsx|js|jsx)$/, - /\.(test|spec)\./, - /\.(story|stories)\./, - ], -}; - -const normalizePath = (p: string) => p.replace(/\\/g, "/"); - -const isExcludeDir = (filePath: string, excludeDirs: string[]) => { - const normalized = normalizePath(filePath); - return excludeDirs.some((dir) => normalized.includes(`/${dir}/`)); -}; - -const isExcludeFileExtensions = ( - filePath: string, - excludeFileExtensions: string[], -) => excludeFileExtensions.some((ext) => filePath.endsWith(ext)); - -const isExcludePattern = (fileName: string, excludePatterns: RegExp[]) => - excludePatterns.some((pattern) => pattern.test(fileName)); - -// ---------------------------------------- -function isValidFile(filePath: string, config: Config): boolean { - const fileName = filePath.split(/[\\/]/).pop()!; - - if (isExcludeDir(filePath, config.excludeDirs)) return false; - if (isExcludeFileExtensions(filePath, config.excludeFileExtensions)) - return false; - if (isExcludePattern(fileName, config.excludePatterns)) return false; - - const ext = path.extname(filePath); - return config.includeExtensions.includes(ext); -} -// ----------------------------------------- -function generateIndexFile(config: Config) { - const currentPath = process.cwd(); - const outputPath = path.resolve(currentPath, config.outputDir); - let exportStatements: string[] = []; - - // ------ scanDirs forEach start ------------------------ - config.scanDirs.forEach((dir) => { - // 2. 路径模式保持不变,tinyglobby 能够正确处理 - const scanPattern = path.resolve(currentPath, dir, "**", "*.*"); - - const allFilePath = globSync(scanPattern, { - absolute: true, - // 3. 移除了 windowsPathsNoEscape,tinyglobby 默认处理路径更智能 - }); - - const validFiles = allFilePath.filter((filePath) => { - return isValidFile(filePath, config); - }); - - if (validFiles.length === 0) { - console.log( - `⚠️ 未找到符合条件的文件,跳过生成 ${config.outputFilenameWithExt}`, - ); - return; - } - - validFiles.sort(); - - validFiles.forEach((file) => { - const relativePath = path.relative(outputPath, file); - const importPath = `./${relativePath.replace(/\\/g, "/")}`; - exportStatements.push(`${config.importPrefix} '${importPath}';`); - }); - }); - - // --------- scanDirs forEach end ---------------- - - const indexFileContent = ` -${config.predefineStatements.join("\n")} -${exportStatements.join("\n")} -`.trim(); - - const indexFilePath = path.resolve( - currentPath, - config.outputDir, - config.outputFilenameWithExt, - ); - - // ✅ 内容比对,避免重复写入 - if (fs.existsSync(indexFilePath)) { - const old = fs.readFileSync(indexFilePath, "utf8"); - if (old === indexFileContent) { - console.log( - `✅ ${config.outputFilenameWithExt} 内容无变化,无需重新生成`, - ); - return; - } - } - - fs.writeFileSync(indexFilePath, indexFileContent, "utf8"); - console.log(`✅ 成功生成 ${config.outputFilenameWithExt}: ${indexFilePath}`); -} - -// -------------------------------------------------- - -try { - console.log(`🚀 [gen-index] 开始扫描`); - generateIndexFile(cssConfig); - generateIndexFile(tsConfig); -} catch (err) { - const msg = err instanceof Error ? err.message : String(err); - console.error(`❌ [gen-index] 执行失败: ${msg}`); - process.exit(1); -} diff --git a/packages/ui-web/src/componnets/button/ButtonIcon.tsx b/packages/ui-web/src/componnets/button/ButtonIcon.tsx deleted file mode 100644 index 93df617..0000000 --- a/packages/ui-web/src/componnets/button/ButtonIcon.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import { Slot } from "../../utils/Slot"; - -export const ButtonIcon = (props: any) => { - const { children, ...rest } = props; - return {children}; -}; -ButtonIcon.displayName = "ButtonIcon"; diff --git a/packages/ui-web/src/componnets/button/ButtonLoading.tsx b/packages/ui-web/src/componnets/button/ButtonLoading.tsx deleted file mode 100644 index af1932a..0000000 --- a/packages/ui-web/src/componnets/button/ButtonLoading.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import { Slot } from "../../utils/Slot"; - -export const ButtonLoading = (props: any) => { - const { children, ...rest } = props; - return {children}; -}; -ButtonLoading.displayName = "ButtonLoading"; diff --git a/packages/ui-web/src/componnets/button/ButtonRoot.tsx b/packages/ui-web/src/componnets/button/ButtonRoot.tsx deleted file mode 100644 index dbd628d..0000000 --- a/packages/ui-web/src/componnets/button/ButtonRoot.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { type ComponentPropsWithoutRef, forwardRef } from "react"; -import { useButtonSlot } from "./useButtonSlots"; -import { useStateProps } from "../../utils/useStateProps"; - -export type ButtonRootOwnProps = { - iconOnly?: boolean; - hideIcon?: boolean; - loading?: boolean; -}; - -export type ButtonRootStateProps = { - disabled?: boolean; -}; - -export type ButtonRootPrimitiveProps = Omit< - ComponentPropsWithoutRef<"button">, - keyof ButtonRootStateProps ->; - -export type ButtonRootProps = ButtonRootOwnProps & - ButtonRootStateProps & - ButtonRootPrimitiveProps; - -export const ButtonRoot = forwardRef( - (props, ref) => { - const { - children, - loading, - iconOnly, - hideIcon, - disabled = false, - ...rest - } = props; - const stateProps = useStateProps({ disabled: disabled }); - const filteredChildren = useButtonSlot(children, props); - - return ( - - ); - }, -); -ButtonRoot.displayName = "ButtonRoot"; diff --git a/packages/ui-web/src/componnets/button/useButtonSlots.ts b/packages/ui-web/src/componnets/button/useButtonSlots.ts deleted file mode 100644 index 64affd3..0000000 --- a/packages/ui-web/src/componnets/button/useButtonSlots.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { - type ReactNode, - Children, - isValidElement, - cloneElement, - type ReactElement, -} from "react"; - -import { ButtonIcon } from "./ButtonIcon"; -import { ButtonLoading } from "./ButtonLoading"; - -export function useButtonSlot( - children: ReactNode, - props: { hideIcon?: boolean; loading?: boolean }, -) { - const filterNodes = (nodes: ReactNode): ReactNode => { - // 存储挑出 Slot 后的纯净 children - const filtered: ReactNode[] = []; - - Children.forEach(nodes, (child: any) => { - if (!isValidElement(child)) { - filtered.push(child); - return; - } - - // loading:只保留 Loading - if (props.loading) { - if (child.type === ButtonLoading) { - filtered.push(child); - } - return; - } - - // 隐藏图标:移除 ButtonIcon - if (props.hideIcon && child.type === ButtonIcon) { - return; - } - - // 普通节点 - const element = child as ReactElement<{ children?: ReactNode }>; - - filtered.push( - cloneElement(element, { - children: filterNodes(element.props.children), - }), - ); - }); - - return filtered; - }; - - return filterNodes(children); -} diff --git a/packages/ui-web/src/styles/recipe/inlineSizeRecipe.ts b/packages/ui-web/src/styles/recipe/inlineSizeRecipe.ts deleted file mode 100644 index 3e68858..0000000 --- a/packages/ui-web/src/styles/recipe/inlineSizeRecipe.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { cvr } from "../utils/cvr"; - -export const inlineSizeRecipe = cvr({ - base: "flex flex-nowrap justify-center items-center", - variants: { - size: { - xs: "text-xs h-inline-xs gap-xs", - sm: "text-sm h-inline-sm gap-sm", - md: "text-md h-inline-md gap-md", - lg: "text-lg h-inline-lg gap-lg", - xl: "text-xl h-inline-xl gap-xl", - "2xl": "text-2xl h-inline-2xl gap-2xl", - }, - }, -}); diff --git a/packages/ui-web/src/styles/recipe/itemSizeRecipe.ts b/packages/ui-web/src/styles/recipe/itemSizeRecipe.ts deleted file mode 100644 index 68ccf6b..0000000 --- a/packages/ui-web/src/styles/recipe/itemSizeRecipe.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { cvr } from "../utils/cvr"; - -export const itemSizeRecipe = cvr({ - base: "relative select-none display-flex flex-nowrap justify-center items-center", - variants: { - size: { - xs: "text-xs h-item-xs gap-xs px-xs", - sm: "text-sm h-item-sm gap-sm px-sm", - md: "text-md h-item-md gap-md px-md", - lg: "text-lg h-item-lg gap-lg px-lg", - xl: "text-xl h-item-xl gap-xl px-xl", - "2xl": "text-2xl h-item-2xl gap-2xl px-2xl", - }, - shape: { - square: "rounded-none", - rounded: "", - circle: "rounded-full", - }, - iconOnly: { - true: "px-none", - false: "", - }, - }, - compoundVariants: [ - { iconOnly: false, size: "xs", class: "px-xs" }, - { iconOnly: false, size: "sm", class: "px-sm" }, - { iconOnly: false, size: "md", class: "px-md" }, - { iconOnly: false, size: "lg", class: "px-lg" }, - { iconOnly: false, size: "xl", class: "px-xl" }, - { iconOnly: false, size: "2xl", class: "px-2xl" }, - { shape: "rounded", size: "xs", class: "rounded-sm" }, - // ================================================== - { - shape: "rounded", - size: "sm", - class: "rounded-md", - }, - { - shape: "rounded", - size: "md", - class: "rounded-lg", - }, - { - shape: "rounded", - size: "lg", - class: "rounded-xl", - }, - { - shape: "rounded", - size: "xl", - class: "rounded-2xl", - }, - { - shape: "rounded", - size: "2xl", - class: "rounded-3xl", - }, - // ================================================== - { - iconOnly: true, - size: "xs", - class: "w-item-xs", - }, - { - iconOnly: true, - size: "sm", - class: "w-item-sm", - }, - { - iconOnly: true, - size: "md", - class: "w-item-md", - }, - { - iconOnly: true, - size: "lg", - class: "w-item-lg", - }, - { - iconOnly: true, - size: "xl", - class: "w-item-xl", - }, - { - iconOnly: true, - size: "2xl", - class: "w-item-2xl", - }, - ], -}); diff --git a/packages/ui-web/src/styles/utility/size-insensitive/position.css b/packages/ui-web/src/styles/utility/size-insensitive/position.css deleted file mode 100644 index 70d3bb9..0000000 --- a/packages/ui-web/src/styles/utility/size-insensitive/position.css +++ /dev/null @@ -1,21 +0,0 @@ -@layer utility { - .position--static { - position: static; - } - - .position--fixed { - position: fixed; - } - - .position--absolute { - position: absolute; - } - - .position--relative { - position: relative; - } - - .position--sticky { - position: sticky; - } -} diff --git a/packages/ui-web/src/styles/utility/size-sensitive/margin-block.css b/packages/ui-web/src/styles/utility/size-sensitive/margin-block.css deleted file mode 100644 index 6d949be..0000000 --- a/packages/ui-web/src/styles/utility/size-sensitive/margin-block.css +++ /dev/null @@ -1,67 +0,0 @@ -@layer utility { - .my--e--none { - margin-block-end: 0; - } - .my--e--xs { - margin-block-end: var(--margin-xs); - } - .my--e--sm { - margin-block-end: var(--margin-sm); - } - .my--e--md { - margin-block-end: var(--margin-md); - } - .my--e--lg { - margin-block-end: var(--margin-lg); - } - .my--e--xl { - margin-block-end: var(--margin-xl); - } - .my--e--2xl { - margin-block-end: var(--margin-2xl); - } - /* ------------------ */ - .my--s--none { - margin-block-start: 0; - } - .my--s--xs { - margin-block-start: var(--margin-xs); - } - .my--s--sm { - margin-block-start: var(--margin-sm); - } - .my--s--md { - margin-block-start: var(--margin-md); - } - .my--s--lg { - margin-block-start: var(--margin-lg); - } - .my--s--xl { - margin-block-start: var(--margin-xl); - } - .my--s--2xl { - margin-block-start: var(--margin-2xl); - } - /* --------------------- */ - .my--none { - margin-block: 0; - } - .my--xs { - margin-block: var(--margin-xs); - } - .my--sm { - margin-block: var(--margin-sm); - } - .my--md { - margin-block: var(--margin-md); - } - .my--lg { - margin-block: var(--margin-lg); - } - .my--xl { - margin-block: var(--margin-xl); - } - .my--2xl { - margin-block: var(--margin-2xl); - } -} diff --git a/packages/ui-web/src/styles/utility/size-sensitive/margin-inline.css b/packages/ui-web/src/styles/utility/size-sensitive/margin-inline.css deleted file mode 100644 index bc215b0..0000000 --- a/packages/ui-web/src/styles/utility/size-sensitive/margin-inline.css +++ /dev/null @@ -1,67 +0,0 @@ -@layer utility { - .mx--e--none { - margin-inline-end: 0; - } - .mx--e--xs { - margin-inline-end: var(--margin-xs); - } - .mx--e--sm { - margin-inline-end: var(--margin-sm); - } - .mx--e--md { - margin-inline-end: var(--margin-md); - } - .mx--e--lg { - margin-inline-end: var(--margin-lg); - } - .mx--e--xl { - margin-inline-end: var(--margin-xl); - } - .mx--e--2xl { - margin-inline-end: var(--margin-2xl); - } - /* ------------------ */ - .mx--s--none { - margin-inline-start: 0; - } - .mx--s--xs { - margin-inline-start: var(--margin-xs); - } - .mx--s--sm { - margin-inline-start: var(--margin-sm); - } - .mx--s--md { - margin-inline-start: var(--margin-md); - } - .mx--s--lg { - margin-inline-start: var(--margin-lg); - } - .mx--s--xl { - margin-inline-start: var(--margin-xl); - } - .mx--s--2xl { - margin-inline-start: var(--margin-2xl); - } - /* --------------------- */ - .mx--none { - margin-inline: 0; - } - .mx--xs { - margin-inline: var(--margin-xs); - } - .mx--sm { - margin-inline: var(--margin-sm); - } - .mx--md { - margin-inline: var(--margin-md); - } - .mx--lg { - margin-inline: var(--margin-lg); - } - .mx--xl { - margin-inline: var(--margin-xl); - } - .mx--2xl { - margin-inline: var(--margin-2xl); - } -} diff --git a/packages/ui-web/src/styles/utility/size-sensitive/padding-long.css b/packages/ui-web/src/styles/utility/size-sensitive/padding-long.css deleted file mode 100644 index dea45ad..0000000 --- a/packages/ui-web/src/styles/utility/size-sensitive/padding-long.css +++ /dev/null @@ -1,24 +0,0 @@ -@layer utility { - .px--long--xs { - /* 6px */ - padding-inline: var(--padding-long-xs); - } - .px--long--sm { - /* 8px */ - padding-inline: var(--padding-long-sm); - } - .px--long--md { - /* 10px */ - padding-inline: var(--padding-long-md); - } - .px--long--lg { - /* 16px */ - padding-inline: var(--padding-long-lg); - } - .px--long--xl { - padding-inline: var(--padding-long-xl); - } - .px--long--2xl { - padding-inline: var(--padding-long-2xl); - } -} diff --git a/packages/ui-web/src/styles/utils/cpm.ts b/packages/ui-web/src/styles/utils/cpm.ts deleted file mode 100644 index 83ee851..0000000 --- a/packages/ui-web/src/styles/utils/cpm.ts +++ /dev/null @@ -1,49 +0,0 @@ -export function cpm(...classes: (string[] | string)[]) { - const map = new Map(); - - function dealClass(cls: string) { - // 提取多段 prefix,以“--”为分隔符 - const fragments = cls.split("--"); - // 前缀后面剩下的部分 - const suffix = fragments.pop() ?? ""; - // ['1', '1--2', '1--2--3'] - const stepwisePrefixList = fragments.map((_, index, array) => { - if (index === 0) return array[0]; - // 截取子数组(注意不是子字符串) - return array.slice(0, index + 1).join("--"); - }); - - // 覆盖,短前缀 - stepwisePrefixList.forEach((key) => { - map.delete(key); - }); - // 覆盖延长前缀,overflow--x 会覆盖 overflow--x--l - // 不覆盖分支前缀,overflow--x 不会覆盖 overfl--y - for (const key of map.keys()) { - if (key.includes(stepwisePrefixList[-1])) { - // 包含 overflow--x 的全部删除 - map.delete(key); - } - } - // 把本次值加进去 - map.set(stepwisePrefixList[-1], suffix); - } - - classes.forEach((item) => { - // 如果是 string 直接处理 - if (typeof item === "string") { - dealClass(item); - } else { - // 否则肯定是 string[] - item.forEach((i) => { - dealClass(i); - }); - } - }); - - const resultString = [...map] - .map(([key, value]) => `${key}--${value}`) - .join(" "); - - return resultString; -} diff --git a/packages/ui-web/src/utils/mergeProps.ts b/packages/ui-web/src/utils/mergeProps.ts deleted file mode 100644 index 748f1fa..0000000 --- a/packages/ui-web/src/utils/mergeProps.ts +++ /dev/null @@ -1,103 +0,0 @@ -function defaultClassMergeFn(...classes: string[]): string { - return classes - .map((str) => str.trim()) - .filter(Boolean) - .join(" ") - .trim(); -} - -// 重载1 -export function mergeProps( - ...propsN: Record[] -): Record; - -// 重载2 -export function mergeProps( - options: { classMergeFn: Function }, - ...propsN: Record[] -): Record; - -// 实现 -export function mergeProps( - arg1: { classMergeFn: Function } | Record, - ...rest: Record[] -) { - let options: { classMergeFn: Function } = { - classMergeFn: defaultClassMergeFn, - }; - let propsN: Record[]; - - if ( - arg1 && - typeof arg1 === "object" && - !Array.isArray(arg1) && - "classMergeFn" in arg1 && - typeof (arg1 as any).classMergeFn === "function" - ) { - options = arg1 as { classMergeFn: Function }; - propsN = rest; - } else { - propsN = [arg1 as Record, ...rest]; - } - // -------------------------------- - const result: any = {}; - const eventHandlerMap = new Map(); - const refs: any[] = []; - - propsN.forEach((props) => { - if (!props) return; - - if (props.className) { - result.className = options.classMergeFn( - result.className || "", - props.className, - ); - } - - if (props.style) { - result.style = { ...(result.style || {}), ...props.style }; - } - - if (props.ref) { - refs.push(props.ref); - } - - Object.keys(props).forEach((key) => { - if (key.startsWith("on") && typeof props[key] === "function") { - if (!eventHandlerMap.has(key)) { - eventHandlerMap.set(key, []); - } - eventHandlerMap.get(key)!.push(props[key]); - } else if (key !== "ref" && key !== "className" && key !== "style") { - // 其他普通属性,后面的覆盖前面的 - result[key] = props[key]; - } - }); - }); - - // 合并 Refs - if (refs.length > 0) { - if (refs.length === 1) { - result.ref = refs[0]; - } else { - result.ref = (node: any) => { - refs.forEach((ref) => { - if (typeof ref === "function") { - ref(node); - } else if (ref) { - ref.current = node; - } - }); - }; - } - } - - // 合并事件处理器 - eventHandlerMap.forEach((handlers, key) => { - result[key] = (...args: any[]) => { - handlers.forEach((handler) => handler(...args)); - }; - }); - - return result; -} diff --git a/packages/ui-web/src/utils/useStateProps.ts b/packages/ui-web/src/utils/useStateProps.ts deleted file mode 100644 index c9e1485..0000000 --- a/packages/ui-web/src/utils/useStateProps.ts +++ /dev/null @@ -1,12 +0,0 @@ -export function useStateProps(props: Record) { - const state: Record = {}; - - Object.entries(props).forEach(([key, value]) => { - const stateKey = `data-${key}:`; - const stateValue = `"${value}"`; - - state[stateKey] = stateValue; - }); - - return state; -} diff --git a/packages/ui-web/vite.config.ts b/packages/ui-web/vite.config.ts deleted file mode 100644 index 4418ded..0000000 --- a/packages/ui-web/vite.config.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { defineConfig } from "vite"; -import react from "@vitejs/plugin-react"; -import path from "path"; - -export default defineConfig(({ command }) => { - if (command === "serve") { - return { - plugins: [react()], - root: "playground", - }; - } - - return { - plugins: [react()], - - build: { - cssMinify: false, - - lib: { - entry: path.resolve(import.meta.dirname, "src/index.ts"), - formats: ["es", "cjs"], - fileName: (format) => `index.${format}.js`, - }, - - rolldownOptions: { - external: ["react", "react-dom", "react/jsx-runtime"], - output: { - globals: { - react: "React", - "react-dom": "ReactDOM", - }, - }, - }, - - emptyOutDir: true, - sourcemap: true, - cssCodeSplit: false, - outDir: "dist", - }, - }; -}); diff --git a/apps/vite-project/.gitignore b/playground/ui-gallery/.gitignore similarity index 100% rename from apps/vite-project/.gitignore rename to playground/ui-gallery/.gitignore diff --git a/apps/vite-project/README.md b/playground/ui-gallery/README.md similarity index 100% rename from apps/vite-project/README.md rename to playground/ui-gallery/README.md diff --git a/apps/vite-project/eslint.config.js b/playground/ui-gallery/eslint.config.js similarity index 100% rename from apps/vite-project/eslint.config.js rename to playground/ui-gallery/eslint.config.js diff --git a/apps/vite-project/index.html b/playground/ui-gallery/index.html similarity index 91% rename from apps/vite-project/index.html rename to playground/ui-gallery/index.html index 1a2df5a..a5fcab6 100644 --- a/apps/vite-project/index.html +++ b/playground/ui-gallery/index.html @@ -4,7 +4,7 @@ - vite-project + ui-gallery
diff --git a/apps/vite-project/package.json b/playground/ui-gallery/package.json similarity index 66% rename from apps/vite-project/package.json rename to playground/ui-gallery/package.json index d525600..be41462 100644 --- a/apps/vite-project/package.json +++ b/playground/ui-gallery/package.json @@ -1,5 +1,5 @@ { - "name": "vite-project", + "name": "ui-gallery", "private": true, "version": "0.0.0", "type": "module", @@ -10,21 +10,22 @@ "preview": "vite preview" }, "dependencies": { - "react": "^19.2.5", - "react-dom": "^19.2.5" + "react": "^19.2.6", + "react-dom": "^19.2.6", + "@dg/ui-react": "workspace:*" }, "devDependencies": { "@eslint/js": "^10.0.1", - "@types/node": "^24.12.2", + "@types/node": "^24.12.3", "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^6.0.1", - "eslint": "^10.2.1", + "eslint": "^10.3.0", "eslint-plugin-react-hooks": "^7.1.1", "eslint-plugin-react-refresh": "^0.5.2", - "globals": "^17.5.0", + "globals": "^17.6.0", "typescript": "~6.0.2", - "typescript-eslint": "^8.58.2", - "vite": "^8.0.10" + "typescript-eslint": "^8.59.2", + "vite": "^8.0.12" } } diff --git a/playground/ui-gallery/public/favicon.svg b/playground/ui-gallery/public/favicon.svg new file mode 100644 index 0000000..6893eb1 --- /dev/null +++ b/playground/ui-gallery/public/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/playground/ui-gallery/public/icons.svg b/playground/ui-gallery/public/icons.svg new file mode 100644 index 0000000..e952219 --- /dev/null +++ b/playground/ui-gallery/public/icons.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/playground/ui-gallery/src/App.tsx b/playground/ui-gallery/src/App.tsx new file mode 100644 index 0000000..8b939ca --- /dev/null +++ b/playground/ui-gallery/src/App.tsx @@ -0,0 +1,12 @@ +import { ButtonGallery } from "./gallery/ButtonGallery"; +import "@dg/ui-react/index.css"; + +function App() { + return ( +
+ +
+ ); +} + +export default App; diff --git a/playground/ui-gallery/src/common/IW.tsx b/playground/ui-gallery/src/common/IW.tsx new file mode 100644 index 0000000..a8b0ac5 --- /dev/null +++ b/playground/ui-gallery/src/common/IW.tsx @@ -0,0 +1,15 @@ +import type { ReactNode } from "react"; + +export const IW = ({ children }: { children: ReactNode }) => { + return ( +
+ {children} +
+ ); +}; diff --git a/apps/vite-project/src/common/OuterWrapper.tsx b/playground/ui-gallery/src/common/OW.tsx similarity index 58% rename from apps/vite-project/src/common/OuterWrapper.tsx rename to playground/ui-gallery/src/common/OW.tsx index a17491a..1cc77b5 100644 --- a/apps/vite-project/src/common/OuterWrapper.tsx +++ b/playground/ui-gallery/src/common/OW.tsx @@ -1,14 +1,13 @@ -import { type ReactNode } from "react"; +import type { ReactNode } from "react"; -export const OuterWrapper = ({ children }: { children: ReactNode }) => { +export const OW = ({ children }: { children: ReactNode }) => { return (
{children} diff --git a/playground/ui-gallery/src/gallery/ButtonGallery.tsx b/playground/ui-gallery/src/gallery/ButtonGallery.tsx new file mode 100644 index 0000000..14fe531 --- /dev/null +++ b/playground/ui-gallery/src/gallery/ButtonGallery.tsx @@ -0,0 +1,18 @@ +import { Button, DownloadSvg } from "@dg/ui-react"; +import { IW } from "../common/IW"; +import { OW } from "../common/OW"; + +export const ButtonGallery = () => { + return ( + + + + + + + Default + + + + ); +}; diff --git a/playground/ui-gallery/src/main.tsx b/playground/ui-gallery/src/main.tsx new file mode 100644 index 0000000..5d4a2be --- /dev/null +++ b/playground/ui-gallery/src/main.tsx @@ -0,0 +1,9 @@ +import { StrictMode } from "react"; +import { createRoot } from "react-dom/client"; +import App from "./App.tsx"; + +createRoot(document.getElementById("root")!).render( + + + , +); diff --git a/apps/vite-project/tsconfig.app.json b/playground/ui-gallery/tsconfig.app.json similarity index 100% rename from apps/vite-project/tsconfig.app.json rename to playground/ui-gallery/tsconfig.app.json diff --git a/apps/vite-project/tsconfig.json b/playground/ui-gallery/tsconfig.json similarity index 100% rename from apps/vite-project/tsconfig.json rename to playground/ui-gallery/tsconfig.json diff --git a/apps/vite-project/tsconfig.node.json b/playground/ui-gallery/tsconfig.node.json similarity index 100% rename from apps/vite-project/tsconfig.node.json rename to playground/ui-gallery/tsconfig.node.json diff --git a/playground/ui-gallery/vite.config.ts b/playground/ui-gallery/vite.config.ts new file mode 100644 index 0000000..8b0f57b --- /dev/null +++ b/playground/ui-gallery/vite.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [react()], +}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2c29e74..235cabe 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,74 +12,6 @@ importers: specifier: ^2.8.0 version: 2.9.12 - apps/vite-project: - dependencies: - react: - specifier: ^19.2.5 - version: 19.2.6 - react-dom: - specifier: ^19.2.5 - version: 19.2.6(react@19.2.6) - devDependencies: - '@eslint/js': - specifier: ^10.0.1 - version: 10.0.1(eslint@10.3.0(jiti@2.7.0)) - '@types/node': - specifier: ^24.12.2 - version: 24.12.3 - '@types/react': - specifier: ^19.2.14 - version: 19.2.14 - '@types/react-dom': - specifier: ^19.2.3 - version: 19.2.3(@types/react@19.2.14) - '@vitejs/plugin-react': - specifier: ^6.0.1 - version: 6.0.1(vite@8.0.12(@types/node@24.12.3)(jiti@2.7.0)) - eslint: - specifier: ^10.2.1 - version: 10.3.0(jiti@2.7.0) - eslint-plugin-react-hooks: - specifier: ^7.1.1 - version: 7.1.1(eslint@10.3.0(jiti@2.7.0)) - eslint-plugin-react-refresh: - specifier: ^0.5.2 - version: 0.5.2(eslint@10.3.0(jiti@2.7.0)) - globals: - specifier: ^17.5.0 - version: 17.6.0 - typescript: - specifier: ~6.0.2 - version: 6.0.3 - typescript-eslint: - specifier: ^8.58.2 - version: 8.59.3(eslint@10.3.0(jiti@2.7.0))(typescript@6.0.3) - vite: - specifier: ^8.0.10 - version: 8.0.12(@types/node@24.12.3)(jiti@2.7.0) - - packages/bookmark-sync: - dependencies: - typescript: - specifier: ^6.0.3 - version: 6.0.3 - devDependencies: - '@types/chrome': - specifier: ^0.1.40 - version: 0.1.42 - '@types/firefox-webext-browser': - specifier: ^143.0.0 - version: 143.0.0 - '@types/node': - specifier: ^25.6.0 - version: 25.6.2 - '@types/webextension-polyfill': - specifier: ^0.12.5 - version: 0.12.5 - webextension-polyfill: - specifier: ^0.12.0 - version: 0.12.0 - packages/css: dependencies: react: @@ -114,8 +46,11 @@ importers: specifier: ^8.0.10 version: 8.0.12(@types/node@25.6.2)(jiti@2.7.0) - packages/ui-web-headless: + packages/ui-react: dependencies: + '@dg/css': + specifier: workspace:* + version: link:../css react: specifier: ^19 version: 19.2.6 @@ -197,18 +132,24 @@ importers: specifier: ^8.0.12 version: 8.0.12(@types/node@25.6.2)(jiti@2.7.0) - templates/vite-react-lib: + playground/ui-gallery: dependencies: + '@dg/ui-react': + specifier: workspace:* + version: link:../../packages/ui-react react: - specifier: ^19 + specifier: ^19.2.6 version: 19.2.6 react-dom: - specifier: ^19 + specifier: ^19.2.6 version: 19.2.6(react@19.2.6) devDependencies: + '@eslint/js': + specifier: ^10.0.1 + version: 10.0.1(eslint@10.4.0(jiti@2.7.0)) '@types/node': - specifier: ^25.6.0 - version: 25.6.2 + specifier: ^24.12.3 + version: 24.12.4 '@types/react': specifier: ^19.2.14 version: 19.2.14 @@ -217,19 +158,28 @@ importers: version: 19.2.3(@types/react@19.2.14) '@vitejs/plugin-react': specifier: ^6.0.1 - version: 6.0.1(vite@8.0.12(@types/node@25.6.2)(jiti@2.7.0)) - tinyglobby: - specifier: ^0.2.16 - version: 0.2.16 - ts-node: - specifier: ^10.9.2 - version: 10.9.2(@types/node@25.6.2)(typescript@6.0.3) + version: 6.0.1(vite@8.0.12(@types/node@24.12.4)(jiti@2.7.0)) + eslint: + specifier: ^10.3.0 + version: 10.4.0(jiti@2.7.0) + eslint-plugin-react-hooks: + specifier: ^7.1.1 + version: 7.1.1(eslint@10.4.0(jiti@2.7.0)) + eslint-plugin-react-refresh: + specifier: ^0.5.2 + version: 0.5.2(eslint@10.4.0(jiti@2.7.0)) + globals: + specifier: ^17.6.0 + version: 17.6.0 typescript: - specifier: ^6.0.3 + specifier: ~6.0.2 version: 6.0.3 + typescript-eslint: + specifier: ^8.59.2 + version: 8.59.4(eslint@10.4.0(jiti@2.7.0))(typescript@6.0.3) vite: - specifier: ^8.0.10 - version: 8.0.12(@types/node@25.6.2)(jiti@2.7.0) + specifier: ^8.0.12 + version: 8.0.12(@types/node@24.12.4)(jiti@2.7.0) packages: @@ -253,8 +203,8 @@ packages: resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} engines: {node: '>=6.9.0'} - '@babel/helper-globals@7.28.0': - resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + '@babel/helper-globals@7.29.7': + resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==} engines: {node: '>=6.9.0'} '@babel/helper-module-imports@7.28.6': @@ -267,16 +217,16 @@ packages: peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-string-parser@7.27.1': - resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.28.5': - resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-option@7.27.1': - resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + '@babel/helper-validator-option@7.29.7': + resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==} engines: {node: '>=6.9.0'} '@babel/helpers@7.29.2': @@ -358,8 +308,8 @@ packages: resolution: {integrity: sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} - '@eslint/config-helpers@0.5.5': - resolution: {integrity: sha512-eIJYKTCECbP/nsKaaruF6LW967mtbQbsw4JTtSVkUQc9MneSkbrgPJAbKl9nWr0ZeowV8BfsarBmPpBzGelA2w==} + '@eslint/config-helpers@0.6.0': + resolution: {integrity: sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} '@eslint/core@1.2.1': @@ -694,32 +644,17 @@ packages: '@tybys/wasm-util@0.10.2': resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==} - '@types/chrome@0.1.42': - resolution: {integrity: sha512-tdT2roFqGecZZDjA9fUEAINb2STxSPifHMDvY6EfRjNRCjdrs/0FwKt5RCIA9MKMd1arAYZZL3nwEkp6ZLZu2w==} - '@types/esrecurse@4.3.1': resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==} '@types/estree@1.0.9': resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} - '@types/filesystem@0.0.36': - resolution: {integrity: sha512-vPDXOZuannb9FZdxgHnqSwAG/jvdGM8Wq+6N4D/d80z+D4HWH+bItqsZaVRQykAn6WEVeEkLm2oQigyHtgb0RA==} - - '@types/filewriter@0.0.33': - resolution: {integrity: sha512-xFU8ZXTw4gd358lb2jw25nxY9QAgqn2+bKKjKOYfNCzN4DKCFetK7sPtrlpg66Ywe3vWY9FNxprZawAh9wfJ3g==} - - '@types/firefox-webext-browser@143.0.0': - resolution: {integrity: sha512-865dYKMOP0CllFyHmgXV4IQgVL51OSQQCwSoihQ17EwugePKFSAZRc0EI+y7Ly4q7j5KyURlA7LgRpFieO4JOw==} - - '@types/har-format@1.2.16': - resolution: {integrity: sha512-fluxdy7ryD3MV6h8pTfTYpy/xQzCFC7m89nOH9y94cNqJ1mDIDPut7MnRHI3F6qRmh/cT2fUjG1MLdCNb4hE9A==} - '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - '@types/node@24.12.3': - resolution: {integrity: sha512-8oljBDGun9cIsZRJR6fkihn0TSXJI0UDOOhncYaERq6M0JMDoPLxyscwruJcb4GKS6dvK/d8xebYBg27h/duaQ==} + '@types/node@24.12.4': + resolution: {integrity: sha512-GUUEShf+PBCGW2KaXwcIt3Yk+e3pkKwWKb9GSyM9WQVE+ep2jzmHdGsHzu4wgcZy5fN9FBdVzjpBQsYlpfpgLA==} '@types/node@25.6.2': resolution: {integrity: sha512-sokuT28dxf9JT5Kady1fsXOvI4HVpjZa95NKT5y9PNTIrs2AsobR4GFAA90ZG8M+nxVRLysCXsVj6eGC7Vbrlw==} @@ -732,66 +667,63 @@ packages: '@types/react@19.2.14': resolution: {integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==} - '@types/webextension-polyfill@0.12.5': - resolution: {integrity: sha512-uKSAv6LgcVdINmxXMKBuVIcg/2m5JZugoZO8x20g7j2bXJkPIl/lVGQcDlbV+aXAiTyXT2RA5U5mI4IGCDMQeg==} - - '@typescript-eslint/eslint-plugin@8.59.3': - resolution: {integrity: sha512-PwFvSKsXGShKGW6n5bZOhGHEcCZXM8HofLK9fNsEwZXzFRjoY+XT1Vsf1zgyXdwTr0ZYz1/2tkZ0DBTT9jZjhw==} + '@typescript-eslint/eslint-plugin@8.59.4': + resolution: {integrity: sha512-PegsU+XfyJJNjd4+u/k6f9yTyp0lEXXiPopUNobZcIAUJFGICFLN+sP0Rb3JehVmiij1Ph0dFGYqODoRo/2+6A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.59.3 + '@typescript-eslint/parser': ^8.59.4 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/parser@8.59.3': - resolution: {integrity: sha512-HPwA+hVkfcriajbNvTmZv4VRauibay+cWArYUYq7u7W7PmGShMxbPxLvrwDme55a6d5alG3nrYfhyJ/G28XlLg==} + '@typescript-eslint/parser@8.59.4': + resolution: {integrity: sha512-zORHqO/tuhxY1zWuTvMUqddRxpiFJ72xVfcNoWpqdLjs6lfPbuQBJuW4pk+49/uBMy7Ssr4bzgjiKmmDB1UbZQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/project-service@8.59.3': - resolution: {integrity: sha512-ECiUWa/KYRGDFUqTNehaRgzDshnJfkTABJxVemHk4ko22gcr0ukloKjWvyQ64g8YCV/UI47kN1dbmjf/GaQYng==} + '@typescript-eslint/project-service@8.59.4': + resolution: {integrity: sha512-Ly00Vu4oAacfDeHp2Zg85ioNG6l8HG+tN1D7J+xTHSxu9y0awYKJ2zH1rFBn8ZSfuGK+7FxK3Cgl3uAz0aZZLg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/scope-manager@8.59.3': - resolution: {integrity: sha512-t2LvZnoEfzKtnPjgeEu41xw5gxq9mQVfYy4OoZ4Vlt0sk3JwxmhCca/AR7DwOiHrjWgjAj6as4AhRLKSDfvZIA==} + '@typescript-eslint/scope-manager@8.59.4': + resolution: {integrity: sha512-mUeR/3H1WrTAddJrwut8OoPjfauaztMQmRwV5fQTUyNVJCLiUXXe4lGEyYIL2oFDpP7UtgbGJXCt72wT0z2S3Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.59.3': - resolution: {integrity: sha512-PcIJHjmaREXLgIAIzLnSY9VucEzz8FKXsRgFa1DmdGCK/5tJpW03TKJF01Q6VZd1lLdz2sIKPWaDUZN9dp//dw==} + '@typescript-eslint/tsconfig-utils@8.59.4': + resolution: {integrity: sha512-DLCpnKgD4alVxTBSKulK+gU1KCqOgUXfDRDXh2mZgzokQKa/70ax93I2uVO3m/LLvIAtWZIFoiifudmIqAxpMA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/type-utils@8.59.3': - resolution: {integrity: sha512-g71d8QD8UaiHGvrJwyIS1hCX5r63w6Jll+4VEYhEAHXTDIqX1JgxhTAbEHtKntL9kuc4jRo7/GWw5xfCepSccQ==} + '@typescript-eslint/type-utils@8.59.4': + resolution: {integrity: sha512-uonTuPAAKr9XaBGqJ3LjYTh72zy5DyGesljO9gtmk/eFW0W1fRHjnwVYKB35Lm8d5Q5CluEW3gPHjTvZTmgrfA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/types@8.59.3': - resolution: {integrity: sha512-ePFoH0g4ludssdRFqqDxQePCxU4WQyRa9+XVwjm7yLn0FKhMeoetC+qBEEI1Eyb1pGSDveTIT09Bvw2WhlGayg==} + '@typescript-eslint/types@8.59.4': + resolution: {integrity: sha512-F1o7WJcCq+bc8dwcO/YsSEOudAH8RDtaOhM6wcAQhcUsFhnWQl81JKy48q1hoxAU0qrzM89+31GYh1515Zde3Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.59.3': - resolution: {integrity: sha512-CbRjVRAf7Lr9Kr8RopKcbY45p2VfmmHrm0ygOCYFi7oU8q19m0Fs/6iHS7kNOmwpp+ob07ZVcAqlxUod9lYdmg==} + '@typescript-eslint/typescript-estree@8.59.4': + resolution: {integrity: sha512-F+RuOmcDXo4+TPdfd/TCLS3m2nw8gE9XXyZLrA3JBfaA5tz9TtdkyD3YJFmPxulyc2cKbEok/CvFE3MgSLWnag==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/utils@8.59.3': - resolution: {integrity: sha512-JAvT14goBzRzzzZyqq3P9BLArIxTtQURUtFgQ/V7FO+eU+Gg6ES+5ymOPP1wRxXcxAYeivCk4uS3jCKWI1K8Zg==} + '@typescript-eslint/utils@8.59.4': + resolution: {integrity: sha512-cYXeNAUsG4lJo5dbc1FcKm+JwIWrj1/UpTORsC6tGMjEZ81DYcvIr9/ueikhMa/Y/gDQYGp+YX9/xQrXje5BJw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/visitor-keys@8.59.3': - resolution: {integrity: sha512-f1UQF7ggd42YiwI5wGrRaPsa+P0CINBlrkLPmGfpq/u/I/oVtecoEIfFR9ag/oa1sLOsRNZ6xehf6qMZhQGBDg==} + '@typescript-eslint/visitor-keys@8.59.4': + resolution: {integrity: sha512-U3gxVaDVnuZKhSspW/MzMxE1kq7zOdc072FcSNoqA1I9p8HyKbBFfEHoWckBAMgNMph4MamwS5iTVzFmrnt8TQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@vitejs/plugin-react@6.0.1': @@ -850,8 +782,8 @@ packages: resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} engines: {node: 18 || 20 || >=22} - baseline-browser-mapping@2.10.29: - resolution: {integrity: sha512-Asa2krT+XTPZINCS+2QcyS8WTkObE77RwkydwF7h6DmnKqbvlalz93m/dnphUyCa6SWSP51VgtEUf2FN+gelFQ==} + baseline-browser-mapping@2.10.32: + resolution: {integrity: sha512-wbPvpyjJPC0zdfdKXxqEL3Ea+bOMD/87X4lftiJkkaBiuG6ALQy1SLmEd7BSmVCuwCQsBrCamgBoLyfFDD1EPg==} engines: {node: '>=6.0.0'} hasBin: true @@ -867,8 +799,8 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true - caniuse-lite@1.0.30001792: - resolution: {integrity: sha512-hVLMUZFgR4JJ6ACt1uEESvQN1/dBVqPAKY0hgrV70eN3391K6juAfTjKZLKvOMsx8PxA7gsY1/tLMMTcfFLLpw==} + caniuse-lite@1.0.30001793: + resolution: {integrity: sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==} color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} @@ -913,8 +845,8 @@ packages: eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - electron-to-chromium@1.5.353: - resolution: {integrity: sha512-kOrWphBi8TOZyiJZqsgqIle0lw+tzmnQK83pV9dZUd01Nm2POECSyFQMAuarzZdYqQW7FH9RaYOuaRo3h+bQ3w==} + electron-to-chromium@1.5.361: + resolution: {integrity: sha512-Q6Hts7N9FnJc5LeGRINFvLhCI9xZmNtTDe5ZbcVezQz7cU4a8Aua3GH1b8J2XY8Al9PF+OCwYqhgsOOheMdvkA==} emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -957,8 +889,8 @@ packages: resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} - eslint@10.3.0: - resolution: {integrity: sha512-XbEXaRva5cF0ZQB8w6MluHA0kZZfV2DuCMJ3ozyEOHLwDpZX2Lmm/7Pp0xdJmI0GL1W05VH5VwIFHEm1Vcw2gw==} + eslint@10.4.0: + resolution: {integrity: sha512-loXy6bWOoP3EP6JA7jo6p5jMpBJmHmsNZM5SFRHLdh1MGOPurMnNBj4ZlAbaqUAaQWbCr7jHV4P7gzAyryZWkQ==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} hasBin: true peerDependencies: @@ -1233,8 +1165,9 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - node-releases@2.0.38: - resolution: {integrity: sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==} + node-releases@2.0.46: + resolution: {integrity: sha512-GYVXHE2KnrzAfsAjl4uP++evGFCrAU1jta4ubEjIG7YWt/64Gqv66a30yKwWczVjA6j3bM4nBwH7Pk1JmDHaxQ==} + engines: {node: '>=18'} optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} @@ -1315,8 +1248,8 @@ packages: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true - semver@7.8.0: - resolution: {integrity: sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==} + semver@7.8.1: + resolution: {integrity: sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==} engines: {node: '>=10'} hasBin: true @@ -1407,8 +1340,8 @@ packages: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} - typescript-eslint@8.59.3: - resolution: {integrity: sha512-KgusgyDgG4LI8Ih/sWaCtZ06tckLAS5CvT5A4D1Q7bYVoAAyzwiZvE4BmwDHkhRVkvhRBepKeASoFzQetha7Fg==} + typescript-eslint@8.59.4: + resolution: {integrity: sha512-Rw6+44QNFaXtgHSjPy+Kw8hrJniMYzR85E9yLmOLcfZ91/rz+JXQbDTCmc6ccxMPY6K6PgAq26f0JCBfR7LIPQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 @@ -1485,9 +1418,6 @@ packages: yaml: optional: true - webextension-polyfill@0.12.0: - resolution: {integrity: sha512-97TBmpoWJEE+3nFBQ4VocyCdLKfw54rFaJ6EVQYLBCXqCIpLSZkwGgASpv4oPt9gdKCJ80RJlcmNzNn008Ag6Q==} - which@2.0.1: resolution: {integrity: sha512-N7GBZOTswtB9lkQBZA4+zAXrjEIWAUOB93AvzUiudRzRxhUdLURQ7D/gAIMY1gatT/LTbmbcv8SiYazy3eYB7w==} engines: {node: '>= 8'} @@ -1525,7 +1455,7 @@ snapshots: '@babel/code-frame@7.29.0': dependencies: - '@babel/helper-validator-identifier': 7.28.5 + '@babel/helper-validator-identifier': 7.29.7 js-tokens: 4.0.0 picocolors: 1.1.1 @@ -1562,12 +1492,12 @@ snapshots: '@babel/helper-compilation-targets@7.28.6': dependencies: '@babel/compat-data': 7.29.3 - '@babel/helper-validator-option': 7.27.1 + '@babel/helper-validator-option': 7.29.7 browserslist: 4.28.2 lru-cache: 5.1.1 semver: 6.3.1 - '@babel/helper-globals@7.28.0': {} + '@babel/helper-globals@7.29.7': {} '@babel/helper-module-imports@7.28.6': dependencies: @@ -1580,16 +1510,16 @@ snapshots: dependencies: '@babel/core': 7.29.0 '@babel/helper-module-imports': 7.28.6 - '@babel/helper-validator-identifier': 7.28.5 + '@babel/helper-validator-identifier': 7.29.7 '@babel/traverse': 7.29.0 transitivePeerDependencies: - supports-color - '@babel/helper-string-parser@7.27.1': {} + '@babel/helper-string-parser@7.29.7': {} - '@babel/helper-validator-identifier@7.28.5': {} + '@babel/helper-validator-identifier@7.29.7': {} - '@babel/helper-validator-option@7.27.1': {} + '@babel/helper-validator-option@7.29.7': {} '@babel/helpers@7.29.2': dependencies: @@ -1612,7 +1542,7 @@ snapshots: dependencies: '@babel/code-frame': 7.29.0 '@babel/generator': 7.29.1 - '@babel/helper-globals': 7.28.0 + '@babel/helper-globals': 7.29.7 '@babel/parser': 7.29.3 '@babel/template': 7.28.6 '@babel/types': 7.29.0 @@ -1622,8 +1552,8 @@ snapshots: '@babel/types@7.29.0': dependencies: - '@babel/helper-string-parser': 7.27.1 - '@babel/helper-validator-identifier': 7.28.5 + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 '@base-ui/react@1.4.1(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: @@ -1668,9 +1598,9 @@ snapshots: tslib: 2.8.1 optional: true - '@eslint-community/eslint-utils@4.9.1(eslint@10.3.0(jiti@2.7.0))': + '@eslint-community/eslint-utils@4.9.1(eslint@10.4.0(jiti@2.7.0))': dependencies: - eslint: 10.3.0(jiti@2.7.0) + eslint: 10.4.0(jiti@2.7.0) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.2': {} @@ -1683,7 +1613,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/config-helpers@0.5.5': + '@eslint/config-helpers@0.6.0': dependencies: '@eslint/core': 1.2.1 @@ -1691,9 +1621,9 @@ snapshots: dependencies: '@types/json-schema': 7.0.15 - '@eslint/js@10.0.1(eslint@10.3.0(jiti@2.7.0))': + '@eslint/js@10.0.1(eslint@10.4.0(jiti@2.7.0))': optionalDependencies: - eslint: 10.3.0(jiti@2.7.0) + eslint: 10.4.0(jiti@2.7.0) '@eslint/object-schema@3.0.5': {} @@ -1932,28 +1862,13 @@ snapshots: tslib: 2.8.1 optional: true - '@types/chrome@0.1.42': - dependencies: - '@types/filesystem': 0.0.36 - '@types/har-format': 1.2.16 - '@types/esrecurse@4.3.1': {} '@types/estree@1.0.9': {} - '@types/filesystem@0.0.36': - dependencies: - '@types/filewriter': 0.0.33 - - '@types/filewriter@0.0.33': {} - - '@types/firefox-webext-browser@143.0.0': {} - - '@types/har-format@1.2.16': {} - '@types/json-schema@7.0.15': {} - '@types/node@24.12.3': + '@types/node@24.12.4': dependencies: undici-types: 7.16.0 @@ -1969,17 +1884,15 @@ snapshots: dependencies: csstype: 3.2.3 - '@types/webextension-polyfill@0.12.5': {} - - '@typescript-eslint/eslint-plugin@8.59.3(@typescript-eslint/parser@8.59.3(eslint@10.3.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.3.0(jiti@2.7.0))(typescript@6.0.3)': + '@typescript-eslint/eslint-plugin@8.59.4(@typescript-eslint/parser@8.59.4(eslint@10.4.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.4.0(jiti@2.7.0))(typescript@6.0.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.59.3(eslint@10.3.0(jiti@2.7.0))(typescript@6.0.3) - '@typescript-eslint/scope-manager': 8.59.3 - '@typescript-eslint/type-utils': 8.59.3(eslint@10.3.0(jiti@2.7.0))(typescript@6.0.3) - '@typescript-eslint/utils': 8.59.3(eslint@10.3.0(jiti@2.7.0))(typescript@6.0.3) - '@typescript-eslint/visitor-keys': 8.59.3 - eslint: 10.3.0(jiti@2.7.0) + '@typescript-eslint/parser': 8.59.4(eslint@10.4.0(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/scope-manager': 8.59.4 + '@typescript-eslint/type-utils': 8.59.4(eslint@10.4.0(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/utils': 8.59.4(eslint@10.4.0(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/visitor-keys': 8.59.4 + eslint: 10.4.0(jiti@2.7.0) ignore: 7.0.5 natural-compare: 1.4.0 ts-api-utils: 2.5.0(typescript@6.0.3) @@ -1987,85 +1900,85 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.59.3(eslint@10.3.0(jiti@2.7.0))(typescript@6.0.3)': + '@typescript-eslint/parser@8.59.4(eslint@10.4.0(jiti@2.7.0))(typescript@6.0.3)': dependencies: - '@typescript-eslint/scope-manager': 8.59.3 - '@typescript-eslint/types': 8.59.3 - '@typescript-eslint/typescript-estree': 8.59.3(typescript@6.0.3) - '@typescript-eslint/visitor-keys': 8.59.3 + '@typescript-eslint/scope-manager': 8.59.4 + '@typescript-eslint/types': 8.59.4 + '@typescript-eslint/typescript-estree': 8.59.4(typescript@6.0.3) + '@typescript-eslint/visitor-keys': 8.59.4 debug: 4.4.3 - eslint: 10.3.0(jiti@2.7.0) + eslint: 10.4.0(jiti@2.7.0) typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.59.3(typescript@6.0.3)': + '@typescript-eslint/project-service@8.59.4(typescript@6.0.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.59.3(typescript@6.0.3) - '@typescript-eslint/types': 8.59.3 + '@typescript-eslint/tsconfig-utils': 8.59.4(typescript@6.0.3) + '@typescript-eslint/types': 8.59.4 debug: 4.4.3 typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.59.3': + '@typescript-eslint/scope-manager@8.59.4': dependencies: - '@typescript-eslint/types': 8.59.3 - '@typescript-eslint/visitor-keys': 8.59.3 + '@typescript-eslint/types': 8.59.4 + '@typescript-eslint/visitor-keys': 8.59.4 - '@typescript-eslint/tsconfig-utils@8.59.3(typescript@6.0.3)': + '@typescript-eslint/tsconfig-utils@8.59.4(typescript@6.0.3)': dependencies: typescript: 6.0.3 - '@typescript-eslint/type-utils@8.59.3(eslint@10.3.0(jiti@2.7.0))(typescript@6.0.3)': + '@typescript-eslint/type-utils@8.59.4(eslint@10.4.0(jiti@2.7.0))(typescript@6.0.3)': dependencies: - '@typescript-eslint/types': 8.59.3 - '@typescript-eslint/typescript-estree': 8.59.3(typescript@6.0.3) - '@typescript-eslint/utils': 8.59.3(eslint@10.3.0(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/types': 8.59.4 + '@typescript-eslint/typescript-estree': 8.59.4(typescript@6.0.3) + '@typescript-eslint/utils': 8.59.4(eslint@10.4.0(jiti@2.7.0))(typescript@6.0.3) debug: 4.4.3 - eslint: 10.3.0(jiti@2.7.0) + eslint: 10.4.0(jiti@2.7.0) ts-api-utils: 2.5.0(typescript@6.0.3) typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.59.3': {} + '@typescript-eslint/types@8.59.4': {} - '@typescript-eslint/typescript-estree@8.59.3(typescript@6.0.3)': + '@typescript-eslint/typescript-estree@8.59.4(typescript@6.0.3)': dependencies: - '@typescript-eslint/project-service': 8.59.3(typescript@6.0.3) - '@typescript-eslint/tsconfig-utils': 8.59.3(typescript@6.0.3) - '@typescript-eslint/types': 8.59.3 - '@typescript-eslint/visitor-keys': 8.59.3 + '@typescript-eslint/project-service': 8.59.4(typescript@6.0.3) + '@typescript-eslint/tsconfig-utils': 8.59.4(typescript@6.0.3) + '@typescript-eslint/types': 8.59.4 + '@typescript-eslint/visitor-keys': 8.59.4 debug: 4.4.3 minimatch: 10.2.5 - semver: 7.8.0 + semver: 7.8.1 tinyglobby: 0.2.16 ts-api-utils: 2.5.0(typescript@6.0.3) typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.59.3(eslint@10.3.0(jiti@2.7.0))(typescript@6.0.3)': + '@typescript-eslint/utils@8.59.4(eslint@10.4.0(jiti@2.7.0))(typescript@6.0.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.3.0(jiti@2.7.0)) - '@typescript-eslint/scope-manager': 8.59.3 - '@typescript-eslint/types': 8.59.3 - '@typescript-eslint/typescript-estree': 8.59.3(typescript@6.0.3) - eslint: 10.3.0(jiti@2.7.0) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.4.0(jiti@2.7.0)) + '@typescript-eslint/scope-manager': 8.59.4 + '@typescript-eslint/types': 8.59.4 + '@typescript-eslint/typescript-estree': 8.59.4(typescript@6.0.3) + eslint: 10.4.0(jiti@2.7.0) typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.59.3': + '@typescript-eslint/visitor-keys@8.59.4': dependencies: - '@typescript-eslint/types': 8.59.3 + '@typescript-eslint/types': 8.59.4 eslint-visitor-keys: 5.0.1 - '@vitejs/plugin-react@6.0.1(vite@8.0.12(@types/node@24.12.3)(jiti@2.7.0))': + '@vitejs/plugin-react@6.0.1(vite@8.0.12(@types/node@24.12.4)(jiti@2.7.0))': dependencies: '@rolldown/pluginutils': 1.0.0-rc.7 - vite: 8.0.12(@types/node@24.12.3)(jiti@2.7.0) + vite: 8.0.12(@types/node@24.12.4)(jiti@2.7.0) '@vitejs/plugin-react@6.0.1(vite@8.0.12(@types/node@25.6.2)(jiti@2.7.0))': dependencies: @@ -2105,7 +2018,7 @@ snapshots: balanced-match@4.0.4: {} - baseline-browser-mapping@2.10.29: {} + baseline-browser-mapping@2.10.32: {} brace-expansion@2.1.0: dependencies: @@ -2117,13 +2030,13 @@ snapshots: browserslist@4.28.2: dependencies: - baseline-browser-mapping: 2.10.29 - caniuse-lite: 1.0.30001792 - electron-to-chromium: 1.5.353 - node-releases: 2.0.38 + baseline-browser-mapping: 2.10.32 + caniuse-lite: 1.0.30001793 + electron-to-chromium: 1.5.361 + node-releases: 2.0.46 update-browserslist-db: 1.2.3(browserslist@4.28.2) - caniuse-lite@1.0.30001792: {} + caniuse-lite@1.0.30001793: {} color-convert@2.0.1: dependencies: @@ -2155,7 +2068,7 @@ snapshots: eastasianwidth@0.2.0: {} - electron-to-chromium@1.5.353: {} + electron-to-chromium@1.5.361: {} emoji-regex@8.0.0: {} @@ -2170,20 +2083,20 @@ snapshots: escape-string-regexp@4.0.0: {} - eslint-plugin-react-hooks@7.1.1(eslint@10.3.0(jiti@2.7.0)): + eslint-plugin-react-hooks@7.1.1(eslint@10.4.0(jiti@2.7.0)): dependencies: '@babel/core': 7.29.0 '@babel/parser': 7.29.3 - eslint: 10.3.0(jiti@2.7.0) + eslint: 10.4.0(jiti@2.7.0) hermes-parser: 0.25.1 zod: 4.4.3 zod-validation-error: 4.0.2(zod@4.4.3) transitivePeerDependencies: - supports-color - eslint-plugin-react-refresh@0.5.2(eslint@10.3.0(jiti@2.7.0)): + eslint-plugin-react-refresh@0.5.2(eslint@10.4.0(jiti@2.7.0)): dependencies: - eslint: 10.3.0(jiti@2.7.0) + eslint: 10.4.0(jiti@2.7.0) eslint-scope@9.1.2: dependencies: @@ -2196,12 +2109,12 @@ snapshots: eslint-visitor-keys@5.0.1: {} - eslint@10.3.0(jiti@2.7.0): + eslint@10.4.0(jiti@2.7.0): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.3.0(jiti@2.7.0)) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.4.0(jiti@2.7.0)) '@eslint-community/regexpp': 4.12.2 '@eslint/config-array': 0.23.5 - '@eslint/config-helpers': 0.5.5 + '@eslint/config-helpers': 0.6.0 '@eslint/core': 1.2.1 '@eslint/plugin-kit': 0.7.1 '@humanfs/node': 0.16.8 @@ -2437,7 +2350,7 @@ snapshots: natural-compare@1.4.0: {} - node-releases@2.0.38: {} + node-releases@2.0.46: {} optionator@0.9.4: dependencies: @@ -2521,7 +2434,7 @@ snapshots: semver@6.3.1: {} - semver@7.8.0: {} + semver@7.8.1: {} shebang-command@2.0.0: dependencies: @@ -2608,13 +2521,13 @@ snapshots: dependencies: prelude-ls: 1.2.1 - typescript-eslint@8.59.3(eslint@10.3.0(jiti@2.7.0))(typescript@6.0.3): + typescript-eslint@8.59.4(eslint@10.4.0(jiti@2.7.0))(typescript@6.0.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.59.3(@typescript-eslint/parser@8.59.3(eslint@10.3.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.3.0(jiti@2.7.0))(typescript@6.0.3) - '@typescript-eslint/parser': 8.59.3(eslint@10.3.0(jiti@2.7.0))(typescript@6.0.3) - '@typescript-eslint/typescript-estree': 8.59.3(typescript@6.0.3) - '@typescript-eslint/utils': 8.59.3(eslint@10.3.0(jiti@2.7.0))(typescript@6.0.3) - eslint: 10.3.0(jiti@2.7.0) + '@typescript-eslint/eslint-plugin': 8.59.4(@typescript-eslint/parser@8.59.4(eslint@10.4.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.4.0(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/parser': 8.59.4(eslint@10.4.0(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/typescript-estree': 8.59.4(typescript@6.0.3) + '@typescript-eslint/utils': 8.59.4(eslint@10.4.0(jiti@2.7.0))(typescript@6.0.3) + eslint: 10.4.0(jiti@2.7.0) typescript: 6.0.3 transitivePeerDependencies: - supports-color @@ -2641,7 +2554,7 @@ snapshots: v8-compile-cache-lib@3.0.1: {} - vite@8.0.12(@types/node@24.12.3)(jiti@2.7.0): + vite@8.0.12(@types/node@24.12.4)(jiti@2.7.0): dependencies: lightningcss: 1.32.0 picomatch: 4.0.4 @@ -2649,7 +2562,7 @@ snapshots: rolldown: 1.0.0 tinyglobby: 0.2.16 optionalDependencies: - '@types/node': 24.12.3 + '@types/node': 24.12.4 fsevents: 2.3.3 jiti: 2.7.0 @@ -2665,8 +2578,6 @@ snapshots: fsevents: 2.3.3 jiti: 2.7.0 - webextension-polyfill@0.12.0: {} - which@2.0.1: dependencies: isexe: 2.0.0 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index ea14cf7..623d538 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,7 +1,7 @@ packages: - "packages/*" - "apps/*" - - "templates/*" + - "playground/*" allowBuilds: esbuild: false sharp: false diff --git a/templates/vite-react-lib/playground/App.tsx b/templates/vite-react-lib/playground/App.tsx deleted file mode 100644 index 8083649..0000000 --- a/templates/vite-react-lib/playground/App.tsx +++ /dev/null @@ -1,7 +0,0 @@ - - -function App() { - return
; -} - -export default App; diff --git a/templates/vite-react-lib/playground/index.html b/templates/vite-react-lib/playground/index.html deleted file mode 100644 index ce9e98b..0000000 --- a/templates/vite-react-lib/playground/index.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - vite-project - - -
- - - diff --git a/templates/vite-react-lib/playground/main.tsx b/templates/vite-react-lib/playground/main.tsx deleted file mode 100644 index 4aff025..0000000 --- a/templates/vite-react-lib/playground/main.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import { StrictMode } from 'react' -import { createRoot } from 'react-dom/client' -import App from './App.tsx' - -createRoot(document.getElementById('root')!).render( - - - , -) diff --git a/templates/vite-react-lib/scripts/gen-index.ts b/templates/vite-react-lib/scripts/gen-index.ts index 64433db..c685295 100644 --- a/templates/vite-react-lib/scripts/gen-index.ts +++ b/templates/vite-react-lib/scripts/gen-index.ts @@ -1,147 +1,254 @@ -import fs from "fs"; -import path from "path"; -// 1. 引入 tinyglobby -import { globSync } from "tinyglobby"; +import * as fs from "fs"; +import * as path from "path"; -interface Config { +/** + * 默认测试 / story / example / fixture / type 文件匹配规则 + * 仅用于“识别”,不直接决定是否排除 + */ +const DEFAULT_TEST_FILE_PATTERNS: RegExp[] = [ + // ========================= + // Test / Spec + // ========================= + /\.(test|spec)\.(ts|tsx|js|jsx)$/, + /\.(test|spec)\.(ts|tsx|js|jsx)\?.*$/, // query param safe + + // ========================= + // E2E / Playwright / Cypress + // ========================= + /\.(cy|playwright|e2e)\.(ts|tsx|js|jsx)$/, + + // ========================= + // Storybook + // ========================= + /\.(story|stories)\.(ts|tsx|js|jsx|mdx)$/, + + // ========================= + // Snapshot / Mock / Fixture + // ========================= + /\.snap$/, + /\.mock\.(ts|tsx|js|jsx)$/, + /\.fixture\.(ts|tsx|js|jsx)$/, + + // ========================= + // Type declarations + // ========================= + /\.d\.ts$/, + + // ========================= + // Examples / Demos / Docs + // ========================= + /\.(example|demo|docs)\.(ts|tsx|js|jsx)$/, +]; + +/** + * 测试 / 辅助目录(目录级排除) + */ +const DEFAULT_TEST_DIR_PATTERNS: RegExp[] = [ + /[/\\](__tests__|tests|test|spec|__mocks__|__fixtures__|__snapshots__)[\/\\]/, + /[/\\](cypress|playwright|e2e|__e2e__|__playwright__)[\/\\]/, + /[/\\](story|stories|\.storybook)[\/\\]/, + /[/\\](types|type|typings|interfaces)[\/\\]/, + /[/\\](examples|example|demo|demos|docs)[\/\\]/, +]; + +type Config = { outputDir: string; - outputFilenameWithExt: string; + outputFile: string; scanDirs: string[]; - importPrefix: string; - predefineStatements: string[]; - includeExtensions: string[]; - excludeDirs: string[]; - excludeFileExtensions: string[]; - excludePatterns: RegExp[]; -} - -const cssConfig: Config = { - outputDir: "src", - outputFilenameWithExt: "index.css", - scanDirs: ["src"], - importPrefix: "@import", - predefineStatements: [], - includeExtensions: [".css"], - excludeDirs: ["__tests__", "tests", "story", "stories", "types"], - excludeFileExtensions: [], - excludePatterns: [/^index\.(css)$/, /\.(test|spec)\./, /\.(story|stories)\./], + preamble?: string[]; + importPrefix?: string; + entryFilePatterns?: RegExp[]; + barrelFirstMode?: boolean; + includeFilePatterns?: RegExp[]; + excludeFilePatterns?: RegExp[]; + excludeDirPatterns?: RegExp[]; }; const tsConfig: Config = { outputDir: "src", - outputFilenameWithExt: "index.ts", + outputFile: "index.ts", scanDirs: ["src"], importPrefix: "export * from", - predefineStatements: ["import './index.css'"], - includeExtensions: [".ts", "tsx", "js", "jsx"], - excludeDirs: ["__tests__", "tests", "story", "stories", "types"], - excludeFileExtensions: [".d.ts"], - excludePatterns: [ - /^index\.(ts|tsx|js|jsx)$/, - /\.(test|spec)\./, - /\.(story|stories)\./, - ], + barrelFirstMode: true, + preamble: ["import './index.css';"], + entryFilePatterns: [/^index\.(js|ts|jsx|tsx)$/], + includeFilePatterns: [/\.(js|ts|jsx|tsx)$/], + excludeFilePatterns: DEFAULT_TEST_FILE_PATTERNS, + excludeDirPatterns: DEFAULT_TEST_DIR_PATTERNS, }; -const normalizePath = (p: string) => p.replace(/\\/g, "/"); - -const isExcludeDir = (filePath: string, excludeDirs: string[]) => { - const normalized = normalizePath(filePath); - return excludeDirs.some((dir) => normalized.includes(`/${dir}/`)); +const cssConfig: Config = { + outputDir: "src", + outputFile: "index.css", + scanDirs: ["src"], + importPrefix: "@import", + barrelFirstMode: false, + entryFilePatterns: [/^index\.(css)$/], + includeFilePatterns: [/\.(css)$/], + excludeFilePatterns: DEFAULT_TEST_FILE_PATTERNS, + excludeDirPatterns: DEFAULT_TEST_DIR_PATTERNS, }; -const isExcludeFileExtensions = ( - filePath: string, - excludeFileExtensions: string[], -) => excludeFileExtensions.some((ext) => filePath.endsWith(ext)); +function isEntryFile(fileName: string, config: Config): boolean { + const regExps = config.entryFilePatterns; -const isExcludePattern = (fileName: string, excludePatterns: RegExp[]) => - excludePatterns.some((pattern) => pattern.test(fileName)); + if (!regExps || regExps.length === 0) return false; -// ---------------------------------------- -function isValidFile(filePath: string, config: Config): boolean { - const fileName = filePath.split(/[\\/]/).pop()!; - - if (isExcludeDir(filePath, config.excludeDirs)) return false; - if (isExcludeFileExtensions(filePath, config.excludeFileExtensions)) - return false; - if (isExcludePattern(fileName, config.excludePatterns)) return false; - - const ext = path.extname(filePath); - return config.includeExtensions.includes(ext); + return regExps.some((regExp) => regExp.test(fileName)); } -// ----------------------------------------- -function generateIndexFile(config: Config) { - const currentPath = process.cwd(); - const outputPath = path.resolve(currentPath, config.outputDir); - let exportStatements: string[] = []; - // ------ scanDirs forEach start ------------------------ - config.scanDirs.forEach((dir) => { - // 2. 路径模式保持不变,tinyglobby 能够正确处理 - const scanPattern = path.resolve(currentPath, dir, "**", "*.*"); +function isIncludeFile(fileName: string, config: Config): boolean { + const regExps = config.includeFilePatterns; - const allFilePath = globSync(scanPattern, { - absolute: true, - // 3. 移除了 windowsPathsNoEscape,tinyglobby 默认处理路径更智能 + if (!regExps || regExps.length === 0) return false; + + return regExps.some((regExp) => regExp.test(fileName)); +} + +function isExcludeFile(fileName: string, config: Config): boolean { + const regExps = config.excludeFilePatterns; + + if (!regExps || regExps.length === 0) return false; + + return regExps.some((regExp) => regExp.test(fileName)); +} + +function isExcludeDir(filePath: string, config: Config): boolean { + const regExps = config.excludeDirPatterns; + + if (!regExps || regExps.length === 0) return false; + + return regExps.some((regExp) => regExp.test(filePath)); +} + +function isValidDir(filePath: string, config: Config): boolean { + if (isExcludeDir(filePath, config)) { + return false; + } + return true; +} + +function isValidFile(fileName: string, config: Config): boolean { + if (isIncludeFile(fileName, config) && !isExcludeFile(fileName, config)) { + return true; + } + + return false; +} + +const buildExportStatement = (filePath: string, config: Config) => { + // 文件夹(不是文件)与目标文件的相对路径 + let exportFilePath = path.relative(config.outputDir, filePath); + + // 去除扩展名,并且转化成正斜杠 + const exportFilePathWithoutExt = path + .join( + path.dirname(exportFilePath), + path.basename(exportFilePath, path.extname(exportFilePath)), + ) + .replace(/\\/g, "/"); + + // 加上 ./ 前缀 + return `${config.importPrefix} './${exportFilePathWithoutExt}';`; +}; + +function isOutputEntry(filePath: string, config: Config) { + const outputFilePath = path.resolve(config.outputDir, config.outputFile); + const targetFilePath = path.resolve(filePath); + + return outputFilePath === targetFilePath; +} + +function generateExports(dirPath: string, config: Config): string[] { + const fileNames = fs.readdirSync(dirPath); + const exports: string[] = []; + + // ========================= + // 逻辑分支:Barrel First Mode + // ========================= + if (config.barrelFirstMode) { + // 查找当前目录有没有 index.ts + const entryFileName = fileNames.find((fileName) => { + const filePath = path.join(dirPath, fileName); + return fs.statSync(filePath).isFile() && isEntryFile(fileName, config); }); + // 如果有 + if (entryFileName) { + const entryFilePath = path.join(dirPath, entryFileName); - const validFiles = allFilePath.filter((filePath) => { - return isValidFile(filePath, config); - }); - - if (validFiles.length === 0) { - console.log( - `⚠️ 未找到符合条件的文件,跳过生成 ${config.outputFilenameWithExt}`, - ); - return; - } - - validFiles.sort(); - - validFiles.forEach((file) => { - const relativePath = path.relative(outputPath, file); - const importPath = `./${relativePath.replace(/\\/g, "/")}`; - exportStatements.push(`${config.importPrefix} '${importPath}';`); - }); - }); - - // --------- scanDirs forEach end ---------------- - - const indexFileContent = ` -${config.predefineStatements.join("\n")} -${exportStatements.join("\n")} -`.trim(); - - const indexFilePath = path.resolve( - currentPath, - config.outputDir, - config.outputFilenameWithExt, - ); - - // ✅ 内容比对,避免重复写入 - if (fs.existsSync(indexFilePath)) { - const old = fs.readFileSync(indexFilePath, "utf8"); - if (old === indexFileContent) { - console.log( - `✅ ${config.outputFilenameWithExt} 内容无变化,无需重新生成`, - ); - return; + // 如果是 outputFile 自身,则跳过 + if (isOutputEntry(entryFilePath, config)) { + console.log(`⏭️ 跳过自身入口文件: ${entryFilePath}`); + } else { + exports.push(buildExportStatement(entryFilePath, config)); + } } } - fs.writeFileSync(indexFilePath, indexFileContent, "utf8"); - console.log(`✅ 成功生成 ${config.outputFilenameWithExt}: ${indexFilePath}`); + // ========================= + // 逻辑分支:index.ts 不存在,则继续正常遍历与递归 + // ========================= + fileNames.forEach((fileName) => { + const filePath = path.join(dirPath, fileName); + const stat = fs.statSync(filePath); + + // 情况1:是文件,且通过了校验 + if ( + stat.isFile() && + isValidFile(fileName, config) && + !isEntryFile(fileName, config) + ) { + exports.push(buildExportStatement(filePath, config)); + } + // 情况2:是文件夹,且通过了校验,递归扫描子文件夹 + else if (stat.isDirectory() && isValidDir(filePath, config)) { + const subExports = generateExports(filePath, config); + exports.push(...subExports); + } + }); + + return exports; } -// -------------------------------------------------- +function genIndexFile(config: Config) { + // 确保输出目录存在,如果不存在就递归创建 + if (!fs.existsSync(config.outputDir)) { + fs.mkdirSync(config.outputDir, { recursive: true }); + } + const allExports: string[] = []; + + // 遍历所有需要扫描的根目录 + config.scanDirs.forEach((scanDir) => { + // 只有当目录真实存在时才进行扫描 + if (fs.existsSync(scanDir)) { + const exports = generateExports(scanDir, config); + allExports.push(...exports); + } else { + console.warn(`⚠️ 警告:扫描目录不存在,已跳过 -> ${scanDir}`); + } + }); + + // 拼接最终的文件内容:前言 + 导出语句(使用 Set 自动去重) + const fileContent = [ + ...(config.preamble ?? []), + ...Array.from(new Set(allExports)), + ].join("\n"); + + // 将内容写入到最终的 index.ts 文件中 + const outputFilePath = path.join(config.outputDir, config.outputFile); + fs.writeFileSync(outputFilePath, fileContent, "utf-8"); + + console.log(`✨ 成功生成入口文件: ${outputFilePath}`); +} + +// ================= 脚本执行入口 ================= try { - console.log(`🚀 [gen-index] 开始扫描`); - generateIndexFile(cssConfig); - generateIndexFile(tsConfig); -} catch (err) { - const msg = err instanceof Error ? err.message : String(err); - console.error(`❌ [gen-index] 执行失败: ${msg}`); - process.exit(1); + console.log("🚀 开始扫描并生成入口文件..."); + genIndexFile(tsConfig); + genIndexFile(cssConfig); + console.log("✅ 脚本执行完毕!"); +} catch (error) { + console.error("❌ 脚本执行失败:", error); + process.exit(1); // 如果报错,让进程以非 0 状态码退出 } diff --git a/templates/vite-react-lib/src/index.css b/templates/vite-react-lib/src/index.css new file mode 100644 index 0000000..e69de29 diff --git a/templates/vite-react-lib/src/index.ts b/templates/vite-react-lib/src/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/templates/vite-react-lib/vite.config.ts b/templates/vite-react-lib/vite.config.ts index 4418ded..ea4923d 100644 --- a/templates/vite-react-lib/vite.config.ts +++ b/templates/vite-react-lib/vite.config.ts @@ -2,40 +2,38 @@ import { defineConfig } from "vite"; import react from "@vitejs/plugin-react"; import path from "path"; -export default defineConfig(({ command }) => { - if (command === "serve") { - return { - plugins: [react()], - root: "playground", - }; - } +export default defineConfig({ + plugins: [react()], - return { - plugins: [react()], + build: { + cssMinify: false, - build: { - cssMinify: false, + lib: { + entry: path.resolve(import.meta.dirname, "src/index.ts"), + formats: ["es", "cjs"], + fileName: (format) => `index.${format}.js`, + }, - lib: { - entry: path.resolve(import.meta.dirname, "src/index.ts"), - formats: ["es", "cjs"], - fileName: (format) => `index.${format}.js`, - }, - - rolldownOptions: { - external: ["react", "react-dom", "react/jsx-runtime"], - output: { - globals: { - react: "React", - "react-dom": "ReactDOM", - }, + rolldownOptions: { + external: ["react", "react-dom", "react/jsx-runtime"], + output: { + // 强制将生成的 CSS 命名为 index.css + assetFileNames: (assetInfo) => { + if (assetInfo.name && assetInfo.name.endsWith(".css")) { + return "index.css"; + } + return "[name].[hash][extname]"; + }, + globals: { + react: "React", + "react-dom": "ReactDOM", }, }, - - emptyOutDir: true, - sourcemap: true, - cssCodeSplit: false, - outDir: "dist", }, - }; + + emptyOutDir: true, + sourcemap: true, + cssCodeSplit: false, + outDir: "dist", + }, }); diff --git a/turbo.json b/turbo.json index f6f4952..bd6da81 100644 --- a/turbo.json +++ b/turbo.json @@ -1,15 +1,12 @@ { - "$schema": "https://turborepo.dev/schema.json", - "ui": "tui", + "$schema": "https://turbo.build/schema.json", "tasks": { "build": { "dependsOn": ["^build"], - "inputs": ["$TURBO_DEFAULT$", ".env*"], - "outputs": [".next/**", "!.next/cache/**"] + "outputs": ["dist/**"] }, "dev": { - "cache": false, - "persistent": true + "cache": false } } }