@dr.pogodin/babel-plugin-react-css-modules
Version:
Transforms styleName to className using compile time CSS module resolution.
131 lines (126 loc) • 5.23 kB
JavaScript
/* global console, process */
// TODO: Flow-bin has issues with using "node:" prefix.
// eslint-disable-next-line import/enforce-node-protocol-usage
import { readFileSync } from 'fs';
// TODO: Flow-bin has issues with using "node:" prefix.
// eslint-disable-next-line import/enforce-node-protocol-usage
import { createRequire } from 'module';
// TODO: Flow-bin has issues with using "node:" prefix.
// eslint-disable-next-line import/enforce-node-protocol-usage
import { dirname, resolve } from 'path';
import postcss from 'postcss';
import ExtractImports from 'postcss-modules-extract-imports';
import LocalByDefault from 'postcss-modules-local-by-default';
import newScopePlugin from 'postcss-modules-scope';
import Values from 'postcss-modules-values';
import parser from '@dr.pogodin/postcss-modules-parser';
import getLocalIdent, { unescape } from "./getLocalIdent.js";
import optionsDefaults from "./schemas/optionsDefaults.js";
// $FlowFixMe
const require = createRequire(import.meta.url);
const getFiletypeOptions = (cssSourceFilePath, filetypes) => {
const extension = cssSourceFilePath.slice(cssSourceFilePath.lastIndexOf('.'));
const filetype = filetypes ? filetypes[extension] : null;
return filetype;
};
const getSyntax = filetypeOptions => {
if (!filetypeOptions || !filetypeOptions.syntax) {
return null;
}
// eslint-disable-next-line import/no-dynamic-require
return require(filetypeOptions.syntax);
};
const getExtraPlugins = filetypeOptions => {
if (!filetypeOptions || !filetypeOptions.plugins) {
return [];
}
return filetypeOptions.plugins.map(plugin => {
if (Array.isArray(plugin)) {
const [pluginName, pluginOptions] = plugin;
// $FlowFixMe
return require(pluginName)(pluginOptions); // eslint-disable-line import/no-dynamic-require
}
// eslint-disable-next-line import/no-dynamic-require
return require(plugin);
});
};
const getTokens = (extraPluginsRunner, runner, cssSourceFilePath, filetypeOptions, pluginOptions) => {
const options = {
from: cssSourceFilePath
};
if (filetypeOptions) {
options.syntax = getSyntax(filetypeOptions);
}
let res = readFileSync(cssSourceFilePath, 'utf-8');
if (pluginOptions.transform) {
res = pluginOptions.transform(res, cssSourceFilePath, pluginOptions);
}
if (extraPluginsRunner) {
res = extraPluginsRunner.process(res, options);
}
res = runner.process(res, options);
res.warnings().forEach(message => {
// eslint-disable-next-line no-console
console.warn(message.text);
});
return res.root.tokens;
};
export default (cssSourceFilePath, options) => {
// eslint-disable-next-line prefer-const
let runner;
let generateScopedName;
if (options.generateScopedName && typeof options.generateScopedName === 'function') {
({
generateScopedName
} = options);
} else {
generateScopedName = (clazz, resourcePath) => getLocalIdent(
// TODO: The loader context used by "css-loader" may has additional
// stuff inside this argument (loader context), allowing for some edge
// cases (though, presumably not with a typical configurations)
// we don't support (yet?).
{
resourcePath
}, options.generateScopedName || optionsDefaults.generateScopedName, unescape(clazz), {
clazz,
context: options.context || process.cwd(),
// TODO: These options should match their counterparts in Webpack
// configuration:
// - https://webpack.js.org/configuration/output/#outputhashdigest
// - https://webpack.js.org/configuration/output/#outputhashdigestlength
// - https://webpack.js.org/configuration/output/#outputhashfunction
// - https://webpack.js.org/configuration/output/#outputhashsalt
// and they should be exposed as babel-plugin-react-css-modules
// options. However, for now they are just hardcoded equal to
// the Webpack's default settings.
hashDigest: 'hex',
hashDigestLength: 20,
hashFunction: 'md4',
hashSalt: '',
// TODO: This option was introduced by css-loader@6.6.0.
// To keep getLocalIdent() in sync with css-loader implementation,
// I updated the code there, but similar to the parameters above,
// it is not yet exposed as this plugin's option.
hashStrategy: 'resource-path-and-local-name',
// TODO: This one allows for some path modifications during
// the transform. Probably, not a Webpack param.
regExp: ''
});
}
const filetypeOptions = getFiletypeOptions(cssSourceFilePath, options.filetypes);
const extraPlugins = getExtraPlugins(filetypeOptions);
const extraPluginsRunner = extraPlugins.length && postcss(extraPlugins);
const fetch = (to, from) => {
const fromDirectoryPath = dirname(from);
const toPath = resolve(fromDirectoryPath, to);
return getTokens(extraPluginsRunner, runner, toPath, filetypeOptions, options);
};
const plugins = [Values, LocalByDefault, ExtractImports, newScopePlugin({
generateScopedName
}), parser({
fetch
})];
runner = postcss(plugins);
return getTokens(extraPluginsRunner, runner, cssSourceFilePath, filetypeOptions, options);
};
//# sourceMappingURL=requireCssModule.js.map