diff --git a/core/server/config/overrides.json b/core/server/config/overrides.json index 320a436b90..63a493d2d4 100644 --- a/core/server/config/overrides.json +++ b/core/server/config/overrides.json @@ -67,6 +67,7 @@ }, "api": { "versions": { + "all": ["v0.1", "v2"], "active": "v2", "stable": "", "deprecated": "v0.1", diff --git a/core/server/services/themes/active.js b/core/server/services/themes/active.js index e0ed02292a..0883aa5b4b 100644 --- a/core/server/services/themes/active.js +++ b/core/server/services/themes/active.js @@ -14,6 +14,7 @@ var join = require('path').join, _ = require('lodash'), themeConfig = require('./config'), + themeEngines = require('./engines'), config = require('../../config'), engine = require('./engine'), // Current instance of ActiveTheme @@ -45,6 +46,9 @@ class ActiveTheme { // Create a theme config object this._config = themeConfig.create(this._packageInfo); + + // Create a theme engines object + this._engines = themeEngines.create(this._packageInfo); } get name() { @@ -83,6 +87,10 @@ class ActiveTheme { return this._config[key]; } + engine(key) { + return this._engines[key]; + } + mount(siteApp) { // reset the asset hash // @TODO: set this on the theme instead of globally, or use proper file-based hash diff --git a/core/server/services/themes/engines/create.js b/core/server/services/themes/engines/create.js new file mode 100644 index 0000000000..d8d2bcdac5 --- /dev/null +++ b/core/server/services/themes/engines/create.js @@ -0,0 +1,49 @@ +const _ = require('lodash'); +const semver = require('semver'); +const config = require('../../../config'); +const DEFAULTS = require('./defaults'); +const allowedKeys = ['ghost-api']; + +/** + * Valid definitions for "ghost-api": + * + * ^0.1 + * ^2 + * ^0.1.0 + * ^2.0.0 + * 2.0.0 + * v2 + * v0.1 + * + * Goal: Extract major version from input. + * + * @param packageJson + * @returns {*} + */ +module.exports = (packageJson) => { + let themeEngines = _.cloneDeep(DEFAULTS); + + if (packageJson && packageJson.hasOwnProperty('engines')) { + // CASE: validate + if (packageJson.engines['ghost-api']) { + const availableApiVersions = {}; + + config.get('api:versions:all').forEach((version) => { + availableApiVersions[semver(semver.coerce(version).version).major] = version; + }); + + const apiVersion = packageJson.engines['ghost-api']; + const apiVersionMajor = semver(semver.coerce(apiVersion).version).major; + + if (availableApiVersions[apiVersionMajor]) { + packageJson.engines['ghost-api'] = availableApiVersions[apiVersionMajor]; + } else { + packageJson.engines['ghost-api'] = 'v0.1'; + } + } + + themeEngines = _.assign(themeEngines, _.pick(packageJson.engines, allowedKeys)); + } + + return themeEngines; +}; diff --git a/core/server/services/themes/engines/defaults.json b/core/server/services/themes/engines/defaults.json new file mode 100644 index 0000000000..b91acae5cf --- /dev/null +++ b/core/server/services/themes/engines/defaults.json @@ -0,0 +1,3 @@ +{ + "ghost-api": "v0.1" +} diff --git a/core/server/services/themes/engines/index.js b/core/server/services/themes/engines/index.js new file mode 100644 index 0000000000..7ab24dbedd --- /dev/null +++ b/core/server/services/themes/engines/index.js @@ -0,0 +1,5 @@ +module.exports = { + get create() { + return require('./create'); + } +}; diff --git a/core/test/unit/services/themes/engines/create_spec.js b/core/test/unit/services/themes/engines/create_spec.js new file mode 100644 index 0000000000..03c20dd3fb --- /dev/null +++ b/core/test/unit/services/themes/engines/create_spec.js @@ -0,0 +1,103 @@ +const should = require('should'); +const sinon = require('sinon'); +const themeEngines = require('../../../../../server/services/themes/engines'); +const sandbox = sinon.sandbox.create(); + +describe('Themes: engines', function () { + afterEach(function () { + sandbox.restore(); + }); + + it('no engines', function () { + const engines = themeEngines.create(); + engines.should.eql({ + 'ghost-api': 'v0.1' + }); + }); + + describe('ghost-api', function () { + it('v2', function () { + const engines = themeEngines.create({ + engines: { + 'ghost-api': 'v2' + } + }); + + engines.should.eql({ + 'ghost-api': 'v2' + }); + }); + + it('v10', function () { + const engines = themeEngines.create({ + engines: { + 'ghost-api': 'v10' + } + }); + + engines.should.eql({ + 'ghost-api': 'v0.1' + }); + }); + + it('^0.1', function () { + const engines = themeEngines.create({ + engines: { + 'ghost-api': '^0.1' + } + }); + + engines.should.eql({ + 'ghost-api': 'v0.1' + }); + }); + + it('^2', function () { + const engines = themeEngines.create({ + engines: { + 'ghost-api': '^2' + } + }); + + engines.should.eql({ + 'ghost-api': 'v2' + }); + }); + + it('2.0.0', function () { + const engines = themeEngines.create({ + engines: { + 'ghost-api': '2.0.0' + } + }); + + engines.should.eql({ + 'ghost-api': 'v2' + }); + }); + + it('2.17.0', function () { + const engines = themeEngines.create({ + engines: { + 'ghost-api': '2.17.0' + } + }); + + engines.should.eql({ + 'ghost-api': 'v2' + }); + }); + + it('3', function () { + const engines = themeEngines.create({ + engines: { + 'ghost-api': '3' + } + }); + + engines.should.eql({ + 'ghost-api': 'v0.1' + }); + }); + }); +});