Skip to content

Commit

Permalink
feat: add type declarations for configs (#347)
Browse files Browse the repository at this point in the history
Since the flat configuration system has us doing native JavaScript
imports, TypeScript can now do proper type checking which means it's
useful to start shipping types.

Because
[TypeScript](typescript-eslint/typescript-eslint#955 (comment))
requires file names to be unique (of which the file extension is not
part of), I've introduced a single declaration file that holds the types
for each config, rather than having a `.d.ts` per config, and to ensure
maximum compatibility I've defined declarations for each configuration
both with and without the `.js` extension.
  • Loading branch information
G-Rath authored Dec 6, 2024
1 parent 4189b8a commit 7d1577d
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 1 deletion.
71 changes: 71 additions & 0 deletions configs.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
declare module 'eslint-config-ackama' {
import type { Linter } from 'eslint';

const config: Linter.Config;

export = config;
}

declare module 'eslint-config-ackama/@typescript-eslint' {
import type { Linter } from 'eslint';

const config: Linter.Config;

export = config;
}

declare module 'eslint-config-ackama/@typescript-eslint.js' {
import type { Linter } from 'eslint';

const config: Linter.Config;

export = config;
}

declare module 'eslint-config-ackama/flowtype' {
import type { Linter } from 'eslint';

const config: Linter.Config;

export = config;
}

declare module 'eslint-config-ackama/flowtype.js' {
import type { Linter } from 'eslint';

const config: Linter.Config;

export = config;
}

declare module 'eslint-config-ackama/jest' {
import type { Linter } from 'eslint';

const config: Linter.Config;

export = config;
}

declare module 'eslint-config-ackama/jest.js' {
import type { Linter } from 'eslint';

const config: Linter.Config;

export = config;
}

declare module 'eslint-config-ackama/react' {
import type { Linter } from 'eslint';

const config: Linter.Config;

export = config;
}

declare module 'eslint-config-ackama/react.js' {
import type { Linter } from 'eslint';

const config: Linter.Config;

export = config;
}
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@
},
"license": "ISC",
"author": "Gareth Jones",
"types": "configs.d.ts",
"files": [
"@typescript-eslint.js",
"flowtype.js",
"index.js",
"jest.js",
"react.js"
"react.js",
"configs.d.ts"
],
"scripts": {
"lint": "eslint . --ext js,ts",
Expand Down
26 changes: 26 additions & 0 deletions test/configs.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ const configFiles = fs
)
.map(value => value.name);

const typeDeclarations = fs.readFileSync('configs.d.ts', 'utf8');

/**
* Determines the canonical package name for the given eslint `plugin`,
* that can be used to install the plugin using a package manager.
Expand Down Expand Up @@ -64,6 +66,13 @@ describe('package.json', () => {
);
});

it('includes typescript types', () => {
expect.hasAssertions();

expect(packageJson.types).toBe('configs.d.ts');
expect(packageJson.files).toContain('configs.d.ts');
});

describe('peer dependencies', () => {
it('includes eslint and prettier as required peer dependencies', () => {
expect.hasAssertions();
Expand Down Expand Up @@ -154,6 +163,23 @@ describe('for each config file', () => {
]);
});

it('is defined as a module in the type declarations', () => {
expect.hasAssertions();

const moduleName =
// eslint-disable-next-line jest/no-conditional-in-test
configFile === 'index.js'
? 'eslint-config-ackama'
: `eslint-config-ackama/${configFile}`;

// we expect the module declared with and without the `.js` extension
// to ensure support for importing in both CJS and ESM environments
expect(typeDeclarations).toContain(`declare module '${moduleName}' {`);
expect(typeDeclarations).toContain(
`declare module '${moduleName.replace(/\.js$/u, '')}' {`
);
});

it('lists any plugins as peer dependencies', () => {
expect.hasAssertions();

Expand Down
1 change: 1 addition & 0 deletions tools/generate-configs-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ const determineConfigDependencies = (configName: string): string[] => {
};

const configs = files
.filter(config => config.endsWith('.js'))
.sort((a, b) => (a === 'index.js' ? -1 : a.localeCompare(b)))
.map(config => path.parse(config).name)
.flatMap(name => [
Expand Down

0 comments on commit 7d1577d

Please sign in to comment.