-
Notifications
You must be signed in to change notification settings - Fork 3.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: update to Vite 3 #22915
feat: update to Vite 3 #22915
Conversation
Thanks for taking the time to open a PR!
|
Test summaryRun details
View run in Cypress Dashboard ➡️ This comment has been generated by cypress-bot as a result of this project's GitHub integration settings. You can manage this integration in this project's settings in the Cypress Dashboard |
@@ -21,7 +21,6 @@ | |||
"@vitejs/plugin-react": "1.3.1", | |||
"axios": "0.21.2", | |||
"cypress": "0.0.0-development", | |||
"cypress-plugin-snapshots": "1.4.4", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was actually messing things up by introducing a transitive dependency. I wrote about it here and ultimately did the change as part of this PR.
// receive and inject it. | ||
// For now we just grab any `<script>` tags and inject them to <head>. | ||
// We will add more handling as we learn the use cases. | ||
const root = parse(html) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, what this is doing is basically grabbing any <script>
tags from the html
that might have been inserted by other plugins and sticking them inside our HTML. An example is the vitejs/react-plugin, which injects this code: https://github.com/vitejs/vite/blob/main/packages/plugin-react/src/fast-refresh.ts#L32-L36. A user already reported this: vitejs/vite#9362.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at the Vite docs for this function and it makes me wonder if we could skip the logic of re-inserting the scripts and let Vite do it for us by returning our file and an array of tag
s we want to propagate
(emphasis mine)
The hook can be async and can return one of the following:
- Transformed HTML string
- An array of tag descriptor objects ({ tag, attrs, children }) to inject to the existing HTML. Each tag can also specify where it should be injected to (default is prepending to )
- An object containing both as { html, tags }
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Had a similar instinct, but I ran into a few roadblocks with this. With regards to using the { html, tags }
API for the HTML, by default we inject something like this:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>Components App</title>
</head>
<body>
<div data-cy-root></div>
</body>
</html>
Let's call this baseHtml
. Users can customize this. We could do:
return {
html: baseHtml,
tags: /* tags to inject */
}
Injecting the script
content is pretty awkward, though, since we need to parse the attrs
to use the tags
API, eg:
const tags = scriptTagsToInject.map<HtmlTagDescriptor>(script => ({
tag: 'script',
injectTo: 'head',
attrs: {
// Is it src="..." or module="..."?
// We gotta pass every possible attribute using`node-html-parser`, which is pretty complex.
}
}))
return {
html: newHtml,
tags
}
So we need to parse the tag in even more detail. For many plugins, this API works great, like plugin-react
, since they know they want to inject type="module"
: see here. For us to use this API, we need to completely pass everything, which can include <script>
, <div>
, etc - this seems challenging and prone to error.
I could be missing something, it would be much nicer to use the Vite API than string substitution, definitely open to improving this if you see some alternatives. Right now there are definitely edge case, for example, if another plugin injects a <div>
or something in <head>
we won't grab it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yuck. Makes sense to just stick with the string sub for now, adding that level of additional parsing feels very brittle and error-prone
|
||
const scriptTagsToInject = root.childNodes.filter((node) => { | ||
// @ts-ignore | ||
return node.rawTagName === 'script' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, the type defs in node-html-parser
seem to be wrong.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is because childNodes
is an array of Node
which is an abstract class - implementations include TextNode
for comments (which have no tag name) and HTMLElement
which has the rawTagName
field. To make TS happy you'd need to do a type-check here then you could use the field name without the linter override
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice catch! I just did:
const scriptTagsToInject = root.childNodes.filter((node) => {
return node instanceof HTMLElement && node.rawTagName === 'script'
})
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here: 0d12d60
@@ -1,4 +1,4 @@ | |||
import { makeConfig } from '../frontend-shared/vite.config' | |||
import { makeConfig } from '../frontend-shared/vite.config.mjs' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So Vite is all-in on ESM it seems, we cannot use ts
like this. We either need type: module
, or something r;dr... I'm not sure what. I tried a bunch of things but no luck.
I think vite.config.mts
should also work, except we need to update TypeScript, which is pretty involved. The least invasive change is to use mjs
here. Once we update to TypeScript 4.7, .mts
should work.
I am a little bit tired of this module madness. I thought we were finally coming to an agreement in the ecosystem, but now we have mts
, too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Worth writing up an issue and tagging it here for eventual cleanup? I suspect we'll never circle back unless we're reminded
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea, done: #22945.
I think it's probably a config and/or TS version related issus.
@@ -72,51 +72,12 @@ shikiWrapperClasses computed property. | |||
</div> | |||
</template> | |||
|
|||
<script lang="ts"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved to it's own file. Vite is more strict and complained about this export.
In general I think it's better if vue
files only have 1 default export, anyway.
@@ -10,17 +10,6 @@ | |||
local("Roboto-Light"), local("DroidSans"), local("Tahoma"); | |||
} | |||
|
|||
/* Set up Roboto for modern browsers, all weights */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is actually incorrect, and introduced inconsistencies in the style of our code font.
I deleted it. Of course, now Percy is showing lots of changes - the snapshots are now more consistent.
Note when looking below, focus on the g
in integration
(the word integration
appears twice).
Before (Percy)
After (Percy)
Figma (how it should be)
You can see that, based on the Figma mocks, the font should be the one with the stylized, curvy g
.
@@ -0,0 +1,8 @@ | |||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Above file has the Vite 3 test:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pulled down locally and everything seems to be working as expected. Left a couple "whatabout" comments that I'll leave up to you whether to incorporate, and one for a TS issue that might be good to fix
npm/react/package.json
Outdated
@@ -30,7 +29,7 @@ | |||
"rollup": "^2.38.5", | |||
"rollup-plugin-typescript2": "^0.29.0", | |||
"typescript": "^4.2.3", | |||
"vite": "2.9.5", | |||
"vite": "3.0.2", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just an FYI - Vite released v3.0.3 yesterday (CHANGELOG)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I updated to Vite 3.0.3! 84ee67e
// receive and inject it. | ||
// For now we just grab any `<script>` tags and inject them to <head>. | ||
// We will add more handling as we learn the use cases. | ||
const root = parse(html) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at the Vite docs for this function and it makes me wonder if we could skip the logic of re-inserting the scripts and let Vite do it for us by returning our file and an array of tag
s we want to propagate
(emphasis mine)
The hook can be async and can return one of the following:
- Transformed HTML string
- An array of tag descriptor objects ({ tag, attrs, children }) to inject to the existing HTML. Each tag can also specify where it should be injected to (default is prepending to )
- An object containing both as { html, tags }
@@ -1,4 +1,4 @@ | |||
import { makeConfig } from '../frontend-shared/vite.config' | |||
import { makeConfig } from '../frontend-shared/vite.config.mjs' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Worth writing up an issue and tagging it here for eventual cleanup? I suspect we'll never circle back unless we're reminded
let indexHtmlContent = await fs.promises.readFile(indexHtmlPath, { encoding: 'utf8' }) | ||
|
||
// Inject the script tags | ||
indexHtmlContent = indexHtmlContent.replace( | ||
'<head>', | ||
`<head> | ||
${scriptTagsToInject.map((script) => script.toString())} | ||
`, | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Total nitpick, but could avoid the let
here by promisifying this:
const indexHtmlContent = await fs.promises.readFile(indexHtmlPath, { encoding: 'utf8' })
.then((content) => {
// Inject the script tags
return content.replace(
'<head>',
`<head>
${scriptTagsToInject.map((script) => script.toString())}
`,
)
})
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Both fine, I think I like let
+ reassign a little more than then
, but good to know this is an option, too.
Thoughts on mixing await
and then
? I usually pick one or the other, but maybe combining them can make code more readable (and avoid mutation).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that it's usually best to avoid mixing async/await
and then
paradigms. I guess there is one other option to just wrap the await statement with parens but that isn't super-elegant either, so feel free to keep it the way it is:
const indexHtmlContent = (await fs.promises.readFile(indexHtmlPath, { encoding: 'utf8' }))
// Inject the script tags
.replace(
'<head>',
`<head>
${scriptTagsToInject.map((script) => script.toString())}
`,
)
|
||
const scriptTagsToInject = root.childNodes.filter((node) => { | ||
// @ts-ignore | ||
return node.rawTagName === 'script' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is because childNodes
is an array of Node
which is an abstract class - implementations include TextNode
for comments (which have no tag name) and HTMLElement
which has the rawTagName
field. To make TS happy you'd need to do a type-check here then you could use the field name without the linter override
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tested it out and it works great! LGTM!!
User facing changelog
vite-dev-server
integration that was introduced by a breaking change in Vite 3. The bug was Vite plugins that modifyindex.html
were not correctly inserted into theindex.html
(eg@vitejs/plugin-react
, example issue.Additional details
Vite 3 came out, it had some breaking changes. Breaking changes. [#6901] fix: sequential injection of tags in transformIndexHtml broke us, specifically.
It's fixed in this PR, see here.
Note: Percy has 51 changes - I actually fixed a bug, Percy was capturing the UI in the incorrect state. See this comment on the problem. I will hit approve before we merge, once everyone is happy the PR.
Steps to test
CI should be passing, and since we use Vite internally a lot, if it's passing, everything should be 💯.
I'd also recommend pulling down the repo, and doing a
yarn dev
in something likepackages/app
with component testing, and making sure it all works as expected.If you like, you could try replicate this user issue and make it it works: vitejs/vite#9362 (we have tests for this specific case - using React and the Vite plugin - but good to be certain).
How has the user experience changed?
Can use Vite 3 with Cypress Component Testing.
PR Tasks
cypress-documentation
?type definitions
?