This commit is contained in:
Yuri Kuznetsov
2023-06-13 18:42:26 +03:00
parent 544e8e9d3f
commit 9099f84441
26 changed files with 574 additions and 291 deletions
+7 -1
View File
@@ -44,7 +44,13 @@ module.exports = grunt => {
const originalLibDir = 'client/lib/original';
let bundleFileMap = {'client/lib/espo-libs.min.js': buildUtils.getPreparedBundleLibList(libs)};
let libsBundleFileList = [
'client/src/namespace.js',
'client/src/loader.js',
...buildUtils.getPreparedBundleLibList(libs),
];
let bundleFileMap = {'client/lib/espo-libs.min.js': libsBundleFileList};
for (let name in bundleConfig.chunks) {
let namePart = 'espo-' + name;
+23 -8
View File
@@ -42,8 +42,8 @@ use Slim\ResponseEmitter;
*/
class ClientManager
{
protected string $mainHtmlFilePath = 'html/main.html';
protected string $runScript = "app.start();";
private string $mainHtmlFilePath = 'html/main.html';
private string $runScript = "app.start();";
private string $favicon = 'client/img/favicon.ico';
private string $favicon196 = 'client/img/favicon196x196.png';
private string $basePath = '';
@@ -59,7 +59,6 @@ class ClientManager
private DevModeJsFileListProvider $devModeJsFileListProvider,
private Module $module
) {
$this->nonce = Util::generateKey();
}
@@ -207,12 +206,13 @@ class ClientManager
'internalModuleList' => Json::encode($internalModuleList),
'applicationDescription' => $this->config->get('applicationDescription') ?? self::APP_DESCRIPTION,
'nonce' => $this->nonce,
'jsLibs' => Json::encode($jsLibs),
'loaderParams' => Json::encode([
'basePath' => $this->basePath,
'cacheTimestamp' => $loaderCacheTimestamp,
'internalModuleList' => $internalModuleList,
'transpiledModuleList' => $this->getTranspiledModuleList(),
'libsConfig' => (object) $jsLibs,
'aliasMap' => $this->getAliasMap(),
]),
];
@@ -240,8 +240,8 @@ class ClientManager
{
if ($this->isDeveloperMode()) {
return array_merge(
$this->getDeveloperModeBundleLibFileList(),
$this->metadata->get(['app', 'client', 'developerModeScriptList']) ?? [],
$this->getDeveloperModeBundleLibFileList(),
);
}
@@ -258,17 +258,17 @@ class ClientManager
private function isDeveloperMode(): bool
{
return $this->config->get('isDeveloperMode');
return (bool) $this->config->get('isDeveloperMode');
}
private function useCache(): bool
{
return $this->config->get('useCache');
return (bool) $this->config->get('useCache');
}
private function useCacheInDeveloperMode(): bool
{
return $this->config->get('useCacheInDeveloperMode');
return (bool) $this->config->get('useCacheInDeveloperMode');
}
private function getCacheTimestamp(): int
@@ -359,4 +359,19 @@ class ClientManager
$modules
);
}
private function getAliasMap(): object
{
$map = (object) [];
foreach ($this->metadata->get(['app', 'jsLibs'], []) as $key => $item) {
$id = $item['amdId'] ?? null;
if ($id) {
$map->$id = 'lib!' . $key;
}
}
return $map;
}
}
@@ -9,7 +9,6 @@
"vis": {
"path": "client/modules/crm/lib/vis.js",
"devPath": "client/modules/crm/lib/original/vis.js",
"exportsAs": "vis",
"sourceMap": true
}
}
@@ -4,8 +4,7 @@
"client/lib/espo.min.js"
],
"developerModeScriptList": [
"client/src/loader.js",
"client/src/loader-init-libs.js"
"client/src/loader.js"
],
"linkList": [
{
@@ -1,4 +1,44 @@
{
"espo": {
"exportsTo": "window",
"exportsAs": "Espo"
},
"jquery": {
"expose": true,
"exposeAs": "$",
"amdId": "jquery"
},
"backbone": {
"exportsTo": "window",
"exportsAs": "Backbone"
},
"bullbone": {
"expose": true,
"exposeAs": "Bull",
"amdId": "bullbone"
},
"handlebars": {
"expose": true,
"exposeAs": "Handlebars"
},
"underscore": {
"expose": true,
"exposeAs": "_",
"amdId": "underscore"
},
"marked": {},
"dompurify": {
"expose": true,
"exposeAs": "DOMPurify"
},
"base64": {
"exportsTo": "window",
"exportsAs": "Base64"
},
"moment": {
"exportsTo": "window",
"exportsAs": "moment"
},
"Flotr": {
"path": "client/lib/flotr2.js",
"devPath": "client/lib/original/flotr2.js",
@@ -26,22 +66,30 @@
"exportsTo": "$.fn",
"exportsAs": "textcomplete"
},
"autocomplete": {
"exportsTo": "$.fn",
"exportsAs": "autocomplete"
},
"timepicker": {
"exportsTo": "$.fn",
"exportsAs": "timepicker"
},
"bootstrap-datepicker": {
"exportsTo": "$.fn",
"exportsAs": "datepicker"
},
"Selectize": {
"path": "client/lib/selectize.js",
"devPath": "client/lib/original/selectize.js",
"exportsTo": "window",
"exportsAs": "Selectize"
},
"autonumeric": {
"exportsTo": "window",
"exportsAs": "AutoNumeric"
},
"autonumeric": {},
"cronstrue": {
"path": "client/lib/cronstrue-i18n.js",
"devPath": "client/lib/original/cronstrue-i18n.js",
"exportsTo": "window",
"exportsAs": "cronstrue",
"sourceMap": true
"sourceMap": true,
"amdId": "cronstrue"
},
"Cropper": {
"path": "client/lib/cropper.js",
@@ -61,8 +109,7 @@
"exif": {
"path": "client/lib/exif.js",
"devPath": "client/lib/original/exif.js",
"exportsTo": "window",
"exportsAs": "EXIF",
"amdId": "exif-js",
"sourceMap": true
},
"JsBarcode": {
+4 -4
View File
@@ -32,7 +32,7 @@ import Espo from 'lib!espo';
import $ from 'lib!jquery';
import Backbone from 'lib!backbone';
import _ from 'lib!underscore';
import Bull from 'lib!bullbone';
import {Events, View as BullView, Factory as BullFactory} from 'lib!bullbone';
import Base64 from 'lib!base64';
import Ui from 'ui';
import Utils from 'utils';
@@ -811,7 +811,7 @@ class App {
return 'client/custom/modules/' + mod + '/' + getResourceInnerPath(type, path);
};
this.viewFactory = new Bull.Factory({
this.viewFactory = new BullFactory({
useCache: false,
defaultViewName: 'views/base',
helper: helper,
@@ -1400,8 +1400,8 @@ class App {
* @property {Number|null} [cacheTimestamp] A cache timestamp.
*/
_.extend(App.prototype, Bull.Events);
_.extend(App.prototype, Events);
App.extend = Bull.View.extend;
App.extend = BullView.extend;
export default App;
+3 -3
View File
@@ -29,7 +29,7 @@
/** @module collection */
import Model from 'model';
import Bull from 'lib!bullbone';
import {Events as BullEvents, View as BullView} from 'lib!bullbone';
import _ from 'lib!underscore';
/**
@@ -1037,8 +1037,8 @@ class Collection {
}
}
Collection.extend = Bull.View.extend;
_.extend(Collection.prototype, Bull.Events);
Collection.extend = BullView.extend;
_.extend(Collection.prototype, BullEvents);
const setOptions = {
add: true,
+3 -3
View File
@@ -29,7 +29,7 @@
/** @module controller */
import Exceptions from 'exceptions';
import Bull from 'lib!bullbone';
import {Events, View as BullView} from 'lib!bullbone';
import _ from 'lib!underscore';
/**
@@ -612,8 +612,8 @@ class Controller {
}
/** For backward compatibility. */
Controller.extend = Bull.View.extend;
Controller.extend = BullView.extend;
_.extend(Controller.prototype, Bull.Events);
_.extend(Controller.prototype, Events);
export default Controller;
+2 -2
View File
@@ -26,7 +26,7 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
import Bull from 'lib!bullbone';
import {Events} from 'lib!bullbone';
/**
* @mixes Bull.Events
@@ -49,6 +49,6 @@ class WorkingTimeRangeHandler {
}
}
_.extend(WorkingTimeRangeHandler.prototype, Bull.Events);
_.extend(WorkingTimeRangeHandler.prototype, Events);
export default WorkingTimeRangeHandler;
+2 -2
View File
@@ -28,7 +28,7 @@
/** @module language */
import Bull from 'lib!bullbone';
import {Events} from 'lib!bullbone';
/**
* A language.
@@ -322,6 +322,6 @@ class Language {
}
}
_.extend(Language.prototype, Bull.Events);
_.extend(Language.prototype, Events);
export default Language;
+2 -2
View File
@@ -28,7 +28,7 @@
/** @module layout-manager */
import Bull from 'lib!bullbone';
import {Events} from 'lib!bullbone';
/**
* A layout manager.
@@ -276,6 +276,6 @@ class LayoutManager {
}
}
_.extend(LayoutManager.prototype, Bull.Events);
_.extend(LayoutManager.prototype, Events);
export default LayoutManager;
+298 -155
View File
@@ -38,11 +38,23 @@
* A callback with resolved dependencies passed as parameters.
* Should return a value to define a module.
*
* @callback Espo.Loader~requireCallback
* @callback Loader~requireCallback
* @param {...any} arguments Resolved dependencies.
* @returns {*}
*/
/**
* @typedef Loader~libData
* @type {Object}
* @property {string} [exportsTo] Exports to.
* @property {string} [exportsAs] Exports as.
* @property {boolean} [sourceMap] Has a source map.
* @property {boolean} [expose] To expose to global.
* @property {string} [exposeAs] To expose to global as.
* @property {string} [path] A path.
* @property {string} [devPath] A path in developer mode.
*/
/**
* A loader. Used for loading and defining AMD modules, resource loading.
* Handles caching.
@@ -56,12 +68,14 @@
constructor(cache, _cacheTimestamp) {
this._cacheTimestamp = _cacheTimestamp || null;
this._cache = cache || null;
/** @type {Object.<string, Loader~libData>} */
this._libsConfig = {};
this._loadCallbacks = {};
this._pathsBeingLoaded = {};
this._dataLoaded = {};
this._classMap = {};
this._loadingSubject = null;
this._definedMap = {};
this._aliasMap = {};
this._contextId = null;
this._responseCache = null;
this._basePath = '';
@@ -172,31 +186,48 @@
/**
* @private
* @param {string} id
*/
_getClass(name) {
if (name in this._classMap) {
return this._classMap[name];
_get(id) {
if (id in this._definedMap) {
return this._definedMap[id];
}
return false;
return void 0;
}
/**
* @private
* @param {string} id
* @param {*} value
*/
_setClass(name, o) {
this._classMap[name] = o;
_set(id, value) {
this._definedMap[id] = value;
if (id.slice(0, 4) === 'lib!') {
let libName = id.slice(4);
const libsData = this._libsConfig[libName];
if (libsData && libsData.expose) {
let key = libsData.exposeAs || libName;
window[key] = value;
}
}
}
/**
* @private
* @param {string} id
* @return {string}
*/
_nameToPath(name) {
if (name.indexOf(':') === -1) {
return 'client/lib/transpiled/src/' + name + '.js';
_idToPath(id) {
if (id.indexOf(':') === -1) {
return 'client/lib/transpiled/src/' + id + '.js';
}
let arr = name.split(':');
let arr = id.split(':');
let namePart = arr[1];
let mod = arr[0];
@@ -218,25 +249,25 @@
/**
* @private
* @param {string} script
* @param {string} name
* @param {string} id
* @param {string} path
*/
_execute(script, name, path) {
_execute(script, id, path) {
/** @var {?string} */
let module = null;
let colonIndex = name.indexOf(':');
let colonIndex = id.indexOf(':');
if (colonIndex > 0) {
module = name.substring(0, colonIndex);
module = id.substring(0, colonIndex);
}
let noStrictMode = false;
if (!module && name.indexOf('lib!') === 0) {
if (!module && id.indexOf('lib!') === 0) {
noStrictMode = true;
let realName = name.substring(4);
let realName = id.substring(4);
let libsData = this._libsConfig[realName] || {};
@@ -272,100 +303,117 @@
/**
* @private
* @param {string} id
* @param {*} value
*/
_executeLoadCallback(subject, o) {
if (subject in this._loadCallbacks) {
this._loadCallbacks[subject].forEach(callback => callback(o));
delete this._loadCallbacks[subject];
_executeLoadCallback(id, value) {
if (!(id in this._loadCallbacks)) {
return;
}
this._loadCallbacks[id].forEach(callback => callback(value));
delete this._loadCallbacks[id];
}
/**
* Define a module.
*
* @param {string} subject A module name to be defined.
* @param {string[]} dependency A dependency list.
* @param {Espo.Loader~requireCallback} callback A callback with resolved dependencies
* @param {string|null} id A module name to be defined.
* @param {string[]} dependencyIds A dependency list.
* @param {Loader~requireCallback} callback A callback with resolved dependencies
* passed as parameters. Should return a value to define the module.
*/
define(subject, dependency, callback) {
if (subject) {
subject = this._normalizeClassName(subject);
define(id, dependencyIds, callback) {
if (id) {
id = this._normalizeClassName(id);
}
if (this._loadingSubject) {
subject = subject || this._loadingSubject;
if (this._contextId) {
id = id || this._contextId;
this._loadingSubject = null;
this._contextId = null;
}
if (this._getClass(subject)) {
let existing = this._get(id);
if (typeof existing !== 'undefined') {
return;
}
if (!dependency) {
this._defineProceed(callback, subject, [], -1);
if (!dependencyIds) {
this._defineProceed(callback, id, [], -1);
return;
}
let indexOfExports = dependency.indexOf('exports');
let indexOfExports = dependencyIds.indexOf('exports');
this.require(dependency, (...args) => {
this._defineProceed(callback, subject, args, indexOfExports);
if (Array.isArray(dependencyIds)) {
dependencyIds = dependencyIds.map(item => this._normalizePath(item, id));
}
this.require(dependencyIds, (...args) => {
this._defineProceed(callback, id, args, indexOfExports);
});
}
/**
* @private
* @param {function} callback
* @param {string} subject
* @param {string} id
* @param {Array} args
* @param {number} indexOfExports
*/
_defineProceed(callback, subject, args, indexOfExports) {
let o = callback.apply(root, args);
_defineProceed(callback, id, args, indexOfExports) {
let value = callback.apply(root, args);
if (!o && indexOfExports === -1) {
if (typeof value === 'undefined' && indexOfExports === -1 && id) {
if (this._cache) {
this._cache.clear('a', subject);
this._cache.clear('a', id);
}
throw new Error("Could not load '" + subject + "'");
throw new Error("Could not load '" + id + "'");
}
if (indexOfExports !== -1) {
let exports = args[indexOfExports];
o = ('default' in exports) ? exports.default : exports;
value = ('default' in exports) ? exports.default : exports;
}
this._setClass(subject, o);
this._executeLoadCallback(subject, o);
if (!id) {
console.error(value);
// Libs can define w/o id and set to the root.
// Not supposed to happen as should be suppressed by require.amd = false;
return;
}
this._set(id, value);
this._executeLoadCallback(id, value);
}
/**
* Require a module or multiple modules.
*
* @param {string|string[]} subject A module or modules to require.
* @param {Espo.Loader~requireCallback} callback A callback with resolved dependencies.
* @param {string|string[]} id A module or modules to require.
* @param {Loader~requireCallback} callback A callback with resolved dependencies.
* @param {Function|null} [errorCallback] An error callback.
*/
require(subject, callback, errorCallback) {
require(id, callback, errorCallback) {
let list;
if (Object.prototype.toString.call(subject) === '[object Array]') {
list = subject;
if (Object.prototype.toString.call(id) === '[object Array]') {
list = id;
list.forEach((item, i) => {
list[i] = this._normalizeClassName(item);
});
}
else if (subject) {
subject = this._normalizeClassName(subject);
else if (id) {
id = this._normalizeClassName(id);
list = [subject];
list = [id];
}
else {
list = [];
@@ -418,11 +466,62 @@
return string.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
}
/**
* @param {string} path
* @param {string} id
* @private
*/
_normalizePath(path, id) {
if (path.at(0) !== '.') {
return path;
}
if (path.slice(0, 2) !== './' && path.slice(0, 3) !== '../') {
return path;
}
let outputPath = path;
let dirParts = id.split('/').slice(0, -1);
if (path.slice(0, 2) === './') {
outputPath = dirParts.join('/') + '/' + path.slice(2);
}
let parts = outputPath.split('/');
let up = 0;
for (let part of parts) {
if (part === '..') {
up++;
continue;
}
break;
}
if (!up) {
return outputPath;
}
if (up) {
outputPath = dirParts.slice(0, -up).join('/') + '/' + outputPath.slice(3 * up);
}
return outputPath;
}
/**
* @private
*/
_normalizeClassName(name) {
if (~name.indexOf('.') && !~name.indexOf('!')) {
if (name in this._aliasMap) {
name = this._aliasMap[name];
}
if (~name.indexOf('.') && !~name.indexOf('!') && !name.slice(-3) === '.js') {
console.warn(
name + ': ' +
'class name should use slashes for a directory separator and hyphen format.'
@@ -484,7 +583,6 @@
let dataType, type, path, exportsTo, exportsAs;
let realName = name;
let noAppCache = false;
if (name.indexOf('lib!') === 0) {
@@ -495,31 +593,49 @@
path = realName;
exportsTo = 'window';
exportsAs = realName;
exportsAs = null;
if (realName in this._libsConfig) {
let libData = this._libsConfig[realName] || {};
let isDefinedLib = realName in this._libsConfig;
if (isDefinedLib) {
const libData = this._libsConfig[realName] || {};
path = libData.path || path;
if (this._isDeveloperMode) {
path = libData.devPath || path;
if (this._isDeveloperMode && libData.devPath) {
path = libData.devPath;
}
exportsTo = libData.exportsTo || exportsTo;
exportsAs = libData.exportsAs || exportsAs;
path = (this._isDeveloperMode ? libData.devPath : libData.path) || path;
exportsTo = libData.exportsTo || null;
exportsAs = libData.exportsAs || null;
}
if (isDefinedLib && !exportsTo) {
type = 'amd';
}
if (path.indexOf(':') !== -1) {
console.error(`Not allowed path '${path}'.`);
throw new Error();
}
noAppCache = true;
let obj = this._fetchObject(exportsTo, exportsAs);
let obj = void 0;
if (obj) {
if (exportsTo && exportsAs) {
obj = this._fetchObject(exportsTo, exportsAs);
}
if (typeof obj === 'undefined' && name in this._definedMap) {
obj = this._definedMap[name];
}
if (typeof obj !== 'undefined') {
callback(obj);
return;
@@ -534,20 +650,21 @@
if (path.indexOf(':') !== -1) {
console.error(`Not allowed path '${path}'.`);
throw new Error();
}
}
else {
dataType = 'script';
type = 'class';
type = 'amd';
if (!name || name === '') {
throw new Error("Can not load empty class name");
}
let classObj = this._getClass(name);
let classObj = this._get(name);
if (classObj) {
if (typeof classObj !== 'undefined') {
callback(classObj);
return;
@@ -557,9 +674,9 @@
let bundleName = this._bundleMapping[name];
this._requireBundle(bundleName).then(() => {
let classObj = this._getClass(name);
let classObj = this._get(name);
if (!classObj) {
if (typeof classObj === 'undefined') {
let msg = `Could not obtain class '${name}' from bundle '${bundleName}'.`;
console.error(msg);
@@ -572,7 +689,7 @@
return;
}
path = this._nameToPath(name);
path = this._idToPath(name);
}
if (name in this._dataLoaded) {
@@ -749,31 +866,31 @@
* @private
*/
_processCached(dto, cached) {
let name = dto.name;
let id = dto.name;
let callback = dto.callback;
let type = dto.type;
let dataType = dto.dataType;
let exportsAs = dto.exportsAs;
let exportsTo = dto.exportsTo;
if (type === 'class') {
this._loadingSubject = name;
if (type === 'amd') {
this._contextId = id;
}
if (dataType === 'script') {
this._execute(cached, name, dto.path);
this._execute(cached, id, dto.path);
}
if (type === 'class') {
let classObj = this._getClass(name);
if (type === 'amd') {
let value = this._get(id);
if (classObj) {
callback(classObj);
if (typeof value !== 'undefined') {
callback(value);
return;
}
this._addLoadCallback(name, callback);
this._addLoadCallback(id, callback);
return;
}
@@ -784,7 +901,7 @@
data = this._fetchObject(exportsTo, exportsAs);
}
this._dataLoaded[name] = data;
this._dataLoaded[id] = data;
callback(data);
}
@@ -793,13 +910,14 @@
* @private
*/
_processRequest(dto) {
let name = dto.name;
let id = dto.name;
let url = dto.url;
let errorCallback = dto.errorCallback;
let path = dto.path;
let useCache = dto.useCache;
let noAppCache = dto.noAppCache;
// @todo Use `fetch`.
$.ajax({
type: 'GET',
cache: useCache,
@@ -810,7 +928,7 @@
})
.then(response => {
if (this._cache && !noAppCache && !this._responseCache) {
this._cache.set('a', name, response);
this._cache.set('a', id, response);
}
if (this._responseCache) {
@@ -834,48 +952,58 @@
* @private
*/
_handleResponse(dto, response) {
let name = dto.name;
let id = dto.name;
let callback = dto.callback;
let type = dto.type;
let dataType = dto.dataType;
let exportsAs = dto.exportsAs;
let exportsTo = dto.exportsTo;
this._addLoadCallback(name, callback);
this._addLoadCallback(id, callback);
if (type === 'class') {
this._loadingSubject = name;
if (type === 'amd') {
this._contextId = id;
}
let isLib = id.slice(0, 4) === 'lib!';
if (isLib && exportsAs) {
define.amd = false;
}
if (dataType === 'script') {
this._execute(response, name, dto.path);
this._execute(response, id, dto.path);
}
let data;
if (isLib && exportsAs) {
define.amd = true;
}
if (type === 'class') {
data = this._getClass(name);
let result;
if (data && typeof data === 'function') {
this._executeLoadCallback(name, data);
if (type === 'amd') {
result = this._get(id);
if (typeof result !== 'undefined') {
this._executeLoadCallback(id, result);
}
return;
}
data = response;
result = response;
if (exportsTo && exportsAs) {
data = this._fetchObject(exportsTo, exportsAs);
result = this._fetchObject(exportsTo, exportsAs);
}
this._dataLoaded[name] = data;
this._dataLoaded[id] = result;
this._executeLoadCallback(name, data);
this._executeLoadCallback(id, result);
}
/**
* @param {Object} data
* @param {Object.<string, Loader~libData>} data
* @internal
*/
addLibsConfig(data) {
@@ -888,6 +1016,13 @@
this._libsConfig = {...this._libsConfig, ...data};
}
/**
* @param {Object.<string, string>} map
*/
setAliasMap(map) {
this._aliasMap = map;
}
/**
* @private
*/
@@ -925,16 +1060,24 @@
Object.assign(this._bundleMapping, mapping);
}
/**
* @param {string} id
* @internal
*/
setContextId(id) {
this._contextId = id;
}
/**
* Require a module or multiple modules.
*
* @param {...string} subject A module or modules to require.
* @param {...string} id A module or modules to require.
* @returns {Promise<unknown>}
*/
requirePromise(subject) {
requirePromise(id) {
return new Promise((resolve, reject) => {
this.require(
subject,
id,
(...args) => resolve(...args),
() => reject()
);
@@ -993,55 +1136,41 @@
loader.setResponseCache(responseCache);
},
/**
* @param {string[]} internalModuleList
*/
setInternalModuleList: function (internalModuleList) {
loader.setInternalModuleList(internalModuleList);
},
/**
* @param {string[]} transpiledModuleList
*/
setTranspiledModuleList: function (transpiledModuleList) {
loader.setTranspiledModuleList(transpiledModuleList);
},
/**
* Define a module.
*
* @param {string} subject A module name to be defined.
* @param {string[]} dependency A dependency list.
* @param {Espo.Loader~requireCallback} callback A callback with resolved dependencies
* @param {string} id A module name to be defined.
* @param {string[]} dependencyIds A dependency list.
* @param {Loader~requireCallback} callback A callback with resolved dependencies
* passed as parameters. Should return a value to define the module.
*/
define: function (subject, dependency, callback) {
loader.define(subject, dependency, callback);
define: function (id, dependencyIds, callback) {
loader.define(id, dependencyIds, callback);
},
/**
* Require a module or multiple modules.
*
* @param {string|string[]} subject A module or modules to require.
* @param {Espo.Loader~requireCallback} callback A callback with resolved dependencies.
* @param {string|string[]} id A module or modules to require.
* @param {Loader~requireCallback} callback A callback with resolved dependencies.
* @param {Function|null} [errorCallback] An error callback.
*/
require: function (subject, callback, errorCallback) {
loader.require(subject, callback, errorCallback);
require: function (id, callback, errorCallback) {
loader.require(id, callback, errorCallback);
},
/**
* Require a module or multiple modules.
*
* @param {string|string[]} subject A module or modules to require.
* @param {string|string[]} id A module or modules to require.
* @returns {Promise<unknown>}
*/
requirePromise: function (subject) {
return loader.requirePromise(subject);
requirePromise: function (id) {
return loader.requirePromise(id);
},
/**
* @param {Object} data
* @param {Object.<string, Loader~libData>} data
* @internal
*/
addLibsConfig: function (data) {
@@ -1073,24 +1202,32 @@
addBundleMapping: function (mapping) {
loader.addBundleMapping(mapping);
},
/**
* @param {string} id
* @internal
*/
setContextId: function (id) {
loader.setContextId(id);
},
};
/**
* Require a module or multiple modules.
*
* @param {string|string[]} subject A module or modules to require.
* @param {Espo.Loader~requireCallback} callback A callback with resolved dependencies.
* @param {string|string[]} id A module or modules to require.
* @param {Loader~requireCallback} callback A callback with resolved dependencies.
* @param {Object} [context] A context.
* @param {Function|null} [errorCallback] An error callback.
*
* @deprecated Use `Espo.loader.require` instead.
*/
root.require = Espo.require = function (subject, callback, context, errorCallback) {
root.require = Espo.require = function (id, callback, context, errorCallback) {
if (context) {
callback = callback.bind(context);
}
loader.require(subject, callback, errorCallback);
loader.require(id, callback, errorCallback);
};
/**
@@ -1101,50 +1238,56 @@
* 2. `(dependencyList, callback)` Unnamed, with dependencies.
* 3. `(moduleName, dependencyList, callback)` Named.
*
* @param {string|string[]|Espo.Loader~requireCallback} arg1 A module name to be defined,
* @param {string|string[]|Loader~requireCallback} arg1 A module name to be defined,
* a dependency list or a callback.
* @param {string[]|Espo.Loader~requireCallback} [arg2] A dependency list or a callback with resolved
* @param {string[]|Loader~requireCallback} [arg2] A dependency list or a callback with resolved
* dependencies.
* @param {Espo.Loader~requireCallback} [arg3] A callback with resolved dependencies.
* @param {Loader~requireCallback} [arg3] A callback with resolved dependencies.
*/
root.define = Espo.define = function (arg1, arg2, arg3) {
let subject = null;
let dependency = null;
let id = null;
let depIds = null;
let callback;
if (typeof arg1 === 'function') {
callback = arg1;
}
else if (typeof arg1 !== 'undefined' && typeof arg2 === 'function') {
dependency = arg1;
if (Array.isArray(arg1)) {
depIds = arg1;
} else {
id = arg1;
depIds = [];
}
callback = arg2;
}
else {
subject = arg1;
dependency = arg2;
id = arg1;
depIds = arg2;
callback = arg3;
}
loader.define(subject, dependency, callback);
loader.define(id, depIds, callback);
};
root.define.amd = true;
(() => {
let loaderParamsTag = document.querySelector('script[data-name="loader-params"]');
const loaderParamsTag = document.querySelector('script[data-name="loader-params"]');
if (loaderParamsTag) {
let params = JSON.parse(loaderParamsTag.textContent);
Espo.loader.setCacheTimestamp(params.cacheTimestamp);
Espo.loader.setBasePath(params.basePath);
Espo.loader.setInternalModuleList(params.internalModuleList);
Espo.loader.setTranspiledModuleList(params.transpiledModuleList);
if (!loaderParamsTag) {
return;
}
let jsLibsTag = document.querySelector('script[data-name="js-libs"]');
const params = JSON.parse(loaderParamsTag.textContent);
if (jsLibsTag) {
Espo.loader.addLibsConfig(JSON.parse(jsLibsTag.textContent));
}
loader.setCacheTimestamp(params.cacheTimestamp);
loader.setBasePath(params.basePath);
loader.setInternalModuleList(params.internalModuleList);
loader.setTranspiledModuleList(params.transpiledModuleList);
loader.addLibsConfig(params.libsConfig);
loader.setAliasMap(params.aliasMap);
})();
}).call(window);
+2 -2
View File
@@ -28,7 +28,7 @@
/** @module metadata */
import Bull from 'lib!bullbone';
import {Events} from 'lib!bullbone';
/**
* Application metadata.
@@ -261,6 +261,6 @@ class Metadata {
}
}
_.extend(Metadata.prototype, Bull.Events);
_.extend(Metadata.prototype, Events);
export default Metadata;
+3 -3
View File
@@ -28,7 +28,7 @@
/** @module model */
import Bull from 'lib!bullbone';
import {Events, View as BullView} from 'lib!bullbone';
import _ from 'lib!underscore';
/**
@@ -1008,8 +1008,8 @@ class Model {
}
}
Model.extend = Bull.View.extend;
Model.extend = BullView.extend;
_.extend(Model.prototype, Bull.Events);
_.extend(Model.prototype, Events);
export default Model;
-2
View File
@@ -28,8 +28,6 @@
/** @module storage */
import Bull from 'lib!bullbone';
/**
* A storage. Data is saved across browser sessions, has no expiration time.
*/
+2 -2
View File
@@ -28,7 +28,7 @@
/** @module view-record-helper */
import Bull from 'lib!bullbone';
import {Events} from 'lib!bullbone';
class ViewRecordHelper {
@@ -207,6 +207,6 @@ class ViewRecordHelper {
}
}
_.extend(ViewRecordHelper.prototype, Bull.Events);
_.extend(ViewRecordHelper.prototype, Events);
export default ViewRecordHelper;
+3 -2
View File
@@ -28,14 +28,15 @@
/** @module view */
import Bull from 'lib!bullbone';
import {View as BullView} from 'lib!bullbone';
/**
* A base view. All views should extend this class.
*
* @see {@link https://docs.espocrm.com/development/view/}
* @mixes Bull.Events
*/
class View extends Bull.View {
class View extends BullView {
/**
* @callback module:view~actionHandlerCallback
+1 -2
View File
@@ -15,12 +15,11 @@
"files": [
"src/namespace.js",
"src/loader.js",
"src/loader-init-libs.js",
"src/exceptions.js",
"src/utils.js"
],
"patterns": [
"src/!(pre-loader).js",
"src/!(pre-loader|namespace|loader).js",
"src/ui/**/*.js",
"src/views/*.js",
"src/views/site/*.js",
+28 -17
View File
@@ -5,7 +5,7 @@
"key": "jquery"
},
{
"src": "node_modules/underscore/underscore.js",
"src": "node_modules/underscore/underscore-umd.js",
"bundle": true,
"key": "underscore"
},
@@ -20,22 +20,27 @@
"key": "handlebars"
},
{
"src": "node_modules/bullbone/dist/bullbone.js",
"src": "node_modules/bullbone/dist/bullbone.umd.js",
"bundle": true,
"key": "bullbone"
},
{
"src": "node_modules/js-base64/base64.js",
"bundle": true,
"key": "Base64"
"key": "Base64",
"suppressAmd": true
},
{
"src": "node_modules/moment/moment.js",
"bundle": true
"bundle": true,
"key": "moment",
"suppressAmd": true
},
{
"src": "node_modules/moment-timezone/moment-timezone.js",
"bundle": true
"bundle": true,
"key": "moment-timezone",
"suppressAmd": true
},
{
"src": "node_modules/moment-timezone-data-build/build/moment-timezone-data.js",
@@ -43,11 +48,15 @@
},
{
"src": "node_modules/timepicker/jquery.timepicker.js",
"bundle": true
"bundle": true,
"key": "timepicker",
"suppressAmd": true
},
{
"src": "node_modules/devbridge-autocomplete/dist/jquery.autocomplete.js",
"bundle": true
"bundle": true,
"key": "autocomplete",
"suppressAmd": true
},
{
"src": "node_modules/jquery-textcomplete/dist/jquery.textcomplete.js",
@@ -60,7 +69,9 @@
},
{
"src": "node_modules/bootstrap-datepicker/dist/js/bootstrap-datepicker.js",
"bundle": true
"bundle": true,
"key": "bootstrap-datepicker",
"suppressAmd": true
},
{
"src": "node_modules/marked/lib/marked.umd.js",
@@ -78,26 +89,26 @@
},
{
"src": "node_modules/jquery-ui-espo/dist/jquery-ui.js",
"bundle": true
"bundle": true,
"suppressAmd": true
},
{
"src": "node_modules/jquery-ui-touch-punch/jquery.ui.touch-punch.js",
"bundle": true
"bundle": true,
"suppressAmd": true
},
{
"name": "gridstack",
"files": [
{
"src": "node_modules/gridstack/dist/gridstack-jq.js"
}
],
"src": "node_modules/gridstack/dist/gridstack-jq.js",
"bundle": true,
"key": "gridstack"
"key": "gridstack",
"suppressAmd": true
},
{
"src": "node_modules/selectize/dist/js/standalone/selectize.js",
"bundle": true,
"key": "Selectize"
"key": "Selectize",
"suppressAmd": true
},
{
"src": "node_modules/autonumeric/dist/autoNumeric.js",
+1 -1
View File
@@ -3,7 +3,7 @@
"srcFiles": [
"client/lib/espo-libs.min.js",
"client/lib/transpiled/src/loader.js",
"client/lib/transpiled/src/loader-init-libs.js",
"frontend/test/loader-init-libs.js",
"client/lib/transpiled/src/exceptions.js",
"client/lib/transpiled/src/utils.js",
"client/lib/transpiled/src/ajax.js",
@@ -26,47 +26,44 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
Espo.loader.addLibsConfig(
{
Espo.loader.addLibsConfig({
"espo": {
"exportsTo": "window",
"exportsAs": "Espo"
},
"jquery": {
"exportsTo": "window",
"exportsAs": "$"
"expose": true,
"exposeAs": "$",
"amdId": "jquery"
},
"backbone": {
"exportsTo": "window",
"exportsAs": "Backbone"
},
"bullbone": {
"exportsTo": "window",
"exportsAs": "Bull"
"expose": true,
"exposeAs": "Bull",
"amdId": "bullbone"
},
"handlebars": {
"exportsTo": "window",
"exportsAs": "Handlebars"
"expose": true,
"exposeAs": "Handlebars"
},
"underscore": {
"exportsTo": "window",
"exportsAs": "_"
},
"marked": {
"exportsTo": "window",
"exportsAs": "marked"
"expose": true,
"exposeAs": "_",
"amdId": "underscore"
},
"marked": {},
"dompurify": {
"exportsTo": "window",
"exportsAs": "DOMPurify"
"expose": true,
"exposeAs": "DOMPurify"
},
"base64": {
"exportsTo": "window",
"exportsAs": "Base64"
},
"moment": {
"exportsTo": "window",
"exportsAs": "moment"
"expose": true
}
}
);
});
+1 -2
View File
@@ -2,8 +2,7 @@
<html>
<head>
<title>{{applicationName}}</title>
<script type="application/json" data-name="loader-params">{{loaderParams}}</script>
<script type="application/json" data-name="js-libs">{{jsLibs}}</script>{{scriptsHtml}}
<script type="application/json" data-name="loader-params">{{loaderParams}}</script>{{scriptsHtml}}
<link rel="stylesheet" href="{{basePath}}{{stylesheet}}?r={{cacheTimestamp}}" id='main-stylesheet'>{{additionalStyleSheetsHtml}}{{linksHtml}}
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
<meta content="utf-8" http-equiv="encoding">
+65 -4
View File
@@ -55,10 +55,7 @@ fs.readdirSync(originalLibDir)
fs.readdirSync(originalLibCrmDir)
.forEach(file => fs.unlinkSync(originalLibCrmDir + '/' + file));
/** @var {string[]} */
const libSrcList = buildUtils.getBundleLibList(libs);
let stripSourceMappingUrl = path => {
const stripSourceMappingUrl = path => {
/** @var {string} */
let originalContents = fs.readFileSync(path, {encoding: 'utf-8'});
@@ -73,11 +70,65 @@ let stripSourceMappingUrl = path => {
fs.writeFileSync(path, contents, {encoding: 'utf-8'});
}
const addLoadingSubject = (path, subject) => {
/** @var {string} */
let contents = fs.readFileSync(path, {encoding: 'utf-8'});
contents =
`Espo.loader.setContextId('${subject}');\n` +
contents + '\n' +
`Espo.loader.setContextId(null);\n`;
fs.writeFileSync(path, contents, {encoding: 'utf-8'});
}
const addSuppressAmd = path => {
/** @var {string} */
let contents = fs.readFileSync(path, {encoding: 'utf-8'});
contents =
`var _previousDefineAmd = define.amd; define.amd = false;\n` +
contents + '\n' +
`define.amd = _previousDefineAmd;\n`;
fs.writeFileSync(path, contents, {encoding: 'utf-8'});
}
/** @var {string[]} */
const libSrcList = buildUtils.getBundleLibList(libs);
let keyMap = {};
let suppressAmdMap = {};
libs.forEach(item => {
if (!item.key || !item.bundle || item.files) {
return;
}
if (item.suppressAmd) {
suppressAmdMap[item.src] = true;
return;
}
keyMap[item.src] = 'lib!' + item.key;
});
libSrcList.forEach(src => {
let dest = originalLibDir + '/' + src.split('/').slice(-1);
fs.copyFileSync(src, dest);
stripSourceMappingUrl(dest);
if (suppressAmdMap[src]) {
addSuppressAmd(dest);
}
let key = keyMap[src];
if (key) {
addLoadingSubject(dest, key);
}
});
buildUtils.getCopyLibDataList(libs)
@@ -85,4 +136,14 @@ buildUtils.getCopyLibDataList(libs)
.forEach(item => {
fs.copyFileSync(item.src, item.originalDest);
stripSourceMappingUrl(item.originalDest);
if (suppressAmdMap[item.src]) {
addSuppressAmd(item.originalDest);
}
let key = keyMap[item.src];
if (key) {
addLoadingSubject(item.originalDest, key);
}
});
+10 -10
View File
@@ -17,7 +17,7 @@
"bootstrap": "^3.4.1",
"bootstrap-colorpicker": "^2.5.2",
"bootstrap-datepicker": "^1.9.0",
"bullbone": "github:yurikuzn/bull#1.1.5",
"bullbone": "github:yurikuzn/bull#1.2.2",
"cronstrue": "^1.114.0",
"cropper": "^0.7.9",
"devbridge-autocomplete": "^1.4.11",
@@ -31,7 +31,7 @@
"handlebars": "^4.7.7",
"jquery": "^3.6.0",
"jquery-textcomplete": "^1.8.5",
"jquery-ui-espo": "github:yurikuzn/jquery-ui-espo#0.2.0",
"jquery-ui-espo": "github:yurikuzn/jquery-ui-espo#0.2.2",
"jquery-ui-touch-punch": "^0.2.3",
"js-base64": "^3.7.2",
"jsbarcode": "^3.11.4",
@@ -2542,8 +2542,8 @@
}
},
"node_modules/bullbone": {
"version": "1.1.5",
"resolved": "git+ssh://git@github.com/yurikuzn/bull.git#219a5bef1b5442c17c58330d67a34e3e4b78341e",
"version": "1.2.2",
"resolved": "git+ssh://git@github.com/yurikuzn/bull.git#25eeb1a94f3ad86845f92bd979be008221075144",
"license": "MIT"
},
"node_modules/bytes": {
@@ -4968,8 +4968,8 @@
}
},
"node_modules/jquery-ui-espo": {
"version": "0.1.0",
"resolved": "git+ssh://git@github.com/yurikuzn/jquery-ui-espo.git#89b2d030d7b13f5f56a19e5f79776e7eb143f77e",
"version": "0.2.2",
"resolved": "git+ssh://git@github.com/yurikuzn/jquery-ui-espo.git#844897483dbf31603b81cadda07b0de5602c231e",
"license": "ISC",
"dependencies": {
"grunt": "^1.4.1",
@@ -8908,8 +8908,8 @@
"dev": true
},
"bullbone": {
"version": "git+ssh://git@github.com/yurikuzn/bull.git#219a5bef1b5442c17c58330d67a34e3e4b78341e",
"from": "bullbone@github:yurikuzn/bull#1.1.5"
"version": "git+ssh://git@github.com/yurikuzn/bull.git#25eeb1a94f3ad86845f92bd979be008221075144",
"from": "bullbone@github:yurikuzn/bull#1.2.2"
},
"bytes": {
"version": "3.1.2",
@@ -10706,8 +10706,8 @@
}
},
"jquery-ui-espo": {
"version": "git+ssh://git@github.com/yurikuzn/jquery-ui-espo.git#89b2d030d7b13f5f56a19e5f79776e7eb143f77e",
"from": "jquery-ui-espo@github:yurikuzn/jquery-ui-espo#0.2.0",
"version": "git+ssh://git@github.com/yurikuzn/jquery-ui-espo.git#844897483dbf31603b81cadda07b0de5602c231e",
"from": "jquery-ui-espo@github:yurikuzn/jquery-ui-espo#0.2.2",
"requires": {
"grunt": "^1.4.1",
"jquery-ui": "^1.13.0"
+2 -2
View File
@@ -47,7 +47,7 @@
"bootstrap": "^3.4.1",
"bootstrap-colorpicker": "^2.5.2",
"bootstrap-datepicker": "^1.9.0",
"bullbone": "github:yurikuzn/bull#1.1.5",
"bullbone": "github:yurikuzn/bull#1.2.2",
"cronstrue": "^1.114.0",
"cropper": "^0.7.9",
"devbridge-autocomplete": "^1.4.11",
@@ -61,7 +61,7 @@
"handlebars": "^4.7.7",
"jquery": "^3.6.0",
"jquery-textcomplete": "^1.8.5",
"jquery-ui-espo": "github:yurikuzn/jquery-ui-espo#0.2.0",
"jquery-ui-espo": "github:yurikuzn/jquery-ui-espo#0.2.2",
"jquery-ui-touch-punch": "^0.2.3",
"js-base64": "^3.7.2",
"jsbarcode": "^3.11.4",
+15 -7
View File
@@ -24,14 +24,10 @@
},
"exportsTo": {
"anyOf": [
{
"type": "string"
},
{
"enum": ["window", "$.fn"]
}
{"type": "string"},
{"enum": ["window", "$.fn"]}
],
"description": "To where the lib is exported. Use dots to define a path."
"description": "If the lib exposes itself w/o a module, to where the lib is exported. Use dots to define a path."
},
"exportsAs": {
"type": "string",
@@ -40,6 +36,18 @@
"sourceMap": {
"type": "boolean",
"description": "Whether the source map file is available."
},
"expose": {
"type": "boolean",
"description": "To expose to the window global. To be used for backward compatibility reasons."
},
"exposeAs": {
"type": "string",
"description": "A name to expose by. By default, matches the lib name."
},
"amdId": {
"type": "string",
"description": "An AMD ID. If the module uses a named define function, specify that name."
}
}
}