Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Write to stdout when --output is omitted #15656

Merged
merged 7 commits into from
Jan 17, 2025
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Resolve values in functional utilities based on `@theme` options ([#15623](https://github.com/tailwindlabs/tailwindcss/pull/15623))
- Discard invalid variants such as `data-checked-[selected=1]:*` ([#15629](https://github.com/tailwindlabs/tailwindcss/pull/15629))
- Ensure `-outline-offset-*` utilities are suggested in IntelliSense ([#15646](https://github.com/tailwindlabs/tailwindcss/pull/15646))
- Write to `stdout` when `--output` flag is omitted for `@tailwindcss/cli` ([#15656](https://github.com/tailwindlabs/tailwindcss/pull/15656))
RobinMalfait marked this conversation as resolved.
Show resolved Hide resolved
- Write to `stdout` when `--output -` flag is used for `@tailwindcss/cli` ([#15656](https://github.com/tailwindlabs/tailwindcss/pull/15656))
- _Upgrade (experimental)_: Pretty print `--spacing(…)` to prevent ambiguity ([#15596](https://github.com/tailwindlabs/tailwindcss/pull/15596))

## [4.0.0-beta.9] - 2025-01-09
Expand Down
143 changes: 143 additions & 0 deletions integrations/cli/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,149 @@ describe.each([
},
)

test(
'production build — read input from stdin',
{
fs: {
'package.json': json`{}`,
'pnpm-workspace.yaml': yaml`
#
packages:
- project-a
`,
'project-a/package.json': json`
{
"dependencies": {
"tailwindcss": "workspace:^",
"@tailwindcss/cli": "workspace:^"
}
}
`,
'project-a/index.html': html`
<div
class="underline 2xl:font-bold hocus:underline inverted:flex *:flex **:flex"
></div>
`,
'project-a/plugin.js': js`
module.exports = function ({ addVariant }) {
addVariant('inverted', '@media (inverted-colors: inverted)')
addVariant('hocus', ['&:focus', '&:hover'])
}
`,
'project-a/tailwind.config.js': js`
module.exports = {
content: ['../project-b/src/**/*.js'],
}
`,
'project-a/src/index.js': js`
const className = "content-['project-a/src/index.js']"
module.exports = { className }
`,
'project-b/src/index.html': html`
<div class="flex" />
`,
'project-b/src/index.js': js`
const className = "content-['project-b/src/index.js']"
module.exports = { className }
`,
},
},
async ({ root, fs, exec }) => {
await exec(
`${command} --input - --output dist/out.css`,
{ cwd: path.join(root, 'project-a') },
{
stdin: css`
@import 'tailwindcss/utilities';
@config './tailwind.config.js';
@source '../project-b/src/**/*.html';
@plugin './plugin.js';
`,
},
)

await fs.expectFileToContain('project-a/dist/out.css', [
candidate`underline`,
candidate`flex`,
candidate`content-['project-a/src/index.js']`,
candidate`content-['project-b/src/index.js']`,
candidate`inverted:flex`,
candidate`hocus:underline`,
candidate`*:flex`,
candidate`**:flex`,
])
},
)

test(
'production build — (write to stdout)',
{
fs: {
'package.json': json`{}`,
'pnpm-workspace.yaml': yaml`
#
packages:
- project-a
`,
'project-a/package.json': json`
{
"dependencies": {
"tailwindcss": "workspace:^",
"@tailwindcss/cli": "workspace:^"
}
}
`,
'project-a/index.html': html`
<div
class="underline 2xl:font-bold hocus:underline inverted:flex *:flex **:flex"
></div>
`,
'project-a/plugin.js': js`
module.exports = function ({ addVariant }) {
addVariant('inverted', '@media (inverted-colors: inverted)')
addVariant('hocus', ['&:focus', '&:hover'])
}
`,
'project-a/tailwind.config.js': js`
module.exports = {
content: ['../project-b/src/**/*.js'],
}
`,
'project-a/src/index.css': css`
@import 'tailwindcss/utilities';
@config '../tailwind.config.js';
@source '../../project-b/src/**/*.html';
@plugin '../plugin.js';
`,
'project-a/src/index.js': js`
const className = "content-['project-a/src/index.js']"
module.exports = { className }
`,
'project-b/src/index.html': html`
<div class="flex" />
`,
'project-b/src/index.js': js`
const className = "content-['project-b/src/index.js']"
module.exports = { className }
`,
},
},
async ({ root, expect, exec }) => {
let stdout = await exec(`${command} --input src/index.css --output -`, {
cwd: path.join(root, 'project-a'),
})

expect(stdout).toContain(candidate`underline`)
expect(stdout).toContain(candidate`flex`)
expect(stdout).toContain(candidate`content-['project-a/src/index.js']`)
expect(stdout).toContain(candidate`content-['project-b/src/index.js']`)
expect(stdout).toContain(candidate`inverted:flex`)
expect(stdout).toContain(candidate`hocus:underline`)
expect(stdout).toContain(candidate`*:flex`)
expect(stdout).toContain(candidate`**:flex`)
},
)

test(
'watch mode',
{
Expand Down
7 changes: 6 additions & 1 deletion integrations/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ interface ChildProcessOptions {

interface ExecOptions {
ignoreStdErr?: boolean
stdin?: string
}

interface TestConfig {
Expand Down Expand Up @@ -112,7 +113,7 @@ export function test(
}
if (debug) console.log(`> ${command}`)
return new Promise((resolve, reject) => {
exec(
let child = exec(
command,
{
cwd,
Expand All @@ -134,6 +135,10 @@ export function test(
}
},
)
if (execOptions.stdin) {
child.stdin?.write(execOptions.stdin)
child.stdin?.end()
}
})
},
async spawn(command: string, childProcessOptions: ChildProcessOptions = {}) {
Expand Down
9 changes: 6 additions & 3 deletions packages/@tailwindcss-cli/src/commands/build/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export function options() {
type: 'string',
description: 'Output file',
alias: '-o',
default: '-',
},
'--watch': {
type: 'boolean | string',
Expand Down Expand Up @@ -72,8 +73,10 @@ export async function handle(args: Result<ReturnType<typeof options>>) {

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

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

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

// Write the output
DEBUG && I.start('Write output')
if (args['--output']) {
if (args['--output'] && args['--output'] !== '-') {
await outputFile(args['--output'], output)
} else {
println(output)
Expand Down
2 changes: 1 addition & 1 deletion packages/@tailwindcss-cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ if (command) {
//
// - `tailwindcss -o output.css` // should run the build command, not show the help message
// - `tailwindcss > output.css` // should run the build command, not show the help message
if ((process.stdout.isTTY && !flags['--output']) || flags['--help']) {
if ((process.stdout.isTTY && process.argv[2] === undefined) || flags['--help']) {
help({
usage: ['tailwindcss [--input input.css] [--output output.css] [--watch] [options…]'],
options: { ...build.options(), ...sharedOptions },
Expand Down
12 changes: 12 additions & 0 deletions packages/@tailwindcss-cli/src/utils/args.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,20 @@ export type Result<T extends Arg> = {
}

export function args<const T extends Arg>(options: T, argv = process.argv.slice(2)): Result<T> {
for (let [idx, value] of argv.entries()) {
if (value === '-') {
argv[idx] = '__IO_DEFAULT_VALUE__'
}
}

let parsed = parse(argv)

for (let key in parsed) {
if (parsed[key] === '__IO_DEFAULT_VALUE__') {
parsed[key] = '-'
}
}

let result: { _: string[]; [key: string]: unknown } = {
_: parsed._,
}
Expand Down