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-sass');
17 const sourcemaps = require('gulp-sourcemaps');
18 const gulp_watch = require('gulp-watch');
19 const injector = require('gulp-inject-string');
22 const packages = require('./package.json');
26 all: [], // must always be empty
28 // Command Line Arguments
33 // Source file locations
36 style_portal: 'interface/themes/patientportal-*.scss',
37 style_tabs: 'interface/themes/tabs_style_*.scss',
38 style_uni: 'interface/themes/oe-styles/style_*.scss',
39 style_color: 'interface/themes/colors/*.scss',
40 directional: 'interface/themes/directional.scss'
44 assets: 'public/assets/'
47 themes: 'public/themes'
51 // Clean up lingering static themes
52 function clean(done) {
53 del.sync([config.dest.themes + "/*"]);
57 // Parses command line arguments
58 function ingest(done) {
59 if (config.dev && typeof config.dev !== "boolean") {
65 // definition of header for all compiled css
66 const autoGeneratedHeader = `
67 /*! This style sheet was autogenerated using gulp + scss
68 * For usage instructions, see: https://github.com/openemr/openemr/blob/master/interface/README.md
72 // standard themes css compilation
73 function styles_style_portal() {
74 return gulp.src(config.src.styles.style_portal)
75 .pipe(injector.replace('// bs4import', '@import "../../public/assets/bootstrap/scss/bootstrap";'))
76 .pipe(sourcemaps.init())
77 .pipe(sass().on('error', sass.logError))
78 .pipe(postcss([ prefix() ]))
79 .pipe(gap.prependText(autoGeneratedHeader))
80 .pipe(gulpif(!config.dev, csso()))
81 .pipe(gulpif(!config.dev, sourcemaps.write()))
82 .pipe(gulp.dest(config.dest.themes));
84 // standard themes css compilation
85 function styles_style_uni() {
86 return gulp.src(config.src.styles.style_uni)
87 .pipe(injector.replace('// bs4import', '@import "../../../public/assets/bootstrap/scss/bootstrap";'))
88 .pipe(sourcemaps.init())
89 .pipe(sass().on('error', sass.logError))
90 .pipe(postcss([ prefix() ]))
91 .pipe(gap.prependText(autoGeneratedHeader))
92 .pipe(gulpif(!config.dev, csso()))
93 .pipe(gulpif(!config.dev, sourcemaps.write()))
94 .pipe(gulp.dest(config.dest.themes));
97 // color themes css compilation
98 function styles_style_color() {
99 return gulp.src(config.src.styles.style_color)
100 .pipe(injector.replace('// bs4import', '@import "../../../public/assets/bootstrap/scss/bootstrap";'))
101 .pipe(sourcemaps.init())
102 .pipe(sass().on('error', sass.logError))
103 .pipe(postcss([ prefix() ]))
104 .pipe(gap.prependText(autoGeneratedHeader))
105 .pipe(gulpif(!config.dev, csso()))
106 .pipe(gulpif(!config.dev, sourcemaps.write()))
107 .pipe(gulp.dest(config.dest.themes));
110 // Tabs CSS compilation
111 function styles_style_tabs() {
112 return gulp.src(config.src.styles.style_tabs)
113 .pipe(sourcemaps.init())
114 .pipe(sass().on('error', sass.logError))
115 .pipe(postcss([ prefix() ]))
116 .pipe(gap.prependText(autoGeneratedHeader))
117 .pipe(gulpif(!config.dev, csso()))
118 .pipe(gulpif(!config.dev, sourcemaps.write()))
119 .pipe(gulp.dest(config.dest.themes));
122 // rtl standard themes css compilation
123 function rtl_style_portal() {
124 return gulp.src(config.src.styles.style_portal)
125 .pipe(gap.prependText('$dir: rtl;\n@import "rtl";\n@import "directional";\n')) // watch out for this relative path!
126 .pipe(gap.appendText('@include if-rtl { @include rtl_style; @include portal_style; }\n'))
127 .pipe(injector.replace('// bs4import', '@import "oemr-rtl";'))
128 .pipe(sourcemaps.init())
129 .pipe(sass().on('error', sass.logError))
130 .pipe(postcss([ prefix() ]))
131 .pipe(gap.prependText(autoGeneratedHeader))
132 .pipe(gulpif(!config.dev, csso()))
133 .pipe(gulpif(!config.dev, sourcemaps.write()))
134 .pipe(rename({ prefix: "rtl_" }))
135 .pipe(gulp.dest(config.dest.themes));
138 // rtl standard themes css compilation
139 function rtl_style_uni() {
140 return gulp.src(config.src.styles.style_uni)
141 .pipe(gap.prependText('$dir: rtl;\n@import "../rtl";\n')) // watch out for this relative path!
142 .pipe(gap.appendText('@include if-rtl { @include rtl_style; #bigCal { border-right: 1px solid $black !important; } }\n'))
143 .pipe(injector.replace('// bs4import', '@import "../oemr-rtl";'))
144 .pipe(sourcemaps.init())
145 .pipe(sass().on('error', sass.logError))
146 .pipe(postcss([ prefix() ]))
147 .pipe(gap.prependText(autoGeneratedHeader))
148 .pipe(gulpif(!config.dev, csso()))
149 .pipe(gulpif(!config.dev, sourcemaps.write()))
150 .pipe(rename({ prefix: "rtl_" }))
151 .pipe(gulp.dest(config.dest.themes));
154 // rtl color themes css compilation
155 function rtl_style_color() {
156 return gulp.src(config.src.styles.style_color)
157 .pipe(gap.prependText('$dir: rtl;\n@import "../rtl";\n')) // watch out for this relative path!
158 .pipe(gap.appendText('@include if-rtl { @include rtl_style; #bigCal { border-right: 1px solid $black !important; } }\n'))
159 .pipe(injector.replace('// bs4import', '@import "../oemr-rtl";'))
160 .pipe(sourcemaps.init())
161 .pipe(sass().on('error', sass.logError))
162 .pipe(postcss([ prefix() ]))
163 .pipe(gap.prependText(autoGeneratedHeader))
164 .pipe(gulpif(!config.dev, csso()))
165 .pipe(gulpif(!config.dev, sourcemaps.write()))
166 .pipe(rename({ prefix: "rtl_" }))
167 .pipe(gulp.dest(config.dest.themes));
170 // rtl standard themes css compilation
171 function rtl_style_tabs() {
172 return gulp.src(config.src.styles.style_tabs)
173 .pipe(gap.prependText('$dir: rtl;\n@import "rtl";\n')) // watch out for this relative path!
174 .pipe(sourcemaps.init())
175 .pipe(sass().on('error', sass.logError))
176 .pipe(postcss([ prefix() ]))
177 .pipe(gap.prependText(autoGeneratedHeader))
178 .pipe(gulpif(!config.dev, csso()))
179 .pipe(gulpif(!config.dev, sourcemaps.write()))
180 .pipe(rename({ prefix: "rtl_" }))
181 .pipe(gulp.dest(config.dest.themes));
185 const styles = gulp.parallel(styles_style_color, styles_style_uni, styles_style_portal, styles_style_tabs, rtl_style_color, rtl_style_uni, rtl_style_portal, rtl_style_tabs);
187 // Copies (and distills, if possible) assets from node_modules to public/assets
188 function install(done) {
189 // combine dependencies and napa sources into one object
190 const dependencies = packages.dependencies;
191 for (let key in packages.napa) {
192 if (packages.napa.hasOwnProperty(key)) {
193 dependencies[key] = packages.napa[key];
197 for (let key in dependencies) {
198 // check if the property/key is defined in the object itself, not in parent
199 if (dependencies.hasOwnProperty(key)) {
201 // dwv is special and need to copy dist, decoders and locales
202 gulp.src('node_modules/' + key + '/dist/**/*')
203 .pipe(gulp.dest(config.dist.assets + key + '/dist'));
204 gulp.src('node_modules/' + key + '/decoders/**/*')
205 .pipe(gulp.dest(config.dist.assets + key + '/decoders'));
206 gulp.src('node_modules/' + key + '/locales/**/*')
207 .pipe(gulp.dest(config.dist.assets + key + '/locales'));
208 } else if (key == 'bootstrap' || key == 'bootstrap-rtl') {
209 // bootstrap and bootstrap-v4-rtl are special and need to copy dist and scss
210 gulp.src('node_modules/' + key + '/dist/**/*')
211 .pipe(gulp.dest(config.dist.assets + key + '/dist'));
212 gulp.src('node_modules/' + key + '/scss/**/*')
213 .pipe(gulp.dest(config.dist.assets + key + '/scss'));
214 } else if (key == '@fortawesome/fontawesome-free') {
215 // @fortawesome/fontawesome-free is special and need to copy css, scss, and webfonts
216 gulp.src('node_modules/' + key + '/css/**/*')
217 .pipe(gulp.dest(config.dist.assets + key + '/css'));
218 gulp.src('node_modules/' + key + '/scss/**/*')
219 .pipe(gulp.dest(config.dist.assets + key + '/scss'));
220 gulp.src('node_modules/' + key + '/webfonts/**/*')
221 .pipe(gulp.dest(config.dist.assets + key + '/webfonts'));
222 } else if (key == '@ttskch/select2-bootstrap4-theme') {
223 // @ttskch/select2-bootstrap4-theme is special and need to copy dist and src
224 // modify src/layout.scss in order for sass build to work by removing:
225 // @import "~bootstrap/scss/functions";
226 // @import "~bootstrap/scss/variables";
227 // @import "~bootstrap/scss/mixins";
228 gulp.src('node_modules/' + key + '/dist/**/*')
229 .pipe(gulp.dest(config.dist.assets + key + '/dist'));
230 gulp.src('node_modules/' + key + '/src/**/*')
231 .pipe(gulp.dest(config.dist.assets + key + '/src'))
232 .on('end', function() {
234 files: config.dist.assets + key + '/src/layout.scss',
237 /@import "~bootstrap\/scss\/functions";/,
238 /@import "~bootstrap\/scss\/variables";/,
239 /@import "~bootstrap\/scss\/mixins";/
244 } else if (fs.existsSync('node_modules/' + key + '/dist')) {
245 // only copy dist directory, if it exists
246 gulp.src('node_modules/' + key + '/dist/**/*')
247 .pipe(gulp.dest(config.dist.assets + key + '/dist'));
250 gulp.src('node_modules/' + key + '/**/*')
251 .pipe(gulp.dest(config.dist.assets + key));
260 // watch all changes and re-run styles
261 gulp.watch('./interface/**/*.scss', { interval: 1000, mode: 'poll' }, styles);
263 // watch php separately since autoprefix is not needed
264 gulp_watch('./interface/themes/*.php', { ignoreInitial: false })
265 .pipe(gulp.dest(config.dest.themes));
267 // watch all changes to css files in themes and
268 // autoprefix them before copying to public
269 return gulp_watch('./interface/themes/*.css', { ignoreInitial: false })
270 .pipe(postcss([ prefix() ]))
271 .pipe(gulp.dest(config.dest.themes));
275 // copy all leftover root-level components to the theme directory
276 // hoping this is only temporary
277 // Copy php file separately since we don't need to autoprefix them
278 gulp.src(['interface/themes/*.php'])
279 .pipe(gulp.dest(config.dest.themes));
281 // Copy CSS files and autoprefix them
282 return gulp.src(['interface/themes/*.css'])
283 .pipe(postcss([ prefix() ]))
284 .pipe(gulp.dest(config.dest.themes));
288 exports.watch = watch;
290 // Export pertinent default task
291 // - Note that the default task runs if no other task is chosen,
292 // which is generally how this script is always used (except in
293 // rare case where the user is running the watch task).
294 if (config.install) {
295 exports.default = gulp.series(install)
297 exports.default = gulp.series(clean, ingest, styles, sync);