defuddle-cli 0.1.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/index.js ADDED
@@ -0,0 +1,1041 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import { JSDOM, VirtualConsole } from 'jsdom';
4
+ import pkg from 'defuddle';
5
+ const { Defuddle } = pkg;
6
+ import chalk from 'chalk';
7
+ import { readFile, writeFile } from 'fs/promises';
8
+ import { fileURLToPath } from 'url';
9
+ import { dirname, resolve } from 'path';
10
+ import { createMarkdownContent } from './markdown.js';
11
+ const __filename = fileURLToPath(import.meta.url);
12
+ const __dirname = dirname(__filename);
13
+ // Define CSS interfaces globally first
14
+ globalThis.CSSRule = class {
15
+ constructor(type) {
16
+ this.type = 1;
17
+ if (type !== undefined) {
18
+ Object.defineProperty(this, 'type', { value: type });
19
+ }
20
+ this.cssText = '';
21
+ this.parentRule = null;
22
+ this.parentStyleSheet = null;
23
+ }
24
+ };
25
+ // Add static properties
26
+ Object.defineProperties(globalThis.CSSRule, {
27
+ STYLE_RULE: { value: 1, writable: false },
28
+ CHARSET_RULE: { value: 2, writable: false },
29
+ IMPORT_RULE: { value: 3, writable: false },
30
+ MEDIA_RULE: { value: 4, writable: false },
31
+ FONT_FACE_RULE: { value: 5, writable: false },
32
+ PAGE_RULE: { value: 6, writable: false },
33
+ KEYFRAMES_RULE: { value: 7, writable: false },
34
+ KEYFRAME_RULE: { value: 8, writable: false },
35
+ NAMESPACE_RULE: { value: 10, writable: false },
36
+ COUNTER_STYLE_RULE: { value: 11, writable: false },
37
+ SUPPORTS_RULE: { value: 12, writable: false },
38
+ DOCUMENT_RULE: { value: 13, writable: false },
39
+ FONT_FEATURE_VALUES_RULE: { value: 14, writable: false },
40
+ VIEWPORT_RULE: { value: 15, writable: false },
41
+ REGION_STYLE_RULE: { value: 16, writable: false }
42
+ });
43
+ globalThis.CSSMediaRule = class extends globalThis.CSSRule {
44
+ constructor() {
45
+ super();
46
+ this.conditionText = '';
47
+ this.deleteRule = () => { };
48
+ this.insertRule = () => 0;
49
+ Object.defineProperty(this, 'type', { value: 4 }); // CSSRule.MEDIA_RULE
50
+ this.media = {
51
+ length: 0,
52
+ mediaText: '',
53
+ item: () => null,
54
+ appendMedium: () => { },
55
+ deleteMedium: () => { },
56
+ toString: () => '',
57
+ [Symbol.iterator]: function* () { yield ''; return undefined; }
58
+ };
59
+ this.cssRules = {
60
+ length: 0,
61
+ item: () => null,
62
+ [Symbol.iterator]: function* () {
63
+ yield new globalThis.CSSRule();
64
+ return undefined;
65
+ }
66
+ };
67
+ }
68
+ };
69
+ globalThis.CSSStyleSheet = class {
70
+ constructor() {
71
+ this.type = 'text/css';
72
+ this.href = null;
73
+ this.ownerNode = null;
74
+ this.parentStyleSheet = null;
75
+ this.title = null;
76
+ this.disabled = false;
77
+ this.ownerRule = null;
78
+ this.addRule = () => 0;
79
+ this.removeRule = () => { };
80
+ this.replace = async () => this;
81
+ this.replaceSync = () => { };
82
+ this.media = {
83
+ length: 0,
84
+ mediaText: '',
85
+ item: () => null,
86
+ appendMedium: () => { },
87
+ deleteMedium: () => { },
88
+ toString: () => '',
89
+ [Symbol.iterator]: function* () { yield ''; return undefined; }
90
+ };
91
+ this.cssRules = {
92
+ length: 0,
93
+ item: () => null,
94
+ [Symbol.iterator]: function* () {
95
+ yield new globalThis.CSSRule();
96
+ return undefined;
97
+ }
98
+ };
99
+ this.rules = this.cssRules;
100
+ }
101
+ insertRule(rule, index) {
102
+ return 0;
103
+ }
104
+ deleteRule(index) { }
105
+ };
106
+ // Define SVGElement globally
107
+ globalThis.SVGElement = class {
108
+ constructor() {
109
+ this.id = '';
110
+ this.className = '';
111
+ this.style = {
112
+ cssText: '',
113
+ length: 0,
114
+ parentRule: null,
115
+ getPropertyPriority: () => '',
116
+ getPropertyValue: () => '',
117
+ item: () => '',
118
+ removeProperty: () => '',
119
+ setProperty: () => '',
120
+ [Symbol.iterator]: function* () { yield ''; return undefined; }
121
+ };
122
+ this.ownerSVGElement = null;
123
+ this.viewportElement = null;
124
+ this.tagName = '';
125
+ this.namespaceURI = null;
126
+ this.prefix = null;
127
+ this.localName = '';
128
+ this.baseURI = '';
129
+ this.textContent = '';
130
+ this.innerHTML = '';
131
+ this.outerHTML = '';
132
+ this.hidden = false;
133
+ this.slot = '';
134
+ this.attributes = {
135
+ length: 0,
136
+ getNamedItem: () => null,
137
+ getNamedItemNS: () => null,
138
+ item: () => null,
139
+ removeNamedItem: () => null,
140
+ removeNamedItemNS: () => null,
141
+ setNamedItem: () => null,
142
+ setNamedItemNS: () => null,
143
+ [Symbol.iterator]: function* () { yield null; return undefined; }
144
+ };
145
+ this.childNodes = {
146
+ length: 0,
147
+ item: () => null,
148
+ forEach: () => { },
149
+ entries: function* () { yield [0, null]; return undefined; },
150
+ keys: function* () { yield 0; return undefined; },
151
+ values: function* () { yield null; return undefined; },
152
+ [Symbol.iterator]: function* () { yield null; return undefined; }
153
+ };
154
+ this.firstChild = null;
155
+ this.lastChild = null;
156
+ this.nextSibling = null;
157
+ this.previousSibling = null;
158
+ this.parentNode = null;
159
+ this.parentElement = null;
160
+ this.childElementCount = 0;
161
+ this.firstElementChild = null;
162
+ this.lastElementChild = null;
163
+ this.nextElementSibling = null;
164
+ this.previousElementSibling = null;
165
+ this.children = {
166
+ length: 0,
167
+ item: () => null,
168
+ namedItem: () => null,
169
+ [Symbol.iterator]: function* () { yield null; return undefined; }
170
+ };
171
+ // Initialize any required properties
172
+ }
173
+ getAttribute(name) {
174
+ return null;
175
+ }
176
+ getAttributeNS(namespaceURI, localName) {
177
+ return null;
178
+ }
179
+ setAttribute(name, value) { }
180
+ setAttributeNS(namespaceURI, qualifiedName, value) { }
181
+ removeAttributeNS(namespaceURI, localName) { }
182
+ hasAttribute(name) {
183
+ return false;
184
+ }
185
+ hasAttributeNS(namespaceURI, localName) {
186
+ return false;
187
+ }
188
+ getBoundingClientRect() {
189
+ return {
190
+ top: 0,
191
+ left: 0,
192
+ bottom: 0,
193
+ right: 0,
194
+ width: 0,
195
+ height: 0,
196
+ x: 0,
197
+ y: 0,
198
+ toJSON: function () { return this; }
199
+ };
200
+ }
201
+ getClientRects() {
202
+ return {
203
+ length: 0,
204
+ item: function () { return null; },
205
+ [Symbol.iterator]: function* () { }
206
+ };
207
+ }
208
+ getElementsByClassName(classNames) {
209
+ return {
210
+ length: 0,
211
+ item: () => null,
212
+ namedItem: () => null,
213
+ [Symbol.iterator]: function* () { yield null; return undefined; }
214
+ };
215
+ }
216
+ getElementsByTagName(qualifiedName) {
217
+ return {
218
+ length: 0,
219
+ item: () => null,
220
+ namedItem: () => null,
221
+ [Symbol.iterator]: function* () { yield null; return undefined; }
222
+ };
223
+ }
224
+ getElementsByTagNameNS(namespaceURI, localName) {
225
+ return {
226
+ length: 0,
227
+ item: () => null,
228
+ namedItem: () => null,
229
+ [Symbol.iterator]: function* () { yield null; return undefined; }
230
+ };
231
+ }
232
+ querySelector(selectors) {
233
+ return null;
234
+ }
235
+ querySelectorAll(selectors) {
236
+ return {
237
+ length: 0,
238
+ item: () => null,
239
+ forEach: () => { },
240
+ entries: function* () { yield [0, null]; return undefined; },
241
+ keys: function* () { yield 0; return undefined; },
242
+ values: function* () { yield null; return undefined; },
243
+ [Symbol.iterator]: function* () { yield null; return undefined; }
244
+ };
245
+ }
246
+ matches(selectors) {
247
+ return false;
248
+ }
249
+ closest(selectors) {
250
+ return null;
251
+ }
252
+ contains(other) {
253
+ return false;
254
+ }
255
+ append(...nodes) { }
256
+ prepend(...nodes) { }
257
+ after(...nodes) { }
258
+ before(...nodes) { }
259
+ replaceWith(...nodes) { }
260
+ remove() { }
261
+ insertAdjacentElement(where, element) {
262
+ return null;
263
+ }
264
+ insertAdjacentText(where, data) { }
265
+ insertAdjacentHTML(position, text) { }
266
+ replaceChildren(...nodes) { }
267
+ };
268
+ // Define HTMLImageElement globally
269
+ globalThis.HTMLImageElement = class {
270
+ constructor() {
271
+ this.alt = '';
272
+ this.src = '';
273
+ this.srcset = '';
274
+ this.sizes = '';
275
+ this.crossOrigin = null;
276
+ this.useMap = '';
277
+ this.isMap = false;
278
+ this.width = 0;
279
+ this.height = 0;
280
+ this.naturalWidth = 0;
281
+ this.naturalHeight = 0;
282
+ this.complete = false;
283
+ this.name = '';
284
+ this.lowsrc = '';
285
+ this.align = '';
286
+ this.hspace = 0;
287
+ this.vspace = 0;
288
+ this.longDesc = '';
289
+ this.border = '';
290
+ this.x = 0;
291
+ this.y = 0;
292
+ this.currentSrc = '';
293
+ this.decoding = 'auto';
294
+ this.fetchPriority = 'auto';
295
+ this.loading = 'eager';
296
+ this.referrerPolicy = '';
297
+ // Initialize any required properties
298
+ }
299
+ decode() {
300
+ return Promise.resolve();
301
+ }
302
+ };
303
+ // Create a virtual console
304
+ const virtualConsole = new VirtualConsole();
305
+ // Function to set up DOM interfaces
306
+ function setupDOMInterfaces(window) {
307
+ var _a;
308
+ try {
309
+ // First, set up basic window properties
310
+ try {
311
+ if (!window.innerWidth) {
312
+ Object.defineProperty(window, 'innerWidth', { value: 1024 });
313
+ }
314
+ if (!window.innerHeight) {
315
+ Object.defineProperty(window, 'innerHeight', { value: 768 });
316
+ }
317
+ if (!window.devicePixelRatio) {
318
+ Object.defineProperty(window, 'devicePixelRatio', { value: 1 });
319
+ }
320
+ }
321
+ catch (error) {
322
+ console.warn('Warning: Could not set basic window properties:', error);
323
+ }
324
+ // Set up CSS interfaces
325
+ try {
326
+ if (!window.CSSRule) {
327
+ window.CSSRule = globalThis.CSSRule;
328
+ }
329
+ if (!window.CSSMediaRule) {
330
+ window.CSSMediaRule = globalThis.CSSMediaRule;
331
+ }
332
+ if (!window.CSSStyleSheet) {
333
+ window.CSSStyleSheet = globalThis.CSSStyleSheet;
334
+ }
335
+ }
336
+ catch (error) {
337
+ console.warn('Warning: Could not set CSS interfaces:', error);
338
+ }
339
+ // Set up HTML and SVG interfaces
340
+ try {
341
+ if (!window.HTMLImageElement) {
342
+ window.HTMLImageElement = globalThis.HTMLImageElement;
343
+ }
344
+ if (!window.SVGElement) {
345
+ window.SVGElement = globalThis.SVGElement;
346
+ }
347
+ }
348
+ catch (error) {
349
+ console.warn('Warning: Could not set HTML/SVG interfaces:', error);
350
+ }
351
+ // Set up screen object
352
+ try {
353
+ if (!window.screen) {
354
+ Object.defineProperty(window, 'screen', {
355
+ value: {
356
+ width: 1024,
357
+ height: 768,
358
+ availWidth: 1024,
359
+ availHeight: 768,
360
+ colorDepth: 24,
361
+ pixelDepth: 24,
362
+ orientation: {
363
+ type: 'landscape-primary',
364
+ angle: 0
365
+ }
366
+ }
367
+ });
368
+ }
369
+ }
370
+ catch (error) {
371
+ console.warn('Warning: Could not set screen object:', error);
372
+ }
373
+ // Set up storage objects
374
+ try {
375
+ if (!window.localStorage) {
376
+ const storage = {
377
+ length: 0,
378
+ getItem: () => null,
379
+ setItem: () => { },
380
+ removeItem: () => { },
381
+ clear: () => { },
382
+ key: () => null
383
+ };
384
+ try {
385
+ Object.defineProperty(window, 'localStorage', {
386
+ value: storage,
387
+ writable: false,
388
+ configurable: false
389
+ });
390
+ }
391
+ catch (error) {
392
+ // Silently ignore storage setup failures
393
+ }
394
+ }
395
+ if (!window.sessionStorage) {
396
+ const storage = {
397
+ length: 0,
398
+ getItem: () => null,
399
+ setItem: () => { },
400
+ removeItem: () => { },
401
+ clear: () => { },
402
+ key: () => null
403
+ };
404
+ try {
405
+ Object.defineProperty(window, 'sessionStorage', {
406
+ value: storage,
407
+ writable: false,
408
+ configurable: false
409
+ });
410
+ }
411
+ catch (error) {
412
+ // Silently ignore storage setup failures
413
+ }
414
+ }
415
+ }
416
+ catch (error) {
417
+ // Silently ignore storage setup failures
418
+ }
419
+ // Set up animation frame methods
420
+ try {
421
+ if (!window.requestAnimationFrame) {
422
+ window.requestAnimationFrame = (callback) => {
423
+ return setTimeout(callback, 0);
424
+ };
425
+ }
426
+ if (!window.cancelAnimationFrame) {
427
+ window.cancelAnimationFrame = (handle) => {
428
+ clearTimeout(handle);
429
+ };
430
+ }
431
+ }
432
+ catch (error) {
433
+ console.warn('Warning: Could not set animation frame methods:', error);
434
+ }
435
+ // Set up DOM methods
436
+ try {
437
+ if (!window.Document.prototype.getElementsByClassName) {
438
+ window.Document.prototype.getElementsByClassName = function (classNames) {
439
+ const elements = this.querySelectorAll('.' + classNames);
440
+ const collection = new HTMLCollection();
441
+ elements.forEach((el, i) => {
442
+ collection[i] = el;
443
+ });
444
+ return collection;
445
+ };
446
+ }
447
+ }
448
+ catch (error) {
449
+ console.warn('Warning: Could not set getElementsByClassName:', error);
450
+ }
451
+ // Set up Node methods
452
+ try {
453
+ if (!window.Node.prototype.contains) {
454
+ window.Node.prototype.contains = function (node) {
455
+ let current = node;
456
+ while (current) {
457
+ if (current === this)
458
+ return true;
459
+ current = current.parentNode;
460
+ }
461
+ return false;
462
+ };
463
+ }
464
+ }
465
+ catch (error) {
466
+ console.warn('Warning: Could not set Node.contains:', error);
467
+ }
468
+ // Set up Element methods
469
+ try {
470
+ if (!window.Element.prototype.getBoundingClientRect) {
471
+ window.Element.prototype.getBoundingClientRect = function () {
472
+ return {
473
+ top: 0,
474
+ left: 0,
475
+ bottom: 0,
476
+ right: 0,
477
+ width: 0,
478
+ height: 0,
479
+ x: 0,
480
+ y: 0,
481
+ toJSON: function () { return this; }
482
+ };
483
+ };
484
+ }
485
+ }
486
+ catch (error) {
487
+ console.warn('Warning: Could not set getBoundingClientRect:', error);
488
+ }
489
+ // Set up Document methods
490
+ try {
491
+ if (!window.Document.prototype.getSelection) {
492
+ window.Document.prototype.getSelection = function () {
493
+ const selection = {
494
+ anchorNode: null,
495
+ anchorOffset: 0,
496
+ direction: 'forward',
497
+ focusNode: null,
498
+ focusOffset: 0,
499
+ isCollapsed: true,
500
+ rangeCount: 0,
501
+ type: 'None',
502
+ getRangeAt: function () { return new window.Range(); },
503
+ removeAllRanges: function () { },
504
+ addRange: function () { },
505
+ collapse: function () { },
506
+ collapseToEnd: function () { },
507
+ collapseToStart: function () { },
508
+ deleteFromDocument: function () { },
509
+ empty: function () { },
510
+ extend: function () { },
511
+ modify: function () { },
512
+ selectAllChildren: function () { },
513
+ setBaseAndExtent: function () { },
514
+ setPosition: function () { },
515
+ toString: function () { return ''; },
516
+ containsNode: function (node, allowPartialContainment = false) {
517
+ return false;
518
+ },
519
+ removeRange: function (range) { }
520
+ };
521
+ return selection;
522
+ };
523
+ }
524
+ }
525
+ catch (error) {
526
+ console.warn('Warning: Could not set getSelection:', error);
527
+ }
528
+ // Set up Window methods
529
+ try {
530
+ if (!window.Window.prototype.getComputedStyle) {
531
+ window.Window.prototype.getComputedStyle = function (elt, pseudoElt) {
532
+ const style = {
533
+ accentColor: '',
534
+ alignContent: '',
535
+ alignItems: '',
536
+ alignSelf: '',
537
+ getPropertyValue: function (prop) { return ''; }
538
+ };
539
+ return style;
540
+ };
541
+ }
542
+ }
543
+ catch (error) {
544
+ console.warn('Warning: Could not set getComputedStyle:', error);
545
+ }
546
+ // Set up Range constructor last
547
+ try {
548
+ if (!window.Range) {
549
+ window.Range = (_a = class Range {
550
+ constructor() {
551
+ this.START_TO_START = 0;
552
+ this.START_TO_END = 1;
553
+ this.END_TO_END = 2;
554
+ this.END_TO_START = 3;
555
+ this.startContainer = document.documentElement;
556
+ this.startOffset = 0;
557
+ this.endContainer = document.documentElement;
558
+ this.endOffset = 0;
559
+ this.collapsed = true;
560
+ this.commonAncestorContainer = document.documentElement;
561
+ }
562
+ createContextualFragment(fragment) {
563
+ return document.createDocumentFragment();
564
+ }
565
+ detach() { }
566
+ cloneContents() {
567
+ return document.createDocumentFragment();
568
+ }
569
+ cloneRange() {
570
+ return new _a();
571
+ }
572
+ collapse(toStart = false) { }
573
+ compareBoundaryPoints(how, sourceRange) {
574
+ return 0;
575
+ }
576
+ comparePoint(node, offset) {
577
+ return 0;
578
+ }
579
+ deleteContents() { }
580
+ extractContents() {
581
+ return document.createDocumentFragment();
582
+ }
583
+ getBoundingClientRect() {
584
+ return {
585
+ top: 0,
586
+ left: 0,
587
+ bottom: 0,
588
+ right: 0,
589
+ width: 0,
590
+ height: 0,
591
+ x: 0,
592
+ y: 0,
593
+ toJSON: function () { return this; }
594
+ };
595
+ }
596
+ getClientRects() {
597
+ return {
598
+ length: 0,
599
+ item: function () { return null; },
600
+ [Symbol.iterator]: function* () { }
601
+ };
602
+ }
603
+ insertNode(node) { }
604
+ intersectsNode(node) {
605
+ return false;
606
+ }
607
+ isPointInRange(node, offset) {
608
+ return false;
609
+ }
610
+ selectNode(node) { }
611
+ selectNodeContents(node) {
612
+ this.startContainer = node;
613
+ this.startOffset = 0;
614
+ this.endContainer = node;
615
+ this.endOffset = node.childNodes.length;
616
+ this.collapsed = false;
617
+ }
618
+ setEnd(node, offset) { }
619
+ setEndAfter(node) { }
620
+ setEndBefore(node) { }
621
+ setStart(node, offset) { }
622
+ setStartAfter(node) { }
623
+ setStartBefore(node) { }
624
+ surroundContents(newParent) { }
625
+ },
626
+ _a.START_TO_START = 0,
627
+ _a.START_TO_END = 1,
628
+ _a.END_TO_END = 2,
629
+ _a.END_TO_START = 3,
630
+ _a);
631
+ }
632
+ }
633
+ catch (error) {
634
+ console.warn('Warning: Could not set Range constructor:', error);
635
+ }
636
+ }
637
+ catch (error) {
638
+ console.error('Error in setupDOMInterfaces:', error);
639
+ // Don't throw the error, just log it
640
+ }
641
+ }
642
+ // Create a virtual DOM
643
+ const dom = new JSDOM('<!DOCTYPE html><html><body></body></html>', {
644
+ virtualConsole,
645
+ runScripts: 'dangerously',
646
+ resources: 'usable',
647
+ pretendToBeVisual: true,
648
+ beforeParse(window) {
649
+ setupDOMInterfaces(window);
650
+ }
651
+ });
652
+ // Get the window object
653
+ const window = dom.window;
654
+ // Add window to global scope
655
+ globalThis.window = window;
656
+ // Add document to global scope
657
+ globalThis.document = window.document;
658
+ // Add required DOM interfaces to global scope
659
+ globalThis.Element = window.Element;
660
+ globalThis.Node = window.Node;
661
+ globalThis.NodeFilter = window.NodeFilter;
662
+ globalThis.Range = window.Range;
663
+ globalThis.DOMParser = window.DOMParser;
664
+ globalThis.XMLSerializer = window.XMLSerializer;
665
+ globalThis.navigator = window.navigator;
666
+ globalThis.HTMLElement = window.HTMLElement;
667
+ // Define DOMSettableTokenList
668
+ globalThis.DOMSettableTokenList = class {
669
+ constructor() {
670
+ this.length = 0;
671
+ this.value = '';
672
+ }
673
+ add(token) { }
674
+ contains(token) { return false; }
675
+ item(index) { return null; }
676
+ remove(token) { }
677
+ replace(oldToken, newToken) { return false; }
678
+ supports(token) { return false; }
679
+ toggle(token, force) { return false; }
680
+ [Symbol.iterator]() {
681
+ return function* () { yield ''; return undefined; }();
682
+ }
683
+ };
684
+ // Define HTML element types
685
+ globalThis.HTMLIFrameElement = class extends globalThis.HTMLElement {
686
+ constructor() {
687
+ super();
688
+ this.align = '';
689
+ this.allow = '';
690
+ this.allowFullscreen = false;
691
+ this.contentDocument = null;
692
+ this.contentWindow = null;
693
+ this.frameBorder = '';
694
+ this.height = '';
695
+ this.longDesc = '';
696
+ this.marginHeight = '';
697
+ this.marginWidth = '';
698
+ this.name = '';
699
+ this.referrerPolicy = '';
700
+ this.sandbox = {
701
+ length: 0,
702
+ value: '',
703
+ add: () => { },
704
+ contains: () => false,
705
+ item: () => null,
706
+ remove: () => { },
707
+ replace: () => false,
708
+ supports: () => false,
709
+ toggle: () => false,
710
+ [Symbol.iterator]: function* () { yield ''; return undefined; }
711
+ };
712
+ this.scrolling = '';
713
+ this.src = '';
714
+ this.srcdoc = '';
715
+ this.width = '';
716
+ }
717
+ };
718
+ globalThis.HTMLOListElement = class extends globalThis.HTMLElement {
719
+ constructor() {
720
+ super();
721
+ this.type = '';
722
+ this.compact = false;
723
+ this.reversed = false;
724
+ this.start = 0;
725
+ }
726
+ };
727
+ globalThis.HTMLUListElement = class extends globalThis.HTMLElement {
728
+ constructor() {
729
+ super();
730
+ this.type = '';
731
+ this.compact = false;
732
+ }
733
+ };
734
+ globalThis.HTMLTableElement = class extends globalThis.HTMLElement {
735
+ constructor() {
736
+ super();
737
+ this.caption = null;
738
+ this.tHead = null;
739
+ this.tFoot = null;
740
+ this.tBodies = {
741
+ length: 0,
742
+ item: () => null,
743
+ namedItem: () => null,
744
+ [Symbol.iterator]: function* () { yield null; return undefined; }
745
+ };
746
+ this.rows = {
747
+ length: 0,
748
+ item: () => null,
749
+ namedItem: () => null,
750
+ [Symbol.iterator]: function* () { yield null; return undefined; }
751
+ };
752
+ this.align = '';
753
+ this.bgColor = '';
754
+ this.border = '';
755
+ this.cellPadding = '';
756
+ this.cellSpacing = '';
757
+ this.frame = '';
758
+ this.rules = '';
759
+ this.summary = '';
760
+ this.width = '';
761
+ }
762
+ createCaption() {
763
+ return new globalThis.HTMLTableCaptionElement();
764
+ }
765
+ deleteCaption() { }
766
+ createTHead() {
767
+ return new globalThis.HTMLTableSectionElement();
768
+ }
769
+ deleteTHead() { }
770
+ createTFoot() {
771
+ return new globalThis.HTMLTableSectionElement();
772
+ }
773
+ deleteTFoot() { }
774
+ createTBody() {
775
+ return new globalThis.HTMLTableSectionElement();
776
+ }
777
+ insertRow(index) {
778
+ return new globalThis.HTMLTableRowElement();
779
+ }
780
+ deleteRow(index) { }
781
+ };
782
+ globalThis.HTMLTableRowElement = class extends globalThis.HTMLElement {
783
+ constructor() {
784
+ super();
785
+ this.rowIndex = 0;
786
+ this.sectionRowIndex = 0;
787
+ this.cells = {
788
+ length: 0,
789
+ item: () => null,
790
+ namedItem: () => null,
791
+ [Symbol.iterator]: function* () { yield null; return undefined; }
792
+ };
793
+ this.align = '';
794
+ this.bgColor = '';
795
+ this.ch = '';
796
+ this.chOff = '';
797
+ this.vAlign = '';
798
+ }
799
+ insertCell(index) {
800
+ return new globalThis.HTMLTableCellElement();
801
+ }
802
+ deleteCell(index) { }
803
+ };
804
+ globalThis.HTMLTableCellElement = class extends globalThis.HTMLElement {
805
+ constructor() {
806
+ super();
807
+ this.colSpan = 1;
808
+ this.rowSpan = 1;
809
+ this.headers = {
810
+ length: 0,
811
+ value: '',
812
+ add: () => { },
813
+ contains: () => false,
814
+ item: () => null,
815
+ remove: () => { },
816
+ replace: () => false,
817
+ supports: () => false,
818
+ toggle: () => false,
819
+ [Symbol.iterator]: function* () { yield ''; return undefined; }
820
+ };
821
+ this.cellIndex = 0;
822
+ this.scope = '';
823
+ this.abbr = '';
824
+ this.align = '';
825
+ this.axis = '';
826
+ this.bgColor = '';
827
+ this.ch = '';
828
+ this.chOff = '';
829
+ this.height = '';
830
+ this.noWrap = false;
831
+ this.vAlign = '';
832
+ this.width = '';
833
+ }
834
+ };
835
+ globalThis.HTMLTableSectionElement = class extends globalThis.HTMLElement {
836
+ constructor() {
837
+ super();
838
+ this.rows = {
839
+ length: 0,
840
+ item: () => null,
841
+ namedItem: () => null,
842
+ [Symbol.iterator]: function* () { yield null; return undefined; }
843
+ };
844
+ this.align = '';
845
+ this.ch = '';
846
+ this.chOff = '';
847
+ this.vAlign = '';
848
+ }
849
+ insertRow(index) {
850
+ return new globalThis.HTMLTableRowElement();
851
+ }
852
+ deleteRow(index) { }
853
+ };
854
+ globalThis.HTMLTableCaptionElement = class extends globalThis.HTMLElement {
855
+ constructor() {
856
+ super();
857
+ this.align = '';
858
+ }
859
+ };
860
+ const program = new Command();
861
+ program
862
+ .name('defuddle')
863
+ .description('Extract article content from web pages')
864
+ .version('0.1.0');
865
+ program
866
+ .command('parse')
867
+ .description('Parse HTML content from a file or URL')
868
+ .argument('<source>', 'HTML file path or URL to parse')
869
+ .option('-o, --output <file>', 'Output file path (default: stdout)')
870
+ .option('-m, --markdown', 'Convert content to markdown format')
871
+ .option('--md', 'Alias for --markdown')
872
+ .option('-j, --json', 'Output as JSON with metadata and content')
873
+ .option('-p, --property <name>', 'Extract a specific property (e.g., title, description, domain)')
874
+ .option('--debug', 'Enable debug mode')
875
+ .action(async (source, options) => {
876
+ try {
877
+ // Handle --md alias
878
+ if (options.md) {
879
+ options.markdown = true;
880
+ }
881
+ let html;
882
+ try {
883
+ // Determine if source is a URL or file path
884
+ if (source.startsWith('http://') || source.startsWith('https://')) {
885
+ const response = await fetch(source);
886
+ html = await response.text();
887
+ }
888
+ else {
889
+ const filePath = resolve(process.cwd(), source);
890
+ html = await readFile(filePath, 'utf-8');
891
+ }
892
+ // Create a new JSDOM instance with the HTML content
893
+ const contentDom = new JSDOM(html, {
894
+ virtualConsole,
895
+ runScripts: 'dangerously',
896
+ resources: 'usable',
897
+ pretendToBeVisual: true,
898
+ url: source.startsWith('http') ? source : undefined,
899
+ beforeParse(window) {
900
+ try {
901
+ setupDOMInterfaces(window);
902
+ }
903
+ catch (error) {
904
+ console.error('Error setting up DOM interfaces:', error);
905
+ }
906
+ }
907
+ });
908
+ // Initialize document properties
909
+ const doc = contentDom.window.document;
910
+ // Ensure document has required properties
911
+ if (!doc.documentElement) {
912
+ throw new Error('Document has no root element');
913
+ }
914
+ // Set up document properties
915
+ try {
916
+ doc.documentElement.style.cssText = '';
917
+ doc.documentElement.className = '';
918
+ }
919
+ catch (error) {
920
+ console.warn('Warning: Could not set document element properties:', error);
921
+ }
922
+ // Ensure body exists and is properly set up
923
+ if (!doc.body) {
924
+ const body = doc.createElement('body');
925
+ doc.documentElement.appendChild(body);
926
+ }
927
+ try {
928
+ doc.body.style.cssText = '';
929
+ doc.body.className = '';
930
+ }
931
+ catch (error) {
932
+ console.warn('Warning: Could not set body properties:', error);
933
+ }
934
+ // Set up viewport and ensure head exists
935
+ if (!doc.head) {
936
+ const head = doc.createElement('head');
937
+ doc.documentElement.insertBefore(head, doc.body);
938
+ }
939
+ // Add viewport meta tag
940
+ try {
941
+ const viewport = doc.createElement('meta');
942
+ viewport.setAttribute('name', 'viewport');
943
+ viewport.setAttribute('content', 'width=device-width, initial-scale=1');
944
+ doc.head.appendChild(viewport);
945
+ }
946
+ catch (error) {
947
+ console.warn('Warning: Could not add viewport meta tag:', error);
948
+ }
949
+ // Add a base style element for mobile styles
950
+ try {
951
+ const style = doc.createElement('style');
952
+ style.textContent = `
953
+ @media (max-width: 768px) {
954
+ body { width: 100%; }
955
+ }
956
+ `;
957
+ doc.head.appendChild(style);
958
+ }
959
+ catch (error) {
960
+ console.warn('Warning: Could not add style element:', error);
961
+ }
962
+ // Parse content with debug mode if enabled
963
+ try {
964
+ const defuddle = new Defuddle(doc, {
965
+ debug: options.debug
966
+ });
967
+ const result = await defuddle.parse();
968
+ // Format output
969
+ let output;
970
+ let content;
971
+ let contentMarkdown;
972
+ // Convert content to markdown if requested
973
+ if (options.markdown || options.json) {
974
+ contentMarkdown = createMarkdownContent(result.content, source);
975
+ }
976
+ // Format the response based on options
977
+ if (options.property) {
978
+ // Extract specific property
979
+ const property = options.property.toLowerCase();
980
+ if (property in result) {
981
+ output = result[property]?.toString() || '';
982
+ }
983
+ else {
984
+ console.error(chalk.red(`Error: Property "${property}" not found in response`));
985
+ process.exit(1);
986
+ }
987
+ }
988
+ else if (options.json) {
989
+ const jsonObj = {
990
+ content: result.content,
991
+ title: result.title,
992
+ description: result.description,
993
+ domain: result.domain,
994
+ favicon: result.favicon,
995
+ image: result.image,
996
+ parseTime: result.parseTime,
997
+ published: result.published,
998
+ author: result.author,
999
+ site: result.site,
1000
+ schemaOrgData: result.schemaOrgData,
1001
+ wordCount: result.wordCount
1002
+ };
1003
+ // Only include markdown content if markdown flag is set
1004
+ if (options.markdown) {
1005
+ jsonObj.contentMarkdown = contentMarkdown;
1006
+ }
1007
+ output = JSON.stringify(jsonObj, null, 2)
1008
+ .replace(/"([^"]+)":/g, chalk.cyan('"$1":'))
1009
+ .replace(/: "([^"]+)"/g, chalk.yellow(': "$1"'))
1010
+ .replace(/: (\d+)/g, chalk.yellow(': $1'))
1011
+ .replace(/: (true|false|null)/g, chalk.magenta(': $1'));
1012
+ }
1013
+ else {
1014
+ output = options.markdown ? contentMarkdown : result.content;
1015
+ }
1016
+ // Handle output
1017
+ if (options.output) {
1018
+ const outputPath = resolve(process.cwd(), options.output);
1019
+ await writeFile(outputPath, output, 'utf-8');
1020
+ console.log(chalk.green(`Output written to ${options.output}`));
1021
+ }
1022
+ else {
1023
+ console.log(output);
1024
+ }
1025
+ }
1026
+ catch (error) {
1027
+ console.error(chalk.red('Error during parsing:'), error);
1028
+ process.exit(1);
1029
+ }
1030
+ }
1031
+ catch (error) {
1032
+ console.error(chalk.red('Error loading content:'), error instanceof Error ? error.message : 'Unknown error occurred');
1033
+ process.exit(1);
1034
+ }
1035
+ }
1036
+ catch (error) {
1037
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : 'Unknown error occurred');
1038
+ process.exit(1);
1039
+ }
1040
+ });
1041
+ program.parse();