mirror of
https://github.com/opensourcepos/opensourcepos.git
synced 2026-10-28 19:18:58 -04:00
- Replace third-party Gulp plugins (gulp-clean, gulp-run, gulp-rename, stream-series, readable-stream) with native Node.js APIs (node:fs/promises, node:child_process, node:stream) - Streamline Bootswatch 3 and 5 theme copies using glob patterns and exclusion lists instead of repetitive streams - Refactor menubar icon copying to use a mapped configuration array with fs.copyFile - Add injectOptions helper to standardize gulp-inject marker configuration - Remove obsolete and unneeded dependencies (coffeescript, es6-promise, gulp-debug, gulp-gzip, gulp-header) - Update dependencies (bootstrap-table, dompurify, jquery-ui-dist, jquery-validation, jspdf-autotable, license-report, npm-check-updates) - Reorganize gulpfile.js into structured task groups and optimize task concurrency in default and compress pipelines
463 lines
19 KiB
JavaScript
463 lines
19 KiB
JavaScript
// Gulp Build Script
|
|
|
|
import gulp from 'gulp';
|
|
import rev from 'gulp-rev';
|
|
import concat from 'gulp-concat';
|
|
import cleanCSS from 'gulp-clean-css';
|
|
import uglify from 'gulp-uglify';
|
|
import inject from 'gulp-inject';
|
|
import tar from 'gulp-tar';
|
|
import zip from 'gulp-zip';
|
|
|
|
import { exec } from 'node:child_process';
|
|
import fs from 'node:fs/promises';
|
|
import { PassThrough, Readable } from 'node:stream';
|
|
import { pipeline } from 'node:stream/promises';
|
|
import { promisify } from 'node:util';
|
|
|
|
const execAsync = promisify(exec);
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Shared Constants & Helpers
|
|
// -----------------------------------------------------------------------------
|
|
|
|
/**
|
|
* Navigation SVG icon mapping for menubar:
|
|
* - source: Original SVG filename from 'node_modules/elegant-circles/svg/full-color/'
|
|
* - renamed: Target filename saved in 'public/images/menubar/'
|
|
*/
|
|
const MENUBAR_ICONS = [
|
|
{ source: 'star.svg', renamed: 'attributes.svg' },
|
|
{ source: 'bookshelf.svg', renamed: 'cashups.svg' },
|
|
{ source: 'gear.svg', renamed: 'config.svg' },
|
|
{ source: 'contacts.svg', renamed: 'customers.svg' },
|
|
{ source: 'profle.svg', renamed: 'employees.svg' },
|
|
{ source: 'compose.svg', renamed: 'expenses.svg' },
|
|
{ source: 'clipboard.svg', renamed: 'expenses_categories.svg' },
|
|
{ source: 'heart.svg', renamed: 'giftcards.svg' },
|
|
{ source: 'door.svg', renamed: 'home.svg' },
|
|
{ source: 'door.svg', renamed: 'office.svg' },
|
|
{ source: 'stack.svg', renamed: 'item_kits.svg' },
|
|
{ source: 'shop.svg', renamed: 'items.svg' },
|
|
{ source: 'smartphone.svg', renamed: 'messages.svg' },
|
|
{ source: 'tools.svg', renamed: 'migrate.svg' },
|
|
{ source: 'dolly.svg', renamed: 'receivings.svg' },
|
|
{ source: 'bar-chart.svg', renamed: 'reports.svg' },
|
|
{ source: 'cart.svg', renamed: 'sales.svg' },
|
|
{ source: 'briefcase.svg', renamed: 'suppliers.svg' },
|
|
{ source: 'money.svg', renamed: 'taxes.svg' }
|
|
];
|
|
|
|
/**
|
|
* Bootswatch 5 themes that were not present in Bootswatch 3 are excluded to maintain theme parity.
|
|
*/
|
|
const BOOTSWATCH5_EXCLUDED_THEMES = [
|
|
'brite', 'litera', 'lux', 'materia', 'minty', 'morph',
|
|
'pulse', 'quartz', 'sketchy', 'solar', 'vapor', 'zephyr'
|
|
];
|
|
|
|
/**
|
|
* Standardized options helper for gulp-inject comment markers.
|
|
* Generates options matching the pattern <!-- inject:${tag} -->.
|
|
*/
|
|
const injectOptions = (tag, options = {}) => ({
|
|
addRootSlash: false,
|
|
ignorePath: '/public/',
|
|
starttag: `<!-- inject:${tag} -->`,
|
|
...options
|
|
});
|
|
|
|
/**
|
|
* Emits Vinyl file objects from multiple streams sequentially.
|
|
* Native replacement for the stream-series package using Readable.from and PassThrough buffers.
|
|
*/
|
|
const streamSeries = (...streams) => {
|
|
const buffers = streams.map(stream => {
|
|
const passThrough = new PassThrough({ objectMode: true, highWaterMark: 64 });
|
|
stream.on('error', err => passThrough.destroy(err));
|
|
return stream.pipe(passThrough);
|
|
});
|
|
|
|
return Readable.from((async function* () {
|
|
for (const buffer of buffers) {
|
|
for await (const file of buffer) {
|
|
yield file;
|
|
}
|
|
}
|
|
})(), { objectMode: true });
|
|
};
|
|
|
|
|
|
// =============================================================================
|
|
// Group 1: Clean & Licensing
|
|
// =============================================================================
|
|
|
|
// Remove compiled public resources directory before a fresh build
|
|
gulp.task('clean', () => fs.rm('./public/resources', { recursive: true, force: true }));
|
|
|
|
// Generate Composer and npm production/development license reports in public/license/
|
|
gulp.task('update-licenses', async function() {
|
|
await fs.mkdir('public/license', { recursive: true });
|
|
|
|
return Promise.all([
|
|
execAsync('composer licenses --format=json --no-dev > public/license/composer.LICENSES'),
|
|
execAsync('npx license-report --only=prod --output=json --fields=name --fields=author --fields=homepage --fields=installedVersion --fields=licenseType > public/license/npm-prod.LICENSES'),
|
|
execAsync('npx license-report --only=dev --output=json --fields=name --fields=author --fields=homepage --fields=installedVersion --fields=licenseType > public/license/npm-dev.LICENSES'),
|
|
fs.copyFile('LICENSE', 'public/license/LICENSE')
|
|
]);
|
|
});
|
|
|
|
|
|
// =============================================================================
|
|
// Group 2: Vendor Themes & Static Asset Copies
|
|
// =============================================================================
|
|
|
|
// Copy all Bootswatch 3 theme stylesheets and fonts to public/resources/bootswatch/
|
|
gulp.task('copy-bootswatch', function() {
|
|
return pipeline(
|
|
gulp.src([
|
|
'./node_modules/bootswatch/*/*.min.css',
|
|
'./node_modules/bootswatch/fonts/*.*'
|
|
], { base: './node_modules/bootswatch', encoding: false }),
|
|
gulp.dest('public/resources/bootswatch')
|
|
);
|
|
});
|
|
|
|
// Copy Bootswatch 5 theme stylesheets to public/resources/bootswatch5/,
|
|
// excluding themes that were not present in Bootswatch 3
|
|
gulp.task('copy-bootswatch5', function() {
|
|
return pipeline(
|
|
gulp.src([
|
|
'./node_modules/bootswatch5/dist/*/*.min.css',
|
|
`!./node_modules/bootswatch5/dist/{${BOOTSWATCH5_EXCLUDED_THEMES.join(',')}}/**`
|
|
], { base: './node_modules/bootswatch5/dist', encoding: false }),
|
|
gulp.dest('public/resources/bootswatch5')
|
|
);
|
|
});
|
|
|
|
// Copy base Bootstrap 3 and Bootstrap 5 stylesheets
|
|
gulp.task('copy-bootstrap', function() {
|
|
return Promise.all([
|
|
pipeline(
|
|
gulp.src('./node_modules/bootstrap/dist/css/bootstrap.min.css*', { encoding: false }),
|
|
gulp.dest('public/resources/bootswatch/bootstrap')
|
|
),
|
|
pipeline(
|
|
gulp.src([
|
|
'./node_modules/bootstrap5/dist/css/bootstrap.min.css*',
|
|
'./node_modules/bootstrap5/dist/css/bootstrap.rtl.min.css*'
|
|
], { encoding: false }),
|
|
gulp.dest('public/resources/bootswatch5/bootstrap')
|
|
)
|
|
]);
|
|
});
|
|
|
|
// Copy and rename menubar navigation SVG icons to public/images/menubar/
|
|
gulp.task('copy-menubar', async function() {
|
|
await fs.mkdir('public/images/menubar', { recursive: true });
|
|
|
|
return Promise.all(
|
|
MENUBAR_ICONS.map(({ source, renamed }) =>
|
|
fs.copyFile(
|
|
`./node_modules/elegant-circles/svg/full-color/${source}`,
|
|
`public/images/menubar/${renamed}`
|
|
)
|
|
)
|
|
);
|
|
});
|
|
|
|
|
|
// =============================================================================
|
|
// Group 3: Global Application Assets (injected into app/Views/partial/header.php)
|
|
// =============================================================================
|
|
|
|
// Development mode: Injects individual global stylesheets into app/Views/partial/header.php
|
|
gulp.task('debug-css', function() {
|
|
const debugCss = gulp.src([
|
|
'./node_modules/jquery-ui-dist/jquery-ui.css',
|
|
'./node_modules/bootstrap3-dialog/dist/css/bootstrap-dialog.css',
|
|
'./node_modules/jasny-bootstrap/dist/css/jasny-bootstrap.css',
|
|
'./node_modules/bootstrap-datetime-picker/css/bootstrap-datetimepicker.css',
|
|
'./node_modules/bootstrap-daterangepicker/daterangepicker.css',
|
|
'./node_modules/bootstrap-tagsinput-2021/src/bootstrap-tagsinput.css',
|
|
'./node_modules/bootstrap-select/dist/css/bootstrap-select.css',
|
|
'./node_modules/bootstrap-table/dist/bootstrap-table.css',
|
|
'./node_modules/bootstrap-table/dist/extensions/sticky-header/bootstrap-table-sticky-header.css',
|
|
'./node_modules/bootstrap-toggle/css/bootstrap-toggle.css',
|
|
'./node_modules/chartist/dist/chartist.css',
|
|
'./node_modules/chartist-plugin-tooltips/dist/chartist-plugin-tooltip.css',
|
|
'./public/css/bootstrap.autocomplete.css',
|
|
'./public/css/invoice.css',
|
|
'./public/css/ospos.css',
|
|
'./public/css/ospos_print.css',
|
|
'./public/css/popupbox.css',
|
|
'./public/css/receipt.css',
|
|
'./public/css/register.css',
|
|
'./public/css/reports.css'
|
|
]).pipe(rev()).pipe(gulp.dest('public/resources/css'));
|
|
|
|
return pipeline(
|
|
gulp.src('./app/Views/partial/header.php'),
|
|
inject(debugCss, injectOptions('debug:css')),
|
|
gulp.dest('./app/Views/partial')
|
|
);
|
|
});
|
|
|
|
// Production mode: Concatenates and minifies global stylesheets into opensourcepos.min.css for app/Views/partial/header.php
|
|
gulp.task('prod-css', function() {
|
|
const prodCss = gulp.src([
|
|
'./node_modules/jquery-ui-dist/jquery-ui.min.css',
|
|
'./node_modules/bootstrap3-dialog/dist/css/bootstrap-dialog.min.css',
|
|
'./node_modules/jasny-bootstrap/dist/css/jasny-bootstrap.min.css',
|
|
'./node_modules/bootstrap-datetime-picker/css/bootstrap-datetimepicker.min.css',
|
|
'./node_modules/bootstrap-daterangepicker/daterangepicker.css',
|
|
'./node_modules/bootstrap-tagsinput-2021/src/bootstrap-tagsinput.css',
|
|
'./node_modules/bootstrap-select/dist/css/bootstrap-select.min.css',
|
|
'./node_modules/bootstrap-table/dist/bootstrap-table.min.css',
|
|
'./node_modules/bootstrap-table/dist/extensions/sticky-header/bootstrap-table-sticky-header.min.css',
|
|
'./node_modules/bootstrap-toggle/css/bootstrap-toggle.min.css',
|
|
'./node_modules/chartist/dist/chartist.min.css',
|
|
'./node_modules/chartist-plugin-tooltips/dist/chartist-plugin-tooltip.css',
|
|
'./public/css/bootstrap.autocomplete.css',
|
|
'./public/css/invoice.css',
|
|
'./public/css/ospos.css',
|
|
'./public/css/ospos_print.css',
|
|
'./public/css/popupbox.css',
|
|
'./public/css/receipt.css',
|
|
'./public/css/register.css',
|
|
'./public/css/reports.css'
|
|
])
|
|
.pipe(cleanCSS())
|
|
.pipe(concat('opensourcepos.min.css'))
|
|
.pipe(rev())
|
|
.pipe(gulp.dest('public/resources'));
|
|
|
|
return pipeline(
|
|
gulp.src('./app/Views/partial/header.php'),
|
|
inject(prodCss, injectOptions('prod:css')),
|
|
gulp.dest('./app/Views/partial')
|
|
);
|
|
});
|
|
|
|
// Development mode: Injects individual global scripts into app/Views/partial/header.php
|
|
gulp.task('debug-js', function() {
|
|
const debugJs = gulp.src([
|
|
'./node_modules/jquery/dist/jquery.js',
|
|
'./node_modules/jquery-ui-dist/jquery-ui.js',
|
|
'./node_modules/bootstrap/dist/js/bootstrap.js',
|
|
'./node_modules/bootstrap-table/dist/bootstrap-table.js',
|
|
'./node_modules/bootstrap-table/dist/extensions/export/bootstrap-table-export.js',
|
|
'./node_modules/bootstrap-table/dist/extensions/mobile/bootstrap-table-mobile.js',
|
|
'./node_modules/bootstrap-table/dist/extensions/sticky-header/bootstrap-table-sticky-header.js',
|
|
'./node_modules/moment/min/moment.min.js',
|
|
'./node_modules/jquery-form/src/jquery.form.js',
|
|
'./node_modules/jquery-validation/dist/jquery.validate.js',
|
|
'./node_modules/bootstrap3-dialog/dist/js/bootstrap-dialog.js',
|
|
'./node_modules/jasny-bootstrap/dist/js/jasny-bootstrap.js',
|
|
'./node_modules/bootstrap-select/dist/js/bootstrap-select.js',
|
|
'./node_modules/bootstrap-toggle/js/bootstrap-toggle.js',
|
|
'./node_modules/bootstrap-notify/bootstrap-notify.js',
|
|
'./node_modules/bootstrap-datetime-picker/js/bootstrap-datetimepicker.js',
|
|
'./node_modules/file-saver/dist/FileSaver.js',
|
|
'./node_modules/html2canvas/dist/html2canvas.js',
|
|
'./node_modules/jspdf/dist/jspdf.umd.js',
|
|
'./node_modules/jspdf-autotable/dist/jspdf.plugin.autotable.js',
|
|
'./node_modules/dompurify/dist/purify.js',
|
|
'./node_modules/xlsx/dist/xlsx.full.min.js',
|
|
'./node_modules/tableexport.jquery.plugin/tableExport.min.js',
|
|
'./node_modules/chartist/dist/chartist.js',
|
|
'./node_modules/chartist-plugin-axistitle/dist/chartist-plugin-axistitle.js',
|
|
'./node_modules/chartist-plugin-barlabels/dist/chartist-plugin-barlabels.js',
|
|
'./node_modules/chartist-plugin-pointlabels/dist/chartist-plugin-pointlabels.js',
|
|
'./node_modules/chartist-plugin-tooltips/dist/chartist-plugin-tooltip.js',
|
|
'./node_modules/bootstrap-daterangepicker/daterangepicker.js',
|
|
'./public/js/imgpreview.full.jquery.js',
|
|
'./public/js/manage_tables.js',
|
|
'./public/js/nominatim.autocomplete.js'
|
|
]).pipe(rev()).pipe(gulp.dest('public/resources/js'));
|
|
|
|
return pipeline(
|
|
gulp.src('./app/Views/partial/header.php'),
|
|
inject(debugJs, injectOptions('debug:js')),
|
|
gulp.dest('./app/Views/partial')
|
|
);
|
|
});
|
|
|
|
// Production mode: Bundles scripts into opensourcepos.min.js for app/Views/partial/header.php
|
|
gulp.task('prod-js', function() {
|
|
const jqueryStream = gulp.src('./node_modules/jquery/dist/jquery.min.js')
|
|
.pipe(rev())
|
|
.pipe(gulp.dest('public/resources'));
|
|
|
|
const preminifiedVendorJs = gulp.src([
|
|
'./node_modules/jquery-ui-dist/jquery-ui.min.js',
|
|
'./node_modules/bootstrap/dist/js/bootstrap.min.js',
|
|
'./node_modules/bootstrap-table/dist/bootstrap-table.min.js',
|
|
'./node_modules/bootstrap-table/dist/extensions/export/bootstrap-table-export.min.js',
|
|
'./node_modules/bootstrap-table/dist/extensions/mobile/bootstrap-table-mobile.min.js',
|
|
'./node_modules/bootstrap-table/dist/extensions/sticky-header/bootstrap-table-sticky-header.min.js',
|
|
'./node_modules/moment/min/moment.min.js',
|
|
'./node_modules/jquery-form/dist/jquery.form.min.js',
|
|
'./node_modules/jquery-validation/dist/jquery.validate.min.js',
|
|
'./node_modules/bootstrap3-dialog/dist/js/bootstrap-dialog.min.js',
|
|
'./node_modules/jasny-bootstrap/dist/js/jasny-bootstrap.min.js',
|
|
'./node_modules/bootstrap-select/dist/js/bootstrap-select.min.js',
|
|
'./node_modules/bootstrap-toggle/js/bootstrap-toggle.min.js',
|
|
'./node_modules/bootstrap-notify/bootstrap-notify.min.js',
|
|
'./node_modules/bootstrap-datetime-picker/js/bootstrap-datetimepicker.min.js',
|
|
'./node_modules/file-saver/dist/FileSaver.min.js',
|
|
'./node_modules/html2canvas/dist/html2canvas.min.js',
|
|
'./node_modules/jspdf/dist/jspdf.umd.min.js',
|
|
'./node_modules/jspdf-autotable/dist/jspdf.plugin.autotable.min.js',
|
|
'./node_modules/dompurify/dist/purify.min.js',
|
|
'./node_modules/xlsx/dist/xlsx.full.min.js',
|
|
'./node_modules/tableexport.jquery.plugin/tableExport.min.js',
|
|
'./node_modules/chartist/dist/chartist.min.js',
|
|
'./node_modules/chartist-plugin-axistitle/dist/chartist-plugin-axistitle.min.js',
|
|
'./node_modules/chartist-plugin-barlabels/dist/chartist-plugin-barlabels.min.js',
|
|
'./node_modules/chartist-plugin-pointlabels/dist/chartist-plugin-pointlabels.min.js',
|
|
'./node_modules/chartist-plugin-tooltips/dist/chartist-plugin-tooltip.min.js'
|
|
]);
|
|
|
|
const uglifiedAppJs = gulp.src([
|
|
'./node_modules/bootstrap-daterangepicker/daterangepicker.js',
|
|
'./public/js/imgpreview.full.jquery.js',
|
|
'./public/js/manage_tables.js',
|
|
'./public/js/nominatim.autocomplete.js'
|
|
]).pipe(uglify());
|
|
|
|
const coreBundleStream = streamSeries(preminifiedVendorJs, uglifiedAppJs)
|
|
.pipe(concat('opensourcepos.min.js'))
|
|
.pipe(rev())
|
|
.pipe(gulp.dest('./public/resources/'));
|
|
|
|
return pipeline(
|
|
gulp.src('./app/Views/partial/header.php'),
|
|
inject(streamSeries(jqueryStream, coreBundleStream), injectOptions('prod:js')),
|
|
gulp.dest('./app/Views/partial')
|
|
);
|
|
});
|
|
|
|
|
|
// =============================================================================
|
|
// Group 4: Page-Scoped Assets
|
|
// =============================================================================
|
|
|
|
// Inject standalone jQuery into app/Views/login.php (development mode)
|
|
gulp.task('debug-login-js', function() {
|
|
const loginDebugJs = gulp.src([
|
|
'./public/resources/js/jquery-*.js',
|
|
'!./public/resources/js/jquery-*.form.js',
|
|
'!./public/resources/js/jquery-*.validate.js',
|
|
'!./public/resources/js/jquery-ui-*.js'
|
|
], { read: false });
|
|
|
|
return pipeline(
|
|
gulp.src('./app/Views/login.php'),
|
|
inject(loginDebugJs, injectOptions('login:debug:js')),
|
|
gulp.dest('./app/Views')
|
|
);
|
|
});
|
|
|
|
// Inject production minified jQuery into app/Views/login.php (production mode)
|
|
gulp.task('prod-login-js', function() {
|
|
const loginProdJs = gulp.src('./public/resources/jquery-*.min.js', { read: false });
|
|
|
|
return pipeline(
|
|
gulp.src('./app/Views/login.php'),
|
|
inject(loginProdJs, injectOptions('login:prod:js')),
|
|
gulp.dest('./app/Views')
|
|
);
|
|
});
|
|
|
|
// Inject individual TagsInput and Clipboard scripts into app/Views/configs/manage.php (development mode)
|
|
gulp.task('debug-config-js', function() {
|
|
const configDebugJs = gulp.src([
|
|
'./node_modules/bootstrap-tagsinput-2021/dist/bootstrap-tagsinput.js',
|
|
'./node_modules/clipboard/dist/clipboard.js'
|
|
]).pipe(rev()).pipe(gulp.dest('public/resources/js'));
|
|
|
|
return pipeline(
|
|
gulp.src('./app/Views/configs/manage.php'),
|
|
inject(configDebugJs, injectOptions('config:debug:js')),
|
|
gulp.dest('./app/Views/configs')
|
|
);
|
|
});
|
|
|
|
// Inject production TagsInput and Clipboard scripts into app/Views/configs/manage.php (production mode)
|
|
gulp.task('prod-config-js', function() {
|
|
const configProdJs = gulp.src([
|
|
'./node_modules/bootstrap-tagsinput-2021/dist/bootstrap-tagsinput.min.js',
|
|
'./node_modules/clipboard/dist/clipboard.min.js'
|
|
]).pipe(rev()).pipe(gulp.dest('public/resources'));
|
|
|
|
return pipeline(
|
|
gulp.src('./app/Views/configs/manage.php'),
|
|
inject(configProdJs, injectOptions('config:prod:js')),
|
|
gulp.dest('./app/Views/configs')
|
|
);
|
|
});
|
|
|
|
|
|
// =============================================================================
|
|
// Group 5: Master Build Pipeline & Distribution Packaging
|
|
// =============================================================================
|
|
|
|
// Master build task: cleans resources, exports licenses, copies vendor assets, and generates all debug/prod bundles
|
|
gulp.task('default',
|
|
gulp.series(
|
|
'clean',
|
|
gulp.parallel(
|
|
'update-licenses',
|
|
'copy-bootswatch',
|
|
'copy-bootswatch5',
|
|
'copy-bootstrap',
|
|
'copy-menubar',
|
|
gulp.series(
|
|
'debug-config-js',
|
|
'prod-config-js'
|
|
),
|
|
gulp.series(
|
|
'debug-js',
|
|
'prod-js',
|
|
gulp.parallel(
|
|
gulp.series('debug-css', 'prod-css'),
|
|
gulp.series('debug-login-js', 'prod-login-js')
|
|
)
|
|
)
|
|
)
|
|
));
|
|
|
|
// Archive project sources and assets into distributable tar and zip packages in dist/
|
|
gulp.task('compress', async function() {
|
|
const sources = [
|
|
'app/**/*',
|
|
'public/**/*',
|
|
'vendor/**/*',
|
|
'writable/**/*',
|
|
'docs/**/*',
|
|
'*.md',
|
|
'LICENSE',
|
|
'docker*',
|
|
'Dockerfile',
|
|
'.env.example',
|
|
'**/.htaccess',
|
|
'!node_modules/**',
|
|
'!dist/**',
|
|
'!.git/**'
|
|
];
|
|
|
|
await pipeline(
|
|
gulp.src(sources, { base: '.', encoding: false }),
|
|
tar('opensourcepos.tar'),
|
|
gulp.dest('dist')
|
|
);
|
|
|
|
await pipeline(
|
|
gulp.src(sources, { base: '.', encoding: false }),
|
|
zip('opensourcepos.zip'),
|
|
gulp.dest('dist')
|
|
);
|
|
});
|