{"id":21020935,"url":"https://github.com/awto/estransducers","last_synced_at":"2025-03-13T17:42:25.270Z","repository":{"id":57230626,"uuid":"61650709","full_name":"awto/estransducers","owner":"awto","description":"DEPRECATED: Transducer as alternative to visitors for ESTree AST traversal (moved into https://github.com/awto/effectfuljs/tree/master/packages/transducers).","archived":false,"fork":false,"pushed_at":"2017-12-07T21:18:42.000Z","size":212,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-01-20T13:19:14.613Z","etag":null,"topics":["ast","estree-ast-traversal","javascript","transducer","transducers","transpiler"],"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/awto.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}},"created_at":"2016-06-21T16:46:37.000Z","updated_at":"2017-12-14T21:24:15.000Z","dependencies_parsed_at":"2022-09-01T22:30:47.505Z","dependency_job_id":null,"html_url":"https://github.com/awto/estransducers","commit_stats":null,"previous_names":["awto/estree-transducers"],"tags_count":19,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/awto%2Festransducers","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/awto%2Festransducers/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/awto%2Festransducers/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/awto%2Festransducers/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/awto","download_url":"https://codeload.github.com/awto/estransducers/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243451032,"owners_count":20293055,"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":["ast","estree-ast-traversal","javascript","transducer","transducers","transpiler"],"created_at":"2024-11-19T10:44:02.464Z","updated_at":"2025-03-13T17:42:25.249Z","avatar_url":"https://github.com/awto.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n# estree-transducers\n\nTransducers as alternative to visitors for ESTree AST traversal.\n\nDEPRECATED: the repository is moved into [effectful.js](https://github.com/awto/effectfuljs) mono-repo, as\n[@effectful/transducers](https://github.com/awto/effectfuljs/tree/master/packages/transducers)\n\n## Transducers in JavaScript\n\nThe primary goal of transducers is to run a pipeline of transformations of an input\nstream of values without creating intermediate values. The functions describing\ncomputations are decoupled, so this makes program design cleaner and easier\nto develop and maintain.\n\nSuch decoupled design probably became popular in JavaScript after jquery\nlibrary. Similar tools have been implemented by other libraries and\nbecame popular too. Here is an example of separating two computations with\n[jquery map function](https://api.jquery.com/map/):\n\n```\nstore(produce().map(f).map(g))\n```\n\nIn the example `produce` may read some big file and store read values in a\njQuery object. While `store` stores computed values into another file.\nEach function may either remove or add several new elements to the collection.\nFirst it runs `f` function over the whole collection and store intermediate\nresult in memory, and after it runs `g` over it and again intermediate result\nis in memory, and only after the final result is stored into file. For big or\nprobably even infinite streams this is clearly not acceptable.\n\nThe task is often occurring and there are many solutions. For example\nnodejs streams and `pipe` operation adding stream transformer to some other\nstream. Or Clojure transducers (different to the ones used in the library)\nwith a few ports to JS, like\n[transducers-js](https://github.com/cognitect-labs/transducers-js)\nor in [Ramda](http://ramdajs.com/).\n\nClojure transducers transform consumers (called reducing function in clojure),\nwhile transducers for this library transform producers, and this is a fundamental\ndifference making these transducers easier to define and use in JS. Making\nand combining transducers became a plain JS programming task, no needs to study\nother libraries interfaces protocols. Many parts of JS may be utilized here, like\nES6 generators functions, or different control statements,\netc.\n\nHere is an example of transducer mapping stream values.\n\n```javascript\n\nfunction map(fun) {\n  return function*(input) {\n    for(const i of input) {\n      yield fun(i)\n    }\n  }\n}\n```\n\nIt is different to jquery map since it cannot remove or replace a single element\nwith a few but it is pretty obvious how to create similar trivial transducers\nfor these tasks. It is not required to make generic transducers at all. It may\nbe some specific ones implementing the concrete task. It just reads one or several\ninput iterators with `for-of` or with `next`, calculates something, stores current\nstate in local variables, and sends output values with `yield` expression. It is\nstill easy to compose it with some other transducers doing another part of the job.\nUnless some of the transducers intentionally buffer input values, nothing is stored\nin memory.\n\nThere is a paper about transducers transforming producers (like in this library):\n[Lazy v. Yield: Incremental, Linear Pretty-printing](http://okmij.org/ftp/continuations/PPYield/yield-pp.pdf)\nfrom 2012 (the first mention of clojure transducers (transforming consumers) is\nprobably this 2014 [blog post](http://blog.cognitect.com/blog/2014/8/6/transducers-are-coming).\nIn the paper transducers are used to ensure linear complexity for pretty-printing\nwith hierarchical document encoding similar to the one employed by this library.\n\nUsing terminology from the paper stream of values is generated by _producer_.\nFor example it is a generator function reading data from channel and returning\nchunks with `yield` expression. Or it may be just some `Array` passed to\ninput of transducers pipeline or anything iterable.\n\nResults of computation are passed to _consumer_ function. `Array.from` is one of as\nsuch function. It just writes all resulting values into in-memory array.\nThis may also be a function writing values into channel.\n\nWith help function's manipulation libraries like Ramda\ncreating and manipulating of such transducers is even simpler. For example\n[pipe](http://ramdajs.com/docs/#pipe) function composes transducers into a\nsingle one because transducers are just functions.\n\nHere is a jquery example may be written using Ramda `pipe`:\n\n```javascript\nconsume(R.comp(map(f),map(g))(produce()))\n```\n\nand next is absolutely the same: \n\n```javascript\nconsume(map(R.comp(f,g))(produce()))\n```\n\ni.e. no intermediate value, and execution of `f` and `g` is interleaved for each element\nof input iterator.\n\n## AST transducers\n\nUnlike streams AST is hierarchical but we still can turn it into sequence. For\ncomplex node it emits value for begin and end and children in between. This is\nvery similar to visitors where value hierarchy is flattened into corresponding\nhandlers call. So transducers and visitors are related like internals and\nexternals iterators.\n\nThe library doesn't export any transducers, it only exports producer and consumer as\nfunctions:\n\n * `produce` - takes AST node and returns stream of AST traversal events\n * `consume` - takes stream and build AST node from it\n\nIt also exports `Tag` object for default AST field and type names.\n\nCalling `consume` may be not necessary if AST is updated in place, however\nthis may be not a good idea.\n\nThe event stream is an object with following fields:\n * `enter` - boolean displaying traversal enters node\n * `leave` - boolean displaying traversal exits node\n * `value` - An object with original AST node value in `node` field, it is\n             shared between `enter` and `leave`.\n * `pos` - name of the field, it is not a string but\n           a special tag value, the default tags are\n           in `Tag` map exported by the library\n * `type` - node type tag, like for `pos`\n\nIf it is node without children `enter` and `leave` are both true.\n\nHere is an example of variable value substitution transducer.\n\n```javascript\nfunction* subst(dict, s) {\n  for(const i of s) {\n    switch (i.type) {\n    case Tag.Identifier:\n      const n = dict[i.value.node.name]\n      if (n) {\n        if (i.enter)\n          yield* produce(n,i.pos)\n        continue\n      }\n      break\n    case Scope:\n      if (i.enter) {\n        dict = Object.create(dict)\n        for (const j in i.value)\n          dict[j] = false\n      } else\n        dict = Object.getPrototypeOf(dict)\n      break\n    }\n    yield i\n  }\n}\n```\n\nThis is pretty simple and comprehensible. No awareness of visitors library\nprotocol is required. For example top stop iteration visitors typically require\nto signal it somehow, for example, with special value for function's result.\nHere it is just plain javascript `break` statement.\n\nAn special node type (`Scope`) is handled here for variables with the same name but\ndefined in sub-functions definitions. If we don't want to touch them we may run\nscope calculation transducer before, injecting such `Scope` nodes. They will be\nignored by `consume` function.\n\nThe usage is pretty simple, for example to rename `i` variables to `j':\n\n```javascript\n    consume(subst({i:{type:\"Identifier\", name: \"j\"}}, scope(produce(ast))))\n```\n\nOr much cleaner with Ramda:\n\n```javascript\n  R.pipe(\n    produce,\n    scope,\n    subst({i:{type:\"Identifier\", name: \"j\"}}),\n    consume\n  )(ast)\n```\n\nThere are more details in\n[test/rename.js](https://github.com/awto/estree-transducers/blob/master/test/rename.js)\n\n## LICENSE\n\nCopyright © 2016,2017 Vitaliy Akimov\n\nDistributed under the terms of The MIT License (MIT).\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fawto%2Festransducers","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fawto%2Festransducers","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fawto%2Festransducers/lists"}