@djangocfg/ext-base 1.0.0 → 1.0.1
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 +62 -174
- package/package.json +20 -6
- package/src/cli/index.ts +277 -0
- package/dist/api.cjs +0 -41
- package/dist/api.d.cts +0 -35
- package/dist/api.d.ts +0 -35
- package/dist/api.js +0 -2
- package/dist/auth.cjs +0 -10
- package/dist/auth.d.cts +0 -1
- package/dist/auth.d.ts +0 -1
- package/dist/auth.js +0 -2
- package/dist/chunk-3RG5ZIWI.js +0 -8
- package/dist/chunk-MECBWZG4.js +0 -44
- package/dist/chunk-YQGNYUBX.js +0 -67
- package/dist/hooks.cjs +0 -190
- package/dist/hooks.d.cts +0 -96
- package/dist/hooks.d.ts +0 -96
- package/dist/hooks.js +0 -65
- package/dist/index.cjs +0 -131
- package/dist/index.d.cts +0 -246
- package/dist/index.d.ts +0 -246
- package/dist/index.js +0 -3
package/README.md
CHANGED
|
@@ -6,17 +6,16 @@ Base utilities and common code for building DjangoCFG extensions.
|
|
|
6
6
|
|
|
7
7
|
## What is this?
|
|
8
8
|
|
|
9
|
-
`@djangocfg/ext-base`
|
|
9
|
+
`@djangocfg/ext-base` provides the foundation for DjangoCFG extensions with:
|
|
10
10
|
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
- **
|
|
17
|
-
- **TypeScript types** - shared types for all extensions
|
|
11
|
+
- Extension registration system and metadata tracking
|
|
12
|
+
- React hooks for pagination, infinite scroll, and data fetching
|
|
13
|
+
- Environment utilities (isDevelopment, isProduction, isStaticBuild)
|
|
14
|
+
- Type-safe context helpers and logger utilities
|
|
15
|
+
- Auth integration and API factory
|
|
16
|
+
- **CLI tool** for managing extensions
|
|
18
17
|
|
|
19
|
-
|
|
18
|
+
Used internally by official extensions (newsletter, payments, support, leads, knowbase) and for building custom extensions.
|
|
20
19
|
|
|
21
20
|
## Install
|
|
22
21
|
|
|
@@ -24,6 +23,35 @@ This package is used internally by all official DjangoCFG extensions (newsletter
|
|
|
24
23
|
pnpm add @djangocfg/ext-base
|
|
25
24
|
```
|
|
26
25
|
|
|
26
|
+
## CLI Usage
|
|
27
|
+
|
|
28
|
+
The package includes a CLI tool for managing DjangoCFG extensions:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# List available extensions
|
|
32
|
+
djangocfg-ext list
|
|
33
|
+
|
|
34
|
+
# Show extension info
|
|
35
|
+
djangocfg-ext info ext-leads
|
|
36
|
+
|
|
37
|
+
# Interactive installation wizard
|
|
38
|
+
djangocfg-ext install
|
|
39
|
+
|
|
40
|
+
# Initialize extension in your project
|
|
41
|
+
djangocfg-ext init
|
|
42
|
+
|
|
43
|
+
# Show help
|
|
44
|
+
djangocfg-ext help
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Available Extensions
|
|
48
|
+
|
|
49
|
+
- **@djangocfg/ext-leads** - Lead management and contact forms
|
|
50
|
+
- **@djangocfg/ext-payments** - Payment processing with multiple providers
|
|
51
|
+
- **@djangocfg/ext-newsletter** - Newsletter and email campaigns
|
|
52
|
+
- **@djangocfg/ext-support** - Customer support and ticketing
|
|
53
|
+
- **@djangocfg/ext-knowbase** - Knowledge base and documentation
|
|
54
|
+
|
|
27
55
|
## Quick Start
|
|
28
56
|
|
|
29
57
|
### 1. Create extension metadata
|
|
@@ -35,16 +63,13 @@ import type { ExtensionMetadata } from '@djangocfg/ext-base';
|
|
|
35
63
|
export const extensionConfig: ExtensionMetadata = {
|
|
36
64
|
name: 'my-extension',
|
|
37
65
|
version: '1.0.0',
|
|
38
|
-
author: 'Your Name',
|
|
39
66
|
displayName: 'My Extension',
|
|
40
67
|
description: 'Amazing extension functionality',
|
|
41
68
|
icon: '🚀',
|
|
42
|
-
license: 'MIT',
|
|
43
|
-
keywords: ['extension', 'feature'],
|
|
44
69
|
};
|
|
45
70
|
```
|
|
46
71
|
|
|
47
|
-
### 2. Create
|
|
72
|
+
### 2. Create extension provider
|
|
48
73
|
|
|
49
74
|
```typescript
|
|
50
75
|
// src/contexts/MyExtensionProvider.tsx
|
|
@@ -56,7 +81,6 @@ import { extensionConfig } from '../config';
|
|
|
56
81
|
export function MyExtensionProvider({ children }) {
|
|
57
82
|
return (
|
|
58
83
|
<ExtensionProvider metadata={extensionConfig}>
|
|
59
|
-
{/* Your extension contexts here */}
|
|
60
84
|
{children}
|
|
61
85
|
</ExtensionProvider>
|
|
62
86
|
);
|
|
@@ -77,60 +101,27 @@ export default function RootLayout({ children }) {
|
|
|
77
101
|
}
|
|
78
102
|
```
|
|
79
103
|
|
|
80
|
-
The extension will automatically register itself and log in development mode:
|
|
81
|
-
```
|
|
82
|
-
[ext-base] Extension registered: My Extension v1.0.0
|
|
83
|
-
```
|
|
84
|
-
|
|
85
104
|
## Core Features
|
|
86
105
|
|
|
87
106
|
### Environment Configuration
|
|
88
107
|
|
|
89
108
|
```typescript
|
|
90
|
-
import { isDevelopment, isProduction, isStaticBuild
|
|
109
|
+
import { isDevelopment, isProduction, isStaticBuild } from '@djangocfg/ext-base';
|
|
91
110
|
|
|
92
111
|
if (isDevelopment) {
|
|
93
112
|
console.log('Running in development mode');
|
|
94
113
|
}
|
|
95
|
-
|
|
96
|
-
if (isStaticBuild) {
|
|
97
|
-
// Special handling for Next.js static export
|
|
98
|
-
}
|
|
99
114
|
```
|
|
100
115
|
|
|
101
116
|
### API Factory
|
|
102
117
|
|
|
103
|
-
Create extension API instances with
|
|
118
|
+
Create extension API instances with automatic configuration:
|
|
104
119
|
|
|
105
|
-
```typescript
|
|
106
|
-
// src/api/index.ts
|
|
107
|
-
import { API } from './generated/ext_myextension';
|
|
108
|
-
import { createExtensionAPI } from '@djangocfg/ext-base/api';
|
|
109
|
-
|
|
110
|
-
// That's it! All configuration is handled automatically:
|
|
111
|
-
// - API URL from environment
|
|
112
|
-
// - Static build detection
|
|
113
|
-
// - Shared authentication storage from @djangocfg/api
|
|
114
|
-
export const apiMyExtension = createExtensionAPI(API);
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
**Before (manual setup):**
|
|
118
|
-
```typescript
|
|
119
|
-
import { API } from './generated/ext_myextension';
|
|
120
|
-
import { api as accountsApi } from '@djangocfg/api';
|
|
121
|
-
|
|
122
|
-
const isStaticBuild = process.env.NEXT_PUBLIC_STATIC_BUILD === 'true';
|
|
123
|
-
const apiUrl = isStaticBuild ? '' : process.env.NEXT_PUBLIC_API_URL || '';
|
|
124
|
-
const storage = (accountsApi as any)._storage;
|
|
125
|
-
|
|
126
|
-
export const apiMyExtension = new API(apiUrl, { storage });
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
**After (with factory):**
|
|
130
120
|
```typescript
|
|
131
121
|
import { API } from './generated/ext_myextension';
|
|
132
122
|
import { createExtensionAPI } from '@djangocfg/ext-base/api';
|
|
133
123
|
|
|
124
|
+
// Handles API URL, static build detection, and shared auth automatically
|
|
134
125
|
export const apiMyExtension = createExtensionAPI(API);
|
|
135
126
|
```
|
|
136
127
|
|
|
@@ -140,23 +131,19 @@ export const apiMyExtension = createExtensionAPI(API);
|
|
|
140
131
|
import { usePagination, useInfinitePagination } from '@djangocfg/ext-base/hooks';
|
|
141
132
|
|
|
142
133
|
// Standard pagination
|
|
143
|
-
const { items, page,
|
|
134
|
+
const { items, page, totalPages, goToPage, nextPage, prevPage } = usePagination({
|
|
144
135
|
keyPrefix: 'articles',
|
|
145
136
|
fetcher: async (page, pageSize) => {
|
|
146
137
|
const response = await api.articles.list({ page, page_size: pageSize });
|
|
147
138
|
return response.data;
|
|
148
139
|
},
|
|
149
|
-
initialPage: 1,
|
|
150
140
|
pageSize: 20,
|
|
151
141
|
});
|
|
152
142
|
|
|
153
143
|
// Infinite scroll
|
|
154
144
|
const { items, isLoading, hasMore, loadMore } = useInfinitePagination({
|
|
155
145
|
keyPrefix: 'articles',
|
|
156
|
-
fetcher: async (page, pageSize) => {
|
|
157
|
-
const response = await api.articles.list({ page, page_size: pageSize });
|
|
158
|
-
return response.data;
|
|
159
|
-
},
|
|
146
|
+
fetcher: async (page, pageSize) => api.articles.list({ page, page_size: pageSize }).then(r => r.data),
|
|
160
147
|
pageSize: 20,
|
|
161
148
|
});
|
|
162
149
|
```
|
|
@@ -175,12 +162,6 @@ const { Provider, useContext: useMyContext } = createExtensionContext<MyContextV
|
|
|
175
162
|
displayName: 'MyContext',
|
|
176
163
|
errorMessage: 'useMyContext must be used within MyProvider',
|
|
177
164
|
});
|
|
178
|
-
|
|
179
|
-
// In components
|
|
180
|
-
export function MyComponent() {
|
|
181
|
-
const { data, refresh } = useMyContext();
|
|
182
|
-
return <div>{data.length} items</div>;
|
|
183
|
-
}
|
|
184
165
|
```
|
|
185
166
|
|
|
186
167
|
### Logger
|
|
@@ -188,14 +169,11 @@ export function MyComponent() {
|
|
|
188
169
|
```typescript
|
|
189
170
|
import { createExtensionLogger } from '@djangocfg/ext-base';
|
|
190
171
|
|
|
191
|
-
const logger = createExtensionLogger({
|
|
192
|
-
tag: 'my-extension',
|
|
193
|
-
level: 'info',
|
|
194
|
-
});
|
|
172
|
+
const logger = createExtensionLogger({ tag: 'my-extension' });
|
|
195
173
|
|
|
196
174
|
logger.info('Extension initialized');
|
|
197
|
-
logger.error('
|
|
198
|
-
logger.success('
|
|
175
|
+
logger.error('Operation failed', error);
|
|
176
|
+
logger.success('Completed!');
|
|
199
177
|
```
|
|
200
178
|
|
|
201
179
|
### Auth Integration
|
|
@@ -206,135 +184,45 @@ import { useAuth } from '@djangocfg/ext-base/auth';
|
|
|
206
184
|
function MyComponent() {
|
|
207
185
|
const { user, isAuthenticated, login, logout } = useAuth();
|
|
208
186
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
}
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
### Error Handling
|
|
218
|
-
|
|
219
|
-
```typescript
|
|
220
|
-
import { handleError, formatError } from '@djangocfg/ext-base';
|
|
221
|
-
|
|
222
|
-
try {
|
|
223
|
-
await someOperation();
|
|
224
|
-
} catch (error) {
|
|
225
|
-
handleError(error, (formattedError) => {
|
|
226
|
-
logger.error('Operation failed:', formattedError);
|
|
227
|
-
});
|
|
187
|
+
return isAuthenticated ? (
|
|
188
|
+
<div>Welcome, {user?.email}</div>
|
|
189
|
+
) : (
|
|
190
|
+
<button onClick={login}>Login</button>
|
|
191
|
+
);
|
|
228
192
|
}
|
|
229
193
|
```
|
|
230
194
|
|
|
231
195
|
## Package Exports
|
|
232
196
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
-
|
|
236
|
-
-
|
|
237
|
-
-
|
|
238
|
-
-
|
|
239
|
-
- Error handling utilities
|
|
240
|
-
|
|
241
|
-
### Hooks entry (`@djangocfg/ext-base/hooks`)
|
|
242
|
-
Client-only exports:
|
|
243
|
-
- `ExtensionProvider` - main provider component
|
|
244
|
-
- `usePagination` - standard pagination hook
|
|
245
|
-
- `useInfinitePagination` - infinite scroll hook
|
|
246
|
-
- `createExtensionContext` - context factory
|
|
247
|
-
|
|
248
|
-
### Auth entry (`@djangocfg/ext-base/auth`)
|
|
249
|
-
Auth re-exports:
|
|
250
|
-
- `useAuth` - authentication hook
|
|
251
|
-
- `UserProfile` type
|
|
252
|
-
- `AuthContextType` type
|
|
253
|
-
|
|
254
|
-
### API entry (`@djangocfg/ext-base/api`)
|
|
255
|
-
API utilities:
|
|
256
|
-
- `createExtensionAPI` - API instance factory
|
|
257
|
-
- `getSharedAuthStorage` - Get shared auth storage
|
|
197
|
+
| Export | Description | Usage |
|
|
198
|
+
|--------|-------------|-------|
|
|
199
|
+
| `@djangocfg/ext-base` | Server-safe exports (types, environment, API factory, logger, error handling) | Server & client components |
|
|
200
|
+
| `@djangocfg/ext-base/hooks` | Client-only exports (ExtensionProvider, pagination hooks, context factory) | Client components only |
|
|
201
|
+
| `@djangocfg/ext-base/auth` | Auth re-exports (useAuth, types) | Client components only |
|
|
202
|
+
| `@djangocfg/ext-base/api` | API utilities (createExtensionAPI, getSharedAuthStorage) | Server & client components |
|
|
258
203
|
|
|
259
204
|
## TypeScript Types
|
|
260
205
|
|
|
261
206
|
```typescript
|
|
262
207
|
import type {
|
|
263
|
-
// Extension metadata
|
|
264
208
|
ExtensionMetadata,
|
|
265
209
|
ExtensionProviderProps,
|
|
266
|
-
|
|
267
|
-
// Pagination
|
|
268
210
|
PaginatedResponse,
|
|
269
211
|
PaginationParams,
|
|
270
212
|
PaginationState,
|
|
271
213
|
InfinitePaginationReturn,
|
|
272
|
-
|
|
273
|
-
// Logger
|
|
274
214
|
ExtensionLogger,
|
|
275
|
-
LoggerOptions,
|
|
276
|
-
|
|
277
|
-
// Errors
|
|
278
215
|
ExtensionError,
|
|
279
|
-
ErrorHandler,
|
|
280
|
-
|
|
281
|
-
// Context
|
|
282
|
-
ExtensionContextOptions,
|
|
283
216
|
} from '@djangocfg/ext-base';
|
|
284
217
|
```
|
|
285
218
|
|
|
286
|
-
## Example: Complete Extension
|
|
287
|
-
|
|
288
|
-
```typescript
|
|
289
|
-
// config.ts
|
|
290
|
-
export const extensionConfig: ExtensionMetadata = {
|
|
291
|
-
name: 'blog',
|
|
292
|
-
version: '1.0.0',
|
|
293
|
-
author: 'Your Company',
|
|
294
|
-
displayName: 'Blog',
|
|
295
|
-
description: 'Blog management system',
|
|
296
|
-
icon: '📝',
|
|
297
|
-
keywords: ['blog', 'articles'],
|
|
298
|
-
};
|
|
299
|
-
|
|
300
|
-
// contexts/BlogProvider.tsx
|
|
301
|
-
'use client';
|
|
302
|
-
|
|
303
|
-
import { ExtensionProvider } from '@djangocfg/ext-base/hooks';
|
|
304
|
-
import { useInfinitePagination } from '@djangocfg/ext-base/hooks';
|
|
305
|
-
import { createExtensionLogger } from '@djangocfg/ext-base';
|
|
306
|
-
import { extensionConfig } from '../config';
|
|
307
|
-
|
|
308
|
-
const logger = createExtensionLogger({ tag: 'blog' });
|
|
309
|
-
|
|
310
|
-
export function BlogProvider({ children }) {
|
|
311
|
-
const { items, loadMore, hasMore } = useInfinitePagination({
|
|
312
|
-
keyPrefix: 'blog-articles',
|
|
313
|
-
fetcher: async (page, pageSize) => {
|
|
314
|
-
const response = await api.articles.list({ page, page_size: pageSize });
|
|
315
|
-
return response.data;
|
|
316
|
-
},
|
|
317
|
-
});
|
|
318
|
-
|
|
319
|
-
logger.info('Blog provider initialized with', items.length, 'articles');
|
|
320
|
-
|
|
321
|
-
return (
|
|
322
|
-
<ExtensionProvider metadata={extensionConfig}>
|
|
323
|
-
{/* Your contexts here */}
|
|
324
|
-
{children}
|
|
325
|
-
</ExtensionProvider>
|
|
326
|
-
);
|
|
327
|
-
}
|
|
328
|
-
```
|
|
329
|
-
|
|
330
219
|
## Best Practices
|
|
331
220
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
6. **Separate entry points** - Use `/hooks` for client code, main entry for server-safe code
|
|
221
|
+
- Always wrap your extension with `ExtensionProvider` for proper registration
|
|
222
|
+
- Define complete metadata in `config.ts`
|
|
223
|
+
- Use provided pagination hooks for consistent data fetching
|
|
224
|
+
- Use `createExtensionLogger` with consistent tags for structured logging
|
|
225
|
+
- Separate client-only code using `/hooks` entry point
|
|
338
226
|
|
|
339
227
|
## License
|
|
340
228
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@djangocfg/ext-base",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Base utilities and common code for DjangoCFG extensions",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"django",
|
|
@@ -55,26 +55,40 @@
|
|
|
55
55
|
"dist",
|
|
56
56
|
"src"
|
|
57
57
|
],
|
|
58
|
+
"bin": {
|
|
59
|
+
"djangocfg-ext": "./dist/cli.mjs"
|
|
60
|
+
},
|
|
58
61
|
"scripts": {
|
|
59
62
|
"build": "tsup",
|
|
60
63
|
"dev": "tsup --watch",
|
|
61
|
-
"check": "tsc --noEmit"
|
|
64
|
+
"check": "tsc --noEmit",
|
|
65
|
+
"cli": "tsx src/cli/index.ts",
|
|
66
|
+
"cli:help": "tsx src/cli/index.ts --help",
|
|
67
|
+
"cli:list": "tsx src/cli/index.ts list",
|
|
68
|
+
"cli:info": "tsx src/cli/index.ts info"
|
|
62
69
|
},
|
|
63
70
|
"peerDependencies": {
|
|
64
|
-
"@djangocfg/api": "^2.1.
|
|
71
|
+
"@djangocfg/api": "^2.1.15",
|
|
65
72
|
"consola": "^3.4.2",
|
|
66
73
|
"react": "^18 || ^19",
|
|
67
74
|
"react-dom": "^18 || ^19",
|
|
68
75
|
"swr": "^2.3.7"
|
|
69
76
|
},
|
|
77
|
+
"dependencies": {
|
|
78
|
+
"chalk": "^5.3.0",
|
|
79
|
+
"consola": "^3.4.2",
|
|
80
|
+
"picocolors": "^1.1.1",
|
|
81
|
+
"prompts": "^2.4.2"
|
|
82
|
+
},
|
|
70
83
|
"devDependencies": {
|
|
71
|
-
"@djangocfg/api": "^2.1.
|
|
72
|
-
"@djangocfg/typescript-config": "^2.1.
|
|
84
|
+
"@djangocfg/api": "^2.1.15",
|
|
85
|
+
"@djangocfg/typescript-config": "^2.1.15",
|
|
73
86
|
"@types/node": "^24.7.2",
|
|
87
|
+
"@types/prompts": "^2.4.9",
|
|
74
88
|
"@types/react": "^19.0.0",
|
|
75
|
-
"consola": "^3.4.2",
|
|
76
89
|
"swr": "^2.3.7",
|
|
77
90
|
"tsup": "^8.5.0",
|
|
91
|
+
"tsx": "^4.19.2",
|
|
78
92
|
"typescript": "^5.9.3"
|
|
79
93
|
}
|
|
80
94
|
}
|
package/src/cli/index.ts
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DjangoCFG Extension CLI
|
|
3
|
+
*
|
|
4
|
+
* Interactive CLI for managing DjangoCFG extensions
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { consola } from 'consola';
|
|
8
|
+
import chalk from 'chalk';
|
|
9
|
+
import prompts from 'prompts';
|
|
10
|
+
import { readFileSync } from 'fs';
|
|
11
|
+
import { join, dirname } from 'path';
|
|
12
|
+
import { fileURLToPath } from 'url';
|
|
13
|
+
|
|
14
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
15
|
+
|
|
16
|
+
// Read package version
|
|
17
|
+
function getVersion(): string {
|
|
18
|
+
try {
|
|
19
|
+
const pkgPath = join(__dirname, '../../package.json');
|
|
20
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
21
|
+
return pkg.version;
|
|
22
|
+
} catch {
|
|
23
|
+
return '1.0.0';
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// CLI Commands
|
|
28
|
+
const COMMANDS = {
|
|
29
|
+
install: 'Install an extension',
|
|
30
|
+
list: 'List available extensions',
|
|
31
|
+
info: 'Show extension info',
|
|
32
|
+
init: 'Initialize extension in your project',
|
|
33
|
+
help: 'Show help',
|
|
34
|
+
} as const;
|
|
35
|
+
|
|
36
|
+
type Command = keyof typeof COMMANDS;
|
|
37
|
+
|
|
38
|
+
// Available extensions
|
|
39
|
+
const EXTENSIONS = {
|
|
40
|
+
'ext-leads': {
|
|
41
|
+
name: '@djangocfg/ext-leads',
|
|
42
|
+
description: 'Lead management and contact forms',
|
|
43
|
+
features: ['Contact forms', 'Lead tracking', 'Email integration'],
|
|
44
|
+
},
|
|
45
|
+
'ext-payments': {
|
|
46
|
+
name: '@djangocfg/ext-payments',
|
|
47
|
+
description: 'Payment processing with multiple providers',
|
|
48
|
+
features: ['NowPayments', 'Crypto payments', 'Payment tracking'],
|
|
49
|
+
},
|
|
50
|
+
'ext-newsletter': {
|
|
51
|
+
name: '@djangocfg/ext-newsletter',
|
|
52
|
+
description: 'Newsletter and email campaigns',
|
|
53
|
+
features: ['Subscription management', 'Email templates', 'Campaign tracking'],
|
|
54
|
+
},
|
|
55
|
+
'ext-support': {
|
|
56
|
+
name: '@djangocfg/ext-support',
|
|
57
|
+
description: 'Customer support and ticketing',
|
|
58
|
+
features: ['Ticket system', 'Chat support', 'Knowledge base'],
|
|
59
|
+
},
|
|
60
|
+
'ext-knowbase': {
|
|
61
|
+
name: '@djangocfg/ext-knowbase',
|
|
62
|
+
description: 'Knowledge base and documentation',
|
|
63
|
+
features: ['Articles', 'Categories', 'Search', 'Markdown support'],
|
|
64
|
+
},
|
|
65
|
+
} as const;
|
|
66
|
+
|
|
67
|
+
// Print banner
|
|
68
|
+
function printBanner() {
|
|
69
|
+
console.log();
|
|
70
|
+
console.log(chalk.cyan.bold(' 🚀 DjangoCFG Extension Manager'));
|
|
71
|
+
console.log(chalk.gray(` v${getVersion()}`));
|
|
72
|
+
console.log();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Print help
|
|
76
|
+
function printHelp() {
|
|
77
|
+
printBanner();
|
|
78
|
+
|
|
79
|
+
console.log(chalk.yellow.bold('Usage:'));
|
|
80
|
+
console.log(` ${chalk.cyan('djangocfg-ext')} ${chalk.gray('[command]')}`);
|
|
81
|
+
console.log();
|
|
82
|
+
|
|
83
|
+
console.log(chalk.yellow.bold('Commands:'));
|
|
84
|
+
Object.entries(COMMANDS).forEach(([cmd, desc]) => {
|
|
85
|
+
console.log(` ${chalk.cyan(cmd.padEnd(12))} ${chalk.gray(desc)}`);
|
|
86
|
+
});
|
|
87
|
+
console.log();
|
|
88
|
+
|
|
89
|
+
console.log(chalk.yellow.bold('Examples:'));
|
|
90
|
+
console.log(` ${chalk.gray('$')} ${chalk.cyan('djangocfg-ext install')}`);
|
|
91
|
+
console.log(` ${chalk.gray('$')} ${chalk.cyan('djangocfg-ext list')}`);
|
|
92
|
+
console.log(` ${chalk.gray('$')} ${chalk.cyan('djangocfg-ext info ext-leads')}`);
|
|
93
|
+
console.log();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// List extensions
|
|
97
|
+
function listExtensions() {
|
|
98
|
+
printBanner();
|
|
99
|
+
console.log(chalk.yellow.bold('Available Extensions:'));
|
|
100
|
+
console.log();
|
|
101
|
+
|
|
102
|
+
Object.entries(EXTENSIONS).forEach(([key, ext]) => {
|
|
103
|
+
console.log(chalk.cyan.bold(` 📦 ${ext.name}`));
|
|
104
|
+
console.log(chalk.gray(` ${ext.description}`));
|
|
105
|
+
console.log(chalk.gray(` Features: ${ext.features.join(', ')}`));
|
|
106
|
+
console.log();
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Show extension info
|
|
111
|
+
function showInfo(extensionKey: string) {
|
|
112
|
+
const ext = EXTENSIONS[extensionKey as keyof typeof EXTENSIONS];
|
|
113
|
+
|
|
114
|
+
if (!ext) {
|
|
115
|
+
consola.error(`Extension not found: ${extensionKey}`);
|
|
116
|
+
consola.info('Run `djangocfg-ext list` to see available extensions');
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
printBanner();
|
|
121
|
+
console.log(chalk.cyan.bold(`📦 ${ext.name}`));
|
|
122
|
+
console.log();
|
|
123
|
+
console.log(chalk.white(ext.description));
|
|
124
|
+
console.log();
|
|
125
|
+
console.log(chalk.yellow.bold('Features:'));
|
|
126
|
+
ext.features.forEach(feature => {
|
|
127
|
+
console.log(chalk.gray(` • ${feature}`));
|
|
128
|
+
});
|
|
129
|
+
console.log();
|
|
130
|
+
console.log(chalk.yellow.bold('Installation:'));
|
|
131
|
+
console.log(chalk.gray(` pnpm add ${ext.name}`));
|
|
132
|
+
console.log();
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Install extension
|
|
136
|
+
async function installExtension() {
|
|
137
|
+
printBanner();
|
|
138
|
+
|
|
139
|
+
const response = await prompts({
|
|
140
|
+
type: 'select',
|
|
141
|
+
name: 'extension',
|
|
142
|
+
message: 'Which extension would you like to install?',
|
|
143
|
+
choices: Object.entries(EXTENSIONS).map(([key, ext]) => ({
|
|
144
|
+
title: ext.name,
|
|
145
|
+
description: ext.description,
|
|
146
|
+
value: key,
|
|
147
|
+
})),
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
if (!response.extension) {
|
|
151
|
+
consola.info('Installation cancelled');
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const ext = EXTENSIONS[response.extension as keyof typeof EXTENSIONS];
|
|
156
|
+
|
|
157
|
+
console.log();
|
|
158
|
+
consola.info(`Installing ${chalk.cyan(ext.name)}...`);
|
|
159
|
+
console.log();
|
|
160
|
+
|
|
161
|
+
// Show installation command
|
|
162
|
+
console.log(chalk.yellow.bold('Run this command:'));
|
|
163
|
+
console.log(chalk.cyan(` pnpm add ${ext.name}`));
|
|
164
|
+
console.log();
|
|
165
|
+
|
|
166
|
+
console.log(chalk.yellow.bold('Then import in your app:'));
|
|
167
|
+
console.log(chalk.gray(` import { ExtensionProvider } from '${ext.name}';`));
|
|
168
|
+
console.log();
|
|
169
|
+
|
|
170
|
+
consola.success('See documentation at https://djangocfg.com/docs');
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Init extension in project
|
|
174
|
+
async function initExtension() {
|
|
175
|
+
printBanner();
|
|
176
|
+
|
|
177
|
+
console.log(chalk.yellow('This will create extension configuration in your project'));
|
|
178
|
+
console.log();
|
|
179
|
+
|
|
180
|
+
const response = await prompts([
|
|
181
|
+
{
|
|
182
|
+
type: 'select',
|
|
183
|
+
name: 'extension',
|
|
184
|
+
message: 'Which extension do you want to configure?',
|
|
185
|
+
choices: Object.entries(EXTENSIONS).map(([key, ext]) => ({
|
|
186
|
+
title: ext.name,
|
|
187
|
+
description: ext.description,
|
|
188
|
+
value: key,
|
|
189
|
+
})),
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
type: 'confirm',
|
|
193
|
+
name: 'confirm',
|
|
194
|
+
message: 'Generate extension configuration?',
|
|
195
|
+
initial: true,
|
|
196
|
+
},
|
|
197
|
+
]);
|
|
198
|
+
|
|
199
|
+
if (!response.confirm) {
|
|
200
|
+
consola.info('Initialization cancelled');
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const ext = EXTENSIONS[response.extension as keyof typeof EXTENSIONS];
|
|
205
|
+
|
|
206
|
+
console.log();
|
|
207
|
+
consola.info(`Initializing ${chalk.cyan(ext.name)}...`);
|
|
208
|
+
console.log();
|
|
209
|
+
|
|
210
|
+
// Show what to do next
|
|
211
|
+
console.log(chalk.yellow.bold('Next steps:'));
|
|
212
|
+
console.log(chalk.gray(' 1. Install the extension: ') + chalk.cyan(`pnpm add ${ext.name}`));
|
|
213
|
+
console.log(chalk.gray(' 2. Add provider to your layout:'));
|
|
214
|
+
console.log();
|
|
215
|
+
console.log(chalk.gray(' ') + chalk.white('import { ExtensionProvider } from \'' + ext.name + '\';'));
|
|
216
|
+
console.log();
|
|
217
|
+
console.log(chalk.gray(' ') + chalk.white('<ExtensionProvider>'));
|
|
218
|
+
console.log(chalk.gray(' ') + chalk.white('{children}'));
|
|
219
|
+
console.log(chalk.gray(' ') + chalk.white('</ExtensionProvider>'));
|
|
220
|
+
console.log();
|
|
221
|
+
|
|
222
|
+
consola.success('Initialization complete!');
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Main CLI
|
|
226
|
+
async function main() {
|
|
227
|
+
const args = process.argv.slice(2);
|
|
228
|
+
const command = args[0] as Command;
|
|
229
|
+
|
|
230
|
+
// No command - show help
|
|
231
|
+
if (!command) {
|
|
232
|
+
printHelp();
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Handle commands
|
|
237
|
+
switch (command) {
|
|
238
|
+
case 'list':
|
|
239
|
+
listExtensions();
|
|
240
|
+
break;
|
|
241
|
+
|
|
242
|
+
case 'info':
|
|
243
|
+
const extKey = args[1];
|
|
244
|
+
if (!extKey) {
|
|
245
|
+
consola.error('Please specify an extension');
|
|
246
|
+
consola.info('Example: djangocfg-ext info ext-leads');
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
showInfo(extKey);
|
|
250
|
+
break;
|
|
251
|
+
|
|
252
|
+
case 'install':
|
|
253
|
+
await installExtension();
|
|
254
|
+
break;
|
|
255
|
+
|
|
256
|
+
case 'init':
|
|
257
|
+
await initExtension();
|
|
258
|
+
break;
|
|
259
|
+
|
|
260
|
+
case 'help':
|
|
261
|
+
case '--help':
|
|
262
|
+
case '-h':
|
|
263
|
+
printHelp();
|
|
264
|
+
break;
|
|
265
|
+
|
|
266
|
+
default:
|
|
267
|
+
consola.error(`Unknown command: ${command}`);
|
|
268
|
+
console.log();
|
|
269
|
+
printHelp();
|
|
270
|
+
process.exit(1);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
main().catch((error) => {
|
|
275
|
+
consola.error('CLI error:', error);
|
|
276
|
+
process.exit(1);
|
|
277
|
+
});
|