UNPKG

jscpd

Version:

detector of copy/paste in files

361 lines (314 loc) 13.8 kB
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, { get: (a, b) => (typeof require !== "undefined" ? require : a)[b] }) : x)(function(x) { if (typeof require !== "undefined") return require.apply(this, arguments); throw Error('Dynamic require of "' + x + '" is not supported'); }); // src/index.ts var _core = require('@jscpd/core'); var _safe = require('colors/safe'); var _finder = require('@jscpd/finder'); // src/init/cli.ts var _commander = require('commander'); function initCli(packageJson, argv) { const cli = new (0, _commander.Command)(packageJson.name); cli.version(packageJson.version).usage("[options] <path ...>").description(packageJson.description).option( "-l, --min-lines [number]", "min size of duplication in code lines (Default is " + _core.getOption.call(void 0, "minLines") + ")" ).option( "-k, --min-tokens [number]", "min size of duplication in code tokens (Default is " + _core.getOption.call(void 0, "minTokens") + ")" ).option("-x, --max-lines [number]", "max size of source in lines (Default is " + _core.getOption.call(void 0, "maxLines") + ")").option( "-z, --max-size [string]", "max size of source in bytes, examples: 1kb, 1mb, 120kb (Default is " + _core.getOption.call(void 0, "maxSize") + ")" ).option( "-t, --threshold [number]", "threshold for duplication, in case duplications >= threshold jscpd will exit with error" ).option("-c, --config [string]", "path to config file (Default is .jscpd.json in <path>)").option("-i, --ignore [string]", "glob pattern for files what should be excluded from duplication detection").option("--ignore-pattern [string]", "Ignore code blocks matching the regexp patterns").option( "-r, --reporters [string]", "reporters or list of reporters separated with comma to use (Default is time,console)" ).option("-o, --output [string]", "reporters to use (Default is ./report/)").option( "-m, --mode [string]", 'mode of quality of search, can be "strict", "mild" and "weak" (Default is "' + _core.getOption.call(void 0, "mode") + '")' ).option("-f, --format [string]", "format or formats separated by comma (Example php,javascript,python)").option("-p, --pattern [string]", "glob pattern to file search (Example **/*.txt)").option("-b, --blame", "blame authors of duplications (get information about authors from git)").option("-s, --silent", "do not write detection progress and result to a console").option("--store [string]", "use for define custom store (e.g. --store leveldb used for big codebase)").option("-a, --absolute", "use absolute path in reports").option("-n, --noSymlinks", "dont use symlinks for detection in files").option("--ignoreCase", "ignore case of symbols in code (experimental)").option("-g, --gitignore", "ignore all files from .gitignore file").option("--formats-exts [string]", "list of formats with file extensions (javascript:es,es6;dart:dt)").option("-d, --debug", "show debug information, not run detection process(options list and selected files)").option("-v, --verbose", "show full information during detection process").option("--list", "show list of total supported formats").option("--skipLocal", "skip duplicates in local folders, just detect cross folders duplications").option("--exitCode [number]", "exit code to use when code duplications are detected"); cli.parse(argv); return cli; } // src/init/options.ts var _tokenizer = require('@jscpd/tokenizer'); // src/init/ignore.ts var _fs = require('fs'); var gitignoreToGlob = __require("gitignore-to-glob"); function initIgnore(options) { const ignore = options.ignore || []; if (options.gitignore && _fs.existsSync.call(void 0, process.cwd() + "/.gitignore")) { let gitignorePatterns = gitignoreToGlob(process.cwd() + "/.gitignore") || []; gitignorePatterns = gitignorePatterns.map( (pattern) => pattern.substr(pattern.length - 1) === "/" ? `${pattern}**/*` : pattern ); ignore.push(...gitignorePatterns); ignore.map((pattern) => pattern.replace("!", "")); } return ignore; } // src/options.ts var _path = require('path'); var _fsextra = require('fs-extra'); var convertCliToOptions = (cli) => { const result = { minTokens: cli.minTokens ? parseInt(cli.minTokens) : void 0, minLines: cli.minLines ? parseInt(cli.minLines) : void 0, maxLines: cli.maxLines ? parseInt(cli.maxLines) : void 0, maxSize: cli.maxSize, debug: cli.debug, store: cli.store, pattern: cli.pattern, executionId: cli.executionId, silent: cli.silent, blame: cli.blame, verbose: cli.verbose, cache: cli.cache, output: cli.output, format: cli.format, formatsExts: _finder.parseFormatsExtensions.call(void 0, cli.formatsExts), list: cli.list, mode: cli.mode, absolute: cli.absolute, noSymlinks: cli.noSymlinks, skipLocal: cli.skipLocal, ignoreCase: cli.ignoreCase, gitignore: cli.gitignore, exitCode: cli.exitCode }; if (cli.threshold !== void 0) { result.threshold = Number(cli.threshold); } if (cli.reporters) { result.reporters = cli.reporters.split(","); } if (cli.format) { result.format = cli.format.split(","); } if (cli.ignore) { result.ignore = cli.ignore.split(","); } if (cli.ignorePattern) { result.ignorePattern = cli.ignorePattern.split(","); } result.path = cli.path ? [cli.path].concat(cli.args) : cli.args; if (result.path.length === 0) { delete result.path; } Object.keys(result).forEach((key) => { if (typeof result[key] === "undefined") { delete result[key]; } }); return result; }; var readConfigJson = (config) => { const configFile = config ? _path.resolve.call(void 0, config) : _path.resolve.call(void 0, ".jscpd.json"); const configExists = _fs.existsSync.call(void 0, configFile); if (configExists) { const result = { config: configFile, ..._fsextra.readJSONSync.call(void 0, configFile) }; if (result.path) { result.path = result.path.map((path) => _path.resolve.call(void 0, _path.dirname.call(void 0, configFile), path)); } return result; } return {}; }; var readPackageJsonConfig = () => { const config = _path.resolve.call(void 0, process.cwd() + "/package.json"); if (_fs.existsSync.call(void 0, config)) { const json = _fsextra.readJSONSync.call(void 0, config); if (json.jscpd && json.jscpd.path) { json.jscpd.path = json.jscpd.path.map((path) => _path.resolve.call(void 0, _path.dirname.call(void 0, config), path)); } return json.jscpd ? { config, ...json.jscpd } : {}; } return {}; }; function prepareOptions(cli) { const storedConfig = readConfigJson(cli.config); const packageJsonConfig = readPackageJsonConfig(); const argsConfig = convertCliToOptions(cli); const result = { ..._core.getDefaultOptions.call(void 0, ), ...packageJsonConfig, ...storedConfig, ...argsConfig }; result.reporters = result.reporters || []; result.listeners = result.listeners || []; if (result.silent) { result.reporters = result.reporters.filter( (reporter) => !reporter.includes("console") ).concat("silent"); } if (result.threshold !== void 0) { result.reporters = [...result.reporters, "threshold"]; } return result; } // src/init/options.ts function initOptionsFromCli(cli) { const options = prepareOptions(cli); options.format = options.format || _tokenizer.getSupportedFormats.call(void 0, ); options.mode = _core.getModeHandler.call(void 0, options.mode); options.ignore = initIgnore(options); return options; } // src/print/files.ts function printFiles(files) { files.forEach((stats) => { console.log(_safe.grey.call(void 0, stats.path)); }); console.log(_safe.bold.call(void 0, `Found ${files.length} files to detect.`)); } // src/print/options.ts function printOptions(options) { console.log(_safe.bold.call(void 0, _safe.white.call(void 0, "Options:"))); console.dir(options); } // src/print/supported-format.ts function printSupportedFormat() { console.log(_safe.bold.call(void 0, _safe.white.call(void 0, "Supported formats: "))); console.log(_tokenizer.getSupportedFormats.call(void 0, ).join(", ")); process.exit(0); } // src/index.ts var _crypto = require('crypto'); // src/init/store.ts function getStore(storeName) { if (storeName) { const packageName = "@jscpd/" + storeName + "-store"; try { const store = __require(packageName).default; return new store(); } catch (e) { console.error(_safe.red.call(void 0, "store name " + storeName + " not installed.")); } } return new (0, _core.MemoryStore)(); } // src/index.ts // src/init/reporters.ts var _htmlreporter = require('@jscpd/html-reporter'); var _htmlreporter2 = _interopRequireDefault(_htmlreporter); var _jscpdsarifreporter = require('jscpd-sarif-reporter'); var _jscpdsarifreporter2 = _interopRequireDefault(_jscpdsarifreporter); var reporters = { xml: _finder.XmlReporter, json: _finder.JsonReporter, csv: _finder.CSVReporter, markdown: _finder.MarkdownReporter, consoleFull: _finder.ConsoleFullReporter, html: _htmlreporter2.default, console: _finder.ConsoleReporter, silent: _finder.SilentReporter, threshold: _finder.ThresholdReporter, xcode: _finder.XcodeReporter, sarif: _jscpdsarifreporter2.default }; function registerReporters(options, detector) { options.reporters.forEach((reporter) => { if (reporter in reporters) { detector.registerReporter(new reporters[reporter](options)); } else { try { const reporterClass = __require(`@jscpd/${reporter}-reporter`).default; detector.registerReporter(new reporterClass(options)); } catch (e) { try { const reporterClass = __require(`jscpd-${reporter}-reporter`).default; detector.registerReporter(new reporterClass(options)); } catch (e2) { console.log(_safe.yellow.call(void 0, `warning: ${reporter} not installed (install packages named @jscpd/${reporter}-reporter or jscpd-${reporter}-reporter)`)); console.log(_safe.grey.call(void 0, e2.message)); } } } }); } // src/init/subscribers.ts function registerSubscribers(options, detector) { if (options.verbose) { detector.registerSubscriber(new (0, _finder.VerboseSubscriber)(options)); } if (!options.silent) { detector.registerSubscriber(new (0, _finder.ProgressSubscriber)(options)); } } // src/init/hooks.ts function registerHooks(options, detector) { detector.registerHook(new (0, _finder.FragmentsHook)()); if (options.blame) { detector.registerHook(new (0, _finder.BlamerHook)()); } } // src/index.ts var TIMER_LABEL = "Detection time:"; var detectClones = (opts, store = void 0) => { const options = { ..._core.getDefaultOptions.call(void 0, ), ...opts }; options.format = options.format || _tokenizer.getSupportedFormats.call(void 0, ); const files = _finder.getFilesToDetect.call(void 0, options); const hashFunction = (value) => { return _crypto.createHash.call(void 0, "md5").update(value).digest("hex"); }; options.hashFunction = options.hashFunction || hashFunction; const currentStore = store || getStore(options.store); const statistic = new (0, _core.Statistic)(); const tokenizer = new (0, _tokenizer.Tokenizer)(); const detector = new (0, _finder.InFilesDetector)(tokenizer, currentStore, statistic, options); registerReporters(options, detector); registerSubscribers(options, detector); registerHooks(options, detector); if (!options.silent) { console.time(_safe.italic.call(void 0, _safe.grey.call(void 0, TIMER_LABEL))); } return detector.detect(files).then((clones) => { if (!options.silent) { console.timeEnd(_safe.italic.call(void 0, _safe.grey.call(void 0, TIMER_LABEL))); } return clones; }); }; async function jscpd(argv, exitCallback) { const packageJson = _fsextra.readJSONSync.call(void 0, __dirname + "/../package.json"); const cli = initCli(packageJson, argv); const options = initOptionsFromCli(cli); if (options.list) { printSupportedFormat(); } if (options.debug) { printOptions(options); } if (!options.path || options.path.length === 0) { options.path = [process.cwd()]; } if (options.debug) { const files = _finder.getFilesToDetect.call(void 0, options); printFiles(files); return Promise.resolve([]); } else { const store = getStore(options.store); return detectClones(options, store).then((clones) => { if (clones.length > 0) { _optionalChain([exitCallback, 'optionalCall', _ => _(options.exitCode || 0)]); } return clones; }).finally(() => { store.close(); }); } } // bin/jscpd.ts (async () => { try { await jscpd(process.argv, process.exit); } catch (e) { console.log(e); process.exit(1); } })(); exports.detectClones = detectClones; exports.jscpd = jscpd; //# sourceMappingURL=jscpd.js.map