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
[WIP] Code Generation Plugin System #572
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is looking great already!
Cool, I'll hold off a bit, let me know if you need/want help |
The PR is ready to merge from my end, it just needs another pair of eyes and I think it would make sense when a few people could beta-test So when you like, a review and/or a test would definitely help getting this out 🤙 |
I'll take a look this morning! |
# Conflicts: # packages/codegen/src/generate.ts
Thanks for incorporating my feedback! Mentally iterating on this... With the idea in the back of my head that I would like to put all oazapfts logic into a default plugin I wonder: Should we try to create an architecture that...
The Idea came from looking at the So to support this, I'd suggest to use some sort of a priority setting on a hook rather then maybe we can leverage tapable or a similar library? (I don't have experience with tapable, just was the first solid looking solution that I found) An API for this approach will likely look more imperative, for example: import { createPlugin } from "oazapfts";
export default createPlugin(({ apiGenerator }) => {
apiGenerator.hooks.convertSchema.tap("just-use-any", () => {
return ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword);
});
apiGenerator.hooks.write.tapPromise(
"write-to-cloud",
{ stage: 0 },
async ({ fileContents, fileName }) => {
await myCloud.write(fileContents, fileName);
return true;
},
);
}); Not feeling 100% confident with this. Might also be overkill 🤷 what do you think @fimion ? |
I think that priority is a great idea (I was planning on the priority being the order they are loaded, but different steps may need different priority) If we are wanting to move the code generation process to be a default plugin, then we need to have a way to block cancelling certain steps. or we need to have a baseline set of functionality that always happens before breaking out the default functionality into a plugin itself. Or maybe not, I don't know. I think I'm having trouble thinking of a case where I would want to skip the OAS->AST generating steps, but that is probably just me having a limited perspective.
at work we've currently got a custom script that breaks out code generation into separate files, so being able to use something like this would be great.
I'll take a look.
I think if we can get an interface like this typed correctly, it would probably be the best solution long term. |
Cool! Thank you for the feedback!
I also don't have a concrete scenario for this. My motivation for moving the core into a plugin is
|
@Xiphe I'm also looking at https://github.com/unjs/hookable as an option. It's typescript and is used by nuxt (I'm partial to vue, so i'm extremely biased here) but i think both of these could be good options, and the way they would get used would be very similar. Would you prefer tapable or hookable? |
Skimming over it, hookable seems to have no support for manual ordering as tapable has with Where tapable seems to order taps by Native typescript is a plus. Also I'm actually not a fan of the tapable ergonomics, it just seemed to cover the feature base we might need here and looking at the repo activity of tapable its either done or abandoned 🤷 I'd say let's not block ourselves with this decision, it seems to me as both point towards a similar direction which I like. I'd assume switching should not be a huge deal. That's all to say: I'm happy with both :) |
Might also be straight forward enough to not use a lib here and just maintain a collection of callbacks ourselves 🤔 |
Hi @fimion I gave this another thought... We might also just create the plugin interface as a collection of arrays with callbacks in it. type InitFn = (args: unknown) => unknown;
type ConvertSchemaFn = (args: unknown) => unknown;
type Oazapfts = {
init: InitFn[];
convertSchema: ConvertSchemaFn[];
}; A plugin could then just manipulate these arrays: definePlugin((o: Oazapfts) => {
// add to the end
o.init.push(myImplementation);
// replace all existing
o.convertSchema = [letMeHandleAllOfThis!];
}; I think this is the simplest solution that still provides a ton of flexibility. |
Okay @Xiphe I've made my first pass using the |
Awesome! This looks like a great base to build upon! I'm not too sure if we actually need the How would you suggest we continue from here? |
Yeah I like the idea of merging with #584! |
Gonna merge this to main because:
Going forward I suggest we:
|
in order to completely remove and disincentivize use of class state in favor of OazapftsContext as a preparation for plugin architecture BREAKING CHANGE: ApiGenerator Class has been removed ref #572
🎉 This PR is included in version oazapfts-v6.0.5 🎉 The release is available on: Your semantic-release bot 📦🚀 |
This is the first draft of a plugin system for the code generation part of Oazapfts (Discussion can be found in #562)