diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d9b5f637148..de378c91af76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - _Upgrade (experimental)_: Migrate `plugins` with options to CSS ([#14700](https://github.com/tailwindlabs/tailwindcss/pull/14700)) +### Changed + +- _Upgrade (experimental)_: Don't create `@source` rules for `content` paths that are already covered by automatic source detection ([#14714](https://github.com/tailwindlabs/tailwindcss/pull/14714)) + ## [4.0.0-alpha.28] - 2024-10-17 ### Added diff --git a/integrations/upgrade/index.test.ts b/integrations/upgrade/index.test.ts index 97409d53d4e5..948c0a9e9fd6 100644 --- a/integrations/upgrade/index.test.ts +++ b/integrations/upgrade/index.test.ts @@ -40,8 +40,6 @@ test( --- ./src/input.css --- @import 'tailwindcss'; - - @source './**/*.{html,js}'; " `) @@ -100,8 +98,6 @@ test( --- ./src/input.css --- @import 'tailwindcss' prefix(tw); - @source './**/*.{html,js}'; - .btn { @apply tw:rounded-md! tw:px-2 tw:py-1 tw:bg-blue-500 tw:text-white; } diff --git a/integrations/upgrade/js-config.test.ts b/integrations/upgrade/js-config.test.ts index aa4088d00832..ef7a188e133a 100644 --- a/integrations/upgrade/js-config.test.ts +++ b/integrations/upgrade/js-config.test.ts @@ -1,5 +1,5 @@ import { expect } from 'vitest' -import { css, json, test, ts } from '../utils' +import { css, html, json, test, ts } from '../utils' test( `upgrade JS config files with flat theme values, darkMode, and content fields`, @@ -18,7 +18,7 @@ test( module.exports = { darkMode: 'selector', - content: ['./src/**/*.{html,js}', './my-app/**/*.{html,js}'], + content: ['./src/**/*.{html,js}', './node_modules/my-external-lib/**/*.{html}'], theme: { boxShadow: { sm: '0 2px 6px rgb(15 23 42 / 0.08)', @@ -72,6 +72,11 @@ test( @tailwind components; @tailwind utilities; `, + 'node_modules/my-external-lib/src/template.html': html` +
+ Hello world! +
+ `, }, }, async ({ exec, fs }) => { @@ -82,8 +87,7 @@ test( --- src/input.css --- @import 'tailwindcss'; - @source './**/*.{html,js}'; - @source '../my-app/**/*.{html,js}'; + @source '../node_modules/my-external-lib/**/*.{html}'; @variant dark (&:where(.dark, .dark *)); diff --git a/packages/@tailwindcss-upgrade/src/migrate-js-config.ts b/packages/@tailwindcss-upgrade/src/migrate-js-config.ts index 19e0305560bb..3c7818902953 100644 --- a/packages/@tailwindcss-upgrade/src/migrate-js-config.ts +++ b/packages/@tailwindcss-upgrade/src/migrate-js-config.ts @@ -1,6 +1,7 @@ +import { Scanner } from '@tailwindcss/oxide' import fs from 'node:fs/promises' import { dirname } from 'path' -import type { Config } from 'tailwindcss' +import { type Config } from 'tailwindcss' import defaultTheme from 'tailwindcss/defaultTheme' import { fileURLToPath } from 'url' import { loadModule } from '../../@tailwindcss-node/src/compile' @@ -54,7 +55,7 @@ export async function migrateJsConfig( } if ('content' in unresolvedConfig) { - sources = migrateContent(unresolvedConfig as any, base) + sources = await migrateContent(unresolvedConfig as any, base) } if ('theme' in unresolvedConfig) { @@ -158,16 +159,31 @@ function createSectionKey(key: string[]): string { return sectionSegments.join('-') } -function migrateContent( +async function migrateContent( unresolvedConfig: Config & { content: any }, base: string, -): { base: string; pattern: string }[] { +): Promise<{ base: string; pattern: string }[]> { + let autoContentFiles = autodetectedSourceFiles(base) + let sources = [] for (let content of unresolvedConfig.content) { if (typeof content !== 'string') { throw new Error('Unsupported content value: ' + content) } - sources.push({ base, pattern: content }) + + let sourceFiles = patternSourceFiles({ base, pattern: content }) + + let autoContentContainsAllSourceFiles = true + for (let sourceFile of sourceFiles) { + if (!autoContentFiles.includes(sourceFile)) { + autoContentContainsAllSourceFiles = false + break + } + } + + if (!autoContentContainsAllSourceFiles) { + sources.push({ base, pattern: content }) + } } return sources } @@ -253,3 +269,15 @@ function keyframesToCss(keyframes: Record): string { let ast: AstNode[] = keyframesToRules({ theme: { keyframes } }) return toCss(ast).trim() + '\n' } + +function autodetectedSourceFiles(base: string) { + let scanner = new Scanner({ detectSources: { base } }) + scanner.scan() + return scanner.files +} + +function patternSourceFiles(source: { base: string; pattern: string }): string[] { + let scanner = new Scanner({ sources: [source] }) + scanner.scan() + return scanner.files +}