mikemorris revised this gist 9 months ago. Go to revision
8 files changed, 872 insertions
assets-watcher.js(file created)
| @@ -0,0 +1,140 @@ | |||
| 1 | + | import chokidar from 'chokidar'; | |
| 2 | + | import fs from 'fs' | |
| 3 | + | import fse from 'fs-extra'; | |
| 4 | + | import child_process from 'child_process'; | |
| 5 | + | ||
| 6 | + | // ==================================================================== | |
| 7 | + | // | | |
| 8 | + | // ==================================================================== | |
| 9 | + | function log(message) { | |
| 10 | + | const options = { | |
| 11 | + | encoding: "utf-8", | |
| 12 | + | flag: "a+", | |
| 13 | + | }; | |
| 14 | + | const datetime = new Date().toISOString() | |
| 15 | + | message = `[${datetime}] ${message}\n` | |
| 16 | + | fse.outputFileSync('/logs/builder/assets.log', message, options); | |
| 17 | + | console.log(message) | |
| 18 | + | } | |
| 19 | + | ||
| 20 | + | // ==================================================================== | |
| 21 | + | // | | |
| 22 | + | // ==================================================================== | |
| 23 | + | function copyAllIntoWWW(paths) { | |
| 24 | + | return new Promise((resolve, reject) => { | |
| 25 | + | for(const path of paths) { | |
| 26 | + | log(path) | |
| 27 | + | if (!fs.existsSync(path)) { | |
| 28 | + | log(`Directory does not exist in project: ${path}`) | |
| 29 | + | continue; | |
| 30 | + | } | |
| 31 | + | ||
| 32 | + | var publicPath = path | |
| 33 | + | publicPath = publicPath.replace('/app','/www') | |
| 34 | + | try { | |
| 35 | + | child_process.execSync(`mkdir -p ${publicPath} & cp -rf ${path}/* ${publicPath}`); | |
| 36 | + | log(`Done copying: ${path}`) | |
| 37 | + | } catch (err) { | |
| 38 | + | console.error(err) | |
| 39 | + | } | |
| 40 | + | } | |
| 41 | + | }); | |
| 42 | + | } | |
| 43 | + | ||
| 44 | + | // ==================================================================== | |
| 45 | + | // | | |
| 46 | + | // ==================================================================== | |
| 47 | + | function copyIntoWWW(path) { | |
| 48 | + | ||
| 49 | + | // Do not compile inside .delete folders | |
| 50 | + | if (path.includes(".delete")) { | |
| 51 | + | return | |
| 52 | + | } | |
| 53 | + | ||
| 54 | + | log(path); | |
| 55 | + | ||
| 56 | + | var publicPath = path | |
| 57 | + | publicPath = publicPath.replace('/app','/www') | |
| 58 | + | ||
| 59 | + | try { | |
| 60 | + | child_process.execSync(`cp -rf ${path} ${publicPath}`); | |
| 61 | + | log(`Done copying: ${path}`) | |
| 62 | + | } catch (err) { | |
| 63 | + | console.error(err) | |
| 64 | + | } | |
| 65 | + | } | |
| 66 | + | ||
| 67 | + | // ==================================================================== | |
| 68 | + | // | | |
| 69 | + | // ==================================================================== | |
| 70 | + | function deleteFromWWW(path) { | |
| 71 | + | ||
| 72 | + | // Do not compile inside .delete folders | |
| 73 | + | if (path.includes(".delete")) { | |
| 74 | + | return | |
| 75 | + | } | |
| 76 | + | ||
| 77 | + | log(path); | |
| 78 | + | ||
| 79 | + | var publicPath = path | |
| 80 | + | publicPath = publicPath.replace('/app','/www') | |
| 81 | + | ||
| 82 | + | try { | |
| 83 | + | child_process.execSync(`rm -rf ${path} ${publicPath}`); | |
| 84 | + | log(`Done deleting: ${path}`) | |
| 85 | + | } catch (err) { | |
| 86 | + | console.error(err) | |
| 87 | + | } | |
| 88 | + | } | |
| 89 | + | ||
| 90 | + | ||
| 91 | + | // ==================================================================== | |
| 92 | + | // | | |
| 93 | + | // ==================================================================== | |
| 94 | + | function startWatcher(watchFolders) { | |
| 95 | + | // https://github.com/paulmillr/chokidar | |
| 96 | + | const options = { | |
| 97 | + | awaitFinish: true, | |
| 98 | + | ignoreInitial: true, | |
| 99 | + | usePolling: true, | |
| 100 | + | interval: 100, | |
| 101 | + | depth: 5 | |
| 102 | + | } | |
| 103 | + | ||
| 104 | + | const watcher = chokidar.watch(watchFolders, options).on('all', (event, path) => { | |
| 105 | + | log(event, path); | |
| 106 | + | }); | |
| 107 | + | ||
| 108 | + | watcher | |
| 109 | + | .on('add', path => copyIntoWWW(path)) | |
| 110 | + | .on('addDir', path => copyIntoWWW(path)) | |
| 111 | + | .on('change', path => copyIntoWWW(path)) | |
| 112 | + | .on('unlink', path => deleteFromWWW(path)) | |
| 113 | + | .on('unlinkDir', path => deleteFromWWW(path)); | |
| 114 | + | ||
| 115 | + | watcher | |
| 116 | + | .on('ready', () => log('********** Watch is running **********')); | |
| 117 | + | } | |
| 118 | + | ||
| 119 | + | ||
| 120 | + | // ==================================================================== | |
| 121 | + | // | MAIN | |
| 122 | + | // ==================================================================== | |
| 123 | + | async function go() { | |
| 124 | + | const watchFolders = [ | |
| 125 | + | '/app/css', | |
| 126 | + | '/app/docs', | |
| 127 | + | '/app/fonts', | |
| 128 | + | '/app/images', | |
| 129 | + | '/app/libs', | |
| 130 | + | '/app/lib' | |
| 131 | + | ] | |
| 132 | + | if (process.argv[2] == 'recompile') { | |
| 133 | + | await copyAllIntoWWW(watchFolders) | |
| 134 | + | startWatcher(watchFolders); | |
| 135 | + | } else { | |
| 136 | + | startWatcher(watchFolders); | |
| 137 | + | } | |
| 138 | + | } | |
| 139 | + | ||
| 140 | + | go() | |
builder-init.sh(file created)
| @@ -0,0 +1,23 @@ | |||
| 1 | + | #!/bin/bash | |
| 2 | + | CANARY_FILE=/app/.provisioning/builder-rebuild | |
| 3 | + | ||
| 4 | + | if [ ! -f "$CANARY_FILE" ] ; then | |
| 5 | + | echo "Initialization incomplete! Running Builder Rebulid..." | |
| 6 | + | cd /opt/builder/app && bash ./clean-and-build.sh & | |
| 7 | + | mkdir -p /app/.provisioning && date +%F_%T >> /app/.provisioning/builder-rebuild | |
| 8 | + | else | |
| 9 | + | ||
| 10 | + | ln -sf /app/docs /www | |
| 11 | + | ln -sf /app/fonts /www | |
| 12 | + | ln -sf /app/images /www | |
| 13 | + | ln -sf /app/libs /www | |
| 14 | + | ln -sf /app/lib /www | |
| 15 | + | ||
| 16 | + | pm2 delete all | |
| 17 | + | pm2 start /opt/builder/app/sass-watcher.js | |
| 18 | + | pm2 start /opt/builder/app/javascript-watcher.js | |
| 19 | + | pm2 start /opt/builder/app/jade-watcher.js | |
| 20 | + | # pm2 start /opt/builder/app/pug-watcher.js | |
| 21 | + | # pm2 start /opt/builder/app/assets-watcher.js | |
| 22 | + | ||
| 23 | + | fi | |
clean-and-build.sh(file created)
| @@ -0,0 +1,20 @@ | |||
| 1 | + | #!/bin/bash | |
| 2 | + | pm2 stop all | |
| 3 | + | ||
| 4 | + | mv /www/index.php /tmp/index.php | |
| 5 | + | rm -rf /www/* | |
| 6 | + | mv /tmp/index.php /www/index.php | |
| 7 | + | ||
| 8 | + | ln -sf /app/docs /www | |
| 9 | + | ln -sf /app/fonts /www | |
| 10 | + | ln -sf /app/images /www | |
| 11 | + | ln -sf /app/libs /www | |
| 12 | + | ln -sf /app/lib /www | |
| 13 | + | ||
| 14 | + | # PM2 Style | |
| 15 | + | pm2 delete all | |
| 16 | + | pm2 start sass-watcher.js -- recompile | |
| 17 | + | pm2 start javascript-watcher.js -- recompile | |
| 18 | + | pm2 start jade-watcher.js -- recompile | |
| 19 | + | # pm2 start pug-watcher.js -- recompile | |
| 20 | + | # pm2 start assets-watcher.js -- recompile | |
jade-watcher.js(file created)
| @@ -0,0 +1,224 @@ | |||
| 1 | + | import chokidar from 'chokidar'; | |
| 2 | + | import fs from 'fs' | |
| 3 | + | import fse from 'fs-extra'; | |
| 4 | + | import jade from 'jade'; | |
| 5 | + | import klaw from 'klaw'; | |
| 6 | + | import beautify from 'beautify'; | |
| 7 | + | import cleaner from 'clean-html'; | |
| 8 | + | import fg from 'fast-glob'; | |
| 9 | + | import inliner from '@css-inline/css-inline'; | |
| 10 | + | ||
| 11 | + | // ==================================================================== | |
| 12 | + | // | | |
| 13 | + | // ==================================================================== | |
| 14 | + | function log(message) { | |
| 15 | + | const options = { | |
| 16 | + | encoding: "utf-8", | |
| 17 | + | flag: "a+", | |
| 18 | + | }; | |
| 19 | + | const datetime = new Date().toISOString() | |
| 20 | + | message = `[${datetime}] ${message}\n` | |
| 21 | + | fse.outputFileSync('/logs/builder/jade.log', message, options); | |
| 22 | + | console.log(message) | |
| 23 | + | } | |
| 24 | + | ||
| 25 | + | // ==================================================================== | |
| 26 | + | // | | |
| 27 | + | // ==================================================================== | |
| 28 | + | function compile(path) { | |
| 29 | + | ||
| 30 | + | log(`${path} was changed`) | |
| 31 | + | ||
| 32 | + | // Do not compile inside .delete folders | |
| 33 | + | if (path.includes(".delete")) { | |
| 34 | + | return | |
| 35 | + | } | |
| 36 | + | ||
| 37 | + | // Do not compile inside _partials folders | |
| 38 | + | if (path.includes("layouts")) { | |
| 39 | + | log("Recompiling /app/views/layouts") | |
| 40 | + | compileAll('/app/views/layouts/**/*.jade') | |
| 41 | + | return | |
| 42 | + | } | |
| 43 | + | ||
| 44 | + | // Do not compile inside _partials folders | |
| 45 | + | if (path.includes("_partials") || path.includes("partials")) { | |
| 46 | + | log("Recompiling /app/views") | |
| 47 | + | compileAll('/app/views/**/*.jade') | |
| 48 | + | return | |
| 49 | + | } | |
| 50 | + | ||
| 51 | + | // Do not compile partials (begins with _) | |
| 52 | + | const filename = path.split("/").slice(-1).toString() | |
| 53 | + | if (filename.startsWith("_")) { | |
| 54 | + | const underscorecount = (filename.match(/_/g) || []).length; | |
| 55 | + | const directoryIndex = 0 - underscorecount | |
| 56 | + | const parentDirectory = path.split("/").slice(0,directoryIndex).join("/")+'/**/*.jade' | |
| 57 | + | log("Recompiling "+parentDirectory) | |
| 58 | + | compileAll( parentDirectory ) | |
| 59 | + | return | |
| 60 | + | } | |
| 61 | + | ||
| 62 | + | // Do not compile if the file does not end with .jade | |
| 63 | + | if (!path.endsWith(".jade")) { | |
| 64 | + | return | |
| 65 | + | } | |
| 66 | + | ||
| 67 | + | log(` Recompiling ${path}`) | |
| 68 | + | const options = { | |
| 69 | + | basedir: '/app/views' | |
| 70 | + | } | |
| 71 | + | var result = ''; | |
| 72 | + | result = jade.renderFile(path,options); | |
| 73 | + | ||
| 74 | + | var publicPath = path | |
| 75 | + | publicPath = publicPath.replace('/app/views','/www') | |
| 76 | + | publicPath = publicPath.replace('.jade','.html') | |
| 77 | + | ||
| 78 | + | // Convert to CSS Inline for Email Templates | |
| 79 | + | // if (publicPath.includes("/www/emails")) { | |
| 80 | + | // console.log('Inlining Email Template CSS') | |
| 81 | + | // result = inliner.inline(result); | |
| 82 | + | // console.log('Inlining Email Template CSS Done'); | |
| 83 | + | // } | |
| 84 | + | ||
| 85 | + | //- | |
| 86 | + | //- Clean HTML Section. Could cause more harm then good. Disabled for now | |
| 87 | + | //- | |
| 88 | + | // try { | |
| 89 | + | // cleaner.clean(result, output => { | |
| 90 | + | // fse.outputFileSync(publicPath, output); | |
| 91 | + | // }); | |
| 92 | + | // } catch (err) { | |
| 93 | + | // try { | |
| 94 | + | // var pretty_options = {format: 'html'} | |
| 95 | + | // fse.outputFileSync(publicPath, beautify(result, pretty_options)); | |
| 96 | + | // } catch (err) { | |
| 97 | + | // fse.outputFileSync(publicPath, result); | |
| 98 | + | // log(`[ERROR] HTML Cleaner: ${path}`) | |
| 99 | + | // log(err) | |
| 100 | + | // log(`[ERROR] Unformatted jade file was saved`) | |
| 101 | + | // } | |
| 102 | + | // } | |
| 103 | + | fse.outputFileSync(publicPath, result); | |
| 104 | + | } | |
| 105 | + | ||
| 106 | + | // ==================================================================== | |
| 107 | + | // | | |
| 108 | + | // ==================================================================== | |
| 109 | + | function compileParents(path) { | |
| 110 | + | ||
| 111 | + | // Do not compile inside .delete folders | |
| 112 | + | if (path.includes(".delete")) { | |
| 113 | + | return | |
| 114 | + | } | |
| 115 | + | ||
| 116 | + | // Do not compile partials (begins with _) | |
| 117 | + | const filename = path.split("/").slice(-1).toString() | |
| 118 | + | if (filename.startsWith("_")) { | |
| 119 | + | return | |
| 120 | + | } | |
| 121 | + | ||
| 122 | + | // Do not compile inside _partials folders | |
| 123 | + | if (path.includes("_partials") || path.includes("partials")) { | |
| 124 | + | return | |
| 125 | + | } | |
| 126 | + | ||
| 127 | + | // Do not compile if the file does not end with .jade | |
| 128 | + | if (!path.endsWith(".jade")) { | |
| 129 | + | return | |
| 130 | + | } | |
| 131 | + | ||
| 132 | + | // log(` Recompiling ${path}`) | |
| 133 | + | const options = { | |
| 134 | + | basedir: '/app/views' | |
| 135 | + | } | |
| 136 | + | var result = ''; | |
| 137 | + | result = jade.renderFile(path, options); | |
| 138 | + | ||
| 139 | + | var publicPath = path | |
| 140 | + | publicPath = publicPath.replace('/app/views','/www') | |
| 141 | + | publicPath = publicPath.replace('.jade','.html') | |
| 142 | + | ||
| 143 | + | // Convert to CSS Inline for Email Templates | |
| 144 | + | // if (publicPath.includes("/www/emails")) { | |
| 145 | + | // console.log('Inlining Email Template CSS') | |
| 146 | + | // result = inliner.inline(result); | |
| 147 | + | // console.log('Inlining Email Template CSS Done'); | |
| 148 | + | // } | |
| 149 | + | ||
| 150 | + | //- | |
| 151 | + | //- Clean HTML Section. Could cause more harm then good. Disabled for now | |
| 152 | + | //- | |
| 153 | + | // try { | |
| 154 | + | // cleaner.clean(result, output => { | |
| 155 | + | // fse.outputFileSync(publicPath, output); | |
| 156 | + | // }); | |
| 157 | + | // } catch (err) { | |
| 158 | + | // try { | |
| 159 | + | // var pretty_options = {format: 'html'} | |
| 160 | + | // fse.outputFileSync(publicPath, beautify(result, pretty_options)); | |
| 161 | + | // } catch (err) { | |
| 162 | + | // fse.outputFileSync(publicPath, result); | |
| 163 | + | // log(`[ERROR] HTML Cleaner: ${path}`) | |
| 164 | + | // log(err) | |
| 165 | + | // log(`[ERROR] Unformatted jade file was saved`) | |
| 166 | + | // } | |
| 167 | + | // } | |
| 168 | + | fse.outputFileSync(publicPath, result); | |
| 169 | + | ||
| 170 | + | } | |
| 171 | + | ||
| 172 | + | // ==================================================================== | |
| 173 | + | // | | |
| 174 | + | // ==================================================================== | |
| 175 | + | function compileAll(glob) { | |
| 176 | + | return new Promise((resolve, reject) => { | |
| 177 | + | const jadeFiles = fg.globSync(glob) | |
| 178 | + | for (const file of jadeFiles) { | |
| 179 | + | compileParents(file); | |
| 180 | + | } | |
| 181 | + | }); | |
| 182 | + | } | |
| 183 | + | ||
| 184 | + | // ==================================================================== | |
| 185 | + | // | | |
| 186 | + | // ==================================================================== | |
| 187 | + | function startWatcher() { | |
| 188 | + | const watchFolders = [ | |
| 189 | + | '/app/views/**/*.jade', | |
| 190 | + | ] | |
| 191 | + | ||
| 192 | + | // https://github.com/paulmillr/chokidar | |
| 193 | + | const options = { | |
| 194 | + | awaitFinish: true, | |
| 195 | + | ignoreInitial: true, | |
| 196 | + | usePolling: true, | |
| 197 | + | interval: 100 | |
| 198 | + | } | |
| 199 | + | ||
| 200 | + | const watcher = chokidar.watch(watchFolders, options).on('all', (event, path) => { | |
| 201 | + | }); | |
| 202 | + | ||
| 203 | + | watcher | |
| 204 | + | .on('add', path => compile(path)) | |
| 205 | + | .on('change', path => compile(path)) | |
| 206 | + | .on('unlink', path => compile(path)); | |
| 207 | + | ||
| 208 | + | watcher | |
| 209 | + | .on('ready', () => log('********** Watch is running **********')); | |
| 210 | + | } | |
| 211 | + | ||
| 212 | + | // ==================================================================== | |
| 213 | + | // | MAIN | |
| 214 | + | // ==================================================================== | |
| 215 | + | async function go() { | |
| 216 | + | if (process.argv[2] == 'recompile') { | |
| 217 | + | await compileAll('/app/views/**/*.jade'); | |
| 218 | + | startWatcher(); | |
| 219 | + | } else { | |
| 220 | + | startWatcher(); | |
| 221 | + | } | |
| 222 | + | } | |
| 223 | + | ||
| 224 | + | go() | |
javascript-watcher.js(file created)
| @@ -0,0 +1,107 @@ | |||
| 1 | + | import chokidar from 'chokidar'; | |
| 2 | + | import fs from 'fs' | |
| 3 | + | import fse from 'fs-extra'; | |
| 4 | + | import path from 'path'; | |
| 5 | + | import fg from 'fast-glob'; | |
| 6 | + | ||
| 7 | + | // ==================================================================== | |
| 8 | + | // | | |
| 9 | + | // ==================================================================== | |
| 10 | + | function log(message) { | |
| 11 | + | const options = { | |
| 12 | + | encoding: "utf-8", | |
| 13 | + | flag: "a+", | |
| 14 | + | }; | |
| 15 | + | const datetime = new Date().toISOString() | |
| 16 | + | message = `[${datetime}] ${message}\n` | |
| 17 | + | fse.outputFileSync('/logs/builder/javascript.log', message, options); | |
| 18 | + | console.log(message) | |
| 19 | + | } | |
| 20 | + | ||
| 21 | + | // ==================================================================== | |
| 22 | + | // | | |
| 23 | + | // ==================================================================== | |
| 24 | + | function concatJavascript(path) { | |
| 25 | + | log('-----------------------'); | |
| 26 | + | log(path); | |
| 27 | + | ||
| 28 | + | var publicPath = path | |
| 29 | + | publicPath = publicPath.replace('/app/js','/www/js') | |
| 30 | + | ||
| 31 | + | var jsFolder = path; | |
| 32 | + | jsFolder = jsFolder.split("/")[3]; | |
| 33 | + | ||
| 34 | + | const source = `/app/js/${jsFolder}` | |
| 35 | + | const destination = `/www/js/${jsFolder}.js` | |
| 36 | + | fs.readdir(source, (err, files) => { | |
| 37 | + | if (err) throw err; | |
| 38 | + | ||
| 39 | + | fse.ensureFileSync(destination) | |
| 40 | + | fs.writeFileSync(destination,'') | |
| 41 | + | var sortedfiles = files.sort() | |
| 42 | + | sortedfiles.forEach(file => { | |
| 43 | + | const filePath = `${source}/${file}` | |
| 44 | + | var contents = fs.readFileSync(filePath); | |
| 45 | + | const spacer = `\n// ====================================================================\n// |\n// |\n// | ${file}\n// |\n// |\n// ====================================================================\n` | |
| 46 | + | fs.appendFileSync(destination, spacer+contents) | |
| 47 | + | }); | |
| 48 | + | }); | |
| 49 | + | ||
| 50 | + | } | |
| 51 | + | ||
| 52 | + | // ==================================================================== | |
| 53 | + | // | | |
| 54 | + | // ==================================================================== | |
| 55 | + | function compileAll(glob) { | |
| 56 | + | return new Promise((resolve, reject) => { | |
| 57 | + | const javascriptFiles = fg.globSync(glob) | |
| 58 | + | for (const file of javascriptFiles) { | |
| 59 | + | concatJavascript(file); | |
| 60 | + | } | |
| 61 | + | log(`Done`) | |
| 62 | + | }); | |
| 63 | + | } | |
| 64 | + | ||
| 65 | + | // ==================================================================== | |
| 66 | + | // | | |
| 67 | + | // ==================================================================== | |
| 68 | + | function startWatcher(watchFolders) { | |
| 69 | + | ||
| 70 | + | // https://github.com/paulmillr/chokidar | |
| 71 | + | const options = { | |
| 72 | + | awaitFinish: true, | |
| 73 | + | ignoreInitial: true, | |
| 74 | + | usePolling: true, | |
| 75 | + | interval: 100 | |
| 76 | + | } | |
| 77 | + | ||
| 78 | + | const watcher = chokidar.watch(watchFolders, options).on('all', (event, path) => { | |
| 79 | + | log(event, path); | |
| 80 | + | }); | |
| 81 | + | ||
| 82 | + | watcher | |
| 83 | + | .on('add', path => concatJavascript(path)) | |
| 84 | + | .on('change', path => concatJavascript(path)) | |
| 85 | + | .on('unlink', path => concatJavascript(path)); | |
| 86 | + | ||
| 87 | + | watcher | |
| 88 | + | .on('ready', () => log('********** Watch is running **********')); | |
| 89 | + | ||
| 90 | + | } | |
| 91 | + | ||
| 92 | + | // ==================================================================== | |
| 93 | + | // | MAIN | |
| 94 | + | // ==================================================================== | |
| 95 | + | async function go() { | |
| 96 | + | const watchFolders = [ | |
| 97 | + | '/app/js' | |
| 98 | + | ] | |
| 99 | + | if (process.argv[2] == 'recompile') { | |
| 100 | + | await compileAll('/app/js/*/**.js') | |
| 101 | + | startWatcher(watchFolders); | |
| 102 | + | } else { | |
| 103 | + | startWatcher(watchFolders); | |
| 104 | + | } | |
| 105 | + | } | |
| 106 | + | ||
| 107 | + | go() | |
package.json(file created)
| @@ -0,0 +1,26 @@ | |||
| 1 | + | { | |
| 2 | + | "name": "workflowmasters-builder", | |
| 3 | + | "type": "module", | |
| 4 | + | "devDependencies": { | |
| 5 | + | "beautify": "0.0.8", | |
| 6 | + | "chokidar": "3.6.0", | |
| 7 | + | "character-parser": "4.0.0", | |
| 8 | + | "clean-html": "2.0.1", | |
| 9 | + | "concat-files": "0.1.1", | |
| 10 | + | "fast-glob": "3.3.1", | |
| 11 | + | "fs-extra": "11.1.1", | |
| 12 | + | "glob": "10.3.10", | |
| 13 | + | "html-prettify": "1.0.7", | |
| 14 | + | "jade": "1.11.0", | |
| 15 | + | "klaw": "4.1.0", | |
| 16 | + | "pug": "3.0.2", | |
| 17 | + | "sass": "1.64.1", | |
| 18 | + | "through2": "4.0.2", | |
| 19 | + | "@css-inline/css-inline":"0.14.1", | |
| 20 | + | "watcher": "2.2.2" | |
| 21 | + | }, | |
| 22 | + | "scripts": { | |
| 23 | + | "watch": "./watcher.js", | |
| 24 | + | "test": "gulp test" | |
| 25 | + | } | |
| 26 | + | } | |
pug-watcher.js(file created)
| @@ -0,0 +1,153 @@ | |||
| 1 | + | import chokidar from 'chokidar'; | |
| 2 | + | import fs from 'fs' | |
| 3 | + | import fse from 'fs-extra'; | |
| 4 | + | import pug from 'pug'; | |
| 5 | + | import klaw from 'klaw'; | |
| 6 | + | import pretty from 'pretty'; | |
| 7 | + | ||
| 8 | + | // ==================================================================== | |
| 9 | + | // | | |
| 10 | + | // ==================================================================== | |
| 11 | + | function log(message) { | |
| 12 | + | const options = { | |
| 13 | + | encoding: "utf-8", | |
| 14 | + | flag: "a+", | |
| 15 | + | }; | |
| 16 | + | const datetime = new Date().toISOString() | |
| 17 | + | message = `[${datetime}] ${message}\n` | |
| 18 | + | fse.outputFileSync('/logs/builder/pug.log', message, options); | |
| 19 | + | console.log(message) | |
| 20 | + | } | |
| 21 | + | ||
| 22 | + | // ==================================================================== | |
| 23 | + | // | | |
| 24 | + | // ==================================================================== | |
| 25 | + | function printChange(path) { | |
| 26 | + | log(path) | |
| 27 | + | } | |
| 28 | + | ||
| 29 | + | // ==================================================================== | |
| 30 | + | // | | |
| 31 | + | // ==================================================================== | |
| 32 | + | function compile(path) { | |
| 33 | + | ||
| 34 | + | // Do not compile inside .delete folders | |
| 35 | + | if (path.includes(".delete")) { | |
| 36 | + | return | |
| 37 | + | } | |
| 38 | + | ||
| 39 | + | // Do not compile partials (begins with _) | |
| 40 | + | const filename = path.split("/").slice(-1).toString() | |
| 41 | + | if (filename.startsWith("_")) { | |
| 42 | + | compileAll() | |
| 43 | + | return | |
| 44 | + | } | |
| 45 | + | ||
| 46 | + | // Do not compile inside _partials folders | |
| 47 | + | if (path.includes("_partials") || path.includes("partials")) { | |
| 48 | + | log('Partial Edited. Recompile all!') | |
| 49 | + | compileAll() | |
| 50 | + | return | |
| 51 | + | } | |
| 52 | + | ||
| 53 | + | // Do not compile if the file does not end with .pug | |
| 54 | + | if (!path.endsWith(".pug")) { | |
| 55 | + | log('Partial Edited. Recompile all!') | |
| 56 | + | return | |
| 57 | + | } | |
| 58 | + | ||
| 59 | + | log(`Recompiling ${path}`) | |
| 60 | + | const result = pug.renderFile(path); | |
| 61 | + | ||
| 62 | + | var publicPath = path | |
| 63 | + | publicPath = publicPath.replace('/app/views','/www') | |
| 64 | + | publicPath = publicPath.replace('.pug','.html') | |
| 65 | + | ||
| 66 | + | fse.outputFile(publicPath, pretty(result), err => { | |
| 67 | + | if (err) { | |
| 68 | + | console.error(err); | |
| 69 | + | } | |
| 70 | + | // file written successfully | |
| 71 | + | }); | |
| 72 | + | ||
| 73 | + | } | |
| 74 | + | ||
| 75 | + | // ==================================================================== | |
| 76 | + | // | | |
| 77 | + | // ==================================================================== | |
| 78 | + | function compileParents(path) { | |
| 79 | + | ||
| 80 | + | // Do not compile inside .delete folders | |
| 81 | + | if (path.includes(".delete")) { | |
| 82 | + | return | |
| 83 | + | } | |
| 84 | + | ||
| 85 | + | // Do not compile partials (begins with _) | |
| 86 | + | const filename = path.split("/").slice(-1).toString() | |
| 87 | + | if (filename.startsWith("_")) { | |
| 88 | + | return | |
| 89 | + | } | |
| 90 | + | ||
| 91 | + | // Do not compile inside _partials folders | |
| 92 | + | if (path.includes("_partials") || path.includes("partials")) { | |
| 93 | + | return | |
| 94 | + | } | |
| 95 | + | ||
| 96 | + | // Do not compile if the file does not end with .pug | |
| 97 | + | if (!path.endsWith(".pug")) { | |
| 98 | + | return | |
| 99 | + | } | |
| 100 | + | ||
| 101 | + | log(`Recompiling ${path}`) | |
| 102 | + | const result = pug.renderFile(path); | |
| 103 | + | ||
| 104 | + | var publicPath = path | |
| 105 | + | publicPath = publicPath.replace('/app/views','/www') | |
| 106 | + | publicPath = publicPath.replace('.pug','.html') | |
| 107 | + | ||
| 108 | + | fse.outputFile(publicPath, pretty(result), err => { | |
| 109 | + | if (err) { | |
| 110 | + | console.error(err); | |
| 111 | + | } | |
| 112 | + | // file written successfully | |
| 113 | + | }); | |
| 114 | + | ||
| 115 | + | } | |
| 116 | + | ||
| 117 | + | ||
| 118 | + | // ==================================================================== | |
| 119 | + | // | | |
| 120 | + | // ==================================================================== | |
| 121 | + | function compileAll() { | |
| 122 | + | klaw('/app/views') | |
| 123 | + | .on('data', item => compileParents(item.path)) | |
| 124 | + | } | |
| 125 | + | ||
| 126 | + | // ==================================================================== | |
| 127 | + | // | | |
| 128 | + | // ==================================================================== | |
| 129 | + | const watchFolders = [ | |
| 130 | + | '/app/views/**/*.pug', | |
| 131 | + | ] | |
| 132 | + | ||
| 133 | + | var ignoreInitial = true | |
| 134 | + | if (process.argv[2] == 'recompile') { | |
| 135 | + | ignoreInitial = false | |
| 136 | + | } | |
| 137 | + | ||
| 138 | + | // https://github.com/paulmillr/chokidar | |
| 139 | + | const options = { | |
| 140 | + | awaitFinish: true, | |
| 141 | + | ignoreInitial: true, | |
| 142 | + | usePolling: true, | |
| 143 | + | interval: 100 | |
| 144 | + | } | |
| 145 | + | ||
| 146 | + | const watcher = chokidar.watch(watchFolders, options).on('all', (event, path) => { | |
| 147 | + | // log(event, path); | |
| 148 | + | }); | |
| 149 | + | ||
| 150 | + | watcher | |
| 151 | + | .on('add', path => compile(path)) | |
| 152 | + | .on('change', path => compile(path)) | |
| 153 | + | .on('unlink', path => compile(path)); | |
sass-watcher.js(file created)
| @@ -0,0 +1,179 @@ | |||
| 1 | + | import chokidar from 'chokidar'; | |
| 2 | + | import fs from 'fs' | |
| 3 | + | import fse from 'fs-extra'; | |
| 4 | + | import * as sass from 'sass'; | |
| 5 | + | import fg from 'fast-glob'; | |
| 6 | + | ||
| 7 | + | // ==================================================================== | |
| 8 | + | // | | |
| 9 | + | // ==================================================================== | |
| 10 | + | function log(message) { | |
| 11 | + | const options = { | |
| 12 | + | encoding: "utf-8", | |
| 13 | + | flag: "a+", | |
| 14 | + | }; | |
| 15 | + | const datetime = new Date().toISOString() | |
| 16 | + | message = `[${datetime}] ${message}\n` | |
| 17 | + | fse.outputFileSync('/logs/builder/sass.log', message, options); | |
| 18 | + | console.log(message) | |
| 19 | + | } | |
| 20 | + | ||
| 21 | + | // ==================================================================== | |
| 22 | + | // | | |
| 23 | + | // ==================================================================== | |
| 24 | + | function compile(path) { | |
| 25 | + | log('-----------------------'); | |
| 26 | + | ||
| 27 | + | // Do not compile inside .delete folders | |
| 28 | + | if (path.includes(".delete")) { | |
| 29 | + | return | |
| 30 | + | } | |
| 31 | + | ||
| 32 | + | const filename = path.split("/").slice(-1).toString() | |
| 33 | + | if (filename.startsWith("_")) { | |
| 34 | + | compileAll() | |
| 35 | + | return | |
| 36 | + | } | |
| 37 | + | ||
| 38 | + | const options = { | |
| 39 | + | logger: { | |
| 40 | + | warn(message, options) { | |
| 41 | + | if (options.span) { | |
| 42 | + | log('====================================================================================================================') | |
| 43 | + | log(`Warning when compiling: ${options.span.url.pathname}:`) | |
| 44 | + | log(` ${message}\n`) | |
| 45 | + | } | |
| 46 | + | }, | |
| 47 | + | debug(message, options) { | |
| 48 | + | if (options.span) { | |
| 49 | + | log('====================================================================================================================') | |
| 50 | + | log(`Debug message compiling: ${options.span.url.pathname}:`) | |
| 51 | + | log(` ${message}\n`) | |
| 52 | + | } | |
| 53 | + | } | |
| 54 | + | } | |
| 55 | + | } | |
| 56 | + | ||
| 57 | + | try { | |
| 58 | + | const result = sass.compile(path, options); | |
| 59 | + | var publicPath = path | |
| 60 | + | publicPath = publicPath.replace('/app/scss','/www/css') | |
| 61 | + | publicPath = publicPath.replace('.scss','.css') | |
| 62 | + | fse.outputFileSync(publicPath, result.css); | |
| 63 | + | } catch (err) { | |
| 64 | + | log(`[ERROR] SASS Compile: ${path}`) | |
| 65 | + | log(err) | |
| 66 | + | } | |
| 67 | + | ||
| 68 | + | } | |
| 69 | + | ||
| 70 | + | // ==================================================================== | |
| 71 | + | // | | |
| 72 | + | // ==================================================================== | |
| 73 | + | function compileParents(path) { | |
| 74 | + | ||
| 75 | + | // Do not compile inside .delete folders | |
| 76 | + | if (path.includes(".delete")) { | |
| 77 | + | return | |
| 78 | + | } | |
| 79 | + | ||
| 80 | + | // Do not compile partials (begins with _) | |
| 81 | + | const filename = path.split("/").slice(-1).toString() | |
| 82 | + | if (filename.startsWith("_")) { | |
| 83 | + | return | |
| 84 | + | } | |
| 85 | + | ||
| 86 | + | // Do not compile if the file does not end with .scss | |
| 87 | + | if (!path.endsWith(".scss")) { | |
| 88 | + | return | |
| 89 | + | } | |
| 90 | + | ||
| 91 | + | const options = { | |
| 92 | + | logger: { | |
| 93 | + | warn(message, options) { | |
| 94 | + | if (options.span) { | |
| 95 | + | log('=============================') | |
| 96 | + | log(`Warning when compiling: ${options.span.url.pathname}:`) | |
| 97 | + | log(` ${message}\n`) | |
| 98 | + | } | |
| 99 | + | }, | |
| 100 | + | debug(message, options) { | |
| 101 | + | if (options.span) { | |
| 102 | + | log('=============================') | |
| 103 | + | log(`Debug message compiling: ${options.span.url.pathname}:`) | |
| 104 | + | log(` ${message}\n`) | |
| 105 | + | } | |
| 106 | + | } | |
| 107 | + | } | |
| 108 | + | } | |
| 109 | + | ||
| 110 | + | try { | |
| 111 | + | const result = sass.compile(path, options); | |
| 112 | + | var publicPath = path | |
| 113 | + | publicPath = publicPath.replace('/app/scss','/www/css') | |
| 114 | + | publicPath = publicPath.replace('.scss','.css') | |
| 115 | + | fse.outputFileSync(publicPath, result.css); | |
| 116 | + | } catch (err) { | |
| 117 | + | log(`[ERROR] SASS Compile: ${path}`) | |
| 118 | + | log(err) | |
| 119 | + | } | |
| 120 | + | ||
| 121 | + | ||
| 122 | + | } | |
| 123 | + | ||
| 124 | + | // ==================================================================== | |
| 125 | + | // | | |
| 126 | + | // ==================================================================== | |
| 127 | + | function compileAll(glob) { | |
| 128 | + | return new Promise((resolve, reject) => { | |
| 129 | + | const sassFiles = fg.globSync(glob) | |
| 130 | + | for (const file of sassFiles) { | |
| 131 | + | compileParents(file); | |
| 132 | + | } | |
| 133 | + | log(`Done`) | |
| 134 | + | }); | |
| 135 | + | } | |
| 136 | + | ||
| 137 | + | // ==================================================================== | |
| 138 | + | // | | |
| 139 | + | // ==================================================================== | |
| 140 | + | function startWatcher(watchFolders) { | |
| 141 | + | ||
| 142 | + | // https://github.com/paulmillr/chokidar | |
| 143 | + | const options = { | |
| 144 | + | awaitFinish: true, | |
| 145 | + | ignoreInitial: true, | |
| 146 | + | usePolling: true, | |
| 147 | + | interval: 100 | |
| 148 | + | } | |
| 149 | + | ||
| 150 | + | const watcher = chokidar.watch(watchFolders, options).on('all', (event, path) => { | |
| 151 | + | log(`${path} was ${event}`); | |
| 152 | + | }); | |
| 153 | + | ||
| 154 | + | watcher | |
| 155 | + | .on('add', path => compile(path)) | |
| 156 | + | .on('change', path => compile(path)) | |
| 157 | + | .on('unlink', path => compile(path)); | |
| 158 | + | ||
| 159 | + | watcher | |
| 160 | + | .on('ready', () => log('********** Watch is running **********')); | |
| 161 | + | ||
| 162 | + | } | |
| 163 | + | ||
| 164 | + | // ==================================================================== | |
| 165 | + | // | MAIN | |
| 166 | + | // ==================================================================== | |
| 167 | + | async function go() { | |
| 168 | + | const watchFolders = [ | |
| 169 | + | '/app/scss/**/*.scss' | |
| 170 | + | ] | |
| 171 | + | if (process.argv[2] == 'recompile') { | |
| 172 | + | await compileAll('/app/scss/**/*.scss') | |
| 173 | + | startWatcher(watchFolders); | |
| 174 | + | } else { | |
| 175 | + | startWatcher(watchFolders); | |
| 176 | + | } | |
| 177 | + | } | |
| 178 | + | ||
| 179 | + | go() | |