Skip to content

Commit

Permalink
feat: introduce trim config option
Browse files Browse the repository at this point in the history
  • Loading branch information
j4w8n committed Nov 29, 2024
1 parent 049d10e commit 76b53db
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 3 deletions.
3 changes: 2 additions & 1 deletion src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
export const CONFIG = {
ignore: {},
strict: false,
tab_size: 2
tab_size: 2,
trim: {}
}
12 changes: 11 additions & 1 deletion src/prettify.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
import { closify } from './closify.js'
import { minify } from './minify.js'
import { ignoreElement, isHtml, validateConfig } from './utils.js'
import { ignoreElement, isHtml, trimify, validateConfig } from './utils.js'
import { CONFIG } from './constants.js'

/**
* @type {boolean}
*/
let strict

/**
* @type {Record<string, string>}
*/
let trim

/**
* @type {{ line: string[] }}
*/
Expand Down Expand Up @@ -47,6 +52,10 @@ const enqueue = (html) => {
*/
const preprocess = (html) => {
html = closify(html, false)

if (trim)
trimify(html, trim)

html = minify(html, false)
html = enqueue(html)

Expand Down Expand Up @@ -148,6 +157,7 @@ export const prettify = (html, config) => {
strict = validated_config.strict

const ignore = Object.keys(validated_config.ignore).length > 0
trim = validated_config.trim

/* Protect ignored elements. */
if (ignore) {
Expand Down
1 change: 1 addition & 0 deletions src/types/internal.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export interface DefaultConfig {
ignore: Record<string, string>;
strict: boolean;
tab_size: number;
trim: Record<string, string>;
}

type RecursiveRequired<T> = {
Expand Down
29 changes: 28 additions & 1 deletion src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,27 @@ const protectElement = (match, capture) => {
})
}

/**
* Trim leading and trailing whitespace characters.
*
* @param {string} html
* @param {Record<string, string>} trim
* @returns {string}
*/
export const trimify = (html, trim) => {
for (let e = 0; e < Object.keys(trim).length; e++) {
/* Whitespace character must be escaped with '\' or RegExp() won't include it. */
const leading_whitespace = new RegExp(`(<${trim[e]}[^>]*>)\\s+`, "g")
const trailing_whitespace = new RegExp(`\\s+(</${trim[e]}>)`, "g")

html = html
.replace(leading_whitespace, '$1')
.replace(trailing_whitespace, '$1')
}

return html
}

/**
* Unprotect an element by removing entities.
*
Expand Down Expand Up @@ -115,7 +136,11 @@ const unprotectElement = (match, capture) => {
export const validateConfig = (config) => {
if (typeof config !== 'object') throw new Error('Config must be an object.')

const config_empty = !(Object.hasOwn(config, 'tab_size') || Object.hasOwn(config, 'strict') || Object.hasOwn(config, 'ignore'))
const config_empty = !(
Object.hasOwn(config, 'tab_size') ||
Object.hasOwn(config, 'strict') ||
Object.hasOwn(config, 'ignore') ||
Object.hasOwn(config, 'trim'))
if (config_empty) return CONFIG

let tab_size = config.tab_size
Expand All @@ -139,6 +164,8 @@ export const validateConfig = (config) => {
throw new Error('Strict config must be a boolean.')
if (Object.hasOwn(config, 'ignore') && (!Array.isArray(config.ignore) || !config.ignore?.every((e) => typeof e === 'string')))
throw new Error('Ignore config must be an array of strings.')
if (Object.hasOwn(config, 'trim') && (!Array.isArray(config.trim) || !config.trim?.every((e) => typeof e === 'string')))
throw new Error('Trim config must be an array of strings.')

return mergeConfig(CONFIG, config)

Expand Down
19 changes: 19 additions & 0 deletions tests/all.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { closify } from '../src/closify.js'
import { entify } from '../src/entify.js'
import { minify } from '../src/minify.js'
import { prettify } from '../src/prettify.js'
import { trimify } from '../src/utils.js'
import { expect, test } from 'vitest'

const ugly_html = `<form id="3" > <!--
Expand Down Expand Up @@ -78,11 +79,25 @@ Please()
</code></pre>
`

const trim_leading_whitespace = `<div>
Hello</div>`
const trim_trailing_whitespace = `<div>Hello
</div>`

// @ts-ignore
const testConfig = async (config) => {
return await prettify(config_html, config)
}

test('Trimify', () => {
expect(trimify(trim_leading_whitespace, { '0': 'div' })).toBe('<div>Hello</div>')
expect(trimify(trim_trailing_whitespace, { '0': 'div' })).toBe('<div>Hello</div>')
})

test('Prettify', () => {
expect(prettify(ugly_html)).toBe(pretty_html)
})
Expand Down Expand Up @@ -194,3 +209,7 @@ test('Tab size config', () => {
test('Catches invalid ignore config', async () => {
await expect(testConfig({ ignore: [ 'script', 1 ]})).rejects.toThrow('Ignore config must be an array of strings.')
})

test('Catches invalid trim config', async () => {
await expect(testConfig({ trim: [ 'textarea', 1 ]})).rejects.toThrow('Trim config must be an array of strings.')
})
2 changes: 2 additions & 0 deletions types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ declare module 'htmlfy' {
ignore?: string[];
strict?: boolean;
tab_size?: number;
trim?: string[];
}

export interface Config {
ignore: Record<string, string>;
strict: boolean;
tab_size: number;
trim: Record<string, string>;
}

/**
Expand Down

0 comments on commit 76b53db

Please sign in to comment.