4 const browserSync = require('browser-sync');
5 const csso = require('gulp-csso');
6 const del = require('del');
7 const fs = require('fs');
8 const glob = require('glob');
9 const gap = require('gulp-append-prepend');
10 const replace = require('replace-in-file');
11 const gulp = require('gulp');
12 const argv = require('minimist')(process.argv.slice(2));
13 const gulpif = require('gulp-if');
14 const prefix = require('gulp-autoprefixer');
15 const reload = browserSync.reload;
16 const rename = require('gulp-rename');
17 const sass = require('gulp-sass');
18 const sourcemaps = require('gulp-sourcemaps');
19 const gulp_watch = require('gulp-watch');
22 const packages = require('./package.json');
26 all: [], // must always be empty
28 // Command Line Arguments
31 syncOnly: argv['sync-only'],
35 // Source file locations
38 style_portal: 'interface/themes/patientportal-style.scss',
39 style_uni: 'interface/themes/oe-styles/style_*.scss',
40 style_color: 'interface/themes/colors/*.scss',
41 directional: 'interface/themes/directional.scss'
45 assets: 'public/assets/'
48 themes: 'public/themes'
52 // Clean up lingering static themes
53 function clean(done) {
54 del.sync([config.dest.themes + "/*"]);
58 // Parses command line arguments
59 function ingest(done) {
60 if (config.dev && typeof config.dev !== "boolean") {
61 // allows for custom proxy to be passed into script
62 config.proxy = config.dev;
68 // definition of header for all compiled css
69 const autoGeneratedHeader = `
70 /*! This style sheet was autogenerated using gulp + scss
71 * For usage instructions, see: https://github.com/openemr/openemr/blob/master/interface/README.md
75 // standard themes css compilation
76 function styles_style_portal() {
77 return gulp.src(config.src.styles.style_portal)
78 .pipe(sourcemaps.init())
79 .pipe(sass().on('error', sass.logError))
80 .pipe(prefix('last 1 version'))
81 .pipe(gap.prependText(autoGeneratedHeader))
82 .pipe(gulpif(!config.dev, csso()))
83 .pipe(gulpif(!config.dev, sourcemaps.write()))
84 .pipe(gulp.dest(config.dest.themes))
85 .pipe(gulpif(config.dev && config.build, gulp.dest(config.dest.themes)))
86 .pipe(gulpif(config.dev, reload({ stream: true })));
88 // standard themes css compilation
89 function styles_style_uni() {
90 return gulp.src(config.src.styles.style_uni)
91 .pipe(sourcemaps.init())
92 .pipe(sass().on('error', sass.logError))
93 .pipe(prefix('last 1 version'))
94 .pipe(gap.prependText(autoGeneratedHeader))
95 .pipe(gulpif(!config.dev, csso()))
96 .pipe(gulpif(!config.dev, sourcemaps.write()))
97 .pipe(gulp.dest(config.dest.themes))
98 .pipe(gulpif(config.dev && config.build, gulp.dest(config.dest.themes)))
99 .pipe(gulpif(config.dev, reload({ stream: true })));
102 // color themes css compilation
103 function styles_style_color() {
104 return gulp.src(config.src.styles.style_color)
105 .pipe(sourcemaps.init())
106 .pipe(sass().on('error', sass.logError))
107 .pipe(prefix('last 1 version'))
108 .pipe(gap.prependText(autoGeneratedHeader))
109 .pipe(gulpif(!config.dev, csso()))
110 .pipe(gulpif(!config.dev, sourcemaps.write()))
111 .pipe(gulp.dest(config.dest.themes))
112 .pipe(gulpif(config.dev && config.build, gulp.dest(config.dest.themes)))
113 .pipe(gulpif(config.dev, reload({ stream: true })));
116 // rtl standard themes css compilation
117 function rtl_style_portal() {
118 return gulp.src(config.src.styles.style_portal)
119 .pipe(gap.prependText('$dir: rtl;\n@import "rtl";\n@import "directional";\n')) // watch out for this relative path!
120 .pipe(gap.appendText('@include if-rtl { @include rtl_style; }\n'))
121 .pipe(sourcemaps.init())
122 .pipe(sass().on('error', sass.logError))
123 .pipe(prefix('last 1 version'))
124 .pipe(gap.prependText(autoGeneratedHeader))
125 .pipe(gulpif(!config.dev, csso()))
126 .pipe(gulpif(!config.dev, sourcemaps.write()))
127 .pipe(rename({ prefix: "rtl_" }))
128 .pipe(gulp.dest(config.dest.themes))
129 .pipe(gulpif(config.dev && config.build, gulp.dest(config.dest.themes)))
130 .pipe(gulpif(config.dev, reload({ stream: true })));
133 // rtl standard themes css compilation
134 function rtl_style_uni() {
135 return gulp.src(config.src.styles.style_uni)
136 .pipe(gap.prependText('$dir: rtl;\n@import "../rtl";\n')) // watch out for this relative path!
137 .pipe(gap.appendText('@include if-rtl { @include rtl_style; #bigCal { border-right: 1px solid $black !important; } }\n'))
138 .pipe(sourcemaps.init())
139 .pipe(sass().on('error', sass.logError))
140 .pipe(prefix('last 1 version'))
141 .pipe(gap.prependText(autoGeneratedHeader))
142 .pipe(gulpif(!config.dev, csso()))
143 .pipe(gulpif(!config.dev, sourcemaps.write()))
144 .pipe(rename({ prefix: "rtl_" }))
145 .pipe(gulp.dest(config.dest.themes))
146 .pipe(gulpif(config.dev && config.build, gulp.dest(config.dest.themes)))
147 .pipe(gulpif(config.dev, reload({ stream: true })));
150 // rtl color themes css compilation
151 function rtl_style_color() {
152 return gulp.src(config.src.styles.style_color)
153 .pipe(gap.prependText('$dir: rtl;\n@import "../rtl";\n')) // watch out for this relative path!
154 .pipe(gap.appendText('@include if-rtl { @include rtl_style; #bigCal { border-right: 1px solid $black !important; } }\n'))
155 .pipe(sourcemaps.init())
156 .pipe(sass().on('error', sass.logError))
157 .pipe(prefix('last 1 version'))
158 .pipe(gap.prependText(autoGeneratedHeader))
159 .pipe(gulpif(!config.dev, csso()))
160 .pipe(gulpif(!config.dev, sourcemaps.write()))
161 .pipe(rename({ prefix: "rtl_" }))
162 .pipe(gulp.dest(config.dest.themes))
163 .pipe(gulpif(config.dev && config.build, gulp.dest(config.dest.themes)))
164 .pipe(gulpif(config.dev, reload({ stream: true })));
168 const styles = gulp.parallel(styles_style_uni, styles_style_portal, styles_style_color, rtl_style_portal, rtl_style_uni, rtl_style_color);
170 // Copies (and distills, if possible) assets from node_modules to public/assets
171 function install(done) {
172 // combine dependencies and napa sources into one object
173 const dependencies = packages.dependencies;
174 for (let key in packages.napa) {
175 if (packages.napa.hasOwnProperty(key)) {
176 dependencies[key] = packages.napa[key];
180 for (let key in dependencies) {
181 // check if the property/key is defined in the object itself, not in parent
182 if (dependencies.hasOwnProperty(key)) {
184 // dwv is special and need to copy dist, decoders and locales
185 gulp.src('node_modules/' + key + '/dist/**/*')
186 .pipe(gulp.dest(config.dist.assets + key + '/dist'));
187 gulp.src('node_modules/' + key + '/decoders/**/*')
188 .pipe(gulp.dest(config.dist.assets + key + '/decoders'));
189 gulp.src('node_modules/' + key + '/locales/**/*')
190 .pipe(gulp.dest(config.dist.assets + key + '/locales'));
191 } else if (key == 'bootstrap' || key == 'bootstrap-v4-rtl' || key == 'bootswatch') {
192 // bootstrap, bootstrap-v4-rtl, and bootswatch are special and need to copy dist and scss
193 gulp.src('node_modules/' + key + '/dist/**/*')
194 .pipe(gulp.dest(config.dist.assets + key + '/dist'));
195 gulp.src('node_modules/' + key + '/scss/**/*')
196 .pipe(gulp.dest(config.dist.assets + key + '/scss'));
197 } else if (key == 'select2-bootstrap4-theme') {
198 // select2-bootstrap4-theme is special and need to copy dist and src
199 // modify src/layout.scss in order for sass build to work by removing:
200 // @import "~bootstrap/scss/functions";
201 // @import "~bootstrap/scss/variables";
202 // @import "~bootstrap/scss/mixins";
203 gulp.src('node_modules/' + key + '/dist/**/*')
204 .pipe(gulp.dest(config.dist.assets + key + '/dist'));
205 gulp.src('node_modules/' + key + '/src/**/*')
206 .pipe(gulp.dest(config.dist.assets + key + '/src'))
207 .on('end', function() {
209 files: config.dist.assets + key + '/src/layout.scss',
212 /@import "~bootstrap\/scss\/functions";/,
213 /@import "~bootstrap\/scss\/variables";/,
214 /@import "~bootstrap\/scss\/mixins";/
219 } else if (fs.existsSync('node_modules/' + key + '/dist')) {
220 // only copy dist directory, if it exists
221 gulp.src('node_modules/' + key + '/dist/**/*')
222 .pipe(gulp.dest(config.dist.assets + key + '/dist'));
225 gulp.src('node_modules/' + key + '/**/*')
226 .pipe(gulp.dest(config.dist.assets + key));
235 // watch all changes and re-run styles
236 gulp.watch('./interface/**/*.scss', { interval: 1000, mode: 'poll' }, styles);
238 // watch all changes to css/php files in themes and copy to public
239 return gulp_watch('./interface/themes/*.{css,php}', { ignoreInitial: false })
240 .pipe(gulp.dest(config.dest.themes));
243 function sync_only(done) {
245 proxy: "127.0.0.1:" + config.proxy,
251 // Will start browser sync and/or watch changes to scss
252 // = Runs task(styles) first
256 proxy: "127.0.0.1:" + config.proxy
260 // copy all leftover root-level components to the theme directory
261 // hoping this is only temporary
262 return gulp.src(['interface/themes/*.{css,php}'])
263 .pipe(gulp.dest(config.dest.themes));
267 exports.watch = watch;
269 // Export pertinent default task
270 // - Note that the default task runs if no other task is chosen,
271 // which is generally how this script is always used (except in
272 // rare case where the user is running the watch task).
273 if (config.install) {
274 exports.default = gulp.series(install)
275 } else if (config.syncOnly && config.proxy) {
276 exports.default = gulp.parallel(sync_only, watch)
278 exports.default = gulp.series(clean, ingest, styles, sync);