diff --git a/README.md b/README.md index 05e52565..18003086 100644 --- a/README.md +++ b/README.md @@ -25,12 +25,12 @@ ## The problem Trying to embed well known services (like [CodePen][codepen], -[CodeSandbox][codesandbox], [GIPHY][giphy], [Instagram][instagram], -[Lichess][lichess], [Pinterest][pinterest], [Slides][slides], -[SoundCloud][soundcloud], [Spotify][spotify], [Streamable][streamable], -[Twitch][twitch], [Twitter][twitter] or [YouTube][youtube]) into your -[Gatsby][gatsby] website can be hard, since you have to know how this needs to -be done for all of these different services. +[CodeSandbox][codesandbox], [Excalidraw][excalidraw], [GIPHY][giphy], +[Instagram][instagram], [Lichess][lichess], [Pinterest][pinterest], +[Slides][slides], [SoundCloud][soundcloud], [Spotify][spotify], +[Streamable][streamable], [Twitch][twitch], [Twitter][twitter] or +[YouTube][youtube]) into your [Gatsby][gatsby] website can be hard, since you +have to know how this needs to be done for all of these different services. ## This solution @@ -49,6 +49,7 @@ empty lines) and replace it with the proper embed-code. - [Supported services](#supported-services) - [CodePen](#codepen) - [CodeSandbox](#codesandbox) + - [Excalidraw](#excalidraw) - [GIPHY](#giphy) - [Instagram](#instagram) - [Lichess](#lichess) @@ -209,6 +210,57 @@ https://codesandbox.io/s/ynn88nx9x?view=split +### Excalidraw + +#### Usage + +```md +https://excalidraw.com/#json=5882351917727744,hCHWNykp-VOHM8S0cV5psw +``` + +
+Result + +```html + + + + + + + + + + + + +``` + +
+ ### GIPHY #### Usage @@ -785,6 +837,7 @@ MIT [codepen]: https://codepen.io [codesandbox]: https://codesandbox.io +[excalidraw]: https://excalidraw.com [embedded-tweet-docs]: https://developer.twitter.com/web/embedded-tweets [gatsby]: https://github.com/gatsbyjs/gatsby [gatsby-plugin-instagram-embed]: https://github.com/MichaelDeBoey/gatsby-plugin-instagram-embed diff --git a/package.json b/package.json index f7006061..edf710e1 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,8 @@ "streamable", "twitch", "twitter", - "youtube" + "youtube", + "excalidraw" ], "author": "Michaƫl De Boey (https://michaeldeboey.be)", "license": "MIT", @@ -50,6 +51,7 @@ "@babel/runtime": "^7.9.6", "fetch-retry": "^3.1.0", "node-fetch": "^2.6.0", + "puppeteer": "^2.1.1", "unist-util-visit": "^2.0.2" }, "devDependencies": { diff --git a/src/__tests__/transformers/Excalidraw.js b/src/__tests__/transformers/Excalidraw.js new file mode 100644 index 00000000..c0e9e714 --- /dev/null +++ b/src/__tests__/transformers/Excalidraw.js @@ -0,0 +1,73 @@ +import cases from 'jest-in-case'; + +import plugin from '../..'; +import { getHTML, shouldTransform } from '../../transformers/Excalidraw'; + +import { cache, getMarkdownASTForFile, parseASTToMarkdown } from '../helpers'; + +cases( + 'url validation', + ({ url, valid }) => { + expect(shouldTransform(url)).toBe(valid); + }, + { + 'non-Excalidraw url': { + url: 'https://not-an-excalidraw-url.com', + valid: false, + }, + "non-Excalidraw url ending with 'excalidraw.com'": { + url: 'https://this-is-not-excalidraw.com', + valid: false, + }, + "non-Excalidraw url ending with 'excalidraw.com' having '#json='": { + url: + 'https://this-is-not-excalidraw.com/#json=5882351917727744,hCHWNykp-VOHM8S0cV5psw', + valid: false, + }, + "Excalidraw url without '#json='": { + url: 'https://excalidraw.com', + valid: false, + }, + 'Excalidraw url': { + url: + 'https://excalidraw.com/#json=5882351917727744,hCHWNykp-VOHM8S0cV5psw', + valid: true, + }, + "Excalidraw url with 'www' subdomain": { + url: + 'https://www.excalidraw.com/#json=5882351917727744,hCHWNykp-VOHM8S0cV5psw', + valid: true, + }, + } +); + +test('Gets the correct Excalidraw svg', async () => { + const html = await getHTML( + 'https://excalidraw.com/#json=5882351917727744,hCHWNykp-VOHM8S0cV5psw' + ); + + expect(html).toMatchInlineSnapshot( + `""` + ); +}); + +test('Plugin can transform Excalidraw links', async () => { + const markdownAST = getMarkdownASTForFile('Excalidraw'); + + const processedAST = await plugin({ cache, markdownAST }); + + expect(parseASTToMarkdown(processedAST)).toMatchInlineSnapshot(` + " + + + + + + + + + + + " + `); +}, 30000); diff --git a/src/__tests__/transformers/__fixtures__/Excalidraw.md b/src/__tests__/transformers/__fixtures__/Excalidraw.md new file mode 100644 index 00000000..c7782e31 --- /dev/null +++ b/src/__tests__/transformers/__fixtures__/Excalidraw.md @@ -0,0 +1,11 @@ +https://not-an-excalidraw-url.com + +https://this-is-not-excalidraw.com + +https://this-is-not-excalidraw.com/#json=5882351917727744,hCHWNykp-VOHM8S0cV5psw + +https://excalidraw.com + +https://excalidraw.com/#json=5882351917727744,hCHWNykp-VOHM8S0cV5psw + +https://www.excalidraw.com/#json=5882351917727744,hCHWNykp-VOHM8S0cV5psw diff --git a/src/transformers/Excalidraw.js b/src/transformers/Excalidraw.js new file mode 100644 index 00000000..4ab56ee4 --- /dev/null +++ b/src/transformers/Excalidraw.js @@ -0,0 +1,45 @@ +import puppeteer from 'puppeteer'; + +const getImage = async (url) => { + const browser = await puppeteer.launch({ + // disable sandbox in production + args: process.env.NODE_ENV === 'production' ? ['--no-sandbox'] : [], + }); + + const page = await browser.newPage(); + await page.goto(url); + await page.click('[aria-label=Export]'); + await page.click("[aria-label='Scale 3 x']"); + + const frame = await page.mainFrame(); + const result = await frame.evaluate( + () => + new Promise((resolve, reject) => { + try { + delete window.chooseFileSystemEntries; + const reader = new FileReader(); + reader.addEventListener('loadend', () => resolve(reader.result)); + reader.addEventListener('error', () => reject(reader.error)); + URL.createObjectURL = (blob) => reader.readAsText(blob); + + const button = document.querySelector('[aria-label="Export to SVG"]'); + button.click(); + } catch (error) { + reject(error); + } + }) + ); + + await browser.close(); + return result; +}; + +export const shouldTransform = (url) => + /^https?:\/\/(www\.)?excalidraw\.com\/#json=/.test(url); + +export const getHTML = async (url) => { + const svg = await getImage(url); + return `${svg + .replace('`; +}; diff --git a/src/transformers/index.js b/src/transformers/index.js index 50483ca3..f1204211 100644 --- a/src/transformers/index.js +++ b/src/transformers/index.js @@ -1,5 +1,6 @@ import * as CodePenTransformer from './CodePen'; import * as CodeSandboxTransformer from './CodeSandbox'; +import * as ExcalidrawTransformer from './Excalidraw'; import * as GIPHYTransformer from './GIPHY'; import * as InstagramTransformer from './Instagram'; import * as LichessTransformer from './Lichess'; @@ -15,6 +16,7 @@ import * as YouTubeTransformer from './YouTube'; export const defaultTransformers = [ CodePenTransformer, CodeSandboxTransformer, + ExcalidrawTransformer, GIPHYTransformer, InstagramTransformer, LichessTransformer,