UNPKG

esbuild-style-plugin

Version:

Another esbuild plugin for your styling with CSS,SASS,LESS,STYLUS

211 lines (208 loc) 8.21 kB
var __getOwnPropNames = Object.getOwnPropertyNames; var __esm = (fn, res) => function __init() { return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res; }; var __commonJS = (cb, mod) => function __require() { return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; }; // src/utils.ts import { TextDecoder } from "util"; import path from "path"; import fs from "fs"; import { globSync } from "glob"; var getModule, renderStylus, renderStyle, importPostcssConfigFile, getPostCSSWatchFiles; var init_utils = __esm({ "src/utils.ts"() { getModule = async (moduleName, checkFunc) => { try { const module = await import(moduleName); if (typeof module[checkFunc] === `function`) return module; if (typeof module.default[checkFunc] === `function`) return module.default; throw new Error(`Func '${checkFunc}' not found for module '${moduleName}'`); } catch (e) { if (e.code === "MODULE_NOT_FOUND") { throw new Error(`Missing module. Please install '${moduleName}' package.`); } else { throw e; } } }; renderStylus = async (css, options) => { const stylus = await getModule("stylus", "render"); return new Promise((resolve, reject) => { stylus.render(css, options, (err, css2) => { if (err) reject(err); resolve(css2); }); }); }; renderStyle = async (filePath, options = {}) => { const { ext } = path.parse(filePath); if (ext === ".css") { return (await fs.promises.readFile(filePath)).toString(); } if (ext === ".sass" || ext === ".scss") { const sassOptions = options.sassOptions || {}; const sass = await getModule("sass", "renderSync"); return sass.renderSync({ ...sassOptions, file: filePath }).css.toString(); } if (ext === ".styl") { const stylusOptions = options.stylusOptions || {}; const source = await fs.promises.readFile(filePath); return await renderStylus(new TextDecoder().decode(source), { ...stylusOptions, filename: filePath }); } if (ext === ".less") { const lestOptions = options.lessOptions || {}; const source = await fs.promises.readFile(filePath); const less = await getModule("less", "render"); return (await less.render(new TextDecoder().decode(source), { ...lestOptions, filename: filePath })).css; } throw new Error(`Can't render this style '${ext}'.`); }; importPostcssConfigFile = async (configFilePath) => { let _configFilePath = configFilePath === true ? path.resolve(process.cwd(), "postcss.config.js") : configFilePath; try { const imported = await import(_configFilePath); if (!imported.default) throw new Error(`Missing default import .`); const config = imported.default; if (!config.plugins) throw new Error(`Missing plugins [array].`); return config; } catch (err) { console.error(err); throw new Error(`PostCSS config file at ${_configFilePath} can't load.`); } }; getPostCSSWatchFiles = (result) => { let watchFiles = []; const { messages } = result; for (const message of messages) { const { type } = message; if (type === "dependency") { watchFiles.push(message.file); } else if (type === "dir-dependency") { if (!message.dir) continue; let globString = `**/*`; if (message.glob && message.glob !== "") globString = message.glob; const globPath = path.join(message.dir, globString); const files = globSync(globPath); watchFiles = [...watchFiles, ...files]; } } return watchFiles; }; } }); // src/index.ts import { createHash } from "crypto"; import path2 from "path"; import postcss from "postcss"; import cssModules from "postcss-modules"; var require_src = __commonJS({ "src/index.ts"(exports, module) { init_utils(); var LOAD_TEMP_NAMESPACE = "temp_stylePlugin"; var LOAD_STYLE_NAMESPACE = "stylePlugin"; var SKIP_RESOLVE = "esbuild-style-plugin-skipResolve"; var styleFilter = /.\.(css|sass|scss|less|styl)$/; var handleCSSModules = (mapping, cssModulesOptions) => { const _getJSON = cssModulesOptions.getJSON; return cssModules({ ...cssModulesOptions, getJSON: (cssFilename, json, outputFilename) => { if (typeof _getJSON === "function") _getJSON(cssFilename, json, outputFilename); mapping.data = JSON.stringify(json, null, 2); } }); }; var onTempStyleResolve = async (build, args) => { const { importer, pluginData, resolveDir } = args; return { path: path2.relative(build.initialOptions.absWorkingDir || "", importer), namespace: LOAD_TEMP_NAMESPACE, pluginData: { contents: pluginData, resolveDir } }; }; var onStyleResolve = async (build, args) => { const { namespace } = args; if (args.pluginData === SKIP_RESOLVE || namespace === LOAD_STYLE_NAMESPACE || namespace === LOAD_TEMP_NAMESPACE) return; const result = await build.resolve(args.path, { resolveDir: args.resolveDir, pluginData: SKIP_RESOLVE, kind: args.kind }); if (result.errors.length > 0) { return { errors: result.errors }; } const fullPath = result.path; if (!styleFilter.test(fullPath)) return; return { path: fullPath, namespace: LOAD_STYLE_NAMESPACE, watchFiles: [fullPath] }; }; var onTempLoad = async (args) => { const { pluginData } = args; return { resolveDir: pluginData.resolveDir, contents: pluginData.contents, loader: "css" }; }; var onStyleLoad = (options) => async (args) => { const extract = options.extract === void 0 ? true : options.extract; const cssModulesMatch = options.cssModulesMatch || /\.module\./; const isCSSModule = args.path.match(cssModulesMatch); const cssModulesOptions = options.cssModulesOptions || {}; const renderOptions = options.renderOptions; let css = await renderStyle(args.path, renderOptions); let watchFiles = []; let mapping = { data: {} }; let { plugins = [], ...processOptions } = options.postcss || {}; let injectMapping = false; let contents = ""; if (isCSSModule) { plugins = [handleCSSModules(mapping, cssModulesOptions), ...plugins]; injectMapping = true; } if (plugins.length > 0) { const result = await postcss(plugins).process(css, { ...processOptions, from: args.path }); css = result.css; watchFiles = [...watchFiles, ...getPostCSSWatchFiles(result)]; if (injectMapping) contents += `export default ${mapping.data};`; } if (extract) { contents += `import ${JSON.stringify("ni:sha-256;" + createHash("sha256").update(css).digest("base64url"))};`; } return { watchFiles, resolveDir: path2.dirname(args.path), // Keep resolveDir for onTempLoad anything resolve inside temp file must be resolve using source dir contents, pluginData: css }; }; var stylePlugin = (options = {}) => ({ name: "esbuild-style-plugin", setup: async (build) => { if (options.postcssConfigFile) { console.log(`Using postcss config file.`); options.postcss = await importPostcssConfigFile(options.postcssConfigFile); } build.onResolve({ filter: styleFilter }, onStyleResolve.bind(null, build)); build.onResolve({ filter: /^ni:/, namespace: LOAD_STYLE_NAMESPACE }, onTempStyleResolve.bind(null, build)); build.onLoad({ filter: /.*/, namespace: LOAD_TEMP_NAMESPACE }, onTempLoad); build.onLoad({ filter: /.*/, namespace: LOAD_STYLE_NAMESPACE }, onStyleLoad(options)); } }); module.exports = stylePlugin; } }); export default require_src();