From a4537837ad60e418b6a50b1eb6560871c7593339 Mon Sep 17 00:00:00 2001 From: Alexander Madyankin Date: Fri, 4 Nov 2022 17:18:07 +0300 Subject: [PATCH] Extract pluginFactory and localConvention into separate modules --- src/index.js | 140 +--------------------------------------- src/localsConvention.js | 40 ++++++++++++ src/pluginFactory.js | 99 ++++++++++++++++++++++++++++ 3 files changed, 141 insertions(+), 138 deletions(-) create mode 100644 src/localsConvention.js create mode 100644 src/pluginFactory.js diff --git a/src/index.js b/src/index.js index 3b4b056..a89fc7a 100644 --- a/src/index.js +++ b/src/index.js @@ -1,144 +1,8 @@ -import postcss from "postcss"; -import camelCase from "lodash.camelcase"; -import unquote from "./unquote"; import { readFile, writeFile } from "fs"; import { setFileSystem } from "./fs"; - -import Parser from "./Parser"; -import FileSystemLoader from "./FileSystemLoader"; - -import saveJSON from "./saveJSON"; -import { - getDefaultPlugins, - getDefaultScopeBehaviour, - behaviours, - getScopedNameGenerator, -} from "./scoping"; - -const PLUGIN_NAME = "postcss-modules"; +import { makePlugin } from "./pluginFactory"; setFileSystem({ readFile, writeFile }); -function getLoader(opts, plugins) { - const root = typeof opts.root === "undefined" ? "/" : opts.root; - return typeof opts.Loader === "function" - ? new opts.Loader(root, plugins, opts.resolve) - : new FileSystemLoader(root, plugins, opts.resolve); -} - -function isGlobalModule(globalModules, inputFile) { - return globalModules.some((regex) => inputFile.match(regex)); -} - -function getDefaultPluginsList(opts, inputFile) { - const globalModulesList = opts.globalModulePaths || null; - const exportGlobals = opts.exportGlobals || false; - const defaultBehaviour = getDefaultScopeBehaviour(opts.scopeBehaviour); - const generateScopedName = getScopedNameGenerator(opts.generateScopedName, opts.hashPrefix); - - if (globalModulesList && isGlobalModule(globalModulesList, inputFile)) { - return getDefaultPlugins({ - behaviour: behaviours.GLOBAL, - generateScopedName, - exportGlobals, - }); - } - - return getDefaultPlugins({ - behaviour: defaultBehaviour, - generateScopedName, - exportGlobals, - }); -} - -function isOurPlugin(plugin) { - return plugin.postcssPlugin === PLUGIN_NAME; -} - -function dashesCamelCase(string) { - return string.replace(/-+(\w)/g, (_, firstLetter) => firstLetter.toUpperCase()); -} - -module.exports = (opts = {}) => { - return { - postcssPlugin: PLUGIN_NAME, - async OnceExit(css, { result }) { - const getJSON = opts.getJSON || saveJSON; - const inputFile = css.source.input.file; - const pluginList = getDefaultPluginsList(opts, inputFile); - const resultPluginIndex = result.processor.plugins.findIndex((plugin) => - isOurPlugin(plugin) - ); - if (resultPluginIndex === -1) { - throw new Error("Plugin missing from options."); - } - - const earlierPlugins = result.processor.plugins.slice(0, resultPluginIndex); - const loaderPlugins = [...earlierPlugins, ...pluginList]; - const loader = getLoader(opts, loaderPlugins); - - const fetcher = async (file, relativeTo, depTrace) => { - const unquoteFile = unquote(file); - - return loader.fetch.call(loader, unquoteFile, relativeTo, depTrace); - }; - const parser = new Parser(fetcher); - - await postcss([...pluginList, parser.plugin()]).process(css, { - from: inputFile, - }); - - const out = loader.finalSource; - if (out) css.prepend(out); - - if (opts.localsConvention) { - const isFunc = typeof opts.localsConvention === "function"; - - parser.exportTokens = Object.entries(parser.exportTokens).reduce( - (tokens, [className, value]) => { - if (isFunc) { - const convention = opts.localsConvention(className, value, inputFile); - tokens[convention] = value; - - return tokens; - } - - switch (opts.localsConvention) { - case "camelCase": - tokens[className] = value; - tokens[camelCase(className)] = value; - break; - - case "camelCaseOnly": - tokens[camelCase(className)] = value; - break; - - case "dashes": - tokens[className] = value; - tokens[dashesCamelCase(className)] = value; - break; - - case "dashesOnly": - tokens[dashesCamelCase(className)] = value; - break; - } - - return tokens; - }, - {} - ); - } - - result.messages.push({ - type: "export", - plugin: "postcss-modules", - exportTokens: parser.exportTokens, - }); - - // getJSON may return a promise - return getJSON(css.source.input.file, parser.exportTokens, result.opts.to); - }, - }; -}; - +module.exports = (opts = {}) => makePlugin(opts); module.exports.postcss = true; diff --git a/src/localsConvention.js b/src/localsConvention.js new file mode 100644 index 0000000..91c9e30 --- /dev/null +++ b/src/localsConvention.js @@ -0,0 +1,40 @@ +import camelCase from "lodash.camelcase"; + +function dashesCamelCase(string) { + return string.replace(/-+(\w)/g, (_, firstLetter) => firstLetter.toUpperCase()); +} + +export function makeLocalsConventionReducer(localsConvention, inputFile) { + const isFunc = typeof localsConvention === "function"; + + return (tokens, [className, value]) => { + if (isFunc) { + const convention = localsConvention(className, value, inputFile); + tokens[convention] = value; + + return tokens; + } + + switch (localsConvention) { + case "camelCase": + tokens[className] = value; + tokens[camelCase(className)] = value; + break; + + case "camelCaseOnly": + tokens[camelCase(className)] = value; + break; + + case "dashes": + tokens[className] = value; + tokens[dashesCamelCase(className)] = value; + break; + + case "dashesOnly": + tokens[dashesCamelCase(className)] = value; + break; + } + + return tokens; + }; +} diff --git a/src/pluginFactory.js b/src/pluginFactory.js new file mode 100644 index 0000000..0195cde --- /dev/null +++ b/src/pluginFactory.js @@ -0,0 +1,99 @@ +import postcss from "postcss"; +import unquote from "./unquote"; +import Parser from "./Parser"; +import saveJSON from "./saveJSON"; +import { makeLocalsConventionReducer } from "./localsConvention"; +import FileSystemLoader from "./FileSystemLoader"; +import { + getDefaultPlugins, + getDefaultScopeBehaviour, + behaviours, + getScopedNameGenerator, +} from "./scoping"; + +const PLUGIN_NAME = "postcss-modules"; + +function isGlobalModule(globalModules, inputFile) { + return globalModules.some((regex) => inputFile.match(regex)); +} + +function getDefaultPluginsList(opts, inputFile) { + const globalModulesList = opts.globalModulePaths || null; + const exportGlobals = opts.exportGlobals || false; + const defaultBehaviour = getDefaultScopeBehaviour(opts.scopeBehaviour); + const generateScopedName = getScopedNameGenerator(opts.generateScopedName, opts.hashPrefix); + + if (globalModulesList && isGlobalModule(globalModulesList, inputFile)) { + return getDefaultPlugins({ + behaviour: behaviours.GLOBAL, + generateScopedName, + exportGlobals, + }); + } + + return getDefaultPlugins({ + behaviour: defaultBehaviour, + generateScopedName, + exportGlobals, + }); +} + +function getLoader(opts, plugins) { + const root = typeof opts.root === "undefined" ? "/" : opts.root; + + return typeof opts.Loader === "function" + ? new opts.Loader(root, plugins, opts.resolve) + : new FileSystemLoader(root, plugins, opts.resolve); +} + +function isOurPlugin(plugin) { + return plugin.postcssPlugin === PLUGIN_NAME; +} + +export function makePlugin(opts) { + return { + postcssPlugin: PLUGIN_NAME, + async OnceExit(css, { result }) { + const getJSON = opts.getJSON || saveJSON; + const inputFile = css.source.input.file; + const pluginList = getDefaultPluginsList(opts, inputFile); + const resultPluginIndex = result.processor.plugins.findIndex((plugin) => + isOurPlugin(plugin) + ); + if (resultPluginIndex === -1) { + throw new Error("Plugin missing from options."); + } + + const earlierPlugins = result.processor.plugins.slice(0, resultPluginIndex); + const loaderPlugins = [...earlierPlugins, ...pluginList]; + const loader = getLoader(opts, loaderPlugins); + + const fetcher = async (file, relativeTo, depTrace) => { + const unquoteFile = unquote(file); + return loader.fetch.call(loader, unquoteFile, relativeTo, depTrace); + }; + const parser = new Parser(fetcher); + + await postcss([...pluginList, parser.plugin()]).process(css, { + from: inputFile, + }); + + const out = loader.finalSource; + if (out) css.prepend(out); + + if (opts.localsConvention) { + const reducer = makeLocalsConventionReducer(opts.localsConvention, inputFile); + parser.exportTokens = Object.entries(parser.exportTokens).reduce(reducer, {}); + } + + result.messages.push({ + type: "export", + plugin: "postcss-modules", + exportTokens: parser.exportTokens, + }); + + // getJSON may return a promise + return getJSON(css.source.input.file, parser.exportTokens, result.opts.to); + }, + }; +}