Giter VIP home page Giter VIP logo

unified-engine's Introduction

unified-engine

Build Coverage Downloads Sponsors Backers Chat

unified engine to process multiple files, lettings users configure from the file system.

Contents

What is this?

This package is the engine. It’s what you use underneath when you use remark-cli or a language server. Compared to unified, this deals with multiple files, often from the file system, and with configuration files and ignore files.

When should I use this?

You typically use something that wraps this, such as:

You can use this to make such things.

Install

This package is ESM only. In Node.js (version 16+), install with npm:

npm install unified-engine

Use

The following example processes all files in the current folder with a markdown extension with remark, allows configuration from .remarkrc and package.json files, ignoring files from .remarkignore files, and more.

/**
 * @typedef {import('unified-engine').Callback} Callback
 */

import process from 'node:process'
import {remark} from 'remark'
import {engine} from 'unified-engine'

engine(
  {
    color: true,
    extensions: ['md', 'markdown', 'mkd', 'mkdn', 'mkdown'],
    files: ['.'],
    ignoreName: '.remarkignore',
    packageField: 'remarkConfig',
    pluginPrefix: 'remark',
    processor: remark,
    rcName: '.remarkrc'
  },
  done
)

/** @type {Callback} */
function done(error, code) {
  if (error) throw error
  process.exitCode = code
}

API

This package exports the identifiers Configuration and engine. There is no default export.

engine(options, callback)

Process.

Parameters
  • options (Options, required) — configuration
  • callback (Callback, required) — configuration
Returns

Nothing (undefined).

Configuration

Internal class to load configuration files.

Exposed to build more complex integrations.

Parameters
  • options (subset of Options, required) — configuration (cwd is required)
Fields
  • load(string, (Error?[, ConfigResult?]): undefined): undefined — get the config for a file

Completer

Completer (TypeScript type).

Type
type Completer = (CompleterCallback | CompleterRegular) & {
    pluginId?: string | symbol | undefined
}

type CompleterCallback = (set: FileSet, next: CompleterCallbackNext) => undefined
type CompleterCallbackNext = (error?: Error | null | undefined) => undefined
type CompleterRegular = (set: FileSet) => Promise<undefined> | undefined

Callback

Callback called when done (TypeScript type).

Called with a fatal error if things went horribly wrong (probably due to incorrect configuration), or a status code and the processing context.

Parameters
  • error (Error, optional) — error
  • code (0 or 1, optional) — exit code, 0 if successful or 1 if unsuccessful
  • context (Context, optional) — processing context
Returns

Nothing (undefined).

ConfigResult

Resolved configuration from Configuration (TypeScript type).

Fields
  • filePath (string) — file path of found configuration
  • plugins (Array<PluginTuple> from unified) — resolved plugins
  • settings (Settings from unified) — resolved settings

ConfigTransform

Transform arbitrary configs to our format (TypeScript type).

Parameters
  • config (unknown) — arbitrary config
  • filePath (string) — file path of config file
Returns

Our config format (Preset).

Context

Processing context (TypeScript type).

Fields

FileSet

A FileSet is created to process multiple files through unified processors (TypeScript type).

This set, containing all files, is exposed to plugins as the second parameter.

Parameters

None.

Fields
  • valueOf(): Array<VFile> — get files in a set
  • use(completer: Completer): this — add middleware to be called when done (see: Completer)
  • add(file: VFile | string): this — add a file; the given file is processed like other files with a few differences: it’s ignored when their file path is already added, never written to the file system or streamOut, and not included in the report

Options

Configuration (TypeScript type).

👉 Note: options.processor is required.

Fields
  • alwaysStringify (boolean, default: false) — whether to always serialize successfully processed files
  • color (boolean, default: false) — whether to report with ANSI color sequences; given to the reporter
  • configTransform (ConfigTransform, optional) — transform config files from a different schema
  • cwd (URL or string, default: process.cwd()) — folder to search files in, load plugins from, and more
  • defaultConfig (Preset, optional) — default configuration to use if no config file is given or found
  • detectConfig (boolean, default: true if options.packageField or options.rcName) — whether to search for configuration files
  • detectIgnore (boolean, default: true if options.ignoreName) — whether to search for ignore files
  • extensions (Array<string>, optional) — search for files with these extensions, when folders are passed; generated files are also given the first extension if treeIn is on and output is on or points to a folder
  • filePath (URL or string, optional) — file path to process the given file on streamIn as
  • files (Array<URL | VFile | string>, optional) — paths or globs to files and folder, or virtual files, to process
  • frail (boolean, default: false) — call back with an unsuccessful (1) code on warnings as well as errors
  • ignoreName (string, optional) — name of ignore files to load
  • ignorePath (URL or string, optional) — filepath to an ignore file to load
  • ignorePathResolveFrom (ResolveFrom, default: 'dir') — resolve patterns in ignorePath from the current working folder ('cwd') or the ignore file’s folder ('dir')
  • ignorePatterns (optional) — patterns to ignore in addition to ignore files
  • ignoreUnconfigured (boolean, default: false) — ignore files that do not have an associated detected configuration file; either rcName or packageField must be defined too; cannot be combined with rcPath or detectConfig: false
  • inspect (boolean, default: false) — whether to output a formatted syntax tree for debugging
  • out (boolean, default: false) — whether to write the processed file to streamOut
  • output (URL, boolean or string, default: false) — whether to write successfully processed files, and where to; when true, overwrites the given files, when false, does not write to the file system; when pointing to an existing folder, files are written to that folder and keep their original basenames; when the parent folder of the given path exists and one file is processed, the file is written to the given path
  • packageField (string, optional) — field where configuration can be found in package.json files
  • pluginPrefix (string, optional) — prefix to use when searching for plugins
  • plugins (Preset['plugins'], optional) — plugins to use
  • processor (Processor, required) — unified processor to transform files
  • quiet (boolean, default: false) — do not report successful files; given to the reporter
  • rcName (string, optional) — name of configuration files to load
  • rcPath (URL or string, optional) — filepath to a configuration file to load
  • reporter (VFileReporter or string, default: vfile-reporter) — reporter to use; if a string is passed, it’s loaded from cwd, and 'vfile-reporter-' can be omitted
  • reporterOptions (Options from vfile-reporter, optional) — config to pass to the used reporter
  • settings (Settings from unified, optional) — configuration for the parser and compiler of the processor
  • silent (boolean, default: false) — report only fatal errors; given to the reporter
  • silentlyIgnore (boolean, default: false) — skip given files if they are ignored
  • streamError (WritableStream from Node.js, default: process.stderr) — stream to write the report (if any) to
  • streamIn (ReadableStream from Node.js, default: process.stdin) — stream to read from if no files are found or given
  • streamOut (WritableStream from Node.js, default: process.stdout) — stream to write processed files to, nothing is streamed if either out is false, output is not false, multiple files are processed, or a fatal error occurred while processing a file
  • tree (boolean, default: false) — whether to treat both input and output as a syntax tree
  • treeIn (boolean, default: options.tree) — whether to treat input as a syntax tree
  • treeOut (boolean, default: options.tree) — whether to output as a syntax tree
  • verbose (boolean, default: false) — report extra info; given to the reporter

Preset

Sharable configuration, with support for specifiers (TypeScript type).

Specifiers should not be used in actual presets (because they can’t be used by regular unified), but they can be used in config files locally, as those are only for the engine.

They can contain plugins and settings.

Type
import type {
  Plugin as UnifiedPlugin,
  PluginTuple as UnifiedPluginTuple,
  Preset as UnifiedPreset,
  Settings
} from 'unified'

type Preset = {
  plugins?: PluggableList | PluggableMap | undefined
  settings?: Settings | undefined
}

type Pluggable = Plugin | PluginTuple | UnifiedPreset
type PluggableList = Array<Pluggable>
type PluggableMap = Record<string, unknown>
type Plugin = UnifiedPlugin | string
type PluginTupleSupportingSpecifiers =
  | [plugin: string, ...parameters: Array<unknown>]
  | UnifiedPluginTuple

ResolveFrom

How to resolve (TypeScript type).

Type
type ResolveFrom = 'cwd' | 'dir';

VFileReporter

Transform arbitrary configs to our format (TypeScript type).

This is essentially the interface of vfile-reporter, with added support for unknown fields in options and async support.

Parameters
  • files (Array<VFile>) — files
  • options (Options from vfile-reporter, optional) — configuration
Returns

Report (Promise<string> or string).

Config files

unified-engine accepts configuration through options and through configuration files (rc files).

Explicit configuration

One configuration file can be given through options.rcPath, this is loaded regardless of options.detectConfig and options.rcName.

Implicit configuration

Otherwise, configuration files are detected if options.detectConfig is turned on, depending on the following options:

  • if options.rcName is given, $rcName (JSON), $rcName.js (CommonJS or ESM depending on the type field of the closest package.json), $rcName.cjs (CommonJS), $rcName.mjs (ESM), $rcName.yml (YAML), and $rcName.yaml (YAML) are loaded
  • if options.packageField is given, package.json (JSON) files are loaded and the configuration at their $packageField field is used

The first file that is searched for in a folder is used as the configuration. If no file is found, the parent folder is searched, and so on.

The schema (type) of rc files is Preset.

Examples

An example rc file could look as follows:

{
  "plugins": [
    "remark-inline-links",
    "remark-lint-recommended"
  ],
  "settings": {
    "bullet": "*",
    "ruleRepetition": 3,
    "fences": true
  }
}

Another example, rc.js, could look as follows:

exports.plugins = [
  './script/natural-language.js',
  'remark-lint-recommended',
  'remark-license'
]

exports.settings = {bullet: '*'}

When using ESM (ECMAScript modules), rc.mjs could look as folows:

export default {
  plugins: [
    './script/natural-language.js',
    'remark-lint-recommended',
    'remark-license'
  ],
  settings: {bullet: '*'}
}

Another example, rc.yaml, could look as follows:

plugins:
  - 'rehype-document'
  - 'rehype-preset-minify'
settings:
  preferUnquoted: true
  quote: "'"
  quoteSmart: true
  verbose: true

Ignore files

unified-engine accepts patterns to ignore when searching for files to process through ignore files.

Explicit ignoring

One ignore file can be given through options.ignorePath, this is loaded regardless of options.detectIgnore and options.ignoreName.

Implicit ignoring

Otherwise, ignore files are detected if options.detectIgnore is turned on and options.ignoreName is given.

The first file named $ignoreName in the parent folder of a checked file is used. Or, if no file is found, the parent folder if searched, and so on.

Extra ignoring

In addition to explicit and implicit ignore files, other patterns can be given with options.ignorePatterns. The format of each pattern in ignorePatterns is the same as a line in an ignore file. Patterns and files are resolved based on the current working folder.

It is also possible to ignore files that do not have an associated detected configuration file by turning on options.ignoreUnconfigured.

Ignoring

Ignoring is used when searching for files in folders. If paths (including those expanded from globs) are passed in that are ignored, an error is thrown. These files can be silently ignored by turning on options.silentlyIgnore.

Normally, files are ignored based on the path of the found ignore file and the patterns inside it. Patterns passed with options.ignorePatterns are resolved based on the current working directory.

Patterns in an explicit ignore file passed in with options.ignorePath can be resolved from the current working directory instead, by setting options.ignorePathResolveFrom to 'cwd' instead of 'dir' (default).

If paths or globs to folders are given to the engine, they will be searched for matching files, but node_modules are normally not searched. Pass paths (or globs) to the node_modules you want to include in options.files to search them.

The format for ignore files is the same as .gitignore, so it’s possible to pass a .gitignore in as options.ignorePath.

node-ignore is used under the hood, see its documentation for more information.

Examples

An example ignore file could look as follows:

# Ignore files in `.github`.
.github/

# Bower.
bower_components/
# Duo dependencies.
components/

# Fixtures.
test/{input,tree}/

If we had an ignore file folder/.remarkignore, with the value: index.txt, and our file system looked as follows:

folder/.remarkignore
folder/index.txt
index.txt

Then folder/index.txt would be ignored but index.txt would not be.

Plugins

Normally, unified plugins receive a single options argument upon attaching (an Object users can provide to configure the plugin).

If a plugin is attached by unified-engine, a second argument is given: FileSet.

Examples

unified-engine can be configured extensively by engine authors.

options.alwaysStringify

This example shows how you can use options.alwaysStringify when you don’t want the engine to write to the file system, but still want to get the compiled results. One example that does this is unified-engine-gulp.

import {remark} from 'remark'
import {engine} from 'unified-engine'
import {VFile} from 'vfile'

const file = new VFile({path: 'example.md', value: '_hi_'})

engine(
  {alwaysStringify: true, files: [file], processor: remark},
  function (error, code, context) {
    if (error) throw error
    console.log(context?.files.map((d) => String(d)))
  }
)

Yields:

example.md: no issues found
[ '*hi*\n' ]

options.configTransform

To support custom rc files, that have a different format than what the engine supports, pass as ConfigTransform.

This example processes readme.md and loads options from custom (from a package.json). configTransform is called with those options and transforms it to configuration unified-engine understands.

import {remark} from 'remark'
import {engine} from 'unified-engine'

engine(
  {
    configTransform,
    files: ['readme.md'],
    packageField: 'custom',
    processor: remark()
  },
  function (error) {
    if (error) throw error
  }
)

function configTransform(config) {
  return {settings: (config || {}).options}
}

Where package.json contains:

{
  "name": "foo",
  "private": true,
  "custom": {
    "options": {
      "bullet": "+"
    }
  }
}

options.defaultConfig

This example processes readme.md. If package.json exists, that config is used, otherwise the configuration at defaultConfig is used.

import {remark} from 'remark'
import {engine} from 'unified-engine'

engine(
  {
    defaultConfig: {settings: {bullet: '+'}},
    files: ['readme.md'],
    packageField: 'remarkConfig',
    processor: remark()
  },
  function (error) {
    if (error) throw error
  }
)

Where package.json contains:

{
  "name": "foo",
  "private": true,
  "remarkConfig": {
    "settings": {
      "bullet": "-"
    }
  }
}

options.detectConfig

This example processes readme.md but does not allow configuration from .remarkrc or package.json files, as detectConfig is false.

import {remark} from 'remark'
import {engine} from 'unified-engine'

engine(
  {
    detectConfig: false,
    files: ['readme.md'],
    processor: remark(),
    packageField: 'remarkConfig',
    rcName: '.remarkrc'
  },
  function (error) {
    if (error) throw error
  }
)

options.detectIgnore

This example processes files in the current working directory with an md extension but does not ignore file paths from the closest .remarkignore file, because detectIgnore is false.

import {remark} from 'remark'
import {engine} from 'unified-engine'

engine(
  {
    detectIgnore: false,
    extensions: ['md'],
    files: ['.'],
    ignoreName: '.remarkignore',
    processor: remark()
  },
  function (error) {
    if (error) throw error
  }
)

options.extensions

This example reformats all files with md, markdown, and mkd extensions in the current folder.

import {remark} from 'remark'
import {engine} from 'unified-engine'

engine(
  {
    extensions: ['md', 'mkd', 'markdown'],
    files: ['.'],
    output: true,
    processor: remark
  },
  function (error) {
    if (error) throw error
  }
)

options.filePath

This example shows that streamIn is named as filePath:

import {PassThrough} from 'node:stream'
import {remark} from 'remark'
import remarkPresetLintRecommended from 'remark-preset-lint-recommended'
import {engine} from 'unified-engine'

const streamIn = new PassThrough()

streamIn.write('doc')

setImmediate(function () {
  streamIn.end('ument')
})

engine(
  {
    filePath: '~/alpha/bravo/charlie.md',
    out: false,
    plugins: [remarkPresetLintRecommended],
    processor: remark(),
    streamIn
  },
  function (error) {
    if (error) throw error
  }
)

Yields:

~/alpha/bravo/charlie.md
  1:1  warning  Missing newline character at end of file  final-newline  remark-lint

⚠ 1 warning

options.files

This example processes LICENSE and all files with an md extension in doc.

import {remark} from 'remark'
import {engine} from 'unified-engine'

engine(
  {
    extensions: ['md'],
    files: ['LICENSE', 'doc/'],
    processor: remark
  },
  function (error) {
    if (error) throw error
  }
)

options.frail

This example uses remark-lint to lint readme.md and exits with the given exit code. Normally, only errors turn the code to 1, but in frail mode lint warnings result in the same.

import process from 'node:process'
import {remark} from 'remark'
import {engine} from 'unified-engine'

engine(
  {
    files: ['readme.md'],
    frail: true,
    plugins: ['remark-preset-lint-recommended'],
    processor: remark()
  },
  function (error, code) {
    process.exitCode = error ? 1 : code
  }
)

options.ignoreName

This example processes files in the current working directory with an md extension, and is configured to ignore file paths from the closest .remarkignore file.

import {remark} from 'remark'
import {engine} from 'unified-engine'

engine(
  {
    extensions: ['md'],
    files: ['.'],
    ignoreName: '.remarkignore',
    processor: remark()
  },
  function (error) {
    if (error) throw error
  }
)

options.ignorePath

This example processes files in the current working directory with an md extension and ignores file paths specified in .gitignore.

import {remark} from 'remark'
import {engine} from 'unified-engine'

engine(
  {
    extensions: ['md'],
    files: ['.'],
    ignorePath: '.gitignore',
    processor: remark()
  },
  function (error) {
    if (error) throw error
  }
)

options.ignorePathResolveFrom

This example processes files in the current working directory with an md extension and takes a reusable configuration file from a dependency.

import {remark} from 'remark'
import {engine} from 'unified-engine'

engine(
  {
    extensions: ['md'],
    files: ['.'],
    ignorePath: 'node_modules/my-config/my-ignore',
    ignorePathResolveFrom: 'cwd',
    processor: remark()
  },
  function (error) {
    if (error) throw error
  }
)

options.ignorePatterns

This example processes files in the current working directory with an md extension, except for readme.md:

import {remark} from 'remark'
import {engine} from 'unified-engine'

engine(
  {
    extensions: ['md'],
    files: ['.'],
    ignorePatterns: ['readme.md'],
    processor: remark()
  },
  function (error) {
    if (error) throw error
  }
)

options.ignoreUnconfigured

This example processes files in the current working directory with an md extension, but only if there is an explicit .remarkrc config file near (upwards) to them:

import {remark} from 'remark'
import {engine} from 'unified-engine'

engine(
  {
    extensions: ['md'],
    files: ['.'],
    ignoreUnconfigured: true,
    processor: remark(),
    rcName: '.remarkrc'
  },
  function (error) {
    if (error) throw error
  }
)

options.inspect

This example shows a module which reads and parses doc.md, then remark-unlink transforms the syntax tree, the tree is formatted with unist-util-inspect, and finally written to stdout(4).

import {remark} from 'remark'
import remarkUnlink from 'remark-unlink'
import {engine} from 'unified-engine'

engine(
  {
    files: ['doc.md'],
    inspect: true,
    plugins: [remarkUnlink],
    processor: remark()
  },
  function (error) {
    if (error) throw error
  }
)

Where doc.md looks as follows:

[foo](https://example.com)

Yields:

root[1] (1:1-2:1, 0-27)
└─ paragraph[1] (1:1-1:27, 0-26)
   └─ text: "foo" (1:2-1:5, 1-4)

options.out

This example uses remark-lint to lint readme.md, writes the report, and ignores the serialized document.

import {remark} from 'remark'
import remarkPresetLintRecommended from 'remark-preset-lint-recommended'
import {engine} from 'unified-engine'

engine(
  {
    files: ['readme.md'],
    out: false,
    plugins: [remarkPresetLintRecommended],
    processor: remark()
  },
  function (error) {
    if (error) throw error
  }
)

options.output

This example writes all files in src/ with an md extension compiled to dest/.

import {remark} from 'remark'
import {engine} from 'unified-engine'

engine(
  {
    extensions: ['md'],
    files: ['src/'],
    output: 'dest/',
    processor: remark()
  },
  function (error) {
    if (error) throw error
  }
)

options.packageField

This example processes readme.md, and allows configuration from remarkConfig fields in package.json files.

import {remark} from 'remark'
import {engine} from 'unified-engine'

engine(
  {
    files: ['readme.md'],
    packageField: 'remarkConfig',
    processor: remark()
  },
  function (error) {
    if (error) throw error
  }
)

options.pluginPrefix

This example processes readme.md and loads the preset-lint-recommended plugin. Because pluginPrefix is given, this resolves to remark-preset-lint-recommended (from node_modules/) if available.

import {remark} from 'remark'
import {engine} from 'unified-engine'

engine(
  {
    files: ['readme.md'],
    pluginPrefix: 'remark',
    plugins: ['preset-lint-recommended'],
    processor: remark()
  },
  function (error) {
    if (error) throw error
  }
)

options.plugins

This example processes readme.md and loads the remark-preset-lint-recommended preset.

import {remark} from 'remark'
import {engine} from 'unified-engine'

engine(
  {
    files: ['readme.md'],
    plugins: ['remark-preset-lint-recommended'],
    processor: remark()
  },
  function (error) {
    if (error) throw error
  }
)

options.processor

This example reformats stdin(4) using remark, writes the report to stderr(4), and formatted document to stdout(4).

import {remark} from 'remark'
import {engine} from 'unified-engine'

engine({processor: remark}, function (error) {
  if (error) throw error
})

options.quiet

This example uses remark-lint to lint readme.md. Nothing is reported if the file processed successfully.

import {remark} from 'remark'
import {engine} from 'unified-engine'

engine(
  {
    files: ['readme.md'],
    plugins: ['remark-preset-lint-recommended'],
    processor: remark(),
    quiet: true
  },
  function (error) {
    if (error) throw error
  }
)

options.rcName

This example processes readme.md and allows configuration from .remarkrc, .remarkrc.json, .remarkrc.yml, .remarkrc.yaml, .remarkrc.js, .remarkrc.cjs, and .remarkrc.mjs files.

import {remark} from 'remark'
import {engine} from 'unified-engine'

engine(
  {files: ['readme.md'], processor: remark(), rcName: '.remarkrc'},
  function (error) {
    if (error) throw error
  }
)

options.rcPath

This example processes readme.md and loads configuration from config.json.

import {remark} from 'remark'
import {engine} from 'unified-engine'

engine(
  {files: ['readme.md'], processor: remark(), rcPath: 'config.json'},
  function (error) {
    if (error) throw error
  }
)

options.reporter and options.reporterOptions

This example processes all HTML files in the current folder with rehype, configures the processor with .rehyperc files, and prints a report in JSON using vfile-reporter-json with reporter options.

import {rehype} from 'rehype'
import {engine} from 'unified-engine'

engine(
  {
    extensions: ['html'],
    files: ['.'],
    processor: rehype(),
    rcName: '.rehyperc',
    reporter: 'json',
    reporterOptions: {pretty: true}
  },
  function (error) {
    if (error) throw error
  }
)

options.settings

This example processes readme.md and configures the compiler (remark-stringify) with bullet: '+'.

import {remark} from 'remark'
import {engine} from 'unified-engine'

engine(
  {files: ['readme.md'], processor: remark(), settings: {bullet: '+'}},
  function (error) {
    if (error) throw error
  }
)

options.silent

This example uses remark-lint to lint readme.md but does not report any warnings or success messages, only fatal errors, if they occur.

import {remark} from 'remark'
import {engine} from 'unified-engine'

engine(
  {
    files: ['readme.md'],
    plugins: ['remark-preset-lint-recommended'],
    processor: remark(),
    silent: true
  },
  function (error) {
    if (error) throw error
  }
)

options.streamError

This example uses remark-lint to lint readme.md and writes the report to report.txt.

import fs from 'node:fs'
import {remark} from 'remark'
import remarkPresetLintRecommended from 'remark-preset-lint-recommended'
import {engine} from 'unified-engine'

engine(
  {
    files: ['readme.md'],
    out: false,
    plugins: [remarkPresetLintRecommended],
    processor: remark(),
    streamErr: fs.createWriteStream('report.txt')
  },
  function (error) {
    if (error) throw error
  }
)

options.streamIn

This example uses remark-lint to lint an incoming stream.

import {PassThrough} from 'node:stream'
import {remark} from 'remark'
import remarkPresetLintRecommended from 'remark-preset-lint-recommended'
import {engine} from 'unified-engine'

const streamIn = new PassThrough()

streamIn.write('doc')

setImmediate(function () {
  streamIn.end('ument')
})

engine(
  {
    out: false,
    plugins: [remarkPresetLintRecommended],
    processor: remark(),
    streamIn
  },
  function (error) {
    if (error) throw error
  }
)

Yields:

<stdin>
  1:1  warning  Missing newline character at end of file  final-newline  remark-lint

⚠ 1 warning

options.streamOut

This example reads readme.md and writes the serialized document to readme-two.md. This can also be achieved by passing output: 'readme-two.md' instead of streamOut.

import fs from 'node:fs'
import {remark} from 'remark'
import {engine} from 'unified-engine'

const streamOut = fs.createWriteStream('readme-two.md')

engine(
  {files: ['readme.md'], processor: remark(), streamOut},
  function (error) {
    if (error) throw error
  }
)

options.tree

This example reads tree.json, then remark-unlink transforms the syntax tree, and the transformed tree is written to stdout(4).

import {remark} from 'remark'
import remarkUnlink from 'remark-unlink'
import {engine} from 'unified-engine'

engine(
  {
    files: ['tree.json'],
    plugins: [remarkUnlink],
    processor: remark(),
    tree: true
  },
  function (error) {
    if (error) throw error
  }
)

Where tree.json looks as follows:

{
  "type": "paragraph",
  "children": [{
    "type": "link",
    "url": "https://example.com",
    "children": [{
      "type": "text",
      "value": "foo"
    }]
  }]
}

Yields:

{
  "type": "paragraph",
  "children": [{
    "type": "text",
    "value": "foo"
  }]
}

options.treeIn

This example reads tree.json, then remark-unlink transforms the syntax tree, the tree is serialized, and the resulting document is written to stdout(4).

import {remark} from 'remark'
import remarkUnlink from 'remark-unlink'
import {engine} from 'unified-engine'

engine(
  {
    files: ['tree.json'],
    plugins: [remarkUnlink],
    processor: remark(),
    treeIn: true
  },
  function (error) {
    if (error) throw error
  }
)

Where tree.json looks as follows:

{
  "type": "paragraph",
  "children": [{
    "type": "link",
    "url": "https://example.com",
    "children": [{
      "type": "text",
      "value": "foo"
    }]
  }]
}

Yields:

foo

options.treeOut

This example shows a module which reads and parses doc.md, then remark-unlink transforms the syntax tree, and the tree is written to stdout(4).

import {remark} from 'remark'
import remarkUnlink from 'remark-unlink'
import {engine} from 'unified-engine'

engine(
  {
    files: ['doc.md'],
    plugins: [remarkUnlink],
    processor: remark(),
    treeOut: true
  },
  function (error) {
    if (error) throw error
  }
)

Where doc.md looks as follows:

[foo](https://example.com)

Yields:

{
  "type": "paragraph",
  "children": [{
    "type": "text",
    "value": "foo"
  }]
}

Types

This package is fully typed with TypeScript. It exports the additional types Completer, Callback, ConfigResult, ConfigTransform, Context, FileSet, Options, Preset, ResolveFrom, and VFileReporter.

Compatibility

Projects maintained by the unified collective are compatible with maintained versions of Node.js.

When we cut a new major release, we drop support for unmaintained versions of Node. This means we try to keep the current release line, unified-engine@^11, compatible with Node.js 16.

Security

unified-engine loads and evaluates configuration files, plugins, and presets from the file system (often from node_modules/). That means code that is on your file system runs. Make sure you trust the workspace where you run unified-engine and be careful with packages from npm and changes made by contributors.

Contribute

See contributing.md in unifiedjs/.github for ways to get started. See support.md for ways to get help.

This project has a code of conduct. By interacting with this repository, organization, or community you agree to abide by its terms.

License

MIT © Titus Wormer

unified-engine's People

Contributors

christianmurphy avatar greenkeeperio-bot avatar jounqin avatar katydecorah avatar ludofischer avatar remcohaszing avatar sarahec avatar starptech avatar stefanprobst avatar steveszc avatar wooorm avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

unified-engine's Issues

Implement overrides similar to ESLint

Initial checklist

Problem

Sometimes certain checks depend on context, not just language.

For example, typically markdown documents need to contain complete and valid markdown documents. Some specific documents may need some specific checks to be included or excluded though. For example:

  • MDX files need additional plugins to enhance the parser
  • GitHub issue templates may not use newlines inside paragraphs
  • Email templates might not need a level 1 heading

Solution

ESLint has pretty much the exact problem. They solved it using overrides

The same could be done for unified-engine. For example:

plugins:
  - remark-gfm
  - [remark-lint-first-heading-level, 1]
overrides:
  - files: ['*.mdx']
    plugins:
      - remark-mdx
  - files: [.github/]
    plugins:
      - [remark-lint-first-heading-level, false]

ESLint also uses the globs to determine additional file extensions to check.

Alternatives

I can’t really think of anything else

`file.value` set to `''` should not result in file system read

Initial checklist

Affected packages and versions

latest

Link to runnable example

No response

Steps to reproduce

file.value set to '' should not result in file system read. testing out unified-language-server (which handles the FS itself), checking an empty file (''), results in an error about a file not existing on disk in tests.

Expected behavior

no fs read

Actual behavior

fs read

Runtime

No response

Package manager

No response

OS

No response

Build and bundle tools

No response

Switch from js-yaml to yaml

Initial checklist

Problem

The js-yaml package doesn't strictly follow the YAML 1.2 spec. It supports some YAML 1.1 features.

Solution

Replace it with the more compliant yaml.

It can be configured to behave the same as js-yaml, so the change could be made as semver patch.

This package also has builtin TypeScript support.

Alternatives

🤷

Support .config folder for storing configuration by default

Initial checklist

Problem

Project root folders being stuck with a bunch of . configs files for various tools, remark or rehype being just one of many.
As a result, it is harder to find actual source files in the project.

Solution

.config/ is a convention supported by cosmiconfig, and by extension a number command line tools (stylelint, prettier, eslint-mdx, and others).
Which allows top level configuration files to be moved out of the root folder and into .config/ folder, clarifying the intent of the files, and separating source code from configuration files/code.

Alternatives

Leave configuration system as it is today, adding notes/recommendations pointing towards either:

Configuration cascade

The configuration cascade is rather unusable (proof: remarkjs/remark-lint#96). I think that some cascading could be useful though. Here’s some ideas.

  • Opt-in cascade (cascade: true);
  • Opt-out cascade (root: true);
  • no cascading: use presets instead.

Unable to define `settings.handlers` with non-structuredClone-able values

Initial checklist

Affected packages and versions

[email protected], remark-lint

Link to runnable example

No response

Steps to reproduce

  • Create a project and set up remark-lint, configured using a remarkrc.js file.
  • In the exported remark config object, define settings.handlers and provide the expected key-value pairs for nodes such as text, paragraph, etc
  • Specifically for text, try to take advantage of search and replace support from remark-stringify by defining some regex + replace values:
import remarkFrontmatter from 'remark-frontmatter';
import remarkGfm from 'remark-gfm';
import remarkLintNoEmptyURL from 'remark-lint-no-empty-url';
import remarkLintNoHtml from 'remark-lint-no-html';
import remarkMdx from 'remark-mdx';
import remarkDirective from 'remark-directive';
import presetLintRecommended from 'remark-preset-lint-recommended';
import {
  paragraph,
  remarkCustomHeadingId,
  remarkLintHttpBackticks,
  remarkLintImages,
  remarkLintRedirects,
  remarkLintSearchReplace,
  remarkLintValidLocalLinks,
  text,
  link,
  remarkLintValidHeadings,
} from './dist/packages/remark/src/index.js';

export const SEARCH_REPLACE_RULES = [
  {
    name: 'curly-double-quotes',
    message: "Don't use curly double quotes",
    regex: /“|”/,
    replace: '"',
  },
  {
    name: 'curly-single-quotes',
    message: "Don't use curly single quotes",
    regex: /‘|’/,
    replace: "'",
  },
  {
    name: 'nbsp',
    message: "Don't use no-break spaces",
    regex: / /,
    replace: ' ',
  },
  {
    name: 'm-dash',
    message: "Don't use '--'. Use m-dash — instead",
    search: ' -- ',
    replace: ' — ',
  },
  {
    name: 'double-spaces',
    message: 'Avoid double spaces',
    regex: /(?<$1>[^\s>])\s{2,}(?<$2>[^\s|])/,
    replace: '$1 $2',
  },
  {
    name: 'incorrect-spelling',
    message: 'Incorrect spelling',
    regex: /e-mail/,
    replace: 'email',
  },
  {
    name: 'incorrect-spelling',
    message: 'Incorrect spelling',
    regex: /(?<$1>w)eb site/,
    replace: '$1ebsite',
  },
  {
    name: 'relative-link',
    message: "Internal links should start with '/'",
    regex: /\[(?:.*?)\]\((?:internal\/.*?)\)/,
    replace: '[$1](/$2)',
  },
];

export const remarkConfig = {
  extensions: '.mdx',
  plugins: [
    remarkFrontmatter,
    presetLintRecommended,
    remarkGfm,
    remarkMdx,
    remarkDirective,
    remarkCustomHeadingId,
    remarkLintNoEmptyURL,
    remarkLintNoHtml,
    remarkLintHttpBackticks,
    remarkLintImages,
    ['remark-lint-no-undefined-references', { allow: [/#[a-zA-Z]/] }],
    ['remark-lint-checkbox-character-style', { checked: 'x', unchecked: ' ' }],
    ['remark-lint-blockquote-indentation', 2],
    ['remark-lint-emphasis-marker', '*'],
    ['remark-lint-heading-style', 'atx'],
    ['remark-lint-link-title-style', '"'],
    ['remark-lint-ordered-list-marker-style', '.'],
    ['remark-lint-strong-marker', '*'],
    ['remark-lint-table-cell-padding', 'padded'],
    ['remark-lint-list-item-bullet-indent', false],
    ['remark-lint-list-item-indent', 'one'],
    ['remark-lint-first-heading-level', 2],
    ['remark-lint-ordered-list-marker-value', 'ordered'],
    ['remark-lint-unordered-list-marker-style', '-'],
    ['remark-lint-rule-style', '---'],
    ['remark-lint-no-literal-urls', false],
    'remark-lint-list-item-content-indent',
    'remark-lint-fenced-code-flag',
    'remark-lint-no-duplicate-headings',
    'remark-lint-no-heading-indent',
    'remark-lint-no-heading-like-paragraph',
    'remark-lint-no-empty-sections',
    ['remark-lint-no-emphasis-as-heading', false],
    ['remark-lint-no-duplicate-headings', false],
    ['remark-lint-no-undefined-references', false],
  ],
  settings: {
    bullet: '-',
    bulletOrdered: '.',
    listItemIndent: 'one',
    rule: '-',
    handlers: {
      paragraph,
      text: text(SEARCH_REPLACE_RULES),
      link,
    },
  },
};

export default remarkConfig;

Expected behavior

Running eslint should run without errors and correctly highlight instances where the find and replace rules apply in targeted markdown/mdx files. This configuration previously worked when we were on MDX v2 (and [email protected]), but now that we're updating plugins to work with MDX v3 and unified@11.* this has come up.

Actual behavior

We get an unexpected error:

error Cannot process file
  [cause]:
    Error: Cannot parse given file `.remarkrc.js`
    at file:///Users/sstern/Documents/trashfire/internal/node_modules/unified-engine/lib/find-up.js:137:19
    at done (file:///Users/sstern/Documents/trashfire/internal/node_modules/trough/index.js:148:7)
  [cause]:
    DataCloneError: function(node, _, state, info) {
  return defaultHandlers.paragraph(splitAndMapParagraphText(node, VERBS_REGEX,...<omitted>...
} could not be cloned.
    at new DOMException (node:internal/per_context/domexception:53:5)
    at structuredClone (node:internal/structured_clone:23:17)
    at file:///Users/sstern/Documents/trashfire/internal/node_modules/@ungap/structured-clone/esm/index.js:20:46
    at addPreset (file:///Users/sstern/Documents/trashfire/internal/node_modules/unified-engine/lib/configuration.js:352:23)
    at async merge (file:///Users/sstern/Documents/trashfire/internal/node_modules/unified-engine/lib/configuration.js:328:5)
    at async Configuration.create (file:///Users/sstern/Documents/trashfire/internal/node_modules/unified-engine/lib/configuration.js:266:7)

Looking at the source for configuration.js at that line, I see that this change was made in v11.2: 65da801#diff-c47540330d377e9cd0e6b633a168ffa74b2781e7a7f14ab6a8e934bd39576e22L265

- target.settings = Object.assign({}, target.settings, result.settings)
+ target.settings = structuredClone({...target.settings, ...result.settings})

I think this stylistic change may have unintentionally broken this element of support for settings.handlers, as functions cannot be cloned.

If this is the case and it would not break anything, I would be happy to open a PR to revert back to normal Object.assign or another object merge method that would still preserve functions.

Affected runtime and version

[email protected]

Affected package manager and version

[email protected]

Affected OS and version

No response

Build and bundle tools

No response

`ignore` dependency upgrade should be reverted

Subject of the issue

In the 7.0.0 release, the ignore dependency was upgraded to version 5, a semver major upgrade, which changed how relative paths are ignored (see kaelzhang/node-ignore#20).

At L38 of the ignore.js file, you resolve a file path to a relative path. This causes ignore to throw an error because ignore does not accept relative paths.

For example, when running remark on a Markdown file, I encounter the following error:

RangeError: path should be a `path.relative()`d string, but got "../../path/to/file.md"

Your environment

Steps to reproduce

Tell us how to reproduce this issue. Please provide a working and simplified example.

Run a command similar to the following (using remark and not unified-engine directly):

$ ./node_modules/.bin/remark --rc-path=./path/to/.remarkrc.js --ignore-path=./path/to/.remarkignore --no-ignore --no-config ./path/to/file.md

For the used .remarkignore file, see here.

Expected behaviour

No error should occur.

Actual behaviour

The following error is encountered:

RangeError: path should be a `path.relative()`d string, but got "../../path/to/file.md"

Feature: `out` should default to whether one file is given (rather than found)

Current behaviour is to switch based on whether multiple files are found. This means that changing the file-system could change the engine. Should switch to whether globs could match multiple files, or not.

out should default to whether there’s one value in globs and it points to a file.

Supersedes remarkjs/remark#184.

This also opens up the door for allowing out: true on multiple files, similar to cat foo.txt bar.txt

Ability to suppress error "Cannot process specified file: it’s ignored"

Subject of the feature

Do not throw error when running with ignored file

Problem

I am using remark-cli with lint-staged, lint-staged doesn't know the content in .remarkignore, so it will try to run on all modified md files with remark-cli. But as the file is ignored by remark-cli, it throws error Cannot process specified file: it’s ignored

Expected behavior

I can use remark-cli with lint-staged without any errors

Alternatives

As it may be a breaking change, maybe a flag to enable?

settings.output will create an empty file when file does not exist

Executing this command with the option output: true will create an empty file test.html

cli.js test.html

An error is displayed but the file is still created.

test.html
  1:1  error  No such file or directory

× 1 error

I expect that no file is created and an error is emitted.

Test doesnt run on windows

Paths, Symbols, Colors aren't tested for windows.

TAP version 13
# engine
# engine
ok 1 should throw w/o `callback`
ok 2 should fail when w/o options
ok 3 should fail when w/o processor
# should fail w/ `output` and w/ `out`
ok 4 should fail
# should fail w/ `detectConfig` w/o `rcName`
ok 5 should fail
# should fail w/ `detectIgnore` w/o `ignoreName`
ok 6 should fail
# input
# should fail without input
ok 7 should fail
# should not fail on empty input stream
ok 8 should report
# should not fail on unmatched given globs
ok 9 should work
# should report unfound given files
not ok 10 should report
  ---
    operator: deepEqual
    expected: |-
      [ null, 1, 'readme.md\n  1:1  error  No such file or directory\n\nΓ£û 1 error\n' ]
    actual: |-
      [ null, 1, 'readme.md\n  1:1  error  No such file or directory\n\n× 1 error\n' ]
    at: onrun (D:\Repositories\unified-engine\test\input.js:106:10)
    stack: |-
      Error: should report
          at Test.assert [as _assert] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:224:54)
          at Test.bound [as _assert] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:76:32)
          at Test.tapeDeepEqual (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:421:10)
          at Test.bound [as deepEqual] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:76:32)
          at onrun (D:\Repositories\unified-engine\test\input.js:106:10)
          at next (D:\Repositories\unified-engine\lib\index.js:7:6842)
          at next (D:\Repositories\unified-engine\node_modules\trough\index.js:60:14)
          at Immediate.done [as _onImmediate] (D:\Repositories\unified-engine\node_modules\trough\wrap.js:56:16)
          at runCallback (timers.js:705:18)
          at tryOnImmediate (timers.js:676:5)
  ...
# should not report unfound given directories
ok 11 should be equivalent
# should search for extensions
not ok 12 should report
  ---
    operator: deepEqual
    expected: |-
      [ null, 0, 'bar.text: no issues found\nfoo.txt: no issues found\nnested/quux.text: no issues found\nnested/qux.txt: no issues found\n' ]
    actual: |-
      [ null, 0, 'bar.text: no issues found\nfoo.txt: no issues found\nnested\\quux.text: no issues found\nnested\\qux.txt: no issues found\n' ]
    at: onrun (D:\Repositories\unified-engine\test\input.js:155:10)
    stack: |-
      Error: should report
          at Test.assert [as _assert] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:224:54)
          at Test.bound [as _assert] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:76:32)
          at Test.tapeDeepEqual (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:421:10)
          at Test.bound [as deepEqual] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:76:32)
          at onrun (D:\Repositories\unified-engine\test\input.js:155:10)
          at next (D:\Repositories\unified-engine\lib\index.js:7:6842)
          at next (D:\Repositories\unified-engine\node_modules\trough\index.js:60:14)
          at Immediate.done [as _onImmediate] (D:\Repositories\unified-engine\node_modules\trough\wrap.js:56:16)
          at runCallback (timers.js:705:18)
          at tryOnImmediate (timers.js:676:5)
  ...
# should search a directory for extensions
not ok 13 should report
  ---
    operator: deepEqual
    expected: |-
      [ null, 0, 'nested/quux.text: no issues found\nnested/qux.txt: no issues found\n' ]
    actual: |-
      [ null, 0, 'nested\\quux.text: no issues found\nnested\\qux.txt: no issues found\n' ]
    at: onrun (D:\Repositories\unified-engine\test\input.js:182:10)
    stack: |-
      Error: should report
          at Test.assert [as _assert] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:224:54)
          at Test.bound [as _assert] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:76:32)
          at Test.tapeDeepEqual (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:421:10)
          at Test.bound [as deepEqual] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:76:32)
          at onrun (D:\Repositories\unified-engine\test\input.js:182:10)
          at next (D:\Repositories\unified-engine\lib\index.js:7:6842)
          at next (D:\Repositories\unified-engine\node_modules\trough\index.js:60:14)
          at Immediate.done [as _onImmediate] (D:\Repositories\unified-engine\node_modules\trough\wrap.js:56:16)
          at runCallback (timers.js:705:18)
          at tryOnImmediate (timers.js:676:5)
  ...
# should search for globs matching files (#1)
ok 14 should report
# should search for globs matching files (#2)
ok 15 should report
# should search for globs matching dirs
not ok 16 should report
  ---
    operator: deepEqual
    expected: |-
      [ null, 0, 'nested/no-3.txt: no issues found\nnested/no-4.text: no issues found\n' ]
    actual: |-
      [ null, 0, 'nested\\no-3.txt: no issues found\nnested\\no-4.text: no issues found\n' ]
    at: onrun (D:\Repositories\unified-engine\test\input.js:263:10)
    stack: |-
      Error: should report
          at Test.assert [as _assert] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:224:54)
          at Test.bound [as _assert] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:76:32)
          at Test.tapeDeepEqual (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:421:10)
          at Test.bound [as deepEqual] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:76:32)
          at onrun (D:\Repositories\unified-engine\test\input.js:263:10)
          at next (D:\Repositories\unified-engine\lib\index.js:7:6842)
          at next (D:\Repositories\unified-engine\node_modules\trough\index.js:60:14)
          at Immediate.done [as _onImmediate] (D:\Repositories\unified-engine\node_modules\trough\wrap.js:56:16)
          at runCallback (timers.js:705:18)
          at tryOnImmediate (timers.js:676:5)
  ...
# should search vfileΓÇÖs pointing to directories
not ok 17 should report
  ---
    operator: deepEqual
    expected: |-
      [ null, 0, 'nested/three.txt: no issues found\n' ]
    actual: |-
      [ null, 0, 'nested\\three.txt: no issues found\n' ]
    at: onrun (D:\Repositories\unified-engine\test\input.js:285:10)
    stack: |-
      Error: should report
          at Test.assert [as _assert] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:224:54)
          at Test.bound [as _assert] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:76:32)
          at Test.tapeDeepEqual (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:421:10)
          at Test.bound [as deepEqual] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:76:32)
          at onrun (D:\Repositories\unified-engine\test\input.js:285:10)
          at next (D:\Repositories\unified-engine\lib\index.js:7:6842)
          at next (D:\Repositories\unified-engine\node_modules\trough\index.js:60:14)
          at Immediate.done [as _onImmediate] (D:\Repositories\unified-engine\node_modules\trough\wrap.js:56:16)
          at runCallback (timers.js:705:18)
          at tryOnImmediate (timers.js:676:5)
  ...
# should not ignore implicitly ignored files in globs
ok 18 should report
# should include given ignored files (#1)
not ok 19 should report
  ---
    operator: deepEqual
    expected: |-
      [ null, 1, 'nested/three.txt: no issues found\nnested/two.txt\n  1:1  error  Cannot process specified file: itΓÇÖs ignored\n\none.txt: no issues found\n\nΓ£û 1 error\n' ]
    actual: |-
      [ null, 1, 'nested\\three.txt: no issues found\nnested\\two.txt\n  1:1  error  Cannot process specified file: it’s ignored\n\none.txt: no issues found\n\n× 1 error\n' ]
    at: onrun (D:\Repositories\unified-engine\test\input.js:355:10)
    stack: |-
      Error: should report
          at Test.assert [as _assert] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:224:54)
          at Test.bound [as _assert] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:76:32)
          at Test.tapeDeepEqual (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:421:10)
          at Test.bound [as deepEqual] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:76:32)
          at onrun (D:\Repositories\unified-engine\test\input.js:355:10)
          at next (D:\Repositories\unified-engine\lib\index.js:7:6842)
          at next (D:\Repositories\unified-engine\node_modules\trough\index.js:60:14)
          at Immediate.done [as _onImmediate] (D:\Repositories\unified-engine\node_modules\trough\wrap.js:56:16)
          at runCallback (timers.js:705:18)
          at tryOnImmediate (timers.js:676:5)
  ...
# should not atempt to read files with `contents` (1)
not ok 20 should report
  ---
    operator: deepEqual
    expected: |-
      [ null, 1, 'not-existing.txt\n  1:1  error  Cannot process specified file: itΓÇÖs ignored\n\nΓ£û 1 error\n' ]
    actual: |-
      [ null, 1, 'not-existing.txt\n  1:1  error  Cannot process specified file: it’s ignored\n\n× 1 error\n' ]
    at: onrun (D:\Repositories\unified-engine\test\input.js:386:10)
    stack: |-
      Error: should report
          at Test.assert [as _assert] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:224:54)
          at Test.bound [as _assert] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:76:32)
          at Test.tapeDeepEqual (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:421:10)
          at Test.bound [as deepEqual] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:76:32)
          at onrun (D:\Repositories\unified-engine\test\input.js:386:10)
          at next (D:\Repositories\unified-engine\lib\index.js:7:6842)
          at next (D:\Repositories\unified-engine\node_modules\trough\index.js:60:14)
          at Immediate.done [as _onImmediate] (D:\Repositories\unified-engine\node_modules\trough\wrap.js:56:16)
          at runCallback (timers.js:705:18)
          at tryOnImmediate (timers.js:676:5)
  ...
# should not atempt to read files with `contents` (2)
ok 21 should report
# should include given ignored files (#2)
not ok 22 should report
  ---
    operator: deepEqual
    expected: |-
      [ null, 1, 'nested/three.txt: no issues found\nnested/two.txt\n  1:1  error  Cannot process specified file: itΓÇÖs ignored\n\none.txt: no issues found\n\nΓ£û 1 error\n' ]
    actual: |-
      [ null, 1, 'nested/three.txt: no issues found\nnested/two.txt\n  1:1  error  Cannot process specified file: it’s ignored\n\none.txt: no issues found\n\n× 1 error\n' ]
    at: onrun (D:\Repositories\unified-engine\test\input.js:445:10)
    stack: |-
      Error: should report
          at Test.assert [as _assert] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:224:54)
          at Test.bound [as _assert] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:76:32)
          at Test.tapeDeepEqual (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:421:10)
          at Test.bound [as deepEqual] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:76:32)
          at onrun (D:\Repositories\unified-engine\test\input.js:445:10)
          at next (D:\Repositories\unified-engine\lib\index.js:7:6842)
          at next (D:\Repositories\unified-engine\node_modules\trough\index.js:60:14)
          at Immediate.done [as _onImmediate] (D:\Repositories\unified-engine\node_modules\trough\wrap.js:56:16)
          at runCallback (timers.js:705:18)
          at tryOnImmediate (timers.js:676:5)
  ...
# silentlyIgnore: skip detected ignored files (#1)
not ok 23 should report
  ---
    operator: deepEqual
    expected: |-
      [ null, 0, 'nested/three.txt: no issues found\none.txt: no issues found\n' ]
    actual: |-
      [ null, 0, 'nested\\three.txt: no issues found\none.txt: no issues found\n' ]
    at: onrun (D:\Repositories\unified-engine\test\input.js:478:10)
    stack: |-
      Error: should report
          at Test.assert [as _assert] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:224:54)
          at Test.bound [as _assert] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:76:32)
          at Test.tapeDeepEqual (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:421:10)
          at Test.bound [as deepEqual] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:76:32)
          at onrun (D:\Repositories\unified-engine\test\input.js:478:10)
          at next (D:\Repositories\unified-engine\lib\index.js:7:6842)
          at next (D:\Repositories\unified-engine\node_modules\trough\index.js:60:14)
          at Immediate.done [as _onImmediate] (D:\Repositories\unified-engine\node_modules\trough\wrap.js:56:16)
          at runCallback (timers.js:705:18)
          at tryOnImmediate (timers.js:676:5)
  ...
# silentlyIgnore: skip detected ignored files (#2)
ok 24 should report
# should search if given files
not ok 25 should report
  ---
    operator: deepEqual
    expected: |-
      [ null, 0, 'nested/three.txt: no issues found\nnested/two.txt: no issues found\none.txt: no issues found\n' ]
    actual: |-
      [ null, 0, 'nested\\three.txt: no issues found\nnested\\two.txt: no issues found\none.txt: no issues found\n' ]
    at: onrun (D:\Repositories\unified-engine\test\input.js:535:10)
    stack: |-
      Error: should report
          at Test.assert [as _assert] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:224:54)
          at Test.bound [as _assert] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:76:32)
          at Test.tapeDeepEqual (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:421:10)
          at Test.bound [as deepEqual] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:76:32)
          at onrun (D:\Repositories\unified-engine\test\input.js:535:10)
          at next (D:\Repositories\unified-engine\lib\index.js:7:6842)
          at next (D:\Repositories\unified-engine\node_modules\trough\index.js:60:14)
          at Immediate.done [as _onImmediate] (D:\Repositories\unified-engine\node_modules\trough\wrap.js:56:16)
          at runCallback (timers.js:705:18)
          at tryOnImmediate (timers.js:676:5)
  ...
# ignore
# should fail fatally when given ignores are not found
ok 26 should fail
# should support custom ignore files
not ok 27 should report
  ---
    operator: deepEqual
    expected: |-
      [ null, 0, 'nested/three.txt: no issues found\none.txt: no issues found\n' ]
    actual: |-
      [ null, 0, 'nested\\three.txt: no issues found\none.txt: no issues found\n' ]
    at: onrun (D:\Repositories\unified-engine\test\ignore.js:75:10)
    stack: |-
      Error: should report
          at Test.assert [as _assert] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:224:54)
          at Test.bound [as _assert] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:76:32)
          at Test.tapeDeepEqual (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:421:10)
          at Test.bound [as deepEqual] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:76:32)
          at onrun (D:\Repositories\unified-engine\test\ignore.js:75:10)
          at next (D:\Repositories\unified-engine\lib\index.js:7:6842)
          at next (D:\Repositories\unified-engine\node_modules\trough\index.js:60:14)
          at Immediate.done [as _onImmediate] (D:\Repositories\unified-engine\node_modules\trough\wrap.js:56:16)
          at runCallback (timers.js:705:18)
          at tryOnImmediate (timers.js:676:5)
  ...
# should support searching ignore files
not ok 28 should report
  ---
    operator: deepEqual
    expected: |-
      [ null, 0, 'nested/three.txt: no issues found\none.txt: no issues found\n' ]
    actual: |-
      [ null, 0, 'nested\\three.txt: no issues found\none.txt: no issues found\n' ]
    at: onrun (D:\Repositories\unified-engine\test\ignore.js:104:10)
    stack: |-
      Error: should report
          at Test.assert [as _assert] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:224:54)
          at Test.bound [as _assert] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:76:32)
          at Test.tapeDeepEqual (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:421:10)
          at Test.bound [as deepEqual] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:76:32)
          at onrun (D:\Repositories\unified-engine\test\ignore.js:104:10)
          at next (D:\Repositories\unified-engine\lib\index.js:7:6842)
          at next (D:\Repositories\unified-engine\node_modules\trough\index.js:60:14)
          at Immediate.done [as _onImmediate] (D:\Repositories\unified-engine\node_modules\trough\wrap.js:56:16)
          at runCallback (timers.js:705:18)
          at tryOnImmediate (timers.js:676:5)
  ...
# should not look into hidden files
ok 29 should report
# should support no ignore files
not ok 30 should report
  ---
    operator: deepEqual
    expected: |-
      [ null, 0, 'nested/three.txt: no issues found\nnested/two.txt: no issues found\none.txt: no issues found\n' ]
    actual: |-
      [ null, 0, 'nested\\three.txt: no issues found\nnested\\two.txt: no issues found\none.txt: no issues found\n' ]
    at: onrun (D:\Repositories\unified-engine\test\ignore.js:160:10)
    stack: |-
      Error: should report
          at Test.assert [as _assert] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:224:54)
          at Test.bound [as _assert] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:76:32)
          at Test.tapeDeepEqual (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:421:10)
          at Test.bound [as deepEqual] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:76:32)
          at onrun (D:\Repositories\unified-engine\test\ignore.js:160:10)
          at next (D:\Repositories\unified-engine\lib\index.js:7:6842)
          at next (D:\Repositories\unified-engine\node_modules\trough\index.js:60:14)
          at Immediate.done [as _onImmediate] (D:\Repositories\unified-engine\node_modules\trough\wrap.js:56:16)
          at runCallback (timers.js:705:18)
          at tryOnImmediate (timers.js:676:5)
  ...
# configuration
# should fail fatally when custom rc files are missing
ok 31 should fail
# should fail fatally when custom rc files are empty
ok 32 should fail
# should fail fatally when custom rc files are invalid
ok 33 should fail
# should support `.rc.js` modules (1)
ok 34 should fail
# should support `.rc.js` modules (2)
ok 35 should support valid .rc modules
# should support `.rc.js` modules (3)
ok 36 should use NodeΓÇÖs module caching (coverage)
# should support `.rc.yaml` modules
ok 37 should fail fatally when custom .rc files are malformed
# should support custom rc files
not ok 38 should report
  ---
    operator: deepEqual
    expected: |-
      [ null, 0, 'nested/four.txt: no issues found\nnested/three.txt: no issues found\none.txt: no issues found\ntwo.txt: no issues found\n' ]
    actual: |-
      [ null, 0, 'nested\\four.txt: no issues found\nnested\\three.txt: no issues found\none.txt: no issues found\ntwo.txt: no issues found\n' ]
    at: onrun (D:\Repositories\unified-engine\test\configuration.js:261:10)
    stack: |-
      Error: should report
          at Test.assert [as _assert] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:224:54)
          at Test.bound [as _assert] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:76:32)
          at Test.tapeDeepEqual (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:421:10)
          at Test.bound [as deepEqual] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:76:32)
          at onrun (D:\Repositories\unified-engine\test\configuration.js:261:10)
          at next (D:\Repositories\unified-engine\lib\index.js:7:6842)
          at next (D:\Repositories\unified-engine\node_modules\trough\index.js:60:14)
          at Immediate.done [as _onImmediate] (D:\Repositories\unified-engine\node_modules\trough\wrap.js:56:16)
          at runCallback (timers.js:705:18)
          at tryOnImmediate (timers.js:676:5)
  ...
# should support searching package files
ok 39 should report
# should support custom rc files
not ok 40 should report
  ---
    operator: deepEqual
    expected: |-
      [ null, 0, 'nested/four.txt: no issues found\nnested/three.txt: no issues found\none.txt: no issues found\ntwo.txt: no issues found\n' ]
    actual: |-
      [ null, 0, 'nested\\four.txt: no issues found\nnested\\three.txt: no issues found\none.txt: no issues found\ntwo.txt: no issues found\n' ]
    at: onrun (D:\Repositories\unified-engine\test\configuration.js:324:10)
    stack: |-
      Error: should report
          at Test.assert [as _assert] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:224:54)
          at Test.bound [as _assert] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:76:32)
          at Test.tapeDeepEqual (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:421:10)
          at Test.bound [as deepEqual] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:76:32)
          at onrun (D:\Repositories\unified-engine\test\configuration.js:324:10)
          at next (D:\Repositories\unified-engine\lib\index.js:7:6842)
          at next (D:\Repositories\unified-engine\node_modules\trough\index.js:60:14)
          at Immediate.done [as _onImmediate] (D:\Repositories\unified-engine\node_modules\trough\wrap.js:56:16)
          at runCallback (timers.js:705:18)
          at tryOnImmediate (timers.js:676:5)
  ...
# should support no config files
not ok 41 should report
  ---
    operator: deepEqual
    expected: |-
      [ null, 0, 'nested/three.txt: no issues found\nnested/two.txt: no issues found\none.txt: no issues found\n' ]
    actual: |-
      [ null, 0, 'nested\\three.txt: no issues found\nnested\\two.txt: no issues found\none.txt: no issues found\n' ]
    at: onrun (D:\Repositories\unified-engine\test\configuration.js:354:10)
    stack: |-
      Error: should report
          at Test.assert [as _assert] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:224:54)
          at Test.bound [as _assert] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:76:32)
          at Test.tapeDeepEqual (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:421:10)
          at Test.bound [as deepEqual] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:76:32)
          at onrun (D:\Repositories\unified-engine\test\configuration.js:354:10)
          at next (D:\Repositories\unified-engine\lib\index.js:7:6842)
          at next (D:\Repositories\unified-engine\node_modules\trough\index.js:60:14)
          at Immediate.done [as _onImmediate] (D:\Repositories\unified-engine\node_modules\trough\wrap.js:56:16)
          at runCallback (timers.js:705:18)
          at tryOnImmediate (timers.js:676:5)
  ...
# should not search if `detectConfig` is `false`
ok 42 should not search for config if `detectConfig` is set to `false`
# should cascade `settings`
ok 43 should configure
ok 44 should report
# configuration
# should cascade `plugins`
ok 45 (unnamed assert)
not ok 46 should work
  ---
    operator: deepEqual
    expected: |-
      [ null, 0, 'nested/one.txt: no issues found\n' ]
    actual: |-
      [ null, 0, 'nested\\one.txt: no issues found\n' ]
    at: onrun (D:\Repositories\unified-engine\test\configuration-plugins.js:36:10)
    stack: |-
      Error: should work
          at Test.assert [as _assert] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:224:54)
          at Test.bound [as _assert] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:76:32)
          at Test.tapeDeepEqual (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:421:10)
          at Test.bound [as deepEqual] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:76:32)
          at onrun (D:\Repositories\unified-engine\test\configuration-plugins.js:36:10)
          at next (D:\Repositories\unified-engine\lib\index.js:7:6842)
          at next (D:\Repositories\unified-engine\node_modules\trough\index.js:60:14)
          at Immediate.done [as _onImmediate] (D:\Repositories\unified-engine\node_modules\trough\wrap.js:56:16)
          at runCallback (timers.js:705:18)
          at tryOnImmediate (timers.js:676:5)
  ...
# should handle failing plugins
ok 47 should work
# should handle missing plugins
ok 48 should work
# should handle invalid plugins
ok 49 should work
# should handle throwing plugins
ok 50 should work
# should handle injected plugins
ok 51 should support a plug-in
ok 52 should support a plug-in--options tuple
ok 53 should work
# configuration-presets
# should fail on invalid `presets`
ok 54 should fail
# should support plugins with the same name
ok 55 should pass the correct options to the deep plugin
ok 56 should pass the correct options to the local plugin
ok 57 should succeed
# should handle missing plugins in presets
ok 58 should succeed
# should reconfigure plugins
ok 59 should pass the correct options to the preset plugin
ok 60 should succeed
# should reconfigure required plugins
ok 61 should pass the correct options to the preset plugin
ok 62 should succeed
# Should reconfigure: turn plugins off
ok 63 should succeed
# should reconfigure settings
ok 64 should configure
ok 65 should succeed
# should reconfigure settings (2)
ok 66 should configure
ok 67 should succeed
# `configTransform`
# should work
ok 68 should pass the correct options to plugin `test`
ok 69 should have one cache entry
ok 70 should set the correct settings
ok 71 should pass the correct options to plugins
ok 72 should succeed
# `defaultConfig`
# should use default config if no config file is found
ok 73 should set the correct configuration
ok 74 should pass the correct options to plugin `test-defaults`
ok 75 should work
# should use found otherwise
ok 76 should set the correct configuration
ok 77 should pass the correct options to plugin `test-found`
ok 78 should work
# stdin
# should support stdin
ok 79 should report
# should not output if `out: false`
ok 80 should report
# should support config files on stdin
ok 81 should configure
ok 82 should work
# output
# should not write to stdout on dirs
ok 83 should report
# should write to stdout on one file
ok 84 should report
# should not write to stdout without `out`
ok 85 should report
# should not write multiple files to stdout
ok 86 should report
# should output files
ok 87 should report
# should write to a path
ok 88 should report
# should write to directories
not ok 89 should report
  ---
    operator: deepEqual
    expected: |-
      [ null, 0, '', 'two', 'one.txt > nested/one.txt: written\n' ]
    actual: |-
      [ null, 0, '', 'two', 'one.txt > nested\\one.txt: written\n' ]
    at: onrun (D:\Repositories\unified-engine\test\output.js:269:10)
    stack: |-
      Error: should report
          at Test.assert [as _assert] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:224:54)
          at Test.bound [as _assert] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:76:32)
          at Test.tapeDeepEqual (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:421:10)
          at Test.bound [as deepEqual] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:76:32)
          at onrun (D:\Repositories\unified-engine\test\output.js:269:10)
          at next (D:\Repositories\unified-engine\lib\index.js:7:6842)
          at next (D:\Repositories\unified-engine\node_modules\trough\index.js:60:14)
          at Immediate.done [as _onImmediate] (D:\Repositories\unified-engine\node_modules\trough\wrap.js:56:16)
          at runCallback (timers.js:705:18)
          at tryOnImmediate (timers.js:676:5)
  ...
# should not create intermediate directories
ok 90 should report
# should write injected files
ok 91 should report
# should not write without file-path
not ok 92 should report
  ---
    operator: deepEqual
    expected: |-
      [ null, 1, '', '<stdin>\n  1:1  error  Error: Cannot write file without an output path ' ]
    actual: |-
      [ null, 1, '', '<stdin>\n  1:1  error  Error: Cannot write file without an output path' ]
    at: onrun (D:\Repositories\unified-engine\test\output.js:388:10)
    stack: |-
      Error: should report
          at Test.assert [as _assert] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:224:54)
          at Test.bound [as _assert] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:76:32)
          at Test.tapeDeepEqual (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:421:10)
          at Test.bound [as deepEqual] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:76:32)
          at onrun (D:\Repositories\unified-engine\test\output.js:388:10)
          at next (D:\Repositories\unified-engine\lib\index.js:7:6842)
          at next (D:\Repositories\unified-engine\node_modules\trough\index.js:60:14)
          at Immediate.done [as _onImmediate] (D:\Repositories\unified-engine\node_modules\trough\wrap.js:56:16)
          at runCallback (timers.js:705:18)
          at tryOnImmediate (timers.js:676:5)
  ...
# should fail when writing files to one path
not ok 93 should report
  ---
    operator: deepEqual
    expected: |-
      [ null, 1, 'one.txt\n  1:1  error  Error: Cannot write multiple files to single output' ]
    actual: |-
      [ null, 1, 'one.txt\n  1:1  error  Error: Cannot write multiple files to single output: D' ]
    at: onrun (D:\Repositories\unified-engine\test\output.js:437:10)
    stack: |-
      Error: should report
          at Test.assert [as _assert] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:224:54)
          at Test.bound [as _assert] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:76:32)
          at Test.tapeDeepEqual (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:421:10)
          at Test.bound [as deepEqual] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:76:32)
          at onrun (D:\Repositories\unified-engine\test\output.js:437:10)
          at next (D:\Repositories\unified-engine\lib\index.js:7:6842)
          at next (D:\Repositories\unified-engine\node_modules\trough\index.js:60:14)
          at Immediate.done [as _onImmediate] (D:\Repositories\unified-engine\node_modules\trough\wrap.js:56:16)
          at runCallback (timers.js:705:18)
          at tryOnImmediate (timers.js:676:5)
  ...
# should fail when writing to non-existent dirs
not ok 94 should report
  ---
    operator: deepEqual
    expected: |-
      [ null, 1, 'one.txt\n  1:1  error  Error: Cannot read output directory. Error:' ]
    actual: |-
      [ null, 1, 'one.txt\n  1:1  error  Error: Cannot write multiple files to single output: D:\\Repositories\\unified-engine\\test\\fixtures\\two-files\\three' ]
    at: onrun (D:\Repositories\unified-engine\test\output.js:470:10)
    stack: |-
      Error: should report
          at Test.assert [as _assert] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:224:54)
          at Test.bound [as _assert] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:76:32)
          at Test.tapeDeepEqual (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:421:10)
          at Test.bound [as deepEqual] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:76:32)
          at onrun (D:\Repositories\unified-engine\test\output.js:470:10)
          at next (D:\Repositories\unified-engine\lib\index.js:7:6842)
          at next (D:\Repositories\unified-engine\node_modules\trough\index.js:60:14)
          at Immediate.done [as _onImmediate] (D:\Repositories\unified-engine\node_modules\trough\wrap.js:56:16)
          at runCallback (timers.js:705:18)
          at tryOnImmediate (timers.js:676:5)
  ...
# tree
# should fail on malformed input
ok 95 should report
# should read and write JSON when `tree` is given
ok 96 should report
# should read JSON when `treeIn` is given
ok 97 should report
# should write JSON when `treeOut` is given
ok 98 should report
# should support `treeOut` for stdin
ok 99 should work
# should support `treeIn` for stdin
ok 100 should work
# should write injected files
ok 101 should work
# inspect
# should write text when `inspect` is given
ok 102 should work
# should support `inspect` for stdin
ok 103 should work
# should support `inspect` with colour
ok 104 should work
# file-path
# should throw on `file-path` with files
ok 105 should fail
# should support `file-path`
ok 106 should report
# color
not ok 107 should support color
  ---
    operator: deepEqual
    expected: |-
      [ null, 1, '\x1b[4m\x1b[31mreadme.md\x1b[39m\x1b[24m\n  1:1  \x1b[31merror\x1b[39m  No such file or directory\n\n\x1b[31mΓ£û\x1b[39m 1 error\n' ]
    actual: |-
      [ null, 1, '\x1b[4m\x1b[31mreadme.md\x1b[39m\x1b[24m\n  1:1  \x1b[31merror\x1b[39m  No such file or directory\n\n\x1b[31m×\x1b[39m 1 error\n' ]
    at: onrun (D:\Repositories\unified-engine\test\color.js:38:7)
    stack: |-
      Error: should support color
          at Test.assert [as _assert] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:224:54)
          at Test.bound [as _assert] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:76:32)
          at Test.tapeDeepEqual (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:421:10)
          at Test.bound [as deepEqual] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:76:32)
          at onrun (D:\Repositories\unified-engine\test\color.js:38:7)
          at next (D:\Repositories\unified-engine\lib\index.js:7:6842)
          at next (D:\Repositories\unified-engine\node_modules\trough\index.js:60:14)
          at Immediate.done [as _onImmediate] (D:\Repositories\unified-engine\node_modules\trough\wrap.js:56:16)
          at runCallback (timers.js:705:18)
          at tryOnImmediate (timers.js:676:5)
  ...
# reporting
# should fail for warnings with `frail`
not ok 108 should report
  ---
    operator: deepEqual
    expected: |-
      [ null, 1, 'one.txt\n  1:1  warning  Warning\n\nΓÜá 1 warning\n' ]
    actual: |-
      [ null, 1, 'one.txt\n  1:1  warning  Warning\n\n‼ 1 warning\n' ]
    at: onrun (D:\Repositories\unified-engine\test\reporting.js:43:10)
    stack: |-
      Error: should report
          at Test.assert [as _assert] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:224:54)
          at Test.bound [as _assert] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:76:32)
          at Test.tapeDeepEqual (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:421:10)
          at Test.bound [as deepEqual] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:76:32)
          at onrun (D:\Repositories\unified-engine\test\reporting.js:43:10)
          at next (D:\Repositories\unified-engine\lib\index.js:7:6842)
          at next (D:\Repositories\unified-engine\node_modules\trough\index.js:60:14)
          at Immediate.done [as _onImmediate] (D:\Repositories\unified-engine\node_modules\trough\wrap.js:56:16)
          at runCallback (timers.js:705:18)
          at tryOnImmediate (timers.js:676:5)
  ...
# should not report succesful files when `quiet` (#1)
not ok 109 should report
  ---
    operator: deepEqual
    expected: |-
      [ null, 0, 'two.txt\n  1:1  warning  Warning\n\nΓÜá 1 warning\n' ]
    actual: |-
      [ null, 0, 'two.txt\n  1:1  warning  Warning\n\n‼ 1 warning\n' ]
    at: onrun (D:\Repositories\unified-engine\test\reporting.js:77:10)
    stack: |-
      Error: should report
          at Test.assert [as _assert] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:224:54)
          at Test.bound [as _assert] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:76:32)
          at Test.tapeDeepEqual (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:421:10)
          at Test.bound [as deepEqual] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:76:32)
          at onrun (D:\Repositories\unified-engine\test\reporting.js:77:10)
          at next (D:\Repositories\unified-engine\lib\index.js:7:6842)
          at next (D:\Repositories\unified-engine\node_modules\trough\index.js:60:14)
          at Immediate.done [as _onImmediate] (D:\Repositories\unified-engine\node_modules\trough\wrap.js:56:16)
          at runCallback (timers.js:705:18)
          at tryOnImmediate (timers.js:676:5)
  ...
# should not report succesful files when `quiet` (#2)
ok 110 should not report
# should not report succesful files when `silent`
not ok 111 should report
  ---
    operator: deepEqual
    expected: |-
      [ null, 1, 'two.txt\n  1:1  error  Error\n\nΓ£û 1 error\n' ]
    actual: |-
      [ null, 1, 'two.txt\n  1:1  error  Error\n\n× 1 error\n' ]
    at: onrun (D:\Repositories\unified-engine\test\reporting.js:135:10)
    stack: |-
      Error: should report
          at Test.assert [as _assert] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:224:54)
          at Test.bound [as _assert] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:76:32)
          at Test.tapeDeepEqual (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:421:10)
          at Test.bound [as deepEqual] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:76:32)
          at onrun (D:\Repositories\unified-engine\test\reporting.js:135:10)
          at next (D:\Repositories\unified-engine\lib\index.js:7:6842)
          at next (D:\Repositories\unified-engine\node_modules\trough\index.js:60:14)
          at Immediate.done [as _onImmediate] (D:\Repositories\unified-engine\node_modules\trough\wrap.js:56:16)
          at runCallback (timers.js:705:18)
          at tryOnImmediate (timers.js:676:5)
  ...
# should support custom reporters (without prefix)
ok 112 should be equivalent
# should support custom reporters (with prefix)
ok 113 should be equivalent
# should fail on an unfound reporter
ok 114 should be equal
# completers
# should pass `fileSet` to plug-ins
ok 115 should pass a set
ok 116 should have a `use` method
ok 117 should have an `add` method
ok 118 should be able to `use` a completer
ok 119 should be able to `add` a file
ok 120 should pass a set
ok 121 should have a `use` method
ok 122 should have an `add` method
ok 123 should be able to `use` a completer
ok 124 should be able to `add` a file
ok 125 should expose the files and set to `completer` (1)
ok 126 should expose the files and set to `completer` (2)
ok 127 should work
# should pass `fileSet` to plug-ins
not ok 128 should work
  ---
    operator: deepEqual
    expected: |-
      [ null, 0, '', 'foo.txt > nested/foo.txt: written\n' ]
    actual: |-
      [ null, 0, '', 'foo.txt > nested\\foo.txt: written\n' ]
    at: onrun (D:\Repositories\unified-engine\test\completers.js:118:10)
    stack: |-
      Error: should work
          at Test.assert [as _assert] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:224:54)
          at Test.bound [as _assert] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:76:32)
          at Test.tapeDeepEqual (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:421:10)
          at Test.bound [as deepEqual] (D:\Repositories\unified-engine\node_modules\tape\lib\test.js:76:32)
          at onrun (D:\Repositories\unified-engine\test\completers.js:118:10)
          at next (D:\Repositories\unified-engine\lib\index.js:7:6842)
          at next (D:\Repositories\unified-engine\node_modules\trough\index.js:60:14)
          at Immediate.done [as _onImmediate] (D:\Repositories\unified-engine\node_modules\trough\wrap.js:56:16)
          at runCallback (timers.js:705:18)
          at tryOnImmediate (timers.js:676:5)
  ...
# settings
# should use `settings`
ok 129 should configure
ok 130 should report
# should cascade `settings`
ok 131 should configure
ok 132 should report
# plugins
# should use `plugins` as list of functions
ok 133 transformer
ok 134 transformer
ok 135 should report
# should use `plugins` as list of strings
ok 136 should pass the correct options to the preset plugin
ok 137 should report
# should use `plugins` as list of objects
ok 138 should pass the correct options to the preset plugin
ok 139 should report

1..139
# tests 139
# pass  113
# fail  26

ERROR: Coverage for lines (99.87%) does not meet global threshold (100%)
ERROR: Coverage for branches (99.75%) does not meet global threshold (100%)
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! [email protected] test-coverage: `nyc --reporter lcov tape test/index.js`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the [email protected] test-coverage script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     C:\Users\deusd\AppData\Roaming\npm-cache\_logs\2018-12-15T17_07_15_619Z-debug.log
npm ERR! Test failed.  See above for more details.

Use native Object.assign instead of xtend

xtend can easily be replaced with Object.assign

This reduce the dependencies and possible also avoid duplicated versions where some package don't use the same version range for the xtend package

// immutable
Object.assign({}, a, b)

// mutable
Object.assign(a, b)

Fix support for ignores in input globs

Was trying to process all files except for readme.md, so I tried remark . !readme.md, which doesn’t work, probably because glob.hasMagic('!readme.md') yields false.
Doing remark . !**/readme.md also doesn’t work, and writes a \!readme.md file 🤔

Another direction to go would be to allow inline ignore patterns with something like --ignore, but I thought normal globs would work as well 🤷‍♂️

Do not set `extname` on stdin

🚀  example: rehype --tree-out -s position:false <<< '<p>Regular. <tt>toString()</tt>. Regular.</p>'
<stdin>
  1:1  error  Error: Setting `extname` requires `path` to be set too
    at assertPath (/Users/tilde/.nvm/versions/node/v8.1.2/lib/node_modules/rehype-cli/node_modules/vfile/index.js:269:11)
    at VFile.set (/Users/tilde/.nvm/versions/node/v8.1.2/lib/node_modules/rehype-cli/node_modules/vfile/index.js:122:5)
    at stringify (/Users/tilde/.nvm/versions/node/v8.1.2/lib/node_modules/rehype-cli/node_modules/unified-engine/lib/file-pipeline/stringify.js:29:18)
    at wrapped (/Users/tilde/.nvm/versions/node/v8.1.2/lib/node_modules/rehype-cli/node_modules/trough/index.js:93:19)
    at next (/Users/tilde/.nvm/versions/node/v8.1.2/lib/node_modules/rehype-cli/node_modules/trough/index.js:56:24)
    at Object.run (/Users/tilde/.nvm/versions/node/v8.1.2/lib/node_modules/rehype-cli/node_modules/trough/index.js:30:10)
    at run (/Users/tilde/.nvm/versions/node/v8.1.2/lib/node_modules/rehype-cli/node_modules/unified-engine/lib/file-pipeline/index.js:30:10)
    at wrapped (/Users/tilde/.nvm/versions/node/v8.1.2/lib/node_modules/rehype-cli/node_modules/trough/index.js:93:19)
    at next (/Users/tilde/.nvm/versions/node/v8.1.2/lib/node_modules/rehype-cli/node_modules/trough/index.js:56:24)
    at done (/Users/tilde/.nvm/versions/node/v8.1.2/lib/node_modules/rehype-cli/node_modules/trough/index.js:124:12)

✖ 1 error

Incorrect reference to `injectPlugins` in plugins.md

The plugins.md documentation refers to injectPlugins, however, this doesn't seem to be an option for unified-engine. Looking at the source code, it appears that a plugin function can be passed directly in the plugins array.

upgrade `load-plugin` (breaking change?)

Initial checklist

Affected packages and versions

unified-engine

Link to runnable example

No response

Steps to reproduce

wooorm/load-plugin#13

Expected behavior

Install without desperation warning

Actual behavior

warning

Runtime

No response

Package manager

No response

OS

No response

Build and bundle tools

No response

Warning on install

warning remark-cli > unified-args > unified-engine > [email protected]: Just use Array.isArray directly

Latest stable node version support Array.isArray. Can we use this instead isarray?

accept `VFile` without `path` as `options.files` input

Initial checklist

Problem

i am trying to use unified-engine in a vscode extension to lint markdown/mdx, similar to this one.

this works great when feeding unified-engine a VFile with an actual on-disk file path. however, it seems that it is not possible to feed it a VFile without a valid file path - for example to lint unsaved files, for which vscode does not provide a file path.

Solution

the following should work:

import { VFile } from "vfile";
import remark from "remark";
import engine from "unified-engine";

const file = new VFile({ value: "Some text" });

engine(
  {
    processor: remark,
    files: [file],
  },
  function done(error, code) {
    if (error) throw error;
  }
);

but throws:

TypeError [ERR_INVALID_ARG_TYPE]: The "to" argument must be of type string. Received undefined
    at validateString (internal/validators.js:124:11)
    at Object.relative (path.js:1168:5)
    at each (/data/Development/playground/engine/node_modules/unified-engine/lib/finder.js:53:24)

Alternatives

not sure

Trough functions should be async

Initial checklist

Problem

Trough native supports middle-ware return promises, but all of the unified middle-ware are sync functions. This cause a problem when creating some plugins.

Solution

Update at least the stringify functions to be async and add a await call to the context.prcessor.stringify() .

Alternatives

Could assert that all plug-ins are sync, but no need when trough handles async for us.

Throw exception when trying to output stdin(4) without `filePath` to file-system

🚀  example: rehype --output <<< '<p>Regular. <tt>toString()</tt>. Regular.</p>'

...currently silently does not output to the file-system.

Either choose a file-name: perhaps a name of index and an extension of extensions[0] (in the case of rehype, html, for remark, md) or json with treeOut?

Or throw an exception, which makes more sense than picking a potentially overwriting file-path.

Ignored files are accounted for in processing list and result in non-zero exit code

Subject of the issue

Files that are in the ignore list are still added to the list of files to process. This makes it difficult to easily run the engine against a large directory (e.g. one that contains node_modules) using a **/** glob as it will find the argument list is too long. Additionally, it makes it more difficult to use within CI, as trying to process and ignored file results in an error being produced rather than silently skipping over files the user has already marked as not wanting to be accounted for.

Steps to reproduce

  1. Add a file name to the ignoreName list when calling engine

Expected behaviour

The engine silently ignored the file, acting as if the file doesn't exist and not attempting to process it. The engine would producing a 'no files were found' message if the user tried to process only ignored file.

Actual behaviour

The error message error Cannot process specified file: it’s ignored is produced and a non-zero return code returned.

[feat] export `Configuration` for reusing

Initial checklist

Problem

Configuration is used in eslint-mdx for loading remark configuration by using unified-engine/lib/configuration

https://github.com/mdx-js/eslint-mdx/blob/1ca5afd1d6e40d021d33c6f3177447e38e7263db/packages/eslint-mdx/src/worker.ts#L67-L76

It should be considered as private API path, I see we are migrating to exports field in package.json, which might make Configuration unavailable later.

Solution

export Configuration from root at the same time.

Alternatives

N/A

Don’t ignore dotfiles by default

Initial checklist

Problem

IMO it’s a bad practice to exclude dotfiles from a project’s code quality standards such as remark-lint. I.e. I don’t understand why one wouldn’t want to check the various markdown files in the .github or .gitlab directory.

Currently there appears to be no way to include dotfiles, except by explicitly passing the dotfile to the CLI.

I think this idea originates from ESLint (where I also think it’s a bad idea), but at least ESLint allows to unignore dotfiles by adding the following .eslintignore file:

!.*

This doesn’t work for .remarkignore.

Solution

Don’t ignore dotfiles by default.

Alternatives

Allow to unignore dotfiles.

Allowing `node_modules`

The blanket ban on any path containing node_modules is problematic; see finder.js. Specifying whether files/paths should be ignored should be left to the .remarkignore file.

For instance, suppose I wanted to ignore only the top-level node_modules directory. My .remarkignore file (just as with a .gitignore file) would be

/node_modules/

If I wanted to ignore all paths containing node_modules, I could do

node_modules/
**/node_modules

But this is not how the unified-engine currently works. Instead, it just assumes that any path which has node_modules is off limits. This is problematic for project structures like

bin/
lib/node_modules/
lib/node_modules/**/node_modules/
node_modules/
test

where the top-level node_modules directory is reserved for external libraries and lib node modules directories are used for local modules. These local node modules are not in any sense "external" and will, for most cases, be considered part of the core library; e.g., tested and used like any other internal file.

Hence, I would suggest removing the blanket ban on node_modules and leave it to the user to specify if, and how, node_modules should be ignored.

Apply pluginPrefix to scoped packages

Current behavior

When I have installed @shinnn/remark-preset-lint package to the current project directory, add

"remarkConfig": {
  "plugins": [
    "@shinnn/preset-lint"
  ]
}

to package.json and run the code below:

'use strict';

const engine = require('unified-engine')
var remark = require('remark')

engine({
  processor: remark,
  files: [__filename],
  pluginPrefix: 'remark',
  packageField: 'remarkConfig'
}, () => {});

it prints this error:

$ node index.js .
index.js
  1:1  error  Error: Could not find module `@shinnn/preset-lint`
    at addModule (/Users/shinnn/github/remark/node_modules/unified-engine/lib/configuration.js:242:27)
    at use (/Users/shinnn/github/remark/node_modules/unified-engine/lib/configuration.js:203:7)
    at addEach (/Users/shinnn/github/remark/node_modules/unified-engine/lib/configuration.js:188:9)
    at addPreset (/Users/shinnn/github/remark/node_modules/unified-engine/lib/configuration.js:164:9)
    at merge (/Users/shinnn/github/remark/node_modules/unified-engine/lib/configuration.js:150:5)
    at Config.create (/Users/shinnn/github/remark/node_modules/unified-engine/lib/configuration.js:105:5)
    at done (/Users/shinnn/github/remark/node_modules/unified-engine/lib/find-up.js:148:20)

As you can see, pluginPrefix option doesn't automatically prefix scoped packages.

Expected behavior

I'd like @${scope}/{plugin name without pluginPrefix} to be resolved to @${scope}/{full plugin name}.


@wooorm if this proposal is acceptable to you, I can create a pull request.

`format` options to specify custom file formatter

Create a vim errorformat processor like atom's unified processor to https://github.com/wooorm/unified-engine-atom

-- basically having everything on one line with single character (space or colon, doesn't matter) delimiters. This translates to %f:%l:%c %tarning %m in vim's errorformat (where %t captures the first character to get the warning/error type, using the literal arning is optional, a %* group could be used instead)

source: davidosomething/projects#1 (comment)

Preset from a local file

I've created an NPM module that is supposed to work as a remark preset inside other modules. Linting rules are defined in index.js. What I would like to do is to lint README.md in my preset module with the same rules as well.

I tried the following in package.json, but it did not work:

  "remarkConfig": {
    "presets": ["./index.js"]
  },

The solution was to create .remarkrc.js next to package.json with the following content:

module.exports = require('./index.js');

After I've don this, remark-cli has started picking up my rules.

Although my problem is solved in general, I'm wondering if it is possible to avoid this extra .remarkrc.js file by being able to refer to a local preset in package.json. Maybe it is allowed already, it's just me doing something wrong. Sharing my solution as someone else might face the same problem.

Support EditorConfig

Initial checklist

Problem

EditorConfig defines certain code-style preferences.
Some of those can be done by the engine (charset, insert_final_newline).
Others by the processor (syntax-tree/mdast-util-to-markdown#51, syntax-tree/mdast-util-to-markdown#50; what about HTML though where the whitespace is part of the AST?).
Some are ambiguous (end_of_line).

Solution

Can we load (as an option), editorconfig files, and pass their keys, camelcased, to settings?
That passes them to *-parse and *-stringify.
We could pass a nested object if there’s fear that it would conflict, but I don’t think it does?

Alternatives

  • Custom adapter functions (e.g., passed by remark-cli to the engine) that transform an EditorConfig into certain settings? Could be modules, e.g., called editorconfig-to-remark-settings or so?

Only the first vfile is processed

Initial checklist

Affected packages and versions

[email protected]

Link to runnable example

No response

Steps to reproduce

Run the following script:

#!/usr/bin/env node
import {unified} from 'unified'
import {engine} from 'unified-engine'
import {VFile} from 'vfile'

function plugin() {
  this.Parser = () => ({type: 'root'})
  this.Compiler = () => ''

  return (ast, file) => {
    file.message('Oh noez!')
  }
}

const files = [
  new VFile({path: new URL('file:///a.txt'), value: 'a'}),
  new VFile({path: new URL('file:///b.txt'), value: 'b'})
]

engine(
  {
    files,
    processor: unified().use(plugin)
  },
  (error, code, context) => {
    console.dir(context.files, {depth: 3})
  }
)

Expected behavior

For all given files the unified plugins are ran.

Actual behavior

Unified plugins are only run for the first vfile given.

If file paths are given as strings instead of vfiles, all given paths are processed.

If a combination of vfiles and string paths is given, all string paths are processed up to the first vfile.

Runtime

Node v16

Package manager

npm v6

OS

Linux

Build and bundle tools

Other (please specify in steps to reproduce)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.