diff --git a/docs/components/content/BlockHero.vue b/docs/components/content/BlockHero.vue index f7c456a33..e72efb7bd 100644 --- a/docs/components/content/BlockHero.vue +++ b/docs/components/content/BlockHero.vue @@ -34,20 +34,22 @@ defineProps({

- + {{ cta[0] }} + {{ secondary[0] }}
+
- +
diff --git a/docs/components/content/ExampleMultiselect.vue b/docs/components/content/ExampleMultiselect.vue new file mode 100644 index 000000000..755f37d4d --- /dev/null +++ b/docs/components/content/ExampleMultiselect.vue @@ -0,0 +1,19 @@ + + + diff --git a/docs/content-v1/en/1.getting-started/1.introduction.md b/docs/content-v1/en/1.getting-started/1.introduction.md new file mode 100644 index 000000000..9728be7bd --- /dev/null +++ b/docs/content-v1/en/1.getting-started/1.introduction.md @@ -0,0 +1,63 @@ +--- +title: Introduction +description: 'Empower your NuxtJS application with the @nuxt/content module: write in a content/ directory and fetch your Markdown, JSON, YAML and CSV files through a MongoDB-like API, acting as a Git-based Headless CMS.' +--- + +Empower your NuxtJS application with the `@nuxt/content` module: write in a `content/` directory and fetch your Markdown, JSON, YAML, XML and CSV files through a MongoDB-like API, acting as a **Git-based Headless CMS**. + +## Features + +::list +- Blazing fast hot reload in development +- Vue components in Markdown +- Full-text search +- Support static site generation with `nuxt generate` +- Powerful QueryBuilder API (MongoDB like) +- Syntax highlighting to code blocks in markdown files using PrismJS. +- Table of contents generation +- Handles Markdown, CSV, YAML, JSON(5), XML +- Extend with custom parsers +- Extend with hooks +:: + +## Videos + +Demonstration of using `$content` and `` to display Markdown pages: + + + +Using `$content()` on a directory to list, filter and search content: + + + +## Tutorial + +[Create a blog with Nuxt Content](https://nuxtjs.org/blog/creating-blog-with-nuxt-content) + +## Testimonials + + + + + + + + + + + + + + + + + + diff --git a/docs/content-v1/en/1.getting-started/2.installation.md b/docs/content-v1/en/1.getting-started/2.installation.md new file mode 100644 index 000000000..ea5056357 --- /dev/null +++ b/docs/content-v1/en/1.getting-started/2.installation.md @@ -0,0 +1,50 @@ +--- +title: Installation +description: 'Install @nuxt/content in only two steps in your Nuxt project.' +--- + +Add `@nuxt/content` dependency to your project: + +::code-group + ```bash [Yarn] + yarn add @nuxt/content + ``` + + ```bash [NPM] + npm install @nuxt/content + ``` +:: + +Then, add `@nuxt/content` to the `modules` section of `nuxt.config.js`: + +```js [nuxt.config.js] +{ + modules: [ + '@nuxt/content' + ], + content: { + // Options + } +} +``` + +## TypeScript + +Add the types to your "types" array in tsconfig.json after the `@nuxt/types` (Nuxt 2.9.0+) or `@nuxt/vue-app` entry. + +**tsconfig.json** + +```json +{ + "compilerOptions": { + "types": [ + "@nuxt/types", + "@nuxt/content" + ] + } +} +``` + +> **Why?** +> +> Because of the way Nuxt works the `$content` property on the context has to be merged into the Nuxt `Context` interface via [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html). Adding `@nuxt/content` to your types will import the types from the package and make TypeScript aware of the additions to the `Context` interface. diff --git a/docs/content-v1/en/1.getting-started/3.writing.md b/docs/content-v1/en/1.getting-started/3.writing.md new file mode 100644 index 000000000..5b326f42d --- /dev/null +++ b/docs/content-v1/en/1.getting-started/3.writing.md @@ -0,0 +1,670 @@ +--- +title: Writing content +description: 'Learn how to write your content/, supporting Markdown, YAML, CSV and JSON.' +multiselectOptions: + - VuePress + - Gridsome + - Nuxt +--- + +First of all, create a `content/` directory in your project: + +```bash +content/ + articles/ + article-1.md + article-2.md + home.md +``` + +This module will parse `.md`, `.yaml`, `.yml`, `.csv`, `.json`, `.json5`, `.xml` files and generate the following properties: + +- `dir` +- `path` +- `slug` +- `extension` (ex: `.md`) +- `createdAt` +- `updatedAt` + +The `createdAt` and `updatedAt` properties are based on the file's actual created & updated datetime, but you can override them by defining your own `createdAt` and `updatedAt` values. This is especially useful if you are migrating your past blog posts where the `createdAt` can be months or years ago. + +## Markdown + +This module converts your `.md` files into a JSON AST tree structure, stored in a `body` variable. + +Make sure to use the `` component to display the `body` of your markdown content, see [displaying content](/displaying). + +> You can check the [basic syntax guide](https://www.markdownguide.org/basic-syntax) to help you master Markdown + +### Front Matter + +You can add a YAML front matter block to your markdown files. The front matter must be the first thing in the file and must take the form of valid YAML set between triple-dashed lines. Here is a basic example: + +```md +--- +title: Introduction +description: Learn how to use @nuxt/content. +--- +``` + +These variables will be injected into the document: + +```json +{ + body: Object + excerpt: Object + title: "Introduction" + description: "Learn how to use @nuxt/content." + dir: "/" + extension: ".md" + path: "/index" + slug: "index" + toc: Array + createdAt: DateTime + updatedAt: DateTime +} +``` + +### Excerpt + +Content excerpt or summary can be extracted from the content using `` as a divider. + +```md +--- +title: Introduction +--- + +Learn how to use @nuxt/content. + +Full amount of content beyond the more divider. +``` + +Description property will contain the excerpt content unless defined within the Front Matter props. + + + +Be careful to enter <!--more--> exactly; i.e., all lowercase and with no whitespace. + + + +Example variables will be injected into the document: + +```json +{ + body: Object + title: "Introduction" + description: "Learn how to use @nuxt/content." + dir: "/" + excerpt: Object + extension: ".md" + path: "/index" + slug: "index" + toc: Array + createdAt: DateTime + updatedAt: DateTime +} +``` + +### Headings + +This module automatically adds an `id` and a `link` to each heading. + +Say we have the following markdown file: + +```md[home.md] +# Lorem ipsum +## dolor—sit—amet +### consectetur & adipisicing +#### elit +##### elit +``` + +It will be transformed to its JSON AST structure, and by using the `nuxt-content` component, it will render HTML like: + +```html +

Lorem ipsum

+

dolor—sit—amet

+

consectetur & adipisicing

+

elit

+
elit
+``` + +> The links in headings are empty and therefore hidden, so it's up to you to style them. For an example, try hovering one of the headers in these docs. + +### Links + +Links are transformed to add valid `target` and `rel` attributes using [remark-external-links](https://github.com/remarkjs/remark-external-links). You can check [here](/configuration#markdown) to learn how to configure this plugin. + +Relative links are also automatically transformed to [nuxt-link](https://nuxtjs.org/api/components-nuxt-link/) to provide navigation between page components with enhanced performance through smart prefetching. + +Here is an example using external, relative, markdown and html links: + +```md +--- +title: Home +--- + +## Links + +Nuxt Link to Blog + +Html Link to Blog + +[Markdown Link to Blog](/articles) + +External link html + +[External Link markdown](https://nuxtjs.org) +``` + +### Footnotes + +This module supports extended markdown syntax for footnotes using [remark-footnotes](https://github.com/remarkjs/remark-footnotes). You can check [here](/configuration#markdown) to learn how to configure this plugin. + +Here is an example using footnotes: + +```md +Here's a simple footnote,[^1] and here's a longer one.[^bignote] + +[^1]: This is the first footnote. + +[^bignote]: Here's one with multiple paragraphs and code. + + Indent paragraphs to include them in the footnote. + + `{ my code }` + + Add as many paragraphs as you like. +``` + +> You can check the [extended syntax guide](https://www.markdownguide.org/extended-syntax/#footnotes) for more information about footnotes. + +### Codeblocks + +This module automatically wraps codeblocks and applies [PrismJS](https://prismjs.com) classes (see [syntax highlighting](/writing#syntax-highlighting)). + +Codeblocks in Markdown are wrapped inside 3 backticks. Optionally, you can define the language of the codeblock to enable specific syntax highlighting. + +Originally markdown did not support filenames or highlighting specific lines inside codeblocks. However, this module allows it with its own custom syntax: + +- Highlighted line numbers inside curly braces +- Filename inside square brackets + +
+```js{1,3-5}[server.js]
+const http = require('http')
+const bodyParser = require('body-parser')
+
+http.createServer((req, res) => {
+  bodyParser.parse(req, (error, body) => {
+    res.end(body)
+  })
+}).listen(3000)
+```
+
+ +After rendering with the `nuxt-content` component, it should look like this (without the filename yet): + +```html[server.js] +
+ server.js +
+    
+      ...
+    
+  
+
+``` + +Line numbers are added to the `pre` tag in `data-line` attribute. + +> Check out [this comment](https://github.com/nuxt/content/issues/28#issuecomment-633246962) on how to render prism line numbers. + +Filename will be converted to a span with a `filename` class. It's up to you to style it. + +> Check out [the main.css file](https://github.com/nuxt/content/blob/dev/packages/theme-docs/src/assets/css/main.css#L56) of this documentation for an example on styling filenames. + +### Syntax highlighting + +It supports by default code highlighting using [PrismJS](https://prismjs.com) and injects the theme defined in options into your Nuxt.js app, see [configuration](/configuration#markdownprismtheme). + +### HTML + +You can write HTML in your Markdown: + +```md[home.md] +--- +title: Home +--- + +## HTML + +

A mix of Markdown and HTML.

+``` + +Beware that when placing Markdown inside a component, it must be preceded and followed by an empty line, otherwise the whole block is treated as custom HTML. + +**This won't work:** + +```html +
+ *Markdown* and HTML. +
+``` + +**But this will:** + +```html +
+ + *Markdown* and HTML. + +
+``` + +**As will this**: + +```html +*Markdown* and HTML. +``` + +### Vue components + +You can use global Vue components or locally registered in the page you're displaying your markdown. + + + +An issue exists with locally registered components and live edit in development, since **v1.5.0** you can disable it by setting `liveEdit: false` (see [configuration](/configuration#liveedit)). + + + +Since `@nuxt/content` operates under the assumption that all Markdown is provided by the author (and not via third-party user submission), sources are processed in full (tags included), with a couple of caveats from [rehype-raw](https://github.com/rehypejs/rehype-raw): + +1. You need to refer to your components and their props by kebab case naming: + +```html +Use instead of +``` + +2. You cannot use self-closing tags, i.e., **this won't work**: + +```html + +``` + +But **this will**: + +```html + +``` + +**Example** + +Say we have a Vue component called [ExampleMultiselect.vue](https://github.com/nuxt/content/blob/master/docs/components/global/examples/ExampleMultiselect.vue): + +```md[home.md] +Please choose a *framework*: + + +``` + +**Result** + +
+Please choose a framework: + + + +
+ +You can also define the options for components in your front matter: + +```md[home.md] +--- +multiselectOptions: + - VuePress + - Gridsome + - Nuxt +--- + + +``` + +
+ + + +
+ + + +These components will be rendered using the `` component, see [displaying content](/displaying#component). + + + +#### Templates + +You can use `template` tags for content distribution inside your Vue.js components: + +```html + + + +``` + +However, you cannot render +[dynamic content](https://vuejs.org/v2/guide/syntax.html) nor use +[slot props](https://vuejs.org/v2/guide/components-slots.html#Scoped-Slots). I.e., +**this wont work**: + +```html + + + +``` + +#### Global components + +Since **v1.4.0** and Nuxt **v2.13.0**, you can now put your components in `components/global/` directory so you don't have to import them in your pages. + +```bash +components/ + global/ + Hello.vue +content/ + home.md +``` + +Then in `content/home.md`, you can use `` component without having to worry about importing it in your page. + +### Table of contents + +When fetching a document, we have access to a toc property which is an array of all the titles. Each title has an `id` so that it is possible to link to, a depth which is the type of heading it is. Only h2 and h3 titles are used for the toc. There is also a text property which is the text of the title. + +```json +{ + "toc": [{ + "id": "welcome", + "depth": 2, + "text": "Welcome!" + }] +} +``` + +> Take a look at the right side of this page for an example. + + + +Check out [this snippet](/snippets#table-of-contents) on how to implement a table of contents into your app + + + +### Example + +A file `content/home.md`: + +```md +--- +title: Home +--- + +## Welcome! +``` + +Will be transformed into: + +```json +{ + "dir": "/", + "slug": "home", + "path": "/home", + "extension": ".md", + "title": "Home", + "toc": [ + { + "id": "welcome", + "depth": 2, + "text": "Welcome!" + } + ], + "body": { + "type": "root", + "children": [ + { + "type": "element", + "tag": "h2", + "props": { + "id": "welcome" + }, + "children": [ + { + "type": "element", + "tag": "a", + "props": { + "ariaHidden": "true", + "href": "#welcome", + "tabIndex": -1 + }, + "children": [ + { + "type": "element", + "tag": "span", + "props": { + "className": [ + "icon", + "icon-link" + ] + }, + "children": [] + } + ] + }, + { + "type": "text", + "value": "Welcome!" + } + ] + } + ] + } +} +``` + +We internally add a `text` key with the markdown body that will be used for [searching](/fetching#searchfield-value) or [extending](/advanced#contentfilebeforeinsert) it. + +## JSON / JSON5 + +Data defined will be injected into the document. + +> No body will be generated. + +### Arrays + +v0.10.0+ + +You can now use arrays inside your `.json` files. Objects will be flattened and inserted into the collection. You can [fetch](/fetching) your content in the same way as your used to. + + + +Since the `slug` is by default taken from the path and missing in this case, you have to define it in your objects for this feature to work properly. + + + +> Check out our [example](https://github.com/nuxt/content/tree/dev/example) with articles and authors. + +### Example + +A file `content/home.json`: + +```json +{ + "title": "Home", + "description": "Welcome!" +} +``` + +Will be transformed into: + +```json +{ + "dir": "/", + "slug": "home", + "path": "/home", + "extension": ".json", + "title": "Home", + "description": "Welcome!" +} +``` + +A file `content/authors.json`: + +```json +[ + { + "name": "Sébastien Chopin", + "slug": "atinux" + }, + { + "name": "Krutie Patel", + "slug": "krutiepatel" + }, + { + "name": "Sergey Bedritsky", + "slug": "sergeybedritsky" + } +] +``` + +Will be transformed into: + +```json +[ + { + "name": "Sébastien Chopin", + "slug": "atinux", + "dir": "/authors", + "path": "/authors/atinux", + "extension": ".json" + }, + { + "name": "Krutie Patel", + "slug": "krutiepatel", + "dir": "/authors", + "path": "/authors/krutiepatel", + "extension": ".json" + }, + { + "name": "Sergey Bedritsky", + "slug": "sergeybedritsky", + "dir": "/authors", + "path": "/authors/sergeybedritsky", + "extension": ".json" + } +] +``` + +## CSV + +Rows will be assigned to body variable. + +### Example + +A file `content/home.csv`: + +```csv +title, description +Home, Welcome! +``` + +Will be transformed into: + +```json +{ + "dir": "/", + "slug": "home", + "path": "/home", + "extension": ".csv", + "body": [ + { + "title": "Home", + "description": "Welcome!" + } + ] +} +``` + +## XML + +XML will be parsed + +### Example + +A file `content/home.xml`: + +```xml + + + Title + Hello World + + +``` + +Will be transformed into: + +```json +{ + "dir": "/", + "slug": "home", + "path": "/home", + "extension": ".xml", + "body": { + "xml": { + "item": [ + { + "$": { + "prop": "abc" + }, + "title": [ + "Title" + ], + "description": [ + "Hello World" + ] + } + ] + } +} +``` + +## YAML / YML + +Data defined will be injected into the document. + +> No body will be generated. + +### Example + +A file `content/home.yaml`: + +```yaml +title: Home +description: Welcome! +``` + +Will be transformed into: + +```json +{ + "dir": "/", + "slug": "home", + "path": "/home", + "extension": ".yaml", + "title": "Home", + "description": "Welcome!" +} +``` diff --git a/docs/content-v1/en/1.getting-started/4.fetching.md b/docs/content-v1/en/1.getting-started/4.fetching.md new file mode 100644 index 000000000..aa46fbbe8 --- /dev/null +++ b/docs/content-v1/en/1.getting-started/4.fetching.md @@ -0,0 +1,252 @@ +--- +title: Fetching content +description: 'Learn how to fetch your static content with $content in your Nuxt.js project.' +--- + +This module globally injects `$content` instance, meaning that you can access it anywhere using `this.$content`. For plugins, asyncData, nuxtServerInit and Middleware, you can access it from `context.$content`. + +## Methods + +### $content(path, options?) + +- `path` + - Type: `String` + - Default: `/` +- `options` + - Type: `Object` + - Default: `{}` + - Version: **>= v1.3.0** +- `options.deep` + - Type: `Boolean` + - Default: `false` + - Version: **>= v1.3.0** + - *Fetch files from subdirectories* +- `options.text` + - Type: `Boolean` + - Default: `false` + - Version: **>= v1.4.0** + - *Returns the original markdown content in a `text` variable* +- Returns a chain sequence + +> You can also give multiple arguments: `$content('articles', params.slug)` will be translated to `/articles/${params.slug}` + +`path` can be a file or a directory. If `path` is a file, `fetch()` will return an `Object`, if it's a directory it will return an `Array`. + +All the methods below can be chained and return a chain sequence, except `fetch` which returns a `Promise`. + +### only(keys) + +- `keys` + - Type: `Array` | `String` + - `required` + +Select a subset of fields. + +```js +const { title } = await this.$content('article-1').only(['title']).fetch() +``` + +### without(keys) + +- `keys` + - Type: `Array` | `String` + - `required` + +Remove a subset of fields. + +```js +const { title, ...propsWithoutBody } = await this.$content('article-1').without(['body']).fetch() +``` + +### where(query) + +- `query` + - Type: `Object` + - `required` + +Filter results by query. + +Where queries are based on subset of mongo query syntax, it handles for example: `$eq`, `$ne`, `$gt`, `$gte`, `$lt`, `$lte`, `$in`, ... + +```js +// implicit (assumes $eq operator) +const articles = await this.$content('articles').where({ title: 'Home' }).fetch() +// explicit $eq +const articles = await this.$content('articles').where({ title: { $eq: 'Home' } }).fetch() + +// $gt +const articles = await this.$content('articles').where({ age: { $gt: 18 } }).fetch() +// $in +const articles = await this.$content('articles').where({ name: { $in: ['odin', 'thor'] } }).fetch() +``` + +In order to filter in objects and array you need to enable nestedProperties, see [configuration](/configuration#nestedproperties). + +```js +const products = await this.$content('products').where({ 'categories.slug': { $contains: 'top' } }).fetch() + +const products = await this.$content('products').where({ 'categories.slug': { $contains: ['top', 'woman'] } }).fetch() +``` + +> This module uses LokiJS under the hood, you can check for [query examples](https://github.com/techfort/LokiJS/wiki/Query-Examples#find-queries). + +### sortBy(key, direction) + +- `key` + - Type: `String` + - `required` +- `direction` + - Type: `String` + - Value: `'asc'` or `'desc'` + - Default: `'asc'` + +Sort results by key. + +```js +const articles = await this.$content('articles').sortBy('title').fetch() +``` + +> Can be chained multiple times to sort on multiple fields. + + + +`sortBy` method does **case-sensitive** sort, which is currently not configurable. + +If you need case-insensitive sorting, check out [this snippet](/snippets#case-insensitive-sorting) on how to work around it. + + + +### limit(n) + +- `n` + - Type: `String` | `Number` + - `required` + +Limit number of results. + +```js +// fetch only 5 articles +const articles = await this.$content('articles').limit(5).fetch() +``` + +### skip(n) + +- `n` + - Type: `String` | `Number` + - `required` + +Skip results. + +```js +// fetch the next 5 articles +const articles = await this.$content('articles').skip(5).limit(5).fetch() +``` + +### search(field, value) + +- `field` + - Type: `String` + - `required` +- `value` + - Type: `String` + +Performs a full-text search on a field. `value` is optional, in this case `field` is the `value` and search will be performed on all defined full-text search fields. + +The fields you want to search on must be defined in options in order to be indexed, see [configuration](/configuration#fulltextsearchfields). + +Using an empty string as parameter will skip the search. + +```js +// Search on field title +const articles = await this.$content('articles').search('title', 'welcome').fetch() +// Search on all pre-defined fields +const articles = await this.$content('articles').search('welcome').fetch() +// Search will be skipped if the search string is empty +const articles = await this.$content('articles').search('').fetch() +``` + + + +Check out [this snippet](/snippets#search) on how to implement search into your app + + + +### surround(slugOrPath, options) + +- `slugOrPath` + - Type: `String` + - `required` +- `options` + - Type: `Object` + - Default: `{ before: 1, after: 1}` + +Get prev and next results around a specific slug or path. + +You will always obtain an array of fixed length filled with the maching document or `null`. + +```js +const [prev, next] = await this.$content('articles') + .only(['title', 'path']) + .sortBy('date') + .where({ isArchived: false }) + .surround('article-2') + .fetch() + +// Returns +[ + { + title: 'Article 1', + path: 'article-1' + }, + null // no article-3 here +] +``` + +> `search`, `limit` and `skip` are ineffective when using this method. + + + +Getting results based on `path` is only supported since v1.12.0 + + + + + +Check out [this snippet](/snippets#prev-and-next) on how to implement prev and next links into your app + + + +### fetch() + +- Returns: `Promise` | `Promise` + +Ends the chain sequence and collects data. + +### catch() + +Checks if the `.md` file exists in content directory or not. + +It should be inserted after the `fetch()`. + +## Example + +```js +const articles = await this.$content('articles') + .only(['title', 'date', 'authors']) + .sortBy('date', 'asc') + .limit(5) + .skip(10) + .where({ + tags: 'testing', + isArchived: false, + date: { $gt: new Date('2020-03-31') }, + rating: { $gte: 3 } + }) + .search('welcome') + .fetch() + .catch((err) => { + error({ statusCode: 404, message: 'Page not found' }) + }) +``` + +> You can check how to use the [Content API](/advanced#api-endpoint) in development. diff --git a/docs/content-v1/en/1.getting-started/5.displaying.md b/docs/content-v1/en/1.getting-started/5.displaying.md new file mode 100644 index 000000000..ce7d016d1 --- /dev/null +++ b/docs/content-v1/en/1.getting-started/5.displaying.md @@ -0,0 +1,102 @@ +--- +title: Displaying content +description: 'You can use `` component directly in your template to display your Markdown.' +--- + +This section is only for Markdown files. + +## Component + +### Page Body + +You can use `` component directly in your template to display the page body: + +```vue + + + +``` + + +**Props:** +- document: + - Type: `Object` + - `required` +- tag: + - Type: `String` + +Learn more about what you can write in your markdown file in the [writing content](/writing#markdown) section. + +### Excerpt + +If you are utilizing the [excerpt](/writing#excerpt) feature, you can display the content of your excerpt using the following model: + +```vue + + + +``` + +## Root Element + +`` component will add a `div` element as the root of the content by default. You can change this by setting the `tag` prop. Below example will use `article` as the root element. + +```vue + +``` + +## Style + +Depending on what you're using to design your app, you may need to write some style to properly display the markdown. + +`` component will automatically add a `.nuxt-content` class. You can use it to customize your styles: + +```css +.nuxt-content h1 { + /* my custom h1 style */ +} +``` + +> You can find an example in the theme-docs [main.css](https://github.com/nuxt/content/blob/master/packages/theme-docs/src/assets/css/main.css) file. You can also take a look at the [TailwindCSS Typography plugin](https://tailwindcss.com/docs/typography-plugin) to style your markdown content like we do in the `@nuxt/content-theme-docs`. + +## Live Editing + +> Available in version **>= v1.4.0** + +**In development**, you can edit your content by **double-clicking** on the `` component. A textarea will allow you to edit the content of the current file and will save it on the file-system. + + diff --git a/docs/content-v1/en/1.getting-started/6.configuration.md b/docs/content-v1/en/1.getting-started/6.configuration.md new file mode 100644 index 000000000..b6894a7de --- /dev/null +++ b/docs/content-v1/en/1.getting-started/6.configuration.md @@ -0,0 +1,546 @@ +--- +title: Configuration +description: 'You can configure @nuxt/content with the content property in your nuxt.config.js.' +--- + +You can configure `@nuxt/content` with the `content` property in your `nuxt.config.js`. + +```js [nuxt.config.js] +export default { + content: { + // My custom configuration + } +} +``` + +Before diving into the individual attributes, please have a look [at the default settings](#defaults) of the module. + +## Merging defaults + +You can define every option either as function or as static value (primitives, objects, arrays, ...). +If you use a function, the default value will be provided as the first argument. + +If you *don't* use a function to define you properties, the module will try to +merge them with the default values. This can be handy for `markdown.remarkPlugins`, `markdown.rehypePlugins` and so on because +the defaults are quite sensible. If you don't want to have the defaults included, just use a function. + +## Properties + +### `apiPrefix` + +- Type: `String` +- Default: `'/_content'` + +Route that will be used for client-side API calls and SSE. + +```js [nuxt.config.js] +content: { + // $content api will be served on localhost:3000/content-api + apiPrefix: 'content-api' +} +``` + +### `dir` + +- Type: `String` +- Default: `'content'` + +Directory used for writing content. +You can give an absolute path, if relative, it will be resolved with Nuxt [srcDir](https://nuxtjs.org/api/configuration-srcdir). + +```js [nuxt.config.js] +content: { + dir: 'my-content' // read content from my-content/ +} +``` + +### `fullTextSearchFields` + +- Type: `Array` +- Default: `['title', 'description', 'slug', 'text']` + +Fields that needs to be indexed to be searchable, learn more about search [here](/fetching#searchfield-value). + +`text` is a special key that contains your Markdown before being parsed to AST. + +```js [nuxt.config.js] +content: { + // Only search in title and description + fullTextSearchFields: ['title', 'description'] +} +``` + +### `nestedProperties` + +- Type `Array` +- Default: `[]` +- Version: **>= v1.3.0** + +Register nested properties to handle dot-notation and deep filtering. + +```js [nuxt.config.js] +content: { + nestedProperties: ['categories.slug'] +} +``` + +### `liveEdit` + +- Type `Boolean` +- Default: `true` +- Version: **>= v1.5.0** + +Disable live edit mode in development: + +```js [nuxt.config.js] +content: { + liveEdit: false +} +``` + +### `markdown` + +This module uses [remark](https://github.com/remarkjs/remark) and [rehype](https://github.com/rehypejs/rehype) under the hood to compile markdown files into JSON AST that will be stored into the `body` variable. + +::alert{type="info"} +The following explanation is valid for both `remarkPlugins` and `rehypePlugins` +:: + +To configure how the module will parse Markdown, you can: + +- Add a new plugin to the defaults: + +```js [nuxt.config.js] +export default { + content: { + markdown: { + remarkPlugins: ['remark-emoji'] + } + } +} +``` + +- Override the default plugins: + +```js [nuxt.config.js] +export default { + content: { + markdown: { + remarkPlugins: () => ['remark-emoji'] + } + } +} +``` + +- Use local plugins + +```js [nuxt.config.js] +export default { + content: { + markdown: { + remarkPlugins: [ + '~/plugins/my-custom-remark-plugin.js' + ] + } + } +} +``` + +- Provide options directly in the definition + +```js [nuxt.config.js] +export default { + content: { + markdown: { + remarkPlugins: [ + ['remark-emoji', { emoticon: true }] + ] + } + } +} +``` + +- Provide options using the name of the plugin in `camelCase` + +```js [nuxt.config.js] +export default { + content: { + markdown: { + // https://github.com/remarkjs/remark-external-links#options + remarkExternalLinks: { + target: '_self', + rel: 'nofollow' + } + } + } +} +``` + +::alert{type="warning"} +When adding a new plugin, make sure to install it in your dependencies: +:: + +::code-group + ```bash [Yarn] + yarn add remark-emoji + ``` + + ```bash [NPM] + npm install remark-emoji + ``` +:: + +```js [nuxt.config.js] +export default { + content: { + markdown: { + remarkPlugins: ['remark-emoji'] + } + } +} +``` + +### `markdown.tocDepth` + +- Type: `Number` +- Default: `3` +- Version: **>= v1.11.0** + +You can change maximum heading depth to include in the table of contents. + +### `markdown.remarkPlugins` + +- Type: `Array` +- Default: `['remark-squeeze-paragraphs', 'remark-slug', 'remark-autolink-headings', 'remark-external-links', 'remark-footnotes']` +- Version: **>= v1.4.0** + +> You can take a look at the list of [remark plugins](https://github.com/remarkjs/remark/blob/master/doc/plugins.md#list-of-plugins). + +### `markdown.rehypePlugins` + +- Type: `Array` +- Default: `['rehype-minify-whitespace', 'rehype-sort-attribute-values', 'rehype-sort-attributes', 'rehype-raw']` +- Version: **>= v1.4.0** + +> You can take a look at the list of [rehype plugins](https://github.com/rehypejs/rehype/blob/master/doc/plugins.md#list-of-plugins). + +### `markdown.basePlugins` + +::alert{type="warning"} +Deprecated. Use `markdown.remarkPlugins` as a function instead. +:: + +### `markdown.plugins` + +::alert{type="warning"} +Deprecated. Use `markdown.remarkPlugins` as an array instead. +:: + +### `markdown.prism.theme` + +- Type: `String` +- Default: `'prismjs/themes/prism.css'` + +This module handles code highlighting in markdown content using [PrismJS](https://prismjs.com). + +It automatically pushes the desired PrismJS theme in your Nuxt.js config, so if you want to use a different theme than the default one, for example [prism-themes](https://github.com/PrismJS/prism-themes): + +::code-group + ```bash [Yarn] + yarn add prism-themes + ``` + + ```bash [NPM] + npm install prism-themes + ``` +:: + +```js [nuxt.config.js] +content: { + markdown: { + prism: { + theme: 'prism-themes/themes/prism-material-oceanic.css' + } + } +} +``` + +To disable the inclusion of the theme, set prism to `false`: + +```js [nuxt.config.js] +content: { + markdown: { + prism: { + theme: false + } + } +} +``` + +### `markdown.highlighter` + +- Type: `Highlighter` | `PromisedHighlighter` +- Version: **>=1.9.0** + +You can change the default code highlighter in markdown content by using this option. As an example, we use [highlight.js](https://highlightjs.org/). + +```js [nuxt.config.js] +import highlightjs from 'highlight.js' + +export default { + content: { + markdown: { + highlighter(rawCode, lang) { + const highlightedCode = highlightjs.highlight(rawCode, { language: lang }).value + + // We need to create a wrapper, because + // the returned code from highlight.js + // is only the highlighted code. + return `
${highlightedCode}
` + } + } + } +} +``` + + + +When `markdown.highlighter` is defined, it will automatically disable the inclusion of the Prism theme. + + + + + +Don't forget to add the corresponding style manually if you define `markdown.highlighter`. + + + +It returns a `string` or [HAST](https://github.com/syntax-tree/hast) (Hypertext Abstract Syntax Tree). You can build HAST by passing the 4th argument. It consits of `h`, `node` and `u`. + +```js [nuxt.config.js] +import highlightjs from 'highlight.js' + +export default { + content: { + markdown: { + highlighter(rawCode, lang, _, { h, node, u }) { + const highlightedCode = highlightjs.highlight(rawCode, { language: lang }).value + + // We can use ast helper to create the wrapper + const childs = [] + childs.push( + h(node, 'pre', [ + h(node, 'code', { className: ['hljs', lang] }, [ + u('raw', highlightedCode) + ]) + ]) + ) + + return h( + node, + 'div', + { className: 'highlighted-with-highlightjs' }, + childs + ) + } + } + } +} +``` + +After rendering with the `nuxt-content` component, it will look like this: + +```html +
+
+    
+      ...
+    
+  
+
+``` + +You can also get the line highlight and file name value from the 3rd argument. Combining them with the HAST, you can pass it to the client. + +```js [nuxt.config.js] +import highlightjs from 'highlight.js' + +export default { + content: { + markdown: { + highlighter(rawCode, lang, { lineHighlights, fileName }, { h, node, u }) { + const highlightedCode = highlightjs.highlight(rawCode, { language: lang }).value + + const childs = [] + const props = { + className: `language-${lang}`, + dataLine: lineHighlights, + dataFileName: fileName + } + childs.push( + h(node, 'pre', [ + h(node, 'code', props, [ + u('raw', highlightedCode) + ]) + ]) + ) + + return h( + node, + 'div', + { className: 'highlighted-with-highlightjs' }, + childs + ) + } + } + } +} +``` + +Then the returned code will look like this: + +```html +
+
+    
+      ...
+    
+  
+
+``` + +> You can learn more about `h`, `node` and `u` from [mdast-util-to-hast](https://github.com/syntax-tree/mdast-util-to-hast), [Universal Syntax Tree](https://github.com/syntax-tree/unist#node) and [unist-builder](https://github.com/syntax-tree/unist-builder) + +If you need to get the highlighter from promised-returned-package/function, you can do it this way: + +```js [nuxt.config.js] +import { getHighlighter } from 'example-highlighter' + +export default { + content: { + markdown: { + async highlighter() { + const highlighter = await getHighlighter() + + return (rawCode, lang) => { + return highlighter.highlight(rawCode, { language: lang }) + } + } + } + } +} +``` + +> You can head over to [Snippets - Custom Highlighter](/snippets#custom-highlighter) section to see more example. + +### `yaml` + +- Type: `Object` +- Default: `{}` + +This module uses `js-yaml` to parse `.yaml`, `.yml` files. You can check here for [options](https://github.com/nodeca/js-yaml#api). + +Note that we force `json: true` option. + + +### `xml` + +- Type: `Object` +- Default: `{}` + +This module uses `xml2js` to parse `.xml` files. You can check here for [options](https://www.npmjs.com/package/xml2js#options). + +### `csv` + +- Type: `Object` +- Default: `{}` + +This module uses `node-csvtojson` to parse csv files. You can check here for [options](https://github.com/Keyang/node-csvtojson#parameters). + +### `extendParser` + +- Type: `Object` +- Default `{}` + +With this option you can define your own parsers for other file types. Also you can **overwrite** the default parser! + +To add your custom parser write a function that gets as an argument the content of the file and returns the extracted data. + +**Example** + +```js [nuxt.config.js] +const parseTxt = file => file.split('\n').map(line => line.trim()) + +// in Config: + +{ + extendParser: { + '.txt': parseTxt + } +} +``` + +### `editor` + +You can provide a custom editor for editing your markdown files in development. Set the `editor` option to a path to your editor component. The code of the default editor you can find [here](https://github.com/nuxt/content/blob/master/packages/content/templates/editor.vue). + + +```js [nuxt.config.js] +content: { + editor: '~/path/to/editor/component.vue' +} +``` + +Your component should implement the following: + +1. `v-model` for getting the markdown code. +2. prop `isEditing` is a boolean with the information if the editing is started and the component is shown. (this is optional) +3. when finished editing your component has to emit `endEdit` + + +You should be aware that you get the full markdown file content so this includes the front-matter. You can use `gray-matter` to split and join the markdown and the front-matter. + +### `useCache` + +- Type: `Boolean` +- Default: `false` + +When `true`, the production server (`nuxt start`) will use cached version of the content (generated after running `nuxt build`) instead of parsing files. This improves app startup time, but makes app unaware of any content changes. + +## Defaults + +```js [nuxt.config.js] +export default { + content: { + editor: '~/.nuxt/content/editor.vue', + apiPrefix: '_content', + dir: 'content', + fullTextSearchFields: ['title', 'description', 'slug', 'text'], + nestedProperties: [], + liveEdit: true, + useCache: false, + markdown: { + remarkPlugins: [ + 'remark-squeeze-paragraphs', + 'remark-slug', + 'remark-autolink-headings', + 'remark-external-links', + 'remark-footnotes' + ], + rehypePlugins: [ + 'rehype-minify-whitespace', + 'rehype-sort-attribute-values', + 'rehype-sort-attributes', + 'rehype-raw' + ], + prism: { + theme: 'prismjs/themes/prism.css' + } + }, + yaml: {}, + csv: {}, + xml: {}, + extendParser: {} + } +} +``` diff --git a/docs/content-v1/en/1.getting-started/7.advanced.md b/docs/content-v1/en/1.getting-started/7.advanced.md new file mode 100644 index 000000000..7c68e4bf9 --- /dev/null +++ b/docs/content-v1/en/1.getting-started/7.advanced.md @@ -0,0 +1,253 @@ +--- +title: Advanced +description: 'Learn advanced usage of @nuxt/content module.' +--- + +## Programmatic Usage + +`$content` is accessible from **@nuxt/content**. + + + +Beware that you can access it only **after the module has been loaded** by Nuxt. `require('@nuxt/content')` should happen in hooks or internal Nuxt methods. + + + +```js +export default { + modules: [ + '@nuxt/content' + ], + generate: { + async ready () { + const { $content } = require('@nuxt/content') + const files = await $content().only(['slug']).fetch() + console.log(files) + } + } +} +``` + +### Static Site Generation + +Since Nuxt 2.14+, `nuxt generate` has a crawler feature integrated which will crawl all your links and generate your routes based on those links. Therefore you do not need to do anything in order for your dynamic routes to be crawled. + +Also, `nuxt generate` will automagically skip webpack build step when no code has been changed and use the previous build using cache. The content module integrates with this feature to ignore changes inside the `content/` folder. In other terms, when changing the content of your site and deploying, the build will be skipped. + +> Learn more in [this article](https://nuxtjs.org/blog/nuxt-static-improvements). + +When using Nuxt <= 2.12, you might need to specify the dynamic routes with [generate.routes](https://nuxtjs.org/api/configuration-generate/#routes) + +**Example** + +```js{}[nuxt.config.js] +export default { + modules: [, + '@nuxt/content' + ], + generate: { + async routes () { + const { $content } = require('@nuxt/content') + const files = await $content({ deep: true }).only(['path']).fetch() + + return files.map(file => file.path === '/index' ? '/' : file.path) + } + } +} +``` + + + +Recommended to use Nuxt 2.14+ with `nuxt generate` because it's awesome! + + + +## Hooks + +The module adds some hooks you can use: + +### `content:file:beforeParse` + +Allows you to modify the contents of a file before it is handled by the parsers. + +Arguments: +- `file` + - Type: `Object` + - Properties: + - path: `String` + - extension: `String` (ex: `.md`) + - data: `String` + +**Example** + +Changing all appearances of `react` to `vue` in all Markdown files: + +```js{}[nuxt.config.js] +hooks: { + 'content:file:beforeParse': (file) => { + if (file.extension !== '.md') return + file.data = file.data.replace(/react/g, 'vue') + } +} +``` + +### `content:file:beforeInsert` + +Allows you to add data to a document before it is stored. + +Arguments: +- `document` + - Type: `Object` + - Properties: + - See [writing content](/writing) +- `database` + - Type: `Object` + - Properties: + - See [type definition](https://github.com/nuxt/content/blob/master/packages/content/types/database.d.ts) + +**Example** + +When building a blog, you can use `file:beforeInsert` to add `readingTime` to a document using [reading-time](https://github.com/ngryman/reading-time). + +> `text` is the body content of a markdown file before it is transformed to JSON AST. You can use at this point, but it is not returned by the API. + +```js{}[nuxt.config.js] +export default { + modules: [, + '@nuxt/content' + ], + hooks: { + 'content:file:beforeInsert': (document) => { + if (document.extension === '.md') { + const { time } = require('reading-time')(document.text) + + document.readingTime = time + } + } + } +} +``` + +**Example** + +You might want to parse markdown inside a `.json` file. You can access the parsers from the `database` object: + +```js{}[nuxt.config.js] +export default { + modules: [, + '@nuxt/content' + ], + hooks: { + 'content:file:beforeInsert': async (document, database) => { + if (document.extension === '.json' && document.body) { + const data = await database.markdown.toJSON(document.body) + + Object.assign(document, data) + } + } + } +} +``` + +### `content:options` + +Extend the content options, useful for modules that wants to read content options when normalized and apply updated to it. + +Arguments: +- `options` + - Type: `Object` + - Properties: + - See [configuration](/configuration#properties) + +**Example** + +```js{}[nuxt.config.js] +export default { + modules: [, + '@nuxt/content' + ], + hooks: { + 'content:options': (options) => { + console.log('Content options:', options) + } + } +} +``` + +## Handling Hot Reload + + + +When you are in development mode, the module will automatically call `nuxtServerInit` store action (if defined) and `$nuxt.refresh()` to refresh the current page. + + + +In case you want to listen to the event to do something more, you can listen on `content:update` event on client-side using `$nuxt.$on('content:update')`: + +```js{}[plugins/update.client.js] +export default function ({ store }) { + // Only in development + if (process.dev) { + window.onNuxtReady(($nuxt) => { + $nuxt.$on('content:update', ({ event, path }) => { + // Refresh the store categories + store.dispatch('fetchCategories') + }) + }) + } +} +``` + +And then add your plugin in your `nuxt.config.js`: + +```js{}[nuxt.config.js] +export default { + plugins: [ + '@/plugins/update.client.js' + ] +} +``` + +Now everytime you will update a file in your `content/` directory, it will also dispatch the `fetchCategories` method. +This documentation uses it actually. You can learn more by looking at [plugins/init.js](https://github.com/nuxt/content/blob/master/docs/plugins/init.js). + +## API Endpoint + +This module exposes an API endpoint in development so you can easily see the JSON of each directory or file, it is available on [http://localhost:3000/_content/](http://localhost:3000/_content/). The prefix is `_content` by default and can be configured with the [apiPrefix](/configuration#apiprefix) property. + +**Example** + +```bash +-| content/ +---| articles/ +------| hello-world.md +---| index.md +---| settings.json +``` + +Will expose on `localhost:3000`: +- `/_content/articles`: list the files in `content/articles/` +- `/_content/articles/hello-world`: get `hello-world.md` as JSON +- `/_content/index`: get `index.md` as JSON +- `/_content/settings`: get `settings.json` as JSON +- `/_content`: list `index` and `settings` + +The endpoint is accessible on `GET` and `POST` request, so you can use query params: [http://localhost:3000/_content/articles?only=title&only=description&limit=10](http://localhost:3000/_content/articles?only=title&only=description&limit=10). + +Since **v1.4.0**, this endpoint also supports `where` in query params: + +- All the keys that don't belong to any of the default ones will be applied to `where` + +`http://localhost:3000/_content/articles?author=...` + +- You can use `$operators` with `_`: + +`http://localhost:3000/_content/articles?author_regex=...` + +> This module uses LokiJS under the hood. You can check here for [query examples](https://github.com/techfort/LokiJS/wiki/Query-Examples#find-queries). + +- You can use [nested properties](/configuration#nestedproperties): + +`http://localhost:3000/_content/products?categories.slug_contains=top` + +> You can learn more about that endpoint in [lib/middleware.js](https://github.com/nuxt/content/blob/main/packages/content/lib/middleware.js). diff --git a/docs/content-v1/en/1.getting-started/_dir.yml b/docs/content-v1/en/1.getting-started/_dir.yml new file mode 100644 index 000000000..7572f6676 --- /dev/null +++ b/docs/content-v1/en/1.getting-started/_dir.yml @@ -0,0 +1 @@ +title: 'Getting Started' diff --git a/docs/content-v1/en/2.examples/1.basic.md b/docs/content-v1/en/2.examples/1.basic.md new file mode 100644 index 000000000..7454f908f --- /dev/null +++ b/docs/content-v1/en/2.examples/1.basic.md @@ -0,0 +1,8 @@ +--- +title: Basic Usage +description: 'Live example of basic usage of Nuxt Content on CodeSandbox.' +toc: false +--- + +::sandbox{src="https://codesandbox.io/embed/nuxt-content-playground-l164h?hidenavigation=1&theme=dark"} +:: diff --git a/docs/content-v1/en/2.examples/2.tailwindcss-typography.md b/docs/content-v1/en/2.examples/2.tailwindcss-typography.md new file mode 100644 index 000000000..0654a9c23 --- /dev/null +++ b/docs/content-v1/en/2.examples/2.tailwindcss-typography.md @@ -0,0 +1,8 @@ +--- +title: TailwindCSS +description: 'Live example of Nuxt Content with TailwindCSS Typography plugin on CodeSandbox.' +toc: false +--- + +::sandbox{src="https://codesandbox.io/embed/nuxt-content-tailwindcss-typography-xq04z?hidenavigation=1&theme=dark"} +:: diff --git a/docs/content-v1/en/2.examples/3.docs-theme.md b/docs/content-v1/en/2.examples/3.docs-theme.md new file mode 100644 index 000000000..44caf4e5e --- /dev/null +++ b/docs/content-v1/en/2.examples/3.docs-theme.md @@ -0,0 +1,8 @@ +--- +title: Docs Theme +description: 'Live example of Nuxt Content docs theme on CodeSandbox.' +toc: false +--- + +::sandbox{src="https://codesandbox.io/embed/nuxt-content-docs-theme-playground-inwxb?hidenavigation=1&theme=dark"} +:: diff --git a/docs/content-v1/en/3.community/1.snippets.md b/docs/content-v1/en/3.community/1.snippets.md new file mode 100644 index 000000000..31e278655 --- /dev/null +++ b/docs/content-v1/en/3.community/1.snippets.md @@ -0,0 +1,435 @@ +--- +title: Snippets +description: 'Learn how to implement @nuxt/content into your app with these code snippets.' +category: Community +subtitle: 'Check out these code snippets that can be copied directly into your application.' +version: 1.1 +--- + +## Usage + +### asyncData + +```js +export default { + async asyncData({ $content, params }) { + const article = await $content('articles', params.slug).fetch() + + return { + article + } + } +} +``` + +### head + +Add dynamic metas based on title and description defined in the [front-matter](https://content.nuxtjs.org/writing#front-matter): + +```js +export default { + async asyncData({ $content, params }) { + const article = await $content('articles', params.slug).fetch() + + return { + article + } + }, + head() { + return { + title: this.article.title, + meta: [ + { hid: 'description', name: 'description', content: this.article.description }, + // Open Graph + { hid: 'og:title', property: 'og:title', content: this.article.title }, + { hid: 'og:description', property: 'og:description', content: this.article.description }, + // Twitter Card + { hid: 'twitter:title', name: 'twitter:title', content: this.article.title }, + { hid: 'twitter:description', name: 'twitter:description', content: this.article.description } + ] + } + } +} +``` + +## Features + +### Search + +Add a search input component by using watch: + +```vue + + + +``` + +> Check out the [search documentation](/fetching#searchfield-value) + +### Prev and Next + +Add previous and next links using the `surround` method: + +```vue + + + +``` + + + +If more than one document has the same slug, you should set `path` as the first argument of the `surround` method, instead of `slug`. +This is because Nuxt Content finds previous and next documents based on the one that matched first. + +For example, if you sort documents according to `position`, the lower-positioned document will be always used for calculation, +even when the current page is showing the higer-positioned document. + + + +> Check out the [surround documentation](/fetching#surroundslug-options) + +### Case-Insensitive Sorting + +It is needed to work around Nuxt Content's case-sensitive sorting, to add extra properties to documents, whose value is lower-cased. + +```js [nuxt.config.js] +export default { + hooks: { + 'content:file:beforeInsert': (document) => { + if (document.extension === '.md') { + Object.entries(document).forEach(([key, value]) => { + const _key = `case_insensitive__${key}`; // prefix is arbitrary + + if (!document[_key] && typeof value === 'string') { + document[_key] = value.toLocaleLowerCase(); + } + }); + } + } + } +}; +``` + +Then, call `sortBy` method with the extra prop's key by which to sort. + +```ts +export default { + async asyncData({ $content, params }) { + const articles = await $content('articles', params.slug) + .sortBy('case_insensitive__title', 'asc') // Set prefixed prop + .fetch() + + return { + articles + } + } +} +``` + +> Check out the [`sortBy` documentation](/fetching#sortbykey-direction) + +### Table of contents + +Add a table of contents by looping over our array of toc and use the `id` to link to it and the `text` to show the title. We can use the `depth` to style the titles differently: + +```vue + + + +``` + +> Check out the [Table of contents documentation](/writing#table-of-contents) + +### Dynamic routing + +Let's say you want to create an app with routes following the `content/` file structure. You can do so by creating a `pages/_.vue` component: + +```vue[pages/_.vue] + +``` + +This way, if you go the `/themes/docs` route, it will display the `content/themes/docs.md` file. If you need an index page for your directories, you need to create a file with the same name as the directory: + +```bash +content/ + themes/ + docs.md + themes.md +``` + + + +Don't forget to prefix your calls with the current locale if you're using `nuxt-i18n`. + + + +### Custom Highlighter + +#### Highlight.js + +```js{}[nuxt.config.js] +import highlightjs from 'highlight.js' + +const wrap = (code, lang) => `
${code}
` + +export default { + // Complete themes: https://github.com/highlightjs/highlight.js/tree/master/src/styles + css: ['highlight.js/styles/nord.css'], + + modules: ['@nuxt/content'], + + content: { + markdown: { + highlighter(rawCode, lang) { + if (!lang) { + return wrap(highlightjs.highlightAuto(rawCode).value, lang) + } + return wrap(highlightjs.highlight(rawCode, { language: lang }).value, lang) + } + } + } +} +``` + +#### Shiki + +[Shiki](https://github.com/shikijs/shiki) is syntax highlighter that uses TexMate grammar and colors the tokens with VS Code themes. It will generate HTML that looks like exactly your code in VS Code. + +You don't need to add custom styling, because Shiki will inline it in the HTML. + +```js{}[nuxt.config.js] +import shiki from 'shiki' + +export default { + modules: ['@nuxt/content'], + + content: { + markdown: { + async highlighter() { + const highlighter = await shiki.getHighlighter({ + // Complete themes: https://github.com/shikijs/shiki/tree/master/packages/themes + theme: 'nord' + }) + return (rawCode, lang) => { + return highlighter.codeToHtml(rawCode, lang) + } + } + } + } +} +``` + +#### Shiki Twoslash + +[Twoslash](https://github.com/microsoft/TypeScript-Website/tree/v2/packages/ts-twoslasher) is a markup format for TypeScript code. Internally, Twoslash uses the TypeScript compiler to generate rich highlight info. + +To get a better idea of how Twoslash works, you can go over to the [Official TypeScript Documentation](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#type-aliases) and hover over some code examples there. + +You can achieve the same result by using [Shiki Twoslash](https://github.com/microsoft/TypeScript-Website/tree/v2/packages/shiki-twoslash). This package is also the one that powers the Official TypeScript Documentation. + +```js{}[nuxt.config.js] +import { + createShikiHighlighter, + runTwoSlash, + renderCodeToHTML +} from 'shiki-twoslash' + +export default { + modules: ['@nuxt/content'], + + content: { + markdown: { + async highlighter() { + const highlighter = await createShikiHighlighter({ + // Complete themes: https://github.com/shikijs/shiki/tree/master/packages/themes + theme: 'nord' + }) + return (rawCode, lang) => { + const twoslashResults = runTwoSlash(rawCode, lang) + return renderCodeToHTML( + twoslashResults.code, + lang, + ['twoslash'], + {}, + highlighter, + twoslashResults + ) + } + } + } + } +} +``` + +### Remark Plugin + +Nuxt Content uses [remark](https://github.com/remarkjs/remark) under the hood to process markdown documents. Creating remark plugins is a way to manipulate documents and add new features. + +#### List all contributors + +Let's say you want to list all contributors of the project in a document. You can create a plugin that fetches all contributors and injects them into the document data. + +- Create the plugin. This plugin fetches the contributors if `fetchContributors` is set to `true` + +```js [~~/plugins/contributors.js] +const fetch = require('node-fetch') + +module.exports = function () { + return async (tree, { data }) => { + if (data.fetchContributors) { + const contributors = await fetch( + 'https://api.github.com/repos/nuxt/content/contributors' + ).then(res => res.json()) + .then(res => res.map(({ login }) => login)) + + data.$contributors = [...new Set(contributors)] + } + return tree + } +} +``` + +- Register plugin in Nuxt config + +```js{}[nuxt.config.js] +export default { + contents: { + markdown: { + remarkPlugins: [ + '~~/plugins/contributors.js' + ] + } + } +} +``` + +- Create a simple component to show contributors + +```vue{}[~~/components/List.vue] + + + +``` + +**Props:** + +- document: + - Type: `Object` + - `requis` + +Vous pouvez en apprendre davantage au sujet de ce que vous pouvez écrire dans vos fichiers Markdown dans la section [écrire du contenu](/fr/writing#markdown). + +## Style + +En fonction de ce que vous utilisez pour designer vos applications, vous pourrez avoir besoin d'ajouter du style pour afficher votre Markdown correctement. + +Le composant `` ajoutera automatiquement la classe `.nuxt-content` que vous pourrez utiliser afin d'ajouter votre propre style: + +```css +.nuxt-content h1 { + /* mon propre style h1 */ +} +``` + +Vous pouvez trouver un exemple dans le [répertoire docs](https://github.com/nuxt/content/blob/master/docs/pages/_slug.vue). diff --git a/docs/content-v1/fr/1.getting-started/6.configuration.md b/docs/content-v1/fr/1.getting-started/6.configuration.md new file mode 100644 index 000000000..8425ef3e3 --- /dev/null +++ b/docs/content-v1/fr/1.getting-started/6.configuration.md @@ -0,0 +1,191 @@ +--- +title: Configuration +description: 'Vous pouvez configurer @nuxt/content avec la propriété content au sein du fichier nuxt.config.js' +--- + +Vous pouvez configurer `@nuxt/content` avec la propriété `content` au sein du fichier `nuxt.config.js`. + +```js [nuxt.config.js] +export default { + content: { + // Ma configuration personnalisée + } +} +``` + +Voir les [options par défaut](#configuration-par-défaut). + +## Propriétés + +### `apiPrefix` + +- Type: `String` +- Défaut: `'/_content'` + +La route qui sera utilisée pour les appels API côté client et les SSE. + +```js [nuxt.config.js] +content: { + // l'api $content sera disponible à l'adresse localhost:3000/content-api + apiPrefix: 'content-api' +} +``` + +### `dir` + +- Type: `String` +- Défaut: `'content'` + +Le répertoire utilisé pour l'écriture du contenu. Vous pouvez fournir un chemin absolu, mais dans le cas où le chemin est relatif, il sera déterminé avec la propriété [srcDir](https://nuxtjs.org/api/configuration-srcdir) de Nuxt. + +```js [nuxt.config.js] +content: { + dir: 'mon-contenu' // lit le contenu depuis mon-contenu/ +} +``` + +### `fullTextSearchFields` + +- Type: `Array` +- Défaut: `['title', 'description', 'slug', 'text']` + +Les champs qui ont besoin d'être indexés afin d'être recherchables, vous pouvez en apprendre plus sur les recherches [ici](/fr/fetching#searchfield-value). + +`text` est une propriété spéciale qui contient votre Markdown avant qu'il soit converti en AST. + +```js [nuxt.config.js] +content: { + // Les recherches s'effectuent uniquement sur les champs titre et description + fullTextSearchFields: ['title', 'description'] +} +``` + +### `nestedProperties` + +- Type `Array` +- Défaut: `[]` +- Version: **v1.3.0** + +Vous pouvez enregistrer des propriétés imbriquées afin que la notation ponctuelle et le filtrage en profondeur soient pris en charge. + +```js [nuxt.config.js] +content: { + nestedProperties: ['categories.slug'] +} +``` + +### `markdown` + +Ce module se sert de [remark](https://github.com/remarkjs/remark) afin de compiler des fichiers Markdown vers du JSON AST qui sera stocké dans une variable `body`. + +Par défaut, ce module a recours à des plugins pour améliorer la conversion du Markdown. Vous pouvez ajouter vos propres plugins ou modifier ceux par défaut en utilisant la propriété `basePlugins`. Chaque plugin est configuré en utilisant son nom en camelCase : `remark-external-links` => `externalLinks`. + +> Vous pouvez retrouver des plugins pour remark [ici](https://github.com/remarkjs/remark/blob/master/doc/plugins.md#list-of-plugins). + +### `markdown.basePlugins` + +- Type: `Array` +- Défaut: `['remark-squeeze-paragraphs', 'remark-slug', 'remark-autolink-headings', 'remark-external-links', 'remark-footnotes']` + +### `markdown.plugins` + +- Type: `Array` +- Défaut: `[]` + +### `markdown.externalLinks` + +- Type: `Object` +- Défaut: `{}` + +Vous pouvez controler le comportement des liens avec cette option. Consultez la liste des options [ici](https://github.com/remarkjs/remark-external-links#api). + +```js [nuxt.config.js] +content: { + markdown: { + externalLinks: { + target: '_self' // désactive target="_blank" + rel: false // désactive rel="nofollow noopener" + } + } +} +``` + +### `markdown.footnotes` + +- Type: `Object` +- Défaut: `{ inlineNotes: true }` + +Vous pouvez controler le comportement des liens avec cette option. Consultez la liste des [ici](https://github.com/remarkjs/remark-footnotes#remarkusefootnotes-options). + +### `markdown.prism.theme` + +- Type: `String` +- Défaut: `'prismjs/themes/prism.css'` + +Ce module gère la coloration syntaxique du contenu Markdown à l'aide de [PrismJS](https://prismjs.com). + +Il insère automatiquement le thème PrismJS désiré au sein de votre configuration Nuxt.js, donc si souhaitez utiliser un thème différent de celui par défaut, consultez les [prism-themes](https://github.com/PrismJS/prism-themes): + +```js [nuxt.config.js] +content: { + markdown: { + prism: { + theme: 'prism-themes/themes/prism-material-oceanic.css' + } + } +} +``` + +Pour désactiver l'inclusion du thème, définissez prism à `false`: + +```js [nuxt.config.js] +content: { + markdown: { + prism: { + theme: false + } + } +} +``` + +### `yaml` + +- Type: `Object` +- Défaut: `{}` + +Ce module utilise `js-yaml` pour convertir les fichiers yaml, vous pouvez consulter les options [ici](https://github.com/nodeca/js-yaml#api). + +Notez que nous forçons l'option `json: true`. + +### `csv` + +- Type: `Object` +- Défaut: `{}` + +Ce module utilise `node-csvtojson` pour convertir les fichiers csv, vous pouvez consulter les options [ici](https://github.com/Keyang/node-csvtojson#parameters). + +## Configuration par défaut + +```js [nuxt.config.js] +export default { + content: { + apiPrefix: '_content', + dir: 'content', + fullTextSearchFields: ['title', 'description', 'slug', 'text'], + nestedProperties: [], + markdown: { + externalLinks: {}, + footnotes: { + inlineNotes: true + }, + basePlugins: ['remark-squeeze-paragraphs', 'remark-slug', 'remark-autolink-headings', 'remark-external-links', 'remark-footnotes'], + plugins: [], + prism: { + theme: 'prismjs/themes/prism.css' + } + }, + yaml: {}, + csv: {} + } +} +``` diff --git a/docs/content-v1/fr/1.getting-started/7.advanced.md b/docs/content-v1/fr/1.getting-started/7.advanced.md new file mode 100644 index 000000000..653de5135 --- /dev/null +++ b/docs/content-v1/fr/1.getting-started/7.advanced.md @@ -0,0 +1,194 @@ +--- +title: Utilisation avancée +description: Apprenez l'utilisation avancée du module @nuxt/content +position: 7 +category: Pour commencer +--- + +## Utilisation Programmatique + +`$content` est accessible depuis **@nuxt/content**. + +::alert + +Notez que vous ne pouvez y accéder seulement **après que le module ait été chargé** par Nuxt. L'utilisation de `require(@nuxt/content)` devrait avoir lieu dans les hooks ou les méthodes internes de Nuxt. + +:: + +```js +export default { + modules: [, + '@nuxt/content' + ], + generate: { + async ready () { + const { $content } = require('@nuxt/content') + const files = await $content().only(['slug']).fetch() + console.log(files) + } + } +} +``` + +### Génération de Site Statique + +::alert{type="info"} + +Si vous utilisez Nuxt 2.13+, la commande `nuxt export` a une fonctionnalité de crawler intégrée, donc vous ne devriez pas avoir besoin de recourir à `generate.routes`. + +:: + +Lors de l'utilisation de `nuxt generate`, vous devez spécifier les routes dynamiques avec `generate.routes`, car Nuxt ne sait pas quelles seront ces routes donc il ne sera pas capable de les générer. + +**Exemple** + +```js +export default { + modules: [, + '@nuxt/content' + ], + generate: { + async routes () { + const { $content } = require('@nuxt/content') + const files = await $content().only(['path']).fetch() + + return files.map(file => file.path === '/index' ? '/' : file.path) + } + } +} +``` + +## Hooks + +Ce module ajoute des hooks que vous pouvez utiliser: + +### `content:file:beforeInsert` + +Vous permez d'ajouter des données à un document avant qu'il ne soit stocké. + +Arguments: + +- `document` + - Type: `Object` + - Propriétés: + - Voir [écrire du contenu](/fr/writing) + +**Exemple** + +En prenant l'exemple du blog starter, on utilise `file:beforeInsert` pour ajouter une propritété `readingTime` à un document en se servant de [reading-time](https://github.com/ngryman/reading-time). + +> `text` est le contenu du corps d'un fichier Markdown avant qu'il ne soit transformé en JSON AST, vous pouvez l'utiliser à ce stade mais il n'est pas retourné par l'API. + +```js +export default { + modules: [, + '@nuxt/content' + ], + hooks: { + 'content:file:beforeInsert': (document) => { + if (document.extension === '.md') { + const { time } = require('reading-time')(document.text) + + document.readingTime = time + } + } + } +} +``` + +## Gérer le Rechargement à Chaud + +::alert{type="info"} + +Lorsque vous développez, ce module appellera automatiquement l'action `nuxtServerInit` (si elle est définie) et `$nuxt.refresh()` afin de rafraîchir la page actuelle. + +:: + +Dans le cas où vous souhaiteriez écouter cet évènement et ajouter des instructions supplémentaires, vous pouvez écouter l'évènement `content:update` côté client en utilisant `$nuxt.$on('content:update')`: + +```js{}[plugins/update.client.js +export default function ({ store }) { + // Uniquement en développement + if (process.dev) { + window.onNuxtReady(($nuxt) => { + $nuxt.$on('content:update', ({ event, path }) => { + // Raffraîchit le store des catégories + store.dispatch('fetchCategories') + }) + }) + } +} +``` + +Ajoutez ensuite votre plugin dans votre fichier `nuxt.config.js`: + +```js{}[nuxt.config.js] +export default { + plugins: [ + '@/plugins/update.client.js' + ] +} +``` + +Dès lors que vous opèrerez un changement sur un des fichiers au sein du répertoire `content/`, la méthode `fetchCategories` sera appelée. D'ailleurs, cette documentation l'utilise, vous pouvez en apprendre davantage en jetant un oeil à [plugins/categories.js](https://github.com/nuxt/content/blob/master/docs/plugins/categories.js). + +## Intégration avec @nuxtjs/feed + +Dans le cas d'articles, le contenu peut être utilisé pour générer des fils d'actualités en utilisant le module [@nuxtjs/feed](https://github.com/nuxt-community/feed-module). + +::alert{type="info"} + +Pour utiliser `$content` au sein de l'option `feed`, vous devez ajouter `@nuxt/content` avant `@nuxtjs/feed` dans la propriété `modules`. + +:: + +**Exemple** + +```js +export default { + modules: [ + '@nuxt/content', + '@nuxtjs/feed' + ], + + feed () { + const baseUrlArticles = 'https://mywebsite.com/articles' + const baseLinkFeedArticles = '/feed/articles' + const feedFormats = { + rss: { type: 'rss2', file: 'rss.xml' }, + atom: { type: 'atom1', file: 'atom.xml' }, + json: { type: 'json1', file: 'feed.json' }, + } + const { $content } = require('@nuxt/content') + + const createFeedArticles = async function (feed) { + feed.options = { + title: 'Mon Blog', + description: 'J\'écris à propos de la téchnologie', + link: baseUrlArticles, + } + const articles = await $content('articles').fetch() + + articles.forEach((article) => { + const url = `${baseUrlArticles}/${article.slug}` + + feed.addItem({ + title: article.title, + id: url, + link: url, + date: article.published, + description: article.summary, + content: article.summary, + author: article.authors, + }) + }) + } + + return Object.values(feedFormats).map(({ file, type }) => ({ + path: `${baseLinkFeedArticles}/${file}`, + type: type, + create: feedCreateArticles, + })) + } +} +``` diff --git a/docs/content-v1/fr/1.getting-started/_dir.yml b/docs/content-v1/fr/1.getting-started/_dir.yml new file mode 100644 index 000000000..387dad20d --- /dev/null +++ b/docs/content-v1/fr/1.getting-started/_dir.yml @@ -0,0 +1 @@ +title: Pour commencer diff --git a/docs/content-v1/fr/2.examples/1.basic.md b/docs/content-v1/fr/2.examples/1.basic.md new file mode 100644 index 000000000..6a9f1f27e --- /dev/null +++ b/docs/content-v1/fr/2.examples/1.basic.md @@ -0,0 +1,8 @@ +--- +title: Exemple d'utilisation de base +description: "Exemple en direct d'utilisation de base de Nuxt Content sur CodeSandbox." +toc: false +--- + +::sandbox{src="https://codesandbox.io/embed/nuxt-content-playground-l164h?hidenavigation=1&theme=dark"} +:: diff --git a/docs/content-v1/fr/2.examples/2..tailwindcss-typography.md b/docs/content-v1/fr/2.examples/2..tailwindcss-typography.md new file mode 100644 index 000000000..ae25d855b --- /dev/null +++ b/docs/content-v1/fr/2.examples/2..tailwindcss-typography.md @@ -0,0 +1,7 @@ +--- +title: Exemple TailwindCSS Typography +description: 'Exemple en direct de contenu Nuxt avec le plugin TailwindCSS Typography sur CodeSandbox.' +toc: true +--- + +::sandbox{src="https://codesandbox.io/embed/nuxt-content-tailwindcss-typography-twhtf?hidenavigation=1&theme=dark"} diff --git a/docs/content-v1/fr/2.examples/3.docs-theme.md b/docs/content-v1/fr/2.examples/3.docs-theme.md new file mode 100644 index 000000000..c20fc9b06 --- /dev/null +++ b/docs/content-v1/fr/2.examples/3.docs-theme.md @@ -0,0 +1,8 @@ +--- +title: Exemple de thèmes de documentation +description: 'Exemple en direct du thème de la documentation Nuxt Content sur CodeSandbox.' +toc: true +--- + +::sandbox{src="https://codesandbox.io/embed/nuxt-content-docs-theme-playground-inwxb?hidenavigation=1&theme=dark"} +:: diff --git a/docs/content-v1/fr/2.examples/_dir.yml b/docs/content-v1/fr/2.examples/_dir.yml new file mode 100644 index 000000000..17f6e57ea --- /dev/null +++ b/docs/content-v1/fr/2.examples/_dir.yml @@ -0,0 +1 @@ +title: Exemples diff --git a/docs/content-v1/fr/3.community/1.snippets.md b/docs/content-v1/fr/3.community/1.snippets.md new file mode 100644 index 000000000..72b3a7896 --- /dev/null +++ b/docs/content-v1/fr/3.community/1.snippets.md @@ -0,0 +1,306 @@ +--- +title: Extraits +description: 'Apprenez comment implémenter @nuxt/content dans votre application avec ces extraits de code.' +subtitle: 'Découvrez ces extraits de code qui peuvent être copiés directement dans votre application.' +version: 1.1 +--- + +## Utilisation + +### asyncData + +```js +export default { + async asyncData({ $content, params }) { + const article = await $content('articles', params.slug).fetch() + + return { + article + } + } +} +``` + +### head + +Ajouter des métadatas en fonction du titre et de la description définie dans [front-matter](https://content.nuxtjs.org/writing#front-matter) : + +```js +export default { + async asyncData({ $content, params }) { + const article = await $content('articles', params.slug).fetch() + + return { + article + } + }, + head() { + return { + title: this.article.title, + meta: [ + { hid: 'description', name: 'description', content: this.article.description }, + // Open Graph + { hid: 'og:title', property: 'og:title', content: this.article.title }, + { hid: 'og:description', property: 'og:description', content: this.article.description }, + // Twitter Card + { hid: 'twitter:title', name: 'twitter:title', content: this.article.title }, + { hid: 'twitter:description', name: 'twitter:description', content: this.article.description } + ] + } + } +} +``` + +## Fonctionnalités + +### Recherche + +Ajoutez un composant de recherche à l'aide de watch : + +```vue + + + +``` + +> Consulter la [documentatio de recherche](/fetching#searchfield-value). + +### Prev et Next + +Ajoutez les liens précédents et suivants en utilisant la méthode `surround` : + +```vue + + + +``` + +> Consulter la [documentation de surround](/fetching#surroundslug-options). + +### Table des matières + +Ajoutez une table des matières en parcourant le tableau toc et utilisez l'`id` pour créer un lien vers celui-ci ainsi que le `text` pour afficher le titre. Nous pouvons utiliser la propriété `depth` pour styliser les titres différemment : + +```vue + + + +``` + +> Consulter la [documentation de la table des matières](/writing#table-of-contents). + +### Routes dynamiques + +Supposons que vous souhaitez créer une application avec des routes suivant la structure de fichier `content/`, vous pouvez le faire en créant un composant `pages/_.vue` : + +```vue[pages/_.vue] + +``` + +De cette façon, si vous suivez la route `/themes/docs`, il affichera le fichier `content/themes/docs.md`. Si vous avez besoin d'une page d'index pour vos répertoires, vous devez créer un fichier avec le même nom que le répertoire : + +```bash +content/ + themes/ + docs.md + themes.md +``` + + + +N'oubliez pas de préfixer vos appels avec les paramètres de langues si vous utilisez `nuxt-i18n`. + + + +### Surligneurs personnalisés + +#### Highlight.js + +```js [nuxt.config.js] +import highlightjs from 'highlight.js' + +const wrap = (code, lang) => `
${code}
` + +export default { + // Complete themes: https://github.com/highlightjs/highlight.js/tree/master/src/styles + css: ['highlight.js/styles/nord.css'], + + modules: ['@nuxt/content'], + + content: { + markdown: { + highlighter(rawCode, lang) { + if (!lang) { + return wrap(highlightjs.highlightAuto(rawCode).value, lang) + } + return wrap(highlightjs.highlight(rawCode, { language: lang }).value, lang) + } + } + } +} +``` + +#### Shiki + +[Shiki](https://github.com/shikijs/shiki) est un surligneur de syntaxe qui utilise la grammaire TexMate et colore les éléments avec des thèmes VS Code. Il générera du HTML qui ressemble exactement à votre code dans VS Code. + +Vous n'avez pas besoin d'ajouter un style personnalisé, car Shiki l'intègrera dans le HTML. + +```js [nuxt.config.js] +import shiki from 'shiki' + +export default { + modules: ['@nuxt/content'], + + content: { + markdown: { + async highlighter() { + const highlighter = await shiki.getHighlighter({ + // Complete themes: https://github.com/shikijs/shiki/tree/master/packages/themes + theme: 'nord' + }) + return (rawCode, lang) => { + return highlighter.codeToHtml(rawCode, lang) + } + } + } + } +} +``` + +#### Shiki Twoslash + +[Twoslash](https://github.com/microsoft/TypeScript-Website/tree/v2/packages/ts-twoslasher) est un format de balisage pour le code TypeScript. En interne, Twoslash utilise le compilateur TypeScript pour générer des informations riches en surlignage. + +Pour avoir une meilleure idée du fonctionnement de Twoslash, vous pouvez jeter un oeil à la [documentation officielle TypeScript](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#type-aliases) et survolez un exemple de code là-bas. + +Vous pouvez obtenir le même résultat en utilisant [Shiki Twoslash](https://github.com/microsoft/TypeScript-Website/tree/v2/packages/shiki-twoslash). Ce package est également celui qui alimente la documentation officielle de TypeScript. + +```js [nuxt.config.js] +import { + createShikiHighlighter, + runTwoSlash, + renderCodeToHTML +} from 'shiki-twoslash' + +export default { + modules: ['@nuxt/content'], + + content: { + markdown: { + async highlighter() { + const highlighter = await createShikiHighlighter({ + // Complete themes: https://github.com/shikijs/shiki/tree/master/packages/themes + theme: 'nord' + }) + return (rawCode, lang) => { + const twoslashResults = runTwoSlash(rawCode, lang) + return renderCodeToHTML( + twoslashResults.code, + lang, + ['twoslash'], + {}, + highlighter, + twoslashResults + ) + } + } + } + } +} +``` diff --git a/docs/content-v1/fr/3.community/2..integrations.md b/docs/content-v1/fr/3.community/2..integrations.md new file mode 100644 index 000000000..cf747b140 --- /dev/null +++ b/docs/content-v1/fr/3.community/2..integrations.md @@ -0,0 +1,285 @@ +--- +title: Intégrations +description: "Apprenez comment utiliser @nuxt/content avec d'autres modules." +--- + +## @nuxtjs/feed + +Dans le cas d'articles, le contenu peut être utilisé pour générer des fils d'actualités +en utilisant le module [@nuxtjs/feed](https://github.com/nuxt-community/feed-module). + +::alert{type="info"} + +Pour utiliser `$content` à l'intérieur de l'option `feed`, vous devez ajouter `@nuxt/content` avant `@nuxtjs/feed` dans la propriété `modules`. + +:: + +Vous pouvez accéder à votre flux via : `baseUrl + baseLinkFeedArticles + file` + +Pour RSS: ```https://mywebsite.com/feed/articles/rss.xml``` + +Pour JSON: ```https://mywebsite.com/feed/articles/feed.json``` + +**Exemple 1** + +```js +export default { + modules: [ + '@nuxt/content', + '@nuxtjs/feed' + ], + + feed () { + const baseUrlArticles = 'https://mywebsite.com/articles' + const baseLinkFeedArticles = '/feed/articles' + const feedFormats = { + rss: { type: 'rss2', file: 'rss.xml' }, + json: { type: 'json1', file: 'feed.json' }, + } + const { $content } = require('@nuxt/content') + + const createFeedArticles = async function (feed) { + feed.options = { + title: 'My Blog', + description: 'I write about technology', + link: baseUrlArticles, + } + const articles = await $content('articles').fetch() + + articles.forEach((article) => { + const url = `${baseUrlArticles}/${article.slug}` + + feed.addItem({ + title: article.title, + id: url, + link: url, + date: article.published, + description: article.summary, + content: article.summary, + author: article.authors, + }) + }) + } + + return Object.values(feedFormats).map(({ file, type }) => ({ + path: `${baseLinkFeedArticles}/${file}`, + type: type, + create: createFeedArticles, + })) + } +} +``` + +L'approche ci-dessus fonctionne bien pour certains cas d'utilisation. Cependant, si vous utilisez `npm run generate`, cette approche produira une erreur. + +Ou, si vous souhaitez inclure le corps de l'article en tant que contenu, y compris le contenu des composants si vous mélangez vos composants Vue et votre markdown, vous devrez aborder cela différemment. + +Une façon possible pour faire cela est d'utiliser l'approche documentée de [@nuxtjs/feed](https://github.com/nuxt-community/feed-module) qui fonctionnera avec `npm run generate` et utilisera le processus de Nuxt pour inclure le contenu. Voici comment procéder. + +Tout d'abord, utilisez l'approche basée sur les tableaux pour déclarer vos flux comme indiqué dans la documentation @nuxtjs/feed. Le tableau de flux se compose d'objets avec 5 valeurs possibles. + +1. Le `path` qui le chemin de votre nom de domaine vers le flux de votre document. +2. Une fonction qui générera le contenu du flux. +3. Le `cacheTime` qui comme son nom l'indique détermine quand le flux doit être actualisé. +4. Le `type` qui détermine quel type de flux rss que vous souhaitez générer. +5. Les `data` qui sont fournies comme argument à la fonction (la propriété args dans l'exemple ci-dessous) + +```js +modules: [ + '@nuxt/content', + '@nuxtjs/feed' + ], + + feed: [ + { + path: '/feed.xml', + async create(feed, args) => { + // create the feed + }, + cacheTime: 1000 * 60 * 15, + type: 'rss2', + data: [ 'some', 'info' ] + }, + { + path: '/feed.json', + async create(feed, args) => { + // create the feed + }, + cacheTime: 1000 * 60 * 15, + type: 'json1', + data: [ 'other', 'info' ] + } + ], +``` + +Vous pouvez tirer le meilleur de l'API nuxt/content, mais en déclarant votre fonction `create` séparément, puis en la fournissant à l'objet de tableau de flux. La fonction `create` peut être déclarée en haut du fichier nuxt.config.js, ou séparément dans un autre répertoire et exportée. Cette fonction `create` s'exécute _après_ que le processus Nuxt a compilé les composants markdown et Vue en HTML. Cela nous permet d'extraire ce contenu et de le fournir au flux. + +**Exemple 2** + +```js +// this Exemple declares the function at the top of the nuxt.config.js file +const fs = require('fs').promises; +const path = require('path'); + +let posts = []; + +const constructFeedItem = async (post, dir, hostname) => { + //note the path used here, we are using a dummy page with an empty layout in order to not send that data along with our other content + const filePath = path.join(__dirname, `dist/rss/${post.slug}/index.html`); + const content = await fs.readFile(filePath, 'utf8'); + const url = `${hostname}/${dir}/${post.slug}`; + return { + title: post.title, + id: url, + link: url, + description: post.description, + content: content + } +} + +const create = async (feed, args) => { + const [filePath, ext] = args; + const hostname = process.NODE_ENV === 'production' ? 'https://my-production-domain.com' : 'http://localhost:3000'; + feed.options = { + title: "My Blog", + description: "Blog Stuff!", + link: `${hostname}/feed.${ext}` + } + const { $content } = require('@nuxt/content') + if (posts === null || posts.length === 0) + posts = await $content(filePath).fetch(); + + for (const post of posts) { + const feedItem = await constructFeedItem(post, filePath, hostname); + feed.addItem(feedItem); + } + return feed; +} + +export default { +... + modules: [ + '@nuxt/content', + '@nuxtjs/feed' + ], + feed: [ + { + path: '/feed.xml', + create, + cacheTime: 1000 * 60 * 15, + type: 'rss2', + data: [ 'blog', 'xml' ] + }, + ], +... +} +``` + +Il y a cependant deux inconvénients à cette approche : + +1. Puisque vous lisez la totalité de la page générée, vous pouvez récupérer du contenu indésirable tel que celui de l'en-tête et du pied de page. Une façon de résoudre ce problème consiste à créer une page factice en utilisant une mise en page vide afin que seul le contenu que vous souhaitez inclure dans le flux rss soit utilisé. +2. rss2 et XML fonctionnent bien car le HTML est automatiquement encodé. Cependant, json1 et json peuvent nécessiter du travail supplémentaire pour que le contenu puisse être transmis. + +N'oubliez pas également que si vous n'utilisez pas de contenu mixte dans votre markdown (donc si vous n'utilisez que du markdown), il est beaucoup plus facile d'inclure uniquement celui-ci. Vous pouvez récupérer le markdown en utilisant ce hook dans votre nuxt.config.js : + +**Exemple 3** + +```js +export default { +... + hooks: { + 'content:file:beforeInsert': (document) => { + if (document.extension === '.md') { + document.bodyPlainText = document.text; + } + }, + }, +... +} +``` + +Et puis dans votre fonction `create` : + +```js + +const constructFeedItem = (post, dir, hostname) => { + const url = `${hostname}/${dir}/${post.slug}`; + return { + title: post.title, + id: url, + link: url, + description: post.description, + content: post.bodyPlainText + } +} + +const create = async (feed, args) => { + const [filePath, ext] = args; + const hostname = process.NODE_ENV === 'production' ? 'https://my-production-domain.com' : 'http://localhost:3000'; + feed.options = { + title: "My Blog", + description: "Blog Stuff!", + link: `${hostname}/feed.${ext}` + } + const { $content } = require('@nuxt/content') + if (posts === null || posts.length === 0) + posts = await $content(filePath).fetch(); + + for (const post of posts) { + const feedItem = await constructFeedItem(post, filePath, hostname); + feed.addItem(feedItem); + } + return feed; +} +``` + +Récupérer uniquement le markdown fonctionne très bien si vous utilisez le flux pour intégrer [dev.to](https://dev.to/) ou [medium](https://medium.com/) puisque ces deux sites utiliser markdown dans leurs éditeurs. + + +## @nuxtjs/sitemap + +Vous souhaiterez peut-être générer un plan du site contenant des liens vers tous vos articles. Vous pouvez le faire de la même manière que vous avez généré votre flux. + +**Exemple 1** + +Le module du plan du site doit toujours être déclaré en dernier afin que les routes créées par d'autres modules puissent être incluses. Après avoir déclaré le module, vous devez configurer le plan du site en ajoutant l'objet de configuration du plan du site dans nuxt.config.js. Vous devez fournir le nom d'hôte et vous pouvez éventuellement inclure toutes les routes générées dynamiquement à partir de nuxt-content. +La propriété routes accepte une fonction asynchrone qui renvoie un tableau d'URL. + +```js +const createSitemapRoutes = async () => { + let routes = []; + const { $content } = require('@nuxt/content') + if (posts === null || posts.length === 0) + posts = await $content('blog').fetch(); + for (const post of posts) { + routes.push(`blog/${post.slug}`); + } + return routes; +} + +export default { +... + modules: [ + '@nuxt/content', + '@nuxtjs/sitemap' + ], + sitemap: { + hostname: 'https://my-domain-name.com', + gzip: true, + routes: createSitemapRoutes + }, +... +} +``` + +## Forestry CMS + +Vous pouvez intégrer Nuxt Content avec [Forestry](https://forestry.io) en quelques étapes. + +::alert + +Nous vous recommandons de jeter un œil à ce tutoriel réalisé par [Pascal Cauhépé](https://twitter.com/eQRoeil) + +👉  https://nuxt-content-and-forestry.netlify.app + +:: diff --git a/docs/content-v1/fr/3.community/_dir.yml b/docs/content-v1/fr/3.community/_dir.yml new file mode 100644 index 000000000..621470a85 --- /dev/null +++ b/docs/content-v1/fr/3.community/_dir.yml @@ -0,0 +1 @@ +title: Communauté diff --git a/docs/content-v1/fr/_dir.yml b/docs/content-v1/fr/_dir.yml new file mode 100644 index 000000000..66128b045 --- /dev/null +++ b/docs/content-v1/fr/_dir.yml @@ -0,0 +1,3 @@ +aside: + root: /fr/v1 + level: 0 diff --git a/docs/content-v1/ja/1.getting-started/1.introduction.md b/docs/content-v1/ja/1.getting-started/1.introduction.md new file mode 100644 index 000000000..211798c51 --- /dev/null +++ b/docs/content-v1/ja/1.getting-started/1.introduction.md @@ -0,0 +1,38 @@ +--- +title: Content とは +description: 'nuxt/contentモジュールを使ってNuxtJSアプリケーションを強化します。content/ディレクトリに書き込むことで、MongoDBのようなAPIを使ってMarkdown、JSON、YAML、CSVファイルを取得します。これはGitベースのヘッドレスCMSとして動作します。' +--- + +`nuxt/content`モジュールを使ってNuxtJSアプリケーションを強化します。`content/`ディレクトリに書き込むことで、MongoDBのようなAPIを使ってMarkdown、JSON、YAML、XML、CSVファイルを取得します。これは**GitベースのヘッドレスCMS**として動作します。 + +## 特徴 + +::list +- 開発モードでのビックリするほど高速なホットリロード +- Markdownの中でVueコンポーネントを利用できます +- 全文検索 +- 静的サイト生成(SSG)のサポート `nuxt generate` +- 強力なクエリビルダーAPI (MongoDBライク) +- PrismJSを利用した、Markdown内コードブロックのシンタックスハイライト +- 目次の自動生成 +- Markdown, CSV, YAML, JSON(5)、XMLを適切に処理します +- hooksによる拡張 +:: + +## 動画 + +Markdownページを表示するために `$content` と `` を使うデモ。 + + + +ディレクトリ上で `$content()` を使うと、コンテンツの一覧表示、フィルタリング、検索を行うことができます。 + + diff --git a/docs/content-v1/ja/1.getting-started/2.installation.md b/docs/content-v1/ja/1.getting-started/2.installation.md new file mode 100644 index 000000000..b6ef50642 --- /dev/null +++ b/docs/content-v1/ja/1.getting-started/2.installation.md @@ -0,0 +1,50 @@ +--- +title: インストール +description: 'たった2つのステップであなたのNuxtプロジェクトに@nuxt/contentをインストールします' +--- + +プロジェクトに `@nuxt/content` の依存関係を追加します。 + +::code-group + ```bash [Yarn] + yarn add @nuxt/content + ``` + + ```bash [NPM] + npm install @nuxt/content + ``` +:: + +そして、`nuxt.config.js` の `modules` セクションに `@nuxt/content` を追加します。 + +```js [nuxt.config.js] +{ + modules: [ + '@nuxt/content' + ], + content: { + // Options + } +} +``` + +## TypeScript + +tsconfig.json内の"types"配列へ、`@nuxt/types` (Nuxt 2.9.0+)もしくは`@nuxt/vue-app`に続けて型を追記します。 + +**tsconfig.json** + +```json +{ + "compilerOptions": { + "types": [ + "@nuxt/types", + "@nuxt/content" + ] + } +} +``` + +> **なぜ?** +> +> nuxt の動作方法のため、コンテキストの `$content` プロパティは TypeScriptの[declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html)機能 を通して、nuxt `Context` インターフェースにマージする必要があります。型に `@nuxt/content` を追加すると、パッケージから型をインポートし、TypeScript が `Context` インターフェースに追加されたものを認識するようになります。 diff --git a/docs/content-v1/ja/1.getting-started/3.writing.md b/docs/content-v1/ja/1.getting-started/3.writing.md new file mode 100644 index 000000000..afccd2e9e --- /dev/null +++ b/docs/content-v1/ja/1.getting-started/3.writing.md @@ -0,0 +1,571 @@ +--- +title: コンテンツを作成する +description: 'Markdown、YAML、CSV、JSONに対応したcontent/内の書き方を学びます。' +multiselectOptions: + - VuePress + - Gridsome + - Nuxt +--- + +まず、プロジェクト内に `content/` ディレクトリを作成します。 + +```bash +content/ + articles/ + article-1.md + article-2.md + home.md +``` + +このモジュールは `.md`, `.yaml`, `.yml`, `.csv`, `.json`, `.json5`, `.xml` ファイルを解析し、以下のプロパティを生成します。 + +- `dir` +- `path` +- `slug` +- `extension` (ex: `.md`) +- `createdAt` +- `updatedAt` + +## Markdown + +このモジュールは `.md` ファイルを変数`body`へ格納されたJSON ASTツリー構造体に変換します。 + +Markdownコンテンツの `body` を表示するために `` コンポーネントを必ず使用してください。[コンテンツを表示する](/displaying)を参照してください。 + +> Markdownをマスターするには、[basic syntax guide](https://www.markdownguide.org/basic-syntax)を参照してください。 + +### フロントマター + +マークダウンファイルにYAMLフロントマターブロックを追加できます。フロントマターはファイルの最初でなければならず、`---`の間に有効なYAMLの形式を取らなければなりません。ここに基本的な例を示します。 + +```md +--- +title: Introduction +description: Learn how to use @nuxt/content. +--- +``` + +これらの変数はドキュメント内に注入されます。 + +```json +{ + body: Object + title: "Introduction" + description: "Learn how to use @nuxt/content." + dir: "/" + extension: ".md" + path: "/index" + slug: "index" + toc: Array + createdAt: DateTime + updatedAt: DateTime +} +``` + +### 抜粋 + +コンテンツの抜粋または要約は、``を仕切りとして使用してコンテンツから抽出できます。 + +```md +--- +title: Introduction +--- + +@nuxt/contentの使用方法を学びます。 + +より多くの仕切りを超えたコンテンツの全量。 +``` + +Description プロパティには、フロントマター プロップ内で定義されていない限り、抜粋コンテンツが含まれます。 + + +::alert{type="info"} +<!--more--> を正確に入力するように注意してください。 つまり、すべて小文字で、空白はありません。 +:: + +変数の例がドキュメントに挿入されます。 +```json +{ + body: Object + title: "Introduction" + description: "@nuxt/contentの使用方法を学びます。" + dir: "/" + extension: ".md" + path: "/index" + slug: "index" + toc: Array + createdAt: DateTime + updatedAt: DateTime +} +``` + +### 見出し + +このモジュールは自動的に`id`と`link`を各見出しに追加します。 + +次のようなMarkdownファイルがあるとします。 + +```md[home.md] +# Lorem ipsum +## dolor—sit—amet +### consectetur & adipisicing +#### elit +##### elit +``` + +これらはJSON ASTに変換され、`nuxt-content`コンポーネントを使用することで、HTMLのようにレンダリングされます。 + +```html +

Lorem ipsum

+

dolor—sit—amet

+

consectetur & adipisicing

+

elit

+
elit
+``` + +> 見出しのリンクは空なので非表示になっています。好きにスタイルを変更できます。たとえば、このドキュメントのヘッダの一つにカーソルを合わせてみてください。 + +### リンク + +リンクは、[remark-external-links](https://github.com/remarkjs/remark-external-links) を使って、有効な `target` 属性と `rel` 属性を追加するように変換されます。このプラグインの設定方法については、[ここ](/ja/configuration#markdown)を参照してください。 + +また、相対リンクは自動的に [nuxt-link](https://nuxtjs.org/api/components-nuxt-link/) に変換されます。これはスマートな事前読込みによって、パフォーマンスを向上させたページコンポーネント間のナビゲーションを提供します。 + +ここでは、相対リンク、HTMLリンク、markdownリンク、外部リンクを使用した例を示します。 + +```md +--- +title: Home +--- + +## Links + +Nuxt Link to Blog + +Html Link to Blog + +[Markdown Link to Blog](/articles) + +External link html + +[External Link markdown](https://nuxtjs.org) +``` + +### 脚注 + +このモジュールは、[remark-footnotes](https://github.com/remarkjs/remark-footnotes)を使った脚注のための拡張Markdown構文をサポートしています。このプラグインの設定方法については、[ここ](/ja/configuration#markdown)をチェックしてください。 + +以下は脚注を使用した例です。 + +```md +Here's a simple footnote,[^1] and here's a longer one.[^bignote] + +[^1]: This is the first footnote. + +[^bignote]: Here's one with multiple paragraphs and code. + + Indent paragraphs to include them in the footnote. + + `{ my code }` + + Add as many paragraphs as you like. +``` + +> さらに詳しく知りたいなら、 [extended syntax guide](https://www.markdownguide.org/extended-syntax/#footnotes)を参照してください。 + +### コードブロック + +このモジュールはコードブロックを自動的にラップし、[PrismJS](https://prismjs.com)クラスを適用します([シンタックスハイライト](#シンタックスハイライト)参照)。 + +Markdownのコードブロックは、` ``` `の内側にラップされます。オプションで、特定のシンタックスハイライトを有効にし、コードブロックの言語を指定できます。 + +基本的に、Markdownはファイル名やコードブロック内の特定の行のハイライトをサポートしていません。しかし、このモジュールは独自のカスタム構文でそれを可能にします。 + +- 中括弧内のハイライトしたい行番号を書く +- 角括弧内にファイル名を書く + +
+```js{1,3-5}[server.js]
+const http = require('http')
+const bodyParser = require('body-parser')
+
+http.createServer((req, res) => {
+  bodyParser.parse(req, (error, body) => {
+    res.end(body)
+  })
+}).listen(3000)
+```
+
+ +`nuxt-content` コンポーネントでレンダリングすると、以下のようになります。 + +```html[server.js] +
+ server.js +
+    
+      ...
+    
+  
+
+``` + +> 行番号は`pre`タグの`data-line`属性に追加されます。 + +> ファイル名は `filename` クラスを持つspanに変換されます。このスタイルはあなた次第です。コードブロックの右上にあるドキュメントを見てください。 + +### シンタックスハイライト + +[PrismJS](https://prismjs.com)を使用したデフォルトのコードハイライトをサポートし、オプションで定義されたテーマをNuxt.jsアプリに注入します([設定](/ja/configuration#markdownprismtheme)を参照)。 + +### HTML + +MarkdownにHTMLを書くこともできます: + +```md[home.md] +--- +title: Home +--- + +## HTML + +

A mix of Markdown and HTML.

+``` + +コンポーネントの中にMarkdownを配置する場合、その前に空の行を続けて配置しなければならないことに注意してください。そうでない場合は、ブロック全体がカスタムHTMLとして扱われます。 + +**ダメな例:** + +```html +
+ *Markdown* and HTML. +
+``` + +**良い例:** + +```html +
+ + *Markdown* and HTML. + +
+``` + +**このようになります**: + +```html +*Markdown* and HTML. +``` + +### Vueコンポーネント + +グローバルVueコンポーネントを使用することも、Markdownを表示しているページに登録されたコンポーネントを使用することもできます。 + +`nuxt/content`はすべてのMarkdownが著者によって提供される(サードパーティのユーザー投稿ではなく)という前提で動作するので、ソースは完全に(タグを含む)処理されます。この仕様に関して、[rehype-raw](https://github.com/rehypejs/rehype-raw)からいくつかの注意点があります。 + +1. コンポーネントはケバブケースで参照する必要があります。 + +```html +Use instead of +``` + +2. セルフクローズタグは使えません。以下は **動作しません**: + +```html + +``` + +以下は **動作します**: + +```html + +``` + +**実例:** + +[ExampleMultiselect.vue](https://github.com/nuxt/content/blob/master/docs/components/examples/global/ExampleMultiselect.vue)というVueコンポーネントがあるとします: + +```md[home.md] +Please choose a *framework*: + + +``` + +**Result:** + +
+Please choose a framework: + + + +
+ +また、フロントマターでコンポーネントのオプションを定義することもできます。 + +```md[home.md] +--- +multiselectOptions: + - VuePress + - Gridsome + - Nuxt +--- + + +``` + +
+ + + +
+ +::alert{type="info"} + +これらのコンポーネントは `` コンポーネントを使ってレンダリングされます。[コンテンツを表示する](/ja/displaying#component)を参照してください。 + +:: + +またMarkdownの中で`