diff --git a/README.md b/README.md index 36fc6c5..44aaf7f 100644 --- a/README.md +++ b/README.md @@ -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). diff --git a/index.js b/index.js index c1df2d3..c54d4c8 100644 --- a/index.js +++ b/index.js @@ -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( @@ -64,19 +65,34 @@ 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}`)); } @@ -84,19 +100,16 @@ module.exports = function(content, ...rest) { 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) => {