7 ms·
Odd that they chose to use the unsafe "eval", where there's a drop-in, safe(r) replacement with ``` const properties = Object.keys(injections); const values =
by catapart 3y ago
Odd that they chose to use the unsafe "eval", where there's a drop-in, safe(r) replacement with
```
const properties = Object.keys(injections);
const values = Object.values(injections);
htmlString = new Function(...properties, 'return `' + templateString + '`')(...values);
```
Other than that weirdness, this is a pretty sweet little library! I've already built my own version of it, so it's nice to see a lot of the same techniques I used re-applied here. Definitely feels like doing something right if other people are thinking along the same lines!
I hope this more project sees more utility than mine! I backpedaled on the idea of 'wrapping' custom web components after I figured that learning my abstractions wasn't any different than learning native web component abstractions (which I couldn't justify). I definitely think there are good uses for this type of thing, but it was very hard to scale it to app level and not have to build data-oriented abstractions which started mildly coupling components (on the interface level, rather than as a requirement for rendering). So once I started breaking down the components abstract enough pieces that they were useful as standalone components, I wasn't using any of my custom functionality, so I just migrated it all back to native, not-extended-from-my-library components.
I'm very eager to see mature projects using these kinds of wrappers, though, because I do think there are ways to push the native web-component experience further, and I think these are the kinds of projects that help with that.
- keepamovin 3y agoThank you for your kind words about Hyphen, catapart! And that is a very sweet little thing, the code. I like your bloody clever little code hack, that is pretty cool. I modified it a little to fit: return new Function( ...Object.keys(this.state), ` return (function ${ this.template.toString() .replace(/^\s*function\s+/,'') }()) `)(...Object.values(this.state)); I might consider including it. Would you like to make the PR? If you don't want to get involved more than that, I get it! No worries. But I mean this you do care about this, and this is your work. It's very clever! My concern is it will fail in some cases that with(state)/eval works in, and require annoying edge-case patching to support cases we already support. For dubious security gains (I don't see the risk of eval here--if anyone can point so specifics I'd value learning!) As to why I didn't choose it, I didn't see it (!!), but I also didn't think I should bother trying to get all properties and values because there are some that can slip through (say state contained some prototype chain and had properties up the chain, these will not be returned by keys). So I just went with with(state), because I know that's safe regarding that. with(state)/eval is simple, neat and elegant. Also I just like the aesthetics of the simpler shorter code. And I guess I like to live dangerously, hahahaha! So those reasons are why I chose it. But your way is very clever! And it looks very clean the way you present it above. I love to see these new, like, tight, idiomatic ways of using JavaScript to push the language. I love what you've done, it's really cool! :) Also, it's good to hear about your experience with that. I encourage you to keep working on your library. I mean, a diversity of approaches is valuable. Even if it's just we're researching and learning and progressing toward something, through our collective effort, even if none of it is actually the destination. Although with multiple demands on your time, I'm sure it makes sense to pull back if you don't see it as worth it. If you want to contribute more of your really clever ideas and experience to Hyphen that would be cool. Maybe not everything is a fit but we could certainly learn from you. Up to you! Thanks for your cool comment on this. High value! Have a good day :)
- catapart 3y agoOh! Ha ha! Nice! I'm glad you like the code! I'd love to tell you it was the product of a journey of discovery that embedded deep magics in a (possibly) one-liner... but it's actually recommended (for the most part) on MDN's documentation about eval (and why not to use it). On that topic, though, my concerns about eval are this: Developer A is using my web components along with advertising that uses library X. Library X is actually malware that tries to harvest user data, but it does provide analytics, so it is silent malware that the advertisers nor Dev A can account for. Since Library X is loaded in an iFrame, it can't execute any code that will be local to Dev A's domain; everything will originate from the ad's domain. But since Dev A is using my web components, which use eval, he can query the dom of the iFrame's parent, get a reference to my component, and then inject the attack in a place that will trigger a re-render. The re-render runs the eval which still creates the html, but it also executes code to harvest user data both from localstorage/browser storage and using any tokens that Dev A's app provides for server access. It then stores a string of malware in the browsers local storage to execute every time the page refreshes. Now, to be clear, I haven't tested this scenario in over a decade. There may be protections, now, that prevent this kind of thing. But that's what my functional testing would be based around: whether or not a third-party can start executing code from my domain. If you can prove that doesn't happen, then I would have much less concerns about security. That aside, I understand your concerns about edge-cases. I can tell you that I haven't done any rigorous testing, in that regard. I used that method for my web components, but I was specifically trying to "not do anything fancy". Where that took me was trying to write loop executions and extracting object properties (like you mention; it's an operation in order to get the [correct] property values out of an object, instead of just the member variables). So even while getting somewhat robust, I didn't have any issues, but there are definitely some annoying things and some stuff to work around. Something that jumps out at me is a weird issue with getting raw DOM text. What I would like to do is query my components for a template element, read the contents of that template element as raw text, and then provide injections for any of the string template variables that are present. The problem, though, is that there is no way to get the RAW text from a DOM element (at least that I could find), without something crazy like a direct text import (heavy cost; latency/bandwidth). If you try to read the text (innerHTML/textcontent/etc) of an element whose content is "My variable name is ${var1}, today.", you will get the string "My variable name is , today." So my work-around was to leave off the $, and just regex for "handlebar" notation (ex: "{var1}"), which I would then prefix just before the template rendering. Not exactly a cheap solution, but fast enough for my work, anyway. As far as contribution, I'm not saying "no", but I'm sure you can imagine I'm working on my own projects. To that end, I'll ping you when the results of my tortured process are public, just in case it's helpful to you (but, again, this project doesn't have a web-component wrapper lib; it's just pure native components). But that's just to say that I don't have a ton of bandwidth, at the moment, to actually contribute code in any standard worth receiving it. That said, it's obviously an area which I'm interested in, so I'm certainly open to being productive! As long as you're good with a slow pace, I'm happy to make some pull requests. In the meantime, though, I did go ahead and upload some of my attempts as a gist: https://gist.github.com/catapart/64c993533b64089f337825d33a5a89bb https://gist.github.com/catapart/64c993533b64089f337825d33a5... In the TS version, you can see the code where I extract the property values, as well as the (pretty buggy) loop functionality. I'm happy to discuss any of it, so if there's an app you're more real-time active on (it's Discord for my default, but I'm open to most apps), just let me know and we can get into it. Anyway, thanks for the high-quality response! Easy to take criticism poorly, so I appreciate that you found it useful or informational in some way! Definitely helps me to have a good day, so I hope you do the same! Edited to add: sorry! I meant to be explicit about this: you are more than welcome to harvest any code from that gist! If any of it is checked in, anywhere, it's licensed under CC0 so you are way in the clear to use it. Just because I might be too busy for a PR doesn't mean I don't want you using "my" code. Let's be real honest - it's a melting pot of stack overflow answers and HN comments. I can't really take particular credit for any of it. So by all means copy and paste or chop and screw to your heart's content! And, of course, if you have any questions about the whys or hows for any of it, just let me know!
- spankalee 3y agoThe custom element spec definitely only deals with the mechanics of when are where to run your component's lifecycle code - it says nothing about data. So your choices are basically property accessors, which are interoperable, but require prop-drilling for global-ish data, or something proprietary like a state management library. The Web Components Community Group (WCCG) is offering something of a third way with the community protocols: https://github.com/webcomponents-cg/community-protocols https://github.com/webcomponents-cg/community-protocols The Context protocol provides tree-scoped ambient data in an interoperable way. It's implemented by Lit and FAST (I believe). It doesn't replace a data store, but it's often used to provide data stores to components, and at least reduce some coupling.
- catapart 3y agoAh, good to know! Hadn't really looked too far into component render batching, but I'm glad there are efforts underway! I just meant that once I was rendering out each component without having to have it's parent know about its sub-components, I didn't have much use for global contexts or passing state to a component, itself. Auto-re-rendering isn't as compelling as just setting whatever data on the attribute change, or via a child element insertion/removal. Child element maintenance requires a lookup, instead of just re-rendering with the new state, but the DOM is really good at querying, so you can just look them up. And so, once you remove auto-re-rendering (first render is still arcane, but it's still a pretty simple code snippet to accommodate templating), and state-based re-render triggering, there's not a lot that is added to the web component that isn't just a "different" way to do it, instead of demonstrably "better". Of course, everyone's mileage may vary! Just my two cents after working with them for a few years. And my work has pushed me toward a "simpler is better" approach, which isn't universal. For people trying to make very complex web components that can benefit from batching tree renders, no amount of breakup will help (and my assumption is that it will actually hurt a lot? Web Components don't feel "cheap" as far as instantiation goes). So it's nice to have people working on these kinds of things so that we can one day get a drop in DAW web-component, or a performant "spread-sheet" component.