adminforth 1.19.0 → 1.20.1-next.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/commands/bundle.js +15 -21
- package/commands/callTsProxy.js +121 -0
- package/commands/cli.js +52 -2
- package/commands/createApp/templates/.dockerignore.hbs +4 -0
- package/commands/createApp/templates/.env.prod.hbs +6 -0
- package/commands/createApp/templates/Dockerfile.hbs +7 -0
- package/commands/createApp/templates/index.ts.hbs +2 -2
- package/commands/createApp/templates/package.json.hbs +8 -5
- package/commands/createApp/templates/readme.md.hbs +14 -2
- package/commands/createApp/utils.js +38 -13
- package/commands/createCustomComponent/configLoader.js +57 -0
- package/commands/createCustomComponent/configUpdater.js +207 -0
- package/commands/createCustomComponent/fileGenerator.js +72 -0
- package/commands/createCustomComponent/main.js +138 -0
- package/commands/createCustomComponent/templates/customFields/create.vue.hbs +41 -0
- package/commands/createCustomComponent/templates/customFields/edit.vue.hbs +41 -0
- package/commands/createCustomComponent/templates/customFields/list.vue.hbs +18 -0
- package/commands/createCustomComponent/templates/customFields/show.vue.hbs +17 -0
- package/commands/proxy.ts +60 -0
- package/dist/commands/proxy.d.ts +2 -0
- package/dist/commands/proxy.d.ts.map +1 -0
- package/dist/commands/proxy.js +71 -0
- package/dist/commands/proxy.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/modules/codeInjector.d.ts +2 -0
- package/dist/modules/codeInjector.d.ts.map +1 -1
- package/dist/modules/codeInjector.js +64 -13
- package/dist/modules/codeInjector.js.map +1 -1
- package/package.json +5 -1
package/commands/bundle.js
CHANGED
|
@@ -1,28 +1,22 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import { callTsProxy, findAdminInstance } from "./callTsProxy.js";
|
|
2
|
+
|
|
3
3
|
|
|
4
4
|
async function bundle() {
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
|
|
5
|
+
console.log("Bundling admin SPA...");
|
|
6
|
+
const instance = await findAdminInstance();
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
try {
|
|
10
|
+
await callTsProxy(`
|
|
11
|
+
import { admin } from './${instance.file}.js';
|
|
8
12
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
try {
|
|
12
|
-
const instance = await getInstance(file, currentDirectory);
|
|
13
|
-
if (instance) {
|
|
14
|
-
await instance.bundleNow({ hotReload: false });
|
|
15
|
-
instanceFound = true;
|
|
16
|
-
break;
|
|
17
|
-
}
|
|
18
|
-
} catch (error) {
|
|
19
|
-
console.error(`Error: Could not bundle '${file}'`, error);
|
|
13
|
+
export async function exec() {
|
|
14
|
+
return await admin.bundleNow({ hotReload: false });
|
|
20
15
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
console.
|
|
25
|
-
return;
|
|
16
|
+
`);
|
|
17
|
+
|
|
18
|
+
} catch (e) {
|
|
19
|
+
console.log(`Running file ${file} failed`, e);
|
|
26
20
|
}
|
|
27
21
|
}
|
|
28
22
|
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
// callTsProxy.js
|
|
2
|
+
import { spawn } from "child_process";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import fs from "fs";
|
|
5
|
+
import chalk from "chalk";
|
|
6
|
+
|
|
7
|
+
const currentFilePath = import.meta.url;
|
|
8
|
+
const currentFileFolder = path.dirname(currentFilePath).replace("file:", "");
|
|
9
|
+
|
|
10
|
+
export function callTsProxy(tsCode) {
|
|
11
|
+
|
|
12
|
+
process.env.HEAVY_DEBUG && console.log("🌐 Calling tsproxy with code:", path.join(currentFileFolder, "proxy.ts"));
|
|
13
|
+
return new Promise((resolve, reject) => {
|
|
14
|
+
const child = spawn("tsx", [
|
|
15
|
+
path.join(currentFileFolder, "proxy.ts")
|
|
16
|
+
]);
|
|
17
|
+
|
|
18
|
+
let stdout = "";
|
|
19
|
+
let stderr = "";
|
|
20
|
+
|
|
21
|
+
child.stdout.on("data", (data) => {
|
|
22
|
+
stdout += data;
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
child.stderr.on("data", (data) => {
|
|
26
|
+
stderr += data;
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
child.on("close", (code) => {
|
|
30
|
+
if (code === 0) {
|
|
31
|
+
try {
|
|
32
|
+
const parsed = JSON.parse(stdout);
|
|
33
|
+
parsed.capturedLogs.forEach((log) => {
|
|
34
|
+
console.log(...log);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
if (parsed.error) {
|
|
38
|
+
reject(new Error(`${parsed.error}\n${parsed.stack}`));
|
|
39
|
+
}
|
|
40
|
+
resolve(parsed.result);
|
|
41
|
+
} catch (e) {
|
|
42
|
+
reject(new Error("Invalid JSON from tsproxy: " + stdout));
|
|
43
|
+
}
|
|
44
|
+
} else {
|
|
45
|
+
console.error(`tsproxy exited with non-0, this should never happen, stdout: ${stdout}, stderr: ${stderr}`);
|
|
46
|
+
reject(new Error(stderr));
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
process.env.HEAVY_DEBUG && console.log("🪲 Writing to tsproxy stdin...\n'''", tsCode, "'''");
|
|
51
|
+
child.stdin.write(tsCode);
|
|
52
|
+
child.stdin.end();
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export async function findAdminInstance() {
|
|
57
|
+
process.env.HEAVY_DEBUG && console.log("🌐 Finding admin instance...");
|
|
58
|
+
const currentDirectory = process.cwd();
|
|
59
|
+
|
|
60
|
+
let files = fs.readdirSync(currentDirectory);
|
|
61
|
+
let instanceFound = {
|
|
62
|
+
file: null,
|
|
63
|
+
version: null,
|
|
64
|
+
};
|
|
65
|
+
// try index.ts first
|
|
66
|
+
if (files.includes("index.ts")) {
|
|
67
|
+
files = files.filter((file) => file !== "index.ts");
|
|
68
|
+
files.unshift("index.ts");
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
for (const file of files) {
|
|
72
|
+
if (file.endsWith(".ts")) {
|
|
73
|
+
const fileNoTs = file.replace(/\.ts$/, "");
|
|
74
|
+
process.env.HEAVY_DEBUG && console.log(`🪲 Trying bundleing ${file}...`);
|
|
75
|
+
try {
|
|
76
|
+
res = await callTsProxy(`
|
|
77
|
+
import { admin } from './${fileNoTs}.js';
|
|
78
|
+
|
|
79
|
+
export async function exec() {
|
|
80
|
+
return admin.formatAdminForth();
|
|
81
|
+
}
|
|
82
|
+
`);
|
|
83
|
+
instanceFound.file = fileNoTs;
|
|
84
|
+
instanceFound.version = res;
|
|
85
|
+
break;
|
|
86
|
+
|
|
87
|
+
} catch (e) {
|
|
88
|
+
// do our best to guess that this file has a good chance to be admin instance
|
|
89
|
+
// and show the error so user can fix it
|
|
90
|
+
const fileContent = fs.readFileSync(file, "utf-8");
|
|
91
|
+
if (fileContent.includes("export const admin")) {
|
|
92
|
+
console.error(chalk.red(`Error running ${file}:`, e));
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
95
|
+
process.env.HEAVY_DEBUG && console.log(`🪲 File ${file} failed`, e);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (!instanceFound.file) {
|
|
100
|
+
console.error(
|
|
101
|
+
chalk.red(
|
|
102
|
+
`Error: No valid instance found to bundle.\n` +
|
|
103
|
+
`Make sure you have a file in the current directory with a .ts extension, and it exports an ` +
|
|
104
|
+
chalk.cyan.bold('admin') +
|
|
105
|
+
` instance like:\n\n` +
|
|
106
|
+
chalk.yellow('export const admin = new AdminForth({...})') +
|
|
107
|
+
`\n\nFor example, adminforth CLI creates an index.ts file which exports the admin instance.`
|
|
108
|
+
)
|
|
109
|
+
);
|
|
110
|
+
process.exit(1);
|
|
111
|
+
}
|
|
112
|
+
return instanceFound;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Example usage:
|
|
116
|
+
// callTsProxy(`
|
|
117
|
+
// import admin from './admin';
|
|
118
|
+
// function exec() {
|
|
119
|
+
// return admin.doX();
|
|
120
|
+
// }
|
|
121
|
+
// `).then(console.log).catch(console.error);
|
package/commands/cli.js
CHANGED
|
@@ -7,6 +7,41 @@ import bundle from "./bundle.js";
|
|
|
7
7
|
import createApp from "./createApp/main.js";
|
|
8
8
|
import generateModels from "./generateModels.js";
|
|
9
9
|
import createPlugin from "./createPlugin/main.js";
|
|
10
|
+
import createComponent from "./createCustomComponent/main.js";
|
|
11
|
+
import chalk from "chalk";
|
|
12
|
+
import path from "path";
|
|
13
|
+
import fs from "fs";
|
|
14
|
+
|
|
15
|
+
function showHelp() {
|
|
16
|
+
console.log(
|
|
17
|
+
chalk.white("Available commands:\n") +
|
|
18
|
+
chalk.green(' create-app') + chalk.white(' Create a new AdminForth app\n') +
|
|
19
|
+
chalk.green(' create-plugin') + chalk.white(' Create a plugin for your AdminForth app\n') +
|
|
20
|
+
chalk.green(' generate-models') + chalk.white(' Generate TypeScript models from your databases\n') +
|
|
21
|
+
chalk.green(' bundle') + chalk.white(' Bundles your AdminForth app SPA for production\n') +
|
|
22
|
+
chalk.green(' component') + chalk.white(' Scaffold a custom Vue component\n')
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function currentFileDir(importMetaUrl) {
|
|
27
|
+
const filePath = importMetaUrl.replace("file://", "");
|
|
28
|
+
const fileDir = path.dirname(filePath);
|
|
29
|
+
return fileDir;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function showVersion() {
|
|
33
|
+
const ADMIN_FORTH_ABSOLUTE_PATH = path.join(currentFileDir(import.meta.url), '..');
|
|
34
|
+
|
|
35
|
+
const package_json = JSON.parse(fs.readFileSync(path.join(ADMIN_FORTH_ABSOLUTE_PATH, 'package.json'), 'utf8'));
|
|
36
|
+
|
|
37
|
+
const ADMINFORTH_VERSION = package_json.version;
|
|
38
|
+
|
|
39
|
+
console.log(
|
|
40
|
+
chalk.white('AdminForth CLI version: ') +
|
|
41
|
+
chalk.cyan.bold(ADMINFORTH_VERSION)
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
10
45
|
switch (command) {
|
|
11
46
|
case "create-app":
|
|
12
47
|
createApp(args);
|
|
@@ -20,8 +55,23 @@ switch (command) {
|
|
|
20
55
|
case "bundle":
|
|
21
56
|
bundle();
|
|
22
57
|
break;
|
|
23
|
-
|
|
58
|
+
case "component":
|
|
59
|
+
createComponent(args);
|
|
60
|
+
break;
|
|
61
|
+
case "help":
|
|
62
|
+
case "--help":
|
|
63
|
+
case "-h":
|
|
64
|
+
showHelp();
|
|
65
|
+
break;
|
|
66
|
+
case "--version":
|
|
67
|
+
case "version":
|
|
68
|
+
case "-v":
|
|
69
|
+
showVersion();
|
|
70
|
+
break;
|
|
71
|
+
default: {
|
|
24
72
|
console.log(
|
|
25
|
-
"Unknown command.
|
|
73
|
+
"Unknown command."
|
|
26
74
|
);
|
|
75
|
+
showHelp();
|
|
76
|
+
}
|
|
27
77
|
}
|
|
@@ -60,9 +60,9 @@ if (import.meta.url === `file://${process.argv[1]}`) {
|
|
|
60
60
|
app.use(express.json());
|
|
61
61
|
|
|
62
62
|
const port = 3500;
|
|
63
|
-
|
|
63
|
+
|
|
64
64
|
await admin.bundleNow({ hotReload: process.env.NODE_ENV === 'development' });
|
|
65
|
-
console.log('Bundling AdminForth done.
|
|
65
|
+
console.log('Bundling AdminForth SPA done.');
|
|
66
66
|
|
|
67
67
|
admin.express.serve(app)
|
|
68
68
|
|
|
@@ -8,11 +8,14 @@
|
|
|
8
8
|
"license": "ISC",
|
|
9
9
|
"description": "",
|
|
10
10
|
"scripts": {
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"makemigration": "npm run
|
|
15
|
-
"
|
|
11
|
+
"dev": "npm run _env:dev -- tsx watch index.ts",
|
|
12
|
+
"prod": "npm run _env:prod -- tsx index.ts",
|
|
13
|
+
"start": "npm run dev",
|
|
14
|
+
"makemigration": "npm run _env:dev -- npx --yes prisma migrate dev --create-only",
|
|
15
|
+
"migrate:local": "npm run _env:dev -- npx --yes prisma migrate deploy",
|
|
16
|
+
"migrate:prod": "npm run _env:prod -- npx --yes prisma migrate deploy",
|
|
17
|
+
"_env:dev": "dotenvx run -f .env -f .env.local --",
|
|
18
|
+
"_env:prod": "dotenvx run -f .env.prod --"
|
|
16
19
|
},
|
|
17
20
|
"engines": {
|
|
18
21
|
"node": ">=20"
|
|
@@ -9,13 +9,13 @@ npm ci
|
|
|
9
9
|
Migrate the database:
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
npm run
|
|
12
|
+
npm run migrate:local
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
Start the server:
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
|
-
npm
|
|
18
|
+
npm run dev
|
|
19
19
|
```
|
|
20
20
|
|
|
21
21
|
{{#if prismaDbUrl}}
|
|
@@ -32,6 +32,18 @@ npm run makemigration -- --name <name_of_changes>
|
|
|
32
32
|
Your colleagues will need to pull the changes and run `npm run migrateLocal` to apply the migration in their local database.
|
|
33
33
|
{{/if}}
|
|
34
34
|
|
|
35
|
+
## Deployment tips
|
|
36
|
+
|
|
37
|
+
You have Dockerfile ready for production deployment. You can test the build with:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
docker build -t {{appName}}-image .
|
|
41
|
+
docker run -p 3500:3500 {{appName}}-image
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
To set non-sensitive environment variables in production, use `.env.prod` file.
|
|
45
|
+
For sensitive variables, use direct docker environment variables or secrets from your vault.
|
|
46
|
+
|
|
35
47
|
## Documentation
|
|
36
48
|
|
|
37
49
|
- [Customizing AdminForth Branding](https://adminforth.dev/docs/tutorial/Customization/branding/)
|
|
@@ -7,8 +7,12 @@ import path from 'path';
|
|
|
7
7
|
import { Listr } from 'listr2'
|
|
8
8
|
import { fileURLToPath } from 'url';
|
|
9
9
|
import {ConnectionString} from 'connection-string';
|
|
10
|
-
import {
|
|
10
|
+
import { exec } from 'child_process';
|
|
11
|
+
|
|
11
12
|
import Handlebars from 'handlebars';
|
|
13
|
+
import { promisify } from 'util';
|
|
14
|
+
|
|
15
|
+
const execAsync = promisify(exec);
|
|
12
16
|
|
|
13
17
|
export function parseArgumentsIntoOptions(rawArgs) {
|
|
14
18
|
const args = arg(
|
|
@@ -145,18 +149,19 @@ async function scaffoldProject(ctx, options, cwd) {
|
|
|
145
149
|
await fse.copy(sourceAssetsDir, targetAssetsDir);
|
|
146
150
|
|
|
147
151
|
// Write templated files
|
|
148
|
-
writeTemplateFiles(dirname, projectDir, {
|
|
152
|
+
await writeTemplateFiles(dirname, projectDir, {
|
|
149
153
|
dbUrl: connectionString.toString(),
|
|
150
154
|
prismaDbUrl,
|
|
151
155
|
appName,
|
|
152
156
|
provider,
|
|
157
|
+
nodeMajor: parseInt(process.versions.node.split('.')[0], 10),
|
|
153
158
|
});
|
|
154
159
|
|
|
155
160
|
return projectDir; // Return the new directory path
|
|
156
161
|
}
|
|
157
162
|
|
|
158
163
|
async function writeTemplateFiles(dirname, cwd, options) {
|
|
159
|
-
const { dbUrl, prismaDbUrl, appName, provider } = options;
|
|
164
|
+
const { dbUrl, prismaDbUrl, appName, provider, nodeMajor } = options;
|
|
160
165
|
|
|
161
166
|
// Build a list of files to generate
|
|
162
167
|
const templateTasks = [
|
|
@@ -191,10 +196,15 @@ async function writeTemplateFiles(dirname, cwd, options) {
|
|
|
191
196
|
dest: '.env.local',
|
|
192
197
|
data: { dbUrl, prismaDbUrl },
|
|
193
198
|
},
|
|
199
|
+
{
|
|
200
|
+
src: '.env.prod.hbs',
|
|
201
|
+
dest: '.env.prod',
|
|
202
|
+
data: { dbUrl, prismaDbUrl },
|
|
203
|
+
},
|
|
194
204
|
{
|
|
195
205
|
src: 'readme.md.hbs',
|
|
196
206
|
dest: 'README.md',
|
|
197
|
-
data: { dbUrl, prismaDbUrl },
|
|
207
|
+
data: { dbUrl, prismaDbUrl, appName },
|
|
198
208
|
},
|
|
199
209
|
{
|
|
200
210
|
// We'll write .env using the same content as .env.sample
|
|
@@ -218,6 +228,18 @@ async function writeTemplateFiles(dirname, cwd, options) {
|
|
|
218
228
|
dest: 'custom/tsconfig.json',
|
|
219
229
|
data: {},
|
|
220
230
|
},
|
|
231
|
+
{
|
|
232
|
+
src: 'Dockerfile.hbs',
|
|
233
|
+
dest: 'Dockerfile',
|
|
234
|
+
data: { nodeMajor },
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
src: '.dockerignore.hbs',
|
|
238
|
+
dest: '.dockerignore',
|
|
239
|
+
data: {
|
|
240
|
+
sqliteFile: detectDbProvider(options.db).startsWith('sqlite') ? options.db.split('://')[1] : null,
|
|
241
|
+
},
|
|
242
|
+
}
|
|
221
243
|
];
|
|
222
244
|
|
|
223
245
|
for (const task of templateTasks) {
|
|
@@ -228,22 +250,25 @@ async function writeTemplateFiles(dirname, cwd, options) {
|
|
|
228
250
|
// fse.ensureDirSync(path.dirname(destPath));
|
|
229
251
|
|
|
230
252
|
if (task.empty) {
|
|
231
|
-
fs.
|
|
253
|
+
await fs.promises.writeFile(destPath, '');
|
|
232
254
|
} else {
|
|
233
255
|
const templatePath = path.join(dirname, 'templates', task.src);
|
|
234
256
|
const compiled = renderHBSTemplate(templatePath, task.data);
|
|
235
|
-
fs.
|
|
257
|
+
await fs.promises.writeFile(destPath, compiled);
|
|
236
258
|
}
|
|
237
259
|
}
|
|
238
260
|
}
|
|
239
261
|
|
|
240
262
|
async function installDependencies(ctx, cwd) {
|
|
241
|
-
const
|
|
263
|
+
const nodeBinary = process.execPath; // Path to the Node.js binary running this script
|
|
264
|
+
const npmPath = path.join(path.dirname(nodeBinary), 'npm'); // Path to the npm executable
|
|
242
265
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
await
|
|
266
|
+
const customDir = ctx.customDir;
|
|
267
|
+
const res = await Promise.all([
|
|
268
|
+
await execAsync(`${nodeBinary} ${npmPath} install`, { cwd, env: { PATH: process.env.PATH } }),
|
|
269
|
+
await execAsync(`${nodeBinary} ${npmPath} install`, { cwd: customDir, env: { PATH: process.env.PATH } }),
|
|
246
270
|
]);
|
|
271
|
+
// console.log(chalk.dim(`Dependencies installed in ${cwd} and ${customDir}: \n${res[0].stdout}${res[1].stdout}`));
|
|
247
272
|
}
|
|
248
273
|
|
|
249
274
|
function generateFinalInstructions(skipPrismaSetup, options) {
|
|
@@ -251,15 +276,15 @@ function generateFinalInstructions(skipPrismaSetup, options) {
|
|
|
251
276
|
if (!skipPrismaSetup)
|
|
252
277
|
instruction += `
|
|
253
278
|
${chalk.dim('// Go to the project directory')}
|
|
254
|
-
${chalk.cyan(
|
|
279
|
+
${chalk.dim('$')}${chalk.cyan(` cd ${options.appName}`)}\n`;
|
|
255
280
|
|
|
256
281
|
instruction += `
|
|
257
282
|
${chalk.dim('// Generate and apply initial migration')}
|
|
258
|
-
${chalk.cyan('
|
|
283
|
+
${chalk.dim('$')}${chalk.cyan(' npm run makemigration -- --name init && npm run migrate:local')}\n`;
|
|
259
284
|
|
|
260
285
|
instruction += `
|
|
261
286
|
${chalk.dim('// Start dev server with tsx watch for hot-reloading')}
|
|
262
|
-
${chalk.cyan('
|
|
287
|
+
${chalk.dim('$')}${chalk.cyan(' npm run dev')}\n
|
|
263
288
|
`;
|
|
264
289
|
|
|
265
290
|
instruction += '😉 Happy coding!';
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import jiti from 'jiti';
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
export async function loadAdminForthConfig() {
|
|
8
|
+
const configFileName = 'index.ts';
|
|
9
|
+
const configPath = path.resolve(process.cwd(), configFileName);
|
|
10
|
+
|
|
11
|
+
try {
|
|
12
|
+
await fs.access(configPath);
|
|
13
|
+
} catch (error) {
|
|
14
|
+
console.error(chalk.red(`\nError: Configuration file not found at ${configPath}`));
|
|
15
|
+
console.error(chalk.yellow(`Please ensure you are running this command from your project's root directory and the '${configFileName}' file exists.`));
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
const _require = jiti(import.meta.url, {
|
|
21
|
+
interopDefault: true,
|
|
22
|
+
cache: true,
|
|
23
|
+
esmResolve: true,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const configModule = _require(configPath);
|
|
27
|
+
|
|
28
|
+
const adminInstance = configModule.admin || configModule.default?.admin;
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
if (!adminInstance) {
|
|
32
|
+
throw new Error(`Could not find 'admin' export in ${configFileName}. Please ensure your config file exports the AdminForth instance like: 'export const admin = new AdminForth({...});'`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const config = adminInstance.config;
|
|
36
|
+
|
|
37
|
+
if (!config || typeof config !== 'object') {
|
|
38
|
+
throw new Error(`Invalid configuration found in admin instance from ${configFileName}. Expected admin.config to be an object.`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (!config.resources || !Array.isArray(config.resources)) {
|
|
42
|
+
console.warn(chalk.yellow(`Warning: The loaded configuration seems incomplete. Missing 'resources' array.`));
|
|
43
|
+
}
|
|
44
|
+
if (!config.customization?.customComponentsDir) {
|
|
45
|
+
console.warn(chalk.yellow(`Warning: 'customization.customComponentsDir' is not defined in the config. Defaulting might occur elsewhere, but defining it is recommended.`));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
console.log(chalk.dim(`Loaded configuration from ${configPath}`));
|
|
50
|
+
return config;
|
|
51
|
+
|
|
52
|
+
} catch (error) {
|
|
53
|
+
console.error(chalk.red(`\nError loading or parsing configuration file: ${configPath}`));
|
|
54
|
+
console.error(error);
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
}
|