diff --git a/rules/large-file-config.json b/rules/large-file-config.json new file mode 100644 index 00000000..63022842 --- /dev/null +++ b/rules/large-file-config.json @@ -0,0 +1,13 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "type": "object", + "properties": { + "globsAll": { + "type": "array", + "items": { "type": "number" } + }, + "size": {"type": "number"} + }, + "required": ["content"], + "oneOf": [{ "required": ["globsAll"] }, { "required": ["files"] }] +} diff --git a/rules/large-file.js b/rules/large-file.js new file mode 100644 index 00000000..c7bdcf12 --- /dev/null +++ b/rules/large-file.js @@ -0,0 +1,69 @@ +const nodefs = require('fs') +// eslint-disable-next-line no-unused-vars +const Result = require('../lib/result') +// eslint-disable-next-line no-unused-vars +const path = require('path') +/** + * Check if a list of files is larger than provided file system size. + * + * @param {FileSystem} fs A filesystem object configured with filter paths and target directories + * @param {object} options The rule configuration + * @param {boolean} not Whether or not to invert the result (not contents instead of contents) + * @returns {Promise} The lint rule result + */ +async function largeFile(fs, options, not = false) { + // support legacy configuration keys + const fileList = options.globsAll || options.files + const files = await fs.findAllFiles(fileList, !!options.nocase) + + if (files.length === 0) { + return new Result( + 'Did not find file matching the specified patterns', + fileList.map(f => { + return { passed: false, pattern: f } + }), + !options['fail-on-non-existent'] + ) + } + + const results = ( + await Promise.all( + files.map(async file => { + const filePath = path.resolve(fs.targetDir, file) + const stat = await nodefs.promises.stat(filePath) + const passed = stat.size < options.size + const readerFriendlySize = + stat.size > 1000 * 1000 + ? `${stat.size / 1000000} MB` + : `${stat.size / 1000} KB` + const message = `File size ${readerFriendlySize} bytes` + + return { + passed: not ? !passed : passed, + path: filePath, + message, + size: stat.size + } + }) + ) + ) + .filter(fileStat => { + return !fileStat.passed + }) + .sort((stat1, stat2) => { + return stat2.size - stat1.size + }) + + const filteredResults = results.filter(r => r !== null) + const passed = !filteredResults.find(r => !r.passed) + if (passed.length === 0 || passed) { + return new Result( + `No file larger than ${options.size} bytes found.`, + filteredResults, + passed + ) + } + return new Result('', filteredResults, passed) +} + +module.exports = largeFile diff --git a/tests/rules/large_file_tests.js b/tests/rules/large_file_tests.js new file mode 100644 index 00000000..7622eaa1 --- /dev/null +++ b/tests/rules/large_file_tests.js @@ -0,0 +1,36 @@ +// Copyright 2022 TODO Group. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +const chai = require('chai') +const expect = chai.expect +const FileSystem = require('../../lib/file_system') + +describe('rule', () => { + describe('largeFile', () => { + const largeFile = require('../../rules/large-file') + + it('returns a passed result if file is smaller than threshold size.', async () => { + const ruleOptions = { + // file size ~41KB + globsAll: ['tests/rules/image_for_test.png'], + size: 42000 + } + + const actual = await largeFile(new FileSystem(), ruleOptions) + + expect(actual.passed).to.equal(true) + }) + + it('returns a failure result if file is larger than threshold size.', async () => { + const ruleOptions = { + // file size ~41KB + globsAll: ['tests/rules/image_for_test.png'], + size: 40000 + } + + const actual = await largeFile(new FileSystem(), ruleOptions) + + expect(actual.passed).to.equal(false) + }) + }) +})