UNPKG

esbuild-style-plugin

Version:

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

217 lines (213 loc) 8.42 kB
var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); // src/index.ts var import_crypto = require("crypto"); var import_path2 = __toESM(require("path")); var import_postcss = __toESM(require("postcss")); var import_postcss_modules = __toESM(require("postcss-modules")); // src/utils.ts var import_util = require("util"); var import_path = __toESM(require("path")); var import_fs = __toESM(require("fs")); var import_glob = require("glob"); var getModule = async (moduleName, checkFunc) => { try { const module2 = await import(moduleName); if (typeof module2[checkFunc] === `function`) return module2; if (typeof module2.default[checkFunc] === `function`) return module2.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; } } }; var 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); }); }); }; var renderStyle = async (filePath, options = {}) => { const { ext } = import_path.default.parse(filePath); if (ext === ".css") { return (await import_fs.default.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 import_fs.default.promises.readFile(filePath); return await renderStylus(new import_util.TextDecoder().decode(source), { ...stylusOptions, filename: filePath }); } if (ext === ".less") { const lestOptions = options.lessOptions || {}; const source = await import_fs.default.promises.readFile(filePath); const less = await getModule("less", "render"); return (await less.render(new import_util.TextDecoder().decode(source), { ...lestOptions, filename: filePath })).css; } throw new Error(`Can't render this style '${ext}'.`); }; var importPostcssConfigFile = async (configFilePath) => { let _configFilePath = configFilePath === true ? import_path.default.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.`); } }; var 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 = import_path.default.join(message.dir, globString); const files = (0, import_glob.globSync)(globPath); watchFiles = [...watchFiles, ...files]; } } return watchFiles; }; // src/index.ts 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 (0, import_postcss_modules.default)({ ...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: import_path2.default.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 (0, import_postcss.default)(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;" + (0, import_crypto.createHash)("sha256").update(css).digest("base64url"))};`; } return { watchFiles, resolveDir: import_path2.default.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;