Four apps on Vuetify 4: what the official codemod leaves behind
I moved four older Nuxt applications from Vuetify 3 to Vuetify 4. The official vuetify-codemods tool does most of the work, but not all of it. Here is the procedure that worked and the two scripts that saved me the most time.
First check that the application builds at all
Older repositories have rotted dependencies that have nothing to do with Vuetify. One of my applications did not build even before the migration, because its translation module had gone stale. Had I started with Vuetify, I would have looked for the fault in the wrong place.
So: a new branch, npm install, npx nuxt build. Only when that passes is it worth going on.
The steps
npm install vuetify@^4(and the newvuetify-nuxt-module, if you use it).- Change the style entry
vuetify/lib/styles/main.sasstovuetify/styles. - Run the codemods on every folder with
.vuefiles. - Fix what the codemods left out.
- Build, run, and walk through the application in a browser.
The codemod wants a terminal
vuetify-codemods is interactive: it asks which changes to apply and refuses to start without a terminal. So it will not simply run in a script or in CI. Give it a pseudo-terminal and send Enter every second, since all the changes are preselected:
#!/bin/sh
GLOB="${1:-{pages,components,layouts}/**/*.vue}"
(for i in $(seq 1 60); do sleep 1; printf '\r'; done) \
| script -qec "npx --yes vuetify-codemods --files '$GLOB'" /dev/null
Mind the scope. The default pattern covers pages, components and layouts. But .vue files live elsewhere too: in modules, shared, features, or in the root. Run the codemod on each such folder.
Classes the codemod leaves alone
The codemod renames typography classes in templates. It skips them in three cases:
- in
<style>blocks, - in class names built in scripts,
- in files it cannot parse.
A short script that walks the files tracked by git is enough:
import pathlib
import re
import subprocess
CLASSES = {
"text-h1": "text-display-large", "text-h2": "text-display-large", "text-h3": "text-display-medium",
"text-h4": "text-headline-large", "text-h5": "text-headline-small", "text-h6": "text-title-large",
"text-subtitle-1": "text-body-large", "text-subtitle-2": "text-title-small",
"text-body-1": "text-body-large", "text-body-2": "text-body-medium",
"text-caption": "text-body-small", "text-overline": "text-label-medium",
}
PATTERN = re.compile(r"(?<![\w-])(" + "|".join(sorted(CLASSES, key=len, reverse=True)) + r")(?![\w-])")
files = subprocess.run(["git", "ls-files", "*.vue", "*.css", "*.scss", "*.ts"], capture_output=True, text=True).stdout.split("\n")
for name in filter(None, files):
path = pathlib.Path(name)
if not path.exists():
continue
before = path.read_text(encoding="utf-8")
after = PATTERN.sub(lambda match: CLASSES[match.group(1)], before)
if after != before:
path.write_text(after, encoding="utf-8")
print(name)
The mapping is not one to one. Vuetify 4 moved to the Material Design 3 scale, so both text-h1 and text-h2 end up as text-display-large. Look at your headings afterwards. The sizes shift a little.
Traps
git add -Ain an old repository. Older projects often lack.outputand.nuxtin.gitignore. I once committed a whole build that way. Add files by name.- A tracked
.env. Before you write local settings, checkgit ls-files .env. One of the projects had it in git. - Deploying without the local environment. After the migration one application ran only with a variable I had set locally. Without it every page returned 500, and the automatic deployment would have taken production down. Run the application once without your local variables before merging.
How long it takes
Vuetify itself is about an hour for a small application. The stale dependencies around it take longer, and so does the check in a browser, which no codemod replaces.