Giter Site home page Giter Site logo

cosmiconfig's Introduction

cosmiconfig

codecov

Cosmiconfig searches for and loads configuration for your program.

It features smart defaults based on conventional expectations in the JavaScript ecosystem. But it's also flexible enough to search wherever you'd like to search, and load whatever you'd like to load.

By default, Cosmiconfig will check the current directory for the following:

  • a package.json property
  • a JSON or YAML, extensionless "rc file"
  • an "rc file" with the extensions .json, .yaml, .yml, .js, .ts, .mjs, or .cjs
  • any of the above two inside a .config subdirectory
  • a .config.js, .config.ts, .config.mjs, or .config.cjs file

For example, if your module's name is "myapp", cosmiconfig will search up the directory tree for configuration in the following places:

  • a myapp property in package.json
  • a .myapprc file in JSON or YAML format
  • a .myapprc.json, .myapprc.yaml, .myapprc.yml, .myapprc.js, .myapprc.ts, .myapprc.mjs, or .myapprc.cjs file
  • a myapprc, myapprc.json, myapprc.yaml, myapprc.yml, myapprc.js, myapprc.ts, myapprc.mjs, or myapprc.cjs file inside a .config subdirectory
  • a myapp.config.js, myapp.config.ts, myapp.config.mjs, or myapp.config.cjs file

Optionally, you can tell it to search up the directory tree using search strategies, checking each of these places in each directory, until it finds some acceptable configuration (or hits the home directory).

Table of contents

Installation

npm install cosmiconfig

Tested in Node 14+.

Usage for tooling developers

If you are an end user (i.e. a user of a tool that uses cosmiconfig, like prettier or stylelint), you can skip down to the end user section.

Create a Cosmiconfig explorer, then either search for or directly load a configuration file.

const { cosmiconfig, cosmiconfigSync } = require('cosmiconfig');
// ...
const explorer = cosmiconfig(moduleName);

// Search for a configuration by walking up directories.
// See documentation for search, below.
explorer.search()
  .then((result) => {
    // result.config is the parsed configuration object.
    // result.filepath is the path to the config file that was found.
    // result.isEmpty is true if there was nothing to parse in the config file.
  })
  .catch((error) => {
    // Do something constructive.
  });

// Load a configuration directly when you know where it should be.
// The result object is the same as for search.
// See documentation for load, below.
explorer.load(pathToConfig).then(/* ... */);

// You can also search and load synchronously.
const explorerSync = cosmiconfigSync(moduleName);

const searchedFor = explorerSync.search();
const loaded = explorerSync.load(pathToConfig);

Result

The result object you get from search or load has the following properties:

  • config: The parsed configuration object. undefined if the file is empty.
  • filepath: The path to the configuration file that was found.
  • isEmpty: true if the configuration file is empty. This property will not be present if the configuration file is not empty.

Asynchronous API

cosmiconfig()

const { cosmiconfig } = require('cosmiconfig');
const explorer = cosmiconfig(moduleName, /* optional */ cosmiconfigOptions)

Creates a cosmiconfig instance ("explorer") configured according to the arguments, and initializes its caches.

moduleName

Type: string. Required.

Your module name. This is used to create the default searchPlaces and packageProp.

If your searchPlaces value will include files, as it does by default (e.g. ${moduleName}rc), your moduleName must consist of characters allowed in filenames. That means you should not copy scoped package names, such as @my-org/my-package, directly into moduleName.

cosmiconfigOptions are documented below. You may not need them, and should first read about the functions you'll use.

explorer.search()

explorer.search([searchFrom]).then(result => { /* ... */ })

Searches for a configuration file. Returns a Promise that resolves with a result or with null, if no configuration file is found.

You can do the same thing synchronously with explorerSync.search().

Let's say your module name is goldengrahams so you initialized with const explorer = cosmiconfig('goldengrahams');. Here's how your default search() will work:

  • Starting from process.cwd() (or some other directory defined by the searchFrom argument to search()), look for configuration objects in the following places:
    1. A goldengrahams property in a package.json file.
    2. A .goldengrahamsrc file with JSON or YAML syntax.
    3. A .goldengrahamsrc.json, .goldengrahamsrc.yaml, .goldengrahamsrc.yml, .goldengrahamsrc.js, .goldengrahamsrc.ts, .goldengrahamsrc.mjs, or .goldengrahamsrc.cjs file. (To learn more about how JS files are loaded, see "Loading JS modules".)
    4. A goldengrahamsrc, goldengrahamsrc.json, goldengrahamsrc.yaml, goldengrahamsrc.yml, goldengrahamsrc.js, goldengrahamsrc.ts, goldengrahamsrc.mjs, or goldengrahamsrc.cjs file in the .config subdirectory.
    5. A goldengrahams.config.js, goldengrahams.config.ts, goldengrahams.config.mjs, or goldengrahams.config.cjs file. (To learn more about how JS files are loaded, see "Loading JS modules".)
  • If none of those searches reveal a configuration object, continue depending on the current search strategy:
    • If it's none (which is the default if you don't specify a stopDir option), stop here and return/resolve with null.
    • If it's global (which is the default if you specify a stopDir option), move up one directory level and try again, recursing until arriving at the configured stopDir option, which defaults to the user's home directory.
      • After arriving at the stopDir, the global configuration directory (as defined by env-paths without prefix) is also checked, looking at the files config, config.json, config.yaml, config.yml, config.js, config.ts, config.cjs, and config.mjs in the directory ~/.config/goldengrahams/ (on Linux; see env-paths documentation for other OSs).
    • If it's project, check whether a package.json file is present in the current directory, and if not, move up one directory level and try again, recursing until there is one.
  • If at any point a parsable configuration is found, the search() Promise resolves with its result (or, with explorerSync.search(), the result is returned).
  • If no configuration object is found, the search() Promise resolves with null (or, with explorerSync.search(), null is returned).
  • If a configuration object is found but is malformed (causing a parsing error), the search() Promise rejects with that error (so you should .catch() it). (Or, with explorerSync.search(), the error is thrown.)

If you know exactly where your configuration file should be, you can use load(), instead.

The search process is highly customizable. Use the cosmiconfig options searchPlaces and loaders to precisely define where you want to look for configurations and how you want to load them.

searchFrom

Type: string. Default: process.cwd().

A filename. search() will start its search here.

If the value is a directory, that's where the search starts. If it's a file, the search starts in that file's directory.

explorer.load()

explorer.load(loadPath).then(result => { /* ... */ })

Loads a configuration file. Returns a Promise that resolves with a result or rejects with an error (if the file does not exist or cannot be loaded).

Use load if you already know where the configuration file is and you just need to load it.

explorer.load('load/this/file.json'); // Tries to load load/this/file.json.

If you load a package.json file, the result will be derived from whatever property is specified as your packageProp. package.yaml will work as well if you specify these file names in your searchPlaces.

You can do the same thing synchronously with explorerSync.load().

explorer.clearLoadCache()

Clears the cache used in load().

explorer.clearSearchCache()

Clears the cache used in search().

explorer.clearCaches()

Performs both clearLoadCache() and clearSearchCache().

Synchronous API

cosmiconfigSync()

const { cosmiconfigSync } = require('cosmiconfig');
const explorerSync = cosmiconfigSync(moduleName, /* optional */ cosmiconfigOptions)

Creates a synchronous cosmiconfig instance ("explorerSync") configured according to the arguments, and initializes its caches.

See cosmiconfig().

explorerSync.search()

const result = explorerSync.search([searchFrom]);

Synchronous version of explorer.search().

Returns a result or null.

explorerSync.load()

const result = explorerSync.load(loadPath);

Synchronous version of explorer.load().

Returns a result.

explorerSync.clearLoadCache()

Clears the cache used in load().

explorerSync.clearSearchCache()

Clears the cache used in search().

explorerSync.clearCaches()

Performs both clearLoadCache() and clearSearchCache().

cosmiconfigOptions

Type: Object.

Possible options are documented below.

searchStrategy

Type: string Default: global if stopDir is specified, none otherwise.

The strategy that should be used to determine which directories to check for configuration files.

  • none: Only checks in the current working directory.
  • project: Starts in the current working directory, traversing upwards until a package.{json,yaml} file is found.
  • global: Starts in the current working directory, traversing upwards until the configured stopDir (or the current user's home directory if none is given). Then, if no configuration is found, also look in the operating system's default configuration directory (according to env-paths without prefix), where a different set of file names is checked:
[
  `config`,
  `config.json`,
  `config.yaml`,
  `config.yml`,
  `config.js`,
  `config.ts`,
  `config.cjs`,
  `config.mjs`
]

searchPlaces

Type: Array<string>. Default: See below.

An array of places that search() will check in each directory as it moves up the directory tree. Each place is relative to the directory being searched, and the places are checked in the specified order.

Default searchPlaces:

For the asynchronous API, these are the default searchPlaces:

[
  'package.json',
  `.${moduleName}rc`,
  `.${moduleName}rc.json`,
  `.${moduleName}rc.yaml`,
  `.${moduleName}rc.yml`,
  `.${moduleName}rc.js`,
  `.${moduleName}rc.ts`,
  `.${moduleName}rc.mjs`,
  `.${moduleName}rc.cjs`,
  `.config/${moduleName}rc`,
  `.config/${moduleName}rc.json`,
  `.config/${moduleName}rc.yaml`,
  `.config/${moduleName}rc.yml`,
  `.config/${moduleName}rc.js`,
  `.config/${moduleName}rc.ts`,
  `.config/${moduleName}rc.mjs`,
  `.config/${moduleName}rc.cjs`,
  `${moduleName}.config.js`,
  `${moduleName}.config.ts`,
  `${moduleName}.config.mjs`,
  `${moduleName}.config.cjs`,
];

For the synchronous API, the only difference is that .mjs files are not included. See "Loading JS modules" for more information.

Create your own array to search more, fewer, or altogether different places.

Every item in searchPlaces needs to have a loader in loaders that corresponds to its extension. (Common extensions are covered by default loaders.) Read more about loaders below.

package.json is a special value: When it is included in searchPlaces, Cosmiconfig will always parse it as JSON and load a property within it, not the whole file. That property is defined with the packageProp option, and defaults to your module name.

package.yaml (used by pnpm) works the same way.

Examples, with a module named porgy:

// Disallow extensions on rc files:
['package.json', '.porgyrc', 'porgy.config.js']
// Limit the options dramatically:
['package.json', '.porgyrc']
// Maybe you want to look for a wide variety of JS flavors:
[
  'porgy.config.js',
  'porgy.config.mjs',
  'porgy.config.ts',
  'porgy.config.coffee'
]
// ^^ You will need to designate a custom loader to tell
// Cosmiconfig how to handle `.coffee` files.
// Look within a .config/ subdirectory of every searched directory:
[
  'package.json',
  '.porgyrc',
  '.config/.porgyrc',
  '.porgyrc.json',
  '.config/.porgyrc.json'
]

loaders

Type: Object. Default: See below.

An object that maps extensions to the loader functions responsible for loading and parsing files with those extensions.

Cosmiconfig exposes its default loaders on the named export defaultLoaders and defaultLoadersSync.

Default loaders:

const { defaultLoaders, defaultLoadersSync } = require('cosmiconfig');

console.log(Object.entries(defaultLoaders));
// [
//   [ '.mjs', [Function: loadJs] ],
//   [ '.cjs', [Function: loadJs] ],
//   [ '.js', [Function: loadJs] ],
//   [ '.ts', [Function: loadTs] ],
//   [ '.json', [Function: loadJson] ],
//   [ '.yaml', [Function: loadYaml] ],
//   [ '.yml', [Function: loadYaml] ],
//   [ 'noExt', [Function: loadYaml] ]
// ]

console.log(Object.entries(defaultLoadersSync));
// [
//   [ '.cjs', [Function: loadJsSync] ],
//   [ '.js', [Function: loadJsSync] ],
//   [ '.ts', [Function: loadTsSync] ],
//   [ '.json', [Function: loadJson] ],
//   [ '.yaml', [Function: loadYaml] ],
//   [ '.yml', [Function: loadYaml] ],
//   [ 'noExt', [Function: loadYaml] ]
// ]

(YAML is a superset of JSON;ย which means YAML parsers can parse JSON;ย which is how extensionless files can be either YAML or JSON with only one parser.)

If you provide a loaders object, your object will be merged with the defaults. So you can override one or two without having to override them all.

Keys in loaders are extensions (starting with a period), or noExt to specify the loader for files without extensions, like .myapprc.

Values in loaders are a loader function (described below) whose values are loader functions.

The most common use case for custom loaders value is to load extensionless rc files as strict JSON, instead of JSON or YAML (the default). To accomplish that, provide the following loaders value:

{
  noExt: defaultLoaders['.json'];
}

If you want to load files that are not handled by the loader functions Cosmiconfig exposes, you can write a custom loader function or use one from NPM if it exists.

Use cases for custom loader function:

  • Allow configuration syntaxes that aren't handled by Cosmiconfig's defaults, like JSON5, INI, or XML.
  • Parse JS files with Babel before deriving the configuration.

Custom loader functions have the following signature:

// Sync
type SyncLoader = (filepath: string, content: string) => Object | null

// Async
type AsyncLoader = (filepath: string, content: string) => Object | null | Promise<Object | null>

Cosmiconfig reads the file when it checks whether the file exists, so it will provide you with both the file's path and its content. Do whatever you need to, and return either a configuration object or null (or, for async-only loaders, a Promise that resolves with one of those). null indicates that no real configuration was found and the search should continue.

A few things to note:

  • If you use a custom loader, be aware of whether it's sync or async: you cannot use async customer loaders with the sync API (cosmiconfigSync()).
  • Special JS syntax can also be handled by using a require hook, because defaultLoaders['.js'] just uses require. Whether you use custom loaders or a require hook is up to you.

Examples:

// Allow JSON5 syntax:
cosmiconfig('foo', {
  loaders: {
    '.json': json5Loader
  }
});

// Allow a special configuration syntax of your own creation:
cosmiconfig('foo', {
  loaders: {
    '.special': specialLoader
  }
});

// Allow many flavors of JS, using custom loaders:
cosmiconfig('foo', {
  loaders: {
    '.coffee': coffeeScriptLoader
  }
});

// Allow many flavors of JS but rely on require hooks:
cosmiconfig('foo', {
  loaders: {
    '.coffee': defaultLoaders['.js']
  }
});

packageProp

Type: string | Array<string>. Default: `${moduleName}`.

Name of the property in package.json (or package.yaml) to look for.

Use a period-delimited string or an array of strings to describe a path to nested properties.

For example, the value 'configs.myPackage' or ['configs', 'myPackage'] will get you the "myPackage" value in a package.json like this:

{
  "configs": {
    "myPackage": {"option":  "value"}
  }
}

If nested property names within the path include periods, you need to use an array of strings. For example, the value ['configs', 'foo.bar', 'baz'] will get you the "baz" value in a package.json like this:

{
  "configs": {
    "foo.bar": {
      "baz": {"option":  "value"}
    }
  }
}

If a string includes period but corresponds to a top-level property name, it will not be interpreted as a period-delimited path. For example, the value 'one.two' will get you the "three" value in a package.json like this:

{
  "one.two": "three",
  "one": {
    "two": "four"
  }
}

stopDir

Type: string. Default: Absolute path to your home directory.

Directory where the search will stop.

cache

Type: boolean. Default: true.

If false, no caches will be used. Read more about "Caching" below.

transform

Type: (Result) => Promise<Result> | Result.

A function that transforms the parsed configuration. Receives the result.

If using search() or load() (which are async), the transform function can return the transformed result or return a Promise that resolves with the transformed result. If using cosmiconfigSync, search() or load(), the function must be synchronous and return the transformed result.

The reason you might use this option โ€”ย instead of simply applying your transform function some other way โ€”ย is that the transformed result will be cached. If your transformation involves additional filesystem I/O or other potentially slow processing, you can use this option to avoid repeating those steps every time a given configuration is searched or loaded.

ignoreEmptySearchPlaces

Type: boolean. Default: true.

By default, if search() encounters an empty file (containing nothing but whitespace) in one of the searchPlaces, it will ignore the empty file and move on. If you'd like to load empty configuration files, instead, set this option to false.

Why might you want to load empty configuration files? If you want to throw an error, or if an empty configuration file means something to your program.

Loading JS modules

Your end users can provide JS configuration files as ECMAScript modules (ESM) under the following conditions:

With cosmiconfig's asynchronous API, the default searchPlaces include .js, .ts, .mjs, and .cjs files. Cosmiconfig loads all these file types with the dynamic import function.

With the synchronous API, JS configuration files are always treated as CommonJS, and .mjs files are ignored, because there is no synchronous API for the dynamic import function.

Caching

As of v2, cosmiconfig uses caching to reduce the need for repetitious reading of the filesystem or expensive transforms. Every new cosmiconfig instance (created with cosmiconfig()) has its own caches.

To avoid or work around caching, you can do the following:

Differences from rc

rc serves its focused purpose well. cosmiconfig differs in a few key ways โ€”ย making it more useful for some projects, less useful for others:

  • Looks for configuration in some different places: in a package.json property, an rc file, a .config.js file, and rc files with extensions.
  • Built-in support for JSON, YAML, and CommonJS formats.
  • Stops at the first configuration found, instead of finding all that can be found up the directory tree and merging them automatically.
  • Options.
  • Asynchronous by default (though can be run synchronously).

Usage for end users

When configuring a tool, you can use multiple file formats and put these in multiple places.

Usually, a tool would mention this in its own README file, but by default, these are the following places, where {NAME} represents the name of the tool:

package.json
.{NAME}rc
.{NAME}rc.json
.{NAME}rc.yaml
.{NAME}rc.yml
.{NAME}rc.js
.{NAME}rc.ts
.{NAME}rc.cjs
.config/{NAME}rc
.config/{NAME}rc.json
.config/{NAME}rc.yaml
.config/{NAME}rc.yml
.config/{NAME}rc.js
.config/{NAME}rc.ts
.config/{NAME}rc.mjs
.config/{NAME}rc.cjs
{NAME}.config.js
{NAME}.config.ts
{NAME}.config.mjs
{NAME}.config.cjs

The contents of these files are defined by the tool. For example, you can configure prettier to enforce semicolons at the end of the line using a file named .config/prettierrc.yml:

semi: true

Additionally, you have the option to put a property named after the tool in your package.json file, with the contents of that property being the same as the file contents. To use the same example as above:

{
  "name": "your-project",
  "dependencies": {},
  "prettier": {
    "semi": true
  }
}

This has the advantage that you can put the configuration of all tools (at least the ones that use cosmiconfig) in one file.

You can also add a cosmiconfig key within your package.json file or create one of the following files to configure cosmiconfig itself:

.config/config.json
.config/config.yaml
.config/config.yml
.config/config.js
.config/config.ts
.config/config.cjs

The following properties are currently actively supported in these places:

cosmiconfig:
  # adds places where configuration files are being searched
  searchPlaces:
    - .config/{name}.yml
  # to enforce a custom naming convention and format, don't merge the above with the tool-defined search places
  # (`true` is the default setting)
  mergeSearchPlaces: false

Note: technically, you can overwrite all options described in cosmiconfigOptions here, but everything not listed above should be used at your own risk, as it has not been tested explicitly. The only exceptions to this are the loaders property, which is explicitly not supported at this time, and the searchStrategy property, which is intentionally disallowed.

You can also add more root properties outside the cosmiconfig property to configure your tools, entirely eliminating the need to look for additional configuration files:

cosmiconfig:
  searchPlaces: []

prettier:
  semi: true

Imports

Wherever you put your configuration (the package.json file, a root config file or a package-specific config file), you can use the special $import key to import another file as a base.

For example, you can import from an npm package (in this example, @foocorp/config).

.prettierrc.base.yml in said npm package could define some company-wide defaults:

printWidth: 120
semi: true
tabWidth: 2

And then, the .prettierrc.yml file in the project itself would just reference that file, optionally overriding the defaults with project-specific settings:

$import: node_modules/@foocorp/config/.prettierrc.base.yml
# we want more space!
printWidth: 200

It is possible to import multiple base files by specifying an array of paths, which will be processed in declaration order; that means that the last entry will win if there are conflicting properties.

It is also possible to import file formats other than the importing format as long as they are supported by the loaders specified by the developer of the tool you're configuring.

$import: [first.yml, second.json, third.config.js]

Contributing & Development

Please note that this project is released with a Contributor Code of Conduct. By participating in this project you agree to abide by its terms.

And please do participate!

cosmiconfig's People

Contributors

azz avatar beerose avatar ben-eb avatar chrisblossom avatar d-fischer avatar danielruf avatar davidtheclark avatar donatj avatar elijaholmos avatar evocateur avatar hansl avatar hassankhan avatar iamogbz avatar ikatyang avatar izaakschroeder avatar jakebailey avatar jrandolf avatar k2snowman69 avatar kellyrmilligan avatar koenpunt avatar msegado avatar olsonpm avatar poppinlp avatar release-please[bot] avatar saadq avatar shernshiou avatar sudo-suhas avatar tjenkinson avatar trysound avatar vkrol avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cosmiconfig's Issues

How to handle stylelint's plugins

This is a rough one.

In order for stylelint to fully take advantage of this module, stylelint needs to be able to interpret plugins values in extended configs relative to the file that references them. This means that if Config A extends Config B, and Config B includes plugins, we can't resolve the plugins relative to Config A once everything has been merged: we have to resolve Config B's plugins as they load.

This seems like a special case ... but we really do need to enable stylelint or other modules that want similar plugin syntax to do what they want.

I see two options:

  1. Build in special support for plugins, such that plugin resolution is done within cosmiconfig, and the resultant configuration object somehow exposes absolute paths to all plugins referenced.

  2. Expose configuration objects to transforms as they load, which I imagine would have to be done with some event-style API, e.g.

    .on('load', function(result) { 
      result.config = transformConfig(result.config);
    });

Any ideas welcome.

Right now I'm kind of leaning towards option 1, because I think this feature might only be required for plugins, so why build in more flexibility than necessary.

Version 2.2.0 and 2.2.2 break webpack build (Module build failed: TypeError: Invalid PostCSS Plugin found: [0])

Hello,

On previous week you have released version 2.2.0 which breaks our webpack build. Your revert in 2.2.1 fixed our problem but today I see that you publish version 2.2.2 which again breaks build.

Exception stack trace

[04:10:19]	Module build failed: TypeError: Invalid PostCSS Plugin found: [0]
[04:10:19]	/.../node_modules/postcss-load-plugins/lib/plugins.js:32:17
[04:10:19]       at Array.forEach (native)
[04:10:19]      (/.../node_modules/postcss-load-plugins/lib/plugins.js:21:15)
[04:10:19]      /.../node_modules/postcss-load-config/index.js:64:18

Versions of npm modules

[email protected]
[04:05:16]	[Step 1/2] โ”‚ โ”œโ”€โ”ฌ [email protected]
[04:05:16]	[Step 1/2] โ”‚ โ”‚ โ””โ”€โ”ฌ [email protected]
[04:05:16]	[Step 1/2] โ”‚ โ”‚ โ”œโ”€โ”ฌ [email protected]

https://github.com/michael-ciniawsky/postcss-load-config/blob/master/package.json

Thanks

Support .config/config folder lookup

๐Ÿ‘‹ @davidtheclark

@postcss-loader got a few requests to support setting a custom config path for postcss.config.js lookup and most of the folks especially wanted to store them in a separate .config/config folder somewhere in their project.

While thats totally fine and works for now, on the other hand it ignores/devalues the reason/benefit behind actually autoloading the config without the need for any additional 'setup' ๐Ÿ˜› . So maybe there is a compromise possible to enable additional search in .config/config folders while walking the file tree.

|โ€“ (.)config(<---)
|   |โ€“ postcss.config.js
|โ€“ client
|   |โ€“ (.)config (<---)
|   |   |โ€“ postcss.config.js 
|   |โ€“ styles
|        |โ€“ index.css (search starts here)
|        |โ€“ postcss.config.js (supported)
|โ€“ gulpfile.js
|โ€“ webpack.config.js

If you are basically ok with it, but don't have the time/interest to do it, coordinating me to the location in the source for getting started would be appreciated :)

cc @ai

Remove const for Safari and node.js 0.12 support

I saw that there is const in this module code. PostCSS will use postcss-load-config, so it will have cosmiconf in dependencies.

And here we have few problems:

  • lack node.js 0.12 support. It is not a big deal, because it will be depreacted soon.
  • lack of Safari support. Some users use PostCSS in client-side. Of course, cosmiconfig will not be executed in browser. But Safari will not even load JS with const.

Of course, we could ask client-side users to use Babel. But because problems are only with const, I think it is too complicated :). var is not so trendy, but it works and it is easy :).

@davidtheclark what do you think about it?

Using external files with --config option - does it work?

I'm confused about how this is meant to work.

I am setting a path to an external config file with a --config command line argument e.g.

--config=/etc/path/to/config/file.conf

I can see this is initially working, in that it is setting a property within cosmiconfig's options variable in index.js to the correct value:

options.configPath = path.resolve(parsedCliArgs[options.argv]);

But, how are you mean to actually load that file? Because when you call the load() method, there's no need to specify searchPath (because the location is fixed), and you can't specify configPath (because only cosmiconfig knows what it got from the command line argument). And you have to specify one or the other, or load() just resolves with null.

I have been through the code and I can't see anywhere that actually uses the .configPath property. So even if I do something like

conf.load(process.cwd())

It's presumably just going to behave in the standard way, and ignore the --config setting.

I must be missing something obvious!

On a probably unrelated note, putting --config aside for the moment. Why can't you just call load() with no arguments and have it default to process.cwd()?

Docs: "down the file tree" => "up the file tree"

File trees, like many others in computing (1), usually grow upside-down ๐Ÿ˜‰ The root is typically referred to as the "top", and descending "down" the tree means moving into subfolders... e.g., see the wording used in http://www.linfo.org/directory_tree.html, https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/3/html/Step_by_Step_Guide/s1-navigating-cd.html, and https://www.ibm.com/support/knowledgecenter/ssw_aix_61/com.ibm.aix.cmds1/cd.htm (2).

Since cosmiconfig searches parent folders rather than subfolders for config files ("the search continues in ./, ../, ../../, ../../../, etc."), the conventional wording would be that it walks "up the file tree" -- or even more idiomatically, "up the directory tree".

(1) Family trees sometime grow this way too (but only the descendancy kind).
(2) Did anyone else know that cd - toggles back and forth between two directories?!

Should `ENOENT` be suppressed when looking up a non-existent location?

Ref: postcss/postcss-cli#122

cosmiconfig will throw an error when trying to resolve config from a postcss-cli input coming in from stdin. This is because the filepath is set to whatever the process.cwd() is plus stdin (e.g. /path/to/project/stdin).

I noticed that the current isDirectory function in https://github.com/davidtheclark/cosmiconfig/blob/de81cf6f4bd8f18f4c36255b8a9f13733e3cc7f7/lib/createExplorer.js#L100 doesn't make any exception for ENOENT type errors, however the utility is-directory from npm does. Do you see any problems with migrating to is-directory instead?

Yarn fails to install

Yarn is a new package manager that competes with npm.

It apparently validates engines (on npm install equivalent), which cosmiconfig has set to 4. Since I'm on node:6, the install fails.

> yarn
yarn install v0.15.0
[2/4] ๐Ÿšš  Fetching packages...
error [email protected]: The engine "node" is incompatible with this module. Expected version "4".
error Found incompatible module

I'm happy to submit a PR for this if it's desired

Consider dropping require-from-string

It seems that require-from-string doesn't allow for modular configuration, i.e. it's impossible to split the configuration in multiple files and export them in index.js.

Is there a specific reason for using it? Perhaps a simple require(filepath) is better (and it also supports loading json files from node v0.5.x)?

js config is treated as yaml

Context: prettier/prettier#2908 (comment)

For example, module.exports = { foo: true } is parsed as { "module.exports = { foo": "true }" } while passing with its filename (myConfig.js), cosmiconfig should infer format based on its filename if there is no format provided instead of always tryAllParsing, which always try yaml parsing first.

something like:

https://github.com/davidtheclark/cosmiconfig/blob/ad33e75766c194eaa9f28df1d9688b3ce5a8e462/src/loadDefinedFile.js#L22

const format = options.format ||
  /\.(js)$/.test(filepath)
    ? "js"
    : /\.(json)$/.test(filepath)
      ? "json"
      : /\.(yml|yaml)$/.test(filepath) ? "yaml" : undefined;

switch (format) {

I originally want to send a PR directly but failed since there are too many expect error tests failed I have no idea how to fix them.

Silently failing

I have the following code block:

const config = require('cosmiconfig');

const flatten = (key, source, rules) => {
    config('weblo')
        .load(null, `${process.cwd()}/package.json`)
        .then(result => {
            // Never hit
           console.log('hit');
        }).catch(err => {
            // Also never hit
            console.log(err);
        });

    // Hit straight away
    console.log( 'end');

};

module.exports = flatten;

If I pass anything invalid to load() it falls over, I have also tried passing nothing through, passing just process.cwd() through but ideally I do just want to read from the package.json hence trying to load the configPath as opposed to the searchPath. I have tried debugging but with silent output there is not too much I can do.

Sorry JS skills are a little limited so can't provide much more info.

Cheers.

A globally-installed ESLint cannot find a locally-installed plugin

E:\work\cosmiconfig (master) ([email protected])
ฮป eslint .

Oops! Something went wrong! :(

ESLint couldn't find the plugin "eslint-plugin-node". This can happen for a couple different reasons:

1. If ESLint is installed globally, then make sure eslint-plugin-node is also installed globally. A globally-installed ESLint cannot find a locally-installed plugin.

2. If ESLint is installed locally, then it's likely that the plugin isn't installed correctly. Try reinstalling by running the following:

    npm i eslint-plugin-node@latest --save-dev

If you still can't figure out the problem, please stop by https://gitter.im/eslint/eslint to chat with the team.

I think it's a bug: A globally-installed ESLint cannot find a locally-installed plugin.

If we can solve this problem in cosmiconfig, it might be possible to recommend eslint to load the configuration file with cosmiconfig.

Double-check API

As of this morning I think I've built in all the features I thought were needed. I'm wondering if anybody has feedback on this API before I cut 1.0.

@jeddy3, @MoOx, @TrySound, @ai --- any thoughts? or anybody else you know that might be interested in using this so should check it out?

Flow types

@sudo-suhas How do you feel about adding Flow types?

I was thinking about adding JSDoc-style comments to more of the functions, but that got me thinking that adding Flow types would both document functions and help with type safety. Type checking might be especially helpful now that we have a bunch of functions that sometimes return Promises, sometimes don't.

If you aren't opposed, I could do that at some point today so we can include it in the 3.0 release.

Look in user's home even if not in the path

Hi! I started the following question on stylelint/stylelint#2479 discussing about ensure that the user's home is checked for configuration files.

The scenario is the following:
I have a 128gb mounted on my card reader. That card is never removed and that's why I place almost all my projects there.

As it isn't mounted inside the user's home it will fail to find the configuration on the user's home.

This is a small suggestion to double check the system's path to be sure it is checked. I didn't made a PR because I'm short in time at this moment to look the best place to change the code. If you guys agree it's a good idea, you can assign it to me and I'll look for it.

Allow extends

An option allowExtends could enable extending.

The model I have in mind is https://github.com/stylelint/stylelint/blob/master/src/postcssPlugin.js#L69. Hopefully we can avoid using require() directly, though, because it is synchronous. Open to other implementation ideas, of course, if they are comparably simple.

Whatever the implementation, key requirements are:

  • extend configurations of JSON or YAML format.
  • extends accepts a single string or an array of strings.
  • extends lookups can be absolute or relative paths, or a node_module name.
  • merging of extends occurs in the order of the array, so the last item takes priority over the first item (merges on top of it).

Pass in initial configuration object directly

Some users (like stylelint) may want to be able to pass in configuration object directly and take advantage of this module's extends support.

Probably should rename the current options.config to options.configPath and then use options.config for this.

Check --config argument

If user specifies a --config argument via the command line, we'd like to use that path (as rc does). It should be an absolute path. I imagine it would just end up passing that argument's value to loadDefinedPath().

JS config never gets garbage collected.

I discovered this issue while trying to catch a memory leak in our build tool. The way cosmiconfig loads '.js' config prevents it from ever getting garbage collected, even if userland code no longer needs. require-from-string package creates a new Module record which then permanently stored in memory. You can see it with this sample:

const cosmiconfig = require('cosmiconfig');

const loadRC = require.resolve('cosmiconfig/lib/loadRc.js');

function loadOnce() {
    const explorer = cosmiconfig('example');
    return explorer.load(__dirname)
        .then(() => console.log(require.cache[loadRC].children.length))
}

let count = 1000;
function loop() {
    if (count > 0) {
        count--;
        loadOnce().then(loop);
    }
}

loop();

Provided there is a non-empty example.config.js nearby, you should see that require.cache[loadRC].children.length grows by 1 after every load and never gets cleaned up.

Features of configuration loader

  • StyleLint
    • plugins path look up. returns path string.
    • extends load and merge
    • ignoreFiles support
  • ESLint
    • plugins / extends path look up or load, returns path string or object.
    • ignore support
  • Babel
    • presets / plugins path look up or load, returns path string or object.
    • extends load and merge
    • ignore support
  • stylefmt
    • same with StyleLint

I found 4 suitable to use cosmiconfig to replace its own loader project.

If they are using cosmiconfig to load the configuration file, you can solve the common existence of these projects bug. such as; con't work in editor plugin

@eslint
@babel

Synchronous API

Hi, I'm the author of stylefmt. I use rc-loader to find stylelint configration file, so it cannot match .stylelint.config.js.

I tried to introduce cosmiconfig, but it has only an asynchronouse API to find files.
Do you have a plan to add synchronouse API?
If you have the plan, I can fix this bug easily :)

Thanks.

Duplicate keys throw an error with an unhelpful message

I am working on stylelint webpack plugin and I think it would be appropriate for stylelint itself to inform users that they have duplicate keys in their .stylelintrc. I have been trying to get a PR going but I haven't had success running the tests on master yet.

Raising the issue here because I don't think that it's difficult to fix but I'm kinda blocked right now.

Document differences from rc

  • Stops at the first found, instead of finding all and merging them.
  • Looks for package.json prop.
  • Loads JSON, YAML, and CommonJS.
  • A little more configurable (different file name expectations).
  • Built-in extends support.
  • Not as built for extension -- not such a low-level module.

Any others?

More options

Followup to discussion in #9:

  • js (default true): if false, don't look for modulename.config.js.
  • rc (default: true): if false, don't look for.modulenamerc`.
  • packageProp (default: true): if false, don't look for property modulename in package.json files.
  • argv (default: true): if false, don't look for --config arguments

Support `config` key in package.json

I know we currently support the module name directly in package.json. But I think it would make things more organized if we also supported by default inside of a config property. Like this:

{
  "name""soursock",
  "version": "1.0.0",
  "config": {
    "soursock": {}
  }
}

Allow searching in `~/.config/`

The more people who use cosmiconfig, the more dot files will be littered in the home dir.

Lots of other modules place their config in ~/.config dir, mine looks like this:

Autodesk
Last.fm       
QCAD            
deluge          
git             
hub             
karabiner       
menus           
sinopia         
xbuild
NuGet           
configstore            
gtk-2.0        
inkscape    
robomongo      
stetic          
yarn

Would be cool...

Support for rc files with extensions

I read about the latest ESLint release (http://eslint.org/blog/2015/11/eslint-v1.10.0-released/) and thought that what they're doing with rc file extensions would be worth mimicking.

The idea is that rc files would not have to be extensionless: if user adds an extension to the file (e.g. .stylelintrc.yaml), then they'll get syntax highlighting in their editor and more exact error stacks if there are syntax errors.

Error: ENOENT: no such file or directory

cosmiconfig().load('/non-existent.css')

When we run this code. we will get a error.

Error: ENOENT: no such file or directory, stat '/non-existent.css'
    at Error (native)

Some gulp plugin will modify file extension.
Such as gulp-stylus, will rename *.styl to *.css.

gulp.src([ '*.styl', '!_*.styl' ])
  .pipe(stylus(options.stylus)) // => will rename `*.styl` to `*.css`.
  .pipe(postcss(processors))  // => postcss plugin that dependence `cosmiconfig` will throw a error.

See: masaakim/stylefmt#236

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.