feat(config): support .config/ directory and YAML/TOML formats via unjs/c12#1364
feat(config): support .config/ directory and YAML/TOML formats via unjs/c12#1364okikio wants to merge 23 commits intoyamadashy:mainfrom
unjs/c12#1364Conversation
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughThis PR transitions Repomix config handling to support interactive initialization with multiple locations and formats. It replaces custom discovery logic with Changes
Sequence DiagramsequenceDiagram
actor User
participant CLI Init
participant Prompts
participant Config System
participant c12 Discovery
User->>CLI Init: Run `repomix --init`
CLI Init->>Prompts: Show location options
Prompts->>User: Select root or .config/
User->>Prompts: Choose location & format
Prompts->>CLI Init: Return selection
CLI Init->>Config System: createConfigFile(location, format)
Config System->>Config System: Resolve configPath & configFileName
Config System->>User: Prompt for overwrite (if exists)
User->>Config System: Confirm/skip
Config System->>Config System: Write config file
Note over User,Config System: Later: Normal config load flow
User->>CLI Init: Run repomix (load config)
CLI Init->>Config System: loadFileConfig()
Config System->>c12 Discovery: Search locations in order
c12 Discovery->>c12 Discovery: Check --config path
c12 Discovery->>c12 Discovery: Check project root
c12 Discovery->>c12 Discovery: Check .config/ directory
c12 Discovery->>c12 Discovery: Check global directory
c12 Discovery->>Config System: Return matched file + parsed config
Config System->>CLI Init: Resolved configuration
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Signed-off-by: Okiki Ojo <hey@okikio.dev>
…gs from the project level `.config/` directory Signed-off-by: Okiki Ojo <hey@okikio.dev>
…tions Signed-off-by: Okiki Ojo <hey@okikio.dev>
Signed-off-by: Okiki Ojo <hey@okikio.dev>
…rompts Signed-off-by: Okiki Ojo <hey@okikio.dev>
…andling and improve mock implementation Signed-off-by: Okiki Ojo <hey@okikio.dev>
…supported formats Signed-off-by: Okiki Ojo <hey@okikio.dev>
…tation for clarity and interactivity Signed-off-by: Okiki Ojo <hey@okikio.dev>
Signed-off-by: Okiki Ojo <hey@okikio.dev>
…Config is true Move the skipLocalConfig guard before the c12 call instead of after it. The previous flow called c12 (which executes JS/TS config files) and then discarded the result, still running arbitrary code from untrusted remote repositories. Now c12 is never invoked for local discovery when skipLocalConfig is true. A general informational note is logged instead, pointing the user to --remote-trust-config. Also add SyntaxError branches to all three catch blocks (explicit, local, global) so config parse errors surface clearly instead of falling through to the generic "Error loading config" message. Signed-off-by: Okiki Ojo <hey@okikio.dev>
The init wizard now prompts for config file format (JSON, YAML, TOML, TypeScript, JavaScript) after the location prompt. The filename extension and serialization are driven by the chosen format. TS output uses defineConfig from repomix for type-safe autocomplete. TS and JS formats strip $schema since it is JSON-specific. YAML and TOML serialization uses confbox. Location prompt hints now show wildcard extensions (repomix.config.*) instead of hardcoded .json. Signed-off-by: Okiki Ojo <hey@okikio.dev>
confbox provides YAML and TOML serialization for the new --init format selection. jiti is an optional peer dependency of c12 required at runtime for loading TS/JS config files. Signed-off-by: Okiki Ojo <hey@okikio.dev>
…nfig is true Replace the previous "should log a message when skipping" test with a stronger assertion: c12Load must never be called with the local cwd when skipLocalConfig is true. Also assert the security note is still logged with the --remote-trust-config hint. Signed-off-by: Okiki Ojo <hey@okikio.dev>
… integration test Fixes biome lint/style/noNonNullAssertion warnings on options.cwd and options.configFile by using ?? '' fallbacks. Signed-off-by: Okiki Ojo <hey@okikio.dev>
…ific cases All existing tests now mock both location and format prompts via mockResolvedValueOnce chaining. New tests cover: format cancel, YAML output (no braces, YAML key syntax), TOML output ([output] section), TypeScript output (defineConfig, no $schema), JavaScript output (export default, no $schema), and dotconfig + non-JSON format filename. Signed-off-by: Okiki Ojo <hey@okikio.dev>
…loaded c12's loadConfig always returns a truthy configFile (the resolved pattern path), even when no actual file exists on disk. Only _configFile is set when a real file was loaded. The previous check on configFile meant loadConfigFromC12 never returned null, causing three failures: - --config non-existent.json silently succeeded with empty config - Global config fallback never triggered because local discovery always returned a truthy configFile - The "No custom config found" message never displayed Switch the emptiness check to _configFile and update all test mocks to mirror real c12 return shape: always-truthy configFile with _configFile only set when a file was actually found. Signed-off-by: Okiki Ojo <hey@okikio.dev>
…handle non-Error throws decision(config): normalize and compare resolved paths so explicit --config rejects discovered fallback files that happen to match a different name constraint(c12): c12 always returns a truthy configFile even when no real file was loaded; _configFile is the reliable provenance signal learned(config): non-Error values thrown by c12 or its loaders (e.g. jiti) silently fell through the catch block — every catch path now needs a String(error) fallback Signed-off-by: Okiki Ojo <hey@okikio.dev>
intent(init): prevent an attacker-controlled symlink in .config/ or .repomixignore from redirecting init writes outside the project directory decision(init): use fs.lstat before every init write to detect symlinks at the target and its parent directory for .config/ locations decision(init): generate plain `export default` for TS/JS configs instead of defineConfig so the output works without a local repomix dependency rejected(init): defineConfig in generated TS/JS — requires repomix as a dev dependency, which breaks npx and global-install workflows constraint(init): cancel symbol from @clack/prompts must be checked before treating the overwrite response as a boolean, otherwise cancel overwrites the file Signed-off-by: Okiki Ojo <hey@okikio.dev>
…covery, and non-Error throws intent(config): regression tests for the three new defensive behaviors in loadFileConfig decision(config): test explicit --config rejection by having the mock return a config loaded from a different path than the one requested Signed-off-by: Okiki Ojo <hey@okikio.dev>
… explicit-path semantics intent(config): validate config loading against real c12 behavior using temp directories, not just mocked return shapes decision(config): use os.tmpdir with cleanup to avoid polluting the workspace learned(config): the JS dynamic-config mock was missing _configFile, which caused it to silently not match real c12 behavior Signed-off-by: Okiki Ojo <hey@okikio.dev>
…lain TS/JS export output intent(init): regression coverage for symlink hardening in config and ignore file writes, and for the cancel-symbol bug in ignore overwrite flow decision(init): mock fs.lstat to return isSymbolicLink for targeted paths and throw ENOENT for all others, matching real filesystem behavior Signed-off-by: Okiki Ojo <hey@okikio.dev>
…at support intent(docs): align README with the interactive format/location selector now available in repomix --init decision(docs): add a note explaining that generated TS/JS configs use plain export default and that users can switch to defineConfig after installing repomix locally Signed-off-by: Okiki Ojo <hey@okikio.dev>
…t --init intent(docs): align the English website guide with the new --init format and location selection behavior decision(docs): rename section from "JSON Configuration" to "Interactive Configuration" since --init now supports json, yaml, toml, ts, and js Signed-off-by: Okiki Ojo <hey@okikio.dev>
aaeeb29 to
66d626b
Compare
Adds support for the
.config/directory convention and expands config format support by replacing the manual config discovery/loading system with unjs/c12.Summary
Config discovery now searches
.config/directory. Repomix finds config files in this order:--configpath (if provided)repomix.config.{ext}.config/directory:.config/repomix.{ext}or.config/repomix.config.{ext}YAML and TOML formats are now supported alongside the existing TypeScript, JavaScript, and JSON/JSONC/JSON5 formats.
repomix --initis now interactive. Users choose where to place the config file:repomix.config.json).config/directory (config/repomix.json).config/directory with full name (.config/repomix.config.json)Dependency changes:
c12— handles config file discovery, loading, and format parsingjson5— c12 uses confbox internally for JSON5/JSONC/YAML/TOMLjiti— c12 declares it as an optional peer dep and imports it dynamicallyWhy c12
c12 is the config loader behind Nuxt and the unjs ecosystem. It handles the hard parts, file discovery across directories, dynamic TS/JS loading via jiti, and multi-format parsing via confbox, while giving control over merge policy, validation, and trust boundaries. This replaces ~100 lines of manual file-existence checks and format-specific loading with a single well-tested dependency. Plus, my primary reason for adding it, is it enables support for project-level
.config/directories, I thought it would be kinda nice.Scope
This PR focuses on
.config/directory support and the format expansion that comes with c12. Several c12 features are explicitly disabled for now:rcFile(.repomixrc) — deferredpackageJson(repomixkey in package.json) — deferredextend(config inheritance) — deferredglobalRc,envName,dotenv— deferredNote
I didn't want to go to far ahead so I limited the change to only adding the
.config/directory support, but you might actually want to enable some of the other powerful featuresunjs/c12supports.This pr doesn't change the merge logic (
mergeConfigs) because c12's defu merges arrays in reversed order, which doesn't match existing behavior, and I was weary of making too many changes in one pr.Warning
A slight side-effect change that shouldn't cause any bugs but it is worth keeping in mind, is that by default the preference order for files is slightly tweaked, so unlike before where if the end user had both a
repomix.config.jsandrepomix.config.tsit would prefer the.ts, with c12 it by default prefers the.tsversion of the config.This is mostly a rare occurrence but it can happen so it's worth being aware.
Files changed
Core:
.config/is now available as a location I thought it best to add an interactive selection menu for determining where to place therepomix.config.ts)Tests:
.config/directory tests, fixed mocks for new interactive promptDocs (README + english language website docs):
.config/directory to search locations--initand--configdescriptionsNote
A separate pr might be necessary to update all the other languages.
Checklist
npm run testnpm run lintCloses #1365