Guchho 1.0.9

A standalone JavaScript linter, archive extraction, and clearer JavaScript syntax diagnostics.

#

Released 2026-10-26.

Added

#

guchho lint - a standalone linter

#

guchho lint checks JavaScript and TypeScript without producing a build. Run it with no paths to lint every supported source file under the working directory, or name the files to check:

guchho lint
guchho lint src
guchho lint --rule no-console:error --rule no-unused-vars:warning

The command has its own configuration file - guchho.lint.config.js, .mjs, .cjs or .json, found by searching upward from the working directory - with include/exclude patterns, extends, globals, a platform (browser, node or neutral), and per-rule levels. Configuration is validated: unknown fields and invalid values are reported rather than ignored.

Fifteen rules ship with the linter:

Rule Default Safe fix Reports
no-undef error References with no visible binding and no configured global.
no-unused-vars warning Unused local variables and parameters.
no-redeclare error Conflicting declarations in the same scope.
no-use-before-define off Reads that precede the declaration that resolves them.
no-shadow off Declarations that shadow an enclosing declaration.
no-unreachable error Statements that cannot be reached after a terminator.
no-duplicate-imports error The same module imported more than once.
no-constant-condition warning Conditions whose truth value is constant.
valid-typeof error typeof compared with an unknown type string.
no-console off Calls and constructions of console.
eqeqeq warning Yes == and != comparisons.
no-debugger error Yes debugger statements.
no-duplicate-case error Duplicate switch cases.
no-empty warning Empty blocks and statements where they are likely a mistake.
import/no-unresolved error Imports that the resolver cannot find.

eqeqeq only offers a fix when both sides are literals of the same type or one side is typeof, so the rewrite cannot alter coercion behaviour. no-debugger removes the statement. Every other finding is reported without a fix.

Configuration is a JavaScript or JSON file. The nearest of these wins, searching upward from the working directory:

guchho.lint.config.js
guchho.lint.config.mjs
guchho.lint.config.cjs
guchho.lint.config.json
// guchho.lint.config.mjs
export default {
  include: ['src/**/*.{js,jsx,ts,tsx}'],
  exclude: ['**/*.test.js'],
  platform: 'browser',
  rules: {
    'no-console': 'error',
    'no-unused-vars': 'warn',
  },
  globals: {
    console: 'readonly',
  },
}

extends loads one or more configs before the current one, later files win, and --rule name:level overrides the result on the command line. A level is off, warning/warn, or error; 0, 1 and 2 are accepted too.

Human output keeps the source line, caret range and location:

src/app.js:3:10: ERROR  [no-undef]  "missing" is not defined.
  3 |   return missing;
    |          ^^^^^^^

✘ 1 problem (1 error, 0 warnings)

--format=json (or --json) answers with per-file messages, a flat message list and these top-level counts:

{
  files: [/* one entry per file, with its own messages and counts */],
  messages: [/* every finding, each with its path */],
  errorCount: 1,
  warningCount: 0,
  fixCount: 0,
  filesWithProblems: 1,
}
  • Fixing: --fix writes safe fixes, --fix-dry-run reports them without writing, and --watch re-runs when sources or configuration change.
  • Output: --format=human|stylish|json (or --json), with --quiet to hide warning blocks but keep their counts.
  • Exit codes: 1 for a lint error or for warnings above --max-warnings, 2 for a usage or configuration failure, and 130 when interrupted in watch mode.

The optional checks that run during a build are separate, configured through build.lint; see guchho lint and the build options.

lint() in the guchho package

#

lint(options) runs the same rule set and safe fixes as the command and answers with the findings as data rather than a terminal report:

import { lint } from "guchho";

const result = await lint({
  files: ["src/app.js"],
  rules: { "no-console": "off" },
  maxWarnings: 0,
});

console.log(`${result.errorCount} errors, ${result.warningCount} warnings`);

The result carries files, a flat messages list, errorCount, warningCount, fixCount, filesWithProblems and maxWarningsExceeded. Nested file messages omit path; flat messages include it:

{
  files: [
    {
      path: 'src/app.js',
      messages: [
        {
          ruleId: 'no-console',
          severity: 'error',
          message: 'Unexpected console statement.',
          line: 4,
          column: 3,
          endLine: 4,
          endColumn: 20,
          syntax: false,
          fixCount: 0,
        },
      ],
      errorCount: 1,
      warningCount: 0,
    },
  ],
  messages: [
    { path: 'src/app.js', ruleId: 'no-console', severity: 'error', /* … */ },
  ],
  errorCount: 1,
  warningCount: 0,
  fixCount: 0,
  filesWithProblems: 1,
  maxWarningsExceeded: false,
}

A lint error is a result, not a rejected promise. fix and fixDryRun behave as their flags do, and maxWarnings sets the threshold reported by maxWarningsExceeded. In the browser, lint() works when the lint config is JSON; a JavaScript config is native-only because evaluating it needs a child process the WebAssembly engine does not have. See the lint() API.

Optional lint checks during a build

#

Beyond the standalone command, a build can run parser-level checks on JavaScript. They are off by default and are enabled through build.lint:

{ "build": { "lint": true } }
{ "build": { "lint": ["undefined", "unused"] } }
{ "build": { "lint": { "undefined": "error", "unused": "warning" } } }
Check Default Reports
undefined off References with no visible binding and no known global.
unused off Unreferenced local variables and parameters.
unused-variable off Unreferenced local variables only.
unused-parameter off Unreferenced parameters only.

true enables every check at warning; a string or array enables just the named checks; an object sets a level per check. The findings use the same message IDs the rest of the build does:

"missing" is not defined
"unusedValue" is declared but never used

They can be silenced or promoted through the existing log_override mechanism. Analysis is platform-aware and stays silent in with scopes and in files containing direct eval.

guchho unpack and unpack()

#

guchho unpack extracts zip, tar and tar.gz archives. The format comes from the extension first and from magic bytes when the name gives nothing away.

guchho unpack dist.zip                    # into ./dist/
guchho unpack release.tar.gz -o out/      # into ./out/
guchho unpack archive.tar --format=tar
guchho unpack bundle.zip --strip-components=1
guchho unpack release.zip --overwrite
Option Default Meaning
-o, --outdir=<dir> archive name without extension Extract into this directory. Created if missing.
--format=<format> auto-detect zip, tar or tar.gz.
--overwrite off Replace files that already exist.
--strip-components=<n> 0 Drop the first n path components from every entry.

An extracted dist.zip becomes:

dist.zip
└── dist/
    ├── index.html
    └── assets/
        └── app.js

Absolute paths and .. components are refused, so an archive cannot write outside the destination, and symlinks and other special entries are skipped with a warning. Files already at a destination are kept unless --overwrite is given, and each skipped file is reported. Tar headers are checksum-verified and PAX records are honoured; gzip streams are checked against their CRC-32 and length trailer. Permissions and modification times from the archive are restored where the host file system supports them.

The same operation is available as unpack() in the guchho package, with the same defaults and safety rules:

import { unpack } from "guchho";

const result = await unpack({
  archive: "dist.zip",
  outDir: "out",
  stripComponents: 1,
});

console.log(result.outDir); // "out"
console.log(result.files);  // ["index.html", "assets/app.js"]
console.log(result.warnings); // [] unless something was skipped

outDir defaults to the archive's name without its extension, format is detected when it is left out, and preservePermissions and preserveTimestamps both default to true. unpack() is native-only: the browser entry exports the name and throws, because the WebAssembly engine has no host file system. See guchho unpack and the unpack() API.

Improved

#

JavaScript syntax diagnostics

#

Misspelled keywords and missing closing delimiters now carry a structured suggestion and a safe fix alongside the existing message text, so an editor or tool can offer the correction without parsing the human-readable string. The fallback message text is unchanged, so existing log filters and snapshots keep working.

Source New diagnostic Fix
consts x = 1; Keyword typo with the intended keyword named Replace consts with const
functon f() {} Keyword typo Replace functon with function
if (ready { run(); } Missing ) before the block Insert )
let x = [1, 2; Missing ] Insert ]
let x = {a: 1; Missing } Insert }

Other messages are more specific too:

let x = "abc      -> Unterminated string literal. Expected a closing quote.
let x = /abc      -> Unterminated regular expression. Expected a closing "/".
let x = 0x        -> Unexpected end of file. The statement or expression is incomplete.
foo bar;          -> Expected ";" after this statement or declaration.
let x; let x;     -> "x" has already been declared in this scope
x + 1 = 2;        -> Invalid assignment target. Expected a variable or assignable expression.
const x;          -> Missing initializer in const declaration for "x"

Ambiguous cases get no fix. far x = 1; and foo bar; are reported without a suggestion rather than guessing at a keyword or separator that might be wrong.

Platform

#

Android builds now target API level 24 (Android 7.0) and link the C++ runtime statically. The dev server needs getifaddrs(), which Android grew in API 24, and a c++_shared binary would fail to start on a device that does not have the NDK runtime library beside it. Binaries for Android therefore require Android 7.0 or newer.

Upgrading

#

Nothing here is breaking for users of 1.0.8. Upgrade to pick up the linter, archive extraction and the improved diagnostics:

npm install -g guchho@1.0.9
npm install -D @guchho/wasm@1.0.9

Full comparison: v1.0.8…v1.0.9.

Edit this page on GitHub