Set up nacara
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,6 +1,7 @@
|
||||
build/sample/*
|
||||
build/test/*
|
||||
!build/test/__snapshots__
|
||||
build/sample/*
|
||||
build/docs/*
|
||||
dist/
|
||||
|
||||
.idea/
|
||||
|
||||
196
README.md
196
README.md
@@ -1,196 +1,26 @@
|
||||
# Fable.Lit
|
||||
|
||||
Fable.Lit is a collection of tools to help you write [Fable](https://fable.io/) apps by embedding HTML code into your F# code with the power of [Lit](https://lit.dev/). Thanks to this, you can use HTML from a designer or a [component library](https://ionicframework.com/docs/api/) right away, without any kind of conversion to F#. Lit only weighs 5KB minified and gzipped so it's very cheap to integrate in your existing app (see below for React integration). If you're using VS Code and install the [Highlight HTML/SQL templates in F#](https://marketplace.visualstudio.com/items?itemName=alfonsogarciacaro.vscode-template-fsharp-highlight) extension the integration will be even smoother.
|
||||
Fable.Lit is a collection of tools to help you write [Fable](https://fable.io/) apps by embedding HTML code into your F# code with the power of [Lit](https://lit.dev/).
|
||||
|
||||
There's an example in the `sample` directory, but you can find [here a more detailed tutorial](https://dev.to/tunaxor/using-lit-html-with-f-3i8b) by Angel Munoz.
|
||||
Before doing anything make sure to install the dependencies after cloning the repository by running:
|
||||
|
||||
## Requirements
|
||||
`npm install`
|
||||
|
||||
Fable.Lit packages require **fable 3.3** dotnet tool and **Lit 2** from npm which, at the time of writing, is in release candidate state.
|
||||
## How to test locally ?
|
||||
|
||||
```
|
||||
dotnet tool update fable
|
||||
npm install lit@next
|
||||
```
|
||||
`npm run test`
|
||||
|
||||
Then, in the directory of your .fsproj, install the packages you need (see below for details), which are also in prelease. Note the package ids are prefixed by `Fable.` but not the actual namespaces.
|
||||
## How to publish a new version of the package ?
|
||||
|
||||
```
|
||||
dotnet add package Fable.Lit --prerelease
|
||||
dotnet add package Fable.Lit.React --prerelease
|
||||
dotnet add package Fable.Lit.Elmish --prerelease
|
||||
```
|
||||
`npm run publish`
|
||||
|
||||
## Lit
|
||||
## How to work on the documentation ?
|
||||
|
||||
Fable.Lit contains bindings and extra helpers for Lit. Please read [Lit documentation](https://lit.dev/docs/templates/overview/) to learn how Lit templates work.
|
||||
1. `npm run docs -- watch`
|
||||
2. Go to [http://localhost:8080/](http://localhost:8080/)
|
||||
|
||||
When you open the `Lit` namespace, you will have access to:
|
||||
## How to update the documentation ?
|
||||
|
||||
- `html` and `svg` helpers, which convert F# interpolated strings into Lit templates
|
||||
- `LitBindings` static class containing _raw_ bindings for Lit (normally you don't need to use this)
|
||||
- `Lit` static class containing wrappers for Lit in a more F# idiomatic fashion
|
||||
Deployment should be done automatically when pushing to `main` branch.
|
||||
|
||||
E.g. Lit [repeat](https://lit.dev/docs/templates/directives/#repeat) directive becomes `Lit.mapUnique` to map a sequence of items into `Lit.TemplateResult` and assign each a unique id. This is important to identify the items when the list is going to be sorted or filtered. For static lists passing the sequence directly just works.
|
||||
|
||||
```fsharp
|
||||
let renderList items =
|
||||
let renderItem item =
|
||||
html $"""<li>Value: <strong>{item.Value}</strong></li>"""
|
||||
|
||||
html $"""<ul>{items |> Lit.mapUnique (fun x -> x.Id) renderItem}</ul>"""
|
||||
```
|
||||
|
||||
### HookComponent
|
||||
|
||||
Fable.Lit includes the `HookComponent` attribute. When you decorate a view function with it, this lets you use [hooks](https://reactjs.org/docs/hooks-overview.html) in a similar way as [ReactComponent](https://zaid-ajaj.github.io/Feliz/#/Feliz/React/NotJustFunctions) attribute does. Hook support is included in Fable.Lit's F# code and doesn't require any extra JS dependency besides Lit.
|
||||
|
||||
```fsharp
|
||||
[<HookComponent>]
|
||||
let NameInput() =
|
||||
// Lit.Hook API is currently evolving, we try to emulate React's API but there may be some differences
|
||||
let value, setValue = Hook.useState "World"
|
||||
let inputRef = Hook.useRef<HTMLInputElement>()
|
||||
|
||||
html $"""
|
||||
<div class="content">
|
||||
<p>Hello {value}!</p>
|
||||
<input
|
||||
value={value}
|
||||
{Lit.refValue inputRef}
|
||||
@focus={fun _ ->
|
||||
inputRef.value |> Option.iter (fun el -> el.select())}
|
||||
@keyup={fun (ev: Event) ->
|
||||
ev.target.Value |> setValue}>
|
||||
</div>
|
||||
"""
|
||||
```
|
||||
|
||||
> Note that hook components are just a way to keep state between renders and are not [web components](https://www.webcomponents.org/introduction). We plan to add bindings to define web components with [lit](https://lit.dev) in the near future. Also check [Fable.Haunted](https://github.com/AngelMunoz/Fable.Haunted) by Angel Munoz to define actual web components with React-style hooks.
|
||||
|
||||
### Hook.useElmish
|
||||
|
||||
Thanks to the great work by [Cody Johnson](https://twitter.com/Cody_S_Johnson) with [Feliz.UsElmish](https://zaid-ajaj.github.io/Feliz/#/Hooks/UseElmish), Fable.Lit HookComponents also include `useElmish` hook to manage the internal state of your components using the model-view-update architecture.
|
||||
|
||||
```fsharp
|
||||
open Elmish
|
||||
open Lit
|
||||
|
||||
type Model = ..
|
||||
type Msg = ..
|
||||
|
||||
let init() = ..
|
||||
let update msg model = ..
|
||||
let view model dispatch = ..
|
||||
|
||||
[<HookComponent>]
|
||||
let Clock(): TemplateResult =
|
||||
let model, dispatch = Hook.useElmish(init, update)
|
||||
view model dispatch
|
||||
```
|
||||
|
||||
## Lit.React
|
||||
|
||||
Fable.Lit.React package contains helpers to integrate Lit with React in both directions: either by rendering a React component with an HTML template or by embedding a React component in an HTML template. This makes it possible to add raw HTML to your apps whenever you need it, no matter you're using [Fable.React](https://github.com/fable-compiler/fable-react/) bindings or [Zaid Ajaj](https://twitter.com/zaid_ajaj)'s [Feliz](https://zaid-ajaj.github.io/Feliz/) API.
|
||||
|
||||
> If you're comfortable with [JSX](https://reactjs.org/docs/introducing-jsx.html) and Typescript/JS, it's also easy to [invoke them from Feliz](https://zaid-ajaj.github.io/Feliz/#/Feliz/UsingJsx) if that suits your needs better.
|
||||
|
||||
Use `React.lit_html` (or svg) to include the string template directly. Or transform an already-compiled template with `React.ofLit: Lit.TemplateResult -> ReactElement`. These helpers use hooks so they must be called directly in the root of a React component.
|
||||
|
||||
```fsharp
|
||||
[<ReactComponent>]
|
||||
let Clock () =
|
||||
let time, setTime = React.useState DateTime.Now
|
||||
|
||||
React.useEffectOnce(fun () ->
|
||||
let id = JS.setInterval (fun _ -> DateTime.Now |> setTime) 1000
|
||||
React.createDisposable(fun () ->
|
||||
JS.clearInterval id))
|
||||
|
||||
// If the template were in another function we would call
|
||||
// view time |> React.ofLit
|
||||
|
||||
React.lit_html $"""
|
||||
<svg viewBox="0 0 100 100"
|
||||
width="350px">
|
||||
<circle
|
||||
cx="50"
|
||||
cy="50"
|
||||
r="45"
|
||||
fill="#0B79CE"></circle>
|
||||
|
||||
{clockHand time.AsHour}
|
||||
{clockHand time.AsMinute}
|
||||
{clockHand time.AsSecond}
|
||||
|
||||
<circle
|
||||
cx="50"
|
||||
cy="50"
|
||||
r="3"
|
||||
fill="#0B79CE"
|
||||
stroke="#023963"
|
||||
stroke-width="1">
|
||||
</circle>
|
||||
</svg>
|
||||
"""
|
||||
```
|
||||
|
||||
Use `React.toLit` to transform a React component into a Lit renderer function. Store the transformed function in a static value to make sure a new React component is not instantiated for every render:
|
||||
|
||||
```fsharp
|
||||
module ReactLib =
|
||||
open Fable.React
|
||||
open Fable.React.Props
|
||||
|
||||
[<ReactComponent>]
|
||||
let MyComponent showClock =
|
||||
let state = Hooks.useState 0
|
||||
div [ Class "card" ] [
|
||||
div [ Class "card-content" ] [
|
||||
div [ Class "content" ] [
|
||||
p [] [str $"""I'm a React component. Clock is {if showClock then "visible" else "hidden"}"""]
|
||||
button [
|
||||
Class "button"
|
||||
OnClick (fun _ -> state.update(state.current + 1))
|
||||
] [ str $"""Clicked {state.current} time{if state.current = 1 then "" else "s"}!"""]
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
open Lit
|
||||
|
||||
let ReactLitComponent =
|
||||
React.toLit ReactLib.MyComponent
|
||||
|
||||
// Now you can embed the React component into your Lit template
|
||||
let view model dispatch =
|
||||
html $"""
|
||||
<div class="vertical-container">
|
||||
{ReactLitComponent model.ShowClock}
|
||||
{if model.ShowClock then Clock.Clock() else Lit.nothing}
|
||||
</div>
|
||||
"""
|
||||
```
|
||||
|
||||
## Lit.Elmish
|
||||
|
||||
Fable.Lit.Elmish allows you to write a frontend app using the popular [Elmish](https://elmish.github.io/) library by [Eugene Tolmachev](https://github.com/et1975) with a view function returning `Lit.TemplateResult`. The package also includes support for [Hot Module Replacement](https://webpack.js.org/concepts/hot-module-replacement/) out-of-the-box thanks to [Maxime Mangel](https://twitter.com/MangelMaxime) original work with Elmish.HMR.
|
||||
|
||||
```fsharp
|
||||
open Elmish
|
||||
open Lit
|
||||
|
||||
type Model = ..
|
||||
type Msg = ..
|
||||
|
||||
let init() = ..
|
||||
let update msg model = ..
|
||||
let view model dispatch = ..
|
||||
|
||||
open Lit.Elmish
|
||||
open Lit.Elmish.HMR
|
||||
|
||||
Program.mkProgram initialState update view
|
||||
|> Program.withLit "app-container"
|
||||
|> Program.run
|
||||
```
|
||||
If the CI is broken, you can manually deploy it by running `npm run docs:deploy`.
|
||||
|
||||
182
docsrc/docs/computation-expression.md
Normal file
182
docsrc/docs/computation-expression.md
Normal file
@@ -0,0 +1,182 @@
|
||||
---
|
||||
title: Computation expression
|
||||
layout: nacara-standard
|
||||
---
|
||||
|
||||
## Introduction
|
||||
|
||||
The `promise` computation expression makes it really easy to create and compose promise using F#.
|
||||
|
||||
<div class="columns" date-disable-copy-button="true">
|
||||
<div class="column is-half-desktop">
|
||||
|
||||
<div class="has-text-centered mb-2 has-text-weight-semibold">Pipeline API</div>
|
||||
|
||||
```fsharp
|
||||
fetch "https://x.x/users"
|
||||
|> Promise.map (fun response ->
|
||||
fetch "https://x.x/posts"
|
||||
)
|
||||
|> Promise.map (fun response ->
|
||||
// Done, do something with the result
|
||||
)
|
||||
```
|
||||
|
||||
</div>
|
||||
<div class="column is-half-desktop">
|
||||
|
||||
<div class="has-text-centered mb-2 has-text-weight-semibold">Computation expression</div>
|
||||
|
||||
```fsharp
|
||||
promise {
|
||||
let! users = fetch "https://x.x/users"
|
||||
let! posts = fetch "https://x.x/posts"
|
||||
|
||||
// Done, do something with the result
|
||||
return //...
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
## Guides
|
||||
|
||||
Here is a quick guides of what you can do with `promise` computations.
|
||||
|
||||
You can read more about computation expression in F# [here](https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/computation-expressions).
|
||||
|
||||
### Create a promise
|
||||
|
||||
Creating a promise is as simple as writing `promise { }`.
|
||||
|
||||
```fsharp
|
||||
let double (value : int) =
|
||||
promise {
|
||||
return value * 2
|
||||
}
|
||||
```
|
||||
|
||||
### Chaining promises
|
||||
|
||||
If you need the result of a promise before calling another one, use the `let!` keyword
|
||||
|
||||
```fsharp
|
||||
promise {
|
||||
let! user = fetchUsers session
|
||||
let! permission = fetchPermission user
|
||||
|
||||
return permission
|
||||
}
|
||||
```
|
||||
|
||||
You can also directly return the result of a promise avoiding to use `let!` and `return`
|
||||
|
||||
`return!` will evaluate the promise and return the result value when completed
|
||||
|
||||
```fsharp
|
||||
promise {
|
||||
let! user = fetchUsers session
|
||||
|
||||
return! fetchPermission user
|
||||
}
|
||||
```
|
||||
|
||||
### Nesting promises
|
||||
|
||||
You can nest `promise` computation as needed.
|
||||
|
||||
```fsharp
|
||||
promise {
|
||||
// Nested promise which returns a value
|
||||
let! isValid =
|
||||
promise {
|
||||
// Do something
|
||||
return aValue
|
||||
}
|
||||
|
||||
// Nested promise which return unit
|
||||
do! promise {
|
||||
// Do something
|
||||
return ()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Parallel
|
||||
|
||||
If you have independent promise, you can use `let!` and `and!` to run them in parallel.
|
||||
|
||||
```fsharp
|
||||
let p1 =
|
||||
promise {
|
||||
do! Promise.sleep 100
|
||||
return 1
|
||||
}
|
||||
let p2 =
|
||||
promise {
|
||||
do! Promise.sleep 200
|
||||
return 2
|
||||
}
|
||||
let p3 =
|
||||
promise {
|
||||
do! Promise.sleep 300
|
||||
return 3
|
||||
}
|
||||
|
||||
promise {
|
||||
let! a = p1
|
||||
and! b = p2
|
||||
and! c = p3
|
||||
|
||||
return a + b + c // 1 + 2 + 3 = 6
|
||||
}
|
||||
```
|
||||
|
||||
### Support non-native promise (aka Thenable)
|
||||
|
||||
In JavaScript, a thenable is an object that has a `then()` function. All promises are thenables, but not all thenables are promises.
|
||||
|
||||
For example, this is the case when working on a VSCode extensions, or with mongoose, axios.
|
||||
|
||||
[Source](https://masteringjs.io/tutorials/fundamentals/thenable)
|
||||
|
||||
You can extends the `promise` extension to make it easy to works with your thenable.
|
||||
|
||||
Example:
|
||||
|
||||
```fsharp
|
||||
/// This is the definition of a thenable from ts2fable's generation VsCode API
|
||||
type [<AllowNullLiteral>] Thenable<'T> =
|
||||
abstract ``then``: ?onfulfilled: ('T -> U2<'TResult, Thenable<'TResult>>) * ?onrejected: (obj option -> U2<'TResult, Thenable<'TResult>>) -> Thenable<'TResult>
|
||||
abstract ``then``: ?onfulfilled: ('T -> U2<'TResult, Thenable<'TResult>>) * ?onrejected: (obj option -> unit) -> Thenable<'TResult>
|
||||
|
||||
module Thenable =
|
||||
// Transform a thenable into a promise
|
||||
let toPromise (t: Thenable<'t>): JS.Promise<'t> = unbox t
|
||||
|
||||
type Promise.PromiseBuilder with
|
||||
/// To make a value interop with the promise builder, you have to add an
|
||||
/// overload of the `Source` member to convert from your type to a promise.
|
||||
/// Because thenables are trivially convertible, we can just unbox them.
|
||||
member x.Source(t: Thenable<'t>): JS.Promise<'t> = Thenable.toPromise t
|
||||
|
||||
// Also provide these cases for overload resolution
|
||||
member _.Source(p: JS.Promise<'T1>): JS.Promise<'T1> = p
|
||||
member _.Source(ps: #seq<_>): _ = ps
|
||||
|
||||
// You can now works with instance of Thenable from the promise computation
|
||||
|
||||
// Dummy thenable for the example
|
||||
let sampleThenable () =
|
||||
promise {
|
||||
return 1
|
||||
}
|
||||
|> Thenable.ofPromise
|
||||
|
||||
// See how you can use the thenable directly from the promise computation
|
||||
promise {
|
||||
let! initialValue = sampleThenable()
|
||||
// ...
|
||||
}
|
||||
```
|
||||
39
docsrc/docs/getting-started.md
Normal file
39
docsrc/docs/getting-started.md
Normal file
@@ -0,0 +1,39 @@
|
||||
---
|
||||
title: Getting started
|
||||
layout: nacara-standard
|
||||
---
|
||||
|
||||
Add `Fable.Promise` package to your project.
|
||||
|
||||
If you are using nuget to manage you dependencies:
|
||||
|
||||
`dotnet add package Fable.Promise`
|
||||
|
||||
If you are using [paket](https://fsprojects.github.io/Paket/):
|
||||
|
||||
`dotnet paket add Fable.Promise`
|
||||
|
||||
You are ready to go, you can directly access `Promise` module or the `promise` computation.
|
||||
|
||||
Example:
|
||||
|
||||
```fsharp
|
||||
let helloPromise =
|
||||
Promise.create (fun resolve reject ->
|
||||
resolve "Hello, from a promise"
|
||||
)
|
||||
|
||||
// Pipeline style
|
||||
helloPromise
|
||||
|> Promise.iter (fun message ->
|
||||
printfn message
|
||||
// Output: Hello, from a promise
|
||||
)
|
||||
|
||||
// Using computation expression
|
||||
promise {
|
||||
let! message = helloPromise
|
||||
printfn message
|
||||
// Output: Hello, from a promise
|
||||
}
|
||||
```
|
||||
11
docsrc/docs/menu.json
Normal file
11
docsrc/docs/menu.json
Normal file
@@ -0,0 +1,11 @@
|
||||
[
|
||||
"docs/getting-started",
|
||||
{
|
||||
"type": "category",
|
||||
"label": "API",
|
||||
"items": [
|
||||
"docs/pipeline",
|
||||
"docs/computation-expression"
|
||||
]
|
||||
}
|
||||
]
|
||||
476
docsrc/docs/pipeline.md
Normal file
476
docsrc/docs/pipeline.md
Normal file
@@ -0,0 +1,476 @@
|
||||
---
|
||||
title: Pipeline
|
||||
layout: nacara-standard
|
||||
---
|
||||
|
||||
## Introduction
|
||||
|
||||
Pipeline style allows you use to chain your promise using the pipe operator `|>`.
|
||||
|
||||
Writing your code using the pipeline style makes your code looks similar to what you would write in JavaScript.
|
||||
|
||||
<div class="columns" date-disable-copy-button="true">
|
||||
<div class="column is-half-desktop">
|
||||
|
||||
<div class="has-text-centered mb-2 has-text-weight-semibold">JavaScript</div>
|
||||
|
||||
```js
|
||||
fetch('https://my-api.com/users')
|
||||
.then(function (response) {
|
||||
return fetch('https://my-api.com/posts')
|
||||
})
|
||||
.then(function (response) {
|
||||
// Done, do something with the result
|
||||
})
|
||||
.catch(function (req) {
|
||||
// An error ocurred
|
||||
})
|
||||
```
|
||||
|
||||
</div>
|
||||
<div class="column is-half-desktop">
|
||||
|
||||
<div class="has-text-centered mb-2 has-text-weight-semibold">F#</div>
|
||||
|
||||
```fsharp
|
||||
fetch "https://my-api.com/users"
|
||||
|> Promise.map (fun response ->
|
||||
fetch "https://my-api.com/posts"
|
||||
)
|
||||
|> Promise.map (fun response ->
|
||||
// Done, do something with the result
|
||||
)
|
||||
|> Promise.catch (fun error ->
|
||||
// An error ocurred
|
||||
)
|
||||
```
|
||||
</div>
|
||||
</div>
|
||||
|
||||
## API
|
||||
|
||||
Description and examples for all the pipeline API.
|
||||
|
||||
### Promise.create
|
||||
|
||||
`create: f: (('T -> unit) -> (exn -> unit) -> unit) -> Promise<'T>`
|
||||
|
||||
Create a promise from a function.
|
||||
|
||||
```fsharp
|
||||
let write (path: string) (content: string) =
|
||||
Promise.create (fun resolve reject ->
|
||||
Node.Api.fs.writeFile(path, content, (fun res ->
|
||||
match res with
|
||||
| Some res -> reject (res :?> System.Exception)
|
||||
| None -> resolve ()
|
||||
))
|
||||
)
|
||||
```
|
||||
|
||||
### Promise.sleep
|
||||
|
||||
`sleep: ms: int -> Promise<unit>`
|
||||
|
||||
Create a promise which wait `X` ms before resolving.
|
||||
|
||||
```fsharp
|
||||
// Do something
|
||||
doSomething ()
|
||||
// Sleep for 1 second
|
||||
|> Promise.sleep 1000
|
||||
// Do another thing
|
||||
|> Promise.map (fun _ ->
|
||||
doAnotherThing ()
|
||||
)
|
||||
|> Promise.map ...
|
||||
```
|
||||
|
||||
### Promise.lift
|
||||
|
||||
`lift: a: 'T -> Promise<'T>`
|
||||
|
||||
Create a promise (in resolved state) with supplied value.
|
||||
|
||||
```fsharp
|
||||
Promise.lift {| Firstname = "John" |}
|
||||
|> Promise.map (fun user ->
|
||||
console.log $"Hello, %s{user.Firstname}"
|
||||
// Expected output: "Hello, John"
|
||||
)
|
||||
|> Promise.map ...
|
||||
```
|
||||
|
||||
### Promise.reject
|
||||
|
||||
`reject: reason: exn -> Promise<'T>`
|
||||
|
||||
Create a promise (in rejected state) with supplied reason.
|
||||
|
||||
```fsharp
|
||||
Promise.reject "User not found"
|
||||
|> Promise.map (fun _ ->
|
||||
// This promise is skipped
|
||||
)
|
||||
|> Promise.catch (fun errorMessage ->
|
||||
console.error $"An error ocurred: %s{errorMessage}"
|
||||
// Expected output: "An error ocurred: User not found"
|
||||
)
|
||||
|> Promise.map ...
|
||||
```
|
||||
|
||||
### Promise.bind
|
||||
|
||||
`bind: a : ('T1 -> JS.Promise<'T2>) -> pr: Promise<'T1> -> Promise<'T2>`
|
||||
|
||||
Bind a value into a promise of a new type.
|
||||
|
||||
```fsharp
|
||||
Promise.lift {| Firstname = "John" |}
|
||||
|> Promise.bind (fun user ->
|
||||
// Do something with user and returns a promise
|
||||
Promise.create (fun resolve reject ->
|
||||
resolve $"Hello, %s{user.Firstname}"
|
||||
)
|
||||
)
|
||||
|> Promise.map (fun message ->
|
||||
console.log message
|
||||
// Expected output: "Hello, John"
|
||||
)
|
||||
|> Promise.map ...
|
||||
```
|
||||
|
||||
### Promise.map
|
||||
|
||||
`map: a : ('T1 -> 'T2) -> pr: Promise<'T1> -> Promise<'T2>`
|
||||
|
||||
Map a value into another type, the result will be wrapped in a promise for you.
|
||||
|
||||
```fsharp
|
||||
Promise.lift {| Firstname = "John" |}
|
||||
|> Promise.map (fun user ->
|
||||
user.Firstname
|
||||
) // Returns a Promise<string> with the value "John"
|
||||
|> Promise.map ...
|
||||
```
|
||||
|
||||
### Promise.iter
|
||||
|
||||
`iter: a : ('T -> unit) -> pr: Promise<'T> -> unit`
|
||||
|
||||
Call a function with the result of a promise and stop the promise chain.
|
||||
|
||||
This is equivalent to `Promise.map ... |> ignore`
|
||||
|
||||
```fsharp
|
||||
fetchUser ()
|
||||
|> Promise.iter (fun user ->
|
||||
console.log "User firstname is user.Firstname"
|
||||
) // unit
|
||||
```
|
||||
|
||||
### Promise.catch
|
||||
|
||||
`catch: fail: (exn -> 'T) -> pr : Promise<'T> -> Promise<'T>`
|
||||
|
||||
Handle an errored promise allowing you pass a return value.
|
||||
|
||||
This version of `catch` fakes a function returning just `'T`, as opposed to `Promise<'T>`. If you need to return `Promise<'T>`, use `catchBind`.
|
||||
|
||||
```fsharp
|
||||
Promise.create (fun resolve reject ->
|
||||
reject (System.Exception "User not found")
|
||||
)
|
||||
|> Promise.catch (fun error ->
|
||||
// Log the error
|
||||
console.error error
|
||||
// Do something to recover from the error
|
||||
Error error.Message
|
||||
)
|
||||
|> Promise.map ...
|
||||
```
|
||||
|
||||
### Promise.catchBind
|
||||
|
||||
`catchBind: fail: (Exception -> JS.Promise<'T>) -> pr : Promise<'T> -> Promise<'T>`
|
||||
|
||||
Handle an errored promise allowing to call a promise.
|
||||
|
||||
This version of `catch` expects a function returning `Promise<'T>` as opposed to just `'T`. If you need to return just `'T`, use `catch`.
|
||||
|
||||
```fsharp
|
||||
Promise.create (fun resolve reject ->
|
||||
reject (System.Exception "User not found")
|
||||
)
|
||||
|> Promise.catchBind (fun error ->
|
||||
// Recover from the error, here we call another promise and returns it's result
|
||||
logErrorToTheServer error
|
||||
)
|
||||
|> Promise.map ...
|
||||
```
|
||||
|
||||
### Promise.catchEnd
|
||||
|
||||
`catchEnd: fail: (exn -> unit) -> pr : Promise<'T> -> unit`
|
||||
|
||||
Used to catch errors at the end of a promise chain.
|
||||
|
||||
```fsharp
|
||||
Promise.create (fun resolve reject ->
|
||||
reject (System.Exception "User not found")
|
||||
)
|
||||
|> Promise.map (fun _ ->
|
||||
// ...
|
||||
)
|
||||
|> Promise.map (fun _ ->
|
||||
// ...
|
||||
)
|
||||
|> Promise.catchEnd (fun error ->
|
||||
// ...
|
||||
) // Returns unit
|
||||
```
|
||||
|
||||
### Promise.either
|
||||
|
||||
`either: success: ('T1 -> 'T2) -> fail : (exn -> 'T2) -> pr : Promise<'T1> -> Promise<'T2>`
|
||||
|
||||
A combination of `map` and `catch`, this function applies the `success` continuation when the input promise resolves successfully, or `fail` continuation when the input promise fails.
|
||||
|
||||
```fsharp
|
||||
somePromise
|
||||
|> Promise.either
|
||||
(fun x -> string x)
|
||||
(fun err -> Promise.lift err.Message)
|
||||
|> Promise.map ...
|
||||
```
|
||||
|
||||
### Promise.eitherBind
|
||||
|
||||
`eitherBind: success: ('T1 -> JS.Promise<'T2>) -> fail : (exn -> JS.Promise<'T2>) -> pr : Promise<'T1> -> Promise<'T2>`
|
||||
|
||||
A combination of `bind` and `catchBind`, this function applies the `success` continuation when the input promise resolves successfully, or `fail` continuation when the input promise fails.
|
||||
|
||||
```fsharp
|
||||
somePromise
|
||||
|> Promise.eitherBind
|
||||
(fun x -> string x |> Promise.lift)
|
||||
(fun err -> Promise.lift err.Message)
|
||||
|> Promise.map ...
|
||||
```
|
||||
|
||||
### Promise.eitherEnd
|
||||
|
||||
`eitherEnd: success: ('T -> unit) -> fail : ('E -> unit) -> pr : Promise<'T> -> unit`
|
||||
|
||||
Same as [`Promise.either`](#Promise.either) but stopping the promise execution.
|
||||
|
||||
```fsharp
|
||||
somePromise
|
||||
|> Promise.eitherEnd
|
||||
(fun x -> string x)
|
||||
(fun err -> Promise.lift err.Message)
|
||||
```
|
||||
|
||||
### Promise.start
|
||||
|
||||
`start: pr: Promise<'T> -> unit`
|
||||
|
||||
Start a promise.
|
||||
|
||||
In version before XXX, it was used because the promise CE was originally cold, so it didn't start until `then` was called. Now it's hot same as native promises, so `Promise.start` is equivalent to `promise |> ignore`
|
||||
|
||||
```fsharp
|
||||
myPromise
|
||||
|> Promise.start
|
||||
```
|
||||
|
||||
### Promise.tryStart
|
||||
|
||||
`tryStart: fail: (exn -> unit) -> pr : Promise<'T> -> unit`
|
||||
|
||||
Same as [Promise.start](#Promise.start) but forcing you to handle the rejected state.
|
||||
|
||||
```fsharp
|
||||
myPromise
|
||||
|> Promise.tryStart
|
||||
(fun error ->
|
||||
// Do something on error
|
||||
)
|
||||
```
|
||||
|
||||
### Promise.Parallel
|
||||
|
||||
`Parallel: pr: seq<JS.Promise<'T>> -> Promise<'T array>`
|
||||
|
||||
Same as [Promise.all](#Promise.all)
|
||||
|
||||
```fsharp
|
||||
let p1 =
|
||||
promise {
|
||||
do! Promise.sleep 100
|
||||
return 1
|
||||
}
|
||||
let p2 =
|
||||
promise {
|
||||
do! Promise.sleep 200
|
||||
return 2
|
||||
}
|
||||
let p3 =
|
||||
promise {
|
||||
do! Promise.sleep 300
|
||||
return 3
|
||||
}
|
||||
|
||||
Promise.Parallel [p1; p2; p3]
|
||||
|> Promise.map (fun res ->
|
||||
// res = [|1; 2; 3 |]
|
||||
)
|
||||
|> Promise.map ...
|
||||
```
|
||||
|
||||
### Promise.all
|
||||
|
||||
`all: pr: seq<JS.Promise<'T>> -> Promise<'T array>`
|
||||
|
||||
`Promise.all` takes a sequence of promises as an input, and returns a single `Promise` that resolves to an array of the results of the input promises.
|
||||
|
||||
```fsharp
|
||||
let p1 =
|
||||
promise {
|
||||
do! Promise.sleep 100
|
||||
return 1
|
||||
}
|
||||
let p2 =
|
||||
promise {
|
||||
do! Promise.sleep 200
|
||||
return 2
|
||||
}
|
||||
let p3 =
|
||||
promise {
|
||||
do! Promise.sleep 300
|
||||
return 3
|
||||
}
|
||||
|
||||
Promise.all [p1; p2; p3]
|
||||
|> Promise.map (fun res ->
|
||||
// res = [|1; 2; 3 |]
|
||||
)
|
||||
|> Promise.map ...
|
||||
```
|
||||
|
||||
Note: If you need to return mixed types you can use boxing and unboxing
|
||||
|
||||
```fsharp
|
||||
let users =
|
||||
promise {
|
||||
let! users = fetchUsers ()
|
||||
return box users
|
||||
}
|
||||
let posts =
|
||||
promise {
|
||||
let! posts = fetchPosts ()
|
||||
return box posts
|
||||
}
|
||||
|
||||
Promise.all [p1; p2]
|
||||
|> Promise.map (fun res ->
|
||||
let users = unbox<User list> res.[0]
|
||||
let posts = unbox<Post list> res.[1]
|
||||
// ...
|
||||
)
|
||||
```
|
||||
|
||||
### Promise.result
|
||||
|
||||
`result: a: Promise<'T> -> Promise<Result<'T,exn>>`
|
||||
|
||||
Map the `Promise` result into a `Result` type.
|
||||
|
||||
```fsharp
|
||||
// Success example
|
||||
Promise.lift 42
|
||||
|> Promise.result
|
||||
|> Promise.map (fun value ->
|
||||
// value = Ok 42
|
||||
)
|
||||
|
||||
// Fail example
|
||||
Promise.reject "Invalid value"
|
||||
|> Promise.result
|
||||
|> Promise.map (fun value ->
|
||||
// value = Error "Invalid value"
|
||||
)
|
||||
```
|
||||
|
||||
### Promise.mapResult
|
||||
|
||||
`mapResult: fn: ('T1 -> 'T2) -> a : Promise<Result<'T1,'E>> -> Promise<Result<'T2,'E>>`
|
||||
|
||||
Evaluates to `myPromise |> Promise.map (Result.map fn)`
|
||||
|
||||
```fsharp
|
||||
Promise.lift 42
|
||||
|> Promise.result
|
||||
|> Promise.mapResult (fun value ->
|
||||
value + 10
|
||||
)
|
||||
|> Promise.map (fun value ->
|
||||
// value = Ok 52
|
||||
)
|
||||
```
|
||||
|
||||
### Promise.bindResult
|
||||
|
||||
`bindResult: fn: ('T1 -> JS.Promise<'T2>) -> a : Promise<Result<'T1,'E>> -> Promise<Result<'T2,'E>>`
|
||||
|
||||
Transform the success part of a result promise into another promise.
|
||||
|
||||
```fsharp
|
||||
let multiplyBy2 (value : int) =
|
||||
Promise.create (fun resolve reject ->
|
||||
resolve (value * 2)
|
||||
)
|
||||
|
||||
Promise.lift 42
|
||||
|> Promise.result
|
||||
|> Promise.bindResult (fun value ->
|
||||
multiplyBy2 value
|
||||
)
|
||||
|> Promise.map (fun value ->
|
||||
// value = Ok 84
|
||||
)
|
||||
```
|
||||
|
||||
### Promise.mapResultError
|
||||
|
||||
`mapResultError: fn: ('E1 -> 'E2) -> a : Promise<Result<'T,'E1>> -> Promise<Result<'T,'E2>>`
|
||||
|
||||
Evaluates to `myPromise |> Promise.map (Result.map fn)`
|
||||
|
||||
```fsharp
|
||||
Promise.reject -1
|
||||
|> Promise.result
|
||||
|> Promise.mapResultError (fun value ->
|
||||
$"%s{value} is not a valid value"
|
||||
)
|
||||
|> Promise.map (fun value ->
|
||||
// value = Error "-1 is not a valid value"
|
||||
)
|
||||
```
|
||||
|
||||
### Promise.tap
|
||||
|
||||
`tap: fn: ('A -> unit) -> a : Promise<'A> -> Promise<'A>`
|
||||
|
||||
This is an identity function, it calls the given function and return the promise value untouched.
|
||||
|
||||
```fsharp
|
||||
fetchUser ()
|
||||
|> Promise.tap (fun user ->
|
||||
// Do something
|
||||
console.log "The user has been received"
|
||||
)
|
||||
|> Promise.map (fun user ->
|
||||
// user value is available here untouched
|
||||
)
|
||||
```
|
||||
BIN
docsrc/favicon.ico
Normal file
BIN
docsrc/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
35
docsrc/index.md
Normal file
35
docsrc/index.md
Normal file
@@ -0,0 +1,35 @@
|
||||
---
|
||||
title:
|
||||
layout: nacara-navbar-only
|
||||
---
|
||||
|
||||
<!-- Disable the copy-button on all the elements contained inside the container (all this page) -->
|
||||
<div class="container mt-5" data-disable-copy-button="true">
|
||||
<!--
|
||||
Selling points
|
||||
For the selling points we use CSS grid instead of Bulma columns because we want all the box to have the same height.
|
||||
This is not something possible to do dynamically using Flexbox/Bulma columns system
|
||||
-->
|
||||
<section class="section">
|
||||
<h2 class="title is-2 has-text-primary has-text-centered">
|
||||
Fable.Lit
|
||||
</h2>
|
||||
<p class="content is-size-5 has-text-centered">
|
||||
Fable.Lit is a comprehensive suite of tools to write <a href="https://fable.io">Fable</a> apps by embedding HTML into your F# code with the power of Google's <a href="https://lit.dev">Lit</a>.
|
||||
</p>
|
||||
<p class="content is-size-5 has-text-centered">
|
||||
Fable.Lit lets you take full advantage of modern web development, including blazing-fast hot reloading, best-practice component testing and a VS Code extension.
|
||||
</p>
|
||||
<p class="has-text-centered">
|
||||
<img src="screencast.gif" style="max-width: 600px; border-radius: 10px">
|
||||
</p>
|
||||
<br />
|
||||
<p class="has-text-centered">
|
||||
<a href="docs/getting-started.html">
|
||||
<button class="button is-info is-large">
|
||||
Get started
|
||||
</button>
|
||||
</a>
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
BIN
docsrc/screencast.gif
Normal file
BIN
docsrc/screencast.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 941 KiB |
6
docsrc/scss/utilities.scss
Normal file
6
docsrc/scss/utilities.scss
Normal file
@@ -0,0 +1,6 @@
|
||||
@mixin strong-shadow-box {
|
||||
border-radius: 4px;
|
||||
box-shadow: 5px 5px 0px 1px lightgrey;
|
||||
border: 1px solid lightgrey;
|
||||
background-color: white;
|
||||
}
|
||||
46
docsrc/style.scss
Normal file
46
docsrc/style.scss
Normal file
@@ -0,0 +1,46 @@
|
||||
@import "./../node_modules/bulma/sass/utilities/initial-variables";
|
||||
|
||||
|
||||
$fable-blue: dodgerblue;
|
||||
$purple: #7a325d;
|
||||
$blue: #0055a4;
|
||||
$primary: $fable-blue;
|
||||
$black: #202020;
|
||||
// $scheme-main: whitesmoke;
|
||||
$text: #444;
|
||||
|
||||
@import "./../node_modules/bulma/sass/utilities/derived-variables";
|
||||
|
||||
$navbar-item-color: $white;
|
||||
$navbar-background-color: $primary;
|
||||
$navbar-item-hover-color: $white;
|
||||
$navbar-item-hover-background-color: lighten($primary, 8%);
|
||||
$navbar-item-active-background-color: lighten($primary, 8%);
|
||||
$navbar-item-active-color: $white;
|
||||
|
||||
$content-heading-color: $primary;
|
||||
|
||||
$menu-item-active-background-color: $primary;
|
||||
$menu-item-active-color: $white;
|
||||
$menu-item-hover-color: $primary;
|
||||
$menu-item-hover-background-color: transparent;
|
||||
$menu-label-font-size: $size-6;
|
||||
$menu-item-radius: $radius-large $radius-large;
|
||||
|
||||
$toc-item-active-font-weight: $weight-semibold;
|
||||
|
||||
$body-size: 14px;
|
||||
|
||||
$textual-step-color: lightgrey;
|
||||
|
||||
@import "./../node_modules/bulma/sass/utilities/_all.sass";
|
||||
@import './../node_modules/bulma/bulma.sass';
|
||||
@import './../node_modules/nacara-layout-standard/scss/nacara.scss';
|
||||
|
||||
@import './scss/utilities.scss';
|
||||
|
||||
.content {
|
||||
pre {
|
||||
@include strong-shadow-box;
|
||||
}
|
||||
}
|
||||
211
lightner/grammars/JSON.tmLanguage.json
Normal file
211
lightner/grammars/JSON.tmLanguage.json
Normal file
@@ -0,0 +1,211 @@
|
||||
{
|
||||
"information_for_contributors": [
|
||||
"This file has been copied from https://github.com/Microsoft/vscode/blob/master/extensions/json/syntaxes/JSON.tmLanguage.json"
|
||||
],
|
||||
"version": "https://github.com/Microsoft/vscode-JSON.tmLanguage/commit/9bd83f1c252b375e957203f21793316203f61f70",
|
||||
"name": "JSON (Javascript Next)",
|
||||
"scopeName": "source.json",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#value"
|
||||
}
|
||||
],
|
||||
"repository": {
|
||||
"array": {
|
||||
"begin": "\\[",
|
||||
"beginCaptures": {
|
||||
"0": {
|
||||
"name": "punctuation.definition.array.begin.json"
|
||||
}
|
||||
},
|
||||
"end": "\\]",
|
||||
"endCaptures": {
|
||||
"0": {
|
||||
"name": "punctuation.definition.array.end.json"
|
||||
}
|
||||
},
|
||||
"name": "meta.structure.array.json",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#value"
|
||||
},
|
||||
{
|
||||
"match": ",",
|
||||
"name": "punctuation.separator.array.json"
|
||||
},
|
||||
{
|
||||
"match": "[^\\s\\]]",
|
||||
"name": "invalid.illegal.expected-array-separator.json"
|
||||
}
|
||||
]
|
||||
},
|
||||
"comments": {
|
||||
"patterns": [
|
||||
{
|
||||
"begin": "/\\*\\*(?!/)",
|
||||
"captures": {
|
||||
"0": {
|
||||
"name": "punctuation.definition.comment.json"
|
||||
}
|
||||
},
|
||||
"end": "\\*/",
|
||||
"name": "comment.block.documentation.json"
|
||||
},
|
||||
{
|
||||
"begin": "/\\*",
|
||||
"captures": {
|
||||
"0": {
|
||||
"name": "punctuation.definition.comment.json"
|
||||
}
|
||||
},
|
||||
"end": "\\*/",
|
||||
"name": "comment.block.json"
|
||||
},
|
||||
{
|
||||
"captures": {
|
||||
"1": {
|
||||
"name": "punctuation.definition.comment.json"
|
||||
}
|
||||
},
|
||||
"match": "(//).*$\\n?",
|
||||
"name": "comment.line.double-slash.js"
|
||||
}
|
||||
]
|
||||
},
|
||||
"constant": {
|
||||
"match": "\\b(?:true|false|null)\\b",
|
||||
"name": "constant.language.json"
|
||||
},
|
||||
"number": {
|
||||
"match": "(?x) # turn on extended mode\n -? # an optional minus\n (?:\n 0 # a zero\n | # ...or...\n [1-9] # a 1-9 character\n \\d* # followed by zero or more digits\n )\n (?:\n (?:\n \\. # a period\n \\d+ # followed by one or more digits\n )?\n (?:\n [eE] # an e character\n [+-]? # followed by an option +/-\n \\d+ # followed by one or more digits\n )? # make exponent optional\n )? # make decimal portion optional",
|
||||
"name": "constant.numeric.json"
|
||||
},
|
||||
"object": {
|
||||
"begin": "\\{",
|
||||
"beginCaptures": {
|
||||
"0": {
|
||||
"name": "punctuation.definition.dictionary.begin.json"
|
||||
}
|
||||
},
|
||||
"end": "\\}",
|
||||
"endCaptures": {
|
||||
"0": {
|
||||
"name": "punctuation.definition.dictionary.end.json"
|
||||
}
|
||||
},
|
||||
"name": "meta.structure.dictionary.json",
|
||||
"patterns": [
|
||||
{
|
||||
"comment": "the JSON object key",
|
||||
"include": "#objectkey"
|
||||
},
|
||||
{
|
||||
"include": "#comments"
|
||||
},
|
||||
{
|
||||
"begin": ":",
|
||||
"beginCaptures": {
|
||||
"0": {
|
||||
"name": "punctuation.separator.dictionary.key-value.json"
|
||||
}
|
||||
},
|
||||
"end": "(,)|(?=\\})",
|
||||
"endCaptures": {
|
||||
"1": {
|
||||
"name": "punctuation.separator.dictionary.pair.json"
|
||||
}
|
||||
},
|
||||
"name": "meta.structure.dictionary.value.json",
|
||||
"patterns": [
|
||||
{
|
||||
"comment": "the JSON object value",
|
||||
"include": "#value"
|
||||
},
|
||||
{
|
||||
"match": "[^\\s,]",
|
||||
"name": "invalid.illegal.expected-dictionary-separator.json"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"match": "[^\\s\\}]",
|
||||
"name": "invalid.illegal.expected-dictionary-separator.json"
|
||||
}
|
||||
]
|
||||
},
|
||||
"string": {
|
||||
"begin": "\"",
|
||||
"beginCaptures": {
|
||||
"0": {
|
||||
"name": "punctuation.definition.string.begin.json"
|
||||
}
|
||||
},
|
||||
"end": "\"",
|
||||
"endCaptures": {
|
||||
"0": {
|
||||
"name": "punctuation.definition.string.end.json"
|
||||
}
|
||||
},
|
||||
"name": "string.quoted.double.json",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#stringcontent"
|
||||
}
|
||||
]
|
||||
},
|
||||
"objectkey": {
|
||||
"begin": "\"",
|
||||
"beginCaptures": {
|
||||
"0": {
|
||||
"name": "punctuation.support.type.property-name.begin.json"
|
||||
}
|
||||
},
|
||||
"end": "\"",
|
||||
"endCaptures": {
|
||||
"0": {
|
||||
"name": "punctuation.support.type.property-name.end.json"
|
||||
}
|
||||
},
|
||||
"name": "string.json support.type.property-name.json",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#stringcontent"
|
||||
}
|
||||
]
|
||||
},
|
||||
"stringcontent": {
|
||||
"patterns": [
|
||||
{
|
||||
"match": "(?x) # turn on extended mode\n \\\\ # a literal backslash\n (?: # ...followed by...\n [\"\\\\/bfnrt] # one of these characters\n | # ...or...\n u # a u\n [0-9a-fA-F]{4}) # and four hex digits",
|
||||
"name": "constant.character.escape.json"
|
||||
},
|
||||
{
|
||||
"match": "\\\\.",
|
||||
"name": "invalid.illegal.unrecognized-string-escape.json"
|
||||
}
|
||||
]
|
||||
},
|
||||
"value": {
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#constant"
|
||||
},
|
||||
{
|
||||
"include": "#number"
|
||||
},
|
||||
{
|
||||
"include": "#string"
|
||||
},
|
||||
{
|
||||
"include": "#array"
|
||||
},
|
||||
{
|
||||
"include": "#object"
|
||||
},
|
||||
{
|
||||
"include": "#comments"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
5586
lightner/grammars/JavaScript.tmLanguage.json
Normal file
5586
lightner/grammars/JavaScript.tmLanguage.json
Normal file
File diff suppressed because it is too large
Load Diff
1703
lightner/grammars/fsharp.json
Normal file
1703
lightner/grammars/fsharp.json
Normal file
File diff suppressed because it is too large
Load Diff
1741
lightner/themes/customized_OneLight.json
Normal file
1741
lightner/themes/customized_OneLight.json
Normal file
File diff suppressed because it is too large
Load Diff
42
nacara.config.json
Normal file
42
nacara.config.json
Normal file
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"url": "https://fable.io",
|
||||
"baseUrl": "/Fable.Lit/",
|
||||
"editUrl": "https://github.com/fable-compiler/Fable.Lit/edit/main/docsrc",
|
||||
"source": "docsrc",
|
||||
"output": "build/docs",
|
||||
"title": "Fable.Lit",
|
||||
"navbar": {
|
||||
"start": [
|
||||
{
|
||||
"url": "/Fable.Lit/docs/getting-started.html",
|
||||
"section": "docs",
|
||||
"label": "Documentation"
|
||||
}
|
||||
],
|
||||
"end": [
|
||||
{
|
||||
"url": "https://github.com/fable-compiler/Fable.Lit",
|
||||
"icon": "fab fa-github",
|
||||
"isExternal": true
|
||||
},
|
||||
{
|
||||
"url": "https://twitter.com/FableCompiler",
|
||||
"icon": "fab fa-twitter",
|
||||
"isExternal": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"lightner": {
|
||||
"backgroundColor": "#FFFFFF",
|
||||
"textColor": "#000000",
|
||||
"themeFile": "./lightner/themes/customized_OneLight.json",
|
||||
"grammars": [
|
||||
"./lightner/grammars/fsharp.json",
|
||||
"./lightner/grammars/JSON.tmLanguage.json",
|
||||
"./lightner/grammars/JavaScript.tmLanguage.json"
|
||||
]
|
||||
},
|
||||
"layouts": [
|
||||
"nacara-layout-standard"
|
||||
]
|
||||
}
|
||||
1698
package-lock.json
generated
1698
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,10 @@
|
||||
"publish": "dotnet fsi build.fsx publish",
|
||||
"test": "dotnet fable test -o build/test --run web-test-runner build/test/*Test.js --node-resolve",
|
||||
"test:watch": "dotnet fable watch test -o build/test --run web-test-runner build/test/*Test.js --node-resolve --watch",
|
||||
"start": "dotnet fable watch sample -o build/sample --runFast vite --open"
|
||||
"start": "dotnet fable watch sample -o build/sample --runFast vite --open",
|
||||
"predocs": "shx rm -rf deploy",
|
||||
"docs": "nacara",
|
||||
"docs:deploy": "npm run docs && gh-pages -d build/docs"
|
||||
},
|
||||
"dependencies": {
|
||||
"lit": "^2.0.0",
|
||||
@@ -15,6 +18,10 @@
|
||||
"devDependencies": {
|
||||
"@web/test-runner": "^0.13.16",
|
||||
"fable-publish-utils": "^2.2.0",
|
||||
"gh-pages": "^3.2.3",
|
||||
"nacara": "^1.0.0-beta-014",
|
||||
"nacara-layout-standard": "^1.0.0-beta-009",
|
||||
"shx": "^0.3.3",
|
||||
"vite": "^2.5.4"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user