From 4ebc7bcb283fa8b8bc134ba61342f5b11f3d32df Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 8 Aug 2026 12:08:53 +0000 Subject: [PATCH 1/2] docs(create-plugin): shrink the page to what does not drift, point at buildPluginFiles() (#3715) The page described a scaffolder that does not exist: it claimed the CLI installs dependencies and inits a git repo (it does neither), listed six prompts (there are two after the name), put the output in the current directory (it writes packages/plugin-NAME), told readers to run `npm run dev` against a build.lib config that has no dev server, and documented a whole `.create-plugin.config.js` configuration surface the generator never reads. Every one of those distortions came from transcribing generator output into prose, so per the triage ruling (route B) the transcriptions are deleted rather than rewritten: the generated tree, the four generated-file samples and the CLI's own dependency list are replaced with a pointer at buildPluginFiles() in packages/create-plugin/src/templates.ts, which is the single source of truth for what a scaffolded plugin contains. What survives is the invocation surface (install, name rules, where it writes, how to iterate, how to publish), plus the structural facts a reader needs and that cannot go stale on their own. No version literal is restated anywhere on the page, so doc-version-claims' KNOWN_CLAIMS needs no row deleted (it had none for this file). --- content/docs/utilities/create-plugin.mdx | 437 +++++++---------------- 1 file changed, 122 insertions(+), 315 deletions(-) diff --git a/content/docs/utilities/create-plugin.mdx b/content/docs/utilities/create-plugin.mdx index 23dd80a5fa..6e4ee31319 100644 --- a/content/docs/utilities/create-plugin.mdx +++ b/content/docs/utilities/create-plugin.mdx @@ -5,7 +5,10 @@ description: "Interactive CLI tool for creating ObjectUI plugins" # Create Plugin -The `@object-ui/create-plugin` package is an interactive CLI tool that scaffolds new ObjectUI plugins with best practices and TypeScript support. It generates a complete plugin structure ready for development. +The `@object-ui/create-plugin` package is a small interactive CLI that scaffolds a new +ObjectUI plugin package. It asks a couple of questions, writes a ready-to-build package, +and prints the commands to run next. Writing those files is the whole of what it does: it +installs no dependencies and initialises no git repository. ## Installation @@ -13,301 +16,123 @@ The `@object-ui/create-plugin` package is an interactive CLI tool that scaffolds npm install -g @object-ui/create-plugin ``` -Or use with npx (recommended): +Or use with npx (recommended — nothing to install, and you always get the published +version): ```bash npx @object-ui/create-plugin my-plugin ``` -## Features - -- 🎯 **Interactive Prompts** - Step-by-step plugin creation -- 📦 **Complete Scaffold** - All files needed to start -- 🔷 **TypeScript First** - Full TypeScript support -- 🎨 **Best Practices** - Follow ObjectUI conventions -- 🚀 **Ready to Develop** - Start coding immediately -- 📝 **Documentation Template** - Includes README and docs - ## Quick Start -### Create a New Plugin +**Run it from the root of a pnpm workspace.** The new package is written to +`packages/plugin-` **relative to the current directory**, and what it writes is +built for a workspace: it depends on the `@object-ui` packages through `workspace:*` +links, and its `tsconfig.json` extends the one two directories above it. Run the +generator anywhere else and you get a package whose dependencies and TypeScript config +resolve to nothing. ```bash -npx @object-ui/create-plugin my-awesome-plugin -``` - -This will: - -1. Ask you a few questions about your plugin -2. Generate the plugin structure -3. Install dependencies -4. Initialize a git repository - -### Interactive Prompts - -When you run `create-plugin`, you'll be asked: - -``` -? Plugin name: my-awesome-plugin -? Description: An awesome plugin for ObjectUI -? Author: Your Name -? Component name: AwesomeComponent -? Component type: awesome-component -? License: MIT -``` - -## Generated Structure - -The tool creates a complete plugin structure: - -``` -my-awesome-plugin/ -├── src/ -│ ├── index.tsx # Main entry point -│ ├── AwesomeComponent.tsx # Component implementation -│ └── types.ts # TypeScript types -├── package.json # Package configuration -├── tsconfig.json # TypeScript config -├── vite.config.ts # Vite build config -├── README.md # Documentation -└── .gitignore # Git ignore rules -``` - -## Generated Files - -### `src/index.tsx` - -The main entry point that registers your component: - -```typescript -import { ComponentRegistry } from '@object-ui/core' -import { AwesomeComponent } from './AwesomeComponent' - -// Auto-register the component -ComponentRegistry.register('awesome-component', AwesomeComponent) - -// Export for direct use -export { AwesomeComponent } -export * from './types' -``` - -### `src/AwesomeComponent.tsx` - -The component implementation with lazy loading: - -```typescript -import React, { Suspense } from 'react' -import { Skeleton } from '@object-ui/components' -import type { AwesomeComponentProps } from './types' - -// Lazy load heavy implementation -const AwesomeImpl = React.lazy(() => import('./AwesomeImpl')) - -export const AwesomeComponent: React.FC = (props) => { - return ( - }> - - - ) -} -``` - -### `src/types.ts` - -TypeScript type definitions: - -```typescript -import type { BaseComponentSchema } from '@object-ui/types' - -export interface AwesomeComponentSchema extends BaseComponentSchema { - type: 'awesome-component' - // Your component-specific props - message?: string - color?: string -} - -export interface AwesomeComponentProps { - schema: AwesomeComponentSchema -} -``` - -### `package.json` - -Pre-configured with all necessary settings: the package identity (`name`, `version`, -`type`, `license`, `description`), the build entry points (`main`, `module`, `types` and -an `exports` map covering both the ES and the UMD bundle), and its `scripts`. - -It also writes three dependency groups: - -- `dependencies` — four `@object-ui` workspace packages plus an icon library, written as - workspace links rather than as published version ranges. Note the field: the generator - puts these under **`dependencies`**, not under `peerDependencies`. -- `peerDependencies` — `react` and `react-dom`, left for the host application to supply. -- `devDependencies` — what the generated Vite build and its tests need. - -Do not transcribe version ranges out of this page into a hand-written manifest. They are -written by one template — the manifest literal in -[`packages/create-plugin/src/index.ts`](https://github.com/objectstack-ai/objectui/blob/main/packages/create-plugin/src/index.ts) — -and reading them there is the only way to see what your scaffold will actually contain. +cd my-workspace +npx @object-ui/create-plugin awesome +``` + +The CLI validates the name you gave it — lowercase letters, digits and hyphens only, no +scopes and no path separators — and then cleans it up: a leading `plugin-` is stripped, so +`awesome` and `plugin-awesome` mean the same thing. From the cleaned name it derives, +without asking: + +- the directory it writes into, `packages/plugin-`; +- the package name, `@object-ui/plugin-`; +- the `type` key the plugin registers itself under in the + [Component Registry](/docs/guide/component-registry) — the cleaned name itself, so + `awesome` in the example above; +- the PascalCase component name, and the name of the file implementing it. + +One name decides all four, so pick it with all four in mind. Everything else — a +description and an author — is asked interactively with a default filled in. The +`--description` and `--author` options **pre-fill those prompts rather than skip them**; +run `npx @object-ui/create-plugin --help` for the options it accepts today. + +## What It Generates + +This page deliberately does **not** reproduce the generated tree or the generated files. +Run the generator and read what it wrote — and when you need the answer without running +it, read the one function that decides it: +[`buildPluginFiles()` in `packages/create-plugin/src/templates.ts`](https://github.com/objectstack-ai/objectui/blob/main/packages/create-plugin/src/templates.ts). +That map of *path to file contents* is the single source of truth for what a scaffolded +plugin contains; the CLI in `src/index.ts` is the loop that writes it to disk. + +Two things are worth knowing before you look: + +- **It scaffolds a library, not an app.** The generated Vite config is a `build.lib` + config emitting an ES and a UMD bundle into `dist/`. There is no application to serve, + so there is no dev server to start — see [Development Workflow](#development-workflow). +- **Its dependency ranges are not listed here on purpose.** They live in that one + template literal, and the testing ones are asserted to equal this repository's own + ranges by `packages/create-plugin/src/__tests__/templates.test.ts`. Reading the literal + is the only way to see what your scaffold will actually contain; a range copied onto + this page would be the first thing to go stale. ## Development Workflow -After creating your plugin: - -### 1. Install Dependencies - -```bash -cd my-awesome-plugin -npm install -``` - -### 2. Start Development - -```bash -npm run dev -``` - -This starts Vite in dev mode with HMR. - -### 3. Implement Your Component - -Edit `src/AwesomeImpl.tsx`: - -```typescript -import React from 'react' -import type { AwesomeComponentProps } from './types' - -const AwesomeImpl: React.FC = ({ schema }) => { - const { message = 'Hello!', color = 'blue' } = schema - - return ( -
-

{message}

-
- ) -} - -export default AwesomeImpl -``` - -### 4. Add Tests - -Create `src/AwesomeComponent.test.tsx`: - -```typescript -import { describe, it, expect } from 'vitest' -import { render, screen } from '@testing-library/react' -import { AwesomeComponent } from './AwesomeComponent' - -describe('AwesomeComponent', () => { - it('renders message', () => { - const schema = { - type: 'awesome-component' as const, - message: 'Test message', - } - - render() - expect(screen.getByText('Test message')).toBeInTheDocument() - }) -}) -``` - -### 5. Build for Production - -```bash -npm run build -``` - -This creates optimized bundles in the `dist/` folder. - -### 6. Test in Your App - -Link your plugin for local testing: - -```bash -# In plugin directory -npm link - -# In your app directory -npm link @object-ui/plugin-awesome -``` - -### 7. Publish to NPM - -```bash -npm publish -``` - -## Configuration Options - -### Custom Templates - -You can customize the scaffolding by creating a `.create-plugin.config.js`: - -```javascript -// .create-plugin.config.js -module.exports = { - template: 'custom', - defaults: { - author: 'Your Name', - license: 'MIT', - }, - prompts: { - // Custom prompts - }, -} -``` - -### Plugin Naming Conventions - -Follow these naming conventions: - -- **Package name:** `@object-ui/plugin-` or `@yourorg/objectui-plugin-` -- **Component type:** `kebab-case` (e.g., `awesome-component`) -- **Component name:** `PascalCase` (e.g., `AwesomeComponent`) +When the generator finishes it prints the next steps for the package it just wrote — +follow those. Inside the new package, `pnpm run` lists the scripts it actually has. Two +notes on what to expect from them: + +- **There is no dev server, and no `dev` script to run one.** A library build has no app + to serve. Iterate either by building the plugin and reloading the app that consumes it, + or — the way this repository develops its own plugins — by pointing the consuming app's + Vite `resolve.alias` at your plugin's `src`, so your sources become part of that app's + dev server. The [Runner](/docs/utilities/runner) documents that alias table, including + the transitive-closure rule that makes it work. +- **Tests are green on a fresh scaffold, and are meant to stay that way.** The generator + writes an example test together with the Vitest setup file its config points at, and + `templates.test.ts` pins those pieces to each other: the test stack is declared, the + environment is a DOM, the setup file exists. If the first test run in a freshly + scaffolded plugin is red, that is a bug in the generator rather than in your machine — + please file it. + +To use the plugin from an app in the same workspace, add it as a `workspace:*` dependency +and import the package for its side effect: importing it is what registers the component, +which is what makes its `type` key usable in a schema. See +[Plugin Concepts](/docs/guide/plugins). + +To publish it, use `pnpm publish`. pnpm rewrites the `workspace:*` links into real ranges +as it packs; a plain `npm publish` would ship the literal `workspace:*` and the resulting +tarball is uninstallable. + +## Configuration + +There is none beyond the command line. The generator reads no configuration file — there +is no `.create-plugin.config.js` and no template directory to point it at. Everything it +can be told, it is told through the plugin-name argument and the options `--help` lists. + +## Plugin Naming Conventions + +- **Package name:** `@object-ui/plugin-` — the CLI composes it, you only choose + ``. +- **Schema `type` key:** `kebab-case`, the cleaned plugin name. +- **Component name:** `PascalCase`, derived from the same name. ## Best Practices -### 1. Lazy Loading - -Always use lazy loading for heavy dependencies: - -```typescript -const HeavyImpl = React.lazy(() => import('./HeavyImpl')) -``` - -### 2. Type Safety - -Export all TypeScript types: - -```typescript -export type { AwesomeComponentSchema, AwesomeComponentProps } -``` - -### 3. Loading States - -Provide meaningful loading states: - -```typescript -}> - - -``` - -### 4. Documentation - -Include comprehensive documentation: +Advice for the plugin you are about to write, beyond whatever the scaffold hands you: -- README with usage examples -- JSDoc comments for all exports +- **Lazy-load heavy dependencies.** `@object-ui/plugin-*` is the layer where heavy + third-party libraries are allowed to live, which makes it the layer that most needs a + `React.lazy` boundary, so an app that never renders your component never pays for it: -### 5. Testing + ```tsx + const HeavyImpl = React.lazy(() => import('./HeavyImpl')); + ``` -Write tests for all functionality: - -- Unit tests for components -- Integration tests for schemas -- E2E tests for user flows +- **Give that boundary a real fallback.** A `Skeleton` shaped like the component beats a + bare spinner: the layout does not jump when the chunk arrives. +- **Export your schema types.** The schema interface is the contract between a metadata + author and your renderer, so make it importable rather than internal. +- **Treat the registry key as public.** The `type` you register appears in every schema + that uses your plugin, so renaming it is a breaking change for metadata already stored. ## Example Plugins @@ -319,55 +144,37 @@ See these official plugins for reference: ## Package Information -**Package Name:** `@object-ui/create-plugin` -**Version:** 0.3.1 +**Package Name:** `@object-ui/create-plugin` — published on npm, see the +[npm page](https://www.npmjs.com/package/@object-ui/create-plugin) for the current version **Binary:** `create-plugin` **License:** MIT -## Dependencies - -The tool uses: - -- **Inquirer** - Interactive prompts -- **Chalk** - Terminal colors -- **Ora** - Loading spinners -- **fs-extra** - File system utilities - -## Next Steps - -- **[Plugin Concepts](/docs/guide/plugins)** - Learn how plugins work -- **[CLI](/docs/utilities/cli)** - Test your plugin with the CLI -- **[Component Registry](/docs/guide/component-registry)** - Register components -- **[Plugin Development Guide](/docs/guide/plugins)** - Learn plugin best practices - ## Troubleshooting -### Permission Denied - -```bash -# On Linux/Mac, use sudo -sudo npm install -g @object-ui/create-plugin -``` +**It stopped because the directory already exists.** The generator never writes into an +existing directory. Pick another name, or remove the old package first. -### Template Not Found +**The name was rejected.** Only lowercase letters, digits and hyphens are accepted; a +scope, a slash or a `..` is refused. The validation is at the top of +[`src/index.ts`](https://github.com/objectstack-ai/objectui/blob/main/packages/create-plugin/src/index.ts). -Make sure you have the latest version: - -```bash -npm update -g @object-ui/create-plugin -``` +**The new package's imports and `tsconfig.json` resolve to nothing.** The generator was +almost certainly run outside a workspace root — see [Quick Start](#quick-start). Move the +package into a real workspace's `packages/` directory, or re-run the generator from +there. -### Dependencies Not Installing +**Permission denied installing globally.** Use npx instead of `npm install -g`; it needs +no write access outside the npm cache. -Try clearing npm cache: +## Next Steps -```bash -npm cache clean --force -npx @object-ui/create-plugin my-plugin -``` +- **[Plugin Concepts](/docs/guide/plugins)** - Learn how plugins work +- **[Plugin Development Guide](/docs/guide/plugin-development)** - The full authoring guide +- **[Component Registry](/docs/guide/component-registry)** - Register components +- **[CLI](/docs/utilities/cli)** - Test your plugin with the CLI ## Need Help? - [GitHub Issues](https://github.com/objectstack-ai/objectui/issues) -- [Plugin Development Guide](/docs/guide/plugins) +- [Plugin Development Guide](/docs/guide/plugin-development) - [Examples](https://github.com/objectstack-ai/objectui/tree/main/packages) From b8b219bac4d46b4fecb1498f820eb1d820ec17e4 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 8 Aug 2026 12:11:54 +0000 Subject: [PATCH 2/2] docs(create-plugin): widen the parity-gate sentence to match #3742 landing (#3715) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR #3754 (#3742) landed while this branch was open and turned templates.test.ts from "the three testing ranges are sourced from the repo" into "every generated devDependency range is anchored, none unpinned". The page said "the testing ones", which was accurate at the fetch point and understated after that merge — exactly the drift this shrink exists to avoid, so the sentence now names the anchoring without naming a subset or a count. --- content/docs/utilities/create-plugin.mdx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/content/docs/utilities/create-plugin.mdx b/content/docs/utilities/create-plugin.mdx index 6e4ee31319..2acbafb6fa 100644 --- a/content/docs/utilities/create-plugin.mdx +++ b/content/docs/utilities/create-plugin.mdx @@ -69,10 +69,11 @@ Two things are worth knowing before you look: config emitting an ES and a UMD bundle into `dist/`. There is no application to serve, so there is no dev server to start — see [Development Workflow](#development-workflow). - **Its dependency ranges are not listed here on purpose.** They live in that one - template literal, and the testing ones are asserted to equal this repository's own - ranges by `packages/create-plugin/src/__tests__/templates.test.ts`. Reading the literal - is the only way to see what your scaffold will actually contain; a range copied onto - this page would be the first thing to go stale. + template literal, and `packages/create-plugin/src/__tests__/templates.test.ts` anchors + the generated `devDependencies` to this repository's own declarations, so the scaffold + cannot drift away from the toolchain it is part of. Reading the literal is the only way + to see what your scaffold will actually contain; a range copied onto this page would be + the first thing to go stale. ## Development Workflow