From 44bb9bf9e39caf201f9d6720d77b913ae95cea1c Mon Sep 17 00:00:00 2001 From: Aileen Nowak Date: Mon, 16 Apr 2018 16:50:54 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=A8=20=20Switched=20to=20eslint=20plug?= =?UTF-8?q?in?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .eslintrc.js | 6 + .gitignore | 3 + app/index.js | 12 +- app/public/.eslintrc.js | 7 + 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 | 3 + shipitfile.js | 6 +- test/.eslintrc.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 +- yarn.lock | 328 ++++++++++++- 32 files changed, 855 insertions(+), 518 deletions(-) create mode 100644 .eslintrc.js create mode 100644 app/public/.eslintrc.js create mode 100644 test/.eslintrc.js diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 00000000..c9c1bcb5 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: ['ghost'], + extends: [ + 'plugin:ghost/node' + ] +}; diff --git a/.gitignore b/.gitignore index 15c052e6..c56041a7 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,6 @@ app/uploads/** # editor .idea *.*~ + +# eslint +.eslintcache diff --git a/app/index.js b/app/index.js index 611fd63f..09fe27bc 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 new file mode 100644 index 00000000..7eeb941e --- /dev/null +++ b/app/public/.eslintrc.js @@ -0,0 +1,7 @@ +/* eslint-env node */ +module.exports = { + plugins: ['ghost'], + extends: [ + 'plugin:ghost/browser' + ] +}; diff --git a/app/public/js/gscan.js b/app/public/js/gscan.js index 3d22591c..856064a4 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 ce1e40e1..75fbec9a 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 7ea5d536..b49869a2 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 a5595cd1..718e3f62 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 7b0dc918..c3bb593b 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 f4ef2d00..63143efb 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 bccb7dd9..6a3e96a6 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 09c60421..ea810700 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 bb26ad64..c02d438a 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 db97a35f..65e9e0e2 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 c2311b18..68b0f3c4 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 57b6e312..6eadd0a0 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 cd06dadc..51e9c14f 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 996a8a00..1f395eb7 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 0830c249..b5f43d29 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 2c74c6bf..bac9bfde 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 65ca5a4b..d7344430 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "dev": "NODE_ENV=development DEBUG=gscan:* nodemon", "test": "NODE_ENV=testing mocha -- $(find test -name '*.test.js')", "coverage": "NODE_ENV=testing istanbul cover --dir test/coverage _mocha -- $(find test -name '*.test.js')", + "lint": "eslint . --ext .js --cache", "ship": "np --yolo", "deploy": "shipit" }, @@ -56,6 +57,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 61ce3075..cff8a681 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/.eslintrc.js b/test/.eslintrc.js new file mode 100644 index 00000000..829b601e --- /dev/null +++ b/test/.eslintrc.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: ['ghost'], + extends: [ + 'plugin:ghost/test' + ] +}; diff --git a/test/001-deprecations.test.js b/test/001-deprecations.test.js index 0dacec00..162d6b47 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 c579d493..eb06ae1f 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 0a5a83df..72222072 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 25efce34..8a5a5dc4 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 4ba1fe00..9bb72ce0 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 dd6d989b..ed12b205 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 b5da966f..632a01ec 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 c4c45105..a767b0f4 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) { diff --git a/yarn.lock b/yarn.lock index 8ffe0532..83f73f8f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -26,6 +26,24 @@ accepts@~1.3.5: mime-types "~2.1.18" negotiator "0.6.1" +acorn-jsx@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b" + dependencies: + acorn "^3.0.4" + +acorn@^3.0.4: + version "3.3.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" + +acorn@^5.5.0: + version "5.5.3" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.5.3.tgz#f473dd47e0277a08e28e9bec5aeeb04751f0b8c9" + +ajv-keywords@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-2.1.1.tgz#617997fc5f60576894c435f940d819e135b80762" + ajv@^4.9.1: version "4.11.8" resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536" @@ -33,7 +51,7 @@ ajv@^4.9.1: co "^4.6.0" json-stable-stringify "^1.0.1" -ajv@^5.1.0: +ajv@^5.1.0, ajv@^5.2.3, ajv@^5.3.0: version "5.5.2" resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" dependencies: @@ -160,7 +178,7 @@ array-unique@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" -arrify@^1.0.1: +arrify@^1.0.0, arrify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" @@ -212,6 +230,14 @@ aws4@^1.2.1, aws4@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e" +babel-code-frame@^6.22.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" + dependencies: + chalk "^1.1.3" + esutils "^2.0.2" + js-tokens "^3.0.2" + balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" @@ -321,6 +347,10 @@ braces@^2.3.0, braces@^2.3.1: split-string "^3.0.2" to-regex "^3.0.1" +buffer-from@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.0.0.tgz#4cb8832d23612589b0406e9e2956c17f06fdf531" + builtin-modules@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" @@ -366,10 +396,20 @@ cache-base@^1.0.1: union-value "^1.0.0" unset-value "^1.0.0" +caller-path@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f" + dependencies: + callsites "^0.2.0" + caller@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/caller/-/caller-1.0.1.tgz#b851860f70e195db3d277395aa1a7e23ea30ecf5" +callsites@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca" + camelcase-keys@^4.0.0: version "4.2.0" resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-4.2.0.tgz#a2aa5fb1af688758259c32c141426d78923b9b77" @@ -423,6 +463,14 @@ chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" +chalk@^2.1.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + chardet@^0.4.0: version "0.4.2" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2" @@ -445,6 +493,10 @@ chokidar@^2.0.2: optionalDependencies: fsevents "^1.0.0" +circular-json@^0.3.1: + version "0.3.3" + resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66" + class-utils@^0.3.5: version "0.3.6" resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" @@ -576,6 +628,15 @@ concat-stream@^1.5.0: readable-stream "^2.2.2" typedarray "^0.0.6" +concat-stream@^1.6.0: + version "1.6.2" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" + dependencies: + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + config-chain@~1.1.5: version "1.1.11" resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.11.tgz#aba09747dfbe4c3e70e766a6e41586e1859fc6f2" @@ -628,7 +689,7 @@ create-error-class@^3.0.0: dependencies: capture-stack-trace "^1.0.0" -cross-spawn@^5.0.1: +cross-spawn@^5.0.1, cross-spawn@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" dependencies: @@ -728,6 +789,18 @@ define-property@^2.0.2: is-descriptor "^1.0.2" isobject "^3.0.1" +del@^2.0.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8" + dependencies: + globby "^5.0.0" + is-path-cwd "^1.0.0" + is-path-in-cwd "^1.0.0" + object-assign "^4.0.1" + pify "^2.0.0" + pinkie-promise "^2.0.0" + rimraf "^2.2.8" + del@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/del/-/del-3.0.0.tgz#53ecf699ffcbcb39637691ab13baf160819766e5" @@ -778,6 +851,12 @@ diff@1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/diff/-/diff-1.4.0.tgz#7f28d2eb9ee7b15a97efd89ce63dcfdaa3ccbabf" +doctrine@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" + dependencies: + esutils "^2.0.2" + dot-prop@^4.1.0: version "4.2.0" resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.0.tgz#1f19e0c2e1aa0e32797c49799f2837ac6af69c57" @@ -822,6 +901,10 @@ elegant-spinner@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e" +ember-rfc176-data@^0.2.7: + version "0.2.7" + resolved "https://registry.yarnpkg.com/ember-rfc176-data/-/ember-rfc176-data-0.2.7.tgz#bd355bc9b473e08096b518784170a23388bc973b" + encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" @@ -861,6 +944,88 @@ escodegen@1.8.x: optionalDependencies: source-map "~0.2.0" +eslint-plugin-ember@^5.0.3: + version "5.1.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-ember/-/eslint-plugin-ember-5.1.0.tgz#fb96abd2d8bf105678a0aa81dadd99d7ca441ba1" + dependencies: + ember-rfc176-data "^0.2.7" + require-folder-tree "^1.4.5" + snake-case "^2.1.0" + +eslint-plugin-ghost@0.0.23: + version "0.0.23" + resolved "https://registry.yarnpkg.com/eslint-plugin-ghost/-/eslint-plugin-ghost-0.0.23.tgz#26057775c28ca06c8a3c5eff40d43c44524e3ff5" + dependencies: + eslint-plugin-ember "^5.0.3" + eslint-plugin-sort-imports-es6-autofix "0.2.2" + +eslint-plugin-sort-imports-es6-autofix@0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-sort-imports-es6-autofix/-/eslint-plugin-sort-imports-es6-autofix-0.2.2.tgz#6d8512bde75142c188a0695db55d442d7f7e011d" + dependencies: + eslint "^4.15.0" + +eslint-scope@^3.7.1: + version "3.7.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.1.tgz#3d63c3edfda02e06e01a452ad88caacc7cdcb6e8" + dependencies: + esrecurse "^4.1.0" + estraverse "^4.1.1" + +eslint-visitor-keys@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d" + +eslint@^4.15.0, eslint@^4.19.1: + version "4.19.1" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.19.1.tgz#32d1d653e1d90408854bfb296f076ec7e186a300" + dependencies: + ajv "^5.3.0" + babel-code-frame "^6.22.0" + chalk "^2.1.0" + concat-stream "^1.6.0" + cross-spawn "^5.1.0" + debug "^3.1.0" + doctrine "^2.1.0" + eslint-scope "^3.7.1" + eslint-visitor-keys "^1.0.0" + espree "^3.5.4" + esquery "^1.0.0" + esutils "^2.0.2" + file-entry-cache "^2.0.0" + functional-red-black-tree "^1.0.1" + glob "^7.1.2" + globals "^11.0.1" + ignore "^3.3.3" + imurmurhash "^0.1.4" + inquirer "^3.0.6" + is-resolvable "^1.0.0" + js-yaml "^3.9.1" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.3.0" + lodash "^4.17.4" + minimatch "^3.0.2" + mkdirp "^0.5.1" + natural-compare "^1.4.0" + optionator "^0.8.2" + path-is-inside "^1.0.2" + pluralize "^7.0.0" + progress "^2.0.0" + regexpp "^1.0.1" + require-uncached "^1.0.3" + semver "^5.3.0" + strip-ansi "^4.0.0" + strip-json-comments "~2.0.1" + table "4.0.2" + text-table "~0.2.0" + +espree@^3.5.4: + version "3.5.4" + resolved "https://registry.yarnpkg.com/espree/-/espree-3.5.4.tgz#b0f447187c8a8bed944b815a660bddf5deb5d1a7" + dependencies: + acorn "^5.5.0" + acorn-jsx "^3.0.0" + esprima@2.7.x, esprima@^2.7.1: version "2.7.3" resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" @@ -869,10 +1034,26 @@ esprima@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.0.tgz#4499eddcd1110e0b218bacf2fa7f7f59f55ca804" +esquery@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708" + dependencies: + estraverse "^4.0.0" + +esrecurse@^4.1.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" + dependencies: + estraverse "^4.1.0" + estraverse@^1.9.1: version "1.9.3" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.9.3.tgz#af67f2dc922582415950926091a4005d29c9bb44" +estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1: + version "4.2.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" + esutils@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" @@ -1068,6 +1249,13 @@ figures@^2.0.0: dependencies: escape-string-regexp "^1.0.5" +file-entry-cache@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361" + dependencies: + flat-cache "^1.2.1" + object-assign "^4.0.1" + fill-range@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" @@ -1122,6 +1310,15 @@ flagged-respawn@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/flagged-respawn/-/flagged-respawn-1.0.0.tgz#4e79ae9b2eb38bf86b3bb56bf3e0a56aa5fcabd7" +flat-cache@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.3.0.tgz#d3030b32b38154f4e3b7e9c709f490f7ef97c481" + dependencies: + circular-json "^0.3.1" + del "^2.0.2" + graceful-fs "^4.1.2" + write "^0.2.1" + for-in@^1.0.1, for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" @@ -1222,6 +1419,10 @@ fstream@^1.0.0, fstream@^1.0.10, fstream@^1.0.2: mkdirp ">=0.5 0" rimraf "2" +functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + gauge@~2.7.3: version "2.7.4" resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" @@ -1304,7 +1505,7 @@ glob@^6.0.1: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.0.3, glob@^7.0.5: +glob@^7.0.3, glob@^7.0.5, glob@^7.1.2: version "7.1.2" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" dependencies: @@ -1339,6 +1540,21 @@ global-prefix@^1.0.1: is-windows "^1.0.1" which "^1.2.14" +globals@^11.0.1: + version "11.5.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.5.0.tgz#6bc840de6771173b191f13d3a9c94d441ee92642" + +globby@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d" + dependencies: + array-union "^1.0.1" + arrify "^1.0.0" + glob "^7.0.3" + object-assign "^4.0.1" + pify "^2.0.0" + pinkie-promise "^2.0.0" + globby@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c" @@ -1541,6 +1757,10 @@ ignore-by-default@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09" +ignore@^3.3.3: + version "3.3.8" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.8.tgz#3f8e9c35d38708a3a7e0e9abb6c73e7ee7707b2b" + import-lazy@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43" @@ -1789,6 +2009,10 @@ is-relative@^1.0.0: dependencies: is-unc-path "^1.0.0" +is-resolvable@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" + is-retry-allowed@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz#11a060568b67339444033d0125a61a20d564fb34" @@ -1876,7 +2100,11 @@ js-beautify@1.6.8: mkdirp "~0.5.0" nopt "~3.0.1" -js-yaml@3.x: +js-tokens@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" + +js-yaml@3.x, js-yaml@^3.9.1: version "3.11.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.11.0.tgz#597c1a8bd57152f26d622ce4117851a51f5ebaef" dependencies: @@ -1899,6 +2127,10 @@ json-schema@0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + json-stable-stringify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" @@ -1976,7 +2208,7 @@ lcid@^1.0.0: dependencies: invert-kv "^1.0.0" -levn@~0.3.0: +levn@^0.3.0, levn@~0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" dependencies: @@ -2067,6 +2299,10 @@ locate-path@^2.0.0: p-locate "^2.0.0" path-exists "^3.0.0" +lodash@3.8.0: + version "3.8.0" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.8.0.tgz#376eb98bdcd9382a9365c33c4cb8250de1325b91" + lodash@4.17.10: version "4.17.10" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" @@ -2113,6 +2349,10 @@ loud-rejection@^1.0.0: currently-unhandled "^0.4.1" signal-exit "^3.0.0" +lower-case@^1.1.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac" + lowercase-keys@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306" @@ -2358,6 +2598,10 @@ nanomatch@^1.2.9: snapdragon "^0.8.1" to-regex "^3.0.1" +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + nconf@^0.10.0: version "0.10.0" resolved "https://registry.yarnpkg.com/nconf/-/nconf-0.10.0.tgz#da1285ee95d0a922ca6cee75adcf861f48205ad2" @@ -2375,6 +2619,12 @@ negotiator@0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" +no-case@^2.2.0: + version "2.3.2" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac" + dependencies: + lower-case "^1.1.1" + node-loggly-bulk@^2.2.1: version "2.2.2" resolved "https://registry.yarnpkg.com/node-loggly-bulk/-/node-loggly-bulk-2.2.2.tgz#4180fdf0ec0a490c5a2edd68cf728fd4a5f60df8" @@ -2577,7 +2827,7 @@ optimist@^0.6.1: minimist "~0.0.1" wordwrap "~0.0.2" -optionator@^0.8.1: +optionator@^0.8.1, optionator@^0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" dependencies: @@ -2708,7 +2958,7 @@ path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" -path-is-inside@^1.0.1: +path-is-inside@^1.0.1, path-is-inside@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" @@ -2776,6 +3026,10 @@ pinkie@^2.0.0: version "2.0.4" resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" +pluralize@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777" + posix-character-classes@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" @@ -2803,6 +3057,10 @@ process-nextick-args@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" +progress@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.0.tgz#8a1be366bf8fc23db2bd23f10c6fe920b4389d1f" + proto-list@~1.2.1: version "1.2.4" resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" @@ -2933,6 +3191,10 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" +regexpp@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-1.1.0.tgz#0e3516dd0b7904f413d2d4193dce4618c3a689ab" + registry-auth-token@^3.0.1: version "3.3.2" resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-3.3.2.tgz#851fd49038eecb586911115af845260eec983f20" @@ -3022,6 +3284,19 @@ require-dir@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/require-dir/-/require-dir-0.3.2.tgz#c1d5c75e9fbffde9f2e6b33e383db4f594b5a6a9" +require-folder-tree@^1.4.5: + version "1.4.5" + resolved "https://registry.yarnpkg.com/require-folder-tree/-/require-folder-tree-1.4.5.tgz#dfe553cbab98cc88e1c56a3f2f358f06ef85bcb0" + dependencies: + lodash "3.8.0" + +require-uncached@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3" + dependencies: + caller-path "^0.1.0" + resolve-from "^1.0.0" + resolve-dir@^1.0.0, resolve-dir@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/resolve-dir/-/resolve-dir-1.0.1.tgz#79a40644c362be82f26effe739c9bb5382046f43" @@ -3029,6 +3304,10 @@ resolve-dir@^1.0.0, resolve-dir@^1.0.1: expand-tilde "^2.0.0" global-modules "^1.0.0" +resolve-from@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226" + resolve-url@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" @@ -3283,6 +3562,18 @@ slice-ansi@0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" +slice-ansi@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-1.0.0.tgz#044f1a49d8842ff307aad6b505ed178bd950134d" + dependencies: + is-fullwidth-code-point "^2.0.0" + +snake-case@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-2.1.0.tgz#41bdb1b73f30ec66a04d4e2cad1b76387d4d6d9f" + dependencies: + no-case "^2.2.0" + snapdragon-node@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" @@ -3563,6 +3854,17 @@ symbol-observable@^1.0.1: version "1.2.0" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" +table@4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/table/-/table-4.0.2.tgz#a33447375391e766ad34d3486e6e2aedc84d2e36" + dependencies: + ajv "^5.2.3" + ajv-keywords "^2.1.0" + chalk "^2.1.0" + lodash "^4.17.4" + slice-ansi "1.0.0" + string-width "^2.1.1" + tar-pack@^3.4.0: version "3.4.1" resolved "https://registry.yarnpkg.com/tar-pack/-/tar-pack-3.4.1.tgz#e1dbc03a9b9d3ba07e896ad027317eb679a10a1f" @@ -3590,6 +3892,10 @@ term-size@^1.2.0: dependencies: execa "^0.7.0" +text-table@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + through@2, through@^2.3.6, through@^2.3.8, through@~2.3, through@~2.3.1: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" @@ -3880,6 +4186,12 @@ write-file-atomic@^2.0.0: imurmurhash "^0.1.4" signal-exit "^3.0.2" +write@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757" + dependencies: + mkdirp "^0.5.1" + xdg-basedir@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4"