From f96db0d0426501f5758155bc4ccc413c25fce007 Mon Sep 17 00:00:00 2001 From: Grant Hutchinson Date: Sun, 28 Apr 2019 11:35:10 -0400 Subject: [PATCH] Make front-matter label var optional (#58) * fix: updating to wc-markdown-loader 0.0.7 removing need for label var * fix: removing all front-matter from default template * fix: remove debug output and update to wc-markdown-loader 0.0.8 refactored * docs: update readme for optional front-matter * test: adding custom front-matter override app and tests * test: reverting default template front-matter, updating tests * fix: updating wc-markdown-loader package depend, removing comment from test * fix: updating yarn.lock * test: modify user workspace tests to not use label fm var, add single page for testing override fm var * test: remove custom-fm fixture entirely * test: remove template var from all previous mock-app pages * fix: remove unnecessary code block fencing --- README.md | 89 +++++++++++++------ package.json | 2 +- packages/cli/config/webpack.config.common.js | 10 +-- packages/cli/config/webpack.config.develop.js | 4 +- packages/cli/config/webpack.config.prod.js | 4 +- packages/cli/lib/graph.js | 21 ++++- packages/cli/tasks/build.js | 4 +- packages/cli/tasks/develop.js | 4 +- packages/cli/templates/hello.md | 2 - packages/cli/templates/index.md | 2 - test/cli.spec.js | 48 +++++++--- .../mock-app/src/pages/blog/20190326/index.md | 2 - test/fixtures/mock-app/src/pages/customfm.md | 8 ++ test/fixtures/mock-app/src/pages/hello.md | 2 - test/fixtures/mock-app/src/pages/index.md | 2 - .../mock-app/src/templates/blog-template.js | 17 ++++ yarn.lock | 10 ++- 17 files changed, 164 insertions(+), 67 deletions(-) create mode 100644 test/fixtures/mock-app/src/pages/customfm.md create mode 100644 test/fixtures/mock-app/src/templates/blog-template.js diff --git a/README.md b/README.md index 1363d84c6..9e9d35bfb 100644 --- a/README.md +++ b/README.md @@ -12,11 +12,7 @@ $ greenwood ``` Fun! But naturally you'll want to make your own pages. So create a folder called _src/pages/_ and create a page called _index.md_. -```shell ---- -label: 'hello' ---- - +```md # Helloworld ``` @@ -47,39 +43,77 @@ Your project will generally have a structure like this: > Customize ### Creating A Page -Here's a an example of a page + +Pages should be placed in your `src/pages/` directory. Page filenames will become the page's generated path. e.g. + +Here's a an example of a `src/pages/mypage.md` +```md +### Hello World + +This is an example page built by Greenwood. Make your own in _src/pages_! +``` + +Will accessible at http://localhost:8000/mypage + +You can nest directories in your `src/pages` directory which will also be used for the final URL. + +e.g. a markdown file at `src/pages/myblog/mycategory/index.md` will be accessible at http://localhost:8000/myblog/mycategory + +Another example a markdown file at `src/pages/myblog/mycategory/mypage.md` will be accessible at http://localhost:8000/myblog/mycategory/mypage + +You can also create [custom templates](#front-matter-template) to style and layout each page. As well as [customize the overall app template](#app-template) + +## Advanced Markdown + +You can add front-matter variables to the top, such as label, template, imports, as well as render components and html within each md file. + +### Front-Matter Label + +By default, a randomly generated export name will be created. `label` front-matter variable is completely optional. + +`label` front-matter variable will set the name of the exported md built web component element, within a default page template. + +e.g. example below would output `` with a child of `` element containing the markdown + + ```md --- label: 'hello' -template: 'page' --- - ### Hello World -This is an example page built by Greenwood. Make your own in _src/pages_! +This is an example page built by Greenwood. Make your own in src/pages! + ``` -And a page template -```javascript -import { html, LitElement } from 'lit-element'; - -class index extends LitElement { - render() { - return html` -

Greenwood

-
- This is the home page built by Greenwood. Make your own pages in src/pages/index.js! -
- `; - } -} - -customElements.define('home-page', index); +### Front-Matter Template + +You can also have your md file compiled within a custom page template. By default, each md file is automatically placed in a default(included) `page-template.js` component and exported with a generated/set label name. + +e.g. example below `` (all template element labels are prepended with `eve-`). + +You can add custom templates by specifying the `template` front-matter variable. + +The `template` variable string value will be appended with `-template.js` and is expected to be the an accessibile filename from within your `src/templates` directory. + +e.g. `template: 'guides'` will use a `src/template/guides-template.js` file. + +[See Page Template section](#page-template) below for an example `page-template.js` file. + +```md +--- +label: 'hello' +template: 'guides' +--- +### Hello World + +This is an example page built by Greenwood. Make your own in src/pages! + ``` -## Advanced Markdown +### Front-Matter Render -You can also render custom html such as a custom style or even a component within your markdown page using `imports` in your front-matter variables at top, as well as utilizing the `render` code block e.g. +You can also render custom html such as a custom style or even a component within your markdown page using `imports` in your front-matter variables, as well as utilizing the `render` code block e.g. ````md --- @@ -99,6 +133,7 @@ This is an example page built by Greenwood. Make your own in src/pages! ``` ```` + ## API Here are some of the features and capabiliites of Greenwood. diff --git a/package.json b/package.json index 312b27ffa..618fce207 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "redux": "^4.0.1", "redux-thunk": "^2.3.0", "rimraf": "^2.6.3", - "wc-markdown-loader": "^0.0.6", + "wc-markdown-loader": "^0.0.8", "webpack": "^4.29.6", "webpack-cli": "^3.3.0", "webpack-dev-server": "^3.2.1", diff --git a/packages/cli/config/webpack.config.common.js b/packages/cli/config/webpack.config.common.js index f571976c5..23a413647 100644 --- a/packages/cli/config/webpack.config.common.js +++ b/packages/cli/config/webpack.config.common.js @@ -25,7 +25,7 @@ const mapUserWorkspaceDirectory = (userPath) => { ); }; -module.exports = (context) => { +module.exports = (context, graph) => { // dynamically map all the user's workspace directories for resolution by webpack // this essentially helps us keep watch over changes from the user, and greenwood's build pipeline const mappedUserDirectoriesForWebpack = getUserWorkspaceDirectories(context.userWorkspace).map(mapUserWorkspaceDirectory); @@ -63,10 +63,10 @@ module.exports = (context) => { } }, { test: /\.md$/, - loaders: [ - 'babel-loader', - 'wc-markdown-loader' - ] + loader: 'wc-markdown-loader', + options: { + graph + } }, { test: /\.css$/, loaders: [ diff --git a/packages/cli/config/webpack.config.develop.js b/packages/cli/config/webpack.config.develop.js index f103fe374..31151c93e 100644 --- a/packages/cli/config/webpack.config.develop.js +++ b/packages/cli/config/webpack.config.develop.js @@ -24,8 +24,8 @@ const rebuild = async() => { } }; -module.exports = (context) => { - const configWithContext = commonConfig(context); +module.exports = ({ context, graph }) => { + const configWithContext = commonConfig(context, graph); const publicPath = configWithContext.output.publicPath; return webpackMerge(configWithContext, { diff --git a/packages/cli/config/webpack.config.prod.js b/packages/cli/config/webpack.config.prod.js index 62b6fc448..7aebb5391 100644 --- a/packages/cli/config/webpack.config.prod.js +++ b/packages/cli/config/webpack.config.prod.js @@ -3,8 +3,8 @@ const path = require('path'); const webpackMerge = require('webpack-merge'); const commonConfig = require(path.join(__dirname, '..', './config/webpack.config.common.js')); -module.exports = (context) => { - const configWithContext = commonConfig(context); +module.exports = ({ context, graph }) => { + const configWithContext = commonConfig(context, graph); return webpackMerge(configWithContext, { diff --git a/packages/cli/lib/graph.js b/packages/cli/lib/graph.js index 0e865a534..c98cc1e62 100644 --- a/packages/cli/lib/graph.js +++ b/packages/cli/lib/graph.js @@ -47,12 +47,12 @@ const createGraphFromPages = async (pagesDir) => { // determine if this is an index file, if so set route to '/' let route = fileRoute === '/index' ? '/' : fileRoute; - + // check if additional nested directories if (seperatorIndex > 0) { // get all remaining nested page directories completeNestedPath = subDir.substring(0, seperatorIndex); - + // set route to the nested pages path and file name(without extension) route = completeNestedPath + route; mdFile = `./${completeNestedPath}${fileRoute}.md`; @@ -62,6 +62,9 @@ const createGraphFromPages = async (pagesDir) => { relativeExpectedPath = `'../${fileName}/${fileName}.js'`; } + // generate a random element name + label = label || generateRandomElementLabel(6); + /* * Variable Definitions *---------------------- @@ -73,6 +76,7 @@ const createGraphFromPages = async (pagesDir) => { * fileName: file name without extension/path, so that it can be copied to scratch dir with same name * relativeExpectedPath: relative import path for generated component within a list.js file to later be * imported into app.js root component + * elementLabel: the element name for the generated md page e.g. */ pages.push({ mdFile, label, route, template, filePath, fileName, relativeExpectedPath }); @@ -97,6 +101,19 @@ const createGraphFromPages = async (pagesDir) => { }); }; +const generateRandomElementLabel = (size) => { + + const letters = 'abcedfghijklmnopqrstuvwxyz'; + let short = [], rand = 0; + + for (let n = 0; n < size; n = n + 1) { + rand = Math.floor(Math.random() * 25); + short.push(letters.substr(rand, 1)); + } + + return short.join(''); +}; + module.exports = generateGraph = async (compilation) => { return new Promise(async (resolve, reject) => { diff --git a/packages/cli/tasks/build.js b/packages/cli/tasks/build.js index 896797216..ccd1628dd 100644 --- a/packages/cli/tasks/build.js +++ b/packages/cli/tasks/build.js @@ -18,8 +18,8 @@ module.exports = runProductionBuild = async(compilation) => { }; // eslint-disable-next-line no-unused-vars -const runWebpack = async ({ context }) => { - const webpackConfig = require(path.join(__dirname, '..', './config/webpack.config.prod.js'))(context); +const runWebpack = async (compilation) => { + const webpackConfig = require(path.join(__dirname, '..', './config/webpack.config.prod.js'))(compilation); return new Promise(async (resolve, reject) => { diff --git a/packages/cli/tasks/develop.js b/packages/cli/tasks/develop.js index 7598b8151..39884855d 100644 --- a/packages/cli/tasks/develop.js +++ b/packages/cli/tasks/develop.js @@ -2,11 +2,11 @@ const path = require('path'); const webpack = require('webpack'); const WebpackDevServer = require('webpack-dev-server'); -module.exports = runDevServer = async ({ context }) => { +module.exports = runDevServer = async (compilation) => { return new Promise(async (resolve, reject) => { try { - const webpackConfig = require(path.join(__dirname, '..', './config/webpack.config.develop.js'))(context); + const webpackConfig = require(path.join(__dirname, '..', './config/webpack.config.develop.js'))(compilation); const devServerConfig = webpackConfig.devServer; let compiler = webpack(webpackConfig); diff --git a/packages/cli/templates/hello.md b/packages/cli/templates/hello.md index 0dd4055b3..bc93e10f2 100644 --- a/packages/cli/templates/hello.md +++ b/packages/cli/templates/hello.md @@ -1,9 +1,7 @@ --- -path: '/hello' label: 'hello' template: 'page' --- - ### Hello World This is an example page built by Greenwood. Make your own in _src/pages_! \ No newline at end of file diff --git a/packages/cli/templates/index.md b/packages/cli/templates/index.md index d9cdffcc8..f0176789c 100644 --- a/packages/cli/templates/index.md +++ b/packages/cli/templates/index.md @@ -1,9 +1,7 @@ --- -path: '/' label: 'index' template: 'page' --- - ### Greenwood This is the home page built by Greenwood. Make your own pages in src/pages/index.js! \ No newline at end of file diff --git a/test/cli.spec.js b/test/cli.spec.js index 5b8e67a2b..8c9e9ac2b 100644 --- a/test/cli.spec.js +++ b/test/cli.spec.js @@ -102,6 +102,7 @@ describe('building greenwood with a user workspace w/custom nested pages directo await setup.run(['./packages/cli/index.js', 'build']); blogPageHtmlPath = path.join(CONTEXT.publicDir, 'blog', '20190326', 'index.html'); + customFMPageHtmlPath = path.join(CONTEXT.publicDir, 'customfm', 'index.html'); }); it('should output one JS bundle', async() => { @@ -126,18 +127,50 @@ describe('building greenwood with a user workspace w/custom nested pages directo }); it('should have the expected heading text within the blog page in the blog directory', async() => { - const heading = dom.window.document.querySelector('h3.wc-md-blog').textContent; + const heading = dom.window.document.querySelector('h3').textContent; expect(heading).to.equal(defaultHeading); }); it('should have the expected paragraph text within the blog page in the blog directory', async() => { - let paragraph = dom.window.document.querySelector('p.wc-md-blog').textContent; + let paragraph = dom.window.document.querySelector('p').textContent; expect(paragraph).to.equal(defaultBody); }); }); + describe('a custom front-matter override page directory', () => { + const defaultPageHeading = 'Custom FM Page'; + const defaultPageBody = 'This is a custom fm page built by Greenwood.'; + let dom; + + beforeEach(async() => { + dom = await JSDOM.fromFile(customFMPageHtmlPath); + }); + + it('should contain a customfm folder with an index html file', () => { + expect(fs.existsSync(customFMPageHtmlPath)).to.be.true; + }); + + it('should have the expected heading text within the customfm page in the customfm directory', async() => { + const heading = dom.window.document.querySelector('h3').textContent; + + expect(heading).to.equal(defaultPageHeading); + }); + + it('should have the expected paragraph text within the customfm page in the customfm directory', async() => { + let paragraph = dom.window.document.querySelector('p').textContent; + + expect(paragraph).to.equal(defaultPageBody); + }); + + it('should have the expected blog-template\'s blog-content class', async() => { + let layout = dom.window.document.querySelector('.blog-content'); + + expect(layout).to.not.equal(null); + }); + }); + after(async() => { await fs.remove(CONTEXT.userSrc); await fs.remove(CONTEXT.publicDir); @@ -146,15 +179,10 @@ describe('building greenwood with a user workspace w/custom nested pages directo }); -// // TODO - https://github.com/ProjectEvergreen/greenwood/issues/32 -// // describe('building greenwood with a user workspace w/custom app-template override', () => { - -// // }); - -// // TODO - https://github.com/ProjectEvergreen/greenwood/issues/30 -// // describe('building greenwood with a user workspace w/custom page-template override', () => { +// TODO - https://github.com/ProjectEvergreen/greenwood/issues/32 +// describe('building greenwood with a user workspace w/custom app-template override', () => { -// // }); +// }); describe('building greenwood with error handling for app and page templates', () => { before(async () => { diff --git a/test/fixtures/mock-app/src/pages/blog/20190326/index.md b/test/fixtures/mock-app/src/pages/blog/20190326/index.md index 9b8432f3f..1a5462435 100644 --- a/test/fixtures/mock-app/src/pages/blog/20190326/index.md +++ b/test/fixtures/mock-app/src/pages/blog/20190326/index.md @@ -1,6 +1,4 @@ --- -label: 'blog' -template: 'page' imports: header: '../../../components/header/header.js' CSS: '../../../styles/theme.css' diff --git a/test/fixtures/mock-app/src/pages/customfm.md b/test/fixtures/mock-app/src/pages/customfm.md new file mode 100644 index 000000000..93b9e9c90 --- /dev/null +++ b/test/fixtures/mock-app/src/pages/customfm.md @@ -0,0 +1,8 @@ +--- +label: 'customfm' +template: 'blog' +--- + +### Custom FM Page + +This is a custom fm page built by Greenwood. \ No newline at end of file diff --git a/test/fixtures/mock-app/src/pages/hello.md b/test/fixtures/mock-app/src/pages/hello.md index 3cd0df422..be938bfb3 100644 --- a/test/fixtures/mock-app/src/pages/hello.md +++ b/test/fixtures/mock-app/src/pages/hello.md @@ -1,6 +1,4 @@ --- -label: 'hello' -template: 'page' imports: CSS: '../styles/theme.css' --- diff --git a/test/fixtures/mock-app/src/pages/index.md b/test/fixtures/mock-app/src/pages/index.md index b0e8a01f4..ab29f0c49 100644 --- a/test/fixtures/mock-app/src/pages/index.md +++ b/test/fixtures/mock-app/src/pages/index.md @@ -1,6 +1,4 @@ --- -label: 'index' -template: 'page' imports: CSS: '../styles/theme.css' --- diff --git a/test/fixtures/mock-app/src/templates/blog-template.js b/test/fixtures/mock-app/src/templates/blog-template.js new file mode 100644 index 000000000..3d8c4f5a8 --- /dev/null +++ b/test/fixtures/mock-app/src/templates/blog-template.js @@ -0,0 +1,17 @@ +import { html, LitElement } from 'lit-element'; + +MDIMPORT; + +class PageTemplate extends LitElement { + render() { + return html` +
+
+ +
+
+ `; + } +} + +customElements.define('page-template', PageTemplate); \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index f947e1d02..72fe22602 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9431,16 +9431,18 @@ wbuf@^1.1.0, wbuf@^1.7.3: dependencies: minimalistic-assert "^1.0.0" -wc-markdown-loader@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/wc-markdown-loader/-/wc-markdown-loader-0.0.6.tgz#a6d01f6d46a55e9522e6a240f7392417c4656445" - integrity sha512-ZAJPuHjvzGYaaq2p9u1fCLOiTO0cy9q3pV+knko4+Wfaz1o7WHo8MOcN085f3Bg0399f18Kwb69nRVkmzV3RCg== +wc-markdown-loader@^0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/wc-markdown-loader/-/wc-markdown-loader-0.0.8.tgz#00d0f4f8b5346cd32fa6138dde42f2d859faaea5" + integrity sha512-5iwsUkrmfZdiACt/y9ujwetvXK/k1VtM6U7HshDOIws4NkunC4Cmhae47ViBUqRnnFRinKcO+wzIP/DyBqBuOg== dependencies: camelize "^1.0.0" except "^0.1.3" front-matter "^3.0.1" + loader-utils "^1.2.3" node-prismjs "^0.1.0" remarkable "^1.7.1" + schema-utils "^1.0.0" webidl-conversions@^4.0.2: version "4.0.2"