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

Parcel ignores resources declared in JS template literals #3553

Closed
nstansbury opened this issue Sep 24, 2019 · 15 comments
Closed

Parcel ignores resources declared in JS template literals #3553

nstansbury opened this issue Sep 24, 2019 · 15 comments

Comments

@nstansbury
Copy link

nstansbury commented Sep 24, 2019

🐛 bug report

LitElement declares its ShadowDOM HTML in JavaScript template literals. When CSS files or other assets are declared inside the literal string Parcel doesn't bundle them. CSS is especially relevant to allow CSS files to be injected into the ShadowDOM. Eg:

import { html } from 'lit-element';

export default (element) => html`
    <link rel="stylesheet" href="/css/global.css">
    <h1><slot></slot></h1>
`;

The JSX pattern for including resources does not work for template literals

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

parcel --port 8080 --https ./src/index.html --cache-dir ./build/cache --out-dir ./build/make

🤔 Expected Behavior

Assets declared in JS template literals are bundled as normal

😯 Current Behavior

The assets included in the template strings are ignored.

💁 Possible Solution

a) Parcel should parse JS template literals for resources and include them
b) Allow specific resource path strings to be mapped and replaced during the build process
c) Import a path variable that can be included in the template literal as a variable

🔦 Context

Build WebComponents using JavaScript template literals

💻 Code Sample

import { html } from 'lit-element';

export default (element) => html`
    <link rel="stylesheet" href="/css/global.css">
    <h1><slot></slot></h1>
`;

🌍 Your Environment

Software Version(s)
Parcel 1.12.3
Node 12.6
npm 6.9
Operating System Mac OS 10.13.6
@mischnic
Copy link
Member

I don't know if this works with the html template tag, but you could do something like this:

import { html } from 'lit-element';
import global from "/css/global.css"; // is the path to the bundles css file

export default (element) => html`
    <link rel="stylesheet" href="${global}">
    <h1><slot></slot></h1>
`;

@nstansbury
Copy link
Author

I don't know if this works with the html template tag, but you could do something like this:

import { html } from 'lit-element';
import global from "/css/global.css"; // is the path to the bundles css file

export default (element) => html`
    <link rel="stylesheet" href="${global}">
    <h1><slot></slot></h1>
`;

No sadly it doesn't, that's what I meant when I mentioned the 'JSX pattern' not working

@mischnic
Copy link
Member

mischnic commented Oct 1, 2019

With Parcel 2, a custom transformer could easily do Possible Solution a).

@nstansbury
Copy link
Author

I would also add I've now discovered Parcel appears to ignore attributes on Web Components custom elements entirely. For example, implementing a custom <my-video> element ignores the poster attribute and never adds the asset URL to the build.

@mischnic
Copy link
Member

mischnic commented Oct 5, 2019

ignore attributes on Web Components custom elements entirely

You mean when using the element in an html file? The issue here is that Parcel needs to know which attributes to process, there is for example no inherent difference between <div class=...> and <img src=...> if you don't keep a list of attributes that should be transformed. So we would need to make this list easily extendable here.

@nstansbury
Copy link
Author

From a dev perspective it should be simple enough to see if a 404 matches an asset in the source path and then add that attribute and asset to the build, but that doesn't really work for a prod build.

One solution might be to process all attributes on unknown elements, then dynamically add that attribute into a known list if it maps to an asset URL. That way the attribute source list can be built at compile time by the first instance discovered.

@mischnic
Copy link
Member

mischnic commented Oct 5, 2019

One solution might be to process all attributes on unknown elements, then dynamically add that attribute into a known list if it maps to an asset URL.

That way you wouldn't notice a typo in a filename.

That way the attribute source list can be built at compile time by the first instance discovered.

I think it would be easier (and less error prone) to have a config file where you specify elements and attributes to process.

@nstansbury
Copy link
Author

I think it would be easier (and less error prone) to have a config file where you specify elements and attributes to process.

I can see your point, but then I'm actually tying my build process into the specifics of the underlying code. Now when a developer changes some attribute in code he now needs to modify the build process config too. It all starts moving away from Parcel's zero-config approach too.

Personally, I'm not keen on the existing asset/attribute detection anyway, I've already had to fork HTMLAsset.js and processSingleDependency(path, opts) to stop Parcel processing moustache values in attributes it thinks should be an asset and so refusing to build.

@mischnic
Copy link
Member

mischnic commented Oct 5, 2019

It all starts moving away from Parcel's zero-config approach too.

I don't like saying this either but: zero-config has technical limits.

to stop Parcel processing moustache values in attributes it thinks should be an asset and so refusing to build

If we just transformed every HTML attribute that looks like a file, our issue track would be full of this....


What would be acceptable for you (between manual config and error-prone automatic detection)?

@devongovett
Copy link
Member

The same issue occurs in JSX. You can't write <img href="/test.png" /> there either. JavaScript is very hard to statically analyze like that. The best way is to import URLs to static assets as @mischnic suggested.

@mischnic
Copy link
Member

mischnic commented Oct 6, 2019

@nstansbury this seems to work: https://codesandbox.io/embed/parcel-issue-3553-1dobw

import { html } from "lit-html";
import img from "./img.png";

export const App = () => html`
  <div>
    <img src="${img}" />
  </div>
`;

@nstansbury
Copy link
Author

nstansbury commented Oct 9, 2019

I can't comment on images specifically as this bug was originally about stylesheets, following the JSX pattern above generates:

import { html } from 'lit-element';
import css from '/css/global.css';

export default (element) => html`
    <link rel="stylesheet" href="${css}">
    <h1><slot></slot></h1>
`;

parts.ts:233 Resource interpreted as Stylesheet but transferred with MIME type text/html: "http://localhost:8080/[object%20Object]".

console.log(css) outputs the result of object.toString() not a string path

Not sure why this issue is closed unless I'm missing something?

@mischnic mischnic reopened this Oct 10, 2019
@mischnic
Copy link
Member

The issue here is that import css from "..../global.css" returns an object because this is how CSS modules would work (it seems like an empty object is returned if CSS modules are disabeld)

This issue is not really specific to lit-html, you can follow this proposal about being able to specify the format of the import: #3477

@benfrain
Copy link

I've been caught out with this a few times too.

Is it too heinous to suggest parcel might support adding an attribute in the template literal to ensure parcel resolves it an associated src or asset type (.jpg/.png/.webp/.svg etc)?

<img alt="" src="{encodeToURLString(offer.title)}.jpg" pjsasset/>

And parcel could strip this pjsasset attribute on transform while ensuring the asset got resolved and sent to dist?

@itamarzil123
Copy link

One Year Later Here.
I'm in a middle of vanilla JS project right now. I'm using template literals for rendering. (basically throwing template literals style html code into the innerHtml of DOM element.
I have the same problem -> <img src="images/pic.png" /> will not work from within a template literal while it does work from within my index.html file.
Finally I chose the solution above, by @mischnic, and so instead of an array containing my list of images, it is an array containing list of import img from '../images'. WORKS FINE. any other solutions in 2020 ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants
@devongovett @benfrain @nstansbury @mischnic @itamarzil123 and others