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

[simplebar-vue] another attempt to deliver the promised vue2 and 3 cross compatibilty #630

Merged

Conversation

renatodeleao
Copy link
Contributor

@renatodeleao renatodeleao commented Nov 29, 2022

TL;DR

The previous implementations 1.7.0/1.7.1 had to be reverted because the bundled output was not cross-compatible as it just worked on vue@3. vue@2 and vue@3 use different SFC (.vue) compilers and have fundamental differences, so rollup-plugin-vue was basically outputting vue@3 only code. The alternative I choose was to remove the compilation step from the equation by using plain a plain render function for the component template, which gave full control of the output without any compilation step (besides js minification/transpilling).

Details

  1. Since this plugin supports [email protected], exclusively using options API is mandatory
  2. Converted <template> block into a render function. Ugly, but effective. Unit testing snapshots ensure that I didn't make any mistake.
  3. Render function has different signatures from vue@2 to vue@31: vue-demi isVue3 flag is used to conditionally middleware this code appropriately
  4. Since the initial implementation setup code was reverted on 1.7.2 I had to bring it back, please look at fix(vue) make compatible with vue 2 & 3 #609 for implementation details. It's mostly related with isomorphic testing and addition of the props API to complement the data-attributes one.
  5. Add examples apps for other vue versions (@2.6 @2.7) just to ensure it actually works as unit testing already tricked me once. Note that since it uses local paths in package.json, it requires you to build the package before using the demos. Since we're lerna is not managing demos, I usually delete node_modules on each example after building just to make sure it is using the latest version and not some cached one.

Alternatives

There isn't a standard art in authoring isomorphic vue components — note that component UI is a different category from composables/hooks and directives which are fairly easy to write in a cross-compatible way. This comment does summarize the feeling in trying to do so: vueuse/vue-demi#154 (comment). The alternatives fit into 3 categories:

  1. the one I've used, a render function with "adapters"
  2. bundling different versions from the same codebase (.vue) input use each version compiler, then use npm postinstall script to switch the correct version for consumer — I would go for this one if had experience in doing it, but I don't. Original reference and example repo
  3. Give up, shelve legacy versions into a branch, create a new one and refactor the code only to support the latest framework version.

Lessons learned

  • Always test the bundled package before asking to release. Just because you're unit tests pass it doesn't mean the package works because you normally test using source code not the bundled version.
  • I now feel that building cross-compatible Component vue libraries is probably a mistake. Just because it's possible it does not mean you should do it.
  • This simplear-vue wrapper is one of the simplest component wrappers you can build and yet I had a lot of trouble making sure it worked correctly on every version. Note that a lot of the complexity comes from supporting <[email protected]. Just supporting the latest 2.7 minor and 3.x would be far easier than this, and to be honest is probably a safe bet for a next minor release as vue 2.x is EOL next year and I'm there's not many people on 2.5.x out there.

Final thoughts

  • Once again, I'm sorry @Grsmto for breaking two versions. I had the best intentions and suffered from my mistakes as well since some of my projects depend on your package.
  • If you release simplebar@6, it's probably a good opportunity for simplebar-vue@2 that only supports vue@3.

Footnotes

  1. https://v3-migration.vuejs.org/breaking-changes/render-function-api.html

This was based on master, do note that there was prior (failed) work at building a cross compatible version that was reverted and this had to be brought into this commit (conditional test-utils, swap-vue, etc)

---

Using a SFC to create cross-compatible component lead us to unwanted bundle results when building the library. Apparently the template compiler used was the one for vue3. Manually changing rollup plugin vue compiler option to explicitly use other vue-template-compiler version did not change the results and even if did, we would need to compile 3 different versions and somewhat do a postinstall script that point consumers to the right one. Not sure how to do that so went with another approach.

The alternative implemented was to convert the component into a pure render function, middleware to adjust arguments to vue2 and 3 signatures. This way there's no transformation when bundling, only minification and transpiling (babel) making output could work as expected even with a slight decrease on readability for maintainers.
- set simplebar-vue dependecy pointing to the local path so that it install the latest version after run build the command. Running yarn add simplbear-vue --force after a new build updates the dependency.
@MichaelFedora
Copy link
Contributor

Alternatively, would it be simpler to just leave a raw .vue file to import? 🤔

@renatodeleao
Copy link
Contributor Author

renatodeleao commented Nov 29, 2022

Assuming that consumers are using some bundling system, yes that would work. But consumers that drop a <script> tag with just the vue runtime still need the components to be pre-compiled, and leaving those out would be a breaking change, I believe, but I've been wrong before @MichaelFedora as we can see by the fact that I've broken 2 releases with my previous suggestions 😅

@Grsmto
Copy link
Owner

Grsmto commented Dec 4, 2022

Hi @renatodeleao and thanks for your hard work on this!
I can't manage to get the examples to work I think, I get this:

[Vue warn]: Error in render: "TypeError: _slots$default.call is not a function"

found in

---> <SimplebarVue>
       <App> at src/App.vue
         <Root>

when loading the page. Is it working for you or I'm missing something?

@renatodeleao
Copy link
Contributor Author

renatodeleao commented Dec 5, 2022

@Grsmto Nope, but I'm going to re-test and clear the cache on each test to be 100% sure. Did you get the in a specific version?

@Grsmto
Copy link
Owner

Grsmto commented Dec 5, 2022

@renatodeleao one thing I noticed is that testing from /examples is not totally "real world" testing because the /examples folder will fallback to the yarn workspace root /node_modules. That's why the previous version of this PR was working locally but not in the real world actually. So ideally we would test with a /examples folder completely outside of the repo code. Or have the directories reorganised like:

  • /examples
  • /lib
    • node_modules
    • package.json
    • /packages
    • ...

Also I usually test with yarn link instead of doing in package.json stuff like:
simplebar-vue: ../../packages/simplebar-vue but tbh it's probably the same anyway.

@renatodeleao
Copy link
Contributor Author

renatodeleao commented Dec 5, 2022

@Grsmto I've retested it, and yes I also usually go with yarn link but I'm not trusting anything automated anymore for this case.

These were my actual QA tests, fully manual mode on:

  1. remove node_modules from root folder
  2. do normal root yarn to install deps
  3. goto packages/simplebar-vue => component.js add console.log to the render function
     render(createElement)  {
        // test
        console.log('testing 1,2,3')
        
        return renderFn({})
     }
  4. run yarn build for simplebar-vue, check dist folder to see that output has the console.log
  5. goto examples/vue-2/, delete local node_modules, run yarn to re-install
  6. manually check that the examples/vue-2/node_modules/simplebar-vue/dist package inside have my console.log
  7. run yarn serve and check the demo is actually working and console.log()
  8. repeated steps 5, 6, 7 for each demo app.

I felt really dumb while doing this, but I'm questioning my sanity so 🤷

Another thing that I did was re-test all these steps above and check if npm run use-vue:x had any impact in the output of yarn build (it doesn't)

// packages/simplebar-vue/package.json
// previous used version (defaults to vue 2) does not have impact in output
"build": "npm run use-vue:3 && rollup -c && cp simplebar-vue.d.ts dist/simplebar-vue.d.ts",

But again, please only ship this if you can 100% run this on your end.

@renatodeleao
Copy link
Contributor Author

renatodeleao commented Dec 5, 2022

@Grsmto yeah i can see your point now, all vue2 demos are using [email protected] and not their package.json versions.

@renatodeleao
Copy link
Contributor Author

I'll do isolated apps testing with yarn link outside of the project. I can already reproduce your error!

@renatodeleao
Copy link
Contributor Author

renatodeleao commented Dec 5, 2022

I'll get back to it later. yarn link will not serve us here because it does not install the linked package dependencies at the target location and for vue-demi to actually work it needs to run a postinstall script on the target environment. This means that if we built the library like this npm run use-vue:3 && npm run build and do yarn link, the isVue3 flag is going to be true (because that's "link environment") even if the project where we do yarn link "simplebar-vue" runs on vue2.x (the "target environment"). In real life, in theory, that would not happen because vue-demi would be a dependency of target project and when installed would set the environment correctly (vue2) and thus isVue3 to false.

This needs more investigation, hold it for a little longer.

@renatodeleao
Copy link
Contributor Author

renatodeleao commented Dec 5, 2022

@Grsmto here's how I've test it out for real:

  1. I've branched from this PR into simplebar-vue/actual-cross-compat-dist.
  2. In this branch I've removed dist/ from .gitignore and built the library (doesn't matter the npm use-vue:x, it's not relevant for this scenario, only for yarn link as described above)
  3. I've pushed the branch, so that we could yarn install from the repo.
  4. Since you're repo is a monorepo, I've used gipkg to generate the an installable link from github.
  5. Created vue projects from scratch and installed dependency => https://github.com/renatodeleao/playgrounds
    yarn install "https://gitpkg.now.sh/renatodeleao/simplebar/packages/simplebar-vue?simplebar-vue/actual-cross-compat-dist"
  6. Successfully asserted that things are working (see issues section).

Issues found

  • The library does not work on vue@^2.5.17 which is your current peerDependency. The reason is that vue-demi requires at least 2.6.x. It might be possible to remove the vue-demi dependency and roll our own isVue3 flag, but I'm not 100% sure and I'm now afraid of going into another rabbit hole and find out later that I broke stuff for people using vite vs webpack. Should we do that extra effort for a version that was launched 4 years ago? Up to you.
  • Also since we depend on vue-demi, installing it on [email protected] project requires installation of @vue/composition-api even if we do not use any feature from it. Because vue-demi will import stuff from there, so either the readme would need to be updated or we can try to see if a postinstall script to do it automatically would work. It's a bit annoying but not a showstopper IMO.
     # vue 2.6
     yarn add simplebar-vue @vue/composition-api
     
     # vue >=2.7
     yarn add simplebar-vue
    

Give it a spin yourself and let me know how you want to proceed!

@Grsmto
Copy link
Owner

Grsmto commented Dec 12, 2022

@renatodeleao ok that seems like a painful process to test...
I think we can just release this as a pre-release (what I should have done in the first place) and test it like that.

so either the readme would need to be updated

Can't we add it as a new dependency?

@Grsmto Grsmto changed the base branch from master to vue-test December 12, 2022 23:32
@Grsmto Grsmto merged commit 85f9883 into Grsmto:vue-test Dec 12, 2022
@Grsmto
Copy link
Owner

Grsmto commented Dec 12, 2022

@renatodeleao I released it as simplebar-vue-1.8.0-alpha.0

@renatodeleao
Copy link
Contributor Author

Great @Grsmto, yeah it's a safe move! There should be easier ways to test it, but I've run out of budget for this month, so I can't really investigate it ATM.

Can't we add it as a new dependency?

We can, but @vue/compositon-api is only necessary for vue2.6. We would be shipping and installing an unused dependency the majority of users [email protected] + vue@3 (I think).

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

Successfully merging this pull request may close these issues.

3 participants