{"id":14957198,"url":"https://github.com/3rd-eden/treason","last_synced_at":"2026-01-21T01:02:00.056Z","repository":{"id":66002010,"uuid":"162125376","full_name":"3rd-Eden/treason","owner":"3rd-Eden","description":"Cheating on React's JSX by using JSON","archived":false,"fork":false,"pushed_at":"2018-12-21T13:00:33.000Z","size":20,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-18T15:06:01.296Z","etag":null,"topics":["json","react","react-js","react-native"],"latest_commit_sha":null,"homepage":null,"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/3rd-Eden.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-12-17T12:12:22.000Z","updated_at":"2022-02-06T15:07:35.000Z","dependencies_parsed_at":"2023-06-07T19:00:45.778Z","dependency_job_id":null,"html_url":"https://github.com/3rd-Eden/treason","commit_stats":{"total_commits":4,"total_committers":1,"mean_commits":4.0,"dds":0.0,"last_synced_commit":"1320dd0e8c93bc6e864925964d60a9fdcfa63b3c"},"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/3rd-Eden%2Ftreason","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/3rd-Eden%2Ftreason/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/3rd-Eden%2Ftreason/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/3rd-Eden%2Ftreason/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/3rd-Eden","download_url":"https://codeload.github.com/3rd-Eden/treason/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247648913,"owners_count":20972944,"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":["json","react","react-js","react-native"],"created_at":"2024-09-24T13:14:19.495Z","updated_at":"2026-01-21T01:01:59.999Z","avatar_url":"https://github.com/3rd-Eden.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Treason\n\nTreason, that's how it feels when you cheat on React by using JSON instead of\nJSX. This allows you to easily an API, or external source to generate your\nviews. Possibilities are endless.\n\nSo how does this work, we iterate over your supplied JSON payload, look for the\n`layout` key and iterate the array, creating new elements using\n`React.createElement`. To support deeply nested structures we automatically\ngenerate the required `key` properties, or you supply them your self in as\nprops for the components. To fully unlock all potential of this module we allow\nyou modify every step of the process using various of helper methods. Want to\nuse a stylesheet object instead of style props? Easy mode. Wrapping the app\nwith Redux store? No problem. Custom components, React-Native? Yep, all\nsupported.\n\nTake a look at our [examples](/examples) folder for some usage patterns.\n\n## Table of Contents\n\n- [Installation](#installation)\n- [Usage](#usage)\n- [API](#api)\n  - [register](#register)\n  - [use](#use)\n  - [before](#before)\n  - [after](#after)\n  - [modify](#modify)\n  - [render](#render)\n  - [clear](#clear)\n- [JSON](#json)\n\n## Installation\n\nThe package is released in the public npm registry and can be installed by\nrunning:\n\n```\nnpm install --save treason\n```\n\n## Usage\n\nThe `treason` module exposes the `Treason` class which you need to initialize.\n\n```js\nimport Treason from 'treason';\n\nconst layout = new Treason();\n```\n\nThe `treason` instance allows a single optional argument:\n\n- `layout` The property that of the supplied JSON that stores the layout\n  structure according to our [JSON](#JSON) specification. Defaults to `layout`.\n\nNow that the Treason instance is created you can register the components that\nshould be allowed to render.\n\n```js\nlayout.register('MyComponent', MyComponent);\n```\n\nNow that we have components register a payload can be decoded so we can render\nan output.\n\n```js\nconst view = layout.render(require('./you/json/payload.json'));\nconsole.log(view);\n```\n\nAnd that it, your JSON payload is now rendered and stored in the `view`\nvariable. So for a more complete picture, here's it all together:\n\n```js\nimport { Text, View, ScrollView } from 'react-native';\nimport { Svg, Path, Circle } from 'react-native-svg';\nimport { Foo, Bar, Loading } from './components';\nimport React, { Component } from 'react';\nimport Treason from 'treason';\n\nconst treason = new Treason();\n\ntreason\n.register('Text', Text)\n.register('View', View)\n.register('Path', Path)\n.register('Foo', foo);\n```\n\nNow that the we've registered the elements we want to allow in our view, we can\npass it some JSON to render.\n\n```js\nconst view = treason.render(require('./path/to/file.json'));\n\n\u003cContainer\u003e\n  { view }\n\u003c/Container\u003e\n```\n\nBut we can take it a step further, so why not load the JSON file from a remote\nservice:\n\n```js\nclass Main extends Component {\n  constructor() {\n    super(...arguments);\n\n    this.state = {\n      view: null\n    };\n  }\n\n  componentDidMount() {\n    fetch('http://api.endpoint.here').then((data) =\u003e {\n      this.setState({ view: layout.parse(data) });\n    });\n  }\n\n  render() {\n    if (!this.state.view) return \u003cLoading /\u003e\n\n    return this.state.view;\n  }\n}\n```\n\nNow you have on the fly updating of your app's layout.\n\n## API\n\n- [register](#register)\n- [use](#use)\n- [before](#before)\n- [after](#after)\n- [modify](#modify)\n- [render](#render)\n- [clear](#clear)\n\n### register\n\nRegisters a new custom component that can be rendered by the JSON structure. The\nmethod accepts 2 arguments:\n\n- `name` Name of the component in which it's addressed in the JSON structure.\n- `Component` The component that should be inserted.\n\n```js\nclass Example extends React.Component {\n  render() {\n    return \u003cp\u003eThis is an example\u003c/p\u003e\n  }\n}\n\ntreason.register('Example', Example);\n```\n\nThe method returns it self so it can be used for chaining purposes.\n\n### use\n\nThe supplied function will receive the plugin API that will give authors\naccess to the following methods:\n\n- [`register`](#register)\n- [`modify`](#modify)\n- [`before`](#before)\n- [`after`](#after)\n- [`use`](#use)\n\n```js\ntreason.use(require('./your-custom-plugin-here'));\n\ntreason.use(function (api) {\n  // api.register();\n  // api.modify();\n  // api.before();\n  // api.after();\n});\n```\n\nThe `treason` library ships with the following plugins by default:\n\n- `treason/svgs` The `svgs` library allows you to create Svgs that work in React\n  and React-Native. This plugin will register all components so they can be used\n  by the layout.\n- `treason/react-native` Registers all available components that are exposed by\n  the `react-native` library.\n\n```js\ntreason.use(require('treason/svgs'));\n```\n\n### modify\n\nThe `modify` functions allows you to transform specific properties. It requires\nthe following properties:\n\n- `prop` The name of the property that needs to be intercepted.\n- `callback` Function that is invoked when the given property is encountered.\n\nThe `callback` receives the following arguments:\n\n- `value` The value of the property.\n- `config` An object with some additional information on where it's encountered\n  - `Component`: The component that is being rendered with the property.\n  - `props`: Reference to all props on the component.\n  - `children`: Children of the `Component`.\n  - `data`: Only set when you use the special `@` syntax.\n\nThe callback should `return` the new value for the property.\n\nFor example to prevent the `dangerouslySetInnerHTML` from being used as\nproperty on any of the components, you could forcefully remove it by modifying\nthe value from the HTML payload to `undefined`:\n\n```js\ntreason.modify('dangerouslySetInnerHTML', function modify(value, config) {\n  return undefined;\n});\n```\n\nThe `modify` function allows you to reference the additional data structures\nthat you've send together with your `layout` payload. You can reference this\ndata by prefixing the name of the properties with an `@`.\n\nBecause these properties are references data structures that were received in\nthe JSON structure they will be removed automatically after they have been\nencountered in the `props` of the component.\n\n```js\ntreason.modify('@style', function modify(value, config) {\n  // config.props\n  // config.Component\n  // config.children\n  // config.data\n\n  config.props.style = config.style[value];\n});\n\ntreason.render({\n  style: { /* this will be set as config.data in the function above */ },\n  layout: ['div' { '@style': 'hello' }, [\n    ['div', { '@style': 'hello' }]\n  ]]\n});\n```\n\nIn the example above, the `@style` modifier will be called for the `div`, and\nreceive `hello` as value. The `config.data` is propagated with the contents of\nthe `style` property of the JSON payload that you supplied to `treason.render`.\n\nSo in this example we're re-using a single `style` object instead of having to\nsupply the same style information multiple times in the JSON structure. Allowing\nyou to create a smaller and more efficient payload.\n\nBut we can do better, you can combine this with the [`before`](#before) method\nthat will pre-process the referenced `style` object. So if you where to do this\non React-Native, you could transform the `style` into a `StyleSheet` instance:\n\n```js\nimport { StyleSheet } from 'react-native';\n\ntreason.before('style', function (payload) {\n  return StyleSheet.create(payload);\n});\n```\n\n### before\n\nPre-process the data in the JSON.\n\n- `name` The name of the property in the initial JSON payload.\n- `callback` Function that is invoked when the given property is encountered.\n\nThe `callback` receives the following arguments:\n\n- `data` Data that the `name` referenced in the JSON payload.\n- `layout` The layout structure **before** they are transformed into React\n  elements.\n\nThe callback should `return` the payload that you received as first argument.\nAnything that you return will be now used as value.\n\n```js\nimport { createStore } from 'redux';\nimport reducer from './reducer';\n\ntreason.before('store', function before(data, layout) {\n  const store = createStore(reducer, data);\n\n  return store;\n});\n```\n\n**PRO TIP:** As the layout is stored with a `layout` key, you also modify that\nwhen it's received:\n\n```js\ntreason.before('layout', function before(data, layout) {\n  //\n  // data == layout, as we're pre-processing the `layout` key from the\n  // supplied JSON payload. But we can now modify the whole JSON structure,\n  // add, remove elements as you wish. So in this case, we're going to\n  // wrap the received elements in a `\u003cdiv class=\"container\"\u003e` element.\n  //\n  return ['div', { className: 'container' }, layout];\n});\n```\n\n### after\n\nAllows you to apply any additional post processing after the layout has been\ntransformed into React elements. The method requires 2 arguments:\n\n- `name` The name of the property in the initial JSON payload.\n- `callback` Function that is invoked when the given property is encountered.\n\nThe `callback` receives the following arguments:\n\n- `elements` The rendered React Elements tree.\n- `data` Data that the `name` referenced in the JSON payload.\n\nThe callback should `return` the React Elements tree, or a new modified tree:\n\n```js\ntreason.after('messages', function after(elements, messages) {\n  return (\n    \u003cIntlProvider locale={ navigator.language } messages={ messages }\u003e\n      { elements }\n    \u003c/IntlProvider\u003e\n  );\n});\n\ntreason.render({ messages: {}, layout: ['Foo'] });\n```\n\nIn the example above we have our React Intl messages stored as `messages` key\nin the initial payload, so after the elements are transformed we wrap the\ngenerated React Tree and pass it the initial messages payload.\n\n**PRO TIP:*** Just like the `before` method, you can also process the `layout`\nkey and apply additional transformation to the elements tree.\n\n### render\n\nThis method transforms your given JSON string, or object structure into the\nactual React elements. It accepts a single argument, the JSON string, or object\nthat needs to be transformed into React Elements. It should have a `layout` key\nthat follows our [JSON](#JSON) structure specification. Additional keys may be\nsupplied as their content can be referenced using the `before`, `after` and\n`modify` functions.\n\nIt's a **synchronous** process so the method will return created elements:\n\n```js\nconst elements = layout.render({\n  layout: ['ComponentName', { props: 'here' }]\n});\n```\n\nAs it's returning React Element's it can be used inside components as well.\n\n```js\nclass Ads extends React.Component {\n  constructor() {\n    super(...arguments);\n\n    this.state = {\n      view: null\n    };\n  }\n\n  componentDidMount() {\n    fetch('https://my.ads.server/treason').then(function parse(response) {\n      return response.json();\n    })\n    .then(function update(myJson) {\n      this.setState({ view: treason.render(myJson) });\n    });\n  }\n\n  render() {\n    if (!this.state.view) return \u003cdiv className=\"placeholder\" /\u003e\n\n    return (\n      \u003cdiv className=\"ads\"\u003e\n        { this.state.view }\n      \u003c/div\u003e\n    )\n  }\n}\n```\n\n**Word of caution** Your JSON, your problem, we do not sanitize your data. Any\nof the registered components as well as normal elements can be rendered through\nthe payload so **do not allow user input as JSON/layout values**.\n\n### clear\n\nClear the instance of all of it's registered components and before, after,\nuse, modify functions.\n\n```js\ntreason.clear();\n```\n\n## JSON\n\nThe JSON structure is simple and straightforward. The root element of the JSON\nstructure should be an object, with a key `layout`. The layout should contain\nthe layout that destructured as `Array`\n\n```js\n{\n  layout: []\n}\n```\n\n```js\n[\"ComponentName\", { props: \"here\" }, [\n  // children of the ComponentName\n  [\"ChildComponent\", { props: \"foo\"} ],\n  [\"AnotherChild\", { props: \"bar\"}]\n]]\n```\n\n1. First item is the name of the component that needs to be rendered. This name\n   should correspond to the name that you specified in the [register](#register)\n   method. If the first item is not a string, we will assume that it's `Fragment`\n   that needs to be rendered.\n2. Next item is an object with the props that should be applied to the specified\n   component. This object can omitted if there are no props to apply.\n3. Last item is an array of children that needs to be rendered within the\n   component. This array should have children that are specified in exactly the\n   same way as the parent component.\n\nIf you are familiar with [AssetSystem], it uses the same structure. Below are\nsome examples where the different use's and their outputs are rendered.\n\n**INPUT:**\n```js\n[{ foo: bar}, [\"Example\"]]\n```\n\n**OUTPUT:**\n```js\n\u003cFragment foo=\"bar\"\u003e\n  \u003cExample /\u003e\n\u003c/Fragment\u003e\n```\n\n**INPUT:**\n```js\n['Svg', [\n  [\"Rect\", { height: 100, width: 100, fillColor: \"red\" }],\n  [\"Path\", { d: \"17n098d\" }],\n  [\"G\", [\n    [\"Circle\", { x: 0, y: 0 }],\n    [\"Circle\", { x: 0, y: 10 }]\n  ]]\n]]\n```\n\n**OUTPUT:**\n```js\n\u003cSvg\u003e\n  \u003cRect height={ 100 } width={ 100 } fillColor=\"red\" /\u003e\n  \u003cPath d=\"17n098d\" /\u003e\n  \u003cG\u003e\n    \u003cCircle x={ 0 } y={ 0 } /\u003e\n    \u003cCircle x={ 0 } y={ 10 } /\u003e\n  \u003c/G\u003e\n\u003c/Svg\u003e\n```\n\nYou can go as deep as you want with the structures, supply as many props as\nwant.\n\n## License\n\n[MIT](LICENSE)\n\n[AssetSystem]: https://github.com/godaddy/asset-system/blob/master/SPECIFICATION.md\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F3rd-eden%2Ftreason","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F3rd-eden%2Ftreason","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F3rd-eden%2Ftreason/lists"}