UNPKG

@twind/cli

Version:
420 lines (409 loc) 16.7 kB
var __create = Object.create; var __defProp = Object.defineProperty; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __getOwnPropNames = Object.getOwnPropertyNames; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropSymbols = Object.getOwnPropertySymbols; var __propIsEnum = Object.prototype.propertyIsEnumerable; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, {enumerable: true, configurable: true, writable: true, value}) : obj[key] = value; var __assign = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp.call(b, prop)) __defNormalProp(a, prop, b[prop]); if (__getOwnPropSymbols) for (var prop of __getOwnPropSymbols(b)) { if (__propIsEnum.call(b, prop)) __defNormalProp(a, prop, b[prop]); } return a; }; var __markAsModule = (target) => __defProp(target, "__esModule", {value: true}); var __rest = (source, exclude) => { var target = {}; for (var prop in source) if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0) target[prop] = source[prop]; if (source != null && __getOwnPropSymbols) for (var prop of __getOwnPropSymbols(source)) { if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop)) target[prop] = source[prop]; } return target; }; var __export = (target, all) => { for (var name in all) __defProp(target, name, {get: all[name], enumerable: true}); }; var __exportStar = (target, module2, desc) => { if (module2 && typeof module2 === "object" || typeof module2 === "function") { for (let key of __getOwnPropNames(module2)) if (!__hasOwnProp.call(target, key) && key !== "default") __defProp(target, key, {get: () => module2[key], enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable}); } return target; }; var __toModule = (module2) => { return __exportStar(__markAsModule(__defProp(module2 != null ? __create(__getProtoOf(module2)) : {}, "default", module2 && module2.__esModule && "default" in module2 ? {get: () => module2.default, enumerable: true} : {value: module2, enumerable: true})), module2); }; // src/index.ts __markAsModule(exports); __export(exports, { cli: () => cli, run: () => run }); // node_modules/distilt/shim-node-cjs.js var import_url = __toModule(require("url")); var shim_import_meta_url = (0, import_url.pathToFileURL)(__filename); // package.json var version = "0.2.1"; // src/cli.ts var import_sade = __toModule(require("sade")); // src/run.ts var import_promises2 = __toModule(require("fs/promises")); var import_path2 = __toModule(require("path")); var import_kleur2 = __toModule(require("kleur")); var import_time_span = __toModule(require("time-span")); var import_esbuild = __toModule(require("esbuild")); var import_twind = __toModule(require("twind")); var import_sheets = __toModule(require("twind/sheets")); // src/watch.ts var import_fs = __toModule(require("fs")); var import_path = __toModule(require("path")); var import_chokidar = __toModule(require("chokidar")); var import_ignore = __toModule(require("ignore")); var import_p_event = __toModule(require("p-event")); var import_p_debounce = __toModule(require("p-debounce")); var import_find_up = __toModule(require("find-up")); var readIgnoreFile = (file, cwd) => { try { const path3 = import_find_up.default.sync(file, {cwd}); return path3 ? import_fs.default.readFileSync(path3, {encoding: "utf-8"}) : ""; } catch { return ""; } }; function watch(globs, {cwd = ".", watch: watch2, ignoreFile}) { cwd = import_path.default.resolve(cwd); const isNotIgnored = (0, import_ignore.default)().add(ignoreFile ? readIgnoreFile(ignoreFile, cwd) : "").add(["**/.git*/**", "**/node_modules/**"]).createFilter(); const watcher = import_chokidar.default.watch(globs, { cwd, ignored: (file) => { if (import_path.default.isAbsolute(file)) { file = import_path.default.relative(cwd, file); } return file ? !isNotIgnored(file) : false; }, persistent: !!watch2, ignoreInitial: false, alwaysStat: true, followSymlinks: true, awaitWriteFinish: { stabilityThreshold: 120, pollInterval: 20 } }); let changed = new Map(); let isDone = false; const nextQueue = []; let onReady = (0, import_p_event.default)(watcher, "ready").then(async () => { if (!watch2) { isDone = true; return watcher.close(); } }); const notify = (0, import_p_debounce.default)(() => { if (isDone || changed.size) { const next = nextQueue.shift(); if (next) { next.resolve(isDone && !changed.size ? {done: true, value: void 0} : {done: false, value: changed}); changed = new Map(); } } }, 50); let pendingError; watcher.on("error", (error) => { const next = nextQueue.shift(); if (next) { next.reject(error); } else { pendingError = error; } }).on("add", (file, stats) => { changed.set(import_path.default.resolve(cwd, file), stats); notify(); }).on("change", (file, stats) => { changed.set(import_path.default.resolve(cwd, file), stats); notify(); }).on("unlink", (file) => { changed.set(import_path.default.resolve(cwd, file), void 0); notify(); }); return { [Symbol.asyncIterator]() { return this; }, async next() { if (pendingError) { throw pendingError; } if (onReady) { await onReady; onReady = null; } return new Promise((resolve2, reject) => { nextQueue.push({resolve: resolve2, reject}); notify(); }); }, async return(value) { isDone = true; await watcher.close(); return {done: true, value}; }, async throw(error) { isDone = true; await watcher.close(); throw error; } }; } // src/extract.ts var import_assert = __toModule(require("assert")); var import_promises = __toModule(require("fs/promises")); var cleanCandidate = (candidate) => { return candidate.replace(/^class:/, ""); }; var COMMON_INVALID_CANDIDATES = new Set([ "!DOCTYPE", "true", "false", "null", "undefined", "class", "className", "currentColor" ]); var removeInvalidCandidate = (candidate) => { return !(COMMON_INVALID_CANDIDATES.has(candidate) || !/[a-z]/.test(candidate) || /^[A-Z]|[-/@$&]$|^\s*$/.test(candidate) || /^[@<>[][^:]+:/.test(candidate) != /^-?[<:#.,;?\d[\]%/$&@_]|^v-[^:]+:|^aria-|^https?:\/\/|^mailto:|^tel:/.test(candidate)); }; var extractRulesFromString = (content) => { return (content.match(/(?:\[[^[]+]:|[^>"'`\s(){}\]=])[^<>"'`\s()[]*(?:\[[^[]+]|[^<>"'`\s(){}=:.,])/g) || []).map(cleanCandidate).filter(removeInvalidCandidate); }; var extractRulesFromFile = async (file) => { try { return extractRulesFromString(await (0, import_promises.readFile)(file, {encoding: "utf-8"})); } catch (error) { return []; } }; // src/config.ts var Path = __toModule(require("path")); var import_module = __toModule(require("module")); var import_url2 = __toModule(require("url")); var import_locate_path = __toModule(require("locate-path")); var import_kleur = __toModule(require("kleur")); var TWIND_CONFIG_FILES = [ "twind.config.ts", "twind.config.mjs", "twind.config.js", "twind.config.cjs" ]; var TAILWIND_CONFIG_FILES = [ "tailwind.config.ts", "tailwind.config.mjs", "tailwind.config.js", "tailwind.config.cjs" ]; var findConfig = async (cwd = process.cwd()) => { return (0, import_locate_path.default)([ ...TWIND_CONFIG_FILES, ...TWIND_CONFIG_FILES.map((file) => Path.join("config", file)), ...TWIND_CONFIG_FILES.map((file) => Path.join("src", file)), ...TWIND_CONFIG_FILES.map((file) => Path.join("public", file)), ...TAILWIND_CONFIG_FILES ].map((file) => Path.resolve(cwd, file))); }; var loadFile = (file, cwd = process.cwd()) => { var _a, _b; const moduleId = Path.resolve(cwd, file); try { const require2 = ((_b = (_a = import_module.default).createRequire) == null ? void 0 : _b.call(_a, moduleId)) || import_module.default.createRequireFromPath(moduleId); require2("sucrase/register"); delete require2.cache[moduleId]; return require2(moduleId); } catch (error) { console.error(import_kleur.default.red(`Failed to to load ${import_kleur.default.bold(Path.relative(process.cwd(), moduleId))}: ${error.message}`)); return {}; } }; var loadConfig = (configFile, cwd = process.cwd()) => { const exports2 = loadFile(configFile, cwd); const config = exports2.default || exports2 || {}; if (Array.isArray(config.plugins) || Object.values(config.variants || {}).some((value) => typeof value == "object") || typeof config.prefix == "string", "presets" in config || "separator" in config || "variantOrder" in config || "corePlugins" in config || "purge" in config) { console.error(import_kleur.default.red(`${import_kleur.default.bold(Path.relative(process.cwd(), configFile))} is a tailwindcss configuration file – ${import_kleur.default.bold("only")} the theme, darkMode, purge files are used`)); return { theme: config.theme, darkMode: config.darkMode, purge: config.purge }; } return config; }; // src/run.ts var run = async (globs, options = {}) => { import_kleur2.default.enabled = !!options.color; options.cwd = import_path2.default.resolve(options.cwd || "."); const configFile = options.config && import_path2.default.resolve(options.cwd, options.config) || await findConfig(options.cwd); const unknownRules = new Set(); const ignoreUnknownRules = (rule) => !unknownRules.has(rule); const mode = { unknown() { }, report(info) { if (info.id == "UNKNOWN_DIRECTIVE") { unknownRules.add(info.rule); } } }; const watched = new Map(); const candidatesByFile = new Map(); let lastCandidates = new Set([""]); let runCount = -1; const loadConfig2 = () => { let config = {}; if (configFile) { const configEndTime = (0, import_time_span.default)(); config = loadConfig(configFile, options.cwd); console.error(import_kleur2.default.green(`Loaded configuration from ${import_kleur2.default.bold(import_path2.default.relative(process.cwd(), configFile))} in ${import_kleur2.default.bold(configEndTime.rounded() + " ms")}`)); if (runCount < 1 && config.purge) { if (Array.isArray(config.purge)) { globs = [...globs, ...config.purge]; } else if (Array.isArray(config.purge.content)) { globs = [...globs, ...config.purge.content]; } } } unknownRules.clear(); lastCandidates = new Set([""]); const sheet2 = (0, import_sheets.virtualSheet)(); return { sheet: sheet2, tw: (0, import_twind.create)(__assign(__assign({}, config), {sheet: sheet2, mode, hash: false})).tw }; }; let {sheet, tw} = await loadConfig2(); const outputFile = options.output && import_path2.default.resolve(options.cwd, options.output); globs = globs.filter(Boolean); if (!globs.length) { globs.push("**/*.{htm,html,js,jsx,tsx,svelte,vue,mdx}"); } console.error(import_kleur2.default.dim(`Using the following patterns: ${import_kleur2.default.bold(JSON.stringify(globs).slice(1, -1))}`)); for await (const changes of watch(configFile ? [configFile, ...globs] : globs, options)) { runCount++; console.error(import_kleur2.default.cyan(`Processing ${import_kleur2.default.bold(changes.size)}${options.watch ? " changed" : ""} file${changes.size == 1 ? "" : "s"}`)); const endTime = (0, import_time_span.default)(); const pendingDetections = []; let hasChanged = false; for (const [file, stats] of changes.entries()) { if (file == configFile) { if (runCount) { ; ({sheet, tw} = await loadConfig2()); hasChanged = true; } } else if (stats) { const watchedStats = watched.get(file); if (!watchedStats || watchedStats.size !== stats.size || watchedStats.mtimeMs !== stats.mtimeMs || watchedStats.ino !== stats.ino) { pendingDetections.push(extractRulesFromFile(file).then((candidates) => { watched.set(file, stats); candidatesByFile.set(file, candidates); if (!hasChanged) { for (let index = candidates.length; index--; ) { if (!lastCandidates.has(candidates[index])) { hasChanged = true; break; } } } })); } } else { watched.delete(file); candidatesByFile.delete(file); hasChanged = true; } } await Promise.all(pendingDetections); const nextCandidates = new Set(); const addCandidate = (candidate) => { nextCandidates.add(candidate); }; candidatesByFile.forEach((candidates) => { candidates.forEach(addCandidate); }); console.error(import_kleur2.default.gray(`Extracted ${import_kleur2.default.bold(nextCandidates.size)} candidate${nextCandidates.size == 1 ? "" : "s"} from ${watched.size} file${watched.size == 1 ? "" : "s"} in ${import_kleur2.default.bold(endTime.rounded() + " ms")}`)); if (hasChanged || !equals(lastCandidates, nextCandidates)) { const twEndTime = (0, import_time_span.default)(); sheet.reset(); tw([...nextCandidates].filter(ignoreUnknownRules).sort().join(" ")); console.error(import_kleur2.default.gray(`Generated ${import_kleur2.default.bold(sheet.target.length)} CSS rule${sheet.target.length == 1 ? "" : "s"} in ${import_kleur2.default.bold(twEndTime.rounded() + " ms")}`)); lastCandidates = nextCandidates; let css = sheet.target.join("\n"); if (options.beautify || !options.watch) { const cssEndTime = (0, import_time_span.default)(); const result = await (0, import_esbuild.transform)(css, { minify: !options.beautify, loader: "css", sourcemap: false }); css = result.code; console.error(import_kleur2.default.gray(`${options.beautify ? "Beautified" : "Minimized"} CSS in ${import_kleur2.default.bold(cssEndTime.rounded() + " ms")}`)); } if (outputFile) { await import_promises2.default.mkdir(import_path2.default.dirname(outputFile), {recursive: true}); await import_promises2.default.writeFile(outputFile, css); console.error(import_kleur2.default.green(`Finished ${import_kleur2.default.bold(import_path2.default.relative(process.cwd(), outputFile))} in ${import_kleur2.default.bold(endTime.rounded() + " ms")}`)); } else { console.error(import_kleur2.default.green(`Finished in ${import_kleur2.default.bold(endTime.rounded() + " ms")}`)); console.log(css); } } else { console.error(import_kleur2.default.green().dim(`No new classes detected - skipped generating CSS`)); } if (options.watch) { console.error("\n" + import_kleur2.default.dim("Waiting for file changes...")); } } if (runCount < 0) { console.error(import_kleur2.default.yellow(`No matching files found...`)); } }; function equals(a, b) { return a.size === b.size && every(b, (value) => a.has(value)); } function every(as, predicate) { for (const a of as) { if (!predicate(a)) { return false; } } return true; } // src/cli.ts var import_supports_color = __toModule(require("supports-color")); var cli = (argv = process.argv) => (0, import_sade.default)("twind [...globs=**/*.{htm,html,js,jsx,tsx,svelte,vue,mdx}]", true).version(version).option("-o, --output", "Set output css file path (default print to console)").option("-c, --config", "Set config file path (default twind.config.{[cm]js,ts} or tailwind.config.{[cm]js,ts})").option("-i, --ignore", "Any file patterns to ignore").option("-I, --ignore-file", "gitignore like file", ".gitignore").option("-b, --beautify", "Generate beautified css file", false).option("-C, --cwd", "The current directory to resolve from", ".").option("-w, --watch", "Watch for changes", false).option("--color", "Print colorized output - to disable use --no-color", !!import_supports_color.default.stderr).action(async (globs, _a) => { var {_, ["ignore-file"]: ignoreFile} = _a, options = __rest(_a, ["_", "ignore-file"]); try { await run([globs, ..._], __assign(__assign({}, options), {ignoreFile})); } catch (error) { console.error(error.stack || error.message); process.exit(1); } }).parse(argv); // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { cli, run }); //# sourceMappingURL=cli.cjs.map