@heartlandone/vega 2.75.0 → 2.75.1
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/cjs/{app-globals-bf183914.js → app-globals-35614ce8.js} +4 -4
- package/dist/cjs/{design-token-cbc01283.js → design-token-bb39e07e.js} +1 -1
- package/dist/cjs/{dto-renderer-manager-2fc24151.js → dto-renderer-manager-03e1a038.js} +197 -22
- package/dist/cjs/{element-appender-slimmer-318d29c7.js → element-appender-slimmer-daa8b9f5.js} +2 -2
- package/dist/cjs/{function-extension-ca9aa115.js → function-extension-cfbbc57e.js} +1 -1
- package/dist/cjs/{image-annotation-action-e4a35325.js → image-annotation-action-3fa501b7.js} +1 -1
- package/dist/cjs/index.cjs.js +6 -6
- package/dist/cjs/{language-extension-38e4905e.js → language-extension-8c4a56ff.js} +2 -2
- package/dist/cjs/loader.cjs.js +4 -4
- package/dist/cjs/{public-rules-661823fa.js → public-rules-dc2a8d62.js} +2 -2
- package/dist/cjs/{responsive-format-facade-f71d399c.js → responsive-format-facade-d5dba942.js} +2 -2
- package/dist/cjs/{rich-text-editor-required-rule-d5feeffd.js → rich-text-editor-required-rule-ba312af9.js} +1 -1
- package/dist/cjs/{style-formatter-566df206.js → style-formatter-49693da7.js} +1 -1
- package/dist/cjs/vega-accordion.cjs.entry.js +4 -4
- package/dist/cjs/vega-app-header-button.cjs.entry.js +4 -4
- package/dist/cjs/vega-box.cjs.entry.js +5 -5
- package/dist/cjs/vega-button-circle.cjs.entry.js +4 -4
- package/dist/cjs/vega-button.cjs.entry.js +3 -3
- package/dist/cjs/vega-card.cjs.entry.js +4 -4
- package/dist/cjs/vega-carousel.cjs.entry.js +3 -3
- package/dist/cjs/vega-chip.cjs.entry.js +4 -4
- package/dist/cjs/vega-date-picker_2.cjs.entry.js +4 -4
- package/dist/cjs/vega-dialog_2.cjs.entry.js +4 -4
- package/dist/cjs/vega-divider.cjs.entry.js +4 -4
- package/dist/cjs/vega-dropdown_5.cjs.entry.js +4 -4
- package/dist/cjs/vega-env-manager-23b8b23c.js +2 -2
- package/dist/cjs/vega-flag-icon.cjs.entry.js +4 -4
- package/dist/cjs/vega-flex.cjs.entry.js +5 -5
- package/dist/cjs/vega-font.cjs.entry.js +4 -4
- package/dist/cjs/vega-grid.cjs.entry.js +4 -4
- package/dist/cjs/vega-icon.cjs.entry.js +4 -4
- package/dist/cjs/vega-image-uploader.cjs.entry.js +3 -3
- package/dist/cjs/vega-input-phone-number.cjs.entry.js +2 -2
- package/dist/cjs/vega-input-select.cjs.entry.js +3 -3
- package/dist/cjs/vega-left-nav_5.cjs.entry.js +4 -4
- package/dist/cjs/vega-loader-wrapper_2.cjs.entry.js +3 -3
- package/dist/cjs/vega-pagination.cjs.entry.js +3 -3
- package/dist/cjs/vega-popover_2.cjs.entry.js +5 -5
- package/dist/cjs/vega-progress-tracker_2.cjs.entry.js +3 -3
- package/dist/cjs/vega-rich-text-content.cjs.entry.js +2 -2
- package/dist/cjs/vega-rich-text-editor_4.cjs.entry.js +60 -10
- package/dist/cjs/vega-selection-chip_2.cjs.entry.js +3 -3
- package/dist/cjs/vega-sidenav_3.cjs.entry.js +3 -3
- package/dist/cjs/vega-signature-capture.cjs.entry.js +4 -4
- package/dist/cjs/vega-table_11.cjs.entry.js +3 -3
- package/dist/cjs/vega-time-picker_2.cjs.entry.js +4 -4
- package/dist/cjs/vega-tooltip_2.cjs.entry.js +4 -4
- package/dist/cjs/vega.cjs.js +4 -4
- package/dist/collection/components/vega-rich-text-editor/dto/blocks/html-block.js +16 -0
- package/dist/collection/components/vega-rich-text-editor/dto/content-state.js +3 -22
- package/dist/collection/components/vega-rich-text-editor/slimmers/controllers/html-cleaner/html-content-cleaner-processor.interface.js +1 -0
- package/dist/collection/components/vega-rich-text-editor/slimmers/controllers/html-cleaner/html-content-cleaner.js +59 -0
- package/dist/collection/components/vega-rich-text-editor/slimmers/controllers/html-cleaner/index.js +4 -0
- package/dist/collection/components/vega-rich-text-editor/slimmers/controllers/html-cleaner/processors/remove-whitespace-after-opening-tag-processor.js +21 -0
- package/dist/collection/components/vega-rich-text-editor/slimmers/controllers/html-cleaner/processors/remove-whitespace-before-closing-tag-processor.js +21 -0
- package/dist/collection/components/vega-rich-text-editor/slimmers/controllers/html-cleaner/processors/remove-whitespace-between-tags-processor.js +77 -0
- package/dist/collection/components/vega-rich-text-editor/test/dto/blocks/html-block.test.js +39 -0
- package/dist/collection/components/vega-rich-text-editor/test/dto/content-state.test.js +166 -1
- package/dist/collection/components/vega-rich-text-editor/test/html-cleaner/html-content-cleaner.test.js +186 -0
- package/dist/collection/components/vega-rich-text-editor/test/html-cleaner/remove-whitespace-after-opening-tag-processor.test.js +22 -0
- package/dist/collection/components/vega-rich-text-editor/test/html-cleaner/remove-whitespace-before-closing-tag-processor.test.js +22 -0
- package/dist/collection/components/vega-rich-text-editor/test/html-cleaner/remove-whitespace-between-tags-processor.test.js +26 -0
- package/dist/collection/helpers/code-format/code-formatter.js +57 -7
- package/dist/collection/helpers/code-format/test/code-formatter.test.js +48 -0
- package/dist/esm/{app-globals-f3414aca.js → app-globals-2ac60ff5.js} +4 -4
- package/dist/esm/{design-token-9b299589.js → design-token-007efbaa.js} +1 -1
- package/dist/esm/{dto-renderer-manager-f07cf2d9.js → dto-renderer-manager-ee1a5864.js} +197 -22
- package/dist/esm/{element-appender-slimmer-7cb08343.js → element-appender-slimmer-35e26b5a.js} +2 -2
- package/dist/esm/{function-extension-63028f20.js → function-extension-e8c0e43e.js} +1 -1
- package/dist/esm/{image-annotation-action-53529cd9.js → image-annotation-action-285d432e.js} +1 -1
- package/dist/esm/index.js +6 -6
- package/dist/esm/{language-extension-c4ff21dd.js → language-extension-29bf8acd.js} +2 -2
- package/dist/esm/loader.js +4 -4
- package/dist/esm/{public-rules-6ebdfcf5.js → public-rules-724873f5.js} +2 -2
- package/dist/esm/{responsive-format-facade-05e53016.js → responsive-format-facade-28ee2dbd.js} +2 -2
- package/dist/esm/{rich-text-editor-required-rule-f63a37d1.js → rich-text-editor-required-rule-873e2e67.js} +1 -1
- package/dist/esm/{style-formatter-5f5bd714.js → style-formatter-314b37f3.js} +1 -1
- package/dist/esm/vega-accordion.entry.js +4 -4
- package/dist/esm/vega-app-header-button.entry.js +4 -4
- package/dist/esm/vega-box.entry.js +5 -5
- package/dist/esm/vega-button-circle.entry.js +4 -4
- package/dist/esm/vega-button.entry.js +3 -3
- package/dist/esm/vega-card.entry.js +4 -4
- package/dist/esm/vega-carousel.entry.js +3 -3
- package/dist/esm/vega-chip.entry.js +4 -4
- package/dist/esm/vega-date-picker_2.entry.js +4 -4
- package/dist/esm/vega-dialog_2.entry.js +4 -4
- package/dist/esm/vega-divider.entry.js +4 -4
- package/dist/esm/vega-dropdown_5.entry.js +4 -4
- package/dist/esm/vega-env-manager-8f8dc473.js +2 -2
- package/dist/esm/vega-flag-icon.entry.js +4 -4
- package/dist/esm/vega-flex.entry.js +5 -5
- package/dist/esm/vega-font.entry.js +4 -4
- package/dist/esm/vega-grid.entry.js +4 -4
- package/dist/esm/vega-icon.entry.js +4 -4
- package/dist/esm/vega-image-uploader.entry.js +3 -3
- package/dist/esm/vega-input-phone-number.entry.js +2 -2
- package/dist/esm/vega-input-select.entry.js +3 -3
- package/dist/esm/vega-left-nav_5.entry.js +4 -4
- package/dist/esm/vega-loader-wrapper_2.entry.js +3 -3
- package/dist/esm/vega-pagination.entry.js +3 -3
- package/dist/esm/vega-popover_2.entry.js +5 -5
- package/dist/esm/vega-progress-tracker_2.entry.js +3 -3
- package/dist/esm/vega-rich-text-content.entry.js +2 -2
- package/dist/esm/vega-rich-text-editor_4.entry.js +60 -10
- package/dist/esm/vega-selection-chip_2.entry.js +3 -3
- package/dist/esm/vega-sidenav_3.entry.js +3 -3
- package/dist/esm/vega-signature-capture.entry.js +4 -4
- package/dist/esm/vega-table_11.entry.js +3 -3
- package/dist/esm/vega-time-picker_2.entry.js +4 -4
- package/dist/esm/vega-tooltip_2.entry.js +4 -4
- package/dist/esm/vega.js +4 -4
- package/dist/sri/vega-sri-manifest.json +162 -162
- package/dist/types/components/vega-rich-text-editor/dto/blocks/html-block.d.ts +10 -0
- package/dist/types/components/vega-rich-text-editor/slimmers/controllers/html-cleaner/html-content-cleaner-processor.interface.d.ts +13 -0
- package/dist/types/components/vega-rich-text-editor/slimmers/controllers/html-cleaner/html-content-cleaner.d.ts +35 -0
- package/dist/types/components/vega-rich-text-editor/slimmers/controllers/html-cleaner/index.d.ts +4 -0
- package/dist/types/components/vega-rich-text-editor/slimmers/controllers/html-cleaner/processors/remove-whitespace-after-opening-tag-processor.d.ts +19 -0
- package/dist/types/components/vega-rich-text-editor/slimmers/controllers/html-cleaner/processors/remove-whitespace-before-closing-tag-processor.d.ts +19 -0
- package/dist/types/components/vega-rich-text-editor/slimmers/controllers/html-cleaner/processors/remove-whitespace-between-tags-processor.d.ts +59 -0
- package/dist/types/components/vega-rich-text-editor/test/html-cleaner/html-content-cleaner.test.d.ts +1 -0
- package/dist/types/components/vega-rich-text-editor/test/html-cleaner/remove-whitespace-after-opening-tag-processor.test.d.ts +1 -0
- package/dist/types/components/vega-rich-text-editor/test/html-cleaner/remove-whitespace-before-closing-tag-processor.test.d.ts +1 -0
- package/dist/types/components/vega-rich-text-editor/test/html-cleaner/remove-whitespace-between-tags-processor.test.d.ts +1 -0
- package/dist/types/helpers/code-format/code-formatter.d.ts +14 -0
- package/dist/vega/index.esm.js +1 -1
- package/dist/vega/{p-aa10eeb3.entry.js → p-012753b5.entry.js} +1 -1
- package/dist/vega/{p-fdc270b8.entry.js → p-024331ce.entry.js} +1 -1
- package/dist/vega/{p-ce1a2982.js → p-0575d22e.js} +1 -1
- package/dist/vega/{p-a86010f7.entry.js → p-08a90cc6.entry.js} +1 -1
- package/dist/vega/p-0ede1fea.entry.js +1 -0
- package/dist/vega/p-3353b3d7.js +1 -0
- package/dist/vega/p-365b1b35.entry.js +1 -0
- package/dist/vega/{p-a5bd3c15.entry.js → p-3ae9ee3d.entry.js} +1 -1
- package/dist/vega/{p-35bf5789.entry.js → p-3af92370.entry.js} +1 -1
- package/dist/vega/{p-ac4563d7.js → p-430be941.js} +1 -1
- package/dist/vega/{p-d6ed3d76.entry.js → p-45296fd4.entry.js} +1 -1
- package/dist/vega/{p-6f239144.entry.js → p-491618a6.entry.js} +1 -1
- package/dist/vega/{p-3cfd8616.entry.js → p-4aa893ef.entry.js} +1 -1
- package/dist/vega/{p-ef001c70.entry.js → p-4c47e8bb.entry.js} +1 -1
- package/dist/vega/{p-20ad42be.js → p-4da75f2b.js} +1 -1
- package/dist/vega/{p-e1610196.entry.js → p-5110ba73.entry.js} +1 -1
- package/dist/vega/{p-07d3df01.entry.js → p-556f9a21.entry.js} +1 -1
- package/dist/vega/{p-a65941d0.entry.js → p-55b54ba0.entry.js} +1 -1
- package/dist/vega/{p-50ab977c.entry.js → p-5efc6fe1.entry.js} +1 -1
- package/dist/vega/p-5f377954.js +1 -1
- package/dist/vega/{p-5df9a856.entry.js → p-64cd1931.entry.js} +1 -1
- package/dist/vega/{p-22c5d83b.js → p-68df4288.js} +1 -1
- package/dist/vega/{p-c8f1b9d5.js → p-691ba1ae.js} +1 -1
- package/dist/vega/{p-fba14ad2.entry.js → p-6fa8e231.entry.js} +1 -1
- package/dist/vega/{p-91ec2e30.js → p-749f96a4.js} +1 -1
- package/dist/vega/{p-67f18b9e.entry.js → p-7aaece55.entry.js} +1 -1
- package/dist/vega/{p-75651f32.entry.js → p-7ec6a104.entry.js} +1 -1
- package/dist/vega/{p-60274fb1.js → p-89d685a8.js} +1 -1
- package/dist/vega/{p-ffbff9b5.entry.js → p-8d701832.entry.js} +1 -1
- package/dist/vega/{p-e87b0b4a.entry.js → p-8ed0faa2.entry.js} +1 -1
- package/dist/vega/{p-87238c4c.entry.js → p-99aa554b.entry.js} +1 -1
- package/dist/vega/{p-ab2f35af.entry.js → p-9a2446f1.entry.js} +1 -1
- package/dist/vega/{p-3bbc0c7d.entry.js → p-a475d525.entry.js} +1 -1
- package/dist/vega/{p-a20854d6.entry.js → p-ac5e9955.entry.js} +1 -1
- package/dist/vega/p-b1f869fb.js +1 -0
- package/dist/vega/{p-f383c856.js → p-c2488ed6.js} +1 -1
- package/dist/vega/{p-747d6afc.entry.js → p-cf5b80f9.entry.js} +1 -1
- package/dist/vega/{p-8a887843.entry.js → p-db60a38e.entry.js} +1 -1
- package/dist/vega/{p-06aaced4.js → p-e93545fd.js} +1 -1
- package/dist/vega/{p-fbd64f9c.entry.js → p-ec192283.entry.js} +1 -1
- package/dist/vega/p-f2a3a1ec.entry.js +1 -0
- package/dist/vega/{p-08f18b22.entry.js → p-f2cdd212.entry.js} +1 -1
- package/dist/vega/{p-34246870.entry.js → p-f4c05b4a.entry.js} +1 -1
- package/dist/vega/{p-5a27084e.entry.js → p-f8641214.entry.js} +1 -1
- package/dist/vega/{p-60da99c6.entry.js → p-f89443aa.entry.js} +1 -1
- package/dist/vega/vega.esm.js +1 -1
- package/package.json +1 -1
- package/dist/vega/p-22b2841f.entry.js +0 -1
- package/dist/vega/p-53a694d1.entry.js +0 -1
- package/dist/vega/p-56e97df8.js +0 -1
- package/dist/vega/p-7ae55fd5.js +0 -1
- package/dist/vega/p-991008a8.entry.js +0 -1
|
@@ -39,6 +39,45 @@ describe('HTMLBlock', () => {
|
|
|
39
39
|
],
|
|
40
40
|
}, { autoMatchFormat: false }).toHtml()).toMatchSnapshot();
|
|
41
41
|
});
|
|
42
|
+
it('toHtml should handle self-closing hr tag correctly', () => {
|
|
43
|
+
const hrBlock = RTEHtmlBlock.from({
|
|
44
|
+
id: 'hr-1',
|
|
45
|
+
type: 'html-block',
|
|
46
|
+
htmlTag: 'hr',
|
|
47
|
+
children: [],
|
|
48
|
+
}, { autoMatchFormat: false });
|
|
49
|
+
expect(hrBlock.toHtml()).toBe('<hr>');
|
|
50
|
+
});
|
|
51
|
+
it('toHtml should handle self-closing hr tag with attributes correctly', () => {
|
|
52
|
+
const hrBlock = RTEHtmlBlock.from({
|
|
53
|
+
id: 'hr-1',
|
|
54
|
+
type: 'html-block',
|
|
55
|
+
htmlTag: 'hr',
|
|
56
|
+
annotations: { customClass: ['divider'] },
|
|
57
|
+
children: [],
|
|
58
|
+
}, { autoMatchFormat: false });
|
|
59
|
+
expect(hrBlock.toHtml()).toBe('<hr class="divider">');
|
|
60
|
+
});
|
|
61
|
+
it('toHtml should handle self-closing input tag correctly', () => {
|
|
62
|
+
const inputBlock = RTEHtmlBlock.from({
|
|
63
|
+
id: 'input-1',
|
|
64
|
+
type: 'html-block',
|
|
65
|
+
htmlTag: 'input',
|
|
66
|
+
annotations: { customAttribute: { type: 'text', placeholder: 'Enter text' } },
|
|
67
|
+
children: [],
|
|
68
|
+
}, { autoMatchFormat: false });
|
|
69
|
+
expect(inputBlock.toHtml()).toBe('<input type="text" placeholder="Enter text">');
|
|
70
|
+
});
|
|
71
|
+
it('toHtml should handle self-closing col tag correctly', () => {
|
|
72
|
+
const colBlock = RTEHtmlBlock.from({
|
|
73
|
+
id: 'col-1',
|
|
74
|
+
type: 'html-block',
|
|
75
|
+
htmlTag: 'col',
|
|
76
|
+
annotations: { customAttribute: { span: '2' } },
|
|
77
|
+
children: [],
|
|
78
|
+
}, { autoMatchFormat: false });
|
|
79
|
+
expect(colBlock.toHtml()).toBe('<col span="2">');
|
|
80
|
+
});
|
|
42
81
|
});
|
|
43
82
|
describe('public methods', () => {
|
|
44
83
|
let htmlBlock;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { VegaRTEContent } from '../../dto/content-state';
|
|
2
|
+
import { HTMLContentCleaner } from '../../slimmers/controllers/html-cleaner';
|
|
2
3
|
import { RTETextBlock } from '../../dto/blocks/text-block';
|
|
3
4
|
import { RTEImageBlock } from '../../dto/blocks/image-block';
|
|
4
5
|
import { RTEListBlock } from '../../dto/blocks/list-block';
|
|
@@ -2613,7 +2614,7 @@ describe('content state', () => {
|
|
|
2613
2614
|
</footer>
|
|
2614
2615
|
</article>
|
|
2615
2616
|
<address>ADDRESS: 123 Main St, City, Country</address>
|
|
2616
|
-
<blockquote cite="https://example.com">BLOCKQUOTE: “This is a quoted text
|
|
2617
|
+
<blockquote cite="https://example.com">BLOCKQUOTE: “This is a quoted text.”</blockquote>
|
|
2617
2618
|
<details>
|
|
2618
2619
|
<summary>DETAILS / SUMMARY Example</summary>
|
|
2619
2620
|
<p>Extra information shown when expanded.</p>
|
|
@@ -2955,4 +2956,168 @@ Line 1
|
|
|
2955
2956
|
});
|
|
2956
2957
|
});
|
|
2957
2958
|
});
|
|
2959
|
+
describe('cleanHtmlWhitespace', () => {
|
|
2960
|
+
const cleaner = new HTMLContentCleaner();
|
|
2961
|
+
const cleanHtmlWhitespace = (html) => {
|
|
2962
|
+
return cleaner.clean(html);
|
|
2963
|
+
};
|
|
2964
|
+
it('should inline a table by removing all whitespace between tags', () => {
|
|
2965
|
+
const input = `<table style="width: 400px; border-collapse: collapse;"> <thead> <tr> ` +
|
|
2966
|
+
`<th style="padding: 12px; text-align: left; font-weight: bold;">Header 1</th> ` +
|
|
2967
|
+
`<th style="padding: 12px; text-align: left; font-weight: bold;">Header 2</th> ` +
|
|
2968
|
+
`<th style="padding: 12px; text-align: left; font-weight: bold;">Header 3</th> ` +
|
|
2969
|
+
`</tr> </thead> <tbody> <tr> <td style="padding: 12px;">Cell 1-1</td> ` +
|
|
2970
|
+
`<td style="padding: 12px;">Cell 1-2</td> <td style="padding: 12px;">Cell 1-3</td> ` +
|
|
2971
|
+
`</tr> <tr> <td style="padding: 12px;">Cell 2-1</td> <td style="padding: 12px;">Cell 2-2</td> ` +
|
|
2972
|
+
`<td style="padding: 12px;">Cell 2-3</td> </tr> <tr> <td style="padding: 12px;">Cell 3-1</td> ` +
|
|
2973
|
+
`<td style="padding: 12px;">Cell 3-2</td> <td style="padding: 12px;">Cell 3-3</td> ` +
|
|
2974
|
+
`</tr> </tbody> </table>`;
|
|
2975
|
+
const expected = `<table style="width: 400px; border-collapse: collapse;"><thead><tr>` +
|
|
2976
|
+
`<th style="padding: 12px; text-align: left; font-weight: bold;">Header 1</th>` +
|
|
2977
|
+
`<th style="padding: 12px; text-align: left; font-weight: bold;">Header 2</th>` +
|
|
2978
|
+
`<th style="padding: 12px; text-align: left; font-weight: bold;">Header 3</th>` +
|
|
2979
|
+
`</tr></thead><tbody><tr>` +
|
|
2980
|
+
`<td style="padding: 12px;">Cell 1-1</td>` +
|
|
2981
|
+
`<td style="padding: 12px;">Cell 1-2</td>` +
|
|
2982
|
+
`<td style="padding: 12px;">Cell 1-3</td>` +
|
|
2983
|
+
`</tr><tr>` +
|
|
2984
|
+
`<td style="padding: 12px;">Cell 2-1</td>` +
|
|
2985
|
+
`<td style="padding: 12px;">Cell 2-2</td>` +
|
|
2986
|
+
`<td style="padding: 12px;">Cell 2-3</td>` +
|
|
2987
|
+
`</tr><tr>` +
|
|
2988
|
+
`<td style="padding: 12px;">Cell 3-1</td>` +
|
|
2989
|
+
`<td style="padding: 12px;">Cell 3-2</td>` +
|
|
2990
|
+
`<td style="padding: 12px;">Cell 3-3</td>` +
|
|
2991
|
+
`</tr></tbody></table>`;
|
|
2992
|
+
expect(cleanHtmlWhitespace(input)).toBe(expected);
|
|
2993
|
+
});
|
|
2994
|
+
it('should remove \\n\\t after opening tag and before closing tag', () => {
|
|
2995
|
+
const input = '<p>\n\t <span>test</span>\n\t </p>';
|
|
2996
|
+
expect(cleanHtmlWhitespace(input)).toBe('<p><span>test</span></p>');
|
|
2997
|
+
});
|
|
2998
|
+
it('should remove all \\n and \\t characters', () => {
|
|
2999
|
+
const input = '<p>\n\ttest\n</p>';
|
|
3000
|
+
expect(cleanHtmlWhitespace(input)).toBe('<p>test</p>');
|
|
3001
|
+
});
|
|
3002
|
+
it('should preserve space between matching tags', () => {
|
|
3003
|
+
const input = '<span> </span>';
|
|
3004
|
+
expect(cleanHtmlWhitespace(input)).toBe('<span> </span>');
|
|
3005
|
+
});
|
|
3006
|
+
it('should preserve space with text between matching tags', () => {
|
|
3007
|
+
const input = '<span> test </span>';
|
|
3008
|
+
expect(cleanHtmlWhitespace(input)).toBe('<span> test </span>');
|
|
3009
|
+
});
|
|
3010
|
+
it('should preserve multiple spaces between matching tags', () => {
|
|
3011
|
+
const input = '<span> </span>';
|
|
3012
|
+
expect(cleanHtmlWhitespace(input)).toBe('<span> </span>');
|
|
3013
|
+
});
|
|
3014
|
+
it('should remove whitespace between opening tags', () => {
|
|
3015
|
+
const input = '<div>\n\t<p></p></div>';
|
|
3016
|
+
expect(cleanHtmlWhitespace(input)).toBe('<div><p></p></div>');
|
|
3017
|
+
});
|
|
3018
|
+
it('should remove \\n\\t after opening tag but preserve regular spaces', () => {
|
|
3019
|
+
const input = '<p>\n\t Hello</p>';
|
|
3020
|
+
expect(cleanHtmlWhitespace(input)).toBe('<p> Hello</p>');
|
|
3021
|
+
});
|
|
3022
|
+
it('should remove whitespace between multiple closing tags', () => {
|
|
3023
|
+
const input = '<table><tbody><tr></tr> </tbody> </table>';
|
|
3024
|
+
expect(cleanHtmlWhitespace(input)).toBe('<table><tbody><tr></tr></tbody></table>');
|
|
3025
|
+
});
|
|
3026
|
+
it('should remove whitespace between multiple opening tags', () => {
|
|
3027
|
+
const input = '<div> <span></span></div>';
|
|
3028
|
+
expect(cleanHtmlWhitespace(input)).toBe('<div><span></span></div>');
|
|
3029
|
+
});
|
|
3030
|
+
it('should handle complex nested structure', () => {
|
|
3031
|
+
const input = '<div>\n\t<p>\n\t\t<span>Hello</span>\n\t</p>\n</div>';
|
|
3032
|
+
expect(cleanHtmlWhitespace(input)).toBe('<div><p><span>Hello</span></p></div>');
|
|
3033
|
+
});
|
|
3034
|
+
it('should preserve regular spaces within text content', () => {
|
|
3035
|
+
const input = '<p>Hello World</p>';
|
|
3036
|
+
expect(cleanHtmlWhitespace(input)).toBe('<p>Hello World</p>');
|
|
3037
|
+
});
|
|
3038
|
+
it('should preserve multiple spaces in text content', () => {
|
|
3039
|
+
const input = '<p>Hello World</p>';
|
|
3040
|
+
expect(cleanHtmlWhitespace(input)).toBe('<p>Hello World</p>');
|
|
3041
|
+
});
|
|
3042
|
+
it('should not remove \\n between words in text content', () => {
|
|
3043
|
+
const input = '<p>Hello\nWorld</p>';
|
|
3044
|
+
expect(cleanHtmlWhitespace(input)).toBe('<p>Hello\nWorld</p>');
|
|
3045
|
+
});
|
|
3046
|
+
it('should not remove \\n between words with space in text content', () => {
|
|
3047
|
+
const input = '<p>Hello\n World</p>';
|
|
3048
|
+
expect(cleanHtmlWhitespace(input)).toBe('<p>Hello\n World</p>');
|
|
3049
|
+
});
|
|
3050
|
+
it('should remove newline after img tag', () => {
|
|
3051
|
+
const input = '<div><img id="image-id" class="img-class" style="vertical-align: bottom; width: 25%; padding: 20px" alt="test alt" src="data:image/svg+xml;base64,xxx=" data-size="sm">\n</div>';
|
|
3052
|
+
const expected = '<div><img id="image-id" class="img-class" style="vertical-align: bottom; width: 25%; padding: 20px" alt="test alt" src="data:image/svg+xml;base64,xxx=" data-size="sm"></div>';
|
|
3053
|
+
expect(cleanHtmlWhitespace(input)).toBe(expected);
|
|
3054
|
+
});
|
|
3055
|
+
it('should handle 4-level deep nesting', () => {
|
|
3056
|
+
const input = '<div>\n\t<section>\n\t\t<article>\n\t\t\t<p>Text</p>\n\t\t</article>\n\t</section>\n</div>';
|
|
3057
|
+
expect(cleanHtmlWhitespace(input)).toBe('<div><section><article><p>Text</p></article></section></div>');
|
|
3058
|
+
});
|
|
3059
|
+
it('should handle 5-level deep nesting', () => {
|
|
3060
|
+
const input = '<div>\n\t<section>\n\t\t<article>\n\t\t\t<main>\n\t\t\t\t<p>Text</p>\n\t\t\t</main>\n\t\t</article>\n\t</section>\n</div>';
|
|
3061
|
+
expect(cleanHtmlWhitespace(input)).toBe('<div><section><article><main><p>Text</p></main></article></section></div>');
|
|
3062
|
+
});
|
|
3063
|
+
it('should handle 6-level deep nesting', () => {
|
|
3064
|
+
const input = '<div>\n\t<section>\n\t\t<article>\n\t\t\t<main>\n\t\t\t\t<aside>\n\t\t\t\t\t<p>Text</p>\n\t\t\t\t</aside>\n\t\t\t</main>\n\t\t</article>\n\t</section>\n</div>';
|
|
3065
|
+
expect(cleanHtmlWhitespace(input)).toBe('<div><section><article><main><aside><p>Text</p></aside></main></article></section></div>');
|
|
3066
|
+
});
|
|
3067
|
+
it('should remove whitespace after br and hr tags', () => {
|
|
3068
|
+
const input1 = '<div><br>\n</div>';
|
|
3069
|
+
expect(cleanHtmlWhitespace(input1)).toBe('<div><br></div>');
|
|
3070
|
+
const input2 = '<div><hr>\n\t</div>';
|
|
3071
|
+
expect(cleanHtmlWhitespace(input2)).toBe('<div><hr></div>');
|
|
3072
|
+
const input3 = '<div><input type="text">\n</div>';
|
|
3073
|
+
expect(cleanHtmlWhitespace(input3)).toBe('<div><input type="text"></div>');
|
|
3074
|
+
});
|
|
3075
|
+
it('should remove multiple consecutive \\n characters', () => {
|
|
3076
|
+
const input = '<p>\n\n\n\nHello</p>';
|
|
3077
|
+
expect(cleanHtmlWhitespace(input)).toBe('<p>Hello</p>');
|
|
3078
|
+
});
|
|
3079
|
+
it('should remove multiple consecutive \\t characters', () => {
|
|
3080
|
+
const input = '<p>\t\t\tHello</p>';
|
|
3081
|
+
expect(cleanHtmlWhitespace(input)).toBe('<p>Hello</p>');
|
|
3082
|
+
});
|
|
3083
|
+
it('should preserve empty matching tags', () => {
|
|
3084
|
+
const input = '<span></span>';
|
|
3085
|
+
expect(cleanHtmlWhitespace(input)).toBe('<span></span>');
|
|
3086
|
+
});
|
|
3087
|
+
it('should handle mixed spaces, tabs, and newlines', () => {
|
|
3088
|
+
const input = '<div> \n\t <p>Text</p> \n\t </div>';
|
|
3089
|
+
expect(cleanHtmlWhitespace(input)).toBe('<div><p>Text</p></div>');
|
|
3090
|
+
});
|
|
3091
|
+
it('should preserve spaces in attribute values', () => {
|
|
3092
|
+
const input = '<p class="my custom class">Text</p>';
|
|
3093
|
+
expect(cleanHtmlWhitespace(input)).toBe('<p class="my custom class">Text</p>');
|
|
3094
|
+
});
|
|
3095
|
+
it('should remove newlines after br tags', () => {
|
|
3096
|
+
const input = '<p>Line 1<br>\nLine 2</p>';
|
|
3097
|
+
expect(cleanHtmlWhitespace(input)).toBe('<p>Line 1<br>Line 2</p>');
|
|
3098
|
+
});
|
|
3099
|
+
it('should preserve spaces in code blocks between matching tags', () => {
|
|
3100
|
+
const input = '<code> const x = 1; </code>';
|
|
3101
|
+
expect(cleanHtmlWhitespace(input)).toBe('<code> const x = 1; </code>');
|
|
3102
|
+
});
|
|
3103
|
+
it('should handle empty string', () => {
|
|
3104
|
+
expect(cleanHtmlWhitespace('')).toBe('');
|
|
3105
|
+
});
|
|
3106
|
+
it('should preserve plain text without tags', () => {
|
|
3107
|
+
const input = 'Hello World';
|
|
3108
|
+
expect(cleanHtmlWhitespace(input)).toBe('Hello World');
|
|
3109
|
+
});
|
|
3110
|
+
it('should remove whitespace between different inline elements', () => {
|
|
3111
|
+
const input = '<span>text</span>\n\t<strong>bold</strong>';
|
|
3112
|
+
expect(cleanHtmlWhitespace(input)).toBe('<span>text</span><strong>bold</strong>');
|
|
3113
|
+
});
|
|
3114
|
+
it('should remove \\t\\n combination', () => {
|
|
3115
|
+
const input = '<p>\t\nHello</p>';
|
|
3116
|
+
expect(cleanHtmlWhitespace(input)).toBe('<p>Hello</p>');
|
|
3117
|
+
});
|
|
3118
|
+
it('should remove complex \\n\\t\\n patterns', () => {
|
|
3119
|
+
const input = '<p>\n\t\n\t\nHello</p>';
|
|
3120
|
+
expect(cleanHtmlWhitespace(input)).toBe('<p>Hello</p>');
|
|
3121
|
+
});
|
|
3122
|
+
});
|
|
2958
3123
|
});
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { HTMLContentCleaner } from '../../slimmers/controllers/html-cleaner';
|
|
2
|
+
describe('HTMLContentCleaner', () => {
|
|
3
|
+
let cleaner;
|
|
4
|
+
beforeEach(() => {
|
|
5
|
+
cleaner = new HTMLContentCleaner();
|
|
6
|
+
});
|
|
7
|
+
it('should handle empty string', () => {
|
|
8
|
+
expect(cleaner.clean('')).toBe('');
|
|
9
|
+
});
|
|
10
|
+
it('should handle whitespace-only string', () => {
|
|
11
|
+
expect(cleaner.clean(' ')).toBe(' ');
|
|
12
|
+
});
|
|
13
|
+
it('should remove \\n\\t after opening tag and before closing tag', () => {
|
|
14
|
+
const input = '<div><p>\n\t <span>test</span>\n\t </p></div>';
|
|
15
|
+
expect(cleaner.clean(input)).toBe('<div><p><span>test</span></p></div>');
|
|
16
|
+
});
|
|
17
|
+
it('should remove all \\n and \\t characters between tags', () => {
|
|
18
|
+
const input = '<div><p>\n\ttest\n</p></div>';
|
|
19
|
+
expect(cleaner.clean(input)).toBe('<div><p>test</p></div>');
|
|
20
|
+
});
|
|
21
|
+
it('should preserve space between matching tags', () => {
|
|
22
|
+
const input = '<div><span> </span></div>';
|
|
23
|
+
expect(cleaner.clean(input)).toBe('<div><span> </span></div>');
|
|
24
|
+
});
|
|
25
|
+
it('should preserve space with text between matching tags', () => {
|
|
26
|
+
const input = '<div><span> test </span></div>';
|
|
27
|
+
expect(cleaner.clean(input)).toBe('<div><span> test </span></div>');
|
|
28
|
+
});
|
|
29
|
+
it('should preserve multiple spaces between matching tags', () => {
|
|
30
|
+
const input = '<div><span> </span></div>';
|
|
31
|
+
expect(cleaner.clean(input)).toBe('<div><span> </span></div>');
|
|
32
|
+
});
|
|
33
|
+
it('should remove whitespace between opening tags', () => {
|
|
34
|
+
const input = '<div>\n\t<p></p></div>';
|
|
35
|
+
expect(cleaner.clean(input)).toBe('<div><p></p></div>');
|
|
36
|
+
});
|
|
37
|
+
it('should remove \\n\\t after opening tag but preserve regular spaces', () => {
|
|
38
|
+
const input = '<p>\n\t Hello</p>';
|
|
39
|
+
expect(cleaner.clean(input)).toBe('<p> Hello</p>');
|
|
40
|
+
});
|
|
41
|
+
it('should remove whitespace between multiple closing tags', () => {
|
|
42
|
+
const input = '<table><tbody><tr></tr> </tbody> </table>';
|
|
43
|
+
expect(cleaner.clean(input)).toBe('<table><tbody><tr></tr></tbody></table>');
|
|
44
|
+
});
|
|
45
|
+
it('should remove whitespace between multiple opening tags', () => {
|
|
46
|
+
const input = '<div> <span></span></div>';
|
|
47
|
+
expect(cleaner.clean(input)).toBe('<div><span></span></div>');
|
|
48
|
+
});
|
|
49
|
+
it('should handle complex nested structure', () => {
|
|
50
|
+
const input = '<div>\n\t<p>\n\t\t<span>Hello</span>\n\t</p>\n</div>';
|
|
51
|
+
expect(cleaner.clean(input)).toBe('<div><p><span>Hello</span></p></div>');
|
|
52
|
+
});
|
|
53
|
+
it('should preserve regular spaces within text content', () => {
|
|
54
|
+
const input = '<div><p>Hello World</p></div>';
|
|
55
|
+
expect(cleaner.clean(input)).toBe('<div><p>Hello World</p></div>');
|
|
56
|
+
});
|
|
57
|
+
it('should preserve multiple spaces in text content', () => {
|
|
58
|
+
const input = '<div><p>Hello World</p></div>';
|
|
59
|
+
expect(cleaner.clean(input)).toBe('<div><p>Hello World</p></div>');
|
|
60
|
+
});
|
|
61
|
+
it('should not remove \\n between words in text content', () => {
|
|
62
|
+
const input = '<div><p>Hello\nWorld</p></div>';
|
|
63
|
+
expect(cleaner.clean(input)).toBe('<div><p>Hello\nWorld</p></div>');
|
|
64
|
+
});
|
|
65
|
+
it('should not remove \\n between words with space in text content', () => {
|
|
66
|
+
const input = '<div><p>Hello\n World</p></div>';
|
|
67
|
+
expect(cleaner.clean(input)).toBe('<div><p>Hello\n World</p></div>');
|
|
68
|
+
});
|
|
69
|
+
it('should remove multiple consecutive \\n characters', () => {
|
|
70
|
+
const input = '<div><p>\n\n\n\nHello</p></div>';
|
|
71
|
+
expect(cleaner.clean(input)).toBe('<div><p>Hello</p></div>');
|
|
72
|
+
});
|
|
73
|
+
it('should remove multiple consecutive \\t characters', () => {
|
|
74
|
+
const input = '<div><p>\t\t\tHello</p></div>';
|
|
75
|
+
expect(cleaner.clean(input)).toBe('<div><p>Hello</p></div>');
|
|
76
|
+
});
|
|
77
|
+
it('should preserve empty matching tags', () => {
|
|
78
|
+
const input = '<div><span></span></div>';
|
|
79
|
+
expect(cleaner.clean(input)).toBe('<div><span></span></div>');
|
|
80
|
+
});
|
|
81
|
+
it('should handle mixed spaces, tabs, and newlines', () => {
|
|
82
|
+
const input = '<div> \n\t <p>Text</p> \n\t </div>';
|
|
83
|
+
expect(cleaner.clean(input)).toBe('<div><p>Text</p></div>');
|
|
84
|
+
});
|
|
85
|
+
it('should remove \\t\\n combination', () => {
|
|
86
|
+
const input = '<div><p>\t\nHello</p></div>';
|
|
87
|
+
expect(cleaner.clean(input)).toBe('<div><p>Hello</p></div>');
|
|
88
|
+
});
|
|
89
|
+
it('should remove complex \\n\\t\\n patterns', () => {
|
|
90
|
+
const input = '<div><p>\n\t\n\t\nHello</p></div>';
|
|
91
|
+
expect(cleaner.clean(input)).toBe('<div><p>Hello</p></div>');
|
|
92
|
+
});
|
|
93
|
+
it('should handle 4-level deep nesting', () => {
|
|
94
|
+
const input = '<div>\n\t<section>\n\t\t<article>\n\t\t\t<p>Text</p>\n\t\t</article>\n\t</section>\n</div>';
|
|
95
|
+
expect(cleaner.clean(input)).toBe('<div><section><article><p>Text</p></article></section></div>');
|
|
96
|
+
});
|
|
97
|
+
it('should handle 5-level deep nesting', () => {
|
|
98
|
+
const input = '<div>\n\t<section>\n\t\t<article>\n\t\t\t<main>\n\t\t\t\t<p>Text</p>\n\t\t\t</main>\n\t\t</article>\n\t</section>\n</div>';
|
|
99
|
+
expect(cleaner.clean(input)).toBe('<div><section><article><main><p>Text</p></main></article></section></div>');
|
|
100
|
+
});
|
|
101
|
+
it('should handle 6-level deep nesting', () => {
|
|
102
|
+
const input = '<div>\n\t<section>\n\t\t<article>\n\t\t\t<main>\n\t\t\t\t<aside>\n\t\t\t\t\t<p>Text</p>\n\t\t\t\t</aside>\n\t\t\t</main>\n\t\t</article>\n\t</section>\n</div>';
|
|
103
|
+
expect(cleaner.clean(input)).toBe('<div><section><article><main><aside><p>Text</p></aside></main></article></section></div>');
|
|
104
|
+
});
|
|
105
|
+
it('should remove whitespace after br and hr tags', () => {
|
|
106
|
+
const input1 = '<div><br>\n</div>';
|
|
107
|
+
expect(cleaner.clean(input1)).toBe('<div><br></div>');
|
|
108
|
+
const input2 = '<div><hr>\n\t</div>';
|
|
109
|
+
expect(cleaner.clean(input2)).toBe('<div><hr></div>');
|
|
110
|
+
const input3 = '<div><input type="text">\n</div>';
|
|
111
|
+
expect(cleaner.clean(input3)).toBe('<div><input type="text"></div>');
|
|
112
|
+
});
|
|
113
|
+
it('should remove newlines after br tags', () => {
|
|
114
|
+
const input = '<p>Line 1<br>\nLine 2</p>';
|
|
115
|
+
expect(cleaner.clean(input)).toBe('<p>Line 1<br>Line 2</p>');
|
|
116
|
+
});
|
|
117
|
+
it('should preserve spaces in code blocks between matching tags', () => {
|
|
118
|
+
const input = '<code> const x = 1; </code>';
|
|
119
|
+
expect(cleaner.clean(input)).toBe('<code> const x = 1; </code>');
|
|
120
|
+
});
|
|
121
|
+
it('should preserve plain text without tags', () => {
|
|
122
|
+
const input = 'Hello World';
|
|
123
|
+
expect(cleaner.clean(input)).toBe('Hello World');
|
|
124
|
+
});
|
|
125
|
+
it('should remove whitespace between different inline elements', () => {
|
|
126
|
+
const input = '<span>text</span>\n\t<strong>bold</strong>';
|
|
127
|
+
expect(cleaner.clean(input)).toBe('<span>text</span><strong>bold</strong>');
|
|
128
|
+
});
|
|
129
|
+
it('should inline a table by removing all whitespace between tags', () => {
|
|
130
|
+
const input = `<table style="width: 400px; border-collapse: collapse;"> <thead> <tr> ` +
|
|
131
|
+
`<th style="padding: 12px; text-align: left; font-weight: bold;">Header 1</th> ` +
|
|
132
|
+
`<th style="padding: 12px; text-align: left; font-weight: bold;">Header 2</th> ` +
|
|
133
|
+
`<th style="padding: 12px; text-align: left; font-weight: bold;">Header 3</th> ` +
|
|
134
|
+
`</tr> </thead> <tbody> <tr> <td style="padding: 12px;">Cell 1-1</td> ` +
|
|
135
|
+
`<td style="padding: 12px;">Cell 1-2</td> <td style="padding: 12px;">Cell 1-3</td> ` +
|
|
136
|
+
`</tr> <tr> <td style="padding: 12px;">Cell 2-1</td> <td style="padding: 12px;">Cell 2-2</td> ` +
|
|
137
|
+
`<td style="padding: 12px;">Cell 2-3</td> </tr> <tr> <td style="padding: 12px;">Cell 3-1</td> ` +
|
|
138
|
+
`<td style="padding: 12px;">Cell 3-2</td> <td style="padding: 12px;">Cell 3-3</td> ` +
|
|
139
|
+
`</tr> </tbody> </table>`;
|
|
140
|
+
const expected = `<table style="width: 400px; border-collapse: collapse;"><thead><tr>` +
|
|
141
|
+
`<th style="padding: 12px; text-align: left; font-weight: bold;">Header 1</th>` +
|
|
142
|
+
`<th style="padding: 12px; text-align: left; font-weight: bold;">Header 2</th>` +
|
|
143
|
+
`<th style="padding: 12px; text-align: left; font-weight: bold;">Header 3</th>` +
|
|
144
|
+
`</tr></thead><tbody><tr>` +
|
|
145
|
+
`<td style="padding: 12px;">Cell 1-1</td>` +
|
|
146
|
+
`<td style="padding: 12px;">Cell 1-2</td>` +
|
|
147
|
+
`<td style="padding: 12px;">Cell 1-3</td>` +
|
|
148
|
+
`</tr><tr>` +
|
|
149
|
+
`<td style="padding: 12px;">Cell 2-1</td>` +
|
|
150
|
+
`<td style="padding: 12px;">Cell 2-2</td>` +
|
|
151
|
+
`<td style="padding: 12px;">Cell 2-3</td>` +
|
|
152
|
+
`</tr><tr>` +
|
|
153
|
+
`<td style="padding: 12px;">Cell 3-1</td>` +
|
|
154
|
+
`<td style="padding: 12px;">Cell 3-2</td>` +
|
|
155
|
+
`<td style="padding: 12px;">Cell 3-3</td>` +
|
|
156
|
+
`</tr></tbody></table>`;
|
|
157
|
+
expect(cleaner.clean(input)).toBe(expected);
|
|
158
|
+
});
|
|
159
|
+
it('should remove newline after img tag', () => {
|
|
160
|
+
const input = '<div><img id="image-id" class="img-class" style="vertical-align: bottom; width: 25%; padding: 20px" alt="test alt" src="data:image/svg+xml;base64,xxx=" data-size="sm">\n</div>';
|
|
161
|
+
const expected = '<div><img id="image-id" class="img-class" style="vertical-align: bottom; width: 25%; padding: 20px" alt="test alt" src="data:image/svg+xml;base64,xxx=" data-size="sm"></div>';
|
|
162
|
+
expect(cleaner.clean(input)).toBe(expected);
|
|
163
|
+
});
|
|
164
|
+
it('should preserve spaces in attribute values', () => {
|
|
165
|
+
const input = '<p class="my custom class">Text</p>';
|
|
166
|
+
expect(cleaner.clean(input)).toBe('<p class="my custom class">Text</p>');
|
|
167
|
+
});
|
|
168
|
+
describe('Edge cases', () => {
|
|
169
|
+
it('should handle self-closing tags', () => {
|
|
170
|
+
expect(cleaner.clean('<br/>\n<hr/>')).toBe('<br/><hr/>');
|
|
171
|
+
expect(cleaner.clean('<img src="test.jpg"/>\n<input/>')).toBe('<img src="test.jpg"/><input/>');
|
|
172
|
+
});
|
|
173
|
+
it('should handle nested matching tags', () => {
|
|
174
|
+
expect(cleaner.clean('<div> <div> </div> </div>')).toBe('<div><div> </div></div>');
|
|
175
|
+
});
|
|
176
|
+
it('should handle tags with special characters in attributes', () => {
|
|
177
|
+
expect(cleaner.clean('<a href="http://example.com?a=1&b=2">\nLink</a>')).toBe('<a href="http://example.com?a=1&b=2">Link</a>');
|
|
178
|
+
});
|
|
179
|
+
it('should handle HTML entities', () => {
|
|
180
|
+
expect(cleaner.clean('<div><p>\n <></p></div>')).toBe('<div><p> <></p></div>');
|
|
181
|
+
});
|
|
182
|
+
it('should handle comments', () => {
|
|
183
|
+
expect(cleaner.clean('<div>\n<!-- comment -->\n<p>Text</p></div>')).toBe('<div><!-- comment --><p>Text</p></div>');
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { RemoveWhitespaceAfterOpeningTagProcessor } from '../../slimmers/controllers/html-cleaner';
|
|
2
|
+
describe('RemoveWhitespaceAfterOpeningTagProcessor', () => {
|
|
3
|
+
let processor;
|
|
4
|
+
beforeEach(() => {
|
|
5
|
+
processor = new RemoveWhitespaceAfterOpeningTagProcessor();
|
|
6
|
+
});
|
|
7
|
+
it('should remove \\n and \\t after opening tag', () => {
|
|
8
|
+
expect(processor.process('<div><p>\n\tHello</p></div>')).toBe('<div><p>Hello</p></div>');
|
|
9
|
+
expect(processor.process('<div><div>\n\n\tWorld</div></div>')).toBe('<div><div>World</div></div>');
|
|
10
|
+
expect(processor.process('<div><span>\tTest</span></div>')).toBe('<div><span>Test</span></div>');
|
|
11
|
+
});
|
|
12
|
+
it('should preserve regular spaces', () => {
|
|
13
|
+
expect(processor.process('<div><p> Hello</p></div>')).toBe('<div><p> Hello</p></div>');
|
|
14
|
+
expect(processor.process('<div><p> World</p></div>')).toBe('<div><p> World</p></div>');
|
|
15
|
+
});
|
|
16
|
+
it('should not affect text without opening tags', () => {
|
|
17
|
+
expect(processor.process('<div>Hello World</div>')).toBe('<div>Hello World</div>');
|
|
18
|
+
});
|
|
19
|
+
it('should handle multiple occurrences', () => {
|
|
20
|
+
expect(processor.process('<div><p>\nHello</p><div>\tWorld</div></div>')).toBe('<div><p>Hello</p><div>World</div></div>');
|
|
21
|
+
});
|
|
22
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { RemoveWhitespaceBeforeClosingTagProcessor } from '../../slimmers/controllers/html-cleaner';
|
|
2
|
+
describe('RemoveWhitespaceBeforeClosingTagProcessor', () => {
|
|
3
|
+
let processor;
|
|
4
|
+
beforeEach(() => {
|
|
5
|
+
processor = new RemoveWhitespaceBeforeClosingTagProcessor();
|
|
6
|
+
});
|
|
7
|
+
it('should remove \\n and \\t before closing tag', () => {
|
|
8
|
+
expect(processor.process('<div><p>World\n\t</p></div>')).toBe('<div><p>World</p></div>');
|
|
9
|
+
expect(processor.process('<div><div>Hello\n</div></div>')).toBe('<div><div>Hello</div></div>');
|
|
10
|
+
expect(processor.process('<div><span>Test\t</span></div>')).toBe('<div><span>Test</span></div>');
|
|
11
|
+
});
|
|
12
|
+
it('should preserve regular spaces', () => {
|
|
13
|
+
expect(processor.process('<div><p>Hello </p></div>')).toBe('<div><p>Hello </p></div>');
|
|
14
|
+
expect(processor.process('<div><p>World </p></div>')).toBe('<div><p>World </p></div>');
|
|
15
|
+
});
|
|
16
|
+
it('should not affect text without closing tags', () => {
|
|
17
|
+
expect(processor.process('<div>Hello World</div>')).toBe('<div>Hello World</div>');
|
|
18
|
+
});
|
|
19
|
+
it('should handle multiple occurrences', () => {
|
|
20
|
+
expect(processor.process('<div><p>Hello\n</p><div>World\t</div></div>')).toBe('<div><p>Hello</p><div>World</div></div>');
|
|
21
|
+
});
|
|
22
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { RemoveWhitespaceBetweenTagsProcessor } from '../../slimmers/controllers/html-cleaner';
|
|
2
|
+
describe('RemoveWhitespaceBetweenTagsProcessor', () => {
|
|
3
|
+
let processor;
|
|
4
|
+
beforeEach(() => {
|
|
5
|
+
processor = new RemoveWhitespaceBetweenTagsProcessor();
|
|
6
|
+
});
|
|
7
|
+
it('should remove whitespace between different tags', () => {
|
|
8
|
+
expect(processor.process('<div><p></p>\n\t<div></div></div>')).toBe('<div><p></p><div></div></div>');
|
|
9
|
+
expect(processor.process('<div><div>\n\t<span></span></div></div>')).toBe('<div><div><span></span></div></div>');
|
|
10
|
+
expect(processor.process('<div><div></div> <p></p></div>')).toBe('<div><div></div><p></p></div>');
|
|
11
|
+
});
|
|
12
|
+
it('should preserve whitespace between matching tags', () => {
|
|
13
|
+
expect(processor.process('<div><span> </span></div>')).toBe('<div><span> </span></div>');
|
|
14
|
+
expect(processor.process('<div><span> </span></div>')).toBe('<div><span> </span></div>');
|
|
15
|
+
expect(processor.process('<div><span>\n\t</span></div>')).toBe('<div><span>\n\t</span></div>');
|
|
16
|
+
expect(processor.process('<div><code> </code></div>')).toBe('<div><code> </code></div>');
|
|
17
|
+
});
|
|
18
|
+
it('should handle custom elements with hyphens', () => {
|
|
19
|
+
expect(processor.process('<div><my-component> </my-component></div>')).toBe('<div><my-component> </my-component></div>');
|
|
20
|
+
expect(processor.process('<div><my-component></my-component>\n<other-component></other-component></div>')).toBe('<div><my-component></my-component><other-component></other-component></div>');
|
|
21
|
+
});
|
|
22
|
+
it('should handle tags with attributes', () => {
|
|
23
|
+
expect(processor.process('<div><div class="test">\n<span></span></div></div>')).toBe('<div><div class="test"><span></span></div></div>');
|
|
24
|
+
expect(processor.process('<div><span id="test"> </span></div>')).toBe('<div><span id="test"> </span></div>');
|
|
25
|
+
});
|
|
26
|
+
});
|
|
@@ -5,6 +5,7 @@ class CodeFormatter {
|
|
|
5
5
|
constructor() {
|
|
6
6
|
/**
|
|
7
7
|
* Formats a given HTML source string by applying indentation and cleaning up styles.
|
|
8
|
+
* Content inside preserve tags (pre) is preserved as-is without formatting.
|
|
8
9
|
*
|
|
9
10
|
* @param {string} source - The HTML source string to format.
|
|
10
11
|
* @param {CodeFormatOptions} options - Formatting options.
|
|
@@ -19,9 +20,25 @@ class CodeFormatter {
|
|
|
19
20
|
const tokens = source.split(tagRegex).filter((t) => t.length > 0);
|
|
20
21
|
let indentLevel = 0;
|
|
21
22
|
let output = '';
|
|
23
|
+
let preserveDepth = 0;
|
|
22
24
|
for (let i = 0; i < tokens.length; i++) {
|
|
23
25
|
let token = tokens[i];
|
|
24
|
-
|
|
26
|
+
const tagName = CodeFormatter.getTagName(token);
|
|
27
|
+
// Track if we're entering or exiting preserve tags
|
|
28
|
+
if (CodeFormatter.PRESERVE_CONTENT_TAGS.has(tagName)) {
|
|
29
|
+
if (token.startsWith('</')) {
|
|
30
|
+
preserveDepth = Math.max(0, preserveDepth - 1);
|
|
31
|
+
}
|
|
32
|
+
else if (token.startsWith('<')) {
|
|
33
|
+
preserveDepth++;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
// If inside preserve tag, output as-is without formatting
|
|
37
|
+
if (preserveDepth > 0) {
|
|
38
|
+
output += token;
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
// Decrease indentation for closing tags (after exiting preserve tags)
|
|
25
42
|
if (token.startsWith('</')) {
|
|
26
43
|
indentLevel = Math.max(0, indentLevel - 1);
|
|
27
44
|
}
|
|
@@ -31,15 +48,20 @@ class CodeFormatter {
|
|
|
31
48
|
token = token.replace(/style\s*=\s*"([^"]*)"/g, (_, styleContent) => {
|
|
32
49
|
return `style="${this.formatStyle(styleContent)}"`;
|
|
33
50
|
});
|
|
34
|
-
const tagName = CodeFormatter.getTagName(token);
|
|
35
51
|
const isInline = CodeFormatter.INLINE_TAGS.has(tagName);
|
|
36
|
-
// Inline tag grouping: e.g., <strong>Text</strong> stays on one line
|
|
37
|
-
|
|
38
|
-
|
|
52
|
+
// Inline tag grouping: e.g., <strong>Text</strong> or <span></span> stays on one line
|
|
53
|
+
// Check for empty tags: <tag></tag> (closing tag immediately follows opening tag)
|
|
54
|
+
if (isInline && i + 1 < tokens.length && tokens[i + 1] === `</${tagName}>`) {
|
|
55
|
+
// Empty inline tag: <span></span>
|
|
56
|
+
output += `${indent}${token}${tokens[i + 1]}\n`;
|
|
57
|
+
i += 1;
|
|
58
|
+
}
|
|
59
|
+
else if (isInline && i + 2 < tokens.length && tokens[i + 2] === `</${tagName}>`) {
|
|
60
|
+
// Group consecutive inline tags with content
|
|
39
61
|
let inlineGroup = `${token}${tokens[i + 1]}${tokens[i + 2]}`;
|
|
40
62
|
i += 2;
|
|
41
63
|
// Keep merging consecutive inline tag pairs
|
|
42
|
-
while (i +
|
|
64
|
+
while (i + 3 < tokens.length &&
|
|
43
65
|
tokens[i + 1].startsWith('<') &&
|
|
44
66
|
CodeFormatter.INLINE_TAGS.has(CodeFormatter.getTagName(tokens[i + 1])) &&
|
|
45
67
|
tokens[i + 3] === `</${CodeFormatter.getTagName(tokens[i + 1])}>`) {
|
|
@@ -53,7 +75,7 @@ class CodeFormatter {
|
|
|
53
75
|
// Increase indentation for non-void opening tags
|
|
54
76
|
if (!token.startsWith('</') &&
|
|
55
77
|
!token.endsWith('/>') &&
|
|
56
|
-
|
|
78
|
+
!CodeFormatter.VOID_TAGS.has(tagName)) {
|
|
57
79
|
indentLevel++;
|
|
58
80
|
}
|
|
59
81
|
}
|
|
@@ -94,6 +116,10 @@ class CodeFormatter {
|
|
|
94
116
|
.join('; ');
|
|
95
117
|
}
|
|
96
118
|
}
|
|
119
|
+
/**
|
|
120
|
+
* Inline/phrasing elements that should be kept on a single line with their content.
|
|
121
|
+
* These tags are formatted as: <tag>content</tag> on one line.
|
|
122
|
+
*/
|
|
97
123
|
CodeFormatter.INLINE_TAGS = new Set([
|
|
98
124
|
'span',
|
|
99
125
|
'strong',
|
|
@@ -113,4 +139,28 @@ CodeFormatter.INLINE_TAGS = new Set([
|
|
|
113
139
|
'del',
|
|
114
140
|
'ins',
|
|
115
141
|
]);
|
|
142
|
+
/**
|
|
143
|
+
* Void/self-closing elements that don't have closing tags.
|
|
144
|
+
* Used to avoid increasing indentation for these elements.
|
|
145
|
+
*/
|
|
146
|
+
CodeFormatter.VOID_TAGS = new Set([
|
|
147
|
+
'area',
|
|
148
|
+
'base',
|
|
149
|
+
'br',
|
|
150
|
+
'col',
|
|
151
|
+
'embed',
|
|
152
|
+
'hr',
|
|
153
|
+
'img',
|
|
154
|
+
'input',
|
|
155
|
+
'link',
|
|
156
|
+
'meta',
|
|
157
|
+
'param',
|
|
158
|
+
'source',
|
|
159
|
+
'track',
|
|
160
|
+
'wbr',
|
|
161
|
+
]);
|
|
162
|
+
/**
|
|
163
|
+
* Tags whose content should be preserved as-is without formatting.
|
|
164
|
+
*/
|
|
165
|
+
CodeFormatter.PRESERVE_CONTENT_TAGS = new Set(['pre']);
|
|
116
166
|
export default new CodeFormatter();
|