@uxf/resizer 1.0.1 → 1.1.2
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 +69 -3
- package/bin/uxf-resizer.js +72 -22
- package/index.js +47 -18
- package/package.json +8 -6
package/README.md
CHANGED
|
@@ -1,10 +1,76 @@
|
|
|
1
1
|
# @uxf/resizer
|
|
2
2
|
[](https://www.npmjs.com/package/@uxf/resizer)
|
|
3
3
|
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
4
6
|
```
|
|
5
|
-
//
|
|
7
|
+
// global installation
|
|
6
8
|
yarn global add @uxf/resizer
|
|
9
|
+
export PATH="$(yarn global bin):$PATH"
|
|
10
|
+
UXF_RESIZER_SOURCE_PATH=${PWD}/example/source UXF_RESIZER_GENERATE_PATH=${PWD}/example/generated uxf-resizer
|
|
11
|
+
|
|
12
|
+
// local installation
|
|
13
|
+
yarn add @uxf/resizer
|
|
14
|
+
UXF_RESIZER_SOURCE_PATH=${PWD}/example/source UXF_RESIZER_GENERATE_PATH=${PWD}/example/generated ./node-modules/.bin/uxf-resizer
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Default filename regexp
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
:originalFilename([a-f0-9\-]+)_:width(\d+)_:height(\d+)_:fit([a-z]+)_:position([a-z]+)_:background([a-z]+)_:trim([a-z]+)_:originalExtension.:toFormat
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### fit
|
|
24
|
+
- `cv`: cover,
|
|
25
|
+
- `f`: fill,
|
|
26
|
+
- `cn`: contain,
|
|
27
|
+
- `in`: inside,
|
|
28
|
+
- `out`: outside,
|
|
29
|
+
|
|
30
|
+
### position
|
|
31
|
+
- `a`: attention
|
|
32
|
+
- `b`: bottom
|
|
33
|
+
- `c`: centre
|
|
34
|
+
- `e`: entropy
|
|
35
|
+
- `l`: left
|
|
36
|
+
- `lb`: left bottom
|
|
37
|
+
- `lt`: left top
|
|
38
|
+
- `r`: right
|
|
39
|
+
- `rb`: right bottom
|
|
40
|
+
- `rt`: right top
|
|
41
|
+
- `t`: top
|
|
42
|
+
|
|
43
|
+
### background
|
|
44
|
+
- `t`: transparent
|
|
45
|
+
- `FFFFF`: white (or other hex color)
|
|
46
|
+
|
|
47
|
+
### trim
|
|
48
|
+
- `nt`: not trim
|
|
49
|
+
- `number`: number value
|
|
50
|
+
|
|
51
|
+
### toFormat
|
|
52
|
+
- `webp | png | avif | or other`
|
|
53
|
+
|
|
54
|
+
## Help
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
Usage:
|
|
58
|
+
uxf-resizer
|
|
59
|
+
|
|
60
|
+
Environment variables:
|
|
61
|
+
UXF_RESIZER_SOURCE_PATH - required
|
|
62
|
+
UXF_RESIZER_GENERATE_PATH - required
|
|
63
|
+
|
|
64
|
+
Options
|
|
65
|
+
--disable-namespace [boolean] [default: false]
|
|
66
|
+
--source-folder-depth Depth of the source folder [number] [default: 2]
|
|
67
|
+
--generated-folder-depth Depth of the generated folder
|
|
68
|
+
[number] [default: 2]
|
|
69
|
+
--filename-regexp Filename regexp (see:
|
|
70
|
+
https://github.com/pillarjs/path-to-regexp)
|
|
71
|
+
[string]
|
|
72
|
+
-h, --help Show help [boolean]
|
|
7
73
|
|
|
8
|
-
|
|
9
|
-
|
|
74
|
+
Options:
|
|
75
|
+
--version Show version number [boolean]
|
|
10
76
|
```
|
package/bin/uxf-resizer.js
CHANGED
|
@@ -36,6 +36,17 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
|
36
36
|
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
37
37
|
}
|
|
38
38
|
};
|
|
39
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
40
|
+
var t = {};
|
|
41
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
42
|
+
t[p] = s[p];
|
|
43
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
44
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
45
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
46
|
+
t[p[i]] = s[p[i]];
|
|
47
|
+
}
|
|
48
|
+
return t;
|
|
49
|
+
};
|
|
39
50
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
40
51
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
41
52
|
};
|
|
@@ -44,49 +55,88 @@ var process_1 = require("process");
|
|
|
44
55
|
var yargs_1 = __importDefault(require("yargs"));
|
|
45
56
|
var index_1 = require("../index");
|
|
46
57
|
var uxfResizer = function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
47
|
-
var help, sourcePath_1, generatePath_1;
|
|
48
|
-
return __generator(this, function (
|
|
58
|
+
var _a, help, options, sourceFolderDepth_1, generatedFolderDepth_1, filenameRegexp_1, disableNamespace_1, sourcePath_1, generatePath_1;
|
|
59
|
+
return __generator(this, function (_b) {
|
|
49
60
|
yargs_1.default.command("$0", "UXF Resizer", function (yargs) {
|
|
50
|
-
yargs.demandCommand(0, 0).usage("
|
|
61
|
+
yargs.demandCommand(0, 0).usage("\nUsage:\n uxf-resizer\n \nEnvironment variables:\n UXF_RESIZER_SOURCE_PATH - required\n UXF_RESIZER_GENERATE_PATH - required");
|
|
62
|
+
})
|
|
63
|
+
.option("disable-namespace", {
|
|
64
|
+
group: "Options",
|
|
65
|
+
default: false,
|
|
66
|
+
type: "boolean",
|
|
67
|
+
})
|
|
68
|
+
.option("source-folder-depth", {
|
|
69
|
+
group: "Options",
|
|
70
|
+
description: "Depth of the source folder",
|
|
71
|
+
default: 2,
|
|
72
|
+
type: "number",
|
|
73
|
+
})
|
|
74
|
+
.option("generated-folder-depth", {
|
|
75
|
+
group: "Options",
|
|
76
|
+
description: "Depth of the generated folder",
|
|
77
|
+
default: 2,
|
|
78
|
+
type: "number",
|
|
79
|
+
})
|
|
80
|
+
.option("filename-regexp", {
|
|
81
|
+
group: "Options",
|
|
82
|
+
description: "Filename regexp (see: https://github.com/pillarjs/path-to-regexp)",
|
|
83
|
+
type: "string",
|
|
51
84
|
})
|
|
52
85
|
.option("h", { alias: "help", group: "Options" })
|
|
53
86
|
.strict(false)
|
|
54
87
|
.exitProcess(false);
|
|
55
88
|
try {
|
|
56
|
-
|
|
89
|
+
_a = yargs_1.default.parse(process_1.argv.slice(2)), help = _a.help, options = __rest(_a, ["help"]);
|
|
57
90
|
if (help) {
|
|
58
91
|
return [2 /*return*/, 0];
|
|
59
92
|
}
|
|
93
|
+
sourceFolderDepth_1 = options["source-folder-depth"];
|
|
94
|
+
generatedFolderDepth_1 = options["generated-folder-depth"];
|
|
95
|
+
filenameRegexp_1 = options["filename-regexp"];
|
|
96
|
+
disableNamespace_1 = options["disable-namespace"];
|
|
60
97
|
sourcePath_1 = process_1.env.UXF_RESIZER_SOURCE_PATH;
|
|
61
98
|
if (!sourcePath_1) {
|
|
62
99
|
// eslint-disable-next-line no-console
|
|
63
|
-
console.
|
|
100
|
+
console.error("Source directory must be set. Use environment variable UXF_RESIZER_SOURCE_PATH.");
|
|
64
101
|
return [2 /*return*/, 1];
|
|
65
102
|
}
|
|
66
103
|
generatePath_1 = process_1.env.UXF_RESIZER_GENERATE_PATH;
|
|
67
104
|
if (!generatePath_1) {
|
|
68
105
|
// eslint-disable-next-line no-console
|
|
69
|
-
console.
|
|
106
|
+
console.error("Generated directory must be set. Use environment variable UXF_RESIZER_GENERATED_PATH.");
|
|
70
107
|
return [2 /*return*/, 1];
|
|
71
108
|
}
|
|
72
|
-
return [2 /*return*/, new Promise(function (resolve) {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
109
|
+
return [2 /*return*/, new Promise(function (resolve, reject) {
|
|
110
|
+
try {
|
|
111
|
+
var app_1 = index_1.createApp(sourcePath_1, generatePath_1, sourceFolderDepth_1, generatedFolderDepth_1, filenameRegexp_1, disableNamespace_1);
|
|
112
|
+
var listenedApp_1 = app_1.listen(3000, function () {
|
|
113
|
+
// eslint-disable-next-line no-console
|
|
114
|
+
console.log("\nSOURCE_PATH: " + sourcePath_1);
|
|
115
|
+
// eslint-disable-next-line no-console
|
|
116
|
+
console.log("GENERATED_PATH: " + generatePath_1);
|
|
117
|
+
// eslint-disable-next-line no-console
|
|
118
|
+
console.log("Source folder depth: " + sourceFolderDepth_1);
|
|
119
|
+
// eslint-disable-next-line no-console
|
|
120
|
+
console.log("Generated folder depth: " + generatedFolderDepth_1);
|
|
121
|
+
// eslint-disable-next-line no-console
|
|
122
|
+
console.log("Filename regexp: " + filenameRegexp_1);
|
|
123
|
+
// eslint-disable-next-line no-console
|
|
124
|
+
console.log("Namespace: " + (disableNamespace_1 ? "disabled" : "enabled"));
|
|
82
125
|
// eslint-disable-next-line no-console
|
|
83
|
-
console.log("
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
126
|
+
console.log("\nImage resizer app listening on port 3000");
|
|
127
|
+
if (process.env.NODE_ENV !== "production") {
|
|
128
|
+
// eslint-disable-next-line no-console
|
|
129
|
+
console.log("Example image: http://localhost:3000/generated/namespace/a/b/ab7dea3a-ba56-4fa0-a407-d9c4a236d153_300_500_cv_c_t_nt_png.png");
|
|
130
|
+
}
|
|
131
|
+
app_1.on("close", function () {
|
|
132
|
+
listenedApp_1.close();
|
|
133
|
+
resolve(true);
|
|
134
|
+
});
|
|
88
135
|
});
|
|
89
|
-
}
|
|
136
|
+
}
|
|
137
|
+
catch (e) {
|
|
138
|
+
reject(e);
|
|
139
|
+
}
|
|
90
140
|
})];
|
|
91
141
|
}
|
|
92
142
|
catch (e) {
|
package/index.js
CHANGED
|
@@ -63,6 +63,7 @@ var express_1 = __importDefault(require("express"));
|
|
|
63
63
|
var fs_1 = require("fs");
|
|
64
64
|
var fs = __importStar(require("fs"));
|
|
65
65
|
var sharp_1 = __importDefault(require("sharp"));
|
|
66
|
+
var path_to_regexp_1 = require("path-to-regexp");
|
|
66
67
|
var FIT_OPTIONS = {
|
|
67
68
|
cv: "cover",
|
|
68
69
|
f: "fill",
|
|
@@ -85,33 +86,61 @@ var POSITION_OPTIONS = {
|
|
|
85
86
|
};
|
|
86
87
|
var isTrim = function (value) { return value !== "nt"; };
|
|
87
88
|
var getTrimValue = function (value) { return (isTrim(value) ? Number(value) : 0); };
|
|
88
|
-
var createApp = function (sourcePath, generatePath) {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
89
|
+
var createApp = function (sourcePath, generatePath, sourceFolderDepth, generatedFolderDepth, filenameRegexp, disableNamespace) {
|
|
90
|
+
if (filenameRegexp === void 0) { filenameRegexp = ":originalFilename([a-f0-9\\-]+)_:width(\\d+)_:height(\\d+)_:fit([a-z]+)_:position([a-z]+)_:background([a-z]+)_:trim([a-z]+)_:originalExtension.:toFormat"; }
|
|
91
|
+
var parseFilename = path_to_regexp_1.match(filenameRegexp, { decode: decodeURIComponent });
|
|
92
|
+
var generatedPxArray = Array.from({ length: generatedFolderDepth }, function (_, i) { return ":p" + (i + 1); });
|
|
93
|
+
var sourcePxArray = Array.from({ length: sourceFolderDepth }, function (_, i) { return ":p" + (i + 1); });
|
|
94
|
+
var routeRegexpBase = disableNamespace ? "/generated" : "/generated/:namespace";
|
|
95
|
+
var routeRegexp = generatedFolderDepth > 0
|
|
96
|
+
? routeRegexpBase + "/" + generatedPxArray.join("/") + "/:filename"
|
|
97
|
+
: routeRegexpBase + "/:filename";
|
|
98
|
+
// eslint-disable-next-line no-console
|
|
99
|
+
console.log("\nExpress route regexp: " + routeRegexp);
|
|
100
|
+
// eslint-disable-next-line no-console
|
|
101
|
+
console.log("Filename regexp: " + filenameRegexp);
|
|
102
|
+
return express_1.default().get(routeRegexp, function (req, res) { return __awaiter(void 0, void 0, void 0, function () {
|
|
103
|
+
var _a, filename, namespace, parsedFilename, _b, originalFilename, originalExtension, fit, height, position, toFormat, trim, width, background, generatedPxDirectories, generatedDirectoryPart, generatedDirectory, generatedFilePath, sourcePxDirectories, sourceDirectoryPart, sourceDirectory, sourceFilename, sourceFilePath, pipeline, contentType, cacheMaxAge;
|
|
104
|
+
return __generator(this, function (_c) {
|
|
105
|
+
switch (_c.label) {
|
|
93
106
|
case 0:
|
|
94
|
-
_a = req.params,
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
return [2 /*return*/, res.sendFile(filePath)];
|
|
107
|
+
_a = req.params, filename = _a.filename, namespace = _a.namespace;
|
|
108
|
+
parsedFilename = parseFilename(filename);
|
|
109
|
+
if (!parsedFilename) {
|
|
110
|
+
process.stdout.write("Filename '" + filename + "' not found.");
|
|
111
|
+
return [2 /*return*/, res.sendStatus(404)];
|
|
100
112
|
}
|
|
101
|
-
|
|
102
|
-
|
|
113
|
+
_b = parsedFilename.params, originalFilename = _b.originalFilename, originalExtension = _b.originalExtension, fit = _b.fit, height = _b.height, position = _b.position, toFormat = _b.toFormat, trim = _b.trim, width = _b.width, background = _b.background;
|
|
114
|
+
generatedPxDirectories = generatedPxArray.map(function (_, index) { return originalFilename.charAt(index); });
|
|
115
|
+
generatedDirectoryPart = disableNamespace ? generatePath : generatePath + "/" + namespace;
|
|
116
|
+
generatedDirectory = generatedPxDirectories.length > 0
|
|
117
|
+
? generatedDirectoryPart + "/" + generatedPxDirectories.join("/")
|
|
118
|
+
: generatedDirectoryPart;
|
|
119
|
+
generatedFilePath = generatedDirectory + "/" + filename;
|
|
120
|
+
sourcePxDirectories = sourcePxArray.map(function (_, index) { return originalFilename.charAt(index); });
|
|
121
|
+
sourceDirectoryPart = disableNamespace ? sourcePath : sourcePath + "/" + namespace;
|
|
122
|
+
sourceDirectory = sourcePxDirectories.length > 0
|
|
123
|
+
? sourceDirectoryPart + "/" + sourcePxDirectories.join("/")
|
|
124
|
+
: sourceDirectoryPart;
|
|
125
|
+
sourceFilename = originalFilename + "." + originalExtension;
|
|
126
|
+
sourceFilePath = sourceDirectory + "/" + sourceFilename;
|
|
127
|
+
if (fs.existsSync(generatedFilePath)) {
|
|
128
|
+
return [2 /*return*/, res.sendFile(generatedFilePath)];
|
|
103
129
|
}
|
|
104
|
-
|
|
130
|
+
if (!fs_1.existsSync(generatedDirectory)) {
|
|
131
|
+
fs_1.mkdirSync(generatedDirectory, { recursive: true });
|
|
132
|
+
}
|
|
133
|
+
return [4 /*yield*/, sharp_1.default(sourceFilePath)
|
|
105
134
|
.rotate()
|
|
106
135
|
.resize(width ? Number(width) : undefined, height ? Number(height) : undefined, {
|
|
107
136
|
background: background === "t" ? "transparent" : "#" + background,
|
|
108
137
|
fastShrinkOnLoad: false,
|
|
109
138
|
fit: FIT_OPTIONS[fit] || "cover",
|
|
110
139
|
position: POSITION_OPTIONS[position] || "centre",
|
|
111
|
-
withoutEnlargement:
|
|
140
|
+
withoutEnlargement: originalExtension !== "svg",
|
|
112
141
|
})];
|
|
113
142
|
case 1:
|
|
114
|
-
pipeline =
|
|
143
|
+
pipeline = _c.sent();
|
|
115
144
|
if (isTrim(trim)) {
|
|
116
145
|
pipeline.trim(getTrimValue(trim));
|
|
117
146
|
}
|
|
@@ -139,8 +168,8 @@ var createApp = function (sourcePath, generatePath) {
|
|
|
139
168
|
pipeline
|
|
140
169
|
.withMetadata({ density: 72 })
|
|
141
170
|
.toColorspace("srgb")
|
|
142
|
-
.toFile(
|
|
143
|
-
.then(function () { return res.sendFile(
|
|
171
|
+
.toFile(generatedFilePath)
|
|
172
|
+
.then(function () { return res.sendFile(generatedFilePath); })
|
|
144
173
|
.catch(function (e) {
|
|
145
174
|
// eslint-disable-next-line no-console
|
|
146
175
|
console.log(e);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uxf/resizer",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"description": "UXF Resizer",
|
|
6
6
|
"author": "UXFans <dev@uxf.cz>",
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"express": "^4.17.1",
|
|
20
20
|
"fs": "^0.0.1-security",
|
|
21
|
+
"path-to-regexp": "^6.2.0",
|
|
21
22
|
"process": "^0.11.10",
|
|
22
23
|
"sharp": "^0.28.3",
|
|
23
24
|
"yargs": "^17.2.1"
|
|
@@ -27,15 +28,16 @@
|
|
|
27
28
|
"@types/express": "^4.17.13",
|
|
28
29
|
"@types/jest": "^27.0.1",
|
|
29
30
|
"@types/node-fetch": "^2.5.12",
|
|
31
|
+
"@types/path-to-regexp": "^1.7.0",
|
|
30
32
|
"@types/sharp": "^0.28.5",
|
|
31
33
|
"@types/supertest": "^2.0.11",
|
|
32
|
-
"@
|
|
34
|
+
"@typescript-eslint/eslint-plugin": "^5.11.0",
|
|
35
|
+
"@uxf/eslint-config": "^1.2.3",
|
|
33
36
|
"concurrently": "^6.2.1",
|
|
34
37
|
"crypto-js": "^4.1.1",
|
|
35
|
-
"eslint": "^
|
|
36
|
-
"eslint-config-prettier": "8.3.0",
|
|
37
|
-
"eslint-plugin-prettier": "
|
|
38
|
-
"eslint-plugin-react": "7.24.0",
|
|
38
|
+
"eslint": "^8.8.0",
|
|
39
|
+
"eslint-config-prettier": "^8.3.0",
|
|
40
|
+
"eslint-plugin-prettier": "^4.0.0",
|
|
39
41
|
"jest": "^27.0.6",
|
|
40
42
|
"jest-junit": "^12.2.0",
|
|
41
43
|
"node-fetch": "^2.6.1",
|