From ca2fc8513581198be7d3731aab348602cc7ce431 Mon Sep 17 00:00:00 2001 From: joe fleming Date: Thu, 4 Apr 2019 15:28:24 -0700 Subject: [PATCH] feat: read config, read history, hash files most of the functionality exists now --- index.js | 3 --- package.json | 17 ++++++++++---- src/args.js | 13 +++++++++++ src/cli.js | 32 ++++++++++++++++++++++++++ src/data.js | 34 ++++++++++++++++++++++++++++ src/index.js | 31 +++++++++++++++++++++++-- src/utils.js | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++ yarn.lock | 10 ++++++++ 8 files changed, 194 insertions(+), 10 deletions(-) delete mode 100644 index.js create mode 100644 src/args.js create mode 100755 src/cli.js create mode 100644 src/data.js create mode 100644 src/utils.js diff --git a/index.js b/index.js deleted file mode 100644 index 9298d9f..0000000 --- a/index.js +++ /dev/null @@ -1,3 +0,0 @@ -const mod = require('./src/index.js'); - -module.exports = mod; diff --git a/package.json b/package.json index fd4ac18..42dcf78 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,10 @@ "name": "pkgcomp", "version": "0.0.0", "description": "Node package compare and change runner", - "main": "index.js", + "bin": { + "pkgcomp": "src/cli.js" + }, + "main": "src/index.js", "scripts": { "lint": "eslint '*.{js,mjs}' 'src/**/*.{js,mjs}'", "version": "npm-auto-version", @@ -50,18 +53,22 @@ "singleQuote": true, "trailingComma": "es5" }, - "dependencies": {}, + "dependencies": { + "joycon": "^2.2.4", + "md5-file": "^4.0.0", + "mkdirp": "^0.5.1" + }, "devDependencies": { "@w33ble/npm-auto-tools": "*", "eslint": "^5.15.3", + "eslint-config-airbnb": "^17.1.0", "eslint-config-prettier": "^4.1.0", "eslint-plugin-import": "^2.14.0", + "eslint-plugin-jsx-a11y": "^6.1.1", "eslint-plugin-prettier": "^3.0.1", + "eslint-plugin-react": "^7.11.0", "husky": "^1.3.1", "lint-staged": "^8.1.4", - "eslint-config-airbnb": "^17.1.0", - "eslint-plugin-jsx-a11y": "^6.1.1", - "eslint-plugin-react": "^7.11.0", "prettier": "^1.16.4" } } diff --git a/src/args.js b/src/args.js new file mode 100644 index 0000000..fe1ff0f --- /dev/null +++ b/src/args.js @@ -0,0 +1,13 @@ +module.exports = () => { + const args = process.argv.slice(2); + + return args.reduce( + (acc, arg) => { + if (arg === '--verbose') acc.verbose = true; + return acc; + }, + { + verbose: false, + } + ); +}; diff --git a/src/cli.js b/src/cli.js new file mode 100755 index 0000000..155272b --- /dev/null +++ b/src/cli.js @@ -0,0 +1,32 @@ +#!/usr/bin/env node + +const utils = require('./utils'); +const pkgcomp = require('./'); + +function parseArgs() { + const args = process.argv.slice(2); + + return args.reduce( + (acc, arg) => { + if (arg === '--verbose') acc.verbose = true; + if (arg === '--use-yarn') acc.useYarn = true; + return acc; + }, + { + verbose: false, + useYarn: false, + } + ); +} + +function buildConfig(args) { + // build config object, mixing in CLI overrides + const configOverrides = {}; + if (args.useYarn) configOverrides.cmd = 'yarn'; + return Object.assign({}, utils.getConfig(), configOverrides); +} + +// parse args from CLI +const args = parseArgs(); + +pkgcomp(buildConfig(args), utils.getIdent(), args); diff --git a/src/data.js b/src/data.js new file mode 100644 index 0000000..0b4e420 --- /dev/null +++ b/src/data.js @@ -0,0 +1,34 @@ +const fs = require('fs'); +const path = require('path'); +const mkdirp = require('mkdirp'); + +const CONFIG_FILENAME = 'pkgcomp.json'; + +exports.read = (fileRoot, ident) => { + const stats = fs.statSync(fileRoot); + const filePath = path.join(fileRoot, CONFIG_FILENAME); + + // throw if pointing to a file + if (stats.isFile()) throw new Error(`getData expects a directory, got a file; ${fileRoot}`); + + // create file if it's missing + if (!stats.isDirectory()) { + mkdirp.sync(fileRoot); + } + + try { + const data = JSON.parse(fs.readFileSync(filePath)); + return data[ident] || {}; + } catch (err) { + if (err.code === 'ENOENT') { + fs.writeFileSync(filePath, JSON.stringify({})); + return {}; + } + throw err; + } +}; + +exports.write = (fileRoot, ident, payload) => { + console.log('WRITE', { fileRoot, ident, payload }); + throw new Error('Data write not implemented'); +}; diff --git a/src/index.js b/src/index.js index 1084f7e..75a2bdf 100644 --- a/src/index.js +++ b/src/index.js @@ -1,3 +1,30 @@ -module.exports = function mod() { - // module code goes here +const md5File = require('md5-file'); +const data = require('./data'); +// const utils = require('./utils'); + +function getFileHash(file, verbose = false) { + try { + return md5File.sync(file); + } catch (e) { + // eslint-disable-next-line no-console + if (verbose) console.warn(`File not found: ${file}`); + return null; + } +} + +module.exports = function mod(config, ident, opts = {}) { + const packageData = data.read(config.dataDir, ident); + const firstRun = Object.keys(packageData).length === 0; + + // create file hash object + const hashes = config.checkFiles.reduce((acc, file) => { + acc[file] = getFileHash(file, opts.verbose); + return acc; + }, {}); + + console.log('First run?', firstRun); + + data.write(config.dataDir, ident, { + hashes, + }); }; diff --git a/src/utils.js b/src/utils.js new file mode 100644 index 0000000..a7ccdbb --- /dev/null +++ b/src/utils.js @@ -0,0 +1,64 @@ +const path = require('path'); +const fs = require('fs'); +const os = require('os'); +const JoyCon = require('joycon'); + +exports.getRootPath = () => process.cwd(); + +exports.getHomeDir = () => os.homedir(); + +exports.getDataDir = () => path.join(exports.getHomeDir(), '.local/share'); + +exports.canAccessFile = (p, flag) => { + try { + fs.accessSync(p, flag || fs.constants.R_OK); + return true; + } catch (e) { + return false; + } +}; + +exports.getFileContents = (filePath, opts = {}) => { + if (!exports.canAccessFile(filePath)) throw new Error(`File not found: ${filePath}`); + + const content = fs.readFileSync(filePath); + if (opts.format === 'json') return JSON.parse(content); + if (opts.format === 'text') return content.toString(); + return content; +}; + +exports.getRootFileContents = (filename, opts = {}) => { + return exports.getFileContents(path.join(exports.getRootPath(), filename), opts); +}; + +exports.getIdent = () => { + const { name } = exports.getRootFileContents('package.json', { format: 'json' }); + return name; +}; + +exports.getBranch = () => { + const headPath = path.join(exports.getRootPath(), '.git/HEAD'); + if (!exports.canAccessFile(headPath)) throw new Error(`Git HEAD not found: ${headPath}`); + + const head = fs.readFileSync(headPath); + const match = /ref: refs\/heads\/([^\n]+)/.exec(head); + if (!match) throw new Error(`Unable to read branch from ${headPath}`); + return match[1]; +}; + +exports.getConfig = (overrides = {}) => { + const joycon = new JoyCon(); + const defaults = { + checkFiles: ['package-lock.json', 'yarn.lock'], + rootDir: process.cwd(), + dataDir: exports.getDataDir(), + cmd: 'npm', + }; + + const result = joycon.loadSync({ + packageKey: 'pkgcomp', + files: ['.config/pkgcomp.json', 'pkgcomp.json', 'package.json'], + }); + + return Object.assign(defaults, result.data, overrides); +}; diff --git a/yarn.lock b/yarn.lock index 66d8f95..20bd378 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1432,6 +1432,11 @@ isobject@^3.0.0, isobject@^3.0.1: resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= +joycon@^2.2.4: + version "2.2.4" + resolved "https://registry.yarnpkg.com/joycon/-/joycon-2.2.4.tgz#ac4119d2673dfaa89b41efe5d6d670208e348bc6" + integrity sha512-Z4G7ZPovoSfAHCSEKznWV4IjyyUxzTY78Sgtr8inbnRxaZMAQGwqNJ46BDwcH3WgIwk0Xt1kpCsC/DFAioRtBA== + "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -1659,6 +1664,11 @@ matcher@^1.0.0: dependencies: escape-string-regexp "^1.0.4" +md5-file@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/md5-file/-/md5-file-4.0.0.tgz#f3f7ba1e2dd1144d5bf1de698d0e5f44a4409584" + integrity sha512-UC0qFwyAjn4YdPpKaDNw6gNxRf7Mcx7jC1UGCY4boCzgvU2Aoc1mOGzTtrjjLKhM5ivsnhoKpQVxKPp+1j1qwg== + micromatch@^3.1.8: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23"