enketo-transformer
Version:
Library that transforms ODK-compliant XForms into a format that Enketo can consume
1 lines • 7.08 kB
Source Map (JSON)
{"version":3,"file":"markdown.cjs","sources":["../../src/markdown.ts"],"sourcesContent":["/**\n * @package\n *\n * Transforms XForm label and hint textnode content with a subset of Markdown into HTML\n *\n * Supported:\n * - `_`, `__`, `*`, `**`, `[]()`, `#`, `##`, `###`, `####`, `#####`,\n * - span tags and html-encoded span tags,\n * - single-level unordered markdown lists and single-level ordered markdown lists\n * - newline characters\n *\n * Also HTML encodes any unsupported HTML tags for safe use inside web-based clients\n */\nexport const markdownToHTML = (text: string): string => {\n // note: in JS $ matches end of line as well as end of string, and ^ both beginning of line and string\n const html = text\n // html encoding of < because libXMLJs Element.text() converts html entities\n .replace(/</gm, '<')\n // html encoding of < because libXMLJs Element.text() converts html entities\n .replace(/>/gm, '>')\n // span\n .replace(\n /<\\s?span([^/\\n]*)>((?:(?!<\\/).)+)<\\/\\s?span\\s?>/gm,\n createSpan\n )\n // sup\n .replace(\n /<\\s?sup(?:[^/\\n]*)>((?:(?!<\\/).)+)<\\/\\s?sup\\s?>/gm,\n createSup\n )\n // sub\n .replace(\n /<\\s?sub(?:[^/\\n]*)>((?:(?!<\\/).)+)<\\/\\s?sub\\s?>/gm,\n createSub\n )\n // \"\\\" will be used as escape character for *, _\n .replace(/&/gm, '&')\n .replace(/\\\\\\\\/gm, '&92;')\n .replace(/\\\\\\*/gm, '&42;')\n .replace(/\\\\_/gm, '&95;')\n .replace(/\\\\#/gm, '&35;')\n // strong\n .replace(/__(.*?)__/gm, '<strong>$1</strong>')\n .replace(/\\*\\*(.*?)\\*\\*/gm, '<strong>$1</strong>')\n // emphasis\n .replace(/(^|\\W)_([^\\s][^_\\n]*)_(\\W|$)/gm, '$1<em>$2</em>$3')\n .replace(/\\*([^\\s][^*\\n]*)\\*/gm, '<em>$1</em>')\n // links\n .replace(/\\[([^\\]]*)\\]\\(([^)]+)\\)/gm, createAnchor)\n // headers\n .replace(/^\\s*(#{1,6})\\s?([^#][^\\n]*)(\\n|$)/gm, createHeader)\n // unordered lists\n .replace(/^((\\*|\\+|-) (.*)(\\n|$))+/gm, createUnorderedList)\n // ordered lists, which have to be preceded by a newline since numbered labels are common\n .replace(/(\\n([0-9]+\\.) (.*))+$/gm, createOrderedList)\n // newline characters followed by <ul> tag\n .replace(/\\n(<ul>)/gm, '$1')\n // reverting escape of special characters\n .replace(/&35;/gm, '#')\n .replace(/&95;/gm, '_')\n .replace(/&92;/gm, '\\\\')\n .replace(/&42;/gm, '*')\n .replace(/&/gm, '&')\n // paragraphs\n .replace(/([^\\n]+)\\n{2,}/gm, createParagraph)\n // any remaining newline characters\n .replace(/([^\\n]+)\\n/gm, '$1<br>');\n\n return html;\n};\n\ntype Matches = [string, ...string[]];\n\ntype Replacer<M extends Matches> = (...args: M) => string;\n\n/* eslint-disable -- the default formatting of this was unreadable */\nconst ignoreMatch = <M extends Matches>(fn: Replacer<M>) => {\n return (_match: string, ...args: M) => fn(...args);\n};\n/* eslint-enable */\n\nconst createAnchor = ignoreMatch(\n (label: string, href: string) =>\n `<a href=\"${encodeURI(\n href\n )}\" rel=\"noopener\" target=\"_blank\">${label}</a>`\n);\n\n/**\n * @param hashtags - Before header text. `#` gives `<h1>`, `####` gives `<h4>`.\n * @param content - Header text.\n */\nconst createHeader = ignoreMatch((hashtags: string, content: string) => {\n const level = hashtags.length;\n\n return `<h${level}>${content.replace(/#+$/, '')}</h${level}>`;\n});\n\nconst createUnorderedList = (match: string) => {\n const items = match.replace(/(?:\\*|\\+|-)(.*)\\n?/gm, createItem);\n\n return `<ul>${items}</ul>`;\n};\n\nconst createOrderedList = (match: string) => {\n const startMatches = match.match(/^\\n?(?<start>[0-9]+)\\./);\n const start =\n startMatches && startMatches.groups && startMatches.groups.start !== '1'\n ? ` start=\"${startMatches.groups.start}\"`\n : '';\n const items = match.replace(/\\n?(?:[0-9]+\\.)(.*)/gm, createItem);\n\n return `<ol${start}>${items}</ol>`;\n};\n\nconst createItem = ignoreMatch(\n (content: string) => `<li>${content.trim()}</li>`\n);\n\nconst createParagraph = ignoreMatch((line: string) => {\n const trimmed = line.trim();\n if (/^<\\/?(ul|ol|li|h|p)/i.test(trimmed)) {\n return line;\n }\n\n return `<p>${trimmed}</p>`;\n});\n\nconst createSpan = ignoreMatch((attributes: string, content: string) => {\n const sanitizedAttributes = sanitizeAttributes(attributes);\n\n return `<span${sanitizedAttributes}>${content}</span>`;\n});\n\nconst createSup = ignoreMatch((content: string) => `<sup>${content}</sup>`);\n\nconst createSub = ignoreMatch((content: string) => `<sub>${content}</sub>`);\n\nconst sanitizeAttributes = (attributes: string) => {\n const styleMatches = attributes.match(/( style=([\"'])[^\"']*\\2)/);\n const style = styleMatches && styleMatches.length ? styleMatches[0] : '';\n\n return style;\n};\n"],"names":[],"mappings":";;AAaa,MAAA,iBAAiB,CAAC,SAAyB;AAE9C,QAAA,OAAO,KAER,QAAQ,OAAO,MAAM,EAErB,QAAQ,OAAO,MAAM,EAErB;AAAA,IACG;AAAA,IACA;AAAA,EAAA,EAGH;AAAA,IACG;AAAA,IACA;AAAA,EAAA,EAGH;AAAA,IACG;AAAA,IACA;AAAA,EAAA,EAGH,QAAQ,OAAO,OAAO,EACtB,QAAQ,UAAU,MAAM,EACxB,QAAQ,UAAU,MAAM,EACxB,QAAQ,SAAS,MAAM,EACvB,QAAQ,SAAS,MAAM,EAEvB,QAAQ,eAAe,qBAAqB,EAC5C,QAAQ,mBAAmB,qBAAqB,EAEhD,QAAQ,kCAAkC,iBAAiB,EAC3D,QAAQ,wBAAwB,aAAa,EAE7C,QAAQ,6BAA6B,YAAY,EAEjD,QAAQ,uCAAuC,YAAY,EAE3D,QAAQ,8BAA8B,mBAAmB,EAEzD,QAAQ,2BAA2B,iBAAiB,EAEpD,QAAQ,cAAc,IAAI,EAE1B,QAAQ,UAAU,GAAG,EACrB,QAAQ,UAAU,GAAG,EACrB,QAAQ,UAAU,IAAI,EACtB,QAAQ,UAAU,GAAG,EACrB,QAAQ,WAAW,GAAG,EAEtB,QAAQ,oBAAoB,eAAe,EAE3C,QAAQ,gBAAgB,QAAQ;AAE9B,SAAA;AACX;AAOA,MAAM,cAAc,CAAoB,OAAoB;AACxD,SAAO,CAAC,WAAmB,SAAY,GAAG,GAAG,IAAI;AACrD;AAGA,MAAM,eAAe;AAAA,EACjB,CAAC,OAAe,SACZ,YAAY;AAAA,IACR;AAAA,EAAA,CACH,oCAAoC,KAAK;AAClD;AAMA,MAAM,eAAe,YAAY,CAAC,UAAkB,YAAoB;AACpE,QAAM,QAAQ,SAAS;AAEhB,SAAA,KAAK,KAAK,IAAI,QAAQ,QAAQ,OAAO,EAAE,CAAC,MAAM,KAAK;AAC9D,CAAC;AAED,MAAM,sBAAsB,CAAC,UAAkB;AAC3C,QAAM,QAAQ,MAAM,QAAQ,wBAAwB,UAAU;AAE9D,SAAO,OAAO,KAAK;AACvB;AAEA,MAAM,oBAAoB,CAAC,UAAkB;AACnC,QAAA,eAAe,MAAM,MAAM,wBAAwB;AACzD,QAAM,QACF,gBAAgB,aAAa,UAAU,aAAa,OAAO,UAAU,MAC/D,WAAW,aAAa,OAAO,KAAK,MACpC;AACV,QAAM,QAAQ,MAAM,QAAQ,yBAAyB,UAAU;AAExD,SAAA,MAAM,KAAK,IAAI,KAAK;AAC/B;AAEA,MAAM,aAAa;AAAA,EACf,CAAC,YAAoB,OAAO,QAAQ,MAAM;AAC9C;AAEA,MAAM,kBAAkB,YAAY,CAAC,SAAiB;AAC5C,QAAA,UAAU,KAAK;AACjB,MAAA,uBAAuB,KAAK,OAAO,GAAG;AAC/B,WAAA;AAAA,EACX;AAEA,SAAO,MAAM,OAAO;AACxB,CAAC;AAED,MAAM,aAAa,YAAY,CAAC,YAAoB,YAAoB;AAC9D,QAAA,sBAAsB,mBAAmB,UAAU;AAElD,SAAA,QAAQ,mBAAmB,IAAI,OAAO;AACjD,CAAC;AAED,MAAM,YAAY,YAAY,CAAC,YAAoB,QAAQ,OAAO,QAAQ;AAE1E,MAAM,YAAY,YAAY,CAAC,YAAoB,QAAQ,OAAO,QAAQ;AAE1E,MAAM,qBAAqB,CAAC,eAAuB;AACzC,QAAA,eAAe,WAAW,MAAM,yBAAyB;AAC/D,QAAM,QAAQ,gBAAgB,aAAa,SAAS,aAAa,CAAC,IAAI;AAE/D,SAAA;AACX;;"}