-
Notifications
You must be signed in to change notification settings - Fork 790
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
feat: component versioning #3138
Comments
Another approach could be https://open-wc.org/docs/development/scoped-elements/ |
Hi All, I just spoke with some on the Stencil team about versioning and was pointed to this issue. My team has a similar scenario where we need to support design system components that could potentially be used in multiple applications that are present on the same page, and there's potential that each application could be using a different version of the design system's components. Especially for the tooling that wraps Stencil components for other frameworks (Angular/React/Vue) It would be great if we were able to keep the framework selectors the same so that each version of the library wouldn't result in a breaking change for consuming applications. IMO it would be best if Stencil could keep track of a generated hash (or a value from config) during build time and append that hash to the web component selector, but keep the original selector for the wrapped framework libraries. Under this example the selector would be defined as I do believe that the scoped elements option is the proper long-term solution, but until the spec is finalized and implemented in enough browsers we'll need a pattern to manage versioning. Since the listed frameworks already hook/scope onto an element this would provide a path forward for those using wrapped stencil components without having to introduce breaking changes to those framework specific libraries with every update. |
Question, how would #3155 relate to this? I feel like, based on the behavior of the compiler's results, either this issue or that one may need to happen first. Or are they effectively accomplishing the same objectives, for the versioning feature? Super interested in forming a strategy around this versioning and bundling behavior so I want to dig deeper. Any advice is super helpful! |
3155 describes a solution for the "collections" folder and a use case where 2 stencil projects working together. I dont have experience with that case but it sounds that this issue here is solving 3155. If the version of a component is being reflect to its tagname at build time, another stencil project could consume that tag. But an app could also use tag version-tag. |
My optional feature request could be covered by "tagNameTransform" but that comes with some points which should be noticed when writing the stencil lib (if a component uses another):
Its possible to solve that but maybe we could have a look at that if we/you design a solution for my optional request. |
Referring to my last post and the optionally requested feature: There is another open issue now: #3269 |
@JSMike were you able to find any work around for this usecase ?. |
@arvindanta No, I haven't had time to focused on this issue. I believe the scoped elements that @danyball linked is a good path forward for pure web-component development, by nesting/bundling web-components in a way to ensure the expected dependency version is used, but it doesn't help for wrapping web-components for use in other frameworks (That would still require using global scope on the page). When component scoping becomes natively available in browsers it would be great if stencil wrapped components were able to be scoped to within the context of the application they're being used with. |
Hi, I am trying to find a way to allow multiple versions of stencil components to be used in the same page. I am thinking of a workaround where we can use a bundler like webpack, write a loader plugin to literally string replace all references to all stencil component tag names to some different tag name of your choosing during the bundle step. so as Devs, you will always use I was able to make this work with Tree shaking. (dist/components) folder . But with lazy loading am getting the below error,
Using Stencil 2.9.0 |
Has anyone tried this https://dev.to/sanderand/running-multiple-versions-of-a-stencil-design-system-without-conflicts-2f46 |
Hi Jared we tried. |
I believe Scoped Custom Elements seems like the best solution, more so considering it's proposed as a web standard. Right now it this polyfill is adding support: https://www.npmjs.com/package/@webcomponents/scoped-custom-element-registry?activeTab=versions, but it's unclear if it could be used with Stencil. Has anyone tried this? |
Got it working by passing the custom registry as part of the the // Add polyfill before.
const registryA = new CustomElementRegistry();
class HostElementA extends HTMLElement {
constructor() {
super();
this.attachShadow({
mode: 'open',
customElements: registryA,
}).innerHTML = `
<style>:host { background: red; }</style>
Wrapper<br/>
<stencil-button>Button inside scoped registry.</stencil-button>
`;
// Pass through registry as options.
defineCustomElements(window, { registry: registryA }).catch(console.warn);
}
}
customElements.define('el-a', HostElementA); The registry is just passed through to the define call via the defineCustomElements options. // Patch. Check if a custom registry is supplied.
const registry = options.registry ?? window.customElements;
if (!exclude.includes(tagName) && !registry.get(tagName)) {
cmpTags.push(tagName);
registry.define(tagName, proxyComponent(HostElement, cmpMeta, 1 /* PROXY_FLAGS.isElementConstructor */));
} In html you can then test it. The button inside <el-a>dsa</el-a>
<br/>
This button should not work:
<stencil-button>Test</patternlib-button> @rwaskiewicz Allowing a custom elements registry in the defineCustomElements options object could work? |
@mayerraphael How were you able to add the custom registry as an option to the |
For anyone looking for a workaround, we managed to make it working doing something like this: const backup = window.customElements;
window.customElements = registry; // pass your registry here
defineCustomElements(...) // call defineCustomElements
window.customElements = backup; // restore the customElements global to what it was That works without any change in Stencyl, but it's not pretty. I'm hoping they can add library support for this soon as @mayerraphael suggested, to stop using this kind of "hacky" workaround |
This only works in older versions of Stencil. In newer versions defineCustomElements is async. See:
|
I wrote a script which manipulates the generates dist/esm sources to also accept a registry as an input parameter: const path = require("path");
const fs = require("fs");
const distPath = path.resolve(__dirname, "../dist/");
const esmPath = `${distPath}\\esm`;
const indexFileName = fs
.readdirSync(esmPath)
.find((fn) => fn.startsWith("index-") && fn.endsWith(".js"));
const indexFile = `${esmPath}\\${indexFileName}`
const fileContent = fs.readFileSync(indexFile, "utf-8");
console.log("Adjust indexFile", indexFile);
return fs.promises.writeFile(indexFile, fileContent.replace(/const customElements = win.customElements;/, "const customElements = options.registry ?? win.customElements;"), "utf-8") I wrote the script for StencilJS 2.22.3 and the "dist" output target so I don't know if this also works for StencilJS versions >= 3. I'm currently in my testing phase so I don't know if this is a viable solution for most use-cases The call to defineCustomElements should look like this: |
Again, defineCustomElements is async in newer Stencil Versions. Imagine you have multiple microfrontends each requiring a custom registry, overwriting the global window.customElements Registry in an async initialization process will be pure chaos. I highly recommend not doing that. The only viable option is that the internal define call in defineCustomElements has the correct registry so it works nicely with the async nature of JS. |
@mayerraphael I agree, the only "proper" solution is support at the Stencil level, but we need workarounds in case that takes a lot... If const backup = window.customElements;
new Promise(resolve => {
window.customElements = registry; // pass your registry here
resolve(defineCustomElements(...)); // call defineCustomElements
}).then(() => {
window.customElements = backup; // restore the customElements global to what it was
}); |
Lets say you have a shell and a microfrontend app, both having different Versions of your component library. // Register shell on global window.customElements.
shell.defineCustomElements();
// Register app.
backup = window.customElements;
window.customElements = myAppRegistry;
microfrontend.defineCustomElements().then(() =>window.customElements = backup); It could happen that the shell is not done regsitering while you override the global window.customElements and also registering other versioned components in between => Chaos. The only thing helping is synchronizing, but this blocks your frontend (one after the other => slow) // Register shell on global window.customElements.
shell.defineCustomElements().then(() => {
// Register app.
backup = window.customElements;
window.customElements = myAppRegistry;
microfrontend.defineCustomElements().then(() =>window.customElements = backup);
}); But i dont wannt to know what other Stencil processes running async this messes with. So just don't do it. It would be easier to patch the Stencil runtime (index.js) by providing the registry in the options than guaranteeing that overriding the global registry works in a correct way. |
I get your point, @mayerraphael, you're right. This totally breaks if the |
@fcano-ut defineCustomElements(window, { customElements: new CustomElementRegistry() }); or return a customElementsRegistry when calling defineCustomElements? const { customElementsRegistry } = defineCustomElements(window); |
Exactly, i just patched the runtime. |
@mayerraphael Isn't the script @FabioGimmillaro is running here, the same as the patch you are doing? The global window.customElements is not being overritten. |
Prerequisites
Describe the Feature Request
It should be possible to set a suffix to the component names that are registered via
customElements.define
to be better hardened when 2 custom elements are "living" on the same page in different versions with the same name.Describe the Use Case
We have our main landing page that is organized with a content management system. And we have several apps, written with angular, react, vue - all using our stencil lib. We have 2 Apps that are loaded at this main page. These 2 Apps are using different versions of our stencil lib that can cause issues on that page. Depending on which app is loaded first on that page and registered "their" customElements first. If app1 uses lib-v-1 with the old, blue button and app2 uses lib-v-2 with the new red one and app1 is loaded first, all the buttons of app2 are blue.
Describe Preferred Solution
Example for mylib version 1.1.0.
At build time:
Add a
customElements.define
with<mylib-button>
and<mylib-button-1-1-0>
.(optional feature) At build time of a consumer of that mylib:
Add a 3.
define
:<mylib-button-consumername>
Describe Alternatives
Waiting for html standard to implement something like
customElements.define('mylib-button', MyButton, { version: 1.1.0 })
.Related Code
No response
Additional Information
I found a solution of how it could work of a lit-element based lib:
https://github.com/axa-ch-webhub-cloud/pattern-library/blob/develop/COMPONENT_VERSIONING.md
The text was updated successfully, but these errors were encountered: