Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Danielbrzn add frontmatter #5

Open
wants to merge 37 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
3c121f9
Add a new function to build siteData file with page informaiton
danielbrzn Mar 21, 2018
9b81aee
Add front matter parsing only for addressable files
Mar 22, 2018
620ef37
Allow user to specify addressable page names in site.json
danielbrzn Mar 27, 2018
32027f1
Add support for user defined title prefixes
danielbrzn Apr 1, 2018
2e13aa7
Update test site to use frontmatter and addressable array
danielbrzn Apr 1, 2018
ee221cd
Update expectedSite
danielbrzn Apr 3, 2018
baa6c94
Prevent frontmatter from being rendered on the final site
danielbrzn Apr 6, 2018
d5dee86
Update search feature to use siteData.json
danielbrzn Apr 6, 2018
60618f7
Add a new function to build siteData file with page information
danielbrzn Mar 21, 2018
6bd17c8
Add front matter parsing only for addressable files
Mar 22, 2018
f210bd0
Allow user to specify addressable page names in site.json
danielbrzn Mar 27, 2018
6ac41b3
Add support for user defined title prefixes
danielbrzn Apr 1, 2018
b9c58f2
Update expectedSite
danielbrzn Apr 3, 2018
d2f313e
Add ignore for site.json only in default config
Apr 12, 2018
e1efb97
Add live reload support for frontmatter changes
danielbrzn Apr 13, 2018
638d665
Update setup.js
danielbrzn Apr 14, 2018
a1ded1f
Add support for globs in site.json
danielbrzn Apr 15, 2018
e023636
Update test site to use globs
danielbrzn Apr 15, 2018
eb50067
Refactor code
Apr 17, 2018
c17b879
Update user guide
danielbrzn Apr 17, 2018
558976b
Add test for equality of siteData
danielbrzn Apr 17, 2018
db4d15f
Adjust whitespace
acjh Apr 17, 2018
1e5dcf0
Reorder alphabetically
acjh Apr 17, 2018
ed5862d
Refactor test for equality of siteData
acjh Apr 17, 2018
682cef1
Refactor front matter related methods
danielbrzn Apr 19, 2018
a763e3d
Update documentation for site configuration
danielbrzn Apr 19, 2018
ecfe64c
update package-lock
nicholaschuayunzhi Apr 19, 2018
99eb322
print the error
nicholaschuayunzhi Apr 19, 2018
fea0131
Add diff printing
nicholaschuayunzhi Apr 19, 2018
6364a3d
Fix diff
nicholaschuayunzhi Apr 19, 2018
cb96b8b
print stuff
nicholaschuayunzhi Apr 19, 2018
7b37c23
remove cache
nicholaschuayunzhi Apr 19, 2018
b916aec
npm list
nicholaschuayunzhi Apr 19, 2018
255c614
delete yarn
nicholaschuayunzhi Apr 19, 2018
90d7e3e
remove list
nicholaschuayunzhi Apr 19, 2018
e15ce6c
update yarn.lock
nicholaschuayunzhi Apr 19, 2018
eb6fabf
use npm install
nicholaschuayunzhi Apr 19, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ node_js:
- '8'
before_install:
- chmod +x ./test/test_site/test.sh
install:
- npm install
cache:
directories:
- node_modules
sudo: false
sudo: false
16 changes: 9 additions & 7 deletions asset/js/setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,30 +10,32 @@ function setup() {
}

function setupWithSearch(siteData) {
const routeArray = jQuery.map(siteData.pages, object => object.src);
const titleArray = jQuery.map(siteData.pages, object => object.title);
const { typeahead } = VueStrap.components;
const vm = new Vue({
el: '#app',
components: {
typeahead,
},
data() {
const helpers = {
value() { return [this.title].concat(this.keywords).join(' '); },
indexOf(query) { return this.value().indexOf(query); },
toLowerCase() { return this.value().toLowerCase(); },
};
return {
searchData: titleArray,
searchData: siteData.pages.map(page => Object.assign({}, page, helpers)),
titleTemplate: '{{ item.title }}<br><sub>{{ item.keywords }}</sub>',
};
},
methods: {
searchCallback(match) {
const index = titleArray.indexOf(match);
const route = routeArray[index];
window.location.pathname = route.replace('.md', '.html');
window.location.pathname = match.src.replace('.md', '.html');
},
},
});
VueStrap.installEvents(vm);
}

jQuery.getJSON('../../site.json')
jQuery.getJSON('../../siteData.json')
.then(siteData => setupWithSearch(siteData))
.catch(() => setup());
30 changes: 15 additions & 15 deletions asset/js/vue-strap.min.js

Large diffs are not rendered by default.

33 changes: 33 additions & 0 deletions docs/userGuide/contentAuthoring.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,39 @@ Being able to include different markdown file into the current context is anothe

For detailed guide on using `<include>` tag for including contents, read the doc [here](includingContents.html).

### Front Matter

Front matter allows you to specify page parameters such as the title and keywords of the page. These parameters will be used to build a site data file (`siteData.json`) that will contain the index used for the search function.
You can specify front matter at the beginning of your page by using the `<frontmatter>` tag as follows:

```
<frontmatter>
title: Binary Search Tree
keywords: bst, avl, red-black
</frontmatter>
```

Note: A title that is defined in `site.json` for a particular page will take precedence over a title defined in the front matter.

Front matter can also be included from a separate file, just like how content can be included. This makes it easy to simply define all your required front matter in one file, and use it everywhere.

`matterDefinition.md`
```
<seg id="bst">
<frontmatter>
title: Binary Search Tree
keywords: bst, avl, red-black
</frontmatter>
</seg>
```

`index.md`
```
<include src="matterDefinition.md#bst" />
```

This will result in `index.md` having the title `Binary Search Tree` and the specified keywords in order for it to be looked up through the search bar.

<include src="../common/userGuideSections.md" />

</div>
7 changes: 6 additions & 1 deletion docs/userGuide/siteConfiguration.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,14 @@ Let's examine a typical `site.json` file:
```
{
"baseUrl": "",
"titlePrefix": "",
"pages": [
{
"src": "index.md",
"title": "Hello World"
},
{
"glob": "**/index.md"
}
],
"ignore": [
Expand All @@ -33,7 +37,8 @@ Let's examine a typical `site.json` file:
| Variable | Description |
|----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **baseUrl** | The url base when you deploy the website. For example, if you are using Github Pages to host your deployed website, and the resulted page is `https://markbind.github.io/site-demo-cs2103`, then your `baseUrl` would be `/site-demo-cs2103` (relative to your domain, default is ` ` (empty)). Later you could use this variable to help you create top-level navigation link in the document. **You may need to change the `baseUrl` when deploy to different repo.** |
| **pages** | An array of the pages config tells MarkBind all the source files that needs to be rendered. The `src` is the page to be rendered; `title` is the page title for the generated web page. |
| **titlePrefix** | The prefix for all page titles. This prefix will be prepended to all rendered pages. |
| **pages** | An array of the pages config tells MarkBind all the source files that needs to be rendered. The `src` is the page to be rendered; `title` is the page title for the generated web page. Titles specified in this array will take priority over titles specified within the front matter of individual pages. In addition, globs can be used to define pages that will be rendered. Front matter for the pages will be automatically indexed. However, if front matter or `title` is not specified, the page will have a default title that consists only of the `titlePrefix`. |
| **ignore** | Files to be ignored when building the website. By default, MarkBind will copy all the assets into the output folder. The ignore pattern follows the pattern used in [`.gitignore`](https://git-scm.com/docs/gitignore#_pattern_format). You may want to ignore all markdown source files by adding the entry `*.md`, as well as the Git working directory `.git/*`. |
| **deploy** | Settings for the auto Github page deployment. Please refer this [doc](ghpagesDeployment.html) for more details. |

Expand Down
83 changes: 77 additions & 6 deletions lib/Page.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,17 @@ const Promise = require('bluebird');
const logger = require('./util/logger');
const FsUtil = require('./util/fsUtil');
const pathIsInside = require('path-is-inside');
const cheerio = require('cheerio');
const fm = require('fastmatter');

const MarkBind = require('./markbind/lib/parser');

const FRONT_MATTER_FENCE = '---';
const TITLE_PREFIX_SEPARATOR = ' - ';

cheerio.prototype.options.xmlMode = true; // Enable xml mode for self-closing tag
cheerio.prototype.options.decodeEntities = false; // Don't escape HTML entities

function Page(pageConfig) {
this.content = pageConfig.content || '';
this.title = pageConfig.title || '';
Expand All @@ -24,7 +32,10 @@ function Page(pageConfig) {
this.asset = pageConfig.asset;
this.baseUrlMap = pageConfig.baseUrlMap;
this.userDefinedVariablesMap = pageConfig.userDefinedVariablesMap;
this.src = pageConfig.src;
this.titlePrefix = pageConfig.titlePrefix;
this.includedFiles = {};
this.frontMatter = {};
}

/**
Expand Down Expand Up @@ -71,6 +82,56 @@ Page.prototype.collectIncludedFiles = function (dependencies) {
});
};

/**
* Records the front matter into this.frontMatter
* @param includedPage a page with its dependencies included
*/
Page.prototype.collectFrontMatter = function (includedPage) {
const $ = cheerio.load(includedPage);
const frontMatter = $('frontmatter');
if (frontMatter.length) {
// Retrieves the front matter from either the first frontmatter element
// or from a frontmatter element that includes from another file
// The latter case will result in the data being wrapped in a div
const frontMatterData = frontMatter.find('div').length
? frontMatter.find('div')[0].children[0].data
: frontMatter[0].children[0].data;
const frontMatterWrapped = `${FRONT_MATTER_FENCE}\n${frontMatterData}${FRONT_MATTER_FENCE}`;
// Parse front matter data
const parsedData = fm(frontMatterWrapped);
this.frontMatter = parsedData.attributes;
this.frontMatter.src = this.src;
// Title specified in site.json will override title specified in front matter
this.frontMatter.title = (this.title || this.frontMatter.title || '');
if (this.titlePrefix) {
this.frontMatter.title = this.frontMatter.title
? this.titlePrefix + TITLE_PREFIX_SEPARATOR + this.frontMatter.title
: this.titlePrefix;
}
} else {
// Page is addressable but no front matter specified
const formattedTitle = this.titlePrefix
? this.titlePrefix + (this.title ? TITLE_PREFIX_SEPARATOR + this.title : '')
: this.title;
this.frontMatter = {
src: this.src,
title: formattedTitle,
};
}
this.title = this.frontMatter.title;
};

/**
* Removes the front matter from an included page
* @param includedPage a page with its dependencies included
*/
Page.prototype.removeFrontMatter = function (includedPage) {
const $ = cheerio.load(includedPage);
const frontMatter = $('frontmatter');
frontMatter.remove();
return $.html();
};

Page.prototype.generate = function (builtFiles) {
this.includedFiles = {};
this.includedFiles[this.sourcePath] = true;
Expand All @@ -85,6 +146,10 @@ Page.prototype.generate = function (builtFiles) {
};
return new Promise((resolve, reject) => {
markbinder.includeFile(this.sourcePath, fileConfig)
.then((result) => {
this.collectFrontMatter(result);
return this.removeFrontMatter(result);
})
.then(result => markbinder.resolveBaseUrl(result, fileConfig))
.then(result => fs.outputFileAsync(this.tempPath, result))
.then(() => markbinder.renderFile(this.tempPath, fileConfig))
Expand Down Expand Up @@ -159,12 +224,18 @@ Page.prototype.resolveDependency = function (dependency, builtFiles) {
rootPath: this.rootPath,
cwf: file,
})
.then(result => markbinder.resolveBaseUrl(result, {
baseUrlMap: this.baseUrlMap,
rootPath: this.rootPath,
isDynamic: true,
dynamicSource: source,
}))
.then(result => this.removeFrontMatter(result))
.then((result) => {
if (resultPath.includes('UserStories')) {
console.log(result);
}
return markbinder.resolveBaseUrl(result, {
baseUrlMap: this.baseUrlMap,
rootPath: this.rootPath,
isDynamic: true,
dynamicSource: source,
});
})
.then(result => fs.outputFileAsync(tempPath, result))
.then(() => markbinder.renderFile(tempPath, {
baseUrlMap: this.baseUrlMap,
Expand Down
Loading