lingo.dev 0.105.0 → 0.105.2
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 -1
- package/build/cli.cjs +460 -72
- package/build/cli.cjs.map +1 -1
- package/build/cli.mjs +396 -8
- package/build/cli.mjs.map +1 -1
- package/package.json +4 -4
package/build/cli.mjs
CHANGED
|
@@ -2749,18 +2749,406 @@ function preserveCommentOrder(section, originalSection) {
|
|
|
2749
2749
|
}
|
|
2750
2750
|
|
|
2751
2751
|
// src/cli/loaders/xliff.ts
|
|
2752
|
-
import
|
|
2752
|
+
import { JSDOM as JSDOM2 } from "jsdom";
|
|
2753
2753
|
function createXliffLoader() {
|
|
2754
2754
|
return createLoader({
|
|
2755
|
-
async pull(locale, input2) {
|
|
2756
|
-
const
|
|
2757
|
-
|
|
2755
|
+
async pull(locale, input2, _ctx, originalLocale) {
|
|
2756
|
+
const trimmedInput = (input2 ?? "").trim();
|
|
2757
|
+
if (!trimmedInput) {
|
|
2758
|
+
return createEmptyResult(originalLocale, locale);
|
|
2759
|
+
}
|
|
2760
|
+
try {
|
|
2761
|
+
const dom = new JSDOM2(trimmedInput, { contentType: "text/xml" });
|
|
2762
|
+
const document = dom.window.document;
|
|
2763
|
+
const parserError = document.querySelector("parsererror");
|
|
2764
|
+
if (parserError) {
|
|
2765
|
+
throw new Error(`XML parsing failed: ${parserError.textContent}`);
|
|
2766
|
+
}
|
|
2767
|
+
const xliffElement = document.documentElement;
|
|
2768
|
+
if (!xliffElement || xliffElement.tagName !== "xliff") {
|
|
2769
|
+
throw new Error("Invalid XLIFF: missing root <xliff> element");
|
|
2770
|
+
}
|
|
2771
|
+
const version = xliffElement.getAttribute("version") || "1.2";
|
|
2772
|
+
const isV2 = version === "2.0";
|
|
2773
|
+
if (isV2) {
|
|
2774
|
+
return pullV2(xliffElement, locale, originalLocale);
|
|
2775
|
+
} else {
|
|
2776
|
+
return pullV1(xliffElement, locale, originalLocale);
|
|
2777
|
+
}
|
|
2778
|
+
} catch (error) {
|
|
2779
|
+
throw new Error(`Failed to parse XLIFF file: ${error.message}`);
|
|
2780
|
+
}
|
|
2758
2781
|
},
|
|
2759
|
-
async push(locale,
|
|
2760
|
-
|
|
2761
|
-
|
|
2782
|
+
async push(locale, translations, originalInput, originalLocale, pullInput) {
|
|
2783
|
+
if (!originalInput) {
|
|
2784
|
+
return pushNewFile(locale, translations, originalLocale);
|
|
2785
|
+
}
|
|
2786
|
+
try {
|
|
2787
|
+
const dom = new JSDOM2(originalInput, { contentType: "text/xml" });
|
|
2788
|
+
const document = dom.window.document;
|
|
2789
|
+
const xliffElement = document.documentElement;
|
|
2790
|
+
const version = xliffElement.getAttribute("version") || "1.2";
|
|
2791
|
+
const isV2 = version === "2.0";
|
|
2792
|
+
if (isV2) {
|
|
2793
|
+
return pushV2(
|
|
2794
|
+
dom,
|
|
2795
|
+
xliffElement,
|
|
2796
|
+
locale,
|
|
2797
|
+
translations,
|
|
2798
|
+
originalLocale,
|
|
2799
|
+
originalInput
|
|
2800
|
+
);
|
|
2801
|
+
} else {
|
|
2802
|
+
return pushV1(
|
|
2803
|
+
dom,
|
|
2804
|
+
xliffElement,
|
|
2805
|
+
locale,
|
|
2806
|
+
translations,
|
|
2807
|
+
originalLocale,
|
|
2808
|
+
originalInput
|
|
2809
|
+
);
|
|
2810
|
+
}
|
|
2811
|
+
} catch (error) {
|
|
2812
|
+
throw new Error(`Failed to update XLIFF file: ${error.message}`);
|
|
2813
|
+
}
|
|
2814
|
+
}
|
|
2815
|
+
});
|
|
2816
|
+
}
|
|
2817
|
+
function pullV1(xliffElement, locale, originalLocale) {
|
|
2818
|
+
const result = {};
|
|
2819
|
+
const fileElement = xliffElement.querySelector("file");
|
|
2820
|
+
if (!fileElement) {
|
|
2821
|
+
return result;
|
|
2822
|
+
}
|
|
2823
|
+
const sourceLanguage = fileElement.getAttribute("source-language") || originalLocale;
|
|
2824
|
+
const isSourceLocale = sourceLanguage === locale;
|
|
2825
|
+
const bodyElement = fileElement.querySelector("body");
|
|
2826
|
+
if (!bodyElement) {
|
|
2827
|
+
return result;
|
|
2828
|
+
}
|
|
2829
|
+
const transUnits = bodyElement.querySelectorAll("trans-unit");
|
|
2830
|
+
const seenKeys = /* @__PURE__ */ new Set();
|
|
2831
|
+
transUnits.forEach((unit) => {
|
|
2832
|
+
let key = getTransUnitKey(unit);
|
|
2833
|
+
if (!key) return;
|
|
2834
|
+
if (seenKeys.has(key)) {
|
|
2835
|
+
const id = unit.getAttribute("id")?.trim();
|
|
2836
|
+
if (id) {
|
|
2837
|
+
key = `${key}#${id}`;
|
|
2838
|
+
} else {
|
|
2839
|
+
let counter = 1;
|
|
2840
|
+
let newKey = `${key}__${counter}`;
|
|
2841
|
+
while (seenKeys.has(newKey)) {
|
|
2842
|
+
counter++;
|
|
2843
|
+
newKey = `${key}__${counter}`;
|
|
2844
|
+
}
|
|
2845
|
+
key = newKey;
|
|
2846
|
+
}
|
|
2847
|
+
}
|
|
2848
|
+
seenKeys.add(key);
|
|
2849
|
+
const elementName = isSourceLocale ? "source" : "target";
|
|
2850
|
+
const textElement = unit.querySelector(elementName);
|
|
2851
|
+
if (textElement) {
|
|
2852
|
+
result[key] = extractTextContent(textElement);
|
|
2853
|
+
} else if (isSourceLocale) {
|
|
2854
|
+
result[key] = key;
|
|
2855
|
+
} else {
|
|
2856
|
+
result[key] = "";
|
|
2857
|
+
}
|
|
2858
|
+
});
|
|
2859
|
+
return result;
|
|
2860
|
+
}
|
|
2861
|
+
function pushV1(dom, xliffElement, locale, translations, originalLocale, originalInput) {
|
|
2862
|
+
const document = dom.window.document;
|
|
2863
|
+
const fileElement = xliffElement.querySelector("file");
|
|
2864
|
+
if (!fileElement) {
|
|
2865
|
+
throw new Error("Invalid XLIFF 1.2: missing <file> element");
|
|
2866
|
+
}
|
|
2867
|
+
const sourceLanguage = fileElement.getAttribute("source-language") || originalLocale;
|
|
2868
|
+
const isSourceLocale = sourceLanguage === locale;
|
|
2869
|
+
if (!isSourceLocale) {
|
|
2870
|
+
fileElement.setAttribute("target-language", locale);
|
|
2871
|
+
}
|
|
2872
|
+
let bodyElement = fileElement.querySelector("body");
|
|
2873
|
+
if (!bodyElement) {
|
|
2874
|
+
bodyElement = document.createElement("body");
|
|
2875
|
+
fileElement.appendChild(bodyElement);
|
|
2876
|
+
}
|
|
2877
|
+
const existingUnits = /* @__PURE__ */ new Map();
|
|
2878
|
+
const seenKeys = /* @__PURE__ */ new Set();
|
|
2879
|
+
bodyElement.querySelectorAll("trans-unit").forEach((unit) => {
|
|
2880
|
+
let key = getTransUnitKey(unit);
|
|
2881
|
+
if (!key) return;
|
|
2882
|
+
if (seenKeys.has(key)) {
|
|
2883
|
+
const id = unit.getAttribute("id")?.trim();
|
|
2884
|
+
if (id) {
|
|
2885
|
+
key = `${key}#${id}`;
|
|
2886
|
+
} else {
|
|
2887
|
+
let counter = 1;
|
|
2888
|
+
let newKey = `${key}__${counter}`;
|
|
2889
|
+
while (seenKeys.has(newKey)) {
|
|
2890
|
+
counter++;
|
|
2891
|
+
newKey = `${key}__${counter}`;
|
|
2892
|
+
}
|
|
2893
|
+
key = newKey;
|
|
2894
|
+
}
|
|
2895
|
+
}
|
|
2896
|
+
seenKeys.add(key);
|
|
2897
|
+
existingUnits.set(key, unit);
|
|
2898
|
+
});
|
|
2899
|
+
Object.entries(translations).forEach(([key, value]) => {
|
|
2900
|
+
let unit = existingUnits.get(key);
|
|
2901
|
+
if (!unit) {
|
|
2902
|
+
unit = document.createElement("trans-unit");
|
|
2903
|
+
unit.setAttribute("id", key);
|
|
2904
|
+
unit.setAttribute("resname", key);
|
|
2905
|
+
unit.setAttribute("restype", "string");
|
|
2906
|
+
unit.setAttribute("datatype", "plaintext");
|
|
2907
|
+
const sourceElement = document.createElement("source");
|
|
2908
|
+
setTextContent(sourceElement, isSourceLocale ? value : key);
|
|
2909
|
+
unit.appendChild(sourceElement);
|
|
2910
|
+
if (!isSourceLocale) {
|
|
2911
|
+
const targetElement = document.createElement("target");
|
|
2912
|
+
targetElement.setAttribute("state", value ? "translated" : "new");
|
|
2913
|
+
setTextContent(targetElement, value);
|
|
2914
|
+
unit.appendChild(targetElement);
|
|
2915
|
+
}
|
|
2916
|
+
bodyElement.appendChild(unit);
|
|
2917
|
+
existingUnits.set(key, unit);
|
|
2918
|
+
} else {
|
|
2919
|
+
updateTransUnitV1(unit, key, value, isSourceLocale);
|
|
2920
|
+
}
|
|
2921
|
+
});
|
|
2922
|
+
const translationKeys = new Set(Object.keys(translations));
|
|
2923
|
+
existingUnits.forEach((unit, key) => {
|
|
2924
|
+
if (!translationKeys.has(key)) {
|
|
2925
|
+
unit.parentNode?.removeChild(unit);
|
|
2926
|
+
}
|
|
2927
|
+
});
|
|
2928
|
+
return serializeWithDeclaration(
|
|
2929
|
+
dom,
|
|
2930
|
+
extractXmlDeclaration(originalInput || "")
|
|
2931
|
+
);
|
|
2932
|
+
}
|
|
2933
|
+
function updateTransUnitV1(unit, key, value, isSourceLocale) {
|
|
2934
|
+
const document = unit.ownerDocument;
|
|
2935
|
+
if (isSourceLocale) {
|
|
2936
|
+
let sourceElement = unit.querySelector("source");
|
|
2937
|
+
if (!sourceElement) {
|
|
2938
|
+
sourceElement = document.createElement("source");
|
|
2939
|
+
unit.appendChild(sourceElement);
|
|
2940
|
+
}
|
|
2941
|
+
setTextContent(sourceElement, value);
|
|
2942
|
+
} else {
|
|
2943
|
+
let targetElement = unit.querySelector("target");
|
|
2944
|
+
if (!targetElement) {
|
|
2945
|
+
targetElement = document.createElement("target");
|
|
2946
|
+
unit.appendChild(targetElement);
|
|
2762
2947
|
}
|
|
2948
|
+
setTextContent(targetElement, value);
|
|
2949
|
+
targetElement.setAttribute("state", value.trim() ? "translated" : "new");
|
|
2950
|
+
}
|
|
2951
|
+
}
|
|
2952
|
+
function pullV2(xliffElement, locale, originalLocale) {
|
|
2953
|
+
const result = {};
|
|
2954
|
+
const srcLang = xliffElement.getAttribute("srcLang") || originalLocale;
|
|
2955
|
+
result.sourceLanguage = srcLang;
|
|
2956
|
+
const fileElements = xliffElement.querySelectorAll("file");
|
|
2957
|
+
fileElements.forEach((fileElement) => {
|
|
2958
|
+
const fileId = fileElement.getAttribute("id");
|
|
2959
|
+
if (!fileId) return;
|
|
2960
|
+
traverseUnitsV2(fileElement, fileId, "", result);
|
|
2763
2961
|
});
|
|
2962
|
+
return result;
|
|
2963
|
+
}
|
|
2964
|
+
function traverseUnitsV2(container, fileId, currentPath, result) {
|
|
2965
|
+
Array.from(container.children).forEach((child) => {
|
|
2966
|
+
const tagName = child.tagName;
|
|
2967
|
+
if (tagName === "unit") {
|
|
2968
|
+
const unitId = child.getAttribute("id")?.trim();
|
|
2969
|
+
if (!unitId) return;
|
|
2970
|
+
const key = `resources/${fileId}/${currentPath}${unitId}/source`;
|
|
2971
|
+
const segment = child.querySelector("segment");
|
|
2972
|
+
const source = segment?.querySelector("source");
|
|
2973
|
+
if (source) {
|
|
2974
|
+
result[key] = extractTextContent(source);
|
|
2975
|
+
} else {
|
|
2976
|
+
result[key] = unitId;
|
|
2977
|
+
}
|
|
2978
|
+
} else if (tagName === "group") {
|
|
2979
|
+
const groupId = child.getAttribute("id")?.trim();
|
|
2980
|
+
const newPath = groupId ? `${currentPath}${groupId}/groupUnits/` : currentPath;
|
|
2981
|
+
traverseUnitsV2(child, fileId, newPath, result);
|
|
2982
|
+
}
|
|
2983
|
+
});
|
|
2984
|
+
}
|
|
2985
|
+
function pushV2(dom, xliffElement, locale, translations, originalLocale, originalInput) {
|
|
2986
|
+
const document = dom.window.document;
|
|
2987
|
+
if (translations.sourceLanguage) {
|
|
2988
|
+
xliffElement.setAttribute("srcLang", translations.sourceLanguage);
|
|
2989
|
+
delete translations.sourceLanguage;
|
|
2990
|
+
}
|
|
2991
|
+
const existingUnits = /* @__PURE__ */ new Map();
|
|
2992
|
+
const fileElements = xliffElement.querySelectorAll("file");
|
|
2993
|
+
fileElements.forEach((fileElement) => {
|
|
2994
|
+
const fileId = fileElement.getAttribute("id");
|
|
2995
|
+
if (!fileId) return;
|
|
2996
|
+
indexUnitsV2(fileElement, fileId, "", existingUnits);
|
|
2997
|
+
});
|
|
2998
|
+
Object.entries(translations).forEach(([key, value]) => {
|
|
2999
|
+
const unit = existingUnits.get(key);
|
|
3000
|
+
if (unit) {
|
|
3001
|
+
updateUnitV2(unit, value);
|
|
3002
|
+
} else {
|
|
3003
|
+
console.warn(`Cannot create new unit for key: ${key} in XLIFF 2.0`);
|
|
3004
|
+
}
|
|
3005
|
+
});
|
|
3006
|
+
return serializeWithDeclaration(
|
|
3007
|
+
dom,
|
|
3008
|
+
extractXmlDeclaration(originalInput || "")
|
|
3009
|
+
);
|
|
3010
|
+
}
|
|
3011
|
+
function indexUnitsV2(container, fileId, currentPath, index) {
|
|
3012
|
+
Array.from(container.children).forEach((child) => {
|
|
3013
|
+
const tagName = child.tagName;
|
|
3014
|
+
if (tagName === "unit") {
|
|
3015
|
+
const unitId = child.getAttribute("id")?.trim();
|
|
3016
|
+
if (!unitId) return;
|
|
3017
|
+
const key = `resources/${fileId}/${currentPath}${unitId}/source`;
|
|
3018
|
+
index.set(key, child);
|
|
3019
|
+
} else if (tagName === "group") {
|
|
3020
|
+
const groupId = child.getAttribute("id")?.trim();
|
|
3021
|
+
const newPath = groupId ? `${currentPath}${groupId}/groupUnits/` : currentPath;
|
|
3022
|
+
indexUnitsV2(child, fileId, newPath, index);
|
|
3023
|
+
}
|
|
3024
|
+
});
|
|
3025
|
+
}
|
|
3026
|
+
function updateUnitV2(unit, value) {
|
|
3027
|
+
const document = unit.ownerDocument;
|
|
3028
|
+
let segment = unit.querySelector("segment");
|
|
3029
|
+
if (!segment) {
|
|
3030
|
+
segment = document.createElement("segment");
|
|
3031
|
+
unit.appendChild(segment);
|
|
3032
|
+
}
|
|
3033
|
+
let source = segment.querySelector("source");
|
|
3034
|
+
if (!source) {
|
|
3035
|
+
source = document.createElement("source");
|
|
3036
|
+
segment.appendChild(source);
|
|
3037
|
+
}
|
|
3038
|
+
setTextContent(source, value);
|
|
3039
|
+
}
|
|
3040
|
+
function getTransUnitKey(transUnit) {
|
|
3041
|
+
const resname = transUnit.getAttribute("resname")?.trim();
|
|
3042
|
+
if (resname) return resname;
|
|
3043
|
+
const id = transUnit.getAttribute("id")?.trim();
|
|
3044
|
+
if (id) return id;
|
|
3045
|
+
const sourceElement = transUnit.querySelector("source");
|
|
3046
|
+
if (sourceElement) {
|
|
3047
|
+
const sourceText = extractTextContent(sourceElement).trim();
|
|
3048
|
+
if (sourceText) return sourceText;
|
|
3049
|
+
}
|
|
3050
|
+
return "";
|
|
3051
|
+
}
|
|
3052
|
+
function extractTextContent(element) {
|
|
3053
|
+
const cdataNode = Array.from(element.childNodes).find(
|
|
3054
|
+
(node) => node.nodeType === element.CDATA_SECTION_NODE
|
|
3055
|
+
);
|
|
3056
|
+
if (cdataNode) {
|
|
3057
|
+
return cdataNode.nodeValue || "";
|
|
3058
|
+
}
|
|
3059
|
+
return element.textContent || "";
|
|
3060
|
+
}
|
|
3061
|
+
function setTextContent(element, content) {
|
|
3062
|
+
const document = element.ownerDocument;
|
|
3063
|
+
while (element.firstChild) {
|
|
3064
|
+
element.removeChild(element.firstChild);
|
|
3065
|
+
}
|
|
3066
|
+
if (/[<>&"']/.test(content)) {
|
|
3067
|
+
const cdataSection = document.createCDATASection(content);
|
|
3068
|
+
element.appendChild(cdataSection);
|
|
3069
|
+
} else {
|
|
3070
|
+
element.textContent = content;
|
|
3071
|
+
}
|
|
3072
|
+
}
|
|
3073
|
+
function extractXmlDeclaration(xmlContent) {
|
|
3074
|
+
const match2 = xmlContent.match(/^<\?xml[^>]*\?>/);
|
|
3075
|
+
return match2 ? match2[0] : "";
|
|
3076
|
+
}
|
|
3077
|
+
function serializeWithDeclaration(dom, declaration) {
|
|
3078
|
+
let serialized = dom.serialize();
|
|
3079
|
+
serialized = formatXml(serialized);
|
|
3080
|
+
if (declaration) {
|
|
3081
|
+
serialized = `${declaration}
|
|
3082
|
+
${serialized}`;
|
|
3083
|
+
}
|
|
3084
|
+
return serialized;
|
|
3085
|
+
}
|
|
3086
|
+
function formatXml(xml) {
|
|
3087
|
+
const dom = new JSDOM2(xml, { contentType: "text/xml" });
|
|
3088
|
+
const doc = dom.window.document;
|
|
3089
|
+
function formatElement(element, depth = 0) {
|
|
3090
|
+
const indent2 = " ".repeat(depth);
|
|
3091
|
+
const tagName = element.tagName;
|
|
3092
|
+
const attributes = Array.from(element.attributes).map((attr) => `${attr.name}="${attr.value}"`).join(" ");
|
|
3093
|
+
const openTag = attributes ? `<${tagName} ${attributes}>` : `<${tagName}>`;
|
|
3094
|
+
const cdataNode = Array.from(element.childNodes).find(
|
|
3095
|
+
(node) => node.nodeType === element.CDATA_SECTION_NODE
|
|
3096
|
+
);
|
|
3097
|
+
if (cdataNode) {
|
|
3098
|
+
return `${indent2}${openTag}<![CDATA[${cdataNode.nodeValue}]]></${tagName}>`;
|
|
3099
|
+
}
|
|
3100
|
+
const textContent = element.textContent?.trim() || "";
|
|
3101
|
+
const hasOnlyText = element.childNodes.length === 1 && element.childNodes[0].nodeType === 3;
|
|
3102
|
+
if (hasOnlyText && textContent) {
|
|
3103
|
+
return `${indent2}${openTag}${textContent}</${tagName}>`;
|
|
3104
|
+
}
|
|
3105
|
+
const children = Array.from(element.children);
|
|
3106
|
+
if (children.length === 0) {
|
|
3107
|
+
return `${indent2}${openTag}</${tagName}>`;
|
|
3108
|
+
}
|
|
3109
|
+
let result = `${indent2}${openTag}
|
|
3110
|
+
`;
|
|
3111
|
+
for (const child of children) {
|
|
3112
|
+
result += formatElement(child, depth + 1) + "\n";
|
|
3113
|
+
}
|
|
3114
|
+
result += `${indent2}</${tagName}>`;
|
|
3115
|
+
return result;
|
|
3116
|
+
}
|
|
3117
|
+
return formatElement(doc.documentElement);
|
|
3118
|
+
}
|
|
3119
|
+
function createEmptyResult(originalLocale, locale) {
|
|
3120
|
+
return {};
|
|
3121
|
+
}
|
|
3122
|
+
function pushNewFile(locale, translations, originalLocale) {
|
|
3123
|
+
const skeleton = `<?xml version="1.0" encoding="utf-8"?>
|
|
3124
|
+
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
|
|
3125
|
+
<file original="" source-language="${originalLocale}" target-language="${locale}" datatype="plaintext">
|
|
3126
|
+
<header></header>
|
|
3127
|
+
<body></body>
|
|
3128
|
+
</file>
|
|
3129
|
+
</xliff>`;
|
|
3130
|
+
const dom = new JSDOM2(skeleton, { contentType: "text/xml" });
|
|
3131
|
+
const document = dom.window.document;
|
|
3132
|
+
const bodyElement = document.querySelector("body");
|
|
3133
|
+
Object.entries(translations).forEach(([key, value]) => {
|
|
3134
|
+
const unit = document.createElement("trans-unit");
|
|
3135
|
+
unit.setAttribute("id", key);
|
|
3136
|
+
unit.setAttribute("resname", key);
|
|
3137
|
+
unit.setAttribute("restype", "string");
|
|
3138
|
+
unit.setAttribute("datatype", "plaintext");
|
|
3139
|
+
const sourceElement = document.createElement("source");
|
|
3140
|
+
setTextContent(sourceElement, key);
|
|
3141
|
+
unit.appendChild(sourceElement);
|
|
3142
|
+
const targetElement = document.createElement("target");
|
|
3143
|
+
targetElement.setAttribute("state", value ? "translated" : "new");
|
|
3144
|
+
setTextContent(targetElement, value);
|
|
3145
|
+
unit.appendChild(targetElement);
|
|
3146
|
+
bodyElement.appendChild(unit);
|
|
3147
|
+
});
|
|
3148
|
+
return serializeWithDeclaration(
|
|
3149
|
+
dom,
|
|
3150
|
+
'<?xml version="1.0" encoding="utf-8"?>'
|
|
3151
|
+
);
|
|
2764
3152
|
}
|
|
2765
3153
|
|
|
2766
3154
|
// src/cli/loaders/xml.ts
|
|
@@ -9954,7 +10342,7 @@ async function renderHero2() {
|
|
|
9954
10342
|
// package.json
|
|
9955
10343
|
var package_default = {
|
|
9956
10344
|
name: "lingo.dev",
|
|
9957
|
-
version: "0.105.
|
|
10345
|
+
version: "0.105.2",
|
|
9958
10346
|
description: "Lingo.dev CLI",
|
|
9959
10347
|
private: false,
|
|
9960
10348
|
publishConfig: {
|