Skip to content

Commit

Permalink
Adds support for tailwind.config.cjs files (#3181)
Browse files Browse the repository at this point in the history
* feat: automatically init and resolve tailwind.config.cjs files

* test: add tests for default tailwind.config.cjs resolution

* fix `cjsConfigFile` constant

* Fix JSDoc type of isModule
  • Loading branch information
natemoo-re committed Feb 7, 2021
1 parent b281535 commit e86b586
Show file tree
Hide file tree
Showing 8 changed files with 150 additions and 19 deletions.
22 changes: 22 additions & 0 deletions __tests__/cli.test.js
Expand Up @@ -8,6 +8,7 @@ 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 esmPackageJsonPath = path.resolve(__dirname, 'fixtures/esm-package.json')
const defaultConfigFixture = utils.readFile(constants.defaultConfigStubFile)
const simpleConfigFixture = utils.readFile(constants.simpleConfigStubFile)
const defaultPostCssConfigFixture = utils.readFile(constants.defaultPostCssConfigStubFile)
Expand Down Expand Up @@ -47,6 +48,27 @@ describe('cli', () => {
})
})

it('creates a .cjs Tailwind config file inside of an ESM project', () => {
return runInTempDirectory(() => {
utils.writeFile('package.json', utils.readFile(esmPackageJsonPath))
return cli(['init']).then(() => {
expect(utils.readFile(constants.cjsConfigFile)).toEqual(simpleConfigFixture)
})
})
})

it('creates a .cjs Tailwind config file and a postcss.config.cjs file inside of an ESM project', () => {
return runInTempDirectory(() => {
utils.writeFile('package.json', utils.readFile(esmPackageJsonPath))
return cli(['init', '-p']).then(() => {
expect(utils.readFile(constants.cjsConfigFile)).toEqual(simpleConfigFixture)
expect(utils.readFile(constants.cjsPostCssConfigFile)).toEqual(
defaultPostCssConfigFixture
)
})
})
})

it('creates a Tailwind config file in a custom location', () => {
return runInTempDirectory(() => {
return cli(['init', 'custom.js']).then(() => {
Expand Down
80 changes: 79 additions & 1 deletion __tests__/customConfig.test.js
Expand Up @@ -2,7 +2,7 @@ import fs from 'fs'
import path from 'path'
import postcss from 'postcss'
import tailwind from '../src/index'
import { defaultConfigFile } from '../src/constants'
import { cjsConfigFile, defaultConfigFile } from '../src/constants'
import inTempDirectory from '../jest/runInTempDirectory'

test('it uses the values from the custom config file', () => {
Expand Down Expand Up @@ -133,6 +133,45 @@ test('custom config can be passed under the `config` property', () => {
})
})

test('tailwind.config.cjs is picked up by default', () => {
return inTempDirectory(() => {
fs.writeFileSync(
path.resolve(cjsConfigFile),
`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;
}
}
`)
})
})
})

test('tailwind.config.js is picked up by default', () => {
return inTempDirectory(() => {
fs.writeFileSync(
Expand Down Expand Up @@ -172,6 +211,45 @@ test('tailwind.config.js is picked up by default', () => {
})
})

test('tailwind.config.cjs is picked up by default when passing an empty object', () => {
return inTempDirectory(() => {
fs.writeFileSync(
path.resolve(cjsConfigFile),
`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;
}
}
`)
})
})
})

test('tailwind.config.js is picked up by default when passing an empty object', () => {
return inTempDirectory(() => {
fs.writeFileSync(
Expand Down
3 changes: 3 additions & 0 deletions __tests__/fixtures/esm-package.json
@@ -0,0 +1,3 @@
{
"type": "module"
}
12 changes: 8 additions & 4 deletions src/cli/commands/init.js
Expand Up @@ -15,7 +15,7 @@ export const options = [
},
{
usage: '-p',
description: 'Generate postcss.config.js file.',
description: 'Generate PostCSS config file.',
},
]

Expand All @@ -35,8 +35,9 @@ export function run(cliParams, cliOptions) {
return new Promise((resolve) => {
utils.header()

const isModule = utils.isModule()
const full = cliOptions.full
const file = cliParams[0] || constants.defaultConfigFile
const file = cliParams[0] || (isModule ? constants.cjsConfigFile : constants.defaultConfigFile)
const simplePath = utils.getSimplePath(file)

utils.exists(file) && utils.die(colors.file(simplePath), 'already exists.')
Expand All @@ -52,10 +53,13 @@ export function run(cliParams, cliOptions) {
utils.log(emoji.yes, 'Created Tailwind config file:', colors.file(simplePath))

if (cliOptions.postcss) {
const path = utils.getSimplePath(constants.defaultPostCssConfigFile)
const postCssConfigFile = isModule
? constants.cjsPostCssConfigFile
: constants.defaultPostCssConfigFile
const path = utils.getSimplePath(postCssConfigFile)
utils.exists(constants.defaultPostCssConfigFile) &&
utils.die(colors.file(path), 'already exists.')
utils.copyFile(constants.defaultPostCssConfigStubFile, constants.defaultPostCssConfigFile)
utils.copyFile(constants.defaultPostCssConfigStubFile, postCssConfigFile)
utils.log(emoji.yes, 'Created PostCSS config file:', colors.file(path))
}

Expand Down
15 changes: 15 additions & 0 deletions src/cli/utils.js
@@ -1,5 +1,6 @@
import { copyFileSync, ensureFileSync, existsSync, outputFileSync, readFileSync } from 'fs-extra'
import { findKey, mapValues, startsWith, trimStart } from 'lodash'
import path from 'path'

import * as colors from './colors'
import * as emoji from './emoji'
Expand Down Expand Up @@ -119,6 +120,20 @@ export function readFile(path) {
return readFileSync(path, 'utf-8')
}

/**
* Checks if current package.json uses type "module"
*
* @return {boolean}
*/
export function isModule() {
const pkgPath = path.resolve('./package.json')
if (exists(pkgPath)) {
const pkg = JSON.parse(readFile(pkgPath))
return pkg.type && pkg.type === 'module'
}
return false
}

/**
* Writes content to file.
*
Expand Down
5 changes: 5 additions & 0 deletions src/constants.js
Expand Up @@ -3,6 +3,11 @@ import path from 'path'
export const cli = 'tailwind'
export const defaultConfigFile = './tailwind.config.js'
export const defaultPostCssConfigFile = './postcss.config.js'
export const cjsConfigFile = './tailwind.config.cjs'
export const cjsPostCssConfigFile = './postcss.config.cjs'

export const supportedConfigFiles = [cjsConfigFile, defaultConfigFile]
export const supportedPostCssConfigFile = [cjsPostCssConfigFile, defaultPostCssConfigFile]

export const defaultConfigStubFile = path.resolve(__dirname, '../stubs/defaultConfig.stub.js')
export const simpleConfigStubFile = path.resolve(__dirname, '../stubs/simpleConfig.stub.js')
Expand Down
16 changes: 9 additions & 7 deletions src/index.js
Expand Up @@ -9,7 +9,7 @@ import processTailwindFeatures from './processTailwindFeatures'
import formatCSS from './lib/formatCSS'
import resolveConfig from './util/resolveConfig'
import getAllConfigs from './util/getAllConfigs'
import { defaultConfigFile } from './constants'
import { supportedConfigFiles } from './constants'
import defaultConfig from '../stubs/defaultConfig.stub.js'

function resolveConfigPath(filePath) {
Expand All @@ -34,13 +34,15 @@ function resolveConfigPath(filePath) {
}

// require('tailwindcss')
try {
const defaultConfigPath = path.resolve(defaultConfigFile)
fs.accessSync(defaultConfigPath)
return defaultConfigPath
} catch (err) {
return undefined
for (const configFile of supportedConfigFiles) {
try {
const configPath = path.resolve(configFile)
fs.accessSync(configPath)
return configPath
} catch (err) {}
}

return undefined
}

const getConfigFunction = (config) => () => {
Expand Down
16 changes: 9 additions & 7 deletions src/index.postcss7.js
Expand Up @@ -10,7 +10,7 @@ import processTailwindFeatures from './processTailwindFeatures'
import formatCSS from './lib/formatCSS'
import resolveConfig from './util/resolveConfig'
import getAllConfigs from './util/getAllConfigs'
import { defaultConfigFile } from './constants'
import { supportedConfigFiles } from './constants'
import defaultConfig from '../stubs/defaultConfig.stub.js'

function resolveConfigPath(filePath) {
Expand All @@ -35,13 +35,15 @@ function resolveConfigPath(filePath) {
}

// require('tailwindcss')
try {
const defaultConfigPath = path.resolve(defaultConfigFile)
fs.accessSync(defaultConfigPath)
return defaultConfigPath
} catch (err) {
return undefined
for (const configFile of supportedConfigFiles) {
try {
const configPath = path.resolve(configFile)
fs.accessSync(configPath)
return configPath
} catch (err) {}
}

return undefined
}

const getConfigFunction = (config) => () => {
Expand Down

0 comments on commit e86b586

Please sign in to comment.