From b92e8deb806d0dd4c3890052191993f0c0d269e6 Mon Sep 17 00:00:00 2001 From: g45t345rt Date: Wed, 24 Mar 2021 13:01:48 -0400 Subject: [PATCH 1/4] compiled code --- .gitignore | 1 - dist/index.esm.js | 119 ++++++++++++++++++++++++++++++++++++++ dist/index.js | 142 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 261 insertions(+), 1 deletion(-) create mode 100644 dist/index.esm.js create mode 100644 dist/index.js diff --git a/.gitignore b/.gitignore index 0c63dcb..7bd55b2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ node_modules -dist build yarn-error.log test/dist diff --git a/dist/index.esm.js b/dist/index.esm.js new file mode 100644 index 0000000..224e51a --- /dev/null +++ b/dist/index.esm.js @@ -0,0 +1,119 @@ +import {ensureDir, readFile, writeFile} from "fs-extra"; +import {TextDecoder} from "util"; +import path from "path"; +import tmp from "tmp"; +import postcss from "postcss"; +import postcssModules from "postcss-modules"; +import less from "less"; +import stylus from "stylus"; +import resolveFile from "resolve-file"; +const postCSSPlugin = ({ + plugins = [], + modules = true, + rootDir = process.cwd() +}) => ({ + name: "postcss2", + setup(build) { + const tmpDirPath = tmp.dirSync().name, modulesMap = []; + const modulesPlugin = postcssModules({ + generateScopedName: "[name]__[local]___[hash:base64:5]", + ...typeof modules !== "boolean" ? modules : {}, + getJSON(filepath, json, outpath) { + modulesMap.push({ + path: filepath, + map: json + }); + if (typeof modules !== "boolean" && typeof modules.getJSON === "function") + return modules.getJSON(filepath, json, outpath); + } + }); + build.onResolve({filter: /.\.(css|sass|scss|less|styl)$/}, async (args) => { + if (args.namespace !== "file" && args.namespace !== "") + return; + let sourceFullPath = resolveFile(args.path); + if (!sourceFullPath) + sourceFullPath = path.resolve(args.resolveDir, args.path); + const sourceExt = path.extname(sourceFullPath); + const sourceBaseName = path.basename(sourceFullPath, sourceExt); + const sourceDir = path.dirname(sourceFullPath); + const sourceRelDir = path.relative(path.dirname(rootDir), sourceDir); + const isModule = sourceBaseName.match(/\.module$/); + const tmpDir = path.resolve(tmpDirPath, sourceRelDir); + let tmpFilePath = path.resolve(tmpDir, `${Date.now()}-${sourceBaseName}.css`); + if (args.kind === "entry-point") + tmpFilePath = path.resolve(tmpDir, `${sourceBaseName}.css`); + await ensureDir(tmpDir); + const fileContent = await readFile(sourceFullPath); + let css = sourceExt === ".css" ? fileContent : ""; + if (sourceExt === ".sass" || sourceExt === ".scss") + css = (await renderSass({file: sourceFullPath})).css.toString(); + if (sourceExt === ".styl") + css = await renderStylus(new TextDecoder().decode(fileContent), { + filename: sourceFullPath + }); + if (sourceExt === ".less") + css = (await less.render(new TextDecoder().decode(fileContent), { + filename: sourceFullPath, + rootpath: path.dirname(args.path) + })).css; + const result = await postcss(isModule ? [modulesPlugin, ...plugins] : plugins).process(css, { + from: sourceFullPath, + to: tmpFilePath + }); + await writeFile(tmpFilePath, result.css); + return { + namespace: isModule ? "postcss-module" : "file", + path: tmpFilePath, + pluginData: { + originalPath: sourceFullPath + } + }; + }); + build.onLoad({filter: /.*/, namespace: "postcss-module"}, async (args) => { + const mod = modulesMap.find(({path: path2}) => path2 === args?.pluginData?.originalPath), resolveDir = path.dirname(args.path); + return { + resolveDir, + contents: `import ${JSON.stringify(args.path)}; +export default ${JSON.stringify(mod && mod.map ? mod.map : {})};` + }; + }); + } +}); +function renderSass(options) { + return new Promise((resolve, reject) => { + getSassImpl().render(options, (e, res) => { + if (e) + reject(e); + else + resolve(res); + }); + }); +} +function renderStylus(str, options) { + return new Promise((resolve, reject) => { + stylus.render(str, options, (e, res) => { + if (e) + reject(e); + else + resolve(res); + }); + }); +} +function getSassImpl() { + let impl = "sass"; + try { + require.resolve("sass"); + } catch { + try { + require.resolve("node-sass"); + impl = "node-sass"; + } catch { + throw new Error('Please install "sass" or "node-sass" package'); + } + } + return require(impl); +} +var src_default = postCSSPlugin; +export { + src_default as default +}; diff --git a/dist/index.js b/dist/index.js new file mode 100644 index 0000000..d2883f5 --- /dev/null +++ b/dist/index.js @@ -0,0 +1,142 @@ +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __markAsModule = (target) => __defProp(target, "__esModule", {value: true}); +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, {get: all[name], enumerable: true}); +}; +var __exportStar = (target, module2, desc) => { + if (module2 && typeof module2 === "object" || typeof module2 === "function") { + for (let key of __getOwnPropNames(module2)) + if (!__hasOwnProp.call(target, key) && key !== "default") + __defProp(target, key, {get: () => module2[key], enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable}); + } + return target; +}; +var __toModule = (module2) => { + return __exportStar(__markAsModule(__defProp(module2 != null ? __create(__getProtoOf(module2)) : {}, "default", module2 && module2.__esModule && "default" in module2 ? {get: () => module2.default, enumerable: true} : {value: module2, enumerable: true})), module2); +}; +__markAsModule(exports); +__export(exports, { + default: () => src_default +}); +var import_fs_extra = __toModule(require("fs-extra")); +var import_util = __toModule(require("util")); +var import_path = __toModule(require("path")); +var import_tmp = __toModule(require("tmp")); +var import_postcss2 = __toModule(require("postcss")); +var import_postcss_modules = __toModule(require("postcss-modules")); +var import_less = __toModule(require("less")); +var import_stylus = __toModule(require("stylus")); +var import_resolve_file = __toModule(require("resolve-file")); +const postCSSPlugin = ({ + plugins = [], + modules = true, + rootDir = process.cwd() +}) => ({ + name: "postcss2", + setup(build) { + const tmpDirPath = import_tmp.default.dirSync().name, modulesMap = []; + const modulesPlugin = (0, import_postcss_modules.default)({ + generateScopedName: "[name]__[local]___[hash:base64:5]", + ...typeof modules !== "boolean" ? modules : {}, + getJSON(filepath, json, outpath) { + modulesMap.push({ + path: filepath, + map: json + }); + if (typeof modules !== "boolean" && typeof modules.getJSON === "function") + return modules.getJSON(filepath, json, outpath); + } + }); + build.onResolve({filter: /.\.(css|sass|scss|less|styl)$/}, async (args) => { + if (args.namespace !== "file" && args.namespace !== "") + return; + let sourceFullPath = (0, import_resolve_file.default)(args.path); + if (!sourceFullPath) + sourceFullPath = import_path.default.resolve(args.resolveDir, args.path); + const sourceExt = import_path.default.extname(sourceFullPath); + const sourceBaseName = import_path.default.basename(sourceFullPath, sourceExt); + const sourceDir = import_path.default.dirname(sourceFullPath); + const sourceRelDir = import_path.default.relative(import_path.default.dirname(rootDir), sourceDir); + const isModule = sourceBaseName.match(/\.module$/); + const tmpDir = import_path.default.resolve(tmpDirPath, sourceRelDir); + let tmpFilePath = import_path.default.resolve(tmpDir, `${Date.now()}-${sourceBaseName}.css`); + if (args.kind === "entry-point") + tmpFilePath = import_path.default.resolve(tmpDir, `${sourceBaseName}.css`); + await (0, import_fs_extra.ensureDir)(tmpDir); + const fileContent = await (0, import_fs_extra.readFile)(sourceFullPath); + let css = sourceExt === ".css" ? fileContent : ""; + if (sourceExt === ".sass" || sourceExt === ".scss") + css = (await renderSass({file: sourceFullPath})).css.toString(); + if (sourceExt === ".styl") + css = await renderStylus(new import_util.TextDecoder().decode(fileContent), { + filename: sourceFullPath + }); + if (sourceExt === ".less") + css = (await import_less.default.render(new import_util.TextDecoder().decode(fileContent), { + filename: sourceFullPath, + rootpath: import_path.default.dirname(args.path) + })).css; + const result = await (0, import_postcss2.default)(isModule ? [modulesPlugin, ...plugins] : plugins).process(css, { + from: sourceFullPath, + to: tmpFilePath + }); + await (0, import_fs_extra.writeFile)(tmpFilePath, result.css); + return { + namespace: isModule ? "postcss-module" : "file", + path: tmpFilePath, + pluginData: { + originalPath: sourceFullPath + } + }; + }); + build.onLoad({filter: /.*/, namespace: "postcss-module"}, async (args) => { + const mod = modulesMap.find(({path: path2}) => path2 === args?.pluginData?.originalPath), resolveDir = import_path.default.dirname(args.path); + return { + resolveDir, + contents: `import ${JSON.stringify(args.path)}; +export default ${JSON.stringify(mod && mod.map ? mod.map : {})};` + }; + }); + } +}); +function renderSass(options) { + return new Promise((resolve, reject) => { + getSassImpl().render(options, (e, res) => { + if (e) + reject(e); + else + resolve(res); + }); + }); +} +function renderStylus(str, options) { + return new Promise((resolve, reject) => { + import_stylus.default.render(str, options, (e, res) => { + if (e) + reject(e); + else + resolve(res); + }); + }); +} +function getSassImpl() { + let impl = "sass"; + try { + require.resolve("sass"); + } catch { + try { + require.resolve("node-sass"); + impl = "node-sass"; + } catch { + throw new Error('Please install "sass" or "node-sass" package'); + } + } + return require(impl); +} +var src_default = postCSSPlugin; From 81d1e9ea779f0ba5169ac06d9a943ba08314573b Mon Sep 17 00:00:00 2001 From: g45t345rt Date: Wed, 31 Mar 2021 13:56:28 -0400 Subject: [PATCH 2/4] New watch css files with simple file edit test --- dist/index.esm.js | 1 + dist/index.js | 1 + package.json | 6 +++--- src/index.ts | 1 + test/index.js | 35 +++++++++++++++++++++++++++++++++- test/styles/preprocessors.less | 6 ++---- test/styles/watch.css | 3 +++ test/tests/watch.ts | 1 + yarn.lock | 24 +++++++++++------------ 9 files changed, 58 insertions(+), 20 deletions(-) create mode 100644 test/styles/watch.css create mode 100644 test/tests/watch.ts diff --git a/dist/index.esm.js b/dist/index.esm.js index 224e51a..172a837 100644 --- a/dist/index.esm.js +++ b/dist/index.esm.js @@ -64,6 +64,7 @@ const postCSSPlugin = ({ return { namespace: isModule ? "postcss-module" : "file", path: tmpFilePath, + watchFiles: [sourceFullPath], pluginData: { originalPath: sourceFullPath } diff --git a/dist/index.js b/dist/index.js index d2883f5..cd5588d 100644 --- a/dist/index.js +++ b/dist/index.js @@ -90,6 +90,7 @@ const postCSSPlugin = ({ return { namespace: isModule ? "postcss-module" : "file", path: tmpFilePath, + watchFiles: [sourceFullPath], pluginData: { originalPath: sourceFullPath } diff --git a/package.json b/package.json index 35fea74..1ec2c69 100644 --- a/package.json +++ b/package.json @@ -39,16 +39,16 @@ }, "devDependencies": { "@types/chai": "^4.2.15", - "@types/fs-extra": "^9.0.8", + "@types/fs-extra": "^9.0.9", "@types/less": "^3.0.2", "@types/mocha": "^8.2.2", - "@types/node": "^14.14.35", + "@types/node": "^14.14.37", "@types/sass": "^1.16.0", "@types/stylus": "^0.48.33", "@types/tmp": "^0.2.0", "chai": "^4.3.4", "cross-env": "^7.0.3", - "esbuild": "^0.9.6", + "esbuild": "^0.11.2", "mocha": "^8.3.2", "normalize.css": "^8.0.1", "prettier": "^2.2.1", diff --git a/src/index.ts b/src/index.ts index 721df76..11f67b4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -117,6 +117,7 @@ const postCSSPlugin = ({ return { namespace: isModule ? "postcss-module" : "file", path: tmpFilePath, + watchFiles: [sourceFullPath], pluginData: { originalPath: sourceFullPath } diff --git a/test/index.js b/test/index.js index be96fd3..53c2a56 100644 --- a/test/index.js +++ b/test/index.js @@ -1,7 +1,8 @@ const autoprefixer = require("autoprefixer"), { build } = require("esbuild"), postCSS = require("../dist"), - { assert } = require("chai"); + { assert } = require("chai"), + fs = require("fs"); describe("PostCSS esbuild tests", () => { it("Works with basic CSS imports", (done) => { @@ -44,6 +45,38 @@ describe("PostCSS esbuild tests", () => { }) .catch(done); }); + it("Works while waching css files", (done) => { + let notTriggerTimeout = null; + build({ + entryPoints: ["tests/watch.ts"], + bundle: true, + outdir: "dist", + watch: { + onRebuild: (error, result) => { + notTriggerTimeout = null; + if (error) return done(error); + assert(result); + done(); + } + }, + plugins: [ + postCSS.default({ + plugins: [autoprefixer] + }) + ] + }) + .then(() => { + // test if modifying the css actually triggers the onRebuild event + const data = `.Test { display: block; }`; + fs.writeFile("./styles/watch.css", data, (err) => { + if (err) return done(err); + notTriggerTimeout = setTimeout(() => { + done("Watch file not triggered!"); + }, 1000); + }); + }) + .catch(() => process.exit(1)); + }); }); function test(entryPoint) { diff --git a/test/styles/preprocessors.less b/test/styles/preprocessors.less index 5bcf682..55d7059 100644 --- a/test/styles/preprocessors.less +++ b/test/styles/preprocessors.less @@ -1,5 +1,3 @@ -@test: 2em; - -.LessClass { - height: @test; +.Test { + display: block; } diff --git a/test/styles/watch.css b/test/styles/watch.css new file mode 100644 index 0000000..55d7059 --- /dev/null +++ b/test/styles/watch.css @@ -0,0 +1,3 @@ +.Test { + display: block; +} diff --git a/test/tests/watch.ts b/test/tests/watch.ts new file mode 100644 index 0000000..4a34187 --- /dev/null +++ b/test/tests/watch.ts @@ -0,0 +1 @@ +import "../styles/watch.css"; diff --git a/yarn.lock b/yarn.lock index 6ec96ca..0eb2e7b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7,10 +7,10 @@ "resolved" "https://registry.npmjs.org/@types/chai/-/chai-4.2.15.tgz" "version" "4.2.15" -"@types/fs-extra@^9.0.8": - "integrity" "sha512-bnlTVTwq03Na7DpWxFJ1dvnORob+Otb8xHyUqUWhqvz/Ksg8+JXPlR52oeMSZ37YEOa5PyccbgUNutiQdi13TA==" - "resolved" "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.8.tgz" - "version" "9.0.8" +"@types/fs-extra@^9.0.9": + "integrity" "sha512-5TqDycCl0oMzwzd1cIjSJWMKMvLCDVErle4ZTjU4EmHDURR/+yZghe6GDHMCpHtcVfq0x0gMoOM546/5TbYHrg==" + "resolved" "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.9.tgz" + "version" "9.0.9" dependencies: "@types/node" "*" @@ -24,10 +24,10 @@ "resolved" "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.2.tgz" "version" "8.2.2" -"@types/node@*", "@types/node@^14.14.35": - "integrity" "sha512-Lt+wj8NVPx0zUmUwumiVXapmaLUcAk3yPuHCFVXras9k5VT9TdhJqKqGVUQCD60OTMCl0qxJ57OiTL0Mic3Iag==" - "resolved" "https://registry.npmjs.org/@types/node/-/node-14.14.35.tgz" - "version" "14.14.35" +"@types/node@*", "@types/node@^14.14.37": + "integrity" "sha512-XYmBiy+ohOR4Lh5jE379fV2IU+6Jn4g5qASinhitfyO71b/sCo6MKsMLF5tc7Zf2CE8hViVQyYSobJNke8OvUw==" + "resolved" "https://registry.npmjs.org/@types/node/-/node-14.14.37.tgz" + "version" "14.14.37" "@types/sass@^1.16.0": "integrity" "sha512-2XZovu4NwcqmtZtsBR5XYLw18T8cBCnU2USFHTnYLLHz9fkhnoEMoDsqShJIOFsFhn5aJHjweiUUdTrDGujegA==" @@ -374,10 +374,10 @@ dependencies: "prr" "~1.0.1" -"esbuild@^0.9.6": - "integrity" "sha512-F6vASxU0wT/Davt9aj2qtDwDNSkQxh9VbyO56M7PDWD+D/Vgq/rmUDGDQo7te76W5auauVojjnQr/wTu3vpaUA==" - "resolved" "https://registry.npmjs.org/esbuild/-/esbuild-0.9.6.tgz" - "version" "0.9.6" +"esbuild@^0.11.2": + "integrity" "sha512-8d5FCQrR+juXC2u9zjTQ3+IYiuFuaWyKYwmApFJLquTrYNbk36H/+MkRQeTuOJg7IjUchRX2Ulwo1zRYXZ1pUg==" + "resolved" "https://registry.npmjs.org/esbuild/-/esbuild-0.11.2.tgz" + "version" "0.11.2" "escalade@^3.1.1": "integrity" "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" From d798528ba690a1871b870017e25a621c4e355588 Mon Sep 17 00:00:00 2001 From: g45t345rt Date: Wed, 31 Mar 2021 14:48:29 -0400 Subject: [PATCH 3/4] Fix map not updating when using watch files --- dist/index.esm.js | 13 +++++++++---- dist/index.js | 13 +++++++++---- src/index.ts | 16 +++++++++++----- 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/dist/index.esm.js b/dist/index.esm.js index 172a837..86823ac 100644 --- a/dist/index.esm.js +++ b/dist/index.esm.js @@ -19,10 +19,15 @@ const postCSSPlugin = ({ generateScopedName: "[name]__[local]___[hash:base64:5]", ...typeof modules !== "boolean" ? modules : {}, getJSON(filepath, json, outpath) { - modulesMap.push({ - path: filepath, - map: json - }); + const mapIndex = modulesMap.findIndex((m) => m.path === filepath); + if (mapIndex !== -1) { + modulesMap[mapIndex].map = json; + } else { + modulesMap.push({ + path: filepath, + map: json + }); + } if (typeof modules !== "boolean" && typeof modules.getJSON === "function") return modules.getJSON(filepath, json, outpath); } diff --git a/dist/index.js b/dist/index.js index cd5588d..dcd4b95 100644 --- a/dist/index.js +++ b/dist/index.js @@ -45,10 +45,15 @@ const postCSSPlugin = ({ generateScopedName: "[name]__[local]___[hash:base64:5]", ...typeof modules !== "boolean" ? modules : {}, getJSON(filepath, json, outpath) { - modulesMap.push({ - path: filepath, - map: json - }); + const mapIndex = modulesMap.findIndex((m) => m.path === filepath); + if (mapIndex !== -1) { + modulesMap[mapIndex].map = json; + } else { + modulesMap.push({ + path: filepath, + map: json + }); + } if (typeof modules !== "boolean" && typeof modules.getJSON === "function") return modules.getJSON(filepath, json, outpath); } diff --git a/src/index.ts b/src/index.ts index 11f67b4..4d20acb 100644 --- a/src/index.ts +++ b/src/index.ts @@ -43,10 +43,16 @@ const postCSSPlugin = ({ generateScopedName: "[name]__[local]___[hash:base64:5]", ...(typeof modules !== "boolean" ? modules : {}), getJSON(filepath, json, outpath) { - modulesMap.push({ - path: filepath, - map: json - }); + // Make sure to replace json map instead of pushing new map everytime with edit file on watch + const mapIndex = modulesMap.findIndex((m) => m.path === filepath); + if (mapIndex !== -1) { + modulesMap[mapIndex].map = json; + } else { + modulesMap.push({ + path: filepath, + map: json + }); + } if ( typeof modules !== "boolean" && @@ -62,7 +68,7 @@ const postCSSPlugin = ({ // Namespace is empty when using CSS as an entrypoint if (args.namespace !== "file" && args.namespace !== "") return; - // Resolve files also from node_modules (ex: npm normalize.css) + // Resolve files from node_modules (ex: npm install normalize.css) let sourceFullPath = resolveFile(args.path); if (!sourceFullPath) sourceFullPath = path.resolve(args.resolveDir, args.path); From 81baa37c85e10d0f57b89a616f77f96a6cfe00d8 Mon Sep 17 00:00:00 2001 From: g45t345rt Date: Wed, 31 Mar 2021 14:52:10 -0400 Subject: [PATCH 4/4] Bump version to 0.0.8 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1ec2c69..d5c715d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "esbuild-plugin-postcss2", - "version": "0.0.7", + "version": "0.0.8", "description": "Use postcss with esbuild", "repository": "https://github.com/martonlederer/esbuild-plugin-postcss2", "author": "Marton Lederer ",