Skip to content

Commit

Permalink
feat: named export for locals (#1108)
Browse files Browse the repository at this point in the history
  • Loading branch information
cap-Bernardito committed Jul 16, 2020
1 parent cb80db0 commit d139ec1
Show file tree
Hide file tree
Showing 19 changed files with 435 additions and 31 deletions.
55 changes: 54 additions & 1 deletion README.md
Expand Up @@ -534,6 +534,7 @@ module.exports = {
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,58 @@ module.exports = {
};
```

##### `namedExport`

Type: `Boolean`
Default: `false`

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

> i It is not allowed to use JavaScript reserved words in css class names
**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
25 changes: 20 additions & 5 deletions src/index.js
Expand Up @@ -41,11 +41,22 @@ 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 (modulesOptions.namedExport === true && esModule === false) {
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 +188,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
4 changes: 4 additions & 0 deletions src/options.json
Expand Up @@ -100,6 +100,10 @@
"instanceof": "Function"
}
]
},
"namedExport": {
"description": "Use the named export ES modules.",
"type": "boolean"
}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/plugins/postcss-icss-parser.js
Expand Up @@ -83,6 +83,7 @@ export default postcss.plugin(
value: {
// 'CSS_LOADER_ICSS_IMPORT'
order: 0,
icss: true,
importName,
url: options.urlHandler(resolvedUrl),
index: currentIndex,
Expand Down
48 changes: 40 additions & 8 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 @@ -264,7 +265,13 @@ function getPreRequester({ loaders, loaderIndex }) {
};
}

function getImportCode(loaderContext, exportType, imports, esModule) {
function getImportCode(
loaderContext,
exportType,
imports,
esModule,
modulesOptions
) {
let code = '';

if (exportType === 'full') {
Expand All @@ -279,10 +286,12 @@ function getImportCode(loaderContext, exportType, imports, esModule) {
}

for (const item of imports) {
const { importName, url } = item;
const { importName, url, icss } = item;

code += esModule
? `import ${importName} from ${url};\n`
? icss && modulesOptions.namedExport
? `import ${importName}, * as ${importName}_NAMED___ from ${url};\n`
: `import ${importName} from ${url};\n`
: `var ${importName} = require(${url});\n`;
}

Expand All @@ -296,7 +305,8 @@ function getModuleCode(
apiImports,
urlReplacements,
icssReplacements,
esModule
esModule,
modulesOptions
) {
if (exportType !== 'full') {
return 'var ___CSS_LOADER_EXPORT___ = {};\n';
Expand Down Expand Up @@ -345,9 +355,12 @@ function getModuleCode(
for (const replacement of icssReplacements) {
const { replacementName, importName, localName } = replacement;

code = code.replace(
new RegExp(replacementName, 'g'),
() => `" + ${importName}.locals[${JSON.stringify(localName)}] + "`
code = code.replace(new RegExp(replacementName, 'g'), () =>
modulesOptions.namedExport
? `" + ${importName}_NAMED___[${JSON.stringify(
camelCase(localName)
)}] + "`
: `" + ${importName}.locals[${JSON.stringify(localName)}] + "`
);
}

Expand All @@ -369,13 +382,20 @@ function getExportCode(
) {
let code = '';
let localsCode = '';
let namedCode = '';

const addExportToLocalsCode = (name, value) => {
if (localsCode) {
localsCode += `,\n`;
}

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

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

for (const { name, value } of exports) {
Expand Down Expand Up @@ -422,10 +442,22 @@ function getExportCode(
new RegExp(replacementName, 'g'),
() => `" + ${importName}.locals[${JSON.stringify(localName)}] + "`
);

if (modulesOptions.namedExport) {
namedCode = namedCode.replace(
new RegExp(replacementName, 'g'),
() =>
`" + ${importName}_NAMED___[${JSON.stringify(
camelCase(localName)
)}] + "`
);
}
}

if (localsCode) {
code += `___CSS_LOADER_EXPORT___.locals = {\n${localsCode}\n};\n`;
code += namedCode
? `${namedCode}\n`
: `___CSS_LOADER_EXPORT___.locals = {\n${localsCode}\n};\n`;
}

code += `${
Expand Down

0 comments on commit d139ec1

Please sign in to comment.