diff --git a/.eslintrc.json b/.eslintrc.json index d0832f697bbf..edbcdd19e459 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -3,7 +3,7 @@ "jest": true }, "parserOptions": { - "ecmaVersion": 2018, + "ecmaVersion": 2020, "sourceType": "module" }, "extends": ["prettier"], diff --git a/jest/customMatchers.js b/jest/customMatchers.js index e5e5b4348566..e89385c17f6e 100644 --- a/jest/customMatchers.js +++ b/jest/customMatchers.js @@ -1,5 +1,5 @@ -import prettier from 'prettier' -import diff from 'jest-diff' +const prettier = require('prettier') +const diff = require('jest-diff').default function format(input) { return prettier.format(input, { @@ -54,3 +54,55 @@ expect.extend({ return { actual: received, message, pass } }, }) + +expect.extend({ + // Compare two CSS strings with all whitespace removed + // This is probably naive but it's fast and works well enough. + toMatchFormattedCss(received, argument) { + function format(input) { + return prettier.format(input, { + parser: 'css', + printWidth: 100, + }) + } + const options = { + comment: 'stripped(received) === stripped(argument)', + isNot: this.isNot, + promise: this.promise, + } + + let formattedReceived = format(received) + let formattedArgument = format(argument) + + const pass = formattedReceived === formattedArgument + + const message = pass + ? () => { + return ( + this.utils.matcherHint('toMatchCss', undefined, undefined, options) + + '\n\n' + + `Expected: not ${this.utils.printExpected(formattedReceived)}\n` + + `Received: ${this.utils.printReceived(formattedArgument)}` + ) + } + : () => { + const actual = formattedReceived + const expected = formattedArgument + + const diffString = diff(expected, actual, { + expand: this.expand, + }) + + return ( + this.utils.matcherHint('toMatchCss', undefined, undefined, options) + + '\n\n' + + (diffString && diffString.includes('- Expect') + ? `Difference:\n\n${diffString}` + : `Expected: ${this.utils.printExpected(expected)}\n` + + `Received: ${this.utils.printReceived(actual)}`) + ) + } + + return { actual: received, message, pass } + }, +}) diff --git a/jit/corePlugins/accessibility.js b/jit/corePlugins/accessibility.js new file mode 100644 index 000000000000..e3242bcf76f4 --- /dev/null +++ b/jit/corePlugins/accessibility.js @@ -0,0 +1,25 @@ +const { createSimpleStaticUtilityPlugin } = require('../pluginUtils') + +module.exports = createSimpleStaticUtilityPlugin({ + '.sr-only': { + position: 'absolute', + width: '1px', + height: '1px', + padding: '0', + margin: '-1px', + overflow: 'hidden', + clip: 'rect(0, 0, 0, 0)', + whiteSpace: 'nowrap', + borderWidth: '0', + }, + '.not-sr-only': { + position: 'static', + width: 'auto', + height: 'auto', + padding: '0', + margin: '0', + overflow: 'visible', + clip: 'auto', + whiteSpace: 'normal', + }, +}) diff --git a/jit/corePlugins/alignContent.js b/jit/corePlugins/alignContent.js new file mode 100644 index 000000000000..69e275aa952d --- /dev/null +++ b/jit/corePlugins/alignContent.js @@ -0,0 +1,22 @@ +const { createSimpleStaticUtilityPlugin } = require('../pluginUtils') + +module.exports = createSimpleStaticUtilityPlugin({ + '.content-center': { + 'align-content': 'center', + }, + '.content-start': { + 'align-content': 'flex-start', + }, + '.content-end': { + 'align-content': 'flex-end', + }, + '.content-between': { + 'align-content': 'space-between', + }, + '.content-around': { + 'align-content': 'space-around', + }, + '.content-evenly': { + 'align-content': 'space-evenly', + }, +}) diff --git a/jit/corePlugins/alignItems.js b/jit/corePlugins/alignItems.js new file mode 100644 index 000000000000..fa44536ea73a --- /dev/null +++ b/jit/corePlugins/alignItems.js @@ -0,0 +1,19 @@ +const { createSimpleStaticUtilityPlugin } = require('../pluginUtils') + +module.exports = createSimpleStaticUtilityPlugin({ + '.items-start': { + 'align-items': 'flex-start', + }, + '.items-end': { + 'align-items': 'flex-end', + }, + '.items-center': { + 'align-items': 'center', + }, + '.items-baseline': { + 'align-items': 'baseline', + }, + '.items-stretch': { + 'align-items': 'stretch', + }, +}) diff --git a/jit/corePlugins/alignSelf.js b/jit/corePlugins/alignSelf.js new file mode 100644 index 000000000000..87b769bbb7fc --- /dev/null +++ b/jit/corePlugins/alignSelf.js @@ -0,0 +1,19 @@ +const { createSimpleStaticUtilityPlugin } = require('../pluginUtils') + +module.exports = createSimpleStaticUtilityPlugin({ + '.self-auto': { + 'align-self': 'auto', + }, + '.self-start': { + 'align-self': 'flex-start', + }, + '.self-end': { + 'align-self': 'flex-end', + }, + '.self-center': { + 'align-self': 'center', + }, + '.self-stretch': { + 'align-self': 'stretch', + }, +}) diff --git a/jit/corePlugins/animation.js b/jit/corePlugins/animation.js new file mode 100644 index 000000000000..a45ed9f7fef0 --- /dev/null +++ b/jit/corePlugins/animation.js @@ -0,0 +1,39 @@ +const { nameClass } = require('../pluginUtils') +const transformThemeValue = require('../../lib/util/transformThemeValue').default +const parseAnimationValue = require('../../lib/util/parseAnimationValue').default + +module.exports = function ({ matchUtilities, theme }) { + let keyframes = Object.fromEntries( + Object.entries(theme('keyframes')).map(([key, value]) => { + return [ + key, + [ + { + [`@keyframes ${key}`]: value, + }, + { respectVariants: false }, + ], + ] + }) + ) + + let transformValue = transformThemeValue('animation') + matchUtilities({ + animate: [ + (modifier, { theme }) => { + let value = transformValue(theme.animation[modifier]) + + if (modifier === '' || value === undefined) { + return [] + } + + let { name: animationName } = parseAnimationValue(value) + + return [ + keyframes[animationName], + { [nameClass('animate', modifier)]: { animation: value } }, + ].filter(Boolean) + }, + ], + }) +} diff --git a/jit/corePlugins/appearance.js b/jit/corePlugins/appearance.js new file mode 100644 index 000000000000..18593e3bc80c --- /dev/null +++ b/jit/corePlugins/appearance.js @@ -0,0 +1,5 @@ +const { createSimpleStaticUtilityPlugin } = require('../pluginUtils') + +module.exports = createSimpleStaticUtilityPlugin({ + '.appearance-none': { appearance: 'none' }, +}) diff --git a/jit/corePlugins/backgroundAttachment.js b/jit/corePlugins/backgroundAttachment.js new file mode 100644 index 000000000000..db77f01d9453 --- /dev/null +++ b/jit/corePlugins/backgroundAttachment.js @@ -0,0 +1,7 @@ +const { createSimpleStaticUtilityPlugin } = require('../pluginUtils') + +module.exports = createSimpleStaticUtilityPlugin({ + '.bg-fixed': { 'background-attachment': 'fixed' }, + '.bg-local': { 'background-attachment': 'local' }, + '.bg-scroll': { 'background-attachment': 'scroll' }, +}) diff --git a/jit/corePlugins/backgroundClip.js b/jit/corePlugins/backgroundClip.js new file mode 100644 index 000000000000..155719925b02 --- /dev/null +++ b/jit/corePlugins/backgroundClip.js @@ -0,0 +1,8 @@ +const { createSimpleStaticUtilityPlugin } = require('../pluginUtils') + +module.exports = createSimpleStaticUtilityPlugin({ + '.bg-clip-border': { 'background-clip': 'border-box' }, + '.bg-clip-padding': { 'background-clip': 'padding-box' }, + '.bg-clip-content': { 'background-clip': 'content-box' }, + '.bg-clip-text': { 'background-clip': 'text' }, +}) diff --git a/jit/corePlugins/backgroundColor.js b/jit/corePlugins/backgroundColor.js new file mode 100644 index 000000000000..2bde03d70bc5 --- /dev/null +++ b/jit/corePlugins/backgroundColor.js @@ -0,0 +1,25 @@ +const flattenColorPalette = require('../../lib/util/flattenColorPalette').default +const withAlphaVariable = require('../../lib/util/withAlphaVariable').default +const { asColor, nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities, theme }) { + let colorPalette = flattenColorPalette(theme('backgroundColor')) + + matchUtilities({ + bg: (modifier) => { + let value = asColor(modifier, colorPalette) + + if (value === undefined) { + return [] + } + + return { + [nameClass('bg', modifier)]: withAlphaVariable({ + color: value, + property: 'background-color', + variable: '--tw-bg-opacity', + }), + } + }, + }) +} diff --git a/jit/corePlugins/backgroundImage.js b/jit/corePlugins/backgroundImage.js new file mode 100644 index 000000000000..69162875d739 --- /dev/null +++ b/jit/corePlugins/backgroundImage.js @@ -0,0 +1,15 @@ +const { nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + bg: (modifier, { theme }) => { + let value = theme.backgroundImage[modifier] + + if (value === undefined) { + return [] + } + + return { [nameClass('bg', modifier)]: { 'background-image': value } } + }, + }) +} diff --git a/jit/corePlugins/backgroundOpacity.js b/jit/corePlugins/backgroundOpacity.js new file mode 100644 index 000000000000..ab1b6655464b --- /dev/null +++ b/jit/corePlugins/backgroundOpacity.js @@ -0,0 +1,15 @@ +const { asValue, nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + 'bg-opacity': (modifier, { theme }) => { + let value = asValue(modifier, theme.backgroundOpacity) + + if (value === undefined) { + return [] + } + + return { [nameClass('bg-opacity', modifier)]: { '--tw-bg-opacity': value } } + }, + }) +} diff --git a/jit/corePlugins/backgroundPosition.js b/jit/corePlugins/backgroundPosition.js new file mode 100644 index 000000000000..c1a793b3db03 --- /dev/null +++ b/jit/corePlugins/backgroundPosition.js @@ -0,0 +1,15 @@ +const { nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + bg: (modifier, { theme }) => { + let value = theme.backgroundPosition[modifier] + + if (value === undefined) { + return [] + } + + return { [nameClass('bg', modifier)]: { 'background-position': value } } + }, + }) +} diff --git a/jit/corePlugins/backgroundRepeat.js b/jit/corePlugins/backgroundRepeat.js new file mode 100644 index 000000000000..a3c4e85fc96e --- /dev/null +++ b/jit/corePlugins/backgroundRepeat.js @@ -0,0 +1,10 @@ +const { createSimpleStaticUtilityPlugin } = require('../pluginUtils') + +module.exports = createSimpleStaticUtilityPlugin({ + '.bg-repeat': { 'background-repeat': 'repeat' }, + '.bg-no-repeat': { 'background-repeat': 'no-repeat' }, + '.bg-repeat-x': { 'background-repeat': 'repeat-x' }, + '.bg-repeat-y': { 'background-repeat': 'repeat-y' }, + '.bg-repeat-round': { 'background-repeat': 'round' }, + '.bg-repeat-space': { 'background-repeat': 'space' }, +}) diff --git a/jit/corePlugins/backgroundSize.js b/jit/corePlugins/backgroundSize.js new file mode 100644 index 000000000000..1659021abae3 --- /dev/null +++ b/jit/corePlugins/backgroundSize.js @@ -0,0 +1,15 @@ +const { nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + bg: (modifier, { theme }) => { + let value = theme.backgroundSize[modifier] + + if (value === undefined) { + return [] + } + + return { [nameClass('bg', modifier)]: { 'background-size': value } } + }, + }) +} diff --git a/jit/corePlugins/borderCollapse.js b/jit/corePlugins/borderCollapse.js new file mode 100644 index 000000000000..5815d7df4b4b --- /dev/null +++ b/jit/corePlugins/borderCollapse.js @@ -0,0 +1,6 @@ +const { createSimpleStaticUtilityPlugin } = require('../pluginUtils') + +module.exports = createSimpleStaticUtilityPlugin({ + '.border-collapse': { 'border-collapse': 'collapse' }, + '.border-separate': { 'border-collapse': 'separate' }, +}) diff --git a/jit/corePlugins/borderColor.js b/jit/corePlugins/borderColor.js new file mode 100644 index 000000000000..5c7902550d72 --- /dev/null +++ b/jit/corePlugins/borderColor.js @@ -0,0 +1,29 @@ +const flattenColorPalette = require('../../lib/util/flattenColorPalette').default +const withAlphaVariable = require('../../lib/util/withAlphaVariable').default +const { asColor, nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities, theme }) { + let colorPalette = flattenColorPalette(theme('borderColor')) + + matchUtilities({ + border: (modifier) => { + if (modifier === 'DEFAULT') { + return [] + } + + let value = asColor(modifier, colorPalette) + + if (value === undefined) { + return [] + } + + return { + [nameClass('border', modifier)]: withAlphaVariable({ + color: value, + property: 'border-color', + variable: '--tw-border-opacity', + }), + } + }, + }) +} diff --git a/jit/corePlugins/borderOpacity.js b/jit/corePlugins/borderOpacity.js new file mode 100644 index 000000000000..4fc4917d87a9 --- /dev/null +++ b/jit/corePlugins/borderOpacity.js @@ -0,0 +1,15 @@ +const { asValue, nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + 'border-opacity': (modifier, { theme }) => { + let value = asValue(modifier, theme.borderOpacity) + + if (value === undefined) { + return [] + } + + return { [nameClass('border-opacity', modifier)]: { '--tw-border-opacity': value } } + }, + }) +} diff --git a/jit/corePlugins/borderRadius.js b/jit/corePlugins/borderRadius.js new file mode 100644 index 000000000000..b3d271c9f198 --- /dev/null +++ b/jit/corePlugins/borderRadius.js @@ -0,0 +1,111 @@ +const { asLength, nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + rounded: (modifier, { theme }) => { + let value = asLength(modifier, theme['borderRadius']) + + if (value === undefined) { + return [] + } + + return { [nameClass('rounded', modifier)]: { 'border-radius': value } } + }, + }) + matchUtilities({ + 'rounded-t': (modifier, { theme }) => { + let value = asLength(modifier, theme['borderRadius']) + + if (value === undefined) { + return [] + } + + return { + [nameClass('rounded-t', modifier)]: { + 'border-top-left-radius': value, + 'border-top-right-radius': value, + }, + } + }, + 'rounded-r': (modifier, { theme }) => { + let value = asLength(modifier, theme['borderRadius']) + + if (value === undefined) { + return [] + } + + return { + [nameClass('rounded-r', modifier)]: { + 'border-top-right-radius': value, + 'border-bottom-right-radius': value, + }, + } + }, + 'rounded-b': (modifier, { theme }) => { + let value = asLength(modifier, theme['borderRadius']) + + if (value === undefined) { + return [] + } + + return { + [nameClass('rounded-b', modifier)]: { + 'border-bottom-right-radius': value, + 'border-bottom-left-radius': value, + }, + } + }, + 'rounded-l': (modifier, { theme }) => { + let value = asLength(modifier, theme['borderRadius']) + + if (value === undefined) { + return [] + } + + return { + [nameClass('rounded-l', modifier)]: { + 'border-top-left-radius': value, + 'border-bottom-left-radius': value, + }, + } + }, + }) + matchUtilities({ + 'rounded-tl': (modifier, { theme }) => { + let value = asLength(modifier, theme['borderRadius']) + + if (value === undefined) { + return [] + } + + return { [nameClass('rounded-tl', modifier)]: { 'border-top-left-radius': value } } + }, + 'rounded-tr': (modifier, { theme }) => { + let value = asLength(modifier, theme['borderRadius']) + + if (value === undefined) { + return [] + } + + return { [nameClass('rounded-tr', modifier)]: { 'border-top-right-radius': value } } + }, + 'rounded-br': (modifier, { theme }) => { + let value = asLength(modifier, theme['borderRadius']) + + if (value === undefined) { + return [] + } + + return { [nameClass('rounded-br', modifier)]: { 'border-bottom-right-radius': value } } + }, + 'rounded-bl': (modifier, { theme }) => { + let value = asLength(modifier, theme['borderRadius']) + + if (value === undefined) { + return [] + } + + return { [nameClass('rounded-bl', modifier)]: { 'border-bottom-left-radius': value } } + }, + }) +} diff --git a/jit/corePlugins/borderStyle.js b/jit/corePlugins/borderStyle.js new file mode 100644 index 000000000000..042e25b9987b --- /dev/null +++ b/jit/corePlugins/borderStyle.js @@ -0,0 +1,19 @@ +const { createSimpleStaticUtilityPlugin } = require('../pluginUtils') + +module.exports = createSimpleStaticUtilityPlugin({ + '.border-solid': { + 'border-style': 'solid', + }, + '.border-dashed': { + 'border-style': 'dashed', + }, + '.border-dotted': { + 'border-style': 'dotted', + }, + '.border-double': { + 'border-style': 'double', + }, + '.border-none': { + 'border-style': 'none', + }, +}) diff --git a/jit/corePlugins/borderWidth.js b/jit/corePlugins/borderWidth.js new file mode 100644 index 000000000000..2177ff5b7d32 --- /dev/null +++ b/jit/corePlugins/borderWidth.js @@ -0,0 +1,53 @@ +const { asLength, nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + border: (modifier, { theme }) => { + let value = asLength(modifier, theme['borderWidth']) + + if (value === undefined) { + return [] + } + + return { [nameClass('border', modifier)]: { 'border-width': value } } + }, + }) + matchUtilities({ + 'border-t': (modifier, { theme }) => { + let value = asLength(modifier, theme['borderWidth']) + + if (value === undefined) { + return [] + } + + return { [nameClass('border-t', modifier)]: { 'border-top-width': value } } + }, + 'border-r': (modifier, { theme }) => { + let value = asLength(modifier, theme['borderWidth']) + + if (value === undefined) { + return [] + } + + return { [nameClass('border-r', modifier)]: { 'border-right-width': value } } + }, + 'border-b': (modifier, { theme }) => { + let value = asLength(modifier, theme['borderWidth']) + + if (value === undefined) { + return [] + } + + return { [nameClass('border-b', modifier)]: { 'border-bottom-width': value } } + }, + 'border-l': (modifier, { theme }) => { + let value = asLength(modifier, theme['borderWidth']) + + if (value === undefined) { + return [] + } + + return { [nameClass('border-l', modifier)]: { 'border-left-width': value } } + }, + }) +} diff --git a/jit/corePlugins/boxShadow.js b/jit/corePlugins/boxShadow.js new file mode 100644 index 000000000000..dcca1040c406 --- /dev/null +++ b/jit/corePlugins/boxShadow.js @@ -0,0 +1,37 @@ +const { nameClass } = require('../pluginUtils') +const transformThemeValue = require('../../lib/util/transformThemeValue').default + +let transformValue = transformThemeValue('boxShadow') +let shadowReset = { + '*': { + '--tw-shadow': '0 0 #0000', + }, +} + +module.exports = function ({ addBase, matchUtilities }) { + addBase(shadowReset) + matchUtilities({ + shadow: (modifier, { theme }) => { + modifier = modifier === '' ? 'DEFAULT' : modifier + + let value = transformValue(theme.boxShadow[modifier]) + + if (value === undefined) { + return [] + } + + return [ + { + [nameClass('shadow', modifier)]: { + '--tw-shadow': value === 'none' ? '0 0 #0000' : value, + 'box-shadow': [ + `var(--tw-ring-offset-shadow, 0 0 #0000)`, + `var(--tw-ring-shadow, 0 0 #0000)`, + `var(--tw-shadow)`, + ].join(', '), + }, + }, + ] + }, + }) +} diff --git a/jit/corePlugins/boxSizing.js b/jit/corePlugins/boxSizing.js new file mode 100644 index 000000000000..5ca7893f88a7 --- /dev/null +++ b/jit/corePlugins/boxSizing.js @@ -0,0 +1,6 @@ +const { createSimpleStaticUtilityPlugin } = require('../pluginUtils') + +module.exports = createSimpleStaticUtilityPlugin({ + '.box-border': { 'box-sizing': 'border-box' }, + '.box-content': { 'box-sizing': 'content-box' }, +}) diff --git a/jit/corePlugins/clear.js b/jit/corePlugins/clear.js new file mode 100644 index 000000000000..d8242454f09c --- /dev/null +++ b/jit/corePlugins/clear.js @@ -0,0 +1,8 @@ +const { createSimpleStaticUtilityPlugin } = require('../pluginUtils') + +module.exports = createSimpleStaticUtilityPlugin({ + '.clear-left': { clear: 'left' }, + '.clear-right': { clear: 'right' }, + '.clear-both': { clear: 'both' }, + '.clear-none': { clear: 'none' }, +}) diff --git a/jit/corePlugins/container.js b/jit/corePlugins/container.js new file mode 100644 index 000000000000..cf278e798459 --- /dev/null +++ b/jit/corePlugins/container.js @@ -0,0 +1,3 @@ +const container = require('../../lib/plugins/container') + +module.exports = container() diff --git a/jit/corePlugins/css/preflight.css b/jit/corePlugins/css/preflight.css new file mode 100644 index 000000000000..ac74dd0613b2 --- /dev/null +++ b/jit/corePlugins/css/preflight.css @@ -0,0 +1,240 @@ +/** + * Manually forked from SUIT CSS Base: https://github.com/suitcss/base + * A thin layer on top of normalize.css that provides a starting point more + * suitable for web applications. + */ + +/** + * Removes the default spacing and border for appropriate elements. + */ + +blockquote, +dl, +dd, +h1, +h2, +h3, +h4, +h5, +h6, +hr, +figure, +p, +pre { + margin: 0; +} + +button { + background-color: transparent; + background-image: none; +} + +/** + * Work around a Firefox/IE bug where the transparent `button` background + * results in a loss of the default `button` focus styles. + */ + +button:focus { + outline: 1px dotted; + outline: 5px auto -webkit-focus-ring-color; +} + +fieldset { + margin: 0; + padding: 0; +} + +ol, +ul { + list-style: none; + margin: 0; + padding: 0; +} + +/** + * Tailwind custom reset styles + */ + +/** + * 1. Use the user's configured `sans` font-family (with Tailwind's default + * sans-serif font stack as a fallback) as a sane default. + * 2. Use Tailwind's default "normal" line-height so the user isn't forced + * to override it to ensure consistency even when using the default theme. + */ + +html { + font-family: theme('fontFamily.sans', ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"); /* 1 */ + line-height: 1.5; /* 2 */ +} + + +/** + * Inherit font-family and line-height from `html` so users can set them as + * a class directly on the `html` element. + */ + +body { + font-family: inherit; + line-height: inherit; +} + +/** + * 1. Prevent padding and border from affecting element width. + * + * We used to set this in the html element and inherit from + * the parent element for everything else. This caused issues + * in shadow-dom-enhanced elements like
where the content + * is wrapped by a div with box-sizing set to `content-box`. + * + * https://github.com/mozdevs/cssremedy/issues/4 + * + * + * 2. Allow adding a border to an element by just adding a border-width. + * + * By default, the way the browser specifies that an element should have no + * border is by setting it's border-style to `none` in the user-agent + * stylesheet. + * + * In order to easily add borders to elements by just setting the `border-width` + * property, we change the default border-style for all elements to `solid`, and + * use border-width to hide them instead. This way our `border` utilities only + * need to set the `border-width` property instead of the entire `border` + * shorthand, making our border utilities much more straightforward to compose. + * + * https://github.com/tailwindcss/tailwindcss/pull/116 + */ + +*, +::before, +::after { + box-sizing: border-box; /* 1 */ + border-width: 0; /* 2 */ + border-style: solid; /* 2 */ + border-color: theme('borderColor.DEFAULT', currentColor); /* 2 */ +} + +/* + * Ensure horizontal rules are visible by default + */ + +hr { + border-top-width: 1px; +} + +/** + * Undo the `border-style: none` reset that Normalize applies to images so that + * our `border-{width}` utilities have the expected effect. + * + * The Normalize reset is unnecessary for us since we default the border-width + * to 0 on all elements. + * + * https://github.com/tailwindcss/tailwindcss/issues/362 + */ + +img { + border-style: solid; +} + +textarea { + resize: vertical; +} + +input::placeholder, +textarea::placeholder { + opacity: 1; + color: theme('colors.gray.400', #a1a1aa); +} + +button, +[role="button"] { + cursor: pointer; +} + +table { + border-collapse: collapse; +} + +h1, +h2, +h3, +h4, +h5, +h6 { + font-size: inherit; + font-weight: inherit; +} + +/** + * Reset links to optimize for opt-in styling instead of + * opt-out. + */ + +a { + color: inherit; + text-decoration: inherit; +} + +/** + * Reset form element properties that are easy to forget to + * style explicitly so you don't inadvertently introduce + * styles that deviate from your design system. These styles + * supplement a partial reset that is already applied by + * normalize.css. + */ + +button, +input, +optgroup, +select, +textarea { + padding: 0; + line-height: inherit; + color: inherit; +} + +/** + * Use the configured 'mono' font family for elements that + * are expected to be rendered with a monospace font, falling + * back to the system monospace stack if there is no configured + * 'mono' font family. + */ + +pre, +code, +kbd, +samp { + font-family: theme('fontFamily.mono', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace); +} + +/** + * Make replaced elements `display: block` by default as that's + * the behavior you want almost all of the time. Inspired by + * CSS Remedy, with `svg` added as well. + * + * https://github.com/mozdevs/cssremedy/issues/14 + */ + +img, +svg, +video, +canvas, +audio, +iframe, +embed, +object { + display: block; + vertical-align: middle; +} + +/** + * Constrain images and videos to the parent width and preserve + * their instrinsic aspect ratio. + * + * https://github.com/mozdevs/cssremedy/issues/14 + */ + +img, +video { + max-width: 100%; + height: auto; +} diff --git a/jit/corePlugins/cursor.js b/jit/corePlugins/cursor.js new file mode 100644 index 000000000000..22065f619c2d --- /dev/null +++ b/jit/corePlugins/cursor.js @@ -0,0 +1,15 @@ +const { asValue, nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + cursor: (modifier, { theme }) => { + let value = asValue(modifier, theme.cursor) + + if (value === undefined) { + return [] + } + + return { [nameClass('cursor', modifier)]: { cursor: value } } + }, + }) +} diff --git a/jit/corePlugins/display.js b/jit/corePlugins/display.js new file mode 100644 index 000000000000..89e943b264a3 --- /dev/null +++ b/jit/corePlugins/display.js @@ -0,0 +1,27 @@ +const { createSimpleStaticUtilityPlugin } = require('../pluginUtils') + +module.exports = [ + createSimpleStaticUtilityPlugin({ + '.block': { display: 'block' }, + '.inline-block': { display: 'inline-block' }, + '.inline': { display: 'inline' }, + '.flex': { display: 'flex' }, + '.inline-flex': { display: 'inline-flex' }, + '.table': { display: 'table' }, + '.table-caption': { display: 'table-caption' }, + '.table-cell': { display: 'table-cell' }, + '.table-column': { display: 'table-column' }, + '.table-column-group': { display: 'table-column-group' }, + '.table-footer-group': { display: 'table-footer-group' }, + '.table-header-group': { display: 'table-header-group' }, + '.table-row-group': { display: 'table-row-group' }, + '.table-row': { display: 'table-row' }, + '.flow-root': { display: 'flow-root' }, + '.grid': { display: 'grid' }, + '.inline-grid': { display: 'inline-grid' }, + '.contents': { display: 'contents' }, + }), + createSimpleStaticUtilityPlugin({ + '.hidden': { display: 'none' }, + }), +] diff --git a/jit/corePlugins/divideColor.js b/jit/corePlugins/divideColor.js new file mode 100644 index 000000000000..c17d83be3d13 --- /dev/null +++ b/jit/corePlugins/divideColor.js @@ -0,0 +1,26 @@ +const flattenColorPalette = require('../../lib/util/flattenColorPalette').default +const withAlphaVariable = require('../../lib/util/withAlphaVariable').default +const { asColor, nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities, theme }) { + let colorPalette = flattenColorPalette(theme('divideColor')) + + // TODO: Make sure there is no issue with DEFAULT here + matchUtilities({ + divide: (modifier) => { + let value = asColor(modifier, colorPalette) + + if (value === undefined) { + return [] + } + + return { + [`${nameClass('divide', modifier)} > :not([hidden]) ~ :not([hidden])`]: withAlphaVariable({ + color: colorPalette[modifier], + property: 'border-color', + variable: '--tw-divide-opacity', + }), + } + }, + }) +} diff --git a/jit/corePlugins/divideOpacity.js b/jit/corePlugins/divideOpacity.js new file mode 100644 index 000000000000..9c2fa2495213 --- /dev/null +++ b/jit/corePlugins/divideOpacity.js @@ -0,0 +1,19 @@ +const { asValue, nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + 'divide-opacity': (modifier, { theme }) => { + let value = asValue(modifier, theme.divideOpacity) + + if (value === undefined) { + return [] + } + + return { + [`${nameClass('divide-opacity', modifier)} > :not([hidden]) ~ :not([hidden])`]: { + '--tw-divide-opacity': value, + }, + } + }, + }) +} diff --git a/jit/corePlugins/divideStyle.js b/jit/corePlugins/divideStyle.js new file mode 100644 index 000000000000..ece35bc0f9b3 --- /dev/null +++ b/jit/corePlugins/divideStyle.js @@ -0,0 +1,19 @@ +module.exports = function ({ addUtilities }) { + addUtilities({ + '.divide-solid > :not([hidden]) ~ :not([hidden])': { + 'border-style': 'solid', + }, + '.divide-dashed > :not([hidden]) ~ :not([hidden])': { + 'border-style': 'dashed', + }, + '.divide-dotted > :not([hidden]) ~ :not([hidden])': { + 'border-style': 'dotted', + }, + '.divide-double > :not([hidden]) ~ :not([hidden])': { + 'border-style': 'double', + }, + '.divide-none > :not([hidden]) ~ :not([hidden])': { + 'border-style': 'none', + }, + }) +} diff --git a/jit/corePlugins/divideWidth.js b/jit/corePlugins/divideWidth.js new file mode 100644 index 000000000000..d572ddb2f81b --- /dev/null +++ b/jit/corePlugins/divideWidth.js @@ -0,0 +1,49 @@ +const { asLength, nameClass } = require('../pluginUtils') + +module.exports = function ({ addUtilities, matchUtilities }) { + matchUtilities({ + 'divide-x': (modifier, { theme }) => { + let value = asLength(modifier, theme['divideWidth']) + + if (value === undefined) { + return [] + } + + value = value === '0' ? '0px' : value + + return { + [`${nameClass('divide-x', modifier)} > :not([hidden]) ~ :not([hidden])`]: { + '--tw-divide-x-reverse': '0', + 'border-right-width': `calc(${value} * var(--tw-divide-x-reverse))`, + 'border-left-width': `calc(${value} * calc(1 - var(--tw-divide-x-reverse)))`, + }, + } + }, + 'divide-y': (modifier, { theme }) => { + let value = asLength(modifier, theme['divideWidth']) + + if (value === undefined) { + return [] + } + + value = value === '0' ? '0px' : value + + return { + [`${nameClass('divide-y', modifier)} > :not([hidden]) ~ :not([hidden])`]: { + '--tw-divide-y-reverse': '0', + 'border-top-width': `calc(${value} * calc(1 - var(--tw-divide-y-reverse)))`, + 'border-bottom-width': `calc(${value} * var(--tw-divide-y-reverse))`, + }, + } + }, + }) + + addUtilities({ + '.divide-y-reverse > :not([hidden]) ~ :not([hidden])': { + '--tw-divide-y-reverse': '1', + }, + '.divide-x-reverse > :not([hidden]) ~ :not([hidden])': { + '--tw-divide-x-reverse': '1', + }, + }) +} diff --git a/jit/corePlugins/fill.js b/jit/corePlugins/fill.js new file mode 100644 index 000000000000..4d1df2c08c72 --- /dev/null +++ b/jit/corePlugins/fill.js @@ -0,0 +1,19 @@ +const flattenColorPalette = require('../../lib/util/flattenColorPalette').default +const toColorValue = require('../../lib/util/toColorValue').default +const { asColor, nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities, theme }) { + let colorPalette = flattenColorPalette(theme('fill')) + + matchUtilities({ + fill: (modifier) => { + let value = asColor(modifier, colorPalette) + + if (value === undefined) { + return [] + } + + return { [nameClass('fill', modifier)]: { fill: toColorValue(value) } } + }, + }) +} diff --git a/jit/corePlugins/flex.js b/jit/corePlugins/flex.js new file mode 100644 index 000000000000..0567bd9172d8 --- /dev/null +++ b/jit/corePlugins/flex.js @@ -0,0 +1,15 @@ +const { nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + flex: (modifier, { theme }) => { + let value = theme.flex[modifier] + + if (value === undefined) { + return [] + } + + return { [nameClass('flex', modifier)]: { flex: theme.flex[modifier] } } + }, + }) +} diff --git a/jit/corePlugins/flexDirection.js b/jit/corePlugins/flexDirection.js new file mode 100644 index 000000000000..620ee123106c --- /dev/null +++ b/jit/corePlugins/flexDirection.js @@ -0,0 +1,16 @@ +const { createSimpleStaticUtilityPlugin } = require('../pluginUtils') + +module.exports = createSimpleStaticUtilityPlugin({ + '.flex-row': { + 'flex-direction': 'row', + }, + '.flex-row-reverse': { + 'flex-direction': 'row-reverse', + }, + '.flex-col': { + 'flex-direction': 'column', + }, + '.flex-col-reverse': { + 'flex-direction': 'column-reverse', + }, +}) diff --git a/jit/corePlugins/flexGrow.js b/jit/corePlugins/flexGrow.js new file mode 100644 index 000000000000..b02b0f7e10f1 --- /dev/null +++ b/jit/corePlugins/flexGrow.js @@ -0,0 +1,15 @@ +const { asValue, nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + 'flex-grow': (modifier, { theme }) => { + let value = asValue(modifier, theme.flexGrow) + + if (value === undefined) { + return [] + } + + return { [nameClass('flex-grow', modifier)]: { 'flex-grow': value } } + }, + }) +} diff --git a/jit/corePlugins/flexShrink.js b/jit/corePlugins/flexShrink.js new file mode 100644 index 000000000000..c789785de9d1 --- /dev/null +++ b/jit/corePlugins/flexShrink.js @@ -0,0 +1,15 @@ +const { asValue, nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + 'flex-shrink': (modifier, { theme }) => { + let value = asValue(modifier, theme.flexShrink) + + if (value === undefined) { + return [] + } + + return { [nameClass('flex-shrink', modifier)]: { 'flex-shrink': value } } + }, + }) +} diff --git a/jit/corePlugins/flexWrap.js b/jit/corePlugins/flexWrap.js new file mode 100644 index 000000000000..b9ac5a7cbdff --- /dev/null +++ b/jit/corePlugins/flexWrap.js @@ -0,0 +1,13 @@ +const { createSimpleStaticUtilityPlugin } = require('../pluginUtils') + +module.exports = createSimpleStaticUtilityPlugin({ + '.flex-wrap': { + 'flex-wrap': 'wrap', + }, + '.flex-wrap-reverse': { + 'flex-wrap': 'wrap-reverse', + }, + '.flex-nowrap': { + 'flex-wrap': 'nowrap', + }, +}) diff --git a/jit/corePlugins/float.js b/jit/corePlugins/float.js new file mode 100644 index 000000000000..c58164fdeacd --- /dev/null +++ b/jit/corePlugins/float.js @@ -0,0 +1,7 @@ +const { createSimpleStaticUtilityPlugin } = require('../pluginUtils') + +module.exports = createSimpleStaticUtilityPlugin({ + '.float-right': { float: 'right' }, + '.float-left': { float: 'left' }, + '.float-none': { float: 'none' }, +}) diff --git a/jit/corePlugins/fontFamily.js b/jit/corePlugins/fontFamily.js new file mode 100644 index 000000000000..2d7607851cc1 --- /dev/null +++ b/jit/corePlugins/fontFamily.js @@ -0,0 +1,18 @@ +const { nameClass } = require('../pluginUtils') +const transformThemeValue = require('../../lib/util/transformThemeValue').default + +module.exports = function ({ matchUtilities }) { + let transformValue = transformThemeValue('fontFamily') + + matchUtilities({ + font: (modifier, { theme }) => { + let value = transformValue(theme.fontFamily[modifier]) + + if (modifier === '' || value === undefined) { + return [] + } + + return { [nameClass('font', modifier)]: { 'font-family': transformValue(value) } } + }, + }) +} diff --git a/jit/corePlugins/fontSize.js b/jit/corePlugins/fontSize.js new file mode 100644 index 000000000000..e20e4663cb68 --- /dev/null +++ b/jit/corePlugins/fontSize.js @@ -0,0 +1,45 @@ +const { asLength, nameClass } = require('../pluginUtils') +const { isPlainObject } = require('../lib/utils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + text: (modifier, { theme }) => { + let value = theme.fontSize[modifier] + + if (value === undefined) { + value = asLength(modifier, {}) + + return value === undefined + ? [] + : { + [nameClass('text', modifier)]: { + 'font-size': value, + }, + } + } + + let [fontSize, options] = Array.isArray(value) ? value : [value] + let { lineHeight, letterSpacing } = isPlainObject(options) + ? options + : { + lineHeight: options, + } + + return { + [nameClass('text', modifier)]: { + 'font-size': fontSize, + ...(lineHeight === undefined + ? {} + : { + 'line-height': lineHeight, + }), + ...(letterSpacing === undefined + ? {} + : { + 'letter-spacing': letterSpacing, + }), + }, + } + }, + }) +} diff --git a/jit/corePlugins/fontSmoothing.js b/jit/corePlugins/fontSmoothing.js new file mode 100644 index 000000000000..3ee692fdb38a --- /dev/null +++ b/jit/corePlugins/fontSmoothing.js @@ -0,0 +1,12 @@ +const { createSimpleStaticUtilityPlugin } = require('../pluginUtils') + +module.exports = createSimpleStaticUtilityPlugin({ + '.antialiased': { + '-webkit-font-smoothing': 'antialiased', + '-moz-osx-font-smoothing': 'grayscale', + }, + '.subpixel-antialiased': { + '-webkit-font-smoothing': 'auto', + '-moz-osx-font-smoothing': 'auto', + }, +}) diff --git a/jit/corePlugins/fontStyle.js b/jit/corePlugins/fontStyle.js new file mode 100644 index 000000000000..d8cd51483ddc --- /dev/null +++ b/jit/corePlugins/fontStyle.js @@ -0,0 +1,6 @@ +const { createSimpleStaticUtilityPlugin } = require('../pluginUtils') + +module.exports = createSimpleStaticUtilityPlugin({ + '.italic': { 'font-style': 'italic' }, + '.not-italic': { 'font-style': 'normal' }, +}) diff --git a/jit/corePlugins/fontVariantNumeric.js b/jit/corePlugins/fontVariantNumeric.js new file mode 100644 index 000000000000..727f27cf0a68 --- /dev/null +++ b/jit/corePlugins/fontVariantNumeric.js @@ -0,0 +1,39 @@ +let fontVariantBaseStyles = { + '.ordinal, .slashed-zero, .lining-nums, .oldstyle-nums, .proportional-nums, .tabular-nums, .diagonal-fractions, .stacked-fractions': { + '--tw-ordinal': 'var(--tw-empty,/*!*/ /*!*/)', + '--tw-slashed-zero': 'var(--tw-empty,/*!*/ /*!*/)', + '--tw-numeric-figure': 'var(--tw-empty,/*!*/ /*!*/)', + '--tw-numeric-spacing': 'var(--tw-empty,/*!*/ /*!*/)', + '--tw-numeric-fraction': 'var(--tw-empty,/*!*/ /*!*/)', + 'font-variant-numeric': + 'var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)', + }, +} + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + 'normal-nums': fontVariantBaseStyles, + ordinal: fontVariantBaseStyles, + 'slashed-zero': fontVariantBaseStyles, + 'lining-nums': fontVariantBaseStyles, + 'oldstyle-nums': fontVariantBaseStyles, + 'proportional-nums': fontVariantBaseStyles, + 'tabular-nums': fontVariantBaseStyles, + 'diagonal-fractions': fontVariantBaseStyles, + 'stacked-fractions': fontVariantBaseStyles, + }) + + matchUtilities({ + 'normal-nums': { '.normal-nums': { 'font-variant-numeric': 'normal' } }, + ordinal: { '.ordinal': { '--tw-ordinal': 'ordinal' } }, + 'slashed-zero': { '.slashed-zero': { '--tw-slashed-zero': 'slashed-zero' } }, + 'lining-nums': { '.lining-nums': { '--tw-numeric-figure': 'lining-nums' } }, + 'oldstyle-nums': { '.oldstyle-nums': { '--tw-numeric-figure': 'oldstyle-nums' } }, + 'proportional-nums': { '.proportional-nums': { '--tw-numeric-spacing': 'proportional-nums' } }, + 'tabular-nums': { '.tabular-nums': { '--tw-numeric-spacing': 'tabular-nums' } }, + 'diagonal-fractions': { + '.diagonal-fractions': { '--tw-numeric-fraction': 'diagonal-fractions' }, + }, + 'stacked-fractions': { '.stacked-fractions': { '--tw-numeric-fraction': 'stacked-fractions' } }, + }) +} diff --git a/jit/corePlugins/fontWeight.js b/jit/corePlugins/fontWeight.js new file mode 100644 index 000000000000..3365d46f3ec8 --- /dev/null +++ b/jit/corePlugins/fontWeight.js @@ -0,0 +1,14 @@ +const { nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + font: (modifier, { theme }) => { + let value = theme.fontWeight[modifier] + if (value === undefined) { + return [] + } + + return { [nameClass('font', modifier)]: { 'font-weight': value } } + }, + }) +} diff --git a/jit/corePlugins/gap.js b/jit/corePlugins/gap.js new file mode 100644 index 000000000000..5519cd76a4a8 --- /dev/null +++ b/jit/corePlugins/gap.js @@ -0,0 +1,35 @@ +const { asLength, nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + gap: (modifier, { theme }) => { + let value = asLength(modifier, theme['gap']) + + if (value === undefined) { + return [] + } + + return { [nameClass('gap', modifier)]: { gap: value } } + }, + }) + matchUtilities({ + 'gap-x': (modifier, { theme }) => { + let value = asLength(modifier, theme['gap']) + + if (value === undefined) { + return [] + } + + return { [nameClass('gap-x', modifier)]: { 'column-gap': value } } + }, + 'gap-y': (modifier, { theme }) => { + let value = asLength(modifier, theme['gap']) + + if (value === undefined) { + return [] + } + + return { [nameClass('gap-y', modifier)]: { 'row-gap': value } } + }, + }) +} diff --git a/jit/corePlugins/gradientColorStops.js b/jit/corePlugins/gradientColorStops.js new file mode 100644 index 000000000000..a1497ae285eb --- /dev/null +++ b/jit/corePlugins/gradientColorStops.js @@ -0,0 +1,75 @@ +const flattenColorPalette = require('../../lib/util/flattenColorPalette').default +const toColorValue = require('../../lib/util/toColorValue').default +const toRgba = require('../../lib/util/withAlphaVariable').toRgba +const { asColor, nameClass } = require('../pluginUtils') + +function transparentTo(value) { + if (typeof value === 'function') { + return value({ opacityValue: 0 }) + } + + try { + const [r, g, b] = toRgba(value) + return `rgba(${r}, ${g}, ${b}, 0)` + } catch (_error) { + return `rgba(255, 255, 255, 0)` + } +} + +module.exports = function ({ matchUtilities, theme }) { + let colorPalette = flattenColorPalette(theme('gradientColorStops')) + + matchUtilities({ + from: (modifier) => { + let value = asColor(modifier, colorPalette) + + if (value === undefined) { + return [] + } + + let transparentToValue = transparentTo(value) + + return { + [nameClass('from', modifier)]: { + '--tw-gradient-from': toColorValue(value, 'from'), + '--tw-gradient-stops': `var(--tw-gradient-from), var(--tw-gradient-to, ${transparentToValue})`, + }, + } + }, + }) + matchUtilities({ + via: (modifier) => { + let value = asColor(modifier, colorPalette) + + if (value === undefined) { + return [] + } + + let transparentToValue = transparentTo(value) + + return { + [nameClass('via', modifier)]: { + '--tw-gradient-stops': `var(--tw-gradient-from), ${toColorValue( + value, + 'via' + )}, var(--tw-gradient-to, ${transparentToValue})`, + }, + } + }, + }) + matchUtilities({ + to: (modifier) => { + let value = asColor(modifier, colorPalette) + + if (value === undefined) { + return [] + } + + return { + [nameClass('to', modifier)]: { + '--tw-gradient-to': toColorValue(value, 'to'), + }, + } + }, + }) +} diff --git a/jit/corePlugins/gridAutoColumns.js b/jit/corePlugins/gridAutoColumns.js new file mode 100644 index 000000000000..90a970b94203 --- /dev/null +++ b/jit/corePlugins/gridAutoColumns.js @@ -0,0 +1,15 @@ +const { asList, nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + 'auto-cols': (modifier, { theme }) => { + let value = asList(modifier, theme.gridAutoColumns) + + if (value === undefined) { + return [] + } + + return { [nameClass('auto-cols', modifier)]: { 'grid-auto-columns': value } } + }, + }) +} diff --git a/jit/corePlugins/gridAutoFlow.js b/jit/corePlugins/gridAutoFlow.js new file mode 100644 index 000000000000..42d3366856cb --- /dev/null +++ b/jit/corePlugins/gridAutoFlow.js @@ -0,0 +1,8 @@ +const { createSimpleStaticUtilityPlugin } = require('../pluginUtils') + +module.exports = createSimpleStaticUtilityPlugin({ + '.grid-flow-row': { gridAutoFlow: 'row' }, + '.grid-flow-col': { gridAutoFlow: 'column' }, + '.grid-flow-row-dense': { gridAutoFlow: 'row dense' }, + '.grid-flow-col-dense': { gridAutoFlow: 'column dense' }, +}) diff --git a/jit/corePlugins/gridAutoRows.js b/jit/corePlugins/gridAutoRows.js new file mode 100644 index 000000000000..0e40b6698b91 --- /dev/null +++ b/jit/corePlugins/gridAutoRows.js @@ -0,0 +1,15 @@ +const { asList, nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + 'auto-rows': (modifier, { theme }) => { + let value = asList(modifier, theme.gridAutoRows) + + if (value === undefined) { + return [] + } + + return { [nameClass('auto-rows', modifier)]: { 'grid-auto-rows': value } } + }, + }) +} diff --git a/jit/corePlugins/gridColumn.js b/jit/corePlugins/gridColumn.js new file mode 100644 index 000000000000..241615bead4e --- /dev/null +++ b/jit/corePlugins/gridColumn.js @@ -0,0 +1,13 @@ +const { nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + col: (modifier, { theme }) => { + if (modifier === '' || theme.gridColumn[modifier] === undefined) { + return [] + } + + return { [nameClass('col', modifier)]: { 'grid-column': theme.gridColumn[modifier] } } + }, + }) +} diff --git a/jit/corePlugins/gridColumnEnd.js b/jit/corePlugins/gridColumnEnd.js new file mode 100644 index 000000000000..53c7543c43e4 --- /dev/null +++ b/jit/corePlugins/gridColumnEnd.js @@ -0,0 +1,15 @@ +const { nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + 'col-end': (modifier, { theme }) => { + let value = theme.gridColumnEnd[modifier] + + if (value === undefined) { + return [] + } + + return { [nameClass('col-end', modifier)]: { 'grid-column-end': value } } + }, + }) +} diff --git a/jit/corePlugins/gridColumnStart.js b/jit/corePlugins/gridColumnStart.js new file mode 100644 index 000000000000..e9adf8474073 --- /dev/null +++ b/jit/corePlugins/gridColumnStart.js @@ -0,0 +1,15 @@ +const { nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + 'col-start': (modifier, { theme }) => { + let value = theme.gridColumnStart[modifier] + + if (value === undefined) { + return [] + } + + return { [nameClass('col-start', modifier)]: { 'grid-column-start': value } } + }, + }) +} diff --git a/jit/corePlugins/gridRow.js b/jit/corePlugins/gridRow.js new file mode 100644 index 000000000000..2e560c3fadb7 --- /dev/null +++ b/jit/corePlugins/gridRow.js @@ -0,0 +1,15 @@ +const { nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + row: (modifier, { theme }) => { + let value = theme.gridRow[modifier] + + if (value === undefined) { + return [] + } + + return { [nameClass('row', modifier)]: { 'grid-row': value } } + }, + }) +} diff --git a/jit/corePlugins/gridRowEnd.js b/jit/corePlugins/gridRowEnd.js new file mode 100644 index 000000000000..64d6e56f1c18 --- /dev/null +++ b/jit/corePlugins/gridRowEnd.js @@ -0,0 +1,15 @@ +const { nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + 'row-end': (modifier, { theme }) => { + let value = theme.gridRowEnd[modifier] + + if (value === undefined) { + return [] + } + + return { [nameClass('row-end', modifier)]: { 'grid-row-end': value } } + }, + }) +} diff --git a/jit/corePlugins/gridRowStart.js b/jit/corePlugins/gridRowStart.js new file mode 100644 index 000000000000..683918ed3360 --- /dev/null +++ b/jit/corePlugins/gridRowStart.js @@ -0,0 +1,15 @@ +const { nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + 'row-start': (modifier, { theme }) => { + let value = theme.gridRowStart[modifier] + + if (value === undefined) { + return [] + } + + return { [nameClass('row-start', modifier)]: { 'grid-row-start': value } } + }, + }) +} diff --git a/jit/corePlugins/gridTemplateColumns.js b/jit/corePlugins/gridTemplateColumns.js new file mode 100644 index 000000000000..745b5ba7b37f --- /dev/null +++ b/jit/corePlugins/gridTemplateColumns.js @@ -0,0 +1,15 @@ +const { asList, nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + 'grid-cols': (modifier, { theme }) => { + let value = asList(modifier, theme.gridTemplateColumns) + + if (value === undefined) { + return [] + } + + return { [nameClass('grid-cols', modifier)]: { 'grid-template-columns': value } } + }, + }) +} diff --git a/jit/corePlugins/gridTemplateRows.js b/jit/corePlugins/gridTemplateRows.js new file mode 100644 index 000000000000..bff4eb3cd716 --- /dev/null +++ b/jit/corePlugins/gridTemplateRows.js @@ -0,0 +1,15 @@ +const { asList, nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + 'grid-rows': (modifier, { theme }) => { + let value = asList(modifier, theme.gridTemplateRows) + + if (value === undefined) { + return [] + } + + return { [nameClass('grid-rows', modifier)]: { 'grid-template-rows': value } } + }, + }) +} diff --git a/jit/corePlugins/height.js b/jit/corePlugins/height.js new file mode 100644 index 000000000000..83b594e1d0a8 --- /dev/null +++ b/jit/corePlugins/height.js @@ -0,0 +1,15 @@ +const { asValue, nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + h: (modifier, { theme }) => { + let value = asValue(modifier, theme['height']) + + if (value === undefined) { + return [] + } + + return { [nameClass('h', modifier)]: { height: value } } + }, + }) +} diff --git a/jit/corePlugins/index.js b/jit/corePlugins/index.js new file mode 100644 index 000000000000..3d4c95d79da6 --- /dev/null +++ b/jit/corePlugins/index.js @@ -0,0 +1,295 @@ +const postcss = require('postcss') +const buildMediaQuery = require('../../lib/util/buildMediaQuery').default +const prefixSelector = require('../../lib/util/prefixSelector').default +const { + updateLastClasses, + updateAllClasses, + transformAllSelectors, + transformAllClasses, + transformLastClasses, +} = require('../pluginUtils') + +module.exports = { + pseudoClassVariants: function ({ config, addVariant }) { + let pseudoVariants = [ + ['first', 'first-child'], + ['last', 'last-child'], + ['odd', 'nth-child(odd)'], + ['even', 'nth-child(even)'], + 'visited', + 'checked', + 'focus-within', + 'hover', + 'focus', + 'focus-visible', + 'active', + 'disabled', + ] + + for (let variant of pseudoVariants) { + let [variantName, state] = Array.isArray(variant) ? variant : [variant, variant] + + addVariant( + variantName, + transformAllClasses((className, { withPseudo }) => { + return withPseudo(`${variantName}${config('separator')}${className}`, state) + }) + ) + } + + for (let variant of pseudoVariants) { + let [variantName, state] = Array.isArray(variant) ? variant : [variant, variant] + let groupVariantName = `group-${variantName}` + + addVariant( + groupVariantName, + transformAllSelectors((selector) => { + let variantSelector = updateAllClasses(selector, (className) => { + return `${groupVariantName}${config('separator')}${className}` + }) + + if (variantSelector === selector) { + return null + } + + let groupSelector = prefixSelector( + config('prefix'), + `.group${config('separator')}${state}` + ) + + return `${groupSelector} ${variantSelector}` + }) + ) + } + }, + directionVariants: function ({ config, addVariant }) { + addVariant( + 'ltr', + transformAllSelectors( + (selector) => + `[dir="ltr"] ${updateAllClasses( + selector, + (className) => `ltr${config('separator')}${className}` + )}` + ) + ) + + addVariant( + 'rtl', + transformAllSelectors( + (selector) => + `[dir="rtl"] ${updateAllClasses( + selector, + (className) => `rtl${config('separator')}${className}` + )}` + ) + ) + }, + reducedMotionVariants: function ({ config, addVariant }) { + addVariant( + 'motion-safe', + transformLastClasses( + (className) => { + return `motion-safe${config('separator')}${className}` + }, + () => postcss.atRule({ name: 'media', params: '(prefers-reduced-motion: no-preference)' }) + ) + ) + + addVariant( + 'motion-reduce', + transformLastClasses( + (className) => { + return `motion-reduce${config('separator')}${className}` + }, + () => postcss.atRule({ name: 'media', params: '(prefers-reduced-motion: reduce)' }) + ) + ) + }, + darkVariants: function ({ config, addVariant }) { + if (config('darkMode') === 'class') { + addVariant( + 'dark', + transformAllSelectors((selector) => { + let variantSelector = updateLastClasses(selector, (className) => { + return `dark${config('separator')}${className}` + }) + + if (variantSelector === selector) { + return null + } + + let darkSelector = prefixSelector(config('prefix'), `.dark`) + + return `${darkSelector} ${variantSelector}` + }) + ) + } else if (config('darkMode') === 'media') { + addVariant( + 'dark', + transformLastClasses( + (className) => { + return `dark${config('separator')}${className}` + }, + () => postcss.atRule({ name: 'media', params: '(prefers-color-scheme: dark)' }) + ) + ) + } + }, + screenVariants: function ({ config, theme, addVariant }) { + for (let screen in theme('screens')) { + let size = theme('screens')[screen] + let query = buildMediaQuery(size) + + addVariant( + screen, + transformLastClasses( + (className) => { + return `${screen}${config('separator')}${className}` + }, + () => postcss.atRule({ name: 'media', params: query }) + ) + ) + } + }, + + // Base + preflight: require('./preflight'), + + // Components + container: require('./container'), + + // Utilitiles + accessibility: require('./accessibility'), + pointerEvents: require('./pointerEvents'), + visibility: require('./visibility'), + position: require('./position'), + inset: require('./inset'), + zIndex: require('./zIndex'), + order: require('./order'), + gridColumn: require('./gridColumn'), + gridColumnEnd: require('./gridColumnEnd'), + gridColumnStart: require('./gridColumnStart'), + gridRow: require('./gridRow'), + gridRowEnd: require('./gridRowEnd'), + gridRowStart: require('./gridRowStart'), + float: require('./float'), + clear: require('./clear'), + margin: require('./margin'), + boxSizing: require('./boxSizing'), + display: require('./display'), + height: require('./height'), + maxHeight: require('./maxHeight'), + minHeight: require('./minHeight'), + width: require('./width'), + minWidth: require('./minWidth'), + maxWidth: require('./maxWidth'), + flex: require('./flex'), + flexShrink: require('./flexShrink'), + flexGrow: require('./flexGrow'), + tableLayout: require('./tableLayout'), + borderCollapse: require('./borderCollapse'), + + transform: require('./transform'), + transformOrigin: require('./transformOrigin'), + translate: require('./translate'), + rotate: require('./rotate'), + skew: require('./skew'), + scale: require('./scale'), + + animation: require('./animation'), + + cursor: require('./cursor'), + userSelect: require('./userSelect'), + resize: require('./resize'), + + listStylePosition: require('./listStylePosition'), + listStyleType: require('./listStyleType'), + + appearance: require('./appearance'), + gridAutoColumns: require('./gridAutoColumns'), + gridAutoFlow: require('./gridAutoFlow'), + gridAutoRows: require('./gridAutoRows'), + gridTemplateColumns: require('./gridTemplateColumns'), + gridTemplateRows: require('./gridTemplateRows'), + flexDirection: require('./flexDirection'), + flexWrap: require('./flexWrap'), + placeContent: require('./placeContent'), + placeItems: require('./placeItems'), + alignContent: require('./alignContent'), + alignItems: require('./alignItems'), + justifyContent: require('./justifyContent'), + justifyItems: require('./justifyItems'), + gap: require('./gap'), + space: require('./space'), + divideWidth: require('./divideWidth'), + divideStyle: require('./divideStyle'), + divideColor: require('./divideColor'), + divideOpacity: require('./divideOpacity'), + + placeSelf: require('./placeSelf'), + alignSelf: require('./alignSelf'), + justifySelf: require('./justifySelf'), + + overflow: require('./overflow'), + overscrollBehavior: require('./overscrollBehavior'), + textOverflow: require('./textOverflow'), + whitespace: require('./whitespace'), + wordBreak: require('./wordBreak'), + + borderRadius: require('./borderRadius'), + borderWidth: require('./borderWidth'), + borderStyle: require('./borderStyle'), + borderColor: require('./borderColor'), + borderOpacity: require('./borderOpacity'), + + backgroundColor: require('./backgroundColor'), + backgroundOpacity: require('./backgroundOpacity'), + backgroundImage: require('./backgroundImage'), + gradientColorStops: require('./gradientColorStops'), + backgroundSize: require('./backgroundSize'), + backgroundAttachment: require('./backgroundAttachment'), + backgroundClip: require('./backgroundClip'), + backgroundPosition: require('./backgroundPosition'), + backgroundRepeat: require('./backgroundRepeat'), + + fill: require('./fill'), + stroke: require('./stroke'), + strokeWidth: require('./strokeWidth'), + + objectFit: require('./objectFit'), + objectPosition: require('./objectPosition'), + + padding: require('./padding'), + + textAlign: require('./textAlign'), + verticalAlign: require('./verticalAlign'), + fontFamily: require('./fontFamily'), + fontSize: require('./fontSize'), + fontWeight: require('./fontWeight'), + textTransform: require('./textTransform'), + fontStyle: require('./fontStyle'), + fontVariantNumeric: require('./fontVariantNumeric'), + lineHeight: require('./lineHeight'), + letterSpacing: require('./letterSpacing'), + textColor: require('./textColor'), + textOpacity: require('./textOpacity'), + textDecoration: require('./textDecoration'), + fontSmoothing: require('./fontSmoothing'), + placeholderColor: require('./placeholderColor'), + placeholderOpacity: require('./placeholderOpacity'), + + opacity: require('./opacity'), + boxShadow: require('./boxShadow'), + outline: require('./outline'), + ringWidth: require('./ringWidth'), + ringColor: require('./ringColor'), + ringOpacity: require('./ringOpacity'), + ringOffsetWidth: require('./ringOffsetWidth'), + ringOffsetColor: require('./ringOffsetColor'), + + transitionProperty: require('./transitionProperty'), + transitionDelay: require('./transitionDelay'), + transitionDuration: require('./transitionDuration'), + transitionTimingFunction: require('./transitionTimingFunction'), +} diff --git a/jit/corePlugins/inset.js b/jit/corePlugins/inset.js new file mode 100644 index 000000000000..31be50069870 --- /dev/null +++ b/jit/corePlugins/inset.js @@ -0,0 +1,75 @@ +const { asValue, nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + inset: (modifier, { theme }) => { + let value = asValue(modifier, theme['inset']) + + if (value === undefined) { + return [] + } + + return { + [nameClass('inset', modifier)]: { top: value, right: value, bottom: value, left: value }, + } + }, + }) + matchUtilities({ + 'inset-x': (modifier, { theme }) => { + let value = asValue(modifier, theme['inset']) + + if (value === undefined) { + return [] + } + + return { [nameClass('inset-x', modifier)]: { left: value, right: value } } + }, + 'inset-y': (modifier, { theme }) => { + let value = asValue(modifier, theme['inset']) + + if (value === undefined) { + return [] + } + + return { [nameClass('inset-y', modifier)]: { top: value, bottom: value } } + }, + }) + matchUtilities({ + top: (modifier, { theme }) => { + let value = asValue(modifier, theme['inset']) + + if (value === undefined) { + return [] + } + + return { [nameClass('top', modifier)]: { top: value } } + }, + right: (modifier, { theme }) => { + let value = asValue(modifier, theme['inset']) + + if (value === undefined) { + return [] + } + + return { [nameClass('right', modifier)]: { right: value } } + }, + bottom: (modifier, { theme }) => { + let value = asValue(modifier, theme['inset']) + + if (value === undefined) { + return [] + } + + return { [nameClass('bottom', modifier)]: { bottom: value } } + }, + left: (modifier, { theme }) => { + let value = asValue(modifier, theme['inset']) + + if (value === undefined) { + return [] + } + + return { [nameClass('left', modifier)]: { left: value } } + }, + }) +} diff --git a/jit/corePlugins/justifyContent.js b/jit/corePlugins/justifyContent.js new file mode 100644 index 000000000000..5914e79f3f5d --- /dev/null +++ b/jit/corePlugins/justifyContent.js @@ -0,0 +1,22 @@ +const { createSimpleStaticUtilityPlugin } = require('../pluginUtils') + +module.exports = createSimpleStaticUtilityPlugin({ + '.justify-start': { + 'justify-content': 'flex-start', + }, + '.justify-end': { + 'justify-content': 'flex-end', + }, + '.justify-center': { + 'justify-content': 'center', + }, + '.justify-between': { + 'justify-content': 'space-between', + }, + '.justify-around': { + 'justify-content': 'space-around', + }, + '.justify-evenly': { + 'justify-content': 'space-evenly', + }, +}) diff --git a/jit/corePlugins/justifyItems.js b/jit/corePlugins/justifyItems.js new file mode 100644 index 000000000000..fddc1c8b64fa --- /dev/null +++ b/jit/corePlugins/justifyItems.js @@ -0,0 +1,19 @@ +const { createSimpleStaticUtilityPlugin } = require('../pluginUtils') + +module.exports = createSimpleStaticUtilityPlugin({ + '.justify-items-auto': { + 'justify-items': 'auto', + }, + '.justify-items-start': { + 'justify-items': 'start', + }, + '.justify-items-end': { + 'justify-items': 'end', + }, + '.justify-items-center': { + 'justify-items': 'center', + }, + '.justify-items-stretch': { + 'justify-items': 'stretch', + }, +}) diff --git a/jit/corePlugins/justifySelf.js b/jit/corePlugins/justifySelf.js new file mode 100644 index 000000000000..dcedf3053deb --- /dev/null +++ b/jit/corePlugins/justifySelf.js @@ -0,0 +1,19 @@ +const { createSimpleStaticUtilityPlugin } = require('../pluginUtils') + +module.exports = createSimpleStaticUtilityPlugin({ + '.justify-self-auto': { + 'justify-self': 'auto', + }, + '.justify-self-start': { + 'justify-self': 'start', + }, + '.justify-self-end': { + 'justify-self': 'end', + }, + '.justify-self-center': { + 'justify-self': 'center', + }, + '.justify-self-stretch': { + 'justify-self': 'stretch', + }, +}) diff --git a/jit/corePlugins/letterSpacing.js b/jit/corePlugins/letterSpacing.js new file mode 100644 index 000000000000..a4111e9b2105 --- /dev/null +++ b/jit/corePlugins/letterSpacing.js @@ -0,0 +1,15 @@ +const { asLength, nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + tracking: (modifier, { theme }) => { + let value = asLength(modifier, theme['letterSpacing']) + + if (value === undefined) { + return [] + } + + return { [nameClass('tracking', modifier)]: { 'letter-spacing': value } } + }, + }) +} diff --git a/jit/corePlugins/lineHeight.js b/jit/corePlugins/lineHeight.js new file mode 100644 index 000000000000..c9e9795ec576 --- /dev/null +++ b/jit/corePlugins/lineHeight.js @@ -0,0 +1,15 @@ +const { asLength, nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + leading: (modifier, { theme }) => { + let value = asLength(modifier, theme['lineHeight']) + + if (value === undefined) { + return [] + } + + return { [nameClass('leading', modifier)]: { 'line-height': value } } + }, + }) +} diff --git a/jit/corePlugins/listStylePosition.js b/jit/corePlugins/listStylePosition.js new file mode 100644 index 000000000000..43c8ee5c3f88 --- /dev/null +++ b/jit/corePlugins/listStylePosition.js @@ -0,0 +1,6 @@ +const { createSimpleStaticUtilityPlugin } = require('../pluginUtils') + +module.exports = createSimpleStaticUtilityPlugin({ + '.list-inside': { 'list-style-position': 'inside' }, + '.list-outside': { 'list-style-position': 'outside' }, +}) diff --git a/jit/corePlugins/listStyleType.js b/jit/corePlugins/listStyleType.js new file mode 100644 index 000000000000..275d2dd17f72 --- /dev/null +++ b/jit/corePlugins/listStyleType.js @@ -0,0 +1,13 @@ +const { nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + list: (modifier, { theme }) => { + if (modifier === '' || theme.listStyleType[modifier] === undefined) { + return [] + } + + return { [nameClass('list', modifier)]: { 'list-style-type': theme.listStyleType[modifier] } } + }, + }) +} diff --git a/jit/corePlugins/margin.js b/jit/corePlugins/margin.js new file mode 100644 index 000000000000..863960523230 --- /dev/null +++ b/jit/corePlugins/margin.js @@ -0,0 +1,73 @@ +const { asValue, nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + m: (modifier, { theme }) => { + let value = asValue(modifier, theme['margin']) + + if (value === undefined) { + return [] + } + + return { [nameClass('m', modifier)]: { margin: value } } + }, + }) + matchUtilities({ + mx: (modifier, { theme }) => { + let value = asValue(modifier, theme['margin']) + + if (value === undefined) { + return [] + } + + return { [nameClass('mx', modifier)]: { 'margin-left': value, 'margin-right': value } } + }, + my: (modifier, { theme }) => { + let value = asValue(modifier, theme['margin']) + + if (value === undefined) { + return [] + } + + return { [nameClass('my', modifier)]: { 'margin-top': value, 'margin-bottom': value } } + }, + }) + matchUtilities({ + mt: (modifier, { theme }) => { + let value = asValue(modifier, theme['margin']) + + if (value === undefined) { + return [] + } + + return { [nameClass('mt', modifier)]: { 'margin-top': value } } + }, + mr: (modifier, { theme }) => { + let value = asValue(modifier, theme['margin']) + + if (value === undefined) { + return [] + } + + return { [nameClass('mr', modifier)]: { 'margin-right': value } } + }, + mb: (modifier, { theme }) => { + let value = asValue(modifier, theme['margin']) + + if (value === undefined) { + return [] + } + + return { [nameClass('mb', modifier)]: { 'margin-bottom': value } } + }, + ml: (modifier, { theme }) => { + let value = asValue(modifier, theme['margin']) + + if (value === undefined) { + return [] + } + + return { [nameClass('ml', modifier)]: { 'margin-left': value } } + }, + }) +} diff --git a/jit/corePlugins/maxHeight.js b/jit/corePlugins/maxHeight.js new file mode 100644 index 000000000000..432a7ee1123e --- /dev/null +++ b/jit/corePlugins/maxHeight.js @@ -0,0 +1,15 @@ +const { asValue, nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + 'max-h': (modifier, { theme }) => { + let value = asValue(modifier, theme['maxHeight']) + + if (value === undefined) { + return [] + } + + return { [nameClass('max-h', modifier)]: { 'max-height': value } } + }, + }) +} diff --git a/jit/corePlugins/maxWidth.js b/jit/corePlugins/maxWidth.js new file mode 100644 index 000000000000..d519d667caa3 --- /dev/null +++ b/jit/corePlugins/maxWidth.js @@ -0,0 +1,15 @@ +const { asValue, nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + 'max-w': (modifier, { theme }) => { + let value = asValue(modifier, theme['maxWidth']) + + if (value === undefined) { + return [] + } + + return { [nameClass('max-w', modifier)]: { 'max-width': value } } + }, + }) +} diff --git a/jit/corePlugins/minHeight.js b/jit/corePlugins/minHeight.js new file mode 100644 index 000000000000..a78682ee3e0b --- /dev/null +++ b/jit/corePlugins/minHeight.js @@ -0,0 +1,15 @@ +const { asValue, nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + 'min-h': (modifier, { theme }) => { + let value = asValue(modifier, theme['minHeight']) + + if (value === undefined) { + return [] + } + + return { [nameClass('min-h', modifier)]: { 'min-height': value } } + }, + }) +} diff --git a/jit/corePlugins/minWidth.js b/jit/corePlugins/minWidth.js new file mode 100644 index 000000000000..3a6be87b4bb2 --- /dev/null +++ b/jit/corePlugins/minWidth.js @@ -0,0 +1,15 @@ +const { asValue, nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + 'min-w': (modifier, { theme }) => { + let value = asValue(modifier, theme['minWidth']) + + if (value === undefined) { + return [] + } + + return { [nameClass('min-w', modifier)]: { 'min-width': value } } + }, + }) +} diff --git a/jit/corePlugins/objectFit.js b/jit/corePlugins/objectFit.js new file mode 100644 index 000000000000..5d6389ad3396 --- /dev/null +++ b/jit/corePlugins/objectFit.js @@ -0,0 +1,9 @@ +const { createSimpleStaticUtilityPlugin } = require('../pluginUtils') + +module.exports = createSimpleStaticUtilityPlugin({ + '.object-contain': { 'object-fit': 'contain' }, + '.object-cover': { 'object-fit': 'cover' }, + '.object-fill': { 'object-fit': 'fill' }, + '.object-none': { 'object-fit': 'none' }, + '.object-scale-down': { 'object-fit': 'scale-down' }, +}) diff --git a/jit/corePlugins/objectPosition.js b/jit/corePlugins/objectPosition.js new file mode 100644 index 000000000000..01afca056292 --- /dev/null +++ b/jit/corePlugins/objectPosition.js @@ -0,0 +1,15 @@ +const { nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + object: (modifier, { theme }) => { + if (modifier === '' || theme.objectPosition[modifier] === undefined) { + return [] + } + + return { + [nameClass('object', modifier)]: { 'object-position': theme.objectPosition[modifier] }, + } + }, + }) +} diff --git a/jit/corePlugins/opacity.js b/jit/corePlugins/opacity.js new file mode 100644 index 000000000000..2fc8fc851c3c --- /dev/null +++ b/jit/corePlugins/opacity.js @@ -0,0 +1,15 @@ +const { asValue, nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + opacity: (modifier, { theme }) => { + let value = asValue(modifier, theme.opacity) + + if (value === undefined) { + return [] + } + + return { [nameClass('opacity', modifier)]: { opacity: value } } + }, + }) +} diff --git a/jit/corePlugins/order.js b/jit/corePlugins/order.js new file mode 100644 index 000000000000..e17cf14c9c27 --- /dev/null +++ b/jit/corePlugins/order.js @@ -0,0 +1,15 @@ +const { asValue, nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + order: (modifier, { theme }) => { + let value = asValue(modifier, theme.order) + + if (value === undefined) { + return [] + } + + return { [nameClass('order', modifier)]: { order: value } } + }, + }) +} diff --git a/jit/corePlugins/outline.js b/jit/corePlugins/outline.js new file mode 100644 index 000000000000..c19beb1a3526 --- /dev/null +++ b/jit/corePlugins/outline.js @@ -0,0 +1,22 @@ +const { nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + outline: (modifier, { theme }) => { + let value = theme.outline[modifier] + + if (value === undefined) { + return [] + } + + let [outline, outlineOffset = '0'] = Array.isArray(value) ? value : [value] + + return { + [nameClass('outline', modifier)]: { + outline, + 'outline-offset': outlineOffset, + }, + } + }, + }) +} diff --git a/jit/corePlugins/overflow.js b/jit/corePlugins/overflow.js new file mode 100644 index 000000000000..2ef1f11adbe1 --- /dev/null +++ b/jit/corePlugins/overflow.js @@ -0,0 +1,16 @@ +const { createSimpleStaticUtilityPlugin } = require('../pluginUtils') + +module.exports = createSimpleStaticUtilityPlugin({ + '.overflow-auto': { overflow: 'auto' }, + '.overflow-hidden': { overflow: 'hidden' }, + '.overflow-visible': { overflow: 'visible' }, + '.overflow-scroll': { overflow: 'scroll' }, + '.overflow-x-auto': { 'overflow-x': 'auto' }, + '.overflow-y-auto': { 'overflow-y': 'auto' }, + '.overflow-x-hidden': { 'overflow-x': 'hidden' }, + '.overflow-y-hidden': { 'overflow-y': 'hidden' }, + '.overflow-x-visible': { 'overflow-x': 'visible' }, + '.overflow-y-visible': { 'overflow-y': 'visible' }, + '.overflow-x-scroll': { 'overflow-x': 'scroll' }, + '.overflow-y-scroll': { 'overflow-y': 'scroll' }, +}) diff --git a/jit/corePlugins/overscrollBehavior.js b/jit/corePlugins/overscrollBehavior.js new file mode 100644 index 000000000000..d6baf94db4c7 --- /dev/null +++ b/jit/corePlugins/overscrollBehavior.js @@ -0,0 +1,13 @@ +const { createSimpleStaticUtilityPlugin } = require('../pluginUtils') + +module.exports = createSimpleStaticUtilityPlugin({ + '.overscroll-auto': { 'overscroll-behavior': 'auto' }, + '.overscroll-contain': { 'overscroll-behavior': 'contain' }, + '.overscroll-none': { 'overscroll-behavior': 'none' }, + '.overscroll-y-auto': { 'overscroll-behavior-y': 'auto' }, + '.overscroll-y-contain': { 'overscroll-behavior-y': 'contain' }, + '.overscroll-y-none': { 'overscroll-behavior-y': 'none' }, + '.overscroll-x-auto': { 'overscroll-behavior-x': 'auto' }, + '.overscroll-x-contain': { 'overscroll-behavior-x': 'contain' }, + '.overscroll-x-none': { 'overscroll-behavior-x': 'none' }, +}) diff --git a/jit/corePlugins/padding.js b/jit/corePlugins/padding.js new file mode 100644 index 000000000000..eaf64aea8ce9 --- /dev/null +++ b/jit/corePlugins/padding.js @@ -0,0 +1,73 @@ +const { asValue, nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + p: (modifier, { theme }) => { + let value = asValue(modifier, theme['padding']) + + if (value === undefined) { + return [] + } + + return { [nameClass('p', modifier)]: { padding: value } } + }, + }) + matchUtilities({ + px: (modifier, { theme }) => { + let value = asValue(modifier, theme['padding']) + + if (value === undefined) { + return [] + } + + return { [nameClass('px', modifier)]: { 'padding-left': value, 'padding-right': value } } + }, + py: (modifier, { theme }) => { + let value = asValue(modifier, theme['padding']) + + if (value === undefined) { + return [] + } + + return { [nameClass('py', modifier)]: { 'padding-top': value, 'padding-bottom': value } } + }, + }) + matchUtilities({ + pt: (modifier, { theme }) => { + let value = asValue(modifier, theme['padding']) + + if (value === undefined) { + return [] + } + + return { [nameClass('pt', modifier)]: { 'padding-top': value } } + }, + pr: (modifier, { theme }) => { + let value = asValue(modifier, theme['padding']) + + if (value === undefined) { + return [] + } + + return { [nameClass('pr', modifier)]: { 'padding-right': value } } + }, + pb: (modifier, { theme }) => { + let value = asValue(modifier, theme['padding']) + + if (value === undefined) { + return [] + } + + return { [nameClass('pb', modifier)]: { 'padding-bottom': value } } + }, + pl: (modifier, { theme }) => { + let value = asValue(modifier, theme['padding']) + + if (value === undefined) { + return [] + } + + return { [nameClass('pl', modifier)]: { 'padding-left': value } } + }, + }) +} diff --git a/jit/corePlugins/placeContent.js b/jit/corePlugins/placeContent.js new file mode 100644 index 000000000000..f20baa873559 --- /dev/null +++ b/jit/corePlugins/placeContent.js @@ -0,0 +1,25 @@ +const { createSimpleStaticUtilityPlugin } = require('../pluginUtils') + +module.exports = createSimpleStaticUtilityPlugin({ + '.place-content-center': { + 'place-content': 'center', + }, + '.place-content-start': { + 'place-content': 'start', + }, + '.place-content-end': { + 'place-content': 'end', + }, + '.place-content-between': { + 'place-content': 'space-between', + }, + '.place-content-around': { + 'place-content': 'space-around', + }, + '.place-content-evenly': { + 'place-content': 'space-evenly', + }, + '.place-content-stretch': { + 'place-content': 'stretch', + }, +}) diff --git a/jit/corePlugins/placeItems.js b/jit/corePlugins/placeItems.js new file mode 100644 index 000000000000..2b9e7af73483 --- /dev/null +++ b/jit/corePlugins/placeItems.js @@ -0,0 +1,19 @@ +const { createSimpleStaticUtilityPlugin } = require('../pluginUtils') + +module.exports = createSimpleStaticUtilityPlugin({ + '.place-items-auto': { + 'place-items': 'auto', + }, + '.place-items-start': { + 'place-items': 'start', + }, + '.place-items-end': { + 'place-items': 'end', + }, + '.place-items-center': { + 'place-items': 'center', + }, + '.place-items-stretch': { + 'place-items': 'stretch', + }, +}) diff --git a/jit/corePlugins/placeSelf.js b/jit/corePlugins/placeSelf.js new file mode 100644 index 000000000000..df6c6d1fb296 --- /dev/null +++ b/jit/corePlugins/placeSelf.js @@ -0,0 +1,19 @@ +const { createSimpleStaticUtilityPlugin } = require('../pluginUtils') + +module.exports = createSimpleStaticUtilityPlugin({ + '.place-self-auto': { + 'place-self': 'auto', + }, + '.place-self-start': { + 'place-self': 'start', + }, + '.place-self-end': { + 'place-self': 'end', + }, + '.place-self-center': { + 'place-self': 'center', + }, + '.place-self-stretch': { + 'place-self': 'stretch', + }, +}) diff --git a/jit/corePlugins/placeholderColor.js b/jit/corePlugins/placeholderColor.js new file mode 100644 index 000000000000..0e55f2ae37ec --- /dev/null +++ b/jit/corePlugins/placeholderColor.js @@ -0,0 +1,25 @@ +const flattenColorPalette = require('../../lib/util/flattenColorPalette').default +const withAlphaVariable = require('../../lib/util/withAlphaVariable').default +const { asColor, nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities, theme }) { + let colorPalette = flattenColorPalette(theme('placeholderColor')) + + matchUtilities({ + placeholder: (modifier) => { + let value = asColor(modifier, colorPalette) + + if (value === undefined) { + return [] + } + + return { + [`${nameClass('placeholder', modifier)}::placeholder`]: withAlphaVariable({ + color: value, + property: 'color', + variable: '--tw-placeholder-opacity', + }), + } + }, + }) +} diff --git a/jit/corePlugins/placeholderOpacity.js b/jit/corePlugins/placeholderOpacity.js new file mode 100644 index 000000000000..98614028d55e --- /dev/null +++ b/jit/corePlugins/placeholderOpacity.js @@ -0,0 +1,19 @@ +const { asValue, nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + 'placeholder-opacity': (modifier, { theme }) => { + let value = asValue(modifier, theme.placeholderOpacity) + + if (value === undefined) { + return [] + } + + return { + [`${nameClass('placeholder-opacity', modifier)}::placeholder`]: { + '--tw-placeholder-opacity': value, + }, + } + }, + }) +} diff --git a/jit/corePlugins/pointerEvents.js b/jit/corePlugins/pointerEvents.js new file mode 100644 index 000000000000..2b8fee6b0f6c --- /dev/null +++ b/jit/corePlugins/pointerEvents.js @@ -0,0 +1,6 @@ +const { createSimpleStaticUtilityPlugin } = require('../pluginUtils') + +module.exports = createSimpleStaticUtilityPlugin({ + '.pointer-events-none': { 'pointer-events': 'none' }, + '.pointer-events-auto': { 'pointer-events': 'auto' }, +}) diff --git a/jit/corePlugins/position.js b/jit/corePlugins/position.js new file mode 100644 index 000000000000..48554d4a9bc9 --- /dev/null +++ b/jit/corePlugins/position.js @@ -0,0 +1,11 @@ +const { createSimpleStaticUtilityPlugin } = require('../pluginUtils') + +module.exports = createSimpleStaticUtilityPlugin({ + '.static': { position: 'static' }, + '.fixed': { position: 'fixed' }, + '.absolute': { position: 'absolute' }, + '.relative': { position: 'relative' }, + '.sticky': { + position: 'sticky', + }, +}) diff --git a/jit/corePlugins/preflight.js b/jit/corePlugins/preflight.js new file mode 100644 index 000000000000..64de4c7dbc1d --- /dev/null +++ b/jit/corePlugins/preflight.js @@ -0,0 +1,3 @@ +const preflight = require('../../lib/plugins/preflight').default + +module.exports = preflight() diff --git a/jit/corePlugins/resize.js b/jit/corePlugins/resize.js new file mode 100644 index 000000000000..8fb65f855153 --- /dev/null +++ b/jit/corePlugins/resize.js @@ -0,0 +1,8 @@ +const { createSimpleStaticUtilityPlugin } = require('../pluginUtils') + +module.exports = createSimpleStaticUtilityPlugin({ + '.resize-none': { resize: 'none' }, + '.resize-y': { resize: 'vertical' }, + '.resize-x': { resize: 'horizontal' }, + '.resize': { resize: 'both' }, +}) diff --git a/jit/corePlugins/ringColor.js b/jit/corePlugins/ringColor.js new file mode 100644 index 000000000000..347a00794e62 --- /dev/null +++ b/jit/corePlugins/ringColor.js @@ -0,0 +1,29 @@ +const flattenColorPalette = require('../../lib/util/flattenColorPalette').default +const withAlphaVariable = require('../../lib/util/withAlphaVariable').default +const { asColor, nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities, theme }) { + let colorPalette = flattenColorPalette(theme('ringColor')) + + matchUtilities({ + ring: (modifier) => { + if (modifier === 'DEFAULT') { + return [] + } + + let value = asColor(modifier, colorPalette) + + if (value === undefined) { + return [] + } + + return { + [nameClass('ring', modifier)]: withAlphaVariable({ + color: value, + property: '--tw-ring-color', + variable: '--tw-ring-opacity', + }), + } + }, + }) +} diff --git a/jit/corePlugins/ringOffsetColor.js b/jit/corePlugins/ringOffsetColor.js new file mode 100644 index 000000000000..73b61b2f0503 --- /dev/null +++ b/jit/corePlugins/ringOffsetColor.js @@ -0,0 +1,23 @@ +const flattenColorPalette = require('../../lib/util/flattenColorPalette').default +const toColorValue = require('../../lib/util/toColorValue').default +const { asColor, nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities, theme }) { + let colorPalette = flattenColorPalette(theme('ringOffsetColor')) + + matchUtilities({ + 'ring-offset': (modifier) => { + let value = asColor(modifier, colorPalette) + + if (value === undefined) { + return [] + } + + return { + [nameClass('ring-offset', modifier)]: { + '--tw-ring-offset-color': toColorValue(value), + }, + } + }, + }) +} diff --git a/jit/corePlugins/ringOffsetWidth.js b/jit/corePlugins/ringOffsetWidth.js new file mode 100644 index 000000000000..a4a5109af315 --- /dev/null +++ b/jit/corePlugins/ringOffsetWidth.js @@ -0,0 +1,19 @@ +const { asLength, nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + 'ring-offset': (modifier, { theme }) => { + let value = asLength(modifier, theme['ringOffsetWidth']) + + if (value === undefined) { + return [] + } + + return { + [nameClass('ring-offset', modifier)]: { + '--tw-ring-offset-width': value, + }, + } + }, + }) +} diff --git a/jit/corePlugins/ringOpacity.js b/jit/corePlugins/ringOpacity.js new file mode 100644 index 000000000000..a093882fac5d --- /dev/null +++ b/jit/corePlugins/ringOpacity.js @@ -0,0 +1,19 @@ +const { asValue, nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + 'ring-opacity': (modifier, { theme }) => { + let value = asValue(modifier, theme['ringOpacity']) + + if (value === undefined) { + return [] + } + + return { + [nameClass('ring-opacity', modifier)]: { + '--tw-ring-opacity': value, + }, + } + }, + }) +} diff --git a/jit/corePlugins/ringWidth.js b/jit/corePlugins/ringWidth.js new file mode 100644 index 000000000000..1716be1c802c --- /dev/null +++ b/jit/corePlugins/ringWidth.js @@ -0,0 +1,60 @@ +const dlv = require('dlv') +const toRgba = require('../../lib/util/withAlphaVariable').toRgba +const { asLength, nameClass } = require('../pluginUtils') + +function safeCall(callback, defaultValue) { + try { + return callback() + } catch (_error) { + return defaultValue + } +} + +module.exports = function ({ addBase, matchUtilities, addUtilities, jit: { theme } }) { + let ringColorDefault = (([r, g, b]) => { + return `rgba(${r}, ${g}, ${b}, ${dlv(theme, ['ringOpacity', 'DEFAULT'], '0.5')})` + })(safeCall(() => toRgba(dlv(theme, ['ringColor', 'DEFAULT'])), ['147', '197', '253'])) + + let ringReset = { + '*': { + '--tw-ring-inset': 'var(--tw-empty,/*!*/ /*!*/)', + '--tw-ring-offset-width': dlv(theme, ['ringOffsetWidth', 'DEFAULT'], '0px'), + '--tw-ring-offset-color': dlv(theme, ['ringOffsetColor', 'DEFAULT'], '#fff'), + '--tw-ring-color': ringColorDefault, + '--tw-ring-offset-shadow': '0 0 #0000', + '--tw-ring-shadow': '0 0 #0000', + }, + } + + addBase(ringReset) + + matchUtilities({ + ring: (modifier, { theme }) => { + let value = asLength(modifier, theme['ringWidth']) + + if (value === undefined) { + return [] + } + + return [ + { + [nameClass('ring', modifier)]: { + '--tw-ring-offset-shadow': `var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)`, + '--tw-ring-shadow': `var(--tw-ring-inset) 0 0 0 calc(${value} + var(--tw-ring-offset-width)) var(--tw-ring-color)`, + 'box-shadow': [ + `var(--tw-ring-offset-shadow)`, + `var(--tw-ring-shadow)`, + `var(--tw-shadow, 0 0 #0000)`, + ].join(', '), + }, + }, + ] + }, + }) + + addUtilities({ + '.ring-inset': { + '--tw-ring-inset': 'inset', + }, + }) +} diff --git a/jit/corePlugins/rotate.js b/jit/corePlugins/rotate.js new file mode 100644 index 000000000000..170b6d825c5b --- /dev/null +++ b/jit/corePlugins/rotate.js @@ -0,0 +1,15 @@ +const { asAngle, nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + rotate: (modifier, { theme }) => { + let value = asAngle(modifier, theme.rotate) + + if (value === undefined) { + return [] + } + + return { [nameClass('rotate', modifier)]: { '--tw-rotate': value } } + }, + }) +} diff --git a/jit/corePlugins/scale.js b/jit/corePlugins/scale.js new file mode 100644 index 000000000000..9e5a1dae298e --- /dev/null +++ b/jit/corePlugins/scale.js @@ -0,0 +1,35 @@ +const { asValue, nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + scale: (modifier, { theme }) => { + let value = asValue(modifier, theme.scale) + + if (value === undefined) { + return [] + } + + return { [nameClass('scale', modifier)]: { '--tw-scale-x': value, '--tw-scale-y': value } } + }, + }) + matchUtilities({ + 'scale-x': (modifier, { theme }) => { + let value = asValue(modifier, theme.scale) + + if (value === undefined) { + return [] + } + + return { [nameClass('scale-x', modifier)]: { '--tw-scale-x': value } } + }, + 'scale-y': (modifier, { theme }) => { + let value = asValue(modifier, theme.scale) + + if (value === undefined) { + return [] + } + + return { [nameClass('scale-y', modifier)]: { '--tw-scale-y': value } } + }, + }) +} diff --git a/jit/corePlugins/skew.js b/jit/corePlugins/skew.js new file mode 100644 index 000000000000..6e53571c6b38 --- /dev/null +++ b/jit/corePlugins/skew.js @@ -0,0 +1,24 @@ +const { asAngle, nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + 'skew-x': (modifier, { theme }) => { + let value = asAngle(modifier, theme.skew) + + if (value === undefined) { + return [] + } + + return { [nameClass('skew-x', modifier)]: { '--tw-skew-x': value } } + }, + 'skew-y': (modifier, { theme }) => { + let value = asAngle(modifier, theme.skew) + + if (value === undefined) { + return [] + } + + return { [nameClass('skew-y', modifier)]: { '--tw-skew-y': value } } + }, + }) +} diff --git a/jit/corePlugins/space.js b/jit/corePlugins/space.js new file mode 100644 index 000000000000..6a33c3c90258 --- /dev/null +++ b/jit/corePlugins/space.js @@ -0,0 +1,45 @@ +const { asLength, nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities, addUtilities }) { + matchUtilities({ + 'space-x': (modifier, { theme }) => { + let value = asLength(modifier, theme['space']) + + if (value === undefined) { + return [] + } + + return { + [`${nameClass('space-x', modifier)} > :not([hidden]) ~ :not([hidden])`]: { + '--tw-space-x-reverse': '0', + 'margin-right': `calc(${value} * var(--tw-space-x-reverse))`, + 'margin-left': `calc(${value} * calc(1 - var(--tw-space-x-reverse)))`, + }, + } + }, + 'space-y': (modifier, { theme }) => { + let value = asLength(modifier, theme['space']) + + if (value === undefined) { + return [] + } + + return { + [`${nameClass('space-y', modifier)} > :not([hidden]) ~ :not([hidden])`]: { + '--tw-space-y-reverse': '0', + 'margin-top': `calc(${value} * calc(1 - var(--tw-space-y-reverse)))`, + 'margin-bottom': `calc(${value} * var(--tw-space-y-reverse))`, + }, + } + }, + }) + + addUtilities({ + '.space-y-reverse > :not([hidden]) ~ :not([hidden])': { + '--tw-space-y-reverse': '1', + }, + '.space-x-reverse > :not([hidden]) ~ :not([hidden])': { + '--tw-space-x-reverse': '1', + }, + }) +} diff --git a/jit/corePlugins/stroke.js b/jit/corePlugins/stroke.js new file mode 100644 index 000000000000..3a7d1f722a36 --- /dev/null +++ b/jit/corePlugins/stroke.js @@ -0,0 +1,19 @@ +const flattenColorPalette = require('../../lib/util/flattenColorPalette').default +const toColorValue = require('../../lib/util/toColorValue').default +const { asColor, nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities, theme }) { + let colorPalette = flattenColorPalette(theme('stroke')) + + matchUtilities({ + stroke: (modifier) => { + let value = asColor(modifier, colorPalette) + + if (value === undefined) { + return [] + } + + return { [nameClass('stroke', modifier)]: { stroke: toColorValue(value) } } + }, + }) +} diff --git a/jit/corePlugins/strokeWidth.js b/jit/corePlugins/strokeWidth.js new file mode 100644 index 000000000000..de7f68aa8d47 --- /dev/null +++ b/jit/corePlugins/strokeWidth.js @@ -0,0 +1,15 @@ +const { asLength, nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + stroke: (modifier, { theme }) => { + let value = asLength(modifier, theme['strokeWidth']) + + if (value === undefined) { + return [] + } + + return { [nameClass('stroke', modifier)]: { 'stroke-width': value } } + }, + }) +} diff --git a/jit/corePlugins/tableLayout.js b/jit/corePlugins/tableLayout.js new file mode 100644 index 000000000000..ba7e966b228b --- /dev/null +++ b/jit/corePlugins/tableLayout.js @@ -0,0 +1,6 @@ +const { createSimpleStaticUtilityPlugin } = require('../pluginUtils') + +module.exports = createSimpleStaticUtilityPlugin({ + '.table-auto': { 'table-layout': 'auto' }, + '.table-fixed': { 'table-layout': 'fixed' }, +}) diff --git a/jit/corePlugins/textAlign.js b/jit/corePlugins/textAlign.js new file mode 100644 index 000000000000..880e5d628324 --- /dev/null +++ b/jit/corePlugins/textAlign.js @@ -0,0 +1,8 @@ +const { createSimpleStaticUtilityPlugin } = require('../pluginUtils') + +module.exports = createSimpleStaticUtilityPlugin({ + '.text-left': { 'text-align': 'left' }, + '.text-center': { 'text-align': 'center' }, + '.text-right': { 'text-align': 'right' }, + '.text-justify': { 'text-align': 'justify' }, +}) diff --git a/jit/corePlugins/textColor.js b/jit/corePlugins/textColor.js new file mode 100644 index 000000000000..fb8e46306f8e --- /dev/null +++ b/jit/corePlugins/textColor.js @@ -0,0 +1,25 @@ +const flattenColorPalette = require('../../lib/util/flattenColorPalette').default +const withAlphaVariable = require('../../lib/util/withAlphaVariable').default +const { asColor, nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities, theme }) { + let colorPalette = flattenColorPalette(theme('textColor')) + + matchUtilities({ + text: (modifier) => { + let value = asColor(modifier, colorPalette) + + if (value === undefined) { + return [] + } + + return { + [nameClass('text', modifier)]: withAlphaVariable({ + color: value, + property: 'color', + variable: '--tw-text-opacity', + }), + } + }, + }) +} diff --git a/jit/corePlugins/textDecoration.js b/jit/corePlugins/textDecoration.js new file mode 100644 index 000000000000..6771e90036c3 --- /dev/null +++ b/jit/corePlugins/textDecoration.js @@ -0,0 +1,7 @@ +const { createSimpleStaticUtilityPlugin } = require('../pluginUtils') + +module.exports = createSimpleStaticUtilityPlugin({ + '.underline': { 'text-decoration': 'underline' }, + '.line-through': { 'text-decoration': 'line-through' }, + '.no-underline': { 'text-decoration': 'none' }, +}) diff --git a/jit/corePlugins/textOpacity.js b/jit/corePlugins/textOpacity.js new file mode 100644 index 000000000000..5e2836257245 --- /dev/null +++ b/jit/corePlugins/textOpacity.js @@ -0,0 +1,15 @@ +const { asValue, nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + 'text-opacity': (modifier, { theme }) => { + let value = asValue(modifier, theme.textOpacity) + + if (value === undefined) { + return [] + } + + return { [nameClass('text-opacity', modifier)]: { '--tw-text-opacity': value } } + }, + }) +} diff --git a/jit/corePlugins/textOverflow.js b/jit/corePlugins/textOverflow.js new file mode 100644 index 000000000000..e5d6e7d0a19c --- /dev/null +++ b/jit/corePlugins/textOverflow.js @@ -0,0 +1,15 @@ +const { createSimpleStaticUtilityPlugin } = require('../pluginUtils') + +module.exports = [ + createSimpleStaticUtilityPlugin({ + '.truncate': { + overflow: 'hidden', + 'text-overflow': 'ellipsis', + 'white-space': 'nowrap', + }, + }), + createSimpleStaticUtilityPlugin({ + '.overflow-ellipsis': { 'text-overflow': 'ellipsis' }, + '.overflow-clip': { 'text-overflow': 'clip' }, + }), +] diff --git a/jit/corePlugins/textTransform.js b/jit/corePlugins/textTransform.js new file mode 100644 index 000000000000..42cb1487a895 --- /dev/null +++ b/jit/corePlugins/textTransform.js @@ -0,0 +1,8 @@ +const { createSimpleStaticUtilityPlugin } = require('../pluginUtils') + +module.exports = createSimpleStaticUtilityPlugin({ + '.uppercase': { 'text-transform': 'uppercase' }, + '.lowercase': { 'text-transform': 'lowercase' }, + '.capitalize': { 'text-transform': 'capitalize' }, + '.normal-case': { 'text-transform': 'none' }, +}) diff --git a/jit/corePlugins/transform.js b/jit/corePlugins/transform.js new file mode 100644 index 000000000000..d17f0af592ba --- /dev/null +++ b/jit/corePlugins/transform.js @@ -0,0 +1,40 @@ +const { createSimpleStaticUtilityPlugin } = require('../pluginUtils') + +module.exports = createSimpleStaticUtilityPlugin({ + '.transform': { + '--tw-translate-x': '0', + '--tw-translate-y': '0', + '--tw-rotate': '0', + '--tw-skew-x': '0', + '--tw-skew-y': '0', + '--tw-scale-x': '1', + '--tw-scale-y': '1', + transform: [ + 'translateX(var(--tw-translate-x))', + 'translateY(var(--tw-translate-y))', + 'rotate(var(--tw-rotate))', + 'skewX(var(--tw-skew-x))', + 'skewY(var(--tw-skew-y))', + 'scaleX(var(--tw-scale-x))', + 'scaleY(var(--tw-scale-y))', + ].join(' '), + }, + '.transform-gpu': { + '--tw-translate-x': '0', + '--tw-translate-y': '0', + '--tw-rotate': '0', + '--tw-skew-x': '0', + '--tw-skew-y': '0', + '--tw-scale-x': '1', + '--tw-scale-y': '1', + transform: [ + 'translate3d(var(--tw-translate-x), var(--tw-translate-y), 0)', + 'rotate(var(--tw-rotate))', + 'skewX(var(--tw-skew-x))', + 'skewY(var(--tw-skew-y))', + 'scaleX(var(--tw-scale-x))', + 'scaleY(var(--tw-scale-y))', + ].join(' '), + }, + '.transform-none': { transform: 'none' }, +}) diff --git a/jit/corePlugins/transformOrigin.js b/jit/corePlugins/transformOrigin.js new file mode 100644 index 000000000000..32735553ee86 --- /dev/null +++ b/jit/corePlugins/transformOrigin.js @@ -0,0 +1,15 @@ +const { nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + origin: (modifier, { theme }) => { + let value = theme.transformOrigin[modifier] + + if (value === undefined) { + return [] + } + + return { [nameClass('origin', modifier)]: { 'transform-origin': value } } + }, + }) +} diff --git a/jit/corePlugins/transitionDelay.js b/jit/corePlugins/transitionDelay.js new file mode 100644 index 000000000000..567101f60d14 --- /dev/null +++ b/jit/corePlugins/transitionDelay.js @@ -0,0 +1,17 @@ +const { nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + delay: (modifier, { theme }) => { + let value = theme.transitionDelay[modifier] + + if (value === undefined) { + return [] + } + + return { + [nameClass('delay', modifier)]: { 'transition-delay': value }, + } + }, + }) +} diff --git a/jit/corePlugins/transitionDuration.js b/jit/corePlugins/transitionDuration.js new file mode 100644 index 000000000000..26667a9e203b --- /dev/null +++ b/jit/corePlugins/transitionDuration.js @@ -0,0 +1,15 @@ +const { nameClass, asValue } = require('../pluginUtils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + duration: (modifier, { theme }) => { + let value = asValue(modifier, theme.transitionDuration) + + if (value === undefined) { + return [] + } + + return { [nameClass('duration', modifier)]: { 'transition-duration': value } } + }, + }) +} diff --git a/jit/corePlugins/transitionProperty.js b/jit/corePlugins/transitionProperty.js new file mode 100644 index 000000000000..2f7ac7e1f973 --- /dev/null +++ b/jit/corePlugins/transitionProperty.js @@ -0,0 +1,28 @@ +const { nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities, theme }) { + let defaultTimingFunction = theme('transitionTimingFunction.DEFAULT') + let defaultDuration = theme('transitionDuration.DEFAULT') + + matchUtilities({ + transition: (modifier, { theme }) => { + let value = theme.transitionProperty[modifier] + + if (value === undefined) { + return [] + } + + return { + [nameClass('transition', modifier)]: { + 'transition-property': value, + ...(value === 'none' + ? {} + : { + 'transition-timing-function': defaultTimingFunction, + 'transition-duration': defaultDuration, + }), + }, + } + }, + }) +} diff --git a/jit/corePlugins/transitionTimingFunction.js b/jit/corePlugins/transitionTimingFunction.js new file mode 100644 index 000000000000..cdfb9955a639 --- /dev/null +++ b/jit/corePlugins/transitionTimingFunction.js @@ -0,0 +1,15 @@ +const { nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + ease: (modifier, { theme }) => { + let value = theme.transitionTimingFunction[modifier] + + if (value === undefined) { + return [] + } + + return { [nameClass('ease', modifier)]: { 'transition-timing-function': value } } + }, + }) +} diff --git a/jit/corePlugins/translate.js b/jit/corePlugins/translate.js new file mode 100644 index 000000000000..c81fbf944f37 --- /dev/null +++ b/jit/corePlugins/translate.js @@ -0,0 +1,24 @@ +const { asLength, nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + 'translate-x': (modifier, { theme }) => { + let value = asLength(modifier, theme['translate']) + + if (value === undefined) { + return [] + } + + return { [nameClass('translate-x', modifier)]: { '--tw-translate-x': value } } + }, + 'translate-y': (modifier, { theme }) => { + let value = asLength(modifier, theme['translate']) + + if (value === undefined) { + return [] + } + + return { [nameClass('translate-y', modifier)]: { '--tw-translate-y': value } } + }, + }) +} diff --git a/jit/corePlugins/userSelect.js b/jit/corePlugins/userSelect.js new file mode 100644 index 000000000000..895700801649 --- /dev/null +++ b/jit/corePlugins/userSelect.js @@ -0,0 +1,8 @@ +const { createSimpleStaticUtilityPlugin } = require('../pluginUtils') + +module.exports = createSimpleStaticUtilityPlugin({ + '.select-none': { 'user-select': 'none' }, + '.select-text': { 'user-select': 'text' }, + '.select-all': { 'user-select': 'all' }, + '.select-auto': { 'user-select': 'auto' }, +}) diff --git a/jit/corePlugins/verticalAlign.js b/jit/corePlugins/verticalAlign.js new file mode 100644 index 000000000000..0a49d19f7878 --- /dev/null +++ b/jit/corePlugins/verticalAlign.js @@ -0,0 +1,10 @@ +const { createSimpleStaticUtilityPlugin } = require('../pluginUtils') + +module.exports = createSimpleStaticUtilityPlugin({ + '.align-baseline': { 'vertical-align': 'baseline' }, + '.align-top': { 'vertical-align': 'top' }, + '.align-middle': { 'vertical-align': 'middle' }, + '.align-bottom': { 'vertical-align': 'bottom' }, + '.align-text-top': { 'vertical-align': 'text-top' }, + '.align-text-bottom': { 'vertical-align': 'text-bottom' }, +}) diff --git a/jit/corePlugins/visibility.js b/jit/corePlugins/visibility.js new file mode 100644 index 000000000000..c4bb8d63ddbd --- /dev/null +++ b/jit/corePlugins/visibility.js @@ -0,0 +1,6 @@ +const { createSimpleStaticUtilityPlugin } = require('../pluginUtils') + +module.exports = createSimpleStaticUtilityPlugin({ + '.visible': { visibility: 'visible' }, + '.invisible': { visibility: 'hidden' }, +}) diff --git a/jit/corePlugins/whitespace.js b/jit/corePlugins/whitespace.js new file mode 100644 index 000000000000..755d4086f5e9 --- /dev/null +++ b/jit/corePlugins/whitespace.js @@ -0,0 +1,9 @@ +const { createSimpleStaticUtilityPlugin } = require('../pluginUtils') + +module.exports = createSimpleStaticUtilityPlugin({ + '.whitespace-normal': { 'white-space': 'normal' }, + '.whitespace-nowrap': { 'white-space': 'nowrap' }, + '.whitespace-pre': { 'white-space': 'pre' }, + '.whitespace-pre-line': { 'white-space': 'pre-line' }, + '.whitespace-pre-wrap': { 'white-space': 'pre-wrap' }, +}) diff --git a/jit/corePlugins/width.js b/jit/corePlugins/width.js new file mode 100644 index 000000000000..3ce8590a7929 --- /dev/null +++ b/jit/corePlugins/width.js @@ -0,0 +1,15 @@ +const { asValue, nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + w: (modifier, { theme }) => { + let value = asValue(modifier, theme['width']) + + if (value === undefined) { + return [] + } + + return { [nameClass('w', modifier)]: { width: value } } + }, + }) +} diff --git a/jit/corePlugins/wordBreak.js b/jit/corePlugins/wordBreak.js new file mode 100644 index 000000000000..8b3eec1cbbd3 --- /dev/null +++ b/jit/corePlugins/wordBreak.js @@ -0,0 +1,12 @@ +const { createSimpleStaticUtilityPlugin } = require('../pluginUtils') + +module.exports = createSimpleStaticUtilityPlugin({ + '.break-normal': { + 'overflow-wrap': 'normal', + 'word-break': 'normal', + }, + '.break-words': { + 'overflow-wrap': 'break-word', + }, + '.break-all': { 'word-break': 'break-all' }, +}) diff --git a/jit/corePlugins/zIndex.js b/jit/corePlugins/zIndex.js new file mode 100644 index 000000000000..9ce3e286aa59 --- /dev/null +++ b/jit/corePlugins/zIndex.js @@ -0,0 +1,15 @@ +const { asValue, nameClass } = require('../pluginUtils') + +module.exports = function ({ matchUtilities }) { + matchUtilities({ + z: (modifier, { theme }) => { + let value = asValue(modifier, theme.zIndex) + + if (value === undefined) { + return [] + } + + return { [nameClass('z', modifier)]: { 'z-index': value } } + }, + }) +} diff --git a/jit/index.js b/jit/index.js new file mode 100644 index 000000000000..e7d466deb832 --- /dev/null +++ b/jit/index.js @@ -0,0 +1,61 @@ +const postcss = require('postcss') + +const evaluateTailwindFunctions = require('../lib/lib/evaluateTailwindFunctions').default +const substituteScreenAtRules = require('../lib/lib/substituteScreenAtRules').default + +const rewriteTailwindImports = require('./lib/rewriteTailwindImports') +const setupContext = require('./lib/setupContext') +const removeLayerAtRules = require('./lib/removeLayerAtRules') +const expandTailwindAtRules = require('./lib/expandTailwindAtRules') +const expandApplyAtRules = require('./lib/expandApplyAtRules') +const collapseAdjacentRules = require('./lib/collapseAdjacentRules') + +const { env } = require('./lib/sharedState') + +module.exports = (configOrPath = {}) => { + return [ + env.DEBUG && + function (root) { + console.log('\n') + console.time('JIT TOTAL') + return root + }, + function (root, result) { + function registerDependency(fileName, type = 'dependency') { + result.messages.push({ + type, + plugin: 'tailwindcss-jit', + parent: result.opts.from, + file: fileName, + }) + } + + rewriteTailwindImports(root) + + let context = setupContext(configOrPath)(result, root) + + if (!env.TAILWIND_DISABLE_TOUCH) { + if (context.configPath !== null) { + registerDependency(context.configPath) + } + } + + return postcss([ + removeLayerAtRules(context), + expandTailwindAtRules(context, registerDependency), + expandApplyAtRules(context), + evaluateTailwindFunctions(context.tailwindConfig), + substituteScreenAtRules(context.tailwindConfig), + collapseAdjacentRules(context), + ]).process(root, { from: undefined }) + }, + env.DEBUG && + function (root) { + console.timeEnd('JIT TOTAL') + console.log('\n') + return root + }, + ].filter(Boolean) +} + +module.exports.postcss = true diff --git a/jit/lib/collapseAdjacentRules.js b/jit/lib/collapseAdjacentRules.js new file mode 100644 index 000000000000..5f9c0166a9c7 --- /dev/null +++ b/jit/lib/collapseAdjacentRules.js @@ -0,0 +1,35 @@ +let comparisonMap = { + atrule: ['name', 'params'], + rule: ['selector'], +} +let types = new Set(Object.keys(comparisonMap)) + +function collapseAdjacentRules() { + return (root) => { + let currentRule = null + root.each((node) => { + if (!types.has(node.type)) { + currentRule = null + return + } + + if (currentRule === null) { + currentRule = node + return + } + + let properties = comparisonMap[node.type] + + if (node.type === 'atrule' && node.name === 'font-face') { + currentRule = node + } else if (properties.every((property) => node[property] === currentRule[property])) { + currentRule.append(node.nodes) + node.remove() + } else { + currentRule = node + } + }) + } +} + +module.exports = collapseAdjacentRules diff --git a/jit/lib/expandApplyAtRules.js b/jit/lib/expandApplyAtRules.js new file mode 100644 index 000000000000..d76274df5a8e --- /dev/null +++ b/jit/lib/expandApplyAtRules.js @@ -0,0 +1,238 @@ +const postcss = require('postcss') +const { resolveMatches } = require('./generateRules') +const { bigSign, escapeClassName } = require('./utils') + +function buildApplyCache(applyCandidates, context) { + for (let candidate of applyCandidates) { + if (context.notClassCache.has(candidate) || context.applyClassCache.has(candidate)) { + continue + } + + if (context.classCache.has(candidate)) { + context.applyClassCache.set( + candidate, + context.classCache.get(candidate).map(([meta, rule]) => [meta, rule.clone()]) + ) + continue + } + + let matches = Array.from(resolveMatches(candidate, context)) + + if (matches.length === 0) { + context.notClassCache.add(candidate) + continue + } + + context.applyClassCache.set(candidate, matches) + } + + return context.applyClassCache +} + +// TODO: Apply `!important` stuff correctly instead of just skipping it +function extractApplyCandidates(params) { + let candidates = params.split(/[\s\t\n]+/g) + + if (candidates[candidates.length - 1] === '!important') { + return [candidates.slice(0, -1), true] + } + + return [candidates, false] +} + +function partitionApplyParents(root) { + let applyParents = new Set() + + root.walkAtRules('apply', (rule) => { + applyParents.add(rule.parent) + }) + + for (let rule of applyParents) { + let nodeGroups = [] + let lastGroup = [] + + for (let node of rule.nodes) { + if (node.type === 'atrule' && node.name === 'apply') { + if (lastGroup.length > 0) { + nodeGroups.push(lastGroup) + lastGroup = [] + } + nodeGroups.push([node]) + } else { + lastGroup.push(node) + } + } + + if (lastGroup.length > 0) { + nodeGroups.push(lastGroup) + } + + if (nodeGroups.length === 1) { + continue + } + + for (let group of [...nodeGroups].reverse()) { + let newParent = rule.clone({ nodes: [] }) + newParent.append(group) + rule.after(newParent) + } + + rule.remove() + } +} + +function processApply(root, context) { + let applyCandidates = new Set() + + // Collect all @apply rules and candidates + let applies = [] + root.walkAtRules('apply', (rule) => { + let [candidates] = extractApplyCandidates(rule.params) + + for (let util of candidates) { + applyCandidates.add(util) + } + applies.push(rule) + }) + + // Start the @apply process if we have rules with @apply in them + if (applies.length > 0) { + // Fill up some caches! + let applyClassCache = buildApplyCache(applyCandidates, context) + + /** + * When we have an apply like this: + * + * .abc { + * @apply hover:font-bold; + * } + * + * What we essentially will do is resolve to this: + * + * .abc { + * @apply .hover\:font-bold:hover { + * font-weight: 500; + * } + * } + * + * Notice that the to-be-applied class is `.hover\:font-bold:hover` and that the utility candidate was `hover:font-bold`. + * What happens in this function is that we prepend a `.` and escape the candidate. + * This will result in `.hover\:font-bold` + * Which means that we can replace `.hover\:font-bold` with `.abc` in `.hover\:font-bold:hover` resulting in `.abc:hover` + */ + // TODO: Should we use postcss-selector-parser for this instead? + function replaceSelector(selector, utilitySelectors, candidate) { + let needle = `.${escapeClassName(candidate)}` + let utilitySelectorsList = utilitySelectors.split(/\s*,\s*/g) + + return selector + .split(/\s*,\s*/g) + .map((s) => { + let replaced = [] + + for (let utilitySelector of utilitySelectorsList) { + let replacedSelector = utilitySelector.replace(needle, s) + if (replacedSelector === utilitySelector) { + continue + } + replaced.push(replacedSelector) + } + return replaced.join(', ') + }) + .join(', ') + } + + /** @type {Map} */ + let perParentApplies = new Map() + + // Collect all apply candidates and their rules + for (let apply of applies) { + let candidates = perParentApplies.get(apply.parent) || [] + + perParentApplies.set(apply.parent, candidates) + + let [applyCandidates, important] = extractApplyCandidates(apply.params) + + if (apply.parent.type === 'atrule') { + if (apply.parent.name === 'screen') { + const screenType = apply.parent.params + + throw apply.error( + `@apply is not supported within nested at-rules like @screen. We suggest you write this as @apply ${applyCandidates + .map((c) => `${screenType}:${c}`) + .join(' ')} instead.` + ) + } + + throw apply.error( + `@apply is not supported within nested at-rules like @${apply.parent.name}. You can fix this by un-nesting @${apply.parent.name}.` + ) + } + + for (let applyCandidate of applyCandidates) { + if (!applyClassCache.has(applyCandidate)) { + throw apply.error( + `The \`${applyCandidate}\` class does not exist. If \`${applyCandidate}\` is a custom class, make sure it is defined within a \`@layer\` directive.` + ) + } + + let rules = applyClassCache.get(applyCandidate) + + candidates.push([applyCandidate, important, rules]) + } + } + + for (const [parent, candidates] of perParentApplies) { + let siblings = [] + + for (let [applyCandidate, important, rules] of candidates) { + for (let [meta, node] of rules) { + let root = postcss.root({ nodes: [node.clone()] }) + let canRewriteSelector = + node.type !== 'atrule' || (node.type === 'atrule' && node.name !== 'keyframes') + + if (canRewriteSelector) { + root.walkRules((rule) => { + rule.selector = replaceSelector(parent.selector, rule.selector, applyCandidate) + + rule.walkDecls((d) => { + d.important = important + }) + }) + } + + siblings.push([meta, root.nodes[0]]) + } + } + + // Inject the rules, sorted, correctly + let nodes = siblings.sort(([a], [z]) => bigSign(a.sort - z.sort)).map((s) => s[1]) + + // console.log(parent) + // `parent` refers to the node at `.abc` in: .abc { @apply mt-2 } + parent.after(nodes) + } + + for (let apply of applies) { + // If there are left-over declarations, just remove the @apply + if (apply.parent.nodes.length > 1) { + apply.remove() + } else { + // The node is empty, drop the full node + apply.parent.remove() + } + } + + // Do it again, in case we have other `@apply` rules + processApply(root, context) + } +} + +function expandApplyAtRules(context) { + return (root) => { + partitionApplyParents(root) + processApply(root, context) + } +} + +module.exports = expandApplyAtRules diff --git a/jit/lib/expandTailwindAtRules.js b/jit/lib/expandTailwindAtRules.js new file mode 100644 index 000000000000..75e015accde9 --- /dev/null +++ b/jit/lib/expandTailwindAtRules.js @@ -0,0 +1,282 @@ +const fs = require('fs') +const path = require('path') +const fastGlob = require('fast-glob') +const parseGlob = require('parse-glob') +const sharedState = require('./sharedState') +const { generateRules } = require('./generateRules') +const { bigSign, cloneNodes } = require('./utils') + +let env = sharedState.env +let contentMatchCache = sharedState.contentMatchCache + +const BROAD_MATCH_GLOBAL_REGEXP = /[^<>"'`\s]*[^<>"'`\s:]/g +const INNER_MATCH_GLOBAL_REGEXP = /[^<>"'`\s.(){}[\]#=%]*[^<>"'`\s.(){}[\]#=%:]/g + +function getDefaultExtractor(fileExtension) { + return function (content) { + if (fileExtension === 'svelte') { + content = content.replace(/\sclass:/g, ' ') + } + let broadMatches = content.match(BROAD_MATCH_GLOBAL_REGEXP) || [] + let innerMatches = content.match(INNER_MATCH_GLOBAL_REGEXP) || [] + + return [...broadMatches, ...innerMatches] + } +} + +function getExtractor(fileName, tailwindConfig) { + const purgeOptions = tailwindConfig && tailwindConfig.purge && tailwindConfig.purge.options + const fileExtension = path.extname(fileName).slice(1) + + if (!purgeOptions) { + return getDefaultExtractor(fileExtension) + } + + const fileSpecificExtractor = (purgeOptions.extractors || []).find((extractor) => + extractor.extensions.includes(fileExtension) + ) + + if (fileSpecificExtractor) { + return fileSpecificExtractor.extractor + } + + return purgeOptions.defaultExtractor || getDefaultExtractor(fileExtension) +} + +// Scans template contents for possible classes. This is a hot path on initial build but +// not too important for subsequent builds. The faster the better though — if we can speed +// up these regexes by 50% that could cut initial build time by like 20%. +function getClassCandidates(content, extractor, contentMatchCache, candidates, seen) { + for (let line of content.split('\n')) { + line = line.trim() + + if (seen.has(line)) { + continue + } + seen.add(line) + + if (contentMatchCache.has(line)) { + for (let match of contentMatchCache.get(line)) { + candidates.add(match) + } + } else { + let extractorMatches = extractor(line) + let lineMatchesSet = new Set(extractorMatches) + + for (let match of lineMatchesSet) { + candidates.add(match) + } + + contentMatchCache.set(line, lineMatchesSet) + } + } +} + +function buildStylesheet(rules, context) { + let sortedRules = rules.sort(([a], [z]) => bigSign(a - z)) + + let returnValue = { + base: new Set(), + components: new Set(), + utilities: new Set(), + screens: new Set(), + } + + for (let [sort, rule] of sortedRules) { + if (sort >= context.minimumScreen) { + returnValue.screens.add(rule) + continue + } + + if (sort & context.layerOrder.base) { + returnValue.base.add(rule) + continue + } + + if (sort & context.layerOrder.components) { + returnValue.components.add(rule) + continue + } + + if (sort & context.layerOrder.utilities) { + returnValue.utilities.add(rule) + continue + } + } + + return returnValue +} + +function expandTailwindAtRules(context, registerDependency) { + return (root) => { + let foundTailwind = false + let layerNodes = { + base: null, + components: null, + utilities: null, + screens: null, + } + + // Make sure this file contains Tailwind directives. If not, we can save + // a lot of work and bail early. Also we don't have to register our touch + // file as a dependency since the output of this CSS does not depend on + // the source of any templates. Think Vue