[Из песочницы] Node.JS: библиотека для модификации http ответов

habr.png

Некоторое время назад писал сайт с бэкендом на Express/Node.JS. Возникла проблема с минификацией ответов. Нашел много готовых пакетов, но у всех была проблема — не минифицировался html после шаблонов. В итоге принял решение написать свой маленький и родной велосипед — библиотеку web-minify, позволяющую встроить хук перед отправкой клиенту и модифицировать ответ.

Установка пакета

npm i @dmitriym09/web-minify --save


Думаю, самое лучшее описание библиотеки для разработчика — пример кода=)

Пример


web-minify — middleware-функция:

const htmlminify = require('html-minifier').minify;

const csso = require('csso').minify;
const postcss = require('postcss');
const precss = require('precss');
const autoprefixer = require('autoprefixer');

const minify = require('web-minify');

app.use(minify([
  {
    contentType: /css/,
    minify: async (data, req, res) => {
      let resData = (await postcss([precss, autoprefixer]).process(data, { from: undefined })).css;
      
      resData = csso(resData).css;

      return resData;
    }
  }
]));


В данном примере будут перехватываются все ответы с Content-Type, содержащие подстроку «css». Тело ответа обрабатывается с помощью csso, postcss, precss, autoprefixer. В параметре contentType передается String (будет искаться вхождение String.prototype.indexOf ()) или RegExp (RegExp.prototype.test ()). Параметр minify — функция Function (data: String, req: Request, res: Response), должна возвращать String с новым телом или Promise, который в свою очередь разрешается String. При неотловленом исключении клиент получит ответ 500.

Как уже сказал, большинство существующих популярных библиотек с похожим функционалом хорошо минифицирует статические файлы. Однако минификация сгенерированных в коде (например html шаблонизатором) ответов не работает. Одна из проблем — ответ может отправляться частями, а для обработки обычно нужны полные данные. Соответственно нужно перехватывать все отправки пользователю, собирать и уже в конце обрабатывать и отсылать. Это нужно учитывать при использовании web-minify: тот терабайтный файл, который вы хотите отправить клиенту и который попадает под contentType, накапливаться в памяти.

Примеры


Минификация HTML с помощью html-minifier из юнит-тестов

const htmlminify = require('html-minifier').minify;
it('HTML', (done) => {
        const app = createServer([minify([
                {
                        contentType: 'html', 
                        minify: (data) => { 
                                let res = htmlminify(data, {
                                        removeAttributeQuotes: true,
                                        collapseWhitespace: true,
                                        conservativeCollapse: false,
                                        decodeEntities: true,
                                        keepClosingSlash: false,
                                        preserveLineBreaks: false,
                                        preventAttributesEscapin,
                                        processConditionalComments: true,
                                        removeAttributeQuotes: true,
                                        removeComments: true,
                                        trimCustomFragments: true,
                                        useShortDoctype: true
                        });
                                return res;
                        }
                }
        ])], function(req, res) {
                res.setHeader('Content-Type', 'text/html; charset=utf-8');
            res.end(`








Test

Test

`); }); request(app) .get('/') .set('Accept', 'text/html; charset=utf-8') .expect('Content-Type', 'text/html; charset=utf-8') .expect('

Test

Test

') .expect(200) .end(done) });


Модификации JSON и кода ответа с возвратом Promise из юнит-тестов

it('JSON', (done) => {
        const app = createServer([minify([
                {
                        contentType: /json/,
                        minify: (data, req, res) => {
                                return new Promise(function(resolve, reject) {
                                        try {
                                                res.statusCode = 456;
                                                let o = JSON.parse(data);
                                                o.dt = new Date('2018-09-28T11:05:13.492Z') 
                                                resolve(JSON.stringify(o))
                                        }
                                        catch(exc) {
                                                reject(exc)
                                        }
                                })
                                
                        }
                }
        ])], function(req, res) {
                res.setHeader('Content-Type', 'application/json; charset=utf-8');
            res.end(JSON.stringify({a: 12}));
        });

        request(app)
          .get('/')
          .set('Accept', 'applicatio3n/json; charset=utf-8')
          .expect('Content-Type', 'application/json; charset=utf-8')
          .expect('{"a":12,"dt":"2018-09-28T11:05:13.492Z"}')
          .expect(456)
          .end(done)
});


Web-minify доступна на github и в npm под лицензией MIT.

Спасибо за внимание! Критика, предложения и комментарии приветствуются!

© Habrahabr.ru