defuddle-cli 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,196 @@
1
+ import { DOMWindow } from 'jsdom';
2
+ import { setupDocumentMethods, setupWindowMethods } from './document.js';
3
+
4
+ // Type for DOM interface setup functions
5
+ export type SetupFunction = (window: DOMWindow) => void;
6
+
7
+ // Setup basic window properties
8
+ export const setupBasicWindow: SetupFunction = (window) => {
9
+ if (!window.innerWidth) {
10
+ Object.defineProperty(window, 'innerWidth', { value: 1024 });
11
+ }
12
+ if (!window.innerHeight) {
13
+ Object.defineProperty(window, 'innerHeight', { value: 768 });
14
+ }
15
+ if (!window.devicePixelRatio) {
16
+ Object.defineProperty(window, 'devicePixelRatio', { value: 1 });
17
+ }
18
+ };
19
+
20
+ // Setup CSS interfaces
21
+ export const setupCSSInterfaces: SetupFunction = (window) => {
22
+ if (!window.CSSRule) {
23
+ window.CSSRule = (globalThis as any).CSSRule as any;
24
+ }
25
+ if (!window.CSSMediaRule) {
26
+ window.CSSMediaRule = (globalThis as any).CSSMediaRule as any;
27
+ }
28
+ if (!window.CSSStyleSheet) {
29
+ window.CSSStyleSheet = (globalThis as any).CSSStyleSheet as any;
30
+ }
31
+ };
32
+
33
+ // Setup HTML and SVG interfaces
34
+ export const setupHTMLAndSVG: SetupFunction = (window) => {
35
+ if (!window.HTMLImageElement) {
36
+ window.HTMLImageElement = (globalThis as any).HTMLImageElement as any;
37
+ }
38
+ if (!window.SVGElement) {
39
+ window.SVGElement = (globalThis as any).SVGElement as any;
40
+ }
41
+ if (!window.HTMLAnchorElement) {
42
+ window.HTMLAnchorElement = (globalThis as any).HTMLAnchorElement as any;
43
+ }
44
+ };
45
+
46
+ // Setup screen object
47
+ export const setupScreen: SetupFunction = (window) => {
48
+ if (!window.screen) {
49
+ Object.defineProperty(window, 'screen', {
50
+ value: {
51
+ width: 1024,
52
+ height: 768,
53
+ availWidth: 1024,
54
+ availHeight: 768,
55
+ colorDepth: 24,
56
+ pixelDepth: 24,
57
+ orientation: {
58
+ type: 'landscape-primary',
59
+ angle: 0
60
+ }
61
+ }
62
+ });
63
+ }
64
+ };
65
+
66
+ // Setup storage objects
67
+ export const setupStorage: SetupFunction = (window) => {
68
+ const createStorage = () => {
69
+ const storage = {
70
+ length: 0,
71
+ getItem: () => null,
72
+ setItem: () => {},
73
+ removeItem: () => {},
74
+ clear: () => {},
75
+ key: () => null
76
+ };
77
+
78
+ // Make the storage object non-extensible
79
+ Object.preventExtensions(storage);
80
+ return storage;
81
+ };
82
+
83
+ try {
84
+ // Create storage objects
85
+ const localStorage = createStorage();
86
+ const sessionStorage = createStorage();
87
+
88
+ // Define properties with more permissive attributes
89
+ Object.defineProperties(window, {
90
+ localStorage: {
91
+ value: localStorage,
92
+ writable: true,
93
+ configurable: true
94
+ },
95
+ sessionStorage: {
96
+ value: sessionStorage,
97
+ writable: true,
98
+ configurable: true
99
+ }
100
+ });
101
+ } catch (error) {
102
+ // Log the error but don't throw
103
+ console.warn('Warning: Could not set up storage objects:', error instanceof Error ? error.message : 'Unknown error');
104
+ }
105
+ };
106
+
107
+ // Setup animation frame methods
108
+ export const setupAnimationFrame: SetupFunction = (window) => {
109
+ if (!window.requestAnimationFrame) {
110
+ window.requestAnimationFrame = (callback: FrameRequestCallback): number => {
111
+ return setTimeout(callback, 0) as unknown as number;
112
+ };
113
+ }
114
+ if (!window.cancelAnimationFrame) {
115
+ window.cancelAnimationFrame = (handle: number): void => {
116
+ clearTimeout(handle as unknown as number);
117
+ };
118
+ }
119
+ };
120
+
121
+ // Setup DOM methods
122
+ export const setupDOMMethods: SetupFunction = (window) => {
123
+ if (!window.Document.prototype.getElementsByClassName) {
124
+ window.Document.prototype.getElementsByClassName = function(classNames: string): HTMLCollectionOf<Element> {
125
+ const elements = this.querySelectorAll('.' + classNames);
126
+ const collection = new HTMLCollection();
127
+ elements.forEach((el, i) => {
128
+ collection[i] = el;
129
+ });
130
+ return collection;
131
+ };
132
+ }
133
+ };
134
+
135
+ // Setup Node methods
136
+ export const setupNodeMethods: SetupFunction = (window) => {
137
+ if (!window.Node.prototype.contains) {
138
+ window.Node.prototype.contains = function(node: Node): boolean {
139
+ let current: Node | null = node;
140
+ while (current) {
141
+ if (current === this) return true;
142
+ current = current.parentNode;
143
+ }
144
+ return false;
145
+ };
146
+ }
147
+ };
148
+
149
+ // Setup Element methods
150
+ export const setupElementMethods: SetupFunction = (window) => {
151
+ if (!window.Element.prototype.getBoundingClientRect) {
152
+ window.Element.prototype.getBoundingClientRect = function(): DOMRect {
153
+ return {
154
+ top: 0,
155
+ left: 0,
156
+ bottom: 0,
157
+ right: 0,
158
+ width: 0,
159
+ height: 0,
160
+ x: 0,
161
+ y: 0,
162
+ toJSON: function() { return this; }
163
+ };
164
+ };
165
+ }
166
+ };
167
+
168
+ // Main setup function that orchestrates all the individual setups
169
+ export const setupDOMInterfaces = (window: DOMWindow): void => {
170
+ const setupFunctions: [string, SetupFunction][] = [
171
+ ['basic window', setupBasicWindow],
172
+ ['CSS interfaces', setupCSSInterfaces],
173
+ ['HTML and SVG interfaces', setupHTMLAndSVG],
174
+ ['screen object', setupScreen],
175
+ ['storage objects', setupStorage],
176
+ ['animation frame methods', setupAnimationFrame],
177
+ ['DOM methods', setupDOMMethods],
178
+ ['Node methods', setupNodeMethods],
179
+ ['Element methods', setupElementMethods],
180
+ ['Document methods', setupDocumentMethods],
181
+ ['Window methods', setupWindowMethods]
182
+ ];
183
+
184
+ try {
185
+ for (const [name, setup] of setupFunctions) {
186
+ try {
187
+ setup(window);
188
+ } catch (error) {
189
+ console.warn(`Warning: Could not set up ${name}:`, error instanceof Error ? error.message : 'Unknown error');
190
+ }
191
+ }
192
+ } catch (error) {
193
+ console.error('Error in setupDOMInterfaces:', error instanceof Error ? error.message : 'Unknown error');
194
+ // Don't throw the error, just log it
195
+ }
196
+ };
package/src/index.ts CHANGED
@@ -9,6 +9,9 @@ import { readFile, writeFile } from 'fs/promises';
9
9
  import { fileURLToPath } from 'url';
10
10
  import { dirname, resolve } from 'path';
11
11
  import { createMarkdownContent } from './markdown.js';
12
+ import { setupDOMInterfaces } from './dom/interfaces/setup.js';
13
+ import { setupRange } from './dom/interfaces/range.js';
14
+ import { setupDocumentMethods, setupWindowMethods } from './dom/interfaces/document.js';
12
15
 
13
16
  interface DOMSettableTokenList {
14
17
  length: number;
@@ -52,7 +55,7 @@ const __dirname = dirname(__filename);
52
55
  }
53
56
  };
54
57
 
55
- // Add static properties
58
+ // Static properties
56
59
  Object.defineProperties((globalThis as any).CSSRule, {
57
60
  STYLE_RULE: { value: 1, writable: false },
58
61
  CHARSET_RULE: { value: 2, writable: false },
@@ -377,370 +380,6 @@ Object.defineProperties((globalThis as any).CSSRule, {
377
380
  // Create a virtual console
378
381
  const virtualConsole = new VirtualConsole();
379
382
 
380
- // Function to set up DOM interfaces
381
- function setupDOMInterfaces(window: DOMWindow) {
382
- try {
383
- // First, set up basic window properties
384
- try {
385
- if (!window.innerWidth) {
386
- Object.defineProperty(window, 'innerWidth', { value: 1024 });
387
- }
388
- if (!window.innerHeight) {
389
- Object.defineProperty(window, 'innerHeight', { value: 768 });
390
- }
391
- if (!window.devicePixelRatio) {
392
- Object.defineProperty(window, 'devicePixelRatio', { value: 1 });
393
- }
394
- } catch (error) {
395
- console.warn('Warning: Could not set basic window properties:', error);
396
- }
397
-
398
- // Set up CSS interfaces
399
- try {
400
- if (!window.CSSRule) {
401
- window.CSSRule = (globalThis as any).CSSRule as any;
402
- }
403
- if (!window.CSSMediaRule) {
404
- window.CSSMediaRule = (globalThis as any).CSSMediaRule as any;
405
- }
406
- if (!window.CSSStyleSheet) {
407
- window.CSSStyleSheet = (globalThis as any).CSSStyleSheet as any;
408
- }
409
- } catch (error) {
410
- console.warn('Warning: Could not set CSS interfaces:', error);
411
- }
412
-
413
- // Set up HTML and SVG interfaces
414
- try {
415
- if (!window.HTMLImageElement) {
416
- window.HTMLImageElement = (globalThis as any).HTMLImageElement as any;
417
- }
418
- if (!window.SVGElement) {
419
- window.SVGElement = (globalThis as any).SVGElement as any;
420
- }
421
- } catch (error) {
422
- console.warn('Warning: Could not set HTML/SVG interfaces:', error);
423
- }
424
-
425
- // Set up screen object
426
- try {
427
- if (!window.screen) {
428
- Object.defineProperty(window, 'screen', {
429
- value: {
430
- width: 1024,
431
- height: 768,
432
- availWidth: 1024,
433
- availHeight: 768,
434
- colorDepth: 24,
435
- pixelDepth: 24,
436
- orientation: {
437
- type: 'landscape-primary',
438
- angle: 0
439
- }
440
- }
441
- });
442
- }
443
- } catch (error) {
444
- console.warn('Warning: Could not set screen object:', error);
445
- }
446
-
447
- // Set up storage objects
448
- try {
449
- if (!window.localStorage) {
450
- const storage = {
451
- length: 0,
452
- getItem: () => null,
453
- setItem: () => {},
454
- removeItem: () => {},
455
- clear: () => {},
456
- key: () => null
457
- };
458
- try {
459
- Object.defineProperty(window, 'localStorage', {
460
- value: storage,
461
- writable: false,
462
- configurable: false
463
- });
464
- } catch (error) {
465
- // Silently ignore storage setup failures
466
- }
467
- }
468
- if (!window.sessionStorage) {
469
- const storage = {
470
- length: 0,
471
- getItem: () => null,
472
- setItem: () => {},
473
- removeItem: () => {},
474
- clear: () => {},
475
- key: () => null
476
- };
477
- try {
478
- Object.defineProperty(window, 'sessionStorage', {
479
- value: storage,
480
- writable: false,
481
- configurable: false
482
- });
483
- } catch (error) {
484
- // Silently ignore storage setup failures
485
- }
486
- }
487
- } catch (error) {
488
- // Silently ignore storage setup failures
489
- }
490
-
491
- // Set up animation frame methods
492
- try {
493
- if (!window.requestAnimationFrame) {
494
- window.requestAnimationFrame = (callback: FrameRequestCallback): number => {
495
- return setTimeout(callback, 0) as unknown as number;
496
- };
497
- }
498
- if (!window.cancelAnimationFrame) {
499
- window.cancelAnimationFrame = (handle: number): void => {
500
- clearTimeout(handle as unknown as number);
501
- };
502
- }
503
- } catch (error) {
504
- console.warn('Warning: Could not set animation frame methods:', error);
505
- }
506
-
507
- // Set up DOM methods
508
- try {
509
- if (!window.Document.prototype.getElementsByClassName) {
510
- window.Document.prototype.getElementsByClassName = function(classNames: string): HTMLCollectionOf<Element> {
511
- const elements = this.querySelectorAll('.' + classNames);
512
- const collection = new HTMLCollection();
513
- elements.forEach((el, i) => {
514
- collection[i] = el;
515
- });
516
- return collection;
517
- };
518
- }
519
- } catch (error) {
520
- console.warn('Warning: Could not set getElementsByClassName:', error);
521
- }
522
-
523
- // Set up Node methods
524
- try {
525
- if (!window.Node.prototype.contains) {
526
- window.Node.prototype.contains = function(node: Node): boolean {
527
- let current: Node | null = node;
528
- while (current) {
529
- if (current === this) return true;
530
- current = current.parentNode;
531
- }
532
- return false;
533
- };
534
- }
535
- } catch (error) {
536
- console.warn('Warning: Could not set Node.contains:', error);
537
- }
538
-
539
- // Set up Element methods
540
- try {
541
- if (!window.Element.prototype.getBoundingClientRect) {
542
- window.Element.prototype.getBoundingClientRect = function(): DOMRect {
543
- return {
544
- top: 0,
545
- left: 0,
546
- bottom: 0,
547
- right: 0,
548
- width: 0,
549
- height: 0,
550
- x: 0,
551
- y: 0,
552
- toJSON: function() { return this; }
553
- };
554
- };
555
- }
556
- } catch (error) {
557
- console.warn('Warning: Could not set getBoundingClientRect:', error);
558
- }
559
-
560
- // Set up Document methods
561
- try {
562
- if (!window.Document.prototype.getSelection) {
563
- window.Document.prototype.getSelection = function(): Selection | null {
564
- const selection = {
565
- anchorNode: null,
566
- anchorOffset: 0,
567
- direction: 'forward',
568
- focusNode: null,
569
- focusOffset: 0,
570
- isCollapsed: true,
571
- rangeCount: 0,
572
- type: 'None',
573
- getRangeAt: function() { return new window.Range(); },
574
- removeAllRanges: function() {},
575
- addRange: function() {},
576
- collapse: function() {},
577
- collapseToEnd: function() {},
578
- collapseToStart: function() {},
579
- deleteFromDocument: function() {},
580
- empty: function() {},
581
- extend: function() {},
582
- modify: function() {},
583
- selectAllChildren: function() {},
584
- setBaseAndExtent: function() {},
585
- setPosition: function() {},
586
- toString: function() { return ''; },
587
- containsNode: function(node: Node, allowPartialContainment: boolean = false): boolean {
588
- return false;
589
- },
590
- removeRange: function(range: Range): void {}
591
- } as unknown as Selection;
592
- return selection;
593
- };
594
- }
595
- } catch (error) {
596
- console.warn('Warning: Could not set getSelection:', error);
597
- }
598
-
599
- // Set up Window methods
600
- try {
601
- if (!window.Window.prototype.getComputedStyle) {
602
- window.Window.prototype.getComputedStyle = function(elt: Element, pseudoElt?: string | null): CSSStyleDeclaration {
603
- const style = {
604
- accentColor: '',
605
- alignContent: '',
606
- alignItems: '',
607
- alignSelf: '',
608
- getPropertyValue: function(prop: string): string { return ''; }
609
- } as CSSStyleDeclaration;
610
- return style;
611
- };
612
- }
613
- } catch (error) {
614
- console.warn('Warning: Could not set getComputedStyle:', error);
615
- }
616
-
617
- // Set up Range constructor last
618
- try {
619
- if (!window.Range) {
620
- window.Range = class Range {
621
- static readonly START_TO_START = 0;
622
- static readonly START_TO_END = 1;
623
- static readonly END_TO_END = 2;
624
- static readonly END_TO_START = 3;
625
-
626
- readonly START_TO_START = 0;
627
- readonly START_TO_END = 1;
628
- readonly END_TO_END = 2;
629
- readonly END_TO_START = 3;
630
-
631
- startContainer: Node;
632
- startOffset: number;
633
- endContainer: Node;
634
- endOffset: number;
635
- collapsed: boolean;
636
- commonAncestorContainer: Node;
637
-
638
- constructor() {
639
- this.startContainer = document.documentElement;
640
- this.startOffset = 0;
641
- this.endContainer = document.documentElement;
642
- this.endOffset = 0;
643
- this.collapsed = true;
644
- this.commonAncestorContainer = document.documentElement;
645
- }
646
-
647
- createContextualFragment(fragment: string): DocumentFragment {
648
- return document.createDocumentFragment();
649
- }
650
-
651
- detach(): void {}
652
-
653
- cloneContents(): DocumentFragment {
654
- return document.createDocumentFragment();
655
- }
656
-
657
- cloneRange(): Range {
658
- return new Range();
659
- }
660
-
661
- collapse(toStart: boolean = false): void {}
662
-
663
- compareBoundaryPoints(how: number, sourceRange: Range): number {
664
- return 0;
665
- }
666
-
667
- comparePoint(node: Node, offset: number): number {
668
- return 0;
669
- }
670
-
671
- deleteContents(): void {}
672
-
673
- extractContents(): DocumentFragment {
674
- return document.createDocumentFragment();
675
- }
676
-
677
- getBoundingClientRect(): DOMRect {
678
- return {
679
- top: 0,
680
- left: 0,
681
- bottom: 0,
682
- right: 0,
683
- width: 0,
684
- height: 0,
685
- x: 0,
686
- y: 0,
687
- toJSON: function() { return this; }
688
- };
689
- }
690
-
691
- getClientRects(): DOMRectList {
692
- return {
693
- length: 0,
694
- item: function() { return null; },
695
- [Symbol.iterator]: function*() {}
696
- } as DOMRectList;
697
- }
698
-
699
- insertNode(node: Node): void {}
700
-
701
- intersectsNode(node: Node): boolean {
702
- return false;
703
- }
704
-
705
- isPointInRange(node: Node, offset: number): boolean {
706
- return false;
707
- }
708
-
709
- selectNode(node: Node): void {}
710
-
711
- selectNodeContents(node: Node): void {
712
- this.startContainer = node;
713
- this.startOffset = 0;
714
- this.endContainer = node;
715
- this.endOffset = node.childNodes.length;
716
- this.collapsed = false;
717
- }
718
-
719
- setEnd(node: Node, offset: number): void {}
720
-
721
- setEndAfter(node: Node): void {}
722
-
723
- setEndBefore(node: Node): void {}
724
-
725
- setStart(node: Node, offset: number): void {}
726
-
727
- setStartAfter(node: Node): void {}
728
-
729
- setStartBefore(node: Node): void {}
730
-
731
- surroundContents(newParent: Node): void {}
732
- };
733
- }
734
- } catch (error) {
735
- console.warn('Warning: Could not set Range constructor:', error);
736
- }
737
-
738
- } catch (error) {
739
- console.error('Error in setupDOMInterfaces:', error);
740
- // Don't throw the error, just log it
741
- }
742
- }
743
-
744
383
  // Create a virtual DOM
745
384
  const dom = new JSDOM('<!DOCTYPE html><html><body></body></html>', {
746
385
  virtualConsole,
@@ -749,6 +388,9 @@ const dom = new JSDOM('<!DOCTYPE html><html><body></body></html>', {
749
388
  pretendToBeVisual: true,
750
389
  beforeParse(window: DOMWindow) {
751
390
  setupDOMInterfaces(window);
391
+ setupRange(window);
392
+ setupDocumentMethods(window);
393
+ setupWindowMethods(window);
752
394
  }
753
395
  });
754
396
 
@@ -768,7 +410,16 @@ const window = dom.window;
768
410
  (globalThis as any).Range = window.Range;
769
411
  (globalThis as any).DOMParser = window.DOMParser;
770
412
  (globalThis as any).XMLSerializer = window.XMLSerializer;
771
- (globalThis as any).navigator = window.navigator;
413
+
414
+ // Handle navigator property
415
+ if (!globalThis.navigator || Object.getOwnPropertyDescriptor(globalThis, 'navigator')?.configurable) {
416
+ Object.defineProperty(globalThis, 'navigator', {
417
+ value: window.navigator,
418
+ writable: false,
419
+ configurable: true
420
+ });
421
+ }
422
+
772
423
  (globalThis as any).HTMLElement = window.HTMLElement;
773
424
 
774
425
  // Define DOMSettableTokenList
@@ -971,6 +622,75 @@ const window = dom.window;
971
622
  align: string = '';
972
623
  };
973
624
 
625
+ (globalThis as any).HTMLButtonElement = class extends (globalThis as any).HTMLElement {
626
+ constructor() {
627
+ super();
628
+ }
629
+ disabled: boolean = false;
630
+ form: HTMLFormElement | null = null;
631
+ formAction: string = '';
632
+ formEnctype: string = '';
633
+ formMethod: string = '';
634
+ formNoValidate: boolean = false;
635
+ formTarget: string = '';
636
+ name: string = '';
637
+ type: string = 'submit';
638
+ value: string = '';
639
+ menu: HTMLMenuElement | null = null;
640
+ };
641
+
642
+ // Add HTMLSpanElement interface
643
+ (globalThis as any).HTMLSpanElement = class extends (globalThis as any).HTMLElement {
644
+ constructor() {
645
+ super();
646
+ }
647
+ };
648
+
649
+ // Add HTMLDivElement interface
650
+ (globalThis as any).HTMLDivElement = class extends (globalThis as any).HTMLElement {
651
+ constructor() {
652
+ super();
653
+ }
654
+ align: string = '';
655
+ };
656
+
657
+ (globalThis as any).HTMLAnchorElement = class extends (globalThis as any).HTMLElement {
658
+ constructor() {
659
+ super();
660
+ }
661
+ href: string = '';
662
+ target: string = '';
663
+ download: string = '';
664
+ ping: string = '';
665
+ rel: string = '';
666
+ relList: DOMSettableTokenList = {
667
+ length: 0,
668
+ value: '',
669
+ add: () => {},
670
+ contains: () => false,
671
+ item: () => null,
672
+ remove: () => {},
673
+ replace: () => false,
674
+ supports: () => false,
675
+ toggle: () => false,
676
+ [Symbol.iterator]: function*() { yield ''; return undefined; }
677
+ } as unknown as DOMSettableTokenList;
678
+ hreflang: string = '';
679
+ type: string = '';
680
+ text: string = '';
681
+ referrerPolicy: string = '';
682
+ origin: string = '';
683
+ protocol: string = '';
684
+ username: string = '';
685
+ password: string = '';
686
+ host: string = '';
687
+ hostname: string = '';
688
+ port: string = '';
689
+ pathname: string = '';
690
+ search: string = '';
691
+ hash: string = '';
692
+ };
693
+
974
694
  const program = new Command();
975
695
 
976
696
  program
@@ -1087,6 +807,11 @@ program
1087
807
 
1088
808
  const result = await defuddle.parse();
1089
809
 
810
+ // If in debug mode, don't show content output
811
+ if (options.debug) {
812
+ process.exit(0);
813
+ }
814
+
1090
815
  // Format output
1091
816
  let output: string;
1092
817
  let content: string;
package/src/markdown.ts CHANGED
@@ -283,17 +283,6 @@ export function createMarkdownContent(content: string, url: string) {
283
283
  }
284
284
  });
285
285
 
286
- turndownService.addRule('removeHiddenElements', {
287
- filter: function (node) {
288
- return (
289
- node.style.display === 'none'
290
- );
291
- },
292
- replacement: function () {
293
- return '';
294
- }
295
- });
296
-
297
286
  turndownService.addRule('citations', {
298
287
  filter: (node: Node): boolean => {
299
288
  if (node instanceof Element) {