From f0a1e28a31e019f0feec5275f8a95e2ce981e845 Mon Sep 17 00:00:00 2001 From: "Xunnamius (Romulus)" Date: Fri, 21 Oct 2022 16:57:37 -0700 Subject: [PATCH] feat(packages/remark-renumber-references): add remark-renumber-references package --- packages/remark-renumber-references/LICENSE | 21 + packages/remark-renumber-references/README.md | 409 ++++++++++++++++++ .../remark-renumber-references/package.json | 88 ++++ .../remark-renumber-references/src/index.ts | 105 +++++ .../test/fixtures/numbered.md | 48 ++ .../test/fixtures/renumbered-all.md | 58 +++ .../test/fixtures/renumbered.md | 58 +++ .../test/helpers.ts | 12 + .../test/integration-node.test.ts | 236 ++++++++++ .../test/unit-index.test.ts | 32 ++ .../tsconfig.docs.json | 4 + .../tsconfig.lint.json | 4 + .../tsconfig.types.json | 13 + 13 files changed, 1088 insertions(+) create mode 100644 packages/remark-renumber-references/LICENSE create mode 100644 packages/remark-renumber-references/README.md create mode 100644 packages/remark-renumber-references/package.json create mode 100644 packages/remark-renumber-references/src/index.ts create mode 100644 packages/remark-renumber-references/test/fixtures/numbered.md create mode 100644 packages/remark-renumber-references/test/fixtures/renumbered-all.md create mode 100644 packages/remark-renumber-references/test/fixtures/renumbered.md create mode 100644 packages/remark-renumber-references/test/helpers.ts create mode 100644 packages/remark-renumber-references/test/integration-node.test.ts create mode 100644 packages/remark-renumber-references/test/unit-index.test.ts create mode 100644 packages/remark-renumber-references/tsconfig.docs.json create mode 100644 packages/remark-renumber-references/tsconfig.lint.json create mode 100644 packages/remark-renumber-references/tsconfig.types.json diff --git a/packages/remark-renumber-references/LICENSE b/packages/remark-renumber-references/LICENSE new file mode 100644 index 0000000..9580002 --- /dev/null +++ b/packages/remark-renumber-references/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Bernard Dickens + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/remark-renumber-references/README.md b/packages/remark-renumber-references/README.md new file mode 100644 index 0000000..52badd4 --- /dev/null +++ b/packages/remark-renumber-references/README.md @@ -0,0 +1,409 @@ + + + +[![Black Lives Matter!][badge-blm]][link-blm] +[![Maintenance status][badge-maintenance]][link-repo] +[![Last commit timestamp][badge-last-commit]][link-repo] +[![Open issues][badge-issues]][link-issues] +[![Pull requests][badge-pulls]][link-pulls] +[![Codecov][badge-codecov]][link-codecov] +[![Source license][badge-license]][link-license] +[![NPM version][badge-npm]][link-npm] +[![Uses Semantic Release!][badge-semantic-release]][link-semantic-release] + + + + +# remark-renumber-references + +This is a [unified][23] ([remark][24]) plugin that renumbers numeric +reference-style link ids contiguously starting from `[1]` and counting up. Also +plays nicely with [GFM footnotes][6] (by completely ignoring them). + +After running this plugin, _all definitions_, both numeric and alphanumeric, +will always be placed at the very bottom of the document. + +You might also be interested in [remark-reference-links][2], which transforms +all your inline links into reference-style links, and +[remark-sort-definitions][3], which will logically reorder the reference +definitions at the bottom of your document. For a live example of these plugins +in action, check the bottom of [this very README.md file][25]. ✨ + +--- + + + + + + +- [Install](#install) +- [Usage](#usage) + - [Via API](#via-api) + - [Via remark-cli](#via-remark-cli) + - [Via unified configuration](#via-unified-configuration) +- [API](#api) + - [Options](#options) +- [Examples](#examples) +- [Related](#related) +- [Contributing and Support](#contributing-and-support) + - [Contributors](#contributors) + + + + + +## Install + +> Due to the nature of the unified ecosystem, this package is ESM only and +> cannot be `require`'d. + +```bash +npm install --save-dev remark-renumber-references +``` + +## Usage + +### Via API + +```typescript +import { read } from 'to-vfile'; +import { remark } from 'remark'; +import remarkRenumberReferences from 'remark-renumber-references'; + +const file = await remark() + // An options object is NOT required + .use(remarkRenumberReferences, { preserveAlphanumericDefinitions: false }) + .process(await read('example.md')); + +console.log(String(file)); +``` + + + +### Via [remark-cli](https://github.com/remarkjs/remark/tree/main/packages/remark-cli) + +```shell +remark -o --use renumber-references README.md +``` + + + +### Via [unified configuration](https://github.com/unifiedjs/unified-engine/blob/main/doc/configure.md) + +In `package.json`: + +```javascript + /* … */ + "remarkConfig": { + "plugins": [ + "remark-renumber-references" + /* … */ + ] + }, + /* … */ +``` + +In `.remarkrc.js`: + +```javascript +module.exports = { + plugins: [ + // … + ['renumber-references', { preserveAlphanumericDefinitions: false }] + ] +}; +``` + +In `.remarkrc.mjs`: + +```javascript +import remarkRenumberReferences from 'remark-renumber-references'; + +export default { + plugins: [ + // … + remarkRenumberReferences + ] +}; +``` + +## API + +Detailed interface information can be found under [`docs/`][docs]. + +### Options + +This plugin recognizes the following options: + +#### `preserveAlphanumericDefinitions` + +Valid values: `true` | `false`\ +Default: `true` + +If `true`, alphanumeric definition ids (i.e. any id that cannot be parsed into +an integer) will be spared during the renumbering. If `false`, _all_ definition +ids will be deleted and recreated starting from `[1]`. + +## Examples + +Suppose we have the following Markdown file `example.md`: + +```markdown +# Documentation + +This [package](https://npm.im/some-package) is [more than][2nd-half-idiom] meets +the eye. + +- [Install remark](#install) +- [Usage](#usage) +- [API](#api) +- [Related](#related) +- [Contributing and Support](#contributing-and-support) + - [Contributors](#contributors) + +## Install [remark](https://npm.im/remark) + +… + +[2nd-half-idiom]: https://meme-link-2 +``` + +Then running the following JavaScript: + +```typescript +import { read } from 'to-vfile'; +import { remark } from 'remark'; +import remarkReferenceLinks from 'remark-reference-links'; + +const file = await remark() + .use(remarkReferenceLinks) + .process(await read('example.md')); + +console.log(String(file)); +``` + +Would output the following (assuming remark is [configured][4] for tight +references, dash bullets, and singular list item indents): + +```markdown +# Documentation + +This [package][1] is [more than][2nd-half-idiom] meets the eye. + +- [Install remark][2] +- [Usage][3] +- [API][4] +- [Related][5] +- [Contributing and Support][6] + - [Contributors][7] + +## Install [remark][8] + +… + +[2nd-half-idiom]: https://meme-link-2 +[1]: https://npm.im/some-package +[2]: #install +[3]: #usage +[4]: #api +[5]: #related +[6]: #contributing-and-support +[7]: #contributors +[8]: https://npm.im/remark +``` + +Later on, we rewrite sections of `example.md` and remove others (using +[remark-remove-unused-definitions][5] to clear out the unused reference +definitions). + +> Note that, while a side-effect of running this plugin is that unused numeric +> reference definitions are removed during renumbering, this behavior is not +> guaranteed and hence should not be relied upon. To ensure all unused reference +> definitions are always removed, use [remark-remove-unused-definitions][5] > +> _before_ this plugin. + +Rerunning the above JavaScript leaves us with the following output: + + +```markdown +# Documentation + +> Warning: [something][2] to pay attention to. + +This [package][1] is [more than][2nd-half-idiom] [meets the eye][1st-half-idiom]. + +- [Install unified][4] +- [Usage][3] +- [Related][5] +- [Contributing and Support][6] + - [Maintainers][8] + - [Contributors][7] + +## Install [unified][9] + +… + +[2nd-half-idiom]: https://meme-link-2 +[1]: https://npm.im/some-package +[3]: #usage +[5]: #related +[6]: #contributing-and-support +[7]: #contributors +[1st-half-idiom]: https://meme-link-1 +[2]: https://something-or-other +[4]: #install +[8]: #maintainers +[9]: https://npm.im/unified +``` + +This might be good enough when run through a Markdown renderer where the end +user is not exposed to the reference numbers, but what about the humans reading +the plain text document itself? + +In the words of one of Markdown's creators: + +> Markdown allows you to write using an _easy-to-read_, easy-to-write _plain +> text_ format ... The overriding design goal for Markdown’s formatting syntax +> is to _make it as readable as possible_. + +What's "easy to read" is subjective. For those who find it bothersome or +distracting reading Markdown documents containing reference links with integer +ids that hop around in a random order, this is the plugin for you. + +Suppose instead we ran the following JavaScript: + +```typescript +import { read } from 'to-vfile'; +import { remark } from 'remark'; +import remarkReferenceLinks from 'remark-reference-links'; +import remarkRenumberReferences from 'remark-renumber-references'; + +const file = await remark() + .use(remarkReferenceLinks) + // It is important that this plugin is loaded AFTER any plugins that + // *manipulate* or *remove* links, reference definitions, and/or their ids + .use(remarkRenumberReferences) + // However, this plugin should be loaded BEFORE any plugins that *sort* + // reference definitions + .process(await read('example.md')); + +console.log(String(file)); +``` + +Then we would get the following output: + + +```markdown +# Documentation + +> Warning: [something][1] to pay attention to. + +This [package][2] is [more than][2nd-half-idiom] [meets the eye][1st-half-idiom]. + +- [Install unified][3] +- [Usage][4] +- [Related][5] +- [Contributing and Support][6] + - [Maintainers][7] + - [Contributors][8] + +## Install [unified][9] + +… + +[2nd-half-idiom]: https://meme-link-2 +[1st-half-idiom]: https://meme-link-1 +[1]: https://something-or-other +[2]: https://npm.im/some-package +[3]: #install +[4]: #usage +[5]: #related +[6]: #contributing-and-support +[7]: #maintainers +[8]: #contributors +[9]: https://npm.im/unified +``` + +Now all the numeric reference ids flow through the document in ascending order +starting from `[1]`. Nice! + +Finally, notice how those reference definitions at the end (specifically the +alphanumeric ids) are unordered. Luckily, there exists [a remark plugin][3] that +will sort all your definitions in whatever order you choose. + +## Related + +- [remark-reference-links][2] — transform inline links into reference-style + links +- [remark-sort-definitions][3] — logically reorder reference definitions at the + bottom of your document +- [remark-remove-unused-definitions][5] — remove unused reference definitions + +## Contributing and Support + +**[New issues][choose-new-issue] and [pull requests][pr-compare] are always +welcome and greatly appreciated! 🤩** Just as well, you can [star 🌟 this +project][link-repo] to let me know you found it useful! ✊🏿 Thank you! + +See [CONTRIBUTING.md][contributing] and [SUPPORT.md][support] for more +information. + +### Contributors + + + +[badge-blm]: https://xunn.at/badge-blm 'Join the movement!' +[link-blm]: https://xunn.at/donate-blm +[badge-maintenance]: + https://img.shields.io/maintenance/active/2022 + 'Is this package maintained?' +[link-repo]: + https://github.com/xunnamius/unified-utils/blob/main/packages/remark-renumber-references +[badge-last-commit]: + https://img.shields.io/github/last-commit/xunnamius/unified-utils + 'Latest commit timestamp' +[badge-issues]: + https://img.shields.io/github/issues/Xunnamius/unified-utils + 'Open issues' +[link-issues]: https://github.com/Xunnamius/unified-utils/issues?q= +[badge-pulls]: + https://img.shields.io/github/issues-pr/xunnamius/unified-utils + 'Open pull requests' +[link-pulls]: https://github.com/xunnamius/unified-utils/pulls +[badge-codecov]: + https://codecov.io/gh/Xunnamius/unified-utils/branch/main/graph/badge.svg?token=HWRIOBAAPW + 'Is this package well-tested?' +[link-codecov]: https://codecov.io/gh/Xunnamius/unified-utils +[badge-license]: + https://img.shields.io/npm/l/remark-renumber-references + "This package's source license" +[link-license]: + https://github.com/Xunnamius/unified-utils/blob/main/packages/remark-renumber-references/LICENSE +[badge-npm]: + https://api.ergodark.com/badges/npm-pkg-version/remark-renumber-references + 'Install this package using npm or yarn!' +[link-npm]: https://www.npmjs.com/package/remark-renumber-references +[badge-semantic-release]: + https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg + 'This repo practices continuous integration and deployment!' +[link-semantic-release]: https://github.com/semantic-release/semantic-release +[docs]: docs +[choose-new-issue]: https://github.com/xunnamius/unified-utils/issues/new/choose +[pr-compare]: https://github.com/xunnamius/unified-utils/compare +[contributing]: /CONTRIBUTING.md +[support]: /.github/SUPPORT.md +[1]: https://github.com/thlorenz/doctoc +[12]: https://github.com/all-contributors/all-contributors +[23]: https://github.com/unifiedjs/unified +[24]: https://github.com/remarkjs/remark +[25]: + https://raw.githubusercontent.com/Xunnamius/unified-utils/main/packages/remark-renumber-references/README.md +[26]: /packages/remark-ignore +[27]: /packages/mdast-util-renumber-references/README.md#usage +[28]: https://github.com/unifiedjs/unified#overview +[29]: /packages/mdast-util-renumber-references +[2]: https://github.com/remarkjs/remark-reference-links +[3]: /packages/remark-sort-definitions +[4]: /.remarkrc.mjs +[5]: /packages/remark-remove-unused-definitions +[6]: https://github.com/remarkjs/remark-gfm#what-is-this diff --git a/packages/remark-renumber-references/package.json b/packages/remark-renumber-references/package.json new file mode 100644 index 0000000..f5f2adb --- /dev/null +++ b/packages/remark-renumber-references/package.json @@ -0,0 +1,88 @@ +{ + "name": "remark-renumber-references", + "version": "0.0.0-semantic", + "description": "remark plugin that renumbers numeric reference-style link ids contiguously starting from [1]", + "keywords": [ + "unified", + "mdast", + "remark", + "remark-plugin", + "plugin", + "markdown", + "link", + "image", + "reference", + "definition", + "number", + "renumber", + "start", + "from", + "one" + ], + "homepage": "https://github.com/Xunnamius/unified-utils/blob/main/packages/remark-renumber-references", + "repository": { + "type": "git", + "url": "https://github.com/Xunnamius/unified-utils" + }, + "license": "MIT", + "author": "Xunnamius", + "sideEffects": false, + "type": "commonjs", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "node": "./dist/index.mjs", + "default": "./dist/index.mjs" + }, + "./package": "./package.json", + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "*": [ + "./dist/index.d.ts" + ] + } + }, + "files": [ + "/dist", + "/LICENSE", + "/package.json", + "/README.md" + ], + "scripts": { + "build": "npm run build:dist --", + "build:changelog": "conventional-changelog --outfile CHANGELOG.md --config ../../conventional.config.js --release-count 0 --skip-unstable && (if [ \"$CHANGELOG_SKIP_TITLE\" != 'true' ]; then { node -e 'console.log(require(\"../../conventional.config.js\").changelogTitle)'; cat CHANGELOG.md; } > CHANGELOG.md.ignore && mv CHANGELOG.md.ignore CHANGELOG.md; fi) && NODE_ENV=format remark --output --frail CHANGELOG.md && prettier --write CHANGELOG.md", + "build:dist": "NODE_ENV=production tsc --project tsconfig.types.json --incremental false && tsconfig-replace-paths --project tsconfig.types.json && NODE_ENV=production-esm babel src --extensions .ts --out-dir dist --out-file-extension .mjs --root-mode upward", + "build:docs": "if [ -r ./next.config.js ]; then typedoc --plugin typedoc-plugin-markdown --tsconfig tsconfig.docs.json --out docs --readme none lib src test types external-scripts --exclude '**/*.test.*' --exclude external-scripts/bin; else ENTRY=`node -e 'const entry = require(\"./package.json\").config?.[\"plugin-build\"]?.docs?.entry; if(!entry) throw new Error(\"\\\"config['\"'\"'plugin-build'\"'\"'].docs.entry\\\" field is not defined in package.json\"); console.log(entry)'` && echo 'Entry file:' \"$ENTRY\" && typedoc --plugin typedoc-plugin-markdown --tsconfig tsconfig.docs.json --out docs --readme none $(echo $ENTRY) && find docs -name '*.md' -exec sed -i -e 's/Project: //g' {} + && sed -i -e 1,4d docs/README.md; fi && find docs -name '*.md' -exec sed -i -e 's/`__namedParameters`/`\\(destructured\\)`/g' {} + && find docs -name '*.md' -exec sed -i -E 's/`__namedParameters\\.([^`]+)`/`\\({ \\1 }\\)`/g' {} +", + "clean": "git ls-files --exclude-standard --ignored --others --directory | grep -vE '^((\\.(env|vscode|husky))|next-env\\.d\\.ts|node_modules)($|\\/)' | xargs -p rm -rf", + "format": "cd ../.. && npm run format", + "lint": "echo 'IMPLEMENT ME'", + "list-tasks": "node -e 'console.log(Object.keys(require(\"./package.json\").scripts).join(\"\\n\"))'", + "test": "npm run test:unit --", + "test:integration": "echo 'IMPLEMENT ME'", + "test:unit": "echo 'IMPLEMENT ME'" + }, + "config": { + "plugin-build": { + "docs": { + "entry": "./src/*" + } + } + }, + "dependencies": { + "@types/mdast": "^3.0.10", + "mdast-util-hidden": "^1.0.1", + "remark-inline-links": "^6.0.1", + "remark-reference-links": "^6.0.1", + "unified": "^10.1.2", + "unist-util-remove-position": "^4.0.1", + "unist-util-visit": "^4.1.1" + }, + "engines": { + "node": "^14.19.0 || ^16.13.0 || >=17.4.0" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/packages/remark-renumber-references/src/index.ts b/packages/remark-renumber-references/src/index.ts new file mode 100644 index 0000000..ca84365 --- /dev/null +++ b/packages/remark-renumber-references/src/index.ts @@ -0,0 +1,105 @@ +import assert from 'node:assert'; +import { visit, SKIP } from 'unist-util-visit'; +import { hide, visitAndReveal } from 'mdast-util-hidden'; +import { removePosition } from 'unist-util-remove-position'; +import remarkInlineLinks from 'remark-inline-links'; +import remarkReferenceLinks from 'remark-reference-links'; + +import type { Plugin } from 'unified'; +import type { Root, Definition } from 'mdast'; + +/** + * Options type for the remark-renumber-references plugin. + */ +export type Options = { + /** + * If `true`, alphanumeric definition ids (i.e. any id that cannot be parsed + * into an integer) will be spared during the renumbering. If `false`, _all_ + * definition ids will be deleted and recreated starting from `[1]`. + * + * @default true + */ + preserveAlphanumericDefinitions: boolean; +}; + +/** + * A remark plugin that takes a Root node as input and returns the same node + * with all reference-style link nodes and their definition nodes renumbered in + * ascending order starting from `[1]`. + * + * Links are assigned a numeric reference id as they are encountered. + */ +const remarkRenumberReferences: Plugin<[options: Options] | void[], Root> = function ( + { preserveAlphanumericDefinitions = true } = {} as Options, + ..._ignored +) { + return async (tree, file) => { + const definitions: Definition[] = []; + + if (preserveAlphanumericDefinitions) { + hideAlphanumericReferenceLinks(); + } + + await convertNonHiddenReferenceLinksToInlineLinks(); + + if (preserveAlphanumericDefinitions) { + revealHiddenReferenceLinks(); + appendRevealedReferenceLinkDefinitionsToDocument(); + } + + await convertInlineLinksToNumericReferenceLinks(); + + function hideAlphanumericReferenceLinks() { + visit( + tree, + ['imageReference', 'linkReference', 'definition'], + (node, index, parent) => { + assert( + node.type === 'imageReference' || + node.type === 'linkReference' || + node.type === 'definition', + `unexpected node type ${node.type}` + ); + + assert(index !== null, 'index is missing'); + assert(parent !== null, 'parent is missing'); + + if (isAlphanumeric(node.identifier)) { + if (node.type == 'definition') { + definitions.push(removePosition(node)); + } else { + hide({ nodes: [node], index, parent }); + return [SKIP, index + 1]; + } + } + } + ); + } + + async function convertNonHiddenReferenceLinksToInlineLinks() { + const transformer = remarkInlineLinks(); + assert(transformer, 'remark-inline-links did not return a transformer'); + await transformer(tree, file, () => undefined); + } + + function revealHiddenReferenceLinks() { + visitAndReveal({ tree }); + } + + function appendRevealedReferenceLinkDefinitionsToDocument() { + tree.children.push(...definitions); + } + + async function convertInlineLinksToNumericReferenceLinks() { + const transformer = remarkReferenceLinks(); + assert(transformer, 'remark-reference-links did not return a transformer'); + await transformer(tree, file, () => undefined); + } + + function isAlphanumeric(identifier: string) { + return identifier && !/^\p{Decimal_Number}+$/u.test(identifier); + } + }; +}; + +export default remarkRenumberReferences; diff --git a/packages/remark-renumber-references/test/fixtures/numbered.md b/packages/remark-renumber-references/test/fixtures/numbered.md new file mode 100644 index 0000000..ef12ac2 --- /dev/null +++ b/packages/remark-renumber-references/test/fixtures/numbered.md @@ -0,0 +1,48 @@ +# Documentation + +> Warning: [something][2] to pay attention to. + +This [package][1] is [more than][2nd-half-idiom] [meets the eye][1st-half-idiom]. + +- [Install unified][4] +- [Usage][3] +- [Related][5] +- [Contributing and Support][6] + - [Maintainers][8] + - [Contributors][7] + +## Footnote + +A note[^1] + +[^1]: Big note. + +## Strikethrough + +~one~ or ~~two~~ tildes. + +## Table + +| a | b | c | d | +| - | :- | -: | :-: | + +## Tasklist + +* [ ] to do +* [x] done + +## Install [unified][9] + +… + +[2nd-half-idiom]: https://meme-link-2 +[1]: https://npm.im/some-package +[3]: #usage +[5]: #related +[6]: #contributing-and-support +[7]: #contributors +[1st-half-idiom]: https://meme-link-1 +[2]: https://something-or-other +[4]: #install +[8]: #maintainers +[9]: https://npm.im/unified diff --git a/packages/remark-renumber-references/test/fixtures/renumbered-all.md b/packages/remark-renumber-references/test/fixtures/renumbered-all.md new file mode 100644 index 0000000..9f61001 --- /dev/null +++ b/packages/remark-renumber-references/test/fixtures/renumbered-all.md @@ -0,0 +1,58 @@ +# Documentation + +> Warning: [something][1] to pay attention to. + +This [package][2] is [more than][3] [meets the eye][4]. + +* [Install unified][5] +* [Usage][6] +* [Related][7] +* [Contributing and Support][8] + * [Maintainers][9] + * [Contributors][10] + +## Footnote + +A note[^1] + +[^1]: Big note. + +## Strikethrough + +~~one~~ or ~~two~~ tildes. + +## Table + +| a | b | c | d | +| - | :- | -: | :-: | + +## Tasklist + +* [ ] to do +* [x] done + +## Install [unified][11] + +… + +[1]: https://something-or-other + +[2]: https://npm.im/some-package + +[3]: https://meme-link-2 + +[4]: https://meme-link-1 + +[5]: #install + +[6]: #usage + +[7]: #related + +[8]: #contributing-and-support + +[9]: #maintainers + +[10]: #contributors + +[11]: https://npm.im/unified diff --git a/packages/remark-renumber-references/test/fixtures/renumbered.md b/packages/remark-renumber-references/test/fixtures/renumbered.md new file mode 100644 index 0000000..60322b6 --- /dev/null +++ b/packages/remark-renumber-references/test/fixtures/renumbered.md @@ -0,0 +1,58 @@ +# Documentation + +> Warning: [something][1] to pay attention to. + +This [package][2] is [more than][2nd-half-idiom] [meets the eye][1st-half-idiom]. + +* [Install unified][3] +* [Usage][4] +* [Related][5] +* [Contributing and Support][6] + * [Maintainers][7] + * [Contributors][8] + +## Footnote + +A note[^1] + +[^1]: Big note. + +## Strikethrough + +~~one~~ or ~~two~~ tildes. + +## Table + +| a | b | c | d | +| - | :- | -: | :-: | + +## Tasklist + +* [ ] to do +* [x] done + +## Install [unified][9] + +… + +[2nd-half-idiom]: https://meme-link-2 + +[1st-half-idiom]: https://meme-link-1 + +[1]: https://something-or-other + +[2]: https://npm.im/some-package + +[3]: #install + +[4]: #usage + +[5]: #related + +[6]: #contributing-and-support + +[7]: #maintainers + +[8]: #contributors + +[9]: https://npm.im/unified diff --git a/packages/remark-renumber-references/test/helpers.ts b/packages/remark-renumber-references/test/helpers.ts new file mode 100644 index 0000000..9939f5b --- /dev/null +++ b/packages/remark-renumber-references/test/helpers.ts @@ -0,0 +1,12 @@ +import { readFileSync as readToString } from 'fs'; +import { read as readToVFile } from 'to-vfile'; + +export function getFixtureString(fixture: string, { trim = false } = {}) { + return readToString(`${__dirname}/fixtures/${fixture}.md`, 'utf8')[ + trim ? 'trim' : 'toString' + ](); +} + +export function getFixtureVFile(fixture: string) { + return readToVFile(`${__dirname}/fixtures/${fixture}.md`); +} diff --git a/packages/remark-renumber-references/test/integration-node.test.ts b/packages/remark-renumber-references/test/integration-node.test.ts new file mode 100644 index 0000000..bb45a11 --- /dev/null +++ b/packages/remark-renumber-references/test/integration-node.test.ts @@ -0,0 +1,236 @@ +import assert from 'node:assert'; +import { debugFactory } from 'multiverse/debug-extended'; +import { run } from 'multiverse/run'; +import { getFixtureString } from 'pkgverse/remark-renumber-references/test/helpers'; +import { name as pkgName, exports as pkgExports } from '../package.json'; + +import { + mockFixtureFactory, + dummyFilesFixture, + dummyNpmPackageFixture, + npmCopySelfFixture, + nodeImportTestFixture, + runTestFixture +} from 'testverse/setup'; + +// TODO: note that we've made some modifications to the setup.ts file that +// TODO: should be propagated! + +const TEST_IDENTIFIER = 'integration-node'; +const debug = debugFactory(`${pkgName}:${TEST_IDENTIFIER}`); + +const pkgMainPaths = Object.values(pkgExports) + .map((xport) => + typeof xport == 'string' ? null : `${__dirname}/../${xport.node || xport.default}` + ) + .filter(Boolean) as string[]; + +const withMockedFixture = mockFixtureFactory(TEST_IDENTIFIER, { + performCleanup: true, + pkgRoot: `${__dirname}/..`, + pkgName, + use: [ + dummyNpmPackageFixture(), + dummyFilesFixture(), + npmCopySelfFixture(), + runTestFixture() + ], + npmInstall: ['remark', 'remark-cli', 'remark-gfm'] +}); + +beforeAll(async () => { + debug('pkgMainPaths: %O', pkgMainPaths); + + await Promise.all( + pkgMainPaths.map(async (pkgMainPath) => { + if ((await run('test', ['-e', pkgMainPath])).code != 0) { + debug(`unable to find main distributable: ${pkgMainPath}`); + throw new Error('must build distributables first (try `npm run build:dist`)'); + } + }) + ); +}); + +describe('via api', () => { + it('works as an ESM import', async () => { + expect.hasAssertions(); + + await withMockedFixture( + async (ctx) => { + assert(ctx.testResult, 'must use node-import-test fixture'); + expect(ctx.testResult?.stderr).toBeEmpty(); + expect(ctx.testResult?.stdout).toBe(getFixtureString('renumbered-all')); + expect(ctx.testResult?.code).toBe(0); + }, + { + initialFileContents: { + 'src/index.mjs': ` + import { remark } from 'remark'; + import remarkGfm from 'remark-gfm'; + import remarkRenumberReferences from 'remark-renumber-references'; + + const file = await remark() + .use(remarkGfm) + .use(remarkRenumberReferences, { preserveAlphanumericDefinitions: false }) + .process(${JSON.stringify(getFixtureString('numbered'))}); + + console.log(String(file)); + ` + }, + use: [ + dummyNpmPackageFixture(), + dummyFilesFixture(), + npmCopySelfFixture(), + nodeImportTestFixture() + ] + } + ); + }); +}); + +describe('via remark-cli inline configuration', () => { + it('works with --use option', async () => { + expect.hasAssertions(); + + await withMockedFixture( + async (ctx) => { + assert(ctx.testResult, 'must use run-test-test fixture'); + expect(ctx.testResult?.stderr).toMatch(/^.*README\.md.*: no issues found$/); + expect(ctx.testResult?.stdout).toBe( + getFixtureString('renumbered', { trim: true }) + ); + expect(ctx.testResult?.code).toBe(0); + }, + { + initialFileContents: { 'README.md': getFixtureString('numbered') }, + runWith: { + binary: 'npx', + args: [ + '--no-install', + 'remark', + '--use', + 'remark-gfm', + '--use', + 'remark-renumber-references', + 'README.md' + ] + } + } + ); + }); +}); + +describe('via remark-cli unified configuration', () => { + it('works with package.json (short-string)', async () => { + expect.hasAssertions(); + + await withMockedFixture( + async (ctx) => { + assert(ctx.testResult, 'must use run-test-test fixture'); + expect(ctx.testResult?.stderr).toMatch(/^.*README\.md.*: no issues found$/); + expect(ctx.testResult?.stdout).toBe( + getFixtureString('renumbered', { trim: true }) + ); + expect(ctx.testResult?.code).toBe(0); + }, + { + initialFileContents: { + 'README.md': getFixtureString('numbered'), + 'package.json': JSON.stringify({ + name: 'dummy-pkg', + remarkConfig: { plugins: ['gfm', 'renumber-references'] } + }) + }, + runWith: { + binary: 'npx', + args: ['--no-install', 'remark', 'README.md'] + } + } + ); + }); + + it('works with package.json (string)', async () => { + expect.hasAssertions(); + + await withMockedFixture( + async (ctx) => { + assert(ctx.testResult, 'must use run-test-test fixture'); + expect(ctx.testResult?.stderr).toMatch(/^.*README\.md.*: no issues found$/); + expect(ctx.testResult?.stdout).toBe( + getFixtureString('renumbered', { trim: true }) + ); + expect(ctx.testResult?.code).toBe(0); + }, + { + initialFileContents: { + 'README.md': getFixtureString('numbered'), + 'package.json': JSON.stringify({ + name: 'dummy-pkg', + remarkConfig: { plugins: ['remark-gfm', 'remark-renumber-references'] } + }) + }, + runWith: { + binary: 'npx', + args: ['--no-install', 'remark', 'README.md'] + } + } + ); + }); + + it('works with .remarkrc.js (string)', async () => { + expect.hasAssertions(); + + await withMockedFixture( + async (ctx) => { + assert(ctx.testResult, 'must use run-test-test fixture'); + expect(ctx.testResult?.stderr).toMatch(/^.*README\.md.*: no issues found$/); + expect(ctx.testResult?.stdout).toBe( + getFixtureString('renumbered', { trim: true }) + ); + expect(ctx.testResult?.code).toBe(0); + }, + { + initialFileContents: { + 'README.md': getFixtureString('numbered'), + '.remarkrc.js': ` + module.exports = { + plugins: ['remark-gfm', 'remark-renumber-references'] + }; + ` + }, + runWith: { + binary: 'npx', + args: ['--no-install', 'remark', 'README.md'] + } + } + ); + }); + + it('works with .remarkrc.mjs (function)', async () => { + expect.hasAssertions(); + + await withMockedFixture( + async (ctx) => { + assert(ctx.testResult, 'must use run-test-test fixture'); + expect(ctx.testResult?.stderr).toMatch(/^.*README\.md.*: no issues found$/); + expect(ctx.testResult?.stdout).toBe( + getFixtureString('renumbered', { trim: true }) + ); + expect(ctx.testResult?.code).toBe(0); + }, + { + initialFileContents: { + 'README.md': getFixtureString('numbered'), + '.remarkrc.mjs': ` + import remarkRenumberReferences from 'remark-renumber-references'; + export default { plugins: ['gfm', remarkRenumberReferences] }; + ` + }, + runWith: { + binary: 'npx', + args: ['--no-install', 'remark', 'README.md'] + } + } + ); + }); +}); diff --git a/packages/remark-renumber-references/test/unit-index.test.ts b/packages/remark-renumber-references/test/unit-index.test.ts new file mode 100644 index 0000000..22a1296 --- /dev/null +++ b/packages/remark-renumber-references/test/unit-index.test.ts @@ -0,0 +1,32 @@ +import { remark } from 'remark'; +import remarkGfm from 'remark-gfm'; +import remarkRenumberReferences from 'pkgverse/remark-renumber-references/src/index'; + +import { + getFixtureString, + getFixtureVFile +} from 'pkgverse/remark-renumber-references/test/helpers'; + +describe('::default', () => { + it('renumbers references and preserves alphanumeric definitions by default while playing nice with GFM', async () => { + expect.hasAssertions(); + + const result = await remark() + .use(remarkGfm) + .use(remarkRenumberReferences) + .process(await getFixtureVFile('numbered')); + + expect(result.toString()).toStrictEqual(getFixtureString('renumbered')); + }); + + it('renumbers references without preserving alphanumeric definitions if so configured', async () => { + expect.hasAssertions(); + + const result = await remark() + .use(remarkGfm) + .use(remarkRenumberReferences, { preserveAlphanumericDefinitions: false }) + .process(await getFixtureVFile('numbered')); + + expect(result.toString()).toStrictEqual(getFixtureString('renumbered-all')); + }); +}); diff --git a/packages/remark-renumber-references/tsconfig.docs.json b/packages/remark-renumber-references/tsconfig.docs.json new file mode 100644 index 0000000..07dd331 --- /dev/null +++ b/packages/remark-renumber-references/tsconfig.docs.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["../../types/**/*", "src/**/*"] +} diff --git a/packages/remark-renumber-references/tsconfig.lint.json b/packages/remark-renumber-references/tsconfig.lint.json new file mode 100644 index 0000000..07dd331 --- /dev/null +++ b/packages/remark-renumber-references/tsconfig.lint.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["../../types/**/*", "src/**/*"] +} diff --git a/packages/remark-renumber-references/tsconfig.types.json b/packages/remark-renumber-references/tsconfig.types.json new file mode 100644 index 0000000..c5793eb --- /dev/null +++ b/packages/remark-renumber-references/tsconfig.types.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "allowJs": false, + "declaration": true, + "emitDeclarationOnly": true, + "isolatedModules": false, + "noEmit": false, + "outDir": "dist", + "rootDir": "./src" + }, + "extends": "../../tsconfig.json", + "include": ["../../types/**/*", "src/**/*"] +}