From 6148527e9edb4e1c4b4cd3e81313874f1d7cecdb Mon Sep 17 00:00:00 2001 From: Remek Ambroziak Date: Wed, 29 May 2019 13:04:34 +0200 Subject: [PATCH] Add _meta info with information about insert index --- .gitignore | 1 + CHANGELOG.md | 12 ++++ README.md | 71 ++++++++++++++------- package.json | 4 +- src/add-link/add-link.spec.js | 11 +++- src/add-link/index.mjs | 7 ++- src/add-meta-tag/add-meta-tag.spec.js | 11 +++- src/add-meta-tag/index.mjs | 7 ++- src/add-script/add-script.spec.js | 14 +++-- src/add-script/index.mjs | 8 ++- src/add-style/add-style.spec.js | 15 +++-- src/add-style/index.mjs | 8 ++- src/index.js | 88 +++++++++++++++++++-------- src/index.mjs | 15 +++-- src/index.spec.js | 22 +++++++ src/prop.mjs | 13 ++++ src/set-title/index.mjs | 2 +- src/set-title/set-title.spec.js | 5 +- src/to-html/render-link/index.mjs | 9 ++- src/to-html/render-meta-tag/index.mjs | 7 ++- src/to-html/render-script/index.mjs | 9 ++- src/to-html/render-style/index.mjs | 9 ++- test/test-helpers.mjs | 6 ++ 23 files changed, 260 insertions(+), 94 deletions(-) create mode 100644 src/prop.mjs diff --git a/.gitignore b/.gitignore index eb79dd5..02960a4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules .idea +.vscode diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d94410..be89511 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,16 +1,28 @@ # Changelog + All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] + ### Changed +## [2.0.0] - 2019-05-29 + +### Added + +- Each item now has a `_meta` property with `insertIndex`. When using build-in rendering it's automagically stripped. + ## [1.1.0] - 2018-12-20 + ### Added + - Rendering set tags to HTML with `render()` method. ## [1.0.0] - 2018-12-05 + ### Added + - First version. diff --git a/README.md b/README.md index 405fe30..b409770 100644 --- a/README.md +++ b/README.md @@ -1,76 +1,103 @@ [![Known Vulnerabilities](https://snyk.io/test/github/reod/koa-head/badge.svg?targetFile=package.json)](https://snyk.io/test/github/reod/koa-head?targetFile=package.json) # koa-head + A document head manager middleware for koa. ## Installation + `npm i koa-head` ## TL;DR example -**note:** this package can be used as native ES6 module but has fallback to CommonJS `require`. + +**note:** this package can be used as native ES6 module but has fallback to CommonJS `require`. ```js -import Koa from 'koa'; -import koaHead from 'koa-head'; +import Koa from "koa"; +import koaHead from "koa-head"; const app = new Koa(); app .use(koaHead()) .use(async (ctx, next) => { - ctx.documentHead.setTitle('Title for my webpage'); - ctx.documentHead.addMetaTag({ name: 'twitter:card', content: 'summary_large_image' }); - ctx.documentHead.addLink({ rel: 'canonical', href: 'index.html' }); - ctx.documentHead.addStyle('body { background: aliceblue; }'); + ctx.documentHead.setTitle("Title for my webpage"); + ctx.documentHead.addMetaTag({ + name: "twitter:card", + content: "summary_large_image" + }); + ctx.documentHead.addLink({ rel: "canonical", href: "index.html" }); + ctx.documentHead.addStyle("body { background: aliceblue; }"); ctx.documentHead.addScript("{ fixture: 'test fixture' }"); await next(); }) .use(ctx => { const documentHead = ctx.documentHead.toHTML(); - const userData = { name: 'John Doe' }; + const userData = { name: "John Doe" }; - await ctx.myAwesomeLayoutEngine('user-view', { + await ctx.myAwesomeLayoutEngine("user-view", { documentHead, - userData, - }) + userData + }); }); app.listen(3333); ``` + will make `documentHead` variable to contain: + ```html Title for my webpage - - + + ``` -so you can use it in a place in your layout. +so you can use it in a place in your layout. ## Available methods ### `.setTitle( string | object )` + Set document title. + ### `.addMetaTag( object )` + Add `` tag. + ### `.addLink( object )` + Add `` tag. + ### `.addStyle( string | object )` + Add `"; return html; }); // CONCATENATED MODULE: ./src/to-html/render-script/index.mjs + /* harmony default export */ var render_script = (script => { + const cleanScript = getCleanProp(script); let html = " key !== "jsText").forEach((key, i, all) => { + Object.keys(cleanScript).filter(key => key !== "jsText").forEach((key, i, all) => { if (i === 0) { html += " "; } - html += `${key}="${script[key]}"`; + html += `${key}="${cleanScript[key]}"`; if (i < all.length - 1) { html += " "; } }); html += ">"; - html += script.jsText; + html += cleanScript.jsText; html += ""; return html; }); @@ -243,6 +275,10 @@ function renderGroup(renderItem, config) { const config = { ...src_config, ...opts }; + const state = { + count: -1 // to make first insert with index 0 + + }; return async function (ctx, next) { ctx.state[config.stateNamespace] = {}; ctx.state[config.stateNamespace].title = ""; @@ -251,12 +287,12 @@ function renderGroup(renderItem, config) { ctx.state[config.stateNamespace].styles = []; ctx.state[config.stateNamespace].scripts = []; const middlewareApi = { - setTitle: set_title(config, ctx), - addMetaTag: add_meta_tag(config, ctx), - addLink: add_link(config, ctx), - addStyle: add_style(config, ctx), - addScript: add_script(config, ctx), - toHtml: to_html(config, ctx) + setTitle: set_title(config, state, ctx), + addMetaTag: add_meta_tag(config, state, ctx), + addLink: add_link(config, state, ctx), + addStyle: add_style(config, state, ctx), + addScript: add_script(config, state, ctx), + toHtml: to_html(config, state, ctx) }; ctx[config.ctxNamespace] = middlewareApi; await next(); diff --git a/src/index.mjs b/src/index.mjs index 47b3fbc..2568316 100644 --- a/src/index.mjs +++ b/src/index.mjs @@ -8,6 +8,9 @@ import createToHtml from "./to-html"; export default function(opts) { const config = { ...defaultConfig, ...opts }; + const state = { + count: -1 // to make first insert with index 0 + }; return async function(ctx, next) { ctx.state[config.stateNamespace] = {}; @@ -18,12 +21,12 @@ export default function(opts) { ctx.state[config.stateNamespace].scripts = []; const middlewareApi = { - setTitle: createSetTitle(config, ctx), - addMetaTag: createAddMetaTag(config, ctx), - addLink: createAddLink(config, ctx), - addStyle: createAddStyle(config, ctx), - addScript: createAddScript(config, ctx), - toHtml: createToHtml(config, ctx) + setTitle: createSetTitle(config, state, ctx), + addMetaTag: createAddMetaTag(config, state, ctx), + addLink: createAddLink(config, state, ctx), + addStyle: createAddStyle(config, state, ctx), + addScript: createAddScript(config, state, ctx), + toHtml: createToHtml(config, state, ctx) }; ctx[config.ctxNamespace] = middlewareApi; diff --git a/src/index.spec.js b/src/index.spec.js index aa75eff..c2e5c38 100644 --- a/src/index.spec.js +++ b/src/index.spec.js @@ -32,3 +32,25 @@ test("middleware setup with custom namespacee", async t => { t.assert(ctx.state[customStateNmspc]); t.end(); }); + +test("ordering elements", async t => { + const docHead = koaHead(); + const ctx = createCtx(); + const next = createNext(); + + await docHead(ctx, next); + + ctx.documentHead.addLink({ rel: "a" }); + ctx.documentHead.addMetaTag({ content: "b" }); + ctx.documentHead.addLink({ rel: "a" }); + + const { + state: { documentHead: head } + } = ctx; + + t.assert(head.links[0]._meta.insertIndex === 0); + t.assert(head.metaTags[0]._meta.insertIndex === 1); + t.assert(head.links[1]._meta.insertIndex === 2); + + t.end(); +}); diff --git a/src/prop.mjs b/src/prop.mjs new file mode 100644 index 0000000..4fa97b1 --- /dev/null +++ b/src/prop.mjs @@ -0,0 +1,13 @@ +export const insertProp = (state, prop) => { + state.count = state.count + 1; + const _meta = { insertIndex: state.count }; + + return { ...prop, _meta }; +}; + +export const getCleanProp = prop => { + const fullProp = { ...prop }; + delete fullProp._meta; + + return fullProp; +}; diff --git a/src/set-title/index.mjs b/src/set-title/index.mjs index e6fce82..ac79ec0 100644 --- a/src/set-title/index.mjs +++ b/src/set-title/index.mjs @@ -1,4 +1,4 @@ -export default (config, ctx) => title => { +export default (config, state, ctx) => title => { const simpleTitle = typeof title === "string"; const documentTitle = simpleTitle diff --git a/src/set-title/set-title.spec.js b/src/set-title/set-title.spec.js index dd7dbb4..27f3ab1 100644 --- a/src/set-title/set-title.spec.js +++ b/src/set-title/set-title.spec.js @@ -1,11 +1,12 @@ import createSetTitle from "./index"; import test from "tape"; import config from "./../config"; -import { createCtxWithKoaHead } from "./../../test/test-helpers"; +import { createCtxWithKoaHead, createState } from "./../../test/test-helpers"; test("setTitle", async t => { const ctx = await createCtxWithKoaHead(); - const setTitle = createSetTitle(config, ctx); + const state = createState(); + const setTitle = createSetTitle(config, state, ctx); setTitle("awesome title"); diff --git a/src/to-html/render-link/index.mjs b/src/to-html/render-link/index.mjs index 05d2594..242059d 100644 --- a/src/to-html/render-link/index.mjs +++ b/src/to-html/render-link/index.mjs @@ -1,8 +1,11 @@ -export default tag => { +import { getCleanProp } from "../../prop"; + +export default link => { + const cleanLink = getCleanProp(link); let html = " { - html += `${key}="${tag[key]}" `; + Object.keys(cleanLink).forEach(key => { + html += `${key}="${cleanLink[key]}" `; }); html += "/>"; diff --git a/src/to-html/render-meta-tag/index.mjs b/src/to-html/render-meta-tag/index.mjs index 09b332e..8d25ad8 100644 --- a/src/to-html/render-meta-tag/index.mjs +++ b/src/to-html/render-meta-tag/index.mjs @@ -1,8 +1,11 @@ +import { getCleanProp } from "../../prop"; + export default tag => { + const cleanTag = getCleanProp(tag); let html = " { - html += `${key}="${tag[key]}" `; + Object.keys(cleanTag).forEach(key => { + html += `${key}="${cleanTag[key]}" `; }); html += "/>"; diff --git a/src/to-html/render-script/index.mjs b/src/to-html/render-script/index.mjs index 38da989..b198011 100644 --- a/src/to-html/render-script/index.mjs +++ b/src/to-html/render-script/index.mjs @@ -1,14 +1,17 @@ +import { getCleanProp } from "../../prop"; + export default script => { + const cleanScript = getCleanProp(script); let html = " key !== "jsText") .forEach((key, i, all) => { if (i === 0) { html += " "; } - html += `${key}="${script[key]}"`; + html += `${key}="${cleanScript[key]}"`; if (i < all.length - 1) { html += " "; @@ -16,7 +19,7 @@ export default script => { }); html += ">"; - html += script.jsText; + html += cleanScript.jsText; html += ""; return html; diff --git a/src/to-html/render-style/index.mjs b/src/to-html/render-style/index.mjs index 5913cd7..65c5e49 100644 --- a/src/to-html/render-style/index.mjs +++ b/src/to-html/render-style/index.mjs @@ -1,14 +1,17 @@ +import { getCleanProp } from "../../prop"; + export default style => { + const cleanStyle = getCleanProp(style); let html = " key !== "cssText") .forEach((key, i, all) => { if (i === 0) { html += " "; } - html += `${key}="${style[key]}"`; + html += `${key}="${cleanStyle[key]}"`; if (i < all.length - 1) { html += " "; @@ -16,7 +19,7 @@ export default style => { }); html += ">"; - html += style.cssText; + html += cleanStyle.cssText; html += ""; return html; diff --git a/test/test-helpers.mjs b/test/test-helpers.mjs index b820a88..c9fc8d9 100644 --- a/test/test-helpers.mjs +++ b/test/test-helpers.mjs @@ -19,3 +19,9 @@ export async function createCtxWithKoaHead(koaHeadOptions) { return ctx; } + +export function createState() { + return { + count: -1 + }; +}