add tests for the --postcss option in the new CLI (#4607)
* add tests for the --postcss option in the new CLI
* add `oneOf` ability to the `arg()` functionality
By default, `arg()` doesn't have a way to define multiple types. We want
the possibility of using `--postcss` (Boolean) or `--postcss
./custom-path.js`. But by default this is not possible.
This commit will allow us to do a few things, mainly:
- Keep the same API using the `{ type: oneOf(String, Boolean), description: '...' }`
- Keep the `--help` output similar
What we did behind the scenes is make sure to put the non recognized
flags in the `_` arguments list. This is possible by doing `permissive:
true`. We then manually parse those and resolve the correct value.
* ensure that we can use a custom `--postcss ./with-custom-path.js`
This commit is contained in:
parent
b86aa5c2a9
commit
f4799a32e8
2 changed files with 185 additions and 8 deletions
|
|
@ -1,6 +1,6 @@
|
|||
let path = require('path')
|
||||
let $ = require('../../execute')
|
||||
let { css, html } = require('../../syntax')
|
||||
let { css, html, javascript } = require('../../syntax')
|
||||
let resolveToolRoot = require('../../resolve-tool-root')
|
||||
|
||||
let { readOutputFile, writeInputFile, cleanupFile, fileExists, removeFile } = require('../../io')({
|
||||
|
|
@ -157,6 +157,108 @@ describe('Build command', () => {
|
|||
)
|
||||
})
|
||||
|
||||
test('--postcss (postcss.config.js)', async () => {
|
||||
await writeInputFile('index.html', html`<div class="font-bold"></div>`)
|
||||
|
||||
let customConfig = javascript`
|
||||
let path = require('path')
|
||||
let postcss = require('postcss')
|
||||
|
||||
module.exports = {
|
||||
plugins: [
|
||||
function before(root, result) {
|
||||
// Inject a custom component with @apply rules to prove that we run
|
||||
// this _before_ the actual tailwind plugin.
|
||||
let btn = postcss.parse('.btn { @apply bg-red-500 px-2 py-1 }')
|
||||
root.append(btn.nodes)
|
||||
},
|
||||
function tailwindcss() {
|
||||
return require(path.resolve('..', '..'))
|
||||
},
|
||||
function after(root, result) {
|
||||
// Add '-after' to all the selectors
|
||||
root.walkRules(rule => {
|
||||
if (!rule.selector.startsWith('.')) return
|
||||
rule.selector = rule.selector + '-after'
|
||||
})
|
||||
},
|
||||
],
|
||||
}
|
||||
`
|
||||
|
||||
await writeInputFile('../postcss.config.js', customConfig)
|
||||
|
||||
await $(`${EXECUTABLE} --output ./dist/main.css --postcss`)
|
||||
|
||||
expect(await readOutputFile('main.css')).toIncludeCss(
|
||||
css`
|
||||
.font-bold-after {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.btn-after {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgba(239, 68, 68, var(--tw-bg-opacity));
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.5rem;
|
||||
padding-top: 0.25rem;
|
||||
padding-bottom: 0.25rem;
|
||||
}
|
||||
`
|
||||
)
|
||||
})
|
||||
|
||||
test('--postcss (custom.postcss.config.js)', async () => {
|
||||
await writeInputFile('index.html', html`<div class="font-bold"></div>`)
|
||||
|
||||
let customConfig = javascript`
|
||||
let path = require('path')
|
||||
let postcss = require('postcss')
|
||||
|
||||
module.exports = {
|
||||
plugins: [
|
||||
function before(root, result) {
|
||||
// Inject a custom component with @apply rules to prove that we run
|
||||
// this _before_ the actual tailwind plugin.
|
||||
let btn = postcss.parse('.btn { @apply bg-red-500 px-2 py-1 }')
|
||||
root.append(btn.nodes)
|
||||
},
|
||||
function tailwindcss() {
|
||||
return require(path.resolve('..', '..'))
|
||||
},
|
||||
function after(root, result) {
|
||||
// Add '-after' to all the selectors
|
||||
root.walkRules(rule => {
|
||||
if (!rule.selector.startsWith('.')) return
|
||||
rule.selector = rule.selector + '-after'
|
||||
})
|
||||
},
|
||||
],
|
||||
}
|
||||
`
|
||||
|
||||
await writeInputFile('../custom.postcss.config.js', customConfig)
|
||||
|
||||
await $(`${EXECUTABLE} --output ./dist/main.css --postcss ./custom.postcss.config.js`)
|
||||
|
||||
expect(await readOutputFile('main.css')).toIncludeCss(
|
||||
css`
|
||||
.font-bold-after {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.btn-after {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgba(239, 68, 68, var(--tw-bg-opacity));
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.5rem;
|
||||
padding-top: 0.25rem;
|
||||
padding-bottom: 0.25rem;
|
||||
}
|
||||
`
|
||||
)
|
||||
})
|
||||
|
||||
test('--help', async () => {
|
||||
let { combined } = await $(`${EXECUTABLE} --help`)
|
||||
|
||||
|
|
|
|||
89
src/cli.js
89
src/cli.js
|
|
@ -7,6 +7,8 @@ import path from 'path'
|
|||
import arg from 'arg'
|
||||
import fs from 'fs'
|
||||
import postcssrc from 'postcss-load-config'
|
||||
import { cosmiconfig } from 'cosmiconfig'
|
||||
import loadPlugins from 'postcss-load-config/src/plugins' // Little bit scary, looking at private/internal API
|
||||
import tailwindJit from './jit/processTailwindFeatures'
|
||||
import tailwindAot from './processTailwindFeatures'
|
||||
import resolveConfigInternal from '../resolveConfig'
|
||||
|
|
@ -104,6 +106,22 @@ function help({ message, usage, commands, options }) {
|
|||
console.log()
|
||||
}
|
||||
|
||||
function oneOf(...options) {
|
||||
return Object.assign(
|
||||
(value = true) => {
|
||||
for (let option of options) {
|
||||
let parsed = option(value)
|
||||
if (parsed === value) {
|
||||
return parsed
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error('...')
|
||||
},
|
||||
{ manualParsing: true }
|
||||
)
|
||||
}
|
||||
|
||||
let commands = {
|
||||
init: {
|
||||
run: init,
|
||||
|
|
@ -123,7 +141,10 @@ let commands = {
|
|||
'--watch': { type: Boolean, description: 'Watch for changes and rebuild as needed' },
|
||||
'--jit': { type: Boolean, description: 'Build using JIT mode' },
|
||||
'--purge': { type: String, description: 'Content paths to use for removing unused classes' },
|
||||
'--postcss': { type: Boolean, description: 'Load custom PostCSS configuration' },
|
||||
'--postcss': {
|
||||
type: oneOf(String, Boolean),
|
||||
description: 'Load custom PostCSS configuration',
|
||||
},
|
||||
'--minify': { type: Boolean, description: 'Minify the output' },
|
||||
'--config': {
|
||||
type: String,
|
||||
|
|
@ -191,13 +212,47 @@ let args = (() => {
|
|||
try {
|
||||
let result = arg(
|
||||
Object.fromEntries(
|
||||
Object.entries({ ...flags, ...sharedFlags }).map(([key, value]) => [
|
||||
key,
|
||||
typeof value === 'object' ? value.type : value,
|
||||
])
|
||||
)
|
||||
Object.entries({ ...flags, ...sharedFlags })
|
||||
.filter(([_key, value]) => !value?.type?.manualParsing)
|
||||
.map(([key, value]) => [key, typeof value === 'object' ? value.type : value])
|
||||
),
|
||||
{ permissive: true }
|
||||
)
|
||||
|
||||
// Manual parsing of flags to allow for special flags like oneOf(Boolean, String)
|
||||
for (let i = result['_'].length - 1; i >= 0; --i) {
|
||||
let flag = result['_'][i]
|
||||
if (!flag.startsWith('-')) continue
|
||||
|
||||
let flagName = flag
|
||||
let handler = flags[flag]
|
||||
|
||||
// Resolve flagName & handler
|
||||
while (typeof handler === 'string') {
|
||||
flagName = handler
|
||||
handler = flags[handler]
|
||||
}
|
||||
|
||||
if (!handler) continue
|
||||
|
||||
let args = []
|
||||
let offset = i + 1
|
||||
|
||||
// Parse args for current flag
|
||||
while (result['_'][offset] && !result['_'][offset].startsWith('-')) {
|
||||
args.push(result['_'][offset++])
|
||||
}
|
||||
|
||||
// Cleanup manually parsed flags + args
|
||||
result['_'].splice(i, 1 + args.length)
|
||||
|
||||
// Set the resolved value in the `result` object
|
||||
result[flagName] = handler.type(
|
||||
args.length === 0 ? undefined : args.length === 1 ? args[0] : args,
|
||||
flagName
|
||||
)
|
||||
}
|
||||
|
||||
// Ensure that the `command` is always the first argument in the `args`.
|
||||
// This is important so that we don't have to check if a default command
|
||||
// (build) was used or not from within each plugin.
|
||||
|
|
@ -317,7 +372,27 @@ async function build() {
|
|||
)
|
||||
|
||||
async function loadPostCssPlugins() {
|
||||
let { plugins: configPlugins } = await postcssrc()
|
||||
let customPostCssPath = typeof args['--postcss'] === 'string' ? args['--postcss'] : undefined
|
||||
let { plugins: configPlugins } = customPostCssPath
|
||||
? await (async () => {
|
||||
let file = path.resolve(customPostCssPath)
|
||||
|
||||
// Implementation, see: https://unpkg.com/browse/postcss-load-config@3.0.1/src/index.js
|
||||
let { config = {} } = await cosmiconfig('postcss').load(file)
|
||||
if (typeof config === 'function') {
|
||||
config = config()
|
||||
} else {
|
||||
config = Object.assign({}, config)
|
||||
}
|
||||
|
||||
if (!config.plugins) {
|
||||
config.plugins = []
|
||||
}
|
||||
|
||||
return { plugins: loadPlugins(config, file) }
|
||||
})()
|
||||
: await postcssrc()
|
||||
|
||||
let configPluginTailwindIdx = configPlugins.findIndex((plugin) => {
|
||||
if (typeof plugin === 'function' && plugin.name === 'tailwindcss') {
|
||||
return true
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue