Tissevert
d6553047d6
* Upgrade code to run with base 4.16 * Stop using cabal-generated module * Fix sjw not returning with exit code 1 on errors |
||
---|---|---|
benchmark | ||
demo | ||
src | ||
test | ||
.gitignore | ||
CHANGELOG.md | ||
guix.scm | ||
LICENSE | ||
README.md | ||
Setup.hs | ||
SJW.cabal |
SJW
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).
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
. 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
sjw demo/src/
By default it prints the resulting Javascript code on its standard output.
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.
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:
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:
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.
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.
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.
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.
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:
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,
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:
(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,
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 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:
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:
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
or even an operating system
declaration
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
:
$ 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:
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:
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
) to my email address
(look at the author's address of the first commit in the history from your
local clone of this repos).