fix: ERA posting based on code mod combo billed that prevents insert into billing...
[openemr.git] / gulpfile.js
blobb5946deb04654a0abe4836e4735dc3afbdf3011f
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             style_other: 'interface/themes/style*.scss'
45         }
46     },
47     dist: {
48         assets: 'public/assets/'
49     },
50     dest: {
51         themes: 'public/themes',
52         misc_themes: 'public/themes/misc'
53     }
56 if (config.install) {
57     console.log("\nCopying OpenEMR dependencies using Gulp".bold.yellow + "\n");
58 } else if (config.build) {
59     console.log("\nBuilding OpenEMR themes using Gulp".bold.yellow + "\n");
60 } else if (config.dev) {
61     console.log("\nBuilding OpenEMR themes using Dev Flag for Gulp".bold.yellow + "\n");
62 } else if (config.all) {
63     console.log("\nBuilding OpenEMR themes using All Flag for Gulp".bold.yellow + "\n");
64 } else {
65     // This is used for gulp watch & other misc things
66     console.log("\nRunning Gulp for OpenEMR".bold.yellow + "\n");
69 function log_error(isSuccess, err) {
70     isSuccess = false;
71     console.error(logprefix + "An error occured! Check the log for details.");
72     // Log error to console
73     console.error(err.toString().red);
74     // Kills gulp on error since if we keep running it will
75     // still fail
76     process.exit(1);
79 // Clean up lingering static themes
80 function clean(done) {
81     del.sync([config.dest.themes + "/*"]);
82     done();
85 // Parses command line arguments
86 function ingest(done) {
87     if (config.dev && typeof config.dev !== "boolean") {
88         config.dev = true;
89     }
90     done();
93 // definition of header for all compiled css
94 const autoGeneratedHeader = `
95 /*! This style sheet was autogenerated using gulp + scss
96  *  For usage instructions, see: https://github.com/openemr/openemr/blob/master/interface/README.md
97  */
100 function styles_style_other() {
101     let isSuccess = true;
102     return gulp.src(config.src.styles.style_other)
103     .pipe(sourcemaps.init())
104     .pipe(sass().on('error', (err) => {
105         log_error(isSuccess, err);
106     }))
107     .pipe(postcss([prefix()]))
108     .pipe(gap.prependText(autoGeneratedHeader))
109     .pipe(gulpif(!config.dev, csso()))
110     .pipe(gulpif(!config.dev, sourcemaps.write()))
111     .on('error', (err) => {
112         log_error(isSuccess, err);
113     })
114     .pipe(gulp.dest(config.dest.themes))
115     .on('end', () => {
116         if (isSuccess) {
117             console.log(logprefix + "Finished compiling OpenEMR root other styles");
118         }
119     });
122 // standard themes css compilation
123 function styles_style_uni() {
124     let isSuccess = true;
125     return gulp.src(config.src.styles.style_uni)
126         .pipe(gap.prependText('$compact-theme: false;\n'))
127         .pipe(injector.replace('// bs4import', '@import "../../../public/assets/bootstrap/scss/bootstrap";'))
128         .pipe(sourcemaps.init())
129         .pipe(sass().on('error', (err) => {
130             log_error(isSuccess, err);
131         }))
132         .pipe(postcss([prefix()]))
133         .pipe(gap.prependText(autoGeneratedHeader))
134         .pipe(gulpif(!config.dev, csso()))
135         .pipe(gulpif(!config.dev, sourcemaps.write()))
136         .on('error', (err) => {
137             log_error(isSuccess, err);
138         })
139         .pipe(gulp.dest(config.dest.themes))
140         .on('end', () => {
141             if (isSuccess) {
142                 console.log(logprefix + "Finished compiling OpenEMR base themes");
143             }
144         });
147 // standard themes compact css compilation
148 function styles_style_uni_compact() {
149     let isSuccess = true;
150     return gulp.src(config.src.styles.style_uni)
151         .pipe(gap.prependText('@import "../compact-theme-defaults";\n'))
152         .pipe(injector.replace('// bs4import', '@import "../oemr_compact_imports";'))
153         .pipe(sourcemaps.init())
154         .pipe(sass().on('error', (err) => {
155             log_error(isSuccess, err);
156         }))
157         .pipe(postcss([prefix()]))
158         .pipe(gap.prependText(autoGeneratedHeader))
159         .pipe(gulpif(!config.dev, csso()))
160         .pipe(gulpif(!config.dev, sourcemaps.write()))
161         .pipe(rename({
162             prefix: "compact_"
163         }))
164         .on('error', (err) => {
165             log_error(isSuccess, err);
166         })
167         .pipe(gulp.dest(config.dest.themes))
168         .on('end', () => {
169             if (isSuccess) {
170                 console.log(logprefix + "Finished compiling OpenEMR compact base themes");
171             }
172         });
175 // color themes css compilation
176 function styles_style_color() {
177     let isSuccess = true;
178     return gulp.src(config.src.styles.style_color)
179         .pipe(gap.prependText('$compact-theme: false;\n'))
180         .pipe(injector.replace('// bs4import', '@import "../../../public/assets/bootstrap/scss/bootstrap";'))
181         .pipe(sourcemaps.init())
182         .pipe(sass().on('error', (err) => {
183             log_error(isSuccess, err);
184         }))
185         .pipe(postcss([prefix()]))
186         .pipe(gap.prependText(autoGeneratedHeader))
187         .pipe(gulpif(!config.dev, csso()))
188         .pipe(gulpif(!config.dev, sourcemaps.write()))
189         .on('error', (err) => {
190             log_error(isSuccess, err);
191         })
192         .pipe(gulp.dest(config.dest.themes))
193         .on('end', () => {
194             if (isSuccess) {
195                 console.log(logprefix + "Finished compiling OpenEMR color themes");
196             }
197         });
200 // color themes compact css compilation
201 function styles_style_color_compact() {
202     let isSuccess = true;
203     return gulp.src(config.src.styles.style_color)
204         .pipe(gap.prependText('@import "../compact-theme-defaults";\n'))
205         .pipe(injector.replace('// bs4import', '@import "../oemr_compact_imports";'))
206         .pipe(sourcemaps.init())
207         .pipe(sass().on('error', (err) => {
208             log_error(isSuccess, err);
209         }))
210         .pipe(postcss([prefix()]))
211         .pipe(gap.prependText(autoGeneratedHeader))
212         .pipe(gulpif(!config.dev, csso()))
213         .pipe(gulpif(!config.dev, sourcemaps.write()))
214         .pipe(rename({
215             prefix: "compact_"
216         }))
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 compact color themes");
224             }
225         });
228 // Tabs CSS compilation
229 function styles_style_tabs() {
230     let isSuccess = true;
231     return gulp.src(config.src.styles.style_tabs)
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.themes))
244         .on('end', () => {
245             if (isSuccess) {
246                 console.log(logprefix + "Finished compiling OpenEMR tab navigation styles");
247             }
248         });
251 // For anything else that needs to be moved, use misc themes
252 function styles_style_misc() {
253     let isSuccess = true;
254     return gulp.src(config.src.styles.misc)
255         .pipe(sourcemaps.init())
256         .pipe(sass().on('error', (err) => {
257             log_error(isSuccess, err);
258         }))
259         .pipe(postcss([prefix()]))
260         .pipe(gap.prependText(autoGeneratedHeader))
261         .pipe(gulpif(!config.dev, csso()))
262         .pipe(gulpif(!config.dev, sourcemaps.write()))
263         .on('error', (err) => {
264             log_error(isSuccess, err);
265         })
266         .pipe(gulp.dest(config.dest.misc_themes))
267         .on('end', () => {
268             if (isSuccess) {
269                 console.log(logprefix + "Finished compiling miscellaneous styles");
270             }
271         });
274 // rtl standard themes css compilation
275 function rtl_style_uni() {
276     let isSuccess = true;
277     return gulp.src(config.src.styles.style_uni)
278         .pipe(gap.prependText('$compact-theme: false;\n$dir: rtl;\n@import "../rtl";\n')) // watch out for this relative path!
279         .pipe(gap.appendText('@include if-rtl { @include rtl_style; #bigCal { border-right: 1px solid $black !important; } }\n'))
280         .pipe(injector.replace('// bs4import', '@import "../oemr-rtl";'))
281         .pipe(sourcemaps.init())
282         .pipe(sass().on('error', (err) => {
283             log_error(isSuccess, err);
284         }))
285         .pipe(postcss([prefix()]))
286         .pipe(gap.prependText(autoGeneratedHeader))
287         .pipe(gulpif(!config.dev, csso()))
288         .pipe(gulpif(!config.dev, sourcemaps.write()))
289         .pipe(rename({
290             prefix: "rtl_"
291         }))
292         .on('error', (err) => {
293             log_error(isSuccess, err);
294         })
295         .pipe(gulp.dest(config.dest.themes))
296         .on('end', () => {
297             if (isSuccess) {
298                 console.log(logprefix + "Finished compiling RTL base themes");
299             }
300         });
303 // rtl standard themes compact css compilation
304 function rtl_style_uni_compact() {
305     let isSuccess = true;
306     return gulp.src(config.src.styles.style_uni)
307         .pipe(gap.prependText('@import "../compact-theme-defaults";\n'))
308         .pipe(gap.prependText('$dir: rtl;\n@import "../rtl";\n')) // watch out for this relative path!
309         .pipe(gap.appendText('@include if-rtl { @include rtl_style; #bigCal { border-right: 1px solid $black !important; } }\n'))
310         .pipe(injector.replace('// bs4import', '@import "../oemr_rtl_compact_imports";'))
311         .pipe(sourcemaps.init())
312         .pipe(sass().on('error', (err) => {
313             log_error(isSuccess, err);
314         }))
315         .pipe(postcss([prefix()]))
316         .pipe(gap.prependText(autoGeneratedHeader))
317         .pipe(gulpif(!config.dev, csso()))
318         .pipe(gulpif(!config.dev, sourcemaps.write()))
319         .pipe(rename({
320             prefix: "rtl_compact_"
321         }))
322         .on('error', (err) => {
323             log_error(isSuccess, err);
324         })
325         .pipe(gulp.dest(config.dest.themes))
326         .on('end', () => {
327             if (isSuccess) {
328                 console.log(logprefix + "Finished compiling RTL base compact themes");
329             }
330         });
333 // rtl color themes css compilation
334 function rtl_style_color() {
335     let isSuccess = true;
336     return gulp.src(config.src.styles.style_color)
337         .pipe(gap.prependText('$compact-theme: false;\n$dir: rtl;\n@import "../rtl";\n')) // watch out for this relative path!
338         .pipe(gap.appendText('@include if-rtl { @include rtl_style; #bigCal { border-right: 1px solid $black !important; } }\n'))
339         .pipe(injector.replace('// bs4import', '@import "../oemr-rtl";'))
340         .pipe(sourcemaps.init())
341         .pipe(sass().on('error', (err) => {
342             log_error(isSuccess, err);
343         }))
344         .pipe(postcss([prefix()]))
345         .pipe(gap.prependText(autoGeneratedHeader))
346         .pipe(gulpif(!config.dev, csso()))
347         .pipe(gulpif(!config.dev, sourcemaps.write()))
348         .pipe(rename({
349             prefix: "rtl_"
350         }))
351         .on('error', (err) => {
352             log_error(isSuccess, err);
353         })
354         .pipe(gulp.dest(config.dest.themes))
355         .on('end', () => {
356             if (isSuccess) {
357                 console.log(logprefix + "Compiled OpenEMR RTL color themes");
358             }
359         });
362 // rtl color themes compact css compilation
363 function rtl_style_color_compact() {
364     let isSuccess = true;
365     return gulp.src(config.src.styles.style_color)
366         .pipe(gap.prependText('@import "../compact-theme-defaults";\n'))
367         .pipe(gap.prependText('$dir: rtl;\n@import "../rtl";\n')) // watch out for this relative path!
368         .pipe(gap.appendText('@include if-rtl { @include rtl_style; #bigCal { border-right: 1px solid $black !important; } }\n'))
369         .pipe(injector.replace('// bs4import', '@import "../oemr_rtl_compact_imports";'))
370         .pipe(sourcemaps.init())
371         .pipe(sass().on('error', (err) => {
372             log_error(isSuccess, err);
373         }))
374         .pipe(postcss([prefix()]))
375         .pipe(gap.prependText(autoGeneratedHeader))
376         .pipe(gulpif(!config.dev, csso()))
377         .pipe(gulpif(!config.dev, sourcemaps.write()))
378         .pipe(rename({
379             prefix: "rtl_compact_"
380         }))
381         .on('error', (err) => {
382             log_error(isSuccess, err);
383         })
384         .pipe(gulp.dest(config.dest.themes))
385         .on('end', () => {
386             if (isSuccess) {
387                 console.log(logprefix + "Finished compiling RTL compact color themes");
388             }
389         });
392 // rtl standard themes css compilation
393 function rtl_style_tabs() {
394     let isSuccess = true;
395     return gulp.src(config.src.styles.style_tabs)
396         .pipe(gap.prependText('$dir: rtl;\n@import "rtl";\n')) // watch out for this relative path!
397         .pipe(sourcemaps.init())
398         .pipe(sass().on('error', (err) => {
399             log_error(isSuccess, err);
400         }))
401         .pipe(postcss([prefix()]))
402         .pipe(gap.prependText(autoGeneratedHeader))
403         .pipe(gulpif(!config.dev, csso()))
404         .pipe(gulpif(!config.dev, sourcemaps.write()))
405         .pipe(rename({
406             prefix: "rtl_"
407         }))
408         .on('error', (err) => {
409             log_error(isSuccess, err);
410         })
411         .pipe(gulp.dest(config.dest.themes))
412         .on('end', () => {
413             if (isSuccess) {
414                 console.log(logprefix + "Finished compiling RTL tabs styles");
415             }
416         });
419 // For anything else that needs to be moved, use misc themes
420 function rtl_style_misc() {
421     let isSuccess = true;
422     return gulp.src(config.src.styles.misc)
423         .pipe(gap.prependText('$dir: rtl;\n')) // Simply a flag here due to a hierarchy possibly being created
424         .pipe(sourcemaps.init())
425         .pipe(sass().on('error', (err) => {
426             log_error(isSuccess, err);
427         }))
428         .pipe(postcss([prefix()]))
429         .pipe(gap.prependText(autoGeneratedHeader))
430         .pipe(gulpif(!config.dev, csso()))
431         .pipe(gulpif(!config.dev, sourcemaps.write()))
432         .pipe(rename({
433             prefix: "rtl_"
434         }))
435         .on('error', (err) => {
436             log_error(isSuccess, err);
437         })
438         .pipe(gulp.dest(config.dest.misc_themes))
439         .on('end', () => {
440             if (isSuccess) {
441                 console.log(logprefix + "Compiled rest of RTL SCSS");
442             }
443         });
446 // compile themes
447 const styles = gulp.parallel(styles_style_color, styles_style_color_compact, styles_style_uni, styles_style_uni_compact, styles_style_tabs, styles_style_misc, styles_style_other, rtl_style_color, rtl_style_color_compact, rtl_style_uni, rtl_style_uni_compact, rtl_style_tabs, rtl_style_misc);
449 // Copies (and distills, if possible) assets from node_modules to public/assets
450 function install(done) {
451     console.log(logprefix + "Running OpenEMR gulp install task...");
452     // combine dependencies and napa sources into one object
453     const dependencies = packages.dependencies;
454     for (let key in packages.napa) {
455         if (Object.prototype.hasOwnProperty.call(packages.napa, key)) {
456             dependencies[key] = packages.napa[key];
457         }
458     }
460     for (let key in dependencies) {
461         // check if the property/key is defined in the object itself, not in parent
462         if (Object.prototype.hasOwnProperty.call(dependencies, key)) {
463             if (key == "dwv") {
464                 // dwv is special and need to copy dist, decoders and locales
465                 gulp.src("node_modules/" + key + "/dist/**/*").pipe(
466                     gulp.dest(config.dist.assets + key + "/dist")
467                 );
468                 gulp.src("node_modules/" + key + "/decoders/**/*").pipe(
469                     gulp.dest(config.dist.assets + key + "/decoders")
470                 );
471                 gulp.src("node_modules/" + key + "/locales/**/*").pipe(
472                     gulp.dest(config.dist.assets + key + "/locales")
473                 );
474             } else if (key == "bootstrap" || key == "bootstrap-rtl") {
475                 // bootstrap and bootstrap-v4-rtl are special and need to copy dist and scss
476                 gulp.src("node_modules/" + key + "/dist/**/*").pipe(
477                     gulp.dest(config.dist.assets + key + "/dist")
478                 );
479                 gulp.src("node_modules/" + key + "/scss/**/*").pipe(
480                     gulp.dest(config.dist.assets + key + "/scss")
481                 );
482             } else if (key == "@fortawesome/fontawesome-free") {
483                 // @fortawesome/fontawesome-free is special and need to copy css, scss, and webfonts
484                 gulp.src("node_modules/" + key + "/css/**/*").pipe(
485                     gulp.dest(config.dist.assets + key + "/css")
486                 );
487                 gulp.src("node_modules/" + key + "/scss/**/*").pipe(
488                     gulp.dest(config.dist.assets + key + "/scss")
489                 );
490                 gulp.src("node_modules/" + key + "/webfonts/**/*").pipe(
491                     gulp.dest(config.dist.assets + key + "/webfonts")
492                 );
493             } else if (key == "moment") {
494                 gulp.src("node_modules/" + key + "/min/**/*").pipe(
495                     gulp.dest(config.dist.assets + key + "/min")
496                 );
497                 gulp.src("node_modules/" + key + "/moment.js").pipe(
498                     gulp.dest(config.dist.assets + key)
499                 );
500             } else if (fs.existsSync("node_modules/" + key + "/dist")) {
501                 // only copy dist directory, if it exists
502                 gulp.src("node_modules/" + key + "/dist/**/*").pipe(
503                     gulp.dest(config.dist.assets + key + "/dist")
504                 );
505             } else {
506                 // copy everything
507                 gulp.src("node_modules/" + key + "/**/*").pipe(
508                     gulp.dest(config.dist.assets + key)
509                 );
510             }
511         }
512     }
514     console.log(logprefix + "Finished running OpenEMR gulp install task");
515     done();
518 function watch() {
519     let isSuccess = true;
520     console.log(logprefix + "Running gulp watch task...");
521     // watch all changes and re-run styles
522     gulp.watch('./interface/**/*.scss', {
523             interval: 1000,
524             mode: 'poll'
525         }, styles)
526         .on('error', (err) => {
527             log_error(isSuccess, err);
528         });
530     // watch php separately since autoprefix is not needed
531     gulp_watch('./interface/themes/*.php', {
532             ignoreInitial: false
533         })
534         .pipe(gulp.dest(config.dest.themes))
535         .on('error', (err) => {
536             log_error(isSuccess, err);
537         });
539     // watch all changes to css files in themes and
540     // autoprefix them before copying to public
541     return gulp_watch('./interface/themes/*.css', {
542             ignoreInitial: false
543         })
544         .pipe(postcss([prefix()]))
545         .on('error', (err) => {
546             log_error(isSuccess, err);
547         })
548         .pipe(gulp.dest(config.dest.themes))
549         .on('end', () => {
550             if (isSuccess) {
551                 console.log(logprefix + "Finished running gulp watch task");
552             }
553         });
556 function sync() {
557     let isSuccess = true;
558     console.log(logprefix + "Running gulp sync task...");
559     // copy all leftover root-level components to the theme directory
560     // hoping this is only temporary
561     // Copy php file separately since we don't need to autoprefix them
562     gulp.src(['interface/themes/*.php'])
563         .pipe(gulp.dest(config.dest.themes))
564         .on('error', (err) => {
565             log_error(isSuccess, err);
566         });
568     // Copy CSS files and autoprefix them
569     return gulp.src(['interface/themes/*.css'])
570         .pipe(postcss([prefix()]))
571         .on('error', (err) => {
572             log_error(isSuccess, err);
573         })
574         .pipe(gulp.dest(config.dest.themes))
575         .on('end', () => {
576             if (isSuccess) {
577                 console.log(logprefix + "Finished running gulp sync task");
578             }
579         });
582 // Export watch task
583 exports.watch = watch;
585 // Export pertinent default task
586 // - Note that the default task runs if no other task is chosen,
587 //    which is generally how this script is always used (except in
588 //    rare case where the user is running the watch task).
589 if (config.install) {
590     exports.default = gulp.series(install);
591 } else {
592     exports.default = gulp.series(clean, ingest, styles, sync);