Skip to content

Commit f93c42f

Browse files
authored
Write to stdout when --output is omitted (#15656)
1 parent 2f8c517 commit f93c42f

File tree

6 files changed

+170
-5
lines changed

6 files changed

+170
-5
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1414
- Resolve values in functional utilities based on `@theme` options ([#15623](https://github.com/tailwindlabs/tailwindcss/pull/15623))
1515
- Discard invalid variants such as `data-checked-[selected=1]:*` ([#15629](https://github.com/tailwindlabs/tailwindcss/pull/15629))
1616
- Ensure `-outline-offset-*` utilities are suggested in IntelliSense ([#15646](https://github.com/tailwindlabs/tailwindcss/pull/15646))
17+
- Write to `stdout` when `--output` is set to `-` or omitted for `@tailwindcss/cli` ([#15656](https://github.com/tailwindlabs/tailwindcss/pull/15656))
18+
- Write to `stdout` when `--output -` flag is used for `@tailwindcss/cli` ([#15656](https://github.com/tailwindlabs/tailwindcss/pull/15656))
1719
- _Upgrade (experimental)_: Pretty print `--spacing(…)` to prevent ambiguity ([#15596](https://github.com/tailwindlabs/tailwindcss/pull/15596))
1820

1921
## [4.0.0-beta.9] - 2025-01-09

integrations/cli/index.test.ts

+143
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,149 @@ describe.each([
9595
},
9696
)
9797

98+
test(
99+
'production build — read input from stdin',
100+
{
101+
fs: {
102+
'package.json': json`{}`,
103+
'pnpm-workspace.yaml': yaml`
104+
#
105+
packages:
106+
- project-a
107+
`,
108+
'project-a/package.json': json`
109+
{
110+
"dependencies": {
111+
"tailwindcss": "workspace:^",
112+
"@tailwindcss/cli": "workspace:^"
113+
}
114+
}
115+
`,
116+
'project-a/index.html': html`
117+
<div
118+
class="underline 2xl:font-bold hocus:underline inverted:flex *:flex **:flex"
119+
></div>
120+
`,
121+
'project-a/plugin.js': js`
122+
module.exports = function ({ addVariant }) {
123+
addVariant('inverted', '@media (inverted-colors: inverted)')
124+
addVariant('hocus', ['&:focus', '&:hover'])
125+
}
126+
`,
127+
'project-a/tailwind.config.js': js`
128+
module.exports = {
129+
content: ['../project-b/src/**/*.js'],
130+
}
131+
`,
132+
'project-a/src/index.js': js`
133+
const className = "content-['project-a/src/index.js']"
134+
module.exports = { className }
135+
`,
136+
'project-b/src/index.html': html`
137+
<div class="flex" />
138+
`,
139+
'project-b/src/index.js': js`
140+
const className = "content-['project-b/src/index.js']"
141+
module.exports = { className }
142+
`,
143+
},
144+
},
145+
async ({ root, fs, exec }) => {
146+
await exec(
147+
`${command} --input - --output dist/out.css`,
148+
{ cwd: path.join(root, 'project-a') },
149+
{
150+
stdin: css`
151+
@import 'tailwindcss/utilities';
152+
@config './tailwind.config.js';
153+
@source '../project-b/src/**/*.html';
154+
@plugin './plugin.js';
155+
`,
156+
},
157+
)
158+
159+
await fs.expectFileToContain('project-a/dist/out.css', [
160+
candidate`underline`,
161+
candidate`flex`,
162+
candidate`content-['project-a/src/index.js']`,
163+
candidate`content-['project-b/src/index.js']`,
164+
candidate`inverted:flex`,
165+
candidate`hocus:underline`,
166+
candidate`*:flex`,
167+
candidate`**:flex`,
168+
])
169+
},
170+
)
171+
172+
test(
173+
'production build — (write to stdout)',
174+
{
175+
fs: {
176+
'package.json': json`{}`,
177+
'pnpm-workspace.yaml': yaml`
178+
#
179+
packages:
180+
- project-a
181+
`,
182+
'project-a/package.json': json`
183+
{
184+
"dependencies": {
185+
"tailwindcss": "workspace:^",
186+
"@tailwindcss/cli": "workspace:^"
187+
}
188+
}
189+
`,
190+
'project-a/index.html': html`
191+
<div
192+
class="underline 2xl:font-bold hocus:underline inverted:flex *:flex **:flex"
193+
></div>
194+
`,
195+
'project-a/plugin.js': js`
196+
module.exports = function ({ addVariant }) {
197+
addVariant('inverted', '@media (inverted-colors: inverted)')
198+
addVariant('hocus', ['&:focus', '&:hover'])
199+
}
200+
`,
201+
'project-a/tailwind.config.js': js`
202+
module.exports = {
203+
content: ['../project-b/src/**/*.js'],
204+
}
205+
`,
206+
'project-a/src/index.css': css`
207+
@import 'tailwindcss/utilities';
208+
@config '../tailwind.config.js';
209+
@source '../../project-b/src/**/*.html';
210+
@plugin '../plugin.js';
211+
`,
212+
'project-a/src/index.js': js`
213+
const className = "content-['project-a/src/index.js']"
214+
module.exports = { className }
215+
`,
216+
'project-b/src/index.html': html`
217+
<div class="flex" />
218+
`,
219+
'project-b/src/index.js': js`
220+
const className = "content-['project-b/src/index.js']"
221+
module.exports = { className }
222+
`,
223+
},
224+
},
225+
async ({ root, expect, exec }) => {
226+
let stdout = await exec(`${command} --input src/index.css --output -`, {
227+
cwd: path.join(root, 'project-a'),
228+
})
229+
230+
expect(stdout).toContain(candidate`underline`)
231+
expect(stdout).toContain(candidate`flex`)
232+
expect(stdout).toContain(candidate`content-['project-a/src/index.js']`)
233+
expect(stdout).toContain(candidate`content-['project-b/src/index.js']`)
234+
expect(stdout).toContain(candidate`inverted:flex`)
235+
expect(stdout).toContain(candidate`hocus:underline`)
236+
expect(stdout).toContain(candidate`*:flex`)
237+
expect(stdout).toContain(candidate`**:flex`)
238+
},
239+
)
240+
98241
test(
99242
'watch mode',
100243
{

integrations/utils.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ interface ChildProcessOptions {
2525

2626
interface ExecOptions {
2727
ignoreStdErr?: boolean
28+
stdin?: string
2829
}
2930

3031
interface TestConfig {
@@ -112,7 +113,7 @@ export function test(
112113
}
113114
if (debug) console.log(`> ${command}`)
114115
return new Promise((resolve, reject) => {
115-
exec(
116+
let child = exec(
116117
command,
117118
{
118119
cwd,
@@ -134,6 +135,10 @@ export function test(
134135
}
135136
},
136137
)
138+
if (execOptions.stdin) {
139+
child.stdin?.write(execOptions.stdin)
140+
child.stdin?.end()
141+
}
137142
})
138143
},
139144
async spawn(command: string, childProcessOptions: ChildProcessOptions = {}) {

packages/@tailwindcss-cli/src/commands/build/index.ts

+6-3
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export function options() {
3232
type: 'string',
3333
description: 'Output file',
3434
alias: '-o',
35+
default: '-',
3536
},
3637
'--watch': {
3738
type: 'boolean | string',
@@ -72,8 +73,10 @@ export async function handle(args: Result<ReturnType<typeof options>>) {
7273

7374
let base = path.resolve(args['--cwd'])
7475

75-
// Resolve the output as an absolute path.
76-
if (args['--output']) {
76+
// Resolve the output as an absolute path. If the output is a `-`, then we
77+
// don't need to resolve it because this is a flag to indicate that we want to
78+
// use `stdout` instead.
79+
if (args['--output'] && args['--output'] !== '-') {
7780
args['--output'] = path.resolve(base, args['--output'])
7881
}
7982

@@ -129,7 +132,7 @@ export async function handle(args: Result<ReturnType<typeof options>>) {
129132

130133
// Write the output
131134
DEBUG && I.start('Write output')
132-
if (args['--output']) {
135+
if (args['--output'] && args['--output'] !== '-') {
133136
await outputFile(args['--output'], output)
134137
} else {
135138
println(output)

packages/@tailwindcss-cli/src/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ if (command) {
3636
//
3737
// - `tailwindcss -o output.css` // should run the build command, not show the help message
3838
// - `tailwindcss > output.css` // should run the build command, not show the help message
39-
if ((process.stdout.isTTY && !flags['--output']) || flags['--help']) {
39+
if ((process.stdout.isTTY && process.argv[2] === undefined) || flags['--help']) {
4040
help({
4141
usage: ['tailwindcss [--input input.css] [--output output.css] [--watch] [options…]'],
4242
options: { ...build.options(), ...sharedOptions },

packages/@tailwindcss-cli/src/utils/args.ts

+12
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,20 @@ export type Result<T extends Arg> = {
6767
}
6868

6969
export function args<const T extends Arg>(options: T, argv = process.argv.slice(2)): Result<T> {
70+
for (let [idx, value] of argv.entries()) {
71+
if (value === '-') {
72+
argv[idx] = '__IO_DEFAULT_VALUE__'
73+
}
74+
}
75+
7076
let parsed = parse(argv)
7177

78+
for (let key in parsed) {
79+
if (parsed[key] === '__IO_DEFAULT_VALUE__') {
80+
parsed[key] = '-'
81+
}
82+
}
83+
7284
let result: { _: string[]; [key: string]: unknown } = {
7385
_: parsed._,
7486
}

0 commit comments

Comments
 (0)