@djangocfg/ext-base 1.0.1 → 1.0.2
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 +102 -26
- package/package.json +9 -7
- package/preview.png +0 -0
- package/src/cli/index.ts +161 -164
- package/src/config.ts +17 -0
- package/src/index.ts +2 -1
- package/src/metadata.ts +73 -0
- package/src/types/context.ts +124 -3
- package/src/utils/createExtensionConfig.ts +139 -0
- package/src/utils/index.ts +5 -0
- package/templates/extension-template/README.md.template +62 -0
- package/templates/extension-template/package.json.template +73 -0
- package/templates/extension-template/preview.png +0 -0
- package/templates/extension-template/src/components/.gitkeep +0 -0
- package/templates/extension-template/src/config.ts +35 -0
- package/templates/extension-template/src/contexts/__PROVIDER_NAME__Context.tsx +41 -0
- package/templates/extension-template/src/contexts/__PROVIDER_NAME__ExtensionProvider.tsx +36 -0
- package/templates/extension-template/src/hooks/index.ts +27 -0
- package/templates/extension-template/src/index.ts +17 -0
- package/templates/extension-template/src/types.ts +7 -0
- package/templates/extension-template/tsconfig.json +8 -0
- package/templates/extension-template/tsup.config.ts +26 -0
package/README.md
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
</div>
|
|
6
|
+
|
|
1
7
|
# @djangocfg/ext-base
|
|
2
8
|
|
|
3
9
|
Base utilities and common code for building DjangoCFG extensions.
|
|
@@ -25,50 +31,61 @@ pnpm add @djangocfg/ext-base
|
|
|
25
31
|
|
|
26
32
|
## CLI Usage
|
|
27
33
|
|
|
28
|
-
The package includes a CLI tool for
|
|
34
|
+
The package includes a CLI tool for creating new DjangoCFG extensions:
|
|
29
35
|
|
|
30
36
|
```bash
|
|
31
|
-
#
|
|
32
|
-
djangocfg-ext
|
|
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
|
|
37
|
+
# Create a new extension with interactive wizard
|
|
38
|
+
djangocfg-ext create
|
|
42
39
|
|
|
43
40
|
# Show help
|
|
44
41
|
djangocfg-ext help
|
|
45
42
|
```
|
|
46
43
|
|
|
47
|
-
|
|
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
|
|
44
|
+
The CLI will guide you through creating a new extension with proper structure and configuration using the `createExtensionConfig` helper.
|
|
54
45
|
|
|
55
46
|
## Quick Start
|
|
56
47
|
|
|
57
48
|
### 1. Create extension metadata
|
|
58
49
|
|
|
50
|
+
Use the `createExtensionConfig` helper to automatically pull data from package.json:
|
|
51
|
+
|
|
59
52
|
```typescript
|
|
60
53
|
// src/config.ts
|
|
61
|
-
import
|
|
54
|
+
import { createExtensionConfig } from '@djangocfg/ext-base';
|
|
55
|
+
import packageJson from '../package.json';
|
|
62
56
|
|
|
63
|
-
export const extensionConfig
|
|
57
|
+
export const extensionConfig = createExtensionConfig(packageJson, {
|
|
64
58
|
name: 'my-extension',
|
|
65
|
-
version: '1.0.0',
|
|
66
59
|
displayName: 'My Extension',
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
60
|
+
icon: 'Rocket', // Lucide icon name
|
|
61
|
+
category: 'utilities',
|
|
62
|
+
features: [
|
|
63
|
+
'Feature 1',
|
|
64
|
+
'Feature 2',
|
|
65
|
+
'Feature 3',
|
|
66
|
+
],
|
|
67
|
+
minVersion: '2.0.0',
|
|
68
|
+
examples: [
|
|
69
|
+
{
|
|
70
|
+
title: 'Basic Usage',
|
|
71
|
+
description: 'How to use this extension',
|
|
72
|
+
code: `import { MyComponent } from '@your-org/my-extension';`,
|
|
73
|
+
language: 'tsx',
|
|
74
|
+
},
|
|
75
|
+
],
|
|
76
|
+
});
|
|
70
77
|
```
|
|
71
78
|
|
|
79
|
+
This automatically imports from package.json:
|
|
80
|
+
- version
|
|
81
|
+
- author
|
|
82
|
+
- description
|
|
83
|
+
- license
|
|
84
|
+
- homepage
|
|
85
|
+
- githubUrl (from repository)
|
|
86
|
+
- keywords
|
|
87
|
+
- peerDependencies
|
|
88
|
+
|
|
72
89
|
### 2. Create extension provider
|
|
73
90
|
|
|
74
91
|
```typescript
|
|
@@ -103,6 +120,52 @@ export default function RootLayout({ children }) {
|
|
|
103
120
|
|
|
104
121
|
## Core Features
|
|
105
122
|
|
|
123
|
+
### Extension Config Helper
|
|
124
|
+
|
|
125
|
+
The `createExtensionConfig` helper creates a typed extension configuration by combining package.json data with manual metadata:
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
import { createExtensionConfig, type ExtensionConfigInput } from '@djangocfg/ext-base';
|
|
129
|
+
import packageJson from '../package.json';
|
|
130
|
+
|
|
131
|
+
export const extensionConfig = createExtensionConfig(packageJson, {
|
|
132
|
+
// Required fields
|
|
133
|
+
name: 'my-extension',
|
|
134
|
+
displayName: 'My Extension',
|
|
135
|
+
icon: 'Package', // Lucide icon name
|
|
136
|
+
category: 'utilities', // 'forms' | 'payments' | 'content' | 'support' | 'utilities' | 'analytics' | 'security' | 'integration' | 'other'
|
|
137
|
+
features: ['Feature list for marketplace'],
|
|
138
|
+
|
|
139
|
+
// Optional fields
|
|
140
|
+
minVersion: '2.0.0',
|
|
141
|
+
githubStars: 100,
|
|
142
|
+
relatedExtensions: ['other-extension'],
|
|
143
|
+
examples: [
|
|
144
|
+
{
|
|
145
|
+
title: 'Example title',
|
|
146
|
+
description: 'Example description',
|
|
147
|
+
code: 'import { Component } from "@your-org/my-extension";',
|
|
148
|
+
language: 'tsx',
|
|
149
|
+
},
|
|
150
|
+
],
|
|
151
|
+
});
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
**Automatically imported from package.json:**
|
|
155
|
+
- `version` - Package version
|
|
156
|
+
- `author` - Author name (from string or object)
|
|
157
|
+
- `description` - Package description
|
|
158
|
+
- `license` - License type
|
|
159
|
+
- `homepage` - Homepage URL
|
|
160
|
+
- `githubUrl` - Repository URL
|
|
161
|
+
- `keywords` - Keywords array
|
|
162
|
+
- `peerDependencies` - Peer dependencies
|
|
163
|
+
|
|
164
|
+
**Auto-generated:**
|
|
165
|
+
- `npmUrl` - npm package URL
|
|
166
|
+
- `installCommand` - pnpm install command
|
|
167
|
+
- `tags` - Same as keywords
|
|
168
|
+
|
|
106
169
|
### Environment Configuration
|
|
107
170
|
|
|
108
171
|
```typescript
|
|
@@ -205,12 +268,22 @@ function MyComponent() {
|
|
|
205
268
|
|
|
206
269
|
```typescript
|
|
207
270
|
import type {
|
|
271
|
+
// Extension configuration
|
|
208
272
|
ExtensionMetadata,
|
|
273
|
+
ExtensionConfigInput,
|
|
274
|
+
ExtensionCategory,
|
|
275
|
+
ExtensionExample,
|
|
276
|
+
|
|
277
|
+
// Provider
|
|
209
278
|
ExtensionProviderProps,
|
|
279
|
+
|
|
280
|
+
// Pagination
|
|
210
281
|
PaginatedResponse,
|
|
211
282
|
PaginationParams,
|
|
212
283
|
PaginationState,
|
|
213
284
|
InfinitePaginationReturn,
|
|
285
|
+
|
|
286
|
+
// Utilities
|
|
214
287
|
ExtensionLogger,
|
|
215
288
|
ExtensionError,
|
|
216
289
|
} from '@djangocfg/ext-base';
|
|
@@ -218,8 +291,11 @@ import type {
|
|
|
218
291
|
|
|
219
292
|
## Best Practices
|
|
220
293
|
|
|
294
|
+
- Use `createExtensionConfig` helper to maintain Single Source of Truth from package.json
|
|
221
295
|
- Always wrap your extension with `ExtensionProvider` for proper registration
|
|
222
|
-
-
|
|
296
|
+
- Use Lucide icon names (not emoji) for `icon` field
|
|
297
|
+
- Include comprehensive `features` list for marketplace visibility
|
|
298
|
+
- Provide code `examples` with proper syntax highlighting
|
|
223
299
|
- Use provided pagination hooks for consistent data fetching
|
|
224
300
|
- Use `createExtensionLogger` with consistent tags for structured logging
|
|
225
301
|
- Separate client-only code using `/hooks` entry point
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@djangocfg/ext-base",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Base utilities and common code for DjangoCFG extensions",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"django",
|
|
@@ -27,33 +27,35 @@
|
|
|
27
27
|
"license": "MIT",
|
|
28
28
|
"type": "module",
|
|
29
29
|
"main": "./dist/index.cjs",
|
|
30
|
-
"module": "./dist/index.
|
|
30
|
+
"module": "./dist/index.js",
|
|
31
31
|
"types": "./dist/index.d.ts",
|
|
32
32
|
"exports": {
|
|
33
33
|
".": {
|
|
34
34
|
"types": "./dist/index.d.ts",
|
|
35
|
-
"import": "./dist/index.
|
|
35
|
+
"import": "./dist/index.js",
|
|
36
36
|
"require": "./dist/index.cjs"
|
|
37
37
|
},
|
|
38
38
|
"./hooks": {
|
|
39
39
|
"types": "./dist/hooks.d.ts",
|
|
40
|
-
"import": "./dist/hooks.
|
|
40
|
+
"import": "./dist/hooks.js",
|
|
41
41
|
"require": "./dist/hooks.cjs"
|
|
42
42
|
},
|
|
43
43
|
"./auth": {
|
|
44
44
|
"types": "./dist/auth.d.ts",
|
|
45
|
-
"import": "./dist/auth.
|
|
45
|
+
"import": "./dist/auth.js",
|
|
46
46
|
"require": "./dist/auth.cjs"
|
|
47
47
|
},
|
|
48
48
|
"./api": {
|
|
49
49
|
"types": "./dist/api.d.ts",
|
|
50
|
-
"import": "./dist/api.
|
|
50
|
+
"import": "./dist/api.js",
|
|
51
51
|
"require": "./dist/api.cjs"
|
|
52
52
|
}
|
|
53
53
|
},
|
|
54
54
|
"files": [
|
|
55
55
|
"dist",
|
|
56
|
-
"src"
|
|
56
|
+
"src",
|
|
57
|
+
"preview.png",
|
|
58
|
+
"templates"
|
|
57
59
|
],
|
|
58
60
|
"bin": {
|
|
59
61
|
"djangocfg-ext": "./dist/cli.mjs"
|
package/preview.png
ADDED
|
Binary file
|
package/src/cli/index.ts
CHANGED
|
@@ -7,9 +7,10 @@
|
|
|
7
7
|
import { consola } from 'consola';
|
|
8
8
|
import chalk from 'chalk';
|
|
9
9
|
import prompts from 'prompts';
|
|
10
|
-
import { readFileSync } from 'fs';
|
|
10
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync, readdirSync, statSync } from 'fs';
|
|
11
11
|
import { join, dirname } from 'path';
|
|
12
12
|
import { fileURLToPath } from 'url';
|
|
13
|
+
import { EXTENSION_CATEGORIES } from '../types/context';
|
|
13
14
|
|
|
14
15
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
15
16
|
|
|
@@ -26,43 +27,62 @@ function getVersion(): string {
|
|
|
26
27
|
|
|
27
28
|
// CLI Commands
|
|
28
29
|
const COMMANDS = {
|
|
29
|
-
|
|
30
|
-
list: 'List available extensions',
|
|
31
|
-
info: 'Show extension info',
|
|
32
|
-
init: 'Initialize extension in your project',
|
|
30
|
+
create: 'Create a new DjangoCFG extension',
|
|
33
31
|
help: 'Show help',
|
|
32
|
+
'--help': 'Show help',
|
|
33
|
+
'-h': 'Show help',
|
|
34
34
|
} as const;
|
|
35
35
|
|
|
36
36
|
type Command = keyof typeof COMMANDS;
|
|
37
37
|
|
|
38
|
-
//
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
38
|
+
// Template helpers
|
|
39
|
+
function replacePlaceholders(content: string, replacements: Record<string, string>): string {
|
|
40
|
+
let result = content;
|
|
41
|
+
for (const [key, value] of Object.entries(replacements)) {
|
|
42
|
+
result = result.replaceAll(key, value);
|
|
43
|
+
}
|
|
44
|
+
return result;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function copyTemplateRecursive(src: string, dest: string, replacements: Record<string, string>) {
|
|
48
|
+
if (!existsSync(src)) {
|
|
49
|
+
throw new Error(`Template not found: ${src}`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const stats = statSync(src);
|
|
53
|
+
|
|
54
|
+
if (stats.isDirectory()) {
|
|
55
|
+
// Create destination directory
|
|
56
|
+
if (!existsSync(dest)) {
|
|
57
|
+
mkdirSync(dest, { recursive: true });
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Copy all files recursively
|
|
61
|
+
const files = readdirSync(src);
|
|
62
|
+
for (const file of files) {
|
|
63
|
+
const srcPath = join(src, file);
|
|
64
|
+
let destFile = file;
|
|
65
|
+
|
|
66
|
+
// Replace __PROVIDER_NAME__ in filenames
|
|
67
|
+
if (file.includes('__PROVIDER_NAME__')) {
|
|
68
|
+
destFile = file.replaceAll('__PROVIDER_NAME__', replacements['__PROVIDER_NAME__']);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Remove .template extension
|
|
72
|
+
if (destFile.endsWith('.template')) {
|
|
73
|
+
destFile = destFile.slice(0, -9); // Remove '.template'
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const destPath = join(dest, destFile);
|
|
77
|
+
copyTemplateRecursive(srcPath, destPath, replacements);
|
|
78
|
+
}
|
|
79
|
+
} else {
|
|
80
|
+
// Copy file with placeholder replacement
|
|
81
|
+
const content = readFileSync(src, 'utf-8');
|
|
82
|
+
const replaced = replacePlaceholders(content, replacements);
|
|
83
|
+
writeFileSync(dest, replaced, 'utf-8');
|
|
84
|
+
}
|
|
85
|
+
}
|
|
66
86
|
|
|
67
87
|
// Print banner
|
|
68
88
|
function printBanner() {
|
|
@@ -87,139 +107,134 @@ function printHelp() {
|
|
|
87
107
|
console.log();
|
|
88
108
|
|
|
89
109
|
console.log(chalk.yellow.bold('Examples:'));
|
|
90
|
-
console.log(` ${chalk.gray('$')} ${chalk.cyan('djangocfg-ext
|
|
91
|
-
console.log(` ${chalk.gray('$')} ${chalk.cyan('djangocfg-ext list')}`);
|
|
92
|
-
console.log(` ${chalk.gray('$')} ${chalk.cyan('djangocfg-ext info ext-leads')}`);
|
|
110
|
+
console.log(` ${chalk.gray('$')} ${chalk.cyan('djangocfg-ext create')}`);
|
|
93
111
|
console.log();
|
|
94
112
|
}
|
|
95
113
|
|
|
96
|
-
//
|
|
97
|
-
function
|
|
114
|
+
// Create new extension
|
|
115
|
+
async function createExtension() {
|
|
98
116
|
printBanner();
|
|
99
|
-
console.log(chalk.yellow.bold('Available Extensions:'));
|
|
100
|
-
console.log();
|
|
101
117
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
console.log(chalk.gray(` ${ext.description}`));
|
|
105
|
-
console.log(chalk.gray(` Features: ${ext.features.join(', ')}`));
|
|
106
|
-
console.log();
|
|
107
|
-
});
|
|
108
|
-
}
|
|
118
|
+
console.log(chalk.yellow('Create a new DjangoCFG extension'));
|
|
119
|
+
console.log();
|
|
109
120
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
121
|
+
const response = await prompts([
|
|
122
|
+
{
|
|
123
|
+
type: 'text',
|
|
124
|
+
name: 'name',
|
|
125
|
+
message: 'Extension name (e.g., "leads", "payments"):',
|
|
126
|
+
validate: (value: string) => {
|
|
127
|
+
if (!value) return 'Extension name is required';
|
|
128
|
+
if (!/^[a-z][a-z0-9-]*$/.test(value)) {
|
|
129
|
+
return 'Extension name must start with a letter and contain only lowercase letters, numbers, and hyphens';
|
|
130
|
+
}
|
|
131
|
+
return true;
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
type: 'text',
|
|
136
|
+
name: 'displayName',
|
|
137
|
+
message: 'Display name (e.g., "Leads & Forms"):',
|
|
138
|
+
validate: (value: string) => value ? true : 'Display name is required',
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
type: 'text',
|
|
142
|
+
name: 'description',
|
|
143
|
+
message: 'Description:',
|
|
144
|
+
validate: (value: string) => value ? true : 'Description is required',
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
type: 'text',
|
|
148
|
+
name: 'icon',
|
|
149
|
+
message: 'Lucide icon name (e.g., "Mail", "CreditCard"):',
|
|
150
|
+
initial: 'Package',
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
type: 'select',
|
|
154
|
+
name: 'category',
|
|
155
|
+
message: 'Category:',
|
|
156
|
+
choices: EXTENSION_CATEGORIES,
|
|
157
|
+
},
|
|
158
|
+
]);
|
|
113
159
|
|
|
114
|
-
if (!
|
|
115
|
-
consola.
|
|
116
|
-
consola.info('Run `djangocfg-ext list` to see available extensions');
|
|
160
|
+
if (!response.name) {
|
|
161
|
+
consola.info('Extension creation cancelled');
|
|
117
162
|
return;
|
|
118
163
|
}
|
|
119
164
|
|
|
120
|
-
|
|
121
|
-
|
|
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
|
-
});
|
|
165
|
+
const extName = `ext-${response.name}`;
|
|
166
|
+
const extDir = join(process.cwd(), 'extensions', extName);
|
|
149
167
|
|
|
150
|
-
if
|
|
151
|
-
|
|
168
|
+
// Check if extension already exists
|
|
169
|
+
if (existsSync(extDir)) {
|
|
170
|
+
consola.error(`Extension already exists: ${extDir}`);
|
|
152
171
|
return;
|
|
153
172
|
}
|
|
154
173
|
|
|
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
174
|
console.log();
|
|
175
|
+
consola.start(`Creating extension: ${chalk.cyan(`@djangocfg/${extName}`)}`);
|
|
165
176
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
177
|
+
try {
|
|
178
|
+
const providerName = response.displayName.replace(/[^a-zA-Z]/g, '');
|
|
179
|
+
|
|
180
|
+
// Prepare replacements
|
|
181
|
+
const replacements = {
|
|
182
|
+
'__NAME__': response.name,
|
|
183
|
+
'__DISPLAY_NAME__': response.displayName,
|
|
184
|
+
'__DESCRIPTION__': response.description,
|
|
185
|
+
'__ICON__': response.icon,
|
|
186
|
+
'__CATEGORY__': response.category,
|
|
187
|
+
'__PROVIDER_NAME__': providerName,
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
// Find template directory
|
|
191
|
+
const templatePaths = [
|
|
192
|
+
// When installed from npm
|
|
193
|
+
join(__dirname, '../templates/extension-template'),
|
|
194
|
+
// Workspace path (for development)
|
|
195
|
+
join(process.cwd(), 'extensions', 'ext-base', 'templates', 'extension-template'),
|
|
196
|
+
];
|
|
197
|
+
|
|
198
|
+
let templateDir: string | null = null;
|
|
199
|
+
for (const path of templatePaths) {
|
|
200
|
+
if (existsSync(path)) {
|
|
201
|
+
templateDir = path;
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
169
205
|
|
|
170
|
-
|
|
171
|
-
|
|
206
|
+
if (!templateDir) {
|
|
207
|
+
throw new Error('Extension template not found');
|
|
208
|
+
}
|
|
172
209
|
|
|
173
|
-
//
|
|
174
|
-
|
|
175
|
-
|
|
210
|
+
// Copy template with replacements
|
|
211
|
+
consola.start('Copying template files...');
|
|
212
|
+
copyTemplateRecursive(templateDir, extDir, replacements);
|
|
213
|
+
consola.success('Extension files created');
|
|
176
214
|
|
|
177
|
-
|
|
178
|
-
|
|
215
|
+
console.log();
|
|
216
|
+
consola.success(`Extension created successfully: ${chalk.cyan(extDir)}`);
|
|
217
|
+
console.log();
|
|
179
218
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
name: 'confirm',
|
|
194
|
-
message: 'Generate extension configuration?',
|
|
195
|
-
initial: true,
|
|
196
|
-
},
|
|
197
|
-
]);
|
|
219
|
+
console.log(chalk.yellow.bold('Next steps:'));
|
|
220
|
+
console.log();
|
|
221
|
+
console.log(chalk.gray('1. Install dependencies:'));
|
|
222
|
+
console.log(chalk.cyan(` cd ${extDir} && pnpm install`));
|
|
223
|
+
console.log();
|
|
224
|
+
console.log(chalk.gray('2. Build the extension:'));
|
|
225
|
+
console.log(chalk.cyan(` pnpm build`));
|
|
226
|
+
console.log();
|
|
227
|
+
console.log(chalk.gray('3. Add your features and customize:'));
|
|
228
|
+
console.log(chalk.cyan(` - Edit src/config.ts to add features`));
|
|
229
|
+
console.log(chalk.cyan(` - Add components, hooks, and utilities`));
|
|
230
|
+
console.log(chalk.cyan(` - Update README.md with usage examples`));
|
|
231
|
+
console.log();
|
|
198
232
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
233
|
+
consola.info('Documentation: https://djangocfg.com/docs');
|
|
234
|
+
} catch (error) {
|
|
235
|
+
consola.error('Failed to create extension:', error);
|
|
236
|
+
process.exit(1);
|
|
202
237
|
}
|
|
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
238
|
}
|
|
224
239
|
|
|
225
240
|
// Main CLI
|
|
@@ -235,26 +250,8 @@ async function main() {
|
|
|
235
250
|
|
|
236
251
|
// Handle commands
|
|
237
252
|
switch (command) {
|
|
238
|
-
case '
|
|
239
|
-
|
|
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();
|
|
253
|
+
case 'create':
|
|
254
|
+
await createExtension();
|
|
258
255
|
break;
|
|
259
256
|
|
|
260
257
|
case 'help':
|
package/src/config.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extension configuration and environment utilities
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export const isDevelopment = process.env.NODE_ENV === 'development';
|
|
6
|
+
export const isProduction = process.env.NODE_ENV === 'production';
|
|
7
|
+
export const isStaticBuild = process.env.STATIC_BUILD === 'true';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Get API URL from environment or default
|
|
11
|
+
*/
|
|
12
|
+
export function getApiUrl(): string {
|
|
13
|
+
return process.env.NEXT_PUBLIC_API_URL || '/api';
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Re-export metadata
|
|
17
|
+
export * from './metadata';
|
package/src/index.ts
CHANGED