Skip to content
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

refactor: ensure HTML is stripped of generated blank lines #14274

Merged
merged 9 commits into from
Oct 9, 2023

Conversation

rschristian
Copy link
Contributor

Description

Fixes #14273

Additional context

Let me know if a test case is desired, and how I'm to add one. I did take a look around but found no obvious location; while playground/html exists, it appears to be entirely for browser/integration tests.

What is the purpose of this pull request?

  • Bug fix
  • New Feature
  • Documentation update
  • Other

Before submitting the PR, please make sure you do the following

  • Read the Contributing Guidelines.
  • Read the Pull Request Guidelines and follow the PR Title Convention.
  • Check that there isn't already a PR that solves the problem the same way to avoid creating a duplicate.
  • Provide a description in this PR that addresses what the PR is solving, or reference the issue that it solves (e.g. fixes #123).
  • Ideally, include relevant tests that fail without this PR but pass with it.

@stackblitz
Copy link

stackblitz bot commented Sep 4, 2023

Review PR in StackBlitz Codeflow Run & review this pull request in StackBlitz Codeflow.

@sapphi-red
Copy link
Member

Won't this break HTMLs like this?

<div style="white-space: pre">
  foo
  bar
</div>

I guess changing s.remove part could work.

@rschristian
Copy link
Contributor Author

Ah, shoot, yeah.

I didn't love the idea of changing s.remove as you'd have to extrapolate new start positions for each node to include the preceding whitespace, but maybe that's the correct solution. Will look into it, thanks.

@rschristian rschristian marked this pull request as draft September 4, 2023 07:05
Comment on lines 326 to 339
const nodeStartWithLeadingWhitespace = (
node: DefaultTreeAdapterMap['node'],
) => {
const lineStartOffset =
node.sourceCodeLocation!.startOffset -
node.sourceCodeLocation!.startCol
const line = s.slice(
lineStartOffset,
node.sourceCodeLocation!.startOffset,
)
return /\S/.test(line)
? node.sourceCodeLocation!.startOffset
: lineStartOffset
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Name could probably be better, open to suggestions! My brain is failing me at the moment though.

Simply checks if the content preceding the startOffset on the line is white space only, and if so, moves the startOffset to the start of the line so we remove it too.

@rschristian rschristian marked this pull request as ready for review September 4, 2023 10:44
@sapphi-red sapphi-red added p2-nice-to-have Not breaking anything but nice to have (priority) feat: html labels Sep 6, 2023
rschristian and others added 2 commits September 6, 2023 17:33
sapphi-red
sapphi-red previously approved these changes Sep 10, 2023
lineStartOffset,
node.sourceCodeLocation!.startOffset,
)
return /\S/.test(line)
Copy link
Member

Choose a reason for hiding this comment

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

Maybe we can use line.trim() instead as it's more performant?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I suppose? Personally, I'd find that to be quite unclear and the performance gain is non-existent for this specific use case. You'd need thousands of tags to make a measurable difference.

If that's what you want though I'll switch it over.

Copy link
Member

Choose a reason for hiding this comment

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

It also took me a while to understand what /\S/ does 😅 I'd prefer .trim() and also put a comment above to explain what we're doing. Every little performance counts and they stack over time if we're not careful.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't love \S either tbh, but that's on your linter! 😅 I believe it forced [^\s] to that, which, fair enough, though I wasn't familiar with \S myself.

Will update sometime tomorrow, pretty late here for me.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry, took a bit longer to get to this. Switched over the detection and added comments hopefully clarifying which situations are being covered, lmk if you'd like to see any further revisions.

@sapphi-red sapphi-red enabled auto-merge (squash) October 9, 2023 08:52
@sapphi-red sapphi-red merged commit bc97091 into vitejs:main Oct 9, 2023
9 checks passed
@BenceSzalai
Copy link
Contributor

My project that works fine with beta.4 is broken with beta.5 (and beta.6). I'm not sure, but I am suspicious it is related to this PR.

[vite:build-html] end must be greater than start
file: /path/to/project/apps/client/index.html
file:///path/to/project/node_modules/vite/dist/node/chunks/dep-db07a1ea.js:16180
                if (start > end) throw new Error('end must be greater than start');
                                       ^

Error: end must be greater than start
    at MagicString.remove (file:///path/to/project/node_modules/vite/dist/node/chunks/dep-db07a1ea.js:16180:26)
    at file:///path/to/project/node_modules/vite/dist/node/chunks/dep-db07a1ea.js:38222:27
    at traverseNodes (file:///path/to/project/node_modules/vite/dist/node/chunks/dep-db07a1ea.js:37931:5)
    at file:///path/to/project/node_modules/vite/dist/node/chunks/dep-db07a1ea.js:37935:48
    at Array.forEach (<anonymous>)
    at traverseNodes (file:///path/to/project/node_modules/vite/dist/node/chunks/dep-db07a1ea.js:37935:25)
    at file:///path/to/project/node_modules/vite/dist/node/chunks/dep-db07a1ea.js:37935:48
    at Array.forEach (<anonymous>)
    at traverseNodes (file:///path/to/project/node_modules/vite/dist/node/chunks/dep-db07a1ea.js:37935:25)
    at file:///path/to/project/node_modules/vite/dist/node/chunks/dep-db07a1ea.js:37935:48 {
  code: 'PLUGIN_ERROR',
  plugin: 'vite:build-html',
  hook: 'transform',
  id: '/path/to/project/apps/client/index.html',
  watchFiles: [
    '/path/to/project/apps/client/index.html'
  ]
}

Node.js v20.5.1
error Command failed with exit code 1.

What do you think?

@rschristian rschristian deleted the chore/html-whitespace-removal branch October 12, 2023 00:43
@rschristian
Copy link
Contributor Author

rschristian commented Oct 12, 2023

@BenceSzalai Can you provide a reproduction? That seems like a reasonable suspicion, but I can't think of any failing situations.

@BenceSzalai
Copy link
Contributor

BenceSzalai commented Oct 12, 2023

@rschristian: It is not trivial to make a reproduction because it is a Quasar project, so the Vite configuration is behind layers of abstraction.

But what I can see looking at the exact lines in the stack trace is that this change is the cause:
https://github.com/vitejs/vite/pull/14274/files#diff-89bae1df62862bb7f4a03d82a1e9cbf4ac6d0c042f21fbbacb0a2238bd050042R552

Namely nodeStartWithLeadingWhitespace(node) gives -1 in my case.

Probably it is because my input HTML has no linebreaks at all. So that is an edge case not handled properly I guess.

If I replace this line with Math.max(0,nodeStartWithLeadingWhitespace(node)) the build succeeds, albeit produces broken HTML with large portion missing in the beginning. For a proper fix it needs to be looked into why it ends up calculating -1, but this should be enough I think to reproduce the issue.

The main thing is try to begin with a HTML where all line-breaks are removed before given to Vite.

@rschristian
Copy link
Contributor Author

It is not trivial to make a reproduction because it is a Quasar project, so the Vite configuration is behind layers of abstraction.

That shouldn't matter, we're just looking for the HTML that actually gets passed to this plugin. Inserting a log statement for the html here would probably be enough to spot the issue, if there is one.

@BenceSzalai
Copy link
Contributor

BenceSzalai commented Oct 12, 2023

I did that exactly, that's how I've found out that Quasar removes all linebreaks before the HTML is given to Vite:

<!DOCTYPE html><html><head><title>Web Client</title><meta charset=utf-8><meta name=description content="A new application"><meta name=format-detection content="telephone=no"><meta name=msapplication-tap-highlight content=no><meta name=viewport content="user-scalable=no,initial-scale=1,maximum-scale=1,minimum-scale=1,width=device-width"><link rel=icon type=image/png sizes=128x128 href=/icons/favicon-128x128.png><link rel=icon type=image/png sizes=96x96 href=/icons/favicon-96x96.png><link rel=icon type=image/png sizes=32x32 href=/icons/favicon-32x32.png><link rel=icon type=image/png sizes=16x16 href=/icons/favicon-16x16.png><link rel=icon type=image/ico href=/favicon.ico></head><body><div id=q-app></div><script type=module src=/.quasar/client-entry.js></script></body></html>

And apparently the changed code cannot any more handle this.

@rschristian
Copy link
Contributor Author

Perfect, thank you. Will look into it a bit later.

@BenceSzalai
Copy link
Contributor

Great, thanks for the quick response!

@rschristian
Copy link
Contributor Author

Got it, need to Math.max the lineStartOffset as the column isn't 0-indexed. Will push a fix in a minute.

Thanks for the HTML, helped track this down much quicker!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feat: html p2-nice-to-have Not breaking anything but nice to have (priority)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

(Frivolous) Suggestion: Clean up empty lines in generated HTML
4 participants