From 19e75eafbd12d68fadbda717f8e37c0b3f5eabc5 Mon Sep 17 00:00:00 2001 From: enzonotario Date: Sat, 21 Feb 2026 22:26:31 -0300 Subject: [PATCH 1/3] feat(vite-plugin-escss): soporte para compilar assets .escss --- packages/vite-plugin-escss/package.json | 9 +- packages/vite-plugin-escss/pnpm-lock.yaml | 273 ++++++++++++++++++ packages/vite-plugin-escss/src/index.ts | 83 +++++- .../test/fixtures/estilos.escss | 4 + .../test/vite-plugin-escss.test.ts | 109 +++++++ 5 files changed, 469 insertions(+), 9 deletions(-) create mode 100644 packages/vite-plugin-escss/test/fixtures/estilos.escss create mode 100644 packages/vite-plugin-escss/test/vite-plugin-escss.test.ts diff --git a/packages/vite-plugin-escss/package.json b/packages/vite-plugin-escss/package.json index f6b07402..2debf3da 100644 --- a/packages/vite-plugin-escss/package.json +++ b/packages/vite-plugin-escss/package.json @@ -1,10 +1,12 @@ { "name": "@es-js/vite-plugin-escss", - "version": "0.0.1", + "version": "0.0.2", "description": "Vite Plugin para EsCSS", "scripts": { "build": "unbuild", - "prepublishOnly": "pnpm run build" + "prepublishOnly": "pnpm run build", + "test": "vitest", + "test:run": "vitest run" }, "repository": { "type": "git", @@ -26,7 +28,8 @@ "@es-js/escss": "workspace:*", "@types/node": "^20.14.9", "typescript": "^5.5.2", - "unbuild": "^2.0.0" + "unbuild": "^2.0.0", + "vitest": "^2.1.9" }, "type": "module", "exports": { diff --git a/packages/vite-plugin-escss/pnpm-lock.yaml b/packages/vite-plugin-escss/pnpm-lock.yaml index 1c82caa7..eee9750d 100644 --- a/packages/vite-plugin-escss/pnpm-lock.yaml +++ b/packages/vite-plugin-escss/pnpm-lock.yaml @@ -24,6 +24,9 @@ importers: unbuild: specifier: ^2.0.0 version: 2.0.0(typescript@5.9.3) + vitest: + specifier: ^2.1.9 + version: 2.1.9(@types/node@20.19.30) packages: @@ -740,11 +743,44 @@ packages: '@types/resolve@1.20.2': resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} + '@vitest/expect@2.1.9': + resolution: {integrity: sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw==} + + '@vitest/mocker@2.1.9': + resolution: {integrity: sha512-tVL6uJgoUdi6icpxmdrn5YNo3g3Dxv+IHJBr0GXHaEdTcw3F+cPKnsXFhli6nO+f/6SDKPHEK1UN+k+TQv0Ehg==} + peerDependencies: + msw: ^2.4.9 + vite: ^5.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@2.1.9': + resolution: {integrity: sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==} + + '@vitest/runner@2.1.9': + resolution: {integrity: sha512-ZXSSqTFIrzduD63btIfEyOmNcBmQvgOVsPNPe0jYtESiXkhd8u2erDLnMxmGrDCwHCCHE7hxwRDCT3pt0esT4g==} + + '@vitest/snapshot@2.1.9': + resolution: {integrity: sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ==} + + '@vitest/spy@2.1.9': + resolution: {integrity: sha512-E1B35FwzXXTs9FHNK6bDszs7mtydNi5MIfUWpceJ8Xbfb1gBMscAnwLbEu+B44ed6W3XjL9/ehLPHR1fkf1KLQ==} + + '@vitest/utils@2.1.9': + resolution: {integrity: sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==} + acorn@8.15.0: resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} engines: {node: '>=0.4.0'} hasBin: true + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + autoprefixer@10.4.23: resolution: {integrity: sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA==} engines: {node: ^10 || ^12 || >=14} @@ -774,16 +810,28 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + caniuse-api@3.0.0: resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} caniuse-lite@1.0.30001764: resolution: {integrity: sha512-9JGuzl2M+vPL+pz70gtMF9sHdMFbY9FJaQBi186cHKH3pSzDvzoUJUPV6fqiKIMyXbud9ZLg4F3Yza1vJ1+93g==} + chai@5.3.3: + resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} + engines: {node: '>=18'} + chalk@5.6.2: resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + check-error@2.1.3: + resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==} + engines: {node: '>= 16'} + citty@0.1.6: resolution: {integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==} @@ -864,6 +912,10 @@ packages: supports-color: optional: true + deep-eql@5.0.2: + resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} + engines: {node: '>=6'} + deepmerge@4.3.1: resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} engines: {node: '>=0.10.0'} @@ -895,6 +947,9 @@ packages: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} + es-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + esbuild@0.19.12: resolution: {integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==} engines: {node: '>=12'} @@ -917,6 +972,13 @@ packages: estree-walker@2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + expect-type@1.3.0: + resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} + engines: {node: '>=12.0.0'} + fast-glob@3.3.3: resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} engines: {node: '>=8.6.0'} @@ -1042,6 +1104,9 @@ packages: lodash.uniq@4.5.0: resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} + loupe@3.2.1: + resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} + lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} @@ -1114,6 +1179,10 @@ packages: pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + pathval@2.0.1: + resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} + engines: {node: '>= 14.16'} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -1363,6 +1432,9 @@ packages: engines: {node: '>=10'} hasBin: true + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + slash@4.0.0: resolution: {integrity: sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==} engines: {node: '>=12'} @@ -1371,6 +1443,12 @@ packages: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + std-env@3.10.0: + resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + stylehacks@7.0.7: resolution: {integrity: sha512-bJkD0JkEtbRrMFtwgpJyBbFIwfDDONQ1Ov3sDLZQP8HuJ73kBOyx66H4bOcAbVWmnfLdvQ0AJwXxOMkpujcO6g==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} @@ -1386,10 +1464,28 @@ packages: engines: {node: '>=16'} hasBin: true + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + tinyglobby@0.2.15: resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} engines: {node: '>=12.0.0'} + tinypool@1.1.1: + resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} + engines: {node: ^18.0.0 || >=20.0.0} + + tinyrainbow@1.2.0: + resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==} + engines: {node: '>=14.0.0'} + + tinyspy@3.0.2: + resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} + engines: {node: '>=14.0.0'} + to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -1427,6 +1523,11 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + vite-node@2.1.9: + resolution: {integrity: sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + vite@5.4.21: resolution: {integrity: sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==} engines: {node: ^18.0.0 || >=20.0.0} @@ -1458,6 +1559,36 @@ packages: terser: optional: true + vitest@2.1.9: + resolution: {integrity: sha512-MSmPM9REYqDGBI8439mA4mWhV5sKmDlBKWIYbA3lRb2PTHACE0mgKwA8yQ2xq9vxDTuk4iPrECBAEW2aoFXY0Q==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/node': ^18.0.0 || >=20.0.0 + '@vitest/browser': 2.1.9 + '@vitest/ui': 2.1.9 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} @@ -1941,8 +2072,50 @@ snapshots: '@types/resolve@1.20.2': {} + '@vitest/expect@2.1.9': + dependencies: + '@vitest/spy': 2.1.9 + '@vitest/utils': 2.1.9 + chai: 5.3.3 + tinyrainbow: 1.2.0 + + '@vitest/mocker@2.1.9(vite@5.4.21(@types/node@20.19.30))': + dependencies: + '@vitest/spy': 2.1.9 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 5.4.21(@types/node@20.19.30) + + '@vitest/pretty-format@2.1.9': + dependencies: + tinyrainbow: 1.2.0 + + '@vitest/runner@2.1.9': + dependencies: + '@vitest/utils': 2.1.9 + pathe: 1.1.2 + + '@vitest/snapshot@2.1.9': + dependencies: + '@vitest/pretty-format': 2.1.9 + magic-string: 0.30.21 + pathe: 1.1.2 + + '@vitest/spy@2.1.9': + dependencies: + tinyspy: 3.0.2 + + '@vitest/utils@2.1.9': + dependencies: + '@vitest/pretty-format': 2.1.9 + loupe: 3.2.1 + tinyrainbow: 1.2.0 + acorn@8.15.0: {} + assertion-error@2.0.1: {} + autoprefixer@10.4.23(postcss@8.5.6): dependencies: browserslist: 4.28.1 @@ -1974,6 +2147,8 @@ snapshots: node-releases: 2.0.27 update-browserslist-db: 1.2.3(browserslist@4.28.1) + cac@6.7.14: {} + caniuse-api@3.0.0: dependencies: browserslist: 4.28.1 @@ -1983,8 +2158,18 @@ snapshots: caniuse-lite@1.0.30001764: {} + chai@5.3.3: + dependencies: + assertion-error: 2.0.1 + check-error: 2.1.3 + deep-eql: 5.0.2 + loupe: 3.2.1 + pathval: 2.0.1 + chalk@5.6.2: {} + check-error@2.1.3: {} + citty@0.1.6: dependencies: consola: 3.4.2 @@ -2079,6 +2264,8 @@ snapshots: dependencies: ms: 2.1.3 + deep-eql@5.0.2: {} + deepmerge@4.3.1: {} defu@6.1.4: {} @@ -2109,6 +2296,8 @@ snapshots: entities@4.5.0: {} + es-module-lexer@1.7.0: {} + esbuild@0.19.12: optionalDependencies: '@esbuild/aix-ppc64': 0.19.12 @@ -2193,6 +2382,12 @@ snapshots: estree-walker@2.0.2: {} + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.8 + + expect-type@1.3.0: {} + fast-glob@3.3.3: dependencies: '@nodelib/fs.stat': 2.0.5 @@ -2295,6 +2490,8 @@ snapshots: lodash.uniq@4.5.0: {} + loupe@3.2.1: {} + lru-cache@5.1.1: dependencies: yallist: 3.1.1 @@ -2365,6 +2562,8 @@ snapshots: pathe@2.0.3: {} + pathval@2.0.1: {} + picocolors@1.1.1: {} picomatch@2.3.1: {} @@ -2616,10 +2815,16 @@ snapshots: semver@7.7.3: {} + siginfo@2.0.0: {} + slash@4.0.0: {} source-map-js@1.2.1: {} + stackback@0.0.2: {} + + std-env@3.10.0: {} + stylehacks@7.0.7(postcss@8.5.6): dependencies: browserslist: 4.28.1 @@ -2638,11 +2843,21 @@ snapshots: picocolors: 1.1.1 sax: 1.4.4 + tinybench@2.9.0: {} + + tinyexec@0.3.2: {} + tinyglobby@0.2.15: dependencies: fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 + tinypool@1.1.1: {} + + tinyrainbow@1.2.0: {} + + tinyspy@3.0.2: {} + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 @@ -2707,6 +2922,24 @@ snapshots: util-deprecate@1.0.2: {} + vite-node@2.1.9(@types/node@20.19.30): + dependencies: + cac: 6.7.14 + debug: 4.4.3 + es-module-lexer: 1.7.0 + pathe: 1.1.2 + vite: 5.4.21(@types/node@20.19.30) + transitivePeerDependencies: + - '@types/node' + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + vite@5.4.21(@types/node@20.19.30): dependencies: esbuild: 0.21.5 @@ -2716,6 +2949,46 @@ snapshots: '@types/node': 20.19.30 fsevents: 2.3.3 + vitest@2.1.9(@types/node@20.19.30): + dependencies: + '@vitest/expect': 2.1.9 + '@vitest/mocker': 2.1.9(vite@5.4.21(@types/node@20.19.30)) + '@vitest/pretty-format': 2.1.9 + '@vitest/runner': 2.1.9 + '@vitest/snapshot': 2.1.9 + '@vitest/spy': 2.1.9 + '@vitest/utils': 2.1.9 + chai: 5.3.3 + debug: 4.4.3 + expect-type: 1.3.0 + magic-string: 0.30.21 + pathe: 1.1.2 + std-env: 3.10.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinypool: 1.1.1 + tinyrainbow: 1.2.0 + vite: 5.4.21(@types/node@20.19.30) + vite-node: 2.1.9(@types/node@20.19.30) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 20.19.30 + transitivePeerDependencies: + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + wrappy@1.0.2: {} yallist@3.1.1: {} diff --git a/packages/vite-plugin-escss/src/index.ts b/packages/vite-plugin-escss/src/index.ts index 89c6f7b8..19c82902 100644 --- a/packages/vite-plugin-escss/src/index.ts +++ b/packages/vite-plugin-escss/src/index.ts @@ -1,7 +1,7 @@ import { compile } from '@es-js/escss' import type { Plugin, ViteDevServer } from 'vite' -import { readFileSync, existsSync } from 'fs' -import { resolve } from 'path' +import { readFileSync, existsSync, writeFileSync, mkdirSync } from 'fs' +import path from 'path' export interface EsCSSOptions { /** @@ -40,6 +40,9 @@ export default function EsCSS(options: EsCSSOptions = {}): Plugin { const { enabled = true } = options let root = process.cwd() + const ESCSS_QUERY = '?from=escss' + const escssAssetsToEmit: Array<{ fileName: string; source: string }> = [] + return { name: 'vite-plugin-escss', enforce: 'pre', @@ -48,7 +51,35 @@ export default function EsCSS(options: EsCSSOptions = {}): Plugin { root = config.root }, - // Handle direct .escss file requests (e.g., tags) + buildStart() { + escssAssetsToEmit.length = 0 + }, + + // Resolver: .escss → módulo .css para que el build lo trate como CSS y no como asset + resolveId(sourceId: string, importer?: string) { + if (!enabled || !sourceId.endsWith('.escss')) return null + const base = importer ? path.dirname(importer) : root + const fullPath = path.resolve(base, sourceId) + if (!existsSync(fullPath)) return null + return fullPath.replace(/\.escss$/, '.css') + ESCSS_QUERY + }, + + // Cargar: leer .escss, compilar a CSS y devolver como contenido del módulo + load(id: string) { + if (!enabled || !id.includes(ESCSS_QUERY)) return null + const escssPath = id.replace(ESCSS_QUERY, '').replace(/\.css$/, '.escss') + if (!existsSync(escssPath)) return null + try { + const raw = readFileSync(escssPath, 'utf-8') + const compiled = compile(raw, { from: 'escss', to: 'css' }) + return { code: compiled, map: null } + } catch (e) { + console.error(`Failed to compile EsCSS: ${escssPath}`, e) + return null + } + }, + + // Handle direct .escss file requests (e.g., tags) en dev configureServer(server: ViteDevServer) { if (!enabled) return @@ -57,7 +88,7 @@ export default function EsCSS(options: EsCSSOptions = {}): Plugin { if (!url.endsWith('.escss')) return next() const cleanUrl = url.split('?')[0] - const filePath = resolve(root, cleanUrl.slice(1)) + const filePath = path.resolve(root, cleanUrl.slice(1)) if (!existsSync(filePath)) return next() @@ -73,13 +104,53 @@ export default function EsCSS(options: EsCSSOptions = {}): Plugin { }) }, - // Handle .escss imports in JS/TS + // Handle .escss imports en JS/TS (por si se importa directamente) transform(raw: string, id: string) { if (!enabled || !/\.escss$/.test(id)) return - + // No transformar si ya viene de nuestro load (id con ?from=escss) + if (id.includes(ESCSS_QUERY)) return const compiled = compile(raw, { from: 'escss', to: 'css' }) return { code: compiled, map: null } }, + + // Build: compilar .escss a CSS; guardar para emitir en writeBundle y actualizar + transformIndexHtml: { + order: 'pre', + handler(html: string) { + if (!enabled) return html + return html.replace( + /]*rel=["']stylesheet["'])[^>]*href=["']([^"']+\.escss)["'][^>]*\/?>/gi, + (match, href: string) => { + const cleanHref = href.replace(/^\//, '').split('?')[0] + const filePath = path.resolve(root, cleanHref) + if (!existsSync(filePath)) return match + try { + const raw = readFileSync(filePath, 'utf-8') + const compiled = compile(raw, { from: 'escss', to: 'css' }) + const baseName = path.basename(cleanHref, '.escss') + '.css' + const fileName = `assets/${baseName}` + escssAssetsToEmit.push({ fileName, source: compiled }) + return `` + } catch (e) { + console.error(`Failed to compile EsCSS: ${filePath}`, e) + return match + } + }, + ) + }, + }, + + // Build: escribir los .css compilados a disco (a nivel archivo) + writeBundle(outputOptions: { dir?: string }) { + if (!enabled || escssAssetsToEmit.length === 0) return + const outDir = outputOptions.dir + if (!outDir) return + for (const { fileName, source } of escssAssetsToEmit) { + const fullPath = path.join(outDir, fileName) + mkdirSync(path.dirname(fullPath), { recursive: true }) + writeFileSync(fullPath, source, 'utf-8') + } + }, } } diff --git a/packages/vite-plugin-escss/test/fixtures/estilos.escss b/packages/vite-plugin-escss/test/fixtures/estilos.escss new file mode 100644 index 00000000..d8a452dc --- /dev/null +++ b/packages/vite-plugin-escss/test/fixtures/estilos.escss @@ -0,0 +1,4 @@ +.clase { + mostrar: flex; + fondo-color: blanco; +} diff --git a/packages/vite-plugin-escss/test/vite-plugin-escss.test.ts b/packages/vite-plugin-escss/test/vite-plugin-escss.test.ts new file mode 100644 index 00000000..6a4f1232 --- /dev/null +++ b/packages/vite-plugin-escss/test/vite-plugin-escss.test.ts @@ -0,0 +1,109 @@ +import path from 'path' +import { fileURLToPath } from 'url' +import { describe, it, expect, beforeAll } from 'vitest' +import EsCSS from '../src/index' + +const __dirname = path.dirname(fileURLToPath(import.meta.url)) +const fixturesDir = path.join(__dirname, 'fixtures') + +describe('vite-plugin-escss', () => { + let plugin: ReturnType + + beforeAll(() => { + plugin = EsCSS() + if (Array.isArray(plugin)) { + throw new Error('Plugin returned array, expected single plugin') + } + plugin.configResolved?.({ root: fixturesDir } as any) + }) + + it('transform: compila .escss a CSS', () => { + const result = plugin.transform!( + `.tarjeta { mostrar: flex; fondo-color: azul; }`, + '/ruta/archivo.escss', + ) + expect(result).toBeDefined() + expect(result?.code).toContain('display: flex') + expect(result?.code).toContain('background-color') + expect(result?.code).not.toContain('mostrar') + expect(result?.code).not.toContain('fondo-color') + }) + + it('transform: no transforma si id tiene ?from=escss', () => { + const result = plugin.transform!( + `.x { display: flex; }`, + '/ruta/estilos.css?from=escss', + ) + expect(result).toBeUndefined() + }) + + it('resolveId: resuelve .escss a .css?from=escss cuando el archivo existe', () => { + const importer = path.join(fixturesDir, 'index.html') + const resolved = plugin.resolveId!('./estilos.escss', importer) + expect(resolved).toBeTruthy() + expect(resolved).toContain('.css') + expect(resolved).toContain('?from=escss') + expect(resolved).not.toContain('.escss') + }) + + it('resolveId: devuelve null si el archivo .escss no existe', () => { + const importer = path.join(fixturesDir, 'index.html') + const resolved = plugin.resolveId!('./no-existe.escss', importer) + expect(resolved).toBeNull() + }) + + it('load: devuelve CSS compilado para id con ?from=escss', () => { + const resolvedId = path.join(fixturesDir, 'estilos.css') + '?from=escss' + const result = plugin.load!(resolvedId) + expect(result).toBeDefined() + expect(result).toHaveProperty('code') + expect((result as { code: string }).code).toContain('display: flex') + expect((result as { code: string }).code).toContain('background-color') + expect((result as { code: string }).code).not.toContain('mostrar') + }) + + it('transformIndexHtml: sustituye link .escss por link al .css (archivo emitido en writeBundle)', () => { + const handler = + typeof plugin.transformIndexHtml === 'object' && + 'handler' in plugin.transformIndexHtml + ? plugin.transformIndexHtml.handler + : plugin.transformIndexHtml + expect(handler).toBeTypeOf('function') + + const html = ` + + + + + + +` + const result = (handler as (html: string) => string)(html) + expect(result).not.toContain('href="./estilos.escss"') + expect(result).toContain('rel="stylesheet"') + expect(result).toContain('href="/assets/estilos.css"') + expect(result).not.toContain('