merge upstream

This commit is contained in:
Sjors 2019-04-27 09:50:57 +02:00
commit 51d0fe1b5d
202 changed files with 36870 additions and 32114 deletions

View file

@ -1,4 +1,4 @@
/lib
/docs
/__tests__/fixtures/cli-utils.js
defaultConfig.stub.js
/stubs/*

View file

@ -3,11 +3,8 @@
"jest": true
},
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module",
"ecmaFeatures": {
"experimentalObjectRestSpread": true
}
"ecmaVersion": 2018,
"sourceType": "module"
},
"extends": ["eslint-config-postcss", "prettier"],
"plugins": ["prettier"],

2
.gitignore vendored
View file

@ -1,4 +1,6 @@
/node_modules
/lib
/example
index.html
package-lock.json
yarn-error.log

View file

@ -1,4 +1,5 @@
/__tests__/
/jest/
/src/
index.html
yarn-error.log

View file

@ -24,7 +24,7 @@ For help, discussion about best practices, or any other conversation that would
For casual chit-chat with others using the framework:
[Join the Tailwind CSS Slack Community](https://join.slack.com/t/tailwindcss/shared_invite/enQtNDQ1MDYyNDA0NzA3LTAzOGEzYTRmMjE2OWUwMGViMGM2NGM5OWVmN2UzZjlmNzQ0ZTA2NGUwODYyOWMzNzM0M2MzMmE1NGYyNjk5NTI)
[Join the Tailwind CSS Discord Server](https://discord.gg/7NF8GNe)
## Contributing

View file

@ -1,12 +1,18 @@
import postcss from 'postcss'
import substituteClassApplyAtRules from '../src/lib/substituteClassApplyAtRules'
import processPlugins from '../src/util/processPlugins'
import resolveConfig from '../src/util/resolveConfig'
import corePlugins from '../src/corePlugins'
import defaultConfig from '../defaultConfig.stub.js'
import defaultConfig from '../stubs/defaultConfig.stub.js'
const { utilities: defaultUtilities } = processPlugins(corePlugins(defaultConfig), defaultConfig)
const resolvedDefaultConfig = resolveConfig([defaultConfig])
function run(input, config = defaultConfig, utilities = defaultUtilities) {
const { utilities: defaultUtilities } = processPlugins(
corePlugins(resolvedDefaultConfig),
resolvedDefaultConfig
)
function run(input, config = resolvedDefaultConfig, utilities = defaultUtilities) {
return postcss([substituteClassApplyAtRules(config, utilities)]).process(input, {
from: undefined,
})
@ -200,10 +206,12 @@ test('you can apply utility classes without using the given prefix', () => {
.foo { margin-top: 1rem; margin-bottom: 1rem; }
`
const config = {
...defaultConfig,
prefix: 'tw-',
}
const config = resolveConfig([
{
...defaultConfig,
prefix: 'tw-',
},
])
return run(input, config, processPlugins(corePlugins(config), config).utilities).then(result => {
expect(result.css).toEqual(expected)
@ -220,12 +228,14 @@ test('you can apply utility classes without using the given prefix when using a
.foo { margin-top: 1rem; margin-bottom: 1rem; }
`
const config = {
...defaultConfig,
prefix: () => {
return 'tw-'
const config = resolveConfig([
{
...defaultConfig,
prefix: () => {
return 'tw-'
},
},
}
])
return run(input, config, processPlugins(corePlugins(config), config).utilities).then(result => {
expect(result.css).toEqual(expected)

View file

@ -0,0 +1,19 @@
import path from 'path'
import autoprefixer from 'autoprefixer'
import tailwind from '../src'
import compile from '../src/cli/compile'
describe('cli compile', () => {
const inputFile = path.resolve(__dirname, 'fixtures/tailwind-input.css')
const outputFile = 'output.css'
const plugins = [tailwind(), autoprefixer]
it('compiles CSS file', () => {
return compile({ inputFile, outputFile, plugins }).then(result => {
expect(result.css).toContain('.example')
expect(result.css).toContain('-ms-input-placeholder')
})
})
})

View file

@ -1,36 +1,43 @@
import path from 'path'
import cli from '../src/cli/main'
import * as constants from '../src/cli/constants'
import * as constants from '../src/constants'
import * as utils from '../src/cli/utils'
import runInTempDirectory from '../jest/runInTempDirectory'
describe('cli', () => {
const inputCssPath = path.resolve(__dirname, 'fixtures/tailwind-input.css')
const customConfigPath = path.resolve(__dirname, 'fixtures/custom-config.js')
const defaultConfigFixture = utils.readFile(constants.defaultConfigStubFile)
const simpleConfigFixture = utils.readFile(constants.simpleConfigStubFile)
beforeEach(() => {
console.log = jest.fn()
process.stdout.write = jest.fn()
utils.writeFile = jest.fn()
})
describe('init', () => {
it('creates a Tailwind config file', () => {
return cli(['init']).then(() => {
expect(utils.writeFile.mock.calls[0][0]).toEqual(constants.defaultConfigFile)
return runInTempDirectory(() => {
return cli(['init']).then(() => {
expect(utils.readFile(constants.defaultConfigFile)).toEqual(simpleConfigFixture)
})
})
})
it('creates a full Tailwind config file', () => {
return runInTempDirectory(() => {
return cli(['init', '--full']).then(() => {
expect(utils.readFile(constants.defaultConfigFile)).toEqual(defaultConfigFixture)
})
})
})
it('creates a Tailwind config file in a custom location', () => {
return cli(['init', 'custom.js']).then(() => {
expect(utils.writeFile.mock.calls[0][0]).toEqual('custom.js')
})
})
it('creates a Tailwind config file without comments', () => {
return cli(['init', '--no-comments']).then(() => {
expect(utils.writeFile.mock.calls[0][1]).not.toContain('/**')
expect(utils.writeFile.mock.calls[0][1]).toContain('//')
return runInTempDirectory(() => {
return cli(['init', 'custom.js']).then(() => {
expect(utils.exists('custom.js')).toEqual(true)
})
})
})
})
@ -49,9 +56,10 @@ describe('cli', () => {
})
it('creates compiled CSS file', () => {
return cli(['build', inputCssPath, '--output', 'output.css']).then(() => {
expect(utils.writeFile.mock.calls[0][0]).toEqual('output.css')
expect(utils.writeFile.mock.calls[0][1]).toContain('.example')
return runInTempDirectory(() => {
return cli(['build', inputCssPath, '--output', 'output.css']).then(() => {
expect(utils.readFile('output.css')).toContain('.example')
})
})
})

View file

@ -1,9 +1,6 @@
import path from 'path'
import * as utils from '../src/cli/utils'
describe('cli utils', () => {
const fixture = utils.readFile(path.resolve(__dirname, 'fixtures/cli-utils.js'))
describe('parseCliParams', () => {
it('parses CLI parameters', () => {
const result = utils.parseCliParams(['a', 'b', '-c', 'd'])
@ -60,52 +57,17 @@ describe('cli utils', () => {
})
})
describe('stripBlockComments', () => {
it('does not strip code', () => {
const result = utils.stripBlockComments(fixture)
describe('getSimplePath', () => {
it('strips leading ./', () => {
const result = utils.getSimplePath('./test')
expect(result).toEqual(expect.stringContaining('__code_no_comment__'))
expect(result).toEqual(expect.stringContaining('__code_comment_line__'))
expect(result).toEqual(expect.stringContaining('__code_comment_block__'))
expect(result).toEqual(expect.stringContaining('__code_comment_line_important__'))
expect(result).toEqual(expect.stringContaining('__code_comment_block_important__'))
expect(result).toEqual('test')
})
it('strips block comments', () => {
const result = utils.stripBlockComments(fixture)
it('returns unchanged path if it does not begin with ./', () => {
const result = utils.getSimplePath('../test')
expect(result).not.toEqual(expect.stringContaining('__comment_block__'))
expect(result).not.toEqual(expect.stringContaining('__comment_block_multiline__'))
expect(result).not.toEqual(expect.stringContaining('__comment_block_code__'))
})
it('strips docblock comments', () => {
const result = utils.stripBlockComments(fixture)
expect(result).not.toEqual(expect.stringContaining('__comment_docblock__'))
})
it('does not strip line comments', () => {
const result = utils.stripBlockComments(fixture)
expect(result).toEqual(expect.stringContaining('__comment_line__'))
expect(result).toEqual(expect.stringContaining('__comment_line_important__'))
expect(result).toEqual(expect.stringContaining('__comment_line_code__'))
expect(result).toEqual(expect.stringContaining('__comment_line_important_code__'))
})
it('does not strip important block comments', () => {
const result = utils.stripBlockComments(fixture)
expect(result).toEqual(expect.stringContaining('__comment_block_important__'))
expect(result).toEqual(expect.stringContaining('__comment_block_multiline_important__'))
expect(result).toEqual(expect.stringContaining('__comment_block_important_code__'))
})
it('does not strip important docblock comments', () => {
const result = utils.stripBlockComments(fixture)
expect(result).toEqual(expect.stringContaining('__comment_docblock_important__'))
expect(result).toEqual('../test')
})
})
})

View file

@ -2,180 +2,41 @@ import configurePlugins from '../src/util/configurePlugins'
test('setting a plugin to false removes it', () => {
const plugins = {
fontSize: options => {
return {
plugin: 'fontSize',
options,
}
},
display: options => {
return {
plugin: 'display',
options,
}
},
backgroundPosition: options => {
return {
plugin: 'backgroundPosition',
options,
}
},
}
const configuredPlugins = configurePlugins(plugins, {
fontSize: {},
display: false,
backgroundPosition: {},
})
expect(configuredPlugins).toEqual([
{ plugin: 'fontSize', options: {} },
{ plugin: 'backgroundPosition', options: {} },
])
})
test('setting a plugin to an object configures that plugin', () => {
const plugins = {
fontSize: options => {
return {
plugin: 'fontSize',
options,
}
},
display: options => {
return {
plugin: 'display',
options,
}
},
backgroundPosition: options => {
return {
plugin: 'backgroundPosition',
options,
}
},
}
const configuredPlugins = configurePlugins(plugins, {
fontSize: {
variants: ['responsive', 'hover'],
values: { '12': '12px', '14': '14px', '16': '16px' },
},
display: { variants: ['responsive'] },
backgroundPosition: {},
})
expect(configuredPlugins).toEqual([
{
plugin: 'fontSize',
options: {
variants: ['responsive', 'hover'],
values: { '12': '12px', '14': '14px', '16': '16px' },
},
},
{ plugin: 'display', options: { variants: ['responsive'] } },
{ plugin: 'backgroundPosition', options: {} },
])
})
test('plugins are configured with their default configuration if no custom config is provided', () => {
const plugins = {
fontSize: options => {
return {
plugin: 'fontSize',
options,
}
},
display: options => {
return {
plugin: 'display',
options,
}
},
backgroundPosition: options => {
return {
plugin: 'backgroundPosition',
options,
}
},
fontSize: () => 'fontSize',
display: () => 'display',
backgroundPosition: () => 'backgroundPosition',
}
const configuredPlugins = configurePlugins(
plugins,
{
fontSize: {
variants: ['responsive', 'hover'],
values: { '12': '12px', '14': '14px', '16': '16px' },
},
backgroundPosition: {},
display: false,
},
{
display: { variants: ['responsive'] },
}
plugins
)
expect(configuredPlugins).toEqual([
{
plugin: 'fontSize',
options: {
variants: ['responsive', 'hover'],
values: { '12': '12px', '14': '14px', '16': '16px' },
},
},
{ plugin: 'display', options: { variants: ['responsive'] } },
{ plugin: 'backgroundPosition', options: {} },
])
expect(configuredPlugins).toEqual(['fontSize', 'backgroundPosition'])
})
test('custom plugin configuration overrides default plugin configuration', () => {
test('passing only false removes all plugins', () => {
const plugins = {
fontSize: options => {
return {
plugin: 'fontSize',
options,
}
},
display: options => {
return {
plugin: 'display',
options,
}
},
backgroundPosition: options => {
return {
plugin: 'backgroundPosition',
options,
}
},
fontSize: () => 'fontSize',
display: () => 'display',
backgroundPosition: () => 'backgroundPosition',
}
const configuredPlugins = configurePlugins(
plugins,
{
fontSize: {
variants: ['responsive', 'hover'],
values: { '12': '12px', '14': '14px', '16': '16px' },
},
display: { variants: ['responsive'] },
backgroundPosition: {},
},
{
fontSize: {
variants: ['focus', 'active'],
values: { sm: '.75rem', md: '1rem', lg: '1.5rem' },
},
}
)
const configuredPlugins = configurePlugins(false, plugins)
expect(configuredPlugins).toEqual([
{
plugin: 'fontSize',
options: {
variants: ['responsive', 'hover'],
values: { '12': '12px', '14': '14px', '16': '16px' },
},
},
{ plugin: 'display', options: { variants: ['responsive'] } },
{ plugin: 'backgroundPosition', options: {} },
])
expect(configuredPlugins).toEqual([])
})
test('passing an array whitelists plugins', () => {
const plugins = {
fontSize: () => 'fontSize',
display: () => 'display',
backgroundPosition: () => 'backgroundPosition',
}
const configuredPlugins = configurePlugins(['display'], plugins)
expect(configuredPlugins).toEqual(['display'])
})

View file

@ -41,38 +41,16 @@ test.only('options are not required', () => {
`)
})
test.only('screens can be specified explicitly', () => {
test.only('screens can be passed explicitly', () => {
const { components } = processPlugins(
[
container({
screens: {
sm: '400px',
lg: '500px',
[container()],
config({
theme: {
container: {
screens: ['400px', '500px'],
},
}),
],
config()
)
expect(css(components)).toMatchCss(`
.container { width: 100% }
@media (min-width: 400px) {
.container { max-width: 400px }
}
@media (min-width: 500px) {
.container { max-width: 500px }
}
`)
})
test.only('screens can be an array', () => {
const { components } = processPlugins(
[
container({
screens: ['400px', '500px'],
}),
],
config()
},
})
)
expect(css(components)).toMatchCss(`
@ -88,12 +66,14 @@ test.only('screens can be an array', () => {
test.only('the container can be centered by default', () => {
const { components } = processPlugins(
[
container({
center: true,
}),
],
config()
[container()],
config({
theme: {
container: {
center: true,
},
},
})
)
expect(css(components)).toMatchCss(`
@ -119,12 +99,14 @@ test.only('the container can be centered by default', () => {
test.only('horizontal padding can be included by default', () => {
const { components } = processPlugins(
[
container({
padding: '2rem',
}),
],
config()
[container()],
config({
theme: {
container: {
padding: '2rem',
},
},
})
)
expect(css(components)).toMatchCss(`
@ -150,17 +132,16 @@ test.only('horizontal padding can be included by default', () => {
test.only('setting all options at once', () => {
const { components } = processPlugins(
[
container({
screens: {
sm: '400px',
lg: '500px',
[container()],
config({
theme: {
container: {
screens: ['400px', '500px'],
center: true,
padding: '2rem',
},
center: true,
padding: '2rem',
}),
],
config()
},
})
)
expect(css(components)).toMatchCss(`

View file

@ -1,6 +1,9 @@
import fs from 'fs'
import path from 'path'
import postcss from 'postcss'
import tailwind from '../src/index'
import { defaultConfigFile } from '../src/constants'
import inTempDirectory from '../jest/runInTempDirectory'
test('it uses the values from the custom config file', () => {
return postcss([tailwind(path.resolve(`${__dirname}/fixtures/custom-config.js`))])
@ -25,7 +28,6 @@ test('it uses the values from the custom config file', () => {
}
}
`
expect(result.css).toMatchCss(expected)
})
})
@ -65,3 +67,42 @@ test('custom config can be passed as an object', () => {
expect(result.css).toMatchCss(expected)
})
})
test('tailwind.config.js is picked up by default', () => {
return inTempDirectory(() => {
fs.writeFileSync(
path.resolve(defaultConfigFile),
`module.exports = {
theme: {
screens: {
mobile: '400px',
},
},
}`
)
return postcss([tailwind])
.process(
`
@responsive {
.foo {
color: blue;
}
}
`,
{ from: undefined }
)
.then(result => {
expect(result.css).toMatchCss(`
.foo {
color: blue;
}
@media (min-width: 400px) {
.mobile\\:foo {
color: blue;
}
}
`)
})
})
})

View file

@ -1,5 +1,11 @@
import config from '../defaultConfig.js'
import configStub from '../stubs/defaultConfig.stub.js'
test('the default config matches the stub', () => {
expect(config()).toEqual(require('../defaultConfig.stub.js'))
expect(config).toEqual(configStub)
})
test('modifying the default config does not affect the stub', () => {
config.theme = {}
expect(config).not.toEqual(configStub)
})

View file

@ -0,0 +1,11 @@
import theme from '../defaultTheme.js'
import configStub from '../stubs/defaultConfig.stub.js'
test('the default theme matches the stub', () => {
expect(theme).toEqual(configStub.theme)
})
test('modifying the default theme does not affect the stub', () => {
theme.colors = {}
expect(theme).not.toEqual(configStub.theme)
})

View file

@ -1,29 +0,0 @@
// __comment_line__
//! __comment_line_important__
/* __comment_block__ */
/*! __comment_block_important__ */
/*
__comment_block_multiline__
*/
/*!
__comment_block_multiline_important__
*/
/**
__comment_docblock__
*/
/**!
__comment_docblock_important__
*/
const __code_no_comment__ = 'test'
const __code_comment_line__ = 'test' // __comment_line_code__
const __code_comment_block__ = 'test' /* __comment_block_code__ */
const __code_comment_line_important__ = 'test' //! __comment_line_important_code__
const __code_comment_block_important__ = 'test' /*! __comment_block_important_code__ */

View file

@ -1,4 +1,4 @@
@tailwind preflight;
@tailwind base;
@tailwind components;
@ -7,6 +7,6 @@
@responsive {
.example {
@apply .font-bold;
color: config('theme.colors.red');
color: theme('colors.red.500');
}
}

File diff suppressed because it is too large Load diff

View file

@ -2,7 +2,7 @@
color: red;
}
@media (min-width: 576px) {
@media (min-width: 640px) {
.sm\:example {
color: red;
}
@ -14,13 +14,13 @@
}
}
@media (min-width: 992px) {
@media (min-width: 1024px) {
.lg\:example {
color: red;
}
}
@media (min-width: 1200px) {
@media (min-width: 1280px) {
.xl\:example {
color: red;
}

File diff suppressed because it is too large Load diff

View file

@ -1,302 +0,0 @@
import mergeConfigWithDefaults from '../src/util/mergeConfigWithDefaults'
test('prefix key overrides default prefix', () => {
const userConfig = {
prefix: 'tw-',
}
const defaultConfig = {
prefix: '',
important: false,
separator: ':',
theme: {
screens: {
mobile: '400px',
},
},
variants: {
appearance: ['responsive'],
borderCollapse: [],
borderColors: ['responsive', 'hover', 'focus'],
},
}
const result = mergeConfigWithDefaults(userConfig, defaultConfig)
expect(result).toEqual({
prefix: 'tw-',
important: false,
separator: ':',
theme: {
screens: {
mobile: '400px',
},
},
variants: {
appearance: ['responsive'],
borderCollapse: [],
borderColors: ['responsive', 'hover', 'focus'],
},
})
})
test('important key overrides default important', () => {
const userConfig = {
important: true,
}
const defaultConfig = {
prefix: '',
important: false,
separator: ':',
theme: {
screens: {
mobile: '400px',
},
},
variants: {
appearance: ['responsive'],
borderCollapse: [],
borderColors: ['responsive', 'hover', 'focus'],
},
}
const result = mergeConfigWithDefaults(userConfig, defaultConfig)
expect(result).toEqual({
prefix: '',
important: true,
separator: ':',
theme: {
screens: {
mobile: '400px',
},
},
variants: {
appearance: ['responsive'],
borderCollapse: [],
borderColors: ['responsive', 'hover', 'focus'],
},
})
})
test('separator key overrides default separator', () => {
const userConfig = {
separator: '__',
}
const defaultConfig = {
prefix: '',
important: false,
separator: ':',
theme: {
screens: {
mobile: '400px',
},
},
variants: {
appearance: ['responsive'],
borderCollapse: [],
borderColors: ['responsive', 'hover', 'focus'],
},
}
const result = mergeConfigWithDefaults(userConfig, defaultConfig)
expect(result).toEqual({
prefix: '',
important: false,
separator: '__',
theme: {
screens: {
mobile: '400px',
},
},
variants: {
appearance: ['responsive'],
borderCollapse: [],
borderColors: ['responsive', 'hover', 'focus'],
},
})
})
test('theme key is merged instead of replaced', () => {
const userConfig = {
theme: {
screens: {
mobile: '400px',
},
},
}
const defaultConfig = {
prefix: '-',
important: false,
separator: ':',
theme: {
colors: {
'grey-darker': '#606f7b',
'grey-dark': '#8795a1',
grey: '#b8c2cc',
'grey-light': '#dae1e7',
'grey-lighter': '#f1f5f8',
},
fonts: {
sans: ['system-ui', 'BlinkMacSystemFont', '-apple-system', 'Roboto', 'sans-serif'],
serif: ['Constantia', 'Lucida Bright', 'Georgia', 'serif'],
},
screens: {
sm: '500px',
md: '750px',
lg: '1000px',
},
},
variants: {
appearance: ['responsive'],
borderCollapse: [],
borderColors: ['responsive', 'hover', 'focus'],
},
}
const result = mergeConfigWithDefaults(userConfig, defaultConfig)
expect(result).toEqual({
prefix: '-',
important: false,
separator: ':',
theme: {
colors: {
'grey-darker': '#606f7b',
'grey-dark': '#8795a1',
grey: '#b8c2cc',
'grey-light': '#dae1e7',
'grey-lighter': '#f1f5f8',
},
fonts: {
sans: ['system-ui', 'BlinkMacSystemFont', '-apple-system', 'Roboto', 'sans-serif'],
serif: ['Constantia', 'Lucida Bright', 'Georgia', 'serif'],
},
screens: {
mobile: '400px',
},
},
variants: {
appearance: ['responsive'],
borderCollapse: [],
borderColors: ['responsive', 'hover', 'focus'],
},
})
})
test('variants key is merged instead of replaced', () => {
const userConfig = {
variants: {
backgroundAttachment: [],
borderColors: ['responsive', 'hover', 'focus', 'active'],
},
}
const defaultConfig = {
prefix: '-',
important: false,
separator: ':',
theme: {
colors: {
'grey-darker': '#606f7b',
'grey-dark': '#8795a1',
grey: '#b8c2cc',
'grey-light': '#dae1e7',
'grey-lighter': '#f1f5f8',
},
fonts: {
sans: ['system-ui', 'BlinkMacSystemFont', '-apple-system', 'Roboto', 'sans-serif'],
serif: ['Constantia', 'Lucida Bright', 'Georgia', 'serif'],
},
screens: {
sm: '500px',
md: '750px',
lg: '1000px',
},
},
variants: {
appearance: ['responsive'],
backgroundAttachment: ['responsive'],
borderCollapse: [],
borderColors: ['responsive', 'hover', 'focus'],
borderRadius: ['responsive'],
},
}
const result = mergeConfigWithDefaults(userConfig, defaultConfig)
expect(result).toEqual({
prefix: '-',
important: false,
separator: ':',
theme: {
colors: {
'grey-darker': '#606f7b',
'grey-dark': '#8795a1',
grey: '#b8c2cc',
'grey-light': '#dae1e7',
'grey-lighter': '#f1f5f8',
},
fonts: {
sans: ['system-ui', 'BlinkMacSystemFont', '-apple-system', 'Roboto', 'sans-serif'],
serif: ['Constantia', 'Lucida Bright', 'Georgia', 'serif'],
},
screens: {
sm: '500px',
md: '750px',
lg: '1000px',
},
},
variants: {
appearance: ['responsive'],
backgroundAttachment: [],
borderCollapse: [],
borderColors: ['responsive', 'hover', 'focus', 'active'],
borderRadius: ['responsive'],
},
})
})
test('missing top level keys are pulled from the default config', () => {
const userConfig = {}
const defaultConfig = {
prefix: '-',
important: false,
separator: ':',
theme: {
colors: { green: '#00ff00' },
screens: {
mobile: '400px',
},
},
variants: {
appearance: ['responsive'],
borderCollapse: [],
borderColors: ['responsive', 'hover', 'focus'],
},
}
const result = mergeConfigWithDefaults(userConfig, defaultConfig)
expect(result).toEqual({
prefix: '-',
important: false,
separator: ':',
theme: {
colors: { green: '#00ff00' },
screens: {
mobile: '400px',
},
},
variants: {
appearance: ['responsive'],
borderCollapse: [],
borderColors: ['responsive', 'hover', 'focus'],
},
})
})

View file

@ -0,0 +1,83 @@
import _ from 'lodash'
import escapeClassName from '../../src/util/escapeClassName'
import plugin from '../../src/plugins/backgroundColor'
test('colors can be a nested object', () => {
const addedUtilities = []
const config = {
theme: {
backgroundColor: {
purple: 'purple',
white: {
25: 'rgba(255,255,255,.25)',
50: 'rgba(255,255,255,.5)',
75: 'rgba(255,255,255,.75)',
default: '#fff',
},
red: {
1: 'rgb(33,0,0)',
2: 'rgb(67,0,0)',
3: 'rgb(100,0,0)',
},
green: {
1: 'rgb(0,33,0)',
2: 'rgb(0,67,0)',
3: 'rgb(0,100,0)',
},
blue: {
1: 'rgb(0,0,33)',
2: 'rgb(0,0,67)',
3: 'rgb(0,0,100)',
},
},
},
variants: {
backgroundColor: ['responsive'],
},
}
const getConfigValue = (path, defaultValue) => _.get(config, path, defaultValue)
const pluginApi = {
config: getConfigValue,
e: escapeClassName,
theme: (path, defaultValue) => getConfigValue(`theme.${path}`, defaultValue),
variants: (path, defaultValue) => {
if (_.isArray(config.variants)) {
return config.variants
}
return getConfigValue(`variants.${path}`, defaultValue)
},
addUtilities(utilities, variants) {
addedUtilities.push({
utilities,
variants,
})
},
}
plugin()(pluginApi)
expect(addedUtilities).toEqual([
{
utilities: {
'.bg-purple': { 'background-color': 'purple' },
'.bg-white-25': { 'background-color': 'rgba(255,255,255,.25)' },
'.bg-white-50': { 'background-color': 'rgba(255,255,255,.5)' },
'.bg-white-75': { 'background-color': 'rgba(255,255,255,.75)' },
'.bg-white': { 'background-color': '#fff' },
'.bg-red-1': { 'background-color': 'rgb(33,0,0)' },
'.bg-red-2': { 'background-color': 'rgb(67,0,0)' },
'.bg-red-3': { 'background-color': 'rgb(100,0,0)' },
'.bg-green-1': { 'background-color': 'rgb(0,33,0)' },
'.bg-green-2': { 'background-color': 'rgb(0,67,0)' },
'.bg-green-3': { 'background-color': 'rgb(0,100,0)' },
'.bg-blue-1': { 'background-color': 'rgb(0,0,33)' },
'.bg-blue-2': { 'background-color': 'rgb(0,0,67)' },
'.bg-blue-3': { 'background-color': 'rgb(0,0,100)' },
},
variants: ['responsive'],
},
])
})

View file

@ -0,0 +1,83 @@
import _ from 'lodash'
import escapeClassName from '../../src/util/escapeClassName'
import plugin from '../../src/plugins/borderColor'
test('colors can be a nested object', () => {
const addedUtilities = []
const config = {
theme: {
borderColor: {
purple: 'purple',
white: {
25: 'rgba(255,255,255,.25)',
50: 'rgba(255,255,255,.5)',
75: 'rgba(255,255,255,.75)',
default: '#fff',
},
red: {
1: 'rgb(33,0,0)',
2: 'rgb(67,0,0)',
3: 'rgb(100,0,0)',
},
green: {
1: 'rgb(0,33,0)',
2: 'rgb(0,67,0)',
3: 'rgb(0,100,0)',
},
blue: {
1: 'rgb(0,0,33)',
2: 'rgb(0,0,67)',
3: 'rgb(0,0,100)',
},
},
},
variants: {
borderColor: ['responsive'],
},
}
const getConfigValue = (path, defaultValue) => _.get(config, path, defaultValue)
const pluginApi = {
config: getConfigValue,
e: escapeClassName,
theme: (path, defaultValue) => getConfigValue(`theme.${path}`, defaultValue),
variants: (path, defaultValue) => {
if (_.isArray(config.variants)) {
return config.variants
}
return getConfigValue(`variants.${path}`, defaultValue)
},
addUtilities(utilities, variants) {
addedUtilities.push({
utilities,
variants,
})
},
}
plugin()(pluginApi)
expect(addedUtilities).toEqual([
{
utilities: {
'.border-purple': { 'border-color': 'purple' },
'.border-white-25': { 'border-color': 'rgba(255,255,255,.25)' },
'.border-white-50': { 'border-color': 'rgba(255,255,255,.5)' },
'.border-white-75': { 'border-color': 'rgba(255,255,255,.75)' },
'.border-white': { 'border-color': '#fff' },
'.border-red-1': { 'border-color': 'rgb(33,0,0)' },
'.border-red-2': { 'border-color': 'rgb(67,0,0)' },
'.border-red-3': { 'border-color': 'rgb(100,0,0)' },
'.border-green-1': { 'border-color': 'rgb(0,33,0)' },
'.border-green-2': { 'border-color': 'rgb(0,67,0)' },
'.border-green-3': { 'border-color': 'rgb(0,100,0)' },
'.border-blue-1': { 'border-color': 'rgb(0,0,33)' },
'.border-blue-2': { 'border-color': 'rgb(0,0,67)' },
'.border-blue-3': { 'border-color': 'rgb(0,0,100)' },
},
variants: ['responsive'],
},
])
})

View file

@ -0,0 +1,83 @@
import _ from 'lodash'
import escapeClassName from '../../src/util/escapeClassName'
import plugin from '../../src/plugins/fill'
test('colors can be a nested object', () => {
const addedUtilities = []
const config = {
theme: {
fill: {
purple: 'purple',
white: {
25: 'rgba(255,255,255,.25)',
50: 'rgba(255,255,255,.5)',
75: 'rgba(255,255,255,.75)',
default: '#fff',
},
red: {
1: 'rgb(33,0,0)',
2: 'rgb(67,0,0)',
3: 'rgb(100,0,0)',
},
green: {
1: 'rgb(0,33,0)',
2: 'rgb(0,67,0)',
3: 'rgb(0,100,0)',
},
blue: {
1: 'rgb(0,0,33)',
2: 'rgb(0,0,67)',
3: 'rgb(0,0,100)',
},
},
},
variants: {
fill: ['responsive'],
},
}
const getConfigValue = (path, defaultValue) => _.get(config, path, defaultValue)
const pluginApi = {
config: getConfigValue,
e: escapeClassName,
theme: (path, defaultValue) => getConfigValue(`theme.${path}`, defaultValue),
variants: (path, defaultValue) => {
if (_.isArray(config.variants)) {
return config.variants
}
return getConfigValue(`variants.${path}`, defaultValue)
},
addUtilities(utilities, variants) {
addedUtilities.push({
utilities,
variants,
})
},
}
plugin()(pluginApi)
expect(addedUtilities).toEqual([
{
utilities: {
'.fill-purple': { fill: 'purple' },
'.fill-white-25': { fill: 'rgba(255,255,255,.25)' },
'.fill-white-50': { fill: 'rgba(255,255,255,.5)' },
'.fill-white-75': { fill: 'rgba(255,255,255,.75)' },
'.fill-white': { fill: '#fff' },
'.fill-red-1': { fill: 'rgb(33,0,0)' },
'.fill-red-2': { fill: 'rgb(67,0,0)' },
'.fill-red-3': { fill: 'rgb(100,0,0)' },
'.fill-green-1': { fill: 'rgb(0,33,0)' },
'.fill-green-2': { fill: 'rgb(0,67,0)' },
'.fill-green-3': { fill: 'rgb(0,100,0)' },
'.fill-blue-1': { fill: 'rgb(0,0,33)' },
'.fill-blue-2': { fill: 'rgb(0,0,67)' },
'.fill-blue-3': { fill: 'rgb(0,0,100)' },
},
variants: ['responsive'],
},
])
})

View file

@ -0,0 +1,83 @@
import _ from 'lodash'
import escapeClassName from '../../src/util/escapeClassName'
import plugin from '../../src/plugins/stroke'
test('colors can be a nested object', () => {
const addedUtilities = []
const config = {
theme: {
stroke: {
purple: 'purple',
white: {
25: 'rgba(255,255,255,.25)',
50: 'rgba(255,255,255,.5)',
75: 'rgba(255,255,255,.75)',
default: '#fff',
},
red: {
1: 'rgb(33,0,0)',
2: 'rgb(67,0,0)',
3: 'rgb(100,0,0)',
},
green: {
1: 'rgb(0,33,0)',
2: 'rgb(0,67,0)',
3: 'rgb(0,100,0)',
},
blue: {
1: 'rgb(0,0,33)',
2: 'rgb(0,0,67)',
3: 'rgb(0,0,100)',
},
},
},
variants: {
stroke: ['responsive'],
},
}
const getConfigValue = (path, defaultValue) => _.get(config, path, defaultValue)
const pluginApi = {
config: getConfigValue,
e: escapeClassName,
theme: (path, defaultValue) => getConfigValue(`theme.${path}`, defaultValue),
variants: (path, defaultValue) => {
if (_.isArray(config.variants)) {
return config.variants
}
return getConfigValue(`variants.${path}`, defaultValue)
},
addUtilities(utilities, variants) {
addedUtilities.push({
utilities,
variants,
})
},
}
plugin()(pluginApi)
expect(addedUtilities).toEqual([
{
utilities: {
'.stroke-purple': { stroke: 'purple' },
'.stroke-white-25': { stroke: 'rgba(255,255,255,.25)' },
'.stroke-white-50': { stroke: 'rgba(255,255,255,.5)' },
'.stroke-white-75': { stroke: 'rgba(255,255,255,.75)' },
'.stroke-white': { stroke: '#fff' },
'.stroke-red-1': { stroke: 'rgb(33,0,0)' },
'.stroke-red-2': { stroke: 'rgb(67,0,0)' },
'.stroke-red-3': { stroke: 'rgb(100,0,0)' },
'.stroke-green-1': { stroke: 'rgb(0,33,0)' },
'.stroke-green-2': { stroke: 'rgb(0,67,0)' },
'.stroke-green-3': { stroke: 'rgb(0,100,0)' },
'.stroke-blue-1': { stroke: 'rgb(0,0,33)' },
'.stroke-blue-2': { stroke: 'rgb(0,0,67)' },
'.stroke-blue-3': { stroke: 'rgb(0,0,100)' },
},
variants: ['responsive'],
},
])
})

View file

@ -0,0 +1,83 @@
import _ from 'lodash'
import escapeClassName from '../../src/util/escapeClassName'
import plugin from '../../src/plugins/textColor'
test('colors can be a nested object', () => {
const addedUtilities = []
const config = {
theme: {
textColor: {
purple: 'purple',
white: {
25: 'rgba(255,255,255,.25)',
50: 'rgba(255,255,255,.5)',
75: 'rgba(255,255,255,.75)',
default: '#fff',
},
red: {
1: 'rgb(33,0,0)',
2: 'rgb(67,0,0)',
3: 'rgb(100,0,0)',
},
green: {
1: 'rgb(0,33,0)',
2: 'rgb(0,67,0)',
3: 'rgb(0,100,0)',
},
blue: {
1: 'rgb(0,0,33)',
2: 'rgb(0,0,67)',
3: 'rgb(0,0,100)',
},
},
},
variants: {
textColor: ['responsive'],
},
}
const getConfigValue = (path, defaultValue) => _.get(config, path, defaultValue)
const pluginApi = {
config: getConfigValue,
e: escapeClassName,
theme: (path, defaultValue) => getConfigValue(`theme.${path}`, defaultValue),
variants: (path, defaultValue) => {
if (_.isArray(config.variants)) {
return config.variants
}
return getConfigValue(`variants.${path}`, defaultValue)
},
addUtilities(utilities, variants) {
addedUtilities.push({
utilities,
variants,
})
},
}
plugin()(pluginApi)
expect(addedUtilities).toEqual([
{
utilities: {
'.text-purple': { color: 'purple' },
'.text-white-25': { color: 'rgba(255,255,255,.25)' },
'.text-white-50': { color: 'rgba(255,255,255,.5)' },
'.text-white-75': { color: 'rgba(255,255,255,.75)' },
'.text-white': { color: '#fff' },
'.text-red-1': { color: 'rgb(33,0,0)' },
'.text-red-2': { color: 'rgb(67,0,0)' },
'.text-red-3': { color: 'rgb(100,0,0)' },
'.text-green-1': { color: 'rgb(0,33,0)' },
'.text-green-2': { color: 'rgb(0,67,0)' },
'.text-green-3': { color: 'rgb(0,100,0)' },
'.text-blue-1': { color: 'rgb(0,0,33)' },
'.text-blue-2': { color: 'rgb(0,0,67)' },
'.text-blue-3': { color: 'rgb(0,0,100)' },
},
variants: ['responsive'],
},
])
})

View file

@ -1,9 +1,9 @@
import _ from 'lodash'
import postcss from 'postcss'
import _postcss from 'postcss'
import processPlugins from '../src/util/processPlugins'
function css(nodes) {
return postcss.root({ nodes }).toString()
return _postcss.root({ nodes }).toString()
}
function makeConfig(overrides) {
@ -95,7 +95,7 @@ test('plugins can create utilities with arrays of objects', () => {
test('plugins can create utilities with raw PostCSS nodes', () => {
const { components, utilities } = processPlugins(
[
function({ addUtilities }) {
function({ addUtilities, postcss }) {
addUtilities([
postcss.rule({ selector: '.object-fill' }).append([
postcss.decl({
@ -140,7 +140,7 @@ test('plugins can create utilities with raw PostCSS nodes', () => {
test('plugins can create utilities with mixed object styles and PostCSS nodes', () => {
const { components, utilities } = processPlugins(
[
function({ addUtilities }) {
function({ addUtilities, postcss }) {
addUtilities([
{
'.object-fill': {
@ -254,10 +254,70 @@ test('plugins can create components with object syntax', () => {
`)
})
test('plugins can add base styles with object syntax', () => {
const { base } = processPlugins(
[
function({ addBase }) {
addBase({
img: {
maxWidth: '100%',
},
button: {
fontFamily: 'inherit',
},
})
},
],
makeConfig()
)
expect(css(base)).toMatchCss(`
img {
max-width: 100%
}
button {
font-family: inherit
}
`)
})
test('plugins can add base styles with raw PostCSS nodes', () => {
const { base } = processPlugins(
[
function({ addBase, postcss }) {
addBase([
postcss.rule({ selector: 'img' }).append([
postcss.decl({
prop: 'max-width',
value: '100%',
}),
]),
postcss.rule({ selector: 'button' }).append([
postcss.decl({
prop: 'font-family',
value: 'inherit',
}),
]),
])
},
],
makeConfig()
)
expect(css(base)).toMatchCss(`
img {
max-width: 100%
}
button {
font-family: inherit
}
`)
})
test('plugins can create components with raw PostCSS nodes', () => {
const { components, utilities } = processPlugins(
[
function({ addComponents }) {
function({ addComponents, postcss }) {
addComponents([
postcss.rule({ selector: '.btn-blue' }).append([
postcss.decl({
@ -306,7 +366,7 @@ test('plugins can create components with raw PostCSS nodes', () => {
test('plugins can create components with mixed object styles and raw PostCSS nodes', () => {
const { components, utilities } = processPlugins(
[
function({ addComponents }) {
function({ addComponents, postcss }) {
addComponents([
postcss.rule({ selector: '.btn-blue' }).append([
postcss.decl({
@ -597,6 +657,91 @@ test('plugins can access the current config', () => {
`)
})
test('plugins can access the variants config directly', () => {
const { components, utilities } = processPlugins(
[
function({ addUtilities, variants }) {
addUtilities(
{
'.object-fill': {
'object-fit': 'fill',
},
'.object-contain': {
'object-fit': 'contain',
},
'.object-cover': {
'object-fit': 'cover',
},
},
variants('objectFit')
)
},
],
makeConfig({
variants: {
objectFit: ['responsive', 'focus', 'hover'],
},
})
)
expect(components.length).toBe(0)
expect(css(utilities)).toMatchCss(`
@variants responsive, focus, hover {
.object-fill {
object-fit: fill
}
.object-contain {
object-fit: contain
}
.object-cover {
object-fit: cover
}
}
`)
})
test('plugins apply all global variants when variants are configured globally', () => {
const { components, utilities } = processPlugins(
[
function({ addUtilities, variants }) {
addUtilities(
{
'.object-fill': {
'object-fit': 'fill',
},
},
variants('objectFit')
)
addUtilities(
{
'.rotate-90deg': {
transform: 'rotate(90deg)',
},
},
variants('rotate')
)
},
],
makeConfig({
variants: ['responsive', 'focus', 'hover'],
})
)
expect(components.length).toBe(0)
expect(css(utilities)).toMatchCss(`
@variants responsive, focus, hover {
.object-fill {
object-fit: fill
}
}
@variants responsive, focus, hover {
.rotate-90deg {
transform: rotate(90deg)
}
}
`)
})
test('plugins can provide fallbacks to keys missing from the config', () => {
const { components, utilities } = processPlugins(
[

View file

@ -0,0 +1,946 @@
import resolveConfig from '../src/util/resolveConfig'
test('prefix key overrides default prefix', () => {
const userConfig = {
prefix: 'tw-',
}
const defaultConfig = {
prefix: '',
important: false,
separator: ':',
theme: {
screens: {
mobile: '400px',
},
},
variants: {
appearance: ['responsive'],
borderCollapse: [],
borderColors: ['responsive', 'hover', 'focus'],
},
}
const result = resolveConfig([userConfig, defaultConfig])
expect(result).toEqual({
prefix: 'tw-',
important: false,
separator: ':',
theme: {
screens: {
mobile: '400px',
},
},
variants: {
appearance: ['responsive'],
borderCollapse: [],
borderColors: ['responsive', 'hover', 'focus'],
},
})
})
test('important key overrides default important', () => {
const userConfig = {
important: true,
}
const defaultConfig = {
prefix: '',
important: false,
separator: ':',
theme: {
screens: {
mobile: '400px',
},
},
variants: {
appearance: ['responsive'],
borderCollapse: [],
borderColors: ['responsive', 'hover', 'focus'],
},
}
const result = resolveConfig([userConfig, defaultConfig])
expect(result).toEqual({
prefix: '',
important: true,
separator: ':',
theme: {
screens: {
mobile: '400px',
},
},
variants: {
appearance: ['responsive'],
borderCollapse: [],
borderColors: ['responsive', 'hover', 'focus'],
},
})
})
test('separator key overrides default separator', () => {
const userConfig = {
separator: '__',
}
const defaultConfig = {
prefix: '',
important: false,
separator: ':',
theme: {
screens: {
mobile: '400px',
},
},
variants: {
appearance: ['responsive'],
borderCollapse: [],
borderColors: ['responsive', 'hover', 'focus'],
},
}
const result = resolveConfig([userConfig, defaultConfig])
expect(result).toEqual({
prefix: '',
important: false,
separator: '__',
theme: {
screens: {
mobile: '400px',
},
},
variants: {
appearance: ['responsive'],
borderCollapse: [],
borderColors: ['responsive', 'hover', 'focus'],
},
})
})
test('theme key is merged instead of replaced', () => {
const userConfig = {
theme: {
screens: {
mobile: '400px',
},
},
}
const defaultConfig = {
prefix: '-',
important: false,
separator: ':',
theme: {
colors: {
'grey-darker': '#606f7b',
'grey-dark': '#8795a1',
grey: '#b8c2cc',
'grey-light': '#dae1e7',
'grey-lighter': '#f1f5f8',
},
fonts: {
sans: ['system-ui', 'BlinkMacSystemFont', '-apple-system', 'Roboto', 'sans-serif'],
serif: ['Constantia', 'Lucida Bright', 'Georgia', 'serif'],
},
screens: {
sm: '500px',
md: '750px',
lg: '1000px',
},
},
variants: {
appearance: ['responsive'],
borderCollapse: [],
borderColors: ['responsive', 'hover', 'focus'],
},
}
const result = resolveConfig([userConfig, defaultConfig])
expect(result).toEqual({
prefix: '-',
important: false,
separator: ':',
theme: {
colors: {
'grey-darker': '#606f7b',
'grey-dark': '#8795a1',
grey: '#b8c2cc',
'grey-light': '#dae1e7',
'grey-lighter': '#f1f5f8',
},
fonts: {
sans: ['system-ui', 'BlinkMacSystemFont', '-apple-system', 'Roboto', 'sans-serif'],
serif: ['Constantia', 'Lucida Bright', 'Georgia', 'serif'],
},
screens: {
mobile: '400px',
},
},
variants: {
appearance: ['responsive'],
borderCollapse: [],
borderColors: ['responsive', 'hover', 'focus'],
},
})
})
test('variants key is merged instead of replaced', () => {
const userConfig = {
variants: {
backgroundAttachment: [],
borderColors: ['responsive', 'hover', 'focus', 'active'],
},
}
const defaultConfig = {
prefix: '-',
important: false,
separator: ':',
theme: {
colors: {
'grey-darker': '#606f7b',
'grey-dark': '#8795a1',
grey: '#b8c2cc',
'grey-light': '#dae1e7',
'grey-lighter': '#f1f5f8',
},
fonts: {
sans: ['system-ui', 'BlinkMacSystemFont', '-apple-system', 'Roboto', 'sans-serif'],
serif: ['Constantia', 'Lucida Bright', 'Georgia', 'serif'],
},
screens: {
sm: '500px',
md: '750px',
lg: '1000px',
},
},
variants: {
appearance: ['responsive'],
backgroundAttachment: ['responsive'],
borderCollapse: [],
borderColors: ['responsive', 'hover', 'focus'],
borderRadius: ['responsive'],
},
}
const result = resolveConfig([userConfig, defaultConfig])
expect(result).toEqual({
prefix: '-',
important: false,
separator: ':',
theme: {
colors: {
'grey-darker': '#606f7b',
'grey-dark': '#8795a1',
grey: '#b8c2cc',
'grey-light': '#dae1e7',
'grey-lighter': '#f1f5f8',
},
fonts: {
sans: ['system-ui', 'BlinkMacSystemFont', '-apple-system', 'Roboto', 'sans-serif'],
serif: ['Constantia', 'Lucida Bright', 'Georgia', 'serif'],
},
screens: {
sm: '500px',
md: '750px',
lg: '1000px',
},
},
variants: {
appearance: ['responsive'],
backgroundAttachment: [],
borderCollapse: [],
borderColors: ['responsive', 'hover', 'focus', 'active'],
borderRadius: ['responsive'],
},
})
})
test('missing top level keys are pulled from the default config', () => {
const userConfig = {}
const defaultConfig = {
prefix: '-',
important: false,
separator: ':',
theme: {
colors: { green: '#00ff00' },
screens: {
mobile: '400px',
},
},
variants: {
appearance: ['responsive'],
borderCollapse: [],
borderColors: ['responsive', 'hover', 'focus'],
},
}
const result = resolveConfig([userConfig, defaultConfig])
expect(result).toEqual({
prefix: '-',
important: false,
separator: ':',
theme: {
colors: { green: '#00ff00' },
screens: {
mobile: '400px',
},
},
variants: {
appearance: ['responsive'],
borderCollapse: [],
borderColors: ['responsive', 'hover', 'focus'],
},
})
})
test('functions in the default theme section are lazily evaluated', () => {
const userConfig = {
theme: {
colors: {
red: 'red',
green: 'green',
blue: 'blue',
},
},
}
const defaultConfig = {
prefix: '-',
important: false,
separator: ':',
theme: {
colors: {
cyan: 'cyan',
magenta: 'magenta',
yellow: 'yellow',
},
backgroundColors: theme => theme('colors'),
textColors: theme => theme('colors'),
},
variants: {
backgroundColors: ['responsive', 'hover', 'focus'],
textColors: ['responsive', 'hover', 'focus'],
},
}
const result = resolveConfig([userConfig, defaultConfig])
expect(result).toEqual({
prefix: '-',
important: false,
separator: ':',
theme: {
colors: {
red: 'red',
green: 'green',
blue: 'blue',
},
backgroundColors: {
red: 'red',
green: 'green',
blue: 'blue',
},
textColors: {
red: 'red',
green: 'green',
blue: 'blue',
},
},
variants: {
backgroundColors: ['responsive', 'hover', 'focus'],
textColors: ['responsive', 'hover', 'focus'],
},
})
})
test('functions in the user theme section are lazily evaluated', () => {
const userConfig = {
theme: {
colors: {
red: 'red',
green: 'green',
blue: 'blue',
},
backgroundColors: theme => ({
...theme('colors'),
customBackground: '#bada55',
}),
textColors: theme => ({
...theme('colors'),
customText: '#facade',
}),
},
}
const defaultConfig = {
prefix: '-',
important: false,
separator: ':',
theme: {
colors: {
cyan: 'cyan',
magenta: 'magenta',
yellow: 'yellow',
},
backgroundColors: ({ colors }) => colors,
textColors: ({ colors }) => colors,
},
variants: {
backgroundColors: ['responsive', 'hover', 'focus'],
textColors: ['responsive', 'hover', 'focus'],
},
}
const result = resolveConfig([userConfig, defaultConfig])
expect(result).toEqual({
prefix: '-',
important: false,
separator: ':',
theme: {
colors: {
red: 'red',
green: 'green',
blue: 'blue',
},
backgroundColors: {
red: 'red',
green: 'green',
blue: 'blue',
customBackground: '#bada55',
},
textColors: {
red: 'red',
green: 'green',
blue: 'blue',
customText: '#facade',
},
},
variants: {
backgroundColors: ['responsive', 'hover', 'focus'],
textColors: ['responsive', 'hover', 'focus'],
},
})
})
test('theme values in the extend section extend the existing theme', () => {
const userConfig = {
theme: {
extend: {
opacity: {
'25': '25',
'75': '.75',
},
backgroundColors: {
customBackground: '#bada55',
},
},
},
}
const defaultConfig = {
prefix: '-',
important: false,
separator: ':',
theme: {
colors: {
cyan: 'cyan',
magenta: 'magenta',
yellow: 'yellow',
},
opacity: {
'0': '0',
'50': '.5',
'100': '1',
},
backgroundColors: theme => theme('colors'),
},
variants: {
backgroundColors: ['responsive', 'hover', 'focus'],
opacity: ['responsive', 'hover', 'focus'],
},
}
const result = resolveConfig([userConfig, defaultConfig])
expect(result).toEqual({
prefix: '-',
important: false,
separator: ':',
theme: {
colors: {
cyan: 'cyan',
magenta: 'magenta',
yellow: 'yellow',
},
opacity: {
'0': '0',
'50': '.5',
'100': '1',
'25': '25',
'75': '.75',
},
backgroundColors: {
cyan: 'cyan',
magenta: 'magenta',
yellow: 'yellow',
customBackground: '#bada55',
},
},
variants: {
backgroundColors: ['responsive', 'hover', 'focus'],
opacity: ['responsive', 'hover', 'focus'],
},
})
})
test('theme values in the extend section extend the user theme', () => {
const userConfig = {
theme: {
opacity: {
'0': '0',
'20': '.2',
'40': '.4',
},
height: theme => theme('width'),
extend: {
opacity: {
'60': '.6',
'80': '.8',
'100': '1',
},
height: {
customHeight: '500vh',
},
},
},
}
const defaultConfig = {
prefix: '-',
important: false,
separator: ':',
theme: {
opacity: {
'0': '0',
'50': '.5',
'100': '1',
},
height: {
'0': 0,
full: '100%',
},
width: {
'0': 0,
'1': '.25rem',
'2': '.5rem',
'3': '.75rem',
'4': '1rem',
},
},
variants: {
opacity: ['responsive', 'hover', 'focus'],
height: ['responsive'],
width: ['responsive'],
},
}
const result = resolveConfig([userConfig, defaultConfig])
expect(result).toEqual({
prefix: '-',
important: false,
separator: ':',
theme: {
opacity: {
'0': '0',
'20': '.2',
'40': '.4',
'60': '.6',
'80': '.8',
'100': '1',
},
height: {
'0': 0,
'1': '.25rem',
'2': '.5rem',
'3': '.75rem',
'4': '1rem',
customHeight: '500vh',
},
width: {
'0': 0,
'1': '.25rem',
'2': '.5rem',
'3': '.75rem',
'4': '1rem',
},
},
variants: {
opacity: ['responsive', 'hover', 'focus'],
height: ['responsive'],
width: ['responsive'],
},
})
})
test('theme values in the extend section can extend values that are depended on lazily', () => {
const userConfig = {
theme: {
extend: {
colors: {
red: 'red',
green: 'green',
blue: 'blue',
},
backgroundColors: {
customBackground: '#bada55',
},
},
},
}
const defaultConfig = {
prefix: '-',
important: false,
separator: ':',
theme: {
colors: {
cyan: 'cyan',
magenta: 'magenta',
yellow: 'yellow',
},
backgroundColors: theme => theme('colors'),
},
variants: {
backgroundColors: ['responsive', 'hover', 'focus'],
},
}
const result = resolveConfig([userConfig, defaultConfig])
expect(result).toEqual({
prefix: '-',
important: false,
separator: ':',
theme: {
colors: {
cyan: 'cyan',
magenta: 'magenta',
yellow: 'yellow',
red: 'red',
green: 'green',
blue: 'blue',
},
backgroundColors: {
cyan: 'cyan',
magenta: 'magenta',
yellow: 'yellow',
red: 'red',
green: 'green',
blue: 'blue',
customBackground: '#bada55',
},
},
variants: {
backgroundColors: ['responsive', 'hover', 'focus'],
},
})
})
test('theme values in the extend section are not deeply merged', () => {
const userConfig = {
theme: {
extend: {
fonts: {
sans: ['Comic Sans'],
},
},
},
}
const defaultConfig = {
prefix: '-',
important: false,
separator: ':',
theme: {
fonts: {
sans: ['system-ui', 'Helvetica Neue', 'sans-serif'],
serif: ['Constantia', 'Georgia', 'serif'],
mono: ['Menlo', 'Courier New', 'monospace'],
},
},
variants: {
fonts: ['responsive'],
},
}
const result = resolveConfig([userConfig, defaultConfig])
expect(result).toEqual({
prefix: '-',
important: false,
separator: ':',
theme: {
fonts: {
sans: ['Comic Sans'],
serif: ['Constantia', 'Georgia', 'serif'],
mono: ['Menlo', 'Courier New', 'monospace'],
},
},
variants: {
fonts: ['responsive'],
},
})
})
test('the theme function can use a default value if the key is missing', () => {
const userConfig = {
theme: {
colors: {
red: 'red',
green: 'green',
blue: 'blue',
},
},
}
const defaultConfig = {
prefix: '-',
important: false,
separator: ':',
theme: {
colors: {
cyan: 'cyan',
magenta: 'magenta',
yellow: 'yellow',
},
borderColor: theme => ({
default: theme('colors.gray', 'currentColor'),
...theme('colors'),
}),
},
variants: {
borderColor: ['responsive', 'hover', 'focus'],
},
}
const result = resolveConfig([userConfig, defaultConfig])
expect(result).toEqual({
prefix: '-',
important: false,
separator: ':',
theme: {
colors: {
red: 'red',
green: 'green',
blue: 'blue',
},
borderColor: {
default: 'currentColor',
red: 'red',
green: 'green',
blue: 'blue',
},
},
variants: {
borderColor: ['responsive', 'hover', 'focus'],
},
})
})
test('the theme function can resolve function values', () => {
const userConfig = {
theme: {
textColor: theme => ({
lime: 'lime',
...theme('colors'),
}),
backgroundColor: theme => ({
orange: 'orange',
...theme('textColor'),
}),
borderColor: theme => theme('backgroundColor'),
},
}
const defaultConfig = {
prefix: '-',
important: false,
separator: ':',
theme: {
colors: {
red: 'red',
green: 'green',
blue: 'blue',
},
},
variants: {
backgroundColor: ['responsive', 'hover', 'focus'],
borderColor: ['responsive', 'hover', 'focus'],
},
}
const result = resolveConfig([userConfig, defaultConfig])
expect(result).toEqual({
prefix: '-',
important: false,
separator: ':',
theme: {
colors: {
red: 'red',
green: 'green',
blue: 'blue',
},
textColor: {
lime: 'lime',
red: 'red',
green: 'green',
blue: 'blue',
},
backgroundColor: {
lime: 'lime',
orange: 'orange',
red: 'red',
green: 'green',
blue: 'blue',
},
borderColor: {
lime: 'lime',
orange: 'orange',
red: 'red',
green: 'green',
blue: 'blue',
},
},
variants: {
backgroundColor: ['responsive', 'hover', 'focus'],
borderColor: ['responsive', 'hover', 'focus'],
},
})
})
test('theme values in the extend section are lazily evaluated', () => {
const userConfig = {
theme: {
colors: {
red: 'red',
green: 'green',
blue: 'blue',
},
extend: {
colors: {
orange: 'orange',
},
borderColor: theme => ({
foo: theme('colors.orange'),
bar: theme('colors.red'),
}),
},
},
}
const defaultConfig = {
prefix: '-',
important: false,
separator: ':',
theme: {
colors: {
cyan: 'cyan',
magenta: 'magenta',
yellow: 'yellow',
},
borderColor: theme => ({
default: theme('colors.yellow', 'currentColor'),
...theme('colors'),
}),
},
variants: {
borderColor: ['responsive', 'hover', 'focus'],
},
}
const result = resolveConfig([userConfig, defaultConfig])
expect(result).toEqual({
prefix: '-',
important: false,
separator: ':',
theme: {
colors: {
orange: 'orange',
red: 'red',
green: 'green',
blue: 'blue',
},
borderColor: {
default: 'currentColor',
foo: 'orange',
bar: 'red',
orange: 'orange',
red: 'red',
green: 'green',
blue: 'blue',
},
},
variants: {
borderColor: ['responsive', 'hover', 'focus'],
},
})
})
test('the original theme is not mutated', () => {
const userConfig = {
theme: {
extend: {
colors: {
orange: 'orange',
},
},
},
variants: {
borderColor: ['responsive', 'hover'],
},
}
const defaultConfig = {
prefix: '-',
important: false,
separator: ':',
theme: {
colors: {
cyan: 'cyan',
magenta: 'magenta',
yellow: 'yellow',
},
},
variants: {
backgroundColor: ['responsive', 'hover', 'focus'],
},
}
resolveConfig([userConfig, defaultConfig])
expect(userConfig).toEqual({
theme: {
extend: {
colors: {
orange: 'orange',
},
},
},
variants: {
borderColor: ['responsive', 'hover'],
},
})
})

View file

@ -1,6 +1,6 @@
import postcss from 'postcss'
import plugin from '../src/lib/substituteResponsiveAtRules'
import config from '../defaultConfig.stub.js'
import config from '../stubs/defaultConfig.stub.js'
function run(input, opts = config) {
return postcss([plugin(opts)]).process(input, { from: undefined })
@ -172,6 +172,136 @@ test('responsive variants are grouped', () => {
})
})
test('it can generate responsive variants for nested at-rules', () => {
const input = `
@responsive {
.banana { color: yellow; }
@supports(display: grid) {
.grid\\:banana { color: blue; }
}
}
`
const output = `
.banana {
color: yellow;
}
@supports(display: grid) {
.grid\\:banana {
color: blue;
}
}
@media (min-width: 500px) {
.sm\\:banana {
color: yellow;
}
@supports(display: grid) {
.sm\\:grid\\:banana {
color: blue;
}
}
}
@media (min-width: 1000px) {
.lg\\:banana {
color: yellow;
}
@supports(display: grid) {
.lg\\:grid\\:banana {
color: blue;
}
}
}
`
return run(input, {
theme: {
screens: {
sm: '500px',
lg: '1000px',
},
},
separator: ':',
}).then(result => {
expect(result.css).toMatchCss(output)
expect(result.warnings().length).toBe(0)
})
})
test('it can generate responsive variants for deeply nested at-rules', () => {
const input = `
@responsive {
.banana { color: yellow; }
@supports(display: grid) {
@supports(display: flex) {
.flex-grid\\:banana { color: blue; }
}
}
}
`
const output = `
.banana {
color: yellow;
}
@supports(display: grid) {
@supports(display: flex) {
.flex-grid\\:banana {
color: blue;
}
}
}
@media (min-width: 500px) {
.sm\\:banana {
color: yellow;
}
@supports(display: grid) {
@supports(display: flex) {
.sm\\:flex-grid\\:banana {
color: blue;
}
}
}
}
@media (min-width: 1000px) {
.lg\\:banana {
color: yellow;
}
@supports(display: grid) {
@supports(display: flex) {
.lg\\:flex-grid\\:banana {
color: blue;
}
}
}
}
`
return run(input, {
theme: {
screens: {
sm: '500px',
lg: '1000px',
},
},
separator: ':',
}).then(result => {
expect(result.css).toMatchCss(output)
expect(result.warnings().length).toBe(0)
})
})
test('screen prefix is only applied to the last class in a selector', () => {
const input = `
@responsive {

View file

@ -2,7 +2,7 @@ import fs from 'fs'
import path from 'path'
import postcss from 'postcss'
import tailwind from '../src/index'
import config from '../defaultConfig.js'
import config from '../stubs/defaultConfig.stub.js'
it('generates the right CSS', () => {
const inputPath = path.resolve(`${__dirname}/fixtures/tailwind-input.css`)

View file

@ -0,0 +1,47 @@
import postcss from 'postcss'
import plugin from '../src/lib/substituteScreenAtRules'
import config from '../stubs/defaultConfig.stub.js'
function run(input, opts = config) {
return postcss([plugin(opts)]).process(input, { from: undefined })
}
test('it can generate media queries from configured screen sizes', () => {
const input = `
@screen sm {
.banana { color: yellow; }
}
@screen md {
.banana { color: red; }
}
@screen lg {
.banana { color: green; }
}
`
const output = `
@media (min-width: 500px) {
.banana { color: yellow; }
}
@media (min-width: 750px) {
.banana { color: red; }
}
@media (min-width: 1000px) {
.banana { color: green; }
}
`
return run(input, {
theme: {
screens: {
sm: '500px',
md: '750px',
lg: '1000px',
},
},
separator: ':',
}).then(result => {
expect(result.css).toMatchCss(output)
expect(result.warnings().length).toBe(0)
})
})

View file

@ -5,9 +5,9 @@ function run(input, opts = {}) {
return postcss([plugin(opts)]).process(input, { from: undefined })
}
test('it looks up values in the config using dot notation', () => {
test('it looks up values in the theme using dot notation', () => {
const input = `
.banana { color: config('theme.colors.yellow'); }
.banana { color: theme('colors.yellow'); }
`
const output = `
@ -28,7 +28,7 @@ test('it looks up values in the config using dot notation', () => {
test('quotes are optional around the lookup path', () => {
const input = `
.banana { color: config(theme.colors.yellow); }
.banana { color: theme(colors.yellow); }
`
const output = `
@ -49,7 +49,7 @@ test('quotes are optional around the lookup path', () => {
test('a default value can be provided', () => {
const input = `
.cookieMonster { color: config('theme.colors.blue', #0000ff); }
.cookieMonster { color: theme('colors.blue', #0000ff); }
`
const output = `
@ -70,7 +70,7 @@ test('a default value can be provided', () => {
test('quotes are preserved around default values', () => {
const input = `
.heading { font-family: config('theme.fonts.sans', "Helvetica Neue"); }
.heading { font-family: theme('fonts.sans', "Helvetica Neue"); }
`
const output = `
@ -88,3 +88,24 @@ test('quotes are preserved around default values', () => {
expect(result.warnings().length).toBe(0)
})
})
test('an unquoted list is valid as a default value', () => {
const input = `
.heading { font-family: theme('fonts.sans', Helvetica, Arial, sans-serif); }
`
const output = `
.heading { font-family: Helvetica, Arial, sans-serif; }
`
return run(input, {
theme: {
fonts: {
serif: 'Constantia',
},
},
}).then(result => {
expect(result.css).toEqual(output)
expect(result.warnings().length).toBe(0)
})
})

View file

@ -1,7 +1,7 @@
import postcss from 'postcss'
import plugin from '../src/lib/substituteVariantsAtRules'
import config from '../defaultConfig.stub.js'
import processPlugins from '../src/util/processPlugins'
import config from '../stubs/defaultConfig.stub.js'
function run(input, opts = config) {
return postcss([plugin(opts, processPlugins(opts.plugins, opts))]).process(input, {
@ -218,6 +218,33 @@ test('variants are generated in the order specified', () => {
})
})
test('the default variant can be generated in a specified position', () => {
const input = `
@variants focus, active, default, hover {
.banana { color: yellow; }
.chocolate { color: brown; }
}
`
const output = `
.focus\\:banana:focus { color: yellow; }
.focus\\:chocolate:focus { color: brown; }
.active\\:banana:active { color: yellow; }
.active\\:chocolate:active { color: brown; }
.banana { color: yellow; }
.chocolate { color: brown; }
.hover\\:banana:hover { color: yellow; }
.hover\\:chocolate:hover { color: brown; }
`
return run(input, {
...config,
}).then(result => {
expect(result.css).toMatchCss(output)
expect(result.warnings().length).toBe(0)
})
})
test('plugin variants can modify rules using the raw PostCSS API', () => {
const input = `
@variants important {
@ -273,10 +300,43 @@ test('plugin variants can modify selectors with a simplified API', () => {
...config,
plugins: [
...config.plugins,
function({ addVariant }) {
function({ addVariant, e }) {
addVariant('first-child', ({ modifySelectors, separator }) => {
modifySelectors(({ className }) => {
return `.first-child${separator}${className}:first-child`
return `.${e(`first-child${separator}${className}`)}:first-child`
})
})
},
],
}).then(result => {
expect(result.css).toMatchCss(output)
expect(result.warnings().length).toBe(0)
})
})
test('plugin variants that use modify selectors need to manually escape the class name they are modifying', () => {
const input = `
@variants first-child {
.banana-1\\/2 { color: yellow; }
.chocolate-1\\.5 { color: brown; }
}
`
const output = `
.banana-1\\/2 { color: yellow; }
.chocolate-1\\.5 { color: brown; }
.first-child\\:banana-1\\/2:first-child { color: yellow; }
.first-child\\:chocolate-1\\.5:first-child { color: brown; }
`
return run(input, {
...config,
plugins: [
...config.plugins,
function({ addVariant, e }) {
addVariant('first-child', ({ modifySelectors, separator }) => {
modifySelectors(({ className }) => {
return `.${e(`first-child${separator}${className}`)}:first-child`
})
})
},
@ -308,13 +368,13 @@ test('plugin variants can wrap rules in another at-rule using the raw PostCSS AP
...config,
plugins: [
...config.plugins,
function({ addVariant }) {
function({ addVariant, e }) {
addVariant('supports-grid', ({ container, separator }) => {
const supportsRule = postcss.atRule({ name: 'supports', params: '(display: grid)' })
supportsRule.nodes = container.nodes
container.nodes = [supportsRule]
supportsRule.walkRules(rule => {
rule.selector = `.supports-grid${separator}${rule.selector.slice(1)}`
rule.selector = `.${e(`supports-grid${separator}${rule.selector.slice(1)}`)}`
})
})
},

1
base.css Normal file
View file

@ -0,0 +1 @@
@tailwind base;

View file

@ -1,475 +0,0 @@
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
/* Document
========================================================================== */
/**
* 1. Correct the line height in all browsers.
* 2. Prevent adjustments of font size after orientation changes in iOS.
*/
html {
line-height: 1.15; /* 1 */
-webkit-text-size-adjust: 100%; /* 2 */
}
/* Sections
========================================================================== */
/**
* Remove the margin in all browsers.
*/
body {
margin: 0;
}
/**
* Render the `main` element consistently in IE.
*/
main {
display: block;
}
/**
* Correct the font size and margin on `h1` elements within `section` and
* `article` contexts in Chrome, Firefox, and Safari.
*/
h1 {
font-size: 2em;
margin: 0.67em 0;
}
/* Grouping content
========================================================================== */
/**
* 1. Add the correct box sizing in Firefox.
* 2. Show the overflow in Edge and IE.
*/
hr {
box-sizing: content-box; /* 1 */
height: 0; /* 1 */
overflow: visible; /* 2 */
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
pre {
font-family: monospace, monospace; /* 1 */
font-size: 1em; /* 2 */
}
/* Text-level semantics
========================================================================== */
/**
* Remove the gray background on active links in IE 10.
*/
a {
background-color: transparent;
}
/**
* 1. Remove the bottom border in Chrome 57-
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
*/
abbr[title] {
border-bottom: none; /* 1 */
text-decoration: underline; /* 2 */
text-decoration: underline dotted; /* 2 */
}
/**
* Add the correct font weight in Chrome, Edge, and Safari.
*/
b,
strong {
font-weight: bolder;
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
code,
kbd,
samp {
font-family: monospace, monospace; /* 1 */
font-size: 1em; /* 2 */
}
/**
* Add the correct font size in all browsers.
*/
small {
font-size: 80%;
}
/**
* Prevent `sub` and `sup` elements from affecting the line height in
* all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
/* Embedded content
========================================================================== */
/**
* Remove the border on images inside links in IE 10.
*/
img {
border-style: none;
}
/* Forms
========================================================================== */
/**
* 1. Change the font styles in all browsers.
* 2. Remove the margin in Firefox and Safari.
*/
button,
input,
optgroup,
select,
textarea {
font-family: inherit; /* 1 */
font-size: 100%; /* 1 */
line-height: 1.15; /* 1 */
margin: 0; /* 2 */
}
/**
* Show the overflow in IE.
* 1. Show the overflow in Edge.
*/
button,
input { /* 1 */
overflow: visible;
}
/**
* Remove the inheritance of text transform in Edge, Firefox, and IE.
* 1. Remove the inheritance of text transform in Firefox.
*/
button,
select { /* 1 */
text-transform: none;
}
/**
* Correct the inability to style clickable types in iOS and Safari.
*/
button,
[type="button"],
[type="reset"],
[type="submit"] {
-webkit-appearance: button;
}
/**
* Remove the inner border and padding in Firefox.
*/
button::-moz-focus-inner,
[type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner {
border-style: none;
padding: 0;
}
/**
* Restore the focus styles unset by the previous rule.
*/
button:-moz-focusring,
[type="button"]:-moz-focusring,
[type="reset"]:-moz-focusring,
[type="submit"]:-moz-focusring {
outline: 1px dotted ButtonText;
}
/**
* Correct the padding in Firefox.
*/
fieldset {
padding: 0.35em 0.75em 0.625em;
}
/**
* 1. Correct the text wrapping in Edge and IE.
* 2. Correct the color inheritance from `fieldset` elements in IE.
* 3. Remove the padding so developers are not caught out when they zero out
* `fieldset` elements in all browsers.
*/
legend {
box-sizing: border-box; /* 1 */
color: inherit; /* 2 */
display: table; /* 1 */
max-width: 100%; /* 1 */
padding: 0; /* 3 */
white-space: normal; /* 1 */
}
/**
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
*/
progress {
vertical-align: baseline;
}
/**
* Remove the default vertical scrollbar in IE 10+.
*/
textarea {
overflow: auto;
}
/**
* 1. Add the correct box sizing in IE 10.
* 2. Remove the padding in IE 10.
*/
[type="checkbox"],
[type="radio"] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
}
/**
* Correct the cursor style of increment and decrement buttons in Chrome.
*/
[type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button {
height: auto;
}
/**
* 1. Correct the odd appearance in Chrome and Safari.
* 2. Correct the outline style in Safari.
*/
[type="search"] {
-webkit-appearance: textfield; /* 1 */
outline-offset: -2px; /* 2 */
}
/**
* Remove the inner padding in Chrome and Safari on macOS.
*/
[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* 1. Correct the inability to style clickable types in iOS and Safari.
* 2. Change font properties to `inherit` in Safari.
*/
::-webkit-file-upload-button {
-webkit-appearance: button; /* 1 */
font: inherit; /* 2 */
}
/* Interactive
========================================================================== */
/*
* Add the correct display in Edge, IE 10+, and Firefox.
*/
details {
display: block;
}
/*
* Add the correct display in all browsers.
*/
summary {
display: list-item;
}
/* Misc
========================================================================== */
/**
* Add the correct display in IE 10+.
*/
template {
display: none;
}
/**
* Add the correct display in IE 10.
*/
[hidden] {
display: none;
}
/**
* 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.
*/
/**
* 1. Prevent padding and border from affecting element width
* https://goo.gl/pYtbK7
* 2. Change the default font family in all browsers (opinionated)
*/
html {
box-sizing: border-box; /* 1 */
font-family: sans-serif; /* 2 */
}
*,
*::before,
*::after {
box-sizing: inherit;
}
/**
* Removes the default spacing and border for appropriate elements.
*/
blockquote,
dl,
dd,
h1,
h2,
h3,
h4,
h5,
h6,
figure,
p,
pre {
margin: 0;
}
button {
background: transparent;
padding: 0;
}
/**
* 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 {
margin: 0;
}
/**
* Tailwind custom reset styles
*/
/**
* 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 {
border-width: 0;
border-style: solid;
border-color: config('theme.borderColors.default', currentColor);
}
/**
* 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 {
color: inherit;
opacity: 0.5;
}
button,
[role="button"] {
cursor: pointer;
}
table {
border-collapse: collapse;
}

View file

@ -1,3 +1,4 @@
module.exports = function() {
return require('lodash').cloneDeep(require('./defaultConfig.stub.js'))
}
const cloneDeep = require('lodash/cloneDeep')
const defaultConfig = require('./stubs/defaultConfig.stub.js')
module.exports = cloneDeep(defaultConfig)

View file

@ -1,70 +0,0 @@
const defaultTheme = require('./defaultTheme')()
module.exports = {
prefix: '',
important: false,
separator: ':',
theme: defaultTheme,
variants: {
appearance: ['responsive'],
backgroundAttachment: ['responsive'],
backgroundColors: ['responsive', 'hover', 'focus'],
backgroundPosition: ['responsive'],
backgroundRepeat: ['responsive'],
backgroundSize: ['responsive'],
borderCollapse: [],
borderColors: ['responsive', 'hover', 'focus'],
borderRadius: ['responsive'],
borderStyle: ['responsive'],
borderWidths: ['responsive'],
cursor: ['responsive'],
display: ['responsive'],
flexbox: ['responsive'],
float: ['responsive'],
fonts: ['responsive'],
fontWeights: ['responsive', 'hover', 'focus'],
height: ['responsive'],
leading: ['responsive'],
lists: ['responsive'],
margin: ['responsive'],
maxHeight: ['responsive'],
maxWidth: ['responsive'],
minHeight: ['responsive'],
minWidth: ['responsive'],
negativeMargin: ['responsive'],
objectFit: [],
objectPosition: [],
opacity: ['responsive'],
outline: ['focus'],
overflow: ['responsive'],
padding: ['responsive'],
pointerEvents: ['responsive'],
position: ['responsive'],
resize: ['responsive'],
shadows: ['responsive', 'hover', 'focus'],
svgFill: [],
svgStroke: [],
tableLayout: ['responsive'],
textAlign: ['responsive'],
textColors: ['responsive', 'hover', 'focus'],
textSizes: ['responsive'],
textStyle: ['responsive', 'hover', 'focus'],
tracking: ['responsive'],
userSelect: ['responsive'],
verticalAlign: ['responsive'],
visibility: ['responsive'],
whitespace: ['responsive'],
width: ['responsive'],
zIndex: ['responsive'],
},
corePlugins: {
objectFit: false,
objectPosition: false,
},
plugins: [
require('./plugins/container')({
// center: true,
// padding: '1rem',
}),
],
}

View file

@ -1,354 +1,4 @@
module.exports = function() {
const colors = {
transparent: 'transparent',
const cloneDeep = require('lodash/cloneDeep')
const defaultConfig = require('./stubs/defaultConfig.stub.js')
black: '#22292f',
'grey-darkest': '#3d4852',
'grey-darker': '#606f7b',
'grey-dark': '#8795a1',
grey: '#b8c2cc',
'grey-light': '#dae1e7',
'grey-lighter': '#f1f5f8',
'grey-lightest': '#f8fafc',
white: '#ffffff',
'red-darkest': '#3b0d0c',
'red-darker': '#621b18',
'red-dark': '#cc1f1a',
red: '#e3342f',
'red-light': '#ef5753',
'red-lighter': '#f9acaa',
'red-lightest': '#fcebea',
'orange-darkest': '#462a16',
'orange-darker': '#613b1f',
'orange-dark': '#de751f',
orange: '#f6993f',
'orange-light': '#faad63',
'orange-lighter': '#fcd9b6',
'orange-lightest': '#fff5eb',
'yellow-darkest': '#453411',
'yellow-darker': '#684f1d',
'yellow-dark': '#f2d024',
yellow: '#ffed4a',
'yellow-light': '#fff382',
'yellow-lighter': '#fff9c2',
'yellow-lightest': '#fcfbeb',
'green-darkest': '#0f2f21',
'green-darker': '#1a4731',
'green-dark': '#1f9d55',
green: '#38c172',
'green-light': '#51d88a',
'green-lighter': '#a2f5bf',
'green-lightest': '#e3fcec',
'teal-darkest': '#0d3331',
'teal-darker': '#20504f',
'teal-dark': '#38a89d',
teal: '#4dc0b5',
'teal-light': '#64d5ca',
'teal-lighter': '#a0f0ed',
'teal-lightest': '#e8fffe',
'blue-darkest': '#12283a',
'blue-darker': '#1c3d5a',
'blue-dark': '#2779bd',
blue: '#3490dc',
'blue-light': '#6cb2eb',
'blue-lighter': '#bcdefa',
'blue-lightest': '#eff8ff',
'indigo-darkest': '#191e38',
'indigo-darker': '#2f365f',
'indigo-dark': '#5661b3',
indigo: '#6574cd',
'indigo-light': '#7886d7',
'indigo-lighter': '#b2b7ff',
'indigo-lightest': '#e6e8ff',
'purple-darkest': '#21183c',
'purple-darker': '#382b5f',
'purple-dark': '#794acf',
purple: '#9561e2',
'purple-light': '#a779e9',
'purple-lighter': '#d6bbfc',
'purple-lightest': '#f3ebff',
'pink-darkest': '#451225',
'pink-darker': '#6f213f',
'pink-dark': '#eb5286',
pink: '#f66d9b',
'pink-light': '#fa7ea8',
'pink-lighter': '#ffbbca',
'pink-lightest': '#ffebef',
}
return {
colors,
screens: {
sm: '576px',
md: '768px',
lg: '992px',
xl: '1200px',
},
fonts: {
sans: [
'system-ui',
'BlinkMacSystemFont',
'-apple-system',
'Segoe UI',
'Roboto',
'Oxygen',
'Ubuntu',
'Cantarell',
'Fira Sans',
'Droid Sans',
'Helvetica Neue',
'sans-serif',
],
serif: [
'Constantia',
'Lucida Bright',
'Lucidabright',
'Lucida Serif',
'Lucida',
'DejaVu Serif',
'Bitstream Vera Serif',
'Liberation Serif',
'Georgia',
'serif',
],
mono: ['Menlo', 'Monaco', 'Consolas', 'Liberation Mono', 'Courier New', 'monospace'],
},
textSizes: {
xs: '.75rem', // 12px
sm: '.875rem', // 14px
base: '1rem', // 16px
lg: '1.125rem', // 18px
xl: '1.25rem', // 20px
'2xl': '1.5rem', // 24px
'3xl': '1.875rem', // 30px
'4xl': '2.25rem', // 36px
'5xl': '3rem', // 48px
},
fontWeights: {
hairline: 100,
thin: 200,
light: 300,
normal: 400,
medium: 500,
semibold: 600,
bold: 700,
extrabold: 800,
black: 900,
},
leading: {
none: 1,
tight: 1.25,
normal: 1.5,
loose: 2,
},
tracking: {
tight: '-0.05em',
normal: '0',
wide: '0.05em',
},
textColors: colors,
backgroundColors: colors,
backgroundSize: {
auto: 'auto',
cover: 'cover',
contain: 'contain',
},
borderWidths: {
default: '1px',
'0': '0',
'2': '2px',
'4': '4px',
'8': '8px',
},
borderColors: global.Object.assign({ default: colors['grey-light'] }, colors),
borderRadius: {
none: '0',
sm: '.125rem',
default: '.25rem',
lg: '.5rem',
full: '9999px',
},
width: {
auto: 'auto',
px: '1px',
'1': '0.25rem',
'2': '0.5rem',
'3': '0.75rem',
'4': '1rem',
'5': '1.25rem',
'6': '1.5rem',
'8': '2rem',
'10': '2.5rem',
'12': '3rem',
'16': '4rem',
'24': '6rem',
'32': '8rem',
'48': '12rem',
'64': '16rem',
'1/2': '50%',
'1/3': '33.33333%',
'2/3': '66.66667%',
'1/4': '25%',
'2/4': '50%',
'3/4': '75%',
'1/5': '20%',
'2/5': '40%',
'3/5': '60%',
'4/5': '80%',
'1/6': '16.66667%',
'2/6': '33.33333%',
'3/6': '50%',
'4/6': '66.66667%',
'5/6': '83.33333%',
'1/12': '8.33333%',
'2/12': '16.66667%',
'3/12': '25%',
'4/12': '33.33333%',
'5/12': '41.66667%',
'6/12': '50%',
'7/12': '58.33333%',
'8/12': '66.66667%',
'9/12': '75%',
'10/12': '83.33333%',
'11/12': '91.66667%',
full: '100%',
screen: '100vw',
},
height: {
auto: 'auto',
px: '1px',
'1': '0.25rem',
'2': '0.5rem',
'3': '0.75rem',
'4': '1rem',
'5': '1.25rem',
'6': '1.5rem',
'8': '2rem',
'10': '2.5rem',
'12': '3rem',
'16': '4rem',
'24': '6rem',
'32': '8rem',
'48': '12rem',
'64': '16rem',
full: '100%',
screen: '100vh',
},
minWidth: {
'0': '0',
full: '100%',
},
minHeight: {
'0': '0',
full: '100%',
screen: '100vh',
},
maxWidth: {
xs: '20rem',
sm: '30rem',
md: '40rem',
lg: '50rem',
xl: '60rem',
'2xl': '70rem',
'3xl': '80rem',
'4xl': '90rem',
'5xl': '100rem',
full: '100%',
},
maxHeight: {
full: '100%',
screen: '100vh',
},
padding: {
px: '1px',
'0': '0',
'1': '0.25rem',
'2': '0.5rem',
'3': '0.75rem',
'4': '1rem',
'5': '1.25rem',
'6': '1.5rem',
'8': '2rem',
'10': '2.5rem',
'12': '3rem',
'16': '4rem',
'20': '5rem',
'24': '6rem',
'32': '8rem',
},
margin: {
auto: 'auto',
px: '1px',
'0': '0',
'1': '0.25rem',
'2': '0.5rem',
'3': '0.75rem',
'4': '1rem',
'5': '1.25rem',
'6': '1.5rem',
'8': '2rem',
'10': '2.5rem',
'12': '3rem',
'16': '4rem',
'20': '5rem',
'24': '6rem',
'32': '8rem',
},
negativeMargin: {
px: '1px',
'0': '0',
'1': '0.25rem',
'2': '0.5rem',
'3': '0.75rem',
'4': '1rem',
'5': '1.25rem',
'6': '1.5rem',
'8': '2rem',
'10': '2.5rem',
'12': '3rem',
'16': '4rem',
'20': '5rem',
'24': '6rem',
'32': '8rem',
},
shadows: {
default: '0 2px 4px 0 rgba(0,0,0,0.10)',
md: '0 4px 8px 0 rgba(0,0,0,0.12), 0 2px 4px 0 rgba(0,0,0,0.08)',
lg: '0 15px 30px 0 rgba(0,0,0,0.11), 0 5px 15px 0 rgba(0,0,0,0.08)',
inner: 'inset 0 2px 4px 0 rgba(0,0,0,0.06)',
outline: '0 0 0 3px rgba(52,144,220,0.5)',
none: 'none',
},
zIndex: {
auto: 'auto',
'0': 0,
'10': 10,
'20': 20,
'30': 30,
'40': 40,
'50': 50,
},
opacity: {
'0': '0',
'25': '.25',
'50': '.5',
'75': '.75',
'100': '1',
},
svgFill: {
current: 'currentColor',
},
svgStroke: {
current: 'currentColor',
},
}
}
module.exports = cloneDeep(defaultConfig.theme)

View file

@ -0,0 +1,21 @@
import fs from 'fs'
import path from 'path'
import rimraf from 'rimraf'
const tmpPath = path.resolve(__dirname, '../__tmp')
export default function(callback) {
return new Promise(resolve => {
const currentPath = process.cwd()
rimraf.sync(tmpPath)
fs.mkdirSync(tmpPath)
process.chdir(tmpPath)
callback().then(() => {
process.chdir(currentPath)
rimraf(tmpPath, resolve)
})
})
}

7729
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
{
"name": "tailwindcss",
"version": "0.7.3",
"version": "1.0.0-beta.5",
"description": "A utility-first CSS framework for rapidly building custom user interfaces.",
"license": "MIT",
"main": "lib/index.js",
@ -18,44 +18,40 @@
],
"scripts": {
"prebabelify": "rimraf lib",
"babelify": "babel src --out-dir lib",
"babelify": "babel src --out-dir lib --copy-files",
"prepare": "npm run babelify && babel-node src/build.js",
"style": "eslint .",
"test": "jest && eslint ."
},
"devDependencies": {
"babel-cli": "^6.6.5",
"babel-core": "^6.7.2",
"babel-jest": "^20.0.3",
"babel-preset-env": "^1.0.0",
"babel-preset-react": "^6.24.1",
"babel-preset-stage-2": "^6.24.1",
"babel-preset-stage-3": "^6.24.1",
"@babel/cli": "^7.0.0",
"@babel/core": "^7.0.0",
"@babel/node": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"babel-jest": "^24.3.1",
"clean-css": "^4.1.9",
"eslint": "^4.10.0",
"eslint": "^5.15.1",
"eslint-config-postcss": "^2.0.2",
"eslint-config-prettier": "^2.7.0",
"eslint-plugin-prettier": "^2.3.1",
"jest": "^23.6.0",
"eslint-config-prettier": "^4.1.0",
"eslint-plugin-prettier": "^3.0.1",
"jest": "^24.3.1",
"prettier": "^1.7.4",
"rimraf": "^2.6.1"
"rimraf": "^2.6.3"
},
"dependencies": {
"autoprefixer": "^9.4.5",
"bytes": "^3.0.0",
"chalk": "^2.4.1",
"css.escape": "^1.5.1",
"fs-extra": "^4.0.2",
"lodash": "^4.17.5",
"fs-extra": "^7.0.1",
"lodash": "^4.17.11",
"node-emoji": "^1.8.1",
"perfectionist": "^2.4.0",
"normalize.css": "^8.0.1",
"postcss": "^7.0.11",
"postcss-functions": "^3.0.0",
"postcss-js": "^2.0.0",
"postcss-nested": "^4.1.1",
"postcss-selector-parser": "^5.0.0",
"pretty-hrtime": "^1.0.3",
"strip-comments": "^1.0.2"
"postcss-selector-parser": "^6.0.0",
"pretty-hrtime": "^1.0.3"
},
"browserslist": [
"> 1%"
@ -63,25 +59,24 @@
"babel": {
"presets": [
[
"env",
"@babel/preset-env",
{
"targets": {
"node": "6.9.0"
"node": "8.9.0"
}
}
],
"stage-2",
"stage-3",
"react"
]
]
},
"jest": {
"setupTestFrameworkScriptFile": "<rootDir>/jest/customMatchers.js",
"setupFilesAfterEnv": [
"<rootDir>/jest/customMatchers.js"
],
"testPathIgnorePatterns": [
"<rootDir>/__tests__/fixtures/"
]
},
"engines": {
"node": ">=6.9.0"
"node": ">=8.9.0"
}
}

View file

@ -1 +0,0 @@
module.exports = require('../lib/plugins/appearance').default

View file

@ -1 +0,0 @@
module.exports = require('../lib/plugins/backgroundAttachment').default

View file

@ -1 +0,0 @@
module.exports = require('../lib/plugins/backgroundColors').default

View file

@ -1 +0,0 @@
module.exports = require('../lib/plugins/backgroundPosition').default

View file

@ -1 +0,0 @@
module.exports = require('../lib/plugins/backgroundRepeat').default

View file

@ -1 +0,0 @@
module.exports = require('../lib/plugins/backgroundSize').default

View file

@ -1 +0,0 @@
module.exports = require('../lib/plugins/borderCollapse').default

View file

@ -1 +0,0 @@
module.exports = require('../lib/plugins/borderColors').default

View file

@ -1 +0,0 @@
module.exports = require('../lib/plugins/borderRadius').default

View file

@ -1 +0,0 @@
module.exports = require('../lib/plugins/borderStyle').default

View file

@ -1 +0,0 @@
module.exports = require('../lib/plugins/borderWidths').default

View file

@ -1 +0,0 @@
module.exports = require('../lib/plugins/container')

View file

@ -1 +0,0 @@
module.exports = require('../lib/plugins/cursor').default

View file

@ -1 +0,0 @@
module.exports = require('../lib/plugins/display').default

View file

@ -1 +0,0 @@
module.exports = require('../lib/plugins/flexbox').default

View file

@ -1 +0,0 @@
module.exports = require('../lib/plugins/float').default

View file

@ -1 +0,0 @@
module.exports = require('../lib/plugins/fontWeights').default

View file

@ -1 +0,0 @@
module.exports = require('../lib/plugins/fonts').default

View file

@ -1 +0,0 @@
module.exports = require('../lib/plugins/height').default

View file

View file

View file

@ -1 +0,0 @@
module.exports = require('../lib/plugins/leading').default

View file

@ -1 +0,0 @@
module.exports = require('../lib/plugins/lists').default

View file

@ -1 +0,0 @@
module.exports = require('../lib/plugins/margin').default

View file

@ -1 +0,0 @@
module.exports = require('../lib/plugins/maxHeight').default

View file

@ -1 +0,0 @@
module.exports = require('../lib/plugins/maxWidth').default

View file

@ -1 +0,0 @@
module.exports = require('../lib/plugins/minHeight').default

View file

@ -1 +0,0 @@
module.exports = require('../lib/plugins/minWidth').default

View file

@ -1 +0,0 @@
module.exports = require('../lib/plugins/negativeMargin').default

View file

@ -1 +0,0 @@
module.exports = require('../lib/plugins/objectFit').default

View file

@ -1 +0,0 @@
module.exports = require('../lib/plugins/objectPosition').default

View file

@ -1 +0,0 @@
module.exports = require('../lib/plugins/opacity').default

View file

@ -1 +0,0 @@
module.exports = require('../lib/plugins/outline').default

View file

@ -1 +0,0 @@
module.exports = require('../lib/plugins/overflow').default

View file

@ -1 +0,0 @@
module.exports = require('../lib/plugins/padding').default

View file

@ -1 +0,0 @@
module.exports = require('../lib/plugins/pointerEvents').default

View file

@ -1 +0,0 @@
module.exports = require('../lib/plugins/position').default

View file

@ -1 +0,0 @@
module.exports = require('../lib/plugins/resize').default

View file

@ -1 +0,0 @@
module.exports = require('../lib/plugins/shadows').default

View file

@ -1 +0,0 @@
module.exports = require('../lib/plugins/svgFill').default

View file

@ -1 +0,0 @@
module.exports = require('../lib/plugins/svgStroke').default

View file

@ -1 +0,0 @@
module.exports = require('../lib/plugins/tableLayout').default

View file

@ -1 +0,0 @@
module.exports = require('../lib/plugins/textAlign').default

View file

@ -1 +0,0 @@
module.exports = require('../lib/plugins/textColors').default

View file

@ -1 +0,0 @@
module.exports = require('../lib/plugins/textSizes').default

View file

@ -1 +0,0 @@
module.exports = require('../lib/plugins/textStyle').default

View file

@ -1 +0,0 @@
module.exports = require('../lib/plugins/tracking').default

View file

@ -1 +0,0 @@
module.exports = require('../lib/plugins/userSelect').default

View file

@ -1 +0,0 @@
module.exports = require('../lib/plugins/verticalAlign').default

View file

@ -1 +0,0 @@
module.exports = require('../lib/plugins/visibility').default

View file

@ -1 +0,0 @@
module.exports = require('../lib/plugins/whitespace').default

View file

@ -1 +0,0 @@
module.exports = require('../lib/plugins/width').default

View file

@ -1 +0,0 @@
module.exports = require('../lib/plugins/zIndex').default

View file

@ -1 +0,0 @@
@tailwind preflight;

6
resolveConfig.js Normal file
View file

@ -0,0 +1,6 @@
const resolveConfigObjects = require('./lib/util/resolveConfig').default
const defaultConfig = require('./stubs/defaultConfig.stub.js')
module.exports = function resolveConfig(config) {
return resolveConfigObjects([config, defaultConfig])
}

View file

@ -39,7 +39,7 @@ function buildDistFile(filename) {
console.info('Building Tailwind!')
Promise.all([
buildDistFile('preflight'),
buildDistFile('base'),
buildDistFile('components'),
buildDistFile('utilities'),
buildDistFile('tailwind'),

46
src/cli/colors.js Normal file
View file

@ -0,0 +1,46 @@
import chalk from 'chalk'
/**
* Applies colors to emphasize
*
* @param {...string} msgs
*/
export function bold(...msgs) {
return chalk.bold(...msgs)
}
/**
* Applies colors to inform
*
* @param {...string} msgs
*/
export function info(...msgs) {
return chalk.bold.cyan(...msgs)
}
/**
* Applies colors to signify error
*
* @param {...string} msgs
*/
export function error(...msgs) {
return chalk.bold.red(...msgs)
}
/**
* Applies colors to represent a command
*
* @param {...string} msgs
*/
export function command(...msgs) {
return chalk.bold.magenta(...msgs)
}
/**
* Applies colors to represent a file
*
* @param {...string} msgs
*/
export function file(...msgs) {
return chalk.bold.magenta(...msgs)
}

View file

@ -1,12 +1,12 @@
import autoprefixer from 'autoprefixer'
import bytes from 'bytes'
import chalk from 'chalk'
import postcss from 'postcss'
import prettyHrtime from 'pretty-hrtime'
import tailwind from '../..'
import commands from '.'
import compile from '../compile'
import * as colors from '../colors'
import * as emoji from '../emoji'
import * as utils from '../utils'
@ -57,68 +57,40 @@ function stopWithHelp(...msgs) {
utils.die()
}
/**
* Compiles CSS file.
*
* @param {string} inputFile
* @param {string} configFile
* @param {string} outputFile
* @param {boolean} autoprefix
* @return {Promise}
*/
function build(inputFile, configFile, outputFile, autoprefix) {
const css = utils.readFile(inputFile)
return new Promise((resolve, reject) => {
postcss([tailwind(configFile)].concat(autoprefix ? [autoprefixer] : []))
.process(css, {
from: inputFile,
to: outputFile,
})
.then(resolve)
.catch(reject)
})
}
/**
* Compiles CSS file and writes it to stdout.
*
* @param {string} inputFile
* @param {string} configFile
* @param {string} outputFile
* @param {boolean} autoprefix
* @param {CompileOptions} compileOptions
* @return {Promise}
*/
function buildToStdout(inputFile, configFile, outputFile, autoprefix) {
return build(inputFile, configFile, outputFile, autoprefix).then(result =>
process.stdout.write(result.css)
)
function buildToStdout(compileOptions) {
return compile(compileOptions).then(result => process.stdout.write(result.css))
}
/**
* Compiles CSS file and writes it to a file.
*
* @param {string} inputFile
* @param {string} configFile
* @param {string} outputFile
* @param {boolean} autoprefix
* @param {CompileOptions} compileOptions
* @param {int[]} startTime
* @return {Promise}
*/
function buildToFile(inputFile, configFile, outputFile, autoprefix, startTime) {
function buildToFile(compileOptions, startTime) {
const inputFileSimplePath = utils.getSimplePath(compileOptions.inputFile)
const outputFileSimplePath = utils.getSimplePath(compileOptions.outputFile)
utils.header()
utils.log()
utils.log(emoji.go, 'Building...', chalk.bold.cyan(inputFile))
utils.log(emoji.go, 'Building...', colors.file(inputFileSimplePath))
return build(inputFile, configFile, outputFile, autoprefix).then(result => {
utils.writeFile(outputFile, result.css)
return compile(compileOptions).then(result => {
utils.writeFile(compileOptions.outputFile, result.css)
const prettyTime = prettyHrtime(process.hrtime(startTime))
utils.log()
utils.log(emoji.yes, 'Finished in', chalk.bold.magenta(prettyTime))
utils.log(emoji.pack, 'Size:', chalk.bold.magenta(bytes(result.css.length)))
utils.log(emoji.disk, 'Saved to', chalk.bold.cyan(outputFile))
utils.log(emoji.yes, 'Finished in', colors.info(prettyTime))
utils.log(emoji.pack, 'Size:', colors.info(bytes(result.css.length)))
utils.log(emoji.disk, 'Saved to', colors.file(outputFileSimplePath))
utils.footer()
})
}
@ -137,17 +109,25 @@ export function run(cliParams, cliOptions) {
const configFile = cliOptions.config && cliOptions.config[0]
const outputFile = cliOptions.output && cliOptions.output[0]
const autoprefix = !cliOptions.noAutoprefixer
const inputFileSimplePath = utils.getSimplePath(inputFile)
const configFileSimplePath = utils.getSimplePath(configFile)
!inputFile && stopWithHelp('CSS file is required.')
!utils.exists(inputFile) && stop(chalk.bold.magenta(inputFile), 'does not exist.')
!utils.exists(inputFile) && stop(colors.file(inputFileSimplePath), 'does not exist.')
configFile &&
!utils.exists(configFile) &&
stop(chalk.bold.magenta(configFile), 'does not exist.')
stop(colors.file(configFileSimplePath), 'does not exist.')
const compileOptions = {
inputFile,
outputFile,
plugins: [tailwind(configFile)].concat(autoprefix ? [autoprefixer] : []),
}
const buildPromise = outputFile
? buildToFile(inputFile, configFile, outputFile, autoprefix, startTime)
: buildToStdout(inputFile, configFile, outputFile, autoprefix)
? buildToFile(compileOptions, startTime)
: buildToStdout(compileOptions)
buildPromise.then(resolve).catch(reject)
})

View file

@ -1,8 +1,8 @@
import chalk from 'chalk'
import { forEach, map, padEnd } from 'lodash'
import commands from '.'
import * as constants from '../constants'
import * as constants from '../../constants'
import * as colors from '../colors'
import * as utils from '../utils'
export const usage = 'help [command]'
@ -18,11 +18,11 @@ export function forApp() {
utils.log()
utils.log('Usage:')
utils.log(' ', chalk.bold(constants.cli + ' <command> [options]'))
utils.log(' ', colors.bold(constants.cli + ' <command> [options]'))
utils.log()
utils.log('Commands:')
forEach(commands, command => {
utils.log(' ', chalk.bold(padEnd(command.usage, pad)), command.description)
utils.log(' ', colors.bold(padEnd(command.usage, pad)), command.description)
})
}
@ -34,10 +34,10 @@ export function forApp() {
export function forCommand(command) {
utils.log()
utils.log('Usage:')
utils.log(' ', chalk.bold(constants.cli, command.usage))
utils.log(' ', colors.bold(constants.cli, command.usage))
utils.log()
utils.log('Description:')
utils.log(' ', chalk.bold(command.description))
utils.log(' ', colors.bold(command.description))
if (command.options) {
const pad = Math.max(...map(command.options, 'usage.length')) + PADDING_SIZE
@ -45,7 +45,7 @@ export function forCommand(command) {
utils.log()
utils.log('Options:')
forEach(command.options, option => {
utils.log(' ', chalk.bold(padEnd(option.usage, pad)), option.description)
utils.log(' ', colors.bold(padEnd(option.usage, pad)), option.description)
})
}
}
@ -56,7 +56,7 @@ export function forCommand(command) {
* @param {string} commandName
*/
export function invalidCommand(commandName) {
utils.error('Invalid command:', chalk.bold.magenta(commandName))
utils.error('Invalid command:', colors.command(commandName))
forApp()
utils.die()
}

View file

@ -1,22 +1,22 @@
import chalk from 'chalk'
import * as constants from '../constants'
import * as constants from '../../constants'
import * as colors from '../colors'
import * as emoji from '../emoji'
import * as utils from '../utils'
export const usage = 'init [file]'
export const description =
'Creates Tailwind config file. Default: ' + chalk.bold.magenta(constants.defaultConfigFile)
'Creates Tailwind config file. Default: ' +
colors.file(utils.getSimplePath(constants.defaultConfigFile))
export const options = [
{
usage: '--no-comments',
description: 'Omit comments from the config file.',
usage: '--full',
description: 'Generate complete configuration file.',
},
]
export const optionMap = {
noComments: ['no-comments'],
full: ['full'],
}
/**
@ -30,21 +30,18 @@ export function run(cliParams, cliOptions) {
return new Promise(resolve => {
utils.header()
const noComments = cliOptions.noComments
const full = cliOptions.full
const file = cliParams[0] || constants.defaultConfigFile
const simplePath = utils.getSimplePath(file)
utils.exists(file) && utils.die(chalk.bold.magenta(file), 'already exists.')
utils.exists(file) && utils.die(colors.file(simplePath), 'already exists.')
let stub = utils
.readFile(constants.configStubFile)
.replace("require('./plugins/container')", "require('tailwindcss/plugins/container')")
const stubFile = full ? constants.defaultConfigStubFile : constants.simpleConfigStubFile
noComments && (stub = utils.stripBlockComments(stub))
utils.writeFile(file, stub)
utils.copyFile(stubFile, file)
utils.log()
utils.log(emoji.yes, 'Created Tailwind config file:', chalk.bold.magenta(file))
utils.log(emoji.yes, 'Created Tailwind config file:', colors.file(simplePath))
utils.footer()

Some files were not shown because too many files have changed in this diff Show more