@uxf/resizer 11.32.0 → 11.37.0

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.
Files changed (32) hide show
  1. package/README.md +31 -7
  2. package/index.d.ts +3 -5
  3. package/index.js +6 -140
  4. package/index.ts +3 -137
  5. package/package.json +43 -37
  6. package/src/middleware.d.ts +5 -0
  7. package/src/middleware.js +115 -0
  8. package/src/middleware.ts +116 -0
  9. package/src/next-route.d.ts +2 -0
  10. package/src/next-route.js +110 -0
  11. package/src/next-route.ts +107 -0
  12. package/src/utils/tools.d.ts +10 -0
  13. package/src/utils/tools.js +45 -0
  14. package/src/utils/tools.ts +40 -0
  15. /package/{utils → src/utils}/get-source-filename.d.ts +0 -0
  16. /package/{utils → src/utils}/get-source-filename.js +0 -0
  17. /package/{utils → src/utils}/get-source-filename.test.d.ts +0 -0
  18. /package/{utils → src/utils}/get-source-filename.test.js +0 -0
  19. /package/{utils → src/utils}/get-source-filename.test.ts +0 -0
  20. /package/{utils → src/utils}/get-source-filename.ts +0 -0
  21. /package/{utils → src/utils}/log.d.ts +0 -0
  22. /package/{utils → src/utils}/log.js +0 -0
  23. /package/{utils → src/utils}/log.ts +0 -0
  24. /package/{utils → src/utils}/parse-http-source.d.ts +0 -0
  25. /package/{utils → src/utils}/parse-http-source.js +0 -0
  26. /package/{utils → src/utils}/parse-http-source.test.d.ts +0 -0
  27. /package/{utils → src/utils}/parse-http-source.test.js +0 -0
  28. /package/{utils → src/utils}/parse-http-source.test.ts +0 -0
  29. /package/{utils → src/utils}/parse-http-source.ts +0 -0
  30. /package/{utils → src/utils}/repair-params.d.ts +0 -0
  31. /package/{utils → src/utils}/repair-params.js +0 -0
  32. /package/{utils → src/utils}/repair-params.ts +0 -0
package/README.md CHANGED
@@ -2,15 +2,39 @@
2
2
  [![npm](https://img.shields.io/npm/v/@uxf/resizer)](https://www.npmjs.com/package/@uxf/resizer)
3
3
 
4
4
 
5
- ## Installation
5
+ ## Installation + example (local dev next routes)
6
6
  ```
7
- // global installation
8
- yarn global add @uxf/resizer
9
- export PATH="$(yarn global bin):$PATH"
10
- uxf-resizer
7
+ yarn add @uxf/resizer --dev
8
+ ```
9
+
10
+ ```shell
11
+ src/
12
+ app/
13
+ generated/
14
+ [...path]/
15
+ route.ts # proxy
16
+ static/
17
+ [...path]/
18
+ route.ts # local static files
19
+ ```
20
+
21
+ ```ts
22
+ // src/app/generated/[...path]/route.ts
23
+ import { ProxyGET as GET } from "@uxf/resizer";
11
24
 
12
- // local installation
13
- yarn add @uxf/resizer
25
+ export { GET };
26
+ ```
27
+
28
+ ```ts
29
+ // src/app/generated/static/[...path]/route.ts
30
+ import { StaticGET as GET } from "@uxf/resizer";
31
+
32
+ export { GET };
33
+ ```
34
+
35
+ ## Installation (server)
36
+ ```
37
+ yarn add @uxf/resizer express path-to-regexp process yargs
14
38
  uxf-resizer
15
39
  ```
16
40
 
package/index.d.ts CHANGED
@@ -1,5 +1,3 @@
1
- export type Config = Array<{
2
- route: string;
3
- source: string;
4
- }>;
5
- export declare const resizerMiddleware: (config: Config) => import("express-serve-static-core").Router;
1
+ import { Config, resizerMiddleware } from "./src/middleware";
2
+ import { ProxyGET, StaticGET } from "./src/next-route";
3
+ export { Config, ProxyGET, resizerMiddleware, StaticGET };
package/index.js CHANGED
@@ -1,142 +1,8 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
25
- var __importDefault = (this && this.__importDefault) || function (mod) {
26
- return (mod && mod.__esModule) ? mod : { "default": mod };
27
- };
28
2
  Object.defineProperty(exports, "__esModule", { value: true });
29
- exports.resizerMiddleware = void 0;
30
- const express_1 = require("express");
31
- const fs = __importStar(require("fs"));
32
- const fs_1 = require("fs");
33
- const path_1 = require("path");
34
- const sharp_1 = __importDefault(require("sharp"));
35
- const get_source_filename_1 = require("./utils/get-source-filename");
36
- const log_1 = require("./utils/log");
37
- const FIT_OPTIONS = {
38
- cv: "cover",
39
- f: "fill",
40
- cn: "contain",
41
- in: "inside",
42
- out: "outside",
43
- };
44
- const POSITION_OPTIONS = {
45
- a: "attention",
46
- b: "bottom",
47
- c: "centre",
48
- e: "entropy",
49
- l: "left",
50
- lb: "left bottom",
51
- lt: "left top",
52
- r: "right",
53
- rb: "right bottom",
54
- rt: "right top",
55
- t: "top",
56
- };
57
- const getQuality = ({ quality }) => (quality && quality !== "x" ? Number(quality) : undefined);
58
- const getWidth = ({ width }) => (width && width !== "x" ? Number(width) : undefined);
59
- const getHeight = ({ height }) => (height && height !== "x" ? Number(height) : undefined);
60
- const getBackground = ({ background }) => background === undefined ? undefined : background === "t" ? "transparent" : `#${background}`;
61
- const getFit = ({ fit }) => { var _a; return (fit ? ((_a = FIT_OPTIONS[fit]) !== null && _a !== void 0 ? _a : "cover") : "cover"); };
62
- const getPosition = ({ position }) => { var _a; return (position ? ((_a = POSITION_OPTIONS[position]) !== null && _a !== void 0 ? _a : "centre") : undefined); };
63
- const getWithoutEnlargement = ({ extension }) => extension !== "svg";
64
- const getTrim = ({ trim }) => (trim === undefined || trim === "nt" ? undefined : Number(trim));
65
- const resizerMiddleware = (config) => {
66
- const middleware = (0, express_1.Router)();
67
- config.forEach((c, i) => {
68
- (0, log_1.log)(`Config #${i + 1}:`);
69
- (0, log_1.log)(` Route: ${c.route}`);
70
- (0, log_1.log)(` Source: ${c.source}`);
71
- middleware.get(c.route, async (req, res) => {
72
- var _a;
73
- (0, log_1.log)("----------------------------------------");
74
- const params = req.params;
75
- const generatedFilename = ((_a = process.env.UXF_RESIZER_GENERATE_PATH) !== null && _a !== void 0 ? _a : process.cwd()) + req.path;
76
- (0, log_1.log)(`Generated filename: ${generatedFilename}`);
77
- if ((0, fs_1.existsSync)(generatedFilename)) {
78
- return res.sendFile(generatedFilename);
79
- }
80
- const generatedDirectory = (0, path_1.dirname)(generatedFilename);
81
- if (!(0, fs_1.existsSync)(generatedDirectory)) {
82
- (0, fs_1.mkdirSync)(generatedDirectory, { recursive: true });
83
- }
84
- const sourceFilename = (0, get_source_filename_1.getSourceFilename)(c.source, params);
85
- (0, log_1.log)(`Source filename: ${sourceFilename}`);
86
- const sourceFile = /^(http|https)/.test(sourceFilename)
87
- ? await fetch(sourceFilename).then((response) => response.arrayBuffer())
88
- : sourceFilename;
89
- if (params.toFormat === "svg") {
90
- fs.writeFileSync(generatedFilename, sourceFile instanceof ArrayBuffer ? Buffer.from(sourceFile) : sourceFile);
91
- return res.sendFile(generatedFilename);
92
- }
93
- const pipeline = (0, sharp_1.default)(sourceFile)
94
- .rotate()
95
- .resize({
96
- width: getWidth(params),
97
- height: getHeight(params),
98
- background: getBackground(params),
99
- fastShrinkOnLoad: false,
100
- fit: getFit(params),
101
- position: getPosition(params),
102
- withoutEnlargement: getWithoutEnlargement(params),
103
- });
104
- const trim = getTrim(params);
105
- if (trim !== undefined) {
106
- pipeline.trim({
107
- threshold: trim,
108
- });
109
- }
110
- const quality = getQuality(params);
111
- switch (params.toFormat) {
112
- case "webp":
113
- pipeline.webp({ quality });
114
- res.setHeader("Content-Type", "image/webp");
115
- break;
116
- case "png":
117
- pipeline.png({ progressive: true, quality });
118
- res.setHeader("Content-Type", "image/png");
119
- break;
120
- case "avif":
121
- pipeline.avif({ quality });
122
- res.setHeader("Content-Type", "image/avif");
123
- break;
124
- default:
125
- pipeline.jpeg({ progressive: true, quality });
126
- res.setHeader("Content-Type", "image/jpeg");
127
- break;
128
- }
129
- pipeline
130
- .withMetadata({ density: 72 })
131
- .toColorspace("srgb")
132
- .toFile(generatedFilename)
133
- .then(() => res.sendFile(generatedFilename))
134
- .catch((e) => {
135
- (0, log_1.log)(e);
136
- return res.sendStatus(404);
137
- });
138
- });
139
- });
140
- return middleware;
141
- };
142
- exports.resizerMiddleware = resizerMiddleware;
3
+ exports.StaticGET = exports.resizerMiddleware = exports.ProxyGET = void 0;
4
+ const middleware_1 = require("./src/middleware");
5
+ Object.defineProperty(exports, "resizerMiddleware", { enumerable: true, get: function () { return middleware_1.resizerMiddleware; } });
6
+ const next_route_1 = require("./src/next-route");
7
+ Object.defineProperty(exports, "ProxyGET", { enumerable: true, get: function () { return next_route_1.ProxyGET; } });
8
+ Object.defineProperty(exports, "StaticGET", { enumerable: true, get: function () { return next_route_1.StaticGET; } });
package/index.ts CHANGED
@@ -1,138 +1,4 @@
1
- import { Router } from "express";
2
- import * as fs from "fs";
3
- import { existsSync, mkdirSync } from "fs";
4
- import { dirname } from "path";
5
- import sharp, { FitEnum, ResizeOptions } from "sharp";
6
- import { getSourceFilename } from "./utils/get-source-filename";
7
- import { log } from "./utils/log";
1
+ import { Config, resizerMiddleware } from "./src/middleware";
2
+ import { ProxyGET, StaticGET } from "./src/next-route";
8
3
 
9
- const FIT_OPTIONS: Record<string, ResizeOptions["fit"]> = {
10
- cv: "cover",
11
- f: "fill",
12
- cn: "contain",
13
- in: "inside",
14
- out: "outside",
15
- } as const;
16
-
17
- const POSITION_OPTIONS: Record<string, ResizeOptions["position"]> = {
18
- a: "attention",
19
- b: "bottom",
20
- c: "centre",
21
- e: "entropy",
22
- l: "left",
23
- lb: "left bottom",
24
- lt: "left top",
25
- r: "right",
26
- rb: "right bottom",
27
- rt: "right top",
28
- t: "top",
29
- };
30
-
31
- export type Config = Array<{
32
- route: string;
33
- source: string;
34
- }>;
35
-
36
- const getQuality = ({ quality }: any) => (quality && quality !== "x" ? Number(quality) : undefined);
37
- const getWidth = ({ width }: any) => (width && width !== "x" ? Number(width) : undefined);
38
- const getHeight = ({ height }: any) => (height && height !== "x" ? Number(height) : undefined);
39
- const getBackground = ({ background }: any) =>
40
- background === undefined ? undefined : background === "t" ? "transparent" : `#${background}`;
41
- const getFit = ({ fit }: any): keyof FitEnum => (fit ? (FIT_OPTIONS[fit] ?? "cover") : "cover");
42
- const getPosition = ({ position }: any) => (position ? (POSITION_OPTIONS[position] ?? "centre") : undefined);
43
- const getWithoutEnlargement = ({ extension }: any) => extension !== "svg";
44
- const getTrim = ({ trim }: any) => (trim === undefined || trim === "nt" ? undefined : Number(trim));
45
-
46
- export const resizerMiddleware = (config: Config) => {
47
- const middleware = Router();
48
-
49
- config.forEach((c, i) => {
50
- log(`Config #${i + 1}:`);
51
- log(` Route: ${c.route}`);
52
- log(` Source: ${c.source}`);
53
-
54
- middleware.get(c.route, async (req, res) => {
55
- log("----------------------------------------");
56
- const params = req.params;
57
-
58
- const generatedFilename = (process.env.UXF_RESIZER_GENERATE_PATH ?? process.cwd()) + req.path;
59
- log(`Generated filename: ${generatedFilename}`);
60
-
61
- if (existsSync(generatedFilename)) {
62
- return res.sendFile(generatedFilename);
63
- }
64
-
65
- const generatedDirectory = dirname(generatedFilename);
66
- if (!existsSync(generatedDirectory)) {
67
- mkdirSync(generatedDirectory, { recursive: true });
68
- }
69
-
70
- const sourceFilename = getSourceFilename(c.source, params);
71
- log(`Source filename: ${sourceFilename}`);
72
-
73
- const sourceFile = /^(http|https)/.test(sourceFilename)
74
- ? await fetch(sourceFilename).then((response) => response.arrayBuffer())
75
- : sourceFilename;
76
-
77
- if (params.toFormat === "svg") {
78
- fs.writeFileSync(
79
- generatedFilename,
80
- sourceFile instanceof ArrayBuffer ? Buffer.from(sourceFile) : sourceFile,
81
- );
82
- return res.sendFile(generatedFilename);
83
- }
84
-
85
- const pipeline = sharp(sourceFile)
86
- .rotate()
87
- .resize({
88
- width: getWidth(params),
89
- height: getHeight(params),
90
- background: getBackground(params),
91
- fastShrinkOnLoad: false,
92
- fit: getFit(params),
93
- position: getPosition(params),
94
- withoutEnlargement: getWithoutEnlargement(params),
95
- });
96
-
97
- const trim = getTrim(params);
98
- if (trim !== undefined) {
99
- pipeline.trim({
100
- threshold: trim,
101
- });
102
- }
103
-
104
- const quality = getQuality(params);
105
-
106
- switch (params.toFormat) {
107
- case "webp":
108
- pipeline.webp({ quality });
109
- res.setHeader("Content-Type", "image/webp");
110
- break;
111
- case "png":
112
- pipeline.png({ progressive: true, quality });
113
- res.setHeader("Content-Type", "image/png");
114
- break;
115
- case "avif":
116
- pipeline.avif({ quality });
117
- res.setHeader("Content-Type", "image/avif");
118
- break;
119
- default:
120
- pipeline.jpeg({ progressive: true, quality });
121
- res.setHeader("Content-Type", "image/jpeg");
122
- break;
123
- }
124
-
125
- pipeline
126
- .withMetadata({ density: 72 })
127
- .toColorspace("srgb")
128
- .toFile(generatedFilename)
129
- .then(() => res.sendFile(generatedFilename))
130
- .catch((e) => {
131
- log(e);
132
- return res.sendStatus(404);
133
- });
134
- });
135
- });
136
-
137
- return middleware;
138
- };
4
+ export { Config, ProxyGET, resizerMiddleware, StaticGET };
package/package.json CHANGED
@@ -1,38 +1,44 @@
1
1
  {
2
- "name": "@uxf/resizer",
3
- "version": "11.32.0",
4
- "main": "index.js",
5
- "description": "UXF Resizer",
6
- "author": "UXFans <dev@uxf.cz>",
7
- "repository": {
8
- "type": "git",
9
- "url": "git+https://gitlab.com/uxf-npm/resizer.git"
10
- },
11
- "bin": {
12
- "uxf-resizer": "bin/uxf-resizer.js"
13
- },
14
- "publishConfig": {
15
- "access": "public"
16
- },
17
- "license": "MIT",
18
- "dependencies": {
19
- "express": "4.19.2",
20
- "path-to-regexp": "6.2.2",
21
- "process": "0.11.10",
22
- "sharp": "0.32.6",
23
- "yargs": "17.7.2"
24
- },
25
- "devDependencies": {
26
- "@types/express": "4.17.21",
27
- "concurrently": "8.2.2",
28
- "crypto-js": "4.2.0"
29
- },
30
- "engines": {
31
- "node": ">=18"
32
- },
33
- "scripts": {
34
- "build": "tsc -P tsconfig.json",
35
- "dev": "ts-node ./bin/uxf-resizer.ts",
36
- "typecheck": "tsc --noEmit --skipLibCheck"
37
- }
38
- }
2
+ "name": "@uxf/resizer",
3
+ "version": "11.37.0",
4
+ "main": "index.js",
5
+ "description": "UXF Resizer",
6
+ "author": "UXFans <dev@uxf.cz>",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://gitlab.com/uxf-npm/resizer.git"
10
+ },
11
+ "bin": {
12
+ "uxf-resizer": "bin/uxf-resizer.js"
13
+ },
14
+ "publishConfig": {
15
+ "access": "public"
16
+ },
17
+ "license": "MIT",
18
+ "dependencies": {
19
+ "sharp": "0.32.6"
20
+ },
21
+ "devDependencies": {
22
+ "@types/express": "5.0.0",
23
+ "concurrently": "9.0.1",
24
+ "crypto-js": "4.2.0",
25
+ "express": "5.0.1",
26
+ "path-to-regexp": "6.3.0",
27
+ "process": "0.11.10",
28
+ "yargs": "17.7.2"
29
+ },
30
+ "peerDependencies": {
31
+ "express": ">=5.0",
32
+ "path-to-regexp": "^6.3.0",
33
+ "process": ">=0.11",
34
+ "yargs": ">=17.7"
35
+ },
36
+ "engines": {
37
+ "node": ">=18"
38
+ },
39
+ "scripts": {
40
+ "build": "tsc -P tsconfig.json",
41
+ "dev": "ts-node ./bin/uxf-resizer.ts",
42
+ "typecheck": "tsc --noEmit --skipLibCheck"
43
+ }
44
+ }
@@ -0,0 +1,5 @@
1
+ export type Config = Array<{
2
+ route: string;
3
+ source: string;
4
+ }>;
5
+ export declare const resizerMiddleware: (config: Config) => import("express-serve-static-core").Router;
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.resizerMiddleware = void 0;
30
+ const express_1 = require("express");
31
+ const fs = __importStar(require("fs"));
32
+ const fs_1 = require("fs");
33
+ const path_1 = require("path");
34
+ const sharp_1 = __importDefault(require("sharp"));
35
+ const get_source_filename_1 = require("./utils/get-source-filename");
36
+ const log_1 = require("./utils/log");
37
+ const tools_1 = require("./utils/tools");
38
+ const resizerMiddleware = (config) => {
39
+ const middleware = (0, express_1.Router)();
40
+ config.forEach((c, i) => {
41
+ (0, log_1.log)(`Config #${i + 1}:`);
42
+ (0, log_1.log)(` Route: ${c.route}`);
43
+ (0, log_1.log)(` Source: ${c.source}`);
44
+ middleware.get(c.route, async (req, res) => {
45
+ var _a;
46
+ (0, log_1.log)("----------------------------------------");
47
+ const params = req.params;
48
+ const generatedFilename = ((_a = process.env.UXF_RESIZER_GENERATE_PATH) !== null && _a !== void 0 ? _a : process.cwd()) + req.path;
49
+ (0, log_1.log)(`Generated filename: ${generatedFilename}`);
50
+ if ((0, fs_1.existsSync)(generatedFilename)) {
51
+ return res.sendFile(generatedFilename);
52
+ }
53
+ const generatedDirectory = (0, path_1.dirname)(generatedFilename);
54
+ if (!(0, fs_1.existsSync)(generatedDirectory)) {
55
+ (0, fs_1.mkdirSync)(generatedDirectory, { recursive: true });
56
+ }
57
+ const sourceFilename = (0, get_source_filename_1.getSourceFilename)(c.source, params);
58
+ (0, log_1.log)(`Source filename: ${sourceFilename}`);
59
+ const sourceFile = /^(http|https)/.test(sourceFilename)
60
+ ? await fetch(sourceFilename).then((response) => response.arrayBuffer())
61
+ : sourceFilename;
62
+ if (params.toFormat === "svg") {
63
+ fs.writeFileSync(generatedFilename, sourceFile instanceof ArrayBuffer ? Buffer.from(sourceFile) : sourceFile);
64
+ return res.sendFile(generatedFilename);
65
+ }
66
+ const pipeline = (0, sharp_1.default)(sourceFile)
67
+ .rotate()
68
+ .resize({
69
+ width: (0, tools_1.getWidth)(params),
70
+ height: (0, tools_1.getHeight)(params),
71
+ background: (0, tools_1.getBackground)(params),
72
+ fastShrinkOnLoad: false,
73
+ fit: (0, tools_1.getFit)(params),
74
+ position: (0, tools_1.getPosition)(params),
75
+ withoutEnlargement: (0, tools_1.getWithoutEnlargement)(params),
76
+ });
77
+ const trim = (0, tools_1.getTrim)(params);
78
+ if (trim !== undefined) {
79
+ pipeline.trim({
80
+ threshold: trim,
81
+ });
82
+ }
83
+ const quality = (0, tools_1.getQuality)(params);
84
+ switch (params.toFormat) {
85
+ case "webp":
86
+ pipeline.webp({ quality });
87
+ res.setHeader("Content-Type", "image/webp");
88
+ break;
89
+ case "png":
90
+ pipeline.png({ progressive: true, quality });
91
+ res.setHeader("Content-Type", "image/png");
92
+ break;
93
+ case "avif":
94
+ pipeline.avif({ quality });
95
+ res.setHeader("Content-Type", "image/avif");
96
+ break;
97
+ default:
98
+ pipeline.jpeg({ progressive: true, quality });
99
+ res.setHeader("Content-Type", "image/jpeg");
100
+ break;
101
+ }
102
+ pipeline
103
+ .withMetadata({ density: 72 })
104
+ .toColorspace("srgb")
105
+ .toFile(generatedFilename)
106
+ .then(() => res.sendFile(generatedFilename))
107
+ .catch((e) => {
108
+ (0, log_1.log)(e);
109
+ return res.sendStatus(404);
110
+ });
111
+ });
112
+ });
113
+ return middleware;
114
+ };
115
+ exports.resizerMiddleware = resizerMiddleware;
@@ -0,0 +1,116 @@
1
+ import { Router } from "express";
2
+ import * as fs from "fs";
3
+ import { existsSync, mkdirSync } from "fs";
4
+ import { dirname } from "path";
5
+ import sharp from "sharp";
6
+ import { getSourceFilename } from "./utils/get-source-filename";
7
+ import { log } from "./utils/log";
8
+ import {
9
+ getBackground,
10
+ getFit,
11
+ getHeight,
12
+ getPosition,
13
+ getQuality,
14
+ getTrim,
15
+ getWidth,
16
+ getWithoutEnlargement,
17
+ } from "./utils/tools";
18
+
19
+ export type Config = Array<{
20
+ route: string;
21
+ source: string;
22
+ }>;
23
+
24
+ export const resizerMiddleware = (config: Config) => {
25
+ const middleware = Router();
26
+
27
+ config.forEach((c, i) => {
28
+ log(`Config #${i + 1}:`);
29
+ log(` Route: ${c.route}`);
30
+ log(` Source: ${c.source}`);
31
+
32
+ middleware.get(c.route, async (req, res) => {
33
+ log("----------------------------------------");
34
+ const params = req.params;
35
+
36
+ const generatedFilename = (process.env.UXF_RESIZER_GENERATE_PATH ?? process.cwd()) + req.path;
37
+ log(`Generated filename: ${generatedFilename}`);
38
+
39
+ if (existsSync(generatedFilename)) {
40
+ return res.sendFile(generatedFilename);
41
+ }
42
+
43
+ const generatedDirectory = dirname(generatedFilename);
44
+ if (!existsSync(generatedDirectory)) {
45
+ mkdirSync(generatedDirectory, { recursive: true });
46
+ }
47
+
48
+ const sourceFilename = getSourceFilename(c.source, params);
49
+ log(`Source filename: ${sourceFilename}`);
50
+
51
+ const sourceFile = /^(http|https)/.test(sourceFilename)
52
+ ? await fetch(sourceFilename).then((response) => response.arrayBuffer())
53
+ : sourceFilename;
54
+
55
+ if (params.toFormat === "svg") {
56
+ fs.writeFileSync(
57
+ generatedFilename,
58
+ sourceFile instanceof ArrayBuffer ? Buffer.from(sourceFile) : sourceFile,
59
+ );
60
+ return res.sendFile(generatedFilename);
61
+ }
62
+
63
+ const pipeline = sharp(sourceFile)
64
+ .rotate()
65
+ .resize({
66
+ width: getWidth(params),
67
+ height: getHeight(params),
68
+ background: getBackground(params),
69
+ fastShrinkOnLoad: false,
70
+ fit: getFit(params),
71
+ position: getPosition(params),
72
+ withoutEnlargement: getWithoutEnlargement(params),
73
+ });
74
+
75
+ const trim = getTrim(params);
76
+ if (trim !== undefined) {
77
+ pipeline.trim({
78
+ threshold: trim,
79
+ });
80
+ }
81
+
82
+ const quality = getQuality(params);
83
+
84
+ switch (params.toFormat) {
85
+ case "webp":
86
+ pipeline.webp({ quality });
87
+ res.setHeader("Content-Type", "image/webp");
88
+ break;
89
+ case "png":
90
+ pipeline.png({ progressive: true, quality });
91
+ res.setHeader("Content-Type", "image/png");
92
+ break;
93
+ case "avif":
94
+ pipeline.avif({ quality });
95
+ res.setHeader("Content-Type", "image/avif");
96
+ break;
97
+ default:
98
+ pipeline.jpeg({ progressive: true, quality });
99
+ res.setHeader("Content-Type", "image/jpeg");
100
+ break;
101
+ }
102
+
103
+ pipeline
104
+ .withMetadata({ density: 72 })
105
+ .toColorspace("srgb")
106
+ .toFile(generatedFilename)
107
+ .then(() => res.sendFile(generatedFilename))
108
+ .catch((e) => {
109
+ log(e);
110
+ return res.sendStatus(404);
111
+ });
112
+ });
113
+ });
114
+
115
+ return middleware;
116
+ };
@@ -0,0 +1,2 @@
1
+ export declare function StaticGET(request: Request): Promise<Response>;
2
+ export declare function ProxyGET(request: Request): Promise<Response>;
@@ -0,0 +1,110 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.StaticGET = StaticGET;
30
+ exports.ProxyGET = ProxyGET;
31
+ const fs_1 = __importStar(require("fs"));
32
+ const path_1 = require("path");
33
+ const sharp_1 = __importDefault(require("sharp"));
34
+ const log_1 = require("./utils/log");
35
+ const tools_1 = require("./utils/tools");
36
+ const sendFile = (generatedFilename, contentType) => {
37
+ const data = fs_1.default.readFileSync(generatedFilename);
38
+ return new Response(data, {
39
+ headers: {
40
+ "Content-Type": contentType,
41
+ },
42
+ });
43
+ };
44
+ // lokalni zpracovani obrazku
45
+ async function StaticGET(request) {
46
+ var _a, _b;
47
+ const url = new URL(request.url);
48
+ const path = url.pathname;
49
+ const regex = /^\/generated\/static\/(?<width>\d+|x)_(?<height>\d+|x)_(?<fit>[a-z]+)_(?<position>[a-z]+)_(?<background>[0-9a-f]+)_(?<trim>[a-z]+)_(?<quality>\d+|x)\/(?<version>[a-z0-9]+)\/(?<filename>.+)\.(?<extension>[a-z]+)\.(?<toFormat>[a-z]+)/i;
50
+ const params = (_a = path.match(regex)) === null || _a === void 0 ? void 0 : _a.groups;
51
+ if (params === undefined) {
52
+ return Response.error();
53
+ }
54
+ const generatedFilename = process.cwd() + path;
55
+ (0, log_1.log)(`Generated filename: ${generatedFilename}`);
56
+ const contentType = (_b = tools_1.CONTENT_TYPES[params.toFormat]) !== null && _b !== void 0 ? _b : "image/jpeg";
57
+ if ((0, fs_1.existsSync)(generatedFilename)) {
58
+ return sendFile(generatedFilename, contentType);
59
+ }
60
+ const generatedDirectory = (0, path_1.dirname)(generatedFilename);
61
+ if (!(0, fs_1.existsSync)(generatedDirectory)) {
62
+ (0, fs_1.mkdirSync)(generatedDirectory, { recursive: true });
63
+ }
64
+ const sourceFile = process.cwd() + `/public/${params.filename}.${params.extension}`;
65
+ (0, log_1.log)(`Source filename: ${sourceFile}`);
66
+ if (params.toFormat === "svg") {
67
+ fs_1.default.writeFileSync(generatedFilename, sourceFile);
68
+ return sendFile(generatedFilename, "image/svg+xml");
69
+ }
70
+ const pipeline = (0, sharp_1.default)(sourceFile)
71
+ .rotate()
72
+ .resize({
73
+ width: (0, tools_1.getWidth)(params),
74
+ height: (0, tools_1.getHeight)(params),
75
+ background: (0, tools_1.getBackground)(params),
76
+ fastShrinkOnLoad: false,
77
+ fit: (0, tools_1.getFit)(params),
78
+ position: (0, tools_1.getPosition)(params),
79
+ withoutEnlargement: (0, tools_1.getWithoutEnlargement)(params),
80
+ });
81
+ const trim = (0, tools_1.getTrim)(params);
82
+ if (trim !== undefined) {
83
+ pipeline.trim({
84
+ threshold: trim,
85
+ });
86
+ }
87
+ const quality = (0, tools_1.getQuality)(params);
88
+ switch (params.toFormat) {
89
+ case "webp":
90
+ pipeline.webp({ quality });
91
+ break;
92
+ case "png":
93
+ pipeline.png({ progressive: true, quality });
94
+ break;
95
+ case "avif":
96
+ pipeline.avif({ quality });
97
+ break;
98
+ default:
99
+ pipeline.jpeg({ progressive: true, quality });
100
+ break;
101
+ }
102
+ await pipeline.withMetadata({ density: 72 }).toColorspace("srgb").toFile(generatedFilename);
103
+ return sendFile(generatedFilename, contentType);
104
+ }
105
+ // proxy na dev
106
+ async function ProxyGET(request) {
107
+ const url = new URL(request.url);
108
+ const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}${url.pathname}`);
109
+ return new Response(response.body);
110
+ }
@@ -0,0 +1,107 @@
1
+ import fs, { existsSync, mkdirSync } from "fs";
2
+ import { dirname } from "path";
3
+ import sharp from "sharp";
4
+ import { log } from "./utils/log";
5
+ import {
6
+ CONTENT_TYPES,
7
+ getBackground,
8
+ getFit,
9
+ getHeight,
10
+ getPosition,
11
+ getQuality,
12
+ getTrim,
13
+ getWidth,
14
+ getWithoutEnlargement,
15
+ } from "./utils/tools";
16
+
17
+ const sendFile = (generatedFilename: string, contentType: string) => {
18
+ const data = fs.readFileSync(generatedFilename);
19
+ return new Response(data, {
20
+ headers: {
21
+ "Content-Type": contentType,
22
+ },
23
+ });
24
+ };
25
+
26
+ // lokalni zpracovani obrazku
27
+ export async function StaticGET(request: Request) {
28
+ const url = new URL(request.url);
29
+ const path = url.pathname;
30
+
31
+ const regex =
32
+ /^\/generated\/static\/(?<width>\d+|x)_(?<height>\d+|x)_(?<fit>[a-z]+)_(?<position>[a-z]+)_(?<background>[0-9a-f]+)_(?<trim>[a-z]+)_(?<quality>\d+|x)\/(?<version>[a-z0-9]+)\/(?<filename>.+)\.(?<extension>[a-z]+)\.(?<toFormat>[a-z]+)/i;
33
+
34
+ const params = path.match(regex)?.groups;
35
+ if (params === undefined) {
36
+ return Response.error();
37
+ }
38
+
39
+ const generatedFilename = process.cwd() + path;
40
+ log(`Generated filename: ${generatedFilename}`);
41
+
42
+ const contentType = CONTENT_TYPES[params.toFormat] ?? "image/jpeg";
43
+
44
+ if (existsSync(generatedFilename)) {
45
+ return sendFile(generatedFilename, contentType);
46
+ }
47
+
48
+ const generatedDirectory = dirname(generatedFilename);
49
+ if (!existsSync(generatedDirectory)) {
50
+ mkdirSync(generatedDirectory, { recursive: true });
51
+ }
52
+
53
+ const sourceFile = process.cwd() + `/public/${params.filename}.${params.extension}`;
54
+ log(`Source filename: ${sourceFile}`);
55
+
56
+ if (params.toFormat === "svg") {
57
+ fs.writeFileSync(generatedFilename, sourceFile);
58
+ return sendFile(generatedFilename, "image/svg+xml");
59
+ }
60
+
61
+ const pipeline = sharp(sourceFile)
62
+ .rotate()
63
+ .resize({
64
+ width: getWidth(params),
65
+ height: getHeight(params),
66
+ background: getBackground(params),
67
+ fastShrinkOnLoad: false,
68
+ fit: getFit(params),
69
+ position: getPosition(params),
70
+ withoutEnlargement: getWithoutEnlargement(params),
71
+ });
72
+
73
+ const trim = getTrim(params);
74
+ if (trim !== undefined) {
75
+ pipeline.trim({
76
+ threshold: trim,
77
+ });
78
+ }
79
+
80
+ const quality = getQuality(params);
81
+
82
+ switch (params.toFormat) {
83
+ case "webp":
84
+ pipeline.webp({ quality });
85
+ break;
86
+ case "png":
87
+ pipeline.png({ progressive: true, quality });
88
+ break;
89
+ case "avif":
90
+ pipeline.avif({ quality });
91
+ break;
92
+ default:
93
+ pipeline.jpeg({ progressive: true, quality });
94
+ break;
95
+ }
96
+
97
+ await pipeline.withMetadata({ density: 72 }).toColorspace("srgb").toFile(generatedFilename);
98
+
99
+ return sendFile(generatedFilename, contentType);
100
+ }
101
+
102
+ // proxy na dev
103
+ export async function ProxyGET(request: Request) {
104
+ const url = new URL(request.url);
105
+ const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}${url.pathname}`);
106
+ return new Response(response.body);
107
+ }
@@ -0,0 +1,10 @@
1
+ import type { FitEnum } from "sharp";
2
+ export declare const CONTENT_TYPES: Record<string, string | undefined>;
3
+ export declare const getQuality: ({ quality }: any) => number | undefined;
4
+ export declare const getWidth: ({ width }: any) => number | undefined;
5
+ export declare const getHeight: ({ height }: any) => number | undefined;
6
+ export declare const getBackground: ({ background }: any) => string | undefined;
7
+ export declare const getFit: ({ fit }: any) => keyof FitEnum;
8
+ export declare const getPosition: ({ position }: any) => string | number | undefined;
9
+ export declare const getWithoutEnlargement: ({ extension }: any) => boolean;
10
+ export declare const getTrim: ({ trim }: any) => number | undefined;
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getTrim = exports.getWithoutEnlargement = exports.getPosition = exports.getFit = exports.getBackground = exports.getHeight = exports.getWidth = exports.getQuality = exports.CONTENT_TYPES = void 0;
4
+ const FIT_OPTIONS = {
5
+ cv: "cover",
6
+ f: "fill",
7
+ cn: "contain",
8
+ in: "inside",
9
+ out: "outside",
10
+ };
11
+ const POSITION_OPTIONS = {
12
+ a: "attention",
13
+ b: "bottom",
14
+ c: "centre",
15
+ e: "entropy",
16
+ l: "left",
17
+ lb: "left bottom",
18
+ lt: "left top",
19
+ r: "right",
20
+ rb: "right bottom",
21
+ rt: "right top",
22
+ t: "top",
23
+ };
24
+ exports.CONTENT_TYPES = {
25
+ webp: "image/webp",
26
+ png: "image/png",
27
+ avif: "image/avif",
28
+ jpeg: "image/jpeg",
29
+ };
30
+ const getQuality = ({ quality }) => (quality && quality !== "x" ? Number(quality) : undefined);
31
+ exports.getQuality = getQuality;
32
+ const getWidth = ({ width }) => (width && width !== "x" ? Number(width) : undefined);
33
+ exports.getWidth = getWidth;
34
+ const getHeight = ({ height }) => (height && height !== "x" ? Number(height) : undefined);
35
+ exports.getHeight = getHeight;
36
+ const getBackground = ({ background }) => background === undefined ? undefined : background === "t" ? "transparent" : `#${background}`;
37
+ exports.getBackground = getBackground;
38
+ const getFit = ({ fit }) => { var _a; return (fit ? ((_a = FIT_OPTIONS[fit]) !== null && _a !== void 0 ? _a : "cover") : "cover"); };
39
+ exports.getFit = getFit;
40
+ const getPosition = ({ position }) => { var _a; return (position ? ((_a = POSITION_OPTIONS[position]) !== null && _a !== void 0 ? _a : "centre") : undefined); };
41
+ exports.getPosition = getPosition;
42
+ const getWithoutEnlargement = ({ extension }) => extension !== "svg";
43
+ exports.getWithoutEnlargement = getWithoutEnlargement;
44
+ const getTrim = ({ trim }) => (trim === undefined || trim === "nt" ? undefined : Number(trim));
45
+ exports.getTrim = getTrim;
@@ -0,0 +1,40 @@
1
+ import type { FitEnum, ResizeOptions } from "sharp";
2
+
3
+ const FIT_OPTIONS: Record<string, ResizeOptions["fit"]> = {
4
+ cv: "cover",
5
+ f: "fill",
6
+ cn: "contain",
7
+ in: "inside",
8
+ out: "outside",
9
+ } as const;
10
+
11
+ const POSITION_OPTIONS: Record<string, ResizeOptions["position"]> = {
12
+ a: "attention",
13
+ b: "bottom",
14
+ c: "centre",
15
+ e: "entropy",
16
+ l: "left",
17
+ lb: "left bottom",
18
+ lt: "left top",
19
+ r: "right",
20
+ rb: "right bottom",
21
+ rt: "right top",
22
+ t: "top",
23
+ };
24
+
25
+ export const CONTENT_TYPES: Record<string, string | undefined> = {
26
+ webp: "image/webp",
27
+ png: "image/png",
28
+ avif: "image/avif",
29
+ jpeg: "image/jpeg",
30
+ };
31
+
32
+ export const getQuality = ({ quality }: any) => (quality && quality !== "x" ? Number(quality) : undefined);
33
+ export const getWidth = ({ width }: any) => (width && width !== "x" ? Number(width) : undefined);
34
+ export const getHeight = ({ height }: any) => (height && height !== "x" ? Number(height) : undefined);
35
+ export const getBackground = ({ background }: any) =>
36
+ background === undefined ? undefined : background === "t" ? "transparent" : `#${background}`;
37
+ export const getFit = ({ fit }: any): keyof FitEnum => (fit ? (FIT_OPTIONS[fit] ?? "cover") : "cover");
38
+ export const getPosition = ({ position }: any) => (position ? (POSITION_OPTIONS[position] ?? "centre") : undefined);
39
+ export const getWithoutEnlargement = ({ extension }: any) => extension !== "svg";
40
+ export const getTrim = ({ trim }: any) => (trim === undefined || trim === "nt" ? undefined : Number(trim));
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes