diff --git a/docs/content/guide/global-validators.md b/docs/content/guide/global-validators.md
index 9996c1276..d3daa8df3 100644
--- a/docs/content/guide/global-validators.md
+++ b/docs/content/guide/global-validators.md
@@ -315,6 +315,7 @@ Object.keys(rules).forEach(rule => {
regex
required
size
+ url
### Playground
@@ -727,3 +728,19 @@ The file size added to the field under validation must not exceed the specified
| Param Name | Required? | Default | Description |
| ---------- | --------- | ------- | ----------------------------------- |
| `size` | **yes** | | The maximum file size in kilobytes. |
+
+#### url
+
+The field under validation must be a valid url. You can pass a `pattern` if you need the url to be more restricted.
+
+```vue
+
+
+
+
+
+```
+
+| Param Name | Required? | Default | Description |
+| ---------- | --------- | ------- | --------------------------------------------------------- |
+| `pattern` | **no** | | A regular expression instance or string representing one. |
diff --git a/packages/i18n/src/locale/en.json b/packages/i18n/src/locale/en.json
index 65713b60c..459c0ca5a 100644
--- a/packages/i18n/src/locale/en.json
+++ b/packages/i18n/src/locale/en.json
@@ -25,6 +25,7 @@
"regex": "The {field} field format is invalid",
"required_if": "The {field} field is required",
"required": "The {field} field is required",
- "size": "The {field} field size must be less than 0:{size}KB"
+ "size": "The {field} field size must be less than 0:{size}KB",
+ "url": "The {field} field is not a valid URL"
}
}
diff --git a/packages/i18n/src/locale/pt_BR.json b/packages/i18n/src/locale/pt_BR.json
index 68f42f85b..9568e0610 100644
--- a/packages/i18n/src/locale/pt_BR.json
+++ b/packages/i18n/src/locale/pt_BR.json
@@ -26,6 +26,7 @@
"regex": "O campo {field} possui um formato inválido",
"required": "O campo {field} é obrigatório",
"required_if": "O campo {field} é obrigatório",
- "size": "O campo {field} deve ser menor que 0:{size}KB"
+ "size": "O campo {field} deve ser menor que 0:{size}KB",
+ "url": "O campo {field} deve ser uma URL válida"
}
}
diff --git a/packages/i18n/src/locale/pt_PT.json b/packages/i18n/src/locale/pt_PT.json
index 5afb29054..ae05aae98 100644
--- a/packages/i18n/src/locale/pt_PT.json
+++ b/packages/i18n/src/locale/pt_PT.json
@@ -24,6 +24,7 @@
"regex": "O campo {field} possui um formato inválido",
"required": "O campo {field} é obrigatório",
"required_if": "O campo {field} é obrigatório",
- "size": "O campo {field} deve ser menor que 0:{size}KB"
+ "size": "O campo {field} deve ser menor que 0:{size}KB",
+ "url": "O campo {field} deve ser uma URL válida"
}
}
diff --git a/packages/rules/src/url.ts b/packages/rules/src/url.ts
new file mode 100644
index 000000000..12e12177b
--- /dev/null
+++ b/packages/rules/src/url.ts
@@ -0,0 +1,23 @@
+import { getSingleParam, isEmpty } from './utils';
+
+const urlValidator = (value: unknown, params: [string | RegExp | undefined] | { pattern?: string | RegExp }) => {
+ if (isEmpty(value)) {
+ return true;
+ }
+
+ let pattern = getSingleParam(params, 'pattern');
+ if (typeof pattern === 'string') {
+ pattern = new RegExp(pattern);
+ }
+
+ try {
+ // eslint-disable-next-line no-new
+ new URL(value as string);
+ } catch {
+ return false;
+ }
+
+ return pattern?.test(value as string) ?? true;
+};
+
+export default urlValidator;
diff --git a/packages/rules/tests/url.spec.ts b/packages/rules/tests/url.spec.ts
new file mode 100644
index 000000000..da07b8027
--- /dev/null
+++ b/packages/rules/tests/url.spec.ts
@@ -0,0 +1,16 @@
+import validate from '../src/url';
+
+test('validates url', () => {
+ const validUrl = 'https://test.com:8080/en/whatever/?q=test#wow';
+
+ // no pattern
+ expect(validate(validUrl, {})).toBe(true);
+ expect(validate('/only/path', {})).toBe(false);
+ expect(validate('invalid', {})).toBe(false);
+
+ // with pattern
+ expect(validate(validUrl, { pattern: 'https://.*' })).toBe(true);
+ expect(validate(validUrl, { pattern: /http:\/\/.*/ })).toBe(false);
+ expect(validate(validUrl, ['/en/whatever/'])).toBe(true);
+ expect(validate(validUrl, ['/fr/whatever/'])).toBe(false);
+});