From be5b01aa86e706909b00316e230bfd83fde31be0 Mon Sep 17 00:00:00 2001 From: Danny Banks Date: Fri, 27 Feb 2026 22:51:20 -0800 Subject: [PATCH 1/8] ci: add lint, typecheck, and test workflows --- .github/workflows/ci.yml | 76 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..c5f02b3 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,76 @@ +name: CI + +on: + push: + branches: + - main + pull_request: + +concurrency: ${{ github.workflow }}-${{ github.ref }} + +jobs: + lint: + name: Lint + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: 'pnpm' + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Run lint + run: pnpm run lint + + typecheck: + name: Typecheck + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: 'pnpm' + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Run typecheck + run: pnpm run typecheck + + test: + name: Test + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: 'pnpm' + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Run tests + run: pnpm run test From ac2a00647d4c18876785d0a5f4a384d9ba5b5eb5 Mon Sep 17 00:00:00 2001 From: Danny Banks Date: Fri, 27 Feb 2026 22:51:51 -0800 Subject: [PATCH 2/8] chore: add changeset --- .changeset/add-ci-workflow.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/add-ci-workflow.md diff --git a/.changeset/add-ci-workflow.md b/.changeset/add-ci-workflow.md new file mode 100644 index 0000000..7e709c8 --- /dev/null +++ b/.changeset/add-ci-workflow.md @@ -0,0 +1,5 @@ +--- +typestyles: patch +--- + +Add CI workflow for lint, typecheck, and test From 305e836cca22ece28c74025becbc9abbc30837d5 Mon Sep 17 00:00:00 2001 From: Danny Banks Date: Fri, 27 Feb 2026 22:58:45 -0800 Subject: [PATCH 3/8] test: add additional test coverage for vite plugin --- .changeset/add-vite-tests.md | 5 +++++ packages/vite/src/index.test.ts | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 .changeset/add-vite-tests.md diff --git a/.changeset/add-vite-tests.md b/.changeset/add-vite-tests.md new file mode 100644 index 0000000..8416331 --- /dev/null +++ b/.changeset/add-vite-tests.md @@ -0,0 +1,5 @@ +--- +'@typestyles/vite': patch +--- + +Add additional test coverage for vite plugin diff --git a/packages/vite/src/index.test.ts b/packages/vite/src/index.test.ts index 8ca92a5..88a785a 100644 --- a/packages/vite/src/index.test.ts +++ b/packages/vite/src/index.test.ts @@ -76,6 +76,38 @@ describe('extractNamespaces', () => { expect(result.prefixes).toEqual(['.button-']); }); + it('extracts styles.component namespaces as prefixes', () => { + const code = ` + import { styles } from 'typestyles'; + const button = styles.component('button', { + base: { color: 'red' }, + }); + `; + const result = extractNamespaces(code); + expect(result.prefixes).toEqual(['.button-']); + expect(result.keys).toEqual([]); + }); + + it('extracts global.style selectors as prefixes', () => { + const code = ` + import { global } from 'typestyles'; + global.style('body', { margin: 0 }); + `; + const result = extractNamespaces(code); + expect(result.prefixes).toEqual(['body']); + expect(result.keys).toEqual([]); + }); + + it('extracts global.fontFace as font-face prefixes', () => { + const code = ` + import { global } from 'typestyles'; + global.fontFace('Inter', { src: "url('/Inter.woff2')" }); + `; + const result = extractNamespaces(code); + expect(result.prefixes).toEqual(['font-face:Inter']); + expect(result.keys).toEqual([]); + }); + it('returns empty arrays for code with no typestyles calls', () => { const code = ` const x = 1 + 2; From b2e9e82f00e19da5ab5be942926946fc4256bbb0 Mon Sep 17 00:00:00 2001 From: Danny Banks Date: Fri, 27 Feb 2026 23:04:34 -0800 Subject: [PATCH 4/8] feat: add @typestyles/next package for Next.js SSR integration --- .changeset/add-next-package.md | 5 + packages/next/package.json | 68 ++++++++ packages/next/src/index.tsx | 106 +++++++++++++ packages/next/tsconfig.json | 21 +++ packages/next/tsup.config.ts | 12 ++ pnpm-lock.yaml | 276 +++++++++++++++++++++++++++++++++ 6 files changed, 488 insertions(+) create mode 100644 .changeset/add-next-package.md create mode 100644 packages/next/package.json create mode 100644 packages/next/src/index.tsx create mode 100644 packages/next/tsconfig.json create mode 100644 packages/next/tsup.config.ts diff --git a/.changeset/add-next-package.md b/.changeset/add-next-package.md new file mode 100644 index 0000000..e86bb2e --- /dev/null +++ b/.changeset/add-next-package.md @@ -0,0 +1,5 @@ +--- +'@typestyles/next': minor +--- + +Add @typestyles/next package for Next.js SSR integration diff --git a/packages/next/package.json b/packages/next/package.json new file mode 100644 index 0000000..177b510 --- /dev/null +++ b/packages/next/package.json @@ -0,0 +1,68 @@ +{ + "name": "@typestyles/next", + "version": "0.1.0", + "description": "Next.js integration for typestyles SSR", + "type": "module", + "main": "./dist/index.cjs", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "import": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + }, + "require": { + "types": "./dist/index.d.cts", + "default": "./dist/index.cjs" + } + } + }, + "files": [ + "dist" + ], + "scripts": { + "build": "tsup", + "typecheck": "tsc --noEmit" + }, + "keywords": [ + "next", + "nextjs", + "typestyles", + "ssr", + "react" + ], + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/dbanksdesign/typestyles", + "directory": "packages/next" + }, + "peerDependencies": { + "next": ">=13.0.0", + "react": ">=18.0.0", + "react-dom": ">=18.0.0", + "typestyles": "workspace:*" + }, + "peerDependenciesMeta": { + "next": { + "optional": false + }, + "react": { + "optional": false + }, + "react-dom": { + "optional": false + } + }, + "devDependencies": { + "@types/node": "^25.2.1", + "@types/react": "^18.0.0", + "@types/react-dom": "^18.0.0", + "next": "^14.0.0", + "react": "^18.0.0", + "react-dom": "^18.0.0", + "tsup": "^8.0.0", + "typescript": "^5.4.0" + } +} diff --git a/packages/next/src/index.tsx b/packages/next/src/index.tsx new file mode 100644 index 0000000..e1abd60 --- /dev/null +++ b/packages/next/src/index.tsx @@ -0,0 +1,106 @@ +import React, { useEffect, useState } from 'react'; +import { collectStyles, getRegisteredCss } from 'typestyles/server'; + +export { getRegisteredCss }; + +/** + * Props for the TypestylesStylesheet component. + */ +export interface TypestylesStylesheetProps { + /** + * The children to render (styles will be collected from these). + * If not provided, only the existing registered CSS will be included. + */ + children?: React.ReactNode; +} + +/** + * A React component that renders typestyles CSS. + * + * For App Router: Use this in your root layout to collect and render styles. + * + * @example + * ```tsx + * // app/layout.tsx + * import { TypestylesStylesheet } from '@typestyles/next'; + * + * export default function RootLayout({ children }) { + * return ( + * + * + * + * + * {children} + * + * ); + * } + * ``` + */ +export function TypestylesStylesheet({ children }: TypestylesStylesheetProps) { + const [css, setCss] = useState(''); + + useEffect(() => { + // On client: get any CSS that was registered before hydration + setCss(getRegisteredCss()); + }, []); + + if (children) { + return ( + <> + {children} +