bundle chunks

This commit is contained in:
Yuri Kuznetsov
2023-06-02 19:26:38 +03:00
parent 39865b4c09
commit 34ec6cfc8d
16 changed files with 702 additions and 149 deletions
+29 -33
View File
@@ -44,11 +44,17 @@ module.exports = grunt => {
const originalLibDir = 'client/lib/original';
let bundleFileMap = {
'client/lib/espo-libs.min.js': buildUtils.getPreparedBundleLibList(libs),
'client/lib/espo-templates.min.js': 'client/lib/original/espo-templates.js',
'client/lib/espo.min.js': originalLibDir + `/espo.js`,
};
let bundleFileMap = {'client/lib/espo-libs.min.js': buildUtils.getPreparedBundleLibList(libs)};
for (let name in bundleConfig.chunks) {
let namePart = 'espo-' + name;
if (name === 'main') {
namePart = 'espo';
}
bundleFileMap[`client/lib/${namePart}.min.js`] = originalLibDir + `/${namePart}.js`
}
let copyJsFileList = buildUtils.getCopyLibDataList(libs);
@@ -255,43 +261,34 @@ module.exports = grunt => {
},
});
grunt.registerTask('bundle-espo', () => {
let chunkConfig = bundleConfig.chunks.main;
let contents = (new BundlerGeneral()).bundle({
files: chunkConfig.files,
patterns: chunkConfig.patterns,
allPatterns: ['client/src/**/*.js'],
libs: libs,
});
contents += '\n' +
(new LayoutTypeBundler()).bundle();
let file = originalLibDir + `/espo.js`;
const writeOriginalLib = (name, contents) => {
if (!fs.existsSync(originalLibDir)) {
fs.mkdirSync(originalLibDir);
}
let file = originalLibDir + `/${name}.js`;
fs.writeFileSync(file, contents, 'utf8');
});
};
grunt.registerTask('bundle-templates', () => {
let chunkConfig = bundleConfig.chunks.templates;
grunt.registerTask('bundle', () => {
let bundler = new BundlerGeneral(bundleConfig, libs);
let contents = (new BundlerGeneral()).bundle({
templatePatterns: chunkConfig.templatePatterns,
modulePaths: {'crm': 'client/modules/crm'},
});
let result = bundler.bundle();
let file = originalLibDir + `/espo-templates.js`;
for (let name in result) {
let contents = result[name];
if (!fs.existsSync(originalLibDir)) {
fs.mkdirSync(originalLibDir);
let key = 'espo-' + name;
if (name === 'main') {
contents += '\n' + (new LayoutTypeBundler()).bundle();
key = 'espo';
}
fs.writeFileSync(file, contents, 'utf8');
writeOriginalLib(key, contents);
}
});
grunt.registerTask('prepare-lib-original', () => {
@@ -476,9 +473,8 @@ module.exports = grunt => {
grunt.registerTask('internal', [
'less',
'cssmin',
'bundle-espo',
'bundle-templates',
'prepare-lib-original',
'bundle',
'uglify:bundle',
'copy:frontendLib',
'prepare-lib',
@@ -1,6 +1,6 @@
{{#if isNotEmpty}}
{{#if viewObject.isSvg}}
{{#if isSvg}}
<svg class="barcode"></svg>
{{else}}
<div class="barcode"></div>
+134
View File
@@ -74,6 +74,10 @@
this._cacheIsSet = false;
this._responseCacheIsSet = false;
this._internalModuleListIsSet = false;
this._bundleFileMap = {};
this._bundleMapping = {};
/** @type Object.<string, string[]> */
this._bundleDependenciesMap = {};
this._addLibsConfigCallCount = 0;
this._addLibsConfigCallMaxCount = 2;
@@ -500,6 +504,25 @@
return;
}
if (name in this._bundleMapping) {
let bundleName = this._bundleMapping[name];
this._addBundle(bundleName).then(() => {
let classObj = this._getClass(name);
if (!classObj) {
let msg = `Could not obtain class '${name}' from bundle '${bundleName}'.`;
console.error(msg);
throw new Error(msg);
}
callback(classObj);
});
return;
}
path = this._nameToPath(name);
}
@@ -578,6 +601,65 @@
});
},
/**
* @private
* @param {string} name
* @return {Promise}
*/
_addBundle: function (name) {
let dependencies = this._bundleDependenciesMap[name] || [];
if (!dependencies.length) {
return this._addBundleInternal(name);
}
return new Promise(resolve => {
let list = dependencies.map(item => Espo.loader.requirePromise(item));
Promise.all(list)
.then(() => {
return this._addBundleInternal(name);
})
.then(() => resolve());
});
},
/**
* @private
* @param {string} name
* @return {Promise}
*/
_addBundleInternal: function (name) {
let src = this._bundleFileMap[name];
if (!src) {
throw new Error(`Unknown bundle '${name}'.`);
}
if (this._cacheTimestamp) {
let sep = (src.indexOf('?') > -1) ? '&' : '?';
src += sep + 'r=' + this._cacheTimestamp;
}
src = this._basePath + src;
let scriptEl = document.createElement('script');
scriptEl.setAttribute('type', 'text/javascript')
scriptEl.setAttribute('src', src);
scriptEl.addEventListener('error', event => {
console.error(`Could not load bundle '${name}'.`, event);
});
return new Promise(resolve => {
document.head.appendChild(scriptEl);
scriptEl.addEventListener('load', () => resolve());
});
},
/**
* @private
*/
@@ -756,6 +838,32 @@
return this._internalModuleMap[moduleName];
},
/**
* @param {string} name A bundle name.
* @param {string} file A bundle file.
* @internal
*/
mapBundleFile: function (name, file) {
this._bundleFileMap[name] = file;
},
/**
* @param {string} name A bundle name.
* @param {string} list Dependencies..
* @internal
*/
mapBundleDependencies: function (name, list) {
this._bundleDependenciesMap[name] = list;
},
/**
* @param {Object.<string, string>} mapping
* @internal
*/
addBundleMapping: function (mapping) {
Object.assign(this._bundleMapping, mapping);
},
/**
* Require a module or multiple modules.
*
@@ -871,6 +979,32 @@
addLibsConfig: function (data) {
loader.addLibsConfig(data);
},
/**
* @param {string} name A bundle name.
* @param {string} file A bundle file.
* @internal
*/
mapBundleFile: function (name, file) {
loader.mapBundleFile(name, file);
},
/**
* @param {string} name A bundle name.
* @param {string} list Dependencies..
* @internal
*/
mapBundleDependencies: function (name, list) {
loader.mapBundleDependencies(name, list);
},
/**
* @param {Object.<string, string>} mapping
* @internal
*/
addBundleMapping: function (mapping) {
loader.addBundleMapping(mapping);
},
};
/**
+31 -13
View File
@@ -26,16 +26,16 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
define('views/fields/barcode',
['views/fields/varchar', 'lib!JsBarcode', 'lib!qrcode'],
function (Dep, JsBarcode, QRCode) {
define('views/fields/barcode', ['views/fields/varchar'], function (Dep) {
let JsBarcode;
let QRCode;
return Dep.extend({
type: 'barcode',
listTemplate: 'fields/barcode/detail',
detailTemplate: 'fields/barcode/detail',
setup: function () {
@@ -66,21 +66,44 @@ function (Dep, JsBarcode, QRCode) {
if (this.params.codeType !== 'QRcode') {
this.isSvg = true;
this.wait(
Espo.loader.requirePromise('lib!JsBarcode').then(lib => {
JsBarcode = lib;
console.log(JsBarcode);
})
);
}
else {
this.wait(
Espo.loader.requirePromise('lib!qrcode').then(lib => {
QRCode = lib;
})
);
}
Dep.prototype.setup.call(this);
$(window).on('resize.' + this.cid, function () {
$(window).on('resize.' + this.cid, () => {
if (!this.isRendered()) {
return;
}
this.controlWidth();
}.bind(this));
});
this.listenTo(this.recordHelper, 'panel-show', () => this.controlWidth());
},
data: function () {
let data = Dep.prototype.data.call(this);
data.isSvg = this.isSvg;
return data;
},
onRemove: function () {
$(window).off('resize.' + this.cid);
},
@@ -122,15 +145,10 @@ function (Dep, JsBarcode, QRCode) {
}
else {
// SVG may be not available yet (in webkit).
setTimeout(
function () {
setTimeout(() => {
this.initBarcode(value);
this.controlWidth();
}
.bind(this),
100
);
}, 100);
}
}
+3 -3
View File
@@ -26,20 +26,20 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
define('views/fields/colorpicker', ['views/fields/varchar', 'lib!Colorpicker'], function (Dep, Colorpicker) {
define('views/fields/colorpicker', ['views/fields/varchar'], function (Dep) {
return Dep.extend({
type: 'varchar',
detailTemplate: 'fields/colorpicker/detail',
listTemplate: 'fields/colorpicker/detail',
editTemplate: 'fields/colorpicker/edit',
setup: function () {
Dep.prototype.setup.call(this);
this.wait(Espo.loader.requirePromise('lib!Colorpicker'));
},
afterRender: function () {
+6 -4
View File
@@ -26,7 +26,7 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
define('views/modals/image-crop', ['views/modal', 'lib!Cropper'], function (Dep, Cropper) {
define('views/modals/image-crop', ['views/modal'], function (Dep) {
return Dep.extend({
@@ -40,7 +40,7 @@ define('views/modals/image-crop', ['views/modal', 'lib!Cropper'], function (Dep,
},
'click [data-action="zoomOut"]': function () {
this.$img.cropper('zoom', -0.1);
}
},
},
setup: function () {
@@ -56,6 +56,8 @@ define('views/modals/image-crop', ['views/modal', 'lib!Cropper'], function (Dep,
},
];
this.wait(Espo.loader.requirePromise('lib!Cropper'));
this.on('remove', () => {
if (this.$img.length) {
this.$img.cropper('destroy');
@@ -65,13 +67,13 @@ define('views/modals/image-crop', ['views/modal', 'lib!Cropper'], function (Dep,
},
afterRender: function () {
var $img = this.$img = $('<img>')
let $img = this.$img = $('<img>')
.attr('src', this.options.contents)
.addClass('hidden');
this.$el.find('.image-container').append($img);
setTimeout(function () {
setTimeout(() => {
$img.cropper({
aspectRatio: 1,
movable: true,
+3 -1
View File
@@ -26,7 +26,7 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
define('views/modals/image-preview', ['views/modal', 'lib!exif'], function (Dep) {
define('views/modals/image-preview', ['views/modal'], function (Dep) {
return Dep.extend({
@@ -82,6 +82,8 @@ define('views/modals/image-preview', ['views/modal', 'lib!exif'], function (Dep)
this.once('remove', () => {
$(window).off('resize.image-review');
});
this.wait(Espo.loader.requirePromise('lib!exif'));
},
getImageUrl: function () {
@@ -26,8 +26,7 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
define('views/scheduled-job/fields/scheduling',
['views/fields/varchar', 'lib!cronstrue'], function (Dep, cronstrue) {
define('views/scheduled-job/fields/scheduling', ['views/fields/varchar'], function (Dep) {
return Dep.extend({
@@ -35,9 +34,14 @@ define('views/scheduled-job/fields/scheduling',
Dep.prototype.setup.call(this);
if (this.isEditMode() || this.isDetailMode()) {
this.listenTo(this.model, 'change:' + this.name, function () {
this.showText();
}, this);
this.wait(
Espo.loader.requirePromise('lib!cronstrue')
.then(Cronstrue => {
this.Cronstrue = Cronstrue;
this.listenTo(this.model, 'change:' + this.name, () => this.showText());
})
);
}
},
@@ -45,7 +49,8 @@ define('views/scheduled-job/fields/scheduling',
Dep.prototype.afterRender.call(this);
if (this.isEditMode() || this.isDetailMode()) {
var $text = this.$text = $('<div class="small text-success"/>');
let $text = this.$text = $('<div class="small text-success"/>');
this.$el.append($text);
this.showText();
}
@@ -56,6 +61,10 @@ define('views/scheduled-job/fields/scheduling',
return;
}
if (!this.Cronstrue) {
return;
}
var exp = this.model.get(this.name);
if (!exp) {
@@ -71,7 +80,7 @@ define('views/scheduled-job/fields/scheduling',
}
var locale = 'en';
var localeList = Object.keys(cronstrue.default.locales);
var localeList = Object.keys(this.Cronstrue.default.locales);
var language = this.getLanguage().name;
if (~localeList.indexOf(language)) {
@@ -82,7 +91,7 @@ define('views/scheduled-job/fields/scheduling',
}
try {
var text = cronstrue.toString(exp, {
var text = this.Cronstrue.toString(exp, {
use24HourTimeFormat: !this.getDateTime().hasMeridian(),
locale: locale,
});
@@ -26,9 +26,9 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
define('views/user-security/modals/totp',
['views/modal', 'model', 'lib!qrcode'],
function (Dep, Model, QRCode) {
define('views/user-security/modals/totp', ['views/modal', 'model'], function (Dep, Model) {
let QRCode;
return Dep.extend({
@@ -114,6 +114,10 @@ define('views/user-security/modals/totp',
}
],
});
Espo.loader.requirePromise('lib!qrcode').then(lib => {
QRCode = lib;
})
},
afterRender: function () {
+111 -1
View File
@@ -1,4 +1,15 @@
{
"order": [
"main",
"templates",
"admin",
"crm",
"wysiwyg",
"chart",
"calendar",
"timeline",
"extra"
],
"chunks": {
"main": {
"files": [
@@ -65,6 +76,105 @@
"client/res/templates/fields/varchar/*.tpl",
"client/res/templates/fields/wysiwyg/*.tpl"
]
},
"admin": {
"patterns": [
"client/src/views/admin/**/*.js",
"client/src/views/authentication-provider/**/*.js",
"client/src/views/inbound-email/**/*.js",
"client/src/views/templates/**/*.js",
"client/src/views/role/**/*.js",
"client/src/views/portal-role/**/*.js",
"client/src/views/scheduled-job/**/*.js",
"client/src/views/lead-capture/**/*.js",
"client/src/views/lead-capture-log-record/**/*.js",
"client/src/views/webhooks/**/*.js",
"client/src/controllers/admin.js",
"client/src/controllers/inbound-email.js",
"client/src/controllers/layout-set.js",
"client/src/controllers/api-user.js",
"client/src/controllers/role.js",
"client/src/controllers/portal-role.js"
],
"templatePatterns": [
"client/res/templates/admin/**/*.tpl"
]
},
"crm": {
"patterns": [
"client/modules/crm/src/**/*.js"
],
"templatePatterns": [
"client/modules/crm/res/templates/**/*.tpl"
],
"allPatterns": [
"client/modules/crm/src/**/*.js"
]
},
"wysiwyg": {
"patterns": [
"client/src/**/*.js",
"client/modules/crm/src/**/*.js"
],
"dependentOn": [
"lib!Summernote"
],
"libs": ["Summernote"],
"allPatterns": [
"client/modules/crm/src/**/*.js"
]
},
"chart": {
"patterns": [
"client/modules/crm/src/**/*.js"
],
"dependentOn": [
"lib!Flotr",
"lib!espo-funnel-chart"
],
"libs": ["Flotr", "espo-funnel-chart"],
"allPatterns": [
"client/modules/crm/src/**/*.js"
]
},
"calendar": {
"patterns": [
"client/modules/crm/src/**/*.js"
],
"dependentOn": [
"lib!full-calendar"
],
"libs": ["full-calendar"],
"allPatterns": [
"client/modules/crm/src/**/*.js"
]
},
"timeline": {
"patterns": [
"client/modules/crm/src/**/*.js"
],
"dependentOn": [
"lib!vis"
],
"libs": ["vis"],
"allPatterns": [
"client/modules/crm/src/**/*.js"
]
},
"extra": {
"patterns": [
"client/src/**/*.js"
],
"templatePatterns": [
"client/res/templates/**/*.tpl"
],
"noDuplicates": true
}
}
},
"modulePaths": {
"crm": "client/modules/crm"
},
"allPatterns": [
"client/src/**/*.js"
]
}
+1 -1
View File
@@ -71,7 +71,7 @@ let BuildUtils = {
* src: string,
* dest: string,
* originalDest: string|null,
* minify: boolean
* minify: boolean,
* }[]}
*/
getCopyLibDataList: function (libs) {
+195 -20
View File
@@ -28,47 +28,222 @@
const Bundler = require("./bundler");
const Precompiler = require('./template-precompiler');
const fs = require('fs');
class BundlerGeneral {
/**
* @param {{
* files: string[],
* chunks: Object.<string, {
* files?: string[],
* patterns?: string[],
* allPatterns?: string[],
* libs: {
* templatePatterns?: string[],
* noDuplicates?: boolean,
* dependentOn?: string[],
* libs?: string[],
* }>,
* modulePaths?: Record.<string, string>,
* allPatterns: string[],
* order: string[],
* }} config
* @param {{
* src?: string,
* bundle?: boolean,
* key?: string,
* }[],
* templatePatterns?: string[],
* modulePaths?: Object.<string, string>,
* }} params
* @return {string}
* files?: {
* src: string,
* }[]
* }[]} libs
* @param {string} [filePattern]
*/
bundle(params) {
let contents = '';
constructor(config, libs, filePattern) {
this.config = config;
this.libs = libs;
this.mainBundleFiles = [];
this.filePattern = filePattern || 'client/lib/espo-{*}.min.js';
}
if (params.patterns) {
let chunks = (new Bundler()).bundle({
files: params.files,
patterns: params.patterns,
allPatterns: params.allPatterns,
libs: params.libs,
/**
* @return {Object.<string, string>}
*/
bundle() {
let result = {};
let mapping = {};
let files = [];
let modules = [];
let templateFiles = [];
let mainName = this.config.order[0];
/** @var {Object.<string, string[]>} */
let notBundledMap = {};
this.config.order.forEach((name, i) => {
let data = this.#bundleChunk(name, i === 0, {
files: files,
templateFiles: templateFiles,
});
contents += chunks[0];
files = files.concat(data.files);
templateFiles = templateFiles.concat(data.templateFiles);
modules = modules.concat(data.modules);
notBundledMap[name] = data.notBundledModules;
result[name] = data.contents;
if (i === 0) {
return;
}
data.modules.forEach(item => mapping[item] = name);
let bundleFile = this.filePattern.replace('{*}', name);
let libs = this.config.chunks[name].libs;
if (libs) {
let part = JSON.stringify(libs.map(item => 'lib!' + item));
result[mainName] += `Espo.loader.mapBundleDependencies('${name}', ${part});\n`;
}
result[mainName] += `Espo.loader.mapBundleFile('${name}', '${bundleFile}');\n`;
});
let notBundledModules = [];
this.config.order.forEach(name => {
notBundledMap[name]
.filter(item => !modules.includes(item))
.filter(item => !notBundledModules.includes(item))
.forEach(item => notBundledModules.push(item));
});
if (notBundledModules.length) {
let part = notBundledModules
.map(item => ' ' + item)
.join('\n');
console.log(`\nNot bundled:\n${part}`);
}
result[mainName] += `Espo.loader.addBundleMapping(${JSON.stringify(mapping)});`
return result;
}
/**
* @param {string} name
* @param {boolean} isMain
* @param {{files: [], templateFiles: []}} alreadyBundled
* @return {{
* contents: string,
* modules: string[],
* files: string[],
* templateFiles: string[],
* notBundledModules: string[],
* }}
*/
#bundleChunk(name, isMain, alreadyBundled) {
let contents = '';
let modules = [];
let params = this.config.chunks[name];
let patterns = params.patterns;
let allPatterns = []
.concat(this.config.allPatterns)
.concat(params.allPatterns || []);
let bundledFiles = [];
let bundledTemplateFiles = [];
let notBundledModules = [];
if (params.patterns) {
let bundler = (new Bundler(this.config.modulePaths));
// The main bundle is always loaded, duplicates are not needed.
let ignoreFiles = [].concat(this.mainBundleFiles);
if (params.noDuplicates) {
ignoreFiles = ignoreFiles.concat(alreadyBundled.files);
}
let data = bundler.bundle({
files: params.files,
patterns: patterns,
allPatterns: allPatterns,
libs: this.libs,
ignoreFiles: ignoreFiles,
dependentOn: params.dependentOn,
});
contents += data.contents;
if (isMain) {
this.mainBundleFiles = data.files;
}
modules = data.modules;
bundledFiles = data.files;
/*if (params.libs) {
contents = this.#bundleLibs(params.libs) + '\n' + contents;
}*/
notBundledModules = data.notBundledModules;
}
if (params.templatePatterns) {
contents += '\n' +
(new Precompiler()).precompile({
let ignoreFiles = params.noDuplicates ? [].concat(alreadyBundled.templateFiles) : [];
let data = (new Precompiler()).precompile({
patterns: params.templatePatterns,
modulePaths: params.modulePaths,
modulePaths: this.config.modulePaths,
ignoreFiles: ignoreFiles,
});
contents += '\n' + data.contents;
bundledTemplateFiles = data.files;
}
return contents;
return {
contents: contents,
modules: modules,
files: bundledFiles,
templateFiles: bundledTemplateFiles,
notBundledModules: notBundledModules,
};
}
/**
* @param {string[]} libs
* @return {string}
*/
#bundleLibs(libs) {
let files = [];
this.libs
.filter(item => libs.includes(item.key))
.forEach(item => {
if (item.src) {
files.push(item.src);
return;
}
if (!item.files) {
return;
}
item.files.forEach(item => {
files.push(item.src);
});
});
let contents = files.map(file => fs.readFileSync(file, 'utf-8'));
return contents.join('\n');
}
}
+123 -39
View File
@@ -38,6 +38,13 @@ const {globSync} = require('glob');
*/
class Bundler {
/**
* @param {Object.<string, string>} modulePaths
*/
constructor(modulePaths) {
this.modulePaths = modulePaths;
}
/**
* @private
* @type {string}
@@ -48,55 +55,60 @@ class Bundler {
* Bundles Espo js files into chunks.
*
* @param {{
* files: string[],
* files?: string[],
* patterns: string[],
* allPatterns: string[],
* chunkNumber?: number,
* allPatterns?: string[],
* ignoreFiles?: string[],
* dependentOn?: string[],
* libs: {
* src?: string,
* bundle?: boolean,
* key?: string,
* }[],
* }} params
* @return {string[]}
* @return {{
* contents: string,
* files: string[],
* modules: string[],
* notBundledModules: string[],
* }}
*/
bundle(params) {
let chunkNumber = params.chunkNumber || 1;
let files = []
.concat(params.files)
.concat(this.#obtainFiles(params.patterns, params.files));
.concat(params.files || [])
.concat(this.#obtainFiles(params.patterns, params.files))
.filter(file => !params.ignoreFiles.includes(file));
let allFiles = this.#obtainFiles(params.allPatterns);
let allFiles = this.#obtainFiles(params.allPatterns || params.patterns);
let ignoreLibs = params.libs
.filter(item => item.key && !item.bundle)
.map(item => 'lib!' + item.key);
.map(item => 'lib!' + item.key)
.filter(item => !(params.dependentOn || []).includes(item));
let sortedFiles = this.#sortFiles(files, allFiles, ignoreLibs);
let notBundledModules = [];
let portions = [];
let portionSize = Math.floor(sortedFiles.length / chunkNumber);
let sortedFiles = this.#sortFiles(
files,
allFiles,
ignoreLibs,
params.ignoreFiles || [],
notBundledModules,
params.dependentOn || null,
);
for (let i = 0; i < chunkNumber; i++) {
let end = i === chunkNumber - 1 ?
sortedFiles.length :
(i + 1) * portionSize;
let contents = '';
portions.push(sortedFiles.slice(i * portionSize, end));
}
sortedFiles.forEach(file => contents += this.#normalizeSourceFile(file));
let chunks = [];
let modules = sortedFiles.map(file => this.#obtainModuleName(file));
portions.forEach(portion => {
let chunk = '';
portion.forEach(file => chunk += this.normalizeSourceFile(file));
chunks.push(chunk);
});
return chunks;
return {
contents: contents,
files: sortedFiles,
modules: modules,
notBundledModules: notBundledModules,
};
}
/**
@@ -123,17 +135,27 @@ class Bundler {
* @param {string[]} files
* @param {string[]} allFiles
* @param {string[]} ignoreLibs
* @param {string[]} ignoreFiles
* @param {string[]} notBundledModules
* @param {string[]|null} dependentOn
* @return {string[]}
*/
#sortFiles(files, allFiles, ignoreLibs) {
#sortFiles(
files,
allFiles,
ignoreLibs,
ignoreFiles,
notBundledModules,
dependentOn
) {
/** @var {Object.<string, string[]>} */
let map = {};
let standalonePathList = [];
let modules = [];
let moduleFileMap = {};
let ignoreModules = ignoreFiles.map(file => this.#obtainModuleName(file));
allFiles.forEach(file => {
let data = this.#obtainModuleData(file);
@@ -170,12 +192,16 @@ class Bundler {
});
});
modules = modules.concat(depModules);
modules = modules
.concat(depModules)
.filter(module => !ignoreModules.includes(module));
/** @var {string[]} */
let discardedModules = [];
/** @var {Object.<string, number>} */
let depthMap = {};
/** @var {string[]} */
let pickedModules = [];
for (let name of modules) {
this.#buildTreeItem(
@@ -183,17 +209,29 @@ class Bundler {
map,
depthMap,
ignoreLibs,
discardedModules
dependentOn,
discardedModules,
pickedModules
);
}
if (dependentOn) {
modules = pickedModules;
}
modules.sort((v1, v2) => {
return depthMap[v2] - depthMap[v1];
});
discardedModules.forEach(item => notBundledModules.push(item));
modules = modules.filter(item => !discardedModules.includes(item));
let modulePaths = modules.map(name => {
if (!moduleFileMap[name]) {
throw Error(`Can't obtain ${name}. Might be missing in allPatterns.`);
}
return moduleFileMap[name];
});
@@ -232,7 +270,9 @@ class Bundler {
* @param {Object.<string, string[]>} map
* @param {Object.<string, number>} depthMap
* @param {string[]} ignoreLibs
* @param {string[]} dependentOn
* @param {string[]} discardedModules
* @param {string[]} pickedModules
* @param {number} [depth]
* @param {string[]} [path]
*/
@@ -241,7 +281,9 @@ class Bundler {
map,
depthMap,
ignoreLibs,
dependentOn,
discardedModules,
pickedModules,
depth,
path
) {
@@ -271,6 +313,12 @@ class Bundler {
return;
}
if (dependentOn && dependentOn.includes(depName)) {
path
.filter(item => !pickedModules.includes(item))
.forEach(item => pickedModules.push(item));
}
}
deps.forEach(depName => {
@@ -283,13 +331,31 @@ class Bundler {
map,
depthMap,
ignoreLibs,
dependentOn,
discardedModules,
pickedModules,
depth + 1,
path
);
});
}
/**
* @param {string} file
* @return string
*/
#obtainModuleName(file) {
for (let mod in this.modulePaths) {
let part = this.modulePaths[mod] + '/src/';
if (file.indexOf(part) === 0) {
return mod + ':' + file.substring(part.length, file.length - 3);
}
}
return file.slice(this.#getBathPath().length, -3);
}
/**
* @param {string} path
* @return {{deps: string[], name: string}|null}
@@ -315,7 +381,7 @@ class Bundler {
return null;
}
let moduleName = path.slice(this._getBathPath().length, -3);
let moduleName = this.#obtainModuleName(path);
let deps = [];
@@ -344,7 +410,25 @@ class Bundler {
* @return {boolean}
*/
#isClientJsFile(path) {
return path.indexOf(this._getBathPath()) === 0 && path.slice(-3) === '.js';
if (path.slice(-3) !== '.js') {
return false;
}
let startParts = [this.#getBathPath()];
for (let mod in this.modulePaths) {
let modPath = this.modulePaths[mod];
startParts.push(modPath);
}
for (let starPart of startParts) {
if (path.indexOf(starPart) === 0) {
return true;
}
}
return false;
}
/**
@@ -352,9 +436,9 @@ class Bundler {
* @param {string} path
* @return {string}
*/
normalizeSourceFile(path) {
#normalizeSourceFile(path) {
let sourceCode = fs.readFileSync(path, 'utf-8');
let basePath = this._getBathPath();
let basePath = this.#getBathPath();
if (!this.#isClientJsFile(path)) {
return sourceCode;
@@ -401,7 +485,7 @@ class Bundler {
* @private
* @return {string}
*/
_getBathPath() {
#getBathPath() {
let path = this.basePath;
if (path.slice(-1) !== '/') {
+10 -4
View File
@@ -32,6 +32,8 @@ const archiver = require('archiver');
const process = require('process');
const buildUtils = require('./build-utils');
const bundleConfig = require('../frontend/bundle-config.json');
const exec = cp.exec;
/**
@@ -281,10 +283,14 @@ class Diff
fileList.push('client/lib/espo-libs.min.js');
fileList.push('client/lib/espo-libs.min.js.map');
fileList.push(`client/lib/espo.min.js`);
fileList.push(`client/lib/espo.min.js.map`);
fileList.push('client/lib/espo-templates.min.js');
fileList.push('client/lib/espo-templates.min.js.map');
Object.keys(bundleConfig.chunks)
.map(name => {
let namePart = name === 'main' ? 'espo' : 'espo-' + name;
fileList.push(`client/lib/${namePart}.min.js`);
fileList.push(`client/lib/${namePart}.min.js.map`);
});
fs.readdirSync('client/css/espo/').forEach(file => {
fileList.push('client/css/espo/' + file);
+7 -4
View File
@@ -30,6 +30,7 @@ const fs = require('fs');
const buildUtils = require('../build-utils');
const libs = require('./../../frontend/libs.json');
const bundleConfig = require('../../frontend/bundle-config.json');
const libDir = './client/lib';
const originalLibDir = './client/lib/original';
@@ -40,10 +41,12 @@ const originalLibCrmDir = './client/modules/crm/lib/original';
.filter(path => !fs.existsSync(path))
.forEach(path => fs.mkdirSync(path));
let bundleFiles = [
'espo-templates.js',
'espo.js',
];
let bundleFiles = Object.keys(bundleConfig.chunks)
.map(name => {
let namePart = name === 'main' ? 'espo' : 'espo-' + name;
return namePart + '.js';
});
fs.readdirSync(originalLibDir)
.filter(file => !bundleFiles.includes(file))
+13 -3
View File
@@ -37,9 +37,10 @@ class TemplatePrecompiler {
/**
* @param {{
* patterns: string[],
* modulePaths: Object.<string, string>,
* modulePaths: Record.<string, string>,
* ignoreFiles: string[],
* }} params
* @return {string}
* @return {{contents: string, files: string[]}}
*/
precompile(params) {
let files = [];
@@ -52,10 +53,15 @@ class TemplatePrecompiler {
});
let nameMap = {};
let compiledFiles = [];
files.forEach(file => {
let module = null;
if (params.ignoreFiles.includes(file)) {
return;
}
for (let itemModule in params.modulePaths) {
let path = params.modulePaths[itemModule];
@@ -79,6 +85,7 @@ class TemplatePrecompiler {
}
nameMap[file] = name
compiledFiles.push(file);
});
let contents =
@@ -96,7 +103,10 @@ class TemplatePrecompiler {
contents += `\n});`;
return contents;
return {
files: compiledFiles,
contents: contents,
};
}
}