Update npm packages to latest versions

Fix JavaScript sourcemap paths to show full file locations
Implement --build-debug flag and complete Build UI streaming
Add xterm.js terminal UI and fix asset path routing
Add RSpade Build UI service with WebSocket support

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
root
2025-10-31 08:12:33 +00:00
parent 393479280f
commit d7d341f752
15084 changed files with 980818 additions and 138 deletions

View File

@@ -0,0 +1,20 @@
Copyright JS Foundation and other contributors
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,317 @@
<div align="center">
<a href="http://json-schema.org">
<img width="160" height="160"
src="https://raw.githubusercontent.com/webpack-contrib/schema-utils/main/.github/assets/logo.png">
</a>
<a href="https://github.com/webpack/webpack">
<img width="200" height="200"
src="https://webpack.js.org/assets/icon-square-big.svg">
</a>
</div>
[![npm][npm]][npm-url]
[![node][node]][node-url]
[![tests][tests]][tests-url]
[![coverage][cover]][cover-url]
[![GitHub Discussions][discussion]][discussion-url]
[![size][size]][size-url]
# schema-utils
Package for validate options in loaders and plugins.
## Getting Started
To begin, you'll need to install `schema-utils`:
```console
npm install schema-utils
```
## API
**schema.json**
```json
{
"type": "object",
"properties": {
"option": {
"type": "boolean"
}
},
"additionalProperties": false
}
```
```js
import schema from "./path/to/schema.json";
import { validate } from "schema-utils";
const options = { option: true };
const configuration = { name: "Loader Name/Plugin Name/Name" };
validate(schema, options, configuration);
```
### `schema`
Type: `String`
JSON schema.
Simple example of schema:
```json
{
"type": "object",
"properties": {
"name": {
"description": "This is description of option.",
"type": "string"
}
},
"additionalProperties": false
}
```
### `options`
Type: `Object`
Object with options.
```js
import schema from "./path/to/schema.json";
import { validate } from "schema-utils";
const options = { foo: "bar" };
validate(schema, { name: 123 }, { name: "MyPlugin" });
```
### `configuration`
Allow to configure validator.
There is an alternative method to configure the `name` and`baseDataPath` options via the `title` property in the schema.
For example:
```json
{
"title": "My Loader options",
"type": "object",
"properties": {
"name": {
"description": "This is description of option.",
"type": "string"
}
},
"additionalProperties": false
}
```
The last word used for the `baseDataPath` option, other words used for the `name` option.
Based on the example above the `name` option equals `My Loader`, the `baseDataPath` option equals `options`.
#### `name`
Type: `Object`
Default: `"Object"`
Allow to setup name in validation errors.
```js
import schema from "./path/to/schema.json";
import { validate } from "schema-utils";
const options = { foo: "bar" };
validate(schema, options, { name: "MyPlugin" });
```
```shell
Invalid configuration object. MyPlugin has been initialised using a configuration object that does not match the API schema.
- configuration.optionName should be a integer.
```
#### `baseDataPath`
Type: `String`
Default: `"configuration"`
Allow to setup base data path in validation errors.
```js
import schema from "./path/to/schema.json";
import { validate } from "schema-utils";
const options = { foo: "bar" };
validate(schema, options, { name: "MyPlugin", baseDataPath: "options" });
```
```shell
Invalid options object. MyPlugin has been initialised using an options object that does not match the API schema.
- options.optionName should be a integer.
```
#### `postFormatter`
Type: `Function`
Default: `undefined`
Allow to reformat errors.
```js
import schema from "./path/to/schema.json";
import { validate } from "schema-utils";
const options = { foo: "bar" };
validate(schema, options, {
name: "MyPlugin",
postFormatter: (formattedError, error) => {
if (error.keyword === "type") {
return `${formattedError}\nAdditional Information.`;
}
return formattedError;
},
});
```
```shell
Invalid options object. MyPlugin has been initialized using an options object that does not match the API schema.
- options.optionName should be a integer.
Additional Information.
```
## Examples
**schema.json**
```json
{
"type": "object",
"properties": {
"name": {
"type": "string"
},
"test": {
"anyOf": [
{ "type": "array" },
{ "type": "string" },
{ "instanceof": "RegExp" }
]
},
"transform": {
"instanceof": "Function"
},
"sourceMap": {
"type": "boolean"
}
},
"additionalProperties": false
}
```
### `Loader`
```js
import { getOptions } from "loader-utils";
import { validate } from "schema-utils";
import schema from "path/to/schema.json";
function loader(src, map) {
const options = getOptions(this);
validate(schema, options, {
name: "Loader Name",
baseDataPath: "options",
});
// Code...
}
export default loader;
```
### `Plugin`
```js
import { validate } from "schema-utils";
import schema from "path/to/schema.json";
class Plugin {
constructor(options) {
validate(schema, options, {
name: "Plugin Name",
baseDataPath: "options",
});
this.options = options;
}
apply(compiler) {
// Code...
}
}
export default Plugin;
```
### Allow to disable and enable validation (the `validate` function do nothing)
This can be useful when you don't want to do validation for `production` builds.
```js
import { disableValidation, enableValidation, validate } from "schema-utils";
// Disable validation
disableValidation();
// Do nothing
validate(schema, options);
// Enable validation
enableValidation();
// Will throw an error if schema is not valid
validate(schema, options);
// Allow to undestand do you need validation or not
const need = needValidate();
console.log(need);
```
Also you can enable/disable validation using the `process.env.SKIP_VALIDATION` env variable.
Supported values (case insensitive):
- `yes`/`y`/`true`/`1`/`on`
- `no`/`n`/`false`/`0`/`off`
## Contributing
Please take a moment to read our contributing guidelines if you haven't yet done so.
[CONTRIBUTING](./.github/CONTRIBUTING.md)
## License
[MIT](./LICENSE)
[npm]: https://img.shields.io/npm/v/schema-utils.svg
[npm-url]: https://npmjs.com/package/schema-utils
[node]: https://img.shields.io/node/v/schema-utils.svg
[node-url]: https://nodejs.org
[tests]: https://github.com/webpack/schema-utils/workflows/schema-utils/badge.svg
[tests-url]: https://github.com/webpack/schema-utils/actions
[cover]: https://codecov.io/gh/webpack/schema-utils/branch/main/graph/badge.svg
[cover-url]: https://codecov.io/gh/webpack/schema-utils
[discussion]: https://img.shields.io/github/discussions/webpack/webpack
[discussion-url]: https://github.com/webpack/webpack/discussions
[size]: https://packagephobia.com/badge?p=schema-utils
[size-url]: https://packagephobia.com/result?p=schema-utils

View File

@@ -0,0 +1,74 @@
export default ValidationError;
export type JSONSchema6 = import("json-schema").JSONSchema6;
export type JSONSchema7 = import("json-schema").JSONSchema7;
export type Schema = import("./validate").Schema;
export type ValidationErrorConfiguration =
import("./validate").ValidationErrorConfiguration;
export type PostFormatter = import("./validate").PostFormatter;
export type SchemaUtilErrorObject = import("./validate").SchemaUtilErrorObject;
declare class ValidationError extends Error {
/**
* @param {Array<SchemaUtilErrorObject>} errors array of error objects
* @param {Schema} schema schema
* @param {ValidationErrorConfiguration} configuration configuration
*/
constructor(
errors: Array<SchemaUtilErrorObject>,
schema: Schema,
configuration?: ValidationErrorConfiguration,
);
/** @type {Array<SchemaUtilErrorObject>} */
errors: Array<SchemaUtilErrorObject>;
/** @type {Schema} */
schema: Schema;
/** @type {string} */
headerName: string;
/** @type {string} */
baseDataPath: string;
/** @type {PostFormatter | null} */
postFormatter: PostFormatter | null;
/**
* @param {string} path path
* @returns {Schema} schema
*/
getSchemaPart(path: string): Schema;
/**
* @param {Schema} schema schema
* @param {boolean} logic logic
* @param {Array<object>} prevSchemas prev schemas
* @returns {string} formatted schema
*/
formatSchema(
schema: Schema,
logic?: boolean,
prevSchemas?: Array<object>,
): string;
/**
* @param {Schema=} schemaPart schema part
* @param {(boolean | Array<string>)=} additionalPath additional path
* @param {boolean=} needDot true when need dot
* @param {boolean=} logic logic
* @returns {string} schema part text
*/
getSchemaPartText(
schemaPart?: Schema | undefined,
additionalPath?: (boolean | Array<string>) | undefined,
needDot?: boolean | undefined,
logic?: boolean | undefined,
): string;
/**
* @param {Schema=} schemaPart schema part
* @returns {string} schema part description
*/
getSchemaPartDescription(schemaPart?: Schema | undefined): string;
/**
* @param {SchemaUtilErrorObject} error error object
* @returns {string} formatted error object
*/
formatValidationError(error: SchemaUtilErrorObject): string;
/**
* @param {Array<SchemaUtilErrorObject>} errors errors
* @returns {string} formatted errors
*/
formatValidationErrors(errors: Array<SchemaUtilErrorObject>): string;
}

View File

@@ -0,0 +1,19 @@
export type Schema = import("./validate").Schema;
export type JSONSchema4 = import("./validate").JSONSchema4;
export type JSONSchema6 = import("./validate").JSONSchema6;
export type JSONSchema7 = import("./validate").JSONSchema7;
export type ExtendedSchema = import("./validate").ExtendedSchema;
export type ValidationErrorConfiguration =
import("./validate").ValidationErrorConfiguration;
import { validate } from "./validate";
import { ValidationError } from "./validate";
import { enableValidation } from "./validate";
import { disableValidation } from "./validate";
import { needValidate } from "./validate";
export {
validate,
ValidationError,
enableValidation,
disableValidation,
needValidate,
};

View File

@@ -0,0 +1,10 @@
export default addAbsolutePathKeyword;
export type Ajv = import("ajv").default;
export type SchemaValidateFunction = import("ajv").SchemaValidateFunction;
export type AnySchemaObject = import("ajv").AnySchemaObject;
export type SchemaUtilErrorObject = import("../validate").SchemaUtilErrorObject;
/**
* @param {Ajv} ajv ajv
* @returns {Ajv} configured ajv
*/
declare function addAbsolutePathKeyword(ajv: Ajv): Ajv;

View File

@@ -0,0 +1,14 @@
export default addLimitKeyword;
export type Ajv = import("ajv").default;
export type Code = import("ajv").Code;
export type Name = import("ajv").Name;
export type KeywordErrorDefinition = import("ajv").KeywordErrorDefinition;
/** @typedef {import("ajv").default} Ajv */
/** @typedef {import("ajv").Code} Code */
/** @typedef {import("ajv").Name} Name */
/** @typedef {import("ajv").KeywordErrorDefinition} KeywordErrorDefinition */
/**
* @param {Ajv} ajv ajv
* @returns {Ajv} ajv with limit keyword
*/
declare function addLimitKeyword(ajv: Ajv): Ajv;

View File

@@ -0,0 +1,14 @@
export default addUndefinedAsNullKeyword;
export type Ajv = import("ajv").default;
export type SchemaValidateFunction = import("ajv").SchemaValidateFunction;
export type AnySchemaObject = import("ajv").AnySchemaObject;
export type ValidateFunction = import("ajv").ValidateFunction;
/** @typedef {import("ajv").default} Ajv */
/** @typedef {import("ajv").SchemaValidateFunction} SchemaValidateFunction */
/** @typedef {import("ajv").AnySchemaObject} AnySchemaObject */
/** @typedef {import("ajv").ValidateFunction} ValidateFunction */
/**
* @param {Ajv} ajv ajv
* @returns {Ajv} configured ajv
*/
declare function addUndefinedAsNullKeyword(ajv: Ajv): Ajv;

View File

@@ -0,0 +1,79 @@
export = Range;
/**
* @typedef {[number, boolean]} RangeValue
*/
/**
* @callback RangeValueCallback
* @param {RangeValue} rangeValue
* @returns {boolean}
*/
declare class Range {
/**
* @param {"left" | "right"} side side
* @param {boolean} exclusive exclusive
* @returns {">" | ">=" | "<" | "<="} operator
*/
static getOperator(
side: "left" | "right",
exclusive: boolean,
): ">" | ">=" | "<" | "<=";
/**
* @param {number} value value
* @param {boolean} logic is not logic applied
* @param {boolean} exclusive is range exclusive
* @returns {string} formatted right
*/
static formatRight(value: number, logic: boolean, exclusive: boolean): string;
/**
* @param {number} value value
* @param {boolean} logic is not logic applied
* @param {boolean} exclusive is range exclusive
* @returns {string} formatted left
*/
static formatLeft(value: number, logic: boolean, exclusive: boolean): string;
/**
* @param {number} start left side value
* @param {number} end right side value
* @param {boolean} startExclusive is range exclusive from left side
* @param {boolean} endExclusive is range exclusive from right side
* @param {boolean} logic is not logic applied
* @returns {string} formatted range
*/
static formatRange(
start: number,
end: number,
startExclusive: boolean,
endExclusive: boolean,
logic: boolean,
): string;
/**
* @param {Array<RangeValue>} values values
* @param {boolean} logic is not logic applied
* @returns {RangeValue} computed value and it's exclusive flag
*/
static getRangeValue(values: Array<RangeValue>, logic: boolean): RangeValue;
/** @type {Array<RangeValue>} */
_left: Array<RangeValue>;
/** @type {Array<RangeValue>} */
_right: Array<RangeValue>;
/**
* @param {number} value value
* @param {boolean=} exclusive true when exclusive, otherwise false
*/
left(value: number, exclusive?: boolean | undefined): void;
/**
* @param {number} value value
* @param {boolean=} exclusive true when exclusive, otherwise false
*/
right(value: number, exclusive?: boolean | undefined): void;
/**
* @param {boolean} logic is not logic applied
* @returns {string} "smart" range string representation
*/
format(logic?: boolean): string;
}
declare namespace Range {
export { RangeValue, RangeValueCallback };
}
type RangeValue = [number, boolean];
type RangeValueCallback = (rangeValue: RangeValue) => boolean;

View File

@@ -0,0 +1,3 @@
export function stringHints(schema: Schema, logic: boolean): string[];
export function numberHints(schema: Schema, logic: boolean): string[];
export type Schema = import("../validate").Schema;

View File

@@ -0,0 +1,12 @@
export default memoize;
export type FunctionReturning<T> = () => T;
/**
* @template T
* @typedef {() => T} FunctionReturning
*/
/**
* @template T
* @param {FunctionReturning<T>} fn memorized function
* @returns {FunctionReturning<T>} new function
*/
declare function memoize<T>(fn: FunctionReturning<T>): FunctionReturning<T>;

View File

@@ -0,0 +1,77 @@
export { default as ValidationError } from "./ValidationError";
export type JSONSchema4 = import("json-schema").JSONSchema4;
export type JSONSchema6 = import("json-schema").JSONSchema6;
export type JSONSchema7 = import("json-schema").JSONSchema7;
export type ErrorObject = import("ajv").ErrorObject;
export type ExtendedSchema = {
/**
* format minimum
*/
formatMinimum?: (string | number) | undefined;
/**
* format maximum
*/
formatMaximum?: (string | number) | undefined;
/**
* format exclusive minimum
*/
formatExclusiveMinimum?: (string | boolean) | undefined;
/**
* format exclusive maximum
*/
formatExclusiveMaximum?: (string | boolean) | undefined;
/**
* link
*/
link?: string | undefined;
/**
* undefined will be resolved as null
*/
undefinedAsNull?: boolean | undefined;
};
export type Extend = ExtendedSchema;
export type Schema = (JSONSchema4 | JSONSchema6 | JSONSchema7) & ExtendedSchema;
export type SchemaUtilErrorObject = ErrorObject & {
children?: Array<ErrorObject>;
};
export type PostFormatter = (
formattedError: string,
error: SchemaUtilErrorObject,
) => string;
export type ValidationErrorConfiguration = {
/**
* name
*/
name?: string | undefined;
/**
* base data path
*/
baseDataPath?: string | undefined;
/**
* post formatter
*/
postFormatter?: PostFormatter | undefined;
};
/**
* @param {Schema} schema schema
* @param {Array<object> | object} options options
* @param {ValidationErrorConfiguration=} configuration configuration
* @returns {void}
*/
export function validate(
schema: Schema,
options: Array<object> | object,
configuration?: ValidationErrorConfiguration | undefined,
): void;
/**
* @returns {void}
*/
export function enableValidation(): void;
/**
* @returns {void}
*/
export function disableValidation(): void;
/**
* @returns {boolean} true when need validate, otherwise false
*/
export function needValidate(): boolean;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,23 @@
"use strict";
/** @typedef {import("./validate").Schema} Schema */
/** @typedef {import("./validate").JSONSchema4} JSONSchema4 */
/** @typedef {import("./validate").JSONSchema6} JSONSchema6 */
/** @typedef {import("./validate").JSONSchema7} JSONSchema7 */
/** @typedef {import("./validate").ExtendedSchema} ExtendedSchema */
/** @typedef {import("./validate").ValidationErrorConfiguration} ValidationErrorConfiguration */
const {
validate,
ValidationError,
enableValidation,
disableValidation,
needValidate
} = require("./validate");
module.exports = {
validate,
ValidationError,
enableValidation,
disableValidation,
needValidate
};

View File

@@ -0,0 +1,83 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
/** @typedef {import("ajv").default} Ajv */
/** @typedef {import("ajv").SchemaValidateFunction} SchemaValidateFunction */
/** @typedef {import("ajv").AnySchemaObject} AnySchemaObject */
/** @typedef {import("../validate").SchemaUtilErrorObject} SchemaUtilErrorObject */
/**
* @param {string} message message
* @param {object} schema schema
* @param {string} data data
* @returns {SchemaUtilErrorObject} error object
*/
function errorMessage(message, schema, data) {
return {
dataPath: undefined,
// @ts-expect-error
schemaPath: undefined,
keyword: "absolutePath",
params: {
absolutePath: data
},
message,
parentSchema: schema
};
}
/**
* @param {boolean} shouldBeAbsolute true when should be absolute path, otherwise false
* @param {object} schema schema
* @param {string} data data
* @returns {SchemaUtilErrorObject} error object
*/
function getErrorFor(shouldBeAbsolute, schema, data) {
const message = shouldBeAbsolute ? `The provided value ${JSON.stringify(data)} is not an absolute path!` : `A relative path is expected. However, the provided value ${JSON.stringify(data)} is an absolute path!`;
return errorMessage(message, schema, data);
}
/**
* @param {Ajv} ajv ajv
* @returns {Ajv} configured ajv
*/
function addAbsolutePathKeyword(ajv) {
ajv.addKeyword({
keyword: "absolutePath",
type: "string",
errors: true,
/**
* @param {boolean} schema schema
* @param {AnySchemaObject} parentSchema parent schema
* @returns {SchemaValidateFunction} validate function
*/
compile(schema, parentSchema) {
/** @type {SchemaValidateFunction} */
const callback = data => {
let passes = true;
const isExclamationMarkPresent = data.includes("!");
if (isExclamationMarkPresent) {
callback.errors = [errorMessage(`The provided value ${JSON.stringify(data)} contains exclamation mark (!) which is not allowed because it's reserved for loader syntax.`, parentSchema, data)];
passes = false;
}
// ?:[A-Za-z]:\\ - Windows absolute path
// \\\\ - Windows network absolute path
// \/ - Unix-like OS absolute path
const isCorrectAbsolutePath = schema === /^(?:[A-Za-z]:(\\|\/)|\\\\|\/)/.test(data);
if (!isCorrectAbsolutePath) {
callback.errors = [getErrorFor(schema, parentSchema, data)];
passes = false;
}
return passes;
};
callback.errors = [];
return callback;
}
});
return ajv;
}
var _default = exports.default = addAbsolutePathKeyword;

View File

@@ -0,0 +1,167 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
/** @typedef {import("ajv").default} Ajv */
/** @typedef {import("ajv").Code} Code */
/** @typedef {import("ajv").Name} Name */
/** @typedef {import("ajv").KeywordErrorDefinition} KeywordErrorDefinition */
/**
* @param {Ajv} ajv ajv
* @returns {Ajv} ajv with limit keyword
*/
function addLimitKeyword(ajv) {
const {
_,
str,
KeywordCxt,
nil,
Name
} = require("ajv");
/**
* @param {Code | Name} nameOrCode name or code
* @returns {Code | Name} name or code
*/
function par(nameOrCode) {
return nameOrCode instanceof Name ? nameOrCode : _`(${nameOrCode})`;
}
/**
* @param {Code} op op
* @returns {(xValue: Code, yValue: Code) => Code} code
*/
function mappend(op) {
return (xValue, yValue) => xValue === nil ? yValue : yValue === nil ? xValue : _`${par(xValue)} ${op} ${par(yValue)}`;
}
const orCode = mappend(_`||`);
// boolean OR (||) expression with the passed arguments
/**
* @param {...Code} args args
* @returns {Code} code
*/
function or(...args) {
return args.reduce(orCode);
}
/**
* @param {string | number} key key
* @returns {Code} property
*/
function getProperty(key) {
return _`[${key}]`;
}
const keywords = {
formatMaximum: {
okStr: "<=",
ok: _`<=`,
fail: _`>`
},
formatMinimum: {
okStr: ">=",
ok: _`>=`,
fail: _`<`
},
formatExclusiveMaximum: {
okStr: "<",
ok: _`<`,
fail: _`>=`
},
formatExclusiveMinimum: {
okStr: ">",
ok: _`>`,
fail: _`<=`
}
};
/** @type {KeywordErrorDefinition} */
const error = {
message: ({
keyword,
schemaCode
}) => str`should be ${keywords[(/** @type {keyof typeof keywords} */keyword)].okStr} ${schemaCode}`,
params: ({
keyword,
schemaCode
}) => _`{comparison: ${keywords[(/** @type {keyof typeof keywords} */keyword)].okStr}, limit: ${schemaCode}}`
};
for (const keyword of Object.keys(keywords)) {
ajv.addKeyword({
keyword,
type: "string",
schemaType: keyword.startsWith("formatExclusive") ? ["string", "boolean"] : ["string", "number"],
$data: true,
error,
code(cxt) {
const {
gen,
data,
schemaCode,
keyword,
it
} = cxt;
const {
opts,
self
} = it;
if (!opts.validateFormats) return;
const fCxt = new KeywordCxt(it,
// eslint-disable-next-line jsdoc/no-restricted-syntax
/** @type {any} */
self.RULES.all.format.definition, "format");
/**
* @param {Name} fmt fmt
* @returns {Code} code
*/
function compareCode(fmt) {
return _`${fmt}.compare(${data}, ${schemaCode}) ${keywords[(/** @type {keyof typeof keywords} */keyword)].fail} 0`;
}
/**
* @returns {void}
*/
function validate$DataFormat() {
const fmts = gen.scopeValue("formats", {
ref: self.formats,
code: opts.code.formats
});
const fmt = gen.const("fmt", _`${fmts}[${fCxt.schemaCode}]`);
cxt.fail$data(or(_`typeof ${fmt} != "object"`, _`${fmt} instanceof RegExp`, _`typeof ${fmt}.compare != "function"`, compareCode(fmt)));
}
/**
* @returns {void}
*/
function validateFormat() {
const format = fCxt.schema;
const fmtDef = self.formats[format];
if (!fmtDef || fmtDef === true) {
return;
}
if (typeof fmtDef !== "object" || fmtDef instanceof RegExp || typeof fmtDef.compare !== "function") {
throw new Error(`"${keyword}": format "${format}" does not define "compare" function`);
}
const fmt = gen.scopeValue("formats", {
key: format,
ref: fmtDef,
code: opts.code.formats ? _`${opts.code.formats}${getProperty(format)}` : undefined
});
cxt.fail$data(compareCode(fmt));
}
if (fCxt.$data) {
validate$DataFormat();
} else {
validateFormat();
}
},
dependencies: ["format"]
});
}
return ajv;
}
var _default = exports.default = addLimitKeyword;

View File

@@ -0,0 +1,34 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
/** @typedef {import("ajv").default} Ajv */
/** @typedef {import("ajv").SchemaValidateFunction} SchemaValidateFunction */
/** @typedef {import("ajv").AnySchemaObject} AnySchemaObject */
/** @typedef {import("ajv").ValidateFunction} ValidateFunction */
/**
* @param {Ajv} ajv ajv
* @returns {Ajv} configured ajv
*/
function addUndefinedAsNullKeyword(ajv) {
ajv.addKeyword({
keyword: "undefinedAsNull",
before: "enum",
modifying: true,
/** @type {SchemaValidateFunction} */
validate(kwVal, data, metadata, dataCxt) {
if (kwVal && dataCxt && metadata && typeof metadata.enum !== "undefined") {
const idx = dataCxt.parentDataProperty;
if (typeof dataCxt.parentData[idx] === "undefined") {
dataCxt.parentData[dataCxt.parentDataProperty] = null;
}
}
return true;
}
});
return ajv;
}
var _default = exports.default = addUndefinedAsNullKeyword;

View File

@@ -0,0 +1,143 @@
"use strict";
/**
* @typedef {[number, boolean]} RangeValue
*/
/**
* @callback RangeValueCallback
* @param {RangeValue} rangeValue
* @returns {boolean}
*/
class Range {
/**
* @param {"left" | "right"} side side
* @param {boolean} exclusive exclusive
* @returns {">" | ">=" | "<" | "<="} operator
*/
static getOperator(side, exclusive) {
if (side === "left") {
return exclusive ? ">" : ">=";
}
return exclusive ? "<" : "<=";
}
/**
* @param {number} value value
* @param {boolean} logic is not logic applied
* @param {boolean} exclusive is range exclusive
* @returns {string} formatted right
*/
static formatRight(value, logic, exclusive) {
if (logic === false) {
return Range.formatLeft(value, !logic, !exclusive);
}
return `should be ${Range.getOperator("right", exclusive)} ${value}`;
}
/**
* @param {number} value value
* @param {boolean} logic is not logic applied
* @param {boolean} exclusive is range exclusive
* @returns {string} formatted left
*/
static formatLeft(value, logic, exclusive) {
if (logic === false) {
return Range.formatRight(value, !logic, !exclusive);
}
return `should be ${Range.getOperator("left", exclusive)} ${value}`;
}
/**
* @param {number} start left side value
* @param {number} end right side value
* @param {boolean} startExclusive is range exclusive from left side
* @param {boolean} endExclusive is range exclusive from right side
* @param {boolean} logic is not logic applied
* @returns {string} formatted range
*/
static formatRange(start, end, startExclusive, endExclusive, logic) {
let result = "should be";
result += ` ${Range.getOperator(logic ? "left" : "right", logic ? startExclusive : !startExclusive)} ${start} `;
result += logic ? "and" : "or";
result += ` ${Range.getOperator(logic ? "right" : "left", logic ? endExclusive : !endExclusive)} ${end}`;
return result;
}
/**
* @param {Array<RangeValue>} values values
* @param {boolean} logic is not logic applied
* @returns {RangeValue} computed value and it's exclusive flag
*/
static getRangeValue(values, logic) {
let minMax = logic ? Infinity : -Infinity;
let j = -1;
const predicate = logic ? /** @type {RangeValueCallback} */
([value]) => value <= minMax : /** @type {RangeValueCallback} */
([value]) => value >= minMax;
for (let i = 0; i < values.length; i++) {
if (predicate(values[i])) {
[minMax] = values[i];
j = i;
}
}
if (j > -1) {
return values[j];
}
return [Infinity, true];
}
constructor() {
/** @type {Array<RangeValue>} */
this._left = [];
/** @type {Array<RangeValue>} */
this._right = [];
}
/**
* @param {number} value value
* @param {boolean=} exclusive true when exclusive, otherwise false
*/
left(value, exclusive = false) {
this._left.push([value, exclusive]);
}
/**
* @param {number} value value
* @param {boolean=} exclusive true when exclusive, otherwise false
*/
right(value, exclusive = false) {
this._right.push([value, exclusive]);
}
/**
* @param {boolean} logic is not logic applied
* @returns {string} "smart" range string representation
*/
format(logic = true) {
const [start, leftExclusive] = Range.getRangeValue(this._left, logic);
const [end, rightExclusive] = Range.getRangeValue(this._right, !logic);
if (!Number.isFinite(start) && !Number.isFinite(end)) {
return "";
}
const realStart = leftExclusive ? start + 1 : start;
const realEnd = rightExclusive ? end - 1 : end;
// e.g. 5 < x < 7, 5 < x <= 6, 6 <= x <= 6
if (realStart === realEnd) {
return `should be ${logic ? "" : "!"}= ${realStart}`;
}
// e.g. 4 < x < ∞
if (Number.isFinite(start) && !Number.isFinite(end)) {
return Range.formatLeft(start, logic, leftExclusive);
}
// e.g. ∞ < x < 4
if (!Number.isFinite(start) && Number.isFinite(end)) {
return Range.formatRight(end, logic, rightExclusive);
}
return Range.formatRange(start, end, leftExclusive, rightExclusive, logic);
}
}
module.exports = Range;

View File

@@ -0,0 +1,85 @@
"use strict";
const Range = require("./Range");
/** @typedef {import("../validate").Schema} Schema */
/**
* @param {Schema} schema schema
* @param {boolean} logic logic
* @returns {string[]} array of hints
*/
module.exports.stringHints = function stringHints(schema, logic) {
const hints = [];
let type = "string";
const currentSchema = {
...schema
};
if (!logic) {
const tmpLength = currentSchema.minLength;
const tmpFormat = currentSchema.formatMinimum;
currentSchema.minLength = currentSchema.maxLength;
currentSchema.maxLength = tmpLength;
currentSchema.formatMinimum = currentSchema.formatMaximum;
currentSchema.formatMaximum = tmpFormat;
}
if (typeof currentSchema.minLength === "number") {
if (currentSchema.minLength === 1) {
type = "non-empty string";
} else {
const length = Math.max(currentSchema.minLength - 1, 0);
hints.push(`should be longer than ${length} character${length > 1 ? "s" : ""}`);
}
}
if (typeof currentSchema.maxLength === "number") {
if (currentSchema.maxLength === 0) {
type = "empty string";
} else {
const length = currentSchema.maxLength + 1;
hints.push(`should be shorter than ${length} character${length > 1 ? "s" : ""}`);
}
}
if (currentSchema.pattern) {
hints.push(`should${logic ? "" : " not"} match pattern ${JSON.stringify(currentSchema.pattern)}`);
}
if (currentSchema.format) {
hints.push(`should${logic ? "" : " not"} match format ${JSON.stringify(currentSchema.format)}`);
}
if (currentSchema.formatMinimum) {
hints.push(`should be ${currentSchema.formatExclusiveMinimum ? ">" : ">="} ${JSON.stringify(currentSchema.formatMinimum)}`);
}
if (currentSchema.formatMaximum) {
hints.push(`should be ${currentSchema.formatExclusiveMaximum ? "<" : "<="} ${JSON.stringify(currentSchema.formatMaximum)}`);
}
return [type, ...hints];
};
/**
* @param {Schema} schema schema
* @param {boolean} logic logic
* @returns {string[]} array of hints
*/
module.exports.numberHints = function numberHints(schema, logic) {
const hints = [schema.type === "integer" ? "integer" : "number"];
const range = new Range();
if (typeof schema.minimum === "number") {
range.left(schema.minimum);
}
if (typeof schema.exclusiveMinimum === "number") {
range.left(schema.exclusiveMinimum, true);
}
if (typeof schema.maximum === "number") {
range.right(schema.maximum);
}
if (typeof schema.exclusiveMaximum === "number") {
range.right(schema.exclusiveMaximum, true);
}
const rangeFormat = range.format(logic);
if (rangeFormat) {
hints.push(rangeFormat);
}
if (typeof schema.multipleOf === "number") {
hints.push(`should${logic ? "" : " not"} be multiple of ${schema.multipleOf}`);
}
return hints;
};

View File

@@ -0,0 +1,34 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
/**
* @template T
* @typedef {() => T} FunctionReturning
*/
/**
* @template T
* @param {FunctionReturning<T>} fn memorized function
* @returns {FunctionReturning<T>} new function
*/
const memoize = fn => {
let cache = false;
/** @type {T} */
let result;
return () => {
if (cache) {
return result;
}
result = fn();
cache = true;
// Allow to clean up memory for fn
// and all dependent resources
/** @type {FunctionReturning<T> | undefined} */
fn = undefined;
return result;
};
};
var _default = exports.default = memoize;

View File

@@ -0,0 +1,215 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "ValidationError", {
enumerable: true,
get: function () {
return _ValidationError.default;
}
});
exports.disableValidation = disableValidation;
exports.enableValidation = enableValidation;
exports.needValidate = needValidate;
exports.validate = validate;
var _ValidationError = _interopRequireDefault(require("./ValidationError"));
var _memorize = _interopRequireDefault(require("./util/memorize"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
const getAjv = (0, _memorize.default)(() => {
// Use CommonJS require for ajv libs so TypeScript consumers aren't locked into esModuleInterop (see #110).
const Ajv = require("ajv").default;
const ajvKeywords = require("ajv-keywords").default;
const addFormats = require("ajv-formats").default;
/**
* @type {Ajv}
*/
const ajv = new Ajv({
strict: false,
allErrors: true,
verbose: true,
$data: true
});
ajvKeywords(ajv, ["instanceof", "patternRequired"]);
// TODO set `{ keywords: true }` for the next major release and remove `keywords/limit.js`
addFormats(ajv, {
keywords: false
});
// Custom keywords
const addAbsolutePathKeyword = require("./keywords/absolutePath").default;
addAbsolutePathKeyword(ajv);
const addLimitKeyword = require("./keywords/limit").default;
addLimitKeyword(ajv);
const addUndefinedAsNullKeyword = require("./keywords/undefinedAsNull").default;
addUndefinedAsNullKeyword(ajv);
return ajv;
});
/** @typedef {import("json-schema").JSONSchema4} JSONSchema4 */
/** @typedef {import("json-schema").JSONSchema6} JSONSchema6 */
/** @typedef {import("json-schema").JSONSchema7} JSONSchema7 */
/** @typedef {import("ajv").ErrorObject} ErrorObject */
/**
* @typedef {object} ExtendedSchema
* @property {(string | number)=} formatMinimum format minimum
* @property {(string | number)=} formatMaximum format maximum
* @property {(string | boolean)=} formatExclusiveMinimum format exclusive minimum
* @property {(string | boolean)=} formatExclusiveMaximum format exclusive maximum
* @property {string=} link link
* @property {boolean=} undefinedAsNull undefined will be resolved as null
*/
// TODO remove me in the next major release
/** @typedef {ExtendedSchema} Extend */
/** @typedef {(JSONSchema4 | JSONSchema6 | JSONSchema7) & ExtendedSchema} Schema */
/** @typedef {ErrorObject & { children?: Array<ErrorObject> }} SchemaUtilErrorObject */
/**
* @callback PostFormatter
* @param {string} formattedError
* @param {SchemaUtilErrorObject} error
* @returns {string}
*/
/**
* @typedef {object} ValidationErrorConfiguration
* @property {string=} name name
* @property {string=} baseDataPath base data path
* @property {PostFormatter=} postFormatter post formatter
*/
/**
* @param {SchemaUtilErrorObject} error error
* @param {number} idx idx
* @returns {SchemaUtilErrorObject} error object with idx
*/
function applyPrefix(error, idx) {
error.instancePath = `[${idx}]${error.instancePath}`;
if (error.children) {
for (const err of error.children) applyPrefix(err, idx);
}
return error;
}
let skipValidation = false;
// We use `process.env.SKIP_VALIDATION` because you can have multiple `schema-utils` with different version,
// so we want to disable it globally, `process.env` doesn't supported by browsers, so we have the local `skipValidation` variables
// Enable validation
/**
* @returns {void}
*/
function enableValidation() {
skipValidation = false;
// Disable validation for any versions
if (process && process.env) {
process.env.SKIP_VALIDATION = "n";
}
}
// Disable validation
/**
* @returns {void}
*/
function disableValidation() {
skipValidation = true;
if (process && process.env) {
process.env.SKIP_VALIDATION = "y";
}
}
// Check if we need to confirm
/**
* @returns {boolean} true when need validate, otherwise false
*/
function needValidate() {
if (skipValidation) {
return false;
}
if (process && process.env && process.env.SKIP_VALIDATION) {
const value = process.env.SKIP_VALIDATION.trim();
if (/^(?:y|yes|true|1|on)$/i.test(value)) {
return false;
}
if (/^(?:n|no|false|0|off)$/i.test(value)) {
return true;
}
}
return true;
}
/**
* @param {Array<ErrorObject>} errors array of error objects
* @returns {Array<SchemaUtilErrorObject>} filtered array of objects
*/
function filterErrors(errors) {
/** @type {Array<SchemaUtilErrorObject>} */
let newErrors = [];
for (const error of (/** @type {Array<SchemaUtilErrorObject>} */errors)) {
const {
instancePath
} = error;
/** @type {Array<SchemaUtilErrorObject>} */
let children = [];
newErrors = newErrors.filter(oldError => {
if (oldError.instancePath.includes(instancePath)) {
if (oldError.children) {
children = [...children, ...oldError.children];
}
oldError.children = undefined;
children.push(oldError);
return false;
}
return true;
});
if (children.length) {
error.children = children;
}
newErrors.push(error);
}
return newErrors;
}
/**
* @param {Schema} schema schema
* @param {Array<object> | object} options options
* @returns {Array<SchemaUtilErrorObject>} array of error objects
*/
function validateObject(schema, options) {
// Not need to cache, because `ajv@8` has built-in cache
const compiledSchema = getAjv().compile(schema);
const valid = compiledSchema(options);
if (valid) return [];
return compiledSchema.errors ? filterErrors(compiledSchema.errors) : [];
}
/**
* @param {Schema} schema schema
* @param {Array<object> | object} options options
* @param {ValidationErrorConfiguration=} configuration configuration
* @returns {void}
*/
function validate(schema, options, configuration) {
if (!needValidate()) {
return;
}
let errors = [];
if (Array.isArray(options)) {
for (let i = 0; i <= options.length - 1; i++) {
errors.push(...validateObject(schema, options[i]).map(err => applyPrefix(err, i)));
}
} else {
errors = validateObject(schema, options);
}
if (errors.length > 0) {
throw new _ValidationError.default(errors, schema, configuration);
}
}

View File

@@ -0,0 +1,89 @@
{
"name": "schema-utils",
"version": "4.3.3",
"description": "webpack Validation Utils",
"license": "MIT",
"repository": "webpack/schema-utils",
"author": "webpack Contrib (https://github.com/webpack-contrib)",
"homepage": "https://github.com/webpack/schema-utils",
"bugs": "https://github.com/webpack/schema-utils/issues",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/webpack"
},
"main": "dist/index.js",
"types": "declarations/index.d.ts",
"engines": {
"node": ">= 10.13.0"
},
"scripts": {
"start": "npm run build -- -w",
"clean": "del-cli dist declarations",
"prebuild": "npm run clean",
"build:types": "tsc --declaration --emitDeclarationOnly --outDir declarations && prettier \"declarations/**/*.ts\" --write",
"build:code": "babel src -d dist --copy-files",
"build": "npm-run-all -p \"build:**\"",
"commitlint": "commitlint --from=main",
"security": "npm audit --production",
"fmt:check": "prettier \"{**/*,*}.{js,json,md,yml,css,ts}\" --list-different",
"lint:code": "eslint --cache .",
"lint:types": "tsc --pretty --noEmit",
"lint": "npm-run-all lint:code lint:types fmt:check",
"fmt": "npm run fmt:check -- --write",
"fix:js": "npm run lint:code -- --fix",
"fix": "npm-run-all fix:js fmt",
"test:only": "jest",
"test:watch": "npm run test:only -- --watch",
"test:coverage": "npm run test:only -- --collectCoverageFrom=\"src/**/*.js\" --coverage",
"pretest": "npm run lint",
"test": "npm run test:coverage",
"prepare": "npm run build && husky install",
"release": "standard-version"
},
"files": [
"dist",
"declarations"
],
"dependencies": {
"@types/json-schema": "^7.0.9",
"ajv": "^8.9.0",
"ajv-formats": "^2.1.1",
"ajv-keywords": "^5.1.0"
},
"devDependencies": {
"@eslint/js": "^9.28.0",
"@eslint/markdown": "^6.5.0",
"@babel/cli": "^7.17.0",
"@babel/core": "^7.17.0",
"@babel/preset-env": "^7.16.11",
"@commitlint/cli": "^17.6.1",
"@commitlint/config-conventional": "^16.0.0",
"@types/node": "^22.15.19",
"@stylistic/eslint-plugin": "^4.4.1",
"babel-jest": "^27.4.6",
"del": "^6.0.0",
"del-cli": "^4.0.1",
"globals": "^16.2.0",
"eslint": "^9.28.0",
"eslint-config-webpack": "^4.0.2",
"eslint-config-prettier": "^10.1.5",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-jest": "^28.12.0",
"eslint-plugin-jsdoc": "^50.7.1",
"eslint-plugin-n": "^17.19.0",
"eslint-plugin-prettier": "^5.4.1",
"eslint-plugin-unicorn": "^59.0.1",
"husky": "^7.0.4",
"jest": "^27.4.7",
"lint-staged": "^16.0.0",
"npm-run-all": "^4.1.5",
"prettier": "^3.5.3",
"prettier-2": "npm:prettier@^2",
"standard-version": "^9.3.2",
"typescript": "^5.8.3",
"webpack": "^5.99.8"
},
"keywords": [
"webpack"
]
}