@medusajs/admin-vite-plugin 0.0.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/README.md +1 -0
- package/dist/index.d.mts +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +688 -0
- package/dist/index.mjs +657 -0
- package/package.json +39 -0
package/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# plugin-extension
|
package/dist/index.d.mts
ADDED
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,688 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var src_exports = {};
|
|
32
|
+
__export(src_exports, {
|
|
33
|
+
default: () => inject
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(src_exports);
|
|
36
|
+
var import_parser = require("@babel/parser");
|
|
37
|
+
var import_traverse = __toESM(require("@babel/traverse"));
|
|
38
|
+
var import_chokidar = __toESM(require("chokidar"));
|
|
39
|
+
var import_fdir = require("fdir");
|
|
40
|
+
var import_promises = __toESM(require("fs/promises"));
|
|
41
|
+
var import_magic_string = __toESM(require("magic-string"));
|
|
42
|
+
var import_path = __toESM(require("path"));
|
|
43
|
+
|
|
44
|
+
// ../admin-shared/dist/constants.js
|
|
45
|
+
var injectionZones = [
|
|
46
|
+
// Order injection zones
|
|
47
|
+
"order.details.before",
|
|
48
|
+
"order.details.after",
|
|
49
|
+
"order.list.before",
|
|
50
|
+
"order.list.after",
|
|
51
|
+
// Draft order injection zones
|
|
52
|
+
"draft_order.list.before",
|
|
53
|
+
"draft_order.list.after",
|
|
54
|
+
"draft_order.details.before",
|
|
55
|
+
"draft_order.details.after",
|
|
56
|
+
// Customer injection zones
|
|
57
|
+
"customer.details.before",
|
|
58
|
+
"customer.details.after",
|
|
59
|
+
"customer.list.before",
|
|
60
|
+
"customer.list.after",
|
|
61
|
+
// Customer group injection zones
|
|
62
|
+
"customer_group.details.before",
|
|
63
|
+
"customer_group.details.after",
|
|
64
|
+
"customer_group.list.before",
|
|
65
|
+
"customer_group.list.after",
|
|
66
|
+
// Product injection zones
|
|
67
|
+
"product.details.before",
|
|
68
|
+
"product.details.after",
|
|
69
|
+
"product.list.before",
|
|
70
|
+
"product.list.after",
|
|
71
|
+
"product.details.side.before",
|
|
72
|
+
"product.details.side.after",
|
|
73
|
+
// Product collection injection zones
|
|
74
|
+
"product_collection.details.before",
|
|
75
|
+
"product_collection.details.after",
|
|
76
|
+
"product_collection.list.before",
|
|
77
|
+
"product_collection.list.after",
|
|
78
|
+
// Product category injection zones
|
|
79
|
+
"product_category.details.before",
|
|
80
|
+
"product_category.details.after",
|
|
81
|
+
"product_category.list.before",
|
|
82
|
+
"product_category.list.after",
|
|
83
|
+
// Price list injection zones
|
|
84
|
+
"price_list.details.before",
|
|
85
|
+
"price_list.details.after",
|
|
86
|
+
"price_list.list.before",
|
|
87
|
+
"price_list.list.after",
|
|
88
|
+
// Discount injection zones
|
|
89
|
+
"discount.details.before",
|
|
90
|
+
"discount.details.after",
|
|
91
|
+
"discount.list.before",
|
|
92
|
+
"discount.list.after",
|
|
93
|
+
// Promotion injection zones
|
|
94
|
+
"promotion.details.before",
|
|
95
|
+
"promotion.details.after",
|
|
96
|
+
"promotion.list.before",
|
|
97
|
+
"promotion.list.after",
|
|
98
|
+
// Gift card injection zones
|
|
99
|
+
"gift_card.details.before",
|
|
100
|
+
"gift_card.details.after",
|
|
101
|
+
"gift_card.list.before",
|
|
102
|
+
"gift_card.list.after",
|
|
103
|
+
"custom_gift_card.before",
|
|
104
|
+
"custom_gift_card.after",
|
|
105
|
+
// Login
|
|
106
|
+
"login.before",
|
|
107
|
+
"login.after"
|
|
108
|
+
];
|
|
109
|
+
|
|
110
|
+
// src/index.ts
|
|
111
|
+
var traverse = import_traverse.default.default;
|
|
112
|
+
var VIRTUAL_PREFIX = "/@virtual/medusajs-admin-vite-plugin/";
|
|
113
|
+
var IMPORT_PREFIX = "medusa-admin:";
|
|
114
|
+
var WIDGET_MODULE = `${IMPORT_PREFIX}widgets/`;
|
|
115
|
+
var WIDGET_MODULES = injectionZones.map((zone) => {
|
|
116
|
+
return `${WIDGET_MODULE}${zone.replace(/\./g, "/")}`;
|
|
117
|
+
});
|
|
118
|
+
var ROUTE_PAGE_MODULE = `${IMPORT_PREFIX}routes/pages`;
|
|
119
|
+
var ROUTE_LINK_MODULE = `${IMPORT_PREFIX}routes/links`;
|
|
120
|
+
var ROUTE_MODULES = [ROUTE_PAGE_MODULE, ROUTE_LINK_MODULE];
|
|
121
|
+
var SETTING_PAGE_MODULE = `${IMPORT_PREFIX}settings/pages`;
|
|
122
|
+
var SETTING_CARD_MODULE = `${IMPORT_PREFIX}settings/cards`;
|
|
123
|
+
var SETTING_MODULE = [SETTING_PAGE_MODULE, SETTING_CARD_MODULE];
|
|
124
|
+
var MODULES = [...WIDGET_MODULES, ...ROUTE_MODULES, ...SETTING_MODULE];
|
|
125
|
+
function inject(args) {
|
|
126
|
+
const _extensionGraph = /* @__PURE__ */ new Map();
|
|
127
|
+
const _sources = /* @__PURE__ */ new Set([...args?.sources || []]);
|
|
128
|
+
let server;
|
|
129
|
+
let watcher;
|
|
130
|
+
let logger;
|
|
131
|
+
async function traverseDirectory(dir, file, depth) {
|
|
132
|
+
const baseDepth = dir.split(import_path.default.sep).length;
|
|
133
|
+
const crawler = new import_fdir.fdir().withBasePath().exclude((dirName) => dirName.startsWith("_")).filter((path2) => path2.endsWith(".tsx") || path2.endsWith(".jsx"));
|
|
134
|
+
if (file) {
|
|
135
|
+
crawler.filter(
|
|
136
|
+
(path2) => path2.endsWith(`${file}.tsx`) || path2.endsWith(`${file}.jsx`)
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
if (depth) {
|
|
140
|
+
crawler.filter((file2) => {
|
|
141
|
+
const directoryDepth = file2.split(import_path.default.sep).length - 1;
|
|
142
|
+
if (depth.max && directoryDepth > baseDepth + depth.max) {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
if (directoryDepth < baseDepth + depth.min) {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
return true;
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
return await crawler.crawl(dir).withPromise();
|
|
152
|
+
}
|
|
153
|
+
function generateModule(code) {
|
|
154
|
+
const magicString = new import_magic_string.default(code);
|
|
155
|
+
return {
|
|
156
|
+
code: magicString.toString(),
|
|
157
|
+
map: magicString.generateMap({ hires: true })
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
function validateDefaultExport(path2, ast) {
|
|
161
|
+
let hasComponentExport = false;
|
|
162
|
+
const declaration = path2.node.declaration;
|
|
163
|
+
if (declaration && (declaration.type === "Identifier" || declaration.type === "FunctionDeclaration")) {
|
|
164
|
+
const exportName = declaration.type === "Identifier" ? declaration.name : declaration.id && declaration.id.name;
|
|
165
|
+
if (exportName) {
|
|
166
|
+
try {
|
|
167
|
+
traverse(ast, {
|
|
168
|
+
VariableDeclarator({ node, scope }) {
|
|
169
|
+
let isDefaultExport = false;
|
|
170
|
+
if (node.id.type === "Identifier" && node.id.name === exportName) {
|
|
171
|
+
isDefaultExport = true;
|
|
172
|
+
}
|
|
173
|
+
if (!isDefaultExport) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
traverse(
|
|
177
|
+
node,
|
|
178
|
+
{
|
|
179
|
+
ReturnStatement(path3) {
|
|
180
|
+
if (path3.node.argument?.type === "JSXElement" || path3.node.argument?.type === "JSXFragment") {
|
|
181
|
+
hasComponentExport = true;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
scope
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
} catch (e) {
|
|
190
|
+
console.error(
|
|
191
|
+
`An error occured while validating the default export of '${path2}'. The following error must be resolved before continuing:
|
|
192
|
+
${e}`
|
|
193
|
+
);
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return hasComponentExport;
|
|
199
|
+
}
|
|
200
|
+
function getProperties(path2) {
|
|
201
|
+
const declaration = path2.node.declaration;
|
|
202
|
+
if (declaration && declaration.type === "VariableDeclaration") {
|
|
203
|
+
const configDeclaration = declaration.declarations.find(
|
|
204
|
+
(d) => d.type === "VariableDeclarator" && d.id.type === "Identifier" && d.id.name === "config"
|
|
205
|
+
);
|
|
206
|
+
if (configDeclaration && configDeclaration.init?.type === "ObjectExpression") {
|
|
207
|
+
return configDeclaration.init.properties;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
function validateInjectionZone(zone) {
|
|
213
|
+
return injectionZones.includes(zone);
|
|
214
|
+
}
|
|
215
|
+
function validateWidgetConfig(path2, zone) {
|
|
216
|
+
const properties = getProperties(path2);
|
|
217
|
+
if (!properties) {
|
|
218
|
+
return { zoneIsValid: false, zoneValue: void 0 };
|
|
219
|
+
}
|
|
220
|
+
const zoneProperty = properties.find(
|
|
221
|
+
(p) => p.type === "ObjectProperty" && p.key.type === "Identifier" && p.key.name === "zone"
|
|
222
|
+
);
|
|
223
|
+
if (!zoneProperty) {
|
|
224
|
+
return { zoneIsValid: false, zoneValue: void 0 };
|
|
225
|
+
}
|
|
226
|
+
let zoneIsValid = false;
|
|
227
|
+
let zoneValue = void 0;
|
|
228
|
+
if (zoneProperty.value.type === "StringLiteral") {
|
|
229
|
+
zoneIsValid = !zone ? validateInjectionZone(zoneProperty.value.value) : zone === zoneProperty.value.value;
|
|
230
|
+
zoneValue = zoneProperty.value.value;
|
|
231
|
+
} else if (zoneProperty.value.type === "ArrayExpression") {
|
|
232
|
+
zoneIsValid = zoneProperty.value.elements.every((_zone) => {
|
|
233
|
+
if (!_zone || _zone.type !== "StringLiteral") {
|
|
234
|
+
return false;
|
|
235
|
+
}
|
|
236
|
+
const isZoneMatch = !zone ? true : zone === _zone.value;
|
|
237
|
+
return validateInjectionZone(_zone.value) && isZoneMatch;
|
|
238
|
+
});
|
|
239
|
+
zoneValue = zoneProperty.value.elements.map((e) => {
|
|
240
|
+
if (e && e.type === "StringLiteral") {
|
|
241
|
+
return e.value;
|
|
242
|
+
}
|
|
243
|
+
}).filter(Boolean);
|
|
244
|
+
}
|
|
245
|
+
return { zoneIsValid, zoneValue };
|
|
246
|
+
}
|
|
247
|
+
async function validateWidget(file, zone) {
|
|
248
|
+
const content = await import_promises.default.readFile(file, "utf-8");
|
|
249
|
+
const parserOptions = {
|
|
250
|
+
sourceType: "module",
|
|
251
|
+
plugins: ["jsx"]
|
|
252
|
+
};
|
|
253
|
+
if (file.endsWith(".tsx")) {
|
|
254
|
+
parserOptions.plugins?.push("typescript");
|
|
255
|
+
}
|
|
256
|
+
let ast;
|
|
257
|
+
try {
|
|
258
|
+
ast = (0, import_parser.parse)(content, parserOptions);
|
|
259
|
+
} catch (err) {
|
|
260
|
+
logger.error(
|
|
261
|
+
`An error occured while parsing the content of ${file}:
|
|
262
|
+
${err}`,
|
|
263
|
+
{
|
|
264
|
+
error: err,
|
|
265
|
+
timestamp: true
|
|
266
|
+
}
|
|
267
|
+
);
|
|
268
|
+
return { isValidWidget: false, zoneValue: void 0 };
|
|
269
|
+
}
|
|
270
|
+
let hasDefaultExport = false;
|
|
271
|
+
let hasNamedExport = false;
|
|
272
|
+
let zoneValue;
|
|
273
|
+
try {
|
|
274
|
+
traverse(ast, {
|
|
275
|
+
ExportDefaultDeclaration(path2) {
|
|
276
|
+
hasDefaultExport = validateDefaultExport(path2, ast);
|
|
277
|
+
},
|
|
278
|
+
ExportNamedDeclaration(path2) {
|
|
279
|
+
const { zoneIsValid, zoneValue: value } = validateWidgetConfig(
|
|
280
|
+
path2,
|
|
281
|
+
zone
|
|
282
|
+
);
|
|
283
|
+
hasNamedExport = zoneIsValid;
|
|
284
|
+
zoneValue = value;
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
} catch (err) {
|
|
288
|
+
logger.error(`An error occured while validating the content of ${file}`, {
|
|
289
|
+
error: err,
|
|
290
|
+
timestamp: true
|
|
291
|
+
});
|
|
292
|
+
return { isValidWidget: false, zoneValue: void 0 };
|
|
293
|
+
}
|
|
294
|
+
return { isValidWidget: hasDefaultExport && hasNamedExport, zoneValue };
|
|
295
|
+
}
|
|
296
|
+
async function generateWidgetEntrypoint(zone) {
|
|
297
|
+
const files = (await Promise.all(
|
|
298
|
+
Array.from(_sources).map(
|
|
299
|
+
async (source) => traverseDirectory(`${source}/widgets`)
|
|
300
|
+
)
|
|
301
|
+
)).flat();
|
|
302
|
+
const validatedWidgets = (await Promise.all(
|
|
303
|
+
files.map(async (widget) => {
|
|
304
|
+
const { isValidWidget } = await validateWidget(widget, zone);
|
|
305
|
+
return isValidWidget ? widget : null;
|
|
306
|
+
})
|
|
307
|
+
)).filter(Boolean);
|
|
308
|
+
if (!validatedWidgets.length) {
|
|
309
|
+
const code2 = `export default {
|
|
310
|
+
widgets: [],
|
|
311
|
+
}`;
|
|
312
|
+
return { module: generateModule(code2), paths: [] };
|
|
313
|
+
}
|
|
314
|
+
const importString = validatedWidgets.map((path2, index) => `import WidgetExt${index} from "${path2}";`).join("\n");
|
|
315
|
+
const exportString = `export default {
|
|
316
|
+
widgets: [${validatedWidgets.map((_, index) => `{ Component: WidgetExt${index} }`).join(", ")}],
|
|
317
|
+
}`;
|
|
318
|
+
const code = `${importString}
|
|
319
|
+
${exportString}`;
|
|
320
|
+
return { module: generateModule(code), paths: validatedWidgets };
|
|
321
|
+
}
|
|
322
|
+
function validateRouteConfig(path2, requireLink) {
|
|
323
|
+
const properties = getProperties(path2);
|
|
324
|
+
if (!properties) {
|
|
325
|
+
return false;
|
|
326
|
+
}
|
|
327
|
+
const linkProperty = properties.find(
|
|
328
|
+
(p) => p.type === "ObjectProperty" && p.key.type === "Identifier" && p.key.name === "link"
|
|
329
|
+
);
|
|
330
|
+
if (!linkProperty && !requireLink) {
|
|
331
|
+
return true;
|
|
332
|
+
}
|
|
333
|
+
const linkValue = linkProperty?.value;
|
|
334
|
+
if (!linkValue) {
|
|
335
|
+
return false;
|
|
336
|
+
}
|
|
337
|
+
let labelIsValid = false;
|
|
338
|
+
if (linkValue.properties.some(
|
|
339
|
+
(p) => p.type === "ObjectProperty" && p.key.type === "Identifier" && p.key.name === "label" && p.value.type === "StringLiteral"
|
|
340
|
+
)) {
|
|
341
|
+
labelIsValid = true;
|
|
342
|
+
}
|
|
343
|
+
return labelIsValid;
|
|
344
|
+
}
|
|
345
|
+
async function validateRoute(file, requireLink) {
|
|
346
|
+
const content = await import_promises.default.readFile(file, "utf-8");
|
|
347
|
+
const parserOptions = {
|
|
348
|
+
sourceType: "module",
|
|
349
|
+
plugins: ["jsx"]
|
|
350
|
+
};
|
|
351
|
+
if (file.endsWith(".tsx")) {
|
|
352
|
+
parserOptions.plugins?.push("typescript");
|
|
353
|
+
}
|
|
354
|
+
let ast;
|
|
355
|
+
try {
|
|
356
|
+
ast = (0, import_parser.parse)(content, parserOptions);
|
|
357
|
+
} catch (err) {
|
|
358
|
+
logger.error("An error occured while validating a route.", {
|
|
359
|
+
error: err,
|
|
360
|
+
timestamp: true
|
|
361
|
+
});
|
|
362
|
+
return false;
|
|
363
|
+
}
|
|
364
|
+
let hasDefaultExport = false;
|
|
365
|
+
let hasNamedExport = false;
|
|
366
|
+
try {
|
|
367
|
+
traverse(ast, {
|
|
368
|
+
ExportDefaultDeclaration(path2) {
|
|
369
|
+
hasDefaultExport = validateDefaultExport(path2, ast);
|
|
370
|
+
},
|
|
371
|
+
ExportNamedDeclaration(path2) {
|
|
372
|
+
hasNamedExport = validateRouteConfig(path2, requireLink);
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
} catch (err) {
|
|
376
|
+
logger.error("An error occured while validating a route.", {
|
|
377
|
+
error: err,
|
|
378
|
+
timestamp: true
|
|
379
|
+
});
|
|
380
|
+
return false;
|
|
381
|
+
}
|
|
382
|
+
return hasDefaultExport && hasNamedExport;
|
|
383
|
+
}
|
|
384
|
+
function createPath(file) {
|
|
385
|
+
return file.replace(/.*\/admin\/(routes|settings)/, "").replace(/\[([^\]]+)\]/g, ":$1").replace(/\/page\.(tsx|jsx)/, "");
|
|
386
|
+
}
|
|
387
|
+
async function generateRouteEntrypoint(get) {
|
|
388
|
+
const files = (await Promise.all(
|
|
389
|
+
Array.from(_sources).map(
|
|
390
|
+
async (source) => traverseDirectory(`${source}/routes`, "page", { min: 1 })
|
|
391
|
+
)
|
|
392
|
+
)).flat();
|
|
393
|
+
const validatedRoutes = (await Promise.all(
|
|
394
|
+
files.map(async (route) => {
|
|
395
|
+
const isValid = await validateRoute(route, get === "link");
|
|
396
|
+
return isValid ? route : null;
|
|
397
|
+
})
|
|
398
|
+
)).filter(Boolean);
|
|
399
|
+
if (!validatedRoutes.length) {
|
|
400
|
+
const code2 = `export default {
|
|
401
|
+
${get}s: [],
|
|
402
|
+
}`;
|
|
403
|
+
return { module: generateModule(code2), paths: [] };
|
|
404
|
+
}
|
|
405
|
+
const importString = validatedRoutes.map((path2, index) => {
|
|
406
|
+
return get === "page" ? `import RouteExt${index} from "${path2}";` : `import { config as routeConfig${index} } from "${path2}";`;
|
|
407
|
+
}).join("\n");
|
|
408
|
+
const exportString = `export default {
|
|
409
|
+
${get}s: [${validatedRoutes.map((file, index) => {
|
|
410
|
+
return get === "page" ? `{ path: "${createPath(file)}", file: "${file}" }` : `{ path: "${createPath(file)}", ...routeConfig${index}.link }`;
|
|
411
|
+
}).join(", ")}],
|
|
412
|
+
}`;
|
|
413
|
+
const code = `${importString}
|
|
414
|
+
${exportString}`;
|
|
415
|
+
return { module: generateModule(code), paths: validatedRoutes };
|
|
416
|
+
}
|
|
417
|
+
async function validateSetting(file) {
|
|
418
|
+
const content = await import_promises.default.readFile(file, "utf-8");
|
|
419
|
+
const parserOptions = {
|
|
420
|
+
sourceType: "module",
|
|
421
|
+
plugins: ["jsx"]
|
|
422
|
+
};
|
|
423
|
+
if (file.endsWith(".tsx")) {
|
|
424
|
+
parserOptions.plugins?.push("typescript");
|
|
425
|
+
}
|
|
426
|
+
let ast;
|
|
427
|
+
try {
|
|
428
|
+
ast = (0, import_parser.parse)(content, parserOptions);
|
|
429
|
+
} catch (err) {
|
|
430
|
+
logger.error("An error occured while validating a setting.", {
|
|
431
|
+
error: err,
|
|
432
|
+
timestamp: true
|
|
433
|
+
});
|
|
434
|
+
return false;
|
|
435
|
+
}
|
|
436
|
+
let hasDefaultExport = false;
|
|
437
|
+
let hasNamedExport = false;
|
|
438
|
+
try {
|
|
439
|
+
traverse(ast, {
|
|
440
|
+
ExportDefaultDeclaration(path2) {
|
|
441
|
+
hasDefaultExport = validateDefaultExport(path2, ast);
|
|
442
|
+
},
|
|
443
|
+
ExportNamedDeclaration(path2) {
|
|
444
|
+
hasNamedExport = validateSettingConfig(path2);
|
|
445
|
+
}
|
|
446
|
+
});
|
|
447
|
+
} catch (err) {
|
|
448
|
+
logger.error("An error occured while validating a setting.", {
|
|
449
|
+
error: err,
|
|
450
|
+
timestamp: true
|
|
451
|
+
});
|
|
452
|
+
return false;
|
|
453
|
+
}
|
|
454
|
+
return hasDefaultExport && hasNamedExport;
|
|
455
|
+
}
|
|
456
|
+
function validateSettingConfig(path2) {
|
|
457
|
+
const properties = getProperties(path2);
|
|
458
|
+
if (!properties) {
|
|
459
|
+
return false;
|
|
460
|
+
}
|
|
461
|
+
const cardProperty = properties.find(
|
|
462
|
+
(p) => p.type === "ObjectProperty" && p.key.type === "Identifier" && p.key.name === "card"
|
|
463
|
+
);
|
|
464
|
+
if (!cardProperty) {
|
|
465
|
+
return false;
|
|
466
|
+
}
|
|
467
|
+
const cardValue = cardProperty.value;
|
|
468
|
+
let hasLabel = false;
|
|
469
|
+
let hasDescription = false;
|
|
470
|
+
if (cardValue.properties.some(
|
|
471
|
+
(p) => p.type === "ObjectProperty" && p.key.type === "Identifier" && p.key.name === "label" && p.value.type === "StringLiteral"
|
|
472
|
+
)) {
|
|
473
|
+
hasLabel = true;
|
|
474
|
+
}
|
|
475
|
+
if (cardValue.properties.some(
|
|
476
|
+
(p) => p.type === "ObjectProperty" && p.key.type === "Identifier" && p.key.name === "description" && p.value.type === "StringLiteral"
|
|
477
|
+
)) {
|
|
478
|
+
hasDescription = true;
|
|
479
|
+
}
|
|
480
|
+
return hasLabel && hasDescription;
|
|
481
|
+
}
|
|
482
|
+
async function generateSettingEntrypoint(get) {
|
|
483
|
+
const files = (await Promise.all(
|
|
484
|
+
Array.from(_sources).map(
|
|
485
|
+
async (source) => traverseDirectory(`${source}/settings`, "page", { min: 1, max: 1 })
|
|
486
|
+
)
|
|
487
|
+
)).flat();
|
|
488
|
+
const validatedSettings = (await Promise.all(
|
|
489
|
+
files.map(async (setting) => {
|
|
490
|
+
const isValid = await validateSetting(setting);
|
|
491
|
+
return isValid ? setting : null;
|
|
492
|
+
})
|
|
493
|
+
)).filter(Boolean);
|
|
494
|
+
if (!validatedSettings.length) {
|
|
495
|
+
const code2 = `export default {
|
|
496
|
+
${get}s: [],
|
|
497
|
+
}`;
|
|
498
|
+
return { module: generateModule(code2), paths: [] };
|
|
499
|
+
}
|
|
500
|
+
const importString = validatedSettings.map((path2, index) => {
|
|
501
|
+
return get === "page" ? `import SettingExt${index} from "${path2}";` : `import { config as settingConfig${index} } from "${path2}";`;
|
|
502
|
+
}).join("\n");
|
|
503
|
+
const exportString = `export default {
|
|
504
|
+
${get}s: [${validatedSettings.map((file, index) => {
|
|
505
|
+
return get === "page" ? `{ path: "${createPath(file)}", file: "${file}" }` : `{ path: "${createPath(file)}", ...settingConfig${index}.card }`;
|
|
506
|
+
}).join(", ")}],
|
|
507
|
+
}`;
|
|
508
|
+
const code = `${importString}
|
|
509
|
+
${exportString}`;
|
|
510
|
+
return { module: generateModule(code), paths: validatedSettings };
|
|
511
|
+
}
|
|
512
|
+
async function loadModule(options) {
|
|
513
|
+
switch (options.type) {
|
|
514
|
+
case "widget": {
|
|
515
|
+
return await generateWidgetEntrypoint(options.get);
|
|
516
|
+
}
|
|
517
|
+
case "route": {
|
|
518
|
+
return await generateRouteEntrypoint(options.get);
|
|
519
|
+
}
|
|
520
|
+
case "setting": {
|
|
521
|
+
return await generateSettingEntrypoint(options.get);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
function getExtensionType(file) {
|
|
526
|
+
const normalizedPath = import_path.default.normalize(file);
|
|
527
|
+
if (normalizedPath.includes(import_path.default.normalize("/admin/widgets/"))) {
|
|
528
|
+
return "widget";
|
|
529
|
+
} else if (normalizedPath.includes(import_path.default.normalize("/admin/routes/"))) {
|
|
530
|
+
return "route";
|
|
531
|
+
} else if (normalizedPath.includes(import_path.default.normalize("/admin/settings/"))) {
|
|
532
|
+
return "setting";
|
|
533
|
+
} else {
|
|
534
|
+
return "none";
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
async function handleWidgetChange(file) {
|
|
538
|
+
const { isValidWidget, zoneValue } = await validateWidget(file);
|
|
539
|
+
if (!isValidWidget || !zoneValue) {
|
|
540
|
+
_extensionGraph.delete(file);
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
const zoneValues = Array.isArray(zoneValue) ? zoneValue : [zoneValue];
|
|
544
|
+
for (const zone of zoneValues) {
|
|
545
|
+
const zonePath = zone.replace(/\./g, "/");
|
|
546
|
+
const moduleId = `${VIRTUAL_PREFIX}${WIDGET_MODULE}${zonePath}`;
|
|
547
|
+
const module2 = server.moduleGraph.getModuleById(moduleId);
|
|
548
|
+
if (module2) {
|
|
549
|
+
await server.reloadModule(module2);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
async function handleRouteChange(file) {
|
|
554
|
+
const isValidRoute = await validateRoute(file, false);
|
|
555
|
+
if (!isValidRoute) {
|
|
556
|
+
_extensionGraph.delete(file);
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
for (const moduleId of ROUTE_MODULES) {
|
|
560
|
+
const fullModuleId = `${VIRTUAL_PREFIX}${moduleId}`;
|
|
561
|
+
const module2 = server.moduleGraph.getModuleById(fullModuleId);
|
|
562
|
+
if (module2) {
|
|
563
|
+
await server.reloadModule(module2);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
async function handleSettingChange(file) {
|
|
568
|
+
const isValidSetting = await validateSetting(file);
|
|
569
|
+
if (!isValidSetting) {
|
|
570
|
+
_extensionGraph.delete(file);
|
|
571
|
+
return;
|
|
572
|
+
}
|
|
573
|
+
for (const moduleId of SETTING_MODULE) {
|
|
574
|
+
const fullModuleId = `${VIRTUAL_PREFIX}${moduleId}`;
|
|
575
|
+
const module2 = server.moduleGraph.getModuleById(fullModuleId);
|
|
576
|
+
if (module2) {
|
|
577
|
+
await server.reloadModule(module2);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
async function handleExtensionUnlink(file) {
|
|
582
|
+
const moduleIds = _extensionGraph.get(file);
|
|
583
|
+
if (!moduleIds) {
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
for (const moduleId of moduleIds) {
|
|
587
|
+
const module2 = server.moduleGraph.getModuleById(moduleId);
|
|
588
|
+
if (module2) {
|
|
589
|
+
_extensionGraph.delete(file);
|
|
590
|
+
await server.reloadModule(module2);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
async function loadModuleAndUpdateGraph(id, options) {
|
|
595
|
+
const { module: module2, paths } = await loadModule(options);
|
|
596
|
+
for (const path2 of paths) {
|
|
597
|
+
const ids = _extensionGraph.get(path2) || /* @__PURE__ */ new Set();
|
|
598
|
+
ids.add(id);
|
|
599
|
+
_extensionGraph.set(path2, ids);
|
|
600
|
+
}
|
|
601
|
+
return module2;
|
|
602
|
+
}
|
|
603
|
+
return {
|
|
604
|
+
name: "@medusajs/admin-vite-plugin",
|
|
605
|
+
configureServer(s) {
|
|
606
|
+
server = s;
|
|
607
|
+
logger = s.config.logger;
|
|
608
|
+
watcher = import_chokidar.default.watch(Array.from(_sources), {
|
|
609
|
+
persistent: true,
|
|
610
|
+
ignoreInitial: true
|
|
611
|
+
});
|
|
612
|
+
watcher.on("add", async (file) => {
|
|
613
|
+
const type = getExtensionType(file);
|
|
614
|
+
if (type === "none") {
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
if (type === "widget") {
|
|
618
|
+
await handleWidgetChange(file);
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
621
|
+
if (type === "route") {
|
|
622
|
+
await handleRouteChange(file);
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
if (type === "setting") {
|
|
626
|
+
await handleSettingChange(file);
|
|
627
|
+
return;
|
|
628
|
+
}
|
|
629
|
+
return;
|
|
630
|
+
});
|
|
631
|
+
watcher.on("change", async (file) => {
|
|
632
|
+
const type = getExtensionType(file);
|
|
633
|
+
if (type === "none") {
|
|
634
|
+
return;
|
|
635
|
+
}
|
|
636
|
+
if (type === "widget") {
|
|
637
|
+
await handleWidgetChange(file);
|
|
638
|
+
return;
|
|
639
|
+
}
|
|
640
|
+
if (type === "route") {
|
|
641
|
+
await handleRouteChange(file);
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
if (type === "setting") {
|
|
645
|
+
await handleSettingChange(file);
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
648
|
+
return;
|
|
649
|
+
});
|
|
650
|
+
watcher.on("unlink", async (file) => {
|
|
651
|
+
await handleExtensionUnlink(file);
|
|
652
|
+
return;
|
|
653
|
+
});
|
|
654
|
+
},
|
|
655
|
+
resolveId(id) {
|
|
656
|
+
if (MODULES.includes(id)) {
|
|
657
|
+
return VIRTUAL_PREFIX + id;
|
|
658
|
+
}
|
|
659
|
+
return null;
|
|
660
|
+
},
|
|
661
|
+
async load(id) {
|
|
662
|
+
if (!id.startsWith(VIRTUAL_PREFIX)) {
|
|
663
|
+
return null;
|
|
664
|
+
}
|
|
665
|
+
const idNoPrefix = id.slice(VIRTUAL_PREFIX.length);
|
|
666
|
+
const moduleMap = {
|
|
667
|
+
[ROUTE_PAGE_MODULE]: { type: "route", get: "page" },
|
|
668
|
+
[ROUTE_LINK_MODULE]: { type: "route", get: "link" },
|
|
669
|
+
[SETTING_PAGE_MODULE]: { type: "setting", get: "page" },
|
|
670
|
+
[SETTING_CARD_MODULE]: { type: "setting", get: "card" }
|
|
671
|
+
};
|
|
672
|
+
if (WIDGET_MODULES.includes(idNoPrefix)) {
|
|
673
|
+
const zone = idNoPrefix.replace(WIDGET_MODULE, "").replace(/\//g, ".");
|
|
674
|
+
return loadModuleAndUpdateGraph(id, { type: "widget", get: zone });
|
|
675
|
+
}
|
|
676
|
+
const moduleOptions = moduleMap[idNoPrefix];
|
|
677
|
+
if (moduleOptions) {
|
|
678
|
+
return loadModuleAndUpdateGraph(id, moduleOptions);
|
|
679
|
+
}
|
|
680
|
+
return null;
|
|
681
|
+
},
|
|
682
|
+
async closeBundle() {
|
|
683
|
+
if (watcher) {
|
|
684
|
+
await watcher.close();
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
};
|
|
688
|
+
}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,657 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { parse } from "@babel/parser";
|
|
3
|
+
import _traverse from "@babel/traverse";
|
|
4
|
+
import chokidar from "chokidar";
|
|
5
|
+
import { fdir } from "fdir";
|
|
6
|
+
import fs from "fs/promises";
|
|
7
|
+
import MagicString from "magic-string";
|
|
8
|
+
import path from "path";
|
|
9
|
+
|
|
10
|
+
// ../admin-shared/dist/constants.js
|
|
11
|
+
var injectionZones = [
|
|
12
|
+
// Order injection zones
|
|
13
|
+
"order.details.before",
|
|
14
|
+
"order.details.after",
|
|
15
|
+
"order.list.before",
|
|
16
|
+
"order.list.after",
|
|
17
|
+
// Draft order injection zones
|
|
18
|
+
"draft_order.list.before",
|
|
19
|
+
"draft_order.list.after",
|
|
20
|
+
"draft_order.details.before",
|
|
21
|
+
"draft_order.details.after",
|
|
22
|
+
// Customer injection zones
|
|
23
|
+
"customer.details.before",
|
|
24
|
+
"customer.details.after",
|
|
25
|
+
"customer.list.before",
|
|
26
|
+
"customer.list.after",
|
|
27
|
+
// Customer group injection zones
|
|
28
|
+
"customer_group.details.before",
|
|
29
|
+
"customer_group.details.after",
|
|
30
|
+
"customer_group.list.before",
|
|
31
|
+
"customer_group.list.after",
|
|
32
|
+
// Product injection zones
|
|
33
|
+
"product.details.before",
|
|
34
|
+
"product.details.after",
|
|
35
|
+
"product.list.before",
|
|
36
|
+
"product.list.after",
|
|
37
|
+
"product.details.side.before",
|
|
38
|
+
"product.details.side.after",
|
|
39
|
+
// Product collection injection zones
|
|
40
|
+
"product_collection.details.before",
|
|
41
|
+
"product_collection.details.after",
|
|
42
|
+
"product_collection.list.before",
|
|
43
|
+
"product_collection.list.after",
|
|
44
|
+
// Product category injection zones
|
|
45
|
+
"product_category.details.before",
|
|
46
|
+
"product_category.details.after",
|
|
47
|
+
"product_category.list.before",
|
|
48
|
+
"product_category.list.after",
|
|
49
|
+
// Price list injection zones
|
|
50
|
+
"price_list.details.before",
|
|
51
|
+
"price_list.details.after",
|
|
52
|
+
"price_list.list.before",
|
|
53
|
+
"price_list.list.after",
|
|
54
|
+
// Discount injection zones
|
|
55
|
+
"discount.details.before",
|
|
56
|
+
"discount.details.after",
|
|
57
|
+
"discount.list.before",
|
|
58
|
+
"discount.list.after",
|
|
59
|
+
// Promotion injection zones
|
|
60
|
+
"promotion.details.before",
|
|
61
|
+
"promotion.details.after",
|
|
62
|
+
"promotion.list.before",
|
|
63
|
+
"promotion.list.after",
|
|
64
|
+
// Gift card injection zones
|
|
65
|
+
"gift_card.details.before",
|
|
66
|
+
"gift_card.details.after",
|
|
67
|
+
"gift_card.list.before",
|
|
68
|
+
"gift_card.list.after",
|
|
69
|
+
"custom_gift_card.before",
|
|
70
|
+
"custom_gift_card.after",
|
|
71
|
+
// Login
|
|
72
|
+
"login.before",
|
|
73
|
+
"login.after"
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
// src/index.ts
|
|
77
|
+
var traverse = _traverse.default;
|
|
78
|
+
var VIRTUAL_PREFIX = "/@virtual/medusajs-admin-vite-plugin/";
|
|
79
|
+
var IMPORT_PREFIX = "medusa-admin:";
|
|
80
|
+
var WIDGET_MODULE = `${IMPORT_PREFIX}widgets/`;
|
|
81
|
+
var WIDGET_MODULES = injectionZones.map((zone) => {
|
|
82
|
+
return `${WIDGET_MODULE}${zone.replace(/\./g, "/")}`;
|
|
83
|
+
});
|
|
84
|
+
var ROUTE_PAGE_MODULE = `${IMPORT_PREFIX}routes/pages`;
|
|
85
|
+
var ROUTE_LINK_MODULE = `${IMPORT_PREFIX}routes/links`;
|
|
86
|
+
var ROUTE_MODULES = [ROUTE_PAGE_MODULE, ROUTE_LINK_MODULE];
|
|
87
|
+
var SETTING_PAGE_MODULE = `${IMPORT_PREFIX}settings/pages`;
|
|
88
|
+
var SETTING_CARD_MODULE = `${IMPORT_PREFIX}settings/cards`;
|
|
89
|
+
var SETTING_MODULE = [SETTING_PAGE_MODULE, SETTING_CARD_MODULE];
|
|
90
|
+
var MODULES = [...WIDGET_MODULES, ...ROUTE_MODULES, ...SETTING_MODULE];
|
|
91
|
+
function inject(args) {
|
|
92
|
+
const _extensionGraph = /* @__PURE__ */ new Map();
|
|
93
|
+
const _sources = /* @__PURE__ */ new Set([...args?.sources || []]);
|
|
94
|
+
let server;
|
|
95
|
+
let watcher;
|
|
96
|
+
let logger;
|
|
97
|
+
async function traverseDirectory(dir, file, depth) {
|
|
98
|
+
const baseDepth = dir.split(path.sep).length;
|
|
99
|
+
const crawler = new fdir().withBasePath().exclude((dirName) => dirName.startsWith("_")).filter((path2) => path2.endsWith(".tsx") || path2.endsWith(".jsx"));
|
|
100
|
+
if (file) {
|
|
101
|
+
crawler.filter(
|
|
102
|
+
(path2) => path2.endsWith(`${file}.tsx`) || path2.endsWith(`${file}.jsx`)
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
if (depth) {
|
|
106
|
+
crawler.filter((file2) => {
|
|
107
|
+
const directoryDepth = file2.split(path.sep).length - 1;
|
|
108
|
+
if (depth.max && directoryDepth > baseDepth + depth.max) {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
if (directoryDepth < baseDepth + depth.min) {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
return true;
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
return await crawler.crawl(dir).withPromise();
|
|
118
|
+
}
|
|
119
|
+
function generateModule(code) {
|
|
120
|
+
const magicString = new MagicString(code);
|
|
121
|
+
return {
|
|
122
|
+
code: magicString.toString(),
|
|
123
|
+
map: magicString.generateMap({ hires: true })
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
function validateDefaultExport(path2, ast) {
|
|
127
|
+
let hasComponentExport = false;
|
|
128
|
+
const declaration = path2.node.declaration;
|
|
129
|
+
if (declaration && (declaration.type === "Identifier" || declaration.type === "FunctionDeclaration")) {
|
|
130
|
+
const exportName = declaration.type === "Identifier" ? declaration.name : declaration.id && declaration.id.name;
|
|
131
|
+
if (exportName) {
|
|
132
|
+
try {
|
|
133
|
+
traverse(ast, {
|
|
134
|
+
VariableDeclarator({ node, scope }) {
|
|
135
|
+
let isDefaultExport = false;
|
|
136
|
+
if (node.id.type === "Identifier" && node.id.name === exportName) {
|
|
137
|
+
isDefaultExport = true;
|
|
138
|
+
}
|
|
139
|
+
if (!isDefaultExport) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
traverse(
|
|
143
|
+
node,
|
|
144
|
+
{
|
|
145
|
+
ReturnStatement(path3) {
|
|
146
|
+
if (path3.node.argument?.type === "JSXElement" || path3.node.argument?.type === "JSXFragment") {
|
|
147
|
+
hasComponentExport = true;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
scope
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
} catch (e) {
|
|
156
|
+
console.error(
|
|
157
|
+
`An error occured while validating the default export of '${path2}'. The following error must be resolved before continuing:
|
|
158
|
+
${e}`
|
|
159
|
+
);
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return hasComponentExport;
|
|
165
|
+
}
|
|
166
|
+
function getProperties(path2) {
|
|
167
|
+
const declaration = path2.node.declaration;
|
|
168
|
+
if (declaration && declaration.type === "VariableDeclaration") {
|
|
169
|
+
const configDeclaration = declaration.declarations.find(
|
|
170
|
+
(d) => d.type === "VariableDeclarator" && d.id.type === "Identifier" && d.id.name === "config"
|
|
171
|
+
);
|
|
172
|
+
if (configDeclaration && configDeclaration.init?.type === "ObjectExpression") {
|
|
173
|
+
return configDeclaration.init.properties;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
function validateInjectionZone(zone) {
|
|
179
|
+
return injectionZones.includes(zone);
|
|
180
|
+
}
|
|
181
|
+
function validateWidgetConfig(path2, zone) {
|
|
182
|
+
const properties = getProperties(path2);
|
|
183
|
+
if (!properties) {
|
|
184
|
+
return { zoneIsValid: false, zoneValue: void 0 };
|
|
185
|
+
}
|
|
186
|
+
const zoneProperty = properties.find(
|
|
187
|
+
(p) => p.type === "ObjectProperty" && p.key.type === "Identifier" && p.key.name === "zone"
|
|
188
|
+
);
|
|
189
|
+
if (!zoneProperty) {
|
|
190
|
+
return { zoneIsValid: false, zoneValue: void 0 };
|
|
191
|
+
}
|
|
192
|
+
let zoneIsValid = false;
|
|
193
|
+
let zoneValue = void 0;
|
|
194
|
+
if (zoneProperty.value.type === "StringLiteral") {
|
|
195
|
+
zoneIsValid = !zone ? validateInjectionZone(zoneProperty.value.value) : zone === zoneProperty.value.value;
|
|
196
|
+
zoneValue = zoneProperty.value.value;
|
|
197
|
+
} else if (zoneProperty.value.type === "ArrayExpression") {
|
|
198
|
+
zoneIsValid = zoneProperty.value.elements.every((_zone) => {
|
|
199
|
+
if (!_zone || _zone.type !== "StringLiteral") {
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
const isZoneMatch = !zone ? true : zone === _zone.value;
|
|
203
|
+
return validateInjectionZone(_zone.value) && isZoneMatch;
|
|
204
|
+
});
|
|
205
|
+
zoneValue = zoneProperty.value.elements.map((e) => {
|
|
206
|
+
if (e && e.type === "StringLiteral") {
|
|
207
|
+
return e.value;
|
|
208
|
+
}
|
|
209
|
+
}).filter(Boolean);
|
|
210
|
+
}
|
|
211
|
+
return { zoneIsValid, zoneValue };
|
|
212
|
+
}
|
|
213
|
+
async function validateWidget(file, zone) {
|
|
214
|
+
const content = await fs.readFile(file, "utf-8");
|
|
215
|
+
const parserOptions = {
|
|
216
|
+
sourceType: "module",
|
|
217
|
+
plugins: ["jsx"]
|
|
218
|
+
};
|
|
219
|
+
if (file.endsWith(".tsx")) {
|
|
220
|
+
parserOptions.plugins?.push("typescript");
|
|
221
|
+
}
|
|
222
|
+
let ast;
|
|
223
|
+
try {
|
|
224
|
+
ast = parse(content, parserOptions);
|
|
225
|
+
} catch (err) {
|
|
226
|
+
logger.error(
|
|
227
|
+
`An error occured while parsing the content of ${file}:
|
|
228
|
+
${err}`,
|
|
229
|
+
{
|
|
230
|
+
error: err,
|
|
231
|
+
timestamp: true
|
|
232
|
+
}
|
|
233
|
+
);
|
|
234
|
+
return { isValidWidget: false, zoneValue: void 0 };
|
|
235
|
+
}
|
|
236
|
+
let hasDefaultExport = false;
|
|
237
|
+
let hasNamedExport = false;
|
|
238
|
+
let zoneValue;
|
|
239
|
+
try {
|
|
240
|
+
traverse(ast, {
|
|
241
|
+
ExportDefaultDeclaration(path2) {
|
|
242
|
+
hasDefaultExport = validateDefaultExport(path2, ast);
|
|
243
|
+
},
|
|
244
|
+
ExportNamedDeclaration(path2) {
|
|
245
|
+
const { zoneIsValid, zoneValue: value } = validateWidgetConfig(
|
|
246
|
+
path2,
|
|
247
|
+
zone
|
|
248
|
+
);
|
|
249
|
+
hasNamedExport = zoneIsValid;
|
|
250
|
+
zoneValue = value;
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
} catch (err) {
|
|
254
|
+
logger.error(`An error occured while validating the content of ${file}`, {
|
|
255
|
+
error: err,
|
|
256
|
+
timestamp: true
|
|
257
|
+
});
|
|
258
|
+
return { isValidWidget: false, zoneValue: void 0 };
|
|
259
|
+
}
|
|
260
|
+
return { isValidWidget: hasDefaultExport && hasNamedExport, zoneValue };
|
|
261
|
+
}
|
|
262
|
+
async function generateWidgetEntrypoint(zone) {
|
|
263
|
+
const files = (await Promise.all(
|
|
264
|
+
Array.from(_sources).map(
|
|
265
|
+
async (source) => traverseDirectory(`${source}/widgets`)
|
|
266
|
+
)
|
|
267
|
+
)).flat();
|
|
268
|
+
const validatedWidgets = (await Promise.all(
|
|
269
|
+
files.map(async (widget) => {
|
|
270
|
+
const { isValidWidget } = await validateWidget(widget, zone);
|
|
271
|
+
return isValidWidget ? widget : null;
|
|
272
|
+
})
|
|
273
|
+
)).filter(Boolean);
|
|
274
|
+
if (!validatedWidgets.length) {
|
|
275
|
+
const code2 = `export default {
|
|
276
|
+
widgets: [],
|
|
277
|
+
}`;
|
|
278
|
+
return { module: generateModule(code2), paths: [] };
|
|
279
|
+
}
|
|
280
|
+
const importString = validatedWidgets.map((path2, index) => `import WidgetExt${index} from "${path2}";`).join("\n");
|
|
281
|
+
const exportString = `export default {
|
|
282
|
+
widgets: [${validatedWidgets.map((_, index) => `{ Component: WidgetExt${index} }`).join(", ")}],
|
|
283
|
+
}`;
|
|
284
|
+
const code = `${importString}
|
|
285
|
+
${exportString}`;
|
|
286
|
+
return { module: generateModule(code), paths: validatedWidgets };
|
|
287
|
+
}
|
|
288
|
+
function validateRouteConfig(path2, requireLink) {
|
|
289
|
+
const properties = getProperties(path2);
|
|
290
|
+
if (!properties) {
|
|
291
|
+
return false;
|
|
292
|
+
}
|
|
293
|
+
const linkProperty = properties.find(
|
|
294
|
+
(p) => p.type === "ObjectProperty" && p.key.type === "Identifier" && p.key.name === "link"
|
|
295
|
+
);
|
|
296
|
+
if (!linkProperty && !requireLink) {
|
|
297
|
+
return true;
|
|
298
|
+
}
|
|
299
|
+
const linkValue = linkProperty?.value;
|
|
300
|
+
if (!linkValue) {
|
|
301
|
+
return false;
|
|
302
|
+
}
|
|
303
|
+
let labelIsValid = false;
|
|
304
|
+
if (linkValue.properties.some(
|
|
305
|
+
(p) => p.type === "ObjectProperty" && p.key.type === "Identifier" && p.key.name === "label" && p.value.type === "StringLiteral"
|
|
306
|
+
)) {
|
|
307
|
+
labelIsValid = true;
|
|
308
|
+
}
|
|
309
|
+
return labelIsValid;
|
|
310
|
+
}
|
|
311
|
+
async function validateRoute(file, requireLink) {
|
|
312
|
+
const content = await fs.readFile(file, "utf-8");
|
|
313
|
+
const parserOptions = {
|
|
314
|
+
sourceType: "module",
|
|
315
|
+
plugins: ["jsx"]
|
|
316
|
+
};
|
|
317
|
+
if (file.endsWith(".tsx")) {
|
|
318
|
+
parserOptions.plugins?.push("typescript");
|
|
319
|
+
}
|
|
320
|
+
let ast;
|
|
321
|
+
try {
|
|
322
|
+
ast = parse(content, parserOptions);
|
|
323
|
+
} catch (err) {
|
|
324
|
+
logger.error("An error occured while validating a route.", {
|
|
325
|
+
error: err,
|
|
326
|
+
timestamp: true
|
|
327
|
+
});
|
|
328
|
+
return false;
|
|
329
|
+
}
|
|
330
|
+
let hasDefaultExport = false;
|
|
331
|
+
let hasNamedExport = false;
|
|
332
|
+
try {
|
|
333
|
+
traverse(ast, {
|
|
334
|
+
ExportDefaultDeclaration(path2) {
|
|
335
|
+
hasDefaultExport = validateDefaultExport(path2, ast);
|
|
336
|
+
},
|
|
337
|
+
ExportNamedDeclaration(path2) {
|
|
338
|
+
hasNamedExport = validateRouteConfig(path2, requireLink);
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
} catch (err) {
|
|
342
|
+
logger.error("An error occured while validating a route.", {
|
|
343
|
+
error: err,
|
|
344
|
+
timestamp: true
|
|
345
|
+
});
|
|
346
|
+
return false;
|
|
347
|
+
}
|
|
348
|
+
return hasDefaultExport && hasNamedExport;
|
|
349
|
+
}
|
|
350
|
+
function createPath(file) {
|
|
351
|
+
return file.replace(/.*\/admin\/(routes|settings)/, "").replace(/\[([^\]]+)\]/g, ":$1").replace(/\/page\.(tsx|jsx)/, "");
|
|
352
|
+
}
|
|
353
|
+
async function generateRouteEntrypoint(get) {
|
|
354
|
+
const files = (await Promise.all(
|
|
355
|
+
Array.from(_sources).map(
|
|
356
|
+
async (source) => traverseDirectory(`${source}/routes`, "page", { min: 1 })
|
|
357
|
+
)
|
|
358
|
+
)).flat();
|
|
359
|
+
const validatedRoutes = (await Promise.all(
|
|
360
|
+
files.map(async (route) => {
|
|
361
|
+
const isValid = await validateRoute(route, get === "link");
|
|
362
|
+
return isValid ? route : null;
|
|
363
|
+
})
|
|
364
|
+
)).filter(Boolean);
|
|
365
|
+
if (!validatedRoutes.length) {
|
|
366
|
+
const code2 = `export default {
|
|
367
|
+
${get}s: [],
|
|
368
|
+
}`;
|
|
369
|
+
return { module: generateModule(code2), paths: [] };
|
|
370
|
+
}
|
|
371
|
+
const importString = validatedRoutes.map((path2, index) => {
|
|
372
|
+
return get === "page" ? `import RouteExt${index} from "${path2}";` : `import { config as routeConfig${index} } from "${path2}";`;
|
|
373
|
+
}).join("\n");
|
|
374
|
+
const exportString = `export default {
|
|
375
|
+
${get}s: [${validatedRoutes.map((file, index) => {
|
|
376
|
+
return get === "page" ? `{ path: "${createPath(file)}", file: "${file}" }` : `{ path: "${createPath(file)}", ...routeConfig${index}.link }`;
|
|
377
|
+
}).join(", ")}],
|
|
378
|
+
}`;
|
|
379
|
+
const code = `${importString}
|
|
380
|
+
${exportString}`;
|
|
381
|
+
return { module: generateModule(code), paths: validatedRoutes };
|
|
382
|
+
}
|
|
383
|
+
async function validateSetting(file) {
|
|
384
|
+
const content = await fs.readFile(file, "utf-8");
|
|
385
|
+
const parserOptions = {
|
|
386
|
+
sourceType: "module",
|
|
387
|
+
plugins: ["jsx"]
|
|
388
|
+
};
|
|
389
|
+
if (file.endsWith(".tsx")) {
|
|
390
|
+
parserOptions.plugins?.push("typescript");
|
|
391
|
+
}
|
|
392
|
+
let ast;
|
|
393
|
+
try {
|
|
394
|
+
ast = parse(content, parserOptions);
|
|
395
|
+
} catch (err) {
|
|
396
|
+
logger.error("An error occured while validating a setting.", {
|
|
397
|
+
error: err,
|
|
398
|
+
timestamp: true
|
|
399
|
+
});
|
|
400
|
+
return false;
|
|
401
|
+
}
|
|
402
|
+
let hasDefaultExport = false;
|
|
403
|
+
let hasNamedExport = false;
|
|
404
|
+
try {
|
|
405
|
+
traverse(ast, {
|
|
406
|
+
ExportDefaultDeclaration(path2) {
|
|
407
|
+
hasDefaultExport = validateDefaultExport(path2, ast);
|
|
408
|
+
},
|
|
409
|
+
ExportNamedDeclaration(path2) {
|
|
410
|
+
hasNamedExport = validateSettingConfig(path2);
|
|
411
|
+
}
|
|
412
|
+
});
|
|
413
|
+
} catch (err) {
|
|
414
|
+
logger.error("An error occured while validating a setting.", {
|
|
415
|
+
error: err,
|
|
416
|
+
timestamp: true
|
|
417
|
+
});
|
|
418
|
+
return false;
|
|
419
|
+
}
|
|
420
|
+
return hasDefaultExport && hasNamedExport;
|
|
421
|
+
}
|
|
422
|
+
function validateSettingConfig(path2) {
|
|
423
|
+
const properties = getProperties(path2);
|
|
424
|
+
if (!properties) {
|
|
425
|
+
return false;
|
|
426
|
+
}
|
|
427
|
+
const cardProperty = properties.find(
|
|
428
|
+
(p) => p.type === "ObjectProperty" && p.key.type === "Identifier" && p.key.name === "card"
|
|
429
|
+
);
|
|
430
|
+
if (!cardProperty) {
|
|
431
|
+
return false;
|
|
432
|
+
}
|
|
433
|
+
const cardValue = cardProperty.value;
|
|
434
|
+
let hasLabel = false;
|
|
435
|
+
let hasDescription = false;
|
|
436
|
+
if (cardValue.properties.some(
|
|
437
|
+
(p) => p.type === "ObjectProperty" && p.key.type === "Identifier" && p.key.name === "label" && p.value.type === "StringLiteral"
|
|
438
|
+
)) {
|
|
439
|
+
hasLabel = true;
|
|
440
|
+
}
|
|
441
|
+
if (cardValue.properties.some(
|
|
442
|
+
(p) => p.type === "ObjectProperty" && p.key.type === "Identifier" && p.key.name === "description" && p.value.type === "StringLiteral"
|
|
443
|
+
)) {
|
|
444
|
+
hasDescription = true;
|
|
445
|
+
}
|
|
446
|
+
return hasLabel && hasDescription;
|
|
447
|
+
}
|
|
448
|
+
async function generateSettingEntrypoint(get) {
|
|
449
|
+
const files = (await Promise.all(
|
|
450
|
+
Array.from(_sources).map(
|
|
451
|
+
async (source) => traverseDirectory(`${source}/settings`, "page", { min: 1, max: 1 })
|
|
452
|
+
)
|
|
453
|
+
)).flat();
|
|
454
|
+
const validatedSettings = (await Promise.all(
|
|
455
|
+
files.map(async (setting) => {
|
|
456
|
+
const isValid = await validateSetting(setting);
|
|
457
|
+
return isValid ? setting : null;
|
|
458
|
+
})
|
|
459
|
+
)).filter(Boolean);
|
|
460
|
+
if (!validatedSettings.length) {
|
|
461
|
+
const code2 = `export default {
|
|
462
|
+
${get}s: [],
|
|
463
|
+
}`;
|
|
464
|
+
return { module: generateModule(code2), paths: [] };
|
|
465
|
+
}
|
|
466
|
+
const importString = validatedSettings.map((path2, index) => {
|
|
467
|
+
return get === "page" ? `import SettingExt${index} from "${path2}";` : `import { config as settingConfig${index} } from "${path2}";`;
|
|
468
|
+
}).join("\n");
|
|
469
|
+
const exportString = `export default {
|
|
470
|
+
${get}s: [${validatedSettings.map((file, index) => {
|
|
471
|
+
return get === "page" ? `{ path: "${createPath(file)}", file: "${file}" }` : `{ path: "${createPath(file)}", ...settingConfig${index}.card }`;
|
|
472
|
+
}).join(", ")}],
|
|
473
|
+
}`;
|
|
474
|
+
const code = `${importString}
|
|
475
|
+
${exportString}`;
|
|
476
|
+
return { module: generateModule(code), paths: validatedSettings };
|
|
477
|
+
}
|
|
478
|
+
async function loadModule(options) {
|
|
479
|
+
switch (options.type) {
|
|
480
|
+
case "widget": {
|
|
481
|
+
return await generateWidgetEntrypoint(options.get);
|
|
482
|
+
}
|
|
483
|
+
case "route": {
|
|
484
|
+
return await generateRouteEntrypoint(options.get);
|
|
485
|
+
}
|
|
486
|
+
case "setting": {
|
|
487
|
+
return await generateSettingEntrypoint(options.get);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
function getExtensionType(file) {
|
|
492
|
+
const normalizedPath = path.normalize(file);
|
|
493
|
+
if (normalizedPath.includes(path.normalize("/admin/widgets/"))) {
|
|
494
|
+
return "widget";
|
|
495
|
+
} else if (normalizedPath.includes(path.normalize("/admin/routes/"))) {
|
|
496
|
+
return "route";
|
|
497
|
+
} else if (normalizedPath.includes(path.normalize("/admin/settings/"))) {
|
|
498
|
+
return "setting";
|
|
499
|
+
} else {
|
|
500
|
+
return "none";
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
async function handleWidgetChange(file) {
|
|
504
|
+
const { isValidWidget, zoneValue } = await validateWidget(file);
|
|
505
|
+
if (!isValidWidget || !zoneValue) {
|
|
506
|
+
_extensionGraph.delete(file);
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
const zoneValues = Array.isArray(zoneValue) ? zoneValue : [zoneValue];
|
|
510
|
+
for (const zone of zoneValues) {
|
|
511
|
+
const zonePath = zone.replace(/\./g, "/");
|
|
512
|
+
const moduleId = `${VIRTUAL_PREFIX}${WIDGET_MODULE}${zonePath}`;
|
|
513
|
+
const module = server.moduleGraph.getModuleById(moduleId);
|
|
514
|
+
if (module) {
|
|
515
|
+
await server.reloadModule(module);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
async function handleRouteChange(file) {
|
|
520
|
+
const isValidRoute = await validateRoute(file, false);
|
|
521
|
+
if (!isValidRoute) {
|
|
522
|
+
_extensionGraph.delete(file);
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
for (const moduleId of ROUTE_MODULES) {
|
|
526
|
+
const fullModuleId = `${VIRTUAL_PREFIX}${moduleId}`;
|
|
527
|
+
const module = server.moduleGraph.getModuleById(fullModuleId);
|
|
528
|
+
if (module) {
|
|
529
|
+
await server.reloadModule(module);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
async function handleSettingChange(file) {
|
|
534
|
+
const isValidSetting = await validateSetting(file);
|
|
535
|
+
if (!isValidSetting) {
|
|
536
|
+
_extensionGraph.delete(file);
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
for (const moduleId of SETTING_MODULE) {
|
|
540
|
+
const fullModuleId = `${VIRTUAL_PREFIX}${moduleId}`;
|
|
541
|
+
const module = server.moduleGraph.getModuleById(fullModuleId);
|
|
542
|
+
if (module) {
|
|
543
|
+
await server.reloadModule(module);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
async function handleExtensionUnlink(file) {
|
|
548
|
+
const moduleIds = _extensionGraph.get(file);
|
|
549
|
+
if (!moduleIds) {
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
for (const moduleId of moduleIds) {
|
|
553
|
+
const module = server.moduleGraph.getModuleById(moduleId);
|
|
554
|
+
if (module) {
|
|
555
|
+
_extensionGraph.delete(file);
|
|
556
|
+
await server.reloadModule(module);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
async function loadModuleAndUpdateGraph(id, options) {
|
|
561
|
+
const { module, paths } = await loadModule(options);
|
|
562
|
+
for (const path2 of paths) {
|
|
563
|
+
const ids = _extensionGraph.get(path2) || /* @__PURE__ */ new Set();
|
|
564
|
+
ids.add(id);
|
|
565
|
+
_extensionGraph.set(path2, ids);
|
|
566
|
+
}
|
|
567
|
+
return module;
|
|
568
|
+
}
|
|
569
|
+
return {
|
|
570
|
+
name: "@medusajs/admin-vite-plugin",
|
|
571
|
+
configureServer(s) {
|
|
572
|
+
server = s;
|
|
573
|
+
logger = s.config.logger;
|
|
574
|
+
watcher = chokidar.watch(Array.from(_sources), {
|
|
575
|
+
persistent: true,
|
|
576
|
+
ignoreInitial: true
|
|
577
|
+
});
|
|
578
|
+
watcher.on("add", async (file) => {
|
|
579
|
+
const type = getExtensionType(file);
|
|
580
|
+
if (type === "none") {
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
if (type === "widget") {
|
|
584
|
+
await handleWidgetChange(file);
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
if (type === "route") {
|
|
588
|
+
await handleRouteChange(file);
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
if (type === "setting") {
|
|
592
|
+
await handleSettingChange(file);
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
return;
|
|
596
|
+
});
|
|
597
|
+
watcher.on("change", async (file) => {
|
|
598
|
+
const type = getExtensionType(file);
|
|
599
|
+
if (type === "none") {
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
if (type === "widget") {
|
|
603
|
+
await handleWidgetChange(file);
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
606
|
+
if (type === "route") {
|
|
607
|
+
await handleRouteChange(file);
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
if (type === "setting") {
|
|
611
|
+
await handleSettingChange(file);
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
return;
|
|
615
|
+
});
|
|
616
|
+
watcher.on("unlink", async (file) => {
|
|
617
|
+
await handleExtensionUnlink(file);
|
|
618
|
+
return;
|
|
619
|
+
});
|
|
620
|
+
},
|
|
621
|
+
resolveId(id) {
|
|
622
|
+
if (MODULES.includes(id)) {
|
|
623
|
+
return VIRTUAL_PREFIX + id;
|
|
624
|
+
}
|
|
625
|
+
return null;
|
|
626
|
+
},
|
|
627
|
+
async load(id) {
|
|
628
|
+
if (!id.startsWith(VIRTUAL_PREFIX)) {
|
|
629
|
+
return null;
|
|
630
|
+
}
|
|
631
|
+
const idNoPrefix = id.slice(VIRTUAL_PREFIX.length);
|
|
632
|
+
const moduleMap = {
|
|
633
|
+
[ROUTE_PAGE_MODULE]: { type: "route", get: "page" },
|
|
634
|
+
[ROUTE_LINK_MODULE]: { type: "route", get: "link" },
|
|
635
|
+
[SETTING_PAGE_MODULE]: { type: "setting", get: "page" },
|
|
636
|
+
[SETTING_CARD_MODULE]: { type: "setting", get: "card" }
|
|
637
|
+
};
|
|
638
|
+
if (WIDGET_MODULES.includes(idNoPrefix)) {
|
|
639
|
+
const zone = idNoPrefix.replace(WIDGET_MODULE, "").replace(/\//g, ".");
|
|
640
|
+
return loadModuleAndUpdateGraph(id, { type: "widget", get: zone });
|
|
641
|
+
}
|
|
642
|
+
const moduleOptions = moduleMap[idNoPrefix];
|
|
643
|
+
if (moduleOptions) {
|
|
644
|
+
return loadModuleAndUpdateGraph(id, moduleOptions);
|
|
645
|
+
}
|
|
646
|
+
return null;
|
|
647
|
+
},
|
|
648
|
+
async closeBundle() {
|
|
649
|
+
if (watcher) {
|
|
650
|
+
await watcher.close();
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
};
|
|
654
|
+
}
|
|
655
|
+
export {
|
|
656
|
+
inject as default
|
|
657
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@medusajs/admin-vite-plugin",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"main": "dist/index.js",
|
|
5
|
+
"types": "dist/index.d.ts",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"import": "./dist/index.mjs",
|
|
10
|
+
"require": "./dist/index.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsup"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@babel/types": "7.22.5",
|
|
21
|
+
"@medusajs/admin-shared": "0.0.1",
|
|
22
|
+
"@types/babel__traverse": "7.20.5",
|
|
23
|
+
"@types/node": "^20.10.4",
|
|
24
|
+
"tsup": "8.0.1",
|
|
25
|
+
"typescript": "5.3.3",
|
|
26
|
+
"vite": "^5.2.11"
|
|
27
|
+
},
|
|
28
|
+
"peerDependencies": {
|
|
29
|
+
"vite": "^5.0.0"
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"@babel/parser": "7.23.5",
|
|
33
|
+
"@babel/traverse": "7.23.5",
|
|
34
|
+
"chokidar": "3.5.3",
|
|
35
|
+
"fdir": "6.1.1",
|
|
36
|
+
"magic-string": "0.30.5"
|
|
37
|
+
},
|
|
38
|
+
"packageManager": "yarn@3.2.1"
|
|
39
|
+
}
|