# SJW - [Usage](#usage) - Install + [How *not* to install](#how-not-to-install) + [How to install](#how-to-install) - [Contributing](#contributing) The Simple Javascript Wrench is a tool made to "compile" a set of independant Javascript modules into a single "executable" Javascript file which can then be loaded and executed by a browser to animate your web pages. Since no conversion to a different language is performed in the process, "compile" is intended here with the meaning of "assembling together". We will keep using it with this meaning by convenience. This allows one to write (relatively) clean Javascript and to design libraries to reuse code accross projects. `SJW` aims at maximal backwards compatibility and requires as little browser extensions as possible. In particular, `SJW`'s modules are not related to ES6' ones. They provide isolation, hiding all the internals of a module from the others, except what the module exposes through the use of a global-scope `return` statement. The output `SJW` produces is a single pure-Javascript file wich code runs once the browser has finished loading the page ([`load` event](https://developer.mozilla.org/en-US/docs/Web/API/Window/load_event)). To achieve this, it slightly extends the syntax of Javascript to add `import` statements at the top of the modules to describe their relations. These statements disappear after the files are compiled by `SJW` and are completely invisible to the web browser eventually loading the script. In this regard, `SJW` can be thought as "yet another language-specific package manager". However, SJW is not actually a package manager and only focuses on assembling the modules into one script. To handle packages and dependencies in themselves, it relies instead on [`guix`](https://guix.gnu.org/). In addition to the benefit of not reinventing the proverbial wheel, this brings cool features like reproducibility, controlled environments (making sure your package isn't actually broken but only seems to work because of that file you have locally but forgot to version) and the ability to keep as many versions of the same library as your various projects need while keeping the packages database unduplicated. Finally, it makes it very simple to develop and deploy your own packages without needing to rely on a central repository. ## Usage ### Command-line arguments The input expected by `SJW` is the path to a directory containing the modules of the web application to compile. For instance, the demo project distributed in this repository, which Javascript sources are found in the subdirectory `src/` can be assembled from the root of this repository with ```sh sjw demo/src/ ``` By default it prints the resulting Javascript code on its standard output. ```javascript window.addEventListener('load', function() { … }); ``` This can be overridden as usual in compilers by using the `-o` (long form `--output`) option to set the path of the file to create. ```sh sjw demo/src/ -o main.js ``` Since the web application `SJW` compiles is an executable script, it needs an entry point. By default `SJW` expects to find a module named `Main` at the root of the web application (as is the case in the demo project). The name of the main module can be set with the `-m` (`--main-is`) option like this: ```sh sjw demo/src/ -m MyCustomMain ``` Libraries don't require any compilation, they are merely copied until being needed in a particular web application. Once they are properly made available to `SJW` as we will next discuss, they can be included into a project, as with most compilers, with the `-I` (`--include`) option: ```sh sjw demo/src -I someAdditionalLib ``` ### Libraries The arguments to the previous `-I` option can be any path to a directory where you have a `SJW` library installed. However, it is common to want to keep all your libraries in a single place and refer to them only by their relative path from this package database. By default, `SJW` will look for a directory named `.sjw` in your home directory and use it as the package database when it exists. ```sh ls ~/.sjw # prints: myLib/ sjw -I myLib src # will work ``` This can be overridden by setting the `$SJW_PACKAGE_DB` environment variable to the path of the directory where you keep your libraries. In both cases, the package database consists of only one directory where the librarie's subdirectories are kept. ```sh export SJW_PACKAGE_DB="somewhere/else" ls $SJW_PACKAGE_DB # prints: anotherLib/ sjw -I anotherLib src # will work sjw -I myLib # no longer works because SJW_PACKAGE_DB hides ~/.sjw ``` Additionally you may keep your libraries in different, unrelated directories and set `$SJW_PATH` to the colon-separated (`:`) list of paths to search. ```sh export SJW_PATH="somewhere/else:${HOME}/.sjw" sjw -I anotherLib -I myLib src # will work ``` Note that these act as the roots of separate package databases, they do not point directly to the directories of each individual library. That is, in the previous example, setting `SJW_PATH` to `"somewhere/else/anotherLib:${HOME}/.sjw/myLib"` would not have worked. We will call directories like `anotherLib` and `myLib` the "name" directories of a library. ### About modules and paths Module names must contain only letters and digits and start with a letter which should by convention be uppercase. A module defined in file `A.js` will be available for import from other source files from the symbolic path `A` (no `.js` extension). Within a directory named `A`, a file called `B.js` is available as the symbolic `A.B` module. Each subdirectory level adds a new component to the module's path with the exact same name as the subdirectory. ```sh ls demo/src/Math/ # prints: Fibonacci.js grep Fibonacci demo/src/Main.js # prints: import get as nth from Math.Fibonacci; ``` The directory containing a library and used to include it with `-I` is not the first component of the modules' paths it defines. Going back to the first `myLib` example, assuming that this library defines a module `MyLib.Utils` it will have the following structure: ```sh find ~/.sjw/myLib # prints: # ~/.sjw/myLib # ~/.sjw/myLib/MyLib # ~/.sjw/myLib/MyLib/Util.sh ``` By contrast with the "name" directory, we will call the first directory in the modules' path the "modules" directory. In the example right above, `myLib` is the name directory, and `MyLib` is its modules directory. ## Install ### How *not* to install Thanks to the way `guix` works, you don't really need to have `SJW` installed in your system or user profile to use it. Instead, you need only to package your web application with `guix` and make sure to add `SJW` to the [`native-inputs` field](https://guix.gnu.org/fr/manual/devel/en/html_node/package-Reference.html#package-Reference), along with the `SJW` libraries your project uses. Until `SJW` makes it to the official `guix` packages, you'll still need to declare it locally in your file, for instance by wrapping your web application's package within a `let` block: ```guile (let ((SJW (load "path/to/where/you/cloned/this/repos/guix.scm"))) (package … )) ``` Then you can use absolutely any building software you like, and the `sjw` command will be available in the build's context (custom script, Makefile… `SJW`'s purpose is not to tell you how to build your web application). And since the `guix` package for `SJW` [handles the `$SJW_PATH` variable](https://guix.gnu.org/fr/manual/devel/en/html_node/Search-Paths.html#Search-Paths), you do not even have to worry about your libraries and can simply add them to the `native-inputs` for them to be available to the `-I` option. All a library has to do to for this to work is to have its `guix` package install its [name directory](#about-modules-and-paths) to the path `lib/SJW` in the resulting `guix` store directory. #### Trying it You may still want to interact directly with the `sjw` command on your favorite shell's command line. It's useful when you're still discovering `SJW` and want to play with it while writing the building rules of your web application (for instance, a `Makefile`). You can simply test `SJW` from this repository by issuing: ```sh guix shell -f guix.scm ``` ### How to install #### Putting `SJW` in your permanent `guix` profile If you want `SJW` installed "once and for all" to have it available whenever you open a new shell, you can use the regular `guix` commands, such as: ```sh guix install -f guix.scm ``` for instance, or to retain a more declarative approach, use the `load` function as above to make the package available within a [manifest](https://guix.gnu.org/fr/manual/devel/en/html_node/Writing-Manifests.html#Writing-Manifests) or even an [operating system declaration](https://guix.gnu.org/fr/manual/devel/en/html_node/operating_002dsystem-Reference.html#operating_002dsystem-Reference) if you need it installed system-wide for some reason. #### When `guix` is not an option SJW can still be compiled and installed as a regular haskell package with [`cabal`](https://www.haskell.org/cabal/): ``` $ cabal new-update $ cabal new-install SJW ``` As in that case `guix` won't be there to take care of your `SJW` libraries either, you'll have to handle them yourself. But since no special action is needed on them before compiling a web application, this requires only the most simple file-management tools available such as `ls`, `cp` or `rm` from your usual shell or even moving things around in your graphical file browser. Make sure you copy the name directory and not the modules directory to `~/.sjw` (or `$SJW_PACKAGE_DB`). If for instance the `myLib` package above was distributed in a repository with the following structure: ```sh ls path/to/myLib # prints: src/ README LICENSE CHANGELOG guix.scm ls path/to/myLib/src # prints: MyLib/ ``` Then in this case `myLib` would simply installed by doing: ```sh cp -R path/to/myLib/src ~/.sjw/myLib ``` ## Contributing Your contribution is welcome ! Since this is a private forge where you don't have an account, just send a git patch (see [`git format-patch`](https://git-scm.com/docs/git-format-patch)) to my email address (look at the author's address of the first commit in the history from your local clone of this repos).