diff --git a/CHANGELOG.md b/CHANGELOG.md index 685018589e77..1bf68dac58d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix `@apply` in files without `@tailwind` directives ([#6580](https://github.com/tailwindlabs/tailwindcss/pull/6580), [#6875](https://github.com/tailwindlabs/tailwindcss/pull/6875)) - CLI: avoid unnecessary writes to output files ([#6550](https://github.com/tailwindlabs/tailwindcss/pull/6550)) +### Added + +- Allow piping data into the CLI ([#6876](https://github.com/tailwindlabs/tailwindcss/pull/6876)) + ## [3.0.9] - 2022-01-03 ### Fixed diff --git a/integrations/execute.js b/integrations/execute.js index bab80b4bad25..7b0f7e799c23 100644 --- a/integrations/execute.js +++ b/integrations/execute.js @@ -21,9 +21,14 @@ module.exports = function $(command, options = {}) { let abortController = new AbortController() let cwd = resolveToolRoot() - let args = command.split(' ') - command = args.shift() - command = command === 'node' ? command : path.resolve(cwd, 'node_modules', '.bin', command) + let args = options.shell + ? [command] + : (() => { + let args = command.split(' ') + command = args.shift() + command = command === 'node' ? command : path.resolve(cwd, 'node_modules', '.bin', command) + return [command, args] + })() let stdoutMessages = [] let stderrMessages = [] @@ -55,7 +60,7 @@ module.exports = function $(command, options = {}) { }, 200) let runningProcess = new Promise((resolve, reject) => { - let child = spawn(command, args, { + let child = spawn(...args, { ...options, env: { ...process.env, diff --git a/integrations/tailwindcss-cli/tests/integration.test.js b/integrations/tailwindcss-cli/tests/integration.test.js index 1992c9cd503d..066417de0635 100644 --- a/integrations/tailwindcss-cli/tests/integration.test.js +++ b/integrations/tailwindcss-cli/tests/integration.test.js @@ -27,6 +27,23 @@ describe('static build', () => { ) }) + it('should be possible to pipe in data', async () => { + await writeInputFile('index.html', html`
`) + + await $('cat ./src/index.css | node ../../lib/cli.js -i - -o ./dist/main.css', { + shell: true, + env: { NODE_ENV: 'production' }, + }) + + expect(await readOutputFile('main.css')).toIncludeCss( + css` + .font-bold { + font-weight: 700; + } + ` + ) + }) + it('should safelist a list of classes to always include', async () => { await writeInputFile('index.html', html`
`) await writeInputFile( diff --git a/src/cli.js b/src/cli.js index 817aa10259a8..c8c1fdd2b9cb 100644 --- a/src/cli.js +++ b/src/cli.js @@ -50,6 +50,17 @@ async function outputFile(file, contents) { await fs.promises.writeFile(file, contents, 'utf8') } +function drainStdin() { + return new Promise((resolve, reject) => { + let result = '' + process.stdin.on('data', (chunk) => { + result += chunk + }) + process.stdin.on('end', () => resolve(result)) + process.stdin.on('error', (err) => reject(err)) + }) +} + function help({ message, usage, commands, options }) { let indent = 2 @@ -364,7 +375,7 @@ async function build() { input = args['--input'] = args['_'][1] } - if (input && !fs.existsSync((input = path.resolve(input)))) { + if (input && input !== '-' && !fs.existsSync((input = path.resolve(input)))) { console.error(`Specified input file ${args['--input']} does not exist.`) process.exit(9) } @@ -546,8 +557,8 @@ async function build() { return Promise.all( [ - fs.promises.writeFile(output, result.css, () => true), - result.map && fs.writeFile(output + '.map', result.map.toString(), () => true), + outputFile(output, result.css), + result.map && outputFile(output + '.map', result.map.toString()), ].filter(Boolean) ) }) @@ -558,9 +569,21 @@ async function build() { }) } - let css = input - ? fs.readFileSync(path.resolve(input), 'utf8') - : '@tailwind base; @tailwind components; @tailwind utilities' + let css = await (() => { + // Piping in data, let's drain the stdin + if (input === '-') { + return drainStdin() + } + + // Input file has been provided + if (input) { + return fs.readFileSync(path.resolve(input), 'utf8') + } + + // No input file provided, fallback to default atrules + return '@tailwind base; @tailwind components; @tailwind utilities' + })() + return processCSS(css) } @@ -694,9 +717,21 @@ async function build() { }) } - let css = input - ? fs.readFileSync(path.resolve(input), 'utf8') - : '@tailwind base; @tailwind components; @tailwind utilities' + let css = await (() => { + // Piping in data, let's drain the stdin + if (input === '-') { + return drainStdin() + } + + // Input file has been provided + if (input) { + return fs.readFileSync(path.resolve(input), 'utf8') + } + + // No input file provided, fallback to default atrules + return '@tailwind base; @tailwind components; @tailwind utilities' + })() + let result = await processCSS(css) env.DEBUG && console.timeEnd('Finished in') return result