@lvce-editor/preview-worker 1.2.0 → 1.4.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.
- package/dist/previewWorkerMain.js +1054 -23
- package/package.json +1 -1
|
@@ -54,6 +54,55 @@ class VError extends Error {
|
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
class AssertionError extends Error {
|
|
58
|
+
constructor(message) {
|
|
59
|
+
super(message);
|
|
60
|
+
this.name = 'AssertionError';
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const Object$1 = 1;
|
|
64
|
+
const Number$1 = 2;
|
|
65
|
+
const Array$1 = 3;
|
|
66
|
+
const String = 4;
|
|
67
|
+
const Boolean = 5;
|
|
68
|
+
const Function = 6;
|
|
69
|
+
const Null = 7;
|
|
70
|
+
const Unknown = 8;
|
|
71
|
+
const getType = value => {
|
|
72
|
+
switch (typeof value) {
|
|
73
|
+
case 'number':
|
|
74
|
+
return Number$1;
|
|
75
|
+
case 'function':
|
|
76
|
+
return Function;
|
|
77
|
+
case 'string':
|
|
78
|
+
return String;
|
|
79
|
+
case 'object':
|
|
80
|
+
if (value === null) {
|
|
81
|
+
return Null;
|
|
82
|
+
}
|
|
83
|
+
if (Array.isArray(value)) {
|
|
84
|
+
return Array$1;
|
|
85
|
+
}
|
|
86
|
+
return Object$1;
|
|
87
|
+
case 'boolean':
|
|
88
|
+
return Boolean;
|
|
89
|
+
default:
|
|
90
|
+
return Unknown;
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
const array = value => {
|
|
94
|
+
const type = getType(value);
|
|
95
|
+
if (type !== Array$1) {
|
|
96
|
+
throw new AssertionError('expected value to be of type array');
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
const string = value => {
|
|
100
|
+
const type = getType(value);
|
|
101
|
+
if (type !== String) {
|
|
102
|
+
throw new AssertionError('expected value to be of type string');
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
57
106
|
const isMessagePort = value => {
|
|
58
107
|
return value && value instanceof MessagePort;
|
|
59
108
|
};
|
|
@@ -368,6 +417,100 @@ const IpcChildWithModuleWorkerAndMessagePort$1 = {
|
|
|
368
417
|
listen: listen$6,
|
|
369
418
|
wrap: wrap$e
|
|
370
419
|
};
|
|
420
|
+
const addListener = (emitter, type, callback) => {
|
|
421
|
+
if ('addEventListener' in emitter) {
|
|
422
|
+
emitter.addEventListener(type, callback);
|
|
423
|
+
} else {
|
|
424
|
+
emitter.on(type, callback);
|
|
425
|
+
}
|
|
426
|
+
};
|
|
427
|
+
const removeListener = (emitter, type, callback) => {
|
|
428
|
+
if ('removeEventListener' in emitter) {
|
|
429
|
+
emitter.removeEventListener(type, callback);
|
|
430
|
+
} else {
|
|
431
|
+
emitter.off(type, callback);
|
|
432
|
+
}
|
|
433
|
+
};
|
|
434
|
+
const getFirstEvent = (eventEmitter, eventMap) => {
|
|
435
|
+
const {
|
|
436
|
+
promise,
|
|
437
|
+
resolve
|
|
438
|
+
} = Promise.withResolvers();
|
|
439
|
+
const listenerMap = Object.create(null);
|
|
440
|
+
const cleanup = value => {
|
|
441
|
+
for (const event of Object.keys(eventMap)) {
|
|
442
|
+
removeListener(eventEmitter, event, listenerMap[event]);
|
|
443
|
+
}
|
|
444
|
+
resolve(value);
|
|
445
|
+
};
|
|
446
|
+
for (const [event, type] of Object.entries(eventMap)) {
|
|
447
|
+
const listener = event => {
|
|
448
|
+
cleanup({
|
|
449
|
+
event,
|
|
450
|
+
type
|
|
451
|
+
});
|
|
452
|
+
};
|
|
453
|
+
addListener(eventEmitter, event, listener);
|
|
454
|
+
listenerMap[event] = listener;
|
|
455
|
+
}
|
|
456
|
+
return promise;
|
|
457
|
+
};
|
|
458
|
+
const Message$1 = 3;
|
|
459
|
+
const create$5$1 = async ({
|
|
460
|
+
isMessagePortOpen,
|
|
461
|
+
messagePort
|
|
462
|
+
}) => {
|
|
463
|
+
if (!isMessagePort(messagePort)) {
|
|
464
|
+
throw new IpcError('port must be of type MessagePort');
|
|
465
|
+
}
|
|
466
|
+
if (isMessagePortOpen) {
|
|
467
|
+
return messagePort;
|
|
468
|
+
}
|
|
469
|
+
const eventPromise = getFirstEvent(messagePort, {
|
|
470
|
+
message: Message$1
|
|
471
|
+
});
|
|
472
|
+
messagePort.start();
|
|
473
|
+
const {
|
|
474
|
+
event,
|
|
475
|
+
type
|
|
476
|
+
} = await eventPromise;
|
|
477
|
+
if (type !== Message$1) {
|
|
478
|
+
throw new IpcError('Failed to wait for ipc message');
|
|
479
|
+
}
|
|
480
|
+
if (event.data !== readyMessage) {
|
|
481
|
+
throw new IpcError('unexpected first message');
|
|
482
|
+
}
|
|
483
|
+
return messagePort;
|
|
484
|
+
};
|
|
485
|
+
const signal$1 = messagePort => {
|
|
486
|
+
messagePort.start();
|
|
487
|
+
};
|
|
488
|
+
class IpcParentWithMessagePort extends Ipc {
|
|
489
|
+
getData = getData$2;
|
|
490
|
+
send(message) {
|
|
491
|
+
this._rawIpc.postMessage(message);
|
|
492
|
+
}
|
|
493
|
+
sendAndTransfer(message) {
|
|
494
|
+
const transfer = getTransferrables(message);
|
|
495
|
+
this._rawIpc.postMessage(message, transfer);
|
|
496
|
+
}
|
|
497
|
+
dispose() {
|
|
498
|
+
this._rawIpc.close();
|
|
499
|
+
}
|
|
500
|
+
onMessage(callback) {
|
|
501
|
+
this._rawIpc.addEventListener('message', callback);
|
|
502
|
+
}
|
|
503
|
+
onClose(callback) {}
|
|
504
|
+
}
|
|
505
|
+
const wrap$5 = messagePort => {
|
|
506
|
+
return new IpcParentWithMessagePort(messagePort);
|
|
507
|
+
};
|
|
508
|
+
const IpcParentWithMessagePort$1 = {
|
|
509
|
+
__proto__: null,
|
|
510
|
+
create: create$5$1,
|
|
511
|
+
signal: signal$1,
|
|
512
|
+
wrap: wrap$5
|
|
513
|
+
};
|
|
371
514
|
|
|
372
515
|
const Two$1 = '2.0';
|
|
373
516
|
const callbacks = Object.create(null);
|
|
@@ -583,7 +726,7 @@ const getErrorResponse = (id, error, preparePrettyError, logError) => {
|
|
|
583
726
|
const errorProperty = getErrorProperty(error, prettyError);
|
|
584
727
|
return create$1$1(id, errorProperty);
|
|
585
728
|
};
|
|
586
|
-
const create$
|
|
729
|
+
const create$4 = (message, result) => {
|
|
587
730
|
return {
|
|
588
731
|
jsonrpc: Two$1,
|
|
589
732
|
id: message.id,
|
|
@@ -592,7 +735,7 @@ const create$3 = (message, result) => {
|
|
|
592
735
|
};
|
|
593
736
|
const getSuccessResponse = (message, result) => {
|
|
594
737
|
const resultProperty = result ?? null;
|
|
595
|
-
return create$
|
|
738
|
+
return create$4(message, resultProperty);
|
|
596
739
|
};
|
|
597
740
|
const getErrorResponseSimple = (id, error) => {
|
|
598
741
|
return {
|
|
@@ -820,6 +963,86 @@ const listen$1 = async (module, options) => {
|
|
|
820
963
|
const ipc = module.wrap(rawIpc);
|
|
821
964
|
return ipc;
|
|
822
965
|
};
|
|
966
|
+
|
|
967
|
+
/* eslint-disable @typescript-eslint/no-misused-promises */
|
|
968
|
+
|
|
969
|
+
const createSharedLazyRpc = factory => {
|
|
970
|
+
let rpcPromise;
|
|
971
|
+
const getOrCreate = () => {
|
|
972
|
+
if (!rpcPromise) {
|
|
973
|
+
rpcPromise = factory();
|
|
974
|
+
}
|
|
975
|
+
return rpcPromise;
|
|
976
|
+
};
|
|
977
|
+
return {
|
|
978
|
+
async dispose() {
|
|
979
|
+
const rpc = await getOrCreate();
|
|
980
|
+
await rpc.dispose();
|
|
981
|
+
},
|
|
982
|
+
async invoke(method, ...params) {
|
|
983
|
+
const rpc = await getOrCreate();
|
|
984
|
+
return rpc.invoke(method, ...params);
|
|
985
|
+
},
|
|
986
|
+
async invokeAndTransfer(method, ...params) {
|
|
987
|
+
const rpc = await getOrCreate();
|
|
988
|
+
return rpc.invokeAndTransfer(method, ...params);
|
|
989
|
+
},
|
|
990
|
+
async send(method, ...params) {
|
|
991
|
+
const rpc = await getOrCreate();
|
|
992
|
+
rpc.send(method, ...params);
|
|
993
|
+
}
|
|
994
|
+
};
|
|
995
|
+
};
|
|
996
|
+
const create$j = async ({
|
|
997
|
+
commandMap,
|
|
998
|
+
isMessagePortOpen,
|
|
999
|
+
send
|
|
1000
|
+
}) => {
|
|
1001
|
+
return createSharedLazyRpc(() => {
|
|
1002
|
+
return create$3({
|
|
1003
|
+
commandMap,
|
|
1004
|
+
isMessagePortOpen,
|
|
1005
|
+
send
|
|
1006
|
+
});
|
|
1007
|
+
});
|
|
1008
|
+
};
|
|
1009
|
+
const LazyTransferMessagePortRpcParent = {
|
|
1010
|
+
__proto__: null,
|
|
1011
|
+
create: create$j
|
|
1012
|
+
};
|
|
1013
|
+
const create$5 = async ({
|
|
1014
|
+
commandMap,
|
|
1015
|
+
isMessagePortOpen = true,
|
|
1016
|
+
messagePort
|
|
1017
|
+
}) => {
|
|
1018
|
+
// TODO create a commandMap per rpc instance
|
|
1019
|
+
register(commandMap);
|
|
1020
|
+
const rawIpc = await IpcParentWithMessagePort$1.create({
|
|
1021
|
+
isMessagePortOpen,
|
|
1022
|
+
messagePort
|
|
1023
|
+
});
|
|
1024
|
+
const ipc = IpcParentWithMessagePort$1.wrap(rawIpc);
|
|
1025
|
+
handleIpc(ipc);
|
|
1026
|
+
const rpc = createRpc(ipc);
|
|
1027
|
+
messagePort.start();
|
|
1028
|
+
return rpc;
|
|
1029
|
+
};
|
|
1030
|
+
const create$3 = async ({
|
|
1031
|
+
commandMap,
|
|
1032
|
+
isMessagePortOpen,
|
|
1033
|
+
send
|
|
1034
|
+
}) => {
|
|
1035
|
+
const {
|
|
1036
|
+
port1,
|
|
1037
|
+
port2
|
|
1038
|
+
} = new MessageChannel();
|
|
1039
|
+
await send(port1);
|
|
1040
|
+
return create$5({
|
|
1041
|
+
commandMap,
|
|
1042
|
+
isMessagePortOpen,
|
|
1043
|
+
messagePort: port2
|
|
1044
|
+
});
|
|
1045
|
+
};
|
|
823
1046
|
const create$2$1 = async ({
|
|
824
1047
|
commandMap
|
|
825
1048
|
}) => {
|
|
@@ -854,20 +1077,68 @@ const createMockRpc = ({
|
|
|
854
1077
|
return mockRpc;
|
|
855
1078
|
};
|
|
856
1079
|
|
|
857
|
-
const
|
|
858
|
-
const
|
|
859
|
-
const
|
|
1080
|
+
const Button$1 = 1;
|
|
1081
|
+
const Div$1 = 4;
|
|
1082
|
+
const H1$1 = 5;
|
|
1083
|
+
const Input$1 = 6;
|
|
1084
|
+
const Span$1 = 8;
|
|
1085
|
+
const Table$1 = 9;
|
|
1086
|
+
const TBody$1 = 10;
|
|
1087
|
+
const Td$1 = 11;
|
|
1088
|
+
const Text$1 = 12;
|
|
1089
|
+
const Th$1 = 13;
|
|
1090
|
+
const THead$1 = 14;
|
|
1091
|
+
const Tr$1 = 15;
|
|
1092
|
+
const Img$1 = 17;
|
|
1093
|
+
const Del$1 = 21;
|
|
1094
|
+
const H2$1 = 22;
|
|
1095
|
+
const H3$1 = 23;
|
|
1096
|
+
const H4$1 = 24;
|
|
1097
|
+
const H5$1 = 25;
|
|
1098
|
+
const H6$1 = 26;
|
|
1099
|
+
const Article$1 = 27;
|
|
1100
|
+
const Aside$1 = 28;
|
|
1101
|
+
const Footer$1 = 29;
|
|
1102
|
+
const Header$1 = 30;
|
|
1103
|
+
const Nav$1 = 40;
|
|
1104
|
+
const Section$1 = 41;
|
|
1105
|
+
const Search$1 = 42;
|
|
1106
|
+
const Dd$1 = 43;
|
|
1107
|
+
const Dl$1 = 44;
|
|
1108
|
+
const Figcaption$1 = 45;
|
|
1109
|
+
const Figure$1 = 46;
|
|
1110
|
+
const Hr$1 = 47;
|
|
1111
|
+
const Li$1 = 48;
|
|
1112
|
+
const Ol$1 = 49;
|
|
1113
|
+
const P$1 = 50;
|
|
1114
|
+
const Pre$1 = 51;
|
|
1115
|
+
const A$1 = 53;
|
|
1116
|
+
const Abbr$1 = 54;
|
|
1117
|
+
const Br$1 = 55;
|
|
1118
|
+
const Cite$1 = 56;
|
|
1119
|
+
const Data$1 = 57;
|
|
1120
|
+
const Time$1 = 58;
|
|
1121
|
+
const Tfoot$1 = 59;
|
|
1122
|
+
const Ul$1 = 60;
|
|
1123
|
+
const TextArea$1 = 62;
|
|
1124
|
+
const Select$1 = 63;
|
|
1125
|
+
const Option$1 = 64;
|
|
1126
|
+
const Code$1 = 65;
|
|
1127
|
+
const Label$1 = 66;
|
|
1128
|
+
const Dt$1 = 67;
|
|
1129
|
+
const Iframe$1 = 68;
|
|
860
1130
|
const Reference = 100;
|
|
861
1131
|
|
|
862
1132
|
const TargetName = 'event.target.name';
|
|
863
1133
|
|
|
1134
|
+
const EditorWorker = 99;
|
|
864
1135
|
const RendererWorker = 1;
|
|
865
1136
|
|
|
866
1137
|
const SetDom2 = 'Viewlet.setDom2';
|
|
867
1138
|
const SetPatches = 'Viewlet.setPatches';
|
|
868
1139
|
|
|
869
1140
|
const rpcs = Object.create(null);
|
|
870
|
-
const set$
|
|
1141
|
+
const set$3 = (id, rpc) => {
|
|
871
1142
|
rpcs[id] = rpc;
|
|
872
1143
|
};
|
|
873
1144
|
const get$1 = id => {
|
|
@@ -900,7 +1171,7 @@ const create$2 = rpcId => {
|
|
|
900
1171
|
const mockRpc = createMockRpc({
|
|
901
1172
|
commandMap
|
|
902
1173
|
});
|
|
903
|
-
set$
|
|
1174
|
+
set$3(rpcId, mockRpc);
|
|
904
1175
|
// @ts-ignore
|
|
905
1176
|
mockRpc[Symbol.dispose] = () => {
|
|
906
1177
|
remove(rpcId);
|
|
@@ -909,14 +1180,28 @@ const create$2 = rpcId => {
|
|
|
909
1180
|
return mockRpc;
|
|
910
1181
|
},
|
|
911
1182
|
set(rpc) {
|
|
912
|
-
set$
|
|
1183
|
+
set$3(rpcId, rpc);
|
|
913
1184
|
}
|
|
914
1185
|
};
|
|
915
1186
|
};
|
|
916
1187
|
|
|
917
1188
|
const {
|
|
1189
|
+
invoke: invoke$1,
|
|
1190
|
+
set: set$2
|
|
1191
|
+
} = create$2(EditorWorker);
|
|
1192
|
+
|
|
1193
|
+
const {
|
|
1194
|
+
invoke,
|
|
1195
|
+
invokeAndTransfer,
|
|
918
1196
|
set: set$1
|
|
919
1197
|
} = create$2(RendererWorker);
|
|
1198
|
+
const sendMessagePortToEditorWorker = async (port, rpcId) => {
|
|
1199
|
+
const command = 'HandleMessagePort.handleMessagePort';
|
|
1200
|
+
await invokeAndTransfer('SendMessagePortToExtensionHostWorker.sendMessagePortToEditorWorker', port, command, rpcId);
|
|
1201
|
+
};
|
|
1202
|
+
const readFile = async uri => {
|
|
1203
|
+
return invoke('FileSystem.readFile', uri);
|
|
1204
|
+
};
|
|
920
1205
|
|
|
921
1206
|
const toCommandId = key => {
|
|
922
1207
|
const dotIndex = key.indexOf('.');
|
|
@@ -1010,6 +1295,7 @@ const terminate = () => {
|
|
|
1010
1295
|
const {
|
|
1011
1296
|
get,
|
|
1012
1297
|
getCommandIds,
|
|
1298
|
+
getKeys: getKeys$1,
|
|
1013
1299
|
registerCommands,
|
|
1014
1300
|
set,
|
|
1015
1301
|
wrapCommand,
|
|
@@ -1021,8 +1307,10 @@ const create = (uid, uri, x, y, width, height, platform, assetDir) => {
|
|
|
1021
1307
|
assetDir,
|
|
1022
1308
|
content: '',
|
|
1023
1309
|
errorCount: 0,
|
|
1310
|
+
errorMessage: '',
|
|
1024
1311
|
initial: true,
|
|
1025
1312
|
parsedDom: [],
|
|
1313
|
+
parsedNodesChildNodeCount: 0,
|
|
1026
1314
|
platform,
|
|
1027
1315
|
uid,
|
|
1028
1316
|
uri,
|
|
@@ -1032,7 +1320,7 @@ const create = (uid, uri, x, y, width, height, platform, assetDir) => {
|
|
|
1032
1320
|
};
|
|
1033
1321
|
|
|
1034
1322
|
const isEqual = (oldState, newState) => {
|
|
1035
|
-
return oldState.warningCount === newState.warningCount && oldState.initial === newState.initial;
|
|
1323
|
+
return oldState.warningCount === newState.warningCount && oldState.initial === newState.initial && oldState.content === newState.content && oldState.parsedDom === newState.parsedDom && oldState.parsedNodesChildNodeCount === newState.parsedNodesChildNodeCount;
|
|
1036
1324
|
};
|
|
1037
1325
|
|
|
1038
1326
|
const RenderItems = 4;
|
|
@@ -1061,12 +1349,11 @@ const diff2 = uid => {
|
|
|
1061
1349
|
return result;
|
|
1062
1350
|
};
|
|
1063
1351
|
|
|
1064
|
-
const
|
|
1352
|
+
const text = data => {
|
|
1065
1353
|
return {
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
warningCount: 1
|
|
1354
|
+
childCount: 0,
|
|
1355
|
+
text: data,
|
|
1356
|
+
type: Text$1
|
|
1070
1357
|
};
|
|
1071
1358
|
};
|
|
1072
1359
|
|
|
@@ -1158,7 +1445,7 @@ const compareNodes = (oldNode, newNode) => {
|
|
|
1158
1445
|
return patches;
|
|
1159
1446
|
}
|
|
1160
1447
|
// Handle text nodes
|
|
1161
|
-
if (oldNode.type === Text && newNode.type === Text) {
|
|
1448
|
+
if (oldNode.type === Text$1 && newNode.type === Text$1) {
|
|
1162
1449
|
if (oldNode.text !== newNode.text) {
|
|
1163
1450
|
patches.push({
|
|
1164
1451
|
type: SetText,
|
|
@@ -1358,33 +1645,747 @@ const diffTree = (oldNodes, newNodes) => {
|
|
|
1358
1645
|
return removeTrailingNavigationPatches(patches);
|
|
1359
1646
|
};
|
|
1360
1647
|
|
|
1648
|
+
const Div = 'div';
|
|
1649
|
+
const H1 = 'h1';
|
|
1650
|
+
const H2 = 'h2';
|
|
1651
|
+
const H3 = 'h3';
|
|
1652
|
+
const H4 = 'h4';
|
|
1653
|
+
const H5 = 'h5';
|
|
1654
|
+
const Img = 'img';
|
|
1655
|
+
const Span = 'span';
|
|
1656
|
+
const Article = 'article';
|
|
1657
|
+
const Aside = 'aside';
|
|
1658
|
+
const Footer = 'footer';
|
|
1659
|
+
const Header = 'header';
|
|
1660
|
+
const Nav = 'nav';
|
|
1661
|
+
const Section = 'section';
|
|
1662
|
+
const Search = 'search';
|
|
1663
|
+
const Dd = 'dd';
|
|
1664
|
+
const Dl = 'dl';
|
|
1665
|
+
const Figcaption = 'figcaption';
|
|
1666
|
+
const Figure = 'figure';
|
|
1667
|
+
const Hr = 'hr';
|
|
1668
|
+
const Li = 'li';
|
|
1669
|
+
const Ol = 'ol';
|
|
1670
|
+
const P = 'p';
|
|
1671
|
+
const Pre = 'pre';
|
|
1672
|
+
const A = 'a';
|
|
1673
|
+
const Abbr = 'abbr';
|
|
1674
|
+
const Br = 'br';
|
|
1675
|
+
const Cite = 'cite';
|
|
1676
|
+
const Data = 'data';
|
|
1677
|
+
const Time = 'time';
|
|
1678
|
+
const Tfoot = 'tfoot';
|
|
1679
|
+
const Button = 'button';
|
|
1680
|
+
const Input = 'input';
|
|
1681
|
+
const Code = 'code';
|
|
1682
|
+
const Form = 'form';
|
|
1683
|
+
const Table = 'table';
|
|
1684
|
+
const TBody = 'tbody';
|
|
1685
|
+
const THead = 'thead';
|
|
1686
|
+
const Tr = 'tr';
|
|
1687
|
+
const Td = 'td';
|
|
1688
|
+
const Th = 'th';
|
|
1689
|
+
const Ul = 'ul';
|
|
1690
|
+
const TextArea = 'textarea';
|
|
1691
|
+
const Select = 'select';
|
|
1692
|
+
const Option = 'option';
|
|
1693
|
+
const Label = 'label';
|
|
1694
|
+
const Dt = 'dt';
|
|
1695
|
+
const Iframe = 'iframe';
|
|
1696
|
+
const Del = 'del';
|
|
1697
|
+
const H6 = 'h6';
|
|
1698
|
+
|
|
1699
|
+
const getVirtualDomTag = text => {
|
|
1700
|
+
switch (text) {
|
|
1701
|
+
case A:
|
|
1702
|
+
return A$1;
|
|
1703
|
+
case Abbr:
|
|
1704
|
+
return Abbr$1;
|
|
1705
|
+
case Article:
|
|
1706
|
+
return Article$1;
|
|
1707
|
+
case Aside:
|
|
1708
|
+
return Aside$1;
|
|
1709
|
+
case Br:
|
|
1710
|
+
return Br$1;
|
|
1711
|
+
case Button:
|
|
1712
|
+
return Button$1;
|
|
1713
|
+
case Cite:
|
|
1714
|
+
return Cite$1;
|
|
1715
|
+
case Code:
|
|
1716
|
+
return Code$1;
|
|
1717
|
+
case Data:
|
|
1718
|
+
return Data$1;
|
|
1719
|
+
case Dd:
|
|
1720
|
+
return Dd$1;
|
|
1721
|
+
case Del:
|
|
1722
|
+
return Del$1;
|
|
1723
|
+
case Div:
|
|
1724
|
+
return Div$1;
|
|
1725
|
+
case Dl:
|
|
1726
|
+
return Dl$1;
|
|
1727
|
+
case Dt:
|
|
1728
|
+
return Dt$1;
|
|
1729
|
+
case Figcaption:
|
|
1730
|
+
return Figcaption$1;
|
|
1731
|
+
case Figure:
|
|
1732
|
+
return Figure$1;
|
|
1733
|
+
case Footer:
|
|
1734
|
+
return Footer$1;
|
|
1735
|
+
case Form:
|
|
1736
|
+
return Div$1;
|
|
1737
|
+
case H1:
|
|
1738
|
+
return H1$1;
|
|
1739
|
+
case H2:
|
|
1740
|
+
return H2$1;
|
|
1741
|
+
case H3:
|
|
1742
|
+
return H3$1;
|
|
1743
|
+
case H4:
|
|
1744
|
+
return H4$1;
|
|
1745
|
+
case H5:
|
|
1746
|
+
return H5$1;
|
|
1747
|
+
case H6:
|
|
1748
|
+
return H6$1;
|
|
1749
|
+
case Header:
|
|
1750
|
+
return Header$1;
|
|
1751
|
+
case Hr:
|
|
1752
|
+
return Hr$1;
|
|
1753
|
+
case Iframe:
|
|
1754
|
+
return Iframe$1;
|
|
1755
|
+
case Img:
|
|
1756
|
+
return Img$1;
|
|
1757
|
+
case Input:
|
|
1758
|
+
return Input$1;
|
|
1759
|
+
case Label:
|
|
1760
|
+
return Label$1;
|
|
1761
|
+
case Li:
|
|
1762
|
+
return Li$1;
|
|
1763
|
+
case Nav:
|
|
1764
|
+
return Nav$1;
|
|
1765
|
+
case Ol:
|
|
1766
|
+
return Ol$1;
|
|
1767
|
+
case Option:
|
|
1768
|
+
return Option$1;
|
|
1769
|
+
case P:
|
|
1770
|
+
return P$1;
|
|
1771
|
+
case Pre:
|
|
1772
|
+
return Pre$1;
|
|
1773
|
+
case Search:
|
|
1774
|
+
return Search$1;
|
|
1775
|
+
case Section:
|
|
1776
|
+
return Section$1;
|
|
1777
|
+
case Select:
|
|
1778
|
+
return Select$1;
|
|
1779
|
+
case Span:
|
|
1780
|
+
return Span$1;
|
|
1781
|
+
case Table:
|
|
1782
|
+
return Table$1;
|
|
1783
|
+
case TBody:
|
|
1784
|
+
return TBody$1;
|
|
1785
|
+
case Td:
|
|
1786
|
+
return Td$1;
|
|
1787
|
+
case TextArea:
|
|
1788
|
+
return TextArea$1;
|
|
1789
|
+
case Tfoot:
|
|
1790
|
+
return Tfoot$1;
|
|
1791
|
+
case Th:
|
|
1792
|
+
return Th$1;
|
|
1793
|
+
case THead:
|
|
1794
|
+
return THead$1;
|
|
1795
|
+
case Time:
|
|
1796
|
+
return Time$1;
|
|
1797
|
+
case Tr:
|
|
1798
|
+
return Tr$1;
|
|
1799
|
+
case Ul:
|
|
1800
|
+
return Ul$1;
|
|
1801
|
+
default:
|
|
1802
|
+
return Div$1;
|
|
1803
|
+
}
|
|
1804
|
+
};
|
|
1805
|
+
|
|
1806
|
+
const None = 0;
|
|
1807
|
+
const OpeningAngleBracket = 1;
|
|
1808
|
+
const ClosingAngleBracket = 2;
|
|
1809
|
+
const TagNameStart = 3;
|
|
1810
|
+
const TagNameEnd = 4;
|
|
1811
|
+
const Content = 5;
|
|
1812
|
+
const ClosingTagSlash = 6;
|
|
1813
|
+
const WhitespaceInsideOpeningTag = 7;
|
|
1814
|
+
const AttributeName = 8;
|
|
1815
|
+
const AttributeEqualSign = 9;
|
|
1816
|
+
const AttributeQuoteStart = 10;
|
|
1817
|
+
const AttributeValue = 11;
|
|
1818
|
+
const AttributeQuoteEnd = 12;
|
|
1819
|
+
const WhitespaceAfterClosingTagSlash = 13;
|
|
1820
|
+
const WhitespaceAfterOpeningTagOpenAngleBracket = 14;
|
|
1821
|
+
const ExclamationMark = 15;
|
|
1822
|
+
const Doctype = 16;
|
|
1823
|
+
const StartCommentDashes = 17;
|
|
1824
|
+
const Comment = 18;
|
|
1825
|
+
const EndCommentTag = 19;
|
|
1826
|
+
const Text = 20;
|
|
1827
|
+
const CommentStart = 21;
|
|
1828
|
+
|
|
1829
|
+
const isSelfClosingTag = tag => {
|
|
1830
|
+
switch (tag) {
|
|
1831
|
+
case Br:
|
|
1832
|
+
case Hr:
|
|
1833
|
+
case Img:
|
|
1834
|
+
case Input:
|
|
1835
|
+
return true;
|
|
1836
|
+
default:
|
|
1837
|
+
return false;
|
|
1838
|
+
}
|
|
1839
|
+
};
|
|
1840
|
+
|
|
1841
|
+
const parseText = text => {
|
|
1842
|
+
return text.replaceAll('>', '>').replaceAll('<', '<').replaceAll('&', '&');
|
|
1843
|
+
};
|
|
1844
|
+
|
|
1845
|
+
class UnexpectedTokenError extends Error {
|
|
1846
|
+
constructor() {
|
|
1847
|
+
super('Unexpected token');
|
|
1848
|
+
this.name = 'UnexpectedTokenError';
|
|
1849
|
+
}
|
|
1850
|
+
}
|
|
1851
|
+
|
|
1852
|
+
const State = {
|
|
1853
|
+
AfterAttributeEqualSign: 8,
|
|
1854
|
+
AfterAttributeName: 7,
|
|
1855
|
+
AfterAttributeValueClosingQuote: 11,
|
|
1856
|
+
AfterAttributeValueInsideDoubleQuote: 10,
|
|
1857
|
+
AfterClosingTagName: 5,
|
|
1858
|
+
AfterClosingTagSlash: 4,
|
|
1859
|
+
AfterExclamationMark: 16,
|
|
1860
|
+
AfterOpeningAngleBracket: 2,
|
|
1861
|
+
InsideAttributeAfterDoubleQuote: 9,
|
|
1862
|
+
InsideComment: 17,
|
|
1863
|
+
InsideOpeningTag: 3,
|
|
1864
|
+
InsideOpeningTagAfterWhitespace: 6,
|
|
1865
|
+
TopLevelContent: 1
|
|
1866
|
+
};
|
|
1867
|
+
const RE_ANGLE_BRACKET_OPEN = /^</;
|
|
1868
|
+
const RE_ANGLE_BRACKET_OPEN_TAG = /^<(?![\s!%])/;
|
|
1869
|
+
const RE_ANGLE_BRACKET_CLOSE = /^>/;
|
|
1870
|
+
const RE_SLASH = /^\//;
|
|
1871
|
+
const RE_TAGNAME = /^[a-zA-Z\d$]+/;
|
|
1872
|
+
const RE_CONTENT = /^[^<>]+/;
|
|
1873
|
+
const RE_WHITESPACE = /^\s+/;
|
|
1874
|
+
const RE_ATTRIBUTE_NAME = /^[a-zA-Z\d-]+/;
|
|
1875
|
+
const RE_EQUAL_SIGN = /^=/;
|
|
1876
|
+
const RE_DOUBLE_QUOTE = /^"/;
|
|
1877
|
+
const RE_ATTRIBUTE_VALUE_INSIDE_DOUBLE_QUOTE = /^[^"\n]+/;
|
|
1878
|
+
const RE_TEXT = /^[^<>]+/;
|
|
1879
|
+
const RE_EXCLAMATION_MARK = /^!/;
|
|
1880
|
+
const RE_DASH_DASH = /^--/;
|
|
1881
|
+
const RE_DOCTYPE = /^doctype/i;
|
|
1882
|
+
const RE_BLOCK_COMMENT_CONTENT = /^[a-zA-Z\s]+/;
|
|
1883
|
+
const RE_COMMENT_END = /^-->/;
|
|
1884
|
+
const RE_TAG_TEXT = /^[^\s>]+/;
|
|
1885
|
+
const RE_ANY_TEXT = /^[^\n]+/;
|
|
1886
|
+
const RE_ATTRIBUTE_TEXT = /^[^\n<>/\s]+/;
|
|
1887
|
+
const RE_BLOCK_COMMENT_START = /^<!--/;
|
|
1888
|
+
const RE_SELF_CLOSING = /^\/>/;
|
|
1889
|
+
const tokenizeHtml = text => {
|
|
1890
|
+
string(text);
|
|
1891
|
+
let state = State.TopLevelContent;
|
|
1892
|
+
let index = 0;
|
|
1893
|
+
let next;
|
|
1894
|
+
const tokens = [];
|
|
1895
|
+
let token = None;
|
|
1896
|
+
while (index < text.length) {
|
|
1897
|
+
const part = text.slice(index);
|
|
1898
|
+
switch (state) {
|
|
1899
|
+
case State.AfterAttributeEqualSign:
|
|
1900
|
+
if (next = part.match(RE_DOUBLE_QUOTE)) {
|
|
1901
|
+
token = AttributeQuoteStart;
|
|
1902
|
+
state = State.InsideAttributeAfterDoubleQuote;
|
|
1903
|
+
} else if (next = part.match(RE_ANGLE_BRACKET_CLOSE)) {
|
|
1904
|
+
token = ClosingAngleBracket;
|
|
1905
|
+
state = State.TopLevelContent;
|
|
1906
|
+
} else if (next = part.match(RE_ATTRIBUTE_TEXT)) {
|
|
1907
|
+
token = AttributeValue;
|
|
1908
|
+
state = State.InsideOpeningTag;
|
|
1909
|
+
} else {
|
|
1910
|
+
throw new UnexpectedTokenError();
|
|
1911
|
+
}
|
|
1912
|
+
break;
|
|
1913
|
+
case State.AfterAttributeName:
|
|
1914
|
+
if (next = part.match(RE_EQUAL_SIGN)) {
|
|
1915
|
+
token = AttributeEqualSign;
|
|
1916
|
+
state = State.AfterAttributeEqualSign;
|
|
1917
|
+
} else if (next = part.match(RE_ANGLE_BRACKET_CLOSE)) {
|
|
1918
|
+
token = ClosingAngleBracket;
|
|
1919
|
+
state = State.TopLevelContent;
|
|
1920
|
+
} else if (next = part.match(RE_WHITESPACE)) {
|
|
1921
|
+
token = WhitespaceInsideOpeningTag;
|
|
1922
|
+
state = State.InsideOpeningTagAfterWhitespace;
|
|
1923
|
+
} else if (next = part.match(RE_ANGLE_BRACKET_OPEN)) {
|
|
1924
|
+
token = OpeningAngleBracket;
|
|
1925
|
+
state = State.AfterOpeningAngleBracket;
|
|
1926
|
+
} else {
|
|
1927
|
+
text.slice(index); // ?
|
|
1928
|
+
throw new UnexpectedTokenError();
|
|
1929
|
+
}
|
|
1930
|
+
break;
|
|
1931
|
+
case State.AfterAttributeValueClosingQuote:
|
|
1932
|
+
if (next = part.match(RE_ANGLE_BRACKET_CLOSE)) {
|
|
1933
|
+
token = ClosingAngleBracket;
|
|
1934
|
+
state = State.TopLevelContent;
|
|
1935
|
+
} else if (next = part.match(RE_WHITESPACE)) {
|
|
1936
|
+
token = WhitespaceInsideOpeningTag;
|
|
1937
|
+
state = State.InsideOpeningTagAfterWhitespace;
|
|
1938
|
+
} else if (next = part.match(RE_SELF_CLOSING)) {
|
|
1939
|
+
token = ClosingAngleBracket;
|
|
1940
|
+
state = State.TopLevelContent;
|
|
1941
|
+
} else {
|
|
1942
|
+
throw new UnexpectedTokenError();
|
|
1943
|
+
}
|
|
1944
|
+
break;
|
|
1945
|
+
case State.AfterAttributeValueInsideDoubleQuote:
|
|
1946
|
+
if (next = part.match(RE_DOUBLE_QUOTE)) {
|
|
1947
|
+
token = AttributeQuoteEnd;
|
|
1948
|
+
state = State.AfterAttributeValueClosingQuote;
|
|
1949
|
+
} else {
|
|
1950
|
+
throw new UnexpectedTokenError();
|
|
1951
|
+
}
|
|
1952
|
+
break;
|
|
1953
|
+
case State.AfterClosingTagName:
|
|
1954
|
+
if (next = part.match(RE_ANGLE_BRACKET_CLOSE)) {
|
|
1955
|
+
token = ClosingAngleBracket;
|
|
1956
|
+
state = State.TopLevelContent;
|
|
1957
|
+
} else if (next = part.match(RE_WHITESPACE)) {
|
|
1958
|
+
token = Content;
|
|
1959
|
+
state = State.TopLevelContent;
|
|
1960
|
+
} else {
|
|
1961
|
+
throw new UnexpectedTokenError();
|
|
1962
|
+
}
|
|
1963
|
+
break;
|
|
1964
|
+
case State.AfterClosingTagSlash:
|
|
1965
|
+
if (next = part.match(RE_TAGNAME)) {
|
|
1966
|
+
token = TagNameEnd;
|
|
1967
|
+
state = State.AfterClosingTagName;
|
|
1968
|
+
} else if (next = part.match(RE_WHITESPACE)) {
|
|
1969
|
+
token = WhitespaceAfterClosingTagSlash;
|
|
1970
|
+
state = State.TopLevelContent;
|
|
1971
|
+
} else if (next = part.match(RE_ANGLE_BRACKET_CLOSE)) {
|
|
1972
|
+
token = ClosingAngleBracket;
|
|
1973
|
+
state = State.TopLevelContent;
|
|
1974
|
+
} else {
|
|
1975
|
+
throw new UnexpectedTokenError();
|
|
1976
|
+
}
|
|
1977
|
+
break;
|
|
1978
|
+
case State.AfterExclamationMark:
|
|
1979
|
+
if (next = part.match(RE_DASH_DASH)) {
|
|
1980
|
+
token = StartCommentDashes;
|
|
1981
|
+
state = State.InsideComment;
|
|
1982
|
+
} else if (next = part.match(RE_DOCTYPE)) {
|
|
1983
|
+
token = Doctype;
|
|
1984
|
+
state = State.InsideOpeningTag;
|
|
1985
|
+
} else {
|
|
1986
|
+
text.slice(index); // ?
|
|
1987
|
+
throw new UnexpectedTokenError();
|
|
1988
|
+
}
|
|
1989
|
+
break;
|
|
1990
|
+
case State.AfterOpeningAngleBracket:
|
|
1991
|
+
if (next = part.match(RE_TAGNAME)) {
|
|
1992
|
+
token = TagNameStart;
|
|
1993
|
+
state = State.InsideOpeningTag;
|
|
1994
|
+
} else if (next = part.match(RE_SLASH)) {
|
|
1995
|
+
token = ClosingTagSlash;
|
|
1996
|
+
state = State.AfterClosingTagSlash;
|
|
1997
|
+
} else if (next = part.match(RE_WHITESPACE)) {
|
|
1998
|
+
token = WhitespaceAfterOpeningTagOpenAngleBracket;
|
|
1999
|
+
state = State.TopLevelContent;
|
|
2000
|
+
} else if (next = part.match(RE_ANGLE_BRACKET_CLOSE)) {
|
|
2001
|
+
token = ClosingAngleBracket;
|
|
2002
|
+
state = State.TopLevelContent;
|
|
2003
|
+
} else if (next = part.match(RE_EXCLAMATION_MARK)) {
|
|
2004
|
+
token = ExclamationMark;
|
|
2005
|
+
state = State.AfterExclamationMark;
|
|
2006
|
+
} else if (next = part.match(RE_ANY_TEXT)) {
|
|
2007
|
+
token = Text;
|
|
2008
|
+
state = State.TopLevelContent;
|
|
2009
|
+
} else {
|
|
2010
|
+
text.slice(index); // ?
|
|
2011
|
+
throw new UnexpectedTokenError();
|
|
2012
|
+
}
|
|
2013
|
+
break;
|
|
2014
|
+
case State.InsideAttributeAfterDoubleQuote:
|
|
2015
|
+
if (next = text.slice(index).match(RE_ATTRIBUTE_VALUE_INSIDE_DOUBLE_QUOTE)) {
|
|
2016
|
+
token = AttributeValue;
|
|
2017
|
+
state = State.AfterAttributeValueInsideDoubleQuote;
|
|
2018
|
+
} else if (next = part.match(RE_DOUBLE_QUOTE)) {
|
|
2019
|
+
token = AttributeQuoteEnd;
|
|
2020
|
+
state = State.AfterAttributeValueClosingQuote;
|
|
2021
|
+
} else {
|
|
2022
|
+
throw new UnexpectedTokenError();
|
|
2023
|
+
}
|
|
2024
|
+
break;
|
|
2025
|
+
case State.InsideComment:
|
|
2026
|
+
if (next = part.match(RE_BLOCK_COMMENT_CONTENT)) {
|
|
2027
|
+
token = Comment;
|
|
2028
|
+
state = State.InsideComment;
|
|
2029
|
+
} else if (next = part.match(RE_COMMENT_END)) {
|
|
2030
|
+
token = EndCommentTag;
|
|
2031
|
+
state = State.TopLevelContent;
|
|
2032
|
+
} else {
|
|
2033
|
+
text.slice(index); // ?
|
|
2034
|
+
throw new UnexpectedTokenError();
|
|
2035
|
+
}
|
|
2036
|
+
break;
|
|
2037
|
+
case State.InsideOpeningTag:
|
|
2038
|
+
if (next = part.match(RE_ANGLE_BRACKET_CLOSE)) {
|
|
2039
|
+
token = ClosingAngleBracket;
|
|
2040
|
+
state = State.TopLevelContent;
|
|
2041
|
+
} else if (next = part.match(RE_WHITESPACE)) {
|
|
2042
|
+
token = WhitespaceInsideOpeningTag;
|
|
2043
|
+
state = State.InsideOpeningTagAfterWhitespace;
|
|
2044
|
+
} else if (next = part.match(RE_TAG_TEXT)) {
|
|
2045
|
+
token = Text;
|
|
2046
|
+
state = State.TopLevelContent;
|
|
2047
|
+
} else {
|
|
2048
|
+
throw new UnexpectedTokenError();
|
|
2049
|
+
}
|
|
2050
|
+
break;
|
|
2051
|
+
case State.InsideOpeningTagAfterWhitespace:
|
|
2052
|
+
if (next = part.match(RE_ATTRIBUTE_NAME)) {
|
|
2053
|
+
token = AttributeName;
|
|
2054
|
+
state = State.AfterAttributeName;
|
|
2055
|
+
} else if (next = part.match(RE_ANGLE_BRACKET_CLOSE)) {
|
|
2056
|
+
token = ClosingAngleBracket;
|
|
2057
|
+
state = State.TopLevelContent;
|
|
2058
|
+
} else if (next = part.match(RE_SELF_CLOSING)) {
|
|
2059
|
+
token = ClosingAngleBracket;
|
|
2060
|
+
state = State.TopLevelContent;
|
|
2061
|
+
} else if (next = part.match(RE_TEXT)) {
|
|
2062
|
+
token = AttributeName;
|
|
2063
|
+
state = State.AfterAttributeName;
|
|
2064
|
+
} else {
|
|
2065
|
+
text.slice(index).match(RE_TEXT); // ?
|
|
2066
|
+
text.slice(index); // ?
|
|
2067
|
+
throw new UnexpectedTokenError();
|
|
2068
|
+
}
|
|
2069
|
+
break;
|
|
2070
|
+
case State.TopLevelContent:
|
|
2071
|
+
if (next = part.match(RE_ANGLE_BRACKET_OPEN_TAG)) {
|
|
2072
|
+
token = OpeningAngleBracket;
|
|
2073
|
+
state = State.AfterOpeningAngleBracket;
|
|
2074
|
+
} else if (next = part.match(RE_CONTENT)) {
|
|
2075
|
+
token = Content;
|
|
2076
|
+
state = State.TopLevelContent;
|
|
2077
|
+
} else if (next = part.match(RE_BLOCK_COMMENT_START)) {
|
|
2078
|
+
token = CommentStart;
|
|
2079
|
+
state = State.InsideComment;
|
|
2080
|
+
} else if (next = part.match(RE_ANGLE_BRACKET_CLOSE)) {
|
|
2081
|
+
token = Content;
|
|
2082
|
+
state = State.TopLevelContent;
|
|
2083
|
+
} else if (next = part.match(RE_ANGLE_BRACKET_OPEN)) {
|
|
2084
|
+
token = Text;
|
|
2085
|
+
state = State.TopLevelContent;
|
|
2086
|
+
} else {
|
|
2087
|
+
throw new UnexpectedTokenError();
|
|
2088
|
+
}
|
|
2089
|
+
break;
|
|
2090
|
+
default:
|
|
2091
|
+
throw new UnexpectedTokenError();
|
|
2092
|
+
}
|
|
2093
|
+
const tokenText = next[0];
|
|
2094
|
+
tokens.push({
|
|
2095
|
+
text: tokenText,
|
|
2096
|
+
type: token
|
|
2097
|
+
});
|
|
2098
|
+
index += tokenText.length;
|
|
2099
|
+
}
|
|
2100
|
+
return tokens;
|
|
2101
|
+
};
|
|
2102
|
+
|
|
2103
|
+
const parseHtml = (html, allowedAttributes) => {
|
|
2104
|
+
string(html);
|
|
2105
|
+
array(allowedAttributes);
|
|
2106
|
+
const tokens = tokenizeHtml(html);
|
|
2107
|
+
const dom = [];
|
|
2108
|
+
const root = {
|
|
2109
|
+
childCount: 0,
|
|
2110
|
+
type: 0
|
|
2111
|
+
};
|
|
2112
|
+
let current = root;
|
|
2113
|
+
const stack = [root];
|
|
2114
|
+
let attributeName = '';
|
|
2115
|
+
let lastTagWasSelfClosing = false;
|
|
2116
|
+
for (const token of tokens) {
|
|
2117
|
+
switch (token.type) {
|
|
2118
|
+
case AttributeName:
|
|
2119
|
+
attributeName = token.text;
|
|
2120
|
+
break;
|
|
2121
|
+
case AttributeValue:
|
|
2122
|
+
if (allowedAttributes.includes(attributeName)) {
|
|
2123
|
+
const finalAttributeName = attributeName === 'class' ? 'className' : attributeName;
|
|
2124
|
+
current[finalAttributeName] = token.text;
|
|
2125
|
+
}
|
|
2126
|
+
attributeName = '';
|
|
2127
|
+
break;
|
|
2128
|
+
case ClosingAngleBracket:
|
|
2129
|
+
// Handle boolean attributes (attributes without values)
|
|
2130
|
+
if (attributeName && allowedAttributes.includes(attributeName)) {
|
|
2131
|
+
const finalAttributeName = attributeName === 'class' ? 'className' : attributeName;
|
|
2132
|
+
current[finalAttributeName] = attributeName;
|
|
2133
|
+
}
|
|
2134
|
+
attributeName = '';
|
|
2135
|
+
// Return to parent if the current tag is self-closing
|
|
2136
|
+
if (lastTagWasSelfClosing) {
|
|
2137
|
+
current = stack.at(-1) || root;
|
|
2138
|
+
lastTagWasSelfClosing = false;
|
|
2139
|
+
}
|
|
2140
|
+
break;
|
|
2141
|
+
case Content:
|
|
2142
|
+
current.childCount++;
|
|
2143
|
+
dom.push(text(parseText(token.text)));
|
|
2144
|
+
break;
|
|
2145
|
+
case TagNameEnd:
|
|
2146
|
+
if (stack.length > 1) {
|
|
2147
|
+
stack.pop();
|
|
2148
|
+
}
|
|
2149
|
+
current = stack.at(-1) || root;
|
|
2150
|
+
break;
|
|
2151
|
+
case TagNameStart:
|
|
2152
|
+
current.childCount++;
|
|
2153
|
+
const newNode = {
|
|
2154
|
+
childCount: 0,
|
|
2155
|
+
type: getVirtualDomTag(token.text)
|
|
2156
|
+
};
|
|
2157
|
+
dom.push(newNode);
|
|
2158
|
+
current = newNode;
|
|
2159
|
+
lastTagWasSelfClosing = isSelfClosingTag(token.text);
|
|
2160
|
+
if (!lastTagWasSelfClosing) {
|
|
2161
|
+
stack.push(current);
|
|
2162
|
+
}
|
|
2163
|
+
break;
|
|
2164
|
+
case WhitespaceInsideOpeningTag:
|
|
2165
|
+
// Handle boolean attributes (attributes without values)
|
|
2166
|
+
if (attributeName && allowedAttributes.includes(attributeName)) {
|
|
2167
|
+
const finalAttributeName = attributeName === 'class' ? 'className' : attributeName;
|
|
2168
|
+
current[finalAttributeName] = attributeName;
|
|
2169
|
+
}
|
|
2170
|
+
attributeName = '';
|
|
2171
|
+
break;
|
|
2172
|
+
}
|
|
2173
|
+
}
|
|
2174
|
+
try {
|
|
2175
|
+
Object.defineProperty(dom, 'rootChildCount', {
|
|
2176
|
+
configurable: true,
|
|
2177
|
+
enumerable: false,
|
|
2178
|
+
value: root.childCount
|
|
2179
|
+
});
|
|
2180
|
+
} catch {
|
|
2181
|
+
dom.rootChildCount = root.childCount;
|
|
2182
|
+
}
|
|
2183
|
+
return dom;
|
|
2184
|
+
};
|
|
2185
|
+
|
|
2186
|
+
const handleEditorChanged = async () => {
|
|
2187
|
+
// Get all preview instance keys
|
|
2188
|
+
const previewKeys = getKeys$1();
|
|
2189
|
+
|
|
2190
|
+
// Get all editor keys from the editor worker
|
|
2191
|
+
const editorKeys = await invoke$1('Editor.getKeys');
|
|
2192
|
+
|
|
2193
|
+
// For each preview instance
|
|
2194
|
+
for (const previewUid of previewKeys) {
|
|
2195
|
+
const {
|
|
2196
|
+
newState: state
|
|
2197
|
+
} = get(previewUid);
|
|
2198
|
+
|
|
2199
|
+
// Skip if no URI is set
|
|
2200
|
+
if (!state.uri) {
|
|
2201
|
+
continue;
|
|
2202
|
+
}
|
|
2203
|
+
|
|
2204
|
+
// Find the editor that matches our preview's URI
|
|
2205
|
+
let matchingEditorUid = null;
|
|
2206
|
+
for (const editorKey of editorKeys) {
|
|
2207
|
+
const editorUid = Number.parseFloat(editorKey);
|
|
2208
|
+
const editorUri = await invoke$1('Editor.getUri', editorUid);
|
|
2209
|
+
if (editorUri === state.uri) {
|
|
2210
|
+
matchingEditorUid = editorUid;
|
|
2211
|
+
break;
|
|
2212
|
+
}
|
|
2213
|
+
}
|
|
2214
|
+
|
|
2215
|
+
// If we found a matching editor, get its text and update the preview
|
|
2216
|
+
if (matchingEditorUid !== null) {
|
|
2217
|
+
try {
|
|
2218
|
+
const content = await invoke$1('Editor.getText', matchingEditorUid);
|
|
2219
|
+
const parsedDom = parseHtml(content, []);
|
|
2220
|
+
const updatedState = {
|
|
2221
|
+
...state,
|
|
2222
|
+
content,
|
|
2223
|
+
errorMessage: '',
|
|
2224
|
+
parsedDom
|
|
2225
|
+
};
|
|
2226
|
+
set(previewUid, state, updatedState);
|
|
2227
|
+
} catch (error) {
|
|
2228
|
+
// If getting text fails, update with error message
|
|
2229
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
2230
|
+
const updatedState = {
|
|
2231
|
+
...state,
|
|
2232
|
+
content: '',
|
|
2233
|
+
errorMessage,
|
|
2234
|
+
parsedDom: []
|
|
2235
|
+
};
|
|
2236
|
+
set(previewUid, state, updatedState);
|
|
2237
|
+
}
|
|
2238
|
+
}
|
|
2239
|
+
}
|
|
2240
|
+
|
|
2241
|
+
// Rerender all previews after updates are complete
|
|
2242
|
+
await invoke('Preview.rerender');
|
|
2243
|
+
};
|
|
2244
|
+
|
|
2245
|
+
const getParsedNodesChildNodeCount = parsedDom => {
|
|
2246
|
+
array(parsedDom);
|
|
2247
|
+
const rootCountFromParse = parsedDom.rootChildCount;
|
|
2248
|
+
if (typeof rootCountFromParse === 'number') {
|
|
2249
|
+
return rootCountFromParse;
|
|
2250
|
+
}
|
|
2251
|
+
let rootChildCount = 0;
|
|
2252
|
+
let i = 0;
|
|
2253
|
+
while (i < parsedDom.length) {
|
|
2254
|
+
rootChildCount++;
|
|
2255
|
+
|
|
2256
|
+
// skip the entire subtree of the current node
|
|
2257
|
+
let toSkip = parsedDom[i].childCount;
|
|
2258
|
+
i++;
|
|
2259
|
+
while (toSkip > 0 && i < parsedDom.length) {
|
|
2260
|
+
toSkip -= 1;
|
|
2261
|
+
toSkip += parsedDom[i].childCount;
|
|
2262
|
+
i++;
|
|
2263
|
+
}
|
|
2264
|
+
}
|
|
2265
|
+
return rootChildCount;
|
|
2266
|
+
};
|
|
2267
|
+
|
|
2268
|
+
const updateContent = async (state, uri) => {
|
|
2269
|
+
try {
|
|
2270
|
+
// Read the file content using RendererWorker RPC
|
|
2271
|
+
// @ts-ignore
|
|
2272
|
+
const content = await readFile(uri);
|
|
2273
|
+
|
|
2274
|
+
// Parse the content into virtual DOM
|
|
2275
|
+
const parsedDom = parseHtml(content, []);
|
|
2276
|
+
const parsedNodesChildNodeCount = getParsedNodesChildNodeCount(parsedDom);
|
|
2277
|
+
return {
|
|
2278
|
+
content,
|
|
2279
|
+
errorMessage: '',
|
|
2280
|
+
parsedDom,
|
|
2281
|
+
parsedNodesChildNodeCount
|
|
2282
|
+
};
|
|
2283
|
+
} catch (error) {
|
|
2284
|
+
// If file reading or parsing fails, return empty content and parsedDom with error message
|
|
2285
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
2286
|
+
return {
|
|
2287
|
+
content: '',
|
|
2288
|
+
errorMessage,
|
|
2289
|
+
parsedDom: [],
|
|
2290
|
+
parsedNodesChildNodeCount: 0
|
|
2291
|
+
};
|
|
2292
|
+
}
|
|
2293
|
+
};
|
|
2294
|
+
|
|
2295
|
+
const handleFileEdited = async state => {
|
|
2296
|
+
const {
|
|
2297
|
+
content,
|
|
2298
|
+
errorMessage,
|
|
2299
|
+
parsedDom,
|
|
2300
|
+
parsedNodesChildNodeCount
|
|
2301
|
+
} = await updateContent(state, state.uri);
|
|
2302
|
+
return {
|
|
2303
|
+
...state,
|
|
2304
|
+
content,
|
|
2305
|
+
errorMessage,
|
|
2306
|
+
parsedDom,
|
|
2307
|
+
parsedNodesChildNodeCount
|
|
2308
|
+
};
|
|
2309
|
+
};
|
|
2310
|
+
|
|
2311
|
+
const loadContent = async state => {
|
|
2312
|
+
// Try to register to receive editor change notifications from the editor worker.
|
|
2313
|
+
// Use dynamic access and ignore errors so this is safe in environments where
|
|
2314
|
+
// the EditorWorker / ListenerType are not available (e.g. unit tests).
|
|
2315
|
+
const EditorChange = 1;
|
|
2316
|
+
const rpcId = 9112;
|
|
2317
|
+
try {
|
|
2318
|
+
await invoke$1('Listener.register', EditorChange, rpcId);
|
|
2319
|
+
} catch (error) {
|
|
2320
|
+
console.error(error);
|
|
2321
|
+
}
|
|
2322
|
+
|
|
2323
|
+
// Read and parse file contents if we have a URI
|
|
2324
|
+
const {
|
|
2325
|
+
content,
|
|
2326
|
+
errorMessage,
|
|
2327
|
+
parsedDom,
|
|
2328
|
+
parsedNodesChildNodeCount
|
|
2329
|
+
} = state.uri ? await updateContent(state, state.uri) : {
|
|
2330
|
+
content: state.content,
|
|
2331
|
+
errorMessage: state.errorMessage,
|
|
2332
|
+
parsedDom: state.parsedDom,
|
|
2333
|
+
parsedNodesChildNodeCount: state.parsedNodesChildNodeCount
|
|
2334
|
+
};
|
|
2335
|
+
return {
|
|
2336
|
+
...state,
|
|
2337
|
+
content,
|
|
2338
|
+
errorCount: 0,
|
|
2339
|
+
errorMessage,
|
|
2340
|
+
initial: false,
|
|
2341
|
+
parsedDom,
|
|
2342
|
+
parsedNodesChildNodeCount,
|
|
2343
|
+
warningCount: 1
|
|
2344
|
+
};
|
|
2345
|
+
};
|
|
2346
|
+
|
|
1361
2347
|
const getEmptyPreviewDom = () => {
|
|
1362
2348
|
return [{
|
|
1363
2349
|
childCount: 1,
|
|
1364
2350
|
className: 'Viewlet Preview',
|
|
1365
|
-
type: Div
|
|
2351
|
+
type: Div$1
|
|
1366
2352
|
}, {
|
|
1367
2353
|
childCount: 1,
|
|
1368
|
-
type: H1
|
|
2354
|
+
type: H1$1
|
|
1369
2355
|
}, {
|
|
1370
2356
|
text: 'No URI has been specified',
|
|
1371
|
-
type: Text
|
|
2357
|
+
type: Text$1
|
|
1372
2358
|
}];
|
|
1373
2359
|
};
|
|
2360
|
+
|
|
1374
2361
|
const getPreviewDom = state => {
|
|
1375
|
-
|
|
2362
|
+
const {
|
|
2363
|
+
parsedDom,
|
|
2364
|
+
parsedNodesChildNodeCount,
|
|
2365
|
+
uri
|
|
2366
|
+
} = state;
|
|
2367
|
+
if (!uri) {
|
|
1376
2368
|
return getEmptyPreviewDom();
|
|
1377
2369
|
}
|
|
2370
|
+
|
|
2371
|
+
// If parsedDom is available, render it as children of the wrapper
|
|
2372
|
+
if (parsedDom && parsedDom.length > 0) {
|
|
2373
|
+
return [{
|
|
2374
|
+
childCount: parsedNodesChildNodeCount,
|
|
2375
|
+
className: 'Viewlet Preview',
|
|
2376
|
+
type: Div$1
|
|
2377
|
+
}, ...parsedDom];
|
|
2378
|
+
}
|
|
1378
2379
|
return [{
|
|
1379
2380
|
childCount: 1,
|
|
1380
2381
|
className: 'Viewlet Preview',
|
|
1381
|
-
type: Div
|
|
2382
|
+
type: Div$1
|
|
1382
2383
|
}, {
|
|
1383
2384
|
childCount: 1,
|
|
1384
|
-
type: H1
|
|
2385
|
+
type: H1$1
|
|
1385
2386
|
}, {
|
|
1386
2387
|
text: 'hello from preview',
|
|
1387
|
-
type: Text
|
|
2388
|
+
type: Text$1
|
|
1388
2389
|
}];
|
|
1389
2390
|
};
|
|
1390
2391
|
|
|
@@ -1449,6 +2450,18 @@ const renderEventListeners = () => {
|
|
|
1449
2450
|
}];
|
|
1450
2451
|
};
|
|
1451
2452
|
|
|
2453
|
+
const rerender = state => {
|
|
2454
|
+
// Create a new copy of parsedDom array to trigger diff
|
|
2455
|
+
const parsedDom = [...state.parsedDom];
|
|
2456
|
+
|
|
2457
|
+
// Return a new state object with the copied parsedDom
|
|
2458
|
+
// This will cause DiffItems.isEqual to return false since parsedDom reference changed
|
|
2459
|
+
return {
|
|
2460
|
+
...state,
|
|
2461
|
+
parsedDom
|
|
2462
|
+
};
|
|
2463
|
+
};
|
|
2464
|
+
|
|
1452
2465
|
const resize = (state, dimensions) => {
|
|
1453
2466
|
return {
|
|
1454
2467
|
...state,
|
|
@@ -1462,20 +2475,33 @@ const saveState = state => {
|
|
|
1462
2475
|
};
|
|
1463
2476
|
};
|
|
1464
2477
|
|
|
1465
|
-
const setUri = (state, uri) => {
|
|
2478
|
+
const setUri = async (state, uri) => {
|
|
2479
|
+
const {
|
|
2480
|
+
content,
|
|
2481
|
+
errorMessage,
|
|
2482
|
+
parsedDom,
|
|
2483
|
+
parsedNodesChildNodeCount
|
|
2484
|
+
} = await updateContent(state, uri);
|
|
1466
2485
|
return {
|
|
1467
2486
|
...state,
|
|
2487
|
+
content,
|
|
2488
|
+
errorMessage,
|
|
2489
|
+
parsedDom,
|
|
2490
|
+
parsedNodesChildNodeCount,
|
|
1468
2491
|
uri
|
|
1469
2492
|
};
|
|
1470
2493
|
};
|
|
1471
2494
|
|
|
1472
2495
|
const commandMap = {
|
|
2496
|
+
handleEditorChanged: handleEditorChanged,
|
|
1473
2497
|
'Preview.create': create,
|
|
1474
2498
|
'Preview.diff2': diff2,
|
|
1475
2499
|
'Preview.getCommandIds': getCommandIds,
|
|
2500
|
+
'Preview.handleFileEdited': wrapCommand(handleFileEdited),
|
|
1476
2501
|
'Preview.loadContent': wrapCommand(loadContent),
|
|
1477
2502
|
'Preview.render2': render2,
|
|
1478
2503
|
'Preview.renderEventListeners': renderEventListeners,
|
|
2504
|
+
'Preview.rerender': wrapCommand(rerender),
|
|
1479
2505
|
'Preview.resize': wrapCommand(resize),
|
|
1480
2506
|
'Preview.saveState': wrapGetter(saveState),
|
|
1481
2507
|
'Preview.setUri': wrapCommand(setUri),
|
|
@@ -1488,6 +2514,11 @@ const listen = async () => {
|
|
|
1488
2514
|
commandMap: commandMap
|
|
1489
2515
|
});
|
|
1490
2516
|
set$1(rpc);
|
|
2517
|
+
const editorRpc = await LazyTransferMessagePortRpcParent.create({
|
|
2518
|
+
commandMap: {},
|
|
2519
|
+
send: port => sendMessagePortToEditorWorker(port, 9112)
|
|
2520
|
+
});
|
|
2521
|
+
set$2(editorRpc);
|
|
1491
2522
|
};
|
|
1492
2523
|
|
|
1493
2524
|
const main = async () => {
|