Skip to content

Commit

Permalink
Add test suite for all current config loading systems (#301)
Browse files Browse the repository at this point in the history
* Add test suite for all current config loading systems

* Introduce async and function config factories

* Rename all fixtures config names to match folder

* Apply suggestions from code review

Co-authored-by: Lars Kappert <lars@webpro.nl>

* Refactor using a exec wrapper for tests

* Add dist cli auto path detection

* Use `path.resolve` to get absolute path to `dist/cli.js`

---------

Co-authored-by: Lars Kappert <lars@webpro.nl>
  • Loading branch information
beaussan and webpro committed Oct 17, 2023
1 parent 50dd048 commit a18f2a6
Show file tree
Hide file tree
Showing 69 changed files with 335 additions and 20 deletions.
13 changes: 13 additions & 0 deletions README.md
Expand Up @@ -125,6 +125,19 @@ const config: KnipConfig = {
export default config;
```

And if you need, you can also expose an (async) function if you need, like so:

```ts
import type { KnipConfig } from 'knip';

const config = async (): Promise<KnipConfig> => ({
entry: ['src/index.ts'],
project: ['src/**/*.ts'],
});

export default config;
```

Use `--config path/to/knip.config.json` for a different path.

### Let's Go!
Expand Down
1 change: 1 addition & 0 deletions fixtures/config/js-async/dangling.js
@@ -0,0 +1 @@
export const unusedFile = true;
3 changes: 3 additions & 0 deletions fixtures/config/js-async/index.js
@@ -0,0 +1,3 @@
import * as MyNamespace from './my-namespace';

export const b = MyNamespace.y;
3 changes: 3 additions & 0 deletions fixtures/config/js-async/knip.js
@@ -0,0 +1,3 @@
module.exports = async () => ({
ignore: ['dangling.js']
});
2 changes: 2 additions & 0 deletions fixtures/config/js-async/my-namespace.js
@@ -0,0 +1,2 @@
const x = 1;
export const y = () => x;
3 changes: 3 additions & 0 deletions fixtures/config/js-async/package.json
@@ -0,0 +1,3 @@
{
"name": "@fixtures/config-js-async"
}
1 change: 1 addition & 0 deletions fixtures/config/js-flat/dangling.js
@@ -0,0 +1 @@
export const unusedFile = true;
3 changes: 3 additions & 0 deletions fixtures/config/js-flat/index.js
@@ -0,0 +1,3 @@
import * as MyNamespace from './my-namespace';

export const b = MyNamespace.y;
3 changes: 3 additions & 0 deletions fixtures/config/js-flat/knip.js
@@ -0,0 +1,3 @@
module.exports = {
ignore: ['dangling.js']
}
2 changes: 2 additions & 0 deletions fixtures/config/js-flat/my-namespace.js
@@ -0,0 +1,2 @@
const x = 1;
export const y = () => x;
3 changes: 3 additions & 0 deletions fixtures/config/js-flat/package.json
@@ -0,0 +1,3 @@
{
"name": "@fixtures/config-js-flat"
}
1 change: 1 addition & 0 deletions fixtures/config/json/dangling.js
@@ -0,0 +1 @@
export const unusedFile = true;
3 changes: 3 additions & 0 deletions fixtures/config/json/index.js
@@ -0,0 +1,3 @@
import * as MyNamespace from './my-namespace';

export const b = MyNamespace.y;
3 changes: 3 additions & 0 deletions fixtures/config/json/knip.json
@@ -0,0 +1,3 @@
{
"ignore": ["dangling.js"]
}
2 changes: 2 additions & 0 deletions fixtures/config/json/my-namespace.js
@@ -0,0 +1,2 @@
const x = 1;
export const y = () => x;
3 changes: 3 additions & 0 deletions fixtures/config/json/package.json
@@ -0,0 +1,3 @@
{
"name": "@fixtures/config-json"
}
1 change: 1 addition & 0 deletions fixtures/config/mjs-async/dangling.js
@@ -0,0 +1 @@
export const unusedFile = true;
3 changes: 3 additions & 0 deletions fixtures/config/mjs-async/index.js
@@ -0,0 +1,3 @@
import * as MyNamespace from './my-namespace';

export const b = MyNamespace.y;
5 changes: 5 additions & 0 deletions fixtures/config/mjs-async/knip.mjs
@@ -0,0 +1,5 @@
const config = async () => ({
ignore: ['dangling.js'],
});

export default config;
2 changes: 2 additions & 0 deletions fixtures/config/mjs-async/my-namespace.js
@@ -0,0 +1,2 @@
const x = 1;
export const y = () => x;
3 changes: 3 additions & 0 deletions fixtures/config/mjs-async/package.json
@@ -0,0 +1,3 @@
{
"name": "@fixtures/config-mjs-async"
}
1 change: 1 addition & 0 deletions fixtures/config/mjs-flat/dangling.js
@@ -0,0 +1 @@
export const unusedFile = true;
3 changes: 3 additions & 0 deletions fixtures/config/mjs-flat/index.js
@@ -0,0 +1,3 @@
import * as MyNamespace from './my-namespace';

export const b = MyNamespace.y;
5 changes: 5 additions & 0 deletions fixtures/config/mjs-flat/knip.mjs
@@ -0,0 +1,5 @@
const config = {
ignore: ['dangling.js'],
};

export default config;
2 changes: 2 additions & 0 deletions fixtures/config/mjs-flat/my-namespace.js
@@ -0,0 +1,2 @@
const x = 1;
export const y = () => x;
3 changes: 3 additions & 0 deletions fixtures/config/mjs-flat/package.json
@@ -0,0 +1,3 @@
{
"name": "@fixtures/config-mjs-flat"
}
1 change: 1 addition & 0 deletions fixtures/config/package-json/dangling.js
@@ -0,0 +1 @@
export const unusedFile = true;
3 changes: 3 additions & 0 deletions fixtures/config/package-json/index.js
@@ -0,0 +1,3 @@
import * as MyNamespace from './my-namespace';

export const b = MyNamespace.y;
2 changes: 2 additions & 0 deletions fixtures/config/package-json/my-namespace.js
@@ -0,0 +1,2 @@
const x = 1;
export const y = () => x;
6 changes: 6 additions & 0 deletions fixtures/config/package-json/package.json
@@ -0,0 +1,6 @@
{
"name": "@fixtures/config-package-json",
"knip": {
"ignore": ["dangling.js"]
}
}
1 change: 1 addition & 0 deletions fixtures/config/ts-async/dangling.js
@@ -0,0 +1 @@
export const unusedFile = true;
3 changes: 3 additions & 0 deletions fixtures/config/ts-async/index.js
@@ -0,0 +1,3 @@
import * as MyNamespace from './my-namespace';

export const b = MyNamespace.y;
7 changes: 7 additions & 0 deletions fixtures/config/ts-async/knip.ts
@@ -0,0 +1,7 @@
import type { KnipConfig } from '../../../src/index';

const config = async (): Promise<KnipConfig> => ({
ignore: ['dangling.js'],
});

export default config;
2 changes: 2 additions & 0 deletions fixtures/config/ts-async/my-namespace.js
@@ -0,0 +1,2 @@
const x = 1;
export const y = () => x;
3 changes: 3 additions & 0 deletions fixtures/config/ts-async/package.json
@@ -0,0 +1,3 @@
{
"name": "@fixtures/config-ts-async"
}
5 changes: 5 additions & 0 deletions fixtures/config/ts-async/tsconfig.json
@@ -0,0 +1,5 @@
{
"compilerOptions": {
"types": ["node"]
}
}
1 change: 1 addition & 0 deletions fixtures/config/ts-flat/dangling.js
@@ -0,0 +1 @@
export const unusedFile = true;
3 changes: 3 additions & 0 deletions fixtures/config/ts-flat/index.js
@@ -0,0 +1,3 @@
import * as MyNamespace from './my-namespace';

export const b = MyNamespace.y;
7 changes: 7 additions & 0 deletions fixtures/config/ts-flat/knip.ts
@@ -0,0 +1,7 @@
import type { KnipConfig } from '../../../src/index';

const config: KnipConfig = {
ignore: ['dangling.js'],
};

export default config;
2 changes: 2 additions & 0 deletions fixtures/config/ts-flat/my-namespace.js
@@ -0,0 +1,2 @@
const x = 1;
export const y = () => x;
3 changes: 3 additions & 0 deletions fixtures/config/ts-flat/package.json
@@ -0,0 +1,3 @@
{
"name": "@fixtures/config-ts-flat"
}
5 changes: 5 additions & 0 deletions fixtures/config/ts-flat/tsconfig.json
@@ -0,0 +1,5 @@
{
"compilerOptions": {
"types": ["node"]
}
}
1 change: 1 addition & 0 deletions fixtures/config/ts-function/dangling.js
@@ -0,0 +1 @@
export const unusedFile = true;
3 changes: 3 additions & 0 deletions fixtures/config/ts-function/index.js
@@ -0,0 +1,3 @@
import * as MyNamespace from './my-namespace';

export const b = MyNamespace.y;
7 changes: 7 additions & 0 deletions fixtures/config/ts-function/knip.ts
@@ -0,0 +1,7 @@
import type { KnipConfig } from '../../../src/index';

const config = (): KnipConfig => ({
ignore: ['dangling.js'],
});

export default config;
2 changes: 2 additions & 0 deletions fixtures/config/ts-function/my-namespace.js
@@ -0,0 +1,2 @@
const x = 1;
export const y = () => x;
3 changes: 3 additions & 0 deletions fixtures/config/ts-function/package.json
@@ -0,0 +1,3 @@
{
"name": "@fixtures/config-ts-function"
}
5 changes: 5 additions & 0 deletions fixtures/config/ts-function/tsconfig.json
@@ -0,0 +1,5 @@
{
"compilerOptions": {
"types": ["node"]
}
}
1 change: 1 addition & 0 deletions fixtures/config/yaml/dangling.js
@@ -0,0 +1 @@
export const unusedFile = true;
3 changes: 3 additions & 0 deletions fixtures/config/yaml/index.js
@@ -0,0 +1,3 @@
import * as MyNamespace from './my-namespace';

export const b = MyNamespace.y;
2 changes: 2 additions & 0 deletions fixtures/config/yaml/knip.yaml
@@ -0,0 +1,2 @@
ignore:
- 'dangling.js'
2 changes: 2 additions & 0 deletions fixtures/config/yaml/my-namespace.js
@@ -0,0 +1,2 @@
const x = 1;
export const y = () => x;
3 changes: 3 additions & 0 deletions fixtures/config/yaml/package.json
@@ -0,0 +1,3 @@
{
"name": "@fixtures/config-yaml"
}
14 changes: 13 additions & 1 deletion src/ConfigurationChief.ts
Expand Up @@ -18,6 +18,7 @@ import { getKeysByValue } from './util/object.js';
import { join, relative, toPosix } from './util/path.js';
import { normalizePluginConfig, toCamelCase } from './util/plugin.js';
import { _require } from './util/require.js';
import { unwrapFunction } from './util/unwrapFunction.js';
import { byPathDepth } from './util/workspace.js';
import type { SyncCompilers, AsyncCompilers } from './types/compilers.js';
import type {
Expand Down Expand Up @@ -150,7 +151,9 @@ export class ConfigurationChief {
throw new ConfigurationError(`Unable to find ${rawConfigArg} or package.json#knip`);
}

this.rawConfig = this.resolvedConfigFilePath ? await _load(this.resolvedConfigFilePath) : manifest.knip;
this.rawConfig = this.resolvedConfigFilePath
? await this.loadResolvedConfigurationFile(this.resolvedConfigFilePath)
: manifest.knip;

// Have to partition compiler functions before Zod touches them
const parsedConfig = this.rawConfig ? ConfigurationValidator.parse(partitionCompilers(this.rawConfig)) : {};
Expand All @@ -159,6 +162,15 @@ export class ConfigurationChief {
await this.setWorkspaces();
}

private async loadResolvedConfigurationFile(configPath: string) {
const loadedValue = await _load(configPath);
try {
return await unwrapFunction(loadedValue);
} catch (e) {
throw new ConfigurationError(`Error running the function from ${configPath}`);
}
}

public getCompilers(): [SyncCompilers, AsyncCompilers] {
return [this.config.syncCompilers, this.config.asyncCompilers];
}
Expand Down
13 changes: 13 additions & 0 deletions src/util/unwrapFunction.ts
@@ -0,0 +1,13 @@
import { debugLogObject } from './debug.js';

export const unwrapFunction = async (possibleFunction: unknown) => {
if (typeof possibleFunction === 'function') {
try {
return await possibleFunction();
} catch (error) {
debugLogObject('*', 'Error executing function:', error);
throw error;
}
}
return possibleFunction;
};
11 changes: 2 additions & 9 deletions test/cli-preprocessor.test.ts
@@ -1,18 +1,11 @@
import assert from 'node:assert/strict';
import { execSync } from 'node:child_process';
import test from 'node:test';
import { resolve } from '../src/util/path.js';
import { execFactory } from './helpers/execKnip.js';

const cwd = resolve('fixtures/cli-preprocessor');

const exec = (command: string) => {
try {
const output = execSync(command.replace(/^knip/, 'node ../../dist/cli.js'), { cwd });
return output.toString().trim();
} catch {
console.error(`Error during execution of command: ${command}`);
}
};
const exec = execFactory(cwd);

test('knip --preprocessor ./index.js', () => {
assert.equal(exec('knip --preprocessor ./index.js'), 'hi from js preprocessor');
Expand Down
7 changes: 2 additions & 5 deletions test/cli-reporter.test.ts
@@ -1,14 +1,11 @@
import assert from 'node:assert/strict';
import { execSync } from 'node:child_process';
import test from 'node:test';
import { resolve } from '../src/util/path.js';
import { execFactory } from './helpers/execKnip.js';

const cwd = resolve('fixtures/cli-reporter');

const exec = (command: string) => {
const output = execSync(command.replace(/^knip/, 'node ../../dist/cli.js'), { cwd });
return output.toString().trim();
};
const exec = execFactory(cwd);

test('knip --reporter ./index.js', () => {
assert.equal(exec('knip --reporter ./index.js'), 'hi from js reporter');
Expand Down
7 changes: 2 additions & 5 deletions test/cli.test.ts
@@ -1,16 +1,13 @@
import assert from 'node:assert/strict';
import { execSync } from 'node:child_process';
import test from 'node:test';
import { helpText } from '../src/util/cli-arguments.js';
import { resolve } from '../src/util/path.js';
import { version } from '../src/version.js';
import { execFactory } from './helpers/execKnip.js';

const cwd = resolve('fixtures/cli');

const exec = (command: string) => {
const output = execSync(command.replace(/^knip/, 'node ../../dist/cli.js'), { cwd });
return output.toString().trim();
};
const exec = execFactory(cwd);

test('knip --version', () => {
assert.equal(exec('knip --version'), version);
Expand Down
12 changes: 12 additions & 0 deletions test/config/js-async.test.ts
@@ -0,0 +1,12 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import { resolve } from '../../src/util/path.js';
import { execFactory } from '../helpers/execKnip.js';

const cwd = resolve('fixtures/config/js-async');

const exec = execFactory(cwd);

test('Support loading js async function for configuration', async () => {
assert.equal(exec('knip'), '');
});
12 changes: 12 additions & 0 deletions test/config/js-flat.test.ts
@@ -0,0 +1,12 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import { resolve } from '../../src/util/path.js';
import { execFactory } from '../helpers/execKnip.js';

const cwd = resolve('fixtures/config/js-flat');

const exec = execFactory(cwd);

test('Support loading js object files for configuration', async () => {
assert.equal(exec('knip'), '');
});
12 changes: 12 additions & 0 deletions test/config/json.test.ts
@@ -0,0 +1,12 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import { resolve } from '../../src/util/path.js';
import { execFactory } from '../helpers/execKnip.js';

const cwd = resolve('fixtures/config/json');

const exec = execFactory(cwd);

test('Support loading json files for configuration', async () => {
assert.equal(exec('knip'), '');
});
12 changes: 12 additions & 0 deletions test/config/mjs-async.test.ts
@@ -0,0 +1,12 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import { resolve } from '../../src/util/path.js';
import { execFactory } from '../helpers/execKnip.js';

const cwd = resolve('fixtures/config/mjs-async');

const exec = execFactory(cwd);

test('Support loading mjs async function files for configuration', async () => {
assert.equal(exec('knip -c knip.mjs'), '');
});
12 changes: 12 additions & 0 deletions test/config/mjs-flat.test.ts
@@ -0,0 +1,12 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import { resolve } from '../../src/util/path.js';
import { execFactory } from '../helpers/execKnip.js';

const cwd = resolve('fixtures/config/mjs-flat');

const exec = execFactory(cwd);

test('Support loading mjs object files for configuration', async () => {
assert.equal(exec('knip -c knip.mjs'), '');
});

0 comments on commit a18f2a6

Please sign in to comment.