feat: Improved spacing of additional code buttons on fee sheet (#6790)
[openemr.git] / gulpfile.js
blob4ae0e08ab8657ce25a831b74044a4ba19918a9c0
1 "use strict";
3 // modules
4 const csso = require('gulp-csso');
5 const del = require('del');
6 const fs = require('fs');
7 const glob = require('glob');
8 const gap = require('gulp-append-prepend');
9 const replace = require('replace-in-file');
10 const gulp = require('gulp');
11 const argv = require('minimist')(process.argv.slice(2));
12 const gulpif = require('gulp-if');
13 const prefix = require('autoprefixer');
14 const postcss = require('gulp-postcss');
15 const rename = require('gulp-rename');
16 const sass = require('gulp-dart-sass');
17 const sourcemaps = require('gulp-sourcemaps');
18 const gulp_watch = require('gulp-watch');
19 const injector = require('gulp-inject-string');
20 const colors = require('colors');
22 // package.json
23 const packages = require('./package.json');
25 const logprefix = "[OpenEMR]".bold.cyan + " ";
27 // configuration
28 let config = {
29     all: [], // must always be empty
31     // Command Line Arguments
32     dev: argv['dev'],
33     build: argv['b'],
34     install: argv['i'],
36     // Source file locations
37     src: {
38         styles: {
39             style_tabs: 'interface/themes/tabs_style_*.scss',
40             style_uni: 'interface/themes/oe-styles/style_*.scss',
41             style_color: 'interface/themes/colors/*.scss',
42             directional: 'interface/themes/directional.scss',
43             misc: 'interface/themes/misc/**/*.scss'
44         }
45     },
46     dist: {
47         assets: 'public/assets/'
48     },
49     dest: {
50         themes: 'public/themes',
51         misc_themes: 'public/themes/misc'
52     }
55 if (config.install) {
56     console.log("\nCopying OpenEMR dependencies using Gulp".bold.yellow + "\n");
57 } else if (config.build) {
58     console.log("\nBuilding OpenEMR themes using Gulp".bold.yellow + "\n");
59 } else if (config.dev) {
60     console.log("\nBuilding OpenEMR themes using Dev Flag for Gulp".bold.yellow + "\n");
61 } else if (config.all) {
62     console.log("\nBuilding OpenEMR themes using All Flag for Gulp".bold.yellow + "\n");
63 } else {
64     // This is used for gulp watch & other misc things
65     console.log("\nRunning Gulp for OpenEMR".bold.yellow + "\n");
68 function log_error(isSuccess, err) {
69     isSuccess = false;
70     console.error(logprefix + "An error occured! Check the log for details.");
71     // Log error to console
72     console.error(err.toString().red);
73     // Kills gulp on error since if we keep running it will
74     // still fail
75     process.exit(1);
78 // Clean up lingering static themes
79 function clean(done) {
80     del.sync([config.dest.themes + "/*"]);
81     done();
84 // Parses command line arguments
85 function ingest(done) {
86     if (config.dev && typeof config.dev !== "boolean") {
87         config.dev = true;
88     }
89     done();
92 // definition of header for all compiled css
93 const autoGeneratedHeader = `
94 /*! This style sheet was autogenerated using gulp + scss
95  *  For usage instructions, see: https://github.com/openemr/openemr/blob/master/interface/README.md
96  */
99 // standard themes css compilation
100 function styles_style_uni() {
101     let isSuccess = true;
102     return gulp.src(config.src.styles.style_uni)
103         .pipe(gap.prependText('$compact-theme: false;\n'))
104         .pipe(injector.replace('// bs4import', '@import "../../../public/assets/bootstrap/scss/bootstrap";'))
105         .pipe(sourcemaps.init())
106         .pipe(sass().on('error', (err) => {
107             log_error(isSuccess, err);
108         }))
109         .pipe(postcss([prefix()]))
110         .pipe(gap.prependText(autoGeneratedHeader))
111         .pipe(gulpif(!config.dev, csso()))
112         .pipe(gulpif(!config.dev, sourcemaps.write()))
113         .on('error', (err) => {
114             log_error(isSuccess, err);
115         })
116         .pipe(gulp.dest(config.dest.themes))
117         .on('end', () => {
118             if (isSuccess) {
119                 console.log(logprefix + "Finished compiling OpenEMR base themes");
120             }
121         });
124 // standard themes compact css compilation
125 function styles_style_uni_compact() {
126     let isSuccess = true;
127     return gulp.src(config.src.styles.style_uni)
128         .pipe(gap.prependText('@import "../compact-theme-defaults";\n'))
129         .pipe(injector.replace('// bs4import', '@import "../oemr_compact_imports";'))
130         .pipe(sourcemaps.init())
131         .pipe(sass().on('error', (err) => {
132             log_error(isSuccess, err);
133         }))
134         .pipe(postcss([prefix()]))
135         .pipe(gap.prependText(autoGeneratedHeader))
136         .pipe(gulpif(!config.dev, csso()))
137         .pipe(gulpif(!config.dev, sourcemaps.write()))
138         .pipe(rename({
139             prefix: "compact_"
140         }))
141         .on('error', (err) => {
142             log_error(isSuccess, err);
143         })
144         .pipe(gulp.dest(config.dest.themes))
145         .on('end', () => {
146             if (isSuccess) {
147                 console.log(logprefix + "Finished compiling OpenEMR compact base themes");
148             }
149         });
152 // color themes css compilation
153 function styles_style_color() {
154     let isSuccess = true;
155     return gulp.src(config.src.styles.style_color)
156         .pipe(gap.prependText('$compact-theme: false;\n'))
157         .pipe(injector.replace('// bs4import', '@import "../../../public/assets/bootstrap/scss/bootstrap";'))
158         .pipe(sourcemaps.init())
159         .pipe(sass().on('error', (err) => {
160             log_error(isSuccess, err);
161         }))
162         .pipe(postcss([prefix()]))
163         .pipe(gap.prependText(autoGeneratedHeader))
164         .pipe(gulpif(!config.dev, csso()))
165         .pipe(gulpif(!config.dev, sourcemaps.write()))
166         .on('error', (err) => {
167             log_error(isSuccess, err);
168         })
169         .pipe(gulp.dest(config.dest.themes))
170         .on('end', () => {
171             if (isSuccess) {
172                 console.log(logprefix + "Finished compiling OpenEMR color themes");
173             }
174         });
177 // color themes compact css compilation
178 function styles_style_color_compact() {
179     let isSuccess = true;
180     return gulp.src(config.src.styles.style_color)
181         .pipe(gap.prependText('@import "../compact-theme-defaults";\n'))
182         .pipe(injector.replace('// bs4import', '@import "../oemr_compact_imports";'))
183         .pipe(sourcemaps.init())
184         .pipe(sass().on('error', (err) => {
185             log_error(isSuccess, err);
186         }))
187         .pipe(postcss([prefix()]))
188         .pipe(gap.prependText(autoGeneratedHeader))
189         .pipe(gulpif(!config.dev, csso()))
190         .pipe(gulpif(!config.dev, sourcemaps.write()))
191         .pipe(rename({
192             prefix: "compact_"
193         }))
194         .on('error', (err) => {
195             log_error(isSuccess, err);
196         })
197         .pipe(gulp.dest(config.dest.themes))
198         .on('end', () => {
199             if (isSuccess) {
200                 console.log(logprefix + "Finished compiling OpenEMR compact color themes");
201             }
202         });
205 // Tabs CSS compilation
206 function styles_style_tabs() {
207     let isSuccess = true;
208     return gulp.src(config.src.styles.style_tabs)
209         .pipe(sourcemaps.init())
210         .pipe(sass().on('error', (err) => {
211             log_error(isSuccess, err);
212         }))
213         .pipe(postcss([prefix()]))
214         .pipe(gap.prependText(autoGeneratedHeader))
215         .pipe(gulpif(!config.dev, csso()))
216         .pipe(gulpif(!config.dev, sourcemaps.write()))
217         .on('error', (err) => {
218             log_error(isSuccess, err);
219         })
220         .pipe(gulp.dest(config.dest.themes))
221         .on('end', () => {
222             if (isSuccess) {
223                 console.log(logprefix + "Finished compiling OpenEMR tab navigation styles");
224             }
225         });
228 // For anything else that needs to be moved, use misc themes
229 function styles_style_misc() {
230     let isSuccess = true;
231     return gulp.src(config.src.styles.misc)
232         .pipe(sourcemaps.init())
233         .pipe(sass().on('error', (err) => {
234             log_error(isSuccess, err);
235         }))
236         .pipe(postcss([prefix()]))
237         .pipe(gap.prependText(autoGeneratedHeader))
238         .pipe(gulpif(!config.dev, csso()))
239         .pipe(gulpif(!config.dev, sourcemaps.write()))
240         .on('error', (err) => {
241             log_error(isSuccess, err);
242         })
243         .pipe(gulp.dest(config.dest.misc_themes))
244         .on('end', () => {
245             if (isSuccess) {
246                 console.log(logprefix + "Finished compiling miscellaneous styles");
247             }
248         });
251 // rtl standard themes css compilation
252 function rtl_style_uni() {
253     let isSuccess = true;
254     return gulp.src(config.src.styles.style_uni)
255         .pipe(gap.prependText('$compact-theme: false;\n$dir: rtl;\n@import "../rtl";\n')) // watch out for this relative path!
256         .pipe(gap.appendText('@include if-rtl { @include rtl_style; #bigCal { border-right: 1px solid $black !important; } }\n'))
257         .pipe(injector.replace('// bs4import', '@import "../oemr-rtl";'))
258         .pipe(sourcemaps.init())
259         .pipe(sass().on('error', (err) => {
260             log_error(isSuccess, err);
261         }))
262         .pipe(postcss([prefix()]))
263         .pipe(gap.prependText(autoGeneratedHeader))
264         .pipe(gulpif(!config.dev, csso()))
265         .pipe(gulpif(!config.dev, sourcemaps.write()))
266         .pipe(rename({
267             prefix: "rtl_"
268         }))
269         .on('error', (err) => {
270             log_error(isSuccess, err);
271         })
272         .pipe(gulp.dest(config.dest.themes))
273         .on('end', () => {
274             if (isSuccess) {
275                 console.log(logprefix + "Finished compiling RTL base themes");
276             }
277         });
280 // rtl standard themes compact css compilation
281 function rtl_style_uni_compact() {
282     let isSuccess = true;
283     return gulp.src(config.src.styles.style_uni)
284         .pipe(gap.prependText('@import "../compact-theme-defaults";\n'))
285         .pipe(gap.prependText('$dir: rtl;\n@import "../rtl";\n')) // watch out for this relative path!
286         .pipe(gap.appendText('@include if-rtl { @include rtl_style; #bigCal { border-right: 1px solid $black !important; } }\n'))
287         .pipe(injector.replace('// bs4import', '@import "../oemr_rtl_compact_imports";'))
288         .pipe(sourcemaps.init())
289         .pipe(sass().on('error', (err) => {
290             log_error(isSuccess, err);
291         }))
292         .pipe(postcss([prefix()]))
293         .pipe(gap.prependText(autoGeneratedHeader))
294         .pipe(gulpif(!config.dev, csso()))
295         .pipe(gulpif(!config.dev, sourcemaps.write()))
296         .pipe(rename({
297             prefix: "rtl_compact_"
298         }))
299         .on('error', (err) => {
300             log_error(isSuccess, err);
301         })
302         .pipe(gulp.dest(config.dest.themes))
303         .on('end', () => {
304             if (isSuccess) {
305                 console.log(logprefix + "Finished compiling RTL base compact themes");
306             }
307         });
310 // rtl color themes css compilation
311 function rtl_style_color() {
312     let isSuccess = true;
313     return gulp.src(config.src.styles.style_color)
314         .pipe(gap.prependText('$compact-theme: false;\n$dir: rtl;\n@import "../rtl";\n')) // watch out for this relative path!
315         .pipe(gap.appendText('@include if-rtl { @include rtl_style; #bigCal { border-right: 1px solid $black !important; } }\n'))
316         .pipe(injector.replace('// bs4import', '@import "../oemr-rtl";'))
317         .pipe(sourcemaps.init())
318         .pipe(sass().on('error', (err) => {
319             log_error(isSuccess, err);
320         }))
321         .pipe(postcss([prefix()]))
322         .pipe(gap.prependText(autoGeneratedHeader))
323         .pipe(gulpif(!config.dev, csso()))
324         .pipe(gulpif(!config.dev, sourcemaps.write()))
325         .pipe(rename({
326             prefix: "rtl_"
327         }))
328         .on('error', (err) => {
329             log_error(isSuccess, err);
330         })
331         .pipe(gulp.dest(config.dest.themes))
332         .on('end', () => {
333             if (isSuccess) {
334                 console.log(logprefix + "Compiled OpenEMR RTL color themes");
335             }
336         });
339 // rtl color themes compact css compilation
340 function rtl_style_color_compact() {
341     let isSuccess = true;
342     return gulp.src(config.src.styles.style_color)
343         .pipe(gap.prependText('@import "../compact-theme-defaults";\n'))
344         .pipe(gap.prependText('$dir: rtl;\n@import "../rtl";\n')) // watch out for this relative path!
345         .pipe(gap.appendText('@include if-rtl { @include rtl_style; #bigCal { border-right: 1px solid $black !important; } }\n'))
346         .pipe(injector.replace('// bs4import', '@import "../oemr_rtl_compact_imports";'))
347         .pipe(sourcemaps.init())
348         .pipe(sass().on('error', (err) => {
349             log_error(isSuccess, err);
350         }))
351         .pipe(postcss([prefix()]))
352         .pipe(gap.prependText(autoGeneratedHeader))
353         .pipe(gulpif(!config.dev, csso()))
354         .pipe(gulpif(!config.dev, sourcemaps.write()))
355         .pipe(rename({
356             prefix: "rtl_compact_"
357         }))
358         .on('error', (err) => {
359             log_error(isSuccess, err);
360         })
361         .pipe(gulp.dest(config.dest.themes))
362         .on('end', () => {
363             if (isSuccess) {
364                 console.log(logprefix + "Finished compiling RTL compact color themes");
365             }
366         });
369 // rtl standard themes css compilation
370 function rtl_style_tabs() {
371     let isSuccess = true;
372     return gulp.src(config.src.styles.style_tabs)
373         .pipe(gap.prependText('$dir: rtl;\n@import "rtl";\n')) // watch out for this relative path!
374         .pipe(sourcemaps.init())
375         .pipe(sass().on('error', (err) => {
376             log_error(isSuccess, err);
377         }))
378         .pipe(postcss([prefix()]))
379         .pipe(gap.prependText(autoGeneratedHeader))
380         .pipe(gulpif(!config.dev, csso()))
381         .pipe(gulpif(!config.dev, sourcemaps.write()))
382         .pipe(rename({
383             prefix: "rtl_"
384         }))
385         .on('error', (err) => {
386             log_error(isSuccess, err);
387         })
388         .pipe(gulp.dest(config.dest.themes))
389         .on('end', () => {
390             if (isSuccess) {
391                 console.log(logprefix + "Finished compiling RTL tabs styles");
392             }
393         });
396 // For anything else that needs to be moved, use misc themes
397 function rtl_style_misc() {
398     let isSuccess = true;
399     return gulp.src(config.src.styles.misc)
400         .pipe(gap.prependText('$dir: rtl;\n')) // Simply a flag here due to a hierarchy possibly being created
401         .pipe(sourcemaps.init())
402         .pipe(sass().on('error', (err) => {
403             log_error(isSuccess, err);
404         }))
405         .pipe(postcss([prefix()]))
406         .pipe(gap.prependText(autoGeneratedHeader))
407         .pipe(gulpif(!config.dev, csso()))
408         .pipe(gulpif(!config.dev, sourcemaps.write()))
409         .pipe(rename({
410             prefix: "rtl_"
411         }))
412         .on('error', (err) => {
413             log_error(isSuccess, err);
414         })
415         .pipe(gulp.dest(config.dest.misc_themes))
416         .on('end', () => {
417             if (isSuccess) {
418                 console.log(logprefix + "Compiled rest of RTL SCSS");
419             }
420         });
423 // compile themes
424 const styles = gulp.parallel(styles_style_color, styles_style_color_compact, styles_style_uni, styles_style_uni_compact, styles_style_tabs, styles_style_misc, rtl_style_color, rtl_style_color_compact, rtl_style_uni, rtl_style_uni_compact, rtl_style_tabs, rtl_style_misc);
426 // Copies (and distills, if possible) assets from node_modules to public/assets
427 function install(done) {
428     console.log(logprefix + "Running OpenEMR gulp install task...");
429     // combine dependencies and napa sources into one object
430     const dependencies = packages.dependencies;
431     for (let key in packages.napa) {
432         if (Object.prototype.hasOwnProperty.call(packages.napa, key)) {
433             dependencies[key] = packages.napa[key];
434         }
435     }
437     for (let key in dependencies) {
438         // check if the property/key is defined in the object itself, not in parent
439         if (Object.prototype.hasOwnProperty.call(dependencies, key)) {
440             if (key == "dwv") {
441                 // dwv is special and need to copy dist, decoders and locales
442                 gulp.src("node_modules/" + key + "/dist/**/*").pipe(
443                     gulp.dest(config.dist.assets + key + "/dist")
444                 );
445                 gulp.src("node_modules/" + key + "/decoders/**/*").pipe(
446                     gulp.dest(config.dist.assets + key + "/decoders")
447                 );
448                 gulp.src("node_modules/" + key + "/locales/**/*").pipe(
449                     gulp.dest(config.dist.assets + key + "/locales")
450                 );
451             } else if (key == "bootstrap" || key == "bootstrap-rtl") {
452                 // bootstrap and bootstrap-v4-rtl are special and need to copy dist and scss
453                 gulp.src("node_modules/" + key + "/dist/**/*").pipe(
454                     gulp.dest(config.dist.assets + key + "/dist")
455                 );
456                 gulp.src("node_modules/" + key + "/scss/**/*").pipe(
457                     gulp.dest(config.dist.assets + key + "/scss")
458                 );
459             } else if (key == "@fortawesome/fontawesome-free") {
460                 // @fortawesome/fontawesome-free is special and need to copy css, scss, and webfonts
461                 gulp.src("node_modules/" + key + "/css/**/*").pipe(
462                     gulp.dest(config.dist.assets + key + "/css")
463                 );
464                 gulp.src("node_modules/" + key + "/scss/**/*").pipe(
465                     gulp.dest(config.dist.assets + key + "/scss")
466                 );
467                 gulp.src("node_modules/" + key + "/webfonts/**/*").pipe(
468                     gulp.dest(config.dist.assets + key + "/webfonts")
469                 );
470             } else if (key == "moment") {
471                 gulp.src("node_modules/" + key + "/min/**/*").pipe(
472                     gulp.dest(config.dist.assets + key + "/min")
473                 );
474                 gulp.src("node_modules/" + key + "/moment.js").pipe(
475                     gulp.dest(config.dist.assets + key)
476                 );
477             } else if (fs.existsSync("node_modules/" + key + "/dist")) {
478                 // only copy dist directory, if it exists
479                 gulp.src("node_modules/" + key + "/dist/**/*").pipe(
480                     gulp.dest(config.dist.assets + key + "/dist")
481                 );
482             } else {
483                 // copy everything
484                 gulp.src("node_modules/" + key + "/**/*").pipe(
485                     gulp.dest(config.dist.assets + key)
486                 );
487             }
488         }
489     }
491     console.log(logprefix + "Finished running OpenEMR gulp install task");
492     done();
495 function watch() {
496     let isSuccess = true;
497     console.log(logprefix + "Running gulp watch task...");
498     // watch all changes and re-run styles
499     gulp.watch('./interface/**/*.scss', {
500             interval: 1000,
501             mode: 'poll'
502         }, styles)
503         .on('error', (err) => {
504             log_error(isSuccess, err);
505         });
507     // watch php separately since autoprefix is not needed
508     gulp_watch('./interface/themes/*.php', {
509             ignoreInitial: false
510         })
511         .pipe(gulp.dest(config.dest.themes))
512         .on('error', (err) => {
513             log_error(isSuccess, err);
514         });
516     // watch all changes to css files in themes and
517     // autoprefix them before copying to public
518     return gulp_watch('./interface/themes/*.css', {
519             ignoreInitial: false
520         })
521         .pipe(postcss([prefix()]))
522         .on('error', (err) => {
523             log_error(isSuccess, err);
524         })
525         .pipe(gulp.dest(config.dest.themes))
526         .on('end', () => {
527             if (isSuccess) {
528                 console.log(logprefix + "Finished running gulp watch task");
529             }
530         });
533 function sync() {
534     let isSuccess = true;
535     console.log(logprefix + "Running gulp sync task...");
536     // copy all leftover root-level components to the theme directory
537     // hoping this is only temporary
538     // Copy php file separately since we don't need to autoprefix them
539     gulp.src(['interface/themes/*.php'])
540         .pipe(gulp.dest(config.dest.themes))
541         .on('error', (err) => {
542             log_error(isSuccess, err);
543         });
545     // Copy CSS files and autoprefix them
546     return gulp.src(['interface/themes/*.css'])
547         .pipe(postcss([prefix()]))
548         .on('error', (err) => {
549             log_error(isSuccess, err);
550         })
551         .pipe(gulp.dest(config.dest.themes))
552         .on('end', () => {
553             if (isSuccess) {
554                 console.log(logprefix + "Finished running gulp sync task");
555             }
556         });
559 // Export watch task
560 exports.watch = watch;
562 // Export pertinent default task
563 // - Note that the default task runs if no other task is chosen,
564 //    which is generally how this script is always used (except in
565 //    rare case where the user is running the watch task).
566 if (config.install) {
567     exports.default = gulp.series(install);
568 } else {
569     exports.default = gulp.series(clean, ingest, styles, sync);