nano-staged
Advanced tools
Comparing version 0.5.0 to 0.6.0
#!/usr/bin/env node | ||
import runner from './run.js' | ||
import { getForceColorLevel } from './utils.js' | ||
import nanoStaged from './index.js' | ||
import * as utils from './utils.js' | ||
let FORCE_COLOR_LEVEL = getForceColorLevel() | ||
const FORCE_COLOR_LEVEL = utils.getForceColorLevel() | ||
if (FORCE_COLOR_LEVEL) { | ||
@@ -11,3 +12,2 @@ process.env.FORCE_COLOR = FORCE_COLOR_LEVEL.toString() | ||
// Do not terminate main process on SIGINT | ||
process.on('SIGINT', () => {}) | ||
@@ -22,3 +22,3 @@ | ||
if (arg === '-c' || arg === '--config') { | ||
options.configPath = process.argv[++i] | ||
options.config = process.argv[++i] | ||
} else if (arg === '-u' || arg === '--unstaged') { | ||
@@ -28,6 +28,10 @@ options.unstaged = true | ||
options.allowEmpty = true | ||
} else if (arg === '--diff') { | ||
options.diff = [] | ||
} else if (options.diff && options.diff.length !== 2) { | ||
options.diff.push(process.argv[i]) | ||
} | ||
} | ||
return runner(options) | ||
return nanoStaged(options) | ||
} | ||
@@ -34,0 +38,0 @@ |
@@ -1,39 +0,50 @@ | ||
import { resolve } from 'path' | ||
import { resolve, parse } from 'path' | ||
import { pathToFileURL } from 'url' | ||
import fs from 'fs' | ||
const NODE_PACKAGE_JSON = 'package.json' | ||
const CONFIG_NAME = 'nano-staged' | ||
const PLACES = [`.${CONFIG_NAME}.json`, `${CONFIG_NAME}.json`, NODE_PACKAGE_JSON] | ||
const places = [ | ||
`.nano-staged.js`, | ||
`nano-staged.js`, | ||
`.nano-staged.cjs`, | ||
`nano-staged.cjs`, | ||
`.nano-staged.mjs`, | ||
`nano-staged.mjs`, | ||
`.nano-staged.json`, | ||
`nano-staged.json`, | ||
'package.json', | ||
] | ||
export function readConfig(filepath) { | ||
if (fs.existsSync(filepath) && fs.lstatSync(filepath).isFile()) { | ||
return JSON.parse(fs.readFileSync(filepath, 'utf-8')) | ||
async function readConfig(path) { | ||
if (fs.existsSync(path) && fs.lstatSync(path).isFile()) { | ||
const { ext, name } = parse(path) | ||
if (ext === '.json') { | ||
const config = JSON.parse(fs.readFileSync(path, 'utf-8')) | ||
return name === 'package' ? config['nano-staged'] : config | ||
} | ||
if (ext === '.js' || ext === '.mjs' || ext === '.cjs') { | ||
const { default: config } = await import(pathToFileURL(path)) | ||
return typeof config === 'function' ? { '*': config } : config | ||
} | ||
} | ||
} | ||
export function loadConfig(cwd = process.cwd()) { | ||
export async function getConfig(cwd = process.cwd(), config = undefined) { | ||
try { | ||
let config | ||
let dir = resolve(cwd) | ||
if (config) { | ||
return typeof config === 'string' ? await readConfig(resolve(config)) : config | ||
} | ||
let up = resolve(cwd) | ||
do { | ||
cwd = dir | ||
for (let place of PLACES) { | ||
let path = resolve(cwd, place) | ||
if (!config && fs.existsSync(path)) { | ||
if (place === NODE_PACKAGE_JSON) { | ||
config = readConfig(path)[CONFIG_NAME] | ||
} else { | ||
config = readConfig(path) | ||
} | ||
return config | ||
} | ||
cwd = up | ||
for (const place of places) { | ||
config = await readConfig(resolve(cwd, place)) | ||
if (config) return config | ||
} | ||
dir = resolve(cwd, '../') | ||
} while (dir !== cwd) | ||
} catch (error) { | ||
up = resolve(cwd, '..') | ||
} while (up !== cwd) | ||
} catch { | ||
return undefined | ||
@@ -53,2 +64,3 @@ } | ||
(typeof config[key] === 'string' || | ||
typeof config[key] === 'function' || | ||
(Array.isArray(config[key]) && | ||
@@ -55,0 +67,0 @@ config[key].every((cmd) => cmd && typeof cmd === 'string'))) |
109
lib/git.js
import { join, normalize } from 'path' | ||
import { spawner } from './spawner.js' | ||
import { executor } from './executor.js' | ||
import { toArray } from './utils.js' | ||
@@ -29,12 +29,38 @@ | ||
export function gitWorker(cwd = process.cwd()) { | ||
let git = { | ||
function group(entries = []) { | ||
const deleted = [] | ||
const changed = [] | ||
const working = [] | ||
for (let { path, type, rename } of entries) { | ||
path = rename || path | ||
if (!working.includes(path)) { | ||
if (type === CHANGED_CODE) { | ||
changed.push(path) | ||
} | ||
if (type === DELETED_CODE) { | ||
deleted.push(path) | ||
} | ||
working.push(path) | ||
} | ||
} | ||
return { working, deleted, changed } | ||
} | ||
export function createGit(cwd = process.cwd()) { | ||
const git = { | ||
cwd, | ||
async exec(args = [], opts = {}) { | ||
try { | ||
return await spawner('git', args, { | ||
return await executor('git', args, { | ||
...opts, | ||
cwd: opts.cwd || cwd, | ||
cwd: opts.cwd || git.cwd, | ||
}) | ||
} catch (err) { | ||
throw err | ||
} catch (e) { | ||
throw e | ||
} | ||
@@ -54,2 +80,19 @@ }, | ||
async diffFileName(ref1, ref2, opts = {}) { | ||
const args = ['diff', '--name-only', '--no-ext-diff', '--diff-filter=ACMR', '-z'] | ||
if (ref1) { | ||
args.push(ref1) | ||
} | ||
if (ref2) { | ||
args.push(ref2) | ||
} | ||
try { | ||
return await git.exec([...args, '--'], opts) | ||
} catch { | ||
return '' | ||
} | ||
}, | ||
async apply(patch, allowConflicts = false, opts = {}) { | ||
@@ -69,15 +112,15 @@ const args = ['apply', ...APPLY_ARGS] | ||
async getRepoAndDotGitPaths(opts = {}) { | ||
async getGitPaths(opts = {}) { | ||
try { | ||
let result = await git.exec(['rev-parse', '--show-toplevel'], opts) | ||
let repositoriyPath = result ? normalize(result.trimLeft().replace(/[\r\n]+$/, '')) : '' | ||
const line = await git.exec(['rev-parse', '--show-toplevel'], opts) | ||
const path = line ? normalize(line.trimLeft().replace(/[\r\n]+$/, '')) : '' | ||
return { | ||
repoPath: repositoriyPath || null, | ||
dotGitPath: repositoriyPath ? join(repositoriyPath, '.git') : null, | ||
root: path || null, | ||
dot: path ? join(path, '.git') : null, | ||
} | ||
} catch (error) { | ||
} catch { | ||
return { | ||
repoPath: null, | ||
dotGitPath: null, | ||
root: null, | ||
dot: null, | ||
} | ||
@@ -111,5 +154,6 @@ } | ||
try { | ||
const raw = await git.exec(args, { env, ...opts }) | ||
let i = 0 | ||
let lastIndex | ||
let raw = await git.exec(args, { env, ...opts }) | ||
@@ -121,3 +165,3 @@ while (i < raw.length) { | ||
let entry = { | ||
const entry = { | ||
x: raw.charCodeAt(i++), | ||
@@ -158,3 +202,3 @@ y: raw.charCodeAt(i++), | ||
return result | ||
} catch (err) { | ||
} catch { | ||
return [] | ||
@@ -164,8 +208,17 @@ } | ||
async changedFiles(refs = [], opts = {}) { | ||
const [ref1, ref2] = refs | ||
const lines = await git.diffFileName(ref1, ref2, opts) | ||
const files = lines ? lines.replace(/\u0000$/, '').split('\u0000') : [] | ||
const result = files.map((path) => ({ type: CHANGED_CODE, path, rename: undefined })) | ||
return group(result) | ||
}, | ||
async stagedFiles(opts = {}) { | ||
let entries = await git.status(opts) | ||
let result = [] | ||
const entries = await git.status(opts) | ||
const result = [] | ||
for (let entry of entries) { | ||
let { x, y } = entry | ||
for (const entry of entries) { | ||
const { x, y } = entry | ||
@@ -185,11 +238,11 @@ if (x === ADDED || x === MODIFIED || x === RENAMED || x === COPIED) { | ||
return result | ||
return group(result) | ||
}, | ||
async unstagedFiles(opts = {}) { | ||
let entries = await git.status(opts) | ||
let result = [] | ||
const entries = await git.status(opts) | ||
const result = [] | ||
for (let entry of entries) { | ||
let { y } = entry | ||
for (const entry of entries) { | ||
const { y } = entry | ||
@@ -202,3 +255,3 @@ if (y !== SPACE && y !== DELETED) { | ||
return result | ||
return group(result) | ||
}, | ||
@@ -205,0 +258,0 @@ } |
import { fileURLToPath } from 'url' | ||
import { readFileSync } from 'fs' | ||
import pico from 'picocolors' | ||
import process from 'process' | ||
import { join } from 'path' | ||
import c from 'picocolors' | ||
import tty from 'tty' | ||
@@ -18,3 +18,3 @@ import os from 'os' | ||
let pkgJson = JSON.parse(pkg.toString()) | ||
print(`Nano Staged ${pico.bold(`v${pkgJson.version}`)}`) | ||
print.write(`Nano Staged ${c.bold(`v${pkgJson.version}`)}\n`) | ||
} | ||
@@ -21,0 +21,0 @@ |
{ | ||
"name": "nano-staged", | ||
"version": "0.5.0", | ||
"description": "Tool to run commands only on git staged files", | ||
"version": "0.6.0", | ||
"description": "Tiny tool to run commands for modified, staged, and committed git files.", | ||
"author": "Usman Yunusov <usman.iunusov@gmail.com>", | ||
@@ -10,3 +10,3 @@ "license": "MIT", | ||
"bin": "./lib/bin.js", | ||
"exports": "./lib/run.js", | ||
"exports": "./lib/index.js", | ||
"engines": { | ||
@@ -23,2 +23,4 @@ "node": "^12.20.0 || ^14.13.1 || >=16.0.0" | ||
"staged", | ||
"unstaged", | ||
"diff", | ||
"eslint", | ||
@@ -25,0 +27,0 @@ "prettier", |
@@ -1,13 +0,8 @@ | ||
# Nano Staged | ||
<p align="center"> | ||
<img src="https://usmanyunusov.github.io/nano-staged/img/logo.svg" height="96"> | ||
<h3 align="center">Nano Staged</h3> | ||
<p align="center">Tiny tool to run commands for modified, staged, and committed git files.<br/> It help <b>speeding up the run tests, linters, scripts</b>, and more</p> | ||
</p> | ||
<img align="right" width="92" height="92" title="Nano Stores logo" | ||
src="https://usmanyunusov.github.io/nano-staged/img/logo.svg"> | ||
Tiny tool to run commands for both staged and unstaged git files. It help **speeding up the run tests, lint code**, etc... | ||
- 📦 **Small**: [40kB](https://packagephobia.com/result?p=nano-staged) (174x+ lighter than **lint-staged**). | ||
- 🥇 **Single dependency** ([`picocolors`](https://github.com/alexeyraspopov/picocolors)). | ||
- ☯️ Support **staged/unstaged** git files. | ||
## Docs | ||
Read **[full docs](https://github.com/usmanyunusov/nano-staged#readme)** on GitHub. |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
40582
16
1400
9
25
2