From ddbbc18d7015f082a86b3bd244c4b0db8362ea67 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Fri, 1 Sep 2023 18:28:10 +0200 Subject: [PATCH] Refactor docs --- index.d.ts | 5 +- readme.md | 940 +++++++++++++++++++++++++++-------------------------- test.js | 20 +- 3 files changed, 501 insertions(+), 464 deletions(-) diff --git a/index.d.ts b/index.d.ts index c80e268..1f4b238 100644 --- a/index.d.ts +++ b/index.d.ts @@ -9,7 +9,7 @@ export interface Image { */ alt?: string | null | undefined /** - * Height of image (optional, example: '550'). + * Height of image (optional, example: `'550'`). */ height?: number | string | null | undefined /** @@ -18,7 +18,7 @@ export interface Image { */ url: string /** - * Width of image (optional, example: '1050'). + * Width of image (optional, example: `'1050'`). */ width?: number | string | null | undefined } @@ -227,6 +227,7 @@ export interface Options { type?: 'article' | 'website' | null | undefined } +// Add custom data supported when `rehype-meta` is added. declare module 'vfile' { interface DataMapMatter extends Options {} interface DataMapMeta extends Options {} diff --git a/readme.md b/readme.md index 2b55f94..46c97a2 100644 --- a/readme.md +++ b/readme.md @@ -18,7 +18,8 @@ * [Use](#use) * [API](#api) * [`unified().use(rehypeMeta[, options])`](#unifieduserehypemeta-options) - * [`Config`](#config) + * [`Image`](#image) + * [`Options`](#options) * [Metadata](#metadata) * [Examples](#examples) * [Example: frontmatter in markdown](#example-frontmatter-in-markdown) @@ -57,8 +58,8 @@ documents. ## Install -This package is [ESM only](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c). -In Node.js (version 12.20+, 14.14+, 16.0+, or 18.0+), install with [npm][]: +This package is [ESM only][esm]. +In Node.js (version 16+), install with [npm][]: ```sh npm install rehype-meta @@ -89,13 +90,28 @@ import rehypeMeta from 'rehype-meta' const file = await rehype() .data('settings', {fragment: true}) .use(rehypeMeta, { - twitter: true, - og: true, + author: 'Jane Doe', + authorFacebook: 'janedoe', + authorTwitter: '@jane', copyright: true, - type: 'article', + description: + 'The city has changed drastically over the past 40 years, yet the M.T.A. map designed in 1979 has largely endured.', + image: { + alt: 'M.T.A. map designed in 1979', + height: '550', + url: 'https://static01.nyt.com/images/2019/12/02/autossell/mta-promo-image/mta-crop-facebookJumbo.jpg', + width: '1050' + }, + modified: '2019-12-03T19:13:00.000Z', + name: 'The New York Times', + og: true, origin: 'https://www.nytimes.com', pathname: '/interactive/2019/12/02/nyregion/nyc-subway-map.html', - name: 'The New York Times', + published: '2019-12-02T10:00:00.000Z', + readingTime: 11.1, + section: 'New York', + separator: ' | ', + siteAuthor: 'The New York Times', siteTags: [ 'US Politics', 'Impeachment', @@ -106,16 +122,7 @@ const file = await rehype() 'Climate Change', 'Global Warming' ], - siteAuthor: 'The New York Times', siteTwitter: '@nytimes', - author: 'Jane Doe', - authorTwitter: '@jane', - authorFacebook: 'janedoe', - title: 'The New York City Subway Map as You’ve Never Seen It Before', - separator: ' | ', - description: - 'The city has changed drastically over the past 40 years, yet the M.T.A. map designed in 1979 has largely endured.', - section: 'New York', tags: [ 'Subway', 'Map', @@ -125,15 +132,10 @@ const file = await rehype() 'Massimo Vignelli', 'NYC' ], - image: { - url: 'https://static01.nyt.com/images/2019/12/02/autossell/mta-promo-image/mta-crop-facebookJumbo.jpg', - alt: 'M.T.A. map designed in 1979', - width: '1050', - height: '550' - }, - published: '2019-12-02T10:00:00.000Z', - modified: '2019-12-03T19:13:00.000Z', - readingTime: 11.1 + title: + 'The New York City Subway Map as You’ve Never Seen It Before', + twitter: true, + type: 'article' }) .process('') @@ -184,232 +186,187 @@ console.log(String(file)) ## API This package exports no identifiers. -The default export is `rehypeMeta`. +The default export is [`rehypeMeta`][api-rehype-meta]. ### `unified().use(rehypeMeta[, options])` Add metadata to the ``. -* adds a `` if one doesn’t already exist -* overwrites existing metadata in `` - (for example, when a `` already exists, it’s updated) +###### Parameters + +* `options` ([`Options`][api-options], optional) + — configuration + +###### Returns + +Transform ([`Transformer`][unified-transformer]). + +##### Notes -##### `options` +###### Algorithm -Configuration with least priority. -This is particularly useful for site wide metadata. -Mixed into [config][]. +* adds a `<head>` if one doesn’t already exist +* overwrites existing metadata in `<head>` (for example, when a `<title>` + already exists, it’s updated) -### `Config` +###### Config There are three ways to configure the metadata of a document. -1. pass an object as `options` when [using `meta`][use] +1. pass an object as `options` when [using `rehypeMeta`][api-rehype-meta] 2. define it in YAML front matter (by integrating with [`vfile-matter`][vfile-matter]) 3. define an object at `file.data.meta` Configuration is created by extending the defaults, with these objects, in the -above order (so `file.data.meta` takes precedence over `options`). +above order (so `file.data.meta` is preferred over `options`). Only `options` is enough if every file has the same metadata. If your workflow enables front matter, that’s a good way to keep data in files. Alternatively, do it yourself by adding data at `file.data.meta`, which can also be done by plugins: * [`rehype-infer-description-meta`](https://github.com/rehypejs/rehype-infer-description-meta) - — infer [`description`][c-description] from the document + — infer [`description`][o-description] from the document * [`rehype-infer-reading-time-meta`](https://github.com/rehypejs/rehype-infer-reading-time-meta) - — infer [`readingTime`][c-readingtime] from the document + — infer [`readingTime`][o-reading-time] from the document * [`rehype-infer-title-meta`](https://github.com/rehypejs/rehype-infer-title-meta) - — infer [`title`][c-title] from the document + — infer [`title`][o-title] from the document * [`unified-infer-git-meta`](https://github.com/unifiedjs/unified-infer-git-meta) - — infer [`author`][c-author], [`modified`][c-modified], and - [`published`][c-published] from Git - -###### `config.og` - -Whether to add Open Graph metadata (`boolean`, default: `false`). - -Affects: [`meta[property=og:site_name]`][m-og-site-name], -[`meta[property=og:url]`][m-og-url], -[`meta[property=og:title]`][m-og-title], -[`meta[property=og:description]`][m-og-description], -[`meta[property=og:image]`][m-og-image], -[`meta[property=article:published_time]`][m-article-published-time], -[`meta[property=article:modified_time]`][m-article-modified-time], -[`meta[property=article:author]`][m-article-author], -[`meta[property=article:section]`][m-article-section], -[`meta[property=article:tag]`][m-article-tag], -[`meta[name=twitter:card]`][m-twitter-card]. - -###### `config.ogNameInTitle` - -Whether to add the site name `name` to the `og:title` (`boolean`, default: -`false`). - -Affects: [`meta[property=og:title]`][m-og-title]. - -###### `config.twitter` - -Whether to add Twitter metadata (`boolean`, default: `false`). - -Affects: [`meta[name=twitter:card]`][m-twitter-card], -[`meta[name=twitter:image]`][m-twitter-image], -[`meta[name=twitter:site]`][m-twitter-site], -[`meta[name=twitter:creator]`][m-twitter-creator], -[`meta[name=twitter:label1]`][m-twitter-label1], -[`meta[name=twitter:data1]`][m-twitter-data1], -[`meta[name=twitter:label2]`][m-twitter-label2], -[`meta[name=twitter:data2]`][m-twitter-data2]. - -###### `config.copyright` - -Whether to add copyright metadata (`boolean`, default: `false`). - -Affects: [`meta[name=copyright]`][m-copyright]. + — infer [`author`][o-author], [`modified`][o-modified], and + [`published`][o-published] from Git -###### `config.type` +### `Image` -What the document refers to (`'website' | 'article'`, default: `website`). +Image metadata (TypeScript type). -Affects: [`meta[property=og:type]`][m-og-type], -[`meta[property=article:published_time]`][m-article-published-time], -[`meta[property=article:modified_time]`][m-article-modified-time], -[`meta[property=article:author]`][m-article-author], -[`meta[property=article:section]`][m-article-section], -[`meta[property=article:tag]`][m-article-tag]. - -###### `config.origin` - -Origin the file will be hosted on (`string`, optional, example: -`https://www.nytimes.com`). - -Affects: [`link[rel=canonical]`][m-canonical], -[`meta[property=og:url]`][m-og-url]. +###### Fields -###### `config.pathname` +* `alt` (`string`, optional, example: `'M.T.A. map designed in 1979'`) + — alt text of image +* `height` (`number | string`, optional, example: `'550'`) + — height of image +* `url` (`string`, required, example: + `'https://static01.nyt.com/images/…/mta-crop-jumbo.jpg'`) + — URL of image +* `width` (`number | string`, optional, example: `'1050'`) + — width of image -Absolute pathname of where the file will be hosted (`string`, default: `/`, -example: `/interactive/2019/12/02/nyregion/nyc-subway-map.html`). +### `Options` -Affects: [`link[rel=canonical]`][m-canonical], -[`meta[property=og:url]`][m-og-url]. +Configuration (TypeScript type). -###### `config.name` +##### Fields -Name of the whole site (`string`, optional, example: `'The New York Times'`). +###### `author` -Affects: [`title`][m-title], [`meta[property=og:site_name]`][m-og-site-name], -[`meta[property=og:title]`][m-og-title]. - -###### `config.siteTags` - -Tags associated with the whole site (`Array<string>`, optional, example: -`['US Politics', 'Impeachment', 'NATO', 'London', 'Food', 'Poverty', 'Climate -Change', 'Global Warming']`). +Name of the author of the document (`string`, optional, example: +`'Jane Doe'`). -Affects: [`meta[name=keywords]`][m-keywords]. +Affects: [`meta[name=author]`][m-author], [`meta[name=copyright]`][m-copyright]. -###### `config.siteAuthor` +###### `authorFacebook` -Name of the author of the whole site (`string`, optional, example: -`'The New York Times'`). +Facebook username of the author of the document (`string`, optional, example: +`'example'`). -Affects: [`meta[name=author]`][m-author], [`meta[name=copyright]`][m-copyright]. +Affects: [`meta[property=article:author]`][m-article-author]. -###### `config.siteTwitter` +###### `authorTwitter` -Twitter username of the whole site (`string`, optional, example: `'@nytimes'`). +Twitter username of the author of the document (`string`, optional, example: +`'@janedoe'`). -Affects: [`meta[name=twitter:site]`][m-twitter-site]. +Affects: [`meta[name=twitter:creator]`][m-twitter-creator]. -###### `config.color` +###### `color` Hexadecimal theme color of document or site (`string`, optional, example: `'#bada55'`). Affects: [`meta[name=theme-color]`][m-theme-color]. -###### `config.author` +###### `copyright` -Name of the author of the document (`string`, optional, example: -`'Jane Doe'`). +Whether to add copyright metadata (`boolean`, default: `false`). -Affects: [`meta[name=author]`][m-author], [`meta[name=copyright]`][m-copyright]. +Affects: [`meta[name=copyright]`][m-copyright]. -###### `config.authorTwitter` +###### `description` -Twitter username of the author of the document (`string`, optional, example: -`'@janedoe'`). +Description of the document (`string`, optional, example: +`'The city has changed drastically over the past 40 years, +yet the M.T.A. map designed in 1979 has largely endured.'`). -Affects: [`meta[name=twitter:creator]`][m-twitter-creator]. +Affects: [`meta[name=description]`][m-description], +[`meta[property=og:description]`][m-og-description]. -###### `config.authorFacebook` +###### `image` -Facebook username of the author of the document (`string`, optional, example: -`'example'`). +One or more images associated with the document +(`Array<Image | string> | Image | string`, optional); if strings are +passed, they are seen as `Image` objects with a `url` field set to that +value. -Affects: [`meta[property=article:author]`][m-article-author]. +Affects: [`meta[property=og:image]`][m-og-image], +[`meta[name=twitter:card]`][m-twitter-card], +[`meta[name=twitter:image]`][m-twitter-image]. -###### `config.title` +###### `modified` -Title of the document (`string`, optional, example: `'The New York City Subway -Map as You’ve Never Seen It Before'`). +Date the document was last modified (`Date` or `string`, optional, example: +`'2019-12-03T19:13:00.000Z'`). -Affects: [`title`][m-title], [`meta[property=og:title]`][m-og-title]. +> 👉 **Note**: parsing a string is [inconsistent][timestamp], prefer dates. -###### `config.separator` +Affects: [`meta[property=article:modified_time]`][m-article-modified-time]. -Value to use to join the `title` and `name` together (`string`, default: -`' - '`). +###### `name` -Affects: [`title`][m-title], [`meta[property=og:title]`][m-og-title]. +Name of the whole site (`string`, optional, example: `'The New York Times'`). -###### `config.description` +Affects: [`title`][m-title], [`meta[property=og:site_name]`][m-og-site-name], +[`meta[property=og:title]`][m-og-title]. -Description of the document (`string`, optional, example: -`'The city has changed drastically over the past 40 years, -yet the M.T.A. map designed in 1979 has largely endured.'`). +###### `og` -Affects: [`meta[name=description]`][m-description], -[`meta[property=og:description]`][m-og-description]. - -###### `config.section` +Whether to add Open Graph metadata (`boolean`, default: `false`). -Section associated with the document (`string`, optional, example: -`'New York'`). +Affects: [`meta[property=og:site_name]`][m-og-site-name], +[`meta[property=og:url]`][m-og-url], +[`meta[property=og:title]`][m-og-title], +[`meta[property=og:description]`][m-og-description], +[`meta[property=og:image]`][m-og-image], +[`meta[property=article:published_time]`][m-article-published-time], +[`meta[property=article:modified_time]`][m-article-modified-time], +[`meta[property=article:author]`][m-article-author], +[`meta[property=article:section]`][m-article-section], +[`meta[property=article:tag]`][m-article-tag], +[`meta[name=twitter:card]`][m-twitter-card]. -Affects: [`meta[property=article:section]`][m-article-section], [`meta[name=twitter:label1]`][m-twitter-label1], -[`meta[name=twitter:data1]`][m-twitter-data1]. +###### `ogNameInTitle` -###### `config.tags` +Whether to add the site name `name` to the `og:title` (`boolean`, default: +`false`). -Tags associated with the document (`Array<string>`, optional, example: -`['Subway', 'Map', 'Public Transit', 'Design', 'MTA', 'Massimo Vignelli', -'NYC']`). +Affects: [`meta[property=og:title]`][m-og-title]. -Affects: [`meta[name=keywords]`][m-keywords], -[`meta[property=article:tag]`][m-article-tag]. +###### `origin` -###### `config.image` +Origin the file will be hosted on (`string`, optional, example: +`https://www.nytimes.com`). -One or more images associated with the document (`string`, `Image`, or -`Array<Image | string>`, optional). -If strings are passed, they are seen as `Image` objects with a `url` field set -to that value. +Affects: [`link[rel=canonical]`][m-canonical], +[`meta[property=og:url]`][m-og-url]. -`Image`: +###### `pathname` -* `url` (`string`, required, example: `'https://static01.nyt.com/images/…/mta-crop-jumbo.jpg'`) -* `alt` (`string`, optional, example: `'M.T.A. map designed in 1979'`) -* `width` (`string`, optional, example: `'1050'`) -* `height` (`string`, optional, example: `'550'`) +Absolute pathname of where the file will be hosted (`string`, default: `/`, +example: `/interactive/2019/12/02/nyregion/nyc-subway-map.html`). -Affects: [`meta[property=og:image]`][m-og-image], -[`meta[name=twitter:card]`][m-twitter-card], -[`meta[name=twitter:image]`][m-twitter-image]. +Affects: [`link[rel=canonical]`][m-canonical], +[`meta[property=og:url]`][m-og-url]. -###### `config.published` +###### `published` Date the document (or site) was first published (`Date` or `string`, optional, example: `'2019-12-02T10:00:00.000Z'`). @@ -419,16 +376,7 @@ example: `'2019-12-02T10:00:00.000Z'`). Affects: [`meta[name=copyright]`][m-copyright], [`meta[property=article:published_time]`][m-article-published-time]. -###### `config.modified` - -Date the document was last modified (`Date` or `string`, optional, example: -`'2019-12-03T19:13:00.000Z'`). - -> 👉 **Note**: parsing a string is [inconsistent][timestamp], prefer dates. - -Affects: [`meta[property=article:modified_time]`][m-article-modified-time]. - -###### `config.readingTime` +###### `readingTime` Estimated reading time in minutes for the document (`[number, number]` or `number`, optional, example: `1.219403`). @@ -439,89 +387,105 @@ Affects: [`meta[name=twitter:label1]`][m-twitter-label1], [`meta[name=twitter:label2]`][m-twitter-label2], [`meta[name=twitter:data2]`][m-twitter-data2]. -## Metadata +###### `section` -The following metadata can be added by `rehype-meta`. +Section associated with the document (`string`, optional, example: +`'New York'`). -###### `title` +Affects: [`meta[property=article:section]`][m-article-section], [`meta[name=twitter:label1]`][m-twitter-label1], +[`meta[name=twitter:data1]`][m-twitter-data1]. -Affected by: [`title`][c-title], [`name`][c-name], [`separator`][c-separator]. +###### `separator` -If `title` is `'About'`: +Value to use to join the `title` and `name` together (`string`, default: +`' - '`). -```html -<title>About -``` +Affects: [`title`][m-title], [`meta[property=og:title]`][m-og-title]. -If `name` is `'Example'`: +###### `siteAuthor` -```html -Example -``` +Name of the author of the whole site (`string`, optional, example: +`'The New York Times'`). -If `title` is `'About'` and `name` is `'Example'`: +Affects: [`meta[name=author]`][m-author], [`meta[name=copyright]`][m-copyright]. -```html -About - Example -``` +###### `siteTags` -If `title` is `'About'`, `name` is `'Example'`, and separator to `' | '`: +Tags associated with the whole site (`Array`, optional, example: +`['US Politics', 'Impeachment', 'NATO', 'London', 'Food', 'Poverty', 'Climate +Change', 'Global Warming']`). -```html -About | Example -``` +Affects: [`meta[name=keywords]`][m-keywords]. -###### `link[rel=canonical]` +###### `siteTwitter` -Affected by: [`origin`][c-origin], [`pathname`][c-pathname]. +Twitter username of the whole site (`string`, optional, example: `'@nytimes'`). -If `origin` is `'https://example.com'` and `path` is `'/path/'`: +Affects: [`meta[name=twitter:site]`][m-twitter-site]. -```html - -``` +###### `tags` -If `origin` is `'https://example.com'` and `path` is not set: +Tags associated with the document (`Array`, optional, example: +`['Subway', 'Map', 'Public Transit', 'Design', 'MTA', 'Massimo Vignelli', +'NYC']`). -```html - -``` +Affects: [`meta[name=keywords]`][m-keywords], +[`meta[property=article:tag]`][m-article-tag]. -###### `meta[name=description]` +###### `title` -Affected by: [`description`][c-description]. +Title of the document (`string`, optional, example: `'The New York City Subway +Map as You’ve Never Seen It Before'`). -If `description` is `'Lorem ipsum'`: +Affects: [`title`][m-title], [`meta[property=og:title]`][m-og-title]. -```html - -``` +###### `twitter` -###### `meta[name=keywords]` +Whether to add Twitter metadata (`boolean`, default: `false`). -Affected by: [`tags`][c-tags], [`siteTags`][c-sitetags]. +Affects: [`meta[name=twitter:card]`][m-twitter-card], +[`meta[name=twitter:image]`][m-twitter-image], +[`meta[name=twitter:site]`][m-twitter-site], +[`meta[name=twitter:creator]`][m-twitter-creator], +[`meta[name=twitter:label1]`][m-twitter-label1], +[`meta[name=twitter:data1]`][m-twitter-data1], +[`meta[name=twitter:label2]`][m-twitter-label2], +[`meta[name=twitter:data2]`][m-twitter-data2]. -If `tags` is `['a', 'b']`: +###### `type` -```html - -``` +What the document refers to (`'article' | 'website'`, default: `'website'`). -If `siteTags` is `['b', 'c']`: +Affects: [`meta[property=og:type]`][m-og-type], +[`meta[property=article:published_time]`][m-article-published-time], +[`meta[property=article:modified_time]`][m-article-modified-time], +[`meta[property=article:author]`][m-article-author], +[`meta[property=article:section]`][m-article-section], +[`meta[property=article:tag]`][m-article-tag]. + +## Metadata + +The following metadata can be added by `rehype-meta`. + +###### `link[rel=canonical]` + +Affected by: [`origin`][o-origin], [`pathname`][o-path-name]. + +If `origin` is `'https://example.com'` and `path` is `'/path/'`: ```html - + ``` -If `tags` is `['a', 'b']` and `siteTags` is `['b', 'c']`: +If `origin` is `'https://example.com'` and `path` is not set: ```html - + ``` ###### `meta[name=author]` -Affected by: [`author`][c-author], [`siteAuthor`][c-siteauthor]. +Affected by: [`author`][o-author], [`siteAuthor`][o-site-author]. If `author` is `'Jane'`: @@ -543,8 +507,8 @@ If `author` is `'Jane'` and `siteAuthor` is `'John'`: ###### `meta[name=copyright]` -Affected by: [`copyright`][c-copyright], [`author`][c-author], -[`siteAuthor`][c-siteauthor], [`published`][c-published]. +Affected by: [`copyright`][o-copyright], [`author`][o-author], +[`siteAuthor`][o-site-author], [`published`][o-published]. The below examples depend on the current date, so for example purposes, say it was the year 2030. @@ -575,155 +539,194 @@ If `copyright` is `true`, `author` is `'Jane'`, and `published` is `'2015'`: ``` -###### `meta[name=theme-color]` +###### `meta[name=description]` -Affected by: [`color`][c-color]. +Affected by: [`description`][o-description]. -If `color` is `'#bada55'`: +If `description` is `'Lorem ipsum'`: ```html - + ``` -###### `meta[property=og:type]` - -Affected by: [`og`][c-og], [`type`][c-type]. +###### `meta[name=keywords]` -If `og` is not `true`, `meta[property=og:type]` is not added. +Affected by: [`tags`][o-tags], [`siteTags`][o-site-tags]. -If `og` is `true` and `type` is `'website'`: +If `tags` is `['a', 'b']`: ```html - + ``` -If `og` is `true` and `type` is `'article'`: +If `siteTags` is `['b', 'c']`: ```html - + ``` -###### `meta[property=og:site_name]` +If `tags` is `['a', 'b']` and `siteTags` is `['b', 'c']`: -Affected by: [`og`][c-og], [`name`][c-name]. +```html + +``` -If `og` is not `true`, `meta[property=og:site_name]` is not added. +###### `meta[name=theme-color]` -If `og` is `true` and `name` is `'Example'`: +Affected by: [`color`][o-color]. + +If `color` is `'#bada55'`: ```html - + ``` -###### `meta[property=og:url]` +###### `meta[name=twitter:card]` -Affected by: [`og`][c-og], [`origin`][c-origin], [`pathname`][c-pathname]. +Affected by: [`og`][o-og], [`twitter`][o-twitter], [`image`][o-image]. -If `og` is not `true`, `meta[property=og:url]` is not added. +If `twitter` is not `true`, `meta[name=twitter:card]` is not added. +If `twitter` is `true`, `og` is true, and there is no valid image, no +`meta[name=twitter:card]` is added either, because Twitter assumes a summary in +this case. -If `og` is `true`, `origin` is `'https://example.com'`, and `path` is -`'/path/'`: +If `twitter` is `true` and there is a valid image: ```html - + ``` -If `origin` is `'https://example.com'` and `path` is not set: +If `twitter` is `true` and there is no valid image: ```html - + ``` -###### `meta[property=og:title]` +###### `meta[name=twitter:creator]` -Affected by: [`og`][c-og], [`ogNameInTitle`][c-og-name-in-title], -[`title`][c-title], [`name`][c-name], [`separator`][c-separator]. +Affected by: [`twitter`][o-twitter], [`authorTwitter`][o-author-twitter]. -If `og` is not `true`, `meta[property=og:title]` is not added. +If `twitter` is not `true`, `meta[name=twitter:creator]` is not added. -If `og` is `true` and `title` is `'About'`: +If `twitter` is `true` and `authorTwitter` is `'@example'`: ```html - + ``` -If `og` is `true`, `ogNameInTitle` is `true`, `title` is `'About'`, and `name` -is `'Site'`: +###### `meta[name=twitter:data1]` + +###### `meta[name=twitter:label1]` + +Affected by: [`twitter`][o-twitter], [`section`][o-section], +[`readingTime`][o-reading-time]. + +> 👉 **Note**: this data is used by Slack, not by Twitter. + +If `twitter` is not `true`, `meta[name=twitter:label1]` and +`meta[name=twitter:data1]` are not added. + +If `twitter` is `true` and `section` is `'Food'`: ```html - + + ``` -If `og` is `true`, `ogNameInTitle` is `true`, `title` is `'About'`, `name` is -`'Site'`, and `separator` is `' | '`: +If `twitter` is `true`, `section` is not defined, and `readingTime` is `3.083`: ```html - + + ``` -###### `meta[property=og:description]` +###### `meta[name=twitter:data2]` -Affected by: [`og`][c-og], [`description`][c-description]. +###### `meta[name=twitter:label2]` -If `og` is not `true`, `meta[property=og:description]` is not added. +Affected by: [`twitter`][o-twitter], [`section`][o-section], +[`readingTime`][o-reading-time]. -If `og` is `true` and `description` is `'Lorem ipsum'`: +> 👉 **Note**: this data is used by Slack, not by Twitter. + +If `twitter` is not `true`, `section` is not defined, or `readingTime` is not +defined, `meta[name=twitter:label2]` and `meta[name=twitter:data2]` are not +added. + +If `twitter` is `true`, `section` is defined, and `readingTime` is `0.8`: ```html - + + ``` -###### `meta[property=og:image]` +If `twitter` is `true`, `section` is defined, and `readingTime` is `[8, 12]`: -Affected by: [`og`][c-og], [`image`][c-image]. +```html + + +``` -If `og` is not `true`, `meta[property=og:image]`, `meta[property=og:image:alt]`, -`meta[property=og:image:width]`, and `meta[property=og:image:height]` are not -added. +###### `meta[name=twitter:image]` -> 👉 **Note**: up to 6 images are added. +Affected by: [`twitter`][o-twitter], [`image`][o-image]. -If `og` is `true` and `image` is `'https://example.com/image.png'`: +If `twitter` is not `true`, `meta[name=twitter:image]` and +`meta[name=twitter:image:alt]` are not added. + +> 👉 **Note**: only one image is added. + +If `twitter` is `true` and `image` is `'https://example.com/image.png'`: ```html - + +``` + +If `twitter` is `true` and `image` is `['https://example.com/a.png', +'https://example.com/b.png']`: + +```html + ``` -If `og` is `true` and `image` is `['https://example.com/a.png', -'https://example.com/b.png']`: +If `twitter` is `true` and `image` is `{url: 'https://example.com/a.png', alt: +'A', width: '670', height: '1012'}`: ```html - - + + ``` -If `og` is `true` and `image` is `{url: 'https://example.com/a.png', alt: 'A', -width: '670', height: '1012'}`: +###### `meta[name=twitter:site]` + +Affected by: [`twitter`][o-twitter], [`siteTwitter`][o-site-twitter]. + +If `twitter` is not `true`, `meta[name=twitter:site]` is not added. + +If `twitter` is `true` and `siteTwitter` is `'@example'`: ```html - - - - + ``` -###### `meta[property=article:published_time]` +###### `meta[property=article:author]` -Affected by: [`og`][c-og], [`type`][c-type], [`published`][c-published]. +Affected by: [`og`][o-og], [`type`][o-type], +[`authorFacebook`][o-author-facebook]. If `og` is not `true` or `type` is not `'article'`, -`meta[property=article:published_time]` is not added. +`meta[property=article:author]` is not added. -If `og` is `true`, `type` is `'article'`, and `published` is -`'2014-06-30T15:01:35-05:00'`: +If `og` is `true`, `type` is `'article'`, and `authorFacebook` is +`'jane'`: ```html - + ``` ###### `meta[property=article:modified_time]` -Affected by: [`og`][c-og], [`type`][c-type], [`modified`][c-modified]. +Affected by: [`og`][o-og], [`type`][o-type], [`modified`][o-modified]. If `og` is not `true` or `type` is not `'article'`, `meta[property=article:modified_time]` is not added. @@ -735,24 +738,23 @@ If `og` is `true`, `type` is `'article'`, and `modified` is ``` -###### `meta[property=article:author]` +###### `meta[property=article:published_time]` -Affected by: [`og`][c-og], [`type`][c-type], -[`authorFacebook`][c-authorfacebook]. +Affected by: [`og`][o-og], [`type`][o-type], [`published`][o-published]. If `og` is not `true` or `type` is not `'article'`, -`meta[property=article:author]` is not added. +`meta[property=article:published_time]` is not added. -If `og` is `true`, `type` is `'article'`, and `authorFacebook` is -`'jane'`: +If `og` is `true`, `type` is `'article'`, and `published` is +`'2014-06-30T15:01:35-05:00'`: ```html - + ``` ###### `meta[property=article:section]` -Affected by: [`og`][c-og], [`type`][c-type], [`section`][c-section]. +Affected by: [`og`][o-og], [`type`][o-type], [`section`][o-section]. If `og` is not `true` or `type` is not `'article'`, `meta[property=article:section]` is not added. @@ -765,7 +767,7 @@ If `og` is `true`, `type` is `'article'`, and `section` is `'Politics'`: ###### `meta[property=article:tag]` -Affected by: [`og`][c-og], [`type`][c-type], [`tag`][c-tags]. +Affected by: [`og`][o-og], [`type`][o-type], [`tag`][o-tags]. If `og` is not `true` or `type` is not `'article'`, `meta[property=article:tag]` are not added. @@ -784,132 +786,154 @@ If `og` is `true`, `type` is `'article'`, and `tags` is `['US Politics', ``` -###### `meta[name=twitter:card]` +###### `meta[property=og:description]` -Affected by: [`og`][c-og], [`twitter`][c-twitter], [`image`][c-image]. +Affected by: [`og`][o-og], [`description`][o-description]. -If `twitter` is not `true`, `meta[name=twitter:card]` is not added. -If `twitter` is `true`, `og` is true, and there is no valid image, no -`meta[name=twitter:card]` is added either, because Twitter assumes a summary in -this case. +If `og` is not `true`, `meta[property=og:description]` is not added. -If `twitter` is `true` and there is a valid image: +If `og` is `true` and `description` is `'Lorem ipsum'`: ```html - + ``` -If `twitter` is `true` and there is no valid image: +###### `meta[property=og:image]` -```html - -``` +Affected by: [`og`][o-og], [`image`][o-image]. -###### `meta[name=twitter:image]` +If `og` is not `true`, `meta[property=og:image]`, `meta[property=og:image:alt]`, +`meta[property=og:image:width]`, and `meta[property=og:image:height]` are not +added. -Affected by: [`twitter`][c-twitter], [`image`][c-image]. +> 👉 **Note**: up to 6 images are added. -If `twitter` is not `true`, `meta[name=twitter:image]` and -`meta[name=twitter:image:alt]` are not added. +If `og` is `true` and `image` is `'https://example.com/image.png'`: -> 👉 **Note**: only one image is added. +```html + +``` -If `twitter` is `true` and `image` is `'https://example.com/image.png'`: +If `og` is `true` and `image` is `['https://example.com/a.png', +'https://example.com/b.png']`: ```html - + + ``` -If `twitter` is `true` and `image` is `['https://example.com/a.png', -'https://example.com/b.png']`: +If `og` is `true` and `image` is `{url: 'https://example.com/a.png', alt: 'A', +width: '670', height: '1012'}`: ```html - + + + + ``` -If `twitter` is `true` and `image` is `{url: 'https://example.com/a.png', alt: -'A', width: '670', height: '1012'}`: +###### `meta[property=og:site_name]` + +Affected by: [`og`][o-og], [`name`][o-name]. + +If `og` is not `true`, `meta[property=og:site_name]` is not added. + +If `og` is `true` and `name` is `'Example'`: ```html - - + ``` -###### `meta[name=twitter:site]` +###### `meta[property=og:title]` -Affected by: [`twitter`][c-twitter], [`siteTwitter`][c-sitetwitter]. +Affected by: [`og`][o-og], [`ogNameInTitle`][o-og-name-in-title], +[`title`][o-title], [`name`][o-name], [`separator`][o-separator]. -If `twitter` is not `true`, `meta[name=twitter:site]` is not added. +If `og` is not `true`, `meta[property=og:title]` is not added. -If `twitter` is `true` and `siteTwitter` is `'@example'`: +If `og` is `true` and `title` is `'About'`: ```html - + ``` -###### `meta[name=twitter:creator]` +If `og` is `true`, `ogNameInTitle` is `true`, `title` is `'About'`, and `name` +is `'Site'`: -Affected by: [`twitter`][c-twitter], [`authorTwitter`][c-authortwitter]. +```html + +``` -If `twitter` is not `true`, `meta[name=twitter:creator]` is not added. +If `og` is `true`, `ogNameInTitle` is `true`, `title` is `'About'`, `name` is +`'Site'`, and `separator` is `' | '`: -If `twitter` is `true` and `authorTwitter` is `'@example'`: +```html + +``` + +###### `meta[property=og:type]` + +Affected by: [`og`][o-og], [`type`][o-type]. + +If `og` is not `true`, `meta[property=og:type]` is not added. + +If `og` is `true` and `type` is `'website'`: ```html - + ``` -###### `meta[name=twitter:label1]` +If `og` is `true` and `type` is `'article'`: -###### `meta[name=twitter:data1]` +```html + +``` -Affected by: [`twitter`][c-twitter], [`section`][c-section], -[`readingTime`][c-readingtime]. +###### `meta[property=og:url]` -> 👉 **Note**: this data is used by Slack, not by Twitter. +Affected by: [`og`][o-og], [`origin`][o-origin], [`pathname`][o-path-name]. -If `twitter` is not `true`, `meta[name=twitter:label1]` and -`meta[name=twitter:data1]` are not added. +If `og` is not `true`, `meta[property=og:url]` is not added. -If `twitter` is `true` and `section` is `'Food'`: +If `og` is `true`, `origin` is `'https://example.com'`, and `path` is +`'/path/'`: ```html - - + ``` -If `twitter` is `true`, `section` is not defined, and `readingTime` is `3.083`: +If `origin` is `'https://example.com'` and `path` is not set: ```html - - + ``` -###### `meta[name=twitter:label2]` +###### `title` -###### `meta[name=twitter:data2]` +Affected by: [`title`][o-title], [`name`][o-name], [`separator`][o-separator]. -Affected by: [`twitter`][c-twitter], [`section`][c-section], -[`readingTime`][c-readingtime]. +If `title` is `'About'`: -> 👉 **Note**: this data is used by Slack, not by Twitter. +```html +About +``` -If `twitter` is not `true`, `section` is not defined, or `readingTime` is not -defined, `meta[name=twitter:label2]` and `meta[name=twitter:data2]` are not -added. +If `name` is `'Example'`: -If `twitter` is `true`, `section` is defined, and `readingTime` is `0.8`: +```html +Example +``` + +If `title` is `'About'` and `name` is `'Example'`: ```html - - +About - Example ``` -If `twitter` is `true`, `section` is defined, and `readingTime` is `[8, 12]`: +If `title` is `'About'`, `name` is `'Example'`, and separator to `' | '`: ```html - - +About | Example ``` ## Examples @@ -938,18 +962,18 @@ tags: To do: write some stuff about why neptune is cool. ``` -And our module `example.js` looks as follows: +…and a module `example.js`: ```js -import {matter} from 'vfile-matter' -import {read} from 'to-vfile' -import {unified} from 'unified' -import remarkParse from 'remark-parse' import remarkFrontmatter from 'remark-frontmatter' +import remarkParse from 'remark-parse' import remarkRehype from 'remark-rehype' import rehypeDocument from 'rehype-document' import rehypeMeta from 'rehype-meta' import rehypeStringify from 'rehype-stringify' +import {read} from 'to-vfile' +import {unified} from 'unified' +import {matter} from 'vfile-matter' const file = await read('example.md') @@ -975,14 +999,14 @@ await unified() }) // Site wide metadata: .use(rehypeMeta, { - og: true, - twitter: true, copyright: true, - type: 'article', name: 'Planets', - siteTags: ['planet', 'solar', 'galaxy'], + og: true, siteAuthor: 'J. Galle', - siteTwitter: '@the_planets' + siteTags: ['planet', 'solar', 'galaxy'], + siteTwitter: '@the_planets', + twitter: true, + type: 'article' }) .use(rehypeStringify) .process(file) @@ -998,13 +1022,13 @@ console.log(String(file)) Neptune - Planets - - + + - + @@ -1034,9 +1058,6 @@ Taking this readme as an example and running the following code within this repo: ```js -import {read} from 'to-vfile' -import {unified} from 'unified' -import unifiedInferGitMeta from 'unified-infer-git-meta' import remarkParse from 'remark-parse' import remarkRehype from 'remark-rehype' import rehypeDocument from 'rehype-document' @@ -1045,6 +1066,9 @@ import rehypeInferDescriptionMeta from 'rehype-infer-description-meta' import rehypeInferReadingTimeMeta from 'rehype-infer-reading-time-meta' import rehypeMeta from 'rehype-meta' import rehypeStringify from 'rehype-stringify' +import {read} from 'to-vfile' +import {unified} from 'unified' +import unifiedInferGitMeta from 'unified-infer-git-meta' const file = await unified() .use(remarkParse) @@ -1052,7 +1076,7 @@ const file = await unified() .use(remarkRehype) .use(rehypeDocument) .use(rehypeInferTitleMeta) // Find the main title. - .use(rehypeInferDescriptionMeta, {truncateSize: 64}) // Find the description. + .use(rehypeInferDescriptionMeta, {truncateSize: 64}) // Find the description. .use(rehypeInferReadingTimeMeta) // Estimate reading time. .use(rehypeMeta, {og: true, twitter: true, copyright: true}) .use(rehypeStringify) @@ -1069,7 +1093,7 @@ Yields: rehype-meta - + @@ -1087,7 +1111,8 @@ Yields: ## Types This package is fully typed with [TypeScript][]. -The additional types `Options` and `Image` are exported. +It exports the additional types [`Image`][api-image] and +[`Options`][api-options]. It also registers expected fields on `file.data.meta` and `file.data.matter` with `vfile`. @@ -1108,10 +1133,13 @@ console.log(file.data.meta.title) //=> TS now knows that this is a `string?`. ## Compatibility -Projects maintained by the unified collective are compatible with all maintained +Projects maintained by the unified collective are compatible with 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. + +When we cut a new major release, we drop support for unmaintained versions of +Node. +This means we try to keep the current release line, `rehype-meta@^3`, compatible +with Node.js 12. This plugin works with `rehype-parse` version 3+, `rehype-stringify` version 3+, `rehype` version 4+, and `unified` version 6+. @@ -1167,9 +1195,9 @@ abide by its terms. [downloads]: https://www.npmjs.com/package/rehype-meta -[size-badge]: https://img.shields.io/bundlephobia/minzip/rehype-meta.svg +[size-badge]: https://img.shields.io/bundlejs/size/rehype-meta -[size]: https://bundlephobia.com/result?p=rehype-meta +[size]: https://bundlejs.com/?q=rehype-meta [sponsors-badge]: https://opencollective.com/unified/sponsors/badge.svg @@ -1183,6 +1211,8 @@ abide by its terms. [npm]: https://docs.npmjs.com/cli/install +[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + [esmsh]: https://esm.sh [health]: https://github.com/rehypejs/.github @@ -1197,118 +1227,124 @@ abide by its terms. [author]: https://wooorm.com +[rehype]: https://github.com/rehypejs/rehype + +[rehype-document]: https://github.com/rehypejs/rehype-document + [typescript]: https://www.typescriptlang.org [unified]: https://github.com/unifiedjs/unified -[rehype]: https://github.com/rehypejs/rehype - -[rehype-document]: https://github.com/rehypejs/rehype-document +[unified-transformer]: https://github.com/unifiedjs/unified#transformer [vfile-matter]: https://github.com/vfile/vfile-matter +[timestamp]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Date#Timestamp_string + [config]: #config -[use]: #unifieduserehypemeta-options +[api-image]: #image -[timestamp]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Date#Timestamp_string +[api-options]: #options + +[api-rehype-meta]: #unifieduserehypemeta-options -[c-og]: #configog +[m-article-author]: #metapropertyarticleauthor -[c-og-name-in-title]: #configognameintitle +[m-article-modified-time]: #metapropertyarticlemodified_time -[c-type]: #configtype +[m-article-published-time]: #metapropertyarticlepublished_time -[c-twitter]: #configtwitter +[m-article-section]: #metapropertyarticlesection -[c-copyright]: #configcopyright +[m-article-tag]: #metapropertyarticletag -[c-origin]: #configorigin +[m-author]: #metanameauthor -[c-pathname]: #configpathname +[m-canonical]: #linkrelcanonical -[c-name]: #configname +[m-copyright]: #metanamecopyright -[c-sitetags]: #configsitetags +[m-description]: #metanamedescription -[c-siteauthor]: #configsiteauthor +[m-keywords]: #metanamekeywords -[c-sitetwitter]: #configsitetwitter +[m-og-description]: #metapropertyogdescription -[c-color]: #configcolor +[m-og-image]: #metapropertyogimage -[c-author]: #configauthor +[m-og-site-name]: #metapropertyogsite_name -[c-authortwitter]: #configauthortwitter +[m-og-title]: #metapropertyogtitle -[c-authorfacebook]: #configauthorfacebook +[m-og-type]: #metapropertyogtype -[c-title]: #configtitle +[m-og-url]: #metapropertyogurl -[c-separator]: #configseparator +[m-title]: #title-1 -[c-description]: #configdescription +[m-theme-color]: #metanametheme-color -[c-section]: #configsection +[m-twitter-card]: #metanametwittercard -[c-tags]: #configtags +[m-twitter-creator]: #metanametwittercreator -[c-image]: #configimage +[m-twitter-data1]: #metanametwitterdata1 -[c-published]: #configpublished +[m-twitter-data2]: #metanametwitterdata2 -[c-modified]: #configmodified +[m-twitter-image]: #metanametwitterimage -[c-readingtime]: #configreadingtime +[m-twitter-label1]: #metanametwitterlabel1 -[m-title]: #title +[m-twitter-label2]: #metanametwitterlabel2 -[m-canonical]: #linkrelcanonical +[m-twitter-site]: #metanametwittersite -[m-description]: #metanamedescription +[o-author]: #author -[m-keywords]: #metanamekeywords +[o-author-facebook]: #authorfacebook -[m-author]: #metanameauthor +[o-author-twitter]: #authortwitter -[m-copyright]: #metanamecopyright +[o-color]: #color -[m-theme-color]: #metanametheme-color +[o-copyright]: #copyright -[m-og-type]: #metapropertyogtype +[o-description]: #description -[m-og-site-name]: #metapropertyogsite_name +[o-image]: #image-1 -[m-og-url]: #metapropertyogurl +[o-modified]: #modified -[m-og-title]: #metapropertyogtitle +[o-name]: #name -[m-og-description]: #metapropertyogdescription +[o-og]: #og -[m-og-image]: #metapropertyogimage +[o-og-name-in-title]: #ognameintitle -[m-article-published-time]: #metapropertyarticlepublished_time +[o-origin]: #origin -[m-article-modified-time]: #metapropertyarticlemodified_time +[o-path-name]: #pathname -[m-article-author]: #metapropertyarticleauthor +[o-published]: #published -[m-article-section]: #metapropertyarticlesection +[o-reading-time]: #readingtime -[m-article-tag]: #metapropertyarticletag +[o-section]: #section -[m-twitter-card]: #metanametwittercard +[o-separator]: #separator -[m-twitter-image]: #metanametwitterimage +[o-site-author]: #siteauthor -[m-twitter-site]: #metanametwittersite +[o-site-tags]: #sitetags -[m-twitter-creator]: #metanametwittercreator +[o-site-twitter]: #sitetwitter -[m-twitter-label1]: #metanametwitterlabel1 +[o-tags]: #tags -[m-twitter-data1]: #metanametwitterdata1 +[o-title]: #title -[m-twitter-label2]: #metanametwitterlabel2 +[o-twitter]: #twitter -[m-twitter-data2]: #metanametwitterdata2 +[o-type]: #type diff --git a/test.js b/test.js index fe539d6..9f9fdb9 100644 --- a/test.js +++ b/test.js @@ -1057,12 +1057,12 @@ test('rehypeMeta', async function (t) { authorTwitter: '@jane', copyright: true, description: - 'The city has changed drastically over the past 40 years, yet the M.assert.A. map designed in 1979 has largely endured.', + 'The city has changed drastically over the past 40 years, yet the M.T.A. map designed in 1979 has largely endured.', image: { + alt: 'M.T.A. map designed in 1979', + height: '550', url: 'https://static01.nyt.com/images/2019/12/02/autossell/mta-promo-image/mta-crop-facebookJumbo.jpg', - alt: 'M.assert.A. map designed in 1979', - width: '1050', - height: '550' + width: '1050' }, modified: '2019-12-03T19:13:00.000Z', name: 'The New York Times', @@ -1105,7 +1105,7 @@ test('rehypeMeta', async function (t) { '', 'The New York City Subway Map as You’ve Never Seen It Before | The New York Times', '', - '', + '', '', '', '', @@ -1113,9 +1113,9 @@ test('rehypeMeta', async function (t) { '', '', '', - '', + '', '', - '', + '', '', '', '', @@ -1130,7 +1130,7 @@ test('rehypeMeta', async function (t) { '', '', '', - '', + '', '', '', '', @@ -1160,9 +1160,9 @@ test('rehypeMeta', async function (t) { description: 'Crispy Sea Salt and Vinegar Roasted Potatoes. These are so crisp and flavorful, you’ll want to eat them as a side dish for every meal!', image: { + height: '1012', url: 'https://hostthetoast.com/wp-content/uploads/2014/06/Salt-and-Vinegar-Potatoes-6.jpg', - width: '670', - height: '1012' + width: '670' }, modified: '2017-04-26T22:37:10-05:00', name: 'Host The Toast',