Skip to content

Conversation

@philipp-spiess
Copy link
Member

@philipp-spiess philipp-spiess commented Sep 24, 2024

This PR adds the initial setup and a first codemod for the template migrations. These are a new set of migrations that operate on files defined in the Tailwind v3 config as part of the content option (so your HTML, JavaScript, TSX files etc.).

The migration for this is integrated in the new @tailwindcss/upgrade package and will require pointing the migration to an input JavaScript config file, like this:

npx @tailwindcss/upgrade --config tailwind.config.js

The idea of template migrations is to apply breaking changes from the v3 to v4 migration within your template files.

Migrating !important syntax

The first migration that I’m adding with this PR is to ensure we use the v4 important syntax that has the exclamation mark at the end of the utility.

For example, this:

<div class="!flex sm:!block"></div>

Will now turn into:

<div class="flex! sm:block!"></div>

Architecture considerations

Implementation wise, we make use of Oxide to scan the content files fast and efficiently. By relying on the same scanner als Tailwind v4, we guarantee that all candidates that are part of the v4 output will have gone through a migration.

Migrations itself operate on the abstract Candidate type, similar to the type we use in the v4 codebase. It will parse the candidate into its parts so they can easily be introspected/modified. Migrations are typed as:

type TemplateMigration = (candidate: Candidate) => Candidate | null

null should be returned if the Candidate does not need a migration.

We currently use the v4 parseCandidate function to get an abstract definition of the candidate rule that we can operate on. This will likely need to change in the future as we need to fork parseCandidate for v3 specific syntax.

Additionally, we're inlining a printCandidate function that can stringify the abstract Candidate type. It is not guaranteed that this is an identity function since some information can be lost during the parse step. This is not a problem though, because migrations will only run selectively and if none of the selectors trigger, the candidates are not updated. h/t to @RobinMalfait for providing the printer.

So the overall flow of a migration looks like this:

  • Scan the config file for content files
  • Use Oxide to extract a list of candidate and their positions from these content files
  • Run a few migrations that operate on the Candidate abstract type.
  • Print the updated Candidate back into the original content file.

@philipp-spiess philipp-spiess changed the title WIP Add setup for template migrations Sep 24, 2024
@philipp-spiess philipp-spiess force-pushed the feat/template-migrations branch from 907b5ef to 7ce3641 Compare September 25, 2024 09:47
@philipp-spiess philipp-spiess force-pushed the feat/template-migrations branch from 7ce3641 to da60fb8 Compare September 25, 2024 09:48
@philipp-spiess philipp-spiess marked this pull request as ready for review September 25, 2024 13:08
let candidates = await extractCandidates(designSystem, contents)

// Sort candidates by starting position desc
candidates.sort((a, z) => z.start - a.start)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is smart, I was wondering how you were planning to keep track of the position of candidates changing as you insert replacements 🧠

Copy link
Member

@RobinMalfait RobinMalfait left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pretty sweet! Approved, but a few suggestions / questions.

}
`,
'src/index.html': html`
<h1>🤠👋</h1>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Howdy!

*
* - `underline`
* - `flex`
* - `box-border`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not that it matters, but what was the idea behind this change? Just an example with a special character like -?

Copy link
Member Author

@philipp-spiess philipp-spiess Sep 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haha flex is not a static utility but a functional one now, so this comment is wrong. I put it as a test case and got confused for a second why it didn't work while I implemented my initial version of the printer and then noticed oh it's cause it's functional. I thought I update it just so it's more correct 🙈

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hah right, forgot about that. Good catch!

@philipp-spiess philipp-spiess merged commit 732147a into next Sep 25, 2024
@philipp-spiess philipp-spiess deleted the feat/template-migrations branch September 25, 2024 14:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants