diff --git a/CHANGELOG.md b/CHANGELOG.md index d1514c8..e92c69f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] +### Added + +- Added support for TOML frontmatter (@bryanfriedman) + ## [4.2.0] - 2020-04-02 ### Added diff --git a/README.md b/README.md index dbdb100..04fe30f 100644 --- a/README.md +++ b/README.md @@ -133,7 +133,7 @@ Spellchecker CLI performs some preprocessing on Markdown files (_i.e._ files wit ### Frontmatter -Spellchecker CLI can parse Markdown frontmatter when the `frontmatter` plugin is used. The `--frontmatter-keys` option can be used to specify a list of top-level keys to extract from the frontmatter. Other top-level keys will be ignored. This is useful for spellchecking only certain parts of the frontmatter. +Spellchecker CLI can parse Markdown frontmatter when the `frontmatter` plugin is used. The `--frontmatter-keys` option can be used to specify a list of top-level keys to extract from the frontmatter. Other top-level keys will be ignored. This is useful for spellchecking only certain parts of the frontmatter. Both YAML and TOML formats are supported using the `frontmatter` plugin, so the Markdown files you are checking may have a mix of files that use either. ### Reports diff --git a/dictionary.txt b/dictionary.txt index 26120ce..40aecf8 100644 --- a/dictionary.txt +++ b/dictionary.txt @@ -16,3 +16,5 @@ Gitlab JSON JUnit Gitignore +TOML +YAML diff --git a/lib/frontmatter-filter.js b/lib/frontmatter-filter.js index d0ce8b3..5a4cf45 100644 --- a/lib/frontmatter-filter.js +++ b/lib/frontmatter-filter.js @@ -1,4 +1,5 @@ const yaml = require('js-yaml'); +const toml = require('toml'); const isArray = require('lodash/isArray'); const isObject = require('lodash/isObject'); const map = require('lodash/map'); @@ -32,6 +33,23 @@ function attacher(options) { const filteredFrontmatter = pick(parsedFrontmatter, options || []); + /* eslint-disable no-param-reassign */ + node.value = stringify(filteredFrontmatter); + node.type = 'text'; + /* eslint-enable no-param-reassign */ + }); + visit(tree, 'toml', (node) => { + let parsedFrontmatter; + + try { + parsedFrontmatter = toml.parse(node.value); + } catch (e) { + printError(`Failed to parse TOML frontmatter, ignoring it. Error: ${e}`); + parsedFrontmatter = {}; + } + + const filteredFrontmatter = pick(parsedFrontmatter, options || []); + /* eslint-disable no-param-reassign */ node.value = stringify(filteredFrontmatter); node.type = 'text'; diff --git a/lib/spellchecker.js b/lib/spellchecker.js index 987fdc8..02734e0 100644 --- a/lib/spellchecker.js +++ b/lib/spellchecker.js @@ -58,7 +58,7 @@ function buildMarkdownSpellchecker({ const frontmatterOptions = plugins.filter(({ frontmatter: fm }) => fm); if (frontmatterOptions.length > 0) { markdownSpellchecker - .use(frontmatter, 'yaml') + .use(frontmatter, ['yaml', 'toml']) .use(frontmatterFilter, frontmatterOptions[0].frontmatter); } diff --git a/package-lock.json b/package-lock.json index 2527d3d..3b53ea1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1920,9 +1920,9 @@ } }, "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" }, "mixin-deep": { "version": "1.3.2", @@ -1944,11 +1944,11 @@ } }, "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", "requires": { - "minimist": "0.0.8" + "minimist": "^1.2.5" } }, "mocha": { @@ -1976,6 +1976,21 @@ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, "supports-color": { "version": "5.4.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", @@ -3136,6 +3151,11 @@ "repeat-string": "^1.6.1" } }, + "toml": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz", + "integrity": "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==" + }, "trim": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz", diff --git a/package.json b/package.json index 9fe30e1..f993f65 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "retext-spell": "^2.4.1", "retext-syntax-mentions": "^1.1.6", "retext-syntax-urls": "^1.0.2", + "toml": "^3.0.0", "unist-util-visit": "^1.4.1", "vfile": "^3.0.1", "vfile-reporter": "^5.1.2" diff --git a/test/cli-test.js b/test/cli-test.js index 08b88be..36e1d66 100644 --- a/test/cli-test.js +++ b/test/cli-test.js @@ -264,6 +264,11 @@ parallel('Spellchecker CLI', function testSpellcheckerCLI() { result.should.not.have.property('code'); }); + it('ignores the frontmatter if no keys are given (toml)', async () => { + const result = await runWithArguments('test/fixtures/frontmatter-incorrect-toml.md -p spell frontmatter'); + result.should.not.have.property('code'); + }); + it('checks only the given keys in the frontmatter (1)', async () => { const { code, stdout } = await runWithArguments('test/fixtures/frontmatter-incorrect.md -p spell frontmatter --frontmatter-keys contributors'); code.should.equal(1); @@ -285,6 +290,27 @@ parallel('Spellchecker CLI', function testSpellcheckerCLI() { stdout.should.include('`tbroadley` is misspelt'); }); + it('checks only the given keys in the frontmatter (toml) (1)', async () => { + const { code, stdout } = await runWithArguments('test/fixtures/frontmatter-incorrect-toml.md -p spell frontmatter --frontmatter-keys contributors'); + code.should.equal(1); + stdout.should.include('`tbroadley` is misspelt'); + stdout.should.not.include('`documnet` is misspelt'); + }); + + it('checks only the given keys in the frontmatter (toml) (2)', async () => { + const { code, stdout } = await runWithArguments('test/fixtures/frontmatter-incorrect-toml.md -p spell frontmatter --frontmatter-keys title'); + code.should.equal(1); + stdout.should.include('`documnet` is misspelt'); + stdout.should.not.include('`tbroadley` is misspelt'); + }); + + it('checks only the given keys in the frontmatter (toml) (3)', async () => { + const { code, stdout } = await runWithArguments('test/fixtures/frontmatter-incorrect-toml.md -p spell frontmatter --frontmatter-keys title contributors'); + code.should.equal(1); + stdout.should.include('`documnet` is misspelt'); + stdout.should.include('`tbroadley` is misspelt'); + }); + it('does not generate a personal dictionary if no spelling mistakes are found', async () => { const { stdout } = await runWithArguments('test/fixtures/repeated-words.md --plugins spell repeated-words'); stdout.should.not.include('Personal dictionary written to dictionary.txt.'); diff --git a/test/fixtures/frontmatter-incorrect-toml.md b/test/fixtures/frontmatter-incorrect-toml.md new file mode 100644 index 0000000..c7b9f35 --- /dev/null +++ b/test/fixtures/frontmatter-incorrect-toml.md @@ -0,0 +1,6 @@ ++++ +title = "A documnet" +sort = 2 +contributors = ["tbroadley"] ++++ +This is a document.