diff --git a/.github/workflows/bb.yml b/.github/workflows/bb.yml new file mode 100644 index 0000000..0198fc3 --- /dev/null +++ b/.github/workflows/bb.yml @@ -0,0 +1,13 @@ +name: bb +on: + issues: + types: [opened, reopened, edited, closed, labeled, unlabeled] + pull_request_target: + types: [opened, reopened, edited, closed, labeled, unlabeled] +jobs: + main: + runs-on: ubuntu-latest + steps: + - uses: unifiedjs/beep-boop-beta@main + with: + repo-token: ${{secrets.GITHUB_TOKEN}} diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..fe284ad --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,21 @@ +name: main +on: + - pull_request + - push +jobs: + main: + name: ${{matrix.node}} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: dcodeIO/setup-node-nvm@master + with: + node-version: ${{matrix.node}} + - run: npm install + - run: npm test + - uses: codecov/codecov-action@v1 + strategy: + matrix: + node: + - lts/erbium + - node diff --git a/.gitignore b/.gitignore index fdefc8c..53a29e3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ -.DS_Store -*.log -.nyc_output/ coverage/ node_modules/ +.DS_Store +*.d.ts +*.log yarn.lock diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index e108609..0000000 --- a/.travis.yml +++ /dev/null @@ -1,5 +0,0 @@ -language: node_js -node_js: - - lts/dubnium - - node -after_script: bash <(curl -s https://codecov.io/bash) diff --git a/index.js b/index.js index 1f9d36b..7c44ca4 100644 --- a/index.js +++ b/index.js @@ -1,39 +1,34 @@ -'use strict' +/** + * @typedef {import('mdast').Root} Root + * @typedef {import('micromark-extension-gfm').Options & import('mdast-util-gfm').Options} Options + */ -var syntax = require('micromark-extension-gfm') -var fromMarkdown = require('mdast-util-gfm/from-markdown') -var toMarkdown = require('mdast-util-gfm/to-markdown') +import {gfm} from 'micromark-extension-gfm' +import {gfmFromMarkdown, gfmToMarkdown} from 'mdast-util-gfm' -var warningIssued +/** + * Plugin to support GitHub Flavored Markdown (GFM). + * + * @type {import('unified').Plugin<[Options?]|void[], Root>} + */ +export default function remarkGfm(options = {}) { + const data = this.data() -module.exports = gfm + add('micromarkExtensions', gfm(options)) + add('fromMarkdownExtensions', gfmFromMarkdown) + add('toMarkdownExtensions', gfmToMarkdown(options)) -function gfm(options) { - var data = this.data() - - /* istanbul ignore next - old remark. */ - if ( - !warningIssued && - ((this.Parser && - this.Parser.prototype && - this.Parser.prototype.blockTokenizers) || - (this.Compiler && - this.Compiler.prototype && - this.Compiler.prototype.visitors)) - ) { - warningIssued = true - console.warn( - '[remark-gfm] Warning: please upgrade to remark 13 to use this plugin' + /** + * @param {string} field + * @param {unknown} value + */ + function add(field, value) { + const list = /** @type {unknown[]} */ ( + // Other extensions + /* c8 ignore next 2 */ + data[field] ? data[field] : (data[field] = []) ) - } - add('micromarkExtensions', syntax(options)) - add('fromMarkdownExtensions', fromMarkdown) - add('toMarkdownExtensions', toMarkdown(options)) - - function add(field, value) { - /* istanbul ignore if - other extensions. */ - if (data[field]) data[field].push(value) - else data[field] = [value] + list.push(value) } } diff --git a/package.json b/package.json index ed9245f..3e442a6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "remark-gfm", - "version": "1.0.0", + "version": "2.0.0", "description": "remark plugin to support GFM (autolink literals, strikethrough, tables, tasklists)", "license": "MIT", "keywords": [ @@ -27,42 +27,43 @@ "contributors": [ "Titus Wormer (https://wooorm.com)" ], - "types": "types/index.d.ts", + "sideEffects": false, + "type": "module", + "main": "index.js", + "types": "index.d.ts", "files": [ - "types/index.d.ts", + "index.d.ts", "index.js" ], "dependencies": { - "mdast-util-gfm": "^0.1.0", - "micromark-extension-gfm": "^0.3.0" + "@types/mdast": "^3.0.0", + "mdast-util-gfm": "^1.0.0", + "micromark-extension-gfm": "^1.0.0", + "unified": "^10.0.0" }, "devDependencies": { + "@types/tape": "^4.0.0", + "c8": "^7.0.0", "dtslint": "^4.0.0", - "is-hidden": "^1.0.0", - "not": "^0.1.0", - "nyc": "^15.0.0", + "is-hidden": "^2.0.0", "prettier": "^2.0.0", - "remark": "^13.0.0-alpha.1", - "remark-cli": "^8.0.0", - "remark-preset-wooorm": "^7.0.0", - "string-width": "^4.2.0", + "remark": "^14.0.0", + "remark-cli": "^10.0.0", + "remark-preset-wooorm": "^8.0.0", + "rimraf": "^3.0.0", + "string-width": "^5.0.0", "tape": "^5.0.0", - "to-vfile": "^6.0.0", - "unified": "^9.0.0", - "xo": "^0.33.0" + "to-vfile": "^7.0.0", + "type-coverage": "^2.0.0", + "typescript": "^4.0.0", + "xo": "^0.43.0" }, "scripts": { - "format": "remark . -qfo --ignore-pattern test/ && prettier . --write && xo --fix", - "test-api": "node test", - "test-coverage": "nyc --reporter lcov tape test/index.js", - "test-types": "dtslint types", - "test": "npm run format && npm run test-coverage && npm run test-types" - }, - "nyc": { - "check-coverage": true, - "lines": 100, - "functions": 100, - "branches": 100 + "build": "rimraf \"test/**/*.d.ts\" \"*.d.ts\" && tsc && type-coverage", + "format": "remark . -qfo --ignore-pattern test/ && prettier . -w --loglevel warn && xo --fix", + "test-api": "node --conditions development test/index.js", + "test-coverage": "c8 --check-coverage --branches 100 --functions 100 --lines 100 --statements 100 --reporter lcov npm run test-api", + "test": "npm run build && npm run format && npm run test-coverage" }, "prettier": { "tabWidth": 2, @@ -73,16 +74,17 @@ "trailingComma": "none" }, "xo": { - "prettier": true, - "esnext": false, - "rules": { - "unicorn/no-fn-reference-in-iterator": "off", - "unicorn/prefer-optional-catch-binding": "off" - } + "prettier": true }, "remarkConfig": { "plugins": [ "preset-wooorm" ] + }, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true } } diff --git a/readme.md b/readme.md index e40c39c..723b210 100644 --- a/readme.md +++ b/readme.md @@ -20,6 +20,9 @@ Use this plugin for remark 13+. ## Install +This package is [ESM only](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c): +Node 12+ is needed to use it and it must be `import`ed instead of `require`d. + [npm][]: ```sh @@ -52,24 +55,25 @@ www.example.com, https://example.com, and contact@example.com. * [x] done ``` -And our script, `example.js`, looks as follows: +And our module, `example.js`, looks as follows: ```js -var vfile = require('to-vfile') -var report = require('vfile-reporter') -var unified = require('unified') -var parse = require('remark-parse') -var gfm = require('remark-gfm') -var remark2rehype = require('remark-rehype') -var stringify = require('rehype-stringify') +import {readSync} from 'to-vfile' +import {reporter} from 'vfile-reporter' +import {unified} from 'unified' +import remarkParse from 'remark-parse' +import remarkGfm from 'remark-gfm' +import remarkRehype from 'remark-rehype' +import remarkStringify from 'rehype-stringify' unified() - .use(parse) - .use(gfm) - .use(remark2rehype) - .use(stringify) - .process(vfile.readSync('example.md'), function (err, file) { - console.error(report(err || file)) + .use(remarkParse) + .use(remarkGfm) + .use(remarkRehype) + .use(remarkStringify) + .process(readSync('example.md')) + .then((file) => { + console.error(reporter(file)) console.log(String(file)) }) ``` @@ -103,7 +107,10 @@ example.md: no issues found ## API -### `remark().use(gfm[, options])` +This package exports no identifiers. +The default export is `remarkGfm`. + +### `unified().use(remarkGfm[, options])` Configures remark so that it can parse and serialize GFM (autolink literals, strikethrough, tables, tasklists). @@ -152,7 +159,7 @@ attacks. — Footnotes * [`remark-frontmatter`](https://github.com/remarkjs/remark-frontmatter) — Frontmatter (YAML, TOML, and more) -* [`remark-math`](https://github.com/rokt33r/remark-math) +* [`remark-math`](https://github.com/remarkjs/remark-math) — Math ## Contribute @@ -171,9 +178,9 @@ abide by its terms. -[build-badge]: https://img.shields.io/travis/remarkjs/remark-gfm/main.svg +[build-badge]: https://github.com/remarkjs/remark-gfm/workflows/main/badge.svg -[build]: https://travis-ci.org/remarkjs/remark-gfm +[build]: https://github.com/remarkjs/remark-gfm/actions [coverage-badge]: https://img.shields.io/codecov/c/github/remarkjs/remark-gfm.svg diff --git a/test/fixtures/autolink-literal/input.md b/test/fixtures/autolink-literal/input.md index 1fd2f30..76efeb7 100644 --- a/test/fixtures/autolink-literal/input.md +++ b/test/fixtures/autolink-literal/input.md @@ -83,3 +83,5 @@ a@a-b.c Can’t end in an underscore followed by a period: aaa@a.b_. Can contain an underscore followed by a period: aaa@a.b_.c + +[https://google.com](https://google.com) diff --git a/test/fixtures/autolink-literal/output.md b/test/fixtures/autolink-literal/output.md index e95f2f1..d9c1644 100644 --- a/test/fixtures/autolink-literal/output.md +++ b/test/fixtures/autolink-literal/output.md @@ -52,7 +52,7 @@ Visit [www.commonmark.org/a.b](http://www.commonmark.org/a.b). -https\://example + @@ -83,3 +83,5 @@ a.b-c_d\@a.b\_ Can’t end in an underscore followed by a period: aaa\@a.b\_. Can contain an underscore followed by a period: + + diff --git a/test/fixtures/autolink-literal/tree.json b/test/fixtures/autolink-literal/tree.json index 458412a..2165724 100644 --- a/test/fixtures/autolink-literal/tree.json +++ b/test/fixtures/autolink-literal/tree.json @@ -430,19 +430,7 @@ "children": [ { "type": "text", - "value": "www.aaa.bbb.ccc_ccc", - "position": { - "start": { - "line": 15, - "column": 1, - "offset": 190 - }, - "end": { - "line": 15, - "column": 20, - "offset": 209 - } - } + "value": "www.aaa.bbb.ccc_ccc" } ], "position": { @@ -463,19 +451,7 @@ "children": [ { "type": "text", - "value": "www.aaa_bbb.ccc", - "position": { - "start": { - "line": 17, - "column": 1, - "offset": 211 - }, - "end": { - "line": 17, - "column": 16, - "offset": 226 - } - } + "value": "www.aaa_bbb.ccc" } ], "position": { @@ -496,19 +472,7 @@ "children": [ { "type": "text", - "value": "www.aaa.bbb.ccc.ddd_ddd", - "position": { - "start": { - "line": 19, - "column": 1, - "offset": 228 - }, - "end": { - "line": 19, - "column": 24, - "offset": 251 - } - } + "value": "www.aaa.bbb.ccc.ddd_ddd" } ], "position": { @@ -529,19 +493,7 @@ "children": [ { "type": "text", - "value": "www.aaa.bbb.ccc_ccc.ddd", - "position": { - "start": { - "line": 21, - "column": 1, - "offset": 253 - }, - "end": { - "line": 21, - "column": 24, - "offset": 276 - } - } + "value": "www.aaa.bbb.ccc_ccc.ddd" } ], "position": { @@ -1535,8 +1487,27 @@ "type": "paragraph", "children": [ { - "type": "text", - "value": "https://example", + "type": "link", + "title": null, + "url": "https://example", + "children": [ + { + "type": "text", + "value": "https://example", + "position": { + "start": { + "line": 55, + "column": 1, + "offset": 813 + }, + "end": { + "line": 55, + "column": 16, + "offset": 828 + } + } + } + ], "position": { "start": { "line": 55, @@ -2061,19 +2032,7 @@ "children": [ { "type": "text", - "value": "a.b-c_d@a.b-", - "position": { - "start": { - "line": 75, - "column": 1, - "offset": 1087 - }, - "end": { - "line": 75, - "column": 13, - "offset": 1099 - } - } + "value": "a.b-c_d@a.b-" } ], "position": { @@ -2094,19 +2053,7 @@ "children": [ { "type": "text", - "value": "a.b-c_d@a.b_", - "position": { - "start": { - "line": 77, - "column": 1, - "offset": 1101 - }, - "end": { - "line": 77, - "column": 13, - "offset": 1113 - } - } + "value": "a.b-c_d@a.b_" } ], "position": { @@ -2231,19 +2178,7 @@ "children": [ { "type": "text", - "value": "Can’t end in an underscore followed by a period: aaa@a.b_.", - "position": { - "start": { - "line": 83, - "column": 1, - "offset": 1133 - }, - "end": { - "line": 83, - "column": 59, - "offset": 1191 - } - } + "value": "Can’t end in an underscore followed by a period: aaa@a.b_." } ], "position": { @@ -2326,6 +2261,58 @@ "offset": 1251 } } + }, + { + "type": "paragraph", + "children": [ + { + "type": "link", + "title": null, + "url": "https://google.com", + "children": [ + { + "type": "text", + "value": "https://google.com", + "position": { + "start": { + "line": 87, + "column": 2, + "offset": 1254 + }, + "end": { + "line": 87, + "column": 20, + "offset": 1272 + } + } + } + ], + "position": { + "start": { + "line": 87, + "column": 1, + "offset": 1253 + }, + "end": { + "line": 87, + "column": 41, + "offset": 1293 + } + } + } + ], + "position": { + "start": { + "line": 87, + "column": 1, + "offset": 1253 + }, + "end": { + "line": 87, + "column": 41, + "offset": 1293 + } + } } ], "position": { @@ -2335,9 +2322,9 @@ "offset": 0 }, "end": { - "line": 86, + "line": 88, "column": 1, - "offset": 1252 + "offset": 1294 } } } diff --git a/test/fixtures/tasklist/tree.json b/test/fixtures/tasklist/tree.json index 89589c9..d038554 100644 --- a/test/fixtures/tasklist/tree.json +++ b/test/fixtures/tasklist/tree.json @@ -21,8 +21,8 @@ "position": { "start": { "line": 1, - "column": 6, - "offset": 5 + "column": 7, + "offset": 6 }, "end": { "line": 1, @@ -35,8 +35,8 @@ "position": { "start": { "line": 1, - "column": 3, - "offset": 2 + "column": 7, + "offset": 6 }, "end": { "line": 1, @@ -73,8 +73,8 @@ "position": { "start": { "line": 2, - "column": 6, - "offset": 16 + "column": 7, + "offset": 17 }, "end": { "line": 2, @@ -87,8 +87,8 @@ "position": { "start": { "line": 2, - "column": 3, - "offset": 13 + "column": 7, + "offset": 17 }, "end": { "line": 2, diff --git a/test/index.js b/test/index.js index e39d27d..a4ae0e7 100644 --- a/test/index.js +++ b/test/index.js @@ -1,78 +1,86 @@ -'use strict' +/** + * @typedef {import('mdast').Root} Root + * @typedef {import('../index.js').Options} Options + */ -var fs = require('fs') -var path = require('path') -var test = require('tape') -var vfile = require('to-vfile') -var unified = require('unified') -var remark = require('remark') -var not = require('not') -var hidden = require('is-hidden') -var stringWidth = require('string-width') -var gfm = require('..') +import fs from 'node:fs' +import path from 'node:path' +import test from 'tape' +import {readSync} from 'to-vfile' +import {unified} from 'unified' +import {remark} from 'remark' +import {isHidden} from 'is-hidden' +import stringWidth from 'string-width' +import gfm from '../index.js' -test('gfm()', function (t) { - t.doesNotThrow(function () { +test('gfm()', (t) => { + t.doesNotThrow(() => { remark().use(gfm).freeze() }, 'should not throw if not passed options') - t.doesNotThrow(function () { + t.doesNotThrow(() => { unified().use(gfm).freeze() }, 'should not throw if without parser or compiler') t.end() }) -test('fixtures', function (t) { - var base = path.join(__dirname, 'fixtures') - var entries = fs.readdirSync(base).filter(not(hidden)) +test('fixtures', (t) => { + const base = path.join('test', 'fixtures') + const entries = fs.readdirSync(base) + let index = -1 - t.plan(entries.length) + while (++index < entries.length) { + if (isHidden(entries[index])) continue - entries.forEach(each) + const file = readSync(path.join(base, entries[index], 'input.md')) + const input = String(file.value) + const treePath = path.join(base, entries[index], 'tree.json') + /** @type {Options|undefined} */ + let config - function each(fixture) { - t.test(fixture, function (st) { - var file = vfile.readSync(path.join(base, fixture, 'input.md')) - var input = String(file.contents) - var outputPath = path.join(base, fixture, 'output.md') - var treePath = path.join(base, fixture, 'tree.json') - var configPath = path.join(base, fixture, 'config.json') - var config - var proc - var actual - var output - var expected + try { + config = JSON.parse( + String(fs.readFileSync(path.join(base, entries[index], 'config.json'))) + ) + } catch {} - try { - config = JSON.parse(fs.readFileSync(configPath)) - } catch (_) {} + if (entries[index] === 'table-string-length') { + config = {stringLength: stringWidth} + } - if (fixture === 'table-string-length') { - config = {stringLength: stringWidth} - } + const proc = remark().use(gfm, config).freeze() + const actual = proc.parse(file) + /** @type {Root} */ + let expected - proc = remark().use(gfm, config).freeze() - actual = proc.parse(file) + try { + expected = JSON.parse(String(fs.readFileSync(treePath))) + } catch { + // New fixture. + fs.writeFileSync(treePath, JSON.stringify(actual, null, 2) + '\n') + expected = actual + } - try { - expected = JSON.parse(fs.readFileSync(treePath)) - } catch (_) { - // New fixture. - fs.writeFileSync(treePath, JSON.stringify(actual, 0, 2) + '\n') - expected = actual - } + /** @type {string} */ + let output - try { - output = fs.readFileSync(outputPath, 'utf8') - } catch (_) { - output = input - } + try { + output = fs.readFileSync( + path.join(base, entries[index], 'output.md'), + 'utf8' + ) + } catch { + output = input + } - st.deepEqual(actual, expected, 'tree') - st.equal(String(proc.processSync(file)), output, 'process') - - st.end() - }) + t.deepEqual(actual, expected, entries[index] + ' (tree)') + t.equal( + String(proc.processSync(file)), + output, + entries[index] + ' (process)' + ) } + + t.end() }) diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..a93b9f9 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,16 @@ +{ + "include": ["test/**/*.js", "*.js"], + "compilerOptions": { + "target": "ES2020", + "lib": ["ES2020"], + "module": "ES2020", + "moduleResolution": "node", + "allowJs": true, + "checkJs": true, + "declaration": true, + "emitDeclarationOnly": true, + "allowSyntheticDefaultImports": true, + "skipLibCheck": true, + "strict": true + } +} diff --git a/types/index.d.ts b/types/index.d.ts deleted file mode 100644 index a37d302..0000000 --- a/types/index.d.ts +++ /dev/null @@ -1,39 +0,0 @@ -// TypeScript Version: 3.4 - -import {Plugin} from 'unified' - -declare namespace remarkGfm { - type Gfm = Plugin<[RemarkGfmOptions?]> - - interface RemarkGfmOptions { - /** - * Whether to support `~single tilde~` strikethrough. - * - * @defaultValue true - */ - singleTilde?: boolean - /** - * Create tables with a space between cell delimiters (`|`) and content. - * - * @defaultValue true - */ - tableCellPadding?: boolean - /** - * Align the delimiters (`|`) between table cells so that they all align - * nicely and form a grid. - * - * @defaultValue true - */ - tablePipeAlign?: boolean - /** - * Function to detect the length of a table cell. Used to align tables. - * - * @defaultValue s => s.length - */ - stringLength?: (s: string) => number - } -} - -declare const remarkGfm: remarkGfm.Gfm - -export = remarkGfm diff --git a/types/test.ts b/types/test.ts deleted file mode 100644 index b0c545b..0000000 --- a/types/test.ts +++ /dev/null @@ -1,22 +0,0 @@ -import unified = require('unified') -import gfm = require('remark-gfm') - -unified().use(gfm) -unified().use(gfm, {}) - -unified().use(gfm, {singleTilde: true}) -unified().use(gfm, {singleTilde: false}) -unified().use(gfm, {singleTilde: 1}) // $ExpectError - -unified().use(gfm, {tableCellPadding: true}) -unified().use(gfm, {tableCellPadding: false}) -unified().use(gfm, {tableCellPadding: []}) // $ExpectError - -unified().use(gfm, {tablePipeAlign: true}) -unified().use(gfm, {tablePipeAlign: false}) -unified().use(gfm, {tablePipeAlign: ''}) // $ExpectError - -unified().use(gfm, {stringLength: (_: string) => 0}) -unified().use(gfm, {stringLength: (_: string) => false}) // $ExpectError - -unified().use(gfm, {weird: true}) // $ExpectError diff --git a/types/tsconfig.json b/types/tsconfig.json deleted file mode 100644 index e883f1c..0000000 --- a/types/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "compilerOptions": { - "lib": ["es2015"], - "strict": true, - "baseUrl": ".", - "paths": { - "remark-gfm": ["index.d.ts"] - } - } -} diff --git a/types/tslint.json b/types/tslint.json deleted file mode 100644 index 70c4494..0000000 --- a/types/tslint.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "dtslint/dtslint.json", - "rules": { - "semicolon": false, - "whitespace": false - } -}