{"id":13422456,"url":"https://github.com/airbnb/hypernova","last_synced_at":"2026-01-14T22:28:24.068Z","repository":{"id":40326123,"uuid":"60133112","full_name":"airbnb/hypernova","owner":"airbnb","description":"A service for server-side rendering your JavaScript views","archived":true,"fork":false,"pushed_at":"2023-10-09T17:55:43.000Z","size":306,"stargazers_count":5816,"open_issues_count":48,"forks_count":218,"subscribers_count":94,"default_branch":"master","last_synced_at":"2025-11-19T16:18:00.370Z","etag":null,"topics":[],"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/airbnb.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,"governance":null}},"created_at":"2016-06-01T00:34:24.000Z","updated_at":"2025-11-11T22:31:02.000Z","dependencies_parsed_at":"2023-01-28T16:16:21.287Z","dependency_job_id":"3a396065-c184-40bf-b142-87081aea4f6a","html_url":"https://github.com/airbnb/hypernova","commit_stats":{"total_commits":129,"total_committers":26,"mean_commits":4.961538461538462,"dds":0.6046511627906976,"last_synced_commit":"125c346022084e2d820f9da490b4468cba60d25e"},"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"purl":"pkg:github/airbnb/hypernova","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/airbnb%2Fhypernova","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/airbnb%2Fhypernova/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/airbnb%2Fhypernova/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/airbnb%2Fhypernova/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/airbnb","download_url":"https://codeload.github.com/airbnb/hypernova/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/airbnb%2Fhypernova/sbom","scorecard":{"id":173380,"data":{"date":"2025-08-11","repo":{"name":"github.com/airbnb/hypernova","commit":"125c346022084e2d820f9da490b4468cba60d25e"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.9,"checks":[{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Code-Review","score":5,"reason":"Found 9/16 approved changesets -- score normalized to 5","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Maintained","score":0,"reason":"project is archived","details":["Warn: Repository is archived."],"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/node-aught.yml:1","Warn: no topLevel permission defined: .github/workflows/node-pretest.yml:1","Warn: no topLevel permission defined: .github/workflows/node-tens.yml:1","Warn: no topLevel permission defined: .github/workflows/rebase.yml:1","Warn: no topLevel permission defined: .github/workflows/require-allow-edits.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: third-party GitHubAction not pinned by hash: .github/workflows/node-aught.yml:7: update your workflow using https://app.stepsecurity.io/secureworkflow/airbnb/hypernova/node-aught.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/node-pretest.yml:7: update your workflow using https://app.stepsecurity.io/secureworkflow/airbnb/hypernova/node-pretest.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/node-tens.yml:7: update your workflow using https://app.stepsecurity.io/secureworkflow/airbnb/hypernova/node-tens.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/rebase.yml:12: update your workflow using https://app.stepsecurity.io/secureworkflow/airbnb/hypernova/rebase.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/rebase.yml:13: update your workflow using https://app.stepsecurity.io/secureworkflow/airbnb/hypernova/rebase.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/require-allow-edits.yml:12: update your workflow using https://app.stepsecurity.io/secureworkflow/airbnb/hypernova/require-allow-edits.yml/master?enable=pin","Info:   0 out of   1 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   5 third-party GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Security-Policy","score":9,"reason":"security policy file detected","details":["Info: security policy file detected: github.com/airbnb/.github/SECURITY.md:1","Info: Found linked content: github.com/airbnb/.github/SECURITY.md:1","Warn: One or no descriptive hints of disclosure, vulnerability, and/or timelines in security policy","Info: Found text in security policy: github.com/airbnb/.github/SECURITY.md:1"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 23 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":0,"reason":"83 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-h47h-mwp9-c6q6","Warn: Project is vulnerable to: GHSA-4g8v-vg43-wpgf","Warn: Project is vulnerable to: GHSA-7wjx-3g7j-8584","Warn: Project is vulnerable to: GHSA-8xww-x3g3-6jcv","Warn: Project is vulnerable to: GHSA-hjg4-8q5f-x6fm","Warn: Project is vulnerable to: GHSA-p84v-45xj-wwqj","Warn: Project is vulnerable to: GHSA-vfg9-r3fq-jvx4","Warn: Project is vulnerable to: GHSA-x76w-6vjr-8xgj","Warn: Project is vulnerable to: GHSA-65cv-r6x7-79hv","Warn: Project is vulnerable to: GHSA-86g5-2wh3-gc9j","Warn: Project is vulnerable to: GHSA-cfjv-5498-mph5","Warn: Project is vulnerable to: GHSA-ch3h-j2vf-95pv","Warn: Project is vulnerable to: GHSA-cr3x-7m39-c6jq","Warn: Project is vulnerable to: GHSA-m63j-wh5w-c252","Warn: Project is vulnerable to: GHSA-q2qw-rmrh-vv42","Warn: Project is vulnerable to: GHSA-3hhc-qp5v-9p2j","Warn: Project is vulnerable to: GHSA-579w-22j4-4749","Warn: Project is vulnerable to: GHSA-j6gc-792m-qgm2","Warn: Project is vulnerable to: GHSA-pj73-v5mw-pm9j","Warn: Project is vulnerable to: GHSA-jxhc-q857-3j6g","Warn: Project is vulnerable to: GHSA-23c2-gwp5-pxw9","Warn: Project is vulnerable to: GHSA-6c3j-c64m-qhgq","Warn: Project is vulnerable to: GHSA-gxr4-xjj5-5px2","Warn: Project is vulnerable to: GHSA-jpcq-cgw6-v4j6","Warn: Project is vulnerable to: GHSA-jphg-qwrw-7w9g","Warn: Project is vulnerable to: GHSA-228g-948r-83gx","Warn: Project is vulnerable to: GHSA-3x8r-x6xp-q4vm","Warn: Project is vulnerable to: GHSA-486f-hjj9-9vhh","Warn: Project is vulnerable to: GHSA-c3gv-9cxf-6f57","Warn: Project is vulnerable to: GHSA-242x-7cm6-4w8j","Warn: Project is vulnerable to: GHSA-286v-pcf5-25rc","Warn: Project is vulnerable to: GHSA-2qc6-mcvw-92cw","Warn: Project is vulnerable to: GHSA-2rr5-8q37-2w7h","Warn: Project is vulnerable to: GHSA-353f-x4gh-cqq8","Warn: Project is vulnerable to: GHSA-4hm9-844j-jmxp","Warn: Project is vulnerable to: GHSA-59gp-qqm7-cw4j","Warn: Project is vulnerable to: GHSA-5w6v-399v-w3cc","Warn: Project is vulnerable to: GHSA-7553-jr98-vx47","Warn: Project is vulnerable to: GHSA-7rrm-v45f-jp64","Warn: Project is vulnerable to: GHSA-cf46-6xxh-pc75","Warn: Project is vulnerable to: GHSA-cgx6-hpwq-fhv5","Warn: Project is vulnerable to: GHSA-cr5j-953j-xw5p","Warn: Project is vulnerable to: GHSA-crjr-9rc5-ghw8","Warn: Project is vulnerable to: GHSA-fq42-c5rg-92c2","Warn: Project is vulnerable to: GHSA-gx8x-g87m-h5q6","Warn: Project is vulnerable to: GHSA-jc36-42cf-vqwj","Warn: Project is vulnerable to: GHSA-jw9f-hh49-cvp9","Warn: Project is vulnerable to: GHSA-mrxw-mxhj-p664","Warn: Project is vulnerable to: GHSA-pxvg-2qj5-37jq","Warn: Project is vulnerable to: GHSA-qxcg-xjjg-66mj","Warn: Project is vulnerable to: GHSA-r95h-9x8f-r3f7","Warn: Project is vulnerable to: GHSA-v4f8-2847-rwm7","Warn: Project is vulnerable to: GHSA-v6gp-9mmm-c6p5","Warn: Project is vulnerable to: GHSA-vmfx-gcfq-wvm2","Warn: Project is vulnerable to: GHSA-vr8q-g5c7-m54m","Warn: Project is vulnerable to: GHSA-vvfq-8hwr-qm4m","Warn: Project is vulnerable to: GHSA-xc9x-jj77-9p9j","Warn: Project is vulnerable to: GHSA-xh29-r2w5-wx8m","Warn: Project is vulnerable to: GHSA-xxx9-3xcr-gjj3","Warn: Project is vulnerable to: GHSA-22f2-v57c-j9cx","Warn: Project is vulnerable to: GHSA-3h57-hmj3-gj3p","Warn: Project is vulnerable to: GHSA-54rr-7fvw-6x8f","Warn: Project is vulnerable to: GHSA-5f9h-9pjv-v6j7","Warn: Project is vulnerable to: GHSA-65f5-mfpf-vfhj","Warn: Project is vulnerable to: GHSA-7g2v-jj9q-g3rg","Warn: Project is vulnerable to: GHSA-7wqh-767x-r66v","Warn: Project is vulnerable to: GHSA-8cgq-6mh2-7j6v","Warn: Project is vulnerable to: GHSA-gjh7-p2fx-99vx","Warn: Project is vulnerable to: GHSA-hrqr-hxpp-chr3","Warn: Project is vulnerable to: GHSA-hxqx-xwvh-44m2","Warn: Project is vulnerable to: GHSA-j6w9-fv6q-3q52","Warn: Project is vulnerable to: GHSA-vpfw-47h7-xj4g","Warn: Project is vulnerable to: GHSA-wq4h-7r42-5hrr","Warn: Project is vulnerable to: GHSA-xj5v-6v4g-jfw6","Warn: Project is vulnerable to: GHSA-5x79-w82f-gw8w","Warn: Project is vulnerable to: GHSA-9h9g-93gc-623h","Warn: Project is vulnerable to: GHSA-mcvf-2q2m-x72m","Warn: Project is vulnerable to: GHSA-pg8v-g4xq-hww9","Warn: Project is vulnerable to: GHSA-rrfc-7g8p-99q8","Warn: Project is vulnerable to: GHSA-jppv-gw3r-w3q8","Warn: Project is vulnerable to: GHSA-ggxm-pgc9-g7fp","Warn: Project is vulnerable to: GHSA-mqcp-p2hv-vw6x","Warn: Project is vulnerable to: GHSA-5cm2-9h8c-rvfx"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-16T17:07:54.718Z","repository_id":40326123,"created_at":"2025-08-16T17:07:54.718Z","updated_at":"2025-08-16T17:07:54.718Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28436404,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T21:32:52.117Z","status":"ssl_error","status_checked_at":"2026-01-14T21:32:33.442Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":[],"created_at":"2024-07-30T23:00:45.394Z","updated_at":"2026-01-14T22:28:24.044Z","avatar_url":"https://github.com/airbnb.png","language":"JavaScript","readme":"| :exclamation: Deprecation Notice |\n|:-|\n|We want to express our sincere gratitude for your support and contributions to the Hypernova open source project. As we are no longer using this technology internally, we have come to the decision to archive the Hypernova repositories. While we won't be providing further updates or support, the existing code and resources will remain accessible for your reference. We encourage anyone interested to fork the repository and continue the project's legacy independently. Thank you for being a part of this journey and for your patience and understanding.|\n---\n\n# Hypernova\n\n\u003e A service for server-side rendering your JavaScript views\n\n[![Join the chat at https://gitter.im/airbnb/hypernova](https://badges.gitter.im/airbnb/hypernova.svg)](https://gitter.im/airbnb/hypernova?utm_source=badge\u0026utm_medium=badge\u0026utm_campaign=pr-badge\u0026utm_content=badge)\n\n[![NPM version](https://badge.fury.io/js/hypernova.svg)](http://badge.fury.io/js/hypernova)\n[![Build Status](https://secure.travis-ci.org/airbnb/hypernova.svg?branch=master)](http://travis-ci.org/airbnb/hypernova)\n[![Dependency Status](https://david-dm.org/airbnb/hypernova.svg)](https://david-dm.org/airbnb/hypernova)\n\n## Why?\n\nFirst and foremost, server-side rendering is a better user experience compared to just client-side rendering. The user gets the content faster, the webpage is more accessible when JS fails or is disabled, and search engines have an easier time indexing it.\n\nSecondly, it provides a better developer experience. Writing the same markup twice both on the server in your preferred templating library and in JavaScript can be tedious and hard to maintain. Hypernova lets you write all of your view code in a single place without having to sacrifice the user’s experience.\n\n## How?\n\n![Diagram that visually explains how hypernova works](docs/hypernova-how-it-works.png)\n\n1. A user requests a page on your server.\n1. Your server then gathers all the data it needs to render the page.\n1. Your server uses a Hypernova client to submit an HTTP request to a Hypernova server.\n1. Hypernova server computes all the views into an HTML string and sends them back to the client.\n1. Your server then sends down the markup plus the JavaScript to the browser.\n1. On the browser, JavaScript is used to progressively enhance the application and make it dynamic.\n\n## Terminology\n\n* **hypernova/server** - Service that accepts data via HTTP request and responds with HTML.\n* **hypernova** - The universal component that takes care of turning your view into the HTML structure it needs to server-render. On the browser it bootstraps the server-rendered markup and runs it.\n* **hypernova-${client}** - This can be something like `hypernova-ruby` or `hypernova-node`. It is the client which gives your application the superpower of querying Hypernova and understanding how to fallback to client-rendering in case there is a failure.\n\n## Get Started\n\nFirst you’ll need to install a few packages: the server, the browser component, and the client. For development purposes it is recommended to install either alongside the code you wish to server-render or in the same application.\n\nFrom here on out we’ll assume you’re using [`hypernova-ruby`](https://github.com/airbnb/hypernova-ruby) and `React` with [`hypernova-react`](https://github.com/airbnb/hypernova-react).\n\n### Node\n\n```sh\nnpm install hypernova --save\n```\n\nThis package contains both the server and the client.\n\nNext, lets configure the development server. To keep things simple we can put the configuration in your root folder, it can be named something like `hypernova.js`.\n\n```js\nvar hypernova = require('hypernova/server');\n\nhypernova({\n  devMode: true,\n\n  getComponent(name) {\n    if (name === 'MyComponent.js') {\n      return require('./app/assets/javascripts/MyComponent.js');\n    }\n    return null;\n  },\n\n  port: 3030,\n});\n```\n\nOnly the `getComponent` function is required for Hypernova. All other configuration options are optional. [Notes on `getComponent` can be found below](#getcomponent).\n\nWe can run this server by starting it up with node.\n\n```sh\nnode hypernova.js\n```\n\nIf all goes well you should see a message that says \"Connected\". If there is an issue, a stack trace should appear in `stderr`.\n\n### Rails\n\nIf your server code is written in a language other than Ruby, then you can build your own client for Hypernova. A [spec](docs/client-spec.md) exists and details on how clients should function as well as fall-back in case of failure.\n\nAdd this line to your application’s Gemfile:\n\n```ruby\ngem 'hypernova'\n```\n\nAnd then execute:\n\n    $ bundle\n\nOr install it yourself as:\n\n    $ gem install hypernova\n\nNow lets add support on the Rails side for Hypernova. First, we’ll need to create an initializer.\n\n`config/initializers/hypernova_initializer.rb`\n\n```ruby\nHypernova.configure do |config|\n  config.host = \"localhost\"\n  config.port = 3030            # The port where the node service is listening\nend\n```\n\nIn your controller, you’ll need an `:around_filter` so you can opt into Hypernova rendering of view partials.\n\n```ruby\nclass SampleController \u003c ApplicationController\n  around_filter :hypernova_render_support\nend\n```\n\nAnd then in your view we `render_react_component`.\n\n```ruby\n\u003c%= render_react_component('MyComponent.js', :name =\u003e 'Hypernova The Renderer') %\u003e\n```\n\n### JavaScript\n\nFinally, lets set up `MyComponent.js` to be server-rendered. We will be using React to render.\n\n```js\nconst React = require('react');\nconst renderReact = require('hypernova-react').renderReact;\n\nfunction MyComponent(props) {\n  return \u003cdiv\u003eHello, {props.name}!\u003c/div\u003e;\n}\n\nmodule.exports = renderReact('MyComponent.js', MyComponent);\n```\n\nVisit the page and you should see your React component has been server-rendered. If you’d like to confirm, you can view the source of the page and look for `data-hypernova-key`. If you see a `div` filled with HTML then your component was server-rendered, if the `div` is empty then there was a problem and your component was client-rendered as a fall-back strategy.\n\nIf the `div` was empty, you can check `stderr` where you’re running the node service.\n\n## Debugging\n\nThe [developer plugin](https://github.com/airbnb/hypernova-ruby/blob/master/lib/hypernova/plugins/development_mode_plugin.rb) for [`hypernova-ruby`](https://github.com/airbnb/hypernova-ruby) is useful for debugging issues with Hypernova and why it falls back to client-rendering. It’ll display a warning plus a stack trace on the page whenever a component fails to render server-side.\n\nYou can install the developer plugin in `examples/simple/config/environments/development.rb`\n\n```ruby\nrequire 'hypernova'\nrequire 'hypernova/plugins/development_mode_plugin'\n\nHypernova.add_plugin!(DevelopmentModePlugin.new)\n```\n\nYou can also check the output of the server. The server outputs to `stdout` and `stderr` so if there is an error, check the process where you ran `node hypernova.js` and you should see the error.\n\n## Deploying\n\nThe recommended approach is running two separate servers, one that contains your server code and another that contains the Hypernova service. You’ll need to deploy the JavaScript code to the server that contains the Hypernova service as well.\n\nDepending on how you have `getComponent` configured, you might need to restart your Hypernova service on every deploy. If `getComponent` caches any code then a restart is paramount so that Hypernova receives the new changes. Caching is recommended because it helps speed up the service.\n\n## FAQ\n\n\u003e Isn’t sending an HTTP request slow?\n\nThere isn’t a lot of overhead or latency, especially if you keep the servers in close proximity to each other. It’s as fast as compiling many ERB templates and gives you the benefit of unifying your view code.\n\n\u003e Why not an in-memory JS VM?\n\nThis is a valid option. If you’re looking for a siloed experience where the JS service is kept separate, then Hypernova is right for you. This approach also lends itself better to environments that don’t already have a JS VM available.\n\n\u003e What if the server blows up?\n\nIf something bad happens while Hypernova is attempting to server-render your components it’ll default to failure mode where your page will be client-rendered instead. While this is a comfortable safety net, the goal is to server-render every request.\n\n## Pitfalls\n\nThese are pitfalls of server-rendering JavaScript code and are not specific to Hypernova.\n\n* You’ll want to do any DOM-related manipulations in `componentDidMount`. `componentDidMount` runs\n  on the browser but not the server, which means it’s safe to put DOM logic in there.\n  Putting logic outside of the component, in the constructor, or in `componentWillMount` will\n  cause the code to fail since the DOM isn’t present on the server.\n\n* It is recommended that you run your code in a VM sandbox so that requests get a fresh new\n  JavaScript environment. In the event that you decide not to use a VM, you should be aware that\n  singleton patterns and globals run the risk of leaking memory and/or leaking data\n  between requests. If you use `createGetComponent` you’ll get VM by default.\n\n## Clients\n\nSee [clients.md](docs/clients.md)\n\n## Browser\n\nThe included browser package is a barebones helper which renders markup on the server and then loads it on the browser.\n\nList of compatible browser packages:\n\n* [`hypernova-react`](https://github.com/airbnb/hypernova-react)\n* [`hypernova-aphrodite`](https://github.com/airbnb/hypernova-aphrodite)\n* [`hypernova-styled-components`](https://github.com/viatsko/hypernova-styled-components)\n\n## Server\n\nStarting up a Hypernova server\n\n```js\nconst hypernova = require('hypernova/server');\n\nhypernova({\n  getComponent: require,\n});\n```\n\nOptions, and their defaults\n\n```js\n{\n  // the limit at which body parser will throw\n  bodyParser: {\n    limit: 1024 * 1000,\n  },\n  // runs on a single process\n  devMode: false,\n  // how components will be retrieved,\n  getComponent: undefined,\n  // if not overridden, default will return the number of reported cpus  - 1\n  getCPUs: undefined,\n  // the host the app will bind to\n  host: '0.0.0.0',\n  // configure the default winston logger\n  logger: {},\n  // logger instance to use instead of the default winston logger\n  loggerInstance: undefined,\n  // the port the app will start on\n  port: 8080,\n  // default endpoint path\n  endpoint: '/batch',\n  // whether jobs in a batch are processed concurrently\n  processJobsConcurrently: true,\n  // arguments for server.listen, by default set to the configured [port, host]\n  listenArgs: null,\n  // default function to create an express app\n  createApplication: () =\u003e express()\n}\n```\n\n#### `getComponent`\n\nThis lets you provide your own implementation on how components are retrieved.\n\nThe most common use-case would be to use a VM to keep each module sandboxed between requests. You can use `createGetComponent` from Hypernova to retrieve a `getComponent` function that does this.\n\n`createGetComponent` receives an Object whose keys are the component’s registered name and the value is the absolute path to the component.\n\n```js\nconst path = require('path');\n\nhypernova({\n  getComponent: createGetComponent({\n    MyComponent: path.resolve(path.join('app', 'assets', 'javascripts', 'MyComponent.js')),\n  }),\n});\n```\n\nThe simplest `getComponent` would be to use `require`. One drawback here is that your components would be cached between requests and thus could leak memory and/or data. Another drawback is that the files would have to exist relative to where this require is being used.\n\n```js\nhypernova({\n  getComponent: require,\n});\n```\n\nYou can also fetch components asynchronously if you wish, and/or cache them. Just return a `Promise` from `getComponent`.\n\n```js\nhypernova({\n  getComponent(name) {\n    return promiseFetch('https://MyComponent');\n  },\n});\n```\n\n#### `getCPUs`\n\nThis lets you specify the number of cores Hypernova will run workers on. Receives an argument containing the number of cores as reported by the OS.\n\nIf this method is not overridden, or if a falsy value is passed, the default method will return the number of reported cores minus 1.\n\n#### `loggerInstance`\nThis lets you provide your own implementation of a logger as long as it has a `log()` method.\n\n```js\nconst winston = require('winston');\nconst options = {};\n\nhypernova({\n  loggerInstance: new winston.Logger({\n        transports: [\n          new winston.transports.Console(options),\n        ],\n      }),\n});\n```\n\n#### `processJobsConcurrently`\n\nThis determines whether jobs in a batch are processed concurrently or serially.  Serial execution is preferable if you use a renderer that is CPU bound and your plugins do not perform IO in the per job hooks.\n\n#### `createApplication`\nThis lets you provide your own function that creates an express app.\nYou are able to add your own express stuff like more routes, middlewares, etc.\nNotice that you __must__ pass a function that returns an express app without calling the `listen` method!\n\n```js\nconst express = require('express');\nconst yourOwnAwesomeMiddleware = require('custom-middleware');\n\nhypernova({\n  createApplication: function() {\n    const app = express();\n    app.use(yourOwnAwesomeMiddleware);\n\n    app.get('/health', function(req, res) {\n      return res.status(200).send('OK');\n    });\n\n    // this is mandatory.\n    return app;\n  }\n```\n\n## API\n\n### Browser\n\n#### load\n\n```typescript\ntype DeserializedData = { [x: string]: any };\ntype ServerRenderedPair = { node: HTMLElement, data: DeserializedData };\n\nfunction load(name: string): Array\u003cServerRenderedPair\u003e {}\n```\n\nLooks up the server-rendered DOM markup and its corresponding `script` JSON payload and returns it.\n\n#### serialize\n\n```typescript\ntype DeserializedData = { [x: string]: any };\n\nfunction serialize(name: string, html: string, data: DeserializedData): string {}\n```\n\nGenerates the markup that the browser will need to bootstrap your view on the browser.\n\n#### toScript\n\n```typescript\ntype DeserializedData = { [x: string]: any };\ntype Attributes = { [x: string]: string };\n\nfunction toScript(attrs: Attributes, props: DeserializedData): string {}\n```\n\nAn interface that allows you to create extra `script` tags for loading more data on the browser.\n\n#### fromScript\n\n```typescript\ntype DeserializedData = { [x: string]: any };\ntype Attributes = { [x: string]: string };\n\nfunction fromScript(attrs: Attributes): DeserializedData {}\n```\n\nThe inverse of `toScript`, this function runs on the browser and attempts to find and `JSON.parse` the contents of the server generated script.\n`attrs` is an object where the key will be a `data-key` to be placed on the element, and the value is the data attribute's value.\n\nThe `serialize` function uses the attributes `DATA_KEY` and `DATA_ID` to generate the data markup. They can be used in the `fromScript` function to get the serialized data.\n\n```typescript\nimport { DATA_KEY, DATA_ID } from 'hypernova'\n\nfromScript({\n    [DATA_KEY]: key,\n    [DATA_ID]: id,\n });\n```\n\n### Server\n\n#### [createGetComponent](src/createGetComponent.js)\n\n```typescript\ntype Files = { [key: string]: string };\ntype VMOptions = { cacheSize: number, environment?: () =\u003e any };\ntype GetComponent = (name: string) =\u003e any;\n\nfunction createGetComponent(files: Files, vmOptions: VMOptions): GetComponent {}\n```\n\nCreates a `getComponent` function which can then be passed into Hypernova so it knows how to retrieve your components. `createGetComponent` will create a VM so all your bundles can run independently from each other on each request so they don’t interfere with global state. Each component is also cached at startup in order to help speed up run time. The files Object key is the component’s name and its value is the absolute path to the component.\n\n#### [createVM](src/createVM.js)\n\n```typescript\ntype VMOptions = { cacheSize: number, environment?: () =\u003e any };\ntype Run = (name: string, code: string) =\u003e any;\ntype VMContainer = { exportsCache: any, run: Run };\n\nfunction createVM(options: VMOptions): VMContainer {}\n```\n\nCreates a VM using Node’s [`vm`](https://nodejs.org/api/vm.html) module. Calling `run` will run the provided code and return its `module.exports`. `exportsCache` is an instance of [`lru-cache`](https://github.com/isaacs/node-lru-cache).\n\n#### [getFiles](src/getFiles.js)\n\n```typescript\nfunction getFiles(fullPathStr: string): Array\u003c{name: string, path: string}\u003e {}\n```\n\nA utility function that allows you to retrieve all JS files recursively given an absolute path.\n\n#### [Module](src/Module.js)\n\n`Module` is a class that mimics Node’s [`module`](https://github.com/nodejs/node/blob/master/lib/module.js) interface. It makes `require` relative to whatever directory it’s run against and makes sure that each JavaScript module runs in its own clean sandbox.\n\n#### [loadModules](src/loadModules.js)\n\n```typescript\nfunction loadModules(require: any, files: Array\u003cstring\u003e): () =\u003e Module? {}\n```\n\nLoads all of the provided files into a `Module` that can be used as a parent `Module` inside a `VM`. This utility is useful when you need to pre-load a set of shims, shams, or JavaScript files that alter the runtime context. The `require` parameter is Node.js’ `require` function.\n","funding_links":[],"categories":["Code Design","Uncategorized","JavaScript","包","Packages","目录","Miscellaneous"],"sub_categories":["Isomorphic Apps","Uncategorized","其他","Miscellaneous"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fairbnb%2Fhypernova","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fairbnb%2Fhypernova","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fairbnb%2Fhypernova/lists"}