@djangocfg/ext-base 1.0.0 → 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 +140 -176
- package/package.json +28 -12
- package/preview.png +0 -0
- package/src/cli/index.ts +274 -0
- 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/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/src/cli/index.ts
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
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, writeFileSync, mkdirSync, existsSync, readdirSync, statSync } from 'fs';
|
|
11
|
+
import { join, dirname } from 'path';
|
|
12
|
+
import { fileURLToPath } from 'url';
|
|
13
|
+
import { EXTENSION_CATEGORIES } from '../types/context';
|
|
14
|
+
|
|
15
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
16
|
+
|
|
17
|
+
// Read package version
|
|
18
|
+
function getVersion(): string {
|
|
19
|
+
try {
|
|
20
|
+
const pkgPath = join(__dirname, '../../package.json');
|
|
21
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
22
|
+
return pkg.version;
|
|
23
|
+
} catch {
|
|
24
|
+
return '1.0.0';
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// CLI Commands
|
|
29
|
+
const COMMANDS = {
|
|
30
|
+
create: 'Create a new DjangoCFG extension',
|
|
31
|
+
help: 'Show help',
|
|
32
|
+
'--help': 'Show help',
|
|
33
|
+
'-h': 'Show help',
|
|
34
|
+
} as const;
|
|
35
|
+
|
|
36
|
+
type Command = keyof typeof COMMANDS;
|
|
37
|
+
|
|
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
|
+
}
|
|
86
|
+
|
|
87
|
+
// Print banner
|
|
88
|
+
function printBanner() {
|
|
89
|
+
console.log();
|
|
90
|
+
console.log(chalk.cyan.bold(' 🚀 DjangoCFG Extension Manager'));
|
|
91
|
+
console.log(chalk.gray(` v${getVersion()}`));
|
|
92
|
+
console.log();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Print help
|
|
96
|
+
function printHelp() {
|
|
97
|
+
printBanner();
|
|
98
|
+
|
|
99
|
+
console.log(chalk.yellow.bold('Usage:'));
|
|
100
|
+
console.log(` ${chalk.cyan('djangocfg-ext')} ${chalk.gray('[command]')}`);
|
|
101
|
+
console.log();
|
|
102
|
+
|
|
103
|
+
console.log(chalk.yellow.bold('Commands:'));
|
|
104
|
+
Object.entries(COMMANDS).forEach(([cmd, desc]) => {
|
|
105
|
+
console.log(` ${chalk.cyan(cmd.padEnd(12))} ${chalk.gray(desc)}`);
|
|
106
|
+
});
|
|
107
|
+
console.log();
|
|
108
|
+
|
|
109
|
+
console.log(chalk.yellow.bold('Examples:'));
|
|
110
|
+
console.log(` ${chalk.gray('$')} ${chalk.cyan('djangocfg-ext create')}`);
|
|
111
|
+
console.log();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Create new extension
|
|
115
|
+
async function createExtension() {
|
|
116
|
+
printBanner();
|
|
117
|
+
|
|
118
|
+
console.log(chalk.yellow('Create a new DjangoCFG extension'));
|
|
119
|
+
console.log();
|
|
120
|
+
|
|
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
|
+
]);
|
|
159
|
+
|
|
160
|
+
if (!response.name) {
|
|
161
|
+
consola.info('Extension creation cancelled');
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const extName = `ext-${response.name}`;
|
|
166
|
+
const extDir = join(process.cwd(), 'extensions', extName);
|
|
167
|
+
|
|
168
|
+
// Check if extension already exists
|
|
169
|
+
if (existsSync(extDir)) {
|
|
170
|
+
consola.error(`Extension already exists: ${extDir}`);
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
console.log();
|
|
175
|
+
consola.start(`Creating extension: ${chalk.cyan(`@djangocfg/${extName}`)}`);
|
|
176
|
+
|
|
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
|
+
}
|
|
205
|
+
|
|
206
|
+
if (!templateDir) {
|
|
207
|
+
throw new Error('Extension template not found');
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Copy template with replacements
|
|
211
|
+
consola.start('Copying template files...');
|
|
212
|
+
copyTemplateRecursive(templateDir, extDir, replacements);
|
|
213
|
+
consola.success('Extension files created');
|
|
214
|
+
|
|
215
|
+
console.log();
|
|
216
|
+
consola.success(`Extension created successfully: ${chalk.cyan(extDir)}`);
|
|
217
|
+
console.log();
|
|
218
|
+
|
|
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();
|
|
232
|
+
|
|
233
|
+
consola.info('Documentation: https://djangocfg.com/docs');
|
|
234
|
+
} catch (error) {
|
|
235
|
+
consola.error('Failed to create extension:', error);
|
|
236
|
+
process.exit(1);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Main CLI
|
|
241
|
+
async function main() {
|
|
242
|
+
const args = process.argv.slice(2);
|
|
243
|
+
const command = args[0] as Command;
|
|
244
|
+
|
|
245
|
+
// No command - show help
|
|
246
|
+
if (!command) {
|
|
247
|
+
printHelp();
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Handle commands
|
|
252
|
+
switch (command) {
|
|
253
|
+
case 'create':
|
|
254
|
+
await createExtension();
|
|
255
|
+
break;
|
|
256
|
+
|
|
257
|
+
case 'help':
|
|
258
|
+
case '--help':
|
|
259
|
+
case '-h':
|
|
260
|
+
printHelp();
|
|
261
|
+
break;
|
|
262
|
+
|
|
263
|
+
default:
|
|
264
|
+
consola.error(`Unknown command: ${command}`);
|
|
265
|
+
console.log();
|
|
266
|
+
printHelp();
|
|
267
|
+
process.exit(1);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
main().catch((error) => {
|
|
272
|
+
consola.error('CLI error:', error);
|
|
273
|
+
process.exit(1);
|
|
274
|
+
});
|
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
package/src/metadata.ts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extension Base configuration
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { ExtensionMetadata } from './types';
|
|
6
|
+
import packageJson from '../package.json';
|
|
7
|
+
|
|
8
|
+
export const extensionConfig: ExtensionMetadata = {
|
|
9
|
+
name: 'base',
|
|
10
|
+
version: packageJson.version,
|
|
11
|
+
author: 'DjangoCFG Team',
|
|
12
|
+
displayName: 'Extension Base',
|
|
13
|
+
description: 'Base utilities and common code for building DjangoCFG extensions. Includes CLI tool for managing extensions.',
|
|
14
|
+
icon: 'Wrench',
|
|
15
|
+
license: 'MIT',
|
|
16
|
+
githubUrl: 'https://github.com/markolofsen/django-cfg',
|
|
17
|
+
homepage: 'https://djangocfg.com',
|
|
18
|
+
keywords: ['base', 'utilities', 'cli', 'toolkit', 'helpers', 'hooks'],
|
|
19
|
+
|
|
20
|
+
// Marketplace metadata
|
|
21
|
+
category: 'utilities',
|
|
22
|
+
tags: ['base', 'utilities', 'cli', 'toolkit'],
|
|
23
|
+
features: [
|
|
24
|
+
'Extension registration system',
|
|
25
|
+
'React hooks for pagination and infinite scroll',
|
|
26
|
+
'Environment utilities and helpers',
|
|
27
|
+
'CLI tool for extension management',
|
|
28
|
+
'Type-safe context helpers',
|
|
29
|
+
'Logger utilities',
|
|
30
|
+
],
|
|
31
|
+
npmUrl: `https://www.npmjs.com/package/${packageJson.name}`,
|
|
32
|
+
installCommand: `pnpm add ${packageJson.name}`,
|
|
33
|
+
peerDependencies: packageJson.peerDependencies,
|
|
34
|
+
examples: [
|
|
35
|
+
{
|
|
36
|
+
title: 'Basic Extension Setup',
|
|
37
|
+
description: 'Initialize a new DjangoCFG extension',
|
|
38
|
+
code: `import { createExtension } from '@djangocfg/ext-base';
|
|
39
|
+
|
|
40
|
+
const myExtension = createExtension({
|
|
41
|
+
name: 'my-extension',
|
|
42
|
+
version: '1.0.0',
|
|
43
|
+
register: (app) => {
|
|
44
|
+
console.log('Extension registered!');
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
export default myExtension;`,
|
|
49
|
+
language: 'typescript',
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
title: 'Using Pagination Hook',
|
|
53
|
+
description: 'Implement infinite scroll with usePagination',
|
|
54
|
+
code: `import { usePagination } from '@djangocfg/ext-base';
|
|
55
|
+
|
|
56
|
+
function DataList() {
|
|
57
|
+
const { data, loading, hasMore, loadMore } = usePagination({
|
|
58
|
+
fetchFn: (page) => fetch(\`/api/data?page=\${page}\`),
|
|
59
|
+
pageSize: 20,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<div>
|
|
64
|
+
{data.map(item => <div key={item.id}>{item.name}</div>)}
|
|
65
|
+
{hasMore && <button onClick={loadMore}>Load More</button>}
|
|
66
|
+
</div>
|
|
67
|
+
);
|
|
68
|
+
}`,
|
|
69
|
+
language: 'tsx',
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
githubStars: 245,
|
|
73
|
+
};
|
package/src/types/context.ts
CHANGED
|
@@ -10,6 +10,60 @@ export interface ExtensionContextOptions {
|
|
|
10
10
|
revalidateIfStale?: boolean;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Extension category types
|
|
15
|
+
*/
|
|
16
|
+
export type ExtensionCategory =
|
|
17
|
+
| 'forms' // Forms, CRM, Lead Management
|
|
18
|
+
| 'payments' // Payment Processing, Billing
|
|
19
|
+
| 'content' // Content Management, Marketing
|
|
20
|
+
| 'support' // Support, Helpdesk, Tickets
|
|
21
|
+
| 'utilities' // Tools, Base, Infrastructure
|
|
22
|
+
| 'analytics' // Analytics, Tracking, Monitoring
|
|
23
|
+
| 'security' // Security, Authentication, Authorization
|
|
24
|
+
| 'integration' // Third-party Integrations
|
|
25
|
+
| 'other'; // Other/Uncategorized
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Extension categories with display names for CLI and UI
|
|
29
|
+
*/
|
|
30
|
+
export const EXTENSION_CATEGORIES: Array<{ title: string; value: ExtensionCategory }> = [
|
|
31
|
+
{ title: 'Forms', value: 'forms' },
|
|
32
|
+
{ title: 'Payments', value: 'payments' },
|
|
33
|
+
{ title: 'Content', value: 'content' },
|
|
34
|
+
{ title: 'Support', value: 'support' },
|
|
35
|
+
{ title: 'Utilities', value: 'utilities' },
|
|
36
|
+
{ title: 'Analytics', value: 'analytics' },
|
|
37
|
+
{ title: 'Security', value: 'security' },
|
|
38
|
+
{ title: 'Integration', value: 'integration' },
|
|
39
|
+
{ title: 'Other', value: 'other' },
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Code example for extension documentation
|
|
44
|
+
*/
|
|
45
|
+
export interface ExtensionExample {
|
|
46
|
+
/**
|
|
47
|
+
* Example title
|
|
48
|
+
*/
|
|
49
|
+
title: string;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Example description
|
|
53
|
+
*/
|
|
54
|
+
description?: string;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Code snippet
|
|
58
|
+
*/
|
|
59
|
+
code: string;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Programming language for syntax highlighting
|
|
63
|
+
*/
|
|
64
|
+
language: string;
|
|
65
|
+
}
|
|
66
|
+
|
|
13
67
|
export interface ExtensionMetadata {
|
|
14
68
|
/**
|
|
15
69
|
* Unique extension name (e.g., 'newsletter', 'payments')
|
|
@@ -58,13 +112,13 @@ export interface ExtensionMetadata {
|
|
|
58
112
|
keywords?: string[];
|
|
59
113
|
|
|
60
114
|
/**
|
|
61
|
-
* Extension icon
|
|
62
|
-
* @example '
|
|
115
|
+
* Extension icon - Lucide icon name or emoji
|
|
116
|
+
* @example 'Mail' or '📧'
|
|
63
117
|
*/
|
|
64
118
|
icon?: string;
|
|
65
119
|
|
|
66
120
|
/**
|
|
67
|
-
* List of extension dependencies
|
|
121
|
+
* List of extension dependencies (other extension names)
|
|
68
122
|
* @example ['payments', 'auth']
|
|
69
123
|
*/
|
|
70
124
|
dependencies?: string[];
|
|
@@ -73,6 +127,73 @@ export interface ExtensionMetadata {
|
|
|
73
127
|
* Minimum required DjangoCFG version
|
|
74
128
|
*/
|
|
75
129
|
minVersion?: string;
|
|
130
|
+
|
|
131
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
132
|
+
// Marketplace-specific fields
|
|
133
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Extension category for marketplace organization
|
|
137
|
+
*/
|
|
138
|
+
category?: ExtensionCategory;
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Tags for search and filtering
|
|
142
|
+
*/
|
|
143
|
+
tags?: string[];
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* List of key features
|
|
147
|
+
*/
|
|
148
|
+
features?: string[];
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* npm package URL
|
|
152
|
+
* @example 'https://www.npmjs.com/package/@djangocfg/ext-newsletter'
|
|
153
|
+
*/
|
|
154
|
+
npmUrl?: string;
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Installation command
|
|
158
|
+
* @example 'pnpm add @djangocfg/ext-newsletter'
|
|
159
|
+
*/
|
|
160
|
+
installCommand?: string;
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Code examples for documentation
|
|
164
|
+
*/
|
|
165
|
+
examples?: ExtensionExample[];
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* npm package dependencies
|
|
169
|
+
*/
|
|
170
|
+
packageDependencies?: Record<string, string>;
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* npm peer dependencies
|
|
174
|
+
*/
|
|
175
|
+
peerDependencies?: Record<string, string>;
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Related extension IDs
|
|
179
|
+
*/
|
|
180
|
+
relatedExtensions?: string[];
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* README content in markdown
|
|
184
|
+
*/
|
|
185
|
+
readme?: string;
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Preview image URL (1200x630 recommended)
|
|
189
|
+
* @example 'https://unpkg.com/@djangocfg/ext-leads@latest/preview.svg'
|
|
190
|
+
*/
|
|
191
|
+
preview?: string;
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* GitHub stars count
|
|
195
|
+
*/
|
|
196
|
+
githubStars?: number;
|
|
76
197
|
}
|
|
77
198
|
|
|
78
199
|
export interface ExtensionProviderProps {
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helper to create extension configuration from package.json
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { ExtensionMetadata } from '../types';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Package.json structure
|
|
9
|
+
*/
|
|
10
|
+
interface PackageJson {
|
|
11
|
+
name: string;
|
|
12
|
+
version: string;
|
|
13
|
+
description?: string;
|
|
14
|
+
keywords?: string[];
|
|
15
|
+
author?: string | { name: string; email?: string; url?: string };
|
|
16
|
+
license?: string;
|
|
17
|
+
homepage?: string;
|
|
18
|
+
repository?: string | { type: string; url: string; directory?: string };
|
|
19
|
+
peerDependencies?: Record<string, string>;
|
|
20
|
+
dependencies?: Record<string, string>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Manual metadata fields that must be provided
|
|
25
|
+
*/
|
|
26
|
+
export interface ExtensionConfigInput {
|
|
27
|
+
/**
|
|
28
|
+
* Extension short name (e.g., 'leads', 'payments')
|
|
29
|
+
*/
|
|
30
|
+
name: string;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Display name for marketplace
|
|
34
|
+
*/
|
|
35
|
+
displayName: string;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Lucide icon name
|
|
39
|
+
* @example 'FileText', 'CreditCard', 'Mail'
|
|
40
|
+
*/
|
|
41
|
+
icon: string;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Extension category
|
|
45
|
+
*/
|
|
46
|
+
category: ExtensionMetadata['category'];
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Key features list
|
|
50
|
+
*/
|
|
51
|
+
features: string[];
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Code examples (optional)
|
|
55
|
+
*/
|
|
56
|
+
examples?: ExtensionMetadata['examples'];
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Minimum required DjangoCFG version (optional)
|
|
60
|
+
*/
|
|
61
|
+
minVersion?: string;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Related extension IDs (optional)
|
|
65
|
+
*/
|
|
66
|
+
relatedExtensions?: string[];
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* GitHub stars count (optional)
|
|
70
|
+
*/
|
|
71
|
+
githubStars?: number;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Create extension configuration from package.json + manual metadata
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```ts
|
|
79
|
+
* import packageJson from '../package.json';
|
|
80
|
+
* import { createExtensionConfig } from '@djangocfg/ext-base';
|
|
81
|
+
*
|
|
82
|
+
* export const extensionConfig = createExtensionConfig(packageJson, {
|
|
83
|
+
* name: 'leads',
|
|
84
|
+
* displayName: 'Leads & Forms',
|
|
85
|
+
* icon: 'FileText',
|
|
86
|
+
* category: 'forms',
|
|
87
|
+
* features: [
|
|
88
|
+
* 'Contact form components',
|
|
89
|
+
* 'Lead tracking',
|
|
90
|
+
* ],
|
|
91
|
+
* });
|
|
92
|
+
* ```
|
|
93
|
+
*/
|
|
94
|
+
export function createExtensionConfig(
|
|
95
|
+
packageJson: PackageJson,
|
|
96
|
+
config: ExtensionConfigInput
|
|
97
|
+
): ExtensionMetadata {
|
|
98
|
+
// Extract author name
|
|
99
|
+
const author = typeof packageJson.author === 'string'
|
|
100
|
+
? packageJson.author
|
|
101
|
+
: packageJson.author?.name || 'Unknown';
|
|
102
|
+
|
|
103
|
+
// Extract repository URL
|
|
104
|
+
const githubUrl = typeof packageJson.repository === 'string'
|
|
105
|
+
? packageJson.repository
|
|
106
|
+
: packageJson.repository?.url;
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
// From package.json
|
|
110
|
+
name: config.name,
|
|
111
|
+
version: packageJson.version,
|
|
112
|
+
author,
|
|
113
|
+
description: packageJson.description,
|
|
114
|
+
keywords: packageJson.keywords,
|
|
115
|
+
license: packageJson.license,
|
|
116
|
+
homepage: packageJson.homepage,
|
|
117
|
+
githubUrl,
|
|
118
|
+
peerDependencies: packageJson.peerDependencies,
|
|
119
|
+
|
|
120
|
+
// From manual config
|
|
121
|
+
displayName: config.displayName,
|
|
122
|
+
icon: config.icon,
|
|
123
|
+
category: config.category,
|
|
124
|
+
features: config.features,
|
|
125
|
+
examples: config.examples,
|
|
126
|
+
minVersion: config.minVersion,
|
|
127
|
+
relatedExtensions: config.relatedExtensions,
|
|
128
|
+
githubStars: config.githubStars,
|
|
129
|
+
|
|
130
|
+
// Auto-generated
|
|
131
|
+
npmUrl: `https://www.npmjs.com/package/${packageJson.name}`,
|
|
132
|
+
installCommand: `pnpm add ${packageJson.name}`,
|
|
133
|
+
tags: packageJson.keywords,
|
|
134
|
+
preview: `https://unpkg.com/${packageJson.name}@latest/preview.png`,
|
|
135
|
+
|
|
136
|
+
// Dependencies (empty by default, can be set via keywords)
|
|
137
|
+
dependencies: [],
|
|
138
|
+
};
|
|
139
|
+
}
|