esbuild-style-plugin
Version:
Another esbuild plugin for your styling with CSS,SASS,LESS,STYLUS
217 lines (213 loc) • 8.42 kB
JavaScript
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;