@valbuild/server 0.17.0 → 0.19.0

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.
@@ -246,6 +246,73 @@ function analyzeValModule(sourceFile) {
246
246
  return analysis;
247
247
  }
248
248
 
249
+ /* eslint-disable @typescript-eslint/no-explicit-any */
250
+
251
+ const HeaderRegEx = /h([\d])/;
252
+ function richTextToTaggedStringTemplate(source) {
253
+ const texts = [""];
254
+ const nodes = [];
255
+ let didAppendNewLines = false;
256
+ function rec(node) {
257
+ if (typeof node === "string") {
258
+ texts[texts.length - 1] += node;
259
+ } else if (node.tag) {
260
+ var _node$tag, _node$children, _node$tag2;
261
+ if ((_node$tag = node.tag) !== null && _node$tag !== void 0 && _node$tag.startsWith("h")) {
262
+ const [, depth] = node.tag.match(HeaderRegEx);
263
+ for (let i = 0; i < Number(depth); i++) {
264
+ texts[texts.length - 1] += "#";
265
+ }
266
+ texts[texts.length - 1] += " ";
267
+ } else if (node.tag === "span") {
268
+ if (node.classes.includes("bold") && !node.classes.includes("italic")) {
269
+ texts[texts.length - 1] += "**";
270
+ }
271
+ if (node.classes.includes("italic") && !node.classes.includes("bold")) {
272
+ texts[texts.length - 1] += "*";
273
+ }
274
+ if (node.classes.includes("italic") && node.classes.includes("bold")) {
275
+ texts[texts.length - 1] += "***";
276
+ }
277
+ if (node.classes.includes("line-through")) {
278
+ texts[texts.length - 1] += "~~";
279
+ }
280
+ }
281
+ (_node$children = node.children) === null || _node$children === void 0 ? void 0 : _node$children.forEach(rec);
282
+ if (node.tag === "span") {
283
+ didAppendNewLines = false;
284
+ if (node.classes.includes("line-through")) {
285
+ texts[texts.length - 1] += "~~";
286
+ }
287
+ if (node.classes.includes("italic") && node.classes.includes("bold")) {
288
+ texts[texts.length - 1] += "***";
289
+ }
290
+ if (node.classes.includes("italic") && !node.classes.includes("bold")) {
291
+ texts[texts.length - 1] += "*";
292
+ }
293
+ if (node.classes.includes("bold") && !node.classes.includes("italic")) {
294
+ texts[texts.length - 1] += "**";
295
+ }
296
+ } else if (node.tag === "p") {
297
+ didAppendNewLines = true;
298
+ texts[texts.length - 1] += "\n\n";
299
+ } else if ((_node$tag2 = node.tag) !== null && _node$tag2 !== void 0 && _node$tag2.startsWith("h")) {
300
+ didAppendNewLines = true;
301
+ texts[texts.length - 1] += "\n\n";
302
+ }
303
+ } else {
304
+ nodes.push(node);
305
+ texts.push("\n");
306
+ }
307
+ }
308
+ source.children.forEach(rec);
309
+ if (texts[texts.length - 1] && didAppendNewLines) {
310
+ // remove last \n\n
311
+ texts[texts.length - 1] = texts[texts.length - 1].slice(0, -2);
312
+ }
313
+ return [texts, nodes];
314
+ }
315
+
249
316
  function isValidIdentifier(text) {
250
317
  if (text.length === 0) {
251
318
  return false;
@@ -263,8 +330,20 @@ function isValidIdentifier(text) {
263
330
  function createPropertyAssignment(key, value) {
264
331
  return ts__default["default"].factory.createPropertyAssignment(isValidIdentifier(key) ? ts__default["default"].factory.createIdentifier(key) : ts__default["default"].factory.createStringLiteral(key), toExpression(value));
265
332
  }
266
- function createValFileReference(ref) {
267
- return ts__default["default"].factory.createCallExpression(ts__default["default"].factory.createPropertyAccessExpression(ts__default["default"].factory.createIdentifier("val"), ts__default["default"].factory.createIdentifier("file")), undefined, [ts__default["default"].factory.createStringLiteral(ref)]);
333
+ function createValFileReference(value) {
334
+ const args = [ts__default["default"].factory.createStringLiteral(value[core.FILE_REF_PROP])];
335
+ if (value.metadata) {
336
+ args.push(toExpression(value.metadata));
337
+ }
338
+ return ts__default["default"].factory.createCallExpression(ts__default["default"].factory.createPropertyAccessExpression(ts__default["default"].factory.createIdentifier("val"), ts__default["default"].factory.createIdentifier("file")), undefined, args);
339
+ }
340
+ function createValRichTextTaggedStringTemplate(value) {
341
+ const [[head, ...others], nodes] = richTextToTaggedStringTemplate(value);
342
+ const tag = ts__default["default"].factory.createPropertyAccessExpression(ts__default["default"].factory.createIdentifier("val"), ts__default["default"].factory.createIdentifier("richtext"));
343
+ if (nodes.length > 0) {
344
+ return ts__default["default"].factory.createTaggedTemplateExpression(tag, undefined, ts__default["default"].factory.createTemplateExpression(ts__default["default"].factory.createTemplateHead(head, head), others.map((s, i) => ts__default["default"].factory.createTemplateSpan(toExpression(nodes[i]), i < others.length - 1 ? ts__default["default"].factory.createTemplateMiddle(s, s) : ts__default["default"].factory.createTemplateTail(s, s)))));
345
+ }
346
+ return ts__default["default"].factory.createTaggedTemplateExpression(tag, undefined, ts__default["default"].factory.createNoSubstitutionTemplateLiteral(head, head));
268
347
  }
269
348
  function toExpression(value) {
270
349
  if (typeof value === "string") {
@@ -280,7 +359,9 @@ function toExpression(value) {
280
359
  return ts__default["default"].factory.createArrayLiteralExpression(value.map(toExpression));
281
360
  } else if (typeof value === "object") {
282
361
  if (isValFileValue(value)) {
283
- return createValFileReference(value[core.FILE_REF_PROP]);
362
+ return createValFileReference(value);
363
+ } else if (isValRichTextValue(value)) {
364
+ return createValRichTextTaggedStringTemplate(value);
284
365
  }
285
366
  return ts__default["default"].factory.createObjectLiteralExpression(Object.entries(value).map(([key, value]) => createPropertyAssignment(key, value)));
286
367
  } else {
@@ -507,7 +588,14 @@ function removeAtPath(document, rootNode, path) {
507
588
  return fp.pipe(getPointerFromPath(rootNode, path), fp.result.flatMap(([node, key]) => removeFromNode(document, node, key)));
508
589
  }
509
590
  function isValFileValue(value) {
510
- return !!(typeof value === "object" && value && core.FILE_REF_PROP in value && typeof value[core.FILE_REF_PROP] === "string");
591
+ return !!(typeof value === "object" && value &&
592
+ // TODO: replace the below with this:
593
+ // VAL_EXTENSION in value &&
594
+ // value[VAL_EXTENSION] === "file" &&
595
+ core.FILE_REF_PROP in value && typeof value[core.FILE_REF_PROP] === "string");
596
+ }
597
+ function isValRichTextValue(value) {
598
+ return !!(typeof value === "object" && value && core.VAL_EXTENSION in value && value[core.VAL_EXTENSION] === "richtext" && "children" in value && typeof value.children === "object" && Array.isArray(value.children));
511
599
  }
512
600
  function addToNode(document, node, key, value) {
513
601
  if (ts__default["default"].isArrayLiteralExpression(node)) {
@@ -691,7 +779,12 @@ const patchValFile = async (id, valConfigPath, patch$1, sourceFileHandler, runti
691
779
  // Buffer.from(content, "base64").toString("binary")
692
780
  // );
693
781
  // sourceFileHandler.moveFile(tempFilePath, "." + filePath);
694
- sourceFileHandler.writeFile("." + filePath, convertDataUrlToBase64(content).toString("binary"), "binary");
782
+ // TODO: ensure that directory exists
783
+ if (content.startsWith("data:/image/svg+xml")) {
784
+ sourceFileHandler.writeFile("." + filePath, convertDataUrlToBase64(content).toString("utf8"), "utf8");
785
+ } else {
786
+ sourceFileHandler.writeFile("." + filePath, convertDataUrlToBase64(content).toString("binary"), "binary");
787
+ }
695
788
  }
696
789
  for (const [ref, patch] of Object.entries(derefRes.value.remotePatches)) {
697
790
  throw Error(`Cannot update remote ${ref} with ${JSON.stringify(patch)}: not implemented`);
@@ -773,7 +866,7 @@ class ValSourceFileHandler {
773
866
 
774
867
  const JsFileLookupMapping = [
775
868
  // NOTE: first one matching will be used
776
- [".cjs.d.ts", [".esm.js", ".mjs.js"]], [".cjs.js", [".esm.js", ".mjs.js"]], [".cjs", [".mjs"]], [".d.ts", [".js"]]];
869
+ [".cjs.d.ts", [".esm.js", ".mjs.js"]], [".cjs.js", [".esm.js", ".mjs.js"]], [".cjs", [".mjs"]], [".d.ts", [".js", ".esm.js", ".mjs.js"]]];
777
870
  class ValModuleLoader {
778
871
  constructor(projectRoot, compilerOptions, sourceFileHandler, host = {
779
872
  ...ts__default["default"].sys,
@@ -1038,6 +1131,10 @@ const OperationJSONT = z__default["default"].discriminatedUnion("op", [z__defaul
1038
1131
  op: z__default["default"].literal("test"),
1039
1132
  path: z__default["default"].string(),
1040
1133
  value: JSONValueT
1134
+ }).strict(), z__default["default"].object({
1135
+ op: z__default["default"].literal("file"),
1136
+ path: z__default["default"].string(),
1137
+ value: z__default["default"].string()
1041
1138
  }).strict()]);
1042
1139
  const PatchJSON = z__default["default"].array(OperationJSONT);
1043
1140
 
@@ -1772,7 +1869,7 @@ async function createFixPatch(config, apply, sourcePath, validationError) {
1772
1869
  }
1773
1870
  const localFile = path__namespace["default"].join(config.projectRoot, maybeRef);
1774
1871
  const buffer = fs__default["default"].readFileSync(localFile);
1775
- const sha256 = await getSHA256Hash(buffer);
1872
+ const sha256 = await core.Internal.getSHA256Hash(buffer);
1776
1873
  const imageSize = sizeOf__default["default"](buffer);
1777
1874
  return {
1778
1875
  ...imageSize,
@@ -1864,12 +1961,6 @@ async function createFixPatch(config, apply, sourcePath, validationError) {
1864
1961
  remainingErrors
1865
1962
  };
1866
1963
  }
1867
- const getSHA256Hash = async bits => {
1868
- const hashBuffer = await crypto__default["default"].subtle.digest("SHA-256", bits);
1869
- const hashArray = Array.from(new Uint8Array(hashBuffer));
1870
- const hash = hashArray.map(item => item.toString(16).padStart(2, "0")).join("");
1871
- return hash;
1872
- };
1873
1964
 
1874
1965
  exports.LocalValServer = LocalValServer;
1875
1966
  exports.PatchJSON = PatchJSON;
@@ -246,6 +246,73 @@ function analyzeValModule(sourceFile) {
246
246
  return analysis;
247
247
  }
248
248
 
249
+ /* eslint-disable @typescript-eslint/no-explicit-any */
250
+
251
+ const HeaderRegEx = /h([\d])/;
252
+ function richTextToTaggedStringTemplate(source) {
253
+ const texts = [""];
254
+ const nodes = [];
255
+ let didAppendNewLines = false;
256
+ function rec(node) {
257
+ if (typeof node === "string") {
258
+ texts[texts.length - 1] += node;
259
+ } else if (node.tag) {
260
+ var _node$tag, _node$children, _node$tag2;
261
+ if ((_node$tag = node.tag) !== null && _node$tag !== void 0 && _node$tag.startsWith("h")) {
262
+ const [, depth] = node.tag.match(HeaderRegEx);
263
+ for (let i = 0; i < Number(depth); i++) {
264
+ texts[texts.length - 1] += "#";
265
+ }
266
+ texts[texts.length - 1] += " ";
267
+ } else if (node.tag === "span") {
268
+ if (node.classes.includes("bold") && !node.classes.includes("italic")) {
269
+ texts[texts.length - 1] += "**";
270
+ }
271
+ if (node.classes.includes("italic") && !node.classes.includes("bold")) {
272
+ texts[texts.length - 1] += "*";
273
+ }
274
+ if (node.classes.includes("italic") && node.classes.includes("bold")) {
275
+ texts[texts.length - 1] += "***";
276
+ }
277
+ if (node.classes.includes("line-through")) {
278
+ texts[texts.length - 1] += "~~";
279
+ }
280
+ }
281
+ (_node$children = node.children) === null || _node$children === void 0 ? void 0 : _node$children.forEach(rec);
282
+ if (node.tag === "span") {
283
+ didAppendNewLines = false;
284
+ if (node.classes.includes("line-through")) {
285
+ texts[texts.length - 1] += "~~";
286
+ }
287
+ if (node.classes.includes("italic") && node.classes.includes("bold")) {
288
+ texts[texts.length - 1] += "***";
289
+ }
290
+ if (node.classes.includes("italic") && !node.classes.includes("bold")) {
291
+ texts[texts.length - 1] += "*";
292
+ }
293
+ if (node.classes.includes("bold") && !node.classes.includes("italic")) {
294
+ texts[texts.length - 1] += "**";
295
+ }
296
+ } else if (node.tag === "p") {
297
+ didAppendNewLines = true;
298
+ texts[texts.length - 1] += "\n\n";
299
+ } else if ((_node$tag2 = node.tag) !== null && _node$tag2 !== void 0 && _node$tag2.startsWith("h")) {
300
+ didAppendNewLines = true;
301
+ texts[texts.length - 1] += "\n\n";
302
+ }
303
+ } else {
304
+ nodes.push(node);
305
+ texts.push("\n");
306
+ }
307
+ }
308
+ source.children.forEach(rec);
309
+ if (texts[texts.length - 1] && didAppendNewLines) {
310
+ // remove last \n\n
311
+ texts[texts.length - 1] = texts[texts.length - 1].slice(0, -2);
312
+ }
313
+ return [texts, nodes];
314
+ }
315
+
249
316
  function isValidIdentifier(text) {
250
317
  if (text.length === 0) {
251
318
  return false;
@@ -263,8 +330,20 @@ function isValidIdentifier(text) {
263
330
  function createPropertyAssignment(key, value) {
264
331
  return ts__default["default"].factory.createPropertyAssignment(isValidIdentifier(key) ? ts__default["default"].factory.createIdentifier(key) : ts__default["default"].factory.createStringLiteral(key), toExpression(value));
265
332
  }
266
- function createValFileReference(ref) {
267
- return ts__default["default"].factory.createCallExpression(ts__default["default"].factory.createPropertyAccessExpression(ts__default["default"].factory.createIdentifier("val"), ts__default["default"].factory.createIdentifier("file")), undefined, [ts__default["default"].factory.createStringLiteral(ref)]);
333
+ function createValFileReference(value) {
334
+ const args = [ts__default["default"].factory.createStringLiteral(value[core.FILE_REF_PROP])];
335
+ if (value.metadata) {
336
+ args.push(toExpression(value.metadata));
337
+ }
338
+ return ts__default["default"].factory.createCallExpression(ts__default["default"].factory.createPropertyAccessExpression(ts__default["default"].factory.createIdentifier("val"), ts__default["default"].factory.createIdentifier("file")), undefined, args);
339
+ }
340
+ function createValRichTextTaggedStringTemplate(value) {
341
+ const [[head, ...others], nodes] = richTextToTaggedStringTemplate(value);
342
+ const tag = ts__default["default"].factory.createPropertyAccessExpression(ts__default["default"].factory.createIdentifier("val"), ts__default["default"].factory.createIdentifier("richtext"));
343
+ if (nodes.length > 0) {
344
+ return ts__default["default"].factory.createTaggedTemplateExpression(tag, undefined, ts__default["default"].factory.createTemplateExpression(ts__default["default"].factory.createTemplateHead(head, head), others.map((s, i) => ts__default["default"].factory.createTemplateSpan(toExpression(nodes[i]), i < others.length - 1 ? ts__default["default"].factory.createTemplateMiddle(s, s) : ts__default["default"].factory.createTemplateTail(s, s)))));
345
+ }
346
+ return ts__default["default"].factory.createTaggedTemplateExpression(tag, undefined, ts__default["default"].factory.createNoSubstitutionTemplateLiteral(head, head));
268
347
  }
269
348
  function toExpression(value) {
270
349
  if (typeof value === "string") {
@@ -280,7 +359,9 @@ function toExpression(value) {
280
359
  return ts__default["default"].factory.createArrayLiteralExpression(value.map(toExpression));
281
360
  } else if (typeof value === "object") {
282
361
  if (isValFileValue(value)) {
283
- return createValFileReference(value[core.FILE_REF_PROP]);
362
+ return createValFileReference(value);
363
+ } else if (isValRichTextValue(value)) {
364
+ return createValRichTextTaggedStringTemplate(value);
284
365
  }
285
366
  return ts__default["default"].factory.createObjectLiteralExpression(Object.entries(value).map(([key, value]) => createPropertyAssignment(key, value)));
286
367
  } else {
@@ -507,7 +588,14 @@ function removeAtPath(document, rootNode, path) {
507
588
  return fp.pipe(getPointerFromPath(rootNode, path), fp.result.flatMap(([node, key]) => removeFromNode(document, node, key)));
508
589
  }
509
590
  function isValFileValue(value) {
510
- return !!(typeof value === "object" && value && core.FILE_REF_PROP in value && typeof value[core.FILE_REF_PROP] === "string");
591
+ return !!(typeof value === "object" && value &&
592
+ // TODO: replace the below with this:
593
+ // VAL_EXTENSION in value &&
594
+ // value[VAL_EXTENSION] === "file" &&
595
+ core.FILE_REF_PROP in value && typeof value[core.FILE_REF_PROP] === "string");
596
+ }
597
+ function isValRichTextValue(value) {
598
+ return !!(typeof value === "object" && value && core.VAL_EXTENSION in value && value[core.VAL_EXTENSION] === "richtext" && "children" in value && typeof value.children === "object" && Array.isArray(value.children));
511
599
  }
512
600
  function addToNode(document, node, key, value) {
513
601
  if (ts__default["default"].isArrayLiteralExpression(node)) {
@@ -691,7 +779,12 @@ const patchValFile = async (id, valConfigPath, patch$1, sourceFileHandler, runti
691
779
  // Buffer.from(content, "base64").toString("binary")
692
780
  // );
693
781
  // sourceFileHandler.moveFile(tempFilePath, "." + filePath);
694
- sourceFileHandler.writeFile("." + filePath, convertDataUrlToBase64(content).toString("binary"), "binary");
782
+ // TODO: ensure that directory exists
783
+ if (content.startsWith("data:/image/svg+xml")) {
784
+ sourceFileHandler.writeFile("." + filePath, convertDataUrlToBase64(content).toString("utf8"), "utf8");
785
+ } else {
786
+ sourceFileHandler.writeFile("." + filePath, convertDataUrlToBase64(content).toString("binary"), "binary");
787
+ }
695
788
  }
696
789
  for (const [ref, patch] of Object.entries(derefRes.value.remotePatches)) {
697
790
  throw Error(`Cannot update remote ${ref} with ${JSON.stringify(patch)}: not implemented`);
@@ -773,7 +866,7 @@ class ValSourceFileHandler {
773
866
 
774
867
  const JsFileLookupMapping = [
775
868
  // NOTE: first one matching will be used
776
- [".cjs.d.ts", [".esm.js", ".mjs.js"]], [".cjs.js", [".esm.js", ".mjs.js"]], [".cjs", [".mjs"]], [".d.ts", [".js"]]];
869
+ [".cjs.d.ts", [".esm.js", ".mjs.js"]], [".cjs.js", [".esm.js", ".mjs.js"]], [".cjs", [".mjs"]], [".d.ts", [".js", ".esm.js", ".mjs.js"]]];
777
870
  class ValModuleLoader {
778
871
  constructor(projectRoot, compilerOptions, sourceFileHandler, host = {
779
872
  ...ts__default["default"].sys,
@@ -1038,6 +1131,10 @@ const OperationJSONT = z__default["default"].discriminatedUnion("op", [z__defaul
1038
1131
  op: z__default["default"].literal("test"),
1039
1132
  path: z__default["default"].string(),
1040
1133
  value: JSONValueT
1134
+ }).strict(), z__default["default"].object({
1135
+ op: z__default["default"].literal("file"),
1136
+ path: z__default["default"].string(),
1137
+ value: z__default["default"].string()
1041
1138
  }).strict()]);
1042
1139
  const PatchJSON = z__default["default"].array(OperationJSONT);
1043
1140
 
@@ -1772,7 +1869,7 @@ async function createFixPatch(config, apply, sourcePath, validationError) {
1772
1869
  }
1773
1870
  const localFile = path__namespace["default"].join(config.projectRoot, maybeRef);
1774
1871
  const buffer = fs__default["default"].readFileSync(localFile);
1775
- const sha256 = await getSHA256Hash(buffer);
1872
+ const sha256 = await core.Internal.getSHA256Hash(buffer);
1776
1873
  const imageSize = sizeOf__default["default"](buffer);
1777
1874
  return {
1778
1875
  ...imageSize,
@@ -1864,12 +1961,6 @@ async function createFixPatch(config, apply, sourcePath, validationError) {
1864
1961
  remainingErrors
1865
1962
  };
1866
1963
  }
1867
- const getSHA256Hash = async bits => {
1868
- const hashBuffer = await crypto__default["default"].subtle.digest("SHA-256", bits);
1869
- const hashArray = Array.from(new Uint8Array(hashBuffer));
1870
- const hash = hashArray.map(item => item.toString(16).padStart(2, "0")).join("");
1871
- return hash;
1872
- };
1873
1964
 
1874
1965
  exports.LocalValServer = LocalValServer;
1875
1966
  exports.PatchJSON = PatchJSON;
@@ -1,7 +1,7 @@
1
1
  import { newQuickJSWASMModule } from 'quickjs-emscripten';
2
2
  import ts from 'typescript';
3
3
  import { result, pipe } from '@valbuild/core/fp';
4
- import { FILE_REF_PROP, derefPatch, Internal, Schema } from '@valbuild/core';
4
+ import { FILE_REF_PROP, VAL_EXTENSION, derefPatch, Internal, Schema } from '@valbuild/core';
5
5
  import { deepEqual, isNotRoot, PatchError, parseAndValidateArrayIndex, applyPatch, parsePatch, sourceToPatchPath } from '@valbuild/core/patch';
6
6
  import * as path from 'path';
7
7
  import path__default from 'path';
@@ -215,6 +215,73 @@ function analyzeValModule(sourceFile) {
215
215
  return analysis;
216
216
  }
217
217
 
218
+ /* eslint-disable @typescript-eslint/no-explicit-any */
219
+
220
+ const HeaderRegEx = /h([\d])/;
221
+ function richTextToTaggedStringTemplate(source) {
222
+ const texts = [""];
223
+ const nodes = [];
224
+ let didAppendNewLines = false;
225
+ function rec(node) {
226
+ if (typeof node === "string") {
227
+ texts[texts.length - 1] += node;
228
+ } else if (node.tag) {
229
+ var _node$tag, _node$children, _node$tag2;
230
+ if ((_node$tag = node.tag) !== null && _node$tag !== void 0 && _node$tag.startsWith("h")) {
231
+ const [, depth] = node.tag.match(HeaderRegEx);
232
+ for (let i = 0; i < Number(depth); i++) {
233
+ texts[texts.length - 1] += "#";
234
+ }
235
+ texts[texts.length - 1] += " ";
236
+ } else if (node.tag === "span") {
237
+ if (node.classes.includes("bold") && !node.classes.includes("italic")) {
238
+ texts[texts.length - 1] += "**";
239
+ }
240
+ if (node.classes.includes("italic") && !node.classes.includes("bold")) {
241
+ texts[texts.length - 1] += "*";
242
+ }
243
+ if (node.classes.includes("italic") && node.classes.includes("bold")) {
244
+ texts[texts.length - 1] += "***";
245
+ }
246
+ if (node.classes.includes("line-through")) {
247
+ texts[texts.length - 1] += "~~";
248
+ }
249
+ }
250
+ (_node$children = node.children) === null || _node$children === void 0 ? void 0 : _node$children.forEach(rec);
251
+ if (node.tag === "span") {
252
+ didAppendNewLines = false;
253
+ if (node.classes.includes("line-through")) {
254
+ texts[texts.length - 1] += "~~";
255
+ }
256
+ if (node.classes.includes("italic") && node.classes.includes("bold")) {
257
+ texts[texts.length - 1] += "***";
258
+ }
259
+ if (node.classes.includes("italic") && !node.classes.includes("bold")) {
260
+ texts[texts.length - 1] += "*";
261
+ }
262
+ if (node.classes.includes("bold") && !node.classes.includes("italic")) {
263
+ texts[texts.length - 1] += "**";
264
+ }
265
+ } else if (node.tag === "p") {
266
+ didAppendNewLines = true;
267
+ texts[texts.length - 1] += "\n\n";
268
+ } else if ((_node$tag2 = node.tag) !== null && _node$tag2 !== void 0 && _node$tag2.startsWith("h")) {
269
+ didAppendNewLines = true;
270
+ texts[texts.length - 1] += "\n\n";
271
+ }
272
+ } else {
273
+ nodes.push(node);
274
+ texts.push("\n");
275
+ }
276
+ }
277
+ source.children.forEach(rec);
278
+ if (texts[texts.length - 1] && didAppendNewLines) {
279
+ // remove last \n\n
280
+ texts[texts.length - 1] = texts[texts.length - 1].slice(0, -2);
281
+ }
282
+ return [texts, nodes];
283
+ }
284
+
218
285
  function isValidIdentifier(text) {
219
286
  if (text.length === 0) {
220
287
  return false;
@@ -232,8 +299,20 @@ function isValidIdentifier(text) {
232
299
  function createPropertyAssignment(key, value) {
233
300
  return ts.factory.createPropertyAssignment(isValidIdentifier(key) ? ts.factory.createIdentifier(key) : ts.factory.createStringLiteral(key), toExpression(value));
234
301
  }
235
- function createValFileReference(ref) {
236
- return ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier("val"), ts.factory.createIdentifier("file")), undefined, [ts.factory.createStringLiteral(ref)]);
302
+ function createValFileReference(value) {
303
+ const args = [ts.factory.createStringLiteral(value[FILE_REF_PROP])];
304
+ if (value.metadata) {
305
+ args.push(toExpression(value.metadata));
306
+ }
307
+ return ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier("val"), ts.factory.createIdentifier("file")), undefined, args);
308
+ }
309
+ function createValRichTextTaggedStringTemplate(value) {
310
+ const [[head, ...others], nodes] = richTextToTaggedStringTemplate(value);
311
+ const tag = ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier("val"), ts.factory.createIdentifier("richtext"));
312
+ if (nodes.length > 0) {
313
+ return ts.factory.createTaggedTemplateExpression(tag, undefined, ts.factory.createTemplateExpression(ts.factory.createTemplateHead(head, head), others.map((s, i) => ts.factory.createTemplateSpan(toExpression(nodes[i]), i < others.length - 1 ? ts.factory.createTemplateMiddle(s, s) : ts.factory.createTemplateTail(s, s)))));
314
+ }
315
+ return ts.factory.createTaggedTemplateExpression(tag, undefined, ts.factory.createNoSubstitutionTemplateLiteral(head, head));
237
316
  }
238
317
  function toExpression(value) {
239
318
  if (typeof value === "string") {
@@ -249,7 +328,9 @@ function toExpression(value) {
249
328
  return ts.factory.createArrayLiteralExpression(value.map(toExpression));
250
329
  } else if (typeof value === "object") {
251
330
  if (isValFileValue(value)) {
252
- return createValFileReference(value[FILE_REF_PROP]);
331
+ return createValFileReference(value);
332
+ } else if (isValRichTextValue(value)) {
333
+ return createValRichTextTaggedStringTemplate(value);
253
334
  }
254
335
  return ts.factory.createObjectLiteralExpression(Object.entries(value).map(([key, value]) => createPropertyAssignment(key, value)));
255
336
  } else {
@@ -476,7 +557,14 @@ function removeAtPath(document, rootNode, path) {
476
557
  return pipe(getPointerFromPath(rootNode, path), result.flatMap(([node, key]) => removeFromNode(document, node, key)));
477
558
  }
478
559
  function isValFileValue(value) {
479
- return !!(typeof value === "object" && value && FILE_REF_PROP in value && typeof value[FILE_REF_PROP] === "string");
560
+ return !!(typeof value === "object" && value &&
561
+ // TODO: replace the below with this:
562
+ // VAL_EXTENSION in value &&
563
+ // value[VAL_EXTENSION] === "file" &&
564
+ FILE_REF_PROP in value && typeof value[FILE_REF_PROP] === "string");
565
+ }
566
+ function isValRichTextValue(value) {
567
+ return !!(typeof value === "object" && value && VAL_EXTENSION in value && value[VAL_EXTENSION] === "richtext" && "children" in value && typeof value.children === "object" && Array.isArray(value.children));
480
568
  }
481
569
  function addToNode(document, node, key, value) {
482
570
  if (ts.isArrayLiteralExpression(node)) {
@@ -660,7 +748,12 @@ const patchValFile = async (id, valConfigPath, patch, sourceFileHandler, runtime
660
748
  // Buffer.from(content, "base64").toString("binary")
661
749
  // );
662
750
  // sourceFileHandler.moveFile(tempFilePath, "." + filePath);
663
- sourceFileHandler.writeFile("." + filePath, convertDataUrlToBase64(content).toString("binary"), "binary");
751
+ // TODO: ensure that directory exists
752
+ if (content.startsWith("data:/image/svg+xml")) {
753
+ sourceFileHandler.writeFile("." + filePath, convertDataUrlToBase64(content).toString("utf8"), "utf8");
754
+ } else {
755
+ sourceFileHandler.writeFile("." + filePath, convertDataUrlToBase64(content).toString("binary"), "binary");
756
+ }
664
757
  }
665
758
  for (const [ref, patch] of Object.entries(derefRes.value.remotePatches)) {
666
759
  throw Error(`Cannot update remote ${ref} with ${JSON.stringify(patch)}: not implemented`);
@@ -742,7 +835,7 @@ class ValSourceFileHandler {
742
835
 
743
836
  const JsFileLookupMapping = [
744
837
  // NOTE: first one matching will be used
745
- [".cjs.d.ts", [".esm.js", ".mjs.js"]], [".cjs.js", [".esm.js", ".mjs.js"]], [".cjs", [".mjs"]], [".d.ts", [".js"]]];
838
+ [".cjs.d.ts", [".esm.js", ".mjs.js"]], [".cjs.js", [".esm.js", ".mjs.js"]], [".cjs", [".mjs"]], [".d.ts", [".js", ".esm.js", ".mjs.js"]]];
746
839
  class ValModuleLoader {
747
840
  constructor(projectRoot, compilerOptions, sourceFileHandler, host = {
748
841
  ...ts.sys,
@@ -1007,6 +1100,10 @@ const OperationJSONT = z.discriminatedUnion("op", [z.object({
1007
1100
  op: z.literal("test"),
1008
1101
  path: z.string(),
1009
1102
  value: JSONValueT
1103
+ }).strict(), z.object({
1104
+ op: z.literal("file"),
1105
+ path: z.string(),
1106
+ value: z.string()
1010
1107
  }).strict()]);
1011
1108
  const PatchJSON = z.array(OperationJSONT);
1012
1109
 
@@ -1741,7 +1838,7 @@ async function createFixPatch(config, apply, sourcePath, validationError) {
1741
1838
  }
1742
1839
  const localFile = path__default.join(config.projectRoot, maybeRef);
1743
1840
  const buffer = fs.readFileSync(localFile);
1744
- const sha256 = await getSHA256Hash(buffer);
1841
+ const sha256 = await Internal.getSHA256Hash(buffer);
1745
1842
  const imageSize = sizeOf(buffer);
1746
1843
  return {
1747
1844
  ...imageSize,
@@ -1833,11 +1930,5 @@ async function createFixPatch(config, apply, sourcePath, validationError) {
1833
1930
  remainingErrors
1834
1931
  };
1835
1932
  }
1836
- const getSHA256Hash = async bits => {
1837
- const hashBuffer = await crypto.subtle.digest("SHA-256", bits);
1838
- const hashArray = Array.from(new Uint8Array(hashBuffer));
1839
- const hash = hashArray.map(item => item.toString(16).padStart(2, "0")).join("");
1840
- return hash;
1841
- };
1842
1933
 
1843
1934
  export { LocalValServer, PatchJSON, Service, ValFSHost, ValModuleLoader, ValSourceFileHandler, createFixPatch, createRequestHandler, createRequestListener, createService, decodeJwt, encodeJwt, formatSyntaxErrorTree, getCompilerOptions, getExpire, patchSourceFile, safeReadGit };
package/package.json CHANGED
@@ -12,7 +12,7 @@
12
12
  "./package.json": "./package.json"
13
13
  },
14
14
  "types": "dist/valbuild-server.cjs.d.ts",
15
- "version": "0.17.0",
15
+ "version": "0.19.0",
16
16
  "scripts": {
17
17
  "typecheck": "tsc --noEmit",
18
18
  "test": "jest",
@@ -25,8 +25,8 @@
25
25
  "concurrently": "^7.6.0"
26
26
  },
27
27
  "dependencies": {
28
- "@valbuild/core": "~0.17.0",
29
- "@valbuild/ui": "~0.17.0",
28
+ "@valbuild/core": "~0.19.0",
29
+ "@valbuild/ui": "~0.19.0",
30
30
  "express": "^4.18.2",
31
31
  "image-size": "^1.0.2",
32
32
  "quickjs-emscripten": "^0.21.1",
@@ -10,7 +10,7 @@ const JsFileLookupMapping: [resolvedFileExt: string, replacements: string[]][] =
10
10
  [".cjs.d.ts", [".esm.js", ".mjs.js"]],
11
11
  [".cjs.js", [".esm.js", ".mjs.js"]],
12
12
  [".cjs", [".mjs"]],
13
- [".d.ts", [".js"]],
13
+ [".d.ts", [".js", ".esm.js", ".mjs.js"]],
14
14
  ];
15
15
 
16
16
  export const createModuleLoader = (
@@ -1,9 +1,13 @@
1
- import { FILE_REF_PROP, SourcePath, ValidationError } from "@valbuild/core";
1
+ import {
2
+ FILE_REF_PROP,
3
+ Internal,
4
+ SourcePath,
5
+ ValidationError,
6
+ } from "@valbuild/core";
2
7
  import { Patch, sourceToPatchPath } from "@valbuild/core/patch";
3
8
  import sizeOf from "image-size";
4
9
  import path from "path";
5
10
  import fs from "fs";
6
- import crypto from "crypto";
7
11
 
8
12
  // TODO: find a better name? transformFixesToPatch?
9
13
  export async function createFixPatch(
@@ -27,7 +31,7 @@ export async function createFixPatch(
27
31
  }
28
32
  const localFile = path.join(config.projectRoot, maybeRef);
29
33
  const buffer = fs.readFileSync(localFile);
30
- const sha256 = await getSHA256Hash(buffer);
34
+ const sha256 = await Internal.getSHA256Hash(buffer);
31
35
  const imageSize = sizeOf(buffer);
32
36
  return {
33
37
  ...imageSize,
@@ -164,12 +168,3 @@ export async function createFixPatch(
164
168
  remainingErrors,
165
169
  };
166
170
  }
167
-
168
- const getSHA256Hash = async (bits: Uint8Array) => {
169
- const hashBuffer = await crypto.subtle.digest("SHA-256", bits);
170
- const hashArray = Array.from(new Uint8Array(hashBuffer));
171
- const hash = hashArray
172
- .map((item) => item.toString(16).padStart(2, "0"))
173
- .join("");
174
- return hash;
175
- };
@@ -3,6 +3,7 @@ import { TSOps } from "./ops";
3
3
  import { result, array, pipe } from "@valbuild/core/fp";
4
4
  import { PatchError, JSONValue } from "@valbuild/core/patch";
5
5
  import { ValSyntaxError } from "./syntax";
6
+ import { FILE_REF_PROP, VAL_EXTENSION } from "@valbuild/core";
6
7
 
7
8
  function testSourceFile(expression: string): ts.SourceFile {
8
9
  return ts.createSourceFile(
@@ -449,6 +450,106 @@ describe("TSOps", () => {
449
450
  value: null,
450
451
  expected: result.err(PatchError),
451
452
  },
453
+ {
454
+ name: "val.richtext basic no nodes",
455
+ input: "val.richtext`Test`",
456
+ path: [],
457
+ value: {
458
+ [VAL_EXTENSION]: "richtext",
459
+ children: [{ tag: "p", children: ["Test 2"] }],
460
+ },
461
+ expected: result.ok("val.richtext `Test 2`"),
462
+ },
463
+ {
464
+ name: "val.richtext advanced no nodes",
465
+ input: "val.richtext`Test`",
466
+ path: [],
467
+ value: {
468
+ [VAL_EXTENSION]: "richtext",
469
+ children: [
470
+ { tag: "h1", children: ["Title 1"] },
471
+ { tag: "p", children: ["Test 2"] },
472
+ ],
473
+ },
474
+ expected: result.ok(`val.richtext \`# Title 1
475
+
476
+ Test 2\``),
477
+ },
478
+ {
479
+ name: "val.richtext basic nodes",
480
+ input: "val.richtext`Test`",
481
+ path: [],
482
+ value: {
483
+ [VAL_EXTENSION]: "richtext",
484
+ children: [
485
+ { tag: "h1", children: ["Title 1"] },
486
+ { tag: "p", children: ["Test 2"] },
487
+ {
488
+ [VAL_EXTENSION]: "file",
489
+ [FILE_REF_PROP]: "/public/test",
490
+ metadata: { width: 100, height: 100, sha256: "123" },
491
+ },
492
+ ],
493
+ },
494
+ expected: result.ok(`val.richtext \`# Title 1
495
+
496
+ Test 2
497
+
498
+ \${val.file("/public/test", { width: 100, height: 100, sha256: "123" })}\``),
499
+ },
500
+ {
501
+ name: "val.richtext advanced nodes",
502
+ input: "val.richtext`Test`",
503
+ path: [],
504
+ value: {
505
+ [VAL_EXTENSION]: "richtext",
506
+ children: [
507
+ { tag: "h1", children: ["Title 1"] },
508
+ {
509
+ [VAL_EXTENSION]: "file",
510
+ [FILE_REF_PROP]: "/public/test1",
511
+ metadata: { width: 100, height: 100, sha256: "123" },
512
+ },
513
+ {
514
+ [VAL_EXTENSION]: "file",
515
+ [FILE_REF_PROP]: "/public/test2",
516
+ metadata: { width: 100, height: 100, sha256: "123" },
517
+ },
518
+ { tag: "p", children: ["Test 2"] },
519
+ ],
520
+ },
521
+ expected: result.ok(`val.richtext \`# Title 1
522
+
523
+ \${val.file("/public/test1", { width: 100, height: 100, sha256: "123" })}
524
+ \${val.file("/public/test2", { width: 100, height: 100, sha256: "123" })}
525
+ Test 2\``),
526
+ },
527
+ {
528
+ name: "val.richtext empty head",
529
+ input:
530
+ "{ text: val.richtext`${val.file('/public/test1', { height: 100 })}` }",
531
+ path: ["text"],
532
+ value: {
533
+ [VAL_EXTENSION]: "richtext",
534
+ children: [
535
+ {
536
+ [VAL_EXTENSION]: "file",
537
+ [FILE_REF_PROP]: "/public/test1",
538
+ metadata: { width: 100, height: 100, sha256: "123" },
539
+ },
540
+ {
541
+ [VAL_EXTENSION]: "file",
542
+ [FILE_REF_PROP]: "/public/test2",
543
+ metadata: { width: 100, height: 100, sha256: "123" },
544
+ },
545
+ { tag: "p", children: ["Test 2"] },
546
+ ],
547
+ },
548
+ expected:
549
+ result.ok(`{ text: val.richtext \`\${val.file("/public/test1", { width: 100, height: 100, sha256: "123" })}
550
+ \${val.file("/public/test2", { width: 100, height: 100, sha256: "123" })}
551
+ Test 2\` }`),
552
+ },
452
553
  ])("replace $name", ({ input, path, value, expected }) => {
453
554
  const src = testSourceFile(input);
454
555
  const ops = new TSOps(findRoot);
@@ -18,8 +18,15 @@ import {
18
18
  JSONValue,
19
19
  parseAndValidateArrayIndex,
20
20
  } from "@valbuild/core/patch";
21
- import { FILE_REF_PROP, FileSource } from "@valbuild/core";
21
+ import {
22
+ AnyRichTextOptions,
23
+ FILE_REF_PROP,
24
+ FileSource,
25
+ RichTextSource,
26
+ VAL_EXTENSION,
27
+ } from "@valbuild/core";
22
28
  import { JsonPrimitive } from "@valbuild/core/src/Json";
29
+ import { richTextToTaggedStringTemplate } from "./richtext";
23
30
 
24
31
  type TSOpsResult<T> = result.Result<T, PatchError | ValSyntaxErrorTree>;
25
32
 
@@ -60,14 +67,53 @@ function createPropertyAssignment(key: string, value: JSONValue) {
60
67
  );
61
68
  }
62
69
 
63
- function createValFileReference(ref: string) {
70
+ function createValFileReference(value: FileSource) {
71
+ const args: ts.Expression[] = [
72
+ ts.factory.createStringLiteral(value[FILE_REF_PROP]),
73
+ ];
74
+ if (value.metadata) {
75
+ args.push(toExpression(value.metadata));
76
+ }
77
+
64
78
  return ts.factory.createCallExpression(
65
79
  ts.factory.createPropertyAccessExpression(
66
80
  ts.factory.createIdentifier("val"),
67
81
  ts.factory.createIdentifier("file")
68
82
  ),
69
83
  undefined,
70
- [ts.factory.createStringLiteral(ref)]
84
+ args
85
+ );
86
+ }
87
+
88
+ function createValRichTextTaggedStringTemplate(
89
+ value: RichTextSource<AnyRichTextOptions>
90
+ ): ts.Expression {
91
+ const [[head, ...others], nodes] = richTextToTaggedStringTemplate(value);
92
+ const tag = ts.factory.createPropertyAccessExpression(
93
+ ts.factory.createIdentifier("val"),
94
+ ts.factory.createIdentifier("richtext")
95
+ );
96
+ if (nodes.length > 0) {
97
+ return ts.factory.createTaggedTemplateExpression(
98
+ tag,
99
+ undefined,
100
+ ts.factory.createTemplateExpression(
101
+ ts.factory.createTemplateHead(head, head),
102
+ others.map((s, i) =>
103
+ ts.factory.createTemplateSpan(
104
+ toExpression(nodes[i]),
105
+ i < others.length - 1
106
+ ? ts.factory.createTemplateMiddle(s, s)
107
+ : ts.factory.createTemplateTail(s, s)
108
+ )
109
+ )
110
+ )
111
+ );
112
+ }
113
+ return ts.factory.createTaggedTemplateExpression(
114
+ tag,
115
+ undefined,
116
+ ts.factory.createNoSubstitutionTemplateLiteral(head, head)
71
117
  );
72
118
  }
73
119
 
@@ -85,7 +131,9 @@ function toExpression(value: JSONValue): ts.Expression {
85
131
  return ts.factory.createArrayLiteralExpression(value.map(toExpression));
86
132
  } else if (typeof value === "object") {
87
133
  if (isValFileValue(value)) {
88
- return createValFileReference(value[FILE_REF_PROP]);
134
+ return createValFileReference(value);
135
+ } else if (isValRichTextValue(value)) {
136
+ return createValRichTextTaggedStringTemplate(value);
89
137
  }
90
138
  return ts.factory.createObjectLiteralExpression(
91
139
  Object.entries(value).map(([key, value]) =>
@@ -549,11 +597,28 @@ function isValFileValue(value: JSONValue): value is FileSource<{
549
597
  return !!(
550
598
  typeof value === "object" &&
551
599
  value &&
600
+ // TODO: replace the below with this:
601
+ // VAL_EXTENSION in value &&
602
+ // value[VAL_EXTENSION] === "file" &&
552
603
  FILE_REF_PROP in value &&
553
604
  typeof value[FILE_REF_PROP] === "string"
554
605
  );
555
606
  }
556
607
 
608
+ function isValRichTextValue(
609
+ value: JSONValue
610
+ ): value is RichTextSource<AnyRichTextOptions> {
611
+ return !!(
612
+ typeof value === "object" &&
613
+ value &&
614
+ VAL_EXTENSION in value &&
615
+ value[VAL_EXTENSION] === "richtext" &&
616
+ "children" in value &&
617
+ typeof value.children === "object" &&
618
+ Array.isArray(value.children)
619
+ );
620
+ }
621
+
557
622
  function addToNode(
558
623
  document: ts.SourceFile,
559
624
  node: ts.Expression,
@@ -0,0 +1,74 @@
1
+ import { initVal } from "@valbuild/core";
2
+ import { richTextToTaggedStringTemplate } from "./richtext";
3
+
4
+ describe("patch richtext", () => {
5
+ test("basic richtext <-> markdown", () => {
6
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
7
+ const input: any = {
8
+ _type: "richtext",
9
+ children: [
10
+ { tag: "h1", children: ["Title 1"] },
11
+ {
12
+ tag: "p",
13
+ children: [
14
+ {
15
+ tag: "span",
16
+ classes: ["bold"],
17
+ children: ["Bold text"],
18
+ },
19
+ ],
20
+ },
21
+ { _type: "file", _ref: "/public/image.png" },
22
+ { tag: "p", children: ["Paragraph 2"] },
23
+ ],
24
+ };
25
+ const r = richTextToTaggedStringTemplate(input);
26
+ expect(r).toStrictEqual([
27
+ ["# Title 1\n\n**Bold text**\n\n", "\nParagraph 2"],
28
+ [{ _type: "file", _ref: "/public/image.png" }],
29
+ ]);
30
+
31
+ const { val } = initVal();
32
+ const r2 = val.richtext(
33
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
34
+ // @ts-expect-error
35
+ r[0],
36
+ ...r[1]
37
+ );
38
+ expect(r2).toStrictEqual(input);
39
+ });
40
+
41
+ test("richtext <-> markdown", () => {
42
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
43
+ const input: any = {
44
+ _type: "richtext",
45
+ children: [
46
+ { tag: "h1", children: ["Title 1"] },
47
+ { tag: "h2", children: ["Title 2"] },
48
+ {
49
+ tag: "p",
50
+ children: [
51
+ "Test 1 2 3. ",
52
+ {
53
+ tag: "span",
54
+ classes: ["bold"],
55
+ children: ["Bold text"],
56
+ },
57
+ ". More text. See image below",
58
+ ],
59
+ },
60
+ { _type: "file", _ref: "/public/image.png" },
61
+ { tag: "p", children: ["Paragraph 2"] },
62
+ ],
63
+ };
64
+ const r = richTextToTaggedStringTemplate(input);
65
+ const { val } = initVal();
66
+ const r2 = val.richtext(
67
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
68
+ // @ts-expect-error
69
+ r[0],
70
+ ...r[1]
71
+ );
72
+ expect(r2).toStrictEqual(input);
73
+ });
74
+ });
@@ -0,0 +1,73 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import { AnyRichTextOptions, RichTextSource } from "@valbuild/core";
3
+
4
+ const HeaderRegEx = /h([\d])/;
5
+
6
+ export function richTextToTaggedStringTemplate(
7
+ source: RichTextSource<AnyRichTextOptions>
8
+ ) {
9
+ const texts: string[] = [""];
10
+ const nodes: any[] = [];
11
+ let didAppendNewLines = false;
12
+
13
+ function rec(node: any) {
14
+ if (typeof node === "string") {
15
+ texts[texts.length - 1] += node;
16
+ } else if (node.tag) {
17
+ if (node.tag?.startsWith("h")) {
18
+ const [, depth] = node.tag.match(HeaderRegEx);
19
+ for (let i = 0; i < Number(depth); i++) {
20
+ texts[texts.length - 1] += "#";
21
+ }
22
+ texts[texts.length - 1] += " ";
23
+ } else if (node.tag === "span") {
24
+ if (node.classes.includes("bold") && !node.classes.includes("italic")) {
25
+ texts[texts.length - 1] += "**";
26
+ }
27
+ if (node.classes.includes("italic") && !node.classes.includes("bold")) {
28
+ texts[texts.length - 1] += "*";
29
+ }
30
+ if (node.classes.includes("italic") && node.classes.includes("bold")) {
31
+ texts[texts.length - 1] += "***";
32
+ }
33
+ if (node.classes.includes("line-through")) {
34
+ texts[texts.length - 1] += "~~";
35
+ }
36
+ }
37
+
38
+ node.children?.forEach(rec);
39
+
40
+ if (node.tag === "span") {
41
+ didAppendNewLines = false;
42
+ if (node.classes.includes("line-through")) {
43
+ texts[texts.length - 1] += "~~";
44
+ }
45
+ if (node.classes.includes("italic") && node.classes.includes("bold")) {
46
+ texts[texts.length - 1] += "***";
47
+ }
48
+ if (node.classes.includes("italic") && !node.classes.includes("bold")) {
49
+ texts[texts.length - 1] += "*";
50
+ }
51
+ if (node.classes.includes("bold") && !node.classes.includes("italic")) {
52
+ texts[texts.length - 1] += "**";
53
+ }
54
+ } else if (node.tag === "p") {
55
+ didAppendNewLines = true;
56
+ texts[texts.length - 1] += "\n\n";
57
+ } else if (node.tag?.startsWith("h")) {
58
+ didAppendNewLines = true;
59
+ texts[texts.length - 1] += "\n\n";
60
+ }
61
+ } else {
62
+ nodes.push(node);
63
+ texts.push("\n");
64
+ }
65
+ }
66
+ source.children.forEach(rec);
67
+
68
+ if (texts[texts.length - 1] && didAppendNewLines) {
69
+ // remove last \n\n
70
+ texts[texts.length - 1] = texts[texts.length - 1].slice(0, -2);
71
+ }
72
+ return [texts, nodes];
73
+ }
@@ -67,6 +67,13 @@ const OperationJSONT: z.ZodType<OperationJSONT> = z.discriminatedUnion("op", [
67
67
  value: JSONValueT,
68
68
  })
69
69
  .strict(),
70
+ z
71
+ .object({
72
+ op: z.literal("file"),
73
+ path: z.string(),
74
+ value: z.string(),
75
+ })
76
+ .strict(),
70
77
  ]);
71
78
 
72
79
  export const PatchJSON: z.ZodType<PatchJSONT> = z.array(OperationJSONT);
@@ -69,11 +69,20 @@ export const patchValFile = async (
69
69
  // Buffer.from(content, "base64").toString("binary")
70
70
  // );
71
71
  // sourceFileHandler.moveFile(tempFilePath, "." + filePath);
72
- sourceFileHandler.writeFile(
73
- "." + filePath,
74
- convertDataUrlToBase64(content).toString("binary"),
75
- "binary"
76
- );
72
+ // TODO: ensure that directory exists
73
+ if (content.startsWith("data:/image/svg+xml")) {
74
+ sourceFileHandler.writeFile(
75
+ "." + filePath,
76
+ convertDataUrlToBase64(content).toString("utf8"),
77
+ "utf8"
78
+ );
79
+ } else {
80
+ sourceFileHandler.writeFile(
81
+ "." + filePath,
82
+ convertDataUrlToBase64(content).toString("binary"),
83
+ "binary"
84
+ );
85
+ }
77
86
  }
78
87
 
79
88
  for (const [ref, patch] of Object.entries(derefRes.value.remotePatches)) {
package/tsconfig.json CHANGED
@@ -5,7 +5,8 @@
5
5
  "noEmit": true,
6
6
  "esModuleInterop": true,
7
7
  "module": "esnext",
8
- "moduleResolution": "node"
8
+ "moduleResolution": "node",
9
+ "skipLibCheck": true
9
10
  },
10
11
  "exclude": ["test/**/*"]
11
12
  }