{"id":16019473,"url":"https://github.com/krasimir/actml","last_synced_at":"2025-07-09T12:35:15.094Z","repository":{"id":57172927,"uuid":"146005249","full_name":"krasimir/actml","owner":"krasimir","description":"ActML is a library that allows you to use React's JSX syntax for writing business logic.","archived":false,"fork":false,"pushed_at":"2020-09-05T04:11:12.000Z","size":2350,"stargazers_count":3,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-06-24T21:19:59.598Z","etag":null,"topics":["declarative-language","jsx","logic"],"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/krasimir.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG","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":"2018-08-24T14:57:52.000Z","updated_at":"2021-11-29T09:24:11.000Z","dependencies_parsed_at":"2022-08-24T13:21:31.352Z","dependency_job_id":null,"html_url":"https://github.com/krasimir/actml","commit_stats":null,"previous_names":["krasimir/dactory","krasimir/dialectica"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/krasimir/actml","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/krasimir%2Factml","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/krasimir%2Factml/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/krasimir%2Factml/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/krasimir%2Factml/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/krasimir","download_url":"https://codeload.github.com/krasimir/actml/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/krasimir%2Factml/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":262209661,"owners_count":23275471,"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":["declarative-language","jsx","logic"],"created_at":"2024-10-08T17:04:30.923Z","updated_at":"2025-06-27T06:39:47.981Z","avatar_url":"https://github.com/krasimir.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"![ActML](assets/logo.jpg)\n\n\u003e :dizzy: ActML is a library that allows you to use JSX syntax outside of React world. It aims to provide the same experience in terms of composability and patterns.\n\n```js\n/** @jsx A */\nimport { A, run } from 'actml';\n\nconst Message = ({ user, children }) =\u003e {\n  console.log(children(user));\n}\nconst Greeting = (user) =\u003e {\n  return `Hello ${ user.name }!`;\n}\n\nrun(\n  \u003cMessage user={ { name: 'Emma' } }\u003e\n    \u003cGreeting /\u003e\n  \u003c/Message\u003e\n);\n// Hello Emma!\n```\n\n(Try it yourself [here](https://poet.codes/e/XD26EjK9ECK).)\n\n---\n\n* [How to use it](#how-to-use-it)\n* [Introduction](#introduction)\n  * [Children](#children)\n  * [Asynchronous](#asynchronous)\n  * [Context](#context)\n* [Hooks](#hooks)\n  * [useState](#usestate)\n  * [useEffect](#useeffect)\n  * [useReducer](#usereducer)\n  * [usePubSub](#usepubsub)\n  * [useContext](#usecontext)\n* [Examples](#examples)\n\n---\n\n## How to use it\n\n* `npm i actml` or `yarn install actml`\n* ActML uses JSX so you need to have some sort of [Babel](https://babeljs.io) integration (or any other transpiler that understands [JSX](https://facebook.github.io/jsx/))\n* ActML requires you to add `/** @jsx A */` at the top of the file. Otherwise the ActML elements will be transpiled to `React.createElement`\n\n## Introduction\n\nActML looks like [React](https://reactjs.org/) but it's not about rendering UI. It's about executing your JavaScript.\n\n```js\nconst Foo = () =\u003e 'bar';\nrun(\u003cFoo /\u003e); // bar\n```\n\nYou'll probably wonder why using ActML and instead of writing `Foo()` we use `\u003cFoo /\u003e`? The answer is same reason why we write `\u003cComponent /\u003e` instead of `React.createElement(Component, null)`. We are declarative instead of imperative. It's better to say what we want to happen instead of how it happens. Being declarative means having more options for composition. The declarative code is easier to read and extend.\n\nAlso when we write `\u003cFoo /\u003e` we are not executing our function `Foo`. We are just saying that this function _should be_ executed at this place. We create a descriptor of that call which is passed to ActML runtime. This abstraction creates a layer where we can do bunch of things. We can for example process async operations behind the scenes or we can build a map the application's logic. We can deffer things or enhance them with additional arguments.\n\n### Children\n\nEvery function run by ActML receives a `children` prop. Similarly to React that prop represents the children of the element. Here we have two use case - we can call `children` as a function or we can return it as a result.\n\n```js\nconst X = ({ children }) =\u003e {\n  children();\n  children();\n};\nconst Y = ({ children }) =\u003e {\n  return children;\n}\nconst Message = () =\u003e {\n  console.log('Hello') \n};\nrun(\u003cX\u003e\u003cMessage /\u003e\u003c/X\u003e); // prints Hello twice\nrun(\u003cY\u003e\u003cMessage /\u003e\u003c/Y\u003e); // prints Hello once\n```\n\nIf we are calling `children` we are getting back an array containing the results of the nested elements. Or if the array contains one item we get directly that item.\n\n```js\nconst X = () =\u003e 'foo';\nconst Y = () =\u003e 'bar';\nconst Results = ({ children }) =\u003e {\n  console.log(JSON.stringify(children()));\n};\nrun(\n  \u003cResults\u003e\n    \u003cX /\u003e\n    \u003cY /\u003e\n  \u003c/Results\u003e\n); // prints [\"foo\",\"bar\"]\n```\n\nCalling manually `children` means running the children `X` and `Y` and receiving the result of them.\n\n### Asynchronous\n\nActML runtime supports both asynchronous and synchronous elements. You can mix them in a single expression. As soon as there is something asynchronous ActML marks the call as such and the result of it is a promise. For example:\n\n```js\nconst App = async ({ children }) =\u003e {\n  const message = await children();\n  \n  return message.join(' ');\n}\nconst Greeting = () =\u003e 'Hey';\nconst GetUserFirstName = async () =\u003e {\n  const { data: { first_name }} = await (await fetch('https://reqres.in/api/users/2')).json();\n  return first_name;\n}\nconst FavoriteColor = () =\u003e 'your favorite color is';\nconst GetFavoriteColor = async () =\u003e {\n  const { data: { color }} = await (await fetch('https://reqres.in/api/products/3')).json();\n  return color;\n}\n\nrun(\n  \u003cApp\u003e\n    \u003cGreeting /\u003e\n    \u003cGetUserFirstName /\u003e\n    \u003cFavoriteColor /\u003e\n    \u003cGetFavoriteColor /\u003e\n  \u003c/App\u003e\n).then(message =\u003e console.log(message));\n\n// outputs: Hey Janet your favorite color is #BF1932\n```\n\n(online demo [here](https://poet.codes/e/ZLOngMd8liP))\n\nNotice that `\u003cGreeting\u003e` and `\u003cFavoriteColor\u003e` are synchronous. ActML waits for all the children to be processed and then resolves the promise returned by the `children` call. If all the elements were synchronous then we'll get an array straight away.\n\n### Context\n\nI can't say it better than [React docs](https://reactjs.org/docs/context.html#reactcreatecontext):\n\n\u003e Context provides a way to pass data through the component tree without having to pass props down manually at every level.\n\n```js\nimport { createContext } from 'actml';\n\nconst Context = createContext('\u003cdefault value\u003e');\nconst { Provider, Consumer } = Context;\n\nconst F = () =\u003e {\n  return (\n    \u003cConsumer\u003e\n      { value =\u003e console.log(value) }\n    \u003c/Consumer\u003e\n  );\n};\n\nrun(\n  \u003cProvider value='foo'\u003e\n    \u003cF /\u003e\n  \u003c/Provider\u003e\n); \n// outputs: 'foo'\n```\n\nActML searches for the `\u003cProvider\u003e` up the tree from the place where the `\u003cConsumer\u003e` is positioned. If it can't find the `\u003cProvider\u003e` then it shows a warning and delivers `\u003cdefault value\u003e` instead.\n\n## Hooks\n\n### useState\n\n```js\nimport { A, useState } from 'actml';\n\nconst E = () =\u003e {\n  const [ getState, setState ] = useState(initialState);\n}\n```\n\nReturns two functions for setting and retrieving a state value. In the original [React docs](https://reactjs.org/docs/hooks-reference.html#usestate) the first item is the state value directly but here ActML diverges a little bit by providing a function. It is done to provide a mechanism for immediate retrieval of the update value.\n\n### useEffect\n\n```js\nimport { A, useEffect } from 'actml';\n\nconst E = () =\u003e {\n  useEffect(function sideEffect() {\n    // ...\n    return function onElementRemoved() {\n      // ...\n    }\n  }, [ dependencyA, dependencyB ]);\n}\n```\n\nThe function `sideEffect` is fired after the function `E` finishes. After that it gets fired only if some of the `dependencyA` or `dependencyB` are changed. If we pass an empty array we are creating a side effect that is fired only once no matter how many times `E` is executed. The function that we pass to `useEffect` may return another function that is invoked when the element is removed from the tree.\n\n_ActML's `useEffect` mimics [React's `useEffect`](https://reactjs.org/docs/hooks-reference.html#useeffect)_\n\n### useReducer\n\n```js\nimport { A, useEffect } from 'actml';\n\nfunction reducer(state, action) {\n  switch (action.type) {\n    case 'increment':\n      return {count: state.count + 1};\n    case 'decrement':\n      return {count: state.count - 1};\n    default:\n      throw new Error();\n  }\n}\nconst E = function () {\n  const [ getState, dispatch ] = useReducer(reducer, initialState);\n  //...\n  dispatch({ type: 'increment' })\n}\n```\n\nVery similar to [`useState`](#usestate). In fact `useReducer` internally uses `useState. The mechanism for updating the state is by using actions which are `dispatch`ed and then processed by the `reducer` which returns the new version of the state.\n\n_ActML's `useReducer` mimics [React's `useReducer`](https://reactjs.org/docs/hooks-reference.html#usereducer)_\n\n### usePubSub\n\n```js\nimport { A, Fragment, usePubSub } from 'actml';\n\nconst TYPE = 'TYPE';\n\nconst Publisher = function () {\n  const { publish } = usePubSub();\n\n  publish(TYPE, 42);\n};\nconst Subscribe = function () {\n  const { subscribe } = usePubSub();\n\n  subscribe(TYPE, (answer) =\u003e {\n    console.log(`The answer is ${ answer }`);\n  });\n};\n\nrun(\n  \u003cFragment\u003e\n    \u003cSubscribe /\u003e\n    \u003cPublisher /\u003e\n  \u003c/Fragment\u003e\n);\n```\n\nThe `usePubSub` hook can help you if you need to communicate between elements on different levels in the tree. It returns an object with two methods `publish` and `subscribe`:\n\n* `publish(\u003ctype\u003e, \u003cpayload\u003e)`\n* `subscribe(\u003ctype\u003e, \u003ccallback\u003e)`\n\n### useContext\n\nConsume the value of a context.\n\n```js\nconst Context = createContext();\nconst F = () =\u003e {\n  const value = useContext(Context);\n  // here value is 'foo'\n};\n\nrun(\n  \u003cContext.Provider value='foo'\u003e\n    \u003cF /\u003e\n  \u003c/Context.Provider\u003e\n);\n```\n\n## Examples\n\n* [Playground](https://poet.codes/e/XD26EjK9ECK)\n* [ToDoMVC app](./examples/todomvc)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkrasimir%2Factml","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkrasimir%2Factml","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkrasimir%2Factml/lists"}