You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

808 lines
31 KiB

2 years ago
  1. #!/usr/bin/env node
  2. "use strict";
  3. Object.defineProperty(exports, "__esModule", {
  4. value: true
  5. });
  6. const _indexJs = require("../peers/index.js");
  7. const _chokidar = /*#__PURE__*/ _interopRequireDefault(require("chokidar"));
  8. const _path = /*#__PURE__*/ _interopRequireDefault(require("path"));
  9. const _arg = /*#__PURE__*/ _interopRequireDefault(require("arg"));
  10. const _fs = /*#__PURE__*/ _interopRequireDefault(require("fs"));
  11. const _postcssLoadConfig = /*#__PURE__*/ _interopRequireDefault(require("postcss-load-config"));
  12. const _lilconfig = require("lilconfig");
  13. const _plugins = /*#__PURE__*/ _interopRequireDefault(require("postcss-load-config/src/plugins" // Little bit scary, looking at private/internal API
  14. ));
  15. const _options = /*#__PURE__*/ _interopRequireDefault(require("postcss-load-config/src/options" // Little bit scary, looking at private/internal API
  16. ));
  17. const _processTailwindFeatures = /*#__PURE__*/ _interopRequireDefault(require("./processTailwindFeatures"));
  18. const _resolveConfig = /*#__PURE__*/ _interopRequireDefault(require("../resolveConfig"));
  19. const _fastGlob = /*#__PURE__*/ _interopRequireDefault(require("fast-glob"));
  20. const _getModuleDependencies = /*#__PURE__*/ _interopRequireDefault(require("./lib/getModuleDependencies"));
  21. const _log = /*#__PURE__*/ _interopRequireDefault(require("./util/log"));
  22. const _packageJson = /*#__PURE__*/ _interopRequireDefault(require("../package.json"));
  23. const _normalizePath = /*#__PURE__*/ _interopRequireDefault(require("normalize-path"));
  24. const _validateConfigJs = require("./util/validateConfig.js");
  25. function _interopRequireDefault(obj) {
  26. return obj && obj.__esModule ? obj : {
  27. default: obj
  28. };
  29. }
  30. let env = {
  31. DEBUG: process.env.DEBUG !== undefined && process.env.DEBUG !== "0"
  32. };
  33. function isESM() {
  34. const pkgPath = _path.default.resolve("./package.json");
  35. try {
  36. let pkg = JSON.parse(_fs.default.readFileSync(pkgPath, "utf8"));
  37. return pkg.type && pkg.type === "module";
  38. } catch (err) {
  39. return false;
  40. }
  41. }
  42. let configs = isESM() ? {
  43. tailwind: "tailwind.config.cjs",
  44. postcss: "postcss.config.cjs"
  45. } : {
  46. tailwind: "tailwind.config.js",
  47. postcss: "postcss.config.js"
  48. };
  49. // ---
  50. function indentRecursive(node, indent = 0) {
  51. node.each && node.each((child, i)=>{
  52. if (!child.raws.before || !child.raws.before.trim() || child.raws.before.includes("\n")) {
  53. child.raws.before = `\n${node.type !== "rule" && i > 0 ? "\n" : ""}${" ".repeat(indent)}`;
  54. }
  55. child.raws.after = `\n${" ".repeat(indent)}`;
  56. indentRecursive(child, indent + 1);
  57. });
  58. }
  59. function formatNodes(root) {
  60. indentRecursive(root);
  61. if (root.first) {
  62. root.first.raws.before = "";
  63. }
  64. }
  65. async function outputFile(file, contents) {
  66. if (_fs.default.existsSync(file) && await _fs.default.promises.readFile(file, "utf8") === contents) {
  67. return; // Skip writing the file
  68. }
  69. // Write the file
  70. await _fs.default.promises.writeFile(file, contents, "utf8");
  71. }
  72. function drainStdin() {
  73. return new Promise((resolve, reject)=>{
  74. let result = "";
  75. process.stdin.on("data", (chunk)=>{
  76. result += chunk;
  77. });
  78. process.stdin.on("end", ()=>resolve(result));
  79. process.stdin.on("error", (err)=>reject(err));
  80. });
  81. }
  82. function help({ message , usage , commands , options }) {
  83. let indent = 2;
  84. // Render header
  85. console.log();
  86. console.log(`${_packageJson.default.name} v${_packageJson.default.version}`);
  87. // Render message
  88. if (message) {
  89. console.log();
  90. for (let msg of message.split("\n")){
  91. console.log(msg);
  92. }
  93. }
  94. // Render usage
  95. if (usage && usage.length > 0) {
  96. console.log();
  97. console.log("Usage:");
  98. for (let example of usage){
  99. console.log(" ".repeat(indent), example);
  100. }
  101. }
  102. // Render commands
  103. if (commands && commands.length > 0) {
  104. console.log();
  105. console.log("Commands:");
  106. for (let command of commands){
  107. console.log(" ".repeat(indent), command);
  108. }
  109. }
  110. // Render options
  111. if (options) {
  112. let groupedOptions = {};
  113. for (let [key, value] of Object.entries(options)){
  114. if (typeof value === "object") {
  115. groupedOptions[key] = {
  116. ...value,
  117. flags: [
  118. key
  119. ]
  120. };
  121. } else {
  122. groupedOptions[value].flags.push(key);
  123. }
  124. }
  125. console.log();
  126. console.log("Options:");
  127. for (let { flags , description , deprecated } of Object.values(groupedOptions)){
  128. if (deprecated) continue;
  129. if (flags.length === 1) {
  130. console.log(" ".repeat(indent + 4 /* 4 = "-i, ".length */ ), flags.slice().reverse().join(", ").padEnd(20, " "), description);
  131. } else {
  132. console.log(" ".repeat(indent), flags.slice().reverse().join(", ").padEnd(24, " "), description);
  133. }
  134. }
  135. }
  136. console.log();
  137. }
  138. function oneOf(...options) {
  139. return Object.assign((value = true)=>{
  140. for (let option of options){
  141. let parsed = option(value);
  142. if (parsed === value) {
  143. return parsed;
  144. }
  145. }
  146. throw new Error("...");
  147. }, {
  148. manualParsing: true
  149. });
  150. }
  151. function loadPostcss() {
  152. // Try to load a local `postcss` version first
  153. try {
  154. return require("postcss");
  155. } catch {}
  156. return (0, _indexJs.lazyPostcss)();
  157. }
  158. let commands = {
  159. init: {
  160. run: init,
  161. args: {
  162. "--full": {
  163. type: Boolean,
  164. description: `Initialize a full \`${configs.tailwind}\` file`
  165. },
  166. "--postcss": {
  167. type: Boolean,
  168. description: `Initialize a \`${configs.postcss}\` file`
  169. },
  170. "-f": "--full",
  171. "-p": "--postcss"
  172. }
  173. },
  174. build: {
  175. run: build,
  176. args: {
  177. "--input": {
  178. type: String,
  179. description: "Input file"
  180. },
  181. "--output": {
  182. type: String,
  183. description: "Output file"
  184. },
  185. "--watch": {
  186. type: Boolean,
  187. description: "Watch for changes and rebuild as needed"
  188. },
  189. "--poll": {
  190. type: Boolean,
  191. description: "Use polling instead of filesystem events when watching"
  192. },
  193. "--content": {
  194. type: String,
  195. description: "Content paths to use for removing unused classes"
  196. },
  197. "--purge": {
  198. type: String,
  199. deprecated: true
  200. },
  201. "--postcss": {
  202. type: oneOf(String, Boolean),
  203. description: "Load custom PostCSS configuration"
  204. },
  205. "--minify": {
  206. type: Boolean,
  207. description: "Minify the output"
  208. },
  209. "--config": {
  210. type: String,
  211. description: "Path to a custom config file"
  212. },
  213. "--no-autoprefixer": {
  214. type: Boolean,
  215. description: "Disable autoprefixer"
  216. },
  217. "-c": "--config",
  218. "-i": "--input",
  219. "-o": "--output",
  220. "-m": "--minify",
  221. "-w": "--watch",
  222. "-p": "--poll"
  223. }
  224. }
  225. };
  226. let sharedFlags = {
  227. "--help": {
  228. type: Boolean,
  229. description: "Display usage information"
  230. },
  231. "-h": "--help"
  232. };
  233. if (process.stdout.isTTY /* Detect redirecting output to a file */ && (process.argv[2] === undefined || process.argv.slice(2).every((flag)=>sharedFlags[flag] !== undefined))) {
  234. help({
  235. usage: [
  236. "tailwindcss [--input input.css] [--output output.css] [--watch] [options...]",
  237. "tailwindcss init [--full] [--postcss] [options...]",
  238. ],
  239. commands: Object.keys(commands).filter((command)=>command !== "build").map((command)=>`${command} [options]`),
  240. options: {
  241. ...commands.build.args,
  242. ...sharedFlags
  243. }
  244. });
  245. process.exit(0);
  246. }
  247. let command = ((arg = "")=>arg.startsWith("-") ? undefined : arg)(process.argv[2]) || "build";
  248. if (commands[command] === undefined) {
  249. if (_fs.default.existsSync(_path.default.resolve(command))) {
  250. // TODO: Deprecate this in future versions
  251. // Check if non-existing command, might be a file.
  252. command = "build";
  253. } else {
  254. help({
  255. message: `Invalid command: ${command}`,
  256. usage: [
  257. "tailwindcss <command> [options]"
  258. ],
  259. commands: Object.keys(commands).filter((command)=>command !== "build").map((command)=>`${command} [options]`),
  260. options: sharedFlags
  261. });
  262. process.exit(1);
  263. }
  264. }
  265. // Execute command
  266. let { args: flags , run } = commands[command];
  267. let args = (()=>{
  268. try {
  269. let result = (0, _arg.default)(Object.fromEntries(Object.entries({
  270. ...flags,
  271. ...sharedFlags
  272. }).filter(([_key, value])=>{
  273. var ref;
  274. return !(value === null || value === void 0 ? void 0 : (ref = value.type) === null || ref === void 0 ? void 0 : ref.manualParsing);
  275. }).map(([key, value])=>[
  276. key,
  277. typeof value === "object" ? value.type : value
  278. ])), {
  279. permissive: true
  280. });
  281. // Manual parsing of flags to allow for special flags like oneOf(Boolean, String)
  282. for(let i = result["_"].length - 1; i >= 0; --i){
  283. let flag = result["_"][i];
  284. if (!flag.startsWith("-")) continue;
  285. let flagName = flag;
  286. let handler = flags[flag];
  287. // Resolve flagName & handler
  288. while(typeof handler === "string"){
  289. flagName = handler;
  290. handler = flags[handler];
  291. }
  292. if (!handler) continue;
  293. let args = [];
  294. let offset = i + 1;
  295. // Parse args for current flag
  296. while(result["_"][offset] && !result["_"][offset].startsWith("-")){
  297. args.push(result["_"][offset++]);
  298. }
  299. // Cleanup manually parsed flags + args
  300. result["_"].splice(i, 1 + args.length);
  301. // Set the resolved value in the `result` object
  302. result[flagName] = handler.type(args.length === 0 ? undefined : args.length === 1 ? args[0] : args, flagName);
  303. }
  304. // Ensure that the `command` is always the first argument in the `args`.
  305. // This is important so that we don't have to check if a default command
  306. // (build) was used or not from within each plugin.
  307. //
  308. // E.g.: tailwindcss input.css -> _: ['build', 'input.css']
  309. // E.g.: tailwindcss build input.css -> _: ['build', 'input.css']
  310. if (result["_"][0] !== command) {
  311. result["_"].unshift(command);
  312. }
  313. return result;
  314. } catch (err) {
  315. if (err.code === "ARG_UNKNOWN_OPTION") {
  316. help({
  317. message: err.message,
  318. usage: [
  319. "tailwindcss <command> [options]"
  320. ],
  321. options: sharedFlags
  322. });
  323. process.exit(1);
  324. }
  325. throw err;
  326. }
  327. })();
  328. if (args["--help"]) {
  329. help({
  330. options: {
  331. ...flags,
  332. ...sharedFlags
  333. },
  334. usage: [
  335. `tailwindcss ${command} [options]`
  336. ]
  337. });
  338. process.exit(0);
  339. }
  340. run();
  341. // ---
  342. function init() {
  343. let messages = [];
  344. var ref;
  345. let tailwindConfigLocation = _path.default.resolve((ref = args["_"][1]) !== null && ref !== void 0 ? ref : `./${configs.tailwind}`);
  346. if (_fs.default.existsSync(tailwindConfigLocation)) {
  347. messages.push(`${_path.default.basename(tailwindConfigLocation)} already exists.`);
  348. } else {
  349. let stubFile = _fs.default.readFileSync(args["--full"] ? _path.default.resolve(__dirname, "../stubs/defaultConfig.stub.js") : _path.default.resolve(__dirname, "../stubs/simpleConfig.stub.js"), "utf8");
  350. // Change colors import
  351. stubFile = stubFile.replace("../colors", "tailwindcss/colors");
  352. _fs.default.writeFileSync(tailwindConfigLocation, stubFile, "utf8");
  353. messages.push(`Created Tailwind CSS config file: ${_path.default.basename(tailwindConfigLocation)}`);
  354. }
  355. if (args["--postcss"]) {
  356. let postcssConfigLocation = _path.default.resolve(`./${configs.postcss}`);
  357. if (_fs.default.existsSync(postcssConfigLocation)) {
  358. messages.push(`${_path.default.basename(postcssConfigLocation)} already exists.`);
  359. } else {
  360. let stubFile1 = _fs.default.readFileSync(_path.default.resolve(__dirname, "../stubs/defaultPostCssConfig.stub.js"), "utf8");
  361. _fs.default.writeFileSync(postcssConfigLocation, stubFile1, "utf8");
  362. messages.push(`Created PostCSS config file: ${_path.default.basename(postcssConfigLocation)}`);
  363. }
  364. }
  365. if (messages.length > 0) {
  366. console.log();
  367. for (let message of messages){
  368. console.log(message);
  369. }
  370. }
  371. }
  372. async function build() {
  373. let input = args["--input"];
  374. let output = args["--output"];
  375. let shouldWatch = args["--watch"];
  376. let shouldPoll = args["--poll"];
  377. let shouldCoalesceWriteEvents = shouldPoll || process.platform === "win32";
  378. let includePostCss = args["--postcss"];
  379. // Polling interval in milliseconds
  380. // Used only when polling or coalescing add/change events on Windows
  381. let pollInterval = 10;
  382. // TODO: Deprecate this in future versions
  383. if (!input && args["_"][1]) {
  384. console.error("[deprecation] Running tailwindcss without -i, please provide an input file.");
  385. input = args["--input"] = args["_"][1];
  386. }
  387. if (input && input !== "-" && !_fs.default.existsSync(input = _path.default.resolve(input))) {
  388. console.error(`Specified input file ${args["--input"]} does not exist.`);
  389. process.exit(9);
  390. }
  391. if (args["--config"] && !_fs.default.existsSync(args["--config"] = _path.default.resolve(args["--config"]))) {
  392. console.error(`Specified config file ${args["--config"]} does not exist.`);
  393. process.exit(9);
  394. }
  395. let configPath = args["--config"] ? args["--config"] : ((defaultPath)=>_fs.default.existsSync(defaultPath) ? defaultPath : null)(_path.default.resolve(`./${configs.tailwind}`));
  396. async function loadPostCssPlugins() {
  397. let customPostCssPath = typeof args["--postcss"] === "string" ? args["--postcss"] : undefined;
  398. let config = customPostCssPath ? await (async ()=>{
  399. let file = _path.default.resolve(customPostCssPath);
  400. // Implementation, see: https://unpkg.com/browse/postcss-load-config@3.1.0/src/index.js
  401. let { config ={} } = await (0, _lilconfig.lilconfig)("postcss").load(file);
  402. if (typeof config === "function") {
  403. config = config();
  404. } else {
  405. config = Object.assign({}, config);
  406. }
  407. if (!config.plugins) {
  408. config.plugins = [];
  409. }
  410. return {
  411. file,
  412. plugins: (0, _plugins.default)(config, file),
  413. options: (0, _options.default)(config, file)
  414. };
  415. })() : await (0, _postcssLoadConfig.default)();
  416. let configPlugins = config.plugins;
  417. let configPluginTailwindIdx = configPlugins.findIndex((plugin)=>{
  418. if (typeof plugin === "function" && plugin.name === "tailwindcss") {
  419. return true;
  420. }
  421. if (typeof plugin === "object" && plugin !== null && plugin.postcssPlugin === "tailwindcss") {
  422. return true;
  423. }
  424. return false;
  425. });
  426. let beforePlugins = configPluginTailwindIdx === -1 ? [] : configPlugins.slice(0, configPluginTailwindIdx);
  427. let afterPlugins = configPluginTailwindIdx === -1 ? configPlugins : configPlugins.slice(configPluginTailwindIdx + 1);
  428. return [
  429. beforePlugins,
  430. afterPlugins,
  431. config.options
  432. ];
  433. }
  434. function loadBuiltinPostcssPlugins() {
  435. let postcss = loadPostcss();
  436. let IMPORT_COMMENT = "__TAILWIND_RESTORE_IMPORT__: ";
  437. return [
  438. [
  439. (root)=>{
  440. root.walkAtRules("import", (rule)=>{
  441. if (rule.params.slice(1).startsWith("tailwindcss/")) {
  442. rule.after(postcss.comment({
  443. text: IMPORT_COMMENT + rule.params
  444. }));
  445. rule.remove();
  446. }
  447. });
  448. },
  449. (()=>{
  450. try {
  451. return require("postcss-import");
  452. } catch {}
  453. return (0, _indexJs.lazyPostcssImport)();
  454. })(),
  455. (root)=>{
  456. root.walkComments((rule)=>{
  457. if (rule.text.startsWith(IMPORT_COMMENT)) {
  458. rule.after(postcss.atRule({
  459. name: "import",
  460. params: rule.text.replace(IMPORT_COMMENT, "")
  461. }));
  462. rule.remove();
  463. }
  464. });
  465. },
  466. ],
  467. [],
  468. {},
  469. ];
  470. }
  471. function resolveConfig() {
  472. let config = configPath ? require(configPath) : {};
  473. if (args["--purge"]) {
  474. _log.default.warn("purge-flag-deprecated", [
  475. "The `--purge` flag has been deprecated.",
  476. "Please use `--content` instead.",
  477. ]);
  478. if (!args["--content"]) {
  479. args["--content"] = args["--purge"];
  480. }
  481. }
  482. if (args["--content"]) {
  483. let files = args["--content"].split(/(?<!{[^}]+),/);
  484. let resolvedConfig = (0, _resolveConfig.default)(config, {
  485. content: {
  486. files
  487. }
  488. });
  489. resolvedConfig.content.files = files;
  490. resolvedConfig = (0, _validateConfigJs.validateConfig)(resolvedConfig);
  491. return resolvedConfig;
  492. }
  493. let resolvedConfig1 = (0, _resolveConfig.default)(config);
  494. resolvedConfig1 = (0, _validateConfigJs.validateConfig)(resolvedConfig1);
  495. return resolvedConfig1;
  496. }
  497. function extractFileGlobs(config) {
  498. return config.content.files.filter((file)=>{
  499. // Strings in this case are files / globs. If it is something else,
  500. // like an object it's probably a raw content object. But this object
  501. // is not watchable, so let's remove it.
  502. return typeof file === "string";
  503. }).map((glob)=>(0, _normalizePath.default)(glob));
  504. }
  505. function extractRawContent(config) {
  506. return config.content.files.filter((file)=>{
  507. return typeof file === "object" && file !== null;
  508. });
  509. }
  510. function getChangedContent(config) {
  511. let changedContent = [];
  512. // Resolve globs from the content config
  513. let globs = extractFileGlobs(config);
  514. let files = _fastGlob.default.sync(globs);
  515. for (let file of files){
  516. changedContent.push({
  517. content: _fs.default.readFileSync(_path.default.resolve(file), "utf8"),
  518. extension: _path.default.extname(file).slice(1)
  519. });
  520. }
  521. // Resolve raw content in the tailwind config
  522. for (let { raw: content , extension ="html" } of extractRawContent(config)){
  523. changedContent.push({
  524. content,
  525. extension
  526. });
  527. }
  528. return changedContent;
  529. }
  530. async function buildOnce() {
  531. let config = resolveConfig();
  532. let changedContent = getChangedContent(config);
  533. let tailwindPlugin = ()=>{
  534. return {
  535. postcssPlugin: "tailwindcss",
  536. Once (root, { result }) {
  537. (0, _processTailwindFeatures.default)(({ createContext })=>{
  538. return ()=>{
  539. return createContext(config, changedContent);
  540. };
  541. })(root, result);
  542. }
  543. };
  544. };
  545. tailwindPlugin.postcss = true;
  546. let [beforePlugins, afterPlugins, postcssOptions] = includePostCss ? await loadPostCssPlugins() : loadBuiltinPostcssPlugins();
  547. let plugins = [
  548. ...beforePlugins,
  549. tailwindPlugin,
  550. !args["--minify"] && formatNodes,
  551. ...afterPlugins,
  552. !args["--no-autoprefixer"] && (()=>{
  553. // Try to load a local `autoprefixer` version first
  554. try {
  555. return require("autoprefixer");
  556. } catch {}
  557. return (0, _indexJs.lazyAutoprefixer)();
  558. })(),
  559. args["--minify"] && (()=>{
  560. let options = {
  561. preset: [
  562. "default",
  563. {
  564. cssDeclarationSorter: false
  565. }
  566. ]
  567. };
  568. // Try to load a local `cssnano` version first
  569. try {
  570. return require("cssnano");
  571. } catch {}
  572. return (0, _indexJs.lazyCssnano)()(options);
  573. })(),
  574. ].filter(Boolean);
  575. let postcss = loadPostcss();
  576. let processor = postcss(plugins);
  577. function processCSS(css) {
  578. let start = process.hrtime.bigint();
  579. return Promise.resolve().then(()=>output ? _fs.default.promises.mkdir(_path.default.dirname(output), {
  580. recursive: true
  581. }) : null).then(()=>processor.process(css, {
  582. ...postcssOptions,
  583. from: input,
  584. to: output
  585. })).then((result)=>{
  586. if (!output) {
  587. return process.stdout.write(result.css);
  588. }
  589. return Promise.all([
  590. outputFile(output, result.css),
  591. result.map && outputFile(output + ".map", result.map.toString()),
  592. ].filter(Boolean));
  593. }).then(()=>{
  594. let end = process.hrtime.bigint();
  595. console.error();
  596. console.error("Done in", (end - start) / BigInt(1e6) + "ms.");
  597. });
  598. }
  599. let css = await (()=>{
  600. // Piping in data, let's drain the stdin
  601. if (input === "-") {
  602. return drainStdin();
  603. }
  604. // Input file has been provided
  605. if (input) {
  606. return _fs.default.readFileSync(_path.default.resolve(input), "utf8");
  607. }
  608. // No input file provided, fallback to default atrules
  609. return "@tailwind base; @tailwind components; @tailwind utilities";
  610. })();
  611. return processCSS(css);
  612. }
  613. let context = null;
  614. async function startWatcher() {
  615. let changedContent = [];
  616. let configDependencies = [];
  617. let contextDependencies = new Set();
  618. let watcher = null;
  619. function refreshConfig() {
  620. env.DEBUG && console.time("Module dependencies");
  621. for (let file of configDependencies){
  622. delete require.cache[require.resolve(file)];
  623. }
  624. if (configPath) {
  625. configDependencies = (0, _getModuleDependencies.default)(configPath).map(({ file })=>file);
  626. for (let dependency of configDependencies){
  627. contextDependencies.add(dependency);
  628. }
  629. }
  630. env.DEBUG && console.timeEnd("Module dependencies");
  631. return resolveConfig();
  632. }
  633. let [beforePlugins, afterPlugins] = includePostCss ? await loadPostCssPlugins() : loadBuiltinPostcssPlugins();
  634. let plugins = [
  635. ...beforePlugins,
  636. "__TAILWIND_PLUGIN_POSITION__",
  637. !args["--minify"] && formatNodes,
  638. ...afterPlugins,
  639. !args["--no-autoprefixer"] && (()=>{
  640. // Try to load a local `autoprefixer` version first
  641. try {
  642. return require("autoprefixer");
  643. } catch {}
  644. return (0, _indexJs.lazyAutoprefixer)();
  645. })(),
  646. args["--minify"] && (()=>{
  647. let options = {
  648. preset: [
  649. "default",
  650. {
  651. cssDeclarationSorter: false
  652. }
  653. ]
  654. };
  655. // Try to load a local `cssnano` version first
  656. try {
  657. return require("cssnano");
  658. } catch {}
  659. return (0, _indexJs.lazyCssnano)()(options);
  660. })(),
  661. ].filter(Boolean);
  662. async function rebuild(config) {
  663. env.DEBUG && console.time("Finished in");
  664. let tailwindPlugin = ()=>{
  665. return {
  666. postcssPlugin: "tailwindcss",
  667. Once (root, { result }) {
  668. env.DEBUG && console.time("Compiling CSS");
  669. (0, _processTailwindFeatures.default)(({ createContext })=>{
  670. console.error();
  671. console.error("Rebuilding...");
  672. return ()=>{
  673. if (context !== null) {
  674. context.changedContent = changedContent.splice(0);
  675. return context;
  676. }
  677. env.DEBUG && console.time("Creating context");
  678. context = createContext(config, changedContent.splice(0));
  679. env.DEBUG && console.timeEnd("Creating context");
  680. return context;
  681. };
  682. })(root, result);
  683. env.DEBUG && console.timeEnd("Compiling CSS");
  684. }
  685. };
  686. };
  687. tailwindPlugin.postcss = true;
  688. let tailwindPluginIdx = plugins.indexOf("__TAILWIND_PLUGIN_POSITION__");
  689. let copy = plugins.slice();
  690. copy.splice(tailwindPluginIdx, 1, tailwindPlugin);
  691. let postcss = loadPostcss();
  692. let processor = postcss(copy);
  693. function processCSS(css) {
  694. let start = process.hrtime.bigint();
  695. return Promise.resolve().then(()=>output ? _fs.default.promises.mkdir(_path.default.dirname(output), {
  696. recursive: true
  697. }) : null).then(()=>processor.process(css, {
  698. from: input,
  699. to: output
  700. })).then(async (result)=>{
  701. for (let message of result.messages){
  702. if (message.type === "dependency") {
  703. contextDependencies.add(message.file);
  704. }
  705. }
  706. watcher.add([
  707. ...contextDependencies
  708. ]);
  709. if (!output) {
  710. return process.stdout.write(result.css);
  711. }
  712. return Promise.all([
  713. outputFile(output, result.css),
  714. result.map && outputFile(output + ".map", result.map.toString()),
  715. ].filter(Boolean));
  716. }).then(()=>{
  717. let end = process.hrtime.bigint();
  718. console.error("Done in", (end - start) / BigInt(1e6) + "ms.");
  719. }).catch((err)=>{
  720. if (err.name === "CssSyntaxError") {
  721. console.error(err.toString());
  722. } else {
  723. console.error(err);
  724. }
  725. });
  726. }
  727. let css = await (()=>{
  728. // Piping in data, let's drain the stdin
  729. if (input === "-") {
  730. return drainStdin();
  731. }
  732. // Input file has been provided
  733. if (input) {
  734. return _fs.default.readFileSync(_path.default.resolve(input), "utf8");
  735. }
  736. // No input file provided, fallback to default atrules
  737. return "@tailwind base; @tailwind components; @tailwind utilities";
  738. })();
  739. let result = await processCSS(css);
  740. env.DEBUG && console.timeEnd("Finished in");
  741. return result;
  742. }
  743. let config = refreshConfig(configPath);
  744. if (input) {
  745. contextDependencies.add(_path.default.resolve(input));
  746. }
  747. watcher = _chokidar.default.watch([
  748. ...contextDependencies,
  749. ...extractFileGlobs(config)
  750. ], {
  751. usePolling: shouldPoll,
  752. interval: shouldPoll ? pollInterval : undefined,
  753. ignoreInitial: true,
  754. awaitWriteFinish: shouldCoalesceWriteEvents ? {
  755. stabilityThreshold: 50,
  756. pollInterval: pollInterval
  757. } : false
  758. });
  759. let chain = Promise.resolve();
  760. watcher.on("change", async (file)=>{
  761. if (contextDependencies.has(file)) {
  762. env.DEBUG && console.time("Resolve config");
  763. context = null;
  764. config = refreshConfig(configPath);
  765. env.DEBUG && console.timeEnd("Resolve config");
  766. env.DEBUG && console.time("Watch new files");
  767. let globs = extractFileGlobs(config);
  768. watcher.add(configDependencies);
  769. watcher.add(globs);
  770. env.DEBUG && console.timeEnd("Watch new files");
  771. chain = chain.then(async ()=>{
  772. changedContent.push(...getChangedContent(config));
  773. await rebuild(config);
  774. });
  775. } else {
  776. chain = chain.then(async ()=>{
  777. changedContent.push({
  778. content: _fs.default.readFileSync(_path.default.resolve(file), "utf8"),
  779. extension: _path.default.extname(file).slice(1)
  780. });
  781. await rebuild(config);
  782. });
  783. }
  784. });
  785. watcher.on("add", async (file)=>{
  786. chain = chain.then(async ()=>{
  787. changedContent.push({
  788. content: _fs.default.readFileSync(_path.default.resolve(file), "utf8"),
  789. extension: _path.default.extname(file).slice(1)
  790. });
  791. await rebuild(config);
  792. });
  793. });
  794. chain = chain.then(()=>{
  795. changedContent.push(...getChangedContent(config));
  796. return rebuild(config);
  797. });
  798. }
  799. if (shouldWatch) {
  800. /* Abort the watcher if stdin is closed to avoid zombie processes */ if (process.stdin.isTTY) {
  801. process.stdin.on("end", ()=>process.exit(0));
  802. process.stdin.resume();
  803. }
  804. startWatcher();
  805. } else {
  806. buildOnce();
  807. }
  808. }