{"id":13527041,"url":"https://github.com/outlandishideas/kasia","last_synced_at":"2025-04-01T09:31:01.442Z","repository":{"id":57151693,"uuid":"64839697","full_name":"outlandishideas/kasia","owner":"outlandishideas","description":":tophat: A React Redux toolset for the WordPress API","archived":true,"fork":false,"pushed_at":"2018-10-31T20:40:49.000Z","size":644,"stargazers_count":218,"open_issues_count":10,"forks_count":15,"subscribers_count":18,"default_branch":"master","last_synced_at":"2025-03-28T20:08:29.632Z","etag":null,"topics":["javascript","kasia","redux","sagas","universal-applications","wordpress","wordpress-api","wp-api"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/outlandishideas.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-08-03T11:06:08.000Z","updated_at":"2024-03-07T18:39:44.000Z","dependencies_parsed_at":"2022-09-03T18:01:38.850Z","dependency_job_id":null,"html_url":"https://github.com/outlandishideas/kasia","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/outlandishideas%2Fkasia","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/outlandishideas%2Fkasia/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/outlandishideas%2Fkasia/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/outlandishideas%2Fkasia/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/outlandishideas","download_url":"https://codeload.github.com/outlandishideas/kasia/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246615931,"owners_count":20806030,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["javascript","kasia","redux","sagas","universal-applications","wordpress","wordpress-api","wp-api"],"created_at":"2024-08-01T06:01:39.756Z","updated_at":"2025-04-01T09:31:00.840Z","avatar_url":"https://github.com/outlandishideas.png","language":"JavaScript","funding_links":[],"categories":["JavaScript","Other Integrations","📦 Legacy \u0026 Inactive Projects"],"sub_categories":["Other"],"readme":"\u003cp align=\"center\"\u003e\n    \u003cimg src=\"https://raw.githubusercontent.com/outlandishideas/kasia/master/assets/tophat.png\" /\u003e\n\u003c/p\u003e\n\n\u003cp\u003e\u003ch1 align=\"center\"\u003ekasia\u003c/h1\u003e\u003c/p\u003e\n\n\u003cp align=\"center\"\u003eA React Redux toolset for the WordPress API\u003c/p\u003e\n\n\u003cp align=\"center\"\u003eMade with ❤ at \u003ca href=\"http://www.twitter.com/outlandish\"\u003e@outlandish\u003c/a\u003e\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"http://badge.fury.io/js/kasia\"\u003e\u003cimg alt=\"npm version\" src=\"https://badge.fury.io/js/kasia.svg\" /\u003e\u003c/a\u003e\n    \u003ca href=\"http://standardjs.com/\"\u003e\u003cimg src=\"https://img.shields.io/badge/code%20style-standard-brightgreen.svg\" /\u003e\u003c/a\u003e\n    \u003ca href=\"https://travis-ci.org/outlandishideas/kasia\"\u003e\u003cimg alt=\"travis ci build\" src=\"https://travis-ci.org/outlandishideas/kasia.svg\" /\u003e\u003c/a\u003e\n    \u003ca href='https://coveralls.io/github/outlandishideas/kasia?branch=master'\u003e\u003cimg src='https://coveralls.io/repos/github/outlandishideas/kasia/badge.svg?branch=master' alt='Coverage Status' /\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://gitter.im/kasia-lib/Lobby\"\u003e\u003cimg src=\"https://badges.gitter.im/gitterHQ/gitter.png\" /\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003chr/\u003e\n\n:sparkles: We welcome contributors!\n\n:vertical_traffic_light: Issues are triaged using a traffic light system:\n\n\u0026nbsp;\u0026nbsp;\u0026nbsp;![#00ff00](http://placehold.it/15/00ff00/000000?text=+) \u003cstrong\u003e[small](https://github.com/outlandishideas/kasia/issues?q=is%3Aopen+is%3Aissue+label%3Asmall)\u003c/strong\u003e - quick tasks, great for beginner contributors\u003cbr/\u003e\n\u0026nbsp;\u0026nbsp;\u0026nbsp;![#ffff00](http://placehold.it/15/ffff00/000000?text=+) \u003cstrong\u003e\u003cspan style=\"color: yellow;\"\u003e[medium](https://github.com/outlandishideas/kasia/issues?q=is%3Aopen+is%3Aissue+label%3Amedium)\u003c/span\u003e\u003c/strong\u003e - tasks with increased complexity, may take some time to implement\u003cbr/\u003e\n\u0026nbsp;\u0026nbsp;\u0026nbsp;![#ff0000](http://placehold.it/15/ff0000/000000?text=+) \u003cstrong\u003e[large](https://github.com/outlandishideas/kasia/issues?q=is%3Aopen+is%3Aissue+label%3Alarge)\u003c/strong\u003e - big tasks that touch many parts of the library, will require commitment\n\n[Get started contributing here.](https://github.com/outlandishideas/kasia/issues)\n\n\u003chr/\u003e\n\nGet data from WordPress and into components with ease...\n\n```js\n// e.g. Get a post by its slug\n@connectWpPost('post', 'spongebob-squarepants')\nexport default class extends React.Component () {\n  render () {\n    const { post: spongebob } = this.props.kasia\n    \n    if (!spongebob) {\n      return \u003cp\u003e{'Who lives in a pineapple under the sea?'}\u003c/p\u003e\n    }\n    \n    return \u003ch1\u003e{spongebob.title.rendered}!\u003c/h1\u003e\n    //=\u003e Spongebob Squarepants!\n  }\n}\n```\n\n\n## Features\n\n- Declaratively connect React components to data from WordPress.\n- Uses [`node-wpapi`](https://github.com/WP-API/node-wpapi) in order to facilitate complex queries.\n- Register and consume Custom Content Types with ease.\n- All WP data is normalised at `store.wordpress`, e.g. `store.wordpress.pages`.\n- Support for universal applications.\n- Support for plugins, e.g. [`wp-api-menus`](https://github.com/outlandishideas/kasia/tree/master/packages/kasia-plugin-wp-api-menus).\n\n## Glossary\n\n- [Requirements](#requirements)\n- [Install](#install)\n- [Import](#import)\n- [__Configure__](#configure)\n- [__Usage__](#usage)\n- [Exports](#exports)\n- [Plugins](#plugins)\n- [Universal Applications](#universal-applications)\n- [Contributing](#contributing)\n- [Author \u0026 License](#author-\u0026-license)\n\n## Requirements\n\nKasia suits applications that are built using these technologies:\n\n- React\n- Redux\n- Redux Sagas (\u003e= 0.10.0)\n- WordPress\n- [WP-API plugin](http://v2.wp-api.org/)\n- [`node-wpapi`](https://github.com/WP-API/node-wpapi)\n\n## Install\n\n```sh\nnpm install kasia --save\n```\n\n```sh\nyarn add kasia\n```\n\n## Import\n\n```js\n// ES2015\nimport kasia from 'kasia'\n```\n\n```js\n// CommonJS\nvar kasia = require('kasia')\n```\n\n## Configure\n\nConfigure Kasia in three steps:\n\n1. Initialise Kasia with an instance of `node-wpapi`.\n\n2. Spread the Kasia reducer when creating the redux root reducer.\n\n3. Run the Kasia sagas after creating the redux-saga middleware.\n\nA slimline example...\n\n```js\nimport { combineReducers, createStore, applyMiddleware } from 'redux'\nimport createSagaMiddleware from 'redux-saga'\nimport kasia from 'kasia'\nimport wpapi from 'wpapi'\n\nconst wpai = new wpapi({ endpoint: 'http://wordpress/wp-json' })\n\nconst { kasiaReducer, kasiaSagas } = kasia({ wpapi })\n\nconst rootSaga = function * () {\n  yield [...kasiaSagas]\n}\n\nconst rootReducer = combineReducers({\n  ...kasiaReducer\n})\n\nconst sagaMiddleware = createSagaMiddleware()\n\nexport default function configureStore (initialState) {\n  const store = createStore(\n    rootReducer,\n    initialState,\n    applyMiddleware(sagaMiddleware)\n  )\n  \n  sagaMiddleware.run(rootSaga)\n\n  return store\n}\n```\n\n## Usage\n\n### `kasia(options) : Object`\n\nConfigure Kasia.\n\n- __options__ {Object} Options object\n\nReturns an object containing the Kasia reducer and sagas.\n\n```js\nconst { kasiaReducer, kasiaSagas } = kasia({\n  wpapi: new wpapi({ endpoint: 'http://wordpress/wp-json' })\n})\n```\n\nThe `options` object accepts:\n\n- `wpapi` {wpapi}\n\n    An instance of `node-wpapi`.\n    \n- `keyEntitiesBy` {String} _(optional, default=`'id'`)_\n\n    Property of entities that is used to key them in the store.\n     \n    One of: `'slug'`, `'id'`.\n    \n- `debug` {Boolean} _(optional, default=`false`)_\n\n  Log debug information to the console.\n\n- `contentTypes` {Array} _(optional)_\n\n    Array of custom content type definitions.\n\n    ```js\n    // Example custom content type definition\n    contentTypes: [{\n      name: 'book',\n      plural: 'books',\n      slug: 'books',\n      route, // optional, default=\"/{plural}/(?P\u003cid\u003e)\"\n      namespace, // optional, default=\"wp/v2\"\n      methodName // optional, default={plural}\n    }]\n    ```\n\n- `plugins` {Array} _(optional)_\n\n    Array of Kasia plugins.\n\n    ```js\n    import kasiaWpApiMenusPlugin from 'kasia-plugin-wp-api-menus'\n\n    // Example passing in plugin\n    plugins: [\n        [kasiaWpApiMenusPlugin, { route: 'menus' }], // with configuration\n        kasiaWpApiMenusPlugin, // without configuration\n    ]\n    ```\n    \n### Decorators\n\nThings to keep in mind:\n\n- A component will make a request for data 1) when it mounts and 2) if its props change. For `connectWpPost` a change\nin props will trigger Kasia to try and find entity data for the new identifier in the store. If it is found, no request\nis made.\n- Content data should be parsed before being rendered as it may contain encoded HTML entities.\n- In arbitrary queries with `connectWpQuery`, we suggest that you always call the `embed` method on the\nquery chain, otherwise embedded content data will be omitted from the response.\n- Paging data for the request made on behalf of the component is available at `this.props.kasia.query.paging`.\n- The examples given assume the use of [decorators (sometimes called annotations)](https://github.com/loganfsmyth/babel-plugin-transform-decorators-legacy).\nHowever decorator support is not necessary. \nSee the end of each example for the alternative Higher Order Component approach.\n\n#### `@connectWpPost(contentType, identifier) : Component`\n\nConnect a component to a single entity in WordPress, e.g. Post, Page, or custom content type. \n\n- __contentType__ {String} The content type to fetch\n- __identifier__ {String|Number|Function} ID of the entity to fetch or function that derives it from `props`\n\nReturns a connected component.\n\nExample, using identifier derived from route parameter on `props`:\n\n```js\nimport React, { Component } from 'react'\nimport { Route } from 'react-router'\nimport { connectWpPost } from 'kasia/connect'\nimport { Page } from 'kasia/types'\n\n@connectWpPost(Page, (props) =\u003e props.params.slug)\nexport default class Page extends Component {\n  render () {\n    const { query, page } = this.props.kasia\n\n    if (!query.complete) {\n      return \u003cspan\u003eLoading...\u003c/span\u003e\n    }\n\n    return \u003ch1\u003e{page.title}\u003c/h1\u003e\n  }\n}\n\n// Without decorator support\nexport default connectWpPost(Page, (props) =\u003e props.params.slug)(Post)\n```\n\n#### `@connectWpQuery(queryFn, shouldUpdate) : Component`\n\nConnect a component to the result of an arbitrary WP-API query. Query is always made with `?embed` query parameter.\n\n- __queryFn__ {Function} Function that accepts args `wpapi`, `props`, `state` and should return a WP-API query\n- __shouldUpdate__ {Function} Called on `componentWillReceiveProps` with args `thisProps`, `nextProps`, `state`\n\nReturns a connected component.\n\nThe component will request new data via `queryFn` if `shouldUpdate` returns true.\n\nEntities returned from the query will be placed on `this.props.kasia.entities` under the same\nnormalised structure as described in [The Shape of Things](#the-shape-of-things).\n\nExample, fetching the most recent \"News\" entities:\n\n```js\nimport React, { Component } from 'react'\nimport { Route } from 'react-router'\nimport { connectWpPost } from 'kasia/connect'\n\n// Note the invocation of `embed` in the query chain\n@connectWpQuery((wpapi, props) =\u003e {\n  return wpapi.news().month(props.month).embed().get()\n}, (thisProps, nextProps) =\u003e thisProps.month != nextProps.month)\nexport default class RecentNews extends Component {\n  render () {\n    const {\n      query,\n      data: { news }\n    } = this.props.kasia\n\n    if (!query.complete) {\n      return \u003cspan\u003eLoading...\u003c/span\u003e\n    }\n\n    return (\n      \u003cdiv\u003e\n        \u003ch1\u003eRecent News Headlines\u003c/h1\u003e\n        {Object.keys(news).map((key) =\u003e\n          \u003ch2\u003e{news[key].title}\u003c/h2\u003e)}\n      \u003c/div\u003e\n    )\n  }\n}\n\n// Without decorator support\nexport default connectWpQuery((wpapi) =\u003e {\n  return wpapi.news().embed().get()\n})(Post)\n```\n\n## Exports\n\n### `kasia`\n\nThe Kasia configurator and preload utilities.\n\n```js\nimport kasia, { preload, preloadQuery } from 'kasia'\n```\n\n### `kasia/connect`\n\nThe connect decorators.\n\n```js\nimport { connectWpPost, connectWpQuery } from 'kasia/connect'\n```\n\n### `kasia/types`\n\nThe built-in WordPress content types that can be passed to `connectWpPost` to define what content type\na request should be made for.\n\n```js\nimport {\n  Category, Comment, Media, Page,\n  Post, PostStatus, PostType,\n  PostRevision, Tag, Taxonomy, User\n} from 'kasia/types'\n```\n\nSee [Universal Application Utilities](#Utilities) for more details.\n\n## Plugins\n\nKasia exposes a simple API for third-party plugins.\n\nA plugin should:\n\n- be a function that accepts these arguments:\n    - __wpapi__ {wpapi} An instance of `wpapi`\n    - __pluginOptions__ {Object} The user's options for the plugin\n    - __kasiaOptions__ {Object} The user's options for Kasia\n\n- return an object containing `reducers` (Object) and `sagas` (Array).\n\n- use the `'kasia/'` action type prefix.\n\n```js\n// Example definition returned by a plugin\n{\n  reducer: {\n    'kasia/SET_DATA': function setDataReducer () {}\n    'kasia/REMOVE_DATA': function removeDataReducer () {}\n  },\n  sagas: [function * fetchDataSaga () {}]\n}\n```\n\nA plugin can hook into Kasia's native action types, available at `kasia/lib/constants/ActionTypes`.\nAll reducers for an action type are merged into a single function that calls each reducer in succession\nwith the state returned by the previous reducer. This means the order of plugins that touch the same\naction type is important.\n\n### Available plugins:\n\n- [`kasia-plugin-wp-api-menus`](https://github.com/outlandishideas/kasia/tree/master/packages/kasia-plugin-wp-api-menus) \n- [`kasia-plugin-wp-api-all-terms`](https://github.com/outlandishideas/kasia/tree/master/packages/kasia-plugin-wp-api-all-terms)\n- [`kasia-plugin-wp-api-response-modify`](https://github.com/outlandishideas/kasia/tree/master/packages/kasia-plugin-wp-api-response-modify)\n\nPlease create a pull request to get your own added to the list.\n\n## Universal Applications\n\n__Important...__ \n\n  - __before calling the preloaders for SSR you must call `kasia.rewind()`__\n  - __or if you call `runSagas()` from the utilities then this is done for you.__\n\n### Utilities\n\n#### `runSagas(store, sagas) : Promise`\n\nRun a bunch of sagas against the store and wait on their completion.\n\n- __store__ {Object} Redux store enhanced with `runSaga` method\n- __sagas__ {Array} Array of functions that accept the store state and return a saga generator\n\nReturns a Promise resolving on completion of all the sagas.\n\n#### `preload(components[, renderProps][, state]) : Generator`\n\nCreate a saga operation that will preload all data for any Kasia components in `components`.\n\n- __components__ {Array} Array of components\n- [__renderProps__] {Object} _(optional)_ Render props object derived from the matched route\n- [__state__] {Object} _(optional)_ Store state\n\nReturns a [saga operation](#saga-operation-signature).\n\n#### `preloadQuery(queryFn[, renderProps][, state]) : Generator`\n\nCreate a saga operation that will preload data for an arbitrary query against the WP API.\n\n- __queryFn__ {Function} Query function that returns `node-wpapi` query\n- [__renderProps__] {Object} _(optional)_ Render props object\n- [__state__] {Object} _(optional)_ Store state\n\nReturns a [saga operation](#saga-operation-signature).\n\n#### `\u003cKasiaConnectedComponent\u003e.preload(renderProps[, state]) : Array\u003cArray\u003e`\n\nConnected components expose a static method `preload` that produces an array of saga operations\nto facilitate the request for entity data on the server.\n\n- __renderProps__ {Object} Render props object derived from the matched route \n- [__state__] {Object} _(optional)_ State object (default: `null`) \n\nReturns an array of [saga operations](#saga-operation-signature).\n\n#### Saga Operation Signature\n\nA saga operation is an array of the form:\n\n```js\n[ sagaGeneratorFn, action ]\n```\n\nWhere:\n\n- `sagaGenerator` Function that must be called with the `action`.\n\n- `action` action Object containing information for the saga to fetch data.\n\n### Example\n\nA somewhat contrived example using the available preloader methods.\n\n```js\nimport { match } from 'react-router'\nimport { runSagas, preload, preloadQuery } from 'kasia'\n\nimport routes from './routes'\nimport store from './store'\nimport renderToString from './render'\nimport getAllCategories from './queries/categories'\n\nexport default function renderPage (res, location) { \n  return match({ routes, location }, (error, redirect, renderProps) =\u003e {\n    if (error) return res.sendStatus(500)\n    if (redirect) return res.redirect(302, redirect.pathname + redirect.search)\n    \n    // We are using `runSagas` which rewinds for us, but if we weren't then\n    // we would call `kasia.rewind()` here instead:\n    //\n    // kasia.rewind()\n    \n    // Each preloader accepts the state that may/may not have been modified by\n    // the saga before it, so the order might be important depending on your use-case!\n    const preloaders = [\n      () =\u003e preload(renderProps.components, renderProps),\n      (state) =\u003e preloadQuery(getAllCategories, renderProps, state)\n    ]\n    \n    return runSagas(store, preloaders)\n      .then(() =\u003e renderToString(renderProps.components, renderProps, store.getState()))\n      .then((document) =\u003e res.send(document))\n  })  \n}\n```\n\n## Testing\n\nKasia components can be tested by:\n- lifting the query function from the connectWpQuery decorator and exporting it\n- lifting the should update function from the connectWpQuery deocrating and exporting it\n- making your component available as a named export prior to decoration\n\nAn example:\n```js\nexport function postQuery (wpapi) {...}\n\nexport function shouldUpdate (thisProps, nextProps, state) {...}\n\nexport class Post extends Component {...}\n\n@connectWpQuery(postQuery, shouldUpdate)\nexport default Post\n```\n\nYou can then test the component without decoration, the query and the shouldUpdate function\nin isolation.\n\n## Contributing\n\nAll pull requests and issues welcome! \n\n- When submitting an issue please provide adequate steps to reproduce the problem.\n- PRs must be made using the `standard` code style.\n- PRs must update the version of the library according to [semantic versioning](http://semver.org/).\n\nIf you're not sure how to contribute, check out Kent C. Dodds'\n[great video tutorials on egghead.io](https://egghead.io/lessons/javascript-identifying-how-to-contribute-to-an-open-source-project-on-github)!\n\n## Author \u0026 License\n\n`kasia` was created by [Outlandish](https://twitter.com/outlandish) and is released under the MIT license.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foutlandishideas%2Fkasia","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Foutlandishideas%2Fkasia","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foutlandishideas%2Fkasia/lists"}