@uxf/resizer 2.3.2 → 10.0.0-beta.79
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 +2 -0
- package/bin/uxf-resizer.d.ts +2 -0
- package/bin/uxf-resizer.js +49 -84
- package/bin/uxf-resizer.ts +65 -0
- package/index.d.ts +5 -0
- package/index.js +115 -168
- package/index.ts +156 -0
- package/package.json +6 -23
- package/tsconfig.json +7 -0
- package/.gitlab-ci.yml +0 -32
- package/.releaserc.yml +0 -6
- package/example-upload/namespace/a/b/ab7dea3a-ba56-4fa0-a407-d9c4a236d153.png +0 -0
package/README.md
CHANGED
|
@@ -50,6 +50,7 @@ You can use configuration file `.resizer-config.json` or environment variable `U
|
|
|
50
50
|
```
|
|
51
51
|
|
|
52
52
|
### Supported route parameters
|
|
53
|
+
|
|
53
54
|
| Parameter name | Type | Default sharp value |
|
|
54
55
|
|----------------|------------------------------------|----------------------------------------|
|
|
55
56
|
| width | `number` or `x` (auto) | `undefined` |
|
|
@@ -61,6 +62,7 @@ You can use configuration file `.resizer-config.json` or environment variable `U
|
|
|
61
62
|
| trim | `number` or `nt` (not trim) | |
|
|
62
63
|
| quality | `number` (min: 1, max: 100) or `x` | default of sharp (avif: 50, other: 80) |
|
|
63
64
|
|
|
65
|
+
|
|
64
66
|
#### Fit
|
|
65
67
|
- `cv`: cover,
|
|
66
68
|
- `f`: fill,
|
package/bin/uxf-resizer.js
CHANGED
|
@@ -23,96 +23,61 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
23
23
|
__setModuleDefault(result, mod);
|
|
24
24
|
return result;
|
|
25
25
|
};
|
|
26
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
27
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
28
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
29
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
30
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
31
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
32
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
33
|
-
});
|
|
34
|
-
};
|
|
35
|
-
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
36
|
-
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
37
|
-
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
38
|
-
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
39
|
-
function step(op) {
|
|
40
|
-
if (f) throw new TypeError("Generator is already executing.");
|
|
41
|
-
while (_) try {
|
|
42
|
-
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
43
|
-
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
44
|
-
switch (op[0]) {
|
|
45
|
-
case 0: case 1: t = op; break;
|
|
46
|
-
case 4: _.label++; return { value: op[1], done: false };
|
|
47
|
-
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
48
|
-
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
49
|
-
default:
|
|
50
|
-
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
51
|
-
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
52
|
-
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
53
|
-
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
54
|
-
if (t[2]) _.ops.pop();
|
|
55
|
-
_.trys.pop(); continue;
|
|
56
|
-
}
|
|
57
|
-
op = body.call(thisArg, _);
|
|
58
|
-
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
59
|
-
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
60
|
-
}
|
|
61
|
-
};
|
|
62
26
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
63
27
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
64
28
|
};
|
|
65
29
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
var help, configFilename, configString, config_1;
|
|
30
|
+
const process_1 = require("process");
|
|
31
|
+
const yargs_1 = __importDefault(require("yargs"));
|
|
32
|
+
const index_1 = require("../index");
|
|
33
|
+
const express_1 = __importDefault(require("express"));
|
|
34
|
+
const fs = __importStar(require("fs"));
|
|
35
|
+
const uxfResizer = async () => {
|
|
73
36
|
var _a;
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
if (!Array.isArray(config_1) || config_1.length === 0) {
|
|
90
|
-
throw new Error("Invalid or empty configuration");
|
|
91
|
-
}
|
|
92
|
-
return [2 /*return*/, new Promise(function (resolve, reject) {
|
|
93
|
-
try {
|
|
94
|
-
var app_1 = (0, express_1.default)().use((0, index_1.resizerMiddleware)(config_1));
|
|
95
|
-
var listenedApp_1 = app_1.listen(3000, function () {
|
|
96
|
-
// eslint-disable-next-line no-console
|
|
97
|
-
console.log("\nImage resizer app listening on port 3000");
|
|
98
|
-
app_1.on("close", function () {
|
|
99
|
-
listenedApp_1.close();
|
|
100
|
-
resolve(true);
|
|
101
|
-
});
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
catch (e) {
|
|
105
|
-
reject(e);
|
|
106
|
-
}
|
|
107
|
-
})];
|
|
37
|
+
yargs_1.default.command("$0", "UXF Resizer", (yargs) => {
|
|
38
|
+
yargs.demandCommand(0, 0).usage(`
|
|
39
|
+
Usage:
|
|
40
|
+
uxf-resizer
|
|
41
|
+
|
|
42
|
+
Environment variables:
|
|
43
|
+
UXF_RESIZER_GENERATE_PATH - required`);
|
|
44
|
+
})
|
|
45
|
+
.option("h", { alias: "help", group: "Options" })
|
|
46
|
+
.strict(false)
|
|
47
|
+
.exitProcess(false);
|
|
48
|
+
try {
|
|
49
|
+
const { help } = yargs_1.default.parse(process_1.argv.slice(2)); // TODO
|
|
50
|
+
if (help) {
|
|
51
|
+
return 0;
|
|
108
52
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
53
|
+
const configFilename = process.cwd() + "/.resizer-config.json";
|
|
54
|
+
const configString = (_a = process_1.env.UXF_RESIZER_CONFIG) !== null && _a !== void 0 ? _a : (fs.existsSync(configFilename) ? fs.readFileSync(configFilename).toString() : "[]");
|
|
55
|
+
const config = JSON.parse(configString);
|
|
56
|
+
if (!Array.isArray(config) || config.length === 0) {
|
|
57
|
+
throw new Error("Invalid or empty configuration");
|
|
113
58
|
}
|
|
114
|
-
return
|
|
115
|
-
|
|
116
|
-
|
|
59
|
+
return new Promise((resolve, reject) => {
|
|
60
|
+
try {
|
|
61
|
+
const app = (0, express_1.default)().use((0, index_1.resizerMiddleware)(config));
|
|
62
|
+
const listenedApp = app.listen(3000, () => {
|
|
63
|
+
// eslint-disable-next-line no-console
|
|
64
|
+
console.log("\nImage resizer app listening on port 3000");
|
|
65
|
+
app.on("close", () => {
|
|
66
|
+
listenedApp.close();
|
|
67
|
+
resolve(true);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
catch (e) {
|
|
72
|
+
reject(e);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
catch (e) {
|
|
77
|
+
// eslint-disable-next-line no-console
|
|
78
|
+
console.error(e);
|
|
79
|
+
return 1;
|
|
80
|
+
}
|
|
81
|
+
};
|
|
117
82
|
// eslint-disable-next-line no-console
|
|
118
83
|
uxfResizer().catch(console.error);
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { argv, env } from "process";
|
|
3
|
+
import cli from "yargs";
|
|
4
|
+
import { resizerMiddleware, Config } from "../index";
|
|
5
|
+
import express from "express";
|
|
6
|
+
import * as fs from "fs";
|
|
7
|
+
|
|
8
|
+
const uxfResizer = async () => {
|
|
9
|
+
cli.command("$0", "UXF Resizer", (yargs) => {
|
|
10
|
+
yargs.demandCommand(0, 0).usage(`
|
|
11
|
+
Usage:
|
|
12
|
+
uxf-resizer
|
|
13
|
+
|
|
14
|
+
Environment variables:
|
|
15
|
+
UXF_RESIZER_GENERATE_PATH - required`);
|
|
16
|
+
})
|
|
17
|
+
.option("h", { alias: "help", group: "Options" })
|
|
18
|
+
.strict(false)
|
|
19
|
+
.exitProcess(false);
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
const { help } = cli.parse(argv.slice(2)) as any; // TODO
|
|
23
|
+
|
|
24
|
+
if (help) {
|
|
25
|
+
return 0;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const configFilename = process.cwd() + "/.resizer-config.json";
|
|
29
|
+
|
|
30
|
+
const configString =
|
|
31
|
+
env.UXF_RESIZER_CONFIG ??
|
|
32
|
+
(fs.existsSync(configFilename) ? fs.readFileSync(configFilename).toString() : "[]");
|
|
33
|
+
|
|
34
|
+
const config: Config = JSON.parse(configString);
|
|
35
|
+
|
|
36
|
+
if (!Array.isArray(config) || config.length === 0) {
|
|
37
|
+
throw new Error("Invalid or empty configuration");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return new Promise((resolve, reject) => {
|
|
41
|
+
try {
|
|
42
|
+
const app = express().use(resizerMiddleware(config));
|
|
43
|
+
|
|
44
|
+
const listenedApp = app.listen(3000, () => {
|
|
45
|
+
// eslint-disable-next-line no-console
|
|
46
|
+
console.log("\nImage resizer app listening on port 3000");
|
|
47
|
+
|
|
48
|
+
app.on("close", () => {
|
|
49
|
+
listenedApp.close();
|
|
50
|
+
resolve(true);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
} catch (e) {
|
|
54
|
+
reject(e);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
} catch (e) {
|
|
58
|
+
// eslint-disable-next-line no-console
|
|
59
|
+
console.error(e);
|
|
60
|
+
return 1;
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// eslint-disable-next-line no-console
|
|
65
|
+
uxfResizer().catch(console.error);
|
package/index.d.ts
ADDED
package/index.js
CHANGED
|
@@ -1,59 +1,47 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
|
-
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
12
|
-
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
13
|
-
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
14
|
-
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
15
|
-
function step(op) {
|
|
16
|
-
if (f) throw new TypeError("Generator is already executing.");
|
|
17
|
-
while (_) try {
|
|
18
|
-
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
19
|
-
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
20
|
-
switch (op[0]) {
|
|
21
|
-
case 0: case 1: t = op; break;
|
|
22
|
-
case 4: _.label++; return { value: op[1], done: false };
|
|
23
|
-
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
24
|
-
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
25
|
-
default:
|
|
26
|
-
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
27
|
-
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
28
|
-
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
29
|
-
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
30
|
-
if (t[2]) _.ops.pop();
|
|
31
|
-
_.trys.pop(); continue;
|
|
32
|
-
}
|
|
33
|
-
op = body.call(thisArg, _);
|
|
34
|
-
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
35
|
-
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
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]; } };
|
|
36
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;
|
|
37
24
|
};
|
|
38
25
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
39
26
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
40
27
|
};
|
|
41
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
29
|
exports.resizerMiddleware = void 0;
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
30
|
+
const express_1 = require("express");
|
|
31
|
+
const node_fetch_1 = __importDefault(require("node-fetch"));
|
|
32
|
+
const fs_1 = require("fs");
|
|
33
|
+
const path_1 = require("path");
|
|
34
|
+
const sharp_1 = __importDefault(require("sharp"));
|
|
35
|
+
const path_to_regexp_1 = require("path-to-regexp");
|
|
36
|
+
const fs = __importStar(require("fs"));
|
|
37
|
+
const FIT_OPTIONS = {
|
|
50
38
|
cv: "cover",
|
|
51
39
|
f: "fill",
|
|
52
40
|
cn: "contain",
|
|
53
41
|
in: "inside",
|
|
54
42
|
out: "outside",
|
|
55
43
|
};
|
|
56
|
-
|
|
44
|
+
const POSITION_OPTIONS = {
|
|
57
45
|
a: "attention",
|
|
58
46
|
b: "bottom",
|
|
59
47
|
c: "centre",
|
|
@@ -66,40 +54,14 @@ var POSITION_OPTIONS = {
|
|
|
66
54
|
rt: "right top",
|
|
67
55
|
t: "top",
|
|
68
56
|
};
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
};
|
|
77
|
-
var getHeight = function (_a) {
|
|
78
|
-
var height = _a.height;
|
|
79
|
-
return (height && height !== "x" ? Number(height) : undefined);
|
|
80
|
-
};
|
|
81
|
-
var getBackground = function (_a) {
|
|
82
|
-
var background = _a.background;
|
|
83
|
-
return background === undefined ? undefined : background === "t" ? "transparent" : "#".concat(background);
|
|
84
|
-
};
|
|
85
|
-
var getFit = function (_a) {
|
|
86
|
-
var _b;
|
|
87
|
-
var fit = _a.fit;
|
|
88
|
-
return (fit ? (_b = FIT_OPTIONS[fit]) !== null && _b !== void 0 ? _b : "cover" : "cover");
|
|
89
|
-
};
|
|
90
|
-
var getPosition = function (_a) {
|
|
91
|
-
var _b;
|
|
92
|
-
var position = _a.position;
|
|
93
|
-
return (position ? (_b = POSITION_OPTIONS[position]) !== null && _b !== void 0 ? _b : "centre" : undefined);
|
|
94
|
-
};
|
|
95
|
-
var getWithoutEnlargement = function (_a) {
|
|
96
|
-
var extension = _a.extension;
|
|
97
|
-
return extension !== "svg";
|
|
98
|
-
};
|
|
99
|
-
var getTrim = function (_a) {
|
|
100
|
-
var trim = _a.trim;
|
|
101
|
-
return (trim === undefined || trim === "nt" ? undefined : Number(trim));
|
|
102
|
-
};
|
|
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));
|
|
103
65
|
function log(message) {
|
|
104
66
|
// eslint-disable-next-line no-console
|
|
105
67
|
console.log(message);
|
|
@@ -111,103 +73,88 @@ function log(message) {
|
|
|
111
73
|
* pro zpětné složení pomocí pathToRegexp::compile() se používá: /generated/:restPath+ => restPath musí být string[]
|
|
112
74
|
* Tato funkce projde celý objekt a pokud v hodnotě atributu najde "/" tak tento atribut převede na pole
|
|
113
75
|
*/
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
Object.keys(params).forEach(
|
|
117
|
-
|
|
76
|
+
const repairParams = (params) => {
|
|
77
|
+
const obj = {};
|
|
78
|
+
Object.keys(params).forEach((key) => {
|
|
79
|
+
const value = params[key];
|
|
118
80
|
obj[key] = value.includes("/") ? value.split("/") : value;
|
|
119
81
|
});
|
|
120
82
|
return obj;
|
|
121
83
|
};
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
config.forEach(
|
|
125
|
-
log(
|
|
126
|
-
log(
|
|
127
|
-
log(
|
|
128
|
-
middleware.get(c.route,
|
|
129
|
-
var
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
pipeline.jpeg({ progressive: true, quality: quality });
|
|
195
|
-
res.setHeader("Content-Type", "image/jpeg");
|
|
196
|
-
break;
|
|
197
|
-
}
|
|
198
|
-
pipeline
|
|
199
|
-
.withMetadata({ density: 72 })
|
|
200
|
-
.toColorspace("srgb")
|
|
201
|
-
.toFile(generatedFilename)
|
|
202
|
-
.then(function () { return res.sendFile(generatedFilename); })
|
|
203
|
-
.catch(function (e) {
|
|
204
|
-
log(e);
|
|
205
|
-
return res.sendStatus(404);
|
|
206
|
-
});
|
|
207
|
-
return [2 /*return*/];
|
|
208
|
-
}
|
|
84
|
+
const resizerMiddleware = (config) => {
|
|
85
|
+
const middleware = (0, express_1.Router)();
|
|
86
|
+
config.forEach((c, i) => {
|
|
87
|
+
log(`Config #${i + 1}:`);
|
|
88
|
+
log(` Route: ${c.route}`);
|
|
89
|
+
log(` Source: ${c.source}`);
|
|
90
|
+
middleware.get(c.route, async (req, res) => {
|
|
91
|
+
var _a, _b;
|
|
92
|
+
const url = c.source.startsWith("http") ? new URL(c.source) : null;
|
|
93
|
+
const domain = url === null || url === void 0 ? void 0 : url.origin;
|
|
94
|
+
const repairedParams = repairParams(req.params);
|
|
95
|
+
const sourceFilename = (0, path_to_regexp_1.compile)((_a = url === null || url === void 0 ? void 0 : url.pathname) !== null && _a !== void 0 ? _a : c.source, {})(repairedParams);
|
|
96
|
+
const generatedFilename = ((_b = process.env.UXF_RESIZER_GENERATE_PATH) !== null && _b !== void 0 ? _b : process.cwd()) + req.path;
|
|
97
|
+
if ((0, fs_1.existsSync)(generatedFilename)) {
|
|
98
|
+
return res.sendFile(generatedFilename);
|
|
99
|
+
}
|
|
100
|
+
log("----------------------------------------");
|
|
101
|
+
log(repairedParams);
|
|
102
|
+
log(`Source filename: ${sourceFilename}`);
|
|
103
|
+
log(`Generated filename: ${generatedFilename}`);
|
|
104
|
+
const generatedDirectory = (0, path_1.dirname)(generatedFilename);
|
|
105
|
+
if (!(0, fs_1.existsSync)(generatedDirectory)) {
|
|
106
|
+
(0, fs_1.mkdirSync)(generatedDirectory, { recursive: true });
|
|
107
|
+
}
|
|
108
|
+
const params = req.params;
|
|
109
|
+
const sourceFile = domain ? await (await (0, node_fetch_1.default)(domain + sourceFilename)).buffer() : sourceFilename;
|
|
110
|
+
if (params.toFormat === "svg") {
|
|
111
|
+
fs.writeFileSync(generatedFilename, sourceFile);
|
|
112
|
+
return res.sendFile(generatedFilename);
|
|
113
|
+
}
|
|
114
|
+
const pipeline = await (0, sharp_1.default)(sourceFile)
|
|
115
|
+
.rotate()
|
|
116
|
+
.resize({
|
|
117
|
+
width: getWidth(params),
|
|
118
|
+
height: getHeight(params),
|
|
119
|
+
background: getBackground(params),
|
|
120
|
+
fastShrinkOnLoad: false,
|
|
121
|
+
fit: getFit(params),
|
|
122
|
+
position: getPosition(params),
|
|
123
|
+
withoutEnlargement: getWithoutEnlargement(params),
|
|
124
|
+
});
|
|
125
|
+
const trim = getTrim(params);
|
|
126
|
+
if (trim !== undefined) {
|
|
127
|
+
pipeline.trim(trim);
|
|
128
|
+
}
|
|
129
|
+
const quality = getQuality(params);
|
|
130
|
+
switch (params.toFormat) {
|
|
131
|
+
case "webp":
|
|
132
|
+
pipeline.webp({ quality });
|
|
133
|
+
res.setHeader("Content-Type", "image/webp");
|
|
134
|
+
break;
|
|
135
|
+
case "png":
|
|
136
|
+
pipeline.png({ progressive: true, quality });
|
|
137
|
+
res.setHeader("Content-Type", "image/png");
|
|
138
|
+
break;
|
|
139
|
+
case "avif":
|
|
140
|
+
pipeline.avif({ quality });
|
|
141
|
+
res.setHeader("Content-Type", "image/avif");
|
|
142
|
+
break;
|
|
143
|
+
default:
|
|
144
|
+
pipeline.jpeg({ progressive: true, quality });
|
|
145
|
+
res.setHeader("Content-Type", "image/jpeg");
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
pipeline
|
|
149
|
+
.withMetadata({ density: 72 })
|
|
150
|
+
.toColorspace("srgb")
|
|
151
|
+
.toFile(generatedFilename)
|
|
152
|
+
.then(() => res.sendFile(generatedFilename))
|
|
153
|
+
.catch((e) => {
|
|
154
|
+
log(e);
|
|
155
|
+
return res.sendStatus(404);
|
|
209
156
|
});
|
|
210
|
-
});
|
|
157
|
+
});
|
|
211
158
|
});
|
|
212
159
|
return middleware;
|
|
213
160
|
};
|
package/index.ts
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { Router } from "express";
|
|
2
|
+
import fetch from "node-fetch";
|
|
3
|
+
import { existsSync, mkdirSync } from "fs";
|
|
4
|
+
import { dirname } from "path";
|
|
5
|
+
import sharp, { FitEnum, ResizeOptions } from "sharp";
|
|
6
|
+
import { compile } from "path-to-regexp";
|
|
7
|
+
import * as fs from "fs";
|
|
8
|
+
|
|
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
|
+
function log(message: any) {
|
|
47
|
+
// eslint-disable-next-line no-console
|
|
48
|
+
console.log(message);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* TODO @vejvis HACK
|
|
53
|
+
*
|
|
54
|
+
* pro odchycení celé cesty i s lomítkama je potřeba udělat hack: /generated/:restPath(*) => restPath bude string
|
|
55
|
+
* pro zpětné složení pomocí pathToRegexp::compile() se používá: /generated/:restPath+ => restPath musí být string[]
|
|
56
|
+
* Tato funkce projde celý objekt a pokud v hodnotě atributu najde "/" tak tento atribut převede na pole
|
|
57
|
+
*/
|
|
58
|
+
const repairParams = (params: any) => {
|
|
59
|
+
const obj: any = {};
|
|
60
|
+
Object.keys(params).forEach((key) => {
|
|
61
|
+
const value: string = params[key];
|
|
62
|
+
obj[key] = value.includes("/") ? value.split("/") : value;
|
|
63
|
+
});
|
|
64
|
+
return obj;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export const resizerMiddleware = (config: Config) => {
|
|
68
|
+
const middleware = Router();
|
|
69
|
+
|
|
70
|
+
config.forEach((c, i) => {
|
|
71
|
+
log(`Config #${i + 1}:`);
|
|
72
|
+
log(` Route: ${c.route}`);
|
|
73
|
+
log(` Source: ${c.source}`);
|
|
74
|
+
|
|
75
|
+
middleware.get(c.route, async (req, res) => {
|
|
76
|
+
const url = c.source.startsWith("http") ? new URL(c.source) : null;
|
|
77
|
+
const domain = url?.origin;
|
|
78
|
+
const repairedParams = repairParams(req.params);
|
|
79
|
+
const sourceFilename = compile(url?.pathname ?? c.source, {})(repairedParams);
|
|
80
|
+
const generatedFilename = (process.env.UXF_RESIZER_GENERATE_PATH ?? process.cwd()) + req.path;
|
|
81
|
+
|
|
82
|
+
if (existsSync(generatedFilename)) {
|
|
83
|
+
return res.sendFile(generatedFilename);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
log("----------------------------------------");
|
|
87
|
+
log(repairedParams);
|
|
88
|
+
log(`Source filename: ${sourceFilename}`);
|
|
89
|
+
log(`Generated filename: ${generatedFilename}`);
|
|
90
|
+
|
|
91
|
+
const generatedDirectory = dirname(generatedFilename);
|
|
92
|
+
if (!existsSync(generatedDirectory)) {
|
|
93
|
+
mkdirSync(generatedDirectory, { recursive: true });
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const params = req.params;
|
|
97
|
+
|
|
98
|
+
const sourceFile = domain ? await (await fetch(domain + sourceFilename)).buffer() : sourceFilename;
|
|
99
|
+
|
|
100
|
+
if (params.toFormat === "svg") {
|
|
101
|
+
fs.writeFileSync(generatedFilename, sourceFile);
|
|
102
|
+
return res.sendFile(generatedFilename);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const pipeline = await sharp(sourceFile)
|
|
106
|
+
.rotate()
|
|
107
|
+
.resize({
|
|
108
|
+
width: getWidth(params),
|
|
109
|
+
height: getHeight(params),
|
|
110
|
+
background: getBackground(params),
|
|
111
|
+
fastShrinkOnLoad: false,
|
|
112
|
+
fit: getFit(params),
|
|
113
|
+
position: getPosition(params),
|
|
114
|
+
withoutEnlargement: getWithoutEnlargement(params),
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
const trim = getTrim(params);
|
|
118
|
+
if (trim !== undefined) {
|
|
119
|
+
pipeline.trim(trim);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const quality = getQuality(params);
|
|
123
|
+
|
|
124
|
+
switch (params.toFormat) {
|
|
125
|
+
case "webp":
|
|
126
|
+
pipeline.webp({ quality });
|
|
127
|
+
res.setHeader("Content-Type", "image/webp");
|
|
128
|
+
break;
|
|
129
|
+
case "png":
|
|
130
|
+
pipeline.png({ progressive: true, quality });
|
|
131
|
+
res.setHeader("Content-Type", "image/png");
|
|
132
|
+
break;
|
|
133
|
+
case "avif":
|
|
134
|
+
pipeline.avif({ quality });
|
|
135
|
+
res.setHeader("Content-Type", "image/avif");
|
|
136
|
+
break;
|
|
137
|
+
default:
|
|
138
|
+
pipeline.jpeg({ progressive: true, quality });
|
|
139
|
+
res.setHeader("Content-Type", "image/jpeg");
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
pipeline
|
|
144
|
+
.withMetadata({ density: 72 })
|
|
145
|
+
.toColorspace("srgb")
|
|
146
|
+
.toFile(generatedFilename)
|
|
147
|
+
.then(() => res.sendFile(generatedFilename))
|
|
148
|
+
.catch((e) => {
|
|
149
|
+
log(e);
|
|
150
|
+
return res.sendStatus(404);
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
return middleware;
|
|
156
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uxf/resizer",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "10.0.0-beta.79",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"description": "UXF Resizer",
|
|
6
6
|
"author": "UXFans <dev@uxf.cz>",
|
|
@@ -26,32 +26,15 @@
|
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@types/express": "^4.17.13",
|
|
29
|
-
"@types/jest": "^27.0.1",
|
|
30
29
|
"@types/node-fetch": "^2.5.12",
|
|
31
30
|
"@types/path-to-regexp": "^1.7.0",
|
|
32
31
|
"@types/sharp": "^0.29.5",
|
|
33
|
-
"@types/supertest": "^2.0.11",
|
|
34
|
-
"@typescript-eslint/eslint-plugin": "^5.11.0",
|
|
35
|
-
"@uxf/eslint-config": "^1.2.3",
|
|
36
32
|
"concurrently": "^7.0.0",
|
|
37
|
-
"crypto-js": "^4.1.1"
|
|
38
|
-
"eslint": "^8.8.0",
|
|
39
|
-
"eslint-config-prettier": "^8.3.0",
|
|
40
|
-
"eslint-plugin-prettier": "^4.0.0",
|
|
41
|
-
"jest": "^27.0.6",
|
|
42
|
-
"jest-junit": "^13.0.0",
|
|
43
|
-
"prettier": "^2.3.2",
|
|
44
|
-
"supertest": "^6.1.5",
|
|
45
|
-
"ts-jest": "^27.0.5",
|
|
46
|
-
"ts-node": "^10.2.0",
|
|
47
|
-
"typescript": "^4.3.5"
|
|
33
|
+
"crypto-js": "^4.1.1"
|
|
48
34
|
},
|
|
49
35
|
"scripts": {
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"typecheck": "./node_modules/.bin/tsc --noEmit --skipLibCheck",
|
|
54
|
-
"dev": "./node_modules/.bin/ts-node ./index.ts",
|
|
55
|
-
"test": "concurrently \"yarn run lint\" \"yarn run typecheck\" \"yarn prettier ./**/*.ts --check\""
|
|
36
|
+
"build": "tsc -P tsconfig.json",
|
|
37
|
+
"dev": "ts-node ./bin/uxf-resizer.ts",
|
|
38
|
+
"typecheck": "tsc --noEmit --skipLibCheck"
|
|
56
39
|
}
|
|
57
|
-
}
|
|
40
|
+
}
|
package/tsconfig.json
ADDED
package/.gitlab-ci.yml
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
stages:
|
|
2
|
-
- test
|
|
3
|
-
- release
|
|
4
|
-
|
|
5
|
-
test:
|
|
6
|
-
image: node:16-buster-slim
|
|
7
|
-
stage: test
|
|
8
|
-
tags:
|
|
9
|
-
- pluto-docker
|
|
10
|
-
script:
|
|
11
|
-
- npm install
|
|
12
|
-
- npm run test
|
|
13
|
-
except:
|
|
14
|
-
- master
|
|
15
|
-
- beta
|
|
16
|
-
|
|
17
|
-
release:
|
|
18
|
-
image: node:16-buster-slim
|
|
19
|
-
stage: release
|
|
20
|
-
tags:
|
|
21
|
-
- pluto-docker
|
|
22
|
-
before_script:
|
|
23
|
-
- apt-get update && apt-get install -y --no-install-recommends git-core ca-certificates
|
|
24
|
-
- npm install -g semantic-release @semantic-release/gitlab
|
|
25
|
-
script:
|
|
26
|
-
- npm install
|
|
27
|
-
- npm run test
|
|
28
|
-
- npm run build
|
|
29
|
-
- semantic-release --debug
|
|
30
|
-
only:
|
|
31
|
-
- master
|
|
32
|
-
- beta
|
package/.releaserc.yml
DELETED
|
Binary file
|