Hello, I'll use the example from the previous lesson to demonstrate plugin templates. To quickly remind you of what we accomplished so far, from within our module, we inject two plugins, one for the client and another for the server. ts, integrates Vue 3 Observe Visibility third-party package, which is basically a Vue plugin. So we register it to Vue, and it provides us with a directive called Observe Visibility.
which module users can use on HTML elements like so. This triggers a callback function if the element enters the user's viewport, which enables the module users to implement features like infinite scroll like we did here. But in the case the app is server-side rendered, the server would return an error because we only have this directive on the client side. So, fix that, we used the directive name to add an empty directive on the server in the observer-stop plugin.
If any of that confuses you, just have a look at the previous lesson. Okay, now let's imagine a scenario where we want to enable the module users to optionally opt-in and out of the Observe Visibility feature, but without having to modify their code at all. ts file, like so. I'll get rid of that and go with inline options.
I'll wrap the module with an array. and pass an object with the module option, let's call it activateObserver, and we'll set that to false. And when they pass this option, the module users will expect the ObserveVisibility feature to simply not to work, even if the directive is still being used in their codebase. So let's work on making this happen.
You could be thinking that we can just modify the interface and add the option type, which is a boolean, then add a default value to it, and this should be activated by default unless the user says otherwise of course then just maybe wrap the entire plugin registration logic with an if condition like so so this will only inject the plugins if the activate observer option is true but this won't work because if you think about it it will just not add the directive to the view app so the user would still get an error if the directive exists in the code base So how are we going to work around this? Well, for this to work, I need to say inside the plugin, if the module options has the activate observer option set to true, then register the return plugin from the package in Vue. If not, just like what we did with the server plugin, register a useless directive with the same name. A directive that does nothing, it's just for Vue to recognize if found in the HTML.
Then it will be probably a good idea to dynamically import ViewObserveVisibility by using the import syntax, which is supported by Vite. Then return the default exported module. Looking good, and now we can safely remove the import statement at the top. And of course we need to await for the import to happen before we inject the plugin to the Vue app.
And here I'll add async. And of course, again, this is not going to work. Because our module options are inside our module definition. And like we keep saying, modules run during build time and plugins run during runtime.
So, we can't just stretch our hand and grab whatever we want from the module. The plugin here knows nothing about the module or the module options passed by the user. We have to either expose the module options to the runtime config like we learned in earlier lessons, or make our conditional block here run during build time to have access to the module options. And here is when, finally, it's about time I introduce you to plugin templates, which can be injected by Add Plugin Template from NoxKit.
Think of them as a mean to make your plugin work in both build time and run time. This is oversimplification, but to better explain this, it allows you to run some build time only code inside your runtime NoxPlugin. And the way we can use it is like so. I'll first remove this if condition, we no longer need it, and we will use addPluginTemplate instead of addPlugin.
We will still need to provide a source file, and we still get to keep the mode option to limit this plugin to the client side only. ejus. js and web development. Anyway, now we just need to wrap whatever the code we need to run during the build time with scriptlet tags, like this one.
And here is how you close it. And I'll just repeat that with every line of code that needs to run during build time. So right here. And we must not forget about the brackets.
Those single brackets can just give us help. But that's not all. The plugin still has no idea what is this options activate. Where is it coming from?
Well, we will have to explicitly provide that from the module definition. ajs. Then we can pass a new property called options, which we can use to define options that will be passed to the plugin template. And in our case, we want to pass activate option and set its value to the activate observer module option.
One final and important thing. If we did run dev, we will notice an error in the terminal. Imports should be transformed with real imports. There seems to be something wrong with the imports plugin.
Okay, don't worry. We didn't do anything wrong. But while I was preparing for this lesson, I did get frustrated due to this error. I did everything right, but still I'm getting an error.
So, like any frustrated developer, I went to the Nox repository to open an issue, and I provided a reproduction for the error, I explained it, and I patiently waited for a review. Then Danny Rowe was kind enough to explain the missing piece to the puzzle, and commented that it is happening because the new file extension, which is EGS. It turned out that Nox doesn't know anything about this extension, so it won't render it. mjs.
config and set it to false, the module will pass the option to the plugin template and the code within the scriptlet tags will run during build time and just add an empty, useless directive. So let's run dev and scroll all the way down Nothing is happening. But if we set the activate observer to true again, scroll down, the directive works, the callback function works, and the infinite scroll works. Awesome.
There is another cool thing that I want to show you. Taking a look at our plugins, they are really simple and they are not doing much. So it would be nice if we could not have to create a file for each one of them in the runtime directory. And guess what?
You can do just that. ejs and delete the whole file, and in the module definition, I'll create a new variable called clientPlugin, and I'll paste the code I copied from the plugin here between string literals. Then I'll replace the src property with a new one called getContents, and set its value to a function that returns the content of this variable. And since we are inside the module anyway, we don't need the EJS or Skriplet tags to grab the options.
We can just remove all the Skriplet tags, one by one, like so. And that will leave us with only the logic. And then we can say, if the activateObserver option is true, then return this, which is the logic to register the plugin and the directive. and i'll remove the part where we add an empty directive and continue the condition and say if this is not the case then just add an empty directive and right here in this variable we have two plugins that will be injected according to different conditions then we can safely remove the options we're passing to the plugin template it's no longer needed and finally let's make sure that everything works we will run dev and check on the directive, it's working just fine.
Now let's disable the option, and it's not working anymore. And if you're wondering how does this magic even works, all you need to know is that Noxt is storing the file you passed to the getContents method in a virtual file system. But we can change that too. We just need to add writeOption or property and set it to true.
then provide a destination with a resolved full path to the file that Nuxt is going to create for us. So let's put it inside the runtime directory and call the file Observer. And at this point, the plugin will have a dynamic content depends on the logic in the client plugin variable we created. So it doesn't have any scraplet tags or unique syntax.
Whether TypeScript or MJS extensions will work just fine. But I'll go with TypeScript. And now if we run Dev one final time, the file will magically pop up again in the runtime folder. How cool is that?