feat: dox++

This commit is contained in:
cha0s 2024-01-02 15:13:43 -06:00
parent c2b5d965c4
commit 18aa304183
23 changed files with 716 additions and 39 deletions

22
.vscode/tasks.json vendored Normal file
View File

@ -0,0 +1,22 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "Start Docusaurus server",
"type": "shell",
"command": "yarn flecks docusaurus start",
"problemMatcher": {
"pattern": {
"regexp": "^Error",
},
"background": {
"activeOnStart": true,
"endsPattern": "compiled successfully$",
"beginsPattern": "Compiling Client$",
}
}
}
]
}

View File

@ -14,7 +14,7 @@ export default async function flecksDocusaurus() {
...defaults,
title: 'flecks',
tagline: 'not static',
// favicon: 'img/flecks.svg',
favicon: 'flecks.png',
url: 'https://cha0s.github.io',
baseUrl: '/flecks/',
organizationName: 'cha0s', // Usually your GitHub org/user name.
@ -25,10 +25,10 @@ export default async function flecksDocusaurus() {
...defaults.themeConfig,
navbar: {
title: 'flecks',
// logo: {
// alt: 'flecks logo',
// src: 'img/flecks.svg',
// },
logo: {
alt: 'flecks logo',
src: 'flecks.png',
},
items: [
{
href: 'https://github.com/cha0s/flecks',

60
website/docs/cli.mdx Normal file
View File

@ -0,0 +1,60 @@
---
title: Command-line interface
description: Document your project.
---
# Command-line interface
flecks has a command-line interface for building, linting, testing, and so much more.
## Built-in commands
### `build`
Build a target in your application.
```bash
flecks build --help
Usage: flecks build [options] [target]
Arguments:
target target (choices: "...")
Options:
-d, --no-production dev build
-h, --hot build with hot module reloading
-w, --watch watch for changes
```
### `lint`
```bash
Usage: flecks lint [options]
run linter
```
### `docusaurus`
(Implemented by `@flecks/dox`)
```bash
Usage: flecks docusaurus [options] <subcommand> [siteDir]
create a documentation website for this project
Arguments:
subcommand Docusaurus command to run (choices: "build", "create", "start")
siteDir Docusaurus directory (default: "website")
```
The `build` and `start` subcommands are sugar on top of the corresponding Docusaurus commands.
The `create` subcommand will create a documentation website starter template for you at `siteDir`
which defaults to `website` if `siteDir` doesn't already exist. A `docusaurus.config.js`
starter configuration will also be copied to your `build` directory, if it doesn't already exist.
## Your commands
You can implement your own command by implementing
[`@flecks/core.commands`](/docs/flecks/@flecks/dox/hooks#fleckscorecommands) in your fleck.

View File

@ -8,7 +8,7 @@ import InstallPackage from '@site/helpers/install-package';
You have a flecks application! ...but it doesn't do much. This is because a flecks application is
composed of individual flecks. By default, your application will have two flecks: `@flecks/core` and
`@flecks/server`. Each fleck may have configuration that can be set through `flecks.yml`. For a
list of configurable core flecks, see [the generated configuration page](/docs/flecks/config).
list of configurable core flecks, see [the generated configuration page](/docs/flecks/@flecks/dox/config).
## First steps
@ -34,7 +34,7 @@ application's ID to `hello_world`, update your `flecks.yml` to look like this:
## Getting somewhere
If you were studious enough to take a peek at [the generated configuration page](/docs/flecks/config)
If you were studious enough to take a peek at [the generated configuration page](/docs/flecks/@flecks/dox/config)
above, you may have noticed that there were a bunch more flecks there than just the two in our
application.

395
website/docs/database.mdx Normal file
View File

@ -0,0 +1,395 @@
---
title: Database
description: How to define models and connect to a database.
---
import Create from '@site/helpers/create';
import InstallPackage from '@site/helpers/install-package';
# Database
flecks provides database connection through [Sequelize](https://sequelize.org/) and database
server instances through either flat SQLite databases or [Docker](https://www.docker.com/)ized
database servers.
## Install and configure
We'll start from scratch as an example. Create a new flecks application:
<Create pkg="db_test" type="app" />
Now in your new application directory, install `@flecks/db`:
<InstallPackage pkg="@flecks/db" />
Then, add the fleck to your `build/flecks.yml`:
```yml
'@flecks/core': {}
// highlight-next-line
'@flecks/db': {}
'@flecks/server': {}
```
Finally, `npm start` your application and you will see lines like the following in the logs:
```
@flecks/db/server/connection config: { dialect: 'sqlite', storage: ':memory:' } +0ms
@flecks/db/server/connection synchronizing... +107ms
@flecks/db/server/connection synchronized +2ms
```
By default, flecks will connect to an in-memory SQLite database to get you started instantly.
## Your models
Astute observers may have noticed a line preceding the ones earlier:
```
@flecks/core/flecks gathered '@flecks/db/server.models': [] +0ms
```
Let's create a fleck that makes a model so we can get a feel for how it works.
First, create a fleck in your application:
<Create pkg="tags" type="fleck" />
Then, add it to `build/flecks.yml`:
```yml
'@flecks/core': {}
'@flecks/db': {}
'@flecks/server': {}
// highlight-next-line
'@db_test/tags:./packages/tags': {}
```
Now, let's hop into `packages/tags/src/index.js` and add a hook implementation:
```javascript
export const hooks = {
'@flecks/db/server.models': (flecks) => {
const {Model, Types} = flecks.fleck('@flecks/db/server');
class Tags extends Model {
static get attributes() {
return {
key: {
type: Types.STRING,
allowNull: false,
},
value: {
type: Types.STRING,
allowNull: false,
},
};
}
};
return {
Tags,
};
},
}
```
:::note
The static `attributes` method above is sugar on top of
[`sequelize.define`](https://sequelize.org/docs/v6/core-concepts/model-basics/#using-sequelizedefine)
and you should consult that documentation for details on how to define your models.
To implement associations between models, there is a static `associate` method. Suppose you also
had a `Post` model along with your `Tags` model. You might do something like this in your `Tags`
model:
```
static associate({Post}) {
Post.hasMany(this);
}
```
:::
Now, `npm start` your application and you will see that line looks different:
```
@flecks/core/flecks gathered '@flecks/db/server.models': [ 'Tags' ] +0ms
```
Our model is recognized! Let's do something with it. Edit `packages/tags/src/index.js` again like
so:
```javascript
export const hooks = {
// highlight-start
'@flecks/server.up': async (flecks) => {
const {Tags} = flecks.get('$flecks/db.models');
console.log('There were', await Tags.count(), 'tags.');
},
// highlight-end
'@flecks/db/server.models': (flecks) => {
// Omitted for clarity...
},
}
```
Now, another `npm start` will greet us with this line in the output:
```
There were 0 tags.
```
Not very interesting. Let's add some, but only if there aren't any tags yet:
```javascript
export const hooks = {
'@flecks/server.up': async (flecks) => {
const {Tags} = flecks.get('$flecks/db.models');
console.log('There were', await Tags.count(), 'tags.');
// highlight-start
if (0 === await Tags.count()) {
await Tags.create({key: 'foo', value: 'bar'});
await Tags.create({key: 'another', value: 'thing'});
}
console.log('There are', await Tags.count(), 'tags.');
// highlight-end
},
'@flecks/db/server.models': (flecks) => {
// Omitted for clarity...
},
}
```
Another `npm start` and we see the tags created!
## Persistence
You'll notice that if you run it again, it will always say
```
There were 0 tags.
There are 2 tags.
```
What's up with that? Remember in the beginning:
> By default, flecks will connect to an in-memory SQLite database to get you started instantly.
This means that the database will only persist as long as the life of your application. When you
restart it, you'll get a fresh new database every time. Obviously, this isn't very helpful for
any real purpose. Let's make a change to our `build/flecks.yml`:
```yml
'@flecks/core': {}
'@flecks/db': {}
// highlight-start
'@flecks/db/server':
database: './persistent.sql'
// highlight-end
'@flecks/server': {}
'@db_test/tags:./packages/tags': {}
```
Now `npm start` again. You'll see our old familiar message:
```
There were 0 tags.
There are 2 tags.
```
This time though, our application wrote the SQLite database to disk at `./persistent.sql`. If we
give it one more go, we'll finally see what we expect:
```
There were 2 tags.
There are 2 tags.
```
A persistent database!
## Containerization
Sure, our database is persistent... kinda. That `persistent.sql` file is a bit of a kludge and
isn't much of a long-term (or production) solution. Let's remove it:
```
rm persistent.sql
```
Our small-core philosophy means that you don't pay for spinning up a database by default. However,
it's trivial to accomplish a *"real"* database connection if you have Docker installed on your
machine.
<details>
<summary>How do I know if I have Docker running on my machine?</summary>
A decent way to test if your machine is ready to continue with this guide is to run the following
command:
`docker run -e POSTGRES_PASSWORD=password postgres`
if the command appears to spin up a database, you're in good shape!
If not, follow the [Docker installation documentation](https://docs.docker.com/engine/install/)
before proceeding.
</details>
Let's add another fleck to our project:
<InstallPackage pkg="@flecks/docker" />
Configure `build/flecks.yml`:
```yml
'@flecks/core': {}
'@flecks/db': {}
// highlight-start
'@flecks/db/server':
database: db
dialect: postgres
password: THIS_PASSWORD_IS_UNSAFE
username: postgres
'@flecks/docker': {}
'@flecks/server':
up:
- '@flecks/docker'
- '@flecks/db'
- '@db_test/tags'
// highlight-end
'@db_test/tags:./packages/tags': {}
```
Notice that we configured `@flecks/server.up` to make sure to enforce a specific order in which
our server flecks come up: first `@flecks/docker` to spin up the database, then
`@flecks/db` to connect to the database, and finally our `@db_test/tags` fleck to interact with
the database. This is important!
Now `npm start` will reveal the following message in the logs:
```
@flecks/server/entry Error: Please install pg package manually
```
Pretty straightforward how to proceed:
<InstallPackage pkg="pg" />
Remember, **small core**! :smile: Now `npm start` again and you will see some new lines in the
logs:
```
@flecks/docker/container creating datadir '/tmp/flecks/flecks/docker/sequelize' +0ms
@flecks/docker/container launching: docker run --name flecks_sequelize -d --rm -p 5432:5432 -e POSTGRES_USER=postgres -e POSTGRES_DB=db -e POSTGRES_PASSWORD=THIS_PASSWORD_IS_UNSAFE -v /tmp/flecks/flecks/docker/sequelize:/var/lib/postgresql/data postgres +0ms
@flecks/docker/container 'sequelize' started +372ms
@flecks/db/server/connection config: { database: 'db', dialect: 'postgres', host: undefined, password: '*** REDACTED ***', port: undefined, username: 'postgres' } +0ms
@flecks/db/server/connection synchronizing... +2s
@flecks/db/server/connection synchronized +3ms
```
and of course, we see:
```
There were 0 tags.
There are 2 tags.
```
because we just created a new postgres database from scratch just then! Kill the application and
run `npm start` one more time and then you will see what you expect:
```
There were 2 tags.
There are 2 tags.
```
Awesome, we have a connection to a real postgres database!
<details>
<summary>Where is the docker container?</summary>
You may notice that the first time you start your application there is a delay while Docker spins
up your new database server. This is normal. You may have also noticed that subsequent runs speed
up to near-instantaneous. This is also normal!
`@flecks/docker` runs your container in a manner that outlives your application. If you kill your
application and then run:
```
docker ps
```
You will see a line for your postgres database looking something like:
```
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
<SOME_ID> postgres "docker-entrypoint.s…" 5 minutes ago Up 5 minutes 0.0.0.0:5432->5432/tcp, :::5432->5432/tcp flecks_sequelize
```
You'll see under the `NAMES` column heading, there is an entry called `flecks_sequelize`. That's
our database! You can always
```
docker kill flecks_sequelize
```
to free up any resources being used. flecks keeps the containers running so that you get a nice
fast application start.
:::note
The container name is based off of `@flecks/core.id` which by default is `flecks`. If you change
your application's ID, the container name will be different.
:::
</details>
## Production
Sure, spinning up a database like magic is spiffy for development, but you probably want to be a
little less freewheeling on your production server.
Build the application we've built so far:
```
npm run build
```
Then, take a look in the `dist` directory. You'll see a file there called `docker-compose.yml`.
`@flecks/docker` automatically emits this file when you build your application for production to
make container orchestration easier. Let's take a look:
```yml
version: '3'
services:
flecks_app:
build:
context: ..
dockerfile: dist/Dockerfile
environment:
FLECKS_ENV_FLECKS_DOCKER_SERVER_enabled: 'false'
// highlight-next-line
FLECKS_ENV_FLECKS_DB_SERVER_host: sequelize
volumes:
- ../node_modules:/var/www/node_modules
// highlight-start
sequelize:
image: postgres
environment:
POSTGRES_USER: postgres
POSTGRES_DB: db
POSTGRES_PASSWORD: THIS_PASSWORD_IS_UNSAFE
// highlight-end
```
Notice our database container is included and already prepopulated with the configuration we
specified!
You can run (after you [install Docker Compose](https://docs.docker.com/compose/install/) if
necessary):
```
docker-compose -f dist/docker-compose.yml up
```
This demonstrates that your application is now being orchestrated by Docker Compose and is
chugging right along!

View File

@ -1,14 +1,92 @@
---
title: Documentation
title: Documentation website
description: Document your project.
---
# Documentation
import useBaseUrl from '@docusaurus/useBaseUrl';
import ImageRow from '@site/helpers/image-row';
import InstallPackage from '@site/helpers/install-package';
import ThemedImage from '@theme/ThemedImage';
# Documentation website
Ah, the most ~~boring~~ awesome part of development.
flecks provides a fleck called `@flecks/dox` that helps you generate a documentation website for
your project. In fact, this very website you're viewing now has been built with the same tooling!
`@flecks/dox` implements a [CLI command](/flecks/docs/flecks/@flecks/dox/hooks#fleckscorecommands)
to run [Docusaurus](https://docusaurus.io/) and generate a documentation site for your project.
## Install `@flecks/dox`
To get started, install `@flecks/dox` in your project:
<InstallPackage pkg="@flecks/dox" />
Then, add it to your `build/flecks.yml`:
```yml
'@flecks/core': {}
// highlight-next-line
'@flecks/dox': {}
'@flecks/server': {}
```
## Spin up a starter website
Next, run:
```bash
npx flecks docusaurus create
```
You should now have a starter configuration in `build/docusaurus.config.js` and a new directory
in your project: `website`. See [the command documentation](/docs/cli#docusaurus) to discover more
options.
Now, start your documentation website's development server with:
```
npx flecks docusaurus start
```
You should now see a message similar to:
```bash
[SUCCESS] Docusaurus website is running at: http://localhost:3000/
```
Go and have a look!
<ImageRow>
<ThemedImage
alt="Screenshot of the front page of the generated documentation site"
sources={{
light: useBaseUrl('/flecks-dox-1-light.png'),
dark: useBaseUrl('/flecks-dox-1.png'),
}}
/>
<ThemedImage
alt="Screenshot of the documentation introduction page of the generated documentation site"
sources={{
light: useBaseUrl('/flecks-dox-2-light.png'),
dark: useBaseUrl('/flecks-dox-2.png'),
}}
/>
</ImageRow>
Not bad, huh?
## Building it out
It's only a starter template, of course. You'll want to pop over to
[the Docusaurus guides page](https://docusaurus.io/docs/category/guides) to build out your new
website. Have fun!
## Production-ready
When you're ready to build for production, just run
```bash
npx flecks docusaurus build
```
After successfully building, your website files will be located at `dist/dox`.

View File

@ -0,0 +1,4 @@
---
title: Electron
description: How to run your application in Electron and build for distribution.
---

View File

@ -0,0 +1,74 @@
---
title: Environment Variables
description: Configure your application with environment variables.
---
# Environment Variables
flecks has first-class support for configuring your application through environment variables.
## Example
`@flecks/core` defines a configuration key `id`. We've already seen how it can be configured like
so:
```yml
'@flecks/core':
id: 'foobar'
```
When running your application in different execution environments (say, production) you may want to
override configuration such as this. This is done by using the a prefix followed by the
fleck name and the key. The template literal for such a transformation would look like:
### Syntax
```javascript
`FLECKS_ENV_${Flecks.environmentalize(path)}_${key}`
```
:::note
Notice the `environmentalize` transformation: `@flecks/core`'s `id` key is set using the
following variable:
```bash
FLECKS_ENV_FLECKS_CORE_id=foobar
```
<details>
<summary>`Flecks.environmentalize`</summary>
```javascript
static environmentalize(path) {
return path
// - `@flecks/core` -> `FLECKS_CORE`
.replace(/[^a-zA-Z0-9]/g, '_')
.replace(/_*(.*)_*/, '$1')
.toUpperCase();
}
```
</details>
Also note that the key is still case-sensitive. This is because configuration keys are
user-defined.
:::
## Resolution Order
There is a possibility for ambiguity arising from the fact that flecks may be contained within a
subpath of another fleck. For instance:
```bash
FLECKS_ENV_FLECKS_CORE_SERVER_variable=something
```
Could be configuring a key at `@flecks/core/server.variable`, but it could also be configuring a
key at `@flecks/core.SERVER.variable`.
To resolve this ambiguity, candidate paths are reverse-sorted alphabetically, so that the longer
fleck path will be preferred.
In the above case, it is `@flecks/core/server.variable` which receives the configuration.

View File

@ -1,20 +0,0 @@
---
description: flecks core handles the orchestration of your flecks
slug: /flecks-core
---
# `@flecks/core`
## CLI
## Debug logs
## Helpers
### Event emitter
### Middleware
### Compose
## `@flecks/core/server`

View File

@ -26,18 +26,18 @@ flecks is built with supreme attention to the developer and end-user experience.
- 🛠️ **Ready to build real applications**
- babel + Webpack 5
- [React](#todo)/[redux](#todo)
- [Database](#todo)
- [Database](/docs/database)
- [electron](#todo)
- [docker](#todo)
- [REPL](#todo)
- [websockets](#todo) with lots of goodies like binary packing and packet dispatching out of the box
- [docusaurus](#todo): This documentation website you're looking at, available to your own apps for your own documentation by simply adding [`@flecks/dox`](#todo) to your own application
- [docusaurus](#todo): This documentation website you're looking at, [available to your own apps for your own documentation](/docs/documentation)
- 👷 **Developers, developers, developers**
- 🪄 Easy to create a fleck; no need to publish packages or use voodoo
- Write server or client (or both) tests, run on server/in browser/[your own custom platform](#todo)
- HMR (even on the server)
- Create a fleck with your [redux slice](https://redux-toolkit.js.org/api/createslice/) provided through a hook. Finally, real [ducks](https://github.com/erikras/ducks-modular-redux)!
- Easily spin up a database or redis server ([or...](/docs/flecks/hooks#flecksdockercontainers)) using [Docker](#todo) during development, and generate a `Dockerfile` and `docker-compose.yml` automatically for production
- [redux slices](https://redux-toolkit.js.org/api/createslice/) provided through hook!
- Easily spin up a database or redis server ([or...](/docs/flecks/@flecks/dox/hooks#flecksdockercontainers)) using [Docker](#todo) during development, and generate a `Dockerfile` and `docker-compose.yml` automatically for production
- Small hookable core means less exposed wires. You could always help by [submitting a pull request](https://github.com/cha0s/flecks/compare) though.
Our shared goal—to help you quickly develop your application. We share our best practices to help you build your application right and well.
@ -49,6 +49,7 @@ Our shared goal—to help you quickly develop your application. We share our bes
- **Modular**. flecks is built from the ground up with separation of concerns as a first-class concern.
- **Sensible defaults**. Common and popular performance optimizations and configurations will be done for users but they are given the option to override them.
- **No vendor lock-in**. Users are not required to use the default flecks, although they are highly encouraged to.
- **Minimize duplicated effort**. For instance, we ripped a lot of this wording off of the Docusaurus introduction page. :dancer:
We believe that, as developers, knowing how a library works helps us become better at using it. Hence we're dedicating effort to explaining the architecture and various aspects of flecks with the hope that users reading it will gain a deeper understanding and be even more proficient in using it.

4
website/docs/react.mdx Normal file
View File

@ -0,0 +1,4 @@
---
title: React
description: How to define a root component, enable, SSR, and more.
---

4
website/docs/redux.mdx Normal file
View File

@ -0,0 +1,4 @@
---
title: Redux
description: How to define your reducers and state.
---

4
website/docs/repl.mdx Normal file
View File

@ -0,0 +1,4 @@
---
title: REPL
description: How to start a REPL server and connect to it.
---

4
website/docs/sockets.mdx Normal file
View File

@ -0,0 +1,4 @@
---
title: Sockets
description: How to run a websocket server and define packets.
---

View File

@ -0,0 +1,19 @@
import CodeBlock from '@theme/CodeBlock';
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
export default function Create({pkg, type}) {
return (
<Tabs>
<TabItem value="npm" label="npm">
<CodeBlock language="bash">npx @flecks/create-{type} {pkg}</CodeBlock>
</TabItem>
<TabItem value="yarn" label="Yarn">
<CodeBlock language="bash">yarn create @flecks/{type} {pkg}</CodeBlock>
</TabItem>
<TabItem value="bun" label="Bun">
<CodeBlock language="bash">bun create @flecks/{type} {pkg}</CodeBlock>
</TabItem>
</Tabs>
);
}

View File

@ -0,0 +1,20 @@
import React from 'react';
export default function ImageRow({children}) {
return (
<div style={{display: 'flex', marginBottom: 'var(--ifm-leading)'}}>
{React.Children.map(children, (child, i) => {
const count = React.Children.count(children);
return React.cloneElement(
child,
{
style: {
marginLeft: 0 === i ? '0' : `calc(var(--ifm-spacing-horizontal) * 1 / ${count - 1})`,
width: `calc(${100 / count}% - var(--ifm-spacing-horizontal) / ${count})`,
},
},
);
})}
</div>
);
}

View File

@ -5,13 +5,13 @@ import TabItem from '@theme/TabItem';
export default function PackageInstall({pkg}) {
return (
<Tabs>
<TabItem value="npm" label="npm" default>
<TabItem value="npm" label="npm">
<CodeBlock language="bash">npm install {pkg}</CodeBlock>
</TabItem>
<TabItem value="yarn" label="Yarn" default>
<TabItem value="yarn" label="Yarn">
<CodeBlock language="bash">yarn add {pkg}</CodeBlock>
</TabItem>
<TabItem value="bun" label="Bun" default>
<TabItem value="bun" label="Bun">
<CodeBlock language="bash">bun install {pkg}</CodeBlock>
</TabItem>
</Tabs>

View File

@ -22,8 +22,16 @@ export default {
},
collapsed: false,
items: [
'documentation',
'cli',
'environment',
'database',
'sockets',
'react',
'electron',
'isomorphism',
'redux',
'repl',
'documentation',
],
},
{

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

BIN
website/static/flecks.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB