aberdeen 1.0.12 → 1.0.13

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aberdeen",
3
- "version": "1.0.12",
3
+ "version": "1.0.13",
4
4
  "author": "Frank van Viegen",
5
5
  "main": "dist-min/aberdeen.js",
6
6
  "devDependencies": {
package/src/aberdeen.ts CHANGED
@@ -1148,28 +1148,67 @@ const arrayHandler: ProxyHandler<any[]> = {
1148
1148
  },
1149
1149
  };
1150
1150
 
1151
+ /**
1152
+ * Helper functions that wrap iterators to proxy values
1153
+ */
1154
+ function wrapIteratorSingle(iterator: IterableIterator<any>): IterableIterator<any> {
1155
+ return {
1156
+ [Symbol.iterator]() { return this; },
1157
+ next() {
1158
+ const result = iterator.next();
1159
+ if (result.done) return result;
1160
+ return {
1161
+ done: false,
1162
+ value: optProxy(result.value)
1163
+ };
1164
+ }
1165
+ };
1166
+ }
1167
+ function wrapIteratorPair(iterator: IterableIterator<[any, any]>): IterableIterator<[any, any]> {
1168
+ return {
1169
+ [Symbol.iterator]() { return this; },
1170
+ next() {
1171
+ const result = iterator.next();
1172
+ if (result.done) return result;
1173
+ return {
1174
+ done: false,
1175
+ value: [optProxy(result.value[0]), optProxy(result.value[1])]
1176
+ };
1177
+ }
1178
+ };
1179
+ }
1180
+
1151
1181
  const mapMethodHandlers = {
1152
1182
  get(this: any, key: any): any {
1153
1183
  const target: Map<any, any> = this[TARGET_SYMBOL];
1184
+ // Make sure key is unproxied
1185
+ if (typeof key === "object" && key)
1186
+ key = (key as any)[TARGET_SYMBOL] || key;
1154
1187
  subscribe(target, key);
1155
1188
  return optProxy(target.get(key));
1156
1189
  },
1157
1190
  set(this: any, key: any, newData: any): any {
1158
1191
  const target: Map<any, any> = this[TARGET_SYMBOL];
1159
- // Make sure newData is unproxied
1192
+ // Make sure key and newData are unproxied
1193
+ if (typeof key === "object" && key)
1194
+ key = (key as any)[TARGET_SYMBOL] || key;
1160
1195
  if (typeof newData === "object" && newData)
1161
1196
  newData = (newData as any)[TARGET_SYMBOL] || newData;
1162
1197
  const oldData = target.get(key);
1163
1198
  if (newData !== oldData) {
1199
+ const oldSize = target.size;
1164
1200
  target.set(key, newData);
1165
1201
  emit(target, key, newData, oldData);
1166
- emit(target, MAP_SIZE_SYMBOL, target.size, target.size - (oldData === undefined ? 1 : 0));
1202
+ emit(target, MAP_SIZE_SYMBOL, target.size, oldSize);
1167
1203
  runImmediateQueue();
1168
1204
  }
1169
1205
  return this;
1170
1206
  },
1171
1207
  delete(this: any, key: any): boolean {
1172
1208
  const target: Map<any, any> = this[TARGET_SYMBOL];
1209
+ // Make sure key is unproxied
1210
+ if (typeof key === "object" && key)
1211
+ key = (key as any)[TARGET_SYMBOL] || key;
1173
1212
  const oldData = target.get(key);
1174
1213
  const result: boolean = target.delete(key);
1175
1214
  if (result) {
@@ -1192,28 +1231,31 @@ const mapMethodHandlers = {
1192
1231
  },
1193
1232
  has(this: any, key: any): boolean {
1194
1233
  const target: Map<any, any> = this[TARGET_SYMBOL];
1234
+ // Make sure key is unproxied
1235
+ if (typeof key === "object" && key)
1236
+ key = (key as any)[TARGET_SYMBOL] || key;
1195
1237
  subscribe(target, key);
1196
1238
  return target.has(key);
1197
1239
  },
1198
1240
  keys(this: any): IterableIterator<any> {
1199
1241
  const target: Map<any, any> = this[TARGET_SYMBOL];
1200
1242
  subscribe(target, ANY_SYMBOL);
1201
- return target.keys();
1243
+ return wrapIteratorSingle(target.keys());
1202
1244
  },
1203
1245
  values(this: any): IterableIterator<any> {
1204
1246
  const target: Map<any, any> = this[TARGET_SYMBOL];
1205
1247
  subscribe(target, ANY_SYMBOL);
1206
- return target.values();
1248
+ return wrapIteratorSingle(target.values());
1207
1249
  },
1208
1250
  entries(this: any): IterableIterator<[any, any]> {
1209
1251
  const target: Map<any, any> = this[TARGET_SYMBOL];
1210
1252
  subscribe(target, ANY_SYMBOL);
1211
- return target.entries();
1253
+ return wrapIteratorPair(target.entries());
1212
1254
  },
1213
1255
  [Symbol.iterator](this: any): IterableIterator<[any, any]> {
1214
1256
  const target: Map<any, any> = this[TARGET_SYMBOL];
1215
1257
  subscribe(target, ANY_SYMBOL);
1216
- return target[Symbol.iterator]();
1258
+ return wrapIteratorPair(target[Symbol.iterator]());
1217
1259
  }
1218
1260
  };
1219
1261
 
package/html-to-aberdeen DELETED
@@ -1,354 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- // WARNING: This script was created by Claude Sonnet 3.7, and hasn't
4
- // received any human code review. It seems to do the job though!
5
-
6
- export function parseHTML(html) {
7
- const result = {
8
- body: []
9
- };
10
-
11
- let currentPosition = 0;
12
- let currentParent = result;
13
- const stack = [];
14
-
15
- while (currentPosition < html.length) {
16
- // Skip whitespace
17
- while (currentPosition < html.length && /\s/.test(html[currentPosition])) {
18
- currentPosition++;
19
- }
20
-
21
- if (currentPosition >= html.length) break;
22
-
23
- // Check for comment
24
- if (html.substring(currentPosition, currentPosition + 4) === '<!--') {
25
- const endComment = html.indexOf('-->', currentPosition);
26
- if (endComment === -1) break;
27
-
28
- const commentContent = html.substring(currentPosition + 4, endComment);
29
- currentParent.children = currentParent.children || [];
30
- currentParent.children.push({
31
- type: 'comment',
32
- content: commentContent
33
- });
34
-
35
- currentPosition = endComment + 3;
36
- continue;
37
- }
38
-
39
- // Check for tag
40
- if (html[currentPosition] === '<') {
41
- // Check if it's a closing tag
42
- if (html[currentPosition + 1] === '/') {
43
- const endTag = html.indexOf('>', currentPosition);
44
- if (endTag === -1) break;
45
-
46
- const tagName = html.substring(currentPosition + 2, endTag).trim().toLowerCase();
47
-
48
- // Pop from stack
49
- if (stack.length > 0) {
50
- currentParent = stack.pop();
51
- }
52
-
53
- currentPosition = endTag + 1;
54
- continue;
55
- }
56
-
57
- // It's an opening tag
58
- const endTag = html.indexOf('>', currentPosition);
59
- if (endTag === -1) break;
60
-
61
- const selfClosing = html[endTag - 1] === '/';
62
- const tagContent = html.substring(currentPosition + 1, selfClosing ? endTag - 1 : endTag).trim();
63
- const spaceIndex = tagContent.search(/\s/);
64
-
65
- let tagName, attributesStr;
66
- if (spaceIndex === -1) {
67
- tagName = tagContent;
68
- attributesStr = '';
69
- } else {
70
- tagName = tagContent.substring(0, spaceIndex);
71
- attributesStr = tagContent.substring(spaceIndex + 1);
72
- }
73
-
74
- tagName = tagName.toLowerCase();
75
-
76
- // Parse attributes
77
- const attributes = [];
78
- let attrMatch;
79
- const attrRegex = /([\w-]+)(?:=(?:"([^"]*)"|'([^']*)'|(\S+)))?/g;
80
-
81
- while ((attrMatch = attrRegex.exec(attributesStr)) !== null) {
82
- const name = attrMatch[1];
83
- const value = attrMatch[2] || attrMatch[3] || attrMatch[4] || '';
84
- attributes.push({ name, value });
85
- }
86
-
87
- const newElement = {
88
- type: 'element',
89
- tagName,
90
- attributes,
91
- children: []
92
- };
93
-
94
- // Add to current parent
95
- if (currentParent === result) {
96
- currentParent.body = currentParent.body || [];
97
- currentParent.body.push(newElement);
98
- } else {
99
- currentParent.children = currentParent.children || [];
100
- currentParent.children.push(newElement);
101
- }
102
-
103
- if (!selfClosing && !['br', 'hr', 'img', 'input', 'link', 'meta'].includes(tagName)) {
104
- stack.push(currentParent);
105
- currentParent = newElement;
106
- }
107
-
108
- currentPosition = endTag + 1;
109
- continue;
110
- }
111
-
112
- // It's text content
113
- let endText = html.indexOf('<', currentPosition);
114
- if (endText === -1) endText = html.length;
115
-
116
- const textContent = html.substring(currentPosition, endText);
117
- if (textContent.trim()) {
118
- if (currentParent === result) {
119
- currentParent.body = currentParent.body || [];
120
- currentParent.body.push({
121
- type: 'text',
122
- content: textContent
123
- });
124
- } else {
125
- currentParent.children = currentParent.children || [];
126
- currentParent.children.push({
127
- type: 'text',
128
- content: textContent
129
- });
130
- }
131
- }
132
-
133
- currentPosition = endText;
134
- }
135
-
136
- return result;
137
- }
138
-
139
- // Read from stdin
140
- let html = '';
141
- process.stdin.setEncoding('utf8');
142
- process.stdin.on('data', (chunk) => {
143
- html += chunk;
144
- });
145
- process.stdin.on('end', () => {
146
- // Convert HTML to Aberdeen code
147
- const aberdeenCode = convertHTMLToAberdeen(html);
148
-
149
- // Output to stdout
150
- process.stdout.write(aberdeenCode);
151
- });
152
-
153
- // Main conversion function
154
- function convertHTMLToAberdeen(html) {
155
- // Parse HTML into a simple AST
156
- const ast = parseHTML(html);
157
-
158
- // Generate the Aberdeen code
159
- let aberdeenCode = ``;
160
-
161
- // Process the body's children
162
- for (const node of ast.body) {
163
- aberdeenCode += processNode(node);
164
- }
165
-
166
- return aberdeenCode;
167
- }
168
-
169
- // Process a node and return Aberdeen code
170
- function processNode(node, indentLevel = 0) {
171
- const indent = ' '.repeat(indentLevel);
172
-
173
- if (node.type === 'text') {
174
- const text = node.content.trim();
175
- if (text) {
176
- return `${indent}$(':${escapeString(text)}');\n`;
177
- }
178
- return '';
179
- }
180
-
181
- if (node.type === 'comment') {
182
- return `${indent}// ${node.content.trim()}\n`;
183
- }
184
-
185
- if (node.type === 'element') {
186
- // Get tag name
187
- const tagName = node.tagName.toLowerCase();
188
-
189
- // Get classes
190
- const classAttr = node.attributes.find(attr => attr.name === 'class');
191
- const classes = classAttr
192
- ? classAttr.value.split(/\s+/).filter(Boolean).join('.')
193
- : '';
194
-
195
- // Get other attributes
196
- const attributes = {};
197
- for (const attr of node.attributes) {
198
- if (attr.name !== 'class') {
199
- attributes[attr.name] = attr.value;
200
- }
201
- }
202
-
203
- // Check if node has only text content
204
- const hasOnlyTextContent =
205
- node.children.length === 1 &&
206
- node.children[0].type === 'text' &&
207
- node.children[0].content.trim();
208
-
209
- // Build the tag string
210
- let tagString = tagName;
211
- if (classes) {
212
- tagString += `.${classes}`;
213
- }
214
-
215
- if (hasOnlyTextContent) {
216
- tagString += `:${escapeString(node.children[0].content.trim())}`;
217
- }
218
-
219
- let result = `${indent}$('${tagString}'`;
220
-
221
- // Add attributes if any
222
- if (Object.keys(attributes).length > 0) {
223
- result += `, ${formatAttributes(attributes, indent)}`;
224
- }
225
-
226
- // Process child nodes
227
- const childNodes = node.children.filter(child =>
228
- child.type === 'element' ||
229
- (child.type === 'text' && child.content.trim())
230
- );
231
-
232
- if (childNodes.length > 0 && !hasOnlyTextContent) {
233
- // Get all descendants in a single-child chain
234
- const singleChildChain = getSingleChildChain(node);
235
-
236
- if (singleChildChain.length > 1) {
237
- // We have a chain of single children, add them all on the same line
238
- for (let i = 1; i < singleChildChain.length; i++) {
239
- const chainNode = singleChildChain[i];
240
-
241
- // Get tag name
242
- const chainTagName = chainNode.tagName.toLowerCase();
243
-
244
- // Get classes
245
- const chainClassAttr = chainNode.attributes.find(attr => attr.name === 'class');
246
- const chainClasses = chainClassAttr
247
- ? chainClassAttr.value.split(/\s+/).filter(Boolean).join('.')
248
- : '';
249
-
250
- // Build the tag string
251
- let chainTagString = chainTagName;
252
- if (chainClasses) {
253
- chainTagString += `.${chainClasses}`;
254
- }
255
-
256
- // Check if node has only text content
257
- const chainHasOnlyTextContent =
258
- chainNode.children.length === 1 &&
259
- chainNode.children[0].type === 'text' &&
260
- chainNode.children[0].content.trim();
261
-
262
- if (chainHasOnlyTextContent) {
263
- chainTagString += `:${escapeString(chainNode.children[0].content.trim())}`;
264
- }
265
-
266
- result += `, '${chainTagString}'`;
267
-
268
- // Add attributes if any
269
- const chainAttributes = {};
270
- for (const attr of chainNode.attributes) {
271
- if (attr.name !== 'class') {
272
- chainAttributes[attr.name] = attr.value;
273
- }
274
- }
275
-
276
- if (Object.keys(chainAttributes).length > 0) {
277
- result += `, ${formatAttributes(chainAttributes, indent)}`;
278
- }
279
- }
280
-
281
- // Check if the last node in the chain has any non-text children
282
- const lastNode = singleChildChain[singleChildChain.length - 1];
283
- const lastNodeChildren = lastNode.children.filter(child =>
284
- child.type === 'element' ||
285
- (child.type === 'text' && child.content.trim() &&
286
- !(lastNode.children.length === 1 && lastNode.children[0].type === 'text'))
287
- );
288
-
289
- if (lastNodeChildren.length > 0) {
290
- result += `, () => {\n`;
291
- for (const child of lastNodeChildren) {
292
- result += processNode(child, indentLevel + 1);
293
- }
294
- result += `${indent}}`;
295
- }
296
- } else {
297
- // Multiple children, use a content function
298
- result += `, () => {\n`;
299
- for (const child of childNodes) {
300
- result += processNode(child, indentLevel + 1);
301
- }
302
- result += `${indent}}`;
303
- }
304
- }
305
-
306
- result += `);\n`;
307
- return result;
308
- }
309
-
310
- return '';
311
- }
312
-
313
- // Get a chain of nodes where each node has exactly one element child
314
- function getSingleChildChain(node) {
315
- const chain = [node];
316
- let current = node;
317
-
318
- while (true) {
319
- // Get element children
320
- const elementChildren = current.children.filter(child => child.type === 'element');
321
-
322
- // If there's exactly one element child, add it to the chain
323
- if (elementChildren.length === 1) {
324
- current = elementChildren[0];
325
- chain.push(current);
326
- } else {
327
- break;
328
- }
329
- }
330
-
331
- return chain;
332
- }
333
-
334
- // Format attributes object with proper indentation
335
- function formatAttributes(attributes, indent) {
336
- const attrLines = JSON.stringify(attributes, null, 4).split('\n');
337
-
338
- if (attrLines.length <= 1) {
339
- return JSON.stringify(attributes);
340
- }
341
-
342
- return attrLines.map((line, i) => {
343
- if (i === 0) return line;
344
- return indent + line;
345
- }).join('\n');
346
- }
347
-
348
- // Escape special characters in strings
349
- function escapeString(str) {
350
- return str
351
- .replace(/\\/g, '\\\\')
352
- .replace(/'/g, "\\'")
353
- .replace(/\n/g, '\\n');
354
- }