Search code examples
liferayosgiliferay-6liferay-7liferay-dxp

Liferay DXP Frontend Hot Deployment


I would like to do a Hot-Deployment of the Front-End-Code (HTML, CSS, JS) of some Portlet so development time can be saved and not the whole Portlet needs to be redeployed.

The Portlet is a .war File.

In the good old Liferay 6.2 this was simply possible by overwriting the static frontend Code in the tomcat/webapps/portlet-name directory. In Liferay DXP this is no longer possible, since the Portlet is not extracted to tomcat/webapps/ anymore.

Is there any possibility for a frontend-Hot-Deploy, so i can change e.g. my .html file on the fly and I don't have to redeploy the whole .war Portlet?


Solution

  • We found an alternative solution to improve our time for frontend development, but it works completly different as it did in the old Liferay 6.2.

    Basically we use an express.js proxy Server, which runs in parallel to Liferay on some different Port. This Proxy Server forwards all the requests to the other running liferay, except the requests for HTML, CSS and JS Files. Those are directly served from the local file System instead. Also an automatic rebuild of the frontend is triggered when a HTML, CSS or JS File is changed and saved.

    This small Proxy-Server consists basically out of those two Files:

    The dev-server.js File:

    const packageConfig = require('../../../package.json');
    const projectName = packageConfig.name;
    
    const config = Object.assign({}, {
        port: 8088,
        liferayUrl: 'http://localhost:8080',
        liferayVersion: 7,
        webpackConfigFile: './webpack.development.config.js',
    }, packageConfig.devServer || {});
    
    const path = require('path');
    const webpack = require('webpack');
    const middleware = require('webpack-dev-middleware');
    const webpackCompiler = webpack(require(config.webpackConfigFile));
    const express = require('express');
    const app = express();
    
    const httpProxy = require('http-proxy');
    const liferayProxy = httpProxy.createProxyServer();
    
    let publicPath = `/o/${projectName}/static/js/`;
    if(config.liferayVersion === 6) {
        publicPath = `/${projectName}/static/js/`
    }
    
    app.use(
        middleware(webpackCompiler, {
            publicPath: `/o/${projectName}/static/js/`
        })
    );
    
    app.all('/*', function(req, res) {
        liferayProxy.web(req, res, {target: config.liferayUrl});
    });
    
    app.listen(config.port, () => console.log('Development server listening on port ' + config.port + '!'));
    

    And the package.json:

    {
      "name": "react-base",
      "version": "1.0.0",
      "license": "NOLICENSE",
      "private": true,
      "scripts": {
        "preinstall": "node ./target/ui-build/build/preInstallHook.js",
        "build-dev": "gulp --gulpfile ./target/ui-build/build/gulpfile.js && webpack --config ./target/ui-build/build/webpack.development.config.js --progress --profile",
        "build": "gulp --gulpfile ./target/ui-build/build/gulpfile.js production && webpack --config ./target/ui-build/build/webpack.production.config.js --bail",
        "lint": "eslint -c ./target/ui-build/build/.eslintrc.js --rulesdir \"node_modules/@myproject/react-component-lib/eslint-rules/\"  \"src/main/react/**/*.js\" ",
        "test": "jest --config=./target/ui-build/build/jest.config.js --rootDir=./ --passWithNoTests",
        "coverage": "jest --config=./target/ui-build/build/jest.config.js --rootDir=./ --passWithNoTests --coverage",
        "stats": "webpack-bundle-analyzer ./target/generated-sources/js/stats.json",
        "start:dev": "node ./target/ui-build/build/dev-server.js"
      },
      "dependencies": {
        "@babel/runtime": "^7.0.0",
        "mobx": "3.1.16",
        "mobx-react": "4.4.3",
        "prop-types": "15.7.2",
        "react": "16.8.4",
        "react-dom": "16.8.4"
      },
      "devDependencies": {
        "autoprefixer": "^9.1.5",
        "babel-core": "^7.0.0-bridge.0",
        "@babel/core": "7.1.0",
        "babel-eslint": "10.0.1",
        "babel-jest": "^23.0.0",
        "babel-loader": "8.0.4",
        "@babel/plugin-proposal-class-properties": "7.1.0",
        "@babel/plugin-proposal-decorators": "7.1.0",
        "@babel/plugin-proposal-object-rest-spread": "7.0.0",
        "@babel/plugin-transform-runtime": "7.1.0",
        "@babel/preset-env": "7.1.0",
        "@babel/preset-react": "7.0.0",
        "css-loader": "1.0.0",
        "enzyme": "3.4.0",
        "enzyme-adapter-react-16": "1.5.0",
        "eslint": "4.19.1",
        "eslint-plugin-jsx-a11y": "6.0.3",
        "eslint-plugin-react": "7.11.1",
        "express": "4.17.1",
        "file-loader": "2.0.0",
        "fs-extra": "7.0.0",
        "gulp": "3.9.1",
        "gulp-concat": "2.6.1",
        "http-proxy": "1.17.0",
        "identity-obj-proxy": "3.0.0",
        "jest": "^23.0.0",
        "jest-cli": "^23.0.0",
        "node-sass": "4.9.3",
        "postcss-loader": "3.0.0",
        "raf": "3.4.1",
        "react-test-renderer": "16.8.4",
        "run-sequence": "1.2.2",
        "sass-loader": "7.1.0",
        "style-loader": "0.23.1",
        "url-loader": "1.1.2",
        "url-search-params-polyfill": "5.0.0",
        "webpack": "4.20.2",
        "webpack-bundle-analyzer": "^3.0.2",
        "webpack-cli": "^3.1.1",
        "webpack-dev-middleware": "3.7.0"
      },
      "sideEffects": [
        "*.css",
        "*.scss"
      ]
    }
    

    The Proxy-Server can be started by calling 'yarn run start:dev', then you can access Liferay over the Proxy-Server via http://localhost:8088