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
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@
"./package.json": "./package.json",
"./exceptions": "./build/src/exceptions.js",
"./test_utils": "./build/src/test_utils/main.js",
"./health": "./build/modules/health.js"
"./health": "./build/modules/health.js",
"./vine": "./build/src/vine.js"
},
"scripts": {
"pretest": "npm run lint",
Expand Down
95 changes: 2 additions & 93 deletions providers/vinejs_provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,10 @@
* file that was distributed with this source code.
*/

import vine, { symbols, BaseLiteralType, Vine } from '@vinejs/vine'
import type { Validation, FieldContext, FieldOptions } from '@vinejs/vine/types'
import type { MultipartFile, FileValidationOptions } from '@adonisjs/bodyparser/types'

import { Vine } from '@vinejs/vine'
import type { ApplicationService } from '../src/types.js'
import { Request, RequestValidator } from '../modules/http/main.js'

const MULTIPART_FILE: typeof symbols.SUBTYPE = symbols.SUBTYPE ?? Symbol.for('subtype')

/**
* Validation options accepted by the "file" rule
*/
export type FileRuleValidationOptions =
| Partial<FileValidationOptions>
| ((field: FieldContext) => Partial<FileValidationOptions>)
import { FileRuleValidationOptions, VineMultipartFile } from '../src/vine.js'

/**
* Extend VineJS
Expand All @@ -39,86 +28,6 @@ declare module '@adonisjs/core/http' {
interface Request extends RequestValidator {}
}

/**
* Checks if the value is an instance of multipart file
* from bodyparser.
*/
function isBodyParserFile(file: unknown): file is MultipartFile {
return !!(file && typeof file === 'object' && 'isMultipartFile' in file)
}

/**
* VineJS validation rule that validates the file to be an
* instance of BodyParser MultipartFile class.
*/
const isMultipartFile = vine.createRule<FileRuleValidationOptions>((file, options, field) => {
/**
* Report error when value is not a field multipart
* file object
*/
if (!isBodyParserFile(file)) {
field.report('The {{ field }} must be a file', 'file', field)
return
}

const validationOptions = typeof options === 'function' ? options(field) : options

/**
* Set size when it's defined in the options and missing
* on the file instance
*/
if (file.sizeLimit === undefined && validationOptions.size) {
file.sizeLimit = validationOptions.size
}

/**
* Set extensions when it's defined in the options and missing
* on the file instance
*/
if (file.allowedExtensions === undefined && validationOptions.extnames) {
file.allowedExtensions = validationOptions.extnames
}

/**
* Validate file
*/
file.validate()

/**
* Report errors
*/
file.errors.forEach((error) => {
field.report(error.message, `file.${error.type}`, field, validationOptions)
})
})

/**
* Represents a multipart file uploaded via multipart/form-data HTTP
* request.
*/
class VineMultipartFile extends BaseLiteralType<MultipartFile, MultipartFile, MultipartFile> {
#validationOptions?: FileRuleValidationOptions;

[MULTIPART_FILE] = 'multipartFile'

constructor(
validationOptions?: FileRuleValidationOptions,
options?: FieldOptions,
validations?: Validation<any>[]
) {
super(options, validations || [isMultipartFile(validationOptions || {})])
this.#validationOptions = validationOptions
}

clone() {
return new VineMultipartFile(
this.#validationOptions,
this.cloneOptions(),
this.cloneValidations()
) as this
}
}

/**
* The Edge service provider configures Edge to work within
* an AdonisJS application environment
Expand Down
105 changes: 105 additions & 0 deletions src/vine.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
* @adonisjs/core
*
* (c) AdonisJS
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

import vine, { symbols, BaseLiteralType } from '@vinejs/vine'
import type { Validation, FieldContext, FieldOptions } from '@vinejs/vine/types'
import type { MultipartFile, FileValidationOptions } from '@adonisjs/bodyparser/types'

const MULTIPART_FILE: typeof symbols.SUBTYPE = symbols.SUBTYPE ?? Symbol.for('subtype')

/**
* Validation options accepted by the "file" rule
*/
export type FileRuleValidationOptions =
| Partial<FileValidationOptions>
| ((field: FieldContext) => Partial<FileValidationOptions>)

/**
* Checks if the value is an instance of multipart file
* from bodyparser.
*/
function isBodyParserFile(file: unknown): file is MultipartFile {
return !!(file && typeof file === 'object' && 'isMultipartFile' in file)
}

/**
* VineJS validation rule that validates the file to be an
* instance of BodyParser MultipartFile class.
*/
const isMultipartFile = vine.createRule<FileRuleValidationOptions>((file, options, field) => {
/**
* Report error when value is not a field multipart
* file object
*/
if (!isBodyParserFile(file)) {
field.report('The {{ field }} must be a file', 'file', field)
return
}

const validationOptions = typeof options === 'function' ? options(field) : options

/**
* Set size when it's defined in the options and missing
* on the file instance
*/
if (file.sizeLimit === undefined && validationOptions.size) {
file.sizeLimit = validationOptions.size
}

/**
* Set extensions when it's defined in the options and missing
* on the file instance
*/
if (file.allowedExtensions === undefined && validationOptions.extnames) {
file.allowedExtensions = validationOptions.extnames
}

/**
* Validate file
*/
file.validate()

/**
* Report errors
*/
file.errors.forEach((error) => {
field.report(error.message, `file.${error.type}`, field, validationOptions)
})
})

/**
* Represents a multipart file uploaded via multipart/form-data HTTP
* request.
*/
export class VineMultipartFile extends BaseLiteralType<
MultipartFile,
MultipartFile,
MultipartFile
> {
#validationOptions?: FileRuleValidationOptions;

[MULTIPART_FILE] = 'multipartFile'

constructor(
validationOptions?: FileRuleValidationOptions,
options?: FieldOptions,
validations?: Validation<any>[]
) {
super(options, validations || [isMultipartFile(validationOptions || {})])
this.#validationOptions = validationOptions
}

clone() {
return new VineMultipartFile(
this.#validationOptions,
this.cloneOptions(),
this.cloneValidations()
) as this
}
}
Loading