Skip to content

Commit 31631cc

Browse files
authored
Merge pull request #150 from mlabs-haskell/jared/typescript-runtime
Typescript runtime
2 parents 506fe69 + 770759c commit 31631cc

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

73 files changed

+12721
-0
lines changed

extras/build.nix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
haskellData = import ./haskell-data.nix pkgs;
3838
haskellFlake = import ./flake-haskell.nix pkgs;
3939
haskellPlutusFlake = import ./flake-haskell-plutus.nix inputs.cardano-haskell-packages pkgs;
40+
typescriptFlake = import ./flake-typescript.nix pkgs;
4041
};
4142

4243
# Makes it available in the per system `lib` argument.

extras/flake-typescript.nix

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
pkgs:
2+
{ name
3+
, src
4+
, # `dependencies` is of type
5+
# ```
6+
# [ nix derivation for a tarball from `npm pack` ]
7+
# ```
8+
# for the the extra dependencies (not included in the `package.json`) for
9+
# `node` to execute. This will _not_ install the "transitive" dependencies.
10+
#
11+
# Loosely, this will `npm install` each of the tarballs in order so its
12+
# important that
13+
#
14+
# - The dependencies are sorted topologically i.e., each tarball should
15+
# _only_ depend on packages before it in the list.
16+
#
17+
# For example, if one wanted to include `typescript` as a dependency, then
18+
# one could have
19+
#
20+
# ```
21+
# let pkgs = import <nixpkgs> {}
22+
# dependencies = [
23+
# (pkgs.fetchurl {
24+
# url = "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz";
25+
# sha512 = "mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==";
26+
# })
27+
# ];
28+
# in ...
29+
# ```
30+
31+
dependencies ? [ ]
32+
33+
, nodejs ? pkgs.nodejs-18_x
34+
, # `devShellHook` is the shell commands to run _before_ entering the shell
35+
# (see the variable `shell`)
36+
devShellHook ? ""
37+
, # `devShellTools` are extra derivations to append to the `buildInputs` for
38+
# the shell (see the variable `shell`)
39+
devShellTools ? [ ]
40+
, # `testTools` are extra derivations to append to the `buildInputs` for
41+
# the tests (see the variable `test`)
42+
testTools ? [ ]
43+
, ...
44+
}:
45+
let
46+
# Derivation for the result of calling the CLI tool `node2nix` with the
47+
# provided `src`.
48+
#
49+
# Notes:
50+
#
51+
# - `node2nix` creates a nix expression in `default.nix` of type
52+
# `{pkgs, system, nodejs} -> {args, sources, tarball, package, shell, nodeDependencies }`
53+
node2nixExprs =
54+
{
55+
# Extra flags passed directly to `node2nix`
56+
extraFlags ? [ ]
57+
}:
58+
pkgs.runCommand (name + "-node2nix") { buildInputs = [ pkgs.node2nix nodejs ]; }
59+
''
60+
mkdir -p "$out"
61+
62+
cd "$out"
63+
64+
cp -r ${src}/* .
65+
66+
#########################################################
67+
# Verify that `package.json` and `package-lock.json` exist
68+
#########################################################
69+
if ! test -f package.json
70+
then { echo 'No `package.json` provided'; exit 1; }
71+
fi
72+
73+
if ! test -f package-lock.json
74+
then { echo 'No `package-lock.json` provided. Running `npm install --package-lock-only` may fix this'; exit 1; }
75+
fi
76+
77+
#########################################################
78+
# Use `npm` to add the extra tarball dependencies from nix.
79+
#########################################################
80+
81+
# Note `npm` needs to modify these files, so we change the permissions
82+
chmod +777 package.json
83+
chmod +777 package-lock.json
84+
85+
# `.nix-node-deps/` is the directory to save the tarball
86+
# dependencies from `nix`.
87+
mkdir .nix-node-deps/
88+
89+
${ if dependencies == [] then "" else
90+
''
91+
# We write the list of `dependencies` as
92+
# `<dependency1>`, `<dependency2>`, ... ,`<dependencyN>`
93+
94+
# Copying all `dependencies` into `.nix-nodes-deps/` i.e.,
95+
# we run:
96+
# ```
97+
# echo "Copying <dependency1>"
98+
# cp Copying <dependency1> .nix-node-deps/
99+
# echo "Copying <dependency2>"
100+
# cp Copying <dependency2> .nix-node-deps/
101+
# ...
102+
# echo "Copying <dependencyN>"
103+
# cp Copying <dependencyN> .nix-node-deps/
104+
# ```
105+
${builtins.concatStringsSep "\n" (builtins.map (pkgPath:
106+
''
107+
echo "Copying ${pkgPath}..."
108+
cp "${pkgPath}" .nix-node-deps/
109+
''
110+
) dependencies)}
111+
112+
# Run `npm install` with the previous dependencies i.e., we run
113+
# ```
114+
# npm install --save --package-lock-only <dependency1> <dependency2> ... <dependencyN>
115+
# ```
116+
echo 'Running `npm install`...'
117+
HOME=$TMPDIR npm install --save --package-lock-only ${builtins.concatStringsSep " " (builtins.map (pkgPath: ''".nix-node-deps/$(basename "${pkgPath}")"'') dependencies) }
118+
''
119+
}
120+
121+
#########################################################
122+
# Run `node2nix`
123+
#########################################################
124+
echo 'Running `node2nix`...'
125+
node2nix --input package.json --lock package-lock.json ${builtins.concatStringsSep " " extraFlags}
126+
127+
# Reset the permissions for `package.json` and `package-lock.json` to
128+
# read only for everyone.
129+
chmod =444 package.json
130+
chmod =444 package-lock.json
131+
'';
132+
133+
node2nixDevelop = node2nixExprs { extraFlags = [ "--development" ]; };
134+
node2nixDevelopAttrs = ((import node2nixDevelop) { inherit nodejs pkgs; inherit (pkgs) system; });
135+
136+
# Build the project (runs `npm run build`), and puts the entire output in the
137+
# nix store
138+
project = node2nixDevelopAttrs.package.override
139+
{
140+
postInstall =
141+
''
142+
npm run --loglevel=verbose build
143+
'';
144+
};
145+
146+
shell = node2nixDevelopAttrs.shell.override
147+
{
148+
shellHook =
149+
node2nixDevelopAttrs.shell.shellHook
150+
+
151+
''
152+
# Note: `node2nix` sets `$NODE_PATH` s.t. it is a single path.
153+
echo 'Creating a symbolic link from `$NODE_PATH` to `node_modules`...'
154+
ln -snf "$NODE_PATH" node_modules
155+
156+
# Run the provided `devShellHook`
157+
${devShellHook}
158+
'';
159+
buildInputs = node2nixDevelopAttrs.shell.buildInputs ++ devShellTools;
160+
};
161+
162+
# Creates a tarball of `project` using `npm pack` and puts it in the nix
163+
# store.
164+
npmPack = pkgs.stdenv.mkDerivation {
165+
buildInputs = [ nodejs ];
166+
name = "${node2nixDevelopAttrs.args.name}.tgz";
167+
168+
# A glance at the generated nix expressions in `node2nixDevelop` will
169+
# show why the following path is where the package really is.
170+
src = "${project}/lib/node_modules/${node2nixDevelopAttrs.args.packageName}";
171+
buildPhase = ''
172+
cp -r "$src" .
173+
174+
# Why do we set `HOME=$TMPDIR`? This is because apparently `npm` will
175+
# attempt to do something like `mkdir $HOME` for which `$HOME` is not
176+
# writable.. so we fix this by making `$HOME` a writable directory.
177+
tgzFile=$(HOME=$TMPDIR npm pack | tail -n 1)
178+
'';
179+
installPhase = ''
180+
mv "$tgzFile" "$out"
181+
'';
182+
183+
};
184+
185+
# Run tests with `npm test`.
186+
test = node2nixDevelopAttrs.package.override
187+
{
188+
postInstall =
189+
''
190+
npm --loglevel=verbose test
191+
rm -rf $out
192+
touch $out
193+
'';
194+
buildInputs = node2nixDevelopAttrs.package.buildInputs ++ testTools;
195+
};
196+
197+
in
198+
{
199+
devShells = {
200+
"${name}-typescript" = shell;
201+
};
202+
203+
packages = {
204+
"${name}-typescript" = project;
205+
"${name}-typescript-tgz" = npmPack;
206+
"${name}-typescript-node2nix" = node2nixDevelop;
207+
};
208+
209+
checks = {
210+
"${name}-typescript-test" = test;
211+
};
212+
}

extras/pre-commit-hooks-extra.nix

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
perSystem = { pkgs, ... }:
66
let
77
rustfmt = pkgs.rust-bin.stable.latest.rustfmt;
8+
deno = pkgs.deno;
89
in
910
{
1011
pre-commit.settings.hooks = {
@@ -15,6 +16,29 @@
1516
entry = "${rustfmt}/bin/rustfmt --color always";
1617
files = "\\.rs$";
1718
};
19+
20+
# TODO(jaredponn): Why do we use our strange version of `denofmt` and
21+
# `denolint`? The default implemented version in `pre-commit-hooks.nix`
22+
# is a bit buggy (see
23+
# https://github.com/cachix/pre-commit-hooks.nix/issues/374), and the
24+
# latest version of `deno` on nix doesn't allow explicitly applying
25+
# the formatter to specific files
26+
my-denofmt =
27+
{
28+
name = "denofmt";
29+
description = "Format Typescript code.";
30+
entry = "${deno}/bin/deno fmt";
31+
files = "(\\.ts$)|(^tsconfig?(-base)\\.json$)";
32+
};
33+
34+
my-denolint =
35+
{
36+
name = "denolint";
37+
description = "Lint Typescript code.";
38+
entry = "${deno}/bin/deno lint";
39+
files = "\\.ts$";
40+
};
41+
1842
};
1943
};
2044
}

flake.nix

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@
4343
./runtimes/purescript/lbr-plutus/build.nix
4444
./runtimes/rust/lbr-prelude/build.nix
4545
./runtimes/rust/lbr-prelude-derive/build.nix
46+
./runtimes/typescript/lbr-prelude/build.nix
47+
./runtimes/typescript/lbr-plutus/build.nix
4648
./testsuites/lbt-prelude/api/build.nix
4749
./testsuites/lbt-prelude/golden/build.nix
4850
./testsuites/lbt-prelude/lbt-prelude-haskell/build.nix

pre-commit.nix

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636
dhall-format.enable = true;
3737
purty.enable = true;
3838
rustfmt-monorepo.enable = true;
39+
my-denofmt.enable = true;
40+
my-denolint.enable = true;
3941

4042
} // (inputs.protobufs-nix.lib.${system}.preCommitHooks { inherit pkgs; });
4143

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{ ... }:
2+
{
3+
perSystem = { config, ... }:
4+
let
5+
typescriptFlake =
6+
config.lbf-nix.typescriptFlake {
7+
name = "lbr-plutus";
8+
src = ./.;
9+
dependencies = [ config.packages."lbr-prelude-typescript-tgz" ];
10+
11+
devShellTools = config.settings.shell.tools;
12+
devShellHook = config.settings.shell.hook;
13+
};
14+
in
15+
{
16+
17+
packages = {
18+
lbr-plutus-typescript = typescriptFlake.packages.lbr-plutus-typescript;
19+
lbr-plutus-typescript-tgz = typescriptFlake.packages.lbr-plutus-typescript-tgz;
20+
lbr-plutus-typescript-node2nix = typescriptFlake.packages.lbr-plutus-typescript-node2nix;
21+
};
22+
23+
devShells = {
24+
lbr-plutus-typescript = typescriptFlake.devShells.lbr-plutus-typescript;
25+
};
26+
27+
checks = {
28+
lbr-plutus-typescript-test = typescriptFlake.checks.lbr-plutus-typescript-test;
29+
};
30+
31+
# TODO(jaredponn): for some reason, writing the more terse `inherit`
32+
# seems to cause infinite recursion.
33+
# ```
34+
# inherit (typescriptFlake) packages checks devShells;
35+
# ```
36+
# So instead, we explicitly write out all the attribute names and what
37+
# they are assigned to.
38+
};
39+
}

0 commit comments

Comments
 (0)