build: update npm install command to use --legacy-peer-deps
This change modifies the build scripts to use --legacy-peer-deps flag when installing npm dependencies to resolve potential peer dependency conflicts during the build process. The change is applied to both bash (build.sh) and batch (build.bat) build scripts.
This commit is contained in:
+355
-355
@@ -9,6 +9,11 @@
|
||||
// Requirements
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* Note: This can be removed in ESLint v9 because structuredClone is available globally
|
||||
* starting in Node.js v17.
|
||||
*/
|
||||
const structuredClone = require("@ungap/structured-clone").default;
|
||||
const { normalizeSeverityToNumber } = require("../shared/severity");
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -28,12 +33,15 @@ const { normalizeSeverityToNumber } = require("../shared/severity");
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
const ruleSeverities = new Map([
|
||||
[0, 0],
|
||||
["off", 0],
|
||||
[1, 1],
|
||||
["warn", 1],
|
||||
[2, 2],
|
||||
["error", 2],
|
||||
[0, 0], ["off", 0],
|
||||
[1, 1], ["warn", 1],
|
||||
[2, 2], ["error", 2]
|
||||
]);
|
||||
|
||||
const globalVariablesValues = new Set([
|
||||
true, "true", "writable", "writeable",
|
||||
false, "false", "readonly", "readable", null,
|
||||
"off"
|
||||
]);
|
||||
|
||||
/**
|
||||
@@ -42,7 +50,7 @@ const ruleSeverities = new Map([
|
||||
* @returns {boolean} `true` if the value is a non-null object.
|
||||
*/
|
||||
function isNonNullObject(value) {
|
||||
return typeof value === "object" && value !== null;
|
||||
return typeof value === "object" && value !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -51,7 +59,7 @@ function isNonNullObject(value) {
|
||||
* @returns {boolean} `true` if the value is a non-null non-array object.
|
||||
*/
|
||||
function isNonArrayObject(value) {
|
||||
return isNonNullObject(value) && !Array.isArray(value);
|
||||
return isNonNullObject(value) && !Array.isArray(value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -60,7 +68,7 @@ function isNonArrayObject(value) {
|
||||
* @returns {boolean} `true` if the value is undefined.
|
||||
*/
|
||||
function isUndefined(value) {
|
||||
return typeof value === "undefined";
|
||||
return typeof value === "undefined";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -71,56 +79,57 @@ function isUndefined(value) {
|
||||
* @returns {Object} An object with properties from both first and second.
|
||||
*/
|
||||
function deepMerge(first, second, mergeMap = new Map()) {
|
||||
let secondMergeMap = mergeMap.get(first);
|
||||
|
||||
if (secondMergeMap) {
|
||||
const result = secondMergeMap.get(second);
|
||||
let secondMergeMap = mergeMap.get(first);
|
||||
|
||||
if (result) {
|
||||
// If this combination of first and second arguments has been already visited, return the previously created result.
|
||||
return result;
|
||||
}
|
||||
} else {
|
||||
secondMergeMap = new Map();
|
||||
mergeMap.set(first, secondMergeMap);
|
||||
}
|
||||
if (secondMergeMap) {
|
||||
const result = secondMergeMap.get(second);
|
||||
|
||||
/*
|
||||
* First create a result object where properties from the second object
|
||||
* overwrite properties from the first. This sets up a baseline to use
|
||||
* later rather than needing to inspect and change every property
|
||||
* individually.
|
||||
*/
|
||||
const result = {
|
||||
...first,
|
||||
...second,
|
||||
};
|
||||
if (result) {
|
||||
|
||||
delete result.__proto__; // eslint-disable-line no-proto -- don't merge own property "__proto__"
|
||||
// If this combination of first and second arguments has been already visited, return the previously created result.
|
||||
return result;
|
||||
}
|
||||
} else {
|
||||
secondMergeMap = new Map();
|
||||
mergeMap.set(first, secondMergeMap);
|
||||
}
|
||||
|
||||
// Store the pending result for this combination of first and second arguments.
|
||||
secondMergeMap.set(second, result);
|
||||
/*
|
||||
* First create a result object where properties from the second object
|
||||
* overwrite properties from the first. This sets up a baseline to use
|
||||
* later rather than needing to inspect and change every property
|
||||
* individually.
|
||||
*/
|
||||
const result = {
|
||||
...first,
|
||||
...second
|
||||
};
|
||||
|
||||
for (const key of Object.keys(second)) {
|
||||
// avoid hairy edge case
|
||||
if (
|
||||
key === "__proto__" ||
|
||||
!Object.prototype.propertyIsEnumerable.call(first, key)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
delete result.__proto__; // eslint-disable-line no-proto -- don't merge own property "__proto__"
|
||||
|
||||
const firstValue = first[key];
|
||||
const secondValue = second[key];
|
||||
// Store the pending result for this combination of first and second arguments.
|
||||
secondMergeMap.set(second, result);
|
||||
|
||||
if (isNonArrayObject(firstValue) && isNonArrayObject(secondValue)) {
|
||||
result[key] = deepMerge(firstValue, secondValue, mergeMap);
|
||||
} else if (isUndefined(secondValue)) {
|
||||
result[key] = firstValue;
|
||||
}
|
||||
}
|
||||
for (const key of Object.keys(second)) {
|
||||
|
||||
// avoid hairy edge case
|
||||
if (key === "__proto__" || !Object.prototype.propertyIsEnumerable.call(first, key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const firstValue = first[key];
|
||||
const secondValue = second[key];
|
||||
|
||||
if (isNonArrayObject(firstValue) && isNonArrayObject(secondValue)) {
|
||||
result[key] = deepMerge(firstValue, secondValue, mergeMap);
|
||||
} else if (isUndefined(secondValue)) {
|
||||
result[key] = firstValue;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -130,27 +139,13 @@ function deepMerge(first, second, mergeMap = new Map()) {
|
||||
* @returns {Array} An array of rule options.
|
||||
*/
|
||||
function normalizeRuleOptions(ruleOptions) {
|
||||
const finalOptions = Array.isArray(ruleOptions)
|
||||
? ruleOptions.slice(0)
|
||||
: [ruleOptions];
|
||||
|
||||
finalOptions[0] = ruleSeverities.get(finalOptions[0]);
|
||||
return structuredClone(finalOptions);
|
||||
}
|
||||
const finalOptions = Array.isArray(ruleOptions)
|
||||
? ruleOptions.slice(0)
|
||||
: [ruleOptions];
|
||||
|
||||
/**
|
||||
* Determines if an object has any methods.
|
||||
* @param {Object} object The object to check.
|
||||
* @returns {boolean} `true` if the object has any methods.
|
||||
*/
|
||||
function hasMethod(object) {
|
||||
for (const key of Object.keys(object)) {
|
||||
if (typeof object[key] === "function") {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
finalOptions[0] = ruleSeverities.get(finalOptions[0]);
|
||||
return structuredClone(finalOptions);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -161,17 +156,16 @@ function hasMethod(object) {
|
||||
* The error type when a rule's options are configured with an invalid type.
|
||||
*/
|
||||
class InvalidRuleOptionsError extends Error {
|
||||
/**
|
||||
* @param {string} ruleId Rule name being configured.
|
||||
* @param {any} value The invalid value.
|
||||
*/
|
||||
constructor(ruleId, value) {
|
||||
super(
|
||||
`Key "${ruleId}": Expected severity of "off", 0, "warn", 1, "error", or 2.`,
|
||||
);
|
||||
this.messageTemplate = "invalid-rule-options";
|
||||
this.messageData = { ruleId, value };
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} ruleId Rule name being configured.
|
||||
* @param {any} value The invalid value.
|
||||
*/
|
||||
constructor(ruleId, value) {
|
||||
super(`Key "${ruleId}": Expected severity of "off", 0, "warn", 1, "error", or 2.`);
|
||||
this.messageTemplate = "invalid-rule-options";
|
||||
this.messageData = { ruleId, value };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -182,30 +176,25 @@ class InvalidRuleOptionsError extends Error {
|
||||
* @throws {InvalidRuleOptionsError} If the value isn't a valid rule options.
|
||||
*/
|
||||
function assertIsRuleOptions(ruleId, value) {
|
||||
if (
|
||||
typeof value !== "string" &&
|
||||
typeof value !== "number" &&
|
||||
!Array.isArray(value)
|
||||
) {
|
||||
throw new InvalidRuleOptionsError(ruleId, value);
|
||||
}
|
||||
if (typeof value !== "string" && typeof value !== "number" && !Array.isArray(value)) {
|
||||
throw new InvalidRuleOptionsError(ruleId, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The error type when a rule's severity is invalid.
|
||||
*/
|
||||
class InvalidRuleSeverityError extends Error {
|
||||
/**
|
||||
* @param {string} ruleId Rule name being configured.
|
||||
* @param {any} value The invalid value.
|
||||
*/
|
||||
constructor(ruleId, value) {
|
||||
super(
|
||||
`Key "${ruleId}": Expected severity of "off", 0, "warn", 1, "error", or 2.`,
|
||||
);
|
||||
this.messageTemplate = "invalid-rule-severity";
|
||||
this.messageData = { ruleId, value };
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} ruleId Rule name being configured.
|
||||
* @param {any} value The invalid value.
|
||||
*/
|
||||
constructor(ruleId, value) {
|
||||
super(`Key "${ruleId}": Expected severity of "off", 0, "warn", 1, "error", or 2.`);
|
||||
this.messageTemplate = "invalid-rule-severity";
|
||||
this.messageData = { ruleId, value };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -216,11 +205,11 @@ class InvalidRuleSeverityError extends Error {
|
||||
* @throws {InvalidRuleSeverityError} If the value isn't a valid rule severity.
|
||||
*/
|
||||
function assertIsRuleSeverity(ruleId, value) {
|
||||
const severity = ruleSeverities.get(value);
|
||||
const severity = ruleSeverities.get(value);
|
||||
|
||||
if (typeof severity === "undefined") {
|
||||
throw new InvalidRuleSeverityError(ruleId, value);
|
||||
}
|
||||
if (typeof severity === "undefined") {
|
||||
throw new InvalidRuleSeverityError(ruleId, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -230,11 +219,9 @@ function assertIsRuleSeverity(ruleId, value) {
|
||||
* @throws {TypeError} If the string isn't in the correct format.
|
||||
*/
|
||||
function assertIsPluginMemberName(value) {
|
||||
if (!/[\w\-@$]+(?:\/[\w\-$]+)+$/iu.test(value)) {
|
||||
throw new TypeError(
|
||||
`Expected string in the form "pluginName/objectName" but found "${value}".`,
|
||||
);
|
||||
}
|
||||
if (!/[@a-z0-9-_$]+(?:\/(?:[a-z0-9-_$]+))+$/iu.test(value)) {
|
||||
throw new TypeError(`Expected string in the form "pluginName/objectName" but found "${value}".`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -244,98 +231,79 @@ function assertIsPluginMemberName(value) {
|
||||
* @throws {TypeError} If the value isn't an object.
|
||||
*/
|
||||
function assertIsObject(value) {
|
||||
if (!isNonNullObject(value)) {
|
||||
throw new TypeError("Expected an object.");
|
||||
}
|
||||
if (!isNonNullObject(value)) {
|
||||
throw new TypeError("Expected an object.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The error type when there's an eslintrc-style options in a flat config.
|
||||
*/
|
||||
class IncompatibleKeyError extends Error {
|
||||
/**
|
||||
* @param {string} key The invalid key.
|
||||
*/
|
||||
constructor(key) {
|
||||
super(
|
||||
"This appears to be in eslintrc format rather than flat config format.",
|
||||
);
|
||||
this.messageTemplate = "eslintrc-incompat";
|
||||
this.messageData = { key };
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} key The invalid key.
|
||||
*/
|
||||
constructor(key) {
|
||||
super("This appears to be in eslintrc format rather than flat config format.");
|
||||
this.messageTemplate = "eslintrc-incompat";
|
||||
this.messageData = { key };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The error type when there's an eslintrc-style plugins array found.
|
||||
*/
|
||||
class IncompatiblePluginsError extends Error {
|
||||
/**
|
||||
* Creates a new instance.
|
||||
* @param {Array<string>} plugins The plugins array.
|
||||
*/
|
||||
constructor(plugins) {
|
||||
super(
|
||||
"This appears to be in eslintrc format (array of strings) rather than flat config format (object).",
|
||||
);
|
||||
this.messageTemplate = "eslintrc-plugins";
|
||||
this.messageData = { plugins };
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
* @param {Array<string>} plugins The plugins array.
|
||||
*/
|
||||
constructor(plugins) {
|
||||
super("This appears to be in eslintrc format (array of strings) rather than flat config format (object).");
|
||||
this.messageTemplate = "eslintrc-plugins";
|
||||
this.messageData = { plugins };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Low-Level Schemas
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/** @type {ObjectPropertySchema} */
|
||||
const booleanSchema = {
|
||||
merge: "replace",
|
||||
validate: "boolean",
|
||||
merge: "replace",
|
||||
validate: "boolean"
|
||||
};
|
||||
|
||||
const ALLOWED_SEVERITIES = new Set(["error", "warn", "off", 2, 1, 0]);
|
||||
|
||||
/** @type {ObjectPropertySchema} */
|
||||
const disableDirectiveSeveritySchema = {
|
||||
merge(first, second) {
|
||||
const value = second === void 0 ? first : second;
|
||||
merge(first, second) {
|
||||
const value = second === void 0 ? first : second;
|
||||
|
||||
if (typeof value === "boolean") {
|
||||
return value ? "warn" : "off";
|
||||
}
|
||||
if (typeof value === "boolean") {
|
||||
return value ? "warn" : "off";
|
||||
}
|
||||
|
||||
return normalizeSeverityToNumber(value);
|
||||
},
|
||||
validate(value) {
|
||||
if (!(ALLOWED_SEVERITIES.has(value) || typeof value === "boolean")) {
|
||||
throw new TypeError(
|
||||
'Expected one of: "error", "warn", "off", 0, 1, 2, or a boolean.',
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
/** @type {ObjectPropertySchema} */
|
||||
const unusedInlineConfigsSeveritySchema = {
|
||||
merge(first, second) {
|
||||
const value = second === void 0 ? first : second;
|
||||
|
||||
return normalizeSeverityToNumber(value);
|
||||
},
|
||||
validate(value) {
|
||||
if (!ALLOWED_SEVERITIES.has(value)) {
|
||||
throw new TypeError(
|
||||
'Expected one of: "error", "warn", "off", 0, 1, or 2.',
|
||||
);
|
||||
}
|
||||
},
|
||||
return normalizeSeverityToNumber(value);
|
||||
},
|
||||
validate(value) {
|
||||
if (!(ALLOWED_SEVERITIES.has(value) || typeof value === "boolean")) {
|
||||
throw new TypeError("Expected one of: \"error\", \"warn\", \"off\", 0, 1, 2, or a boolean.");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** @type {ObjectPropertySchema} */
|
||||
const deepObjectAssignSchema = {
|
||||
merge(first = {}, second = {}) {
|
||||
return deepMerge(first, second);
|
||||
},
|
||||
validate: "object",
|
||||
merge(first = {}, second = {}) {
|
||||
return deepMerge(first, second);
|
||||
},
|
||||
validate: "object"
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -343,194 +311,221 @@ const deepObjectAssignSchema = {
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/** @type {ObjectPropertySchema} */
|
||||
const languageOptionsSchema = {
|
||||
merge(first = {}, second = {}) {
|
||||
const result = deepMerge(first, second);
|
||||
const globalsSchema = {
|
||||
merge: "assign",
|
||||
validate(value) {
|
||||
|
||||
for (const [key, value] of Object.entries(result)) {
|
||||
/*
|
||||
* Special case: Because the `parser` property is an object, it should
|
||||
* not be deep merged. Instead, it should be replaced if it exists in
|
||||
* the second object. To make this more generic, we just check for
|
||||
* objects with methods and replace them if they exist in the second
|
||||
* object.
|
||||
*/
|
||||
if (isNonArrayObject(value)) {
|
||||
if (hasMethod(value)) {
|
||||
result[key] = second[key] ?? first[key];
|
||||
continue;
|
||||
}
|
||||
assertIsObject(value);
|
||||
|
||||
// for other objects, make sure we aren't reusing the same object
|
||||
result[key] = { ...result[key] };
|
||||
continue;
|
||||
}
|
||||
}
|
||||
for (const key of Object.keys(value)) {
|
||||
|
||||
return result;
|
||||
},
|
||||
validate: "object",
|
||||
// avoid hairy edge case
|
||||
if (key === "__proto__") {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (key !== key.trim()) {
|
||||
throw new TypeError(`Global "${key}" has leading or trailing whitespace.`);
|
||||
}
|
||||
|
||||
if (!globalVariablesValues.has(value[key])) {
|
||||
throw new TypeError(`Key "${key}": Expected "readonly", "writable", or "off".`);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** @type {ObjectPropertySchema} */
|
||||
const languageSchema = {
|
||||
merge: "replace",
|
||||
validate: assertIsPluginMemberName,
|
||||
const parserSchema = {
|
||||
merge: "replace",
|
||||
validate(value) {
|
||||
|
||||
if (!value || typeof value !== "object" ||
|
||||
(typeof value.parse !== "function" && typeof value.parseForESLint !== "function")
|
||||
) {
|
||||
throw new TypeError("Expected object with parse() or parseForESLint() method.");
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
/** @type {ObjectPropertySchema} */
|
||||
const pluginsSchema = {
|
||||
merge(first = {}, second = {}) {
|
||||
const keys = new Set([...Object.keys(first), ...Object.keys(second)]);
|
||||
const result = {};
|
||||
merge(first = {}, second = {}) {
|
||||
const keys = new Set([...Object.keys(first), ...Object.keys(second)]);
|
||||
const result = {};
|
||||
|
||||
// manually validate that plugins are not redefined
|
||||
for (const key of keys) {
|
||||
// avoid hairy edge case
|
||||
if (key === "__proto__") {
|
||||
continue;
|
||||
}
|
||||
// manually validate that plugins are not redefined
|
||||
for (const key of keys) {
|
||||
|
||||
if (key in first && key in second && first[key] !== second[key]) {
|
||||
throw new TypeError(`Cannot redefine plugin "${key}".`);
|
||||
}
|
||||
// avoid hairy edge case
|
||||
if (key === "__proto__") {
|
||||
continue;
|
||||
}
|
||||
|
||||
result[key] = second[key] || first[key];
|
||||
}
|
||||
if (key in first && key in second && first[key] !== second[key]) {
|
||||
throw new TypeError(`Cannot redefine plugin "${key}".`);
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
validate(value) {
|
||||
// first check the value to be sure it's an object
|
||||
if (value === null || typeof value !== "object") {
|
||||
throw new TypeError("Expected an object.");
|
||||
}
|
||||
result[key] = second[key] || first[key];
|
||||
}
|
||||
|
||||
// make sure it's not an array, which would mean eslintrc-style is used
|
||||
if (Array.isArray(value)) {
|
||||
throw new IncompatiblePluginsError(value);
|
||||
}
|
||||
return result;
|
||||
},
|
||||
validate(value) {
|
||||
|
||||
// second check the keys to make sure they are objects
|
||||
for (const key of Object.keys(value)) {
|
||||
// avoid hairy edge case
|
||||
if (key === "__proto__") {
|
||||
continue;
|
||||
}
|
||||
// first check the value to be sure it's an object
|
||||
if (value === null || typeof value !== "object") {
|
||||
throw new TypeError("Expected an object.");
|
||||
}
|
||||
|
||||
if (value[key] === null || typeof value[key] !== "object") {
|
||||
throw new TypeError(`Key "${key}": Expected an object.`);
|
||||
}
|
||||
}
|
||||
},
|
||||
// make sure it's not an array, which would mean eslintrc-style is used
|
||||
if (Array.isArray(value)) {
|
||||
throw new IncompatiblePluginsError(value);
|
||||
}
|
||||
|
||||
// second check the keys to make sure they are objects
|
||||
for (const key of Object.keys(value)) {
|
||||
|
||||
// avoid hairy edge case
|
||||
if (key === "__proto__") {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (value[key] === null || typeof value[key] !== "object") {
|
||||
throw new TypeError(`Key "${key}": Expected an object.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** @type {ObjectPropertySchema} */
|
||||
const processorSchema = {
|
||||
merge: "replace",
|
||||
validate(value) {
|
||||
if (typeof value === "string") {
|
||||
assertIsPluginMemberName(value);
|
||||
} else if (value && typeof value === "object") {
|
||||
if (
|
||||
typeof value.preprocess !== "function" ||
|
||||
typeof value.postprocess !== "function"
|
||||
) {
|
||||
throw new TypeError(
|
||||
"Object must have a preprocess() and a postprocess() method.",
|
||||
);
|
||||
}
|
||||
} else {
|
||||
throw new TypeError("Expected an object or a string.");
|
||||
}
|
||||
},
|
||||
merge: "replace",
|
||||
validate(value) {
|
||||
if (typeof value === "string") {
|
||||
assertIsPluginMemberName(value);
|
||||
} else if (value && typeof value === "object") {
|
||||
if (typeof value.preprocess !== "function" || typeof value.postprocess !== "function") {
|
||||
throw new TypeError("Object must have a preprocess() and a postprocess() method.");
|
||||
}
|
||||
} else {
|
||||
throw new TypeError("Expected an object or a string.");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** @type {ObjectPropertySchema} */
|
||||
const rulesSchema = {
|
||||
merge(first = {}, second = {}) {
|
||||
const result = {
|
||||
...first,
|
||||
...second,
|
||||
};
|
||||
merge(first = {}, second = {}) {
|
||||
|
||||
for (const ruleId of Object.keys(result)) {
|
||||
try {
|
||||
// avoid hairy edge case
|
||||
if (ruleId === "__proto__") {
|
||||
/* eslint-disable-next-line no-proto -- Though deprecated, may still be present */
|
||||
delete result.__proto__;
|
||||
continue;
|
||||
}
|
||||
const result = {
|
||||
...first,
|
||||
...second
|
||||
};
|
||||
|
||||
result[ruleId] = normalizeRuleOptions(result[ruleId]);
|
||||
|
||||
/*
|
||||
* If either rule config is missing, then the correct
|
||||
* config is already present and we just need to normalize
|
||||
* the severity.
|
||||
*/
|
||||
if (!(ruleId in first) || !(ruleId in second)) {
|
||||
continue;
|
||||
}
|
||||
for (const ruleId of Object.keys(result)) {
|
||||
|
||||
const firstRuleOptions = normalizeRuleOptions(first[ruleId]);
|
||||
const secondRuleOptions = normalizeRuleOptions(second[ruleId]);
|
||||
try {
|
||||
|
||||
/*
|
||||
* If the second rule config only has a severity (length of 1),
|
||||
* then use that severity and keep the rest of the options from
|
||||
* the first rule config.
|
||||
*/
|
||||
if (secondRuleOptions.length === 1) {
|
||||
result[ruleId] = [
|
||||
secondRuleOptions[0],
|
||||
...firstRuleOptions.slice(1),
|
||||
];
|
||||
continue;
|
||||
}
|
||||
// avoid hairy edge case
|
||||
if (ruleId === "__proto__") {
|
||||
|
||||
/*
|
||||
* In any other situation, then the second rule config takes
|
||||
* precedence. That means the value at `result[ruleId]` is
|
||||
* already correct and no further work is necessary.
|
||||
*/
|
||||
} catch (ex) {
|
||||
throw new Error(`Key "${ruleId}": ${ex.message}`, {
|
||||
cause: ex,
|
||||
});
|
||||
}
|
||||
}
|
||||
/* eslint-disable-next-line no-proto -- Though deprecated, may still be present */
|
||||
delete result.__proto__;
|
||||
continue;
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
result[ruleId] = normalizeRuleOptions(result[ruleId]);
|
||||
|
||||
validate(value) {
|
||||
assertIsObject(value);
|
||||
/*
|
||||
* If either rule config is missing, then the correct
|
||||
* config is already present and we just need to normalize
|
||||
* the severity.
|
||||
*/
|
||||
if (!(ruleId in first) || !(ruleId in second)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* We are not checking the rule schema here because there is no
|
||||
* guarantee that the rule definition is present at this point. Instead
|
||||
* we wait and check the rule schema during the finalization step
|
||||
* of calculating a config.
|
||||
*/
|
||||
for (const ruleId of Object.keys(value)) {
|
||||
// avoid hairy edge case
|
||||
if (ruleId === "__proto__") {
|
||||
continue;
|
||||
}
|
||||
const firstRuleOptions = normalizeRuleOptions(first[ruleId]);
|
||||
const secondRuleOptions = normalizeRuleOptions(second[ruleId]);
|
||||
|
||||
const ruleOptions = value[ruleId];
|
||||
/*
|
||||
* If the second rule config only has a severity (length of 1),
|
||||
* then use that severity and keep the rest of the options from
|
||||
* the first rule config.
|
||||
*/
|
||||
if (secondRuleOptions.length === 1) {
|
||||
result[ruleId] = [secondRuleOptions[0], ...firstRuleOptions.slice(1)];
|
||||
continue;
|
||||
}
|
||||
|
||||
assertIsRuleOptions(ruleId, ruleOptions);
|
||||
/*
|
||||
* In any other situation, then the second rule config takes
|
||||
* precedence. That means the value at `result[ruleId]` is
|
||||
* already correct and no further work is necessary.
|
||||
*/
|
||||
} catch (ex) {
|
||||
throw new Error(`Key "${ruleId}": ${ex.message}`, { cause: ex });
|
||||
}
|
||||
|
||||
if (Array.isArray(ruleOptions)) {
|
||||
assertIsRuleSeverity(ruleId, ruleOptions[0]);
|
||||
} else {
|
||||
assertIsRuleSeverity(ruleId, ruleOptions);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
|
||||
},
|
||||
|
||||
validate(value) {
|
||||
assertIsObject(value);
|
||||
|
||||
/*
|
||||
* We are not checking the rule schema here because there is no
|
||||
* guarantee that the rule definition is present at this point. Instead
|
||||
* we wait and check the rule schema during the finalization step
|
||||
* of calculating a config.
|
||||
*/
|
||||
for (const ruleId of Object.keys(value)) {
|
||||
|
||||
// avoid hairy edge case
|
||||
if (ruleId === "__proto__") {
|
||||
continue;
|
||||
}
|
||||
|
||||
const ruleOptions = value[ruleId];
|
||||
|
||||
assertIsRuleOptions(ruleId, ruleOptions);
|
||||
|
||||
if (Array.isArray(ruleOptions)) {
|
||||
assertIsRuleSeverity(ruleId, ruleOptions[0]);
|
||||
} else {
|
||||
assertIsRuleSeverity(ruleId, ruleOptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** @type {ObjectPropertySchema} */
|
||||
const ecmaVersionSchema = {
|
||||
merge: "replace",
|
||||
validate(value) {
|
||||
if (typeof value === "number" || value === "latest") {
|
||||
return;
|
||||
}
|
||||
|
||||
throw new TypeError("Expected a number or \"latest\".");
|
||||
}
|
||||
};
|
||||
|
||||
/** @type {ObjectPropertySchema} */
|
||||
const sourceTypeSchema = {
|
||||
merge: "replace",
|
||||
validate(value) {
|
||||
if (typeof value !== "string" || !/^(?:script|module|commonjs)$/u.test(value)) {
|
||||
throw new TypeError("Expected \"script\", \"module\", or \"commonjs\".");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -540,25 +535,25 @@ const rulesSchema = {
|
||||
* @returns {ObjectPropertySchema} The schema.
|
||||
*/
|
||||
function createEslintrcErrorSchema(key) {
|
||||
return {
|
||||
merge: "replace",
|
||||
validate() {
|
||||
throw new IncompatibleKeyError(key);
|
||||
},
|
||||
};
|
||||
return {
|
||||
merge: "replace",
|
||||
validate() {
|
||||
throw new IncompatibleKeyError(key);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const eslintrcKeys = [
|
||||
"env",
|
||||
"extends",
|
||||
"globals",
|
||||
"ignorePatterns",
|
||||
"noInlineConfig",
|
||||
"overrides",
|
||||
"parser",
|
||||
"parserOptions",
|
||||
"reportUnusedDisableDirectives",
|
||||
"root",
|
||||
"env",
|
||||
"extends",
|
||||
"globals",
|
||||
"ignorePatterns",
|
||||
"noInlineConfig",
|
||||
"overrides",
|
||||
"parser",
|
||||
"parserOptions",
|
||||
"reportUnusedDisableDirectives",
|
||||
"root"
|
||||
];
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -566,25 +561,30 @@ const eslintrcKeys = [
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
const flatConfigSchema = {
|
||||
// eslintrc-style keys that should always error
|
||||
...Object.fromEntries(
|
||||
eslintrcKeys.map(key => [key, createEslintrcErrorSchema(key)]),
|
||||
),
|
||||
|
||||
// flat config keys
|
||||
settings: deepObjectAssignSchema,
|
||||
linterOptions: {
|
||||
schema: {
|
||||
noInlineConfig: booleanSchema,
|
||||
reportUnusedDisableDirectives: disableDirectiveSeveritySchema,
|
||||
reportUnusedInlineConfigs: unusedInlineConfigsSeveritySchema,
|
||||
},
|
||||
},
|
||||
language: languageSchema,
|
||||
languageOptions: languageOptionsSchema,
|
||||
processor: processorSchema,
|
||||
plugins: pluginsSchema,
|
||||
rules: rulesSchema,
|
||||
// eslintrc-style keys that should always error
|
||||
...Object.fromEntries(eslintrcKeys.map(key => [key, createEslintrcErrorSchema(key)])),
|
||||
|
||||
// flat config keys
|
||||
settings: deepObjectAssignSchema,
|
||||
linterOptions: {
|
||||
schema: {
|
||||
noInlineConfig: booleanSchema,
|
||||
reportUnusedDisableDirectives: disableDirectiveSeveritySchema
|
||||
}
|
||||
},
|
||||
languageOptions: {
|
||||
schema: {
|
||||
ecmaVersion: ecmaVersionSchema,
|
||||
sourceType: sourceTypeSchema,
|
||||
globals: globalsSchema,
|
||||
parser: parserSchema,
|
||||
parserOptions: deepObjectAssignSchema
|
||||
}
|
||||
},
|
||||
processor: processorSchema,
|
||||
plugins: pluginsSchema,
|
||||
rules: rulesSchema
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -592,7 +592,7 @@ const flatConfigSchema = {
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
module.exports = {
|
||||
flatConfigSchema,
|
||||
hasMethod,
|
||||
assertIsRuleSeverity,
|
||||
flatConfigSchema,
|
||||
assertIsRuleSeverity,
|
||||
assertIsRuleOptions
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user