I've been thinking about the architecture of Inertia.js recently, and how best to move the library forward. One of the challenges with the current design is the fact that each client-side adapter (Vue, React, Svelte) all use the Inertia core library as a base. While this seemed like a good design decision at the time, practically it's made the development more challenging for two reasons:
npm link helps, but even shipping simple features often means deploying updates two multiple libraries.So, recently I鈥檝e been thinking about a different approach: merging Inertia core into each client-side adapter. This allows three things:
It does mean that the core logic gets duplicated three times, but that seems like a worthwhile tradeoff. And honestly, there really isn鈥檛 THAT much core logic. Roughly 350 LoC right now. Further, we'd likely be able to make better use of existing adapter library code in the core logic, allowing us to avoid writing some code entirely.
Any thoughts? 馃
I think you have correctly assessed your consideration. If this would have been my project and given the pros and cons I would also choose to merge the core.
Also this way, I guess, it would be easier for other people to contribute to inertia because they usually have 1 adapter and will not test the core for all adapter accordingly.
Aside from things that can be improved for each adapter if merged by the core, how would the workflow for working on new features for the Inertia core look?
Would @sebastiandedeyne or @pedroborges be free to implement any number of feature they see fit or would this go through some sort of vetting or RFC process?
Overall this seems like it would be really beneficial to the ecosystem.
I think that would be a huge improvement for implement new features for each adapter accordingly, my only concern is the same as @Juhlinus , how new features for the core would be delivered?
Great question @Juhlinus. The goal would be the same as it is right now鈥攖o have feature parity as much as possible between the libraries. However, they already differ slightly, and I think that's okay in certain situations. I want there to be freedom for adapter maintainers to optimize the adapter as much as possible for their particular language or framework. That goes for both client-side adapters as well as server-side adapters. 馃憤
My thought:
If proceeded this way
Thanks.
What about using Lerna instead? I don't think duplicating the core into each adapter is a great solution honestly.
I now need to manage two repos (core and the adapter) while developing new features, which practically speaking is just annoying.
[...]
It makes it very hard to improve one adapter at a time.
This would be solved using Lerna.
If changes are required to core that require adapter updates, all three of us have to do that work at roughly the same time.
This wouldn't be solved using Lerna, but on the other hand, I don't think this should happen often. These kind of changes happen on major releases usually, if you're following semver. Not ideal, I agree, but is there a lot of core breaking changes?
We can develop each adapter independently.
I'm not sure if having the logic differ from an adapter to another is really a good idea. Not sure what would be the downsides, but it doesn't feel right to me...
We can really optimize the core logic for that adapter, in ways that I haven鈥檛 been able to do with the shared library.
Do you have an example of a feature that would benefit from custom core logic? And wouldn't it be better if that feature could be added after an update to the core that would benefit other adapters too?
Overall I don't think it would be beneficial on the long term, especially if the Inertia protocol evolves. In my opinion, the best solution for an easier development process would be to use Lerna. That said, those are just my thoughts, and I won't mind that much if you decide to duplicate the core logic. I'm with you.
@innocenzi Absolutely, this is the other approach that I am seriously considering. I am trying to weigh the pros and cons of each approach right now. With Lerna, we could literally have on repo, and that would make me the happiest. I think I'm going to just have to test both approaches, and see which one feels right.
I'm also in favor of using a monorepo (with lerna) instead of copying over the core to each adapter.
I also do think that having the code in one repo is the better idea.
Where there can be Learna there can also be Nx
Also, quite unrelated, but since it's big-change-time, have you considered rewriting Inertia with TypeScript ?
If you do JavaScript, you can do TypeScript, the additional syntax doesn't take long to learn. The big plus is the support for typings for everyone, without having to add a types.d.ts file (the Vue one isn't working btw).
I'd be willing to contribute a lot for this.
I want to outline some things that came to my mind:
Change of thoughts
The current paradigm is a core implementation and we have adapter for different frameworks.
So the main implementation and api is baked into the core which is used by each framework.
new way to look at it
Inertia is more like a protocol which can be written as a specification.
Each Implementation can use those specification as a testing tool.
The single source is the protocol version where all features are specified (RFC) first.
PS:
I rewrote an api with specification first in openapi. I don't know if there is a tool already for a protocol like inertia would be which can be used in testing tools across all ecosystems server and client side.
Without further investigation maybe miragejs could be one part for the client side.
While striving for API consistency between adapters is nobel goal, it's not necessarily practical.
I think it's safe to say that most Inertia users are either using React, OR Svelte, OR Vue, not a combination of them. Inertia adapters should implement patterns based on their host environment, not based on an upstream core package. Side note: the server adapters don't (and can't) have a "core library" either, and we've seen no issues there.
I came to this conclusion working on the React adapter. Inertia is a global, mutable object, and mutable is the bane of React. This can leads to strange behaviour, where the props passed to the page component (which are immutable, since inertia-react acts as a bridge between the mutable and immutable world) don't match the props on the global Inertia object (which is also accessible to consumers)
Vue on the other hand, loves mutable objects and everything works like a charm. If Inertia was built for React first, it'd probably look a lot different.
So, does this mean I'm in favour of dropping the core entirely? Not necessarily. I believe we should redefine the core.
Here are a few things the core takes care of now:
preserveState and preserveScroll based on the request methoda click should trigger an Inertia visit or notThese are just a few examples. What these examples have in common is that they're hard to get right, and don't really depend on the adapters.
On the other hand, which part of the core makes can make it tricky to interop with adapters? State management.
I believe we should rebuild the core to something stateless. We should move the state management to the adapters so they can deal with state in the best way possible. Keep everything server and browser-API related in a core package, because those are hard to keep bug-free and consistent.
Regarding a Lerna monorepo
Based on my experience using Lerna in recent projects, it solves "share tooling across packages" more than it solves "working at multiple packages at once". It helps a bit, but Lerna still requires some npm link voodoo to get things up and running.
"Share tooling across packages" isn't really relevant since React, Svelte, and Vue all have different build requirements.
Jonathan maintains the core and Vue adapter, and it'll be me and Pedro that do 90% of the work on the React and Svelte adapters. It sounds like a monorepo would only increase the odds of getting in each other's way.
That said, I'm by no means a Lerna expert, so I'd happily be proven wrong here!
Also, why should we be the happy few to be blessed with monorepo developer experience? I'd rather look into streamlining the DX for _everyone_, first and third party adapters alike.
Regarding TypeScript
I'll happily discuss the pros and cons, but let's keep this issue on topic since there's already a lot to discuss.
I can't disagree with anything you've said. Especially this part:
I believe we should rebuild the core to something stateless. We should move the state management to the adapters so they can deal with state in the best way possible.
Does it mean that you are in favor of keeping core separated? I think it would be okay to have the core reduced in term of responsibilities, and to keep everything separated into different respositories.
I'm in favor of keeping the core alive, but not required. Adapters would use the core to share code and keep the internals consistent, but you _could_ create an adapter without using the core.
For end users, this also means they'd only need to require a single dependency (only @inertiajs/inertia-vue, opposed do @inertiajs/inertia @inertiajs/inertia-vue).
We use custom adapters built with web-components (via StencilJS). As long as the core doesn't go away and stays up to date I'm fine with the original plan :) thanks!
This is done. #155
Most helpful comment
While striving for API consistency between adapters is nobel goal, it's not necessarily practical.
I think it's safe to say that most Inertia users are either using React, OR Svelte, OR Vue, not a combination of them. Inertia adapters should implement patterns based on their host environment, not based on an upstream core package. Side note: the server adapters don't (and can't) have a "core library" either, and we've seen no issues there.
I came to this conclusion working on the React adapter. Inertia is a global, mutable object, and mutable is the bane of React. This can leads to strange behaviour, where the props passed to the page component (which are immutable, since
inertia-reactacts as a bridge between the mutable and immutable world) don't match the props on the global Inertia object (which is also accessible to consumers)Vue on the other hand, loves mutable objects and everything works like a charm. If Inertia was built for React first, it'd probably look a lot different.
So, does this mean I'm in favour of dropping the core entirely? Not necessarily. I believe we should redefine the core.
Here are a few things the core takes care of now:
preserveStateandpreserveScrollbased on the request methodaclick should trigger an Inertia visit or notThese are just a few examples. What these examples have in common is that they're hard to get right, and don't really depend on the adapters.
On the other hand, which part of the core makes can make it tricky to interop with adapters? State management.
I believe we should rebuild the core to something stateless. We should move the state management to the adapters so they can deal with state in the best way possible. Keep everything server and browser-API related in a core package, because those are hard to keep bug-free and consistent.
Regarding a Lerna monorepo
Based on my experience using Lerna in recent projects, it solves "share tooling across packages" more than it solves "working at multiple packages at once". It helps a bit, but Lerna still requires some
npm linkvoodoo to get things up and running."Share tooling across packages" isn't really relevant since React, Svelte, and Vue all have different build requirements.
Jonathan maintains the core and Vue adapter, and it'll be me and Pedro that do 90% of the work on the React and Svelte adapters. It sounds like a monorepo would only increase the odds of getting in each other's way.
That said, I'm by no means a Lerna expert, so I'd happily be proven wrong here!
Also, why should we be the happy few to be blessed with monorepo developer experience? I'd rather look into streamlining the DX for _everyone_, first and third party adapters alike.
Regarding TypeScript
I'll happily discuss the pros and cons, but let's keep this issue on topic since there's already a lot to discuss.