-
Notifications
You must be signed in to change notification settings - Fork 1k
/
tailwind.js
195 lines (174 loc) · 5.5 KB
/
tailwind.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
import fs from 'fs'
import path from 'path'
import chalk from 'chalk'
import execa from 'execa'
import Listr from 'listr'
import { getPaths, writeFile } from 'src/lib'
import c from 'src/lib/colors'
export const command = 'tailwind'
export const description = 'Setup tailwindcss and PostCSS'
export const builder = (yargs) => {
yargs.option('force', {
alias: 'f',
default: false,
description: 'Overwrite existing configuration',
type: 'boolean',
})
}
const tailwindImportsAndNotes = [
'/**',
' * START --- TAILWIND GENERATOR EDIT',
' *',
' * `yarn rw setup tailwind` placed these imports here',
" * to inject Tailwind's styles into your CSS.",
' * For more information, see: https://tailwindcss.com/docs/installation#add-tailwind-to-your-css',
' */',
'@import "tailwindcss/base";',
'@import "tailwindcss/components";',
'@import "tailwindcss/utilities";',
'/**',
' * END --- TAILWIND GENERATOR EDIT',
' */\n',
]
const INDEX_CSS_PATH = path.join(getPaths().web.src, 'index.css')
const tailwindImportsExist = (indexCSS) => {
let content = indexCSS.toString()
const hasBaseImport = () => /@import "tailwindcss\/base"/.test(content)
const hasComponentsImport = () =>
/@import "tailwindcss\/components"/.test(content)
const hasUtilitiesImport = () =>
/@import "tailwindcss\/utilities"/.test(content)
return hasBaseImport() && hasComponentsImport() && hasUtilitiesImport()
}
const postCSSConfigExists = () => {
return fs.existsSync(getPaths().web.postcss)
}
export const handler = async ({ force }) => {
const tasks = new Listr([
{
title: 'Installing packages...',
task: () => {
return new Listr([
{
title: 'Install postcss-loader, tailwindcss, and autoprefixer',
task: async () => {
/**
* Install postcss-loader, tailwindcss, and autoprefixer
* RedwoodJS currently uses PostCSS v7; postcss-loader and autoprefixers pinned for compatibility
*/
await execa('yarn', [
'workspace',
'web',
'add',
'-D',
'postcss-loader@4.0.2',
'tailwindcss@npm:@tailwindcss/postcss7-compat',
'autoprefixer@9.8.6',
])
},
},
{
title: 'Sync yarn.lock and node_modules',
task: async () => {
/**
* Sync yarn.lock file and node_modules folder.
* Refer https://github.com/redwoodjs/redwood/issues/1301 for more details.
*/
await execa('yarn', ['install', '--check-files'])
},
},
])
},
},
{
title: 'Configuring PostCSS...',
task: () => {
/**
* Make web/config if it doesn't exist
* and write postcss.config.js there
*/
/**
* Check if PostCSS config already exists.
* If it exists, throw an error.
*/
if (!force && postCSSConfigExists()) {
throw new Error(
'PostCSS config already exists.\nUse --force to override existing config.'
)
} else {
return writeFile(
getPaths().web.postcss,
fs
.readFileSync(
path.resolve(
__dirname,
'templates',
'postcss.config.js.template'
)
)
.toString(),
{ overwriteExisting: force }
)
}
},
},
{
title: 'Initializing Tailwind CSS...',
task: async () => {
const basePath = getPaths().web.base
const tailwindConfigPath = path.join(basePath, 'tailwind.config.js')
const configExists = fs.existsSync(tailwindConfigPath)
if (configExists) {
if (force) {
// yarn tailwindcss init will fail if the file already exists
fs.unlinkSync(tailwindConfigPath)
} else {
throw new Error(
'Tailwindcss config already exists.\nUse --force to override existing config.'
)
}
}
await execa('yarn', ['tailwindcss', 'init'], { cwd: basePath })
// opt-in to upcoming changes
const config = fs.readFileSync(tailwindConfigPath, 'utf-8')
const uncommentFlags = (str) =>
str.replace(/\/{2} ([\w-]+: true)/g, '$1')
const newConfig = config.replace(/future.*purge/s, uncommentFlags)
fs.writeFileSync(tailwindConfigPath, newConfig)
},
},
{
title: 'Adding imports to index.css...',
task: (_ctx, task) => {
/**
* Add tailwind imports and notes to the top of index.css
*/
let indexCSS = fs.readFileSync(INDEX_CSS_PATH)
if (tailwindImportsExist(indexCSS)) {
task.skip('Imports already exist in index.css')
} else {
indexCSS = tailwindImportsAndNotes.join('\n') + indexCSS
fs.writeFileSync(INDEX_CSS_PATH, indexCSS)
}
},
},
{
title: 'One more thing...',
task: (_ctx, task) => {
task.title = `One more thing...\n
${c.green(
'Tailwind configured with "upcoming change" opt-in enabled'
)}\n
${chalk.hex('#e8e8e8')(
'See this doc for info: https://tailwindcss.com/docs/upcoming-changes'
)}
`
},
},
])
try {
await tasks.run()
} catch (e) {
console.log(c.error(e.message))
}
}