@mercurjs/dashboard-sdk 2.0.0-canary.1 → 2.0.0-canary.100
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/index.cjs +442 -141
- package/dist/index.d.cts +16 -6
- package/package.json +4 -1
package/dist/index.cjs
CHANGED
|
@@ -30,24 +30,49 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
generatePluginEntryModule: () => generatePluginEntryModule,
|
|
34
|
+
mercurDashboardPlugin: () => mercurDashboardPlugin
|
|
35
35
|
});
|
|
36
36
|
module.exports = __toCommonJS(index_exports);
|
|
37
37
|
|
|
38
38
|
// src/plugin.ts
|
|
39
39
|
var import_path5 = __toESM(require("path"), 1);
|
|
40
|
+
var import_fs4 = __toESM(require("fs"), 1);
|
|
41
|
+
|
|
42
|
+
// src/babel.ts
|
|
43
|
+
var import_parser = require("@babel/parser");
|
|
44
|
+
var import_traverse = __toESM(require("@babel/traverse"), 1);
|
|
45
|
+
var import_types = require("@babel/types");
|
|
46
|
+
var traverse;
|
|
47
|
+
if (typeof import_traverse.default === "function") {
|
|
48
|
+
traverse = import_traverse.default;
|
|
49
|
+
} else {
|
|
50
|
+
traverse = import_traverse.default.default;
|
|
51
|
+
}
|
|
40
52
|
|
|
41
53
|
// src/utils.ts
|
|
54
|
+
function normalizePath(filePath) {
|
|
55
|
+
return filePath.replace(/\\/g, "/");
|
|
56
|
+
}
|
|
57
|
+
function getParserOptions(file) {
|
|
58
|
+
const options = {
|
|
59
|
+
sourceType: "module",
|
|
60
|
+
plugins: ["jsx"]
|
|
61
|
+
};
|
|
62
|
+
if (file.endsWith(".ts") || file.endsWith(".tsx")) {
|
|
63
|
+
options.plugins.push("typescript");
|
|
64
|
+
}
|
|
65
|
+
return options;
|
|
66
|
+
}
|
|
42
67
|
function resolveExports(moduleExports) {
|
|
43
68
|
if ("default" in moduleExports && moduleExports.default && "default" in moduleExports.default) {
|
|
44
69
|
return resolveExports(moduleExports.default);
|
|
45
70
|
}
|
|
46
71
|
return moduleExports;
|
|
47
72
|
}
|
|
48
|
-
async function getFileExports(
|
|
73
|
+
async function getFileExports(path7) {
|
|
49
74
|
const { unregister } = await safeRegister();
|
|
50
|
-
const module2 = require(
|
|
75
|
+
const module2 = require(path7);
|
|
51
76
|
unregister();
|
|
52
77
|
return resolveExports(module2);
|
|
53
78
|
}
|
|
@@ -67,13 +92,31 @@ var safeRegister = async () => {
|
|
|
67
92
|
}
|
|
68
93
|
return res;
|
|
69
94
|
};
|
|
70
|
-
function
|
|
71
|
-
|
|
95
|
+
function hasDefaultExport(ast) {
|
|
96
|
+
let found = false;
|
|
97
|
+
traverse(ast, {
|
|
98
|
+
ExportDefaultDeclaration() {
|
|
99
|
+
found = true;
|
|
100
|
+
},
|
|
101
|
+
AssignmentExpression(path7) {
|
|
102
|
+
if (path7.node.left.type === "MemberExpression" && path7.node.left.object.type === "Identifier" && path7.node.left.object.name === "exports" && path7.node.left.property.type === "Identifier" && path7.node.left.property.name === "default") {
|
|
103
|
+
found = true;
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
ExportNamedDeclaration(path7) {
|
|
107
|
+
const specifiers = path7.node.specifiers;
|
|
108
|
+
if (specifiers?.some(
|
|
109
|
+
(s) => s.type === "ExportSpecifier" && s.exported.type === "Identifier" && s.exported.name === "default"
|
|
110
|
+
)) {
|
|
111
|
+
found = true;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
return found;
|
|
72
116
|
}
|
|
73
117
|
|
|
74
118
|
// src/constants.ts
|
|
75
119
|
var VALID_FILE_EXTENSIONS = [".tsx", ".ts", ".jsx", ".js"];
|
|
76
|
-
var CONFIG_NAME = "mercur.config.ts";
|
|
77
120
|
var CONFIG_VIRTUAL_MODULE = "virtual:mercur/config";
|
|
78
121
|
var ROUTES_VIRTUAL_MODULE = "virtual:mercur/routes";
|
|
79
122
|
var COMPONENTS_VIRTUAL_MODULE = "virtual:mercur/components";
|
|
@@ -98,17 +141,17 @@ var import_path4 = __toESM(require("path"), 1);
|
|
|
98
141
|
// src/routes.ts
|
|
99
142
|
var import_fs = __toESM(require("fs"), 1);
|
|
100
143
|
var import_path = __toESM(require("path"), 1);
|
|
101
|
-
function getRoute(file,
|
|
144
|
+
function getRoute(file, routesDir) {
|
|
102
145
|
const importPath = normalizePath(file);
|
|
103
|
-
const
|
|
104
|
-
return importPath.replace(
|
|
146
|
+
const normalizedRoutesDir = normalizePath(routesDir);
|
|
147
|
+
return importPath.replace(normalizedRoutesDir, "").replace(/\[\[\*\]\]/g, "*?").replace(/\[\*\]/g, "*").replace(/\(([^\[\]\)]+)\)/g, "$1?").replace(/\[\[([^\]]+)\]\]/g, ":$1?").replace(/\[([^\]]+)\]/g, ":$1").replace(
|
|
105
148
|
new RegExp(
|
|
106
149
|
`/page\\.(${VALID_FILE_EXTENSIONS.map((ext) => ext.slice(1)).join("|")})$`
|
|
107
150
|
),
|
|
108
151
|
""
|
|
109
152
|
) || "/";
|
|
110
153
|
}
|
|
111
|
-
function
|
|
154
|
+
function crawlRoutes(dir, pattern = "page") {
|
|
112
155
|
const files = [];
|
|
113
156
|
if (!import_fs.default.existsSync(dir)) {
|
|
114
157
|
return files;
|
|
@@ -117,7 +160,7 @@ function crawlPages(dir, pattern = "page") {
|
|
|
117
160
|
for (const entry of entries) {
|
|
118
161
|
const fullPath = import_path.default.join(dir, entry.name);
|
|
119
162
|
if (entry.isDirectory()) {
|
|
120
|
-
files.push(...
|
|
163
|
+
files.push(...crawlRoutes(fullPath, pattern));
|
|
121
164
|
} else if (entry.isFile()) {
|
|
122
165
|
const ext = import_path.default.extname(entry.name);
|
|
123
166
|
const baseName = import_path.default.basename(entry.name, ext);
|
|
@@ -128,23 +171,51 @@ function crawlPages(dir, pattern = "page") {
|
|
|
128
171
|
}
|
|
129
172
|
return files;
|
|
130
173
|
}
|
|
131
|
-
function
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
174
|
+
function hasConfigPublic(ast) {
|
|
175
|
+
let found = false;
|
|
176
|
+
traverse(ast, {
|
|
177
|
+
ExportNamedDeclaration(path7) {
|
|
178
|
+
const declaration = path7.node.declaration;
|
|
179
|
+
if (!(0, import_types.isVariableDeclaration)(declaration)) return;
|
|
180
|
+
for (const decl of declaration.declarations) {
|
|
181
|
+
if ((0, import_types.isVariableDeclarator)(decl) && (0, import_types.isIdentifier)(decl.id, { name: "config" }) && decl.init?.type === "ObjectExpression") {
|
|
182
|
+
const publicProp = decl.init.properties.find(
|
|
183
|
+
(prop) => (0, import_types.isObjectProperty)(prop) && (0, import_types.isIdentifier)(prop.key, { name: "public" }) && (0, import_types.isBooleanLiteral)(prop.value, { value: true })
|
|
184
|
+
);
|
|
185
|
+
if (publicProp) {
|
|
186
|
+
found = true;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
return found;
|
|
193
|
+
}
|
|
194
|
+
function getNamedExports(ast) {
|
|
195
|
+
let hasHandle = false;
|
|
196
|
+
let hasLoader = false;
|
|
197
|
+
traverse(ast, {
|
|
198
|
+
ExportNamedDeclaration(path7) {
|
|
199
|
+
const declaration = path7.node.declaration;
|
|
200
|
+
if (declaration?.type === "VariableDeclaration") {
|
|
201
|
+
declaration.declarations.forEach((decl) => {
|
|
202
|
+
if (decl.id.type === "Identifier" && decl.id.name === "handle") {
|
|
203
|
+
hasHandle = true;
|
|
204
|
+
}
|
|
205
|
+
if (decl.id.type === "Identifier" && decl.id.name === "loader") {
|
|
206
|
+
hasLoader = true;
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
if (declaration?.type === "FunctionDeclaration" && declaration.id?.name === "loader") {
|
|
211
|
+
hasLoader = true;
|
|
212
|
+
}
|
|
213
|
+
if (declaration?.type === "FunctionDeclaration" && declaration.id?.name === "handle") {
|
|
214
|
+
hasHandle = true;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
return { hasHandle, hasLoader };
|
|
148
219
|
}
|
|
149
220
|
function generateRouteComponentName(index) {
|
|
150
221
|
return `RouteComponent${index}`;
|
|
@@ -170,12 +241,13 @@ function generateImports(file, index, hasHandle, hasLoader) {
|
|
|
170
241
|
}
|
|
171
242
|
return imports;
|
|
172
243
|
}
|
|
173
|
-
function generateRouteObject(routePath, index, hasHandle, hasLoader) {
|
|
244
|
+
function generateRouteObject(routePath, index, hasHandle, hasLoader, isPublic) {
|
|
174
245
|
return {
|
|
175
246
|
Component: generateRouteComponentName(index),
|
|
176
247
|
path: routePath,
|
|
177
248
|
handle: hasHandle ? generateHandleName(index) : void 0,
|
|
178
|
-
loader: hasLoader ? generateLoaderName(index) : void 0
|
|
249
|
+
loader: hasLoader ? generateLoaderName(index) : void 0,
|
|
250
|
+
isPublic
|
|
179
251
|
};
|
|
180
252
|
}
|
|
181
253
|
function formatRoute(route, indent = " ") {
|
|
@@ -191,6 +263,10 @@ ${indent} handle: ${route.handle}`;
|
|
|
191
263
|
if (route.loader) {
|
|
192
264
|
result += `,
|
|
193
265
|
${indent} loader: ${route.loader}`;
|
|
266
|
+
}
|
|
267
|
+
if (route.isPublic) {
|
|
268
|
+
result += `,
|
|
269
|
+
${indent} isPublic: true`;
|
|
194
270
|
}
|
|
195
271
|
if (route.children?.length) {
|
|
196
272
|
result += `,
|
|
@@ -204,18 +280,25 @@ ${indent} ]`;
|
|
|
204
280
|
${indent}}`;
|
|
205
281
|
return result;
|
|
206
282
|
}
|
|
207
|
-
function parseFile(file,
|
|
208
|
-
|
|
283
|
+
function parseFile(file, routesDir, index) {
|
|
284
|
+
try {
|
|
285
|
+
const code = import_fs.default.readFileSync(file, "utf-8");
|
|
286
|
+
const ast = (0, import_parser.parse)(code, getParserOptions(file));
|
|
287
|
+
if (!hasDefaultExport(ast)) {
|
|
288
|
+
return null;
|
|
289
|
+
}
|
|
290
|
+
const { hasHandle, hasLoader } = getNamedExports(ast);
|
|
291
|
+
const isPublic = hasConfigPublic(ast);
|
|
292
|
+
const routePath = getRoute(file, routesDir);
|
|
293
|
+
const imports = generateImports(file, index, hasHandle, hasLoader);
|
|
294
|
+
const route = generateRouteObject(routePath, index, hasHandle, hasLoader, isPublic);
|
|
295
|
+
return {
|
|
296
|
+
imports,
|
|
297
|
+
route
|
|
298
|
+
};
|
|
299
|
+
} catch {
|
|
209
300
|
return null;
|
|
210
301
|
}
|
|
211
|
-
const { hasHandle, hasLoader } = getNamedExports(file);
|
|
212
|
-
const routePath = getRoute(file, pagesDir);
|
|
213
|
-
const imports = generateImports(file, index, hasHandle, hasLoader);
|
|
214
|
-
const route = generateRouteObject(routePath, index, hasHandle, hasLoader);
|
|
215
|
-
return {
|
|
216
|
-
imports,
|
|
217
|
-
route
|
|
218
|
-
};
|
|
219
302
|
}
|
|
220
303
|
function buildRouteTree(results) {
|
|
221
304
|
const routeMap = /* @__PURE__ */ new Map();
|
|
@@ -229,7 +312,7 @@ function buildRouteTree(results) {
|
|
|
229
312
|
const parentPath = routePath.split("/@")[0];
|
|
230
313
|
const parent = routeMap.get(parentPath);
|
|
231
314
|
if (parent) {
|
|
232
|
-
parent.route.children = parent.route.children
|
|
315
|
+
parent.route.children = parent.route.children ?? [];
|
|
233
316
|
parent.route.children.push({
|
|
234
317
|
...result.route,
|
|
235
318
|
path: result.route.path.replace("/@", "/")
|
|
@@ -244,38 +327,44 @@ function buildRouteTree(results) {
|
|
|
244
327
|
}
|
|
245
328
|
return Array.from(routeMap.values());
|
|
246
329
|
}
|
|
247
|
-
function generateRoutes({ srcDir }) {
|
|
248
|
-
const
|
|
249
|
-
const files = crawlPages(pagesDir);
|
|
250
|
-
if (files.length === 0) {
|
|
251
|
-
return `export const customRoutes = []`;
|
|
252
|
-
}
|
|
330
|
+
function generateRoutes({ srcDir, pluginExtensions }) {
|
|
331
|
+
const routesDir = import_path.default.join(srcDir, "routes");
|
|
253
332
|
let index = 0;
|
|
254
333
|
const results = [];
|
|
255
|
-
for (const file of
|
|
256
|
-
const result = parseFile(file,
|
|
334
|
+
for (const file of crawlRoutes(routesDir)) {
|
|
335
|
+
const result = parseFile(file, routesDir, index);
|
|
257
336
|
if (result) {
|
|
258
337
|
results.push(result);
|
|
259
338
|
index++;
|
|
260
339
|
}
|
|
261
340
|
}
|
|
262
|
-
|
|
341
|
+
const pluginDeclarations = pluginExtensions.map(
|
|
342
|
+
(ext, i) => `const __plugin${i} = (await import("${normalizePath(ext)}")).default`
|
|
343
|
+
);
|
|
344
|
+
const pluginSpreads = pluginExtensions.map(
|
|
345
|
+
(_, i) => ` ...(__plugin${i}.routeModule?.routes ?? [])`
|
|
346
|
+
);
|
|
347
|
+
const routeTree = buildRouteTree(results);
|
|
348
|
+
const appImports = routeTree.flatMap((r) => r.imports);
|
|
349
|
+
const appRoutes = routeTree.map((r) => formatRoute(r.route));
|
|
350
|
+
const allImports = [...appImports];
|
|
351
|
+
const allRoutes = [...appRoutes, ...pluginSpreads];
|
|
352
|
+
if (allImports.length === 0 && pluginDeclarations.length === 0 && allRoutes.length === 0) {
|
|
263
353
|
return `export const customRoutes = []`;
|
|
264
354
|
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
return `${imports.join("\n")}
|
|
355
|
+
return `${allImports.join("\n")}
|
|
356
|
+
|
|
357
|
+
${pluginDeclarations.join("\n")}
|
|
269
358
|
|
|
270
359
|
export const customRoutes = [
|
|
271
|
-
${
|
|
360
|
+
${allRoutes.join(",\n")}
|
|
272
361
|
]`;
|
|
273
362
|
}
|
|
274
363
|
|
|
275
364
|
// src/menu-items.ts
|
|
276
365
|
var import_fs2 = __toESM(require("fs"), 1);
|
|
277
366
|
var import_path2 = __toESM(require("path"), 1);
|
|
278
|
-
function
|
|
367
|
+
function crawlRoutes2(dir, pattern = "page") {
|
|
279
368
|
const files = [];
|
|
280
369
|
if (!import_fs2.default.existsSync(dir)) {
|
|
281
370
|
return files;
|
|
@@ -284,7 +373,7 @@ function crawlPages2(dir, pattern = "page") {
|
|
|
284
373
|
for (const entry of entries) {
|
|
285
374
|
const fullPath = import_path2.default.join(dir, entry.name);
|
|
286
375
|
if (entry.isDirectory()) {
|
|
287
|
-
files.push(...
|
|
376
|
+
files.push(...crawlRoutes2(fullPath, pattern));
|
|
288
377
|
} else if (entry.isFile()) {
|
|
289
378
|
const ext = import_path2.default.extname(entry.name);
|
|
290
379
|
const baseName = import_path2.default.basename(entry.name, ext);
|
|
@@ -295,39 +384,100 @@ function crawlPages2(dir, pattern = "page") {
|
|
|
295
384
|
}
|
|
296
385
|
return files;
|
|
297
386
|
}
|
|
298
|
-
function getRoute2(file,
|
|
387
|
+
function getRoute2(file, routesDir) {
|
|
299
388
|
const importPath = normalizePath(file);
|
|
300
|
-
const
|
|
301
|
-
return importPath.replace(
|
|
389
|
+
const normalizedRoutesDir = normalizePath(routesDir);
|
|
390
|
+
return importPath.replace(normalizedRoutesDir, "").replace(/\[\[\*\]\]/g, "*?").replace(/\[\*\]/g, "*").replace(/\(([^\[\]\)]+)\)/g, "$1?").replace(/\[\[([^\]]+)\]\]/g, ":$1?").replace(/\[([^\]]+)\]/g, ":$1").replace(
|
|
302
391
|
new RegExp(
|
|
303
392
|
`/page\\.(${VALID_FILE_EXTENSIONS.map((ext) => ext.slice(1)).join("|")})$`
|
|
304
393
|
),
|
|
305
394
|
""
|
|
306
395
|
) || "/";
|
|
307
396
|
}
|
|
308
|
-
function
|
|
309
|
-
|
|
310
|
-
const
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
397
|
+
function getConfigObjectProperties(path7) {
|
|
398
|
+
if ((0, import_types.isVariableDeclarator)(path7.node)) {
|
|
399
|
+
const decl = (0, import_types.isIdentifier)(path7.node.id, { name: "config" }) ? path7.node : null;
|
|
400
|
+
if (!decl) return null;
|
|
401
|
+
if ((0, import_types.isCallExpression)(decl.init) && decl.init.arguments.length > 0 && (0, import_types.isObjectExpression)(decl.init.arguments[0])) {
|
|
402
|
+
return decl.init.arguments[0].properties;
|
|
403
|
+
}
|
|
404
|
+
if ((0, import_types.isObjectExpression)(decl.init)) {
|
|
405
|
+
return decl.init.properties;
|
|
406
|
+
}
|
|
407
|
+
return null;
|
|
408
|
+
}
|
|
409
|
+
const declaration = path7.node.declaration;
|
|
410
|
+
if ((0, import_types.isVariableDeclaration)(declaration)) {
|
|
411
|
+
const configDecl = declaration.declarations.find(
|
|
412
|
+
(d) => (0, import_types.isVariableDeclarator)(d) && (0, import_types.isIdentifier)(d.id, { name: "config" })
|
|
413
|
+
);
|
|
414
|
+
if (configDecl && (0, import_types.isCallExpression)(configDecl.init) && configDecl.init.arguments.length > 0 && (0, import_types.isObjectExpression)(configDecl.init.arguments[0])) {
|
|
415
|
+
return configDecl.init.arguments[0].properties;
|
|
416
|
+
}
|
|
417
|
+
const directDecl = declaration.declarations.find(
|
|
418
|
+
(d) => (0, import_types.isVariableDeclarator)(d) && (0, import_types.isIdentifier)(d.id, { name: "config" })
|
|
419
|
+
);
|
|
420
|
+
if (directDecl && (0, import_types.isObjectExpression)(directDecl.init)) {
|
|
421
|
+
return directDecl.init.properties;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
return null;
|
|
425
|
+
}
|
|
426
|
+
function processConfigProperties(properties) {
|
|
427
|
+
const hasProperty = (name) => properties.some(
|
|
428
|
+
(prop) => (0, import_types.isObjectProperty)(prop) && (0, import_types.isIdentifier)(prop.key, { name })
|
|
429
|
+
);
|
|
430
|
+
const hasLabel = hasProperty("label");
|
|
431
|
+
if (!hasLabel) {
|
|
432
|
+
return null;
|
|
314
433
|
}
|
|
434
|
+
const hasIcon = hasProperty("icon");
|
|
435
|
+
const nested = properties.find(
|
|
436
|
+
(prop) => (0, import_types.isObjectProperty)(prop) && (0, import_types.isIdentifier)(prop.key, { name: "nested" })
|
|
437
|
+
);
|
|
438
|
+
let nestedValue;
|
|
439
|
+
if (nested && (0, import_types.isObjectProperty)(nested) && (0, import_types.isStringLiteral)(nested.value)) {
|
|
440
|
+
nestedValue = nested.value.value;
|
|
441
|
+
}
|
|
442
|
+
const translationNs = properties.find(
|
|
443
|
+
(prop) => (0, import_types.isObjectProperty)(prop) && (0, import_types.isIdentifier)(prop.key, { name: "translationNs" })
|
|
444
|
+
);
|
|
445
|
+
let translationNsValue;
|
|
446
|
+
if (translationNs && (0, import_types.isObjectProperty)(translationNs) && (0, import_types.isStringLiteral)(translationNs.value)) {
|
|
447
|
+
translationNsValue = translationNs.value.value;
|
|
448
|
+
}
|
|
449
|
+
const rank = properties.find(
|
|
450
|
+
(prop) => (0, import_types.isObjectProperty)(prop) && (0, import_types.isIdentifier)(prop.key, { name: "rank" })
|
|
451
|
+
);
|
|
452
|
+
let rankValue;
|
|
453
|
+
if (rank && (0, import_types.isObjectProperty)(rank) && (0, import_types.isNumericLiteral)(rank.value)) {
|
|
454
|
+
rankValue = rank.value.value;
|
|
455
|
+
}
|
|
456
|
+
return { label: hasLabel, icon: hasIcon, rank: rankValue, nested: nestedValue, translationNs: translationNsValue };
|
|
315
457
|
}
|
|
316
|
-
function
|
|
458
|
+
function getRouteConfig(file) {
|
|
317
459
|
try {
|
|
318
|
-
const
|
|
319
|
-
const
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
460
|
+
const code = import_fs2.default.readFileSync(file, "utf-8");
|
|
461
|
+
const ast = (0, import_parser.parse)(code, getParserOptions(file));
|
|
462
|
+
let config = null;
|
|
463
|
+
let configFound = false;
|
|
464
|
+
traverse(ast, {
|
|
465
|
+
VariableDeclarator(path7) {
|
|
466
|
+
if (configFound) return;
|
|
467
|
+
const properties = getConfigObjectProperties(path7);
|
|
468
|
+
if (!properties) return;
|
|
469
|
+
config = processConfigProperties(properties);
|
|
470
|
+
if (config) configFound = true;
|
|
471
|
+
},
|
|
472
|
+
ExportNamedDeclaration(path7) {
|
|
473
|
+
if (configFound) return;
|
|
474
|
+
const properties = getConfigObjectProperties(path7);
|
|
475
|
+
if (!properties) return;
|
|
476
|
+
config = processConfigProperties(properties);
|
|
477
|
+
if (config) configFound = true;
|
|
478
|
+
}
|
|
479
|
+
});
|
|
480
|
+
return config;
|
|
331
481
|
} catch {
|
|
332
482
|
return null;
|
|
333
483
|
}
|
|
@@ -339,12 +489,12 @@ function generateImport(file, index) {
|
|
|
339
489
|
const importPath = normalizePath(file);
|
|
340
490
|
return `import { config as ${generateRouteConfigName(index)} } from "${importPath}"`;
|
|
341
491
|
}
|
|
342
|
-
function generateMenuItem(config, file,
|
|
492
|
+
function generateMenuItem(config, file, routesDir, index) {
|
|
343
493
|
const configName = generateRouteConfigName(index);
|
|
344
494
|
return {
|
|
345
495
|
label: `${configName}.label`,
|
|
346
496
|
icon: config.icon ? `${configName}.icon` : void 0,
|
|
347
|
-
path: getRoute2(file,
|
|
497
|
+
path: getRoute2(file, routesDir),
|
|
348
498
|
rank: config.rank,
|
|
349
499
|
nested: config.nested,
|
|
350
500
|
translationNs: config.translationNs ? `${configName}.translationNs` : void 0
|
|
@@ -363,44 +513,47 @@ function formatMenuItem(menuItem) {
|
|
|
363
513
|
${parts.join(",\n")}
|
|
364
514
|
}`;
|
|
365
515
|
}
|
|
366
|
-
function
|
|
367
|
-
|
|
368
|
-
return null;
|
|
369
|
-
}
|
|
370
|
-
const config = getConfigProperties(file);
|
|
516
|
+
function parseMenuItemFile(file, routesDir, index) {
|
|
517
|
+
const config = getRouteConfig(file);
|
|
371
518
|
if (!config) {
|
|
372
519
|
return null;
|
|
373
520
|
}
|
|
374
521
|
return {
|
|
375
522
|
import: generateImport(file, index),
|
|
376
|
-
menuItem: generateMenuItem(config, file,
|
|
523
|
+
menuItem: generateMenuItem(config, file, routesDir, index)
|
|
377
524
|
};
|
|
378
525
|
}
|
|
379
|
-
function generateMenuItems({ srcDir }) {
|
|
380
|
-
const
|
|
381
|
-
const files = crawlPages2(pagesDir);
|
|
382
|
-
if (files.length === 0) {
|
|
383
|
-
return `export default { menuItems: [] }`;
|
|
384
|
-
}
|
|
526
|
+
function generateMenuItems({ srcDir, pluginExtensions }) {
|
|
527
|
+
const routesDir = import_path2.default.join(srcDir, "routes");
|
|
385
528
|
let index = 0;
|
|
386
529
|
const results = [];
|
|
387
|
-
for (const file of
|
|
388
|
-
const result =
|
|
530
|
+
for (const file of crawlRoutes2(routesDir)) {
|
|
531
|
+
const result = parseMenuItemFile(file, routesDir, index);
|
|
389
532
|
if (result) {
|
|
390
533
|
results.push(result);
|
|
391
534
|
index++;
|
|
392
535
|
}
|
|
393
536
|
}
|
|
394
|
-
|
|
537
|
+
const pluginDeclarations = pluginExtensions.map(
|
|
538
|
+
(ext, i) => `const __plugin${i} = (await import("${normalizePath(ext)}")).default`
|
|
539
|
+
);
|
|
540
|
+
const pluginSpreads = pluginExtensions.map(
|
|
541
|
+
(_, i) => ` ...(__plugin${i}.menuItemModule?.menuItems ?? [])`
|
|
542
|
+
);
|
|
543
|
+
const appImports = results.map((r) => r.import);
|
|
544
|
+
const appMenuItems = results.map((r) => formatMenuItem(r.menuItem));
|
|
545
|
+
const allImports = [...appImports];
|
|
546
|
+
const allMenuItems = [...appMenuItems, ...pluginSpreads];
|
|
547
|
+
if (allImports.length === 0 && pluginDeclarations.length === 0 && allMenuItems.length === 0) {
|
|
395
548
|
return `export default { menuItems: [] }`;
|
|
396
549
|
}
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
550
|
+
return `${allImports.join("\n")}
|
|
551
|
+
|
|
552
|
+
${pluginDeclarations.join("\n")}
|
|
400
553
|
|
|
401
554
|
export default {
|
|
402
555
|
menuItems: [
|
|
403
|
-
${
|
|
556
|
+
${allMenuItems.join(",\n")}
|
|
404
557
|
]
|
|
405
558
|
}`;
|
|
406
559
|
}
|
|
@@ -492,44 +645,138 @@ function loadI18nModule(mercurConfig) {
|
|
|
492
645
|
}
|
|
493
646
|
|
|
494
647
|
// src/plugin.ts
|
|
495
|
-
function
|
|
496
|
-
const
|
|
497
|
-
return
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
648
|
+
function isRouteFile(file) {
|
|
649
|
+
const basename = import_path5.default.basename(file, import_path5.default.extname(file));
|
|
650
|
+
return basename === "page";
|
|
651
|
+
}
|
|
652
|
+
var UI_MODULE_KEYS = ["admin_ui", "vendor_ui"];
|
|
653
|
+
function findNodeModulesRoot(configDir) {
|
|
654
|
+
let dir = configDir;
|
|
655
|
+
while (dir !== import_path5.default.dirname(dir)) {
|
|
656
|
+
const candidate = import_path5.default.join(dir, "node_modules");
|
|
657
|
+
if (import_fs4.default.existsSync(candidate) && import_fs4.default.statSync(candidate).isDirectory()) {
|
|
658
|
+
return candidate;
|
|
659
|
+
}
|
|
660
|
+
dir = import_path5.default.dirname(dir);
|
|
661
|
+
}
|
|
662
|
+
return import_path5.default.join(configDir, "node_modules");
|
|
504
663
|
}
|
|
505
|
-
|
|
506
|
-
const configPath = import_path5.default.resolve(root, CONFIG_NAME);
|
|
664
|
+
function resolvePluginRoot(resolve, configDir, nodeModulesRoot) {
|
|
507
665
|
try {
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
666
|
+
if (resolve.startsWith(".")) {
|
|
667
|
+
const resolved = import_path5.default.resolve(configDir, resolve);
|
|
668
|
+
if (import_fs4.default.existsSync(resolved)) {
|
|
669
|
+
return import_fs4.default.realpathSync(resolved);
|
|
670
|
+
}
|
|
671
|
+
return null;
|
|
672
|
+
}
|
|
673
|
+
const packagePath = import_path5.default.join(nodeModulesRoot, resolve);
|
|
674
|
+
if (!import_fs4.default.existsSync(packagePath)) {
|
|
675
|
+
return null;
|
|
676
|
+
}
|
|
677
|
+
return import_fs4.default.realpathSync(packagePath);
|
|
678
|
+
} catch {
|
|
679
|
+
return null;
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
function resolvePluginExtensions(plugins, configDir, appType) {
|
|
683
|
+
const nodeModulesRoot = findNodeModulesRoot(configDir);
|
|
684
|
+
const extensions = [];
|
|
685
|
+
for (const plugin of plugins) {
|
|
686
|
+
const resolve = typeof plugin === "string" ? plugin : plugin?.resolve;
|
|
687
|
+
if (!resolve || typeof resolve !== "string") continue;
|
|
688
|
+
const pluginRoot = resolvePluginRoot(resolve, configDir, nodeModulesRoot);
|
|
689
|
+
if (!pluginRoot) continue;
|
|
690
|
+
const extFile = import_path5.default.join(
|
|
691
|
+
pluginRoot,
|
|
692
|
+
".medusa/server/src",
|
|
693
|
+
appType,
|
|
694
|
+
"index.mjs"
|
|
515
695
|
);
|
|
696
|
+
if (import_fs4.default.existsSync(extFile)) {
|
|
697
|
+
extensions.push(extFile);
|
|
698
|
+
}
|
|
516
699
|
}
|
|
700
|
+
return extensions;
|
|
517
701
|
}
|
|
518
|
-
function
|
|
519
|
-
|
|
520
|
-
|
|
702
|
+
async function loadMedusaConfig(medusaConfigPath, root) {
|
|
703
|
+
try {
|
|
704
|
+
const mod = await getFileExports(medusaConfigPath);
|
|
705
|
+
const medusaConfig = mod.default ?? mod;
|
|
706
|
+
const modules = medusaConfig?.modules ?? {};
|
|
707
|
+
const configDir = import_path5.default.dirname(medusaConfigPath);
|
|
708
|
+
let base;
|
|
709
|
+
let appType = "admin";
|
|
710
|
+
for (const key of UI_MODULE_KEYS) {
|
|
711
|
+
const value = modules[key];
|
|
712
|
+
if (!value || typeof value !== "object" || !value.options?.appDir)
|
|
713
|
+
continue;
|
|
714
|
+
const appDir = import_path5.default.resolve(configDir, value.options.appDir);
|
|
715
|
+
if (appDir === root) {
|
|
716
|
+
base = value.options.path;
|
|
717
|
+
appType = key === "vendor_ui" ? "vendor" : "admin";
|
|
718
|
+
break;
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
const plugins = medusaConfig?.plugins?.filter(
|
|
722
|
+
(plugin) => plugin.resolve !== "@medusajs/draft-order"
|
|
723
|
+
) ?? [];
|
|
724
|
+
const pluginExtensions = resolvePluginExtensions(plugins, configDir, appType);
|
|
725
|
+
return { base, pluginExtensions };
|
|
726
|
+
} catch {
|
|
727
|
+
return { pluginExtensions: [] };
|
|
728
|
+
}
|
|
521
729
|
}
|
|
522
|
-
function
|
|
730
|
+
function mercurDashboardPlugin(pluginConfig) {
|
|
523
731
|
let root;
|
|
524
732
|
let config;
|
|
525
733
|
return {
|
|
526
734
|
name: "@mercurjs/dashboard-sdk",
|
|
527
735
|
async config(viteConfig) {
|
|
528
736
|
root = viteConfig.root || process.cwd();
|
|
529
|
-
|
|
737
|
+
const medusaConfigPath = import_path5.default.resolve(
|
|
738
|
+
root,
|
|
739
|
+
pluginConfig.medusaConfigPath
|
|
740
|
+
);
|
|
741
|
+
const { base, pluginExtensions } = await loadMedusaConfig(
|
|
742
|
+
medusaConfigPath,
|
|
743
|
+
root
|
|
744
|
+
);
|
|
745
|
+
const srcDir = import_path5.default.join(root, "src");
|
|
746
|
+
const backendUrl = pluginConfig.backendUrl ?? "http://localhost:9000";
|
|
747
|
+
config = {
|
|
748
|
+
...pluginConfig,
|
|
749
|
+
backendUrl,
|
|
750
|
+
base,
|
|
751
|
+
root,
|
|
752
|
+
srcDir,
|
|
753
|
+
pluginExtensions
|
|
754
|
+
};
|
|
530
755
|
return {
|
|
756
|
+
base: config.base,
|
|
531
757
|
define: {
|
|
532
|
-
|
|
758
|
+
__BACKEND_URL__: JSON.stringify(config.backendUrl),
|
|
759
|
+
__BASE__: JSON.stringify(config.base || "/")
|
|
760
|
+
},
|
|
761
|
+
optimizeDeps: {
|
|
762
|
+
exclude: [
|
|
763
|
+
"virtual:mercur/config",
|
|
764
|
+
"virtual:mercur/routes",
|
|
765
|
+
"virtual:mercur/components",
|
|
766
|
+
"virtual:mercur/menu-items",
|
|
767
|
+
"virtual:mercur/i18n"
|
|
768
|
+
],
|
|
769
|
+
include: [
|
|
770
|
+
"react",
|
|
771
|
+
"react/jsx-runtime",
|
|
772
|
+
"react-dom/client",
|
|
773
|
+
"react-router-dom",
|
|
774
|
+
"react-i18next",
|
|
775
|
+
"@medusajs/ui",
|
|
776
|
+
"@medusajs/dashboard",
|
|
777
|
+
"@medusajs/js-sdk",
|
|
778
|
+
"@tanstack/react-query"
|
|
779
|
+
]
|
|
533
780
|
}
|
|
534
781
|
};
|
|
535
782
|
},
|
|
@@ -546,24 +793,19 @@ function dashboardPlugin() {
|
|
|
546
793
|
return loadVirtualModule({ cwd: root, id, mercurConfig: config });
|
|
547
794
|
},
|
|
548
795
|
configureServer(server) {
|
|
549
|
-
const
|
|
550
|
-
if (!
|
|
796
|
+
const handleRouteChange = (file) => {
|
|
797
|
+
if (!isRouteFile(file)) return;
|
|
551
798
|
const mod = server.moduleGraph.getModuleById(RESOLVED_ROUTES_MODULE);
|
|
552
799
|
if (mod) {
|
|
553
800
|
server.moduleGraph.invalidateModule(mod);
|
|
554
801
|
server.ws.send({ type: "full-reload" });
|
|
555
802
|
}
|
|
556
803
|
};
|
|
557
|
-
server.watcher.on("add",
|
|
558
|
-
server.watcher.on("unlink",
|
|
804
|
+
server.watcher.on("add", handleRouteChange);
|
|
805
|
+
server.watcher.on("unlink", handleRouteChange);
|
|
559
806
|
},
|
|
560
807
|
handleHotUpdate({ file, server }) {
|
|
561
|
-
|
|
562
|
-
if (file === configPath) {
|
|
563
|
-
server.restart();
|
|
564
|
-
return;
|
|
565
|
-
}
|
|
566
|
-
if (isPageFile(file)) {
|
|
808
|
+
if (isRouteFile(file)) {
|
|
567
809
|
const mod = server.moduleGraph.getModuleById(RESOLVED_ROUTES_MODULE);
|
|
568
810
|
if (mod) {
|
|
569
811
|
server.moduleGraph.invalidateModule(mod);
|
|
@@ -573,12 +815,71 @@ function dashboardPlugin() {
|
|
|
573
815
|
};
|
|
574
816
|
}
|
|
575
817
|
|
|
576
|
-
// src/
|
|
577
|
-
|
|
578
|
-
|
|
818
|
+
// src/generate-plugin-entry.ts
|
|
819
|
+
var import_path6 = __toESM(require("path"), 1);
|
|
820
|
+
var import_fs5 = __toESM(require("fs"), 1);
|
|
821
|
+
function findI18nIndex2(srcDir) {
|
|
822
|
+
const i18nDir = import_path6.default.join(srcDir, "i18n");
|
|
823
|
+
if (!import_fs5.default.existsSync(i18nDir)) return null;
|
|
824
|
+
for (const ext of VALID_FILE_EXTENSIONS) {
|
|
825
|
+
const filePath = import_path6.default.join(i18nDir, `index${ext}`);
|
|
826
|
+
if (import_fs5.default.existsSync(filePath)) return filePath;
|
|
827
|
+
}
|
|
828
|
+
return null;
|
|
829
|
+
}
|
|
830
|
+
function generatePluginEntryModule(srcDir) {
|
|
831
|
+
const routesDir = import_path6.default.join(srcDir, "routes");
|
|
832
|
+
const files = crawlRoutes(routesDir);
|
|
833
|
+
let index = 0;
|
|
834
|
+
const routeResults = [];
|
|
835
|
+
const menuItemResults = [];
|
|
836
|
+
for (const file of files) {
|
|
837
|
+
const route = parseFile(file, routesDir, index);
|
|
838
|
+
if (route) {
|
|
839
|
+
routeResults.push(route);
|
|
840
|
+
}
|
|
841
|
+
const menuItem = parseMenuItemFile(file, routesDir, index);
|
|
842
|
+
if (menuItem) {
|
|
843
|
+
menuItemResults.push(menuItem);
|
|
844
|
+
}
|
|
845
|
+
index++;
|
|
846
|
+
}
|
|
847
|
+
const routeTree = buildRouteTree(routeResults.filter(Boolean));
|
|
848
|
+
const routeImports = routeTree.flatMap((r) => r.imports);
|
|
849
|
+
const routes = routeTree.map((r) => formatRoute(r.route));
|
|
850
|
+
const menuItemImports = menuItemResults.filter(Boolean).map((r) => r.import);
|
|
851
|
+
const menuItems = menuItemResults.filter(Boolean).map((r) => formatMenuItem(r.menuItem));
|
|
852
|
+
const i18nFile = findI18nIndex2(srcDir);
|
|
853
|
+
const i18nImport = i18nFile ? `import i18nResources from "${normalizePath(i18nFile)}"` : "";
|
|
854
|
+
const i18nValue = i18nFile ? "i18nResources" : "{}";
|
|
855
|
+
const allImports = [...routeImports, ...menuItemImports];
|
|
856
|
+
if (i18nImport) allImports.push(i18nImport);
|
|
857
|
+
return `// Auto-generated plugin extensions entry
|
|
858
|
+
${allImports.join("\n")}
|
|
859
|
+
|
|
860
|
+
const routeModule = {
|
|
861
|
+
routes: [
|
|
862
|
+
${routes.join(",\n")}
|
|
863
|
+
]
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
const menuItemModule = {
|
|
867
|
+
menuItems: [
|
|
868
|
+
${menuItems.join(",\n")}
|
|
869
|
+
]
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
const plugin = {
|
|
873
|
+
routeModule,
|
|
874
|
+
menuItemModule,
|
|
875
|
+
i18nModule: ${i18nValue},
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
export default plugin
|
|
879
|
+
`;
|
|
579
880
|
}
|
|
580
881
|
// Annotate the CommonJS export names for ESM import in node:
|
|
581
882
|
0 && (module.exports = {
|
|
582
|
-
|
|
583
|
-
|
|
883
|
+
generatePluginEntryModule,
|
|
884
|
+
mercurDashboardPlugin
|
|
584
885
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -1,25 +1,27 @@
|
|
|
1
1
|
import * as Vite from 'vite';
|
|
2
2
|
import { ComponentType } from 'react';
|
|
3
3
|
|
|
4
|
-
declare function dashboardPlugin(): Vite.Plugin;
|
|
5
|
-
|
|
6
4
|
interface MercurConfig {
|
|
5
|
+
medusaConfigPath: string;
|
|
6
|
+
backendUrl?: string;
|
|
7
7
|
name?: string;
|
|
8
8
|
logo?: string;
|
|
9
9
|
components?: {
|
|
10
10
|
MainSidebar?: string;
|
|
11
11
|
SettingsSidebar?: string;
|
|
12
|
+
TopbarActions?: string;
|
|
12
13
|
};
|
|
13
14
|
i18n?: {
|
|
14
15
|
defaultLanguage: string;
|
|
15
16
|
};
|
|
16
|
-
|
|
17
|
+
enableSellerRegistration?: boolean;
|
|
17
18
|
}
|
|
18
19
|
interface BuiltMercurConfig extends MercurConfig {
|
|
19
20
|
backendUrl: string;
|
|
21
|
+
base?: string;
|
|
20
22
|
root: string;
|
|
21
23
|
srcDir: string;
|
|
22
|
-
|
|
24
|
+
pluginExtensions: string[];
|
|
23
25
|
}
|
|
24
26
|
type RouteConfig = {
|
|
25
27
|
label: string;
|
|
@@ -27,8 +29,16 @@ type RouteConfig = {
|
|
|
27
29
|
rank?: number;
|
|
28
30
|
nested?: string;
|
|
29
31
|
translationNs?: string;
|
|
32
|
+
public?: boolean;
|
|
30
33
|
};
|
|
31
34
|
|
|
32
|
-
declare function
|
|
35
|
+
declare function mercurDashboardPlugin(pluginConfig: MercurConfig): Vite.Plugin;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Generates a plugin entry module for a given source directory (e.g. src/vendor).
|
|
39
|
+
* Scans routes, menu items, and i18n — outputs a single module string
|
|
40
|
+
* that exports default `{ routeModule, menuItemModule, i18nModule }`.
|
|
41
|
+
*/
|
|
42
|
+
declare function generatePluginEntryModule(srcDir: string): string;
|
|
33
43
|
|
|
34
|
-
export { type BuiltMercurConfig, type MercurConfig, type RouteConfig,
|
|
44
|
+
export { type BuiltMercurConfig, type MercurConfig, type RouteConfig, generatePluginEntryModule, mercurDashboardPlugin };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mercurjs/dashboard-sdk",
|
|
3
|
-
"version": "2.0.0-canary.
|
|
3
|
+
"version": "2.0.0-canary.100",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/mercurjs/mercur",
|
|
@@ -25,6 +25,9 @@
|
|
|
25
25
|
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
+
"@babel/parser": "7.25.6",
|
|
29
|
+
"@babel/traverse": "7.25.6",
|
|
30
|
+
"@babel/types": "7.25.6",
|
|
28
31
|
"esbuild-register": "^3.6.0"
|
|
29
32
|
},
|
|
30
33
|
"devDependencies": {
|