hast-util-from-parse5
Advanced tools
Comparing version 7.1.0 to 7.1.1
export {fromParse5} from './lib/index.js' | ||
export type Options = import('./lib/index.js').Options | ||
export type Space = import('./lib/index.js').Space |
/** | ||
* @typedef {import('./lib/index.js').Options} Options | ||
* @typedef {import('./lib/index.js').Space} Space | ||
*/ | ||
export {fromParse5} from './lib/index.js' |
/** | ||
* Transform Parse5’s AST to a hast tree. | ||
* Transform a `parse5` AST to hast. | ||
* | ||
* @param {P5Node} ast | ||
* @param {Options|VFile} [options] | ||
* @param {P5Node} tree | ||
* `parse5` tree to transform. | ||
* @param {Options | VFile | null | undefined} [options] | ||
* Configuration. | ||
* @returns {Node} | ||
* hast tree. | ||
*/ | ||
export function fromParse5( | ||
ast: P5Node, | ||
options?: import('vfile').VFile | Options | undefined | ||
tree: P5Node, | ||
options?: Options | VFile | null | undefined | ||
): Node | ||
@@ -15,52 +19,67 @@ export type VFile = import('vfile').VFile | ||
export type Point = import('unist').Point | ||
export type Parent = import('hast').Parent | ||
export type Element = import('hast').Element | ||
export type Root = import('hast').Root | ||
export type Text = import('hast').Text | ||
export type Comment = import('hast').Comment | ||
export type Doctype = import('hast').DocType | ||
export type Child = Parent['children'][number] | ||
export type ElementChild = Element['children'][number] | ||
export type Node = Child | Root | ||
export type P5Document = import('parse5').Document | ||
export type P5Doctype = import('parse5').DocumentType | ||
export type P5Comment = import('parse5').CommentNode | ||
export type P5Text = import('parse5').TextNode | ||
export type P5Element = import('parse5').Element | ||
export type P5ElementLocation = import('parse5').ElementLocation | ||
export type P5Location = import('parse5').Location | ||
export type P5Attribute = import('parse5').Attribute | ||
export type P5Node = import('parse5').Node | ||
export type Content = import('hast').Content | ||
export type DefaultTreeAdapterMap = import('parse5').DefaultTreeAdapterMap | ||
export type P5ElementLocation = import('parse5').Token.ElementLocation | ||
export type P5Location = import('parse5').Token.Location | ||
export type Node = Content | Root | ||
export type P5Document = DefaultTreeAdapterMap['document'] | ||
export type P5DocumentFragment = DefaultTreeAdapterMap['documentFragment'] | ||
export type P5DocumentType = DefaultTreeAdapterMap['documentType'] | ||
export type P5Comment = DefaultTreeAdapterMap['commentNode'] | ||
export type P5Text = DefaultTreeAdapterMap['textNode'] | ||
export type P5Element = DefaultTreeAdapterMap['element'] | ||
export type P5Node = DefaultTreeAdapterMap['node'] | ||
export type P5Template = DefaultTreeAdapterMap['template'] | ||
/** | ||
* Namespace. | ||
*/ | ||
export type Space = 'html' | 'svg' | ||
export type Handler = ( | ||
ctx: Context, | ||
node: P5Node, | ||
children?: | ||
| ( | ||
| import('hast').Element | ||
| import('hast').Text | ||
| import('hast').Comment | ||
| import('hast').DocType | ||
)[] | ||
| undefined | ||
) => Node | ||
/** | ||
* Configuration. | ||
*/ | ||
export type Options = { | ||
/** | ||
* Whether the root of the tree is in the `'html'` or `'svg'` space. If an element in with the SVG namespace is found in `ast`, `fromParse5` automatically switches to the SVG space when entering the element, and switches back when leaving | ||
* Which space the document is in. | ||
* | ||
* When an `<svg>` element is found in the HTML space, this package already | ||
* automatically switches to and from the SVG space when entering and exiting | ||
* it. | ||
*/ | ||
space?: Space | undefined | ||
space?: Space | null | undefined | ||
/** | ||
* `VFile`, used to add positional information to nodes. If given, the file should have the original HTML source as its contents | ||
* File used to add positional info to nodes. | ||
* | ||
* If given, the file should represent the original HTML source. | ||
*/ | ||
file?: import('vfile').VFile | undefined | ||
file?: VFile | null | undefined | ||
/** | ||
* Whether to add extra positional information about starting tags, closing tags, and attributes to elements. Note: not used without `file` | ||
* Whether to add extra positional info about starting tags, closing tags, | ||
* and attributes to elements. | ||
* | ||
* > 👉 **Note**: only used when `file` is given. | ||
*/ | ||
verbose?: boolean | undefined | ||
} | ||
export type Context = { | ||
/** | ||
* Info passed around about the current state. | ||
*/ | ||
export type State = { | ||
/** | ||
* Current schema. | ||
*/ | ||
schema: Schema | ||
/** | ||
* Corresponding file. | ||
*/ | ||
file: VFile | undefined | ||
/** | ||
* Add extra positional info. | ||
*/ | ||
verbose: boolean | undefined | ||
/** | ||
* Whether location info was found. | ||
*/ | ||
location: boolean | ||
} |
381
lib/index.js
@@ -6,39 +6,52 @@ /** | ||
* @typedef {import('unist').Point} Point | ||
* @typedef {import('hast').Parent} Parent | ||
* @typedef {import('hast').Element} Element | ||
* @typedef {import('hast').Root} Root | ||
* @typedef {import('hast').Text} Text | ||
* @typedef {import('hast').Comment} Comment | ||
* @typedef {import('hast').DocType} Doctype | ||
* @typedef {Parent['children'][number]} Child | ||
* @typedef {Element['children'][number]} ElementChild | ||
* @typedef {Child|Root} Node | ||
* @typedef {import('parse5').Document} P5Document | ||
* @typedef {import('parse5').DocumentType} P5Doctype | ||
* @typedef {import('parse5').CommentNode} P5Comment | ||
* @typedef {import('parse5').TextNode} P5Text | ||
* @typedef {import('parse5').Element} P5Element | ||
* @typedef {import('parse5').ElementLocation} P5ElementLocation | ||
* @typedef {import('parse5').Location} P5Location | ||
* @typedef {import('parse5').Attribute} P5Attribute | ||
* @typedef {import('parse5').Node} P5Node | ||
* @typedef {import('hast').Content} Content | ||
* @typedef {import('parse5').DefaultTreeAdapterMap} DefaultTreeAdapterMap | ||
* @typedef {import('parse5').Token.ElementLocation} P5ElementLocation | ||
* @typedef {import('parse5').Token.Location} P5Location | ||
*/ | ||
/** | ||
* @typedef {Content | Root} Node | ||
* @typedef {DefaultTreeAdapterMap['document']} P5Document | ||
* @typedef {DefaultTreeAdapterMap['documentFragment']} P5DocumentFragment | ||
* @typedef {DefaultTreeAdapterMap['documentType']} P5DocumentType | ||
* @typedef {DefaultTreeAdapterMap['commentNode']} P5Comment | ||
* @typedef {DefaultTreeAdapterMap['textNode']} P5Text | ||
* @typedef {DefaultTreeAdapterMap['element']} P5Element | ||
* @typedef {DefaultTreeAdapterMap['node']} P5Node | ||
* @typedef {DefaultTreeAdapterMap['template']} P5Template | ||
* | ||
* @typedef {'html'|'svg'} Space | ||
* @typedef {'html' | 'svg'} Space | ||
* Namespace. | ||
* | ||
* @callback Handler | ||
* @param {Context} ctx | ||
* @param {P5Node} node | ||
* @param {Array.<Child>} [children] | ||
* @returns {Node} | ||
* | ||
* @typedef Options | ||
* @property {Space} [space='html'] Whether the root of the tree is in the `'html'` or `'svg'` space. If an element in with the SVG namespace is found in `ast`, `fromParse5` automatically switches to the SVG space when entering the element, and switches back when leaving | ||
* @property {VFile} [file] `VFile`, used to add positional information to nodes. If given, the file should have the original HTML source as its contents | ||
* @property {boolean} [verbose=false] Whether to add extra positional information about starting tags, closing tags, and attributes to elements. Note: not used without `file` | ||
* Configuration. | ||
* @property {Space | null | undefined} [space='html'] | ||
* Which space the document is in. | ||
* | ||
* @typedef Context | ||
* When an `<svg>` element is found in the HTML space, this package already | ||
* automatically switches to and from the SVG space when entering and exiting | ||
* it. | ||
* @property {VFile | null | undefined} [file] | ||
* File used to add positional info to nodes. | ||
* | ||
* If given, the file should represent the original HTML source. | ||
* @property {boolean} [verbose=false] | ||
* Whether to add extra positional info about starting tags, closing tags, | ||
* and attributes to elements. | ||
* | ||
* > 👉 **Note**: only used when `file` is given. | ||
* | ||
* @typedef State | ||
* Info passed around about the current state. | ||
* @property {Schema} schema | ||
* @property {VFile|undefined} file | ||
* @property {boolean|undefined} verbose | ||
* Current schema. | ||
* @property {VFile | undefined} file | ||
* Corresponding file. | ||
* @property {boolean | undefined} verbose | ||
* Add extra positional info. | ||
* @property {boolean} location | ||
* Whether location info was found. | ||
*/ | ||
@@ -53,32 +66,28 @@ | ||
// Handlers. | ||
const map = { | ||
'#document': root, | ||
'#document-fragment': root, | ||
'#text': text, | ||
'#comment': comment, | ||
'#documentType': doctype | ||
} | ||
/** | ||
* Transform Parse5’s AST to a hast tree. | ||
* Transform a `parse5` AST to hast. | ||
* | ||
* @param {P5Node} ast | ||
* @param {Options|VFile} [options] | ||
* @param {P5Node} tree | ||
* `parse5` tree to transform. | ||
* @param {Options | VFile | null | undefined} [options] | ||
* Configuration. | ||
* @returns {Node} | ||
* hast tree. | ||
*/ | ||
export function fromParse5(ast, options = {}) { | ||
export function fromParse5(tree, options) { | ||
const options_ = options || {} | ||
/** @type {Options} */ | ||
let settings | ||
/** @type {VFile|undefined} */ | ||
/** @type {VFile | undefined} */ | ||
let file | ||
if (isFile(options)) { | ||
file = options | ||
if (isFile(options_)) { | ||
file = options_ | ||
settings = {} | ||
} else { | ||
file = options.file | ||
settings = options | ||
file = options_.file || undefined | ||
settings = options_ | ||
} | ||
return transform( | ||
return one( | ||
{ | ||
@@ -90,3 +99,3 @@ schema: settings.space === 'svg' ? svg : html, | ||
}, | ||
ast | ||
tree | ||
) | ||
@@ -96,40 +105,68 @@ } | ||
/** | ||
* Transform children. | ||
* Transform a node. | ||
* | ||
* @param {Context} ctx | ||
* @param {P5Node} ast | ||
* @param {State} state | ||
* Info passed around about the current state. | ||
* @param {P5Node} node | ||
* p5 node. | ||
* @returns {Node} | ||
* hast node. | ||
*/ | ||
function transform(ctx, ast) { | ||
const schema = ctx.schema | ||
/** @type {Handler} */ | ||
// @ts-expect-error: index is fine. | ||
const fn = own.call(map, ast.nodeName) ? map[ast.nodeName] : element | ||
/** @type {Array.<Child>|undefined} */ | ||
let children | ||
function one(state, node) { | ||
/** @type {Node} */ | ||
let result | ||
// Element. | ||
if ('tagName' in ast) { | ||
ctx.schema = ast.namespaceURI === webNamespaces.svg ? svg : html | ||
} | ||
switch (node.nodeName) { | ||
case '#comment': { | ||
const reference = /** @type {P5Comment} */ (node) | ||
result = {type: 'comment', value: reference.data} | ||
patch(state, reference, result) | ||
return result | ||
} | ||
if ('childNodes' in ast) { | ||
children = nodes(ctx, ast.childNodes) | ||
} | ||
case '#document': | ||
case '#document-fragment': { | ||
const reference = /** @type {P5Document | P5DocumentFragment} */ (node) | ||
const quirksMode = | ||
'mode' in reference | ||
? reference.mode === 'quirks' || reference.mode === 'limited-quirks' | ||
: false | ||
const result = fn(ctx, ast, children) | ||
result = { | ||
type: 'root', | ||
children: all(state, node.childNodes), | ||
data: {quirksMode} | ||
} | ||
if ('sourceCodeLocation' in ast && ast.sourceCodeLocation && ctx.file) { | ||
// @ts-expect-error It’s fine. | ||
const position = createLocation(ctx, result, ast.sourceCodeLocation) | ||
if (state.file && state.location) { | ||
const doc = String(state.file) | ||
const loc = location(doc) | ||
result.position = {start: loc.toPoint(0), end: loc.toPoint(doc.length)} | ||
} | ||
if (position) { | ||
ctx.location = true | ||
result.position = position | ||
return result | ||
} | ||
} | ||
ctx.schema = schema | ||
case '#documentType': { | ||
const reference = /** @type {P5DocumentType} */ (node) | ||
// @ts-expect-error Types are out of date. | ||
result = {type: 'doctype'} | ||
patch(state, reference, result) | ||
return result | ||
} | ||
return result | ||
case '#text': { | ||
const reference = /** @type {P5Text} */ (node) | ||
result = {type: 'text', value: reference.value} | ||
patch(state, reference, result) | ||
return result | ||
} | ||
// Element. | ||
default: { | ||
const reference = /** @type {P5Element} */ (node) | ||
result = element(state, reference) | ||
return result | ||
} | ||
} | ||
} | ||
@@ -140,14 +177,17 @@ | ||
* | ||
* @param {Context} ctx | ||
* @param {Array.<P5Node>} children | ||
* @returns {Array.<Child>} | ||
* @param {State} state | ||
* Info passed around about the current state. | ||
* @param {Array<P5Node>} nodes | ||
* Nodes. | ||
* @returns {Array<Content>} | ||
* hast nodes. | ||
*/ | ||
function nodes(ctx, children) { | ||
function all(state, nodes) { | ||
let index = -1 | ||
/** @type {Array.<Child>} */ | ||
/** @type {Array<Content>} */ | ||
const result = [] | ||
while (++index < children.length) { | ||
// @ts-expect-error Assume no roots in children. | ||
result[index] = transform(ctx, children[index]) | ||
while (++index < nodes.length) { | ||
// @ts-expect-error Assume no roots in `nodes`. | ||
result[index] = one(state, nodes[index]) | ||
} | ||
@@ -159,79 +199,23 @@ | ||
/** | ||
* Transform a document. | ||
* Stores `ast.quirksMode` in `node.data.quirksMode`. | ||
* Transform an element. | ||
* | ||
* @type {Handler} | ||
* @param {P5Document} ast | ||
* @param {Array.<Child>} children | ||
* @returns {Root} | ||
* @param {State} state | ||
* Info passed around about the current state. | ||
* @param {P5Element} node | ||
* `parse5` node to transform. | ||
* @returns {Element} | ||
* hast node. | ||
*/ | ||
function root(ctx, ast, children) { | ||
/** @type {Root} */ | ||
const result = { | ||
type: 'root', | ||
children, | ||
data: {quirksMode: ast.mode === 'quirks' || ast.mode === 'limited-quirks'} | ||
} | ||
function element(state, node) { | ||
const schema = state.schema | ||
if (ctx.file && ctx.location) { | ||
const doc = String(ctx.file) | ||
const loc = location(doc) | ||
result.position = { | ||
start: loc.toPoint(0), | ||
end: loc.toPoint(doc.length) | ||
} | ||
} | ||
state.schema = node.namespaceURI === webNamespaces.svg ? svg : html | ||
return result | ||
} | ||
/** | ||
* Transform a doctype. | ||
* | ||
* @type {Handler} | ||
* @returns {Doctype} | ||
*/ | ||
function doctype() { | ||
// @ts-expect-error Types are out of date. | ||
return {type: 'doctype'} | ||
} | ||
/** | ||
* Transform a text. | ||
* | ||
* @type {Handler} | ||
* @param {P5Text} ast | ||
* @returns {Text} | ||
*/ | ||
function text(_, ast) { | ||
return {type: 'text', value: ast.value} | ||
} | ||
/** | ||
* Transform a comment. | ||
* | ||
* @type {Handler} | ||
* @param {P5Comment} ast | ||
* @returns {Comment} | ||
*/ | ||
function comment(_, ast) { | ||
return {type: 'comment', value: ast.data} | ||
} | ||
/** | ||
* Transform an element. | ||
* | ||
* @type {Handler} | ||
* @param {P5Element} ast | ||
* @param {Array.<ElementChild>} children | ||
* @returns {Element} | ||
*/ | ||
function element(ctx, ast, children) { | ||
const fn = ctx.schema.space === 'svg' ? s : h | ||
// Props. | ||
let index = -1 | ||
/** @type {Object.<string, string>} */ | ||
/** @type {Record<string, string>} */ | ||
const props = {} | ||
while (++index < ast.attrs.length) { | ||
const attribute = ast.attrs[index] | ||
while (++index < node.attrs.length) { | ||
const attribute = node.attrs[index] | ||
props[(attribute.prefix ? attribute.prefix + ':' : '') + attribute.name] = | ||
@@ -241,6 +225,11 @@ attribute.value | ||
const result = fn(ast.tagName, props, children) | ||
// Build. | ||
const fn = state.schema.space === 'svg' ? s : h | ||
const result = fn(node.tagName, props, all(state, node.childNodes)) | ||
patch(state, node, result) | ||
if (result.tagName === 'template' && 'content' in ast) { | ||
const pos = ast.sourceCodeLocation | ||
// Switch content. | ||
if (result.tagName === 'template') { | ||
const reference = /** @type {P5Template} */ (node) | ||
const pos = reference.sourceCodeLocation | ||
const startTag = pos && pos.startTag && position(pos.startTag) | ||
@@ -251,5 +240,5 @@ const endTag = pos && pos.endTag && position(pos.endTag) | ||
// @ts-expect-error Types are wrong. | ||
const content = transform(ctx, ast.content) | ||
const content = one(state, reference.content) | ||
if (startTag && endTag && ctx.file) { | ||
if (startTag && endTag && state.file) { | ||
content.position = {start: startTag.end, end: endTag.start} | ||
@@ -261,2 +250,4 @@ } | ||
state.schema = schema | ||
return result | ||
@@ -266,10 +257,37 @@ } | ||
/** | ||
* Patch positional info from `from` onto `to`. | ||
* | ||
* @param {State} state | ||
* Info passed around about the current state. | ||
* @param {P5Node} from | ||
* p5 node. | ||
* @param {Node} to | ||
* hast node. | ||
* @returns {void} | ||
* Nothing. | ||
*/ | ||
function patch(state, from, to) { | ||
if ('sourceCodeLocation' in from && from.sourceCodeLocation && state.file) { | ||
const position = createLocation(state, to, from.sourceCodeLocation) | ||
if (position) { | ||
state.location = true | ||
to.position = position | ||
} | ||
} | ||
} | ||
/** | ||
* Create clean positional information. | ||
* | ||
* @param {Context} ctx | ||
* @param {State} state | ||
* Info passed around about the current state. | ||
* @param {Node} node | ||
* hast node. | ||
* @param {P5ElementLocation} location | ||
* @returns {Position|null} | ||
* p5 location info. | ||
* @returns {Position | undefined} | ||
* Position, or nothing. | ||
*/ | ||
function createLocation(ctx, node, location) { | ||
function createLocation(state, node, location) { | ||
const result = position(location) | ||
@@ -292,4 +310,4 @@ | ||
if (ctx.verbose) { | ||
/** @type {Object.<string, Position|null>} */ | ||
if (state.verbose) { | ||
/** @type {Record<string, Position | undefined>} */ | ||
const props = {} | ||
@@ -299,5 +317,9 @@ /** @type {string} */ | ||
for (key in location.attrs) { | ||
if (own.call(location.attrs, key)) { | ||
props[find(ctx.schema, key).property] = position(location.attrs[key]) | ||
if (location.attrs) { | ||
for (key in location.attrs) { | ||
if (own.call(location.attrs, key)) { | ||
props[find(state.schema, key).property] = position( | ||
location.attrs[key] | ||
) | ||
} | ||
} | ||
@@ -308,2 +330,3 @@ } | ||
position: { | ||
// @ts-expect-error: assume not `undefined`. | ||
opening: position(location.startTag), | ||
@@ -321,4 +344,8 @@ closing: location.endTag ? position(location.endTag) : null, | ||
/** | ||
* Turn a p5 location into a position. | ||
* | ||
* @param {P5Location} loc | ||
* @returns {Position|null} | ||
* Location. | ||
* @returns {Position | undefined} | ||
* Position or nothing. | ||
*/ | ||
@@ -336,17 +363,25 @@ function position(loc) { | ||
}) | ||
// @ts-expect-error `null` is fine. | ||
return start || end ? {start, end} : null | ||
// @ts-expect-error `undefined` is fine. | ||
return start || end ? {start, end} : undefined | ||
} | ||
/** | ||
* Filter out invalid points. | ||
* | ||
* @param {Point} point | ||
* @returns {Point|null} | ||
* Point with potentially `undefined` values. | ||
* @returns {Point | undefined} | ||
* Point or nothing. | ||
*/ | ||
function point(point) { | ||
return point.line && point.column ? point : null | ||
return point.line && point.column ? point : undefined | ||
} | ||
/** | ||
* @param {VFile|Options} value | ||
* Check if something is a file. | ||
* | ||
* @param {VFile | Options} value | ||
* File or options. | ||
* @returns {value is VFile} | ||
* Whether `value` is a file. | ||
*/ | ||
@@ -353,0 +388,0 @@ function isFile(value) { |
{ | ||
"name": "hast-util-from-parse5", | ||
"version": "7.1.0", | ||
"version": "7.1.1", | ||
"description": "hast utility to transform from Parse5’s AST", | ||
@@ -37,3 +37,2 @@ "license": "MIT", | ||
"@types/hast": "^2.0.0", | ||
"@types/parse5": "^6.0.0", | ||
"@types/unist": "^2.0.0", | ||
@@ -47,23 +46,21 @@ "hastscript": "^7.0.0", | ||
"devDependencies": { | ||
"@types/tape": "^4.0.0", | ||
"@types/node": "^18.0.0", | ||
"c8": "^7.0.0", | ||
"is-hidden": "^2.0.0", | ||
"parse5": "^6.0.0", | ||
"parse5": "^7.0.0", | ||
"prettier": "^2.0.0", | ||
"remark-cli": "^9.0.0", | ||
"remark-preset-wooorm": "^8.0.0", | ||
"rimraf": "^3.0.0", | ||
"tape": "^5.0.0", | ||
"remark-cli": "^11.0.0", | ||
"remark-preset-wooorm": "^9.0.0", | ||
"to-vfile": "^7.0.0", | ||
"type-coverage": "^2.0.0", | ||
"typescript": "^4.0.0", | ||
"unist-util-visit": "^3.0.0", | ||
"xo": "^0.42.0" | ||
"unist-util-visit": "^4.0.0", | ||
"xo": "^0.53.0" | ||
}, | ||
"scripts": { | ||
"prepack": "npm run build && npm run format", | ||
"build": "rimraf \"{test/**,lib/**,}*.d.ts\" && tsc && type-coverage", | ||
"build": "tsc --build --clean && tsc --build && type-coverage", | ||
"format": "remark . -qfo && prettier . -w --loglevel warn && xo --fix", | ||
"test-api": "node test/index.js", | ||
"test-coverage": "c8 --check-coverage --branches 100 --functions 100 --lines 100 --statements 100 --reporter lcov node test/index.js", | ||
"test-api": "node --conditions development test/index.js", | ||
"test-coverage": "c8 --check-coverage --100 --reporter lcov npm run test-api", | ||
"test": "npm run build && npm run format && npm run test-coverage" | ||
@@ -80,3 +77,14 @@ }, | ||
"xo": { | ||
"prettier": true | ||
"prettier": true, | ||
"rules": { | ||
"max-depth": "off" | ||
}, | ||
"overrides": [ | ||
{ | ||
"files": "test/**/*.js", | ||
"rules": { | ||
"no-await-in-loop": 0 | ||
} | ||
} | ||
] | ||
}, | ||
@@ -83,0 +91,0 @@ "remarkConfig": { |
182
readme.md
@@ -11,12 +11,42 @@ # hast-util-from-parse5 | ||
[**hast**][hast] utility to transform [Parse5’s AST][ast] to a hast | ||
[*tree*][tree]. | ||
[hast][] utility to transform from [`parse5`][parse5]s AST. | ||
## Contents | ||
* [What is this?](#what-is-this) | ||
* [When should I use this?](#when-should-i-use-this) | ||
* [Install](#install) | ||
* [Use](#use) | ||
* [API](#api) | ||
* [`fromParse5(tree[, file|options])`](#fromparse5tree-fileoptions) | ||
* [`Options`](#options) | ||
* [`Space`](#space-1) | ||
* [Types](#types) | ||
* [Compatibility](#compatibility) | ||
* [Security](#security) | ||
* [Related](#related) | ||
* [Contribute](#contribute) | ||
* [License](#license) | ||
## What is this? | ||
This package is a utility that can turn a parse5 tree into a hast tree. | ||
## When should I use this? | ||
You can use this package when using `parse5` as an HTML parser and wanting to | ||
work with hast. | ||
The utility [`hast-util-to-parse5`][hast-util-to-parse5] does the inverse of | ||
this utility. | ||
It generates `parse5`s AST again. | ||
The utility [`hast-util-from-html`][hast-util-from-html] wraps this utility and | ||
`parse5` to both parse HTML and generate hast from it. | ||
## Install | ||
This package is [ESM only](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c): | ||
Node 12+ is needed to use it and it must be `import`ed instead of `require`d. | ||
This package is [ESM only][esm]. | ||
In Node.js (version 14.14+ and 16.0+), install with [npm][]: | ||
[npm][]: | ||
```sh | ||
@@ -26,5 +56,19 @@ npm install hast-util-from-parse5 | ||
In Deno with [`esm.sh`][esmsh]: | ||
```js | ||
import {fromParse5} from "https://esm.sh/hast-util-from-parse5@7" | ||
``` | ||
In browsers with [`esm.sh`][esmsh]: | ||
```html | ||
<script type="module"> | ||
import {fromParse5} from "https://esm.sh/hast-util-from-parse5@7?bundle" | ||
</script> | ||
``` | ||
## Use | ||
Say we have the following file, `example.html`: | ||
Say our document `example.html` contains: | ||
@@ -35,12 +79,12 @@ ```html | ||
And our script, `example.js`, looks as follows: | ||
…and our module `example.js` looks as follows: | ||
```js | ||
import parse5 from 'parse5' | ||
import {readSync} from 'to-vfile' | ||
import {parse} from 'parse5' | ||
import {read} from 'to-vfile' | ||
import {inspect} from 'unist-util-inspect' | ||
import {fromParse5} from 'hast-util-from-parse5' | ||
const file = readSync('example.html') | ||
const p5ast = parse5.parse(String(file), {sourceCodeLocationInfo: true}) | ||
const file = await read('example.html') | ||
const p5ast = parse(String(file), {sourceCodeLocationInfo: true}) | ||
const hast = fromParse5(p5ast, file) | ||
@@ -51,3 +95,3 @@ | ||
Now, running `node example` yields: | ||
…now running `node example.js` yields: | ||
@@ -78,35 +122,49 @@ ```text | ||
This package exports the following identifiers: `fromParse5`. | ||
This package exports the identifier [`fromParse5`][fromparse5]. | ||
There is no default export. | ||
### `fromParse5(ast[, file|options])` | ||
### `fromParse5(tree[, file|options])` | ||
Transform [Parse5’s AST][ast] to a [**hast**][hast] [*tree*][tree]. | ||
Transform a `parse5` AST to hast. | ||
##### `options` | ||
###### Parameters | ||
If `options` is a [`VFile`][vfile], it’s treated as `{file: options}`. | ||
* `tree` ([`Parse5Node`][parse5-node]) | ||
— `parse5` tree to transform | ||
* `file` ([`VFile`][vfile], optional) | ||
— corresponding file (treated as `{file: file}`) | ||
* `options` ([`Options`][options], optional) | ||
— configuration | ||
###### `options.space` | ||
###### Returns | ||
Whether the [*root*][root] of the [*tree*][tree] is in the `'html'` or `'svg'` | ||
space (enum, `'svg'` or `'html'`, default: `'html'`). | ||
hast tree ([`HastNode`][hast-node]). | ||
If an element in with the SVG namespace is found in `ast`, `fromParse5` | ||
automatically switches to the SVG space when entering the element, and switches | ||
back when leaving. | ||
### `Options` | ||
###### `options.file` | ||
Configuration (TypeScript type). | ||
[`VFile`][vfile], used to add [positional information][positional-information] | ||
to [*nodes*][node]. | ||
If given, the [*file*][file] should have the original HTML source as its | ||
contents. | ||
##### Fields | ||
###### `options.verbose` | ||
###### `space` | ||
Whether to add extra positional information about starting tags, closing tags, | ||
Which space the document is in ([`Space`][space], default: `'html'`). | ||
When an `<svg>` element is found in the HTML space, this package already | ||
automatically switches to and from the SVG space when entering and exiting | ||
it. | ||
###### `file` | ||
File used to add positional info to nodes ([`VFile`][vfile], optional). | ||
If given, the file should represent the original HTML source. | ||
###### `verbose` | ||
Whether to add extra positional info about starting tags, closing tags, | ||
and attributes to elements (`boolean`, default: `false`). | ||
Note: not used without `file`. | ||
> 👉 **Note**: only used when `file` is given. | ||
For the following HTML: | ||
@@ -156,2 +214,24 @@ | ||
### `Space` | ||
Namespace (TypeScript type). | ||
###### Type | ||
```ts | ||
type Space = 'html' | 'svg' | ||
``` | ||
## Types | ||
This package is fully typed with [TypeScript][]. | ||
It exports the additional types [`Options`][options] and [`Space`][space]. | ||
## Compatibility | ||
Projects maintained by the unified collective are compatible with all maintained | ||
versions of Node.js. | ||
As of now, that is Node.js 12.20+, 14.14+, 16.0+, and 18.0+. | ||
Our projects sometimes work with older versions, but this is not guaranteed. | ||
## Security | ||
@@ -179,4 +259,4 @@ | ||
See [`contributing.md` in `syntax-tree/.github`][contributing] for ways to get | ||
started. | ||
See [`contributing.md`][contributing] in [`syntax-tree/.github`][health] for | ||
ways to get started. | ||
See [`support.md`][support] for ways to get help. | ||
@@ -222,2 +302,8 @@ | ||
[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c | ||
[esmsh]: https://esm.sh | ||
[typescript]: https://www.typescriptlang.org | ||
[license]: license | ||
@@ -227,24 +313,30 @@ | ||
[contributing]: https://github.com/syntax-tree/.github/blob/HEAD/contributing.md | ||
[health]: https://github.com/syntax-tree/.github | ||
[support]: https://github.com/syntax-tree/.github/blob/HEAD/support.md | ||
[contributing]: https://github.com/syntax-tree/.github/blob/main/contributing.md | ||
[coc]: https://github.com/syntax-tree/.github/blob/HEAD/code-of-conduct.md | ||
[support]: https://github.com/syntax-tree/.github/blob/main/support.md | ||
[ast]: https://github.com/inikulin/parse5/blob/HEAD/packages/parse5/docs/tree-adapter/default/interface-list.md | ||
[coc]: https://github.com/syntax-tree/.github/blob/main/code-of-conduct.md | ||
[vfile]: https://github.com/vfile/vfile | ||
[xss]: https://en.wikipedia.org/wiki/Cross-site_scripting | ||
[tree]: https://github.com/syntax-tree/unist#tree | ||
[parse5]: https://github.com/inikulin/parse5 | ||
[root]: https://github.com/syntax-tree/unist#root | ||
[parse5-node]: https://github.com/inikulin/parse5/blob/master/packages/parse5/lib/tree-adapters/default.ts | ||
[positional-information]: https://github.com/syntax-tree/unist#positional-information | ||
[vfile]: https://github.com/vfile/vfile | ||
[file]: https://github.com/syntax-tree/unist#file | ||
[hast-util-to-parse5]: https://github.com/syntax-tree/hast-util-to-parse5 | ||
[hast]: https://github.com/syntax-tree/hast | ||
[node]: https://github.com/syntax-tree/hast#nodes | ||
[hast-util-from-html]: https://github.com/syntax-tree/hast-util-from-html | ||
[xss]: https://en.wikipedia.org/wiki/Cross-site_scripting | ||
[hast-node]: https://github.com/syntax-tree/hast#nodes | ||
[fromparse5]: #fromparse5tree-fileoptions | ||
[options]: #options | ||
[space]: #space-1 |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
24478
7
12
426
334
1
- Removed@types/parse5@^6.0.0
- Removed@types/parse5@6.0.3(transitive)