螺旋管
此工程为创建超媒体处理管道提供了辅助功能和默认执行.
它使用还原器和续作来创建一个简单的处理管道,可以进行HTML,JSON等超媒体的处理前和后处理.
目录
状态
螺旋马克下
螺旋管支持一些 标记下延。 。 。 .
管道解剖
管道由下列主要部分组成:
- 预处理功能.
- 主要响应生成函数.
- 一个可选的包装功能.
- 后处理功能.
- 错误处理函数 .
管道的每个步骤都是处理单个上下文对象,最终可以用来还原http响应.
以下关于上下文的解剖学.
一般情况下,每个支持的内容类型都有一个管线,管线由文件名确定,例如:
html.pipe.js- 创建 HTML 文档text/html内容类型json.pipe.js- 创建 JSON 文档application/json内容类型
建设管道
可以通过创建导出函数的 CommonJS 模块来创建管道构建器 pipe 它接受以下参数并返回管道函数.
这个项目的主要条目为管道建设提供了辅助功能和一些辅助功能,这样就可以建造这样的基本管道:
// the pipeline itself
const pipeline = require("@adobe/hypermedia-pipeline");
module.exports.pipe = function(cont, context, action) {
action.logger.debug("Constructing Custom Pipeline");
return pipeline()
.use(adjustContent)
.use(cont) // execute the continuation function
.use(cleanupContent)
}
在典型的管道中,您将添加额外的处理步骤作为 .use(require('some-module'))。 。 。 .
主要函数
主要功能一般是转换 request 财务报告和审定财务报表 content 属性 context 改为 a response 对象.
在大多数情景中,主要功能都是从HTL,JST,或JSX等诱发语言的模板中编译而来.
通常情况下,文件类型的每个内容变异都有一个模板(并因此有一个主要功能). 内容变化由选择者识别(文件扩展名前的文件名块,如: example.navigation.html 选择者是 navigation) (中文(简体) ). 如果没有提供选择器,模板是管道的默认模板.
可能的模板名称的例子包括:
html.jsx(编为:html.js) – HTML 管道默认.html.navigation.jst(编为:html.navigation.js) 使导航.dropdown.json.js(没有编译) – 创建纯JSON输出.dropdown.html.htl(编为:dropdown.html.js– 使降下部分.
(可选) Wrapper 函数
有时需要以模板特定的方式预先处理上下文. 这种包装功能(为简洁起见,常被称作"Pre-JS")可以使管道的上下文完全转变.
相较于处理请求,内容和响应的管道专用预处理功能,包装器功能的焦点是执行主模板功能所需的业务逻辑. 这使得可以将:
- 演示文稿(主要功能中,常以声明模板表示).
- 商业逻辑(在包装功能中,常以必备代码来表示).
- 内容类型的具体实施(在编审中,以功能代码表示).
简单执行一个包接功能就像这样:
// All wrapper functions must export `pre`
// The functions takes the following arguments:
// - `cont` (the continuation function, i.e. the main template function)
// - `context` (the context of the pipeline)
// - `action` (the action of the pipeline)
module.exports.pre = (cont, context, action) => {
const {request, content, response} = context;
// modifying the context content before invoking the main function
content.hello = 'World';
const modifiedcontext = {request, content, response};
// invoking the main function with the new context. Capturing the response
// context for further modification
const responsecontext = cont(modifiedcontext, action);
// Adding a value to the context response
const modifiedresponse = modifiedcontext.response;
modifiedresponse.hello = 'World';
return Object.assign(modifiedcontext, modifiedresponse);
}
处理前函数
处理前功能意在:
- 解析和处理请求参数.
- 获取并解析请求的内容 .
- 转换请求的内容 .
处理后的职能
处理后的职能旨在:
- 处理和改变反应.
处理错误
在默认状态下, 管道将处理所有正常的函数, 但会跳过 error handlers (单位:千美元).error()) (中文(简体) ). 当管道在 错误状态 正常处理功能暂停,直到管道结束,或 错误状态 已经清除。 期间 错误状态, (中文(简体) ), 错误处理器 将被执行.
输油管执行在 错误状态 若为 context.error 定义。 一个 错误状态 当处理函数抛出例外时,或者当它设置了例外时,可以发生 context.error 对象直接.
示例
new pipeline()
.use(doSomething)
.use(render)
.use(cleanup)
.error(handleError)
.use(done);
如果在上述例子中 doSomething 导致错误,随后, render 财务报告和审定财务报表 cleanup 将不援引。 不过 handleError 会的 若为 handleError 清除错误状态(即设置) context.error = null页:1 done 函数将再次被引用.
如果在上述例子中,没有任何函数造成错误,则 handleError 永远不会被引用.
扩展点
除可在 once 函数,管道创建者可以曝光命名的扩展点. 这些扩展点允许管道的用户在扩展点之前、之后或之后注入额外的功能。 为了使扩展点独立于执行(即函数的名称),管道作者应使用 expose(name) 函数以曝光特定扩展点.
示例
new pipeline()
.use(doSomething).expose('init')
.use(render)
.use(cleanup).expose('cleanup')
.use(done);
在这个例子中,两个扩展点, init 财务报告和审定财务报表 cleanup 已定义。 注意扩展点的名称如何与函数的名称(即. cleanup),但不一定是相同的(即:. init 文选. doSomething) (中文(简体) ).
通用扩展点
建立扩展点是管道作者的责任,但为了实现扩展点标准化,确定了以下共同名称:
fetch用于回收原始内容的管道步骤,即Markdown文档.parse用于描述原始内容并将其转化为诸如Markdown AST等文档结构的管道步骤.meta用于提取内容结构元数据的管道步骤.html用于将Markdown文档变成(HTML)DOM的管道步骤.esi用于扫描 ESI 标记生成的输出并设置适当的信头的管道步骤.
使用扩展点
使用扩展点的最简单方式是扩大 以上描述的折叠函数。而不是仅仅输出一个 pre 函数,可以导出 :
- (单位:千美元)
before对象. - a 一个
after对象. - (单位:千美元)
replace对象.
每一个这些物体都可以有对应为管道所定义的命名扩展点的密钥.
示例
module.exports.before = {
html: (context, action) => {
// will get called before the "html" pipeline step
}
}
module.exports.after = {
fetch: (context, action) => {
// will get called after the "fetch" pipeline step
}
}
module.exports.replace = {
meta: (context, action) => {
// will get called instead of the "meta" pipeline step
}
}
所有正在使用 before 财务报告和审定财务报表 after 扩展点需要遵循所有其他管道功能所遵循的相同接口,即它们可以访问 context 财务报告和审定财务报表 action 他们应该返回一个修改的 context 对象.
使用这些扩展点执行自定义标记下的内容节点和处理404个错误的更复杂的例子可见于 螺旋-克力融合测试。 。 。 .
背景解剖学
存在以下主要属性 :
requestcontentresponseerror
也见 上下文计划。 。 。 .
这个 request 对象
params:请求参数地图.headers: HTTP 头图.
也见 请求计划。 。 。 .
这个 content 对象
body: 未解析的内容正文作为string。 。 。 .mdast: 解析 标记AST。 。 。 .meta: 映射元数据属性,包括:title: 文件标题.intro:平正文字介绍或说明.type:文档的内容类型.image:文档中第一个图像的URL.
document: 一个 DOM 兼容Document代表(HTML)文件见下文) (中文(简体) ).sections[]: 该文件的主要部分,作为强化的MDAST(见下文) (中文(简体) ).html: 作为 HTML 渲染的内容的字符串 .children: HTML 递归内容的顶级元素阵列.
也见 内容计划。 。 。 .
content.document 细节
对于更喜欢使用已渲染的 HTML 而非输入 Markdown AST 的开发者, content.document 提供与 API 兼容的 HTML 的描述 window.document 在浏览器中查找对象.
最常用的用法可能是叫 content.document.innerHTML,它给出了页面的全部 HTML,但其他功能如:
content.document.getElementsByClassNamecontent.document.querySelectorcontent.document.querySelectorAll
也可查阅。 请注意一些职能,如:
content.document.getElementsByClassNamecontent.document.getElementByID
由于默认管道生成的 HTML 不注入类名或ID 属性,所以使用较少.
从 Markdown AST 生成(Virtual) DOM 节点的工具作为公用类提供,以便用于自定义 pre.js 脚本,和 说明如下:。 。 。 .
content.sections 细节
默认管道从Markdown文档中取出段落,使用两个"数学断开",如 *** 或者说 --- 并嵌入了 YAML 块作为区域标记。 如果文档中找不到章节, 则全部 content.mdast 将等于 content.sections[0]。 。 。 .
content.sections 是数组 section 节点,带有 type (竖起)和 children (节点阵)属性. 此外,每个科有一个 types 属性,是一系列衍生内容类型。 Helix项目(和Hypermedia Pipeline)在声明的内容打字上使用隐含打字方式,这意味着作者的任务不是明确声明某节或文档的内容类型,而是让模板解释某节的内容来理解它所处理的内容类型.
这个 types 属性是一组字符串值,根据孩子节点的发生来描述该节的种类。 这样很容易复制 types 输入 class hTML 元素的属性,这样CSS的表达式匹配了章节的类型,就可以轻松地写入. 遵循模式 type 值可以找到:
has-<type>:对于该节中至少发生一次的每一种内容,例如有标题has-only-<type>: 对于只包含单一类型内容的段落, 例如: has- only-imageis-<type-1>-<type-2>-<type3>, (中文(简体) ),is-<type-1>-<type-2>,以及is-<type-1>对于本节中最常见的三类儿童。 例如,一个有标题和描述的画廊is-image-text-heading。您可以使用utils.types。 。 。 .nb-<type>-<occurences>:本节中每种类型的发生次数.
每个部分都有额外的内容衍生元数据属性,特别是:
title:该节中第一个标题的值.intro:本节第一段的价值.image: 一节中第一个图像的URL.meta: 解析了该区段的YAML元数据(作为一个对象).
这个 response 对象
body: 未分解的反应机构string。 。 。 .headers: HTTP 响应头的映射.status: HTTP 状态代码.
也见 响应计划。 。 。 .
这个 error 对象
此对象仅在管道处理过程中出错时设置. 管道中的任何步骤都可能使 error 对象。 后续步骤如果遇到 error 对象.
或者,步骤可以试图处理 error 对象,例如通过生成格式化错误消息并将其放入 response.body。 。 。 .
唯一的已知财产 error 为:
message: 错误消息.
公用事业
生成一个虚拟 DOM utils.vdom
VDOM 是一个转换的帮助类 医生 使用自定义匹配器函数或表达式将下沉标记为 DOM 节点 .
它可用于下列情况:
- 您只需要代表一个
section中显示的文档. - 您已经更改到
content.mdast并想要在 HTML 中反映它们. - 您想要自定义某些 Markdown 元素的 HTML 输出.
开始
在大多数情况下,你可以简单地访问一个从 action.transformer 属性。 该设备将在 parse 步骤并用于 html 步骤,所以注册 after.parse 财务报告和审定财务报表 before.html 是调整生成的 HTML 的理想点.
或者,装入 VDOM 帮助通过 :
const VDOM = require('@adobe/helix-pipeline').utils.vdom;
简单转换
content.document = new VDOM(content.mdast).getDocument();
此替换 content.document 并重新授予Markdown AST代表. 可在更改为 content.mdast 已经做了.
使用时 action.transformer,不需要使用此手册.
content.document = new VDOM(content.sections[0]).getDocument();
这只使用第一节的内容来渲染文档.
匹配节点
Markdown AST 中的节点可以用两种方式匹配: 选择- 声明或使用上游函数.
action.transformer.match('heading', () => '<h1>This text replaces your heading</h1>');
content.document = vdom.getDocument();
每个带有该类型的节点 heading 将变为 <h1>This text replaces your heading</h1>· ;
action.transformer.match(function test(node) {
return node.type === 'heading';
}, () => '<h1>This text replaces your heading</h1>');
content.document = vdom.getDocument();
而不是选择语句,您也可以提供返回的上游函数 true 或者说 false。上面的两个例子将具有相同的行为.
创建 DOM 节点
第二个论点 match 是一个节点生成函数,应当返回以下三个选项之一:
- 1个DOM 节点。 。 。 .
- (单位:千美元)
String包含 HTML 标记.
action.transformer.match('link', (_, node) => {
return {
type: 'element',
tagName: 'a',
properties: {
href: node.url,
rel: 'nofollow'
},
children: [
{
type: 'text',
value: node.children.map(({ value }) => value)
}
]
}
}
以上:注射 rel="nofollow" 使用HTAST.
const h = require('hyperscript');
action.transformer.match('link', (_, node) => h(
'a',
{ href: node.url, rel: 'nofollow' },
node.children.map(({ value }) => value),
);
上方: 使用同样方法 上标 (它创造了DOM元素)明显短.
action.transformer.match('link', (_, node) =>
`<a href="${node.url}" rel="nofollow">$(node.children.map(({ value }) => value)).join('')</a>`;
上层:平面 Strings可以使用ES6中的字符串模板来构建相同的结果.
处理儿童节点
以上例子只是处理终端节点(或几乎终端节点). 在现实中,你需要确保匹配节点的所有子节点也被处理. 为此,处理器函数的签名为您提供了一个 handlechild 函数.
// match all "emphasis" nodes
action.transformer.match('emphasis', (h, node, _, handlechild) => {
// create a new HTML tag <i class="its-not-semantic…">
const i = h(node, 'i', { className: 'its-not-semantic-html-but-i-like-it' });
// make sure all child nodes of the markdown node are processed
node.children.forEach((childnode) => handlechild(h, childnode, node, i));
// return the i HTML element
return i;
});
这个 handlechild 函数调用 :
h: DOM- 节点生成函数.childnode:待处理的儿童节点.node:要处理的节点(通常是当前节点)的母节点.i: DOM节点将是新创建的 DOM节点的新父节点.
推断内容类型 utils.types
除了每一节自动推断的内容类型外, utils.types 页:1 TypeMatcher 工具类,允许将段落内容与简单的表达式语言相匹配,从而丰富 section[].types 数值.
const TypeMatcher = require('@adobe/hypermedia-pipeline').utils.types;
const matcher = new TypeMatcher(content.sections);
matcher.match('^heading', 'starts-with-heading');
content.sections = matcher.process();
在上述例子中,所有设有 heading 因为第一个孩子会得到价值 starts-with-heading 附录 types 阵列. ^heading 是内容表达语言的一个例子,它允许将内容与简单的正则表达式类似语法相匹配.
内容表达式语言
^heading- 第一个元素是heading。 。 。 .paragraph$- 最后一个元素是paragraph。 。 。 .heading image+- 一个heading随后是一个或多个image编号.heading? image- 可选heading随后是1个image。 。 。 .heading paragraph* image- 一个heading编号paragraphs(也没有任何段落),后为image。 。 。 .(paragraph|list)- 一个paragraph或一个list。 。 。 .^heading (image paragraph)+$- 一个heading,然后是双image财务报告和审定财务报表paragraph但至少一个.
检查管道上下文
当运行在非生产时,即运行在 OpenWhisk 动作之外,例如 hlx up,管道倾销启用。 管道倾销使开发人员能够方便地检查 Context 用于调试管道的功能并生成现实的测试案例.
管道处理的每个阶段都会产生一个像 $PWD/logs/debug/context_dump_34161BE5KuR0nuFDp/context-20180902-1418-05.0635-step-2.json 内部 debug 目录。 这些垃圾场将在 node 进程结束, 这样在停止后 hlx up 页:1 debug 目录将再次被清除 。 这个 -step-n 在文件名中表示已登录的管道步骤.
简单的例子可能是这样的:
步骤 0:
{}
步骤1:
{
"request": {}
}
步骤2:
{
"request": {},
"content": {
"body": "---\ntemplate: Medium\n---\n\n# Bill, Welcome to the future\n> Project Helix\n\n## Let's talk about Project Helix\n\n",
"sources": [
"https://raw.githubusercontent.com/trieloff/soupdemo/master/hello.md"
]
}
}
步骤3(仅限):
@@ -1,6 +1,58 @@
{
"content": {
- "body": "Hello World"
+ "body": "Hello World",
+ "mdast": {
+ "type": "root",
+ "children": [
+ {
+ "type": "paragraph",
+ "children": [
+ {
+ "type": "text",
+ "value": "Hello World",
+ "position": {
+ "start": {
+ "line": 1,
+ "column": 1,
+ "offset": 0
+ },
+ "end": {
+ "line": 1,
+ "column": 12,
+ "offset": 11
+ },
+ "indent": []
+ }
+ }
+ ],
+ "position": {
+ "start": {
+ "line": 1,
+ "column": 1,
+ "offset": 0
+ },
+ "end": {
+ "line": 1,
+ "column": 12,
+ "offset": 11
+ },
+ "indent": []
+ }
+ }
+ ],
+ "position": {
+ "start": {
+ "line": 1,
+ "column": 1,
+ "offset": 0
+ },
+ "end": {
+ "line": 1,
+ "column": 12,
+ "offset": 11
+ }
+ }
+ }
},
"request": {}
}
步骤5(仅提及):
@@ -52,7 +52,49 @@
"offset": 11
}
}
- }
+ },
+ "sections": [
+ {
+ "type": "root",
+ "children": [
+ {
+ "type": "paragraph",
+ "children": [
+ {
+ "type": "text",
+ "value": "Hello World",
+ "position": {
+ "start": {
+ "line": 1,
+ "column": 1,
+ "offset": 0
+ },
+ "end": {
+ "line": 1,
+ "column": 12,
+ "offset": 11
+ },
+ "indent": []
+ }
+ }
+ ],
+ "position": {
+ "start": {
+ "line": 1,
+ "column": 1,
+ "offset": 0
+ },
+ "end": {
+ "line": 1,
+ "column": 12,
+ "offset": 11
+ },
+ "indent": []
+ }
+ }
+ ]
+ }
+ ]
},
"request": {}
}
步骤6(仅指):
@@ -92,9 +92,19 @@
"indent": []
}
}
- ]
+ ],
+ "title": "Hello World",
+ "types": [
+ "has-text",
+ "has-only-text"
+ ],
+ "intro": "Hello World",
+ "meta": {}
}
- ]
+ ],
+ "meta": {},
+ "title": "Hello World",
+ "intro": "Hello World"
},
"request": {}
}
步骤9(仅指):
@@ -169,7 +169,11 @@
"search": "",
"hash": ""
}
- }
+ },
+ "html": "<p>Hello World</p>",
+ "children": [
+ "<p>Hello World</p>"
+ ]
},
"request": {}
}
步骤10(仅指):
@@ -175,5 +175,9 @@
"<p>Hello World</p>"
]
},
- "request": {}
+ "request": {},
+ "response": {
+ "status": 201,
+ "body": "<p>Hello World</p>"
+ }
}