Skip to content

Commit e86b586

Browse files
authored
Adds support for tailwind.config.cjs files (#3181)
* 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
1 parent b281535 commit e86b586

File tree

8 files changed

+150
-19
lines changed

8 files changed

+150
-19
lines changed

__tests__/cli.test.js

+22
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import runInTempDirectory from '../jest/runInTempDirectory'
88
describe('cli', () => {
99
const inputCssPath = path.resolve(__dirname, 'fixtures/tailwind-input.css')
1010
const customConfigPath = path.resolve(__dirname, 'fixtures/custom-config.js')
11+
const esmPackageJsonPath = path.resolve(__dirname, 'fixtures/esm-package.json')
1112
const defaultConfigFixture = utils.readFile(constants.defaultConfigStubFile)
1213
const simpleConfigFixture = utils.readFile(constants.simpleConfigStubFile)
1314
const defaultPostCssConfigFixture = utils.readFile(constants.defaultPostCssConfigStubFile)
@@ -47,6 +48,27 @@ describe('cli', () => {
4748
})
4849
})
4950

51+
it('creates a .cjs Tailwind config file inside of an ESM project', () => {
52+
return runInTempDirectory(() => {
53+
utils.writeFile('package.json', utils.readFile(esmPackageJsonPath))
54+
return cli(['init']).then(() => {
55+
expect(utils.readFile(constants.cjsConfigFile)).toEqual(simpleConfigFixture)
56+
})
57+
})
58+
})
59+
60+
it('creates a .cjs Tailwind config file and a postcss.config.cjs file inside of an ESM project', () => {
61+
return runInTempDirectory(() => {
62+
utils.writeFile('package.json', utils.readFile(esmPackageJsonPath))
63+
return cli(['init', '-p']).then(() => {
64+
expect(utils.readFile(constants.cjsConfigFile)).toEqual(simpleConfigFixture)
65+
expect(utils.readFile(constants.cjsPostCssConfigFile)).toEqual(
66+
defaultPostCssConfigFixture
67+
)
68+
})
69+
})
70+
})
71+
5072
it('creates a Tailwind config file in a custom location', () => {
5173
return runInTempDirectory(() => {
5274
return cli(['init', 'custom.js']).then(() => {

__tests__/customConfig.test.js

+79-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import fs from 'fs'
22
import path from 'path'
33
import postcss from 'postcss'
44
import tailwind from '../src/index'
5-
import { defaultConfigFile } from '../src/constants'
5+
import { cjsConfigFile, defaultConfigFile } from '../src/constants'
66
import inTempDirectory from '../jest/runInTempDirectory'
77

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

136+
test('tailwind.config.cjs is picked up by default', () => {
137+
return inTempDirectory(() => {
138+
fs.writeFileSync(
139+
path.resolve(cjsConfigFile),
140+
`module.exports = {
141+
theme: {
142+
screens: {
143+
mobile: '400px',
144+
},
145+
},
146+
}`
147+
)
148+
149+
return postcss([tailwind])
150+
.process(
151+
`
152+
@responsive {
153+
.foo {
154+
color: blue;
155+
}
156+
}
157+
`,
158+
{ from: undefined }
159+
)
160+
.then((result) => {
161+
expect(result.css).toMatchCss(`
162+
.foo {
163+
color: blue;
164+
}
165+
@media (min-width: 400px) {
166+
.mobile\\:foo {
167+
color: blue;
168+
}
169+
}
170+
`)
171+
})
172+
})
173+
})
174+
136175
test('tailwind.config.js is picked up by default', () => {
137176
return inTempDirectory(() => {
138177
fs.writeFileSync(
@@ -172,6 +211,45 @@ test('tailwind.config.js is picked up by default', () => {
172211
})
173212
})
174213

214+
test('tailwind.config.cjs is picked up by default when passing an empty object', () => {
215+
return inTempDirectory(() => {
216+
fs.writeFileSync(
217+
path.resolve(cjsConfigFile),
218+
`module.exports = {
219+
theme: {
220+
screens: {
221+
mobile: '400px',
222+
},
223+
},
224+
}`
225+
)
226+
227+
return postcss([tailwind({})])
228+
.process(
229+
`
230+
@responsive {
231+
.foo {
232+
color: blue;
233+
}
234+
}
235+
`,
236+
{ from: undefined }
237+
)
238+
.then((result) => {
239+
expect(result.css).toMatchCss(`
240+
.foo {
241+
color: blue;
242+
}
243+
@media (min-width: 400px) {
244+
.mobile\\:foo {
245+
color: blue;
246+
}
247+
}
248+
`)
249+
})
250+
})
251+
})
252+
175253
test('tailwind.config.js is picked up by default when passing an empty object', () => {
176254
return inTempDirectory(() => {
177255
fs.writeFileSync(

__tests__/fixtures/esm-package.json

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"type": "module"
3+
}

src/cli/commands/init.js

+8-4
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export const options = [
1515
},
1616
{
1717
usage: '-p',
18-
description: 'Generate postcss.config.js file.',
18+
description: 'Generate PostCSS config file.',
1919
},
2020
]
2121

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

38+
const isModule = utils.isModule()
3839
const full = cliOptions.full
39-
const file = cliParams[0] || constants.defaultConfigFile
40+
const file = cliParams[0] || (isModule ? constants.cjsConfigFile : constants.defaultConfigFile)
4041
const simplePath = utils.getSimplePath(file)
4142

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

5455
if (cliOptions.postcss) {
55-
const path = utils.getSimplePath(constants.defaultPostCssConfigFile)
56+
const postCssConfigFile = isModule
57+
? constants.cjsPostCssConfigFile
58+
: constants.defaultPostCssConfigFile
59+
const path = utils.getSimplePath(postCssConfigFile)
5660
utils.exists(constants.defaultPostCssConfigFile) &&
5761
utils.die(colors.file(path), 'already exists.')
58-
utils.copyFile(constants.defaultPostCssConfigStubFile, constants.defaultPostCssConfigFile)
62+
utils.copyFile(constants.defaultPostCssConfigStubFile, postCssConfigFile)
5963
utils.log(emoji.yes, 'Created PostCSS config file:', colors.file(path))
6064
}
6165

src/cli/utils.js

+15
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { copyFileSync, ensureFileSync, existsSync, outputFileSync, readFileSync } from 'fs-extra'
22
import { findKey, mapValues, startsWith, trimStart } from 'lodash'
3+
import path from 'path'
34

45
import * as colors from './colors'
56
import * as emoji from './emoji'
@@ -119,6 +120,20 @@ export function readFile(path) {
119120
return readFileSync(path, 'utf-8')
120121
}
121122

123+
/**
124+
* Checks if current package.json uses type "module"
125+
*
126+
* @return {boolean}
127+
*/
128+
export function isModule() {
129+
const pkgPath = path.resolve('./package.json')
130+
if (exists(pkgPath)) {
131+
const pkg = JSON.parse(readFile(pkgPath))
132+
return pkg.type && pkg.type === 'module'
133+
}
134+
return false
135+
}
136+
122137
/**
123138
* Writes content to file.
124139
*

src/constants.js

+5
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ import path from 'path'
33
export const cli = 'tailwind'
44
export const defaultConfigFile = './tailwind.config.js'
55
export const defaultPostCssConfigFile = './postcss.config.js'
6+
export const cjsConfigFile = './tailwind.config.cjs'
7+
export const cjsPostCssConfigFile = './postcss.config.cjs'
8+
9+
export const supportedConfigFiles = [cjsConfigFile, defaultConfigFile]
10+
export const supportedPostCssConfigFile = [cjsPostCssConfigFile, defaultPostCssConfigFile]
611

712
export const defaultConfigStubFile = path.resolve(__dirname, '../stubs/defaultConfig.stub.js')
813
export const simpleConfigStubFile = path.resolve(__dirname, '../stubs/simpleConfig.stub.js')

src/index.js

+9-7
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import processTailwindFeatures from './processTailwindFeatures'
99
import formatCSS from './lib/formatCSS'
1010
import resolveConfig from './util/resolveConfig'
1111
import getAllConfigs from './util/getAllConfigs'
12-
import { defaultConfigFile } from './constants'
12+
import { supportedConfigFiles } from './constants'
1313
import defaultConfig from '../stubs/defaultConfig.stub.js'
1414

1515
function resolveConfigPath(filePath) {
@@ -34,13 +34,15 @@ function resolveConfigPath(filePath) {
3434
}
3535

3636
// require('tailwindcss')
37-
try {
38-
const defaultConfigPath = path.resolve(defaultConfigFile)
39-
fs.accessSync(defaultConfigPath)
40-
return defaultConfigPath
41-
} catch (err) {
42-
return undefined
37+
for (const configFile of supportedConfigFiles) {
38+
try {
39+
const configPath = path.resolve(configFile)
40+
fs.accessSync(configPath)
41+
return configPath
42+
} catch (err) {}
4343
}
44+
45+
return undefined
4446
}
4547

4648
const getConfigFunction = (config) => () => {

src/index.postcss7.js

+9-7
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import processTailwindFeatures from './processTailwindFeatures'
1010
import formatCSS from './lib/formatCSS'
1111
import resolveConfig from './util/resolveConfig'
1212
import getAllConfigs from './util/getAllConfigs'
13-
import { defaultConfigFile } from './constants'
13+
import { supportedConfigFiles } from './constants'
1414
import defaultConfig from '../stubs/defaultConfig.stub.js'
1515

1616
function resolveConfigPath(filePath) {
@@ -35,13 +35,15 @@ function resolveConfigPath(filePath) {
3535
}
3636

3737
// require('tailwindcss')
38-
try {
39-
const defaultConfigPath = path.resolve(defaultConfigFile)
40-
fs.accessSync(defaultConfigPath)
41-
return defaultConfigPath
42-
} catch (err) {
43-
return undefined
38+
for (const configFile of supportedConfigFiles) {
39+
try {
40+
const configPath = path.resolve(configFile)
41+
fs.accessSync(configPath)
42+
return configPath
43+
} catch (err) {}
4444
}
45+
46+
return undefined
4547
}
4648

4749
const getConfigFunction = (config) => () => {

0 commit comments

Comments
 (0)