mirror of
https://github.com/tenrok/BBob.git
synced 2026-05-15 11:59:37 +03:00
449 lines
13 KiB
Markdown
449 lines
13 KiB
Markdown
|
|
<p align="center">
|
|
<img alt="BBob a BBCode processor" src="https://github.com/JiLiZART/bbob/blob/master/.github/logo.png?raw=true" />
|
|
</p>
|
|
|
|
<p align="center">
|
|
BBob is a tool to parse and transform [BBCode](https://en.wikipedia.org/wiki/BBCode)
|
|
written in pure javascript, no dependencies
|
|
</p>
|
|
|
|
[](https://github.com/JiLiZART/BBob/actions/workflows/test.yml)
|
|
[](https://github.com/JiLiZART/BBob/actions/workflows/benchmark.yml)
|
|
<a href='https://coveralls.io/github/JiLiZART/BBob?branch=master'>
|
|
<img src='https://coveralls.io/repos/github/JiLiZART/BBob/badge.svg?branch=master' alt='Coverage Status' />
|
|
</a>
|
|
<a href="https://www.codefactor.io/repository/github/jilizart/bbob">
|
|
<img src="https://www.codefactor.io/repository/github/jilizart/bbob/badge" alt="CodeFactor">
|
|
</a>
|
|
<a href="https://snyk.io/test/github/JiLiZART/bbob?targetFile=package.json">
|
|
<img src="https://snyk.io/test/github/JiLiZART/bbob/badge.svg?targetFile=package.json" alt="Known Vulnerabilities">
|
|
</a>
|
|
|
|
## Packages
|
|
|
|
| Package | Status | Size | Description |
|
|
|----------------------|------------------------------------------------------------|-----------------------------|---------------------------|
|
|
| @bbob/react | [![@bbob/react-status]][@bbob/react-package] | ![@bbob/react-size] | React renderer |
|
|
| @bbob/preset-react | [![@bbob/preset-react-status]][@bbob/preset-react-package] | ![@bbob/preset-react-size] | React default tags preset |
|
|
| @bbob/vue3 | [![@bbob/vue3-status]][@bbob/vue3-package] | ![@bbob/vue3-size] | Vue 3 renderer |
|
|
| @bbob/vue2 | [![@bbob/vue2-status]][@bbob/vue2-package] | ![@bbob/vue2-size] | Vue 2 renderer |
|
|
| @bbob/preset-vue | [![@bbob/preset-vue-status]][@bbob/preset-vue-package] | ![@bbob/preset-vue-size] | Vue default tags preset |
|
|
| @bbob/html | [![@bbob/html-status]][@bbob/html-package] | ![@bbob/html-size] | HTML renderer |
|
|
| @bbob/preset-html5 | [![@bbob/preset-html5-status]][@bbob/preset-html5-package] | ![@bbob/preset-html5-size] | HTML5 default tags preset |
|
|
| @bbob/core | [![@bbob/core-status]][@bbob/core-package] | ![@bbob/core-size] | Core package |
|
|
|
|
[@bbob/core-status]: https://img.shields.io/npm/v/@bbob/core.svg
|
|
|
|
[@bbob/react-status]: https://img.shields.io/npm/v/@bbob/react.svg
|
|
[@bbob/preset-react-status]: https://img.shields.io/npm/v/@bbob/preset-react.svg
|
|
|
|
[@bbob/vue3-status]: https://img.shields.io/npm/v/@bbob/vue3.svg
|
|
[@bbob/vue2-status]: https://img.shields.io/npm/v/@bbob/vue2.svg
|
|
[@bbob/preset-vue-status]: https://img.shields.io/npm/v/@bbob/preset-vue.svg
|
|
|
|
[@bbob/html-status]: https://img.shields.io/npm/v/@bbob/html.svg
|
|
[@bbob/preset-html5-status]: https://img.shields.io/npm/v/@bbob/preset-html5.svg
|
|
|
|
|
|
[@bbob/core-size]: https://badgen.net/bundlephobia/minzip/@bbob/core
|
|
|
|
[@bbob/react-size]: https://badgen.net/bundlephobia/minzip/@bbob/react
|
|
[@bbob/preset-react-size]: https://badgen.net/bundlephobia/minzip/@bbob/preset-react
|
|
|
|
[@bbob/vue3-size]: https://badgen.net/bundlephobia/minzip/@bbob/vue3
|
|
[@bbob/vue2-size]: https://badgen.net/bundlephobia/minzip/@bbob/vue2
|
|
[@bbob/preset-vue-size]: https://badgen.net/bundlephobia/minzip/@bbob/preset-vue
|
|
|
|
[@bbob/html-size]: https://badgen.net/bundlephobia/minzip/@bbob/html
|
|
[@bbob/preset-html5-size]: https://badgen.net/bundlephobia/minzip/@bbob/preset-html5
|
|
|
|
[@bbob/core-package]: https://npmjs.com/package/@bbob/core
|
|
|
|
[@bbob/react-package]: https://npmjs.com/package/@bbob/react
|
|
[@bbob/preset-react-package]: https://npmjs.com/package/@bbob/preset-react
|
|
|
|
[@bbob/vue3-package]: https://npmjs.com/package/@bbob/vue3
|
|
[@bbob/vue2-package]: https://npmjs.com/package/@bbob/vue2
|
|
[@bbob/preset-vue-package]: https://npmjs.com/package/@bbob/preset-vue
|
|
|
|
[@bbob/html-package]: https://npmjs.com/package/@bbob/html
|
|
[@bbob/preset-html5-package]: https://npmjs.com/package/@bbob/preset-html5
|
|
|
|
[DEMO Playground](https://codepen.io/JiLiZART/full/vzMvpd)
|
|
|
|
## Table of contents
|
|
* [Usage](#usage)
|
|
* [Basic usage](#basic-usage)
|
|
* [React usage](#react-usage)
|
|
* [Vue 2 usage](#vue2-usage)
|
|
* [Parse Options](#parse-options)
|
|
* [Presets](#presets)
|
|
* [Create your own preset](#create-preset)
|
|
* [HTML Preset](#html-preset)
|
|
* [React Preset](#react-preset)
|
|
* [React usage](#react)
|
|
* [Component](#react-component)
|
|
* [Render prop](#react-render)
|
|
* [PostHTML usage](#posthtml)
|
|
* [Create Plugin](#plugin)
|
|
* [Benchmarks](#benchmarks)
|
|
* [Donate](#donations)
|
|
|
|
### Basic usage <a name="basic-usage"></a>
|
|
|
|
```shell
|
|
npm i @bbob/html @bbob/preset-html5
|
|
```
|
|
|
|
```js
|
|
import bbobHTML from '@bbob/html'
|
|
import presetHTML5 from '@bbob/preset-html5'
|
|
|
|
const processed = bbobHTML(`[i]Text[/i]`, presetHTML5())
|
|
|
|
console.log(processed); // <span style="font-style: italic;">Text</span>
|
|
```
|
|
|
|
### React usage <a name="react-usage"></a>
|
|
|
|
```shell
|
|
npm i @bbob/react @bbob/preset-react
|
|
```
|
|
|
|
```jsx
|
|
import React from 'react'
|
|
import BBCode from '@bbob/react';
|
|
import presetReact from '@bbob/preset-react';
|
|
|
|
const plugins = [presetReact()];
|
|
|
|
export default () => (
|
|
<BBCode plugins={plugins}>
|
|
[table]
|
|
[tr]
|
|
[td]table 1[/td]
|
|
[td]table 2[/td]
|
|
[/tr]
|
|
[tr]
|
|
[td]table 3[/td]
|
|
[td]table 4[/td]
|
|
[/tr]
|
|
[/table]
|
|
</BBCode>
|
|
)
|
|
```
|
|
|
|
```jsx
|
|
import { render } from '@bbob/react'
|
|
|
|
export default () => render(`
|
|
[table]
|
|
[tr]
|
|
[td]table 1[/td]
|
|
[td]table 2[/td]
|
|
[/tr]
|
|
[tr]
|
|
[td]table 3[/td]
|
|
[td]table 4[/td]
|
|
[/tr]
|
|
[/table]
|
|
`)
|
|
```
|
|
|
|
### Vue 2 usage <a name="vue2-usage"></a>
|
|
|
|
```shell
|
|
npm i @bbob/vue2 @bbob/preset-vue
|
|
```
|
|
|
|
```js
|
|
import Vue from 'vue'
|
|
import VueBbob from '@bbob/vue2';
|
|
|
|
Vue.use(VueBbob);
|
|
```
|
|
|
|
```html
|
|
<template>
|
|
<div class="html">
|
|
<h2>Generated HTML here</h2>
|
|
<bbob-bbcode container="div" :plugins="plugins">{{ bbcode }}</bbob-bbcode>
|
|
</div>
|
|
</template>
|
|
<script>
|
|
import Vue from 'vue'
|
|
import preset from '@bbob/preset-vue'
|
|
|
|
export default Vue.extend({
|
|
name: 'App',
|
|
data() {
|
|
return {
|
|
bbcode: 'Text [b]bolded[/b] and [i]Some Name[/i]',
|
|
plugins: [
|
|
preset()
|
|
],
|
|
}
|
|
}
|
|
})
|
|
</script>
|
|
```
|
|
|
|
More examples available in <a href="https://github.com/JiLiZART/BBob/tree/master/examples">examples folder</a>
|
|
|
|
### Parse options <a name="parse-options"></a>
|
|
|
|
#### onlyAllowTags
|
|
|
|
Parse only allowed tags
|
|
|
|
```js
|
|
import bbobHTML from '@bbob/html'
|
|
import presetHTML5 from '@bbob/preset-html5'
|
|
|
|
const processed = bbobHTML(`[i][b]Text[/b][/i]`, presetHTML5(), { onlyAllowTags: ['i'] })
|
|
|
|
console.log(processed); // <span style="font-style: italic;">[b]Text[/b]</span>
|
|
```
|
|
|
|
#### contextFreeTags
|
|
|
|
Enable context free mode that ignores parsing all tags inside given tags
|
|
|
|
```js
|
|
import bbobHTML from '@bbob/html'
|
|
import presetHTML5 from '@bbob/preset-html5'
|
|
|
|
const processed = bbobHTML(`[b]Text[/b][code][b]Text[/b][/code]`, presetHTML5(), { contextFreeTags: ['code'] })
|
|
|
|
console.log(processed); // <span style="font-weight: bold;">Text</span><pre>[b]Text[/b]</pre>
|
|
```
|
|
|
|
#### enableEscapeTags
|
|
|
|
Enable escape support for tags
|
|
|
|
```js
|
|
import bbobHTML from '@bbob/html'
|
|
import presetHTML5 from '@bbob/preset-html5'
|
|
|
|
const processed = bbobHTML(`[b]Text[/b]'\\[b\\]Text\\[/b\\]'`, presetHTML5(), { enableEscapeTags: true })
|
|
|
|
console.log(processed); // <span style="font-weight: bold;">Text</span>[b]Text[/b]
|
|
```
|
|
|
|
#### caseFreeTags
|
|
|
|
Allows to parse case insensitive tags like `[h1]some[/H1]` -> `<h1>some</h1>`
|
|
|
|
```js
|
|
import bbobHTML from '@bbob/html'
|
|
import presetHTML5 from '@bbob/preset-html5'
|
|
|
|
const processed = bbobHTML(`[h1]some[/H1]`, presetHTML5(), { caseFreeTags: true })
|
|
|
|
console.log(processed); // <h1>some</h1>
|
|
```
|
|
|
|
```js
|
|
import bbobHTML from '@bbob/html'
|
|
import presetHTML5 from '@bbob/preset-html5'
|
|
|
|
const processed = bbobHTML(`[b]Text[/b]'\\[b\\]Text\\[/b\\]'`, presetHTML5(), { enableEscapeTags: true })
|
|
|
|
console.log(processed); // <span style="font-weight: bold;">Text</span>[b]Text[/b]
|
|
```
|
|
|
|
|
|
### Presets <a name="basic"></a>
|
|
|
|
Its a way to transform parsed BBCode AST tree to another tree by rules in preset
|
|
|
|
#### Create your own preset <a name="create-preset"></a>
|
|
|
|
```js
|
|
import { createPreset } from '@bbob/preset'
|
|
|
|
export default createPreset({
|
|
quote: (node) => ({
|
|
tag: 'blockquote',
|
|
attrs: node.attrs,
|
|
content: [{
|
|
tag: 'p',
|
|
attrs: {},
|
|
content: node.content,
|
|
}],
|
|
}),
|
|
})
|
|
```
|
|
|
|
#### HTML Preset <a name="html-preset"></a>
|
|
|
|
Also you can use predefined preset for HTML
|
|
|
|
```js
|
|
import html5Preset from '@bbob/preset-html5/es'
|
|
import { render } from '@bbob/html/es'
|
|
import bbob from '@bbob/core'
|
|
|
|
console.log(bbob(html5Preset()).process(`[quote]Text[/quote]`, { render }).html) // <blockquote><p>Text</p></blockquote>
|
|
```
|
|
|
|
#### React Preset <a name="react-preset"></a>
|
|
|
|
Also you can use predefined preset for React
|
|
|
|
```js
|
|
import reactPreset from "@bbob/preset-react";
|
|
import reactRender from "@bbob/react/es/render";
|
|
|
|
const preset = reactPreset.extend((tags, options) => ({
|
|
...tags,
|
|
quote: node => ({
|
|
tag: "blockquote",
|
|
content: node.content
|
|
})
|
|
}));
|
|
|
|
const result = reactRender(`[quote]Text[/quote]`, reactPreset());
|
|
|
|
/*
|
|
It produces a VDOM Nodes equal to
|
|
React.createElement('blockquote', 'Text')
|
|
*/
|
|
document.getElementById("root").innerHTML = JSON.stringify(result, 4);
|
|
```
|
|
|
|
[](https://codesandbox.io/s/lp7q9yj0lq)
|
|
|
|
### React usage <a name="react"></a>
|
|
|
|
#### Component <a name="react-component"></a>
|
|
|
|
Or you can use React Component
|
|
|
|
```js
|
|
import React from 'react'
|
|
import { render } from 'react-dom'
|
|
|
|
import BBCode from '@bbob/react/es/Component'
|
|
import reactPreset from '@bbob/preset-react/es'
|
|
|
|
const MyComponent = () => (
|
|
<BBCode plugins={[reactPreset()]} options={{ onlyAllowTags: ['i'] }}>
|
|
[quote]Text[/quote]
|
|
</BBCode>
|
|
)
|
|
|
|
render(<MyComponent />) // <div><blockquote><p>Text</p></blockquote></div>
|
|
```
|
|
[](https://codesandbox.io/s/306pzr9k5p)
|
|
|
|
|
|
#### Render prop <a name="react-render"></a>
|
|
|
|
Or pass result as render prop
|
|
|
|
```js
|
|
import React from "react";
|
|
import { render } from 'react-dom'
|
|
|
|
import reactRender from '@bbob/react/es/render'
|
|
import reactPreset from '@bbob/preset-react/es'
|
|
|
|
const toReact = input => reactRender(input, reactPreset())
|
|
|
|
const text = toReact('[b]Super [i]easy[/i][/b] [u]to[/u] render')
|
|
|
|
const App = ({ renderProp }) => (
|
|
<span>{text}</span>
|
|
)
|
|
|
|
render(<App />) // <span><span style="font-weight: bold;">Super <span style="font-style: italic;">easy</span></span> <span style="text-decoration: underline;">to</span> render</span>
|
|
```
|
|
|
|
[](https://codesandbox.io/s/jovial-cohen-bvo08)
|
|
|
|
### PostHTML usage <a name="posthtml"></a>
|
|
|
|
### Create Plugin <a name="plugin"></a>
|
|
|
|
For example lets parse all strings that similar to links like "https://some-site.com"
|
|
|
|
```js
|
|
import { createRoot } from "react-dom/client";
|
|
|
|
import BBCode from "@bbob/react/es/Component";
|
|
import TagNode from "@bbob/plugin-helper/es/TagNode";
|
|
import { isStringNode } from "@bbob/plugin-helper/es";
|
|
|
|
const URL_RE = new RegExp(
|
|
`([--:\\w?@%&+~#=]+\\/*\\.[a-z]{2,4}\\/{0,2})((?:[?&](?:\\w+)=(?:\\w+))+|[^^).|,][--:\\w?@%&+~#=()_]+)?`,
|
|
"g"
|
|
);
|
|
|
|
const isValidUrl = (url) => URL_RE.test(url);
|
|
|
|
const linkParsePlugin = (tree) => {
|
|
return tree.walk((node) => {
|
|
if (isStringNode(node) && isValidUrl(node)) {
|
|
return TagNode.create(
|
|
"a",
|
|
{
|
|
href: node
|
|
},
|
|
`Url to: ${node}`
|
|
);
|
|
}
|
|
|
|
return node;
|
|
});
|
|
};
|
|
|
|
const rootElement = document.getElementById("root");
|
|
const root = createRoot(rootElement);
|
|
|
|
root.render(
|
|
<BBCode plugins={[linkParsePlugin]}>
|
|
https://github.com/JiLiZART/BBob Other text without link
|
|
</BBCode>
|
|
);
|
|
```
|
|
|
|
[](https://codesandbox.io/s/bbob-plugin-example-dmq1bh)
|
|
|
|
|
|
### Benchmarks <a name="benchmarks"></a>
|
|
|
|
To test on your machine run
|
|
```shell
|
|
npm run build
|
|
node benchmark
|
|
```
|
|
|
|
Tested on Node v20.11.1
|
|
|
|
| Package | Ops/sec |
|
|
|----------------------|----------------------|
|
|
| regex/parser | `6 ops/sec` |
|
|
| ya-bbcode | `11 ops/sec` |
|
|
| xbbcode/parser | `102 ops/sec` |
|
|
| @bbob/parser | `174 ops/sec` |
|
|
|
|
[Checkout Benckmark job results](https://github.com/JiLiZART/BBob/actions/workflows/benchmark.yml)
|
|
|
|
### Donations <a name="donations"></a>
|
|
|
|
You can support this projecti with donation in:
|
|
|
|
*Bitcoin:* `bc1qx34sx3zmfd5e2km607p8s8t30d4rt33d2l9pwt`
|
|
|
|
*USDT(TRC20):* `TT94uVjJho8n47xbdfNYz6vdebgmKFpxAT`
|
|
|
|
|
|
Also thanks to support
|
|
|
|

|
|
|
|
Developed with <3 using JetBrains
|