diff --git a/README.md b/README.md index 8e731a8..ee3f555 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ Monorepo of [remark](https://github.com/remarkjs/remark) packages including a Ma - [`ggsmark`](https://github.com/johnnyhuy/ggsmark/tree/master/packages/ggsmark) - Markdown package used in ggs.sx - [`remark-text-alignment`](https://github.com/johnnyhuy/ggsmark/tree/master/packages/remark-text-alignment) - remark plugin to align text +- [`remark-spoilers`](https://github.com/johnnyhuy/ggsmark/tree/master/packages/remark-spoilers) - remark spoilers to hide Markdown content - [`remark-color-text`](https://github.com/johnnyhuy/ggsmark/tree/master/packages/remark-color-text) - remark plugin to set colors inline and blocks ## Credits @@ -14,10 +15,6 @@ This package is built on top of the [remarkjs/remark](https://github.com/remarkj Packages in this monorepo have taken inspiration from [zestedesavoir/zmarkdown](https://github.com/zestedesavoir/zmarkdown). Check out their large suite of plugins! -## Installing +## Contibuting -When you have Node JS installed run the following command in your project. - -```bash -yarn add ggsmark -``` +Follow the [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) standard to calculate versioning. diff --git a/packages/ggsmark/README.md b/packages/ggsmark/README.md index c068441..b56ac8a 100644 --- a/packages/ggsmark/README.md +++ b/packages/ggsmark/README.md @@ -1,13 +1,16 @@ # GGSMark -This is the Markdown package used in the GGS.SX website. +Markdown package used in the [GGS.SX](https://ggs.sx/) website. -## Usage +## Installation + +```bash +npm i ggsmark +``` -Basic usage example: +## Usage ```js -// If you use ESModules import ggsmark from 'ggsmark' let output = ggsmark('**foo** bar') @@ -20,15 +23,11 @@ It's also worth investigating unit tests to understand the expected output. ## Examples ### Strikethrough + ```markdown ~~Text~~ ``` - - ### SoundCloud ```markdown @@ -40,4 +39,3 @@ It's also worth investigating unit tests to understand the expected output. ```markdown !(https://www.youtube.com/watch?v=pwmY1XUTBpE) ``` - diff --git a/packages/ggsmark/index.js b/packages/ggsmark/index.js index 01395fd..959a503 100644 --- a/packages/ggsmark/index.js +++ b/packages/ggsmark/index.js @@ -8,6 +8,7 @@ import sanitize from 'rehype-sanitize' import iframe from 'remark-iframes' import align from 'remark-text-alignment' import color from 'remark-color-text' +import spoilers from 'remark-spoilers' // Don't use remark-html otherwise we can't customize HTML import stringify from 'rehype-stringify' @@ -85,6 +86,7 @@ export default (text) => { .use(rehype) .use(stringify) .use(color) + .use(spoilers) .use(sanitize, schema) .processSync(text) .toString() diff --git a/packages/ggsmark/package.json b/packages/ggsmark/package.json index 99fdde0..98b3812 100644 --- a/packages/ggsmark/package.json +++ b/packages/ggsmark/package.json @@ -10,6 +10,7 @@ "hast-util-sanitize": "^3.0.0", "rehype-sanitize": "^4.0.0", "rehype-stringify": "^8.0.0", + "remark-spoilers": "^0.0.0", "remark-color-text": "^0.0.2", "remark-iframes": "^4.0.4", "remark-parse": "^8.0.3", diff --git a/packages/remark-color-text/README.md b/packages/remark-color-text/README.md index a26127f..95da7a6 100644 --- a/packages/remark-color-text/README.md +++ b/packages/remark-color-text/README.md @@ -1,6 +1,12 @@ # remark-color-text -Markdown extension for the Gentlemen's Gaming Society website. +[remark](https://github.com/remarkjs/remark) plugin to color text via block and inline text in Markdown. + +## Installation + +```bash +npm i remark-color-text +``` ## Usage @@ -9,14 +15,11 @@ import remark from 'remark' import html from 'remark-html' import color from 'remark-color-text' -// Basic use -let output = remark() +remark() .use(html) .use(color) - .processSync('example markdown text') - .toString() -console.log(output) +... ``` ## Options @@ -27,7 +30,7 @@ Add the color plugin to remark. ### `options` -#### `options.tokens` +#### `options.token` Token used to open and close colored text. diff --git a/packages/remark-spoilers/LICENSE b/packages/remark-spoilers/LICENSE new file mode 100644 index 0000000..64a94a0 --- /dev/null +++ b/packages/remark-spoilers/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Johnny Huynh + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/remark-spoilers/README.md b/packages/remark-spoilers/README.md new file mode 100644 index 0000000..5c4dd58 --- /dev/null +++ b/packages/remark-spoilers/README.md @@ -0,0 +1,76 @@ +# remark-spoilers + +remark spoiler plugin that uses native [details and summary](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/summary) HTML elements. + +```html +
+ I have keys but no doors. I have space but no room. You can enter but can’t leave. What am I? + A keyboard. +
+``` + +## Installation + +```bash +npm i remark-spoilers +``` + +## Usage + +```js +import remark from 'remark' +import html from 'remark-html' +import spoilers from 'remark-spoilers' + +remark() + .use(html) + .use(spoilers) + +... +``` + +## Options + +### `.use(spoilers [, options])` + +Add the spoilers plugin to remark. + +### `options` + +#### `options.defaultSummary` + +The default summary text in the spoiler. + +**Default** `Open spoiler` + +#### `options.token` + +Token used to open and close spoilers text. + +**Default** `!spoiler` + +#### `options.detailsClassName` + +Class name for the `
` HTML element. No class name set by default. + +**Default** `''` + +#### `options.summaryClassName` + +Class name for the `` HTML element. No class name set by default. + +**Default** `''` + +## Examples + +```markdown +# Basic spoiler +!spoiler +Hello world, I'm in a spoiler +!spoiler + +# Basic spoiler with custom summary +!spoiler Hello summary, click me to open the spoiler! +Hello world, I'm in a spoiler +!spoiler +``` diff --git a/packages/remark-spoilers/__tests__/__snapshots__/index.spec.js.snap b/packages/remark-spoilers/__tests__/__snapshots__/index.spec.js.snap new file mode 100644 index 0000000..6c113b6 --- /dev/null +++ b/packages/remark-spoilers/__tests__/__snapshots__/index.spec.js.snap @@ -0,0 +1,49 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`spoilers should not show spoiler if duped start token 1`] = ` +"
+
+
+
+
+
+

why isn't this working!?

+

there's no end!!!

+" +`; + +exports[`spoilers should not show spoiler if duped start token with summary 1`] = ` +"
testestestsetset
+
testipsum
+

!spoiler yeet

+

why isn't this working!?

+

there's no end!!!

+" +`; + +exports[`spoilers should not show spoiler if not closed 1`] = ` +"

!spoiler

+

why isn't this working!?

+

there's no end!!!

+" +`; + +exports[`spoilers should show multiple spoilers 1`] = ` +"

Hello world

+

This is some text.

+
Click me to see a surprise

peek-a-boo!

+

between the spoilers

+
Another click me to see a surprise

peek-a-boo!

+

the end

+" +`; + +exports[`spoilers should show spoiler with custom summary 1`] = ` +"
Click me to see a surprise

peek-a-boo!

+" +`; + +exports[`spoilers should show spoiler without custom summary 1`] = ` +"

peek-a-boo!

+" +`; diff --git a/packages/remark-spoilers/__tests__/index.spec.js b/packages/remark-spoilers/__tests__/index.spec.js new file mode 100644 index 0000000..126bfe7 --- /dev/null +++ b/packages/remark-spoilers/__tests__/index.spec.js @@ -0,0 +1,129 @@ +import remark from 'remark' +import html from 'remark-html' +import spoiler from '..' +import dedent from 'dedent' + +describe('spoilers', () => { + test('should show spoiler without custom summary', () => { + // Arrange + let string = dedent` + !spoiler + peek-a-boo! + !spoiler + ` + + // Act + let result = remark().use(html).use(spoiler).processSync(string).toString() + + // Assert + expect(result).toMatchSnapshot() + }) + + test('should show spoiler with custom summary', () => { + // Arrange + let string = dedent` + !spoiler Click me to see a surprise + peek-a-boo! + !spoiler + ` + + // Act + let result = remark().use(html).use(spoiler).processSync(string).toString() + + // Assert + expect(result).toMatchSnapshot() + }) + + test('should show multiple spoilers', () => { + // Arrange + let string = dedent` + # Hello world + + This is some text. + + !spoiler Click me to see a surprise + peek-a-boo! + !spoiler + + between the spoilers + + !spoiler Another click me to see a surprise + peek-a-boo! + !spoiler + + the end + ` + + // Act + let result = remark().use(html).use(spoiler).processSync(string).toString() + + // Assert + expect(result).toMatchSnapshot() + }) + + test('should not show spoiler if not closed', () => { + // Arrange + let string = dedent` + !spoiler + + why isn't this working!? + + there's no end!!! + ` + + // Act + let result = remark().use(html).use(spoiler).processSync(string).toString() + + // Assert + expect(result).toMatchSnapshot() + }) + + test('should not show spoiler if duped start token', () => { + // Arrange + let string = dedent` + !spoiler + !spoiler + !spoiler + !spoiler + !spoiler + !spoiler + !spoiler + !spoiler + !spoiler + !spoiler + !spoiler + !spoiler + + why isn't this working!? + + there's no end!!! + ` + + // Act + let result = remark().use(html).use(spoiler).processSync(string).toString() + + // Assert + expect(result).toMatchSnapshot() + }) + + test('should not show spoiler if duped start token with summary', () => { + // Arrange + let string = dedent` + !spoiler testestestsetset + !spoiler lorem + !spoiler testipsum + !spoiler loreeem + !spoiler yeet + + why isn't this working!? + + there's no end!!! + ` + + // Act + let result = remark().use(html).use(spoiler).processSync(string).toString() + + // Assert + expect(result).toMatchSnapshot() + }) +}) diff --git a/packages/remark-spoilers/index.js b/packages/remark-spoilers/index.js new file mode 100644 index 0000000..123b7ee --- /dev/null +++ b/packages/remark-spoilers/index.js @@ -0,0 +1,104 @@ +const C_NEWLINE = '\n' +const C_NEWPARAGRAPH = '\n\n' + +/** + * Match line against an array of token + * @param {String} token token like '!#' + * @param {String} value value to check of the token + */ +function matchToken(token, value) { + return value.trim().startsWith(token) +} + +export default function plugin(options = {}) { + const Parser = this.Parser + const blockTokenizers = Parser.prototype.blockTokenizers + const blockMethods = Parser.prototype.blockMethods + const inlineTokenizers = Parser.prototype.inlineTokenizers + const inlineMethods = Parser.prototype.inlineMethods + + options.defaultSummary = options.defaultSummary ?? 'Open spoiler' + options.token = options.token ?? '!spoiler' + options.detailsClassName = options.detailsClassName ?? '' + options.summaryClassName = options.summaryClassName ?? '' + + function tokenizeBlocks(eat, value, silent) { + let match = matchToken(options.token, value) + + if (!match) return + + if (silent) return true + + let startBlock, + endBlock = 0 + let index, + newLine = 0 + let completeBlock = false + let firstRun = true + + do { + newLine = value.indexOf(C_NEWLINE, index + 1) + + let line = value.substring(index, newLine === -1 ? value.length : newLine) + let matchedEndToken = matchToken(options.token, line) && !firstRun + + // Found a match to end the block + if (!!matchedEndToken) { + endBlock = newLine === -1 ? value.length : newLine + completeBlock = true + } + + index = newLine + firstRun = false + } while (!completeBlock && newLine !== -1) + + if (!completeBlock) return + + let block = value.substring(startBlock, endBlock) + let blockContent = block + .substring(block.indexOf(C_NEWLINE), block.lastIndexOf(C_NEWLINE)) + .trim() + let summary = block + .slice(options.token.length, block.indexOf(C_NEWLINE)) + .trim() + + const start = eat.now() + const add = eat(block) + const end = eat.now() + let children = [] + let childrenBlockContent = this.tokenizeBlock(blockContent, start) + + if (summary !== '') { + children.push({ + type: 'summary', + data: { + hName: 'summary' + }, + children: [ + { + type: 'text', + value: summary + } + ] + }) + } + + children = [...children, ...childrenBlockContent] + + return add({ + type: 'spoiler', + children: children, + data: { + hName: 'details' + }, + position: { + start, + end + } + }) + } + + blockTokenizers.spoiler = tokenizeBlocks + + blockMethods.splice(blockMethods.indexOf('blockquote') + 1, 0, 'spoiler') +} diff --git a/packages/remark-spoilers/package.json b/packages/remark-spoilers/package.json new file mode 100644 index 0000000..e1c44e3 --- /dev/null +++ b/packages/remark-spoilers/package.json @@ -0,0 +1,9 @@ +{ + "name": "remark-spoilers", + "version": "0.0.0", + "author": "Johnny Huynh ", + "keywords": [ + "remark" + ], + "license": "MIT" +} diff --git a/packages/remark-text-alignment/README.md b/packages/remark-text-alignment/README.md index feae8c8..2490874 100644 --- a/packages/remark-text-alignment/README.md +++ b/packages/remark-text-alignment/README.md @@ -1,11 +1,17 @@ # remark-text-alignment -Markdown extension for the Gentlemen's Gaming Society website. +[remark](https://github.com/remarkjs/remark) plugin to align text. ## Credits Plugin was originally built on top of [zMarkdown's remark-align plugin](https://github.com/zestedesavoir/zmarkdown/tree/master/packages/remark-align). +## Installation + +```bash +npm i remark-text-alignment +``` + ## Usage ```js @@ -14,16 +20,14 @@ import html from 'remark-html' import alignment from 'remark-text-alignment' // Basic use -let output = remark() +remark() .use(html) .use(alignment) - .processSync('example markdown text') - .toString() -console.log(output) +... // Use class names and set right class name to example-right -let output = remark() +remark() .use(html) .use(alignment, { useClassNames: true, @@ -31,16 +35,29 @@ let output = remark() left: 'example-right' } }) - .processSync('example markdown text') - .toString() -console.log(output) +... ``` ## Options -- `useClassNames`: Enable class names instead of inline CSS -- `classNames`: Object that contains left, right and center class names (useClassNames must be enabled) +### `.use(color [, options])` + +Add the color plugin to remark. + +### `options` + +#### `options.classNames` + +Enable class names instead of inline CSS + +**Default** `{}` + +#### `options.useClassNames` + +Object that contains left, right and center class names (useClassNames must be enabled) + +**Default** `false` ## Examples