From 54dbf9ad00e7405e22fee77239854acaf185a24e Mon Sep 17 00:00:00 2001 From: Boris Date: Sat, 24 Dec 2022 04:19:16 +0300 Subject: [PATCH] feat: add asciidoc editor module (#5954) * feat: add asciidoc editor module * fix storage file extension for asciidoc pages * fix: asciidoc editor + rendering improvements * fix: description list css improvements Co-authored-by: NGPixel --- client/components/admin.vue | 12 - client/components/editor.vue | 1 + .../{markdown/fold.js => common/cmFold.js} | 10 +- client/components/editor/editor-asciidoc.vue | 707 ++++++++++++++++++ client/components/editor/editor-markdown.vue | 34 +- .../editor/editor-modal-editorselect.vue | 179 +---- client/static/svg/editor-icon-asciidoc.svg | 1 + client/themes/default/scss/app.scss | 159 ++++ package.json | 2 + server/helpers/page.js | 1 + server/models/editors.js | 2 + server/modules/editor/asciidoc/definition.yml | 6 + .../rendering/asciidoc-core/definition.yml | 20 + .../rendering/asciidoc-core/renderer.js | 26 + yarn.lock | 71 ++ 15 files changed, 1039 insertions(+), 192 deletions(-) rename client/components/editor/{markdown/fold.js => common/cmFold.js} (92%) create mode 100644 client/components/editor/editor-asciidoc.vue create mode 100644 client/static/svg/editor-icon-asciidoc.svg create mode 100644 server/modules/editor/asciidoc/definition.yml create mode 100644 server/modules/rendering/asciidoc-core/definition.yml create mode 100644 server/modules/rendering/asciidoc-core/renderer.js diff --git a/client/components/admin.vue b/client/components/admin.vue index f6e3722a..1d0762f3 100644 --- a/client/components/admin.vue +++ b/client/components/admin.vue @@ -65,15 +65,6 @@ v-list-item(to='/comments') v-list-item-avatar(size='24', tile): v-icon mdi-comment-text-outline v-list-item-title {{ $t('admin:comments.title') }} - v-list-item(to='/editor', disabled) - v-list-item-avatar(size='24', tile): v-icon(color='grey lighten-2') mdi-playlist-edit - v-list-item-title {{ $t('admin:editor.title') }} - v-list-item(to='/extensions') - v-list-item-avatar(size='24', tile): v-icon mdi-chip - v-list-item-title {{ $t('admin:extensions.title') }} - v-list-item(to='/logging', disabled) - v-list-item-avatar(size='24', tile): v-icon(color='grey lighten-2') mdi-script-text-outline - v-list-item-title {{ $t('admin:logging.title') }} v-list-item(to='/rendering', color='primary') v-list-item-avatar(size='24', tile): v-icon mdi-cogs v-list-item-title {{ $t('admin:rendering.title') }} @@ -104,9 +95,6 @@ v-list-item(to='/utilities', color='primary', v-if='hasPermission(`manage:system`)') v-list-item-avatar(size='24', tile): v-icon mdi-wrench-outline v-list-item-title {{ $t('admin:utilities.title') }} - v-list-item(to='/webhooks', v-if='hasPermission(`manage:system`)', disabled) - v-list-item-avatar(size='24', tile): v-icon(color='grey lighten-2') mdi-webhook - v-list-item-title {{ $t('admin:webhooks.title') }} v-list-group( to='/dev' no-action diff --git a/client/components/editor.vue b/client/components/editor.vue index 15311e9b..7cd577b1 100644 --- a/client/components/editor.vue +++ b/client/components/editor.vue @@ -77,6 +77,7 @@ export default { editorApi: () => import(/* webpackChunkName: "editor-api", webpackMode: "lazy" */ './editor/editor-api.vue'), editorCode: () => import(/* webpackChunkName: "editor-code", webpackMode: "lazy" */ './editor/editor-code.vue'), editorCkeditor: () => import(/* webpackChunkName: "editor-ckeditor", webpackMode: "lazy" */ './editor/editor-ckeditor.vue'), + editorAsciidoc: () => import(/* webpackChunkName: "editor-asciidoc", webpackMode: "lazy" */ './editor/editor-asciidoc.vue'), editorMarkdown: () => import(/* webpackChunkName: "editor-markdown", webpackMode: "lazy" */ './editor/editor-markdown.vue'), editorRedirect: () => import(/* webpackChunkName: "editor-redirect", webpackMode: "lazy" */ './editor/editor-redirect.vue'), editorModalEditorselect: () => import(/* webpackChunkName: "editor", webpackMode: "eager" */ './editor/editor-modal-editorselect.vue'), diff --git a/client/components/editor/markdown/fold.js b/client/components/editor/common/cmFold.js similarity index 92% rename from client/components/editor/markdown/fold.js rename to client/components/editor/common/cmFold.js index 1bc56ef5..52267303 100644 --- a/client/components/editor/markdown/fold.js +++ b/client/components/editor/common/cmFold.js @@ -7,7 +7,13 @@ const maxDepth = 100 const codeBlockStartMatch = /^`{3}[a-zA-Z0-9]+$/ const codeBlockEndMatch = /^`{3}$/ -CodeMirror.registerHelper('fold', 'markdown', function (cm, start) { +export default { + register(lang) { + CodeMirror.registerHelper('fold', lang, foldHandler) + } +} + +function foldHandler (cm, start) { const firstLine = cm.getLine(start.line) const lastLineNo = cm.lastLine() let end @@ -59,4 +65,4 @@ CodeMirror.registerHelper('fold', 'markdown', function (cm, start) { from: CodeMirror.Pos(start.line, firstLine.length), to: CodeMirror.Pos(end, cm.getLine(end).length) } -}) +} diff --git a/client/components/editor/editor-asciidoc.vue b/client/components/editor/editor-asciidoc.vue new file mode 100644 index 00000000..296b2414 --- /dev/null +++ b/client/components/editor/editor-asciidoc.vue @@ -0,0 +1,707 @@ + + + + + diff --git a/client/components/editor/editor-markdown.vue b/client/components/editor/editor-markdown.vue index 6fc2e581..04b5c6aa 100644 --- a/client/components/editor/editor-markdown.vue +++ b/client/components/editor/editor-markdown.vue @@ -124,44 +124,19 @@ span {{$t('editor:markup.insertAssets')}} v-tooltip(right, color='teal') template(v-slot:activator='{ on }') - v-btn.mt-3.animated.fadeInLeft.wait-p2s(icon, tile, v-on='on', dark, disabled, @click='toggleModal(`editorModalBlocks`)').mx-0 - v-icon(:color='activeModal === `editorModalBlocks` ? `teal` : ``') mdi-view-dashboard-outline - span {{$t('editor:markup.insertBlock')}} - v-tooltip(right, color='teal') - template(v-slot:activator='{ on }') - v-btn.mt-3.animated.fadeInLeft.wait-p3s(icon, tile, v-on='on', dark, disabled).mx-0 - v-icon mdi-code-braces - span {{$t('editor:markup.insertCodeBlock')}} - v-tooltip(right, color='teal') - template(v-slot:activator='{ on }') - v-btn.mt-3.animated.fadeInLeft.wait-p4s(icon, tile, v-on='on', dark, disabled).mx-0 - v-icon mdi-movie - span {{$t('editor:markup.insertVideoAudio')}} - v-tooltip(right, color='teal') - template(v-slot:activator='{ on }') - v-btn.mt-3.animated.fadeInLeft.wait-p5s(icon, tile, v-on='on', dark, @click='toggleModal(`editorModalDrawio`)').mx-0 + v-btn.mt-3.animated.fadeInLeft.wait-p2s(icon, tile, v-on='on', dark, @click='toggleModal(`editorModalDrawio`)').mx-0 v-icon mdi-chart-multiline span {{$t('editor:markup.insertDiagram')}} - v-tooltip(right, color='teal') - template(v-slot:activator='{ on }') - v-btn.mt-3.animated.fadeInLeft.wait-p6s(icon, tile, v-on='on', dark, disabled).mx-0 - v-icon mdi-function-variant - span {{$t('editor:markup.insertMathExpression')}} - v-tooltip(right, color='teal') - template(v-slot:activator='{ on }') - v-btn.mt-3.animated.fadeInLeft.wait-p7s(icon, tile, v-on='on', dark, disabled).mx-0 - v-icon mdi-table-plus - span {{$t('editor:markup.tableHelper')}} template(v-if='$vuetify.breakpoint.mdAndUp') v-spacer v-tooltip(right, color='teal') template(v-slot:activator='{ on }') - v-btn.mt-3.animated.fadeInLeft.wait-p8s(icon, tile, v-on='on', dark, @click='toggleFullscreen').mx-0 + v-btn.mt-3.animated.fadeInLeft.wait-p3s(icon, tile, v-on='on', dark, @click='toggleFullscreen').mx-0 v-icon mdi-arrow-expand-all span {{$t('editor:markup.distractionFreeMode')}} v-tooltip(right, color='teal') template(v-slot:activator='{ on }') - v-btn.mt-3.animated.fadeInLeft.wait-p9s(icon, tile, v-on='on', dark, @click='toggleHelp').mx-0 + v-btn.mt-3.animated.fadeInLeft.wait-p4s(icon, tile, v-on='on', dark, @click='toggleHelp').mx-0 v-icon(:color='helpShown ? `teal` : ``') mdi-help-circle span {{$t('editor:markup.markdownFormattingHelp')}} .editor-markdown-editor @@ -220,7 +195,6 @@ import 'codemirror/addon/hint/show-hint.js' import 'codemirror/addon/fold/foldcode.js' import 'codemirror/addon/fold/foldgutter.js' import 'codemirror/addon/fold/foldgutter.css' -import './markdown/fold' // Markdown-it import MarkdownIt from 'markdown-it' @@ -251,6 +225,7 @@ import mermaid from 'mermaid' // Helpers import katexHelper from './common/katex' import tabsetHelper from './markdown/tabset' +import cmFold from './common/cmFold' // ======================================== // INIT @@ -337,6 +312,7 @@ md.renderer.rules.paragraph_open = injectLineNumbers md.renderer.rules.heading_open = injectLineNumbers md.renderer.rules.blockquote_open = injectLineNumbers +cmFold.register('markdown') // ======================================== // PLANTUML // ======================================== diff --git a/client/components/editor/editor-modal-editorselect.vue b/client/components/editor/editor-modal-editorselect.vue index b5ce35f5..df2adf96 100644 --- a/client/components/editor/editor-modal-editorselect.vue +++ b/client/components/editor/editor-modal-editorselect.vue @@ -6,57 +6,7 @@ .subtitle-1.white--text {{$t('editor:select.title')}} v-container(grid-list-lg, fluid) v-layout(row, wrap, justify-center) - v-flex(xs4) - v-hover - template(v-slot:default='{ hover }') - v-card.radius-7.primary.animated.fadeInUp( - hover - light - ripple - ) - v-card-text.text-center(@click='') - img(src='/_assets/svg/editor-icon-api.svg', alt='API', style='width: 36px; opacity: .5;') - .body-2.blue--text.mt-2.text--lighten-2 API Docs - .caption.blue--text.text--lighten-1 REST / GraphQL - v-fade-transition - v-overlay( - v-if='hover' - absolute - color='primary' - opacity='.8' - ) - .body-2.mt-7 Coming Soon - v-flex(xs4) - v-hover - template(v-slot:default='{ hover }') - v-card.radius-7.primary.animated.fadeInUp.wait-p1s( - hover - light - ripple - ) - v-card-text.text-center(@click='') - img(src='/_assets/svg/editor-icon-wikitext.svg', alt='WikiText', style='width: 36px; opacity: .5;') - .body-2.blue--text.mt-2.text--lighten-2 Blog - .caption.blue--text.text--lighten-1 Timeline of Posts - v-fade-transition - v-overlay( - v-if='hover' - absolute - color='primary' - opacity='.8' - ) - .body-2.mt-7 Coming Soon - v-flex(xs4) - v-card.radius-7.animated.fadeInUp.wait-p2s( - hover - light - ripple - ) - v-card-text.text-center(@click='selectEditor("code")') - img(src='/_assets/svg/editor-icon-code.svg', alt='Code', style='width: 36px;') - .body-2.primary--text.mt-2 Code - .caption.grey--text Raw HTML - v-flex(xs4) + v-flex(xs6) v-card.radius-7.animated.fadeInUp.wait-p1s( hover light @@ -66,28 +16,8 @@ img(src='/_assets/svg/editor-icon-markdown.svg', alt='Markdown', style='width: 36px;') .body-2.primary--text.mt-2 Markdown .caption.grey--text Plain Text Formatting - v-flex(xs4) - v-hover - template(v-slot:default='{ hover }') - v-card.radius-7.primary.animated.fadeInUp.wait-p2s( - hover - light - ripple - ) - v-card-text.text-center(@click='') - img(src='/_assets/svg/editor-icon-tabular.svg', alt='Tabular', style='width: 36px; opacity: .5;') - .body-2.blue--text.mt-2.text--lighten-2 Tabular - .caption.blue--text.text--lighten-1 Excel-like - v-fade-transition - v-overlay( - v-if='hover' - absolute - color='primary' - opacity='.8' - ) - .body-2.mt-7 Coming Soon - v-flex(xs4) - v-card.radius-7.animated.fadeInUp.wait-p3s( + v-flex(xs6) + v-card.radius-7.animated.fadeInUp.wait-p2s( hover light ripple @@ -96,85 +26,36 @@ img(src='/_assets/svg/editor-icon-ckeditor.svg', alt='Visual Editor', style='width: 36px;') .body-2.mt-2.primary--text Visual Editor .caption.grey--text Rich-text WYSIWYG - //- .caption.blue--text.text--lighten-2 {{$t('editor:select.cannotChange')}} - - v-card.radius-7.mt-2(color='teal darken-3', dark) - v-card-text.text-center.py-4 - .subtitle-1.white--text {{$t('editor:select.customView')}} - v-container(grid-list-lg, fluid) - v-layout(row, wrap, justify-center) v-flex(xs4) - v-hover - template(v-slot:default='{ hover }') - v-card.radius-7.animated.fadeInUp( - hover - light - ripple - ) - v-card-text.text-center(@click='fromTemplate') - img(src='/_assets/svg/icon-cube.svg', alt='From Template', style='width: 42px; opacity: .5;') - .body-2.mt-1.teal--text From Template - .caption.grey--text Use an existing page... + v-card.radius-7.animated.fadeInUp.wait-p3s( + hover + light + ripple + ) + v-card-text.text-center(@click='selectEditor("asciidoc")') + img(src='/_assets/svg/editor-icon-asciidoc.svg', alt='AsciiDoc', style='width: 36px;') + .body-2.primary--text.mt-2 AsciiDoc + .caption.grey--text Plain Text Formatting v-flex(xs4) - v-hover - template(v-slot:default='{ hover }') - v-card.radius-7.teal.animated.fadeInUp.wait-p1s( - hover - light - ripple - ) - //- v-card-text.text-center(@click='selectEditor("redirect")') - v-card-text.text-center(@click='') - img(src='/_assets/svg/icon-route.svg', alt='Redirection', style='width: 42px; opacity: .5;') - .body-2.mt-1.teal--text.text--lighten-2 Redirection - .caption.teal--text.text--lighten-1 Redirect the user to... + v-card.radius-7.animated.fadeInUp.wait-p4s( + hover + light + ripple + ) + v-card-text.text-center(@click='selectEditor("code")') + img(src='/_assets/svg/editor-icon-code.svg', alt='Code', style='width: 36px;') + .body-2.primary--text.mt-2 Code + .caption.grey--text Raw HTML v-flex(xs4) - v-hover - template(v-slot:default='{ hover }') - v-card.radius-7.teal.animated.fadeInUp.wait-p2s( - hover - light - ripple - ) - v-card-text.text-center(@click='') - img(src='/_assets/svg/icon-sewing-patch.svg', alt='Code', style='width: 42px; opacity: .5;') - .body-2.mt-1.teal--text.text--lighten-2 Embed - .caption.teal--text.text--lighten-1 Include external pages - v-fade-transition - v-overlay( - v-if='hover' - absolute - color='teal' - opacity='.8' - ) - .body-2.mt-7 Coming Soon - v-hover - template(v-slot:default='{ hover }') - v-card.radius-7.mt-2(color='indigo darken-3', dark) - v-toolbar(dense, flat, color='light-green darken-3') - v-spacer - .caption.mr-1 or convert from - v-btn.mx-1.animated.fadeInUp(depressed, color='light-green darken-2', @click='', disabled) - v-icon(left) mdi-alpha-a-circle - .body-2.text-none AsciiDoc - v-btn.mx-1.animated.fadeInUp.wait-p1s(depressed, color='light-green darken-2', @click='', disabled) - v-icon(left) mdi-alpha-c-circle - .body-2.text-none CREOLE - v-btn.mx-1.animated.fadeInUp.wait-p2s(depressed, color='light-green darken-2', @click='', disabled) - v-icon(left) mdi-alpha-t-circle - .body-2.text-none Textile - v-btn.mx-1.animated.fadeInUp.wait-p3s(depressed, color='light-green darken-2', @click='', disabled) - v-icon(left) mdi-alpha-w-circle - .body-2.text-none WikiText - v-spacer - v-fade-transition - v-overlay( - v-if='hover' - absolute - color='light-green darken-3' - opacity='.8' - ) - .body-2 Coming Soon + v-card.radius-7.animated.fadeInUp.wait-p5s( + hover + light + ripple + ) + v-card-text.text-center(@click='fromTemplate') + img(src='/_assets/svg/icon-cube.svg', alt='From Template', style='width: 42px; opacity: .5;') + .body-2.mt-1.teal--text From Template + .caption.grey--text Use an existing page... page-selector(mode='select', v-model='templateDialogIsShown', :open-handler='fromTemplateHandle', :path='path', :locale='locale', must-exist) diff --git a/client/static/svg/editor-icon-asciidoc.svg b/client/static/svg/editor-icon-asciidoc.svg new file mode 100644 index 00000000..d0c954b1 --- /dev/null +++ b/client/static/svg/editor-icon-asciidoc.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/themes/default/scss/app.scss b/client/themes/default/scss/app.scss index 8bba5e72..5e7f0a95 100644 --- a/client/themes/default/scss/app.scss +++ b/client/themes/default/scss/app.scss @@ -356,6 +356,148 @@ } } + // --------------------------------- + // ASCIIDOC SPECIFIC + // --------------------------------- + + .admonitionblock { + margin: 1rem 0; + position: relative; + + table { + border: none; + background-color: transparent; + width: 100%; + } + + td.icon { + border-bottom-left-radius: 7px; + border-top-left-radius: 7px; + text-align: center; + width: 56px; + + &::before { + display: inline-block; + font: normal normal normal 24px/1 "Material Design Icons", sans-serif !important; + } + } + + td.content { + border-bottom-right-radius: 7px; + border-top-right-radius: 7px; + } + + &.note { + td.icon { + background-color: mc('blue', '300'); + color: mc('blue', '50'); + &::before { + content: "\F02FC"; + } + } + td.content { + color: darken(mc('blue', '900'), 10%); + background-color: mc('blue', '50'); + + @at-root .theme--dark & { + background-color: mc('blue', '900'); + color: mc('blue', '50'); + } + } + } + &.tip { + td.icon { + background-color: mc('green', '300'); + color: mc('green', '50'); + &::before { + content: "\F0335"; + } + } + td.content { + color: darken(mc('green', '900'), 10%); + background-color: mc('green', '50'); + + @at-root .theme--dark & { + background-color: mc('green', '900'); + color: mc('green', '50'); + } + } + } + &.warning { + background-color: transparent !important; + + td.icon { + background-color: mc('orange', '300'); + color: #FFF; + &::before { + content: "\F0026"; + } + } + td.content { + color: darken(mc('orange', '900'), 10%); + background-color: mc('orange', '50'); + + @at-root .theme--dark & { + background-color: darken(mc('orange', '900'), 5%); + color: mc('orange', '100'); + } + } + } + &.caution { + td.icon { + background-color: mc('purple', '300'); + color: mc('purple', '50'); + &::before { + content: "\f0238"; + } + } + td.content { + color: darken(mc('purple', '900'), 10%); + background-color: mc('purple', '50'); + + @at-root .theme--dark & { + background-color: mc('purple', '900'); + color: mc('purple', '100'); + } + } + } + &.important { + td.icon { + background-color: mc('red', '300'); + color: mc('red', '50'); + &::before { + content: "\F0159"; + } + } + td.content { + color: darken(mc('red', '900'), 10%); + background-color: mc('red', '50'); + + @at-root .theme--dark & { + background-color: mc('red', '900'); + color: mc('red', '100'); + } + } + } + } + + .exampleblock { + > .title { + font-style: italic; + font-size: 1rem !important; + color: #7a2717; + + @at-root .theme--dark & { + color: mc('brown', '300'); + } + } + > .content { + border: 1px solid mc('grey', '200'); + border-radius: 7px; + margin-bottom: 12px; + padding: 16px; + } + } // --------------------------------- // LISTS // --------------------------------- @@ -547,6 +689,23 @@ } } + dl { + dt { + margin-top: 0.3em; + margin-bottom: 0.3em; + font-weight: bold; + } + + dd { + margin-left: 1.125em; + margin-bottom: 0.75em; + + > p { + padding: 0; + } + } + } + // --------------------------------- // CODE // --------------------------------- diff --git a/package.json b/package.json index d6edb3fb..32d4ecd6 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "node": ">=10.12" }, "dependencies": { + "asciidoctor": "2.2.6", "@azure/storage-blob": "12.12.0", "@exlinc/keycloak-passport": "1.0.2", "@joplin/turndown-plugin-gfm": "1.0.45", @@ -231,6 +232,7 @@ "clean-webpack-plugin": "3.0.0", "clipboard": "2.0.11", "codemirror": "5.58.2", + "codemirror-asciidoc": "1.0.4", "copy-webpack-plugin": "6.2.1", "core-js": "3.6.5", "css-loader": "4.3.0", diff --git a/server/helpers/page.js b/server/helpers/page.js index 8049edc1..8bea0aa3 100644 --- a/server/helpers/page.js +++ b/server/helpers/page.js @@ -10,6 +10,7 @@ const unsafeCharsRegex = /[\x00-\x1f\x80-\x9f\\"|<>:*?]/ const contentToExt = { markdown: 'md', + asciidoc: 'adoc', html: 'html' } const extToContent = _.invert(contentToExt) diff --git a/server/models/editors.js b/server/models/editors.js index 8a05ba98..f4d091af 100644 --- a/server/models/editors.js +++ b/server/models/editors.js @@ -101,6 +101,8 @@ module.exports = class Editor extends Model { return 'markdown' case 'html': return 'ckeditor' + case 'asciidoc': + return 'asciidoc' default: return 'code' } diff --git a/server/modules/editor/asciidoc/definition.yml b/server/modules/editor/asciidoc/definition.yml new file mode 100644 index 00000000..378b60da --- /dev/null +++ b/server/modules/editor/asciidoc/definition.yml @@ -0,0 +1,6 @@ +key: asciidoc +title: Asciidoc +description: Basic Asciidoc editor +contentType: asciidoc +author: dzruyk +props: {} diff --git a/server/modules/rendering/asciidoc-core/definition.yml b/server/modules/rendering/asciidoc-core/definition.yml new file mode 100644 index 00000000..8a11eb14 --- /dev/null +++ b/server/modules/rendering/asciidoc-core/definition.yml @@ -0,0 +1,20 @@ +key: asciidocCore +title: Core +description: Basic Asciidoc Parser +author: dzruyk (Based on asciidoctor.js renderer) +input: asciidoc +output: html +icon: mdi-sitemap +enabledDefault: true +props: + safeMode: + type: String + default: server + title: Safe Mode + hint: Sets the safe mode to use when parsing content to HTML. + order: 1 + enum: + - unsafe + - safe + - server + - secure diff --git a/server/modules/rendering/asciidoc-core/renderer.js b/server/modules/rendering/asciidoc-core/renderer.js new file mode 100644 index 00000000..e37217d8 --- /dev/null +++ b/server/modules/rendering/asciidoc-core/renderer.js @@ -0,0 +1,26 @@ +const asciidoctor = require('asciidoctor')() +const cheerio = require('cheerio') + +module.exports = { + async render() { + const html = asciidoctor.convert(this.input, { + standalone: false, + safe: this.config.safeMode, + attributes: { + showtitle: true, + icons: 'font' + } + }) + + const $ = cheerio.load(html, { + decodeEntities: true + }) + + $('pre.highlight > code.language-diagram').each((i, elm) => { + const diagramContent = Buffer.from($(elm).html(), 'base64').toString() + $(elm).parent().replaceWith(`
${diagramContent}`)
+    })
+
+    return $.html()
+  }
+}
diff --git a/yarn.lock b/yarn.lock
index 37a56058..92107b1c 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -177,6 +177,21 @@
   dependencies:
     tslib "~2.0.1"
 
+"@asciidoctor/cli@3.5.0":
+  version "3.5.0"
+  resolved "https://registry.yarnpkg.com/@asciidoctor/cli/-/cli-3.5.0.tgz#0b0a0204880b325971fb2af33bf490ab67672d8f"
+  integrity sha512-/VMHXcZBnZ9vgWfmqk9Hu0x0gMjPLup0YGq/xA8qCQuk11kUIZNMVQwgSsIUzOEwJqIUD7CgncJdtfwv1Ndxuw==
+  dependencies:
+    yargs "16.2.0"
+
+"@asciidoctor/core@2.2.6":
+  version "2.2.6"
+  resolved "https://registry.yarnpkg.com/@asciidoctor/core/-/core-2.2.6.tgz#a59a9e8ab48ac0a615d5a3200214d3071291c5d5"
+  integrity sha512-TmB2K5UfpDpSbCNBBntXzKHcAk2EA3/P68jmWvmJvglVUdkO9V6kTAuXVe12+h6C4GK0ndwuCrHHtEVcL5t6pQ==
+  dependencies:
+    asciidoctor-opal-runtime "0.3.3"
+    unxhr "1.0.1"
+
 "@azure/abort-controller@^1.0.0":
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/@azure/abort-controller/-/abort-controller-1.0.1.tgz#8510935b25ac051e58920300e9d7b511ca6e656a"
@@ -5168,6 +5183,22 @@ asap@^2.0.0, asap@~2.0.3:
   resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
   integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=
 
+asciidoctor-opal-runtime@0.3.3:
+  version "0.3.3"
+  resolved "https://registry.yarnpkg.com/asciidoctor-opal-runtime/-/asciidoctor-opal-runtime-0.3.3.tgz#2667635f858d3eb3fdfcf6795cf68138e2040174"
+  integrity sha512-/CEVNiOia8E5BMO9FLooo+Kv18K4+4JBFRJp8vUy/N5dMRAg+fRNV4HA+o6aoSC79jVU/aT5XvUpxSxSsTS8FQ==
+  dependencies:
+    glob "7.1.3"
+    unxhr "1.0.1"
+
+asciidoctor@2.2.6:
+  version "2.2.6"
+  resolved "https://registry.yarnpkg.com/asciidoctor/-/asciidoctor-2.2.6.tgz#43b5fec8ab91ed2d8d1815c75067cfa29da2e568"
+  integrity sha512-EXG3+F2pO21B+COfQmV/WgEgGiy7nG/mJiS/o5DXpaT2q82FRZWPVkbMZrpDvpu4pjXe5c754RbZR9Vz0L0Vtw==
+  dependencies:
+    "@asciidoctor/cli" "3.5.0"
+    "@asciidoctor/core" "2.2.6"
+
 asn1.js@^4.0.0:
   version "4.10.1"
   resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0"
@@ -6681,6 +6712,11 @@ code-point-at@^1.0.0:
   resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
   integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=
 
+codemirror-asciidoc@1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/codemirror-asciidoc/-/codemirror-asciidoc-1.0.4.tgz#7439fcc38f30f1408ab144d156bf398ed35b2cc0"
+  integrity sha512-U+G8+ToPONYFGkwTprxpFzV6EV1bCA9zChAA8uT2YAnKFn357JMWL2VkdIPy2yP5N/X13GzslMOGaAk1UNE3rA==
+
 codemirror@5.58.2:
   version "5.58.2"
   resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.58.2.tgz#ed54a1796de1498688bea1cdd4e9eeb187565d1b"
@@ -10252,6 +10288,18 @@ glob-parent@~5.1.2:
   dependencies:
     is-glob "^4.0.1"
 
+glob@7.1.3:
+  version "7.1.3"
+  resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1"
+  integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==
+  dependencies:
+    fs.realpath "^1.0.0"
+    inflight "^1.0.4"
+    inherits "2"
+    minimatch "^3.0.4"
+    once "^1.3.0"
+    path-is-absolute "^1.0.0"
+
 glob@^6.0.1:
   version "6.0.4"
   resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22"
@@ -19425,6 +19473,11 @@ untildify@^4.0.0:
   resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b"
   integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==
 
+unxhr@1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/unxhr/-/unxhr-1.0.1.tgz#92200322d66c728993de771f9e01eeb21f41bc7b"
+  integrity sha512-MAhukhVHyaLGDjyDYhy8gVjWJyhTECCdNsLwlMoGFoNJ3o79fpQhtQuzmAE4IxCMDwraF4cW8ZjpAV0m9CRQbg==
+
 upath@^1.1.1:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894"
@@ -20445,6 +20498,11 @@ y18n@^5.0.2:
   resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.5.tgz#8769ec08d03b1ea2df2500acef561743bbb9ab18"
   integrity sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg==
 
+y18n@^5.0.5:
+  version "5.0.8"
+  resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
+  integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==
+
 yaeti@^0.0.6:
   version "0.0.6"
   resolved "https://registry.yarnpkg.com/yaeti/-/yaeti-0.0.6.tgz#f26f484d72684cf42bedfb76970aa1608fbf9577"
@@ -20514,6 +20572,19 @@ yargs@16.1.0:
     y18n "^5.0.2"
     yargs-parser "^20.2.2"
 
+yargs@16.2.0:
+  version "16.2.0"
+  resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66"
+  integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==
+  dependencies:
+    cliui "^7.0.2"
+    escalade "^3.1.1"
+    get-caller-file "^2.0.5"
+    require-directory "^2.1.1"
+    string-width "^4.2.0"
+    y18n "^5.0.5"
+    yargs-parser "^20.2.2"
+
 yargs@^13.3.2:
   version "13.3.2"
   resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd"