diff --git a/less-watch-compiler.sample.config.json b/less-watch-compiler.sample.config.json index 1ebaafb..db04cb9 100755 --- a/less-watch-compiler.sample.config.json +++ b/less-watch-compiler.sample.config.json @@ -1,6 +1,6 @@ { "allowedExtensions":[".less"], - "enableJs": true, + "enableJs": false, "minified": true, "includeHidden": true, "sourceMap": true, diff --git a/package.json b/package.json index 537422d..99f7e00 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "less-watch-compiler", "version": "1.16.2", "description": "A command that watches folders(and subfolders) for file changes and automatically compile the less css files into css. This is a file system watcher and compiler.", - "main": "dist/less-watch-compiler.js", + "main": "dist/main.js", "directories": { "test": "tests" }, @@ -15,8 +15,8 @@ "clean": "rm -R ./test/css || true && mkdir ./test/css", "prepublishOnly": "yarn run build", "commit": "git-cz -n", - "build": "babel src --out-dir dist", - "start": "yarn run build && node ./dist/less-watch-compiler.js", + "build": "./node_modules/typescript/bin/tsc", + "start": "yarn run build && node ./dist/main.js", "postinstall": "opencollective-postinstall" }, "repository": { @@ -37,11 +37,15 @@ }, "engine": "node 0.10.26", "devDependencies": { - "@types/commander": "2.12.2", + "@types/commander": "^2.12.2", + "@types/extend": "^3.0.1", + "@types/node": "^16.3.2", + "@types/shelljs": "^0.8.9", "babel-cli": "6.26.0", "babel-preset-env": "1.7.0", "cz-conventional-changelog": "3.3.0", - "mocha": "9.0.3" + "mocha": "9.0.3", + "typescript": "^4.3.5" }, "preferGlobal": true, "keywords": [ diff --git a/src/less-watch-compiler.js b/src/less-watch-compiler.js deleted file mode 100755 index 003dccc..0000000 --- a/src/less-watch-compiler.js +++ /dev/null @@ -1,174 +0,0 @@ -#!/usr/bin/env node - -/* Copyright 2012, Jonathan Cheung Licensed and released under the MIT - license. Refer to MIT-LICENSE.txt. - - A nodejs script that watches folders(and subfolders) for changes and automatically compile the less css files into css. - - Always give credit where it is due. Parts of this script is modified from Mikeal Rogers's watch script (https://github.com/mikeal/watch) - - Basic Usage: less-watch-compiler FOLDER_TO_WATCH FOLDER_TO_OUTPUT - Example: 'less-watch-compiler less css' will watch ./less folder - and compile the less css files into ./css when they are added/updated -*/ -var sys = require('util') - , fs = require('fs') - , path= require('path') - , sh = require('shelljs') - , extend = require('extend') - , exec = require('child_process').exec - , lessWatchCompilerUtils = require('./lib/lessWatchCompilerUtils.js') - , cwd = sh.pwd() - , data - , mainFilePath = undefined - , program = require('commander') - , packagejson = require('../package.json') - , events = require('events'); - -//bypass maxlistener errors because more files means more listeners #90 -events.EventEmitter.defaultMaxListeners = 0; - -program - .version(packagejson.version, '-v, -V, --version', 'Output the current version') - .usage('[options] [main_file_name]') - .option('--main-file ', "Specify as the file to always re-compile e.g. '--main-file style.less'.") - .option('--config ', 'Custom configuration file path.', 'less-watch-compiler.config.json') - .option('--run-once', 'Run the compiler once without waiting for additional changes.') - .option('--include-hidden', "Don't ignore files beginning with a '.' or a '_'") - //Less Options - .option('--enable-js', 'Less.js Option: To enable inline JavaScript in less files.') - .option('--source-map', "Less.js Option: To generate source map for css files.") - .option('--plugins ,', 'Less.js Option: To specify plugins separated by commas.') - .option('--less-args =,=', 'Less.js Option: To specify any other less options e.g. \'--less-args math=strict,strict-units=on,include-path=.\/dir1\\;.\/dir2\'.') - .parse(); - -const programOption = program.opts(); - - // Check if configuration file exists - var configPath = programOption.config ? (path.isAbsolute(programOption.config))? programOption.config : (cwd + path.sep + programOption.config): "less-watch-compiler.config.json"; - - fs.access(configPath, fs.constants.F_OK, (err) => { - if (!err) { - let data = fs.readFileSync(configPath); - var customConfig = JSON.parse(data.toString()); - console.log('Config file ' + configPath + ' is loaded.'); - extend(true, lessWatchCompilerUtils.config, customConfig); - } - init(); - }); - - -function init(){ - if (program.args[0]) lessWatchCompilerUtils.config.watchFolder = program.args[0]; - if (program.args[1]) lessWatchCompilerUtils.config.outputFolder = program.args[1]; - if (program.args[2]) lessWatchCompilerUtils.config.mainFile = program.args[2]; - if (programOption.mainFile) lessWatchCompilerUtils.config.mainFile = programOption.mainFile; - if (programOption.sourceMap) lessWatchCompilerUtils.config.sourceMap = programOption.sourceMap; - if (programOption.plugins) lessWatchCompilerUtils.config.plugins = programOption.plugins; - if (programOption.runOnce) lessWatchCompilerUtils.config.runOnce = programOption.runOnce; - if (programOption.inludeHidden) lessWatchCompilerUtils.config.includeHidden = programOption.includeHidden; - if (programOption.enableJs) lessWatchCompilerUtils.config.enableJs = programOption.enableJs; - if (programOption.lessArgs) lessWatchCompilerUtils.config.lessArgs = programOption.lessArgs; - - lessWatchCompilerUtils.config = Object.assign({}, lessWatchCompilerUtils.config, program.opts()) - - /* - 3rd parameter is optional, but once you define it, then we will just compile - the main and generate as "{main_file_name}.css". All the files that has been - referenced from the main one will be minified into it. - Assuming the 3rd is "main.less" - - input folder: src - src - main.less (import aux.less) - aux.less - - output folder: dist - dist - main.css - - Otherwise, it will behave as previously: - Assuming the 3rd is empty - - input folder: src - src - main.less (import aux.less) - aux.less - - output folder: dist - dist - main.css - aux.css - */ - - if ( !lessWatchCompilerUtils.config.watchFolder || !lessWatchCompilerUtils.config.outputFolder ){ - console.log('Missing arguments. Example:'); - console.log('\tnode less-watch-compiler.js FOLDER_TO_WATCH FOLDER_TO_OUTPUT'); - console.log('\tExample 1: To watch all files under the folder "less" and compile all into a folder "css".'); - console.log('\t\t less-watch-compiler less css'); - process.exit(1); - } - - lessWatchCompilerUtils.config.watchFolder = path.resolve(lessWatchCompilerUtils.config.watchFolder); - lessWatchCompilerUtils.config.outputFolder = path.resolve(lessWatchCompilerUtils.config.outputFolder); - - if (lessWatchCompilerUtils.config.mainFile) { - mainFilePath = path.resolve(lessWatchCompilerUtils.config.watchFolder, lessWatchCompilerUtils.config.mainFile); - fs.exists(mainFilePath, function(exists) { - if (!exists){ - console.log("Main file " + mainFilePath+" does not exist."); - process.exit(); - } - }); - } - - if (lessWatchCompilerUtils.config.runOnce === true) - console.log('Running less-watch-compiler once.'); - else - console.log('Watching directory for file changes.'); - lessWatchCompilerUtils.watchTree( - lessWatchCompilerUtils.config.watchFolder, - { - interval: 200, - // If we've set --include-hidden, don't ignore dotfiles - ignoreDotFiles: !lessWatchCompilerUtils.config.includeHidden, - filter: lessWatchCompilerUtils.filterFiles - }, - function (f, curr, prev, fileimportlist) { - if (typeof f == 'object' && prev === null && curr === null) { - // Finished walking the tree - return; - } else if (curr.nlink === 0) { - // f was removed - console.log(f +' was removed.') - } else { - // f is a new file or changed - // console.log(f) - var importedFile = false; - var filename = f.substring(lessWatchCompilerUtils.config.watchFolder.length+1) - for (var i in fileimportlist){ - for (var k in fileimportlist[i]){ - var hasExtension = path.extname(fileimportlist[i][k]).length > 1; - var importFile = path.join(fileimportlist[i][k], (hasExtension ? '' : '.less')); - var normalizedPath = path.normalize(path.dirname(i) + path.sep + importFile); - - // console.log('compare ' + f + ' with import #' + k + ' in ' + i + ' value ' + normalized); - if (f == normalizedPath && !mainFilePath) { - // Compile changed file only if a main file is there. - var compileResult = lessWatchCompilerUtils.compileCSS(i); - console.log('The file: ' + i + ' was changed because '+JSON.stringify(f)+' is specified as an import. Recompiling '+compileResult.outputFilePath+' at ' + lessWatchCompilerUtils.getDateTime()); - importedFile = true; - } - } - } - if (!importedFile){ - var compileResult = lessWatchCompilerUtils.compileCSS(mainFilePath || f); - console.log('The file: ' + JSON.stringify(f) + ' was changed. Recompiling '+compileResult.outputFilePath+' at ' + lessWatchCompilerUtils.getDateTime()); - } - } - }, - // init function - function(f){ - if (!mainFilePath || mainFilePath === f) { - // compile each file when main file is missing or compile main file only once - lessWatchCompilerUtils.compileCSS(f); - } - } - ); -} diff --git a/src/lib/Options.ts b/src/lib/Options.ts new file mode 100644 index 0000000..c412113 --- /dev/null +++ b/src/lib/Options.ts @@ -0,0 +1,75 @@ +import { OptionValues } from "commander"; + +export class Options { + private static instance: Options; + + inputOptions: OptionValues = []; + config: string = ""; + watchFolder: string = ""; + outputFolder: string = ""; + mainFile: string = ""; + sourceMap: boolean = false; + plugins: string = ""; + runOnce: boolean = false; + includeHidden: boolean = false; + enableJs: boolean = false; + lessArgs: string = ""; + minified: boolean = false; + allowedExtensions: string[] = [".less"]; + + private constructor(options?: OptionValues) { + if (options) this.setValues(options); + } + + private setValues(options: OptionValues|undefined) { + if (options !== undefined){ + + this.inputOptions = options; + this.config = options.config; + this.watchFolder = options.args[0]; + this.outputFolder = options.args[1]; + this.mainFile = options.mainFile || options.args[2]; + this.sourceMap = options.sourceMap; + this.plugins = options.plugins; + this.runOnce = options.runOnce; + this.includeHidden = options.includeHidden; + this.enableJs = options.enableJs; + this.lessArgs = options.lessArgs; + this.minified = options.minified; + this.allowedExtensions = [".less"]; + } + } + public setValue(key:string, value:any):Options|boolean { + if (key in this){ + [key] = value; + return Options.instance; + }else{ + return false; + } + + } + public reset():void { + this.inputOptions = []; + this.config = ''; + this.watchFolder = ''; + this.outputFolder = ''; + this.mainFile = ''; + this.sourceMap = false; + this.plugins = ''; + this.runOnce = false; + this.includeHidden = false; + this.enableJs = false; + this.lessArgs = ''; + this.minified = false; + } + + public static getInstance(options?: OptionValues): Options { + if (!Options.instance) { + Options.instance = new Options(options); + }else{ + Options.instance.setValues(options); + } + + return Options.instance; + } +} diff --git a/src/lib/Utils.ts b/src/lib/Utils.ts new file mode 100644 index 0000000..a5c8088 --- /dev/null +++ b/src/lib/Utils.ts @@ -0,0 +1,220 @@ +"use strict"; +import * as path from "path"; +import FileSearch from "./fileSearch"; +import sh from "shelljs"; +import { Options } from "./Options"; +const fileSearch = new FileSearch(); +import * as child from "child_process"; +import * as fs from "fs"; +const lessWatchCompilerUtilsModule = require("./lessWatchCompilerUtils.cjs.js"); + +export function compileCSS( + inputFilePath: string, + test?: boolean +): child.ChildProcess | undefined { + // As a rule, we don't compile hidden files for now. If we encounter one, + // just return. + const outputFilePath = resolveOutputPath(inputFilePath); + if (fileSearch.isHiddenFile(outputFilePath)) return undefined; + + const command = getCommand(inputFilePath, outputFilePath); + // Run the command + if (!test) return child.exec(command); + else return child.exec(""); +} + +export function getCommand( + inputFilePath: string, + outputFilePath: string +): string { + const Config = Options.getInstance(); + const enableJsFlag = Config.enableJs ? " --js" : "", + minifiedFlag = Config.minified ? " -x" : "", + sourceMap = Config.sourceMap ? " --source-map" : "", + lessArgs = Config.lessArgs ? getLessArgs(Config.lessArgs) : "", + plugins = Config.plugins + ? " --" + Config.plugins.split(",").join(" --") + : "", + command = + "lessc" + + lessArgs + + sourceMap + + enableJsFlag + + minifiedFlag + + plugins + + " " + + JSON.stringify(inputFilePath) + + " " + + JSON.stringify(outputFilePath); + return command; +} + +export function getDateTime(): string { + const date = new Date(); + let displayDate: string = "", + hour = date.getHours(), + min = date.getMinutes(), + sec = date.getSeconds(), + year = date.getFullYear(), + month = date.getMonth() + 1, + day = date.getDate(); + + displayDate += (hour < 10 ? "0" : "") + hour; + displayDate += ":" + (min < 10 ? "0" : "") + min; + displayDate += ":" + (sec < 10 ? "0" : "") + sec; + displayDate += " on " + (day < 10 ? "0" : "") + day; + displayDate += "/" + (month < 10 ? "0" : "") + month + "/" + year; + return displayDate; +} + +export function resolveOutputPath(filePath: string) { + const cwd = sh.pwd().toString(), + fullPath = path.resolve(filePath), + parsedPath = path.parse(fullPath), + Config = Options.getInstance(); + + // Only empty when unit testing it seems + let relativePath: string, dirname: string; + if (Config.watchFolder) { + relativePath = path.relative(Config.watchFolder, fullPath); + dirname = path.dirname(relativePath); + } else { + dirname = path.dirname(filePath); + } + const filename = parsedPath.name; + + let formatted: string = path.format({ + dir: dirname, + name: filename, + ext: (Config.minified ? ".min" : "") + ".css", + }); + + // No matter the path of the main file, the output must always land in the output folder + formatted = formatted.replace(/^(\.\.[\/\\])+/, ""); + + const finalFullPath = path.resolve(Config.outputFolder, formatted); + const shortPath = path.relative(cwd, finalFullPath); + + return shortPath; +} + +export function getLessArgs(args: string) { + const arr = args.split(","); + return " --" + arr.join(" --"); +} + +export function filterFiles(f: string) { + var filename = path.basename(f), + extension = path.extname(f), + Config = Options.getInstance(), + allowedExtensions = Config.allowedExtensions; + + if (filename == "" || allowedExtensions.indexOf(extension) == -1) { + return true; + } else { + // If we're including hidden files then don't ignore this file + if (Config.includeHidden) return false; + // Otherwise, do ignore this file if it's a hidden file + else { + return fileSearch.isHiddenFile(filename); + } + } +} + +export function walk( + dir: string, + options: { ignoreDotFiles: boolean; filter: any }, + callback: (err: any, object?: object) => void, + initCallback: (file: string) => void, + callbackOptions: { files: { [key: string]: any }; pending: number } = { + files: {}, + pending: 0, + } +) { + callbackOptions.pending += 1; + + fs.stat(dir, function (err, stat) { + if (err) return callback(err); + callbackOptions.files[dir] = stat; + fs.readdir(dir, function (err, files) { + if (err) return callback(err); + callbackOptions.pending -= 1; + files.forEach(function (f, index) { + f = path.join(dir, f); + callbackOptions.pending += 1; + fs.stat(f, function (err, stat) { + var enoent = false, + done = false; + + if (err) { + if (err.code !== "ENOENT") { + console.log(err.code); + return callback(err); + } else { + enoent = true; + } + } + callbackOptions.pending -= 1; + done = callbackOptions.pending === 0; + if (!enoent) { + callbackOptions.files[f] = stat; + if (stat.isDirectory()) { + walk(f, options, callback, initCallback); + } else { + if (options.ignoreDotFiles && path.basename(f)[0] === ".") + return done && callback(null, callbackOptions.files); + if (options.filter && options.filter(f)) + return done && callback(null, callbackOptions.files); + initCallback && initCallback(f); + } + + if (done) callback(null, callbackOptions.files); + } + }); + }); + if (callbackOptions.pending === 0) callback(null, callbackOptions.files); + }); + if (callbackOptions.pending === 0) callback(null, callbackOptions.files); + }); +} + +export function watchTree( + root: string, + options: { interval: number; ignoreDotFiles: boolean; filter: any }, + watchCallback: ( + files: any, + current: any, + next: any, + fileimportlist: [] + ) => void, + initCallback: (f: string) => void, + watchCallbackOption?: {} +) { + walk( + root, + options, + function (err, files) { + if (err) throw err; + lessWatchCompilerUtilsModule.fileWatcher( + root, + files, + options, + [], + [], + watchCallback + ); + for (var i in files) { + lessWatchCompilerUtilsModule.fileWatcher( + i, + files, + options, + [], + [], + watchCallback + ); + } + watchCallback(files, null, null, []); + }, + initCallback + ); +} diff --git a/src/lib/fileSearch.ts b/src/lib/fileSearch.ts new file mode 100644 index 0000000..8065f07 --- /dev/null +++ b/src/lib/fileSearch.ts @@ -0,0 +1,26 @@ +"use strict"; +import * as fs from "fs"; +import * as path from "path"; + +export default class FileSearch { + + + findLessImportsInFile (f: string): string[] { + if (fs.statSync(f) && fs.statSync(f).isFile() === false) return []; + else { + let m:RegExpExecArray | null, + files:string[] = []; + const fileContent = fs.readFileSync(f, "utf8"); + const re = /@import (\(reference\) )?['"](.*?)['"];/g; + while (m = re.exec(fileContent)) { + let [, , filename] = m; + if (filename) files.push(filename); + }; + return files + } + } + isHiddenFile (filename: string):boolean { + filename = path.basename(filename); + return filename.substr(0, 1) === "_" || filename.substr(0, 1) === "."; + } +}; \ No newline at end of file diff --git a/src/lib/filesearch.js b/src/lib/filesearch.js deleted file mode 100755 index 56e699e..0000000 --- a/src/lib/filesearch.js +++ /dev/null @@ -1,32 +0,0 @@ -'use strict'; -if (typeof define !== 'function') { - var define = require('amdefine')(module); -} - -define(function (require){ - const fs = require('fs'); - const path = require('path') - - const filesearch = { - findLessImportsInFile: function(f){ - if (fs.statSync(f) && fs.statSync(f).isFile() === false) - return [] - else{ - let m, files = []; - const fileContent = fs.readFileSync(f, 'utf8'); - const re = /@import (\(reference\) )?['"](.*?)['"];/g; - while (m = re.exec(fileContent)){ - let [ , , filename ] = m; - // console.log('found import ' + filename); - if (m) files.push(filename); - } - return files; - } - }, - isHiddenFile: function (filename) { - filename = path.basename(filename) - return filename.substr(0, 1) === '_' || filename.substr(0, 1) === '.'; - } - } - return filesearch; -}); \ No newline at end of file diff --git a/src/lib/lessWatchCompilerUtils.cjs.js b/src/lib/lessWatchCompilerUtils.cjs.js new file mode 100755 index 0000000..07843c0 --- /dev/null +++ b/src/lib/lessWatchCompilerUtils.cjs.js @@ -0,0 +1,150 @@ +if (typeof define !== "function") { + var define = require("amdefine")(module); +} + +define(function (require) { + // return a value that defines the module export + // (i.e the functionality we want to expose for consumption) + var self = this; + var fs = require("fs"), + path = require("path"), + FileSearch = require("./fileSearch.js"), + Config = require("./Options.js").Options.getInstance(), + filelist = [], + fileimportlist = {}, + walk = require("./Utils.js").walk; + + var fileSearch = new FileSearch.default(); + + var lessWatchCompilerUtilsModule = { + //Setup fs.watchFile() for each file. + // watchTree: function (root, options, watchCallback, initCallback) { + // if (!watchCallback) { + // watchCallback = options; + // options = {}; + // } + // walk( + // root, + // options, + // function (err, files) { + // if (err) throw err; + // lessWatchCompilerUtilsModule.fileWatcher( + // root, + // files, + // options, + // filelist, + // fileimportlist, + // watchCallback + // ); + // for (var i in files) { + // lessWatchCompilerUtilsModule.fileWatcher( + // i, + // files, + // options, + // filelist, + // fileimportlist, + // watchCallback + // ); + // } + // watchCallback(files, null, null, fileimportlist); + // }, + // initCallback + // ); + // }, + // We build the function to filter the files to watch. + // Returning true marks a file to be ignored. + setupWatcher: function (f, files, options, watchCallback) { + if (Config.runOnce === true) return; + fs.watchFile(f, options, function (c, p) { + // Check if anything actually changed in stat + if ( + files[f] && + !files[f].isDirectory() && + c.nlink !== 0 && + files[f].mtime.getTime() == c.mtime.getTime() + ) + return; + files[f] = c; + if (!files[f].isDirectory()) { + if (options.ignoreDotFiles && path.basename(f)[0] === ".") return; + if (options.filter && options.filter(f)) return; + fs.exists(f, function (exists) { + if (!exists) { + console.log("Does not exist : " + f); + } else { + fileimportlist[f] = fileSearch.findLessImportsInFile(f); + watchCallback(f, c, p, fileimportlist); + } + }); + } else { + fs.readdir(f, function (err, nfiles) { + if (err) return; + nfiles.forEach(function (b) { + var file = path.join(f, b); + if (!files[file]) { + fs.stat(file, function (err, stat) { + if (options.ignoreDotFiles && path.basename(b)[0] === ".") + return; + if (options.filter && options.filter(b)) return; + fs.exists(file, function (exists) { + if (!exists) { + console.log("Does not exist : " + f); + } else { + fileimportlist[file] = + fileSearch.findLessImportsInFile(file); + watchCallback(file, stat, null, fileimportlist); + files[file] = stat; + lessWatchCompilerUtilsModule.fileWatcher( + file, + files, + options, + filelist, + fileimportlist, + watchCallback + ); + } + }); + }); + } + }); + }); + } + if (c.nlink === 0) { + // unwatch removed files. + delete files[f]; + fs.unwatchFile(f); + } + }); + }, + fileWatcher: function ( + f, + files, + options, + filelist, + fileimportlist, + watchCallback + ) { + if (filelist.indexOf(f) !== -1) return; + filelist[filelist.length] = f; + + fileimportlist[f] = fileSearch.findLessImportsInFile(f); + lessWatchCompilerUtilsModule.setupWatcher( + f, + files, + options, + watchCallback + ); + for (var i in fileimportlist[f]) { + if (filelist.indexOf(fileimportlist[f][i]) === -1) { + lessWatchCompilerUtilsModule.setupWatcher( + path.normalize(path.dirname(f) + path.sep + fileimportlist[f][i]), + files, + options, + watchCallback + ); + } + } + }, + }; + return lessWatchCompilerUtilsModule; +}); diff --git a/src/lib/lessWatchCompilerUtils.js b/src/lib/lessWatchCompilerUtils.js deleted file mode 100755 index 68c7e5e..0000000 --- a/src/lib/lessWatchCompilerUtils.js +++ /dev/null @@ -1,254 +0,0 @@ -if (typeof define !== 'function') { - var define = require('amdefine')(module); -} - -define(function (require) { - // return a value that defines the module export - // (i.e the functionality we want to expose for consumption) - var self = this; - var sys = require('util'), - fs = require('fs'), - path = require('path'), - events = require('events'), - sh = require('shelljs'), - extend = require('extend'), - exec = require('child_process').exec, - cwd = sh.pwd().toString(), - defaultAllowedExtensions = [".less"], - fileSearch = require('./filesearch'), - filelist = [], - fileimportlist = {}; - - var lessWatchCompilerUtilsModule = { - config: {}, - walk: function (dir, options, callback, initCallback) { - if (!callback) { - callback = options; - options = {} - } - if (!callback.files) callback.files = {}; - if (!callback.pending) callback.pending = 0; - callback.pending += 1; - fs.stat(dir, function (err, stat) { - if (err) return callback(err); - callback.files[dir] = stat; - fs.readdir(dir, function (err, files) { - if (err) return callback(err); - callback.pending -= 1; - files.forEach(function (f, index) { - f = path.join(dir, f); - callback.pending += 1; - fs.stat(f, function (err, stat) { - var enoent = false, - done = false; - - if (err) { - if (err.code !== 'ENOENT') { - console.log(err.code) - return callback(err); - } else { - enoent = true; - } - } - callback.pending -= 1; - done = callback.pending === 0; - if (!enoent) { - callback.files[f] = stat; - if (stat.isDirectory()) { - lessWatchCompilerUtilsModule.walk(f, options, callback, initCallback); - } else { - if (options.ignoreDotFiles && path.basename(f)[0] === '.') return done && callback(null, callback.files); - if (options.filter && options.filter(f)) return done && callback(null, callback.files); - initCallback && initCallback(f); - } - - if (done) callback(null, callback.files); - } - }) - }) - if (callback.pending === 0) callback(null, callback.files); - }) - if (callback.pending === 0) callback(null, callback.files); - }) - }, - //Setup fs.watchFile() for each file. - watchTree: function (root, options, watchCallback, initCallback) { - if (!watchCallback) { - watchCallback = options; - options = {} - } - lessWatchCompilerUtilsModule.walk(root, options, function (err, files) { - if (err) throw err; - lessWatchCompilerUtilsModule.fileWatcher(root, files, options, filelist, fileimportlist, watchCallback); - for (var i in files) { - lessWatchCompilerUtilsModule.fileWatcher(i, files, options, filelist, fileimportlist, watchCallback); - } - watchCallback(files, null, null, fileimportlist); - }, - initCallback); - }, - // Here's where we run the less compiler - getLessArgs: function(args) { - var arr = args.split(','); - return " --" + arr.join(' --'); - }, - compileCSS: function (file, test) { - - var outputFilePath = this.resolveOutputPath(file); - - // As a rule, we don't compile hidden files for now. If we encounter one, - // just return. - if (fileSearch.isHiddenFile(outputFilePath)) return - - var enableJsFlag = lessWatchCompilerUtilsModule.config.enableJs ? ' --js' : ''; - var minifiedFlag = lessWatchCompilerUtilsModule.config.minified ? ' -x' : ''; - var sourceMap = (lessWatchCompilerUtilsModule.config.sourceMap) ? ' --source-map' : ''; - var lessArgs = (lessWatchCompilerUtilsModule.config.lessArgs)? this.getLessArgs(lessWatchCompilerUtilsModule.config.lessArgs): ''; - var plugins = (lessWatchCompilerUtilsModule.config.plugins) ? ' --' + lessWatchCompilerUtilsModule.config.plugins.split(',').join(' --') : ''; - var command = 'lessc' + lessArgs + sourceMap + enableJsFlag + minifiedFlag + plugins + ' ' + JSON.stringify(file) + ' ' + outputFilePath; - // Run the command - // console.log(command) - if (!test) - exec(command, function (error, stdout, stderr) { - if (error !== null) { - console.log(error); - if (lessWatchCompilerUtilsModule.config.runOnce) - process.exit(1); - } - if (stdout) - console.error(stdout); - }); - return { - "command": command, - "outputFilePath": outputFilePath - }; - - }, - resolveOutputPath: function (filePath) { - var fullPath = path.resolve(filePath); - var parsedPath = path.parse(fullPath); - - // Only empty when unit testing it seems - var relativePath = null; - var dirname = null; - if (lessWatchCompilerUtilsModule.config.watchFolder) { - relativePath = path.relative(lessWatchCompilerUtilsModule.config.watchFolder, fullPath); - dirname = path.dirname(relativePath); - } else { - dirname = path.dirname(filePath); - } - var filename = parsedPath.name; - - var formatted = path.format({ - dir: dirname, - name: filename, - ext: (lessWatchCompilerUtilsModule.config.minified ? '.min' : '') + '.css', - }); - - // No matter the path of the main file, the output must always land in the output folder - formatted = formatted.replace(/^(\.\.[\/\\])+/, ''); - - var finalFullPath = path.resolve(lessWatchCompilerUtilsModule.config.outputFolder, formatted); - var shortPath = path.relative(cwd, finalFullPath); - - return JSON.stringify(shortPath); - }, - // We build the function to filter the files to watch. - // Returning true marks a file to be ignored. - filterFiles: function (f) { - var filename = path.basename(f); - var extension = path.extname(f), - allowedExtensions = lessWatchCompilerUtilsModule.config.allowedExtensions || defaultAllowedExtensions; - if (filename == '' || allowedExtensions.indexOf(extension) == -1) { - return true; - } else { - // If we're including hidden files then don't ignore this file - if (lessWatchCompilerUtilsModule.config.includeHidden) return false; - // Otherwise, do ignore this file if it's a hidden file - else return fileSearch.isHiddenFile(filename) - } - }, - getDateTime: function () { - var date = new Date(); - var hour = date.getHours(); - hour = (hour < 10 ? "0" : "") + hour; - var min = date.getMinutes(); - min = (min < 10 ? "0" : "") + min; - var sec = date.getSeconds(); - sec = (sec < 10 ? "0" : "") + sec; - var year = date.getFullYear(); - var month = date.getMonth() + 1; - month = (month < 10 ? "0" : "") + month; - var day = date.getDate(); - day = (day < 10 ? "0" : "") + day; - return hour + ":" + min + ":" + sec + " on " + day + '/' + month + "/" + year; - }, - setupWatcher: function (f, files, options, watchCallback) { - if (lessWatchCompilerUtilsModule.config.runOnce === true) return; - fs.watchFile(f, options, function (c, p) { - // Check if anything actually changed in stat - if (files[f] && !files[f].isDirectory() && c.nlink !== 0 && files[f].mtime.getTime() == c.mtime.getTime()) return; - files[f] = c; - if (!files[f].isDirectory()) { - if (options.ignoreDotFiles && (path.basename(f)[0] === '.')) return; - if (options.filter && options.filter(f)) return; - fs.exists(f, function (exists) { - if (!exists) { - console.log("Does not exist : " + f) - } else { - fileimportlist[f] = fileSearch.findLessImportsInFile(f); - watchCallback(f, c, p, fileimportlist); - } - }); - } else { - fs.readdir(f, function (err, nfiles) { - if (err) return; - nfiles.forEach(function (b) { - var file = path.join(f, b); - if (!files[file]) { - fs.stat(file, function (err, stat) { - if (options.ignoreDotFiles && (path.basename(b)[0] === '.')) return; - if (options.filter && options.filter(b)) return; - fs.exists(file, function (exists) { - if (!exists) { - console.log("Does not exist : " + f) - } else { - fileimportlist[file] = fileSearch.findLessImportsInFile(file); - watchCallback(file, stat, null, fileimportlist); - files[file] = stat; - lessWatchCompilerUtilsModule.fileWatcher(file, files, options, filelist, fileimportlist, watchCallback); - } - }); - - }) - } - }) - }) - } - if (c.nlink === 0) { - // unwatch removed files. - delete files[f] - fs.unwatchFile(f); - } - }) - }, - fileWatcher: function (f, files, options, filelist, fileimportlist, watchCallback) { - if (filelist.indexOf(f) !== -1) return; - filelist[filelist.length] = f; - - fileimportlist[f] = fileSearch.findLessImportsInFile(f); - lessWatchCompilerUtilsModule.setupWatcher(f, files, options, watchCallback); - for (var i in fileimportlist[f]) { - if (filelist.indexOf(fileimportlist[f][i]) === -1) { - lessWatchCompilerUtilsModule.setupWatcher( - path.normalize(path.dirname(f) + path.sep + fileimportlist[f][i]), - files, - options, - watchCallback - ); - } - } - } - } - return lessWatchCompilerUtilsModule; -}); \ No newline at end of file diff --git a/src/lib/lessWatchCompilerUtils.ts b/src/lib/lessWatchCompilerUtils.ts new file mode 100755 index 0000000..c3b5b0c --- /dev/null +++ b/src/lib/lessWatchCompilerUtils.ts @@ -0,0 +1,203 @@ +"use strict"; +import * as fs from "fs"; +import * as path from "path"; +import * as sys from "util"; +import * as events from "events"; +import * as sh from "shelljs"; +import * as extend from "extend"; +import * as child_process from "child_process"; +import * as FileSearch from "./fileSearch.js"; + +const exec = child_process.exec, + cwd = sh.pwd().toString(), + filelist: string[] = [], + fileimportlist = {}, + fileSearch = new FileSearch.default(); + +// var lessWatchCompilerUtilsModule = { +// walk: function ( +// dir: string, +// options: { ignoreDotFiles: boolean; filter: function }, +// callback: (err: any, object?: object) => void, +// initCallback: () => void, +// callbackOptions: { files: { [key: string]: any }; pending: number } = { +// files: {}, +// pending: 0, +// } +// ) { +// callbackOptions.pending += 1; + +// fs.stat(dir, function (err, stat) { +// if (err) return callback(err); +// callbackOptions.files[dir] = stat; +// fs.readdir(dir, function (err, files) { +// if (err) return callback(err); +// callbackOptions.pending -= 1; +// files.forEach(function (f, index) { +// f = path.join(dir, f); +// callbackOptions.pending += 1; +// fs.stat(f, function (err, stat) { +// var enoent = false, +// done = false; + +// if (err) { +// if (err.code !== "ENOENT") { +// console.log(err.code); +// return callback(err); +// } else { +// enoent = true; +// } +// } +// callbackOptions.pending -= 1; +// done = callbackOptions.pending === 0; +// if (!enoent) { +// callbackOptions.files[f] = stat; +// if (stat.isDirectory()) { +// lessWatchCompilerUtilsModule.walk( +// f, +// options, +// callback, +// initCallback +// ); +// } else { +// if (options.ignoreDotFiles && path.basename(f)[0] === ".") +// return done && callback(null, callbackOptions.files); +// if (options.filter && options.filter(f)) +// return done && callback(null, callbackOptions.files); +// initCallback && initCallback(f); +// } + +// if (done) callback(null, callbackOptions.files); +// } +// }); +// }); +// if (callbackOptions.pending === 0) +// callback(null, callbackOptions.files); +// }); +// if (callbackOptions.pending === 0) callback(null, callbackOptions.files); +// }); +// }, +// //Setup fs.watchFile() for each file. +// watchTree: function (root, options, watchCallback, initCallback) { +// if (!watchCallback) { +// watchCallback = options; +// options = {}; +// } +// lessWatchCompilerUtilsModule.walk( +// root, +// options, +// function (err, files) { +// if (err) throw err; +// lessWatchCompilerUtilsModule.fileWatcher( +// root, +// files, +// options, +// filelist, +// fileimportlist, +// watchCallback +// ); +// for (var i in files) { +// lessWatchCompilerUtilsModule.fileWatcher( +// i, +// files, +// options, +// filelist, +// fileimportlist, +// watchCallback +// ); +// } +// watchCallback(files, null, null, fileimportlist); +// }, +// initCallback +// ); +// }, +// // We build the function to filter the files to watch. +// // Returning true marks a file to be ignored. +// setupWatcher: function (f, files, options, watchCallback) { +// if (Config.runOnce === true) return; +// fs.watchFile(f, options, function (c, p) { +// // Check if anything actually changed in stat +// if ( +// files[f] && +// !files[f].isDirectory() && +// c.nlink !== 0 && +// files[f].mtime.getTime() == c.mtime.getTime() +// ) +// return; +// files[f] = c; +// if (!files[f].isDirectory()) { +// if (options.ignoreDotFiles && path.basename(f)[0] === ".") return; +// if (options.filter && options.filter(f)) return; +// fs.exists(f, function (exists) { +// if (!exists) { +// console.log("Does not exist : " + f); +// } else { +// fileimportlist[f] = fileSearch.findLessImportsInFile(f); +// watchCallback(f, c, p, fileimportlist); +// } +// }); +// } else { +// fs.readdir(f, function (err, nfiles) { +// if (err) return; +// nfiles.forEach(function (b) { +// var file = path.join(f, b); +// if (!files[file]) { +// fs.stat(file, function (err, stat) { +// if (options.ignoreDotFiles && path.basename(b)[0] === ".") +// return; +// if (options.filter && options.filter(b)) return; +// fs.exists(file, function (exists) { +// if (!exists) { +// console.log("Does not exist : " + f); +// } else { +// fileimportlist[file] = +// fileSearch.findLessImportsInFile(file); +// watchCallback(file, stat, null, fileimportlist); +// files[file] = stat; +// lessWatchCompilerUtilsModule.fileWatcher( +// file, +// files, +// options, +// filelist, +// fileimportlist, +// watchCallback +// ); +// } +// }); +// }); +// } +// }); +// }); +// } +// if (c.nlink === 0) { +// // unwatch removed files. +// delete files[f]; +// fs.unwatchFile(f); +// } +// }); +// }, +// fileWatcher: function ( +// f, +// files, +// options, +// filelist, +// fileimportlist, +// watchCallback +// ) { +// if (filelist.indexOf(f) !== -1) return; +// filelist[filelist.length] = f; + +// fileimportlist[f] = fileSearch.findLessImportsInFile(f); +// lessWatchCompilerUtilsModule.setupWatcher(f, files, options, watchCallback); +// for (var i in fileimportlist[f]) { +// if (filelist.indexOf(fileimportlist[f][i]) === -1) { +// lessWatchCompilerUtilsModule.setupWatcher( +// path.normalize(path.dirname(f) + path.sep + fileimportlist[f][i]), +// files, +// options, +// watchCallback +// ); +// } +// } +// }, +// }; diff --git a/src/main.ts b/src/main.ts new file mode 100755 index 0000000..50b39af --- /dev/null +++ b/src/main.ts @@ -0,0 +1,238 @@ +#!/usr/bin/env node + +/* Copyright 2012, Jonathan Cheung Licensed and released under the MIT + license. Refer to MIT-LICENSE.txt. + + A nodejs script that watches folders(and subfolders) for changes and automatically compile the less css files into css. + + Always give credit where it is due. Parts of this script is modified from Mikeal Rogers's watch script (https://github.com/mikeal/watch) + + Basic Usage: less-watch-compiler FOLDER_TO_WATCH FOLDER_TO_OUTPUT + Example: 'less-watch-compiler less css' will watch ./less folder + and compile the less css files into ./css when they are added/updated +*/ +import * as fs from "fs"; +import * as path from "path"; +import * as sh from "shelljs"; +import { Command } from "commander"; +import * as events from "events"; +import { compileCSS, getDateTime, resolveOutputPath,watchTree } from "./lib/Utils"; +import { Options } from "./lib/Options"; +import * as child from "child_process"; + +const cwd = sh.pwd(), + lessWatchCompilerUtils = require("./lib/lessWatchCompilerUtils.cjs.js"), + packagejson = require("../package.json"), + extend = require("extend"), + program = new Command(); + +let mainFilePath: string; +//bypass maxlistener errors because more files means more listeners #90 +events.EventEmitter.defaultMaxListeners = 0; + +program + .version( + packagejson.version, + "-v, -V, --version", + "Output the current version" + ) + .usage("[options] [main_file_name]") + .option( + "--main-file ", + "Specify as the file to always re-compile e.g. '--main-file style.less'." + ) + .option( + "--config ", + "Custom configuration file path.", + "less-watch-compiler.config.json" + ) + .option( + "--run-once", + "Run the compiler once without waiting for additional changes." + ) + .option( + "--include-hidden", + "Don't ignore files beginning with a '.' or a '_'" + ) + //Less Options + .option( + "--enable-js", + "Less.js Option: To enable inline JavaScript in less files." + ) + .option( + "--source-map", + "Less.js Option: To generate source map for css files." + ) + .option( + "--plugins ,", + "Less.js Option: To specify plugins separated by commas." + ) + .option( + "--less-args =,=", + "Less.js Option: To specify any other less options e.g. '--less-args math=strict,strict-units=on,include-path=./dir1\\;./dir2'." + ) + .parse(process.argv); + +const Config = Options.getInstance({ ...program.opts(), args: program.args }); + +// Check if configuration file exists +const configPath: string = Config.config + ? path.isAbsolute(Config.config) + ? Config.config + : cwd + path.sep + Config.config + : "less-watch-compiler.config.json"; + +fs.access(configPath, fs.constants.F_OK, (err) => { + if (!err) { + let data = fs.readFileSync(configPath); + const customConfig = JSON.parse(data.toString()); + console.log("Config file " + configPath + " is loaded."); + extend(true, Config, customConfig); + } + + init(); +}); + +function init(): void { + /* + 3rd parameter is optional, but once you define it, then we will just compile + the main and generate as "{main_file_name}.css". All the files that has been + referenced from the main one will be minified into it. + Assuming the 3rd is "main.less" + - input folder: src + src + main.less (import aux.less) + aux.less + - output folder: dist + dist + main.css + + Otherwise, it will behave as previously: + Assuming the 3rd is empty + - input folder: src + src + main.less (import aux.less) + aux.less + - output folder: dist + dist + main.css + aux.css + */ + + if (!Config.watchFolder || !Config.outputFolder) { + console.log("Missing arguments. Example:"); + console.log( + "\tnode less-watch-compiler.js FOLDER_TO_WATCH FOLDER_TO_OUTPUT" + ); + console.log( + '\tExample 1: To watch all files under the folder "less" and compile all into a folder "css".' + ); + console.log("\t\t less-watch-compiler less css"); + process.exit(1); + } + + Config.watchFolder = path.resolve(Config.watchFolder); + Config.outputFolder = path.resolve(Config.outputFolder); + + if (Config.mainFile) { + mainFilePath = path.resolve(Config.watchFolder, Config.mainFile); + fs.access(mainFilePath, fs.constants.F_OK, (err) => { + console.log( + `Main file ${mainFilePath} ${err ? "does not exist" : "exists"}` + ); + if (err) process.exit(); + }); + } + + if (Config.runOnce === true) + console.log("Running less-watch-compiler only once."); + else console.log("Watching directory for file changes."); + watchTree( + Config.watchFolder, + { + interval: 200, + // If we've set --include-hidden, don't ignore dotfiles + ignoreDotFiles: !Config.includeHidden, + filter: lessWatchCompilerUtils.filterFiles, + }, + ( + f: string, + curr: { nlink: number }, + prev: object, + fileimportlist: string[][] + ) => { + if (typeof f == "object" && prev === null && curr === null) { + // Finished walking the tree + return; + } else if (curr.nlink === 0) { + // f was removed + console.log(f + " was removed."); + } else { + // f is a new file or changed + // console.log(f) + let importedFile = false; + // var filename = f.substring(Config.watchFolder.length+1) + for (var i in fileimportlist) { + for (var k in fileimportlist[i]) { + const hasExtension = path.extname(fileimportlist[i][k]).length > 1, + importFile = path.join( + fileimportlist[i][k], + hasExtension ? "" : ".less" + ), + normalizedPath = path.normalize( + path.dirname(i) + path.sep + importFile + ); + + // console.log('compare ' + f + ' with import #' + k + ' in ' + i + ' value ' + normalized); + if (f == normalizedPath && !mainFilePath) { + // Compile changed file only if a main file is there. + const outputFilePath = resolveOutputPath(i); + + const child_process: child.ChildProcess | undefined = + compileCSS(outputFilePath); + + if (child_process !== undefined) + child_process.on("exit", () => { + console.log( + "The file: " + + i + + " was changed because " + + JSON.stringify(f) + + " is specified as an import. Recompiling " + + outputFilePath + + " at " + + getDateTime() + ); + importedFile = true; + }); + } + } + } + if (!importedFile) { + const path = mainFilePath || f; + const outputFilePath = resolveOutputPath(path); + const child_process: child.ChildProcess | undefined = + compileCSS(outputFilePath); + if (child_process) + child_process.on("exit", () => { + console.log( + "The file: " + + JSON.stringify(path) + + " was changed. Recompiling " + + outputFilePath + + " at " + + getDateTime() + ); + }); + } + } + }, + // init + (f: string) => { + if (!mainFilePath || mainFilePath === f) { + // compile each file when main file is missing or compile main file only once + compileCSS(f); + } + } + ); +} diff --git a/test/Utils.js b/test/Utils.js new file mode 100755 index 0000000..6a56b9d --- /dev/null +++ b/test/Utils.js @@ -0,0 +1,177 @@ +var assert = require("assert"), + Options = require("../dist/lib/Options.js").Options, + sh = require("shelljs"); +const { resolve } = require("path/posix"); + +const {getCommand, compileCSS, resolveOutputPath, getDateTime,filterFiles, watchTree} = require("../dist/lib/Utils.js"), + Config = Options.getInstance(); + +describe("getDateTime()", function () { + it("getDateTime() function should be there and has value", function () { + assert.strictEqual(true, getDateTime().length > 0); + }); + it("getDateTime() format should be correct [HH:MM:SS on DD/MM/YYYY]", function () { + let pattern = new RegExp(/\d+\:\d+\:\d+ on \d+\/\d+\/\d{4}/g); + assert.ok(pattern.test(getDateTime())); + }); +}); +describe("compileCSS()", function () { + this.beforeEach(() => { + Config.reset(); + }); + + it("compileCSS() function should be there", function () { + assert.strictEqual("function", typeof compileCSS); + }); +}); + +describe("watchTree()", function () { + it("watchTree() function should be there", function () { + assert.strictEqual("function", typeof(watchTree)); + }); + // it("watchTree() function should complete and call a callback ", async (done) => { + // await runCommand(done); + // function runCommand(done) { + // watchTree( + // testroot, + // {}, + // function () {}, + // function () {} + // ); + // assert.ok("completed"); + // done(); + // } + // }); +}); + +describe("getCommand()", function () { + beforeEach(()=>{ + Config.reset(); + Config.outputFolder = "testFolder"; + const inputPath = "test.less"; + }) + it("should run the correct command with minified flag", function () { + Config.minified = true, + inputPath="test.less"; + assert.strictEqual( + 'lessc -x "test.less" "testFolder/test.min.css"', + getCommand(inputPath, resolveOutputPath(inputPath)) + ); + }); + it("should run the correct command with enableJs flag", function () { + Config.enableJs = true; + assert.strictEqual( + 'lessc --js "test.less" "testFolder/test.css"', + getCommand(inputPath, resolveOutputPath(inputPath)) + ); + }); + it("should run the correct command with sourceMap flag", function () { + Config.sourceMap = true, + assert.strictEqual( + 'lessc --source-map "test.less" "testFolder/test.css"', + getCommand(inputPath, resolveOutputPath(inputPath)) + ); + }); + it("should run the correct command with 1 plugin", function () { + Config.plugins = "plugin1"; + assert.strictEqual( + 'lessc --plugin1 "test.less" "testFolder/test.css"', + getCommand(inputPath, resolveOutputPath(inputPath)) + ); + }); + it("should run the correct command with 2 plugins", function () { + Config.plugins = "plugin1,plugin2"; + assert.strictEqual( + 'lessc --plugin1 --plugin2 "test.less" "testFolder/test.css"', + getCommand(inputPath, resolveOutputPath(inputPath)) + ); + }); + + it("should run the correct command with minified flag", function () { + Config.minified = true; + assert.strictEqual( + 'lessc -x "test.less" "testFolder/test.min.css"', + getCommand(inputPath, resolveOutputPath(inputPath)) + ); + }); + + it("should run the correct command with math LESS flag", function () { + Config.lessArgs = "math=strict"; + assert.strictEqual( + 'lessc --math=strict "test.less" "testFolder/test.css"', + getCommand(inputPath, resolveOutputPath(inputPath)) + ); + }); + + it("should run the correct command with strict-unit LESS flag", function () { + Config.lessArgs = "strict-units=on"; + assert.strictEqual( + 'lessc --strict-units=on "test.less" "testFolder/test.css"', + getCommand(inputPath, resolveOutputPath(inputPath)) + ); + }); + + it("should run the correct command with math, strict-unit, include-path LESS flags", function () { + Config.lessArgs = "math=strict,strict-units=on,include-path=./dir1;./dir2"; + assert.strictEqual( + 'lessc --math=strict --strict-units=on --include-path=./dir1;./dir2 "test.less" "testFolder/test.css"', + getCommand(inputPath, resolveOutputPath(inputPath)) + ); + }); +}); +describe("resolveOutputPath()", function () { + // reset config + Config.reset(); + + it("should resolve filepaths correctly", function () { + Config.watchFolder = "./inputFolder/inner"; + Config.outputFolder = "./testFolder/nested"; + Config.minified = true; + + // Walker will always return paths relative to watchFolder + assert.strictEqual( + resolveOutputPath("inputFolder/inner/evenmore/afile.less"), + 'testFolder/nested/evenmore/afile.min.css' + ); + }); + + it("should resolve always put output files in output folder", function () { + Config.watchFolder = "./inputFolder/inner"; + Config.outputFolder = "./testFolder/nested"; + Config.minified = true; + + // Main file is relative to watchFolder as well, but can be a relative path + // it should however always land in the destination folder + assert.strictEqual( + resolveOutputPath("inputFolder/inner/../afile.less"), + 'testFolder/nested/afile.min.css' + ); + }); +}); +describe("filterFiles()", function () { + // reset config + this.beforeEach(() => { + Config.reset(); + }); + it("filterFiles() function should be there", function () { + assert.strictEqual("function", typeof filterFiles); + }); + it('filterFiles() function should return "false" for allowed files', function () { + Config.allowedExtensions = [".css"]; + assert.strictEqual(true, filterFiles("file.less")); + assert.strictEqual(false, filterFiles("file.css")); + }); + it('filterFiles() function should return "true" for non-allowed files', function () { + assert.strictEqual(true, filterFiles("file.js")); + }); + it('filterFiles() function should return "true" for hidden files', function () { + assert.strictEqual(true, filterFiles("_file.less")); + assert.strictEqual(true, filterFiles(".file.less")); + }); + it('filterFiles() function should return "false" for hidden files with includeHidden flag', function () { + Config.includeHidden = true; + Config.allowedExtensions = [".less"]; + assert.strictEqual(false, filterFiles("_file.less")); + assert.strictEqual(false, filterFiles(".file.less")); + }); +}); diff --git a/test/filesearch.js b/test/filesearch.js index f102407..111ff8d 100755 --- a/test/filesearch.js +++ b/test/filesearch.js @@ -1,42 +1,46 @@ var assert = require("assert"), - filesearch = require('../dist/lib/filesearch.js'), - sh = require('shelljs'), - cwd = sh.pwd().toString(); - + FileSearch = require("../dist/lib/fileSearch.js"), + sh = require("shelljs"), + cwd = sh.pwd().toString(), + filesearch = new FileSearch.default(); // TODO - Add meaningful tests duh! -describe('filesearch Module', function () { - describe('Should have the following API\'s', function () { - - describe('findLessImportsInFile()', function () { - it('should be a function', function () { - assert.equal("function", typeof (filesearch.findLessImportsInFile)); - }); +describe("filesearch Module", function () { + describe("Should have the following API's", function () { + describe("findLessImportsInFile()", function () { + it("should be a function", function () { + assert.strictEqual("function", typeof filesearch.findLessImportsInFile); + }); - it('should search through a file and find LESS @import statements ', function (done) { - var file = "./test/less/test.less"; - var result = ['lvl1.less', 'lvl2/lvl2.less', 'lvl2/lvl3/lvl3.less', 'hidden/_hidden.less', 'hidden/.hidden2.less'], - filesearchresult = filesearch.findLessImportsInFile(file); - assert.equal(result.toString(), filesearchresult.toString()); - done(); - }); - it('should return empty array if argument isn\'t a file ', function (done) { - var file = "./test/less"; - var result = [], - filesearchresult = filesearch.findLessImportsInFile(file); - assert.equal(result.toString(), filesearchresult.toString()); - done(); - }); - }) - describe('isHiddenFile()', function () { - it('should return `true` on hidden files', function () { - assert.equal(filesearch.isHiddenFile(".hidden.less"), true); - assert.equal(filesearch.isHiddenFile("_hidden.less"), true); - }) - it('should return `false` on non-hidden files', function () { - assert.equal(filesearch.isHiddenFile("non-hidden.less"), false); - }) - }); - }) -}) \ No newline at end of file + it("should search through a file and find LESS @import statements ", function () { + var file = "./test/less/test.less"; + var result = [ + "lvl1.less", + "lvl2/lvl2.less", + "lvl2/lvl3/lvl3.less", + "hidden/_hidden.less", + "hidden/.hidden2.less", + ], + filesearchresult = filesearch.findLessImportsInFile(file); + assert.strictEqual(result.toString(), filesearchresult.toString()); + }); + it("should return empty array if argument isn't a file ", function (done) { + var file = "./test/less"; + var result = [], + filesearchresult = filesearch.findLessImportsInFile(file); + assert.strictEqual(result.toString(), filesearchresult.toString()); + done(); + }); + }); + describe("isHiddenFile()", function () { + it("should return `true` on hidden files", function () { + assert.strictEqual(filesearch.isHiddenFile(".hidden.less"), true); + assert.strictEqual(filesearch.isHiddenFile("_hidden.less"), true); + }); + it("should return `false` on non-hidden files", function () { + assert.strictEqual(filesearch.isHiddenFile("non-hidden.less"), false); + }); + }); + }); +}); diff --git a/test/less-watch-compiler.config.json b/test/less-watch-compiler.config.json index 3f0d9b0..44c85a2 100755 --- a/test/less-watch-compiler.config.json +++ b/test/less-watch-compiler.config.json @@ -1,10 +1,10 @@ { - "allowedExtensions":[".less"], - "enableJs": false, - "runOnce": true, - "minified": false, - "sourceMap": false, - "plugins": "", - "watchFolder": "test/examples/with-config/css", - "outputFolder": "test/examples/with-config/css" -} \ No newline at end of file + "allowedExtensions": [".less"], + "enableJs": false, + "runOnce": true, + "minified": false, + "sourceMap": false, + "plugins": "", + "watchFolder": "test/examples/with-config/css", + "outputFolder": "test/examples/with-config/css" +} diff --git a/test/less-watch-compiler.js b/test/less-watch-compiler.js index 56b2bc1..1a394d2 100644 --- a/test/less-watch-compiler.js +++ b/test/less-watch-compiler.js @@ -141,6 +141,6 @@ describe("The CLI should", function () { }); function cli(...args) { - const command = `node ${path.resolve("dist/less-watch-compiler.js")} ${args.join(" ")}`; + const command = `node ${path.resolve("dist/main.js")} ${args.join(" ")}`; return execSync(command); } diff --git a/test/less/test.less b/test/less/test.less index a2c0129..87f094a 100755 --- a/test/less/test.less +++ b/test/less/test.less @@ -11,3 +11,6 @@ Test Comments @import "lvl2/lvl3/lvl3.less"; @import "hidden/_hidden.less"; @import "hidden/.hidden2.less"; + + + diff --git a/test/lessWatchCompilerUtils.js b/test/lessWatchCompilerUtils.js index cff7820..e3e6e60 100755 --- a/test/lessWatchCompilerUtils.js +++ b/test/lessWatchCompilerUtils.js @@ -1,200 +1,68 @@ var assert = require("assert"), - lessWatchCompilerUtils = require('../dist/lib/lessWatchCompilerUtils.js'), - sh = require('shelljs'), - cwd = sh.pwd().toString(), - testroot = cwd+'/test/less/', - testRelative = './test/less'; + lessWatchCompilerUtils = require("../dist/lib/lessWatchCompilerUtils.cjs.js"), + sh = require("shelljs"), + cwd = sh.pwd().toString(), + testroot = cwd + "/test/less/", + testRelative = "./test/less"; -describe('lessWatchCompilerUtils Module API', function () { - describe('Should have the following API\'s', function () { - describe('walk()', function () { - it('walk() function should be there', function () { - assert.equal("function", typeof (lessWatchCompilerUtils.walk)); - }); - it('walk() function should return an object of files ', async (done) => { - await runCommand(done); - function runCommand (done) { - lessWatchCompilerUtils.walk(testroot, {}, (err, files) => { - for (var i in files) { - assert.equal("object", typeof (files[i])); - } - }, function () {}); - done(); - } - - }); - }) - describe('watchTree()', function () { - it('watchTree() function should be there', function () { - assert.strictEqual("function", typeof (lessWatchCompilerUtils.watchTree)); - }); - it('watchTree() function should complete and call a callback ', async (done) => { - await runCommand(done); - function runCommand (done) { - lessWatchCompilerUtils.watchTree(testroot, {}, function () {}, function() { }); - assert.ok("completed") - done() - } - }); - }) - describe('compileCSS()', function () { - // reset config - lessWatchCompilerUtils.config = {}; - - it('compileCSS() function should be there', function () { - assert.equal("function", typeof (lessWatchCompilerUtils.compileCSS)); - }); - it('should run the correct command with minified flag', function () { - lessWatchCompilerUtils.config = { - outputFolder: "testFolder", - minified: true - }; - assert.equal("lessc -x \"test.less\" \"testFolder/test.min.css\"", lessWatchCompilerUtils.compileCSS("test.less", true).command); - }); - it('should run the correct command with enableJs flag', function () { - lessWatchCompilerUtils.config = { - outputFolder: "testFolder", - enableJs: true - }; - assert.equal("lessc --js \"test.less\" \"testFolder/test.css\"", lessWatchCompilerUtils.compileCSS("test.less", true).command); - }); - it('should run the correct command with sourceMap flag', function () { - lessWatchCompilerUtils.config = { - outputFolder: "testFolder", - sourceMap: true - }; - assert.equal("lessc --source-map \"test.less\" \"testFolder/test.css\"", lessWatchCompilerUtils.compileCSS("test.less", true).command); - }); - it('should run the correct command with 1 plugin', function () { - lessWatchCompilerUtils.config = { - outputFolder: "testFolder", - plugins: "plugin1" - }; - assert.equal("lessc --plugin1 \"test.less\" \"testFolder/test.css\"", lessWatchCompilerUtils.compileCSS("test.less", true).command); - }); - it('should run the correct command with 2 plugins', function () { - lessWatchCompilerUtils.config = { - outputFolder: "testFolder", - plugins: "plugin1,plugin2" - }; - assert.equal("lessc --plugin1 --plugin2 \"test.less\" \"testFolder/test.css\"", lessWatchCompilerUtils.compileCSS("test.less", true).command); - }); - - it('should run the correct command with minified flag', function () { - lessWatchCompilerUtils.config = { - outputFolder: "testFolder", - minified: true - }; - assert.equal("lessc -x \"test.less\" \"testFolder/test.min.css\"", lessWatchCompilerUtils.compileCSS("test.less", true).command); - }); - - it('should run the correct command with math LESS flag', function () { - lessWatchCompilerUtils.config = { - outputFolder: "testFolder", - lessArgs: "math=strict" - }; - assert.equal("lessc --math=strict \"test.less\" \"testFolder/test.css\"", lessWatchCompilerUtils.compileCSS("test.less", true).command); - }); - - it('should run the correct command with strict-unit LESS flag', function () { - lessWatchCompilerUtils.config = { - outputFolder: "testFolder", - lessArgs: 'strict-units=on' - }; - assert.equal("lessc --strict-units=on \"test.less\" \"testFolder/test.css\"", lessWatchCompilerUtils.compileCSS("test.less", true).command); - }); - - it('should run the correct command with math, strict-unit, include-path LESS flags', function () { - lessWatchCompilerUtils.config = { - outputFolder: "testFolder", - lessArgs: 'math=strict,strict-units=on,include-path=./dir1\;./dir2' - }; - assert.equal("lessc --math=strict --strict-units=on --include-path=./dir1\;./dir2 \"test.less\" \"testFolder/test.css\"", lessWatchCompilerUtils.compileCSS("test.less", true).command); - }); - }); - describe('resolveOutputPath()', function () { - // reset config - lessWatchCompilerUtils.config = {}; - - it('should resolve filepaths correctly', function () { - lessWatchCompilerUtils.config = { - watchFolder: "./inputFolder/inner", - outputFolder: "./testFolder/nested", - minified: true - }; - - // Walker will always return paths relative to watchFolder - assert.equal(lessWatchCompilerUtils.resolveOutputPath('inputFolder/inner/evenmore/afile.less'), '\"testFolder/nested/evenmore/afile.min.css\"'); - }); - - it('should resolve always put output files in output folder', function () { - lessWatchCompilerUtils.config = { - watchFolder: "./inputFolder/inner", - outputFolder: "./testFolder/nested", - minified: true - }; - - // Main file is relative to watchFolder as well, but can be a relative path - // it should however always land in the destination folder - assert.equal(lessWatchCompilerUtils.resolveOutputPath('inputFolder/inner/../afile.less'), '\"testFolder/nested/afile.min.css\"'); - }); - }); - describe('filterFiles()', function () { - // reset config - lessWatchCompilerUtils.config = {}; - - it('filterFiles() function should be there' + JSON.stringify(lessWatchCompilerUtils.config), function () { - assert.equal("function", typeof (lessWatchCompilerUtils.filterFiles)); - }); - it('filterFiles() function should return "false" for allowed files:' + JSON.stringify(lessWatchCompilerUtils.config), function () { - assert.equal(false, lessWatchCompilerUtils.filterFiles("file.less")); - - lessWatchCompilerUtils.config.allowedExtensions = [".css"] - assert.equal(false, lessWatchCompilerUtils.filterFiles("file.css")); - lessWatchCompilerUtils.config = {} - }); - it('filterFiles() function should return "true" for non-allowed files' + JSON.stringify(lessWatchCompilerUtils.config), function () { - assert.equal(true, lessWatchCompilerUtils.filterFiles("file.js")); - }); - it('filterFiles() function should return "true" for hidden files' + JSON.stringify(lessWatchCompilerUtils.config), function () { - assert.equal(true, lessWatchCompilerUtils.filterFiles("_file.less")); - assert.equal(true, lessWatchCompilerUtils.filterFiles(".file.less")); - }); - it('filterFiles() function should return "false" for hidden files with includeHidden flag' + JSON.stringify(lessWatchCompilerUtils.config), function () { - lessWatchCompilerUtils.config.includeHidden = true - assert.equal(false, lessWatchCompilerUtils.filterFiles("_file.less")); - assert.equal(false, lessWatchCompilerUtils.filterFiles(".file.less")); - lessWatchCompilerUtils.config = {} - }); - - }) - describe('getDateTime()', function () { - it('getDateTime() function should be there and has value', function () { - assert.equal(true, lessWatchCompilerUtils.getDateTime().length > 0); - }); - }) - describe('setupWatcher()', function () { - it('setupWatcher() function should be there', function () { - assert.equal("function", typeof (lessWatchCompilerUtils.setupWatcher)); - }); - it('setupWatcher() function should take the correct parameters', function (done) { - lessWatchCompilerUtils.setupWatcher(cwd, {}, {}, function () {}); - done(); - }); - }) - describe('fileWatcher()', function () { - it('fileWatcher() function should be there', function () { - assert.equal("function", typeof (lessWatchCompilerUtils.fileWatcher)); - }); - it('fileWatcher() function should take the correct parameters', function (done) { - lessWatchCompilerUtils.fileWatcher(cwd, {}, {}, [], [],function () {}); - done(); - }); - it('fileWatcher() function should not fail for relative paths', function (done) { - lessWatchCompilerUtils.fileWatcher(testRelative, {}, {}, [], [],function () {}); - done(); - }); - }) - - }) -}) \ No newline at end of file +describe("lessWatchCompilerUtils Module API", function () { + describe("Should have the following API's", function () { + // describe("walk()", function () { + // it("walk() function should be there", function () { + // assert.strictEqual("function", typeof lessWatchCompilerUtils.walk); + // }); + // it("walk() function should return an object of files ", async (done) => { + // await runCommand(done); + // function runCommand(done) { + // lessWatchCompilerUtils.walk( + // testroot, + // {}, + // (err, files) => { + // for (var i in files) { + // assert.strictEqual("object", typeof files[i]); + // } + // }, + // function () {} + // ); + // done(); + // } + // }); + // }); + + describe("setupWatcher()", function () { + it("setupWatcher() function should be there", function () { + assert.strictEqual( + "function", + typeof lessWatchCompilerUtils.setupWatcher + ); + }); + it("setupWatcher() function should take the correct parameters", function (done) { + lessWatchCompilerUtils.setupWatcher(cwd, {}, {}, function () {}); + done(); + }); + }); + describe("fileWatcher()", function () { + it("fileWatcher() function should be there", function () { + assert.strictEqual( + "function", + typeof lessWatchCompilerUtils.fileWatcher + ); + }); + it("fileWatcher() function should take the correct parameters", function (done) { + lessWatchCompilerUtils.fileWatcher(cwd, {}, {}, [], [], function () {}); + done(); + }); + it("fileWatcher() function should not fail for relative paths", function (done) { + lessWatchCompilerUtils.fileWatcher( + testRelative, + {}, + {}, + [], + [], + function () {} + ); + done(); + }); + }); + }); +}); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..21aa61a --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,73 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig.json to read more about this file */ + + /* Basic Options */ + // "incremental": true, /* Enable incremental compilation */ + "target": "ES3", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */ + "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ + // "lib": [], /* Specify library files to be included in the compilation. */ + "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */ + // "declaration": true, /* Generates corresponding '.d.ts' file. */ + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + // "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + "outDir": "./dist", /* Redirect output structure to the directory. */ + "rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ + "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + + /* Strict Type-Checking Options */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an 'override' modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */ + + /* Module Resolution Options */ + // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + + /* Advanced Options */ + "skipLibCheck": true, /* Skip type checking of declaration files. */ + "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ + }, + "exclude": ["dist/*", "test/*"] +} diff --git a/yarn.lock b/yarn.lock index 6dbf513..682f7c4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -58,18 +58,54 @@ dependencies: chalk "^4.0.0" -"@types/commander@2.12.2": +"@types/commander@^2.12.2": version "2.12.2" resolved "https://registry.yarnpkg.com/@types/commander/-/commander-2.12.2.tgz#183041a23842d4281478fa5d23c5ca78e6fd08ae" integrity sha512-0QEFiR8ljcHp9bAbWxecjVRuAMr16ivPiGOw6KFQBVrVd0RQIcM3xKdRisH2EDWgVWujiYtHwhSkSUoAAGzH7Q== dependencies: commander "*" +"@types/extend@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/extend/-/extend-3.0.1.tgz#923dc2d707d944382433e01d6cc0c69030ab2c75" + integrity sha512-R1g/VyKFFI2HLC1QGAeTtCBWCo6n75l41OnsVYNbmKG+kempOESaodf6BeJyUM3Q0rKa/NQcTHbB2+66lNnxLw== + +"@types/glob@*": + version "7.1.4" + resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.4.tgz#ea59e21d2ee5c517914cb4bc8e4153b99e566672" + integrity sha512-w+LsMxKyYQm347Otw+IfBXOv9UWVjpHpCDdbBMt8Kz/xbvCYNjP+0qPh91Km3iKfSRLBB0P7fAMf0KHrPu+MyA== + dependencies: + "@types/minimatch" "*" + "@types/node" "*" + +"@types/minimatch@*": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40" + integrity sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ== + +"@types/node@*": + version "16.3.3" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.3.3.tgz#0c30adff37bbbc7a50eb9b58fae2a504d0d88038" + integrity sha512-8h7k1YgQKxKXWckzFCMfsIwn0Y61UK6tlD6y2lOb3hTOIMlK3t9/QwHOhc81TwU+RMf0As5fj7NPjroERCnejQ== + +"@types/node@^16.3.2": + version "16.3.2" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.3.2.tgz#655432817f83b51ac869c2d51dd8305fb8342e16" + integrity sha512-jJs9ErFLP403I+hMLGnqDRWT0RYKSvArxuBVh2veudHV7ifEC1WAmjJADacZ7mRbA2nWgHtn8xyECMAot0SkAw== + "@types/parse-json@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== +"@types/shelljs@^0.8.9": + version "0.8.9" + resolved "https://registry.yarnpkg.com/@types/shelljs/-/shelljs-0.8.9.tgz#45dd8501aa9882976ca3610517dac3831c2fbbf4" + integrity sha512-flVe1dvlrCyQJN/SGrnBxqHG+RzXrVKsmjD8WS/qYHpq5UPjfq7UWFBENP0ZuOl0g6OpAlL6iBoLSvKYUUmyQw== + dependencies: + "@types/glob" "*" + "@types/node" "*" + "@ungap/promise-all-settled@1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44" @@ -2966,6 +3002,11 @@ tslib@^1.10.0, tslib@^1.9.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== +typescript@^4.3.5: + version "4.3.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.5.tgz#4d1c37cc16e893973c45a06886b7113234f119f4" + integrity sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA== + union-value@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847"