{"_id":"@alexvipond/vue-create-provider","_rev":"2-c9c1b44bd76ca67a0125f28f0ebee947","name":"@alexvipond/vue-create-provider","dist-tags":{"latest":"0.0.1"},"versions":{"0.0.0":{"name":"@alexvipond/vue-create-provider","version":"0.0.0","description":"Nearly no-op function to support type inference for provide and inject in Vue 3","module":"lib/index.es.js","types":"lib/index.d.ts","exports":{".":{"import":"./lib/index.es.js"}},"scripts":{"dev":"vite","build:lib":"tsc -p tsconfig.lib.json && vite build --config vite.lib.config.ts && cp types/createProvider.d.ts lib/index.d.ts"},"devDependencies":{"@babel/types":"^7.16.8","@fontsource/inter":"^4.5.0","@vitejs/plugin-vue":"^2.1.0","autoprefixer":"^10.4.2","tailwindcss":"^3.0.18","typescript":"^4.5.5","vite":"^2.7.13"},"dependencies":{"vue":"^3.2.29"},"gitHead":"cbb02e220b726efd5760ea5e1f3417692c9109f0","_id":"@alexvipond/vue-create-provider@0.0.0","_nodeVersion":"17.2.0","_npmVersion":"8.1.4","dist":{"integrity":"sha512-PFN8DLHMtHnGnccRHvbj9YRII77JQP6aRlIi0Kia9DchsNefgLhbjEaMEGOFScUq/LRmnIi31gUiWOFJIxK+Ng==","shasum":"2e77080a03fd26e2f10056277351597d5266745f","tarball":"https://registry.npmjs.org/@alexvipond/vue-create-provider/-/vue-create-provider-0.0.0.tgz","fileCount":5,"unpackedSize":32736,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh9yGOCRA9TVsSAnZWagAAUg0P/0JuuJxIKJ13tfzNFNsw\nVAFPnNhfCNOwDSHnyMsNhTnseFIYA0dad9vAVZG1lhOY7GKs+BXWRHPpnzrZ\nHHTH4TevFtoCJfmoJs4l4ahwlnYkRA/I0u6izudGXmlVKpfcsSQBuOgh6ePm\nZHliS7vUdvAKz9vyNCBP3xksJeqQHI99siFJ8hUtTl/qpfQJXQ90DNYyGfR8\n0tKsZonByM/UT5dk03SQJdRVQiNRA83fchz9jjKoUV1DMJzTjJCRkD+9TVqM\n1rE0isAzEGOOZXG799AoFiPazzAqCQm5KktXlu+pka2YoORmgKO5SxjxOfog\neeDSlhNTgNUMB4dcf8wGh9Fq2NJ/+SyHoGtTS2mx2Bga6zzqaL1UYXH8Ij9K\nkouMXN/fHJU14MnsiuhUazZNVlrMJuIzey26E/31z39o+SYLZTT0y789VMCR\nM6Bffzy+3VDnZ57mcfPIt0SCZSR4ELn3VOYm+WiINxOca5eXyRbJsaSthOvc\n+nFPtGMBad5z1JO54e5B5F7Noqz5Faq8Ltqzd6/iJIL8AOih+9vUN8/eZQzz\nRWHT3iK/lCoFeIqN3Q/St+VHrN2/I8fDj4SXLqTEDhA0TpLBW3Qgm9Uxauyl\nq1SuvdQ9yIHg8xjvszeYtDsEDVwbFYJb3n2/NObtld756gybXVKZKorEQDrK\nrhdR\r\n=uXNg\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDsU2PxUD5flYXri1x9GlGSmOvHp64qYSOdrQwmDGtkHQIgEE/LN6gwd7+mK0ta5J+totwqjDX7gSl0DCrEkGgvGds="}]},"_npmUser":{"name":"alexvipond","email":"hello@alexvipond.dev"},"directories":{},"maintainers":[{"name":"alexvipond","email":"hello@alexvipond.dev"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/vue-create-provider_0.0.0_1643585934290_0.3223447920204219"},"_hasShrinkwrap":false},"0.0.1":{"name":"@alexvipond/vue-create-provider","version":"0.0.1","description":"Nearly no-op function to support type inference for provide and inject in Vue 3","module":"lib/index.es.js","types":"lib/index.d.ts","exports":{".":{"import":"./lib/index.es.js"}},"scripts":{"dev":"vite","build:lib":"tsc -p tsconfig.lib.json && vite build --config vite.lib.config.ts && cp types/createProvider.d.ts lib/index.d.ts"},"devDependencies":{"@babel/types":"^7.17.0","@fontsource/inter":"^4.5.5","@vitejs/plugin-vue":"^2.2.4","autoprefixer":"^10.4.4","tailwindcss":"^3.0.23","typescript":"^4.6.2","vite":"^2.8.6"},"dependencies":{"vue":"^3.2.31"},"gitHead":"2f477d17a2080a1e58200b0270a39d9478f5a462","_id":"@alexvipond/vue-create-provider@0.0.1","_nodeVersion":"17.2.0","_npmVersion":"8.1.4","dist":{"integrity":"sha512-+TRoK/v0PDKgQaWQf0yEaG4gfjlKBj4lQimzEKdgMOKrS//GXp+Z96O6YB6KSV/0+YqQcu4ZMOOULJSffFIg5w==","shasum":"b1c15fea92544d08cbc8d648b227c9a3c9d65671","tarball":"https://registry.npmjs.org/@alexvipond/vue-create-provider/-/vue-create-provider-0.0.1.tgz","fileCount":5,"unpackedSize":32849,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiO07JACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmq5ng//SqgiK0pESHq4rHdWylVL+QYUrjoN0Lj/P8/OmvDVB9z8JRwZ\r\nEAx1ZiInks6rytP9a/dza+M1TpZvYzzy4M6NMP2t/m1O0LrhsCLfGA2W8DSd\r\n+IVwcEehuRhzi3LZe7HcleXpyJPX6Auyl0Apz/KGqqZfyfzP/GMu09BcWQ2j\r\nUSW0Hfe0wXVnTob/cwa/LoLXfAv6CJEPXWCYosQyBOTu48t/tLbMfJJ9eO54\r\nYhtGbhcpIJKeAhGqbgL2Cl3gyKLGYHrXgHt7Bmz9Iw59g/6+JCR1ZPbgciD3\r\nYsRXttqKrBRCINzjSjFjQDZthBXUkRDJ3edtLVpXrxtuQrnH0TCX7L0ANS/A\r\npHobzOfiNQPrH2D9P/Q5XdLItHT1MEJTPkzkXXEH8Qur18ys07LMJkoCN0zX\r\nJdUSuzi5QxKzwh7PAL8IHn6MvE59t63Lr3csQ5PRfivZKk0FZ3NRsdfuWN5c\r\nxWIAsR1SE1mv/97T6AICUuUIHcfXVDp+wK5g+k1Try9PkS6OvqBMLc/9W53j\r\nZjlF7ZXOBAJ49fIRMG4YeKTW6MfTAQmln0MFjz3jwfV+aGLuDv+iIGUG+tkJ\r\nrPeAdscdzA+++OxshlmwEUsi8k8fRaABuQrw5gSxqQq2sqRM1E1sb7m3oFCW\r\nvP2kSXBKjcGcE+GbUZoyyf/sileKzwAXQ/0=\r\n=f3AE\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHyIsLh+BxLSBa6mFPa1lU0AXlZd4AH5uKoy1Voo93RcAiEA5b6b4wN0nbNZfkr3yvGnV5pjmXxfhceTcXPTnBSlCJY="}]},"_npmUser":{"name":"alexvipond","email":"hello@alexvipond.dev"},"directories":{},"maintainers":[{"name":"alexvipond","email":"hello@alexvipond.dev"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/vue-create-provider_0.0.1_1648053961585_0.25735508753927117"},"_hasShrinkwrap":false}},"time":{"created":"2022-01-30T23:38:54.236Z","0.0.0":"2022-01-30T23:38:54.637Z","modified":"2022-04-04T12:41:58.106Z","0.0.1":"2022-03-23T16:46:01.755Z"},"maintainers":[{"name":"alexvipond","email":"hello@alexvipond.dev"}],"description":"Nearly no-op function to support type inference for provide and inject in Vue 3","readme":"# `createProvider`\n\n`createProvider` is a nearly no-op function to support type inference and easier type safety for `provide` and `inject` in Vue 3.\n\n\n## Installation and usage\n\n```bash\nnpm i @alexvipond/vue-create-provider\n```\n\n```ts\n// MyComponentGroup.ts\nimport { ref, h } from 'vue'\nimport { createProvider } from '@alexvipond/vue-create-provider'\n\n// Use the `createProvider` function to assemble a parent\n// component that needs to provide data to its component tree.\n//\n// `createProvider` will return an object with an\n// `injectionKey` (Symbol) and an assembled Vue component.\n//\n// Let's go through each of the function's parameters to see\n// how things work, and how data ultimately gets provided\n// to descendants.\nconst { injectionKey, component: ParentComponent } = createProvider(\n  // The first parameter is `parentProps`: the `props` definition\n  // for your parent component (the component that is supposed\n  // to provide data).\n  //\n  // Format `parentProps` exactly as if you were passing it to\n  // the `props` option of a normal component.\n  {\n    initialCount: {\n      type: Number,\n      default: 42,\n    }\n  },\n  \n  // The second parameter is a callback function that we'll \n  // call `createProvided`.\n  //\n  // `createProvided` is basically a component `setup` function\n  // but instead of returning a render function or data for your\n  // Vue template, it should return the data that you want to\n  // `provide` to the component tree.\n  //\n  // Just like `setup`, `createProvided` accepts `props` and \n  // `context` as its only parameters.\n  //\n  // If you're in VS code, you can hover over `props` at this\n  // point to see that data types have already been inferred\n  // from the `parentProps` parameter. In this example,\n  // `props.initialCount` would automatically be type-checked\n  // as a number.\n  (props, context) {\n    // Write code exactly as if you were in the `setup` function.\n    // Behind the scenes, `createProvider` will run this code\n    // inside your component's `setup` function, so it all works\n    // the same way.\n    const count = ref(props.initialCount)\n\n    // Instead of calling the `provide` function with an injection\n    // key and the data you want to provide, just return that data\n    // from this function. In most use cases, this will be an object\n    // that holds multiple reactive references.\n    //\n    // Behind the scenes, the data will get passed to `provide`,\n    // and it can be accessed from child components.\n    return { count }\n  },\n\n  // The third parameter is `parentRender`. This is a callback\n  // function that should return a render function.\n  //\n  // `parentRender` accepts `props` and `context` parameters,\n  // just like `setup` normally would. In addition, it accepts\n  // a `provided` parameter, where you can access the data returned\n  // from your `createProvided` function (the second parameter of\n  // `createProvider`).\n  (props, context, provided) {\n    // Right here, TypeScript will know that `count` is a reactive\n    // reference to a number. Automatic type inference is already\n    // working!\n    const { count } = provided\n\n    return () => h(\n      'div',\n      [\n        // Render the values of reactive references from `provided`,\n        // just like you normally would in a render function returned\n        // from `setup`.\n        h('span', count.value),\n        // You can also use `props` or `context` to render prop\n        // values, slots, etc., just you normally would in `setup`.\n        context.slots.default()\n      ]\n    )\n  },\n\n  // For the fourth and final parameter, provide a name (String)\n  // for your injection key.\n  'my injection key'\n)\n\n// With all of that work done, `createProvider` will assemble your\n// parent component from the pieces you've provided. That component\n// will use `provide` internally to make sure data is provided to the\n// component tree.\n//\n// Also, with the magic of TypeScript's type inference and generic\n// types, all type information about your provided data is stored\n// in the `injectionKey`\n// \n// You can now use that injection key in child components with\n// full type safety.\nconst ChildComponent = defineComponent({\n  setup (props, context) {\n    // `count` will be correctly detected here as a reactive\n    // reference to a number! No need to maintain or pass around\n    // manual type definitions of provided data—everything is\n    // inferred and type-checked automatically.\n    const { count } = inject(injectionKey)\n  }\n})\n\n// The injection key and any defined components can be exported\n// normally.\nexport {\n  injectionKey,\n  ParentComponent,\n  ChildComponent\n}\n```\n\nHere's that same code without comments, to give you a better sense of code size and shape:\n\n```ts\n// MyComponentGroup.ts\nimport { ref, h } from 'vue'\nimport { createProvider } from '@alexvipond/vue-create-provider'\n\nconst { injectionKey, component: ParentComponent } = createProvider(\n  {\n    initialCount: {\n      type: Number,\n      default: 42,\n    }\n  },\n  (props, context) => {\n    const count = ref(props.initialCount)\n    return { count }\n  },\n  (props, context, provided) => {\n    const { count } = provided\n\n    return () => h(\n      'div',\n      [\n        h('span', count.value),\n        context.slots.default()\n      ]\n    )\n  },\n  'my injection key'\n)\n\nconst ChildComponent = defineComponent({\n  setup (props, context) {\n    const { count } = inject(injectionKey)\n  }\n})\n\nexport {\n  injectionKey,\n  ParentComponent,\n  ChildComponent\n}\n```\n\n\n## Motivation\n\nVue 3's `provide` and `inject` functions are fantastically useful for components that need to share data behind the scenes, without foisting delicate, repetitive `props` and `emit` configuration on other developers.\n\nBut type safety is a little tricky when you're working with `provide` and `inject`.\n\nLet's take a look:\n\n```ts\n// MyComponentGroup.ts\nimport { ref, provide, inject, defineComponent } from 'vue'\n\nconst injectionKey = Symbol('my injection key')\n\nexport const ParentComponent = defineComponent({\n  setup () {\n    // TypeScript can infer that `count` is a reactive reference\n    // to a number. If we try to assign a string to count.value,\n    // we'll get a compiler error.\n    const count = ref(0)\n\n    // We can provide `count` down to the component tree in case\n    // other components need it.\n    provide(injectionKey, { count })\n  }\n})\n\nexport const ChildComponent = defineComponent({\n  setup () {\n    // By default, `count` will have a type of `unknown`. TypeScript\n    // has no understanding of how `provide` and `inject` work at\n    // runtime, so it's not possible for the compiler to know that\n    // this `inject` call is dealing with the same data we passed\n    // to `provide`.\n    const { count } = inject(injectionKey)\n  }\n})\n```\n\nVue 3's type system has some nice features designed to get around this problem. Specifically, both `provide` and `inject` accept a generic type that describes the provided data.\n\nIf we're willing to manually define the type of our provided data, we can pass that type into `provide` and `inject` to achieve type safety:\n\n\n```ts\n// MyComponentGroup.ts\nimport { ref, provide, inject, defineComponent } from 'vue'\nimport type { Ref } from 'vue'\n\n// We can manually define a type that describes our provided data.\ntype Provided = {\n  count: Ref<number>,\n}\n\nconst injectionKey = Symbol('my injection key')\n\nexport const ParentComponent = defineComponent({\n  setup () {\n    const count = ref(0)\n\n    // We can pass our manual type to `provide` as a generic, and\n    // TS will type-check the provided data.\n    provide<Provided>(injectionKey, { count })\n  }\n})\n\nexport const ChildComponent = defineComponent({\n  setup () {\n    // We can also pass the manual type to `inject` as a generic.\n    // Once we do that, `count` will no longer be `unknown`—it will\n    // be correctly identified as `Ref<number>`.\n    const { count } = inject<Provided>(injectionKey)\n  }\n})\n```\n\nThat works, but there's another solution that's slightly smoother: we can define our injection key using Vue's `InjectionKey` type. `InjectionKey`, just like `provide` and `inject`, accepts a generic type that describes the provided data.\n\nInstead of passing our manual type into `provide` and `inject` separately, we can just pass it into the `InjectionKey` type. `provide` and `inject` will use type inference on our injection key to figure out what type of data they're dealing with.\n\n```ts\n// MyComponentGroup.ts\nimport { ref, provide, inject, defineComponent } from 'vue'\nimport type { InjectionKey, Ref } from 'vue'\n\n// We can manually define a type that describes our provided data.\ntype Provided = {\n  count: Ref<number>,\n}\n\n// Then, we can assert that our injection key is an `InjectionKey`,\n// specifically created for the `Provided` data type.\nconst injectionKey: InjectionKey<Provided> = Symbol('my injection key')\n\nexport const ParentComponent = defineComponent({\n  setup () {\n    const count = ref(0)\n\n    // `provide` will infer from `injectionKey` that its data\n    // should match the `Provided` type. No need to pass the generic!\n    provide(injectionKey, { count })\n  }\n})\n\nexport const ChildComponent = defineComponent({\n  setup () {\n    // Likewise, `inject` will infer the same info from `injectionKey`.\n    // Even though we haven't explicitly passed the generic to `inject`,\n    // TS will automatically know that `count` is `Ref<number>`.\n    const { count } = inject(injectionKey)\n  }\n})\n```\n\nThis is pretty cool stuff, and it's a testament to the Vue team's deep and thoughtful effort to encourage type safety in our Vue 3 codebases.\n\nI only have two gripes with this manual type + `InjectionKey` solution:\n1. I'm not a huge fan of importing and wiring up the `InjectionKey` and `Ref` utility types any time I need to define a group of components that use `provide` and `inject`. It just feels like yet another piece of boilerplate code to be concerned with.\n2. I really don't enjoy writing and maintaining manually defined types in these situations. Every time I want to provide an additional piece of data, or change the type of an existing piece, I have to go back to that manual type and update it, just to keep the TS compiler happy, even if my code is already passing tests and working properly at runtime. I can imagine this is even more difficult to manage for larger teams working on more complex components, possibly in multiple git branches.\n\n`createProvider` is an effort to eliminate that boilerplate code, and all of the ongoing maintenance and TS compiler frustration that goes along with it.\n","readmeFilename":"README.md"}