const _ = require('lodash');
const oneLineTrim = require('common-tags/lib/oneLineTrim');
const previousSpec = require('./v4');
const ghostVersions = require('../utils').versions;
const docsBaseUrl = `https://ghost.org/docs/themes/`;
const prevDocsBaseUrl = `https://themes.ghost.org/v${ghostVersions.v5.docs}/docs/`;
const prevDocsBaseUrlRegEx = new RegExp(prevDocsBaseUrl, 'g');
const previousKnownHelpers = previousSpec.knownHelpers;
const previousTemplates = previousSpec.templates;
const previousRules = _.cloneDeep(previousSpec.rules);
const multiAuthorDesc = `Ghost allows multiple authors to be assigned to a post, so all helpers have been reworked to account for this.`;
const authorHelperDocs = `Find more information about the {{authors}} helper here`;
const tierDesc = `Ghost now supports multiple tiers and subscriptions. All product and price related helpers have been reworked to account for this.`;
// assign new or overwrite existing knownHelpers, templates, or rules here:
let knownHelpers = ['total_members', 'total_paid_members', 'comment_count', 'comments', 'recommendations', 'readable_url'];
let templates = [];
let rules = {
// New rules
'GS010-PJ-GHOST-API-PRESENT': {
level: 'warning',
rule: 'Remove "engines.ghost-api" from package.json',
details: oneLineTrim`The "ghost-api" version is no longer used and can be removed.
Find more information about the package.json file here.`
},
'GS010-PJ-GHOST-CARD-ASSETS-NOT-PRESENT': {
level: 'warning',
rule: '"card_assets" will now be included by default, including bookmark and gallery cards.',
details: oneLineTrim`The "card_assets" property is enabled by default and set to true (include all) if not explicitly set.
Find more information about the card_assets property here.`
},
'GS090-NO-AUTHOR-HELPER-IN-POST-CONTEXT': {
level: 'error',
fatal: false,
rule: 'Replace {{author}} with {{authors}}',
details: oneLineTrim`The {{author}} helper was removed in favor of {{}}
${multiAuthorDesc}
${authorHelperDocs}`,
helper: '{{author}}'
},
'GS090-NO-PRODUCTS-HELPER': {
level: 'error',
fatal: false,
rule: 'Replace {{products}} with {{tiers}}',
details: oneLineTrim`The {{products}} helper was removed in favor of {{tiers}}
${tierDesc}
Find more information about the {{tiers}} property here.`,
helper: '{{products}}'
},
'GS090-NO-PRODUCT-DATA-HELPER': {
level: 'error',
fatal: false,
rule: 'Replace {{@product}} with {{#get "tiers"}}',
details: oneLineTrim`The {{@product}} data helper was removed in favor of {{#get "tiers"}}
${tierDesc}
Find more information about the {{#get "tiers"}} property here.`,
helper: '{{@product}}'
},
'GS090-NO-PRODUCTS-DATA-HELPER': {
level: 'error',
fatal: false,
rule: 'Replace {{@products}} with {{#get "tiers"}}',
details: oneLineTrim`The {{@products}} data helper was removed in favor of {{#get "tiers"}}
${tierDesc}
Find more information about the {{#get "tiers"}} property here.`,
helper: '{{@products}}'
},
'GS090-NO-MEMBER-PRODUCTS-DATA-HELPER': {
level: 'error',
rule: 'Replace {{@member.products}} with {{@member.subscriptions}}',
details: oneLineTrim`The {{@member.products}} helper was removed in favor of {{@member.subscriptions}}
${tierDesc}
Find more information about the {{@member.subscriptions}} property here.`,
helper: '{{@member.products}}'
},
'GS090-NO-PRICE-DATA-CURRENCY-GLOBAL': {
level: 'error',
fatal: false,
rule: 'Replace {{@price.currency}} with {{#get "tiers"}} and {{currency}} or {{#foreach @member.subscriptions}} and {{plan.currency}}',
details: oneLineTrim`There is no longer a global @price object. You need to use either {{#get "tiers"}} to fetch all tiers and use the {{currency}} property of a tier
or use {{#foreach @member.subscriptions}} to fetch an individual member's subscriptions, and use the {{plan.currency}} property from the subscription.
Find more information about the {{price}} helper here.`
},
'GS090-NO-PRICE-DATA-CURRENCY-CONTEXT': {
level: 'error',
fatal: false,
rule: 'Replace {{@price.currency}} with {{currency}} or {{plan.currency}}',
details: oneLineTrim`There is no longer a global @price object. Instead the {{currency}} property can be used inside {{#get "tiers"}}
or {{plan.currency}} can be used inside {{#foreach @member.subscriptions}}
Find more information about the {{price}} helper here.`
},
'GS090-NO-PRICE-DATA-MONTHLY-YEARLY': {
level: 'error',
fatal: false,
rule: 'Replace {{@price.monthly}} and {{@price.yearly}} with {{price monthly_price currency=currency}} and {{price yearly_price currency=currency}} after fetching tier data with {{#get "tiers"}}',
details: oneLineTrim`There is no longer a global @price object. You need to use {{#get "tiers"}} to fetch all the tiers and get access to the {{price monthly_price currency=currency}} or {{price yearly_price currency=currency}} for each tier
Find more information about the {{price}} helper here.`
},
'GS090-NO-TIER-PRICE-AS-OBJECT': {
level: 'error',
fatal: false,
rule: 'Remove usage of {{monthly_price.*}} and {{yearly_price.*}}.',
details: oneLineTrim`The usage of {{monthly_price.*}} and {{yearly_price.*}} is no longer supported.
${tierDesc}
Find more information about the {{#get "tiers"}} here.`,
helper: '{{#get "tiers"}}'
},
'GS090-NO-TIER-BENEFIT-AS-OBJECT': {
level: 'error',
fatal: true,
rule: 'Remove usage of {{name}} for tier benefits.',
details: oneLineTrim`The usage of {{name}} for tier benefits is no longer supported.
${tierDesc}
Find more information about the {{#get "tiers"}} here.`,
helper: '{{#get "tiers"}}'
},
'GS001-DEPR-AUTH-ID': {
level: 'error',
fatal: false,
rule: 'Replace {{author.id}} with {{primary_author.id}} or {{authors.[#].id}}',
details: oneLineTrim`The usage of {{author.id}} is no longer supported and should be replaced with either {{primary_author.id}}
or {{authors.[#].id}}.
${multiAuthorDesc}
${authorHelperDocs}`,
regex: /{{\s*?author\.id\s*?}}/g,
helper: '{{author.id}}'
},
'GS001-DEPR-AUTH-SLUG': {
level: 'error',
fatal: false,
rule: 'Replace {{author.slug}} with {{primary_author.slug}} or {{authors.[#].slug}}',
details: oneLineTrim`The usage of {{author.slug}} is no longer supported and should be replaced with either {{primary_author.slug}}
or {{authors.[#].slug}}.
${multiAuthorDesc}
${authorHelperDocs}`,
regex: /{{\s*?author\.slug\s*?}}/g,
helper: '{{author.slug}}'
},
'GS001-DEPR-AUTH-MAIL': {
level: 'error',
fatal: false,
rule: 'Replace {{author.email}} with {{primary_author.email}} or {{authors.[#].email}}',
details: oneLineTrim`The usage of {{author.email}} is no longer supported and should be replaced with either {{primary_author.email}}
or {{authors.[#].email}}.
${multiAuthorDesc}
${authorHelperDocs}`,
regex: /{{\s*?author\.email\s*?}}/g,
helper: '{{author.email}}'
},
'GS001-DEPR-AUTH-MT': {
level: 'error',
fatal: false,
rule: 'Replace {{author.meta_title}} with {{primary_author.meta_title}} or {{authors.[#].meta_title}}',
details: oneLineTrim`The usage of {{author.meta_title}} is no longer supported and should be replaced with either {{primary_author.meta_title}}
or {{authors.[#].meta_title}}.
${multiAuthorDesc}
${authorHelperDocs}`,
regex: /{{\s*?author\.meta_title\s*?}}/g,
helper: '{{author.meta_title}}'
},
'GS001-DEPR-AUTH-MD': {
level: 'error',
fatal: false,
rule: 'Replace {{author.meta_description}} with {{primary_author.meta_description}} or {{authors.[#].meta_description}}',
details: oneLineTrim`The usage of {{author.meta_description}} is no longer supported and should be replaced with either {{primary_author.meta_description}}
or {{authors.[#].meta_description}}.
${multiAuthorDesc}
${authorHelperDocs}`,
regex: /{{\s*?author\.meta_description\s*?}}/g,
helper: '{{author.meta_description}}'
},
'GS001-DEPR-AUTH-NAME': {
level: 'error',
fatal: false,
rule: 'Replace {{author.name}} with {{primary_author.name}} or {{authors.[#].name}}',
details: oneLineTrim`The usage of {{author.name}} is no longer supported and should be replaced with either {{primary_author.name}}
or {{authors.[#].name}}.
${multiAuthorDesc}
${authorHelperDocs}`,
regex: /{{\s*?author\.name\s*?}}/g,
helper: '{{author.name}}'
},
'GS001-DEPR-AUTH-BIO': {
level: 'error',
fatal: false,
rule: 'Replace {{author.bio}} with {{primary_author.bio}} or {{authors.[#].bio}}',
details: oneLineTrim`The usage of {{author.bio}} is no longer supported and should be replaced with either {{primary_author.bio}}
or {{authors.[#].bio}}.
${multiAuthorDesc}
${authorHelperDocs}`,
regex: /{{\s*?author\.bio\s*?}}/g,
helper: '{{author.bio}}'
},
'GS001-DEPR-AUTH-LOC': {
level: 'error',
fatal: false,
rule: 'Replace {{author.location}} with {{primary_author.location}} or {{authors.[#].location}}',
details: oneLineTrim`The usage of {{author.location}} is no longer supported and should be replaced with either {{primary_author.location}}
or {{authors.[#].location}}.
${multiAuthorDesc}
${authorHelperDocs}`,
regex: /{{\s*?author\.location\s*?}}/g,
helper: '{{author.location}}'
},
'GS001-DEPR-AUTH-WEB': {
level: 'error',
fatal: false,
rule: 'Replace {{author.website}} with {{primary_author.website}} or {{authors.[#].website}}',
details: oneLineTrim`The usage of {{author.website}} is no longer supported and should be replaced with either {{primary_author.website}}
or {{authors.[#].website}}.
${multiAuthorDesc}
${authorHelperDocs}`,
regex: /{{\s*?author\.website\s*?}}/g,
helper: '{{author.website}}'
},
'GS001-DEPR-AUTH-TW': {
level: 'error',
fatal: false,
rule: 'Replace {{author.twitter}} with {{primary_author.twitter}} or {{authors.[#].twitter}}',
details: oneLineTrim`The usage of {{author.twitter}} is no longer supported and should be replaced with either {{primary_author.twitter}}
or {{authors.[#].twitter}}.
${multiAuthorDesc}
${authorHelperDocs}`,
regex: /{{\s*?author\.twitter\s*?}}/g,
helper: '{{author.twitter}}'
},
'GS001-DEPR-AUTH-FB': {
level: 'error',
fatal: false,
rule: 'Replace {{author.facebook}} with {{primary_author.facebook}} or {{authors.[#].facebook}}',
details: oneLineTrim`The usage of {{author.facebook}} is no longer supported and should be replaced with either {{primary_author.facebook}}
or {{authors.[#].facebook}}.
${multiAuthorDesc}
${authorHelperDocs}`,
regex: /{{\s*?author\.facebook\s*?}}/g,
helper: '{{author.facebook}}'
},
'GS001-DEPR-AUTH-PIMG': {
level: 'error',
fatal: false,
rule: 'Replace {{author.profile_image}} with {{primary_author.profile_image}} or {{authors.[#].profile_image}}',
details: oneLineTrim`The usage of {{author.profile_image}} is no longer supported and should be replaced with either {{primary_author.profile_image}}
or {{authors.[#].profile_image}}.
${multiAuthorDesc}
${authorHelperDocs}`,
regex: /{{\s*?author\.profile_image\s*?}}/g,
helper: '{{author.profile_image}}'
},
'GS001-DEPR-AUTH-CIMG': {
level: 'error',
fatal: false,
rule: 'Replace {{author.cover_image}} with {{primary_author.cover_image}} or {{authors.[#].cover_image}}',
details: oneLineTrim`The usage of {{author.cover_image}} is no longer supported and should be replaced with either {{primary_author.cover_image}}
or {{authors.[#].cover_image}}.
${multiAuthorDesc}
${authorHelperDocs}`,
regex: /{{\s*?author\.cover_image\s*?}}/g,
helper: '{{author.cover_image}}'
},
'GS001-DEPR-AUTH-URL': {
level: 'error',
fatal: false,
rule: 'Replace {{author.url}} with {{primary_author.url}} or {{authors.[#].url}}',
details: oneLineTrim`The usage of {{author.url}} is no longer supported and should be replaced with either {{primary_author.url}}
or {{authors.[#].url}}.
${multiAuthorDesc}
${authorHelperDocs}`,
regex: /{{\s*?author\.url\s*?}}/g,
helper: '{{author.url}}'
},
'GS001-DEPR-PAUTH': {
level: 'error',
fatal: true,
rule: 'Replace {{post.author}} with {{post.primary_author}} or {{authors.[#]}}',
details: oneLineTrim`The usage of {{post.author}} is no longer supported and should be replaced with either {{post.primary_author}}
or {{post.authors.[#]}}.
${multiAuthorDesc}
${authorHelperDocs}`,
regex: /{{\s*?post\.author\s*?}}/g,
helper: '{{post.author}}'
},
'GS001-DEPR-PAUTH-ID': {
level: 'error',
fatal: true,
rule: 'Replace {{post.author.id}} with {{post.primary_author.id}} or {{authors.[#].id}}',
details: oneLineTrim`The usage of {{post.author.id}} is no longer supported and should be replaced with either {{post.primary_author.id}}
or {{post.authors.[#].id}}.
${multiAuthorDesc}
${authorHelperDocs}`,
regex: /{{\s*?post\.author\.id\s*?}}/g,
helper: '{{post.author.id}}'
},
'GS001-DEPR-PAUTH-SLUG': {
level: 'error',
fatal: true,
rule: 'Replace {{post.author.slug}} with {{post.primary_author.slug}} or {{post.authors.[#].slug}}',
details: oneLineTrim`The usage of {{post.author.slug}} is no longer supported and should be replaced with either {{post.primary_author.slug}}
or {{post.authors.[#].slug}}.
${multiAuthorDesc}
${authorHelperDocs}`,
regex: /{{\s*?post\.author\.slug\s*?}}/g,
helper: '{{post.author.slug}}'
},
'GS001-DEPR-PAUTH-MAIL': {
level: 'error',
fatal: true,
rule: 'Replace {{post.author.email}} with {{post.primary_author.email}} or {{post.authors.[#].email}}',
details: oneLineTrim`The usage of {{post.author.email}} is no longer supported and should be replaced with either {{post.primary_author.email}}
or {{post.authors.[#].email}}.
${multiAuthorDesc}
${authorHelperDocs}`,
regex: /{{\s*?post\.author\.email\s*?}}/g,
helper: '{{post.author.email}}'
},
'GS001-DEPR-PAUTH-MT': {
level: 'error',
fatal: true,
rule: 'Replace {{post.author.meta_title}} with {{post.primary_author.meta_title}} or {{post.authors.[#].meta_title}}',
details: oneLineTrim`The usage of {{post.author.meta_title}} is no longer supported and should be replaced with either {{post.primary_author.meta_title}}
or {{post.authors.[#].meta_title}}.
${multiAuthorDesc}
${authorHelperDocs}`,
regex: /{{\s*?post\.author\.meta_title\s*?}}/g,
helper: '{{post.author.meta_title}}'
},
'GS001-DEPR-PAUTH-MD': {
level: 'error',
fatal: true,
rule: 'Replace {{post.author.meta_description}} with {{post.primary_author.meta_description}} or {{post.authors.[#].meta_description}}',
details: oneLineTrim`The usage of {{post.author.meta_description}} is no longer supported and should be replaced with either {{post.primary_author.meta_description}}
or {{post.authors.[#].meta_description}}.
${multiAuthorDesc}
${authorHelperDocs}`,
regex: /{{\s*?post\.author\.meta_description\s*?}}/g,
helper: '{{post.author.meta_description}}'
},
'GS001-DEPR-PAUTH-NAME': {
level: 'error',
fatal: true,
rule: 'Replace {{post.author.name}} with {{post.primary_author.name}} or {{post.authors.[#].name}}',
details: oneLineTrim`The usage of {{post.author.name}} is no longer supported and should be replaced with either {{post.primary_author.name}}
or {{post.authors.[#].name}}.
${multiAuthorDesc}
${authorHelperDocs}`,
regex: /{{\s*?post\.author\.name\s*?}}/g,
helper: '{{post.author.name}}'
},
'GS001-DEPR-PAUTH-BIO': {
level: 'error',
fatal: true,
rule: 'Replace {{post.author.bio}} with {{post.primary_author.bio}} or {{post.authors.[#].bio}}',
details: oneLineTrim`The usage of {{post.author.bio}} is no longer supported and should be replaced with either {{post.primary_author.bio}}
or {{post.authors.[#].bio}}.
${multiAuthorDesc}
${authorHelperDocs}`,
regex: /{{\s*?post\.author\.bio\s*?}}/g,
helper: '{{post.author.bio}}'
},
'GS001-DEPR-PAUTH-LOC': {
level: 'error',
fatal: true,
rule: 'Replace {{post.author.location}} with {{post.primary_author.location}} or {{post.authors.[#].location}}',
details: oneLineTrim`The usage of {{post.author.location}} is no longer supported and should be replaced with either {{post.primary_author.location}}
or {{post.authors.[#].location}}.
${multiAuthorDesc}
${authorHelperDocs}`,
regex: /{{\s*?post\.author\.location\s*?}}/g,
helper: '{{post.author.location}}'
},
'GS001-DEPR-PAUTH-WEB': {
level: 'error',
fatal: true,
rule: 'Replace {{post.author.website}} with {{post.primary_author.website}} or {{post.authors.[#].website}}',
details: oneLineTrim`The usage of {{post.author.website}} is no longer supported and should be replaced with either {{post.primary_author.website}}
or {{post.authors.[#].website}}.
${multiAuthorDesc}
${authorHelperDocs}`,
regex: /{{\s*?post\.author\.website\s*?}}/g,
helper: '{{post.author.website}}'
},
'GS001-DEPR-PAUTH-TW': {
level: 'error',
fatal: true,
rule: 'Replace {{post.author.twitter}} with {{post.primary_author.twitter}} or {{post.authors.[#].twitter}}',
details: oneLineTrim`The usage of {{post.author.twitter}} is no longer supported and should be replaced with either {{post.primary_author.twitter}}
or {{post.authors.[#].twitter}}.
${multiAuthorDesc}
${authorHelperDocs}`,
regex: /{{\s*?post\.author\.twitter\s*?}}/g,
helper: '{{post.author.twitter}}'
},
'GS001-DEPR-PAUTH-FB': {
level: 'error',
fatal: true,
rule: 'Replace {{post.author.facebook}} with {{post.primary_author.facebook}} or {{post.authors.[#].facebook}}',
details: oneLineTrim`The usage of {{post.author.facebook}} is no longer supported and should be replaced with either {{post.primary_author.facebook}}
or {{post.authors.[#].facebook}}.
${multiAuthorDesc}
${authorHelperDocs}`,
regex: /{{\s*?post\.author\.facebook\s*?}}/g,
helper: '{{post.author.facebook}}'
},
'GS001-DEPR-PAUTH-PIMG': {
level: 'error',
fatal: true,
rule: 'Replace {{post.author.profile_image}} with {{post.primary_author.profile_image}} or {{post.authors.[#].profile_image}}',
details: oneLineTrim`The usage of {{post.author.profile_image}} is no longer supported and should be replaced with either {{post.primary_author.profile_image}}
or {{post.authors.[#].profile_image}}.
${multiAuthorDesc}
${authorHelperDocs}`,
regex: /{{\s*?post\.author\.profile_image\s*?}}/g,
helper: '{{post.author.profile_image}}'
},
'GS001-DEPR-PAUTH-CIMG': {
level: 'error',
fatal: true,
rule: 'Replace {{post.author.cover_image}} with {{post.primary_author.cover_image}} or {{post.authors.[#].cover_image}}',
details: oneLineTrim`The usage of {{post.author.cover_image}} is no longer supported and should be replaced with either {{post.primary_author.cover_image}}
or {{post.authors.[#].cover_image}}.
${multiAuthorDesc}
${authorHelperDocs}`,
regex: /{{\s*?post\.author\.cover_image\s*?}}/g,
helper: '{{post.author.cover_image}}'
},
'GS001-DEPR-PAUTH-URL': {
level: 'error',
fatal: true,
rule: 'Replace {{post.author.url}} with {{post.primary_author.url}} or {{post.authors.[#].url}}',
details: oneLineTrim`The usage of {{post.author.url}} is no longer supported and should be replaced with either {{post.primary_author.url}}
or {{post.authors.[#].url}}.
${multiAuthorDesc}
${authorHelperDocs}`,
regex: /{{\s*?post\.author\.url\s*?}}/g,
helper: '{{post.author.url}}'
},
'GS001-DEPR-PAID': {
level: 'error',
fatal: true,
rule: 'Replace {{post.author_id}} code with {{post.primary_author.id}}',
details: oneLineTrim`The {{post.author_id}} attribute in post context was removed
Instead of {{post.author_id}} you need to use {{post.primary_author.id}}.
Find more information about the object attributes of post here.`,
regex: /{{\s*?post\.author_id\s*?}}/g,
helper: '{{post.author_id}}'
},
'GS001-DEPR-NAUTH': {
level: 'error',
fatal: true,
rule: 'Replace ../author with ../primary_author or ../authors.[#]',
details: oneLineTrim`The usage of ../author is no longer supported and should be replaced with either ../primary_author
or ../authors.[#].
${multiAuthorDesc}
${authorHelperDocs}`,
regex: /{{\s*?(?:#|#if)?\s*?\.\.\/author(?:\.\S*?)?\s*?}}/g,
helper: '{{../author}}'
},
'GS001-DEPR-IUA': {
level: 'error',
fatal: false,
rule: 'Replace {{img_url author.*}} with {{img_url primary_author.*}} or .{img_url author.[#].*}}',
details: oneLineTrim`The usage of {{img_url author.*}} is no longer supported and should be replaced with either {{img_url primary_author.*}}
or {{img_url author.[#].*}}.
${multiAuthorDesc}
${authorHelperDocs}`,
regex: /{{\s*?img_url\s*?(author.).*}}/g,
helper: '{{img_url author.*}}'
},
'GS001-DEPR-AC': {
level: 'error',
fatal: false,
rule: 'Replace {{author.cover}} with {{primary_author.cover_image}}',
details: oneLineTrim`The cover attribute was replaced with cover_image.
Instead of {{author.cover}} you need to use
{{primary_author.cover_image}} or {{authors.[#].cover_image}}.
Find more information about the object attributes of author here.`,
regex: /{{\s*?author\.cover\s*?}}/g,
helper: '{{author.cover}}'
},
'GS001-DEPR-AIMG': {
level: 'error',
fatal: false,
rule: 'Replace {{author.image}} with {{primary_author.profile_image}} or {{authors.[#].profile_image}}',
details: oneLineTrim`The image attribute was replaced with profile_image.
Instead of {{author.image}}, you need to use
{{primary_author.profile_image}} or {{authors.[#].profile_image}}.
Find more information about the object attributes of author here.`,
regex: /{{\s*?author\.image\s*?}}/g,
helper: '{{author.image}}'
},
'GS001-DEPR-PAC': {
level: 'error',
fatal: false,
rule: 'Replace {{post.author.cover}} with {{post.primary_author.cover_image}} or {{post.authors.[#].cover_image}}',
details: oneLineTrim`The cover attribute was replaced with cover_image.
Instead of {{post.author.cover}}, you need to use
{{post.primary_author.cover_image}} or {{post.authors.[#].cover_image}}.
Find more information about the object attributes of author here.`,
regex: /{{\s*?post\.author\.cover\s*?}}/g,
helper: '{{post.author.cover}}'
},
'GS001-DEPR-AUTH-INCL': {
level: 'error',
fatal: false,
rule: `Replace include="author" with include="authors"`,
details: oneLineTrim`The usage of {{#get "posts" include="author"}} is no longer supported and should be replaced with {{#get "posts" include="authors"}}.
Find more information about the {{get}} helper here.`,
// This regex seems only to work properly with the escaped characters. Removing them resulted
// in not detecting the wrong usage.
regex: /{{\s*?#get.+include=("|')\s*?([\w\[\]]+,{1}\s*?)*?(\s*?author\s*?)(\s*,{1}\s?[\w\[\]]+)*?\s*?("|')(.*)}}/g, // eslint-disable-line no-useless-escape
helper: 'include="author"'
},
'GS001-DEPR-AUTH-FIELD': {
level: 'error',
fatal: false,
rule: `fields="author" should be replaced with fields="authors"`,
details: oneLineTrim`The usage of {{#get "posts" fields="author"}} is no longer supported and should be replaced with
{{#get "posts" fields="primary_author"}} or {{#get "posts" fields="authors.[#]"}}.
Find more information about the {{get}} helper here.`,
// This regex seems only to work properly with the escaped characters. Removing them resulted
// in not detecting the wrong usage.
regex: /{{\s*?#get.+fields=("|')\s*?([\w\[\]]+,{1}\s*?)*?(\s*?author\s*?)(\s*,{1}\s?[\w\[\]]+)*?\s*?("|')(.*)}}/g, // eslint-disable-line no-useless-escape
helper: 'fields="author"'
},
'GS001-DEPR-AUTH-FILT': {
level: 'error',
fatal: false,
rule: `filter="author:[...]" should be replaced with filter="authors:[...]"`,
details: oneLineTrim`The usage of {{#get "posts" filter="author:[...]"}} is no longer supported and should be replaced with {{#get "posts" filter="authors:[...]"}}.
Find more information about the {{get}} helper here.`,
// This regex seems only to work properly with the escaped characters. Removing them resulted
// in not detecting the wrong usage.
regex: /{{\s*?#get.+filter=("|')\s*?([\w\[\]]+,{1}\s*?)*?(\s*?author:).*("|')(.*)}}/g, // eslint-disable-line no-useless-escape
helper: 'filter="author:[...]"'
},
'GS001-DEPR-AUTHBL': {
level: 'error',
fatal: false,
rule: 'The {{#author}} block helper should be replaced with {{#primary_author}} or {{#foreach authors}}...{{/foreach}}',
details: oneLineTrim`The usage of {{#author}} block helper outside of author.hbs is no longer supported and
should be replaced with {{#primary_author}} or {{#foreach authors}}...{{/foreach}}.
${multiAuthorDesc}
${authorHelperDocs}`,
regex: /{{\s*?#author\s*?}}/g,
notValidIn: 'author.hbs',
helper: '{{#author}}'
},
'GS001-DEPR-PAIMG': {
level: 'error',
fatal: false,
rule: 'Replace {{post.author.image}} with {{post.primary_author.profile_image}} or {{post.authors.[#].profile_image}}',
details: oneLineTrim`The image attribute was replaced with profile_image.
Instead of {{post.author.image}}, you need to use
{{post.primary_author.profile_image}} or {{post.authors.[#].profile_image}}.
Find more information about the object attributes of author here.`,
regex: /{{\s*?post\.author\.image\s*?}}/g,
helper: '{{post.author.image}}'
},
'GS001-DEPR-CON-AUTH': {
level: 'error',
fatal: false,
rule: `The {{#if author.*}} block helper should be replaced with {{#if primary_author.*}}
or {{#if authors.[#].*}}`,
details: oneLineTrim`The usage of {{#if author.*}} is no longer supported and should be replaced with {{#if primary_author.*}}
or {{#if authors.[#].*}}.
${multiAuthorDesc}
${authorHelperDocs}`,
regex: /{{\s*?#if\s*?(author)(?:\.\w+)*?\s*?}}/g,
helper: '{{#if author.*}}'
},
'GS001-DEPR-CON-PAUTH': {
level: 'error',
fatal: false,
rule: `The {{#if post.author.*}} block helper should be replaced with {{#if post.primary_author.*}}
or {{#if post.authors.[#].*}}`,
details: oneLineTrim`The usage of {{#if post.author.*}} is no longer supported and should be replaced with {{#if post.primary_author.*}}
or {{#if post.authors.[#].*}}.
${multiAuthorDesc}
${authorHelperDocs}`,
regex: /{{\s*?#if\s*?(?:post\.)(author)(?:\.\w+)*?\s*?}}/g,
helper: '{{#if post.author.*}}'
},
'GS001-DEPR-CON-AC': {
level: 'error',
fatal: false,
rule: 'Replace {{#if author.cover}} with {{#if primary_author.cover_image}} or {{#if authors.[#].cover_image}}',
details: oneLineTrim`The cover attribute was replaced with cover_image.
Instead of {{#if author.cover}}, you need to use
{{#if primary_author.cover_image}} or {{#if authors.[#].cover_image}}.
Find more information about the object attributes of author here.`,
regex: /{{\s*?#if\s*?author\.cover\s*?}}/g,
helper: '{{#if author.cover}}'
},
'GS001-DEPR-CON-AIMG': {
level: 'error',
fatal: false,
rule: 'Replace {{#if author.image}} with {{#if primary_author.profile_image}} or {{#if authors.[#].profile_image}}',
details: oneLineTrim`The image attribute was replaced with profile_image.
Instead of {{#if author.image}}, you need to use
{{#if primary_author.profile_image}} or {{#if authors.[#].profile_image}}.
Find more information about the object attributes of author here.`,
regex: /{{\s*?#if\s*?author\.image\s*?}}/g,
helper: '{{#if author.image}}'
},
'GS001-DEPR-CON-PAC': {
level: 'error',
fatal: false,
rule: 'Replace {{#if post.author.cover}} with {{#if post.primary_author.cover_image}} or {{#if post.authors.[#].cover_image}}',
details: oneLineTrim`The cover attribute was replaced with cover_image.
Instead of {{#if post.author.cover}}, you need to use
{{#if post.primary_author.cover_image}} or {{#if post.authors.[#].cover_image}}.
Find more information about the object attributes of author here.`,
regex: /{{\s*?#if\s*?post\.author\.cover\s*?}}/g,
helper: '{{#if post.author.cover}}'
},
'GS001-DEPR-CON-PAIMG': {
level: 'error',
fatal: false,
rule: 'Replace {{#if post.author.image}} with {{#if post.primary_author.profile_image}} or {{#if post.authors.[#].profile_image}}',
details: oneLineTrim`The image attribute was replaced with profile_image.
Instead of {{#if post.author.image}}, you need to use
{{#if post.primary_author.profile_image}} or {{#if post.authors.[#].profile_image}}.
Find more information about the object attributes of author here.`,
regex: /{{\s*?#if\s*?post\.author\.image\s*?}}/g,
helper: '{{#if post.author.image}}'
},
'GS001-DEPR-LABS-MEMBERS': {
level: 'error',
rule: 'Replace {{@labs.members}} with {{@site.members_enabled}}',
details: oneLineTrim`Usage of {{@labs.members}} is no longer supported and should be replaced with {{@site.members_enabled}}
Find more information about the @site property here.`,
regex: /@labs\.members/g,
helper: '{{@labs.members}}'
},
'GS001-DEPR-SPL': {
level: 'error',
fatal: true,
rule: 'Remove uses of {{@site.permalinks}}',
details: oneLineTrim`With the introduction of Dynamic Routing, you can define multiple permalinks.
The {{@site.permalinks}} property will therefore no longer be used and should be removed from the theme.
Find more information about the @site property here.`,
regex: /{{\s*?@site\.permalinks\s*?}}/g,
helper: '{{@site.permalinks}}'
},
'GS001-DEPR-BPL': {
level: 'error',
fatal: true,
rule: 'Remove uses of {{@blog.permalinks}}',
details: oneLineTrim`With the introduction of Dynamic Routing, you can define multiple permalinks.
The {{@blog.permalinks}} property will therefore no longer be used and should be removed from the theme.
Find more information about Ghost data helpers here.`,
regex: /{{\s*?@blog\.permalinks\s*?}}/g,
helper: '{{@blog.permalinks}}'
},
'GS001-DEPR-SGF': {
level: 'error',
fatal: true,
rule: 'Replace {{@site.ghost_foot}} with {{ghost_foot}}',
details: oneLineTrim`The usage of {{@site.ghost_foot}} is no longer supported and should be replaced with {{ghost_foot}}.
Find more information about the {{ghost_foot}} property here.`,
regex: /{{\s*?@site\.ghost_foot\s*?}}/g,
helper: '{{@site.ghost_foot}}'
},
'GS001-DEPR-SGH': {
level: 'error',
fatal: true,
rule: 'Replace {{@site.ghost_head}} with {{ghost_head}}',
details: oneLineTrim`The usage of {{@site.ghost_head}} is no longer supported and should be replaced with {{ghost_head}}.
Find more information about the {{ghost_head}} property here.`,
regex: /{{\s*?@site\.ghost_head\s*?}}/g,
helper: '{{@site.ghost_head}}'
},
'GS001-DEPR-LANG': {
level: 'error',
rule: 'Replace {{lang}} with {{@site.locale}}',
details: oneLineTrim`The usage of {{lang}} is no longer supported and should be replaced with {{@site.locale}}.
Find more information about the @site.locale property here.`,
regex: /{{\s*?lang\s*?}}/g,
helper: '{{lang}}'
},
'GS001-DEPR-SITE-LANG': {
level: 'error',
fatal: false,
rule: 'Replace {{@site.lang}} with {{@site.locale}}',
details: oneLineTrim`The usage of {{@site.lang}} is no longer supported and shoud be replaced with {{@site.locale}}.
Find more information about the @site.locale property here.`,
regex: /@site\.lang/g,
helper: '{{@site.lang}}'
},
'GS001-DEPR-USER-GET': {
level: 'error',
fatal: false,
rule: `Replace {{#get "users"}} with {{#get "authors"}}`,
details: oneLineTrim`The usage of {{#get "users"}} is no longer supported and should be replaced with {{#get "authors"}}.
Find more information about the {{get}} helper here.`,
regex: /{{\s*?#get ("|')\s*users("|')\s*/g,
helper: '{{#get "users"}}'
},
'GS001-DEPR-CURR-SYM': {
level: 'error',
fatal: false,
rule: 'Replace {{[#].currency_symbol}} with {{price currency=currency}}.',
details: oneLineTrim`The currency_symbol attribute is no longer supported in favour of passing the currency to updated {{price}} helper.
Find more information about the updated {{price}} helper here.`,
helper: '{{[#].currency_symbol}}',
regex: /currency_symbol/g
},
'GS010-PJ-CUST-THEME-SETTINGS-DESCRIPTION-LENGTH': {
level: 'warning',
rule: 'package.json property config.custom contains an entry with a description that is too long',
details: oneLineTrim`config.custom entry description should be less than 100 characters so that it is displayed correctly.
Check the config.custom documentation for further information.`
},
'GS010-PJ-CUST-THEME-SETTINGS-VISIBILITY-SYNTAX': {
level: 'error',
rule: 'package.json property config.custom contains an entry with visibility that contains invalid syntax',
details: oneLineTrim`config.custom entry visibility should be valid nql.
Check the config.custom documentation for further information.`
},
'GS010-PJ-CUST-THEME-SETTINGS-VISIBILITY-VALUE': {
level: 'error',
rule: 'package.json property config.custom contains an entry with visibility that references a custom setting that does not exist',
details: oneLineTrim`config.custom entry visibility should be only reference other custom settings.
Check the config.custom documentation for further information.`
},
'GS110-NO-MISSING-PAGE-BUILDER-USAGE': {
level: 'error',
rule: 'Not all page features are being used',
details: oneLineTrim`This error only applies to pages created with the Beta editor. Some page features used by Ghost via the {{@page}} global are not implemented in this theme.
Find more information about the {{@page}} global here.`
},
'GS110-NO-UNKNOWN-PAGE-BUILDER-USAGE': {
level: 'error',
fatal: true,
rule: 'Unsupported page builder feature used',
details: oneLineTrim`A page feature used via the {{@page}} global was detected but is not supported by this version of Ghost. Please upgrade to the latest version for full access.
You can find more information about the {{@page}} global here.`
},
'GS120-NO-UNKNOWN-GLOBALS': {
level: 'error',
rule: 'No unknown global helper used',
details: oneLineTrim`A global helper was detected that is not supported by this version of Ghost. Check the
helpers documentation for further information.`
}
};
knownHelpers = _.union(previousKnownHelpers, knownHelpers);
templates = _.union(previousTemplates, templates);
// Merge the previous rules into the new rules, but overwrite any specified property,
// as well as adding any new rule to the spec.
// Furthermore, replace the usage of the old doc URLs that we're linking to, with the
// new version.
delete previousRules['GS010-PJ-GHOST-API-V01'];
rules = _.each(_.merge({}, previousRules, rules), function replaceDocsUrl(value) {
value.details = value.details.replace(prevDocsBaseUrlRegEx, docsBaseUrl);
});
module.exports = {
knownHelpers: knownHelpers,
templates: templates,
rules: rules,
/**
* Copy of Ghost defaults for https://github.com/TryGhost/Ghost/blob/e25f1df0ae551c447da0d319bae06eadf9665444/core/frontend/services/theme-engine/config/defaults.json
*/
defaultPackageJSON: {
posts_per_page: 5,
card_assets: true
}
};