Enable and Validate Plugin Configuration Options
0:00 All right, let's do this. I'm going to explode my page by removing the themes import, and instead in the config file, this is where I'm going to import the themes, const themes = require, and we'll grab the themes.json file.
0:18 Here where I register the plugin, I can actually call it as a function and pass an options object where we will have colorThemes -- let's call it that -- which is themes.
0:29 This is still not working, obviously, but now, we're going to modify our plugin to accept options and read from that color themes option that we've just passed. Let's get rid of that comment here, and we'll pass our helper functions into the plugin definition down here.
0:46 The plugin function also has the plugin.withOptions API. If you look at the signature, it's fairly similar, except both arguments accept a function which gives you the options inside of it. The first part and the second part of our plugin functions need to be wrapped in another function where you receive the options.
1:07 I'm going to move all the first argument inside there and return it, and we move the comma down one level. Then the second argument, which is our config object, also needs to be wrapped in a function that receives the options. I will move the config object and return it.
1:28 You can see themes is still not defined, because we're receiving the options but we're actually not doing anything with this. Before we can use the themes here, we want to try and grab it from the options. Remember, we have named our option colorThemes. Let's try this.
1:47 Const colorThemes = options. We're hoping that this colorThemes object gives us what we want so we can replace themes in two places. You can see it's still complaining about themes not defined.
2:01 This is because here, we are in the first function wrapper, but there's another one down here where we need to do the same thing, const colorThemes = options, and replace the reference to themes to colorThemes. Boom. It's working again.
2:19 We're not done yet though, because we're passing the colorThemes option, but here, we can assume that this is going to exist in two places. If it was accidentally called colorTheme, everything will explode, or if no options was passed, the same.
2:35 We could do a check like if we don't have colorThemes, throw new error and here paste a message like the multi-theme plugin expects a colorThemes option passed to it. You also need to check that there is at least one themes object, because here, we're referencing the first object. We will need to add this to the check and then do it a second time down here.
2:58 I think what we can do -- let me remove that -- is create another little helper function here, check for valid color themes input. I'll paste some code here and explain what it does.
3:11 We have a checkForValidColorThemesInput function, and it checks that the type of input is an object. It also checks that there is at least one object inside which is of type object. Essentially, it checks that we have a colorThemes object with at least one object inside of it.
3:31 If it's not valid, it's going to throw this error saying that it expects a colorThemes option passed to it, which must contain at least one theme object.
3:40 Now we can go where we grab our colorThemes option, and checkForValidColorThemesInput, and pass our colorThemes object, which is going to make it throw the error here. I will paste that same line down here after we do the second check.
3:57 That gives us a much more robust approach now. You can see that we have this meaningful message, because we're currently not passing a colorThemes object. If I fix it to colorThemes, it will now work, but let's pass an empty object, which means that it doesn't have a nested object inside of it. Again, we're going to have the same problem.
4:22 With all that, our plugin is slowly but surely reaching its final form. Let's recap everything we've done.
4:28 We've defined helper functions to get R, G, B channels to generate CSS variables, generate utility classes, and validate the correct input from the theme object.
4:39 Then in our plugin, we use plugin.withOptions, which lets us grab the colorThemes options passed to the plugin. We check that it's valid, and if it is, we can add the root scope CSS variable and the themes scope CSS variables.
4:55 Once we've done that, we also grab the colorThemes option from the second argument and we check that it's valid. If it is, we extend the Tailwind config theme object with our getColorUtilitiesWithCssVariableReferences, where we grab the first object in the theme options.
5:16 This is really nice because anyone can use this plugin and register the plugin in their config file, pass their own themes object, and this will take care of generating all the CSS variables and the custom utility classes for everything to work.