BBob a BBCode processor

BBob is a tool to parse and transform [BBCode](https://en.wikipedia.org/wiki/BBCode) written in pure javascript, no dependencies [![Tests](https://github.com/JiLiZART/BBob/actions/workflows/test.yml/badge.svg)](https://github.com/JiLiZART/BBob/actions/workflows/test.yml) [![Benchmark](https://github.com/JiLiZART/BBob/actions/workflows/benchmark.yml/badge.svg)](https://github.com/JiLiZART/BBob/actions/workflows/benchmark.yml) codecov CodeFactor Known Vulnerabilities ## 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/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/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/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/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) ### Basic usage ```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); // Text ``` ### React usage ```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 () => ( [table] [tr] [td]table 1[/td] [td]table 2[/td] [/tr] [tr] [td]table 3[/td] [td]table 4[/td] [/tr] [/table] ) ``` ```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 ```shell npm i @bbob/vue2 @bbob/preset-vue ``` ```js import Vue from 'vue' import VueBbob from '@bbob/vue2'; Vue.use(VueBbob); ``` ```html ``` More examples available in examples folder ### Parse options #### 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); // [b]Text[/b] ``` #### 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); // Text
[b]Text[/b]
``` #### 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); // Text[b]Text[/b] ``` ### Presets Its a way to transform parsed BBCode AST tree to another tree by rules in preset #### Create your own preset ```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 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) //

Text

``` #### React Preset 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); ``` [![Edit lp7q9yj0lq](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/lp7q9yj0lq) ### React usage #### Component 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 = () => ( [quote]Text[/quote] ) render() //

Text

``` [![Edit 306pzr9k5p](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/306pzr9k5p) #### Render prop 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 }) => ( {text} ) render() // Super easy to render ``` [![Edit x7w52lqmzz](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/jovial-cohen-bvo08) ### PostHTML usage ### Create Plugin 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( https://github.com/JiLiZART/BBob Other text without link ); ``` [![Edit x7w52lqmzz](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/bbob-plugin-example-dmq1bh) ### Benchmarks To test on your machine run ```shell npm run build node benchmark ``` Tested on Node v12.18.3 | Package | Ops/sec | Runs sampled | |----------------------|----------------------|-------------------| | regex/parser | 6.02 ops/sec ±2.77% | (20 runs sampled) | | ya-bbcode | 10.70 ops/sec ±1.94% | (31 runs sampled) | | xbbcode/parser | 107 ops/sec ±2.29% | (69 runs sampled) | | @bbob/parser | 140 ops/sec ±1.11% | (78 runs sampled) | ![Jetbrains IDEA](https://resources.jetbrains.com/storage/products/company/brand/logos/jb_square.svg) Developed with <3 using JetBrains