Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: esModule export named #1108

Merged
merged 2 commits into from Jul 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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