Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/true-kiwis-kneel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@css-modules-kit/ts-plugin': minor
'@css-modules-kit/codegen': minor
'@css-modules-kit/core': minor
---

feat: support `namedExports` option
25 changes: 23 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,9 @@ In TypeScript, the `include`/`exclude` properties specify which `*.ts` files to

### `cmkOptions.dtsOutDir`

Specifies the directory where `*.d.ts` files are output. The default is `"generated"`.
Type: `string`, Default: `"generated"`

Specifies the directory where `*.d.ts` files are output.

```jsonc
{
Expand All @@ -131,7 +133,9 @@ Specifies the directory where `*.d.ts` files are output. The default is `"genera

### `cmkOptions.arbitraryExtensions`

Determines whether to generate `*.module.d.css.ts` instead of `*.module.css.d.ts`. The default is `false`.
Type: `boolean`, Default: `false`

Determines whether to generate `*.module.d.css.ts` instead of `*.module.css.d.ts`.

```jsonc
{
Expand All @@ -144,6 +148,23 @@ Determines whether to generate `*.module.d.css.ts` instead of `*.module.css.d.ts
}
```

### `cmkOptions.namedExports`

Type: `boolean`, Default: `false`

Determines whether to generate named exports in the d.ts file instead of a default export.

```jsonc
{
"compilerOptions": {
// ...
},
"cmkOptions": {
"namedExports": true,
},
}
```

## Limitations

- Sass/Less are not supported to simplify the implementation
Expand Down
19 changes: 9 additions & 10 deletions example/generated/src/a.module.css.d.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
// @ts-nocheck
declare const styles = {
a_1: '' as readonly string,
a_2: '' as readonly string,
a_2: '' as readonly string,
a_3: '' as readonly string,
...(await import('./b.module.css')).default,
c_1: (await import('./c.module.css')).default.c_1,
c_alias: (await import('./c.module.css')).default.c_2,
};
export default styles;
export var a_1: string;
export var a_2: string;
export var a_2: string;
export var a_3: string;
export * from './b.module.css';
export {
c_1,
c_2 as c_alias,
} from './c.module.css';
7 changes: 2 additions & 5 deletions example/generated/src/b.module.css.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
// @ts-nocheck
declare const styles = {
b_1: '' as readonly string,
b_2: '' as readonly string,
};
export default styles;
export var b_1: string;
export var b_2: string;
7 changes: 2 additions & 5 deletions example/generated/src/c.module.css.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
// @ts-nocheck
declare const styles = {
c_1: '' as readonly string,
c_2: '' as readonly string,
};
export default styles;
export var c_1: string;
export var c_2: string;
5 changes: 0 additions & 5 deletions example/generated/src/d.module.css.d.ts

This file was deleted.

8 changes: 1 addition & 7 deletions example/src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
import styles from './a.module.css';
import * as styles from "./a.module.css";

styles.a_1;
styles.a_2;
styles.a_3;
styles.b_1;
styles.b_2;
styles.c_1;
styles.c_alias;
3 changes: 3 additions & 0 deletions example/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,8 @@
"paths": { "@/*": ["./*"] },
"rootDirs": [".", "generated"],
"types": [] // Simplify tsserver.log
},
"cmkOptions": {
"namedExports": true
}
}
4 changes: 2 additions & 2 deletions packages/codegen/src/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,11 @@ async function parseCSSModuleByFileName(fileName: string): Promise<ParseCSSModul
*/
async function writeDtsByCSSModule(
cssModule: CSSModule,
{ dtsOutDir, basePath, arbitraryExtensions }: CMKConfig,
{ dtsOutDir, basePath, arbitraryExtensions, namedExports }: CMKConfig,
resolver: Resolver,
matchesPattern: MatchesPattern,
): Promise<void> {
const dts = createDts(cssModule, { resolver, matchesPattern });
const dts = createDts(cssModule, { resolver, matchesPattern, namedExports });
await writeDtsFile(dts.text, cssModule.fileName, {
outDir: dtsOutDir,
basePath,
Expand Down
6 changes: 5 additions & 1 deletion packages/core/src/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ describe('readTsConfigFile', () => {
"module": "esnext"
},
"cmkOptions": {
"dtsOutDir": "generated/cmk"
"dtsOutDir": "generated/cmk",
"arbitraryExtensions": false,
"namedExports": true
}
}
`,
Expand All @@ -38,6 +40,8 @@ describe('readTsConfigFile', () => {
includes: ['src'],
excludes: ['src/test'],
dtsOutDir: 'generated/cmk',
arbitraryExtensions: false,
namedExports: true,
},
compilerOptions: expect.objectContaining({
module: ts.ModuleKind.ESNext,
Expand Down
16 changes: 16 additions & 0 deletions packages/core/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ export interface CMKConfig {
excludes: string[];
dtsOutDir: string;
arbitraryExtensions: boolean;
/** Whether to generate named exports in the d.ts file instead of a default export. */
namedExports: boolean;
/**
* A root directory to resolve relative path entries in the config file to.
* This is an absolute path.
Expand Down Expand Up @@ -68,6 +70,7 @@ interface UnnormalizedRawConfig {
excludes?: string[];
dtsOutDir?: string;
arbitraryExtensions?: boolean;
namedExports?: boolean;
}

/**
Expand Down Expand Up @@ -134,6 +137,17 @@ function parseRawData(raw: unknown, tsConfigSourceFile: ts.TsConfigSourceFile):
});
}
}
if ('namedExports' in raw.cmkOptions) {
if (typeof raw.cmkOptions.namedExports === 'boolean') {
result.config.namedExports = raw.cmkOptions.namedExports;
} else {
result.diagnostics.push({
category: 'error',
text: `\`namedExports\` in ${tsConfigSourceFile.fileName} must be a boolean.`,
// MEMO: Location information can be obtained from `tsConfigSourceFile.statements`, but this is complicated and will be omitted.
});
}
}
}
return result;
}
Expand All @@ -146,6 +160,7 @@ function mergeParsedRawData(base: ParsedRawData, overrides: ParsedRawData): Pars
if (overrides.config.dtsOutDir !== undefined) result.config.dtsOutDir = overrides.config.dtsOutDir;
if (overrides.config.arbitraryExtensions !== undefined)
result.config.arbitraryExtensions = overrides.config.arbitraryExtensions;
if (overrides.config.namedExports !== undefined) result.config.namedExports = overrides.config.namedExports;
result.diagnostics.push(...overrides.diagnostics);
return result;
}
Expand Down Expand Up @@ -226,6 +241,7 @@ export function readConfigFile(project: string): CMKConfig {
excludes: (config.excludes ?? []).map((e) => join(basePath, e)),
dtsOutDir: join(basePath, config.dtsOutDir ?? 'generated'),
arbitraryExtensions: config.arbitraryExtensions ?? false,
namedExports: config.namedExports ?? false,
basePath,
configFileName,
compilerOptions,
Expand Down
81 changes: 81 additions & 0 deletions packages/core/src/dts-creator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { fakeCSSModule } from './test/css-module.js';
const options: CreateDtsOptions = {
resolver: (specifier, { request }) => join(dirname(request), specifier),
matchesPattern: () => true,
namedExports: false,
};

function fakeLoc(offset: number) {
Expand Down Expand Up @@ -377,4 +378,84 @@ describe('createDts', () => {
}
`);
});
test('creates d.ts file with named exports', () => {
expect(
createDts(
fakeCSSModule({
localTokens: [
{ name: 'local1', loc: fakeLoc(0) },
{ name: 'local2', loc: fakeLoc(1) },
],
tokenImporters: [
{ type: 'import', from: './a.module.css', fromLoc: fakeLoc(2) },
{
type: 'value',
values: [
{ name: 'imported1', loc: fakeLoc(3) },
{ name: 'imported2', loc: fakeLoc(4), localName: 'aliasedImported2', localLoc: fakeLoc(5) },
],
from: './b.module.css',
fromLoc: fakeLoc(6),
},
],
}),
{ ...options, namedExports: true },
),
).toMatchInlineSnapshot(`
{
"linkedCodeMapping": {
"generatedLengths": [
9,
],
"generatedOffsets": [
125,
],
"lengths": [
16,
],
"sourceOffsets": [
138,
],
},
"mapping": {
"generatedOffsets": [
26,
53,
83,
112,
125,
138,
163,
],
"lengths": [
6,
6,
16,
9,
9,
16,
16,
],
"sourceOffsets": [
0,
1,
1,
3,
4,
5,
5,
],
},
"text": "// @ts-nocheck
export var local1: string;
export var local2: string;
export * from './a.module.css';
export {
imported1,
imported2 as aliasedImported2,
} from './b.module.css';
",
}
`);
});
});
Loading