Skip to content

Commit

Permalink
feat: add css.resolveCalc option
Browse files Browse the repository at this point in the history
resolve calc() functions throughout CSS, not just in inlined styles
  • Loading branch information
cossssmin committed Nov 26, 2024
1 parent 619ef35 commit cde20d4
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 118 deletions.
81 changes: 16 additions & 65 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@
"html-emails"
],
"dependencies": {
"@csstools/css-calc": "^2.0.1",
"@maizzle/cli": "next",
"cheerio": "^1.0.0",
"chokidar": "^3.6.0",
Expand All @@ -68,6 +67,7 @@
"ora": "^8.1.0",
"pathe": "^1.1.2",
"postcss": "^8.4.49",
"postcss-calc": "^10.0.2",
"postcss-css-variables": "^0.19.0",
"postcss-import": "^16.1.0",
"postcss-safe-parser": "^7.0.0",
Expand Down
13 changes: 13 additions & 0 deletions src/posthtml/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,29 @@ import { getPosthtmlOptions } from './defaultConfig.js'

// PostCSS
import tailwindcss from 'tailwindcss'
import postcssCalc from 'postcss-calc'
import postcssImport from 'postcss-import'
import cssVariables from 'postcss-css-variables'
import postcssSafeParser from 'postcss-safe-parser'

import defaultComponentsConfig from './defaultComponentsConfig.js'

export async function process(html = '', config = {}) {
/**
* Configure PostCSS pipeline. Plugins defined and added here
* will apply to all `<style>` tags in the HTML.
*/
const resolveCSSProps = get(config, 'css.resolveProps')
const resolveCalc = get(config, 'css.resolveCalc') !== false
? get(config, 'css.resolveCalc', { precision: 2 }) // it's true by default, use default precision 2
: false

const postcssPlugin = posthtmlPostcss(
[
postcssImport(),
tailwindcss(get(config, 'css.tailwind', {})),
resolveCSSProps !== false && cssVariables(resolveCSSProps),
resolveCalc !== false && postcssCalc(resolveCalc),
...get(config, 'postcss.plugins', []),
],
merge(
Expand All @@ -38,6 +47,10 @@ export async function process(html = '', config = {}) {
)
)

/**
* Define PostHTML options by merging user-provided ones
* on top of a default configuration.
*/
const posthtmlOptions = getPosthtmlOptions(get(config, 'posthtml.options', {}))

const componentsUserOptions = get(config, 'components', {})
Expand Down
18 changes: 0 additions & 18 deletions src/transformers/inline.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import has from 'lodash-es/has.js'
import * as cheerio from 'cheerio/slim'
import remove from 'lodash-es/remove.js'
import { render } from 'posthtml-render'
import { calc } from '@csstools/css-calc'
import isEmpty from 'lodash-es/isEmpty.js'
import safeParser from 'postcss-safe-parser'
import isObject from 'lodash-es/isObject.js'
Expand All @@ -31,7 +30,6 @@ export async function inline(html = '', options = {}) {

options.removeInlinedSelectors = get(options, 'removeInlinedSelectors', true)
options.preferUnitlessValues = get(options, 'preferUnitlessValues', true)
options.resolveCalc = get(options, 'resolveCalc', true)
options.safelist = new Set([
...get(options, 'safelist', []),
...[
Expand Down Expand Up @@ -145,18 +143,6 @@ export async function inline(html = '', options = {}) {

// For each rule in the CSS block we're parsing
root.walkRules(rule => {
// Keep track of declarations in the rule
const declarations = new Set()

rule.walkDecls(decl => {
// Resolve calc() values to static values
if (options.resolveCalc) {
decl.value = decl.value.includes('calc(') ? calc(decl.value, { precision: 2 }) : decl.value
}

declarations.add(decl)
})

const { selector } = rule

selectors.add({
Expand Down Expand Up @@ -196,10 +182,6 @@ export async function inline(html = '', options = {}) {
inlineStyles = styleAttr.split(';').reduce((acc, i) => {
let { property, value } = parseCSSRule(i)

if (value && options.resolveCalc) {
value = value.includes('calc') ? calc(value, { precision: 2 }) : value
}

if (value && options.preferUnitlessValues) {
value = value.replace(
/\b0(px|rem|em|%|vh|vw|vmin|vmax|in|cm|mm|pt|pc|ex|ch)\b/g,
Expand Down
70 changes: 42 additions & 28 deletions test/postcss.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import { process as posthtml } from '../src/posthtml/index.js'
const cleanString = (str) => str.replace(/\s+/g, ' ').trim()

describe.concurrent('PostCSS', () => {
test('Resolves CSS variables by default', async () => {
const html = `
test('resolveProps', async () => {
// Default: resolves CSS variables
posthtml(`
<style>
:root {
--color: red;
Expand All @@ -15,15 +16,32 @@ describe.concurrent('PostCSS', () => {
}
</style>
<p class="foo">test</p>
`

const { html: result } = await posthtml(html)
`).then(({ html }) => {
expect(cleanString(html)).toBe(`<style> .foo { color: red; } </style> <p class="foo">test</p>`)
})

expect(cleanString(result)).toBe(`<style> .foo { color: red; } </style> <p class="foo">test</p>`)
})
// Passing options
posthtml(`
<style>
.foo {
font-weight: var(--font-weight);
}
</style>
<p class="foo">test</p>
`, {
css: {
resolveProps: {
variables: {
'--font-weight': 'bold',
}
},
}
}).then(({ html }) => {
expect(cleanString(html)).toBe(`<style>.foo { font-weight: bold; } </style> <p class="foo">test</p>`)
})

test('Does not resolve CSS variables', async () => {
const html = `
// Disabling `resolveProps`
posthtml(`
<style>
:root {
--color: red;
Expand All @@ -33,41 +51,37 @@ describe.concurrent('PostCSS', () => {
}
</style>
<p class="foo">test</p>
`

const { html: result } = await posthtml(html, {
`, {
css: {
resolveProps: false,
}
}).then(({ html }) => {
expect(cleanString(html)).toBe(`<style> :root { --color: red; } .foo { color: var(--color); } </style> <p class="foo">test</p>`)
})

expect(cleanString(result)).toBe(`<style> :root { --color: red; } .foo { color: var(--color); } </style> <p class="foo">test</p>`)
})

test('Resolves CSS variables (with options)', async () => {
test('resolveCalc', async () => {
const html = `
<style>
:root {
--color: red;
}
.foo {
color: var(--color);
font-weight: var(--font-weight);
width: calc(16px * 1.5569);
}
</style>
<p class="foo">test</p>
`

const { html: result } = await posthtml(html, {
posthtml(html)
.then(({ html }) => {
expect(cleanString(html)).toBe('<style> .foo { width: 24.91px; } </style>')
})

posthtml(html, {
css: {
resolveProps: {
variables: {
'--font-weight': 'bold',
}
resolveCalc: {
precision: 1,
},
}
}).then(({ html }) => {
expect(cleanString(html)).toBe('<style> .foo { width: 24.9px; } </style>')
})

expect(cleanString(result)).toBe(`<style>.foo { color: red; font-weight: bold; } </style> <p class="foo">test</p>`)
})
})
12 changes: 6 additions & 6 deletions test/transformers/inlineCSS.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const css = `
.w-1 {width: 4px}
.h-1 {height: 4px}
.foo {color: red}
.bar {cursor: pointer; margin: calc(4px * 0)}
.bar {cursor: pointer}
.hover\\:foo:hover {color: blue}
.bg-custom {background-image: url('https://picsum.photos/600/400') !important}
@media (max-width: 600px) {
Expand Down Expand Up @@ -52,10 +52,10 @@ describe.concurrent('Inline CSS', () => {
}
u + .body .gmail\\:hidden { display: none; }
</style>
<p style="cursor: pointer; margin: 0">test</p>
<p style="cursor: pointer">test</p>
<table class="sm:text-center" style="width: 4px; height: 4px; background-image: url('https://picsum.photos/600/400')">
<tr>
<td class="gmail:hidden" style="height: 4px; color: red; cursor: pointer; margin: 0">test</td>
<td class="gmail:hidden" style="height: 4px; color: red; cursor: pointer">test</td>
</tr>
</table>`))
})
Expand Down Expand Up @@ -93,18 +93,18 @@ describe.concurrent('Inline CSS', () => {
.w-1 {width: 4px}
.h-1 {height: 4px}
.foo {color: red}
.bar {cursor: pointer; margin: calc(4px * 0)}
.bar {cursor: pointer}
.hover\\:foo:hover {color: blue}
.bg-custom {background-image: url('https://picsum.photos/600/400') !important}
@media (max-width: 600px) {
.sm\\:text-center {text-align: center}
}
u + .body .gmail\\:hidden { display: none; }
</style>
<p class="bar" style="cursor: pointer; margin: 0">test</p>
<p class="bar" style="cursor: pointer">test</p>
<table class="w-1 h-1 sm:text-center bg-custom" style="width: 4px; height: 4px; background-image: url('https://picsum.photos/600/400')">
<tr>
<td class="foo bar h-1 gmail:hidden" style="height: 4px; color: red; cursor: pointer; margin: 0">test</td>
<td class="foo bar h-1 gmail:hidden" style="height: 4px; color: red; cursor: pointer">test</td>
</tr>
</table>`))
})
Expand Down

0 comments on commit cde20d4

Please sign in to comment.