GODSENAL

marked + highlight 번들 사이즈 줄이기

# javascript # react
2019-06-18 · 2

이번에 프로젝트를 하면서 markedhighlight.js를 사용하여 마크다운에 코드 syntax 하이라이팅을 지원하려고 했다.

marked 옵션에 highlight가 있어서 그 안에 이런식으로 넣어줬었다.

import hljs from 'highlight.js';

marked.setOptions({
  highlight: function(code, lang) {
    return hljs.highlight(lang, code).value;
  }
});

문제는 highlight.js를 CommonJs 모듈로 default 임포트 할 경우, 모든 언어를 다 임포트시킨다.

var hljs = require('./highlight.js');

hljs.registerLanguage('1c', require('./languages/1c'));
hljs.registerLanguage('abnf', require('./languages/abnf'));
hljs.registerLanguage('accesslog', require('./languages/accesslog'));
hljs.registerLanguage('actionscript', require('./languages/actionscript'));
hljs.registerLanguage('ada', require('./languages/ada'));
hljs.registerLanguage('angelscript', require('./languages/angelscript'));
hljs.registerLanguage('apache', require('./languages/apache'));
...

이런식으로 되어있다. 이렇게 모든 언어를 다 임포트하면 같이 번들링되어서 번들 사이즈가 굉장히 커진다. 이 중에 사용자가 사용하는 언어는 극히 일부이므로 그 부분만 추가해서 사용하도록 해보자.

highlightjs의 default 모듈은 위와 같은 코드로 모든 언어를 임포트 시키기 때문에, highlight.js/lib/highlight 에서 직접 코어 코드를 가져오도록 하자.

import hljs from 'highlight.js/lib/highlight';
import javascript from 'highlight.js/lib/languages/javascript';
hljs.registerLanguage('javascript', javascript);

// ...

번들러가 Tree Shaking을 해주기 때문에 요렇게 필요한 부분만 임포트해서 번들사이즈를 줄일 수 있다.

그런데, 내가 하고있는 프로젝트는 사용자가 쓸 수 있는 언어가 정해지지 않았기 때문에 위 방법으로는 힘들었다. 그래서 다이나믹 임포트를 이용했다.

먼저, Create React App을 사용하거나 webpack과 babel 설정을 하여서 다이나믹 임포트가 가능한 환경을 만들자.

문제는 highlight.js에서 언어를 추가하려면 위 코드처럼

hljs.registerLanguage('javascript', require('./languages/javascript'));

이런식으로 언어에 해당하는 특정 파일 이름을 알아야 한다. 언어 -> 파일 이름 1:1 매칭되면 좋겠지만, javascript 같은 경우 js로 쓰기도 하므로 언어마다 별명도 다 알아야 한다.

별 수 없다. highlight.js에 현재 로드된 언어의 이름을 주는 Api와 그 이름을 통해 언어의 별명을 주는 Api가 있기 때문에, 일단 모두 임포트 시킨 후 이 Api를 통해서 가져온 정보를 파싱해서 사용했다.

{
  // ...
  "apache": "apache",
  "osascript": "applescript",
  "applescript": "applescript",
  "arcade": "arcade",
  "c": "cpp",
  "cc": "cpp",
  "h": "cpp",
  "c++": "cpp",
  // ...
}

이런식으로 해당하는 별명의 언어 이름을 [key]: value 로 하는 데이터를 만들었다.

import highlightData from './highlight.json';
marked.setOptions({
  highlight: (code, lang, callback) => {
    if (!lang) {
      return code;
    }
    const path = highlightData[lang];
    if (!path) {
      return callback(null, code);
    }
    import(`highlight.js/lib/languages/${path}`).then(
      module => {
        hljs.registerLanguage(path, module.default);
        return callback(null, hljs.highlight(path, code).value);
      },
      err => {
        callback(err, code);
      },
    );
  },
});

그리고 이렇게 markdown 옵션으로 언어를 가져오는 코드를 넣으면 된다. 참고로 marked를 사용할 때 이렇게 callback 을 이용하여 비동기 highlighting을 수행할 경우,

console.log(marked(value)); // Sync

marked(value, (err, text) => {
  console.log(text); // Async
})

이렇게 사용해주자.

» List

Godsenal

안녕하세요. 개발 공부 중인 학생입니다.
개발하고 공부하며 배우는 내용들을 정리하고 있습니다.
의견, 조언 감사합니다.

» Mail» Github
« react-redux의 hook API 미리 써보기
debounce와 throttle »