diff --git a/.gitignore b/.gitignore index 0f30646..80b39dd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ -# Ignore npm modules and src folder -node_modules -# src -# tsconfig.json +# Ignore npm modules and src folder +node_modules +# src +# tsconfig.json test \ No newline at end of file diff --git a/README.md b/README.md index 0a483eb..ec06f0e 100644 --- a/README.md +++ b/README.md @@ -1,114 +1,114 @@ -# Less watch for .less files - -This is a super-light npm package that watches .less files and actively compiles them into .css files - ---- - -## Prerequisites - -You need `less` npm package insalled. you can add it your project by running: - -```bash -npm install less -``` - -## Installation - -To install this package simply run: - -```bash -npm install lessc-watcher -``` - -## How to use - -There are few different ways to run your less compiler - -### Basic usage - -```bash -npx lessc-watcher --src ./folder --dst ./dist/less.css -``` - -This traverses the `--src` folder and searches for a `main.less` file. This file serves as the source for your bundled `.css` file. If you want to target a specific file, use: - -```bash -npx lessc-watcher --src ./folder/src.less --dst ./dist/less.css -``` - -Or you can just watch an entire folder. In this case less-watch will searc for an entry file named `main.less`. Without this entry file, your compiler wouldn't work. - -```bash -npx lessc-watcher --src ./folder --dst ./dist/less.css -``` - -**_NOTE:_** If you only provide a destination path, without specifying the exact file name in `.css`, your files will be compiled to a css file named `_main.css`. This is done to prevent conflicts with another possible `main.css` file. - -### Adding more source folders - -You can add more source folders by using a comma(`,`) separator. Example: - -```bash -npx lessc-watcher --src ./folder-1,./folder-2 --dst ./dist/folder-1.css,./dist/folder-2.css -``` - -**_NOTE:_** Your `--dst` input must match the number of comma-separated folders in your `src` input. - -## Advanced Features - -lessc-watcher has more advanced features to better fine-tune your files - -### Ignore files - -Ignore files/folders by enlosing the names in braces. Eg `(general).less` - -### Compile specific files - -If you're watching an entire folder, you can compile specific files in that folder to a stanalone file. Example if you create a file named `[test].less` in your watch directory, in your distribution directory there will be an extra file named `test.css`. - -## Using a `lesscw.config.json` file - -You can use a `lesscw.config.json` file to add your files instead of using the CLI interface. For this to work your `lesscw.config.json` file must be located in the root directory of your project, and it must contain at least one `src` and one `dst` entry - -### Basic Config JSON - -```json -{ - "src": "./folder", - "dst": "./dist/less.css" -} -``` - -This works the same as running `npx lessc-watcher --src ./folder --dst ./dist/less.css` in your terminal. Instead you only need to run: - -```bash -npx lessc-watcher -``` - -### Using multiple sources and destinations - -```json -{ - "src": ["./folder", "./folder-2", "./folder/sub-folder/admin.less"], - "dst": ["./dist/less.css", "./dist/folder-2/less.css", "./dist/sub-folder.css"] -} -``` - -Like the CLI paradigm, the number of paths in the `src` array must match the `dst array`. And note that you can use multiple different folders with multiple different destinations. - -### Using a `srcDst` cofiguration - -ALternative to the `src` and `dst` paths, you can provide a `srcDst` key instead, this will contain an array of key-value pairs as follows - -```json -{ - "srcDst": [ - { - "src": "./folder", - "dst": "./folder-2" - } - ] -} -``` - -**_NOTE:_** If you provide a config file and still add `--src` and `--dst` arguments in your terminal, the terminal source and distributuion arguments will be ignored. +# Less watch for .less files + +This is a super-light npm package that watches .less files and actively compiles them into .css files + +--- + +## Prerequisites + +You need `less` npm package insalled. you can add it your project by running: + +```bash +npm install less +``` + +## Installation + +To install this package simply run: + +```bash +npm install lessc-watcher +``` + +## How to use + +There are few different ways to run your less compiler + +### Basic usage + +```bash +npx lessc-watcher --src ./folder --dst ./dist/less.css +``` + +This traverses the `--src` folder and searches for a `main.less` file. This file serves as the source for your bundled `.css` file. If you want to target a specific file, use: + +```bash +npx lessc-watcher --src ./folder/src.less --dst ./dist/less.css +``` + +Or you can just watch an entire folder. In this case less-watch will searc for an entry file named `main.less`. Without this entry file, your compiler wouldn't work. + +```bash +npx lessc-watcher --src ./folder --dst ./dist/less.css +``` + +**_NOTE:_** If you only provide a destination path, without specifying the exact file name in `.css`, your files will be compiled to a css file named `_main.css`. This is done to prevent conflicts with another possible `main.css` file. + +### Adding more source folders + +You can add more source folders by using a comma(`,`) separator. Example: + +```bash +npx lessc-watcher --src ./folder-1,./folder-2 --dst ./dist/folder-1.css,./dist/folder-2.css +``` + +**_NOTE:_** Your `--dst` input must match the number of comma-separated folders in your `src` input. + +## Advanced Features + +lessc-watcher has more advanced features to better fine-tune your files + +### Ignore files + +Ignore files/folders by enlosing the names in braces. Eg `(general).less` + +### Compile specific files + +If you're watching an entire folder, you can compile specific files in that folder to a stanalone file. Example if you create a file named `[test].less` in your watch directory, in your distribution directory there will be an extra file named `test.css`. + +## Using a `lesscw.config.json` file + +You can use a `lesscw.config.json` file to add your files instead of using the CLI interface. For this to work your `lesscw.config.json` file must be located in the root directory of your project, and it must contain at least one `src` and one `dst` entry + +### Basic Config JSON + +```json +{ + "src": "./folder", + "dst": "./dist/less.css" +} +``` + +This works the same as running `npx lessc-watcher --src ./folder --dst ./dist/less.css` in your terminal. Instead you only need to run: + +```bash +npx lessc-watcher +``` + +### Using multiple sources and destinations + +```json +{ + "src": ["./folder", "./folder-2", "./folder/sub-folder/admin.less"], + "dst": ["./dist/less.css", "./dist/folder-2/less.css", "./dist/sub-folder.css"] +} +``` + +Like the CLI paradigm, the number of paths in the `src` array must match the `dst array`. And note that you can use multiple different folders with multiple different destinations. + +### Using a `srcDst` cofiguration + +ALternative to the `src` and `dst` paths, you can provide a `srcDst` key instead, this will contain an array of key-value pairs as follows + +```json +{ + "srcDst": [ + { + "src": "./folder", + "dst": "./folder-2" + } + ] +} +``` + +**_NOTE:_** If you provide a config file and still add `--src` and `--dst` arguments in your terminal, the terminal source and distributuion arguments will be ignored. diff --git a/dist/index.js b/dist/index.js index e603266..fe5abe5 100644 --- a/dist/index.js +++ b/dist/index.js @@ -18,18 +18,31 @@ const grabSrcDisStrings = () => { } try { const configObject = JSON.parse(fs_1.default.readFileSync("./lesscw.config.json", "utf-8")); - if ((configObject === null || configObject === void 0 ? void 0 : configObject.src) && (configObject === null || configObject === void 0 ? void 0 : configObject.dst) && typeof configObject.src === "string" && typeof configObject.dst === "string") { + if ((configObject === null || configObject === void 0 ? void 0 : configObject.src) && + (configObject === null || configObject === void 0 ? void 0 : configObject.dst) && + typeof configObject.src === "string" && + typeof configObject.dst === "string") { srcArray = configObject.src.split(","); dstArray = configObject.dst.split(","); } - else if ((configObject === null || configObject === void 0 ? void 0 : configObject.src) && (configObject === null || configObject === void 0 ? void 0 : configObject.dst) && typeof configObject.src === "object" && typeof configObject.dst === "object" && Array.isArray(configObject.src) && Array.isArray(configObject.dst)) { + else if ((configObject === null || configObject === void 0 ? void 0 : configObject.src) && + (configObject === null || configObject === void 0 ? void 0 : configObject.dst) && + typeof configObject.src === "object" && + typeof configObject.dst === "object" && + Array.isArray(configObject.src) && + Array.isArray(configObject.dst)) { srcArray = configObject.src; dstArray = configObject.dst; } - else if ((configObject === null || configObject === void 0 ? void 0 : configObject.srcDst) && Array.isArray(configObject.srcDst) && configObject.srcDst.length > 0) { + else if ((configObject === null || configObject === void 0 ? void 0 : configObject.srcDst) && + Array.isArray(configObject.srcDst) && + configObject.srcDst.length > 0) { const srcDstArray = configObject.srcDst; srcDstArray.forEach((item) => { - if ((item === null || item === void 0 ? void 0 : item.src) && (item === null || item === void 0 ? void 0 : item.dst) && typeof item.src === "string" && typeof item.dst === "string") { + if ((item === null || item === void 0 ? void 0 : item.src) && + (item === null || item === void 0 ? void 0 : item.dst) && + typeof item.src === "string" && + typeof item.dst === "string") { srcArray.push(item.src); dstArray.push(item.dst); } @@ -46,10 +59,13 @@ const grabSrcDisStrings = () => { } } else { - if (process.argv.indexOf("--src") >= 0 && process.argv.indexOf("--dst") >= 0) { + if (process.argv.indexOf("--src") >= 0 && + process.argv.indexOf("--dst") >= 0) { try { - srcArray = process.argv[process.argv.indexOf("--src") + 1].split(","); - dstArray = process.argv[process.argv.indexOf("--dst") + 1].split(","); + srcArray = + process.argv[process.argv.indexOf("--src") + 1].split(","); + dstArray = + process.argv[process.argv.indexOf("--dst") + 1].split(","); } catch (error) { } } @@ -73,33 +89,44 @@ if (!sourceFile || !destinationFile) { process.exit(); } function traverseFiles(src, dst) { + var _a; const sourceFiles = src.split(","); const dstFiles = dst.split(","); for (let i = 0; i < sourceFiles.length; i++) { const srcFolder = sourceFiles[i]; const dstFile = dstFiles[i]; - if ((srcFolder === null || srcFolder === void 0 ? void 0 : srcFolder.match(/\/[^\/]+\.[^\/]+$/)) && !(srcFolder === null || srcFolder === void 0 ? void 0 : srcFolder.match(/\.less$/))) { + if ((srcFolder === null || srcFolder === void 0 ? void 0 : srcFolder.match(/\/[^\/]+\.[^\/]+$/)) && + !(srcFolder === null || srcFolder === void 0 ? void 0 : srcFolder.match(/\.less$/))) { console.log("- \x1b[31mERROR:\x1b[0m Source must be a folder or a .less file"); process.exit(); } if (!fs_1.default.existsSync(srcFolder)) { if (srcFolder === null || srcFolder === void 0 ? void 0 : srcFolder.match(/\.less$/)) { - fs_1.default.mkdirSync(srcFolder.replace(/\/[^\/]+\.less$/, ""), { recursive: true }); + fs_1.default.mkdirSync(srcFolder.replace(/\/[^\/]+\.less$/, ""), { + recursive: true, + }); fs_1.default.writeFileSync(srcFolder, "", "utf-8"); } else { - fs_1.default.mkdirSync(srcFolder.replace(/\/[^\/]+\.[^\/]+$/, ""), { recursive: true }); + fs_1.default.mkdirSync(srcFolder.replace(/\/[^\/]+\.[^\/]+$/, ""), { + recursive: true, + }); fs_1.default.writeFileSync((srcFolder + "/main.less").replace(/\/\//g, ""), "", "utf-8"); } } - else if (fs_1.default.existsSync(srcFolder) && fs_1.default.existsSync((srcFolder + "/main.less").replace(/\/\//g, ""))) { + else if (fs_1.default.existsSync(srcFolder) && + fs_1.default.existsSync((srcFolder + "/main.less").replace(/\/\//g, ""))) { } if (!fs_1.default.existsSync(dstFile)) { if (dstFile === null || dstFile === void 0 ? void 0 : dstFile.match(/\.css$/)) { - fs_1.default.mkdirSync(dstFile.replace(/\/[^\/]+\.css$/, ""), { recursive: true }); + fs_1.default.mkdirSync(dstFile.replace(/\/[^\/]+\.css$/, ""), { + recursive: true, + }); } else { - fs_1.default.mkdirSync(dstFile.replace(/\/[^\/]+\.[^\/]+$/, ""), { recursive: true }); + fs_1.default.mkdirSync(dstFile.replace(/\/[^\/]+\.[^\/]+$/, ""), { + recursive: true, + }); } } compile(srcFolder, dstFile, null); @@ -113,7 +140,9 @@ function traverseFiles(src, dst) { catch (error) { } if (srcFolder === null || srcFolder === void 0 ? void 0 : srcFolder.match(/\.less$/)) { fs_1.default.watchFile(srcFolder, { interval: 500 }, (current, previous) => { - const dstFilePathRoot = (dstFile === null || dstFile === void 0 ? void 0 : dstFile.match(/\.css$/)) ? dstFile : dstFile + "/" + "_main.css"; + const dstFilePathRoot = (dstFile === null || dstFile === void 0 ? void 0 : dstFile.match(/\.css$/)) + ? dstFile + : dstFile + "/" + "_main.css"; try { const currentProcessArgsSrc = process.argv[process.argv.indexOf("--src") + 1]; const activeSourceFiles = currentProcessArgsSrc.split(","); @@ -130,7 +159,11 @@ function traverseFiles(src, dst) { }); } else if (!(srcFolder === null || srcFolder === void 0 ? void 0 : srcFolder.match(/\.[^\/]+$/))) { - fs_1.default.watch(srcFolder, { recursive: true }, (evtType, fileName) => { + fs_1.default.watch(srcFolder, { + recursive: ((_a = process.platform) === null || _a === void 0 ? void 0 : _a.match(/win/i)) + ? true + : undefined, + }, (evtType, fileName) => { if (!(evtType === null || evtType === void 0 ? void 0 : evtType.match(/change/i))) { return; } @@ -143,7 +176,8 @@ function traverseFiles(src, dst) { if (fileName === null || fileName === void 0 ? void 0 : fileName.match(/^\[/)) { compile(srcFolder + "/" + fileName, dstFile, evtType); } - else if ((fileName === null || fileName === void 0 ? void 0 : fileName.match(/^\(/)) || activeSourceFiles.includes(srcFilePathRoot)) { + else if ((fileName === null || fileName === void 0 ? void 0 : fileName.match(/^\(/)) || + activeSourceFiles.includes(srcFilePathRoot)) { return; } else { @@ -163,10 +197,13 @@ function traverseFiles(src, dst) { } traverseFiles(sourceFile, destinationFile); function compile(fileName, dst, evtType) { - if ((fileName === null || fileName === void 0 ? void 0 : fileName.match(/\(/)) || (fileName.match(/\.[\/]$/) && !(fileName === null || fileName === void 0 ? void 0 : fileName.match(/\.less$/i)))) { + if ((fileName === null || fileName === void 0 ? void 0 : fileName.match(/\(/)) || + (fileName.match(/\.[\/]$/) && !(fileName === null || fileName === void 0 ? void 0 : fileName.match(/\.less$/i)))) { return; } - let finalSrcPath = (fileName === null || fileName === void 0 ? void 0 : fileName.match(/\.less$/)) ? fileName : `${fileName}/main.less`; + let finalSrcPath = (fileName === null || fileName === void 0 ? void 0 : fileName.match(/\.less$/)) + ? fileName + : `${fileName}/main.less`; const distFolder = (dst === null || dst === void 0 ? void 0 : dst.match(/\.css$/)) ? null : dst === null || dst === void 0 ? void 0 : dst.replace(/\/+$/, ""); let finalDstPath = distFolder ? `${distFolder}/_main.css` : dst; if (distFolder && !fs_1.default.existsSync(distFolder)) { @@ -175,7 +212,9 @@ function compile(fileName, dst, evtType) { if (fileName === null || fileName === void 0 ? void 0 : fileName.match(/\[/)) { const paths = fileName.split("/"); const targetPathFull = paths[paths.length - 1]; - const targetPath = targetPathFull.replace(/\[|\]/g, "").replace(/\.less/, ""); + const targetPath = targetPathFull + .replace(/\[|\]/g, "") + .replace(/\.less/, ""); const destinationFileParentFolder = dst.replace(/\/[^\/]+\.css$/, ""); const targetDstFilePath = `${destinationFileParentFolder}/${targetPath}.css`; finalSrcPath = fileName; @@ -185,7 +224,9 @@ function compile(fileName, dst, evtType) { (0, child_process_1.exec)(executionCmd, (error, stdout, stderr) => { if (error) { console.log("- \x1b[33mWarn:\x1b[0m Compilation didn't run successfully. ERROR =>", error.message); - if (!(evtType === null || evtType === void 0 ? void 0 : evtType.match(/change/i)) && fileName && fileName.match(/\[/)) { + if (!(evtType === null || evtType === void 0 ? void 0 : evtType.match(/change/i)) && + fileName && + fileName.match(/\[/)) { fs_1.default.unlinkSync(finalDstPath); } return; @@ -197,7 +238,8 @@ if (fs_1.default.existsSync("./lesscw.config.json")) { fs_1.default.watchFile("./lesscw.config.json", { interval: 500 }, (evtType, fileName) => { console.log("- \x1b[34mInfo:\x1b[0m Restarting process..."); const newSrcDistStrings = grabSrcDisStrings(); - if (newSrcDistStrings.destinationFile && newSrcDistStrings.sourceFile) { + if (newSrcDistStrings.destinationFile && + newSrcDistStrings.sourceFile) { process.argv.push("--src", newSrcDistStrings.sourceFile, "--dst", newSrcDistStrings.destinationFile); traverseFiles(newSrcDistStrings.sourceFile, newSrcDistStrings.destinationFile); } diff --git a/package.json b/package.json index 28f0d79..f47c775 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "lessc-watcher", - "version": "1.1.9", + "version": "1.2.0", "description": "A minimal package to watch less files and compile them to css", "main": "dist/index.js", "bin": { diff --git a/src/index.d.ts b/src/index.d.ts index 10f2d32..4d89865 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -1,8 +1,8 @@ -export type LessCssWatcherConfigObject = { - src?: string | string[]; - dst?: string | string[]; - srcDst?: { - src?: string; - dst?: string; - }[]; -}; +export type LessCssWatcherConfigObject = { + src?: string | string[]; + dst?: string | string[]; + srcDst?: { + src?: string; + dst?: string; + }[]; +}; diff --git a/src/index.ts b/src/index.ts index 22af8fc..54ee251 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,260 +1,374 @@ -#! /usr/bin/env node - -import fs from "fs"; -import { exec } from "child_process"; -import { LessCssWatcherConfigObject } from "./index.d"; - -////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// - -const grabSrcDisStrings = () => { - let srcArray: string[] = []; - let dstArray: string[] = []; - - if (fs.existsSync("./lesscw.config.json")) { - if (process.argv.indexOf("--src")) { - try { - process.argv.splice(process.argv.indexOf("--src")); - } catch (error) {} - } - - try { - const configObject: LessCssWatcherConfigObject = JSON.parse(fs.readFileSync("./lesscw.config.json", "utf-8")); - - if (configObject?.src && configObject?.dst && typeof configObject.src === "string" && typeof configObject.dst === "string") { - srcArray = configObject.src.split(","); - dstArray = configObject.dst.split(","); - } else if (configObject?.src && configObject?.dst && typeof configObject.src === "object" && typeof configObject.dst === "object" && Array.isArray(configObject.src) && Array.isArray(configObject.dst)) { - srcArray = configObject.src; - dstArray = configObject.dst; - } else if (configObject?.srcDst && Array.isArray(configObject.srcDst) && configObject.srcDst.length > 0) { - const srcDstArray = configObject.srcDst; - - srcDstArray.forEach((item) => { - if (item?.src && item?.dst && typeof item.src === "string" && typeof item.dst === "string") { - srcArray.push(item.src); - dstArray.push(item.dst); - } - }); - } else { - console.log("- \x1b[31mERROR:\x1b[0m Your config file has some errors. Please check your config file"); - process.exit(); - } - } catch (error: any) { - console.log("- \x1b[31mERROR:\x1b[0m Your config file has some errors. ERROR =>", error.message); - process.exit(); - } - } else { - if (process.argv.indexOf("--src") >= 0 && process.argv.indexOf("--dst") >= 0) { - try { - srcArray = process.argv[process.argv.indexOf("--src") + 1].split(","); - dstArray = process.argv[process.argv.indexOf("--dst") + 1].split(","); - } catch (error) {} - } else { - console.log("- \x1b[31mERROR:\x1b[0m Missing source or destination file"); - process.exit(); - } - } - - ////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////////// - - return { - sourceFile: srcArray.join(","), - destinationFile: dstArray.join(","), - }; -}; - -const { sourceFile, destinationFile } = grabSrcDisStrings(); - -if (sourceFile && destinationFile) { - process.argv.push("--src", sourceFile, "--dst", destinationFile); -} - -////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// - -console.log("- \x1b[35mStart:\x1b[0m Running Less compiler ..."); - -if (!sourceFile || !destinationFile) { - console.log("- \x1b[31mERROR:\x1b[0m => Missing source or destination file"); - process.exit(); -} - -////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// - -/** - * Loop through source files and destination files and run the compile function - */ -function traverseFiles(src: string, dst: string) { - const sourceFiles = src.split(","); - const dstFiles = dst.split(","); - - for (let i = 0; i < sourceFiles.length; i++) { - const srcFolder = sourceFiles[i]; - const dstFile = dstFiles[i]; - - if (srcFolder?.match(/\/[^\/]+\.[^\/]+$/) && !srcFolder?.match(/\.less$/)) { - console.log("- \x1b[31mERROR:\x1b[0m Source must be a folder or a .less file"); - process.exit(); - } - - if (!fs.existsSync(srcFolder)) { - if (srcFolder?.match(/\.less$/)) { - fs.mkdirSync(srcFolder.replace(/\/[^\/]+\.less$/, ""), { recursive: true }); - fs.writeFileSync(srcFolder, "", "utf-8"); - } else { - fs.mkdirSync(srcFolder.replace(/\/[^\/]+\.[^\/]+$/, ""), { recursive: true }); - fs.writeFileSync((srcFolder + "/main.less").replace(/\/\//g, ""), "", "utf-8"); - } - } else if (fs.existsSync(srcFolder) && fs.existsSync((srcFolder + "/main.less").replace(/\/\//g, ""))) { - } - - if (!fs.existsSync(dstFile)) { - if (dstFile?.match(/\.css$/)) { - fs.mkdirSync(dstFile.replace(/\/[^\/]+\.css$/, ""), { recursive: true }); - } else { - fs.mkdirSync(dstFile.replace(/\/[^\/]+\.[^\/]+$/, ""), { recursive: true }); - } - } - - compile(srcFolder, dstFile, null); - - try { - fs.readdirSync(srcFolder).forEach((file) => { - if (file?.match(/^\[.*\.less$/)) { - compile(srcFolder + "/" + file, dstFile, null); - } - }); - } catch (error) {} - - if (srcFolder?.match(/\.less$/)) { - fs.watchFile(srcFolder, { interval: 500 }, (current, previous) => { - const dstFilePathRoot = dstFile?.match(/\.css$/) ? dstFile : dstFile + "/" + "_main.css"; - - try { - const currentProcessArgsSrc = process.argv[process.argv.indexOf("--src") + 1]; - const activeSourceFiles = currentProcessArgsSrc.split(","); - - if (activeSourceFiles.includes(srcFolder)) { - compile(srcFolder, dstFilePathRoot, null); - } else { - fs.unwatchFile(srcFolder); - } - } catch (error: any) { - console.log("- \x1b[31mERROR:\x1b[0m Please check your config file =>", error.message); - } - }); - } else if (!srcFolder?.match(/\.[^\/]+$/)) { - fs.watch(srcFolder, { recursive: true }, (evtType, fileName) => { - if (!evtType?.match(/change/i)) { - return; - } - - if (!fileName) return; - - const srcFilePathRoot = srcFolder + "/main.less"; - - try { - const currentProcessArgsSrc = process.argv[process.argv.indexOf("--src") + 1]; - const activeSourceFiles = currentProcessArgsSrc.split(","); - - if (fileName?.match(/^\[/)) { - compile(srcFolder + "/" + fileName, dstFile, evtType); - } else if (fileName?.match(/^\(/) || activeSourceFiles.includes(srcFilePathRoot)) { - return; - } else { - compile(srcFilePathRoot, dstFile, evtType); - } - } catch (error: any) { - console.log("- \x1b[31mERROR:\x1b[0m Please check your config file =>", error.message); - } - }); - } else { - console.log("- \x1b[31mERROR:\x1b[0m Source must be a folder or a .less file"); - process.exit(); - } - } -} - -traverseFiles(sourceFile, destinationFile); - -////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// - -/** - * Compile less file to css function - * @param fileName - less file path or folder path - * @param dst - destination file path or folder path - * @param evtType - event type (change, rename, etc.) or null - * @returns - */ -function compile(fileName: string, dst: string, evtType: string | null) { - if (fileName?.match(/\(/) || (fileName.match(/\.[\/]$/) && !fileName?.match(/\.less$/i))) { - return; - } - - let finalSrcPath = fileName?.match(/\.less$/) ? fileName : `${fileName}/main.less`; - const distFolder = dst?.match(/\.css$/) ? null : dst?.replace(/\/+$/, ""); - let finalDstPath = distFolder ? `${distFolder}/_main.css` : dst; - - if (distFolder && !fs.existsSync(distFolder)) { - fs.mkdirSync(distFolder, { recursive: true }); - } - - if (fileName?.match(/\[/)) { - const paths = fileName.split("/"); - const targetPathFull = paths[paths.length - 1]; - const targetPath = targetPathFull.replace(/\[|\]/g, "").replace(/\.less/, ""); - - const destinationFileParentFolder = dst.replace(/\/[^\/]+\.css$/, ""); - - const targetDstFilePath = `${destinationFileParentFolder}/${targetPath}.css`; - - finalSrcPath = fileName; - finalDstPath = targetDstFilePath; - } - - const executionCmd = `lessc ${finalSrcPath} ${finalDstPath}`; - - exec(executionCmd, (error, stdout, stderr) => { - /** @type {Error} */ - if (error) { - console.log("- \x1b[33mWarn:\x1b[0m Compilation didn't run successfully. ERROR =>", error.message); - - if (!evtType?.match(/change/i) && fileName && fileName.match(/\[/)) { - fs.unlinkSync(finalDstPath); - } - - return; - } - - console.log("- \x1b[32mCompiled:\x1b[0m Less Compilation Successful!"); - }); -} - -////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// - -/** - * watch for changes to the config file - */ -if (fs.existsSync("./lesscw.config.json")) { - fs.watchFile("./lesscw.config.json", { interval: 500 }, (evtType, fileName) => { - console.log("- \x1b[34mInfo:\x1b[0m Restarting process..."); - - const newSrcDistStrings = grabSrcDisStrings(); - - if (newSrcDistStrings.destinationFile && newSrcDistStrings.sourceFile) { - process.argv.push("--src", newSrcDistStrings.sourceFile, "--dst", newSrcDistStrings.destinationFile); - traverseFiles(newSrcDistStrings.sourceFile, newSrcDistStrings.destinationFile); - } - }); -} +#! /usr/bin/env node + +import fs from "fs"; +import { exec } from "child_process"; +import { LessCssWatcherConfigObject } from "./index.d"; + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +const grabSrcDisStrings = () => { + let srcArray: string[] = []; + let dstArray: string[] = []; + + if (fs.existsSync("./lesscw.config.json")) { + if (process.argv.indexOf("--src")) { + try { + process.argv.splice(process.argv.indexOf("--src")); + } catch (error) {} + } + + try { + const configObject: LessCssWatcherConfigObject = JSON.parse( + fs.readFileSync("./lesscw.config.json", "utf-8") + ); + + if ( + configObject?.src && + configObject?.dst && + typeof configObject.src === "string" && + typeof configObject.dst === "string" + ) { + srcArray = configObject.src.split(","); + dstArray = configObject.dst.split(","); + } else if ( + configObject?.src && + configObject?.dst && + typeof configObject.src === "object" && + typeof configObject.dst === "object" && + Array.isArray(configObject.src) && + Array.isArray(configObject.dst) + ) { + srcArray = configObject.src; + dstArray = configObject.dst; + } else if ( + configObject?.srcDst && + Array.isArray(configObject.srcDst) && + configObject.srcDst.length > 0 + ) { + const srcDstArray = configObject.srcDst; + + srcDstArray.forEach((item) => { + if ( + item?.src && + item?.dst && + typeof item.src === "string" && + typeof item.dst === "string" + ) { + srcArray.push(item.src); + dstArray.push(item.dst); + } + }); + } else { + console.log( + "- \x1b[31mERROR:\x1b[0m Your config file has some errors. Please check your config file" + ); + process.exit(); + } + } catch (error: any) { + console.log( + "- \x1b[31mERROR:\x1b[0m Your config file has some errors. ERROR =>", + error.message + ); + process.exit(); + } + } else { + if ( + process.argv.indexOf("--src") >= 0 && + process.argv.indexOf("--dst") >= 0 + ) { + try { + srcArray = + process.argv[process.argv.indexOf("--src") + 1].split(","); + dstArray = + process.argv[process.argv.indexOf("--dst") + 1].split(","); + } catch (error) {} + } else { + console.log( + "- \x1b[31mERROR:\x1b[0m Missing source or destination file" + ); + process.exit(); + } + } + + ////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// + + return { + sourceFile: srcArray.join(","), + destinationFile: dstArray.join(","), + }; +}; + +const { sourceFile, destinationFile } = grabSrcDisStrings(); + +if (sourceFile && destinationFile) { + process.argv.push("--src", sourceFile, "--dst", destinationFile); +} + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +console.log("- \x1b[35mStart:\x1b[0m Running Less compiler ..."); + +if (!sourceFile || !destinationFile) { + console.log( + "- \x1b[31mERROR:\x1b[0m => Missing source or destination file" + ); + process.exit(); +} + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +/** + * Loop through source files and destination files and run the compile function + */ +function traverseFiles(src: string, dst: string) { + const sourceFiles = src.split(","); + const dstFiles = dst.split(","); + + for (let i = 0; i < sourceFiles.length; i++) { + const srcFolder = sourceFiles[i]; + const dstFile = dstFiles[i]; + + if ( + srcFolder?.match(/\/[^\/]+\.[^\/]+$/) && + !srcFolder?.match(/\.less$/) + ) { + console.log( + "- \x1b[31mERROR:\x1b[0m Source must be a folder or a .less file" + ); + process.exit(); + } + + if (!fs.existsSync(srcFolder)) { + if (srcFolder?.match(/\.less$/)) { + fs.mkdirSync(srcFolder.replace(/\/[^\/]+\.less$/, ""), { + recursive: true, + }); + fs.writeFileSync(srcFolder, "", "utf-8"); + } else { + fs.mkdirSync(srcFolder.replace(/\/[^\/]+\.[^\/]+$/, ""), { + recursive: true, + }); + fs.writeFileSync( + (srcFolder + "/main.less").replace(/\/\//g, ""), + "", + "utf-8" + ); + } + } else if ( + fs.existsSync(srcFolder) && + fs.existsSync((srcFolder + "/main.less").replace(/\/\//g, "")) + ) { + } + + if (!fs.existsSync(dstFile)) { + if (dstFile?.match(/\.css$/)) { + fs.mkdirSync(dstFile.replace(/\/[^\/]+\.css$/, ""), { + recursive: true, + }); + } else { + fs.mkdirSync(dstFile.replace(/\/[^\/]+\.[^\/]+$/, ""), { + recursive: true, + }); + } + } + + compile(srcFolder, dstFile, null); + + try { + fs.readdirSync(srcFolder).forEach((file) => { + if (file?.match(/^\[.*\.less$/)) { + compile(srcFolder + "/" + file, dstFile, null); + } + }); + } catch (error) {} + + if (srcFolder?.match(/\.less$/)) { + fs.watchFile(srcFolder, { interval: 500 }, (current, previous) => { + const dstFilePathRoot = dstFile?.match(/\.css$/) + ? dstFile + : dstFile + "/" + "_main.css"; + + try { + const currentProcessArgsSrc = + process.argv[process.argv.indexOf("--src") + 1]; + const activeSourceFiles = currentProcessArgsSrc.split(","); + + if (activeSourceFiles.includes(srcFolder)) { + compile(srcFolder, dstFilePathRoot, null); + } else { + fs.unwatchFile(srcFolder); + } + } catch (error: any) { + console.log( + "- \x1b[31mERROR:\x1b[0m Please check your config file =>", + error.message + ); + } + }); + } else if (!srcFolder?.match(/\.[^\/]+$/)) { + fs.watch( + srcFolder, + { + recursive: process.platform?.match(/win/i) + ? true + : undefined, + }, + (evtType, fileName) => { + if (!evtType?.match(/change/i)) { + return; + } + + if (!fileName) return; + + const srcFilePathRoot = srcFolder + "/main.less"; + + try { + const currentProcessArgsSrc = + process.argv[process.argv.indexOf("--src") + 1]; + const activeSourceFiles = + currentProcessArgsSrc.split(","); + + if (fileName?.match(/^\[/)) { + compile( + srcFolder + "/" + fileName, + dstFile, + evtType + ); + } else if ( + fileName?.match(/^\(/) || + activeSourceFiles.includes(srcFilePathRoot) + ) { + return; + } else { + compile(srcFilePathRoot, dstFile, evtType); + } + } catch (error: any) { + console.log( + "- \x1b[31mERROR:\x1b[0m Please check your config file =>", + error.message + ); + } + } + ); + } else { + console.log( + "- \x1b[31mERROR:\x1b[0m Source must be a folder or a .less file" + ); + process.exit(); + } + } +} + +traverseFiles(sourceFile, destinationFile); + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +/** + * Compile less file to css function + * @param fileName - less file path or folder path + * @param dst - destination file path or folder path + * @param evtType - event type (change, rename, etc.) or null + * @returns + */ +function compile(fileName: string, dst: string, evtType: string | null) { + if ( + fileName?.match(/\(/) || + (fileName.match(/\.[\/]$/) && !fileName?.match(/\.less$/i)) + ) { + return; + } + + let finalSrcPath = fileName?.match(/\.less$/) + ? fileName + : `${fileName}/main.less`; + const distFolder = dst?.match(/\.css$/) ? null : dst?.replace(/\/+$/, ""); + let finalDstPath = distFolder ? `${distFolder}/_main.css` : dst; + + if (distFolder && !fs.existsSync(distFolder)) { + fs.mkdirSync(distFolder, { recursive: true }); + } + + if (fileName?.match(/\[/)) { + const paths = fileName.split("/"); + const targetPathFull = paths[paths.length - 1]; + const targetPath = targetPathFull + .replace(/\[|\]/g, "") + .replace(/\.less/, ""); + + const destinationFileParentFolder = dst.replace(/\/[^\/]+\.css$/, ""); + + const targetDstFilePath = `${destinationFileParentFolder}/${targetPath}.css`; + + finalSrcPath = fileName; + finalDstPath = targetDstFilePath; + } + + const executionCmd = `lessc ${finalSrcPath} ${finalDstPath}`; + + exec(executionCmd, (error, stdout, stderr) => { + /** @type {Error} */ + if (error) { + console.log( + "- \x1b[33mWarn:\x1b[0m Compilation didn't run successfully. ERROR =>", + error.message + ); + + if ( + !evtType?.match(/change/i) && + fileName && + fileName.match(/\[/) + ) { + fs.unlinkSync(finalDstPath); + } + + return; + } + + console.log("- \x1b[32mCompiled:\x1b[0m Less Compilation Successful!"); + }); +} + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +/** + * watch for changes to the config file + */ +if (fs.existsSync("./lesscw.config.json")) { + fs.watchFile( + "./lesscw.config.json", + { interval: 500 }, + (evtType, fileName) => { + console.log("- \x1b[34mInfo:\x1b[0m Restarting process..."); + + const newSrcDistStrings = grabSrcDisStrings(); + + if ( + newSrcDistStrings.destinationFile && + newSrcDistStrings.sourceFile + ) { + process.argv.push( + "--src", + newSrcDistStrings.sourceFile, + "--dst", + newSrcDistStrings.destinationFile + ); + traverseFiles( + newSrcDistStrings.sourceFile, + newSrcDistStrings.destinationFile + ); + } + } + ); +} diff --git a/tsconfig.json b/tsconfig.json index 1479c8c..05e807b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,13 +1,13 @@ -{ - "compilerOptions": { - "target": "es2016", - "module": "commonjs", - "rootDir": "./src", - "outDir": "./dist", - "removeComments": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "strict": true, - "skipLibCheck": true - } -} +{ + "compilerOptions": { + "target": "es2016", + "module": "commonjs", + "rootDir": "./src", + "outDir": "./dist", + "removeComments": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": true + } +}