stylebot-harmony/scripts/plugins/esbuild-css-modules.ts
2023-08-03 20:09:32 -04:00

93 lines
2.3 KiB
TypeScript

import { relative, resolve } from "path"
import { promises as fs } from "fs"
import type { Plugin } from "esbuild"
import postcss from "postcss"
// @ts-expect-error
import postcssSass from "@csstools/postcss-sass"
import cssModules from "postcss-modules"
type Options = Parameters<typeof cssModules>[0]
const PLUGIN_NAME = "esbuild-css-modules"
async function buildCSSModule(cssFullPath: string, options: Options) {
options = {
localsConvention: "camelCaseOnly",
...options,
}
const source = await fs.readFile(cssFullPath)
let classNames = {}
const { css } = await postcss([
postcssSass(),
cssModules({
getJSON(_, json) {
classNames = json
return classNames
},
...options,
}),
]).process(source, {
from: cssFullPath,
map: false,
})
return {
css,
classNames,
}
}
const srcDir = resolve(__dirname, "../../src")
const plugin = (options: Options = {}): Plugin => ({
name: PLUGIN_NAME,
async setup(build) {
const memfs = new Map<string, string>()
const FS_NAMESPACE = PLUGIN_NAME + "-fs"
build.onResolve({ filter: /\.modules?\.s?css$/, namespace: "file" }, async args => {
const res = await build.resolve(args.path, {
kind: "import-statement",
resolveDir: args.resolveDir,
})
// This is just the unique ID for this CSS module. We use a relative path to make it easier to debug.
const path = relative(srcDir, res.path)
return {
path,
namespace: PLUGIN_NAME,
pluginData: {
realPath: res.path,
},
}
})
build.onResolve({ filter: /^@css-modules\/.*/ }, ({ path }) => ({
path,
namespace: FS_NAMESPACE,
}))
build.onLoad({ filter: /./, namespace: FS_NAMESPACE }, ({ path }) => ({
contents: memfs.get(path)!,
loader: "css",
}))
build.onLoad({ filter: /./, namespace: PLUGIN_NAME }, async args => {
const tmpFilePath = "@css-modules/" + args.path
const { classNames, css } = await buildCSSModule(args.pluginData.realPath, options)
memfs.set(tmpFilePath, css)
return {
contents:
`import ${JSON.stringify(tmpFilePath)};\n` +
`module.exports = ${JSON.stringify(classNames)};`,
loader: "js",
}
})
},
})
export { plugin as cssModules }