Skip to content

Commit af063d7

Browse files
committed
Create CLAUDE.md
1 parent f11641b commit af063d7

1 file changed

Lines changed: 335 additions & 0 deletions

File tree

CLAUDE.md

Lines changed: 335 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,335 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Project Overview
6+
7+
Strapi Webtools is a plugin for Strapi CMS v5 that provides URL management, routing, and an extensible addon system. It's a monorepo managed with Yarn workspaces and Turborepo, containing:
8+
9+
- **packages/core**: Main plugin (`strapi-plugin-webtools`)
10+
- **packages/cli**: Installation CLI tool (`webtools-cli`)
11+
- **packages/addons/sitemap**: Sitemap addon example
12+
- **packages/docs**: Documentation site
13+
- **playground**: Development Strapi instance for testing
14+
15+
## Development Setup
16+
17+
### Initial Setup
18+
19+
```bash
20+
# Install root dependencies
21+
yarn install
22+
23+
# Install playground dependencies (runs automatically via postinstall)
24+
yarn playground:install
25+
```
26+
27+
### Development Workflow
28+
29+
Development requires running **two terminal sessions**:
30+
31+
**Terminal 1 - Build the plugin in watch mode:**
32+
```bash
33+
yarn develop
34+
```
35+
This runs TypeScript compilation in watch mode across all packages and uses `yalc` to link them.
36+
37+
**Terminal 2 - Run the playground Strapi instance:**
38+
```bash
39+
yarn playground:develop
40+
```
41+
This starts Strapi at http://localhost:1337 with the plugin pre-installed.
42+
43+
Changes to the plugin code will rebuild automatically (Terminal 1) and hot-reload in the playground (Terminal 2).
44+
45+
### Building for Production
46+
47+
```bash
48+
yarn build
49+
```
50+
Builds all packages using Turborepo's caching and dependency graph.
51+
52+
## Testing
53+
54+
### Unit Tests
55+
56+
```bash
57+
# Run all unit tests
58+
yarn test:unit
59+
60+
# Run unit tests in watch mode (add to jest command in package.json)
61+
ENV_PATH=./playground/.env jest --watch
62+
```
63+
64+
Unit tests are located in `__tests__` directories:
65+
- `packages/core/server/middlewares/__tests__/`
66+
- `packages/core/server/hooks/__tests__/`
67+
- `packages/core/server/controllers/__tests__/`
68+
- `packages/addons/sitemap/server/utils/__tests__/`
69+
70+
Uses Jest with ts-jest preset. Test files: `*.test.ts` or `*.test.js`
71+
72+
### Integration Tests
73+
74+
```bash
75+
yarn test:integration
76+
```
77+
Runs healthcheck integration test against the playground instance.
78+
79+
### E2E Tests
80+
81+
```bash
82+
yarn test:e2e
83+
```
84+
Opens Cypress test runner. E2E tests have `.cy.ts` or `.cy.tsx` extensions.
85+
86+
## Code Quality
87+
88+
### Linting
89+
90+
```bash
91+
# Check all packages
92+
yarn eslint
93+
94+
# Auto-fix issues
95+
yarn eslint:fix
96+
97+
# Type checking (no emit)
98+
yarn tscheck
99+
```
100+
101+
Uses `@uncinc/eslint-config` with special overrides for Cypress and Jest files.
102+
103+
## Architecture Overview
104+
105+
### Core Plugin Structure
106+
107+
The plugin follows Strapi 5's plugin architecture with two entry points:
108+
109+
**Server-side** (`packages/core/server/index.ts`):
110+
- `register()`: Registers services, controllers, routes, content types
111+
- `bootstrap()`: Registers document middlewares and hooks
112+
- `contentTypes`: Defines `url-alias` and `url-pattern` internal content types
113+
- `routes`: Admin API and public Content API endpoints
114+
- `services`: Business logic (url-alias, url-pattern, info)
115+
- `middlewares`: Document lifecycle hooks for automatic URL generation
116+
- `controllers`: Request handlers for API endpoints
117+
118+
**Admin-side** (`packages/core/admin/index.ts`):
119+
- `register()`: Registers the plugin with Strapi Admin
120+
- `bootstrap()`: Injects UI into Content Manager and Content-Type Builder
121+
- `permissions`: RBAC permission definitions
122+
123+
### Key Architectural Concepts
124+
125+
#### 1. URL Aliases and Patterns
126+
127+
**URL Pattern** (`plugin::webtools.url-pattern`):
128+
- Template that defines how URLs are generated for a content type
129+
- Uses bracket syntax: `[fieldName]`, `[relation.field]`, `[pluralName]`, `[documentId]`
130+
- Example: `/blog/[category.slug]/[title]``/blog/news/hello-world`
131+
- Stored in database, managed via Admin UI
132+
133+
**URL Alias** (`plugin::webtools.url-alias`):
134+
- The actual generated URL path for an entry
135+
- Localized (supports i18n)
136+
- Tracks whether it was generated or manually set (`generated` field)
137+
- Automatically created/updated by middlewares
138+
139+
#### 2. Document Middleware Chain
140+
141+
Three middlewares hook into the Strapi 5 document lifecycle:
142+
143+
1. **generate-url-alias.ts**: Runs on create/update/clone
144+
- Fetches URL patterns for the content type
145+
- Resolves pattern templates using entry data
146+
- Creates/updates URL alias records
147+
- Respects manual URLs (`generated: false`)
148+
149+
2. **prevent-duplicate-urls.ts**: Ensures URL uniqueness
150+
- Appends numeric suffixes when conflicts exist (-1, -2, etc.)
151+
152+
3. **delete-url-alias.ts**: Cleans up on entry deletion
153+
154+
These run BEFORE/AFTER document operations using Strapi's document middleware API (not legacy entity service).
155+
156+
#### 3. Content Type Enablement
157+
158+
Content types opt into Webtools via `pluginOptions`:
159+
160+
```javascript
161+
{
162+
"pluginOptions": {
163+
"webtools": { "enabled": true }
164+
}
165+
}
166+
```
167+
168+
When enabled:
169+
- A `url_alias` relation field is injected at bootstrap
170+
- Middlewares activate for that content type
171+
- Admin UI shows the Webtools side panel in the content editor
172+
173+
#### 4. Addon System
174+
175+
Addons are Strapi plugins with a special flag in their `package.json`:
176+
177+
```json
178+
{
179+
"strapi": {
180+
"webtoolsAddon": true
181+
}
182+
}
183+
```
184+
185+
**Discovery**: Core scans `enabledPlugins` at runtime for this flag
186+
187+
**Integration**: Addons inject components via named zones:
188+
- `webtoolsRouter`: Adds routes to main navigation
189+
- `webtoolsSidePanel`: Adds components to content editor sidebar
190+
191+
**Implementation**: Addons call:
192+
```typescript
193+
app.getPlugin('webtools').injectComponent(zone, type, { Component, ... })
194+
```
195+
196+
Example: The Sitemap addon extends enabled content types with a `sitemap_exclude` field and adds UI to manage sitemaps.
197+
198+
#### 5. Services
199+
200+
Key services accessible via `getPluginService()`:
201+
202+
**url-alias**:
203+
- `findByPath(path, locale?)`: Find entries by URL path
204+
- `findRelatedEntity(path, locale?)`: Resolve URL to entity
205+
- `makeUniquePath(uid, path, locale?, excludeId?)`: Ensure uniqueness
206+
207+
**url-pattern**:
208+
- `resolvePattern(uid, entity, locale?)`: Convert pattern to path
209+
- `validatePattern(uid, pattern)`: Check pattern syntax
210+
- `getAllowedFields(uid)`: List fields available in patterns
211+
- `getFieldsFromPattern(pattern)`: Extract field references
212+
- `getRelationsFromPattern(uid, pattern)`: Get relations to populate
213+
214+
#### 6. Frontend Router
215+
216+
The `/api/webtools/router` endpoint enables frontend routing:
217+
218+
```
219+
GET /api/webtools/router?path=/blog/hello-world
220+
```
221+
222+
Returns:
223+
- The content entity
224+
- Content type UID
225+
- Applies full permission checks
226+
- Optionally executes Strapi controllers (if `router_use_controllers: true`)
227+
228+
### Admin UI Structure
229+
230+
**Main Routes**:
231+
- `/` - Overview page
232+
- `/urls` - List/edit all URL aliases
233+
- `/patterns` - Manage URL patterns
234+
235+
**Injection Zones**:
236+
- Addons can add navigation items (webtoolsRouter)
237+
- Addons can add sidebar components (webtoolsSidePanel)
238+
239+
**Content Manager Integration**:
240+
- `WebtoolsPanel` appears in Content Manager's edit view for enabled types
241+
- Shows current URL alias, edit form, and injected addon components
242+
243+
## Configuration
244+
245+
Plugin options in `config/plugins.js`:
246+
247+
```javascript
248+
module.exports = {
249+
webtools: {
250+
config: {
251+
default_pattern: '/[pluralName]/[documentId]',
252+
unique_per_locale: true,
253+
router_use_controllers: false,
254+
slugify: (text) => text.toLowerCase().replace(/\s+/g, '-'),
255+
website_url: 'https://example.com',
256+
},
257+
},
258+
};
259+
```
260+
261+
## Common Patterns
262+
263+
### Adding a New Service Method
264+
265+
1. Add method to service interface in `server/services/[service-name].ts`
266+
2. Export service in `server/services/index.ts`
267+
3. Access via `getPluginService('[service-name]')` with full type safety
268+
269+
### Creating a New Middleware
270+
271+
1. Add file to `server/middlewares/`
272+
2. Export from `server/middlewares/index.ts`
273+
3. Register in `bootstrap()` using `strapi.documents.use(middleware)`
274+
275+
### Adding Admin UI Screens
276+
277+
1. Create screen component in `admin/screens/[ScreenName]/`
278+
2. Add route to `containers/App/index.tsx`
279+
3. Add permission check if needed
280+
4. Add navigation link in sidebar configuration
281+
282+
### Creating an Addon
283+
284+
See `packages/addons/sitemap` as reference:
285+
286+
1. Create Strapi plugin with `strapi.webtoolsAddon: true` in package.json
287+
2. Add `strapi-plugin-webtools` as peerDependency
288+
3. In `admin/index.js` bootstrap, call injection APIs:
289+
```typescript
290+
const webtoolsPlugin = app.getPlugin('webtools');
291+
webtoolsPlugin.injectComponent('webtoolsRouter', 'route', {
292+
path: '/sitemap',
293+
label: 'Sitemap',
294+
Component: SitemapPage,
295+
});
296+
```
297+
4. Optionally extend content types in server `bootstrap()`
298+
299+
## Release Process
300+
301+
This project uses Changesets for version management:
302+
303+
```bash
304+
# After making changes, create a changeset
305+
npx changeset
306+
307+
# Prepare release (bump versions, update CHANGELOGs)
308+
yarn release:prepare
309+
310+
# Publish to npm
311+
yarn release:publish
312+
```
313+
314+
## Troubleshooting
315+
316+
### Playground not seeing plugin changes
317+
318+
1. Ensure `yarn develop` is running (builds plugin in watch mode)
319+
2. Check that yalc linked correctly: `cd playground && yalc check`
320+
3. Rebuild admin: `cd playground && yarn build`
321+
4. Clear Strapi cache: `rm -rf playground/node_modules/.strapi/`
322+
323+
### TypeScript errors in IDE but builds succeed
324+
325+
Run `yarn tscheck` to see actual type errors. The playground has a separate tsconfig that may cause confusion.
326+
327+
### Tests fail with "Cannot find module"
328+
329+
Ensure `ENV_PATH=./playground/.env` is set when running tests. The tests need access to the playground's Strapi configuration.
330+
331+
## Documentation
332+
333+
- Main docs: https://docs.pluginpal.io/webtools
334+
- Contributing guide: CONTRIBUTING.md
335+
- Strapi plugin development: https://strapi.io/documentation/developer-docs/latest/plugin-development/

0 commit comments

Comments
 (0)