@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 CHANGED
@@ -1,10 +1,76 @@
1
1
  # @uxf/resizer
2
2
  [![npm](https://img.shields.io/npm/v/@uxf/resizer)](https://www.npmjs.com/package/@uxf/resizer)
3
3
 
4
+
5
+ ## Installation
4
6
  ```
5
- // install resizer
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
- // run resizer
9
- UXF_RESIZER_SOURCE=/path/to/source/directory UXF_RESIZER_GENERATED=/path/to/generated/directory uxf-resizer
74
+ Options:
75
+ --version Show version number [boolean]
10
76
  ```
@@ -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 (_a) {
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("UXF Resizer\nUsage:\n uxf-resizer\n \nEnvironment variables:\n UXF_RESIZER_SOURCE_PATH - required\n UXF_RESIZER_GENERATE_PATH - required");
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
- help = yargs_1.default.parse(process_1.argv.slice(2)).help;
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.log("Source directory must be set. Use environment variable UXF_RESIZER_SOURCE.");
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.log("Generated directory must be set. Use environment variable UXF_RESIZER_GENERATED.");
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
- var app = index_1.createApp(sourcePath_1, generatePath_1);
74
- var listenedApp = app.listen(5000, function () {
75
- // eslint-disable-next-line no-console
76
- console.log("SOURCE_PATH=" + sourcePath_1);
77
- // eslint-disable-next-line no-console
78
- console.log("GENERATED_PATH=" + generatePath_1);
79
- // eslint-disable-next-line no-console
80
- console.log("Image resizer app listening on port 5000");
81
- if (process.env.NODE_ENV !== "production") {
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("Example image: http://localhost:5000/generated/namespace/i/m/img_300_500_cv_c_t_nt_png.png");
84
- }
85
- app.on("close", function () {
86
- listenedApp.close();
87
- resolve(true);
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
- return express_1.default().get("/generated/:namespace/:p1/:p2/:uuid[_]:width(\\d+)[_]:height(\\d+)[_]:fit[_]:position[_]:background[_]:trim[_]:ext.:toFormat", function (req, res) { return __awaiter(void 0, void 0, void 0, function () {
90
- var _a, background, ext, fit, toFormat, height, namespace, position, trim, uuid, width, filename, dirPath, filePath, pipeline, contentType, cacheMaxAge;
91
- return __generator(this, function (_b) {
92
- switch (_b.label) {
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, background = _a.background, ext = _a.ext, fit = _a.fit, toFormat = _a.toFormat, height = _a.height, namespace = _a.namespace, position = _a.position, trim = _a.trim, uuid = _a.uuid, width = _a.width;
95
- filename = uuid + "_" + width + "_" + height + "_" + fit + "_" + position + "_" + background + "_" + trim + "_" + ext + "." + toFormat;
96
- dirPath = [generatePath, namespace, uuid.charAt(0), uuid.charAt(1)].join("/");
97
- filePath = dirPath + "/" + filename;
98
- if (fs.existsSync(filePath)) {
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
- if (!fs_1.existsSync(dirPath)) {
102
- fs_1.mkdirSync(dirPath, { recursive: true });
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
- return [4 /*yield*/, sharp_1.default(sourcePath + "/" + namespace + "/" + uuid + "." + ext)
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: ext !== "svg",
140
+ withoutEnlargement: originalExtension !== "svg",
112
141
  })];
113
142
  case 1:
114
- pipeline = _b.sent();
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(filePath)
143
- .then(function () { return res.sendFile(filePath); })
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.0.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
- "@uxf/eslint-config": "^1.0.3",
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": "^7.32.0",
36
- "eslint-config-prettier": "8.3.0",
37
- "eslint-plugin-prettier": "3.4.0",
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",