Skip to content

Instantly share code, notes, and snippets.

@rlugojr
Forked from sebz/grunt-hugo-lunrjs.md
Created March 28, 2017 11:54
Show Gist options
  • Save rlugojr/fea952a528b72efdf327f195dff6b76c to your computer and use it in GitHub Desktop.
Save rlugojr/fea952a528b72efdf327f195dff6b76c to your computer and use it in GitHub Desktop.

Revisions

  1. @sebz sebz revised this gist Apr 14, 2015. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion grunt-hugo-lunrjs.md
    Original file line number Diff line number Diff line change
    @@ -236,7 +236,7 @@ On the client side here is a small usage example:
    });
    }
    // Nothing crazy here, just hook a listener on the input field
    // Nothing crazy here, just hook up a listener on the input field
    function initUI() {
    $results = $("#results");
    $("#search").keyup(function() {
  2. @sebz sebz revised this gist Apr 14, 2015. 1 changed file with 151 additions and 8 deletions.
    159 changes: 151 additions & 8 deletions grunt-hugo-lunrjs.md
    Original file line number Diff line number Diff line change
    @@ -50,14 +50,12 @@ We will work both at buildtime and runtime. With Gruntjs (buildtime), we'll gene

    Lunrjs allows you to define fields to describe your pages (documents in lunrjs terms) that will be used to search and hopefully find stuff. The index file is basically a JSON file corresponding to an array of all the documents (pages) composing the website.

    Here are the fields I chose (and the boost level to increase the page ranking) to describe my pages:
    Here are the fields I chose to describe my pages:

    * `title` <=> page title
    - boost 10
    * `tags` <=> page tags
    - boost 5
    * `content` <=> page content
    * `ref` <=> page URI
    * `title` <=> Frontmatter title or file name
    * `tags` <=> Frontmatter tags or nothing
    * `content` <=> File content
    * `ref` <=> Reworked file path used as absolute URL

    #### Workflow

    @@ -158,8 +156,153 @@ module.exports = function(grunt) {

    ```

    The index file looks like:

    ```js
    [{
    "title": "Page1",
    "href": "/section/page1",
    "content": " This is the cleaned content of 'site/content/section/page1.md' "
    }, {
    "title": "Page2",
    "tags": ["tag1", "tag2", "tag3"],
    "href": "/section/page2",
    "content": " This is the cleaned content of 'site/content/section/page2.md' "
    }, {
    "title": "Page3",
    "href": "/section/page3",
    "content": " This is the cleaned content of 'site/content/section/page3.md' "
    }]
    ```

    Launch the task: `grunt lunr-index`

    ### Use the index

    TBD
    On the client side here is a small usage example:

    ```html
    <!DOCTYPE html>
    <html>

    <head>
    <title>Hugo + Lunrjs = &lt;3 search </title>
    </head>

    <body>
    Search:
    <input id="search" type="text">
    <br> Results:
    <ul id="results">
    </ul>
    <script type="text/javascript" src="https://code.jquery.com/jquery-2.1.3.min.js"></script>
    <script type="text/javascript" src="js/vendor/lunr.min.js"></script>
    <script type="text/javascript">
    var lunrIndex,
    $results,
    pagesIndex;
    // Initialize lunrjs using our generated index file
    function initLunr() {
    // First retrieve the index file
    $.getJSON("js/lunr/PagesIndex.json")
    .done(function(index) {
    pagesIndex = index;
    console.log("index:", pagesIndex);
    // Set up lunrjs by declaring the fields we use
    // Also provide their boost level for the ranking
    lunrIndex = lunr(function() {
    this.field("title", {
    boost: 10
    });
    this.field("tags", {
    boost: 5
    });
    this.field("content");
    // ref is the result item identifier (I chose the page URL)
    this.ref("href");
    });
    // Feed lunr with each file and let lunr actually index them
    pagesIndex.forEach(function(page) {
    lunrIndex.add(page);
    });
    })
    .fail(function(jqxhr, textStatus, error) {
    var err = textStatus + ", " + error;
    console.error("Error getting Hugo index flie:", err);
    });
    }
    // Nothing crazy here, just hook a listener on the input field
    function initUI() {
    $results = $("#results");
    $("#search").keyup(function() {
    $results.empty();
    // Only trigger a search when 2 chars. at least have been provided
    var query = $(this).val();
    if (query.length < 2) {
    return;
    }
    var results = search(query);
    renderResults(results);
    });
    }
    /**
    * Trigger a search in lunr and transform the result
    *
    * @param {String} query
    * @return {Array} results
    */
    function search(query) {
    // Find the item in our index corresponding to the lunr one to have more info
    // Lunr result:
    // {ref: "/section/page1", score: 0.2725657778206127}
    // Our result:
    // {title:"Page1", href:"/section/page1", ...}
    return lunrIndex.search(query).map(function(result) {
    return pagesIndex.filter(function(page) {
    return page.href === result.ref;
    })[0];
    });
    }
    /**
    * Display the 10 first results
    *
    * @param {Array} results to display
    */
    function renderResults(results) {
    if (!results.length) {
    return;
    }
    // Only show the ten first results
    results.slice(0, 10).forEach(function(result) {
    var $result = $("<li>");
    $result.append($("<a>", {
    href: result.href,
    text: "» " + result.title
    }));
    $results.append($result);
    });
    }
    // Let's get started
    initLunr();
    $(document).ready(function() {
    initUI();
    });
    </script>
    </body>

    </html>

    ```
  3. @sebz sebz revised this gist Apr 14, 2015. 1 changed file with 3 additions and 7 deletions.
    10 changes: 3 additions & 7 deletions grunt-hugo-lunrjs.md
    Original file line number Diff line number Diff line change
    @@ -113,15 +113,11 @@ module.exports = function(grunt) {
    var processHTMLFile = function(abspath, filename) {
    var content = grunt.file.read(abspath);
    var pageName = S(filename).chompRight(".html").s;
    var apiHref = S(abspath)
    .chompLeft(CONTENT_PATH_PREFIX)
    .chompRight(filename).s +
    "index.html#" +
    pageName + "/";

    var href = S(abspath)
    .chompLeft(CONTENT_PATH_PREFIX).s;
    return {
    title: pageName,
    href: apiHref,
    href: href,
    content: S(content).trim().stripTags().stripPunctuation().s
    };
    };
  4. @sebz sebz renamed this gist Apr 14, 2015. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  5. @sebz sebz renamed this gist Apr 13, 2015. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  6. @sebz sebz revised this gist Apr 13, 2015. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -162,6 +162,8 @@ module.exports = function(grunt) {

    ```

    Launch the task: `grunt lunr-index`

    ### Use the index

    TBD
  7. @sebz sebz revised this gist Apr 13, 2015. 1 changed file with 99 additions and 3 deletions.
    102 changes: 99 additions & 3 deletions gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -52,20 +52,116 @@ Lunrjs allows you to define fields to describe your pages (documents in lunrjs t

    Here are the fields I chose (and the boost level to increase the page ranking) to describe my pages:

    * `title` <=> age title
    * `title` <=> page title
    - boost 10
    * `tags` <=> page tags
    - boost 5
    * `content` <=> page content
    * `href` <=> page URL
    * `ref` <=> page URI

    #### Workflow

    1. Recursively walk through all files of the `content` folder
    2. Two possibilities
    1. Markdown file
    1. Parse the Frontmatter to extract the `title` and the `tags`
    2.
    2. Parse and clean the content
    2. HTML file
    1. Parse and clean the content
    2. Use the file name as `title`
    3. Use the path file as `ref` (link toward the page)

    #### Show me the code!

    Here is the `Gruntfile.js` file:

    ```js

    var toml = require("toml");
    var S = require("string");

    var CONTENT_PATH_PREFIX = "site/content";

    module.exports = function(grunt) {

    grunt.registerTask("lunr-index", function() {

    grunt.log.writeln("Build pages index");

    var indexPages = function() {
    var pagesIndex = [];
    grunt.file.recurse(CONTENT_PATH_PREFIX, function(abspath, rootdir, subdir, filename) {
    grunt.verbose.writeln("Parse file:",abspath);
    pagesIndex.push(processFile(abspath, filename));
    });

    return pagesIndex;
    };

    var processFile = function(abspath, filename) {
    var pageIndex;

    if (S(filename).endsWith(".html")) {
    pageIndex = processHTMLFile(abspath, filename);
    } else {
    pageIndex = processMDFile(abspath, filename);
    }

    return pageIndex;
    };

    var processHTMLFile = function(abspath, filename) {
    var content = grunt.file.read(abspath);
    var pageName = S(filename).chompRight(".html").s;
    var apiHref = S(abspath)
    .chompLeft(CONTENT_PATH_PREFIX)
    .chompRight(filename).s +
    "index.html#" +
    pageName + "/";

    return {
    title: pageName,
    href: apiHref,
    content: S(content).trim().stripTags().stripPunctuation().s
    };
    };

    var processMDFile = function(abspath, filename) {
    var content = grunt.file.read(abspath);
    var pageIndex;
    // First separate the Front Matter from the content and parse it
    content = content.split("+++");
    var frontMatter;
    try {
    frontMatter = toml.parse(content[1].trim());
    } catch (e) {
    conzole.failed(e.message);
    }

    var href = S(abspath).chompLeft(CONTENT_PATH_PREFIX).chompRight(".md").s;
    // href for index.md files stops at the folder name
    if (filename === "index.md") {
    href = S(abspath).chompLeft(CONTENT_PATH_PREFIX).chompRight(filename).s;
    }

    // Build Lunr index for this page
    pageIndex = {
    title: frontMatter.title,
    tags: frontMatter.tags,
    href: href,
    content: S(content[2]).trim().stripTags().stripPunctuation().s
    };

    return pageIndex;
    };

    grunt.file.write("site/static/js/lunr/PagesIndex.json", JSON.stringify(indexPages()));
    grunt.log.ok("Index built");
    });
    };

    ```

    ### Use the index

    TBD
  8. @sebz sebz revised this gist Apr 13, 2015. 1 changed file with 70 additions and 2 deletions.
    72 changes: 70 additions & 2 deletions gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,71 @@
    > How to implement a custom search for [Hugo](http://gohugo.io) usig [Gruntjs](http://gruntjs.com) and [Lunrjs](http://lunrjs.com)
    > How to implement a custom search for [Hugo](http://gohugo.io) usig [Gruntjs](http://gruntjs.com) and [Lunrjs](http://lunrjs.com).
    ## Requirements

    Install the following tools:

    * [Nodejs](https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager)
    * [Gruntjs](http://gruntjs.com/getting-started)

    ## Setup

    ### Project organization

    Here is my Hugo based website project structure

    ```
    MyWebsite/
    |- site/ <= Hugo project root folder
    |- content/
    |- layout/
    |- static/
    |- js/
    |- lunr/ <= Where we generate the lunr json index file
    |- vendor/
    |- lunrjs.min.js <= lunrjs library
    |- ...
    |- config.yaml
    |- ...
    |- Gruntfile.js <= Where the magic happens
    |- package.json <= Dependencies declaration required to build the index
    |- ...
    ```

    ### Install the Nodejs dependencies

    From the project root folder launch `npm install --save-dev grunt string toml`

    * [string](http://stringjs.com/) <= do almost all the work
    * [toml](https://github.com/BinaryMuse/toml-node)
    - Used to parse the Frontmatter, mine is in TOML... obviously
    - Otherwise you can install [yamljs](https://www.npmjs.com/package/yamljs)

    ## Time to work

    ### The principle

    We will work both at buildtime and runtime. With Gruntjs (buildtime), we'll generate a JSON index file and with a small js script (runtime) initilize and use lunrjs.

    ### Build the Lunr index file

    Lunrjs allows you to define fields to describe your pages (documents in lunrjs terms) that will be used to search and hopefully find stuff. The index file is basically a JSON file corresponding to an array of all the documents (pages) composing the website.

    Here are the fields I chose (and the boost level to increase the page ranking) to describe my pages:

    * `title` <=> age title
    - boost 10
    * `tags` <=> page tags
    - boost 5
    * `content` <=> page content
    * `href` <=> page URL

    #### Workflow

    1. Recursively walk through all files of the `content` folder
    2. Two possibilities
    1. Markdown file
    1. Parse the Frontmatter to extract the `title` and the `tags`
    2.

    #### Show me the code!

    ## Requirements
  9. @sebz sebz created this gist Apr 13, 2015.
    3 changes: 3 additions & 0 deletions gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,3 @@
    > How to implement a custom search for [Hugo](http://gohugo.io) usig [Gruntjs](http://gruntjs.com) and [Lunrjs](http://lunrjs.com)
    ## Requirements