scribe-cms 0.0.10 → 0.0.12
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/cli/index.cjs +193 -111
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +191 -111
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +188 -110
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +186 -110
- package/dist/index.js.map +1 -1
- package/dist/runtime.cjs +118 -1
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.js +116 -1
- package/dist/runtime.js.map +1 -1
- package/dist/src/core/types.d.ts +0 -2
- package/dist/src/core/types.d.ts.map +1 -1
- package/dist/src/loader/create-loader.d.ts.map +1 -1
- package/dist/src/translate/gemini-client.d.ts +5 -0
- package/dist/src/translate/gemini-client.d.ts.map +1 -1
- package/dist/src/translate/normalize-mdx-body.d.ts +9 -0
- package/dist/src/translate/normalize-mdx-body.d.ts.map +1 -0
- package/dist/src/translate/resolve-translate-config.d.ts.map +1 -1
- package/dist/src/translate/validate-mdx-body.d.ts +16 -0
- package/dist/src/translate/validate-mdx-body.d.ts.map +1 -0
- package/dist/src/validate/validate-project.d.ts.map +1 -1
- package/dist/studio/server.cjs +6 -0
- package/dist/studio/server.cjs.map +1 -1
- package/dist/studio/server.js +4 -0
- package/dist/studio/server.js.map +1 -1
- package/package.json +5 -2
package/dist/cli/index.cjs
CHANGED
|
@@ -6,6 +6,9 @@ var path3 = require('path');
|
|
|
6
6
|
var Database = require('better-sqlite3');
|
|
7
7
|
var zod = require('zod');
|
|
8
8
|
var matter = require('gray-matter');
|
|
9
|
+
var remarkMdx = require('remark-mdx');
|
|
10
|
+
var remarkParse = require('remark-parse');
|
|
11
|
+
var unified = require('unified');
|
|
9
12
|
var jiti = require('jiti');
|
|
10
13
|
var crypto = require('crypto');
|
|
11
14
|
var genai = require('@google/genai');
|
|
@@ -22,6 +25,8 @@ var fs2__default = /*#__PURE__*/_interopDefault(fs2);
|
|
|
22
25
|
var path3__default = /*#__PURE__*/_interopDefault(path3);
|
|
23
26
|
var Database__default = /*#__PURE__*/_interopDefault(Database);
|
|
24
27
|
var matter__default = /*#__PURE__*/_interopDefault(matter);
|
|
28
|
+
var remarkMdx__default = /*#__PURE__*/_interopDefault(remarkMdx);
|
|
29
|
+
var remarkParse__default = /*#__PURE__*/_interopDefault(remarkParse);
|
|
25
30
|
var dotenv__default = /*#__PURE__*/_interopDefault(dotenv);
|
|
26
31
|
|
|
27
32
|
var __defProp = Object.defineProperty;
|
|
@@ -727,6 +732,140 @@ function bulkLoadTranslations(db) {
|
|
|
727
732
|
return db.prepare(`SELECT * FROM translations ORDER BY content_type, en_slug, locale`).all();
|
|
728
733
|
}
|
|
729
734
|
|
|
735
|
+
// src/translate/validate-mdx-body.ts
|
|
736
|
+
init_cjs_shims();
|
|
737
|
+
|
|
738
|
+
// src/translate/normalize-mdx-body.ts
|
|
739
|
+
init_cjs_shims();
|
|
740
|
+
function needsMdxEscapeNormalization(body) {
|
|
741
|
+
return /\\n[ \t/>]|<[\w.-][^>]*\\n/.test(body);
|
|
742
|
+
}
|
|
743
|
+
function normalizeTranslatedMdxBody(body) {
|
|
744
|
+
if (!needsMdxEscapeNormalization(body)) {
|
|
745
|
+
return { body, adjusted: false };
|
|
746
|
+
}
|
|
747
|
+
const normalized = body.replace(/\\r\\n/g, "\n").replace(/\\n/g, "\n").replace(/\\t/g, " ").replace(/\\"/g, '"').replace(/\\\\/g, "\\");
|
|
748
|
+
return { body: normalized, adjusted: normalized !== body };
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
// src/translate/sanitize-mdx-jsx.ts
|
|
752
|
+
init_cjs_shims();
|
|
753
|
+
function sanitizeMdxJsxAttributeQuotes(body) {
|
|
754
|
+
let adjusted = false;
|
|
755
|
+
let out = "";
|
|
756
|
+
let i = 0;
|
|
757
|
+
while (i < body.length) {
|
|
758
|
+
if (body[i] !== "<" || !/[\w.]/.test(body[i + 1] ?? "")) {
|
|
759
|
+
out += body[i];
|
|
760
|
+
i += 1;
|
|
761
|
+
continue;
|
|
762
|
+
}
|
|
763
|
+
const tagStart = i;
|
|
764
|
+
i += 1;
|
|
765
|
+
while (i < body.length && /[\w.-]/.test(body[i] ?? "")) i += 1;
|
|
766
|
+
const tagName = body.slice(tagStart + 1, i);
|
|
767
|
+
let tagOut = `<${tagName}`;
|
|
768
|
+
let tagAdjusted = false;
|
|
769
|
+
while (i < body.length && body[i] !== ">" && body[i] !== "/") {
|
|
770
|
+
while (i < body.length && /\s/.test(body[i] ?? "")) {
|
|
771
|
+
tagOut += body[i];
|
|
772
|
+
i += 1;
|
|
773
|
+
}
|
|
774
|
+
if (i >= body.length || body[i] === ">" || body[i] === "/") break;
|
|
775
|
+
const attrStart = i;
|
|
776
|
+
while (i < body.length && /[\w:.-]/.test(body[i] ?? "")) i += 1;
|
|
777
|
+
body.slice(attrStart, i);
|
|
778
|
+
while (i < body.length && /\s/.test(body[i] ?? "")) i += 1;
|
|
779
|
+
if (body[i] !== "=") {
|
|
780
|
+
tagOut += body.slice(attrStart, i);
|
|
781
|
+
continue;
|
|
782
|
+
}
|
|
783
|
+
tagOut += body.slice(attrStart, i);
|
|
784
|
+
tagOut += "=";
|
|
785
|
+
i += 1;
|
|
786
|
+
while (i < body.length && /\s/.test(body[i] ?? "")) i += 1;
|
|
787
|
+
const quote = body[i];
|
|
788
|
+
if (quote !== '"' && quote !== "'") {
|
|
789
|
+
continue;
|
|
790
|
+
}
|
|
791
|
+
if (quote === "'") {
|
|
792
|
+
const valStart2 = i + 1;
|
|
793
|
+
i += 1;
|
|
794
|
+
while (i < body.length) {
|
|
795
|
+
if (body[i] === "\\") {
|
|
796
|
+
i += 2;
|
|
797
|
+
continue;
|
|
798
|
+
}
|
|
799
|
+
if (body[i] === "'") break;
|
|
800
|
+
i += 1;
|
|
801
|
+
}
|
|
802
|
+
tagOut += body.slice(valStart2 - 1, i + 1);
|
|
803
|
+
i += 1;
|
|
804
|
+
continue;
|
|
805
|
+
}
|
|
806
|
+
const valStart = i + 1;
|
|
807
|
+
i += 1;
|
|
808
|
+
let closeIdx = -1;
|
|
809
|
+
for (let scan = valStart; scan < body.length; scan += 1) {
|
|
810
|
+
if (body[scan] !== '"') continue;
|
|
811
|
+
let j = scan + 1;
|
|
812
|
+
while (j < body.length && /\s/.test(body[j] ?? "")) j += 1;
|
|
813
|
+
if (j < body.length && (body[j] === ">" || body[j] === "/" || /[a-zA-Z]/.test(body[j] ?? ""))) {
|
|
814
|
+
closeIdx = scan;
|
|
815
|
+
break;
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
if (closeIdx === -1) {
|
|
819
|
+
tagOut += body.slice(valStart - 1, i);
|
|
820
|
+
break;
|
|
821
|
+
}
|
|
822
|
+
const value = body.slice(valStart, closeIdx);
|
|
823
|
+
const hasInternalQuote = value.includes('"');
|
|
824
|
+
if (hasInternalQuote) {
|
|
825
|
+
const escaped = value.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
826
|
+
tagOut += `'${escaped}'`;
|
|
827
|
+
tagAdjusted = true;
|
|
828
|
+
} else {
|
|
829
|
+
tagOut += `"${value}"`;
|
|
830
|
+
}
|
|
831
|
+
i = closeIdx + 1;
|
|
832
|
+
}
|
|
833
|
+
if (tagAdjusted) adjusted = true;
|
|
834
|
+
tagOut += body[i] ?? "";
|
|
835
|
+
out += tagOut;
|
|
836
|
+
i += 1;
|
|
837
|
+
}
|
|
838
|
+
return { body: out, adjusted };
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
// src/translate/validate-mdx-body.ts
|
|
842
|
+
var parser = unified.unified().use(remarkParse__default.default).use(remarkMdx__default.default);
|
|
843
|
+
function prepareTranslatedMdxBody(body) {
|
|
844
|
+
const normalized = normalizeTranslatedMdxBody(body);
|
|
845
|
+
const sanitized = sanitizeMdxJsxAttributeQuotes(normalized.body);
|
|
846
|
+
return {
|
|
847
|
+
body: sanitized.body,
|
|
848
|
+
adjusted: normalized.adjusted || sanitized.adjusted
|
|
849
|
+
};
|
|
850
|
+
}
|
|
851
|
+
function validateMdxBody(body) {
|
|
852
|
+
try {
|
|
853
|
+
parser.parse(body);
|
|
854
|
+
return { ok: true };
|
|
855
|
+
} catch (error) {
|
|
856
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
857
|
+
return { ok: false, error: message };
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
function assertValidTranslatedMdxBody(body) {
|
|
861
|
+
const prepared = prepareTranslatedMdxBody(body);
|
|
862
|
+
const validated = validateMdxBody(prepared.body);
|
|
863
|
+
if (!validated.ok) {
|
|
864
|
+
throw new Error(`MDX validation failed: ${validated.error}`);
|
|
865
|
+
}
|
|
866
|
+
return prepared;
|
|
867
|
+
}
|
|
868
|
+
|
|
730
869
|
// src/loader/normalize-en.ts
|
|
731
870
|
init_cjs_shims();
|
|
732
871
|
function normalizeEnFrontmatter(data) {
|
|
@@ -848,7 +987,7 @@ function buildDocumentFromTranslation(row, enDoc, type, config) {
|
|
|
848
987
|
noindex: seo.noindex,
|
|
849
988
|
canonicalPathOverride: seo.canonicalPathOverride,
|
|
850
989
|
frontmatter,
|
|
851
|
-
content: row.body
|
|
990
|
+
content: prepareTranslatedMdxBody(row.body).body
|
|
852
991
|
};
|
|
853
992
|
}
|
|
854
993
|
var DEV_REVALIDATE_MS = 1500;
|
|
@@ -1767,6 +1906,20 @@ function listEnSlugs3(rootDir, contentDir) {
|
|
|
1767
1906
|
if (!fs2__default.default.existsSync(dir)) return [];
|
|
1768
1907
|
return fs2__default.default.readdirSync(dir).filter(isPublishableContentFile).map((f) => f.replace(/\.(md|mdx)$/, ""));
|
|
1769
1908
|
}
|
|
1909
|
+
function validateDocumentMdxBody(issues, input) {
|
|
1910
|
+
const preparedBody = prepareTranslatedMdxBody(input.body).body;
|
|
1911
|
+
const mdxValidation = validateMdxBody(preparedBody);
|
|
1912
|
+
if (!mdxValidation.ok) {
|
|
1913
|
+
issues.push({
|
|
1914
|
+
level: "error",
|
|
1915
|
+
contentType: input.contentType,
|
|
1916
|
+
enSlug: input.enSlug,
|
|
1917
|
+
locale: input.locale,
|
|
1918
|
+
field: "body",
|
|
1919
|
+
message: `Invalid MDX: ${mdxValidation.error}`
|
|
1920
|
+
});
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1770
1923
|
function validateProject(config) {
|
|
1771
1924
|
const issues = [];
|
|
1772
1925
|
const project = createProject(config);
|
|
@@ -1825,6 +1978,12 @@ function validateProject(config) {
|
|
|
1825
1978
|
})) {
|
|
1826
1979
|
issues.push(issue);
|
|
1827
1980
|
}
|
|
1981
|
+
validateDocumentMdxBody(issues, {
|
|
1982
|
+
contentType: type.id,
|
|
1983
|
+
enSlug,
|
|
1984
|
+
locale: config.defaultLocale,
|
|
1985
|
+
body: enDoc.content
|
|
1986
|
+
});
|
|
1828
1987
|
for (const locale of config.locales) {
|
|
1829
1988
|
if (locale === config.defaultLocale) continue;
|
|
1830
1989
|
const row = getTranslation(db, type.id, enSlug, locale);
|
|
@@ -1840,15 +1999,22 @@ function validateProject(config) {
|
|
|
1840
1999
|
message: issue.message
|
|
1841
2000
|
});
|
|
1842
2001
|
}
|
|
2002
|
+
const preparedBody = prepareTranslatedMdxBody(row.body).body;
|
|
1843
2003
|
for (const issue of validateDocumentAssets(config, {
|
|
1844
2004
|
contentType: type.id,
|
|
1845
2005
|
enSlug,
|
|
1846
2006
|
locale,
|
|
1847
2007
|
frontmatter: localeFm,
|
|
1848
|
-
body:
|
|
2008
|
+
body: preparedBody
|
|
1849
2009
|
})) {
|
|
1850
2010
|
issues.push(issue);
|
|
1851
2011
|
}
|
|
2012
|
+
validateDocumentMdxBody(issues, {
|
|
2013
|
+
contentType: type.id,
|
|
2014
|
+
enSlug,
|
|
2015
|
+
locale,
|
|
2016
|
+
body: row.body
|
|
2017
|
+
});
|
|
1852
2018
|
}
|
|
1853
2019
|
}
|
|
1854
2020
|
try {
|
|
@@ -2016,12 +2182,20 @@ function normalizeGeminiDisplayName(model) {
|
|
|
2016
2182
|
// src/translate/gemini-client.ts
|
|
2017
2183
|
var DEFAULT_MODEL = DEFAULT_GEMINI_MODEL;
|
|
2018
2184
|
function extractJson(text) {
|
|
2019
|
-
const
|
|
2185
|
+
const trimmed = text.trim();
|
|
2186
|
+
const fenced = trimmed.match(/^```(?:json)?\s*\n?([\s\S]*?)\n?```$/);
|
|
2020
2187
|
if (fenced?.[1]) return fenced[1].trim();
|
|
2021
|
-
const start =
|
|
2022
|
-
const end =
|
|
2023
|
-
if (start >= 0 && end > start) return
|
|
2024
|
-
return
|
|
2188
|
+
const start = trimmed.indexOf("{");
|
|
2189
|
+
const end = trimmed.lastIndexOf("}");
|
|
2190
|
+
if (start >= 0 && end > start) return trimmed.slice(start, end + 1);
|
|
2191
|
+
return trimmed;
|
|
2192
|
+
}
|
|
2193
|
+
function parseGeminiResponse(text) {
|
|
2194
|
+
try {
|
|
2195
|
+
return JSON.parse(text.trim());
|
|
2196
|
+
} catch {
|
|
2197
|
+
return JSON.parse(extractJson(text));
|
|
2198
|
+
}
|
|
2025
2199
|
}
|
|
2026
2200
|
async function translatePageWithGemini(input) {
|
|
2027
2201
|
const apiKey = input.apiKey ?? process.env.GEMINI_API_KEY;
|
|
@@ -2042,7 +2216,7 @@ async function translatePageWithGemini(input) {
|
|
|
2042
2216
|
}
|
|
2043
2217
|
});
|
|
2044
2218
|
const raw = response.text ?? "";
|
|
2045
|
-
const parsed =
|
|
2219
|
+
const parsed = parseGeminiResponse(raw);
|
|
2046
2220
|
if (!parsed.frontmatter || typeof parsed.body !== "string") {
|
|
2047
2221
|
throw new Error("Gemini response missing frontmatter/body");
|
|
2048
2222
|
}
|
|
@@ -2216,24 +2390,21 @@ function buildGeminiResponseSchema(schema, slugStrategy) {
|
|
|
2216
2390
|
|
|
2217
2391
|
// src/translate/resolve-translate-config.ts
|
|
2218
2392
|
init_cjs_shims();
|
|
2219
|
-
|
|
2393
|
+
var BUILTIN_TRANSLATE_RULES = [
|
|
2394
|
+
"Do not translate brand or product names unless the brand has a well-known localized name in the target market (rare). Keep the original spelling and capitalization.",
|
|
2395
|
+
"Return the MDX body with real line breaks; do not use JSON escape sequences like \\n or \\t in the body string.",
|
|
2396
|
+
'In JSX attributes (e.g. FaqItem question="..."), use single quotes when the value contains double-quote characters (e.g. Hebrew \u05D3\u05D5\u05D0"\u05DC), or escape them as \\".'
|
|
2397
|
+
];
|
|
2398
|
+
function slugStrategyRules(slugStrategy) {
|
|
2220
2399
|
if (slugStrategy === "localized") {
|
|
2221
|
-
|
|
2400
|
+
return [
|
|
2222
2401
|
"Provide a URL slug in JSON field `slug` that is Localized into the target language.",
|
|
2223
2402
|
"Base the slug on the meaning of the translated title \u2014 do NOT reuse the English slug words.",
|
|
2224
2403
|
"Slug MUST be ASCII only: a-z, 0-9, hyphens. No uppercase, accents, underscores, or spaces.",
|
|
2225
2404
|
"For non-Latin languages, write the words in the target language and transliterate them into Latin script (e.g. Russian -> romanized Russian, not English).",
|
|
2226
2405
|
"Do NOT append locale codes to the slug (e.g. -fr, -he, -zh-cn). Locale routing is handled by the URL prefix, not the slug.",
|
|
2227
|
-
|
|
2406
|
+
"Preserve 4-digit years and proper nouns in slugs."
|
|
2228
2407
|
];
|
|
2229
|
-
if (preserveTerms?.length) {
|
|
2230
|
-
rules.push(
|
|
2231
|
-
`Preserve these terms verbatim in slugs (lowercase): ${preserveTerms.join(", ")}.`
|
|
2232
|
-
);
|
|
2233
|
-
} else {
|
|
2234
|
-
rules.push("Preserve 4-digit years and proper nouns in slugs.");
|
|
2235
|
-
}
|
|
2236
|
-
return rules;
|
|
2237
2408
|
}
|
|
2238
2409
|
return ["Do NOT output a slug \u2014 slugStrategy is fixed."];
|
|
2239
2410
|
}
|
|
@@ -2246,9 +2417,10 @@ function mergeTranslateConfig(project, type, slugStrategy) {
|
|
|
2246
2417
|
promptOverride: type?.prompt ?? project?.prompt,
|
|
2247
2418
|
context: mergeContext(project, type),
|
|
2248
2419
|
rules: [
|
|
2420
|
+
...BUILTIN_TRANSLATE_RULES,
|
|
2249
2421
|
...project?.rules ?? [],
|
|
2250
2422
|
...type?.rules ?? [],
|
|
2251
|
-
...slugStrategyRules(slugStrategy
|
|
2423
|
+
...slugStrategyRules(slugStrategy)
|
|
2252
2424
|
],
|
|
2253
2425
|
model: type?.model ?? project?.defaultModel
|
|
2254
2426
|
};
|
|
@@ -2257,96 +2429,6 @@ function resolveTranslateConfig(config, type) {
|
|
|
2257
2429
|
return mergeTranslateConfig(config.translate, type.translate, type.slugStrategy);
|
|
2258
2430
|
}
|
|
2259
2431
|
|
|
2260
|
-
// src/translate/sanitize-mdx-jsx.ts
|
|
2261
|
-
init_cjs_shims();
|
|
2262
|
-
function sanitizeMdxJsxAttributeQuotes(body) {
|
|
2263
|
-
let adjusted = false;
|
|
2264
|
-
let out = "";
|
|
2265
|
-
let i = 0;
|
|
2266
|
-
while (i < body.length) {
|
|
2267
|
-
if (body[i] !== "<" || !/[\w.]/.test(body[i + 1] ?? "")) {
|
|
2268
|
-
out += body[i];
|
|
2269
|
-
i += 1;
|
|
2270
|
-
continue;
|
|
2271
|
-
}
|
|
2272
|
-
const tagStart = i;
|
|
2273
|
-
i += 1;
|
|
2274
|
-
while (i < body.length && /[\w.-]/.test(body[i] ?? "")) i += 1;
|
|
2275
|
-
const tagName = body.slice(tagStart + 1, i);
|
|
2276
|
-
let tagOut = `<${tagName}`;
|
|
2277
|
-
let tagAdjusted = false;
|
|
2278
|
-
while (i < body.length && body[i] !== ">" && body[i] !== "/") {
|
|
2279
|
-
while (i < body.length && /\s/.test(body[i] ?? "")) {
|
|
2280
|
-
tagOut += body[i];
|
|
2281
|
-
i += 1;
|
|
2282
|
-
}
|
|
2283
|
-
if (i >= body.length || body[i] === ">" || body[i] === "/") break;
|
|
2284
|
-
const attrStart = i;
|
|
2285
|
-
while (i < body.length && /[\w:.-]/.test(body[i] ?? "")) i += 1;
|
|
2286
|
-
body.slice(attrStart, i);
|
|
2287
|
-
while (i < body.length && /\s/.test(body[i] ?? "")) i += 1;
|
|
2288
|
-
if (body[i] !== "=") {
|
|
2289
|
-
tagOut += body.slice(attrStart, i);
|
|
2290
|
-
continue;
|
|
2291
|
-
}
|
|
2292
|
-
tagOut += body.slice(attrStart, i);
|
|
2293
|
-
tagOut += "=";
|
|
2294
|
-
i += 1;
|
|
2295
|
-
while (i < body.length && /\s/.test(body[i] ?? "")) i += 1;
|
|
2296
|
-
const quote = body[i];
|
|
2297
|
-
if (quote !== '"' && quote !== "'") {
|
|
2298
|
-
continue;
|
|
2299
|
-
}
|
|
2300
|
-
if (quote === "'") {
|
|
2301
|
-
const valStart2 = i + 1;
|
|
2302
|
-
i += 1;
|
|
2303
|
-
while (i < body.length) {
|
|
2304
|
-
if (body[i] === "\\") {
|
|
2305
|
-
i += 2;
|
|
2306
|
-
continue;
|
|
2307
|
-
}
|
|
2308
|
-
if (body[i] === "'") break;
|
|
2309
|
-
i += 1;
|
|
2310
|
-
}
|
|
2311
|
-
tagOut += body.slice(valStart2 - 1, i + 1);
|
|
2312
|
-
i += 1;
|
|
2313
|
-
continue;
|
|
2314
|
-
}
|
|
2315
|
-
const valStart = i + 1;
|
|
2316
|
-
i += 1;
|
|
2317
|
-
let closeIdx = -1;
|
|
2318
|
-
for (let scan = valStart; scan < body.length; scan += 1) {
|
|
2319
|
-
if (body[scan] !== '"') continue;
|
|
2320
|
-
let j = scan + 1;
|
|
2321
|
-
while (j < body.length && /\s/.test(body[j] ?? "")) j += 1;
|
|
2322
|
-
if (j < body.length && (body[j] === ">" || body[j] === "/" || /[a-zA-Z]/.test(body[j] ?? ""))) {
|
|
2323
|
-
closeIdx = scan;
|
|
2324
|
-
break;
|
|
2325
|
-
}
|
|
2326
|
-
}
|
|
2327
|
-
if (closeIdx === -1) {
|
|
2328
|
-
tagOut += body.slice(valStart - 1, i);
|
|
2329
|
-
break;
|
|
2330
|
-
}
|
|
2331
|
-
const value = body.slice(valStart, closeIdx);
|
|
2332
|
-
const hasInternalQuote = value.includes('"');
|
|
2333
|
-
if (hasInternalQuote) {
|
|
2334
|
-
const escaped = value.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
2335
|
-
tagOut += `'${escaped}'`;
|
|
2336
|
-
tagAdjusted = true;
|
|
2337
|
-
} else {
|
|
2338
|
-
tagOut += `"${value}"`;
|
|
2339
|
-
}
|
|
2340
|
-
i = closeIdx + 1;
|
|
2341
|
-
}
|
|
2342
|
-
if (tagAdjusted) adjusted = true;
|
|
2343
|
-
tagOut += body[i] ?? "";
|
|
2344
|
-
out += tagOut;
|
|
2345
|
-
i += 1;
|
|
2346
|
-
}
|
|
2347
|
-
return { body: out, adjusted };
|
|
2348
|
-
}
|
|
2349
|
-
|
|
2350
2432
|
// src/translate/validate-translation.ts
|
|
2351
2433
|
init_cjs_shims();
|
|
2352
2434
|
function formatZodIssues(error) {
|
|
@@ -2481,7 +2563,7 @@ async function translatePage(config, item, options = {}) {
|
|
|
2481
2563
|
if (!validated.ok) {
|
|
2482
2564
|
throw new Error(`Translation validation failed: ${validated.error}`);
|
|
2483
2565
|
}
|
|
2484
|
-
const { body: translatedBody, adjusted: mdxAdjusted } =
|
|
2566
|
+
const { body: translatedBody, adjusted: mdxAdjusted } = assertValidTranslatedMdxBody(
|
|
2485
2567
|
result.parsed.body
|
|
2486
2568
|
);
|
|
2487
2569
|
const writeDb = openStore(config, "readwrite");
|