@i18n-micro/devtools-ui 1.2.8 → 1.2.15
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/App.ce.vue.d.ts +3 -2
- package/dist/bridge/create-bridge.js +69 -76
- package/dist/bridge/create-bridge.js.map +1 -1
- package/dist/bridge/interface.js +6 -5
- package/dist/bridge/interface.js.map +1 -1
- package/dist/components/config/ConfigCard.vue.d.ts +9 -12
- package/dist/components/config/DiffViewer.vue.d.ts +2 -1
- package/dist/components/config/SettingsCard.vue.d.ts +9 -12
- package/dist/components/data/LocaleTable.vue.d.ts +2 -1
- package/dist/components/data/LocalesList.vue.d.ts +3 -2
- package/dist/components/data/StatItem.vue.d.ts +3 -2
- package/dist/components/data/Statistics.vue.d.ts +2 -16
- package/dist/components/data/TreeItem.vue.d.ts +3 -2
- package/dist/components/editor/TranslationControls.vue.d.ts +3 -2
- package/dist/components/editor/TranslationEditor.vue.d.ts +3 -2
- package/dist/components/layout/ActionBar.vue.d.ts +3 -2
- package/dist/components/layout/SplitPane.vue.d.ts +10 -13
- package/dist/components/ui/Button.vue.d.ts +8 -12
- package/dist/components/ui/Loader.vue.d.ts +3 -2
- package/dist/components/ui/Modal.vue.d.ts +11 -13
- package/dist/components/ui/Pagination.vue.d.ts +3 -2
- package/dist/components/ui/StatusIndicator.vue.d.ts +3 -2
- package/dist/components/ui/Tabs.vue.d.ts +3 -2
- package/dist/components/ui/icons/GlobeIcon.vue.d.ts +2 -1
- package/dist/components/ui/icons/ServerIcon.vue.d.ts +2 -1
- package/dist/components/ui/icons/SettingsIcon.vue.d.ts +2 -1
- package/dist/devtools-ui.css +3 -1
- package/dist/index.es.js +6261 -8230
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +28 -9
- package/dist/index.umd.js.map +1 -1
- package/dist/views/ConfigView.vue.d.ts +2 -1
- package/dist/views/I18nView.vue.d.ts +2 -3
- package/dist/views/SettingsView.vue.d.ts +2 -1
- package/dist/vite/plugin.mjs +211 -254
- package/dist/vite/plugin.mjs.map +1 -1
- package/package.json +14 -12
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
declare const
|
|
1
|
+
declare const __VLS_export: import('vue').DefineComponent<{}, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
|
|
2
|
+
declare const _default: typeof __VLS_export;
|
|
2
3
|
export default _default;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
declare const
|
|
2
|
-
|
|
3
|
-
}, HTMLDivElement>;
|
|
1
|
+
declare const __VLS_export: import('vue').DefineComponent<{}, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
|
|
2
|
+
declare const _default: typeof __VLS_export;
|
|
4
3
|
export default _default;
|
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
declare const
|
|
1
|
+
declare const __VLS_export: import('vue').DefineComponent<{}, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
|
|
2
|
+
declare const _default: typeof __VLS_export;
|
|
2
3
|
export default _default;
|
package/dist/vite/plugin.mjs
CHANGED
|
@@ -1,72 +1,63 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import { readdir } from "node:fs/promises";
|
|
3
3
|
import * as path from "node:path";
|
|
4
|
+
//#region src/safe-path.ts
|
|
4
5
|
function rejectInvalidSymlink(linkPath, originalPath) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
throw new Error(`Access denied: Path ${originalPath} resolves through an invalid symlink`);
|
|
9
|
-
}
|
|
6
|
+
const linkTarget = fs.readlinkSync(linkPath);
|
|
7
|
+
const absoluteTarget = path.isAbsolute(linkTarget) ? linkTarget : path.resolve(path.dirname(linkPath), linkTarget);
|
|
8
|
+
if (!fs.existsSync(absoluteTarget)) throw new Error(`Access denied: Path ${originalPath} resolves through an invalid symlink`);
|
|
10
9
|
}
|
|
10
|
+
/**
|
|
11
|
+
* Resolve the deepest existing ancestor of `p` through `fs.realpathSync` (so
|
|
12
|
+
* symlinks are followed), then re-append the not-yet-existing tail. Lets us
|
|
13
|
+
* validate both existing reads and to-be-created write targets.
|
|
14
|
+
*/
|
|
11
15
|
function realpathAllowingMissing(p) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
tail.unshift(path.basename(current));
|
|
30
|
-
const parent = path.dirname(current);
|
|
31
|
-
if (parent === current) break;
|
|
32
|
-
current = parent;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
const realExisting = fs.realpathSync(current);
|
|
36
|
-
return tail.length > 0 ? path.join(realExisting, ...tail) : realExisting;
|
|
16
|
+
let current = p;
|
|
17
|
+
const tail = [];
|
|
18
|
+
while (true) try {
|
|
19
|
+
if (fs.lstatSync(current).isSymbolicLink()) rejectInvalidSymlink(current, p);
|
|
20
|
+
break;
|
|
21
|
+
} catch (error) {
|
|
22
|
+
if (error instanceof Error && error.message.startsWith("Access denied:")) throw error;
|
|
23
|
+
const code = error instanceof Error && "code" in error ? error.code : void 0;
|
|
24
|
+
if (code !== "ENOENT" && code !== "ENOTDIR") throw error;
|
|
25
|
+
tail.unshift(path.basename(current));
|
|
26
|
+
const parent = path.dirname(current);
|
|
27
|
+
if (parent === current) break;
|
|
28
|
+
current = parent;
|
|
29
|
+
}
|
|
30
|
+
const realExisting = fs.realpathSync(current);
|
|
31
|
+
return tail.length > 0 ? path.join(realExisting, ...tail) : realExisting;
|
|
37
32
|
}
|
|
38
33
|
function safeResolvePath(projectRoot, filePath) {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
47
|
-
return resolvedPath;
|
|
34
|
+
const normalizedFile = filePath.replace(/^\/+/, "").replace(/\/+/g, "/");
|
|
35
|
+
const resolvedPath = path.resolve(projectRoot, normalizedFile);
|
|
36
|
+
const realRoot = fs.realpathSync(path.resolve(projectRoot));
|
|
37
|
+
const realCandidate = realpathAllowingMissing(resolvedPath);
|
|
38
|
+
const rel = path.relative(realRoot, realCandidate);
|
|
39
|
+
if (rel === ".." || rel.startsWith(`..${path.sep}`) || path.isAbsolute(rel)) throw new Error(`Access denied: Path ${resolvedPath} is outside project root`);
|
|
40
|
+
return resolvedPath;
|
|
48
41
|
}
|
|
42
|
+
//#endregion
|
|
43
|
+
//#region vite/plugin.ts
|
|
49
44
|
async function scanTranslationFiles(dir, baseDir) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
} catch (error) {
|
|
65
|
-
console.warn(`[i18n-devtools] Cannot scan directory ${dir}:`, error);
|
|
66
|
-
}
|
|
67
|
-
return files;
|
|
45
|
+
const files = [];
|
|
46
|
+
try {
|
|
47
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
48
|
+
const subdirFiles = await Promise.all(entries.filter((entry) => entry.isDirectory()).map((entry) => scanTranslationFiles(path.join(dir, entry.name), baseDir)));
|
|
49
|
+
files.push(...subdirFiles.flat());
|
|
50
|
+
for (const entry of entries) if (entry.isFile() && entry.name.endsWith(".json")) {
|
|
51
|
+
const fullPath = path.join(dir, entry.name);
|
|
52
|
+
const relativePath = path.relative(baseDir, fullPath);
|
|
53
|
+
files.push(relativePath.replace(/\\/g, "/"));
|
|
54
|
+
}
|
|
55
|
+
} catch (error) {
|
|
56
|
+
console.warn(`[i18n-devtools] Cannot scan directory ${dir}:`, error);
|
|
57
|
+
}
|
|
58
|
+
return files;
|
|
68
59
|
}
|
|
69
|
-
|
|
60
|
+
var BUTTON_INJECTION_SCRIPT = `
|
|
70
61
|
(function() {
|
|
71
62
|
if (typeof window === 'undefined' || document.getElementById('i18n-devtools-button-container')) {
|
|
72
63
|
return;
|
|
@@ -219,38 +210,29 @@ const BUTTON_INJECTION_SCRIPT = `
|
|
|
219
210
|
})();
|
|
220
211
|
`;
|
|
221
212
|
function i18nDevToolsPlugin(options = {}) {
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
const scriptTag = `<script>${BUTTON_INJECTION_SCRIPT}<\/script>`;
|
|
246
|
-
if (html.includes("</body>")) {
|
|
247
|
-
return html.replace("</body>", `${scriptTag}</body>`);
|
|
248
|
-
}
|
|
249
|
-
return html + scriptTag;
|
|
250
|
-
},
|
|
251
|
-
configureServer(server) {
|
|
252
|
-
const projectRoot = server.config.root;
|
|
253
|
-
const devtoolsHtml = `<!DOCTYPE html>
|
|
213
|
+
const apiBase = options.base || "/__i18n_api";
|
|
214
|
+
const translationDir = options.translationDir || "src/locales";
|
|
215
|
+
const injectButton = options.injectButton !== false;
|
|
216
|
+
return {
|
|
217
|
+
name: "i18n-micro-devtools-plugin",
|
|
218
|
+
apply: "serve",
|
|
219
|
+
resolveId(id) {
|
|
220
|
+
if (id === "/@vite-plugin-i18n-devtools/devtools-ui.js") return id;
|
|
221
|
+
return null;
|
|
222
|
+
},
|
|
223
|
+
load(id) {
|
|
224
|
+
if (id === "/@vite-plugin-i18n-devtools/devtools-ui.js") return `export * from '@i18n-micro/devtools-ui'`;
|
|
225
|
+
return null;
|
|
226
|
+
},
|
|
227
|
+
transformIndexHtml(html) {
|
|
228
|
+
if (!injectButton) return html;
|
|
229
|
+
const scriptTag = `<script>${BUTTON_INJECTION_SCRIPT}<\/script>`;
|
|
230
|
+
if (html.includes("</body>")) return html.replace("</body>", `${scriptTag}</body>`);
|
|
231
|
+
return html + scriptTag;
|
|
232
|
+
},
|
|
233
|
+
configureServer(server) {
|
|
234
|
+
const projectRoot = server.config.root;
|
|
235
|
+
const devtoolsHtml = `<!DOCTYPE html>
|
|
254
236
|
<html lang="en">
|
|
255
237
|
<head>
|
|
256
238
|
<meta charset="UTF-8">
|
|
@@ -384,168 +366,143 @@ function i18nDevToolsPlugin(options = {}) {
|
|
|
384
366
|
<\/script>
|
|
385
367
|
</body>
|
|
386
368
|
</html>`;
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
if (!filePath.endsWith(".json")) {
|
|
523
|
-
throw new Error("Invalid file: only .json files are allowed");
|
|
524
|
-
}
|
|
525
|
-
const dir = path.dirname(filePath);
|
|
526
|
-
if (!fs.existsSync(dir)) {
|
|
527
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
528
|
-
}
|
|
529
|
-
fs.writeFileSync(filePath, JSON.stringify(content, null, 2), "utf-8");
|
|
530
|
-
res.statusCode = 200;
|
|
531
|
-
res.setHeader("Content-Type", "application/json");
|
|
532
|
-
res.end(JSON.stringify({ success: true }));
|
|
533
|
-
} catch (e) {
|
|
534
|
-
console.error("[i18n-devtools] Save error:", e);
|
|
535
|
-
res.statusCode = 500;
|
|
536
|
-
res.setHeader("Content-Type", "application/json");
|
|
537
|
-
res.end(
|
|
538
|
-
JSON.stringify({
|
|
539
|
-
success: false,
|
|
540
|
-
error: e instanceof Error ? e.message : String(e)
|
|
541
|
-
})
|
|
542
|
-
);
|
|
543
|
-
}
|
|
544
|
-
});
|
|
545
|
-
}
|
|
546
|
-
};
|
|
369
|
+
server.middlewares.use("/__i18n_devtools.html", (_req, res) => {
|
|
370
|
+
res.statusCode = 200;
|
|
371
|
+
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
372
|
+
res.end(devtoolsHtml);
|
|
373
|
+
});
|
|
374
|
+
server.middlewares.use(`${apiBase}/config`, async (_req, res, next) => {
|
|
375
|
+
if (_req.method !== "GET") return next();
|
|
376
|
+
try {
|
|
377
|
+
res.statusCode = 200;
|
|
378
|
+
res.setHeader("Content-Type", "application/json");
|
|
379
|
+
res.end(JSON.stringify({
|
|
380
|
+
defaultLocale: "en",
|
|
381
|
+
fallbackLocale: "en",
|
|
382
|
+
locales: [],
|
|
383
|
+
translationDir
|
|
384
|
+
}));
|
|
385
|
+
} catch (e) {
|
|
386
|
+
console.error("[i18n-devtools] Config error:", e);
|
|
387
|
+
res.statusCode = 500;
|
|
388
|
+
res.setHeader("Content-Type", "application/json");
|
|
389
|
+
res.end(JSON.stringify({
|
|
390
|
+
success: false,
|
|
391
|
+
error: e instanceof Error ? e.message : String(e)
|
|
392
|
+
}));
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
server.middlewares.use(`${apiBase}/files`, async (req, res, next) => {
|
|
396
|
+
if (req.method !== "GET") return next();
|
|
397
|
+
try {
|
|
398
|
+
const localesPath = path.resolve(projectRoot, translationDir);
|
|
399
|
+
safeResolvePath(projectRoot, translationDir);
|
|
400
|
+
if (!fs.existsSync(localesPath)) {
|
|
401
|
+
res.statusCode = 200;
|
|
402
|
+
res.setHeader("Content-Type", "application/json");
|
|
403
|
+
res.end(JSON.stringify({
|
|
404
|
+
files: [],
|
|
405
|
+
structure: {}
|
|
406
|
+
}));
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
const files = await scanTranslationFiles(localesPath, localesPath);
|
|
410
|
+
const structure = {};
|
|
411
|
+
for (const file of files) {
|
|
412
|
+
const parts = file.split("/");
|
|
413
|
+
let current = structure;
|
|
414
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
415
|
+
const part = parts[i];
|
|
416
|
+
if (!current[part]) current[part] = {};
|
|
417
|
+
current = current[part];
|
|
418
|
+
}
|
|
419
|
+
const last = parts[parts.length - 1];
|
|
420
|
+
if (last !== void 0) current[last] = file;
|
|
421
|
+
}
|
|
422
|
+
res.statusCode = 200;
|
|
423
|
+
res.setHeader("Content-Type", "application/json");
|
|
424
|
+
res.end(JSON.stringify({
|
|
425
|
+
files,
|
|
426
|
+
structure
|
|
427
|
+
}));
|
|
428
|
+
} catch (e) {
|
|
429
|
+
console.error("[i18n-devtools] Files list error:", e);
|
|
430
|
+
res.statusCode = 500;
|
|
431
|
+
res.setHeader("Content-Type", "application/json");
|
|
432
|
+
res.end(JSON.stringify({
|
|
433
|
+
success: false,
|
|
434
|
+
error: e instanceof Error ? e.message : String(e)
|
|
435
|
+
}));
|
|
436
|
+
}
|
|
437
|
+
});
|
|
438
|
+
server.middlewares.use(`${apiBase}/file`, async (req, res, next) => {
|
|
439
|
+
if (req.method !== "GET") return next();
|
|
440
|
+
try {
|
|
441
|
+
const filePath = new URL(req.url || "", `http://${req.headers.host}`).searchParams.get("path");
|
|
442
|
+
if (!filePath) throw new Error("Path parameter is required");
|
|
443
|
+
const fullPath = path.join(translationDir, filePath);
|
|
444
|
+
const resolvedPath = safeResolvePath(projectRoot, fullPath);
|
|
445
|
+
if (!fs.existsSync(resolvedPath)) {
|
|
446
|
+
res.statusCode = 404;
|
|
447
|
+
res.setHeader("Content-Type", "application/json");
|
|
448
|
+
res.end(JSON.stringify({
|
|
449
|
+
success: false,
|
|
450
|
+
error: "File not found"
|
|
451
|
+
}));
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
if (!resolvedPath.endsWith(".json")) throw new Error("Invalid file: only .json files are allowed");
|
|
455
|
+
const content = fs.readFileSync(resolvedPath, "utf-8");
|
|
456
|
+
const parsed = JSON.parse(content);
|
|
457
|
+
res.statusCode = 200;
|
|
458
|
+
res.setHeader("Content-Type", "application/json");
|
|
459
|
+
res.end(JSON.stringify({
|
|
460
|
+
success: true,
|
|
461
|
+
content: parsed,
|
|
462
|
+
path: filePath
|
|
463
|
+
}));
|
|
464
|
+
} catch (e) {
|
|
465
|
+
console.error("[i18n-devtools] File read error:", e);
|
|
466
|
+
res.statusCode = 500;
|
|
467
|
+
res.setHeader("Content-Type", "application/json");
|
|
468
|
+
res.end(JSON.stringify({
|
|
469
|
+
success: false,
|
|
470
|
+
error: e instanceof Error ? e.message : String(e)
|
|
471
|
+
}));
|
|
472
|
+
}
|
|
473
|
+
});
|
|
474
|
+
server.middlewares.use(`${apiBase}/save`, async (req, res, next) => {
|
|
475
|
+
if (req.method !== "POST") return next();
|
|
476
|
+
try {
|
|
477
|
+
const buffers = [];
|
|
478
|
+
for await (const chunk of req) buffers.push(chunk);
|
|
479
|
+
const bodyData = Buffer.concat(buffers).toString();
|
|
480
|
+
if (!bodyData) throw new Error("Empty request body");
|
|
481
|
+
const { file, content } = JSON.parse(bodyData);
|
|
482
|
+
if (!file || !content) throw new Error("Invalid data: file and content are required");
|
|
483
|
+
const fullPath = file.startsWith(translationDir) ? file : path.join(translationDir, file);
|
|
484
|
+
const filePath = safeResolvePath(projectRoot, fullPath);
|
|
485
|
+
if (!filePath.endsWith(".json")) throw new Error("Invalid file: only .json files are allowed");
|
|
486
|
+
const dir = path.dirname(filePath);
|
|
487
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
488
|
+
fs.writeFileSync(filePath, JSON.stringify(content, null, 2), "utf-8");
|
|
489
|
+
res.statusCode = 200;
|
|
490
|
+
res.setHeader("Content-Type", "application/json");
|
|
491
|
+
res.end(JSON.stringify({ success: true }));
|
|
492
|
+
} catch (e) {
|
|
493
|
+
console.error("[i18n-devtools] Save error:", e);
|
|
494
|
+
res.statusCode = 500;
|
|
495
|
+
res.setHeader("Content-Type", "application/json");
|
|
496
|
+
res.end(JSON.stringify({
|
|
497
|
+
success: false,
|
|
498
|
+
error: e instanceof Error ? e.message : String(e)
|
|
499
|
+
}));
|
|
500
|
+
}
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
};
|
|
547
504
|
}
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
//# sourceMappingURL=plugin.mjs.map
|
|
505
|
+
//#endregion
|
|
506
|
+
export { i18nDevToolsPlugin };
|
|
507
|
+
|
|
508
|
+
//# sourceMappingURL=plugin.mjs.map
|