diff --git a/.htaccess b/.htaccess index 2695b31506..d7d9e860f9 100644 --- a/.htaccess +++ b/.htaccess @@ -24,11 +24,6 @@ Options -Indexes # Skip redirect for `client` dir. RewriteRule ^client/ - [L] -# {#dev} - # Skip redirect for `node_modules` dir. Actual only for dev environment. - RewriteRule ^node_modules/ - [L] -# {/dev} - # Store base path. RewriteCond %{REQUEST_URI}::$1 ^(.*?/)(.*)::\2$ RewriteRule ^(.*)$ - [E=BASE:%1] diff --git a/Gruntfile.js b/Gruntfile.js index a2169838cb..c93e1661c5 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -42,7 +42,7 @@ module.exports = grunt => { const bundledDir = 'client/lib/bundled'; - let bundleJsFileList = buildUtils.getBundleLibList(libs).concat(bundledDir + '/espo.js'); + let bundleJsFileList = buildUtils.getPreparedBundleLibList(libs).concat(bundledDir + '/espo.js'); let copyJsFileList = buildUtils.getCopyLibDataList(libs); let minifyLibFileList = copyJsFileList @@ -254,13 +254,18 @@ module.exports = grunt => { let contents = (new Bundler()).bundle(bundleConfig.jsFiles); - if (!fs.existsSync(bundledDir)){ + if (!fs.existsSync(bundledDir)) { fs.mkdirSync(bundledDir); } fs.writeFileSync(bundledDir + '/espo.js', contents, 'utf8'); }); + grunt.registerTask('prepare-bundled-libs', () => { + // Even though `npm ci` runs the same script, 'clean:start' deletes files. + cp.execSync("node js/scripts/prepare-bundled"); + }); + grunt.registerTask('chmod-folders', () => { cp.execSync( "find . -type d -exec chmod 755 {} +", @@ -434,6 +439,7 @@ module.exports = grunt => { 'less', 'cssmin', 'espo-bundle', + 'prepare-bundled-libs', 'uglify:bundle', 'copy:frontendLib', 'uglify:lib', diff --git a/application/Espo/Core/Utils/Client/DevModeJsFileListProvider.php b/application/Espo/Core/Utils/Client/DevModeJsFileListProvider.php index 1fda83d412..bd3f4b426d 100644 --- a/application/Espo/Core/Utils/Client/DevModeJsFileListProvider.php +++ b/application/Espo/Core/Utils/Client/DevModeJsFileListProvider.php @@ -64,13 +64,16 @@ class DevModeJsFileListProvider if ($files !== null) { $list = array_merge( $list, - $this->getLibFileListFromItems($files) + array_map( + fn($item) => self::prepareBundleLibFilePath($item), + $this->getLibFileListFromItems($files) + ) ); continue; } - $list[] = $item->src; + $list[] = self::prepareBundleLibFilePath($item->src); } return $list; @@ -90,4 +93,11 @@ class DevModeJsFileListProvider return $list; } + + private function prepareBundleLibFilePath(string $path): string + { + $arr = explode('/', $path); + + return 'client/lib/bundled/' . array_slice($arr, -1)[0]; + } } diff --git a/js/build-utils.js b/js/build-utils.js index 46d2c3c63c..bcaf197e82 100644 --- a/js/build-utils.js +++ b/js/build-utils.js @@ -52,6 +52,11 @@ let BuildUtils = { return list; }, + getPreparedBundleLibList: function (libs) { + return BuildUtils.getBundleLibList(libs) + .map(file => 'client/lib/bundled/' + file.split('/').slice(-1)); + }, + getCopyLibDataList: function (libs) { let list = []; diff --git a/js/diff.js b/js/diff.js index 392c27c332..e0929db22d 100644 --- a/js/diff.js +++ b/js/diff.js @@ -511,6 +511,7 @@ class Diff } let libOldDataList = []; + let bundledOldDataList = []; if (~this._getVersionAllFileList(versionFrom).indexOf('frontend/libs.json')) { libOldDataList = JSON @@ -519,14 +520,24 @@ class Diff ) .filter(item => !item.bundle); + bundledOldDataList = JSON + .parse( + cp.execSync("git show " + commitHash + ":frontend/libs.json").toString() || '[]' + ) + .filter(item => item.bundle); } let libNewDataList = require(this.espoPath + '/frontend/libs.json') .filter(item => !item.bundle); + let bundledNewDataList = require(this.espoPath + '/frontend/libs.json') + .filter(item => item.bundle); + let resolveItemDest = item => item.dest || 'client/lib/' + item.src.split('/').pop(); + let resolveBundledItemDest = item => 'client/lib/bundled/' + item.src.split('/').pop(); + let resolveItemName = item => { if (item.name) { return item.name; @@ -548,18 +559,26 @@ class Diff let changedLibList = []; let currentLibList = []; - let toMinifyOldMap = {}; + let changedBundledList = []; + let currentBundledList = []; + let toMinifyOldMap = {}; let libOldDataMap = {}; + let bundledOldDataMap = {}; libOldDataList.forEach(item => { let name = resolveItemName(item); toMinifyOldMap[name] = item.minify || false; - libOldDataMap[name] = item; }); + bundledOldDataList.forEach(item => { + let name = resolveItemName(item); + + bundledOldDataMap[name] = item; + }) + libNewDataList.forEach(item => { let name = resolveItemName(item); @@ -653,6 +672,72 @@ class Diff } }); + bundledNewDataList.forEach(item => { + let name = resolveItemName(item); + + if (!depsNew[name]) { + throw new Error("Not installed lib '" + name + "' `frontend/libs.json`."); + } + + currentBundledList.push(name); + + let isAdded = !(name in depsOld); + + let versionNew = depsNew[name].version || null; + let versionOld = (depsOld[name] || {}).version || null; + + let isDefsChanged = libOldDataMap[name] ? + JSON.stringify(item) !== JSON.stringify(libOldDataMap[name]) : + false; + + if ( + !isAdded && + versionNew === versionOld && + !isDefsChanged + ) { + return; + } + + changedBundledList.push(name); + + if (item.files) { + item.files.forEach(item => + data.filesToCopy.push(resolveBundledItemDest(item)) + ); + + return; + } + + data.filesToCopy.push(resolveBundledItemDest(item)); + }); + + bundledOldDataList.forEach(item => { + let name = resolveItemName(item); + + let toRemove = false; + + if ( + ~changedBundledList.indexOf(name) || + !~currentBundledList.indexOf(name) + ) { + toRemove = true; + } + + if (!toRemove) { + return; + } + + if (item.files) { + item.files.forEach(item => + data.filesToDelete.push(resolveBundledItemDest(item)) + ); + + return; + } + + data.filesToDelete.push(resolveBundledItemDest(item)); + }); + return data; } @@ -827,13 +912,11 @@ function deleteDirRecursively(path) { if (fs.existsSync(path) && fs.lstatSync(path).isFile()) { fs.unlinkSync(path); - - return; } } function execute(command, callback) { exec(command, (error, stdout, stderr) => callback(stdout)); -}; +} module.exports = Diff; diff --git a/js/scripts/prepare-bundled.js b/js/scripts/prepare-bundled.js new file mode 100644 index 0000000000..2b67025d3e --- /dev/null +++ b/js/scripts/prepare-bundled.js @@ -0,0 +1,56 @@ +/************************************************************************ + * This file is part of EspoCRM. + * + * EspoCRM - Open Source CRM application. + * Copyright (C) 2014-2022 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko + * Website: https://www.espocrm.com + * + * EspoCRM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * EspoCRM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with EspoCRM. If not, see http://www.gnu.org/licenses/. + * + * The interactive user interfaces in modified source and object code versions + * of this program must display Appropriate Legal Notices, as required under + * Section 5 of the GNU General Public License version 3. + * + * In accordance with Section 7(b) of the GNU General Public License version 3, + * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. + ************************************************************************/ + +const fs = require('fs'); +const buildUtils = require('../build-utils'); + +const libs = require('./../../frontend/libs.json'); + +const libDir = './client/lib'; +const bundledDir = './client/lib/bundled'; + +if (!fs.existsSync(libDir)) { + fs.mkdirSync(libDir); +} + +if (!fs.existsSync(bundledDir)) { + fs.mkdirSync(bundledDir); +} + +fs.readdirSync(bundledDir) + .filter(file => file !== 'espo.js') + .forEach(file => fs.unlinkSync(bundledDir + '/' + file)); + +/** @var {string[]} */ +const libSrcList = buildUtils.getBundleLibList(libs); + +libSrcList.forEach(src => { + let dest = bundledDir + '/' + src.split('/').slice(-1); + + fs.copyFileSync(src, dest); +}); diff --git a/package.json b/package.json index 53fd7593de..f718898cd7 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "test": "grunt run-tests", "test-unit": "grunt run-unit-tests", "test-integration": "grunt run-integration-tests", - "postinstall": "node js/scripts/postinstall-cleanup" + "postinstall": "node js/scripts/postinstall-cleanup && node js/scripts/prepare-bundled" }, "author": "Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko", "license": "GPL-3.0",