Skip to content

Commit 657bf5f

Browse files
authored
Allow piping data into the CLI (#6876)
* use outputFile instead of direct writeFile This is an improvement we introduced earlier but forgot this part. * allow to pipe in data to the CLI * add integration tests to validate piping to the CLI * update changelog
1 parent 058a925 commit 657bf5f

File tree

4 files changed

+74
-13
lines changed

4 files changed

+74
-13
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1212
- Fix `@apply` in files without `@tailwind` directives ([#6580](https://github.com/tailwindlabs/tailwindcss/pull/6580), [#6875](https://github.com/tailwindlabs/tailwindcss/pull/6875))
1313
- CLI: avoid unnecessary writes to output files ([#6550](https://github.com/tailwindlabs/tailwindcss/pull/6550))
1414

15+
### Added
16+
17+
- Allow piping data into the CLI ([#6876](https://github.com/tailwindlabs/tailwindcss/pull/6876))
18+
1519
## [3.0.9] - 2022-01-03
1620

1721
### Fixed

integrations/execute.js

+9-4
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,14 @@ module.exports = function $(command, options = {}) {
2121
let abortController = new AbortController()
2222
let cwd = resolveToolRoot()
2323

24-
let args = command.split(' ')
25-
command = args.shift()
26-
command = command === 'node' ? command : path.resolve(cwd, 'node_modules', '.bin', command)
24+
let args = options.shell
25+
? [command]
26+
: (() => {
27+
let args = command.split(' ')
28+
command = args.shift()
29+
command = command === 'node' ? command : path.resolve(cwd, 'node_modules', '.bin', command)
30+
return [command, args]
31+
})()
2732

2833
let stdoutMessages = []
2934
let stderrMessages = []
@@ -55,7 +60,7 @@ module.exports = function $(command, options = {}) {
5560
}, 200)
5661

5762
let runningProcess = new Promise((resolve, reject) => {
58-
let child = spawn(command, args, {
63+
let child = spawn(...args, {
5964
...options,
6065
env: {
6166
...process.env,

integrations/tailwindcss-cli/tests/integration.test.js

+17
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,23 @@ describe('static build', () => {
2727
)
2828
})
2929

30+
it('should be possible to pipe in data', async () => {
31+
await writeInputFile('index.html', html`<div class="font-bold"></div>`)
32+
33+
await $('cat ./src/index.css | node ../../lib/cli.js -i - -o ./dist/main.css', {
34+
shell: true,
35+
env: { NODE_ENV: 'production' },
36+
})
37+
38+
expect(await readOutputFile('main.css')).toIncludeCss(
39+
css`
40+
.font-bold {
41+
font-weight: 700;
42+
}
43+
`
44+
)
45+
})
46+
3047
it('should safelist a list of classes to always include', async () => {
3148
await writeInputFile('index.html', html`<div class="font-bold"></div>`)
3249
await writeInputFile(

src/cli.js

+44-9
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,17 @@ async function outputFile(file, contents) {
5050
await fs.promises.writeFile(file, contents, 'utf8')
5151
}
5252

53+
function drainStdin() {
54+
return new Promise((resolve, reject) => {
55+
let result = ''
56+
process.stdin.on('data', (chunk) => {
57+
result += chunk
58+
})
59+
process.stdin.on('end', () => resolve(result))
60+
process.stdin.on('error', (err) => reject(err))
61+
})
62+
}
63+
5364
function help({ message, usage, commands, options }) {
5465
let indent = 2
5566

@@ -364,7 +375,7 @@ async function build() {
364375
input = args['--input'] = args['_'][1]
365376
}
366377

367-
if (input && !fs.existsSync((input = path.resolve(input)))) {
378+
if (input && input !== '-' && !fs.existsSync((input = path.resolve(input)))) {
368379
console.error(`Specified input file ${args['--input']} does not exist.`)
369380
process.exit(9)
370381
}
@@ -546,8 +557,8 @@ async function build() {
546557

547558
return Promise.all(
548559
[
549-
fs.promises.writeFile(output, result.css, () => true),
550-
result.map && fs.writeFile(output + '.map', result.map.toString(), () => true),
560+
outputFile(output, result.css),
561+
result.map && outputFile(output + '.map', result.map.toString()),
551562
].filter(Boolean)
552563
)
553564
})
@@ -558,9 +569,21 @@ async function build() {
558569
})
559570
}
560571

561-
let css = input
562-
? fs.readFileSync(path.resolve(input), 'utf8')
563-
: '@tailwind base; @tailwind components; @tailwind utilities'
572+
let css = await (() => {
573+
// Piping in data, let's drain the stdin
574+
if (input === '-') {
575+
return drainStdin()
576+
}
577+
578+
// Input file has been provided
579+
if (input) {
580+
return fs.readFileSync(path.resolve(input), 'utf8')
581+
}
582+
583+
// No input file provided, fallback to default atrules
584+
return '@tailwind base; @tailwind components; @tailwind utilities'
585+
})()
586+
564587
return processCSS(css)
565588
}
566589

@@ -694,9 +717,21 @@ async function build() {
694717
})
695718
}
696719

697-
let css = input
698-
? fs.readFileSync(path.resolve(input), 'utf8')
699-
: '@tailwind base; @tailwind components; @tailwind utilities'
720+
let css = await (() => {
721+
// Piping in data, let's drain the stdin
722+
if (input === '-') {
723+
return drainStdin()
724+
}
725+
726+
// Input file has been provided
727+
if (input) {
728+
return fs.readFileSync(path.resolve(input), 'utf8')
729+
}
730+
731+
// No input file provided, fallback to default atrules
732+
return '@tailwind base; @tailwind components; @tailwind utilities'
733+
})()
734+
700735
let result = await processCSS(css)
701736
env.DEBUG && console.timeEnd('Finished in')
702737
return result

0 commit comments

Comments
 (0)