diff --git a/docs/.textlintrc.js b/docs/.textlintrc.js index e9508e371..d73458267 100644 --- a/docs/.textlintrc.js +++ b/docs/.textlintrc.js @@ -11,7 +11,23 @@ module.exports = { severity: 'warning' }, terminology: { - terms: `${__dirname}/.textlint.terms.json` + terms: `${__dirname}/.textlint.terms.json`, + exclude: [ + "front[- ]end(\\w*)", + "back[- ]end(\\w*)", + "command ?line", + "PDF", + "PNG", + "JPG", + "GIF", + "HTML", + "CSS", + "ID", + "Markdown", + "URL", + "walk[- ]through", + "web[- ]?site(s)?" + ] }, 'write-good': { severity: 'warning' diff --git a/docs/docs/.vitepress/config.ts b/docs/docs/.vitepress/config.ts index daa23fb04..430bed7ed 100644 --- a/docs/docs/.vitepress/config.ts +++ b/docs/docs/.vitepress/config.ts @@ -29,19 +29,63 @@ export default defineConfig({ lastUpdatedText: 'Last Updated', sidebar: [ { - text: 'Topics', + text: 'Getting Started', items: [ - {text: 'SEOmatic Plugin', link: '/'}, - {text: 'SEOmatic Overview', link: '/overview.html'}, - {text: 'Issues & Upgrading', link: '/issues.html'}, - {text: 'SEO Resources', link: '/resources.html'}, - {text: 'SEO Technologies', link: '/technologies.html'}, - {text: 'Configuring SEOmatic', link: '/configuring.html'}, - {text: 'SEOmatic Fields', link: '/fields.html'}, - {text: 'Using SEOmatic', link: '/using.html'}, - {text: 'Advanced Usage', link: '/advanced.html'}, + {text: 'Features & Installation', link: '/'}, + {text: 'Overview', link: '/overview'}, + {text: 'Issues & Upgrading', link: '/issues'}, + {text: 'What it Does', link: '/what-it-does'}, ], - } + }, + { + text: 'Using SEOmatic', + items: [ + { + text: 'Configuration', + link: '/configuring/', + collapsed: true, + items: [ + {text: 'Dashboard', link: '/configuring/dashboard'}, + {text: 'Global SEO', link: '/configuring/global-seo'}, + {text: 'Content SEO', link: '/configuring/content-seo'}, + {text: 'Site Settings', link: '/configuring/site-settings'}, + {text: 'Tracking Scripts', link: '/configuring/tracking-scripts'}, + {text: 'Plugin Settings', link: '/configuring/plugin-settings'}, + {text: 'Multi-Environment Config Settings', link: '/configuring/multi-environment'}, + {text: 'Access Permissions', link: '/configuring/access-permissions'}, + ] + }, + { + text: 'Twig Templating', + link: '/using/', + collapsed: true, + items: [ + {text: '???', link: '/using/empty-coalesce-operator'}, + {text: 'seomatic.meta', link: '/using/meta-variables'}, + {text: 'seomatic.site', link: '/using/site-variables'}, + {text: 'seomatic.config', link: '/using/config-variables'}, + {text: 'seomatic.helper', link: '/using/helper-functions'}, + {text: 'seomatic.jsonLd', link: '/using/json-ld-meta'}, + {text: 'seomatic.link', link: '/using/link-meta'}, + {text: 'seomatic.script', link: '/using/script-meta'}, + {text: 'seomatic.tag', link: '/using/tag-meta'}, + {text: 'seomatic.title', link: '/using/title-meta'}, + {text: 'Pagination', link: '/using/pagination'}, + {text: 'Meta Containers', link: '/using/meta-containers'}, + ] + }, + {text: 'SEO Settings Field', link: '/fields'}, + {text: 'Multi-Site', link: '/multi-site'}, + {text: 'Google AMP', link: '/google-amp'}, + {text: 'Advanced Usage', link: '/advanced'}, + ], + }, + { + text: 'SEO Reference', + items: [ + {text: 'Resources', link: '/resources'}, + ], + }, ], nav: [ {text: 'Home', link: 'https://nystudio107.com/plugins/seomatic'}, diff --git a/docs/docs/.vitepress/theme/UsedByLogos.vue b/docs/docs/.vitepress/theme/UsedByLogos.vue new file mode 100644 index 000000000..5785f9b35 --- /dev/null +++ b/docs/docs/.vitepress/theme/UsedByLogos.vue @@ -0,0 +1,22 @@ + + + diff --git a/docs/docs/.vitepress/theme/img/craft-cms-logo.svg b/docs/docs/.vitepress/theme/img/craft-cms-logo.svg new file mode 100644 index 000000000..657256a0e --- /dev/null +++ b/docs/docs/.vitepress/theme/img/craft-cms-logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/docs/.vitepress/theme/img/moz-logo.svg b/docs/docs/.vitepress/theme/img/moz-logo.svg new file mode 100644 index 000000000..77aa2de9d --- /dev/null +++ b/docs/docs/.vitepress/theme/img/moz-logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/docs/.vitepress/theme/index.ts b/docs/docs/.vitepress/theme/index.ts index 66de69c4d..6293e9cf4 100644 --- a/docs/docs/.vitepress/theme/index.ts +++ b/docs/docs/.vitepress/theme/index.ts @@ -3,6 +3,7 @@ import {h, watch} from 'vue' import './custom.css' import NYSLogo from './NYSLogo.vue'; +import UsedByLogos from './UsedByLogos.vue'; // Could also come from .env const GA_ID = 'UA-69117511-1'; @@ -15,7 +16,9 @@ export default { } ) }, - enhanceApp: (ctx) => { + enhanceApp: ({ app, router }) => { + app.component('UsedByLogos', UsedByLogos); + // Google analytics integration if (import.meta.env.PROD && GA_ID && typeof window !== 'undefined') { (function (i, s, o, g, r, a, m) { @@ -33,7 +36,7 @@ export default { ga('create', GA_ID, 'auto') ga('set', 'anonymizeIp', true) // Send a page view any time the route changes - watch(ctx.router.route, (newValue, oldValue) => { + watch(router.route, (newValue, oldValue) => { ga('set', 'page', newValue.path) ga('send', 'pageview') }) diff --git a/docs/docs/advanced.md b/docs/docs/advanced.md index 47ba685ef..19c6c6f17 100644 --- a/docs/docs/advanced.md +++ b/docs/docs/advanced.md @@ -2,23 +2,30 @@ title: Advanced Usage description: Advanced Usage documentation for the SEOmatic plugin. The SEOmatic plugin facilitates modern SEO best practices & implementation for Craft CMS 3. --- + # Advanced Usage +Using the debug toolbar, managing config settings, events for PHP developers, bundle / container settings, and using the headless SPA API. + ## Debug Toolbar -SEOmatic included a debug panel for the [Yii2 Debug Toolbar](https://nystudio107.com/blog/profiling-your-website-with-craft-cms-3s-debug-toolbar) that allows you to inspect & debug your SEO data. +SEOmatic includes a debug panel for the [Yii2 Debug Toolbar](https://nystudio107.com/blog/profiling-your-website-with-craft-cms-3s-debug-toolbar) that allows you to inspect and debug your SEO data. -![Screenshot](./resources/screenshots/seomatic-yii2-debug-toolbar.png) +![Screenshot of Craft’s debug toolbar with an SEOmatic section at the far right](./resources/screenshots/seomatic-yii2-debug-toolbar.png) The debug panel displays debug information about the tags SEOmatic generates, as well as the variables it uses to do so. You can view the **Combined** composed SEO data, or the discrete SEO data coming from the layered **Entry SEO**, **Content SEO**, and **Global SEO** settings. +The SEOmatic Debug Toolbar panel provides valuable debugging information if you have the Yii2 Debug Toolbar enabled, but it does add some overhead as well. + +If you're doing performance profiling, you can keep the Yii2 Debug Toolbar enabled, but disable the SEOmatic panel via **Plugin Settings → Advanced → SEOmatic Debug Toolbar Panel**. + ### Tags Tags are objects that represent rendered HTML tags the in the webpage. Tags are grouped together into containers for organizational purposes. -![Screenshot](./resources/screenshots/seomatic-debug-tags.png) +![Screenshot of the expanded debug toolbar open to the SEOmatic section, which has tabs for Tags and Variables, a visualization of the SEO cascade, and a listing of meta tag properties and how they’re rendered](./resources/screenshots/seomatic-debug-tags.png) You can inspect the **Properties** of the tags in each container, as well as the corresponding **Parsed Properties** after they have been rendered as Twig. @@ -47,7 +54,7 @@ Tag property example: Variables are used throughout SEOmatic when rendering tags, or controlling how tags are rendered. Tag properties often reference these variables via Twig expressions. -![Screenshot](./resources/screenshots/seomatic-debug-variables.png) +![Screenshot of the SEOmatic debug toolbar section examining meta global vars, where the title text includes Twig usage tips](./resources/screenshots/seomatic-debug-variables.png) You can inspect the **Properties** of the variables, as well as the corresponding **Parsed Properties** after they have been rendered as Twig. @@ -56,6 +63,7 @@ You can expand and sub-properties of nested properties by clicking on them. When hovering the cursor over any property, a clipboard icon will appear that when clicked on copies to the clipboard example code of how to get/set values for that particular property via Twig. Variable example: + ```twig {#-- Get the value --#} {% set value = seomatic.meta.seoImage %} @@ -67,97 +75,110 @@ Variable example: SEOmatic supports the standard `config.php` multi-environment friendly config file for the plugin settings. Just copy the `config.php` to your Craft `config/` directory as `seomatic.php` and you can configure the settings in a multi-environment friendly way. -These are the same settings that are configured in the **Plugin Settings** in the Control Panel. +These are the same settings that are configured in the **Plugin Settings** in the control panel. ## Events ### IncludeContainerEvent - const EVENT_INCLUDE_CONTAINER = 'includeContainer'; - The event that is triggered when a container is about to be included. ```php use nystudio107\seomatic\events\IncludeContainerEvent; use nystudio107\seomatic\base\Container; use yii\base\Event; -Event::on(Container::class, Container::EVENT_INCLUDE_CONTAINER, function(IncludeContainerEvent $e) { - $e->include = false; -}); + +Event::on( + Container::class, + Container::EVENT_INCLUDE_CONTAINER, + function(IncludeContainerEvent $event) { + $event->include = false; + } +); ``` ### InvalidateContainerCachesEvent - const EVENT_INVALIDATE_CONTAINER_CACHES = 'invalidateContainerCaches'; - The event that is triggered when SEOmatic is about to clear its meta container caches ```php use nystudio107\seomatic\events\InvalidateContainerCachesEvent; use nystudio107\seomatic\services\MetaContainers; use yii\base\Event; -Event::on(MetaContainers::class, MetaContainers::EVENT_INVALIDATE_CONTAINER_CACHES, function(InvalidateContainerCachesEvent $e) { - // Container caches are about to be cleared -}); + +Event::on( + MetaContainers::class, + MetaContainers::EVENT_INVALIDATE_CONTAINER_CACHES, + function(InvalidateContainerCachesEvent $event) { + // Container caches are about to be cleared + } +); ``` ### IncludeSitemapEntryEvent - const EVENT_INCLUDE_SITEMAP_ENTRY = 'IncludeSitemapEntryEvent'; - The event that is triggered when an entry is about to be included in a sitemap. ```php - use nystudio107\seomatic\events\IncludeSitemapEntryEvent; - use nystudio107\seomatic\helpers\Sitemap; - use yii\base\Event; - Event::on(Sitemap::class, Sitemap::EVENT_INCLUDE_SITEMAP_ENTRY, function(IncludeSitemapEntryEvent $e) { - $e->include = false; - }); +use nystudio107\seomatic\events\IncludeSitemapEntryEvent; +use nystudio107\seomatic\helpers\Sitemap; +use yii\base\Event; + +Event::on( + Sitemap::class, + Sitemap::EVENT_INCLUDE_SITEMAP_ENTRY, + function(IncludeSitemapEntryEvent $event) { + $event->include = false; + } +); ``` ### RegisterSitemapUrlsEvent - const EVENT_REGISTER_SITEMAP_URLS = 'registerSitemapUrls'; - The event that is triggered when registering additional URLs for a sitemap. ```php use nystudio107\seomatic\events\RegisterSitemapUrlsEvent; use nystudio107\seomatic\models\SitemapCustomTemplate; use yii\base\Event; -Event::on(SitemapCustomTemplate::class, SitemapCustomTemplate::EVENT_REGISTER_SITEMAP_URLS, function(RegisterSitemapUrlsEvent $e) { - $e->sitemaps[] = [ - 'loc' => $url, - 'changefreq' => $changeFreq, - 'priority' => $priority, - 'lastmod' => $lastMod, - ]; -}); + +Event::on( + SitemapCustomTemplate::class, + SitemapCustomTemplate::EVENT_REGISTER_SITEMAP_URLS, + function(RegisterSitemapUrlsEvent $event) { + $event->sitemaps[] = [ + 'loc' => $url, + 'changefreq' => $changeFreq, + 'priority' => $priority, + 'lastmod' => $lastMod, + ]; + } +); ``` ### RegisterSitemapsEvent - const EVENT_REGISTER_SITEMAPS = 'registerSitemaps'; - The event that is triggered when registering additional sitemaps for the sitemap index. ```php use nystudio107\seomatic\events\RegisterSitemapsEvent; use nystudio107\seomatic\models\SitemapIndexTemplate; use yii\base\Event; -Event::on(SitemapIndexTemplate::class, SitemapIndexTemplate::EVENT_REGISTER_SITEMAPS, function(RegisterSitemapsEvent $e) { - $e->sitemaps[] = [ - 'loc' => $url, - 'lastmod' => $lastMod, - ]; -}); + +Event::on( + SitemapIndexTemplate::class, + SitemapIndexTemplate::EVENT_REGISTER_SITEMAPS, + function(RegisterSitemapsEvent $event) { + $event->sitemaps[] = [ + 'loc' => $url, + 'lastmod' => $lastMod, + ]; + } +); ``` ### RegisterComponentTypesEvent - const EVENT_REGISTER_SEO_ELEMENT_TYPES = 'registerSeoElementTypes'; - The event that is triggered when registering SeoElement types SeoElement types must implement [[SeoElementInterface]] @@ -167,7 +188,8 @@ use nystudio107\seomatic\services\SeoElements; use craft\events\RegisterComponentTypesEvent; use yii\base\Event; -Event::on(SeoElements::class, +Event::on( + SeoElements::class, SeoElements::EVENT_REGISTER_SEO_ELEMENT_TYPES, function(RegisterComponentTypesEvent $event) { $event->types[] = MySeoElement::class; @@ -177,24 +199,31 @@ Event::on(SeoElements::class, ### AddDynamicMetaEvent - const EVENT_ADD_DYNAMIC_META = 'addDynamicMeta'; - The event that is triggered when SEOmatic has included the standard meta containers, and gives your plugin/module the chance to add whatever custom dynamic meta items you like ```php use nystudio107\seomatic\events\AddDynamicMetaEvent; use nystudio107\seomatic\helpers\DynamicMeta; use yii\base\Event; -Event::on(DynamicMeta::class, DynamicMeta::EVENT_ADD_DYNAMIC_META, function(AddDynamicMetaEvent $e) { - // Add whatever dynamic meta items to the containers as you like -}); +use nystudio107\seomatic\Seomatic; + +Event::on( + DynamicMeta::class, + DynamicMeta::EVENT_ADD_DYNAMIC_META, + function(AddDynamicMetaEvent $event) { + // Add whatever dynamic meta items to the containers as you like + Seomatic::$plugin->getMetaContainers() + ->metaGlobalVars + ->seoDescription = "This description overrides any others!"; + } +); ``` ## Meta Bundle / Container Settings -The directory `vendor/nystudio107/seomatic/src/seomatic-config` contains a number of files that are used when initially configuring SEOmatic. +The [`vendor/nystudio107/seomatic/src/seomatic-config`](https://github.com/nystudio107/craft-seomatic/tree/develop/src/seomatic-config) directory contains a number of files that are used when initially configuring SEOmatic. -![Screenshot](./resources/screenshots/seomatic-seomatic-config.png) +![Screenshot of a Finder window displaying the contents of the seomatic-config directory, whose visible top-level folders are categorymeta, entrymeta, and globalmeta](./resources/screenshots/seomatic-seomatic-config.png) You can copy this entire directory to your Craft `config/` directory, and customize the files to your heart’s content. SEOmatic will first look in the `config/` directory for any given file, and then fall back on its own internal `seomatic-config` files. @@ -204,11 +233,11 @@ You can bump the `Bundle.php`'s `bundleVersion` setting if you want it to re-rea ## Headless SPA API -SEOmatic allows you to fetch the meta information for any page via a controller API endpoint, so you can render the metadata via a frontend framework like VueJS or React. +SEOmatic allows you to fetch the meta information for any page via a controller API endpoint, so you can render the metadata via a front-end framework like VueJS or React. ### GraphQL Query support -To retrieve SEOmatic container data through the native [GraphQL in Craft CMS 3.3](https://docs.craftcms.com/v3/graphql.html#sending-api-requests) or the [CraftQL plugin](https://github.com/markhuot/craftql), use the `seomatic` field in your GraphQL query. Each parameter will return that container’s data, ready for insertion into the DOM. +To retrieve SEOmatic container data through [GraphQL in Craft CMS 3](https://craftcms.com/docs/3.x/graphql.html#sending-api-requests), use the `seomatic` field in your GraphQL query. Each parameter will return that container’s data, ready for insertion into the DOM. #### Stand-alone GraphQL queries @@ -217,12 +246,12 @@ You must as least pass in the URI you want metadata for: ```graphql { seomatic (uri: "/") { - metaTitleContainer - metaTagContainer - metaLinkContainer - metaScriptContainer - metaJsonLdContainer - metaSiteVarsContainer + metaTitleContainer + metaTagContainer + metaLinkContainer + metaScriptContainer + metaJsonLdContainer + metaSiteVarsContainer } } ``` @@ -232,12 +261,12 @@ You must as least pass in the URI you want metadata for: ```graphql { seomatic (uri: "/", siteId: 2) { - metaTitleContainer - metaTagContainer - metaLinkContainer - metaScriptContainer - metaJsonLdContainer - metaSiteVarsContainer + metaTitleContainer + metaTagContainer + metaLinkContainer + metaScriptContainer + metaJsonLdContainer + metaSiteVarsContainer } } ``` @@ -247,12 +276,12 @@ You must as least pass in the URI you want metadata for: ```graphql { seomatic (uri: "/", site: "french") { - metaTitleContainer - metaTagContainer - metaLinkContainer - metaScriptContainer - metaJsonLdContainer - metaSiteVarsContainer + metaTitleContainer + metaTagContainer + metaLinkContainer + metaScriptContainer + metaJsonLdContainer + metaSiteVarsContainer } } ``` @@ -262,12 +291,12 @@ You must as least pass in the URI you want metadata for: ```graphql { seomatic (uri: "/", asArray: true) { - metaTitleContainer - metaTagContainer - metaLinkContainer - metaScriptContainer - metaJsonLdContainer - metaSiteVarsContainer + metaTitleContainer + metaTagContainer + metaLinkContainer + metaScriptContainer + metaJsonLdContainer + metaSiteVarsContainer } } ``` @@ -280,12 +309,12 @@ This is useful if you’re using Next.js, Nuxt.js, Gatsby, Gridsome, or anything ```graphql { seomatic (uri: "/", environment: staging) { - metaTitleContainer - metaTagContainer - metaLinkContainer - metaScriptContainer - metaJsonLdContainer - metaSiteVarsContainer + metaTitleContainer + metaTagContainer + metaLinkContainer + metaScriptContainer + metaJsonLdContainer + metaSiteVarsContainer } } ``` @@ -295,15 +324,13 @@ This is useful if you are using a single Craft CMS instance to render metadata f Valid values are `local` for local development, `staging` for staging, and `live` for live production. -![Screenshot](./resources/screenshots/seomatic-craftql-query.png) +![Screenshot of CraftQL for some reason demonstrating a GraphQL query](./resources/screenshots/seomatic-craftql-query.png) #### Piggybacking GraphQL queries You can also piggyback on an entries query, to return all of your data for an entry as well as the SEOmatic metadata in one request. -**N.B.:** This requires using either Craft CMS 3.4 or later, or the CraftQL plugin to work. - -Native Craft CMS GraphQL: +Craft CMS GraphQL: ```graphql { @@ -322,29 +349,9 @@ Native Craft CMS GraphQL: } ``` -CraftQL Plugin: - -```graphql -{ - entry(section: homepage) { - id - title - ... on Homepage { - seomatic { - metaTitleContainer - metaTagContainer - metaLinkContainer - metaScriptContainer - metaJsonLdContainer - } - } - } -} -``` - In this case, no arguments are passed in, because the URI and siteId will be taken from the parent Entry element. But you can pass in the `asArray` argument too: -Native Craft CMS GraphQL: +Craft CMS GraphQL: ```graphql { @@ -363,31 +370,11 @@ Native Craft CMS GraphQL: } ``` -CraftQL Plugin: - -```graphql -{ - entry(section: homepage) { - id - title - ... on Homepage { - seomatic(asArray: true) { - metaTitleContainer - metaTagContainer - metaLinkContainer - metaScriptContainer - metaJsonLdContainer - } - } - } -} -``` - ### Frontend Templates GraphQL queries -SEOmatic an provide you with the frontend templates such as `robots.txt`, `humans.txt`, etc. as well: +SEOmatic an provide you with the front-end templates such as `robots.txt`, `humans.txt`, etc. as well: -![Screenshot](./resources/screenshots/seomatic-graphql-frontendtemplates-query.png) +![Screenshot of Craft’s GraphQL explorer querying SEOmatic’s `frontendTemplates`](./resources/screenshots/seomatic-graphql-frontendtemplates-query.png) ```graphql { @@ -408,13 +395,13 @@ Arguments: `site:` String - Optional - The site handle to resolve the sitemap for. -`type:` String - The frontend container type, which can be `robots`, `humans`, `security`, or `ads` +`type:` String - The front-end container type, which can be `robots`, `humans`, `security`, or `ads` #### Sitemap GraphQL queries SEOmatic can provide you with the sitemap data via GraphQL as well. -![Screenshot](./resources/screenshots/seomatic-graphql-sitemaps-query.png) +![Screenshot of Craft’s GraphQL explorer querying SEOmatic’s `sitemapIndexes`, `sitemaps`, and `sitemapStyles`](./resources/screenshots/seomatic-graphql-sitemaps-query.png) SEOmatic allows you to query for `sitemapIndexes`: @@ -469,7 +456,7 @@ SEOmatic also allows you to query for `sitemapStyles`: ```graphql { seomatic { - sitemapStyles { + sitemapStyles { filename contents } @@ -481,7 +468,9 @@ This returns the [XSL stylesheet](https://www.w3.org/Style/XSL/WhatIsXSL.html) t ### Meta Container API Endpoints -**N.B.:** Anonymous access to the Meta Container endpoints are disabled by default; you’ll need to enable them in SEOmatic → Plugin Settings → Endpoints +::: tip +Anonymous access to the Meta Container endpoints are disabled by default; enable them in **SEOmatic** → **Plugin Settings** → **Endpoints**. +::: To get all of the meta containers for a given URI, the controller action is: @@ -516,287 +505,290 @@ Should you wish to have the items in the meta containers return as an array of d ``` Which will return the data in array form: + +::: details Expand Result ```json { - "MetaTitleContainer": { - "title": { - "title": "[devMode] Craft3 | Homepage" - } + "MetaTitleContainer": { + "title": { + "title": "[devMode] Craft3 | Homepage" + } + }, + "MetaTagContainer": { + "generator": { + "content": "SEOmatic", + "name": "generator" + }, + "referrer": { + "content": "no-referrer-when-downgrade", + "name": "referrer" }, - "MetaTagContainer": { - "generator": { - "content": "SEOmatic", - "name": "generator" + "robots": { + "content": "all", + "name": "robots" + } + }, + "MetaLinkContainer": { + "canonical": { + "href": "http://craft3.test/", + "rel": "canonical" + }, + "author": { + "href": "/humans.txt", + "rel": "author", + "type": "text/plain" + }, + "alternate": { + "href": "http://craft3.test/", + "hreflang": "es", + "rel": "alternate" + } + }, + "MetaScriptContainer": [], + "MetaJsonLdContainer": { + "WebPage": { + "@context": "http://schema.org", + "@type": "WebPage", + "image": { + "@type": "ImageObject", + "height": "804", + "width": "1200" + }, + "inLanguage": "en-us", + "mainEntityOfPage": "http://craft3.test/", + "name": "Homepage", + "url": "http://craft3.test/" + }, + "BreadcrumbList": { + "@context": "http://schema.org", + "@type": "BreadcrumbList", + "description": "Breadcrumbs list", + "itemListElement": [ + { + "@type": "ListItem", + "name": "Homepage", + "item": "http://craft3.test/", + "position": 1 + } + ], + "name": "Breadcrumbs" + } + }, + "MetaSiteVarsContainer": { + "siteName": "woof", + "identity": { + "siteType": "Organization", + "siteSubType": "LocalBusiness", + "siteSpecificType": "none", + "computedType": "LocalBusiness", + "genericName": "", + "genericAlternateName": "", + "genericDescription": "", + "genericUrl": "$HOME", + "genericImage": null, + "genericImageWidth": "229", + "genericImageHeight": "220", + "genericImageIds": [ + "25" + ], + "genericTelephone": "", + "genericEmail": "", + "genericStreetAddress": "", + "genericAddressLocality": "", + "genericAddressRegion": "", + "genericPostalCode": "", + "genericAddressCountry": "", + "genericGeoLatitude": "", + "genericGeoLongitude": "", + "personGender": "Male", + "personBirthPlace": "", + "organizationDuns": "", + "organizationFounder": "", + "organizationFoundingDate": "", + "organizationFoundingLocation": "", + "organizationContactPoints": "", + "corporationTickerSymbol": "", + "localBusinessPriceRange": "$", + "localBusinessOpeningHours": [ + { + "open": null, + "close": null }, - "referrer": { - "content": "no-referrer-when-downgrade", - "name": "referrer" + { + "open": null, + "close": null }, - "robots": { - "content": "all", - "name": "robots" - } - }, - "MetaLinkContainer": { - "canonical": { - "href": "http://craft3.test/", - "rel": "canonical" + { + "open": null, + "close": null }, - "author": { - "href": "/humans.txt", - "rel": "author", - "type": "text/plain" + { + "open": null, + "close": null }, - "alternate": { - "href": "http://craft3.test/", - "hreflang": "es", - "rel": "alternate" - } - }, - "MetaScriptContainer": [], - "MetaJsonLdContainer": { - "WebPage": { - "@context": "http://schema.org", - "@type": "WebPage", - "image": { - "@type": "ImageObject", - "height": "804", - "width": "1200" - }, - "inLanguage": "en-us", - "mainEntityOfPage": "http://craft3.test/", - "name": "Homepage", - "url": "http://craft3.test/" + { + "open": null, + "close": null + }, + { + "open": null, + "close": null }, - "BreadcrumbList": { - "@context": "http://schema.org", - "@type": "BreadcrumbList", - "description": "Breadcrumbs list", - "itemListElement": [ - { - "@type": "ListItem", - "name": "Homepage", - "item": "http://craft3.test/", - "position": 1 - } - ], - "name": "Breadcrumbs" + { + "open": null, + "close": null } + ], + "restaurantServesCuisine": "", + "restaurantMenuUrl": "", + "restaurantReservationsUrl": "" }, - "MetaSiteVarsContainer": { - "siteName": "woof", - "identity": { - "siteType": "Organization", - "siteSubType": "LocalBusiness", - "siteSpecificType": "none", - "computedType": "LocalBusiness", - "genericName": "", - "genericAlternateName": "", - "genericDescription": "", - "genericUrl": "$HOME", - "genericImage": null, - "genericImageWidth": "229", - "genericImageHeight": "220", - "genericImageIds": [ - "25" - ], - "genericTelephone": "", - "genericEmail": "", - "genericStreetAddress": "", - "genericAddressLocality": "", - "genericAddressRegion": "", - "genericPostalCode": "", - "genericAddressCountry": "", - "genericGeoLatitude": "", - "genericGeoLongitude": "", - "personGender": "Male", - "personBirthPlace": "", - "organizationDuns": "", - "organizationFounder": "", - "organizationFoundingDate": "", - "organizationFoundingLocation": "", - "organizationContactPoints": "", - "corporationTickerSymbol": "", - "localBusinessPriceRange": "$", - "localBusinessOpeningHours": [ - { - "open": null, - "close": null - }, - { - "open": null, - "close": null - }, - { - "open": null, - "close": null - }, - { - "open": null, - "close": null - }, - { - "open": null, - "close": null - }, - { - "open": null, - "close": null - }, - { - "open": null, - "close": null - } - ], - "restaurantServesCuisine": "", - "restaurantMenuUrl": "", - "restaurantReservationsUrl": "" + "creator": { + "siteType": "Organization", + "siteSubType": "LocalBusiness", + "siteSpecificType": "none", + "computedType": "LocalBusiness", + "genericName": "", + "genericAlternateName": "", + "genericDescription": "", + "genericUrl": "", + "genericImage": null, + "genericImageWidth": "1340", + "genericImageHeight": "596", + "genericImageIds": [ + "24" + ], + "genericTelephone": "", + "genericEmail": "", + "genericStreetAddress": "", + "genericAddressLocality": "", + "genericAddressRegion": "", + "genericPostalCode": "", + "genericAddressCountry": "", + "genericGeoLatitude": "", + "genericGeoLongitude": "", + "personGender": "Male", + "personBirthPlace": "", + "organizationDuns": "", + "organizationFounder": "", + "organizationFoundingDate": "", + "organizationFoundingLocation": "", + "organizationContactPoints": "", + "corporationTickerSymbol": "", + "localBusinessPriceRange": "$", + "localBusinessOpeningHours": [ + { + "open": null, + "close": null }, - "creator": { - "siteType": "Organization", - "siteSubType": "LocalBusiness", - "siteSpecificType": "none", - "computedType": "LocalBusiness", - "genericName": "", - "genericAlternateName": "", - "genericDescription": "", - "genericUrl": "", - "genericImage": null, - "genericImageWidth": "1340", - "genericImageHeight": "596", - "genericImageIds": [ - "24" - ], - "genericTelephone": "", - "genericEmail": "", - "genericStreetAddress": "", - "genericAddressLocality": "", - "genericAddressRegion": "", - "genericPostalCode": "", - "genericAddressCountry": "", - "genericGeoLatitude": "", - "genericGeoLongitude": "", - "personGender": "Male", - "personBirthPlace": "", - "organizationDuns": "", - "organizationFounder": "", - "organizationFoundingDate": "", - "organizationFoundingLocation": "", - "organizationContactPoints": "", - "corporationTickerSymbol": "", - "localBusinessPriceRange": "$", - "localBusinessOpeningHours": [ - { - "open": null, - "close": null - }, - { - "open": null, - "close": null - }, - { - "open": null, - "close": null - }, - { - "open": null, - "close": null - }, - { - "open": null, - "close": null - }, - { - "open": null, - "close": null - }, - { - "open": null, - "close": null - } - ], - "restaurantServesCuisine": "", - "restaurantMenuUrl": "", - "restaurantReservationsUrl": "" + { + "open": null, + "close": null }, - "twitterHandle": "", - "facebookProfileId": "", - "facebookAppId": "", - "googleSiteVerification": "", - "bingSiteVerification": "", - "pinterestSiteVerification": "", - "sameAsLinks": { - "twitter": { - "siteName": "Twitter", - "handle": "twitter", - "url": "" - }, - "facebook": { - "siteName": "Facebook", - "handle": "facebook", - "url": "" - }, - "wikipedia": { - "siteName": "Wikipedia", - "handle": "wikipedia", - "url": "" - }, - "linkedin": { - "siteName": "LinkedIn", - "handle": "linkedin", - "url": "" - }, - "googleplus": { - "siteName": "Google+", - "handle": "googleplus", - "url": "" - }, - "youtube": { - "siteName": "YouTube", - "handle": "youtube", - "url": "" - }, - "instagram": { - "siteName": "Instagram", - "handle": "instagram", - "url": "" - }, - "pinterest": { - "siteName": "Pinterest", - "handle": "pinterest", - "url": "" - }, - "github": { - "siteName": "GitHub", - "handle": "github", - "url": "" - }, - "vimeo": { - "siteName": "Vimeo", - "handle": "vimeo", - "url": "" - } + { + "open": null, + "close": null }, - "siteLinksSearchTarget": "", - "siteLinksQueryInput": "", - "referrer": "no-referrer-when-downgrade", - "additionalSitemapUrls": [], - "additionalSitemapUrlsDateUpdated": null, - "additionalSitemaps": [] - }, - "FrontendTemplateContainer": [ { - "humans": "/* TEAM */\n\nCreator: nystudio107\nURL: https://nystudio107.com/\nDescription: We do technology-based consulting, branding, design, and development. Making the web better one site at a time, with a focus on performance, usability & SEO\n\n/* THANKS */\n\nCraft CMS - https://craftcms.com\nPixel & Tonic - https://pixelandtonic.com\n\n/* SITE */\n\nStandards: HTML5, CSS3\nComponents: Craft CMS 3, Yii2, PHP, JavaScript, SEOmatic\n" + "open": null, + "close": null }, { - "robots": "# robots.txt for http://localhost:8000/\n\nsitemap: http://localhost:8000/sitemaps-1-sitemap.xml\nsitemap: http://localhost:8000/es/sitemaps-1-sitemap.xml\n\n# local - disallow all\n\nUser-agent: *\nDisallow: /\n\n" + "open": null, + "close": null }, { - "ads": "# ads.txt file for http://localhost:8000/\n# More info: https://support.google.com/admanager/answer/7441288?hl=en\nhttp://localhost:8000/,123,DIRECT\n" + "open": null, + "close": null + }, + { + "open": null, + "close": null } - ] + ], + "restaurantServesCuisine": "", + "restaurantMenuUrl": "", + "restaurantReservationsUrl": "" + }, + "twitterHandle": "", + "facebookProfileId": "", + "facebookAppId": "", + "googleSiteVerification": "", + "bingSiteVerification": "", + "pinterestSiteVerification": "", + "sameAsLinks": { + "twitter": { + "siteName": "Twitter", + "handle": "twitter", + "url": "" + }, + "facebook": { + "siteName": "Facebook", + "handle": "facebook", + "url": "" + }, + "wikipedia": { + "siteName": "Wikipedia", + "handle": "wikipedia", + "url": "" + }, + "linkedin": { + "siteName": "LinkedIn", + "handle": "linkedin", + "url": "" + }, + "googleplus": { + "siteName": "Google+", + "handle": "googleplus", + "url": "" + }, + "youtube": { + "siteName": "YouTube", + "handle": "youtube", + "url": "" + }, + "instagram": { + "siteName": "Instagram", + "handle": "instagram", + "url": "" + }, + "pinterest": { + "siteName": "Pinterest", + "handle": "pinterest", + "url": "" + }, + "github": { + "siteName": "GitHub", + "handle": "github", + "url": "" + }, + "vimeo": { + "siteName": "Vimeo", + "handle": "vimeo", + "url": "" + } + }, + "siteLinksSearchTarget": "", + "siteLinksQueryInput": "", + "referrer": "no-referrer-when-downgrade", + "additionalSitemapUrls": [], + "additionalSitemapUrlsDateUpdated": null, + "additionalSitemaps": [] + }, + "FrontendTemplateContainer": [ + { + "humans": "/* TEAM */\n\nCreator: nystudio107\nURL: https://nystudio107.com/\nDescription: We do technology-based consulting, branding, design, and development. Making the web better one site at a time, with a focus on performance, usability & SEO\n\n/* THANKS */\n\nCraft CMS - https://craftcms.com\nPixel & Tonic - https://pixelandtonic.com\n\n/* SITE */\n\nStandards: HTML5, CSS3\nComponents: Craft CMS 3, Yii2, PHP, JavaScript, SEOmatic\n" + }, + { + "robots": "# robots.txt for http://localhost:8000/\n\nsitemap: http://localhost:8000/sitemaps-1-sitemap.xml\nsitemap: http://localhost:8000/es/sitemaps-1-sitemap.xml\n\n# local - disallow all\n\nUser-agent: *\nDisallow: /\n\n" + }, + { + "ads": "# ads.txt file for http://localhost:8000/\n# More info: https://support.google.com/admanager/answer/7441288?hl=en\nhttp://localhost:8000/,123,DIRECT\n" + } + ] } ``` +::: You can also request individual meta containers. @@ -807,9 +799,10 @@ Title container: ``` ...will return just the Title container: + ```json { - "MetaTitleContainer": "[devMode] Craft3 | Homepage" + "MetaTitleContainer": "[devMode] Craft3 | Homepage" } ``` @@ -820,10 +813,11 @@ Tag container: ``` ...will return just the Tag container: + ```json { - "MetaTagContainer": "" - } + "MetaTagContainer": "" +} ``` Script container: @@ -835,7 +829,7 @@ Script container: ...will return just the Script container: ```json { - "MetaScriptContainer": "" + "MetaScriptContainer": "" } ``` @@ -846,9 +840,10 @@ Link container: ``` ...will return just the Link container: + ```json { - "MetaLinkContainer": "" + "MetaLinkContainer": "" } ``` @@ -861,7 +856,7 @@ JSON-LD container: ...will return just the JSON-LD container: ```json { - "MetaJsonLdContainer": "" + "MetaJsonLdContainer": "" } ``` @@ -874,7 +869,7 @@ SiteVars container: ...will return just the MetaSiteVars container: ```json { - "MetaSiteVarsContainer": "{\"siteName\":\"woof\",\"identity\":{\"siteType\":\"Organization\",\"siteSubType\":\"LocalBusiness\",\"siteSpecificType\":\"none\",\"computedType\":\"LocalBusiness\",\"genericName\":\"\",\"genericAlternateName\":\"\",\"genericDescription\":\"\",\"genericUrl\":\"$HOME\",\"genericImage\":null,\"genericImageWidth\":\"229\",\"genericImageHeight\":\"220\",\"genericImageIds\":[\"25\"],\"genericTelephone\":\"\",\"genericEmail\":\"\",\"genericStreetAddress\":\"\",\"genericAddressLocality\":\"\",\"genericAddressRegion\":\"\",\"genericPostalCode\":\"\",\"genericAddressCountry\":\"\",\"genericGeoLatitude\":\"\",\"genericGeoLongitude\":\"\",\"personGender\":\"Male\",\"personBirthPlace\":\"\",\"organizationDuns\":\"\",\"organizationFounder\":\"\",\"organizationFoundingDate\":\"\",\"organizationFoundingLocation\":\"\",\"organizationContactPoints\":\"\",\"corporationTickerSymbol\":\"\",\"localBusinessPriceRange\":\"$\",\"localBusinessOpeningHours\":[{\"open\":null,\"close\":null},{\"open\":null,\"close\":null},{\"open\":null,\"close\":null},{\"open\":null,\"close\":null},{\"open\":null,\"close\":null},{\"open\":null,\"close\":null},{\"open\":null,\"close\":null}],\"restaurantServesCuisine\":\"\",\"restaurantMenuUrl\":\"\",\"restaurantReservationsUrl\":\"\"},\"creator\":{\"siteType\":\"Organization\",\"siteSubType\":\"LocalBusiness\",\"siteSpecificType\":\"none\",\"computedType\":\"LocalBusiness\",\"genericName\":\"\",\"genericAlternateName\":\"\",\"genericDescription\":\"\",\"genericUrl\":\"\",\"genericImage\":null,\"genericImageWidth\":\"1340\",\"genericImageHeight\":\"596\",\"genericImageIds\":[\"24\"],\"genericTelephone\":\"\",\"genericEmail\":\"\",\"genericStreetAddress\":\"\",\"genericAddressLocality\":\"\",\"genericAddressRegion\":\"\",\"genericPostalCode\":\"\",\"genericAddressCountry\":\"\",\"genericGeoLatitude\":\"\",\"genericGeoLongitude\":\"\",\"personGender\":\"Male\",\"personBirthPlace\":\"\",\"organizationDuns\":\"\",\"organizationFounder\":\"\",\"organizationFoundingDate\":\"\",\"organizationFoundingLocation\":\"\",\"organizationContactPoints\":\"\",\"corporationTickerSymbol\":\"\",\"localBusinessPriceRange\":\"$\",\"localBusinessOpeningHours\":[{\"open\":null,\"close\":null},{\"open\":null,\"close\":null},{\"open\":null,\"close\":null},{\"open\":null,\"close\":null},{\"open\":null,\"close\":null},{\"open\":null,\"close\":null},{\"open\":null,\"close\":null}],\"restaurantServesCuisine\":\"\",\"restaurantMenuUrl\":\"\",\"restaurantReservationsUrl\":\"\"},\"twitterHandle\":\"\",\"facebookProfileId\":\"\",\"facebookAppId\":\"\",\"googleSiteVerification\":\"\",\"bingSiteVerification\":\"\",\"pinterestSiteVerification\":\"\",\"sameAsLinks\":{\"twitter\":{\"siteName\":\"Twitter\",\"handle\":\"twitter\",\"url\":\"\"},\"facebook\":{\"siteName\":\"Facebook\",\"handle\":\"facebook\",\"url\":\"\"},\"wikipedia\":{\"siteName\":\"Wikipedia\",\"handle\":\"wikipedia\",\"url\":\"\"},\"linkedin\":{\"siteName\":\"LinkedIn\",\"handle\":\"linkedin\",\"url\":\"\"},\"googleplus\":{\"siteName\":\"Google+\",\"handle\":\"googleplus\",\"url\":\"\"},\"youtube\":{\"siteName\":\"YouTube\",\"handle\":\"youtube\",\"url\":\"\"},\"instagram\":{\"siteName\":\"Instagram\",\"handle\":\"instagram\",\"url\":\"\"},\"pinterest\":{\"siteName\":\"Pinterest\",\"handle\":\"pinterest\",\"url\":\"\"},\"github\":{\"siteName\":\"GitHub\",\"handle\":\"github\",\"url\":\"\"},\"vimeo\":{\"siteName\":\"Vimeo\",\"handle\":\"vimeo\",\"url\":\"\"}},\"siteLinksSearchTarget\":\"\",\"siteLinksQueryInput\":\"\",\"referrer\":\"no-referrer-when-downgrade\",\"additionalSitemapUrls\":[],\"additionalSitemapUrlsDateUpdated\":null,\"additionalSitemaps\":[]}" + "MetaSiteVarsContainer": "{\"siteName\":\"woof\",\"identity\":{\"siteType\":\"Organization\",\"siteSubType\":\"LocalBusiness\",\"siteSpecificType\":\"none\",\"computedType\":\"LocalBusiness\",\"genericName\":\"\",\"genericAlternateName\":\"\",\"genericDescription\":\"\",\"genericUrl\":\"$HOME\",\"genericImage\":null,\"genericImageWidth\":\"229\",\"genericImageHeight\":\"220\",\"genericImageIds\":[\"25\"],\"genericTelephone\":\"\",\"genericEmail\":\"\",\"genericStreetAddress\":\"\",\"genericAddressLocality\":\"\",\"genericAddressRegion\":\"\",\"genericPostalCode\":\"\",\"genericAddressCountry\":\"\",\"genericGeoLatitude\":\"\",\"genericGeoLongitude\":\"\",\"personGender\":\"Male\",\"personBirthPlace\":\"\",\"organizationDuns\":\"\",\"organizationFounder\":\"\",\"organizationFoundingDate\":\"\",\"organizationFoundingLocation\":\"\",\"organizationContactPoints\":\"\",\"corporationTickerSymbol\":\"\",\"localBusinessPriceRange\":\"$\",\"localBusinessOpeningHours\":[{\"open\":null,\"close\":null},{\"open\":null,\"close\":null},{\"open\":null,\"close\":null},{\"open\":null,\"close\":null},{\"open\":null,\"close\":null},{\"open\":null,\"close\":null},{\"open\":null,\"close\":null}],\"restaurantServesCuisine\":\"\",\"restaurantMenuUrl\":\"\",\"restaurantReservationsUrl\":\"\"},\"creator\":{\"siteType\":\"Organization\",\"siteSubType\":\"LocalBusiness\",\"siteSpecificType\":\"none\",\"computedType\":\"LocalBusiness\",\"genericName\":\"\",\"genericAlternateName\":\"\",\"genericDescription\":\"\",\"genericUrl\":\"\",\"genericImage\":null,\"genericImageWidth\":\"1340\",\"genericImageHeight\":\"596\",\"genericImageIds\":[\"24\"],\"genericTelephone\":\"\",\"genericEmail\":\"\",\"genericStreetAddress\":\"\",\"genericAddressLocality\":\"\",\"genericAddressRegion\":\"\",\"genericPostalCode\":\"\",\"genericAddressCountry\":\"\",\"genericGeoLatitude\":\"\",\"genericGeoLongitude\":\"\",\"personGender\":\"Male\",\"personBirthPlace\":\"\",\"organizationDuns\":\"\",\"organizationFounder\":\"\",\"organizationFoundingDate\":\"\",\"organizationFoundingLocation\":\"\",\"organizationContactPoints\":\"\",\"corporationTickerSymbol\":\"\",\"localBusinessPriceRange\":\"$\",\"localBusinessOpeningHours\":[{\"open\":null,\"close\":null},{\"open\":null,\"close\":null},{\"open\":null,\"close\":null},{\"open\":null,\"close\":null},{\"open\":null,\"close\":null},{\"open\":null,\"close\":null},{\"open\":null,\"close\":null}],\"restaurantServesCuisine\":\"\",\"restaurantMenuUrl\":\"\",\"restaurantReservationsUrl\":\"\"},\"twitterHandle\":\"\",\"facebookProfileId\":\"\",\"facebookAppId\":\"\",\"googleSiteVerification\":\"\",\"bingSiteVerification\":\"\",\"pinterestSiteVerification\":\"\",\"sameAsLinks\":{\"twitter\":{\"siteName\":\"Twitter\",\"handle\":\"twitter\",\"url\":\"\"},\"facebook\":{\"siteName\":\"Facebook\",\"handle\":\"facebook\",\"url\":\"\"},\"wikipedia\":{\"siteName\":\"Wikipedia\",\"handle\":\"wikipedia\",\"url\":\"\"},\"linkedin\":{\"siteName\":\"LinkedIn\",\"handle\":\"linkedin\",\"url\":\"\"},\"googleplus\":{\"siteName\":\"Google+\",\"handle\":\"googleplus\",\"url\":\"\"},\"youtube\":{\"siteName\":\"YouTube\",\"handle\":\"youtube\",\"url\":\"\"},\"instagram\":{\"siteName\":\"Instagram\",\"handle\":\"instagram\",\"url\":\"\"},\"pinterest\":{\"siteName\":\"Pinterest\",\"handle\":\"pinterest\",\"url\":\"\"},\"github\":{\"siteName\":\"GitHub\",\"handle\":\"github\",\"url\":\"\"},\"vimeo\":{\"siteName\":\"Vimeo\",\"handle\":\"vimeo\",\"url\":\"\"}},\"siteLinksSearchTarget\":\"\",\"siteLinksQueryInput\":\"\",\"referrer\":\"no-referrer-when-downgrade\",\"additionalSitemapUrls\":[],\"additionalSitemapUrlsDateUpdated\":null,\"additionalSitemaps\":[]}" } ``` @@ -885,17 +880,20 @@ Frontend Templates container: ``` ...will return just the Frontend Templates container: + ```json { - "FrontendTemplateContainer": "[{\"humans\":\"/* TEAM */\\n\\nCreator: nystudio107\\nURL: https://nystudio107.com/\\nDescription: We do technology-based consulting, branding, design, and development. Making the web better one site at a time, with a focus on performance, usability & SEO\\n\\n/* THANKS */\\n\\nCraft CMS - https://craftcms.com\\nPixel & Tonic - https://pixelandtonic.com\\n\\n/* SITE */\\n\\nStandards: HTML5, CSS3\\nComponents: Craft CMS 3, Yii2, PHP, JavaScript, SEOmatic\\n\"},{\"robots\":\"# robots.txt for http://localhost:8000/\\n\\nsitemap: http://localhost:8000/sitemaps-1-sitemap.xml\\nsitemap: http://localhost:8000/es/sitemaps-1-sitemap.xml\\n\\n# local - disallow all\\n\\nUser-agent: *\\nDisallow: /\\n\\n\"},{\"ads\":\"# ads.txt file for http://localhost:8000/\\n# More info: https://support.google.com/admanager/answer/7441288?hl=en\\nhttp://localhost:8000/,123,DIRECT\\n\"}]" + "FrontendTemplateContainer": "[{\"humans\":\"/* TEAM */\\n\\nCreator: nystudio107\\nURL: https://nystudio107.com/\\nDescription: We do technology-based consulting, branding, design, and development. Making the web better one site at a time, with a focus on performance, usability & SEO\\n\\n/* THANKS */\\n\\nCraft CMS - https://craftcms.com\\nPixel & Tonic - https://pixelandtonic.com\\n\\n/* SITE */\\n\\nStandards: HTML5, CSS3\\nComponents: Craft CMS 3, Yii2, PHP, JavaScript, SEOmatic\\n\"},{\"robots\":\"# robots.txt for http://localhost:8000/\\n\\nsitemap: http://localhost:8000/sitemaps-1-sitemap.xml\\nsitemap: http://localhost:8000/es/sitemaps-1-sitemap.xml\\n\\n# local - disallow all\\n\\nUser-agent: *\\nDisallow: /\\n\\n\"},{\"ads\":\"# ads.txt file for http://localhost:8000/\\n# More info: https://support.google.com/admanager/answer/7441288?hl=en\\nhttp://localhost:8000/,123,DIRECT\\n\"}]" } ``` All of the individual container controller API endpoints also accept the `&asArray=true` parameter if you’d like the data in array form. - + ### Schema.org API Endpoints -**N.B.:** Anonymous access to the Schema.org JSON-LD endpoints are disabled by default; you’ll need to enable them in SEOmatic → Plugin Settings → Endpoints +::: tip +Anonymous access to the Schema.org JSON-LD endpoints are disabled by default; enable them in **SEOmatic** → **Plugin Settings** → **Endpoints**. +::: To get a key-value array of a given [Schema.org](http://schema.org/docs/full.html) type: @@ -920,7 +918,8 @@ You can narrow this down to a specific sub-type list by passing in a `path` of s /actions/seomatic/json-ld/get-type-array?path=CreativeWork.Article ``` ...this would output all of the sub-types of `Article`: -``` + +```json { "AdvertiserContentArticle": "AdvertiserContentArticle", "NewsArticle": { @@ -946,5 +945,3 @@ You can narrow this down to a specific sub-type list by passing in a `path` of s } } ``` - -Brought to you by [nystudio107](https://nystudio107.com/) diff --git a/docs/docs/configuring.md b/docs/docs/configuring.md deleted file mode 100644 index 3cb3dc05c..000000000 --- a/docs/docs/configuring.md +++ /dev/null @@ -1,622 +0,0 @@ ---- -title: Configuring SEOmatic -description: Configuring SEOmatic documentation for the SEOmatic plugin. The SEOmatic plugin facilitates modern SEO best practices & implementation for Craft CMS 3. ---- -# Configuring SEOmatic - -As soon as you install SEOmatic, it automatically will render metadata on your web pages, and create sitemaps for all of your Sections, Category Groups, and Commerce Product Types that have public URLs. You don’t need to add any template code for this to happen. - -![Screenshot](./resources/screenshots/seomatic-multi-site.png) - -All of SEOmatic’s settings are multi-site aware, allowing you to have different settings for each site/language combination. - -For SEOmatic to be truly useful, you need to configure it so that it knows where to _pull_ SEO content from. - -**N.B.** Please ensure that you set up your [Multi-Environment Config Settings](#multi-environment-config-settings) if you will be using SEOmatic across multiple environments. - -## Dashboard - -![Screenshot](./resources/screenshots/seomatic-dashboard.png) - -The Dashboard gives you an overview of how fully set up your SEO setting in SEOmatic are, for Global SEO, Content SEO, and Site Settings. - -Click on any of the graphs to jump to the section in question to fill things out in more detail. - -## Global SEO - -**Global SEO** is where you set all of the default site-wide settings. The **Copy Settings From:** dropdown allows you to easily copy settings from one site to another. - -SEOmatic allows you to have different Global SEO settings on a per-site basis. - -### General - -![Screenshot](./resources/screenshots/seomatic-global-general.png) - -Best practices for modern SEO are for the meta information to _reflect your content_, so you should set up the fields that SEOmatic _pulls_ the **SEO Title**, **SEO Description**, and **SEO Image** from. - -### Twitter - -![Screenshot](./resources/screenshots/seomatic-global-twitter.png) - -By default, the Twitter and Facebook settings will mirror what you set in the **General** section, but you can customize them to your heart’s content. - -### Facebook - -![Screenshot](./resources/screenshots/seomatic-global-facebook.png) - -By default, the Twitter and Facebook settings will mirror what you set in the **General** section, but you can customize them to your heart’s content. - -### Robots - -![Screenshot](./resources/screenshots/seomatic-global-robots.png) - -A `robots.txt` file is a file at the root of your site that indicates those parts of your site you don’t want accessed by search engine crawlers. The file uses the [Robots Exclusion Standard](http://www.robotstxt.org/robotstxt.html), which is a protocol with a small set of commands that can be used to indicate access to your site by section and by specific kinds of web crawlers (such as mobile crawlers vs desktop crawlers). - -You shouldn’t need to edit the default `robots.txt` Template, but you can if you like. - -SEOmatic automatically handles requests for `/robots.txt`. For this to work, make sure that you do not have an actual `robots.txt` file in your `web/` folder (because that will take precedence). - -If you are running Nginx, make sure that you don’t have a line like: - - location = /robots.txt { access_log off; log_not_found off; } - -...in your config file. A directive like this will prevent SEOmatic from being able to service the request for `/robots.txt`. If you do have a line like this in your config file, just comment it out, and restart Nginx with `sudo nginx -s reload`. - -The **View robots.txt** button lets you view your rendered `robots.txt`. - -### Humans - -![Screenshot](./resources/screenshots/seomatic-global-humans.png) - -[Humans.txt](http://humanstxt.org/) is an initiative for knowing the people behind a site. It’s a text file that contains information about the different people who have contributed to building the site. By adding a text file, you can prove your authorship (not your property) in an external, fast, easy and accessible way. - -Feel free to edit the default `humans.txt` Template to your heart’s content. - -The **View humans.txt** button lets you view your rendered `humans.txt`. - -### Ads - -![Screenshot](./resources/screenshots/seomatic-global-ads.png) - -The [ads.txt](https://iabtechlab.com/ads-txt/) project is simple: Increase transparency in the programmatic advertising ecosystem. Ads.txt stands for Authorized Digital Sellers and is a simple, flexible and secure method that publishers and distributors can use to publicly declare the companies they authorize to sell their digital inventory. - -Feel free to edit the default `ads.txt` Template to your heart’s content. - -The **View ads.txt** button lets you view your rendered `ads.txt`. - -### Security - -![Screenshot](./resources/screenshots/seomatic-global-security.png) - -The [security.txt](https://securitytxt.org/) defines a standard to help organizations define the process for security researchers to disclose security vulnerabilities securely - -Feel free to edit the default `security.txt` Template to your heart’s content. - -The **View security.txt** button lets you view your rendered `security.txt`. - -### Global SEO Control Panel Fields - -The fields in the Control Panel Global SEO settings are parsed as Twig object templates, so in addition to plain old text, you can also put single and double bracket Twig expressions. - -SEOmatic fields are also parsed for aliases, and in Craft 3.1, for [environment variables](https://docs.craftcms.com/v3/config/environments.html#control-panel-settings) as well. - -This is entirely optional; in typical usage the controls you have in the Control Panel for pulling from other fields will be all you need. But the ability is there if you need it. - -For example, the following will output the contents of the **companyInfo** field from the **siteInfo** Global: - -```twig -{{ siteInfo.companyInfo }} -``` - -You can even do complex expressions, such as the following which outputs the first field that isn’t empty, or a default text: - -```twig -{{ siteInfo.companyInfo ??? siteInfo.companySummary ??? "Some default text" }} -``` - -The above uses the `???` empty coalesce operator that comes with SEOmatic; check out [SEOmatic’s ??? Empty Coalesce operator](#seomatics--empty-coalesce-operator) for details. - -You can also access SEOmatic global variables (discussed below): - -```twig -{seomatic.meta.seoTitle} -``` -Normal Twig double bracket syntax is supported too: - -```twig -{{ seomatic.meta.seoTitle }} -``` -The single bracket syntax is just a shortcut, and provided for backwards compatibility with previous versions of SEOmatic. - -## Content SEO - -![Screenshot](./resources/screenshots/seomatic-content.png) - -**Content SEO** is where you can configure each Section, Category Group and Commerce Product Type that has public URLs. You’ll see a list of all of your Sections, Category Groups, and Commerce Product Types that have public URLs, with status indicators letting you know what has been configured for each. - -SEOmatic allows you to have different Content SEO settings on a per-site basis, and also on a per-[Entry Type](https://docs.craftcms.com/v3/sections-and-entries.html#entry-types) basis. - -Click on a Section, Category Group, or Commerce Product Type name to edit its settings. - -### General - -![Screenshot](./resources/screenshots/seomatic-content-general.png) - -Best practices for modern SEO are for the meta information to _reflect your content_, so you should set up the fields that SEOmatic _pulls_ the **SEO Title**, **SEO Description**, and **SEO Image** from. - -### Twitter - -![Screenshot](./resources/screenshots/seomatic-content-twitter.png) - -By default, the Twitter and Facebook settings will mirror what you set in the **General** section, but you can customize them to your heart’s content. - -### Facebook - -![Screenshot](./resources/screenshots/seomatic-content-facebook.png) - -By default, the Twitter and Facebook settings will mirror what you set in the **General** section, but you can customize them to your heart’s content. - -### Sitemap - -![Screenshot](./resources/screenshots/seomatic-content-sitemap.png) - -SEOmatic automatically creates a sitemap index for each of your Site Groups. This sitemap index points to individual sitemaps for each of your Sections, Category Groups, and Commerce Product Types. - -Instead of one massive sitemap that must be updated any time anything changes, only the sitemap for the Section, Category Group, or Commerce Product Type will be updated when something changes in it. - -SEOmatic can automatically include files such as `.pdf`, `.xls`, `.doc` and other indexable file types in Asset fields or Asset fields in matrix or Neo blocks. - -In addition, SEOmatic can automatically create [Image sitemaps](https://support.google.com/webmasters/answer/178636?hl=en) and [Video sitemaps](https://developers.google.com/webmasters/videosearch/sitemaps) from images & videos in Asset fields or Asset fields in matrix or Neo blocks - -Sitemap Indexes are automatically submitted to search engines whenever a new Section, Category Group, or Commerce Product Type is added. - -Section Sitemaps are automatically submitted to search engines whenever a new Element in that Section, Category Group, or Commerce Product Type is added. - -#### Sitemap Generation - -Because XML sitemaps can be quite time-intensive to generate as the number of entries scales up, SEOmatic creates your sitemaps via a Queue job, and caches the result. The cache is automatically broken whenever something in that sitemap is changed, and a new Queue job is created to regenerate it. - -If `runQueueAutomatically` is set to `false` in [General Config Settings](https://docs.craftcms.com/v3/config/config-settings.html#runqueueautomatically) the Queue job to create the sitemap will not be run during the http request for the sitemap. You’ll need to run it manually via whatever means you use to run the Queue. - -Normally SEOmatic will regenerate the sitemap for a Section, Category Group, or Product any time you save an element. However, if you are importing a large number of elements, or prefer to regenerate the sitemap manually you can set disable the **Regenerate Sitemaps Automatically** option in SEOmatic’s Plugin Settings. - -![Screenshot](./resources/screenshots/seomatic-sitemap-console-command.png) - -You can then regenerate the sitemap via CLI. This will regenerate all sitemaps: - -```bash -./craft seomatic/sitemap/generate -``` - -You can also limit it to a specific Section, Category Group, or Product handle: - -```bash -./craft seomatic/sitemap/generate --handle=blog -``` - -...or you can regenerate all sitemaps for a specific `siteId`: - -```bash -./craft seomatic/sitemap/generate --siteId=1 -``` - -...or both: - -```bash -./craft seomatic/sitemap/generate --handle=blog --siteId=1 -``` -**N.B.:** If you do disable **Regenerate Sitemaps Automatically** sitemaps will _not_ be updated unless you do so manually via the CLI, or clear SEOmatic’s sitemap caches via Utilities->Clear Caches. - -#### Additional Sitemaps - -If you have custom sitemaps that are not in the CMS, you can manually add them to their own Sitemap Index via **Site Settings** → **Sitemap**. - -You can also add to it via a plugin: - -```php -use nystudio107\seomatic\events\RegisterSitemapsEvent; -use nystudio107\seomatic\models\SitemapIndexTemplate; -use yii\base\Event; -Event::on(SitemapIndexTemplate::class, SitemapIndexTemplate::EVENT_REGISTER_SITEMAPS, function(RegisterSitemapsEvent $e) { - $e->sitemaps[] = [ - 'loc' => $url, - 'lastmod' => $lastMod, - ]; -}); -``` - -#### Additional Sitemap URLs - -If you have custom URLs that are not in the CMS, you can manually add them to their own Sitemap Index via **Site Settings** → **Sitemap**. - -You can also add to it via a plugin: - -```php -use nystudio107\seomatic\events\RegisterSitemapUrlsEvent; -use nystudio107\seomatic\models\SitemapCustomTemplate; -use yii\base\Event; -Event::on(SitemapCustomTemplate::class, SitemapCustomTemplate::EVENT_REGISTER_SITEMAP_URLS, function(RegisterSitemapUrlsEvent $e) { - $e->sitemaps[] = [ - 'loc' => $url, - 'changefreq' => $changeFreq, - 'priority' => $priority, - 'lastmod' => $lastMod, - ]; - }); -``` - -### Content SEO Control Panel Fields - -The fields in the Control Panel Content SEO settings are parsed as Twig object templates, so in addition to plain old text, you can also put single and double bracket Twig expressions. - -SEOmatic fields are also parsed for aliases, and in Craft 3.1, for [environment variables](https://docs.craftcms.com/v3/config/environments.html#control-panel-settings) as well. - -This is entirely optional; in typical usage the controls you have in the Control Panel for pulling from other fields will be all you need. But the ability is there if you need it. - -For example, the following will output the contents of the **description** field from the current **Entry**: - -```twig -{entry.description} -``` - -Normal Twig double bracket syntax is supported too: - -```twig -{{ entry.description }} -``` -The single bracket syntax is just a shortcut, and provided for backwards compatibility with previous versions of SEOmatic. - -The same applies to any SEOmatic global variables (discussed below): - -```twig -{seomatic.meta.seoTitle} -``` -Is the same as: - -```twig -{{ seomatic.meta.seoTitle }} -``` - -You can even do complex expressions, such as the following which outputs the first field that isn’t empty, or a default text: - -```twig -{{ entry.description ??? entry.summary ??? "Some default text" }} -``` - -The above uses the `???` empty coalesce operator that comes with SEOmatic; check out [SEOmatic’s ??? Empty Coalesce operator](#seomatics--empty-coalesce-operator) for details. - -## Site Settings - -### Identity - -![Screenshot](./resources/screenshots/seomatic-site-identity.png) - -These Site Identity settings are used to globally define the identity and ownership of the site. - -They are used in combination with the SEO Template Meta settings to generate [JSON-LD](https://developers.google.com/schemas/formats/json-ld?hl=en) microdata. - -The Site Owner type determines the JSON-LD schema that will be used to identity the site to search engines. - -Leave any fields blank that aren’t applicable or which you do not want as part of the SEO schema. - -### Creator - -![Screenshot](./resources/screenshots/seomatic-site-creator.png) - -These Site Creator settings are used to globally define the _creator_ (such as the agency or freelancer) of the site. - -They are used in combination with the SEO Template Meta settings to generate [JSON-LD](https://developers.google.com/schemas/formats/json-ld?hl=en) microdata as well as the `humans.txt` file. - -The Site Creator type determines the JSON-LD schema that will be used to identity the site to search engines. - -Leave any fields blank that aren’t applicable or which you do not want as part of the SEO schema. - -### Social Media - -![Screenshot](./resources/screenshots/seomatic-site-social.png) - -The social media settings connect your site to its other points of pressence on the internet. They also facilitate attaching your branding to social media posts via Twitter Cards and Facebook OpenGraph. - -### Sitemap - -![Screenshot](./resources/screenshots/seomatic-site-sitemap.png) - -SEOmatic will automatically create a sitemap for each of your sections, but if you have additional sitemaps or individual URLs that are outside of the CMS that you want to include, you can add them here. - -### Miscellaneous - -![Screenshot](./resources/screenshots/seomatic-site-misc.png) - -Miscellaneous site-wide SEO settings. - -## Tracking Scripts - -None of the Tracking Scripts are included on the page unless the SEOmatic environment is set to `live` production. If `devMode` is enabled, the SEOmatic environment is automatically set to `local` development. - -### Google Analytics - -![Screenshot](./resources/screenshots/seomatic-tracking-ga.png) - -Google Analytics gives you the digital analytics tools you need to analyze data from all touchpoints in one place, for a deeper understanding of the customer experience. You can then share the insights that matter with your whole organization. [Learn More](https://www.google.com/analytics/analytics/#?modal_active=none) - -To include the Google Analytics script despite `devMode` being enabled, you can do: - -```twig -{% do seomatic.script.get('googleAnalytics').include(true) %} -``` - -### Google `gtag.js` - -![Screenshot](./resources/screenshots/seomatic-tracking-gtag.png) - -The global site tag (gtag.js) is a JavaScript tagging framework and API that allows you to send event data to AdWords, DoubleClick, and Google Analytics. Instead of having to manage multiple tags for different products, you can use gtag.js and more easily benefit from the latest tracking features and integrations as they become available. [Learn More](https://developers.google.com/gtagjs/) - -To include the gtag script despite `devMode` being enabled, you can do: - -```twig -{% do seomatic.script.get('gtag').include(true) %} -``` - -### Google Tag Manager - -![Screenshot](./resources/screenshots/seomatic-tracking-gtm.png) - -Google Tag Manager is a tag management system that allows you to quickly and easily update tags and code snippets on your site. Once the Tag Manager snippet has been added to your site or mobile app, you can configure tags via a web-based user interface without having to alter and deploy additional code. [Learn More](https://support.google.com/tagmanager/answer/6102821?hl=en) - -You can set the `dataLayer` passed in to Google Tag Manager via Twig: - -```twig -{% do seomatic.script.get('googleTagManager').dataLayer({ - 'woof': 'bark' -}) %} -``` - -To include the Google Tag Manager script despite `devMode` being enabled, you can do: - -```twig -{% do seomatic.script.get('googleTagManager').include(true) %} -``` - -### Facebook Pixel - -![Screenshot](./resources/screenshots/seomatic-tracking-fb.png) - -The Facebook pixel is an analytics tool that helps you measure the effectiveness of your advertising. You can use the Facebook pixel to understand the actions people are taking on your site and reach audiences you care about. [Learn More](https://www.facebook.com/business/help/651294705016616) - -To include the Facebook Pixel script despite `devMode` being enabled, you can do: - -```twig -{% do seomatic.script.get('facebookPixel').include(true) %} -``` - -### LinkedIn Insight - -![Screenshot](./resources/screenshots/seomatic-tracking-li.png) - -The LinkedIn Insight Tag is a lightweight JavaScript tag that powers conversion tracking, retargeting, and web analytics for LinkedIn ad campaigns. - -To include the LinkedIn Insight script despite `devMode` being enabled, you can do: - -```twig -{% do seomatic.script.get('linkedInInsight').include(true) %} -``` - -### HubSpot - -![Screenshot](./resources/screenshots/seomatic-tracking-hs.png) - -If you’re not hosting your entire site on HubSpot, or have pages on your site that are not hosted on HubSpot, you’ll need to install the HubSpot tracking code on your non-HubSpot pages to capture those analytics. - -To include the HubSpot script despite `devMode` being enabled, you can do: - -```twig -{% do seomatic.script.get('hubSpot').include(true) %} -``` - -## Plugin Settings - -![Screenshot](./resources/screenshots/seomatic-plugin-settings.png) - -The Plugin Settings lets you control various SEOmatic settings globally (across all sites/languages). - -### General Plugin Settings - -* **Plugin name** - This is the name that will be used for the plugin everywhere it is referenced in the Control Panel GUI -* **Automatic Render Enabled** - Controls whether SEOmatic will automatically render metadata on your pages. If you turn this off, you will need to manually render the metadata via `seomatic.tag.render()`, `seomatic.link.render()`, etc. You can selectively disable rendering via Twig with `{% do seomatic.config.renderEnabled(false)` %} -* **Sitemaps Enabled** - Controls whether SEOmatic will automatically render frontend sitemaps for your site. -* **Regenerate Sitemaps Automatically** - Controls whether sitemaps will automatically be regenerated when entries are saved. -* **Submit Sitemap Changes** - Should sitemaps be submitted to search engines automatically whenever there are changes? -* **Include Homepage in Breadcrumbs** - Should the homepage be included in the generated Breadcrumbs JSON-LD? -* **Manually Set SEOmatic Environment** - If off, SEOmatic will automatically attempt to determine the current environment. Turn this on to manually set the environment. -* **Environment** - The server environment, either `live`, `staging`, or `local`. If `devMode` is on, SEOmatic will override this setting to local Development. This setting controls whether certain things render; for instance only in the `live` production environment will Google Analytics and other tracking tags send analytics data. SEOmatic also automatically sets the `robots` tag to `none` for everything but the `live` production environment. - -### Appearance Plugin Settings - -* **Display Sidebar SEO Preview** - Controls whether to display the Google, Twitter, and Facebook social media previews in the sidebar on entry. Category, and product pages. -* **Add Social Media Preview Target** - Controls whether to add the Google, Twitter, Facebook, etc. social media previews as a Preview Target. -* **SEO Preview Sites** - The social media platforms that should be displayed in the SEO Preview - -### Title Plugin Settings - -* **devMode `` prefix** - If devMode is on, prefix the `<title>` with this string -* **Control Panel `<title>` prefix** - Prefix the Control Panel `<title>` with this string -* **devMode Control Panel `<title>` prefix** - If devMode is on, prefix the Control Panel `<title>` with this string -* **Separator Character** - The separator character to use for the `<title>` tag -* **Max SEO Title Length** - The max number of characters in the `<title>` tag; anything beyond this will be truncated on word boundaries -* **Max SEO Description Length** - The max number of characters in the `meta description` tag -* **Truncate Title Tags** - Should Title tags be truncated at the max length, on word boundaries? -* **Truncate Description Tags** - Should Description tags be truncated at the max length, on word boundaries? - -### Tags Plugin Settings - -* **Add `hreflang` Tags** - Controls whether SEOmatic will automatically add `hreflang` and `og:locale:alternate` tags. -* **Include `x-default` hreflang Tag** - Controls whether SEOmatic will automatically include an x-default hreflang tag -* **Include Paginated `hreflang` Tags** - Controls whether SEOmatic will automatically include hreflang tags on paginated pages -* **Generator Enabled** - Controls whether SEOmatic will include the meta `generator` tag and `X-Powered-By` header -* **HTTP Headers Enabled** - Controls whether SEOmatic will automatically add `X-Robots-Tag`, `canonical`, & `Referrer-Policy` to the http response headers. -* **Nonces for `<script>` tags** - Whether SEOmatic should automatically add script-src [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src) (CSP) nonces to `<script>` tags (including JSON-LD) -* **Fixed `script-src` Content Security Policies** - Fixed [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src) (CSP) script-src policies that should be added before the Nonces - -### Endpoints Plugin Settings - -* **Meta Container Endpoint Access** - Whether anonymous access to the Meta Container endpoint should be allowed -* **JSON-LD Endpoint Access** - Whether anonymous access to the JSON-LD endpoint should be allowed - -### Advanced Plugin Settings - -* **Site Groups define logically separate sites** - If you are using Site Groups to logically separate 'sister sites’, turn this on. -* **Lowercase Canonical URL** - Should the Canonical URL be automatically lower-cased? -* **Site URL Override** - SEOmatic uses the Craft siteUrl to generate the external URLs. If you are using it in a non-standard environment, such as a headless GraphQL or ElementAPI server, you can override what it uses for the `siteUrl`. -* **Meta Cache Duration** - The duration of the SEOmatic meta cache. The default Unlimited setting is typically desired, as SEOmatic will break the cache as needed. If devMode is on, caches last 30 seconds. - -## Multi-Environment Config Settings - -SEOmatic does different things depending on the SEOmatic environment it is running in. This is a separate setting from your Craft environment, because you can name those anything you like. - -SEOmatic needs some way to map what you call your local, staging, and production environments to a normalized representation. - -In `local` dev and `staging` environments, the following things change: - -1. `<meta name="robots">` tags are rendered with `none` to prevent Google from indexing the pages -2. The `robots.txt` page is rendered to disallow all indexing -3. No scripts are loaded on the page, to prevent errant data being sent to endpoints -4. Because the `<meta name="robots">` tag is set to `none`, the `<link rel="canonical">` is not rendered - -You can override all of these things as you see fit, but they are automatically changed in this manner to help protect you from having pages indexed or sending data from environments where you should not. - -If you’re using a multi-environment config, you can map your environment settings using SEOmatic’s `config.php` something like this: - -```php -<?php -return [ - // The public-facing name of the plugin - 'pluginName' => 'SEOmatic', - - // Should SEOmatic render metadata? - 'renderEnabled' => true, - - // Should SEOmatic render frontend sitemaps? - 'sitemapsEnabled' => true, - - // Should sitemaps be regenerated automatically? - 'regenerateSitemapsAutomatically' => true, - - // Should sitemaps be submitted to search engines automatically whenever there are changes? - 'submitSitemaps' => true, - - // Should SEOmatic add to the http response headers? - 'headersEnabled' => true, - - // The server environment, either `live`, `staging`, or `local` - 'environment' => 'live', - - // Should SEOmatic display the SEO Preview sidebar? - 'displayPreviewSidebar' => true, - - // Should SEOmatic add a Social Media Preview Target? - 'socialMediaPreviewTarget' => true, - - // The social media platforms that should be displayed in the SEO Preview sidebar - 'sidebarDisplayPreviewTypes' => [ - 'google', - 'twitter', - 'facebook' - ], - - // Should SEOmatic display the SEO Analysis sidebar? - 'displayAnalysisSidebar' => true, - - // If `devMode` is on, prefix the <title> with this string - 'devModeTitlePrefix' => '🚧 ', - - // Prefix the Control Panel <title> with this string - 'cpTitlePrefix' => '⚙ ', - - // If `devMode` is on, prefix the Control Panel <title> with this string - 'devModeCpTitlePrefix' => '🚧⚙ ', - - // The separator character to use for the `<title>` tag - 'separatorChar' => '|', - - // The max number of characters in the `<title>` tag - 'maxTitleLength' => 70, - - // The max number of characters in the `<meta name="description">` tag - 'maxDescriptionLength' => 155, - - // Site Groups define logically separate sites - 'siteGroupsSeparate' => true, - - // Whether to dynamically include the hreflang tags - 'addHrefLang' => true, - - // Whether to dynamically include the `x-default` hreflang tags - 'addXDefaultHrefLang' => true, - - // Whether to dynamically include hreflang tags on paginated pages - 'addPaginatedHreflang' => true, - - // Should the Canonical URL be automatically lower-cased? - 'lowercaseCanonicalUrl' => true, - - // Should the meta generator tag and X-Powered-By header be included? - 'generatorEnabled' => true, - - // SEOmatic uses the Craft `siteUrl` to generate the external URLs. If you - // are using it in a non-standard environment, such as a headless GraphQL or - // ElementAPI server, you can override what it uses for the `siteUrl` below. - 'siteUrlOverride' => '', - - // The duration of the SEOmatic meta cache in seconds. Null means always cached until explicitly broken - // If devMode is on, caches last 30 seconds. - 'metaCacheDuration' => null, - - // Determines whether the meta container endpoint should be enabled for anonymous frontend access - 'enableMetaContainerEndpoint' => false, - - // Determines whether the JSON-LD endpoint should be enabled for anonymous frontend access - 'enableJsonLdEndpoint' => false, - - // SeoElementInterface[] The default SeoElement type classes - 'defaultSeoElementTypes' => [ - ], -]; -``` -Just copy the `config.php` to your Craft `config/` directory as `seomatic.php` and you can configure the settings in a multi-environment friendly way. See the [Craft Environments](https://docs.craftcms.com/v3/config/environments.html#config-files) page for details, and **N.B.:** - -> The `'*'` key is required here so Craft knows to treat it as a multi-environment key, but the other keys are up to you - -This is how you can make your multi-environment nomenclature to SEOmatic’s. This works exactly like Craft’s [multi-environment config](https://docs.craftcms.com/v3/configuration.html#application-config) files such as `general.php` and `db.php`. See SEOmatic’s `config.php` for details. - -## Access Permissions - -![Screenshot](./resources/screenshots/seomatic-permissions.png) - -SEOmatic allows you to restrict access to various parts of the plugin based on User Group Permissions: - -* Dashboard -* Edit Global Meta - * General - * Twitter - * Facebook - * Robots - * Humans - * Ads -* Edit Content SEO - * General - * Twitter - * Facebook - * Sitemap -* Edit Site Settings - * Identity - * Creator - * Social Media - * Miscellaneous -* Edit Tracking Scripts - * Google Analytics - * Google gtag.js - * Google Tag Manager - * Facebook Pixel -* Edit Plugin Settings - -Brought to you by [nystudio107](https://nystudio107.com/) diff --git a/docs/docs/configuring/access-permissions.md b/docs/docs/configuring/access-permissions.md new file mode 100644 index 000000000..7155860c4 --- /dev/null +++ b/docs/docs/configuring/access-permissions.md @@ -0,0 +1,30 @@ +# Access Permissions + +SEOmatic allows you to restrict access to various parts of the plugin based on User Group Permissions: + +![Screenshot of the SEOmatic section of Craft’s User Group permissions, which includes the items detailed below](../resources/screenshots/seomatic-permissions.png) + +* Dashboard +* Edit Global Meta + * General + * Twitter + * Facebook + * Robots + * Humans + * Ads +* Edit Content SEO + * General + * Twitter + * Facebook + * Sitemap +* Edit Site Settings + * Identity + * Creator + * Social Media + * Miscellaneous +* Edit Tracking Scripts + * Google Analytics + * Google gtag.js + * Google Tag Manager + * Facebook Pixel +* Edit Plugin Settings diff --git a/docs/docs/configuring/content-seo.md b/docs/docs/configuring/content-seo.md new file mode 100644 index 000000000..91f6c5b94 --- /dev/null +++ b/docs/docs/configuring/content-seo.md @@ -0,0 +1,135 @@ +# Content SEO + +Content SEO is where you can configure each Section, Category Group and Commerce Product Type that has public URLs. + +A list of these content types includes status indicators identifying what’s been configured for each one: + +![Screenshot of SEOmatic’s Content SEO settings landing, which lists each of the site’s content sections in a table by name, with additional columns for entry count, section type, title indicator, description indicator, image indicator, sitemap indicator, and robots setting](../resources/screenshots/seomatic-content.png) + +SEOmatic allows you to have different Content SEO settings on both a per-site and per-[Entry Type](https://craftcms.com/docs/3.x/entries.html#entry-types) basis. + +Click the name of any listed item to edit its settings. + +::: tip Settings are unique for each type! +Unlike the previous [Global SEO](./global-seo.md) section, the settings below each correspond with a specific Content SEO section or entry type you’ve chosen. +::: + +## General Settings + +![Screenshot of SEOmatic’s General Content SEO settings for a Blog section, with an SEO preview and fields for Main Entity of Page, SEO Title Source, and Site Name Position Source](../resources/screenshots/seomatic-content-general.png) + +This is where you can set up the fields from which SEOmatic extracts the **SEO Title**, **SEO Description**, and **SEO Image**. These per-section Content SEO settings, when they have values, will override the Global SEO general settings. + +## Twitter Settings + +![Screenshot of SEOmatic’s Twitter Content SEO settings for a Blog section, with an SEO preview and fields for Twitter Card Type and Twitter Creator Source](../resources/screenshots/seomatic-content-twitter.png) + +By default, Twitter settings will mirror what you set in the **General** section, but you can customize them to your heart’s content. + +## Facebook Settings + +![Screenshot of SEOmatic’s Facebook Content SEO settings for a Blog section, with an SEO preview and fields for Facebook Open Graph Type and Facebook Open Graph Title Source](../resources/screenshots/seomatic-content-facebook.png) + +Facebook settings will also mirror what you set in the **General** section, but you can customize them here. + +## Sitemap Settings + +![Screenshot of SEOmatic’s Sitemap Content SEO settings for a Blog section, with an enabled lightswitch, lightswitches for including images and videos, including indexable files, and alternate translation URLs; more fields for Change Frequency and Priority](../resources/screenshots/seomatic-content-sitemap.png) + +SEOmatic automatically creates a sitemap index for each of your Site Groups. This sitemap index points to individual sitemaps for each of your Sections, Category Groups, and Commerce Product Types. + +Instead of one massive sitemap that must be updated any time anything changes, only the sitemap for the Section, Category Group, or Commerce Product Type will be updated when something changes in it. + +SEOmatic can automatically include files such as `.pdf`, `.xls`, `.doc` and other indexable file types in Asset fields or Asset fields in matrix or Neo blocks. + +In addition, SEOmatic can automatically create [Image sitemaps](https://support.google.com/webmasters/answer/178636?hl=en) and [Video sitemaps](https://developers.google.com/webmasters/videosearch/sitemaps) from images & videos in Asset fields or Asset fields in matrix or Neo blocks. + +Sitemap Indexes are automatically submitted to search engines whenever a new Section, Category Group, or Commerce Product Type is added. + +Section Sitemaps are automatically submitted to search engines whenever a new Element in that Section, Category Group, or Commerce Product Type is added. + +### Sitemap Generation + +Because XML sitemaps can be time-intensive to generate with a growing number of entries, SEOmatic creates your sitemaps via a queue job and caches the result. The cache is automatically broken whenever something in that sitemap is changed, and a new queue job is created to regenerate it. + +If `runQueueAutomatically` is set to `false` in [General Config Settings](https://craftcms.com/docs/3.x/config/config-settings.html#runqueueautomatically) the Queue job to create the sitemap will not be run during the http request for the sitemap. You’ll need to run it manually via whatever means you use to run the Queue. + +Normally SEOmatic will regenerate the sitemap for a Section, Category Group, or Product any time you save an element. However, if you are importing a large number of elements, or prefer to regenerate the sitemap manually you can set disable the **Regenerate Sitemaps Automatically** option in SEOmatic’s Plugin Settings. + +![Screenshot of a console running the following command to generate a blog sitemap: `./craft seomatic/sitemap/generate --siteId=1 --handle=blog`](../resources/screenshots/seomatic-sitemap-console-command.png) + +You can then regenerate the sitemap via CLI. This will regenerate all sitemaps: + +```bash +./craft seomatic/sitemap/generate +``` + +You can also limit it to a specific Section, Category Group, or Product handle: + +```bash +./craft seomatic/sitemap/generate --handle=blog +``` + +...or you can regenerate all sitemaps for a specific `siteId`: + +```bash +./craft seomatic/sitemap/generate --siteId=1 +``` + +...or both: + +```bash +./craft seomatic/sitemap/generate --handle=blog --siteId=1 +``` + +::: tip Manually Updating Sitemaps +If you disable **Regenerate Sitemaps Automatically**, sitemaps can _only_ be updated via CLI command, or by clearing SEOmatic’s sitemap caches via **Utilities** → **Clear Caches**. +::: + +### Additional Sitemaps + +If you have custom sitemaps that are not in the CMS, you can manually add them to their own Sitemap Index via **Site Settings** → **Sitemap**. + +You can also add to it via plugin: + +```php +use nystudio107\seomatic\events\RegisterSitemapsEvent; +use nystudio107\seomatic\models\SitemapIndexTemplate; +use yii\base\Event; + +Event::on( + SitemapIndexTemplate::class, + SitemapIndexTemplate::EVENT_REGISTER_SITEMAPS, + function(RegisterSitemapsEvent $event) { + $event->sitemaps[] = [ + 'loc' => $url, + 'lastmod' => $lastMod, + ]; + } +); +``` + +### Additional Sitemap URLs + +If you have custom URLs that are not in the CMS, you can manually add them to their own Sitemap Index via **Site Settings** → **Sitemap**. + +You can also add to it via a plugin: + +```php +use nystudio107\seomatic\events\RegisterSitemapUrlsEvent; +use nystudio107\seomatic\models\SitemapCustomTemplate; +use yii\base\Event; + +Event::on( + SitemapCustomTemplate::class, + SitemapCustomTemplate::EVENT_REGISTER_SITEMAP_URLS, + function(RegisterSitemapUrlsEvent $event) { + $event->sitemaps[] = [ + 'loc' => $url, + 'changefreq' => $changeFreq, + 'priority' => $priority, + 'lastmod' => $lastMod, + ]; + } +); +``` diff --git a/docs/docs/configuring/dashboard.md b/docs/docs/configuring/dashboard.md new file mode 100644 index 000000000..c515ff23a --- /dev/null +++ b/docs/docs/configuring/dashboard.md @@ -0,0 +1,7 @@ +# Dashboard + +The Dashboard gives you a progress overview for how fully set up your settings are for SEOmatic’s Global SEO, Content SEO, and Site Settings. + +![Screenshot the SEOmatic Dashboard, with ring completion charts for Global SEO, Content SEO, and Site Settings](../resources/screenshots/seomatic-dashboard.png) + +Click on any of the graphs to explore the relevant settings in more detail. diff --git a/docs/docs/configuring/global-seo.md b/docs/docs/configuring/global-seo.md new file mode 100644 index 000000000..f92b0951b --- /dev/null +++ b/docs/docs/configuring/global-seo.md @@ -0,0 +1,70 @@ +# Global SEO + +Global SEO is where you set default site-wide settings, on a per-site basis. + +The **Copy Settings From:** dropdown lets you copy settings from one site to another. + +## General Settings + +![Screenshot of SEOmatic’s General settings in the Global SEO section, with an SEO Preview and fields for Main Entity of Page, SEO Title Source, and Site Name Position](../resources/screenshots/seomatic-global-general.png) + +Because meta information should reflect your content, you’ll want designate the fields that SEOmatic extracts the **SEO Title**, **SEO Description**, and **SEO Image** from. + +## Twitter Settings + +![Screenshot of SEOmatic’s Twitter settings in the Global SEO section, with an SEO Preview and fields for Twitter Card Type, Twitter Creator Source, and Twitter Title Source](../resources/screenshots/seomatic-global-twitter.png) + +By default, the Twitter settings will mirror what you set in the [General section](#general-settings), but you can customize them to your heart’s content. + +## Facebook Settings + +![Screenshot of SEOmatic’s Facebook settings in the Global SEO section, with an SEO Preview and fields for Facebook Open Graph Type and Facebook Open Graph Title Source](../resources/screenshots/seomatic-global-facebook.png) + +Like the Twitter settings, Facebook settings will also mirror whatever you’ve set in the [General section](#general-settings), but you can further customize them here. + +## Robots Settings + +![Screenshot of SEOmatic’s Robots settings in the Global SEO section, with a “View robots.txt” button, a lightswitch for whether it should be rendered, and a code field for editing its template](../resources/screenshots/seomatic-global-robots.png) + +A `robots.txt` file at the root of your site tells search engine crawlers what they’re allowed to access. + +The file uses the [Robots Exclusion Standard](http://www.robotstxt.org/robotstxt.html), which is a protocol with a small set of commands that can be used to indicate access to your site by section and by specific kinds of web crawlers (such as mobile crawlers vs desktop crawlers). + +You shouldn’t need to edit the default **Robots.txt Template** setting, but you can if you like. Click **View robots.txt** to see how `robots.txt` is rendered by the template. + +### Troubleshooting + +Two things could prevent SEOmatic from automatically handling `/robots.txt` requests: + +1. An actual `robots.txt` file in your `web/` folder, which will take precedence. +2. An nginx directive that prevents Craft CMS from handling the `robots.txt` URL. + +If you’re running nginx, make sure that you _don’t_ have a line like this in your config file: + +```nginx +location = /robots.txt { access_log off; log_not_found off; } +``` + +A directive like this will prevent SEOmatic from being able to service the request for `/robots.txt`. If you do have a line like this in your config file, comment it out and restart nginx by running `sudo nginx -s reload`. + +### Humans Settings + +![Screenshot of SEOmatic’s Humans settings in the Global SEO section, with a “View humans.txt” button, a lightswitch for whether it should be rendered, and a code field for editing its template](../resources/screenshots/seomatic-global-humans.png) + +[Humans.txt](http://humanstxt.org/) is an initiative for including a text file identifying a site’s creators. By adding a text file, you can prove your authorship (not your property) in an external, fast, easy and accessible way. + +Edit the default **Humans.txt Template** to your heart’s content. Click **View humans.txt** to view the rendered `humans.txt` output. + +## Ads Settings + +![Screenshot of SEOmatic’s Ads settings in the Global SEO section, with a “View ads.txt” button, a lightswitch for whether it should be rendered, and a code field for editing its template](../resources/screenshots/seomatic-global-ads.png) + +The [ads.txt](https://iabtechlab.com/ads-txt/) project aims to increase transparency in the programmatic advertising ecosystem. “Ads.txt” stands for Authorized Digital Sellers and is a simple, flexible and secure method that publishers and distributors can use to publicly declare the companies they authorize to sell their digital inventory. + +Edit the **Ads.txt Template** as needed and click **View ads.txt** to view the rendered output. + +## Security Settings + +The [security.txt](https://securitytxt.org/) defines a standard to help organizations define the process for security researchers to disclose security vulnerabilities securely. + +Edit the default **Security.txt Template** as needed and click **View security.txt** to view the rendered `security.txt` output. diff --git a/docs/docs/configuring/index.md b/docs/docs/configuring/index.md new file mode 100644 index 000000000..7b8407995 --- /dev/null +++ b/docs/docs/configuring/index.md @@ -0,0 +1,67 @@ +--- +title: Configuring SEOmatic +description: Configuring SEOmatic documentation for the SEOmatic plugin. The SEOmatic plugin facilitates modern SEO best practices & implementation for Craft CMS 3. +--- +# Configuring SEOmatic + +SEOmatic gets working as soon as you install it, but it needs to be configured for your site to be truly useful. + +This section covers [what SEOmatic does automatically](#automatic-behavior) and common [text field features](#control-panel-settings-fields), then walks through each of the SEOmatic settings sections. + +## Automatic Behavior + +As soon as you install SEOmatic, it automatically will render metadata on your web pages, and create sitemaps for all of your Sections, Category Groups, and Commerce Product Types that have public URLs. You don’t need to add any template code for this to happen. + +![Screenshot of Content SEO Twitter settings for a Blog section, with an open site menu displaying options for “Affiliate”, “English”, and “Spanish” sites](../resources/screenshots/seomatic-multi-site.png) + +All of SEOmatic’s settings are multi-site aware, allowing you to have different settings for each site/language combination. + +::: tip Check Your Multi-Environment Settings +Make sure you’ve set up your [Multi-Environment Config Settings](./multi-environment.md) properly if you’re using SEOmatic in multiple environments. +::: + +## Control Panel Settings Fields + +While you may not normally need to take advantage of it, SEOmatic’s text input fields for the [Global SEO](./global-seo.md) and [Content SEO](./content-seo.md) settings have bonus perks: + +- They’re parsed as Twig object templates, so you can use single- and double-bracket Twig expressions in them along with plain old text. +- They’re parsed for aliases and [environment variables](https://craftcms.com/docs/3.x/config/#control-panel-settings). +- They have access to SEOmatic’s global variables. + +#### Examples + +This outputs the contents of the **companyInfo** field from the **siteInfo** global: + +```twig +{{ siteInfo.companyInfo }} +``` + +This outputs the contents of the **description** field from the current entry, which would be relevant to a [Content SEO](./content-seo.md) setting: + +```twig +{{ entry.description }} +``` + +This complex expression uses SEOmatic’s [empty coalesce operator](../using/empty-coalesce-operator.md) (`???`) to output the first global field that isn’t empty, or fallback text: + +```twig +{{ siteInfo.companyInfo ??? siteInfo.companyText ??? "Some default text" }} +``` + +You could do the exact same thing with the `entry` variable when that’s available: + +```twig +{{ entry.description ??? entry.summary ??? "Some default text" }} +``` + +You can access SEOmatic global variables using Twig’s usual double-bracket syntax: + +```twig +{{ seomatic.meta.seoTitle }} +``` + +Single-bracket syntax is available for compatibility with previous SEOmatic versions: + +```twig +{seomatic.meta.seoTitle} +``` diff --git a/docs/docs/configuring/multi-environment.md b/docs/docs/configuring/multi-environment.md new file mode 100644 index 000000000..923dce2a0 --- /dev/null +++ b/docs/docs/configuring/multi-environment.md @@ -0,0 +1,119 @@ +# Multi-Environment Config Settings + +SEOmatic does different things depending on the SEOmatic environment it is running in. This is a separate setting from your Craft environment, because you can name those anything you like. + +SEOmatic needs some way to map what you call your local, staging, and production environments to a normalized representation. + +In `local` dev and `staging` environments, the following things change: + +1. `<meta name="robots">` tags are rendered with `none` to prevent Google indexing. +2. The `robots.txt` page is rendered to disallow all indexing. +3. No scripts are loaded on the page, to prevent errant data being sent to endpoints. +4. Because the `<meta name="robots">` tag is set to `none`, the `<link rel="canonical">` is not rendered. + +You can override all of these things as you see fit, but they are automatically changed in this manner to help protect you from having pages indexed or sending data from environments where you should not. + +If you’re using a multi-environment config, you can map your environment settings using SEOmatic’s `config.php` something like this: + +```php +<?php +return [ + // The public-facing name of the plugin + 'pluginName' => 'SEOmatic', + + // Should SEOmatic render metadata? + 'renderEnabled' => true, + + // Should SEOmatic render frontend sitemaps? + 'sitemapsEnabled' => true, + + // Should sitemaps be regenerated automatically? + 'regenerateSitemapsAutomatically' => true, + + // Should sitemaps be submitted to search engines automatically whenever there are changes? + 'submitSitemaps' => true, + + // Should SEOmatic add to the http response headers? + 'headersEnabled' => true, + + // The server environment, either `live`, `staging`, or `local` + 'environment' => 'live', + + // Should SEOmatic display the SEO Preview sidebar? + 'displayPreviewSidebar' => true, + + // Should SEOmatic add a Social Media Preview Target? + 'socialMediaPreviewTarget' => true, + + // The social media platforms that should be displayed in the SEO Preview sidebar + 'sidebarDisplayPreviewTypes' => [ + 'google', + 'twitter', + 'facebook' + ], + + // Should SEOmatic display the SEO Analysis sidebar? + 'displayAnalysisSidebar' => true, + + // If `devMode` is on, prefix the <title> with this string + 'devModeTitlePrefix' => '🚧 ', + + // Prefix the control panel <title> with this string + 'cpTitlePrefix' => '⚙ ', + + // If `devMode` is on, prefix the control panel <title> with this string + 'devModeCpTitlePrefix' => '🚧⚙ ', + + // The separator character to use for the `<title>` tag + 'separatorChar' => '|', + + // The max number of characters in the `<title>` tag + 'maxTitleLength' => 70, + + // The max number of characters in the `<meta name="description">` tag + 'maxDescriptionLength' => 155, + + // Site Groups define logically separate sites + 'siteGroupsSeparate' => true, + + // Whether to dynamically include the hreflang tags + 'addHrefLang' => true, + + // Whether to dynamically include the `x-default` hreflang tags + 'addXDefaultHrefLang' => true, + + // Whether to dynamically include hreflang tags on paginated pages + 'addPaginatedHreflang' => true, + + // Should the Canonical URL be automatically lower-cased? + 'lowercaseCanonicalUrl' => true, + + // Should the meta generator tag and X-Powered-By header be included? + 'generatorEnabled' => true, + + // SEOmatic uses the Craft `siteUrl` to generate the external URLs. If you + // are using it in a non-standard environment, such as a headless GraphQL or + // ElementAPI server, you can override what it uses for the `siteUrl` below. + 'siteUrlOverride' => '', + + // The duration of the SEOmatic meta cache in seconds. Null means always cached until explicitly broken + // If devMode is on, caches last 30 seconds. + 'metaCacheDuration' => null, + + // Determines whether the meta container endpoint should be enabled for anonymous front end access + 'enableMetaContainerEndpoint' => false, + + // Determines whether the JSON-LD endpoint should be enabled for anonymous front end access + 'enableJsonLdEndpoint' => false, + + // SeoElementInterface[] The default SeoElement type classes + 'defaultSeoElementTypes' => [ + ], +]; +``` + +Copy the `config.php` to your Craft `config/` directory as `seomatic.php` and you can configure the settings in a multi-environment friendly way. See the [Craft Environments](https://craftcms.com/docs/3.x/config/#config-files) page for details, and **N.B.:** + +> The `'*'` key is required here so Craft knows to treat it as a multi-environment key, but the other keys are up to you + +This is how you can make your multi-environment nomenclature to SEOmatic’s. This works exactly like Craft’s [multi-environment config](https://craftcms.com/docs/3.x/config/#multi-environment-configs) files such as `general.php` and `db.php`. See [SEOmatic’s `config.php`](https://github.com/nystudio107/craft-seomatic/blob/develop/src/config.php) for details. diff --git a/docs/docs/configuring/plugin-settings.md b/docs/docs/configuring/plugin-settings.md new file mode 100644 index 000000000..da99c394c --- /dev/null +++ b/docs/docs/configuring/plugin-settings.md @@ -0,0 +1,58 @@ +# Plugin Settings + +Plugin Settings let you control various SEOmatic settings across all sites/languages. + +![Screenshot of the SEOmatic plugin settings in the Craft control panel, with tabs for General, Appearance, Titles, Tags, Endpoints, and Advanced](../resources/screenshots/seomatic-plugin-settings.png) + +## General Plugin Settings + +* **Plugin name** – The name used for the plugin throughout the control panel. +* **Automatic Render Enabled** – Controls whether SEOmatic automatically renders metadata on your pages. If you turn this off, you will need to manually render the metadata via `seomatic.tag.render()`, `seomatic.link.render()`, etc. Selectively disable rendering via Twig with `{% do seomatic.config.renderEnabled(false) %}`. +* **Sitemaps Enabled** – Controls whether SEOmatic will automatically render front-end sitemaps for your site. +* **Regenerate Sitemaps Automatically** – Controls whether sitemaps will automatically be regenerated when entries are saved. +* **Submit Sitemap Changes** – Should sitemaps be submitted to search engines automatically whenever there are changes? +* **Include Homepage in Breadcrumbs** – Should the homepage be included in the generated Breadcrumbs JSON-LD? +* **Manually Set SEOmatic Environment** – If off, SEOmatic will automatically attempt to determine the current environment. Turn this on to manually set the environment. +* **Environment** – The server environment, either `live`, `staging`, or `local`. If `devMode` is on, SEOmatic will override this setting to local Development. This setting controls whether certain things render; for instance only in the `live` production environment will Google Analytics and other tracking tags send analytics data. SEOmatic also automatically sets the `robots` tag to `none` for everything but the `live` production environment. + +## Appearance Plugin Settings + +* **Display Sidebar SEO Preview** – Controls whether to display the Google, Twitter, and Facebook social media previews in the sidebar on entry. Category, and product pages. +* **Add Social Media Preview Target** – Controls whether to add the Google, Twitter, Facebook, etc. social media previews as a Preview Target. +* **SEO Preview Sites** – Social media platforms that should be displayed in the SEO Preview. + +## Title Plugin Settings + +* **devMode `<title>` prefix** – If devMode is on, prefix the `<title>` with this string. +* **Control Panel `<title>` prefix** – Prefix the control panel `<title>` with this string. +* **devMode Control Panel `<title>` prefix** – If devMode is on, prefix the control panel `<title>` with this string. +* **Separator Character** – The separator character to use for the `<title>` tag. +* **Max SEO Title Length** – The max number of characters in the `<title>` tag; anything beyond this will be truncated on word boundaries. +* **Max SEO Description Length** – The max number of characters in the `meta description` tag. +* **Truncate Title Tags** – Should Title tags be truncated at the max length, on word boundaries? +* **Truncate Description Tags** – Should Description tags be truncated at the max length, on word boundaries? + +## Tags Plugin Settings + +* **Add `hreflang` Tags** – Controls whether SEOmatic will automatically add `hreflang` and `og:locale:alternate` tags. +* **Include `x-default` hreflang Tag** – Controls whether SEOmatic will automatically include an x-default hreflang tag. +* **Include Paginated `hreflang` Tags** – Controls whether SEOmatic will automatically include hreflang tags on paginated pages +* **Generator Enabled** – Controls whether SEOmatic will include the meta `generator` tag and `X-Powered-By` header +* **HTTP Headers Enabled** – Controls whether SEOmatic will automatically add `X-Robots-Tag`, `canonical`, & `Referrer-Policy` to the http response headers. +* **Nonces for `<script>` tags** – Whether SEOmatic should automatically add script-src [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src) (CSP) nonces to `<script>` tags (including JSON-LD). +* **Fixed `script-src` Content Security Policies** – Fixed [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src) (CSP) script-src policies that should be added before the Nonces. + +## Endpoints Plugin Settings + +* **Meta Container Endpoint Access** – Whether anonymous access to the Meta Container endpoint should be allowed. +* **JSON-LD Endpoint Access** – Whether anonymous access to the JSON-LD endpoint should be allowed. + +## Advanced Plugin Settings + +* **Site Groups define logically separate sites** – If you are using Site Groups to logically separate 'sister sites’, turn this on. +* **Always include `canonical` links regardless of environment** - SEO best practices are to have canonical links not appear on pages that are not intended to be indexed. SEOmatic does this for you by default, but you can override that behavior with this setting +* **Lowercase Canonical URL** – Should the Canonical URL be automatically lower-cased? +* **SEOmatic Debug Toolbar Panel** - Determines whether the SEOmatic debug toolbar panel should be added to the Yii2 debug toolbar. +* **Site URL Override** – SEOmatic uses the Craft siteUrl to generate the external URLs. If you are using it in a non-standard environment, such as a headless GraphQL or ElementAPI server, you can override what it uses for the `siteUrl`. +* **Meta Cache Duration** – The duration of the SEOmatic meta cache. The default Unlimited setting is typically desired, as SEOmatic will break the cache as needed. If devMode is on, caches last 30 seconds. + diff --git a/docs/docs/configuring/site-settings.md b/docs/docs/configuring/site-settings.md new file mode 100644 index 000000000..bc1630af7 --- /dev/null +++ b/docs/docs/configuring/site-settings.md @@ -0,0 +1,45 @@ +# Site Settings + +Site Settings are broadly used to identify the site’s creator and establish links for search engines and external services. + +## Identity Settings + +![Screenshot of SEOmatic’s Identity settings in the Site Settings section, with fields visible for Site Name, Site Owner Entity Type, Entity Name, Alternate Entity Name, Entity Description, and Entity URL](../resources/screenshots/seomatic-site-identity.png) + +These settings are used to globally define the identity and ownership of the site. + +They’re combined with the SEO Template Meta settings to generate [JSON-LD](https://developers.google.com/schemas/formats/json-ld?hl=en) microdata. + +The Site Owner type determines the JSON-LD schema that will be used to identity the site to search engines. + +Leave any fields blank that aren’t applicable or which you do not want as part of the SEO schema. + +## Creator Settings + +![Screenshot of SEOmatic’s Creator settings in the Site Settings section, with fields visible for Site Creator Entity Type, Entity Name, Alternate Entity Name, Entity Description, and Entity URL](../resources/screenshots/seomatic-site-creator.png) + +These settings are used to globally define the _creator_ of the site, such as an agency or freelancer. + +They’re combined with the SEO Template Meta settings to generate [JSON-LD](https://developers.google.com/schemas/formats/json-ld?hl=en) microdata and the `humans.txt` file. + +The Site Creator type determines the JSON-LD schema that will be used to identity the site to search engines. + +Leave any fields blank that aren’t applicable or which you do not want as part of the SEO schema. + +## Social Media Settings + +![Screenshot of SEOmatic’s Social Media settings in the Site Settings section, with fields visible for Twitter Handle, Facebook Profile ID, Facebook App ID, Google Site Verification, Bing Site Verification, and Pinterest Site Verification](../resources/screenshots/seomatic-site-social.png) + +The social media settings connect your site to its other points of pressence on the internet. They also facilitate attaching your branding to social media posts via Twitter Cards and Facebook Open Graph. + +## Sitemap Settings + +![Screenshot of SEOmatic’s Sitemap settings in the Site Settings section, with table fields for adding additional sitemaps by URL and last modification date, and sitemap URLs by URL, change frequency, and priority](../resources/screenshots/seomatic-site-sitemap.png) + +SEOmatic will automatically create a sitemap for each of your sections, but if you have additional sitemaps or individual URLs that are outside of the CMS that you want to include, you can add them here. + +## Miscellaneous Settings + +![Screenshot of SEOmatic’s Miscellaneous settings in the Site Settings section, with fields for Search Target URL Pattern and Search Query Input](../resources/screenshots/seomatic-site-misc.png) + +Miscellaneous site-wide SEO settings. diff --git a/docs/docs/configuring/tracking-scripts.md b/docs/docs/configuring/tracking-scripts.md new file mode 100644 index 000000000..8d0f7c48c --- /dev/null +++ b/docs/docs/configuring/tracking-scripts.md @@ -0,0 +1,85 @@ +# Tracking Scripts + +SEOmatic supports setting up common tracking scripts from the control panel. + +These are included on front-end pages when the SEOmatic environment is set to `live` production. If `devMode` is enabled, the SEOmatic environment is automatically set to `local` development. + +## Google Analytics Settings + +![Screenshot of SEOmatic’s Google Analytics settings in the Tracking Scripts section, with an enabled switch and fields visible for Google Analytics Tracking ID, Automatically send Google Analytics PageView, Google Analytics IP Anonymization, Display Features, and Ecommerce](../resources/screenshots/seomatic-tracking-ga.png) + +Google Analytics gives you the digital analytics tools you need to analyze data from all touchpoints in one place, for a deeper understanding of the customer experience. You can then share the insights that matter with your whole organization. [Learn More](https://www.google.com/analytics/analytics/#?modal_active=none) + +If you’d like to include the Google Analytics script even when `devMode` is enabled, you can add this line to a template: + +```twig +{% do seomatic.script.get('googleAnalytics').include(true) %} +``` + +## Google `gtag.js` Settings + +![Screenshot of SEOmatic’s Google gtag.js settings in the Tracking Scripts section, with an enabled switch and fields visible for Google Analytics Tracking ID, AdWords Conversion ID, DoubleClick Floodlight ID, and Automatically send PageView](../resources/screenshots/seomatic-tracking-gtag.png) + +The [global site tag (gtag.js)](https://developers.google.com/gtagjs/) is a JavaScript tagging framework and API that allows you to send event data to AdWords, DoubleClick, and Google Analytics. Instead of having to manage multiple tags for different products, you can use gtag.js and more easily benefit from the latest tracking features and integrations as they become available. + +If you’d like to include the gtag.js script even when `devMode` is enabled, you can add this line to a template: + +```twig +{% do seomatic.script.get('gtag').include(true) %} +``` + +## Google Tag Manager Settings + +![Screenshot of SEOmatic’s Google Tag Manager settings in the Tracking Scripts section, with an enabled switch and fields visible for Google Tag Manager ID, DataLayer Variable Name, Google Tag Manager Script URL, and Google Tag Manager Script <noscript> URL](../resources/screenshots/seomatic-tracking-gtm.png) + +[Google Tag Manager](https://support.google.com/tagmanager/answer/6102821?hl=en) is a tag management system that allows you to quickly and easily update tags and code snippets on your site. Once the Tag Manager snippet has been added to your site or mobile app, you can configure tags via Google’s web-based user interface without having to alter and deploy additional code. + +You can set the `dataLayer` passed in to Google Tag Manager via Twig: + +```twig +{% do seomatic.script.get('googleTagManager').dataLayer({ + 'woof': 'bark' +}) %} +``` + +If you’d like to include the Google Tag Manager script even when `devMode` is enabled, you can add this line to a template: + +```twig +{% do seomatic.script.get('googleTagManager').include(true) %} +``` + +## Facebook Pixel Settings + +![Screenshot of SEOmatic’s Facebook Pixel settings in the Tracking Scripts section, with an enabled switch and fields visible for Facebook Pixel ID, Automatically send Facebook Pixel PageView, Facebook Pixel Script URL, and Facebook Pixel Script <noscript> URL](../resources/screenshots/seomatic-tracking-fb.png) + +The [Facebook Pixel](https://www.facebook.com/business/help/651294705016616) is an analytics tool that helps you measure the effectiveness of your advertising. You can use the Facebook pixel to understand the actions people are taking on your site and reach audiences you care about. + +If you’d like to include the Facebook Pixel script even when `devMode` is enabled, you can add this line to a template: + +```twig +{% do seomatic.script.get('facebookPixel').include(true) %} +``` + +## LinkedIn Insight Settings + +![Screenshot of SEOmatic’s LinkedIn Insight settings in the Tracking Scripts section, with an enabled switch and fields visible for LinkedIn Data Partner ID, LinkedIn Insight Script URL, LinkedIn Insight <noscript> URL, and Script Template](../resources/screenshots/seomatic-tracking-li.png) + +The LinkedIn Insight Tag is a lightweight JavaScript tag that powers conversion tracking, retargeting, and web analytics for LinkedIn ad campaigns. + +If you’d like to include the LinkedIn Insight script even when `devMode` is enabled, you can add this line to a template: + +```twig +{% do seomatic.script.get('linkedInInsight').include(true) %} +``` + +## HubSpot Settings + +![Screenshot of SEOmatic’s HubSpot settings in the Tracking Scripts section, with an enabled switch and fields visible for HubSpot ID, HubSpot Script URL, and Body Script Template](../resources/screenshots/seomatic-tracking-hs.png) + +If you’re not hosting your entire site on HubSpot, or have pages on your site that are not hosted on HubSpot, you’ll need to install the HubSpot tracking code on your non-HubSpot pages to capture those analytics. + +If you’d like to include the HubSpot script even when `devMode` is enabled, you can add this line to a template: + +```twig +{% do seomatic.script.get('hubSpot').include(true) %} +``` diff --git a/docs/docs/fields.md b/docs/docs/fields.md index a262c342a..a6c0c3707 100644 --- a/docs/docs/fields.md +++ b/docs/docs/fields.md @@ -1,50 +1,49 @@ --- -title: SEOmatic Fields +title: SEO Settings Field description: SEOmatic Fields documentation for the SEOmatic plugin. The SEOmatic plugin facilitates modern SEO best practices & implementation for Craft CMS 3. --- -# SEOmatic Fields -## SEO Settings Field +# SEO Settings Field SEOmatic has an SEO Settings Field that you can add to your Field Layouts. For most sites, the Field is not needed; instead set up the fields to pull from in the Content SEO settings for each Section. Modern SEO works best if it actually reflects what is on the page, visible to the user, so pulling from your page’s content will work well in most cases. -### Using SEO Settings Fields +## Using SEO Settings Fields However, in some cases you may want more control over page SEO for specific entries. That’s where the SEO Settings field comes in. Add it to your Section’s Field Layout, and you can override specific SEO settings on a per-entry basis. When a new entry is created with an SEO Settings field in it, the field values will all have the **Override** light switch turned off. -The Field settings let you control exactly what fields will appear and be visible for you or your client to override: +The field settings let you control exactly what fields will appear and be visible for you or your client to override: -![Screenshot](./resources/screenshots/seomatic-field-settings.png) +![Screenshot of settings for the SEO Settings field type](./resources/screenshots/seomatic-field-settings.png) By default, just a few sensible settings are made visible in the Field: -![Screenshot](./resources/screenshots/seomatic-field-defaults.png) +![Screenshot of the SEO Settings field as seen from an entry detail page](./resources/screenshots/seomatic-field-defaults.png) With the **Override** light switch off, the settings will default to the Content SEO and/or Global SEO setting for that field, so you can use the Field only for the exceptional cases. If you turn the **Override** light switch on: -![Screenshot](./resources/screenshots/seomatic-field-defaults-override.png) +![Screenshot of the SEO Settings field as seen from an entry detail page, with the SEO Description Source switch flipped on and a Custom Text selection making an input available for text entry](./resources/screenshots/seomatic-field-defaults-override.png) ...you can override the given setting on a per-entry basis. You can enable every possible field to be displayed in the SEO Settings field if you like: -![Screenshot](./resources/screenshots/seomatic-field-full.png) +![Screenshot of the SEO Settings field as seen from an entry detail page, showing more tabs (General, Twitter, Facebook, Sitemap) and more fields in the selected General tab](./resources/screenshots/seomatic-field-full.png) But it’s probably best to limit it to just the things that you or your client might want to change on a per-entry basis. If you enable an SEO Settings field in an Element Index’s Table Columns, you’ll see an SEO preview there as well: -![Screenshot](./resources/screenshots/seomatic-table-columns-google.png) +![Screenshot of a Craft CMS Entries channel listing, with an SEO Settings column to the right that displays a SERP preview next to each item](./resources/screenshots/seomatic-table-columns-google.png) You can control whether the preview will be from Google, Facebook, or Twitter in the Field Settings. -![Screenshot](./resources/screenshots/seomatic-table-columns-facebook.png) +![Screenshot of a Craft CMS Entries channel listing, with an SEO Settings column to the right displaying a social card preview next to each item](./resources/screenshots/seomatic-table-columns-facebook.png) ### Template Access @@ -54,9 +53,9 @@ To access your SEOmatic field’s data directly in a template, you can do so via {{ entry.mySeoSettingsField.metaGlobalVars.seoTitle }} ``` -All of the variables listed in the [General Variables](./using.html#general-variables) can be accessed in this manner. +All of the variables listed in the [General Variables](./using/index.md#seomatic-variables) can be accessed in this manner. -There may be occasions where you want to output the final parsed value of an SEOmatic variable on the frontend. You can do that via `entry.mySeoSettingsField.metaGlobalVars.parsedValue()`. For example: +There may be occasions where you want to output the final parsed value of an SEOmatic variable on the front end. You can do that via `entry.mySeoSettingsField.metaGlobalVars.parsedValue()`. For example: ```twig {{ entry.mySeoSettingsField.metaGlobalVars.parsedValue('seoDescription') }} @@ -65,5 +64,3 @@ There may be occasions where you want to output the final parsed value of an SEO This will output the final parsed value of the `entry.mySeoSettingsField.metaGlobalVars.seoDescription` variable. This parsing is done automatically by SEOmatic just before the meta information is added to your page. - -Brought to you by [nystudio107](https://nystudio107.com/) diff --git a/docs/docs/google-amp.md b/docs/docs/google-amp.md new file mode 100644 index 000000000..98c019d1f --- /dev/null +++ b/docs/docs/google-amp.md @@ -0,0 +1,53 @@ +# Google AMP Support + +SEOmatic works great with [Google AMP](https://www.ampproject.org/)! In fact, it will provide the [JSON-LD structured data](https://www.ampproject.org/docs/fundamentals/spec) that is _required_ by the AMP spec. + +You do however need to [Make your page discoverable](https://www.ampproject.org/docs/fundamentals/discovery): + +Add the following to the non-AMP template to tell Google where the AMP version of the page is; `yourAmpPageLink` the URL to your AMP page: + +```twig +{% set linkTag = seomatic.link.create({ + "rel": "amphtml", + "href": yourAmpPageLink +}) %} +``` + +And this to the AMP template to tell Google where the canonical HTML page is: + +```twig +{% do seomatic.meta.canonicalUrl(entry.url) %} +``` + +Since AMP [doesn’t allow for third-party JavaScript](https://medium.com/google-developers/how-to-avoid-common-mistakes-when-publishing-accelerated-mobile-pages-9ea61abf530f), you might want to add this to your AMP templates: + +```twig +{% do seomatic.script.container().include(false) %} +``` + +This will cause SEOmatic to not render _any_ custom scripts you might have enabled (such as Google Analytics, gtag, etc.) + +Then you can include Google AMP Analytics as per [Adding Analytics to your AMP pages](https://developers.google.com/analytics/devguides/collection/amp-analytics/) (this assumes you’re using `gtag`): + +```twig +{% set script = seomatic.script.get('gtag') %} +{% set analyticsId = script.vars.googleAnalyticsId.value ??? '' %} + +<amp-analytics type="googleanalytics"> + <script type="application/json"> + { + "vars": { + "account": "{{ analyticsId }}" + }, + "triggers": { + "trackPageview": { + "on": "visible", + "request": "pageview" + } + } + } + </script> +</amp-analytics> +``` + +The above uses the `???` empty coalesce operator that comes with SEOmatic; check out [SEOmatic’s ??? Empty Coalesce operator](./using/empty-coalesce-operator.md) for details. diff --git a/docs/docs/index.md b/docs/docs/index.md index 11be1bce8..c9b994fbc 100644 --- a/docs/docs/index.md +++ b/docs/docs/index.md @@ -2,47 +2,61 @@ title: SEOmatic plugin for Craft CMS 3.x description: Documentation for the SEOmatic plugin. The SEOmatic plugin facilitates modern SEO best practices & implementation for Craft CMS 3. --- -[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/nystudio107/craft-seomatic/badges/quality-score.png?b=v3)](https://scrutinizer-ci.com/g/nystudio107/craft-seomatic/?branch=v3) [![Code Coverage](https://scrutinizer-ci.com/g/nystudio107/craft-seomatic/badges/coverage.png?b=v3)](https://scrutinizer-ci.com/g/nystudio107/craft-seomatic/?branch=v3) [![Build Status](https://scrutinizer-ci.com/g/nystudio107/craft-seomatic/badges/build.png?b=v3)](https://scrutinizer-ci.com/g/nystudio107/craft-seomatic/build-status/v3) -[![Code Intelligence Status](https://scrutinizer-ci.com/g/nystudio107/craft-seomatic/badges/code-intelligence.svg?b=v3)](https://scrutinizer-ci.com/code-intelligence) -# SEOmatic plugin for Craft CMS 3.x +# SEOmatic Plugin for Craft CMS 3.x -SEOmatic facilitates [modern SEO best practices](https://nystudio107.com/blog/modern-seo-snake-oil-vs-substance) & implementation for Craft CMS 3. It is a turnkey SEO system that is comprehensive, powerful, and flexible. +SEOmatic is a comprehensive, powerful, and flexible turnkey SEO system that facilitates [modern SEO best practices](https://nystudio107.com/blog/modern-seo-snake-oil-vs-substance) & implementation for Craft CMS. -![Screenshot](./resources/img/plugin-banner.jpg) +![Plugin banner that reads “Introducing SEOmatic, SEO done right.”](./resources/img/plugin-banner.jpg) -Related: [SEOmatic for Craft 2.x](https://github.com/nystudio107/seomatic). SEOmatic for Craft CMS 3 is a complete rewrite & re-architecture from scratch. +[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/nystudio107/craft-seomatic/badges/quality-score.png?b=v4)](https://scrutinizer-ci.com/g/nystudio107/craft-seomatic/?branch=v4) [![Code Coverage](https://scrutinizer-ci.com/g/nystudio107/craft-seomatic/badges/coverage.png?b=v4)](https://scrutinizer-ci.com/g/nystudio107/craft-seomatic/?branch=v4) [![Build Status](https://scrutinizer-ci.com/g/nystudio107/craft-seomatic/badges/build.png?b=v4)](https://scrutinizer-ci.com/g/nystudio107/craft-seomatic/build-status/v4) [![Code Intelligence Status](https://scrutinizer-ci.com/g/nystudio107/craft-seomatic/badges/code-intelligence.svg?b=v4)](https://scrutinizer-ci.com/code-intelligence) -**Note**: _The license fee for this plugin is $99.00 via the Craft Plugin Store._ +## Key Features -One SEO-related topic that SEOmatic _does not_ cover is 404 redirects; for that we recommend our [Retour plugin](https://github.com/nystudio107/craft-retour). +- Healthy SEO by default, tailored to your Craft site’s content model—including Craft Commerce products. +- Implements HTML tags, JSON-LD microdata, Twitter Card tags, Facebook Open Graph tags, XML sitemaps, robots.txt, humans.txt, and ads.txt. +- Supports SEO-friendly pagination. +- Control panel SEO previews for content authors. +- Custom fields for overriding default SEO values. +- Visual Configuration overview for helping with project setup. +- Deep support for Craft features: multi-site, customizable permissions, headless mode, GraphQL, and more. +- Tools for validating and debugging metadata. +- Various utilities for managing additional meta tags, text excerpts, and more. + +::: tip Use Retour for 404 Redirects +SEOmatic _does not_ cover is 404 redirects; for that we recommend our [Retour plugin](https://github.com/nystudio107/craft-retour). +::: ## Used By -![Screenshot](./resources/img/moz-logo-blue.png)![Screenshot](./resources/img/craft-cms-logo.png) +<UsedByLogos /> -SEOmatic is the SEO tool that the SEO experts at [Moz.com](https://moz.com/) and the creators of Craft CMS, Pixel & Tonic, rely on to handle their SEO! +The SEO experts at [Moz.com](https://moz.com/) and the creators of Craft CMS rely on SEOmatic! ## Requirements -This plugin requires Craft CMS 3.1.19 or later. +This plugin requires Craft CMS 3.0.0 or later. ## Installation -To install SEOmatic, follow these steps: - 1. Open your terminal and go to your Craft project: - cd /path/to/project + ``` + cd /path/to/project + ``` -2. Then tell Composer to load the plugin: +2. Tell Composer to load the plugin: - composer require nystudio107/craft-seomatic + ``` + composer require nystudio107/craft-seomatic + ``` -3. Install the plugin via `./craft install/plugin seomatic` via the CLI, or in the Control Panel, go to Settings → Plugins and click the “Install” button for SEOmatic. +3. Install the plugin via CLI: -You can also install SEOmatic via the **Plugin Store** in the Craft Control Panel. + ``` + php craft install/plugin seomatic + ``` -SEOmatic works on Craft 3.x. + Or in the control panel, go to **Settings** → **Plugins** and click **Install** for SEOmatic. -Brought to you by [nystudio107](https://nystudio107.com/) +You can alternatively install SEOmatic via the **Plugin Store** in the Craft control panel. diff --git a/docs/docs/issues.md b/docs/docs/issues.md index 91ef73939..f0096e3ac 100644 --- a/docs/docs/issues.md +++ b/docs/docs/issues.md @@ -2,42 +2,48 @@ title: Issues & Upgrading description: Issues & Upgrading documentation for the SEOmatic plugin. The SEOmatic plugin facilitates modern SEO best practices & implementation for Craft CMS 3. --- + # Issues & Upgrading +Where to report issues and notes for upgrading from previous versions. + ## Issues -Please report any issues you find to the [SEOmatic Issues](https://github.com/nystudio107/craft-seomatic/issues) page. +Please report any issues you find to [SEOmatic Issues](https://github.com/nystudio107/craft-seomatic/issues) on GitHub. ## Upgrading from SEOmatic 1.x for Craft CMS 2.x -If you are upgrading a site from Craft CMS 2.x to Craft CMS 3.x that used the older SEOmatic plugin, here’s what you need to know. +If you’re upgrading a site from Craft CMS 2.x to 3.x that used the older SEOmatic plugin, here’s what you need to know. SEOmatic will migrate your old Craft 2.x Field settings & data in the following ways: - - * The Content SEO settings for each Section where you had an old SEOmatic Meta FieldType will be migrated for you. - + +* The Content SEO settings for each Section where you had an old SEOmatic Meta FieldType will be migrated for you. * If you add a new SEO Settings Field to a section that had an old SEOmatic Meta field in it, it will migrate any custom data you had entered on a per-Entry basis -**Important:** Keep your old Craft 2.x Seomatic_Meta fields intact; don’t delete them or change the Field type to the new SEO Settings field type. Instead, create a new SEO Settings field in the same Section, Category Group, or Commerce Product Type. It will automatically look for and migrate data from the old Seomatic_Meta Field. +::: warning +Keep your old Craft 2.x Seomatic_Meta fields intact; don’t delete them or change them to use the new SEO Settings field type. Instead, create a new SEO Settings field in the same Section, Category Group, or Commerce Product Type. It will automatically look for and migrate data from the old Seomatic_Meta Field. +::: SEOmatic for Craft CMS 3 is a complete rewrite and re-architecture from scratch of the plugin. This allowed us to take what we learned from SEOmatic 1.x, and rebuild it with a much more robust and extendable architecture. If feasible, we think the best way to update sites using SEOmatic is to start fresh, and explore how the conceptual changes in the plugin affect how you use it. In most cases, you don’t even need to use an SEOmatic Field, and the setup is cleaner and easier without it! We hope you love it! -**N.B.:** The Twig templating syntax has changed (but you may not need to use Twig at all with the new version), so give the docs a once-over. +::: tip Check Your Templates +The Twig templating syntax has changed. You may not need to use Twig at all with the new version, but consider giving the [Twig Templating](using/) docs a once-over. +::: ## Importing Data to SEOmatic It is highly recommended that you set up mappings from existing content to your SEO via the **Content SEO** settings as described above. Usually this obviates the need to import data into SEOmatic, instead _pulling_ it from your content. -In addition to that, however, if you also have custom SEO data that you need to import, you can import that into an SEO Settings field using the [FeedMe plugin](https://plugins.craftcms.com/feed-me) from Pixel & Tonic. - -**N.B.:** The SEO Settings field is intended to be used as a custom override for your Content SEO Settings. In many cases, you won’t need or want an SEO Settings field at all. +In addition to that, however, if you also have custom SEO data that you need to import, you can import that into an SEO Settings field using Pixel & Tonic’s [Feed Me plugin](https://plugins.craftcms.com/feed-me). -Once you have added an SEO Settings field added to a Section, it will show up as an import target in FeedMe: +::: tip +The SEO Settings field is meant to be a custom override for your Content SEO Settings. In many cases, you won’t need or want an SEO Settings field at all. +::: -![Screenshot](./resources/screenshots/seomatic-feedme-import.png) +Once you have added an SEO Settings field added to a Section, it will show up as an import target in Feed Me: -See the [FeedMe documentation](https://docs.craftcms.com/feed-me/v4/) for more information on importing data into Craft CMS. +![Screenshot of Feed Me import fields with a long list of SEO mappings](./resources/screenshots/seomatic-feedme-import.png) -Brought to you by [nystudio107](https://nystudio107.com/) +See the [Feed Me documentation](https://docs.craftcms.com/feed-me/v4/) for more information on importing data into Craft CMS. diff --git a/docs/docs/multi-site.md b/docs/docs/multi-site.md new file mode 100644 index 000000000..8e3dce9f5 --- /dev/null +++ b/docs/docs/multi-site.md @@ -0,0 +1,40 @@ +# Multi-Site Language/Locale Support + +SEOmatic comes with multi-site support baked in. Each site has its own localized settings that can be different on a per-site basis. + +Craft CMS [defines Sites](https://craftcms.com/docs/3.x/sites.html) as any combination of site settings and locale (language). But there needs to be some way to organize these sites to define a relationship between them. That’s what [Site Groups](https://github.com/craftcms/cms/issues/1668) are for. + +SEOmatic treats each Site Group as a separate entity, and any sites contained in that site group are treated as localizations of the same site. + +This is necessary because there needs to be some way to let SEOmatic know what the relationship is between the various sites. + +So for example, you might have: + +``` +├── Primary Site Group +│   ├── English Site +│   ├── Chinese Site +| └── German Site +├── Sister Site Group +│   ├── English Site +| └── German Site +``` + +While you technically don’t have to organize your Site Groups in this manner, SEOmatic currently requires it so that it can understand the relationship between your sites. + +This is necessary because for a variety of SEO-related things, we need to tell search engines what is really just another localization/translation of the same thing. + +If you _don’t_ want to organize your sites in this manner, you’ll need to turn off the **Site Groups define logically separate sites** setting on the Plugin Settings page. + +Sites that are grouped together under the same Site Group will have `<link rel="alternate" hreflang="XX">` & `<meta content="xx_XX" property="og:locale:alternate">` URLs added automatically in the HTML. + +To disable SEOmatic’s automatic rendering of these tags, you can do: + +```twig +{% do seomatic.link.get('alternate').include(false) %} +{% do seomatic.tag.get('og:locale:alternate').include(false) %} +``` + +Sites that are grouped together under the same Site Group will also be included in the appropriate sitemap indexes, and have `<xhtml:link rel="alternate" hreflang="xx-xx">` tags added to the respective sitemaps. + +To disable the generation of the `<xhtml:link rel="alternate" hreflang="xx-xx">` on a per-Entry basis, you can do this by adding an SEO Settings to the Section/Category Group/Product in question, and turn off **Sitemap Enabled** on a per-entry basis. diff --git a/docs/docs/overview.md b/docs/docs/overview.md index 72d17deb5..9123d28e7 100644 --- a/docs/docs/overview.md +++ b/docs/docs/overview.md @@ -2,27 +2,24 @@ title: SEOmatic Overview description: SEOmatic Overview documentation for the SEOmatic plugin. The SEOmatic plugin facilitates modern SEO best practices & implementation for Craft CMS 3 & 4. --- -# SEOmatic Overview - -SEOmatic facilitates [modern SEO best practices](https://nystudio107.com/blog/modern-seo-snake-oil-vs-substance) & implementation for Craft CMS 3 & 4. It is a turnkey SEO system that is comprehensive, powerful, and flexible. -SEOmatic allows you to quickly get a site up and running with a robust, comprehensive SEO strategy. It is also implemented in a Craft-y way, in that it is also flexible and customizable. +# SEOmatic Overview -It implements [JSON-LD](https://developers.google.com/schemas/formats/json-ld?hl=en) microdata, [Twitter Cards](https://dev.twitter.com/cards/overview) tags, [Facebook OpenGraph](https://developers.facebook.com/docs/sharing/opengraph) tags, [Sitemaps](https://www.sitemaps.org/protocol.html) of your content, [Robots.txt](http://www.robotstxt.org/robotstxt.html) bot directives, [Humans.txt](http://humanstxt.org) authorship accreditation, and as well as HTML meta tags. +SEOmatic allows you to quickly get a site running with a robust, comprehensive SEO strategy that follows [modern best practices](https://nystudio107.com/blog/modern-seo-snake-oil-vs-substance). It does this in a Craft-y way that’s flexible and customizable. -SEOmatic populates your templates with SEO Meta in the same way that Craft populates your templates with `entry` variables, with a similar level of freedom and flexibility in terms of how you use them. +SEOmatic adds meta containers that are available to your templates much like Craft’s `entry` variables. Working with them should feel familiar and flexible. -SEOmatic works automatically with Craft Commerce 2 as well, providing metadata, JSON-LD structured data, and sitemaps for your Products. +Unlike Craft’s `entry` variables, the meta containers may use information that’s modified at different levels and expressed in different ways on the site. -SEOmatic also caches each unique SEO Meta request so that your site performance is minimally impacted by the rich SEO Meta tags provided. +SEOmatic manages caches behind the scenes to ensure all this information can be used effectively with a minimal impact on site performance. ## The Meta Cascade -SEOmatic cascades SEO data for any given route, by allowing three distinct places where content authors / admins can add that data: +SEOmatic cascades SEO data for any given route, by allowing three distinct places where content authors can add that data: -* **Global SEO** - Global SEO settings are the base settings that are used site-wide. If there are no more specific SEO settings for a given page, it will fall back on the Global SEO settings -* **Content SEO** - Each Section in Craft CMS (Entries, Categories, Products, etc.) gets its own unique set of SEO settings that will be applied to that section (and Entry Types also can have unique settings). Typically you would set up your Content SEO settings to _pull_ from existing fields in the corresponding sections. -* **SEO Settings fields** - If you require overrides on a per-entry basis, the SEO Settings field allows you to do that as well. However, if Content SEO is set up to pull from your existing content fields, SEO Settings fields only sparringly need to be used +1. **Global SEO** – Site-wide base settings applied when there isn’t anything more specific. +2. **Content SEO** – Settings for each Craft _section_, like Entries, Categories, and Products. Sections and entry types can designate which fields SEOmatic should pull details from. +3. **SEO Settings Fields** – Entry-level customization via the included SEO Settings field type. Ideally this is only necessary for tailoring when _Content SEO_ configuration designates useful defaults. These SEO settings layer on top of each other, so the most specific value provided is always what is used for a given page. Content SEO settings override Global SEO settings, and SEO Settings fields override them both. @@ -32,10 +29,37 @@ In this way, the SEO data that SEOmatic provides _cascades_ together to form the ## Twig Overrides -SEOmatic also has a very robust Twig API for all of the SEO settings. For a given page, all of the SEO information provided by content authors cascades together as described above, and then is available in your Twig templates (or in PHP) for you to manipulate further as you see fit. +You can work with and further manipulate the SEO information from that cascade using robust Twig and PHP APIs, for complete control over the SEO meta data that is rendered for your website. + +See [Using SEOmatic](./using/) for an in-depth look at how SEOmatic works under the hood, and for reference on the Twig APIs. + +## Plugin Support + +SEOmatic automatically works with the following plugins: + +* [Craft Commerce](https://plugins.craftcms.com/commerce) from Pixel & Tonic +* [Digital Products](https://plugins.craftcms.com/digital-products) from Pixel & Tonic +* [Calendar](https://plugins.craftcms.com/calendar) from Solspace +* [Campaign](https://plugins.craftcms.com/campaign) from Put Your Lights On + +This means that SEOmatic will treat the Elements that these plugins provide as first class citizens, just like Craft Entries & Categories. + +SEOmatic will generate metadata, sitemaps, and have a Craft CP UI for them. If you have a custom Element provided by a plugin or module, you can integrate it using the [SeoElementInterface](https://github.com/nystudio107/craft-seomatic/blob/v4/src/base/SeoElementInterface.php). + +## Emoji Support + +SEOmatic supports using Emojis in any of the fields in SEOmatic, so you could use one in the SEO Description, for instance: + +![Screenshot of an SEO Settings field that includes emoji in a Custom Text description override](./resources/screenshots/seomatic-emoji-support.png) + +It’s up to Google whether or not to display the emojis that you add to your SEO meta, but used effectively, they can help make your entries in the SERP stand out from others. Learn more: [Why Use Emojis in Your SEO / PPC Strategy?](https://www.jellyfish.net/en-us/news-and-views/why-use-emojis-in-your-seo-ppc-strategy) + +![Screenshot of SEOmatic’s General settings in the Global SEO section, with the macOS emoji picker open and a boom emoji leading the global site title](./resources/screenshots/seomatic-mac-emoji-keyboard.png) + +On the Mac, you can invoke an Emoji keyboard inside of any text field by hitting Command Control Space. This works in any Mac application, not just web browsers or SEOmatic. -In this way, you have complete control over the SEO meta data that is rendered for your website. +## Single Page App (SPA) Support -See the **Using SEOmatic** section for an in-depth look at how SEOmatic works under the hood, and for reference on the Twig APIs. +SEOmatic fully supports working with SPAs, allowing you to receive the metadata needed for a given route either as an array, or as DOM elements ready to be inserted. -Brought to you by [nystudio107](https://nystudio107.com/) +See the [Headless SPA API](advanced.md#headless-spa-api) section for details. diff --git a/docs/docs/resources.md b/docs/docs/resources.md index b9c2829c2..1e9a7c34b 100644 --- a/docs/docs/resources.md +++ b/docs/docs/resources.md @@ -2,8 +2,11 @@ title: SEO Resources description: SEO Resources documentation for the SEOmatic plugin. The SEOmatic plugin facilitates modern SEO best practices & implementation for Craft CMS 3. --- + # SEO Resources +Learn more about SEO goals, strategy, and tips. + ## Crafting Modern SEO Presentation: [![Crafting Modern SEO video](https://i.vimeocdn.com/video/671925645-ac6a1d9ae202eede970bc17a8438f8efe076e0a4be34eac8bfff83bad4885ac3-d?mw=1920&mh=1080&q=70)](http://dotall.com/sessions/seo-best-practices-from-a-developers-point-of-view) @@ -21,5 +24,3 @@ description: SEO Resources documentation for the SEOmatic plugin. The SEOmatic p * [Modern SEO: Snake Oil vs. Substance](https://nystudio107.com/blog/modern-seo-snake-oil-vs-substance) * [JSON-LD, Structured Data and Erotica](https://nystudio107.com/blog/json-ld-structured-data-and-erotica) - -Brought to you by [nystudio107](https://nystudio107.com/) diff --git a/docs/docs/resources/img/craft-cms-logo.png b/docs/docs/resources/img/craft-cms-logo.png deleted file mode 100644 index bf334f2d8..000000000 Binary files a/docs/docs/resources/img/craft-cms-logo.png and /dev/null differ diff --git a/docs/docs/resources/img/moz-logo-blue.png b/docs/docs/resources/img/moz-logo-blue.png deleted file mode 100644 index f29b5f3fe..000000000 Binary files a/docs/docs/resources/img/moz-logo-blue.png and /dev/null differ diff --git a/docs/docs/resources/screenshots/seomatic-global-security.png b/docs/docs/resources/screenshots/seomatic-global-security.png deleted file mode 100644 index cbaba1a9e..000000000 Binary files a/docs/docs/resources/screenshots/seomatic-global-security.png and /dev/null differ diff --git a/docs/docs/technologies.md b/docs/docs/technologies.md deleted file mode 100644 index a15c0f6a7..000000000 --- a/docs/docs/technologies.md +++ /dev/null @@ -1,157 +0,0 @@ ---- -title: SEO Technologies -description: SEO Technologies documentation for the SEOmatic plugin. The SEOmatic plugin facilitates modern SEO best practices & implementation for Craft CMS 3. ---- -# SEO Technologies - -## Pagination and SEO - -If you are using paginated entries, you’ll want to add some additional markup to your templates to make Google et al aware of this. Fortunately, SEOmatic makes that easy, you simply do: - -```twig -{% do seomatic.helper.paginate(PAGEINFO) %} -``` - - The `PAGEINFO` here is the variable from the `{% paginate %}` tag as [described here](https://docs.craftcms.com/v3/templating/tags/paginate.html#the-pageInfo-variable), this will properly set the `canonicalUrl`, as well as adding the `<link rel='prev'>` and `<link rel='next'>` tags for you. - -A complete example (just a modified version of the [Craft 3 Documentation on {% Paginate %}](https://docs.craftcms.com/v3/templating/tags/paginate.html#the-pageInfo-variable)) might look like this: - -```twig -{% paginate craft.entries.section('blog').limit(10) as pageInfo, pageEntries %} -{% do seomatic.helper.paginate(pageInfo) %} - -{% for entry in pageEntries %} - <article> - <h1>{{ entry.title }}</h1> - {{ entry.body }} - </article> -{% endfor %} - -{% if pageInfo.prevUrl %}<a href="{{ pageInfo.prevUrl }}">Previous Page</a>{% endif %} -{% if pageInfo.nextUrl %}<a href="{{ pageInfo.nextUrl }}">Next Page</a>{% endif %} -``` -More info: [SEO Guide to Google Webmaster Recommendations for Pagination](https://moz.com/blog/seo-guide-to-google-webmaster-recommendations-for-pagination) - -## Multi-Site Language/Locale Support - -SEOmatic comes with multi-site support baked in. Each site has its own localized settings that can be different on a per-site basis. - -Craft CMS [defines Sites](https://docs.craftcms.com/v3/sites.html) as any combination of site settings and locale (language). But there needs to be some way to organize these sites to define a relationship between them. That’s what [Site Groups](https://github.com/craftcms/cms/issues/1668) are for. - -SEOmatic treats each Site Group as a separate entity, and any sites contained in that site group are treated as localizations of the same site. - -This is necessary because there needs to be some way to let SEOmatic know what the relationship is between the various sites. - -So for example, you might have: - -``` -├── Primary Site Group -│   ├── English Site -│   ├── Chinese Site -| └── German Site -├── Sister Site Group -│   ├── English Site -| └── German Site -``` - -While you technically don’t have to organize your Site Groups in this manner, SEOmatic currently requires it so that it can understand the relationship between your sites. - -This is necessary because for a variety of SEO-related things, we need to tell search engines what is really just another localization/translation of the same thing. - -If you _don’t_ want to organize your sites in this manner, you’ll need to turn off the **Site Groups define logically separate sites** setting on the Plugin Settings page. - -Sites that are grouped together under the same Site Group will have `<link rel="alternate" hreflang="XX">` & `<meta content="xx_XX" property="og:locale:alternate">` URLs added automatically in the HTML. - -To disable SEOmatic’s automatic rendering of these tags, you can do: -```twig -{% do seomatic.link.get('alternate').include(false) %} -{% do seomatic.tag.get('og:locale:alternate').include(false) %} -``` - -Sites that are grouped together under the same Site Group will also be included in the appropriate sitemap indexes, and have `<xhtml:link rel="alternate" hreflang="xx-xx">` tags added to the respective sitemaps. - -To disable the generation of the `<xhtml:link rel="alternate" hreflang="xx-xx">` on a per-Entry basis, you can do this by adding an SEO Settings to the Section/Category Group/Product in question, and turn off **Sitemap Enabled** on a per-entry basis. - -## Plugin Support - -SEOmatic automatically works with the following plugins: - -* [Craft Commerce](https://plugins.craftcms.com/commerce) from Pixel & Tonic -* [Digital Products](https://plugins.craftcms.com/digital-products) from Pixel & Tonic -* [Calendar](https://plugins.craftcms.com/calendar) from Solspace - -This means that SEOmatic will treat the Elements that these plugins provide as first class citizens, just like Craft Entries & Categories. - -SEOmatic will generate metadata, sitemaps, and have a Craft CP UI for them. If you have a custom Element provided by a plugin or module, you can integrate it using the [SeoElementInterface](https://github.com/nystudio107/craft-seomatic/blob/v3/src/base/SeoElementInterface.php). - -## Emoji Support - -SEOmatic supports using Emojis in any of the fields in SEOmatic, so you could use one in the SEO Description, for instance: - -![Screenshot](./resources/screenshots/seomatic-emoji-support.png) - -It’s up to Google whether or not to display the emojis that you add to your SEO meta, but used effectively, they can help make your entries in the SERP stand out from others. Learn more: [Why Use Emojis in Your SEO / PPC Strategy?](https://www.jellyfish.net/en-us/news-and-views/why-use-emojis-in-your-seo-ppc-strategy) - -![Screenshot](./resources/screenshots/seomatic-mac-emoji-keyboard.png) - -On the Mac, you can invoke an Emoji keyboard inside of any text field by hitting Command Control Space. This works in any Mac application, not just web browsers or SEOmatic. - -## Google AMP Support - -SEOmatic works great with [Google AMP](https://www.ampproject.org/)! In fact, it will provide the [JSON-LD structured data](https://www.ampproject.org/docs/fundamentals/spec) that is _required_ by the AMP spec. - -You do however need to [Make your page discoverable](https://www.ampproject.org/docs/fundamentals/discovery): - -Add the following to the non-AMP template to tell Google where the AMP version of the page is; `yourAmpPageLink` the URL to your AMP page: - -```twig -{% set linkTag = seomatic.link.create({ - "rel": "amphtml", - "href": yourAmpPageLink - }) -%} -``` - -And this to the AMP template to tell Google where the canonical HTML page is: - -```twig -{% do seomatic.meta.canonicalUrl(entry.url) %} -``` - -Since AMP [doesn’t allow for third-party JavaScript](https://medium.com/google-developers/how-to-avoid-common-mistakes-when-publishing-accelerated-mobile-pages-9ea61abf530f), you might want to add this to your AMP templates: -```twig -{% do seomatic.script.container().include(false) %} -``` - -This will cause SEOmatic to not render _any_ custom scripts you might have enabled (such as Google Analytics, gtag, etc.) - -Then you can include Google AMP Analytics as per [Adding Analytics to your AMP pages](https://developers.google.com/analytics/devguides/collection/amp-analytics/) (this assumes you’re using `gtag`): -``` -{% set script = seomatic.script.get('gtag') %} -{% set analyticsId = script.vars.googleAnalyticsId.value ??? '' %} -<amp-analytics type="googleanalytics"> - <script type="application/json"> - { - "vars": { - "account": "{{ analyticsId }}" - }, - "triggers": { - "trackPageview": { - "on": "visible", - "request": "pageview" - } - } - } - </script> -</amp-analytics> -``` - -The above uses the `???` empty coalesce operator that comes with SEOmatic; check out [SEOmatic’s ??? Empty Coalesce operator](#seomatics--empty-coalesce-operator) for details. - -## Single Page App (SPA) Support - -SEOmatic fully supports working with SPAs, allowing you to receive the metadata needed for a given route either as an array, or as DOM elements ready to be inserted. - -See the **Headless SPA API** section for details. - -Brought to you by [nystudio107](https://nystudio107.com/) diff --git a/docs/docs/using.md b/docs/docs/using.md deleted file mode 100644 index d068797b5..000000000 --- a/docs/docs/using.md +++ /dev/null @@ -1,841 +0,0 @@ ---- -title: Using SEOmatic -description: Using SEOmatic documentation for the SEOmatic plugin. The SEOmatic plugin facilitates modern SEO best practices & implementation for Craft CMS 3. ---- -# Using SEOmatic - -## SEOmatic’s ??? Empty Coalesce operator - -SEOmatic adds the `???` operator to Twig that will return the first thing that is defined, not null, and not empty. This allows you to safely "cascade" empty text/image values. - -This can be used both in Twig templates, and in any of SEOmatic’s fields, which are parsed as Twig templates as well. - -This is particularly useful for SEO fields (both text & images), where you’re dealing with a number of fallback/default values that may or may not exist, and may or may not be empty. - -The `???` Empty Coalescing operator is similar to the `??` [null coalescing operator](https://nystudio107.com/blog/handling-errors-gracefully-in-craft-cms#coalescing-the-night-away), but also ignores empty strings (`""`) and empty arrays (`[]`) as well. - -The problem is that to [code defensively](https://nystudio107.com/blog/handling-errors-gracefully-in-craft-cms#defensive-coding-in-twig), you want to make sure that all of these things are defined, not null, and also have a value. So you end up with something like: - -```twig -{% if entry is defined and entry.description is defined and entry.description | length %} - {% set description = entry.description %} -{% elseif category is defined and category.description is defined and category.description | length %} - {% set description = category.description %} -{% else %} - {% set description = global.description %} -{% endif %} -``` - -This gets quite verbose and quite tiresome quickly. There are other ways you can do something similar, such as using using the `?:` [ternary operator](https://twig.symfony.com/doc/2.x/templates.html#other-operators) and the [default filter](https://twig.symfony.com/doc/2.x/filters/default.html), but this too gets a bit unwieldy. - -You can use the [null coalescing operator](https://nystudio107.com/blog/handling-errors-gracefully-in-craft-cms#coalescing-the-night-away), which picks the first thing that is defined and not null: - -```twig -{% set description = entry.description ?? category.description ?? global.description %} -``` - -But the problem here is it’ll _just_ pick the first thing that is defined and not `null`. So if `entry.description` is an empty string, it’ll use that, which is rarely what you want. - -Enter the Empty Coalescing operator, and it becomes: - -```twig -{% set description = entry.description ??? category.description ??? global.description %} -``` - -Now the first thing that is defined, not null, _and_ not empty will be what `description` is set to. - -Nice. Simple. Readable. And most importantly, likely the result you’re expecting. - -The examples presented here use the `???` operator for SEOmatic functions, but you can use them for anything you like. - -We’ve submitted a [pull request](https://github.com/twigphp/Twig/pull/2787) in the hopes of making this part of Twig core. This functionality is also available separately in the [Empty Coalesce](https://nystudio107.com/plugins/empty-coalesce) plugin. - -## Twig Templating - -SEOmatic can work fully without any Twig templating code at all. However, it provides a robust API that you can tap into from your Twig templates should you desire to do so. - -SEOmatic makes a global `seomatic` variable available in your Twig templates that allows you to work with the SEOmatic variables and functions. - -## A Word About `{% cache %}` Tags - -If you use Craft’s built-in `{% cache %}` tags, ensure that you don’t have any of SEOmatic’s tags (listed below) inside of them. The reason is that SEOmatic dynamically generates the tags on each request, using its own caching system for performance reasons. - -When you surround any Twig code in a `{% cache %}` tag, that code will only ever be executed once. On subsequent runs, the HTML result of what was inside of the `{% cache %}` tag is just returned, and the Twig code inside of it is never executed. - -For more information on how the `{% cache %}` tag works, see the [The Craft {% cache %} Tag In-Depth](https://nystudio107.com/blog/the-craft-cache-tag-in-depth) article. - -## SEOmatic Variables - -All of the SEOmatic variables can be accessed as you would any normal Twig variable: - -```twig -{{ seomatic.meta.seoTitle }} -``` -Or -```twig -{% set title = seomatic.meta.seoTitle %} -``` - -They can also be changed by passing in a value with the Twig `{% do %}` syntax: - -```twig -{% do seomatic.meta.seoTitle("Some Title") %} -``` -Or -```twig -{% do seomatic.meta.seoDescription("This is my description. There are many like it, but this one is mine.") %} -``` - -You can also set multiple variables at once using array syntax: - -```twig -{% do seomatic.meta.setAttributes({ - "seoTitle": "Some Title", - "seoDescription": "This is my description. There are many like it, but this one is mine." - }) -%} -``` - -Or you can chain them together: - -```twig -{% do seomatic.meta - .seoTitle("Some Title") - .seoDescription("This is my description. There are many like it, but this one is mine.") -%} -``` - -These do the same thing, so use whichever you prefer. - -You can set SEOmatic variables anywhere in your templates, even in sub-templates you `include` from other templates. This works because SEOmatic dynamically injects the meta tags, scripts, links, and JSON-LD into your page after the template is done rendering. - -SEOmatic variables can also reference other SEOmatic variables using single-bracket syntax: - - ```twig - {% do seomatic.meta.seoDescription("{seomatic.meta.seoTitle}") %} - ``` - -You can also reference `entry`, `category`, or `product` Craft variables, if they are present in your template: - - ```twig - {% do seomatic.meta.seoTitle("{entry.title}") %} - ``` -Or -```twig - {% do seomatic.meta.seoTitle("{category.title}") %} -``` - -But most of the time, you’ll want to just set them like you would regular variables: - - ```twig - {% do seomatic.meta.seoTitle(entry.title) %} - ``` -Or -```twig - {% do seomatic.meta.seoTitle(category.title) %} -``` -...so that there is no additional Twig parsing that needs to be done. - -SEOmatic variables are also parsed for aliases, and in Craft 3.1, for [environment variables](https://docs.craftcms.com/v3/config/environments.html#control-panel-settings) as well. - -There may be occasions where you want to output the final parsed value of an SEOmatic variable on the frontend. You can do that via `seomatic.meta.parsedValue()`. For example: - -```twig -{{ seomatic.meta.parsedValue('seoDescription') }} -``` - -This will output the final parsed value of the `seomatic.meta.seoDescription` variable. - -This parsing is done automatically by SEOmatic just before the meta information is added to your page. - -## Meta Variables: `seomatic.meta` - -The `seomatic.meta` variable contains all of the meta variables that control the SEO that will be rendered on the site. They are pre-populated from your settings and content in the Control Panel, but you can change them as you see fit. - -### General Variables: - -* **`seomatic.meta.mainEntityOfPage`** - the [schema.org](http://schema.org/docs/full.html) type that represents the main entity of the page -* **`seomatic.meta.seoTitle`** - the title that is used for the `<title>` tag -* **`seomatic.meta.siteNamePosition`** - controls where the `seomatic.site.siteName` appears relative to the `seomatic.meta.seoTitle` in the `<title>` tag. Valid values are `before`, `after`, or `none`. -* **`seomatic.meta.seoDescription`** - the description that is used for the `<meta name="description">` tag -* **`seomatic.meta.seoKeywords`** - the keywords that are used for the `<meta name="keywords">` tag. Note that this tag is _ignored_ by Google -* **`seomatic.meta.seoImage`** - the image URL that is used for SEO image -* **`seomatic.meta.seoImageWidth`** - the width of the SEO image -* **`seomatic.meta.seoImageHeight`** - the height of the SEO image -* **`seomatic.meta.seoImageDescription`** - a textual description of the SEO image -* **`seomatic.meta.canonicalUrl`** - the URL used for the `<link rel="canonical">` tag. By default, this is set to `{seomatic.helper.safeCanonicalUrl()}` or `{entry.url}`/`{category.url}`/`{product.url}`, but you can change it as you see fit. This variable is also used to set the `link rel="canonical"` HTTP header. -* **`seomatic.meta.robots`** - the setting used for the `<meta name="robots">` tag that controls how bots should index your site. This variable is also used to set the `X-Robots-Tag` HTTP header. [Learn More](https://developers.google.com/search/reference/robots_meta_tag) - -### Facebook OpenGraph Variables: - -* **`seomatic.meta.ogType`** - the value used for the `<meta property="og:type">` tag, such as `website` or `article` -* **`seomatic.meta.ogTitle`** - the value used for the `<meta property="og:title">` tag. This defaults to `{seomatic.meta.seoTitle}` -* **`seomatic.meta.ogSiteNamePosition`** - controls where the `seomatic.site.siteName` appears relative to the `seomatic.meta.ogTitle` in the `<meta property="og:title">` tag. Valid values are `before`, `after`, or `none`. -* **`seomatic.meta.ogDescription`** - the value used for the `<meta property="og:description">` tag. This defaults to `{seomatic.meta.seoDescription}` -* **`seomatic.meta.ogImage`** - the value used for the `<meta property="og:image">` tag. This defaults to `{seomatic.meta.seoImage}` -* **`seomatic.meta.ogImageWidth`** - the width of the ogImage. This defaults to `{seomatic.meta.seoImageWidth}` -* **`seomatic.meta.ogImageHeight`** - the height of the ogImage. This defaults to `{seomatic.meta.seoImageHeight}` -* **`seomatic.meta.ogImageDescription`** - the value used for the `<meta property="og:image:alt">` tag. This defaults to `{seomatic.meta.seoImageDescription}` - -### Twitter Variables: - -* **`seomatic.meta.twitterCard`** - the value used for the `<meta name="twitter:card">` tag, such as `summary` or `summary_large_image` -* **`seomatic.meta.twitterCreator`** - the value used for the `<meta name="twitter:creator">` tag. This defaults to `{seomatic.site.twitterHandle}` -* **`seomatic.meta.twitterTitle`** - the value used for the `<meta name="twitter:title">` tag. This defaults to `{seomatic.meta.seoTitle}` -* **`seomatic.meta.twitterSiteNamePosition`** - controls where the `seomatic.site.siteName` appears relative to the `seomatic.meta.twitterTitle` in the `<meta name="twitter:title">` tag. Valid values are `before`, `after`, or `none`. -* **`seomatic.meta.twitterDescription`** - the value used for the `<meta name="twitter:description">` tag. This defaults to `{seomatic.meta.seoDescription}` -* **`seomatic.meta.twitterImage`** - the value used for the `<meta name="twitter:image">` tag. This defaults to `{seomatic.meta.seoImage}` -* **`seomatic.meta.twitterImageWidth`** - the width of the Twitter image. This defaults to `{seomatic.meta.seoImageWidth}` -* **`seomatic.meta.twitterImageHeight`** - the height of the Twitter image. This defaults to `{seomatic.meta.seoImageHeight}` -* **`seomatic.meta.twitterImageDescription`** - the value used for the `<meta name="twitter:image:alt">` tag. This defaults to `{seomatic.meta.seoImageDescription}` - -## Site Variables `seomatic.site` - -The `seomatic.site` variable has site-wide settings that are available on a per-site basis for multi-site setups. - -* **`seomatic.site.siteName`** - The name of the site -* **`seomatic.site.twitterHandle`** - The site Twitter handle -* **`seomatic.site.facebookProfileId`** - The site Facebook profile ID -* **`seomatic.site.facebookAppId`** - The site Facebook app ID -* **`seomatic.site.googleSiteVerification`** - The Google Site Verification code -* **`seomatic.site.bingSiteVerification`** - The Bing Site Verification code -* **`seomatic.site.pinterestSiteVerification`** - The Pinterest Site Verification code -* **`seomatic.site.sameAsLinks`** - Array of links for Same As... Sites, indexed by the handle. So for example you could access the site Facebook URL via `seomatic.site.sameAsLinks["facebook"]["url"]`. These links are used to generate the `<meta property="og:same_as">` tags, and are also used in the `sameAs` property in the `mainEntityOfPage` JSON-LD. -* **`seomatic.site.siteLinksSearchTarget`** - Google Site Links search target. [Learn More](https://developers.google.com/search/docs/data-types/sitelinks-searchbox) -* **`seomatic.site.siteLinksQueryInput`** - Google Site Links query input. [Learn More](https://developers.google.com/search/docs/data-types/sitelinks-searchbox) - -### Site Identity Variables `seomatic.site.identity` - -The `seomatic.site.identity` variable is used to create [JSON-LD Structured Data](https://developers.google.com/search/docs/guides/intro-structured-data) that _can_ appear as [Rich Snippets](https://developers.google.com/search/docs/guides/mark-up-content) on Google Search Engine Results Pages (SERP). JSON-LD Structured Data helps computers understand context and relationships, and is also read by other social media sites and apps. - -The `seomatic.site.identity` encapsulates all of the information associated with the owner of the site. - -* **`seomatic.site.identity.siteType`** - The schema.org general type -* **`seomatic.site.identity.siteSubType`** - The schema.org sub-type -* **`seomatic.site.identity.siteSpecificType`** - The schema.org specific type -* **`seomatic.site.identity.computedType`** - The computed most specific schema.org type -* **`seomatic.site.identity.genericName`** - The name of the entity that owns the site -* **`seomatic.site.identity.genericAlternateName`** - An alternate or nickname for the entity that owns the site -* **`seomatic.site.identity.genericDescription`** - A description of the entity that owns the site -* **`seomatic.site.identity.genericUrl`** - A URL for the entity that owns the site -* **`seomatic.site.identity.genericImage`** - A URL to an image or logo that represents the entity that owns the site. The image must be in JPG, PNG, or GIF format. -* **`seomatic.site.identity.genericImageWidth`** - The width of the entity image -* **`seomatic.site.identity.genericImageHeight`** - The height of the entity image -* **`seomatic.site.identity.genericImageIds`** - Asset ID array for the entity image -* **`seomatic.site.identity.genericTelephone`** - The primary contact telephone number for the entity that owns the site -* **`seomatic.site.identity.genericEmail`** - The primary contact email address for the entity that owns the site -* **`seomatic.site.identity.genericStreetAddress`** - The street address of the entity that owns the website, for example: 123 Main Street -* **`seomatic.site.identity.genericAddressLocality`** - locality of the entity that owns the website, for example: Portchester -* **`seomatic.site.identity.genericAddressRegion`** - The region of the entity that owns the website, for example: New York or NY -* **`seomatic.site.identity.genericPostalCode`** - The postal code of the entity that owns the website, for example: 14580 -* **`seomatic.site.identity.genericAddressCountry`** - The country in which the entity that owns the site is located, for example: US -* **`seomatic.site.identity.genericGeoLatitude`** - The latitude of the location of the entity that owns the website, for example: -120.5436367 -* **`seomatic.site.identity.genericGeoLongitude`** - The longitude of the location of the entity that owns the website, for example: 80.6033588 -* **`seomatic.site.identity.personGender`** - Only for entities of the type Person, the gender of the person -* **`seomatic.site.identity.personBirthPlace`** - Only for entities of the type Person, the place where the person was born -* **`seomatic.site.identity.organizationDuns`** - Only for entities of the type Organization, the DUNS (Dunn & Bradstreet) number of the organization that owns the site -* **`seomatic.site.identity.organizationFounder`** - Only for entities of the type Organization, the name of the founder of the organization -* **`seomatic.site.identity.organizationFoundingDate`** - Only for entities of the type Organization, the date the organization/company/restaurant was founded in [ISO 8601 date format](http://schema.org/Date), for example: `2018-03-26` -* **`seomatic.site.identity.organizationFoundingLocation`** - Only for entities of the type Organization, the location where the organization was founded -* **`seomatic.site.identity.organizationContactPoints`** - Only for entities of the type Organization, an array of contact points for the organization. [Learn More](https://developers.google.com/search/docs/guides/enhance-site#provide-business-contact-markup) -* **`seomatic.site.identity.corporationTickerSymbol`** - Only for entities of the type Corporation, the exchange ticker symbol of the corporation -* **`seomatic.site.identity.localBusinessPriceRange`** - Only for entities of the type LocalBusiness, the approximate price range of the goods or services offered by this local business -* **`seomatic.site.identity.localBusinessOpeningHours`** - Only for entities of the type LocalBusiness, an array of the opening hours for this local business. [Learn More][https://developers.google.com/search/docs/data-types/local-business] -* **`seomatic.site.identity.restaurantServesCuisine`** - Only for entities of the type Food Establishment, the primary type of cuisine that the food establishment serves -* **`seomatic.site.identity.restaurantMenuUrl`** - Only for entities of the type Food Establishment, a URL to the food establishment’s menu -* **`seomatic.site.identity.restaurantReservationsUrl`** - Only for entities of the type Food Establishment, a URL to the food establishment’s reservations page - -### Site Creator Variables `seomatic.site.creator` - -The `seomatic.site.creator` variable is used to create [JSON-LD Structured Data](https://developers.google.com/search/docs/guides/intro-structured-data) that _can_ appear as [Rich Snippets](https://developers.google.com/search/docs/guides/mark-up-content) on Google Search Engine Results Pages (SERP). JSON-LD Structured Data helps computers understand context and relationships, and is also read by other social media sites and apps. - -The `seomatic.site.creator` encapsulates all of the information associated with the creator of the site. This information is also used in the `humans.txt` page - -* **`seomatic.site.creator.siteType`** - The schema.org general type -* **`seomatic.site.creator.siteSubType`** - The schema.org sub-type -* **`seomatic.site.creator.siteSpecificType`** - The schema.org specific type -* **`seomatic.site.creator.computedType`** - The computed most specific schema.org type -* **`seomatic.site.creator.genericName`** - The name of the entity that created the site -* **`seomatic.site.creator.genericAlternateName`** - An alternate or nickname for the entity that created the site -* **`seomatic.site.creator.genericDescription`** - A description of the entity that created the site -* **`seomatic.site.creator.genericUrl`** - A URL for the entity that created the site -* **`seomatic.site.creator.genericImage`** - A URL to an image or logo that represents the entity that created the site. The image must be in JPG, PNG, or GIF format. -* **`seomatic.site.creator.genericImageWidth`** - The width of the entity image -* **`seomatic.site.creator.genericImageHeight`** - The height of the entity image -* **`seomatic.site.creator.genericImageIds`** - Asset ID array for the entity image -* **`seomatic.site.creator.genericTelephone`** - The primary contact telephone number for the entity that created the site -* **`seomatic.site.creator.genericEmail`** - The primary contact email address for the entity that created the site -* **`seomatic.site.creator.genericStreetAddress`** - The street address of the entity that created the website, for example: 123 Main Street -* **`seomatic.site.creator.genericAddressLocality`** - locality of the entity that created the website, for example: Portchester -* **`seomatic.site.creator.genericAddressRegion`** - The region of the entity that created the website, for example: New York or NY -* **`seomatic.site.creator.genericPostalCode`** - The postal code of the entity that created the website, for example: 14580 -* **`seomatic.site.creator.genericAddressCountry`** - The country in which the entity that created the site is located, for example: US -* **`seomatic.site.creator.genericGeoLatitude`** - The latitude of the location of the entity that created the website, for example: -120.5436367 -* **`seomatic.site.creator.genericGeoLongitude`** - The longitude of the location of the entity that created the website, for example: 80.6033588 -* **`seomatic.site.creator.personGender`** - Only for entities of the type Person, the gender of the person -* **`seomatic.site.creator.personBirthPlace`** - Only for entities of the type Person, the place where the person was born -* **`seomatic.site.creator.organizationDuns`** - Only for entities of the type Organization, the DUNS (Dunn & Bradstreet) number of the organization that created the site -* **`seomatic.site.creator.organizationFounder`** - Only for entities of the type Organization, the name of the founder of the organization -* **`seomatic.site.creator.organizationFoundingDate`** - Only for entities of the type Organization, the date the organization/company/restaurant was founded in [ISO 8601 date format](http://schema.org/Date), for example: `2018-03-26` -* **`seomatic.site.creator.organizationFoundingLocation`** - Only for entities of the type Organization, the location where the organization was founded -* **`seomatic.site.creator.organizationContactPoints`** - Only for entities of the type Organization, an array of contact points for the organization. [Learn More](https://developers.google.com/search/docs/guides/enhance-site#provide-business-contact-markup) -* **`seomatic.site.creator.corporationTickerSymbol`** - Only for entities of the type Corporation, the exchange ticker symbol of the corporation -* **`seomatic.site.creator.localBusinessPriceRange`** - Only for entities of the type LocalBusiness, the approximate price range of the goods or services offered by this local business -* **`seomatic.site.creator.localBusinessOpeningHours`** - Only for entities of the type LocalBusiness, an array of the opening hours for this local business. [Learn More][https://developers.google.com/search/docs/data-types/local-business] -* **`seomatic.site.creator.restaurantServesCuisine`** - Only for entities of the type Food Establishment, the primary type of cuisine that the food establishment serves -* **`seomatic.site.creator.restaurantMenuUrl`** - Only for entities of the type Food Establishment, a URL to the food establishment’s menu -* **`seomatic.site.creator.restaurantReservationsUrl`** - Only for entities of the type Food Establishment, a URL to the food establishment’s reservations page - -## Config Variables `seomatic.config` - -The `seomatic.config` variables are the global plugin configuration variables set in the `config.php` file. You can copy the `config.php` file to the Craft `config/` directory as `seomatic.php` to change them in a multi-environment friendly way. - -* **`seomatic.config.pluginName`** - The public-facing name of the plugin -* **`seomatic.config.renderEnabled`** - Should SEOmatic render metadata? -* **`seomatic.config.environment`** - The server environment, either `live`, `staging`, or `local` -* **`seomatic.config.displayPreviewSidebar`** - Should SEOmatic display the SEO Preview sidebar? -* **`seomatic.config.displayAnalysisSidebar`** - Should SEOmatic display the SEO Analysis sidebar? -* **`seomatic.config.devModeTitlePrefix`** - If `devMode` is on, prefix the `<title>` with this string -* **`seomatic.config.separatorChar`** - The separator character to use for the `<title>` tag -* **`seomatic.config.maxTitleLength`** - The max number of characters in the `<title>` tag -* **`seomatic.config.maxDescriptionLength`** - The max number of characters in the `<meta name="description">` tag - -## Helper Functions `seomatic.helper` - -* **`seomatic.helper.paginate(PAGEINFO)`** - Given the `PAGEINFO` variable from the `{% paginate %}` tag as [described here](https://docs.craftcms.com/v3/templating/tags/paginate.html#the-pageInfo-variable), this will properly set the `canonicalUrl`, as well as adding the `<link rel='prev'>` and `<link rel='next'>` tags for you. -* **`seomatic.helper.isPreview()`** - returns `true` if the current request is a preview, `false` if it is not -* **`seomatic.helper.sameAsByHandle(HANDLE)`** - returns an array of information about the **Same As URLs** site specified in `HANDLE`. Here’s an example of the information in the returned array: -``` -array (size=4) - 'siteName' => string 'Twitter' - 'handle' => string 'twitter' - 'url' => string 'https://twitter.com/nystudio107' - 'account' => string 'nystudio107' -``` -* **`seomatic.helper.truncate(TEXT, LENGTH, SUBSTR)`** - Truncates the `TEXT` to a given `LENGTH`. If `SUBSTR` is provided, and truncating occurs, the string is further truncated so that the substring may be appended without exceeding the desired length. -* **`seomatic.helper.truncateOnWord(TEXT, LENGTH, SUBSTR)`** - Truncates the `TEXT` to a given `LENGTH`, while ensuring that it does not split words. If `SUBSTR` is provided, and truncating occurs, the string is further truncated so that the substring may be appended without exceeding the desired length. -* **`seomatic.helper.getLocalizedUrls(URI, SITE_ID)`** - Return a list of localized URLs for a given `URI` that are in the `SITE_ID` site’s group. Both `URI` and `SITE_ID` are optional, and will use the current request’s `URI` and the current site’s `SITE_ID` if omitted. -* **`seomatic.helper.loadMetadataForUri(URI, SITE_ID)`** - Load the appropriate meta containers for the given `URI` and optional `SITE_ID` -* **`seomatic.helper.sitemapIndexForSiteId(SITE_ID)`** - Get the URL to the `SITE_ID`s sitemap index -* **`seomatic.helper.extractTextFromField(FIELD)`** - Extract plain text from a PlainText, Redactor, CKEdtior, Tags, Matrix, or Neo field -* **`seomatic.helper.extractKeywords(TEXT, LIMIT)`** - Extract up to `LIMIT` most important keywords from `TEXT` -* **`seomatic.helper.extractSummary(TEXT)`** - Extract the most important 3 sentences from `TEXT` -* **`seomatic.helper.socialTransform(ASSET, TRANSFORMNAME)`** - Transform the `ASSET` (either an Asset or an Asset ID) for social media sites in `TRANSFORMNAME`; valid values are `base`, `facebook`, `twitter-summary`, and `twitter-large` -* **`seomatic.helper.seoFileLink(FILE_URL, ROBOTS, CANONICAL, INLINE)`** - Generates a link to a local or remote file that allows you to set the `X-Robots-Tag` header via `ROBOTS` (defaults to `all`) and `Link` canonical header via `CANONICAL` (defaults to `''`) as per [Advanced rel="canonical" HTTP Headers](https://moz.com/blog/how-to-advanced-relcanonical-http-headers). `INLINE` controls whether the file will be displayed inline or downloaded. If any values are empty `''`, the headers will not be included. -* **`seomatic.helper.sanitizeUserInput(TEXT)`** - Sanitize the `TEXT` by decoding any HTML Entities, URL decoding the text, then removing any newlines, stripping HTML tags, stripping Twig tags, and changing single {}'s into ()'s - -## SEOmatic Tags & Containers - -All of the SEOmatic tags, links, scripts, title, and JSON-LD are meta objects that have their values set from the `seomatic.meta` variables. - -These meta objects know what properties they should have, and can self-validate. If `devMode` is on, you can check the Yii2 Debug Toolbar’s Log to see any validation warnings or errors with your tags. - -All of SEOmatic’s meta objects are stored in containers, and they can be accessed and manipulated directly. You can even dynamically create new tags via Twig at template render time. - -All of the meta object (tags, scripts, links, title, and JSON-LD) have the same API to make it easy to use. - -### Meta Object `.get()` -```twig -{% set descriptionTag = seomatic.tag.get("description") %} -``` -...will return the `<meta name="description">` meta object to you in `descriptionTag`. - -### Meta Object Properties - -You can access meta object properties just like you can any Twig variable: - -```twig -{{ descriptionTag.content }} -``` -Or -```twig -{% set myContent = seomatic.meta.seoTitle %} -``` - -They can also be changed by passing in a value with the Twig `{% do %}` syntax: - - -```twig -{% do descriptionTag.content("Some description") %} -``` - -All meta objects also have an `include` property that determines whether or not they should be included on your web page: - -```twig -{% do descriptionTag.include(false) %} -``` - -You could also chain this together in a single line: -```twig -{% do seomatic.tag.get("description").include(false) %} -``` - -And you can set multiple attributes at once using an array syntax: - -```twig -{% do seomatic.tag.get("description").setAttributes({ - "content": "Some Description", - "include": false - }) -%} -``` - -Which is the same as doing: - - -```twig -{% do seomatic.tag.get("description") - .content("Some Description") - .include(false) -%} -``` - -So use whatever you like better. - -### Extra Tag Attributes - -Should you need to add extra tag attributes to a Meta Item, such as the various `data-` tags, you can do that with the `.tagAttrs` property: - -```twig -{% set tag = seomatic.tag.get('description') %} -{% if tag | length %} - {% do tag.tagAttrs({ - "data-type": "lazy-description", - }) %} -{% endif %} -``` - -This will generate a tag that looks like this: -```html -<meta name="description" content="Here is my description!" data-type="lazy-description"> -``` - -A more practical example would be using [Klaro](https://heyklaro.com/) to manage Cookie consent, etc. to not activate Google Analytics until consent is given: - -```twig -{% set tag = seomatic.script.get('googleAnalytics') %} -{% if tag | length %} - {% do tag.tagAttrs({ - "type": "text/plain", - "data-type": "application/javascript", - "data-name": "google-analytics", - }) %} -{% endif %} -``` - -Then when the page renders in production, it’ll look like this: -```html -<script type="text/plain" data-name="google-analytics" data-type="application/javascript">(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ -(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), -m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) -})(window,document,'script','https://www.google-analytics.com/analytics.js','ga'); -ga('create', 'UA-XXXXXXXXX', 'auto'); -ga('send', 'pageview'); -</script> - -``` - -For a complete list of the Script handles SEOmatic uses can be found in [ScriptContainer.php](https://github.com/nystudio107/craft-seomatic/blob/v3/src/seomatic-config/globalmeta/ScriptContainer.php) - -### Meta Object `.create()` - -To create a new meta object, you pass in a key:value array of the attributes to use when creating it: - -```twig -{% set linkTag = seomatic.link.create({ - "rel": "canonical", - "href": "https://nystudio107.com" - }) -%} -``` - -By default, newly created meta objects are added to the appropriate meta container, so they will be rendered on the page. Should you wish to create a meta object but _not_ have it added to a container, you can pass in an optional `false` parameter: - -```twig -{% set linkTag = seomatic.link.create({ - "rel": "canonical", - "href": "https://nystudio107.com" - }, false) -%} -``` - -### Meta Object Validation - -All meta objects can self-validate: -```twig -{% set myJsonLd = seomatic.jsonLd.create({ - 'type': 'Article', - 'name': 'Some Blog', - 'url': 'woopsie', -}) %} - -{% if myJsonLd.validate() %} - <p>Valid!</p> -{% else %} - <ul> - {% for param,errors in myJsonLd.errors %} - <li> - {{ param ~ " " }} - <ul> - {% for error in errors %} - <li> - {{ error ~ " " }} - </li> - {% endfor %} - </ul> - </li> - {% endfor %} - </ul> -{% endif %} -``` - -This will output: - -* URL - * Must be one of these types: URL - -Which tells you that the `url` parameter is invalid. The default validation just ensures that all of the properties are correct. - -You can also set the _scenario_ to display properties that Google requires/recommends: - -```twig -{% set myJsonLd = seomatic.jsonLd.create({ - 'type': 'Article', - 'name': 'Some Blog', - 'url': 'woopsie', -}) %} - -{% do myJsonLd.setScenario('google') %} - -{% if myJsonLd.validate() %} - <p>Valid!</p> -{% else %} - <ul> - {% for param,errors in myJsonLd.errors %} - <li> - {{ param ~ " " }} - <ul> - {% for error in errors %} - <li> - {{ error ~ " " }} - </li> - {% endfor %} - </ul> - </li> - {% endfor %} - </ul> -{% endif %} -``` - -This will output: - -* URL - * Must be one of these types: URL -* Image - * This property is recommended by Google. -* Author - * This property is required by Google. -* DatePublished - * This property is required by Google. -* Headline - * This property is required by Google. -* Publisher - * This property is required by Google. -* MainEntityOfPage - * This property is recommended by Google. -* DateModified - * This property is recommended by Google. - -If the site has `devMode` on, all of the meta objects are automatically validated as they are rendered, with the results displayed in the Yii Debug Toolbar. The Yii Debug Toolbar can be enabled in your account settings page. - -## JSON-LD Meta Object Functions `seomatic.jsonLd` - -* **`seomatic.jsonLd.get(META_HANDLE)`** Returns the JSON-LD meta object of the handle `META_HANDLE` or `null` if it is not found -* **`seomatic.jsonLd.create()`** Creates a JSON-LD meta object from an array of key-value properties. The `type` can be any of the [Schema.org](http://schema.org/docs/full.html) types. -* **`seomatic.jsonLd.add(META_OBJECT)`** Adds the `META_OBJECT` to the JSON-LD container to be rendered -* **`seomatic.jsonLd.render()`** Renders all of the JSON-LD meta objects to your template. This is only needed if you have turned off **Automatic Render** in Plugin Settings -* **`seomatic.jsonLd.container()`** Returns the container that holds an array of all of the JSON-LD meta objects - -### JSON-LD Meta Object Examples: - -Create a new [Article](http://schema.org/Article) JSON-LD meta object: -```twig -{% set myJsonLd = seomatic.jsonLd.create({ - 'type': 'Article', - 'name': 'Some Blog', - 'url': 'https://nystudio107.com/blog', -}) %} -``` - -Get the existing **MainEntityOfPage** as set in the Global SEO or Content SEO Control Panel section to modify it (schema.org: [mainEntityOfPage](http://schema.org/docs/datamodel.html#mainEntityBackground)): -```twig -{% set mainEntity = seomatic.jsonLd.get('mainEntityOfPage') %} -``` - -To add something to the existing **MainEntityOfPage** (in this case an [Offer](https://schema.org/Offer)), you can do it like this: -```twig -{% set mainEntity = seomatic.jsonLd.get('mainEntityOfPage') %} - -{% set offersJsonLd = seomatic.jsonLd.create({ - 'type': 'Offer', - 'name': 'Some prop', - 'url': 'Some url', -}, false) %} - -{% do mainEntity.offers(offersJsonLd) %} -``` - -The `, false` parameter tells it to create the JSON-LD object, but to _not_ automatically add it to the JSON-LD container. We do this because we don’t want it rendered on its own, we want it as part of the existing `mainEntityOfPage` JSON-LD object. - -Get the existing **BreadcrumbList** as generated automatically by SEOmatic to modify them (schema.org: [BreadcrumbList](http://schema.org/BreadcrumbList)): -```twig -{% set crumbs = seomatic.jsonLd.get('breadcrumbList') %} -``` - -Display the breadcrumbs on the page: - -```twig -{% set crumbList = seomatic.jsonLd.get('breadcrumbList').itemListElement %} -{% for crumb in crumbList %} - <a href="{{ crumb.item }}">{{ crumb.name }}</a> - {% if not loop.last %}»{% endif %} -{% endfor %} -``` - -To entirely replace the existing **BreadcrumbList** on a page: -```twig -{% set crumbList = seomatic.jsonLd.create({ - 'type': 'BreadcrumbList', - 'name': 'Breadcrumbs', - 'description': 'Breadcrumbs list', - 'itemListElement': [ - { - 'type': 'ListItem', - 'position': 1, - 'name': 'Homepage', - 'item': 'http://example.com/' - }, - { - 'type': 'ListItem', - 'position': 2, - 'name': 'Our blog', - 'item': 'http://example.com/blog/' - }, - { - 'type': 'ListItem', - 'position': 3, - 'name': 'Technology blogs', - 'item': 'http://example.com/blog/tech' - }, - ] -}) %} -``` - -If you need to create a schema element and propagate it, then use "key". -Propagate **SiteNavigationElement**: -```twig - {% for nav in navigationMenu %} - {% do seomatic.jsonLd.create({ - 'key': 'navItem' ~ nav.title, - 'type': 'SiteNavigationElement', - 'name': nav.title, - 'url': nav.url - }) %} - {% endfor %} -``` - -Get the existing **Identity** as set in the Site Settings Control Panel section to modify it: -```twig -{% set identity = seomatic.jsonLd.get('identity') %} -``` - -Let’s say you want to add a [Brand](https://schema.org/Brand) to the **Identity**, you’d do this: - -```twig -{% set identity = seomatic.jsonLd.get('identity') %} - -{% set brand = seomatic.jsonLd.create({ - 'type': 'Brand', - 'name': 'Some prop', - 'url': 'Some url', -}, false) %} - -{% do identity.brand(brand) %} -``` - -The `, false` parameter tells it to create the JSON-LD object, but to _not_ automatically add it to the JSON-LD container. We do this because we don’t want it rendered on its own, we want it as part of the existing `mainEntityOfPage` JSON-LD object. - -Get the existing **Creator** as set in the Site Settings Control Panel section to modify it: -```twig -{% set identity = seomatic.jsonLd.get('creator') %} -``` - -## Link Meta Object Functions `seomatic.link` - -* **`seomatic.link.get(META_HANDLE)`** Returns the Link meta object of the handle `META_HANDLE` or `null` if it is not found -* **`seomatic.link.create(CONFIG_ARRAY)`** Creates a Link meta object from an array of key-value properties -* **`seomatic.link.add(META_OBJECT)`** Adds the `META_OBJECT` to the Link container to be rendered -* **`seomatic.link.render()`** Renders all of the Link meta objects to your template. This is only needed if you have turned off **Automatic Render** in Plugin Settings -* **`seomatic.link.container()`** Returns the container that holds an array of all of the Link meta objects - -### Link Meta Object Examples: - -Change the `<link rel="canonical">`: -```twig -{% do seomatic.link.get("canonical").href("https://nystudio107.com") %} -``` - -Note that you can achieve the same result with: -```twig -{% do seomatic.meta.canonicalUrl("https://nystudio107.com") %} -``` - -...since the `canonicalUrl` populates the `<link rel="canonical">` Link meta object - -To check what `alternate` links are rendered: - -```twig - {% set alt = seomatic.link.get('alternate') %} - {% do alt.href([ - 'http://example.com', - 'http://example.com/es' - ]).hreflang([ - 'x-default', - 'es', - ]) - %} -``` - -## Script Meta Object Functions `seomatic.script` - -* **`seomatic.script.get(META_HANDLE)`** Returns the Script meta object of the handle `META_HANDLE` or `null` if it is not found -* **`seomatic.script.create()`** Creates a Script meta object from an array of key-value properties -* **`seomatic.script.add(META_OBJECT)`** Adds the `META_OBJECT` to the Script container to be rendered -* **`seomatic.script.render()`** Renders all of the Script meta objects to your template. This is only needed if you have turned off **Automatic Render** in Plugin Settings -* **`seomatic.script.container()`** Returns the container that holds an array of all of the Script meta objects - -### Script Meta Object Examples: - -Don’t include the Google Analytics script on the page: -```twig -{% do seomatic.script.get("googleAnalytics").include(false) %} -``` - -For a complete list of the Script handles SEOmatic uses can be found in [ScriptContainer.php](https://github.com/nystudio107/craft-seomatic/blob/v3/src/seomatic-config/globalmeta/ScriptContainer.php) - -## Tag Meta Object Functions `seomatic.tag` - -* **`seomatic.tag.get(META_HANDLE)`** Returns the Tag meta object of the handle `META_HANDLE` or `null` if it is not found -* **`seomatic.tag.create()`** Creates a Tag meta object from an array of key-value properties -* **`seomatic.tag.add(META_OBJECT)`** Adds the `META_OBJECT` to the Tag container to be rendered -* **`seomatic.tag.render()`** Renders all of the Tag meta objects to your template. This is only needed if you have turned off **Automatic Render** in Plugin Settings -* **`seomatic.tag.container()`** Returns the container that holds an array of all of the Tag meta objects - -### Tag Meta Object Examples: - -Change the `<meta name="twitter:title">`: - -```twig -{% do seomatic.tag.get("twitter:title").content("Hello, world") %} -``` - -Note that you can achieve the same result with: -```twig -{% do seomatic.meta.twitterTitle("Hello, world") %} -``` - -...since the `twitterTitle` populates the `<meta name="twitter:title">` Tag meta object by default. - -Let’s say you didn’t want Google et al to index a particular page or under certain conditions. You could do this: - -```twig -{% do seomatic.tag.get("robots").content("none") %} -``` - -Note that you can achieve the same result with: -```twig -{% do seomatic.meta.robots("none") %} -``` - -...since the `robots` populates the `<meta name="robots">` Tag meta object by default. - -You can have multiple OpenGraph tags of the same time, for example `og:image`: - -```twig -{% set ogImage = seomatic.tag.get('og:image') %} -{% do ogImage.content([ - 'http://example.com/image1.jpg', - 'http://example.com/image2.jpg', -]) %} -``` - -...and it’ll generate a tag for each image: -```html -<meta content="http://example.com/image2.jpg" property="og:image"> -<meta content="http://example.com/image1.jpg" property="og:image"> -``` - -## Title Meta Object Functions `seomatic.title` - -* **`seomatic.title.get(META_HANDLE)`** Returns the Title meta object of the handle `META_HANDLE` or `null` if it is not found -* **`seomatic.title.create()`** Creates a Title meta object from an array of key/value properties -* **`seomatic.title.add(META_OBJECT)`** Adds the `META_OBJECT` to the Title container to be rendered -* **`seomatic.title.render()`** Renders Title meta object to your template. This is only needed if you have turned off **Automatic Render** in Plugin Settings -* **`seomatic.title.container()`** Returns the container that holds an array with the Title meta object in it - -### Tag Meta Object Examples: - -Change the `<title>`: - -```twig -{% do seomatic.title.get("title").content("My page title") %} -``` - -Note that you can achieve the same result with: -```twig -{% do seomatic.meta.seoTitle("My page title") %} -``` - -...since the `seoTitle` populates the `<title">` Title meta object - -## Meta Containers - -Normally you don’t need to work with meta containers directly, but SEOmatic gives you access to them to. - -You can get the meta container for each type of meta object by doing: - -```twig -{% set jsonLdContainer = seomatic.jsonLd.container() %} -{% set linkContainer = seomatic.link.container() %} -{% set scriptContainer = seomatic.script.container() %} -{% set tagContainer = seomatic.tag.container() %} -{% set titleContainer = seomatic.title.container() %} -``` - -Then you can do things like tell an entire container to not render: - -```twig -{% set scriptContainer = seomatic.script.container() %} -{% do scriptContainer.include(false) %} -``` - -Or just: - -```twig -{% do seomatic.script.container().include(false) %} -``` - -Containers are also cached. Typically SEOmatic manages this cache for you, but should you wish to invalidate the cache manually, you can do so via: - -```twig -{% set scriptContainer = seomatic.script.container() %} -{% do scriptContainer.clearCache(true) %} -``` - -Or just: - -```twig -{% do seomatic.script.container().clearCache(true) %} -``` - -Brought to you by [nystudio107](https://nystudio107.com/) diff --git a/docs/docs/using/config-variables.md b/docs/docs/using/config-variables.md new file mode 100644 index 000000000..075c01de5 --- /dev/null +++ b/docs/docs/using/config-variables.md @@ -0,0 +1,13 @@ +# Config Variables + +The `seomatic.config` variables are the global plugin configuration variables set in the `config.php` file. You can copy the `config.php` file to the Craft `config/` directory as `seomatic.php` to change them in a multi-environment friendly way. + +* **`seomatic.config.pluginName`** – The public-facing name of the plugin. +* **`seomatic.config.renderEnabled`** – Should SEOmatic render metadata? +* **`seomatic.config.environment`** – The server environment, either `live`, `staging`, or `local`. +* **`seomatic.config.displayPreviewSidebar`** – Should SEOmatic display the SEO Preview sidebar? +* **`seomatic.config.displayAnalysisSidebar`** – Should SEOmatic display the SEO Analysis sidebar? +* **`seomatic.config.devModeTitlePrefix`** – If `devMode` is on, prefix the `<title>` with this string. +* **`seomatic.config.separatorChar`** – The separator character to use for the `<title>` tag. +* **`seomatic.config.maxTitleLength`** – The max number of characters in the `<title>` tag. +* **`seomatic.config.maxDescriptionLength`** – The max number of characters in the `<meta name="description">` tag. diff --git a/docs/docs/using/empty-coalesce-operator.md b/docs/docs/using/empty-coalesce-operator.md new file mode 100644 index 000000000..7872fca1d --- /dev/null +++ b/docs/docs/using/empty-coalesce-operator.md @@ -0,0 +1,55 @@ +# Empty Coalesce Operator + +SEOmatic adds the `???` operator to Twig that will return the first thing that is defined, not null, and not empty. This allows you to safely "cascade" empty text/image values. + +This can be used both in Twig templates, and in any of SEOmatic’s fields, which are parsed as Twig templates as well. + +This is particularly useful for SEO fields (both text & images), where you’re dealing with a number of fallback/default values that may or may not exist, and may or may not be empty. + +The `???` Empty Coalescing operator is similar to the `??` [null coalescing operator](https://nystudio107.com/blog/handling-errors-gracefully-in-craft-cms#coalescing-the-night-away), but also ignores empty strings (`""`) and empty arrays (`[]`) as well. + +The problem is that to [code defensively](https://nystudio107.com/blog/handling-errors-gracefully-in-craft-cms#defensive-coding-in-twig), you want to make sure that all of these things are defined, not null, and also have a value. So you end up with something like: + +```twig +{% if entry is defined and + entry.description is defined and + entry.description | length +%} + {% set description = entry.description %} +{% elseif category is defined and + category.description is defined and + category.description | length +%} + {% set description = category.description %} +{% else %} + {% set description = global.description %} +{% endif %} +``` + +This gets quite verbose and quite tiresome quickly. There are other ways you can do something similar, such as using using the `?:` [ternary operator](https://twig.symfony.com/doc/2.x/templates.html#other-operators) and the [default filter](https://twig.symfony.com/doc/2.x/filters/default.html), but this too gets a bit unwieldy. + +You can use the [null coalescing operator](https://nystudio107.com/blog/handling-errors-gracefully-in-craft-cms#coalescing-the-night-away), which picks the first thing that is defined and not null: + +```twig +{% set description = entry.description ?? + category.description ?? + global.description %} +``` + +But the problem here is it’ll _just_ pick the first thing that is defined and not `null`. So if `entry.description` is an empty string, it’ll use that, which is rarely what you want. + +Enter the Empty Coalescing operator, and it becomes: + +```twig +{% set description = entry.description ??? + category.description ??? + global.description %} +``` + +Now `description` will be set to the first thing that is defined, not null, _and_ not empty. + +Nice. Simple. Readable. And most importantly, likely the result you’re expecting. + +The examples presented here use the `???` operator for SEOmatic functions, but you can use them for anything you like. + +We’ve submitted a [pull request](https://github.com/twigphp/Twig/pull/2787) in the hopes of making this part of Twig core. This functionality is also available separately in the [Empty Coalesce](https://nystudio107.com/plugins/empty-coalesce) plugin. diff --git a/docs/docs/using/helper-functions.md b/docs/docs/using/helper-functions.md new file mode 100644 index 000000000..d33992e8b --- /dev/null +++ b/docs/docs/using/helper-functions.md @@ -0,0 +1,25 @@ +# Helper Functions + +The `seomatic.helper` functions are utilities that can help with pagination, previews, and preparing text and Assets. + +* **`seomatic.helper.paginate(PAGEINFO)`** – Given the `PAGEINFO` variable from the `{% paginate %}` tag as [described here](https://craftcms.com/docs/3.x/dev/tags.html#paginate), this will properly set the `canonicalUrl`, as well as adding the `<link rel='prev'>` and `<link rel='next'>` tags for you. +* **`seomatic.helper.isPreview()`** – Returns `true` if the current request is a preview, `false` if it is not. +* **`seomatic.helper.sameAsByHandle(HANDLE)`** – Returns an array of information about the **Same As URLs** site specified in `HANDLE`. Here’s an example of the information in the returned array: + ```php + array (size=4) + 'siteName' => string 'Twitter' + 'handle' => string 'twitter' + 'url' => string 'https://twitter.com/nystudio107' + 'account' => string 'nystudio107' + ``` +* **`seomatic.helper.truncate(TEXT, LENGTH, SUBSTR)`** – Truncates the `TEXT` to a given `LENGTH`. If `SUBSTR` is provided, and truncating occurs, the string is further truncated so that the substring may be appended without exceeding the desired length. +* **`seomatic.helper.truncateOnWord(TEXT, LENGTH, SUBSTR)`** – Truncates the `TEXT` to a given `LENGTH`, while ensuring that it does not split words. If `SUBSTR` is provided, and truncating occurs, the string is further truncated so that the substring may be appended without exceeding the desired length. +* **`seomatic.helper.getLocalizedUrls(URI, SITE_ID)`** – Return a list of localized URLs for a given `URI` that are in the `SITE_ID` site’s group. Both `URI` and `SITE_ID` are optional, and will use the current request’s `URI` and the current site’s `SITE_ID` if omitted. +* **`seomatic.helper.loadMetadataForUri(URI, SITE_ID)`** – Load the appropriate meta containers for the given `URI` and optional `SITE_ID`. +* **`seomatic.helper.sitemapIndexForSiteId(SITE_ID)`** – Get the URL to the `SITE_ID`s sitemap index +* **`seomatic.helper.extractTextFromField(FIELD)`** – Extract plain text from a PlainText, Redactor, CKEdtior, Tags, Matrix, or Neo field. +* **`seomatic.helper.extractKeywords(TEXT, LIMIT)`** – Extract up to `LIMIT` most important keywords from `TEXT`. +* **`seomatic.helper.extractSummary(TEXT)`** – Extract the most important 3 sentences from `TEXT`. +* **`seomatic.helper.socialTransform(ASSET, TRANSFORMNAME)`** – Transform the `ASSET` (either an Asset or an Asset ID) for social media sites in `TRANSFORMNAME`; valid values are `base`, `facebook`, `twitter-summary`, and `twitter-large`. +* **`seomatic.helper.seoFileLink(FILE_URL, ROBOTS, CANONICAL, INLINE)`** – Generates a link to a local or remote file that allows you to set the `X-Robots-Tag` header via `ROBOTS` (defaults to `all`) and `Link` canonical header via `CANONICAL` (defaults to `''`) as per [Advanced rel="canonical" HTTP Headers](https://moz.com/blog/how-to-advanced-relcanonical-http-headers). `INLINE` controls whether the file will be displayed inline or downloaded. If any values are empty `''`, the headers will not be included. +* **`seomatic.helper.sanitizeUserInput(TEXT)`** – Sanitize the `TEXT` by decoding any HTML Entities, URL decoding the text, then removing any newlines, stripping HTML tags, stripping Twig tags, and changing single {}'s into ()'s. diff --git a/docs/docs/using/index.md b/docs/docs/using/index.md new file mode 100644 index 000000000..2d54df096 --- /dev/null +++ b/docs/docs/using/index.md @@ -0,0 +1,331 @@ +--- +title: Twig Templating +description: Using SEOmatic documentation for the SEOmatic plugin. The SEOmatic plugin facilitates modern SEO best practices & implementation for Craft CMS 3. +--- + +# Twig Templating + +SEOmatic can work fully without any Twig templating code at all. However, it provides a robust API that you can tap into from your Twig templates should you desire to do so. + +SEOmatic makes a global `seomatic` variable available in your Twig templates that allows you to work with the SEOmatic variables and functions. + +## A Word About `{% cache %}` Tags + +If you use Craft’s built-in `{% cache %}` tags, ensure that you don’t have any of SEOmatic’s tags (listed below) inside of them. The reason is that SEOmatic dynamically generates the tags on each request, using its own caching system for performance reasons. + +When you surround any Twig code in a `{% cache %}` tag, that code will only ever be executed once. On subsequent runs, the HTML result of what was inside of the `{% cache %}` tag is just returned, and the Twig code inside of it is never executed. + +For more information on how the `{% cache %}` tag works, see the [The Craft {% cache %} Tag In-Depth](https://nystudio107.com/blog/the-craft-cache-tag-in-depth) article. + +## SEOmatic Variables + +All of the SEOmatic variables can be accessed as you would any normal Twig variable: + +```twig +{{ seomatic.meta.seoTitle }} +``` +Or +```twig +{% set title = seomatic.meta.seoTitle %} +``` + +They can also be changed by passing in a value with the Twig `{% do %}` syntax: + +```twig +{% do seomatic.meta.seoTitle("Some Title") %} +``` +Or +```twig +{% do seomatic.meta.seoDescription( + "This is my description. There are many like it, but this one is mine." +) %} +``` + +You can also set multiple variables at once using array syntax: + +```twig +{% do seomatic.meta.setAttributes({ + "seoTitle": "Some Title", + "seoDescription": "This is my description. There are many like it..." +}) %} +``` + +Or you can chain them together: + +```twig +{% do seomatic.meta + .seoTitle("Some Title") + .seoDescription("This is my description. There are many like it...") %} +``` + +These do the same thing, so use whichever you prefer. + +You can set SEOmatic variables anywhere in your templates, even in sub-templates you `include` from other templates. This works because SEOmatic dynamically injects the meta tags, scripts, links, and JSON-LD into your page after the template is done rendering. + +SEOmatic variables can also reference other SEOmatic variables using single-bracket syntax: + +```twig +{% do seomatic.meta.seoDescription("{seomatic.meta.seoTitle}") %} +``` + +You can also reference `entry`, `category`, or `product` Craft variables, if they are present in your template: + +```twig +{% do seomatic.meta.seoTitle("{entry.title}") %} +``` + +Or + +```twig +{% do seomatic.meta.seoTitle("{category.title}") %} +``` + +But most of the time, you’ll want to just set them like you would regular variables: + +```twig +{% do seomatic.meta.seoTitle(entry.title) %} +``` + +Or + +```twig +{% do seomatic.meta.seoTitle(category.title) %} +``` + +...so that there is no additional Twig parsing that needs to be done. + +SEOmatic variables are also parsed for aliases, and in Craft 3.1, for [environment variables](https://craftcms.com/docs/3.x/config/#control-panel-settings) as well. + +There may be occasions where you want to output the final parsed value of an SEOmatic variable on the front end. You can do that via `seomatic.meta.parsedValue()`. For example: + +```twig +{{ seomatic.meta.parsedValue('seoDescription') }} +``` + +This will output the final parsed value of the `seomatic.meta.seoDescription` variable. + +This parsing is done automatically by SEOmatic just before the meta information is added to your page. + +## Tags & Containers + +All of the SEOmatic tags, links, scripts, title, and JSON-LD are meta objects that have their values set from the `seomatic.meta` variables. + +These meta objects know what properties they should have, and can self-validate. If `devMode` is on, you can check the Yii2 Debug Toolbar’s Log to see any validation warnings or errors with your tags. + +All of SEOmatic’s meta objects are stored in containers, and they can be accessed and manipulated directly. You can even dynamically create new tags via Twig at template render time. + +All of the meta object (tags, scripts, links, title, and JSON-LD) have the same API to make it easy to use. + +### Meta Object `.get()` + +```twig +{% set descriptionTag = seomatic.tag.get("description") %} +``` + +...will return the `<meta name="description">` meta object to you in `descriptionTag`. + +### Meta Object Properties + +You can access meta object properties just like you can any Twig variable: + +```twig +{{ descriptionTag.content }} +``` + +Or + +```twig +{% set myContent = seomatic.meta.seoTitle %} +``` + +They can also be changed by passing in a value with the Twig `{% do %}` syntax: + +```twig +{% do descriptionTag.content("Some description") %} +``` + +All meta objects also have an `include` property that determines whether or not they should be included on your web page: + +```twig +{% do descriptionTag.include(false) %} +``` + +You could also chain this together in a single line: + +```twig +{% do seomatic.tag.get("description").include(false) %} +``` + +And you can set multiple attributes at once using an array syntax: + +```twig +{% do seomatic.tag.get("description").setAttributes({ + "content": "Some Description", + "include": false +}) %} +``` + +Which is the same as doing: + +```twig +{% do seomatic.tag.get("description") + .content("Some Description") + .include(false) %} +``` + +So use whatever you like better. + +### Extra Tag Attributes + +Should you need to add extra tag attributes to a Meta Item, such as the various `data-` tags, you can do that with the `.tagAttrs` property: + +```twig +{% set tag = seomatic.tag.get('description') %} +{% if tag | length %} + {% do tag.tagAttrs({ + "data-type": "whatever", + }) %} +{% endif %} +``` + +This will generate a tag like this: + +```html +<meta name="description" content="My description!" data-type="whatever"> +``` + +A more practical example would be using [Klaro](https://heyklaro.com/) to manage Cookie consent, etc. to not activate Google Analytics until consent is given: + +```twig +{% set tag = seomatic.script.get('googleAnalytics') %} +{% if tag | length %} + {% do tag.tagAttrs({ + "type": "text/plain", + "data-type": "application/javascript", + "data-name": "google-analytics", + }) %} +{% endif %} +``` + +Then when the page renders in production, it’ll look like this: +```html +<script type="text/plain" data-name="google-analytics" data-type="application/javascript">(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ +(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), +m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) +})(window,document,'script','https://www.google-analytics.com/analytics.js','ga'); +ga('create', 'UA-XXXXXXXXX', 'auto'); +ga('send', 'pageview'); +</script> +``` + +See [ScriptContainer.php](https://github.com/nystudio107/craft-seomatic/blob/v4/src/seomatic-config/globalmeta/ScriptContainer.php) for a complete list of the script handles SEOmatic uses. + +### Meta Object `.create()` + +To create a new meta object, you pass in a key:value array of the attributes to use when creating it: + +```twig +{% set linkTag = seomatic.link.create({ + "rel": "canonical", + "href": "https://nystudio107.com" +}) %} +``` + +By default, newly created meta objects are added to the appropriate meta container, so they will be rendered on the page. Should you wish to create a meta object but _not_ have it added to a container, you can pass in an optional `false` parameter: + +```twig +{% set linkTag = seomatic.link.create({ + "rel": "canonical", + "href": "https://nystudio107.com" +}, false) %} +``` + +### Meta Object Validation + +All meta objects can self-validate: + +```twig +{% set myJsonLd = seomatic.jsonLd.create({ + 'type': 'Article', + 'name': 'Some Blog', + 'url': 'woopsie', +}) %} + +{% if myJsonLd.validate() %} + <p>Valid!</p> +{% else %} + <ul> + {% for param,errors in myJsonLd.errors %} + <li> + {{ param ~ " " }} + <ul> + {% for error in errors %} + <li> + {{ error ~ " " }} + </li> + {% endfor %} + </ul> + </li> + {% endfor %} + </ul> +{% endif %} +``` + +This will output: + +* URL + * Must be one of these types: URL + +Which tells you that the `url` parameter is invalid. The default validation just ensures that all of the properties are correct. + +You can also set the _scenario_ to display properties that Google requires/recommends: + +```twig +{% set myJsonLd = seomatic.jsonLd.create({ + 'type': 'Article', + 'name': 'Some Blog', + 'url': 'woopsie', +}) %} + +{% do myJsonLd.setScenario('google') %} + +{% if myJsonLd.validate() %} + <p>Valid!</p> +{% else %} + <ul> + {% for param,errors in myJsonLd.errors %} + <li> + {{ param ~ " " }} + <ul> + {% for error in errors %} + <li> + {{ error ~ " " }} + </li> + {% endfor %} + </ul> + </li> + {% endfor %} + </ul> +{% endif %} +``` + +This will output: + +* URL + * Must be one of these types: URL +* Image + * This property is recommended by Google. +* Author + * This property is required by Google. +* DatePublished + * This property is required by Google. +* Headline + * This property is required by Google. +* Publisher + * This property is required by Google. +* MainEntityOfPage + * This property is recommended by Google. +* DateModified + * This property is recommended by Google. + +If the site has `devMode` on, all of the meta objects are automatically validated as they are rendered, with the results displayed in the Yii Debug Toolbar. The Yii Debug Toolbar can be enabled in your account settings page. diff --git a/docs/docs/using/json-ld-meta.md b/docs/docs/using/json-ld-meta.md new file mode 100644 index 000000000..9bd904608 --- /dev/null +++ b/docs/docs/using/json-ld-meta.md @@ -0,0 +1,126 @@ +# JSON-LD Meta Object Functions + +The `seomatic.jsonLd` functions make it easier to create, manipulate, and render valid JSON-LD microdata. + +* **`seomatic.jsonLd.get(META_HANDLE)`** – Returns the JSON-LD meta object of the handle `META_HANDLE` or `null` if it is not found. +* **`seomatic.jsonLd.create()`** – Creates a JSON-LD meta object from an array of key-value properties. The `type` can be any of the [Schema.org](http://schema.org/docs/full.html) types. +* **`seomatic.jsonLd.add(META_OBJECT)`** – Adds the `META_OBJECT` to the JSON-LD container to be rendered. +* **`seomatic.jsonLd.render()`** – Renders all of the JSON-LD meta objects to your template. Only necessary if you’ve disabled **Automatic Render** in [Plugin Settings](../configuring/plugin-settings.md). +* **`seomatic.jsonLd.container()`** – Returns the container that holds an array of all of the JSON-LD meta objects. + +## JSON-LD Meta Object Examples: + +Create a new [Article](http://schema.org/Article) JSON-LD meta object: + +```twig +{% set myJsonLd = seomatic.jsonLd.create({ + 'type': 'Article', + 'name': 'Some Blog', + 'url': 'https://nystudio107.com/blog', +}) %} +``` + +Get the existing **MainEntityOfPage** as set in the Global SEO or Content SEO control panel section to modify it (schema.org: [mainEntityOfPage](http://schema.org/docs/datamodel.html#mainEntityBackground)): +```twig +{% set mainEntity = seomatic.jsonLd.get('mainEntityOfPage') %} +``` + +To add something to the existing **MainEntityOfPage** (in this case an [Offer](https://schema.org/Offer)), you can do it like this: +```twig +{% set mainEntity = seomatic.jsonLd.get('mainEntityOfPage') %} + +{% set offersJsonLd = seomatic.jsonLd.create({ + 'type': 'Offer', + 'name': 'Some prop', + 'url': 'Some url', +}, false) %} + +{% do mainEntity.offers(offersJsonLd) %} +``` + +The `, false` parameter tells it to create the JSON-LD object, but to _not_ automatically add it to the JSON-LD container. We do this because we don’t want it rendered on its own, we want it as part of the existing `mainEntityOfPage` JSON-LD object. + +Get the existing **BreadcrumbList** as generated automatically by SEOmatic to modify them (schema.org: [BreadcrumbList](http://schema.org/BreadcrumbList)): +```twig +{% set crumbs = seomatic.jsonLd.get('breadcrumbList') %} +``` + +Display the breadcrumbs on the page: + +```twig +{% set crumbList = seomatic.jsonLd.get('breadcrumbList').itemListElement %} +{% for crumb in crumbList %} + <a href="{{ crumb.item }}">{{ crumb.name }}</a> + {% if not loop.last %}»{% endif %} +{% endfor %} +``` + +To replace the existing **BreadcrumbList** on a page: + +```twig +{% set crumbList = seomatic.jsonLd.create({ + 'type': 'BreadcrumbList', + 'name': 'Breadcrumbs', + 'description': 'Breadcrumbs list', + 'itemListElement': [ + { + 'type': 'ListItem', + 'position': 1, + 'name': 'Homepage', + 'item': 'http://example.com/' + }, + { + 'type': 'ListItem', + 'position': 2, + 'name': 'Our blog', + 'item': 'http://example.com/blog/' + }, + { + 'type': 'ListItem', + 'position': 3, + 'name': 'Technology blogs', + 'item': 'http://example.com/blog/tech' + }, + ] +}) %} +``` + +Use `key` to create a schema element and propagate it. Propagate **SiteNavigationElement**: + +```twig +{% for nav in navigationMenu %} + {% do seomatic.jsonLd.create({ + 'key': 'navItem' ~ nav.title, + 'type': 'SiteNavigationElement', + 'name': nav.title, + 'url': nav.url + }) %} +{% endfor %} +``` + +Get the existing **Identity** as set in the Site Settings control panel section to modify it: + +```twig +{% set identity = seomatic.jsonLd.get('identity') %} +``` + +Let’s say you want to add a [Brand](https://schema.org/Brand) to the **Identity**, you’d do this: + +```twig +{% set identity = seomatic.jsonLd.get('identity') %} + +{% set brand = seomatic.jsonLd.create({ + 'type': 'Brand', + 'name': 'Some prop', + 'url': 'Some url', +}, false) %} + +{% do identity.brand(brand) %} +``` + +The `, false` parameter tells it to create the JSON-LD object, but to _not_ automatically add it to the JSON-LD container. We do this because we don’t want it rendered on its own, we want it as part of the existing `mainEntityOfPage` JSON-LD object. + +Get the existing **Creator** as set in the Site Settings control panel section to modify it: +```twig +{% set identity = seomatic.jsonLd.get('creator') %} +``` diff --git a/docs/docs/using/link-meta.md b/docs/docs/using/link-meta.md new file mode 100644 index 000000000..7b9ca9958 --- /dev/null +++ b/docs/docs/using/link-meta.md @@ -0,0 +1,36 @@ +# Link Meta Object Functions + +The `seomatic.link` functions are for working with `<link>` tags. + +* **`seomatic.link.get(META_HANDLE)`** – Returns the Link meta object of the handle `META_HANDLE` or `null` if it is not found. +* **`seomatic.link.create(CONFIG_ARRAY)`** – Creates a Link meta object from an array of key-value properties. +* **`seomatic.link.add(META_OBJECT)`** – Adds the `META_OBJECT` to the Link container to be rendered. +* **`seomatic.link.render()`** – Renders all of the Link meta objects to your template. Only necessary if you’ve disabled **Automatic Render** in [Plugin Settings](../configuring/plugin-settings.md). +* **`seomatic.link.container()`** – Returns the container that holds an array of all of the Link meta objects. + +## Link Meta Object Examples: + +Change the `<link rel="canonical">`: +```twig +{% do seomatic.link.get("canonical").href("https://nystudio107.com") %} +``` + +Note that you can achieve the same result with: +```twig +{% do seomatic.meta.canonicalUrl("https://nystudio107.com") %} +``` + +...since the `canonicalUrl` populates the `<link rel="canonical">` Link meta object + +To check what `alternate` links are rendered: + +```twig +{% set alt = seomatic.link.get('alternate') %} +{% do alt.href([ + 'http://example.com', + 'http://example.com/es' +]).hreflang([ + 'x-default', + 'es', +]) %} +``` diff --git a/docs/docs/using/meta-containers.md b/docs/docs/using/meta-containers.md new file mode 100644 index 000000000..3bc03a51e --- /dev/null +++ b/docs/docs/using/meta-containers.md @@ -0,0 +1,39 @@ +# Meta Containers + +Normally you don’t need to work with meta containers directly, but SEOmatic gives you access to them too. + +You can get the meta container for each type of meta object by doing: + +```twig +{% set jsonLdContainer = seomatic.jsonLd.container() %} +{% set linkContainer = seomatic.link.container() %} +{% set scriptContainer = seomatic.script.container() %} +{% set tagContainer = seomatic.tag.container() %} +{% set titleContainer = seomatic.title.container() %} +``` + +Then you can do things like tell an entire container to not render: + +```twig +{% set scriptContainer = seomatic.script.container() %} +{% do scriptContainer.include(false) %} +``` + +Or just: + +```twig +{% do seomatic.script.container().include(false) %} +``` + +Containers are also cached. Typically SEOmatic manages this cache for you, but should you wish to invalidate the cache manually, you can do so via: + +```twig +{% set scriptContainer = seomatic.script.container() %} +{% do scriptContainer.clearCache(true) %} +``` + +Or just: + +```twig +{% do seomatic.script.container().clearCache(true) %} +``` diff --git a/docs/docs/using/meta-variables.md b/docs/docs/using/meta-variables.md new file mode 100644 index 000000000..a276f7861 --- /dev/null +++ b/docs/docs/using/meta-variables.md @@ -0,0 +1,41 @@ +# Meta Variables + +The `seomatic.meta` variable contains all of the meta variables that control the SEO that will be rendered on the site. They are pre-populated from your settings and content in the control panel, but you can change them as you see fit. + +## General Variables + +* **`seomatic.meta.mainEntityOfPage`** – The [schema.org](http://schema.org/docs/full.html) type that represents the main entity of the page. +* **`seomatic.meta.seoTitle`** – The title that is used for the `<title>` tag. +* **`seomatic.meta.siteNamePosition`** – controls where the `seomatic.site.siteName` appears relative to the `seomatic.meta.seoTitle` in the `<title>` tag. Valid values are `before`, `after`, or `none`. +* **`seomatic.meta.seoDescription`** – The description that is used for the `<meta name="description">` tag. +* **`seomatic.meta.seoKeywords`** – The keywords that are used for the `<meta name="keywords">` tag. Note that this tag is _ignored_ by Google. +* **`seomatic.meta.seoImage`** – The image URL that is used for SEO image. +* **`seomatic.meta.seoImageWidth`** – The width of the SEO image. +* **`seomatic.meta.seoImageHeight`** – The height of the SEO image. +* **`seomatic.meta.seoImageDescription`** – A textual description of the SEO image. +* **`seomatic.meta.canonicalUrl`** – The URL used for the `<link rel="canonical">` tag. By default, this is set to `{seomatic.helper.safeCanonicalUrl()}` or `{entry.url}`/`{category.url}`/`{product.url}`, but you can change it as you see fit. This variable is also used to set the `link rel="canonical"` HTTP header. +* **`seomatic.meta.robots`** – The setting used for the `<meta name="robots">` tag that controls how bots should index your site. This variable is also used to set the `X-Robots-Tag` HTTP header. [Learn More](https://developers.google.com/search/reference/robots_meta_tag) + +## Facebook Open Graph Variables + +* **`seomatic.meta.ogType`** – The value used for the `<meta property="og:type">` tag, such as `website` or `article`. +* **`seomatic.meta.ogTitle`** – The value used for the `<meta property="og:title">` tag. This defaults to `{seomatic.meta.seoTitle}`. +* **`seomatic.meta.ogSiteNamePosition`** – controls where the `seomatic.site.siteName` appears relative to the `seomatic.meta.ogTitle` in the `<meta property="og:title">` tag. Valid values are `before`, `after`, or `none`. +* **`seomatic.meta.ogDescription`** – The value used for the `<meta property="og:description">` tag. This defaults to `{seomatic.meta.seoDescription}`. +* **`seomatic.meta.ogImage`** – The value used for the `<meta property="og:image">` tag. This defaults to `{seomatic.meta.seoImage}`. +* **`seomatic.meta.ogImageWidth`** – The width of the ogImage. This defaults to `{seomatic.meta.seoImageWidth}`. +* **`seomatic.meta.ogImageHeight`** – The height of the ogImage. This defaults to `{seomatic.meta.seoImageHeight}`. +* **`seomatic.meta.ogImageDescription`** – The value used for the `<meta property="og:image:alt">` tag. This defaults to `{seomatic.meta.seoImageDescription}`. + +## Twitter Variables + +* **`seomatic.meta.twitterCard`** – The value used for the `<meta name="twitter:card">` tag, such as `summary` or `summary_large_image`. +* **`seomatic.meta.twitterCreator`** – The value used for the `<meta name="twitter:creator">` tag. This defaults to `{seomatic.site.twitterHandle}`. +* **`seomatic.meta.twitterTitle`** – The value used for the `<meta name="twitter:title">` tag. This defaults to `{seomatic.meta.seoTitle}`. +* **`seomatic.meta.twitterSiteNamePosition`** – controls where the `seomatic.site.siteName` appears relative to the `seomatic.meta.twitterTitle` in the `<meta name="twitter:title">` tag. Valid values are `before`, `after`, or `none`. +* **`seomatic.meta.twitterDescription`** – The value used for the `<meta name="twitter:description">` tag. This defaults to `{seomatic.meta.seoDescription}`. +* **`seomatic.meta.twitterImage`** – The value used for the `<meta name="twitter:image">` tag. This defaults to `{seomatic.meta.seoImage}`. +* **`seomatic.meta.twitterImageWidth`** – The width of the Twitter image. This defaults to `{seomatic.meta.seoImageWidth}`. +* **`seomatic.meta.twitterImageHeight`** – The height of the Twitter image. This defaults to `{seomatic.meta.seoImageHeight}`. +* **`seomatic.meta.twitterImageDescription`** – The value used for the `<meta name="twitter:image:alt">` tag. This defaults to `{seomatic.meta.seoImageDescription}`. + diff --git a/docs/docs/using/pagination.md b/docs/docs/using/pagination.md new file mode 100644 index 000000000..28006a8af --- /dev/null +++ b/docs/docs/using/pagination.md @@ -0,0 +1,35 @@ +# Pagination & SEO + +If you are using paginated entries, you’ll want to add some additional markup to your templates to make Google et al aware of this. Fortunately, SEOmatic makes that easy, you simply do: + +```twig +{% do seomatic.helper.paginate(PAGEINFO) %} +``` + +The `PAGEINFO` here is the variable from the `{% paginate %}` tag as [described here](https://craftcms.com/docs/3.x/dev/tags.html#paginate), this will properly set the `canonicalUrl`, as well as adding the `<link rel='prev'>` and `<link rel='next'>` tags for you. + +A complete example (following [Craft’s {% paginate %} documentation](https://craftcms.com/docs/3.x/dev/tags.html#paginate)) might look like this: + +```twig{4} +{% paginate craft.entries() + .section('blog') + .limit(10) as pageInfo, pageEntries %} +{% do seomatic.helper.paginate(pageInfo) %} + +{% for entry in pageEntries %} + <article> + <h1>{{ entry.title }}</h1> + {{ entry.body }} + </article> +{% endfor %} + +{% if pageInfo.prevUrl %} + <a href="{{ pageInfo.prevUrl }}">Previous Page</a> +{% endif %} + +{% if pageInfo.nextUrl %} + <a href="{{ pageInfo.nextUrl }}">Next Page</a> +{% endif %} +``` + +More info: [SEO Guide to Google Webmaster Recommendations for Pagination](https://moz.com/blog/seo-guide-to-google-webmaster-recommendations-for-pagination) diff --git a/docs/docs/using/script-meta.md b/docs/docs/using/script-meta.md new file mode 100644 index 000000000..098efcc1f --- /dev/null +++ b/docs/docs/using/script-meta.md @@ -0,0 +1,19 @@ +# Script Meta Object Functions + +The `seomatic.script` functions are for working with `<script>` tags. + +* **`seomatic.script.get(META_HANDLE)`** – Returns the Script meta object of the handle `META_HANDLE` or `null` if it is not found. +* **`seomatic.script.create()`** – Creates a Script meta object from an array of key-value properties. +* **`seomatic.script.add(META_OBJECT)`** – Adds the `META_OBJECT` to the Script container to be rendered. +* **`seomatic.script.render()`** – Renders all of the Script meta objects to your template. Only necessary if you’ve disabled **Automatic Render** in [Plugin Settings](../configuring/plugin-settings.md). +* **`seomatic.script.container()`** – Returns the container that holds an array of all of the Script meta objects. + +## Script Meta Object Examples: + +Don’t include the Google Analytics script on the page: + +```twig +{% do seomatic.script.get("googleAnalytics").include(false) %} +``` + +For a complete list of the Script handles SEOmatic uses can be found in [ScriptContainer.php](https://github.com/nystudio107/craft-seomatic/blob/v4/src/seomatic-config/globalmeta/ScriptContainer.php) diff --git a/docs/docs/using/site-variables.md b/docs/docs/using/site-variables.md new file mode 100644 index 000000000..4faf38f3d --- /dev/null +++ b/docs/docs/using/site-variables.md @@ -0,0 +1,96 @@ +# Site Variables + +The `seomatic.site` variable has site-wide settings that are available on a per-site basis for multi-site setups. + +* **`seomatic.site.siteName`** – The name of the site. +* **`seomatic.site.twitterHandle`** – The site Twitter handle. +* **`seomatic.site.facebookProfileId`** – The site Facebook profile ID. +* **`seomatic.site.facebookAppId`** – The site Facebook app ID. +* **`seomatic.site.googleSiteVerification`** – The Google Site Verification code. +* **`seomatic.site.bingSiteVerification`** – The Bing Site Verification code. +* **`seomatic.site.pinterestSiteVerification`** – The Pinterest Site Verification code. +* **`seomatic.site.sameAsLinks`** – Array of links for Same As... Sites, indexed by the handle. So for example you could access the site Facebook URL via `seomatic.site.sameAsLinks["facebook"]["url"]`. These links are used to generate the `<meta property="og:same_as">` tags, and are also used in the `sameAs` property in the `mainEntityOfPage` JSON-LD. +* **`seomatic.site.siteLinksSearchTarget`** – Google Site Links search target. [Learn More](https://developers.google.com/search/docs/data-types/sitelinks-searchbox) +* **`seomatic.site.siteLinksQueryInput`** – Google Site Links query input. [Learn More](https://developers.google.com/search/docs/data-types/sitelinks-searchbox) + +## Site Identity Variables + +The `seomatic.site.identity` variable is used to create [JSON-LD Structured Data](https://developers.google.com/search/docs/guides/intro-structured-data) that _can_ appear as [Rich Snippets](https://developers.google.com/search/docs/guides/mark-up-content) on Google Search Engine Results Pages (SERP). JSON-LD Structured Data helps computers understand context and relationships, and is also read by other social media sites and apps. + +The `seomatic.site.identity` encapsulates all of the information associated with the owner of the site. + +* **`seomatic.site.identity.siteType`** – The schema.org general type. +* **`seomatic.site.identity.siteSubType`** – The schema.org sub-type. +* **`seomatic.site.identity.siteSpecificType`** – The schema.org specific type. +* **`seomatic.site.identity.computedType`** – The computed most specific schema.org type. +* **`seomatic.site.identity.genericName`** – The name of the entity that owns the site. +* **`seomatic.site.identity.genericAlternateName`** – An alternate or nickname for the entity that owns the site. +* **`seomatic.site.identity.genericDescription`** – A description of the entity that owns the site. +* **`seomatic.site.identity.genericUrl`** – A URL for the entity that owns the site. +* **`seomatic.site.identity.genericImage`** – A URL to an image or logo that represents the entity that owns the site. The image must be in JPG, PNG, or GIF format. +* **`seomatic.site.identity.genericImageWidth`** – The width of the entity image. +* **`seomatic.site.identity.genericImageHeight`** – The height of the entity image. +* **`seomatic.site.identity.genericImageIds`** – Asset ID array for the entity image. +* **`seomatic.site.identity.genericTelephone`** – The primary contact telephone number for the entity that owns the site. +* **`seomatic.site.identity.genericEmail`** – The primary contact email address for the entity that owns the site. +* **`seomatic.site.identity.genericStreetAddress`** – The street address of the entity that owns the website, for example: 123 Main Street. +* **`seomatic.site.identity.genericAddressLocality`** – Locality of the entity that owns the website, for example: Portchester. +* **`seomatic.site.identity.genericAddressRegion`** – The region of the entity that owns the website, for example: New York or NY. +* **`seomatic.site.identity.genericPostalCode`** – The postal code of the entity that owns the website, for example: 14580 +* **`seomatic.site.identity.genericAddressCountry`** – The country in which the entity that owns the site is located, for example: US +* **`seomatic.site.identity.genericGeoLatitude`** – The latitude of the location of the entity that owns the website, for example: -120.5436367 +* **`seomatic.site.identity.genericGeoLongitude`** – The longitude of the location of the entity that owns the website, for example: 80.6033588 +* **`seomatic.site.identity.personGender`** – Only for entities of the type Person, the gender of the person. +* **`seomatic.site.identity.personBirthPlace`** – Only for entities of the type Person, the place where the person was born. +* **`seomatic.site.identity.organizationDuns`** – Only for entities of the type Organization, the DUNS (Dunn & Bradstreet) number of the organization that owns the site. +* **`seomatic.site.identity.organizationFounder`** – Only for entities of the type Organization, the name of the founder of the organization. +* **`seomatic.site.identity.organizationFoundingDate`** – Only for entities of the type Organization, the date the organization/company/restaurant was founded in [ISO 8601 date format](http://schema.org/Date), for example: `2018-03-26`. +* **`seomatic.site.identity.organizationFoundingLocation`** – Only for entities of the type Organization, the location where the organization was founded. +* **`seomatic.site.identity.organizationContactPoints`** – Only for entities of the type Organization, an array of contact points for the organization. [Learn More](https://developers.google.com/search/docs/guides/enhance-site#provide-business-contact-markup) +* **`seomatic.site.identity.corporationTickerSymbol`** – Only for entities of the type Corporation, the exchange ticker symbol of the corporation. +* **`seomatic.site.identity.localBusinessPriceRange`** – Only for entities of the type LocalBusiness, the approximate price range of the goods or services offered by this local business. +* **`seomatic.site.identity.localBusinessOpeningHours`** – Only for entities of the type LocalBusiness, an array of the opening hours for this local business. [Learn More](https://developers.google.com/search/docs/appearance/structured-data/local-business) +* **`seomatic.site.identity.restaurantServesCuisine`** – Only for entities of the type Food Establishment, the primary type of cuisine that the food establishment serves. +* **`seomatic.site.identity.restaurantMenuUrl`** – Only for entities of the type Food Establishment, a URL to the food establishment’s menu. +* **`seomatic.site.identity.restaurantReservationsUrl`** – Only for entities of the type Food Establishment, a URL to the food establishment’s reservations page. + +## Site Creator Variables + +The `seomatic.site.creator` variable is used to create [JSON-LD Structured Data](https://developers.google.com/search/docs/guides/intro-structured-data) that _can_ appear as [Rich Snippets](https://developers.google.com/search/docs/guides/mark-up-content) on Google Search Engine Results Pages (SERP). JSON-LD Structured Data helps computers understand context and relationships, and is also read by other social media sites and apps. + +The `seomatic.site.creator` encapsulates all of the information associated with the creator of the site. This information is also used in the `humans.txt` page + +* **`seomatic.site.creator.siteType`** – The schema.org general type. +* **`seomatic.site.creator.siteSubType`** – The schema.org sub-type. +* **`seomatic.site.creator.siteSpecificType`** – The schema.org specific type. +* **`seomatic.site.creator.computedType`** – The computed most specific schema.org type. +* **`seomatic.site.creator.genericName`** – The name of the entity that created the site. +* **`seomatic.site.creator.genericAlternateName`** – An alternate or nickname for the entity that created the site. +* **`seomatic.site.creator.genericDescription`** – A description of the entity that created the site. +* **`seomatic.site.creator.genericUrl`** – A URL for the entity that created the site. +* **`seomatic.site.creator.genericImage`** – A URL to an image or logo that represents the entity that created the site. The image must be in JPG, PNG, or GIF format. +* **`seomatic.site.creator.genericImageWidth`** – The width of the entity image. +* **`seomatic.site.creator.genericImageHeight`** – The height of the entity image. +* **`seomatic.site.creator.genericImageIds`** – Asset ID array for the entity image. +* **`seomatic.site.creator.genericTelephone`** – The primary contact telephone number for the entity that created the site. +* **`seomatic.site.creator.genericEmail`** – The primary contact email address for the entity that created the site. +* **`seomatic.site.creator.genericStreetAddress`** – The street address of the entity that created the website, for example: 123 Main Street. +* **`seomatic.site.creator.genericAddressLocality`** – locality of the entity that created the website, for example: Portchester +* **`seomatic.site.creator.genericAddressRegion`** – The region of the entity that created the website, for example: New York or NY +* **`seomatic.site.creator.genericPostalCode`** – The postal code of the entity that created the website, for example: 14580 +* **`seomatic.site.creator.genericAddressCountry`** – The country in which the entity that created the site is located, for example: US +* **`seomatic.site.creator.genericGeoLatitude`** – The latitude of the location of the entity that created the website, for example: -120.5436367 +* **`seomatic.site.creator.genericGeoLongitude`** – The longitude of the location of the entity that created the website, for example: 80.6033588 +* **`seomatic.site.creator.personGender`** – Only for entities of the type Person, the gender of the person. +* **`seomatic.site.creator.personBirthPlace`** – Only for entities of the type Person, the place where the person was born. +* **`seomatic.site.creator.organizationDuns`** – Only for entities of the type Organization, the DUNS (Dunn & Bradstreet) number of the organization that created the site. +* **`seomatic.site.creator.organizationFounder`** – Only for entities of the type Organization, the name of the founder of the organization. +* **`seomatic.site.creator.organizationFoundingDate`** – Only for entities of the type Organization, the date the organization/company/restaurant was founded in [ISO 8601 date format](http://schema.org/Date), for example: `2018-03-26` +* **`seomatic.site.creator.organizationFoundingLocation`** – Only for entities of the type Organization, the location where the organization was founded. +* **`seomatic.site.creator.organizationContactPoints`** – Only for entities of the type Organization, an array of contact points for the organization. [Learn More](https://developers.google.com/search/docs/guides/enhance-site#provide-business-contact-markup) +* **`seomatic.site.creator.corporationTickerSymbol`** – Only for entities of the type Corporation, the exchange ticker symbol of the corporation. +* **`seomatic.site.creator.localBusinessPriceRange`** – Only for entities of the type LocalBusiness, the approximate price range of the goods or services offered by this local business. +* **`seomatic.site.creator.localBusinessOpeningHours`** – Only for entities of the type LocalBusiness, an array of the opening hours for this local business. [Learn More](https://developers.google.com/search/docs/appearance/structured-data/local-business) +* **`seomatic.site.creator.restaurantServesCuisine`** – Only for entities of the type Food Establishment, the primary type of cuisine that the food establishment serves. +* **`seomatic.site.creator.restaurantMenuUrl`** – Only for entities of the type Food Establishment, a URL to the food establishment’s menu. +* **`seomatic.site.creator.restaurantReservationsUrl`** – Only for entities of the type Food Establishment, a URL to the food establishment’s reservations page. diff --git a/docs/docs/using/tag-meta.md b/docs/docs/using/tag-meta.md new file mode 100644 index 000000000..70e898b09 --- /dev/null +++ b/docs/docs/using/tag-meta.md @@ -0,0 +1,56 @@ +# Tag Meta Object Functions + +The `seomatic.tag` functions are for working with `<meta>` tags. + +* **`seomatic.tag.get(META_HANDLE)`** – Returns the Tag meta object of the handle `META_HANDLE` or `null` if it is not found. +* **`seomatic.tag.create()`** – Creates a Tag meta object from an array of key-value properties. +* **`seomatic.tag.add(META_OBJECT)`** – Adds the `META_OBJECT` to the Tag container to be rendered. +* **`seomatic.tag.render()`** – Renders all of the Tag meta objects to your template. Only necessary if you’ve disabled **Automatic Render** in [Plugin Settings](../configuring/plugin-settings.md). +* **`seomatic.tag.container()`** – Returns the container that holds an array of all of the Tag meta objects. + +## Tag Meta Object Examples + +Change the `<meta name="twitter:title">`: + +```twig +{% do seomatic.tag.get("twitter:title").content("Hello, world") %} +``` + +Note that you can achieve the same result with: + +```twig +{% do seomatic.meta.twitterTitle("Hello, world") %} +``` + +...since the `twitterTitle` populates the `<meta name="twitter:title">` Tag meta object by default. + +Let’s say you didn’t want Google et al to index a particular page or under certain conditions. You could do this: + +```twig +{% do seomatic.tag.get("robots").content("none") %} +``` + +Note that you can achieve the same result with: + +```twig +{% do seomatic.meta.robots("none") %} +``` + +...since the `robots` populates the `<meta name="robots">` Tag meta object by default. + +You can have multiple Open Graph tags of the same time, for example `og:image`: + +```twig +{% set ogImage = seomatic.tag.get('og:image') %} +{% do ogImage.content([ + 'http://example.com/image1.jpg', + 'http://example.com/image2.jpg', +]) %} +``` + +...and it’ll generate a tag for each image: + +```html +<meta content="http://example.com/image2.jpg" property="og:image"> +<meta content="http://example.com/image1.jpg" property="og:image"> +``` diff --git a/docs/docs/using/title-meta.md b/docs/docs/using/title-meta.md new file mode 100644 index 000000000..51cd5b2af --- /dev/null +++ b/docs/docs/using/title-meta.md @@ -0,0 +1,26 @@ +# Title Meta Object Functions + +The `seomatic.title` functions are for working with a page’s `<title>` tag. + +* **`seomatic.title.get(META_HANDLE)`** – Returns the Title meta object of the handle `META_HANDLE` or `null` if it’s not found. +* **`seomatic.title.create()`** – Creates a Title meta object from an array of key-value properties. +* **`seomatic.title.add(META_OBJECT)`** – Adds the `META_OBJECT` to the Title container to be rendered. +* **`seomatic.title.render()`** – Renders Title meta object to your template. Only necessary if you’ve disabled **Automatic Render** in [Plugin Settings](../configuring/plugin-settings.md). +* **`seomatic.title.container()`** – Returns the container that holds an array with the Title meta object in it. + +## Tag Meta Object Examples + +Change the `<title>`: + +```twig +{% do seomatic.title.get("title").content("My page title") %} +``` + +Note that you can achieve the same result with: + +```twig +{% do seomatic.meta.seoTitle("My page title") %} +``` + +...since the `seoTitle` populates the `<title>` Title meta object. + diff --git a/docs/docs/what-it-does.md b/docs/docs/what-it-does.md new file mode 100644 index 000000000..b4a5bb4fc --- /dev/null +++ b/docs/docs/what-it-does.md @@ -0,0 +1,102 @@ +# What SEOmatic Does + +This page offers a more in-depth look at what SEOmatic does for you automatically, so can know where to jump in if there’s something you’d like to change. + +Once you’re familiar with the [meta cascade](./overview.md#the-meta-cascade), it’s safe to jump straight to [configuration](./configuring/) if you’d rather skip this under-the-hood tour. + +None of the information presented here is necessary for you to understand in order to use SEOmatic effectively. + +This section is provided in case you want a gestalt view of how it all works. + +## Minimal Configuration + +SEOmatic’s approach is to be as useful as possible with the least amount of required configuration. + +Because of this, and because it’s deeply integrated with Craft CMS, several things happen the moment you install it: + +- SEO preview targets are added to every section with public URLs. +- Front-end templates are injected with title, meta, link, and JSON-LD tags. +- XML sitemaps and robots.txt are served, taking care to prevent indexing for non-production environments. +- Front-end and control panel page titles include emoji to visually identify non-production environments. +- HTTP response headers are added to front-end and control panel URLs. +- An SEOmatic panel is included when Craft’s [debug toolbar](./advanced.md#debug-toolbar) is enabled. +- Page titles are prepended with 🚧 to indicate non-production environments. + +## Leveraging the Content Model + +### Healthy SEO by Default + +SEOmatic’s most important benefit is the ability to render the best metadata for a page using information that’s already available. The details can come from layered configuration, tailoring by content authors, and complete customization by developers using PHP or Twig. + +This is more complicated than relying on entry fields or elaborate templates for one very good reason: it leverages Craft’s bespoke content modeling for healthy SEO by default. This frees content authors from having to manage SEO fields so they can focus on excellent content. + +This can be a subtle thing for a small site, and a huge deal as it grows and evolves. + +### Routes → Bundles → Containers + +In order to make this possible, SEOmatic responds to a Craft request by examining its route and finding relevant SEO settings organized in bundles. + +You can think of bundles as blobs of SEO settings that may be relevant to a given route. + +SEOmatic starts with the Global SEO settings as a base, and then layers on top any Content SEO settings the correspond to a more specific Craft CMS Section. If there are any SEO Settings fields in that Section, those are then layered on top of the Content SEO settings. + +This is called the [meta cascade](./overview.md#the-meta-cascade). + +From these combined SEO settings, SEOmatic then creates containers that have objects for all of the tags, links, scripts, and other SEO metadata that it will render on your page for you. + +These containers are injected in your Twig templates so that you can modify them as you see fit before the metadata they contain is finally rendered on the page. + +You can also modify the containers via PHP in a custom module or plugin as well. + +The [title container](https://github.com/nystudio107/craft-seomatic/blob/develop/src/seomatic-config/globalmeta/TitleContainer.php), for example, describes the `<title>` HTML tag. It has simple settings for how the site name should be treated in addition to the page title. + +The [tag container](https://github.com/nystudio107/craft-seomatic/blob/develop/src/seomatic-config/globalmeta/TagContainer.php) is more complicated, detailing every expected `<meta>` tag that would commonly appear on a page. + +A series of these containers is added by default, because most sites will use them: + +- [TitleContainer](https://github.com/nystudio107/craft-seomatic/blob/develop/src/seomatic-config/globalmeta/TitleContainer.php) describes the `<title>` tag that’s rendered in the `<head>`. +- [TagContainer](https://github.com/nystudio107/craft-seomatic/blob/develop/src/seomatic-config/globalmeta/TagContainer.php) describes all the `<meta>` tags, rendered just after the `<title>` tag. +- [LinkContainer](https://github.com/nystudio107/craft-seomatic/blob/develop/src/seomatic-config/globalmeta/LinkContainer.php) describes the `<link>` tags, rendered following the `<meta>` tags. +- [ScriptContainer](https://github.com/nystudio107/craft-seomatic/blob/develop/src/seomatic-config/globalmeta/ScriptContainer.php) describes the supported tracking `<script>` tags. +- [JsonLdContainer](https://github.com/nystudio107/craft-seomatic/blob/develop/src/seomatic-config/globalmeta/JsonLdContainer.php) describes the JSON-LD `<script type="application/ld+json">` tag data that’s rendered below any `<script>` tags just before `</body>`. + +SEOmatic renders all of these for you, using settings you’ll see in each one. + +Script tags, for example, may need to appear at different positions in a page depending on what they do. `ScriptContainer.php` includes [`position`](https://github.com/nystudio107/craft-seomatic/blob/develop/src/seomatic-config/globalmeta/ScriptContainer.php#L46) and https://github.com/nystudio107/craft-seomatic/blob/develop/src/seomatic-config/globalmeta/ScriptContainer.php#L47 properties used to clarify exactly where individual scripts need to appear—and they’re already configured to ensure everything ends up in the right place. + +You can [override any of these containers](https://nystudio107.com/blog/tips-for-using-seomatic-effectively#customized-setup) if you’d like. + +You can disable automatic rendering globally in the [General Plugin Settings](./configuring/plugin-settings.md), and instead render whichever containers you’d like manually: + +```twig +{# Render the title container #} +{{ seomatic.title.container().render() }} +``` + +## Customizing with Twig and PHP + +If you choose to customize SEO details programmatically, it’s more likely that you’ll want to adjust the content itself rather than the containers that describe how it will be rendered. + +The [SEOmatic Variables](./using/index.md#seomatic-variables) section walks through common usage and examples, and anything you can do with Twig is also available via PHP. You may even want to use PHP if your adjustments should be reflected in a control panel SEO preview. + +You might set your own SEO description—used in several places—in Twig: + +```twig +{% do seomatic.meta.seoDescription( + "This is my description. There are many like it, but this one is mine." +) %} +``` + +You could do the exact same thing with PHP: + +```php +Seomatic::$seomaticVariable->meta->seoDescription = "This is my description. There are many like it, but this one is mine."; +``` + +::: tip +It’s important to consider _when_ you do this with PHP. The [AddDynamicMetaEvent](advanced.md#adddynamicmetaevent) is ideal for customizing meta items in this way. +::: + +## Tooling + +SEOmatic also includes tools like its [debug panel](advanced.md#debug-toolbar) and [self-validating objects](using/index.md#meta-object-validation) to make it easier to inspect and double-check output. diff --git a/docs/package-lock.json b/docs/package-lock.json index 565f4f188..b6ed35d41 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -225,9 +225,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.6.tgz", - "integrity": "sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.9.tgz", + "integrity": "sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -285,9 +285,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.11.tgz", - "integrity": "sha512-FnzU0LyE3ySQk7UntJO4+qIiQgI7KoODnZg5xzXIrFJlKd2P2gwHsHY4927xj9y5PJmJSzULiUCWmv7iWnNa7g==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", + "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", "cpu": [ "ppc64" ], @@ -301,9 +301,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.11.tgz", - "integrity": "sha512-5OVapq0ClabvKvQ58Bws8+wkLCV+Rxg7tUVbo9xu034Nm536QTII4YzhaFriQ7rMrorfnFKUsArD2lqKbFY4vw==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", + "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", "cpu": [ "arm" ], @@ -317,9 +317,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.11.tgz", - "integrity": "sha512-aiu7K/5JnLj//KOnOfEZ0D90obUkRzDMyqd/wNAUQ34m4YUPVhRZpnqKV9uqDGxT7cToSDnIHsGooyIczu9T+Q==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", + "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", "cpu": [ "arm64" ], @@ -333,9 +333,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.11.tgz", - "integrity": "sha512-eccxjlfGw43WYoY9QgB82SgGgDbibcqyDTlk3l3C0jOVHKxrjdc9CTwDUQd0vkvYg5um0OH+GpxYvp39r+IPOg==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", + "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", "cpu": [ "x64" ], @@ -349,9 +349,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.11.tgz", - "integrity": "sha512-ETp87DRWuSt9KdDVkqSoKoLFHYTrkyz2+65fj9nfXsaV3bMhTCjtQfw3y+um88vGRKRiF7erPrh/ZuIdLUIVxQ==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", + "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", "cpu": [ "arm64" ], @@ -365,9 +365,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.11.tgz", - "integrity": "sha512-fkFUiS6IUK9WYUO/+22omwetaSNl5/A8giXvQlcinLIjVkxwTLSktbF5f/kJMftM2MJp9+fXqZ5ezS7+SALp4g==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", + "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", "cpu": [ "x64" ], @@ -381,9 +381,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.11.tgz", - "integrity": "sha512-lhoSp5K6bxKRNdXUtHoNc5HhbXVCS8V0iZmDvyWvYq9S5WSfTIHU2UGjcGt7UeS6iEYp9eeymIl5mJBn0yiuxA==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", + "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", "cpu": [ "arm64" ], @@ -397,9 +397,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.11.tgz", - "integrity": "sha512-JkUqn44AffGXitVI6/AbQdoYAq0TEullFdqcMY/PCUZ36xJ9ZJRtQabzMA+Vi7r78+25ZIBosLTOKnUXBSi1Kw==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", + "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", "cpu": [ "x64" ], @@ -413,9 +413,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.11.tgz", - "integrity": "sha512-3CRkr9+vCV2XJbjwgzjPtO8T0SZUmRZla+UL1jw+XqHZPkPgZiyWvbDvl9rqAN8Zl7qJF0O/9ycMtjU67HN9/Q==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", + "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", "cpu": [ "arm" ], @@ -429,9 +429,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.11.tgz", - "integrity": "sha512-LneLg3ypEeveBSMuoa0kwMpCGmpu8XQUh+mL8XXwoYZ6Be2qBnVtcDI5azSvh7vioMDhoJFZzp9GWp9IWpYoUg==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", + "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", "cpu": [ "arm64" ], @@ -445,9 +445,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.11.tgz", - "integrity": "sha512-caHy++CsD8Bgq2V5CodbJjFPEiDPq8JJmBdeyZ8GWVQMjRD0sU548nNdwPNvKjVpamYYVL40AORekgfIubwHoA==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", + "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", "cpu": [ "ia32" ], @@ -461,9 +461,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.11.tgz", - "integrity": "sha512-ppZSSLVpPrwHccvC6nQVZaSHlFsvCQyjnvirnVjbKSHuE5N24Yl8F3UwYUUR1UEPaFObGD2tSvVKbvR+uT1Nrg==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", + "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", "cpu": [ "loong64" ], @@ -477,9 +477,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.11.tgz", - "integrity": "sha512-B5x9j0OgjG+v1dF2DkH34lr+7Gmv0kzX6/V0afF41FkPMMqaQ77pH7CrhWeR22aEeHKaeZVtZ6yFwlxOKPVFyg==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", + "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", "cpu": [ "mips64el" ], @@ -493,9 +493,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.11.tgz", - "integrity": "sha512-MHrZYLeCG8vXblMetWyttkdVRjQlQUb/oMgBNurVEnhj4YWOr4G5lmBfZjHYQHHN0g6yDmCAQRR8MUHldvvRDA==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", + "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", "cpu": [ "ppc64" ], @@ -509,9 +509,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.11.tgz", - "integrity": "sha512-f3DY++t94uVg141dozDu4CCUkYW+09rWtaWfnb3bqe4w5NqmZd6nPVBm+qbz7WaHZCoqXqHz5p6CM6qv3qnSSQ==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", + "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", "cpu": [ "riscv64" ], @@ -525,9 +525,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.11.tgz", - "integrity": "sha512-A5xdUoyWJHMMlcSMcPGVLzYzpcY8QP1RtYzX5/bS4dvjBGVxdhuiYyFwp7z74ocV7WDc0n1harxmpq2ePOjI0Q==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", + "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", "cpu": [ "s390x" ], @@ -541,9 +541,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.11.tgz", - "integrity": "sha512-grbyMlVCvJSfxFQUndw5mCtWs5LO1gUlwP4CDi4iJBbVpZcqLVT29FxgGuBJGSzyOxotFG4LoO5X+M1350zmPA==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", + "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", "cpu": [ "x64" ], @@ -557,9 +557,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.11.tgz", - "integrity": "sha512-13jvrQZJc3P230OhU8xgwUnDeuC/9egsjTkXN49b3GcS5BKvJqZn86aGM8W9pd14Kd+u7HuFBMVtrNGhh6fHEQ==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", + "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", "cpu": [ "x64" ], @@ -573,9 +573,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.11.tgz", - "integrity": "sha512-ysyOGZuTp6SNKPE11INDUeFVVQFrhcNDVUgSQVDzqsqX38DjhPEPATpid04LCoUr2WXhQTEZ8ct/EgJCUDpyNw==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", + "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", "cpu": [ "x64" ], @@ -589,9 +589,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.11.tgz", - "integrity": "sha512-Hf+Sad9nVwvtxy4DXCZQqLpgmRTQqyFyhT3bZ4F2XlJCjxGmRFF0Shwn9rzhOYRB61w9VMXUkxlBy56dk9JJiQ==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", + "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", "cpu": [ "x64" ], @@ -605,9 +605,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.11.tgz", - "integrity": "sha512-0P58Sbi0LctOMOQbpEOvOL44Ne0sqbS0XWHMvvrg6NE5jQ1xguCSSw9jQeUk2lfrXYsKDdOe6K+oZiwKPilYPQ==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", + "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", "cpu": [ "arm64" ], @@ -621,9 +621,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.11.tgz", - "integrity": "sha512-6YOrWS+sDJDmshdBIQU+Uoyh7pQKrdykdefC1avn76ss5c+RN6gut3LZA4E2cH5xUEp5/cA0+YxRaVtRAb0xBg==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", + "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", "cpu": [ "ia32" ], @@ -637,9 +637,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.11.tgz", - "integrity": "sha512-vfkhltrjCAb603XaFhqhAF4LGDi2M4OrCRrFusyQ+iTLQ/o60QQXxc9cZC/FFpihBI9N1Grn6SMKVJ4KP7Fuiw==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", + "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", "cpu": [ "x64" ], @@ -659,9 +659,9 @@ "dev": true }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.9.5.tgz", - "integrity": "sha512-idWaG8xeSRCfRq9KpRysDHJ/rEHBEXcHuJ82XY0yYFIWnLMjZv9vF/7DOq8djQ2n3Lk6+3qfSH8AqlmHlmi1MA==", + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.12.0.tgz", + "integrity": "sha512-+ac02NL/2TCKRrJu2wffk1kZ+RyqxVUlbjSagNgPm94frxtr+XDL12E5Ll1enWskLrtrZ2r8L3wED1orIibV/w==", "cpu": [ "arm" ], @@ -672,9 +672,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.9.5.tgz", - "integrity": "sha512-f14d7uhAMtsCGjAYwZGv6TwuS3IFaM4ZnGMUn3aCBgkcHAYErhV1Ad97WzBvS2o0aaDv4mVz+syiN0ElMyfBPg==", + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.12.0.tgz", + "integrity": "sha512-OBqcX2BMe6nvjQ0Nyp7cC90cnumt8PXmO7Dp3gfAju/6YwG0Tj74z1vKrfRz7qAv23nBcYM8BCbhrsWqO7PzQQ==", "cpu": [ "arm64" ], @@ -685,9 +685,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.9.5.tgz", - "integrity": "sha512-ndoXeLx455FffL68OIUrVr89Xu1WLzAG4n65R8roDlCoYiQcGGg6MALvs2Ap9zs7AHg8mpHtMpwC8jBBjZrT/w==", + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.12.0.tgz", + "integrity": "sha512-X64tZd8dRE/QTrBIEs63kaOBG0b5GVEd3ccoLtyf6IdXtHdh8h+I56C2yC3PtC9Ucnv0CpNFJLqKFVgCYe0lOQ==", "cpu": [ "arm64" ], @@ -698,9 +698,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.9.5.tgz", - "integrity": "sha512-UmElV1OY2m/1KEEqTlIjieKfVwRg0Zwg4PLgNf0s3glAHXBN99KLpw5A5lrSYCa1Kp63czTpVll2MAqbZYIHoA==", + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.12.0.tgz", + "integrity": "sha512-cc71KUZoVbUJmGP2cOuiZ9HSOP14AzBAThn3OU+9LcA1+IUqswJyR1cAJj3Mg55HbjZP6OLAIscbQsQLrpgTOg==", "cpu": [ "x64" ], @@ -711,9 +711,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.9.5.tgz", - "integrity": "sha512-Q0LcU61v92tQB6ae+udZvOyZ0wfpGojtAKrrpAaIqmJ7+psq4cMIhT/9lfV6UQIpeItnq/2QDROhNLo00lOD1g==", + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.12.0.tgz", + "integrity": "sha512-a6w/Y3hyyO6GlpKL2xJ4IOh/7d+APaqLYdMf86xnczU3nurFTaVN9s9jOXQg97BE4nYm/7Ga51rjec5nfRdrvA==", "cpu": [ "arm" ], @@ -724,9 +724,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.9.5.tgz", - "integrity": "sha512-dkRscpM+RrR2Ee3eOQmRWFjmV/payHEOrjyq1VZegRUa5OrZJ2MAxBNs05bZuY0YCtpqETDy1Ix4i/hRqX98cA==", + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.12.0.tgz", + "integrity": "sha512-0fZBq27b+D7Ar5CQMofVN8sggOVhEtzFUwOwPppQt0k+VR+7UHMZZY4y+64WJ06XOhBTKXtQB/Sv0NwQMXyNAA==", "cpu": [ "arm64" ], @@ -737,9 +737,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.9.5.tgz", - "integrity": "sha512-QaKFVOzzST2xzY4MAmiDmURagWLFh+zZtttuEnuNn19AiZ0T3fhPyjPPGwLNdiDT82ZE91hnfJsUiDwF9DClIQ==", + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.12.0.tgz", + "integrity": "sha512-eTvzUS3hhhlgeAv6bfigekzWZjaEX9xP9HhxB0Dvrdbkk5w/b+1Sxct2ZuDxNJKzsRStSq1EaEkVSEe7A7ipgQ==", "cpu": [ "arm64" ], @@ -750,9 +750,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.9.5.tgz", - "integrity": "sha512-HeGqmRJuyVg6/X6MpE2ur7GbymBPS8Np0S/vQFHDmocfORT+Zt76qu+69NUoxXzGqVP1pzaY6QIi0FJWLC3OPA==", + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.12.0.tgz", + "integrity": "sha512-ix+qAB9qmrCRiaO71VFfY8rkiAZJL8zQRXveS27HS+pKdjwUfEhqo2+YF2oI+H/22Xsiski+qqwIBxVewLK7sw==", "cpu": [ "riscv64" ], @@ -763,9 +763,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.9.5.tgz", - "integrity": "sha512-Dq1bqBdLaZ1Gb/l2e5/+o3B18+8TI9ANlA1SkejZqDgdU/jK/ThYaMPMJpVMMXy2uRHvGKbkz9vheVGdq3cJfA==", + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.12.0.tgz", + "integrity": "sha512-TenQhZVOtw/3qKOPa7d+QgkeM6xY0LtwzR8OplmyL5LrgTWIXpTQg2Q2ycBf8jm+SFW2Wt/DTn1gf7nFp3ssVA==", "cpu": [ "x64" ], @@ -776,9 +776,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.9.5.tgz", - "integrity": "sha512-ezyFUOwldYpj7AbkwyW9AJ203peub81CaAIVvckdkyH8EvhEIoKzaMFJj0G4qYJ5sw3BpqhFrsCc30t54HV8vg==", + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.12.0.tgz", + "integrity": "sha512-LfFdRhNnW0zdMvdCb5FNuWlls2WbbSridJvxOvYWgSBOYZtgBfW9UGNJG//rwMqTX1xQE9BAodvMH9tAusKDUw==", "cpu": [ "x64" ], @@ -789,9 +789,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.9.5.tgz", - "integrity": "sha512-aHSsMnUw+0UETB0Hlv7B/ZHOGY5bQdwMKJSzGfDfvyhnpmVxLMGnQPGNE9wgqkLUs3+gbG1Qx02S2LLfJ5GaRQ==", + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.12.0.tgz", + "integrity": "sha512-JPDxovheWNp6d7AHCgsUlkuCKvtu3RB55iNEkaQcf0ttsDU/JZF+iQnYcQJSk/7PtT4mjjVG8N1kpwnI9SLYaw==", "cpu": [ "arm64" ], @@ -802,9 +802,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.9.5.tgz", - "integrity": "sha512-AiqiLkb9KSf7Lj/o1U3SEP9Zn+5NuVKgFdRIZkvd4N0+bYrTOovVd0+LmYCPQGbocT4kvFyK+LXCDiXPBF3fyA==", + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.12.0.tgz", + "integrity": "sha512-fjtuvMWRGJn1oZacG8IPnzIV6GF2/XG+h71FKn76OYFqySXInJtseAqdprVTDTyqPxQOG9Exak5/E9Z3+EJ8ZA==", "cpu": [ "ia32" ], @@ -815,9 +815,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.9.5.tgz", - "integrity": "sha512-1q+mykKE3Vot1kaFJIDoUFv5TuW+QQVaf2FmTT9krg86pQrGStOSJJ0Zil7CFagyxDuouTepzt5Y5TVzyajOdQ==", + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.12.0.tgz", + "integrity": "sha512-ZYmr5mS2wd4Dew/JjT0Fqi2NPB/ZhZ2VvPp7SmvPZb4Y1CG/LRcS6tcRo2cYU7zLK5A7cdbhWnnWmUjoI4qapg==", "cpu": [ "x64" ], @@ -827,6 +827,21 @@ "win32" ] }, + "node_modules/@shikijs/core": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.1.5.tgz", + "integrity": "sha512-cKc5vGQ4p/4sjx48BHIO7CvLaN32vqpz5Wh7v2n+U1EezGdfX4Wms7khBctKz3iCg9yYq4sfGUc2t+JWj6EUsw==", + "dev": true + }, + "node_modules/@shikijs/transformers": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@shikijs/transformers/-/transformers-1.1.5.tgz", + "integrity": "sha512-ot6KWPmLuSN9nA9FAhttOXZIjKIy7cnwpNtI9aWmYN72RUaDz8eojRfMGUXsXXUxW/buvcvdZQAQldk7/pFpdw==", + "dev": true, + "dependencies": { + "shiki": "1.1.5" + } + }, "node_modules/@textlint-rule/textlint-rule-no-unmatched-pair": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@textlint-rule/textlint-rule-no-unmatched-pair/-/textlint-rule-no-unmatched-pair-1.0.9.tgz", @@ -1105,9 +1120,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.11.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.0.tgz", - "integrity": "sha512-o9bjXmDNcF7GbM4CNQpmi+TutCgap/K3w1JyKgxAjqx41zp9qlIAVFi0IhCNsJcXolEqLWhbFbEeL0PvYm4pcQ==", + "version": "20.11.19", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.19.tgz", + "integrity": "sha512-7xMnVEcZFu0DikYjWOlRq7NTPETrm7teqUT2WkQjrTIkEgUyyGdWsj/Zg8bEJt5TNklzbPD1X3fqfsHw3SpapQ==", "dev": true, "dependencies": { "undici-types": "~5.26.4" @@ -1126,9 +1141,9 @@ "dev": true }, "node_modules/@vitejs/plugin-vue": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.0.3.tgz", - "integrity": "sha512-b8S5dVS40rgHdDrw+DQi/xOM9ed+kSRZzfm1T74bMmBDCd8XO87NKlFYInzCtwvtWwXZvo1QxE2OSspTATWrbA==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.0.4.tgz", + "integrity": "sha512-WS3hevEszI6CEVEx28F8RjTX97k3KsrcY6kvTg7+Whm5y3oYvcqzVeGCU3hxSAn4uY2CLCkeokkGKpoctccilQ==", "dev": true, "engines": { "node": "^18.0.0 || >=20.0.0" @@ -1139,119 +1154,147 @@ } }, "node_modules/@vue/compiler-core": { - "version": "3.4.11", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.11.tgz", - "integrity": "sha512-xFD+p14L4J0DkzHMdgLiQBU5g861fuOTzag30GsfPXBpghLZOvmd22lKiBMTRRpQRpp7qxPnBlFMoeiGMM4MBg==", + "version": "3.4.19", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.19.tgz", + "integrity": "sha512-gj81785z0JNzRcU0Mq98E56e4ltO1yf8k5PQ+tV/7YHnbZkrM0fyFyuttnN8ngJZjbpofWE/m4qjKBiLl8Ju4w==", "dev": true, "dependencies": { - "@babel/parser": "^7.23.6", - "@vue/shared": "3.4.11", + "@babel/parser": "^7.23.9", + "@vue/shared": "3.4.19", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.0.2" } }, "node_modules/@vue/compiler-dom": { - "version": "3.4.11", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.11.tgz", - "integrity": "sha512-cRVLROlY7D72WK2xS91L126Dd6xHNTWDWPUBRh1Syk7+TahCk8Eown1/fSi+VX9c76sMMqEZROQSbwV0HSJnhg==", + "version": "3.4.19", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.19.tgz", + "integrity": "sha512-vm6+cogWrshjqEHTzIDCp72DKtea8Ry/QVpQRYoyTIg9k7QZDX6D8+HGURjtmatfgM8xgCFtJJaOlCaRYRK3QA==", "dev": true, "dependencies": { - "@vue/compiler-core": "3.4.11", - "@vue/shared": "3.4.11" + "@vue/compiler-core": "3.4.19", + "@vue/shared": "3.4.19" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.4.11", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.11.tgz", - "integrity": "sha512-1y5xHAD4a/AhK5+dgsZwFg145J6/rl1c8ILC7Gokca+ql51tTpduz/njCHeNmU15XiE7O62LjJFNOtSZ9vxKOQ==", + "version": "3.4.19", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.19.tgz", + "integrity": "sha512-LQ3U4SN0DlvV0xhr1lUsgLCYlwQfUfetyPxkKYu7dkfvx7g3ojrGAkw0AERLOKYXuAGnqFsEuytkdcComei3Yg==", "dev": true, "dependencies": { - "@babel/parser": "^7.23.6", - "@vue/compiler-core": "3.4.11", - "@vue/compiler-dom": "3.4.11", - "@vue/compiler-ssr": "3.4.11", - "@vue/shared": "3.4.11", + "@babel/parser": "^7.23.9", + "@vue/compiler-core": "3.4.19", + "@vue/compiler-dom": "3.4.19", + "@vue/compiler-ssr": "3.4.19", + "@vue/shared": "3.4.19", "estree-walker": "^2.0.2", - "magic-string": "^0.30.5", - "postcss": "^8.4.32", + "magic-string": "^0.30.6", + "postcss": "^8.4.33", "source-map-js": "^1.0.2" } }, "node_modules/@vue/compiler-ssr": { - "version": "3.4.11", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.11.tgz", - "integrity": "sha512-cP9Z2ArRgciYmNraqE0gQkuYInfdn66+LE4pR+16uyBiQeswcU4kEzGA+mF1MdhqYXuENpyGQsTkZapq4cy9YA==", + "version": "3.4.19", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.19.tgz", + "integrity": "sha512-P0PLKC4+u4OMJ8sinba/5Z/iDT84uMRRlrWzadgLA69opCpI1gG4N55qDSC+dedwq2fJtzmGald05LWR5TFfLw==", "dev": true, "dependencies": { - "@vue/compiler-dom": "3.4.11", - "@vue/shared": "3.4.11" + "@vue/compiler-dom": "3.4.19", + "@vue/shared": "3.4.19" } }, "node_modules/@vue/devtools-api": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.5.1.tgz", - "integrity": "sha512-+KpckaAQyfbvshdDW5xQylLni1asvNSGme1JFs8I1+/H5pHEhqUKMEQD/qn3Nx5+/nycBq11qAEi8lk+LXI2dA==", - "dev": true + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.0.15.tgz", + "integrity": "sha512-kgEYWosDyWpS1vFSuJNNWUnHkP+VkL3Y+9mw+rf7ex41SwbYL/WdC3KXqAtjiSrEs7r/FrHmUTh0BkINJPFkbA==", + "dev": true, + "dependencies": { + "@vue/devtools-kit": "^7.0.15" + } + }, + "node_modules/@vue/devtools-kit": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.0.15.tgz", + "integrity": "sha512-dT7OeCe1LUCIhHIb/yRR6Hn+XHh73r1o78onqCrxEKHdoZwBItiIeVnmJZPEUDFstIxfs+tJL231mySk3laTow==", + "dev": true, + "dependencies": { + "@vue/devtools-shared": "^7.0.15", + "hookable": "^5.5.3", + "mitt": "^3.0.1", + "perfect-debounce": "^1.0.0", + "speakingurl": "^14.0.1" + }, + "peerDependencies": { + "vue": "^3.0.0" + } + }, + "node_modules/@vue/devtools-shared": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.0.15.tgz", + "integrity": "sha512-fpfvMVvS7aDgO7x2JPFiTQ1MHcCc63/bE7yTgs278gMBybuO9b3hdiZ/k0Pw1rN+RefaU9yQiFA+5CCFc1D+6w==", + "dev": true, + "dependencies": { + "rfdc": "^1.3.1" + } }, "node_modules/@vue/reactivity": { - "version": "3.4.11", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.11.tgz", - "integrity": "sha512-KscADwKpSynT3S2iJEX8EfPqc9kPFR261sHIQnDh1xhOBf8qd4ait9tEgLt1/uVxyrAgFj/TNGmjDkcsytyA8w==", + "version": "3.4.19", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.19.tgz", + "integrity": "sha512-+VcwrQvLZgEclGZRHx4O2XhyEEcKaBi50WbxdVItEezUf4fqRh838Ix6amWTdX0CNb/b6t3Gkz3eOebfcSt+UA==", "dev": true, "dependencies": { - "@vue/shared": "3.4.11" + "@vue/shared": "3.4.19" } }, "node_modules/@vue/runtime-core": { - "version": "3.4.11", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.11.tgz", - "integrity": "sha512-wduRf9w1OtSORFs5KVpKEQ1bRwW5D9/E8mB0I4m0f5Wrd53OZridzWWVZaowSKNMXXIF5Y/lYFP9GOM/IL5i2g==", + "version": "3.4.19", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.19.tgz", + "integrity": "sha512-/Z3tFwOrerJB/oyutmJGoYbuoadphDcJAd5jOuJE86THNZji9pYjZroQ2NFsZkTxOq0GJbb+s2kxTYToDiyZzw==", "dev": true, "dependencies": { - "@vue/reactivity": "3.4.11", - "@vue/shared": "3.4.11" + "@vue/reactivity": "3.4.19", + "@vue/shared": "3.4.19" } }, "node_modules/@vue/runtime-dom": { - "version": "3.4.11", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.11.tgz", - "integrity": "sha512-pWlCTzo6Ad3pSBjzgcZ9maPaz+N/SngLOMfkSKIx7rIWJgcHBoFp4GAbhnkR3jxT4BqIvti6EH3aNSC02VtgOg==", + "version": "3.4.19", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.19.tgz", + "integrity": "sha512-IyZzIDqfNCF0OyZOauL+F4yzjMPN2rPd8nhqPP2N1lBn3kYqJpPHHru+83Rkvo2lHz5mW+rEeIMEF9qY3PB94g==", "dev": true, "dependencies": { - "@vue/runtime-core": "3.4.11", - "@vue/shared": "3.4.11", + "@vue/runtime-core": "3.4.19", + "@vue/shared": "3.4.19", "csstype": "^3.1.3" } }, "node_modules/@vue/server-renderer": { - "version": "3.4.11", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.11.tgz", - "integrity": "sha512-19rLK9N0yNNzQ83ieyoO9ZT/iBt0S8IkxQ4eVmnqPLCbZgSRMm7GRXnjTFvo0n5vTVVeyaYosBzZ2559L/rP+w==", + "version": "3.4.19", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.19.tgz", + "integrity": "sha512-eAj2p0c429RZyyhtMRnttjcSToch+kTWxFPHlzGMkR28ZbF1PDlTcmGmlDxccBuqNd9iOQ7xPRPAGgPVj+YpQw==", "dev": true, "dependencies": { - "@vue/compiler-ssr": "3.4.11", - "@vue/shared": "3.4.11" + "@vue/compiler-ssr": "3.4.19", + "@vue/shared": "3.4.19" }, "peerDependencies": { - "vue": "3.4.11" + "vue": "3.4.19" } }, "node_modules/@vue/shared": { - "version": "3.4.11", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.11.tgz", - "integrity": "sha512-BtC+vE8kHf/jZoyJnTFd0PmY8NejyUeUkshXm8LriHs8KmQUmcZXIbrifjA3WDmvzg7C8D6gBSvdl49pOfU2lQ==", + "version": "3.4.19", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.19.tgz", + "integrity": "sha512-/KliRRHMF6LoiThEy+4c1Z4KB/gbPrGjWwJR+crg2otgrf/egKzRaCPvJ51S5oetgsgXLfc4Rm5ZgrKHZrtMSw==", "dev": true }, "node_modules/@vueuse/core": { - "version": "10.7.1", - "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.7.1.tgz", - "integrity": "sha512-74mWHlaesJSWGp1ihg76vAnfVq9NTv1YT0SYhAQ6zwFNdBkkP+CKKJmVOEHcdSnLXCXYiL5e7MaewblfiYLP7g==", + "version": "10.7.2", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.7.2.tgz", + "integrity": "sha512-AOyAL2rK0By62Hm+iqQn6Rbu8bfmbgaIMXcE3TSr7BdQ42wnSFlwIdPjInO62onYsEMK/yDMU8C6oGfDAtZ2qQ==", "dev": true, "dependencies": { "@types/web-bluetooth": "^0.0.20", - "@vueuse/metadata": "10.7.1", - "@vueuse/shared": "10.7.1", + "@vueuse/metadata": "10.7.2", + "@vueuse/shared": "10.7.2", "vue-demi": ">=0.14.6" }, "funding": { @@ -1259,9 +1302,9 @@ } }, "node_modules/@vueuse/core/node_modules/vue-demi": { - "version": "0.14.6", - "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.6.tgz", - "integrity": "sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==", + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.7.tgz", + "integrity": "sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==", "dev": true, "hasInstallScript": true, "bin": { @@ -1285,13 +1328,13 @@ } }, "node_modules/@vueuse/integrations": { - "version": "10.7.1", - "resolved": "https://registry.npmjs.org/@vueuse/integrations/-/integrations-10.7.1.tgz", - "integrity": "sha512-cKo5LEeKVHdBRBtMTOrDPdR0YNtrmN9IBfdcnY2P3m5LHVrsD0xiHUtAH1WKjHQRIErZG6rJUa6GA4tWZt89Og==", + "version": "10.7.2", + "resolved": "https://registry.npmjs.org/@vueuse/integrations/-/integrations-10.7.2.tgz", + "integrity": "sha512-+u3RLPFedjASs5EKPc69Ge49WNgqeMfSxFn+qrQTzblPXZg6+EFzhjarS5edj2qAf6xQ93f95TUxRwKStXj/sQ==", "dev": true, "dependencies": { - "@vueuse/core": "10.7.1", - "@vueuse/shared": "10.7.1", + "@vueuse/core": "10.7.2", + "@vueuse/shared": "10.7.2", "vue-demi": ">=0.14.6" }, "funding": { @@ -1351,9 +1394,9 @@ } }, "node_modules/@vueuse/integrations/node_modules/vue-demi": { - "version": "0.14.6", - "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.6.tgz", - "integrity": "sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==", + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.7.tgz", + "integrity": "sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==", "dev": true, "hasInstallScript": true, "bin": { @@ -1377,18 +1420,18 @@ } }, "node_modules/@vueuse/metadata": { - "version": "10.7.1", - "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-10.7.1.tgz", - "integrity": "sha512-jX8MbX5UX067DYVsbtrmKn6eG6KMcXxLRLlurGkZku5ZYT3vxgBjui2zajvUZ18QLIjrgBkFRsu7CqTAg18QFw==", + "version": "10.7.2", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-10.7.2.tgz", + "integrity": "sha512-kCWPb4J2KGrwLtn1eJwaJD742u1k5h6v/St5wFe8Quih90+k2a0JP8BS4Zp34XUuJqS2AxFYMb1wjUL8HfhWsQ==", "dev": true, "funding": { "url": "https://github.com/sponsors/antfu" } }, "node_modules/@vueuse/shared": { - "version": "10.7.1", - "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-10.7.1.tgz", - "integrity": "sha512-v0jbRR31LSgRY/C5i5X279A/WQjD6/JsMzGa+eqt658oJ75IvQXAeONmwvEMrvJQKnRElq/frzBR7fhmWY5uLw==", + "version": "10.7.2", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-10.7.2.tgz", + "integrity": "sha512-qFbXoxS44pi2FkgFjPvF4h7c9oMDutpyBdcJdMYIMg9XyXli2meFMuaKn+UMgsClo//Th6+beeCgqweT/79BVA==", "dev": true, "dependencies": { "vue-demi": ">=0.14.6" @@ -1398,9 +1441,9 @@ } }, "node_modules/@vueuse/shared/node_modules/vue-demi": { - "version": "0.14.6", - "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.6.tgz", - "integrity": "sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==", + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.7.tgz", + "integrity": "sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==", "dev": true, "hasInstallScript": true, "bin": { @@ -1548,14 +1591,19 @@ "dev": true }, "node_modules/call-bind": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", - "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "dev": true, "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.1", - "set-function-length": "^1.1.1" + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -1710,17 +1758,20 @@ "dev": true }, "node_modules/define-data-property": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", - "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dev": true, "dependencies": { - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/define-properties": { @@ -1818,10 +1869,31 @@ "is-arrayish": "^0.2.1" } }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/esbuild": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.11.tgz", - "integrity": "sha512-HJ96Hev2hX/6i5cDVwcqiJBBtuo9+FeIJOtZ9W1kA5M6AMJRHUZlpYZ1/SbEwtO0ioNAW8rUooVpC/WehY2SfA==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", + "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", "dev": true, "hasInstallScript": true, "bin": { @@ -1831,29 +1903,29 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.19.11", - "@esbuild/android-arm": "0.19.11", - "@esbuild/android-arm64": "0.19.11", - "@esbuild/android-x64": "0.19.11", - "@esbuild/darwin-arm64": "0.19.11", - "@esbuild/darwin-x64": "0.19.11", - "@esbuild/freebsd-arm64": "0.19.11", - "@esbuild/freebsd-x64": "0.19.11", - "@esbuild/linux-arm": "0.19.11", - "@esbuild/linux-arm64": "0.19.11", - "@esbuild/linux-ia32": "0.19.11", - "@esbuild/linux-loong64": "0.19.11", - "@esbuild/linux-mips64el": "0.19.11", - "@esbuild/linux-ppc64": "0.19.11", - "@esbuild/linux-riscv64": "0.19.11", - "@esbuild/linux-s390x": "0.19.11", - "@esbuild/linux-x64": "0.19.11", - "@esbuild/netbsd-x64": "0.19.11", - "@esbuild/openbsd-x64": "0.19.11", - "@esbuild/sunos-x64": "0.19.11", - "@esbuild/win32-arm64": "0.19.11", - "@esbuild/win32-ia32": "0.19.11", - "@esbuild/win32-x64": "0.19.11" + "@esbuild/aix-ppc64": "0.19.12", + "@esbuild/android-arm": "0.19.12", + "@esbuild/android-arm64": "0.19.12", + "@esbuild/android-x64": "0.19.12", + "@esbuild/darwin-arm64": "0.19.12", + "@esbuild/darwin-x64": "0.19.12", + "@esbuild/freebsd-arm64": "0.19.12", + "@esbuild/freebsd-x64": "0.19.12", + "@esbuild/linux-arm": "0.19.12", + "@esbuild/linux-arm64": "0.19.12", + "@esbuild/linux-ia32": "0.19.12", + "@esbuild/linux-loong64": "0.19.12", + "@esbuild/linux-mips64el": "0.19.12", + "@esbuild/linux-ppc64": "0.19.12", + "@esbuild/linux-riscv64": "0.19.12", + "@esbuild/linux-s390x": "0.19.12", + "@esbuild/linux-x64": "0.19.12", + "@esbuild/netbsd-x64": "0.19.12", + "@esbuild/openbsd-x64": "0.19.12", + "@esbuild/sunos-x64": "0.19.12", + "@esbuild/win32-arm64": "0.19.12", + "@esbuild/win32-ia32": "0.19.12", + "@esbuild/win32-x64": "0.19.12" } }, "node_modules/escape-string-regexp": { @@ -2016,16 +2088,20 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", - "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "dev": true, "dependencies": { + "es-errors": "^1.3.0", "function-bind": "^1.1.2", "has-proto": "^1.0.1", "has-symbols": "^1.0.3", "hasown": "^2.0.0" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -2087,12 +2163,12 @@ } }, "node_modules/has-property-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", - "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dev": true, "dependencies": { - "get-intrinsic": "^1.2.2" + "es-define-property": "^1.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -2123,9 +2199,9 @@ } }, "node_modules/hasown": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", - "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz", + "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==", "dev": true, "dependencies": { "function-bind": "^1.1.2" @@ -2134,6 +2210,12 @@ "node": ">= 0.4" } }, + "node_modules/hookable": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz", + "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", + "dev": true + }, "node_modules/hosted-git-info": { "version": "2.8.9", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", @@ -2362,9 +2444,9 @@ } }, "node_modules/magic-string": { - "version": "0.30.5", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz", - "integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==", + "version": "0.30.7", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.7.tgz", + "integrity": "sha512-8vBuFF/I/+OSLRmdf2wwFCJCz+nSn0m6DPvGH1fS/KiQoSaR+sETbov0eIk9KhEKy8CYqIkIAnbohxT/4H0kuA==", "dev": true, "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" @@ -2731,6 +2813,12 @@ "integrity": "sha512-4QT2u/8X7PccbiHUcsZeEZrt3jGIVEpfcQ1RU01wDHKHVNtNhaP+0Xmsg7YPxD7OCc8bO802BTEWeGPvAXBwuw==", "dev": true }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "dev": true + }, "node_modules/mkdirp": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", @@ -2957,6 +3045,12 @@ "node": ">=0.10.0" } }, + "node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "dev": true + }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -3000,9 +3094,9 @@ "dev": true }, "node_modules/postcss": { - "version": "8.4.33", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz", - "integrity": "sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==", + "version": "8.4.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", + "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==", "dev": true, "funding": [ { @@ -3028,9 +3122,9 @@ } }, "node_modules/preact": { - "version": "10.19.3", - "resolved": "https://registry.npmjs.org/preact/-/preact-10.19.3.tgz", - "integrity": "sha512-nHHTeFVBTHRGxJXKkKu5hT8C/YWBkPso4/Gad6xuj5dbptt9iF9NZr9pHbPhBrnT2klheu7mHTxTZ/LjwJiEiQ==", + "version": "10.19.5", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.19.5.tgz", + "integrity": "sha512-OPELkDmSVbKjbFqF9tgvOowiiQ9TmsJljIzXRyNE8nGiis94pwv1siF78rQkAP1Q1738Ce6pellRg/Ns/CtHqQ==", "dev": true, "funding": { "type": "opencollective", @@ -3288,6 +3382,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/rfdc": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.1.tgz", + "integrity": "sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==", + "dev": true + }, "node_modules/rimraf": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", @@ -3301,9 +3401,9 @@ } }, "node_modules/rollup": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.9.5.tgz", - "integrity": "sha512-E4vQW0H/mbNMw2yLSqJyjtkHY9dslf/p0zuT1xehNRqUTBOFMqEjguDvqhXr7N7r/4ttb2jr4T41d3dncmIgbQ==", + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.12.0.tgz", + "integrity": "sha512-wz66wn4t1OHIJw3+XU7mJJQV/2NAfw5OAk6G6Hoo3zcvz/XOfQ52Vgi+AN4Uxoxi0KBBwk2g8zPrTDA4btSB/Q==", "dev": true, "dependencies": { "@types/estree": "1.0.5" @@ -3316,19 +3416,19 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.9.5", - "@rollup/rollup-android-arm64": "4.9.5", - "@rollup/rollup-darwin-arm64": "4.9.5", - "@rollup/rollup-darwin-x64": "4.9.5", - "@rollup/rollup-linux-arm-gnueabihf": "4.9.5", - "@rollup/rollup-linux-arm64-gnu": "4.9.5", - "@rollup/rollup-linux-arm64-musl": "4.9.5", - "@rollup/rollup-linux-riscv64-gnu": "4.9.5", - "@rollup/rollup-linux-x64-gnu": "4.9.5", - "@rollup/rollup-linux-x64-musl": "4.9.5", - "@rollup/rollup-win32-arm64-msvc": "4.9.5", - "@rollup/rollup-win32-ia32-msvc": "4.9.5", - "@rollup/rollup-win32-x64-msvc": "4.9.5", + "@rollup/rollup-android-arm-eabi": "4.12.0", + "@rollup/rollup-android-arm64": "4.12.0", + "@rollup/rollup-darwin-arm64": "4.12.0", + "@rollup/rollup-darwin-x64": "4.12.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.12.0", + "@rollup/rollup-linux-arm64-gnu": "4.12.0", + "@rollup/rollup-linux-arm64-musl": "4.12.0", + "@rollup/rollup-linux-riscv64-gnu": "4.12.0", + "@rollup/rollup-linux-x64-gnu": "4.12.0", + "@rollup/rollup-linux-x64-musl": "4.12.0", + "@rollup/rollup-win32-arm64-msvc": "4.12.0", + "@rollup/rollup-win32-ia32-msvc": "4.12.0", + "@rollup/rollup-win32-x64-msvc": "4.12.0", "fsevents": "~2.3.2" } }, @@ -3384,42 +3484,29 @@ } }, "node_modules/set-function-length": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", - "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz", + "integrity": "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==", "dev": true, "dependencies": { - "define-data-property": "^1.1.1", - "get-intrinsic": "^1.2.1", + "define-data-property": "^1.1.2", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.3", "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" + "has-property-descriptors": "^1.0.1" }, "engines": { "node": ">= 0.4" } }, - "node_modules/shikiji": { - "version": "0.9.19", - "resolved": "https://registry.npmjs.org/shikiji/-/shikiji-0.9.19.tgz", - "integrity": "sha512-Kw2NHWktdcdypCj1GkKpXH4o6Vxz8B8TykPlPuLHOGSV8VkhoCLcFOH4k19K4LXAQYRQmxg+0X/eM+m2sLhAkg==", - "dev": true, - "dependencies": { - "shikiji-core": "0.9.19" - } - }, - "node_modules/shikiji-core": { - "version": "0.9.19", - "resolved": "https://registry.npmjs.org/shikiji-core/-/shikiji-core-0.9.19.tgz", - "integrity": "sha512-AFJu/vcNT21t0e6YrfadZ+9q86gvPum6iywRyt1OtIPjPFe25RQnYJyxHQPMLKCCWA992TPxmEmbNcOZCAJclw==", - "dev": true - }, - "node_modules/shikiji-transformers": { - "version": "0.9.19", - "resolved": "https://registry.npmjs.org/shikiji-transformers/-/shikiji-transformers-0.9.19.tgz", - "integrity": "sha512-lGLI7Z8frQrIBbhZ74/eiJtxMoCQRbpaHEB+gcfvdIy+ZFaAtXncJGnc52932/UET+Y4GyKtwwC/vjWUCp+c/Q==", + "node_modules/shiki": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.1.5.tgz", + "integrity": "sha512-754GuKIwkUdT810Xm8btuyNQPL+q3PqOkwGW/VlmAWyMYp+HbvvDt69sWXO1sm5aeczBJQjmQTTMR4GkKQNQPw==", "dev": true, "dependencies": { - "shikiji": "0.9.19" + "@shikijs/core": "1.1.5" } }, "node_modules/slice-ansi": { @@ -3459,9 +3546,9 @@ } }, "node_modules/spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", "dev": true }, "node_modules/spdx-expression-parse": { @@ -3475,11 +3562,20 @@ } }, "node_modules/spdx-license-ids": { - "version": "3.0.16", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.16.tgz", - "integrity": "sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw==", + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz", + "integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==", "dev": true }, + "node_modules/speakingurl": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz", + "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/split-lines": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/split-lines/-/split-lines-2.1.0.tgz", @@ -4180,13 +4276,13 @@ } }, "node_modules/vite": { - "version": "5.0.11", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.0.11.tgz", - "integrity": "sha512-XBMnDjZcNAw/G1gEiskiM1v6yzM4GE5aMGvhWTlHAYYhxb7S3/V1s3m2LDHa8Vh6yIWYYB0iJwsEaS523c4oYA==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.3.tgz", + "integrity": "sha512-UfmUD36DKkqhi/F75RrxvPpry+9+tTkrXfMNZD+SboZqBCMsxKtO52XeGzzuh7ioz+Eo/SYDBbdb0Z7vgcDJew==", "dev": true, "dependencies": { "esbuild": "^0.19.3", - "postcss": "^8.4.32", + "postcss": "^8.4.35", "rollup": "^4.2.0" }, "bin": { @@ -4235,33 +4331,33 @@ } }, "node_modules/vitepress": { - "version": "1.0.0-rc.36", - "resolved": "https://registry.npmjs.org/vitepress/-/vitepress-1.0.0-rc.36.tgz", - "integrity": "sha512-2z4dpM9PplN/yvTifhavOIAazlCR6OJ5PvLoRbc+7LdcFeIlCsuDGENLX4HjMW18jQZF5/j7++PNqdBfeazxUA==", + "version": "1.0.0-rc.43", + "resolved": "https://registry.npmjs.org/vitepress/-/vitepress-1.0.0-rc.43.tgz", + "integrity": "sha512-XZ9xaN76/LxCBqvk6U+3ne3T60JOavdOlk+FMQBlXYK/9pyyKGfjnEra4yKYvOdZdStoTg8VXTAj4wcsCTlJaQ==", "dev": true, "dependencies": { "@docsearch/css": "^3.5.2", "@docsearch/js": "^3.5.2", + "@shikijs/core": "^1.1.3", + "@shikijs/transformers": "^1.1.3", "@types/markdown-it": "^13.0.7", - "@vitejs/plugin-vue": "^5.0.2", - "@vue/devtools-api": "^6.5.1", - "@vueuse/core": "^10.7.1", - "@vueuse/integrations": "^10.7.1", + "@vitejs/plugin-vue": "^5.0.4", + "@vue/devtools-api": "^7.0.14", + "@vueuse/core": "^10.7.2", + "@vueuse/integrations": "^10.7.2", "focus-trap": "^7.5.4", "mark.js": "8.11.1", "minisearch": "^6.3.0", - "shikiji": "^0.9.17", - "shikiji-core": "^0.9.17", - "shikiji-transformers": "^0.9.17", - "vite": "^5.0.11", - "vue": "^3.4.5" + "shiki": "^1.1.3", + "vite": "^5.1.3", + "vue": "^3.4.19" }, "bin": { "vitepress": "bin/vitepress.js" }, "peerDependencies": { "markdown-it-mathjax3": "^4.3.2", - "postcss": "^8.4.33" + "postcss": "^8.4.35" }, "peerDependenciesMeta": { "markdown-it-mathjax3": { @@ -4273,16 +4369,16 @@ } }, "node_modules/vue": { - "version": "3.4.11", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.11.tgz", - "integrity": "sha512-iaA98z14ZrrVJlclpHX/HCNeacbMOLdX5foYN7/vt4cHFhDkBRzojjbLQZ2UDRAeNV1v4V5I21+QpdCXWlpG5Q==", + "version": "3.4.19", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.19.tgz", + "integrity": "sha512-W/7Fc9KUkajFU8dBeDluM4sRGc/aa4YJnOYck8dkjgZoXtVsn3OeTGni66FV1l3+nvPA7VBFYtPioaGKUmEADw==", "dev": true, "dependencies": { - "@vue/compiler-dom": "3.4.11", - "@vue/compiler-sfc": "3.4.11", - "@vue/runtime-dom": "3.4.11", - "@vue/server-renderer": "3.4.11", - "@vue/shared": "3.4.11" + "@vue/compiler-dom": "3.4.19", + "@vue/compiler-sfc": "3.4.19", + "@vue/runtime-dom": "3.4.19", + "@vue/server-renderer": "3.4.19", + "@vue/shared": "3.4.19" }, "peerDependencies": { "typescript": "*"