@mercurjs/dashboard-sdk 2.2.0-rc.0 → 2.2.0-rc.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/dist/chunk-3RG5ZIWI.js +10 -0
- package/dist/index.cjs +16 -917
- package/dist/index.d.cts +159 -80
- package/dist/index.d.ts +173 -0
- package/dist/index.js +23 -0
- package/dist/types-zgnwn8ra.d.cts +74 -0
- package/dist/types-zgnwn8ra.d.ts +74 -0
- package/dist/vite.cjs +1201 -0
- package/dist/vite.d.cts +14 -0
- package/dist/vite.d.ts +14 -0
- package/dist/vite.js +1180 -0
- package/package.json +13 -4
package/dist/vite.js
ADDED
|
@@ -0,0 +1,1180 @@
|
|
|
1
|
+
import {
|
|
2
|
+
__require
|
|
3
|
+
} from "./chunk-3RG5ZIWI.js";
|
|
4
|
+
|
|
5
|
+
// src/plugin.ts
|
|
6
|
+
import path8 from "path";
|
|
7
|
+
import fs7 from "fs";
|
|
8
|
+
|
|
9
|
+
// src/utils.ts
|
|
10
|
+
import fs from "fs";
|
|
11
|
+
import path from "path";
|
|
12
|
+
|
|
13
|
+
// src/babel.ts
|
|
14
|
+
import { parse } from "@babel/parser";
|
|
15
|
+
import _traverse from "@babel/traverse";
|
|
16
|
+
import {
|
|
17
|
+
isBooleanLiteral,
|
|
18
|
+
isCallExpression,
|
|
19
|
+
isFunctionDeclaration,
|
|
20
|
+
isIdentifier,
|
|
21
|
+
isMemberExpression,
|
|
22
|
+
isNumericLiteral,
|
|
23
|
+
isObjectExpression,
|
|
24
|
+
isObjectProperty,
|
|
25
|
+
isStringLiteral,
|
|
26
|
+
isVariableDeclaration,
|
|
27
|
+
isVariableDeclarator,
|
|
28
|
+
isArrayExpression
|
|
29
|
+
} from "@babel/types";
|
|
30
|
+
var traverse;
|
|
31
|
+
if (typeof _traverse === "function") {
|
|
32
|
+
traverse = _traverse;
|
|
33
|
+
} else {
|
|
34
|
+
traverse = _traverse.default;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// src/constants.ts
|
|
38
|
+
var VALID_FILE_EXTENSIONS = [".tsx", ".ts", ".jsx", ".js"];
|
|
39
|
+
var CONFIG_VIRTUAL_MODULE = "virtual:mercur/config";
|
|
40
|
+
var ROUTES_VIRTUAL_MODULE = "virtual:mercur/routes";
|
|
41
|
+
var MENU_ITEMS_VIRTUAL_MODULE = "virtual:mercur/menu-items";
|
|
42
|
+
var I18N_VIRTUAL_MODULE = "virtual:mercur/i18n";
|
|
43
|
+
var WIDGETS_VIRTUAL_MODULE = "virtual:mercur/widgets";
|
|
44
|
+
var NAVIGATION_VIRTUAL_MODULE = "virtual:mercur/navigation";
|
|
45
|
+
var CUSTOM_FIELDS_VIRTUAL_MODULE = "virtual:mercur/custom-fields";
|
|
46
|
+
var RESOLVED_CONFIG_MODULE = "\0" + CONFIG_VIRTUAL_MODULE;
|
|
47
|
+
var RESOLVED_ROUTES_MODULE = "\0" + ROUTES_VIRTUAL_MODULE;
|
|
48
|
+
var RESOLVED_MENU_ITEMS_MODULE = "\0" + MENU_ITEMS_VIRTUAL_MODULE;
|
|
49
|
+
var RESOLVED_I18N_MODULE = "\0" + I18N_VIRTUAL_MODULE;
|
|
50
|
+
var RESOLVED_WIDGETS_MODULE = "\0" + WIDGETS_VIRTUAL_MODULE;
|
|
51
|
+
var RESOLVED_NAVIGATION_MODULE = "\0" + NAVIGATION_VIRTUAL_MODULE;
|
|
52
|
+
var RESOLVED_CUSTOM_FIELDS_MODULE = "\0" + CUSTOM_FIELDS_VIRTUAL_MODULE;
|
|
53
|
+
var VIRTUAL_MODULES = [
|
|
54
|
+
CONFIG_VIRTUAL_MODULE,
|
|
55
|
+
ROUTES_VIRTUAL_MODULE,
|
|
56
|
+
MENU_ITEMS_VIRTUAL_MODULE,
|
|
57
|
+
I18N_VIRTUAL_MODULE,
|
|
58
|
+
WIDGETS_VIRTUAL_MODULE,
|
|
59
|
+
NAVIGATION_VIRTUAL_MODULE,
|
|
60
|
+
CUSTOM_FIELDS_VIRTUAL_MODULE
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
// src/utils.ts
|
|
64
|
+
function normalizePath(filePath) {
|
|
65
|
+
return filePath.replace(/\\/g, "/");
|
|
66
|
+
}
|
|
67
|
+
function crawlModuleFiles(dir) {
|
|
68
|
+
const files = [];
|
|
69
|
+
if (!fs.existsSync(dir)) return files;
|
|
70
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
71
|
+
const full = path.join(dir, entry.name);
|
|
72
|
+
if (entry.isDirectory()) {
|
|
73
|
+
files.push(...crawlModuleFiles(full));
|
|
74
|
+
} else if (entry.isFile()) {
|
|
75
|
+
const ext = path.extname(entry.name);
|
|
76
|
+
const base = path.basename(entry.name, ext);
|
|
77
|
+
if (base.endsWith(".d")) continue;
|
|
78
|
+
if (base.startsWith("_") || base === "index") continue;
|
|
79
|
+
if (VALID_FILE_EXTENSIONS.includes(ext)) files.push(full);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return files;
|
|
83
|
+
}
|
|
84
|
+
function getParserOptions(file) {
|
|
85
|
+
const options = {
|
|
86
|
+
sourceType: "module",
|
|
87
|
+
plugins: ["jsx"]
|
|
88
|
+
};
|
|
89
|
+
if (file.endsWith(".ts") || file.endsWith(".tsx")) {
|
|
90
|
+
options.plugins.push("typescript");
|
|
91
|
+
}
|
|
92
|
+
return options;
|
|
93
|
+
}
|
|
94
|
+
function resolveExports(moduleExports) {
|
|
95
|
+
if ("default" in moduleExports && moduleExports.default && "default" in moduleExports.default) {
|
|
96
|
+
return resolveExports(moduleExports.default);
|
|
97
|
+
}
|
|
98
|
+
return moduleExports;
|
|
99
|
+
}
|
|
100
|
+
async function getFileExports(path10) {
|
|
101
|
+
const { unregister } = await safeRegister();
|
|
102
|
+
const module = __require(path10);
|
|
103
|
+
unregister();
|
|
104
|
+
return resolveExports(module);
|
|
105
|
+
}
|
|
106
|
+
var safeRegister = async () => {
|
|
107
|
+
const { register } = await import("esbuild-register/dist/node");
|
|
108
|
+
let res;
|
|
109
|
+
try {
|
|
110
|
+
res = register({
|
|
111
|
+
format: "cjs",
|
|
112
|
+
loader: "ts"
|
|
113
|
+
});
|
|
114
|
+
} catch {
|
|
115
|
+
res = {
|
|
116
|
+
unregister: () => {
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
return res;
|
|
121
|
+
};
|
|
122
|
+
function hasDefaultExport(ast) {
|
|
123
|
+
let found = false;
|
|
124
|
+
traverse(ast, {
|
|
125
|
+
ExportDefaultDeclaration() {
|
|
126
|
+
found = true;
|
|
127
|
+
},
|
|
128
|
+
AssignmentExpression(path10) {
|
|
129
|
+
if (path10.node.left.type === "MemberExpression" && path10.node.left.object.type === "Identifier" && path10.node.left.object.name === "exports" && path10.node.left.property.type === "Identifier" && path10.node.left.property.name === "default") {
|
|
130
|
+
found = true;
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
ExportNamedDeclaration(path10) {
|
|
134
|
+
const specifiers = path10.node.specifiers;
|
|
135
|
+
if (specifiers?.some(
|
|
136
|
+
(s) => s.type === "ExportSpecifier" && s.exported.type === "Identifier" && s.exported.name === "default"
|
|
137
|
+
)) {
|
|
138
|
+
found = true;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
return found;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// src/routes.ts
|
|
146
|
+
import fs2 from "fs";
|
|
147
|
+
import path2 from "path";
|
|
148
|
+
function getRoute(file, routesDir) {
|
|
149
|
+
const importPath = normalizePath(file);
|
|
150
|
+
const normalizedRoutesDir = normalizePath(routesDir);
|
|
151
|
+
return importPath.replace(normalizedRoutesDir, "").replace(/\[\[\*\]\]/g, "*?").replace(/\[\*\]/g, "*").replace(/\(([^[\])]+)\)/g, "$1?").replace(/\[\[([^\]]+)\]\]/g, ":$1?").replace(/\[([^\]]+)\]/g, ":$1").replace(
|
|
152
|
+
new RegExp(
|
|
153
|
+
`/page\\.(${VALID_FILE_EXTENSIONS.map((ext) => ext.slice(1)).join("|")})$`
|
|
154
|
+
),
|
|
155
|
+
""
|
|
156
|
+
) || "/";
|
|
157
|
+
}
|
|
158
|
+
function crawlRoutes(dir, pattern = "page") {
|
|
159
|
+
const files = [];
|
|
160
|
+
if (!fs2.existsSync(dir)) {
|
|
161
|
+
return files;
|
|
162
|
+
}
|
|
163
|
+
const entries = fs2.readdirSync(dir, { withFileTypes: true });
|
|
164
|
+
for (const entry of entries) {
|
|
165
|
+
const fullPath = path2.join(dir, entry.name);
|
|
166
|
+
if (entry.isDirectory()) {
|
|
167
|
+
files.push(...crawlRoutes(fullPath, pattern));
|
|
168
|
+
} else if (entry.isFile()) {
|
|
169
|
+
const ext = path2.extname(entry.name);
|
|
170
|
+
const baseName = path2.basename(entry.name, ext);
|
|
171
|
+
if (baseName === pattern && VALID_FILE_EXTENSIONS.includes(ext)) {
|
|
172
|
+
files.push(fullPath);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return files;
|
|
177
|
+
}
|
|
178
|
+
function hasConfigPublic(ast) {
|
|
179
|
+
let found = false;
|
|
180
|
+
traverse(ast, {
|
|
181
|
+
ExportNamedDeclaration(path10) {
|
|
182
|
+
const declaration = path10.node.declaration;
|
|
183
|
+
if (!isVariableDeclaration(declaration)) return;
|
|
184
|
+
for (const decl of declaration.declarations) {
|
|
185
|
+
if (isVariableDeclarator(decl) && isIdentifier(decl.id, { name: "config" }) && decl.init?.type === "ObjectExpression") {
|
|
186
|
+
const publicProp = decl.init.properties.find(
|
|
187
|
+
(prop) => isObjectProperty(prop) && isIdentifier(prop.key, { name: "public" }) && isBooleanLiteral(prop.value, { value: true })
|
|
188
|
+
);
|
|
189
|
+
if (publicProp) {
|
|
190
|
+
found = true;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
return found;
|
|
197
|
+
}
|
|
198
|
+
function getNamedExports(ast) {
|
|
199
|
+
let hasHandle = false;
|
|
200
|
+
let hasLoader = false;
|
|
201
|
+
traverse(ast, {
|
|
202
|
+
ExportNamedDeclaration(path10) {
|
|
203
|
+
const declaration = path10.node.declaration;
|
|
204
|
+
if (declaration?.type === "VariableDeclaration") {
|
|
205
|
+
declaration.declarations.forEach((decl) => {
|
|
206
|
+
if (decl.id.type === "Identifier" && decl.id.name === "handle") {
|
|
207
|
+
hasHandle = true;
|
|
208
|
+
}
|
|
209
|
+
if (decl.id.type === "Identifier" && decl.id.name === "loader") {
|
|
210
|
+
hasLoader = true;
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
if (declaration?.type === "FunctionDeclaration" && declaration.id?.name === "loader") {
|
|
215
|
+
hasLoader = true;
|
|
216
|
+
}
|
|
217
|
+
if (declaration?.type === "FunctionDeclaration" && declaration.id?.name === "handle") {
|
|
218
|
+
hasHandle = true;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
return { hasHandle, hasLoader };
|
|
223
|
+
}
|
|
224
|
+
function generateRouteComponentName(index) {
|
|
225
|
+
return `RouteComponent${index}`;
|
|
226
|
+
}
|
|
227
|
+
function generateHandleName(index) {
|
|
228
|
+
return `RouteHandle${index}`;
|
|
229
|
+
}
|
|
230
|
+
function generateLoaderName(index) {
|
|
231
|
+
return `RouteLoader${index}`;
|
|
232
|
+
}
|
|
233
|
+
function generateImports(file, index, hasHandle, hasLoader) {
|
|
234
|
+
const imports = [];
|
|
235
|
+
const componentName = generateRouteComponentName(index);
|
|
236
|
+
const importPath = normalizePath(file);
|
|
237
|
+
if (!hasHandle && !hasLoader) {
|
|
238
|
+
imports.push(`import ${componentName} from "${importPath}"`);
|
|
239
|
+
} else {
|
|
240
|
+
const namedImports = [
|
|
241
|
+
hasHandle && `handle as ${generateHandleName(index)}`,
|
|
242
|
+
hasLoader && `loader as ${generateLoaderName(index)}`
|
|
243
|
+
].filter(Boolean).join(", ");
|
|
244
|
+
imports.push(`import ${componentName}, { ${namedImports} } from "${importPath}"`);
|
|
245
|
+
}
|
|
246
|
+
return imports;
|
|
247
|
+
}
|
|
248
|
+
function generateRouteObject(routePath, index, hasHandle, hasLoader, isPublic) {
|
|
249
|
+
return {
|
|
250
|
+
Component: generateRouteComponentName(index),
|
|
251
|
+
path: routePath,
|
|
252
|
+
handle: hasHandle ? generateHandleName(index) : void 0,
|
|
253
|
+
loader: hasLoader ? generateLoaderName(index) : void 0,
|
|
254
|
+
isPublic
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
function formatRoute(route, indent = " ") {
|
|
258
|
+
let result = `${indent}{
|
|
259
|
+
`;
|
|
260
|
+
result += `${indent} Component: ${route.Component},
|
|
261
|
+
`;
|
|
262
|
+
result += `${indent} path: "${route.path}"`;
|
|
263
|
+
if (route.handle) {
|
|
264
|
+
result += `,
|
|
265
|
+
${indent} handle: ${route.handle}`;
|
|
266
|
+
}
|
|
267
|
+
if (route.loader) {
|
|
268
|
+
result += `,
|
|
269
|
+
${indent} loader: ${route.loader}`;
|
|
270
|
+
}
|
|
271
|
+
if (route.isPublic) {
|
|
272
|
+
result += `,
|
|
273
|
+
${indent} isPublic: true`;
|
|
274
|
+
}
|
|
275
|
+
if (route.children?.length) {
|
|
276
|
+
result += `,
|
|
277
|
+
${indent} children: [
|
|
278
|
+
`;
|
|
279
|
+
result += route.children.map((child) => formatRoute(child, indent + " ")).join(",\n");
|
|
280
|
+
result += `
|
|
281
|
+
${indent} ]`;
|
|
282
|
+
}
|
|
283
|
+
result += `
|
|
284
|
+
${indent}}`;
|
|
285
|
+
return result;
|
|
286
|
+
}
|
|
287
|
+
function parseFile(file, routesDir, index) {
|
|
288
|
+
try {
|
|
289
|
+
const code = fs2.readFileSync(file, "utf-8");
|
|
290
|
+
const ast = parse(code, getParserOptions(file));
|
|
291
|
+
if (!hasDefaultExport(ast)) {
|
|
292
|
+
return null;
|
|
293
|
+
}
|
|
294
|
+
const { hasHandle, hasLoader } = getNamedExports(ast);
|
|
295
|
+
const isPublic = hasConfigPublic(ast);
|
|
296
|
+
const routePath = getRoute(file, routesDir);
|
|
297
|
+
const imports = generateImports(file, index, hasHandle, hasLoader);
|
|
298
|
+
const route = generateRouteObject(routePath, index, hasHandle, hasLoader, isPublic);
|
|
299
|
+
return {
|
|
300
|
+
imports,
|
|
301
|
+
route
|
|
302
|
+
};
|
|
303
|
+
} catch {
|
|
304
|
+
return null;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
function buildRouteTree(results) {
|
|
308
|
+
const routeMap = /* @__PURE__ */ new Map();
|
|
309
|
+
const sortedResults = [...results].sort(
|
|
310
|
+
(a, b) => a.route.path.split("/").length - b.route.path.split("/").length
|
|
311
|
+
);
|
|
312
|
+
for (const result of sortedResults) {
|
|
313
|
+
const routePath = result.route.path;
|
|
314
|
+
const isParallel = routePath.includes("/@");
|
|
315
|
+
if (isParallel) {
|
|
316
|
+
const parentPath = routePath.split("/@")[0];
|
|
317
|
+
const parent = routeMap.get(parentPath);
|
|
318
|
+
if (parent) {
|
|
319
|
+
parent.route.children = parent.route.children ?? [];
|
|
320
|
+
parent.route.children.push({
|
|
321
|
+
...result.route,
|
|
322
|
+
path: result.route.path.replace("/@", "/")
|
|
323
|
+
});
|
|
324
|
+
parent.imports.push(...result.imports);
|
|
325
|
+
} else {
|
|
326
|
+
routeMap.set(routePath, result);
|
|
327
|
+
}
|
|
328
|
+
} else {
|
|
329
|
+
routeMap.set(routePath, result);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
return Array.from(routeMap.values());
|
|
333
|
+
}
|
|
334
|
+
function generateRoutes({ srcDir, pluginExtensions }) {
|
|
335
|
+
const routesDir = path2.join(srcDir, "routes");
|
|
336
|
+
let index = 0;
|
|
337
|
+
const results = [];
|
|
338
|
+
for (const file of crawlRoutes(routesDir)) {
|
|
339
|
+
const result = parseFile(file, routesDir, index);
|
|
340
|
+
if (result) {
|
|
341
|
+
results.push(result);
|
|
342
|
+
index++;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
const pluginDeclarations = pluginExtensions.map(
|
|
346
|
+
(ext, i) => `const __plugin${i} = (await import("${normalizePath(ext)}")).default`
|
|
347
|
+
);
|
|
348
|
+
const pluginSpreads = pluginExtensions.map(
|
|
349
|
+
(_, i) => ` ...(__plugin${i}.routeModule?.routes ?? [])`
|
|
350
|
+
);
|
|
351
|
+
const routeTree = buildRouteTree(results);
|
|
352
|
+
const appImports = routeTree.flatMap((r) => r.imports);
|
|
353
|
+
const appRoutes = routeTree.map((r) => formatRoute(r.route));
|
|
354
|
+
const allImports = [...appImports];
|
|
355
|
+
const allRoutes = [...appRoutes, ...pluginSpreads];
|
|
356
|
+
if (allImports.length === 0 && pluginDeclarations.length === 0 && allRoutes.length === 0) {
|
|
357
|
+
return `export const customRoutes = []`;
|
|
358
|
+
}
|
|
359
|
+
return `${allImports.join("\n")}
|
|
360
|
+
|
|
361
|
+
${pluginDeclarations.join("\n")}
|
|
362
|
+
|
|
363
|
+
export const customRoutes = [
|
|
364
|
+
${allRoutes.join(",\n")}
|
|
365
|
+
]`;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// src/menu-items.ts
|
|
369
|
+
import fs3 from "fs";
|
|
370
|
+
import path3 from "path";
|
|
371
|
+
function crawlRoutes2(dir, pattern = "page") {
|
|
372
|
+
const files = [];
|
|
373
|
+
if (!fs3.existsSync(dir)) {
|
|
374
|
+
return files;
|
|
375
|
+
}
|
|
376
|
+
const entries = fs3.readdirSync(dir, { withFileTypes: true });
|
|
377
|
+
for (const entry of entries) {
|
|
378
|
+
const fullPath = path3.join(dir, entry.name);
|
|
379
|
+
if (entry.isDirectory()) {
|
|
380
|
+
files.push(...crawlRoutes2(fullPath, pattern));
|
|
381
|
+
} else if (entry.isFile()) {
|
|
382
|
+
const ext = path3.extname(entry.name);
|
|
383
|
+
const baseName = path3.basename(entry.name, ext);
|
|
384
|
+
if (baseName === pattern && VALID_FILE_EXTENSIONS.includes(ext)) {
|
|
385
|
+
files.push(fullPath);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
return files;
|
|
390
|
+
}
|
|
391
|
+
function getRoute2(file, routesDir) {
|
|
392
|
+
const importPath = normalizePath(file);
|
|
393
|
+
const normalizedRoutesDir = normalizePath(routesDir);
|
|
394
|
+
return importPath.replace(normalizedRoutesDir, "").replace(/\[\[\*\]\]/g, "*?").replace(/\[\*\]/g, "*").replace(/\(([^[\])]+)\)/g, "$1?").replace(/\[\[([^\]]+)\]\]/g, ":$1?").replace(/\[([^\]]+)\]/g, ":$1").replace(
|
|
395
|
+
new RegExp(
|
|
396
|
+
`/page\\.(${VALID_FILE_EXTENSIONS.map((ext) => ext.slice(1)).join("|")})$`
|
|
397
|
+
),
|
|
398
|
+
""
|
|
399
|
+
) || "/";
|
|
400
|
+
}
|
|
401
|
+
function getConfigObjectProperties(path10) {
|
|
402
|
+
if (isVariableDeclarator(path10.node)) {
|
|
403
|
+
const decl = isIdentifier(path10.node.id, { name: "config" }) ? path10.node : null;
|
|
404
|
+
if (!decl) return null;
|
|
405
|
+
if (isCallExpression(decl.init) && decl.init.arguments.length > 0 && isObjectExpression(decl.init.arguments[0])) {
|
|
406
|
+
return decl.init.arguments[0].properties;
|
|
407
|
+
}
|
|
408
|
+
if (isObjectExpression(decl.init)) {
|
|
409
|
+
return decl.init.properties;
|
|
410
|
+
}
|
|
411
|
+
return null;
|
|
412
|
+
}
|
|
413
|
+
const declaration = path10.node.declaration;
|
|
414
|
+
if (isVariableDeclaration(declaration)) {
|
|
415
|
+
const configDecl = declaration.declarations.find(
|
|
416
|
+
(d) => isVariableDeclarator(d) && isIdentifier(d.id, { name: "config" })
|
|
417
|
+
);
|
|
418
|
+
if (configDecl && isCallExpression(configDecl.init) && configDecl.init.arguments.length > 0 && isObjectExpression(configDecl.init.arguments[0])) {
|
|
419
|
+
return configDecl.init.arguments[0].properties;
|
|
420
|
+
}
|
|
421
|
+
const directDecl = declaration.declarations.find(
|
|
422
|
+
(d) => isVariableDeclarator(d) && isIdentifier(d.id, { name: "config" })
|
|
423
|
+
);
|
|
424
|
+
if (directDecl && isObjectExpression(directDecl.init)) {
|
|
425
|
+
return directDecl.init.properties;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
return null;
|
|
429
|
+
}
|
|
430
|
+
function processConfigProperties(properties) {
|
|
431
|
+
const hasProperty = (name) => properties.some(
|
|
432
|
+
(prop) => isObjectProperty(prop) && isIdentifier(prop.key, { name })
|
|
433
|
+
);
|
|
434
|
+
const hasLabel = hasProperty("label");
|
|
435
|
+
if (!hasLabel) {
|
|
436
|
+
return null;
|
|
437
|
+
}
|
|
438
|
+
const hasIcon = hasProperty("icon");
|
|
439
|
+
const nested = properties.find(
|
|
440
|
+
(prop) => isObjectProperty(prop) && isIdentifier(prop.key, { name: "nested" })
|
|
441
|
+
);
|
|
442
|
+
let nestedValue;
|
|
443
|
+
if (nested && isObjectProperty(nested) && isStringLiteral(nested.value)) {
|
|
444
|
+
nestedValue = nested.value.value;
|
|
445
|
+
}
|
|
446
|
+
const translationNs = properties.find(
|
|
447
|
+
(prop) => isObjectProperty(prop) && isIdentifier(prop.key, { name: "translationNs" })
|
|
448
|
+
);
|
|
449
|
+
let translationNsValue;
|
|
450
|
+
if (translationNs && isObjectProperty(translationNs) && isStringLiteral(translationNs.value)) {
|
|
451
|
+
translationNsValue = translationNs.value.value;
|
|
452
|
+
}
|
|
453
|
+
const rank = properties.find(
|
|
454
|
+
(prop) => isObjectProperty(prop) && isIdentifier(prop.key, { name: "rank" })
|
|
455
|
+
);
|
|
456
|
+
let rankValue;
|
|
457
|
+
if (rank && isObjectProperty(rank) && isNumericLiteral(rank.value)) {
|
|
458
|
+
rankValue = rank.value.value;
|
|
459
|
+
}
|
|
460
|
+
return { label: hasLabel, icon: hasIcon, rank: rankValue, nested: nestedValue, translationNs: translationNsValue };
|
|
461
|
+
}
|
|
462
|
+
function getRouteConfig(file) {
|
|
463
|
+
try {
|
|
464
|
+
const code = fs3.readFileSync(file, "utf-8");
|
|
465
|
+
const ast = parse(code, getParserOptions(file));
|
|
466
|
+
let config = null;
|
|
467
|
+
let configFound = false;
|
|
468
|
+
traverse(ast, {
|
|
469
|
+
VariableDeclarator(path10) {
|
|
470
|
+
if (configFound) return;
|
|
471
|
+
const properties = getConfigObjectProperties(path10);
|
|
472
|
+
if (!properties) return;
|
|
473
|
+
config = processConfigProperties(properties);
|
|
474
|
+
if (config) configFound = true;
|
|
475
|
+
},
|
|
476
|
+
ExportNamedDeclaration(path10) {
|
|
477
|
+
if (configFound) return;
|
|
478
|
+
const properties = getConfigObjectProperties(path10);
|
|
479
|
+
if (!properties) return;
|
|
480
|
+
config = processConfigProperties(properties);
|
|
481
|
+
if (config) configFound = true;
|
|
482
|
+
}
|
|
483
|
+
});
|
|
484
|
+
return config;
|
|
485
|
+
} catch {
|
|
486
|
+
return null;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
function generateRouteConfigName(index) {
|
|
490
|
+
return `RouteConfig${index}`;
|
|
491
|
+
}
|
|
492
|
+
function generateImport(file, index) {
|
|
493
|
+
const importPath = normalizePath(file);
|
|
494
|
+
return `import { config as ${generateRouteConfigName(index)} } from "${importPath}"`;
|
|
495
|
+
}
|
|
496
|
+
function generateMenuItem(config, file, routesDir, index) {
|
|
497
|
+
const configName = generateRouteConfigName(index);
|
|
498
|
+
return {
|
|
499
|
+
label: `${configName}.label`,
|
|
500
|
+
icon: config.icon ? `${configName}.icon` : void 0,
|
|
501
|
+
path: getRoute2(file, routesDir),
|
|
502
|
+
rank: config.rank,
|
|
503
|
+
nested: config.nested,
|
|
504
|
+
translationNs: config.translationNs ? `${configName}.translationNs` : void 0
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
function formatMenuItem(menuItem) {
|
|
508
|
+
const parts = [
|
|
509
|
+
` label: ${menuItem.label}`,
|
|
510
|
+
` icon: ${menuItem.icon || "undefined"}`,
|
|
511
|
+
` path: "${menuItem.path}"`,
|
|
512
|
+
` rank: ${menuItem.rank !== void 0 ? menuItem.rank : "undefined"}`,
|
|
513
|
+
` nested: ${menuItem.nested ? `"${menuItem.nested}"` : "undefined"}`,
|
|
514
|
+
` translationNs: ${menuItem.translationNs || "undefined"}`
|
|
515
|
+
];
|
|
516
|
+
return ` {
|
|
517
|
+
${parts.join(",\n")}
|
|
518
|
+
}`;
|
|
519
|
+
}
|
|
520
|
+
function parseMenuItemFile(file, routesDir, index) {
|
|
521
|
+
const config = getRouteConfig(file);
|
|
522
|
+
if (!config) {
|
|
523
|
+
return null;
|
|
524
|
+
}
|
|
525
|
+
return {
|
|
526
|
+
import: generateImport(file, index),
|
|
527
|
+
menuItem: generateMenuItem(config, file, routesDir, index)
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
function generateMenuItems({ srcDir, pluginExtensions }) {
|
|
531
|
+
const routesDir = path3.join(srcDir, "routes");
|
|
532
|
+
let index = 0;
|
|
533
|
+
const results = [];
|
|
534
|
+
for (const file of crawlRoutes2(routesDir)) {
|
|
535
|
+
const result = parseMenuItemFile(file, routesDir, index);
|
|
536
|
+
if (result) {
|
|
537
|
+
results.push(result);
|
|
538
|
+
index++;
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
const pluginDeclarations = pluginExtensions.map(
|
|
542
|
+
(ext, i) => `const __plugin${i} = (await import("${normalizePath(ext)}")).default`
|
|
543
|
+
);
|
|
544
|
+
const pluginSpreads = pluginExtensions.map(
|
|
545
|
+
(_, i) => ` ...(__plugin${i}.menuItemModule?.menuItems ?? [])`
|
|
546
|
+
);
|
|
547
|
+
const appImports = results.map((r) => r.import);
|
|
548
|
+
const appMenuItems = results.map((r) => formatMenuItem(r.menuItem));
|
|
549
|
+
const allImports = [...appImports];
|
|
550
|
+
const allMenuItems = [...appMenuItems, ...pluginSpreads];
|
|
551
|
+
if (allImports.length === 0 && pluginDeclarations.length === 0 && allMenuItems.length === 0) {
|
|
552
|
+
return `export default { menuItems: [] }`;
|
|
553
|
+
}
|
|
554
|
+
return `${allImports.join("\n")}
|
|
555
|
+
|
|
556
|
+
${pluginDeclarations.join("\n")}
|
|
557
|
+
|
|
558
|
+
export default {
|
|
559
|
+
menuItems: [
|
|
560
|
+
${allMenuItems.join(",\n")}
|
|
561
|
+
]
|
|
562
|
+
}`;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
// src/i18n.ts
|
|
566
|
+
import fs4 from "fs";
|
|
567
|
+
import path4 from "path";
|
|
568
|
+
function findI18nIndex(srcDir) {
|
|
569
|
+
const i18nDir = path4.join(srcDir, "i18n");
|
|
570
|
+
if (!fs4.existsSync(i18nDir)) {
|
|
571
|
+
return null;
|
|
572
|
+
}
|
|
573
|
+
for (const ext of VALID_FILE_EXTENSIONS) {
|
|
574
|
+
const filePath = path4.join(i18nDir, `index${ext}`);
|
|
575
|
+
if (fs4.existsSync(filePath)) {
|
|
576
|
+
return filePath;
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
return null;
|
|
580
|
+
}
|
|
581
|
+
function generateI18n({ srcDir }) {
|
|
582
|
+
const indexFile = findI18nIndex(srcDir);
|
|
583
|
+
if (!indexFile) {
|
|
584
|
+
return `export default {}`;
|
|
585
|
+
}
|
|
586
|
+
const importPath = normalizePath(indexFile);
|
|
587
|
+
return `import i18nResources from "${importPath}"
|
|
588
|
+
export default i18nResources`;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
// src/widgets.ts
|
|
592
|
+
import fs5 from "fs";
|
|
593
|
+
import path5 from "path";
|
|
594
|
+
function extractZones(value, zones) {
|
|
595
|
+
if (isStringLiteral(value)) {
|
|
596
|
+
zones.push(value.value);
|
|
597
|
+
} else if (isArrayExpression(value)) {
|
|
598
|
+
for (const el of value.elements) {
|
|
599
|
+
if (isStringLiteral(el)) zones.push(el.value);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
function readWidgetConfig(properties) {
|
|
604
|
+
const zones = [];
|
|
605
|
+
let id;
|
|
606
|
+
for (const prop of properties) {
|
|
607
|
+
if (!isObjectProperty(prop)) continue;
|
|
608
|
+
if (isIdentifier(prop.key, { name: "zone" })) {
|
|
609
|
+
extractZones(prop.value, zones);
|
|
610
|
+
}
|
|
611
|
+
if (isIdentifier(prop.key, { name: "id" }) && isStringLiteral(prop.value)) {
|
|
612
|
+
id = prop.value.value;
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
if (zones.length === 0) return null;
|
|
616
|
+
return { zones, id };
|
|
617
|
+
}
|
|
618
|
+
function getConfigProperties(node) {
|
|
619
|
+
if (isCallExpression(node) && node.arguments.length > 0 && isObjectExpression(node.arguments[0])) {
|
|
620
|
+
return node.arguments[0].properties;
|
|
621
|
+
}
|
|
622
|
+
if (isObjectExpression(node)) {
|
|
623
|
+
return node.properties;
|
|
624
|
+
}
|
|
625
|
+
return null;
|
|
626
|
+
}
|
|
627
|
+
function getWidgetConfig(file) {
|
|
628
|
+
try {
|
|
629
|
+
const code = fs5.readFileSync(file, "utf-8");
|
|
630
|
+
const ast = parse(code, getParserOptions(file));
|
|
631
|
+
if (!hasDefaultExport(ast)) return null;
|
|
632
|
+
let info = null;
|
|
633
|
+
const visit = (declaration) => {
|
|
634
|
+
if (info) return;
|
|
635
|
+
if (!isVariableDeclaration(declaration)) return;
|
|
636
|
+
for (const decl of declaration.declarations) {
|
|
637
|
+
if (isVariableDeclarator(decl) && isIdentifier(decl.id, { name: "config" })) {
|
|
638
|
+
const props = getConfigProperties(decl.init);
|
|
639
|
+
if (props) info = readWidgetConfig(props);
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
};
|
|
643
|
+
traverse(ast, {
|
|
644
|
+
ExportNamedDeclaration(p) {
|
|
645
|
+
visit(p.node.declaration);
|
|
646
|
+
},
|
|
647
|
+
VariableDeclaration(p) {
|
|
648
|
+
visit(p.node);
|
|
649
|
+
}
|
|
650
|
+
});
|
|
651
|
+
return info;
|
|
652
|
+
} catch {
|
|
653
|
+
return null;
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
function hashPath(relative) {
|
|
657
|
+
let hash = 0;
|
|
658
|
+
for (let i = 0; i < relative.length; i++) {
|
|
659
|
+
hash = (hash << 5) - hash + relative.charCodeAt(i);
|
|
660
|
+
hash |= 0;
|
|
661
|
+
}
|
|
662
|
+
return Math.abs(hash).toString(36).slice(0, 6);
|
|
663
|
+
}
|
|
664
|
+
function widgetId(info, file, widgetsDir) {
|
|
665
|
+
if (info.id) return info.id;
|
|
666
|
+
const relative = normalizePath(path5.relative(widgetsDir, file));
|
|
667
|
+
return `Widget-${hashPath(relative)}`;
|
|
668
|
+
}
|
|
669
|
+
function parseWidgetFile(file, widgetsDir, index) {
|
|
670
|
+
const info = getWidgetConfig(file);
|
|
671
|
+
if (!info) return null;
|
|
672
|
+
const name = `Widget${index}`;
|
|
673
|
+
const importPath = normalizePath(file);
|
|
674
|
+
const zonesLiteral = JSON.stringify(info.zones);
|
|
675
|
+
const id = widgetId(info, file, widgetsDir);
|
|
676
|
+
return {
|
|
677
|
+
import: `import ${name} from "${importPath}"`,
|
|
678
|
+
entry: ` { Component: ${name}, zone: ${zonesLiteral}, widgetId: ${JSON.stringify(
|
|
679
|
+
id
|
|
680
|
+
)} }`
|
|
681
|
+
};
|
|
682
|
+
}
|
|
683
|
+
function collectWidgets(srcDir, startIndex = 0) {
|
|
684
|
+
const widgetsDir = path5.join(srcDir, "widgets");
|
|
685
|
+
let index = startIndex;
|
|
686
|
+
const results = [];
|
|
687
|
+
for (const file of crawlModuleFiles(widgetsDir)) {
|
|
688
|
+
const result = parseWidgetFile(file, widgetsDir, index);
|
|
689
|
+
if (result) {
|
|
690
|
+
results.push(result);
|
|
691
|
+
index++;
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
return {
|
|
695
|
+
imports: results.map((r) => r.import),
|
|
696
|
+
entries: results.map((r) => r.entry)
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
function generateWidgets({
|
|
700
|
+
srcDir,
|
|
701
|
+
pluginExtensions
|
|
702
|
+
}) {
|
|
703
|
+
const { imports, entries: widgetEntries } = collectWidgets(srcDir);
|
|
704
|
+
const pluginDeclarations = pluginExtensions.map(
|
|
705
|
+
(ext, i) => `const __plugin${i} = (await import("${normalizePath(ext)}")).default`
|
|
706
|
+
);
|
|
707
|
+
const pluginSpreads = pluginExtensions.map(
|
|
708
|
+
(_, i) => ` ...(__plugin${i}.widgetModule?.widgets ?? [])`
|
|
709
|
+
);
|
|
710
|
+
const entries = [...widgetEntries, ...pluginSpreads];
|
|
711
|
+
if (imports.length === 0 && pluginDeclarations.length === 0 && entries.length === 0) {
|
|
712
|
+
return `export default { widgets: [] }`;
|
|
713
|
+
}
|
|
714
|
+
return `${imports.join("\n")}
|
|
715
|
+
|
|
716
|
+
${pluginDeclarations.join("\n")}
|
|
717
|
+
|
|
718
|
+
export default {
|
|
719
|
+
widgets: [
|
|
720
|
+
${entries.join(",\n")}
|
|
721
|
+
]
|
|
722
|
+
}`;
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
// src/navigation.ts
|
|
726
|
+
import fs6 from "fs";
|
|
727
|
+
import path6 from "path";
|
|
728
|
+
function findNavigationFile(srcDir) {
|
|
729
|
+
for (const ext of VALID_FILE_EXTENSIONS) {
|
|
730
|
+
const filePath = path6.join(srcDir, `_navigation${ext}`);
|
|
731
|
+
if (fs6.existsSync(filePath)) return filePath;
|
|
732
|
+
}
|
|
733
|
+
return null;
|
|
734
|
+
}
|
|
735
|
+
function generateNavigation({ srcDir }) {
|
|
736
|
+
const file = findNavigationFile(srcDir);
|
|
737
|
+
if (!file) {
|
|
738
|
+
return `export default { items: [] }`;
|
|
739
|
+
}
|
|
740
|
+
return `import navigationConfig from "${normalizePath(file)}"
|
|
741
|
+
export default navigationConfig`;
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
// src/custom-fields.ts
|
|
745
|
+
import path7 from "path";
|
|
746
|
+
function generateCustomFields({
|
|
747
|
+
srcDir,
|
|
748
|
+
pluginExtensions
|
|
749
|
+
}) {
|
|
750
|
+
const dir = path7.join(srcDir, "custom-fields");
|
|
751
|
+
const files = crawlModuleFiles(dir);
|
|
752
|
+
const imports = files.map(
|
|
753
|
+
(file, i) => `import __cf${i} from "${normalizePath(file)}"`
|
|
754
|
+
);
|
|
755
|
+
const localRefs = files.map((_, i) => `__cf${i}`);
|
|
756
|
+
const pluginDeclarations = pluginExtensions.map(
|
|
757
|
+
(ext, i) => `const __plugin${i} = (await import("${normalizePath(ext)}")).default`
|
|
758
|
+
);
|
|
759
|
+
const pluginSpreads = pluginExtensions.map(
|
|
760
|
+
(_, i) => ` ...(__plugin${i}.customFieldsModule?.configs ?? [])`
|
|
761
|
+
);
|
|
762
|
+
const configs = [
|
|
763
|
+
...localRefs.map((ref) => ` ${ref}`),
|
|
764
|
+
...pluginSpreads
|
|
765
|
+
];
|
|
766
|
+
if (configs.length === 0 && pluginDeclarations.length === 0) {
|
|
767
|
+
return `export default { configs: [] }`;
|
|
768
|
+
}
|
|
769
|
+
return `${imports.join("\n")}
|
|
770
|
+
|
|
771
|
+
${pluginDeclarations.join("\n")}
|
|
772
|
+
|
|
773
|
+
export default {
|
|
774
|
+
configs: [
|
|
775
|
+
${configs.join(",\n")}
|
|
776
|
+
]
|
|
777
|
+
}`;
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
// src/virtual-modules.ts
|
|
781
|
+
function isVirtualModule(id) {
|
|
782
|
+
return VIRTUAL_MODULES.includes(id);
|
|
783
|
+
}
|
|
784
|
+
function resolveVirtualModule(id) {
|
|
785
|
+
return "\0" + id;
|
|
786
|
+
}
|
|
787
|
+
function loadVirtualModule({
|
|
788
|
+
id,
|
|
789
|
+
mercurConfig
|
|
790
|
+
}) {
|
|
791
|
+
if (id === RESOLVED_CONFIG_MODULE) {
|
|
792
|
+
return loadConfigModule(mercurConfig);
|
|
793
|
+
}
|
|
794
|
+
if (id === RESOLVED_ROUTES_MODULE) {
|
|
795
|
+
return loadRoutesModule(mercurConfig);
|
|
796
|
+
}
|
|
797
|
+
if (id === RESOLVED_MENU_ITEMS_MODULE) {
|
|
798
|
+
return loadMenuItemsModule(mercurConfig);
|
|
799
|
+
}
|
|
800
|
+
if (id === RESOLVED_I18N_MODULE) {
|
|
801
|
+
return loadI18nModule(mercurConfig);
|
|
802
|
+
}
|
|
803
|
+
if (id === RESOLVED_WIDGETS_MODULE) {
|
|
804
|
+
return generateWidgets(mercurConfig);
|
|
805
|
+
}
|
|
806
|
+
if (id === RESOLVED_NAVIGATION_MODULE) {
|
|
807
|
+
return generateNavigation(mercurConfig);
|
|
808
|
+
}
|
|
809
|
+
if (id === RESOLVED_CUSTOM_FIELDS_MODULE) {
|
|
810
|
+
return generateCustomFields(mercurConfig);
|
|
811
|
+
}
|
|
812
|
+
return null;
|
|
813
|
+
}
|
|
814
|
+
function loadConfigModule(mercurConfig) {
|
|
815
|
+
return `export default ${JSON.stringify(mercurConfig)}`;
|
|
816
|
+
}
|
|
817
|
+
function loadRoutesModule(mercurConfig) {
|
|
818
|
+
return generateRoutes(mercurConfig);
|
|
819
|
+
}
|
|
820
|
+
function loadMenuItemsModule(mercurConfig) {
|
|
821
|
+
return generateMenuItems(mercurConfig);
|
|
822
|
+
}
|
|
823
|
+
function loadI18nModule(mercurConfig) {
|
|
824
|
+
return generateI18n(mercurConfig);
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
// src/plugin.ts
|
|
828
|
+
function isRouteFile(file) {
|
|
829
|
+
const basename = path8.basename(file, path8.extname(file));
|
|
830
|
+
return basename === "page";
|
|
831
|
+
}
|
|
832
|
+
function isWidgetFile(file) {
|
|
833
|
+
return normalizeSep(file).includes("/src/widgets/");
|
|
834
|
+
}
|
|
835
|
+
function isNavigationFile(file) {
|
|
836
|
+
const basename = path8.basename(file, path8.extname(file));
|
|
837
|
+
return basename === "_navigation";
|
|
838
|
+
}
|
|
839
|
+
function isCustomFieldFile(file) {
|
|
840
|
+
return normalizeSep(file).includes("/src/custom-fields/");
|
|
841
|
+
}
|
|
842
|
+
function normalizeSep(file) {
|
|
843
|
+
return file.replace(/\\/g, "/");
|
|
844
|
+
}
|
|
845
|
+
var UI_MODULE_KEYS = ["admin_ui", "vendor_ui"];
|
|
846
|
+
var MEDUSA_VIRTUAL_MODULES = [
|
|
847
|
+
"virtual:medusa/displays",
|
|
848
|
+
"virtual:medusa/forms",
|
|
849
|
+
"virtual:medusa/i18n",
|
|
850
|
+
"virtual:medusa/menu-items",
|
|
851
|
+
"virtual:medusa/routes",
|
|
852
|
+
"virtual:medusa/widgets",
|
|
853
|
+
"virtual:medusa/links"
|
|
854
|
+
];
|
|
855
|
+
function isMedusaVirtualModule(id) {
|
|
856
|
+
return MEDUSA_VIRTUAL_MODULES.includes(id);
|
|
857
|
+
}
|
|
858
|
+
function resolveMedusaVirtualModule(id) {
|
|
859
|
+
return "\0" + id;
|
|
860
|
+
}
|
|
861
|
+
function isResolvedMedusaVirtualModule(id) {
|
|
862
|
+
return id.startsWith("\0virtual:medusa/");
|
|
863
|
+
}
|
|
864
|
+
function findNodeModulesRoot(configDir) {
|
|
865
|
+
let dir = configDir;
|
|
866
|
+
while (dir !== path8.dirname(dir)) {
|
|
867
|
+
const candidate = path8.join(dir, "node_modules");
|
|
868
|
+
if (fs7.existsSync(candidate) && fs7.statSync(candidate).isDirectory()) {
|
|
869
|
+
return candidate;
|
|
870
|
+
}
|
|
871
|
+
dir = path8.dirname(dir);
|
|
872
|
+
}
|
|
873
|
+
return path8.join(configDir, "node_modules");
|
|
874
|
+
}
|
|
875
|
+
function resolvePluginRoot(resolve, configDir, nodeModulesRoot) {
|
|
876
|
+
try {
|
|
877
|
+
if (resolve.startsWith(".")) {
|
|
878
|
+
const resolved = path8.resolve(configDir, resolve);
|
|
879
|
+
if (fs7.existsSync(resolved)) {
|
|
880
|
+
return fs7.realpathSync(resolved);
|
|
881
|
+
}
|
|
882
|
+
return null;
|
|
883
|
+
}
|
|
884
|
+
const packagePath = path8.join(nodeModulesRoot, resolve);
|
|
885
|
+
if (!fs7.existsSync(packagePath)) {
|
|
886
|
+
return null;
|
|
887
|
+
}
|
|
888
|
+
return fs7.realpathSync(packagePath);
|
|
889
|
+
} catch {
|
|
890
|
+
return null;
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
function resolvePluginExtensions(plugins, configDir, appType) {
|
|
894
|
+
const nodeModulesRoot = findNodeModulesRoot(configDir);
|
|
895
|
+
const extensions = [];
|
|
896
|
+
for (const plugin of plugins) {
|
|
897
|
+
const resolve = typeof plugin === "string" ? plugin : plugin?.resolve;
|
|
898
|
+
if (!resolve || typeof resolve !== "string") continue;
|
|
899
|
+
const pluginRoot = resolvePluginRoot(resolve, configDir, nodeModulesRoot);
|
|
900
|
+
if (!pluginRoot) continue;
|
|
901
|
+
const extFile = path8.join(
|
|
902
|
+
pluginRoot,
|
|
903
|
+
".medusa/server/src",
|
|
904
|
+
appType,
|
|
905
|
+
"index.mjs"
|
|
906
|
+
);
|
|
907
|
+
if (fs7.existsSync(extFile)) {
|
|
908
|
+
extensions.push(extFile);
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
return extensions;
|
|
912
|
+
}
|
|
913
|
+
function trimTrailingSlashes(value) {
|
|
914
|
+
let end = value.length;
|
|
915
|
+
while (end > 0 && value.charCodeAt(end - 1) === 47) {
|
|
916
|
+
end -= 1;
|
|
917
|
+
}
|
|
918
|
+
return end === value.length ? value : value.slice(0, end);
|
|
919
|
+
}
|
|
920
|
+
async function loadMedusaConfig(medusaConfigPath, root, options) {
|
|
921
|
+
const configDir = path8.dirname(medusaConfigPath);
|
|
922
|
+
try {
|
|
923
|
+
const previousCwd = process.cwd();
|
|
924
|
+
let mod;
|
|
925
|
+
process.chdir(configDir);
|
|
926
|
+
try {
|
|
927
|
+
mod = await getFileExports(medusaConfigPath);
|
|
928
|
+
} finally {
|
|
929
|
+
process.chdir(previousCwd);
|
|
930
|
+
}
|
|
931
|
+
const medusaConfig = mod.default ?? mod;
|
|
932
|
+
const modules = medusaConfig?.modules ?? {};
|
|
933
|
+
let base;
|
|
934
|
+
let appType = "admin";
|
|
935
|
+
let vendorAppUrl;
|
|
936
|
+
const vendorModule = modules.vendor_ui;
|
|
937
|
+
const vendorPath = vendorModule?.options?.path ?? "/seller";
|
|
938
|
+
if (options.vendorUrl) {
|
|
939
|
+
vendorAppUrl = trimTrailingSlashes(options.vendorUrl);
|
|
940
|
+
} else if (options.isDevelopment) {
|
|
941
|
+
const vendorHost = vendorModule?.options?.viteDevServerHost ?? "localhost";
|
|
942
|
+
const vendorPort = vendorModule?.options?.viteDevServerPort ?? 7001;
|
|
943
|
+
vendorAppUrl = `http://${vendorHost}:${vendorPort}${vendorPath}`;
|
|
944
|
+
} else {
|
|
945
|
+
vendorAppUrl = vendorPath;
|
|
946
|
+
}
|
|
947
|
+
for (const key of UI_MODULE_KEYS) {
|
|
948
|
+
const value = modules[key];
|
|
949
|
+
if (!value || typeof value !== "object" || !value.options?.appDir)
|
|
950
|
+
continue;
|
|
951
|
+
const appDir = path8.resolve(configDir, value.options.appDir);
|
|
952
|
+
if (appDir === root) {
|
|
953
|
+
base = value.options.path;
|
|
954
|
+
appType = key === "vendor_ui" ? "vendor" : "admin";
|
|
955
|
+
break;
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
const plugins = medusaConfig?.plugins?.filter(
|
|
959
|
+
(plugin) => plugin.resolve !== "@medusajs/draft-order"
|
|
960
|
+
) ?? [];
|
|
961
|
+
const pluginExtensions = resolvePluginExtensions(plugins, configDir, appType);
|
|
962
|
+
return { base, pluginExtensions, vendorAppUrl };
|
|
963
|
+
} catch (error) {
|
|
964
|
+
console.warn(
|
|
965
|
+
`[@mercurjs/dashboard-sdk] Could not load the Medusa config from "${medusaConfigPath}": ${error instanceof Error ? error.message : String(error)}. Building with base "/" and no plugin extensions \u2014 if this panel is served under a sub-path (e.g. /dashboard), its assets will not resolve.`
|
|
966
|
+
);
|
|
967
|
+
return { pluginExtensions: [] };
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
function mercurDashboardPlugin(pluginConfig) {
|
|
971
|
+
let root;
|
|
972
|
+
let config;
|
|
973
|
+
return {
|
|
974
|
+
name: "@mercurjs/dashboard-sdk",
|
|
975
|
+
async config(viteConfig) {
|
|
976
|
+
root = viteConfig.root || process.cwd();
|
|
977
|
+
const isDevelopment = (viteConfig.mode || process.env.NODE_ENV || "development") !== "production";
|
|
978
|
+
const medusaConfigPath = path8.resolve(
|
|
979
|
+
root,
|
|
980
|
+
pluginConfig.medusaConfigPath
|
|
981
|
+
);
|
|
982
|
+
const { base, pluginExtensions, vendorAppUrl } = await loadMedusaConfig(
|
|
983
|
+
medusaConfigPath,
|
|
984
|
+
root,
|
|
985
|
+
{
|
|
986
|
+
isDevelopment,
|
|
987
|
+
vendorUrl: pluginConfig.vendorUrl
|
|
988
|
+
}
|
|
989
|
+
);
|
|
990
|
+
const srcDir = path8.join(root, "src");
|
|
991
|
+
const backendUrl = pluginConfig.backendUrl ?? "http://localhost:9000";
|
|
992
|
+
const imageLimit = pluginConfig.imageLimit ?? 2 * 1024 * 1024;
|
|
993
|
+
config = {
|
|
994
|
+
...pluginConfig,
|
|
995
|
+
backendUrl,
|
|
996
|
+
base,
|
|
997
|
+
root,
|
|
998
|
+
srcDir,
|
|
999
|
+
pluginExtensions,
|
|
1000
|
+
imageLimit
|
|
1001
|
+
};
|
|
1002
|
+
return {
|
|
1003
|
+
base: config.base,
|
|
1004
|
+
define: {
|
|
1005
|
+
__BACKEND_URL__: JSON.stringify(config.backendUrl),
|
|
1006
|
+
__BASE__: JSON.stringify(config.base || "/"),
|
|
1007
|
+
__VENDOR_URL__: JSON.stringify(vendorAppUrl || "")
|
|
1008
|
+
},
|
|
1009
|
+
resolve: {
|
|
1010
|
+
dedupe: ["i18next", "react-i18next", "react", "react-dom"]
|
|
1011
|
+
},
|
|
1012
|
+
optimizeDeps: {
|
|
1013
|
+
exclude: [
|
|
1014
|
+
"virtual:mercur/config",
|
|
1015
|
+
"virtual:mercur/routes",
|
|
1016
|
+
"virtual:mercur/menu-items",
|
|
1017
|
+
"virtual:mercur/i18n",
|
|
1018
|
+
"virtual:mercur/widgets",
|
|
1019
|
+
"virtual:mercur/navigation",
|
|
1020
|
+
"virtual:mercur/custom-fields",
|
|
1021
|
+
...MEDUSA_VIRTUAL_MODULES
|
|
1022
|
+
],
|
|
1023
|
+
include: [
|
|
1024
|
+
"react",
|
|
1025
|
+
"react/jsx-runtime",
|
|
1026
|
+
"react-dom/client",
|
|
1027
|
+
"react-router-dom",
|
|
1028
|
+
"react-i18next",
|
|
1029
|
+
"i18next",
|
|
1030
|
+
"@medusajs/ui",
|
|
1031
|
+
"@medusajs/dashboard",
|
|
1032
|
+
"@mercurjs/client",
|
|
1033
|
+
"@tanstack/react-query"
|
|
1034
|
+
]
|
|
1035
|
+
}
|
|
1036
|
+
};
|
|
1037
|
+
},
|
|
1038
|
+
configResolved(resolvedConfig) {
|
|
1039
|
+
root = resolvedConfig.root;
|
|
1040
|
+
},
|
|
1041
|
+
resolveId(id) {
|
|
1042
|
+
if (isVirtualModule(id)) {
|
|
1043
|
+
return resolveVirtualModule(id);
|
|
1044
|
+
}
|
|
1045
|
+
if (isMedusaVirtualModule(id)) {
|
|
1046
|
+
return resolveMedusaVirtualModule(id);
|
|
1047
|
+
}
|
|
1048
|
+
return null;
|
|
1049
|
+
},
|
|
1050
|
+
load(id) {
|
|
1051
|
+
if (isResolvedMedusaVirtualModule(id)) {
|
|
1052
|
+
return "export default {}";
|
|
1053
|
+
}
|
|
1054
|
+
return loadVirtualModule({ id, mercurConfig: config });
|
|
1055
|
+
},
|
|
1056
|
+
configureServer(server) {
|
|
1057
|
+
const invalidate = (moduleId, reload) => {
|
|
1058
|
+
const mod = server.moduleGraph.getModuleById(moduleId);
|
|
1059
|
+
if (mod) {
|
|
1060
|
+
server.moduleGraph.invalidateModule(mod);
|
|
1061
|
+
if (reload) server.ws.send({ type: "full-reload" });
|
|
1062
|
+
}
|
|
1063
|
+
};
|
|
1064
|
+
const handleChange = (file) => {
|
|
1065
|
+
if (isRouteFile(file)) invalidate(RESOLVED_ROUTES_MODULE, true);
|
|
1066
|
+
if (isWidgetFile(file)) invalidate(RESOLVED_WIDGETS_MODULE, true);
|
|
1067
|
+
if (isNavigationFile(file)) invalidate(RESOLVED_NAVIGATION_MODULE, true);
|
|
1068
|
+
if (isCustomFieldFile(file)) invalidate(RESOLVED_CUSTOM_FIELDS_MODULE, true);
|
|
1069
|
+
};
|
|
1070
|
+
server.watcher.on("add", handleChange);
|
|
1071
|
+
server.watcher.on("unlink", handleChange);
|
|
1072
|
+
},
|
|
1073
|
+
handleHotUpdate({ file, server }) {
|
|
1074
|
+
const invalidate = (moduleId) => {
|
|
1075
|
+
const mod = server.moduleGraph.getModuleById(moduleId);
|
|
1076
|
+
if (mod) server.moduleGraph.invalidateModule(mod);
|
|
1077
|
+
};
|
|
1078
|
+
if (isRouteFile(file)) invalidate(RESOLVED_ROUTES_MODULE);
|
|
1079
|
+
if (isWidgetFile(file)) invalidate(RESOLVED_WIDGETS_MODULE);
|
|
1080
|
+
if (isNavigationFile(file)) invalidate(RESOLVED_NAVIGATION_MODULE);
|
|
1081
|
+
if (isCustomFieldFile(file)) invalidate(RESOLVED_CUSTOM_FIELDS_MODULE);
|
|
1082
|
+
}
|
|
1083
|
+
};
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
// src/generate-plugin-entry.ts
|
|
1087
|
+
import path9 from "path";
|
|
1088
|
+
import fs8 from "fs";
|
|
1089
|
+
function findI18nIndex2(srcDir) {
|
|
1090
|
+
const i18nDir = path9.join(srcDir, "i18n");
|
|
1091
|
+
if (!fs8.existsSync(i18nDir)) return null;
|
|
1092
|
+
for (const ext of VALID_FILE_EXTENSIONS) {
|
|
1093
|
+
const filePath = path9.join(i18nDir, `index${ext}`);
|
|
1094
|
+
if (fs8.existsSync(filePath)) return filePath;
|
|
1095
|
+
}
|
|
1096
|
+
return null;
|
|
1097
|
+
}
|
|
1098
|
+
function generatePluginEntryModule(srcDir) {
|
|
1099
|
+
const routesDir = path9.join(srcDir, "routes");
|
|
1100
|
+
const files = crawlRoutes(routesDir);
|
|
1101
|
+
let index = 0;
|
|
1102
|
+
const routeResults = [];
|
|
1103
|
+
const menuItemResults = [];
|
|
1104
|
+
for (const file of files) {
|
|
1105
|
+
const route = parseFile(file, routesDir, index);
|
|
1106
|
+
if (route) {
|
|
1107
|
+
routeResults.push(route);
|
|
1108
|
+
}
|
|
1109
|
+
const menuItem = parseMenuItemFile(file, routesDir, index);
|
|
1110
|
+
if (menuItem) {
|
|
1111
|
+
menuItemResults.push(menuItem);
|
|
1112
|
+
}
|
|
1113
|
+
index++;
|
|
1114
|
+
}
|
|
1115
|
+
const routeTree = buildRouteTree(routeResults.filter(Boolean));
|
|
1116
|
+
const routeImports = routeTree.flatMap((r) => r.imports);
|
|
1117
|
+
const routes = routeTree.map((r) => formatRoute(r.route));
|
|
1118
|
+
const menuItemImports = menuItemResults.filter(Boolean).map((r) => r.import);
|
|
1119
|
+
const menuItems = menuItemResults.filter(Boolean).map((r) => formatMenuItem(r.menuItem));
|
|
1120
|
+
const { imports: widgetImports, entries: widgetEntries } = collectWidgets(
|
|
1121
|
+
srcDir,
|
|
1122
|
+
index
|
|
1123
|
+
);
|
|
1124
|
+
const cfFiles = crawlModuleFiles(path9.join(srcDir, "custom-fields"));
|
|
1125
|
+
const cfImports = cfFiles.map(
|
|
1126
|
+
(file, i) => `import __cf${i} from "${normalizePath(file)}"`
|
|
1127
|
+
);
|
|
1128
|
+
const cfRefs = cfFiles.map((_, i) => ` __cf${i}`);
|
|
1129
|
+
const i18nFile = findI18nIndex2(srcDir);
|
|
1130
|
+
const i18nImport = i18nFile ? `import i18nResources from "${normalizePath(i18nFile)}"` : "";
|
|
1131
|
+
const i18nValue = i18nFile ? "i18nResources" : "{}";
|
|
1132
|
+
const allImports = [
|
|
1133
|
+
...routeImports,
|
|
1134
|
+
...menuItemImports,
|
|
1135
|
+
...widgetImports,
|
|
1136
|
+
...cfImports
|
|
1137
|
+
];
|
|
1138
|
+
if (i18nImport) allImports.push(i18nImport);
|
|
1139
|
+
return `// Auto-generated plugin extensions entry
|
|
1140
|
+
${allImports.join("\n")}
|
|
1141
|
+
|
|
1142
|
+
const routeModule = {
|
|
1143
|
+
routes: [
|
|
1144
|
+
${routes.join(",\n")}
|
|
1145
|
+
]
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
const menuItemModule = {
|
|
1149
|
+
menuItems: [
|
|
1150
|
+
${menuItems.join(",\n")}
|
|
1151
|
+
]
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
const widgetModule = {
|
|
1155
|
+
widgets: [
|
|
1156
|
+
${widgetEntries.join(",\n")}
|
|
1157
|
+
]
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
const customFieldsModule = {
|
|
1161
|
+
configs: [
|
|
1162
|
+
${cfRefs.join(",\n")}
|
|
1163
|
+
]
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
const plugin = {
|
|
1167
|
+
routeModule,
|
|
1168
|
+
menuItemModule,
|
|
1169
|
+
widgetModule,
|
|
1170
|
+
customFieldsModule,
|
|
1171
|
+
i18nModule: ${i18nValue},
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
export default plugin
|
|
1175
|
+
`;
|
|
1176
|
+
}
|
|
1177
|
+
export {
|
|
1178
|
+
generatePluginEntryModule,
|
|
1179
|
+
mercurDashboardPlugin
|
|
1180
|
+
};
|