tools: update to ESLint 4.10.0

PR-URL: https://github.com/nodejs/node/pull/16738
Reviewed-By: Rich Trott <rtrott@gmail.com>
Reviewed-By: Anatoli Papirovski <apapirovski@mac.com>
This commit is contained in:
cjihrig 2017-11-03 23:38:04 -04:00
parent ba885c42ef
commit 5f6d64789d
No known key found for this signature in database
GPG Key ID: 7434390BDBE9B9C5
263 changed files with 9336 additions and 8841 deletions

View File

@ -27,6 +27,8 @@ ESLint is a tool for identifying and reporting on patterns found in ECMAScript/J
## Installation and Usage
Prerequisites: [Node.js](https://nodejs.org/en/) (>=4.x), npm version 2+.
There are two ways to install ESLint: globally and locally.
### Local Installation and Usage
@ -132,6 +134,8 @@ These folks keep the project moving and are resources for help.
* Vitor Balocco ([@vitorbal](https://github.com/vitorbal))
* James Henry ([@JamesHenry](https://github.com/JamesHenry))
* Reyad Attiyat ([@soda0289](https://github.com/soda0289))
* 薛定谔的猫 ([@Aladdin-ADD](https://github.com/Aladdin-ADD))
* Victor Hom ([@VictorHom](https://github.com/VictorHom))
## Releases
@ -202,11 +206,23 @@ Maybe, depending on how much you need it. [JSCS has reached end of life](https:/
If you are having issues with JSCS, you can try to move to ESLint. We are focusing our time and energy on JSCS compatibility issues.
### Is ESLint just linting or does it also check style?
ESLint does both traditional linting (looking for problematic patterns) and style checking (enforcement of conventions). You can use it for both.
### Why can't ESLint find my plugins?
ESLint can be [globally or locally installed](#installation-and-usage). If you install ESLint globally, your plugins must also be installed globally; if you install ESLint locally, your plugins must also be installed locally.
If you are trying to run globally, make sure your plugins are installed globally (use `npm ls -g`).
If you are trying to run locally:
* Make sure your plugins (and ESLint) are both in your project's `package.json` as devDependencies (or dependencies, if your project uses ESLint at runtime).
* Make sure you have run `npm install` and all your dependencies are installed.
In all cases, make sure your plugins' peerDependencies have been installed as well. You can use `npm view eslint-plugin-myplugin peerDepencies` to see what peer dependencies `eslint-plugin-myplugin` has.
### Does ESLint support JSX?
Yes, ESLint natively supports parsing JSX syntax (this must be enabled in [configuration](https://eslint.org/docs/user-guide/configuring).). Please note that supporting JSX syntax *is not* the same as supporting React. React applies specific semantics to JSX syntax that ESLint doesn't recognize. We recommend using [eslint-plugin-react](https://www.npmjs.com/package/eslint-plugin-react) if you are using React and want React semantics.

View File

@ -63,6 +63,7 @@ module.exports = {
"linebreak-style": "off",
"lines-around-comment": "off",
"lines-around-directive": "off",
"lines-between-class-members": "off",
"max-depth": "off",
"max-len": "off",
"max-lines": "off",
@ -70,6 +71,7 @@ module.exports = {
"max-params": "off",
"max-statements": "off",
"max-statements-per-line": "off",
"multiline-comment-style": "off",
"multiline-ternary": "off",
"new-cap": "off",
"new-parens": "off",

View File

@ -1041,7 +1041,8 @@ module.exports = {
} else if (parent.type === "Property" || parent.type === "MethodDefinition") {
if (parent.kind === "constructor") {
return "constructor";
} else if (parent.kind === "get") {
}
if (parent.kind === "get") {
tokens.push("getter");
} else if (parent.kind === "set") {
tokens.push("setter");

View File

@ -240,8 +240,8 @@ function processFile(filename, configHelper, options, linter) {
function createIgnoreResult(filePath, baseDir) {
let message;
const isHidden = /^\./.test(path.basename(filePath));
const isInNodeModules = baseDir && /^node_modules/.test(path.relative(baseDir, filePath));
const isInBowerComponents = baseDir && /^bower_components/.test(path.relative(baseDir, filePath));
const isInNodeModules = baseDir && path.relative(baseDir, filePath).startsWith("node_modules");
const isInBowerComponents = baseDir && path.relative(baseDir, filePath).startsWith("bower_components");
if (isHidden) {
message = "File ignored by default. Use a negated ignore pattern (like \"--ignore-pattern '!<relative/path/to/filename>'\") to override.";

View File

@ -63,7 +63,7 @@ function translateOptions(cliOptions) {
cache: cliOptions.cache,
cacheFile: cliOptions.cacheFile,
cacheLocation: cliOptions.cacheLocation,
fix: cliOptions.fix && (cliOptions.quiet ? quietFixPredicate : true),
fix: (cliOptions.fix || cliOptions.fixDryRun) && (cliOptions.quiet ? quietFixPredicate : true),
allowInlineConfig: cliOptions.inlineConfig,
reportUnusedDisableDirectives: cliOptions.reportUnusedDisableDirectives
};
@ -144,6 +144,8 @@ const cli = {
const files = currentOptions._;
const useStdin = typeof text === "string";
if (currentOptions.version) { // version from package.json
log.info(`v${require("../package.json").version}`);
@ -152,7 +154,8 @@ const cli = {
if (files.length) {
log.error("The --print-config option must be used with exactly one file name.");
return 1;
} else if (text) {
}
if (useStdin) {
log.error("The --print-config option is not available for piped-in code.");
return 1;
}
@ -163,23 +166,27 @@ const cli = {
log.info(JSON.stringify(fileConfig, null, " "));
return 0;
} else if (currentOptions.help || (!files.length && !text)) {
} else if (currentOptions.help || (!files.length && !useStdin)) {
log.info(options.generateHelp());
} else {
debug(`Running on ${text ? "text" : "files"}`);
debug(`Running on ${useStdin ? "text" : "files"}`);
// disable --fix for piped-in code until we know how to do it correctly
if (text && currentOptions.fix) {
log.error("The --fix option is not available for piped-in code.");
if (currentOptions.fix && currentOptions.fixDryRun) {
log.error("The --fix option and the --fix-dry-run option cannot be used together.");
return 1;
}
if (useStdin && currentOptions.fix) {
log.error("The --fix option is not available for piped-in code; use --fix-dry-run instead.");
return 1;
}
const engine = new CLIEngine(translateOptions(currentOptions));
const report = text ? engine.executeOnText(text, currentOptions.stdinFilename, true) : engine.executeOnFiles(files);
const report = useStdin ? engine.executeOnText(text, currentOptions.stdinFilename, true) : engine.executeOnFiles(files);
if (currentOptions.fix) {
debug("Fix mode enabled - applying fixes");

View File

@ -51,7 +51,8 @@ function renderSummary(totalErrors, totalWarnings) {
function renderColor(totalErrors, totalWarnings) {
if (totalErrors !== 0) {
return 2;
} else if (totalWarnings !== 0) {
}
if (totalWarnings !== 0) {
return 1;
}
return 0;

View File

@ -184,7 +184,7 @@ class IgnoredPaths {
addPattern(this.ig.default, pattern);
});
} else {
throw new Error("Package.json eslintIgnore property requires an array of paths");
throw new TypeError("Package.json eslintIgnore property requires an array of paths");
}
}
}

View File

@ -1,3 +0,0 @@
rules:
internal-no-invalid-meta: "error"
internal-consistent-docs-description: "error"

View File

@ -1,130 +0,0 @@
/**
* @fileoverview Internal rule to enforce meta.docs.description conventions.
* @author Vitor Balocco
*/
"use strict";
const ALLOWED_FIRST_WORDS = [
"enforce",
"require",
"disallow"
];
//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------
/**
* Gets the property of the Object node passed in that has the name specified.
*
* @param {string} property Name of the property to return.
* @param {ASTNode} node The ObjectExpression node.
* @returns {ASTNode} The Property node or null if not found.
*/
function getPropertyFromObject(property, node) {
const properties = node.properties;
for (let i = 0; i < properties.length; i++) {
if (properties[i].key.name === property) {
return properties[i];
}
}
return null;
}
/**
* Verifies that the meta.docs.description property follows our internal conventions.
*
* @param {RuleContext} context The ESLint rule context.
* @param {ASTNode} exportsNode ObjectExpression node that the rule exports.
* @returns {void}
*/
function checkMetaDocsDescription(context, exportsNode) {
if (exportsNode.type !== "ObjectExpression") {
// if the exported node is not the correct format, "internal-no-invalid-meta" will already report this.
return;
}
const metaProperty = getPropertyFromObject("meta", exportsNode);
const metaDocs = metaProperty && getPropertyFromObject("docs", metaProperty.value);
const metaDocsDescription = metaDocs && getPropertyFromObject("description", metaDocs.value);
if (!metaDocsDescription) {
// if there is no `meta.docs.description` property, "internal-no-invalid-meta" will already report this.
return;
}
const description = metaDocsDescription.value.value;
if (typeof description !== "string") {
context.report({
node: metaDocsDescription.value,
message: "`meta.docs.description` should be a string."
});
return;
}
if (description === "") {
context.report({
node: metaDocsDescription.value,
message: "`meta.docs.description` should not be empty."
});
return;
}
if (description.indexOf(" ") === 0) {
context.report({
node: metaDocsDescription.value,
message: "`meta.docs.description` should not start with whitespace."
});
return;
}
const firstWord = description.split(" ")[0];
if (ALLOWED_FIRST_WORDS.indexOf(firstWord) === -1) {
context.report({
node: metaDocsDescription.value,
message: "`meta.docs.description` should start with one of the following words: {{ allowedWords }}. Started with \"{{ firstWord }}\" instead.",
data: {
allowedWords: ALLOWED_FIRST_WORDS.join(", "),
firstWord
}
});
}
}
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = {
meta: {
docs: {
description: "enforce correct conventions of `meta.docs.description` property in core rules",
category: "Internal",
recommended: false
},
schema: []
},
create(context) {
return {
AssignmentExpression(node) {
if (node.left &&
node.right &&
node.left.type === "MemberExpression" &&
node.left.object.name === "module" &&
node.left.property.name === "exports") {
checkMetaDocsDescription(context, node.right);
}
}
};
}
};

View File

@ -1,188 +0,0 @@
/**
* @fileoverview Internal rule to prevent missing or invalid meta property in core rules.
* @author Vitor Balocco
*/
"use strict";
//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------
/**
* Gets the property of the Object node passed in that has the name specified.
*
* @param {string} property Name of the property to return.
* @param {ASTNode} node The ObjectExpression node.
* @returns {ASTNode} The Property node or null if not found.
*/
function getPropertyFromObject(property, node) {
const properties = node.properties;
for (let i = 0; i < properties.length; i++) {
if (properties[i].key.name === property) {
return properties[i];
}
}
return null;
}
/**
* Extracts the `meta` property from the ObjectExpression that all rules export.
*
* @param {ASTNode} exportsNode ObjectExpression node that the rule exports.
* @returns {ASTNode} The `meta` Property node or null if not found.
*/
function getMetaPropertyFromExportsNode(exportsNode) {
return getPropertyFromObject("meta", exportsNode);
}
/**
* Whether this `meta` ObjectExpression has a `docs` property defined or not.
*
* @param {ASTNode} metaPropertyNode The `meta` ObjectExpression for this rule.
* @returns {boolean} `true` if a `docs` property exists.
*/
function hasMetaDocs(metaPropertyNode) {
return Boolean(getPropertyFromObject("docs", metaPropertyNode.value));
}
/**
* Whether this `meta` ObjectExpression has a `docs.description` property defined or not.
*
* @param {ASTNode} metaPropertyNode The `meta` ObjectExpression for this rule.
* @returns {boolean} `true` if a `docs.description` property exists.
*/
function hasMetaDocsDescription(metaPropertyNode) {
const metaDocs = getPropertyFromObject("docs", metaPropertyNode.value);
return metaDocs && getPropertyFromObject("description", metaDocs.value);
}
/**
* Whether this `meta` ObjectExpression has a `docs.category` property defined or not.
*
* @param {ASTNode} metaPropertyNode The `meta` ObjectExpression for this rule.
* @returns {boolean} `true` if a `docs.category` property exists.
*/
function hasMetaDocsCategory(metaPropertyNode) {
const metaDocs = getPropertyFromObject("docs", metaPropertyNode.value);
return metaDocs && getPropertyFromObject("category", metaDocs.value);
}
/**
* Whether this `meta` ObjectExpression has a `docs.recommended` property defined or not.
*
* @param {ASTNode} metaPropertyNode The `meta` ObjectExpression for this rule.
* @returns {boolean} `true` if a `docs.recommended` property exists.
*/
function hasMetaDocsRecommended(metaPropertyNode) {
const metaDocs = getPropertyFromObject("docs", metaPropertyNode.value);
return metaDocs && getPropertyFromObject("recommended", metaDocs.value);
}
/**
* Whether this `meta` ObjectExpression has a `schema` property defined or not.
*
* @param {ASTNode} metaPropertyNode The `meta` ObjectExpression for this rule.
* @returns {boolean} `true` if a `schema` property exists.
*/
function hasMetaSchema(metaPropertyNode) {
return getPropertyFromObject("schema", metaPropertyNode.value);
}
/**
* Checks the validity of the meta definition of this rule and reports any errors found.
*
* @param {RuleContext} context The ESLint rule context.
* @param {ASTNode} exportsNode ObjectExpression node that the rule exports.
* @param {boolean} ruleIsFixable whether the rule is fixable or not.
* @returns {void}
*/
function checkMetaValidity(context, exportsNode) {
const metaProperty = getMetaPropertyFromExportsNode(exportsNode);
if (!metaProperty) {
context.report(exportsNode, "Rule is missing a meta property.");
return;
}
if (!hasMetaDocs(metaProperty)) {
context.report(metaProperty, "Rule is missing a meta.docs property.");
return;
}
if (!hasMetaDocsDescription(metaProperty)) {
context.report(metaProperty, "Rule is missing a meta.docs.description property.");
return;
}
if (!hasMetaDocsCategory(metaProperty)) {
context.report(metaProperty, "Rule is missing a meta.docs.category property.");
return;
}
if (!hasMetaDocsRecommended(metaProperty)) {
context.report(metaProperty, "Rule is missing a meta.docs.recommended property.");
return;
}
if (!hasMetaSchema(metaProperty)) {
context.report(metaProperty, "Rule is missing a meta.schema property.");
}
}
/**
* Whether this node is the correct format for a rule definition or not.
*
* @param {ASTNode} node node that the rule exports.
* @returns {boolean} `true` if the exported node is the correct format for a rule definition
*/
function isCorrectExportsFormat(node) {
return node.type === "ObjectExpression";
}
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = {
meta: {
docs: {
description: "enforce correct use of `meta` property in core rules",
category: "Internal",
recommended: false
},
schema: []
},
create(context) {
let exportsNode;
return {
AssignmentExpression(node) {
if (node.left &&
node.right &&
node.left.type === "MemberExpression" &&
node.left.object.name === "module" &&
node.left.property.name === "exports") {
exportsNode = node.right;
}
},
"Program:exit"() {
if (!isCorrectExportsFormat(exportsNode)) {
context.report({ node: exportsNode, message: "Rule does not export an Object. Make sure the rule follows the new rule format." });
return;
}
checkMetaValidity(context, exportsNode);
}
};
}
};

View File

@ -291,7 +291,7 @@ function createDisableDirectives(type, loc, value) {
*/
function modifyConfigsFromComments(filename, ast, config, linterContext) {
let commentConfig = {
const commentConfig = {
exported: {},
astGlobals: {},
rules: {},
@ -320,10 +320,6 @@ function modifyConfigsFromComments(filename, ast, config, linterContext) {
Object.assign(commentConfig.astGlobals, parseBooleanConfig(value, comment));
break;
case "eslint-env":
Object.assign(commentConfig.env, parseListConfig(value));
break;
case "eslint-disable":
[].push.apply(disableDirectives, createDisableDirectives("disable", comment.loc.start, value));
break;
@ -361,14 +357,6 @@ function modifyConfigsFromComments(filename, ast, config, linterContext) {
}
});
// apply environment configs
Object.keys(commentConfig.env).forEach(name => {
const env = linterContext.environments.get(name);
if (env) {
commentConfig = ConfigOps.merge(commentConfig, env);
}
});
Object.assign(commentConfig.rules, commentRules);
return {

View File

@ -190,6 +190,12 @@ module.exports = optionator({
default: false,
description: "Automatically fix problems"
},
{
option: "fix-dry-run",
type: "Boolean",
default: false,
description: "Automatically fix problems without saving the changes to the file system"
},
{
option: "debug",
type: "Boolean",

View File

@ -23,7 +23,7 @@ module.exports = {
{
oneOf: [
{
enum: ["always", "never"]
enum: ["always", "never", "consistent"]
},
{
type: "object",
@ -58,11 +58,15 @@ module.exports = {
* @returns {{multiline: boolean, minItems: number}} Normalized option object.
*/
function normalizeOptionValue(option) {
let consistent = false;
let multiline = false;
let minItems = 0;
if (option) {
if (option === "always" || option.minItems === 0) {
if (option === "consistent") {
consistent = true;
minItems = Number.POSITIVE_INFINITY;
} else if (option === "always" || option.minItems === 0) {
minItems = 0;
} else if (option === "never") {
minItems = Number.POSITIVE_INFINITY;
@ -71,11 +75,12 @@ module.exports = {
minItems = option.minItems || Number.POSITIVE_INFINITY;
}
} else {
consistent = false;
multiline = true;
minItems = Number.POSITIVE_INFINITY;
}
return { multiline, minItems };
return { consistent, multiline, minItems };
}
/**
@ -173,8 +178,7 @@ module.exports = {
/**
* Reports a given node if it violated this rule.
*
* @param {ASTNode} node - A node to check. This is an ObjectExpression node or an ObjectPattern node.
* @param {{multiline: boolean, minItems: number}} options - An option object.
* @param {ASTNode} node - A node to check. This is an ArrayExpression node or an ArrayPattern node.
* @returns {void}
*/
function check(node) {
@ -194,6 +198,16 @@ module.exports = {
options.multiline &&
elements.length > 0 &&
firstIncComment.loc.start.line !== lastIncComment.loc.end.line
) ||
(
elements.length === 0 &&
firstIncComment.type === "Block" &&
firstIncComment.loc.start.line !== lastIncComment.loc.end.line &&
firstIncComment === lastIncComment
) ||
(
options.consistent &&
firstIncComment.loc.start.line !== openBracket.loc.end.line
)
);

View File

@ -14,7 +14,7 @@ const util = require("../ast-utils");
module.exports = {
meta: {
docs: {
description: "enforce consistent spacing inside single-line blocks",
description: "disallow or enforce spaces inside of blocks after opening block and before closing block",
category: "Stylistic Issues",
recommended: false
},

View File

@ -60,7 +60,8 @@ module.exports = {
if (node.type === "MemberExpression") {
if (node.object.type === "Identifier") {
return true;
} else if (node.object.type === "MemberExpression") {
}
if (node.object.type === "MemberExpression") {
return containsOnlyIdentifiers(node.object);
}
}

View File

@ -247,7 +247,8 @@ module.exports = {
if (capitalize === "always" && isLowercase) {
return false;
} else if (capitalize === "never" && isUppercase) {
}
if (capitalize === "never" && isUppercase) {
return false;
}

View File

@ -208,7 +208,9 @@ module.exports = {
if (item) {
const tokenAfterItem = sourceCode.getTokenAfter(item, astUtils.isNotClosingParenToken);
previousItemToken = tokenAfterItem ? sourceCode.getTokenBefore(tokenAfterItem) : sourceCode.ast.tokens[sourceCode.ast.tokens.length - 1];
previousItemToken = tokenAfterItem
? sourceCode.getTokenBefore(tokenAfterItem)
: sourceCode.ast.tokens[sourceCode.ast.tokens.length - 1];
}
});

View File

@ -733,7 +733,9 @@ module.exports = {
} else if (parent.type === "ObjectExpression" || parent.type === "ArrayExpression") {
const parentElements = node.parent.type === "ObjectExpression" ? node.parent.properties : node.parent.elements;
if (parentElements[0] && parentElements[0].loc.start.line === parent.loc.start.line && parentElements[0].loc.end.line !== parent.loc.start.line) {
if (parentElements[0] &&
parentElements[0].loc.start.line === parent.loc.start.line &&
parentElements[0].loc.end.line !== parent.loc.start.line) {
/*
* If the first element of the array spans multiple lines, don't increase the expected indentation of the rest.
@ -797,7 +799,8 @@ module.exports = {
}
}
checkLastNodeLineIndent(node, nodeIndent + (isNodeInVarOnTop(node, parentVarNode) ? options.VariableDeclarator[parentVarNode.parent.kind] * indentSize : 0));
checkLastNodeLineIndent(node, nodeIndent +
(isNodeInVarOnTop(node, parentVarNode) ? options.VariableDeclarator[parentVarNode.parent.kind] * indentSize : 0));
}
/**

View File

@ -235,10 +235,12 @@ class OffsetStorage {
/**
* @param {TokenInfo} tokenInfo a TokenInfo instance
* @param {number} indentSize The desired size of each indentation level
* @param {string} indentType The indentation character
*/
constructor(tokenInfo, indentSize) {
constructor(tokenInfo, indentSize, indentType) {
this._tokenInfo = tokenInfo;
this._indentSize = indentSize;
this._indentType = indentType;
this._tree = new BinarySearchTree();
this._tree.insert(0, { offset: 0, from: null, force: false });
@ -408,7 +410,7 @@ class OffsetStorage {
/**
* Gets the desired indent of a token
* @param {Token} token The token
* @returns {number} The desired indent of the token
* @returns {string} The desired indent of the token
*/
getDesiredIndent(token) {
if (!this._desiredIndentCache.has(token)) {
@ -417,7 +419,10 @@ class OffsetStorage {
// If the token is ignored, use the actual indent of the token as the desired indent.
// This ensures that no errors are reported for this token.
this._desiredIndentCache.set(token, this._tokenInfo.getTokenIndent(token).length / this._indentSize);
this._desiredIndentCache.set(
token,
this._tokenInfo.getTokenIndent(token)
);
} else if (this._lockedFirstTokens.has(token)) {
const firstToken = this._lockedFirstTokens.get(token);
@ -428,7 +433,7 @@ class OffsetStorage {
this.getDesiredIndent(this._tokenInfo.getFirstTokenOfLine(firstToken)) +
// (space between the start of the first element's line and the first element)
(firstToken.loc.start.column - this._tokenInfo.getFirstTokenOfLine(firstToken).loc.start.column) / this._indentSize
this._indentType.repeat(firstToken.loc.start.column - this._tokenInfo.getFirstTokenOfLine(firstToken).loc.start.column)
);
} else {
const offsetInfo = this._getOffsetDescriptor(token);
@ -436,9 +441,12 @@ class OffsetStorage {
offsetInfo.from &&
offsetInfo.from.loc.start.line === token.loc.start.line &&
!offsetInfo.force
) ? 0 : offsetInfo.offset;
) ? 0 : offsetInfo.offset * this._indentSize;
this._desiredIndentCache.set(token, offset + (offsetInfo.from ? this.getDesiredIndent(offsetInfo.from) : 0));
this._desiredIndentCache.set(
token,
(offsetInfo.from ? this.getDesiredIndent(offsetInfo.from) : "") + this._indentType.repeat(offset)
);
}
}
return this._desiredIndentCache.get(token);
@ -655,7 +663,7 @@ module.exports = {
const sourceCode = context.getSourceCode();
const tokenInfo = new TokenInfo(sourceCode);
const offsets = new OffsetStorage(tokenInfo, indentSize);
const offsets = new OffsetStorage(tokenInfo, indentSize, indentType === "space" ? " " : "\t");
const parameterParens = new WeakSet();
/**
@ -688,27 +696,24 @@ module.exports = {
/**
* Reports a given indent violation
* @param {Token} token Token violating the indent rule
* @param {int} neededIndentLevel Expected indentation level
* @param {int} gottenSpaces Actual number of indentation spaces for the token
* @param {int} gottenTabs Actual number of indentation tabs for the token
* @param {string} neededIndent Expected indentation string
* @returns {void}
*/
function report(token, neededIndentLevel) {
function report(token, neededIndent) {
const actualIndent = Array.from(tokenInfo.getTokenIndent(token));
const numSpaces = actualIndent.filter(char => char === " ").length;
const numTabs = actualIndent.filter(char => char === "\t").length;
const neededChars = neededIndentLevel * indentSize;
context.report({
node: token,
message: createErrorMessage(neededChars, numSpaces, numTabs),
message: createErrorMessage(neededIndent.length, numSpaces, numTabs),
loc: {
start: { line: token.loc.start.line, column: 0 },
end: { line: token.loc.start.line, column: token.loc.start.column }
},
fix(fixer) {
const range = [token.range[0] - token.loc.start.column, token.range[0]];
const newText = (indentType === "space" ? " " : "\t").repeat(neededChars);
const newText = neededIndent;
return fixer.replaceTextRange(range, newText);
}
@ -718,14 +723,13 @@ module.exports = {
/**
* Checks if a token's indentation is correct
* @param {Token} token Token to examine
* @param {int} desiredIndentLevel needed indent level
* @param {string} desiredIndent Desired indentation of the string
* @returns {boolean} `true` if the token's indentation is correct
*/
function validateTokenIndent(token, desiredIndentLevel) {
function validateTokenIndent(token, desiredIndent) {
const indentation = tokenInfo.getTokenIndent(token);
const expectedChar = indentType === "space" ? " " : "\t";
return indentation === expectedChar.repeat(desiredIndentLevel * indentSize) ||
return indentation === desiredIndent ||
// To avoid conflicts with no-mixed-spaces-and-tabs, don't report mixed spaces and tabs.
indentation.includes(" ") && indentation.includes("\t");
@ -1241,7 +1245,9 @@ module.exports = {
NewExpression(node) {
// Only indent the arguments if the NewExpression has parens (e.g. `new Foo(bar)` or `new Foo()`, but not `new Foo`
if (node.arguments.length > 0 || astUtils.isClosingParenToken(sourceCode.getLastToken(node)) && astUtils.isOpeningParenToken(sourceCode.getLastToken(node, 1))) {
if (node.arguments.length > 0 ||
astUtils.isClosingParenToken(sourceCode.getLastToken(node)) &&
astUtils.isOpeningParenToken(sourceCode.getLastToken(node, 1))) {
addFunctionCallIndent(node);
}
},

View File

@ -82,6 +82,12 @@ module.exports = {
allowBlockEnd: {
type: "boolean"
},
allowClassStart: {
type: "boolean"
},
allowClassEnd: {
type: "boolean"
},
allowObjectStart: {
type: "boolean"
},
@ -224,6 +230,24 @@ module.exports = {
return isCommentAtParentEnd(token, "ClassBody") || isCommentAtParentEnd(token, "BlockStatement") || isCommentAtParentEnd(token, "SwitchCase") || isCommentAtParentEnd(token, "SwitchStatement");
}
/**
* Returns whether or not comments are at the class start or not.
* @param {token} token The Comment token.
* @returns {boolean} True if the comment is at class start.
*/
function isCommentAtClassStart(token) {
return isCommentAtParentStart(token, "ClassBody");
}
/**
* Returns whether or not comments are at the class end or not.
* @param {token} token The Comment token.
* @returns {boolean} True if the comment is at class end.
*/
function isCommentAtClassEnd(token) {
return isCommentAtParentEnd(token, "ClassBody");
}
/**
* Returns whether or not comments are at the object start or not.
* @param {token} token The Comment token.
@ -284,15 +308,20 @@ module.exports = {
nextLineNum = token.loc.end.line + 1,
commentIsNotAlone = codeAroundComment(token);
const blockStartAllowed = options.allowBlockStart && isCommentAtBlockStart(token),
blockEndAllowed = options.allowBlockEnd && isCommentAtBlockEnd(token),
const blockStartAllowed = options.allowBlockStart &&
isCommentAtBlockStart(token) &&
!(options.allowClassStart === false &&
isCommentAtClassStart(token)),
blockEndAllowed = options.allowBlockEnd && isCommentAtBlockEnd(token) && !(options.allowClassEnd === false && isCommentAtClassEnd(token)),
classStartAllowed = options.allowClassStart && isCommentAtClassStart(token),
classEndAllowed = options.allowClassEnd && isCommentAtClassEnd(token),
objectStartAllowed = options.allowObjectStart && isCommentAtObjectStart(token),
objectEndAllowed = options.allowObjectEnd && isCommentAtObjectEnd(token),
arrayStartAllowed = options.allowArrayStart && isCommentAtArrayStart(token),
arrayEndAllowed = options.allowArrayEnd && isCommentAtArrayEnd(token);
const exceptionStartAllowed = blockStartAllowed || objectStartAllowed || arrayStartAllowed;
const exceptionEndAllowed = blockEndAllowed || objectEndAllowed || arrayEndAllowed;
const exceptionStartAllowed = blockStartAllowed || classStartAllowed || objectStartAllowed || arrayStartAllowed;
const exceptionEndAllowed = blockEndAllowed || classEndAllowed || objectEndAllowed || arrayEndAllowed;
// ignore top of the file and bottom of the file
if (prevLineNum < 1) {

View File

@ -0,0 +1,91 @@
/**
* @fileoverview Rule to check empty newline between class members
* @author 薛定谔的猫<hh_2013@foxmail.com>
*/
"use strict";
const astUtils = require("../ast-utils");
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = {
meta: {
docs: {
description: "require or disallow an empty line between class members",
category: "Stylistic Issues",
recommended: false
},
fixable: "whitespace",
schema: [
{
enum: ["always", "never"]
},
{
type: "object",
properties: {
exceptAfterSingleLine: {
type: "boolean"
}
},
additionalProperties: false
}
]
},
create(context) {
const options = [];
options[0] = context.options[0] || "always";
options[1] = context.options[1] || { exceptAfterSingleLine: false };
const ALWAYS_MESSAGE = "Expected blank line between class members.";
const NEVER_MESSAGE = "Unexpected blank line between class members.";
const sourceCode = context.getSourceCode();
/**
* Checks if there is padding between two tokens
* @param {Token} first The first token
* @param {Token} second The second token
* @returns {boolean} True if there is at least a line between the tokens
*/
function isPaddingBetweenTokens(first, second) {
return second.loc.start.line - first.loc.end.line >= 2;
}
return {
ClassBody(node) {
const body = node.body;
for (let i = 0; i < body.length - 1; i++) {
const curFirst = sourceCode.getFirstToken(body[i]);
const curLast = sourceCode.getLastToken(body[i]);
const comments = sourceCode.getCommentsBefore(body[i + 1]);
const nextFirst = comments.length ? comments[0] : sourceCode.getFirstToken(body[i + 1]);
const isPadded = isPaddingBetweenTokens(curLast, nextFirst);
const isMulti = !astUtils.isTokenOnSameLine(curFirst, curLast);
const skip = !isMulti && options[1].exceptAfterSingleLine;
if ((options[0] === "always" && !skip && !isPadded) ||
(options[0] === "never" && isPadded)) {
context.report({
node: body[i + 1],
message: isPadded ? NEVER_MESSAGE : ALWAYS_MESSAGE,
fix(fixer) {
return isPadded
? fixer.replaceTextRange([curLast.range[1], nextFirst.range[0]], "\n")
: fixer.insertTextAfter(curLast, "\n");
}
});
}
}
}
};
}
};

View File

@ -0,0 +1,294 @@
/**
* @fileoverview enforce a particular style for multiline comments
* @author Teddy Katz
*/
"use strict";
const astUtils = require("../ast-utils");
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = {
meta: {
docs: {
description: "enforce a particular style for multiline comments",
category: "Stylistic Issues",
recommended: false
},
fixable: "whitespace",
schema: [{ enum: ["starred-block", "separate-lines", "bare-block"] }]
},
create(context) {
const sourceCode = context.getSourceCode();
const option = context.options[0] || "starred-block";
const EXPECTED_BLOCK_ERROR = "Expected a block comment instead of consecutive line comments.";
const START_NEWLINE_ERROR = "Expected a linebreak after '/*'.";
const END_NEWLINE_ERROR = "Expected a linebreak before '*/'.";
const MISSING_STAR_ERROR = "Expected a '*' at the start of this line.";
const ALIGNMENT_ERROR = "Expected this line to be aligned with the start of the comment.";
const EXPECTED_LINES_ERROR = "Expected multiple line comments instead of a block comment.";
//----------------------------------------------------------------------
// Helpers
//----------------------------------------------------------------------
/**
* Gets a list of comment lines in a group
* @param {Token[]} commentGroup A group of comments, containing either multiple line comments or a single block comment
* @returns {string[]} A list of comment lines
*/
function getCommentLines(commentGroup) {
if (commentGroup[0].type === "Line") {
return commentGroup.map(comment => comment.value);
}
return commentGroup[0].value
.split(astUtils.LINEBREAK_MATCHER)
.map(line => line.replace(/^\s*\*?/, ""));
}
/**
* Converts a comment into starred-block form
* @param {Token} firstComment The first comment of the group being converted
* @param {string[]} commentLinesList A list of lines to appear in the new starred-block comment
* @returns {string} A representation of the comment value in starred-block form, excluding start and end markers
*/
function convertToStarredBlock(firstComment, commentLinesList) {
const initialOffset = sourceCode.text.slice(firstComment.range[0] - firstComment.loc.start.column, firstComment.range[0]);
const starredLines = commentLinesList.map(line => `${initialOffset} *${line}`);
return `\n${starredLines.join("\n")}\n${initialOffset} `;
}
/**
* Converts a comment into separate-line form
* @param {Token} firstComment The first comment of the group being converted
* @param {string[]} commentLinesList A list of lines to appear in the new starred-block comment
* @returns {string} A representation of the comment value in separate-line form
*/
function convertToSeparateLines(firstComment, commentLinesList) {
const initialOffset = sourceCode.text.slice(firstComment.range[0] - firstComment.loc.start.column, firstComment.range[0]);
const separateLines = commentLinesList.map(line => `// ${line.trim()}`);
return separateLines.join(`\n${initialOffset}`);
}
/**
* Converts a comment into bare-block form
* @param {Token} firstComment The first comment of the group being converted
* @param {string[]} commentLinesList A list of lines to appear in the new starred-block comment
* @returns {string} A representation of the comment value in bare-block form
*/
function convertToBlock(firstComment, commentLinesList) {
const initialOffset = sourceCode.text.slice(firstComment.range[0] - firstComment.loc.start.column, firstComment.range[0]);
const blockLines = commentLinesList.map(line => line.trim());
return `/* ${blockLines.join(`\n${initialOffset} `)} */`;
}
/**
* Check a comment is JSDoc form
* @param {Token[]} commentGroup A group of comments, containing either multiple line comments or a single block comment
* @returns {boolean} if commentGroup is JSDoc form, return true
*/
function isJSDoc(commentGroup) {
const lines = commentGroup[0].value.split(astUtils.LINEBREAK_MATCHER);
return commentGroup[0].type === "Block" &&
/^\*\s*$/.test(lines[0]) &&
lines.slice(1, -1).every(line => /^\s* /.test(line)) &&
/^\s*$/.test(lines[lines.length - 1]);
}
/**
* Each method checks a group of comments to see if it's valid according to the given option.
* @param {Token[]} commentGroup A list of comments that appear together. This will either contain a single
* block comment or multiple line comments.
* @returns {void}
*/
const commentGroupCheckers = {
"starred-block"(commentGroup) {
const commentLines = getCommentLines(commentGroup);
if (commentLines.some(value => value.includes("*/"))) {
return;
}
if (commentGroup.length > 1) {
context.report({
loc: {
start: commentGroup[0].loc.start,
end: commentGroup[commentGroup.length - 1].loc.end
},
message: EXPECTED_BLOCK_ERROR,
fix(fixer) {
const range = [commentGroup[0].range[0], commentGroup[commentGroup.length - 1].range[1]];
const starredBlock = `/*${convertToStarredBlock(commentGroup[0], commentLines)}*/`;
return commentLines.some(value => value.startsWith("/"))
? null
: fixer.replaceTextRange(range, starredBlock);
}
});
} else {
const block = commentGroup[0];
const lines = block.value.split(astUtils.LINEBREAK_MATCHER);
const expectedLinePrefix = `${sourceCode.text.slice(block.range[0] - block.loc.start.column, block.range[0])} *`;
if (!/^\*?\s*$/.test(lines[0])) {
const start = block.value.startsWith("*") ? block.range[0] + 1 : block.range[0];
context.report({
loc: {
start: block.loc.start,
end: { line: block.loc.start.line, column: block.loc.start.column + 2 }
},
message: START_NEWLINE_ERROR,
fix: fixer => fixer.insertTextAfterRange([start, start + 2], `\n${expectedLinePrefix}`)
});
}
if (!/^\s*$/.test(lines[lines.length - 1])) {
context.report({
loc: {
start: { line: block.loc.end.line, column: block.loc.end.column - 2 },
end: block.loc.end
},
message: END_NEWLINE_ERROR,
fix: fixer => fixer.replaceTextRange([block.range[1] - 2, block.range[1]], `\n${expectedLinePrefix}/`)
});
}
for (let lineNumber = block.loc.start.line + 1; lineNumber <= block.loc.end.line; lineNumber++) {
const lineText = sourceCode.lines[lineNumber - 1];
if (!lineText.startsWith(expectedLinePrefix)) {
context.report({
loc: {
start: { line: lineNumber, column: 0 },
end: { line: lineNumber, column: sourceCode.lines[lineNumber - 1].length }
},
message: /^\s*\*/.test(lineText)
? ALIGNMENT_ERROR
: MISSING_STAR_ERROR,
fix(fixer) {
const lineStartIndex = sourceCode.getIndexFromLoc({ line: lineNumber, column: 0 });
const linePrefixLength = lineText.match(/^\s*\*? ?/)[0].length;
const commentStartIndex = lineStartIndex + linePrefixLength;
const replacementText = lineNumber === block.loc.end.line || lineText.length === linePrefixLength
? expectedLinePrefix
: `${expectedLinePrefix} `;
return fixer.replaceTextRange([lineStartIndex, commentStartIndex], replacementText);
}
});
}
}
}
},
"separate-lines"(commentGroup) {
if (!isJSDoc(commentGroup) && commentGroup[0].type === "Block") {
const commentLines = getCommentLines(commentGroup);
const block = commentGroup[0];
const tokenAfter = sourceCode.getTokenAfter(block, { includeComments: true });
if (tokenAfter && block.loc.end.line === tokenAfter.loc.start.line) {
return;
}
context.report({
loc: {
start: block.loc.start,
end: { line: block.loc.start.line, column: block.loc.start.column + 2 }
},
message: EXPECTED_LINES_ERROR,
fix(fixer) {
return fixer.replaceText(block, convertToSeparateLines(block, commentLines.filter(line => line)));
}
});
}
},
"bare-block"(commentGroup) {
if (!isJSDoc(commentGroup)) {
const commentLines = getCommentLines(commentGroup);
// disallows consecutive line comments in favor of using a block comment.
if (commentGroup[0].type === "Line" && commentLines.length > 1 &&
!commentLines.some(value => value.includes("*/"))) {
context.report({
loc: {
start: commentGroup[0].loc.start,
end: commentGroup[commentGroup.length - 1].loc.end
},
message: EXPECTED_BLOCK_ERROR,
fix(fixer) {
const range = [commentGroup[0].range[0], commentGroup[commentGroup.length - 1].range[1]];
const block = convertToBlock(commentGroup[0], commentLines.filter(line => line));
return fixer.replaceTextRange(range, block);
}
});
}
// prohibits block comments from having a * at the beginning of each line.
if (commentGroup[0].type === "Block") {
const block = commentGroup[0];
const lines = block.value.split(astUtils.LINEBREAK_MATCHER).filter(line => line.trim());
if (lines.length > 0 && lines.every(line => /^\s*\*/.test(line))) {
context.report({
loc: {
start: block.loc.start,
end: { line: block.loc.start.line, column: block.loc.start.column + 2 }
},
message: EXPECTED_BLOCK_ERROR,
fix(fixer) {
return fixer.replaceText(block, convertToBlock(block, commentLines.filter(line => line)));
}
});
}
}
}
}
};
//----------------------------------------------------------------------
// Public
//----------------------------------------------------------------------
return {
Program() {
return sourceCode.getAllComments()
.filter(comment => comment.type !== "Shebang")
.filter(comment => !astUtils.COMMENTS_IGNORE_PATTERN.test(comment.value))
.filter(comment => {
const tokenBefore = sourceCode.getTokenBefore(comment, { includeComments: true });
return !tokenBefore || tokenBefore.loc.end.line < comment.loc.start.line;
})
.reduce((commentGroups, comment, index, commentList) => {
const tokenBefore = sourceCode.getTokenBefore(comment, { includeComments: true });
if (
comment.type === "Line" &&
index && commentList[index - 1].type === "Line" &&
tokenBefore && tokenBefore.loc.end.line === comment.loc.start.line - 1 &&
tokenBefore === commentList[index - 1]
) {
commentGroups[commentGroups.length - 1].push(comment);
} else {
commentGroups.push([comment]);
}
return commentGroups;
}, [])
.filter(commentGroup => !(commentGroup.length === 1 && commentGroup[0].loc.start.line === commentGroup[0].loc.end.line))
.forEach(commentGroupCheckers[option]);
}
};
}
};

View File

@ -178,7 +178,8 @@ module.exports = {
// char has no uppercase variant, so it's non-alphabetic
return "non-alpha";
} else if (firstChar === firstCharLower) {
}
if (firstChar === firstCharLower) {
return "lower";
}
return "upper";

View File

@ -59,9 +59,11 @@ module.exports = {
if (parentType === "IfStatement") {
return isPrecededByTokens(node, ["else", ")"]);
} else if (parentType === "DoWhileStatement") {
}
if (parentType === "DoWhileStatement") {
return isPrecededByTokens(node, ["do"]);
} else if (parentType === "SwitchCase") {
}
if (parentType === "SwitchCase") {
return isPrecededByTokens(node, [":"]);
}
return isPrecededByTokens(node, [")"]);

View File

@ -71,7 +71,8 @@ function isShadowed(scope, node) {
function isGlobalThisReferenceOrGlobalWindow(scope, node) {
if (scope.type === "global" && node.type === "ThisExpression") {
return true;
} else if (node.name === "window") {
}
if (node.name === "window") {
return !isShadowed(scope, node);
}

View File

@ -51,7 +51,7 @@ module.exports = {
CatchClause(node) {
let scope = context.getScope();
// When blockBindings is enabled, CatchClause creates its own scope
// When ecmaVersion >= 6, CatchClause creates its own scope
// so start from one upper scope to exclude the current node
if (scope.block === node) {
scope = scope.upper;

View File

@ -138,7 +138,7 @@ module.exports = {
function checkConstantConditionLoopInSet(node) {
if (loopsInCurrentScope.has(node)) {
loopsInCurrentScope.delete(node);
context.report({ node, message: "Unexpected constant condition." });
context.report({ node: node.test, message: "Unexpected constant condition." });
}
}
@ -150,7 +150,7 @@ module.exports = {
*/
function reportIfConstant(node) {
if (node.test && isConstant(node.test, true)) {
context.report({ node, message: "Unexpected constant condition." });
context.report({ node: node.test, message: "Unexpected constant condition." });
}
}

View File

@ -31,7 +31,8 @@ module.exports = {
function getRegExp(node) {
if (node.value instanceof RegExp) {
return node.value;
} else if (typeof node.value === "string") {
}
if (typeof node.value === "string") {
const parent = context.getAncestors().pop();

View File

@ -24,8 +24,15 @@ module.exports = {
recommended: false
},
schema: [],
schema: [{
type: "object",
properties: {
allowElseIf: {
type: "boolean"
}
},
additionalProperties: false
}],
fixable: "code"
},
@ -134,13 +141,13 @@ module.exports = {
/**
* Check to see if the node is valid for evaluation,
* meaning it has an else and not an else-if
* meaning it has an else.
*
* @param {Node} node The node being evaluated
* @returns {boolean} True if the node is valid
*/
function hasElse(node) {
return node.alternate && node.consequent && node.alternate.type !== "IfStatement";
return node.alternate && node.consequent;
}
/**
@ -189,14 +196,15 @@ module.exports = {
return checkForReturnOrIf(node);
}
/**
* Check the if statement
* Check the if statement, but don't catch else-if blocks.
* @returns {void}
* @param {Node} node The node for the if statement to check
* @private
*/
function IfStatement(node) {
const parent = context.getAncestors().pop();
function checkIfWithoutElse(node) {
const parent = node.parent;
let consequents,
alternate;
@ -221,13 +229,40 @@ module.exports = {
}
}
/**
* Check the if statement
* @returns {void}
* @param {Node} node The node for the if statement to check
* @private
*/
function checkIfWithElse(node) {
const parent = node.parent;
/*
* Fixing this would require splitting one statement into two, so no error should
* be reported if this node is in a position where only one statement is allowed.
*/
if (!astUtils.STATEMENT_LIST_PARENTS.has(parent.type)) {
return;
}
const alternate = node.alternate;
if (alternate && alwaysReturns(node.consequent)) {
displayReport(alternate);
}
}
const allowElseIf = !(context.options[0] && context.options[0].allowElseIf === false);
//--------------------------------------------------------------------------
// Public API
//--------------------------------------------------------------------------
return {
"IfStatement:exit": IfStatement
"IfStatement:exit": allowElseIf ? checkIfWithoutElse : checkIfWithElse
};

View File

@ -195,10 +195,12 @@ module.exports = {
function containsAssignment(node) {
if (node.type === "AssignmentExpression") {
return true;
} else if (node.type === "ConditionalExpression" &&
}
if (node.type === "ConditionalExpression" &&
(node.consequent.type === "AssignmentExpression" || node.alternate.type === "AssignmentExpression")) {
return true;
} else if ((node.left && node.left.type === "AssignmentExpression") ||
}
if ((node.left && node.left.type === "AssignmentExpression") ||
(node.right && node.right.type === "AssignmentExpression")) {
return true;
}
@ -219,7 +221,8 @@ module.exports = {
if (node.type === "ReturnStatement") {
return node.argument && containsAssignment(node.argument);
} else if (node.type === "ArrowFunctionExpression" && node.body.type !== "BlockStatement") {
}
if (node.type === "ArrowFunctionExpression" && node.body.type !== "BlockStatement") {
return containsAssignment(node.body);
}
return containsAssignment(node);

View File

@ -45,7 +45,8 @@ module.exports = {
const lastIfToken = sourceCode.getLastToken(node.consequent);
const sourceText = sourceCode.getText();
if (sourceText.slice(openingElseCurly.range[1], node.range[0]).trim() || sourceText.slice(node.range[1], closingElseCurly.range[0]).trim()) {
if (sourceText.slice(openingElseCurly.range[1],
node.range[0]).trim() || sourceText.slice(node.range[1], closingElseCurly.range[0]).trim()) {
// Don't fix if there are any non-whitespace characters interfering (e.g. comments)
return null;

View File

@ -104,14 +104,16 @@ module.exports = {
// "var x = require('util');"
return DECL_REQUIRE;
} else if (allowCall &&
}
if (allowCall &&
initExpression.type === "CallExpression" &&
initExpression.callee.type === "CallExpression"
) {
// "var x = require('diagnose')('sub-module');"
return getDeclarationType(initExpression.callee);
} else if (initExpression.type === "MemberExpression") {
}
if (initExpression.type === "MemberExpression") {
// "var x = require('glob').Glob;"
return getDeclarationType(initExpression.object);
@ -131,7 +133,8 @@ module.exports = {
// "var x = require('glob').Glob;"
return inferModuleType(initExpression.object);
} else if (initExpression.arguments.length === 0) {
}
if (initExpression.arguments.length === 0) {
// "var x = require();"
return REQ_COMPUTED;
@ -149,7 +152,8 @@ module.exports = {
// "var fs = require('fs');"
return REQ_CORE;
} else if (/^\.{0,2}\//.test(arg.value)) {
}
if (/^\.{0,2}\//.test(arg.value)) {
// "var utils = require('./utils');"
return REQ_FILE;

View File

@ -4,6 +4,13 @@
*/
"use strict";
//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------
const DEFAULT_MESSAGE_TEMPLATE = "'{{importName}}' import is restricted from being used.";
const CUSTOM_MESSAGE_TEMPLATE = "'{{importName}}' import is restricted from being used. {{customMessage}}";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
@ -11,9 +18,29 @@
const ignore = require("ignore");
const arrayOfStrings = {
type: "array",
items: { type: "string" },
uniqueItems: true
};
const arrayOfStringsOrObjects = {
type: "array",
items: {
type: "string"
anyOf: [
{ type: "string" },
{
type: "object",
properties: {
name: { type: "string" },
message: {
type: "string",
minLength: 1
}
},
additionalProperties: false,
required: ["name"]
}
]
},
uniqueItems: true
};
@ -28,17 +55,17 @@ module.exports = {
schema: {
anyOf: [
arrayOfStrings,
arrayOfStringsOrObjects,
{
type: "array",
items: [{
items: {
type: "object",
properties: {
paths: arrayOfStrings,
paths: arrayOfStringsOrObjects,
patterns: arrayOfStrings
},
additionalProperties: false
}],
},
additionalItems: false
}
]
@ -47,35 +74,77 @@ module.exports = {
create(context) {
const options = Array.isArray(context.options) ? context.options : [];
const isStringArray = typeof options[0] !== "object";
const restrictedPaths = new Set(isStringArray ? context.options : options[0].paths || []);
const restrictedPatterns = isStringArray ? [] : options[0].patterns || [];
const isPathAndPatternsObject =
typeof options[0] === "object" &&
(options[0].hasOwnProperty("paths") || options[0].hasOwnProperty("patterns"));
const restrictedPaths = (isPathAndPatternsObject ? options[0].paths : context.options) || [];
const restrictedPatterns = (isPathAndPatternsObject ? options[0].patterns : []) || [];
const restrictedPathMessages = restrictedPaths.reduce((memo, importName) => {
if (typeof importName === "string") {
memo[importName] = null;
} else {
memo[importName.name] = importName.message;
}
return memo;
}, {});
// if no imports are restricted we don"t need to check
if (restrictedPaths.size === 0 && restrictedPatterns.length === 0) {
if (Object.keys(restrictedPaths).length === 0 && restrictedPatterns.length === 0) {
return {};
}
const ig = ignore().add(restrictedPatterns);
/**
* Report a restricted path.
* @param {node} node representing the restricted path reference
* @returns {void}
* @private
*/
function reportPath(node) {
const importName = node.source.value.trim();
const customMessage = restrictedPathMessages[importName];
const message = customMessage
? CUSTOM_MESSAGE_TEMPLATE
: DEFAULT_MESSAGE_TEMPLATE;
context.report({
node,
message,
data: {
importName,
customMessage
}
});
}
/**
* Check if the given name is a restricted path name.
* @param {string} name name of a variable
* @returns {boolean} whether the variable is a restricted path or not
* @private
*/
function isRestrictedPath(name) {
return Object.prototype.hasOwnProperty.call(restrictedPathMessages, name);
}
return {
ImportDeclaration(node) {
if (node && node.source && node.source.value) {
const importName = node.source.value.trim();
if (restrictedPaths.has(importName)) {
context.report({
node,
message: "'{{importName}}' import is restricted from being used.",
data: { importName }
});
if (isRestrictedPath(importName)) {
reportPath(node);
}
if (restrictedPatterns.length > 0 && ig.ignores(importName)) {
context.report({
node,
message: "'{{importName}}' import is restricted from being used by a pattern.",
data: { importName }
data: {
importName
}
});
}
}

View File

@ -4,6 +4,13 @@
*/
"use strict";
//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------
const DEFAULT_MESSAGE_TEMPLATE = "'{{moduleName}}' module is restricted from being used.";
const CUSTOM_MESSAGE_TEMPLATE = "'{{moduleName}}' module is restricted from being used. {{customMessage}}";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
@ -11,9 +18,29 @@
const ignore = require("ignore");
const arrayOfStrings = {
type: "array",
items: { type: "string" },
uniqueItems: true
};
const arrayOfStringsOrObjects = {
type: "array",
items: {
type: "string"
anyOf: [
{ type: "string" },
{
type: "object",
properties: {
name: { type: "string" },
message: {
type: "string",
minLength: 1
}
},
additionalProperties: false,
required: ["name"]
}
]
},
uniqueItems: true
};
@ -28,17 +55,17 @@ module.exports = {
schema: {
anyOf: [
arrayOfStrings,
arrayOfStringsOrObjects,
{
type: "array",
items: [{
items: {
type: "object",
properties: {
paths: arrayOfStrings,
paths: arrayOfStringsOrObjects,
patterns: arrayOfStrings
},
additionalProperties: false
}],
},
additionalItems: false
}
]
@ -47,17 +74,30 @@ module.exports = {
create(context) {
const options = Array.isArray(context.options) ? context.options : [];
const isStringArray = typeof options[0] !== "object";
const restrictedPaths = new Set(isStringArray ? context.options : options[0].paths || []);
const restrictedPatterns = isStringArray ? [] : options[0].patterns || [];
const isPathAndPatternsObject =
typeof options[0] === "object" &&
(options[0].hasOwnProperty("paths") || options[0].hasOwnProperty("patterns"));
const restrictedPaths = (isPathAndPatternsObject ? options[0].paths : context.options) || [];
const restrictedPatterns = (isPathAndPatternsObject ? options[0].patterns : []) || [];
const restrictedPathMessages = restrictedPaths.reduce((memo, importName) => {
if (typeof importName === "string") {
memo[importName] = null;
} else {
memo[importName.name] = importName.message;
}
return memo;
}, {});
// if no imports are restricted we don"t need to check
if (restrictedPaths.size === 0 && restrictedPatterns.length === 0) {
if (Object.keys(restrictedPaths).length === 0 && restrictedPatterns.length === 0) {
return {};
}
const ig = ignore().add(restrictedPatterns);
/**
* Function to check if a node is a string literal.
* @param {ASTNode} node The node to check.
@ -76,6 +116,39 @@ module.exports = {
return node.callee.type === "Identifier" && node.callee.name === "require";
}
/**
* Report a restricted path.
* @param {node} node representing the restricted path reference
* @returns {void}
* @private
*/
function reportPath(node) {
const moduleName = node.arguments[0].value.trim();
const customMessage = restrictedPathMessages[moduleName];
const message = customMessage
? CUSTOM_MESSAGE_TEMPLATE
: DEFAULT_MESSAGE_TEMPLATE;
context.report({
node,
message,
data: {
moduleName,
customMessage
}
});
}
/**
* Check if the given name is a restricted path name
* @param {string} name name of a variable
* @returns {boolean} whether the variable is a restricted path or not
* @private
*/
function isRestrictedPath(name) {
return Object.prototype.hasOwnProperty.call(restrictedPathMessages, name);
}
return {
CallExpression(node) {
if (isRequireCall(node)) {
@ -85,12 +158,8 @@ module.exports = {
const moduleName = node.arguments[0].value.trim();
// check if argument value is in restricted modules array
if (restrictedPaths.has(moduleName)) {
context.report({
node,
message: "'{{moduleName}}' module is restricted from being used.",
data: { moduleName }
});
if (isRestrictedPath(moduleName)) {
reportPath(node);
}
if (restrictedPatterns.length > 0 && ig.ignores(moduleName)) {

View File

@ -49,7 +49,7 @@ module.exports = {
const options = context.options[0] || {},
skipBlankLines = options.skipBlankLines || false,
ignoreComments = typeof options.ignoreComments === "undefined" || options.ignoreComments;
ignoreComments = typeof options.ignoreComments === "boolean" && options.ignoreComments;
/**
* Report the error message

View File

@ -72,8 +72,10 @@ module.exports = {
node.right,
token => token.value === node.operator
);
const text = sourceCode.getText();
return sourceCode.getText().slice(node.range[0], operatorToken.range[0]) + OPERATOR_INVERSES[node.operator] + sourceCode.getText().slice(operatorToken.range[1], node.range[1]);
return text.slice(node.range[0],
operatorToken.range[0]) + OPERATOR_INVERSES[node.operator] + text.slice(operatorToken.range[1], node.range[1]);
}
if (astUtils.getPrecedence(node) < astUtils.getPrecedence({ type: "UnaryExpression" })) {

View File

@ -59,7 +59,8 @@ module.exports = {
* Only perform a fix if there are no comments between the label and the body. This will be the case
* when there is exactly one token/comment (the ":") between the label and the body.
*/
if (sourceCode.getTokenAfter(node.label, { includeComments: true }) === sourceCode.getTokenBefore(node.body, { includeComments: true })) {
if (sourceCode.getTokenAfter(node.label, { includeComments: true }) ===
sourceCode.getTokenBefore(node.body, { includeComments: true })) {
return fixer.removeRange([node.range[0], node.body.range[0]]);
}

View File

@ -50,7 +50,8 @@ module.exports = {
const rightSquareBracket = sourceCode.getFirstTokenBetween(node.key, node.value, astUtils.isClosingBracketToken);
const tokensBetween = sourceCode.getTokensBetween(leftSquareBracket, rightSquareBracket, 1);
if (tokensBetween.slice(0, -1).some((token, index) => sourceCode.getText().slice(token.range[1], tokensBetween[index + 1].range[0]).trim())) {
if (tokensBetween.slice(0, -1).some((token, index) =>
sourceCode.getText().slice(token.range[1], tokensBetween[index + 1].range[0]).trim())) {
// If there are comments between the brackets and the property name, don't do a fix.
return null;

View File

@ -63,7 +63,14 @@ function parseRegExp(regExpText) {
return Object.assign(state, { inCharClass: false, startingCharClass: false });
}
}
charList.push({ text: char, index, escaped: state.escapeNextChar, inCharClass: state.inCharClass, startsCharClass: state.startingCharClass, endsCharClass: false });
charList.push({
text: char,
index,
escaped: state.escapeNextChar,
inCharClass: state.inCharClass,
startsCharClass: state.startingCharClass,
endsCharClass: false
});
return Object.assign(state, { escapeNextChar: false, startingCharClass: false });
}, { escapeNextChar: false, inCharClass: false, startingCharClass: false });

View File

@ -15,6 +15,15 @@ const astUtils = require("../ast-utils");
// Helpers
//------------------------------------------------------------------------------
/**
* Check whether a given variable is a global variable or not.
* @param {eslint-scope.Variable} variable The variable to check.
* @returns {boolean} `true` if the variable is a global variable.
*/
function isGlobal(variable) {
return Boolean(variable.scope) && variable.scope.type === "global";
}
/**
* Finds the nearest function scope or global scope walking up the scope
* hierarchy.
@ -203,6 +212,7 @@ module.exports = {
* Checks whether it can fix a given variable declaration or not.
* It cannot fix if the following cases:
*
* - A variable is a global variable.
* - A variable is declared on a SwitchCase node.
* - A variable is redeclared.
* - A variable is used from outside the scope.
@ -256,6 +266,7 @@ module.exports = {
if (node.parent.type === "SwitchCase" ||
node.declarations.some(hasSelfReferenceInTDZ) ||
variables.some(isGlobal) ||
variables.some(isRedeclared) ||
variables.some(isUsedFromOutsideOf(scopeNode))
) {

View File

@ -215,8 +215,12 @@ module.exports = {
* @returns {Object} A fix for this node
*/
function makeFunctionShorthand(fixer, node) {
const firstKeyToken = node.computed ? sourceCode.getFirstToken(node, astUtils.isOpeningBracketToken) : sourceCode.getFirstToken(node.key);
const lastKeyToken = node.computed ? sourceCode.getFirstTokenBetween(node.key, node.value, astUtils.isClosingBracketToken) : sourceCode.getLastToken(node.key);
const firstKeyToken = node.computed
? sourceCode.getFirstToken(node, astUtils.isOpeningBracketToken)
: sourceCode.getFirstToken(node.key);
const lastKeyToken = node.computed
? sourceCode.getFirstTokenBetween(node.key, node.value, astUtils.isClosingBracketToken)
: sourceCode.getLastToken(node.key);
const keyText = sourceCode.text.slice(firstKeyToken.range[0], lastKeyToken.range[1]);
let keyPrefix = "";

View File

@ -87,7 +87,9 @@ module.exports = {
if (hasLinebreakBefore !== hasLinebreakAfter && desiredStyle !== "none") {
// If there is a comment before and after the operator, don't do a fix.
if (sourceCode.getTokenBefore(operatorToken, { includeComments: true }) !== tokenBefore && sourceCode.getTokenAfter(operatorToken, { includeComments: true }) !== tokenAfter) {
if (sourceCode.getTokenBefore(operatorToken, { includeComments: true }) !== tokenBefore &&
sourceCode.getTokenAfter(operatorToken, { includeComments: true }) !== tokenAfter) {
return null;
}

View File

@ -30,6 +30,9 @@ module.exports = {
},
ArrowFunctionExpression: {
type: "boolean"
},
FunctionExpression: {
type: "boolean"
}
},
additionalProperties: false
@ -45,7 +48,9 @@ module.exports = {
const DEFAULT_OPTIONS = {
FunctionDeclaration: true,
MethodDefinition: false,
ClassDeclaration: false
ClassDeclaration: false,
ArrowFunctionExpression: false,
FunctionExpression: false
};
const options = Object.assign(DEFAULT_OPTIONS, context.options[0] && context.options[0].require || {});
@ -58,21 +63,6 @@ module.exports = {
context.report({ node, message: "Missing JSDoc comment." });
}
/**
* Check if the jsdoc comment is present for class methods
* @param {ASTNode} node node to examine
* @returns {void}
*/
function checkClassMethodJsDoc(node) {
if (node.parent.type === "MethodDefinition") {
const jsdocComment = source.getJSDocComment(node);
if (!jsdocComment) {
report(node);
}
}
}
/**
* Check if the jsdoc comment is present or not.
* @param {ASTNode} node node to examine
@ -93,8 +83,11 @@ module.exports = {
}
},
FunctionExpression(node) {
if (options.MethodDefinition) {
checkClassMethodJsDoc(node);
if (
(options.MethodDefinition && node.parent.type === "MethodDefinition") ||
(options.FunctionExpression && (node.parent.type === "VariableDeclarator" || (node.parent.type === "Property" && node === node.parent.value)))
) {
checkJsDoc(node);
}
},
ClassDeclaration(node) {

View File

@ -67,9 +67,11 @@ module.exports = {
function usedMemberSyntax(node) {
if (node.specifiers.length === 0) {
return "none";
} else if (node.specifiers[0].type === "ImportNamespaceSpecifier") {
}
if (node.specifiers[0].type === "ImportNamespaceSpecifier") {
return "all";
} else if (node.specifiers.length === 1) {
}
if (node.specifiers.length === 1) {
return "single";
}
return "multiple";
@ -149,7 +151,8 @@ module.exports = {
message: "Member '{{memberName}}' of the import declaration should be sorted alphabetically.",
data: { memberName: importSpecifiers[firstUnsortedIndex].local.name },
fix(fixer) {
if (importSpecifiers.some(specifier => sourceCode.getCommentsBefore(specifier).length || sourceCode.getCommentsAfter(specifier).length)) {
if (importSpecifiers.some(specifier =>
sourceCode.getCommentsBefore(specifier).length || sourceCode.getCommentsAfter(specifier).length)) {
// If there are comments in the ImportSpecifier list, don't rearrange the specifiers.
return null;

View File

@ -226,8 +226,10 @@ module.exports = {
function checkJSDoc(node) {
const jsdocNode = sourceCode.getJSDocComment(node),
functionData = fns.pop(),
params = Object.create(null);
params = Object.create(null),
paramsTags = [];
let hasReturns = false,
returnsTag,
hasConstructor = false,
isInterface = false,
isOverride = false,
@ -261,43 +263,13 @@ module.exports = {
case "param":
case "arg":
case "argument":
if (!tag.type) {
context.report({ node: jsdocNode, message: "Missing JSDoc parameter type for '{{name}}'.", data: { name: tag.name } });
}
if (!tag.description && requireParamDescription) {
context.report({ node: jsdocNode, message: "Missing JSDoc parameter description for '{{name}}'.", data: { name: tag.name } });
}
if (params[tag.name]) {
context.report({ node: jsdocNode, message: "Duplicate JSDoc parameter '{{name}}'.", data: { name: tag.name } });
} else if (tag.name.indexOf(".") === -1) {
params[tag.name] = 1;
}
paramsTags.push(tag);
break;
case "return":
case "returns":
hasReturns = true;
if (!requireReturn && !functionData.returnPresent && (tag.type === null || !isValidReturnType(tag)) && !isAbstract) {
context.report({
node: jsdocNode,
message: "Unexpected @{{title}} tag; function has no return statement.",
data: {
title: tag.title
}
});
} else {
if (requireReturnType && !tag.type) {
context.report({ node: jsdocNode, message: "Missing JSDoc return type." });
}
if (!isValidReturnType(tag) && !tag.description && requireReturnDescription) {
context.report({ node: jsdocNode, message: "Missing JSDoc return description." });
}
}
returnsTag = tag;
break;
case "constructor":
@ -333,6 +305,40 @@ module.exports = {
}
});
paramsTags.forEach(param => {
if (!param.type) {
context.report({ node: jsdocNode, message: "Missing JSDoc parameter type for '{{name}}'.", data: { name: param.name } });
}
if (!param.description && requireParamDescription) {
context.report({ node: jsdocNode, message: "Missing JSDoc parameter description for '{{name}}'.", data: { name: param.name } });
}
if (params[param.name]) {
context.report({ node: jsdocNode, message: "Duplicate JSDoc parameter '{{name}}'.", data: { name: param.name } });
} else if (param.name.indexOf(".") === -1) {
params[param.name] = 1;
}
});
if (hasReturns) {
if (!requireReturn && !functionData.returnPresent && (returnsTag.type === null || !isValidReturnType(returnsTag)) && !isAbstract) {
context.report({
node: jsdocNode,
message: "Unexpected @{{title}} tag; function has no return statement.",
data: {
title: returnsTag.title
}
});
} else {
if (requireReturnType && !returnsTag.type) {
context.report({ node: jsdocNode, message: "Missing JSDoc return type." });
}
if (!isValidReturnType(returnsTag) && !returnsTag.description && requireReturnDescription) {
context.report({ node: jsdocNode, message: "Missing JSDoc return description." });
}
}
}
// check for functions missing @returns
if (!isOverride && !hasReturns && !hasConstructor && !isInterface &&
node.parent.kind !== "get" && node.parent.kind !== "constructor" &&

View File

@ -178,7 +178,7 @@ class RuleTester {
*/
static setDefaultConfig(config) {
if (typeof config !== "object") {
throw new Error("RuleTester.setDefaultConfig: config must be an object");
throw new TypeError("RuleTester.setDefaultConfig: config must be an object");
}
defaultConfig = config;
@ -254,7 +254,7 @@ class RuleTester {
linter = this.linter;
if (lodash.isNil(test) || typeof test !== "object") {
throw new Error(`Test Scenarios for rule ${ruleName} : Could not find test scenario object`);
throw new TypeError(`Test Scenarios for rule ${ruleName} : Could not find test scenario object`);
}
requiredScenarios.forEach(scenarioType => {
@ -369,6 +369,7 @@ class RuleTester {
if (!lodash.isEqual(beforeAST, afterAST)) {
// Not using directly to avoid performance problem in node 6.1.0. See #6111
// eslint-disable-next-line no-restricted-properties
assert.deepEqual(beforeAST, afterAST, "Rule should not modify AST.");
}
}
@ -384,7 +385,7 @@ class RuleTester {
const result = runRuleForItem(item);
const messages = result.messages;
assert.equal(messages.length, 0, util.format("Should have no errors but had %d: %s",
assert.strictEqual(messages.length, 0, util.format("Should have no errors but had %d: %s",
messages.length, util.inspect(messages)));
assertASTDidntChange(result.beforeAST, result.afterAST);
@ -408,7 +409,7 @@ class RuleTester {
`Expected '${actual}' to match ${expected}`
);
} else {
assert.equal(actual, expected);
assert.strictEqual(actual, expected);
}
}
@ -428,10 +429,10 @@ class RuleTester {
if (typeof item.errors === "number") {
assert.equal(messages.length, item.errors, util.format("Should have %d error%s but had %d: %s",
assert.strictEqual(messages.length, item.errors, util.format("Should have %d error%s but had %d: %s",
item.errors, item.errors === 1 ? "" : "s", messages.length, util.inspect(messages)));
} else {
assert.equal(
assert.strictEqual(
messages.length, item.errors.length,
util.format(
"Should have %d error%s but had %d: %s",
@ -460,23 +461,35 @@ class RuleTester {
assertMessageMatches(messages[i].message, item.errors[i].message);
}
// The following checks use loose equality assertions for backwards compatibility.
if (item.errors[i].type) {
// eslint-disable-next-line no-restricted-properties
assert.equal(messages[i].nodeType, item.errors[i].type, `Error type should be ${item.errors[i].type}, found ${messages[i].nodeType}`);
}
if (item.errors[i].hasOwnProperty("line")) {
// eslint-disable-next-line no-restricted-properties
assert.equal(messages[i].line, item.errors[i].line, `Error line should be ${item.errors[i].line}`);
}
if (item.errors[i].hasOwnProperty("column")) {
// eslint-disable-next-line no-restricted-properties
assert.equal(messages[i].column, item.errors[i].column, `Error column should be ${item.errors[i].column}`);
}
if (item.errors[i].hasOwnProperty("endLine")) {
// eslint-disable-next-line no-restricted-properties
assert.equal(messages[i].endLine, item.errors[i].endLine, `Error endLine should be ${item.errors[i].endLine}`);
}
if (item.errors[i].hasOwnProperty("endColumn")) {
// eslint-disable-next-line no-restricted-properties
assert.equal(messages[i].endColumn, item.errors[i].endColumn, `Error endColumn should be ${item.errors[i].endColumn}`);
}
} else {
@ -497,6 +510,7 @@ class RuleTester {
} else {
const fixResult = SourceCodeFixer.applyFixes(item.code, messages);
// eslint-disable-next-line no-restricted-properties
assert.equal(fixResult.output, item.output, "Output is incorrect.");
}
}

View File

@ -160,7 +160,7 @@ function tryParseSelector(rawSelector) {
return esquery.parse(rawSelector.replace(/:exit$/, ""));
} catch (err) {
if (typeof err.offset === "number") {
throw new Error(`Syntax error in selector "${rawSelector}" at position ${err.offset}: ${err.message}`);
throw new SyntaxError(`Syntax error in selector "${rawSelector}" at position ${err.offset}: ${err.message}`);
}
throw err;
}

View File

@ -25,7 +25,6 @@ const TokenStore = require("../token-store"),
* @private
*/
function validate(ast) {
if (!ast.tokens) {
throw new Error("AST is missing the tokens array.");
}
@ -44,34 +43,9 @@ function validate(ast) {
}
/**
* Finds a JSDoc comment node in an array of comment nodes.
* @param {ASTNode[]} comments The array of comment nodes to search.
* @param {int} line Line number to look around
* @returns {ASTNode} The node if found, null if not.
* @private
*/
function findJSDocComment(comments, line) {
if (comments) {
for (let i = comments.length - 1; i >= 0; i--) {
if (comments[i].type === "Block" && comments[i].value.charAt(0) === "*") {
if (line - comments[i].loc.end.line <= 1) {
return comments[i];
}
break;
}
}
}
return null;
}
/**
* Check to see if its a ES6 export declaration
* @param {ASTNode} astNode - any node
* @returns {boolean} whether the given node represents a export declaration
* Check to see if its a ES6 export declaration.
* @param {ASTNode} astNode An AST node.
* @returns {boolean} whether the given node represents an export declaration.
* @private
*/
function looksLikeExport(astNode) {
@ -80,10 +54,11 @@ function looksLikeExport(astNode) {
}
/**
* Merges two sorted lists into a larger sorted list in O(n) time
* @param {Token[]} tokens The list of tokens
* @param {Token[]} comments The list of comments
* @returns {Token[]} A sorted list of tokens and comments
* Merges two sorted lists into a larger sorted list in O(n) time.
* @param {Token[]} tokens The list of tokens.
* @param {Token[]} comments The list of comments.
* @returns {Token[]} A sorted list of tokens and comments.
* @private
*/
function sortedMerge(tokens, comments) {
const result = [];
@ -182,9 +157,9 @@ class SourceCode extends TokenStore {
}
/**
* Split the source code into multiple lines based on the line delimiters
* @param {string} text Source code as a string
* @returns {string[]} Array of source code lines
* Split the source code into multiple lines based on the line delimiters.
* @param {string} text Source code as a string.
* @returns {string[]} Array of source code lines.
* @public
*/
static splitLines(text) {
@ -197,6 +172,7 @@ class SourceCode extends TokenStore {
* @param {int=} beforeCount The number of characters before the node to retrieve.
* @param {int=} afterCount The number of characters after the node to retrieve.
* @returns {string} The text representing the AST node.
* @public
*/
getText(node, beforeCount, afterCount) {
if (node) {
@ -209,6 +185,7 @@ class SourceCode extends TokenStore {
/**
* Gets the entire source text split into an array of lines.
* @returns {Array} The source text as an array of lines.
* @public
*/
getLines() {
return this.lines;
@ -217,6 +194,7 @@ class SourceCode extends TokenStore {
/**
* Retrieves an array containing all comments in the source code.
* @returns {ASTNode[]} An array of comment nodes.
* @public
*/
getAllComments() {
return this.ast.comments;
@ -225,7 +203,8 @@ class SourceCode extends TokenStore {
/**
* Gets all comments for the given node.
* @param {ASTNode} node The AST node to get the comments for.
* @returns {Object} The list of comments indexed by their position.
* @returns {Object} An object containing a leading and trailing array
* of comments indexed by their position.
* @public
*/
getComments(node) {
@ -297,47 +276,68 @@ class SourceCode extends TokenStore {
/**
* Retrieves the JSDoc comment for a given node.
* @param {ASTNode} node The AST node to get the comment for.
* @returns {ASTNode} The Block comment node containing the JSDoc for the
* given node or null if not found.
* @returns {Token|null} The Block comment token containing the JSDoc comment
* for the given node or null if not found.
* @public
*/
getJSDocComment(node) {
/**
* Checks for the presence of a JSDoc comment for the given node and returns it.
* @param {ASTNode} astNode The AST node to get the comment for.
* @returns {Token|null} The Block comment token containing the JSDoc comment
* for the given node or null if not found.
* @private
*/
const findJSDocComment = astNode => {
const tokenBefore = this.getTokenBefore(astNode, { includeComments: true });
if (
tokenBefore &&
astUtils.isCommentToken(tokenBefore) &&
tokenBefore.type === "Block" &&
tokenBefore.value.charAt(0) === "*" &&
astNode.loc.start.line - tokenBefore.loc.end.line <= 1
) {
return tokenBefore;
}
return null;
};
let parent = node.parent;
const leadingComments = this.getCommentsBefore(node);
switch (node.type) {
case "ClassDeclaration":
case "FunctionDeclaration":
if (looksLikeExport(parent)) {
return findJSDocComment(this.getCommentsBefore(parent), parent.loc.start.line);
}
return findJSDocComment(leadingComments, node.loc.start.line);
return findJSDocComment(looksLikeExport(parent) ? parent : node);
case "ClassExpression":
return findJSDocComment(this.getCommentsBefore(parent.parent), parent.parent.loc.start.line);
return findJSDocComment(parent.parent);
case "ArrowFunctionExpression":
case "FunctionExpression":
if (parent.type !== "CallExpression" && parent.type !== "NewExpression") {
let parentLeadingComments = this.getCommentsBefore(parent);
while (!parentLeadingComments.length && !/Function/.test(parent.type) && parent.type !== "MethodDefinition" && parent.type !== "Property") {
while (
!this.getCommentsBefore(parent).length &&
!/Function/.test(parent.type) &&
parent.type !== "MethodDefinition" &&
parent.type !== "Property"
) {
parent = parent.parent;
if (!parent) {
break;
}
parentLeadingComments = this.getCommentsBefore(parent);
}
return parent && parent.type !== "FunctionDeclaration" && parent.type !== "Program" ? findJSDocComment(parentLeadingComments, parent.loc.start.line) : null;
} else if (leadingComments.length) {
return findJSDocComment(leadingComments, node.loc.start.line);
if (parent && parent.type !== "FunctionDeclaration" && parent.type !== "Program") {
return findJSDocComment(parent);
}
}
// falls through
return findJSDocComment(node);
// falls through
default:
return null;
}
@ -347,6 +347,7 @@ class SourceCode extends TokenStore {
* Gets the deepest node containing a range index.
* @param {int} index Range index of the desired node.
* @returns {ASTNode} The node if found or null if not found.
* @public
*/
getNodeByRangeIndex(index) {
let result = null,
@ -380,6 +381,7 @@ class SourceCode extends TokenStore {
* @param {Token} second The token to check before.
* @returns {boolean} True if there is only space between tokens, false
* if there is anything other than whitespace between tokens.
* @public
*/
isSpaceBetweenTokens(first, second) {
const text = this.text.slice(first.range[1], second.range[0]);
@ -388,10 +390,11 @@ class SourceCode extends TokenStore {
}
/**
* Converts a source text index into a (line, column) pair.
* @param {number} index The index of a character in a file
* @returns {Object} A {line, column} location object with a 0-indexed column
*/
* Converts a source text index into a (line, column) pair.
* @param {number} index The index of a character in a file
* @returns {Object} A {line, column} location object with a 0-indexed column
* @public
*/
getLocFromIndex(index) {
if (typeof index !== "number") {
throw new TypeError("Expected `index` to be a number.");
@ -422,12 +425,13 @@ class SourceCode extends TokenStore {
}
/**
* Converts a (line, column) pair into a range index.
* @param {Object} loc A line/column location
* @param {number} loc.line The line number of the location (1-indexed)
* @param {number} loc.column The column number of the location (0-indexed)
* @returns {number} The range index of the location in the file.
*/
* Converts a (line, column) pair into a range index.
* @param {Object} loc A line/column location
* @param {number} loc.line The line number of the location (1-indexed)
* @param {number} loc.column The column number of the location (0-indexed)
* @returns {number} The range index of the location in the file.
* @public
*/
getIndexFromLoc(loc) {
if (typeof loc !== "object" || typeof loc.line !== "number" || typeof loc.column !== "number") {
throw new TypeError("Expected `loc` to be an object with numeric `line` and `column` properties.");

View File

@ -2,6 +2,6 @@ ESLint couldn't find a configuration file. To set up a configuration file for th
eslint --init
ESLint looked for configuration files in <%= directory %> and its ancestors.
ESLint looked for configuration files in <%= directory %> and its ancestors. If it found none, it then looked in your home directory.
If you think you already have a configuration file or if you need more help, please stop by the ESLint chat room: https://gitter.im/eslint/eslint

View File

@ -21,7 +21,7 @@
"_resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz",
"_shasum": "45e37fb39e8da3f25baee3ff5369e2bb5f22017a",
"_spec": "acorn@^3.0.4",
"_where": "/Users/apapirovski/Web/nodejs/tools/eslint-tmp/node_modules/eslint/node_modules/acorn-jsx",
"_where": "/Users/cjihrig/iojs/node/tools/eslint-tmp/node_modules/eslint/node_modules/acorn-jsx",
"bin": {
"acorn": "./bin/acorn"
},

View File

@ -21,7 +21,7 @@
"_resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz",
"_shasum": "afdf9488fb1ecefc8348f6fb22f464e32a58b36b",
"_spec": "acorn-jsx@^3.0.0",
"_where": "/Users/apapirovski/Web/nodejs/tools/eslint-tmp/node_modules/eslint/node_modules/espree",
"_where": "/Users/cjihrig/iojs/node/tools/eslint-tmp/node_modules/eslint/node_modules/espree",
"bugs": {
"url": "https://github.com/RReverser/acorn-jsx/issues"
},

View File

@ -351,8 +351,8 @@ The `bin/acorn` utility can be used to parse a file from the command
line. It accepts as arguments its input file and the following
options:
- `--ecma3|--ecma5|--ecma6|--ecma7`: Sets the ECMAScript version to parse. Default is
version 5.
- `--ecma3|--ecma5|--ecma6|--ecma7|--ecma8|--ecma9`: Sets the ECMAScript version
to parse. Default is version 7.
- `--module`: Sets the parsing mode to `"module"`. Is set to `"script"` otherwise.

View File

@ -14,7 +14,7 @@ var options = {};
function help(status) {
var print = (status == 0) ? console.log : console.error;
print("usage: " + path.basename(process.argv[1]) + " [--ecma3|--ecma5|--ecma6|--ecma7|...|--ecma2015|--ecma2016|...]");
print("usage: " + path.basename(process.argv[1]) + " [--ecma3|--ecma5|--ecma6|--ecma7|--ecma8|--ecma9|...|--ecma2015|--ecma2016|--ecma2017|--ecma2018|...]");
print(" [--tokenize] [--locations] [---allow-hash-bang] [--compact] [--silent] [--module] [--help] [--] [infile]");
process.exit(status);
}

View File

@ -684,6 +684,7 @@ pp$1.parseTopLevel = function(node) {
var stmt = this$1.parseStatement(true, true, exports);
node.body.push(stmt);
}
this.adaptDirectivePrologue(node.body);
this.next();
if (this.options.ecmaVersion >= 6) {
node.sourceType = this.options.sourceType;
@ -1464,6 +1465,22 @@ pp$1.parseImportSpecifiers = function() {
return nodes
};
// Set `ExpressionStatement#directive` property for directive prologues.
pp$1.adaptDirectivePrologue = function(statements) {
for (var i = 0; i < statements.length && this.isDirectiveCandidate(statements[i]); ++i) {
statements[i].directive = statements[i].expression.raw.slice(1, -1);
}
};
pp$1.isDirectiveCandidate = function(statement) {
return (
statement.type === "ExpressionStatement" &&
statement.expression.type === "Literal" &&
typeof statement.expression.value === "string" &&
// Reject parenthesized strings.
(this.input[statement.start] === "\"" || this.input[statement.start] === "'")
)
};
var pp$2 = Parser.prototype;
// Convert existing expression atom to assignable pattern
@ -1578,23 +1595,19 @@ pp$2.parseRestBinding = function() {
// Parses lvalue (assignable) atom.
pp$2.parseBindingAtom = function() {
if (this.options.ecmaVersion < 6) { return this.parseIdent() }
switch (this.type) {
case types.name:
return this.parseIdent()
if (this.options.ecmaVersion >= 6) {
switch (this.type) {
case types.bracketL:
var node = this.startNode();
this.next();
node.elements = this.parseBindingList(types.bracketR, true, true);
return this.finishNode(node, "ArrayPattern")
case types.bracketL:
var node = this.startNode();
this.next();
node.elements = this.parseBindingList(types.bracketR, true, true);
return this.finishNode(node, "ArrayPattern")
case types.braceL:
return this.parseObj(true)
default:
this.unexpected();
case types.braceL:
return this.parseObj(true)
}
}
return this.parseIdent()
};
pp$2.parseBindingList = function(close, allowEmpty, allowTrailingComma) {
@ -1907,7 +1920,7 @@ pp$3.parseMaybeUnary = function(refDestructuringErrors, sawUnary) {
var startPos = this.start, startLoc = this.startLoc, expr;
if (this.inAsync && this.isContextual("await")) {
expr = this.parseAwait(refDestructuringErrors);
expr = this.parseAwait();
sawUnary = true;
} else if (this.type.prefix) {
var node = this.startNode(), update = this.type === types.incDec;
@ -2011,12 +2024,22 @@ pp$3.parseExprAtom = function(refDestructuringErrors) {
case types._super:
if (!this.inFunction)
{ this.raise(this.start, "'super' outside of function or class"); }
case types._this:
var type = this.type === types._this ? "ThisExpression" : "Super";
node = this.startNode();
this.next();
return this.finishNode(node, type)
// The `super` keyword can appear at below:
// SuperProperty:
// super [ Expression ]
// super . IdentifierName
// SuperCall:
// super Arguments
if (this.type !== types.dot && this.type !== types.bracketL && this.type !== types.parenL)
{ this.unexpected(); }
return this.finishNode(node, "Super")
case types._this:
node = this.startNode();
this.next();
return this.finishNode(node, "ThisExpression")
case types.name:
var startPos = this.start, startLoc = this.startLoc;
@ -2251,14 +2274,14 @@ pp$3.parseTemplate = function(ref) {
return this.finishNode(node, "TemplateLiteral")
};
// Parse an object literal or binding pattern.
pp$3.isAsyncProp = function(prop) {
return !prop.computed && prop.key.type === "Identifier" && prop.key.name === "async" &&
(this.type === types.name || this.type === types.num || this.type === types.string || this.type === types.bracketL || this.type.keyword) &&
!lineBreak.test(this.input.slice(this.lastTokEnd, this.start))
};
// Parse an object literal or binding pattern.
pp$3.parseObj = function(isPattern, refDestructuringErrors) {
var this$1 = this;
@ -2271,31 +2294,36 @@ pp$3.parseObj = function(isPattern, refDestructuringErrors) {
if (this$1.afterTrailingComma(types.braceR)) { break }
} else { first = false; }
var prop = this$1.startNode(), isGenerator = (void 0), isAsync = (void 0), startPos = (void 0), startLoc = (void 0);
if (this$1.options.ecmaVersion >= 6) {
prop.method = false;
prop.shorthand = false;
if (isPattern || refDestructuringErrors) {
startPos = this$1.start;
startLoc = this$1.startLoc;
}
if (!isPattern)
{ isGenerator = this$1.eat(types.star); }
}
this$1.parsePropertyName(prop);
if (!isPattern && this$1.options.ecmaVersion >= 8 && !isGenerator && this$1.isAsyncProp(prop)) {
isAsync = true;
this$1.parsePropertyName(prop, refDestructuringErrors);
} else {
isAsync = false;
}
this$1.parsePropertyValue(prop, isPattern, isGenerator, isAsync, startPos, startLoc, refDestructuringErrors);
var prop = this$1.parseProperty(isPattern, refDestructuringErrors);
this$1.checkPropClash(prop, propHash);
node.properties.push(this$1.finishNode(prop, "Property"));
node.properties.push(prop);
}
return this.finishNode(node, isPattern ? "ObjectPattern" : "ObjectExpression")
};
pp$3.parseProperty = function(isPattern, refDestructuringErrors) {
var prop = this.startNode(), isGenerator, isAsync, startPos, startLoc;
if (this.options.ecmaVersion >= 6) {
prop.method = false;
prop.shorthand = false;
if (isPattern || refDestructuringErrors) {
startPos = this.start;
startLoc = this.startLoc;
}
if (!isPattern)
{ isGenerator = this.eat(types.star); }
}
this.parsePropertyName(prop);
if (!isPattern && this.options.ecmaVersion >= 8 && !isGenerator && this.isAsyncProp(prop)) {
isAsync = true;
this.parsePropertyName(prop, refDestructuringErrors);
} else {
isAsync = false;
}
this.parsePropertyValue(prop, isPattern, isGenerator, isAsync, startPos, startLoc, refDestructuringErrors);
return this.finishNode(prop, "Property")
};
pp$3.parsePropertyValue = function(prop, isPattern, isGenerator, isAsync, startPos, startLoc, refDestructuringErrors) {
if ((isGenerator || isAsync) && this.type === types.colon)
{ this.unexpected(); }
@ -2308,10 +2336,11 @@ pp$3.parsePropertyValue = function(prop, isPattern, isGenerator, isAsync, startP
prop.kind = "init";
prop.method = true;
prop.value = this.parseMethod(isGenerator, isAsync);
} else if (this.options.ecmaVersion >= 5 && !prop.computed && prop.key.type === "Identifier" &&
} else if (!isPattern &&
this.options.ecmaVersion >= 5 && !prop.computed && prop.key.type === "Identifier" &&
(prop.key.name === "get" || prop.key.name === "set") &&
(this.type != types.comma && this.type != types.braceR)) {
if (isGenerator || isAsync || isPattern) { this.unexpected(); }
if (isGenerator || isAsync) { this.unexpected(); }
prop.kind = prop.key.name;
this.parsePropertyName(prop);
prop.value = this.parseMethod(false);
@ -2459,6 +2488,7 @@ pp$3.parseFunctionBody = function(node, isArrowFunction) {
this.checkParams(node, !oldStrict && !useStrict && !isArrowFunction && this.isSimpleParamList(node.params));
node.body = this.parseBlock(false);
node.expression = false;
this.adaptDirectivePrologue(node.body.body);
this.labels = oldLabels;
}
this.exitFunctionScope();
@ -2526,10 +2556,6 @@ pp$3.parseExprList = function(close, allowTrailingComma, allowEmpty, refDestruct
return elts
};
// Parse the next token as an identifier. If `liberal` is true (used
// when parsing properties), it will also convert keywords into
// identifiers.
pp$3.checkUnreserved = function(ref) {
var start = ref.start;
var end = ref.end;
@ -2548,6 +2574,10 @@ pp$3.checkUnreserved = function(ref) {
{ this.raiseRecoverable(start, ("The keyword '" + name + "' is reserved")); }
};
// Parse the next token as an identifier. If `liberal` is true (used
// when parsing properties), it will also convert keywords into
// identifiers.
pp$3.parseIdent = function(liberal, isBinding) {
var node = this.startNode();
if (liberal && this.options.allowReserved == "never") { liberal = false; }
@ -2555,6 +2585,15 @@ pp$3.parseIdent = function(liberal, isBinding) {
node.name = this.value;
} else if (this.type.keyword) {
node.name = this.type.keyword;
// To fix https://github.com/ternjs/acorn/issues/575
// `class` and `function` keywords push new context into this.context.
// But there is no chance to pop the context if the keyword is consumed as an identifier such as a property name.
// If the previous token is a dot, this does not apply because the context-managing code already ignored the keyword
if ((node.name === "class" || node.name === "function") &&
(this.lastTokEnd !== this.lastTokStart + 1 || this.input.charCodeAt(this.lastTokStart) !== 46)) {
this.context.pop();
}
} else {
this.unexpected();
}
@ -3105,7 +3144,7 @@ pp$8.readToken_mult_modulo_exp = function(code) { // '%*'
var tokentype = code === 42 ? types.star : types.modulo;
// exponentiation operator ** and **=
if (this.options.ecmaVersion >= 7 && next === 42) {
if (this.options.ecmaVersion >= 7 && code == 42 && next === 42) {
++size;
tokentype = types.starstar;
next = this.input.charCodeAt(this.pos + 2);
@ -3650,7 +3689,7 @@ pp$8.readWord = function() {
// [dammit]: acorn_loose.js
// [walk]: util/walk.js
var version = "5.1.2";
var version = "5.2.1";
// The main exported interface (under `self.acorn` when in the
// browser) is a `parse` function that takes a code string and

View File

@ -690,6 +690,7 @@ pp$1.parseTopLevel = function(node) {
var stmt = this$1.parseStatement(true, true, exports);
node.body.push(stmt);
}
this.adaptDirectivePrologue(node.body);
this.next();
if (this.options.ecmaVersion >= 6) {
node.sourceType = this.options.sourceType;
@ -1470,6 +1471,22 @@ pp$1.parseImportSpecifiers = function() {
return nodes
};
// Set `ExpressionStatement#directive` property for directive prologues.
pp$1.adaptDirectivePrologue = function(statements) {
for (var i = 0; i < statements.length && this.isDirectiveCandidate(statements[i]); ++i) {
statements[i].directive = statements[i].expression.raw.slice(1, -1);
}
};
pp$1.isDirectiveCandidate = function(statement) {
return (
statement.type === "ExpressionStatement" &&
statement.expression.type === "Literal" &&
typeof statement.expression.value === "string" &&
// Reject parenthesized strings.
(this.input[statement.start] === "\"" || this.input[statement.start] === "'")
)
};
var pp$2 = Parser.prototype;
// Convert existing expression atom to assignable pattern
@ -1584,23 +1601,19 @@ pp$2.parseRestBinding = function() {
// Parses lvalue (assignable) atom.
pp$2.parseBindingAtom = function() {
if (this.options.ecmaVersion < 6) { return this.parseIdent() }
switch (this.type) {
case types.name:
return this.parseIdent()
if (this.options.ecmaVersion >= 6) {
switch (this.type) {
case types.bracketL:
var node = this.startNode();
this.next();
node.elements = this.parseBindingList(types.bracketR, true, true);
return this.finishNode(node, "ArrayPattern")
case types.bracketL:
var node = this.startNode();
this.next();
node.elements = this.parseBindingList(types.bracketR, true, true);
return this.finishNode(node, "ArrayPattern")
case types.braceL:
return this.parseObj(true)
default:
this.unexpected();
case types.braceL:
return this.parseObj(true)
}
}
return this.parseIdent()
};
pp$2.parseBindingList = function(close, allowEmpty, allowTrailingComma) {
@ -1913,7 +1926,7 @@ pp$3.parseMaybeUnary = function(refDestructuringErrors, sawUnary) {
var startPos = this.start, startLoc = this.startLoc, expr;
if (this.inAsync && this.isContextual("await")) {
expr = this.parseAwait(refDestructuringErrors);
expr = this.parseAwait();
sawUnary = true;
} else if (this.type.prefix) {
var node = this.startNode(), update = this.type === types.incDec;
@ -2017,12 +2030,22 @@ pp$3.parseExprAtom = function(refDestructuringErrors) {
case types._super:
if (!this.inFunction)
{ this.raise(this.start, "'super' outside of function or class"); }
case types._this:
var type = this.type === types._this ? "ThisExpression" : "Super";
node = this.startNode();
this.next();
return this.finishNode(node, type)
// The `super` keyword can appear at below:
// SuperProperty:
// super [ Expression ]
// super . IdentifierName
// SuperCall:
// super Arguments
if (this.type !== types.dot && this.type !== types.bracketL && this.type !== types.parenL)
{ this.unexpected(); }
return this.finishNode(node, "Super")
case types._this:
node = this.startNode();
this.next();
return this.finishNode(node, "ThisExpression")
case types.name:
var startPos = this.start, startLoc = this.startLoc;
@ -2257,14 +2280,14 @@ pp$3.parseTemplate = function(ref) {
return this.finishNode(node, "TemplateLiteral")
};
// Parse an object literal or binding pattern.
pp$3.isAsyncProp = function(prop) {
return !prop.computed && prop.key.type === "Identifier" && prop.key.name === "async" &&
(this.type === types.name || this.type === types.num || this.type === types.string || this.type === types.bracketL || this.type.keyword) &&
!lineBreak.test(this.input.slice(this.lastTokEnd, this.start))
};
// Parse an object literal or binding pattern.
pp$3.parseObj = function(isPattern, refDestructuringErrors) {
var this$1 = this;
@ -2277,31 +2300,36 @@ pp$3.parseObj = function(isPattern, refDestructuringErrors) {
if (this$1.afterTrailingComma(types.braceR)) { break }
} else { first = false; }
var prop = this$1.startNode(), isGenerator = (void 0), isAsync = (void 0), startPos = (void 0), startLoc = (void 0);
if (this$1.options.ecmaVersion >= 6) {
prop.method = false;
prop.shorthand = false;
if (isPattern || refDestructuringErrors) {
startPos = this$1.start;
startLoc = this$1.startLoc;
}
if (!isPattern)
{ isGenerator = this$1.eat(types.star); }
}
this$1.parsePropertyName(prop);
if (!isPattern && this$1.options.ecmaVersion >= 8 && !isGenerator && this$1.isAsyncProp(prop)) {
isAsync = true;
this$1.parsePropertyName(prop, refDestructuringErrors);
} else {
isAsync = false;
}
this$1.parsePropertyValue(prop, isPattern, isGenerator, isAsync, startPos, startLoc, refDestructuringErrors);
var prop = this$1.parseProperty(isPattern, refDestructuringErrors);
this$1.checkPropClash(prop, propHash);
node.properties.push(this$1.finishNode(prop, "Property"));
node.properties.push(prop);
}
return this.finishNode(node, isPattern ? "ObjectPattern" : "ObjectExpression")
};
pp$3.parseProperty = function(isPattern, refDestructuringErrors) {
var prop = this.startNode(), isGenerator, isAsync, startPos, startLoc;
if (this.options.ecmaVersion >= 6) {
prop.method = false;
prop.shorthand = false;
if (isPattern || refDestructuringErrors) {
startPos = this.start;
startLoc = this.startLoc;
}
if (!isPattern)
{ isGenerator = this.eat(types.star); }
}
this.parsePropertyName(prop);
if (!isPattern && this.options.ecmaVersion >= 8 && !isGenerator && this.isAsyncProp(prop)) {
isAsync = true;
this.parsePropertyName(prop, refDestructuringErrors);
} else {
isAsync = false;
}
this.parsePropertyValue(prop, isPattern, isGenerator, isAsync, startPos, startLoc, refDestructuringErrors);
return this.finishNode(prop, "Property")
};
pp$3.parsePropertyValue = function(prop, isPattern, isGenerator, isAsync, startPos, startLoc, refDestructuringErrors) {
if ((isGenerator || isAsync) && this.type === types.colon)
{ this.unexpected(); }
@ -2314,10 +2342,11 @@ pp$3.parsePropertyValue = function(prop, isPattern, isGenerator, isAsync, startP
prop.kind = "init";
prop.method = true;
prop.value = this.parseMethod(isGenerator, isAsync);
} else if (this.options.ecmaVersion >= 5 && !prop.computed && prop.key.type === "Identifier" &&
} else if (!isPattern &&
this.options.ecmaVersion >= 5 && !prop.computed && prop.key.type === "Identifier" &&
(prop.key.name === "get" || prop.key.name === "set") &&
(this.type != types.comma && this.type != types.braceR)) {
if (isGenerator || isAsync || isPattern) { this.unexpected(); }
if (isGenerator || isAsync) { this.unexpected(); }
prop.kind = prop.key.name;
this.parsePropertyName(prop);
prop.value = this.parseMethod(false);
@ -2465,6 +2494,7 @@ pp$3.parseFunctionBody = function(node, isArrowFunction) {
this.checkParams(node, !oldStrict && !useStrict && !isArrowFunction && this.isSimpleParamList(node.params));
node.body = this.parseBlock(false);
node.expression = false;
this.adaptDirectivePrologue(node.body.body);
this.labels = oldLabels;
}
this.exitFunctionScope();
@ -2532,10 +2562,6 @@ pp$3.parseExprList = function(close, allowTrailingComma, allowEmpty, refDestruct
return elts
};
// Parse the next token as an identifier. If `liberal` is true (used
// when parsing properties), it will also convert keywords into
// identifiers.
pp$3.checkUnreserved = function(ref) {
var start = ref.start;
var end = ref.end;
@ -2554,6 +2580,10 @@ pp$3.checkUnreserved = function(ref) {
{ this.raiseRecoverable(start, ("The keyword '" + name + "' is reserved")); }
};
// Parse the next token as an identifier. If `liberal` is true (used
// when parsing properties), it will also convert keywords into
// identifiers.
pp$3.parseIdent = function(liberal, isBinding) {
var node = this.startNode();
if (liberal && this.options.allowReserved == "never") { liberal = false; }
@ -2561,6 +2591,15 @@ pp$3.parseIdent = function(liberal, isBinding) {
node.name = this.value;
} else if (this.type.keyword) {
node.name = this.type.keyword;
// To fix https://github.com/ternjs/acorn/issues/575
// `class` and `function` keywords push new context into this.context.
// But there is no chance to pop the context if the keyword is consumed as an identifier such as a property name.
// If the previous token is a dot, this does not apply because the context-managing code already ignored the keyword
if ((node.name === "class" || node.name === "function") &&
(this.lastTokEnd !== this.lastTokStart + 1 || this.input.charCodeAt(this.lastTokStart) !== 46)) {
this.context.pop();
}
} else {
this.unexpected();
}
@ -3111,7 +3150,7 @@ pp$8.readToken_mult_modulo_exp = function(code) { // '%*'
var tokentype = code === 42 ? types.star : types.modulo;
// exponentiation operator ** and **=
if (this.options.ecmaVersion >= 7 && next === 42) {
if (this.options.ecmaVersion >= 7 && code == 42 && next === 42) {
++size;
tokentype = types.starstar;
next = this.input.charCodeAt(this.pos + 2);
@ -3656,7 +3695,7 @@ pp$8.readWord = function() {
// [dammit]: acorn_loose.js
// [walk]: util/walk.js
var version = "5.1.2";
var version = "5.2.1";
// The main exported interface (under `self.acorn` when in the
// browser) is a `parse` function that takes a code string and

View File

@ -300,6 +300,7 @@ lp$1.parseTopLevel = function() {
var node = this.startNodeAt(this.options.locations ? [0, getLineInfo(this.input, 0)] : 0);
node.body = [];
while (this.tok.type !== tokTypes.eof) { node.body.push(this$1.parseStatement()); }
this.toks.adaptDirectivePrologue(node.body);
this.last = this.tok;
if (this.options.ecmaVersion >= 6) {
node.sourceType = this.options.sourceType;
@ -630,6 +631,7 @@ lp$1.parseFunction = function(node, isStatement, isAsync) {
this.inAsync = node.async;
node.params = this.parseFunctionParams();
node.body = this.parseBlock();
this.toks.adaptDirectivePrologue(node.body.body);
this.inAsync = oldInAsync;
return this.finishNode(node, isStatement ? "FunctionDeclaration" : "FunctionExpression")
};
@ -676,7 +678,6 @@ lp$1.parseImport = function() {
if (this.tok.type === tokTypes.string) {
node.specifiers = [];
node.source = this.parseExprAtom();
node.kind = "";
} else {
var elt;
if (this.tok.type === tokTypes.name && this.tok.value !== "from") {
@ -1150,7 +1151,7 @@ lp$2.parseObj = function() {
prop.value = this$1.parseMethod(isGenerator, isAsync);
} else if (this$1.options.ecmaVersion >= 5 && prop.key.type === "Identifier" &&
!prop.computed && (prop.key.name === "get" || prop.key.name === "set") &&
(this$1.tok.type != tokTypes.comma && this$1.tok.type != tokTypes.braceR)) {
(this$1.tok.type != tokTypes.comma && this$1.tok.type != tokTypes.braceR && this$1.tok.type != tokTypes.eq)) {
prop.kind = prop.key.name;
this$1.parsePropertyName(prop);
prop.value = this$1.parseMethod(false);
@ -1285,8 +1286,8 @@ lp$2.parseMethod = function(isGenerator, isAsync) {
{ node.async = !!isAsync; }
this.inAsync = node.async;
node.params = this.parseFunctionParams();
node.expression = this.options.ecmaVersion >= 6 && this.tok.type !== tokTypes.braceL;
node.body = node.expression ? this.parseMaybeAssign() : this.parseBlock();
node.body = this.parseBlock();
this.toks.adaptDirectivePrologue(node.body.body);
this.inAsync = oldInAsync;
return this.finishNode(node, "FunctionExpression")
};
@ -1299,7 +1300,12 @@ lp$2.parseArrowExpression = function(node, params, isAsync) {
this.inAsync = node.async;
node.params = this.toAssignableList(params, true);
node.expression = this.tok.type !== tokTypes.braceL;
node.body = node.expression ? this.parseMaybeAssign() : this.parseBlock();
if (node.expression) {
node.body = this.parseMaybeAssign();
} else {
node.body = this.parseBlock();
this.toks.adaptDirectivePrologue(node.body.body);
}
this.inAsync = oldInAsync;
return this.finishNode(node, "ArrowFunctionExpression")
};

View File

@ -304,6 +304,7 @@ lp$1.parseTopLevel = function() {
var node = this.startNodeAt(this.options.locations ? [0, __acorn.getLineInfo(this.input, 0)] : 0);
node.body = [];
while (this.tok.type !== __acorn.tokTypes.eof) { node.body.push(this$1.parseStatement()); }
this.toks.adaptDirectivePrologue(node.body);
this.last = this.tok;
if (this.options.ecmaVersion >= 6) {
node.sourceType = this.options.sourceType;
@ -634,6 +635,7 @@ lp$1.parseFunction = function(node, isStatement, isAsync) {
this.inAsync = node.async;
node.params = this.parseFunctionParams();
node.body = this.parseBlock();
this.toks.adaptDirectivePrologue(node.body.body);
this.inAsync = oldInAsync;
return this.finishNode(node, isStatement ? "FunctionDeclaration" : "FunctionExpression")
};
@ -680,7 +682,6 @@ lp$1.parseImport = function() {
if (this.tok.type === __acorn.tokTypes.string) {
node.specifiers = [];
node.source = this.parseExprAtom();
node.kind = "";
} else {
var elt;
if (this.tok.type === __acorn.tokTypes.name && this.tok.value !== "from") {
@ -1154,7 +1155,7 @@ lp$2.parseObj = function() {
prop.value = this$1.parseMethod(isGenerator, isAsync);
} else if (this$1.options.ecmaVersion >= 5 && prop.key.type === "Identifier" &&
!prop.computed && (prop.key.name === "get" || prop.key.name === "set") &&
(this$1.tok.type != __acorn.tokTypes.comma && this$1.tok.type != __acorn.tokTypes.braceR)) {
(this$1.tok.type != __acorn.tokTypes.comma && this$1.tok.type != __acorn.tokTypes.braceR && this$1.tok.type != __acorn.tokTypes.eq)) {
prop.kind = prop.key.name;
this$1.parsePropertyName(prop);
prop.value = this$1.parseMethod(false);
@ -1289,8 +1290,8 @@ lp$2.parseMethod = function(isGenerator, isAsync) {
{ node.async = !!isAsync; }
this.inAsync = node.async;
node.params = this.parseFunctionParams();
node.expression = this.options.ecmaVersion >= 6 && this.tok.type !== __acorn.tokTypes.braceL;
node.body = node.expression ? this.parseMaybeAssign() : this.parseBlock();
node.body = this.parseBlock();
this.toks.adaptDirectivePrologue(node.body.body);
this.inAsync = oldInAsync;
return this.finishNode(node, "FunctionExpression")
};
@ -1303,7 +1304,12 @@ lp$2.parseArrowExpression = function(node, params, isAsync) {
this.inAsync = node.async;
node.params = this.toAssignableList(params, true);
node.expression = this.tok.type !== __acorn.tokTypes.braceL;
node.body = node.expression ? this.parseMaybeAssign() : this.parseBlock();
if (node.expression) {
node.body = this.parseMaybeAssign();
} else {
node.body = this.parseBlock();
this.toks.adaptDirectivePrologue(node.body.body);
}
this.inAsync = oldInAsync;
return this.finishNode(node, "ArrowFunctionExpression")
};

View File

@ -68,7 +68,7 @@ function full(node, callback, base, state, override) {
; }(function c(node, st, override) {
var type = override || node.type;
base[type](node, st, c);
callback(node, st, type);
if (!override) { callback(node, st, type); }
})(node, state, override);
}
@ -81,7 +81,7 @@ function fullAncestor(node, callback, base, state) {
var isNew = node != ancestors[ancestors.length - 1];
if (isNew) { ancestors.push(node); }
base[type](node, st, c);
callback(node, st || ancestors, ancestors, type);
if (!override) { callback(node, st || ancestors, ancestors, type); }
if (isNew) { ancestors.pop(); }
})(node, state);
}

View File

@ -74,7 +74,7 @@ function full(node, callback, base, state, override) {
; }(function c(node, st, override) {
var type = override || node.type;
base[type](node, st, c);
callback(node, st, type);
if (!override) { callback(node, st, type); }
})(node, state, override);
}
@ -87,7 +87,7 @@ function fullAncestor(node, callback, base, state) {
var isNew = node != ancestors[ancestors.length - 1];
if (isNew) { ancestors.push(node); }
base[type](node, st, c);
callback(node, st || ancestors, ancestors, type);
if (!override) { callback(node, st || ancestors, ancestors, type); }
if (isNew) { ancestors.pop(); }
})(node, state);
}

View File

@ -1,8 +1,8 @@
{
"_from": "acorn@^5.1.1",
"_id": "acorn@5.1.2",
"_id": "acorn@5.2.1",
"_inBundle": false,
"_integrity": "sha512-o96FZLJBPY1lvTuJylGA9Bk3t/GKPPJG8H0ydQQl01crzwJgspa4AEIq/pVTXigmK0PHVQhiAtn8WMBLL9D2WA==",
"_integrity": "sha512-jG0u7c4Ly+3QkkW18V+NRDN+4bWHdln30NL1ZL2AvFZZmQe/BfopYCtghCKKVBUSetZ4QKcyA0pY6/4Gw8Pv8w==",
"_location": "/eslint/acorn",
"_phantomChildren": {},
"_requested": {
@ -18,10 +18,10 @@
"_requiredBy": [
"/eslint/espree"
],
"_resolved": "https://registry.npmjs.org/acorn/-/acorn-5.1.2.tgz",
"_shasum": "911cb53e036807cf0fa778dc5d370fbd864246d7",
"_resolved": "https://registry.npmjs.org/acorn/-/acorn-5.2.1.tgz",
"_shasum": "317ac7821826c22c702d66189ab8359675f135d7",
"_spec": "acorn@^5.1.1",
"_where": "/Users/apapirovski/Web/nodejs/tools/eslint-tmp/node_modules/eslint/node_modules/espree",
"_where": "/Users/cjihrig/iojs/node/tools/eslint-tmp/node_modules/eslint/node_modules/espree",
"bin": {
"acorn": "./bin/acorn"
},
@ -282,5 +282,5 @@
"pretest": "npm run build:main && npm run build:loose",
"test": "node test/run.js && node test/lint.js"
},
"version": "5.1.2"
"version": "5.2.1"
}

View File

@ -216,7 +216,7 @@ var validItems = [1, 5, 10, 20, 50, 100, 200, 500, 1000];
var invalidItems = [1, 0, 2000, 11, 57, 123, 'foo'];
```
__Please note__: this keyword is moved here from Ajv, mainly to preserve beckward compatibility. It is unlikely to become a standard. It's preferreable to use `if`/`then`/`else` keywords if possible, as they are likely to be added to the standard. The above schema is equivalent to (for example):
__Please note__: this keyword is moved here from Ajv, mainly to preserve backward compatibility. It is unlikely to become a standard. It's preferable to use `if`/`then`/`else` keywords if possible, as they are likely to be added to the standard. The above schema is equivalent to (for example):
```javascript
{
@ -312,12 +312,12 @@ var invalidDataList = [
];
```
__Please note__: the current implementation is BETA. It does not allow using relative URIs in $ref keywords in schemas in `selectCases` and `selectDefault` that point ouside of these schemas. The workaround is to use absolute URIs (that can point to any (sub-)schema added to Ajv, including those inside the current root schema where `select` is used). See [tests](https://github.com/epoberezkin/ajv-keywords/blob/v2.0.0/spec/tests/select.json#L314).
__Please note__: the current implementation is BETA. It does not allow using relative URIs in $ref keywords in schemas in `selectCases` and `selectDefault` that point outside of these schemas. The workaround is to use absolute URIs (that can point to any (sub-)schema added to Ajv, including those inside the current root schema where `select` is used). See [tests](https://github.com/epoberezkin/ajv-keywords/blob/v2.0.0/spec/tests/select.json#L314).
### `patternRequired`
This keyword allows to require the presense of properties that match some pattern(s).
This keyword allows to require the presence of properties that match some pattern(s).
This keyword applies only to objects. If the data is not an object, the validation succeeds.
@ -456,7 +456,7 @@ The keyword allows to check that some properties in array items are unique.
This keyword applies only to arrays. If the data is not an array, the validation succeeds.
The value of this keyword must be an array of strings - property names that should have unique values accross all items.
The value of this keyword must be an array of strings - property names that should have unique values across all items.
```javascript
var schema = { uniqueItemProperties: [ "id", "name" ] };

View File

@ -1,8 +1,8 @@
{
"_from": "ajv-keywords@^2.1.0",
"_id": "ajv-keywords@2.1.0",
"_id": "ajv-keywords@2.1.1",
"_inBundle": false,
"_integrity": "sha1-opbhf3v658HOT34N5T0pyzIWLfA=",
"_integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=",
"_location": "/eslint/ajv-keywords",
"_phantomChildren": {},
"_requested": {
@ -18,10 +18,10 @@
"_requiredBy": [
"/eslint/table"
],
"_resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.0.tgz",
"_shasum": "a296e17f7bfae7c1ce4f7e0de53d29cb32162df0",
"_resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz",
"_shasum": "617997fc5f60576894c435f940d819e135b80762",
"_spec": "ajv-keywords@^2.1.0",
"_where": "/Users/apapirovski/Web/nodejs/tools/eslint-tmp/node_modules/eslint/node_modules/table",
"_where": "/Users/cjihrig/iojs/node/tools/eslint-tmp/node_modules/eslint/node_modules/table",
"author": {
"name": "Evgeny Poberezkin"
},
@ -34,15 +34,15 @@
"devDependencies": {
"ajv": "^5.0.0",
"ajv-pack": "^0.3.0",
"chai": "^3.5.0",
"coveralls": "^2.11.9",
"chai": "^4.0.2",
"coveralls": "^3.0.0",
"dot": "^1.1.1",
"eslint": "^3.6.0",
"eslint": "^4.9.0",
"glob": "^7.1.1",
"istanbul": "^0.4.3",
"js-beautify": "^1.6.4",
"js-beautify": "^1.7.4",
"json-schema-test": "^1.3.0",
"mocha": "^3.0.2",
"mocha": "^4.0.0",
"pre-commit": "^1.1.3",
"uuid": "^3.0.1"
},
@ -60,7 +60,7 @@
"main": "index.js",
"name": "ajv-keywords",
"peerDependencies": {
"ajv": ">=5.0.0"
"ajv": "^5.0.0"
},
"repository": {
"type": "git",
@ -74,5 +74,5 @@
"test-cov": "istanbul cover -x 'spec/**' node_modules/mocha/bin/_mocha -- spec/*.spec.js -R spec",
"test-spec": "mocha spec/*.spec.js -R spec"
},
"version": "2.1.0"
"version": "2.1.1"
}

View File

@ -2,7 +2,7 @@
# Ajv: Another JSON Schema Validator
The fastest JSON Schema validator for node.js and browser with draft 6 support.
The fastest JSON Schema validator for Node.js and browser with draft 6 support.
[![Build Status](https://travis-ci.org/epoberezkin/ajv.svg?branch=master)](https://travis-ci.org/epoberezkin/ajv)
@ -69,7 +69,7 @@ Currently Ajv is the fastest and the most standard compliant validator according
- [themis benchmark](https://cdn.rawgit.com/playlyfe/themis/master/benchmark/results.html)
Performace of different validators by [json-schema-benchmark](https://github.com/ebdrup/json-schema-benchmark):
Performance of different validators by [json-schema-benchmark](https://github.com/ebdrup/json-schema-benchmark):
[![performance](https://chart.googleapis.com/chart?chxt=x,y&cht=bhs&chco=76A4FB&chls=2.0&chbh=32,4,1&chs=600x416&chxl=-1:%7Cajv%7Cis-my-json-valid%7Cjsen%7Cschemasaurus%7Cthemis%7Cz-schema%7Cjsck%7Cjsonschema%7Cskeemas%7Ctv4%7Cjayschema&chd=t:100,68,61,22.8,17.6,6.6,2.7,0.9,0.7,0.4,0.1)](https://github.com/ebdrup/json-schema-benchmark/blob/master/README.md#performance)
@ -83,7 +83,7 @@ Performace of different validators by [json-schema-benchmark](https://github.com
- correct string lengths for strings with unicode pairs (can be turned off)
- [formats](#formats) defined by JSON Schema draft 4 standard and custom formats (can be turned off)
- [validates schemas against meta-schema](#api-validateschema)
- supports [browsers](#using-in-browser) and nodejs 0.10-7.x
- supports [browsers](#using-in-browser) and Node.js 0.10-8.x
- [asynchronous loading](#asynchronous-schema-compilation) of referenced schemas during compilation
- "All errors" validation mode with [option allErrors](#options)
- [error messages with parameters](#validation-errors) describing error reasons to allow creating custom error messages
@ -110,7 +110,7 @@ npm install ajv
## <a name="usage"></a>Getting started
Try it in the node REPL: https://tonicdev.com/npm/ajv
Try it in the Node.js REPL: https://tonicdev.com/npm/ajv
The fastest validation call:
@ -144,11 +144,11 @@ if (!valid) console.log(ajv.errorsText());
See [API](#api) and [Options](#options) for more details.
Ajv compiles schemas to functions and caches them in all cases (using schema serialized with [json-stable-stringify](https://github.com/substack/json-stable-stringify) or a custom function as a key), so that the next time the same schema is used (not necessarily the same object instance) it won't be compiled again.
Ajv compiles schemas to functions and caches them in all cases (using schema serialized with [fast-json-stable-stringify](https://github.com/epoberezkin/fast-json-stable-stringify) or a custom function as a key), so that the next time the same schema is used (not necessarily the same object instance) it won't be compiled again.
The best performance is achieved when using compiled functions returned by `compile` or `getSchema` methods (there is no additional function call).
__Please note__: every time validation function or `ajv.validate` are called `errors` property is overwritten. You need to copy `errors` array reference to another variable if you want to use it later (e.g., in the callback). See [Validation errors](#validation-errors)
__Please note__: every time a validation function or `ajv.validate` are called `errors` property is overwritten. You need to copy `errors` array reference to another variable if you want to use it later (e.g., in the callback). See [Validation errors](#validation-errors)
## Using in browser
@ -162,7 +162,7 @@ Then you need to load Ajv in the browser:
<script src="ajv.min.js"></script>
```
This bundle can be used with different module systems or creates global `Ajv` if no module system is found.
This bundle can be used with different module systems; it creates global `Ajv` if no module system is found.
The browser bundle is available on [cdnjs](https://cdnjs.com/libraries/ajv).
@ -197,7 +197,7 @@ Ajv supports all validation keywords from draft 4 of JSON-schema standard:
- [for numbers](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#keywords-for-numbers) - maximum, minimum, exclusiveMaximum, exclusiveMinimum, multipleOf
- [for strings](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#keywords-for-strings) - maxLength, minLength, pattern, format
- [for arrays](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#keywords-for-arrays) - maxItems, minItems, uniqueItems, items, additionalItems, [contains](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#contains)
- [for objects](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#keywords-for-objects) - maxProperties, minproperties, required, properties, patternProperties, additionalProperties, dependencies, [propertyNames](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#propertynames)
- [for objects](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#keywords-for-objects) - maxProperties, minProperties, required, properties, patternProperties, additionalProperties, dependencies, [propertyNames](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#propertynames)
- [for all types](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#keywords-for-all-types) - enum, [const](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#const)
- [compound keywords](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#compound-keywords) - not, oneOf, anyOf, allOf
@ -233,7 +233,7 @@ There are two modes of format validation: `fast` and `full`. This mode affects f
You can add additional formats and replace any of the formats above using [addFormat](#api-addformat) method.
The option `unknownFormats` allows to change the behaviour in case an unknown format is encountered - Ajv can either fail schema compilation (default) or ignore it (default in versions before 5.0.0). You also can whitelist specific format(s) to be ignored. See [Options](#options) for details.
The option `unknownFormats` allows changing the default behaviour when an unknown format is encountered. In this case Ajv can either fail schema compilation (default) or ignore it (default in versions before 5.0.0). You also can whitelist specific format(s) to be ignored. See [Options](#options) for details.
You can find patterns used for format validation and the sources that were used in [formats.js](https://github.com/epoberezkin/ajv/blob/master/lib/compile/formats.js).
@ -265,14 +265,14 @@ var defsSchema = {
Now to compile your schema you can either pass all schemas to Ajv instance:
```
```javascript
var ajv = new Ajv({schemas: [schema, defsSchema]});
var validate = ajv.getSchema('http://example.com/schemas/schema.json');
```
or use `addSchema` method:
```
```javascript
var ajv = new Ajv;
ajv.addSchema(defsSchema);
var validate = ajv.compile(schema);
@ -287,7 +287,7 @@ __Please note__:
- The actual location of the schema file in the file system is not used.
- You can pass the identifier of the schema as the second parameter of `addSchema` method or as a property name in `schemas` option. This identifier can be used instead of (or in addition to) schema $id.
- You cannot have the same $id (or the schema identifier) used for more than one schema - the exception will be thrown.
- You can implement dynamic resolution of the referenced schemas using `compileAsync` method. In this way you can store schemas in any system (files, web, database, etc.) and reference them without explicitely adding to Ajv instance. See [Asynchronous schema compilation](#asynchronous-schema-compilation).
- You can implement dynamic resolution of the referenced schemas using `compileAsync` method. In this way you can store schemas in any system (files, web, database, etc.) and reference them without explicitly adding to Ajv instance. See [Asynchronous schema compilation](#asynchronous-schema-compilation).
## $data reference
@ -481,9 +481,9 @@ __Please note__: [Option](#options) `missingRefs` should NOT be set to `"ignore"
## Asynchronous validation
Example in node REPL: https://tonicdev.com/esp/ajv-asynchronous-validation
Example in Node.js REPL: https://tonicdev.com/esp/ajv-asynchronous-validation
You can define custom formats and keywords that perform validation asyncronously by accessing database or some service. You should add `async: true` in the keyword or format defnition (see [addFormat](#api-addformat), [addKeyword](#api-addkeyword) and [Defining custom keywords](#defining-custom-keywords)).
You can define custom formats and keywords that perform validation asynchronously by accessing database or some other service. You should add `async: true` in the keyword or format definition (see [addFormat](#api-addformat), [addKeyword](#api-addkeyword) and [Defining custom keywords](#defining-custom-keywords)).
If your schema uses asynchronous formats/keywords or refers to some schema that contains them it should have `"$async": true` keyword so that Ajv can compile it correctly. If asynchronous format/keyword or reference to asynchronous schema is used in the schema without `$async` keyword Ajv will throw an exception during schema compilation.
@ -491,17 +491,17 @@ __Please note__: all asynchronous subschemas that are referenced from the curren
Validation function for an asynchronous custom format/keyword should return a promise that resolves with `true` or `false` (or rejects with `new Ajv.ValidationError(errors)` if you want to return custom errors from the keyword function). Ajv compiles asynchronous schemas to either [es7 async functions](http://tc39.github.io/ecmascript-asyncawait/) that can optionally be transpiled with [nodent](https://github.com/MatAtBread/nodent) or with [regenerator](https://github.com/facebook/regenerator) or to [generator functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*) that can be optionally transpiled with regenerator as well. You can also supply any other transpiler as a function. See [Options](#options).
The compiled validation function has `$async: true` property (if the schema is asynchronous), so you can differentiate these functions if you are using both syncronous and asynchronous schemas.
The compiled validation function has `$async: true` property (if the schema is asynchronous), so you can differentiate these functions if you are using both synchronous and asynchronous schemas.
If you are using generators, the compiled validation function can be either wrapped with [co](https://github.com/tj/co) (default) or returned as generator function, that can be used directly, e.g. in [koa](http://koajs.com/) 1.0. `co` is a small library, it is included in Ajv (both as npm dependency and in the browser bundle).
Async functions are currently supported in Chrome 55, Firefox 52, Node 7 (with --harmony-async-await) and MS Edge 13 (with flag).
Async functions are currently supported in Chrome 55, Firefox 52, Node.js 7 (with --harmony-async-await) and MS Edge 13 (with flag).
Generator functions are currently supported in Chrome, Firefox and node.js.
Generator functions are currently supported in Chrome, Firefox and Node.js.
If you are using Ajv in other browsers or in older versions of node.js you should use one of available transpiling options. All provided async modes use global Promise class. If your platform does not have Promise you should use a polyfill that defines it.
If you are using Ajv in other browsers or in older versions of Node.js you should use one of available transpiling options. All provided async modes use global Promise class. If your platform does not have Promise you should use a polyfill that defines it.
Validation result will be a promise that resolves with validated data or rejects with an exception `Ajv.ValidationError` that has the array of validation errors in `errors` property.
Validation result will be a promise that resolves with validated data or rejects with an exception `Ajv.ValidationError` that contains the array of validation errors in `errors` property.
Example:
@ -564,10 +564,9 @@ validate({ userId: 1, postId: 19 })
// data is invalid
console.log('Validation errors:', err.errors);
});
```
### Using transpilers with asyncronous validation functions.
### Using transpilers with asynchronous validation functions.
To use a transpiler you should separately install it (or load its bundle in the browser).
@ -621,13 +620,13 @@ See [Options](#options).
|es7.regenerator|1.0|2.7|1109Kb|
|regenerator|1.0|3.2|1109Kb|
\* Relative performance in node v.7, smaller is better.
\* Relative performance in Node.js 7.x — smaller is better.
[nodent](https://github.com/MatAtBread/nodent) has several advantages:
- much smaller browser bundle than regenerator
- almost the same performance of generated code as native generators in nodejs and the latest Chrome
- much better performace than native generators in other browsers
- almost the same performance of generated code as native generators in Node.js and the latest Chrome
- much better performance than native generators in other browsers
- works in IE 9 (regenerator does not)
@ -880,13 +879,13 @@ Unless the option `validateSchema` is false, the schema will be validated agains
##### <a name="api-compileAsync"></a>.compileAsync(Object schema [, Boolean meta] [, Function callback]) -&gt; Promise
Asyncronous version of `compile` method that loads missing remote schemas using asynchronous function in `options.loadSchema`. This function returns a Promise that resolves to a validation function. An optional callback passed to `compileAsync` will be called with 2 parameters: error (or null) and validating function. The returned promise will reject (and callback if passed will be called with an error) when:
Asynchronous version of `compile` method that loads missing remote schemas using asynchronous function in `options.loadSchema`. This function returns a Promise that resolves to a validation function. An optional callback passed to `compileAsync` will be called with 2 parameters: error (or null) and validating function. The returned promise will reject (and the callback will be called with an error) when:
- missing schema can't be loaded (`loadSchema` returns the Promise that rejects).
- the schema containing missing reference is loaded, but the reference cannot be resolved.
- schema (or some referenced schema) is invalid.
- missing schema can't be loaded (`loadSchema` returns a Promise that rejects).
- a schema containing a missing reference is loaded, but the reference cannot be resolved.
- schema (or some loaded/referenced schema) is invalid.
The function compiles schema and loads the first missing schema (or meta-schema), until all missing schemas are loaded.
The function compiles schema and loads the first missing schema (or meta-schema) until all missing schemas are loaded.
You can asynchronously compile meta-schema by passing `true` as the second parameter.
@ -935,16 +934,16 @@ Validates schema. This method should be used to validate schemas rather than `va
By default this method is called automatically when the schema is added, so you rarely need to use it directly.
If schema doesn't have `$schema` property it is validated against draft 6 meta-schema (option `meta` should not be false).
If schema doesn't have `$schema` property, it is validated against draft 6 meta-schema (option `meta` should not be false).
If schema has `$schema` property then the schema with this id (that should be previously added) is used to validate passed schema.
If schema has `$schema` property, then the schema with this id (that should be previously added) is used to validate passed schema.
Errors will be available at `ajv.errors`.
##### .getSchema(String key) -&gt; Function&lt;Object data&gt;
Retrieve compiled schema previously added with `addSchema` by the key passed to `addSchema` or by its full reference (id). Returned validating function has `schema` property with the reference to the original schema.
Retrieve compiled schema previously added with `addSchema` by the key passed to `addSchema` or by its full reference (id). The returned validating function has `schema` property with the reference to the original schema.
##### .removeSchema([Object schema|String key|String ref|RegExp pattern])
@ -1087,7 +1086,7 @@ Defaults:
- _$data_: support [$data references](#data-reference). Draft 6 meta-schema that is added by default will be extended to allow them. If you want to use another meta-schema you need to use $dataMetaSchema method to add support for $data reference. See [API](#api).
- _allErrors_: check all rules collecting all errors. Default is to return after the first error.
- _verbose_: include the reference to the part of the schema (`schema` and `parentSchema`) and validated data in errors (false by default).
- _jsonPointers_: set `dataPath` propery of errors using [JSON Pointers](https://tools.ietf.org/html/rfc6901) instead of JavaScript property access notation.
- _jsonPointers_: set `dataPath` property of errors using [JSON Pointers](https://tools.ietf.org/html/rfc6901) instead of JavaScript property access notation.
- _uniqueItems_: validate `uniqueItems` keyword (true by default).
- _unicode_: calculate correct length of strings with unicode pairs (true by default). Pass `false` to use `.length` of strings that is faster, but gives "incorrect" lengths of strings with unicode pairs - each unicode pair is counted as two characters.
- _format_: formats validation mode ('fast' by default). Pass 'full' for more correct and slow validation or `false` not to validate formats at all. E.g., 25:00:00 and 2015/14/33 will be invalid time and date in 'full' mode but it will be valid in 'fast' mode.
@ -1111,7 +1110,7 @@ Defaults:
- `"fail"` - to log error and successfully compile schema but fail validation if this rule is checked.
- _extendRefs_: validation of other keywords when `$ref` is present in the schema. Option values:
- `"ignore"` (default) - when `$ref` is used other keywords are ignored (as per [JSON Reference](https://tools.ietf.org/html/draft-pbryan-zyp-json-ref-03#section-3) standard). A warning will be logged during the schema compilation.
- `"fail"` (recommended) - if other validation keywords are used together with `$ref` the exception will be thrown when the schema is compiled. This option is recomended to make sure schema has no keywords that are ignored, which can be confusing.
- `"fail"` (recommended) - if other validation keywords are used together with `$ref` the exception will be thrown when the schema is compiled. This option is recommended to make sure schema has no keywords that are ignored, which can be confusing.
- `true` - validate all keywords in the schemas with `$ref` (the default behaviour in versions before 5.0.0).
- _loadSchema_: asynchronous function that will be used to load remote schemas when `compileAsync` [method](#api-compileAsync) is used and some reference is missing (option `missingRefs` should NOT be 'fail' or 'ignore'). This function should accept remote schema uri as a parameter and return a Promise that resolves to a schema. See example in [Asynchronous compilation](#asynchronous-schema-compilation).
@ -1137,12 +1136,12 @@ Defaults:
- _async_: determines how Ajv compiles asynchronous schemas (see [Asynchronous validation](#asynchronous-validation)) to functions. Option values:
- `"*"` / `"co*"` (default) - compile to generator function ("co*" - wrapped with `co.wrap`). If generators are not supported and you don't provide `processCode` option (or `transpile` option if you use [ajv-async](https://github.com/epoberezkin/ajv-async) package), the exception will be thrown when async schema is compiled.
- `"es7"` - compile to es7 async function. Unless your platform supports them you need to provide `processCode`/`transpile` option. According to [compatibility table](http://kangax.github.io/compat-table/es7/)) async functions are supported by:
- `"es7"` - compile to es7 async function. Unless your platform supports them you need to provide `processCode` or `transpile` option. According to [compatibility table](http://kangax.github.io/compat-table/es7/)) async functions are supported by:
- Firefox 52,
- Chrome 55,
- Node.js 7 (with `--harmony-async-await`),
- MS Edge 13 (with flag).
- `undefined`/`true` - auto-detect async mode. It requires [ajv-async](https://github.com/epoberezkin/ajv-async) package. If transpile option is not passed ajv-async will choose the first of supported/installed async/transpile modes in this order:
- `undefined`/`true` - auto-detect async mode. It requires [ajv-async](https://github.com/epoberezkin/ajv-async) package. If `transpile` option is not passed, ajv-async will choose the first of supported/installed async/transpile modes in this order:
- "es7" (native async functions),
- "co*" (native generators with co.wrap),
- "es7"/"nodent",
@ -1176,14 +1175,14 @@ Defaults:
- _sourceCode_: add `sourceCode` property to validating function (for debugging; this code can be different from the result of toString call).
- _processCode_: an optional function to process generated code before it is passed to Function constructor. It can be used to either beautify (the validating function is generated without line-breaks) or to transpile code. Starting from version 5.0.0 this option replaced options:
- `beautify` that formatted the generated function using [js-beautify](https://github.com/beautify-web/js-beautify). If you want to beautify the generated code pass `require('js-beautify').js_beautify`.
- `transpile` that transpiled asynchronous validation function. You can still use `transpile` option with [ajv-async](https://github.com/epoberezkin/ajv-async) package. See [Asynchronous validation](#asynchronous-validation) for more information.
- `transpile` that transpiled asynchronous validation function. You can still use `transpile` option with [ajv-async](https://github.com/epoberezkin/ajv-async) package. See [Asynchronous validation](#asynchronous-validation) for more information.
- _cache_: an optional instance of cache to store compiled schemas using stable-stringified schema as a key. For example, set-associative cache [sacjs](https://github.com/epoberezkin/sacjs) can be used. If not passed then a simple hash is used which is good enough for the common use case (a limited number of statically defined schemas). Cache should have methods `put(key, value)`, `get(key)`, `del(key)` and `clear()`.
- _serialize_: an optional function to serialize schema to cache key. Pass `false` to use schema itself as a key (e.g., if WeakMap used as a cache). By default [json-stable-stringify](https://github.com/substack/json-stable-stringify) is used.
- _serialize_: an optional function to serialize schema to cache key. Pass `false` to use schema itself as a key (e.g., if WeakMap used as a cache). By default [fast-json-stable-stringify](https://github.com/epoberezkin/fast-json-stable-stringify) is used.
## Validation errors
In case of validation failure Ajv assigns the array of errors to `.errors` property of validation function (or to `.errors` property of Ajv instance in case `validate` or `validateSchema` methods were called). In case of [asynchronous validation](#asynchronous-validation) the returned promise is rejected with the exception of the class `Ajv.ValidationError` that has `.errors` property.
In case of validation failure, Ajv assigns the array of errors to `errors` property of validation function (or to `errors` property of Ajv instance when `validate` or `validateSchema` methods were called). In case of [asynchronous validation](#asynchronous-validation), the returned promise is rejected with exception `Ajv.ValidationError` that has `errors` property.
### Error objects
@ -1213,7 +1212,7 @@ Properties of `params` object in errors depend on the keyword that failed valida
- `property` (dependent property),
- `missingProperty` (required missing dependency - only the first one is reported currently)
- `deps` (required dependencies, comma separated list as a string),
- `depsCount` (the number of required dependedncies).
- `depsCount` (the number of required dependencies).
- `format` - property `format` (the schema of the keyword).
- `maximum`, `minimum` - properties:
- `limit` (number, the schema of the keyword),
@ -1248,7 +1247,7 @@ Properties of `params` object in errors depend on the keyword that failed valida
- [har-validator](https://github.com/ahmadnassri/har-validator) - HTTP Archive (HAR) validator
- [jsoneditor](https://github.com/josdejong/jsoneditor) - a web-based tool to view, edit, format, and validate JSON http://jsoneditoronline.org
- [JSON Schema Lint](https://github.com/nickcmaynard/jsonschemalint) - a web tool to validate JSON/YAML document against a single JSON-schema http://jsonschemalint.com
- [objection](https://github.com/vincit/objection.js) - SQL-friendly ORM for node.js
- [objection](https://github.com/vincit/objection.js) - SQL-friendly ORM for Node.js
- [table](https://github.com/gajus/table) - formats data into a string table
- [ripple-lib](https://github.com/ripple/ripple-lib) - a JavaScript API for interacting with [Ripple](https://ripple.com) in Node.js and the browser
- [restbase](https://github.com/wikimedia/restbase) - distributed storage with REST API & dispatcher for backend services built to provide a low-latency & high-throughput API for Wikipedia / Wikimedia content
@ -1259,7 +1258,7 @@ Properties of `params` object in errors depend on the keyword that failed valida
- [chai-ajv-json-schema](https://github.com/peon374/chai-ajv-json-schema) - chai plugin to us JSON-schema with expect in mocha tests
- [grunt-jsonschema-ajv](https://github.com/SignpostMarv/grunt-jsonschema-ajv) - Grunt plugin for validating files against JSON Schema
- [extract-text-webpack-plugin](https://github.com/webpack-contrib/extract-text-webpack-plugin) - extract text from bundle into a file
- [electron-builder](https://github.com/electron-userland/electron-builder) - a solution to package and build a ready for distribution Electron app
- [electron-builder](https://github.com/electron-userland/electron-builder) - a solution to package and build a ready for distribution Electron app
- [addons-linter](https://github.com/mozilla/addons-linter) - Mozilla Add-ons Linter
- [gh-pages-generator](https://github.com/epoberezkin/gh-pages-generator) - multi-page site generator converting markdown files to GitHub pages

View File

@ -381,7 +381,7 @@ function regex(str) {
var resolve = require('./resolve')
, util = require('./util')
, errorClasses = require('./error_classes')
, stableStringify = require('json-stable-stringify');
, stableStringify = require('fast-json-stable-stringify');
var validateGenerator = require('../dotjs/validate');
@ -756,7 +756,7 @@ function vars(arr, statement) {
return code;
}
},{"../dotjs/validate":35,"./error_classes":5,"./resolve":8,"./util":12,"co":40,"fast-deep-equal":41,"json-stable-stringify":43}],8:[function(require,module,exports){
},{"../dotjs/validate":35,"./error_classes":5,"./resolve":8,"./util":12,"co":40,"fast-deep-equal":41,"fast-json-stable-stringify":42}],8:[function(require,module,exports){
'use strict';
var url = require('url')
@ -1029,7 +1029,7 @@ function resolveIds(schema) {
return localRefs;
}
},{"./schema_obj":10,"./util":12,"fast-deep-equal":41,"json-schema-traverse":42,"url":51}],9:[function(require,module,exports){
},{"./schema_obj":10,"./util":12,"fast-deep-equal":41,"json-schema-traverse":43,"url":48}],9:[function(require,module,exports){
'use strict';
var ruleModules = require('./_rules')
@ -5218,6 +5218,67 @@ module.exports = function equal(a, b) {
},{}],42:[function(require,module,exports){
'use strict';
module.exports = function (data, opts) {
if (!opts) opts = {};
if (typeof opts === 'function') opts = { cmp: opts };
var cycles = (typeof opts.cycles === 'boolean') ? opts.cycles : false;
var cmp = opts.cmp && (function (f) {
return function (node) {
return function (a, b) {
var aobj = { key: a, value: node[a] };
var bobj = { key: b, value: node[b] };
return f(aobj, bobj);
};
};
})(opts.cmp);
var seen = [];
return (function stringify (node) {
if (node && node.toJSON && typeof node.toJSON === 'function') {
node = node.toJSON();
}
if (node === undefined) return;
if (typeof node == 'number') return isFinite(node) ? '' + node : 'null';
if (typeof node !== 'object') return JSON.stringify(node);
var i, out;
if (Array.isArray(node)) {
out = '[';
for (i = 0; i < node.length; i++) {
if (i) out += ',';
out += stringify(node[i]) || 'null';
}
return out + ']';
}
if (node === null) return 'null';
if (seen.indexOf(node) !== -1) {
if (cycles) return JSON.stringify('__cycle__');
throw new TypeError('Converting circular structure to JSON');
}
var seenIndex = seen.push(node) - 1;
var keys = Object.keys(node).sort(cmp && cmp(node));
out = '';
for (i = 0; i < keys.length; i++) {
var key = keys[i];
var value = stringify(node[key]);
if (!value) continue;
if (out) out += ',';
out += JSON.stringify(key) + ':' + value;
}
seen.splice(seenIndex, 1);
return '{' + out + '}';
})(data);
};
},{}],43:[function(require,module,exports){
'use strict';
var traverse = module.exports = function (schema, opts, cb) {
if (typeof opts == 'function') {
cb = opts;
@ -5298,528 +5359,7 @@ function escapeJsonPtr(str) {
return str.replace(/~/g, '~0').replace(/\//g, '~1');
}
},{}],43:[function(require,module,exports){
var json = typeof JSON !== 'undefined' ? JSON : require('jsonify');
module.exports = function (obj, opts) {
if (!opts) opts = {};
if (typeof opts === 'function') opts = { cmp: opts };
var space = opts.space || '';
if (typeof space === 'number') space = Array(space+1).join(' ');
var cycles = (typeof opts.cycles === 'boolean') ? opts.cycles : false;
var replacer = opts.replacer || function(key, value) { return value; };
var cmp = opts.cmp && (function (f) {
return function (node) {
return function (a, b) {
var aobj = { key: a, value: node[a] };
var bobj = { key: b, value: node[b] };
return f(aobj, bobj);
};
};
})(opts.cmp);
var seen = [];
return (function stringify (parent, key, node, level) {
var indent = space ? ('\n' + new Array(level + 1).join(space)) : '';
var colonSeparator = space ? ': ' : ':';
if (node && node.toJSON && typeof node.toJSON === 'function') {
node = node.toJSON();
}
node = replacer.call(parent, key, node);
if (node === undefined) {
return;
}
if (typeof node !== 'object' || node === null) {
return json.stringify(node);
}
if (isArray(node)) {
var out = [];
for (var i = 0; i < node.length; i++) {
var item = stringify(node, i, node[i], level+1) || json.stringify(null);
out.push(indent + space + item);
}
return '[' + out.join(',') + indent + ']';
}
else {
if (seen.indexOf(node) !== -1) {
if (cycles) return json.stringify('__cycle__');
throw new TypeError('Converting circular structure to JSON');
}
else seen.push(node);
var keys = objectKeys(node).sort(cmp && cmp(node));
var out = [];
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
var value = stringify(node, key, node[key], level+1);
if(!value) continue;
var keyValue = json.stringify(key)
+ colonSeparator
+ value;
;
out.push(indent + space + keyValue);
}
seen.splice(seen.indexOf(node), 1);
return '{' + out.join(',') + indent + '}';
}
})({ '': obj }, '', obj, 0);
};
var isArray = Array.isArray || function (x) {
return {}.toString.call(x) === '[object Array]';
};
var objectKeys = Object.keys || function (obj) {
var has = Object.prototype.hasOwnProperty || function () { return true };
var keys = [];
for (var key in obj) {
if (has.call(obj, key)) keys.push(key);
}
return keys;
};
},{"jsonify":44}],44:[function(require,module,exports){
exports.parse = require('./lib/parse');
exports.stringify = require('./lib/stringify');
},{"./lib/parse":45,"./lib/stringify":46}],45:[function(require,module,exports){
var at, // The index of the current character
ch, // The current character
escapee = {
'"': '"',
'\\': '\\',
'/': '/',
b: '\b',
f: '\f',
n: '\n',
r: '\r',
t: '\t'
},
text,
error = function (m) {
// Call error when something is wrong.
throw {
name: 'SyntaxError',
message: m,
at: at,
text: text
};
},
next = function (c) {
// If a c parameter is provided, verify that it matches the current character.
if (c && c !== ch) {
error("Expected '" + c + "' instead of '" + ch + "'");
}
// Get the next character. When there are no more characters,
// return the empty string.
ch = text.charAt(at);
at += 1;
return ch;
},
number = function () {
// Parse a number value.
var number,
string = '';
if (ch === '-') {
string = '-';
next('-');
}
while (ch >= '0' && ch <= '9') {
string += ch;
next();
}
if (ch === '.') {
string += '.';
while (next() && ch >= '0' && ch <= '9') {
string += ch;
}
}
if (ch === 'e' || ch === 'E') {
string += ch;
next();
if (ch === '-' || ch === '+') {
string += ch;
next();
}
while (ch >= '0' && ch <= '9') {
string += ch;
next();
}
}
number = +string;
if (!isFinite(number)) {
error("Bad number");
} else {
return number;
}
},
string = function () {
// Parse a string value.
var hex,
i,
string = '',
uffff;
// When parsing for string values, we must look for " and \ characters.
if (ch === '"') {
while (next()) {
if (ch === '"') {
next();
return string;
} else if (ch === '\\') {
next();
if (ch === 'u') {
uffff = 0;
for (i = 0; i < 4; i += 1) {
hex = parseInt(next(), 16);
if (!isFinite(hex)) {
break;
}
uffff = uffff * 16 + hex;
}
string += String.fromCharCode(uffff);
} else if (typeof escapee[ch] === 'string') {
string += escapee[ch];
} else {
break;
}
} else {
string += ch;
}
}
}
error("Bad string");
},
white = function () {
// Skip whitespace.
while (ch && ch <= ' ') {
next();
}
},
word = function () {
// true, false, or null.
switch (ch) {
case 't':
next('t');
next('r');
next('u');
next('e');
return true;
case 'f':
next('f');
next('a');
next('l');
next('s');
next('e');
return false;
case 'n':
next('n');
next('u');
next('l');
next('l');
return null;
}
error("Unexpected '" + ch + "'");
},
value, // Place holder for the value function.
array = function () {
// Parse an array value.
var array = [];
if (ch === '[') {
next('[');
white();
if (ch === ']') {
next(']');
return array; // empty array
}
while (ch) {
array.push(value());
white();
if (ch === ']') {
next(']');
return array;
}
next(',');
white();
}
}
error("Bad array");
},
object = function () {
// Parse an object value.
var key,
object = {};
if (ch === '{') {
next('{');
white();
if (ch === '}') {
next('}');
return object; // empty object
}
while (ch) {
key = string();
white();
next(':');
if (Object.hasOwnProperty.call(object, key)) {
error('Duplicate key "' + key + '"');
}
object[key] = value();
white();
if (ch === '}') {
next('}');
return object;
}
next(',');
white();
}
}
error("Bad object");
};
value = function () {
// Parse a JSON value. It could be an object, an array, a string, a number,
// or a word.
white();
switch (ch) {
case '{':
return object();
case '[':
return array();
case '"':
return string();
case '-':
return number();
default:
return ch >= '0' && ch <= '9' ? number() : word();
}
};
// Return the json_parse function. It will have access to all of the above
// functions and variables.
module.exports = function (source, reviver) {
var result;
text = source;
at = 0;
ch = ' ';
result = value();
white();
if (ch) {
error("Syntax error");
}
// If there is a reviver function, we recursively walk the new structure,
// passing each name/value pair to the reviver function for possible
// transformation, starting with a temporary root object that holds the result
// in an empty key. If there is not a reviver function, we simply return the
// result.
return typeof reviver === 'function' ? (function walk(holder, key) {
var k, v, value = holder[key];
if (value && typeof value === 'object') {
for (k in value) {
if (Object.prototype.hasOwnProperty.call(value, k)) {
v = walk(value, k);
if (v !== undefined) {
value[k] = v;
} else {
delete value[k];
}
}
}
}
return reviver.call(holder, key, value);
}({'': result}, '')) : result;
};
},{}],46:[function(require,module,exports){
var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
gap,
indent,
meta = { // table of character substitutions
'\b': '\\b',
'\t': '\\t',
'\n': '\\n',
'\f': '\\f',
'\r': '\\r',
'"' : '\\"',
'\\': '\\\\'
},
rep;
function quote(string) {
// If the string contains no control characters, no quote characters, and no
// backslash characters, then we can safely slap some quotes around it.
// Otherwise we must also replace the offending characters with safe escape
// sequences.
escapable.lastIndex = 0;
return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
var c = meta[a];
return typeof c === 'string' ? c :
'\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
}) + '"' : '"' + string + '"';
}
function str(key, holder) {
// Produce a string from holder[key].
var i, // The loop counter.
k, // The member key.
v, // The member value.
length,
mind = gap,
partial,
value = holder[key];
// If the value has a toJSON method, call it to obtain a replacement value.
if (value && typeof value === 'object' &&
typeof value.toJSON === 'function') {
value = value.toJSON(key);
}
// If we were called with a replacer function, then call the replacer to
// obtain a replacement value.
if (typeof rep === 'function') {
value = rep.call(holder, key, value);
}
// What happens next depends on the value's type.
switch (typeof value) {
case 'string':
return quote(value);
case 'number':
// JSON numbers must be finite. Encode non-finite numbers as null.
return isFinite(value) ? String(value) : 'null';
case 'boolean':
case 'null':
// If the value is a boolean or null, convert it to a string. Note:
// typeof null does not produce 'null'. The case is included here in
// the remote chance that this gets fixed someday.
return String(value);
case 'object':
if (!value) return 'null';
gap += indent;
partial = [];
// Array.isArray
if (Object.prototype.toString.apply(value) === '[object Array]') {
length = value.length;
for (i = 0; i < length; i += 1) {
partial[i] = str(i, value) || 'null';
}
// Join all of the elements together, separated with commas, and
// wrap them in brackets.
v = partial.length === 0 ? '[]' : gap ?
'[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' :
'[' + partial.join(',') + ']';
gap = mind;
return v;
}
// If the replacer is an array, use it to select the members to be
// stringified.
if (rep && typeof rep === 'object') {
length = rep.length;
for (i = 0; i < length; i += 1) {
k = rep[i];
if (typeof k === 'string') {
v = str(k, value);
if (v) {
partial.push(quote(k) + (gap ? ': ' : ':') + v);
}
}
}
}
else {
// Otherwise, iterate through all of the keys in the object.
for (k in value) {
if (Object.prototype.hasOwnProperty.call(value, k)) {
v = str(k, value);
if (v) {
partial.push(quote(k) + (gap ? ': ' : ':') + v);
}
}
}
}
// Join all of the member texts together, separated with commas,
// and wrap them in braces.
v = partial.length === 0 ? '{}' : gap ?
'{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' :
'{' + partial.join(',') + '}';
gap = mind;
return v;
}
}
module.exports = function (value, replacer, space) {
var i;
gap = '';
indent = '';
// If the space parameter is a number, make an indent string containing that
// many spaces.
if (typeof space === 'number') {
for (i = 0; i < space; i += 1) {
indent += ' ';
}
}
// If the space parameter is a string, it will be used as the indent string.
else if (typeof space === 'string') {
indent = space;
}
// If there is a replacer, it must be a function or an array.
// Otherwise, throw an error.
rep = replacer;
if (replacer && typeof replacer !== 'function'
&& (typeof replacer !== 'object' || typeof replacer.length !== 'number')) {
throw new Error('JSON.stringify');
}
// Make a fake root object containing our value under the key of ''.
// Return the result of stringifying the value.
return str('', {'': value});
};
},{}],47:[function(require,module,exports){
},{}],44:[function(require,module,exports){
(function (global){
/*! https://mths.be/punycode v1.4.1 by @mathias */
;(function(root) {
@ -6356,7 +5896,7 @@ module.exports = function (value, replacer, space) {
}(this));
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{}],48:[function(require,module,exports){
},{}],45:[function(require,module,exports){
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
@ -6442,7 +5982,7 @@ var isArray = Array.isArray || function (xs) {
return Object.prototype.toString.call(xs) === '[object Array]';
};
},{}],49:[function(require,module,exports){
},{}],46:[function(require,module,exports){
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
@ -6529,13 +6069,13 @@ var objectKeys = Object.keys || function (obj) {
return res;
};
},{}],50:[function(require,module,exports){
},{}],47:[function(require,module,exports){
'use strict';
exports.decode = exports.parse = require('./decode');
exports.encode = exports.stringify = require('./encode');
},{"./decode":48,"./encode":49}],51:[function(require,module,exports){
},{"./decode":45,"./encode":46}],48:[function(require,module,exports){
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
@ -7269,7 +6809,7 @@ Url.prototype.parseHost = function() {
if (host) this.hostname = host;
};
},{"./util":52,"punycode":47,"querystring":50}],52:[function(require,module,exports){
},{"./util":49,"punycode":44,"querystring":47}],49:[function(require,module,exports){
'use strict';
module.exports = {
@ -7294,7 +6834,7 @@ var compileSchema = require('./compile')
, resolve = require('./compile/resolve')
, Cache = require('./cache')
, SchemaObject = require('./compile/schema_obj')
, stableStringify = require('json-stable-stringify')
, stableStringify = require('fast-json-stable-stringify')
, formats = require('./compile/formats')
, rules = require('./compile/rules')
, $dataMetaSchema = require('./$data')
@ -7371,7 +6911,7 @@ function Ajv(opts) {
/**
* Validate data using schema
* Schema will be compiled and cached (using serialized JSON as key. [json-stable-stringify](https://github.com/substack/json-stable-stringify) is used to serialize.
* Schema will be compiled and cached (using serialized JSON as key. [fast-json-stable-stringify](https://github.com/epoberezkin/fast-json-stable-stringify) is used to serialize.
* @this Ajv
* @param {String|Object} schemaKeyRef key, ref or schema object
* @param {Any} data to be validated
@ -7766,5 +7306,5 @@ function getMetaSchemaOptions(self) {
return metaOpts;
}
},{"./$data":1,"./cache":2,"./compile":7,"./compile/async":4,"./compile/error_classes":5,"./compile/formats":6,"./compile/resolve":8,"./compile/rules":9,"./compile/schema_obj":10,"./compile/util":12,"./keyword":36,"./patternGroups":37,"./refs/$data.json":38,"./refs/json-schema-draft-06.json":39,"co":40,"json-stable-stringify":43}]},{},[])("ajv")
},{"./$data":1,"./cache":2,"./compile":7,"./compile/async":4,"./compile/error_classes":5,"./compile/formats":6,"./compile/resolve":8,"./compile/rules":9,"./compile/schema_obj":10,"./compile/util":12,"./keyword":36,"./patternGroups":37,"./refs/$data.json":38,"./refs/json-schema-draft-06.json":39,"co":40,"fast-json-stable-stringify":42}]},{},[])("ajv")
});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,13 +1,16 @@
declare var ajv: {
(options?: ajv.Options): ajv.Ajv;
new (options?: ajv.Options): ajv.Ajv;
ValidationError: ValidationError;
MissingRefError: MissingRefError;
$dataMetaSchema: Object;
}
declare namespace ajv {
interface Ajv {
/**
* Validate data using schema
* Schema will be compiled and cached (using serialized JSON as key, [json-stable-stringify](https://github.com/substack/json-stable-stringify) is used to serialize by default).
* Schema will be compiled and cached (using serialized JSON as key, [fast-json-stable-stringify](https://github.com/epoberezkin/fast-json-stable-stringify) is used to serialize by default).
* @param {String|Object|Boolean} schemaKeyRef key, ref or schema object
* @param {Any} data to be validated
* @return {Boolean} validation result. Errors from the last validation will be available in `ajv.errors` (and also in compiled schema: `schema.errors`).
@ -110,8 +113,13 @@ declare namespace ajv {
parentDataProperty?: string | number,
rootData?: Object | Array<any>
): boolean | Thenable<any>;
errors?: Array<ErrorObject>;
schema?: Object | boolean;
errors?: null | Array<ErrorObject>;
refs?: Object;
refVal?: Array<any>;
root?: ValidateFunction | Object;
$async?: true;
source?: Object;
}
interface Options {
@ -149,7 +157,7 @@ declare namespace ajv {
cache?: Object;
}
type FormatValidator = string | RegExp | ((data: string) => boolean);
type FormatValidator = string | RegExp | ((data: string) => boolean | Thenable<any>);
interface FormatDefinition {
validate: FormatValidator;
@ -293,4 +301,22 @@ declare namespace ajv {
}
}
declare class ValidationError extends Error {
constructor(errors: Array<ajv.ErrorObject>);
message: string;
errors: Array<ajv.ErrorObject>;
ajv: true;
validation: true;
}
declare class MissingRefError extends Error {
constructor(baseId: string, ref: string, message?: string);
static message: (baseId: string, ref: string) => string;
message: string;
missingRef: string;
missingSchema: string;
}
export = ajv;

View File

@ -4,7 +4,7 @@ var compileSchema = require('./compile')
, resolve = require('./compile/resolve')
, Cache = require('./cache')
, SchemaObject = require('./compile/schema_obj')
, stableStringify = require('json-stable-stringify')
, stableStringify = require('fast-json-stable-stringify')
, formats = require('./compile/formats')
, rules = require('./compile/rules')
, $dataMetaSchema = require('./$data')
@ -81,7 +81,7 @@ function Ajv(opts) {
/**
* Validate data using schema
* Schema will be compiled and cached (using serialized JSON as key. [json-stable-stringify](https://github.com/substack/json-stable-stringify) is used to serialize.
* Schema will be compiled and cached (using serialized JSON as key. [fast-json-stable-stringify](https://github.com/epoberezkin/fast-json-stable-stringify) is used to serialize.
* @this Ajv
* @param {String|Object} schemaKeyRef key, ref or schema object
* @param {Any} data to be validated

View File

@ -3,7 +3,7 @@
var resolve = require('./resolve')
, util = require('./util')
, errorClasses = require('./error_classes')
, stableStringify = require('json-stable-stringify');
, stableStringify = require('fast-json-stable-stringify');
var validateGenerator = require('../dotjs/validate');

View File

@ -1,8 +1,8 @@
{
"_from": "ajv@^5.2.0",
"_id": "ajv@5.2.3",
"_id": "ajv@5.3.0",
"_inBundle": false,
"_integrity": "sha1-wG9Zh3jETGsWGrr+NGa4GtGBTtI=",
"_integrity": "sha1-RBT/dKUIecII7l/cgm4ywwNUnto=",
"_location": "/eslint/ajv",
"_phantomChildren": {},
"_requested": {
@ -19,10 +19,10 @@
"/eslint",
"/eslint/table"
],
"_resolved": "https://registry.npmjs.org/ajv/-/ajv-5.2.3.tgz",
"_shasum": "c06f598778c44c6b161abafe3466b81ad1814ed2",
"_resolved": "https://registry.npmjs.org/ajv/-/ajv-5.3.0.tgz",
"_shasum": "4414ff74a50879c208ee5fdc826e32c303549eda",
"_spec": "ajv@^5.2.0",
"_where": "/Users/apapirovski/Web/nodejs/tools/eslint-tmp/node_modules/eslint",
"_where": "/Users/cjihrig/iojs/node/tools/eslint-tmp/node_modules/eslint",
"author": {
"name": "Evgeny Poberezkin"
},
@ -33,8 +33,8 @@
"dependencies": {
"co": "^4.6.0",
"fast-deep-equal": "^1.0.0",
"json-schema-traverse": "^0.3.0",
"json-stable-stringify": "^1.0.1"
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.3.0"
},
"deprecated": false,
"description": "Another JSON Schema Validator",
@ -44,14 +44,14 @@
"brfs": "^1.4.3",
"browserify": "^14.1.0",
"chai": "^4.0.1",
"coveralls": "^2.11.4",
"coveralls": "^3.0.0",
"del-cli": "^1.1.0",
"dot": "^1.0.3",
"eslint": "^4.1.0",
"gh-pages-generator": "^0.2.0",
"glob": "^7.0.0",
"if-node-version": "^1.0.0",
"js-beautify": "1.7.3",
"js-beautify": "^1.7.3",
"jshint": "^2.9.4",
"json-schema-test": "^1.3.0",
"karma": "^1.0.0",
@ -59,15 +59,15 @@
"karma-mocha": "^1.1.1",
"karma-phantomjs-launcher": "^1.0.0",
"karma-sauce-launcher": "^1.1.0",
"mocha": "^3.0.0",
"mocha": "^4.0.0",
"nodent": "^3.0.17",
"nyc": "^11.0.2",
"phantomjs-prebuilt": "^2.1.4",
"pre-commit": "^1.1.1",
"regenerator": "0.9.7",
"regenerator": "0.10.0",
"require-globify": "^1.3.0",
"typescript": "^2.0.3",
"uglify-js": "^3.0.8",
"uglify-js": "^3.1.5",
"watch": "^1.0.0"
},
"files": [
@ -127,5 +127,5 @@
},
"tonicExampleFilename": ".tonic_example.js",
"typings": "lib/ajv.d.ts",
"version": "5.2.3"
"version": "5.3.0"
}

View File

@ -21,7 +21,7 @@
"_resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.0.0.tgz",
"_shasum": "ec3e8b4e9f8064fc02c3ac9b65f1c275bda8ef92",
"_spec": "ansi-escapes@^3.0.0",
"_where": "/Users/apapirovski/Web/nodejs/tools/eslint-tmp/node_modules/eslint/node_modules/inquirer",
"_where": "/Users/cjihrig/iojs/node/tools/eslint-tmp/node_modules/eslint/node_modules/inquirer",
"author": {
"name": "Sindre Sorhus",
"email": "sindresorhus@gmail.com",

View File

@ -22,7 +22,7 @@
"_resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
"_shasum": "c3b33ab5ee360d86e0e628f0468ae7ef27d654df",
"_spec": "ansi-regex@^2.0.0",
"_where": "/Users/apapirovski/Web/nodejs/tools/eslint-tmp/node_modules/eslint/node_modules/has-ansi",
"_where": "/Users/cjihrig/iojs/node/tools/eslint-tmp/node_modules/eslint/node_modules/has-ansi",
"author": {
"name": "Sindre Sorhus",
"email": "sindresorhus@gmail.com",

View File

@ -21,7 +21,7 @@
"_resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
"_shasum": "b432dd3358b634cf75e1e4664368240533c1ddbe",
"_spec": "ansi-styles@^2.2.1",
"_where": "/Users/apapirovski/Web/nodejs/tools/eslint-tmp/node_modules/eslint/node_modules/babel-code-frame/node_modules/chalk",
"_where": "/Users/cjihrig/iojs/node/tools/eslint-tmp/node_modules/eslint/node_modules/babel-code-frame/node_modules/chalk",
"author": {
"name": "Sindre Sorhus",
"email": "sindresorhus@gmail.com",

View File

@ -21,7 +21,7 @@
"_resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz",
"_shasum": "73d83bc263f86e97f8cc4f6bae1b0e90a7d22c86",
"_spec": "argparse@^1.0.7",
"_where": "/Users/apapirovski/Web/nodejs/tools/eslint-tmp/node_modules/eslint/node_modules/js-yaml",
"_where": "/Users/cjihrig/iojs/node/tools/eslint-tmp/node_modules/eslint/node_modules/js-yaml",
"bugs": {
"url": "https://github.com/nodeca/argparse/issues"
},

View File

@ -21,7 +21,7 @@
"_resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
"_shasum": "9a34410e4f4e3da23dea375be5be70f24778ec39",
"_spec": "array-union@^1.0.1",
"_where": "/Users/apapirovski/Web/nodejs/tools/eslint-tmp/node_modules/eslint/node_modules/globby",
"_where": "/Users/cjihrig/iojs/node/tools/eslint-tmp/node_modules/eslint/node_modules/globby",
"author": {
"name": "Sindre Sorhus",
"email": "sindresorhus@gmail.com",

View File

@ -21,7 +21,7 @@
"_resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
"_shasum": "af6ac877a25cc7f74e058894753858dfdb24fdb6",
"_spec": "array-uniq@^1.0.1",
"_where": "/Users/apapirovski/Web/nodejs/tools/eslint-tmp/node_modules/eslint/node_modules/array-union",
"_where": "/Users/cjihrig/iojs/node/tools/eslint-tmp/node_modules/eslint/node_modules/array-union",
"author": {
"name": "Sindre Sorhus",
"email": "sindresorhus@gmail.com",

View File

@ -21,7 +21,7 @@
"_resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
"_shasum": "898508da2226f380df904728456849c1501a4b0d",
"_spec": "arrify@^1.0.0",
"_where": "/Users/apapirovski/Web/nodejs/tools/eslint-tmp/node_modules/eslint/node_modules/globby",
"_where": "/Users/cjihrig/iojs/node/tools/eslint-tmp/node_modules/eslint/node_modules/globby",
"author": {
"name": "Sindre Sorhus",
"email": "sindresorhus@gmail.com",

View File

@ -21,7 +21,7 @@
"_resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"_shasum": "a8115c55e4a702fe4d150abd3872822a7e09fc98",
"_spec": "chalk@^1.1.3",
"_where": "/Users/apapirovski/Web/nodejs/tools/eslint-tmp/node_modules/eslint/node_modules/babel-code-frame",
"_where": "/Users/cjihrig/iojs/node/tools/eslint-tmp/node_modules/eslint/node_modules/babel-code-frame",
"bugs": {
"url": "https://github.com/chalk/chalk/issues"
},

View File

@ -21,7 +21,7 @@
"_resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"_shasum": "6a385fb8853d952d5ff05d0e8aaf94278dc63dcf",
"_spec": "strip-ansi@^3.0.0",
"_where": "/Users/apapirovski/Web/nodejs/tools/eslint-tmp/node_modules/eslint/node_modules/babel-code-frame/node_modules/chalk",
"_where": "/Users/cjihrig/iojs/node/tools/eslint-tmp/node_modules/eslint/node_modules/babel-code-frame/node_modules/chalk",
"author": {
"name": "Sindre Sorhus",
"email": "sindresorhus@gmail.com",

View File

@ -27,7 +27,7 @@
"_resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
"_shasum": "63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b",
"_spec": "babel-code-frame@^6.22.0",
"_where": "/Users/apapirovski/Web/nodejs/tools/eslint-tmp/node_modules/eslint",
"_where": "/Users/cjihrig/iojs/node/tools/eslint-tmp/node_modules/eslint",
"author": {
"name": "Sebastian McKenzie",
"email": "sebmck@gmail.com"

View File

@ -21,7 +21,7 @@
"_resolved": "https://registry.npmjs.org/bail/-/bail-1.0.2.tgz",
"_shasum": "f7d6c1731630a9f9f0d4d35ed1f962e2074a1764",
"_spec": "bail@^1.0.0",
"_where": "/Users/apapirovski/Web/nodejs/tools/eslint-tmp/node_modules/eslint/node_modules/unified",
"_where": "/Users/cjihrig/iojs/node/tools/eslint-tmp/node_modules/eslint/node_modules/unified",
"author": {
"name": "Titus Wormer",
"email": "tituswormer@gmail.com",

View File

@ -21,7 +21,7 @@
"_resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
"_shasum": "89b4d199ab2bee49de164ea02b89ce462d71b767",
"_spec": "balanced-match@^1.0.0",
"_where": "/Users/apapirovski/Web/nodejs/tools/eslint-tmp/node_modules/eslint/node_modules/brace-expansion",
"_where": "/Users/cjihrig/iojs/node/tools/eslint-tmp/node_modules/eslint/node_modules/brace-expansion",
"author": {
"name": "Julian Gruber",
"email": "mail@juliangruber.com",

View File

@ -21,7 +21,7 @@
"_resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz",
"_shasum": "c07b211c7c952ec1f8efd51a77ef0d1d3990a292",
"_spec": "brace-expansion@^1.1.7",
"_where": "/Users/apapirovski/Web/nodejs/tools/eslint-tmp/node_modules/eslint/node_modules/minimatch",
"_where": "/Users/cjihrig/iojs/node/tools/eslint-tmp/node_modules/eslint/node_modules/minimatch",
"author": {
"name": "Julian Gruber",
"email": "mail@juliangruber.com",

View File

@ -21,7 +21,7 @@
"_resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz",
"_shasum": "94085ef63581ecd3daa92444a8fe94e82577751f",
"_spec": "caller-path@^0.1.0",
"_where": "/Users/apapirovski/Web/nodejs/tools/eslint-tmp/node_modules/eslint/node_modules/require-uncached",
"_where": "/Users/cjihrig/iojs/node/tools/eslint-tmp/node_modules/eslint/node_modules/require-uncached",
"author": {
"name": "Sindre Sorhus",
"email": "sindresorhus@gmail.com",

View File

@ -21,7 +21,7 @@
"_resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz",
"_shasum": "afab96262910a7f33c19a5775825c69f34e350ca",
"_spec": "callsites@^0.2.0",
"_where": "/Users/apapirovski/Web/nodejs/tools/eslint-tmp/node_modules/eslint/node_modules/caller-path",
"_where": "/Users/cjihrig/iojs/node/tools/eslint-tmp/node_modules/eslint/node_modules/caller-path",
"author": {
"name": "Sindre Sorhus",
"email": "sindresorhus@gmail.com",

View File

@ -58,11 +58,17 @@ for (const key of Object.keys(ansiStyles)) {
styles[key] = {
get() {
const codes = ansiStyles[key];
return build.call(this, this._styles ? this._styles.concat(codes) : [codes], key);
return build.call(this, this._styles ? this._styles.concat(codes) : [codes], this._empty, key);
}
};
}
styles.visible = {
get() {
return build.call(this, this._styles || [], true, 'visible');
}
};
ansiStyles.color.closeRe = new RegExp(escapeStringRegexp(ansiStyles.color.close), 'g');
for (const model of Object.keys(ansiStyles.color.ansi)) {
if (skipModels.has(model)) {
@ -79,7 +85,7 @@ for (const model of Object.keys(ansiStyles.color.ansi)) {
close: ansiStyles.color.close,
closeRe: ansiStyles.color.closeRe
};
return build.call(this, this._styles ? this._styles.concat(codes) : [codes], model);
return build.call(this, this._styles ? this._styles.concat(codes) : [codes], this._empty, model);
};
}
};
@ -102,7 +108,7 @@ for (const model of Object.keys(ansiStyles.bgColor.ansi)) {
close: ansiStyles.bgColor.close,
closeRe: ansiStyles.bgColor.closeRe
};
return build.call(this, this._styles ? this._styles.concat(codes) : [codes], model);
return build.call(this, this._styles ? this._styles.concat(codes) : [codes], this._empty, model);
};
}
};
@ -110,12 +116,13 @@ for (const model of Object.keys(ansiStyles.bgColor.ansi)) {
const proto = Object.defineProperties(() => {}, styles);
function build(_styles, key) {
function build(_styles, _empty, key) {
const builder = function () {
return applyStyle.apply(builder, arguments);
};
builder._styles = _styles;
builder._empty = _empty;
const self = this;
@ -167,7 +174,7 @@ function applyStyle() {
}
if (!this.enabled || this.level <= 0 || !str) {
return str;
return this._empty ? '' : str;
}
// Turns out that on Windows dimmed gray text becomes invisible in cmd.exe,
@ -218,3 +225,4 @@ Object.defineProperties(Chalk.prototype, styles);
module.exports = Chalk(); // eslint-disable-line new-cap
module.exports.supportsColor = supportsColor;
module.exports.default = module.exports; // For TypeScript

View File

@ -21,7 +21,7 @@
"_resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz",
"_shasum": "c159b8d5be0f9e5a6f346dab94f16ce022161b88",
"_spec": "ansi-styles@^3.1.0",
"_where": "/Users/apapirovski/Web/nodejs/tools/eslint-tmp/node_modules/eslint/node_modules/chalk",
"_where": "/Users/cjihrig/iojs/node/tools/eslint-tmp/node_modules/eslint/node_modules/chalk",
"author": {
"name": "Sindre Sorhus",
"email": "sindresorhus@gmail.com",

View File

@ -93,7 +93,7 @@ let supportLevel = (() => {
return 2;
}
if (/^screen|^xterm|^vt100|color|ansi|cygwin|linux/i.test(env.TERM)) {
if (/^screen|^xterm|^vt100|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) {
return 1;
}

View File

@ -1,8 +1,8 @@
{
"_from": "supports-color@^4.0.0",
"_id": "supports-color@4.4.0",
"_id": "supports-color@4.5.0",
"_inBundle": false,
"_integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==",
"_integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=",
"_location": "/eslint/chalk/supports-color",
"_phantomChildren": {},
"_requested": {
@ -18,10 +18,10 @@
"_requiredBy": [
"/eslint/chalk"
],
"_resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz",
"_shasum": "883f7ddabc165142b2a61427f3352ded195d1a3e",
"_resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz",
"_shasum": "be7a0de484dec5c5cddf8b3d59125044912f635b",
"_spec": "supports-color@^4.0.0",
"_where": "/Users/apapirovski/Web/nodejs/tools/eslint-tmp/node_modules/eslint/node_modules/chalk",
"_where": "/Users/cjihrig/iojs/node/tools/eslint-tmp/node_modules/eslint/node_modules/chalk",
"author": {
"name": "Sindre Sorhus",
"email": "sindresorhus@gmail.com",
@ -81,5 +81,5 @@
"scripts": {
"test": "xo && ava"
},
"version": "4.4.0"
"version": "4.5.0"
}

View File

@ -1,8 +1,8 @@
{
"_from": "chalk@^2.1.0",
"_id": "chalk@2.1.0",
"_id": "chalk@2.3.0",
"_inBundle": false,
"_integrity": "sha512-LUHGS/dge4ujbXMJrnihYMcL4AoOweGnw9Tp3kQuqy1Kx5c1qKjqvMJZ6nVJPMWJtKCTN72ZogH3oeSO9g9rXQ==",
"_integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==",
"_location": "/eslint/chalk",
"_phantomChildren": {
"color-convert": "1.9.0",
@ -23,10 +23,10 @@
"/eslint/inquirer",
"/eslint/table"
],
"_resolved": "https://registry.npmjs.org/chalk/-/chalk-2.1.0.tgz",
"_shasum": "ac5becf14fa21b99c6c92ca7a7d7cfd5b17e743e",
"_resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz",
"_shasum": "b5ea48efc9c1793dccc9b4767c93914d3f2d52ba",
"_spec": "chalk@^2.1.0",
"_where": "/Users/apapirovski/Web/nodejs/tools/eslint-tmp/node_modules/eslint",
"_where": "/Users/cjihrig/iojs/node/tools/eslint-tmp/node_modules/eslint",
"bugs": {
"url": "https://github.com/chalk/chalk/issues"
},
@ -40,12 +40,13 @@
"description": "Terminal string styling done right",
"devDependencies": {
"ava": "*",
"coveralls": "^2.11.2",
"execa": "^0.7.0",
"coveralls": "^3.0.0",
"execa": "^0.8.0",
"import-fresh": "^2.0.0",
"matcha": "^0.7.0",
"nyc": "^11.0.2",
"resolve-from": "^3.0.0",
"resolve-from": "^4.0.0",
"typescript": "^2.5.3",
"xo": "*"
},
"engines": {
@ -53,7 +54,8 @@
},
"files": [
"index.js",
"templates.js"
"templates.js",
"types/index.d.ts"
],
"homepage": "https://github.com/chalk/chalk#readme",
"keywords": [
@ -88,9 +90,10 @@
"scripts": {
"bench": "matcha benchmark.js",
"coveralls": "nyc report --reporter=text-lcov | coveralls",
"test": "xo && nyc ava"
"test": "xo && tsc --project types && nyc ava"
},
"version": "2.1.0",
"types": "types/index.d.ts",
"version": "2.3.0",
"xo": {
"envs": [
"node",

View File

@ -9,7 +9,7 @@
> Terminal string styling done right
[![Build Status](https://travis-ci.org/chalk/chalk.svg?branch=master)](https://travis-ci.org/chalk/chalk) [![Coverage Status](https://coveralls.io/repos/github/chalk/chalk/badge.svg?branch=master)](https://coveralls.io/github/chalk/chalk?branch=master) [![](https://img.shields.io/badge/unicorn-approved-ff69b4.svg)](https://www.youtube.com/watch?v=9auOCbH5Ns4) [![XO code style](https://img.shields.io/badge/code_style-XO-5ed9c7.svg)](https://github.com/sindresorhus/xo)
[![Build Status](https://travis-ci.org/chalk/chalk.svg?branch=master)](https://travis-ci.org/chalk/chalk) [![Coverage Status](https://coveralls.io/repos/github/chalk/chalk/badge.svg?branch=master)](https://coveralls.io/github/chalk/chalk?branch=master) [![](https://img.shields.io/badge/unicorn-approved-ff69b4.svg)](https://www.youtube.com/watch?v=9auOCbH5Ns4) [![XO code style](https://img.shields.io/badge/code_style-XO-5ed9c7.svg)](https://github.com/sindresorhus/xo) [![Mentioned in Awesome Node.js](https://awesome.re/mentioned-badge.svg)](https://github.com/sindresorhus/awesome-nodejs)
### [See what's new in Chalk 2](https://github.com/chalk/chalk/releases/tag/v2.0.0)
@ -170,6 +170,7 @@ Explicit 256/Truecolor mode can be enabled using the `--color=256` and `--color=
- `inverse`
- `hidden`
- `strikethrough` *(Not widely supported)*
- `visible` (Text is emitted only if enabled)
### Colors
@ -286,6 +287,7 @@ If you're on Windows, do yourself a favor and use [`cmder`](http://cmder.net/) i
- [ansi-styles](https://github.com/chalk/ansi-styles) - ANSI escape codes for styling strings in the terminal
- [supports-color](https://github.com/chalk/supports-color) - Detect whether a terminal supports color
- [strip-ansi](https://github.com/chalk/strip-ansi) - Strip ANSI escape codes
- [strip-ansi-stream](https://github.com/chalk/strip-ansi-stream) - Strip ANSI escape codes from a stream
- [has-ansi](https://github.com/chalk/has-ansi) - Check if a string has ANSI escape codes
- [ansi-regex](https://github.com/chalk/ansi-regex) - Regular expression for matching ANSI escape codes
- [wrap-ansi](https://github.com/chalk/wrap-ansi) - Wordwrap a string with ANSI escape codes
@ -293,6 +295,7 @@ If you're on Windows, do yourself a favor and use [`cmder`](http://cmder.net/) i
- [color-convert](https://github.com/qix-/color-convert) - Converts colors between different models
- [chalk-animation](https://github.com/bokub/chalk-animation) - Animate strings in the terminal
- [gradient-string](https://github.com/bokub/gradient-string) - Apply color gradients to strings
- [chalk-pipe](https://github.com/LitoMore/chalk-pipe) - Create chalk style schemes with simpler style strings
## Maintainers

View File

@ -1,28 +1,28 @@
'use strict';
const TEMPLATE_REGEX = /(?:\\(u[a-f0-9]{4}|x[a-f0-9]{2}|.))|(?:\{(~)?(\w+(?:\([^)]*\))?(?:\.\w+(?:\([^)]*\))?)*)(?:[ \t]|(?=\r?\n)))|(\})|((?:.|[\r\n\f])+?)/gi;
const TEMPLATE_REGEX = /(?:\\(u[a-f\d]{4}|x[a-f\d]{2}|.))|(?:\{(~)?(\w+(?:\([^)]*\))?(?:\.\w+(?:\([^)]*\))?)*)(?:[ \t]|(?=\r?\n)))|(\})|((?:.|[\r\n\f])+?)/gi;
const STYLE_REGEX = /(?:^|\.)(\w+)(?:\(([^)]*)\))?/g;
const STRING_REGEX = /^(['"])((?:\\.|(?!\1)[^\\])*)\1$/;
const ESCAPE_REGEX = /\\(u[0-9a-f]{4}|x[0-9a-f]{2}|.)|([^\\])/gi;
const ESCAPE_REGEX = /\\(u[a-f\d]{4}|x[a-f\d]{2}|.)|([^\\])/gi;
const ESCAPES = {
n: '\n',
r: '\r',
t: '\t',
b: '\b',
f: '\f',
v: '\v',
0: '\0',
'\\': '\\',
e: '\u001b',
a: '\u0007'
};
const ESCAPES = new Map([
['n', '\n'],
['r', '\r'],
['t', '\t'],
['b', '\b'],
['f', '\f'],
['v', '\v'],
['0', '\0'],
['\\', '\\'],
['e', '\u001B'],
['a', '\u0007']
]);
function unescape(c) {
if ((c[0] === 'u' && c.length === 5) || (c[0] === 'x' && c.length === 3)) {
return String.fromCharCode(parseInt(c.slice(1), 16));
}
return ESCAPES[c] || c;
return ESCAPES.get(c) || c;
}
function parseArguments(name, args) {

97
tools/eslint/node_modules/chalk/types/index.d.ts generated vendored Normal file
View File

@ -0,0 +1,97 @@
// Type definitions for Chalk
// Definitions by: Thomas Sauer <https://github.com/t-sauer>
export const enum Level {
None = 0,
Basic = 1,
Ansi256 = 2,
TrueColor = 3
}
export interface ChalkOptions {
enabled?: boolean;
level?: Level;
}
export interface ChalkConstructor {
new (options?: ChalkOptions): Chalk;
(options?: ChalkOptions): Chalk;
}
export interface ColorSupport {
level: Level;
hasBasic: boolean;
has256: boolean;
has16m: boolean;
}
export interface Chalk {
(...text: string[]): string;
(text: TemplateStringsArray, ...placeholders: string[]): string;
constructor: ChalkConstructor;
enabled: boolean;
level: Level;
rgb(r: number, g: number, b: number): this;
hsl(h: number, s: number, l: number): this;
hsv(h: number, s: number, v: number): this;
hwb(h: number, w: number, b: number): this;
bgHex(color: string): this;
bgKeyword(color: string): this;
bgRgb(r: number, g: number, b: number): this;
bgHsl(h: number, s: number, l: number): this;
bgHsv(h: number, s: number, v: number): this;
bgHwb(h: number, w: number, b: number): this;
hex(color: string): this;
keyword(color: string): this;
readonly reset: this;
readonly bold: this;
readonly dim: this;
readonly italic: this;
readonly underline: this;
readonly inverse: this;
readonly hidden: this;
readonly strikethrough: this;
readonly visible: this;
readonly black: this;
readonly red: this;
readonly green: this;
readonly yellow: this;
readonly blue: this;
readonly magenta: this;
readonly cyan: this;
readonly white: this;
readonly gray: this;
readonly grey: this;
readonly blackBright: this;
readonly redBright: this;
readonly greenBright: this;
readonly yellowBright: this;
readonly blueBright: this;
readonly magentaBright: this;
readonly cyanBright: this;
readonly whiteBright: this;
readonly bgBlack: this;
readonly bgRed: this;
readonly bgGreen: this;
readonly bgYellow: this;
readonly bgBlue: this;
readonly bgMagenta: this;
readonly bgCyan: this;
readonly bgWhite: this;
readonly bgBlackBright: this;
readonly bgRedBright: this;
readonly bgGreenBright: this;
readonly bgYellowBright: this;
readonly bgBlueBright: this;
readonly bgMagentaBright: this;
readonly bgCyanBright: this;
readonly bgWhiteBright: this;
}
declare const chalk: Chalk & { supportsColor: ColorSupport };
export default chalk

View File

@ -21,7 +21,7 @@
"_resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.1.tgz",
"_shasum": "f40779df1a101872bb510a3d295e1fccf147202f",
"_spec": "character-entities-legacy@^1.0.0",
"_where": "/Users/apapirovski/Web/nodejs/tools/eslint-tmp/node_modules/eslint/node_modules/parse-entities",
"_where": "/Users/cjihrig/iojs/node/tools/eslint-tmp/node_modules/eslint/node_modules/parse-entities",
"author": {
"name": "Titus Wormer",
"email": "tituswormer@gmail.com",

View File

@ -21,7 +21,7 @@
"_resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.1.tgz",
"_shasum": "f76871be5ef66ddb7f8f8e3478ecc374c27d6dca",
"_spec": "character-entities@^1.0.0",
"_where": "/Users/apapirovski/Web/nodejs/tools/eslint-tmp/node_modules/eslint/node_modules/parse-entities",
"_where": "/Users/cjihrig/iojs/node/tools/eslint-tmp/node_modules/eslint/node_modules/parse-entities",
"author": {
"name": "Titus Wormer",
"email": "tituswormer@gmail.com",

View File

@ -21,7 +21,7 @@
"_resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.1.tgz",
"_shasum": "942835f750e4ec61a308e60c2ef8cc1011202efc",
"_spec": "character-reference-invalid@^1.0.0",
"_where": "/Users/apapirovski/Web/nodejs/tools/eslint-tmp/node_modules/eslint/node_modules/parse-entities",
"_where": "/Users/cjihrig/iojs/node/tools/eslint-tmp/node_modules/eslint/node_modules/parse-entities",
"author": {
"name": "Titus Wormer",
"email": "tituswormer@gmail.com",

View File

@ -21,7 +21,7 @@
"_resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz",
"_shasum": "815c99ea84f6809529d2f45791bdf82711352d66",
"_spec": "circular-json@^0.3.1",
"_where": "/Users/apapirovski/Web/nodejs/tools/eslint-tmp/node_modules/eslint/node_modules/flat-cache",
"_where": "/Users/cjihrig/iojs/node/tools/eslint-tmp/node_modules/eslint/node_modules/flat-cache",
"author": {
"name": "Andrea Giammarchi",
"url": "http://webreflection.blogspot.com/"

View File

@ -21,7 +21,7 @@
"_resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
"_shasum": "b35dac376479facc3e94747d41d0d0f5238ffcb5",
"_spec": "cli-cursor@^2.1.0",
"_where": "/Users/apapirovski/Web/nodejs/tools/eslint-tmp/node_modules/eslint/node_modules/inquirer",
"_where": "/Users/cjihrig/iojs/node/tools/eslint-tmp/node_modules/eslint/node_modules/inquirer",
"author": {
"name": "Sindre Sorhus",
"email": "sindresorhus@gmail.com",

Some files were not shown because too many files have changed in this diff Show More