Compare commits

...
Sign in to create a new pull request.

4 commits

Author SHA1 Message Date
Jordan Pittman
440954a163 Add test 2022-09-02 09:34:57 -04:00
Jordan Pittman
494d5c9709 Update lockfile 2022-09-02 09:34:57 -04:00
Jordan Pittman
97f93639f4 Add prototype using @parcel/css for minification 2022-09-02 09:34:57 -04:00
Jordan Pittman
0971ee1cff Allow negating utilities using min/max/clamp 2022-09-01 12:06:43 -04:00
7 changed files with 150 additions and 52 deletions

View file

@ -12,13 +12,13 @@
} }
}, },
"../..": { "../..": {
"version": "3.0.24", "version": "3.1.5",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"arg": "^5.0.1", "arg": "^5.0.2",
"chokidar": "^3.5.3", "chokidar": "^3.5.3",
"color-name": "^1.1.4", "color-name": "^1.1.4",
"detective": "^5.2.0", "detective": "^5.2.1",
"didyoumean": "^1.2.2", "didyoumean": "^1.2.2",
"dlv": "^1.1.3", "dlv": "^1.1.3",
"fast-glob": "^3.2.11", "fast-glob": "^3.2.11",
@ -36,26 +36,26 @@
"postcss-selector-parser": "^6.0.10", "postcss-selector-parser": "^6.0.10",
"postcss-value-parser": "^4.2.0", "postcss-value-parser": "^4.2.0",
"quick-lru": "^5.1.1", "quick-lru": "^5.1.1",
"resolve": "^1.22.0" "resolve": "^1.22.1"
}, },
"bin": { "bin": {
"tailwind": "lib/cli.js", "tailwind": "lib/cli.js",
"tailwindcss": "lib/cli.js" "tailwindcss": "lib/cli.js"
}, },
"devDependencies": { "devDependencies": {
"@parcel/css": "^1.11.2",
"@swc/cli": "^0.1.57", "@swc/cli": "^0.1.57",
"@swc/core": "^1.2.160", "@swc/core": "^1.2.196",
"@swc/jest": "^0.2.21", "@swc/jest": "^0.2.21",
"@swc/register": "^0.1.10", "@swc/register": "^0.1.10",
"autoprefixer": "^10.4.7", "autoprefixer": "^10.4.7",
"cssnano": "^5.1.9", "esbuild": "^0.14.48",
"esbuild": "^0.14.39", "eslint": "^8.18.0",
"eslint": "^8.15.0",
"eslint-config-prettier": "^8.5.0", "eslint-config-prettier": "^8.5.0",
"eslint-plugin-prettier": "^4.0.0", "eslint-plugin-prettier": "^4.2.1",
"jest": "^28.0.3", "jest": "^28.1.2",
"jest-diff": "^28.1.0", "jest-diff": "^28.1.1",
"prettier": "^2.6.2", "prettier": "^2.7.1",
"prettier-plugin-tailwindcss": "^0.1.11", "prettier-plugin-tailwindcss": "^0.1.11",
"rimraf": "^3.0.0", "rimraf": "^3.0.0",
"source-map-js": "^1.0.2" "source-map-js": "^1.0.2"
@ -76,27 +76,27 @@
"tailwindcss": { "tailwindcss": {
"version": "file:../..", "version": "file:../..",
"requires": { "requires": {
"@parcel/css": "^1.11.2",
"@swc/cli": "^0.1.57", "@swc/cli": "^0.1.57",
"@swc/core": "^1.2.160", "@swc/core": "^1.2.196",
"@swc/jest": "^0.2.21", "@swc/jest": "^0.2.21",
"@swc/register": "^0.1.10", "@swc/register": "^0.1.10",
"arg": "^5.0.1", "arg": "^5.0.2",
"autoprefixer": "^10.4.7", "autoprefixer": "^10.4.7",
"chokidar": "^3.5.3", "chokidar": "^3.5.3",
"color-name": "^1.1.4", "color-name": "^1.1.4",
"cssnano": "^5.1.9", "detective": "^5.2.1",
"detective": "^5.2.0",
"didyoumean": "^1.2.2", "didyoumean": "^1.2.2",
"dlv": "^1.1.3", "dlv": "^1.1.3",
"esbuild": "^0.14.39", "esbuild": "^0.14.48",
"eslint": "^8.15.0", "eslint": "^8.18.0",
"eslint-config-prettier": "^8.5.0", "eslint-config-prettier": "^8.5.0",
"eslint-plugin-prettier": "^4.0.0", "eslint-plugin-prettier": "^4.2.1",
"fast-glob": "^3.2.11", "fast-glob": "^3.2.11",
"glob-parent": "^6.0.2", "glob-parent": "^6.0.2",
"is-glob": "^4.0.3", "is-glob": "^4.0.3",
"jest": "^28.0.3", "jest": "^28.1.2",
"jest-diff": "^28.1.0", "jest-diff": "^28.1.1",
"lilconfig": "^2.0.5", "lilconfig": "^2.0.5",
"normalize-path": "^3.0.0", "normalize-path": "^3.0.0",
"object-hash": "^3.0.0", "object-hash": "^3.0.0",
@ -108,10 +108,10 @@
"postcss-nested": "5.0.6", "postcss-nested": "5.0.6",
"postcss-selector-parser": "^6.0.10", "postcss-selector-parser": "^6.0.10",
"postcss-value-parser": "^4.2.0", "postcss-value-parser": "^4.2.0",
"prettier": "^2.6.2", "prettier": "^2.7.1",
"prettier-plugin-tailwindcss": "^0.1.11", "prettier-plugin-tailwindcss": "^0.1.11",
"quick-lru": "^5.1.1", "quick-lru": "^5.1.1",
"resolve": "^1.22.0", "resolve": "^1.22.1",
"rimraf": "^3.0.0", "rimraf": "^3.0.0",
"source-map-js": "^1.0.2" "source-map-js": "^1.0.2"
} }

View file

@ -96,6 +96,21 @@ describe('Build command', () => {
expect(withoutMinify.length).toBeGreaterThan(withMinify.length) expect(withoutMinify.length).toBeGreaterThan(withMinify.length)
}) })
test('--minify applies @parcel/css optimizations', async () => {
await writeInputFile('index.html', html`<div class="font-bold"></div>`)
await writeInputFile('index.css', css`
.text-teal {
color: hsl(calc(360deg / 2) 50% 50%);
}
`)
await $(`${EXECUTABLE} --input ./src/index.css --output ./dist/main.css --minify`)
let withMinify = await readOutputFile('main.css')
expect(withMinify).toEqual(css`.text-teal{color:#40bfbf}`)
})
test('--no-autoprefixer', async () => { test('--no-autoprefixer', async () => {
await writeInputFile('index.html', html`<div class="select-none"></div>`) await writeInputFile('index.html', html`<div class="select-none"></div>`)

View file

@ -15,7 +15,7 @@
"scripts": { "scripts": {
"preswcify": "npm run generate && rimraf lib", "preswcify": "npm run generate && rimraf lib",
"swcify": "swc src --out-dir lib --copy-files", "swcify": "swc src --out-dir lib --copy-files",
"postswcify": "esbuild lib/cli-peer-dependencies.js --bundle --platform=node --outfile=peers/index.js", "postswcify": "esbuild lib/cli-peer-dependencies.js --bundle --platform=node --outfile=peers/index.js --define:process.env.CSS_TRANSFORMER_WASM=false",
"rebuild-fixtures": "npm run swcify && node -r @swc/register scripts/rebuildFixtures.js", "rebuild-fixtures": "npm run swcify && node -r @swc/register scripts/rebuildFixtures.js",
"prepublishOnly": "npm install --force && npm run swcify", "prepublishOnly": "npm install --force && npm run swcify",
"style": "eslint .", "style": "eslint .",
@ -42,12 +42,12 @@
"*.js" "*.js"
], ],
"devDependencies": { "devDependencies": {
"@parcel/css": "^1.11.2",
"@swc/cli": "^0.1.57", "@swc/cli": "^0.1.57",
"@swc/core": "^1.2.244", "@swc/core": "^1.2.244",
"@swc/jest": "^0.2.22", "@swc/jest": "^0.2.22",
"@swc/register": "^0.1.10", "@swc/register": "^0.1.10",
"autoprefixer": "^10.4.8", "autoprefixer": "^10.4.8",
"cssnano": "^5.1.13",
"esbuild": "^0.14.54", "esbuild": "^0.14.54",
"eslint": "^8.21.0", "eslint": "^8.21.0",
"eslint-config-prettier": "^8.5.0", "eslint-config-prettier": "^8.5.0",

View file

@ -10,6 +10,6 @@ export function lazyAutoprefixer() {
return require('autoprefixer') return require('autoprefixer')
} }
export function lazyCssnano() { export function lazyParcelCss() {
return require('cssnano') return require('@parcel/css')
} }

View file

@ -1,6 +1,6 @@
#!/usr/bin/env node #!/usr/bin/env node
import { lazyPostcss, lazyPostcssImport, lazyCssnano, lazyAutoprefixer } from '../peers/index.js' import { lazyPostcss, lazyPostcssImport, lazyParcelCss, lazyAutoprefixer } from '../peers/index.js'
import chokidar from 'chokidar' import chokidar from 'chokidar'
import path from 'path' import path from 'path'
@ -353,6 +353,65 @@ if (args['--help']) {
run() run()
/// ---
/** @type {import('@parcel/css')} */
let parcelCss
/** @returns {import('@parcel/css')} */
function loadParcelCss() {
if (parcelCss) {
return parcelCss
}
// Try to load a local version first
try {
return parcelCss = require('@parcel/css')
} catch {}
return parcelCss = lazyParcelCss()
}
/**
*
* @param {boolean} shouldMinify
* @param {import('postcss').Result} result
* @returns {import('postcss').Result}
*/
async function minifyCss(shouldMinify, result) {
if (! shouldMinify) {
return result
}
let css = loadParcelCss()
let transformed
try {
transformed = css.transform({
filename: result.opts.from || 'input.css',
code: Buffer.from(result.css, 'utf-8'),
minify: true,
sourceMap: true,
sourceMap: !!result.map,
inputSourceMap: result.map ? result.map.toString() : undefined,
})
} catch (err) {
console.error("Unable to minify CSS. Using unminified version instead.")
console.error(err)
return result
}
return Object.assign(result, {
css: transformed.code.toString('utf8'),
map: result.map ? Object.assign(result.map, {
toString() {
return transformed.map.toString()
}
}) : result.map,
})
}
// --- // ---
function init() { function init() {
@ -626,17 +685,6 @@ async function build() {
return lazyAutoprefixer() return lazyAutoprefixer()
})(), })(),
args['--minify'] &&
(() => {
let options = { preset: ['default', { cssDeclarationSorter: false }] }
// Try to load a local `cssnano` version first
try {
return require('cssnano')
} catch {}
return lazyCssnano()(options)
})(),
].filter(Boolean) ].filter(Boolean)
let postcss = loadPostcss() let postcss = loadPostcss()
@ -647,6 +695,7 @@ async function build() {
return Promise.resolve() return Promise.resolve()
.then(() => (output ? fs.promises.mkdir(path.dirname(output), { recursive: true }) : null)) .then(() => (output ? fs.promises.mkdir(path.dirname(output), { recursive: true }) : null))
.then(() => processor.process(css, { ...postcssOptions, from: input, to: output })) .then(() => processor.process(css, { ...postcssOptions, from: input, to: output }))
.then((result) => minifyCss(!!args['--minify'], result))
.then((result) => { .then((result) => {
if (!output) { if (!output) {
return process.stdout.write(result.css) return process.stdout.write(result.css)
@ -728,17 +777,6 @@ async function build() {
return lazyAutoprefixer() return lazyAutoprefixer()
})(), })(),
args['--minify'] &&
(() => {
let options = { preset: ['default', { cssDeclarationSorter: false }] }
// Try to load a local `cssnano` version first
try {
return require('cssnano')
} catch {}
return lazyCssnano()(options)
})(),
].filter(Boolean) ].filter(Boolean)
async function rebuild(config) { async function rebuild(config) {
@ -785,6 +823,7 @@ async function build() {
output ? fs.promises.mkdir(path.dirname(output), { recursive: true }) : null output ? fs.promises.mkdir(path.dirname(output), { recursive: true }) : null
) )
.then(() => processor.process(css, { from: input, to: output })) .then(() => processor.process(css, { from: input, to: output }))
.then((result) => minifyCss(!!args['--minify'], result))
.then(async (result) => { .then(async (result) => {
for (let message of result.messages) { for (let message of result.messages) {
if (message.type === 'dependency') { if (message.type === 'dependency') {

View file

@ -10,7 +10,15 @@ export default function (value) {
return value.replace(/^[+-]?/, (sign) => (sign === '-' ? '' : '-')) return value.replace(/^[+-]?/, (sign) => (sign === '-' ? '' : '-'))
} }
if (value.includes('var(') || value.includes('calc(')) { // What functions we support negating numeric values for
return `calc(${value} * -1)` // var() isn't inherently a numeric function but we support it anyway
// The trigonometric functions are omitted because you'll need to use calc(…) with them _anyway_
// to produce generally useful results and that will be covered already
let numericFunctions = ['var', 'calc', 'min', 'max', 'clamp']
for (const fn of numericFunctions) {
if (value.includes(`${fn}(`)) {
return `calc(${value} * -1)`
}
} }
} }

View file

@ -146,6 +146,42 @@ test('a value that includes a calc', () => {
}) })
}) })
test('a value that includes min/max/clamp functions', () => {
let config = {
content: [{ raw: html`<div class="mt-min -mt-min mt-max -mt-max mt-clamp -mt-clamp"></div>` }],
theme: {
margin: {
'min': 'min(100vmin, 3rem)',
'max': 'max(100vmax, 3rem)',
'clamp': 'clamp(1rem, 100vh, 3rem)',
},
},
}
return run('@tailwind utilities', config).then((result) => {
return expect(result.css).toMatchCss(css`
.mt-min {
margin-top: min(100vmin, 3rem);
}
.-mt-min {
margin-top: calc(min(100vmin, 3rem) * -1);
}
.mt-max {
margin-top: max(100vmax, 3rem);
}
.-mt-max {
margin-top: calc(max(100vmax, 3rem) * -1);
}
.mt-clamp {
margin-top: clamp(1rem, 100vh, 3rem);
}
.-mt-clamp {
margin-top: calc(clamp(1rem, 100vh, 3rem) * -1);
}
`)
})
})
test('a keyword value', () => { test('a keyword value', () => {
let config = { let config = {
content: [{ raw: html`<div class="-mt-auto mt-auto"></div>` }], content: [{ raw: html`<div class="-mt-auto mt-auto"></div>` }],