Skip to content

Commit 7d1577d

Browse files
authored
feat: add type declarations for configs (#347)
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.
1 parent 4189b8a commit 7d1577d

4 files changed

Lines changed: 101 additions & 1 deletion

File tree

configs.d.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
declare module 'eslint-config-ackama' {
2+
import type { Linter } from 'eslint';
3+
4+
const config: Linter.Config;
5+
6+
export = config;
7+
}
8+
9+
declare module 'eslint-config-ackama/@typescript-eslint' {
10+
import type { Linter } from 'eslint';
11+
12+
const config: Linter.Config;
13+
14+
export = config;
15+
}
16+
17+
declare module 'eslint-config-ackama/@typescript-eslint.js' {
18+
import type { Linter } from 'eslint';
19+
20+
const config: Linter.Config;
21+
22+
export = config;
23+
}
24+
25+
declare module 'eslint-config-ackama/flowtype' {
26+
import type { Linter } from 'eslint';
27+
28+
const config: Linter.Config;
29+
30+
export = config;
31+
}
32+
33+
declare module 'eslint-config-ackama/flowtype.js' {
34+
import type { Linter } from 'eslint';
35+
36+
const config: Linter.Config;
37+
38+
export = config;
39+
}
40+
41+
declare module 'eslint-config-ackama/jest' {
42+
import type { Linter } from 'eslint';
43+
44+
const config: Linter.Config;
45+
46+
export = config;
47+
}
48+
49+
declare module 'eslint-config-ackama/jest.js' {
50+
import type { Linter } from 'eslint';
51+
52+
const config: Linter.Config;
53+
54+
export = config;
55+
}
56+
57+
declare module 'eslint-config-ackama/react' {
58+
import type { Linter } from 'eslint';
59+
60+
const config: Linter.Config;
61+
62+
export = config;
63+
}
64+
65+
declare module 'eslint-config-ackama/react.js' {
66+
import type { Linter } from 'eslint';
67+
68+
const config: Linter.Config;
69+
70+
export = config;
71+
}

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,14 @@
99
},
1010
"license": "ISC",
1111
"author": "Gareth Jones",
12+
"types": "configs.d.ts",
1213
"files": [
1314
"@typescript-eslint.js",
1415
"flowtype.js",
1516
"index.js",
1617
"jest.js",
17-
"react.js"
18+
"react.js",
19+
"configs.d.ts"
1820
],
1921
"scripts": {
2022
"lint": "eslint . --ext js,ts",

test/configs.spec.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ const configFiles = fs
1313
)
1414
.map(value => value.name);
1515

16+
const typeDeclarations = fs.readFileSync('configs.d.ts', 'utf8');
17+
1618
/**
1719
* Determines the canonical package name for the given eslint `plugin`,
1820
* that can be used to install the plugin using a package manager.
@@ -64,6 +66,13 @@ describe('package.json', () => {
6466
);
6567
});
6668

69+
it('includes typescript types', () => {
70+
expect.hasAssertions();
71+
72+
expect(packageJson.types).toBe('configs.d.ts');
73+
expect(packageJson.files).toContain('configs.d.ts');
74+
});
75+
6776
describe('peer dependencies', () => {
6877
it('includes eslint and prettier as required peer dependencies', () => {
6978
expect.hasAssertions();
@@ -154,6 +163,23 @@ describe('for each config file', () => {
154163
]);
155164
});
156165

166+
it('is defined as a module in the type declarations', () => {
167+
expect.hasAssertions();
168+
169+
const moduleName =
170+
// eslint-disable-next-line jest/no-conditional-in-test
171+
configFile === 'index.js'
172+
? 'eslint-config-ackama'
173+
: `eslint-config-ackama/${configFile}`;
174+
175+
// we expect the module declared with and without the `.js` extension
176+
// to ensure support for importing in both CJS and ESM environments
177+
expect(typeDeclarations).toContain(`declare module '${moduleName}' {`);
178+
expect(typeDeclarations).toContain(
179+
`declare module '${moduleName.replace(/\.js$/u, '')}' {`
180+
);
181+
});
182+
157183
it('lists any plugins as peer dependencies', () => {
158184
expect.hasAssertions();
159185

tools/generate-configs-list.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ const determineConfigDependencies = (configName: string): string[] => {
9191
};
9292

9393
const configs = files
94+
.filter(config => config.endsWith('.js'))
9495
.sort((a, b) => (a === 'index.js' ? -1 : a.localeCompare(b)))
9596
.map(config => path.parse(config).name)
9697
.flatMap(name => [

0 commit comments

Comments
 (0)