diff options
author | Eugene Sokolov <eug-vs@keemail.me> | 2021-01-11 22:15:33 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-01-11 22:15:33 +0200 |
commit | 5f72950c56ea8853b41b64a092a2e98dbf53cf3f (patch) | |
tree | 240101bb69aea59c72e1e6fa328b9c5e1729f367 | |
parent | bc288a43c90ab830019bf077d72081fade76c06e (diff) | |
parent | aa4493b08baae2d41a4c4346b9a5c63b08df471e (diff) | |
download | react-benzin-5f72950c56ea8853b41b64a092a2e98dbf53cf3f.tar.gz |
Merge pull request #20 from eug-vs/feat/react-markdown
Feat: use react-markdown library and support context
-rw-r--r-- | package-lock.json | 336 | ||||
-rw-r--r-- | package.json | 2 | ||||
-rw-r--r-- | src/demo/content.md | 13 | ||||
-rw-r--r-- | src/index.tsx | 125 | ||||
-rw-r--r-- | src/lib/Markdown/CodeBlock.tsx | 13 | ||||
-rw-r--r-- | src/lib/Markdown/Content.tsx | 90 | ||||
-rw-r--r-- | src/lib/Markdown/Heading.tsx | 33 | ||||
-rw-r--r-- | src/lib/Markdown/Image.tsx | 12 | ||||
-rw-r--r-- | src/lib/Markdown/InlineCode.tsx | 18 | ||||
-rw-r--r-- | src/lib/Markdown/Markdown.tsx | 45 | ||||
-rw-r--r-- | src/lib/Markdown/Section.tsx | 61 | ||||
-rw-r--r-- | src/lib/Markdown/SyntacticSpan.tsx | 96 | ||||
-rw-r--r-- | src/lib/Markdown/Text.tsx | 13 | ||||
-rw-r--r-- | src/lib/Markdown/emojilib.d.ts | 2 | ||||
-rw-r--r-- | src/lib/Markdown/types.d.ts | 7 | ||||
-rw-r--r-- | src/lib/Markdown/types.ts | 4 | ||||
-rw-r--r-- | src/lib/index.ts | 4 |
17 files changed, 520 insertions, 354 deletions
diff --git a/package-lock.json b/package-lock.json index a147a70..a5dcc22 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2374,6 +2374,14 @@ "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", "dev": true }, + "@types/mdast": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.3.tgz", + "integrity": "sha512-SXPBMnFVQg1s00dlMCc/jCdvPqdE4mXaMMCeRlxLDmTAEoegHT53xKtkDnzDTOcmMHUfcjyf36/YYZ6SxRdnsw==", + "requires": { + "@types/unist": "*" + } + }, "@types/minimatch": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", @@ -2492,6 +2500,11 @@ } } }, + "@types/unist": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.3.tgz", + "integrity": "sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ==" + }, "@types/webpack": { "version": "4.41.25", "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.25.tgz", @@ -3605,6 +3618,11 @@ "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", "dev": true }, + "bail": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.5.tgz", + "integrity": "sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==" + }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -4191,6 +4209,21 @@ "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", "dev": true }, + "character-entities": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz", + "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==" + }, + "character-entities-legacy": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", + "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==" + }, + "character-reference-invalid": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz", + "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==" + }, "chardet": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", @@ -5215,7 +5248,6 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", - "dev": true, "requires": { "ms": "2.1.2" } @@ -5850,8 +5882,7 @@ "entities": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", - "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", - "dev": true + "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==" }, "errno": { "version": "0.1.7", @@ -6844,8 +6875,7 @@ "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" }, "extend-shallow": { "version": "3.0.2", @@ -7608,6 +7638,11 @@ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, + "gemoji": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/gemoji/-/gemoji-6.1.0.tgz", + "integrity": "sha512-MOlX3doQ1fsfzxQX8Y+u6bC5Ssc1pBUBIPVyrS69EzKt+5LIZAOm0G5XGVNhwXFgkBF3r+Yk88ONyrFHo8iNFA==" + }, "gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -8093,6 +8128,83 @@ } } }, + "html-to-react": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/html-to-react/-/html-to-react-1.4.5.tgz", + "integrity": "sha512-KONZUDFPg5OodWaQu2ymfkDmU0JA7zB1iPfvyHehTmMUZnk0DS7/TyCMTzsLH6b4BvxX15g88qZCXFhJWktsmA==", + "requires": { + "domhandler": "^3.3.0", + "htmlparser2": "^5.0", + "lodash.camelcase": "^4.3.0", + "ramda": "^0.27.1" + }, + "dependencies": { + "dom-serializer": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.2.0.tgz", + "integrity": "sha512-n6kZFH/KlCrqs/1GHMOd5i2fd/beQHuehKdWvNNffbGHTr/almdhuVvTVFb3V7fglz+nC50fFusu3lY33h12pA==", + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "entities": "^2.0.0" + }, + "dependencies": { + "domhandler": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.0.0.tgz", + "integrity": "sha512-KPTbnGQ1JeEMQyO1iYXoagsI6so/C96HZiFyByU3T6iAzpXn8EGEvct6unm1ZGoed8ByO2oirxgwxBmqKF9haA==", + "requires": { + "domelementtype": "^2.1.0" + } + } + } + }, + "domelementtype": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.1.0.tgz", + "integrity": "sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w==" + }, + "domhandler": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-3.3.0.tgz", + "integrity": "sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA==", + "requires": { + "domelementtype": "^2.0.1" + } + }, + "domutils": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.4.4.tgz", + "integrity": "sha512-jBC0vOsECI4OMdD0GC9mGn7NXPLb+Qt6KW1YDQzeQYRUFKmNG8lh7mO5HiELfr+lLQE7loDVI4QcAxV80HS+RA==", + "requires": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0" + }, + "dependencies": { + "domhandler": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.0.0.tgz", + "integrity": "sha512-KPTbnGQ1JeEMQyO1iYXoagsI6so/C96HZiFyByU3T6iAzpXn8EGEvct6unm1ZGoed8ByO2oirxgwxBmqKF9haA==", + "requires": { + "domelementtype": "^2.1.0" + } + } + } + }, + "htmlparser2": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-5.0.1.tgz", + "integrity": "sha512-vKZZra6CSe9qsJzh0BjBGXo8dvzNsq/oGvsjfRdOrrryfeD9UOBEEQdeoqCRmKZchF5h2zOBMQ6YuQ0uRUmdbQ==", + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^3.3.0", + "domutils": "^2.4.2", + "entities": "^2.0.0" + } + } + } + }, "html-webpack-plugin": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-4.5.0.tgz", @@ -8657,6 +8769,20 @@ "kind-of": "^3.0.2" } }, + "is-alphabetical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", + "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==" + }, + "is-alphanumerical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", + "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", + "requires": { + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0" + } + }, "is-arguments": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", @@ -8738,6 +8864,11 @@ "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", "dev": true }, + "is-decimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", + "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==" + }, "is-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", @@ -8802,6 +8933,11 @@ "is-extglob": "^2.1.1" } }, + "is-hexadecimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz", + "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==" + }, "is-in-browser": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz", @@ -10517,6 +10653,11 @@ "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", "dev": true }, + "lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=" + }, "lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -10647,6 +10788,31 @@ "safe-buffer": "^5.1.2" } }, + "mdast-add-list-metadata": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdast-add-list-metadata/-/mdast-add-list-metadata-1.0.1.tgz", + "integrity": "sha512-fB/VP4MJ0LaRsog7hGPxgOrSL3gE/2uEdZyDuSEnKCv/8IkYHiDkIQSbChiJoHyxZZXZ9bzckyRk+vNxFzh8rA==", + "requires": { + "unist-util-visit-parents": "1.1.2" + } + }, + "mdast-util-from-markdown": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-0.8.4.tgz", + "integrity": "sha512-jj891B5pV2r63n2kBTFh8cRI2uR9LQHsXG1zSDqfhXkIlDzrTcIlbB5+5aaYEkl8vOPIOPLf8VT7Ere1wWTMdw==", + "requires": { + "@types/mdast": "^3.0.0", + "mdast-util-to-string": "^2.0.0", + "micromark": "~2.11.0", + "parse-entities": "^2.0.0", + "unist-util-stringify-position": "^2.0.0" + } + }, + "mdast-util-to-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz", + "integrity": "sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==" + }, "mdn-data": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", @@ -10736,6 +10902,15 @@ "integrity": "sha512-jo1OfR4TaEwd5HOrt5+tAZ9mqT4jmpNAusXtyfNzqVm9uiSYFZlKM1wYL4oU7azZW/PxQW53wM0S6OR1JHNa2g==", "dev": true }, + "micromark": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-2.11.2.tgz", + "integrity": "sha512-IXuP76p2uj8uMg4FQc1cRE7lPCLsfAXuEfdjtdO55VRiFO1asrCSQ5g43NmPqFtRwzEnEhafRVzn2jg0UiKArQ==", + "requires": { + "debug": "^4.0.0", + "parse-entities": "^2.0.0" + } + }, "micromatch": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", @@ -11667,6 +11842,19 @@ "safe-buffer": "^5.1.1" } }, + "parse-entities": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", + "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", + "requires": { + "character-entities": "^1.0.0", + "character-entities-legacy": "^1.0.0", + "character-reference-invalid": "^1.0.0", + "is-alphanumerical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-hexadecimal": "^1.0.0" + } + }, "parse-json": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", @@ -13294,6 +13482,11 @@ "performance-now": "^2.1.0" } }, + "ramda": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.1.tgz", + "integrity": "sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw==" + }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -13503,6 +13696,23 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "react-markdown": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-5.0.3.tgz", + "integrity": "sha512-jDWOc1AvWn0WahpjW6NK64mtx6cwjM4iSsLHJPNBqoAgGOVoIdJMqaKX4++plhOtdd4JksdqzlDibgPx6B/M2w==", + "requires": { + "@types/mdast": "^3.0.3", + "@types/unist": "^2.0.3", + "html-to-react": "^1.3.4", + "mdast-add-list-metadata": "1.0.1", + "prop-types": "^15.7.2", + "react-is": "^16.8.6", + "remark-parse": "^9.0.0", + "unified": "^9.0.0", + "unist-util-visit": "^2.0.0", + "xtend": "^4.0.1" + } + }, "react-refresh": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.8.3.tgz", @@ -13800,6 +14010,23 @@ "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=", "dev": true }, + "remark-gemoji": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/remark-gemoji/-/remark-gemoji-6.0.0.tgz", + "integrity": "sha512-LDW2h6QqNzAbAcOjscgfkJW9/8TGBasBe/ji+3mCxHlJdhF2IEXFSmm/3tdEPP1JJDZ4y+Ea+xlFQ4tOIU9WvA==", + "requires": { + "gemoji": "^6.0.0", + "unist-util-visit": "^2.0.0" + } + }, + "remark-parse": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-9.0.0.tgz", + "integrity": "sha512-geKatMwSzEXKHuzBNU1z676sGcDcFoChMK38TgdHJNAYfFtsfHDQG7MoJAjs6sgYMqyLduCYWDIWZIxiPeafEw==", + "requires": { + "mdast-util-from-markdown": "^0.8.0" + } + }, "remove-trailing-separator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", @@ -16071,6 +16298,11 @@ "escape-string-regexp": "^1.0.2" } }, + "trough": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.5.tgz", + "integrity": "sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==" + }, "tryer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", @@ -16222,6 +16454,31 @@ "integrity": "sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==", "dev": true }, + "unified": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/unified/-/unified-9.2.0.tgz", + "integrity": "sha512-vx2Z0vY+a3YoTj8+pttM3tiJHCwY5UFbYdiWrwBEbHmK8pvsPj2rtAX2BFfgXen8T39CJWblWRDT4L5WGXtDdg==", + "requires": { + "bail": "^1.0.0", + "extend": "^3.0.0", + "is-buffer": "^2.0.0", + "is-plain-obj": "^2.0.0", + "trough": "^1.0.0", + "vfile": "^4.0.0" + }, + "dependencies": { + "is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==" + }, + "is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==" + } + } + }, "union-value": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", @@ -16273,6 +16530,45 @@ "crypto-random-string": "^1.0.0" } }, + "unist-util-is": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.0.4.tgz", + "integrity": "sha512-3dF39j/u423v4BBQrk1AQ2Ve1FxY5W3JKwXxVFzBODQ6WEvccguhgp802qQLKSnxPODE6WuRZtV+ohlUg4meBA==" + }, + "unist-util-stringify-position": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz", + "integrity": "sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==", + "requires": { + "@types/unist": "^2.0.2" + } + }, + "unist-util-visit": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-2.0.3.tgz", + "integrity": "sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==", + "requires": { + "@types/unist": "^2.0.0", + "unist-util-is": "^4.0.0", + "unist-util-visit-parents": "^3.0.0" + }, + "dependencies": { + "unist-util-visit-parents": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz", + "integrity": "sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg==", + "requires": { + "@types/unist": "^2.0.0", + "unist-util-is": "^4.0.0" + } + } + } + }, + "unist-util-visit-parents": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-1.1.2.tgz", + "integrity": "sha512-yvo+MMLjEwdc3RhhPYSximset7rwjMrdt9E41Smmvg25UQIenzrN83cRnF1JMzoMi9zZOQeYXHSDf7p+IQkW3Q==" + }, "universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", @@ -16522,6 +16818,33 @@ "extsprintf": "^1.2.0" } }, + "vfile": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-4.2.1.tgz", + "integrity": "sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA==", + "requires": { + "@types/unist": "^2.0.0", + "is-buffer": "^2.0.0", + "unist-util-stringify-position": "^2.0.0", + "vfile-message": "^2.0.0" + }, + "dependencies": { + "is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==" + } + } + }, + "vfile-message": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.4.tgz", + "integrity": "sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==", + "requires": { + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^2.0.0" + } + }, "vm-browserify": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", @@ -18164,8 +18487,7 @@ "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" }, "y18n": { "version": "4.0.0", diff --git a/package.json b/package.json index 05fa52e..3592ae8 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,8 @@ "emojilib": "^2.4.0", "react": "^17.0.1", "react-dom": "^17.0.1", + "react-markdown": "^5.0.3", + "remark-gemoji": "^6.0.0", "typeface-roboto": "1.1.13" }, "devDependencies": { diff --git a/src/demo/content.md b/src/demo/content.md new file mode 100644 index 0000000..41607a9 --- /dev/null +++ b/src/demo/content.md @@ -0,0 +1,13 @@ +## Markdown +[Markdown file](${url}) *(...`$fileName`)* that you can see on the left was parsed and rendered by **BENZIN**! :rocket: +Switch between tabs on the header to explore other markdown templates. :recycle: +Templates on the left are being loaded from the [GitHub](https://github.com), though this pane is generated from plaintext. :pen: +## How do I use this feature? +``` +import Markdown from \'react-benzin\'; +const data = \'# Header\\nHello, *world!*\'; +ReactDOM.render(<Markdown data={data}/>, document.getElementById(\'root\')); +``` + +`$tryButton` + diff --git a/src/index.tsx b/src/index.tsx index 2ebe975..aa786ac 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -9,11 +9,12 @@ import { import { Benzin, Markdown, - ContentSection, + Heading, } from './lib'; import Header from './demo/Header/Header'; import Window from './demo/Window/Window'; +import content from './demo/content.md'; import icon from './assets/icon.svg'; const useStyles = makeStyles(theme => ({ @@ -55,24 +56,23 @@ const CustomPage: React.FC = () => { return ( <> - <ContentSection sectionName="Render custom markdown document" level={2}> - <p> - This should be a link to a valid markdown file. Response should give the file contents. - If you copy README file from GitHub, make sure you provide link to raw view. - </p> - <p> - <TextField - fullWidth - inputRef={inputEl} - variant="outlined" - color="secondary" - label="Markdown url" - /> - </p> - <Button variant="contained" color="secondary" onClick={handleParseUrl}> - Render! - </Button> - </ContentSection> + <Heading level={2}>Render custom markdown document</Heading> + <p> + This should be a link to a valid markdown file. Response should give the file contents. + If you copy README file from GitHub, make sure you provide link to raw view. + </p> + <p> + <TextField + fullWidth + inputRef={inputEl} + variant="outlined" + color="secondary" + label="Markdown url" + /> + </p> + <Button variant="contained" color="secondary" onClick={handleParseUrl}> + Render! + </Button> <Markdown url={url} /> </> ); @@ -91,29 +91,28 @@ const LivePreviewPage: React.FC<LivePropTypes> = ({ setLivePreviewData }) => { return ( <> - <ContentSection sectionName="Markdown live preview" level={2}> - <p> - Start typing and see your text rendered on the left window! - You can find the list of all Markdown features - {' '} - <Link href="https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet"> - here - </Link> - . (some of them are yet in progress). - We recommend starting with # Header. - </p> - <p> - <TextField - fullWidth - multiline - inputRef={inputEl} - variant="outlined" - color="primary" - label="Markdown" - onChange={handleRender} - /> - </p> - </ContentSection> + <Heading level={2}>Markdown live preview</Heading> + <p> + Start typing and see your text rendered on the left window! + You can find the list of all Markdown features + {' '} + <Link href="https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet"> + here + </Link> + . (some of them are yet in progress). + We recommend starting with # Header. + </p> + <p> + <TextField + fullWidth + multiline + inputRef={inputEl} + variant="outlined" + color="primary" + label="Markdown" + onChange={handleRender} + /> + </p> </> ); }; @@ -130,19 +129,6 @@ const App: React.FC = () => { const url = pageMap[page]; const fileName = url?.slice(url.lastIndexOf('/') + 1); - const info = [ - /* eslint-disable max-len */ - `## Markdown\n [Markdown file](${url}) *(...${fileName})* that you can see on the left was parsed and rendered by **BENZIN**! :rocket:`, - 'Switch between tabs on the header to explore other markdown templates. :recycle: ', - 'Templates on the left are being loaded from the [GitHub](https://github.com), though this pane is generated from plaintext. :pen:', - '## How do I use this feature?', - '```', - 'import Markdown from \'react-benzin\';', - 'const data = \'# Header\\nHello, *world!*\';', - 'ReactDOM.render(<Markdown data={data}/>, document.getElementById(\'root\'));', - '```', - /* eslint-enable max-len */ - ].join('\n'); let primaryWindowContent = <Markdown url={url} />; if (page === 'custom') primaryWindowContent = <CustomPage />; @@ -150,6 +136,19 @@ const App: React.FC = () => { primaryWindowContent = <Markdown data={livePreviewData || '# Start typing in the right window!'} />; } + const tryButton = ( + <p className={classes.promoButton}> + <Button + variant="contained" + color="primary" + size="large" + onClick={handleGoLivePreview} + > + Try it yourself! + </Button> + </p> + ); + return ( <Benzin> <Header @@ -169,21 +168,7 @@ const App: React.FC = () => { { (page === 'live preview') ? <LivePreviewPage setLivePreviewData={setLivePreviewData} /> - : ( - <> - <Markdown data={info} /> - <p className={classes.promoButton}> - <Button - variant="contained" - color="primary" - size="large" - onClick={handleGoLivePreview} - > - Try it yourself! - </Button> - </p> - </> - ) + : <Markdown url={content} context={{ tryButton, fileName }} /> } </div> </Window> diff --git a/src/lib/Markdown/CodeBlock.tsx b/src/lib/Markdown/CodeBlock.tsx index 394458e..7431881 100644 --- a/src/lib/Markdown/CodeBlock.tsx +++ b/src/lib/Markdown/CodeBlock.tsx @@ -1,8 +1,9 @@ import React from 'react'; -import { Paper } from '@material-ui/core'; +import { Paper, makeStyles } from '@material-ui/core'; -import { makeStyles } from '@material-ui/core/styles'; -import { ParserPropTypes } from './types'; +interface PropTypes { + value: string; +} const useStyles = makeStyles(theme => ({ root: { @@ -14,11 +15,13 @@ const useStyles = makeStyles(theme => ({ }, })); -const CodeBlock: React.FC<ParserPropTypes> = ({ rawLines }) => { +const CodeBlock: React.FC<PropTypes> = ({ value }) => { const classes = useStyles(); return ( <Paper variant="outlined" className={classes.root}> - {rawLines.map(line => <pre>{line}</pre>)} + <pre> + {value} + </pre> </Paper> ); }; diff --git a/src/lib/Markdown/Content.tsx b/src/lib/Markdown/Content.tsx deleted file mode 100644 index 88409fa..0000000 --- a/src/lib/Markdown/Content.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import React from 'react'; - -import CodeBlock from './CodeBlock'; -import Text from './Text'; -import { ParserPropTypes } from './types'; - - -const denotesCodeBlock = (line: string): boolean => { - return line.match(/^\s*```.*$/) !== null; -}; - -const denotesDottedList = (line: string): boolean => { - return line.match(/^ ?[-*] .*$/) !== null; -}; - -const denotesOpenHtml = (line: string): string => { - const regex = /<([^/\s]*)[^<]*[^/]>/g; - const match = regex.exec(line); - return match ? match[1] : ''; -}; - -const denotesClosingHtml = (line: string, tag: string): boolean => { - const regex = new RegExp(`</${tag}[^<]*>`); - return line.match(regex) !== null; -}; - -const denotesSelfClosingHtml = (line: string): string[] | null => { - const regex = /(<[^/\s]*[^<]*\/>)/g; - return line.match(regex); -}; - -const declaresNoLineBreak = (line: string): boolean => { - return line.match(/\\\|$/) !== null; -}; - -const Content: React.FC<ParserPropTypes> = ({ rawLines }) => { - if (!rawLines.length) return null; - - const line = rawLines.splice(0, 1)[0]; - - let buffer; - if (denotesCodeBlock(line)) { - const closeIndex = rawLines.findIndex(rawLine => denotesCodeBlock(rawLine)); - const codeBlockLines = rawLines.splice(0, closeIndex + 1).slice(0, closeIndex); - buffer = <CodeBlock rawLines={codeBlockLines} />; - } else if (denotesDottedList(line)) { - const closeIndex = rawLines.findIndex(rawLine => !denotesDottedList(rawLine)); - const dottedListLines = rawLines.splice(0, closeIndex).slice(0, closeIndex); - dottedListLines.unshift(line); - buffer = <ul>{dottedListLines.map(li => <li><Text line={li.slice(2)} /></li>)}</ul>; - } else if ((buffer = denotesOpenHtml(line))) { - const tag = buffer; - const closeIndex = denotesClosingHtml(line, tag) ? -1 : rawLines.findIndex( - rawLine => denotesClosingHtml(rawLine, tag), - ); - const htmlLines = rawLines.splice(0, closeIndex + 1); - htmlLines.unshift(line); - buffer = <div dangerouslySetInnerHTML={{ __html: htmlLines.join('\n') }} />; - } else if ((buffer = denotesSelfClosingHtml(line)) !== null) { - const match = buffer[0]; - const [before, after] = line.split(match); - buffer = ( - <> - <Text line={before} /> - <div dangerouslySetInnerHTML={{ __html: match }} /> - <Text line={after} /> - </> - ); - } else if (declaresNoLineBreak(line)) { - const closeIndex = rawLines.findIndex(rawLine => !declaresNoLineBreak(rawLine)); - const lineBreakLines = rawLines.splice(0, closeIndex).map(rawLine => rawLine.slice(0, -2)); - lineBreakLines.unshift(line.slice(0, -2)); - lineBreakLines.push(rawLines.splice(0, 1)[0]); - buffer = <p>{lineBreakLines.map(lineBreakLine => <Text line={lineBreakLine} />)}</p>; - } else if (denotesClosingHtml(line, '')) { - buffer = null; - } else { - buffer = <p><Text line={line} /></p>; - } - - return ( - <> - { buffer } - <Content rawLines={rawLines} /> - </> - ); -}; - -export default Content; - diff --git a/src/lib/Markdown/Heading.tsx b/src/lib/Markdown/Heading.tsx new file mode 100644 index 0000000..cc0b709 --- /dev/null +++ b/src/lib/Markdown/Heading.tsx @@ -0,0 +1,33 @@ +import React from 'react'; +import { Typography, Divider, makeStyles } from '@material-ui/core'; + +interface PropTypes { + level: number; +} + +type Variant = 'h3' | 'h4' | 'h5' | 'h6'; + +const useStyles = makeStyles(theme => ({ + root: { + padding: theme.spacing(2, 0, 1, 0), + }, +})); + +const Heading: React.FC<PropTypes> = ({ children, level }) => { + const classes = useStyles(); + + let adjustedLevel = level + 2; // Make everything smaller + if (adjustedLevel > 6) adjustedLevel = 6; + + const variant: Variant = `h${adjustedLevel}` as Variant; + + return ( + <div className={classes.root}> + <Typography variant={variant}>{children}</Typography> + <Divider variant="middle" /> + </div> + ); +}; + +export default Heading; + diff --git a/src/lib/Markdown/Image.tsx b/src/lib/Markdown/Image.tsx new file mode 100644 index 0000000..472d6f0 --- /dev/null +++ b/src/lib/Markdown/Image.tsx @@ -0,0 +1,12 @@ +import React from 'react'; + +interface PropTypes { + src: string; + alt: string; +} + +const Image: React.FC<PropTypes> = ({ src, alt }) => { + return <img src={src} alt={alt} style={{ maxWidth: '100%', maxHeight: '100%' }} />; +}; + +export default Image; diff --git a/src/lib/Markdown/InlineCode.tsx b/src/lib/Markdown/InlineCode.tsx new file mode 100644 index 0000000..c51f924 --- /dev/null +++ b/src/lib/Markdown/InlineCode.tsx @@ -0,0 +1,18 @@ +import React from 'react'; +import { makeStyles } from '@material-ui/core'; + +const useStyles = makeStyles(theme => ({ + root: { + background: theme.palette.background.default, + borderRadius: theme.spacing(0.5), + padding: theme.spacing(0.5), + fontFamily: 'Monospace', + }, +})); + +const InlineCode: React.FC = ({ children }) => { + const classes = useStyles(); + return <span className={classes.root}>{children}</span>; +}; + +export default InlineCode; diff --git a/src/lib/Markdown/Markdown.tsx b/src/lib/Markdown/Markdown.tsx index cfcf117..4fa6a9a 100644 --- a/src/lib/Markdown/Markdown.tsx +++ b/src/lib/Markdown/Markdown.tsx @@ -1,22 +1,31 @@ import React, { useState, useEffect } from 'react'; +import { Link, Typography } from '@material-ui/core'; import axios from 'axios'; +import ReactMarkdown from 'react-markdown'; +import emoji from 'remark-gemoji'; -import Section from './Section'; +import CodeBlock from './CodeBlock'; +import InlineCode from './InlineCode'; +import Heading from './Heading'; +import Image from './Image'; interface PropTypes { data?: string; url?: string; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + context?: Record<string, any>; } const resolveUrls = (line: string, baseUrl: string): string => line.replace( /src="(?!http)(.*)"[\s>]/, (match, url) => `src="${baseUrl}/${url}?sanitize=true"`, ).replace( - /\[(.*\]?.*)\]\((?!http)(.+?)\)/, + /\[(.*\]?.*)\]\((?!http)(.+?)\)/g, (match, text, url) => `[${text}](${baseUrl}/${url})`, ); -const Markdown: React.FC<PropTypes> = ({ data, url }) => { + +const Markdown: React.FC<PropTypes> = ({ data, url, context = {} }) => { const [markdown, setMarkdown] = useState<string>(data || ''); if (url) axios.get(url).then(response => setMarkdown(response.data)); @@ -26,8 +35,34 @@ const Markdown: React.FC<PropTypes> = ({ data, url }) => { }, [data, url]); const baseUrl = url?.slice(0, url.lastIndexOf('/')) || ''; - const lines = markdown.split(/\r?\n/).map(line => resolveUrls(line, baseUrl)); - return <Section rawLines={lines} />; + const sanitized = resolveUrls(markdown, baseUrl); + + const WrappedInlineCode: React.FC = ({ children }) => { + if (typeof children === 'string' && children?.startsWith('$')) { + const symbol = children.slice(1); + return context[symbol] || null; + } + return <InlineCode>{children}</InlineCode>; + }; + + const renderers = { + heading: Heading, + code: CodeBlock, + link: Link, + image: Image, + inlineCode: WrappedInlineCode, + }; + + return ( + <Typography> + <ReactMarkdown + source={sanitized} + renderers={renderers} + plugins={[emoji]} + allowDangerousHtml + /> + </Typography> + ); }; diff --git a/src/lib/Markdown/Section.tsx b/src/lib/Markdown/Section.tsx deleted file mode 100644 index fb2933d..0000000 --- a/src/lib/Markdown/Section.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import React from 'react'; -import { Typography } from '@material-ui/core'; -import ContentSection from '../ContentSection/ContentSection'; -import Content from './Content'; -import { ParserPropTypes } from './types'; - -interface PropTypes extends ParserPropTypes { - level?: number; -} - -interface MapperPropTypes extends PropTypes { - SectionComponent: React.FC<PropTypes>; -} - -const getHeaderLevel = (header: string): number => { - if (!header) return 0; - let level = 0; - while (header[level] === '#') level += 1; - return level; -}; - -const SectionMapper: React.FC<MapperPropTypes> = ({ rawLines, level = 0, SectionComponent }) => { - const children = rawLines - .reduce((sections: string[][], line: string) => { - if (line) { - if (getHeaderLevel(line) === level) sections.push([]); - if (sections.length) sections[sections.length - 1].push(line); - } - return sections; - }, []) - .map(sectionLines => <SectionComponent rawLines={sectionLines} level={level} />); - - return <>{children}</>; -}; - - -const Section: React.FC<PropTypes> = ({ rawLines, level = 0 }) => { - const deeperLevelIndex = rawLines.findIndex(line => line.match(`^#{${level + 1},} .*$`)); - const rawContent = rawLines.splice(0, (deeperLevelIndex < 0) ? rawLines.length : deeperLevelIndex); - - if (!level) { - return ( - <> - <Typography><Content rawLines={rawContent} /></Typography> - <SectionMapper rawLines={rawLines} level={getHeaderLevel(rawLines[0])} SectionComponent={Section} /> - </> - ); - } - - const sectionName = rawContent.splice(0, 1)[0].slice(level).trim(); - const deeperLevel = getHeaderLevel(rawLines[0]); - return ( - <ContentSection sectionName={sectionName} level={level}> - <Content rawLines={rawContent} /> - <SectionMapper rawLines={rawLines} level={deeperLevel} SectionComponent={Section} /> - </ContentSection> - ); -}; - -export default Section; - diff --git a/src/lib/Markdown/SyntacticSpan.tsx b/src/lib/Markdown/SyntacticSpan.tsx deleted file mode 100644 index 11cc024..0000000 --- a/src/lib/Markdown/SyntacticSpan.tsx +++ /dev/null @@ -1,96 +0,0 @@ -import React from 'react'; -import { Link, makeStyles } from '@material-ui/core'; - -import { lib as emojiLib } from 'emojilib'; - -interface PropTypes { - span: string; -} - -interface RegexPair { - global: RegExp; - local: RegExp; -} - -interface Emoji { - name: string; - char: string; -} - -const enclosureRegex = (e: string): RegexPair => ({ - local: new RegExp(`${e}([^${e}]+)${e}`), - global: new RegExp(`(${e}[^${e}]+${e})`), -}); - -const regex: Record<string, RegexPair> = { - conceal: { - global: /(!?\[.+?\]\(.+?\))(?!])/g, - local: /!?\[(.*\]?.*)\]\((.+?)\)/, - }, - rawLink: { - // eslint-disable-next-line max-len - global: /((?:(?:[A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=+$,\w]+@)?[A-Za-z0-9.-]+|(?:www\.|[-;:&=+$,\w]+@)[A-Za-z0-9.-]+)(?:(?:\/[+~%/.\w-_]*)?\??(?:[-+=&;%@.\w_]*)#?(?:[.!/\\\w]*))?)/, - local: /&^/, - }, - emoji: enclosureRegex(':'), - bold: enclosureRegex('\\*\\*'), - italic: enclosureRegex('\\*'), - code: enclosureRegex('`'), - strikeThrough: enclosureRegex('~~'), -}; - -const splitter = new RegExp(Object.values(regex).map(pair => pair.global.source).join('|')); - -const emojiList: Emoji[] = []; -Object.keys(emojiLib).forEach(name => emojiList.push({ name, char: emojiLib[name].char })); - -const useStyles = makeStyles(theme => ({ - code: { - background: theme.palette.background.default, - borderRadius: theme.spacing(0.5), - padding: theme.spacing(0.5), - fontFamily: 'Monospace', - }, - image: { - maxWidth: '100%', - maxHeight: '100%', - }, -})); - -const SyntacticSpan: React.FC<PropTypes> = ({ span }) => { - const classes = useStyles(); - if (!span) return null; - - const matchConceal = regex.conceal.local.exec(span); - if (matchConceal) { - if (span[0] === '!') return <img src={matchConceal[2]} alt={matchConceal[1]} className={classes.image} />; - return <Link href={matchConceal[2]}><SyntacticSpan span={matchConceal[1]} /></Link>; - } - - const matchEmoji = span.match(regex.emoji.local); - if (matchEmoji) { - const emoji = emojiList.find(e => e.name === matchEmoji[1]); - return <span>{emoji ? emoji.char : span}</span>; - } - - const matchCode = span.match(regex.code.local); - if (matchCode) return <span className={classes.code}>{matchCode[1]}</span>; - - const matchBold = span.match(regex.bold.local); - if (matchBold) return <b>{matchBold[1]}</b>; - - const matchItalic = span.match(regex.italic.local); - if (matchItalic) return <i>{matchItalic[1]}</i>; - - const matchStrikeThrough = span.match(regex.strikeThrough.local); - if (matchStrikeThrough) return <span style={{ textDecoration: 'line-through' }}>{matchStrikeThrough[1]}</span>; - - if (span.match(regex.rawLink.global)) return <Link href={span}>{span}</Link>; - - return <>{span}</>; -}; - - -export { splitter }; -export default SyntacticSpan; - diff --git a/src/lib/Markdown/Text.tsx b/src/lib/Markdown/Text.tsx deleted file mode 100644 index be715fd..0000000 --- a/src/lib/Markdown/Text.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; -import SyntacticSpan, { splitter } from './SyntacticSpan'; - -interface PropTypes { - line: string; -} - -const Text: React.FC<PropTypes> = ({ line }) => { - return <>{line.split(splitter).map(span => <SyntacticSpan span={span} />)}</>; -}; - -export default Text; - diff --git a/src/lib/Markdown/emojilib.d.ts b/src/lib/Markdown/emojilib.d.ts deleted file mode 100644 index cddfeea..0000000 --- a/src/lib/Markdown/emojilib.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare module 'emojilib'; - diff --git a/src/lib/Markdown/types.d.ts b/src/lib/Markdown/types.d.ts new file mode 100644 index 0000000..f444cf1 --- /dev/null +++ b/src/lib/Markdown/types.d.ts @@ -0,0 +1,7 @@ +declare module 'remark-gemoji'; +declare module '*.md' { + // eslint-disable-next-line import/no-mutable-exports + let Markdown: string; + export default Markdown; +} + diff --git a/src/lib/Markdown/types.ts b/src/lib/Markdown/types.ts deleted file mode 100644 index 0b6f4b6..0000000 --- a/src/lib/Markdown/types.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface ParserPropTypes { - rawLines: string[]; -} - diff --git a/src/lib/index.ts b/src/lib/index.ts index 545e6f8..2e6dd9e 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -1,3 +1,5 @@ -export { default as ContentSection } from './ContentSection/ContentSection'; export { default as Benzin } from './Benzin/Benzin'; export { default as Markdown } from './Markdown/Markdown'; +export { default as CodeBlock } from './Markdown/CodeBlock'; +export { default as InlineCode } from './Markdown/InlineCode'; +export { default as Heading } from './Markdown/Heading'; |