Skip to content

Commit

Permalink
Added cjs support (#238)
Browse files Browse the repository at this point in the history
Co-authored-by: Ricky Patel <rickyp@tesla.com>
  • Loading branch information
2 people authored and davidtheclark committed Aug 1, 2020
1 parent 285289a commit f25f378
Show file tree
Hide file tree
Showing 6 changed files with 105 additions and 6 deletions.
15 changes: 9 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,16 @@ By default, Cosmiconfig will start where you tell it to start and search up the

- a `package.json` property
- a JSON or YAML, extensionless "rc file"
- an "rc file" with the extensions `.json`, `.yaml`, `.yml`, or `.js`.
- a `.config.js` CommonJS module
- an "rc file" with the extensions `.json`, `.yaml`, `.yml`, `.cjs`, or `.js`.
- a `.config.js` or `.config.cjs` CommonJS module

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` file
- a `.myapprc.yaml`, `.myapprc.yml`, or `.myapprc.js` file
- a `myapp.config.js` file exporting a JS object
- a `.myapprc.yaml`, `.myapprc.yml`, `.myapprc.cjs`, or `.myapprc.js` file
- a `myapp.config.js` or `myapp.config.cjs` file exporting a JS object

Cosmiconfig continues to search up the directory tree, checking each of these places in each directory, until it finds some acceptable configuration (or hits the home directory).

Expand Down Expand Up @@ -149,8 +149,8 @@ Here's how your default [`search()`] will work:
1. A `goldengrahams` property in a `package.json` file.
2. A `.goldengrahamsrc` file with JSON or YAML syntax.
3. A `.goldengrahamsrc.json` file.
4. A `.goldengrahamsrc.yaml`, `.goldengrahamsrc.yml`, or `.goldengrahamsrc.js` file.
5. A `goldengrahams.config.js` JS file exporting the object.
4. A `.goldengrahamsrc.yaml`, `.goldengrahamsrc.yml`, `.goldengrahamsrc.cjs` or `.goldengrahamsrc.js` file.
5. A `goldengrahams.config.js` or `goldengrahams.config.cjs` JS file exporting the object.
- If none of those searches reveal a configuration object, move up one directory level and try again.
So the search continues in `./`, `../`, `../../`, `../../../`, etc., checking the same places in each directory.
- Continue searching until arriving at your home directory (or some other directory defined by the cosmiconfig option [`stopDir`]).
Expand Down Expand Up @@ -274,6 +274,8 @@ Each place is relative to the directory being searched, and the places are check
`.${moduleName}rc.yml`,
`.${moduleName}rc.js`,
`${moduleName}.config.js`,
`.${moduleName}rc.cjs`,
`${moduleName}.config.cjs`,
]
```

Expand Down Expand Up @@ -348,6 +350,7 @@ const { defaultLoaders } = require('cosmiconfig');

console.log(Object.entries(defaultLoaders))
// [
// [ '.cjs', [Function: loadJs] ],
// [ '.js', [Function: loadJs] ],
// [ '.json', [Function: loadJson] ],
// [ '.yaml', [Function: loadYaml] ],
Expand Down
3 changes: 3 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ function cosmiconfigSync(moduleName: string, options: OptionsSync = {}) {

// do not allow mutation of default loaders. Make sure it is set inside options
const defaultLoaders = Object.freeze({
'.cjs': loaders.loadJs,
'.js': loaders.loadJs,
'.json': loaders.loadJson,
'.yaml': loaders.loadYaml,
Expand Down Expand Up @@ -115,6 +116,8 @@ function normalizeOptions(
`.${moduleName}rc.yml`,
`.${moduleName}rc.js`,
`${moduleName}.config.js`,
`.${moduleName}rc.cjs`,
`${moduleName}.config.cjs`,
],
ignoreEmptySearchPlaces: true,
stopDir: os.homedir(),
Expand Down
12 changes: 12 additions & 0 deletions test/caches.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ describe('cache is not used initially', () => {
'a/b/c/d/e/.foorc.yml',
'a/b/c/d/e/.foorc.js',
'a/b/c/d/e/foo.config.js',
'a/b/c/d/e/.foorc.cjs',
'a/b/c/d/e/foo.config.cjs',
'a/b/c/d/package.json',
'a/b/c/d/.foorc',
]);
Expand Down Expand Up @@ -143,6 +145,8 @@ describe('cache is used when some directories in search are already visted', ()
'a/b/c/d/e/f/.foorc.yml',
'a/b/c/d/e/f/.foorc.js',
'a/b/c/d/e/f/foo.config.js',
'a/b/c/d/e/f/.foorc.cjs',
'a/b/c/d/e/f/foo.config.cjs',
]);

expect(result).toEqual({
Expand Down Expand Up @@ -225,6 +229,8 @@ describe('cache is not used in a new cosmiconfig instance', () => {
'a/b/c/d/e/.foorc.yml',
'a/b/c/d/e/.foorc.js',
'a/b/c/d/e/foo.config.js',
'a/b/c/d/e/.foorc.cjs',
'a/b/c/d/e/foo.config.cjs',
'a/b/c/d/package.json',
'a/b/c/d/.foorc',
]);
Expand Down Expand Up @@ -341,6 +347,8 @@ describe('clears directory cache on calling clearSearchCache', () => {
'a/b/c/d/e/.foorc.yml',
'a/b/c/d/e/.foorc.js',
'a/b/c/d/e/foo.config.js',
'a/b/c/d/e/.foorc.cjs',
'a/b/c/d/e/foo.config.cjs',
'a/b/c/d/package.json',
'a/b/c/d/.foorc',
]);
Expand Down Expand Up @@ -387,6 +395,8 @@ describe('clears directory cache on calling clearCaches', () => {
'a/b/c/d/e/.foorc.yml',
'a/b/c/d/e/.foorc.js',
'a/b/c/d/e/foo.config.js',
'a/b/c/d/e/.foorc.cjs',
'a/b/c/d/e/foo.config.cjs',
'a/b/c/d/package.json',
'a/b/c/d/.foorc',
]);
Expand Down Expand Up @@ -449,6 +459,8 @@ describe('with cache disabled, does not cache directory results', () => {
'a/b/c/d/e/.foorc.yml',
'a/b/c/d/e/.foorc.js',
'a/b/c/d/e/foo.config.js',
'a/b/c/d/e/.foorc.cjs',
'a/b/c/d/e/foo.config.cjs',
'a/b/c/d/package.json',
'a/b/c/d/.foorc',
]);
Expand Down
10 changes: 10 additions & 0 deletions test/failed-directories.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,26 @@ describe('gives up if it cannot find the file', () => {
'a/b/.foorc.yml',
'a/b/.foorc.js',
'a/b/foo.config.js',
'a/b/.foorc.cjs',
'a/b/foo.config.cjs',
'a/package.json',
'a/.foorc',
'a/.foorc.json',
'a/.foorc.yaml',
'a/.foorc.yml',
'a/.foorc.js',
'a/foo.config.js',
'a/.foorc.cjs',
'a/foo.config.cjs',
'package.json',
'.foorc',
'.foorc.json',
'.foorc.yaml',
'.foorc.yml',
'.foorc.js',
'foo.config.js',
'.foorc.cjs',
'foo.config.cjs',
]);

expect(result).toBe(null);
Expand Down Expand Up @@ -86,13 +92,17 @@ describe('stops at stopDir and gives up', () => {
'a/b/.foorc.yml',
'a/b/.foorc.js',
'a/b/foo.config.js',
'a/b/.foorc.cjs',
'a/b/foo.config.cjs',
'a/package.json',
'a/.foorc',
'a/.foorc.json',
'a/.foorc.yaml',
'a/.foorc.yml',
'a/.foorc.js',
'a/foo.config.js',
'a/.foorc.cjs',
'a/foo.config.cjs',
]);

expect(result).toBe(null);
Expand Down
5 changes: 5 additions & 0 deletions test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ describe('cosmiconfig', () => {
expect(transform.name).toBe('identity');
const loaderFunctionsByName = getLoaderFunctionsByName(loaders);
expect(loaderFunctionsByName).toEqual({
'.cjs': 'loadJs',
'.js': 'loadJs',
'.json': 'loadJson',
'.yaml': 'loadYaml',
Expand All @@ -99,6 +100,8 @@ describe('cosmiconfig', () => {
`.${moduleName}rc.yml`,
`.${moduleName}rc.js`,
`${moduleName}.config.js`,
`.${moduleName}rc.cjs`,
`${moduleName}.config.cjs`,
],
ignoreEmptySearchPlaces: true,
stopDir: os.homedir(),
Expand Down Expand Up @@ -155,6 +158,7 @@ describe('cosmiconfig', () => {
ignoreEmptySearchPlaces: false,
loaders: {
noExt: noExtLoader,
'.cjs': jsLoader,
'.js': jsLoader,
'.json': jsonLoader,
'.yaml': yamlLoader,
Expand All @@ -169,6 +173,7 @@ describe('cosmiconfig', () => {
expect(transform.name).toBe('identity');
const loaderFunctionsByName = getLoaderFunctionsByName(loaders);
expect(loaderFunctionsByName).toEqual({
'.cjs': 'jsLoader',
'.js': 'jsLoader',
'.json': 'jsonLoader',
'.yaml': 'yamlLoader',
Expand Down
66 changes: 66 additions & 0 deletions test/successful-directories.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,17 @@ describe('finds rc file in third searched dir, with a package.json lacking prop'
'a/b/c/d/e/f/.foorc.yml',
'a/b/c/d/e/f/.foorc.js',
'a/b/c/d/e/f/foo.config.js',
'a/b/c/d/e/f/.foorc.cjs',
'a/b/c/d/e/f/foo.config.cjs',
'a/b/c/d/e/package.json',
'a/b/c/d/e/.foorc',
'a/b/c/d/e/.foorc.json',
'a/b/c/d/e/.foorc.yaml',
'a/b/c/d/e/.foorc.yml',
'a/b/c/d/e/.foorc.js',
'a/b/c/d/e/foo.config.js',
'a/b/c/d/e/.foorc.cjs',
'a/b/c/d/e/foo.config.cjs',
'a/b/c/d/package.json',
'a/b/c/d/.foorc',
]);
Expand Down Expand Up @@ -86,6 +90,8 @@ describe('finds package.json prop in second searched dir', () => {
'a/b/c/d/e/f/.foorc.yml',
'a/b/c/d/e/f/.foorc.js',
'a/b/c/d/e/f/foo.config.js',
'a/b/c/d/e/f/.foorc.cjs',
'a/b/c/d/e/f/foo.config.cjs',
'a/b/c/d/e/package.json',
]);

Expand Down Expand Up @@ -139,6 +145,8 @@ describe('finds package.json with nested packageProp in second searched dir', ()
'a/b/c/d/e/f/.foorc.yml',
'a/b/c/d/e/f/.foorc.js',
'a/b/c/d/e/f/foo.config.js',
'a/b/c/d/e/f/.foorc.cjs',
'a/b/c/d/e/f/foo.config.cjs',
'a/b/c/d/e/package.json',
]);

Expand Down Expand Up @@ -206,6 +214,53 @@ describe('finds JS file in first searched dir', () => {
});
});

describe('finds CJS file in first searched dir', () => {
beforeEach(() => {
temp.createFile(
'a/b/c/d/e/f/foo.config.cjs',
'module.exports = { found: true };',
);
});

const startDir = temp.absolutePath('a/b/c/d/e/f');
const explorerOptions = { stopDir: temp.absolutePath('.') };

const checkResult = (readFileSpy: any, result: any) => {
const filesChecked = temp.getSpyPathCalls(readFileSpy);

expect(filesChecked).toEqual([
'a/b/c/d/e/f/package.json',
'a/b/c/d/e/f/.foorc',
'a/b/c/d/e/f/.foorc.json',
'a/b/c/d/e/f/.foorc.yaml',
'a/b/c/d/e/f/.foorc.yml',
'a/b/c/d/e/f/.foorc.js',
'a/b/c/d/e/f/foo.config.js',
'a/b/c/d/e/f/.foorc.cjs',
'a/b/c/d/e/f/foo.config.cjs',
]);

expect(result).toEqual({
config: { found: true },
filepath: temp.absolutePath('a/b/c/d/e/f/foo.config.cjs'),
});
};

test('async', async () => {
const readFileSpy = jest.spyOn(fs, 'readFile');

const result = await cosmiconfig('foo', explorerOptions).search(startDir);
checkResult(readFileSpy, result);
});

test('sync', () => {
const readFileSpy = jest.spyOn(fs, 'readFileSync');

const result = cosmiconfigSync('foo', explorerOptions).search(startDir);
checkResult(readFileSpy, result);
});
});

describe('finds .foorc.js file in first searched dir', () => {
beforeEach(() => {
temp.createFile(
Expand Down Expand Up @@ -443,6 +498,8 @@ describe('finds .foorc.json in second searched dir', () => {
'a/b/c/d/e/f/.foorc.yml',
'a/b/c/d/e/f/.foorc.js',
'a/b/c/d/e/f/foo.config.js',
'a/b/c/d/e/f/.foorc.cjs',
'a/b/c/d/e/f/foo.config.cjs',
'a/b/c/d/e/package.json',
'a/b/c/d/e/.foorc',
'a/b/c/d/e/.foorc.json',
Expand Down Expand Up @@ -567,6 +624,8 @@ describe('adding myfooconfig.js to searchPlaces, finds it in first searched dir'
'.foorc.json',
'.foorc.yaml',
'.foorc.yml',
'.foorc.cjs',
'foo.config.cjs',
'.foorc.js',
'foo.config.js',
'myfooconfig.js',
Expand All @@ -581,6 +640,8 @@ describe('adding myfooconfig.js to searchPlaces, finds it in first searched dir'
'a/b/c/d/e/f/.foorc.json',
'a/b/c/d/e/f/.foorc.yaml',
'a/b/c/d/e/f/.foorc.yml',
'a/b/c/d/e/f/.foorc.cjs',
'a/b/c/d/e/f/foo.config.cjs',
'a/b/c/d/e/f/.foorc.js',
'a/b/c/d/e/f/foo.config.js',
'a/b/c/d/e/f/myfooconfig.js',
Expand Down Expand Up @@ -635,6 +696,8 @@ describe('finds JS file traversing from cwd', () => {
'a/b/c/d/e/f/.foorc.yml',
'a/b/c/d/e/f/.foorc.js',
'a/b/c/d/e/f/foo.config.js',
'a/b/c/d/e/f/.foorc.cjs',
'a/b/c/d/e/f/foo.config.cjs',
'a/b/c/d/e/package.json',
'a/b/c/d/e/.foorc',
'a/b/c/d/e/.foorc.json',
Expand Down Expand Up @@ -861,6 +924,7 @@ describe('defaults loaders can be overridden', () => {
searchPlaces: [
'package.json',
'.foorc.json',
'foo.config.cjs',
'foo.config.js',
'.foorc.yml',
],
Expand All @@ -874,10 +938,12 @@ describe('defaults loaders can be overridden', () => {
expect(filesChecked).toEqual([
'a/b/c/d/e/f/package.json',
'a/b/c/d/e/f/.foorc.json',
'a/b/c/d/e/f/foo.config.cjs',
'a/b/c/d/e/f/foo.config.js',
'a/b/c/d/e/f/.foorc.yml',
'a/b/c/d/e/package.json',
'a/b/c/d/e/.foorc.json',
'a/b/c/d/e/foo.config.cjs',
'a/b/c/d/e/foo.config.js',
]);

Expand Down

0 comments on commit f25f378

Please sign in to comment.