parcel-bundler / parcel

The zero configuration build tool for the web. 📦🚀
https://parceljs.org
MIT License
43.5k stars 2.27k forks source link

Cannot have Vue components in template with self-closing tags #1363

Closed SteffenL closed 3 years ago

SteffenL commented 6 years ago

🐛 bug report

When I use multiple Vue components in my Vue template, only the first child component of a parent component appears to be displayed after minification. This happens when building the code for production, and does not happen when building for development or when using the --no-minify parameter.

🎛 Configuration (.babelrc, package.json, cli command)

.babelrc: N/A

package.json:

{
  "name": "testapp",
  "version": "1.0.0",
  "description": "testapp",
  "private": true,
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "parcel build src/index.html",
    "dev": "parcel src/index.html"
  },
  "author": "Steffen",
  "license": "ISC",
  "dependencies": {
    "vue": "^2.5.16"
  },
  "devDependencies": {
    "@vue/component-compiler-utils": "^1.2.1",
    "vue-template-compiler": "^2.5.16"
  }
}

cli command:

parcel build src/index.html

🤔 Expected Behavior

All of the components should be displayed.

In my example code, I expect A B C D to be displayed in the UI.

😯 Current Behavior

Only the first child component of a parent component appears to be displayed.

In my example code, A B is displayed in the UI. This means that component A did not display component B and C, and the App component did not display component D.

💁 Possible Solution

🔦 Context

I use multiple components to build my UI, so only some of the UI is displayed right now.

💻 Code Sample

Please see the example code here:

https://github.com/SteffenL/parcel-vue-bug-repro/tree/vue-components

Here is a smaller example template:

<template>
<div id="app">
    <A /> <!-- displayed -->
    <B /> <!-- not displayed -->
</div>
</template>

<script>
import A from './A.vue';
import B from './B.vue';

export default {
    components: {
        A,
        B
    }
};
</script>

Building the code:

npm install
npm run build

Serve the files located in the dist/ directory.

🌍 Your Environment

Software Version(s)
Parcel 1.8.1
Node 8.11.1
npm/Yarn 5.6.0
Operating System Windows 10
SteffenL commented 6 years ago

FYI, this .uglifyrc config is not helping:

{
    compress: false,
    mangle: false
}
SteffenL commented 6 years ago

In VueAsset#compileTemplate, I have found that the value of html.value is unexpected.

Let's say that the original template is the following:

<span>A
    <ComponentB />
    <ComponentC />
</span>

At some point, that turns into the following:

<span>A <ComponentB> <ComponentC> </ComponentC></ComponentB></span>

This then goes through the Vue template compiler.

I was able to work around the problem like so:

<span>A
    <ComponentB></ComponentB>
    <ComponentC></ComponentC>
</span>

Something is doing the wrong thing here.

houd1ni commented 6 years ago

I'm working on the project with full-featured vue, and that's working good. Could you, please, make a minimal reproducing repo?

SteffenL commented 6 years ago

@houd1ni I listed one before:

https://github.com/SteffenL/parcel-vue-bug-repro/tree/vue-components

Did you try this?

houd1ni commented 6 years ago

@SteffenL nope. Haven't noticed. Will do :+1:

houd1ni commented 6 years ago

@SteffenL

Good news: your problem is connected with mine one https://github.com/parcel-bundler/parcel/issues/1294

You may install the bundler locally, like I do with every project, then change that lines of code (see last messages there). You'll have good minified vue project :smile:

Then wait until the maintainers apply the fixes.

SteffenL commented 6 years ago

@houd1ni Thanks for letting me know!

@DeMoorJasper I have investigated further and found that the problem is with posthtml and its lack of support for self-closing custom tags.

const posthtml = require('posthtml');

const html = `
<span>A
    <ComponentB />
    <ComponentC />
</span>
`;

const result = posthtml()
  .process(html, { sync: true })
  .html;

console.log(result);

This code turns the template into the following:

<span>A
    <ComponentB>
    <ComponentC>
</ComponentC></ComponentB></span>

There is an open issue for this: https://github.com/posthtml/posthtml/issues/221

Based on comments, I added xmlMode: true:

const posthtml = require('posthtml');

const html = `
<span>A
    <ComponentB />
    <ComponentC />
</span>
`;

const result = posthtml()
  .process(html, {
    sync: true,
    xmlMode: true
  }).html;

console.log(result);

This results in the following template:

<span>A
    <ComponentB></ComponentB>
    <ComponentC></ComponentC>
</span>

This does not seem ideal to me, and it would be a breaking change that would require users to write templates with XML syntax.

Example:

<span>A
    <img>
    <ComponentB />
    <ComponentC />
</span>

Turns into:

<span>A
    <img></span>
SteffenL commented 6 years ago

Code for reproducing the problem with posthtml: https://github.com/SteffenL/parcel-vue-bug-repro/tree/posthtml

Run it with node src/index.js.

SteffenL commented 6 years ago

This has been reported before: #1103 Related PR: #1316

luikore commented 6 years ago

Passing the recognizeSelfClosing: true option to posthtml fixed self-closing tags for me in dev mode. And this option doesn't force html tags like , , to close.

But there is another html compressing pass (htmlnano) that I don't know how to fix, thus I skipped it for my project.

DeMoorJasper commented 6 years ago

What if we change

if (descriptor.template) {
      parts.push({
        type: descriptor.template.lang || 'html',
        value: descriptor.template.content.trim()
      });
    }

Into this:

if (descriptor.template) {
      parts.push({
        type: descriptor.template.lang || 'html',
        value: descriptor.template.content.trim(),
        final: true
      });
    }

Inside vueasset.js than it should skip all the processing after vueasset.js for html

Sent with GitHawk

houd1ni commented 6 years ago

@DeMoorJasper

Just checked. It works in the case of usual vue tempalts, but draws nothing in the case of pug. <template lang="pug"> I've previously installed pug.

DeMoorJasper commented 6 years ago

@houd1ni Ow yeah the final flag would tell parcel to not transpile pug, hmm that sucks. Not sure how to fix this anymore.

houd1ni commented 6 years ago

@DeMoorJasper

It's interesting that by idea (and it's realised for webpack by vue-loader), template language should be transpiled to vue-template (that is valid xml) and then to JS-render-functions, that I've mentioned earlier.

So, I guess, that VueAssets could transpile template, if some lang attribute is presented, to xml(html), then feed it to vue-template itself, huh?

oksurf commented 5 years ago

Still seeing the same problem with the latest code.

Changing: `A

<ComponentC />

to: A

<ComponentC></ComponentC>

` seems to work but wish they would fix this...

evanleck commented 5 years ago

@oksurf add the following to your package.json:

"posthtml": {
  "recognizeSelfClosing": true
}
n0v1 commented 5 years ago

Thanks @evanleck. That works for me.

Another possibility is to add a .posthtmlrc file to the root of your project with this content:

{
  "recognizeSelfClosing": true
}
puresick commented 5 years ago

Thanks @evanleck. That works for me.

Another possibility is to add a .posthtmlrc file to the root of your project with this content:

{
  "recognizeSelfClosing": true
}

Are there any caveats settings this somehow as a default for vue projects?

mischnic commented 5 years ago

Are there any caveats settings this somehow as a default for vue projects?

(Here are the docs for that option: https://github.com/fb55/htmlparser2/wiki/Parser-options#option-recognizeselfclosing - posthtml-parser uses htmlparser2 under the hood)

guillenotfound commented 4 years ago

Right now I have self-closing disabled in eslint to avoid this, but it will be nite to have this in parcel 2.x

ffigiel commented 4 years ago

The issue title is a bit misleading. In my experience self-closing tags don't work for html tags as well as vue components, so it's not specific to just using multiple vue components

traed commented 4 years ago
"posthtml": {
  "recognizeSelfClosing": true
}

This isn't working for me. Do I need to do anything else than adding it to package.json?

ncjones commented 4 years ago

Just wasted half a day on this. So infuriating.

DeMoorJasper commented 4 years ago

Pretty sure this has been fixed in Parcel 2 as we no longer run it through posthtml

github-actions[bot] commented 3 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed in 14 days if no further activity occurs.

imolorhe commented 3 years ago

Just wasted a whole day on this issue. I was looking for an easy way to get started with a vue-typescript project quickly and expected the parcel approach to be faster, but ended up debugging this one issue for the entire day.

corwin-of-amber commented 3 years ago

I really think this flag should be set by default in Parcel. This problem does not occur with other bundlers.

imolorhe commented 3 years ago

Parcel updates my package.json with the dependencies it detects I would need (like installing vue-template-compiler dependency). Why can't it also create the .posthtmlrc config file with this? Is there any known case of working with vue where this current behavior is expected (I believe not)?

If not, then why can't we patch this posthtml config already?

rust-floppy commented 7 months ago

I've had the same issue today, and saw an issue from 2018 with a comment saying it's been fixed in this and that version, and now I'm here seeing 2021 comments stating it's still and issue - and I'm in 2024, still experiencing the issue. So I'll just won't use self-closing tags.

Thanks!