@vue/shared generateCodeFrame can infinite loop, breaking a build process.
Version
3.1.1
Reproduction link
https://github.com/DV8FromTheWorld/vue3-sfc-compiler-bug-repro-template
Steps to reproduce
- Pull the repo and
npm install
it. - Run
npm run serve
- Observe how the build process gets stuck and never completes when it is handing the
BrokenComponent.vue
file.
What is expected?
The build should complete and generate warnings about using undecorated <template>
elements in Vue3.
What is actually happening?
The build hangs forever due to an infinite loop when generating the code frame to display with the vue-migration compatibility warning.
I originally encountered this problem when transitioning my application from Vue 2.6 to @vue/compat: 3.1.1
. The problem surfaced as a Webpack build that would never complete the compilation step. The Vue-loader would successfully separate the SFC into the specific template/script/style portions and pitch them, but when compiling the template (thus generating migration warnings) the system will get stuck.
In the screenshot below observe that the generateCodeFrame
will infinite loop.
This will infinite loop because j >= lines.length
will resolve to true and hit the continue
statement without incrementing count
any further.
It is possible that the culprit causing this problem is not so much the generateCodeFrame
function itself but instead the function calculating the loc
for the start and end of the <template> ... </template>
. From my testing, the loc.*.column
and loc.*.line
for both loc.start
and loc.end
were accurate, however the loc.*.offset
for both seemed to be off by 75 - 110 characters. The generateCodeFrame
code uses offset
as it's source of truth here and thus could be the problem here.
Additionally, another issue caused by this oddity with the offset
is that the code frame actually generated has the ^^^^^^^
pointers multiple times in a very confusing fashion. This is caused by the count
not exceeding end
fast enough.
The provided reproduction gives this: and it doesn't hang in an infinite loop.
The loop you showed still increments j
when it continues but maybe end > count
never changes, probably creating the infinite loop you saw
The loop indeed does increment j
but yes, as mentioned before, the problem is that end > count
never changes. You can see that the debugger is currently stopped in a state of infinite loop by comparing the values shown in the Scope tab on the right.
I just freshly pulled the repository, ran npm i
, and npm run serve
.
I was able to see the error still:
Gif showing it happen
I'm not sure what more information I can provide given that the repo does break for me. What direction would you like to pursue for this?
Cleared my node_modules
, installed yarn, ran yarn install
and yarn run serve
.
Resulted in the same behavior.
Here is the generated yarn.lock
file if you'd like to do a diff between it and your own.
https://gist.github.com/DV8FromTheWorld/8bd1dc6dc8288b86663630b44f466324
...I bet this has to do with the way the parser is consuming characters and adjusting offset
. This probably is a difference between \n
and \r\n
given you are on unix and I am on windows.
Yeah, so the problem here is: https://github.com/vuejs/vue-next/blob/master/packages/shared/src/codeframe.ts#L8
Basically, everything else in the parser deals with \n
vs \r\n
by using the .length
property, thus getting 1
or 2
respectively.
This split treats \n
and \r\n
identically, so every time another line is processed, the count
will deviate from offset
by 1 additional character.
If there are enough lines in the file proceeding the piece of code that the generateCodeFrame
is suppose to be generating a codeframe for and not enough content after the fragment to make up for the disparity then this will loop forever.