Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/absolute-path-file-url.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"schema-utils": minor
---

The `absolutePath` keyword reads a `file:` URL the way Node's own URL parser does: the scheme may be followed by any number of slashes, so `file:/directory` is accepted alongside `file:///directory`, and it is matched case-insensitively, so `FILE:///directory` is too. A scheme with no slash after it — `file:directory` — still names no absolute path and is rejected.
7 changes: 5 additions & 2 deletions src/keywords/absolutePath.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,15 @@ function addAbsolutePathKeyword(ajv) {
passes = false;
}

// (?:file:\/\/)? - optional file:// protocol prefix
// (?:file:(?=\/))? - optional file: scheme, which Node's URL parser
// takes with any number of slashes after it, the lookahead leaving
// the first one to the path itself
// ?:[A-Za-z]:\\ - Windows absolute path
// \\\\ - Windows network absolute path
// \/ - Unix-like OS absolute path
const isCorrectAbsolutePath =
schema === /^(?:file:\/\/)?(?:[A-Za-z]:(\\|\/)|\\\\|\/)/.test(data);
schema ===
/^(?:file:(?=\/))?(?:[A-Za-z]:(\\|\/)|\\\\|\/)/i.test(data);

if (!isCorrectAbsolutePath) {
callback.errors = [getErrorFor(schema, parentSchema, data)];
Expand Down
10 changes: 10 additions & 0 deletions test/__snapshots__/index.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,16 @@ exports[`validation should fail validation for absolute path #4 1`] = `
- configuration.testAbsolutePath: The provided value ".." is not an absolute path!"
`;

exports[`validation should fail validation for absolute path #5 1`] = `
"Invalid configuration object. Object has been initialized using a configuration object that does not match the API schema.
- configuration.testAbsolutePath: The provided value "file:directory/deep/tree" is not an absolute path!"
`;

exports[`validation should fail validation for absolute path #6 1`] = `
"Invalid configuration object. Object has been initialized using a configuration object that does not match the API schema.
- configuration.testAbsolutePath: The provided value "file:C:/directory/deep/tree" is not an absolute path!"
`;

exports[`validation should fail validation for absolute path 1`] = `
"Invalid configuration object. Object has been initialized using a configuration object that does not match the API schema.
- configuration.context: The provided value "baz" is not an absolute path!
Expand Down
41 changes: 41 additions & 0 deletions test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,18 @@ describe("validation", () => {
testAbsolutePath: "file:///C:/directory/deep/tree",
});

createSuccessTestCase("absolutePath #8", {
testAbsolutePath: "file:/Users/username/directory/deep/tree",
});

createSuccessTestCase("absolutePath #9", {
testAbsolutePath: "file:/C:/directory/deep/tree",
});

createSuccessTestCase("absolutePath #10", {
testAbsolutePath: "FILE:///Users/username/directory/deep/tree",
});

createSuccessTestCase("$data", {
dollarData: {
smaller: 5,
Expand Down Expand Up @@ -2982,6 +2994,22 @@ describe("validation", () => {
(msg) => expect(msg).toMatchSnapshot(),
);

createFailedTestCase(
"absolute path #5",
{
testAbsolutePath: "file:directory/deep/tree",
},
(msg) => expect(msg).toMatchSnapshot(),
);

createFailedTestCase(
"absolute path #6",
{
testAbsolutePath: "file:C:/directory/deep/tree",
},
(msg) => expect(msg).toMatchSnapshot(),
);

createFailedTestCase(
"not empty string #1",
{
Expand Down Expand Up @@ -3109,6 +3137,8 @@ describe("validation", () => {
// `import.meta.resolve()` returns a `file://` URL, so every option of webpack's own schema that
// takes an absolute path has to accept one - these are all of them
const WEBPACK_FILE_URL = "file:///directory/deep/tree";
// The same URL with the single slash Node's URL parser normalizes to three
const WEBPACK_SHORT_FILE_URL = "file:/directory/deep/tree";

/** @type {Record<string, Record<string, EXPECTED_ANY>>} */
const webpackAbsolutePathOptions = {
Expand Down Expand Up @@ -3205,6 +3235,17 @@ describe("validation", () => {
{},
webpackSchema,
);

createSuccessTestCase(
`\`file:/\` for the webpack option \`${option}\``,
JSON.parse(
JSON.stringify(config)
.split(WEBPACK_FILE_URL)
.join(WEBPACK_SHORT_FILE_URL),
),
{},
webpackSchema,
);
}

// The prefix must not turn an option that wants a relative path into an absolute one
Expand Down
Loading