├── .gitignore ├── README.md ├── dist ├── src │ ├── _worker.js │ └── index.js └── types │ └── index.js ├── example ├── index.html ├── my-async-config.js ├── styles │ ├── input.css │ └── output.css └── tailwind.config.js ├── package.json ├── src ├── _worker.js └── index.ts ├── tsconfig.json └── types └── index.ts /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .idea 3 | .vscode 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tailwind Async Config. 2 | 3 | ### Use an async function to generate your Tailwind CSS config. 4 | 5 | ## Usage: 6 | 7 | * Install the package: 8 | 9 | ```bash 10 | npm install tailwind-async-config 11 | ``` 12 | 13 | * Create a file to wrap your async function 14 | 15 | * if you are not using Bun, Then this file should be a commonjs module that has a default export of an async function that returns your tailwind 16 | config. 17 | * you can not use your tsconfig paths in this file, you must use relative paths. 18 | * This is an example of what the file should look like: 19 | ```javascript 20 | const MyAsyncConfig = async () => { 21 | // Simulate an async operation 22 | await fetch('https://jsonplaceholder.typicode.com/todos/1') 23 | 24 | return { 25 | extend: { 26 | colors: { 27 | 'primary': '#FF6363' 28 | } 29 | } 30 | } 31 | } 32 | 33 | module.exports = MyAsyncConfig; 34 | ``` 35 | * Use your file in your tailwind.config.js 36 | ```javascript 37 | const TailwindAsyncConfig = require("tailwind-async-config").default; 38 | /** @type {import('tailwindcss').Config} */ 39 | module.exports = { 40 | content: ["index.html"], 41 | theme: TailwindAsyncConfig({ 42 | configPath: "./my-async-config.js", 43 | useBun: false, 44 | }), 45 | plugins: [], 46 | } 47 | ``` 48 | * If you are using Bun, you must set the useBun option to true, and you must use es module in your async config file. 49 | * now you can run your project 50 | ```bash 51 | npx tailwindcss -i ./src/styles.css -o ./dist/styles.css 52 | ``` 53 | * there is an example at [example](https://github.com/amirrezaDev1378/tailwind-async-config/tree/master/example) 54 | 55 | ## Options: 56 | 57 | | Name | Default | Description | 58 | |------------|---------|------------------------------------------------------------------------------------------------------| 59 | | configPath | - | Path to your async config e.g. "my-async-config.js" | 60 | | useBun | false | Whether to use Bun or not, if you are using typescript or es module you must set this option to true | 61 | -------------------------------------------------------------------------------- /dist/src/_worker.js: -------------------------------------------------------------------------------- 1 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 2 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 3 | return new (P || (P = Promise))(function (resolve, reject) { 4 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 5 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 6 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 7 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 8 | }); 9 | }; 10 | var __generator = (this && this.__generator) || function (thisArg, body) { 11 | var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; 12 | return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; 13 | function verb(n) { return function (v) { return step([n, v]); }; } 14 | function step(op) { 15 | if (f) throw new TypeError("Generator is already executing."); 16 | while (g && (g = 0, op[0] && (_ = 0)), _) try { 17 | if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; 18 | if (y = 0, t) op = [op[0] & 2, t.value]; 19 | switch (op[0]) { 20 | case 0: case 1: t = op; break; 21 | case 4: _.label++; return { value: op[1], done: false }; 22 | case 5: _.label++; y = op[1]; op = [0]; continue; 23 | case 7: op = _.ops.pop(); _.trys.pop(); continue; 24 | default: 25 | if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } 26 | if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } 27 | if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } 28 | if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } 29 | if (t[2]) _.ops.pop(); 30 | _.trys.pop(); continue; 31 | } 32 | op = body.call(thisArg, _); 33 | } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } 34 | if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; 35 | } 36 | }; 37 | var _this = this; 38 | var _worker = function () { return __awaiter(_this, void 0, void 0, function () { 39 | var targetPath, asyncConfigModule, asyncConfig, config; 40 | return __generator(this, function (_a) { 41 | switch (_a.label) { 42 | case 0: 43 | targetPath = process.argv[2]; 44 | return [4 /*yield*/, Promise.resolve("".concat(targetPath)).then(function (s) { return require(s); })]; 45 | case 1: 46 | asyncConfigModule = _a.sent(); 47 | asyncConfig = (asyncConfigModule === null || asyncConfigModule === void 0 ? void 0 : asyncConfigModule.default) ? asyncConfigModule.default : asyncConfigModule; 48 | if (typeof asyncConfig !== "function") { 49 | throw new Error("".concat(targetPath, " does not have a valid default export")); 50 | } 51 | return [4 /*yield*/, asyncConfig()]; 52 | case 2: 53 | config = _a.sent(); 54 | if (typeof config !== "object") { 55 | throw new Error("".concat(targetPath, " does not return a valid object")); 56 | } 57 | return [2 /*return*/, config]; 58 | } 59 | }); 60 | }); }; 61 | try { 62 | _worker().then(function (r) { return process.stdout.write(JSON.stringify(r)); }); 63 | } 64 | catch (e) { 65 | console.error("failed to run the worker! please check the logs for more information."); 66 | throw e; 67 | } 68 | -------------------------------------------------------------------------------- /dist/src/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var child_process_1 = require("child_process"); 4 | var path = require("path"); 5 | /** 6 | * Loads your tailwind config file asynchronously 7 | * 8 | * @param {Object} params 9 | * @param {String} params.configPath - The path to your tailwind config file 10 | * @param {Boolean=} [params.useBun] - Whether to use bun to load the file. If the file is a typescript file, this must be true 11 | * 12 | * @return {Object=} - The tailwind config object 13 | */ 14 | var TailwindAsyncConfig = function (params) { 15 | if (!(params === null || params === void 0 ? void 0 : params.configPath) || typeof params.configPath !== "string") 16 | throw new Error("You must provide a path to your async Tailwind CSS config file"); 17 | var configPath = params.configPath; 18 | var useBun = typeof params.useBun === "boolean" ? params.useBun : false; 19 | var runtime = useBun ? "bun" : "node"; 20 | if (configPath.endsWith(".ts") && !useBun) { 21 | throw new Error("You must use bun to load your file if it is a typescript file"); 22 | } 23 | try { 24 | var _a = (0, child_process_1.spawnSync)(runtime, [path.resolve(__dirname, "./_worker.js"), path.resolve(process.cwd(), configPath)]), stdout = _a.stdout, stderr = _a.stderr; 25 | if (stderr.toString()) 26 | throw stderr.toString(); 27 | var tailwindConfigJson = JSON.parse(stdout.toString()); 28 | return tailwindConfigJson; 29 | } 30 | catch (e) { 31 | console.error("failed to read tailwind config from worker."); 32 | console.error(e); 33 | } 34 | }; 35 | exports.default = TailwindAsyncConfig; 36 | -------------------------------------------------------------------------------- /dist/types/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Tailwind Async Config 8 | 9 | 10 |

11 | Hello world! 12 |

13 | 14 | 15 | -------------------------------------------------------------------------------- /example/my-async-config.js: -------------------------------------------------------------------------------- 1 | // This is an example of how to use an async function to return a configuration object 2 | // Please note that the async function must be a commonjs module 3 | 4 | const MyAsyncConfig = async () => { 5 | // Simulate an async operation 6 | await fetch('https://jsonplaceholder.typicode.com/todos/1') 7 | 8 | return { 9 | extend: { 10 | colors: { 11 | 'primary': '#FF6363', 12 | } 13 | }, 14 | } 15 | } 16 | 17 | module.exports = MyAsyncConfig; 18 | -------------------------------------------------------------------------------- /example/styles/input.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /example/styles/output.css: -------------------------------------------------------------------------------- 1 | /* 2 | ! tailwindcss v3.4.1 | MIT License | https://tailwindcss.com 3 | */ 4 | 5 | /* 6 | 1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4) 7 | 2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116) 8 | */ 9 | 10 | *, 11 | ::before, 12 | ::after { 13 | box-sizing: border-box; 14 | /* 1 */ 15 | border-width: 0; 16 | /* 2 */ 17 | border-style: solid; 18 | /* 2 */ 19 | border-color: #e5e7eb; 20 | /* 2 */ 21 | } 22 | 23 | ::before, 24 | ::after { 25 | --tw-content: ''; 26 | } 27 | 28 | /* 29 | 1. Use a consistent sensible line-height in all browsers. 30 | 2. Prevent adjustments of font size after orientation changes in iOS. 31 | 3. Use a more readable tab size. 32 | 4. Use the user's configured `sans` font-family by default. 33 | 5. Use the user's configured `sans` font-feature-settings by default. 34 | 6. Use the user's configured `sans` font-variation-settings by default. 35 | 7. Disable tap highlights on iOS 36 | */ 37 | 38 | html, 39 | :host { 40 | line-height: 1.5; 41 | /* 1 */ 42 | -webkit-text-size-adjust: 100%; 43 | /* 2 */ 44 | -moz-tab-size: 4; 45 | /* 3 */ 46 | -o-tab-size: 4; 47 | tab-size: 4; 48 | /* 3 */ 49 | font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; 50 | /* 4 */ 51 | font-feature-settings: normal; 52 | /* 5 */ 53 | font-variation-settings: normal; 54 | /* 6 */ 55 | -webkit-tap-highlight-color: transparent; 56 | /* 7 */ 57 | } 58 | 59 | /* 60 | 1. Remove the margin in all browsers. 61 | 2. Inherit line-height from `html` so users can set them as a class directly on the `html` element. 62 | */ 63 | 64 | body { 65 | margin: 0; 66 | /* 1 */ 67 | line-height: inherit; 68 | /* 2 */ 69 | } 70 | 71 | /* 72 | 1. Add the correct height in Firefox. 73 | 2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) 74 | 3. Ensure horizontal rules are visible by default. 75 | */ 76 | 77 | hr { 78 | height: 0; 79 | /* 1 */ 80 | color: inherit; 81 | /* 2 */ 82 | border-top-width: 1px; 83 | /* 3 */ 84 | } 85 | 86 | /* 87 | Add the correct text decoration in Chrome, Edge, and Safari. 88 | */ 89 | 90 | abbr:where([title]) { 91 | -webkit-text-decoration: underline dotted; 92 | text-decoration: underline dotted; 93 | } 94 | 95 | /* 96 | Remove the default font size and weight for headings. 97 | */ 98 | 99 | h1, 100 | h2, 101 | h3, 102 | h4, 103 | h5, 104 | h6 { 105 | font-size: inherit; 106 | font-weight: inherit; 107 | } 108 | 109 | /* 110 | Reset links to optimize for opt-in styling instead of opt-out. 111 | */ 112 | 113 | a { 114 | color: inherit; 115 | text-decoration: inherit; 116 | } 117 | 118 | /* 119 | Add the correct font weight in Edge and Safari. 120 | */ 121 | 122 | b, 123 | strong { 124 | font-weight: bolder; 125 | } 126 | 127 | /* 128 | 1. Use the user's configured `mono` font-family by default. 129 | 2. Use the user's configured `mono` font-feature-settings by default. 130 | 3. Use the user's configured `mono` font-variation-settings by default. 131 | 4. Correct the odd `em` font sizing in all browsers. 132 | */ 133 | 134 | code, 135 | kbd, 136 | samp, 137 | pre { 138 | font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; 139 | /* 1 */ 140 | font-feature-settings: normal; 141 | /* 2 */ 142 | font-variation-settings: normal; 143 | /* 3 */ 144 | font-size: 1em; 145 | /* 4 */ 146 | } 147 | 148 | /* 149 | Add the correct font size in all browsers. 150 | */ 151 | 152 | small { 153 | font-size: 80%; 154 | } 155 | 156 | /* 157 | Prevent `sub` and `sup` elements from affecting the line height in all browsers. 158 | */ 159 | 160 | sub, 161 | sup { 162 | font-size: 75%; 163 | line-height: 0; 164 | position: relative; 165 | vertical-align: baseline; 166 | } 167 | 168 | sub { 169 | bottom: -0.25em; 170 | } 171 | 172 | sup { 173 | top: -0.5em; 174 | } 175 | 176 | /* 177 | 1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) 178 | 2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) 179 | 3. Remove gaps between table borders by default. 180 | */ 181 | 182 | table { 183 | text-indent: 0; 184 | /* 1 */ 185 | border-color: inherit; 186 | /* 2 */ 187 | border-collapse: collapse; 188 | /* 3 */ 189 | } 190 | 191 | /* 192 | 1. Change the font styles in all browsers. 193 | 2. Remove the margin in Firefox and Safari. 194 | 3. Remove default padding in all browsers. 195 | */ 196 | 197 | button, 198 | input, 199 | optgroup, 200 | select, 201 | textarea { 202 | font-family: inherit; 203 | /* 1 */ 204 | font-feature-settings: inherit; 205 | /* 1 */ 206 | font-variation-settings: inherit; 207 | /* 1 */ 208 | font-size: 100%; 209 | /* 1 */ 210 | font-weight: inherit; 211 | /* 1 */ 212 | line-height: inherit; 213 | /* 1 */ 214 | color: inherit; 215 | /* 1 */ 216 | margin: 0; 217 | /* 2 */ 218 | padding: 0; 219 | /* 3 */ 220 | } 221 | 222 | /* 223 | Remove the inheritance of text transform in Edge and Firefox. 224 | */ 225 | 226 | button, 227 | select { 228 | text-transform: none; 229 | } 230 | 231 | /* 232 | 1. Correct the inability to style clickable types in iOS and Safari. 233 | 2. Remove default button styles. 234 | */ 235 | 236 | button, 237 | [type='button'], 238 | [type='reset'], 239 | [type='submit'] { 240 | -webkit-appearance: button; 241 | /* 1 */ 242 | background-color: transparent; 243 | /* 2 */ 244 | background-image: none; 245 | /* 2 */ 246 | } 247 | 248 | /* 249 | Use the modern Firefox focus style for all focusable elements. 250 | */ 251 | 252 | :-moz-focusring { 253 | outline: auto; 254 | } 255 | 256 | /* 257 | Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737) 258 | */ 259 | 260 | :-moz-ui-invalid { 261 | box-shadow: none; 262 | } 263 | 264 | /* 265 | Add the correct vertical alignment in Chrome and Firefox. 266 | */ 267 | 268 | progress { 269 | vertical-align: baseline; 270 | } 271 | 272 | /* 273 | Correct the cursor style of increment and decrement buttons in Safari. 274 | */ 275 | 276 | ::-webkit-inner-spin-button, 277 | ::-webkit-outer-spin-button { 278 | height: auto; 279 | } 280 | 281 | /* 282 | 1. Correct the odd appearance in Chrome and Safari. 283 | 2. Correct the outline style in Safari. 284 | */ 285 | 286 | [type='search'] { 287 | -webkit-appearance: textfield; 288 | /* 1 */ 289 | outline-offset: -2px; 290 | /* 2 */ 291 | } 292 | 293 | /* 294 | Remove the inner padding in Chrome and Safari on macOS. 295 | */ 296 | 297 | ::-webkit-search-decoration { 298 | -webkit-appearance: none; 299 | } 300 | 301 | /* 302 | 1. Correct the inability to style clickable types in iOS and Safari. 303 | 2. Change font properties to `inherit` in Safari. 304 | */ 305 | 306 | ::-webkit-file-upload-button { 307 | -webkit-appearance: button; 308 | /* 1 */ 309 | font: inherit; 310 | /* 2 */ 311 | } 312 | 313 | /* 314 | Add the correct display in Chrome and Safari. 315 | */ 316 | 317 | summary { 318 | display: list-item; 319 | } 320 | 321 | /* 322 | Removes the default spacing and border for appropriate elements. 323 | */ 324 | 325 | blockquote, 326 | dl, 327 | dd, 328 | h1, 329 | h2, 330 | h3, 331 | h4, 332 | h5, 333 | h6, 334 | hr, 335 | figure, 336 | p, 337 | pre { 338 | margin: 0; 339 | } 340 | 341 | fieldset { 342 | margin: 0; 343 | padding: 0; 344 | } 345 | 346 | legend { 347 | padding: 0; 348 | } 349 | 350 | ol, 351 | ul, 352 | menu { 353 | list-style: none; 354 | margin: 0; 355 | padding: 0; 356 | } 357 | 358 | /* 359 | Reset default styling for dialogs. 360 | */ 361 | 362 | dialog { 363 | padding: 0; 364 | } 365 | 366 | /* 367 | Prevent resizing textareas horizontally by default. 368 | */ 369 | 370 | textarea { 371 | resize: vertical; 372 | } 373 | 374 | /* 375 | 1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300) 376 | 2. Set the default placeholder color to the user's configured gray 400 color. 377 | */ 378 | 379 | input::-moz-placeholder, textarea::-moz-placeholder { 380 | opacity: 1; 381 | /* 1 */ 382 | color: #9ca3af; 383 | /* 2 */ 384 | } 385 | 386 | input::placeholder, 387 | textarea::placeholder { 388 | opacity: 1; 389 | /* 1 */ 390 | color: #9ca3af; 391 | /* 2 */ 392 | } 393 | 394 | /* 395 | Set the default cursor for buttons. 396 | */ 397 | 398 | button, 399 | [role="button"] { 400 | cursor: pointer; 401 | } 402 | 403 | /* 404 | Make sure disabled buttons don't get the pointer cursor. 405 | */ 406 | 407 | :disabled { 408 | cursor: default; 409 | } 410 | 411 | /* 412 | 1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14) 413 | 2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210) 414 | This can trigger a poorly considered lint error in some tools but is included by design. 415 | */ 416 | 417 | img, 418 | svg, 419 | video, 420 | canvas, 421 | audio, 422 | iframe, 423 | embed, 424 | object { 425 | display: block; 426 | /* 1 */ 427 | vertical-align: middle; 428 | /* 2 */ 429 | } 430 | 431 | /* 432 | Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14) 433 | */ 434 | 435 | img, 436 | video { 437 | max-width: 100%; 438 | height: auto; 439 | } 440 | 441 | /* Make elements with the HTML hidden attribute stay hidden by default */ 442 | 443 | [hidden] { 444 | display: none; 445 | } 446 | 447 | *, ::before, ::after { 448 | --tw-border-spacing-x: 0; 449 | --tw-border-spacing-y: 0; 450 | --tw-translate-x: 0; 451 | --tw-translate-y: 0; 452 | --tw-rotate: 0; 453 | --tw-skew-x: 0; 454 | --tw-skew-y: 0; 455 | --tw-scale-x: 1; 456 | --tw-scale-y: 1; 457 | --tw-pan-x: ; 458 | --tw-pan-y: ; 459 | --tw-pinch-zoom: ; 460 | --tw-scroll-snap-strictness: proximity; 461 | --tw-gradient-from-position: ; 462 | --tw-gradient-via-position: ; 463 | --tw-gradient-to-position: ; 464 | --tw-ordinal: ; 465 | --tw-slashed-zero: ; 466 | --tw-numeric-figure: ; 467 | --tw-numeric-spacing: ; 468 | --tw-numeric-fraction: ; 469 | --tw-ring-inset: ; 470 | --tw-ring-offset-width: 0px; 471 | --tw-ring-offset-color: #fff; 472 | --tw-ring-color: rgb(59 130 246 / 0.5); 473 | --tw-ring-offset-shadow: 0 0 #0000; 474 | --tw-ring-shadow: 0 0 #0000; 475 | --tw-shadow: 0 0 #0000; 476 | --tw-shadow-colored: 0 0 #0000; 477 | --tw-blur: ; 478 | --tw-brightness: ; 479 | --tw-contrast: ; 480 | --tw-grayscale: ; 481 | --tw-hue-rotate: ; 482 | --tw-invert: ; 483 | --tw-saturate: ; 484 | --tw-sepia: ; 485 | --tw-drop-shadow: ; 486 | --tw-backdrop-blur: ; 487 | --tw-backdrop-brightness: ; 488 | --tw-backdrop-contrast: ; 489 | --tw-backdrop-grayscale: ; 490 | --tw-backdrop-hue-rotate: ; 491 | --tw-backdrop-invert: ; 492 | --tw-backdrop-opacity: ; 493 | --tw-backdrop-saturate: ; 494 | --tw-backdrop-sepia: ; 495 | } 496 | 497 | ::backdrop { 498 | --tw-border-spacing-x: 0; 499 | --tw-border-spacing-y: 0; 500 | --tw-translate-x: 0; 501 | --tw-translate-y: 0; 502 | --tw-rotate: 0; 503 | --tw-skew-x: 0; 504 | --tw-skew-y: 0; 505 | --tw-scale-x: 1; 506 | --tw-scale-y: 1; 507 | --tw-pan-x: ; 508 | --tw-pan-y: ; 509 | --tw-pinch-zoom: ; 510 | --tw-scroll-snap-strictness: proximity; 511 | --tw-gradient-from-position: ; 512 | --tw-gradient-via-position: ; 513 | --tw-gradient-to-position: ; 514 | --tw-ordinal: ; 515 | --tw-slashed-zero: ; 516 | --tw-numeric-figure: ; 517 | --tw-numeric-spacing: ; 518 | --tw-numeric-fraction: ; 519 | --tw-ring-inset: ; 520 | --tw-ring-offset-width: 0px; 521 | --tw-ring-offset-color: #fff; 522 | --tw-ring-color: rgb(59 130 246 / 0.5); 523 | --tw-ring-offset-shadow: 0 0 #0000; 524 | --tw-ring-shadow: 0 0 #0000; 525 | --tw-shadow: 0 0 #0000; 526 | --tw-shadow-colored: 0 0 #0000; 527 | --tw-blur: ; 528 | --tw-brightness: ; 529 | --tw-contrast: ; 530 | --tw-grayscale: ; 531 | --tw-hue-rotate: ; 532 | --tw-invert: ; 533 | --tw-saturate: ; 534 | --tw-sepia: ; 535 | --tw-drop-shadow: ; 536 | --tw-backdrop-blur: ; 537 | --tw-backdrop-brightness: ; 538 | --tw-backdrop-contrast: ; 539 | --tw-backdrop-grayscale: ; 540 | --tw-backdrop-hue-rotate: ; 541 | --tw-backdrop-invert: ; 542 | --tw-backdrop-opacity: ; 543 | --tw-backdrop-saturate: ; 544 | --tw-backdrop-sepia: ; 545 | } 546 | 547 | .bg-primary { 548 | --tw-bg-opacity: 1; 549 | background-color: rgb(255 99 99 / var(--tw-bg-opacity)); 550 | } 551 | 552 | .text-3xl { 553 | font-size: 1.875rem; 554 | line-height: 2.25rem; 555 | } 556 | 557 | .font-bold { 558 | font-weight: 700; 559 | } 560 | 561 | .underline { 562 | text-decoration-line: underline; 563 | } 564 | -------------------------------------------------------------------------------- /example/tailwind.config.js: -------------------------------------------------------------------------------- 1 | const TailwindAsyncConfig = require("../dist/src/index").default; 2 | /** @type {import('tailwindcss').Config} */ 3 | module.exports = { 4 | content: ["index.html"], 5 | theme: TailwindAsyncConfig({ 6 | configPath: "./my-async-config", 7 | useBun: true, 8 | }), 9 | plugins: [], 10 | } 11 | 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tailwind-async-config", 3 | "type": "commonjs", 4 | "version": "1.0.0", 5 | "description": "Use an async function to generate your Tailwind CSS config.", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/amirrezaDev1378/tailwind-async-config" 9 | }, 10 | "bugs": { 11 | "url": "https://github.com/amirrezaDev1378/tailwind-async-config/issues/new" 12 | }, 13 | "homepage": "https://github.com/amirrezaDev1378/tailwind-async-config", 14 | 15 | "main": "dist/src/index.js", 16 | "exports": { 17 | "types": "./types/index.ts", 18 | "require": "./dist/src/index.js" 19 | }, 20 | "scripts": { 21 | "build": "tsc -p tsconfig.json", 22 | "run-example": "cd example && tailwindcss -i ./styles/input.css -o ./styles/output.css --watch" 23 | }, 24 | "keywords": [ 25 | "tailwind-async-config", 26 | "tailwindcss", 27 | "async", 28 | "config", 29 | "tailwind", 30 | "css", 31 | "async-config" 32 | ], 33 | "author": { 34 | "name": "Amirreza Hossein Panah", 35 | "email": "rezaamir731@gmail.com" 36 | }, 37 | "license": "Apache-2.0", 38 | "devDependencies": { 39 | "@types/bun": "^1.0.8", 40 | "@types/node": "^20.11.24", 41 | "typescript": "^5.3.3", 42 | "tailwindcss": "^3.4.1" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/_worker.js: -------------------------------------------------------------------------------- 1 | const _worker = async () => { 2 | const targetPath = process.argv[2]; 3 | const asyncConfigModule = await import(targetPath) 4 | const asyncConfig = asyncConfigModule?.default ? asyncConfigModule.default : asyncConfigModule; 5 | if (typeof asyncConfig !== "function"){ 6 | throw new Error(`${targetPath} does not have a valid default export`); 7 | } 8 | const config = await asyncConfig(); 9 | if (typeof config !== "object"){ 10 | throw new Error(`${targetPath} does not return a valid object`); 11 | } 12 | 13 | return config 14 | }; 15 | 16 | try { 17 | _worker().then((r) => process.stdout.write(JSON.stringify(r))); 18 | } catch (e) { 19 | console.error("failed to run the worker! please check the logs for more information."); 20 | throw e; 21 | } 22 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import {spawnSync} from "child_process"; 2 | import * as path from "path"; 3 | import {TailwindAsyncConfigParams} from "../types"; 4 | 5 | 6 | /** 7 | * Loads your tailwind config file asynchronously 8 | * 9 | * @param {Object} params 10 | * @param {String} params.configPath - The path to your tailwind config file 11 | * @param {Boolean=} [params.useBun] - Whether to use bun to load the file. If the file is a typescript file, this must be true 12 | * 13 | * @return {Object=} - The tailwind config object 14 | */ 15 | const TailwindAsyncConfig = ( 16 | params: TailwindAsyncConfigParams 17 | ): object | undefined => { 18 | if (!params?.configPath || typeof params.configPath !== "string") throw new Error("You must provide a path to your async Tailwind CSS config file"); 19 | const {configPath} = params; 20 | const useBun = typeof params.useBun === "boolean" ? params.useBun : false; 21 | const runtime = useBun ? "bun" : "node"; 22 | 23 | if (configPath.endsWith(".ts") && !useBun) { 24 | throw new Error("You must use bun to load your file if it is a typescript file"); 25 | } 26 | 27 | try { 28 | const { 29 | stdout, 30 | stderr 31 | } = spawnSync(runtime, [path.resolve(__dirname, "./_worker.js"), path.resolve(process.cwd(), configPath)]); 32 | 33 | if (stderr.toString()) throw stderr.toString(); 34 | 35 | const tailwindConfigJson = JSON.parse(stdout.toString()); 36 | return tailwindConfigJson; 37 | } catch (e) { 38 | console.error("failed to read tailwind config from worker."); 39 | console.error(e); 40 | } 41 | }; 42 | 43 | export default TailwindAsyncConfig; 44 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "CommonJS", 4 | "outDir": "dist", 5 | "allowJs": true, 6 | "baseUrl": ".", 7 | "paths": { 8 | "tailwind-async-config/*": ["./*"], 9 | }, 10 | "skipLibCheck": true 11 | }, 12 | "include": ["src/**/*.ts", "src/**/*.js" , ], 13 | "exclude": ["node_modules" , "dist" , "example" , "types" ], 14 | } 15 | -------------------------------------------------------------------------------- /types/index.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | export interface TailwindAsyncConfigParams { 4 | /** 5 | * The path to your async Tailwind CSS config file 6 | */ 7 | configPath: string; 8 | 9 | /** 10 | * Using bun.js to load your file (if your file is a typescript file you should use this) 11 | */ 12 | useBun?: boolean; 13 | } 14 | --------------------------------------------------------------------------------