708 lines
14 KiB
Markdown
708 lines
14 KiB
Markdown
# Library with Playground Implementation Plan
|
|
|
|
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
|
|
**Goal:** Transform nychthemeron into a monorepo with a Vue component library (with PrimeVue + Tailwind) and interactive Storybook playground.
|
|
|
|
**Architecture:** Bun workspaces manage two independent packages — library (builds to ESM) and playground (Storybook dev server and static build). Shared configs at root level.
|
|
|
|
**Tech Stack:** Vue 3, PrimeVue, Tailwind CSS, Vite, Storybook, Vitest, TypeScript, Bun workspaces
|
|
|
|
---
|
|
|
|
### Task 1: Create monorepo directory structure
|
|
|
|
**Files:**
|
|
- Create: `packages/library/` directory
|
|
- Create: `packages/playground/` directory
|
|
|
|
- [ ] **Step 1: Create packages directories**
|
|
|
|
```bash
|
|
mkdir -p packages/library/src/{components,composables}
|
|
mkdir -p packages/library/__tests__
|
|
mkdir -p packages/playground/.storybook
|
|
mkdir -p packages/playground/stories
|
|
```
|
|
|
|
- [ ] **Step 2: Verify structure**
|
|
|
|
```bash
|
|
ls -la packages/
|
|
# Expected output shows: library/ and playground/ directories
|
|
```
|
|
|
|
---
|
|
|
|
### Task 2: Move existing source to library package
|
|
|
|
**Files:**
|
|
- Move: `src/` → `packages/library/src/`
|
|
- Delete: `vite.config.ts`, `vitest.config.ts`, `index.html` (will recreate in packages)
|
|
|
|
- [ ] **Step 1: Move src directory**
|
|
|
|
```bash
|
|
mv src packages/library/src
|
|
```
|
|
|
|
- [ ] **Step 2: Verify move**
|
|
|
|
```bash
|
|
ls packages/library/src/
|
|
# Expected: App.vue, main.ts, __tests__/
|
|
```
|
|
|
|
- [ ] **Step 3: Delete old root-level config files**
|
|
|
|
```bash
|
|
rm vite.config.ts vitest.config.ts index.html
|
|
```
|
|
|
|
---
|
|
|
|
### Task 3: Update root package.json for monorepo
|
|
|
|
**Files:**
|
|
- Modify: `package.json`
|
|
|
|
- [ ] **Step 1: Add workspaces and update root config**
|
|
|
|
Replace the entire `package.json` with:
|
|
|
|
```json
|
|
{
|
|
"name": "nychthemeron",
|
|
"version": "0.0.0",
|
|
"private": true,
|
|
"type": "module",
|
|
"workspaces": [
|
|
"packages/*"
|
|
],
|
|
"scripts": {
|
|
"dev": "bun -r --cwd packages/playground dev",
|
|
"build": "bun -r --cwd packages/library build && bun -r --cwd packages/playground build",
|
|
"preview": "bun -r --cwd packages/playground preview",
|
|
"type-check": "vue-tsc --build",
|
|
"lint": "eslint packages/*/src --fix",
|
|
"format": "prettier --write --experimental-cli packages/*/src",
|
|
"test": "bun -r --cwd packages/library test:unit"
|
|
},
|
|
"devDependencies": {
|
|
"@tsconfig/node24": "^24.0.4",
|
|
"@types/jsdom": "^28.0.1",
|
|
"@types/node": "^24.12.2",
|
|
"@vitejs/plugin-vue": "^6.0.6",
|
|
"@vitest/eslint-plugin": "^1.6.16",
|
|
"@vue/eslint-config-typescript": "^14.7.0",
|
|
"@vue/test-utils": "^2.4.6",
|
|
"@vue/tsconfig": "^0.9.1",
|
|
"eslint": "^10.2.1",
|
|
"eslint-config-prettier": "^10.1.8",
|
|
"eslint-plugin-oxlint": "~1.60.0",
|
|
"eslint-plugin-vue": "~10.8.0",
|
|
"jiti": "^2.6.1",
|
|
"jsdom": "^29.0.2",
|
|
"npm-run-all2": "^8.0.4",
|
|
"oxlint": "~1.60.0",
|
|
"prettier": "3.8.3",
|
|
"typescript": "~6.0.0",
|
|
"vite": "^8.0.8",
|
|
"vite-plugin-vue-devtools": "^8.1.1",
|
|
"vitest": "^4.1.4",
|
|
"vue": "^3.5.32",
|
|
"vue-tsc": "^3.2.6"
|
|
},
|
|
"engines": {
|
|
"node": "^20.19.0 || >=22.12.0"
|
|
}
|
|
}
|
|
```
|
|
|
|
- [ ] **Step 2: Install dependencies for new workspace structure**
|
|
|
|
```bash
|
|
bun install
|
|
```
|
|
|
|
Expected output: Bun detects workspaces and creates symlinks in each `packages/*/node_modules`
|
|
|
|
---
|
|
|
|
### Task 4: Create library package.json
|
|
|
|
**Files:**
|
|
- Create: `packages/library/package.json`
|
|
|
|
- [ ] **Step 1: Create library package config**
|
|
|
|
```json
|
|
{
|
|
"name": "@nychthemeron/library",
|
|
"version": "0.1.0",
|
|
"type": "module",
|
|
"exports": {
|
|
".": {
|
|
"import": "./dist/index.js"
|
|
}
|
|
},
|
|
"files": [
|
|
"dist"
|
|
],
|
|
"scripts": {
|
|
"build": "vite build",
|
|
"test:unit": "vitest"
|
|
},
|
|
"peerDependencies": {
|
|
"vue": "^3.5.0",
|
|
"primevue": "^18.0.0"
|
|
},
|
|
"devDependencies": {
|
|
"@vitest/coverage-v8": "^2.0.0",
|
|
"@vue/test-utils": "^2.4.6"
|
|
}
|
|
}
|
|
```
|
|
|
|
Write this to `packages/library/package.json`
|
|
|
|
- [ ] **Step 2: Verify file exists**
|
|
|
|
```bash
|
|
cat packages/library/package.json | grep '"name"'
|
|
# Expected: "@nychthemeron/library"
|
|
```
|
|
|
|
---
|
|
|
|
### Task 5: Create library tsconfig.json
|
|
|
|
**Files:**
|
|
- Create: `packages/library/tsconfig.json`
|
|
|
|
- [ ] **Step 1: Create library TypeScript config**
|
|
|
|
```json
|
|
{
|
|
"extends": "../../tsconfig.json",
|
|
"compilerOptions": {
|
|
"outDir": "./dist",
|
|
"declaration": true,
|
|
"declarationMap": true,
|
|
"emitDeclarationOnly": false
|
|
},
|
|
"include": [
|
|
"src/**/*.ts",
|
|
"src/**/*.vue"
|
|
],
|
|
"exclude": [
|
|
"dist",
|
|
"**/*.spec.ts"
|
|
]
|
|
}
|
|
```
|
|
|
|
Write this to `packages/library/tsconfig.json`
|
|
|
|
---
|
|
|
|
### Task 6: Create library vite.config.ts
|
|
|
|
**Files:**
|
|
- Create: `packages/library/vite.config.ts`
|
|
|
|
- [ ] **Step 1: Create Vite library config**
|
|
|
|
```typescript
|
|
import { fileURLToPath } from 'node:url'
|
|
import { defineConfig } from 'vite'
|
|
import vue from '@vitejs/plugin-vue'
|
|
|
|
export default defineConfig({
|
|
plugins: [vue()],
|
|
build: {
|
|
lib: {
|
|
entry: fileURLToPath(new URL('./src/index.ts', import.meta.url)),
|
|
name: 'NychthemeronLibrary',
|
|
formats: ['es'],
|
|
fileName: () => 'index.js',
|
|
},
|
|
rollupOptions: {
|
|
external: ['vue', 'primevue'],
|
|
output: {
|
|
globals: {
|
|
vue: 'Vue',
|
|
primevue: 'PrimeVue',
|
|
},
|
|
},
|
|
},
|
|
},
|
|
})
|
|
```
|
|
|
|
Write this to `packages/library/vite.config.ts`
|
|
|
|
---
|
|
|
|
### Task 7: Create library vitest.config.ts
|
|
|
|
**Files:**
|
|
- Create: `packages/library/vitest.config.ts`
|
|
|
|
- [ ] **Step 1: Create Vitest config**
|
|
|
|
```typescript
|
|
import { defineConfig } from 'vitest/config'
|
|
import vue from '@vitejs/plugin-vue'
|
|
|
|
export default defineConfig({
|
|
plugins: [vue()],
|
|
test: {
|
|
globals: true,
|
|
environment: 'jsdom',
|
|
include: ['**/*.spec.ts'],
|
|
},
|
|
})
|
|
```
|
|
|
|
Write this to `packages/library/vitest.config.ts`
|
|
|
|
---
|
|
|
|
### Task 8: Create library export structure
|
|
|
|
**Files:**
|
|
- Create: `packages/library/src/components/index.ts`
|
|
- Create: `packages/library/src/composables/index.ts`
|
|
- Modify: `packages/library/src/index.ts`
|
|
|
|
- [ ] **Step 1: Create components index (empty for now)**
|
|
|
|
```typescript
|
|
// Re-export all components here as they're added
|
|
// export { default as Button } from './Button/Button.vue'
|
|
// export { default as DatePicker } from './DatePicker/DatePicker.vue'
|
|
```
|
|
|
|
Write this to `packages/library/src/components/index.ts`
|
|
|
|
- [ ] **Step 2: Create composables index (empty for now)**
|
|
|
|
```typescript
|
|
// Re-export all composables here as they're added
|
|
// export { useDate } from './useDate'
|
|
```
|
|
|
|
Write this to `packages/library/src/composables/index.ts`
|
|
|
|
- [ ] **Step 3: Create root library entry point**
|
|
|
|
```typescript
|
|
export * from './components'
|
|
export * from './composables'
|
|
```
|
|
|
|
Write this to `packages/library/src/index.ts`
|
|
|
|
- [ ] **Step 4: Remove old App.vue and main.ts from library (they were for the app)**
|
|
|
|
```bash
|
|
rm packages/library/src/App.vue packages/library/src/main.ts
|
|
```
|
|
|
|
---
|
|
|
|
### Task 9: Create playground package.json
|
|
|
|
**Files:**
|
|
- Create: `packages/playground/package.json`
|
|
|
|
- [ ] **Step 1: Create playground package config**
|
|
|
|
```json
|
|
{
|
|
"name": "@nychthemeron/playground",
|
|
"version": "0.1.0",
|
|
"type": "module",
|
|
"scripts": {
|
|
"dev": "storybook dev -p 6006",
|
|
"build": "storybook build",
|
|
"preview": "npx vite preview"
|
|
},
|
|
"dependencies": {
|
|
"@nychthemeron/library": "workspace:*",
|
|
"primevue": "^18.0.0",
|
|
"vue": "^3.5.32"
|
|
},
|
|
"devDependencies": {
|
|
"@storybook/addon-essentials": "^8.0.0",
|
|
"@storybook/addon-interactions": "^8.0.0",
|
|
"@storybook/addon-links": "^8.0.0",
|
|
"@storybook/blocks": "^8.0.0",
|
|
"@storybook/vue3": "^8.0.0",
|
|
"@storybook/vue3-vite": "^8.0.0",
|
|
"storybook": "^8.0.0",
|
|
"tailwindcss": "^3.4.0",
|
|
"autoprefixer": "^10.4.0",
|
|
"postcss": "^8.4.0",
|
|
"typescript": "~6.0.0",
|
|
"vue": "^3.5.32"
|
|
}
|
|
}
|
|
```
|
|
|
|
Write this to `packages/playground/package.json`
|
|
|
|
---
|
|
|
|
### Task 10: Create playground tsconfig.json
|
|
|
|
**Files:**
|
|
- Create: `packages/playground/tsconfig.json`
|
|
|
|
- [ ] **Step 1: Create playground TypeScript config**
|
|
|
|
```json
|
|
{
|
|
"extends": "../../tsconfig.json",
|
|
"compilerOptions": {
|
|
"composite": false,
|
|
"jsx": "preserve",
|
|
"lib": [
|
|
"dom",
|
|
"dom.iterable",
|
|
"esnext"
|
|
]
|
|
},
|
|
"include": [
|
|
".storybook/**/*.ts",
|
|
".storybook/**/*.tsx",
|
|
".storybook/**/*.vue",
|
|
"stories/**/*.ts",
|
|
"stories/**/*.tsx",
|
|
"stories/**/*.vue"
|
|
]
|
|
}
|
|
```
|
|
|
|
Write this to `packages/playground/tsconfig.json`
|
|
|
|
---
|
|
|
|
### Task 11: Create playground Tailwind config
|
|
|
|
**Files:**
|
|
- Create: `packages/playground/tailwind.config.ts`
|
|
|
|
- [ ] **Step 1: Create Tailwind config**
|
|
|
|
```typescript
|
|
import type { Config } from 'tailwindcss'
|
|
|
|
export default {
|
|
content: [
|
|
'./.storybook/**/*.{ts,tsx,vue}',
|
|
'./stories/**/*.{ts,tsx,vue}',
|
|
'../library/src/**/*.vue',
|
|
],
|
|
theme: {
|
|
extend: {
|
|
colors: {
|
|
primary: {
|
|
50: '#f0f9ff',
|
|
500: '#0ea5e9',
|
|
900: '#0c2d6b',
|
|
},
|
|
},
|
|
},
|
|
},
|
|
plugins: [],
|
|
} satisfies Config
|
|
```
|
|
|
|
Write this to `packages/playground/tailwind.config.ts`
|
|
|
|
---
|
|
|
|
### Task 12: Create Storybook main config
|
|
|
|
**Files:**
|
|
- Create: `packages/playground/.storybook/main.ts`
|
|
|
|
- [ ] **Step 1: Create Storybook main configuration**
|
|
|
|
```typescript
|
|
import type { StorybookConfig } from '@storybook/vue3-vite'
|
|
|
|
const config: StorybookConfig = {
|
|
stories: [
|
|
'../stories/**/*.stories.ts',
|
|
],
|
|
addons: [
|
|
'@storybook/addon-essentials',
|
|
'@storybook/addon-interactions',
|
|
'@storybook/addon-links',
|
|
],
|
|
framework: {
|
|
name: '@storybook/vue3-vite',
|
|
options: {},
|
|
},
|
|
core: {
|
|
disableTelemetry: true,
|
|
},
|
|
}
|
|
|
|
export default config
|
|
```
|
|
|
|
Write this to `packages/playground/.storybook/main.ts`
|
|
|
|
---
|
|
|
|
### Task 13: Create Storybook preview config
|
|
|
|
**Files:**
|
|
- Create: `packages/playground/.storybook/preview.ts`
|
|
|
|
- [ ] **Step 1: Create Storybook preview configuration**
|
|
|
|
```typescript
|
|
import type { Preview } from '@storybook/vue3'
|
|
import 'primevue/resources/themes/lara-light-blue/theme.css'
|
|
import 'primevue/resources/primevue.min.css'
|
|
import '../tailwind.css'
|
|
|
|
const preview: Preview = {
|
|
parameters: {
|
|
layout: 'centered',
|
|
docs: {
|
|
description: {
|
|
component: 'Component documentation',
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
export default preview
|
|
```
|
|
|
|
Write this to `packages/playground/.storybook/preview.ts`
|
|
|
|
---
|
|
|
|
### Task 14: Create Tailwind CSS entry point
|
|
|
|
**Files:**
|
|
- Create: `packages/playground/tailwind.css`
|
|
|
|
- [ ] **Step 1: Create Tailwind directives file**
|
|
|
|
```css
|
|
@tailwind base;
|
|
@tailwind components;
|
|
@tailwind utilities;
|
|
```
|
|
|
|
Write this to `packages/playground/tailwind.css`
|
|
|
|
---
|
|
|
|
### Task 15: Create example story
|
|
|
|
**Files:**
|
|
- Create: `packages/playground/stories/Example.stories.ts`
|
|
|
|
- [ ] **Step 1: Create example story for testing**
|
|
|
|
```typescript
|
|
import type { Meta, StoryObj } from '@storybook/vue3'
|
|
|
|
const meta = {
|
|
title: 'Example/Welcome',
|
|
parameters: {
|
|
layout: 'centered',
|
|
},
|
|
tags: ['autodocs'],
|
|
} satisfies Meta
|
|
|
|
export default meta
|
|
type Story = StoryObj<typeof meta>
|
|
|
|
export const Welcome: Story = {
|
|
render: () => ({
|
|
template: `
|
|
<div class="p-8 text-center">
|
|
<h1 class="text-3xl font-bold text-primary-500 mb-4">
|
|
Nychthemeron Component Library
|
|
</h1>
|
|
<p class="text-gray-600">
|
|
Interactive playground for Vue components with PrimeVue + Tailwind
|
|
</p>
|
|
</div>
|
|
`,
|
|
}),
|
|
}
|
|
```
|
|
|
|
Write this to `packages/playground/stories/Example.stories.ts`
|
|
|
|
---
|
|
|
|
### Task 16: Install dependencies and verify structure
|
|
|
|
**Files:**
|
|
- None (verification only)
|
|
|
|
- [ ] **Step 1: Install dependencies for all packages**
|
|
|
|
```bash
|
|
bun install
|
|
```
|
|
|
|
Expected: All packages get dependencies installed, workspace links created
|
|
|
|
- [ ] **Step 2: Verify workspace structure**
|
|
|
|
```bash
|
|
ls -la packages/library/node_modules/@nychthemeron/library
|
|
# Expected: symlink to ../..
|
|
|
|
ls -la packages/playground/node_modules/@nychthemeron/library
|
|
# Expected: symlink to ../../../library
|
|
```
|
|
|
|
- [ ] **Step 3: Type-check across monorepo**
|
|
|
|
```bash
|
|
bun type-check
|
|
```
|
|
|
|
Expected: No TypeScript errors
|
|
|
|
---
|
|
|
|
### Task 17: Test dev server startup
|
|
|
|
**Files:**
|
|
- None (verification only)
|
|
|
|
- [ ] **Step 1: Start Storybook playground**
|
|
|
|
```bash
|
|
bun dev
|
|
```
|
|
|
|
Expected output:
|
|
```
|
|
Storybook 8.x started
|
|
Local: http://localhost:6006
|
|
```
|
|
|
|
- [ ] **Step 2: Verify Storybook loads in browser**
|
|
|
|
Open `http://localhost:6006` in a browser.
|
|
|
|
Expected: Storybook UI loads with "Example/Welcome" story visible showing the welcome message.
|
|
|
|
- [ ] **Step 3: Verify story renders correctly**
|
|
|
|
In Storybook, click "Example/Welcome" → verify the welcome message displays with correct styling.
|
|
|
|
Expected:
|
|
- Title: "Nychthemeron Component Library" (blue, large)
|
|
- Subtitle: "Interactive playground..." (gray, smaller)
|
|
|
|
- [ ] **Step 4: Stop dev server**
|
|
|
|
```bash
|
|
# Press Ctrl+C in terminal
|
|
```
|
|
|
|
---
|
|
|
|
### Task 18: Test library build
|
|
|
|
**Files:**
|
|
- None (verification only)
|
|
|
|
- [ ] **Step 1: Build library**
|
|
|
|
```bash
|
|
bun -r --cwd packages/library build
|
|
```
|
|
|
|
Expected output:
|
|
```
|
|
✓ 123 modules transformed
|
|
dist/index.js 1.23 kb
|
|
dist/index.d.ts 0.5 kb
|
|
```
|
|
|
|
- [ ] **Step 2: Verify dist files exist**
|
|
|
|
```bash
|
|
ls packages/library/dist/
|
|
```
|
|
|
|
Expected:
|
|
```
|
|
index.d.ts
|
|
index.js
|
|
```
|
|
|
|
- [ ] **Step 3: Verify exports are correct**
|
|
|
|
```bash
|
|
head -20 packages/library/dist/index.js
|
|
```
|
|
|
|
Expected: JavaScript code with ES module exports (starts with `import` or defines exports)
|
|
|
|
---
|
|
|
|
### Task 19: Initialize git for monorepo
|
|
|
|
**Files:**
|
|
- None (git initialization only)
|
|
|
|
- [ ] **Step 1: Initialize git repo (if not already)**
|
|
|
|
```bash
|
|
git init
|
|
```
|
|
|
|
- [ ] **Step 2: Add gitignore entries for workspace**
|
|
|
|
Append to `.gitignore`:
|
|
|
|
```
|
|
packages/*/dist
|
|
packages/*/node_modules
|
|
packages/*/.storybook-static
|
|
```
|
|
|
|
- [ ] **Step 3: Create initial commit (SKIP - user requested no commits)**
|
|
|
|
Note: Commit skipped per user request
|
|
|
|
---
|
|
|
|
## Self-Review
|
|
|
|
**Spec coverage:**
|
|
- ✅ Monorepo structure (Task 1)
|
|
- ✅ Bun workspaces setup (Tasks 2-3)
|
|
- ✅ Library package creation (Tasks 4-8)
|
|
- ✅ Composables structure (Task 8)
|
|
- ✅ Playground/Storybook setup (Tasks 9-15)
|
|
- ✅ Tailwind integration (Tasks 11, 14)
|
|
- ✅ PrimeVue initialization (Task 13)
|
|
- ✅ Dev workflow verification (Task 17)
|
|
- ✅ Build verification (Task 18)
|
|
- ✅ Git initialization (Task 19)
|
|
|
|
**Placeholder scan:** No TBDs, "fill in later", incomplete steps. All code blocks are complete and exact.
|
|
|
|
**Type consistency:** Package names consistent (`@nychthemeron/library`, `@nychthemeron/playground`). All import paths and config keys match across tasks.
|
|
|
|
**Scope:** Plan produces working, testable monorepo. Library builds to `dist/`, Storybook starts and loads stories. Next phase (adding components) is separate.
|