@automattic/vip 3.6.0 → 3.7.1

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.
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+
3
+ exports.__esModule = true;
4
+ exports.getMediaImportConfig = getMediaImportConfig;
5
+ var _graphqlTag = _interopRequireDefault(require("graphql-tag"));
6
+ var _api = _interopRequireDefault(require("../api"));
7
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
8
+ const IMPORT_MEDIA_CONFIG_QUERY = (0, _graphqlTag.default)`
9
+ {
10
+ mediaImportConfig {
11
+ fileNameCharCount
12
+ fileSizeLimitInBytes
13
+ allowedFileTypes
14
+ }
15
+ }
16
+ `;
17
+ async function getMediaImportConfig() {
18
+ const api = (0, _api.default)();
19
+ const response = await api.query({
20
+ query: IMPORT_MEDIA_CONFIG_QUERY,
21
+ variables: {},
22
+ fetchPolicy: 'network-only'
23
+ });
24
+ return response?.data?.mediaImportConfig;
25
+ }
@@ -1,13 +1,119 @@
1
1
  "use strict";
2
2
 
3
3
  exports.__esModule = true;
4
- exports.summaryLogs = exports.logErrorsForInvalidFilenames = exports.logErrorsForInvalidFileTypes = exports.logErrorsForIntermediateImages = exports.isFileSanitized = exports.folderStructureValidation = exports.findNestedDirectories = exports.doesImageHaveExistingSource = exports.acceptedExtensions = void 0;
4
+ exports.summaryLogs = exports.logErrors = exports.isFileSanitized = exports.isDirectory = exports.folderStructureValidation = exports.findNestedDirectories = exports.doesImageHaveExistingSource = exports.ValidateFilesErrors = void 0;
5
+ exports.validateFiles = validateFiles;
5
6
  var _chalk = _interopRequireDefault(require("chalk"));
6
7
  var _fs = _interopRequireDefault(require("fs"));
7
8
  var _path = _interopRequireDefault(require("path"));
8
9
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
9
- // Accepted media file extensions
10
- const acceptedExtensions = exports.acceptedExtensions = ['jpg', 'jpeg', 'jpe', 'gif', 'png', 'bmp', 'svg', 'tiff', 'tif', 'ico', 'asf', 'asx', 'wmv', 'wmx', 'wm', 'avi', 'divx', 'mov', 'qt', 'mpeg', 'mpg', 'mpe', 'mp4', 'm4v', 'ogv', 'webm', 'mkv', '3gp', '3gpp', '3g2', '3gp2', 'txt', 'asc', 'c', 'cc', 'h', 'srt', 'csv', 'tsv', 'ics', 'rtx', 'css', 'vtt', 'dfxp', 'mp3', 'm4a', 'm4b', 'ra', 'ram', 'wav', 'ogg', 'oga', 'mid', 'midi', 'wma', 'wax', 'mka', 'rtf', 'js', 'pdf', 'class', 'psd', 'xcf', 'doc', 'pot', 'pps', 'ppt', 'wri', 'xla', 'xls', 'xlt', 'xlw', 'mdb', 'mpp', 'docx', 'docm', 'dotx', 'dotm', 'xlsx', 'xlsm', 'xlsb', 'xltx', 'xltm', 'xlam', 'pptx', 'pptm', 'ppsx', 'ppsm', 'potx', 'potm', 'ppam', 'sldx', 'sldm', 'onetoc', ' onetoc2', 'onetmp', 'onepkg', 'oxps', 'xps', 'odt', 'odp', 'ods', 'odg', 'odc', 'odb', 'odf', 'webp', 'wp', 'wpd', 'key', 'numbers', 'pages'];
10
+ /**
11
+ * External dependencies
12
+ */
13
+ /**
14
+ * Internal dependencies
15
+ */
16
+ let ValidateFilesErrors = exports.ValidateFilesErrors = /*#__PURE__*/function (ValidateFilesErrors) {
17
+ ValidateFilesErrors["INVALID_TYPES"] = "invalid_types";
18
+ ValidateFilesErrors["INTERMEDIATE_IMAGES"] = "intermediate_images";
19
+ ValidateFilesErrors["INVALID_SIZES"] = "invalid_sizes";
20
+ ValidateFilesErrors["INVALID_NAMES"] = "invalid_names";
21
+ ValidateFilesErrors["INVALID_NAME_CHARACTER_COUNTS"] = "invalid_name_character_counts";
22
+ return ValidateFilesErrors;
23
+ }({});
24
+ /**
25
+ * File info validation
26
+ *
27
+ * Validate the file info for media files
28
+ */
29
+ async function validateFiles(files, mediaImportConfig) {
30
+ const validationResult = {
31
+ intermediateImagesTotal: 0,
32
+ errorFileTypes: [],
33
+ errorFileNames: [],
34
+ errorFileSizes: [],
35
+ errorFileNamesCharCount: [],
36
+ intermediateImages: {}
37
+ };
38
+ const fileValidationPromises = files.map(async file => {
39
+ const isFolder = await isDirectory(file);
40
+ const fileExtType = getFileExtType(file,
41
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
42
+ mediaImportConfig.allowedFileTypes);
43
+ if (isInvalidFile(fileExtType, isFolder)) {
44
+ validationResult.errorFileTypes.push(file);
45
+ }
46
+
47
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
48
+ if (!isFileSizeValid(file, mediaImportConfig.fileSizeLimitInBytes)) {
49
+ validationResult.errorFileSizes.push(file);
50
+ }
51
+ if (isFileSanitized(file)) {
52
+ validationResult.errorFileNames.push(file);
53
+ }
54
+
55
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
56
+ if (!isFileNameCharCountValid(file, mediaImportConfig?.fileNameCharCount)) {
57
+ validationResult.errorFileNamesCharCount.push(file);
58
+ }
59
+ const original = doesImageHaveExistingSource(file);
60
+ if (original) {
61
+ validationResult.intermediateImagesTotal++;
62
+ if (validationResult.intermediateImages[original]) {
63
+ validationResult.intermediateImages[original] += `, ${file}`;
64
+ } else {
65
+ validationResult.intermediateImages[original] = file;
66
+ }
67
+ }
68
+ });
69
+ await Promise.all(fileValidationPromises);
70
+ return validationResult;
71
+ }
72
+ const isDirectory = async file => {
73
+ const stats = await _fs.default.promises.stat(file);
74
+ return stats.isDirectory();
75
+ };
76
+ exports.isDirectory = isDirectory;
77
+ const getFileExtType = (file, allowedFileTypes) => {
78
+ if (!allowedFileTypes) return {
79
+ ext: null,
80
+ type: null
81
+ };
82
+ return getExtAndType(file, allowedFileTypes);
83
+ };
84
+ const isInvalidFile = (fileExtType, isFolder) => {
85
+ return !fileExtType.type || !fileExtType.ext || isFolder;
86
+ };
87
+ const getExtAndType = (filePath, allowedFileTypes) => {
88
+ const extType = {
89
+ ext: null,
90
+ type: null
91
+ };
92
+ for (const [key, value] of Object.entries(allowedFileTypes)) {
93
+ // Create a regular expression to match the file extension
94
+ // eslint-disable-next-line security/detect-non-literal-regexp
95
+ const regex = new RegExp(`(?:\\.)(${key})$`, 'i');
96
+ const matches = regex.exec(filePath);
97
+ if (matches) {
98
+ extType.type = value;
99
+ extType.ext = matches[1];
100
+ break;
101
+ }
102
+ }
103
+ return extType;
104
+ };
105
+ const isFileSizeValid = (filePathOnDisk, fileSizeLimitInBytes) => {
106
+ const fileStat = _fs.default.statSync(filePathOnDisk);
107
+ return fileSizeLimitInBytes >= fileStat.size;
108
+ };
109
+ const isFileNameCharCountValid = (file, fileNameCharCount) => {
110
+ const filename = _path.default.basename(file);
111
+ return filename.length <= fileNameCharCount;
112
+ };
113
+
114
+ /**
115
+ * End file info validation
116
+ */
11
117
 
12
118
  /**
13
119
  * Character validation global variables
@@ -38,8 +144,8 @@ const recommendedFileStructure = () => {
38
144
  };
39
145
 
40
146
  // Recommend accepted file types
41
- const recommendAcceptableFileTypes = () => {
42
- console.log('Accepted file types: \n\n' + _chalk.default.magenta(`${acceptedExtensions.join(', ')}`));
147
+ const recommendAcceptableFileTypes = allowedFileTypesString => {
148
+ console.log('Accepted file types: \n\n' + _chalk.default.magenta(`${allowedFileTypesString}`));
43
149
  console.log();
44
150
  };
45
151
 
@@ -392,8 +498,8 @@ const isFileSanitized = file => {
392
498
  sanitizedFile = sanitizedFile.replace(regexSpaces, ' ');
393
499
 
394
500
  // Check if the filename has been sanitized
395
- const checkFile = sanitizedFile !== filename;
396
- return checkFile;
501
+
502
+ return sanitizedFile !== filename;
397
503
  };
398
504
 
399
505
  /**
@@ -449,45 +555,50 @@ const doesImageHaveExistingSource = file => {
449
555
  *
450
556
  * Log errors for invalid folders or files
451
557
  */
452
-
453
- // Log errors for files with invalid file extensions and recommend accepted file types
454
558
  exports.doesImageHaveExistingSource = doesImageHaveExistingSource;
455
- const logErrorsForInvalidFileTypes = invalidFiles => {
456
- invalidFiles.forEach(file => {
457
- console.error(_chalk.default.red('✕'), 'File extensions: Invalid file type for file: ', _chalk.default.cyan(`${file}`));
458
- });
459
- console.log();
460
- recommendAcceptableFileTypes();
461
- console.log('------------------------------------------------------------');
462
- console.log();
463
- };
464
-
465
- // Log errors for files with invalid filenames and show a list of accepted/prohibited chars
466
- exports.logErrorsForInvalidFileTypes = logErrorsForInvalidFileTypes;
467
- const logErrorsForInvalidFilenames = invalidFiles => {
559
+ const logErrors = ({
560
+ errorType,
561
+ invalidFiles,
562
+ limit
563
+ }) => {
564
+ if (invalidFiles.length === 0) {
565
+ return;
566
+ }
468
567
  invalidFiles.forEach(file => {
469
- console.error(_chalk.default.red('✕'), 'Character validation: Invalid filename for file: ', _chalk.default.cyan(`${file}`));
568
+ switch (errorType) {
569
+ case ValidateFilesErrors.INVALID_TYPES:
570
+ console.error(_chalk.default.red('✕'), 'File extensions: Invalid file type for file: ', _chalk.default.cyan(`${file}`));
571
+ console.log();
572
+ recommendAcceptableFileTypes(limit);
573
+ break;
574
+ case ValidateFilesErrors.INTERMEDIATE_IMAGES:
575
+ console.error(_chalk.default.red('✕'), 'Intermediate images: Duplicate files found:\n' + 'Original file: ' + _chalk.default.blue(`${file}\n`) + 'Intermediate images: ' + _chalk.default.cyan(`${limit[file]}\n`));
576
+ break;
577
+ case ValidateFilesErrors.INVALID_SIZES:
578
+ console.error(_chalk.default.red('✕'), `File size cannot be more than ${limit / 1024 / 1024 / 1024} GB`, _chalk.default.cyan(`${file}`));
579
+ console.log();
580
+ break;
581
+ case ValidateFilesErrors.INVALID_NAME_CHARACTER_COUNTS:
582
+ console.error(_chalk.default.red('✕'), `File name cannot have more than ${limit} characters`, _chalk.default.cyan(`${file}`));
583
+ break;
584
+ case ValidateFilesErrors.INVALID_NAMES:
585
+ console.error(_chalk.default.red('✕'), 'Character validation: Invalid filename for file: ', _chalk.default.cyan(`${file}`));
586
+ recommendAcceptableFileNames();
587
+ break;
588
+ default:
589
+ console.error(_chalk.default.red('✕'), 'Unknown error type:', errorType);
590
+ }
470
591
  });
471
592
  console.log();
472
- recommendAcceptableFileNames();
473
- console.log('------------------------------------------------------------');
474
- console.log();
475
- };
476
-
477
- // Log errors for intermediate image file duplicates
478
- exports.logErrorsForInvalidFilenames = logErrorsForInvalidFilenames;
479
- const logErrorsForIntermediateImages = obj => {
480
- for (const original in obj) {
481
- console.error(_chalk.default.red('✕'), 'Intermediate images: Duplicate files found:\n' + 'Original file: ' + _chalk.default.blue(`${original}\n`) + 'Intermediate images: ' + _chalk.default.cyan(`${obj[original]}\n`));
482
- }
483
- console.log('------------------------------------------------------------');
484
593
  };
485
- exports.logErrorsForIntermediateImages = logErrorsForIntermediateImages;
594
+ exports.logErrors = logErrors;
486
595
  const summaryLogs = ({
487
596
  folderErrorsLength,
488
597
  intImagesErrorsLength,
489
598
  fileTypeErrorsLength,
599
+ fileErrorFileSizesLength,
490
600
  filenameErrorsLength,
601
+ fileNameCharCountErrorsLength,
491
602
  totalFiles,
492
603
  totalFolders
493
604
  }) => {
@@ -507,11 +618,21 @@ const summaryLogs = ({
507
618
  } else {
508
619
  messages.push(_chalk.default.white.bgGreen(' PASS ') + _chalk.default.green(` ${fileTypeErrorsLength} invalid file extensions`) + `, ${totalFiles} files total`);
509
620
  }
621
+ if (fileErrorFileSizesLength > 0) {
622
+ messages.push(_chalk.default.white.bgRed(' ERROR ') + _chalk.default.red(` ${fileTypeErrorsLength} invalid file sizes`) + `, ${totalFiles} files total`);
623
+ } else {
624
+ messages.push(_chalk.default.white.bgGreen(' PASS ') + _chalk.default.green(` ${fileTypeErrorsLength} invalid file sizes`) + `, ${totalFiles} files total`);
625
+ }
510
626
  if (filenameErrorsLength) {
511
627
  messages.push(_chalk.default.white.bgRed(' ERROR ') + _chalk.default.red(` ${filenameErrorsLength} invalid filenames`) + `, ${totalFiles} files total`);
512
628
  } else {
513
629
  messages.push(_chalk.default.bgGreen(' PASS ') + _chalk.default.green(` ${filenameErrorsLength} invalid filenames`) + `, ${totalFiles} files total`);
514
630
  }
631
+ if (fileNameCharCountErrorsLength) {
632
+ messages.push(_chalk.default.white.bgRed(' ERROR ') + _chalk.default.red(` ${filenameErrorsLength} file names reached the maximum character count limit `) + `, ${totalFiles} files total`);
633
+ } else {
634
+ messages.push(_chalk.default.bgGreen(' PASS ') + _chalk.default.green(` ${filenameErrorsLength} file names reached the maximum character count limit`) + `, ${totalFiles} files total`);
635
+ }
515
636
  console.log(`\n${messages.join('\n')}\n`);
516
637
  };
517
638
  exports.summaryLogs = summaryLogs;
package/docs/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  ## Changelog
2
2
 
3
+ ## 3.7.0
4
+
5
+ * New develop release: 3.6.1-dev.0 by @github-actions in #1931
6
+ * build(deps-dev): bump @automattic/eslint-plugin-wpvip from 0.12.0 to 0.13.0 by @dependabot in #1932
7
+ * build(deps-dev): bump the babel group with 3 updates by @dependabot in #1936
8
+ * build(deps): bump update-notifier from 7.0.0 to 7.1.0 by @dependabot in #1937
9
+ * build(deps): bump actions/dependency-review-action from 4.3.3 to 4.3.4 by @dependabot in #1938
10
+ * build(deps-dev): bump @babel/core from 7.24.8 to 7.24.9 in the babel group by @dependabot in #1939
11
+ * build(deps-dev): bump @types/dockerode from 3.3.29 to 3.3.30 by @dependabot in #1941
12
+ * update: media import validate-files by @ariskataoka in #1919
13
+ * New package release: v3.7.0 by @github-actions in #1943
14
+
15
+ **Full Changelog**: https://github.com/Automattic/vip-cli/compare/3.6.0...3.7.0
16
+
3
17
  ## 3.6.0
4
18
 
5
19
  * build(deps-dev): bump rimraf from 5.0.7 to 5.0.8