Skip to content

Commit

Permalink
feat(find): ignore tsconfig.json inside node_modules by default (#123)
Browse files Browse the repository at this point in the history
* feat(find): ignore tsconfig.json files inside node_modules

* feat(find): add option scanNodeModules, update documentation and more node: import specifiers

* fix: stupid cp bug in tests
  • Loading branch information
dominikg authored Sep 4, 2023
1 parent fec5da8 commit e907d31
Show file tree
Hide file tree
Showing 23 changed files with 137 additions and 24 deletions.
5 changes: 5 additions & 0 deletions .changeset/tricky-trees-tell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'tsconfck': major
---

breaking(find): ignore tsconfig files inside node_modules
7 changes: 7 additions & 0 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ interface TSConfckFindOptions {
* Improves performance but may lead to different results from native typescript when no tsconfig is found inside root
*/
root?: string;

/**
* set to true if you want to find tsconfig.json files inside node_modules
*
* @default false
*/
scanNodeModules?: boolean;
}
```

Expand Down
31 changes: 26 additions & 5 deletions packages/tsconfck/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,19 @@ see [API-DOCS](docs/api.md)

## Advanced

### finding tsconfig.json files inside node_modules

By default, tsconfck ignores tsconfig.json files inside node_modules, similar to esbuild.
If you want to include tsconfig.json files inside node_modules in your find/parse results, set `scanNodeModules`

```js
import { find } from 'tsconfck';
// does not return some-lib/tsconfig.json but first tsconfig outside of node_modules
const fooTSConfig = await find('node_modules/some-lib/src/foo.ts');
// returns some-lib/tsconfig.json if it exists otherwise continues finding up the tree
const fooResult = await find('node_modules/some-lib/src/foo.ts', { scanNodeModules: true });
```

### caching

a TSConfckCache instance can be created and passed to find and parse functions to reduce overhead when they are called often within the same project
Expand Down Expand Up @@ -107,16 +120,24 @@ const barResult = await parse('src/bar.ts', parseOptions);
### error handling

find and parse reject for all errors they encounter.
find and parse reject for errors they encounter, but return null or empty result if no config was found

For parse, you can choose to resolve with an empty result instead if no tsconfig file was found
If you want them to error instead, test the result and throw

```js
import { parse } from 'tsconfck';
const result = await parse('some/path/without/tsconfig/foo.ts', {
resolveWithEmptyIfConfigNotFound: true
find('some/path/without/tsconfig/foo.ts').then((result) => {
if (result === null) {
throw new Error('not found');
}
return result;
});
parse('some/path/without/tsconfig/foo.ts').then((result) => {
if (result.tsconfigFile === null) {
throw new Error('not found');
}
return result;
});
// result = { tsconfigFile: 'no_tsconfig_file_found',tsconfig: {} }
```

### TSConfig type (optional, requires typescript as devDependency)
Expand Down
2 changes: 1 addition & 1 deletion packages/tsconfck/src/find-all.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import path from 'path';
import path from 'node:path';
import { readdir } from 'node:fs';

/**
Expand Down
17 changes: 10 additions & 7 deletions packages/tsconfck/src/find-native.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import path from 'path';
import { loadTS } from './util.js';
import path from 'node:path';
import { loadTS, stripNodeModules } from './util.js';

/**
* find the closest tsconfig.json file using native ts.findConfigFile
Expand All @@ -11,20 +11,23 @@ import { loadTS } from './util.js';
* @returns {Promise<string>} absolute path to closest tsconfig.json
*/
export async function findNative(filename, options) {
const fileDir = path.dirname(path.resolve(filename));
let dir = path.dirname(path.resolve(filename));
if (!options?.scanNodeModules) {
dir = stripNodeModules(dir);
}
const cache = options?.cache;
const root = options?.root ? path.resolve(options.root) : undefined;
if (cache?.hasTSConfigPath(fileDir)) {
return cache.getTSConfigPath(fileDir);
if (cache?.hasTSConfigPath(dir)) {
return cache.getTSConfigPath(dir);
}
const ts = await loadTS();
const { findConfigFile, sys } = ts;
let tsconfigFile = findConfigFile(fileDir, sys.fileExists);
let tsconfigFile = findConfigFile(dir, sys.fileExists);
if (!tsconfigFile || is_out_of_root(tsconfigFile, root)) {
tsconfigFile = null;
}
if (cache) {
cache_result(tsconfigFile, fileDir, cache, root);
cache_result(tsconfigFile, dir, cache, root);
}
return tsconfigFile;
}
Expand Down
4 changes: 4 additions & 0 deletions packages/tsconfck/src/find.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import path from 'node:path';
import fs from 'node:fs';
import { stripNodeModules } from './util.js';
/**
* find the closest tsconfig.json file
*
Expand All @@ -10,6 +11,9 @@ import fs from 'node:fs';
export async function find(filename, options) {
const cache = options?.cache;
let dir = path.dirname(path.resolve(filename));
if (!options?.scanNodeModules) {
dir = stripNodeModules(dir);
}
if (cache?.hasTSConfigPath(dir)) {
return cache.getTSConfigPath(dir);
}
Expand Down
2 changes: 1 addition & 1 deletion packages/tsconfck/src/parse-native.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import path from 'path';
import path from 'node:path';
import {
loadTS,
native2posix,
Expand Down
2 changes: 1 addition & 1 deletion packages/tsconfck/src/parse.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import path from 'path';
import path from 'node:path';
import { promises as fs } from 'node:fs';
import { createRequire } from 'module';
import { find } from './find.js';
Expand Down
7 changes: 7 additions & 0 deletions packages/tsconfck/src/public.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ export interface TSConfckFindOptions {
* Improves performance but may lead to different results from native typescript when no tsconfig is found inside root
*/
root?: string;

/**
* set to true if you want to find tsconfig.json files inside node_modules
*
* @default false
*/
scanNodeModules?: boolean;
}

export interface TSConfckParseOptions extends TSConfckFindOptions {
Expand Down
13 changes: 13 additions & 0 deletions packages/tsconfck/src/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,19 @@ export async function resolveTSConfigJson(filename, cache) {
});
}

/**
* remove path segments inside node_modules
*
* @param {string} dir an absolute directory path
* @returns {string} parent dir of node_modules if inside of node_modules, dir if not
*/
export const stripNodeModules = IS_POSIX
? (dir) => {
const i = dir.indexOf('/node_modules/');
return i > -1 ? dir.slice(0, i) : dir;
}
: (dir) => dir.replace(/[/\\]node_modules[/\\].*$/, '');

/**
* convert posix separator to native separator
*
Expand Down
4 changes: 2 additions & 2 deletions packages/tsconfck/tests/find-all.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@ describe('find-all', () => {
it('should find tsconfig in same directory', async () => {
const fixtureDir = 'find/a';
const expected = absFixture(`${fixtureDir}/tsconfig.json`);
const found = await findAll(absFixture(fixtureDir));
const found = await findAll(absFixture(fixtureDir), { skip: (dir) => dir === 'node_modules' });
expect(found).toEqual([expected]);
});

it('should find tsconfig in child directory', async () => {
const fixtureDir = 'find';
const expected = absFixture(`${fixtureDir}/a/tsconfig.json`);
const found = await findAll(absFixture(fixtureDir));
const found = await findAll(absFixture(fixtureDir), { skip: (dir) => dir === 'node_modules' });
expect(found).toEqual([expected]);
});

Expand Down
24 changes: 23 additions & 1 deletion packages/tsconfck/tests/find-native.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, it, expect } from 'vitest';
import path from 'path';
import path from 'node:path';
import os from 'os';
import { findNative } from '../src/find-native.js';
import { absFixture, absRoot, relFixture } from './util/fixture-paths.js';
Expand Down Expand Up @@ -47,6 +47,28 @@ describe('find-native', () => {
}
});

it('should ignore tsconfig in node_modules directory', async () => {
const fixtureDir = 'find/a';
const expected = native2posix(absFixture(`${fixtureDir}/tsconfig.json`));
const relativeTS = relFixture(`${fixtureDir}/node_modules/some-lib/src/foo.ts`);
const absoluteTS = absFixture(`${fixtureDir}/node_modules/some-lib/src/foo.ts`);
const inputs = [relativeTS, `./${relativeTS}`, absoluteTS];
for (const input of inputs) {
expect(await findNative(input), `input: ${input}`).toBe(expected);
}
});

it('should find tsconfig in node_modules directory with scanNodeModules=true', async () => {
const fixtureDir = 'find/a';
const expected = native2posix(absFixture(`${fixtureDir}/node_modules/some-lib/tsconfig.json`));
const relativeTS = relFixture(`${fixtureDir}/node_modules/some-lib/src/foo.ts`);
const absoluteTS = absFixture(`${fixtureDir}/node_modules/some-lib/src/foo.ts`);
const inputs = [relativeTS, `./${relativeTS}`, absoluteTS];
for (const input of inputs) {
expect(await findNative(input, { scanNodeModules: true }), `input: ${input}`).toBe(expected);
}
});

it('should stop searching at root', async () => {
const fixtureDir = 'find-root/a';
const relativeTS = relFixture(`${fixtureDir}/b/foo.ts`);
Expand Down
24 changes: 23 additions & 1 deletion packages/tsconfck/tests/find.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, it, expect } from 'vitest';
import path from 'path';
import path from 'node:path';
import os from 'os';
import { find } from '../src/find.js';
import { absFixture, absRoot, relFixture } from './util/fixture-paths.js';
Expand Down Expand Up @@ -44,6 +44,28 @@ describe('find', () => {
}
});

it('should ignore tsconfig in node_modules directory', async () => {
const fixtureDir = 'find/a';
const expected = absFixture(`${fixtureDir}/tsconfig.json`);
const relativeTS = relFixture(`${fixtureDir}/node_modules/some-lib/src/foo.ts`);
const absoluteTS = absFixture(`${fixtureDir}/node_modules/some-lib/src/foo.ts`);
const inputs = [relativeTS, `./${relativeTS}`, absoluteTS];
for (const input of inputs) {
expect(await find(input), `input: ${input}`).toBe(expected);
}
});

it('should find tsconfig in node_modules directory with scanNodeModules=true', async () => {
const fixtureDir = 'find/a';
const expected = absFixture(`${fixtureDir}/node_modules/some-lib/tsconfig.json`);
const relativeTS = relFixture(`${fixtureDir}/node_modules/some-lib/src/foo.ts`);
const absoluteTS = absFixture(`${fixtureDir}/node_modules/some-lib/src/foo.ts`);
const inputs = [relativeTS, `./${relativeTS}`, absoluteTS];
for (const input of inputs) {
expect(await find(input, { scanNodeModules: true }), `input: ${input}`).toBe(expected);
}
});

it('should stop searching at root', async () => {
const fixtureDir = 'find-root/a';
const relativeTS = relFixture(`${fixtureDir}/b/foo.ts`);
Expand Down
1 change: 1 addition & 0 deletions packages/tsconfck/tests/fixtures/find/a/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
!node_modules
Empty file.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Empty file.
2 changes: 1 addition & 1 deletion packages/tsconfck/tests/parse.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, it, expect } from 'vitest';
import path from 'path';
import path from 'node:path';
import { parse, TSConfckParseError } from '../src/parse.js';
import os from 'os';

Expand Down
2 changes: 1 addition & 1 deletion packages/tsconfck/tests/util.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, it, expect } from 'vitest';
import path from 'path';
import path from 'node:path';
import os from 'os';
import { isGlobMatch, native2posix, resolve2posix } from '../src/util.js';

Expand Down
2 changes: 1 addition & 1 deletion packages/tsconfck/tests/util/copy-fixtures.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { promises as fs } from 'node:fs';
import path from 'path';
import path from 'node:path';

/**
*
Expand Down
2 changes: 1 addition & 1 deletion packages/tsconfck/tests/util/fixture-paths.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { fileURLToPath } from 'node:url';
import path from 'path';
import path from 'node:path';
import glob from 'tiny-glob';
const cwd = process.cwd();
const root = fileURLToPath(new URL('../..', import.meta.url));
Expand Down
7 changes: 7 additions & 0 deletions packages/tsconfck/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,13 @@ declare module 'tsconfck' {
* Improves performance but may lead to different results from native typescript when no tsconfig is found inside root
*/
root?: string;

/**
* set to true if you want to find tsconfig.json files inside node_modules
*
* @default false
*/
scanNodeModules?: boolean;
}

interface TSConfckParseOptions extends TSConfckFindOptions {
Expand Down
2 changes: 1 addition & 1 deletion packages/tsconfck/types/index.d.ts.map
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,5 @@
null,
null
],
"mappings": ";;;;;;;;iBASsBA,IAAIA;;;;;;;;iBCYJC,OAAOA;;;;;;;iBCRbC,MAAMA;;;;;;;;;;iBCDAC,UAAUA;cCXnBC,aAAaA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBCyBJC,KAAKA;cAqTdC,kBAAkBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBCrTTC,WAAWA;cAyNpBC,wBAAwBA;;;;;;;;;;;;;;;;;;;;;;;;;WCjPpBC,mBAAmBA;;;;;;;;;;;;;;;;WAgBnBC,oBAAoBA;;;;WAIpBC,sBAAsBA;;;;;;;;;WAStBC,mBAAmBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WA6BnBC,0BAA0BA;;;;;;;;;;;;WAY1BC,yBAAyBA"
"mappings": ";;;;;;;;iBAUsBA,IAAIA;;;;;;;;iBCWJC,OAAOA;;;;;;;iBCRbC,MAAMA;;;;;;;;;;iBCDAC,UAAUA;cCXnBC,aAAaA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBCyBJC,KAAKA;cAqTdC,kBAAkBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBCrTTC,WAAWA;cAyNpBC,wBAAwBA;;;;;;;;;;;;;;;;;;;;;;;;;WCjPpBC,mBAAmBA;;;;;;;;;;;;;;;;;;;;;;;WAuBnBC,oBAAoBA;;;;WAIpBC,sBAAsBA;;;;;;;;;WAStBC,mBAAmBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WA6BnBC,0BAA0BA;;;;;;;;;;;;WAY1BC,yBAAyBA"
}

0 comments on commit e907d31

Please sign in to comment.