Skip to content

Commit

Permalink
feat: namedExport
Browse files Browse the repository at this point in the history
  • Loading branch information
cap-Bernardito committed Jul 16, 2020
1 parent fabc58a commit a071af9
Show file tree
Hide file tree
Showing 15 changed files with 191 additions and 104 deletions.
105 changes: 53 additions & 52 deletions README.md
Expand Up @@ -118,7 +118,6 @@ module.exports = {
| **[`importLoaders`](#importloaders)** | `{Number}` | `0` | Enables/Disables or setups number of loaders applied before CSS loader |
| **[`onlyLocals`](#onlylocals)** | `{Boolean}` | `false` | Export only locals |
| **[`esModule`](#esmodule)** | `{Boolean}` | `false` | Use ES modules syntax |
| **[`namedExport`](#namedExport)** | `{Boolean}` | `false` | Use ES modules named export |

### `url`

Expand Down Expand Up @@ -532,8 +531,10 @@ module.exports = {
mode: 'local',
exportGlobals: true,
localIdentName: '[path][name]__[local]--[hash:base64:5]',
localsConvention: 'camelCase',
context: path.resolve(__dirname, 'src'),
hashPrefix: 'my-custom-hash',
namedExport: true,
},
},
},
Expand Down Expand Up @@ -756,7 +757,7 @@ module.exports = {
};
```

### `localsConvention`
##### `localsConvention`

Type: `String`
Default: `'asIs'`
Expand Down Expand Up @@ -915,6 +916,56 @@ module.exports = {
};
```

##### `namedExport`

Type: `Boolean`
Default: `false`

Enable/disable ES modules named export for css classes.
Names of exported classes are converted to camelCase.

**styles.css**

```css
.foo-baz {
color: red;
}
.bar {
color: blue;
}
```

**index.js**

```js
import { fooBaz, bar } from './styles.css';

console.log(fooBaz, bar);
```

You can enable a ES module named export using:

**webpack.config.js**

```js
module.exports = {
module: {
rules: [
{
test: /\.css$/i,
loader: 'css-loader',
options: {
esModule: true,
modules: {
namedExport: true,
},
},
},
],
},
};
```

### `sourceMap`

Type: `Boolean`
Expand Down Expand Up @@ -1036,56 +1087,6 @@ module.exports = {
};
```

### `namedExport`

Type: `Boolean`
Default: `false`

Enable/disable ES modules named export for css classes.
Names of exported classes are converted to camelCase.

**styles.css**

```css
.foo-baz {
color: red;
}
.bar {
color: blue;
}
```

**index.js**

```js
import { fooBaz, bar } from './styles.css';

console.log(fooBaz, bar);
```

You can enable a ES module named export using:

**webpack.config.js**

```js
module.exports = {
module: {
rules: [
{
test: /\.css$/i,
loader: 'css-loader',
options: {
esModule: true,
modules: {
namedExport: true,
},
},
},
],
},
};
```

## Examples

### Assets
Expand Down
28 changes: 23 additions & 5 deletions src/index.js
Expand Up @@ -41,11 +41,25 @@ export default function loader(content, map, meta) {
const urlHandler = (url) =>
stringifyRequest(this, preRequester(options.importLoaders) + url);

const esModule =
typeof options.esModule !== 'undefined' ? options.esModule : false;

let modulesOptions;

if (shouldUseModulesPlugins(options.modules, this.resourcePath)) {
modulesOptions = getModulesOptions(options, this);

if (
Boolean(modulesOptions.namedExport) &&
Boolean(modulesOptions.namedExport) !== Boolean(esModule)
) {
this.emitError(
new Error(
'`Options.module.namedExport` cannot be used without `options.esModule`'
)
);
}

plugins.push(...getModulesPlugins(modulesOptions, this));

const icssResolver = this.getResolve({
Expand Down Expand Up @@ -177,18 +191,22 @@ export default function loader(content, map, meta) {
);
});

const esModule =
typeof options.esModule !== 'undefined' ? options.esModule : false;

const importCode = getImportCode(this, exportType, imports, esModule);
const importCode = getImportCode(
this,
exportType,
imports,
esModule,
modulesOptions
);
const moduleCode = getModuleCode(
result,
exportType,
sourceMap,
apiImports,
urlReplacements,
icssReplacements,
esModule
esModule,
modulesOptions
);
const exportCode = getExportCode(
exports,
Expand Down
20 changes: 11 additions & 9 deletions src/utils.js
Expand Up @@ -146,6 +146,7 @@ function getModulesOptions(options, loaderContext) {
getLocalIdent,
hashPrefix: '',
exportGlobals: false,
namedExport: false,
};

if (
Expand Down Expand Up @@ -269,7 +270,7 @@ function getImportCode(
exportType,
imports,
esModule,
namedExport
modulesOptions
) {
let code = '';

Expand All @@ -288,7 +289,7 @@ function getImportCode(
const { importName, url, icss } = item;

code += esModule
? icss && namedExport
? icss && modulesOptions.namedExport
? `import ${importName}, * as ${importName}_NAMED___ from ${url};\n`
: `import ${importName} from ${url};\n`
: `var ${importName} = require(${url});\n`;
Expand All @@ -305,7 +306,7 @@ function getModuleCode(
urlReplacements,
icssReplacements,
esModule,
namedExport
modulesOptions
) {
if (exportType !== 'full') {
return 'var ___CSS_LOADER_EXPORT___ = {};\n';
Expand Down Expand Up @@ -355,7 +356,7 @@ function getModuleCode(
const { replacementName, importName, localName } = replacement;

code = code.replace(new RegExp(replacementName, 'g'), () =>
namedExport
modulesOptions.namedExport
? `" + ${importName}_NAMED___[${JSON.stringify(
camelCase(localName)
)}] + "`
Expand All @@ -377,8 +378,7 @@ function getExportCode(
exportType,
icssReplacements,
esModule,
modulesOptions,
namedExport
modulesOptions
) {
let code = '';
let localsCode = '';
Expand All @@ -391,8 +391,10 @@ function getExportCode(

localsCode += `\t${JSON.stringify(name)}: ${JSON.stringify(value)}`;

if (namedExport) {
namedCode += `export const ${name} = ${JSON.stringify(value)};\n`;
if (modulesOptions.namedExport) {
namedCode += `export const ${camelCase(name)} = ${JSON.stringify(
value
)};\n`;
}
};

Expand Down Expand Up @@ -441,7 +443,7 @@ function getExportCode(
() => `" + ${importName}.locals[${JSON.stringify(localName)}] + "`
);

if (namedExport) {
if (modulesOptions.namedExport) {
namedCode = namedCode.replace(
new RegExp(replacementName, 'g'),
() =>
Expand Down
38 changes: 19 additions & 19 deletions test/__snapshots__/esModule-option.test.js.snap
Expand Up @@ -9,16 +9,16 @@ File was processed with these loaders:",

exports[`"esModule" option should emit error when class has unsupported name: warnings 1`] = `Array []`;

exports[`"esModule" option should emit error when exportNamed true && esModule false: errors 1`] = `
exports[`"esModule" option should emit error when namedExport true && esModule false: errors 1`] = `
Array [
"ModuleError: Module Error (from \`replaced original path\`):
\`Options.module.namedExport\` cannot be used without \`options.esModule\`",
]
`;

exports[`"esModule" option should work js template with "exportNamed" option: errors 1`] = `Array []`;
exports[`"esModule" option should work js template with "namedExport" option: errors 1`] = `Array []`;

exports[`"esModule" option should work js template with "exportNamed" option: module 1`] = `
exports[`"esModule" option should work js template with "namedExport" option: module 1`] = `
"// Imports
import ___CSS_LOADER_API_IMPORT___ from \\"../../../../../src/runtime/api.js\\";
var ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(false);
Expand All @@ -33,7 +33,7 @@ export default ___CSS_LOADER_EXPORT___;
"
`;

exports[`"esModule" option should work js template with "exportNamed" option: result 1`] = `
exports[`"esModule" option should work js template with "namedExport" option: result 1`] = `
Object {
"css": Array [
Array [
Expand Down Expand Up @@ -61,7 +61,7 @@ Object {
}
`;
exports[`"esModule" option should work js template with "exportNamed" option: warnings 1`] = `Array []`;
exports[`"esModule" option should work js template with "namedExport" option: warnings 1`] = `Array []`;
exports[`"esModule" option should work when not specified: errors 1`] = `Array []`;
Expand Down Expand Up @@ -109,25 +109,25 @@ Array [
exports[`"esModule" option should work when not specified: warnings 1`] = `Array []`;
exports[`"esModule" option should work with "exportNamed" option with nested import: errors 1`] = `Array []`;
exports[`"esModule" option should work with "namedExport" option with nested import: errors 1`] = `Array []`;
exports[`"esModule" option should work with "exportNamed" option with nested import: module 1`] = `
exports[`"esModule" option should work with "namedExport" option with nested import: module 1`] = `
"// Imports
import ___CSS_LOADER_API_IMPORT___ from \\"../../../../../src/runtime/api.js\\";
import ___CSS_LOADER_ICSS_IMPORT_0___, * as ___CSS_LOADER_ICSS_IMPORT_0____NAMED___ from \\"-!../../../../../src/index.js??[ident]!../../../modules/composes/values.css\\";
var ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(false);
___CSS_LOADER_EXPORT___.i(___CSS_LOADER_ICSS_IMPORT_0___, \\"\\", true);
// Module
___CSS_LOADER_EXPORT___.push([module.id, \\".qwCT06AE1ZDvQtiz0EQJ8 {\\\\n color: \\" + ___CSS_LOADER_ICSS_IMPORT_0____NAMED___[\\"vDef\\"] + \\";\\\\n}\\\\n\\", \\"\\"]);
___CSS_LOADER_EXPORT___.push([module.id, \\".lOpALu8t_iv2-GccTMbIq {\\\\n color: \\" + ___CSS_LOADER_ICSS_IMPORT_0____NAMED___[\\"vDef\\"] + \\";\\\\n}\\\\n\\", \\"\\"]);
// Exports
export const vDef = \\"\\" + ___CSS_LOADER_ICSS_IMPORT_0____NAMED___[\\"vDef\\"] + \\"\\";
export const ghi = \\"qwCT06AE1ZDvQtiz0EQJ8\\";
export const ghi = \\"lOpALu8t_iv2-GccTMbIq\\";
export default ___CSS_LOADER_EXPORT___;
"
`;
exports[`"esModule" option should work with "exportNamed" option with nested import: result 1`] = `
exports[`"esModule" option should work with "namedExport" option with nested import: result 1`] = `
Array [
Array [
"../../src/index.js?[ident]!./modules/composes/values.css",
Expand All @@ -137,7 +137,7 @@ Array [
],
Array [
"./es-module/named/nested/index.css",
".qwCT06AE1ZDvQtiz0EQJ8 {
".lOpALu8t_iv2-GccTMbIq {
color: red;
}
",
Expand All @@ -146,28 +146,28 @@ Array [
]
`;
exports[`"esModule" option should work with "exportNamed" option with nested import: warnings 1`] = `Array []`;
exports[`"esModule" option should work with "namedExport" option with nested import: warnings 1`] = `Array []`;
exports[`"esModule" option should work with "exportNamed" option: errors 1`] = `Array []`;
exports[`"esModule" option should work with "namedExport" option: errors 1`] = `Array []`;
exports[`"esModule" option should work with "exportNamed" option: module 1`] = `
exports[`"esModule" option should work with "namedExport" option: module 1`] = `
"// Imports
import ___CSS_LOADER_API_IMPORT___ from \\"../../../../../src/runtime/api.js\\";
var ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(false);
// Module
___CSS_LOADER_EXPORT___.push([module.id, \\"._1Cb_30DnkF22nebwtzVBFY {\\\\n color: red;\\\\n}\\\\n\\\\n.bar {\\\\n color: red;\\\\n}\\\\n\\", \\"\\"]);
___CSS_LOADER_EXPORT___.push([module.id, \\".jSf5EjnYI1bvqKHBrOPz6 {\\\\n color: red;\\\\n}\\\\n\\\\n.bar {\\\\n color: red;\\\\n}\\\\n\\", \\"\\"]);
// Exports
export const barBaz = \\"_1Cb_30DnkF22nebwtzVBFY\\";
export const barBaz = \\"jSf5EjnYI1bvqKHBrOPz6\\";
export default ___CSS_LOADER_EXPORT___;
"
`;
exports[`"esModule" option should work with "exportNamed" option: result 1`] = `
exports[`"esModule" option should work with "namedExport" option: result 1`] = `
Array [
Array [
"./es-module/named/base/index.css",
"._1Cb_30DnkF22nebwtzVBFY {
".jSf5EjnYI1bvqKHBrOPz6 {
color: red;
}
Expand All @@ -180,7 +180,7 @@ Array [
]
`;
exports[`"esModule" option should work with "exportNamed" option: warnings 1`] = `Array []`;
exports[`"esModule" option should work with "namedExport" option: warnings 1`] = `Array []`;
exports[`"esModule" option should work with a value equal to "false": errors 1`] = `Array []`;
Expand Down

0 comments on commit a071af9

Please sign in to comment.