@djangocfg/ext-base 1.0.3 → 1.0.5
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 +134 -8
- 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-2CHOCDMY.js +237 -0
- package/dist/chunk-3RG5ZIWI.js +8 -0
- package/dist/chunk-NXFE5CDE.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 +6 -4
- package/src/cli/index.ts +281 -15
- package/src/context/ExtensionProvider.tsx +67 -4
- package/src/extensionConfig.ts +77 -28
- package/templates/extension-template/README.md.template +30 -0
- package/templates/extension-template/package.json.template +2 -1
- 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 +32 -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 +36 -0
- package/templates/extension-template/playground/postcss.config.js.template +5 -0
- package/templates/extension-template/playground/tsconfig.json.template +18 -0
- 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/index.ts +12 -4
- package/templates/extension-template/src/utils/withSmartProvider.tsx +70 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { __require } from './chunk-3RG5ZIWI.js';
|
|
2
|
+
|
|
3
|
+
// package.json
|
|
4
|
+
var package_default = {
|
|
5
|
+
name: "@djangocfg/ext-base",
|
|
6
|
+
version: "1.0.5",
|
|
7
|
+
description: "Base utilities and common code for DjangoCFG extensions",
|
|
8
|
+
keywords: [
|
|
9
|
+
"django",
|
|
10
|
+
"djangocfg",
|
|
11
|
+
"extension",
|
|
12
|
+
"base",
|
|
13
|
+
"utilities",
|
|
14
|
+
"typescript",
|
|
15
|
+
"react"
|
|
16
|
+
],
|
|
17
|
+
author: {
|
|
18
|
+
name: "DjangoCFG",
|
|
19
|
+
url: "https://djangocfg.com"
|
|
20
|
+
},
|
|
21
|
+
homepage: "https://hub.djangocfg.com/extensions/djangocfg-ext-base",
|
|
22
|
+
repository: {
|
|
23
|
+
type: "git",
|
|
24
|
+
url: "https://github.com/markolofsen/django-cfg.git",
|
|
25
|
+
directory: "extensions/base"
|
|
26
|
+
},
|
|
27
|
+
bugs: {
|
|
28
|
+
url: "https://github.com/markolofsen/django-cfg/issues"
|
|
29
|
+
},
|
|
30
|
+
license: "MIT",
|
|
31
|
+
type: "module",
|
|
32
|
+
main: "./dist/index.cjs",
|
|
33
|
+
module: "./dist/index.js",
|
|
34
|
+
types: "./dist/index.d.ts",
|
|
35
|
+
exports: {
|
|
36
|
+
".": {
|
|
37
|
+
types: "./dist/index.d.ts",
|
|
38
|
+
import: "./dist/index.js",
|
|
39
|
+
require: "./dist/index.cjs"
|
|
40
|
+
},
|
|
41
|
+
"./hooks": {
|
|
42
|
+
types: "./dist/hooks.d.ts",
|
|
43
|
+
import: "./dist/hooks.js",
|
|
44
|
+
require: "./dist/hooks.cjs"
|
|
45
|
+
},
|
|
46
|
+
"./auth": {
|
|
47
|
+
types: "./dist/auth.d.ts",
|
|
48
|
+
import: "./dist/auth.js",
|
|
49
|
+
require: "./dist/auth.cjs"
|
|
50
|
+
},
|
|
51
|
+
"./api": {
|
|
52
|
+
types: "./dist/api.d.ts",
|
|
53
|
+
import: "./dist/api.js",
|
|
54
|
+
require: "./dist/api.cjs"
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
files: [
|
|
58
|
+
"dist",
|
|
59
|
+
"src",
|
|
60
|
+
"preview.png",
|
|
61
|
+
"templates"
|
|
62
|
+
],
|
|
63
|
+
bin: {
|
|
64
|
+
"djangocfg-ext": "./dist/cli.mjs"
|
|
65
|
+
},
|
|
66
|
+
scripts: {
|
|
67
|
+
build: "tsup",
|
|
68
|
+
dev: "tsup --watch",
|
|
69
|
+
check: "tsc --noEmit",
|
|
70
|
+
test: "tsx src/cli/index.ts test",
|
|
71
|
+
cli: "tsx src/cli/index.ts",
|
|
72
|
+
"cli:help": "tsx src/cli/index.ts --help",
|
|
73
|
+
"cli:create": "tsx src/cli/index.ts create",
|
|
74
|
+
"cli:test": "tsx src/cli/index.ts test",
|
|
75
|
+
"cli:list": "tsx src/cli/index.ts list",
|
|
76
|
+
"cli:info": "tsx src/cli/index.ts info"
|
|
77
|
+
},
|
|
78
|
+
peerDependencies: {
|
|
79
|
+
"@djangocfg/api": "workspace:*",
|
|
80
|
+
consola: "^3.4.2",
|
|
81
|
+
react: "^18 || ^19",
|
|
82
|
+
"react-dom": "^18 || ^19",
|
|
83
|
+
swr: "^2.3.7"
|
|
84
|
+
},
|
|
85
|
+
dependencies: {
|
|
86
|
+
chalk: "^5.3.0",
|
|
87
|
+
consola: "^3.4.2",
|
|
88
|
+
picocolors: "^1.1.1",
|
|
89
|
+
prompts: "^2.4.2"
|
|
90
|
+
},
|
|
91
|
+
devDependencies: {
|
|
92
|
+
"@djangocfg/api": "workspace:*",
|
|
93
|
+
"@djangocfg/typescript-config": "workspace:*",
|
|
94
|
+
"@types/node": "^24.7.2",
|
|
95
|
+
"@types/prompts": "^2.4.9",
|
|
96
|
+
"@types/react": "^19.0.0",
|
|
97
|
+
swr: "^2.3.7",
|
|
98
|
+
tsup: "^8.5.0",
|
|
99
|
+
tsx: "^4.19.2",
|
|
100
|
+
typescript: "^5.9.3"
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
// src/metadata.ts
|
|
105
|
+
({
|
|
106
|
+
version: package_default.version,
|
|
107
|
+
npmUrl: `https://www.npmjs.com/package/${package_default.name}`,
|
|
108
|
+
installCommand: `pnpm add ${package_default.name}`,
|
|
109
|
+
packagePeerDependencies: package_default.peerDependencies});
|
|
110
|
+
|
|
111
|
+
// src/config.ts
|
|
112
|
+
var isDevelopment = process.env.NODE_ENV === "development";
|
|
113
|
+
var isProduction = process.env.NODE_ENV === "production";
|
|
114
|
+
var isStaticBuild = process.env.STATIC_BUILD === "true";
|
|
115
|
+
function getApiUrl() {
|
|
116
|
+
return process.env.NEXT_PUBLIC_API_URL || "/api";
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// src/api/createExtensionAPI.ts
|
|
120
|
+
function createExtensionAPI(APIClass) {
|
|
121
|
+
let storage;
|
|
122
|
+
try {
|
|
123
|
+
const { api: accountsApi } = __require("@djangocfg/api");
|
|
124
|
+
storage = accountsApi._storage;
|
|
125
|
+
} catch (error) {
|
|
126
|
+
storage = void 0;
|
|
127
|
+
}
|
|
128
|
+
const apiUrl = isStaticBuild ? "" : getApiUrl();
|
|
129
|
+
return new APIClass(apiUrl, storage ? { storage } : void 0);
|
|
130
|
+
}
|
|
131
|
+
function getSharedAuthStorage() {
|
|
132
|
+
try {
|
|
133
|
+
const { api: accountsApi } = __require("@djangocfg/api");
|
|
134
|
+
return accountsApi._storage;
|
|
135
|
+
} catch (error) {
|
|
136
|
+
return void 0;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export { createExtensionAPI, getApiUrl, getSharedAuthStorage, isDevelopment, isProduction, isStaticBuild, package_default };
|
package/dist/cli.mjs
ADDED
|
@@ -0,0 +1,530 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { consola } from 'consola';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import prompts from 'prompts';
|
|
5
|
+
import { existsSync, readdirSync, readFileSync, writeFileSync, statSync, mkdirSync } from 'fs';
|
|
6
|
+
import { dirname, join } from 'path';
|
|
7
|
+
import { fileURLToPath } from 'url';
|
|
8
|
+
import { execSync } from 'child_process';
|
|
9
|
+
|
|
10
|
+
// src/types/context.ts
|
|
11
|
+
var EXTENSION_CATEGORIES = [
|
|
12
|
+
{ title: "Forms", value: "forms" },
|
|
13
|
+
{ title: "Payments", value: "payments" },
|
|
14
|
+
{ title: "Content", value: "content" },
|
|
15
|
+
{ title: "Support", value: "support" },
|
|
16
|
+
{ title: "Utilities", value: "utilities" },
|
|
17
|
+
{ title: "Analytics", value: "analytics" },
|
|
18
|
+
{ title: "Security", value: "security" },
|
|
19
|
+
{ title: "Integration", value: "integration" },
|
|
20
|
+
{ title: "Other", value: "other" }
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
// src/cli/index.ts
|
|
24
|
+
var __dirname$1 = dirname(fileURLToPath(import.meta.url));
|
|
25
|
+
function getVersion() {
|
|
26
|
+
try {
|
|
27
|
+
const pkgPath = join(__dirname$1, "../../package.json");
|
|
28
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
29
|
+
return pkg.version;
|
|
30
|
+
} catch {
|
|
31
|
+
return "1.0.0";
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
var COMMANDS = {
|
|
35
|
+
create: "Create a new DjangoCFG extension",
|
|
36
|
+
test: "Quick create test extension (no prompts)",
|
|
37
|
+
help: "Show help",
|
|
38
|
+
"--help": "Show help",
|
|
39
|
+
"-h": "Show help"
|
|
40
|
+
};
|
|
41
|
+
function replacePlaceholders(content, replacements) {
|
|
42
|
+
let result = content;
|
|
43
|
+
for (const [key, value] of Object.entries(replacements)) {
|
|
44
|
+
result = result.replaceAll(key, value);
|
|
45
|
+
}
|
|
46
|
+
return result;
|
|
47
|
+
}
|
|
48
|
+
function copyTemplateRecursive(src, dest, replacements) {
|
|
49
|
+
if (!existsSync(src)) {
|
|
50
|
+
throw new Error(`Template not found: ${src}`);
|
|
51
|
+
}
|
|
52
|
+
const stats = statSync(src);
|
|
53
|
+
if (stats.isDirectory()) {
|
|
54
|
+
if (!existsSync(dest)) {
|
|
55
|
+
mkdirSync(dest, { recursive: true });
|
|
56
|
+
}
|
|
57
|
+
const files = readdirSync(src);
|
|
58
|
+
for (const file of files) {
|
|
59
|
+
const srcPath = join(src, file);
|
|
60
|
+
let destFile = file;
|
|
61
|
+
if (file.includes("__PROVIDER_NAME__")) {
|
|
62
|
+
destFile = file.replaceAll("__PROVIDER_NAME__", replacements["__PROVIDER_NAME__"]);
|
|
63
|
+
}
|
|
64
|
+
if (destFile.endsWith(".template")) {
|
|
65
|
+
destFile = destFile.slice(0, -9);
|
|
66
|
+
}
|
|
67
|
+
const destPath = join(dest, destFile);
|
|
68
|
+
copyTemplateRecursive(srcPath, destPath, replacements);
|
|
69
|
+
}
|
|
70
|
+
} else {
|
|
71
|
+
const content = readFileSync(src, "utf-8");
|
|
72
|
+
const replaced = replacePlaceholders(content, replacements);
|
|
73
|
+
writeFileSync(dest, replaced, "utf-8");
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function printBanner() {
|
|
77
|
+
console.log();
|
|
78
|
+
console.log(chalk.cyan.bold(" \u{1F680} DjangoCFG Extension Manager"));
|
|
79
|
+
console.log(chalk.gray(` v${getVersion()}`));
|
|
80
|
+
console.log();
|
|
81
|
+
}
|
|
82
|
+
async function checkPackageExists(packageName) {
|
|
83
|
+
try {
|
|
84
|
+
const encodedName = encodeURIComponent(packageName);
|
|
85
|
+
const response = await fetch(`https://registry.npmjs.org/${encodedName}`);
|
|
86
|
+
return response.status === 200;
|
|
87
|
+
} catch {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function checkDirectoryExists(packageName) {
|
|
92
|
+
const extDirName = packageName.replace("@", "").replace("/", "-");
|
|
93
|
+
const isInExtBase = process.cwd().endsWith("ext-base");
|
|
94
|
+
const baseDir = isInExtBase ? join(process.cwd(), "..") : join(process.cwd(), "extensions");
|
|
95
|
+
const extDir = join(baseDir, extDirName);
|
|
96
|
+
if (existsSync(extDir)) {
|
|
97
|
+
return { exists: true, path: extDir };
|
|
98
|
+
}
|
|
99
|
+
try {
|
|
100
|
+
const files = readdirSync(baseDir);
|
|
101
|
+
const lowerCaseName = extDirName.toLowerCase();
|
|
102
|
+
for (const file of files) {
|
|
103
|
+
if (file.toLowerCase() === lowerCaseName) {
|
|
104
|
+
const fullPath = join(baseDir, file);
|
|
105
|
+
const stats = statSync(fullPath);
|
|
106
|
+
if (stats.isDirectory()) {
|
|
107
|
+
return { exists: true, path: fullPath };
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
} catch {
|
|
112
|
+
return { exists: false };
|
|
113
|
+
}
|
|
114
|
+
return { exists: false };
|
|
115
|
+
}
|
|
116
|
+
function detectPackageManager() {
|
|
117
|
+
const managers = ["pnpm", "yarn", "npm"];
|
|
118
|
+
for (const manager of managers) {
|
|
119
|
+
try {
|
|
120
|
+
const result = execSync(`${manager} --version`, {
|
|
121
|
+
encoding: "utf-8",
|
|
122
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
123
|
+
});
|
|
124
|
+
if (result) {
|
|
125
|
+
return manager;
|
|
126
|
+
}
|
|
127
|
+
} catch {
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
function printHelp() {
|
|
134
|
+
printBanner();
|
|
135
|
+
console.log(chalk.yellow.bold("Usage:"));
|
|
136
|
+
console.log(` ${chalk.cyan("djangocfg-ext")} ${chalk.gray("[command]")}`);
|
|
137
|
+
console.log();
|
|
138
|
+
console.log(chalk.yellow.bold("Commands:"));
|
|
139
|
+
Object.entries(COMMANDS).forEach(([cmd, desc]) => {
|
|
140
|
+
console.log(` ${chalk.cyan(cmd.padEnd(12))} ${chalk.gray(desc)}`);
|
|
141
|
+
});
|
|
142
|
+
console.log();
|
|
143
|
+
console.log(chalk.yellow.bold("Examples:"));
|
|
144
|
+
console.log(` ${chalk.gray("$")} ${chalk.cyan("djangocfg-ext create")}`);
|
|
145
|
+
console.log();
|
|
146
|
+
}
|
|
147
|
+
async function createExtension() {
|
|
148
|
+
printBanner();
|
|
149
|
+
console.log(chalk.yellow("Create a new DjangoCFG extension"));
|
|
150
|
+
console.log();
|
|
151
|
+
let packageName = "";
|
|
152
|
+
let packageNameValid = false;
|
|
153
|
+
while (!packageNameValid) {
|
|
154
|
+
const nameResponse = await prompts({
|
|
155
|
+
type: "text",
|
|
156
|
+
name: "packageName",
|
|
157
|
+
message: 'Package name (e.g., "@my-org/my-extension" or "my-extension"):',
|
|
158
|
+
validate: (value) => {
|
|
159
|
+
if (!value) return "Package name is required";
|
|
160
|
+
const normalized = value.toLowerCase();
|
|
161
|
+
if (!/^(@[a-zA-Z0-9-]+\/)?[a-zA-Z0-9][a-zA-Z0-9-]*$/.test(normalized)) {
|
|
162
|
+
return "Invalid package name format. Use @scope/name or name";
|
|
163
|
+
}
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
if (!nameResponse.packageName) {
|
|
168
|
+
consola.info("Extension creation cancelled");
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
packageName = nameResponse.packageName.toLowerCase();
|
|
172
|
+
const dirCheck = checkDirectoryExists(packageName);
|
|
173
|
+
if (dirCheck.exists) {
|
|
174
|
+
consola.error(`Extension directory already exists: ${chalk.red(dirCheck.path)}`);
|
|
175
|
+
console.log(chalk.gray("Please try a different name."));
|
|
176
|
+
console.log();
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
consola.start(`Checking if ${chalk.cyan(packageName)} is available on npm...`);
|
|
180
|
+
const packageExists = await checkPackageExists(packageName);
|
|
181
|
+
if (packageExists) {
|
|
182
|
+
consola.error(`Package ${chalk.red(packageName)} already exists on npm!`);
|
|
183
|
+
console.log(chalk.gray("Please try a different name."));
|
|
184
|
+
console.log();
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
consola.success("Package name is available!");
|
|
188
|
+
console.log();
|
|
189
|
+
packageNameValid = true;
|
|
190
|
+
}
|
|
191
|
+
const response = await prompts([
|
|
192
|
+
{
|
|
193
|
+
type: "text",
|
|
194
|
+
name: "displayName",
|
|
195
|
+
message: 'Display name (e.g., "My Extension"):',
|
|
196
|
+
validate: (value) => value ? true : "Display name is required"
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
type: "text",
|
|
200
|
+
name: "description",
|
|
201
|
+
message: "Description:",
|
|
202
|
+
validate: (value) => value ? true : "Description is required"
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
type: "select",
|
|
206
|
+
name: "category",
|
|
207
|
+
message: "Category:",
|
|
208
|
+
choices: EXTENSION_CATEGORIES
|
|
209
|
+
}
|
|
210
|
+
]);
|
|
211
|
+
if (!response.displayName) {
|
|
212
|
+
consola.info("Extension creation cancelled");
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
const fullResponse = {
|
|
216
|
+
...response,
|
|
217
|
+
packageName
|
|
218
|
+
};
|
|
219
|
+
const isInDjangoCfgMonorepo = existsSync(join(process.cwd(), "../../pnpm-workspace.yaml")) && existsSync(join(process.cwd(), "../../packages/api/package.json"));
|
|
220
|
+
let extDirName;
|
|
221
|
+
if (isInDjangoCfgMonorepo && fullResponse.packageName.startsWith("@djangocfg/")) {
|
|
222
|
+
extDirName = fullResponse.packageName.split("/")[1];
|
|
223
|
+
} else {
|
|
224
|
+
extDirName = fullResponse.packageName.replace("@", "").replace("/", "-");
|
|
225
|
+
}
|
|
226
|
+
const isInExtBase = process.cwd().endsWith("ext-base");
|
|
227
|
+
const extDir = isInExtBase ? join(process.cwd(), "..", extDirName) : join(process.cwd(), "extensions", extDirName);
|
|
228
|
+
console.log();
|
|
229
|
+
consola.start(`Creating extension: ${chalk.cyan(fullResponse.packageName)}`);
|
|
230
|
+
try {
|
|
231
|
+
const providerName = fullResponse.displayName.split(/[\s-_]+/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("");
|
|
232
|
+
const shortName = fullResponse.packageName.split("/").pop() || fullResponse.packageName.replace("@", "");
|
|
233
|
+
const marketplaceId = fullResponse.packageName.replace("@", "").replace("/", "-");
|
|
234
|
+
const marketplaceLink = `**[\u{1F4E6} View in Marketplace](https://hub.djangocfg.com/extensions/${marketplaceId})** \u2022 **[\u{1F4D6} Documentation](https://djangocfg.com)** \u2022 **[\u2B50 GitHub](https://github.com/markolofsen/django-cfg)**`;
|
|
235
|
+
const replacements = {
|
|
236
|
+
"__NAME__": shortName,
|
|
237
|
+
"__PACKAGE_NAME__": fullResponse.packageName,
|
|
238
|
+
"__DISPLAY_NAME__": fullResponse.displayName,
|
|
239
|
+
"__DESCRIPTION__": fullResponse.description,
|
|
240
|
+
"__CATEGORY__": fullResponse.category,
|
|
241
|
+
"__PROVIDER_NAME__": providerName,
|
|
242
|
+
"__MARKETPLACE_ID__": marketplaceId,
|
|
243
|
+
"__MARKETPLACE_LINK__": marketplaceLink
|
|
244
|
+
};
|
|
245
|
+
const templatePaths = [
|
|
246
|
+
// When running from built dist/ (installed from npm)
|
|
247
|
+
join(__dirname$1, "../templates/extension-template"),
|
|
248
|
+
// When running from src/ during development (tsx)
|
|
249
|
+
join(__dirname$1, "../../templates/extension-template"),
|
|
250
|
+
// Workspace path (for development from monorepo root)
|
|
251
|
+
join(process.cwd(), "extensions", "ext-base", "templates", "extension-template"),
|
|
252
|
+
// When running from ext-base directory
|
|
253
|
+
join(process.cwd(), "templates", "extension-template")
|
|
254
|
+
];
|
|
255
|
+
let templateDir = null;
|
|
256
|
+
for (const path of templatePaths) {
|
|
257
|
+
if (existsSync(path)) {
|
|
258
|
+
templateDir = path;
|
|
259
|
+
break;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
if (!templateDir) {
|
|
263
|
+
throw new Error("Extension template not found");
|
|
264
|
+
}
|
|
265
|
+
consola.start("Copying template files...");
|
|
266
|
+
copyTemplateRecursive(templateDir, extDir, replacements);
|
|
267
|
+
consola.success("Extension files created");
|
|
268
|
+
if (isInDjangoCfgMonorepo) {
|
|
269
|
+
consola.info("Detected djangocfg monorepo - using workspace:* for @djangocfg packages");
|
|
270
|
+
const pkgJsonPath = join(extDir, "package.json");
|
|
271
|
+
const pkgJson = JSON.parse(readFileSync(pkgJsonPath, "utf-8"));
|
|
272
|
+
const replaceDeps = (deps) => {
|
|
273
|
+
if (!deps) return;
|
|
274
|
+
for (const [key, value] of Object.entries(deps)) {
|
|
275
|
+
if (key.startsWith("@djangocfg/") && value === "latest") {
|
|
276
|
+
deps[key] = "workspace:*";
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
replaceDeps(pkgJson.dependencies);
|
|
281
|
+
replaceDeps(pkgJson.peerDependencies);
|
|
282
|
+
replaceDeps(pkgJson.devDependencies);
|
|
283
|
+
writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 2) + "\n", "utf-8");
|
|
284
|
+
}
|
|
285
|
+
console.log();
|
|
286
|
+
const packageManager = detectPackageManager();
|
|
287
|
+
if (packageManager) {
|
|
288
|
+
consola.start(`Installing dependencies with ${chalk.cyan(packageManager)}...`);
|
|
289
|
+
try {
|
|
290
|
+
execSync(`${packageManager} install`, {
|
|
291
|
+
cwd: extDir,
|
|
292
|
+
stdio: "inherit"
|
|
293
|
+
});
|
|
294
|
+
consola.success("Dependencies installed successfully");
|
|
295
|
+
console.log();
|
|
296
|
+
const playgroundDir = join(extDir, "playground");
|
|
297
|
+
if (existsSync(playgroundDir)) {
|
|
298
|
+
consola.start(`Installing playground dependencies with ${chalk.cyan(packageManager)}...`);
|
|
299
|
+
try {
|
|
300
|
+
execSync(`${packageManager} install`, {
|
|
301
|
+
cwd: playgroundDir,
|
|
302
|
+
stdio: "inherit"
|
|
303
|
+
});
|
|
304
|
+
consola.success("Playground dependencies installed successfully");
|
|
305
|
+
console.log();
|
|
306
|
+
} catch (error) {
|
|
307
|
+
consola.warn(`Failed to install playground dependencies. Please run: ${chalk.cyan(`cd ${extDirName}/playground && ${packageManager} install`)}`);
|
|
308
|
+
console.log();
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
consola.start("Running type check...");
|
|
312
|
+
try {
|
|
313
|
+
execSync(`${packageManager} run check`, {
|
|
314
|
+
cwd: extDir,
|
|
315
|
+
stdio: "inherit"
|
|
316
|
+
});
|
|
317
|
+
consola.success("Type check passed");
|
|
318
|
+
} catch (error) {
|
|
319
|
+
consola.warn("Type check failed. Please review and fix type errors.");
|
|
320
|
+
}
|
|
321
|
+
} catch (error) {
|
|
322
|
+
consola.warn(`Failed to install dependencies automatically. Please run: ${chalk.cyan(`cd ${extDirName} && ${packageManager} install`)}`);
|
|
323
|
+
}
|
|
324
|
+
console.log();
|
|
325
|
+
} else {
|
|
326
|
+
consola.warn("No package manager found (pnpm, yarn, or npm). Please install dependencies manually.");
|
|
327
|
+
console.log();
|
|
328
|
+
}
|
|
329
|
+
consola.success(`Extension created successfully: ${chalk.cyan(extDir)}`);
|
|
330
|
+
console.log();
|
|
331
|
+
console.log(chalk.yellow.bold("Next steps:"));
|
|
332
|
+
console.log();
|
|
333
|
+
console.log(chalk.gray("1. Navigate to extension directory:"));
|
|
334
|
+
console.log(chalk.cyan(` cd ${extDirName}`));
|
|
335
|
+
console.log();
|
|
336
|
+
console.log(chalk.gray("2. Build the extension:"));
|
|
337
|
+
console.log(chalk.cyan(` ${packageManager || "pnpm"} build`));
|
|
338
|
+
console.log();
|
|
339
|
+
console.log(chalk.gray("3. Add your features and customize:"));
|
|
340
|
+
console.log(chalk.cyan(` - Edit src/config.ts to add features`));
|
|
341
|
+
console.log(chalk.cyan(` - Add components, hooks, and utilities`));
|
|
342
|
+
console.log(chalk.cyan(` - Update README.md with usage examples`));
|
|
343
|
+
console.log();
|
|
344
|
+
consola.info("Documentation: https://djangocfg.com/docs");
|
|
345
|
+
} catch (error) {
|
|
346
|
+
consola.error("Failed to create extension:", error);
|
|
347
|
+
process.exit(1);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
async function createTestExtension() {
|
|
351
|
+
printBanner();
|
|
352
|
+
const isInExtBase = process.cwd().endsWith("ext-base");
|
|
353
|
+
const baseDir = isInExtBase ? join(process.cwd(), "..") : join(process.cwd(), "extensions");
|
|
354
|
+
if (existsSync(baseDir)) {
|
|
355
|
+
const files = readdirSync(baseDir);
|
|
356
|
+
const testExtensions = files.filter((f) => f.startsWith("test-"));
|
|
357
|
+
if (testExtensions.length > 0) {
|
|
358
|
+
consola.info(`Cleaning up ${testExtensions.length} old test extension(s)...`);
|
|
359
|
+
for (const testExt of testExtensions) {
|
|
360
|
+
const testPath = join(baseDir, testExt);
|
|
361
|
+
try {
|
|
362
|
+
execSync(`rm -rf "${testPath}"`, { stdio: "ignore" });
|
|
363
|
+
consola.success(`Removed ${testExt}`);
|
|
364
|
+
} catch (error) {
|
|
365
|
+
consola.warn(`Failed to remove ${testExt}`);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
console.log();
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
const timestamp = Date.now().toString(36);
|
|
372
|
+
const packageName = `@djangocfg/test-${timestamp}`;
|
|
373
|
+
const displayName = `Test ${timestamp}`;
|
|
374
|
+
const description = "Test extension created for development";
|
|
375
|
+
const category = "utilities";
|
|
376
|
+
console.log(chalk.yellow("Quick Test Extension Creation"));
|
|
377
|
+
console.log();
|
|
378
|
+
console.log(chalk.cyan(`Package: ${packageName}`));
|
|
379
|
+
console.log(chalk.cyan(`Name: ${displayName}`));
|
|
380
|
+
console.log();
|
|
381
|
+
const isInDjangoCfgMonorepo = existsSync(join(process.cwd(), "../../pnpm-workspace.yaml")) && existsSync(join(process.cwd(), "../../packages/api/package.json"));
|
|
382
|
+
const extDirName = packageName.split("/")[1];
|
|
383
|
+
const extDir = isInExtBase ? join(process.cwd(), "..", extDirName) : join(process.cwd(), "extensions", extDirName);
|
|
384
|
+
if (existsSync(extDir)) {
|
|
385
|
+
consola.error(`Extension directory already exists: ${chalk.red(extDir)}`);
|
|
386
|
+
process.exit(1);
|
|
387
|
+
}
|
|
388
|
+
consola.start(`Creating test extension: ${chalk.cyan(packageName)}`);
|
|
389
|
+
try {
|
|
390
|
+
const providerName = displayName.split(/[\s-_]+/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("");
|
|
391
|
+
const shortName = packageName.split("/").pop() || packageName.replace("@", "");
|
|
392
|
+
const marketplaceId = packageName.replace("@", "").replace("/", "-");
|
|
393
|
+
const marketplaceLink = `**[\u{1F4E6} View in Marketplace](https://hub.djangocfg.com/extensions/${marketplaceId})** \u2022 **[\u{1F4D6} Documentation](https://djangocfg.com)** \u2022 **[\u2B50 GitHub](https://github.com/markolofsen/django-cfg)**`;
|
|
394
|
+
const replacements = {
|
|
395
|
+
"__NAME__": shortName,
|
|
396
|
+
"__PACKAGE_NAME__": packageName,
|
|
397
|
+
"__DISPLAY_NAME__": displayName,
|
|
398
|
+
"__DESCRIPTION__": description,
|
|
399
|
+
"__CATEGORY__": category,
|
|
400
|
+
"__PROVIDER_NAME__": providerName,
|
|
401
|
+
"__MARKETPLACE_ID__": marketplaceId,
|
|
402
|
+
"__MARKETPLACE_LINK__": marketplaceLink
|
|
403
|
+
};
|
|
404
|
+
const templatePaths = [
|
|
405
|
+
join(__dirname$1, "../templates/extension-template"),
|
|
406
|
+
join(__dirname$1, "../../templates/extension-template"),
|
|
407
|
+
join(process.cwd(), "extensions", "ext-base", "templates", "extension-template"),
|
|
408
|
+
join(process.cwd(), "templates", "extension-template")
|
|
409
|
+
];
|
|
410
|
+
let templateDir = null;
|
|
411
|
+
for (const path of templatePaths) {
|
|
412
|
+
if (existsSync(path)) {
|
|
413
|
+
templateDir = path;
|
|
414
|
+
break;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
if (!templateDir) {
|
|
418
|
+
throw new Error("Extension template not found");
|
|
419
|
+
}
|
|
420
|
+
consola.start("Copying template files...");
|
|
421
|
+
copyTemplateRecursive(templateDir, extDir, replacements);
|
|
422
|
+
consola.success("Extension files created");
|
|
423
|
+
if (isInDjangoCfgMonorepo) {
|
|
424
|
+
consola.info("Detected djangocfg monorepo - using workspace:* for @djangocfg packages");
|
|
425
|
+
const pkgJsonPath = join(extDir, "package.json");
|
|
426
|
+
const pkgJson = JSON.parse(readFileSync(pkgJsonPath, "utf-8"));
|
|
427
|
+
const replaceDeps = (deps) => {
|
|
428
|
+
if (!deps) return;
|
|
429
|
+
for (const [key, value] of Object.entries(deps)) {
|
|
430
|
+
if (key.startsWith("@djangocfg/") && value === "latest") {
|
|
431
|
+
deps[key] = "workspace:*";
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
};
|
|
435
|
+
replaceDeps(pkgJson.dependencies);
|
|
436
|
+
replaceDeps(pkgJson.peerDependencies);
|
|
437
|
+
replaceDeps(pkgJson.devDependencies);
|
|
438
|
+
const playgroundPkgPath = join(extDir, "playground", "package.json");
|
|
439
|
+
if (existsSync(playgroundPkgPath)) {
|
|
440
|
+
const playgroundPkg = JSON.parse(readFileSync(playgroundPkgPath, "utf-8"));
|
|
441
|
+
replaceDeps(playgroundPkg.dependencies);
|
|
442
|
+
replaceDeps(playgroundPkg.devDependencies);
|
|
443
|
+
writeFileSync(playgroundPkgPath, JSON.stringify(playgroundPkg, null, 2) + "\n", "utf-8");
|
|
444
|
+
}
|
|
445
|
+
writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 2) + "\n", "utf-8");
|
|
446
|
+
}
|
|
447
|
+
console.log();
|
|
448
|
+
const packageManager = detectPackageManager();
|
|
449
|
+
if (packageManager) {
|
|
450
|
+
consola.start(`Installing dependencies with ${chalk.cyan(packageManager)}...`);
|
|
451
|
+
try {
|
|
452
|
+
execSync(`${packageManager} install`, {
|
|
453
|
+
cwd: extDir,
|
|
454
|
+
stdio: "inherit"
|
|
455
|
+
});
|
|
456
|
+
consola.success("Dependencies installed successfully");
|
|
457
|
+
console.log();
|
|
458
|
+
const playgroundDir = join(extDir, "playground");
|
|
459
|
+
if (existsSync(playgroundDir)) {
|
|
460
|
+
consola.start(`Installing playground dependencies with ${chalk.cyan(packageManager)}...`);
|
|
461
|
+
try {
|
|
462
|
+
execSync(`${packageManager} install`, {
|
|
463
|
+
cwd: playgroundDir,
|
|
464
|
+
stdio: "inherit"
|
|
465
|
+
});
|
|
466
|
+
consola.success("Playground dependencies installed successfully");
|
|
467
|
+
console.log();
|
|
468
|
+
} catch (error) {
|
|
469
|
+
consola.warn(`Failed to install playground dependencies.`);
|
|
470
|
+
console.log();
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
consola.start("Running type check...");
|
|
474
|
+
try {
|
|
475
|
+
execSync(`${packageManager} run check`, {
|
|
476
|
+
cwd: extDir,
|
|
477
|
+
stdio: "inherit"
|
|
478
|
+
});
|
|
479
|
+
consola.success("Type check passed");
|
|
480
|
+
} catch (error) {
|
|
481
|
+
consola.warn("Type check failed. Please review and fix type errors.");
|
|
482
|
+
}
|
|
483
|
+
} catch (error) {
|
|
484
|
+
consola.warn(`Failed to install dependencies automatically.`);
|
|
485
|
+
}
|
|
486
|
+
console.log();
|
|
487
|
+
}
|
|
488
|
+
consola.success(`Test extension created: ${chalk.cyan(extDir)}`);
|
|
489
|
+
console.log();
|
|
490
|
+
console.log(chalk.yellow.bold("Quick commands:"));
|
|
491
|
+
console.log();
|
|
492
|
+
console.log(chalk.cyan(` cd ${extDirName}`));
|
|
493
|
+
console.log(chalk.cyan(` pnpm build`));
|
|
494
|
+
console.log(chalk.cyan(` pnpm dev:playground`));
|
|
495
|
+
console.log();
|
|
496
|
+
} catch (error) {
|
|
497
|
+
consola.error("Failed to create test extension:", error);
|
|
498
|
+
process.exit(1);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
async function main() {
|
|
502
|
+
const args = process.argv.slice(2);
|
|
503
|
+
const command = args[0];
|
|
504
|
+
if (!command) {
|
|
505
|
+
printHelp();
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
switch (command) {
|
|
509
|
+
case "create":
|
|
510
|
+
await createExtension();
|
|
511
|
+
break;
|
|
512
|
+
case "test":
|
|
513
|
+
await createTestExtension();
|
|
514
|
+
break;
|
|
515
|
+
case "help":
|
|
516
|
+
case "--help":
|
|
517
|
+
case "-h":
|
|
518
|
+
printHelp();
|
|
519
|
+
break;
|
|
520
|
+
default:
|
|
521
|
+
consola.error(`Unknown command: ${command}`);
|
|
522
|
+
console.log();
|
|
523
|
+
printHelp();
|
|
524
|
+
process.exit(1);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
main().catch((error) => {
|
|
528
|
+
consola.error("CLI error:", error);
|
|
529
|
+
process.exit(1);
|
|
530
|
+
});
|