From 2068489d671a7957c11b93204c016aadec5a5482 Mon Sep 17 00:00:00 2001 From: Aileen Nowak Date: Mon, 16 Apr 2018 18:06:21 +0800 Subject: [PATCH] Fixed linting issues --- app/index.js | 12 +- app/public/.eslintrc.js | 1 + app/public/js/gscan.js | 62 +-- bin/cli.js | 10 +- lib/checker.js | 6 +- lib/checks/001-deprecations.js | 16 +- lib/checks/002-comment-id.js | 4 +- lib/checks/005-template-compile.js | 14 +- lib/checks/010-package-json.js | 4 +- lib/checks/020-theme-structure.js | 28 +- lib/checks/030-assets.js | 5 +- lib/checks/040-ghost-head-foot.js | 20 +- lib/faker/index.js | 4 +- lib/format.js | 9 +- lib/promised-fs.js | 3 +- lib/read-theme.js | 16 +- lib/read-zip.js | 18 +- lib/spec.js | 730 ++++++++++++++--------------- package.json | 4 +- shipitfile.js | 6 +- test/001-deprecations.test.js | 5 +- test/005-template-compile.test.js | 3 +- test/010-package-json.test.js | 3 +- test/020-theme-structure.test.js | 5 +- test/030-assets.test.js | 3 +- test/040-ghost-head-foot.test.js | 3 +- test/general.test.js | 19 +- test/utils.js | 12 +- 28 files changed, 513 insertions(+), 512 deletions(-) diff --git a/app/index.js b/app/index.js index 611fd63f5..09fe27bc1 100644 --- a/app/index.js +++ b/app/index.js @@ -13,7 +13,6 @@ var express = require('express'), app = express(), scanHbs = hbs.create(); - // Configure express app.set('x-powered-by', false); app.set('query parser', false); @@ -42,15 +41,15 @@ app.get('/example/', function (req, res) { }); }); -app.post('/', - upload.single('theme'), +app.post('/', + upload.single('theme'), function (req, res, next) { var zip = { - path: req.file.path, + path: req.file.path, name: req.file.originalname }; debug('Uploaded: ' + zip.name + ' to ' + zip.path); - + gscan.checkZip(zip).then(function processResult(theme) { debug('Checked: ' + zip.name); res.theme = theme; @@ -76,9 +75,10 @@ app.use(function (req, res, next) { next(new errors.NotFoundError({message: 'Page not found'})); }); +// eslint-disable-next-line no-unused-vars app.use(function (err, req, res, next) { req.err = err; res.render('error', {message: err.message, stack: err.stack}); }); -server.start(app); \ No newline at end of file +server.start(app); diff --git a/app/public/.eslintrc.js b/app/public/.eslintrc.js index 3022af196..7eeb941e6 100644 --- a/app/public/.eslintrc.js +++ b/app/public/.eslintrc.js @@ -1,3 +1,4 @@ +/* eslint-env node */ module.exports = { plugins: ['ghost'], extends: [ diff --git a/app/public/js/gscan.js b/app/public/js/gscan.js index 3d22591cf..856064a48 100644 --- a/app/public/js/gscan.js +++ b/app/public/js/gscan.js @@ -1,48 +1,52 @@ (function ($) { - function timeSince(date) { - var seconds = Math.floor((new Date() - date) / 1000); var interval = Math.floor(seconds / 31536000); if (interval >= 1) { - if(interval===1) - return interval + " year"; - else - return interval + " years"; + if (interval === 1) { + return interval + ' year'; + } else { + return interval + ' years'; + } } interval = Math.floor(seconds / 2592000); if (interval >= 1) { - if(interval===1) - return interval + " month"; - else - return interval + " months"; + if (interval === 1) { + return interval + ' month'; + } else { + return interval + ' months'; + } } interval = Math.floor(seconds / 86400); if (interval >= 1) { - if(interval===1) - return interval + " day"; - else - return interval + " days"; + if (interval === 1) { + return interval + ' day'; + } else { + return interval + ' days'; + } } interval = Math.floor(seconds / 3600); if (interval >= 1) { - if(interval===1) - return interval + " hour"; - else - return interval + " hours"; + if (interval === 1) { + return interval + ' hour'; + } else { + return interval + ' hours'; + } } interval = Math.floor(seconds / 60); if (interval >= 1) { - if(interval===1) - return interval + " minute"; - else - return interval + " minutes"; + if (interval === 1) { + return interval + ' minute'; + } else { + return interval + ' minutes'; + } + } + if (Math.floor(seconds) === 1) { + return Math.floor(seconds) + ' second'; + } else { + return Math.floor(seconds) + ' seconds'; } - if(Math.floor(seconds)===1) - return Math.floor(seconds) + " second"; - else - return Math.floor(seconds) + " seconds"; } $(document).ready(function ($) { if ($('#theme')[0]) { @@ -56,6 +60,7 @@ /** Latest News **/ if ($('.myblogs-latest-news').length && window.ghost) { $.get(window.ghost.url.api('posts', {limit: 1, include: 'author'}), function (json) { + /* eslint-disable camelcase */ var item = json.posts[0], parsed_date = new Date(item.published_at), image_url = item.author.image.substr(0, 2) === '//' ? item.author.image : '//dev.ghost.org/' + item.author.image, @@ -63,13 +68,14 @@ '' + '' + ' by ' + item.author.name + ''; - $(".myblogs-latest-news").html(news_html); + $('.myblogs-latest-news').html(news_html); + + /* eslint-disable camelcase */ }); } /** Toggle Details **/ if ($('.toggle-details').length) { - $('.toggle-details').on('click', function () { if ($(this).find('~ .details').is(':hidden')) { $(this).find('~ .details').show(); diff --git a/bin/cli.js b/bin/cli.js index ce1e40e12..75fbec9a5 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -2,9 +2,9 @@ var pkgJson = require('../package.json'), program = require('commander'), - _ = require('lodash'), - chalk = require('chalk'), - gscan = require('../lib'), + _ = require('lodash'), + chalk = require('chalk'), + gscan = require('../lib'), themePath = '', levels; @@ -27,6 +27,7 @@ levels = { feature: chalk.green }; +/* eslint-disable no-console */ function outputResult(result) { console.log('-', levels[result.level](result.level), result.rule); } @@ -70,7 +71,8 @@ if (!program.args.length) { return err.code === 'ENOTDIR'; }, function (err) { console.error(err.message); - console.error('Did you mean to add the -z flag to read a zip file?') + console.error('Did you mean to add the -z flag to read a zip file?'); + /* eslint-enable no-console */ }); } } diff --git a/lib/checker.js b/lib/checker.js index 7ea5d536a..b49869a25 100644 --- a/lib/checker.js +++ b/lib/checker.js @@ -1,7 +1,7 @@ -var Promise = require('bluebird'), - _ = require('lodash'), +var Promise = require('bluebird'), + _ = require('lodash'), requireDir = require('require-dir'), - readTheme = require('./read-theme'), + readTheme = require('./read-theme'), checker, checks; diff --git a/lib/checks/001-deprecations.js b/lib/checks/001-deprecations.js index a5595cd13..718e3f625 100644 --- a/lib/checks/001-deprecations.js +++ b/lib/checks/001-deprecations.js @@ -1,4 +1,4 @@ -var _ = require('lodash'), +var _ = require('lodash'), path = require('path'), fs = require('fs-extra'), checkDeprecations; @@ -121,19 +121,19 @@ checkDeprecations = function checkDeprecations(theme, themePath) { ruleCode: 'GS001-DEPR-C0H' }, { - cssRegEx: /\.archive-template[\s\{]/g, + cssRegEx: /\.archive-template[\s{]/g, className: '.archive-template', css: true, ruleCode: 'GS001-DEPR-CSS-AT' }, { - cssRegEx: /\.page[\s\{]/g, + cssRegEx: /\.page[\s{]/g, className: '.page', css: true, ruleCode: 'GS001-DEPR-CSS-PA' }, { - cssRegEx: /\.page-template-\w+[\s\{]/g, + cssRegEx: /\.page-template-\w+[\s{]/g, className: '.page-template-slug', css: true, ruleCode: 'GS001-DEPR-CSS-PATS' @@ -142,7 +142,7 @@ checkDeprecations = function checkDeprecations(theme, themePath) { _.each(checks, function (check) { _.each(theme.files, function (themeFile) { - var template = themeFile.file.match(/^[^\/]+.hbs$/) || themeFile.file.match(/^partials[\/\\]+(.*)\.hbs$/), + var template = themeFile.file.match(/^[^/]+.hbs$/) || themeFile.file.match(/^partials[/\\]+(.*)\.hbs$/), css = themeFile.file.match(/\.css/), cssDeprecations; @@ -184,9 +184,9 @@ checkDeprecations = function checkDeprecations(theme, themePath) { } }); - if (theme.results.pass.indexOf(check.ruleCode) === -1 && !theme.results.fail.hasOwnProperty(check.ruleCode)) { - theme.results.pass.push(check.ruleCode); - } + if (theme.results.pass.indexOf(check.ruleCode) === -1 && !theme.results.fail.hasOwnProperty(check.ruleCode)) { + theme.results.pass.push(check.ruleCode); + } }); return theme; diff --git a/lib/checks/002-comment-id.js b/lib/checks/002-comment-id.js index 7b0dc9183..c3bb593b2 100644 --- a/lib/checks/002-comment-id.js +++ b/lib/checks/002-comment-id.js @@ -1,7 +1,7 @@ var _ = require('lodash'), checkCommentID; -checkCommentID = function checkCommentID(theme, themePath) { +checkCommentID = function checkCommentID(theme) { var checks = [ // Check 1: // Look for instances of: this.page.identifier = 'ghost-{{id}}'; @@ -20,7 +20,7 @@ checkCommentID = function checkCommentID(theme, themePath) { _.each(checks, function (check) { _.each(theme.files, function (themeFile) { - var template = themeFile.file.match(/^[^\/]+.hbs$/) || themeFile.file.match(/^partials[\/\\]+(.*)\.hbs$/); + var template = themeFile.file.match(/^[^/]+.hbs$/) || themeFile.file.match(/^partials[/\\]+(.*)\.hbs$/); if (template) { if (themeFile.content.match(check.regex)) { diff --git a/lib/checks/005-template-compile.js b/lib/checks/005-template-compile.js index f4ef2d003..63143efb3 100644 --- a/lib/checks/005-template-compile.js +++ b/lib/checks/005-template-compile.js @@ -1,6 +1,6 @@ -var _ = require('lodash'), - hbs = require('express-hbs'), - spec = require('../spec.js'), +var _ = require('lodash'), + hbs = require('express-hbs'), + spec = require('../spec.js'), checkTemplatesCompile, fakeData = require('../faker'); @@ -30,7 +30,7 @@ checkTemplatesCompile = function checkTemplatesCompile(theme) { }); _.each(theme.files, function (themeFile) { - var templateTest = themeFile.file.match(/^[^\/]+.hbs$/), + var templateTest = themeFile.file.match(/^[^/]+.hbs$/), compiled; // If this is a main template, test compiling it properly @@ -39,7 +39,9 @@ checkTemplatesCompile = function checkTemplatesCompile(theme) { // When we read the theme, we precompile, here we compile with registered partials and helpers // Which will let us detect any missing partials, helpers or parse errors compiled = localHbs.handlebars.compile(themeFile.content, { - knownHelpers: _.zipObject(spec.knownHelpers, _.map(spec.knownHelpers, function() { return true; })), + knownHelpers: _.zipObject(spec.knownHelpers, _.map(spec.knownHelpers, function () { + return true; + })), knownHelpersOnly: true }); @@ -50,7 +52,7 @@ checkTemplatesCompile = function checkTemplatesCompile(theme) { if (error.message.match(/you specified knownhelpersOnly/gi)) { try { missingHelper = error.message.match(/but\sused\sthe\sunknown\shelper\s(.*)\s-/)[1]; - } catch(err) { + } catch (err) { missingHelper = 'unknown helper name'; } diff --git a/lib/checks/010-package-json.js b/lib/checks/010-package-json.js index bccb7dd90..6a3e96a60 100644 --- a/lib/checks/010-package-json.js +++ b/lib/checks/010-package-json.js @@ -53,7 +53,7 @@ _private.validatePackageJSONFields = function validatePackageJSONFields(packageJ failed.push(packageJSONValidationRules.nameIsLowerCase); } - if (packageJSON.name && !packageJSON.name.match(/^[a-z0-9]+(\-?[a-z0-9]+)*$/gi)) { + if (packageJSON.name && !packageJSON.name.match(/^[a-z0-9]+(-?[a-z0-9]+)*$/gi)) { failed.push(packageJSONValidationRules.nameIsHyphenated); } @@ -85,7 +85,7 @@ _private.getFailedRules = function getFailedRules(keys) { })); }; -module.exports = function checkPackageJSON(theme, themePath) { +module.exports = function checkPackageJSON(theme) { var packageJSONPath = path.join(theme.path, packageJSONFile); // CASE: package.json must be present (if not, all validation rules fail) diff --git a/lib/checks/020-theme-structure.js b/lib/checks/020-theme-structure.js index 09c60421d..ea810700d 100644 --- a/lib/checks/020-theme-structure.js +++ b/lib/checks/020-theme-structure.js @@ -1,4 +1,4 @@ -var _ = require('lodash'), +var _ = require('lodash'), checkThemeStructure; // TODO: template inspection @@ -18,19 +18,19 @@ var _ = require('lodash'), checkThemeStructure = function checkThemeStructure(theme) { var checks = [ - { - path: 'index.hbs', - ruleCode: 'GS020-INDEX-REQ' - }, - { - path: 'post.hbs', - ruleCode: 'GS020-POST-REQ' - }, - { - path: 'default.hbs', - ruleCode: 'GS020-DEF-REC' - } - ]; + { + path: 'index.hbs', + ruleCode: 'GS020-INDEX-REQ' + }, + { + path: 'post.hbs', + ruleCode: 'GS020-POST-REQ' + }, + { + path: 'default.hbs', + ruleCode: 'GS020-DEF-REC' + } + ]; _.each(checks, function (check) { if (!_.some(theme.files, {file: check.path})) { diff --git a/lib/checks/030-assets.js b/lib/checks/030-assets.js index bb26ad640..c02d438aa 100644 --- a/lib/checks/030-assets.js +++ b/lib/checks/030-assets.js @@ -1,4 +1,4 @@ -var _ = require('lodash'), +var _ = require('lodash'), path = require('path'), fs = require('fs'), checkAssets; @@ -33,10 +33,9 @@ checkAssets = function checkAssets(theme, themePath) { if (stats.isSymbolicLink()) { failures.push({ref: theme.file}); } - } catch(err) { + } catch (err) { // ignore } - }); if (failures.length > 0) { diff --git a/lib/checks/040-ghost-head-foot.js b/lib/checks/040-ghost-head-foot.js index db97a35f0..65e9e0e27 100644 --- a/lib/checks/040-ghost-head-foot.js +++ b/lib/checks/040-ghost-head-foot.js @@ -1,17 +1,17 @@ -var _ = require('lodash'), +var _ = require('lodash'), checkGhostHeadFoot; checkGhostHeadFoot = function checkGhostHeadFoot(theme) { var checks = [ - { - helper: 'ghost_head', - ruleCode: 'GS040-GH-REQ' - }, - { - helper: 'ghost_foot', - ruleCode: 'GS040-GF-REQ' - } - ]; + { + helper: 'ghost_head', + ruleCode: 'GS040-GH-REQ' + }, + { + helper: 'ghost_foot', + ruleCode: 'GS040-GF-REQ' + } + ]; _.each(checks, function (check) { if (!theme.helpers || !theme.helpers.hasOwnProperty(check.helper)) { diff --git a/lib/faker/index.js b/lib/faker/index.js index c2311b185..68b0f3c4e 100644 --- a/lib/faker/index.js +++ b/lib/faker/index.js @@ -28,8 +28,8 @@ var fakeData = function fakeData(themeFile) { title: 'The highs and lows of hills', slug: 'the-highs-and-low-of-hills', excerpt: 'Hills can be hilly', - content: "Hills can be hilly when they are like hills", - url: "http://talltalesofhighhills.com/the-highs-and-low-of-hills", + content: 'Hills can be hilly when they are like hills', + url: 'http://talltalesofhighhills.com/the-highs-and-low-of-hills', feature_image: '/content/2017/07/highhill.jpg', featured: 0, page: 0, diff --git a/lib/format.js b/lib/format.js index 57b6e312c..6eadd0a0c 100644 --- a/lib/format.js +++ b/lib/format.js @@ -1,5 +1,5 @@ -var _ = require('lodash'), - spec = require('./spec'), +var _ = require('lodash'), + spec = require('./spec'), format, calcScore, levelWeights = { @@ -55,7 +55,7 @@ format = function format(theme, options) { } theme.results[rule.level].push(_.extend({}, _.merge({}, {fatal: false}, rule), info, {code: code})); - stats[rule.level]++; + stats[rule.level] += 1; processedCodes.push(code); } }); @@ -65,7 +65,7 @@ format = function format(theme, options) { _.each(theme.results.pass, function (code, index) { var rule = spec.rules[code]; theme.results.pass[index] = _.extend({}, rule, {code: code}); - stats[rule.level]++; + stats[rule.level] += 1; processedCodes.push(code); }); @@ -82,5 +82,4 @@ format = function format(theme, options) { return theme; }; - module.exports = format; diff --git a/lib/promised-fs.js b/lib/promised-fs.js index cd06dadcd..51e9c14f3 100644 --- a/lib/promised-fs.js +++ b/lib/promised-fs.js @@ -1,7 +1,6 @@ -var fs = require('fs-extra'), +var fs = require('fs-extra'), Promise = require('bluebird'); - module.exports = fs; module.exports.readDir = Promise.promisify(fs.readdir); module.exports.remove = Promise.promisify(fs.remove); diff --git a/lib/read-theme.js b/lib/read-theme.js index 996a8a00e..1f395eb7d 100644 --- a/lib/read-theme.js +++ b/lib/read-theme.js @@ -10,15 +10,17 @@ var Promise = require('bluebird'), readThemeStructure, extractTemplates, extractCustomTemplates, - readHbsFiles; + readHbsFiles, + processHelpers; readThemeStructure = function readThemeFiles(themePath, subPath, arr) { themePath = path.join(themePath, '.'); subPath = subPath || ''; var tmpPath = os.tmpdir(), - inTmp = themePath.substr(0, tmpPath.length) === tmpPath, - arr = arr || []; + inTmp = themePath.substr(0, tmpPath.length) === tmpPath; + + arr = arr || []; var makeResult = function makeResult(result, subFilePath, ext) { result.push({ @@ -69,7 +71,7 @@ readHbsFiles = function readHbsFiles(theme) { return Promise.map(_.filter(theme.files, {ext: '.hbs'}), function (themeFile) { return pfs.readFile(path.join(theme.path, themeFile.file), 'utf8').then(function (content) { - var partialMatch = themeFile.file.match(/^partials[\/\\]+(.*)\.hbs$/); + var partialMatch = themeFile.file.match(/^partials[/\\]+(.*)\.hbs$/); themeFile.content = content; try { @@ -128,7 +130,7 @@ extractCustomTemplates = function extractCustomTemplates(allTemplates) { name = name.replace(/^post-|page-|custom-/, ''); name = name.replace(/-/g, ' '); name = name.replace(/\b\w/g, function (letter) { - return letter.toUpperCase() + return letter.toUpperCase(); }); return name.trim(); @@ -157,7 +159,7 @@ extractCustomTemplates = function extractCustomTemplates(allTemplates) { toReturn.push({ filename: templateName, name: generateName(templateName), - 'for': generateFor(templateName), + for: generateFor(templateName), slug: generateSlug(templateName) }); } @@ -171,7 +173,7 @@ extractCustomTemplates = function extractCustomTemplates(allTemplates) { */ extractTemplates = function extractTemplates(allFiles) { return _.reduce(allFiles, function (templates, entry) { - var tplMatch = entry.file.match(/(^[^\/]+).hbs$/); + var tplMatch = entry.file.match(/(^[^/]+).hbs$/); if (tplMatch) { templates.push(tplMatch[1]); } diff --git a/lib/read-zip.js b/lib/read-zip.js index 0830c2495..b5f43d29c 100644 --- a/lib/read-zip.js +++ b/lib/read-zip.js @@ -1,16 +1,16 @@ -var debug = require('ghost-ignition').debug('zip'), - path = require('path'), - Promise = require('bluebird'), - os = require('os'), - glob = require('glob'), - extract = require('extract-zip'), - uuid = require('uuid'), - _ = require('lodash'), +var debug = require('ghost-ignition').debug('zip'), + path = require('path'), + Promise = require('bluebird'), + os = require('os'), + glob = require('glob'), + extract = require('extract-zip'), + uuid = require('uuid'), + _ = require('lodash'), readZip, resolveBaseDir; resolveBaseDir = function resolveBaseDir(zipPath, cb) { - glob("**/index.hbs", {cwd: zipPath}, function (err, matches) { + glob('**/index.hbs', {cwd: zipPath}, function (err, matches) { var matchedPath; if (!err && !_.isEmpty(matches)) { diff --git a/lib/spec.js b/lib/spec.js index 2c74c6bfe..bac9bfde4 100644 --- a/lib/spec.js +++ b/lib/spec.js @@ -4,7 +4,7 @@ * This file contains details of the theme API spec, in a format that can be used by GScan */ -var knownHelpers, templates, rules, ruleNext; +var knownHelpers, templates, rules, ruleNext; // eslint-disable-line no-unused-vars knownHelpers = [ // Ghost @@ -75,367 +75,367 @@ templates = [ // @TODO: register rules in each checker! rules = { - "GS001-DEPR-PURL": { - "level": "error", - "rule": "Replace {{pageUrl}} with {{page_url}}", - "fatal": true, - "details": "The helper {{pageUrl}} was replaced with {{page_url}}.
" + - "Find more information about the {{page_url}} helper here." - }, - "GS001-DEPR-MD": { - "level": "error", - "rule": "The usage of {{meta_description}} in HTML head is no longer required", - "details": "The usage of {{meta_description}} in the HTML head tag is no longer required because Ghost outputs this for you automatically in {{ghost_head}}.
" + - "Check out the documentation for {{meta_description}} here.
" + - "To see, what else is rendered with the {{ghost_head}} helper, look here." - }, - "GS001-DEPR-IMG": { - "level": "error", - "rule": "The {{image}} helper was replaced with the {{img_url}} helper. Please read the details..", - "fatal": true, - "details": "The {{image}} helper was replaced with the {{img_url}} helper.
" + - "Depending on the context of the {{img_url}} helper you would need to use e. g.

{{#post}}
    {{img_url feature_image}}
{{/post}}


to render the feature image of the blog post.
" + - "
If you are using {{if image}}, then you have to replace it with e.g. {{if feature_image}}." + - "

Find more information about the {{img_url}} helper here and " + - "read more about Ghost's usage of contexts here." - }, - "GS001-DEPR-COV": { - "level": "error", - "rule": "Replace the {{cover}} helper with {{cover_image}}", - "fatal": true, - "details": "The cover attribute was replaced with cover_image. To render the cover image in author context, you need to use

" + - "{{#author}}
    {{cover_image}}
{{/author}}


" + - "See the object attributes of author here.
" + - "To render the cover image of your blog, just use {{@blog.cover_image}}. See here." - }, - "GS001-DEPR-AIMG": { - "level": "error", - "rule": "Replace the {{author.image}} helper with {{author.profile_image}}", - "fatal": true, - "details": "The image attribute in author context was replaced with profile_image.
" + - "Instead of {{author.image}} you need to use {{author.profile_image}}.
" + - "See the object attributes of author here." - }, - "GS001-DEPR-PIMG": { - "level": "error", - "rule": "Replace the {{post.image}} helper with {{post.feature_image}}", - "fatal": true, - "details": "The image attribute in post context was replaced with feature_image.
" + - "Instead of {{post.image}} you need to use {{post.feature_image}}.
" + - "See the object attributes of post here." - }, - "GS001-DEPR-BC": { - "level": "error", - "rule": "Replace the {{@blog.cover}} helper with {{@blog.cover_image}}", - "fatal": true, - "details": "The cover attribute was replaced with cover_image.
" + - "Instead of {{@blog.cover}} you need to use {{@blog.cover_image}}.
" + - "See here." - }, - "GS001-DEPR-AC": { - "level": "error", - "rule": "Replace the {{author.cover}} helper with {{author.cover_image}}", - "fatal": true, - "details": "The cover attribute was replaced with cover_image.
" + - "Instead of {{author.cover}} you need to use {{author.cover_image}}.
" + - "See the object attributes of author here." - }, - "GS001-DEPR-TIMG": { - "level": "error", - "rule": "Replace the {{tag.image}} helper with {{tag.feature_image}}", - "fatal": true, - "details": "The image attribute in tag context was replaced with feature_image.
" + - "Instead of {{tag.image}} you need to use {{tag.feature_image}}.
" + - "See the object attributes of tags here." - }, - "GS001-DEPR-PAIMG": { - "level": "error", - "rule": "Replace the {{post.author.image}} helper with {{post.author.feature_image}}", - "fatal": true, - "details": "The image attribute in author context was replaced with feature_image.
" + - "Instead of {{post.author.image}} you need to use {{post.author.feature_image}}.
" + - "See the object attributes of author here." - }, - "GS001-DEPR-PAC": { - "level": "error", - "rule": "Replace the {{post.author.cover}} helper with {{post.author.cover_image}}", - "fatal": true, - "details": "The cover attribute in author context was replaced with cover_image.
" + - "Instead of {{post.author.cover}} you need to use {{post.author.cover_image}}.
" + - "See the object attributes of author here." - }, - "GS001-DEPR-PTIMG": { - "level": "error", - "rule": "Replace the {{post.tags.[#].image}} helper with {{post.tags.[#].feature_image}}", - "fatal": true, - "details": "The image attribute in tag context was replaced with feature_image.
" + - "Instead of {{post.tags.[#].image}} you need to use {{post.tags.[#].feature_image}}.
" + - "See the object attributes of tags here." - }, - "GS001-DEPR-TSIMG": { - "level": "error", - "rule": "Replace the {{tags.[#].image}} helper with {{tags.[#].feature_image}}", - "fatal": true, - "details": "The image attribute in tag context was replaced with feature_image.
" + - "Instead of {{tags.[#].image}} you need to use {{tags.[#].feature_image}}.
" + - "See the object attributes of tags here." - }, - "GS001-DEPR-CON-IMG": { - "level": "error", - "rule": "Replace the {{#if image}} helper with {{#if feature_image}}, or " + - "{{#if profile_image}}", - "fatal": true, - "details": "The image attribute was replaced with feature_image and profile_image.
" + - "Depending on the context you will need to replace it like this:

" + - "{{#author}}
" + - "    {{#if profile_image}}
        {{profile_image}}
    {{/if}}
" + - "{{/author}}


" + - "See the object attributes of author here.

" + - "{{#post}}
" + - "    {{#if feature_image}}
        {{feature_image}}
    {{/if}}
" + - "{{/post}}


" + - "See the object attributes of post here.

" + - "{{#tag}}
" + - "    {{#if feature_image}}
        {{feature_image}}
    {{/if}}
" + - "{{/tag}}


" + - "See the object attributes of tags here." - }, - "GS001-DEPR-CON-COV": { - "level": "error", - "rule": "Replace the {{#if cover}} helper with {{#if cover_image}}", - "fatal": true, - "details": "The cover attribute was replaced with cover_image. To check for the cover image in author context, you need to use

" + - "{{#if cover_image}}
    {{cover_image}}
{{/if}}


" + - "See the object attributes of author here.
" + - "To check for the cover image of your blog, just use {{#if @blog.cover_image}}. See here." - }, - "GS001-DEPR-CON-BC": { - "level": "error", - "rule": "Replace the {{#if @blog.cover}} helper with {{#if @blog.cover_image}}", - "fatal": true, - "details": "The cover attribute was replaced with cover_image.
" + - "Instead of {{#if @blog.cover}} you need to use {{#if @blog.cover_image}}.
" + - "See here." - }, - "GS001-DEPR-CON-AC": { - "level": "error", - "rule": "Replace the {{#if author.cover}} helper with {{#if author.cover_image}}", - "fatal": true, - "details": "The cover attribute was replaced with cover_image.
" + - "Instead of {{#if author.cover}} you need to use {{#if author.cover_image}}.
" + - "See the object attributes of author here." - }, - "GS001-DEPR-CON-AIMG": { - "level": "error", - "rule": "Replace the {{#if author.image}} helper with {{#if author.profile_image}}", - "fatal": true, - "details": "The image attribute in author context was replaced with profile_image.
" + - "Instead of {{#if author.image}} you need to use {{#if author.profile_image}}.
" + - "See the object attributes of author here." - }, - "GS001-DEPR-CON-TIMG": { - "level": "error", - "rule": "Replace the {{#if tag.image}} helper with {{#if tag.feature_image}}", - "fatal": true, - "details": "The image attribute in tag context was replaced with feature_image.
" + - "Instead of {{#if tag.image}} you need to use {{#if tag.feature_image}}.
" + - "See the object attributes of tags here." - }, - "GS001-DEPR-CON-PTIMG": { - "level": "error", - "rule": "Replace the {{#if post.tags.[#].image}} helper with {{#if post.tags.[#].feature_image}}", - "fatal": true, - "details": "The image attribute in tag context was replaced with feature_image.
" + - "Instead of {{#if post.tags.[#].image}} you need to use {{#if post.tags.[#].feature_image}}.
" + - "See the object attributes of tags here." - }, - "GS001-DEPR-CON-TSIMG": { - "level": "error", - "rule": "Replace the {{#if tags.[#].image}} helper with {{#if tags.[#].feature_image}}", - "fatal": true, - "details": "The image attribute in tag context was replaced with feature_image.
" + - "Instead of {{#if tags.[#].image}} you need to use {{#if tags.[#].feature_image}}.
" + - "See the object attributes of tags here." - }, - "GS001-DEPR-PPP": { - "level": "error", - "rule": "Replace {{@blog.posts_per_page}} with {{@config.posts_per_page}}", - "details": "The global {{@blog.posts_per_page}} property was replaced with {{@config.posts_per_page}}.
" + - "Read here about the attribute and " + - "check here where you can customise the posts per page setting, as this is now adjustable in your theme." - }, - "GS001-DEPR-C0H": { - "level": "error", - "rule": "Replace {{content words=\"0\"}} with the {{img_url}} helper", - "details": "The {{content words=\"0\"}} hack doesn't work anymore (and was never supported). Please use the {{img_url}} helper to render images.
" + - "Find more information about the {{img_url}} helper here and " - }, - "GS001-DEPR-CSS-AT": { - "level": "error", - "rule": "Replace .archive-template with the .paged CSS class", - "details": "The .archive-template CSS class was replaced with the .paged. Please replace this in your stylesheet.
" + - "See the context table to check which classes Ghost uses for each context." - }, - "GS001-DEPR-CSS-PA": { - "level": "error", - "rule": "Replace .page with the .page-template css class", - "details": "The .page CSS class was replaced with the .page-template. Please replace this in your stylesheet.
" + - "See the context table to check which classes Ghost uses for each context." - }, - "GS001-DEPR-CSS-PATS": { - "level": "error", - "rule": "Replace .page-template-slug with the .page-slug css class", - "details": "The .page-template-slug CSS class was replaced with the .page-slug. Please replace this in your stylesheet.
" + - "See the context table to check which classes Ghost uses for each context." - }, - "GS002-DISQUS-ID": { - "level": "error", - "rule": "Replace {{id}} with {{comment_id}} in Disqus embeds.", - "fatal": true, - "details": "The output of {{id}} has changed between Ghost LTS and 1.0.0. " + - "This results in Disqus comments not loading on Ghost 1.0.0 posts which were imported from Ghost LTS. " + - "To resolve this, we've added a new {{comment_id}} helper that will output the old ID " + - "for posts that have been imported from LTS, and the new ID for new posts. " + - "The Disqus embed must be updated from this.page.identifier = 'ghost-{{id}}'; to " + - "this.page.identifier = 'ghost-{{comment_id}}'; to ensure Disqus continues to work." - }, - "GS002-ID-HELPER": { - "level": "recommendation", - "rule": "The output of {{id}} changed between Ghost LTS and 1.0.0, you may need to use {{comment_id}} instead.", - "details": - "The output of {{id}} has changed between Ghost LTS and 1.0.0. " + - "{{id}} used to output an incrementing integer ID, and will now output an ObjectID. " + - "We've added a new {{comment_id}} helper that will output the old ID " + - "for posts that have been imported from LTS, and the new ID for new posts. " + - "If you need the old ID to be output on imported posts, then you will need to use " + - "{{comment_id}} rather than {{id}}." - }, - "GS005-TPL-ERR": { - "level": "error", - "rule": "Templates must contain valid Handlebars", - "fatal": true, - "details": "Oops! You seemed to have used invalid Handlebars syntax. This mostly happens, when you use a helper that is not supported.
" + - "See the full list of available helpers here." - }, - "GS010-PJ-REQ": { - "level": "error", - "rule": "package.json file should be present", - "details": "You should provide a package.json file for your theme.
" + - "Check the package.json documentation to see which properties are required and which are recommended." - }, - "GS010-PJ-PARSE": { - "level": "error", - "rule": "package.json file can be parsed", - "details": "Your package.json file couldn't be parsed. This is mostly caused by a missing or unnecessary ',' or the wrong usage of '\"\"'.
" + - "Check the package.json documentation for further information.
" + - "A good reference for your package.json file is always the latest version of Casper." - }, - "GS010-PJ-NAME-LC": { - "level": "error", - "rule": "package.json property \"name\" must be lowercase", - "details": "The property \"name\" in your package.json file must be lowercase.
" + - "Good examples are: \"my-theme\" or \"theme\" rather than \"My Theme\" or \"Theme\".
" + - "Check the package.json documentation for further information." - }, - "GS010-PJ-NAME-HY": { - "level": "error", - "rule": "package.json property \"name\" must be hyphenated", - "details": "The property \"name\" in your package.json file must be hyphenated.
" + - "Please use \"my-theme\" rather than \"My Theme\" or \"my theme\".
" + - "Check the package.json documentation for further information." - }, - "GS010-PJ-NAME-REQ": { - "level": "error", - "rule": "package.json property \"name\" is required", - "details": "Please add the property \"name\" to your package.json. E.g. {\"name\": \"my-theme\"}.
" + - "Check the package.json documentation to see which properties are required and which are recommended." - }, - "GS010-PJ-VERSION-SEM": { - "level": "error", - "rule": "package.json property \"version\" must be semver compliant", - "details": "The property \"version\" in your package.json file must be semver compliant. E.g. {\"version\": \"1.0.0\"}.
" + - "Check the package.json documentation for further information." - }, - "GS010-PJ-VERSION-REQ": { - "level": "error", - "rule": "package.json property \"version\" is required", - "details": "Please add the property \"version\" to your package.json. E.g. {\"version\": \"1.0.0\"}.
" + - "Check the package.json documentation to see which properties are required and which are recommended." - }, - "GS010-PJ-AUT-EM-VAL": { - "level": "error", - "rule": "package.json property \"author.email\" must be valid", - "details": "The property \"author.email\" in your package.json file must a valid email. E.g. {\"author\": {\"email\": \"hello@example.com\"}}.
" + - "Check the package.json documentation for further information." - }, - "GS010-PJ-CONF-PPP": { - "level": "recommendation", - "rule": "package.json property \"config.posts_per_page\" is recommended. Otherwise, it falls back to 5", - "details": "Please add \"posts_per_page\" to your package.json. E.g. {\"config\": { \"posts_per_page\": 5}}.
" + - "If no \"posts_per_page\" property is provided, Ghost will use its default setting of 5 posts per page.
" + - "Check the package.json documentation for further information." - }, - "GS010-PJ-CONF-PPP-INT": { - "level": "error", - "rule": "package.json property \"config.posts_per_page\" must be a number above 0", - "details": "The property \"config.posts_per_page\" in your package.json file must be a number greater than zero. E.g. {\"config\": { \"posts_per_page\": 5}}.
" + - "Check the package.json documentation for further information." - }, - "GS010-PJ-AUT-EM-REQ": { - "level": "error", - "rule": "package.json property \"author.email\" is required", - "details": "Please add the property \"author.email\" to your package.json. E.g. {\"author\": {\"email\": \"hello@example.com\"}}.
" + - "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.
" + - "The package.json file is NOT accessible when uploaded to a blog so if the theme is only uploaded to a single blog, no one will see this email address.
" + - "Check the package.json documentation to see which properties are required and which are recommended." - }, - "GS020-INDEX-REQ": { - "level": "error", - "rule": "A template file called index.hbs must be present", - "fatal": true, - "details": "Your theme must have a template file called index.hbs.
" + - "Read here more about the required template structure and index.hbs in particular." - }, - "GS020-POST-REQ": { - "level": "error", - "rule": "A template file called post.hbs must be present", - "fatal": true, - "details": "Your theme must have a template file called index.hbs.
" + - "Read here more about the required template structure and post.hbs in particular." - }, - "GS020-DEF-REC": { - "level": "recommendation", - "rule": "Provide a default layout template called default.hbs", - "details": "It is recommended that your theme has a template file called default.hbs.
" + - "Read here more about the recommended template structure and default.hbs in particular." - }, - "GS030-ASSET-REQ": { - "level": "warning", - "rule": "Assets such as CSS & JS must use the {{asset}} helper", - "details": "The listed files should be included using the {{asset}} helper.
" + - "For more information, please see the {{asset}} helper documentation." - }, - "GS030-ASSET-SYM": { - "level": "error", - "rule": "Symlinks in themes are not allowed", - "fatal": true, - "details": "Symbolic links in themes are not allowed. Please use the {{asset}} helper.
" + - "For more information, please see the {{asset}} helper documentation." - }, - "GS040-GH-REQ": { - "level": "warning", - "rule": "The helper {{ghost_head}} should be present", - "details": "The {{ghost_head}} helper should be present in your theme. It outputs many useful things, such as \"code injection\" scripts, structured data, canonical links, meta description etc.
" + - "The helper belongs just before the tag in your default.hbs template.
" + - "For more details, please see the {{ghost_head}} helper documentation." - }, - "GS040-GF-REQ": { - "level": "warning", - "rule": "The helper {{ghost_foot}} should be present", - "details": "The {{ghost_foot}} helper should be present in your theme. It outputs scripts as saved in \"code injection\" scripts.
" + - "The helper belongs just before the tag in your default.hbs template.
" + - "For more details, please see the {{ghost_foot}} helper documentation." + 'GS001-DEPR-PURL': { + level: 'error', + rule: 'Replace {{pageUrl}} with {{page_url}}', + fatal: true, + details: 'The helper {{pageUrl}} was replaced with {{page_url}}.
' + + 'Find more information about the {{page_url}} helper here.' + }, + 'GS001-DEPR-MD': { + level: 'error', + rule: 'The usage of {{meta_description}} in HTML head is no longer required', + details: 'The usage of {{meta_description}} in the HTML head tag is no longer required because Ghost outputs this for you automatically in {{ghost_head}}.
' + + 'Check out the documentation for {{meta_description}} here.
' + + 'To see, what else is rendered with the {{ghost_head}} helper, look here.' + }, + 'GS001-DEPR-IMG': { + level: 'error', + rule: 'The {{image}} helper was replaced with the {{img_url}} helper. Please read the details..', + fatal: true, + details: 'The {{image}} helper was replaced with the {{img_url}} helper.
' + + 'Depending on the context of the {{img_url}} helper you would need to use e. g.

{{#post}}
    {{img_url feature_image}}
{{/post}}


to render the feature image of the blog post.
' + + '
If you are using {{if image}}, then you have to replace it with e.g. {{if feature_image}}.' + + '

Find more information about the {{img_url}} helper here and ' + + 'read more about Ghost\'s usage of contexts here.' + }, + 'GS001-DEPR-COV': { + level: 'error', + rule: 'Replace the {{cover}} helper with {{cover_image}}', + fatal: true, + details: 'The cover attribute was replaced with cover_image. To render the cover image in author context, you need to use

' + + '{{#author}}
    {{cover_image}}
{{/author}}


' + + 'See the object attributes of author here.
' + + 'To render the cover image of your blog, just use {{@blog.cover_image}}. See here.' + }, + 'GS001-DEPR-AIMG': { + level: 'error', + rule: 'Replace the {{author.image}} helper with {{author.profile_image}}', + fatal: true, + details: 'The image attribute in author context was replaced with profile_image.
' + + 'Instead of {{author.image}} you need to use {{author.profile_image}}.
' + + 'See the object attributes of author here.' + }, + 'GS001-DEPR-PIMG': { + level: 'error', + rule: 'Replace the {{post.image}} helper with {{post.feature_image}}', + fatal: true, + details: 'The image attribute in post context was replaced with feature_image.
' + + 'Instead of {{post.image}} you need to use {{post.feature_image}}.
' + + 'See the object attributes of post here.' + }, + 'GS001-DEPR-BC': { + level: 'error', + rule: 'Replace the {{@blog.cover}} helper with {{@blog.cover_image}}', + fatal: true, + details: 'The cover attribute was replaced with cover_image.
' + + 'Instead of {{@blog.cover}} you need to use {{@blog.cover_image}}.
' + + 'See here.' + }, + 'GS001-DEPR-AC': { + level: 'error', + rule: 'Replace the {{author.cover}} helper with {{author.cover_image}}', + fatal: true, + details: 'The cover attribute was replaced with cover_image.
' + + 'Instead of {{author.cover}} you need to use {{author.cover_image}}.
' + + 'See the object attributes of author here.' + }, + 'GS001-DEPR-TIMG': { + level: 'error', + rule: 'Replace the {{tag.image}} helper with {{tag.feature_image}}', + fatal: true, + details: 'The image attribute in tag context was replaced with feature_image.
' + + 'Instead of {{tag.image}} you need to use {{tag.feature_image}}.
' + + 'See the object attributes of tags here.' + }, + 'GS001-DEPR-PAIMG': { + level: 'error', + rule: 'Replace the {{post.author.image}} helper with {{post.author.feature_image}}', + fatal: true, + details: 'The image attribute in author context was replaced with feature_image.
' + + 'Instead of {{post.author.image}} you need to use {{post.author.feature_image}}.
' + + 'See the object attributes of author here.' + }, + 'GS001-DEPR-PAC': { + level: 'error', + rule: 'Replace the {{post.author.cover}} helper with {{post.author.cover_image}}', + fatal: true, + details: 'The cover attribute in author context was replaced with cover_image.
' + + 'Instead of {{post.author.cover}} you need to use {{post.author.cover_image}}.
' + + 'See the object attributes of author here.' + }, + 'GS001-DEPR-PTIMG': { + level: 'error', + rule: 'Replace the {{post.tags.[#].image}} helper with {{post.tags.[#].feature_image}}', + fatal: true, + details: 'The image attribute in tag context was replaced with feature_image.
' + + 'Instead of {{post.tags.[#].image}} you need to use {{post.tags.[#].feature_image}}.
' + + 'See the object attributes of tags here.' + }, + 'GS001-DEPR-TSIMG': { + level: 'error', + rule: 'Replace the {{tags.[#].image}} helper with {{tags.[#].feature_image}}', + fatal: true, + details: 'The image attribute in tag context was replaced with feature_image.
' + + 'Instead of {{tags.[#].image}} you need to use {{tags.[#].feature_image}}.
' + + 'See the object attributes of tags here.' + }, + 'GS001-DEPR-CON-IMG': { + level: 'error', + rule: 'Replace the {{#if image}} helper with {{#if feature_image}}, or ' + + '{{#if profile_image}}', + fatal: true, + details: 'The image attribute was replaced with feature_image and profile_image.
' + + 'Depending on the context you will need to replace it like this:

' + + '{{#author}}
' + + '    {{#if profile_image}}
        {{profile_image}}
    {{/if}}
' + + '{{/author}}


' + + 'See the object attributes of author here.

' + + '{{#post}}
' + + '    {{#if feature_image}}
        {{feature_image}}
    {{/if}}
' + + '{{/post}}


' + + 'See the object attributes of post here.

' + + '{{#tag}}
' + + '    {{#if feature_image}}
        {{feature_image}}
    {{/if}}
' + + '{{/tag}}


' + + 'See the object attributes of tags here.' + }, + 'GS001-DEPR-CON-COV': { + level: 'error', + rule: 'Replace the {{#if cover}} helper with {{#if cover_image}}', + fatal: true, + details: 'The cover attribute was replaced with cover_image. To check for the cover image in author context, you need to use

' + + '{{#if cover_image}}
    {{cover_image}}
{{/if}}


' + + 'See the object attributes of author here.
' + + 'To check for the cover image of your blog, just use {{#if @blog.cover_image}}. See here.' + }, + 'GS001-DEPR-CON-BC': { + level: 'error', + rule: 'Replace the {{#if @blog.cover}} helper with {{#if @blog.cover_image}}', + fatal: true, + details: 'The cover attribute was replaced with cover_image.
' + + 'Instead of {{#if @blog.cover}} you need to use {{#if @blog.cover_image}}.
' + + 'See here.' + }, + 'GS001-DEPR-CON-AC': { + level: 'error', + rule: 'Replace the {{#if author.cover}} helper with {{#if author.cover_image}}', + fatal: true, + details: 'The cover attribute was replaced with cover_image.
' + + 'Instead of {{#if author.cover}} you need to use {{#if author.cover_image}}.
' + + 'See the object attributes of author here.' + }, + 'GS001-DEPR-CON-AIMG': { + level: 'error', + rule: 'Replace the {{#if author.image}} helper with {{#if author.profile_image}}', + fatal: true, + details: 'The image attribute in author context was replaced with profile_image.
' + + 'Instead of {{#if author.image}} you need to use {{#if author.profile_image}}.
' + + 'See the object attributes of author here.' + }, + 'GS001-DEPR-CON-TIMG': { + level: 'error', + rule: 'Replace the {{#if tag.image}} helper with {{#if tag.feature_image}}', + fatal: true, + details: 'The image attribute in tag context was replaced with feature_image.
' + + 'Instead of {{#if tag.image}} you need to use {{#if tag.feature_image}}.
' + + 'See the object attributes of tags here.' + }, + 'GS001-DEPR-CON-PTIMG': { + level: 'error', + rule: 'Replace the {{#if post.tags.[#].image}} helper with {{#if post.tags.[#].feature_image}}', + fatal: true, + details: 'The image attribute in tag context was replaced with feature_image.
' + + 'Instead of {{#if post.tags.[#].image}} you need to use {{#if post.tags.[#].feature_image}}.
' + + 'See the object attributes of tags here.' + }, + 'GS001-DEPR-CON-TSIMG': { + level: 'error', + rule: 'Replace the {{#if tags.[#].image}} helper with {{#if tags.[#].feature_image}}', + fatal: true, + details: 'The image attribute in tag context was replaced with feature_image.
' + + 'Instead of {{#if tags.[#].image}} you need to use {{#if tags.[#].feature_image}}.
' + + 'See the object attributes of tags here.' + }, + 'GS001-DEPR-PPP': { + level: 'error', + rule: 'Replace {{@blog.posts_per_page}} with {{@config.posts_per_page}}', + details: 'The global {{@blog.posts_per_page}} property was replaced with {{@config.posts_per_page}}.
' + + 'Read here about the attribute and ' + + 'check here where you can customise the posts per page setting, as this is now adjustable in your theme.' + }, + 'GS001-DEPR-C0H': { + level: 'error', + rule: 'Replace {{content words="0"}} with the {{img_url}} helper', + details: 'The {{content words="0"}} hack doesn\'t work anymore (and was never supported). Please use the {{img_url}} helper to render images.
' + + 'Find more information about the {{img_url}} helper here and ' + }, + 'GS001-DEPR-CSS-AT': { + level: 'error', + rule: 'Replace .archive-template with the .paged CSS class', + details: 'The .archive-template CSS class was replaced with the .paged. Please replace this in your stylesheet.
' + + 'See the context table to check which classes Ghost uses for each context.' + }, + 'GS001-DEPR-CSS-PA': { + level: 'error', + rule: 'Replace .page with the .page-template css class', + details: 'The .page CSS class was replaced with the .page-template. Please replace this in your stylesheet.
' + + 'See the context table to check which classes Ghost uses for each context.' + }, + 'GS001-DEPR-CSS-PATS': { + level: 'error', + rule: 'Replace .page-template-slug with the .page-slug css class', + details: 'The .page-template-slug CSS class was replaced with the .page-slug. Please replace this in your stylesheet.
' + + 'See the context table to check which classes Ghost uses for each context.' + }, + 'GS002-DISQUS-ID': { + level: 'error', + rule: 'Replace {{id}} with {{comment_id}} in Disqus embeds.', + fatal: true, + details: 'The output of {{id}} has changed between Ghost LTS and 1.0.0. ' + + 'This results in Disqus comments not loading on Ghost 1.0.0 posts which were imported from Ghost LTS. ' + + 'To resolve this, we\'ve added a new {{comment_id}} helper that will output the old ID ' + + 'for posts that have been imported from LTS, and the new ID for new posts. ' + + 'The Disqus embed must be updated from this.page.identifier = \'ghost-{{id}}\'; to ' + + 'this.page.identifier = \'ghost-{{comment_id}}\'; to ensure Disqus continues to work.' + }, + 'GS002-ID-HELPER': { + level: 'recommendation', + rule: 'The output of {{id}} changed between Ghost LTS and 1.0.0, you may need to use {{comment_id}} instead.', + details: + 'The output of {{id}} has changed between Ghost LTS and 1.0.0. ' + + '{{id}} used to output an incrementing integer ID, and will now output an ObjectID. ' + + 'We\'ve added a new {{comment_id}} helper that will output the old ID ' + + 'for posts that have been imported from LTS, and the new ID for new posts. ' + + 'If you need the old ID to be output on imported posts, then you will need to use ' + + '{{comment_id}} rather than {{id}}.' + }, + 'GS005-TPL-ERR': { + level: 'error', + rule: 'Templates must contain valid Handlebars', + fatal: true, + details: 'Oops! You seemed to have used invalid Handlebars syntax. This mostly happens, when you use a helper that is not supported.
' + + 'See the full list of available helpers here.' + }, + 'GS010-PJ-REQ': { + level: 'error', + rule: 'package.json file should be present', + details: 'You should provide a package.json file for your theme.
' + + 'Check the package.json documentation to see which properties are required and which are recommended.' + }, + 'GS010-PJ-PARSE': { + level: 'error', + rule: 'package.json file can be parsed', + details: 'Your package.json file couldn\'t be parsed. This is mostly caused by a missing or unnecessary \',\' or the wrong usage of \'""\'.
' + + 'Check the package.json documentation for further information.
' + + 'A good reference for your package.json file is always the latest version of Casper.' + }, + 'GS010-PJ-NAME-LC': { + level: 'error', + rule: 'package.json property "name" must be lowercase', + details: 'The property "name" in your package.json file must be lowercase.
' + + 'Good examples are: "my-theme" or "theme" rather than "My Theme" or "Theme".
' + + 'Check the package.json documentation for further information.' + }, + 'GS010-PJ-NAME-HY': { + level: 'error', + rule: 'package.json property "name" must be hyphenated', + details: 'The property "name" in your package.json file must be hyphenated.
' + + 'Please use "my-theme" rather than "My Theme" or "my theme".
' + + 'Check the package.json documentation for further information.' + }, + 'GS010-PJ-NAME-REQ': { + level: 'error', + rule: 'package.json property "name" is required', + details: 'Please add the property "name" to your package.json. E.g. {"name": "my-theme"}.
' + + 'Check the package.json documentation to see which properties are required and which are recommended.' + }, + 'GS010-PJ-VERSION-SEM': { + level: 'error', + rule: 'package.json property "version" must be semver compliant', + details: 'The property "version" in your package.json file must be semver compliant. E.g. {"version": "1.0.0"}.
' + + 'Check the package.json documentation for further information.' + }, + 'GS010-PJ-VERSION-REQ': { + level: 'error', + rule: 'package.json property "version" is required', + details: 'Please add the property "version" to your package.json. E.g. {"version": "1.0.0"}.
' + + 'Check the package.json documentation to see which properties are required and which are recommended.' + }, + 'GS010-PJ-AUT-EM-VAL': { + level: 'error', + rule: 'package.json property "author.email" must be valid', + details: 'The property "author.email" in your package.json file must a valid email. E.g. {"author": {"email": "hello@example.com"}}.
' + + 'Check the package.json documentation for further information.' + }, + 'GS010-PJ-CONF-PPP': { + level: 'recommendation', + rule: 'package.json property "config.posts_per_page" is recommended. Otherwise, it falls back to 5', + details: 'Please add "posts_per_page" to your package.json. E.g. {"config": { "posts_per_page": 5}}.
' + + 'If no "posts_per_page" property is provided, Ghost will use its default setting of 5 posts per page.
' + + 'Check the package.json documentation for further information.' + }, + 'GS010-PJ-CONF-PPP-INT': { + level: 'error', + rule: 'package.json property "config.posts_per_page" must be a number above 0', + details: 'The property "config.posts_per_page" in your package.json file must be a number greater than zero. E.g. {"config": { "posts_per_page": 5}}.
' + + 'Check the package.json documentation for further information.' + }, + 'GS010-PJ-AUT-EM-REQ': { + level: 'error', + rule: 'package.json property "author.email" is required', + details: 'Please add the property "author.email" to your package.json. E.g. {"author": {"email": "hello@example.com"}}.
' + + '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.
' + + 'The package.json file is NOT accessible when uploaded to a blog so if the theme is only uploaded to a single blog, no one will see this email address.
' + + 'Check the package.json documentation to see which properties are required and which are recommended.' + }, + 'GS020-INDEX-REQ': { + level: 'error', + rule: 'A template file called index.hbs must be present', + fatal: true, + details: 'Your theme must have a template file called index.hbs.
' + + 'Read here more about the required template structure and index.hbs in particular.' + }, + 'GS020-POST-REQ': { + level: 'error', + rule: 'A template file called post.hbs must be present', + fatal: true, + details: 'Your theme must have a template file called index.hbs.
' + + 'Read here more about the required template structure and post.hbs in particular.' + }, + 'GS020-DEF-REC': { + level: 'recommendation', + rule: 'Provide a default layout template called default.hbs', + details: 'It is recommended that your theme has a template file called default.hbs.
' + + 'Read here more about the recommended template structure and default.hbs in particular.' + }, + 'GS030-ASSET-REQ': { + level: 'warning', + rule: 'Assets such as CSS & JS must use the {{asset}} helper', + details: 'The listed files should be included using the {{asset}} helper.
' + + 'For more information, please see the {{asset}} helper documentation.' + }, + 'GS030-ASSET-SYM': { + level: 'error', + rule: 'Symlinks in themes are not allowed', + fatal: true, + details: 'Symbolic links in themes are not allowed. Please use the {{asset}} helper.
' + + 'For more information, please see the {{asset}} helper documentation.' + }, + 'GS040-GH-REQ': { + level: 'warning', + rule: 'The helper {{ghost_head}} should be present', + details: 'The {{ghost_head}} helper should be present in your theme. It outputs many useful things, such as "code injection" scripts, structured data, canonical links, meta description etc.
' + + 'The helper belongs just before the tag in your default.hbs template.
' + + 'For more details, please see the {{ghost_head}} helper documentation.' + }, + 'GS040-GF-REQ': { + level: 'warning', + rule: 'The helper {{ghost_foot}} should be present', + details: 'The {{ghost_foot}} helper should be present in your theme. It outputs scripts as saved in "code injection" scripts.
' + + 'The helper belongs just before the tag in your default.hbs template.
' + + 'For more details, please see the {{ghost_foot}} helper documentation.' } }; @@ -443,9 +443,9 @@ rules = { * These are rules that haven't been implemented yet, but should be! */ ruleNext = { - "GS030-CSS-CACHE": { - "level": "warning", - "rule": "CSS files should use cache bustable URLs" + 'GS030-CSS-CACHE': { + level: 'warning', + rule: 'CSS files should use cache bustable URLs' } }; diff --git a/package.json b/package.json index d8230d6da..254eb2773 100644 --- a/package.json +++ b/package.json @@ -41,8 +41,6 @@ "bluebird": "^3.4.6", "chalk": "^1.1.1", "commander": "2.15.1", - "eslint": "^4.19.1", - "eslint-plugin-ghost": "0.0.23", "express": "^4.16.2", "express-hbs": "^1.0.3", "extract-zip": "^1.6.5", @@ -58,6 +56,8 @@ }, "devDependencies": { "@tryghost/deploy": "0.1.3", + "eslint": "^4.19.1", + "eslint-plugin-ghost": "0.0.23", "istanbul": "^0.4.1", "mocha": "^2.4.5", "nodemon": "1.17.3", diff --git a/shipitfile.js b/shipitfile.js index 61ce30750..cff8a681f 100644 --- a/shipitfile.js +++ b/shipitfile.js @@ -6,14 +6,14 @@ function init(shipit) { yarn: true, workspace: './', deployTo: '/opt/gscan/', - ignores: ['.git', '.gitkeep', '.gitignore', '.jshintrc', 'node_modules'], + ignores: ['.git', '.gitkeep', '.gitignore', '.eslintrc.js', '.eslintcache', 'node_modules', '/test', '/app/public/.eslintrc.js'] }, staging: { servers: process.env.STG_USER + '@' + process.env.STG_SERVER, sharedLinks: [{ name: 'node_modules', type: 'directory' - }, { + }, { name: 'uploads', type: 'directory' }, { @@ -26,7 +26,7 @@ function init(shipit) { sharedLinks: [{ name: 'node_modules', type: 'directory' - }, { + }, { name: 'uploads', type: 'directory' }, { diff --git a/test/001-deprecations.test.js b/test/001-deprecations.test.js index 0dacec00a..162d6b47f 100644 --- a/test/001-deprecations.test.js +++ b/test/001-deprecations.test.js @@ -1,7 +1,4 @@ -/*globals describe, it */ -var should = require('should'), - path = require('path'), - checker = require('../lib/checker'), +var should = require('should'), // eslint-disable-line no-unused-vars thisCheck = require('../lib/checks/001-deprecations'), utils = require('./utils'); diff --git a/test/005-template-compile.test.js b/test/005-template-compile.test.js index c579d4930..eb06ae1f8 100644 --- a/test/005-template-compile.test.js +++ b/test/005-template-compile.test.js @@ -1,5 +1,4 @@ -/*globals describe, it */ -var should = require('should'), +var should = require('should'), // eslint-disable-line no-unused-vars utils = require('./utils'), thisCheck = require('../lib/checks/005-template-compile'); diff --git a/test/010-package-json.test.js b/test/010-package-json.test.js index 0a5a83df8..72222072a 100644 --- a/test/010-package-json.test.js +++ b/test/010-package-json.test.js @@ -1,5 +1,4 @@ -/*globals describe, it */ -var should = require('should'), +var should = require('should'), // eslint-disable-line no-unused-vars utils = require('./utils'), thisCheck = require('../lib/checks/010-package-json'); diff --git a/test/020-theme-structure.test.js b/test/020-theme-structure.test.js index 25efce34a..8a5a5dc44 100644 --- a/test/020-theme-structure.test.js +++ b/test/020-theme-structure.test.js @@ -1,5 +1,4 @@ -/*globals describe, it */ -var should = require('should'), +var should = require('should'), // eslint-disable-line no-unused-vars utils = require('./utils'), thisCheck = require('../lib/checks/020-theme-structure'); @@ -53,4 +52,4 @@ describe('Theme structure', function () { done(); }); }); -}); \ No newline at end of file +}); diff --git a/test/030-assets.test.js b/test/030-assets.test.js index 4ba1fe001..9bb72ce00 100644 --- a/test/030-assets.test.js +++ b/test/030-assets.test.js @@ -1,5 +1,4 @@ -/*globals describe, it */ -var should = require('should'), +var should = require('should'), // eslint-disable-line no-unused-vars utils = require('./utils'), thisCheck = require('../lib/checks/030-assets'); diff --git a/test/040-ghost-head-foot.test.js b/test/040-ghost-head-foot.test.js index dd6d989b8..ed12b2056 100644 --- a/test/040-ghost-head-foot.test.js +++ b/test/040-ghost-head-foot.test.js @@ -1,5 +1,4 @@ -/*globals describe, it */ -var should = require('should'), +var should = require('should'), // eslint-disable-line no-unused-vars utils = require('./utils'), thisCheck = require('../lib/checks/040-ghost-head-foot'); diff --git a/test/general.test.js b/test/general.test.js index b5da966ff..632a01ec1 100644 --- a/test/general.test.js +++ b/test/general.test.js @@ -1,4 +1,3 @@ -/*globals describe, it */ var should = require('should'), sinon = require('sinon'), path = require('path'), @@ -26,7 +25,7 @@ process.env.NODE_ENV = 'testing'; */ function testReadZip(name) { - return readZip({path: themePath(name), name: name}) + return readZip({path: themePath(name), name: name}); } describe('Zip file handler can read zip files', function () { @@ -170,18 +169,18 @@ describe('Read theme', function () { theme.files.should.be.an.Array().with.lengthOf(7); var fileNames = _.map(theme.files, function (file) { - return _.pickBy(file, function(value, key) { + return _.pickBy(file, function (value, key) { return key === 'file' || key === 'ext'; }); }); - fileNames.should.containEql({ file: 'index.hbs', ext: '.hbs'}); - fileNames.should.containEql({ file: 'package.json', ext: '.json' }); - fileNames.should.containEql({ file: 'partialsbroke.hbs', ext: '.hbs'}); - fileNames.should.containEql({ file: 'partials/mypartial.hbs', ext: '.hbs'}); - fileNames.should.containEql({ file: 'partials/subfolder/test.hbs', ext: '.hbs'}); - fileNames.should.containEql({ file: 'post.hbs', ext: '.hbs'}); - fileNames.should.containEql({ file: 'logo.new.hbs', ext: '.hbs' }); + fileNames.should.containEql({file: 'index.hbs', ext: '.hbs'}); + fileNames.should.containEql({file: 'package.json', ext: '.json'}); + fileNames.should.containEql({file: 'partialsbroke.hbs', ext: '.hbs'}); + fileNames.should.containEql({file: 'partials/mypartial.hbs', ext: '.hbs'}); + fileNames.should.containEql({file: 'partials/subfolder/test.hbs', ext: '.hbs'}); + fileNames.should.containEql({file: 'post.hbs', ext: '.hbs'}); + fileNames.should.containEql({file: 'logo.new.hbs', ext: '.hbs'}); done(); }); diff --git a/test/utils.js b/test/utils.js index c4c45105c..a767b0f44 100644 --- a/test/utils.js +++ b/test/utils.js @@ -1,12 +1,12 @@ -var path = require('path'), - should = require('should'), - readTheme = require('../lib/read-theme'), +var path = require('path'), + should = require('should'), + readTheme = require('../lib/read-theme'), testThemePath = 'test/fixtures/themes', getThemePath, testCheck; should.Assertion.add('ValidResultObject', function () { - this.params = { operator: 'to be valid result object' }; + this.params = {operator: 'to be valid result object'}; should.exist(this.obj); @@ -16,7 +16,7 @@ should.Assertion.add('ValidResultObject', function () { }); should.Assertion.add('ValidThemeObject', function () { - this.params = { operator: 'to be valid theme object' }; + this.params = {operator: 'to be valid theme object'}; should.exist(this.obj); this.obj.should.be.an.Object().with.properties(['path', 'files', 'results']); @@ -45,7 +45,7 @@ should.Assertion.add('ValidFailObject', function () { }); should.Assertion.add('ValidRule', function () { - var levels = ['error', 'warning', 'recommendation', 'feature']; + var levels = ['error', 'warning', 'recommendation', 'feature']; // eslint-disable-line no-unused-vars }); getThemePath = function (themeId) {