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:
--fixwrites safe fixes,--fix-dry-runreports them without writing, and--watchre-runs when sources or configuration change. - Output:
--format=human|stylish|json(or--json), with--quietto hide warning blocks but keep their counts. - Exit codes:
1for a lint error or for warnings above--max-warnings,2for a usage or configuration failure, and130when 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.