Skip to content
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))
- 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