Aucune description

plugin.js 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. Object.defineProperty(exports, "createProcessor", {
  6. enumerable: true,
  7. get: function() {
  8. return createProcessor;
  9. }
  10. });
  11. const _path = /*#__PURE__*/ _interop_require_default(require("path"));
  12. const _fs = /*#__PURE__*/ _interop_require_default(require("fs"));
  13. const _postcssloadconfig = /*#__PURE__*/ _interop_require_default(require("postcss-load-config"));
  14. const _lilconfig = require("lilconfig");
  15. const _plugins = /*#__PURE__*/ _interop_require_default(require("postcss-load-config/src/plugins" // Little bit scary, looking at private/internal API
  16. ));
  17. const _options = /*#__PURE__*/ _interop_require_default(require("postcss-load-config/src/options" // Little bit scary, looking at private/internal API
  18. ));
  19. const _processTailwindFeatures = /*#__PURE__*/ _interop_require_default(require("../../../processTailwindFeatures"));
  20. const _deps = require("./deps");
  21. const _utils = require("./utils");
  22. const _sharedState = require("../../../lib/sharedState");
  23. const _resolveConfig = /*#__PURE__*/ _interop_require_default(require("../../../../resolveConfig"));
  24. const _content = require("../../../lib/content");
  25. const _watching = require("./watching");
  26. const _fastglob = /*#__PURE__*/ _interop_require_default(require("fast-glob"));
  27. const _findAtConfigPath = require("../../../lib/findAtConfigPath");
  28. const _log = /*#__PURE__*/ _interop_require_default(require("../../../util/log"));
  29. const _loadconfig = require("../../../lib/load-config");
  30. const _getModuleDependencies = /*#__PURE__*/ _interop_require_default(require("../../../lib/getModuleDependencies"));
  31. function _interop_require_default(obj) {
  32. return obj && obj.__esModule ? obj : {
  33. default: obj
  34. };
  35. }
  36. /**
  37. *
  38. * @param {string} [customPostCssPath ]
  39. * @returns
  40. */ async function loadPostCssPlugins(customPostCssPath) {
  41. let config = customPostCssPath ? await (async ()=>{
  42. let file = _path.default.resolve(customPostCssPath);
  43. // Implementation, see: https://unpkg.com/browse/postcss-load-config@3.1.0/src/index.js
  44. // @ts-ignore
  45. let { config ={} } = await (0, _lilconfig.lilconfig)("postcss").load(file);
  46. if (typeof config === "function") {
  47. config = config();
  48. } else {
  49. config = Object.assign({}, config);
  50. }
  51. if (!config.plugins) {
  52. config.plugins = [];
  53. }
  54. return {
  55. file,
  56. plugins: (0, _plugins.default)(config, file),
  57. options: (0, _options.default)(config, file)
  58. };
  59. })() : await (0, _postcssloadconfig.default)();
  60. let configPlugins = config.plugins;
  61. let configPluginTailwindIdx = configPlugins.findIndex((plugin)=>{
  62. if (typeof plugin === "function" && plugin.name === "tailwindcss") {
  63. return true;
  64. }
  65. if (typeof plugin === "object" && plugin !== null && plugin.postcssPlugin === "tailwindcss") {
  66. return true;
  67. }
  68. return false;
  69. });
  70. let beforePlugins = configPluginTailwindIdx === -1 ? [] : configPlugins.slice(0, configPluginTailwindIdx);
  71. let afterPlugins = configPluginTailwindIdx === -1 ? configPlugins : configPlugins.slice(configPluginTailwindIdx + 1);
  72. return [
  73. beforePlugins,
  74. afterPlugins,
  75. config.options
  76. ];
  77. }
  78. function loadBuiltinPostcssPlugins() {
  79. let postcss = (0, _deps.loadPostcss)();
  80. let IMPORT_COMMENT = "__TAILWIND_RESTORE_IMPORT__: ";
  81. return [
  82. [
  83. (root)=>{
  84. root.walkAtRules("import", (rule)=>{
  85. if (rule.params.slice(1).startsWith("tailwindcss/")) {
  86. rule.after(postcss.comment({
  87. text: IMPORT_COMMENT + rule.params
  88. }));
  89. rule.remove();
  90. }
  91. });
  92. },
  93. (0, _deps.loadPostcssImport)(),
  94. (root)=>{
  95. root.walkComments((rule)=>{
  96. if (rule.text.startsWith(IMPORT_COMMENT)) {
  97. rule.after(postcss.atRule({
  98. name: "import",
  99. params: rule.text.replace(IMPORT_COMMENT, "")
  100. }));
  101. rule.remove();
  102. }
  103. });
  104. }
  105. ],
  106. [],
  107. {}
  108. ];
  109. }
  110. let state = {
  111. /** @type {any} */ context: null,
  112. /** @type {ReturnType<typeof createWatcher> | null} */ watcher: null,
  113. /** @type {{content: string, extension: string}[]} */ changedContent: [],
  114. /** @type {{config: Config, dependencies: Set<string>, dispose: Function } | null} */ configBag: null,
  115. contextDependencies: new Set(),
  116. /** @type {import('../../lib/content.js').ContentPath[]} */ contentPaths: [],
  117. refreshContentPaths () {
  118. var _this_context;
  119. this.contentPaths = (0, _content.parseCandidateFiles)(this.context, (_this_context = this.context) === null || _this_context === void 0 ? void 0 : _this_context.tailwindConfig);
  120. },
  121. get config () {
  122. return this.context.tailwindConfig;
  123. },
  124. get contentPatterns () {
  125. return {
  126. all: this.contentPaths.map((contentPath)=>contentPath.pattern),
  127. dynamic: this.contentPaths.filter((contentPath)=>contentPath.glob !== undefined).map((contentPath)=>contentPath.pattern)
  128. };
  129. },
  130. loadConfig (configPath, content) {
  131. if (this.watcher && configPath) {
  132. this.refreshConfigDependencies();
  133. }
  134. let config = (0, _loadconfig.loadConfig)(configPath);
  135. let dependencies = (0, _getModuleDependencies.default)(configPath);
  136. this.configBag = {
  137. config,
  138. dependencies,
  139. dispose () {
  140. for (let file of dependencies){
  141. delete require.cache[require.resolve(file)];
  142. }
  143. }
  144. };
  145. // @ts-ignore
  146. this.configBag.config = (0, _resolveConfig.default)(this.configBag.config, {
  147. content: {
  148. files: []
  149. }
  150. });
  151. // Override content files if `--content` has been passed explicitly
  152. if ((content === null || content === void 0 ? void 0 : content.length) > 0) {
  153. this.configBag.config.content.files = content;
  154. }
  155. return this.configBag.config;
  156. },
  157. refreshConfigDependencies (configPath) {
  158. var _this_configBag;
  159. _sharedState.env.DEBUG && console.time("Module dependencies");
  160. (_this_configBag = this.configBag) === null || _this_configBag === void 0 ? void 0 : _this_configBag.dispose();
  161. _sharedState.env.DEBUG && console.timeEnd("Module dependencies");
  162. },
  163. readContentPaths () {
  164. let content = [];
  165. // Resolve globs from the content config
  166. // TODO: When we make the postcss plugin async-capable this can become async
  167. let files = _fastglob.default.sync(this.contentPatterns.all);
  168. for (let file of files){
  169. if (false) {
  170. content.push({
  171. file,
  172. extension: _path.default.extname(file).slice(1)
  173. });
  174. } else {
  175. content.push({
  176. content: _fs.default.readFileSync(_path.default.resolve(file), "utf8"),
  177. extension: _path.default.extname(file).slice(1)
  178. });
  179. }
  180. }
  181. // Resolve raw content in the tailwind config
  182. let rawContent = this.config.content.files.filter((file)=>{
  183. return file !== null && typeof file === "object";
  184. });
  185. for (let { raw: htmlContent , extension ="html" } of rawContent){
  186. content.push({
  187. content: htmlContent,
  188. extension
  189. });
  190. }
  191. return content;
  192. },
  193. getContext ({ createContext , cliConfigPath , root , result , content }) {
  194. if (this.context) {
  195. this.context.changedContent = this.changedContent.splice(0);
  196. return this.context;
  197. }
  198. _sharedState.env.DEBUG && console.time("Searching for config");
  199. var _findAtConfigPath1;
  200. let configPath = (_findAtConfigPath1 = (0, _findAtConfigPath.findAtConfigPath)(root, result)) !== null && _findAtConfigPath1 !== void 0 ? _findAtConfigPath1 : cliConfigPath;
  201. _sharedState.env.DEBUG && console.timeEnd("Searching for config");
  202. _sharedState.env.DEBUG && console.time("Loading config");
  203. let config = this.loadConfig(configPath, content);
  204. _sharedState.env.DEBUG && console.timeEnd("Loading config");
  205. _sharedState.env.DEBUG && console.time("Creating context");
  206. this.context = createContext(config, []);
  207. Object.assign(this.context, {
  208. userConfigPath: configPath
  209. });
  210. _sharedState.env.DEBUG && console.timeEnd("Creating context");
  211. _sharedState.env.DEBUG && console.time("Resolving content paths");
  212. this.refreshContentPaths();
  213. _sharedState.env.DEBUG && console.timeEnd("Resolving content paths");
  214. if (this.watcher) {
  215. _sharedState.env.DEBUG && console.time("Watch new files");
  216. this.watcher.refreshWatchedFiles();
  217. _sharedState.env.DEBUG && console.timeEnd("Watch new files");
  218. }
  219. for (let file of this.readContentPaths()){
  220. this.context.changedContent.push(file);
  221. }
  222. return this.context;
  223. }
  224. };
  225. async function createProcessor(args, cliConfigPath) {
  226. var _args_content;
  227. let postcss = (0, _deps.loadPostcss)();
  228. let input = args["--input"];
  229. let output = args["--output"];
  230. let includePostCss = args["--postcss"];
  231. let customPostCssPath = typeof args["--postcss"] === "string" ? args["--postcss"] : undefined;
  232. let [beforePlugins, afterPlugins, postcssOptions] = includePostCss ? await loadPostCssPlugins(customPostCssPath) : loadBuiltinPostcssPlugins();
  233. if (args["--purge"]) {
  234. _log.default.warn("purge-flag-deprecated", [
  235. "The `--purge` flag has been deprecated.",
  236. "Please use `--content` instead."
  237. ]);
  238. if (!args["--content"]) {
  239. args["--content"] = args["--purge"];
  240. }
  241. }
  242. var _args_content_split;
  243. let content = (_args_content_split = (_args_content = args["--content"]) === null || _args_content === void 0 ? void 0 : _args_content.split(/(?<!{[^}]+),/)) !== null && _args_content_split !== void 0 ? _args_content_split : [];
  244. let tailwindPlugin = ()=>{
  245. return {
  246. postcssPlugin: "tailwindcss",
  247. Once (root, { result }) {
  248. _sharedState.env.DEBUG && console.time("Compiling CSS");
  249. (0, _processTailwindFeatures.default)(({ createContext })=>{
  250. console.error();
  251. console.error("Rebuilding...");
  252. return ()=>{
  253. return state.getContext({
  254. createContext,
  255. cliConfigPath,
  256. root,
  257. result,
  258. content
  259. });
  260. };
  261. })(root, result);
  262. _sharedState.env.DEBUG && console.timeEnd("Compiling CSS");
  263. }
  264. };
  265. };
  266. tailwindPlugin.postcss = true;
  267. let plugins = [
  268. ...beforePlugins,
  269. tailwindPlugin,
  270. !args["--minify"] && _utils.formatNodes,
  271. ...afterPlugins
  272. ].filter(Boolean);
  273. /** @type {import('postcss').Processor} */ // @ts-ignore
  274. let processor = postcss(plugins);
  275. async function readInput() {
  276. // Piping in data, let's drain the stdin
  277. if (input === "-") {
  278. return (0, _utils.drainStdin)();
  279. }
  280. // Input file has been provided
  281. if (input) {
  282. return _fs.default.promises.readFile(_path.default.resolve(input), "utf8");
  283. }
  284. // No input file provided, fallback to default atrules
  285. return "@tailwind base; @tailwind components; @tailwind utilities";
  286. }
  287. async function build() {
  288. let start = process.hrtime.bigint();
  289. return readInput().then((css)=>processor.process(css, {
  290. ...postcssOptions,
  291. from: input,
  292. to: output
  293. })).then((result)=>(0, _deps.lightningcss)(!!args["--minify"], result)).then((result)=>{
  294. if (!state.watcher) {
  295. return result;
  296. }
  297. _sharedState.env.DEBUG && console.time("Recording PostCSS dependencies");
  298. for (let message of result.messages){
  299. if (message.type === "dependency") {
  300. state.contextDependencies.add(message.file);
  301. }
  302. }
  303. _sharedState.env.DEBUG && console.timeEnd("Recording PostCSS dependencies");
  304. // TODO: This needs to be in a different spot
  305. _sharedState.env.DEBUG && console.time("Watch new files");
  306. state.watcher.refreshWatchedFiles();
  307. _sharedState.env.DEBUG && console.timeEnd("Watch new files");
  308. return result;
  309. }).then((result)=>{
  310. if (!output) {
  311. process.stdout.write(result.css);
  312. return;
  313. }
  314. return Promise.all([
  315. (0, _utils.outputFile)(result.opts.to, result.css),
  316. result.map && (0, _utils.outputFile)(result.opts.to + ".map", result.map.toString())
  317. ]);
  318. }).then(()=>{
  319. let end = process.hrtime.bigint();
  320. console.error();
  321. console.error("Done in", (end - start) / BigInt(1e6) + "ms.");
  322. }).then(()=>{}, (err)=>{
  323. // TODO: If an initial build fails we can't easily pick up any PostCSS dependencies
  324. // that were collected before the error occurred
  325. // The result is not stored on the error so we have to store it externally
  326. // and pull the messages off of it here somehow
  327. // This results in a less than ideal DX because the watcher will not pick up
  328. // changes to imported CSS if one of them caused an error during the initial build
  329. // If you fix it and then save the main CSS file so there's no error
  330. // The watcher will start watching the imported CSS files and will be
  331. // resilient to future errors.
  332. if (state.watcher) {
  333. console.error(err);
  334. } else {
  335. return Promise.reject(err);
  336. }
  337. });
  338. }
  339. /**
  340. * @param {{file: string, content(): Promise<string>, extension: string}[]} changes
  341. */ async function parseChanges(changes) {
  342. return Promise.all(changes.map(async (change)=>({
  343. content: await change.content(),
  344. extension: change.extension
  345. })));
  346. }
  347. if (input !== undefined && input !== "-") {
  348. state.contextDependencies.add(_path.default.resolve(input));
  349. }
  350. return {
  351. build,
  352. watch: async ()=>{
  353. state.watcher = (0, _watching.createWatcher)(args, {
  354. state,
  355. /**
  356. * @param {{file: string, content(): Promise<string>, extension: string}[]} changes
  357. */ async rebuild (changes) {
  358. let needsNewContext = changes.some((change)=>{
  359. var _state_configBag;
  360. return ((_state_configBag = state.configBag) === null || _state_configBag === void 0 ? void 0 : _state_configBag.dependencies.has(change.file)) || state.contextDependencies.has(change.file);
  361. });
  362. if (needsNewContext) {
  363. state.context = null;
  364. } else {
  365. for (let change of (await parseChanges(changes))){
  366. state.changedContent.push(change);
  367. }
  368. }
  369. return build();
  370. }
  371. });
  372. await build();
  373. }
  374. };
  375. }