index.mjs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. // src/index.ts
  2. import { resolve as resolve2 } from "path";
  3. import _debug2, { log } from "debug";
  4. import { createUtils } from "@windicss/plugin-utils";
  5. // ../shared/virtual-module.ts
  6. import { existsSync, promises as fs } from "fs";
  7. var MODULE_IDS = [/^virtual:windi(.*?)\.css/, /^windi(.*?)\.css/];
  8. var MODULE_ID_VIRTUAL_PREFIX = "/@windicss/windi";
  9. var MODULE_ID_VIRTUAL = /\/\@windicss\/windi-?(.*?)\.css/;
  10. var MODULE_ID_VIRTUAL_MODULES = [
  11. `${MODULE_ID_VIRTUAL_PREFIX}.css`,
  12. `${MODULE_ID_VIRTUAL_PREFIX}-base.css`,
  13. `${MODULE_ID_VIRTUAL_PREFIX}-utilities.css`,
  14. `${MODULE_ID_VIRTUAL_PREFIX}-components.css`
  15. ];
  16. function createVirtualModuleLoader(ctx) {
  17. return {
  18. resolveId(id) {
  19. if (id.startsWith(MODULE_ID_VIRTUAL_PREFIX))
  20. return id;
  21. for (const idRegex of MODULE_IDS) {
  22. const match = id.match(idRegex);
  23. if (match)
  24. return `${MODULE_ID_VIRTUAL_PREFIX}${match[1]}.css`;
  25. }
  26. return null;
  27. },
  28. async load(id) {
  29. const match = id.match(MODULE_ID_VIRTUAL);
  30. if (match) {
  31. await ctx.utils.scan();
  32. await ctx.utils.waitLocks();
  33. ctx.utils.files.map((id2) => this.addWatchFile(id2));
  34. const layer = match[1] || void 0;
  35. const css = await ctx.utils.generateCSS(layer);
  36. return css;
  37. }
  38. },
  39. async watchChange(id, change) {
  40. if (change.event === "delete" || !existsSync(id))
  41. return;
  42. if (!ctx.utils.isDetectTarget(id))
  43. return;
  44. ctx.utils.lock(async () => {
  45. const content = await fs.readFile(id, "utf-8");
  46. await ctx.utils.extractFile(content, id, true);
  47. });
  48. }
  49. };
  50. }
  51. // src/devtools.ts
  52. import fs2 from "fs";
  53. import { dirname, resolve } from "path";
  54. import { fileURLToPath } from "url";
  55. import _debug from "debug";
  56. // src/constants.ts
  57. var NAME = "vite-plugin-windicss";
  58. // src/modules.ts
  59. function getChangedModuleNames(utils) {
  60. if (utils.hasPending)
  61. utils.buildPendingStyles();
  62. const moduleNames = [
  63. `${MODULE_ID_VIRTUAL_PREFIX}.css`
  64. ];
  65. Object.entries(utils.layersMeta).forEach(([name, meta]) => {
  66. if (meta.cssCache == null)
  67. moduleNames.push(`${MODULE_ID_VIRTUAL_PREFIX}-${name}.css`);
  68. });
  69. return moduleNames;
  70. }
  71. function getCssModules(server, names = MODULE_ID_VIRTUAL_MODULES) {
  72. return names.map((name) => server.moduleGraph.getModuleById(name)).filter(Boolean);
  73. }
  74. function invalidateCssModules(server, modules = getCssModules(server)) {
  75. return modules.forEach((m) => server.moduleGraph.invalidateModule(m));
  76. }
  77. function sendHmrReload(server, modules = getCssModules(server)) {
  78. const timestamp = +Date.now();
  79. server.ws.send({
  80. type: "update",
  81. updates: modules.map((m) => ({
  82. acceptedPath: m.id || m.file,
  83. path: m.id || m.file,
  84. timestamp,
  85. type: "js-update"
  86. }))
  87. });
  88. }
  89. function reloadChangedCssModules(server, utils) {
  90. const cssModules = getCssModules(server, getChangedModuleNames(utils));
  91. invalidateCssModules(server, cssModules);
  92. sendHmrReload(server, cssModules);
  93. return cssModules;
  94. }
  95. // src/devtools.ts
  96. var _dirname = typeof __dirname !== "undefined" ? __dirname : dirname(fileURLToPath(import.meta.url));
  97. var debug = {
  98. devtools: _debug(`${NAME}:devtools`)
  99. };
  100. var DEVTOOLS_MODULE_ID = "virtual:windi-devtools";
  101. var MOCK_CLASSES_MODULE_ID = "virtual:windi-mock-classes";
  102. var MOCK_CLASSES_PATH = "/@windicss/mock-classes";
  103. var DEVTOOLS_PATH = "/@windicss/devtools";
  104. var MODULES_MAP = {
  105. [DEVTOOLS_MODULE_ID]: DEVTOOLS_PATH,
  106. [MOCK_CLASSES_MODULE_ID]: MOCK_CLASSES_PATH
  107. };
  108. var POST_PATH = "/@windicss-devtools-update";
  109. function getBodyJson(req) {
  110. return new Promise((resolve3, reject) => {
  111. let body = "";
  112. req.on("data", (chunk) => body += chunk);
  113. req.on("error", reject);
  114. req.on("end", () => {
  115. try {
  116. resolve3(JSON.parse(body) || {});
  117. } catch (e) {
  118. reject(e);
  119. }
  120. });
  121. });
  122. }
  123. function createDevtoolsPlugin(ctx) {
  124. let config;
  125. let server;
  126. let clientCode = "";
  127. function updateCSS() {
  128. if (!server)
  129. return;
  130. const names = getChangedModuleNames(ctx.utils);
  131. const modules = getCssModules(server, names);
  132. invalidateCssModules(server, modules);
  133. sendHmrReload(server, modules);
  134. }
  135. function toClass(name) {
  136. return `.${ctx.utils.processor.e(name)}{}`;
  137. }
  138. function getMockClassesInjector() {
  139. const completions = ctx.utils.getCompletions();
  140. const comment = "/* Windi CSS mock class names for devtools auto-completion */\n";
  141. const css = [
  142. ...completions.color,
  143. ...completions.static
  144. ].map(toClass).join("");
  145. return `
  146. const style = document.createElement('style')
  147. style.setAttribute('type', 'text/css')
  148. style.innerHTML = ${JSON.stringify(comment + css)}
  149. document.head.prepend(style)
  150. `;
  151. }
  152. return [
  153. {
  154. name: `${NAME}:devtools`,
  155. configResolved(_config) {
  156. config = _config;
  157. },
  158. configureServer(_server) {
  159. server = _server;
  160. server.middlewares.use(async (req, res, next) => {
  161. if (req.url !== POST_PATH)
  162. return next();
  163. try {
  164. const data = await getBodyJson(req);
  165. const type = data?.type;
  166. debug.devtools(data);
  167. let changed = false;
  168. switch (type) {
  169. case "add-classes":
  170. changed = ctx.utils.addClasses(data.data || []);
  171. }
  172. if (changed)
  173. updateCSS();
  174. res.statusCode = 200;
  175. } catch (e) {
  176. console.error(e);
  177. res.statusCode = 500;
  178. }
  179. res.end();
  180. });
  181. },
  182. resolveId(id) {
  183. return MODULES_MAP[id];
  184. },
  185. async load(id, options) {
  186. if (options?.ssr && [DEVTOOLS_PATH, MOCK_CLASSES_PATH].includes(id))
  187. return "";
  188. if (id === DEVTOOLS_PATH) {
  189. if (!clientCode) {
  190. clientCode = [
  191. await fs2.promises.readFile(resolve(_dirname, "client.mjs"), "utf-8"),
  192. `import('${MOCK_CLASSES_MODULE_ID}')`
  193. ].join("\n").replace("__POST_PATH__", (config.server?.origin ?? "") + POST_PATH);
  194. }
  195. return config.command === "build" ? "" : clientCode;
  196. } else if (id === MOCK_CLASSES_PATH) {
  197. return getMockClassesInjector();
  198. }
  199. }
  200. }
  201. ];
  202. }
  203. // src/index.ts
  204. export * from "@windicss/plugin-utils";
  205. var debug2 = {
  206. hmr: _debug2(`${NAME}:hmr`),
  207. css: _debug2(`${NAME}:transform:css`),
  208. group: _debug2(`${NAME}:transform:group`),
  209. alias: _debug2(`${NAME}:transform:alias`),
  210. memory: _debug2(`${NAME}:memory`)
  211. };
  212. function VitePluginWindicss(userOptions = {}, utilsOptions = {}) {
  213. let utils;
  214. let server;
  215. let viteConfig;
  216. const plugins = [];
  217. plugins.push({
  218. name: `${NAME}:alias`,
  219. enforce: "pre",
  220. configResolved(_config) {
  221. viteConfig = _config;
  222. },
  223. async transform(code, id) {
  224. await utils.ensureInit();
  225. if (!utils.isDetectTarget(id))
  226. return;
  227. debug2.alias(id);
  228. return utils.transformAlias(code, !!viteConfig.build.sourcemap);
  229. }
  230. });
  231. if (userOptions.transformGroups !== false) {
  232. plugins.push({
  233. name: `${NAME}:groups`,
  234. enforce: "pre",
  235. async transform(code, id) {
  236. await utils.ensureInit();
  237. if (!utils.isDetectTarget(id))
  238. return;
  239. debug2.group(id);
  240. return utils.transformGroups(code, !!viteConfig.build.sourcemap);
  241. }
  242. });
  243. }
  244. plugins.push({
  245. name: NAME,
  246. get api() {
  247. return utils;
  248. }
  249. });
  250. plugins.push({
  251. name: `${NAME}:entry`,
  252. enforce: "post",
  253. configureServer(_server) {
  254. server = _server;
  255. },
  256. async configResolved(_config) {
  257. utils = utilsOptions.utils ?? createUtils(userOptions, {
  258. name: NAME,
  259. root: _config.root,
  260. onConfigurationError(e) {
  261. if (_config.command === "build") {
  262. throw e;
  263. } else {
  264. console.error(`[${NAME}] Error on loading configurations`);
  265. console.error(e);
  266. }
  267. },
  268. ...utilsOptions
  269. });
  270. await utils.ensureInit();
  271. },
  272. ...createVirtualModuleLoader({ get utils() {
  273. return utils;
  274. } })
  275. });
  276. let _cssReloadTask;
  277. function reloadCssModules(server2) {
  278. clearTimeout(_cssReloadTask);
  279. _cssReloadTask = setTimeout(() => {
  280. reloadChangedCssModules(server2, utils);
  281. }, 1);
  282. }
  283. plugins.push({
  284. name: `${NAME}:hmr`,
  285. apply: "serve",
  286. enforce: "pre",
  287. async configureServer(_server) {
  288. server = _server;
  289. await utils.ensureInit();
  290. if (utils.configFilePath)
  291. server.watcher.add(utils.configFilePath);
  292. const supportsGlobs = server.config.server.watch?.disableGlobbing === false;
  293. server.watcher.add(supportsGlobs ? utils.globs : await utils.getFiles());
  294. },
  295. async handleHotUpdate({ server: server2, file, read }) {
  296. if (resolve2(file) === utils.configFilePath) {
  297. debug2.hmr(`config file changed: ${file}`);
  298. await utils.init();
  299. setTimeout(() => {
  300. log("configure file changed, reloading");
  301. server2.ws.send({ type: "full-reload" });
  302. }, 0);
  303. return getCssModules(server2);
  304. }
  305. if (!utils.isDetectTarget(file))
  306. return;
  307. utils.extractFile(await read(), file, true).then((changed) => {
  308. if (changed) {
  309. debug2.hmr(`refreshed by ${file}`);
  310. reloadCssModules(server2);
  311. }
  312. });
  313. }
  314. });
  315. const { transformCSS: transformCSSOptions = true } = userOptions;
  316. const transformCSS = (code, id) => utils.transformCSS(code, id, {
  317. onLayerUpdated() {
  318. if (server)
  319. reloadCssModules(server);
  320. }
  321. });
  322. if (transformCSSOptions === true) {
  323. plugins.push({
  324. name: `${NAME}:css`,
  325. async transform(code, id) {
  326. await utils.ensureInit();
  327. if (!utils.isCssTransformTarget(id) || id.startsWith(MODULE_ID_VIRTUAL_PREFIX))
  328. return;
  329. debug2.css(id);
  330. code = transformCSS(code, id);
  331. if (viteConfig.build.sourcemap) {
  332. return {
  333. code: transformCSS(code, id),
  334. map: { mappings: "" }
  335. };
  336. } else {
  337. return code;
  338. }
  339. }
  340. });
  341. } else if (typeof transformCSSOptions === "string") {
  342. plugins.push({
  343. name: `${NAME}:css`,
  344. enforce: transformCSSOptions,
  345. transform(code, id) {
  346. if (!utils.isCssTransformTarget(id) || id.startsWith(MODULE_ID_VIRTUAL_PREFIX))
  347. return;
  348. debug2.css(id, transformCSSOptions);
  349. code = transformCSS(code, id);
  350. if (viteConfig.build.sourcemap) {
  351. return {
  352. code: transformCSS(code, id),
  353. map: { mappings: "" }
  354. };
  355. } else {
  356. return code;
  357. }
  358. }
  359. });
  360. }
  361. plugins.push({
  362. name: `${NAME}:css:svelte`,
  363. api: {
  364. sveltePreprocess: {
  365. style({ content, id }) {
  366. return {
  367. code: transformCSS(content, id)
  368. };
  369. }
  370. }
  371. }
  372. });
  373. plugins.push(...createDevtoolsPlugin({
  374. get utils() {
  375. return utils;
  376. }
  377. }));
  378. return plugins;
  379. }
  380. var src_default = VitePluginWindicss;
  381. export {
  382. src_default as default
  383. };