536 lines
33 KiB
JavaScript
536 lines
33 KiB
JavaScript
/**
|
|
* # Spec
|
|
*
|
|
* This file contains details of the theme API spec, in a format that can be used by GScan
|
|
*/
|
|
const oneLineTrim = require('common-tags/lib/oneLineTrim');
|
|
const docsBaseUrl = `https://ghost.org/docs/themes/`;
|
|
let knownHelpers, templates, rules, ruleNext; // eslint-disable-line no-unused-vars
|
|
|
|
knownHelpers = [
|
|
// Ghost
|
|
'foreach', 'has', 'is', 'get', 'content', 'excerpt', 'title', 'tags', 'author', 'authors', 'img_url', 'navigation', 'pagination',
|
|
'page_url', 'url', 'date', 'plural', 'encode', 'asset', 'body_class', 'post_class', 'ghost_head', 'ghost_foot',
|
|
'lang', 'meta_title', 'meta_description', 'next_post', 'prev_post', 't', 'twitter_url', 'facebook_url', 'reading_time',
|
|
// Ghost apps
|
|
'input_email', 'input_password', 'amp_components', 'amp_content', 'amp_ghost_head', 'subscribe_form',
|
|
// Handlebars and express handlebars
|
|
'log', 'if', 'unless', 'with', 'block', 'contentFor', 'each', 'lookup'
|
|
// Registering these will break template compile checks
|
|
// 'blockHelperMissing', 'helperMissing',
|
|
];
|
|
|
|
templates = [
|
|
{
|
|
name: 'Page template',
|
|
pattern: /^page\.hbs$/,
|
|
version: '>=0.4.0'
|
|
},
|
|
{
|
|
name: 'Error template',
|
|
pattern: /^error\.hbs$/,
|
|
version: '>=0.4.0'
|
|
},
|
|
{
|
|
name: 'Tag template',
|
|
pattern: /^tag\.hbs$/,
|
|
version: '>=0.4.2'
|
|
},
|
|
{
|
|
name: 'Custom page template',
|
|
pattern: /^page-([a-z0-9\-_]+)\.hbs$/,
|
|
version: '>=0.4.2'
|
|
},
|
|
{
|
|
name: 'Author template',
|
|
pattern: /^author\.hbs$/,
|
|
version: '>=0.5.0'
|
|
},
|
|
{
|
|
name: 'Home template',
|
|
pattern: /^home\.hbs$/,
|
|
version: '>=0.5.0'
|
|
},
|
|
{
|
|
name: 'Custom tag template',
|
|
pattern: /^tag-([a-z0-9\-_]+)\.hbs$/,
|
|
version: '>=0.5.0'
|
|
},
|
|
{
|
|
name: 'Custom author template',
|
|
pattern: /^author-([a-z0-9\-_]+)\.hbs$/,
|
|
version: '>=0.6.3'
|
|
},
|
|
{
|
|
name: 'Private template',
|
|
pattern: /^private\.hbs$/,
|
|
version: '>=0.6.3'
|
|
},
|
|
{
|
|
name: 'Custom post template',
|
|
pattern: /^post-([a-z0-9\-_]+)\.hbs$/,
|
|
version: '>=0.7.3'
|
|
}
|
|
];
|
|
|
|
rules = {
|
|
'GS001-DEPR-PURL': {
|
|
level: 'error',
|
|
rule: 'Replace <code>{{pageUrl}}</code> with <code>{{page_url}}</code>',
|
|
fatal: true,
|
|
details: oneLineTrim`The helper <code>{{pageUrl}}</code> was replaced with <code>{{page_url}}</code>.<br>
|
|
Find more information about the <code>{{page_url}}</code> helper <a href="${docsBaseUrl}helpers/pagination/" target=_blank>here</a>.`,
|
|
regex: /{{\s*?pageUrl\b[\w\s='"]*?}}/ig,
|
|
helper: '{{pageUrl}}'
|
|
},
|
|
'GS001-DEPR-MD': {
|
|
level: 'error',
|
|
rule: 'The usage of <code>{{meta_description}}</code> in HTML <code>head</code> is no longer required',
|
|
details: oneLineTrim`The usage of <code>{{meta_description}}</code> in the HTML <code>head</code> tag is no longer required because Ghost outputs this for you automatically in <code>{{ghost_head}}</code>.<br>
|
|
Check out the documentation for <code>{{meta_description}}</code> <a href="${docsBaseUrl}helpers/meta_data/" target=_blank>here</a>.<br>
|
|
To see, what else is rendered with the <code>{{ghost_head}}</code> helper, look <a href="${docsBaseUrl}helpers/ghost_head_foot/" target=_blank>here</a>.`,
|
|
regex: /<meta name=("|')description("|') content=("|'){{meta_description}}("|')/ig,
|
|
helper: '{{meta_description}}'
|
|
},
|
|
'GS001-DEPR-IMG': {
|
|
level: 'error',
|
|
rule: 'The <code>{{image}}</code> helper was replaced with the <code>{{img_url}}</code> helper.</code>.',
|
|
fatal: true,
|
|
details: oneLineTrim`The <code>{{image}}</code> helper was replaced with the <code>{{img_url}}</code> helper.<br>
|
|
Depending on the context of the <code>{{img_url}}</code> helper you would need to use e. g. <br><br><code>{{#post}}<br> {{img_url feature_image}}<br>{{/post}}</code><br><br>to render the feature image of the blog post.<br>
|
|
<br><b>If you are using <code>{{if image}}</code></b>, then you have to replace it with e.g. <code>{{if feature_image}}.</code>
|
|
<br><br>Find more information about the <code>{{img_url}}</code> helper <a href="${docsBaseUrl}helpers/img_url/" target=_blank>here</a> and
|
|
read more about Ghost's usage of contexts <a href="${docsBaseUrl}contexts/" target=_blank>here</a>.`,
|
|
regex: /{{\s*?image\b[\w\s='"]*?}}/g,
|
|
helper: '{{image}}'
|
|
},
|
|
'GS001-DEPR-COV': {
|
|
level: 'error',
|
|
rule: 'Replace <code>{{cover}}</code> with <code>{{cover_image}}</code>',
|
|
fatal: true,
|
|
details: oneLineTrim`The <code>cover</code> attribute was replaced with <code>cover_image</code>. To render the cover image in author context, you need to use<br><br>
|
|
<code>{{#author}}<br> {{cover_image}}<br>{{/author}}</code><br><br>
|
|
See the object attributes of <code>author</code> <a href="${docsBaseUrl}contexts/author/#author-object-attributes" target=_blank>here</a>.<br>
|
|
To render the cover image of your blog, just use <code>{{@site.cover_image}}</code>. See <a href="${docsBaseUrl}helpers/site/" target=_blank>here</a>.`,
|
|
regex: /{{\s*?cover\s*?}}/g,
|
|
helper: '{{cover}}'
|
|
},
|
|
'GS001-DEPR-AIMG': {
|
|
level: 'error',
|
|
rule: 'Replace <code>{{author.image}}</code> with <code>{{author.profile_image}}</code>',
|
|
fatal: true,
|
|
details: oneLineTrim`The <code>image</code> attribute in author context was replaced with <code>profile_image</code>.<br>
|
|
Instead of <code>{{author.image}}</code> you need to use <code>{{author.profile_image}}</code>.<br>
|
|
See the object attributes of <code>author</code> <a href="${docsBaseUrl}contexts/author/#author-object-attributes" target=_blank>here</a>.`,
|
|
regex: /{{\s*?author\.image\s*?}}/g,
|
|
helper: '{{author.image}}'
|
|
},
|
|
'GS001-DEPR-PIMG': {
|
|
level: 'error',
|
|
rule: 'Replace <code>{{post.image}}</code> with <code>{{post.feature_image}}</code>',
|
|
fatal: true,
|
|
details: oneLineTrim`The <code>image</code> attribute in post context was replaced with <code>feature_image</code>.<br>
|
|
Instead of <code>{{post.image}}</code> you need to use <code>{{post.feature_image}}</code>.<br>
|
|
See the object attributes of <code>post</code> <a href="${docsBaseUrl}contexts/post/#post-object-attributes" target=_blank>here</a>.`,
|
|
regex: /{{\s*?post\.image\s*?}}/g,
|
|
helper: '{{post.image}}'
|
|
},
|
|
'GS001-DEPR-BC': {
|
|
level: 'error',
|
|
rule: 'Replace <code>{{@blog.cover}}</code> with <code>{{@site.cover_image}}</code>',
|
|
fatal: true,
|
|
details: oneLineTrim`The <code>cover</code> attribute was replaced with <code>cover_image</code>.<br>
|
|
Instead of <code>{{@blog.cover}}</code> you need to use <code>{{@site.cover_image}}</code>.<br>
|
|
See <a href="${docsBaseUrl}helpers/site/" target=_blank>here</a>.`,
|
|
regex: /{{\s*?@blog\.cover\s*?}}/g,
|
|
helper: '{{@blog.cover}}'
|
|
},
|
|
'GS001-DEPR-AC': {
|
|
level: 'error',
|
|
rule: 'Replace <code>{{author.cover}}</code> with <code>{{author.cover_image}}</code>',
|
|
fatal: true,
|
|
details: oneLineTrim`The <code>cover</code> attribute was replaced with <code>cover_image</code>.<br>
|
|
Instead of <code>{{author.cover}}</code> you need to use <code>{{author.cover_image}}</code>.<br>
|
|
See the object attributes of <code>author</code> <a href="${docsBaseUrl}contexts/author/#author-object-attributes" target=_blank>here</a>.`,
|
|
regex: /{{\s*?author\.cover\s*?}}/g,
|
|
helper: '{{author.cover}}'
|
|
},
|
|
'GS001-DEPR-TIMG': {
|
|
level: 'error',
|
|
rule: 'Replace <code>{{tag.image}}</code> with <code>{{tag.feature_image}}</code>',
|
|
fatal: true,
|
|
details: oneLineTrim`The <code>image</code> attribute in tag context was replaced with <code>feature_image</code>.<br>
|
|
Instead of <code>{{tag.image}}</code> you need to use <code>{{tag.feature_image}}</code>.<br>
|
|
See the object attributes of <code>tags</code> <a href="${docsBaseUrl}contexts/tag/#tag-object-attributes" target=_blank>here</a>.`,
|
|
regex: /{{\s*?tag\.image\s*?}}/g,
|
|
helper: '{{tag.image}}'
|
|
},
|
|
'GS001-DEPR-PAIMG': {
|
|
level: 'error',
|
|
rule: 'Replace <code>{{post.author.image}}</code> with <code>{{post.author.feature_image}}</code>',
|
|
fatal: true,
|
|
details: oneLineTrim`The <code>image</code> attribute in author context was replaced with <code>feature_image</code>.<br>
|
|
Instead of <code>{{post.author.image}}</code> you need to use <code>{{post.author.feature_image}}</code>.<br>
|
|
See the object attributes of <code>author</code> <a href="${docsBaseUrl}contexts/author/#author-object-attributes" target=_blank>here</a>.`,
|
|
regex: /{{\s*?post\.author\.image\s*?}}/g,
|
|
helper: '{{post.author.image}}'
|
|
},
|
|
'GS001-DEPR-PAC': {
|
|
level: 'error',
|
|
rule: 'Replace <code>{{post.author.cover}}</code> with <code>{{post.author.cover_image}}</code>',
|
|
fatal: true,
|
|
details: oneLineTrim`The <code>cover</code> attribute in author context was replaced with <code>cover_image</code>.<br>
|
|
Instead of <code>{{post.author.cover}}</code> you need to use <code>{{post.author.cover_image}}</code>.<br>
|
|
See the object attributes of <code>author</code> <a href="${docsBaseUrl}contexts/author/#author-object-attributes" target=_blank>here</a>.`,
|
|
regex: /{{\s*?post\.author\.cover\s*?}}/g,
|
|
helper: '{{post.author.cover}}'
|
|
},
|
|
'GS001-DEPR-PTIMG': {
|
|
level: 'error',
|
|
rule: 'Replace <code>{{post.tags.[#].image}}</code> with <code>{{post.tags.[#].feature_image}}</code>',
|
|
fatal: true,
|
|
details: oneLineTrim`The <code>image</code> attribute in tag context was replaced with <code>feature_image</code>.<br>
|
|
Instead of <code>{{post.tags.[#].image}}</code> you need to use <code>{{post.tags.[#].feature_image}}</code>.<br>
|
|
See the object attributes of <code>tags</code> <a href="${docsBaseUrl}contexts/tag/#tag-object-attributes" target=_blank>here</a>.`,
|
|
regex: /{{\s*?post\.tags\.\[[0-9]+\]\.image\s*?}}/g,
|
|
helper: '{{post.tags.[#].image}}'
|
|
},
|
|
'GS001-DEPR-TSIMG': {
|
|
level: 'error',
|
|
rule: 'Replace <code>{{tags.[#].image}}</code> with <code>{{tags.[#].feature_image}}</code>',
|
|
fatal: true,
|
|
details: oneLineTrim`The <code>image</code> attribute in tag context was replaced with <code>feature_image</code>.<br>
|
|
Instead of <code>{{tags.[#].image}}</code> you need to use <code>{{tags.[#].feature_image}}</code>.<br>
|
|
See the object attributes of <code>tags</code> <a href="${docsBaseUrl}contexts/tag/#tag-object-attributes" target=_blank>here</a>.`,
|
|
regex: /{{\s*?tags\.\[[0-9]+\]\.image\s*?}}/g,
|
|
helper: '{{tags.[#].image}}'
|
|
},
|
|
'GS001-DEPR-CON-IMG': {
|
|
level: 'error',
|
|
rule: 'Replace <code>{{#if image}}</code> with <code>{{#if feature_image}}</code>, or <code>' +
|
|
'{{#if profile_image}}</code>',
|
|
fatal: true,
|
|
details: oneLineTrim`The <code>image</code> attribute was replaced with <code>feature_image</code> and <code>profile_image</code>.<br>
|
|
Depending on the <a href="${docsBaseUrl}contexts/" target=_blank>context</a> you will need to replace it like this:<br><br>
|
|
<code>{{#author}}<br>
|
|
{{#if profile_image}}<br> {{profile_image}}<br> {{/if}}<br>
|
|
{{/author}}</code><br><br>
|
|
See the object attributes of <code>author</code> <a href="${docsBaseUrl}contexts/author/#author-object-attributes" target=_blank>here</a>.<br><br>
|
|
<code>{{#post}}<br>
|
|
{{#if feature_image}}<br> {{feature_image}}<br> {{/if}}<br>
|
|
{{/post}}</code><br><br>
|
|
See the object attributes of <code>post</code> <a href="${docsBaseUrl}contexts/post/#post-object-attributes" target=_blank>here</a>.<br><br>
|
|
<code>{{#tag}}<br>
|
|
{{#if feature_image}}<br> {{feature_image}}<br> {{/if}}<br>
|
|
{{/tag}}</code><br><br>
|
|
See the object attributes of <code>tags</code> <a href="${docsBaseUrl}contexts/tag/#tag-object-attributes" target=_blank>here</a>.`,
|
|
regex: /{{\s*?#if\s*?image\s*?}}/g,
|
|
helper: '{{#if image}}'
|
|
},
|
|
'GS001-DEPR-CON-COV': {
|
|
level: 'error',
|
|
rule: 'Replace <code>{{#if cover}}</code> with <code>{{#if cover_image}}</code>',
|
|
fatal: true,
|
|
details: oneLineTrim`The <code>cover</code> attribute was replaced with <code>cover_image</code>. To check for the cover image in author context, you need to use<br><br>
|
|
<code>{{#if cover_image}}<br> {{cover_image}}<br>{{/if}}</code><br><br>
|
|
See the object attributes of <code>author</code> <a href="${docsBaseUrl}contexts/author/#author-object-attributes" target=_blank>here</a>.<br>
|
|
To check for the cover image of your blog, just use <code>{{#if @site.cover_image}}</code>. See <a href="${docsBaseUrl}helpers/site/" target=_blank>here</a>.`,
|
|
regex: /{{\s*?#if\s*?cover\s*?}}/g,
|
|
helper: '{{#if cover}}'
|
|
},
|
|
'GS001-DEPR-CON-BC': {
|
|
level: 'error',
|
|
rule: 'Replace <code>{{#if @blog.cover}}</code> with <code>{{#if @site.cover_image}}</code>',
|
|
fatal: true,
|
|
details: oneLineTrim`The <code>cover</code> attribute was replaced with <code>cover_image</code>.<br>
|
|
Instead of <code>{{#if @blog.cover}}</code> you need to use <code>{{#if @site.cover_image}}</code>.<br>
|
|
See <a href="${docsBaseUrl}helpers/site/" target=_blank>here</a>.`,
|
|
regex: /{{\s*?#if\s*?@blog\.cover\s*?}}/g,
|
|
helper: '{{#if @blog.cover}}'
|
|
},
|
|
'GS001-DEPR-CON-AC': {
|
|
level: 'error',
|
|
rule: 'Replace <code>{{#if author.cover}}</code> with <code>{{#if author.cover_image}}</code>',
|
|
fatal: true,
|
|
details: oneLineTrim`The <code>cover</code> attribute was replaced with <code>cover_image</code>.<br>
|
|
Instead of <code>{{#if author.cover}}</code> you need to use <code>{{#if author.cover_image}}</code>.<br>
|
|
See the object attributes of <code>author</code> <a href="${docsBaseUrl}contexts/author/#author-object-attributes" target=_blank>here</a>.`,
|
|
regex: /{{\s*?#if\s*?author\.cover\s*?}}/g,
|
|
helper: '{{#if author.cover}}'
|
|
},
|
|
'GS001-DEPR-CON-AIMG': {
|
|
level: 'error',
|
|
rule: 'Replace <code>{{#if author.image}}</code> with <code>{{#if author.profile_image}}</code>',
|
|
fatal: true,
|
|
details: oneLineTrim`The <code>image</code> attribute in author context was replaced with <code>profile_image</code>.<br>
|
|
Instead of <code>{{#if author.image}}</code> you need to use <code>{{#if author.profile_image}}</code>.<br>
|
|
See the object attributes of <code>author</code> <a href="${docsBaseUrl}contexts/author/#author-object-attributes" target=_blank>here</a>.`,
|
|
regex: /{{\s*?#if\s*?author\.image\s*?}}/g,
|
|
helper: '{{#if author.image}}'
|
|
},
|
|
'GS001-DEPR-CON-PAC': {
|
|
level: 'error',
|
|
rule: 'Replace <code>{{#if post.author.cover}}</code> with <code>{{#if post.author.cover_image}}</code>',
|
|
fatal: true,
|
|
details: oneLineTrim`The <code>cover</code> attribute was replaced with <code>cover_image</code>.<br>
|
|
Instead of <code>{{#if post.author.cover}}</code> you need to use <code>{{#if post.author.cover_image}}</code>.<br>
|
|
See the object attributes of <code>author</code> <a href="${docsBaseUrl}contexts/author/#author-object-attributes" target=_blank>here</a>.`,
|
|
regex: /{{\s*?#if\s*?post\.author\.cover\s*?}}/g,
|
|
helper: '{{#if post.author.cover}}'
|
|
},
|
|
'GS001-DEPR-CON-PAIMG': {
|
|
level: 'error',
|
|
rule: 'Replace <code>{{#if post.author.image}}</code> with <code>{{#if post.author.profile_image}}</code>',
|
|
fatal: true,
|
|
details: oneLineTrim`The <code>image</code> attribute in author context was replaced with <code>profile_image</code>.<br>
|
|
Instead of <code>{{#if post.author.image}}</code> you need to use <code>{{#if post.author.profile_image}}</code>.<br>
|
|
See the object attributes of <code>author</code> <a href="${docsBaseUrl}contexts/author/#author-object-attributes" target=_blank>here</a>.`,
|
|
regex: /{{\s*?#if\s*?post\.author\.image\s*?}}/g,
|
|
helper: '{{#if post.author.image}}'
|
|
},
|
|
'GS001-DEPR-CON-TIMG': {
|
|
level: 'error',
|
|
rule: 'Replace <code>{{#if tag.image}}</code> with <code>{{#if tag.feature_image}}</code>',
|
|
fatal: true,
|
|
details: oneLineTrim`The <code>image</code> attribute in tag context was replaced with <code>feature_image</code>.<br>
|
|
Instead of <code>{{#if tag.image}}</code> you need to use <code>{{#if tag.feature_image}}</code>.<br>
|
|
See the object attributes of <code>tags</code> <a href="${docsBaseUrl}contexts/tag/#tag-object-attributes" target=_blank>here</a>.`,
|
|
regex: /{{\s*?#if\s*?tag\.image\s*?}}/g,
|
|
helper: '{{#if tag.image}}'
|
|
},
|
|
'GS001-DEPR-CON-PTIMG': {
|
|
level: 'error',
|
|
rule: 'Replace <code>{{#if post.tags.[#].image}}</code> with <code>{{#if post.tags.[#].feature_image}}</code>',
|
|
fatal: true,
|
|
details: oneLineTrim`The <code>image</code> attribute in tag context was replaced with <code>feature_image</code>.<br>
|
|
Instead of <code>{{#if post.tags.[#].image}}</code> you need to use <code>{{#if post.tags.[#].feature_image}}</code>.<br>
|
|
See the object attributes of <code>tags</code> <a href="${docsBaseUrl}contexts/tag/#tag-object-attributes" target=_blank>here</a>.`,
|
|
regex: /{{\s*?#if\s*?post\.tags\.\[[0-9]+\].image\s*?}}/g,
|
|
helper: '{{#if posts.tags.[#].image}}'
|
|
},
|
|
'GS001-DEPR-CON-TSIMG': {
|
|
level: 'error',
|
|
rule: 'Replace <code>{{#if tags.[#].image}}</code> with <code>{{#if tags.[#].feature_image}}</code>',
|
|
fatal: true,
|
|
details: oneLineTrim`The <code>image</code> attribute in tag context was replaced with <code>feature_image</code>.<br>
|
|
Instead of <code>{{#if tags.[#].image}}</code> you need to use <code>{{#if tags.[#].feature_image}}</code>.<br>
|
|
See the object attributes of <code>tags</code> <a href="${docsBaseUrl}contexts/tag/#tag-object-attributes" target=_blank>here</a>.`,
|
|
regex: /{{\s*?#if\s*?tags\.\[[0-9]+\].image\s*?}}/g,
|
|
helper: '{{#if tags.[#].image}}'
|
|
},
|
|
'GS001-DEPR-PPP': {
|
|
level: 'error',
|
|
rule: 'Replace <code>{{@blog.posts_per_page}}</code> with <code>{{@config.posts_per_page}}</code>',
|
|
details: oneLineTrim`The global <code>{{@blog.posts_per_page}}</code> property was replaced with <code>{{@config.posts_per_page}}</code>.<br>
|
|
Read <a href="${docsBaseUrl}helpers/config/" target=_blank>here</a> about the attribute and
|
|
check <a href="${docsBaseUrl}structure/#packagejson" target=_blank>here</a> where you can customise the posts per page setting, as this is now adjustable in your theme.`,
|
|
regex: /{{\s*?@blog\.posts_per_page\s*?}}/g,
|
|
helper: '{{@blog.posts_per_page}}'
|
|
},
|
|
'GS001-DEPR-C0H': {
|
|
level: 'error',
|
|
rule: 'Replace <code>{{content words="0"}}</code> with <code><img src="{{img_url feature_image}}"/></code>.',
|
|
details: oneLineTrim`The <code>{{content words="0"}}</code> hack doesn't work anymore (and was never supported).<br>
|
|
Find more information about the <code>{{img_url}}</code> helper <a href="${docsBaseUrl}helpers/img_url/" target=_blank>here</a>.`,
|
|
regex: /{{\s*?content words=("|')0("|')\s*?}}/g,
|
|
helper: '{{content words="0"}}'
|
|
},
|
|
'GS001-DEPR-CSS-AT': {
|
|
level: 'error',
|
|
rule: 'Replace <code>.archive-template</code> with the <code>.paged</code> CSS class',
|
|
details: oneLineTrim`The <code>.archive-template</code> CSS class was replaced with the <code>.paged</code>. Please replace this in your stylesheet.<br>
|
|
See the <a href="${docsBaseUrl}contexts/" target=_blank>context table</a> to check which classes Ghost uses for each context.`,
|
|
regex: /\.archive-template[\s{]/g,
|
|
className: '.archive-template',
|
|
css: true
|
|
},
|
|
'GS001-DEPR-CSS-PATS': {
|
|
level: 'error',
|
|
rule: 'Replace <code>.page-template-slug</code> with the <code>.page-slug</code> css class',
|
|
details: oneLineTrim`The <code>.page-template-slug</code> CSS class was replaced with the <code>.page-slug</code>. Please replace this in your stylesheet.<br>
|
|
See the <a href="${docsBaseUrl}contexts/" target=_blank>context table</a> to check which classes Ghost uses for each context.`,
|
|
regex: /\.page-template-\w+[\s{]/g,
|
|
className: '.page-template-slug',
|
|
css: true
|
|
},
|
|
'GS001-DEPR-EACH': {
|
|
level: 'warning',
|
|
rule: 'Replace <code>{{#each}}</code> with <code>{{#foreach}}</code>',
|
|
fatal: false,
|
|
details: oneLineTrim`The <code>{{#foreach}}</code> helper is context-aware and should always be used instead of Handlebars <code>{{#each}}</code> when working with Ghost themes.<br>
|
|
See the description of <code>{{#foreach}}</code> helper <a href="${docsBaseUrl}helpers/foreach/" target=_blank>here</a>.`,
|
|
regex: /{{\s*?#each\s*/g
|
|
},
|
|
'GS002-DISQUS-ID': {
|
|
level: 'error',
|
|
rule: 'Replace <code>{{id}}</code> with <code>{{comment_id}}</code> in Disqus embeds.',
|
|
fatal: true,
|
|
details: oneLineTrim`The output of <code>{{id}}</code> has changed in v1.0.0 from an incremental ID to an ObjectID.
|
|
This results in Disqus comments not loading in Ghost v1.0.0 posts which were imported from earlier versions.
|
|
To resolve this, we've added a <code>{{comment_id}}</code> helper that will output the old ID
|
|
for posts that have been imported from earlier versions, and ID for new posts.
|
|
The Disqus embed must be updated from <code>this.page.identifier = 'ghost-{{id}}';</code> to
|
|
<code>this.page.identifier = 'ghost-{{comment_id}}';</code> to ensure Disqus continues to work.`,
|
|
regex: /(page\.|disqus_)identifier\s?=\s?['"].*?({{\s*?id\s*?}}).*?['"];?/g
|
|
},
|
|
'GS002-ID-HELPER': {
|
|
level: 'recommendation',
|
|
rule: 'The output of <code>{{id}}</code> changed in Ghost v1.0.0, you may need to use <code>{{comment_id}}</code> instead.',
|
|
details: oneLineTrim`The output of <code>{{id}}</code> has changed in v1.0.0 from an incremental ID to an ObjectID.
|
|
In v1.0.0 we added a <code>{{comment_id}}</code> helper that will output the old ID
|
|
for posts that have been imported from earlier versions, and ID for new posts.
|
|
If you need the old ID to be output on imported posts, then you will need to use
|
|
<code>{{comment_id}}</code> rather than <code>{{id}}</code>.`,
|
|
regex: /({{\s*?id\s*?}})/g
|
|
},
|
|
'GS005-TPL-ERR': {
|
|
level: 'error',
|
|
rule: 'Templates must contain valid Handlebars',
|
|
fatal: true,
|
|
details: oneLineTrim`Oops! You seemed to have used invalid Handlebars syntax. This mostly happens when you use a helper that is not supported.<br>
|
|
See the full list of available helpers <a href="${docsBaseUrl}helpers/" target=_blank>here</a>.`
|
|
},
|
|
'GS010-PJ-REQ': {
|
|
level: 'error',
|
|
rule: '<code>package.json</code> file should be present',
|
|
details: oneLineTrim`You should provide a <code>package.json</code> file for your theme.<br>
|
|
Check the <a href="${docsBaseUrl}structure/#packagejson" target=_blank><code>package.json</code> documentation</a> to see which properties are required and which are recommended.`
|
|
},
|
|
'GS010-PJ-PARSE': {
|
|
level: 'error',
|
|
rule: '<code>package.json</code> file can be parsed',
|
|
details: oneLineTrim`Your <code>package.json</code> file couldn't be parsed. This is mostly caused by a missing or unnecessary <code>','</code> or the wrong usage of <code>'""'</code>.<br>
|
|
Check the <a href="${docsBaseUrl}structure/#packagejson" target=_blank><code>package.json</code> documentation</a> for further information.<br>
|
|
A good reference for your <code>package.json</code> file is always the latest version of <a href="https://github.com/TryGhost/Casper/blob/master/package.json" target=_blank>Casper</a>.`
|
|
},
|
|
'GS010-PJ-NAME-LC': {
|
|
level: 'error',
|
|
rule: '<code>package.json</code> property <code>"name"</code> must be lowercase',
|
|
details: oneLineTrim`The property <code>"name"</code> in your <code>package.json</code> file must be lowercase.<br>
|
|
Good examples are: <code>"my-theme"</code> or <code>"theme"</code> rather than <code>"My Theme"</code> or <code>"Theme"</code>.<br>
|
|
Check the <a href="${docsBaseUrl}structure/#packagejson" target=_blank><code>package.json</code> documentation</a> for further information.`
|
|
},
|
|
'GS010-PJ-NAME-HY': {
|
|
level: 'error',
|
|
rule: '<code>package.json</code> property <code>"name"</code> must be hyphenated',
|
|
details: oneLineTrim`The property <code>"name"</code> in your <code>package.json</code> file must be hyphenated.<br>
|
|
Please use <code>"my-theme"</code> rather than <code>"My Theme"</code> or <code>"my theme"</code>.<br>
|
|
Check the <a href="${docsBaseUrl}structure/#packagejson" target=_blank><code>package.json</code> documentation</a> for further information.`
|
|
},
|
|
'GS010-PJ-NAME-REQ': {
|
|
level: 'error',
|
|
rule: '<code>package.json</code> property <code>"name"</code> is required',
|
|
details: oneLineTrim`Please add the property <code>"name"</code> to your <code>package.json</code>. E.g. <code>{"name": "my-theme"}</code>.<br>
|
|
Check the <a href="${docsBaseUrl}structure/#packagejson" target=_blank><code>package.json</code> documentation</a> to see which properties are required and which are recommended.`
|
|
},
|
|
'GS010-PJ-VERSION-SEM': {
|
|
level: 'error',
|
|
rule: '<code>package.json</code> property <code>"version"</code> must be semver compliant',
|
|
details: oneLineTrim`The property <code>"version"</code> in your <code>package.json</code> file must be semver compliant. E.g. <code>{"version": "1.0.0"}</code>.<br>
|
|
Check the <a href="${docsBaseUrl}structure/#packagejson" target=_blank><code>package.json</code> documentation</a> for further information.`
|
|
},
|
|
'GS010-PJ-VERSION-REQ': {
|
|
level: 'error',
|
|
rule: '<code>package.json</code> property <code>"version"</code> is required',
|
|
details: oneLineTrim`Please add the property <code>"version"</code> to your <code>package.json</code>. E.g. <code>{"version": "1.0.0"}</code>.<br>
|
|
Check the <a href="${docsBaseUrl}structure/#packagejson" target=_blank><code>package.json</code> documentation</a> to see which properties are required and which are recommended.`
|
|
},
|
|
'GS010-PJ-AUT-EM-VAL': {
|
|
level: 'error',
|
|
rule: '<code>package.json</code> property <code>"author.email"</code> must be valid',
|
|
details: oneLineTrim`The property <code>"author.email"</code> in your <code>package.json</code> file must a valid email. E.g. <code>{"author": {"email": "hello@example.com"}}</code>.<br>
|
|
Check the <a href="${docsBaseUrl}structure/#packagejson" target=_blank><code>package.json</code> documentation</a> for further information.`
|
|
},
|
|
'GS010-PJ-CONF-PPP': {
|
|
level: 'recommendation',
|
|
rule: '<code>package.json</code> property <code>"config.posts_per_page"</code> is recommended. Otherwise, it falls back to 5',
|
|
details: oneLineTrim`Please add <code>"posts_per_page"</code> to your <code>package.json</code>. E.g. <code>{"config": { "posts_per_page": 5}}</code>.<br>
|
|
If no <code>"posts_per_page"</code> property is provided, Ghost will use its default setting of 5 posts per page.<br>
|
|
Check the <a href="${docsBaseUrl}structure/#packagejson" target=_blank><code>package.json</code> documentation</a> for further information.`
|
|
},
|
|
'GS010-PJ-CONF-PPP-INT': {
|
|
level: 'error',
|
|
rule: '<code>package.json</code> property <code>"config.posts_per_page"</code> must be a number above 0',
|
|
details: oneLineTrim`The property <code>"config.posts_per_page"</code> in your <code>package.json</code> file must be a number greater than zero. E.g. <code>{"config": { "posts_per_page": 5}}</code>.<br>
|
|
Check the <a href="${docsBaseUrl}structure/#packagejson" target=_blank><code>package.json</code> documentation</a> for further information.`
|
|
},
|
|
'GS010-PJ-AUT-EM-REQ': {
|
|
level: 'error',
|
|
rule: '<code>package.json</code> property <code>"author.email"</code> is required',
|
|
details: oneLineTrim`Please add the property <code>"author.email"</code> to your <code>package.json</code>. E.g. <code>{"author": {"email": "hello@example.com"}}</code>.<br>
|
|
The email is required so that themes which are distributed (either free or paid) have a method of contacting the author so users can get support and more importantly so that>
|
|
Ghost can reach out about breaking changes and security updates.<br>
|
|
The <code>package.json</code> file is <strong>NOT</strong> accessible when uploaded to a blog so if the theme is only uploaded to a single blog, no one will see this email address.<br>
|
|
Check the <a href="${docsBaseUrl}structure/#packagejson" target=_blank><code>package.json</code> documentation</a> to see which properties are required and which are recommended.`
|
|
},
|
|
'GS020-INDEX-REQ': {
|
|
level: 'error',
|
|
rule: 'A template file called <code>index.hbs</code> must be present',
|
|
fatal: true,
|
|
details: oneLineTrim`Your theme must have a template file called <code>index.hbs</code>.<br>
|
|
Read <a href="${docsBaseUrl}contexts/" target=_blank>here</a> more about the required template structure and <code>index.hbs</code> in <a href="${docsBaseUrl}contexts/index-context/" target=_blank>particular</a>.`,
|
|
path: 'index.hbs'
|
|
},
|
|
'GS020-POST-REQ': {
|
|
level: 'error',
|
|
rule: 'A template file called <code>post.hbs</code> must be present',
|
|
fatal: true,
|
|
details: oneLineTrim`Your theme must have a template file called <code>index.hbs</code>.<br>
|
|
Read <a href="${docsBaseUrl}structure/#templates" target=_blank>here</a> more about the required template structure and <code>post.hbs</code> in <a href="${docsBaseUrl}structure/#posthbs" target=_blank>particular</a>.`,
|
|
path: 'post.hbs'
|
|
},
|
|
'GS020-DEF-REC': {
|
|
level: 'recommendation',
|
|
rule: 'Provide a default layout template called default.hbs',
|
|
details: oneLineTrim`It is recommended that your theme has a template file called <code>default.hbs</code>.<br>
|
|
Read <a href="${docsBaseUrl}structure/#templates" target=_blank>here</a> more about the recommended template structure and <code>default.hbs</code> in <a href="${docsBaseUrl}structure/#defaulthbs" target=_blank>particular</a>.`,
|
|
path: 'default.hbs'
|
|
},
|
|
'GS030-ASSET-REQ': {
|
|
level: 'warning',
|
|
rule: 'Assets such as CSS & JS must use the <code>{{asset}}</code> helper',
|
|
details: oneLineTrim`The listed files should be included using the <code>{{asset}}</code> helper.<br>
|
|
For more information, please see the <a href="${docsBaseUrl}helpers/asset/" target=_blank><code>{{asset}}</code> helper documentation</a>.`,
|
|
regex: /(src|href)=['"](.*?\/assets\/.*?)['"]/gmi
|
|
},
|
|
'GS030-ASSET-SYM': {
|
|
level: 'error',
|
|
rule: 'Symlinks in themes are not allowed',
|
|
fatal: true,
|
|
details: oneLineTrim`Symbolic links in themes are not allowed. Please use the <code>{{asset}}</code> helper.<br>
|
|
For more information, please see the <a href="${docsBaseUrl}helpers/asset/" target=_blank><code>{{asset}}</code> helper documentation</a>.`
|
|
},
|
|
'GS040-GH-REQ': {
|
|
level: 'warning',
|
|
rule: 'The helper <code>{{ghost_head}}</code> should be present',
|
|
details: oneLineTrim`The <code>{{ghost_head}}</code> helper should be present in your theme. It outputs many useful things, such as <a href="${docsBaseUrl}helpers/ghost_head_foot/" target=_blank>"code injection" scripts</a>, structured data, canonical links, meta description etc.<br>
|
|
The helper belongs just before the <code></head></code> tag in your <code>default.hbs</code> template.<br>
|
|
For more details, please see the <a href="${docsBaseUrl}helpers/ghost_head_foot/" target=_blank><code>{{ghost_head}}</code> helper documentation</a>.`,
|
|
helper: 'ghost_head'
|
|
},
|
|
'GS040-GF-REQ': {
|
|
level: 'warning',
|
|
rule: 'The helper <code>{{ghost_foot}}</code> should be present',
|
|
details: oneLineTrim`The <code>{{ghost_foot}}</code> helper should be present in your theme. It outputs scripts as saved in <a href="${docsBaseUrl}helpers/ghost_head_foot/" target=_blank>"code injection" scripts</a>.<br>
|
|
The helper belongs just before the <code></body></code> tag in your <code>default.hbs</code> template.<br>
|
|
For more details, please see the <a href="${docsBaseUrl}helpers/ghost_head_foot/" target=_blank><code>{{ghost_foot}}</code> helper documentation</a>.`,
|
|
helper: 'ghost_foot'
|
|
}
|
|
};
|
|
|
|
/**
|
|
* These are rules that haven't been implemented yet, but should be!
|
|
*/
|
|
ruleNext = { //eslint-disable-line
|
|
'GS030-CSS-CACHE': {
|
|
level: 'warning',
|
|
rule: 'CSS files should use cache bustable URLs'
|
|
}
|
|
};
|
|
|
|
module.exports = {
|
|
knownHelpers: knownHelpers,
|
|
templates: templates,
|
|
rules: rules
|
|
};
|