diff --git a/integrations/upgrade/index.test.ts b/integrations/upgrade/index.test.ts index 9f81c1d2a5d4..8835c5483f36 100644 --- a/integrations/upgrade/index.test.ts +++ b/integrations/upgrade/index.test.ts @@ -808,3 +808,68 @@ test( `) }, ) + +test( + 'migrate utility files imported by multiple roots', + { + fs: { + 'package.json': json` + { + "dependencies": { + "tailwindcss": "workspace:^", + "@tailwindcss/cli": "workspace:^", + "@tailwindcss/upgrade": "workspace:^" + } + } + `, + 'tailwind.config.js': js`module.exports = {}`, + 'src/index.html': html` +
+ `, + 'src/root.1.css': css` + @import 'tailwindcss/utilities'; + @import './a.1.css' layer(utilities); + `, + 'src/root.2.css': css` + @import 'tailwindcss/utilities'; + @import './a.1.css' layer(components); + `, + 'src/root.3.css': css` + @import 'tailwindcss/utilities'; + @import './a.1.css'; + `, + 'src/a.1.css': css` + .foo-from-a { + color: red; + } + `, + }, + }, + async ({ fs, exec }) => { + let output = await exec('npx @tailwindcss/upgrade --force') + + expect(output).toMatch( + /You have one or more stylesheets that are imported into a utility layer and non-utility layer./, + ) + + expect(await fs.dumpFiles('./src/**/*.css')).toMatchInlineSnapshot(` + " + --- ./src/a.1.css --- + .foo-from-a { + color: red; + } + + --- ./src/root.1.css --- + @import 'tailwindcss/utilities' layer(utilities); + @import './a.1.css' layer(utilities); + + --- ./src/root.2.css --- + @import 'tailwindcss/utilities' layer(utilities); + @import './a.1.css' layer(components); + + --- ./src/root.3.css --- + @import 'tailwindcss/utilities' layer(utilities); + @import './a.1.css' layer(utilities);" + `) + }, +) diff --git a/integrations/utils.ts b/integrations/utils.ts index c40476931536..ce700bce7f7a 100644 --- a/integrations/utils.ts +++ b/integrations/utils.ts @@ -114,7 +114,7 @@ export function test( if (execOptions.ignoreStdErr !== true) console.error(stderr) reject(error) } else { - resolve(stdout.toString()) + resolve(stdout.toString() + '\n\n' + stderr.toString()) } }, ) diff --git a/packages/@tailwindcss-upgrade/src/migrate.ts b/packages/@tailwindcss-upgrade/src/migrate.ts index c0ae87f5829c..0b32cace52c0 100644 --- a/packages/@tailwindcss-upgrade/src/migrate.ts +++ b/packages/@tailwindcss-upgrade/src/migrate.ts @@ -9,7 +9,7 @@ import { migrateAtLayerUtilities } from './codemods/migrate-at-layer-utilities' import { migrateMediaScreen } from './codemods/migrate-media-screen' import { migrateMissingLayers } from './codemods/migrate-missing-layers' import { migrateTailwindDirectives } from './codemods/migrate-tailwind-directives' -import { Stylesheet, type StylesheetId } from './stylesheet' +import { Stylesheet, type StylesheetConnection, type StylesheetId } from './stylesheet' import { resolveCssId } from './utils/resolve' import { walk, WalkAction } from './utils/walk' @@ -43,6 +43,8 @@ export async function migrate(stylesheet: Stylesheet, options: MigrateOptions) { throw new Error('Cannot migrate a stylesheet without a file path') } + if (!stylesheet.canMigrate) return + await migrateContents(stylesheet, options) } @@ -123,6 +125,57 @@ export async function analyze(stylesheets: Stylesheet[]) { await processor.process(sheet.root, { from: sheet.file }) } + + let commonPath = process.cwd() + + function pathToString(path: StylesheetConnection[]) { + let parts: string[] = [] + + for (let connection of path) { + if (!connection.item.file) continue + + let filePath = connection.item.file.replace(commonPath, '') + let layers = connection.meta.layers.join(', ') + + if (layers.length > 0) { + parts.push(`${filePath} (layers: ${layers})`) + } else { + parts.push(filePath) + } + } + + return parts.join(' <- ') + } + + let lines: string[] = [] + + for (let sheet of stylesheets) { + if (!sheet.file) continue + + let { convertablePaths, nonConvertablePaths } = sheet.analyzeImportPaths() + let isAmbiguous = convertablePaths.length > 0 && nonConvertablePaths.length > 0 + + if (!isAmbiguous) continue + + sheet.canMigrate = false + + let filePath = sheet.file.replace(commonPath, '') + + for (let path of convertablePaths) { + lines.push(`- ${filePath} <- ${pathToString(path)}`) + } + + for (let path of nonConvertablePaths) { + lines.push(`- ${filePath} <- ${pathToString(path)}`) + } + } + + if (lines.length === 0) return + + let error = `You have one or more stylesheets that are imported into a utility layer and non-utility layer.\n` + error += `We cannot convert stylesheets under these conditions. Please look at the following stylesheets:\n` + + throw new Error(error + lines.join('\n')) } export async function split(stylesheets: Stylesheet[]) { diff --git a/packages/@tailwindcss-upgrade/src/stylesheet.ts b/packages/@tailwindcss-upgrade/src/stylesheet.ts index 76666b75d8e5..c3a91fe4ba88 100644 --- a/packages/@tailwindcss-upgrade/src/stylesheet.ts +++ b/packages/@tailwindcss-upgrade/src/stylesheet.ts @@ -42,6 +42,11 @@ export class Stylesheet { */ children = new Set