Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/joe-sky/narrative
A compiler tool for create neater control flow tags such as <If>/<For>/<Switch> for JSX/TSX.
https://github.com/joe-sky/narrative
jsx react tsx
Last synced: 3 months ago
JSON representation
A compiler tool for create neater control flow tags such as <If>/<For>/<Switch> for JSX/TSX.
- Host: GitHub
- URL: https://github.com/joe-sky/narrative
- Owner: joe-sky
- License: mit
- Created: 2020-07-24T09:32:40.000Z (over 4 years ago)
- Default Branch: master
- Last Pushed: 2023-11-10T14:21:11.000Z (about 1 year ago)
- Last Synced: 2024-09-29T06:41:09.812Z (3 months ago)
- Topics: jsx, react, tsx
- Language: Rust
- Homepage:
- Size: 1.04 MB
- Stars: 9
- Watchers: 2
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# Narrative
## Packages
| Package | Badges |
| ------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| [@narrative/control-flow](https://github.com/joe-sky/narrative/tree/master/packages/control-flow) | |
| [@narrative/babel-plugin-compiler](https://github.com/joe-sky/narrative/tree/master/packages/babel-plugin-compiler) | |
| [@narrative/swc-plugin-compiler](https://github.com/joe-sky/narrative/tree/master/packages/swc-plugin-compiler) | |
| [@narrative/vite-plugin-compiler](https://github.com/joe-sky/narrative/tree/master/packages/vite-plugin-compiler) | |## Introduction
`Narrative`(abbreviated as `nt`) is a compiler tool for create neater control flow tags such as ``/``/`` for React JSX/TSX. It does so by transforming component-like control flow tags to their JavaScript counterparts:
```tsx
Hello World!;// Compiled ↓ ↓ ↓ ↓ ↓ ↓
{
condition() ? 'Hello World!' : null;
}
```The inspiration mainly comes from [jsx-control-statements](https://github.com/AlexGilleran/jsx-control-statements), this tool can be seen as an alternative solution with syntactic differences to `jsx-control-statements`. It also only depends on Babel(or SWC), and it's compatible with React and React Native.
> In addition, its API has also referenced the following projects:
- [React If](https://github.com/romac/react-if)
- [React Loops](https://github.com/leebyron/react-loops)
- [Solid(Control Flow)](https://www.solidjs.com/docs/latest/api#control-flow)### Basic Overview
```js
import { useState, FC } from 'react';
import { If, ElseIf, Else, For, Empty, Switch, Case, Default } from '@narrative/control-flow';const App: FC = () => {
const [todos, setTodos] = useState([]);const addTodo = () => {
setTodos(todos.concat(`Item ${todos.length}`));
};return (
{(todo, { index }) => (
5}>
- {todo * 3}
10}>
- {todo * 4}
- {todo * 5}
)}
- No data
{(item, { key }) =>- {item}
}
- No data
1
2
3/4/5
More than 2
);
};
```### Highlights
`Narrative` has similar or different features as `jsx-control-statements`:
- ✨ Tag names are more like native JavaScript control statements.
- 💫 More concise syntax keywords of tags.
- ⭐ Tags supports full TypeScript inference.
- ⚡ No runtime code, just need compiler.
- 🔥 Supports both Babel and SWC compilers.
- 🔧 Support syntax error prompt for Babel and SWC.## Table of Contents
- [Examples](#examples)
- [Installation](#installation)
- [Using with Babel](#using-with-babel)
- [Using with SWC](#using-with-swc)
- [Usage](#usage)
- [If Tag](#if-tag)
- [<If>](#if)
- [<Else>](#else)
- [<ElseIf>](#elseif)
- [Function children of <If> <ElseIf> <Else>](#function-children-of-if-elseif-else)
- [Switch Tag](#switch-tag)
- [<Switch> <Case>](#switch-case)
- [Multiple values of <Case>](#multiple-values-of-case)
- [<Default>](#default)
- [Function children of <Case> <Default>](#function-children-of-case-default)
- [For Tag](#for-tag)
- [<For of>](#for-of)
- [<For in>](#for-in)
- [Loop iteration metadata](#loop-iteration-metadata)
- [<Empty>](#empty)## Examples
### React + Vite(use Babel compiler)
narrative-react-vite-demo[https://github.com/joe-sky/narrative-react-vite-demo]
### React + Vite(use SWC compiler)
narrative-react-vite-swc-demo[https://github.com/joe-sky/narrative-react-vite-swc-demo]
## Installation
### Using with Babel
```bash
npm i @narrative/control-flow @narrative/babel-plugin-compiler
```Configure `Babel`:
```json
{
"plugins": ["@narrative/compiler"]
}
```### Using with SWC
```bash
npm install @narrative/control-flow @narrative/swc-plugin-compiler
```Configure `SWC`:
```json
{
"jsc": {
"experimental": {
"plugins": [["@narrative/swc-plugin-compiler", {}]]
}
}
}
```- If your `swc_core` version is lower than 0.8, please install the old version:
```bash
npm install @narrative/[email protected]
```## Usage
Each JSX tags must to be imported from `@narrative/control-flow` when use:
```tsx
import { If, Else, ElseIf } from '@narrative/control-flow';function render(no: number) {
return (
1
2
0
);
}
```As above the usage is similar to the regular React components. Each tags and its props also support TypeScript type checking.
### If Tag
`` tag is an alternative syntax for conditional logic in JSX, it is similar to the `if statement` in JavaScript. Also supports ``, ``, and the syntax design fully supports JSX native formatting. Simple examples:
```tsx
import { If } from '@narrative/control-flow';// simple
IfBlock
// using multiple child elements or expressions
one
{"two"}
three
four
= 5}>
five
```
Click here to view how the compiler works
```tsx
5}>
10}>
;
// Compiled ↓ ↓ ↓ ↓ ↓ ↓
{
index > 5 ?
}
```
#### <If>
If only use ``, the children of `` will be returned when the value of `when prop` is true.
| Prop Name | Prop Type | Required |
| --------- | --------- | ------------------ |
| when | boolean | :white_check_mark: |
```tsx
import { If } from '@narrative/control-flow';
1}>
IfBlock1
IfBlock2
;
// Compiled ↓ ↓ ↓ ↓ ↓ ↓
{
no > 1 ? (
<>
IfBlock1
IfBlock2
>
) : null;
}
```
#### <Else>
Only one `` can be added within a ``. If the `when prop` value of `` is false, then the children of `` will be returned:
```tsx
import { If, Else } from '@narrative/control-flow';
1}>
IfBlock1
IfBlock2
IfBlock3
IfBlock4
;
// Compiled ↓ ↓ ↓ ↓ ↓ ↓
{
no > 1 ? (
<>
IfBlock1
IfBlock2
>
) : (
<>
IfBlock3
IfBlock4
>
);
}
```
#### <ElseIf>
Multiple `` can be added within a ``, and any one of the tag children with a `when prop` of true will be returned:
```tsx
import { If, Else, ElseIf } from '@narrative/control-flow';
10}>
IfBlock1
5}>
IfBlock2
1}>
IfBlock3
IfBlock4
;
// Compiled ↓ ↓ ↓ ↓ ↓ ↓
{
no > 10 ? (
IfBlock1
) : no > 5 ? (
IfBlock2
) : no > 1 ? (
IfBlock3
) : (
IfBlock4
);
}
```
#### Function children of <If> <ElseIf> <Else>
The children of ``, ``, `` also supports a function. It can be used for logic that calculates first and then renders:
```tsx
import { If } from '@narrative/control-flow';
1}>
{() => {
const blockName = 'IfBlock';
return (
<>
{blockName}1
{blockName}2
>
);
}}
{() => {
const blockName = 'ElseBlock';
return (
<>
{blockName}1
{blockName}2
>
);
}}
;
// Compiled ↓ ↓ ↓ ↓ ↓ ↓
{
no > 1
? (() => {
const blockName = 'IfBlock';
return (
<>
{blockName}1
{blockName}2
>
);
})()
: (() => {
const blockName = 'ElseBlock';
return (
<>
{blockName}1
{blockName}2
>
);
})();
}
```
### Switch Tag
`` tag is an alternative syntax for multi branch conditional statements in JSX, it is similar to the `switch statement` in JavaScript. Also supports ``, ``. Simple examples:
```tsx
import { Switch } from '@narrative/control-flow';
1
2
3
0
;
// Compiled ↓ ↓ ↓ ↓ ↓ ↓
{
todos.length === 1 ? (
1
) : todos.length === 2 ? (
2
) : todos.length === 3 ? (
3
) : (
0
);
}
```
#### <Switch> <Case>
`` requires to set the `value prop`.
| Prop Name | Prop Type | Required |
| --------- | --------- | ------------------ |
| value | any | :white_check_mark: |
Each `` matches the `value prop of ` via `is prop`. At least one `` is required within the ``.
| Prop Name | Prop Type | Required |
| --------- | --------- | ------------------------------- |
| is | any | Either `is` or `in` is required |
Example:
```tsx
import { Switch, Case } from '@narrative/control-flow';
1
2
;
// Compiled ↓ ↓ ↓ ↓ ↓ ↓
{
todos.length === 1 ? 1 : todos.length === 2 ? 2 : null;
}
```
As above, the `` use `strict equality(===)` when matching.
#### Multiple values of <Case>
If multiple values need to be matched in one ``, the `in prop` can be used.
| Prop Name | Prop Type | Required |
| --------- | -------------------- | ------------------------------- |
| in | ArrayLike<any> | Either `is` or `in` is required |
Example:
```tsx
import { Switch, Case } from '@narrative/control-flow';
1
2
3/4/5
;
// Compiled ↓ ↓ ↓ ↓ ↓ ↓
{
todos.length === 1 ? (
1
) : todos.length === 2 ? (
2
) : [3, 4, 5].includes(todos.length) ? (
3/4/5
) : null;
}
```
#### <Default>
`` can have one `` inside it, which will be matched to the `` when all `` do not match:
```tsx
import { Switch, Case, Default } from '@narrative/control-flow';
1
2/3
0
;
// Compiled ↓ ↓ ↓ ↓ ↓ ↓
{
todos.length === 1 ? 1 : [2, 3].includes(todos.length) ? 2/3 : 0;
}
```
#### Function children of <Case> <Default>
The children of ``, `` also supports a function. It can be used for logic that calculates first and then renders:
```tsx
import { Switch, Case, Default } from '@narrative/control-flow';
{() => {
const blockName = 'CaseBlock';
return (
<>
{blockName}1
{blockName}2
>
);
}}
{() => {
const blockName = 'DefaultBlock';
return (
<>
{blockName}1
{blockName}2
>
);
}}
;
// Compiled ↓ ↓ ↓ ↓ ↓ ↓
{
todos.length === 1
? (() => {
const blockName = 'CaseBlock';
return (
<>
{blockName}1
{blockName}2
>
);
})()
: (() => {
const blockName = 'DefaultBlock';
return (
<>
{blockName}1
{blockName}2
>
);
})();
}
```
### For Tag
`` tag is an alternative syntax for loops logic in JSX. Example:
```tsx
import { For } from '@narrative/control-flow';
{(todo, { index }) =>
;
// Compiled ↓ ↓ ↓ ↓ ↓ ↓
{
(__arr => {
if (__arr?.length) {
return __arr.map((todo, index) => {todo}, this);
}
return
})(todos);
}
```
#### <For of>
`` loops is similar to the `for of statement` in JavaScript, the `of prop` accepts Arrays and Array-likes.
| Prop Name | Prop Type | Required |
| --------- | -------------------- | ------------------ |
| of | ArrayLike<any> | :white_check_mark: |
The loop callback function is in children of ``, example:
```tsx
import { For } from '@narrative/control-flow';
{(todo, { index }, arr) =>
// Compiled ↓ ↓ ↓ ↓ ↓ ↓
{
todos?.map?.((todo, index, arr) => {
return {todo};
}, this) || null;
}
```
As above the callback function parameters:
| Parameter order | Type | Description | Required |
| --------------- | --------------------------------------------------- | --------------------------- | ------------------ |
| first | type of Array items | each items of Array | :white_check_mark: |
| second | [Loop iteration metadata](#loop-iteration-metadata) | mainly using index of Array | |
| third | type of Array | looping Array variable | |
#### <For in>
`` loops is similar to the `for in statement` in JavaScript, the `in prop` accepts an Object.
| Prop Name | Prop Type | Required |
| --------- | ---------------- | ------------------ |
| in | Record | :white_check_mark: |
The loop callback function is in children of ``, example:
```tsx
import { For } from '@narrative/control-flow';
{(item, { key }, obj) => {item}};
// Compiled ↓ ↓ ↓ ↓ ↓ ↓
{
(__obj => {
const __keys = __obj ? Object.keys(__obj) : [];
if (__keys.length) {
return __keys.map(key => {
const item = __obj[key];
const obj = __obj;
return {item};
}, this);
}
})({ a: 1, b: 2, c: 3 });
}
```
As above the callback function parameters:
| Parameter order | Type | Description | Required |
| --------------- | --------------------------------------------------- | ---------------------------- | ------------------ |
| first | type of Object values | each values of source object | :white_check_mark: |
| second | [Loop iteration metadata](#loop-iteration-metadata) | mainly using key and keys | |
| third | type of Object | looping Object variable | |
#### Loop iteration metadata
Access additional information about each iteration by the second callback argument:
- `index`: A number from 0 to the length of the Arrays or Objects.
- `key`: The key for this item in Objects, same as `index` for Arrays.
- `keys`: The keys Arrays for Objects.
```tsx
import { For, If } from '@narrative/control-flow';
{(item, { key, index, keys }) => (
{item}
2 && index > 1}>{index}
)}
;
// Compiled ↓ ↓ ↓ ↓ ↓ ↓
{
(__obj => {
const __keys = __obj ? Object.keys(__obj) : [];
if (__keys.length) {
return __keys.map((key, index) => {
const item = __obj[key];
const keys = __keys;
return (
{item}
{keys.length > 2 && index > 1 ? index : null}
);
}, this);
}
})({ a: 1, b: 2, c: 3 });
}
```
#### <Empty>
A common pattern when rendering a collection is to render a special case when the collection is empty. Optionally provide a `` to handle this case for both `` and `` loops. `` should be set in the children of ``, Example:
```tsx
import { For, Empty } from '@narrative/control-flow';
const emptyObj = {};
{(item, { key }) => {item}}
No Data
;
// Compiled ↓ ↓ ↓ ↓ ↓ ↓
{
(__obj => {
const __keys = __obj ? Object.keys(__obj) : [];
if (__keys.length) {
return __keys.map(key => {
const item = __obj[key];
return {item};
}, this);
}
return 'No Data';
})(emptyObj);
}
```
## The Origin of Name
🤖 `RX-9 Narrative Gundam`, ready to launch!
## License
MIT