css-module-flow-loader
Version:
css module flow loader
96 lines (68 loc) • 2.78 kB
JavaScript
// @flow
/* :: import {type NodeSassRenderCallbackResultType} from 'node-sass'; */
const path = require('path');
const fileSystem = require('fs');
const readDirRecursive = require('recursive-readdir');
const nodeSass = require('node-sass');
const fileExtensionList = new Set(['.css', '.scss', '.sass']);
const excludeFolderList = new Set(['node_modules', '.git']);
type FileStatsType = {
isDirectory: () => boolean,
};
function fileExclude(pathToFile: string, stats: FileStatsType): boolean {
const filePathChunkList = new Set(pathToFile.split(path.sep));
// eslint-disable-next-line no-loops/no-loops
for (const excludeFolder of excludeFolderList) {
if (filePathChunkList.has(excludeFolder)) {
return true;
}
}
if (stats.isDirectory()) {
return false;
}
const extname = path.extname(pathToFile);
return !fileExtensionList.has(extname);
}
const classListReplaceValue = '{{class-list}}';
const templateWrapper = `// @flow strict
/* This file is automatically generated by css-module-flow-loader */
declare module.exports: {|
${classListReplaceValue}
|};
`;
function rawClassNameToFlowProperty(rawClassName: string): string {
return ` +'${rawClassName.slice(1)}': string;`;
}
function getFlowTypeFileContent(classNameList: Array<string>): string {
const allClassNameList = classNameList.map(rawClassNameToFlowProperty);
return templateWrapper.replace(classListReplaceValue, allClassNameList.join('\n'));
}
const removeCssCommentRegExpGlobal = /\/\*[\S\s]*?\*\//g;
const removeCssRuleRegExpGlobal = /{[\S\s]*?}/g;
function getCleanCssText(cssText: string): string {
return cssText.replace(removeCssCommentRegExpGlobal, '').replace(removeCssRuleRegExpGlobal, '');
}
function renderNodeSassCallback(sassRenderError: ?Error, result: ?NodeSassRenderCallbackResultType) {
if (sassRenderError || !result) {
throw sassRenderError;
}
const cleanCssText = getCleanCssText(result.css.toString());
const classNameList = [...new Set(cleanCssText.match(/\.[_a-z]+[\w-_]*/g) || [])].sort();
function fileWriteCallback(fileWriteError: ?Error) {
if (fileWriteError) {
throw fileWriteError;
}
}
fileSystem.writeFile(result.stats.entry + '.flow', getFlowTypeFileContent(classNameList), fileWriteCallback);
}
function writeFlowType(pathToFile: string) {
nodeSass.render({file: pathToFile}, renderNodeSassCallback);
}
module.exports = function cssModuleFlowLoader(source: string): string {
const rootPathFolder = this.rootContext;
(async () => {
const filePathList = await readDirRecursive(rootPathFolder, [fileExclude]);
filePathList.forEach(writeFlowType);
})();
return source;
};