@uxf/resizer 1.0.2 → 1.1.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.
package/README.md CHANGED
@@ -5,6 +5,20 @@
5
5
  // install resizer
6
6
  yarn global add @uxf/resizer
7
7
 
8
- // run resizer
9
- UXF_RESIZER_SOURCE=/path/to/source/directory UXF_RESIZER_GENERATED=/path/to/generated/directory uxf-resizer
8
+ Environment variables:
9
+ UXF_RESIZER_SOURCE_PATH - required
10
+ UXF_RESIZER_GENERATE_PATH - required
11
+
12
+ Options
13
+ --disable-namespace [boolean] [default: false]
14
+ --source-folder-depth Depth of the source folder [number] [default: 2]
15
+ --generated-folder-depth Depth of the generated folder
16
+ [number] [default: 2]
17
+ --filename-regexp Filename regexp (see:
18
+ https://github.com/pillarjs/path-to-regexp)
19
+ [string]
20
+ -h, --help Show help [boolean]
21
+
22
+ Options:
23
+ --version Show version number [boolean]
10
24
  ```
@@ -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,19 +55,45 @@ 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
@@ -69,24 +106,37 @@ var uxfResizer = function () { return __awaiter(void 0, void 0, void 0, function
69
106
  console.log("Generated directory must be set. Use environment variable UXF_RESIZER_GENERATED.");
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/i/m/img_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,60 @@ 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-z\\-]+)_: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
+ throw new Error("Wrong filename.");
100
111
  }
101
- if (!fs_1.existsSync(dirPath)) {
102
- fs_1.mkdirSync(dirPath, { recursive: true });
112
+ _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;
113
+ generatedPxDirectories = generatedPxArray.map(function (_, index) { return originalFilename.charAt(index); });
114
+ generatedDirectoryPart = disableNamespace ? generatePath : generatePath + "/" + namespace;
115
+ generatedDirectory = generatedPxDirectories.length > 0
116
+ ? generatedDirectoryPart + "/" + generatedPxDirectories.join("/")
117
+ : generatedDirectoryPart;
118
+ generatedFilePath = generatedDirectory + "/" + filename;
119
+ sourcePxDirectories = sourcePxArray.map(function (_, index) { return originalFilename.charAt(index); });
120
+ sourceDirectoryPart = disableNamespace ? sourcePath : sourcePath + "/" + namespace;
121
+ sourceDirectory = sourcePxDirectories.length > 0
122
+ ? sourceDirectoryPart + "/" + sourcePxDirectories.join("/")
123
+ : sourceDirectoryPart;
124
+ sourceFilename = originalFilename + "." + originalExtension;
125
+ sourceFilePath = sourceDirectory + "/" + sourceFilename;
126
+ if (fs.existsSync(generatedFilePath)) {
127
+ return [2 /*return*/, res.sendFile(generatedFilePath)];
103
128
  }
104
- return [4 /*yield*/, sharp_1.default(sourcePath + "/" + namespace + "/" + uuid.charAt(0) + "/" + uuid.charAt(1) + "/" + uuid + "." + ext)
129
+ if (!fs_1.existsSync(generatedDirectory)) {
130
+ fs_1.mkdirSync(generatedDirectory, { recursive: true });
131
+ }
132
+ return [4 /*yield*/, sharp_1.default(sourceFilePath)
105
133
  .rotate()
106
134
  .resize(width ? Number(width) : undefined, height ? Number(height) : undefined, {
107
135
  background: background === "t" ? "transparent" : "#" + background,
108
136
  fastShrinkOnLoad: false,
109
137
  fit: FIT_OPTIONS[fit] || "cover",
110
138
  position: POSITION_OPTIONS[position] || "centre",
111
- withoutEnlargement: ext !== "svg",
139
+ withoutEnlargement: originalExtension !== "svg",
112
140
  })];
113
141
  case 1:
114
- pipeline = _b.sent();
142
+ pipeline = _c.sent();
115
143
  if (isTrim(trim)) {
116
144
  pipeline.trim(getTrimValue(trim));
117
145
  }
@@ -139,8 +167,8 @@ var createApp = function (sourcePath, generatePath) {
139
167
  pipeline
140
168
  .withMetadata({ density: 72 })
141
169
  .toColorspace("srgb")
142
- .toFile(filePath)
143
- .then(function () { return res.sendFile(filePath); })
170
+ .toFile(generatedFilePath)
171
+ .then(function () { return res.sendFile(generatedFilePath); })
144
172
  .catch(function (e) {
145
173
  // eslint-disable-next-line no-console
146
174
  console.log(e);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uxf/resizer",
3
- "version": "1.0.2",
3
+ "version": "1.1.0",
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,6 +28,7 @@
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
  "@uxf/eslint-config": "^1.0.3",