@lipemat/js-boilerplate
Version:
Dependencies and scripts for a no config JavaScript app
100 lines (98 loc) • 3.36 kB
JavaScript
import { existsSync, readFileSync, writeFileSync } from 'fs';
import { basename } from 'path';
import camelCase from '../helpers/camel-case.js';
/**
* Extracts CSS class names from a CSS Module into TypeScript definitions.
*
* Allows TS and PHPStorm to validate uses of CSS Modules in the codebase.
*
* Inspired by: `@teamsupercell/typings-for-css-modules-loader` but without dependencies and under our control.
*/
export default function createCssModuleTypings(content, ...args) {
if ('cacheable' in this && 'function' === typeof this.cacheable) {
this.cacheable();
}
try {
const indexOfLocals = content.indexOf('.locals');
const cssModuleKeys = -1 === indexOfLocals ? [] : getCssModuleKeys(content.substring(indexOfLocals));
if (cssModuleKeys.length > 0) {
const fileName = this.resourcePath;
const cssModuleDefinition = generateModuleTypeDefinition(cssModuleKeys, camelCase(basename(fileName), true));
const typingsPath = fileName.replace(/\.pcss$/, '.pcss.d.ts');
writeTypingsFile(typingsPath, cssModuleDefinition);
}
this.callback(null, content, ...args);
}
catch (error) {
this.emitError(error);
}
}
/**
* Formats the content of a TypeScript definition file.
*
* Turns a list of CSS module keys into a TypeScript interface definition.
*/
function generateModuleTypeDefinition(cssModuleKeys, pascalCaseFileName) {
const interfaceName = `I${pascalCaseFileName}`;
const moduleName = `${pascalCaseFileName}Module`;
const namespaceName = `${pascalCaseFileName}Namespace`;
const interfaceProperties = [...cssModuleKeys]
.sort()
.map((key) => {
if (key.includes('-')) {
return `\t\t'${key}': string;`;
}
return `\t\t${key}: string;`;
})
.join('\n');
return `// Autogenerated by typings-for-css-modules-loader.
declare namespace ${namespaceName} {
export interface I${pascalCaseFileName} {
${interfaceProperties}
}
}
declare const ${moduleName}: ${namespaceName}.${interfaceName};
export = ${moduleName};
`;
}
/**
* Write the typing file if it does not exist or if the content has changed.
*/
function writeTypingsFile(fileName, content) {
if (!existsSync(fileName)) {
writeTypingsFile.fileWriter(fileName, content);
}
else {
const existingContent = readFileSync(fileName, 'utf8');
if (existingContent !== content) {
writeTypingsFile.fileWriter(fileName, content);
}
}
}
writeTypingsFile.fileWriter = writeFileSync;
/**
* Extracts CSS module keys from the content of a CSS Module file.
*
* @param {string} content - The content of the CSS Module file.
* @return {string[]} - An array of unique CSS module keys.
*/
function getCssModuleKeys(content) {
const keyRegex = /"([^"\n]+)":/g;
let match;
const cssModuleKeys = [];
while ((match = keyRegex.exec(content))) {
if (-1 === cssModuleKeys.indexOf(match[1])) {
cssModuleKeys.push(match[1]);
}
}
return cssModuleKeys;
}
/**
* Modifier file writer used by the writeTypingsFile function.
*
* Here for unit test overrides of `writeFileSync`.
*/
export function modifyFileWriter(fn) {
writeTypingsFile.fileWriter = fn;
}
//# sourceMappingURL=css-module-types.js.map