UNPKG

@lipemat/js-boilerplate

Version:

Dependencies and scripts for a no config JavaScript app

100 lines (98 loc) 3.36 kB
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