Skip to content

Commit 525cd6d

Browse files
committed
feat: Support named exports
BREAKING CHANGE: Updates the declaration template. This is a followup on seek-oss#38 The `exports =` declaration was added in e7342df but removed in 908d491 due some issue in babel which I can't reproduce. Maybe that has been fixed downstream in the meantime. Due to microsoft/TypeScript#40594 we cannot export these names directly since class names might not be valid JavaScript identifiers, even though they are valid exported names. When that TypeScript bug is resolved this can be changed to export the names directly instead of using `export =`. The problem with `export =` is that it will let you do `import * as css from ...` in addition to `import css from ...` even though only `import *` will work.
1 parent bb4892a commit 525cd6d

File tree

2 files changed

+31
-14
lines changed

2 files changed

+31
-14
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ Instead of emitting new TypeScript declarations, this will throw an error if a g
5858

5959
This workflow is similar to using the [Prettier](https://github.com/prettier/prettier) [`--list-different` option](https://prettier.io/docs/en/cli.html#list-different).
6060

61+
### Named Exports
62+
63+
If using the `namedExports` option of `css-loader` then you can enable the same option in this loader. This can improve tree shaking and reduce bundled JavaScript size by dropping the original class names.
64+
6165
## With Thanks
6266

6367
This package borrows heavily from [typings-for-css-modules-loader](https://github.com/Jimdo/typings-for-css-modules-loader).

index.js

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const bannerMessage =
77
'// This file is automatically generated.\n// Please do not change this file!';
88

99
const cssModuleExport = 'export const cssExports: CssExports;\nexport default cssExports;\n';
10+
const cssNamedModuleExport = 'export const cssExports: CssExports;\nexport = cssExports;\n';
1011

1112
const getNoDeclarationFileError = ({ filename }) =>
1213
new Error(
@@ -64,39 +65,51 @@ const makeFileHandlers = filename => ({
6465
fs.writeFile(filename, content, { encoding: 'utf-8' }, handler)
6566
});
6667

67-
const extractLocalExports = (content) => {
68-
let localExports = content.split('exports.locals')[1];
68+
function* extractLocalExports(content) {
69+
let localExports = content.split('exports.locals = {')[1];
6970
if (!localExports) {
70-
localExports = content.split('___CSS_LOADER_EXPORT___.locals')[1];
71+
localExports = content.split('___CSS_LOADER_EXPORT___.locals = {')[1];
72+
}
73+
if (localExports) {
74+
// // Exports
75+
// ___CSS_LOADER_EXPORT___.locals = {
76+
// "class": `class__file`,
77+
const keyRegex = /"([^\\"]+)":/g;
78+
let match;
79+
while ((match = keyRegex.exec(localExports))) {
80+
yield match[1];
81+
}
82+
} else {
83+
// export { _1 as "class" };
84+
const keyRegex = /export { [_a-z0-9]+ as ("[^\\"]+") }/g;
85+
while ((match = keyRegex.exec(content))) {
86+
yield JSON.parse(match[1]);
87+
}
7188
}
72-
return localExports;
7389
}
7490

7591
module.exports = function(content, ...rest) {
7692
const { failed, success } = makeDoneHandlers(this.async(), content, rest);
7793

7894
const filename = this.resourcePath;
79-
const { mode = 'emit' } = loaderUtils.getOptions(this) || {};
95+
const { mode = 'emit', namedExports = false } = loaderUtils.getOptions(this) || {};
8096
if (!validModes.includes(mode)) {
8197
return failed(new Error(`Invalid mode option: ${mode}`));
8298
}
8399

84100
const cssModuleInterfaceFilename = filenameToTypingsFilename(filename);
85101
const { read, write } = makeFileHandlers(cssModuleInterfaceFilename);
86102

87-
const keyRegex = /"([^\\"]+)":/g;
88-
let match;
89103
const cssModuleKeys = [];
90-
91-
const localExports = extractLocalExports(content);
92-
93-
while ((match = keyRegex.exec(localExports))) {
94-
if (cssModuleKeys.indexOf(match[1]) < 0) {
95-
cssModuleKeys.push(match[1]);
104+
for (const key of extractLocalExports(content)) {
105+
// Do you really need this existence check?
106+
if (cssModuleKeys.indexOf(key) < 0) {
107+
cssModuleKeys.push(key);
96108
}
97109
}
98110

99-
const cssModuleDefinition = `${bannerMessage}\n${cssModuleToInterface(cssModuleKeys)}\n${cssModuleExport}`;
111+
const exportsDeclaration = namedExports ? cssNamedModuleExport : cssModuleExport;
112+
const cssModuleDefinition = `${bannerMessage}\n${cssModuleToInterface(cssModuleKeys)}\n${exportsDeclaration}`;
100113

101114
if (mode === 'verify') {
102115
read((err, fileContents) => {

0 commit comments

Comments
 (0)