{"id":16906529,"url":"https://github.com/rsms/jo","last_synced_at":"2025-09-07T03:40:02.710Z","repository":{"id":29433825,"uuid":"32969853","full_name":"rsms/jo","owner":"rsms","description":"Go-style JavaScript ES6 compiler and packager, based on Babel","archived":false,"fork":false,"pushed_at":"2015-10-05T15:50:15.000Z","size":1156,"stargazers_count":30,"open_issues_count":1,"forks_count":3,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-03-15T21:17:05.426Z","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":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rsms.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-03-27T05:41:45.000Z","updated_at":"2021-05-05T16:55:53.000Z","dependencies_parsed_at":"2022-09-01T00:13:00.558Z","dependency_job_id":null,"html_url":"https://github.com/rsms/jo","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/rsms%2Fjo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rsms%2Fjo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rsms%2Fjo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rsms%2Fjo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rsms","download_url":"https://codeload.github.com/rsms/jo/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244943732,"owners_count":20536290,"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":[],"created_at":"2024-10-13T18:43:03.890Z","updated_at":"2025-03-22T10:31:12.771Z","avatar_url":"https://github.com/rsms.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"Jo is a [Go-style](http://golang.org/doc/code.html#Introduction) JavaScript ES6 compiler and packager, based on [Babel](http://babeljs.io/).\n\n\u003e THIS IS WORK IN PROGRESS\n\n- Go-style *[convention over configuration](https://en.wikipedia.org/wiki/Convention_over_configuration)* means Jo is straight-forward and opinionated\n- Modules are comprised of directories, not files\n- Built-in linting will tell you about usage of undefined variables, etc\n- Compilation and compatibility fills provided by well-tested Babel\n- Separation of target code generation allows building both Nodejs code and web browser code, for optimal performance and linting\n\nJo comes precompiled and can be used from source:\n\n```\n$ ./bin/jo help\n```\n\nOr installed via [npm](https://www.npmjs.com/package/jo):\n```\n$ npm install -g jo\n$ jo help\n```\n\nFor hacking on Jo, Jo builds itself, like a true compiler:\n```\n$ ./bin/jo-g env\n-bash: bin/jo-g: No such file or directory\n$ ./bin/jo build -dev -v jo/jo\nbuilding source package jo/jo\n...\n$ ./bin/jo-g env\nJOPATH=\"\"\nJOROOT=\"/Users/rasmus/src2/jo\"\n```\n\n\n## Automatic package-internal vs exported symbols\n\nLike Go, Jo automatically exports symbols that begin with a captial letter:\n\nfoo/info.js\n```js\nvar Version = '1.2'\nfunction Name() { return 'Foo' }\nvar something = 123\n```\n\nbar/bar.js\n```js\nimport 'foo'\nfunction main() {\n  console.log(foo.Version)   // -\u003e \"1.2\"\n  console.log(foo.Name())    // -\u003e \"Foo\"\n  console.log(foo.something) // -\u003e undefined\n}\n```\n\nHowever, any symbol can be explicitly exported:\n\n```js\nexport var something = 123\n```\n\nAll symbols inside a package are automatically available within all source files of that package:\n\nfoo/a.js\n```js\nvar a = 100\n```\n\nfoo/b.js\n```js\nfunction main() {\n  console.log(a)  // -\u003e 100\n}\n```\n\nThis means that you can start small by building your software with a single file and as it grows create new files, rename files etc without any side-effects on other source files or other packages.\n\nValue dependencies are automatically resolved, meaning that this is possible:\n\nfoo/ape.js\n```js\nclass Ape extends Primate {}\n```\n\nfoo/primate.js\n```js\nclass Primate {}\n```\n\nWhich would compile to something like this:\n\n```js\n// import _classCallCheck and _inherits from babel-runtime/helpers\n\"use strict\";\nvar Primate = function Primate(){\n  _classCallCheck(this, Primate);\n};\nvar Ape = (function(_Primate){\n  function Ape(){\n    _classCallCheck(this, Ape);\n    if (_Primate != null){\n      _Primate.apply(this, arguments);\n    }\n  }\n  _inherits(Ape, _Primate);\n  return Ape;\n})(Primate);\nexports.Primate = Primate;\nexports.Ape = Ape;\n```\n\n## File-local imports\n\nJust like in Go, imports are *file local* and does not affect an entire package:\n\nfoo/a.js\n```js\nimport {Hello} from 'something'\nvar Message = Hello;\n```\n\nfoo/b.js\n```js\nvar OtherMessage = Hello;\n```\n\n```\n$ jo build\n./b.js:1:19 unresolvable identifier \"Hello\" (ReferenceError)\n →  1  var OtherMessage = Hello;\n                          ^~~~~\n```\n\nMeaning this is valid:\n\nfoo/b.js\n```js\nimport {Hello} from 'something-else'\nvar OtherMessage = Hello;\n```\n\nNow the \"foo\" package builds and exports `Message` with the value of `Hello` of package \"something\", and `OtherMessage` with the value of `Hello` of package \"something-else\".\n\nFile-local identifiers are internally converted to `_filename$originalname`. When multiple files import the same package or some combination of specific package symbols and packages, Jo figures out the minimal amount of imports needed:\n\nfoo/a.js\n```\nimport {A} from 'some/thing'\n```\n\nfoo/b.js\n```\nimport 'some/thing'\n```\n\nfoo/c.js\n```\nimport {A, B} from 'some/thing'\n```\n\nThe final code contains only a single import:\n\n```\n$ jo build -o=-\n...\n  , _b_js$thing = _$import(\"some/thing\")\n  , _a_js$A     = _b_js$thing.A\n  , _c_js$A     = _b_js$thing.A\n  , _c_js$B     = _b_js$thing.B\n...\n```\n\n## Catching programming errors during compilation\n\nSince Jo compiles your code it already needs to understand it and while doing so also checks for a bunch of common programming errors like usage of undefined symbols or cyclic dependencies:\n\nfoo/ape.js\n```js\nclass Ape extends Primate {}\n```\n\nfoo/primate.js\n```js\nclass Primate extends Ape {}\n```\n\nJo will tell us that we screwed up:\n\n```\n$ jo build\ncyclic dependency between source files \"primate.js\" and \"ape.js\" in package \".\" (ReferenceError)\n./ape.js:1:0 \"Ape\" defined here\n →  1  class Ape extends Primate {}\n       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~\n    ./primate.js:1:22 \"Ape\" referenced here\n     →  1  class Primate extends Ape {}\n                                 ^~~\n    ./primate.js:1:0 \"Primate\" defined here\n     →  1  class Primate extends Ape {}\n           ^~~~~~~~~~~~~~~~~~~~~~~~~~~~\n    ./ape.js:1:18 \"Primate\" referenced here\n     →  1  class Ape extends Primate {}\n                             ^~~~~~~\n```\n\nJo isn't perfect and doesn't handle the following case:\n\nfoo/ape.js\n```js\nclass Ape extends Primate {}\nclass Bob {}\n```\n\nfoo/primate.js\n```js\nclass Primate {}\nclass Funny extends Bob {}\n```\n\noutput:\n```\n$ jo build\ncyclic dependency between source files \"primate.js\" and \"ape.js\" in package \".\" (ReferenceError)\n./ape.js:2:0 \"Bob\" defined here\n    1  class Ape extends Primate {}\n →  2  class Bob {}\n       ^~~~~~~~~~~~\n    ./primate.js:2:20 \"Bob\" referenced here\n        1  class Primate {}\n     →  2  class Funny extends Bob {}\n                               ^~~\n    ./primate.js:1:0 \"Primate\" defined here\n     →  1  class Primate {}\n           ^~~~~~~~~~~~~~~~\n    ./ape.js:1:18 \"Primate\" referenced here\n     →  1  class Ape extends Primate {}\n                             ^~~~~~~\n```\n\nIn this scenario, simply break out the classes into separate files.\n\nAnother common programming error that Jo helps you avoid is using undefined variables:\n\nfoo/ape.js\n```js\nclass Primate {}\nvar Primat;\nclass Ape extends primate {}\n```\n\n```\n$ jo build\n./ape.js:3:18 unresolvable identifier \"primate\" (ReferenceError)\n    1  class Primate {}\n    2  var Primat;\n →  3  class Ape extends primate {}\n                         ^~~~~~~\n  Did you mean:\n    Primate defined in ./ape.js:1:0\n    Primat defined in ./ape.js:2:4\n```\n\nThe same goes for duplicate identifiers within a package:\n\nfoo/a.js\n```js\nvar x;\n```\n\nfoo/b.js\n```js\nfunction x() {}\n```\n\noutput:\n```\n$ jo build\n./b.js:1:9 duplicate identifier in function declaration (ReferenceError)\n →  1  function x() {}\n                ^\n    ./a.js:1:4 var declared here\n     →  1  var x;\n               ^\n```\n\n\n## Import statements\n\nLike Go, Jo dictates where dependency packages are located. When a non-relative import is encountered, Jo looks for the package in the following places:\n\n1. JOROOT/src\n2. JOPATH[0]/src\n3. JOPATH[n]/src ...\n\n\u003e Internally, this logic is contained within `Env`\n\nAs ES6 does not specify the effect of the `import` statement (only the syntax), Jo attempts to be as flexible as possible to allow as many meningful forms as possible.\n\nOne of the forms usually not understood by other ES6 systems is the short form (matching Go). The following statements are both equivalent:\n\n```js\nimport \"foo/bar\"\nimport bar from \"foo/bar\"\n```\n\nThe first form import the \"default\" namespace of the module into an inferred name based on the package's ref (i.e. \"bar\"). How the name is inferred from follows the rules of Go.\n\n```\n\"bar\"                        =\u003e bar\n\"foo/bar\"                    =\u003e bar\n\"foo/bar-baz\"                =\u003e baz\n\"foo/bar-baz.js\"             =\u003e baz\n\"github.com/rsms/jo-leveldb\" =\u003e leveldb\n\"foo/-\"                      Error: failed to infer module identifier\n```\n\n\u003e The inferred-name logic is defined in `JSIdentifier.fromString`\n\nBoth \"ref\" imports (e.g. \"foo\") and relative imports (e.g. \"./foo\") are checked at compile-time for existence, though there are no guarantees of runtime behaviour as they are loaded from disk for the \"nodejs\" code target (the \"browser\" target makes copies.)\n\nExample of various import statements:\n\n```js\nimport \"some/thing\"\nimport {Component} from \"some/thing\"\nimport react from \"./my-react\"\nimport {Anne, Bob} from \"friends\"\nimport foes, {Zorro, Baltazhar as BMan} from \"foes\"\nimport * as cats from \"lolcats\"\n```\n\n\n## Automatic package dependency resolution\n\nJo automatically checks and builds any dependency packages\n\nfoo/a.js\n```js\nimport \"bar\"\n```\n\nbar/a.js\n```js\nimport \"react\"\n```\n\n```\n$ cd foo\n$ jo build -v\nbuilding source package .\n  building source package bar\n    building precompiled package react\n```\n\nDepending on the primary package and build target (in the above case the current directory for nodejs) dependencies are stored next to its source JOROOT or JOPATH, embedded locally for \"browser\" target, or in a nodejs target's \"node_modules\" directory. Packages are only recompiled when its source code changes (or when passing the \"-a\" flag to build), meaning that your workflow is simply:\n\n```\n$ jo build\n```\n\n\n## init()\n\nBecause of Jo packages being composed of a variable number of files which order is undefined, there's no clear way of running package initialization code, code that needs to be run when the package's module is imported. In a traditional module this is not a problem as a traditional module is just a single file, so you just add your initialization code to the end of the file, but with Jo packages this isn't possible when more than one file is used.\n\n\u003e init is called after all the variable declarations in the package have evaluated their initializers, and those are evaluated only after all the imported packages have been initialized. Any init functions are called before any main function is called.\n\nGo solves this by allowing each file to define an \"init\" function, and so does Jo:\n\nfoo/a.js\n```js\nfunction init() { console.log('value =', value); }\n```\n\nfoo/b.js\n```js\nvar value = 123;\n```\n\n```\n$ jo build \u0026\u0026 node foo\nvalue = 123\n```\n\n## main()\n\nIf a package defines a function called `main` that function is automatically invoked when the package's product is executed.\n\nFor the \"nodejs\" (default) build target, this means that the product is an executable program:\n\nfoo/a.js\n```js\nfunction main() {\n  console.log('hello ' + what)\n}\n```\n\nfoo/b.js\n```js\nvar what = 'world'\n```\n\n```\n$ go build\n$ ./foo\nhello world\n```\n\nfoo/foo\n```js\n#!/usr/bin/env node --harmony\n//module.paths is updated with any paths for finding babel helpers and source map support\nfunction main() {\n  console.log('hello world')\n}\nvar what = 'world';\nmain();\n```\n\nFor the \"browser\" target—for building web pages—the product is a collection of files with \"index.html\" being the entry-point, generated from \"index.template.html\":\n\nfoo/index.template.html\n```html\n\u003c!DOCTYPE HTML\u003e\n\u003chtml lang=\"en\"\u003e\n  \u003chead\u003e\n    \u003cmeta charset=\"utf-8\"\u003e\n  \u003c/head\u003e\n  \u003cbody\u003e\n    Hello\n  \u003c/body\u003e\n\u003c/html\u003e\n```\n\nfoo/main.js\n```js\nfunction main() {\n  document.body.innerText = 'Hello world'\n}\n```\n\n```\n$ go build -target=browser\n```\n\n\nfoo/index.html\n```html\n\u003c!DOCTYPE HTML\u003e\n\u003chtml lang=\"en\"\u003e\n  \u003chead\u003e\n    \u003cmeta charset=\"utf-8\"\u003e\n    \u003cscript type=\"text/javascript\"\u003e\n    _$jomodules = {};\n    (function(){\n      // Code to load all packages, resolve imports and finally invoke any main() function\n      lm(\".jopkg.foo.js?ibmqga6g\");\n      lm(\".jopkg.babel-runtime.js?ibo35xco\");\n    })();\n    \u003c/script\u003e\n  \u003c/head\u003e\n  \u003cbody\u003e\n    Hello\n  \u003c/body\u003e\n\u003c/html\u003e\n```\n\nAll dependencies, including the main package's compiled code, are stored in the output directory prefixed by \".jopkg.*\". This makes \"browser\" products self-contained and easily relocatable.\n\n\n## Built-in React support\n\n- JSX compiler with linting\n- No need to explicitly import React\n\nfoo/a.js\n```js\nclass Foo extends ReactComponent {\n  render() {\n    return \u003cdiv\u003eHello\u003c/div\u003e;\n  }\n}\n```\n\n```js\n$ jo build -o=-\n//[header with import of some babel-runtime/helpers]\nvar React = _$import(\"react\"), ReactComponent = React.Component;\nvar Foo = (function(_ReactComponent){\n  function Foo(){\n    _classCallCheck(this, Foo);\n    if (_ReactComponent != null){\n      _ReactComponent.apply(this, arguments);\n    }\n  }\n  _inherits(Foo, _ReactComponent);\n  _createClass(Foo, {\n    render:{\n      value:function render(){\n        return React.createElement(\"div\", null, \"Hello\");\n      }\n    }\n  });\n  return Foo;\n})(ReactComponent);\nexports.Foo = Foo;\n//#sourceMappingURL=data...\n```\n\n\n## MIT license\n\nCopyright (c) 2015 Rasmus Andersson \u003chttp://rsms.me/\u003e\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frsms%2Fjo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frsms%2Fjo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frsms%2Fjo/lists"}