tailwindcss/src/plugin.js
2023-10-03 13:04:17 -04:00

133 lines
4.3 KiB
JavaScript

import postcss from 'postcss'
import lightningcss, { Features } from 'lightningcss'
import browserslist from 'browserslist'
import setupTrackingContext from './lib/setupTrackingContext'
import processTailwindFeatures from './processTailwindFeatures'
import { env } from './lib/sharedState'
import { findAtConfigPath } from './lib/findAtConfigPath'
import { handleImportAtRules } from './lib/handleImportAtRules'
import { version as tailwindVersion } from '../package.json'
function license() {
return `/* ! tailwindcss v${tailwindVersion} | MIT License | https://tailwindcss.com */\n`
}
module.exports = function tailwindcss(configOrPath) {
return {
postcssPlugin: 'tailwindcss',
plugins: [
env.DEBUG &&
function (root) {
console.log('\n')
console.time('JIT TOTAL')
return root
},
...handleImportAtRules(),
async function (root, result) {
// Use the path for the `@config` directive if it exists, otherwise use the
// path for the file being processed
configOrPath = findAtConfigPath(root, result) ?? configOrPath
let context = setupTrackingContext(configOrPath)
if (root.type === 'document') {
let roots = root.nodes.filter((node) => node.type === 'root')
for (const root of roots) {
if (root.type === 'root') {
await processTailwindFeatures(context)(root, result)
}
}
return
}
await processTailwindFeatures(context)(root, result)
},
function lightningCssPlugin(_root, result) {
let map = result.map ?? result.opts.map
let intermediateResult = result.root.toResult({
map: map ? { inline: true } : false,
})
let intermediateMap = intermediateResult.map?.toJSON?.() ?? map
try {
let resolvedBrowsersListConfig = browserslist.findConfig(
result.opts.from ?? process.cwd()
)?.defaults
let defaultBrowsersListConfig = require('../package.json').browserslist
let browsersListConfig = resolvedBrowsersListConfig ?? defaultBrowsersListConfig
let transformed = lightningcss.transform({
filename: result.opts.from,
code: Buffer.from(intermediateResult.css),
minify: false,
sourceMap: !!intermediateMap,
targets: lightningcss.browserslistToTargets(browserslist(browsersListConfig)),
errorRecovery: true,
drafts: {
customMedia: true,
},
nonStandard: {
deepSelectorCombinator: true,
},
include: Features.Nesting,
exclude: Features.LogicalProperties,
})
let code = transformed.code.toString()
// https://postcss.org/api/#sourcemapoptions
if (intermediateMap && transformed.map != null) {
let prev = transformed.map.toString()
if (typeof intermediateMap === 'object') {
intermediateMap.prev = prev
} else {
code = `${code}\n/*# sourceMappingURL=data:application/json;base64,${Buffer.from(
prev
).toString('base64')} */`
}
}
result.root = postcss.parse(license() + code, {
...result.opts,
map: intermediateMap,
})
} catch (err) {
if (err.source && typeof process !== 'undefined' && process.env.JEST_WORKER_ID) {
let lines = err.source.split('\n')
err = new Error(
[
'Error formatting using Lightning CSS:',
'',
...[
'```css',
...lines.slice(Math.max(err.loc.line - 3, 0), err.loc.line),
' '.repeat(err.loc.column - 1) + '^-- ' + err.toString(),
...lines.slice(err.loc.line, err.loc.line + 2),
'```',
],
].join('\n')
)
}
if (Error.captureStackTrace) {
Error.captureStackTrace(err, lightningCssPlugin)
}
throw err
}
},
env.DEBUG &&
function (root) {
console.timeEnd('JIT TOTAL')
console.log('\n')
return root
},
].filter(Boolean),
}
}
module.exports.postcss = true