Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

remark-message-control

Package Overview
Dependencies
Maintainers
1
Versions
18
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

remark-message-control - npm Package Compare versions

Comparing version 2.0.2 to 2.0.3

LICENSE

580

index.js

@@ -11,8 +11,3 @@ /**

/* eslint-env commonjs */
/*
* Dependencies.
*/
/* Dependencies. */
var trim = require('trim');

@@ -23,387 +18,310 @@ var vfileLocation = require('vfile-location');

/*
* Map of allowed verbs.
*/
/* Map of allowed verbs. */
var ALLOWED_VERBS = {
'enable': true,
'disable': true,
'ignore': true
enable: true,
disable: true,
ignore: true
};
/**
* Detect gaps in `ast`.
*
* @param {Node} ast - Syntax tree.
* @param {VFile} file - Virtual file.
* @return {Array.<Object>} - Gaps.
*/
function detectGaps(ast, file) {
var lastNode = ast.children[ast.children.length - 1];
var offset = 0;
var isGap = false;
var gaps = [];
/* Expose. */
module.exports = attacher;
/**
* Patch a new position.
*
* @param {number?} [latest] - Last found position.
*/
function update(latest) {
if (latest === undefined || latest === null) {
isGap = true;
/* Filter .*/
function attacher(processor, options) {
var name = options && options.name;
var sources;
var known;
var reset;
var enable;
var disable;
return;
}
if (!name) {
throw new Error('Expected `name` in `options`, got `' + name + '`');
}
if (offset >= latest) {
return;
}
known = options.known;
reset = options.reset;
enable = options.enable || [];
disable = options.disable || [];
sources = options.source;
if (isGap) {
gaps.push({
'start': offset,
'end': latest
});
if (!sources) {
sources = [name];
} else if (typeof sources === 'string') {
sources = [sources];
}
isGap = false;
}
return transformer;
offset = latest;
}
function transformer(ast, file) {
var location = vfileLocation(file);
var initial = !reset;
var gaps = detectGaps(ast, file);
var scope = {};
var globals = [];
/*
* Find all gaps.
/**
* Helper to check (and possibly warn) if a ruleId
* is unknown.
*
* @param {string} ruleId - Identifier.
* @param {string} verb - Thing to do with `ruleId`.
* @param {Position} pos - Position of marker.
*/
function isKnown(ruleId, verb, pos) {
var result = known ? known.indexOf(ruleId) !== -1 : true;
visit(ast, function (node) {
var pos = node.position;
if (!result) {
file.warn('Unknown rule: cannot ' + verb + ' `\'' + ruleId + '\'`', pos);
}
update(pos && pos.start && pos.start.offset);
return result;
}
if (!node.children) {
update(pos && pos.end && pos.end.offset);
}
});
/* Get the latest state of a rule.
* When without `ruleId`, gets global state. */
function getState(ruleId) {
var ranges = ruleId ? scope[ruleId] : globals;
/*
* Get the end of the document.
* This detects if the last node was the last node.
* If not, there’s an extra gap between the last node
* and the end of the document.
*/
if (ranges && ranges.length) {
return ranges[ranges.length - 1].state;
}
if (
lastNode &&
lastNode.position &&
lastNode.position.end &&
offset === lastNode.position.end.offset &&
trim(file.toString().slice(offset)) !== ''
) {
update();
if (!ruleId) {
return !reset;
}
update(
ast &&
ast.position &&
ast.position.end &&
ast.position.end.offset - 1
);
if (reset) {
return enable.indexOf(ruleId) !== -1;
}
return disable.indexOf(ruleId) === -1;
}
return gaps;
}
/* Handle a rule. */
function toggle(pos, state, ruleId) {
var markers = ruleId ? scope[ruleId] : globals;
var currentState;
var previousState;
/**
* Attacher.
*
* @param {Processor} processor - Instance.
* @param {Object?} [options] - Configuration.
* @return {Function} - Transformer.
*/
function attacher(processor, options) {
var name = options && options.name;
var sources;
var known;
var reset;
var enable;
var disable;
if (!markers) {
markers = scope[ruleId] = [];
}
if (!name) {
throw new Error(
'Expected `name` in `options`, got `' + name + '`'
);
}
previousState = getState(ruleId);
currentState = state;
known = options.known;
reset = options.reset;
enable = options.enable || [];
disable = options.disable || [];
sources = options.source;
if (currentState !== previousState) {
markers.push({state: currentState, position: pos});
}
if (!sources) {
sources = [name];
} else if (typeof sources === 'string') {
sources = [sources];
/* Toggle all known rules. */
if (!ruleId) {
for (ruleId in scope) {
toggle(pos, state, ruleId);
}
}
}
return function (ast, file) {
var location = vfileLocation(file);
var initial = !reset;
var gaps = detectGaps(ast, file);
var scope = {};
var globals = [];
visit(ast, 'html', function (node, position, parent) {
var mark = marker(node);
var ruleIds;
var ruleId;
var verb;
var index;
var length;
var next;
var pos;
var tail;
/**
* Helper to check (and possibly warn) if a ruleId
* is unknown.
*
* @param {string} ruleId - Identifier.
* @param {string} verb - Thing to do with `ruleId`.
* @param {Position} pos - Position of marker.
*/
function isKnown(ruleId, verb, pos) {
var result = known ? known.indexOf(ruleId) !== -1 : true;
if (!mark || mark.name !== options.name) {
return;
}
if (!result) {
file.warn(
'Unknown rule: cannot ' + verb +
' `\'' + ruleId + '\'`',
pos
);
}
ruleIds = mark.attributes.split(/\s/g);
verb = ruleIds.shift();
next = parent.children[position + 1];
pos = mark.node.position && mark.node.position.start;
tail = next && next.position && next.position.end;
return result;
}
if (!verb || !ALLOWED_VERBS[verb] === true) {
return file.fail(
'Unknown keyword `' + verb + '`: expected ' +
'`\'enable\'`, `\'disable\'`, or `\'ignore\'`',
mark.node
);
}
/**
* Get the latest state of a rule.
* When without `ruleId`, gets global state.
*
* @param {string?} [ruleId] - Unique rule name.
*/
function getState(ruleId) {
var ranges = ruleId ? scope[ruleId] : globals;
length = ruleIds.length;
index = -1;
if (ranges && ranges.length) {
return ranges[ranges.length - 1].state;
}
while (++index < length) {
ruleId = ruleIds[index];
if (!ruleId) {
return !reset;
}
if (isKnown(ruleId, verb, mark.node)) {
toggle(pos, verb === 'enable', ruleId);
if (reset) {
return enable.indexOf(ruleId) !== -1;
}
if (verb === 'ignore') {
toggle(tail, true, ruleId);
}
}
}
return disable.indexOf(ruleId) === -1;
/* Apply to all rules. */
if (!length) {
if (verb === 'ignore') {
toggle(pos, false);
toggle(tail, true);
} else {
toggle(pos, verb === 'enable');
reset = verb !== 'enable';
}
}
});
/**
* Handle a rule.
*
* @param {Position} pos - Starting position.
* @param {boolean} state - State to toggle to.
* @param {*?} [ruleId] - Rule to toggle.
*/
function toggle(pos, state, ruleId) {
var markers = ruleId ? scope[ruleId] : globals;
var currentState;
var previousState;
/* Check all `ranges` for `message`. */
function check(message, ranges, id) {
/* Check the state at the message's position. */
var index = ranges && ranges.length;
var length = -1;
var range;
if (!markers) {
markers = scope[ruleId] = [];
}
while (--index > length) {
range = ranges[index];
previousState = getState(ruleId);
currentState = state;
/* istanbul ignore if - generated marker. */
if (
!range.position ||
!range.position.line ||
!range.position.column
) {
continue;
}
if (currentState !== previousState) {
markers.push({
'state': currentState,
'position': pos
});
}
/*
* Toggle all known rules.
*/
if (!ruleId) {
for (ruleId in scope) {
toggle(pos, state, ruleId);
}
}
if (
range.position.line < message.line ||
(
range.position.line === message.line &&
range.position.column < message.column
)
) {
return range.state === true;
}
}
visit(ast, 'html', function (node, position, parent) {
var mark = marker(node);
var ruleIds;
var ruleId;
var verb;
var index;
var length;
var next;
var pos;
var tail;
/* The first marker ocurred after the first
* message, so we check the initial state. */
if (!id) {
return initial || reset;
}
if (!mark || mark.name !== options.name) {
return;
}
return reset ? enable.indexOf(id) !== -1 : disable.indexOf(id) === -1;
}
ruleIds = mark.attributes.split(/\s/g);
verb = ruleIds.shift();
next = parent.children[position + 1];
pos = mark.node.position && mark.node.position.start;
tail = next && next.position && next.position.end;
file.messages = file.messages.filter(function (message) {
var gapIndex = gaps.length;
var ruleId = message.ruleId;
var ranges = scope[ruleId];
var pos;
if (!verb || !ALLOWED_VERBS[verb] === true) {
return file.fail(
'Unknown keyword `' + verb + '`: expected ' +
'`\'enable\'`, `\'disable\'`, or `\'ignore\'`',
mark.node
);
}
/* Keep messages from a different source. */
if (!message.source || sources.indexOf(message.source) === -1) {
return true;
}
length = ruleIds.length;
index = -1;
/* We only ignore messages if they‘re disabled,
* *not* when they’re not in the document. */
if (!message.line) {
message.line = 1;
}
while (++index < length) {
ruleId = ruleIds[index];
if (!message.column) {
message.column = 1;
}
if (isKnown(ruleId, verb, mark.node)) {
toggle(pos, verb === 'enable', ruleId);
/* Check whether the warning is inside a gap. */
pos = location.toOffset(message);
if (verb === 'ignore') {
toggle(tail, true, ruleId);
}
}
}
while (gapIndex--) {
if (
gaps[gapIndex].start <= pos &&
gaps[gapIndex].end > pos
) {
return false;
}
}
/*
* Apply to all rules.
*/
/* Check whether allowed by specific and global states. */
return check(message, ranges, ruleId) && check(message, globals);
});
}
}
if (!length) {
if (verb === 'ignore') {
toggle(pos, false);
toggle(tail, true);
} else {
toggle(pos, verb === 'enable');
reset = verb !== 'enable';
}
}
});
/**
* Detect gaps in `ast`.
*
* @param {Node} ast - Syntax tree.
* @param {VFile} file - Virtual file.
* @return {Array.<Object>} - Gaps.
*/
function detectGaps(ast, file) {
var lastNode = ast.children[ast.children.length - 1];
var offset = 0;
var isGap = false;
var gaps = [];
/**
* Check all `ranges` for `message`.
*
* @param {Object} message - Message.
* @param {Array} ranges - Current ranges.
* @param {*?} id - Identifier.
*/
function check(message, ranges, id) {
/*
* Check the state at the message's position.
*/
/* Find all gaps. */
visit(ast, one);
var index = ranges && ranges.length;
var length = -1;
var range;
/* Get the end of the document.
* This detects if the last node was the last node.
* If not, there’s an extra gap between the last node
* and the end of the document. */
if (
lastNode &&
lastNode.position &&
lastNode.position.end &&
offset === lastNode.position.end.offset &&
trim(file.toString().slice(offset)) !== ''
) {
update();
while (--index > length) {
range = ranges[index];
update(
ast &&
ast.position &&
ast.position.end &&
ast.position.end.offset - 1
);
}
/* istanbul ignore if - generated marker. */
if (
!range.position ||
!range.position.line ||
!range.position.column
) {
continue;
}
return gaps;
if (
range.position.line < message.line ||
(
range.position.line === message.line &&
range.position.column < message.column
)
) {
return range.state === true;
}
}
function one(node) {
var pos = node.position;
/*
* The first marker ocurred after the first
* message, so we check the initial state.
*/
update(pos && pos.start && pos.start.offset);
if (!id) {
return initial || reset;
}
if (!node.children) {
update(pos && pos.end && pos.end.offset);
}
}
if (reset) {
return enable.indexOf(id) !== -1;
}
/* Detect a new position. */
function update(latest) {
if (latest == null) {
isGap = true;
return;
}
return disable.indexOf(id) === -1;
}
if (offset >= latest) {
return;
}
file.messages = file.messages.filter(function (message) {
var gapIndex = gaps.length;
var ruleId = message.ruleId;
var ranges = scope[ruleId];
var pos;
if (isGap) {
gaps.push({start: offset, end: latest});
isGap = false;
}
/*
* Keep messages from a different source.
*/
if (!message.source || sources.indexOf(message.source) === -1) {
return true;
}
/*
* We only ignore messages if they‘re disabled,
* *not* when they’re not in the document.
*/
if (!message.line) {
message.line = 1;
}
if (!message.column) {
message.column = 1;
}
/*
* Check whether the warning is inside a gap.
*/
pos = location.toOffset(message);
while (gapIndex--) {
if (
gaps[gapIndex].start <= pos &&
gaps[gapIndex].end > pos
) {
return false;
}
}
/*
* Check whether allowed by specific and global
* states.
*/
return check(message, ranges, ruleId) && check(message, globals);
});
};
offset = latest;
}
}
/*
* Expose.
*/
module.exports = attacher;
{
"name": "remark-message-control",
"version": "2.0.2",
"version": "2.0.3",
"description": "Enable, disable, and ignore messages with remark",

@@ -33,18 +33,10 @@ "license": "MIT",

"browserify": "^13.0.0",
"eslint": "^2.0.0",
"esmangle": "^1.0.0",
"istanbul": "^0.4.0",
"jscs": "^3.0.0",
"jscs-jsdoc": "^2.0.0",
"remark": "^5.0.0",
"remark-cli": "^1.0.0",
"remark-comment-config": "^4.0.0",
"remark-github": "^5.0.0-alpha.1",
"remark-lint": "^4.0.0",
"remark-slug": "^4.0.0",
"nyc": "^8.1.0",
"remark": "^6.0.0",
"remark-cli": "^2.0.0",
"remark-preset-wooorm": "^1.0.0",
"remark-toc": "^3.0.0",
"remark-validate-links": "^4.0.0-alpha.1",
"remark-yaml-config": "^3.0.0",
"tape": "^4.0.0",
"vfile-reporter": "^1.5.0"
"xo": "^0.16.0"
},

@@ -56,9 +48,26 @@ "scripts": {

"build": "npm run build-md && npm run build-bundle && npm run build-mangle",
"lint-api": "eslint .",
"lint-style": "jscs --reporter inline .",
"lint": "npm run lint-api && npm run lint-style",
"lint": "xo",
"test-api": "node test.js",
"test-coverage": "istanbul cover test.js",
"test-coverage": "nyc --reporter lcov tape test.js",
"test": "npm run build && npm run lint && npm run test-coverage"
},
"xo": {
"space": true,
"rules": {
"no-eq-null": "off",
"eqeqeq": [
2,
"allow-null"
],
"guard-for-in": "off",
"max-lines": "off"
},
"ignores": [
"remark-message-control.js"
]
},
"remarkConfig": {
"output": true,
"presets": "wooorm"
}
}

@@ -10,10 +10,36 @@ # remark-message-control [![Build Status][build-badge]][build-status] [![Coverage Status][coverage-badge]][coverage-status] [![Chat][chat-badge]][chat]

```bash
npm install remark-parse
npm install remark-message-control
```
**remark-message-control** is also available as an AMD, CommonJS, and
globals module, [uncompressed and compressed][releases].
## Usage
```js
var remark = require('remark');
var report = require('vfile-reporter');
var control = require('remark-message-control');
remark().use(warn).use(control, {name: 'foo'}).process([
'<!--foo ignore-->',
'',
'## Heading',
''
].join('\n'), function (err, file) {
console.log(report(err || file));
});
function warn() {
return function (tree, file) {
var message = file.message('Whoops!', tree.children[1]);
message.ruleId = 'thing';
message.source = 'foo';
};
}
```
Yields:
```txt
no issues found
```
## API

@@ -23,3 +49,3 @@

Let comment markers control messages from a certain source.
Let comment markers control messages from a certain sources.

@@ -112,4 +138,2 @@ ###### `options`

[releases]: https://github.com/wooorm/remark-message-control/releases
[license]: LICENSE

@@ -116,0 +140,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc