2019-12-30 12:16:05 +01:00
|
|
|
# SJW
|
|
|
|
|
2022-08-09 19:27:45 +02:00
|
|
|
- [Usage](#usage)
|
|
|
|
- Install
|
|
|
|
+ [How *not* to install](#how-not-to-install)
|
|
|
|
+ [How to install](#how-to-install)
|
|
|
|
- [Contributing](#contributing)
|
2020-01-01 20:01:17 +01:00
|
|
|
|
2022-08-09 19:27:45 +02:00
|
|
|
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.
|
2020-01-01 20:01:17 +01:00
|
|
|
|
2022-08-09 19:27:45 +02:00
|
|
|
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)).
|
2020-01-01 20:01:17 +01:00
|
|
|
|
2022-08-09 19:27:45 +02:00
|
|
|
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/
|
2020-01-01 20:01:17 +01:00
|
|
|
```
|
|
|
|
|
2022-08-09 19:27:45 +02:00
|
|
|
By default it prints the resulting Javascript code on its standard output.
|
2020-01-01 20:01:17 +01:00
|
|
|
|
2022-08-09 19:27:45 +02:00
|
|
|
```javascript
|
|
|
|
window.addEventListener('load', function() {
|
|
|
|
…
|
|
|
|
});
|
|
|
|
```
|
2020-05-17 17:09:15 +02:00
|
|
|
|
2022-08-09 19:27:45 +02:00
|
|
|
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.
|
2020-01-01 20:01:17 +01:00
|
|
|
|
2022-08-09 19:27:45 +02:00
|
|
|
```sh
|
|
|
|
sjw demo/src/ -o main.js
|
|
|
|
```
|
2020-01-01 20:01:17 +01:00
|
|
|
|
2022-08-09 19:27:45 +02:00
|
|
|
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
|
2020-01-01 20:01:17 +01:00
|
|
|
```
|
2022-08-09 19:27:45 +02:00
|
|
|
|
|
|
|
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
|
2020-01-01 20:01:17 +01:00
|
|
|
```
|
|
|
|
|
2022-08-09 19:27:45 +02:00
|
|
|
### Libraries
|
2020-05-17 17:09:15 +02:00
|
|
|
|
2022-08-09 19:27:45 +02:00
|
|
|
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.
|
2020-05-17 17:09:15 +02:00
|
|
|
|
2022-08-09 19:27:45 +02:00
|
|
|
```sh
|
|
|
|
ls ~/.sjw
|
|
|
|
# prints: myLib/
|
|
|
|
sjw -I myLib src # will work
|
|
|
|
```
|
2020-05-17 17:09:15 +02:00
|
|
|
|
2022-08-09 19:27:45 +02:00
|
|
|
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.
|
2020-05-17 17:09:15 +02:00
|
|
|
|
2022-08-09 19:27:45 +02:00
|
|
|
```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
|
|
|
|
```
|
2020-05-17 17:09:15 +02:00
|
|
|
|
2022-08-09 19:27:45 +02:00
|
|
|
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
|
2020-05-17 17:09:15 +02:00
|
|
|
```
|
2022-08-09 19:27:45 +02:00
|
|
|
|
|
|
|
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;
|
2020-05-17 17:09:15 +02:00
|
|
|
```
|
|
|
|
|
2022-08-09 19:27:45 +02:00
|
|
|
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:
|
2020-05-17 17:09:15 +02:00
|
|
|
|
2022-08-09 19:27:45 +02:00
|
|
|
```sh
|
|
|
|
find ~/.sjw/myLib
|
|
|
|
# prints:
|
|
|
|
# ~/.sjw/myLib
|
|
|
|
# ~/.sjw/myLib/MyLib
|
|
|
|
# ~/.sjw/myLib/MyLib/Util.sh
|
2020-05-17 17:09:15 +02:00
|
|
|
```
|
2022-08-09 19:27:45 +02:00
|
|
|
|
|
|
|
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
|
|
|
|
…
|
|
|
|
))
|
2020-05-17 17:09:15 +02:00
|
|
|
```
|
|
|
|
|
2022-08-09 19:27:45 +02:00
|
|
|
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).
|
2020-05-17 17:09:15 +02:00
|
|
|
|
2022-08-09 19:27:45 +02:00
|
|
|
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
|
2020-05-17 17:09:15 +02:00
|
|
|
```
|
2022-08-09 19:27:45 +02:00
|
|
|
|
|
|
|
### 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
|
2020-05-17 17:09:15 +02:00
|
|
|
```
|
|
|
|
|
2022-08-09 19:27:45 +02:00
|
|
|
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/):
|
2020-05-17 17:09:15 +02:00
|
|
|
|
|
|
|
```
|
2022-08-09 19:27:45 +02:00
|
|
|
$ cabal new-update
|
|
|
|
$ cabal new-install SJW
|
2020-05-17 17:09:15 +02:00
|
|
|
```
|
|
|
|
|
2022-08-09 19:27:45 +02:00
|
|
|
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/
|
2020-05-17 17:09:15 +02:00
|
|
|
```
|
2022-08-09 19:27:45 +02:00
|
|
|
|
|
|
|
Then in this case `myLib` would simply installed by doing:
|
|
|
|
|
|
|
|
```sh
|
|
|
|
cp -R path/to/myLib/src ~/.sjw/myLib
|
2020-05-17 17:09:15 +02:00
|
|
|
```
|
2022-08-09 19:27:45 +02:00
|
|
|
|
|
|
|
## 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).
|