Remove website
The website now lives in a separate repo: https://github.com/tweag/nickel-lang.org.
40
website/.github/settings.yml
vendored
@ -1,40 +0,0 @@
|
|||||||
repository:
|
|
||||||
has_wiki: false
|
|
||||||
|
|
||||||
labels:
|
|
||||||
- name: "duplicate"
|
|
||||||
color: cfd3d7
|
|
||||||
- name: "good first issue"
|
|
||||||
color: 7057ff
|
|
||||||
- name: "invalid"
|
|
||||||
color: cfd3d7
|
|
||||||
- name: "more data needed"
|
|
||||||
color: bfdadc
|
|
||||||
- name: "P0"
|
|
||||||
color: b60205
|
|
||||||
description: "blocker: fix immediately!"
|
|
||||||
- name: "P1"
|
|
||||||
color: d93f0b
|
|
||||||
description: "critical: next release"
|
|
||||||
- name: "P2"
|
|
||||||
color: e99695
|
|
||||||
description: "major: an upcoming release"
|
|
||||||
- name: "P3"
|
|
||||||
color: fbca04
|
|
||||||
description: "minor: not priorized"
|
|
||||||
- name: "P4"
|
|
||||||
color: fef2c0
|
|
||||||
description: "unimportant: consider wontfix or other priority"
|
|
||||||
- name: "question"
|
|
||||||
color: d876e3
|
|
||||||
- name: "type: bug"
|
|
||||||
color: 0052cc
|
|
||||||
- name: "type: documentation"
|
|
||||||
color: 0052cc
|
|
||||||
- name: "type: feature request"
|
|
||||||
color: 0052cc
|
|
||||||
- name: "wontfix"
|
|
||||||
color: ffffff
|
|
||||||
- name: "merge-queue"
|
|
||||||
color: 0e8a16
|
|
||||||
description: "merge on green CI"
|
|
4
website/.gitignore
vendored
@ -1,4 +0,0 @@
|
|||||||
node_modules/
|
|
||||||
.cache/
|
|
||||||
public
|
|
||||||
.idea
|
|
@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) Tweag Holding and its affiliates.
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
@ -1,4 +0,0 @@
|
|||||||
# Nickel-lang.org website
|
|
||||||
|
|
||||||
This repository contains the content of the Nickel language website. This is
|
|
||||||
WIP.
|
|
@ -1,5 +0,0 @@
|
|||||||
import "./src/styles/custom.scss";
|
|
||||||
import "jquery/dist/jquery.min.js";
|
|
||||||
import "popper.js/dist/popper.min";
|
|
||||||
import "bootstrap/dist/js/bootstrap.min.js";
|
|
||||||
import "./src/styles/global.css";
|
|
@ -1,35 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
siteMetadata: {
|
|
||||||
title: "Nickel",
|
|
||||||
menuLinks: [
|
|
||||||
{
|
|
||||||
name: 'Getting started',
|
|
||||||
link: '/getting-started'
|
|
||||||
},
|
|
||||||
// Disabling the documentation page for now.
|
|
||||||
// There is just not enough interesting content to show.
|
|
||||||
/*{
|
|
||||||
name: 'Documentation',
|
|
||||||
link: '/documentation'
|
|
||||||
},*/
|
|
||||||
{
|
|
||||||
name: 'Playground',
|
|
||||||
link: '/playground'
|
|
||||||
},
|
|
||||||
]
|
|
||||||
},
|
|
||||||
plugins: [
|
|
||||||
'gatsby-plugin-react-helmet',
|
|
||||||
'gatsby-plugin-sharp',
|
|
||||||
'gatsby-transformer-sharp',
|
|
||||||
'gatsby-plugin-image',
|
|
||||||
{
|
|
||||||
resolve: `gatsby-source-filesystem`,
|
|
||||||
options: {
|
|
||||||
name: `markdown-pages`,
|
|
||||||
path: `${__dirname}/src/markdown-pages`,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
`gatsby-plugin-sass`,
|
|
||||||
],
|
|
||||||
};
|
|
@ -1,8 +0,0 @@
|
|||||||
exports.onCreateWebpackConfig = ({ _stage, actions, _loaders }) => {
|
|
||||||
actions.setWebpackConfig({
|
|
||||||
experiments: {
|
|
||||||
// This was necessary to have the Nickel WASM REPL work with Webpack
|
|
||||||
asyncWebAssembly: true,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
};
|
|
@ -1,29 +0,0 @@
|
|||||||
[[redirects]]
|
|
||||||
from = "https://nickel-lang.netlify.com/*"
|
|
||||||
to = "https://nickel-lang.org/:splat"
|
|
||||||
status = 301
|
|
||||||
force = true
|
|
||||||
|
|
||||||
[[redirects]]
|
|
||||||
from = "http://nickel-lang.netlify.com/*"
|
|
||||||
to = "https://nickel-lang.org/:splat"
|
|
||||||
status = 301
|
|
||||||
force = true
|
|
||||||
|
|
||||||
[[redirects]]
|
|
||||||
from = "http://www.nickel-lang.org/*"
|
|
||||||
to = "https://nickel-lang.org/:splat"
|
|
||||||
status = 301
|
|
||||||
force = true
|
|
||||||
|
|
||||||
[[redirects]]
|
|
||||||
from = "https://www.nickel-lang.org/*"
|
|
||||||
to = "https://nickel-lang.org/:splat"
|
|
||||||
status = 301
|
|
||||||
force = true
|
|
||||||
|
|
||||||
[[redirects]]
|
|
||||||
from = "http://nickel-lang.org/*"
|
|
||||||
to = "https://nickel-lang.org/:splat"
|
|
||||||
status = 301
|
|
||||||
force = true
|
|
@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) Tweag Holding and its affiliates.
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
@ -1,217 +0,0 @@
|
|||||||
# Nickel
|
|
||||||
|
|
||||||
[![Continuous integration](https://github.com/tweag/nickel/workflows/Continuous%20integration/badge.svg)](https://github.com/tweag/nickel/actions?query=branch%3Amaster)
|
|
||||||
|
|
||||||
Nickel is the cheap configuration language.
|
|
||||||
|
|
||||||
Its purpose is to automate the generation of static configuration files - think
|
|
||||||
JSON, YAML, XML, or your favorite data representation language - that are then
|
|
||||||
fed to another system. It is designed to have a simple, well-understood core: it
|
|
||||||
is in essence JSON with functions.
|
|
||||||
|
|
||||||
Nickel's salient traits are:
|
|
||||||
|
|
||||||
- **Lightweight**: Nickel is easy to embed. An interpreter should be simple to
|
|
||||||
implement. The reference interpreter can be called from many programming
|
|
||||||
languages.
|
|
||||||
- **Composable code**: the basic building blocks for computing are functions.
|
|
||||||
They are first-class citizens, which can be passed around, called and
|
|
||||||
composed.
|
|
||||||
- **Composable data**: the basic building blocks for data are records
|
|
||||||
(called *objects* in JSON). In Nickel, records can be merged at will,
|
|
||||||
including associated metadata (documentation, default values, type
|
|
||||||
contracts, etc).
|
|
||||||
- **Typed, but only when it helps**: static types improve code quality, serve as
|
|
||||||
documentation and eliminate bugs early. But application-specific
|
|
||||||
self-contained code will always evaluate to the same value, so type errors
|
|
||||||
will show up at runtime anyway. Some JSON is hard to type. There, types are
|
|
||||||
only a burden. Whereas reusable code - that is, *functions* - is evaluated
|
|
||||||
on potentially infinitely many different inputs, and is impossible to test
|
|
||||||
exhaustively. There, types are precious. Nickel has types, but you get to
|
|
||||||
choose when you want it or not, and it handles safely the interaction between
|
|
||||||
the typed and the untyped world.
|
|
||||||
- **Design by contract**: complementary to the type system, contracts are
|
|
||||||
a principled approach to checking assertions. The interpreter automatically
|
|
||||||
inserts assertions at the boundary between typed and untyped code. Nickel
|
|
||||||
lets users add arbitrary assertions of their own and easily understand why
|
|
||||||
when assertions fail.
|
|
||||||
|
|
||||||
The motto guiding Nickel's design is:
|
|
||||||
> Great defaults, design for extensibility
|
|
||||||
|
|
||||||
There should be a standard, clear path for common things. There should be no
|
|
||||||
arbitrary restrictions that limit what you can do you the one day you need to go
|
|
||||||
beyond.
|
|
||||||
|
|
||||||
## Use cases
|
|
||||||
|
|
||||||
Nickel is a good fit in any situation where you need to generate a complex
|
|
||||||
configuration, be it for a single app, a machine, whole infrastructure, or a
|
|
||||||
build system.
|
|
||||||
|
|
||||||
The motivating use cases are in particular:
|
|
||||||
- The [Nix package manager](https://nixos.org/): Nix is a declarative package
|
|
||||||
manager using its own language for specifying packages. Nickel is an
|
|
||||||
evolution of the Nix language, while trying to overcome some of its
|
|
||||||
limitations.
|
|
||||||
- Infrastructure as code: infrastructure is becoming increasingly complex,
|
|
||||||
requiring a rigorous approach to deployment, modification and configuration.
|
|
||||||
This is where a declarative approach also shines, as adopted by
|
|
||||||
[Terraform](https://www.terraform.io/),
|
|
||||||
[NixOps](https://github.com/NixOS/nixops) or
|
|
||||||
[Kubernetes](https://kubernetes.io/), all requiring potentially complex
|
|
||||||
generation of configuration.
|
|
||||||
- Build systems: build systems (like [Bazel](https://bazel.build/)) need
|
|
||||||
a specification of the dependency graph.
|
|
||||||
|
|
||||||
Most aforementioned projects have their own bespoke configuration language. See
|
|
||||||
[Related projects and inspirations](#Related-projects-and-inspirations). In
|
|
||||||
general, application-specific languages might suffer from feature creep, lack of
|
|
||||||
abstractions or just feel ad hoc. Nickel buys you more for less.
|
|
||||||
|
|
||||||
## Getting started
|
|
||||||
|
|
||||||
### Run
|
|
||||||
|
|
||||||
1. Start Nickel
|
|
||||||
* with [flake-enabled](https://nixos.wiki/wiki/Flakes) Nix directly
|
|
||||||
with `nix run nickel` (which pulls it from the global flakes
|
|
||||||
registry), or with `nix run github:tweag/nickel` (which pulls it
|
|
||||||
from the repo). You can use [our binary cache](https://nickel.cachix.org) to
|
|
||||||
prevent rebuilding a lot of packages. You pass in arguments with
|
|
||||||
an extra `--` as in `nix run nickel -- repl`,
|
|
||||||
* with `./nickel`, after [building](#Build) this repo, depending on the
|
|
||||||
location of the executable and passing in arguments directly,
|
|
||||||
* or with `cargo run` after [building](#Build), passing in arguments with
|
|
||||||
an extra `--` as in `cargo run -- -f program.ncl`.
|
|
||||||
|
|
||||||
2. Run your first program:
|
|
||||||
```console
|
|
||||||
$ ./nickel <<< 'let x = 2 in x + x'
|
|
||||||
Typechecked: Ok(Types(Dyn))
|
|
||||||
Done: Num(4.0)
|
|
||||||
```
|
|
||||||
Or load it from a file:
|
|
||||||
```console
|
|
||||||
$ echo 'let s = "world" in "Hello, " ++ s' > program.ncl
|
|
||||||
$ ./nickel -f program.ncl
|
|
||||||
Typechecked: Ok(Types(Dyn))
|
|
||||||
Done: Str("Hello, world")
|
|
||||||
```
|
|
||||||
3. Start a REPL:
|
|
||||||
```console
|
|
||||||
$ ./nickel repl
|
|
||||||
nickel> let x = 2 in x + x
|
|
||||||
4
|
|
||||||
|
|
||||||
nickel>
|
|
||||||
```
|
|
||||||
Use `:help` for a list of available commands.
|
|
||||||
|
|
||||||
4. Export your configuration to JSON, YAML or TOML:
|
|
||||||
```console
|
|
||||||
$ ./nickel export --format json <<< '{foo = "Hello, world!"}'
|
|
||||||
{
|
|
||||||
"foo": "Hello, world!"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Use `nickel help` for a list of subcommands, and `nickel help <subcommand>`
|
|
||||||
for help about a specific subcommand.
|
|
||||||
|
|
||||||
### Build
|
|
||||||
|
|
||||||
[rust-guide]: https://doc.rust-lang.org/cargo/getting-started/installation.html
|
|
||||||
|
|
||||||
1. Download build dependencies:
|
|
||||||
- **With Nix**: If you have [Nix](https://nixos.org/nix) installed:
|
|
||||||
```console
|
|
||||||
$ nix-shell shell.nix
|
|
||||||
```
|
|
||||||
to be dropped in a shell, ready to build. You can use [our binary
|
|
||||||
cache](https://nickel.cachix.org) to prevent rebuilding a lot of
|
|
||||||
packages.
|
|
||||||
- **Without Nix**: otherwise, follow [this guide][rust-guide] to install Rust
|
|
||||||
and Cargo first.
|
|
||||||
2. Build Nickel:
|
|
||||||
```console
|
|
||||||
$ cargo build
|
|
||||||
```
|
|
||||||
And voilà! Generated files are placed in `target/debug`.
|
|
||||||
|
|
||||||
|
|
||||||
1. *(optional)* make a symbolic link to the executable:
|
|
||||||
```console
|
|
||||||
$ ln -S nickel target/debug/nickel
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
### Tests
|
|
||||||
|
|
||||||
```console
|
|
||||||
$ cargo test
|
|
||||||
```
|
|
||||||
|
|
||||||
### Documentation
|
|
||||||
|
|
||||||
1. Build the doc:
|
|
||||||
```console
|
|
||||||
$ cargo doc --no-deps
|
|
||||||
```
|
|
||||||
2. Open the file `target/doc/nickel/index.html` in your browser.
|
|
||||||
|
|
||||||
### Examples
|
|
||||||
|
|
||||||
You can find examples in
|
|
||||||
the [`./examples`](./examples) directory. Note
|
|
||||||
that as the syntax is not yet fixed, and some basic helpers are missing, they
|
|
||||||
may seem a bit alien currently.
|
|
||||||
|
|
||||||
## Roadmap
|
|
||||||
|
|
||||||
The design is settled and implemented for the most part, but the final syntax
|
|
||||||
and other important practical aspects are still being debated. We aim to
|
|
||||||
transition from an experimental stage to a minimum viable product stage. The
|
|
||||||
next points to deal with are:
|
|
||||||
|
|
||||||
- [Stdlib stabilization](https://github.com/tweag/nickel/issues/321)
|
|
||||||
- [Overriding](https://github.com/tweag/nickel/pull/330)
|
|
||||||
- Memory management (use reference counting) & basic performance improvements
|
|
||||||
- [List comprehensions](https://github.com/tweag/nickel/issues/80)
|
|
||||||
- [Destructuring](https://github.com/tweag/nickel/issues/81)
|
|
||||||
|
|
||||||
## Related projects and inspirations
|
|
||||||
|
|
||||||
- [Cue](https://cuelang.org/) is a configuration language with a focus on data
|
|
||||||
validation. It introduces a new constraint system backed by a solid theory
|
|
||||||
which ensures strong guarantees about your code. It allows for very elegant
|
|
||||||
schema specifications. In return, the cost to pay is to abandon functions
|
|
||||||
and
|
|
||||||
[Turing-completeness](https://en.wikipedia.org/wiki/Turing_completeness).
|
|
||||||
Nickel's merge system is inspired by the one of CUE, even if since Nickel
|
|
||||||
does have general functions and is Turing-complete, they are necessarily
|
|
||||||
different.
|
|
||||||
- [Nix](https://nixos.org/): The Nix language, or *Nix expressions*, is one of
|
|
||||||
the main inspirations for Nickel. It is a very simple yet powerful lazy
|
|
||||||
functional language. We strive to retain this simplicity, while adding
|
|
||||||
typing capabilities, modularity, and detaching the language from the Nix
|
|
||||||
package manager.
|
|
||||||
- [Dhall](https://dhall-lang.org/) is a statically typed configuration language.
|
|
||||||
It is also inspired by Nix, to which it adds a powerful static type system.
|
|
||||||
However, this forces the programmer to annotate all of their code with types.
|
|
||||||
- [Jsonnet](https://jsonnet.org/) is another language which could be dubbed as
|
|
||||||
"JSON with functions" (and others things as well). It is a lazy functional
|
|
||||||
language with object oriented features, among which inheritance is similar
|
|
||||||
to Nickel's merge system. One big difference with Nickel is the absence of
|
|
||||||
typing.
|
|
||||||
- [Pulumi](https://www.pulumi.com/) is not a language in itself, but a cloud
|
|
||||||
tool (like Terraform) where you can use your preferred language for
|
|
||||||
describing your infrastructure. This is a different approach to the problem,
|
|
||||||
with different trade-offs.
|
|
||||||
- [Starlark](https://docs.bazel.build/versions/master/skylark/language.html) is
|
|
||||||
the language of [Bazel](https://bazel.build/), which is a dialect of
|
|
||||||
[Python](https://www.python.org/). It does not have types and recursion is
|
|
||||||
forbidden, making it not Turing-complete.
|
|
||||||
|
|
||||||
See [RATIONALE.md](./RATIONALE.md) for the design rationale and a more detailed
|
|
||||||
comparison with a selection of these languages.
|
|
77
website/nickel-repl/nickel.d.ts
vendored
@ -1,77 +0,0 @@
|
|||||||
/* tslint:disable */
|
|
||||||
/* eslint-disable */
|
|
||||||
/**
|
|
||||||
* Return a new instance of the WASM REPL, with the standard library loaded.
|
|
||||||
* @returns {WasmInitResult}
|
|
||||||
*/
|
|
||||||
export function repl_init(): WasmInitResult;
|
|
||||||
/**
|
|
||||||
* Evaluate an input in the WASM REPL.
|
|
||||||
* @param {ReplState} state
|
|
||||||
* @param {string} line
|
|
||||||
* @returns {WasmInputResult}
|
|
||||||
*/
|
|
||||||
export function repl_input(state: ReplState, line: string): WasmInputResult;
|
|
||||||
/**
|
|
||||||
* Evaluate an input in the WASM REPL and serialize it.
|
|
||||||
* @param {ReplState} state
|
|
||||||
* @param {any} format
|
|
||||||
* @param {string} line
|
|
||||||
* @returns {WasmInputResult}
|
|
||||||
*/
|
|
||||||
export function repl_serialize(state: ReplState, format: any, line: string): WasmInputResult;
|
|
||||||
/**
|
|
||||||
* Return codes of the WASM REPL.
|
|
||||||
*
|
|
||||||
* wasm-bindgen doesn't support exporting arbitrary enumeration. Thus we have to encode these
|
|
||||||
* enums as structures with a tag and values. The values that are actually set depend on the
|
|
||||||
* tag.
|
|
||||||
*/
|
|
||||||
export enum WasmResultTag {
|
|
||||||
Success,
|
|
||||||
Blank,
|
|
||||||
Partial,
|
|
||||||
Error,
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* WASM-compatible wrapper around `ReplImpl`.
|
|
||||||
*/
|
|
||||||
export class ReplState {
|
|
||||||
free(): void;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* WASM wrapper for the result type of the initialization of the REPL.
|
|
||||||
*/
|
|
||||||
export class WasmInitResult {
|
|
||||||
free(): void;
|
|
||||||
/**
|
|
||||||
* @returns {ReplState}
|
|
||||||
*/
|
|
||||||
repl(): ReplState;
|
|
||||||
/**
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
readonly msg: string;
|
|
||||||
/**
|
|
||||||
* @returns {number}
|
|
||||||
*/
|
|
||||||
tag: number;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* WASM wrapper for the result type of an execution of the REPL.
|
|
||||||
*/
|
|
||||||
export class WasmInputResult {
|
|
||||||
free(): void;
|
|
||||||
/**
|
|
||||||
* @returns {any}
|
|
||||||
*/
|
|
||||||
readonly errors: any;
|
|
||||||
/**
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
readonly msg: string;
|
|
||||||
/**
|
|
||||||
* @returns {number}
|
|
||||||
*/
|
|
||||||
tag: number;
|
|
||||||
}
|
|
@ -1,2 +0,0 @@
|
|||||||
import * as wasm from "./nickel_bg.wasm";
|
|
||||||
export * from "./nickel_bg.js";
|
|
@ -1,340 +0,0 @@
|
|||||||
import * as wasm from './nickel_bg.wasm';
|
|
||||||
|
|
||||||
const lTextDecoder = typeof TextDecoder === 'undefined' ? (0, module.require)('util').TextDecoder : TextDecoder;
|
|
||||||
|
|
||||||
let cachedTextDecoder = new lTextDecoder('utf-8', { ignoreBOM: true, fatal: true });
|
|
||||||
|
|
||||||
cachedTextDecoder.decode();
|
|
||||||
|
|
||||||
let cachegetUint8Memory0 = null;
|
|
||||||
function getUint8Memory0() {
|
|
||||||
if (cachegetUint8Memory0 === null || cachegetUint8Memory0.buffer !== wasm.memory.buffer) {
|
|
||||||
cachegetUint8Memory0 = new Uint8Array(wasm.memory.buffer);
|
|
||||||
}
|
|
||||||
return cachegetUint8Memory0;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getStringFromWasm0(ptr, len) {
|
|
||||||
return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len));
|
|
||||||
}
|
|
||||||
|
|
||||||
const heap = new Array(32).fill(undefined);
|
|
||||||
|
|
||||||
heap.push(undefined, null, true, false);
|
|
||||||
|
|
||||||
let heap_next = heap.length;
|
|
||||||
|
|
||||||
function addHeapObject(obj) {
|
|
||||||
if (heap_next === heap.length) heap.push(heap.length + 1);
|
|
||||||
const idx = heap_next;
|
|
||||||
heap_next = heap[idx];
|
|
||||||
|
|
||||||
heap[idx] = obj;
|
|
||||||
return idx;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getObject(idx) { return heap[idx]; }
|
|
||||||
|
|
||||||
function dropObject(idx) {
|
|
||||||
if (idx < 36) return;
|
|
||||||
heap[idx] = heap_next;
|
|
||||||
heap_next = idx;
|
|
||||||
}
|
|
||||||
|
|
||||||
function takeObject(idx) {
|
|
||||||
const ret = getObject(idx);
|
|
||||||
dropObject(idx);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
let WASM_VECTOR_LEN = 0;
|
|
||||||
|
|
||||||
const lTextEncoder = typeof TextEncoder === 'undefined' ? (0, module.require)('util').TextEncoder : TextEncoder;
|
|
||||||
|
|
||||||
let cachedTextEncoder = new lTextEncoder('utf-8');
|
|
||||||
|
|
||||||
const encodeString = (typeof cachedTextEncoder.encodeInto === 'function'
|
|
||||||
? function (arg, view) {
|
|
||||||
return cachedTextEncoder.encodeInto(arg, view);
|
|
||||||
}
|
|
||||||
: function (arg, view) {
|
|
||||||
const buf = cachedTextEncoder.encode(arg);
|
|
||||||
view.set(buf);
|
|
||||||
return {
|
|
||||||
read: arg.length,
|
|
||||||
written: buf.length
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
function passStringToWasm0(arg, malloc, realloc) {
|
|
||||||
|
|
||||||
if (realloc === undefined) {
|
|
||||||
const buf = cachedTextEncoder.encode(arg);
|
|
||||||
const ptr = malloc(buf.length);
|
|
||||||
getUint8Memory0().subarray(ptr, ptr + buf.length).set(buf);
|
|
||||||
WASM_VECTOR_LEN = buf.length;
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
let len = arg.length;
|
|
||||||
let ptr = malloc(len);
|
|
||||||
|
|
||||||
const mem = getUint8Memory0();
|
|
||||||
|
|
||||||
let offset = 0;
|
|
||||||
|
|
||||||
for (; offset < len; offset++) {
|
|
||||||
const code = arg.charCodeAt(offset);
|
|
||||||
if (code > 0x7F) break;
|
|
||||||
mem[ptr + offset] = code;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (offset !== len) {
|
|
||||||
if (offset !== 0) {
|
|
||||||
arg = arg.slice(offset);
|
|
||||||
}
|
|
||||||
ptr = realloc(ptr, len, len = offset + arg.length * 3);
|
|
||||||
const view = getUint8Memory0().subarray(ptr + offset, ptr + len);
|
|
||||||
const ret = encodeString(arg, view);
|
|
||||||
|
|
||||||
offset += ret.written;
|
|
||||||
}
|
|
||||||
|
|
||||||
WASM_VECTOR_LEN = offset;
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isLikeNone(x) {
|
|
||||||
return x === undefined || x === null;
|
|
||||||
}
|
|
||||||
|
|
||||||
let cachegetInt32Memory0 = null;
|
|
||||||
function getInt32Memory0() {
|
|
||||||
if (cachegetInt32Memory0 === null || cachegetInt32Memory0.buffer !== wasm.memory.buffer) {
|
|
||||||
cachegetInt32Memory0 = new Int32Array(wasm.memory.buffer);
|
|
||||||
}
|
|
||||||
return cachegetInt32Memory0;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Return a new instance of the WASM REPL, with the standard library loaded.
|
|
||||||
* @returns {WasmInitResult}
|
|
||||||
*/
|
|
||||||
export function repl_init() {
|
|
||||||
var ret = wasm.repl_init();
|
|
||||||
return WasmInitResult.__wrap(ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
function _assertClass(instance, klass) {
|
|
||||||
if (!(instance instanceof klass)) {
|
|
||||||
throw new Error(`expected instance of ${klass.name}`);
|
|
||||||
}
|
|
||||||
return instance.ptr;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Evaluate an input in the WASM REPL.
|
|
||||||
* @param {ReplState} state
|
|
||||||
* @param {string} line
|
|
||||||
* @returns {WasmInputResult}
|
|
||||||
*/
|
|
||||||
export function repl_input(state, line) {
|
|
||||||
_assertClass(state, ReplState);
|
|
||||||
var ptr0 = passStringToWasm0(line, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
||||||
var len0 = WASM_VECTOR_LEN;
|
|
||||||
var ret = wasm.repl_input(state.ptr, ptr0, len0);
|
|
||||||
return WasmInputResult.__wrap(ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Evaluate an input in the WASM REPL and serialize it.
|
|
||||||
* @param {ReplState} state
|
|
||||||
* @param {any} format
|
|
||||||
* @param {string} line
|
|
||||||
* @returns {WasmInputResult}
|
|
||||||
*/
|
|
||||||
export function repl_serialize(state, format, line) {
|
|
||||||
_assertClass(state, ReplState);
|
|
||||||
var ptr0 = passStringToWasm0(line, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
||||||
var len0 = WASM_VECTOR_LEN;
|
|
||||||
var ret = wasm.repl_serialize(state.ptr, addHeapObject(format), ptr0, len0);
|
|
||||||
return WasmInputResult.__wrap(ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return codes of the WASM REPL.
|
|
||||||
*
|
|
||||||
* wasm-bindgen doesn't support exporting arbitrary enumeration. Thus we have to encode these
|
|
||||||
* enums as structures with a tag and values. The values that are actually set depend on the
|
|
||||||
* tag.
|
|
||||||
*/
|
|
||||||
export const WasmResultTag = Object.freeze({ Success:0,"0":"Success",Blank:1,"1":"Blank",Partial:2,"2":"Partial",Error:3,"3":"Error", });
|
|
||||||
/**
|
|
||||||
* WASM-compatible wrapper around `ReplImpl`.
|
|
||||||
*/
|
|
||||||
export class ReplState {
|
|
||||||
|
|
||||||
static __wrap(ptr) {
|
|
||||||
const obj = Object.create(ReplState.prototype);
|
|
||||||
obj.ptr = ptr;
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
__destroy_into_raw() {
|
|
||||||
const ptr = this.ptr;
|
|
||||||
this.ptr = 0;
|
|
||||||
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
free() {
|
|
||||||
const ptr = this.__destroy_into_raw();
|
|
||||||
wasm.__wbg_replstate_free(ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* WASM wrapper for the result type of the initialization of the REPL.
|
|
||||||
*/
|
|
||||||
export class WasmInitResult {
|
|
||||||
|
|
||||||
static __wrap(ptr) {
|
|
||||||
const obj = Object.create(WasmInitResult.prototype);
|
|
||||||
obj.ptr = ptr;
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
__destroy_into_raw() {
|
|
||||||
const ptr = this.ptr;
|
|
||||||
this.ptr = 0;
|
|
||||||
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
free() {
|
|
||||||
const ptr = this.__destroy_into_raw();
|
|
||||||
wasm.__wbg_wasminitresult_free(ptr);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @returns {number}
|
|
||||||
*/
|
|
||||||
get tag() {
|
|
||||||
var ret = wasm.__wbg_get_wasminitresult_tag(this.ptr);
|
|
||||||
return ret >>> 0;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @param {number} arg0
|
|
||||||
*/
|
|
||||||
set tag(arg0) {
|
|
||||||
wasm.__wbg_set_wasminitresult_tag(this.ptr, arg0);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
get msg() {
|
|
||||||
try {
|
|
||||||
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
|
||||||
wasm.wasminitresult_msg(retptr, this.ptr);
|
|
||||||
var r0 = getInt32Memory0()[retptr / 4 + 0];
|
|
||||||
var r1 = getInt32Memory0()[retptr / 4 + 1];
|
|
||||||
return getStringFromWasm0(r0, r1);
|
|
||||||
} finally {
|
|
||||||
wasm.__wbindgen_add_to_stack_pointer(16);
|
|
||||||
wasm.__wbindgen_free(r0, r1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @returns {ReplState}
|
|
||||||
*/
|
|
||||||
repl() {
|
|
||||||
const ptr = this.__destroy_into_raw();
|
|
||||||
var ret = wasm.wasminitresult_repl(ptr);
|
|
||||||
return ReplState.__wrap(ret);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* WASM wrapper for the result type of an execution of the REPL.
|
|
||||||
*/
|
|
||||||
export class WasmInputResult {
|
|
||||||
|
|
||||||
static __wrap(ptr) {
|
|
||||||
const obj = Object.create(WasmInputResult.prototype);
|
|
||||||
obj.ptr = ptr;
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
__destroy_into_raw() {
|
|
||||||
const ptr = this.ptr;
|
|
||||||
this.ptr = 0;
|
|
||||||
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
free() {
|
|
||||||
const ptr = this.__destroy_into_raw();
|
|
||||||
wasm.__wbg_wasminputresult_free(ptr);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @returns {number}
|
|
||||||
*/
|
|
||||||
get tag() {
|
|
||||||
var ret = wasm.__wbg_get_wasminputresult_tag(this.ptr);
|
|
||||||
return ret >>> 0;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @param {number} arg0
|
|
||||||
*/
|
|
||||||
set tag(arg0) {
|
|
||||||
wasm.__wbg_set_wasminputresult_tag(this.ptr, arg0);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
get msg() {
|
|
||||||
try {
|
|
||||||
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
|
||||||
wasm.wasminputresult_msg(retptr, this.ptr);
|
|
||||||
var r0 = getInt32Memory0()[retptr / 4 + 0];
|
|
||||||
var r1 = getInt32Memory0()[retptr / 4 + 1];
|
|
||||||
return getStringFromWasm0(r0, r1);
|
|
||||||
} finally {
|
|
||||||
wasm.__wbindgen_add_to_stack_pointer(16);
|
|
||||||
wasm.__wbindgen_free(r0, r1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @returns {any}
|
|
||||||
*/
|
|
||||||
get errors() {
|
|
||||||
var ret = wasm.wasminputresult_errors(this.ptr);
|
|
||||||
return takeObject(ret);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function __wbindgen_json_parse(arg0, arg1) {
|
|
||||||
var ret = JSON.parse(getStringFromWasm0(arg0, arg1));
|
|
||||||
return addHeapObject(ret);
|
|
||||||
};
|
|
||||||
|
|
||||||
export function __wbindgen_object_drop_ref(arg0) {
|
|
||||||
takeObject(arg0);
|
|
||||||
};
|
|
||||||
|
|
||||||
export function __wbindgen_object_clone_ref(arg0) {
|
|
||||||
var ret = getObject(arg0);
|
|
||||||
return addHeapObject(ret);
|
|
||||||
};
|
|
||||||
|
|
||||||
export function __wbindgen_string_get(arg0, arg1) {
|
|
||||||
const obj = getObject(arg1);
|
|
||||||
var ret = typeof(obj) === 'string' ? obj : undefined;
|
|
||||||
var ptr0 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
||||||
var len0 = WASM_VECTOR_LEN;
|
|
||||||
getInt32Memory0()[arg0 / 4 + 1] = len0;
|
|
||||||
getInt32Memory0()[arg0 / 4 + 0] = ptr0;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function __wbindgen_throw(arg0, arg1) {
|
|
||||||
throw new Error(getStringFromWasm0(arg0, arg1));
|
|
||||||
};
|
|
||||||
|
|
21
website/nickel-repl/nickel_bg.wasm.d.ts
vendored
@ -1,21 +0,0 @@
|
|||||||
/* tslint:disable */
|
|
||||||
/* eslint-disable */
|
|
||||||
export const memory: WebAssembly.Memory;
|
|
||||||
export function __wbg_wasminitresult_free(a: number): void;
|
|
||||||
export function __wbg_get_wasminitresult_tag(a: number): number;
|
|
||||||
export function __wbg_set_wasminitresult_tag(a: number, b: number): void;
|
|
||||||
export function wasminitresult_msg(a: number, b: number): void;
|
|
||||||
export function wasminitresult_repl(a: number): number;
|
|
||||||
export function __wbg_wasminputresult_free(a: number): void;
|
|
||||||
export function __wbg_get_wasminputresult_tag(a: number): number;
|
|
||||||
export function __wbg_set_wasminputresult_tag(a: number, b: number): void;
|
|
||||||
export function wasminputresult_msg(a: number, b: number): void;
|
|
||||||
export function wasminputresult_errors(a: number): number;
|
|
||||||
export function __wbg_replstate_free(a: number): void;
|
|
||||||
export function repl_init(): number;
|
|
||||||
export function repl_input(a: number, b: number, c: number): number;
|
|
||||||
export function repl_serialize(a: number, b: number, c: number, d: number): number;
|
|
||||||
export function __wbindgen_malloc(a: number): number;
|
|
||||||
export function __wbindgen_realloc(a: number, b: number, c: number): number;
|
|
||||||
export function __wbindgen_add_to_stack_pointer(a: number): number;
|
|
||||||
export function __wbindgen_free(a: number, b: number): void;
|
|
@ -1,17 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "nickel-repl",
|
|
||||||
"collaborators": [
|
|
||||||
"Nicl team"
|
|
||||||
],
|
|
||||||
"description": "Programmable configuration files.",
|
|
||||||
"version": "0.1.0",
|
|
||||||
"license": "MIT OR Apache-2.0",
|
|
||||||
"files": [
|
|
||||||
"nickel_bg.wasm",
|
|
||||||
"nickel.js",
|
|
||||||
"nickel.d.ts"
|
|
||||||
],
|
|
||||||
"module": "nickel.js",
|
|
||||||
"types": "nickel.d.ts",
|
|
||||||
"sideEffects": false
|
|
||||||
}
|
|
47479
website/package-lock.json
generated
@ -1,48 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "nickel-lang.org",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"private": true,
|
|
||||||
"description": "nickel-lang.org",
|
|
||||||
"author": "Yann Hamdaoui",
|
|
||||||
"keywords": [
|
|
||||||
"gatsby"
|
|
||||||
],
|
|
||||||
"scripts": {
|
|
||||||
"develop": "gatsby develop",
|
|
||||||
"start": "gatsby develop",
|
|
||||||
"build": "gatsby build",
|
|
||||||
"serve": "gatsby serve",
|
|
||||||
"clean": "gatsby clean"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@fortawesome/fontawesome-svg-core": "^1.2.35",
|
|
||||||
"@fortawesome/free-brands-svg-icons": "^5.15.3",
|
|
||||||
"@fortawesome/free-solid-svg-icons": "^5.15.3",
|
|
||||||
"@fortawesome/react-fontawesome": "^0.1.14",
|
|
||||||
"@loadable/component": "^5.15.0",
|
|
||||||
"ace-builds": "^1.4.12",
|
|
||||||
"ansi-to-react": "^6.1.5",
|
|
||||||
"bootstrap": "^4.6.0",
|
|
||||||
"create-react-class": "^15.7.0",
|
|
||||||
"gatsby": "^3.2.1",
|
|
||||||
"gatsby-plugin-fontawesome-css": "^1.1.0",
|
|
||||||
"gatsby-plugin-image": "^1.3.0",
|
|
||||||
"gatsby-plugin-react-helmet": "^4.3.0",
|
|
||||||
"gatsby-plugin-sass": "^4.8.0",
|
|
||||||
"gatsby-plugin-sharp": "^3.3.0",
|
|
||||||
"gatsby-remark-prismjs": "^5.0.0",
|
|
||||||
"gatsby-source-filesystem": "^3.3.0",
|
|
||||||
"gatsby-transformer-sharp": "^3.3.0",
|
|
||||||
"jquery": "^3.6.0",
|
|
||||||
"nickel-repl": "file:nickel-repl",
|
|
||||||
"popper.js": "^1.16.1",
|
|
||||||
"prismjs": "^1.23.0",
|
|
||||||
"react": "^17.0.1",
|
|
||||||
"react-ace": "^9.4.0",
|
|
||||||
"react-bootstrap": "^1.5.2",
|
|
||||||
"react-bootstrap-icons": "^1.5.0",
|
|
||||||
"react-dom": "^17.0.1",
|
|
||||||
"react-helmet": "^6.1.0",
|
|
||||||
"sass": "^1.35.1"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,158 +0,0 @@
|
|||||||
/**
|
|
||||||
* Define a Nickel mode in the ace editor. This enables syntax highlighting for the Nickel language.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import ace from "ace-builds/src-noconflict/ace";
|
|
||||||
|
|
||||||
ace.define('ace/mode/nickel_highlight_rules', ['require', 'exports', 'ace/lib/oop', 'ace/mode/text_highlight_rules'], function (_require, exports, _module) {
|
|
||||||
const oop = ace.require("ace/lib/oop");
|
|
||||||
const TextHighlightRules = ace.require("ace/mode/text_highlight_rules").TextHighlightRules;
|
|
||||||
|
|
||||||
const NickelHighlightRules = function () {
|
|
||||||
const constantLanguage = "true|false|null";
|
|
||||||
const keywordControl = "switch|import|if|else|then";
|
|
||||||
const keywordDeclaration = "let|in";
|
|
||||||
const keywordMetavalue = "doc|default";
|
|
||||||
|
|
||||||
const keywordMapper = this.createKeywordMapper({
|
|
||||||
"constant.language.nickel": constantLanguage,
|
|
||||||
"keyword.control.nickel": keywordControl,
|
|
||||||
"keyword.declaration.nickel": keywordDeclaration,
|
|
||||||
'keyword.metavalue.nickel': keywordMetavalue,
|
|
||||||
}, "identifier");
|
|
||||||
|
|
||||||
// Although Ace supports modal lexing (the next, push and pop rules allow to
|
|
||||||
// maintain a state and a stack), we can't encode nickel
|
|
||||||
// variable-length delimiter directly with one nice generic rule.
|
|
||||||
//
|
|
||||||
// We thus generate a rule for lengths 1, 2 and 3 (m#", m##", and m###")
|
|
||||||
// plus write a generic rule for size n. The generic rule is wrong for
|
|
||||||
// length 5 and above, but this is highly unlikely to be used in
|
|
||||||
// practice.
|
|
||||||
|
|
||||||
// Generate the starting rule of a string with variable-length
|
|
||||||
// delimiters
|
|
||||||
let genQqdoc = length => ({
|
|
||||||
token: "string",
|
|
||||||
regex: `m${'#'.repeat(length)}"`,
|
|
||||||
next: `qqdoc${length}`,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Generate the escape and end rules of a string with variable-length delimiters
|
|
||||||
let genQqdocState = length => ({
|
|
||||||
[`qqdoc${length}`]: [
|
|
||||||
{
|
|
||||||
token: "constant.language.escape",
|
|
||||||
regex: `${'#'.repeat(length)}{`,
|
|
||||||
push: "start",
|
|
||||||
}, {
|
|
||||||
token: "string",
|
|
||||||
regex: `"${'#'.repeat(length)}m`,
|
|
||||||
next: "pop",
|
|
||||||
}, {
|
|
||||||
defaultToken: "string"
|
|
||||||
}]
|
|
||||||
});
|
|
||||||
|
|
||||||
this.$rules = {
|
|
||||||
"start": [{
|
|
||||||
token: "comment",
|
|
||||||
regex: /\/\/.*$/
|
|
||||||
}, {
|
|
||||||
regex: "(==|!=|<=?|>=?)",
|
|
||||||
token: ["keyword.operator.comparison.nickel"]
|
|
||||||
}, {
|
|
||||||
regex: "(\\+\\+|@)",
|
|
||||||
token: ["keyword.operator.combinator.nickel"]
|
|
||||||
}, {
|
|
||||||
regex: "(#|->|:)",
|
|
||||||
token: ["keyword.operator.type.nickel"]
|
|
||||||
}, {
|
|
||||||
regex: "=",
|
|
||||||
token: "keyword.operator.assignment.nickel"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
token: "string",
|
|
||||||
regex: "\"",
|
|
||||||
next: "qqstring",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
token: "string",
|
|
||||||
regex: "m(#{4,})\"",
|
|
||||||
next: "qqdocn"
|
|
||||||
},
|
|
||||||
genQqdoc(1),
|
|
||||||
genQqdoc(2),
|
|
||||||
genQqdoc(3), {
|
|
||||||
token: "constant.numeric", // hex
|
|
||||||
regex: "0[xX][0-9a-fA-F]+\\b"
|
|
||||||
}, {
|
|
||||||
token: "constant.numeric", // float
|
|
||||||
regex: "[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"
|
|
||||||
}, {
|
|
||||||
token: keywordMapper,
|
|
||||||
regex: "[a-zA-Z_$][a-zA-Z0-9_$]*\\b"
|
|
||||||
}, {
|
|
||||||
regex: "}",
|
|
||||||
token: function (_val, _start, stack) {
|
|
||||||
return stack[1] && stack[1].charAt(0) === "q" ? "constant.language.escape" : "text";
|
|
||||||
},
|
|
||||||
next: "pop"
|
|
||||||
}],
|
|
||||||
"qqdocn": [
|
|
||||||
{
|
|
||||||
token: "constant.language.escape",
|
|
||||||
regex: "#{4,}{",
|
|
||||||
push: "start"
|
|
||||||
}, {
|
|
||||||
token: "string",
|
|
||||||
regex: "\"(#{4,})m",
|
|
||||||
next: "pop"
|
|
||||||
}, {
|
|
||||||
defaultToken: "string"
|
|
||||||
}],
|
|
||||||
...genQqdocState(1),
|
|
||||||
...genQqdocState(2),
|
|
||||||
...genQqdocState(3),
|
|
||||||
"qqstring": [
|
|
||||||
{
|
|
||||||
token: "constant.language.escape",
|
|
||||||
regex: "#{",
|
|
||||||
push: "start"
|
|
||||||
}, {
|
|
||||||
token: "string",
|
|
||||||
regex: '"',
|
|
||||||
next: "pop"
|
|
||||||
}, {
|
|
||||||
defaultToken: "string"
|
|
||||||
}],
|
|
||||||
};
|
|
||||||
|
|
||||||
this.normalizeRules();
|
|
||||||
};
|
|
||||||
|
|
||||||
oop.inherits(NickelHighlightRules, TextHighlightRules);
|
|
||||||
|
|
||||||
exports.NickelHighlightRules = NickelHighlightRules;
|
|
||||||
});
|
|
||||||
|
|
||||||
ace.define("ace/mode/nickel",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/nickel_highlight_rules","ace/mode/folding/cstyle"], function (_require, exports, _module) {
|
|
||||||
const oop = ace.require("ace/lib/oop");
|
|
||||||
const TextMode = ace.require("ace/mode/text").Mode;
|
|
||||||
const NimHighlightRules = ace.require("ace/mode/nickel_highlight_rules").NickelHighlightRules;
|
|
||||||
|
|
||||||
const Mode = function () {
|
|
||||||
TextMode.call(this);
|
|
||||||
this.HighlightRules = NimHighlightRules;
|
|
||||||
this.$behaviour = this.$defaultBehaviour;
|
|
||||||
};
|
|
||||||
|
|
||||||
oop.inherits(Mode, TextMode);
|
|
||||||
|
|
||||||
(function () {
|
|
||||||
this.lineCommentStart = "//";
|
|
||||||
this.$id = "ace/mode/nickel";
|
|
||||||
}).call(Mode.prototype);
|
|
||||||
|
|
||||||
exports.Mode = Mode;
|
|
||||||
});
|
|
@ -1,37 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import { Link } from "gatsby";
|
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|
||||||
import {
|
|
||||||
faGithub,
|
|
||||||
faTwitter
|
|
||||||
} from "@fortawesome/free-brands-svg-icons";
|
|
||||||
import {
|
|
||||||
faArrowUp,
|
|
||||||
faComments
|
|
||||||
} from '@fortawesome/free-solid-svg-icons';
|
|
||||||
|
|
||||||
const Footer = () => (
|
|
||||||
<footer className="bg-secondary text-center">
|
|
||||||
<div className="container pt-3">
|
|
||||||
<section>
|
|
||||||
<Link className="btn text-black btn-outline btn-floating m-1" to="https://twitter.com/nickel_lang" role="button"
|
|
||||||
><FontAwesomeIcon color="black" icon={faTwitter}/></Link>
|
|
||||||
|
|
||||||
<Link className="btn btn-outline btn-floating m-1" to="https://github.com/tweag/nickel" role="button"
|
|
||||||
><FontAwesomeIcon color="black" icon={faGithub}/></Link>
|
|
||||||
|
|
||||||
<Link className="btn btn-outline btn-floating m-1" to="https://github.com/tweag/nickel/discussions" role="button"
|
|
||||||
><FontAwesomeIcon color="black" icon={faComments}/></Link>
|
|
||||||
|
|
||||||
<Link className="btn btn-outline-dark btn-floating m-1 ml-4" to="#" role="button"
|
|
||||||
><FontAwesomeIcon color="black" icon={faArrowUp}/></Link>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
<hr/>
|
|
||||||
<div className="text-center pb-3">
|
|
||||||
© 2021 Copyright: <Link to={'https://github.com/tweag/nickel/graphs/contributors'} className={'link-footer'}>Nickel contributors</Link>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
)
|
|
||||||
|
|
||||||
export default Footer
|
|
@ -1,30 +0,0 @@
|
|||||||
import React from "react"
|
|
||||||
import { Link } from "gatsby"
|
|
||||||
import {StaticImage} from "gatsby-plugin-image";
|
|
||||||
|
|
||||||
const Header = ({ menuLinks }) => (
|
|
||||||
<header>
|
|
||||||
<nav className="navbar navbar-expand-md navbar-light bg-primary">
|
|
||||||
<div className="container-fluid">
|
|
||||||
<Link className="navbar-brand flex-md-fill w-md-100" to="/">
|
|
||||||
<StaticImage className={"logo-navbar"} src="../images/nickel-logo-2.svg" alt="logo"/><span className="nickel">Nickel</span>
|
|
||||||
</Link>
|
|
||||||
<button className="navbar-toggler" type="button" data-toggle="collapse"
|
|
||||||
data-target="#navbarNavAltMarkup"
|
|
||||||
aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
|
|
||||||
<span className="navbar-toggler-icon"/>
|
|
||||||
</button>
|
|
||||||
<div className="collapse navbar-collapse justify-content-center flex-fill w-100" id="navbarNavAltMarkup">
|
|
||||||
<div className="navbar-nav">
|
|
||||||
{menuLinks.map(link => (
|
|
||||||
<Link key={link.name} className="nav-link" activeClassName="active" to={link.link}>{link.name}</Link>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<ul className={'d-none d-md-block w-100 justify-contend-end'}/>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
</header>
|
|
||||||
);
|
|
||||||
|
|
||||||
export default Header
|
|
@ -1,42 +0,0 @@
|
|||||||
import React from "react"
|
|
||||||
import {graphql, StaticQuery} from "gatsby"
|
|
||||||
import {Helmet} from "react-helmet"
|
|
||||||
import Header from "./header"
|
|
||||||
import Footer from "./footer"
|
|
||||||
|
|
||||||
export default function Layout({children}) {
|
|
||||||
return (
|
|
||||||
<StaticQuery
|
|
||||||
query={graphql`
|
|
||||||
query SiteTitleQuery {
|
|
||||||
site {
|
|
||||||
siteMetadata {
|
|
||||||
title
|
|
||||||
menuLinks {
|
|
||||||
name
|
|
||||||
link
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`}
|
|
||||||
render={data => (
|
|
||||||
<React.Fragment>
|
|
||||||
<Helmet
|
|
||||||
title={'Nickel'}
|
|
||||||
meta={[
|
|
||||||
{name: 'description', content: 'Sample'},
|
|
||||||
{name: 'keywords', content: 'sample, something'},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
</Helmet>
|
|
||||||
<Header menuLinks={data.site.siteMetadata.menuLinks}/>
|
|
||||||
<div>
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
<Footer/>
|
|
||||||
</React.Fragment>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
import {lazy} from "@loadable/component";
|
|
||||||
import React from 'react'
|
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|
||||||
import {
|
|
||||||
faSpinner,
|
|
||||||
} from '@fortawesome/free-solid-svg-icons';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wrapper around the Playground component to use it on the client side only (and not via server-side rendering).
|
|
||||||
* This is made necessary by the code editor, based on react-ace and ace-builds, which use `window` and don't seem to fully support SSR yet.
|
|
||||||
* @type {React.ForwardRefExoticComponent<React.PropsWithoutRef<{}> & React.RefAttributes<unknown>>}
|
|
||||||
*/
|
|
||||||
const LoadablePlayground = lazy(() => import("./playground/playground"));
|
|
||||||
|
|
||||||
export default function ClientSidePlayground(props) {
|
|
||||||
const isSSR = typeof window === "undefined";
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{!isSSR && (
|
|
||||||
<React.Suspense fallback={
|
|
||||||
<div className={"text-center playground-loader mt-4"}>
|
|
||||||
Loading playground <span className={"ml-2"}><FontAwesomeIcon icon={faSpinner} size={"lg"} spin/></span>
|
|
||||||
</div>
|
|
||||||
}>
|
|
||||||
<LoadablePlayground {...props}/>
|
|
||||||
</React.Suspense>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,184 +0,0 @@
|
|||||||
import * as React from 'react';
|
|
||||||
import AceEditor from 'react-ace';
|
|
||||||
import nickelCodes from './nickel-codes'
|
|
||||||
import {PLAYGROUND_SEND_EVENT, EDITOR_SEND_EVENT, REPL_RUN_EVENT} from "./events";
|
|
||||||
|
|
||||||
import "ace-builds/src-noconflict/theme-solarized_dark";
|
|
||||||
import "../../ace-nickel-mode/ace-nickel-mode";
|
|
||||||
import ReactDOMServer from "react-dom/server";
|
|
||||||
import {wrapPageElement} from "gatsby/dist/utils/api-browser-docs";
|
|
||||||
import modes from "./modes";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Nickel code editor component, based on the Ace editor.
|
|
||||||
*/
|
|
||||||
export default class Editor extends React.Component {
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
value: this.props.value,
|
|
||||||
name: this.props.name,
|
|
||||||
placeholder: 'Write your code. Press Ctrl+Enter (Cmd+Enter) to run it',
|
|
||||||
theme: "solarized_dark",
|
|
||||||
mode: "nickel",
|
|
||||||
height: "100%",
|
|
||||||
width: "100%",
|
|
||||||
enableBasicAutocompletion: false,
|
|
||||||
enableLiveAutocompletion: false,
|
|
||||||
fontSize: 14,
|
|
||||||
showGutter: true,
|
|
||||||
showPrintMargin: true,
|
|
||||||
highlightActiveLine: true,
|
|
||||||
enableSnippets: false,
|
|
||||||
showLineNumbers: true,
|
|
||||||
annotations: [],
|
|
||||||
wrapEnabled: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
if(this.props.fit && this.props.fit === 'code') {
|
|
||||||
// Taking more lines to account for potential wrapping
|
|
||||||
const lines = this.props.value.split(/\r?\n/g).length+2;
|
|
||||||
this.state.maxLines = lines;
|
|
||||||
this.state.minLines = lines;
|
|
||||||
}
|
|
||||||
else if(this.props.fit && this.props.fit === 'lines' && this.props.lines) {
|
|
||||||
this.state.maxLines = this.props.lines;
|
|
||||||
this.state.minLines = this.props.lines;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.onChange = this.onChange.bind(this);
|
|
||||||
this.onREPLRun = this.onREPLRun.bind(this);
|
|
||||||
this.send = this.send.bind(this);
|
|
||||||
this.aceEditorRef = React.createRef();
|
|
||||||
}
|
|
||||||
|
|
||||||
static defaultProps = {
|
|
||||||
value: `let data = {value = "Hello," ++ " world!"} in data.value`,
|
|
||||||
name: 'nickel-repl-input',
|
|
||||||
};
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
// Listen to the REPL's execution events, in order to update potential error messages.
|
|
||||||
document.addEventListener(REPL_RUN_EVENT, this.onREPLRun);
|
|
||||||
document.addEventListener(PLAYGROUND_SEND_EVENT, this.send);
|
|
||||||
|
|
||||||
if(this.props.onResize) {
|
|
||||||
const ro = new ResizeObserver(entries => {
|
|
||||||
for(let entry of entries) {
|
|
||||||
this.props.onResize(entry.target.clientHeight);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
ro.observe(document.getElementById(this.state.name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onChange(newValue) {
|
|
||||||
this.setState({
|
|
||||||
value: newValue
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
getHeight() {
|
|
||||||
return document.getElementById(this.state.name).clientHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Static component displaying a Nickel diagnostic error.
|
|
||||||
* @param diagnostic
|
|
||||||
* @param label
|
|
||||||
* @returns {*}
|
|
||||||
*/
|
|
||||||
annotationWidget(diagnostic, label) {
|
|
||||||
const labelClass = label.style === nickelCodes.error.label.PRIMARY ? 'ansi-red-fg' : 'ansi-blue-fg';
|
|
||||||
return (<div>
|
|
||||||
<span className={"ansi-bright-red-fg"}>{diagnostic.msg}</span><br/>
|
|
||||||
<span className={labelClass}>{label.msg}</span><br/>
|
|
||||||
<ul>
|
|
||||||
{diagnostic.notes.map(note => <li>{note}</li>)}
|
|
||||||
</ul>
|
|
||||||
</div>)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Once the REPL has run, update the error messages.
|
|
||||||
* @param result
|
|
||||||
*/
|
|
||||||
onREPLRun({detail: result}) {
|
|
||||||
if (result.tag === nickelCodes.result.ERROR) {
|
|
||||||
const annotations = result.errors.filter(diagnostic => diagnostic.severity >= nickelCodes.error.severity.WARNING)
|
|
||||||
.map(diagnostic => (
|
|
||||||
diagnostic.labels.map(label => ({
|
|
||||||
row: label.line_start,
|
|
||||||
column: label.col_start,
|
|
||||||
html: ReactDOMServer.renderToStaticMarkup(this.annotationWidget(diagnostic, label)),
|
|
||||||
type: label.style === nickelCodes.error.label.PRIMARY ? 'error' : 'warning',
|
|
||||||
}))
|
|
||||||
)).flat();
|
|
||||||
|
|
||||||
// In some obscure circumstances (annotation on the last line, and then insertion of a new line), annotations disappear, even if the user send the same input again.
|
|
||||||
// To avoid this and make annotations reappear at least when sending an input, we clear the old one first, to triggers reactive updates.
|
|
||||||
this.setState({annotations: []}, () => this.setState({annotations}));
|
|
||||||
} else {
|
|
||||||
this.setState({annotations: []});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dispatch an EDITOR_SEND_EVENT with the current content as a payload.
|
|
||||||
*/
|
|
||||||
send() {
|
|
||||||
// Dispatch the result as an event, so that the editor or other components can react to the outcome of the last input
|
|
||||||
const event = new CustomEvent(EDITOR_SEND_EVENT, {detail: this.state.value});
|
|
||||||
document.dispatchEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const setEditorMargin = (editor) => {
|
|
||||||
editor.renderer.setPadding(10);
|
|
||||||
editor.renderer.setScrollMargin(10, 10, 0, 0);
|
|
||||||
};
|
|
||||||
|
|
||||||
return <AceEditor
|
|
||||||
ref={this.aceEditorRef}
|
|
||||||
placeholder={this.state.placeholder}
|
|
||||||
mode={this.state.mode}
|
|
||||||
theme={this.state.theme}
|
|
||||||
name={this.state.name}
|
|
||||||
height={this.state.height}
|
|
||||||
width={this.state.width}
|
|
||||||
minLines={this.state.minLines}
|
|
||||||
maxLines={this.state.maxLines}
|
|
||||||
onChange={this.onChange}
|
|
||||||
onSelectionChange={this.onSelectionChange}
|
|
||||||
onCursorChange={this.onCursorChange}
|
|
||||||
onValidate={this.onValidate}
|
|
||||||
value={this.state.value}
|
|
||||||
annotations={this.state.annotations}
|
|
||||||
fontSize={this.state.fontSize}
|
|
||||||
showPrintMargin={this.state.showPrintMargin}
|
|
||||||
showGutter={this.state.showGutter}
|
|
||||||
highlightActiveLine={this.state.highlightActiveLine}
|
|
||||||
wrapEnabled={this.state.wrapEnabled}
|
|
||||||
onLoad={setEditorMargin}
|
|
||||||
commands={[
|
|
||||||
{
|
|
||||||
name: 'send-repl',
|
|
||||||
bindKey: {
|
|
||||||
win: 'Ctrl-enter',
|
|
||||||
mac: 'Cmd-enter',
|
|
||||||
},
|
|
||||||
exec: this.send,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
setOptions={{
|
|
||||||
useWorker: false,
|
|
||||||
enableBasicAutocompletion: this.state.enableBasicAutocompletion,
|
|
||||||
enableLiveAutocompletion: this.state.enableLiveAutocompletion,
|
|
||||||
enableSnippets: this.state.enableSnippets,
|
|
||||||
showLineNumbers: this.state.showLineNumbers,
|
|
||||||
tabSize: 2
|
|
||||||
}}
|
|
||||||
/>;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
/**
|
|
||||||
* The user request the execution of the current snippet (by clicking the "Run" button, or using the corresponding shortcut).
|
|
||||||
* @type {string}
|
|
||||||
*/
|
|
||||||
const PLAYGROUND_SEND_EVENT = 'playground:send';
|
|
||||||
/**
|
|
||||||
* The editor is sending a snippet for execution (by clicking the "Run" button, or usign the corresponding shortcut). Editor listens to `PLAYGROUND_SEND_EVENT`,
|
|
||||||
* and dispatch `EDITOR_SEND_ENVENT`. The payload (field `detail` of the event) contains the input as a string.
|
|
||||||
* @type {string}
|
|
||||||
*/
|
|
||||||
const EDITOR_SEND_EVENT = 'nickel-repl:send';
|
|
||||||
/**
|
|
||||||
* The REPL has run a snippet and returned. The `details`
|
|
||||||
* @type {string}
|
|
||||||
*/
|
|
||||||
const REPL_RUN_EVENT = 'nickel-repl:run';
|
|
||||||
|
|
||||||
export {PLAYGROUND_SEND_EVENT, EDITOR_SEND_EVENT, REPL_RUN_EVENT};
|
|
@ -1,12 +0,0 @@
|
|||||||
/**
|
|
||||||
* The different execution modes of the REPL component.
|
|
||||||
* @type {{REPL: string, JSON: string, TOML: string, YAML: string}}
|
|
||||||
*/
|
|
||||||
const modes = {
|
|
||||||
REPL: 'repl',
|
|
||||||
JSON: 'json',
|
|
||||||
TOML: 'toml',
|
|
||||||
YAML: 'yaml',
|
|
||||||
};
|
|
||||||
|
|
||||||
export default modes;
|
|
@ -1,27 +0,0 @@
|
|||||||
/**
|
|
||||||
* Codes returned by the Nickel WASM evaluator.
|
|
||||||
* @type {{result: {BLANK: number, SUCCESS: number, PARTIAL: number, ERROR: number}, error: {severity: {HELP: number, BUG: number, NOTE: number, ERROR: number, WARNING: number}, label: {SECONDARY: number, PRIMARY: number}}}}
|
|
||||||
*/
|
|
||||||
const nickelCodes = {
|
|
||||||
result: {
|
|
||||||
SUCCESS: 0,
|
|
||||||
BLANK: 1,
|
|
||||||
PARTIAL: 2,
|
|
||||||
ERROR: 3,
|
|
||||||
},
|
|
||||||
error: {
|
|
||||||
severity: {
|
|
||||||
HELP: 1,
|
|
||||||
NOTE: 2,
|
|
||||||
WARNING: 3,
|
|
||||||
ERROR: 4,
|
|
||||||
BUG: 5,
|
|
||||||
},
|
|
||||||
label: {
|
|
||||||
PRIMARY: 0,
|
|
||||||
SECONDARY: 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default nickelCodes;
|
|
@ -1,98 +0,0 @@
|
|||||||
import * as React from 'react';
|
|
||||||
|
|
||||||
import Editor from "./editor";
|
|
||||||
import Repl from "./repl";
|
|
||||||
import modes from "./modes";
|
|
||||||
import {PLAYGROUND_SEND_EVENT} from "./events.js";
|
|
||||||
import {Command} from 'react-bootstrap-icons';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Playground component, composed of both a code editor and a REPL component.
|
|
||||||
*/
|
|
||||||
export default class Playground extends React.Component {
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.state = {mode: this.props.mode};
|
|
||||||
this.setMode = this.setMode.bind(this);
|
|
||||||
this.dispatchSendEvent = this.dispatchSendEvent.bind(this);
|
|
||||||
|
|
||||||
if(this.props.fit === 'code' || this.props.fit === 'lines') {
|
|
||||||
// In fit-to-code mode, fix the height of the output (terminal) element to the height of the current code
|
|
||||||
this.onEditorResize = (height) => {
|
|
||||||
if(this.terminalContainer) {
|
|
||||||
this.terminalContainer.style.height = height + "px";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
setTimeout(this.onEditorResize, 100)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
// If a program was provided initially, run it.
|
|
||||||
if(this.props.value) {
|
|
||||||
this.editor.send();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static defaultProps = {
|
|
||||||
mode: modes.REPL,
|
|
||||||
fit: 'page',
|
|
||||||
};
|
|
||||||
|
|
||||||
setTerminalContainer = element => this.terminalContainer = element;
|
|
||||||
setEditor = editor => this.editor = editor;
|
|
||||||
|
|
||||||
replTabStyle = (mode) => ('nav-link link-secondary playground-nav-item' + (this.state.mode === mode ? ' active' : ''));
|
|
||||||
|
|
||||||
setMode = (mode) => {
|
|
||||||
this.setState({mode});
|
|
||||||
};
|
|
||||||
|
|
||||||
dispatchSendEvent = () => {
|
|
||||||
const event = new CustomEvent(PLAYGROUND_SEND_EVENT);
|
|
||||||
document.dispatchEvent(event);
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return <React.Fragment>
|
|
||||||
<div className={"row"}>
|
|
||||||
<div className={"col-6 playground-tab d-flex align-items-center"}>
|
|
||||||
<div>
|
|
||||||
<button className={"btn btn-primary"} onClick={() => this.dispatchSendEvent()}>Run</button>
|
|
||||||
<span className={'ml-4'}> or press</span> <kbd>Ctrl</kbd>+<kbd>Enter</kbd> or <kbd>Cmd <Command/>
|
|
||||||
</kbd>+<kbd>Enter</kbd>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<ul className={"col-6 nav nav-pills playground-tab"}>
|
|
||||||
<li className="nav-item">
|
|
||||||
<a className={this.replTabStyle(modes.REPL)} onClick={() => this.setMode(modes.REPL)}>REPL</a>
|
|
||||||
</li>
|
|
||||||
<li className="nav-item">
|
|
||||||
<a className={this.replTabStyle(modes.JSON)} onClick={() => this.setMode(modes.JSON)}>JSON</a>
|
|
||||||
</li>
|
|
||||||
<li className="nav-item">
|
|
||||||
<a className={this.replTabStyle(modes.YAML)} onClick={() => this.setMode(modes.YAML)}>YAML</a>
|
|
||||||
</li>
|
|
||||||
<li className="nav-item">
|
|
||||||
<a className={this.replTabStyle(modes.TOML)} onClick={() => this.setMode(modes.TOML)}>TOML</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<section className={'row playground-container overflow-hidden flex-grow-1'}>
|
|
||||||
<div className={'col-6'}>
|
|
||||||
<Editor ref={this.setEditor} fit={this.props.fit} lines={this.props.lines} value={this.props.value} onResize={this.onEditorResize}/>
|
|
||||||
</div>
|
|
||||||
<div id={"playground-terminal-container"}
|
|
||||||
ref={this.setTerminalContainer}
|
|
||||||
className={'col-6 ansi-monokai playground-terminal-container'}>
|
|
||||||
<Repl containerId={"playground-terminal-container"} className={'playground-terminal'}
|
|
||||||
mode={this.state.mode}/>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</React.Fragment>
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,179 +0,0 @@
|
|||||||
import * as React from 'react';
|
|
||||||
import {repl_init, repl_input, repl_serialize} from "nickel-repl";
|
|
||||||
import Ansi from "ansi-to-react";
|
|
||||||
import {EDITOR_SEND_EVENT, REPL_RUN_EVENT} from "./events";
|
|
||||||
import modes from './modes';
|
|
||||||
import nickelCodes from './nickel-codes';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An REPL. This component can run Nickel programs or REPL commands and display a stylized output.
|
|
||||||
*/
|
|
||||||
export default class Repl extends React.Component {
|
|
||||||
constructor(props, context) {
|
|
||||||
super(props, context);
|
|
||||||
this.state = {
|
|
||||||
lastInput: '',
|
|
||||||
// Output displayed in REPL mode. It is appended to at each run.
|
|
||||||
output_repl: "Nickel Online REPL | Welcome to the Nickel online REPL.\n"
|
|
||||||
+ "See the output of your snippets here.\n\n",
|
|
||||||
// Output displayed in serialize mode. Cleared at each new run.
|
|
||||||
output_serialize: '',
|
|
||||||
};
|
|
||||||
this.endRef = React.createRef();
|
|
||||||
}
|
|
||||||
|
|
||||||
static defaultProps = {
|
|
||||||
mode: modes.REPL
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the current REPL output as an array of lines.
|
|
||||||
* @returns {string[]}
|
|
||||||
*/
|
|
||||||
lines() {
|
|
||||||
return this.state.output_repl.split(/\r?\n/g);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
const result = repl_init();
|
|
||||||
|
|
||||||
if (result.tag === nickelCodes.result.ERROR) {
|
|
||||||
this.write(`Initialization error: ${result.msg}\n`);
|
|
||||||
} else {
|
|
||||||
// /!\ WARNING: result is moved by the Rust code when calling to the repl() method. Do not use or copy result after this call to repl().
|
|
||||||
this.repl = result.repl();
|
|
||||||
this.prompt();
|
|
||||||
}
|
|
||||||
document.addEventListener(EDITOR_SEND_EVENT, this.onSend.bind(this))
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write text. Newlines and ANSI escape codes are converted to HTML before rendering.
|
|
||||||
* In serialize mode, the new output erase the old content. In REPL mode, the new output is appended to.
|
|
||||||
* Because state updates are asynchronous, this returns a Promise that resolves when everything is up to date.
|
|
||||||
* @param data String
|
|
||||||
* @returns {Promise<unknown>}
|
|
||||||
*/
|
|
||||||
write(data) {
|
|
||||||
return new Promise(resolve => {
|
|
||||||
if (this.props.mode === modes.REPL) {
|
|
||||||
this.setState({output_repl: this.state.output_repl + data}, resolve);
|
|
||||||
} else {
|
|
||||||
this.setState({output_serialize: data}, resolve);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clear the output.
|
|
||||||
* @returns {Promise<unknown>}
|
|
||||||
*/
|
|
||||||
clear() {
|
|
||||||
return new Promise(resolve => {
|
|
||||||
if (this.props.mode === modes.REPL) {
|
|
||||||
this.setState({output_repl: ''}, resolve);
|
|
||||||
} else {
|
|
||||||
this.setState({output_serialize: ''}, resolve);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
).then(() => this.prompt());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write a new line followed by a prompt, if in REPL mode. Do nothing otherwise.
|
|
||||||
* @returns {Promise<unknown>}
|
|
||||||
*/
|
|
||||||
prompt = () => {
|
|
||||||
if (this.props.mode === modes.REPL) {
|
|
||||||
return this.write('\n\u001b[32mnickel>\u001b[0m ');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Run it. In REPL mode, the input is also appended to the output.
|
|
||||||
* @param input String
|
|
||||||
*/
|
|
||||||
onSend = ({detail: input}) => {
|
|
||||||
if (this.props.mode === modes.REPL) {
|
|
||||||
return this.write(input).then(() => this.run(input));
|
|
||||||
} else {
|
|
||||||
return this.run(input);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Run an input and write the result in the output.
|
|
||||||
* @param input String
|
|
||||||
* @returns {Promise<number>} A promise resolving to the return code of the execution of the Nickel REPL, or -1 if the REPL wasn't loaded.
|
|
||||||
*/
|
|
||||||
run = (input) => {
|
|
||||||
if (this.repl === null) {
|
|
||||||
console.error("Terminal: REPL is not loaded (this.repl === null)");
|
|
||||||
return new Promise(resolve => resolve(-1));
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({lastInput: input});
|
|
||||||
|
|
||||||
let result;
|
|
||||||
|
|
||||||
if (this.props.mode !== modes.REPL) {
|
|
||||||
result = repl_serialize(this.repl, this.props.mode, input);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
result = repl_input(this.repl, input);
|
|
||||||
}
|
|
||||||
|
|
||||||
let task;
|
|
||||||
|
|
||||||
if (this.props.mode === modes.REPL) {
|
|
||||||
task = this.write("\n" + result.msg).then(() => this.prompt());
|
|
||||||
} else {
|
|
||||||
//If there's an error, we run the original snippet in order to have a better error message.
|
|
||||||
if (result.tag === nickelCodes.result.ERROR) {
|
|
||||||
const resultAlone = repl_input(this.repl, this.state.lastInput);
|
|
||||||
|
|
||||||
// If there's no error for the original snippet alone, this may be a NonSerializable error. In this case,
|
|
||||||
// we keep the first error message.
|
|
||||||
if (resultAlone.tag === nickelCodes.result.ERROR) {
|
|
||||||
result = resultAlone;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
task = this.write(result.msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dispatch the result as an event, so that the editor or other components can react to the outcome of the last input
|
|
||||||
const event = new CustomEvent(REPL_RUN_EVENT, {detail: result});
|
|
||||||
document.dispatchEvent(event);
|
|
||||||
|
|
||||||
return task.then(() => result.tag);
|
|
||||||
};
|
|
||||||
|
|
||||||
componentDidUpdate = (prevProps) => {
|
|
||||||
// If we switched mode to a serialization mode and there is a last input, we re-run the last input
|
|
||||||
if (this.props.mode !== prevProps.mode && this.props.mode !== modes.REPL && this.state.lastInput !== '') {
|
|
||||||
this.run(this.state.lastInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scroll to the last message
|
|
||||||
const terminalContainer = document.getElementById(this.props.containerId);
|
|
||||||
terminalContainer.scrollTop = terminalContainer.scrollHeight;
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
let content;
|
|
||||||
|
|
||||||
if (this.props.mode === modes.REPL) {
|
|
||||||
content = this.lines().map((line, index) => <div key={index}><Ansi useClasses>{line.toString()}</Ansi><br/>
|
|
||||||
</div>);
|
|
||||||
} else {
|
|
||||||
content = <Ansi useClasses>{this.state.output_serialize}</Ansi>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return <div style={{whiteSpace: 'pre-wrap'}}>
|
|
||||||
{content}
|
|
||||||
<div ref={this.endRef}/>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
}
|
|
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 9.7 KiB |
Before Width: | Height: | Size: 10 KiB |
@ -1 +0,0 @@
|
|||||||
<svg id="Calque_1" data-name="Calque 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 299.83 339.82"><defs><style>.cls-1{fill:#e0c3fc;}.cls-2{fill:#fff;}</style></defs><path class="cls-1" d="M1110.37,614.62V465.38a20.67,20.67,0,0,0-10.34-17.9L970.79,372.86a20.68,20.68,0,0,0-20.67,0L820.88,447.48a20.67,20.67,0,0,0-10.34,17.9V614.62a20.67,20.67,0,0,0,10.34,17.9l129.24,74.62a20.68,20.68,0,0,0,20.67,0L1100,632.52A20.67,20.67,0,0,0,1110.37,614.62Z" transform="translate(-810.54 -370.09)"/><path class="cls-2" d="M1023.14,509.09V511q0,34.5,0,69a2.31,2.31,0,0,1-1.57,2.53q-25.8,11.37-51.54,22.85c-.44.2-.89.37-1.5.62,0-.49-.1-.88-.1-1.28q0-35,0-70a1.77,1.77,0,0,1,1.2-2q26.14-11.55,52.27-23.18C1022.24,509.45,1022.59,509.32,1023.14,509.09Z" transform="translate(-810.54 -370.09)"/><path class="cls-2" d="M898.14,509.06l8.94,3.94q22,9.79,44.11,19.56a2.38,2.38,0,0,1,1.67,2.64q-.08,34.41,0,68.83v2c-.62-.24-1.11-.41-1.59-.62q-25.83-11.48-51.68-22.92a2.09,2.09,0,0,1-1.45-2.28q.06-34.66,0-69.33Z" transform="translate(-810.54 -370.09)"/><path class="cls-2" d="M1011.61,497.07l-23.47,10.42c-8.76,3.89-17.52,7.8-26.3,11.65a3.1,3.1,0,0,1-2.24.06q-24.45-10.77-48.86-21.66c-.25-.1-.48-.25-.91-.47a11.69,11.69,0,0,1,1.11-.67q24.27-10.78,48.56-21.54a3,3,0,0,1,2.1-.07q24.52,10.83,49,21.74C1010.84,496.63,1011.06,496.77,1011.61,497.07Z" transform="translate(-810.54 -370.09)"/></svg>
|
|
Before Width: | Height: | Size: 1.3 KiB |
@ -1 +0,0 @@
|
|||||||
<svg id="Calque_1" data-name="Calque 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 299.09 338.96"><defs><style>.cls-1{fill:#3a3a3c;}.cls-2{fill:#e0c3fc;}</style></defs><path class="cls-1" d="M1110,614.41V465.59a20.68,20.68,0,0,0-10.34-17.9l-128.87-74.4a20.63,20.63,0,0,0-20.67,0l-128.88,74.4a20.69,20.69,0,0,0-10.33,17.9V614.41a20.69,20.69,0,0,0,10.33,17.9l128.88,74.4a20.63,20.63,0,0,0,20.67,0l128.87-74.4A20.68,20.68,0,0,0,1110,614.41Z" transform="translate(-810.91 -370.52)"/><path class="cls-2" d="M1023,509.16v1.9q0,34.41,0,68.83a2.31,2.31,0,0,1-1.56,2.53Q995.71,593.76,970,605.21c-.44.2-.89.37-1.5.62,0-.49-.1-.88-.1-1.28q0-34.9,0-69.8a1.77,1.77,0,0,1,1.19-2q26.1-11.52,52.15-23.12Z" transform="translate(-810.91 -370.52)"/><path class="cls-2" d="M898.29,509.14c3.12,1.37,6,2.64,8.92,3.93,14.67,6.51,29.32,13,44,19.51a2.39,2.39,0,0,1,1.66,2.63q-.07,34.34,0,68.67v2c-.62-.24-1.11-.41-1.58-.62q-25.77-11.44-51.56-22.87a2.07,2.07,0,0,1-1.44-2.27q0-34.58,0-69.15Z" transform="translate(-810.91 -370.52)"/><path class="cls-2" d="M1011.49,497.17l-23.42,10.4c-8.74,3.88-17.47,7.79-26.23,11.62a3.15,3.15,0,0,1-2.24.07q-24.39-10.75-48.74-21.61c-.25-.11-.48-.25-.9-.48.44-.26.75-.5,1.1-.66q24.21-10.77,48.45-21.49a3,3,0,0,1,2.09-.07q24.46,10.8,48.89,21.69C1010.72,496.74,1010.94,496.88,1011.49,497.17Z" transform="translate(-810.91 -370.52)"/></svg>
|
|
Before Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 7.9 KiB |
Before Width: | Height: | Size: 8.2 KiB |
Before Width: | Height: | Size: 5.6 KiB |
@ -1,174 +0,0 @@
|
|||||||
---
|
|
||||||
page: "getting-started"
|
|
||||||
---
|
|
||||||
|
|
||||||
# Getting started
|
|
||||||
|
|
||||||
Nickel is quite new and not yet distributed using the standard channels
|
|
||||||
(binaries, nix package, Rust crate, and so on). We are sorry if the installation
|
|
||||||
process is not yet optimal, but this should change soon, so stay tuned.
|
|
||||||
|
|
||||||
## Build from source using Nix
|
|
||||||
|
|
||||||
Using [Nix]("https://nixos.org/") is the easiest way to get a Nickel executable
|
|
||||||
running:
|
|
||||||
|
|
||||||
1. Clone the [Nickel repository](https://github.io/tweag/nickel)
|
|
||||||
locally and set it as the current directory:
|
|
||||||
|
|
||||||
```shell-session
|
|
||||||
$ git clone git@github.com:tweag/nickel.git
|
|
||||||
Cloning in 'nickel'...
|
|
||||||
[..]
|
|
||||||
$ cd nickel
|
|
||||||
devops@nickel-lang:~/nickel$
|
|
||||||
```
|
|
||||||
|
|
||||||
1. Invoke nix build:
|
|
||||||
|
|
||||||
```shell-session
|
|
||||||
devops@nickel-lang:~/nickel$ nix build
|
|
||||||
[1 built, 0.0 MiB DL]
|
|
||||||
devops@nickel-lang:~/nickel$
|
|
||||||
```
|
|
||||||
|
|
||||||
1. If everything went right, a binary is now available in the
|
|
||||||
result directory:
|
|
||||||
|
|
||||||
```shell-session
|
|
||||||
devops@nickel-lang:~/nickel$ ./result/bin/nickel -V
|
|
||||||
nickel 0.1.0
|
|
||||||
devops@nickel-lang:~/nickel$
|
|
||||||
```
|
|
||||||
|
|
||||||
## Build from source without Nix
|
|
||||||
|
|
||||||
You will find alternative ways to build Nickel from source by cloning the
|
|
||||||
[repository](href="https://github.io/tweag/nickel) and following the
|
|
||||||
instructions of the
|
|
||||||
[README](href="https://github.com/tweag/nickel/#getting-started").
|
|
||||||
|
|
||||||
## Write your first configuration
|
|
||||||
|
|
||||||
Nickel has a ton of cool features, like gradual typing, contracts and a merge
|
|
||||||
system. However, you'll only have to deal with them once you need them. Writing
|
|
||||||
basic configuration is almost as writing JSON or YAML. Let us start with a
|
|
||||||
basic fictional app configuration:
|
|
||||||
|
|
||||||
```nickel
|
|
||||||
{
|
|
||||||
name = "example",
|
|
||||||
description = m#"
|
|
||||||
This is an awesome software I'm developing.
|
|
||||||
Please use it!
|
|
||||||
"#m,
|
|
||||||
version = "0.1.1",
|
|
||||||
main = "index.js",
|
|
||||||
keywords = ["example", "config"],
|
|
||||||
scripts = {
|
|
||||||
test = m#"test.sh --option --install example --version "0.1.1""#m,
|
|
||||||
do_stuff = "do_stuff.sh subcommand",
|
|
||||||
},
|
|
||||||
contributors = [{
|
|
||||||
name = "John Doe",
|
|
||||||
email = "johndoe@example.com"
|
|
||||||
}, {
|
|
||||||
name = "Ivy Lane",
|
|
||||||
url = "https=//example.com/ivylane"
|
|
||||||
}],
|
|
||||||
dependencies = {
|
|
||||||
dep1 = "^1.0.0",
|
|
||||||
dep3 = "6.7"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
This program describe a record delimited by `{` and `}`, consisting in a list of
|
|
||||||
key-value pairs, akin to JSON's objects. Nickel basic datatypes include strings
|
|
||||||
delimited by `"` and lists, by `[` and `]`.
|
|
||||||
|
|
||||||
The m#" and "#m delimits multiline strings. In such strings, the common
|
|
||||||
indentation prefix is stripped, and special characters (excepted
|
|
||||||
interpolation #{}) loose their meaning. It is useful for two purpose
|
|
||||||
illustrated here:
|
|
||||||
|
|
||||||
- Writing strings spanning multiple lines while keeping the same
|
|
||||||
indentation as code.
|
|
||||||
- Writing strings with special characters in it, without having to
|
|
||||||
escape them (", \, and so on).
|
|
||||||
|
|
||||||
## Export
|
|
||||||
|
|
||||||
Now, save the content in "example.ncl" and run nickel export (or
|
|
||||||
./result/bin/nickel export if you haven't made a symbolic link):
|
|
||||||
|
|
||||||
```shell-session
|
|
||||||
devops@nickel-lang:~/nickel$ nickel -f example.ncl export --format yaml
|
|
||||||
---
|
|
||||||
contributors:
|
|
||||||
- email: johndoe@example.com
|
|
||||||
name: John Doe
|
|
||||||
- name: Ivy Lane
|
|
||||||
url: https=//example.com/ivylane
|
|
||||||
dependencies:
|
|
||||||
dep1: ^1.0.0
|
|
||||||
dep3: "6.7"
|
|
||||||
description: "This is an awesome software I'm developing.\nPlease use it!"
|
|
||||||
keywords:
|
|
||||||
- example
|
|
||||||
- config
|
|
||||||
main: index.js
|
|
||||||
name: example
|
|
||||||
scripts:
|
|
||||||
do_stuff: do_stuff.sh subcommand
|
|
||||||
test: "test.sh --option --install example --version \"0.1.1\""
|
|
||||||
version: 0.1.1
|
|
||||||
```
|
|
||||||
|
|
||||||
Currently supported formats are yaml, toml, json, and raw. json is the
|
|
||||||
default, while raw expect a string result that it output directly, useful to
|
|
||||||
generate e.g. shell scripts or other custom data.
|
|
||||||
|
|
||||||
## Reuse
|
|
||||||
|
|
||||||
Nickel is a programming language. This allows you not only to describe, but to
|
|
||||||
generate data. There's some repetition in our previous example (reproducing only
|
|
||||||
the interesting part):
|
|
||||||
|
|
||||||
```nickel
|
|
||||||
name = "example",
|
|
||||||
version = "0.1.1",
|
|
||||||
scripts = {
|
|
||||||
test = m#"test.sh --option --install example --version "0.1.1""#m,
|
|
||||||
```
|
|
||||||
|
|
||||||
Apart from aesthetics, a more serious issue is inconsistency. If you bump the
|
|
||||||
version number in version, you may forget to do so in the test scripts as well,
|
|
||||||
leading to an incorrect configuration. To remedy this problem, let us have a
|
|
||||||
single source of truth by reusing the value of name and version in test, using
|
|
||||||
the interpolation syntax `#{expr}`:
|
|
||||||
|
|
||||||
```nickel
|
|
||||||
name = "example",
|
|
||||||
version = "0.1.1",
|
|
||||||
scripts = {
|
|
||||||
test = m#"test.sh --option --install #{name} --version "#{version}""#m
|
|
||||||
```
|
|
||||||
|
|
||||||
Now, if we change version to "0.1.2" and export the result, the test script
|
|
||||||
invocation is updated as well:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# [...]
|
|
||||||
scripts:
|
|
||||||
do_stuff: do_stuff.sh subcommand
|
|
||||||
test: "test.sh --option --install example --version \"0.1.2\""
|
|
||||||
version: 0.1.2
|
|
||||||
```
|
|
||||||
|
|
||||||
## Going further
|
|
||||||
|
|
||||||
This was a short introduction that should get you started. But Nickel is a
|
|
||||||
full-fledged programming language, featuring higher-order functions, gradual
|
|
||||||
typing, contracts, and much more. You'll find more resources on the
|
|
||||||
[Documentation](/documentation) page.
|
|
@ -1,54 +0,0 @@
|
|||||||
import * as React from "react"
|
|
||||||
import { Link } from "gatsby"
|
|
||||||
|
|
||||||
// styles
|
|
||||||
const pageStyles = {
|
|
||||||
color: "#232129",
|
|
||||||
padding: "96px",
|
|
||||||
fontFamily: "-apple-system, Roboto, sans-serif, serif",
|
|
||||||
}
|
|
||||||
const headingStyles = {
|
|
||||||
marginTop: 0,
|
|
||||||
marginBottom: 64,
|
|
||||||
maxWidth: 320,
|
|
||||||
}
|
|
||||||
|
|
||||||
const paragraphStyles = {
|
|
||||||
marginBottom: 48,
|
|
||||||
}
|
|
||||||
const codeStyles = {
|
|
||||||
color: "#8A6534",
|
|
||||||
padding: 4,
|
|
||||||
backgroundColor: "#FFF4DB",
|
|
||||||
fontSize: "1.25rem",
|
|
||||||
borderRadius: 4,
|
|
||||||
}
|
|
||||||
|
|
||||||
// markup
|
|
||||||
const NotFoundPage = () => {
|
|
||||||
return (
|
|
||||||
<main style={pageStyles}>
|
|
||||||
<title>Not found</title>
|
|
||||||
<h1 style={headingStyles}>Page not found</h1>
|
|
||||||
<p style={paragraphStyles}>
|
|
||||||
Sorry{" "}
|
|
||||||
<span role="img" aria-label="Pensive emoji">
|
|
||||||
😔
|
|
||||||
</span>{" "}
|
|
||||||
we couldn’t find what you were looking for.
|
|
||||||
<br />
|
|
||||||
{process.env.NODE_ENV === "development" ? (
|
|
||||||
<>
|
|
||||||
<br />
|
|
||||||
Try creating a page in <code style={codeStyles}>src/pages/</code>.
|
|
||||||
<br />
|
|
||||||
</>
|
|
||||||
) : null}
|
|
||||||
<br />
|
|
||||||
<Link to="/">Go home</Link>.
|
|
||||||
</p>
|
|
||||||
</main>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default NotFoundPage
|
|
@ -1,30 +0,0 @@
|
|||||||
import * as React from "react"
|
|
||||||
import Layout from "../components/layout"
|
|
||||||
import { Link } from "gatsby"
|
|
||||||
|
|
||||||
const IndexPage = () => {
|
|
||||||
return (
|
|
||||||
<Layout>
|
|
||||||
<main className="container main-container">
|
|
||||||
<div className="row">
|
|
||||||
<h1 className="main-title col-12">Documentation</h1>
|
|
||||||
|
|
||||||
<div className="mt-4 col-12">
|
|
||||||
<div className="list-group">
|
|
||||||
<Link to="https://github.com/tweag/nickel/#readme" className="list-group-item list-group-item-action">The
|
|
||||||
Nickel README</Link>
|
|
||||||
<Link to="https://github.com/tweag/nickel/blob/master/RATIONALE.md"
|
|
||||||
className="list-group-item list-group-item-action">Design rationale</Link>
|
|
||||||
<Link to="#" className="list-group-item list-group-item-action
|
|
||||||
disabled">Tutorials<span className={'text-primary'}> - coming soon</span></Link>
|
|
||||||
<Link to="#" className="list-group-item list-group-item-action disabled">The Nickel Manual <span className={'text-primary'}> - coming soon</span></Link>
|
|
||||||
<Link to="#" className="list-group-item list-group-item-action disabled">Language specification<span className={'text-primary'}> - coming soon</span></Link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
</Layout>
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
export default IndexPage
|
|
@ -1,211 +0,0 @@
|
|||||||
import * as React from "react"
|
|
||||||
import {useEffect} from "react"
|
|
||||||
import Layout from "../components/layout"
|
|
||||||
|
|
||||||
import Prism from "prismjs";
|
|
||||||
import "prismjs/components/prism-bash";
|
|
||||||
import "prismjs/components/prism-yaml";
|
|
||||||
import "prismjs/themes/prism-tomorrow.css";
|
|
||||||
import "prismjs/plugins/command-line/prism-command-line";
|
|
||||||
import "prismjs/plugins/command-line/prism-command-line.css";
|
|
||||||
import Playground from "../components/playground-clientside";
|
|
||||||
import modes from "../components/playground/modes";
|
|
||||||
import nickelLanguageDefinition from "../prism/nickel";
|
|
||||||
|
|
||||||
// Escaping curly braces and other stuff in JSX is tiring, so we define all code examples here
|
|
||||||
const codeExamples = {
|
|
||||||
withNix: {
|
|
||||||
clone: `git clone git@github.com:tweag/nickel.git
|
|
||||||
Cloning in 'nickel'...
|
|
||||||
[..]
|
|
||||||
cd nickel`,
|
|
||||||
build: `nix-build
|
|
||||||
[1 built, 0.0 MiB DL]`,
|
|
||||||
run: `./result/bin/nickel -V
|
|
||||||
nickel 0.1.0`,
|
|
||||||
},
|
|
||||||
firstConfig: `{
|
|
||||||
name = "example",
|
|
||||||
description = m#"
|
|
||||||
This is an awesome software I'm developing.
|
|
||||||
Please use it!
|
|
||||||
"#m,
|
|
||||||
version = "0.1.1",
|
|
||||||
main = "index.js",
|
|
||||||
keywords = ["example", "config"],
|
|
||||||
scripts = {
|
|
||||||
test = m#"test.sh --option --install example --version "0.1.1""#m,
|
|
||||||
do_stuff = "do_stuff.sh subcommand",
|
|
||||||
},
|
|
||||||
contributors = [{
|
|
||||||
name = "John Doe",
|
|
||||||
email = "johndoe@example.com"
|
|
||||||
}, {
|
|
||||||
name = "Ivy Lane",
|
|
||||||
url = "https://example.com/ivylane"
|
|
||||||
}],
|
|
||||||
dependencies = {
|
|
||||||
dep1 = "^1.0.0",
|
|
||||||
dep3 = "6.7"
|
|
||||||
}
|
|
||||||
}`,
|
|
||||||
export: `./result/bin/nickel -f example.ncl export --format yaml
|
|
||||||
---
|
|
||||||
contributors:
|
|
||||||
- email: johndoe@example.com
|
|
||||||
name: John Doe
|
|
||||||
- name: Ivy Lane
|
|
||||||
url: https://example.com/ivylane
|
|
||||||
dependencies:
|
|
||||||
dep1: ^1.0.0
|
|
||||||
dep3: "6.7"
|
|
||||||
description: "This is an awesome software I'm developing.\\nPlease use it!"
|
|
||||||
keywords:
|
|
||||||
- example
|
|
||||||
- config
|
|
||||||
main: index.js
|
|
||||||
name: example
|
|
||||||
scripts:
|
|
||||||
do_stuff: do_stuff.sh subcommand
|
|
||||||
test: "test.sh --option --install example --version \\"0.1.1\\""
|
|
||||||
version: 0.1.1`,
|
|
||||||
reuse: {
|
|
||||||
problem: `name = "example",
|
|
||||||
version = "0.1.1",
|
|
||||||
scripts = {
|
|
||||||
test = m#"test.sh --option --install example --version "0.1.1""#m,`,
|
|
||||||
diff: `name = "example",
|
|
||||||
version = "0.1.1",
|
|
||||||
scripts = {
|
|
||||||
test = m#"test.sh --option --install #{name} --version "#{version}""#m`,
|
|
||||||
result: `# [...]
|
|
||||||
scripts:
|
|
||||||
do_stuff: do_stuff.sh subcommand
|
|
||||||
test: "test.sh --option --install example --version \\"0.1.2\\""
|
|
||||||
version: 0.1.2`,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const IndexPage = () => {
|
|
||||||
useEffect(() => {
|
|
||||||
Prism.languages.nickel = nickelLanguageDefinition;
|
|
||||||
Prism.highlightAll();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Layout>
|
|
||||||
<main className="container content-main-container content">
|
|
||||||
<h1 id="getting-started" className={'main-title'}>Getting started</h1>
|
|
||||||
|
|
||||||
<p>Nickel is still young and the installation process is not yet optimal. Sorry about that! We are focused on improving the
|
|
||||||
experience, so stay tuned. </p>
|
|
||||||
|
|
||||||
<h2 id="build-from-source-using-nix">Build from source using Nix</h2>
|
|
||||||
|
|
||||||
<p>Using <a className={"link-primary"} href="https://nixos.org/">Nix</a> is the easiest way
|
|
||||||
to get a Nickel executable
|
|
||||||
running.</p>
|
|
||||||
|
|
||||||
<ol>
|
|
||||||
<li><p>Clone the <a className={"link-primary"} href="https://github.com/tweag/nickel">Nickel
|
|
||||||
repository</a> and set it as the current directory:</p>
|
|
||||||
|
|
||||||
<pre className={'command-line language-bash'} data-user="devops" data-host="nickel"
|
|
||||||
data-output="2-3:"><code>{codeExamples.withNix.clone}</code></pre>
|
|
||||||
</li>
|
|
||||||
<li><p>Invoke <code>nix-build</code>:</p>
|
|
||||||
<pre className={'command-line language-bash'} data-user="devops" data-host="nickel:~/nickel"
|
|
||||||
data-output="2:"><code>{codeExamples.withNix.build}</code></pre>
|
|
||||||
</li>
|
|
||||||
<li><p>If everything went right, a binary is now available in the <code>result</code> directory:</p>
|
|
||||||
<pre className={'command-line language-bash'} data-user="devops" data-host="nickel:~/nickel"
|
|
||||||
data-output="2:"><code>{codeExamples.withNix.run}</code></pre>
|
|
||||||
</li>
|
|
||||||
</ol>
|
|
||||||
|
|
||||||
<h2 id="build-from-source-without-nix">Build from source without Nix</h2>
|
|
||||||
|
|
||||||
<p>Please refer to the <a
|
|
||||||
className={"link-primary"}
|
|
||||||
href="https://github.com/tweag/nickel/#getting-started">README</a> of the <a
|
|
||||||
className={"link-primary"}
|
|
||||||
href="https://github.com/tweag/nickel">Nickel repository</a> for alternative ways of building Nickel.</p>
|
|
||||||
|
|
||||||
<h2 id="write-your-first-configuration">Write your first configuration</h2>
|
|
||||||
|
|
||||||
<p> Nickel has advanced features to help you handle and organize complex configurations (gradual typing, contracts, a merge system, and so on).
|
|
||||||
But you'll only have to deal with any of this once you need to.
|
|
||||||
Writing a basic configuration is as simple as writing JSON or YAML. Let us write a manifest of a fictional app:</p>
|
|
||||||
|
|
||||||
<div className={'d-none d-md-block'}>
|
|
||||||
<Playground fit={'code'} mode={modes.YAML} value={codeExamples.firstConfig}/>
|
|
||||||
</div>
|
|
||||||
<div className={'d-block d-md-none'}>
|
|
||||||
<pre><code className={'language-nickel'}>{codeExamples.firstConfig}</code></pre>
|
|
||||||
</div>
|
|
||||||
<p/>This program is composed of <i>record</i>. A record is the same thing as an object in JSON. It is a list of
|
|
||||||
key-value pairs delimited
|
|
||||||
by <code>{'{'}</code> and <code>{'}'}</code>. In general, the values of Nickel map directly to
|
|
||||||
corresponding values in JSON (excluding functions). Thus, the basic datatypes of Nickel are the same as in JSON:
|
|
||||||
<ul>
|
|
||||||
<li>Records (objects), delimited by <code>{'{'}</code> and <code>{'}'}</code>.</li>
|
|
||||||
<li>Strings, delimited by <code>"</code>. The sequence <code>m#"</code> and <code>"#m</code> delimits multiline strings.
|
|
||||||
</li>
|
|
||||||
<li>Numbers</li>
|
|
||||||
<li>Lists, delimited by <code>[</code> and <code>]</code> and separated by <code>,</code>.</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<p/>Multiline strings are an alternative way of defining strings. Line 11 is an example of such a string. Without diving into the details, multiline strings are
|
|
||||||
useful for:
|
|
||||||
<ul>
|
|
||||||
<li>Write strings spanning several lines, as their name suggests. Multiline strings can be indented at the same
|
|
||||||
level as the surrounding code while still producing the expected result (the common indentation prefix is stripped).
|
|
||||||
</li>
|
|
||||||
<li>Write strings with special characters without having to escape them.</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
In our example, using a multiline string saves us from escaping the recurring double quotes <code>"</code>.
|
|
||||||
<h2 id="export">Export</h2>
|
|
||||||
<p>The ultimate goal of a Nickel program is to produce a static configuration. To do so, save the content of our example above in <code>example.ncl</code> and run <code>nickel export</code>:</p>
|
|
||||||
<pre className={'command-line language-bash'} data-user="devops" data-host="nickel:~/nickel"
|
|
||||||
data-output="2-21:"><code>{codeExamples.export}</code></pre>
|
|
||||||
|
|
||||||
<p>Nickel currently supports exporting to and importing from YAML, TOML and JSON.</p>
|
|
||||||
|
|
||||||
<h2 id="reuse">Reuse</h2>
|
|
||||||
|
|
||||||
<p>Nickel is a programming language. This allows you not only to describe, but to
|
|
||||||
generate data. There's repetition in our previous example:</p>
|
|
||||||
<pre><code className={'language-nickel'}>{codeExamples.reuse.problem}</code></pre>
|
|
||||||
|
|
||||||
<p>The version <code>0.1.1</code> appears both in <code>version</code> and <code>scripts.test</code>.
|
|
||||||
The name <code>example</code> appears both in <code>name</code> and <code>scripts.test</code> as well.
|
|
||||||
Pure aesthetics aside, a more serious issue is inconsistency. If you bump the
|
|
||||||
version number in <code>version</code>, you may forget to do so in the <code>scripts.test</code> as well,
|
|
||||||
ending up wih incoherent version numbers in the same configuration. To remedy the problem, let's have a
|
|
||||||
single source of truth by reusing the value of <code>name</code> and <code>version</code> in <code>scripts.test</code>, using
|
|
||||||
the string interpolation syntax <code>#{'{expr}'}</code>:</p>
|
|
||||||
<pre><code className={'language-nickel'}>{codeExamples.reuse.diff}</code></pre>
|
|
||||||
|
|
||||||
<p>Now, if we change version to <code>0.1.2</code> and export the result, the test script
|
|
||||||
invocation is updated as well:</p>
|
|
||||||
|
|
||||||
<pre><code className={'language-yaml'}>{codeExamples.reuse.result}</code></pre>
|
|
||||||
|
|
||||||
<h2 id="going-further">Going further</h2>
|
|
||||||
|
|
||||||
<p>This short introduction should get you started. Nickel is a
|
|
||||||
full-fledged programming language, featuring higher-order functions, gradual
|
|
||||||
typing, contracts, and more! Additional resources are to come on this website. In the meantime, you can find <a
|
|
||||||
className={"link-primary"}
|
|
||||||
href="https://github.com/tweag/nickel/tree/master/examples">examples in the repository</a>. You will also find more details on the language and its design in the <a
|
|
||||||
className={"link-primary"}
|
|
||||||
href="https://github.com/tweag/nickel/#nickel">README</a> and in the <a
|
|
||||||
className={"link-primary"}
|
|
||||||
href="https://github.com/tweag/nickel/blob/master/RATIONALE.md">design rationale</a>.</p>
|
|
||||||
</main>
|
|
||||||
</Layout>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default IndexPage
|
|
@ -1,128 +0,0 @@
|
|||||||
import * as React from "react"
|
|
||||||
import Layout from "../components/layout"
|
|
||||||
import {StaticImage} from "gatsby-plugin-image";
|
|
||||||
import mergeImage from '../images/merge-2.png';
|
|
||||||
import validateImage from '../images/validate-2.png';
|
|
||||||
import reuseImage from '../images/reuse-2.png';
|
|
||||||
import PlaygroundComponent from "../components/playground-clientside";
|
|
||||||
import modes from "../components/playground/modes";
|
|
||||||
import {Command} from "react-bootstrap-icons";
|
|
||||||
import {
|
|
||||||
faChevronDown,
|
|
||||||
} from '@fortawesome/free-solid-svg-icons';
|
|
||||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
|
||||||
|
|
||||||
const codeExample = `let conf = {
|
|
||||||
name = "NiCl",
|
|
||||||
version = "0.0.1$",
|
|
||||||
description = "My cool app!"
|
|
||||||
} in
|
|
||||||
|
|
||||||
let SemanticVersion = fun label value =>
|
|
||||||
let pattern = "^\\\\d{1,2}\\\\.\\\\d{1,2}(\\\\.\\\\d{1,2})?$" in
|
|
||||||
if strings.isMatch pattern value then
|
|
||||||
value
|
|
||||||
else
|
|
||||||
let msg = "invalid version number" in
|
|
||||||
contracts.blame (contracts.tag msg label)
|
|
||||||
in
|
|
||||||
|
|
||||||
let AppSchema = {
|
|
||||||
name | Str,
|
|
||||||
version | #SemanticVersion,
|
|
||||||
description | Str,
|
|
||||||
} in
|
|
||||||
|
|
||||||
conf | #AppSchema`;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Scroll offset after which the scrolldown arrow is hidden, in pixels.
|
|
||||||
* @type {number}
|
|
||||||
*/
|
|
||||||
const HIDE_SCROLLDOWN_ARROW_AFTER = 100;
|
|
||||||
|
|
||||||
const IndexPage = () => {
|
|
||||||
const [isArrowVisible, setArrowVisible] = React.useState(true);
|
|
||||||
|
|
||||||
const onScroll = () => {
|
|
||||||
const currentScroll = document.body.scrollTop || document.documentElement.scrollTop;
|
|
||||||
|
|
||||||
if(isArrowVisible && currentScroll > HIDE_SCROLLDOWN_ARROW_AFTER) {
|
|
||||||
setArrowVisible(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
window.addEventListener('scroll', onScroll);
|
|
||||||
return () =>
|
|
||||||
window.removeEventListener('scroll', onScroll);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Layout>
|
|
||||||
<main className="container main-container">
|
|
||||||
<section className="row first-section-block">
|
|
||||||
<div className="col-12 text-center">
|
|
||||||
<h1 className="main-title mb-4"><StaticImage className={"logo"} src="../images/nickel-logo-2.svg" alt="logo"/><span className="nickel">Nickel</span></h1>
|
|
||||||
<div className="main-subtitle mt-4 mb-4 title-font">Better configuration
|
|
||||||
for less
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="mt-4 mb-4 main-text">
|
|
||||||
Write complex configurations. Modular, correct and boilerplate-free.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<hr className={'horizontal-sep d-none d-md-block'}/>
|
|
||||||
|
|
||||||
<section className={'row section-block d-none d-md-block'}>
|
|
||||||
<div className="col-12 text-center">
|
|
||||||
<h2 className="mb-4">Try it out. Find the <span className={'landingpage-error'}>error</span>!</h2>
|
|
||||||
<div className="mt-4 mb-4 main-text">
|
|
||||||
This configuration contains an error. Fix it and press <kbd>Ctrl</kbd>+<kbd>Enter</kbd> (or <kbd>Cmd <Command/>
|
|
||||||
</kbd>+<kbd>Enter</kbd>) or click <span className={'btn btn-primary disabled'}>Run</span> to try your solution.
|
|
||||||
</div>
|
|
||||||
<div className={'text-left landingpage-playground-wrapper'}>
|
|
||||||
<PlaygroundComponent value={codeExample} fit={'code'} mode={modes.JSON}/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<hr className={'horizontal-sep'}/>
|
|
||||||
|
|
||||||
<section className="row last-section-block">
|
|
||||||
<div className="col-12 col-lg-4 mb-5 mb-lg-0 main-text text-center landingpage-column">
|
|
||||||
<img src={mergeImage} className="abstract-illustration" alt={""}/>
|
|
||||||
<h3 className="mb-4 mt-4">Merge</h3>
|
|
||||||
<div className="text-left mt-4">
|
|
||||||
Write simple, modular blocks. Merge them into a complex configuration.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="col-12 col-lg-4 mb-5 mb-lg-0 main-text text-center landingpage-column">
|
|
||||||
<img src={validateImage} className="abstract-illustration" alt={""}/>
|
|
||||||
<h3 className="mb-4 mt-4">Verify & Validate</h3>
|
|
||||||
<div className="text-left mt-4">
|
|
||||||
<p>Use (opt-in) static typing to verify functions, if you need to. Let
|
|
||||||
type inference do the boring work.</p>
|
|
||||||
|
|
||||||
<p>Use contracts
|
|
||||||
to validate your data and ensure they conform to a given schema.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="col-12 col-lg-4 main-text text-center landingpage-column">
|
|
||||||
<img src={reuseImage} className="abstract-illustration" alt={""}/>
|
|
||||||
<h3 className="mb-4 mt-4">Reuse</h3>
|
|
||||||
<div className="text-left lt-4">
|
|
||||||
Don't use hacks, don't reinvent the wheel: Nickel is a
|
|
||||||
programming language. Factorize. Reuse the generic parts. Import external libraries.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
{isArrowVisible && <FontAwesomeIcon icon={faChevronDown} className={'scroll-down-arrow'}/>}
|
|
||||||
</main>
|
|
||||||
</Layout>
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
export default IndexPage
|
|
@ -1,21 +0,0 @@
|
|||||||
import * as React from "react"
|
|
||||||
import Layout from "../components/layout"
|
|
||||||
import PlaygroundComponent from "../components/playground-clientside";
|
|
||||||
|
|
||||||
const PlaygroundPage = () => {
|
|
||||||
return (
|
|
||||||
<Layout>
|
|
||||||
<div className={"container-fluid playground-main-container d-flex flex-column"}>
|
|
||||||
<section className={"row"}>
|
|
||||||
<div className={"col-12 text-center"}>
|
|
||||||
<h1 className="main-title">Playground</h1>
|
|
||||||
Experiment with the Nickel REPL online!
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<PlaygroundComponent/>
|
|
||||||
</div>
|
|
||||||
</Layout>
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
export default PlaygroundPage
|
|
@ -1,28 +0,0 @@
|
|||||||
/**
|
|
||||||
* Simple language definition for Nickel to use with the Prism highlighting library.
|
|
||||||
* @type {{number: RegExp, string: RegExp[], builtin: RegExp, punctuation: RegExp[], comment: RegExp, keyword: RegExp[], operator: RegExp[]}}
|
|
||||||
*/
|
|
||||||
const nickel = {
|
|
||||||
comment: /\/\/.+/,
|
|
||||||
string: [
|
|
||||||
{ pattern: /m(#+)"(.|\n)*?"\1m/, greedy: true},
|
|
||||||
{ pattern: /".*?"/, greedy: true},
|
|
||||||
],
|
|
||||||
operator: [
|
|
||||||
/>/, />=/, /</, /<=/, /&/, /==/, /&&/, /\|\|/, /!/, /\+/, /@/, /-/, /\+\+/,
|
|
||||||
],
|
|
||||||
keyword: [
|
|
||||||
/let/,
|
|
||||||
/in/,
|
|
||||||
/fun/,
|
|
||||||
/switch/,
|
|
||||||
/forall/,
|
|
||||||
],
|
|
||||||
punctuation: [
|
|
||||||
/:/, /,/, /;/, /\{/, /}/, /\(/, /\)/, /=/, /\|/, /#/,
|
|
||||||
],
|
|
||||||
number: /[0-9]*\.?[0-9]+/,
|
|
||||||
builtin: /((?:Dyn)|(?:Num)|(?:Bool)|(?:Str)|(?:List:(?:[a-zA-Z0-9_]*)?))/,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default nickel;
|
|
@ -1,108 +0,0 @@
|
|||||||
@use "sass:map";
|
|
||||||
@use "sass:color";
|
|
||||||
|
|
||||||
$theme-colors: (
|
|
||||||
"primary": #e0c3fc,
|
|
||||||
"secondary": #8ec5fc,
|
|
||||||
);
|
|
||||||
|
|
||||||
@import '~bootstrap/scss/bootstrap.scss';
|
|
||||||
|
|
||||||
a:hover { text-decoration: none};
|
|
||||||
|
|
||||||
/* Correctly style links with our custom primary and secondary colors */
|
|
||||||
|
|
||||||
@mixin link($color) {
|
|
||||||
color: $color;
|
|
||||||
&:hover, &:focus {
|
|
||||||
color: color.adjust($color, $lightness: -30%);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
background-color: color.adjust($color, $lightness: +10%) !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@mixin link-theme($color) {
|
|
||||||
$selected: map.get($theme-colors, $color);
|
|
||||||
@include link(color.adjust($selected, $lightness: -25%));
|
|
||||||
}
|
|
||||||
|
|
||||||
.link-primary {
|
|
||||||
@include link-theme("primary");
|
|
||||||
}
|
|
||||||
|
|
||||||
.link-secondary {
|
|
||||||
@include link-theme("secondary");
|
|
||||||
}
|
|
||||||
|
|
||||||
.link-footer {
|
|
||||||
color: #757575;
|
|
||||||
&:hover, &:focus {
|
|
||||||
color: black;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.horizontal-sep {
|
|
||||||
height: 5px;
|
|
||||||
background: map.get($theme-colors, "primary");
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
h1, h2, h3, h4, h5 {
|
|
||||||
margin-bottom: 0.5em;
|
|
||||||
margin-top: 2.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1:first-child {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Augment bootstrap with breakpoint variants of w-100, w-50, and so on. */
|
|
||||||
|
|
||||||
@each $breakpoint in map-keys($grid-breakpoints) {
|
|
||||||
@each $size, $length in $sizes {
|
|
||||||
@include media-breakpoint-up($breakpoint) {
|
|
||||||
.w-#{$breakpoint}-#{$size} {width: $length !important;}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Animation of the landing page's scrolldown arrow */
|
|
||||||
|
|
||||||
.scroll-down-arrow {
|
|
||||||
height: 60px;
|
|
||||||
width: 80px;
|
|
||||||
margin: 0px 0 0 -40px;
|
|
||||||
line-height: 60px;
|
|
||||||
position: absolute;
|
|
||||||
left: 50%;
|
|
||||||
bottom: 0px;
|
|
||||||
color: black;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 70px;
|
|
||||||
z-index: 100;
|
|
||||||
text-decoration: none;
|
|
||||||
text-shadow: 0px 0px 3px rgba(0, 0, 0, 0.4);
|
|
||||||
|
|
||||||
-webkit-animation: ca3_fade_move_down 1.5s ease-in-out infinite;
|
|
||||||
-moz-animation: ca3_fade_move_down 1.5s ease-in-out infinite;
|
|
||||||
animation: ca3_fade_move_down 1.5s ease-in-out infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
@-webkit-keyframes ca3_fade_move_down {
|
|
||||||
0% { -webkit-transform:translate(0,-20px); opacity: 0; }
|
|
||||||
50% { opacity: 1; }
|
|
||||||
100% { -webkit-transform:translate(0,20px); opacity: 0; }
|
|
||||||
}
|
|
||||||
@-moz-keyframes ca3_fade_move_down {
|
|
||||||
0% { -moz-transform:translate(0,-20px); opacity: 0; }
|
|
||||||
50% { opacity: 1; }
|
|
||||||
100% { -moz-transform:translate(0,20px); opacity: 0; }
|
|
||||||
}
|
|
||||||
@keyframes ca3_fade_move_down {
|
|
||||||
0% { transform:translate(0,-20px); opacity: 0; }
|
|
||||||
50% { opacity: 1; }
|
|
||||||
100% { transform:translate(0,20px); opacity: 0; }
|
|
||||||
}
|
|
@ -1,256 +0,0 @@
|
|||||||
.main-title {
|
|
||||||
font-size: 3em;
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-subtitle {
|
|
||||||
font-size: 2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-text {
|
|
||||||
font-size: 1.5em;
|
|
||||||
line-height: 1.3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.playground-loader {
|
|
||||||
font-size: 1.5em;
|
|
||||||
line-height: 1.3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.secondary-text {
|
|
||||||
font-size: 1.3em;
|
|
||||||
line-height: 1.2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-container {
|
|
||||||
padding-top: 5em;
|
|
||||||
padding-bottom: 5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-main-container {
|
|
||||||
margin-top: 5em;
|
|
||||||
padding-bottom: 5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.playground-main-container {
|
|
||||||
margin-top: 5em;
|
|
||||||
margin-bottom: 5em;
|
|
||||||
height: 80vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
.playground-terminal {
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.playground-tab {
|
|
||||||
margin-top: 2.5em;
|
|
||||||
margin-bottom: 0.5em;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.playground-container {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.playground-terminal-container {
|
|
||||||
max-height: 100%;
|
|
||||||
padding: 10px;
|
|
||||||
overflow-y: scroll;
|
|
||||||
overflow-x: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
#playground-input {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.playground-nav-item {
|
|
||||||
color: black;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.playground-nav-item.active {
|
|
||||||
color: black !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar {
|
|
||||||
font-size: 1.2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section-block {
|
|
||||||
padding-top: 12.5em;
|
|
||||||
padding-bottom: 12.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.first-section-block {
|
|
||||||
padding-top: 7.5em;
|
|
||||||
padding-bottom: 12.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.last-section-block {
|
|
||||||
padding-top: 12.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.landingpage-column {
|
|
||||||
padding-left: 2em;
|
|
||||||
padding-right: 2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.landingpage-playground-wrapper {
|
|
||||||
margin-top: 5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.landingpage-error {
|
|
||||||
color: #D44045;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo {
|
|
||||||
height: auto;
|
|
||||||
width: 100px;
|
|
||||||
vertical-align: middle;
|
|
||||||
margin-right: 0.3em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo-navbar {
|
|
||||||
height: auto;
|
|
||||||
width: 50px;
|
|
||||||
vertical-align: middle;
|
|
||||||
margin-right: 0.3em;
|
|
||||||
}
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: "Century Gothic";
|
|
||||||
src: url('../fonts/century-gothic.woff')
|
|
||||||
}
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: "Bureau Grotesque";
|
|
||||||
src: url('../fonts/bureau-grotesque-55-regular.woff')
|
|
||||||
}
|
|
||||||
|
|
||||||
.nickel {
|
|
||||||
font-family: "Century Gothic";
|
|
||||||
}
|
|
||||||
|
|
||||||
.title-font, h1, h2, h3 {
|
|
||||||
font-family: "Bureau Grotesque";
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-elt {
|
|
||||||
padding-top: 0.2em;
|
|
||||||
padding-bottom: 0.2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.abstract-illustration {
|
|
||||||
height: 6em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.code {
|
|
||||||
word-wrap: normal;
|
|
||||||
background-color: #f8f9fa;
|
|
||||||
padding: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.code pre {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.code pre > code {
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
word-break: normal;
|
|
||||||
white-space: pre;
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.full-page {
|
|
||||||
min-height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
.overflow-scroll {
|
|
||||||
overflow: scroll;
|
|
||||||
}
|
|
||||||
|
|
||||||
.asciinema-iframe {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ansi-monokai {
|
|
||||||
background: #2F3129;
|
|
||||||
color: #8F908A;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ansi-monokai code {
|
|
||||||
color: #8F908A;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ansi-black-bg {
|
|
||||||
color: #002b36;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ansi-black-fg .ansi-bright-black-fg {
|
|
||||||
color: #073642;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ansi-green-bg {
|
|
||||||
color: #586e75;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ansi-yellow-bg {
|
|
||||||
color: #657b83;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ansi-blue-bg {
|
|
||||||
color: #839496;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ansi-cyan-bg {
|
|
||||||
color: #93a1a1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ansi-white-fg .ansi-bright-white-fg {
|
|
||||||
color: #eee8d5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ansi-white-bg {
|
|
||||||
color: #fdf6e3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ansi-yellow-fg .ansi-bright-yellow-fg {
|
|
||||||
color: #b58900;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ansi-red-bg {
|
|
||||||
color: #cb4b16;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ansi-red-fg, .ansi-bright-red-fg {
|
|
||||||
color: #dc322f;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ansi-magenta-fg, .ansi-bright-magenta-fg {
|
|
||||||
color: #d33682;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ansi-magenta-fb {
|
|
||||||
color: #6c71c4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ansi-blue-fg, .ansi-bright-blue-fg {
|
|
||||||
color: #268bd2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ansi-cyan-fg, .ansi-bright-cyan-fg {
|
|
||||||
color: #2aa198;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ansi-green-fg, .ansi-bright-green-fg {
|
|
||||||
color: #859900;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ansi-bright-black-fg, .ansi-bright-red-fg, .ansi-bright-green-fg, .ansi-bright-yellow-fg,
|
|
||||||
.ansi-bright-blue-fg, .ansi-bright-magenta-fg, .ansi-bright-cyan-fg, .ansi-bright-white-fg {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|