├── .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 |
--------------------------------------------------------------------------------