What am I doing wrong?
this._recipe = {
"directions":"<p>In a microwave safe bowl, combine vinegars, tamarind, honey & saffron. Microwave on high for 1 minute. Stir until tamarind is dissolved. Puree tamarind mixture, cashews, 2/3 c. cilantro, garlic, onions, sugar, pepper & cumin in blender. Pour mixture into a bowl and refrigerate until ready to use.</p><p>Gently stir avocados, tomatoes, onion & ½ tsp. cilantro and salt. Distribute evenly among egg roll wrappers. Use the beaten egg to seal the wrapper. Deep-fry egg rolls in 375 degree oil for 3-4 minutes or until golden brown. Drain on paper towel. Slice egg rolls diagonally across the middle and serve with prepared dipping sauce.</p>",
"ingredients":"<p>1 Tbsp. white vinegar<br>1 tsp. balsamic vinegar<br>½tsp. tamarind pulp<br>½c. honey<br>1 pinch ground saffron<br>1/8 c. chopped cashews<br>2/3 c. fresh cilantro<br>2 cloves garlic<br>2 green onions<br>1 Tbsp. sugar<br>1 tsp. black pepper<br>1 tsp. ground cumin<br>¼c. olive oil (*this is the end of the sauce) </p><p>3 large avocados – peeled,pitted,diced<br>2 Tbsp. sun–dried tomatoes in oil – chopped<br>1 Tbsp. minced red onion<br>½tsp. chopped fresh cilantro<br>1 pinch salt<br>1 pkg. egg roll wrappers<br>1 egg – beaten</p>",
"subtitle":"Cheesecake Factory",
"title":"AVOCADO EGG ROLLS",
"md_directions":"In a microwave safe bowl, combine vinegars, tamarind, honey & saffron. Microwave on high for 1 minute. Stir until tamarind is dissolved. Puree tamarind mixture, cashews, 2/3 c. cilantro, garlic, onions, sugar, pepper & cumin in blender. Pour mixture into a bowl and refrigerate until ready to use.\n\nGently stir avocados, tomatoes, onion & ½ tsp. cilantro and salt. Distribute evenly among egg roll wrappers. Use the beaten egg to seal the wrapper. Deep-fry egg rolls in 375 degree oil for 3-4 minutes or until golden brown. Drain on paper towel. Slice egg rolls diagonally across the middle and serve with prepared dipping sauce.",
"md_ingredients":"1 Tbsp. white vinegar \n1 tsp. balsamic vinegar \n½tsp. tamarind pulp \n½c. honey \n1 pinch ground saffron \n1/8 c. chopped cashews \n2/3 c. fresh cilantro \n2 cloves garlic \n2 green onions \n1 Tbsp. sugar \n1 tsp. black pepper \n1 tsp. ground cumin \n¼c. olive oil (*this is the end of the sauce) \n\n3 large avocados – peeled,pitted,diced \n2 Tbsp. sun–dried tomatoes in oil – chopped \n1 Tbsp. minced red onion \n½tsp. chopped fresh cilantro \n1 pinch salt \n1 pkg. egg roll wrappers \n1 egg – beaten"
}
render(){
return html`
<h2>${this._recipe.title}</h2>
<h3>${this._recipe.subtitle}</h3>
<h4>Ingredients</h4>
${unsafeHTML(this._recipe.ingredients)}
<h4>Directions</h4>
${unsafeHTML(this._recipe.directions)}
`
}
is rendering:

what is this._recipe.ingredients?
did you maybe forgot to execute it?
e.g. use this._recipe.ingredients()
Which version of lit-html are you using?
@mariusGundersen the latest.
@daKmoR sorry, I added my object to the issue after the email was sent. It’s just a string with html in it.
This looks like the kind of problem you might get with two copies of lit-html, and passing a TemplateResult from one copy to a render function from the other copy.
The directive function brands a function as a directive by placing it into a WeakMap. If you have two copies of lit-html you get two WeakMaps for branding, and the receiver of a directive value might not have that value in it's branding, so instead of calling the directive it renders its toString().
I wondered abut this looking at the source code, why exactly is the branding needed? Why can't any function be assumed to be a directive?
Because you can also pass functions as values, so you need a marker to differentiate directives from regular functions.
html`<my-element property=${() => 'some function'}></my-element>`
Without the directive marker, lit-html wouldn't know if it should execute that function as a directive, or assign it directly to the property property of my-element.
Ok, so I'm using lit-element, which is in my package.json. I need unsafeHTML from lit-html, but it can't resolve it because lit-html is in the node_modules dir of @polymer/lit-element. So I installed lit-html which resolves lit-html ok, but it sounds like there may be two versions of lit-html now. Should I be getting at unsafeHTML differently? Here's what I've got right now:
import {LitElement, html} from '@polymer/lit-element/lit-element.js';
import {unsafeHTML} from 'lit-html/directives/unsafe-html.js';
I had this issue today using the repeat directive from lit-html alongside @polymer/lit-element. A clear out and Installation of the npm dev package of lit-element seemed to solve the problem for now:
rm -r node_modules
rm package-lock.json
npm install --save @polymer/lit-element@dev
When dealing with issues like this it's always good to run npm ls to see where the different versions are coming from
As this issue has come up before and is very frustrating to debug, it would be nice if there was a better solution than "use single instance of lit-html for all dependencies".
If a new version of lit-html comes out and you have five dependencies that use lit-html, you can't upgrade any of them until they dupport the new version.
You can also run npm dedupe instead of nuking everything btw..
You can force a single version of lit-html by loading it as a peer dependency instead of using npm module resolution. If you publish an element on NPM with a relative import:
import { html, render } from '../lit-html/lit-html.js';
It will always use whatever version of lit-html is installed in the node_modules root by the user. The downside of this is that you do not have control over which version is used, but at least it avoids loading duplicate versions.
Going to close, since this seems like a package duplication issue. We'll try to mitigate that in other ways.
Any progress on the "other ways", since this is biting me now?
If you are using unpkg.com CDN, you can get unsafeHTML in the directives package:
import { html, render } from 'https://unpkg.com/lit-html?module';
import { unsafeHTML } from 'https://unpkg.com/lit-html/directives/unsafe-html.js?module';
Getting hit by this as well, lit-element & lit-html can't be properly imported as ES modules w/o the use of a bundler.
Getting rid of the direct import (from lit-element) to lit-html/lib/shady-render.js (by exporting it on lit-html instead) would fix one issue, properly exporting directives (e.g. unsafeHTML) from lit-html would also be required to prevent users from importing lit-html/directives/unsafe-html.js.
With tree shaking as it is today, I'm not sure I get the rationale for requiring direct imports like that.
Having sub-packages (like preact or svelte are doing) would also be an alternative (compatible w/ ES modules and bundlers alike), if splitting the package is the objective.
+1 for going the sub-package route. While @pika/web supports any entrypoint, Pika CDN only has plans to support sub-packages
Tree-shaking is a feature of tools, not the language, implemented differently across tools, and not fully reliable in the face of such a dynamic language as JS. The "native" tree-shaking system in JavaScript _is_ the module system. What you don't import doesn't load. That's a simple, effective, and broadly supported method.
@FredKSchott regarding Pika: other libraries are using multi-entrypoints as well. Anyone package importing individual modules out of lodash will not work for instance:
Compare unpkg and Pika:
I'm not sure how Svelte is supposed to work. I can't seem to find a URL that returns the svelte/internal module.
Here's a package that uses Svelte and imports svelte/internal: https://www.npmjs.com/package/svelte-fa
On unpkg, you can see the module here, https://unpkg.com/svelte-fa?module where svelte/internal is resolved to https://unpkg.com/svelte@latest/internal?module. So far, so good.
On Pika, I can't get a build of svelte-fa at all. https://cdn.pika.dev/svelte-fa/ returns:
Package not found. It is either missing an ESM "module" entrypoint, or there was a problem building it for the CDN.
Learn more at: https://www.pika.dev/packages/svelte-fa
but there is a "module" field in package.json: https://unpkg.com/[email protected]/package.json So is the Pika build breaking because it imports a module?
Regardless, we can't do a big change to import structure or package layout to support a new tool. I hope Pika ends up supporting the full JS modules system.
If you follow that link, you can see the build result:
“✖ 'svelte/internal' is imported by 'node_modules/svelte-fa/dist/svelte-fa.mjs', but could not be resolved.
Make sure that the file or package exists on disk.”
Svelte isnt mentioned as a dependency or peerDepwndency, but it is imported by the package. So our build failed.
You say “the true module system” but I think you mean your interpretation of “the true module system”. I understand you to just mean “support all package files as valid entrypoints”, which the project views as an anti patern and not really compatible with the goals of the CDN (each package is built ahead-of-time, so building each file as an entrypoint wouldn’t scale). The good news is that for your use case, unpkg should work just fine.
Just sharing our plans in case you want to support the CDN (differential serving, etc).
Ah, that does seems like a problem with svelte-fa. Still, our package structure is totally valid and Pika is the first this I've seen that doesn't work with it. I don't think we can just change for a new tool, and I'm not sure we would want to. It seems like Pika has invented it's own scheme that doesn't work universally. Are the lodash sub-module imports ever going to work?
Unpkg emulates a filesystem over URLs, hence the lodash map import results in 121 http requests to unpkg, It often works, and is very useful at times, yet I'll respectfully disagree that this is the proper use of ES modules.
sub-package approach seems to be the recommended way to go for splitting ESM modules (and having multiple entry points), at least that's what unpkg author is thinking (amongst many others who are already doing that as per that twitter thread/links: stencil, preact, svelte, date-fns, gl-matrix).
While it loading 121 different files is problematic, I don't think it is an issue with the way we use the module system but rather with how a particular module is structured.
It isn't really improper use of ES modules as it is a feature and that is how they are meant to be consumed (importing something from a file). You could also find many excellent modules which cause only 1-2 HTTP requests but still exist across multiple files.
It would be worthwhile to take much care around thinking ES modules are being used improperly as, in the cases I've seen so far, it is rather a question of the individual module's structure rather than usage of the system its self.
On topic, though, this is disagreement around how module systems and bundlers work or are used as opposed to how this particular project should be authored. I don't think it is for the lit maintainers to solve.
Unpkg doesn't emulate a filesystem, The number of requests for unbundled and bundled loading has almost zero relationship between each other. Importing lots of small module is fine, and can result in very few requests with proper bundling. unpkg is not intended for production, and so loading a lot of files isn't too much of a problem.
Production applications bunde and don't typically include multiple copies of dependencies that weren't already duplicated by the package manager. An application that uses lit-html and works without bundling, works with bundling just fine with basically all bundlers.
I only jumped into this thread to share my perspective on package structure and singleton npm package. Hopefully it didn’t come off to anyone as “lit-html is wrong and bad” or “lit-html should change because I/pika says so”
So I'm using lit-element 2.3.1 and lit-html 1.2.1 and still running into this exact problem. I ran npm dedup to make sure there aren't any duplicates and still run into it. I'm not sure where to go from here or how to debug.
I can't share the exact code that I'm using but it is essentially this:
import { html, unsafeCSS } from 'lit-element';
import { unsafeHTML } from 'lit-html/directives/unsafe-html';
export class MyComponent extends LitElement {
createRenderRoot() {
return this;
}
render() {
const myNamespace = 'test';
return html`
<style>${unsafeCSS('/* some css */')}</style>
<h1>Hello, world!</h1>
${unsafeHTML(`<${myNamespace}-tag>Some text goes in here</${myNamespace}-tag>`)}
`;
}
}
There really isn't anything more complex than that. Everything else is just some text and attribute values that are interpolated. I'm really confused on what the problem is or how to even debug it.
I did pull the unsafeHTML() into a variable and console.log(html${dumbCode}) which output a template rendered right in the template result object in the browser console.
If anyone has any suggestions for where else to go for how to debug it or things to try, please let me know!
Any news on this?
@timreichen what news are you looking for? Do you have a specific instance of this problem with certain tools? Did you try npm dedupe? etc.
@justinfagnani sorry for not clarifying at all.
I ran into the same problem, though I am using lit-element/lit-html it in combination with Deno and Bundler and import it from esm.sh. So npm isn't involved at all.
import { customElement, LitElement, html } from 'https://esm.sh/lit-element';
import { unsafeHTML } from 'https://esm.sh/lit-html/directives/unsafe-html';
const string = "<h1>Hello, world!</h1>"
@customElement("my-component")
export class MyComponent extends LitElement {
render() {
return html`
${unsafeHTML(string)}
`;
}
}
after bundling renders
e => { if (!(e instanceof c))
throw new Error("unsafeHTML can only be used in text bindings"); let t = B.get(e); if (t !== void 0 && h(i) && i === t.value && e.value === t.fragment)
return; let s = document.createElement("template"); s.innerHTML = i; let n = document.importNode(s.content, !0); e.setValue(n), B.set(e, { value: i, fragment: n }); }
We are currently discussing the pros/cons of different modules in the deno front end community, I think lit-element/lit-html could become quite popular, if we can make it work.
From what I understood, that occurs if two different instances of lit-html are used? What are the reasons why only one lit-html is allowed?
@timreichen in lit-html 1.x we use instanceof and some WeakMap/Sets to determine the type of values passed to template expressions so we know how to render them. Directives in particular are branded by being added to a WeakSet. When a directive from one copy of lit-html is used in another copy, the brand check fails because there are two WeakSets - one for each copy - and the directive is not present in the one doing the rendering.
In lit-html 2.x we are addressing this by using a special discriminator property on objects we call TemplateResults and DirectiveResults. This will fix this problem going forward, but lit-html 1.x doesn't know about these properties and 2.x won't have the WeakSets, so you will need to upgrade both copies to make multiple copies interoperate.
However... even once we _do_ allow multiple copies to interoperate, it's still not a good idea to needlessly load multiple copies. Since lit-element depends on lit-html, the idea solution here is for the lit-element import above to depend on 'https://esm.sh/lit-html/' as well, so there is only one copy.
This generally requires package resolution to be done ahead of time so that you know you don't need to load 'https://esm.sh/lit-element/node_modules/lit-html/' due to version incompatibilities, but it's a much better solution as it maximizes package reuse. At the limit a naive/greedy resolution scheme could cause a new lit-html (or other common dependency) copy to be loaded for every use.
@justinfagnani Thank you for your explanations. I get what you mean now, although I don't really see any reasons why lit-html module should prevent interoperation between multiple copies. While I agree that package reusability is preferable, this should be handled by the user or bundler rather than the module, no?
I think we should be able to run multiple copies of lit-html that can interoperate with each other, at least that is what is normally the expected behaviour I think.
esm.sh apparently bundles up the dependencies while jspm.dev does not. And therefore jspm.dev imports are working while esm.sh doesn't. That seems to be a pretty weird behaviour to me.
Most helpful comment
If you are using
unpkg.comCDN, you can getunsafeHTMLin the directives package: