nuxt-cereal v1.4.0
nuxt-cereal
🥣 Cereal-ize JSON data into literally-typed constants, enums, & options in Nuxt.
Configuration
- Install the module
pnpm add nuxt-cereal
- Create a config
// ~/cereal.config.ts
import { defineCerealConfig } from "nuxt-cereal/config";
export default defineCerealConfig({
constants: {
foo: "an example string",
},
enums: {
bar: ["primary", "secondary", "tertiary"],
},
options: {
baz: [
{ key: 1, label: "One" },
{ key: 2, label: "Two" },
],
},
});
- Activate the module
// ~/nuxt.config.ts
import cereal from "./cereal.config";
export default defineNuxtConfig({
modules: ["nuxt-cereal"],
cereal,
});
- Eat some cereal, you are done!
Features
Define a JSON config object for:
constants
A key/value collection of static strings & numbersenums
A key/value collection of static string arrays & number arraysoptions
A key/value collection of static option arrays w/key
&label
properties
The config object is "cereal-ized" into read-only literals & implemented in Nuxt using a set of utility types/composables.
Types
Type templates are created using the JSON definitions & are automatically available in composables/components:
Type | Description |
---|---|
Constant | String-literal keys of the constants cereal config |
ConstantValue<C extends Constant> | Literal value of the provided constants config key |
Enum | String-literal keys of the enums cereal config |
EnumValue<E extends Enum> | Literal values of the provided enums config key |
EnumValueItem<E extends Enum> | Template values of the provided enums config key |
Option | String-literal keys of the options cereal config |
OptionValue<O extends Option> | Literal values of the provided options config key |
OptionValueItem<O extends Option> | Template values of the provided options config key |
Example
These utility types can be useful when creating components. Let's say we have a button component that has a variant
property that can be set to either filled
, outlined
, or plain
. We can configure that option as an enum
and use the helper types to define our component properties:
// ~/cereal.config.ts
export default defineCerealConfig({
enums: {
buttonVariant: ["filled", "outlined", "plain"],
},
});
<script setup lang="ts">
// ~/components/Button.vue
defineProps<{
variant: EnumValue<"buttonVariant">; // a string-literal type of the options in our config
}>();
</script>
<template>
<button :data-variant="variant">
<slot />
</button>
</template>
Functions
Type-safe utility functions that enable access to the configuration literal values are automatically imported in any component/composable:
Function | Description |
---|---|
isConstant(key) | Check if a provided string is a constants key & cast to Constant if valid |
useConstant(key) | Grab the literal value represented by the provided constants key |
useConstantsConfig() | Grab the entire constants configuration as an object literal |
useConstantsKeys() | Grab an array ofavailable constants configuration keys |
isEnum(key) | Check if a provided string is a enums key & cast to Enum if valid |
useEnum(key) | Grab the literal value represented by the provided enums key |
useEnumsConfig() | Grab the entire enums configuration as an object literal |
useEnumsKeys() | Grab an array ofavailable enums configuration keys |
isOption(key) | Check if a provided string is a options key & cast to Option if valid |
useOption(key) | Grab the literal value represented by the provided options key |
useOptionsConfig() | Grab the entire options configuration as an object literal |
useOptionsKeys() | Grab an array ofavailable options configuration keys |
Example
These functions can be leveraged w/ generics to extend the power of our components even further. Imagine we have a pre-defined set of dropdowns needed for a form. We can quickly build a component that uses our cereal-ized data to provide a type-safe selector that only needs a key
to get started:
// ~/cereal.config.ts
export default defineCerealConfig({
options: {
breakfast: [
{ key: "cereal", label: "Cereal" },
{ key: "pancakes", label: "Pancakes" },
{ key: "eggs", label: "Eggs" },
],
lunch: [
{ key: "sandwhich", label: "Sandwhich" },
{ key: "soup", label: "Soup" },
{ key: "salad", label: "Salad" },
],
dinner: [
{ key: "steak", label: "Steak" },
{ key: "lobster", label: "Lobster" },
{ key: "risotto", label: "Risotto" },
],
},
});
<script setup lang="ts" generic="O extends Option">
// ~/components/Select.vue
const props = defineProps<{
option: O; // "breakfast" | "lunch" | "dinner"
modelValue?: OptionValueItem<O>["key"]; // if "breakfast" is the option, allowed values are "cereal" | "pancakes" | "eggs"
}>();
const emits = defineEmits<{
"update:modelValue": [OptionValueItem<O>];
}>();
const options = useOption(props.option);
const modelValue = useVModel(props, "modelValue", emits, { passive: true }); // https://vueuse.org/core/useVModel/#usevmodel
</script>
<template>
<select v-model="modelValue">
<option v-for="o in options" :key="o.key" :value="o.key">
{{ o.label }}
</option>
</select>
</template>
Server
The server does not have the access to the rich literal types we have in the frontend, but we can still use cereal-ized data within the server context:
Function | Description |
---|---|
useConstant(key) | Access the value of a given constant key, evaluates to string | number . |
useEnum(key) | Access the value of a given enum key, evaluates to string[] | number . |
useOptions(key) | Access the value of a given options key, evaluates to { key: string | number; value: string}[] . |
License
MIT License © 2024-PRESENT Alexander Thorwaldson