From 4e13306fb44a3dcba7cfc393f147234ff4ee99e1 Mon Sep 17 00:00:00 2001 From: Chris Oakman Date: Sun, 2 Aug 2026 14:35:21 -0500 Subject: [PATCH 01/10] WIP WIP WIP --- test/cli_file_discovery.test.js | 388 ++++++++++++++++++++++++++++++++ 1 file changed, 388 insertions(+) create mode 100644 test/cli_file_discovery.test.js diff --git a/test/cli_file_discovery.test.js b/test/cli_file_discovery.test.js new file mode 100644 index 0000000..dfcf2fe --- /dev/null +++ b/test/cli_file_discovery.test.js @@ -0,0 +1,388 @@ +/* global afterEach beforeEach describe expect test */ + +const childProcess = require('node:child_process') +const fs = require('node:fs') +const os = require('node:os') +const path = require('node:path') + +const cliFile = path.join(__dirname, '..', 'cli.mjs') + +let projectDir = null + +function writeFile (relativeFilename, contents) { + const absoluteFilename = path.join(projectDir, relativeFilename) + fs.mkdirSync(path.dirname(absoluteFilename), { recursive: true }) + fs.writeFileSync(absoluteFilename, contents) +} + +function createTestProject () { + const files = { + 'dev/user.clj': '(ns user)\n', + 'resources/config.edn': '{:enabled true}\n', + 'src/browser.cljs': '(ns browser)\n', + 'src/core.clj': '(ns core)\n', + 'src/data.edn': '{:hello "world"}\n', + 'src/experimental.jank': '(ns experimental)\n', + 'src/generated/generated.clj': '(ns generated)\n', + 'src/generated/generated.cljs': '(ns generated-browser)\n', + 'src/nested/deep.clj': '(ns nested.deep)\n', + 'src/nested/deep.cljs': '(ns nested.deep-browser)\n', + 'src/nested/deep.cljc': '(ns nested.deep-shared)\n', + 'src/nested/notes.md': '# notes\n', + 'src/notes.txt': 'not Clojure\n', + 'src/shared.cljc': '(ns shared)\n', + 'test/core_test.clj': '(ns core-test)\n', + 'test/nested/browser_test.cljs': '(ns browser-test)\n' + } + + Object.entries(files).forEach(([relativeFilename, contents]) => { + writeFile(relativeFilename, contents) + }) +} + +function absoluteFiles (...relativeFilenames) { + return relativeFilenames + .map(relativeFilename => path.join(projectDir, relativeFilename)) + .sort() +} + +function runList (...args) { + const result = childProcess.spawnSync( + process.execPath, + [cliFile, 'list', ...args, '--output', 'json'], + { + cwd: projectDir, + encoding: 'utf8', + env: { + ...process.env, + FORCE_COLOR: '0' + }, + windowsHide: true + } + ) + + if (result.error) { + throw result.error + } + + expect(result.signal).toBeNull() + expect(result.status).toBe(0) + expect(result.stderr).toBe('') + + return JSON.parse(result.stdout) +} + +function writeJSONConfig (config) { + writeFile('.standard-clj.json', JSON.stringify(config, null, 2) + '\n') +} + +function writeEDNConfig (configText) { + writeFile('.standard-clj.edn', configText) +} + +beforeEach(() => { + projectDir = fs.mkdtempSync(path.join(os.tmpdir(), 'standard-clj-file-discovery-')) + createTestProject() +}) + +afterEach(() => { + fs.rmSync(projectDir, { force: true, recursive: true }) + projectDir = null +}) + +describe('direct file and directory arguments', () => { + test('includes a directly named file', () => { + expect(runList('src/core.clj')).toEqual(absoluteFiles('src/core.clj')) + }) + + test('allows a directly named file with a non-default extension', () => { + expect(runList('src/notes.txt')).toEqual(absoluteFiles('src/notes.txt')) + }) + + test('recursively discovers supported files in a directory', () => { + expect(runList('src')).toEqual(absoluteFiles( + 'src/browser.cljs', + 'src/core.clj', + 'src/data.edn', + 'src/experimental.jank', + 'src/generated/generated.clj', + 'src/generated/generated.cljs', + 'src/nested/deep.clj', + 'src/nested/deep.cljs', + 'src/nested/deep.cljc', + 'src/shared.cljc' + )) + }) + + test('uses --file-ext when discovering files from a directory', () => { + expect(runList('src', '--file-ext', 'clj,cljs')).toEqual(absoluteFiles( + 'src/browser.cljs', + 'src/core.clj', + 'src/generated/generated.clj', + 'src/generated/generated.cljs', + 'src/nested/deep.clj', + 'src/nested/deep.cljs' + )) + }) +}) + +describe('--include patterns', () => { + test('* matches files in one directory level', () => { + expect(runList('--include', 'src/*.clj')).toEqual(absoluteFiles( + 'src/core.clj' + )) + }) + + test('** recursively matches nested directories', () => { + expect(runList('--include', 'src/**/*.clj')).toEqual(absoluteFiles( + 'src/core.clj', + 'src/generated/generated.clj', + 'src/nested/deep.clj' + )) + }) + + test('supports brace expansion for file extensions', () => { + expect(runList('--include', 'src/**/*.{clj,cljs,cljc}')).toEqual(absoluteFiles( + 'src/browser.cljs', + 'src/core.clj', + 'src/generated/generated.clj', + 'src/generated/generated.cljs', + 'src/nested/deep.clj', + 'src/nested/deep.cljs', + 'src/nested/deep.cljc', + 'src/shared.cljc' + )) + }) + + test('supports brace expansion for directory names', () => { + expect(runList('--include', '{src,test}/**/*.clj')).toEqual(absoluteFiles( + 'src/core.clj', + 'src/generated/generated.clj', + 'src/nested/deep.clj', + 'test/core_test.clj' + )) + }) + + test('combines multiple include patterns', () => { + expect(runList( + '--include', 'src/**/*.clj', + '--include', 'test/**/*.clj' + )).toEqual(absoluteFiles( + 'src/core.clj', + 'src/generated/generated.clj', + 'src/nested/deep.clj', + 'test/core_test.clj' + )) + }) + + test('deduplicates files matched by multiple include patterns', () => { + expect(runList( + '--include', 'src/**/*.clj', + '--include', 'src/core.clj' + )).toEqual(absoluteFiles( + 'src/core.clj', + 'src/generated/generated.clj', + 'src/nested/deep.clj' + )) + }) + + test('combines direct arguments with command-line include patterns', () => { + expect(runList( + 'dev/user.clj', + '--include', 'src/*.clj' + )).toEqual(absoluteFiles( + 'dev/user.clj', + 'src/core.clj' + )) + }) + + test('broad include patterns return only regular files with supported extensions', () => { + expect(runList('--include', 'src/**/*')).toEqual(absoluteFiles( + 'src/browser.cljs', + 'src/core.clj', + 'src/data.edn', + 'src/experimental.jank', + 'src/generated/generated.clj', + 'src/generated/generated.cljs', + 'src/nested/deep.clj', + 'src/nested/deep.cljs', + 'src/nested/deep.cljc', + 'src/shared.cljc' + )) + }) + + test('uses --file-ext to filter files discovered by include patterns', () => { + expect(runList( + '--include', 'src/**/*', + '--file-ext', 'clj,cljc' + )).toEqual(absoluteFiles( + 'src/core.clj', + 'src/generated/generated.clj', + 'src/nested/deep.clj', + 'src/nested/deep.cljc', + 'src/shared.cljc' + )) + }) + + test('returns an empty list when no include pattern matches', () => { + expect(runList('--include', 'does-not-exist/**/*.clj')).toEqual([]) + }) +}) + +describe('--ignore patterns', () => { + test('ignores a relative filename selected by an include pattern', () => { + expect(runList( + '--include', 'src/**/*.clj', + '--ignore', 'src/nested/deep.clj' + )).toEqual(absoluteFiles( + 'src/core.clj', + 'src/generated/generated.clj' + )) + }) + + test('ignores all supported files beneath a directory', () => { + expect(runList( + '--include', 'src/**/*.{clj,cljs,cljc}', + '--ignore', 'src/generated' + )).toEqual(absoluteFiles( + 'src/browser.cljs', + 'src/core.clj', + 'src/nested/deep.clj', + 'src/nested/deep.cljs', + 'src/nested/deep.cljc', + 'src/shared.cljc' + )) + }) + + test('supports recursive ignore glob patterns', () => { + expect(runList( + '--include', 'src/**/*.{clj,cljs,cljc}', + '--ignore', 'src/generated/**/*' + )).toEqual(absoluteFiles( + 'src/browser.cljs', + 'src/core.clj', + 'src/nested/deep.clj', + 'src/nested/deep.cljs', + 'src/nested/deep.cljc', + 'src/shared.cljc' + )) + }) + + test('supports brace expansion in ignore patterns', () => { + expect(runList( + '--include', 'src/**/*.{clj,cljs,cljc}', + '--ignore', 'src/{generated,nested}/**/*.{clj,cljs,cljc}' + )).toEqual(absoluteFiles( + 'src/browser.cljs', + 'src/core.clj', + 'src/shared.cljc' + )) + }) + + test('combines multiple ignore patterns', () => { + expect(runList( + '--include', 'src/**/*.clj', + '--ignore', 'src/generated/**/*.clj', + '--ignore', 'src/nested/**/*.clj' + )).toEqual(absoluteFiles( + 'src/core.clj' + )) + }) + + test('applies ignore patterns to files discovered from a directory argument', () => { + expect(runList( + 'src', + '--ignore', 'src/{generated,nested}/**/*' + )).toEqual(absoluteFiles( + 'src/browser.cljs', + 'src/core.clj', + 'src/data.edn', + 'src/experimental.jank', + 'src/shared.cljc' + )) + }) +}) + +describe('configuration files and command-line precedence', () => { + test('loads include and ignore patterns from .standard-clj.json', () => { + writeJSONConfig({ + include: ['src/**/*.{clj,cljs,cljc}'], + ignore: ['src/generated/**/*'] + }) + + expect(runList()).toEqual(absoluteFiles( + 'src/browser.cljs', + 'src/core.clj', + 'src/nested/deep.clj', + 'src/nested/deep.cljs', + 'src/nested/deep.cljc', + 'src/shared.cljc' + )) + }) + + test('loads include and ignore patterns from .standard-clj.edn', () => { + writeEDNConfig( + '{:include ["src/**/*.{clj,cljs,cljc}"]\n' + + ' :ignore ["src/generated/**/*"]}\n' + ) + + expect(runList()).toEqual(absoluteFiles( + 'src/browser.cljs', + 'src/core.clj', + 'src/nested/deep.clj', + 'src/nested/deep.cljs', + 'src/nested/deep.cljc', + 'src/shared.cljc' + )) + }) + + test('command-line include patterns replace configured include patterns', () => { + writeJSONConfig({ + include: ['test/**/*.clj'] + }) + + expect(runList( + '--include', 'src/*.clj' + )).toEqual(absoluteFiles( + 'src/core.clj' + )) + }) + + test('direct file arguments replace configured include patterns', () => { + writeJSONConfig({ + include: ['test/**/*.clj'] + }) + + expect(runList('dev/user.clj')).toEqual(absoluteFiles( + 'dev/user.clj' + )) + }) + + test('configured ignore patterns still apply when include is supplied on the command line', () => { + writeJSONConfig({ + include: ['test/**/*.clj'], + ignore: ['src/generated/**/*'] + }) + + expect(runList( + '--include', 'src/**/*.clj' + )).toEqual(absoluteFiles( + 'src/core.clj', + 'src/nested/deep.clj' + )) + }) + + test('command-line ignore patterns replace configured ignore patterns', () => { + writeJSONConfig({ + include: ['src/**/*.clj'], + ignore: ['src/generated/**/*'] + }) + + expect(runList( + '--ignore', 'src/nested/**/*' + )).toEqual(absoluteFiles( + 'src/core.clj', + 'src/generated/generated.clj' + )) + }) +}) From 439ba61888ba0802ce16a41169be38a7e9cf4679 Mon Sep 17 00:00:00 2001 From: Chris Oakman Date: Sun, 2 Aug 2026 14:45:10 -0500 Subject: [PATCH 02/10] WIP WIP WIP --- cli.mjs | 116 ++++++++++----------------------- cli_file_discovery.mjs | 143 +++++++++++++++++++++++++++++++++++++++++ package.json | 2 +- 3 files changed, 177 insertions(+), 84 deletions(-) create mode 100644 cli_file_discovery.mjs diff --git a/cli.mjs b/cli.mjs index a62e61b..e8e66ba 100755 --- a/cli.mjs +++ b/cli.mjs @@ -18,7 +18,6 @@ import process from 'process' // npm imports import { parseEDNString, toEDNStringFromSimpleObject } from 'edn-data' -import { globSync } from 'glob' import yargs from 'yargs' import { hideBin } from 'yargs/helpers' import yocto from 'yoctocolors' @@ -28,7 +27,8 @@ import yocto from 'yoctocolors' // script before publishing to npm import standardClj from './lib/standard-clojure-style.js' // 7b323d1c-2984-4bd1-9304-d62d8dee9a1f -// Pure helper functions (no side effects, tested separately) +// CLI helpers +import cliFileDiscovery from './cli_file_discovery.mjs' import cliUtil from './cli_util.js' const scriptStartTime = performance.now() @@ -110,10 +110,6 @@ async function readStream (stream) { return Buffer.concat(chunks).toString('utf8') } -function alwaysTrue () { - return true -} - function setLogLevel (level) { logLevel = cliUtil.normalizeLogLevel(level) } @@ -176,94 +172,48 @@ function injectConfigFile (argv) { // ============================================================================= // returns a Set of files from the args passed to the "list", "check", or "fix" commands -function getFilesFromArgv (argv, cmd) { +function getFilesFromArgv (argv) { // remove the first item, which is the command argv._.shift() const directArgs = argv._ - const fileExtensionsSet = argv['file-ext'] - let includeFiles = [] - - // process the direct arguments - directArgs.forEach(arg => { - let possibleFileOrDir = arg - if (!fs.isAbsolute(arg)) { - // if the argument is not an absolute path, assume it is relative to the - // directory where the script is being run from - possibleFileOrDir = path.join(rootDir, arg) - } - - if (fs.isFileSync(possibleFileOrDir)) { - includeFiles.push(possibleFileOrDir) - } else if (fs.isDirectorySync(possibleFileOrDir)) { - fs.traverseTreeSync(possibleFileOrDir, (f) => { - const fileExt = path.extname(f) - if (fileExtensionsSet.has(fileExt)) { - includeFiles.push(f) - } - return true - }, alwaysTrue) - } else { - printToStderr(yocto.bold(yocto.yellow('WARN')) + ' Could not find a file or directory at "' + arg + '"') - } - }) - // process the --include glob patterns - if (cliUtil.isArray(argv.include)) { - argv.include.forEach(includeStr => { - const filesFromGlob = globSync(includeStr) - includeFiles = includeFiles.concat(filesFromGlob) - }) + let cliIncludePatterns = [] + const cliIncludeWasPassed = cliUtil.isArray(argv.include) + if (cliIncludeWasPassed) { + cliIncludePatterns = argv.include } - - // load --include files via config file if the user did not pass any direct arguments - const anyDirectArgsPassed = directArgs.length > 0 - if (!anyDirectArgsPassed && argv._optionsLoadedViaConfigFile && cliUtil.isArray(argv.includeFromConfig)) { - argv.includeFromConfig.forEach(includeStr => { - const filesFromGlob = globSync(includeStr) - includeFiles = includeFiles.concat(filesFromGlob) - }) + const anyCliFileSelection = directArgs.length > 0 || cliIncludeWasPassed + let includePatterns = cliIncludePatterns + if (!anyCliFileSelection && + argv._optionsLoadedViaConfigFile && + cliUtil.isArray(argv.includeFromConfig)) { + includePatterns = argv.includeFromConfig } - // exclude files if necessary - const ignoreFiles = [] - let ignorePatterns = null - // use --ignore from CLI argument + let ignorePatterns = [] if (cliUtil.isArray(argv.ignore)) { ignorePatterns = argv.ignore - // or from config file if present } else if (argv._optionsLoadedViaConfigFile && cliUtil.isArray(argv.ignoreFromConfig)) { ignorePatterns = argv.ignoreFromConfig } - if (ignorePatterns) { - ignorePatterns.forEach(ignoreStr => { - let possibleFileOrDir = ignoreStr - if (!fs.isAbsolute(ignoreStr)) { - // if the argument is not an absolute path, assume it is relative to the - // directory where the script is being run from - possibleFileOrDir = path.join(rootDir, ignoreStr) + return cliFileDiscovery.discoverFiles({ + rootDir, + directArgs, + includePatterns, + ignorePatterns, + fileExtensions: argv['file-ext'], + onMissingPath: (kind, filename) => { + let ignoreText = '' + if (kind === 'ignore') { + ignoreText = ' to ignore' } - - if (fs.isFileSync(possibleFileOrDir)) { - ignoreFiles.push(possibleFileOrDir) - } else if (fs.isDirectorySync(possibleFileOrDir)) { - fs.traverseTreeSync(possibleFileOrDir, (f) => { - const fileExt = path.extname(f) - if (fileExtensionsSet.has(fileExt)) { - ignoreFiles.push(f) - } - return true - }, alwaysTrue) - } else { - printToStderr(yocto.bold(yocto.yellow('WARN')) + ' Could not find a file or directory to ignore at "' + ignoreStr + '"') - } - }) - } - - const includeFilesSet = new Set(includeFiles) - const ignoreFileSet = new Set(ignoreFiles) - - return cliUtil.setDifference(includeFilesSet, ignoreFileSet) + printToStderr( + yocto.bold(yocto.yellow('WARN')) + + ' Could not find a file or directory' + ignoreText + ' at "' + filename + '"' + ) + } + }) } // ============================================================================= @@ -366,7 +316,7 @@ function processCheckCmd (argv) { printProgramInfo({ command: 'check' }) - const filesToProcess = getFilesFromArgv(argv, 'check') + const filesToProcess = getFilesFromArgv(argv) if (filesToProcess.size === 0) { exitSad('No files were passed to the "check" command. Please pass a filename, directory, or --include glob pattern.') @@ -413,7 +363,7 @@ function processFixCmdNotStdin (argv) { printProgramInfo({ command: 'fix' }) - const filesToProcess = getFilesFromArgv(argv, 'fix') + const filesToProcess = getFilesFromArgv(argv) if (filesToProcess.size === 0) { exitSad('No files were passed to the "fix" command. Please pass a filename, directory, or --include glob pattern.') @@ -487,7 +437,7 @@ function processFixCmd (argv) { } function processListCmd (argv) { - const filesSet = getFilesFromArgv(argv, 'list') + const filesSet = getFilesFromArgv(argv) const sortedFiles = setToArray(filesSet).sort() if (argv.output === 'json') { diff --git a/cli_file_discovery.mjs b/cli_file_discovery.mjs new file mode 100644 index 0000000..b87f871 --- /dev/null +++ b/cli_file_discovery.mjs @@ -0,0 +1,143 @@ +// Filesystem-backed file discovery for the Standard Clojure Style CLI. +// +// This module owns the behavior of positional files/directories, --include +// patterns, and --ignore patterns. The glob library is an implementation +// detail; callers should only depend on discoverFiles(). + +import fs from 'fs-plus' +import path from 'path' +import { globSync } from 'glob' + +function absolutePath (rootDir, filename) { + if (path.isAbsolute(filename)) { + return path.normalize(filename) + } + return path.resolve(rootDir, filename) +} + +function hasAllowedExtension (filename, fileExtensions) { + return fileExtensions.has(path.extname(filename)) +} + +function addFilesFromDirectory (files, directory, shouldIncludeFile) { + fs.traverseTreeSync(directory, filename => { + const absoluteFilename = path.resolve(filename) + if (fs.isFileSync(absoluteFilename) && shouldIncludeFile(absoluteFilename)) { + files.add(absoluteFilename) + } + return true + }, () => true) +} + +function filesFromGlob (rootDir, pattern) { + return globSync(pattern, { + absolute: true, + cwd: rootDir, + nodir: true + }) + .map(filename => absolutePath(rootDir, filename)) + .filter(filename => fs.isFileSync(filename)) +} + +// This is intentionally narrower than every pattern understood by "glob". +// Its only purpose is deciding whether a no-match --ignore value should be +// treated as a harmless pattern or warned about as a missing literal path. +function containsGlobSyntax (pattern) { + return pattern.includes('*') || + pattern.includes('?') || + pattern.includes('[') || + pattern.includes(']') || + pattern.includes('{') || + pattern.includes('}') +} + +function addDirectArgument (files, rootDir, directArg, fileExtensions, onMissingPath) { + const possibleFileOrDir = absolutePath(rootDir, directArg) + + if (fs.isFileSync(possibleFileOrDir)) { + // A directly named file is explicit, so allow any extension. + files.add(possibleFileOrDir) + } else if (fs.isDirectorySync(possibleFileOrDir)) { + addFilesFromDirectory( + files, + possibleFileOrDir, + filename => hasAllowedExtension(filename, fileExtensions) + ) + } else { + onMissingPath('include', directArg) + } +} + +function addIncludePattern (files, rootDir, pattern, fileExtensions) { + filesFromGlob(rootDir, pattern).forEach(filename => { + if (hasAllowedExtension(filename, fileExtensions)) { + files.add(filename) + } + }) +} + +function addIgnorePattern (files, rootDir, pattern, onMissingPath) { + const possibleFileOrDir = absolutePath(rootDir, pattern) + + if (fs.isFileSync(possibleFileOrDir)) { + files.add(possibleFileOrDir) + } else if (fs.isDirectorySync(possibleFileOrDir)) { + // Ignore every file beneath an explicitly named directory. Do not apply + // the extension filter here: ignores should also be able to remove a + // directly named file with a non-standard extension. + addFilesFromDirectory(files, possibleFileOrDir, () => true) + } else { + const globFiles = filesFromGlob(rootDir, pattern) + globFiles.forEach(filename => files.add(filename)) + + // A glob pattern that matches nothing is valid and should be silent. Keep + // the existing warning for a literal path that does not exist. + if (globFiles.length === 0 && !containsGlobSyntax(pattern)) { + onMissingPath('ignore', pattern) + } + } +} + +function discoverFiles (opts) { + const includeFiles = new Set() + const ignoreFiles = new Set() + const directArgs = opts.directArgs || [] + const includePatterns = opts.includePatterns || [] + const ignorePatterns = opts.ignorePatterns || [] + const onMissingPath = opts.onMissingPath || (() => {}) + + directArgs.forEach(directArg => { + addDirectArgument( + includeFiles, + opts.rootDir, + directArg, + opts.fileExtensions, + onMissingPath + ) + }) + + includePatterns.forEach(pattern => { + addIncludePattern( + includeFiles, + opts.rootDir, + pattern, + opts.fileExtensions + ) + }) + + ignorePatterns.forEach(pattern => { + addIgnorePattern(ignoreFiles, opts.rootDir, pattern, onMissingPath) + }) + + const discoveredFiles = new Set() + includeFiles.forEach(filename => { + if (!ignoreFiles.has(filename)) { + discoveredFiles.add(filename) + } + }) + return discoveredFiles +} + +export default { + discoverFiles +} diff --git a/package.json b/package.json index a1fbe88..4e3c22e 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "standard-clj": "cli.mjs" }, "scripts": { - "lint": "standard --fix lib/ scripts/ test/ ./cli.mjs ./cli_util.js", + "lint": "standard --fix lib/ scripts/ test/ ./cli.mjs ./cli_file_discovery.mjs ./cli_util.js", "test": "jest" }, "dependencies": { From 0e4cefa0c887684ee8ae6c344b75e573df62362f Mon Sep 17 00:00:00 2001 From: Chris Oakman Date: Mon, 3 Aug 2026 09:55:21 -0500 Subject: [PATCH 03/10] WIP WIP WIP --- bun.lockb | Bin 234582 -> 235679 bytes cli_file_discovery.mjs | 22 +- package.json | 1 + scripts/dump-codebase.js | 10 +- test/cli_file_discovery.test.js | 409 ++++++++++++++++++++++++++++++++ 5 files changed, 433 insertions(+), 9 deletions(-) diff --git a/bun.lockb b/bun.lockb index f227d76ab25f6888536b9729f20771836b169c75..cda00810ed20afb2c9bf605a7b93936f6201d248 100755 GIT binary patch delta 48041 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 D7xQyF delta 47215 zcmeFad3+Vc_V&AXLogc%^N@rDWRxjkCc!`gDhP&o4s(D20WvVpCeEOMNQ=cFDvAgy z0tZy^h#qHAQI8{_2sna@<559H-|y314Uwb2_r3RXKllD|-5;N+zSmbS+TQlDJ0z9nd2$`pZcLtaVp7j$a&e*C(a=K8YnNlRcgMhp0WCda41v_{x+oSmmXj@*De39Su~rbA}D*1GMYHGV5}4qbL_tn3&S3ROhE1z7<( zVMbEn)LAn^abo$)`|&*Y9UJ_Ya*|Mmz*&(JAZuk^zmQdGg-y& zBh`%?`J;LZr%X&5SCBuU96plOzjy%&H8kDI!rTe-G(MBi<=`qsQEqz9tQi`aD{Z-* zc&~EzAr+fr>iFZm4H*fQ4iyiq;~pwiyC@jm+==-G({nouCQ~-?6$tg_Aoe}z_x}X0Y(&G!p&77W_pOZ6V*38_gGeSWaRb^0dH@-NCr^2ME z1>>@3Qp?obyxf_gwr+M*&7x15``w?EJtK*!o*1m|tx5hg{EC!2(`Zg{vL)Fcg-GjA zh=4B6L25WJY3cJ53kt`p8>d?M?rlUWf1TCGw(*BKxvf`UQSmwwT3-{iD6+>*%C!}B zrGOg#0;&8ZNLEJi3*=Ws;&8znMyw;e#^HnZejL>$U&|~b#h>>^wMc2^3fmEbE zg_I+sI{W%t^icU_y7)a_%@isBW9pNG+mI^vZyc3f2XuApG4vu4n@Ok#%aIzQ*+>mx zHd2mUfs|b@3Ti5%yZUoD3&|=e&Ouf}zGE92h+Y}J3+2>MeWa$W3R3*9*l9{$y`1qE z!9*spugv(`Hn74P6eBBxRHM9UJN2^h*hVlWrtr1D< z;|EKfwPwfs}}{t*jc$#XXfV5 zIDaH=p+edyt~%6r=r*J(nmnT*za8s5%JSb0@f{7WGw-5ns2@Xe1t{K*)X2=CKE+rz zlBsjQj>Hu`KEfZ}*Qif*2gj1tBmGehjwR!!=FZB+COC;Kq?|@EGTN8Hs0VvkmgVEe z`1P~J=Hyd81h2R}GTd7rMa3ht{f0LpHQN1=S~)2kaO6-LdQIe;=yJXedIGWvQVtv# z=a2e#qx?adfn#c5Gh3nLxZI@N{0Rl2N${$7#?B zu!IctG&0ri(NLr+%;8EgGiUm|GcrUEuEIm;i5xX~{)p9~yhdUwQv6EBSq%rriehxd z`6Am+a7a2;z#gQ6Gv?)wo5EEof12NL1EdPnLaHb2r`x?|Zeh;2nW50v6cqn

OvK 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 { - if (hasAllowedExtension(filename, fileExtensions)) { - files.add(filename) + const possibleFileOrDir = absolutePath(rootDir, pattern) + + if (fs.isFileSync(possibleFileOrDir)) { + if (hasAllowedExtension(possibleFileOrDir, fileExtensions)) { + files.add(possibleFileOrDir) } - }) + } else if (fs.isDirectorySync(possibleFileOrDir)) { + addFilesFromDirectory( + files, + possibleFileOrDir, + filename => hasAllowedExtension(filename, fileExtensions) + ) + } else { + filesFromGlob(rootDir, pattern).forEach(filename => { + if (hasAllowedExtension(filename, fileExtensions)) { + files.add(filename) + } + }) + } } function addIgnorePattern (files, rootDir, pattern, onMissingPath) { diff --git a/package.json b/package.json index 4e3c22e..30c6922 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "edn-data": "1.1.2", "fs-plus": "3.1.1", "glob": "13.0.6", + "tinyglobby": "0.2.17", "yargs": "18.0.0", "yoctocolors": "2.1.2" }, diff --git a/scripts/dump-codebase.js b/scripts/dump-codebase.js index 6ee573c..163d525 100755 --- a/scripts/dump-codebase.js +++ b/scripts/dump-codebase.js @@ -7,7 +7,7 @@ const { globSync } = require('tinyglobby') // ====================== FILE LIST ====================== const patterns = [ 'README.md', - 'lib/standard-clojure-style.js', + // 'lib/standard-clojure-style.js', 'cli.mjs', 'cli_file_discovery.mjs', 'cli_util.js', @@ -19,11 +19,11 @@ const patterns = [ // 'test/parse_ns.test.js', // 'test/parser.test.js', - 'test/*.js', + 'test/*.js' - 'test_format/*.eno', - 'test_parse_ns/*.eno', - 'test_parser/*.eno' + // 'test_format/*.eno', + // 'test_parse_ns/*.eno', + // 'test_parser/*.eno' ] // ====================================================== diff --git a/test/cli_file_discovery.test.js b/test/cli_file_discovery.test.js index dfcf2fe..b688027 100644 --- a/test/cli_file_discovery.test.js +++ b/test/cli_file_discovery.test.js @@ -72,6 +72,36 @@ function runList (...args) { return JSON.parse(result.stdout) } +// Like runList, but does not require an empty stderr or exit code 0. +// Used to test warning behavior. Returns { files, status, stderr }. +function runListCaptureStderr (...args) { + const result = childProcess.spawnSync( + process.execPath, + [cliFile, 'list', ...args, '--output', 'json'], + { + cwd: projectDir, + encoding: 'utf8', + env: { + ...process.env, + FORCE_COLOR: '0' + }, + windowsHide: true + } + ) + + if (result.error) { + throw result.error + } + + expect(result.signal).toBeNull() + + return { + files: JSON.parse(result.stdout || '[]'), + status: result.status, + stderr: result.stderr + } +} + function writeJSONConfig (config) { writeFile('.standard-clj.json', JSON.stringify(config, null, 2) + '\n') } @@ -126,6 +156,187 @@ describe('direct file and directory arguments', () => { }) }) +describe('--include files and directories', () => { + test('includes a directly named file with a supported extension', () => { + expect(runList( + '--include', 'src/core.clj' + )).toEqual(absoluteFiles( + 'src/core.clj' + )) + }) + + test('filters a directly named file with a non-default extension', () => { + expect(runList( + '--include', 'src/notes.txt' + )).toEqual([]) + }) + + test('recursively discovers supported files in a literal directory', () => { + expect(runList( + '--include', 'src' + )).toEqual(absoluteFiles( + 'src/browser.cljs', + 'src/core.clj', + 'src/data.edn', + 'src/experimental.jank', + 'src/generated/generated.clj', + 'src/generated/generated.cljs', + 'src/nested/deep.clj', + 'src/nested/deep.cljs', + 'src/nested/deep.cljc', + 'src/shared.cljc' + )) + }) + + test('accepts a trailing slash on a literal directory', () => { + expect(runList( + '--include', 'src/' + )).toEqual(absoluteFiles( + 'src/browser.cljs', + 'src/core.clj', + 'src/data.edn', + 'src/experimental.jank', + 'src/generated/generated.clj', + 'src/generated/generated.cljs', + 'src/nested/deep.clj', + 'src/nested/deep.cljs', + 'src/nested/deep.cljc', + 'src/shared.cljc' + )) + }) + + test('recursively discovers files from a nested literal directory', () => { + expect(runList( + '--include', 'src/nested' + )).toEqual(absoluteFiles( + 'src/nested/deep.clj', + 'src/nested/deep.cljs', + 'src/nested/deep.cljc' + )) + }) + + test('supports an absolute literal directory', () => { + expect(runList( + '--include', path.join(projectDir, 'src/nested') + )).toEqual(absoluteFiles( + 'src/nested/deep.clj', + 'src/nested/deep.cljs', + 'src/nested/deep.cljc' + )) + }) + + test('combines multiple literal directories', () => { + expect(runList( + '--include', 'dev', + '--include', 'test' + )).toEqual(absoluteFiles( + 'dev/user.clj', + 'test/core_test.clj', + 'test/nested/browser_test.cljs' + )) + }) + + test('combines a literal directory with a glob pattern', () => { + expect(runList( + '--include', 'dev', + '--include', 'src/**/*.cljc' + )).toEqual(absoluteFiles( + 'dev/user.clj', + 'src/nested/deep.cljc', + 'src/shared.cljc' + )) + }) + + test('deduplicates a literal directory and an overlapping glob pattern', () => { + expect(runList( + '--include', 'src', + '--include', 'src/**/*.clj' + )).toEqual(absoluteFiles( + 'src/browser.cljs', + 'src/core.clj', + 'src/data.edn', + 'src/experimental.jank', + 'src/generated/generated.clj', + 'src/generated/generated.cljs', + 'src/nested/deep.clj', + 'src/nested/deep.cljs', + 'src/nested/deep.cljc', + 'src/shared.cljc' + )) + }) + + test('uses --file-ext when discovering files from a literal directory', () => { + expect(runList( + '--include', 'src', + '--file-ext', 'clj,cljc' + )).toEqual(absoluteFiles( + 'src/core.clj', + 'src/generated/generated.clj', + 'src/nested/deep.clj', + 'src/nested/deep.cljc', + 'src/shared.cljc' + )) + }) + + test('applies literal directory ignores to a literal include directory', () => { + expect(runList( + '--include', 'src', + '--ignore', 'src/generated' + )).toEqual(absoluteFiles( + 'src/browser.cljs', + 'src/core.clj', + 'src/data.edn', + 'src/experimental.jank', + 'src/nested/deep.clj', + 'src/nested/deep.cljs', + 'src/nested/deep.cljc', + 'src/shared.cljc' + )) + }) + + test('applies ignore globs to a literal include directory', () => { + expect(runList( + '--include', 'src', + '--ignore', 'src/{generated,nested}/**/*' + )).toEqual(absoluteFiles( + 'src/browser.cljs', + 'src/core.clj', + 'src/data.edn', + 'src/experimental.jank', + 'src/shared.cljc' + )) + }) + + test('recursively discovers supported files from the current directory', () => { + expect(runList( + '--include', '.' + )).toEqual(absoluteFiles( + 'dev/user.clj', + 'resources/config.edn', + 'src/browser.cljs', + 'src/core.clj', + 'src/data.edn', + 'src/experimental.jank', + 'src/generated/generated.clj', + 'src/generated/generated.cljs', + 'src/nested/deep.clj', + 'src/nested/deep.cljs', + 'src/nested/deep.cljc', + 'src/shared.cljc', + 'test/core_test.clj', + 'test/nested/browser_test.cljs' + )) + }) + + test('returns an empty list for an empty literal directory', () => { + fs.mkdirSync(path.join(projectDir, 'empty')) + + expect(runList( + '--include', 'empty' + )).toEqual([]) + }) +}) + describe('--include patterns', () => { test('* matches files in one directory level', () => { expect(runList('--include', 'src/*.clj')).toEqual(absoluteFiles( @@ -304,6 +515,85 @@ describe('--ignore patterns', () => { }) describe('configuration files and command-line precedence', () => { + test('loads a literal include directory from .standard-clj.json', () => { + writeJSONConfig({ + include: ['src'] + }) + + expect(runList()).toEqual(absoluteFiles( + 'src/browser.cljs', + 'src/core.clj', + 'src/data.edn', + 'src/experimental.jank', + 'src/generated/generated.clj', + 'src/generated/generated.cljs', + 'src/nested/deep.clj', + 'src/nested/deep.cljs', + 'src/nested/deep.cljc', + 'src/shared.cljc' + )) + }) + + test('loads a literal include directory with a trailing slash from .standard-clj.edn', () => { + writeEDNConfig( + '{:include ["src/"]}\n' + ) + + expect(runList()).toEqual(absoluteFiles( + 'src/browser.cljs', + 'src/core.clj', + 'src/data.edn', + 'src/experimental.jank', + 'src/generated/generated.clj', + 'src/generated/generated.cljs', + 'src/nested/deep.clj', + 'src/nested/deep.cljs', + 'src/nested/deep.cljc', + 'src/shared.cljc' + )) + }) + + test('loads multiple literal include directories from configuration', () => { + writeJSONConfig({ + include: ['dev', 'test'] + }) + + expect(runList()).toEqual(absoluteFiles( + 'dev/user.clj', + 'test/core_test.clj', + 'test/nested/browser_test.cljs' + )) + }) + + test('applies configured ignore patterns to a configured literal directory', () => { + writeJSONConfig({ + include: ['src'], + ignore: ['src/{generated,nested}/**/*'] + }) + + expect(runList()).toEqual(absoluteFiles( + 'src/browser.cljs', + 'src/core.clj', + 'src/data.edn', + 'src/experimental.jank', + 'src/shared.cljc' + )) + }) + + test('command-line literal directories replace configured include patterns', () => { + writeJSONConfig({ + include: ['test/**/*.clj'] + }) + + expect(runList( + '--include', 'src/nested' + )).toEqual(absoluteFiles( + 'src/nested/deep.clj', + 'src/nested/deep.cljs', + 'src/nested/deep.cljc' + )) + }) + test('loads include and ignore patterns from .standard-clj.json', () => { writeJSONConfig({ include: ['src/**/*.{clj,cljs,cljc}'], @@ -386,3 +676,122 @@ describe('configuration files and command-line precedence', () => { )) }) }) + +describe('warnings for missing paths', () => { + test('warns on stderr when a direct argument does not exist', () => { + const result = runListCaptureStderr('does-not-exist.clj', 'src/core.clj') + + expect(result.status).toBe(0) + expect(result.stderr).toContain('WARN') + expect(result.stderr).toContain('does-not-exist.clj') + expect(result.stderr).not.toContain('to ignore') + expect(result.files).toEqual(absoluteFiles('src/core.clj')) + }) + + test('warns on stderr when a literal --ignore path does not exist', () => { + const result = runListCaptureStderr( + 'src/core.clj', + '--ignore', 'no-such-path' + ) + + expect(result.status).toBe(0) + expect(result.stderr).toContain('WARN') + expect(result.stderr).toContain('no-such-path') + expect(result.stderr).toContain('to ignore') + expect(result.files).toEqual(absoluteFiles('src/core.clj')) + }) + + test('an --ignore glob pattern that matches nothing is silent', () => { + // NOTE: runList itself asserts stderr === '' and exit code 0, + // which is exactly the contract this test exists to pin + expect(runList( + '--include', 'src/*.clj', + '--ignore', 'does-not-exist/**/*' + )).toEqual(absoluteFiles( + 'src/core.clj' + )) + }) +}) + +describe('absolute glob patterns', () => { + // NOTE: glob patterns always use forward slashes, even on Windows, + // so these are constructed via string concatenation, not path.join + test('supports an absolute --include glob pattern', () => { + expect(runList( + '--include', projectDir + '/src/**/*.cljc' + )).toEqual(absoluteFiles( + 'src/nested/deep.cljc', + 'src/shared.cljc' + )) + }) + + test('supports an absolute --ignore glob pattern', () => { + expect(runList( + '--include', 'src/**/*.cljc', + '--ignore', projectDir + '/src/nested/**/*' + )).toEqual(absoluteFiles( + 'src/shared.cljc' + )) + }) +}) + +describe('ignoring explicitly named files', () => { + test('ignores a directly named file with a non-default extension', () => { + expect(runList( + 'src/notes.txt', + '--ignore', 'src/notes.txt' + )).toEqual([]) + }) + + test('a directory ignore removes a directly named file with a non-default extension', () => { + // the ignore side intentionally applies no extension filter when + // traversing an ignored directory + expect(runList( + 'src/notes.txt', + '--ignore', 'src' + )).toEqual([]) + }) +}) + +describe('hidden files and directories', () => { + // These two tests pin the CURRENT (divergent) behavior: literal directory + // traversal descends into hidden directories, while glob patterns use the + // standard "dot: false" default and skip them. If you decide to unify + // this behavior, update these tests deliberately. + test('literal directory traversal descends into hidden directories', () => { + writeFile('src/.hidden/secret.clj', '(ns secret)\n') + + expect(runList('src')).toContain( + path.join(projectDir, 'src/.hidden/secret.clj') + ) + }) + + test('glob patterns do not match files inside hidden directories', () => { + writeFile('src/.hidden/secret.clj', '(ns secret)\n') + + expect(runList('--include', 'src/**/*.clj')).toEqual(absoluteFiles( + 'src/core.clj', + 'src/generated/generated.clj', + 'src/nested/deep.clj' + )) + }) +}) + +describe('glob library edge cases', () => { + test('a pattern matching only a directory yields no files', () => { + // "src*" is not a literal path, so it falls through to the glob library, + // where it matches the src/ directory itself. With nodir (glob) or + // onlyFiles (tinyglobby) semantics this must yield no files -- it must + // NOT silently expand into the directory's contents. When swapping to + // tinyglobby, pass expandDirectories: false to preserve this. + expect(runList('--include', 'src*')).toEqual([]) + }) + + test('a pattern matching a directory does not shadow sibling file matches', () => { + writeFile('srcfile.clj', '(ns srcfile)\n') + + expect(runList('--include', 'src*')).toEqual(absoluteFiles( + 'srcfile.clj' + )) + }) +}) From 2810d132dffaccc3dafa88d093a7bd2c2c1a0e00 Mon Sep 17 00:00:00 2001 From: Chris Oakman Date: Mon, 3 Aug 2026 10:20:02 -0500 Subject: [PATCH 04/10] WIP WIP WIP --- bun.lockb | Bin 235679 -> 232752 bytes cli_file_discovery.mjs | 6 ++++-- package.json | 1 - 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/bun.lockb b/bun.lockb index cda00810ed20afb2c9bf605a7b93936f6201d248..7793d5c0a5599570ba9db8a11b011186bb4a2051 100755 GIT binary patch delta 46282 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 delta 47912 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 diff --git a/cli_file_discovery.mjs b/cli_file_discovery.mjs index 144bd78..e0d34ff 100644 --- a/cli_file_discovery.mjs +++ b/cli_file_discovery.mjs @@ -6,7 +6,7 @@ import fs from 'fs-plus' import path from 'path' -import { globSync } from 'glob' +import { globSync } from 'tinyglobby' function absolutePath (rootDir, filename) { if (path.isAbsolute(filename)) { @@ -33,8 +33,10 @@ function filesFromGlob (rootDir, pattern) { return globSync(pattern, { absolute: true, cwd: rootDir, - nodir: true + expandDirectories: false, + onlyFiles: true }) + .map(filename => absolutePath(rootDir, filename)) .filter(filename => fs.isFileSync(filename)) } diff --git a/package.json b/package.json index 30c6922..8cd8028 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,6 @@ "dependencies": { "edn-data": "1.1.2", "fs-plus": "3.1.1", - "glob": "13.0.6", "tinyglobby": "0.2.17", "yargs": "18.0.0", "yoctocolors": "2.1.2" From 4c53b690afa739b38d43216cac58414b40a61139 Mon Sep 17 00:00:00 2001 From: Chris Oakman Date: Mon, 3 Aug 2026 10:47:40 -0500 Subject: [PATCH 05/10] WIP WIP WIP --- cli_file_discovery.mjs | 12 +++++---- test/cli_file_discovery.test.js | 46 --------------------------------- 2 files changed, 7 insertions(+), 51 deletions(-) diff --git a/cli_file_discovery.mjs b/cli_file_discovery.mjs index e0d34ff..6593124 100644 --- a/cli_file_discovery.mjs +++ b/cli_file_discovery.mjs @@ -30,20 +30,22 @@ function addFilesFromDirectory (files, directory, shouldIncludeFile) { } function filesFromGlob (rootDir, pattern) { - return globSync(pattern, { + const tinyGlobbyOpts = { absolute: true, + braceExpansion: true, cwd: rootDir, expandDirectories: false, + globstar: true, onlyFiles: true - }) + } + return globSync(pattern, tinyGlobbyOpts) .map(filename => absolutePath(rootDir, filename)) .filter(filename => fs.isFileSync(filename)) } -// This is intentionally narrower than every pattern understood by "glob". -// Its only purpose is deciding whether a no-match --ignore value should be -// treated as a harmless pattern or warned about as a missing literal path. +// This only identifies values that should be treated as patterns when +// deciding whether an unmatched --ignore value should produce a warning. function containsGlobSyntax (pattern) { return pattern.includes('*') || pattern.includes('?') || diff --git a/test/cli_file_discovery.test.js b/test/cli_file_discovery.test.js index b688027..a072deb 100644 --- a/test/cli_file_discovery.test.js +++ b/test/cli_file_discovery.test.js @@ -713,28 +713,6 @@ describe('warnings for missing paths', () => { }) }) -describe('absolute glob patterns', () => { - // NOTE: glob patterns always use forward slashes, even on Windows, - // so these are constructed via string concatenation, not path.join - test('supports an absolute --include glob pattern', () => { - expect(runList( - '--include', projectDir + '/src/**/*.cljc' - )).toEqual(absoluteFiles( - 'src/nested/deep.cljc', - 'src/shared.cljc' - )) - }) - - test('supports an absolute --ignore glob pattern', () => { - expect(runList( - '--include', 'src/**/*.cljc', - '--ignore', projectDir + '/src/nested/**/*' - )).toEqual(absoluteFiles( - 'src/shared.cljc' - )) - }) -}) - describe('ignoring explicitly named files', () => { test('ignores a directly named file with a non-default extension', () => { expect(runList( @@ -753,30 +731,6 @@ describe('ignoring explicitly named files', () => { }) }) -describe('hidden files and directories', () => { - // These two tests pin the CURRENT (divergent) behavior: literal directory - // traversal descends into hidden directories, while glob patterns use the - // standard "dot: false" default and skip them. If you decide to unify - // this behavior, update these tests deliberately. - test('literal directory traversal descends into hidden directories', () => { - writeFile('src/.hidden/secret.clj', '(ns secret)\n') - - expect(runList('src')).toContain( - path.join(projectDir, 'src/.hidden/secret.clj') - ) - }) - - test('glob patterns do not match files inside hidden directories', () => { - writeFile('src/.hidden/secret.clj', '(ns secret)\n') - - expect(runList('--include', 'src/**/*.clj')).toEqual(absoluteFiles( - 'src/core.clj', - 'src/generated/generated.clj', - 'src/nested/deep.clj' - )) - }) -}) - describe('glob library edge cases', () => { test('a pattern matching only a directory yields no files', () => { // "src*" is not a literal path, so it falls through to the glob library, From 7b63f2dcd99ca97de6ae1cfcba2f9b7754d9639f Mon Sep 17 00:00:00 2001 From: Chris Oakman Date: Mon, 3 Aug 2026 10:50:21 -0500 Subject: [PATCH 06/10] WIP WIP WIP --- scripts/dump-codebase.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/dump-codebase.js b/scripts/dump-codebase.js index 163d525..6ee573c 100755 --- a/scripts/dump-codebase.js +++ b/scripts/dump-codebase.js @@ -7,7 +7,7 @@ const { globSync } = require('tinyglobby') // ====================== FILE LIST ====================== const patterns = [ 'README.md', - // 'lib/standard-clojure-style.js', + 'lib/standard-clojure-style.js', 'cli.mjs', 'cli_file_discovery.mjs', 'cli_util.js', @@ -19,11 +19,11 @@ const patterns = [ // 'test/parse_ns.test.js', // 'test/parser.test.js', - 'test/*.js' + 'test/*.js', - // 'test_format/*.eno', - // 'test_parse_ns/*.eno', - // 'test_parser/*.eno' + 'test_format/*.eno', + 'test_parse_ns/*.eno', + 'test_parser/*.eno' ] // ====================================================== From 4abfd372c1ff3c2d60ab5ac81f5689125fd606ba Mon Sep 17 00:00:00 2001 From: Chris Oakman Date: Mon, 3 Aug 2026 11:06:02 -0500 Subject: [PATCH 07/10] WIP WIP WIP --- test/cli_file_discovery.test.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/test/cli_file_discovery.test.js b/test/cli_file_discovery.test.js index a072deb..cf40703 100644 --- a/test/cli_file_discovery.test.js +++ b/test/cli_file_discovery.test.js @@ -731,13 +731,12 @@ describe('ignoring explicitly named files', () => { }) }) -describe('glob library edge cases', () => { +describe('patterns that also match directories', () => { test('a pattern matching only a directory yields no files', () => { - // "src*" is not a literal path, so it falls through to the glob library, - // where it matches the src/ directory itself. With nodir (glob) or - // onlyFiles (tinyglobby) semantics this must yield no files -- it must - // NOT silently expand into the directory's contents. When swapping to - // tinyglobby, pass expandDirectories: false to preserve this. + // "src*" is not a literal path, so it is treated as a pattern. It matches + // the src/ directory itself, which must yield no files: only regular + // files are ever returned, and a matched directory must NOT silently + // expand into its contents. expect(runList('--include', 'src*')).toEqual([]) }) From 72cd805f365d54ff17948c9ba68a72e7dc3e855a Mon Sep 17 00:00:00 2001 From: Chris Oakman Date: Mon, 3 Aug 2026 11:10:10 -0500 Subject: [PATCH 08/10] WIP WIP WIP --- README.md | 170 +++++-------------------- docs/cli.md | 347 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 376 insertions(+), 141 deletions(-) create mode 100644 docs/cli.md diff --git a/README.md b/README.md index 7d36d14..91e737f 100644 --- a/README.md +++ b/README.md @@ -106,179 +106,67 @@ but I do not want this project to live in "pre-1.0" forever. ## Command Line Usage -The `@chrisoakman/standard-clojure-style` npm package exposes a command-line -tool to help format your Clojure projects. You may wish to run this as a git -hook, via continuous integration, an editor integration, etc. - -If you have Node.js installed on your system, you can try out Standard Clojure -Style with the `npx` command: +Use `list` to preview files, `check` to verify formatting, and `fix` to format +files in place. `check` does not modify files; `fix` does. ```sh -## NOTE: the "fix" command will change your files on disk! -## Please ensure a clean git working tree or new branch as necessary - -# formats the file located at src/com/example/foo.clj -npx @chrisoakman/standard-clojure-style fix src/com/example/foo.clj - -# formats all .clj, .cljs, .cljc, .jank, .edn files found in the src/ directory -# and subdirectories (ie: recursive) -npx @chrisoakman/standard-clojure-style fix src/ +standard-clj list src/ test/ +standard-clj check src/ test/ +standard-clj fix src/ test/ ``` -If you plan to use the library frequently you may wish to install it globally: +Directories are searched recursively. By default, Standard Clojure Style finds +`.clj`, `.cljs`, `.cljc`, `.jank`, and `.edn` files. You can also pass individual +files: ```sh -# Installs "standard-clj" globally onto your system via npm -npm install --global @chrisoakman/standard-clojure-style -``` - -#### Quick Reference - -```sh -# use the "list" command to see which files standard-clj will analyze -standard-clj list src/ - -# use the "check" command to see which files need formatting -standard-clj check src-clj/ src-cljs/ - -## use the "fix" command to format files with Standard Clojure Style -standard-clj fix src/ test/ project.clj - -## you can pass a glob pattern for more control over which files are formatted -standard-clj fix --include "src/**/*.{clj,cljs,cljc}" - -## ignore files or folders with the --ignore flag -standard-clj fix --include "src/**/*.{clj,cljs,cljc}" --ignore "src/com/example/some_weird_file.clj" - -## standard-clj will look for a .standard-clj.edn or .standard-clj.json file in the directory where -## the command is run from (likely the root directory for your project) -echo '{:include ["src-clj/**/*.clj" "src-cljs/**/*.cljs"]}' > .standard-clj.edn -standard-clj fix - -## or pass a config file explicitly using the --config argument -standard-clj list --config /home/user1/my-project/my-standard-cfg.json - -## pipe code directly to the fix command using "-" -echo '(ns my.company.core (:require [clojure.string :as str]))' | standard-clj fix - +standard-clj fix src/my_app/core.clj deps.edn ``` -#### `list` command - -Use `standard-clj list` to see which files will be effected by the `check` and -`fix` commands. This command is useful in order to test your `--include` -glob patterns or `.standard-clj.edn` config files. +For more control, use `--include` and `--ignore`: ```sh -# prints each filename that will be effected by the "check" and "fix" commands -standard-clj list src/ - -# output the same file list in various data formats -standard-clj list src/ --output json -standard-clj list src/ --output json-pretty -standard-clj list src/ --output edn -standard-clj list src/ --output edn-pretty +standard-clj check \ + --include "src/**/*.{clj,cljs,cljc}" \ + --ignore "src/generated/**/*" ``` -#### `check` command - -Use `standard-clj check` to see if files are already formatted with Standard -Clojure Style. Useful for continuous integration. This command will **not** write -to any files on disk. - -Returns exit code 0 if all files are already formatted, 1 otherwise. +Most projects that use Standard Clojure Style regularly should commit a +`.standard-clj.edn` file: -```sh -# check to see if files are already formatted with Standard Clojure Style -standard-clj check src-clj/ src-cljs/ test/ - -# runs the same check, but only prints files that need fixing -standard-clj check src-clj/ src-cljs/ test/ --log-level=ignore-already-formatted +```clojure +{:include ["src/" "test/"] + :ignore ["src/generated/"]} ``` -#### `fix` command - -Use `standard-clj fix` to format files according to Standard Clojure Style. -This command **will** write to files on disk, so please ensure a clean git -working tree or new branch as necessary. The changes made by this command -cannot be undone by this program. - -Returns exit code 0 if all files have been formatted, 1 otherwise. +Then run: ```sh -# format files according to Standard Clojure Style -standard-clj fix src/ test/ deps.edn +standard-clj check ``` -#### `fix -` command (stdin / stdout) - -Use `standard-clj fix -` to pipe code directly via stdin. +Use `standard-clj list` whenever you want to confirm which files were selected. -Prints the formatted code to stdout with error code 0 if successful. Prints an -error message to stderr with error code 1 otherwise. +Run the package without installation: ```sh -echo '(ns my.company.core (:require [clojure.string :as str]))' | standard-clj fix - +npx @chrisoakman/standard-clojure-style check src/ test/ ``` -#### Which files will be formatted? - -`standard-clj` accepts several ways to know which files to format: - -* pass filenames directly as arguments -* pass directories directly as arguments -* pass a [glob pattern] with the `--include` option +Or install the CLI globally: ```sh -# will fix: -# - dev/user.clj (single file argument) -# - project.clj (single file argument) -# - all .clj, .cljs, .cljc, .edn files in the src-clj/ directory and subdirectories (directory argument) -# - all .edn files in the resources/ directory and subdirectories (glob pattern argument) -standard-clj fix dev/user.clj project.clj src-clj/ test/ --include "resources/**/*.edn" +npm install --global @chrisoakman/standard-clojure-style ``` -`--include` or `--ignore` arguments passed via command line will supercede any -`--include` or `--ignore` arguments found via config file. - -You can always use the `list` command to see which files will be formatted by `standard-clj`. - -#### Other options - -- `--config` or `-c` - pass a filepath of a config file to use for options to the `standard-clj` program. -- `--ignore` or `-ig` - exclude files from `list`, `check`, or `fix` commands. Accepts individual files or directories. -- `--include` or `-in` - include files for the `list`, `check`, or `fix` commands. Accepts a [glob pattern]. -- `--log-level` or `-l` - specify a logging level - - `"everything"` or `0` - prints everything to either stdout or stderr. This is the default. - - `"ignore-already-formatted"` or `1` - - For the `check` command, will only print files that need formatting. - - For the `fix` command, will only print files that were formatted or have errors. - - This option can be less noisy in your terminal if you have a project with many files and only - want to see the ones that need formatting. - - `"quiet"` or `5` - will not print anything to stdout or stderr for the `check` or `fix` commands - -[glob pattern]:https://github.com/isaacs/node-glob?tab=readme-ov-file#glob-primer - -#### Options via config file - -By default, `standard-clj` will look for a `.standard-clj.edn` or -`.standard-clj.json` file located in the directory where the command is run. -Most projects that use `standard-clj` regularly will want to commit this file -to their git repo for convenience. +The `fix` command also supports stdin: ```sh -# create a .standard-clj.edn file -echo '{:include ["src-clj/**/*.clj" "src-cljs/**/*.cljs"]}' > .standard-clj.edn - -# run the "fix" command with options from that file -standard-clj fix +echo '(ns my.company.core)' | standard-clj fix - ``` -You can use the `--config` or `-c` flag to specify a different file location: - -```sh -# run the "fix" command with options from ./my-config-file.edn -standard-clj fix --config ./my-config-file.edn -``` +See [CLI file selection and configuration](docs/cli.md) for config-file formats, +glob syntax, option precedence, custom file extensions, and additional examples. ## Ignore a file or form diff --git a/docs/cli.md b/docs/cli.md new file mode 100644 index 0000000..0ff455a --- /dev/null +++ b/docs/cli.md @@ -0,0 +1,347 @@ +# CLI File Selection and Configuration + +Most projects only need this: + +```sh +standard-clj check src/ test/ +standard-clj fix src/ test/ +``` + +Directories are searched recursively for `.clj`, `.cljs`, `.cljc`, `.jank`, +and `.edn` files. + +Use `standard-clj list` to preview the exact files selected by any command: + +```sh +standard-clj list src/ test/ +``` + +## Commands + +### `list` + +Print the files that `check` or `fix` would process: + +```sh +standard-clj list src/ +``` + +Machine-readable output is also available: + +```sh +standard-clj list src/ --output json +standard-clj list src/ --output json-pretty +standard-clj list src/ --output edn +standard-clj list src/ --output edn-pretty +``` + +### `check` + +Check formatting without changing files: + +```sh +standard-clj check src/ test/ +``` + +Exit code `0` means every selected file is formatted. Exit code `1` means at +least one file needs formatting or produced an error. + +### `fix` + +Format selected files in place: + +```sh +standard-clj fix src/ test/ +``` + +Use a clean git working tree before running `fix`. + +Code can also be read from stdin: + +```sh +echo '(ns my.company.core)' | standard-clj fix - +``` + +## Selecting Files + +Files can be selected in three ways. + +### Individual files + +```sh +standard-clj check src/my_app/core.clj deps.edn +``` + +A file passed directly is explicit and is accepted regardless of its extension. + +### Directories + +```sh +standard-clj check src/ test/ +``` + +Directories are searched recursively. Only files with the configured extensions +are selected. + +### `--include` + +`--include` accepts a file, directory, or glob pattern: + +```sh +standard-clj check --include src/ +standard-clj check --include "src/**/*.clj" +standard-clj check --include "src/**/*.{clj,cljs,cljc}" +``` + +The option can be repeated: + +```sh +standard-clj check \ + --include src-clj/ \ + --include src-cljs/ \ + --include test/ +``` + +Direct arguments and command-line `--include` values are combined and +deduplicated. + +Files selected through `--include` must have one of the configured extensions. +This differs slightly from passing an individual file directly. + +## Ignoring Files + +`--ignore` accepts a file, directory, or glob pattern: + +```sh +standard-clj check src/ \ + --ignore src/generated/ \ + --ignore "src/**/fixtures/**/*" +``` + +The option can be repeated. Ignored files are removed after all file-selection +inputs have been combined. + +A directory passed literally to `--ignore` removes every selected file beneath +that directory. + +## Supported Glob Syntax + +Standard Clojure Style officially supports these pattern features: + +- `*` matches within one path segment. +- `**` recursively matches directories. +- `{a,b,c}` matches one of several alternatives. + +Examples: + +```text +src/*.clj +src/**/*.clj +src/**/*.{clj,cljs,cljc} +{src,test}/**/*.{clj,cljs,cljc} +``` + +Quote glob patterns in your shell: + +```sh +standard-clj check --include "src/**/*.{clj,cljs,cljc}" +``` + +Other syntax may happen to work through the underlying glob library, but it is +not part of Standard Clojure Style's documented compatibility contract. Prefer +relative glob patterns. Absolute literal files and directories are supported. + +A literal directory is recursive: + +```sh +standard-clj check --include src/ +``` + +A wildcard pattern that happens to match a directory does not automatically +turn that directory into a recursive search. Use a literal directory or `**` +when recursion is intended. + +## File Extensions + +The default extensions are: + +```text +.clj +.cljs +.cljc +.jank +.edn +``` + +Override them with `--file-ext`: + +```sh +standard-clj check src/ --file-ext clj,cljc +``` + +The extension filter applies to files discovered through directories, +`--include`, and glob patterns. + +A file passed directly as a positional argument is accepted regardless of its +extension. + +## Project Configuration + +By default, Standard Clojure Style looks in the current working directory for +one of these files: + +```text +.standard-clj.edn +.standard-clj.json +``` + +Use one config file per project. + +EDN example: + +```clojure +{:include ["src/" "test/"] + :ignore ["src/generated/"]} +``` + +JSON example: + +```json +{ + "include": ["src/", "test/"], + "ignore": ["src/generated/"] +} +``` + +The `include` and `ignore` values may be a single string or an array of strings. +Arrays are recommended because they are easy to extend. + +Run the CLI without file arguments to use the configured selection: + +```sh +standard-clj list +standard-clj check +standard-clj fix +``` + +Use a different config file with `--config` or `-c`: + +```sh +standard-clj check --config ./config/standard-clj.edn +``` + +Relative files, directories, and patterns are resolved from the directory where +the command is run, not from the config file's directory. + +Supported config keys are `include`, `ignore`, and `log-level`. The +`--file-ext` option is command-line only. + +## Command-Line and Config Precedence + +Command-line file selection replaces configured `include` values. + +This means either of these causes config-file `include` values to be ignored: + +```sh +standard-clj check src/ +standard-clj check --include src/ +``` + +Direct arguments and command-line `--include` values can still be used together: + +```sh +standard-clj check dev/user.clj --include src/ +``` + +A command-line `--ignore` replaces configured `ignore` values. + +When file selection is supplied on the command line but `--ignore` is not, the +configured `ignore` values still apply. + +Example config: + +```clojure +{:include ["test/"] + :ignore ["src/generated/"]} +``` + +This command replaces the configured `include`, selects `src/`, and still +excludes `src/generated/`: + +```sh +standard-clj check src/ +``` + +This command replaces both configured values: + +```sh +standard-clj check src/ --ignore src/fixtures/ +``` + +## Common Project Layouts + +### Standard Clojure project + +```clojure +{:include ["src/" "test/"]} +``` + +### Separate Clojure and ClojureScript directories + +```clojure +{:include ["src-clj/" "src-cljs/" "test/"]} +``` + +### Ignore generated source + +```clojure +{:include ["src/" "test/"] + :ignore ["src/generated/"]} +``` + +### Select only common Clojure source extensions + +```clojure +{:include ["src/**/*.{clj,cljs,cljc}" + "test/**/*.{clj,cljs,cljc}"]} +``` + +### Include EDN resources + +```clojure +{:include ["src/" "test/" "resources/**/*.edn"]} +``` + +## Missing Paths and Empty Matches + +A missing direct file or directory prints a warning and does not stop other +valid inputs from being processed. + +A missing literal `--ignore` path also prints a warning. + +A glob pattern that matches nothing is valid and produces no files. This is +useful for configuration shared across projects where some directories may not +exist. + +## Other Options + +### Logging + +```sh +standard-clj check src/ --log-level everything +standard-clj check src/ --log-level ignore-already-formatted +standard-clj check src/ --log-level quiet +``` + +The aliases are `--log-level` / `-l`. + +### Option aliases + +```text +--config -c +--include -in +--ignore -ig +--log-level -l +``` + +Run `standard-clj --help` for the complete command-line help. From ba21ca56cd3f8836084af51758696f054b18c834 Mon Sep 17 00:00:00 2001 From: Chris Oakman Date: Mon, 3 Aug 2026 11:21:40 -0500 Subject: [PATCH 09/10] WIP WIP WIP --- README.md | 9 +++++++-- docs/cli.md | 2 ++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 91e737f..34c95d7 100644 --- a/README.md +++ b/README.md @@ -112,6 +112,9 @@ files in place. `check` does not modify files; `fix` does. ```sh standard-clj list src/ test/ standard-clj check src/ test/ + +# NOTE: "fix" writes to your files on disk and cannot undo its changes. +# Please ensure a clean git working tree or new branch as necessary. standard-clj fix src/ test/ ``` @@ -165,8 +168,10 @@ The `fix` command also supports stdin: echo '(ns my.company.core)' | standard-clj fix - ``` -See [CLI file selection and configuration](docs/cli.md) for config-file formats, -glob syntax, option precedence, custom file extensions, and additional examples. +See the [CLI docs] for config-file formats, glob syntax, option precedence, +custom file extensions, and additional examples. + +[CLI docs]:https://github.com/oakmac/standard-clojure-style-js/blob/master/docs/cli.md ## Ignore a file or form diff --git a/docs/cli.md b/docs/cli.md index 0ff455a..2315927 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -323,6 +323,8 @@ A glob pattern that matches nothing is valid and produces no files. This is useful for configuration shared across projects where some directories may not exist. +If no files are selected at all, `check` and `fix` exit with code 1. + ## Other Options ### Logging From 5036915d95b79e0e749c42fa2ccc1edbb0c5f7ff Mon Sep 17 00:00:00 2001 From: Chris Oakman Date: Mon, 3 Aug 2026 11:31:59 -0500 Subject: [PATCH 10/10] WIP WIP WIP --- docs/cli.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/cli.md b/docs/cli.md index 2315927..7b08494 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -335,8 +335,6 @@ standard-clj check src/ --log-level ignore-already-formatted standard-clj check src/ --log-level quiet ``` -The aliases are `--log-level` / `-l`. - ### Option aliases ```text