{"id":18736664,"url":"https://github.com/radiergummi/phoenix","last_synced_at":"2025-07-08T22:02:23.161Z","repository":{"id":88617213,"uuid":"111910814","full_name":"Radiergummi/phoenix","owner":"Radiergummi","description":"A lightweight and modular framework to document code projects","archived":false,"fork":false,"pushed_at":"2018-01-10T08:08:44.000Z","size":747,"stargazers_count":8,"open_issues_count":3,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-26T14:01:41.112Z","etag":null,"topics":["code-documentation","documentation","documentation-generator"],"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/Radiergummi.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":".github/CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":".github/CODE_OF_CONDUCT.md","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":"2017-11-24T11:25:21.000Z","updated_at":"2019-10-25T15:58:14.000Z","dependencies_parsed_at":null,"dependency_job_id":"a8aadeee-d75a-41ed-a43f-0a5de6f12273","html_url":"https://github.com/Radiergummi/phoenix","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Radiergummi%2Fphoenix","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Radiergummi%2Fphoenix/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Radiergummi%2Fphoenix/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Radiergummi%2Fphoenix/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Radiergummi","download_url":"https://codeload.github.com/Radiergummi/phoenix/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248621124,"owners_count":21134749,"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":["code-documentation","documentation","documentation-generator"],"created_at":"2024-11-07T15:22:02.266Z","updated_at":"2025-04-12T19:31:45.926Z","avatar_url":"https://github.com/Radiergummi.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"Phoenix [![Build status](https://teamcity.hades.9dev.de/app/rest/builds/buildType:(id:Phoenix_Build)/statusIcon.svg)](https://teamcity.hades.9dev.de/project.html?projectId=Phoenix\u0026branch_Phoenix=__all_branches__)\n=======\n*A lightweight and modular framework to document code projects*\n\n\nReasoning\n---------\nBorn out of frustration while working on a Vue.js project, I decided to write something on my own. \nThe amount of work to generate documentation for a simple project involving Vue.js components seemed\ntedious to me: While there are certain packages that parse single file components, it seemed near \nimpossible to get them to play nice with JSDoc or similar.  \nWhat lacks to date is a modular framework that clearly separates parsing sources from writing output\nfiles.  \n\nWelcome to Phoenix.\n\n\nContents\n--------\n\n1. [Abstract Concept](#abstract-concept)\n2. [Core Principles](#core-principles)\n3. [Installation](#installation)\n4. [Command line usage](#command-line-usage)\n5. [Programmatic Usage](#programmatic-usage)\n6. [Documents](#documents)\n7. [Terminology](#terminology)\n8. [Writing your own module](#writing-your-own-module)\n9. [Builds](#builds)\n10. [Attribution](#attribution)\n\n\u003e **A note beforehand:**  \n\u003e This document might look pretty intimidating. This is due to the project currently being in active\n development, so most of the stuff outlined here is detailed documentation of the inner workings of \n Phoenix.  \n\u003e As a user, you can skip straight to [3. Installation](#installation).  \n\u003e As a developer, or someone interested in how Phoenix works, continue on reading...\n\nAbstract Concept\n----------------\nPhoenix consists of five core building blocks:\n\n1. **Readers**\n   Readers are the first step in the chain: They *read source files*, obviously. By default, there \n   are only readers for STDIN and the file system, though you could create additional ones to read \n   from a deployment server, an HTTP resource or a Github repository.  \n   [Continue reading...](./lib/reader/README.md)\n   \n2. **Parsers**  \n   A parser *parses source code* and returns it as a *JavaScript representation*. It's sole \n   responsibility is to analyze the code passed to it as a string and return a `Document` (more on \n   that below). You might note this is not limited to JavaScript source: \n   A parser can parse any language.  \n   [Continue reading...](./lib/parser/README.md)\n\n\n3. **Transformers**  \n   A transformer takes the previously generated document object and\n   *transforms them into an output format*.  \n   This might be HTML, XML, Markdown or even binary ASCII code if that's your thing.  \n   [Continue reading...](./lib/transformer/README.md)\n\n\n4. **Writers**  \n   A writer takes the output data and *writes it to a target*. By default, as with readers there are\n   only writers for STDOUT and the file system, but you could easily create one that writes to an S3\n   bucket, a database or a git repository.  \n   [Continue reading...](./lib/writer/README.md)\n\n   \n5. **Documents**  \n   The Document object is an abstract representation of the documentation. Similar to the document\n   in browsers, it is a tree structure with infinitely nested nodes. This makes it possible to\n   document even the biggest projects cleanly.  \n   [Continue reading...](#documents)\n\n\nTherefore, the chain works as follows:\n\u003e **read input** → **parse input** → **transform AST** → **write output**  \n\nPhoenix makes it possible to document just about anything, as long as it can be parsed using \nJavaScript.\n\n\nCore principles\n---------------\n1. **Ease of use**: For the 90% use case, you should be able to install phoenix, pass it an input\n   path and get your documentation.\n2. **Modularity**: Extending Phoenix should be as easy as extending one of the abstract classes and\n   pass the name as an option.\n3. **Interoperability**: Phoenix should play nice with other tools, including CI servers, build\n   chains and system tools.\n4. **Implementation agnostic**: Phoenix should be able to read from anywhere, parse anything, build\n   any output and write to anywhere. No limits.\n5. **Stability**: New versions should follow Semver strictly and deprecate things slowly.\n\n\nInstallation\n------------\n\u003e Note: Phoenix is currently in initial development and not ready for use at the moment. If you plan\nto contribute, please have a look at the [Contributing section](#contributing).\n\nInstall the module:\n\n```bash\n// from the npm registry\nnpm install phoenix-docs\n\n// from Github\nnpm install Radiergummi/phoenix\n```\n\n\nCommand line usage\n------------------\nPhoenix provides a CLI application that can be found at [./bin/phoenix](./bin/phoenix)(.bat) or \ninvoked with `npm run phoenix`. It does load Phoenix with the specified configuration and run it.\n\n### Configuration files\nBy default, Phoenix uses a `.phoenixrc` file in the working directory. You can use the `-c` or \n`--config` switch to specify the path to another file, though.  \nThe PhoenixRC file is a single AMD module that exports an object. Contrary to JSON, this provides \nthe ability to retrieve your settings from somewhere else or use values depending on the environment\n(think CI or build servers).   \nNote, however, that Phoenix out of itself does not make use of environment variables. That might be\nchanged if anyone can provide a use case where evaluating them in the config file is not possible, \nthough. The config file has to be structured like so:  \n\n```js\nconst Phoenix = require('phoenix');\n\nmodule.exports = {\n  \n  // Holds general information about your project. Most values will be populated from the \n  // package.json, if found in the working directory.\n  project: {\n    \n    // Project name\n    name: String,\n    \n    // Project version. Defaults to 0.0.1\n    version: String\n  },\n  \n  // The next section holds all modules that should be loaded for the build process. All of them are\n  // supplied as arrays to retain the correct order for module chains.\n  \n  // Holds all readers. Readers are executed in parallel, chaining them is not necessary. \n  readers: [\n    \n    // The default Phoenix modules are available as static properties on the Phoenix class\n    Phoenix.FileSystemReader,\n    \n    // Third-party modules can be inserted using `require`\n    require('phoenix-reader-s3')\n  ],\n  \n  // Holds all parsers. Parsers are executed in parallel by default, but can be chained optionally.\n  parsers: [\n    \n    // This parser will be executed in parallel with all others. So in order to just parse all \n    // files, you can simply include them here one after the other.\n    require('phoenix-parser-php'),\n    \n    // This parser chains two or more parsers. They will be executed sequentially, receiving the \n    // files from the previous reader.\n    new Phoenix.ChainedParser([\n      Phoenix.VueComponentParser,\n      Phoenix.JSDocParser\n    ])\n  ],\n  \n  // Holds all transformers. Transformers are executed in parallel, chaining them is not necessary. \n  transformers: [\n    // ...\n  ],\n  \n  // Holds all writers. Writers are executed in parallel, chaining them is not necessary. \n  writers: [\n    // ...\n  ],\n  \n  // this is the first options object for a Phoenix module. It will be passed down to the file \n  // system reader, as is. You can specify options for any module here, as long as you name the \n  // options object as the module class name. So, for `class MyAwesomeModule {}` that'd be\n  // `MyAwesomeModule: {}`.\n  // Any options you pass will be merged with the default options deeply. Phoenix will try to make\n  // reasonable decisions by default, but you might want to modify some of them. All modules state\n  // their default options in the documentation.\n  FileSystemReader: {}\n};\n```\n\n\nProgrammatic Usage\n------------------\nBy default, the only thing required is calling `run()` on a configured Phoenix instance. That will \nread the input and write documentation output. If you want to customize the process, however, the \nAPI is at your hands at all times in the build process.\nThe API is available in two flavors: Either *event based* or *promise based*.\nEvery module is obliged to return a promise and emit events at certain points in the flow. You are \nnot bound to one, of course: Promises and events mix just fine.\n\n### Simple usage\n\n```js\nconst phoenix = new Phoenix(options);\n\nphoenix.run();\n```\n\n### Using Promises\n\n```js\nconst phoenix = new Phoenix(options);\n\nphoenix.createDocument('my project title')\n\n// we can read arbitrary glob paths here if we use the FileSystemReader\n.then(document =\u003e phoenix.read(['path1', 'path2']))\n\n// at this point, we have an array of objects that describe all of our source files\n.then(sourceFilesContent =\u003e phoenix.parse(sourceFilesContent));\n```\n\nPoint of the code examples being, you can intervene at any point in the pipeline and do your thing \nwith the current results, then continue.\n\n### Using events\nNo surprises with event emitters: I used the ordinary `events` module. Phoenix emits all sorts of \nevents, all of which you can find [in the table below](#event-list). You can hook into them and \nmodify the current results at any time.\n\n```js\nconst phoenix = new Phoenix(options);\n\nphoenix.on('read:after', sourceFilesContent =\u003e console.log(sourceFilesContent));\n\nphoenix.run();\n```\n\n\nDocuments\n---------\nWhat would be the best way to maintain an output agnostic data format for documentation? It's \nalready in there: A `Document`. And yes, it's similar to your everyday browser document, just a \nlittle stripped down and optimized for the use case.  \nThe Document object resembles a tree structure that holds nested document nodes. There is a basic\npreset of different node types, but you can of course create new node types based on the existing.\nThe task of a transformer is to transform the document nodes into the output nodes. Consider this:\nA `section` node might directly map to the `\u003csection\u003e` tag, while in markdown, it's just a new line.\nAn Overview of the `Document` API can be found in the \n[Document API documentation](./lib/document/README.md).\n\n\n#### Event list\n**This section is, as the whole project, still a work in progress.**\n\n| Name     \t\t\t\t  | Event properties | Description |\n|:------------------|:-----------------|:------------|\n| init      \t\t\t\t| `ENV`, `options` | Phoenix initialisation. Receives current environment and the options Phoenix has retrieved from `ENV`, a CLI parameter or config file. \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t       |\n| read:before \t\t\t|  | Before any read operation happens. \t\t\t\t\t\t |\n| read:init \t\t\t\t|  | When all read instances have been created. \t\t |\n| read:file \t\t\t\t|  | When a file is read. \t\t\t\t\t\t\t\t\t\t\t\t\t |\n| read:directory \t\t|  | When a directory is read. \t\t\t\t\t\t\t\t\t\t\t |\n| read:done \t\t\t\t|  | When all read operations have started. \t\t\t\t |\n| read:after      \t|  | After all read operations are finished. \t\t\t\t |\n| parse:before      |  | Before any parse operation happens. \t\t\t\t\t\t |\n| parse:init      \t|  | When all parse instances have been created.     |\n| parse:file \t\t\t\t|  | When a file is parsed. \t\t\t\t\t\t\t\t\t\t\t   |\n| parse:symbol \t\t\t|  | When a symbol is parsed. \t\t\t\t\t\t\t\t\t\t\t |\n| parse:method \t\t\t|  | When a method is parsed. \t\t\t\t\t\t\t\t\t\t\t |\n| parse:property \t\t|  | When a property is parsed. \t\t\t\t\t\t\t\t\t\t |\n| parse:class \t\t\t|  | When a class is parsed. \t\t\t\t\t\t\t\t\t\t\t\t |\n| parse:done \t\t\t\t|  | When all parse operations have started. \t\t\t\t |\n| parse:after \t\t\t|  | After all parse operations are finished. \t\t\t |\n| transform:before  |  | Before any transform operation happens.\t \t\t\t |\n| transform:init \t  |  | When all transform instances have been created. |\n| transform:file    |  | When the transform of a file is created. \t\t\t |\n| transform:section |  | When the transform of a section is created. \t\t |\n| transform:done    |  | When all transform operations have started. \t\t |\n| transform:after   |  | After all transform operations are finished. \t |\n| write:before \t\t\t|  | Before any write operation happens. \t\t\t\t\t\t |\n| write:init \t\t\t\t|  | When all write instances have been created. \t\t |\n| write:file \t\t\t\t|  | When a file is written. \t\t\t\t\t\t\t\t\t\t\t\t |\n| write:directory  \t|  | When a directory is created. \t\t\t\t\t\t\t\t\t |\n| write:done\t\t\t\t|  | When all write operations have started. \t\t\t\t |\n| write:after\t\t\t\t|  | After all write operations are finished. \t\t\t |\n\nAdditionally, each module has the possibility of emitting their own events. They will be proxied to\nthe Phoenix instance as `{{module prototype name}}:{{module class name}}:{{event name}}`. For \nexample, should a transform module that creates XML output (*\"FoobarXMLTransformer\"*) emit a \n`newNode` event, it would be emitted like so: `transform:foobarxmltransformer:newNode`.\n\n\nTerminology\n===========\nPhoenix uses several terms that might sound unfamiliar at first. This is necessary to distinguish \nbetween several states the subject code can take and to stay technology agnostic.\n\n### Origin\nAn *origin* is a place to pull [sources](#source) from. This might be a file path, a database \nconnection or a remote URI.\n   \n### Source\nA *source* is an object representing a code fragment. At minimum, it has two properties: `name` and \n`code`, where `name` is the source identifier (for example a file path) and `code` is the actual \nsource code that is subject to documentation.\n   \n### AST\n*AST* is an abbreviation for *Abstract Syntax Tree*. It represents the abstract structure of any \nkind of source code. You can read more on the topic over at \n[Wikipedia](https://en.wikipedia.org/wiki/Abstract_syntax_tree).\n\n### Documentation object\nIn lieu of a better word, I decided to call transformed documentation snippets (already in their\ntarget output format) *objects*. This might be subject to change, though.  \nAn *object* refers to a [document node](#node) that holds transformed documentation text.\n\n### Node\n*Nodes* represent a single branch in the tree structure of a [document](#document). They can hold \nchildren nodes themselves and are roughly comparable to the \n[Browser node object](https://developer.mozilla.org/en/docs/Web/API/Node).\n\n### Document\nThe *Document* class is the heart of Phoenix. The whole process of reading, parsing, transforming \nand writing code and documentation focuses on modifying a single `document` instance that holds the\norigins, sources, documentation objects and output fragments.\n\nContributing\n------------\nContributions are welcome at any time. If you're experiencing a problem with Phoenix, please \n[create a new issue](https://github.com/Radiergummi/phoenix/issues/new).  \nBefore submitting a new pull request, please read [CONTRIBUTING.md](./.github/CONTRIBUTING.md) for \ndetails on our code of conduct and the process for submitting pull requests.\n\n\nWriting your own Phoenix module\n-------------------------------\nPhoenix has been designed to be a framework from the start. I wanted to make sure anyone can design,\nimplement and test modules for whatever purpose as easy as possible. That has been the driving force\nbehind many design decisions, too: Instead of a promise-based, object-oriented approach, using \nsomething based on streams would have been entirely possible, maybe even more efficient - but not as \nfriendly to work with or build upon.  \nTo start off with your own module, you should first consult the general module documentation and \nhave a look at some of the existing core modules. All of them inherit their base module, that is, a\nclass providing the general API Phoenix expects from the module, event emitter inheritance, error \nhandling, option merging and logging. You don't need to take care of any of these, but you can, via\noverwriting the parent properties and methods. Let's take a look at an example:  \n\n```js\nconst Phoenix = require('phoenix');\n\nclass MyAwesomeReader extends Phoenix.Reader {\n  \n  /**\n  * `_invoke` is the only required method for any module. Depending on what kind of module it is, a \n  * Reader in this case, it provides the main functionality.\n  * \n  * @returns {Promise} \n\t*/\n  _invoke() {\n    // we have access to `this.origins`, `this.document` and `this._options` already\n  }\n}\n```\nß\nThis class is almost complete! You could include it in any Phoenix workflow, it just would not do \nanything actually useful. Inside the `_invoke` method, you should use the `this.document` property\nto work with the code provided by the user. You can find detailed documentation on what is expected\nfrom a module in the module documentation.\n\n\nBuilds\n------\nPhoenix is currently built [on my TeamCity server](https://teamcity.hades.9dev.de/project.html?projectId=Phoenix\u0026branch_Phoenix=__all_branches__). You'll have guest access if you click on *Login as guest* below the login form.  \nThe TeamCity page provides several detailed reports, including code coverage and test sources, as well as documentation.\n\n\nAttribution\n-----------\nPhoenix would not have been possible without the work of a lot of awesome people and open source \nprojects, way too many to mention.  \nThere are some, however, that I'd like to mention specifically:\n\n - [Craig](https://github.com/craigchilds94) for joining in right from the start!\n - [onury](https://github.com/onury) from [jsdoc-x](https://github.com/onury/jsdoc-x) for providing \n   an almost-instant change to the library's error output\n - [rafaesc](https://github.com/rafaesc) from\n   [vue-docgen-api](https://github.com/vue-styleguidist/vue-docgen-api) for including a method to \n   parse sources instead of files\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fradiergummi%2Fphoenix","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fradiergummi%2Fphoenix","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fradiergummi%2Fphoenix/lists"}