@djangocfg/ext-base 1.0.2 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +186 -7
- package/dist/api.cjs +40 -0
- package/dist/api.d.cts +35 -0
- package/dist/api.d.ts +35 -0
- package/dist/api.js +2 -0
- package/dist/auth.cjs +10 -0
- package/dist/auth.d.cts +1 -0
- package/dist/auth.d.ts +1 -0
- package/dist/auth.js +2 -0
- package/dist/chunk-3RG5ZIWI.js +8 -0
- package/dist/chunk-UXTBBEO5.js +237 -0
- package/dist/chunk-VJEGYVBV.js +140 -0
- package/dist/cli.mjs +530 -0
- package/dist/hooks.cjs +437 -0
- package/dist/hooks.d.cts +97 -0
- package/dist/hooks.d.ts +97 -0
- package/dist/hooks.js +95 -0
- package/dist/index.cjs +345 -0
- package/dist/index.d.cts +363 -0
- package/dist/index.d.ts +363 -0
- package/dist/index.js +3 -0
- package/package.json +5 -2
- package/src/cli/index.ts +470 -35
- package/src/context/ExtensionProvider.tsx +67 -4
- package/src/extensionConfig.ts +114 -0
- package/src/index.ts +3 -0
- package/src/metadata.ts +1 -2
- package/src/types/context.ts +21 -15
- package/src/utils/createExtensionConfig.ts +34 -18
- package/templates/extension-template/README.md.template +37 -5
- package/templates/extension-template/package.json.template +13 -5
- package/templates/extension-template/playground/.gitignore.template +34 -0
- package/templates/extension-template/playground/CLAUDE.md +35 -0
- package/templates/extension-template/playground/README.md.template +76 -0
- package/templates/extension-template/playground/app/globals.css.template +19 -0
- package/templates/extension-template/playground/app/layout.tsx.template +30 -0
- package/templates/extension-template/playground/app/page.tsx.template +44 -0
- package/templates/extension-template/playground/next.config.ts.template +62 -0
- package/templates/extension-template/playground/package.json.template +33 -0
- package/templates/extension-template/playground/tsconfig.json.template +27 -0
- package/templates/extension-template/src/config.ts +1 -2
- package/templates/extension-template/src/contexts/__PROVIDER_NAME__Context.tsx +1 -1
- package/templates/extension-template/src/contexts/__PROVIDER_NAME__ExtensionProvider.tsx +1 -0
- package/templates/extension-template/src/hooks/index.ts +1 -1
- package/templates/extension-template/src/index.ts +12 -4
- package/templates/extension-template/src/utils/withSmartProvider.tsx +70 -0
- package/templates/extension-template/tsup.config.ts +36 -22
package/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|

|
|
4
4
|
|
|
5
|
+
**[📦 View in Marketplace](https://hub.djangocfg.com/extensions/djangocfg-ext-base)** • **[📖 Documentation](https://djangocfg.com)** • **[⭐ GitHub](https://github.com/markolofsen/django-cfg)**
|
|
6
|
+
|
|
5
7
|
</div>
|
|
6
8
|
|
|
7
9
|
# @djangocfg/ext-base
|
|
@@ -37,12 +39,30 @@ The package includes a CLI tool for creating new DjangoCFG extensions:
|
|
|
37
39
|
# Create a new extension with interactive wizard
|
|
38
40
|
djangocfg-ext create
|
|
39
41
|
|
|
42
|
+
# Quick test extension (auto-cleanup + unique name)
|
|
43
|
+
djangocfg-ext test
|
|
44
|
+
|
|
45
|
+
# List all extensions in workspace
|
|
46
|
+
djangocfg-ext list
|
|
47
|
+
|
|
48
|
+
# Show extension info
|
|
49
|
+
djangocfg-ext info <extension-name>
|
|
50
|
+
|
|
40
51
|
# Show help
|
|
41
52
|
djangocfg-ext help
|
|
42
53
|
```
|
|
43
54
|
|
|
44
55
|
The CLI will guide you through creating a new extension with proper structure and configuration using the `createExtensionConfig` helper.
|
|
45
56
|
|
|
57
|
+
**Features:**
|
|
58
|
+
- Interactive prompts for package name, description, icon, and category
|
|
59
|
+
- Automatic npm registry validation to prevent duplicate package names
|
|
60
|
+
- Support for both scoped (`@my-org/my-extension`) and unscoped (`my-extension`) package names
|
|
61
|
+
- Template generation with proper placeholder replacement
|
|
62
|
+
- **Playground environment** with Next.js 15 for rapid development
|
|
63
|
+
- Auto-dependency installation and type checking
|
|
64
|
+
- Smart workspace detection for monorepos
|
|
65
|
+
|
|
46
66
|
## Quick Start
|
|
47
67
|
|
|
48
68
|
### 1. Create extension metadata
|
|
@@ -107,7 +127,7 @@ export function MyExtensionProvider({ children }) {
|
|
|
107
127
|
### 3. Use in your app
|
|
108
128
|
|
|
109
129
|
```typescript
|
|
110
|
-
import { MyExtensionProvider } from '@your-org/my-extension
|
|
130
|
+
import { MyExtensionProvider } from '@your-org/my-extension';
|
|
111
131
|
|
|
112
132
|
export default function RootLayout({ children }) {
|
|
113
133
|
return (
|
|
@@ -118,6 +138,94 @@ export default function RootLayout({ children }) {
|
|
|
118
138
|
}
|
|
119
139
|
```
|
|
120
140
|
|
|
141
|
+
## Development Workflow
|
|
142
|
+
|
|
143
|
+
Every extension created with the CLI includes a **playground** - a Next.js 15 development environment for rapid testing and iteration.
|
|
144
|
+
|
|
145
|
+
### Starting the Playground
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
cd your-extension
|
|
149
|
+
pnpm dev:playground
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
This command:
|
|
153
|
+
1. ✅ Automatically builds your extension
|
|
154
|
+
2. ✅ Starts Next.js dev server on port 3333
|
|
155
|
+
3. ✅ Opens browser automatically
|
|
156
|
+
4. ✅ Hot-reloads on source changes
|
|
157
|
+
|
|
158
|
+
### Playground Features
|
|
159
|
+
|
|
160
|
+
- **Next.js 15 App Router** with React 19
|
|
161
|
+
- **Tailwind CSS v4** pre-configured
|
|
162
|
+
- **BaseApp** wrapper with theme, auth, and SWR
|
|
163
|
+
- **Auto-build** extension before starting (via `predev` script)
|
|
164
|
+
- **Auto-open browser** when dev server is ready
|
|
165
|
+
- **Hot reload** for instant feedback
|
|
166
|
+
|
|
167
|
+
### Building for Production
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
# Build extension
|
|
171
|
+
pnpm build
|
|
172
|
+
|
|
173
|
+
# Build playground
|
|
174
|
+
pnpm build:playground
|
|
175
|
+
|
|
176
|
+
# Type check
|
|
177
|
+
pnpm check
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Smart Provider Pattern
|
|
181
|
+
|
|
182
|
+
Extensions support a **Smart Provider Pattern** that allows components to work both standalone and with manual provider wrapping.
|
|
183
|
+
|
|
184
|
+
### Using withSmartProvider
|
|
185
|
+
|
|
186
|
+
Create self-contained components that automatically wrap themselves with the provider when needed:
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
import { withSmartProvider } from '@your-org/my-extension';
|
|
190
|
+
|
|
191
|
+
// Your component
|
|
192
|
+
function MyComponent() {
|
|
193
|
+
const { data } = useMyExtension();
|
|
194
|
+
return <div>{data}</div>;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Wrap with smart provider
|
|
198
|
+
export const MySmartComponent = withSmartProvider(MyComponent);
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
**Usage:**
|
|
202
|
+
|
|
203
|
+
```typescript
|
|
204
|
+
// Works standalone (auto-wrapped)
|
|
205
|
+
<MySmartComponent />
|
|
206
|
+
|
|
207
|
+
// Also works with manual provider (shares context)
|
|
208
|
+
<MyExtensionProvider>
|
|
209
|
+
<MySmartComponent />
|
|
210
|
+
<MySmartComponent />
|
|
211
|
+
</MyExtensionProvider>
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
**Benefits:**
|
|
215
|
+
- ✅ Components work out-of-the-box without provider boilerplate
|
|
216
|
+
- ✅ Multiple components can still share context when manually wrapped
|
|
217
|
+
- ✅ Best of both worlds - simplicity and flexibility
|
|
218
|
+
|
|
219
|
+
**When to use:**
|
|
220
|
+
- Library components that should "just work"
|
|
221
|
+
- Components that might be used in isolation
|
|
222
|
+
- Reducing boilerplate for simple use cases
|
|
223
|
+
|
|
224
|
+
**When to use manual provider:**
|
|
225
|
+
- Multiple components need to share state
|
|
226
|
+
- Performance optimization (single provider instance)
|
|
227
|
+
- Explicit context boundaries
|
|
228
|
+
|
|
121
229
|
## Core Features
|
|
122
230
|
|
|
123
231
|
### Extension Config Helper
|
|
@@ -139,7 +247,6 @@ export const extensionConfig = createExtensionConfig(packageJson, {
|
|
|
139
247
|
// Optional fields
|
|
140
248
|
minVersion: '2.0.0',
|
|
141
249
|
githubStars: 100,
|
|
142
|
-
relatedExtensions: ['other-extension'],
|
|
143
250
|
examples: [
|
|
144
251
|
{
|
|
145
252
|
title: 'Example title',
|
|
@@ -159,13 +266,20 @@ export const extensionConfig = createExtensionConfig(packageJson, {
|
|
|
159
266
|
- `homepage` - Homepage URL
|
|
160
267
|
- `githubUrl` - Repository URL
|
|
161
268
|
- `keywords` - Keywords array
|
|
162
|
-
- `peerDependencies` - Peer dependencies
|
|
269
|
+
- `peerDependencies` - Peer dependencies (workspace:* auto-replaced with latest)
|
|
270
|
+
- `packageDependencies` - Dependencies from package.json (workspace:* auto-replaced with latest)
|
|
163
271
|
|
|
164
272
|
**Auto-generated:**
|
|
165
|
-
- `npmUrl` - npm package URL
|
|
273
|
+
- `npmUrl` - npm package URL (`https://www.npmjs.com/package/[name]`)
|
|
274
|
+
- `marketplaceId` - URL-safe ID (@ and / replaced with -), e.g. `@djangocfg/ext-newsletter` → `djangocfg-ext-newsletter`
|
|
275
|
+
- `marketplaceUrl` - Marketplace URL (only for official @djangocfg extensions): `https://hub.djangocfg.com/extensions/[marketplaceId]`
|
|
166
276
|
- `installCommand` - pnpm install command
|
|
277
|
+
- `downloadUrl` - npm tarball download URL
|
|
278
|
+
- `preview` - Preview image URL (`https://unpkg.com/[name]@latest/preview.png`)
|
|
167
279
|
- `tags` - Same as keywords
|
|
168
280
|
|
|
281
|
+
> **Note:** All `workspace:*` dependencies are automatically replaced with `latest` for marketplace display.
|
|
282
|
+
|
|
169
283
|
### Environment Configuration
|
|
170
284
|
|
|
171
285
|
```typescript
|
|
@@ -264,6 +378,52 @@ function MyComponent() {
|
|
|
264
378
|
| `@djangocfg/ext-base/auth` | Auth re-exports (useAuth, types) | Client components only |
|
|
265
379
|
| `@djangocfg/ext-base/api` | API utilities (createExtensionAPI, getSharedAuthStorage) | Server & client components |
|
|
266
380
|
|
|
381
|
+
**For your own extensions:**
|
|
382
|
+
|
|
383
|
+
Extension templates automatically export:
|
|
384
|
+
- `extensionConfig` - Extension metadata
|
|
385
|
+
- `[Name]Provider` - Main provider component (wraps ExtensionProvider)
|
|
386
|
+
- `use[Name]` - Context hook (required provider)
|
|
387
|
+
- `use[Name]Optional` - Context hook (optional provider)
|
|
388
|
+
- `withSmartProvider` - HOC for self-contained components
|
|
389
|
+
|
|
390
|
+
**Server-safe imports:**
|
|
391
|
+
- Use `@your-org/ext-name/config` to import extension metadata without loading React components
|
|
392
|
+
- This is recommended for server components and build tools
|
|
393
|
+
|
|
394
|
+
## Extension Categories
|
|
395
|
+
|
|
396
|
+
The package exports a standardized list of extension categories:
|
|
397
|
+
|
|
398
|
+
```typescript
|
|
399
|
+
import { EXTENSION_CATEGORIES } from '@djangocfg/ext-base';
|
|
400
|
+
|
|
401
|
+
// Array of { title: string, value: ExtensionCategory }
|
|
402
|
+
EXTENSION_CATEGORIES.forEach(category => {
|
|
403
|
+
console.log(category.title, category.value);
|
|
404
|
+
// Forms, forms
|
|
405
|
+
// Payments, payments
|
|
406
|
+
// Content, content
|
|
407
|
+
// Support, support
|
|
408
|
+
// Utilities, utilities
|
|
409
|
+
// Analytics, analytics
|
|
410
|
+
// Security, security
|
|
411
|
+
// Integration, integration
|
|
412
|
+
// Other, other
|
|
413
|
+
});
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
Available categories:
|
|
417
|
+
- `forms` - Forms, CRM, Lead Management
|
|
418
|
+
- `payments` - Payment Processing, Billing
|
|
419
|
+
- `content` - Content Management, Marketing
|
|
420
|
+
- `support` - Support, Helpdesk, Tickets
|
|
421
|
+
- `utilities` - Tools, Base, Infrastructure
|
|
422
|
+
- `analytics` - Analytics, Tracking, Monitoring
|
|
423
|
+
- `security` - Security, Authentication, Authorization
|
|
424
|
+
- `integration` - Third-party Integrations
|
|
425
|
+
- `other` - Other/Uncategorized
|
|
426
|
+
|
|
267
427
|
## TypeScript Types
|
|
268
428
|
|
|
269
429
|
```typescript
|
|
@@ -291,14 +451,33 @@ import type {
|
|
|
291
451
|
|
|
292
452
|
## Best Practices
|
|
293
453
|
|
|
454
|
+
### Configuration & Metadata
|
|
294
455
|
- Use `createExtensionConfig` helper to maintain Single Source of Truth from package.json
|
|
295
|
-
- Always wrap your extension with `ExtensionProvider` for proper registration
|
|
296
456
|
- Use Lucide icon names (not emoji) for `icon` field
|
|
297
457
|
- Include comprehensive `features` list for marketplace visibility
|
|
298
458
|
- Provide code `examples` with proper syntax highlighting
|
|
299
|
-
-
|
|
459
|
+
- **Include a `preview.png` file** in your extension root (1200x630px recommended)
|
|
460
|
+
- List `preview.png` in your package.json `files` array for npm publication
|
|
461
|
+
|
|
462
|
+
### Development
|
|
463
|
+
- Use the **playground** for rapid development (`pnpm dev:playground`)
|
|
464
|
+
- Test your components in isolation before integrating
|
|
465
|
+
- Run type checks before building (`pnpm check`)
|
|
300
466
|
- Use `createExtensionLogger` with consistent tags for structured logging
|
|
301
|
-
|
|
467
|
+
|
|
468
|
+
### Architecture
|
|
469
|
+
- Always wrap your extension with `ExtensionProvider` for proper registration
|
|
470
|
+
- Export main provider as `[Name]Provider` (not `[Name]ExtensionProvider`)
|
|
471
|
+
- Use `withSmartProvider` for components that should work standalone
|
|
472
|
+
- Prefer manual provider wrapping when components need shared state
|
|
473
|
+
- Use provided pagination hooks for consistent data fetching
|
|
474
|
+
- Separate client-only code appropriately
|
|
475
|
+
|
|
476
|
+
### Publishing
|
|
477
|
+
- Test with `pnpm build` before publishing
|
|
478
|
+
- Verify preview image is accessible via unpkg
|
|
479
|
+
- Ensure all peer dependencies are correctly listed
|
|
480
|
+
- Include clear usage examples in README
|
|
302
481
|
|
|
303
482
|
## License
|
|
304
483
|
|
package/dist/api.cjs
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
4
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
5
|
+
}) : x)(function(x) {
|
|
6
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
7
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
// src/config.ts
|
|
11
|
+
process.env.NODE_ENV === "development";
|
|
12
|
+
process.env.NODE_ENV === "production";
|
|
13
|
+
var isStaticBuild = process.env.STATIC_BUILD === "true";
|
|
14
|
+
function getApiUrl() {
|
|
15
|
+
return process.env.NEXT_PUBLIC_API_URL || "/api";
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// src/api/createExtensionAPI.ts
|
|
19
|
+
function createExtensionAPI(APIClass) {
|
|
20
|
+
let storage;
|
|
21
|
+
try {
|
|
22
|
+
const { api: accountsApi } = __require("@djangocfg/api");
|
|
23
|
+
storage = accountsApi._storage;
|
|
24
|
+
} catch (error) {
|
|
25
|
+
storage = void 0;
|
|
26
|
+
}
|
|
27
|
+
const apiUrl = isStaticBuild ? "" : getApiUrl();
|
|
28
|
+
return new APIClass(apiUrl, storage ? { storage } : void 0);
|
|
29
|
+
}
|
|
30
|
+
function getSharedAuthStorage() {
|
|
31
|
+
try {
|
|
32
|
+
const { api: accountsApi } = __require("@djangocfg/api");
|
|
33
|
+
return accountsApi._storage;
|
|
34
|
+
} catch (error) {
|
|
35
|
+
return void 0;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
exports.createExtensionAPI = createExtensionAPI;
|
|
40
|
+
exports.getSharedAuthStorage = getSharedAuthStorage;
|
package/dist/api.d.cts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Factory for creating extension API instances
|
|
3
|
+
*
|
|
4
|
+
* Provides consistent API setup across all extensions with shared authentication
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Creates an extension API instance with shared authentication storage
|
|
8
|
+
*
|
|
9
|
+
* @param APIClass - The generated API class from your extension
|
|
10
|
+
* @returns Configured API instance with shared auth storage
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* // In your extension's api/index.ts
|
|
15
|
+
* import { API } from './generated/ext_newsletter';
|
|
16
|
+
* import { createExtensionAPI } from '@djangocfg/ext-base/api';
|
|
17
|
+
*
|
|
18
|
+
* export const apiNewsletter = createExtensionAPI(API);
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
declare function createExtensionAPI<T>(APIClass: new (url: string, options?: any) => T): T;
|
|
22
|
+
/**
|
|
23
|
+
* Get shared authentication storage from accounts API
|
|
24
|
+
*
|
|
25
|
+
* @returns Storage instance or undefined if not available
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```typescript
|
|
29
|
+
* const storage = getSharedAuthStorage();
|
|
30
|
+
* const api = new API(apiUrl, { storage });
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
declare function getSharedAuthStorage(): any | undefined;
|
|
34
|
+
|
|
35
|
+
export { createExtensionAPI, getSharedAuthStorage };
|
package/dist/api.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Factory for creating extension API instances
|
|
3
|
+
*
|
|
4
|
+
* Provides consistent API setup across all extensions with shared authentication
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Creates an extension API instance with shared authentication storage
|
|
8
|
+
*
|
|
9
|
+
* @param APIClass - The generated API class from your extension
|
|
10
|
+
* @returns Configured API instance with shared auth storage
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* // In your extension's api/index.ts
|
|
15
|
+
* import { API } from './generated/ext_newsletter';
|
|
16
|
+
* import { createExtensionAPI } from '@djangocfg/ext-base/api';
|
|
17
|
+
*
|
|
18
|
+
* export const apiNewsletter = createExtensionAPI(API);
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
declare function createExtensionAPI<T>(APIClass: new (url: string, options?: any) => T): T;
|
|
22
|
+
/**
|
|
23
|
+
* Get shared authentication storage from accounts API
|
|
24
|
+
*
|
|
25
|
+
* @returns Storage instance or undefined if not available
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```typescript
|
|
29
|
+
* const storage = getSharedAuthStorage();
|
|
30
|
+
* const api = new API(apiUrl, { storage });
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
declare function getSharedAuthStorage(): any | undefined;
|
|
34
|
+
|
|
35
|
+
export { createExtensionAPI, getSharedAuthStorage };
|
package/dist/api.js
ADDED
package/dist/auth.cjs
ADDED
package/dist/auth.d.cts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { AuthContextType, UserProfile, useAuth } from '@djangocfg/api/auth';
|
package/dist/auth.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { AuthContextType, UserProfile, useAuth } from '@djangocfg/api/auth';
|
package/dist/auth.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
export { __require };
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { package_default } from './chunk-VJEGYVBV.js';
|
|
2
|
+
import { createConsola } from 'consola';
|
|
3
|
+
|
|
4
|
+
// src/types/context.ts
|
|
5
|
+
var EXTENSION_CATEGORIES = [
|
|
6
|
+
{ title: "Forms", value: "forms" },
|
|
7
|
+
{ title: "Payments", value: "payments" },
|
|
8
|
+
{ title: "Content", value: "content" },
|
|
9
|
+
{ title: "Support", value: "support" },
|
|
10
|
+
{ title: "Utilities", value: "utilities" },
|
|
11
|
+
{ title: "Analytics", value: "analytics" },
|
|
12
|
+
{ title: "Security", value: "security" },
|
|
13
|
+
{ title: "Integration", value: "integration" },
|
|
14
|
+
{ title: "Other", value: "other" }
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
// src/utils/createExtensionConfig.ts
|
|
18
|
+
function replaceWorkspaceDeps(deps) {
|
|
19
|
+
if (!deps) return void 0;
|
|
20
|
+
const result = {};
|
|
21
|
+
for (const [key, value] of Object.entries(deps)) {
|
|
22
|
+
result[key] = value === "workspace:*" ? "latest" : value;
|
|
23
|
+
}
|
|
24
|
+
return result;
|
|
25
|
+
}
|
|
26
|
+
function createExtensionConfig(packageJson, config) {
|
|
27
|
+
const author = typeof packageJson.author === "string" ? packageJson.author : packageJson.author?.name || "Unknown";
|
|
28
|
+
const githubUrl = typeof packageJson.repository === "string" ? packageJson.repository : packageJson.repository?.url;
|
|
29
|
+
const packageNameWithoutScope = packageJson.name.split("/").pop() || packageJson.name;
|
|
30
|
+
const downloadUrl = `https://registry.npmjs.org/${packageJson.name}/-/${packageNameWithoutScope}-${packageJson.version}.tgz`;
|
|
31
|
+
const marketplaceId = packageJson.name.replace("@", "").replace("/", "-");
|
|
32
|
+
return {
|
|
33
|
+
// From package.json
|
|
34
|
+
name: config.name,
|
|
35
|
+
version: packageJson.version,
|
|
36
|
+
author,
|
|
37
|
+
description: packageJson.description,
|
|
38
|
+
keywords: packageJson.keywords,
|
|
39
|
+
license: packageJson.license,
|
|
40
|
+
homepage: packageJson.homepage,
|
|
41
|
+
githubUrl,
|
|
42
|
+
packageDependencies: replaceWorkspaceDeps(packageJson.dependencies),
|
|
43
|
+
packagePeerDependencies: replaceWorkspaceDeps(packageJson.peerDependencies),
|
|
44
|
+
packageDevDependencies: replaceWorkspaceDeps(packageJson.devDependencies),
|
|
45
|
+
// From manual config
|
|
46
|
+
displayName: config.displayName,
|
|
47
|
+
category: config.category,
|
|
48
|
+
features: config.features,
|
|
49
|
+
examples: config.examples,
|
|
50
|
+
minVersion: config.minVersion,
|
|
51
|
+
githubStars: config.githubStars,
|
|
52
|
+
// Auto-generated
|
|
53
|
+
npmUrl: `https://www.npmjs.com/package/${packageJson.name}`,
|
|
54
|
+
marketplaceId,
|
|
55
|
+
// Only generate marketplace URL for official @djangocfg extensions
|
|
56
|
+
marketplaceUrl: packageJson.name.startsWith("@djangocfg/ext-") ? `https://hub.djangocfg.com/extensions/${marketplaceId}` : void 0,
|
|
57
|
+
installCommand: `pnpm add ${packageJson.name}`,
|
|
58
|
+
downloadUrl,
|
|
59
|
+
tags: packageJson.keywords,
|
|
60
|
+
preview: `https://unpkg.com/${packageJson.name}@latest/preview.png`
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// src/extensionConfig.ts
|
|
65
|
+
var extensionConfig = createExtensionConfig(package_default, {
|
|
66
|
+
name: "base",
|
|
67
|
+
displayName: "Extension Base",
|
|
68
|
+
category: "utilities",
|
|
69
|
+
features: [
|
|
70
|
+
"CLI for creating extensions",
|
|
71
|
+
"Next.js 15 playground for rapid development",
|
|
72
|
+
"Smart Provider Pattern for flexible component usage",
|
|
73
|
+
"Extension context and provider system",
|
|
74
|
+
"Pagination hooks (standard & infinite scroll)",
|
|
75
|
+
"API client utilities with auth integration",
|
|
76
|
+
"Type-safe context creation helpers",
|
|
77
|
+
"Logging system with structured output",
|
|
78
|
+
"Environment detection (dev, prod, static)",
|
|
79
|
+
"TypeScript types and utilities"
|
|
80
|
+
],
|
|
81
|
+
minVersion: "2.0.0",
|
|
82
|
+
examples: [
|
|
83
|
+
{
|
|
84
|
+
title: "Create Extension with CLI",
|
|
85
|
+
description: "Interactive wizard with playground environment",
|
|
86
|
+
code: `# Create extension with interactive wizard
|
|
87
|
+
pnpm dlx @djangocfg/ext-base create
|
|
88
|
+
|
|
89
|
+
# Quick test extension (auto-cleanup)
|
|
90
|
+
pnpm dlx @djangocfg/ext-base test
|
|
91
|
+
|
|
92
|
+
# List all extensions
|
|
93
|
+
pnpm dlx @djangocfg/ext-base list`,
|
|
94
|
+
language: "bash"
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
title: "Development with Playground",
|
|
98
|
+
description: "Start Next.js playground for rapid testing",
|
|
99
|
+
code: `cd your-extension
|
|
100
|
+
|
|
101
|
+
# Start playground (auto-builds extension + opens browser)
|
|
102
|
+
pnpm dev:playground
|
|
103
|
+
|
|
104
|
+
# Build for production
|
|
105
|
+
pnpm build
|
|
106
|
+
|
|
107
|
+
# Type check
|
|
108
|
+
pnpm check`,
|
|
109
|
+
language: "bash"
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
title: "Smart Provider Pattern",
|
|
113
|
+
description: "Components that work standalone or with shared context",
|
|
114
|
+
code: `import { withSmartProvider } from '@your-org/my-extension';
|
|
115
|
+
|
|
116
|
+
// Your component
|
|
117
|
+
function MyComponent() {
|
|
118
|
+
const { data } = useMyExtension();
|
|
119
|
+
return <div>{data}</div>;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Wrap with smart provider
|
|
123
|
+
export const MySmartComponent = withSmartProvider(MyComponent);
|
|
124
|
+
|
|
125
|
+
// Usage 1: Standalone (auto-wrapped)
|
|
126
|
+
<MySmartComponent />
|
|
127
|
+
|
|
128
|
+
// Usage 2: Manual provider (shared context)
|
|
129
|
+
<MyExtensionProvider>
|
|
130
|
+
<MySmartComponent />
|
|
131
|
+
<MySmartComponent />
|
|
132
|
+
</MyExtensionProvider>`,
|
|
133
|
+
language: "tsx"
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
title: "Extension Provider",
|
|
137
|
+
description: "Register and wrap your extension",
|
|
138
|
+
code: `import { ExtensionProvider } from '@djangocfg/ext-base/hooks';
|
|
139
|
+
import { extensionConfig } from './config';
|
|
140
|
+
|
|
141
|
+
export function MyExtensionProvider({ children }) {
|
|
142
|
+
return (
|
|
143
|
+
<ExtensionProvider metadata={extensionConfig}>
|
|
144
|
+
{children}
|
|
145
|
+
</ExtensionProvider>
|
|
146
|
+
);
|
|
147
|
+
}`,
|
|
148
|
+
language: "tsx"
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
title: "Pagination Hooks",
|
|
152
|
+
description: "Built-in hooks for standard and infinite scroll",
|
|
153
|
+
code: `import { usePagination, useInfinitePagination } from '@djangocfg/ext-base/hooks';
|
|
154
|
+
|
|
155
|
+
// Standard pagination
|
|
156
|
+
const { items, page, totalPages, nextPage, prevPage } = usePagination({
|
|
157
|
+
keyPrefix: 'articles',
|
|
158
|
+
fetcher: async (page, pageSize) => api.articles.list({ page, page_size: pageSize }),
|
|
159
|
+
pageSize: 20,
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// Infinite scroll
|
|
163
|
+
const { items, isLoading, hasMore, loadMore } = useInfinitePagination({
|
|
164
|
+
keyPrefix: 'articles',
|
|
165
|
+
fetcher: async (page, pageSize) => api.articles.list({ page, page_size: pageSize }),
|
|
166
|
+
pageSize: 20,
|
|
167
|
+
});`,
|
|
168
|
+
language: "tsx"
|
|
169
|
+
}
|
|
170
|
+
]
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// src/utils/errors.ts
|
|
174
|
+
function isExtensionError(error) {
|
|
175
|
+
return typeof error === "object" && error !== null && "message" in error && "timestamp" in error;
|
|
176
|
+
}
|
|
177
|
+
function createExtensionError(error, code, details) {
|
|
178
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
179
|
+
return {
|
|
180
|
+
message,
|
|
181
|
+
code,
|
|
182
|
+
details,
|
|
183
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
function formatErrorMessage(error) {
|
|
187
|
+
if (isExtensionError(error)) {
|
|
188
|
+
return error.code ? `[${error.code}] ${error.message}` : error.message;
|
|
189
|
+
}
|
|
190
|
+
if (error instanceof Error) {
|
|
191
|
+
return error.message;
|
|
192
|
+
}
|
|
193
|
+
return String(error);
|
|
194
|
+
}
|
|
195
|
+
function handleExtensionError(error, logger, callback) {
|
|
196
|
+
const extensionError = isExtensionError(error) ? error : createExtensionError(error);
|
|
197
|
+
if (logger) {
|
|
198
|
+
logger.error("Extension error:", extensionError);
|
|
199
|
+
}
|
|
200
|
+
if (callback) {
|
|
201
|
+
callback(extensionError);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
var isDevelopment = process.env.NODE_ENV === "development";
|
|
205
|
+
var LEVEL_MAP = {
|
|
206
|
+
debug: 4,
|
|
207
|
+
info: 3,
|
|
208
|
+
warn: 2,
|
|
209
|
+
error: 1
|
|
210
|
+
};
|
|
211
|
+
function createExtensionLogger(options) {
|
|
212
|
+
const { tag, level = "info", enabled = true } = options;
|
|
213
|
+
if (!enabled) {
|
|
214
|
+
const noop = () => {
|
|
215
|
+
};
|
|
216
|
+
return {
|
|
217
|
+
info: noop,
|
|
218
|
+
warn: noop,
|
|
219
|
+
error: noop,
|
|
220
|
+
debug: noop,
|
|
221
|
+
success: noop
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
const logLevel = isDevelopment ? LEVEL_MAP[level] : LEVEL_MAP.error;
|
|
225
|
+
const consola = createConsola({
|
|
226
|
+
level: logLevel
|
|
227
|
+
}).withTag(tag);
|
|
228
|
+
return {
|
|
229
|
+
info: (...args) => consola.info(...args),
|
|
230
|
+
warn: (...args) => consola.warn(...args),
|
|
231
|
+
error: (...args) => consola.error(...args),
|
|
232
|
+
debug: (...args) => consola.debug(...args),
|
|
233
|
+
success: (...args) => consola.success(...args)
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export { EXTENSION_CATEGORIES, createExtensionConfig, createExtensionError, createExtensionLogger, extensionConfig, formatErrorMessage, handleExtensionError, isExtensionError };
|