diff --git a/doc/getting-started.md b/doc/getting-started.md index 5ee6fad46..49c768e91 100644 --- a/doc/getting-started.md +++ b/doc/getting-started.md @@ -1,22 +1,23 @@ ![remark][logo] -# Getting Started +# Getting started -**remark** transforms markdown. It’s an ecosystem of [plugins][]. +**remark** transforms Markdown. +It’s an ecosystem of [plugins][]. If you get stuck, [issues][] and [Spectrum][] are good places to get help. It’s built on [unified][], make sure to read it and its [website][] too. -## Table of Contents +## Contents -* [Introduction](#introduction) +* [Intro](#intro) * [Command-line](#command-line) * [Using remark in a project](#using-remark-in-a-project) * [Programmatic usage](#programmatic-usage) -## Introduction +## Intro -Out of the box, **remark** transpiles markdown: markdown is given, reformatted, +Out of the box, **remark** transforms Markdown: Markdown is given, reformatted, and written: ```md @@ -43,7 +44,7 @@ But, much more can be done, [through plugins][plugins]. ## Command-line -**remark**’s CLI is a simple way to process markdown files from the +**remark**’s CLI is a simple way to process Markdown files from the command line. Its interface is provided by [**unified-args**][unified-args]. Install [`remark-cli`][cli] and dependencies (in this case a [linting @@ -148,7 +149,7 @@ Now from the command line we can run: npm test ``` -This will lint all markdown files when we test the project. +This will lint all Markdown files when we test the project. [`--frail`][frail] ensures the command fails if a code-style violation is found, and [`--quiet`][quiet] hides successful files from the report. @@ -208,7 +209,7 @@ remark() [unified]: https://github.com/unifiedjs/unified -[website]: https://unifiedjs.github.io +[website]: https://unifiedjs.com [unified-args]: https://github.com/unifiedjs/unified-args diff --git a/doc/plugins.md b/doc/plugins.md index c84443488..8d614b15c 100644 --- a/doc/plugins.md +++ b/doc/plugins.md @@ -5,15 +5,15 @@ **remark** is a Markdown processor powered by plugins part of the [unified][] [collective][]. -## Table of Contents +## Contents -* [List of Plugins](#list-of-plugins) -* [List of Presets](#list-of-presets) -* [List of Utilities](#list-of-utilities) +* [List of plugins](#list-of-plugins) +* [List of presets](#list-of-presets) +* [List of utilities](#list-of-utilities) * [Using plugins](#using-plugins) * [Creating plugins](#creating-plugins) -## List of Plugins +## List of plugins See [awesome remark][awesome] for the most awesome projects in the ecosystem. More plugins can be found on GitHub tagged with the @@ -22,12 +22,16 @@ More plugins can be found on GitHub tagged with the Have a good idea for a new plugin? See [Creating plugins][create] below. +* [`remark-a11y-emoji`](https://github.com/florianeckerstorfer/remark-a11y-emoji) + — accessible emoji * [`remark-abbr`](https://github.com/zestedesavoir/zmarkdown/tree/master/packages/remark-abbr#readme) — custom syntax for abbreviations (new node type, rehype compatible) +* [`remark-admonitions`](https://github.com/elviswolcott/remark-admonitions) + — support admonitions * [`remark-align`](https://github.com/zestedesavoir/zmarkdown/tree/master/packages/remark-align#readme) — custom syntax to align text or blocks (new node types, rehype compatible) * [`remark-attr`](https://github.com/arobase-che/remark-attr) - — custom syntax to add attributes to markdown + — custom syntax to add attributes to Markdown * [`remark-autolink-headings`](https://github.com/remarkjs/remark-autolink-headings) — add GitHub-style links to headings * [`remark-behead`](https://github.com/mrzmmr/remark-behead) @@ -41,18 +45,26 @@ See [Creating plugins][create] below. * [`remark-capitalize`](https://github.com/zeit/remark-capitalize) – transform all titles with [title.sh](https://github.com/zeit/title) +* [`remark-code-blocks`](https://github.com/mrzmmr/remark-code-blocks) + — select and store code blocks * [`remark-code-extra`](https://github.com/samlanning/remark-code-extra) - — Add to or transform the HTML output of code blocks (rehype compatible) + — add to or transform the HTML output of code blocks (rehype compatible) * [`remark-code-frontmatter`](https://github.com/samlanning/remark-code-frontmatter) - — Extract frontmatter from markdown code blocks + — extract frontmatter from code blocks +* [`remark-code-import`](https://github.com/kevin940726/remark-code-import) + — populate code blocks from files * [`remark-code-screenshot`](https://github.com/Swizec/remark-code-screenshot) – turn code blocks into carbon.now.sh screenshots +* [`remark-codesandbox`](https://github.com/kevin940726/remark-codesandbox) + – create CodeSandbox from code blocks * [`remark-collapse`](https://github.com/Rokt33r/remark-collapse) — make a section collapsible * [`remark-comment-config`](https://github.com/remarkjs/remark-comment-config) — configure remark with comments * [`remark-comments`](https://github.com/zestedesavoir/zmarkdown/tree/master/packages/remark-comments#readme) — custom syntax to ignore things +* [`remark-container`](https://github.com/zWingz/remark-container) + — add custom containers * [`remark-containers`](https://github.com/Nevenall/remark-containers) — add custom containers * [`remark-contributors`](https://github.com/remarkjs/remark-contributors) @@ -63,6 +75,8 @@ See [Creating plugins][create] below. — change links and images to references with separate definitions * [`remark-disable-tokenizers`](https://github.com/zestedesavoir/zmarkdown/tree/master/packages/remark-disable-tokenizers#readme) — turn some or all remark’s tokenizers on or off +* [`remark-dropcap`](https://github.com/brev/remark-dropcap) + — fancy and accessible drop caps * [`remark-embed-images`](https://github.com/remarkjs/remark-embed-images) — embed local images as base64-encoded data URIs * [`remark-emoji`](https://github.com/rhysd/remark-emoji) @@ -70,11 +84,15 @@ See [Creating plugins][create] below. * [`remark-emoji-to-gemoji`](https://github.com/jackycute/remark-emoji-to-gemoji) — transform emoji to Gemoji short-codes * [`remark-external-links`](https://github.com/remarkjs/remark-external-links) - — add target and rel attributes to external links + — add `target` and `rel` attributes to external links +* [`remark-extract-frontmatter`](https://github.com/mrzmmr/remark-extract-frontmatter) + — store front matter in vfiles * [`remark-first-heading`](https://github.com/laat/remark-first-heading) — replace the first heading in a document * [`remark-fix-guillemets`](https://github.com/zestedesavoir/zmarkdown/tree/master/packages/remark-fix-guillemets#readme) — support ASCII guillements (`<<`, `>>`) mapping them to HTML +* [`remark-footnotes`](https://github.com/remarkjs/remark-footnotes) + – support pandoc footnotes * [`remark-frontmatter`](https://github.com/remarkjs/remark-frontmatter) – support frontmatter (yaml, toml, and more) * [`remark-gemoji`](https://github.com/remarkjs/remark-gemoji) @@ -93,13 +111,15 @@ See [Creating plugins][create] below. — custom syntax to describe tables (rehype compatible) * [`remark-graphviz`](https://github.com/temando/remark-graphviz) — transform [graphviz](https://www.graphviz.org) dot graphs to SVG +* [`remark-heading-id`](https://github.com/imcuttle/remark-heading-id) + — custom heading id support `{#custom-id}` * [`remark-heading-gap`](https://github.com/remarkjs/remark-heading-gap) - — stringify with more blank lines between headings + — serialize with more blank lines between headings * [`remark-highlight.js`](https://github.com/remarkjs/remark-highlight.js) — highlight code blocks with [highlight.js](https://github.com/isagalaev/highlight.js) (rehype compatible) * [`remark-html`](https://github.com/remarkjs/remark-html) - — stringify Markdown as HTML + — serialize Markdown as HTML * [`remark-html-emoji-image`](https://github.com/jackycute/remark-html-emoji-image) — transform emoji to images * [`remark-html-katex`](https://github.com/Rokt33r/remark-math/tree/master/packages/remark-html-katex#readme) @@ -126,7 +146,7 @@ See [Creating plugins][create] below. * [`remark-macro`](https://github.com/dimerapp/remark-macro) — support for block macros (new node types, rehype compatible) * [`remark-man`](https://github.com/remarkjs/remark-man) - — stringify Markdown as man pages (roff) + — serialize Markdown as man pages (roff) * [`remark-math`](https://github.com/Rokt33r/remark-math) — custom syntax for math (new node types, rehype compatible) * [`remark-mermaid`](https://github.com/temando/remark-mermaid) @@ -140,15 +160,21 @@ See [Creating plugins][create] below. (rehype compatible) * [`remark-normalize-headings`](https://github.com/remarkjs/remark-normalize-headings) — make sure at most one top-level heading exists +* [`remark-numbered-footnote-labels`](https://github.com/jackfletch/remark-numbered-footnote-labels) + — label footnotes with numbers +* [`remark-oembed`](https://github.com/agentofuser/remark-oembed) + — transform URLs for youtube, twitter, etc. embeds * [`remark-openapi`](https://github.com/temando/remark-openapi) — transform links to local or remote OpenAPI definitions to tables +* [`remark-package-dependencies`](https://github.com/unlight/remark-package-dependencies) + — inject your dependencies * [`remark-parse-yaml`](https://github.com/landakram/remark-parse-yaml) — parse YAML nodes and expose their value as `parsedValue` * [`remark-ping`](https://github.com/zestedesavoir/zmarkdown/tree/master/packages/remark-ping#readme) — custom syntax for mentions with configurable existence check (new node type, rehype compatible) * [`remark-react`](https://github.com/remarkjs/remark-react) - — “stringify” Markdown as [React](https://github.com/facebook/react) + — compile Markdown to [React](https://github.com/facebook/react) * [`remark-react-codemirror`](https://github.com/craftzdog/remark-react-codemirror) — highlight code blocks for **remark-react** with [CodeMirror](https://codemirror.net) * [`remark-react-lowlight`](https://github.com/inlinestyle/remark-react-lowlight) @@ -173,8 +199,16 @@ See [Creating plugins][create] below. rehype compatible) * [`remark-shortcodes`](https://github.com/djm/remark-shortcodes) — custom syntax Wordpress- and Hugo-like shortcodes (new node type) +* [`remark-simple-plantuml`](https://github.com/akebifiky/remark-simple-plantuml) + — convert PlantUML code to images * [`remark-slug`](https://github.com/remarkjs/remark-slug) — add anchors to headings using GitHub’s algorithm +* [`remark-smartypants`](https://github.com/silvenon/remark-smartypants) + — SmartyPants +* [`remark-smcat`](https://github.com/shedali/remark-smcat) + — state machine cat +* [`remark-sources`](https://github.com/unlight/remark-sources) + — insert source code * [`remark-strip-badges`](https://github.com/remarkjs/remark-strip-badges) — remove badges (such as `shields.io`) * [`remark-strip-html`](https://github.com/craftzdog/remark-strip-html) @@ -195,6 +229,12 @@ See [Creating plugins][create] below. — highlight code blocks in Markdown files using [Tree-sitter](https://tree-sitter.github.io/tree-sitter/) (rehype compatible) +* [`remark-truncate-links`](https://github.com/GaiAma/Coding4GaiAma/tree/master/packages/remark-truncate-links) + — truncate/shorten urls not manually named +* [`remark-twemoji`](https://github.com/madiodio/remark-twemoji) + — replace unicode emoji with [Twemoji](https://github.com/twitter/twemoji) +* [`remark-typescript`](https://github.com/trevorblades/remark-typescript) + — transform TypeScript code blocks to JavaScript * [`remark-unlink`](https://github.com/remarkjs/remark-unlink) — remove all links, references, and definitions * [`remark-unwrap-images`](https://github.com/remarkjs/remark-unwrap-images) @@ -205,19 +245,21 @@ See [Creating plugins][create] below. — turn bolds, italics, and code into UTF-8 special characters * [`remark-validate-links`](https://github.com/remarkjs/remark-validate-links) — check links to headings and files +* [`remark-variables`](https://github.com/mrzmmr/remark-variables) + — variables! * [`remark-vdom`](https://github.com/remarkjs/remark-vdom) - — “stringify” Markdown as [VDOM](https://github.com/Matt-Esch/virtual-dom/) + — compile Markdown to [VDOM](https://github.com/Matt-Esch/virtual-dom/) * [`remark-wiki-link`](https://github.com/landakram/remark-wiki-link) — custom syntax for wiki links (rehype compatible) * [`remark-yaml-config`](https://github.com/remarkjs/remark-yaml-config) — configure remark with YAML -## List of Presets +## List of presets See [npm search][npm-preset-search] or [github search][github-preset-search] for available and often inspirational presets. -## List of Utilities +## List of utilities See [**mdast**][mdast-util] for a list of utilities for working with the syntax tree. @@ -275,7 +317,7 @@ use `unist-util-`, and if it works with virtual files, use `vfile-`. [spectrum]: https://spectrum.chat/unified/remark -[guide]: https://unifiedjs.github.io/create-a-plugin.html +[guide]: https://unifiedjs.com/learn/guide/create-a-plugin/ [npm-preset-search]: https://www.npmjs.com/search?q=remark-preset diff --git a/package.json b/package.json index d9601abe3..f60faa780 100644 --- a/package.json +++ b/package.json @@ -14,25 +14,25 @@ "browserify": "^16.0.0", "camelcase": "^5.0.0", "clone": "^2.0.0", - "dtslint": "^2.0.0", - "execa": "^3.0.0", + "dtslint": "^3.0.0", + "execa": "^4.0.0", "lerna": "^3.0.0", - "mdast-util-assert": "^2.0.0", - "mdast-util-compact": "^1.0.0", - "mdast-zone": "^3.0.0", - "nyc": "^14.0.0", - "prettier": "^1.0.0", + "mdast-util-assert": "^3.0.0", + "mdast-util-compact": "^2.0.0", + "mdast-zone": "^4.0.0", + "nyc": "^15.0.0", + "prettier": "^2.0.0", "remark-preset-wooorm": "^6.0.0", "tape": "^4.0.0", "tinyify": "^2.0.0", "typescript": "^3.0.0", - "unified": "^8.0.0", + "unified": "^9.0.0", "unist-builder": "^2.0.0", - "unist-util-remove-position": "^1.0.0", + "unist-util-remove-position": "^2.0.0", "unist-util-visit": "^2.0.0", "vfile": "^4.0.0", "wcwidth": "^1.0.0", - "xo": "^0.25.0" + "xo": "^0.28.0" }, "scripts": { "postinstall": "lerna bootstrap --no-ci", @@ -64,9 +64,11 @@ "prettier": true, "esnext": false, "rules": { + "unicorn/string-content": "off", "unicorn/prefer-type-error": "off", "unicorn/prefer-reflect-apply": "off", "unicorn/prefer-includes": "off", + "complexity": "off", "eqeqeq": [ "error", "always", @@ -76,17 +78,23 @@ ], "guard-for-in": "off", "max-depth": "off", - "complexity": "off", "no-eq-null": "off" }, "ignores": [ + "**/types", "remark.js" ] }, "remarkConfig": { "plugins": [ "./script/list-of-methods", - "preset-wooorm" + "preset-wooorm", + [ + "toc", + { + "heading": "contents" + } + ] ] } } diff --git a/packages/remark-cli/package.json b/packages/remark-cli/package.json index 6274151b8..0e2bb5a91 100644 --- a/packages/remark-cli/package.json +++ b/packages/remark-cli/package.json @@ -1,7 +1,7 @@ { "name": "remark-cli", - "version": "7.0.1", - "description": "CLI to process Markdown with remark using plugins", + "version": "8.0.0", + "description": "CLI to process Markdown with remark", "license": "MIT", "keywords": [ "unified", @@ -30,8 +30,8 @@ ], "dependencies": { "markdown-extensions": "^1.1.0", - "remark": "^11.0.0", - "unified-args": "^7.0.0" + "remark": "^12.0.0", + "unified-args": "^8.0.0" }, "scripts": { "test": "tape test.js" diff --git a/packages/remark-cli/readme.md b/packages/remark-cli/readme.md index 576f24a3f..e26406ac9 100644 --- a/packages/remark-cli/readme.md +++ b/packages/remark-cli/readme.md @@ -22,35 +22,41 @@ Command line interface for [**remark**][remark]. - - - - + + + - +
- -

🥇 - ZEIT +
+ Gatsby
🥇

+
- -

🥇 - Gatsby +
+ ZEIT
🥇

+ +
- -

🥇 - Netlify +
+ Netlify
🥇

+ +
- -

- Holloway +
+ Holloway


+ +
+ ThemeIsle
🥉

+
+ + BoostIO
🥉

+ +




- You? + You?
-[**Read more about the unified collective on Medium »**][announcement] - ## Install [npm][]: @@ -65,8 +71,8 @@ npm install remark-cli # Add a table of contents to `readme.md` $ remark readme.md --use toc --output -# Lint markdown files in the current directory -# according to the markdown style guide. +# Lint Markdown files in the current directory +# according to the Markdown style guide. $ remark . --use preset-lint-markdown-style-guide ``` @@ -137,8 +143,8 @@ Ideas for new plugins and tools can be posted in [`remarkjs/ideas`][ideas]. A curated list of awesome remark resources can be found in [**awesome remark**][awesome]. -This project has a [Code of Conduct][coc]. -By interacting with this repository, organisation, or community you agree to +This project has a [code of conduct][coc]. +By interacting with this repository, organization, or community you agree to abide by its terms. ## License @@ -165,7 +171,7 @@ abide by its terms. [collective]: https://opencollective.com/unified -[chat-badge]: https://img.shields.io/badge/join%20the%20community-on%20spectrum-7b16ff.svg +[chat-badge]: https://img.shields.io/badge/chat-spectrum-7b16ff.svg [chat]: https://spectrum.chat/unified/remark @@ -199,8 +205,6 @@ abide by its terms. [unified-args]: https://github.com/unifiedjs/unified-args#cli -[announcement]: https://medium.com/unifiedjs/collectively-evolving-through-crowdsourcing-22c359ea95cc - [xss]: https://en.wikipedia.org/wiki/Cross-site_scripting [rehype]: https://github.com/rehypejs/rehype diff --git a/packages/remark-cli/test.js b/packages/remark-cli/test.js index 17db557a6..163ad8095 100644 --- a/packages/remark-cli/test.js +++ b/packages/remark-cli/test.js @@ -1,55 +1,53 @@ 'use strict' var path = require('path') - -/* eslint-disable import/no-extraneous-dependencies */ var execa = require('execa') var test = require('tape') -/* eslint-enable import/no-extraneous-dependencies */ var join = path.join -test('remark-cli', function(t) { +test('remark-cli', function (t) { t.plan(2) - t.test('should show help on `--help`', function(st) { + t.test('should show help on `--help`', function (st) { var bin = join('packages', 'remark-cli', 'cli.js') st.plan(1) - execa(bin, ['--help']).then(function(result) { + execa(bin, ['--help']).then(function (result) { st.equal( result.stdout, [ 'Usage: remark [options] [path | glob ...]', '', - ' CLI to process Markdown with remark using plugins', + ' CLI to process Markdown with remark', '', 'Options:', '', - ' -h --help output usage information', - ' -v --version output version number', - ' -o --output [path] specify output location', - ' -r --rc-path specify configuration file', - ' -i --ignore-path specify ignore file', - ' -s --setting specify settings', - ' -e --ext specify extensions', - ' -u --use use plugins', - ' -w --watch watch for changes and reprocess', - ' -q --quiet output only warnings and errors', - ' -S --silent output only errors', - ' -f --frail exit with 1 on warnings', - ' -t --tree specify input and output as syntax tree', - ' --report specify reporter', - ' --file-path specify path to process as', - ' --ignore-pattern specify ignore patterns', - ' --tree-in specify input as syntax tree', - ' --tree-out output syntax tree', - ' --inspect output formatted syntax tree', - ' --[no-]stdout specify writing to stdout (on by default)', - ' --[no-]color specify color in report (on by default)', - ' --[no-]config search for configuration files (on by default)', - ' --[no-]ignore search for ignore files (on by default)', + ' -h --help output usage information', + ' -v --version output version number', + ' -o --output [path] specify output location', + ' -r --rc-path specify configuration file', + ' -i --ignore-path specify ignore file', + ' -s --setting specify settings', + ' -e --ext specify extensions', + ' -u --use use plugins', + ' -w --watch watch for changes and reprocess', + ' -q --quiet output only warnings and errors', + ' -S --silent output only errors', + ' -f --frail exit with 1 on warnings', + ' -t --tree specify input and output as syntax tree', + ' --report specify reporter', + ' --file-path specify path to process as', + ' --ignore-path-resolve-from dir|cwd resolve patterns in `ignore-path` from its directory or cwd', + ' --ignore-pattern specify ignore patterns', + ' --tree-in specify input as syntax tree', + ' --tree-out output syntax tree', + ' --inspect output formatted syntax tree', + ' --[no-]stdout specify writing to stdout (on by default)', + ' --[no-]color specify color in report (on by default)', + ' --[no-]config search for configuration files (on by default)', + ' --[no-]ignore search for ignore files (on by default)', '', 'Examples:', '', @@ -67,12 +65,12 @@ test('remark-cli', function(t) { }) }) - t.test('should show version on `--version`', function(st) { + t.test('should show version on `--version`', function (st) { var bin = join('packages', 'remark-cli', 'cli.js') st.plan(2) - execa(bin, ['--version']).then(function(result) { + execa(bin, ['--version']).then(function (result) { st.ok( /remark: \d+\.\d+\.\d+/.test(result.stdout), 'should include remark version' diff --git a/packages/remark-parse/lib/defaults.js b/packages/remark-parse/lib/defaults.js index 7776e3c08..76443a57a 100644 --- a/packages/remark-parse/lib/defaults.js +++ b/packages/remark-parse/lib/defaults.js @@ -4,7 +4,6 @@ module.exports = { position: true, gfm: true, commonmark: false, - footnotes: false, pedantic: false, blocks: require('./block-elements') } diff --git a/packages/remark-parse/lib/locate/email.js b/packages/remark-parse/lib/locate/email.js new file mode 100644 index 000000000..bd3105d6b --- /dev/null +++ b/packages/remark-parse/lib/locate/email.js @@ -0,0 +1,51 @@ +'use strict' + +var decimal = require('is-decimal') +var alphabetical = require('is-alphabetical') + +var plusSign = 43 // '+' +var dash = 45 // '-' +var dot = 46 // '.' +var underscore = 95 // '_' + +module.exports = locate + +// See: +function locate(value, fromIndex) { + var self = this + var at + var position + + if (!this.options.gfm) { + return -1 + } + + at = value.indexOf('@', fromIndex) + + if (at === -1) { + return -1 + } + + position = at + + if (position === fromIndex || !isGfmAtext(value.charCodeAt(position - 1))) { + return locate.call(self, value, at + 1) + } + + while (position > fromIndex && isGfmAtext(value.charCodeAt(position - 1))) { + position-- + } + + return position +} + +function isGfmAtext(code) { + return ( + decimal(code) || + alphabetical(code) || + code === plusSign || + code === dash || + code === dot || + code === underscore + ) +} diff --git a/packages/remark-parse/lib/locate/url.js b/packages/remark-parse/lib/locate/url.js index e5bf5bfa4..c2cc1beba 100644 --- a/packages/remark-parse/lib/locate/url.js +++ b/packages/remark-parse/lib/locate/url.js @@ -2,22 +2,25 @@ module.exports = locate -var protocols = ['https://', 'http://', 'mailto:'] +var values = ['www.', 'http://', 'https://'] function locate(value, fromIndex) { - var length = protocols.length - var index = -1 var min = -1 + var index + var length var position if (!this.options.gfm) { - return -1 + return min } + length = values.length + index = -1 + while (++index < length) { - position = value.indexOf(protocols[index], fromIndex) + position = value.indexOf(values[index], fromIndex) - if (position !== -1 && (position < min || min === -1)) { + if (position !== -1 && (min === -1 || position < min)) { min = position } } diff --git a/packages/remark-parse/lib/parser.js b/packages/remark-parse/lib/parser.js index 4add90e01..34b544da6 100644 --- a/packages/remark-parse/lib/parser.js +++ b/packages/remark-parse/lib/parser.js @@ -55,8 +55,7 @@ proto.interruptParagraph = [ ['blockquote'], ['html'], ['setextHeading', {commonmark: false}], - ['definition', {commonmark: false}], - ['footnote', {commonmark: false}] + ['definition', {commonmark: false}] ] // Nodes that can interupt a list: @@ -71,8 +70,7 @@ proto.interruptList = [ ['atxHeading', {pedantic: false}], ['fencedCode', {pedantic: false}], ['thematicBreak', {pedantic: false}], - ['definition', {commonmark: false}], - ['footnote', {commonmark: false}] + ['definition', {commonmark: false}] ] // Nodes that can interupt a blockquote: @@ -91,13 +89,12 @@ proto.interruptBlockquote = [ ['thematicBreak', {commonmark: true}], ['html', {commonmark: true}], ['list', {commonmark: true}], - ['definition', {commonmark: false}], - ['footnote', {commonmark: false}] + ['definition', {commonmark: false}] ] // Handlers. proto.blockTokenizers = { - newline: require('./tokenize/newline'), + blankLine: require('./tokenize/blank-line'), indentedCode: require('./tokenize/code-indented'), fencedCode: require('./tokenize/code-fenced'), blockquote: require('./tokenize/blockquote'), @@ -106,7 +103,6 @@ proto.blockTokenizers = { list: require('./tokenize/list'), setextHeading: require('./tokenize/heading-setext'), html: require('./tokenize/html-block'), - footnote: require('./tokenize/footnote-definition'), definition: require('./tokenize/definition'), table: require('./tokenize/table'), paragraph: require('./tokenize/paragraph') @@ -116,6 +112,7 @@ proto.inlineTokenizers = { escape: require('./tokenize/escape'), autoLink: require('./tokenize/auto-link'), url: require('./tokenize/url'), + email: require('./tokenize/email'), html: require('./tokenize/html-inline'), link: require('./tokenize/link'), reference: require('./tokenize/reference'), diff --git a/packages/remark-parse/lib/tokenize/blank-line.js b/packages/remark-parse/lib/tokenize/blank-line.js new file mode 100644 index 000000000..387fd45a5 --- /dev/null +++ b/packages/remark-parse/lib/tokenize/blank-line.js @@ -0,0 +1,43 @@ +'use strict' + +// A line containing no characters, or a line containing only spaces (U+0020) or +// tabs (U+0009), is called a blank line. +// See . +var reBlankLine = /^[ \t]*(\n|$)/ + +// Note that though blank lines play a special role in lists to determine +// whether the list is tight or loose +// (), it’s done by the list +// tokenizer and this blank line tokenizer does not have to be responsible for +// that. +// Therefore, configs such as `blankLine.notInList` do not have to be set here. +module.exports = blankLine + +function blankLine(eat, value, silent) { + var match + var subvalue = '' + var index = 0 + var length = value.length + + while (index < length) { + match = reBlankLine.exec(value.slice(index)) + + if (match == null) { + break + } + + index += match[0].length + subvalue += match[0] + } + + if (subvalue === '') { + return + } + + /* istanbul ignore if - never used (yet) */ + if (silent) { + return true + } + + eat(subvalue) +} diff --git a/packages/remark-parse/lib/tokenize/email.js b/packages/remark-parse/lib/tokenize/email.js new file mode 100644 index 000000000..08a34c91f --- /dev/null +++ b/packages/remark-parse/lib/tokenize/email.js @@ -0,0 +1,114 @@ +'use strict' + +var decode = require('parse-entities') +var decimal = require('is-decimal') +var alphabetical = require('is-alphabetical') +var locate = require('../locate/email') + +module.exports = email +email.locator = locate +email.notInLink = true + +var plusSign = 43 // '+' +var dash = 45 // '-' +var dot = 46 // '.' +var atSign = 64 // '@' +var underscore = 95 // '_' + +function email(eat, value, silent) { + var self = this + var gfm = self.options.gfm + var tokenizers = self.inlineTokenizers + var index = 0 + var length = value.length + var firstDot = -1 + var code + var content + var children + var exit + + if (!gfm) { + return + } + + code = value.charCodeAt(index) + + while ( + decimal(code) || + alphabetical(code) || + code === plusSign || + code === dash || + code === dot || + code === underscore + ) { + code = value.charCodeAt(++index) + } + + if (index === 0) { + return + } + + if (code !== atSign) { + return + } + + index++ + + while (index < length) { + code = value.charCodeAt(index) + + if ( + decimal(code) || + alphabetical(code) || + code === dash || + code === dot || + code === underscore + ) { + index++ + + if (firstDot === -1 && code === dot) { + firstDot = index + } + + continue + } + + break + } + + if ( + firstDot === -1 || + firstDot === index || + code === dash || + code === underscore + ) { + return + } + + if (code === dot) { + index-- + } + + content = value.slice(0, index) + + /* istanbul ignore if - never used (yet) */ + if (silent) { + return true + } + + exit = self.enterLink() + + // Temporarily remove all tokenizers except text in url. + self.inlineTokenizers = {text: tokenizers.text} + children = self.tokenizeInline(content, eat.now()) + self.inlineTokenizers = tokenizers + + exit() + + return eat(content)({ + type: 'link', + title: null, + url: 'mailto:' + decode(content, {nonTerminated: false}), + children: children + }) +} diff --git a/packages/remark-parse/lib/tokenize/emphasis.js b/packages/remark-parse/lib/tokenize/emphasis.js index 2cbfd314a..9484b5c37 100644 --- a/packages/remark-parse/lib/tokenize/emphasis.js +++ b/packages/remark-parse/lib/tokenize/emphasis.js @@ -22,7 +22,7 @@ function emphasis(eat, value, silent) { var queue var subvalue var length - var prev + var previous if (character !== asterisk && character !== underscore) { return @@ -41,14 +41,14 @@ function emphasis(eat, value, silent) { } while (index < length) { - prev = character + previous = character character = value.charAt(index) - if (character === marker && (!pedantic || !whitespace(prev))) { + if (character === marker && (!pedantic || !whitespace(previous))) { character = value.charAt(++index) if (character !== marker) { - if (!trim(queue) || prev === marker) { + if (!trim(queue) || previous === marker) { return } diff --git a/packages/remark-parse/lib/tokenize/footnote-definition.js b/packages/remark-parse/lib/tokenize/footnote-definition.js deleted file mode 100644 index 62d8ce7b2..000000000 --- a/packages/remark-parse/lib/tokenize/footnote-definition.js +++ /dev/null @@ -1,186 +0,0 @@ -'use strict' - -var whitespace = require('is-whitespace-character') -var normalize = require('../util/normalize') - -module.exports = footnoteDefinition -footnoteDefinition.notInList = true -footnoteDefinition.notInBlock = true - -var backslash = '\\' -var lineFeed = '\n' -var tab = '\t' -var space = ' ' -var leftSquareBracket = '[' -var rightSquareBracket = ']' -var caret = '^' -var colon = ':' - -var EXPRESSION_INITIAL_TAB = /^( {4}|\t)?/gm - -function footnoteDefinition(eat, value, silent) { - var self = this - var offsets = self.offset - var index - var length - var subvalue - var now - var currentLine - var content - var queue - var subqueue - var character - var identifier - var add - var exit - - if (!self.options.footnotes) { - return - } - - index = 0 - length = value.length - subvalue = '' - now = eat.now() - currentLine = now.line - - while (index < length) { - character = value.charAt(index) - - if (!whitespace(character)) { - break - } - - subvalue += character - index++ - } - - if ( - value.charAt(index) !== leftSquareBracket || - value.charAt(index + 1) !== caret - ) { - return - } - - subvalue += leftSquareBracket + caret - index = subvalue.length - queue = '' - - while (index < length) { - character = value.charAt(index) - - if (character === rightSquareBracket) { - break - } else if (character === backslash) { - queue += character - index++ - character = value.charAt(index) - } - - queue += character - index++ - } - - if ( - !queue || - value.charAt(index) !== rightSquareBracket || - value.charAt(index + 1) !== colon - ) { - return - } - - if (silent) { - return true - } - - identifier = queue - subvalue += queue + rightSquareBracket + colon - index = subvalue.length - - while (index < length) { - character = value.charAt(index) - - if (character !== tab && character !== space) { - break - } - - subvalue += character - index++ - } - - now.column += subvalue.length - now.offset += subvalue.length - queue = '' - content = '' - subqueue = '' - - while (index < length) { - character = value.charAt(index) - - if (character === lineFeed) { - subqueue = character - index++ - - while (index < length) { - character = value.charAt(index) - - if (character !== lineFeed) { - break - } - - subqueue += character - index++ - } - - queue += subqueue - subqueue = '' - - while (index < length) { - character = value.charAt(index) - - if (character !== space) { - break - } - - subqueue += character - index++ - } - - if (subqueue.length === 0) { - break - } - - queue += subqueue - } - - if (queue) { - content += queue - queue = '' - } - - content += character - index++ - } - - subvalue += content - - content = content.replace(EXPRESSION_INITIAL_TAB, function(line) { - offsets[currentLine] = (offsets[currentLine] || 0) + line.length - currentLine++ - - return '' - }) - - add = eat(subvalue) - - exit = self.enterBlock() - content = self.tokenizeBlock(content, now) - exit() - - return add({ - type: 'footnoteDefinition', - identifier: normalize(identifier), - label: identifier, - children: content - }) -} diff --git a/packages/remark-parse/lib/tokenize/html-block.js b/packages/remark-parse/lib/tokenize/html-block.js index 149a71860..33571f630 100644 --- a/packages/remark-parse/lib/tokenize/html-block.js +++ b/packages/remark-parse/lib/tokenize/html-block.js @@ -18,7 +18,7 @@ var instructionCloseExpression = /\?>/ var directiveOpenExpression = /^/ var cdataOpenExpression = /^/ +var cdataCloseExpression = /]]>/ var elementCloseExpression = /^$/ var otherElementOpenExpression = new RegExp(openCloseTag.source + '\\s*$') diff --git a/packages/remark-parse/lib/tokenize/link.js b/packages/remark-parse/lib/tokenize/link.js index ab4d3fa3c..e9a0f6dde 100644 --- a/packages/remark-parse/lib/tokenize/link.js +++ b/packages/remark-parse/lib/tokenize/link.js @@ -106,20 +106,6 @@ function link(eat, value, silent) { if (depth) { depth-- } else { - // Allow white-space between content and url in GFM mode. - if (!pedantic) { - while (index < length) { - character = value.charAt(index + 1) - - if (!whitespace(character)) { - break - } - - subqueue += character - index++ - } - } - if (value.charAt(index + 1) !== leftParenthesis) { return } diff --git a/packages/remark-parse/lib/tokenize/list.js b/packages/remark-parse/lib/tokenize/list.js index 3eef0d668..033c46a19 100644 --- a/packages/remark-parse/lib/tokenize/list.js +++ b/packages/remark-parse/lib/tokenize/list.js @@ -22,7 +22,7 @@ var lowercaseX = 'x' var tabSize = 4 var looseListItemExpression = /\n\n(?!\s*$)/ -var taskItemExpression = /^\[([ \t]|x|X)][ \t]/ +var taskItemExpression = /^\[([ X\tx])][ \t]/ var bulletExpression = /^([ \t]*)([*+-]|\d+[.)])( {1,4}(?! )| |\t|$|(?=\n))([^\n]*)/ var pedanticBulletExpression = /^([ \t]*)([*+-]|\d+[.)])([ \t]+)/ var initialIndentExpression = /^( {1,4}|\t)?/gm @@ -47,7 +47,7 @@ function list(eat, value, silent) { var currentMarker var content var line - var prevEmpty + var previousEmpty var empty var items var allLines @@ -262,7 +262,7 @@ function list(eat, value, silent) { } } - prevEmpty = empty + previousEmpty = empty empty = !prefixed && !trim(content).length if (indented && item) { @@ -286,13 +286,13 @@ function list(eat, value, silent) { allLines = allLines.concat(emptyLines, line) emptyLines = [] } else if (empty) { - if (prevEmpty && !commonmark) { + if (previousEmpty && !commonmark) { break } emptyLines.push(line) } else { - if (prevEmpty) { + if (previousEmpty) { break } diff --git a/packages/remark-parse/lib/tokenize/newline.js b/packages/remark-parse/lib/tokenize/newline.js deleted file mode 100644 index 680020cf0..000000000 --- a/packages/remark-parse/lib/tokenize/newline.js +++ /dev/null @@ -1,48 +0,0 @@ -'use strict' - -var whitespace = require('is-whitespace-character') - -module.exports = newline - -var lineFeed = '\n' - -function newline(eat, value, silent) { - var character = value.charAt(0) - var length - var subvalue - var queue - var index - - if (character !== lineFeed) { - return - } - - /* istanbul ignore if - never used (yet) */ - if (silent) { - return true - } - - index = 1 - length = value.length - subvalue = character - queue = '' - - while (index < length) { - character = value.charAt(index) - - if (!whitespace(character)) { - break - } - - queue += character - - if (character === lineFeed) { - subvalue += queue - queue = '' - } - - index++ - } - - eat(subvalue) -} diff --git a/packages/remark-parse/lib/tokenize/paragraph.js b/packages/remark-parse/lib/tokenize/paragraph.js index 13db0ff40..c367d12a4 100644 --- a/packages/remark-parse/lib/tokenize/paragraph.js +++ b/packages/remark-parse/lib/tokenize/paragraph.js @@ -96,12 +96,6 @@ function paragraph(eat, value, silent) { subvalue = value.slice(0, index) - if (trim(subvalue) === '') { - eat(subvalue) - - return null - } - /* istanbul ignore if - never used (yet) */ if (silent) { return true diff --git a/packages/remark-parse/lib/tokenize/reference.js b/packages/remark-parse/lib/tokenize/reference.js index fdf753f73..420b04d29 100644 --- a/packages/remark-parse/lib/tokenize/reference.js +++ b/packages/remark-parse/lib/tokenize/reference.js @@ -9,21 +9,17 @@ reference.locator = locate var link = 'link' var image = 'image' -var footnote = 'footnote' var shortcut = 'shortcut' var collapsed = 'collapsed' var full = 'full' -var space = ' ' var exclamationMark = '!' var leftSquareBracket = '[' var backslash = '\\' var rightSquareBracket = ']' -var caret = '^' function reference(eat, value, silent) { var self = this var commonmark = self.options.commonmark - var footnotes = self.options.footnotes var character = value.charAt(0) var index = 0 var length = value.length @@ -55,19 +51,6 @@ function reference(eat, value, silent) { intro += character queue = '' - // Check whether we’re eating a footnote. - if (footnotes && value.charAt(index) === caret) { - // Exit if `![^` is found, so the `!` will be seen as text after this, - // and we’ll enter this function again when `[^` is found. - if (type === image) { - return - } - - intro += caret - index++ - type = footnote - } - // Eat the text. depth = 0 @@ -124,13 +107,7 @@ function reference(eat, value, silent) { character = value.charAt(index) - // Inline footnotes cannot have a label. - // If footnotes are enabled, link labels cannot start with a caret. - if ( - type !== footnote && - character === leftSquareBracket && - (!footnotes || value.charAt(index + 1) !== caret) - ) { + if (character === leftSquareBracket) { identifier = '' queue += character index++ @@ -187,13 +164,6 @@ function reference(eat, value, silent) { return true } - if (type === footnote && content.indexOf(space) !== -1) { - return eat(subvalue)({ - type: footnote, - children: this.tokenizeInline(content, eat.now()) - }) - } - now = eat.now() now.column += intro.length now.offset += intro.length @@ -202,18 +172,15 @@ function reference(eat, value, silent) { node = { type: type + 'Reference', identifier: normalize(identifier), - label: identifier - } - - if (type === link || type === image) { - node.referenceType = referenceType + label: identifier, + referenceType: referenceType } if (type === link) { exit = self.enterLink() node.children = self.tokenizeInline(content, now) exit() - } else if (type === image) { + } else { node.alt = self.decode.raw(self.unescape(content), now) || null } diff --git a/packages/remark-parse/lib/tokenize/strong.js b/packages/remark-parse/lib/tokenize/strong.js index 3e36462a7..62d552486 100644 --- a/packages/remark-parse/lib/tokenize/strong.js +++ b/packages/remark-parse/lib/tokenize/strong.js @@ -21,7 +21,7 @@ function strong(eat, value, silent) { var queue var subvalue var length - var prev + var previous if ( (character !== asterisk && character !== underscore) || @@ -43,13 +43,13 @@ function strong(eat, value, silent) { } while (index < length) { - prev = character + previous = character character = value.charAt(index) if ( character === marker && value.charAt(index + 1) === marker && - (!pedantic || !whitespace(prev)) + (!pedantic || !whitespace(previous)) ) { character = value.charAt(index + 2) diff --git a/packages/remark-parse/lib/tokenize/table.js b/packages/remark-parse/lib/tokenize/table.js index ba165d78e..4a3589216 100644 --- a/packages/remark-parse/lib/tokenize/table.js +++ b/packages/remark-parse/lib/tokenize/table.js @@ -183,7 +183,7 @@ function table(eat, value, silent) { if (queue.length > 1) { if (character) { - subvalue += queue.slice(0, queue.length - 1) + subvalue += queue.slice(0, -1) queue = queue.charAt(queue.length - 1) } else { subvalue += queue diff --git a/packages/remark-parse/lib/tokenize/url.js b/packages/remark-parse/lib/tokenize/url.js index 92d1c6229..30077cace 100644 --- a/packages/remark-parse/lib/tokenize/url.js +++ b/packages/remark-parse/lib/tokenize/url.js @@ -1,6 +1,9 @@ 'use strict' +var ccount = require('ccount') var decode = require('parse-entities') +var decimal = require('is-decimal') +var alphabetical = require('is-alphabetical') var whitespace = require('is-whitespace-character') var locate = require('../locate/url') @@ -8,146 +11,200 @@ module.exports = url url.locator = locate url.notInLink = true -var quotationMark = '"' -var apostrophe = "'" -var leftParenthesis = '(' -var rightParenthesis = ')' -var comma = ',' -var dot = '.' -var colon = ':' -var semicolon = ';' -var lessThan = '<' -var atSign = '@' -var leftSquareBracket = '[' -var rightSquareBracket = ']' - -var http = 'http://' -var https = 'https://' -var mailto = 'mailto:' - -var protocols = [http, https, mailto] - -var protocolsLength = protocols.length +var exclamationMark = 33 // '!' +var ampersand = 38 // '&' +var rightParenthesis = 41 // ')' +var asterisk = 42 // '*' +var comma = 44 // ',' +var dash = 45 // '-' +var dot = 46 // '.' +var colon = 58 // ':' +var semicolon = 59 // ';' +var questionMark = 63 // '?' +var lessThan = 60 // '<' +var underscore = 95 // '_' +var tilde = 126 // '~' + +var leftParenthesisCharacter = '(' +var rightParenthesisCharacter = ')' function url(eat, value, silent) { var self = this - var subvalue - var content - var character + var gfm = self.options.gfm + var tokenizers = self.inlineTokenizers + var length = value.length + var previousDot = -1 + var protocolless = false + var dots + var lastTwoPartsStart + var start var index - var position - var protocol - var match - var length - var queue - var parenCount - var nextCharacter - var tokenizers + var pathStart + var path + var code + var end + var leftCount + var rightCount + var content + var children + var url var exit - if (!self.options.gfm) { + if (!gfm) { + return + } + + // `WWW.` doesn’t work. + if (value.slice(0, 4) === 'www.') { + protocolless = true + index = 4 + } else if (value.slice(0, 7).toLowerCase() === 'http://') { + index = 7 + } else if (value.slice(0, 8).toLowerCase() === 'https://') { + index = 8 + } else { return } - subvalue = '' - index = -1 + // Act as if the starting boundary is a dot. + previousDot = index - 1 - while (++index < protocolsLength) { - protocol = protocols[index] - match = value.slice(0, protocol.length) + // Parse a valid domain. + start = index + dots = [] - if (match.toLowerCase() === protocol) { - subvalue = match - break + while (index < length) { + code = value.charCodeAt(index) + + if (code === dot) { + // Dots may not appear after each other. + if (previousDot === index - 1) { + break + } + + dots.push(index) + previousDot = index + index++ + continue } + + if ( + decimal(code) || + alphabetical(code) || + code === dash || + code === underscore + ) { + index++ + continue + } + + break + } + + // Ignore a final dot: + if (code === dot) { + dots.pop() + index-- } - if (!subvalue) { + // If there are not dots, exit. + if (dots[0] === undefined) { return } - index = subvalue.length - length = value.length - queue = '' - parenCount = 0 + // If there is an underscore in the last two domain parts, exit: + // `www.example.c_m` and `www.ex_ample.com` are not OK, but + // `www.sub_domain.example.com` is. + lastTwoPartsStart = dots.length < 2 ? start : dots[dots.length - 2] + 1 + if (value.slice(lastTwoPartsStart, index).indexOf('_') !== -1) { + return + } + + /* istanbul ignore if - never used (yet) */ + if (silent) { + return true + } + + end = index + pathStart = index + + // Parse a path. while (index < length) { - character = value.charAt(index) + code = value.charCodeAt(index) - if (whitespace(character) || character === lessThan) { + if (whitespace(code) || code === lessThan) { break } + index++ + if ( - character === dot || - character === comma || - character === colon || - character === semicolon || - character === quotationMark || - character === apostrophe || - character === rightParenthesis || - character === rightSquareBracket + code === exclamationMark || + code === asterisk || + code === comma || + code === dot || + code === colon || + code === questionMark || + code === underscore || + code === tilde ) { - nextCharacter = value.charAt(index + 1) - - if (!nextCharacter || whitespace(nextCharacter)) { - break - } + // Empty + } else { + end = index } + } - if (character === leftParenthesis || character === leftSquareBracket) { - parenCount++ - } + index = end - if (character === rightParenthesis || character === rightSquareBracket) { - parenCount-- + // If the path ends in a closing paren, and the count of closing parens is + // higher than the opening count, then remove the supefluous closing parens. + if (value.charCodeAt(index - 1) === rightParenthesis) { + path = value.slice(pathStart, index) + leftCount = ccount(path, leftParenthesisCharacter) + rightCount = ccount(path, rightParenthesisCharacter) - if (parenCount < 0) { - break - } + while (rightCount > leftCount) { + index = pathStart + path.lastIndexOf(rightParenthesisCharacter) + path = value.slice(pathStart, index) + rightCount-- } - - queue += character - index++ } - if (!queue) { - return - } + if (value.charCodeAt(index - 1) === semicolon) { + // GitHub doesn’t document this, but final semicolons aren’t paret of the + // URL either. + index-- - subvalue += queue - content = subvalue + // // If the path ends in what looks like an entity, it’s not part of the path. + if (alphabetical(value.charCodeAt(index - 1))) { + end = index - 2 - if (protocol === mailto) { - position = queue.indexOf(atSign) + while (alphabetical(value.charCodeAt(end))) { + end-- + } - if (position === -1 || position === length - 1) { - return + if (value.charCodeAt(end) === ampersand) { + index = end + } } - - content = content.slice(mailto.length) } - /* istanbul ignore if - never used (yet) */ - if (silent) { - return true + content = value.slice(0, index) + url = decode(content, {nonTerminated: false}) + + if (protocolless) { + url = 'http://' + url } exit = self.enterLink() // Temporarily remove all tokenizers except text in url. - tokenizers = self.inlineTokenizers self.inlineTokenizers = {text: tokenizers.text} - - content = self.tokenizeInline(content, eat.now()) - + children = self.tokenizeInline(content, eat.now()) self.inlineTokenizers = tokenizers + exit() - return eat(subvalue)({ - type: 'link', - title: null, - url: decode(subvalue, {nonTerminated: false}), - children: content - }) + return eat(content)({type: 'link', title: null, url: url, children: children}) } diff --git a/packages/remark-parse/lib/tokenizer.js b/packages/remark-parse/lib/tokenizer.js index a4e2b4cf9..42d3cf127 100644 --- a/packages/remark-parse/lib/tokenizer.js +++ b/packages/remark-parse/lib/tokenizer.js @@ -46,11 +46,15 @@ function factory(type) { name = methods[index] method = tokenizers[name] + // Previously, we had constructs such as footnotes and YAML that used + // these properties. + // Those are now external (plus there are userland extensions), that may + // still use them. if ( method && /* istanbul ignore next */ (!method.onlyAtStart || self.atStart) && - (!method.notInList || !self.inList) && - (!method.notInBlock || !self.inBlock) && + /* istanbul ignore next */ (!method.notInList || !self.inList) && + /* istanbul ignore next */ (!method.notInBlock || !self.inBlock) && (!method.notInLink || !self.inLink) ) { valueLength = value.length @@ -109,7 +113,7 @@ function factory(type) { // Done. Called when the last character is eaten to retrieve the range’s // offsets. - return function() { + return function () { var last = line + 1 while (pos < last) { @@ -160,10 +164,10 @@ function factory(type) { // Add the position to a node. function update(node, indent) { - var prev = node.position - var start = prev ? prev.start : before + var previous = node.position + var start = previous ? previous.start : before var combined = [] - var n = prev && prev.end.line + var n = previous && previous.end.line var l = before.line node.position = new Position(start) @@ -173,8 +177,8 @@ function factory(type) { // because some information, the indent between `n` and `l` wasn’t // tracked. Luckily, that space is (should be?) empty, so we can // safely check for it now. - if (prev && indent && prev.indent) { - combined = prev.indent + if (previous && indent && previous.indent) { + combined = previous.indent if (n < l) { while (++n < l) { @@ -197,21 +201,21 @@ function factory(type) { // possible. function add(node, parent) { var children = parent ? parent.children : tokens - var prev = children[children.length - 1] + var previous = children[children.length - 1] var fn if ( - prev && - node.type === prev.type && + previous && + node.type === previous.type && (node.type === 'text' || node.type === 'blockquote') && - mergeable(prev) && + mergeable(previous) && mergeable(node) ) { fn = node.type === 'text' ? mergeText : mergeBlockquote - node = fn.call(self, prev, node) + node = fn.call(self, previous, node) } - if (node !== prev) { + if (node !== previous) { children.push(node) } @@ -296,19 +300,19 @@ function mergeable(node) { } // Merge two text nodes: `node` into `prev`. -function mergeText(prev, node) { - prev.value += node.value +function mergeText(previous, node) { + previous.value += node.value - return prev + return previous } // Merge two blockquotes: `node` into `prev`, unless in CommonMark or gfm modes. -function mergeBlockquote(prev, node) { +function mergeBlockquote(previous, node) { if (this.options.commonmark || this.options.gfm) { return node } - prev.children = prev.children.concat(node.children) + previous.children = previous.children.concat(node.children) - return prev + return previous } diff --git a/packages/remark-parse/lib/unescape.js b/packages/remark-parse/lib/unescape.js index 90cdb74c0..18fe26917 100644 --- a/packages/remark-parse/lib/unescape.js +++ b/packages/remark-parse/lib/unescape.js @@ -10,26 +10,26 @@ function factory(ctx, key) { // De-escape a string using the expression at `key` in `ctx`. function unescape(value) { - var prev = 0 + var previous = 0 var index = value.indexOf(backslash) var escape = ctx[key] var queue = [] var character while (index !== -1) { - queue.push(value.slice(prev, index)) - prev = index + 1 - character = value.charAt(prev) + queue.push(value.slice(previous, index)) + previous = index + 1 + character = value.charAt(previous) // If the following character is not a valid escape, add the slash. if (!character || escape.indexOf(character) === -1) { queue.push(backslash) } - index = value.indexOf(backslash, prev + 1) + index = value.indexOf(backslash, previous + 1) } - queue.push(value.slice(prev)) + queue.push(value.slice(previous)) return queue.join('') } diff --git a/packages/remark-parse/lib/util/interrupt.js b/packages/remark-parse/lib/util/interrupt.js index e707c8b13..22a839fc6 100644 --- a/packages/remark-parse/lib/util/interrupt.js +++ b/packages/remark-parse/lib/util/interrupt.js @@ -2,7 +2,7 @@ module.exports = interrupt -function interrupt(interruptors, tokenizers, ctx, params) { +function interrupt(interruptors, tokenizers, ctx, parameters) { var length = interruptors.length var index = -1 var interruptor @@ -26,7 +26,7 @@ function interrupt(interruptors, tokenizers, ctx, params) { continue } - if (tokenizers[interruptor[0]].apply(ctx, params)) { + if (tokenizers[interruptor[0]].apply(ctx, parameters)) { return true } } diff --git a/packages/remark-parse/package.json b/packages/remark-parse/package.json index e5fc0656c..f5a0084dd 100644 --- a/packages/remark-parse/package.json +++ b/packages/remark-parse/package.json @@ -1,11 +1,12 @@ { "name": "remark-parse", - "version": "7.0.2", + "version": "8.0.0", "description": "remark plugin to parse Markdown", "license": "MIT", "keywords": [ "unified", "remark", + "remark-plugin", "plugin", "markdown", "mdast", @@ -37,20 +38,21 @@ "types/index.d.ts" ], "dependencies": { + "ccount": "^1.0.0", "collapse-white-space": "^1.0.2", "is-alphabetical": "^1.0.0", "is-decimal": "^1.0.0", "is-whitespace-character": "^1.0.0", "is-word-character": "^1.0.0", "markdown-escapes": "^1.0.0", - "parse-entities": "^1.1.0", + "parse-entities": "^2.0.0", "repeat-string": "^1.5.4", "state-toggle": "^1.0.0", "trim": "0.0.1", "trim-trailing-lines": "^1.0.0", "unherit": "^1.0.4", - "unist-util-remove-position": "^1.0.0", - "vfile-location": "^2.0.0", + "unist-util-remove-position": "^2.0.0", + "vfile-location": "^3.0.0", "xtend": "^4.0.1" }, "scripts": { diff --git a/packages/remark-parse/readme.md b/packages/remark-parse/readme.md index f9d1e8ae5..afc731bf2 100644 --- a/packages/remark-parse/readme.md +++ b/packages/remark-parse/readme.md @@ -11,7 +11,7 @@ [Parser][] for [**unified**][unified]. Parses Markdown to [**mdast**][mdast] syntax trees. Used in the [**remark** processor][remark] but can be used on its own as well. -Can be [extended][extend] to change how markdown is parsed. +Can be [extended][extend] to change how Markdown is parsed. ## Sponsors @@ -19,35 +19,41 @@ Can be [extended][extend] to change how markdown is parsed. - - - - + + + - +
- -

🥇 - ZEIT +
+ Gatsby
🥇

+
- -

🥇 - Gatsby +
+ ZEIT
🥇

+ +
- -

🥇 - Netlify +
+ Netlify
🥇

+ +
- -

- Holloway +
+ Holloway


+ +
+ ThemeIsle
🥉

+
+ + BoostIO
🥉

+ +




- You? + You?
-[**Read more about the unified collective on Medium »**][announcement] - ## Install [npm][]: @@ -75,12 +81,12 @@ process.stdin.pipe(createStream(processor)).pipe(process.stdout) [See **unified** for more examples »][unified] -## Table of Contents +## Contents * [API](#api) * [`processor().use(parse[, options])`](#processoruseparse-options) * [`parse.Parser`](#parseparser) -* [Extending the Parser](#extending-the-parser) +* [Extending the `Parser`](#extending-the-parser) * [`Parser#blockTokenizers`](#parserblocktokenizers) * [`Parser#blockMethods`](#parserblockmethods) * [`Parser#inlineTokenizers`](#parserinlinetokenizers) @@ -137,11 +143,11 @@ This is a paragraph Allows: -* Empty lines to split blockquotes +* Empty lines to split block quotes * Parentheses (`(` and `)`) around link and image titles * Any escaped [ASCII punctuation][escapes] character * Closing parenthesis (`)`) as an ordered list marker -* URL definitions (and footnotes, when enabled) in blockquotes +* URL definitions in block quotes Disallows: @@ -152,43 +158,15 @@ Disallows: * Newlines in link and image titles * White space in link and image URLs in auto-links (links in brackets, `<` and `>`) -* Lazy blockquote continuation, lines not preceded by a greater than character - (`>`), for lists, code, and thematic breaks - -###### `options.footnotes` - -Footnotes mode (`boolean`, default: `false`). - -```markdown -Something something[^or something?]. - -And something else[^1]. - -[^1]: This reference footnote contains a paragraph... - - * ...and a list -``` - -Enables reference footnotes and inline footnotes. -Both are wrapped in square brackets and preceded by a caret (`^`), and can be -referenced from inside other footnotes. +* Lazy block quote continuation, lines not preceded by a greater than + character (`>`), for lists, code, and thematic breaks ###### `options.pedantic` -Pedantic mode (`boolean`, default: `false`). - -```markdown -Check out some_file_name.txt -``` - -Turns on: - -* Emphasis (`_alpha_`) and importance (`__bravo__`) with underscores in words -* Unordered lists with different markers (`*`, `-`, `+`) -* If `commonmark` is also turned on, ordered lists with different markers - (`.`, `)`) -* And removes less spaces in list items (at most four, instead of the whole - indent) +⚠️ Pedantic was previously used to mimic old-style Markdown mode: no tables, no +fenced code, and with many bugs. +It’s currently still “working”, but please do not use it, it’ll be removed in +the future. ###### `options.blocks` @@ -205,7 +183,7 @@ Defines which HTML elements are seen as block level. Access to the [parser][], if you need it. -## Extending the Parser +## Extending the `Parser` Typically, using [*transformers*][transformer] to manipulate a syntax tree produces the desired output. @@ -252,7 +230,7 @@ Precedence of default block methods is as follows: -* `newline` +* `blankLine` * `indentedCode` * `fencedCode` * `blockquote` @@ -261,7 +239,6 @@ Precedence of default block methods is as follows: * `list` * `setextHeading` * `html` -* `footnote` * `definition` * `table` * `paragraph` @@ -289,6 +266,7 @@ Precedence of default inline methods is as follows: * `escape` * `autoLink` * `url` +* `email` * `html` * `link` * `reference` @@ -354,8 +332,7 @@ entity may occur. * `locator` ([`Function`][locator]) — Required for inline tokenizers * `onlyAtStart` (`boolean`) — Whether nodes can only be found at the beginning of the document -* `notInBlock` (`boolean`) — Whether nodes cannot be in blockquotes, lists, or - footnote definitions +* `notInBlock` (`boolean`) — Whether nodes cannot be in block quotes or lists * `notInList` (`boolean`) — Whether nodes cannot be in lists * `notInLink` (`boolean`) — Whether nodes cannot be in links @@ -500,8 +477,8 @@ Ideas for new plugins and tools can be posted in [`remarkjs/ideas`][ideas]. A curated list of awesome remark resources can be found in [**awesome remark**][awesome]. -This project has a [Code of Conduct][coc]. -By interacting with this repository, organisation, or community you agree to +This project has a [code of conduct][coc]. +By interacting with this repository, organization, or community you agree to abide by its terms. ## License @@ -532,7 +509,7 @@ abide by its terms. [collective]: https://opencollective.com/unified -[chat-badge]: https://img.shields.io/badge/join%20the%20community-on%20spectrum-7b16ff.svg +[chat-badge]: https://img.shields.io/badge/chat-spectrum-7b16ff.svg [chat]: https://spectrum.chat/unified/remark @@ -586,8 +563,6 @@ abide by its terms. [add]: #addnode-parent -[announcement]: https://medium.com/unifiedjs/collectively-evolving-through-crowdsourcing-22c359ea95cc - [remark-disable-tokenizers]: https://github.com/zestedesavoir/zmarkdown/tree/master/packages/remark-disable-tokenizers [xss]: https://en.wikipedia.org/wiki/Cross-site_scripting diff --git a/packages/remark-parse/test.js b/packages/remark-parse/test.js index f4ec9427c..91bc9e6b7 100644 --- a/packages/remark-parse/test.js +++ b/packages/remark-parse/test.js @@ -2,44 +2,36 @@ var path = require('path') var fs = require('fs') - -/* eslint-disable import/no-extraneous-dependencies */ var test = require('tape') var vfile = require('vfile') var unified = require('unified') -/* eslint-enable import/no-extraneous-dependencies */ var parse = require('.') var Parser = parse.Parser -test('remark().parse(file)', function(t) { +test('remark().parse(file)', function (t) { t.equal( - unified() - .use(parse) - .parse('Alfred').children.length, + unified().use(parse).parse('Alfred').children.length, 1, 'should accept a `string`' ) t.throws( - function() { - unified() - .use(parse) - .data('settings', {position: 0}) - .parse('') + function () { + unified().use(parse).data('settings', {position: 0}).parse('') }, /options.position/, 'should throw when `options.position` is not a boolean' ) - t.doesNotThrow(function() { + t.doesNotThrow(function () { var parser = new Parser() parser.setOptions() }, 'should not throw when setting nothing') t.throws( - function() { + function () { var parser = new Parser() parser.setOptions(true) }, @@ -48,33 +40,16 @@ test('remark().parse(file)', function(t) { ) t.throws( - function() { - unified() - .use(parse) - .data('settings', {gfm: Infinity}) - .parse('') + function () { + unified().use(parse).data('settings', {gfm: Infinity}).parse('') }, /options.gfm/, 'should throw when `options.gfm` is not a boolean' ) t.throws( - function() { - unified() - .use(parse) - .data('settings', {footnotes: 1}) - .parse('') - }, - /options.footnotes/, - 'should throw when `options.footnotes` is not a boolean' - ) - - t.throws( - function() { - unified() - .use(parse) - .data('settings', {pedantic: {}}) - .parse('') + function () { + unified().use(parse).data('settings', {pedantic: {}}).parse('') }, /options.pedantic/, 'should throw when `options.pedantic` is not a boolean' @@ -112,10 +87,8 @@ test('remark().parse(file)', function(t) { 'should support given `blocks`' ) - t.test('should throw parse errors', function(st) { - var processor = unified() - .use(parse) - .use(plugin) + t.test('should throw parse errors', function (st) { + var processor = unified().use(parse).use(plugin) st.plan(5) @@ -145,12 +118,10 @@ test('remark().parse(file)', function(t) { } }) - t.test('should warn when missing locators', function(st) { - var processor = unified() - .use(parse) - .use(plugin) + t.test('should warn when missing locators', function (st) { + var processor = unified().use(parse).use(plugin) - st.throws(function() { + st.throws(function () { processor.parse(vfile('Hello *World*!')) }, /1:1: Missing locator: `foo`/) @@ -168,7 +139,7 @@ test('remark().parse(file)', function(t) { } }) - t.test('should warn about entities', function(st) { + t.test('should warn about entities', function (st) { var filePath = path.join( 'test', 'fixtures', @@ -179,9 +150,7 @@ test('remark().parse(file)', function(t) { var notTerminated = 'Named character references must be terminated by a semicolon' - unified() - .use(parse) - .parse(file) + unified().use(parse).parse(file) st.deepEqual(file.messages.map(String), [ '1:13: Named character references must be known', @@ -204,7 +173,7 @@ test('remark().parse(file)', function(t) { st.end() }) - t.test('should be able to set options', function(st) { + t.test('should be able to set options', function (st) { var tree = unified() .use(parse) .use(plugin) diff --git a/packages/remark-parse/types/index.d.ts b/packages/remark-parse/types/index.d.ts index 9586f76a8..7847ffa21 100644 --- a/packages/remark-parse/types/index.d.ts +++ b/packages/remark-parse/types/index.d.ts @@ -9,25 +9,79 @@ declare class RemarkParser implements Parser { inlineTokenizers: { [key: string]: remarkParse.Tokenizer } + inlineMethods: string[] } declare namespace remarkParse { - interface Parse extends Plugin<[Partial?]> { - (options: Partial): void + interface Parse extends Plugin<[PartialRemarkParseOptions?]> { + (options: PartialRemarkParseOptions): void Parser: typeof RemarkParser } type Parser = RemarkParser interface RemarkParseOptions { + /** + * GFM mode + * + * Turns on: + * * Fenced code blocks + * * Autolinking of URLs + * * Deletions (strikethrough) + * * Task lists + * * Tables + * + * @defaultValue `true` + */ gfm: boolean + + /** + * CommonMark mode + * + * Allows: + * * Empty lines to split blockquotes + * * Parentheses (`(` and `)`) around link and image titles + * * Any escaped ASCII punctuation character + * * Closing parenthesis (`)`) as an ordered list marker + * * URL definitions in blockquotes + * + * Disallows: + * * Indented code blocks directly following a paragraph + * * ATX headings (# Hash headings) without spacing after opening hashes or and before closing hashes + * * Setext headings (`Underline headings\n---`) when following a paragraph + * * Newlines in link and image titles + * * White space in link and image URLs in auto-links (links in brackets, `<` and `>`) + * * Lazy blockquote continuation, lines not preceded by a greater than character (`>`), for lists, code, and thematic breaks + * + * @defaultValue `false` + */ commonmark: boolean - footnotes: boolean + + /** + * Defines which HTML elements are seen as block level. + * + * @defaultValue blocks listed in + */ blocks: string[] + + /** + * Pedantic mode + * + * Turns on: + * * Emphasis (`_alpha_`) and importance (`__bravo__`) with underscores in words + * * Unordered lists with different markers (`*`, `-`, `+`) + * * If commonmark is also turned on, ordered lists with different markers (`.`, `)`) + * * And removes less spaces in list items (at most four, instead of the whole indent) + * + * @defaultValue `false` + * @deprecated pedantic mode is buggy. It won’t be in micromark, which will be the basis of a future version of remark. + */ pedantic: boolean } + type PartialRemarkParseOptions = Partial + interface Add { (node: Node, parent?: Parent): Node test(): Position diff --git a/packages/remark-parse/types/test.ts b/packages/remark-parse/types/test.ts index f600cc9d9..93a14ba39 100644 --- a/packages/remark-parse/types/test.ts +++ b/packages/remark-parse/types/test.ts @@ -1,13 +1,13 @@ import unified = require('unified') import remarkParse = require('remark-parse') -const parseOptions = { +const partialParseOptions: remarkParse.PartialRemarkParseOptions = { gfm: true, pedantic: true } unified().use(remarkParse) -unified().use(remarkParse, parseOptions) +unified().use(remarkParse, partialParseOptions) const badParseOptions = { gfm: 'true' @@ -15,3 +15,14 @@ const badParseOptions = { // $ExpectError unified().use(remarkParse, badParseOptions) + +// $ExpectError +const parseOptions: remarkParse.RemarkParseOptions = { + gfm: true, + pedantic: true +} + +const badParseOptionsInterface: remarkParse.PartialRemarkParseOptions = { + // $ExpectError + gfm: 'true' +} diff --git a/packages/remark-parse/types/tsconfig.json b/packages/remark-parse/types/tsconfig.json index 9680afacc..03e437921 100644 --- a/packages/remark-parse/types/tsconfig.json +++ b/packages/remark-parse/types/tsconfig.json @@ -2,10 +2,6 @@ "compilerOptions": { "lib": ["es2015"], "strict": true, - "noImplicitAny": true, - "noImplicitThis": true, - "strictNullChecks": true, - "strictFunctionTypes": true, "baseUrl": ".", "paths": { "remark-parse": ["index.d.ts"] diff --git a/packages/remark-stringify/lib/compiler.js b/packages/remark-stringify/lib/compiler.js index 213314f9c..6c5a221f0 100644 --- a/packages/remark-stringify/lib/compiler.js +++ b/packages/remark-stringify/lib/compiler.js @@ -55,9 +55,6 @@ proto.visitors = { imageReference: require('./visitors/image-reference'), definition: require('./visitors/definition'), image: require('./visitors/image'), - footnote: require('./visitors/footnote'), - footnoteReference: require('./visitors/footnote-reference'), - footnoteDefinition: require('./visitors/footnote-definition'), table: require('./visitors/table'), tableCell: require('./visitors/table-cell') } diff --git a/packages/remark-stringify/lib/defaults.js b/packages/remark-stringify/lib/defaults.js index fa19a368b..a9f153dc2 100644 --- a/packages/remark-stringify/lib/defaults.js +++ b/packages/remark-stringify/lib/defaults.js @@ -7,9 +7,8 @@ module.exports = { entities: 'false', setext: false, closeAtx: false, - looseTable: false, - spacedTable: true, - paddedTable: true, + tableCellPadding: true, + tablePipeAlign: true, stringLength: stringLength, incrementListMarker: true, fences: false, diff --git a/packages/remark-stringify/lib/escape.js b/packages/remark-stringify/lib/escape.js index cf4801529..3343d8253 100644 --- a/packages/remark-stringify/lib/escape.js +++ b/packages/remark-stringify/lib/escape.js @@ -59,7 +59,7 @@ function factory(options) { var markers = commonmark ? [dot, rightParenthesis] : [dot] var siblings = parent && parent.children var index = siblings && siblings.indexOf(node) - var prev = siblings && siblings[index - 1] + var previous = siblings && siblings[index - 1] var next = siblings && siblings[index + 1] var length = value.length var escapable = escapes(options) @@ -73,8 +73,8 @@ function factory(options) { var offset var replace - if (prev) { - afterNewLine = text(prev) && blankExpression.test(prev.value) + if (previous) { + afterNewLine = text(previous) && blankExpression.test(previous.value) } else { afterNewLine = !parent || parent.type === 'root' || parent.type === 'paragraph' @@ -152,7 +152,7 @@ function factory(options) { if (siblings && text(node)) { // Check for an opening parentheses after a link-reference (which can be // joined by white-space). - if (prev && prev.referenceType === shortcut) { + if (previous && previous.referenceType === shortcut) { position = -1 length = escaped.length @@ -186,9 +186,9 @@ function factory(options) { if ( gfm && !self.inLink && - text(prev) && + text(previous) && value.charAt(0) === colon && - protocol(prev.value.slice(-6)) + protocol(previous.value.slice(-6)) ) { escaped[0] = one(colon) } @@ -218,11 +218,11 @@ function factory(options) { value.charAt(length - 1) === tilde && next.value.charAt(0) === tilde ) { - escaped.splice(escaped.length - 1, 0, backslash) + escaped.splice(-1, 0, backslash) } // Escape underscores, but not mid-word (unless in pedantic mode). - wordCharBefore = text(prev) && alphanumeric(prev.value.slice(-1)) + wordCharBefore = text(previous) && alphanumeric(previous.value.slice(-1)) wordCharAfter = text(next) && alphanumeric(next.value.charAt(0)) if (length === 1) { @@ -246,7 +246,7 @@ function factory(options) { !wordCharAfter || !alphanumeric(value.charAt(length - 2))) ) { - escaped.splice(escaped.length - 1, 0, backslash) + escaped.splice(-1, 0, backslash) } } } @@ -292,6 +292,6 @@ function text(node) { // Check if `value` ends in a protocol. function protocol(value) { - var val = value.slice(-6).toLowerCase() - return val === mailto || val.slice(-5) === https || val.slice(-4) === http + var tail = value.slice(-6).toLowerCase() + return tail === mailto || tail.slice(-5) === https || tail.slice(-4) === http } diff --git a/packages/remark-stringify/lib/macro/block.js b/packages/remark-stringify/lib/macro/block.js index bc7130959..359ea99be 100644 --- a/packages/remark-stringify/lib/macro/block.js +++ b/packages/remark-stringify/lib/macro/block.js @@ -20,14 +20,14 @@ function block(node) { var children = node.children var length = children.length var index = -1 - var prev + var previous var child while (++index < length) { - prev = child + previous = child child = children[index] - if (prev) { + if (previous) { // A list preceding another list that are equally ordered, or a // list preceding an indented code block, need a gap between them, // so as not to see them as one list, or content of the list, @@ -37,8 +37,8 @@ function block(node) { // so we opt for an empty, invisible comment. In other flavours, // two blank lines are fine. if ( - prev.type === 'list' && - ((child.type === 'list' && prev.ordered === child.ordered) || + previous.type === 'list' && + ((child.type === 'list' && previous.ordered === child.ordered) || (child.type === 'code' && !child.lang && !fences)) ) { values.push(gap) diff --git a/packages/remark-stringify/lib/visitors/footnote-definition.js b/packages/remark-stringify/lib/visitors/footnote-definition.js deleted file mode 100644 index 42d27f4df..000000000 --- a/packages/remark-stringify/lib/visitors/footnote-definition.js +++ /dev/null @@ -1,30 +0,0 @@ -'use strict' - -var repeat = require('repeat-string') - -var lineFeed = '\n' -var space = ' ' -var colon = ':' -var leftSquareBracket = '[' -var rightSquareBracket = ']' -var caret = '^' - -var tabSize = 4 -var blank = lineFeed + lineFeed -var indent = repeat(space, tabSize) - -module.exports = footnoteDefinition - -function footnoteDefinition(node) { - var content = this.all(node).join(blank + indent) - - return ( - leftSquareBracket + - caret + - (node.label || node.identifier) + - rightSquareBracket + - colon + - space + - content - ) -} diff --git a/packages/remark-stringify/lib/visitors/footnote-reference.js b/packages/remark-stringify/lib/visitors/footnote-reference.js deleted file mode 100644 index 1ee9b4c38..000000000 --- a/packages/remark-stringify/lib/visitors/footnote-reference.js +++ /dev/null @@ -1,16 +0,0 @@ -'use strict' - -module.exports = footnoteReference - -var leftSquareBracket = '[' -var rightSquareBracket = ']' -var caret = '^' - -function footnoteReference(node) { - return ( - leftSquareBracket + - caret + - (node.label || node.identifier) + - rightSquareBracket - ) -} diff --git a/packages/remark-stringify/lib/visitors/footnote.js b/packages/remark-stringify/lib/visitors/footnote.js deleted file mode 100644 index 9269bd4c5..000000000 --- a/packages/remark-stringify/lib/visitors/footnote.js +++ /dev/null @@ -1,13 +0,0 @@ -'use strict' - -module.exports = footnote - -var leftSquareBracket = '[' -var rightSquareBracket = ']' -var caret = '^' - -function footnote(node) { - return ( - leftSquareBracket + caret + this.all(node).join('') + rightSquareBracket - ) -} diff --git a/packages/remark-stringify/lib/visitors/table-cell.js b/packages/remark-stringify/lib/visitors/table-cell.js index a90f8c50b..9de3dbf88 100644 --- a/packages/remark-stringify/lib/visitors/table-cell.js +++ b/packages/remark-stringify/lib/visitors/table-cell.js @@ -5,7 +5,5 @@ module.exports = tableCell var lineFeed = /\r?\n/g function tableCell(node) { - return this.all(node) - .join('') - .replace(lineFeed, ' ') + return this.all(node).join('').replace(lineFeed, ' ') } diff --git a/packages/remark-stringify/lib/visitors/table.js b/packages/remark-stringify/lib/visitors/table.js index e246103b6..33771508f 100644 --- a/packages/remark-stringify/lib/visitors/table.js +++ b/packages/remark-stringify/lib/visitors/table.js @@ -4,22 +4,19 @@ var markdownTable = require('markdown-table') module.exports = table -var space = ' ' -var verticalBar = '|' - // Stringify table. // -// Creates a fenced table by default, but not in `looseTable: true` mode: +// Creates a fenced table. +// The table has aligned delimiters by default, but not in +// `tablePipeAlign: false`: // // ```markdown -// Foo | Bar -// :-: | --- -// Baz | Qux -// -// NOTE: Be careful with `looseTable: true` mode, as a loose table inside an -// indented code block on GitHub renders as an actual table! +// | Header 1 | Header 2 | +// | :-: | - | +// | Alpha | Bravo | +// ``` // -// Creates a spaced table by default, but not in `spacedTable: false`: +// The table is spaced by default, but not in `tableCellPadding: false`: // // ```markdown // |Foo|Bar| @@ -29,16 +26,13 @@ var verticalBar = '|' function table(node) { var self = this var options = self.options - var loose = options.looseTable - var spaced = options.spacedTable - var pad = options.paddedTable + var padding = options.tableCellPadding + var alignDelimiters = options.tablePipeAlign var stringLength = options.stringLength var rows = node.children var index = rows.length var exit = self.enterTable() var result = [] - var start - var end while (index--) { result[index] = self.all(rows[index]) @@ -46,23 +40,10 @@ function table(node) { exit() - if (loose) { - start = '' - end = '' - } else if (spaced) { - start = verticalBar + space - end = space + verticalBar - } else { - start = verticalBar - end = verticalBar - } - return markdownTable(result, { align: node.align, - pad: pad, - start: start, - end: end, - stringLength: stringLength, - delimiter: spaced ? space + verticalBar + space : verticalBar + alignDelimiters: alignDelimiters, + padding: padding, + stringLength: stringLength }) } diff --git a/packages/remark-stringify/package.json b/packages/remark-stringify/package.json index edd940286..5a6b4a057 100644 --- a/packages/remark-stringify/package.json +++ b/packages/remark-stringify/package.json @@ -1,11 +1,12 @@ { "name": "remark-stringify", - "version": "7.0.4", + "version": "8.0.0", "description": "remark plugin to compile Markdown", "license": "MIT", "keywords": [ "unified", "remark", + "remark-plugin", "plugin", "markdown", "mdast", @@ -14,7 +15,9 @@ "syntax", "tree", "ast", - "stringify" + "stringify", + "serialize", + "compile" ], "homepage": "https://remark.js.org", "repository": "https://github.com/remarkjs/remark/tree/master/packages/remark-stringify", @@ -41,12 +44,12 @@ "is-whitespace-character": "^1.0.0", "longest-streak": "^2.0.1", "markdown-escapes": "^1.0.0", - "markdown-table": "^1.1.0", - "mdast-util-compact": "^1.0.0", - "parse-entities": "^1.0.2", + "markdown-table": "^2.0.0", + "mdast-util-compact": "^2.0.0", + "parse-entities": "^2.0.0", "repeat-string": "^1.5.4", "state-toggle": "^1.0.0", - "stringify-entities": "^2.0.0", + "stringify-entities": "^3.0.0", "unherit": "^1.0.4", "xtend": "^4.0.1" }, diff --git a/packages/remark-stringify/readme.md b/packages/remark-stringify/readme.md index 63883c23f..953032b16 100644 --- a/packages/remark-stringify/readme.md +++ b/packages/remark-stringify/readme.md @@ -9,9 +9,9 @@ [![Backers][backers-badge]][collective] [Compiler][] for [**unified**][unified]. -Stringifies [**mdast**][mdast] syntax trees to Markdown. +Serializes [**mdast**][mdast] syntax trees to Markdown. Used in the [**remark** processor][remark] but can be used on its own as well. -Can be [extended][extend] to change how Markdown is compiled. +Can be [extended][extend] to change how Markdown is serialized. ## Sponsors @@ -19,35 +19,41 @@ Can be [extended][extend] to change how Markdown is compiled. - - - - + + + - +
- -

🥇 - ZEIT +
+ Gatsby
🥇

+
- -

🥇 - Gatsby +
+ ZEIT
🥇

+ +
- -

🥇 - Netlify +
+ Netlify
🥇

+ +
- -

- Holloway +
+ Holloway


+ +
+ ThemeIsle
🥉

+
+ + BoostIO
🥉

+ +




- You? + You?
-[**Read more about the unified collective on Medium »**][announcement] - ## Install [npm][]: @@ -80,12 +86,12 @@ process.stdin.pipe(createStream(processor)).pipe(process.stdout) [See **unified** for more examples »][unified] -## Table of Contents +## Contents * [API](#api) * [`processor().use(stringify[, options])`](#processorusestringify-options) * [`stringify.Compiler`](#stringifycompiler) -* [Extending the Compiler](#extending-the-compiler) +* [Extending the `Compiler`](#extending-the-compiler) * [`Compiler#visitors`](#compilervisitors) * [`function visitor(node[, parent])`](#function-visitornode-parent) * [Security](#security) @@ -98,7 +104,7 @@ process.stdin.pipe(createStream(processor)).pipe(process.stdout) ### `processor().use(stringify[, options])` -Configure the `processor` to stringify [**mdast**][mdast] syntax trees to +Configure the `processor` to serialize [**mdast**][mdast] syntax trees to Markdown. ##### `options` @@ -108,7 +114,7 @@ Options can be passed directly, or passed later through ###### `options.gfm` -Stringify with the required escapes for GFM compatible Markdown (`boolean`, +Serialize with the required escapes for GFM compatible Markdown (`boolean`, default: `true`). * Escape pipes (`|`, for tables) @@ -117,20 +123,21 @@ default: `true`). ###### `options.commonmark` -Stringify for CommonMark compatible Markdown (`boolean`, default: `false`). +Serialize for CommonMark compatible Markdown (`boolean`, default: `false`). -* Compile adjacent blockquotes separately +* Serialize adjacent block quotes separately * Escape more characters using slashes, instead of as entities ###### `options.pedantic` -Stringify for pedantic compatible markdown (`boolean`, default: `false`). - -* Escape underscores in words +⚠️ Pedantic was previously used to mimic old-style Markdown mode: no tables, no +fenced code, and with many bugs. +It’s currently still “working”, but please do not use it, it’ll be removed in +the future. ###### `options.entities` -How to stringify entities (`string` or `boolean`, default: `false`): +How to serialize entities (`string` or `boolean`, default: `false`): * `true` — Entities are generated for special HTML characters (`&` > `&`) and non-ASCII characters (`©` > `©`). @@ -143,29 +150,24 @@ How to stringify entities (`string` or `boolean`, default: `false`): ###### `options.setext` -Compile headings, when possible, in Setext-style (`boolean`, default: `false`). +Serialize headings, when possible, in Setext-style (`boolean`, default: `false`). Uses `=` for level one headings and `-` for level two headings. -Other heading levels are compiled as ATX (respecting `closeAtx`). +Other heading levels are serialized as ATX (respecting `closeAtx`). ###### `options.closeAtx` -Compile ATX headings with the same amount of closing hashes as opening hashes +Serialize ATX headings with the same amount of closing hashes as opening hashes (`boolean`, default: `false`). -###### `options.looseTable` +###### `options.tableCellPadding` -Create tables without fences: initial and final pipes (`boolean`, default: -`false`). - -###### `options.spacedTable` +Create tables with a space between cell delimiters (`|`) and content (`boolean`, +default: `true`). -Create tables with a space between a pipe and content (`boolean`, default: -`true`). +###### `options.tablePipeAlign` -###### `options.paddedTable` - -Create tables with more spaces so that all cells in a column align (`boolean`, -default: `true`). +Align the delimiters (`|`) between table cells so that they all align nicely and +form a grid (`boolean`, default: `true`). ###### `options.stringLength` @@ -233,12 +235,12 @@ Marker to use for emphasis (`'_'` or `'*'`, default `'_'`). Access to the [compiler][], if you need it. -## Extending the Compiler +## Extending the `Compiler` If the `remark-stringify` plugin is used, it adds a [`Compiler`][compiler] constructor function to the `processor`. Other plugins can add visitors to its prototype to change how Markdown is -compiled. +serialized. The below plugin modifies a [visitor][] to add an extra blank line before headings with a rank of `2`. @@ -265,7 +267,7 @@ Map of types to [visitor][]s (`Object.`). ### `function visitor(node[, parent])` -Stringify `node`. +Serialize `node`. ###### Parameters @@ -275,7 +277,7 @@ Stringify `node`. ###### Returns -`string` — Compiled given `node`. +`string` — Serialized given `node`. ## Security @@ -297,8 +299,8 @@ Ideas for new plugins and tools can be posted in [`remarkjs/ideas`][ideas]. A curated list of awesome remark resources can be found in [**awesome remark**][awesome]. -This project has a [Code of Conduct][coc]. -By interacting with this repository, organisation, or community you agree to +This project has a [code of conduct][coc]. +By interacting with this repository, organization, or community you agree to abide by its terms. ## License @@ -329,7 +331,7 @@ abide by its terms. [collective]: https://opencollective.com/unified -[chat-badge]: https://img.shields.io/badge/join%20the%20community-on%20spectrum-7b16ff.svg +[chat-badge]: https://img.shields.io/badge/chat-spectrum-7b16ff.svg [chat]: https://spectrum.chat/unified/remark @@ -369,8 +371,6 @@ abide by its terms. [visitor]: #function-visitornode-parent -[announcement]: https://medium.com/unifiedjs/collectively-evolving-through-crowdsourcing-22c359ea95cc - [markdown-table]: https://github.com/wooorm/markdown-table [string-length]: https://github.com/wooorm/markdown-table#stringlengthcell diff --git a/packages/remark-stringify/test.js b/packages/remark-stringify/test.js index 79ae009dd..533df7fee 100644 --- a/packages/remark-stringify/test.js +++ b/packages/remark-stringify/test.js @@ -1,13 +1,11 @@ 'use strict' -/* eslint-disable import/no-extraneous-dependencies */ var test = require('tape') var wcwidth = require('wcwidth') var u = require('unist-builder') var visit = require('unist-util-visit') var unified = require('unified') var parse = require('../remark-parse') -/* eslint-enable import/no-extraneous-dependencies */ var stringify = require('.') @@ -17,7 +15,7 @@ var commonmark = {commonmark: true} var pedantic = {pedantic: true} var uncollapsable = {start: {line: 1, column: NaN}, end: {line: 1, column: NaN}} -test('remark().stringify(ast, file)', function(t) { +test('remark().stringify(ast, file)', function (t) { t.equal( unified() .use(stringify) @@ -31,22 +29,20 @@ test('remark().stringify(ast, file)', function(t) { ) t.throws( - function() { - unified() - .use(stringify) - .stringify(false) + function () { + unified().use(stringify).stringify(false) }, /false/, 'should throw when `ast` is not an object' ) - t.doesNotThrow(function() { + t.doesNotThrow(function () { var compiler = new Compiler() compiler.setOptions() }, 'should not throw when setting nothing') t.throws( - function() { + function () { var compiler = new Compiler() compiler.setOptions(true) }, @@ -54,14 +50,14 @@ test('remark().stringify(ast, file)', function(t) { 'should throw when setting invalid values' ) - t.test('should ignore nully numbers', function(st) { + t.test('should ignore nully numbers', function (st) { var compiler = new Compiler() compiler.setOptions({ruleRepetition: null}) st.equal(compiler.options.ruleRepetition, 3) st.end() }) - t.test('should ignore nully strings', function(st) { + t.test('should ignore nully strings', function (st) { var compiler = new Compiler() compiler.setOptions({listItemIndent: null}) st.equal(compiler.options.listItemIndent, 'tab') @@ -69,17 +65,15 @@ test('remark().stringify(ast, file)', function(t) { }) t.throws( - function() { - unified() - .use(stringify) - .stringify({type: 'unicorn'}) + function () { + unified().use(stringify).stringify({type: 'unicorn'}) }, /unicorn/, 'should throw when `ast` is not a valid node' ) t.throws( - function() { + function () { unified() .use(stringify) .data('settings', {bullet: true}) @@ -90,7 +84,7 @@ test('remark().stringify(ast, file)', function(t) { ) t.throws( - function() { + function () { unified() .use(stringify) .data('settings', {listItemIndent: 'foo'}) @@ -101,18 +95,15 @@ test('remark().stringify(ast, file)', function(t) { ) t.throws( - function() { - unified() - .use(stringify) - .data('settings', {rule: true}) - .stringify(empty()) + function () { + unified().use(stringify).data('settings', {rule: true}).stringify(empty()) }, /options\.rule/, 'should throw when `options.rule` is not a valid horizontal rule bullet' ) t.throws( - function() { + function () { unified() .use(stringify) .data('settings', {ruleSpaces: 1}) @@ -123,7 +114,7 @@ test('remark().stringify(ast, file)', function(t) { ) t.throws( - function() { + function () { unified() .use(stringify) .data('settings', {ruleRepetition: 1}) @@ -134,7 +125,7 @@ test('remark().stringify(ast, file)', function(t) { ) t.throws( - function() { + function () { unified() .use(stringify) .data('settings', {ruleRepetition: NaN}) @@ -145,7 +136,7 @@ test('remark().stringify(ast, file)', function(t) { ) t.throws( - function() { + function () { unified() .use(stringify) .data('settings', {ruleRepetition: true}) @@ -156,7 +147,7 @@ test('remark().stringify(ast, file)', function(t) { ) t.throws( - function() { + function () { unified() .use(stringify) .data('settings', {emphasis: '-'}) @@ -167,7 +158,7 @@ test('remark().stringify(ast, file)', function(t) { ) t.throws( - function() { + function () { unified() .use(stringify) .data('settings', {strong: '-'}) @@ -178,18 +169,15 @@ test('remark().stringify(ast, file)', function(t) { ) t.throws( - function() { - unified() - .use(stringify) - .data('settings', {setext: 0}) - .stringify(empty()) + function () { + unified().use(stringify).data('settings', {setext: 0}).stringify(empty()) }, /options\.setext/, 'should throw when `options.setext` is not a boolean' ) t.throws( - function() { + function () { unified() .use(stringify) .data('settings', {incrementListMarker: -1}) @@ -200,7 +188,7 @@ test('remark().stringify(ast, file)', function(t) { ) t.throws( - function() { + function () { unified() .use(stringify) .data('settings', {fences: NaN}) @@ -211,18 +199,15 @@ test('remark().stringify(ast, file)', function(t) { ) t.throws( - function() { - unified() - .use(stringify) - .data('settings', {fence: '-'}) - .stringify(empty()) + function () { + unified().use(stringify).data('settings', {fence: '-'}).stringify(empty()) }, /options\.fence/, 'should throw when `options.fence` is not a valid fence marker' ) t.throws( - function() { + function () { unified() .use(stringify) .data('settings', {closeAtx: NaN}) @@ -233,40 +218,29 @@ test('remark().stringify(ast, file)', function(t) { ) t.throws( - function() { + function () { unified() .use(stringify) - .data('settings', {looseTable: '!'}) + .data('settings', {tableCellPadding: '?'}) .stringify(empty()) }, - /options\.looseTable/, - 'should throw when `options.looseTable` is not a boolean' + /options\.tableCellPadding/, + 'should throw when `options.tableCellPadding` is not a boolean' ) t.throws( - function() { + function () { unified() .use(stringify) - .data('settings', {spacedTable: '?'}) + .data('settings', {tablePipeAlign: '.'}) .stringify(empty()) }, - /options\.spacedTable/, - 'should throw when `options.spacedTable` is not a boolean' + /options\.tablePipeAlign/, + 'should throw when `options.tablePipeAlign` is not a boolean' ) t.throws( - function() { - unified() - .use(stringify) - .data('settings', {paddedTable: '.'}) - .stringify(empty()) - }, - /options\.paddedTable/, - 'should throw when `options.paddedTable` is not a boolean' - ) - - t.throws( - function() { + function () { unified() .use(stringify) .data('settings', {stringLength: 1}) @@ -276,7 +250,7 @@ test('remark().stringify(ast, file)', function(t) { 'should throw when `options.stringLength` is not a function' ) - t.test('should handle underscores in emphasis in pedantic mode', function( + t.test('should handle underscores in emphasis in pedantic mode', function ( st ) { st.plan(2) @@ -285,11 +259,7 @@ test('remark().stringify(ast, file)', function(t) { // Without pedantic mode, emphasis always defaults to underscores. st.equal( - unified() - .use(parse) - .use(stringify) - .processSync(example) - .toString(), + unified().use(parse).use(stringify).processSync(example).toString(), '_alpha_bravo_\n', 'baseline' ) @@ -308,7 +278,7 @@ test('remark().stringify(ast, file)', function(t) { ) }) - t.test('should support optional list fields', function(st) { + t.test('should support optional list fields', function (st) { st.equal( toString({ type: 'list', @@ -461,15 +431,11 @@ test('remark().stringify(ast, file)', function(t) { st.end() function toString(value) { - return String( - unified() - .use(stringify) - .stringify(value) - ) + return String(unified().use(stringify).stringify(value)) } }) - t.test('should support optional list item fields', function(st) { + t.test('should support optional list item fields', function (st) { var children = [ { type: 'paragraph', @@ -524,15 +490,11 @@ test('remark().stringify(ast, file)', function(t) { st.end() function toString(value) { - return String( - unified() - .use(stringify) - .stringify(value) - ) + return String(unified().use(stringify).stringify(value)) } }) - t.test('should support empty list items', function(st) { + t.test('should support empty list items', function (st) { st.equal(toString({type: 'listItem', children: []}), '-', 'no checked') st.equal( @@ -544,17 +506,13 @@ test('remark().stringify(ast, file)', function(t) { st.end() function toString(value) { - return String( - unified() - .use(stringify) - .stringify(value) - ) + return String(unified().use(stringify).stringify(value)) } }) t.test( 'emphasis in pedantic mode should support a variety of contained inline content', - function(st) { + function (st) { // Data-driven tests in the format: [name, input, expected] var tests = [ ['words with asterisks', '*inner content*', '_inner content_\n'], @@ -588,7 +546,7 @@ test('remark().stringify(ast, file)', function(t) { ] st.plan(tests.length) - tests.forEach(function(test) { + tests.forEach(function (test) { st.equal( unified() .use(parse) @@ -603,7 +561,7 @@ test('remark().stringify(ast, file)', function(t) { } ) - t.test('should process references with casing properly', function(st) { + t.test('should process references with casing properly', function (st) { // Data-driven tests in the format: [name, value] var tests = [ ['capitalized link references - full', '[alpha][Bravo]'], @@ -611,8 +569,7 @@ test('remark().stringify(ast, file)', function(t) { ['capitalized link references - shortcut', '[Bravo]'], ['capitalized image references - full', '![alpha][Bravo]'], ['capitalized image references - collapsed', '![Bravo][]'], - ['capitalized image references - shortcut', '![Bravo]'], - ['capitalized footnote references', '[^Alpha]'] + ['capitalized image references - shortcut', '![Bravo]'] ] tests.forEach(each) @@ -621,18 +578,14 @@ test('remark().stringify(ast, file)', function(t) { function each(test) { st.equal( - unified() - .use(parse) - .use(stringify) - .processSync(test[1]) - .toString(), + unified().use(parse).use(stringify).processSync(test[1]).toString(), test[1] + '\n', test[0] ) } }) - t.test('should process associations without label', function(st) { + t.test('should process associations without label', function (st) { st.equal( toString({ type: 'definition', @@ -643,21 +596,6 @@ test('remark().stringify(ast, file)', function(t) { 'definition' ) - st.equal( - toString({ - type: 'footnoteDefinition', - identifier: 'a', - children: [ - { - type: 'paragraph', - children: [{type: 'text', value: 'b'}] - } - ] - }), - '[^a]: b', - 'footnote definition' - ) - st.equal( toString({ type: 'linkReference', @@ -678,27 +616,14 @@ test('remark().stringify(ast, file)', function(t) { 'image reference' ) - st.equal( - toString({ - type: 'footnoteReference', - identifier: 'a' - }), - '[^a]', - 'footnote reference' - ) - st.end() function toString(value) { - return String( - unified() - .use(stringify) - .stringify(value) - ) + return String(unified().use(stringify).stringify(value)) } }) - t.test('should support `stringLength`', function(st) { + t.test('should support `stringLength`', function (st) { st.plan(2) var example = [ @@ -709,11 +634,7 @@ test('remark().stringify(ast, file)', function(t) { ].join('\n') st.equal( - unified() - .use(parse) - .use(stringify) - .processSync(example) - .toString(), + unified().use(parse).use(stringify).processSync(example).toString(), [ '| alpha | bravo |', '| ----- | ------- |', @@ -740,7 +661,7 @@ test('remark().stringify(ast, file)', function(t) { ) }) - t.test('should support valid strings', function(st) { + t.test('should support valid strings', function (st) { var compiler = new Compiler() st.equal(compiler.options.listItemIndent, 'tab') compiler.setOptions({listItemIndent: 'mixed'}) @@ -748,7 +669,7 @@ test('remark().stringify(ast, file)', function(t) { st.end() }) - t.test('should support valid numbers', function(st) { + t.test('should support valid numbers', function (st) { var compiler = new Compiler() st.equal(compiler.options.ruleRepetition, 3) compiler.setOptions({ruleRepetition: 5}) @@ -756,15 +677,15 @@ test('remark().stringify(ast, file)', function(t) { st.end() }) - t.test('should support valid booleans', function(st) { + t.test('should support valid booleans', function (st) { var compiler = new Compiler() - st.equal(compiler.options.looseTable, false) - compiler.setOptions({looseTable: true}) - st.equal(compiler.options.looseTable, true) + st.equal(compiler.options.setext, false) + compiler.setOptions({setext: true}) + st.equal(compiler.options.setext, true) st.end() }) - t.test('should support valid enums', function(st) { + t.test('should support valid enums', function (st) { var compiler = new Compiler() st.equal(compiler.options.strong, '*') compiler.setOptions({strong: '_'}) @@ -772,7 +693,7 @@ test('remark().stringify(ast, file)', function(t) { st.end() }) - t.test('should support valid functions', function(st) { + t.test('should support valid functions', function (st) { // Coverage: stringLength() @@ -784,11 +705,8 @@ test('remark().stringify(ast, file)', function(t) { function stringLength() {} }) - t.test('should be able to set options', function(st) { - var processor = unified() - .use(parse) - .use(stringify) - .use(plugin) + t.test('should be able to set options', function (st) { + var processor = unified().use(parse).use(stringify).use(plugin) var tree = processor.parse( ['', '', '# Hello World', ''].join('\n') @@ -821,38 +739,26 @@ test('remark().stringify(ast, file)', function(t) { st.end() }) - t.test('should stringify mailto links properly', function(st) { + t.test('should stringify mailto links properly', function (st) { st.plan(3) var example = '[example@foo.com](mailto:example@foo.com)' st.equal( - unified() - .use(parse) - .use(stringify) - .processSync(example) - .toString(), + unified().use(parse).use(stringify).processSync(example).toString(), example + '\n', 'url is `mailto:` plus link text' ) example = '[mailto:example@foo.com](mailto:example@foo.com)' st.equal( - unified() - .use(parse) - .use(stringify) - .processSync(example) - .toString(), + unified().use(parse).use(stringify).processSync(example).toString(), '\n', 'url is link text' ) example = '[example](mailto:example@foo.com)' st.equal( - unified() - .use(parse) - .use(stringify) - .processSync(example) - .toString(), + unified().use(parse).use(stringify).processSync(example).toString(), example + '\n', 'url is not link text' ) @@ -888,7 +794,7 @@ test('remark().stringify(ast, file)', function(t) { t.end() }) -test('stringify escapes', function(t) { +test('stringify escapes', function (t) { t.equal(toString('a\\b'), 'a\\\\b', '`\\`') t.equal(toString('a`b'), 'a\\`b', '`` ` ``') t.equal(toString('a*b'), 'a\\*b', '`*`') @@ -1356,7 +1262,7 @@ test('stringify escapes', function(t) { function toString(value, options) { var tree = typeof value === 'string' ? u('text', value) : value - visit(tree, function(node) { + visit(tree, function (node) { node.position = uncollapsable }) diff --git a/packages/remark-stringify/types/index.d.ts b/packages/remark-stringify/types/index.d.ts index 20bcbb064..44dc5103a 100644 --- a/packages/remark-stringify/types/index.d.ts +++ b/packages/remark-stringify/types/index.d.ts @@ -11,9 +11,9 @@ declare class RemarkCompiler implements Compiler { } declare namespace remarkStringify { - interface Stringify extends Plugin<[Partial?]> { + interface Stringify extends Plugin<[PartialRemarkStringifyOptions?]> { Compiler: typeof RemarkCompiler - (this: Processor, options?: Partial): void + (this: Processor, options?: PartialRemarkStringifyOptions): void } type Compiler = RemarkCompiler @@ -24,9 +24,8 @@ declare namespace remarkStringify { entities: boolean | 'numbers' | 'escape' setext: boolean closeAtx: boolean - looseTable: boolean - spacedTable: boolean - paddedTable: boolean + tableCellPadding: boolean + tablePipeAlign: boolean stringLength: (s: string) => number fence: '~' | '`' fences: boolean @@ -40,6 +39,8 @@ declare namespace remarkStringify { emphasis: '_' | '*' } + type PartialRemarkStringifyOptions = Partial + type Visitor = (node: Node, parent?: Parent) => string } diff --git a/packages/remark-stringify/types/test.ts b/packages/remark-stringify/types/test.ts index 3908dd7cb..45b3e2d50 100644 --- a/packages/remark-stringify/types/test.ts +++ b/packages/remark-stringify/types/test.ts @@ -1,5 +1,6 @@ import remarkStringify = require('remark-stringify') import unified = require('unified') + import {Node, Parent} from 'unist' const inferredStringifyOptions = { @@ -12,7 +13,7 @@ unified().use(remarkStringify) unified().use(remarkStringify, inferredStringifyOptions) // These cannot be automatically inferred by TypeScript -const nonInferredStringifyOptions: Partial = { +const nonInferredStringifyOptions: remarkStringify.PartialRemarkStringifyOptions = { fence: '~', bullet: '+', listItemIndent: 'tab', @@ -48,3 +49,15 @@ function gap(this: unified.Processor) { } const plugin: unified.Plugin = gap + +const badPartialStringifyOptions: remarkStringify.PartialRemarkStringifyOptions = { + // $ExpectError + gfm: 'true' +} + +// $ExpectError +const incompleteStringifyOptions: remarkStringify.RemarkStringifyOptions = { + gfm: true, + fences: true, + incrementListMarker: false +} diff --git a/packages/remark-stringify/types/tsconfig.json b/packages/remark-stringify/types/tsconfig.json index 91a76bf11..ec6ce3be0 100644 --- a/packages/remark-stringify/types/tsconfig.json +++ b/packages/remark-stringify/types/tsconfig.json @@ -2,10 +2,6 @@ "compilerOptions": { "lib": ["es2015"], "strict": true, - "noImplicitAny": true, - "noImplicitThis": true, - "strictNullChecks": true, - "strictFunctionTypes": true, "baseUrl": ".", "paths": { "remark-stringify": ["index.d.ts"] diff --git a/packages/remark/index.js b/packages/remark/index.js index 148d2de84..e96cfbd82 100644 --- a/packages/remark/index.js +++ b/packages/remark/index.js @@ -4,7 +4,4 @@ var unified = require('unified') var parse = require('remark-parse') var stringify = require('remark-stringify') -module.exports = unified() - .use(parse) - .use(stringify) - .freeze() +module.exports = unified().use(parse).use(stringify).freeze() diff --git a/packages/remark/package.json b/packages/remark/package.json index 40a9883e6..42e4a30ae 100644 --- a/packages/remark/package.json +++ b/packages/remark/package.json @@ -1,7 +1,7 @@ { "name": "remark", - "version": "11.0.2", - "description": "Markdown processor powered by plugins", + "version": "12.0.0", + "description": "Markdown processor powered by plugins part of the unified collective", "license": "MIT", "keywords": [ "unified", @@ -14,6 +14,8 @@ "ast", "parse", "stringify", + "serialize", + "compile", "process" ], "homepage": "https://remark.js.org", @@ -33,9 +35,9 @@ ], "types": "types/index.d.ts", "dependencies": { - "remark-parse": "^7.0.0", - "remark-stringify": "^7.0.0", - "unified": "^8.2.0" + "remark-parse": "^8.0.0", + "remark-stringify": "^8.0.0", + "unified": "^9.0.0" }, "scripts": { "test": "tape test.js" diff --git a/packages/remark/readme.md b/packages/remark/readme.md index 865966e91..f988848ab 100644 --- a/packages/remark/readme.md +++ b/packages/remark/readme.md @@ -8,7 +8,7 @@ [![Backers][backers-badge]][collective] [![Chat][chat-badge]][chat] -[**unified**][unified] processor to parse and compile Markdown. +[**unified**][unified] processor to parse and serialize Markdown. Powered by [plugins][]. Part of the [unified][] collective. @@ -16,7 +16,7 @@ Part of the [unified][] collective. * Parses Markdown to a syntax tree with [`remark-parse`][parse] * [**mdast**][mdast] syntax tree * [Plugins][] transform the tree -* Stringifies syntax trees to Markdown with [`remark-stringify`][stringify] +* Serializes syntax trees to Markdown with [`remark-stringify`][stringify] Don’t need the parser? Or compiler? @@ -28,35 +28,41 @@ Or compiler? - - - - + + + - +
- -

🥇 - ZEIT +
+ Gatsby
🥇

+
- -

🥇 - Gatsby +
+ ZEIT
🥇

+ +
- -

🥇 - Netlify +
+ Netlify
🥇

+ +
- -

- Holloway +
+ Holloway


+ +
+ ThemeIsle
🥉

+
+ + BoostIO
🥉

+ +




- You? + You?
-[**Read more about the unified collective on Medium »**][announcement] - ## Install [npm][]: @@ -170,8 +176,8 @@ Ideas for new plugins and tools can be posted in [`remarkjs/ideas`][ideas]. A curated list of awesome remark resources can be found in [**awesome remark**][awesome]. -This project has a [Code of Conduct][coc]. -By interacting with this repository, organisation, or community you agree to +This project has a [code of conduct][coc]. +By interacting with this repository, organization, or community you agree to abide by its terms. ## License @@ -202,7 +208,7 @@ abide by its terms. [collective]: https://opencollective.com/unified -[chat-badge]: https://img.shields.io/badge/join%20the%20community-on%20spectrum-7b16ff.svg +[chat-badge]: https://img.shields.io/badge/chat-spectrum-7b16ff.svg [chat]: https://spectrum.chat/unified/remark @@ -240,8 +246,6 @@ abide by its terms. [data]: https://github.com/unifiedjs/unified#processordatakey-value -[announcement]: https://medium.com/unifiedjs/collectively-evolving-through-crowdsourcing-22c359ea95cc - [xss]: https://en.wikipedia.org/wiki/Cross-site_scripting [rehype]: https://github.com/rehypejs/rehype diff --git a/packages/remark/test.js b/packages/remark/test.js index 9bcc2dc69..6985457bf 100644 --- a/packages/remark/test.js +++ b/packages/remark/test.js @@ -1,16 +1,11 @@ 'use strict' -/* eslint-disable import/no-extraneous-dependencies */ var test = require('tape') -/* eslint-enable import/no-extraneous-dependencies */ - var remark = require('.') -test('remark().processSync(value)', function(t) { +test('remark().processSync(value)', function (t) { t.equal( - remark() - .processSync('*foo*') - .toString(), + remark().processSync('*foo*').toString(), '_foo_\n', 'should parse and stringify a file' ) @@ -25,16 +20,13 @@ test('remark().processSync(value)', function(t) { ) t.equal( - remark() - .data('settings', {closeAtx: true}) - .processSync('# foo') - .toString(), + remark().data('settings', {closeAtx: true}).processSync('# foo').toString(), '# foo #\n', 'should accept stringify options' ) t.throws( - function() { + function () { remark() .data('settings', {pedantic: true, listItemIndent: '1'}) .processSync(['* List', '', ' code()'].join('\n')) diff --git a/packages/remark/types/index.d.ts b/packages/remark/types/index.d.ts index b14ba670c..70087245e 100644 --- a/packages/remark/types/index.d.ts +++ b/packages/remark/types/index.d.ts @@ -7,10 +7,11 @@ import remarkStringify = require('remark-stringify') declare namespace remark { type RemarkOptions = remarkParse.RemarkParseOptions & remarkStringify.RemarkStringifyOptions + + type PartialRemarkOptions = remarkParse.PartialRemarkParseOptions & + remarkStringify.PartialRemarkStringifyOptions } -declare function remark< - P extends Partial = Partial ->(): unified.Processor

+declare function remark(): unified.Processor export = remark diff --git a/packages/remark/types/test.ts b/packages/remark/types/test.ts index e4ef0e845..8b000913f 100644 --- a/packages/remark/types/test.ts +++ b/packages/remark/types/test.ts @@ -13,3 +13,14 @@ remark().use({settings: {commonmark: true}}) remark().use({settings: {doesNotExist: true}}) // $ExpectError remark().use(plugin, {doesNotExist: 'true'}) + +// $ExpectError +const parseOptions: remark.RemarkOptions = { + gfm: true, + pedantic: true +} + +const badParseOptionsInterface: remark.PartialRemarkOptions = { + // $ExpectError + gfm: 'true' +} diff --git a/packages/remark/types/tsconfig.json b/packages/remark/types/tsconfig.json index c688cea16..2c0015e3d 100644 --- a/packages/remark/types/tsconfig.json +++ b/packages/remark/types/tsconfig.json @@ -2,10 +2,6 @@ "compilerOptions": { "lib": ["es2015"], "strict": true, - "noImplicitAny": true, - "noImplicitThis": true, - "strictNullChecks": true, - "strictFunctionTypes": true, "baseUrl": ".", "paths": { "remark": ["index.d.ts"] diff --git a/readme.md b/readme.md index e26b13ed1..e208b72a3 100644 --- a/readme.md +++ b/readme.md @@ -13,25 +13,33 @@ ## Intro -**remark** is not another Markdown to HTML compiler. -It can generate and reformat Markdown too. -Powered by plugins to do all kinds of things: [check Markdown code -style][remark-lint], [transform safely to React][remark-react], [add a table of -contents][remark-toc], or [compile to man pages][remark-man]. - -* Visit [`unifiedjs.com`][website] and try its [guides][] for an overview +**remark** is [the world’s most popular Markdown parser][popular]! +And it does so much more than parse: it inspects (check, lint) and transforms +(generate, compile) Markdown too! + +Everything is possible with plugins, such as [checking Markdown code +style (`remark-lint`)][remark-lint], [transforming safely to React +(`remark-react`)][remark-react], [adding a table of +contents (`remark-toc`)][remark-toc], or [compiling to man pages +(`remark-man`)][remark-man]. + +On top of that, remark is part of the [unified][website] +[collective][governance]. +Learn more about us: + +* Visit [`unifiedjs.com`][website] and peruse its [Learn][] section for an + overview * Read [unified][]’s readme for a technical intro * Browse [awesome remark][awesome] to find out more about the ecosystem -* Follow us on [Medium][] and [Twitter][] to see what we’re up to +* Follow us on [Twitter][] to see what we’re up to * Check out [Contribute][] below to find out how to help out This repository contains the following projects: -* [`remark-parse`][parse] — Parse Markdown documents to syntax trees -* [`remark-stringify`][stringify] — Stringify syntax trees to Markdown - documents +* [`remark-parse`][parse] — Parse Markdown to syntax trees +* [`remark-stringify`][stringify] — Serialize syntax trees to Markdown * [`remark`][api] — Programmatic interface with both `remark-parse` and `remark-stringify` -* [`remark-cli`][cli] — Command-line interface wrapping `remark` +* [`remark-cli`][cli] — Command line interface wrapping `remark` ## Sponsors @@ -39,35 +47,41 @@ This repository contains the following projects: - + - + + + - - -
- -

🥇 - ZEIT +
+ Gatsby
🥇

+ +
+ ZEIT
🥇

+ +
- -

🥇 - Gatsby +
+ Netlify
🥇

+ + +
+ Holloway


+
- -

🥇 - Netlify +
+ ThemeIsle
🥉

+
- -

- Holloway +
+ BoostIO
🥉

+
+



- You? + You?
-[**Read more about the unified collective on Medium »**][announcement] - ## Security As Markdown is sometimes used for HTML, and improper use of HTML can open you up @@ -89,8 +103,8 @@ Join us in [spectrum][chat] to chat with the community and contributors. A curated list of awesome resources can be found in [**awesome remark**][awesome]. -This project has a [Code of Conduct][coc]. -By interacting with this repository, organisation, or community you agree to +This project has a [code of conduct][coc]. +By interacting with this repository, organization, or community you agree to abide by its terms. ## License @@ -117,7 +131,7 @@ abide by its terms. [size]: https://bundlephobia.com/result?p=remark -[chat-badge]: https://img.shields.io/badge/join%20the%20community-on%20spectrum-7b16ff.svg +[chat-badge]: https://img.shields.io/badge/chat-spectrum-7b16ff.svg [chat]: https://spectrum.chat/unified/remark @@ -125,6 +139,8 @@ abide by its terms. [backers-badge]: https://opencollective.com/unified/backers/badge.svg +[popular]: https://www.npmtrends.com/remark-parse-vs-marked-vs-markdown-it + [api]: https://github.com/remarkjs/remark/tree/master/packages/remark [parse]: https://github.com/remarkjs/remark/tree/master/packages/remark-parse @@ -145,9 +161,9 @@ abide by its terms. [unified]: https://github.com/unifiedjs/unified -[website]: https://unifiedjs.github.io +[website]: https://unifiedjs.com -[guides]: https://unifiedjs.com/#guides +[learn]: https://unifiedjs.com/learn/ [contribute]: #contribute @@ -165,9 +181,7 @@ abide by its terms. [collective]: https://opencollective.com/unified -[medium]: https://medium.com/unifiedjs - -[announcement]: https://medium.com/unifiedjs/collectively-evolving-through-crowdsourcing-22c359ea95cc +[governance]: https://github.com/unifiedjs/governance [twitter]: https://twitter.com/unifiedjs diff --git a/script/list-of-methods.js b/script/list-of-methods.js index 97b715a36..ab7d48b67 100644 --- a/script/list-of-methods.js +++ b/script/list-of-methods.js @@ -26,7 +26,7 @@ function replace(name) { u( 'list', {ordered: false}, - list.map(function(name) { + list.map(function (name) { return u('listItem', [u('paragraph', [u('inlineCode', name)])]) }) ), diff --git a/script/regenerate-fixtures.js b/script/regenerate-fixtures.js index 97727b974..e5eddccc1 100644 --- a/script/regenerate-fixtures.js +++ b/script/regenerate-fixtures.js @@ -5,12 +5,12 @@ var path = require('path') var remark = require('../packages/remark') var fixtures = require('../test/fixtures') -fixtures.forEach(function(fixture) { +fixtures.forEach(function (fixture) { var input = fixture.input var name = fixture.name var mapping = fixture.mapping - Object.keys(mapping).forEach(function(key) { + Object.keys(mapping).forEach(function (key) { var filename = name + (key ? '.' + key : key) + '.json' var result diff --git a/test/fixtures/index.js b/test/fixtures/index.js index 3f4c06c7f..fce44e28a 100644 --- a/test/fixtures/index.js +++ b/test/fixtures/index.js @@ -33,13 +33,13 @@ keys.splice(keys.indexOf('blocks'), 1) var sources = [keys.join('.')] // Create all possible `parse` values. -keys.forEach(function(key) { +keys.forEach(function (key) { sources = [].concat.apply( sources, - sources.map(function(source) { + sources.map(function (source) { return source .split('.') - .map(function(subkey) { + .map(function (subkey) { return subkey === key ? 'no' + key : subkey }) .join('.') @@ -120,7 +120,7 @@ var virtual = {} var physical = {} var all = {} -sources.forEach(function(source) { +sources.forEach(function (source) { var options = parseOptions(source) source = options.source @@ -144,7 +144,7 @@ sources.forEach(function(source) { function difference(options, compare) { var count = 0 - Object.keys(options).forEach(function(key) { + Object.keys(options).forEach(function (key) { if (options[key] !== compare[key]) { count++ } @@ -160,7 +160,7 @@ function resolveFixture(source, fixtures, options) { var resolved var offset - Object.keys(fixtures).forEach(function(key) { + Object.keys(fixtures).forEach(function (key) { offset = difference(options[source], options[key]) if (offset < minimum) { @@ -177,7 +177,7 @@ function resolveFixture(source, fixtures, options) { function resolveFixtures(fixtures, options) { var resolved = {} - Object.keys(options).forEach(function(source) { + Object.keys(options).forEach(function (source) { resolved[source] = resolveFixture(source, fixtures, options) }) @@ -187,10 +187,10 @@ function resolveFixtures(fixtures, options) { // Gather fixtures. var tests = fs .readdirSync(join(__dirname, 'input')) - .filter(function(filepath) { + .filter(function (filepath) { return filepath.indexOf('.') !== 0 }) - .map(function(filepath) { + .map(function (filepath) { var filename = filepath.split('.').slice(0, -1) var name = filename.join('.').replace(/-asterisk-/g, '*') var settings = parseOptions(name) @@ -199,7 +199,7 @@ var tests = fs var possibilities = {} var resolved - Object.keys(all).forEach(function(source) { + Object.keys(all).forEach(function (source) { var treename var tree diff --git a/test/fixtures/input/auto-link-url.text b/test/fixtures/input/auto-link-url.text index 8567d57c9..4b1fc21ee 100644 --- a/test/fixtures/input/auto-link-url.text +++ b/test/fixtures/input/auto-link-url.text @@ -5,3 +5,5 @@ Also, subdomain should be a part of the link (http://foo.example.com/(hello[worl So should this: mailto:foo@bar.com. And even with underscore http://domain.org/this_is_good. + +All links should work http://a.b, https://c.d, http://e.f, https://g.h. diff --git a/test/fixtures/input/footnote-consecutive.text b/test/fixtures/input/footnote-consecutive.text deleted file mode 100644 index 10afb2e29..000000000 --- a/test/fixtures/input/footnote-consecutive.text +++ /dev/null @@ -1,10 +0,0 @@ -# International Radiotelephony Spelling Alphabet[^wiki] - -Here's the NATO phonetic alphabet[^wiki][^wiki2]: Alfa, Bravo, Charlie, Delta, Echo, Foxtrot, Golf, Hotel, India, Juliet, Kilo, Lima, Mike, November, Oscar, Papa, Quebec, Romeo, Sierra, Tango, Uniform, Victor[^name][^consecutive], Whiskey, X-ray, Yankee, and Zulu. - -And here's some more text. - -[^wiki]: Read more about it here. -[^wiki2]: Here's another good article on the subject. -[^name]: A great first name. -[^consecutive]: I know. diff --git a/test/fixtures/input/footnote-duplicate.text b/test/fixtures/input/footnote-duplicate.text deleted file mode 100644 index 4490d03d7..000000000 --- a/test/fixtures/input/footnote-duplicate.text +++ /dev/null @@ -1,4 +0,0 @@ -The NATO phonetic alphabet[^wiki]. - -[^wiki]: Read more about it here. -[^wiki]: And here. diff --git a/test/fixtures/input/footnote-empty.output.text b/test/fixtures/input/footnote-empty.output.text deleted file mode 100644 index d95686c0f..000000000 --- a/test/fixtures/input/footnote-empty.output.text +++ /dev/null @@ -1 +0,0 @@ -This is a document with `footnotes: true`, but not actual footnote definition. diff --git a/test/fixtures/input/footnote-escape.text b/test/fixtures/input/footnote-escape.text deleted file mode 100644 index 0f9039518..000000000 --- a/test/fixtures/input/footnote-escape.text +++ /dev/null @@ -1,3 +0,0 @@ -The NATO phonetic alphabet[^wi\-ki]. - -[^wi\-ki]: Read more about it somewhere else. diff --git a/test/fixtures/input/footnote-ids.text b/test/fixtures/input/footnote-ids.text deleted file mode 100644 index bf3ccf571..000000000 --- a/test/fixtures/input/footnote-ids.text +++ /dev/null @@ -1,3 +0,0 @@ -This example checks that [^the generated] IDs do not overwrite the user's IDs[^1]. - -[^1]: Old behavior would, for "generated", generate a footnote with an ID set to `1`, thus overwriting this footnote. diff --git a/test/fixtures/input/footnote-indent.text b/test/fixtures/input/footnote-indent.text deleted file mode 100644 index ebbaa8eab..000000000 --- a/test/fixtures/input/footnote-indent.text +++ /dev/null @@ -1,3 +0,0 @@ -The NATO phonetic alphabet[^wiki]. - - [^wiki]: Read more about it somewhere else. diff --git a/test/fixtures/input/footnote-inline.text b/test/fixtures/input/footnote-inline.text deleted file mode 100644 index 003962754..000000000 --- a/test/fixtures/input/footnote-inline.text +++ /dev/null @@ -1 +0,0 @@ -This is an example of an inline footnote.[^This is the _actual_ footnote.] diff --git a/test/fixtures/input/footnote-like.text b/test/fixtures/input/footnote-like.text deleted file mode 100644 index c18285be8..000000000 --- a/test/fixtures/input/footnote-like.text +++ /dev/null @@ -1,3 +0,0 @@ -This one isn't even [defined][^foo]. - -[^both][invalid], [^this too][]. diff --git a/test/fixtures/input/footnote-matrix.text b/test/fixtures/input/footnote-matrix.text deleted file mode 100644 index c989646c1..000000000 --- a/test/fixtures/input/footnote-matrix.text +++ /dev/null @@ -1,4 +0,0 @@ -1. [foo][bar] -2. [^foo][bar] -3. [foo][^bar] -4. [^foo][^bar] diff --git a/test/fixtures/input/footnote-multiple.text b/test/fixtures/input/footnote-multiple.text deleted file mode 100644 index 02c12f870..000000000 --- a/test/fixtures/input/footnote-multiple.text +++ /dev/null @@ -1,7 +0,0 @@ -# International Radiotelephony Spelling Alphabet[^wiki] - -Here's the NATO phonetic alphabet[^wiki]: Alfa, Bravo, Charlie, Delta, Echo, Foxtrot, Golf, Hotel, India, Juliet, Kilo, Lima, Mike, November, Oscar, Papa, Quebec, Romeo, Sierra, Tango, Uniform, Victor, Whiskey, X-ray, Yankee, and Zulu. - -And here's some more text. - -[^wiki]: Read more about it here. diff --git a/test/fixtures/input/footnote-nested.text b/test/fixtures/input/footnote-nested.text deleted file mode 100644 index 2b9992c7d..000000000 --- a/test/fixtures/input/footnote-nested.text +++ /dev/null @@ -1,3 +0,0 @@ -A footnote[^1]. - -[^1]: Including [^another **footnote**] diff --git a/test/fixtures/input/footnote-or-reference-label.text b/test/fixtures/input/footnote-or-reference-label.text deleted file mode 100644 index f358b4b68..000000000 --- a/test/fixtures/input/footnote-or-reference-label.text +++ /dev/null @@ -1,5 +0,0 @@ -[alpha][^bravo] - -[^bravo]: Footnote definition. - -[alpha]: https://example.com diff --git a/test/fixtures/input/footnote-proto.text b/test/fixtures/input/footnote-proto.text deleted file mode 100644 index 3818bead4..000000000 --- a/test/fixtures/input/footnote-proto.text +++ /dev/null @@ -1,5 +0,0 @@ -A footnote[^toString] and [^__proto__] and [^constructor]. - -[^toString]: See `Object.prototype.toString()`. -[^constructor]: See `Object.prototype.valueOf()`. -[^__proto__]: See `Object.prototype.__proto__()`. diff --git a/test/fixtures/input/footnote-without-space.text b/test/fixtures/input/footnote-without-space.text deleted file mode 100644 index 898b4272b..000000000 --- a/test/fixtures/input/footnote-without-space.text +++ /dev/null @@ -1,14 +0,0 @@ -foo[^abc] bar. foo[^xyz] bar - -And some more tests for “image like” footnotes, related to: -: - -Hello[^footnote] -Hello![^footnote] -Hello! [^footnote] - -[^abc]: Baz baz - -[^xyz]: Baz - -[^footnote]: Some definition. diff --git a/test/fixtures/input/footnote.output.text b/test/fixtures/input/footnote.output.text deleted file mode 100644 index e647aedc7..000000000 --- a/test/fixtures/input/footnote.output.text +++ /dev/null @@ -1,11 +0,0 @@ -Lorem ipsum dolor sit amet[^1]. - -Nulla finibus[^2] neque et diam rhoncus convallis. - -[^1]: Consectetur **adipiscing** elit. Praesent dictum purus ullamcorper ligula semper pellentesque[^3]. - - - Containing a list. - -[^2]: Nam dictum sapien nec sem ultrices fermentum. Nulla **facilisi**. In et feugiat massa. - -[^3]: Nunc dapibus ipsum ut mi _ultrices_, non euismod velit pretium. diff --git a/test/fixtures/input/footnote.text b/test/fixtures/input/footnote.text deleted file mode 100644 index 73360eaeb..000000000 --- a/test/fixtures/input/footnote.text +++ /dev/null @@ -1,11 +0,0 @@ -Here is some text containing a footnote[^somesamplefootnote]. You can then continue your thought... - -[^somesamplefootnote]: Here is the text of the footnote itself. - -Even go to a new [paragraph] and the footnotes will go to the bottom of the document[^documentdetails]. - -[^documentdetails]: Depending on the **final** form of your document, of course. See the documentation and experiment. - - This footnote has a second [paragraph]. - -[paragraph]: http://example.com diff --git a/test/fixtures/input/isolated-hard-break.text b/test/fixtures/input/isolated-hard-break.text new file mode 100644 index 000000000..b34a1910b --- /dev/null +++ b/test/fixtures/input/isolated-hard-break.text @@ -0,0 +1,2 @@ + +The previous line, which contains 2 spaces, should be considered to be empty. diff --git a/test/fixtures/input/literal-email.text b/test/fixtures/input/literal-email.text new file mode 100644 index 000000000..13f848d6b --- /dev/null +++ b/test/fixtures/input/literal-email.text @@ -0,0 +1,18 @@ +example1 (1234567@example.com) +example1 (mailto:1234567@example.com) + +Lorem foo@bar.baz ipsum. + +alpha@bravo+charlie.delta isn’t valid, but echo+foxtrot@golf.hotel is. + +Valid: a.b-c_d@a.b + +Valid, but the dot is not part of the email: a.b-c_d@a.b. + +Not valid: a.b-c_d@a.b- + +Not valid: a.b-c_d@a.b_ + +Not valid: alpha@bravo. + +<foo@example.com diff --git a/test/fixtures/input/literal-url.text b/test/fixtures/input/literal-url.text new file mode 100644 index 000000000..03ccfde62 --- /dev/null +++ b/test/fixtures/input/literal-url.text @@ -0,0 +1,57 @@ +# Literal URLs + +## Extended www “autolinks” + +Here’s a URL: www.alpha.org. + +Visit www.bravo.org/help for more information. + +Dots cannot appear together: www..one.com and www.two..com. + +And www. is not a URL, neither are www.a nor www.b. + +Underscores cannot be used in the last two domain parts, so www.three.c_m, +and www.fo_ur.com are not URLs, but www.fi_ve.six.com is. + +Valid, and the dot is not part of the link: Visit www.charlie.org. + +Valid, and this dot isn’t part of the link either: Visit www.delta.org/a.b. + +Valid, but two dots are both not part of the URL: www.example.com.. + +Here are parens: www.echo.com/search?q=Markup+(business) + +The last two aren’t part of the +link: www.foxtrot.com/search?q=golf+(hotel))) + +These first and last ones aren’t +either: (www.india.com/search?q=juliett+(kilo)) + +This last one is: (www.lima.com/search?q=mike+(november) + +Paren counting is only done if the last character is a closing +paren: www.google.com/search?q=(business))+ok + +If it “looks” like an entity at the end, it isn’t included. + +This is a whole URL: www.entity.com/search?q=alpha&hl=en + +This is a without the semicolon: www.entity.com/search?q=bravo&; + +This one is without the “entity”: www.entity.com/search?q=charlie&hl; + +This one one too: www.entity.com/search?q=delta© + +Only “named” ones work, numericals don’t, so this one is only without the +semicolon: www.entity.com/search?q=delta∊ + +`<` immediately ends an autolink: www.alpha.org/he