@jrmc/adonis-attachment 3.3.0-beta.3 → 3.3.0-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,8 +2,6 @@
2
2
 
3
3
  This package is currently development and will replace [attachment-advanced](https://github.com/batosai/attachment-advanced) for AdonisJS 6.
4
4
 
5
- Project sample : [adonis-starter-kit](https://github.com/batosai/adonis-starter-kit)
6
-
7
5
  ## Links
8
6
 
9
7
  [View documentation](https://adonis-attachment.jrmc.dev/)
@@ -12,7 +10,7 @@ Project sample : [adonis-starter-kit](https://github.com/batosai/adonis-starter-
12
10
 
13
11
  [Discord](https://discord.gg/89eMn2vB)
14
12
 
15
- ⚠️ [Breaking change](https://adonis-attachment.jrmc.dev/changelog.html) version 2, include [@adonisjs/drive](https://docs.adonisjs.com/guides/digging-deeper/drive)
13
+ Project sample : [adonis-starter-kit](https://github.com/batosai/adonis-starter-kit)
16
14
 
17
15
  ## Roadmap
18
16
 
@@ -21,9 +19,11 @@ Project sample : [adonis-starter-kit](https://github.com/batosai/adonis-starter-
21
19
  - [x] attachment file by path
22
20
  - [x] attachment file by url
23
21
  - [x] attachment file by stream
22
+ - [x] attachment files
24
23
  - [x] save meta data
25
24
  - [x] variantes
26
25
  - [x] images
26
+ - [x] [blurhash](https://blurha.sh/)
27
27
  - [x] documents thumbnail
28
28
  - [x] videos thumbnail
29
29
  - [ ] command regenerate
@@ -45,7 +45,7 @@ export class Attachment extends AttachmentBase {
45
45
  const attributes = {
46
46
  ...meta,
47
47
  key,
48
- folder: path.join(this.options.folder, 'variants', this.name),
48
+ folder: path.join(this.folder, 'variants', this.name),
49
49
  };
50
50
  const variant = new Variant(this.drive, attributes, input);
51
51
  variant.setOptions(this.options);
@@ -58,17 +58,20 @@ export class Attachment extends AttachmentBase {
58
58
  getVariant(variantName) {
59
59
  return this.variants?.find((v) => v.key === variantName);
60
60
  }
61
- getUrl(variantName) {
61
+ async getUrl(variantName) {
62
62
  if (variantName) {
63
63
  const variant = this.getVariant(variantName);
64
64
  if (variant) {
65
65
  variant.setOptions(this.options);
66
- return variant.getUrl();
66
+ const url = await variant.getUrl();
67
+ if (url) {
68
+ return url;
69
+ }
67
70
  }
68
71
  }
69
72
  return super.getUrl();
70
73
  }
71
- getSignedUrl(variantNameOrOptions, signedUrlOptions) {
74
+ async getSignedUrl(variantNameOrOptions, signedUrlOptions) {
72
75
  let variantName;
73
76
  let options = signedUrlOptions;
74
77
  if (typeof variantNameOrOptions === 'string') {
@@ -81,7 +84,10 @@ export class Attachment extends AttachmentBase {
81
84
  const variant = this.getVariant(variantName);
82
85
  if (variant) {
83
86
  variant.setOptions(this.options);
84
- return variant.getSignedUrl(options);
87
+ const url = variant.getSignedUrl(options);
88
+ if (url) {
89
+ return url;
90
+ }
85
91
  }
86
92
  }
87
93
  return super.getSignedUrl(options);
@@ -4,6 +4,7 @@
4
4
  * @license MIT
5
5
  * @copyright Jeremy Chaufourier <jeremy@chaufourier.fr>
6
6
  */
7
+ import type { LucidRow } from '@adonisjs/lucid/types/model';
7
8
  import type { DriveService, SignedURLOptions } from '@adonisjs/drive/types';
8
9
  import type { LucidOptions, AttachmentBaseAttributes, AttachmentBase as AttachmentBaseInterface } from '../types/attachment.js';
9
10
  import type { Exif, Input } from '../types/input.js';
@@ -17,7 +18,7 @@ export declare class AttachmentBase implements AttachmentBaseInterface {
17
18
  meta?: Exif;
18
19
  originalPath?: string;
19
20
  url?: string;
20
- options?: LucidOptions;
21
+ options: LucidOptions;
21
22
  constructor(drive: DriveService, attributes: AttachmentBaseAttributes, input?: Input);
22
23
  /**
23
24
  * Getters
@@ -32,6 +33,7 @@ export declare class AttachmentBase implements AttachmentBaseInterface {
32
33
  getUrl(): Promise<string>;
33
34
  getSignedUrl(signedUrlOptions?: SignedURLOptions): Promise<string>;
34
35
  setOptions(options: LucidOptions): this;
36
+ makeFolder(record?: LucidRow): this;
35
37
  /**
36
38
  *
37
39
  */
@@ -4,9 +4,11 @@
4
4
  * @license MIT
5
5
  * @copyright Jeremy Chaufourier <jeremy@chaufourier.fr>
6
6
  */
7
+ import string from '@adonisjs/core/helpers/string';
7
8
  import path from 'node:path';
8
9
  import { cuid } from '@adonisjs/core/helpers';
9
10
  import { defaultOptionsDecorator } from '../utils/default_values.js';
11
+ import { extractPathParameters } from '../utils/helpers.js';
10
12
  export class AttachmentBase {
11
13
  drive;
12
14
  input;
@@ -27,6 +29,7 @@ export class AttachmentBase {
27
29
  this.mimeType = attributes.mimeType;
28
30
  this.originalPath = attributes.path;
29
31
  this.#folder = attributes.folder;
32
+ this.setOptions({ folder: attributes.folder });
30
33
  if (attributes.name) {
31
34
  this.#name = attributes.name;
32
35
  }
@@ -43,12 +46,20 @@ export class AttachmentBase {
43
46
  return this.#name;
44
47
  }
45
48
  get folder() {
46
- if (this.options) {
47
- return this.options?.folder;
49
+ if (this.#folder) {
50
+ return this.#folder;
51
+ }
52
+ if (typeof this.options.folder === 'string') {
53
+ const parameters = extractPathParameters(this.options.folder);
54
+ if (!parameters.length) {
55
+ return this.options.folder;
56
+ }
48
57
  }
49
- return this.#folder;
50
58
  }
51
59
  get path() {
60
+ if (!this.folder && this.originalPath) {
61
+ return this.originalPath;
62
+ }
52
63
  return path.join(this.folder, this.name);
53
64
  }
54
65
  /**
@@ -70,6 +81,27 @@ export class AttachmentBase {
70
81
  };
71
82
  return this;
72
83
  }
84
+ makeFolder(record) {
85
+ if (typeof this.options.folder === 'function' && record) {
86
+ this.#folder = this.options.folder(record);
87
+ }
88
+ else if (this.options.folder) {
89
+ this.#folder = this.options.folder;
90
+ }
91
+ if (this.#folder && record) {
92
+ const parameters = extractPathParameters(this.#folder);
93
+ if (parameters) {
94
+ parameters.forEach((parameter) => {
95
+ const attribute = record.$attributes[parameter];
96
+ if (typeof attribute === 'string') {
97
+ const name = string.slug(string.noCase(string.escapeHTML(attribute.toLowerCase())));
98
+ this.#folder = this.#folder?.replace(`:${parameter}`, name);
99
+ }
100
+ });
101
+ }
102
+ }
103
+ return this;
104
+ }
73
105
  /**
74
106
  *
75
107
  */
@@ -57,7 +57,7 @@ export default class Record {
57
57
  * file.
58
58
  */
59
59
  if (newAttachments[i]) {
60
- newAttachments[i].setOptions(options);
60
+ newAttachments[i].setOptions(options).makeFolder(this.#model);
61
61
  this.#model.$attachments.attached.push(newAttachments[i]);
62
62
  /**
63
63
  * Also write the file to the disk right away
@@ -4,6 +4,7 @@
4
4
  * @license MIT
5
5
  * @copyright Jeremy Chaufourier <jeremy@chaufourier.fr>
6
6
  */
7
+ import type { LucidRow } from '@adonisjs/lucid/types/model';
7
8
  import type { DriveService } from '@adonisjs/drive/types';
8
9
  import type { Exif, Input } from './input.js';
9
10
  import type { Disk } from '@adonisjs/drive';
@@ -21,7 +22,8 @@ export type AttachmentBase = {
21
22
  meta?: Exif;
22
23
  originalPath?: string;
23
24
  url?: string;
24
- options?: LucidOptions;
25
+ options: LucidOptions;
26
+ makeFolder(record?: LucidRow): void;
25
27
  getDisk(): Disk;
26
28
  getUrl(): Promise<string>;
27
29
  getSignedUrl(signedUrlOptions?: SignedURLOptions): Promise<string>;
@@ -46,7 +48,7 @@ export type Variant = AttachmentBase & {
46
48
  };
47
49
  export type LucidOptions = {
48
50
  disk?: string;
49
- folder?: string;
51
+ folder?: string | ((record?: LucidRow) => string);
50
52
  preComputeUrl?: boolean;
51
53
  variants?: (keyof AttachmentVariants)[];
52
54
  rename?: boolean;
@@ -31,40 +31,40 @@ type jpeg = {
31
31
  format: 'jpeg';
32
32
  options: {
33
33
  quality?: number;
34
- progressive?: Boolean;
34
+ progressive?: boolean;
35
35
  chromaSubsampling?: string;
36
- optimiseCoding?: Boolean;
37
- optimizeCoding?: Boolean;
38
- mozjpeg?: Boolean;
39
- trellisQuantisation?: Boolean;
40
- overshootDeringing?: Boolean;
41
- optimiseScans?: Boolean;
42
- optimizeScans?: Boolean;
36
+ optimiseCoding?: boolean;
37
+ optimizeCoding?: boolean;
38
+ mozjpeg?: boolean;
39
+ trellisQuantisation?: boolean;
40
+ overshootDeringing?: boolean;
41
+ optimiseScans?: boolean;
42
+ optimizeScans?: boolean;
43
43
  quantisationTable?: number;
44
44
  quantizationTable?: number;
45
- force?: Boolean;
45
+ force?: boolean;
46
46
  };
47
47
  };
48
48
  type png = {
49
49
  format: 'png';
50
50
  options: {
51
51
  quality?: number;
52
- progressive?: Boolean;
52
+ progressive?: boolean;
53
53
  compressionLevel?: number;
54
- adaptiveFiltering?: Boolean;
55
- palette?: Boolean;
54
+ adaptiveFiltering?: boolean;
55
+ palette?: boolean;
56
56
  effort?: number;
57
57
  colours?: number;
58
58
  colors?: number;
59
59
  dither?: number;
60
- force?: Boolean;
60
+ force?: boolean;
61
61
  };
62
62
  };
63
63
  type gif = {
64
64
  format: 'gif';
65
65
  options: {
66
- reuse?: Boolean;
67
- progressive?: Boolean;
66
+ reuse?: boolean;
67
+ progressive?: boolean;
68
68
  colours?: number;
69
69
  colors?: number;
70
70
  effort?: number;
@@ -73,7 +73,7 @@ type gif = {
73
73
  interPaletteMaxError?: number;
74
74
  loop?: number;
75
75
  delay?: number | number[];
76
- force?: Boolean;
76
+ force?: boolean;
77
77
  };
78
78
  };
79
79
  type webp = {
@@ -81,23 +81,23 @@ type webp = {
81
81
  options: {
82
82
  quality?: number;
83
83
  alphaQuality?: number;
84
- lossless?: Boolean;
85
- nearLossless?: Boolean;
86
- smartSubsample?: Boolean;
84
+ lossless?: boolean;
85
+ nearLossless?: boolean;
86
+ smartSubsample?: boolean;
87
87
  preset?: string;
88
88
  effort?: number;
89
89
  loop?: number;
90
90
  delay?: number | number[];
91
- minSize?: Boolean;
92
- mixed?: Boolean;
93
- force?: Boolean;
91
+ minSize?: boolean;
92
+ mixed?: boolean;
93
+ force?: boolean;
94
94
  };
95
95
  };
96
96
  type avif = {
97
97
  format: 'avif';
98
98
  options: {
99
99
  quality?: number;
100
- lossless?: Boolean;
100
+ lossless?: boolean;
101
101
  effort?: number;
102
102
  chromaSubsampling?: string;
103
103
  bitdepth?: number;
@@ -108,7 +108,7 @@ type heif = {
108
108
  options: {
109
109
  compression?: string;
110
110
  quality?: number;
111
- lossless?: Boolean;
111
+ lossless?: boolean;
112
112
  effort?: number;
113
113
  chromaSubsampling?: string;
114
114
  bitdepth?: number;
@@ -127,9 +127,9 @@ export type ConverterOptions = {
127
127
  alpha: number;
128
128
  };
129
129
  kernel?: string;
130
- withoutEnlargement?: Boolean;
131
- withoutReduction?: Boolean;
132
- fastShrinkOnLoad?: Boolean;
130
+ withoutEnlargement?: boolean;
131
+ withoutReduction?: boolean;
132
+ fastShrinkOnLoad?: boolean;
133
133
  };
134
134
  format?: 'jpeg' | 'jpg' | 'png' | 'gif' | 'webp' | 'avif' | 'heif' | 'tiff' | 'raw' | jpeg | png | gif | webp | avif | heif;
135
135
  blurhash?: boolean | BlurhashOptions;
@@ -13,3 +13,4 @@ export declare function streamToTempFile(input: NodeJS.ReadableStream): Promise<
13
13
  export declare function downloadToTempFile(input: URL): Promise<string>;
14
14
  export declare function isBase64(str: string): boolean;
15
15
  export declare function imageToBlurhash(input: Input, options?: BlurhashOptions): Promise<string>;
16
+ export declare function extractPathParameters(path: string): string[];
@@ -110,3 +110,12 @@ export function imageToBlurhash(input, options) {
110
110
  }
111
111
  });
112
112
  }
113
+ export function extractPathParameters(path) {
114
+ const paramRegex = /:(\w+)/g;
115
+ const parameters = [];
116
+ let match;
117
+ while ((match = paramRegex.exec(path)) !== null) {
118
+ parameters.push(match[1]);
119
+ }
120
+ return parameters;
121
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jrmc/adonis-attachment",
3
- "version": "3.3.0-beta.3",
3
+ "version": "3.3.0-beta.4",
4
4
  "type": "module",
5
5
  "description": "Turn any field on your Lucid model to an attachment data type",
6
6
  "engines": {