Skip to content

feat(config):optimize processing for detecting custom config file#5107

Merged
zyyv merged 8 commits intounocss:mainfrom
henrikvilhelmberglund:feat-error-on-load-missing-config
Feb 25, 2026
Merged

feat(config):optimize processing for detecting custom config file#5107
zyyv merged 8 commits intounocss:mainfrom
henrikvilhelmberglund:feat-error-on-load-missing-config

Conversation

@henrikvilhelmberglund
Copy link
Copy Markdown
Contributor

…ng config file.

This PR throws an error when a specified custom config file is missing and logs an error when a normal config file is missing. Before this PR this would silently load the default config file which can be useful but probably not if you're trying to use your custom config file.

Right now this breaks CLI (at least tests) so marking this as draft for now. Feel free to make changes.

fixes #5104

@netlify
Copy link
Copy Markdown

netlify bot commented Feb 12, 2026

Deploy Preview for unocss ready!

Built without sensitive environment variables

Name Link
🔨 Latest commit a485cdd
🔍 Latest deploy log https://app.netlify.com/projects/unocss/deploys/699e6ae9d006bc0008b778ce
😎 Deploy Preview https://deploy-preview-5107--unocss.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Feb 12, 2026

Open in StackBlitz

commit: a485cdd

@henrikvilhelmberglund henrikvilhelmberglund marked this pull request as ready for review February 13, 2026 16:23
@henrikvilhelmberglund
Copy link
Copy Markdown
Contributor Author

henrikvilhelmberglund commented Feb 13, 2026

For an error to be thrown the custom config has to have either a .ts, .js or .config extension.

Expectations:

  • uno.config.ts file present - no error
  • uno.config.ts file missing - log error saying [UnoCSS] Config file not found in ${configOrPath} - loading default config.
  • configFile: custom-config.ts which is missing - error thrown saying [UnoCSS] Custom config file not found: ${configOrPath}. Please check the path and try again.

Feel free to make changes.

@antfu
Copy link
Copy Markdown
Member

antfu commented Feb 14, 2026

The direction sounds good to me

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR aims to improve error handling when config files are missing by throwing an error for explicitly specified custom config files and logging a warning when default config files are not found. This addresses issue #5104 where missing custom config files would silently fall back to defaults, making debugging difficult.

Changes:

  • Added validation to throw an error when a custom config file path is explicitly provided but the file doesn't exist
  • Added console error logging when no default config files are found during automatic discovery
  • Implemented regex-based detection to distinguish explicit file paths from directory paths

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

}
else {
const isExplicitFilePath = typeof configOrPath === 'string' && /\.(?:ts|js|config)$/.test(configOrPath)
if (isExplicitFilePath && resolve(configOrPath) !== resolve(cwd)) {
Copy link

Copilot AI Feb 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The condition resolve(configOrPath) !== resolve(cwd) has a logical flaw. When configOrPath is a custom config file path like './uno.config.custom.ts' and cwd is the current working directory (e.g., /home/user/project), resolve(configOrPath) will resolve to /home/user/project/uno.config.custom.ts, while resolve(cwd) will be /home/user/project. These will never be equal, so this condition will always be true for file paths.

However, this means the error will be thrown even when the config path happens to equal the current working directory, which could occur in edge cases. The real issue is that this condition doesn't accurately distinguish between "explicit custom config file" vs "default directory". The logic should check whether configOrPath was originally set to process.cwd() by line 26, but that information is lost after reassignment. Consider tracking whether an explicit config file was provided separately.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the check for if an explicit config file was provided so I don't really understand, feel free to add actual code suggestions. This was added to fix errors with CLI.

const result = await loader.load()

if (!isFile && !result.sources?.length) {
console.error(`[UnoCSS] Config file not found in ${configOrPath} - loading default config.`)
Copy link

Copilot AI Feb 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using console.error for this warning message is inconsistent with the error handling pattern used in the rest of this file. When an explicit custom config file is missing, an Error is thrown (line 40). For consistency, consider whether this should also throw an error, or if both should use a logger interface.

Additionally, the message "Config file not found in ${configOrPath}" is misleading when configOrPath is a directory path (like /home/user/project). It would be clearer to say something like "No config file found in directory ${configOrPath}" or "Using default config - no config file found in ${configOrPath}".

Suggested change
console.error(`[UnoCSS] Config file not found in ${configOrPath} - loading default config.`)
console.error(`[UnoCSS] Using default config - no config file found in directory ${cwd}.`)

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consistency is not wanted here, if the file is missing without an explicit custom config it's a an error but generally fine to continue with the default config (for now), if the user specified a custom config file but it is missing an error should be thrown because otherwise the defult config would be used instead removing the user's custom configuration.

The changes suggested to the message only shifts the misleading part to when configOrPath is a file.


const result = await loader.load()

if (!isFile && !result.sources?.length) {
Copy link

Copilot AI Feb 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The condition !isFile && !result.sources?.length will log an error even when no config file was explicitly requested. Consider this scenario: a user calls loadConfig() with default parameters (no explicit configFile), and no default config files exist in the directory. The user gets a console.error message even though they may have intentionally chosen to use only inline configuration.

This differs from the intended behavior described in the PR, which should only warn when looking for a normal config file fails. The code should differentiate between:

  1. User explicitly provided a custom config file path → throw error (lines 38-41)
  2. User didn't provide a config file, but default search found nothing → log warning (this case)
  3. User provided inline config only (configFile: false) → no warning (already handled on line 19-23)

Consider checking whether inlineConfig.configFile was undefined to determine if a config file search was actually intended.

Suggested change
if (!isFile && !result.sources?.length) {
if (!isFile && !result.sources?.length && inlineConfig.configFile === undefined) {

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docs say that using a dedicated uno.config.ts is highly recommended (https://unocss.dev/guide/config-file) so I think it's fine logging a warning if the file doesn't exist.

Comment on lines 37 to +69
@@ -57,6 +63,11 @@ export async function loadConfig<U extends UserConfig>(
})

const result = await loader.load()

if (!isFile && !result.sources?.length) {
console.error(`[UnoCSS] Config file not found in ${configOrPath} - loading default config.`)
}
Copy link

Copilot AI Feb 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error is thrown before the loader attempts to load the config, but the console.error is logged after the loader runs. This creates an inconsistency: when an explicit custom config file is missing, the function throws immediately and never attempts to load anything. However, when the default config search fails, the loader still runs (and fails), but the function continues executing and returns a result with default configuration.

This asymmetry could be confusing. If the intent is to throw for explicit custom configs and warn for default configs, the logic is correct but could benefit from a comment explaining this design decision. However, based on the PR description which says "logs an error when a normal config file is missing", it seems the intent is just to inform, not to change behavior for the default case.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is confusing, a user specifically specifying a configFile and it being missing is more serious than a user not specifying any configFile and it being missing. More input is welcome. Also don't agree that behavior is changed for the default case, there is a log but the functionality is the same.

Comment on lines +37 to +41
else {
const isExplicitFilePath = typeof configOrPath === 'string' && /\.(?:ts|js|config)$/.test(configOrPath)
if (isExplicitFilePath && resolve(configOrPath) !== resolve(cwd)) {
throw new Error(`[UnoCSS] Custom config file not found: ${configOrPath}. Please check the path and try again.`)
}
Copy link

Copilot AI Feb 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic for detecting whether a custom config file was explicitly provided is fragile. The check resolve(configOrPath) !== resolve(cwd) attempts to distinguish between an explicit file path and a directory, but this doesn't account for the code flow:

  1. Line 26: If inlineConfig.configFile is falsy, configOrPath is set to process.cwd()
  2. Line 12: The default value for configOrPath parameter is also cwd

The code can't distinguish between:

  • User passed loadConfig(cwd, cwd) (no custom config)
  • User passed loadConfig(cwd, { configFile: './uno.config.ts' }) where the file doesn't exist

A more robust approach would be to track whether inlineConfig.configFile was explicitly set to a string value, rather than trying to infer this from the resolved paths.

Copilot uses AI. Check for mistakes.
@henrikvilhelmberglund
Copy link
Copy Markdown
Contributor Author

I'm not sure how much I trust copilot but I would like to test everything more to make sure this doesn't break anything and actually works without ending up annoying people.

For example right now you will get a warning if you don't have a UnoCSS config file but maybe you just want to use the defaults(?). Not sure if this is ever true though, I feel like you would want to have a config file.

@zyyv zyyv changed the title feat: throw error on missing custom config file, log error with missi… feat(config):optimize processing for detecting custom config file Feb 25, 2026
Copy link
Copy Markdown
Member

@zyyv zyyv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. :( I apologize for replying so late during the holiday.

@zyyv zyyv enabled auto-merge February 25, 2026 03:26
@zyyv zyyv added this pull request to the merge queue Feb 25, 2026
Merged via the queue into unocss:main with commit 419b6f9 Feb 25, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Throw error when specified configFile doesn't exist

4 participants