Skip to content
Open
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
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ Instead of emitting new TypeScript declarations, this will throw an error if a g

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).

### Named Exports

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.

## With Thanks

This package borrows heavily from [typings-for-css-modules-loader](https://github.com/Jimdo/typings-for-css-modules-loader).
Expand Down
41 changes: 27 additions & 14 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const bannerMessage =
'// This file is automatically generated.\n// Please do not change this file!';

const cssModuleExport = 'export const cssExports: CssExports;\nexport default cssExports;\n';
const cssNamedModuleExport = 'export const cssExports: CssExports;\nexport = cssExports;\n';

const getNoDeclarationFileError = ({ filename }) =>
new Error(
Expand Down Expand Up @@ -64,39 +65,51 @@ const makeFileHandlers = filename => ({
fs.writeFile(filename, content, { encoding: 'utf-8' }, handler)
});

const extractLocalExports = (content) => {
let localExports = content.split('exports.locals')[1];
function* extractLocalExports(content) {
let localExports = content.split('exports.locals = {')[1];
if (!localExports) {
localExports = content.split('___CSS_LOADER_EXPORT___.locals')[1];
localExports = content.split('___CSS_LOADER_EXPORT___.locals = {')[1];
}
if (localExports) {
// // Exports
// ___CSS_LOADER_EXPORT___.locals = {
// "class": `class__file`,
const keyRegex = /"([^\\"]+)":/g;
let match;
while ((match = keyRegex.exec(localExports))) {
yield match[1];
}
} else {
// export { _1 as "class" };
const keyRegex = /export { [_a-z0-9]+ as ("[^\\"]+") }/g;
while ((match = keyRegex.exec(content))) {
yield JSON.parse(match[1]);
}
}
return localExports;
}

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

const filename = this.resourcePath;
const { mode = 'emit' } = loaderUtils.getOptions(this) || {};
const { mode = 'emit', namedExports = false } = loaderUtils.getOptions(this) || {};
if (!validModes.includes(mode)) {
return failed(new Error(`Invalid mode option: ${mode}`));
}

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

const keyRegex = /"([^\\"]+)":/g;
let match;
const cssModuleKeys = [];

const localExports = extractLocalExports(content);

while ((match = keyRegex.exec(localExports))) {
if (cssModuleKeys.indexOf(match[1]) < 0) {
cssModuleKeys.push(match[1]);
for (const key of extractLocalExports(content)) {
// Do you really need this existence check?
if (cssModuleKeys.indexOf(key) < 0) {
cssModuleKeys.push(key);
}
}

const cssModuleDefinition = `${bannerMessage}\n${cssModuleToInterface(cssModuleKeys)}\n${cssModuleExport}`;
const exportsDeclaration = namedExports ? cssNamedModuleExport : cssModuleExport;
const cssModuleDefinition = `${bannerMessage}\n${cssModuleToInterface(cssModuleKeys)}\n${exportsDeclaration}`;

if (mode === 'verify') {
read((err, fileContents) => {
Expand Down