Files
workspace/code/g3soft-libs/CODEBUDDY.md
T
2026-10-08 11:34:12 +08:00

7.4 KiB

CODEBUDDY.md

This file provides guidance to CodeBuddy Code when working with code in this repository.

Overview

G3Soft frontend infrastructure — a pnpm monorepo holding a self-built Vue 3 component library plus a Docus documentation site.

  • packages/ui (@g3soft/ui) — component library, published to a private npm registry. One directory per component under src/<name>/.
  • packages/tokens (@g3soft/tokens) — design tokens as plain CSS (--g3-* variables), shared by the library and the docs site.
  • docs (@g3soft/docs) — Docus/Nuxt docs site. private: true, never published.
  • scripts/gen-api.mjs — generates component API data from source.

Requires Node >= 20.19 and pnpm 11 (see packageManager in root package.json).

Commands

pnpm install

pnpm dev            # start docs site (http://localhost:3000); live-reloads packages/ui via alias
pnpm build          # build all packages under packages/** (component lib -> dist)
pnpm build:docs     # build docs static site -> docs/.output/public
pnpm preview:docs   # preview the built docs site

pnpm gen:api        # regenerate docs/data/api/*.json from component source (REQUIRED after changing props/events/slots)

Deploying docs to a subpath:

NUXT_APP_BASE_URL=/docs/ pnpm build:docs

Releasing the component library (changesets):

pnpm changeset      # record the change
pnpm version        # bump versions + generate CHANGELOG
pnpm release        # build + publish to the private registry

packages/ui runs publint via prepublishOnly to validate package structure (exports/files consistency).

Testing: there is no test runner configured in this repo (no vitest/jest, no test files). Do not assume pnpm test exists.

Running a single component's checks

There is no per-component test/lint script. The closest equivalent is pnpm --filter @g3soft/ui build (runs vite build, which type-checks declarations via vite-plugin-dts), and pnpm gen:api to verify the docgen extraction still parses a component.

Architecture

Two independent pipelines

The docs site and the component library are deliberately decoupled:

Docs site Component package
Published No (private: true) Yes (private npm registry)
Versioning None changesets (ignore: ["@g3soft/docs"])
Build pnpm build:docs pnpm build
Output docs/.output/public (static) packages/ui/dist (ESM + d.ts)
Dep boundary Docus/Nuxt/Tailwind live only in the docs package only vue (peer) + @g3soft/tokens

Nuxt-related deps appear only in docs/package.json; the library's dependency tree has none of it. changesets' ignore keeps the docs site out of version tags.

Component library build (packages/ui)

vite.config.ts builds ESM + type declarations. Critical points:

  • vue is marked external — never bundle Vue, or consumers get duplicate instances.
  • preserveModules keeps the src structure so consumers can tree-shake.
  • vite-plugin-dts with rollupTypes emits a single dist/index.d.ts, matched by exports.types.
  • src/index.ts is the public entry: it re-exports every component plus its types, and exports the optional G3UI install plugin for whole-library registration.

Docs site (docs)

  • extends: ['docus'] — all navigation/search/theme comes from the Docus Nuxt Layer.
  • vite.resolve.alias points @g3soft/ui and @g3soft/tokens directly at packages/*/src, so the docs site can develop against the library without building it. Editing packages/ui hot-reloads the docs immediately.
  • site.url is required (sitemap, llms.txt, OG images) — missing it breaks prerender with a 500.
  • Deploy base path comes from NUXT_APP_BASE_URL (Nuxt's env override for app.baseURL), never hardcoded.
  • docs/app/components/content/ holds components usable directly inside Markdown: Demo.vue (renders a runnable example + source) and ApiTable.vue (renders the generated API tables).

API generation pipeline

scripts/gen-api.mjs uses vue-docgen-api to parse every .vue under packages/ui/src, extracts props/events/slots and their JSDoc comments, and writes docs/data/api/<dirName>.json. ApiTable.vue reads those JSON files by component name.

Consequence: API tables are generated from source and must never be hand-written. The component directory name is the doc key (src/button/Button.vue → button). After any change to a component's props/events/slots, run pnpm gen:api.

Design tokens (packages/tokens)

  • src/index.css imports tokens.css (light/default) and dark.css. Consumed as import '@g3soft/tokens'.
  • Dark theme activates via html[data-g3-theme='dark'] (document.documentElement.dataset.g3Theme = 'dark').
  • Re-theming/customer customization = overriding same-named --g3-* variables, no component changes.

Windows: dev-server port selection

docs/package.json runs node scripts/dev.mjs for dev (not nuxt dev directly). On Windows, Node sets SO_REUSEADDR, so two processes can bind the same host:port. Nuxt's built-in "is the port free?" check works by trying to bind a probe socket — which therefore succeeds even when another service already listens on 3000, so pnpm dev silently lands on 3000 (no 3000→3001 fallback). scripts/dev.mjs sidesteps this by testing the port with a real TCP connection (connect to 127.0.0.1/::1) and walking upward from DOCS_PORT/PORT/3000 until it finds a free one, then passing --port. devServer.host: '' (empty string — deliberately not '0.0.0.0') makes listhen listen on all interfaces and print a usable Local: http://localhost:<port>/ plus the LAN Network: URL; with '0.0.0.0' the Local line would be an unopenable http://0.0.0.0:<port>/.

Conventions (must follow)

  • No scoped in component <style>. Isolate with the g3- prefix + BEM naming so consumers can override.
  • Never hardcode colors or sizes — always reference --g3-* variables, and give every variable a fallback value (e.g. var(--g3-color-primary, #1677ff)) so components render correctly even when @g3soft/tokens is not loaded.
  • Component API docs are generated, not authored — changes to props/events/slots require pnpm gen:api.
  • Examples live at docs/examples/<component>/<example>.vue and are referenced in Markdown as ::demo{name="<component>/<example>"}. API tables use <ApiTable name="<component>" />.
  • New components get a src/<name>/ directory (component .vue, types.ts, index.ts) and must be re-exported from src/index.ts.

Version constraints (do not relax)

pnpm-workspace.yaml documents these deliberately — read it before touching dependency versions:

  • nuxt is pinned to exactly 4.4.8 in docs/package.json. Do not widen to ^4.4.8 or upgrade to 4.5+. Nuxt 4.5+ pulls Vite 8 (Rolldown), which cannot resolve Nitro's file:///D:/... virtual modules on Windows, breaking #nitro-internal-virtual/* and causing every page to 500 (Either manifest or precomputed data must be provided). 4.4.8 → Vite 7 (Rollup) and matches Docus's official starter.
  • pnpm 11 uses the allowBuilds map form in pnpm-workspace.yaml — the v10 onlyBuiltDependencies array form is inert. better-sqlite3 (Docus FTS5 search native module), vue-demi, and esbuild must stay allowed.
  • vue and typescript versions are centralized in the workspace catalog:; each package references "vue": "catalog:" to avoid version drift.