allow manual initialization and config as an arg
This commit is contained in:
parent
95b6d8a884
commit
a83c04cad0
@ -43,7 +43,6 @@
|
||||
"setupFiles": [
|
||||
"./setupTests.js"
|
||||
],
|
||||
"mapCoverage": true,
|
||||
"coverageReporters": [
|
||||
"lcov"
|
||||
],
|
||||
@ -77,7 +76,7 @@
|
||||
"babel": "^6.5.2",
|
||||
"babel-cli": "^6.18.0",
|
||||
"babel-core": "^6.23.1",
|
||||
"babel-jest": "^21.2.0",
|
||||
"babel-jest": "^22.0.0",
|
||||
"babel-loader": "^7.0.0",
|
||||
"babel-plugin-lodash": "^3.2.0",
|
||||
"babel-plugin-module-resolver": "^3.0.0",
|
||||
@ -104,8 +103,8 @@
|
||||
"file-loader": "^1.1.4",
|
||||
"identity-obj-proxy": "^3.0.0",
|
||||
"imports-loader": "^0.7.1",
|
||||
"jest": "^21.2.1",
|
||||
"jest-cli": "^21.2.1",
|
||||
"jest": "^22.0.0",
|
||||
"jest-cli": "^22.0.0",
|
||||
"lint-staged": "^3.3.1",
|
||||
"npm-check": "^5.2.3",
|
||||
"postcss-cssnext": "^3.0.2",
|
||||
|
@ -1,61 +1,64 @@
|
||||
import { fromJS } from 'immutable';
|
||||
import { applyDefaults, validateConfig } from '../config';
|
||||
|
||||
describe('config', () => {
|
||||
describe('applyDefaults', () => {
|
||||
it('should set publish_mode if not set', () => {
|
||||
expect(applyDefaults({
|
||||
const config = fromJS({
|
||||
foo: 'bar',
|
||||
media_folder: 'path/to/media',
|
||||
})).toEqual({
|
||||
foo: 'bar',
|
||||
publish_mode: 'simple',
|
||||
media_folder: 'path/to/media',
|
||||
public_folder: '/path/to/media',
|
||||
});
|
||||
expect(
|
||||
applyDefaults(config)
|
||||
).toEqual(
|
||||
config.set('publish_mode', 'simple')
|
||||
);
|
||||
});
|
||||
|
||||
it('should set publish_mode from config', () => {
|
||||
expect(applyDefaults({
|
||||
foo: 'bar',
|
||||
publish_mode: 'complex',
|
||||
media_folder: 'path/to/media',
|
||||
})).toEqual({
|
||||
const config = fromJS({
|
||||
foo: 'bar',
|
||||
publish_mode: 'complex',
|
||||
media_folder: 'path/to/media',
|
||||
public_folder: '/path/to/media',
|
||||
});
|
||||
expect(
|
||||
applyDefaults(config)
|
||||
).toEqual(
|
||||
config
|
||||
);
|
||||
});
|
||||
|
||||
it('should set public_folder based on media_folder if not set', () => {
|
||||
expect(applyDefaults({
|
||||
expect(applyDefaults(fromJS({
|
||||
foo: 'bar',
|
||||
media_folder: 'path/to/media',
|
||||
})).toEqual({
|
||||
}))).toEqual(fromJS({
|
||||
foo: 'bar',
|
||||
publish_mode: 'simple',
|
||||
media_folder: 'path/to/media',
|
||||
public_folder: '/path/to/media',
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
it('should not overwrite public_folder if set', () => {
|
||||
expect(applyDefaults({
|
||||
expect(applyDefaults(fromJS({
|
||||
foo: 'bar',
|
||||
media_folder: 'path/to/media',
|
||||
public_folder: '/publib/path',
|
||||
})).toEqual({
|
||||
}))).toEqual(fromJS({
|
||||
foo: 'bar',
|
||||
publish_mode: 'simple',
|
||||
media_folder: 'path/to/media',
|
||||
public_folder: '/publib/path',
|
||||
});
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateConfig', () => {
|
||||
it('should return the config if no errors', () => {
|
||||
const config = { foo: 'bar', backend: { name: 'bar' }, media_folder: 'baz', collections: [{}] };
|
||||
const config = fromJS({ foo: 'bar', backend: { name: 'bar' }, media_folder: 'baz', collections: [{}] });
|
||||
expect(
|
||||
validateConfig(config)
|
||||
).toEqual(config);
|
||||
@ -63,55 +66,55 @@ describe('config', () => {
|
||||
|
||||
it('should throw if backend is not defined in config', () => {
|
||||
expect(() => {
|
||||
validateConfig({ foo: 'bar' });
|
||||
validateConfig(fromJS({ foo: 'bar' }));
|
||||
}).toThrowError('Error in configuration file: A `backend` wasn\'t found. Check your config.yml file.');
|
||||
});
|
||||
|
||||
it('should throw if backend name is not defined in config', () => {
|
||||
expect(() => {
|
||||
validateConfig({ foo: 'bar', backend: {} });
|
||||
validateConfig(fromJS({ foo: 'bar', backend: {} }));
|
||||
}).toThrowError('Error in configuration file: A `backend.name` wasn\'t found. Check your config.yml file.');
|
||||
});
|
||||
|
||||
it('should throw if backend name is not a string in config', () => {
|
||||
expect(() => {
|
||||
validateConfig({ foo: 'bar', backend: { name: { } } });
|
||||
validateConfig(fromJS({ foo: 'bar', backend: { name: { } } }));
|
||||
}).toThrowError('Error in configuration file: Your `backend.name` must be a string. Check your config.yml file.');
|
||||
});
|
||||
|
||||
it('should throw if media_folder is not defined in config', () => {
|
||||
expect(() => {
|
||||
validateConfig({ foo: 'bar', backend: { name: 'bar' } });
|
||||
validateConfig(fromJS({ foo: 'bar', backend: { name: 'bar' } }));
|
||||
}).toThrowError('Error in configuration file: A `media_folder` wasn\'t found. Check your config.yml file.');
|
||||
});
|
||||
|
||||
it('should throw if media_folder is not a string in config', () => {
|
||||
expect(() => {
|
||||
validateConfig({ foo: 'bar', backend: { name: 'bar' }, media_folder: {} });
|
||||
validateConfig(fromJS({ foo: 'bar', backend: { name: 'bar' }, media_folder: {} }));
|
||||
}).toThrowError('Error in configuration file: Your `media_folder` must be a string. Check your config.yml file.');
|
||||
});
|
||||
|
||||
it('should throw if collections is not defined in config', () => {
|
||||
expect(() => {
|
||||
validateConfig({ foo: 'bar', backend: { name: 'bar' }, media_folder: 'baz' });
|
||||
validateConfig(fromJS({ foo: 'bar', backend: { name: 'bar' }, media_folder: 'baz' }));
|
||||
}).toThrowError('Error in configuration file: A `collections` wasn\'t found. Check your config.yml file.');
|
||||
});
|
||||
|
||||
it('should throw if collections not an array in config', () => {
|
||||
expect(() => {
|
||||
validateConfig({ foo: 'bar', backend: { name: 'bar' }, media_folder: 'baz', collections: {} });
|
||||
validateConfig(fromJS({ foo: 'bar', backend: { name: 'bar' }, media_folder: 'baz', collections: {} }));
|
||||
}).toThrowError('Error in configuration file: Your `collections` must be an array with at least one element. Check your config.yml file.');
|
||||
});
|
||||
|
||||
it('should throw if collections is an empty array in config', () => {
|
||||
expect(() => {
|
||||
validateConfig({ foo: 'bar', backend: { name: 'bar' }, media_folder: 'baz', collections: [] });
|
||||
validateConfig(fromJS({ foo: 'bar', backend: { name: 'bar' }, media_folder: 'baz', collections: [] }));
|
||||
}).toThrowError('Error in configuration file: Your `collections` must be an array with at least one element. Check your config.yml file.');
|
||||
});
|
||||
|
||||
it('should throw if collections is an array with a single null element in config', () => {
|
||||
expect(() => {
|
||||
validateConfig({ foo: 'bar', backend: { name: 'bar' }, media_folder: 'baz', collections: [null] });
|
||||
validateConfig(fromJS({ foo: 'bar', backend: { name: 'bar' }, media_folder: 'baz', collections: [null] }));
|
||||
}).toThrowError('Error in configuration file: Your `collections` must be an array with at least one element. Check your config.yml file.');
|
||||
});
|
||||
});
|
||||
|
@ -1,50 +1,63 @@
|
||||
import yaml from "js-yaml";
|
||||
import { set, defaultsDeep, get } from "lodash";
|
||||
import { Map, List, fromJS } from "immutable";
|
||||
import { trimStart, flow } from "lodash";
|
||||
import { authenticateUser } from "Actions/auth";
|
||||
import * as publishModes from "Constants/publishModes";
|
||||
|
||||
export const CONFIG_REQUEST = "CONFIG_REQUEST";
|
||||
export const CONFIG_SUCCESS = "CONFIG_SUCCESS";
|
||||
export const CONFIG_FAILURE = "CONFIG_FAILURE";
|
||||
export const CONFIG_MERGE = "CONFIG_MERGE";
|
||||
|
||||
const defaults = {
|
||||
publish_mode: publishModes.SIMPLE,
|
||||
};
|
||||
|
||||
export function applyDefaults(config) {
|
||||
// Make sure there is a public folder
|
||||
set(defaults,
|
||||
"public_folder",
|
||||
config.media_folder.charAt(0) === "/" ? config.media_folder : `/${ config.media_folder }`);
|
||||
|
||||
return defaultsDeep(config, defaults);
|
||||
return Map(defaults)
|
||||
.mergeDeep(config)
|
||||
.withMutations(map => {
|
||||
/**
|
||||
* Use media_folder as default public_folder.
|
||||
*/
|
||||
const defaultPublicFolder = `/${trimStart(map.get('media_folder'), '/')}`;
|
||||
if (!map.get('public_folder')) {
|
||||
map.set('public_folder', defaultPublicFolder);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function validateConfig(config) {
|
||||
if (!get(config, 'backend')) {
|
||||
if (!config.get('backend')) {
|
||||
throw new Error("Error in configuration file: A `backend` wasn't found. Check your config.yml file.");
|
||||
}
|
||||
if (!get(config, ['backend', 'name'])) {
|
||||
if (!config.getIn(['backend', 'name'])) {
|
||||
throw new Error("Error in configuration file: A `backend.name` wasn't found. Check your config.yml file.");
|
||||
}
|
||||
if (typeof config.backend.name !== 'string') {
|
||||
if (typeof config.getIn(['backend', 'name']) !== 'string') {
|
||||
throw new Error("Error in configuration file: Your `backend.name` must be a string. Check your config.yml file.");
|
||||
}
|
||||
if (!get(config, 'media_folder')) {
|
||||
if (!config.get('media_folder')) {
|
||||
throw new Error("Error in configuration file: A `media_folder` wasn\'t found. Check your config.yml file.");
|
||||
}
|
||||
if (typeof config.media_folder !== 'string') {
|
||||
if (typeof config.get('media_folder') !== 'string') {
|
||||
throw new Error("Error in configuration file: Your `media_folder` must be a string. Check your config.yml file.");
|
||||
}
|
||||
if (!get(config, 'collections')) {
|
||||
if (!config.get('collections')) {
|
||||
throw new Error("Error in configuration file: A `collections` wasn\'t found. Check your config.yml file.");
|
||||
}
|
||||
if (!Array.isArray(config.collections) || config.collections.length === 0 || !config.collections[0]) {
|
||||
const collections = config.get('collections');
|
||||
if (!List.isList(collections) || collections.isEmpty() || !collections.first()) {
|
||||
throw new Error("Error in configuration file: Your `collections` must be an array with at least one element. Check your config.yml file.");
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
function mergePreloadedConfig(preloadedConfig, loadedConfig) {
|
||||
const map = fromJS(loadedConfig) || Map();
|
||||
return preloadedConfig ? preloadedConfig.mergeDeep(map) : map;
|
||||
}
|
||||
|
||||
function parseConfig(data) {
|
||||
const config = yaml.safeLoad(data);
|
||||
if (typeof CMS_ENV === "string" && config[CMS_ENV]) {
|
||||
@ -82,29 +95,40 @@ export function configDidLoad(config) {
|
||||
};
|
||||
}
|
||||
|
||||
export function mergeConfig(config) {
|
||||
return { type: CONFIG_MERGE, payload: config };
|
||||
}
|
||||
|
||||
export function loadConfig() {
|
||||
if (window.CMS_CONFIG) {
|
||||
return configDidLoad(window.CMS_CONFIG);
|
||||
return configDidLoad(fromJS(window.CMS_CONFIG));
|
||||
}
|
||||
return (dispatch) => {
|
||||
return async (dispatch, getState) => {
|
||||
dispatch(configLoading());
|
||||
|
||||
fetch("config.yml", { credentials: 'same-origin' })
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
try {
|
||||
const preloadedConfig = getState().config;
|
||||
const response = await fetch('config.yml', { credentials: 'same-origin' })
|
||||
const requestSuccess = response.status === 200;
|
||||
|
||||
if (!preloadedConfig && !requestSuccess) {
|
||||
throw new Error(`Failed to load config.yml (${ response.status })`);
|
||||
}
|
||||
return response.text();
|
||||
})
|
||||
.then(parseConfig)
|
||||
.then(validateConfig)
|
||||
.then(applyDefaults)
|
||||
.then((config) => {
|
||||
|
||||
const loadedConfig = parseConfig(requestSuccess ? await response.text() : '');
|
||||
|
||||
/**
|
||||
* Merge any existing configuration so the result can be validated.
|
||||
*/
|
||||
const mergedConfig = mergePreloadedConfig(preloadedConfig, loadedConfig)
|
||||
const config = flow(validateConfig, applyDefaults)(mergedConfig);
|
||||
|
||||
dispatch(configDidLoad(config));
|
||||
dispatch(authenticateUser());
|
||||
})
|
||||
.catch((err) => {
|
||||
}
|
||||
catch(err) {
|
||||
dispatch(configFailed(err));
|
||||
});
|
||||
throw(err)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
67
src/bootstrap.js
vendored
Normal file
67
src/bootstrap.js
vendored
Normal file
@ -0,0 +1,67 @@
|
||||
import React from 'react';
|
||||
import { render } from 'react-dom';
|
||||
import { Provider } from 'react-redux';
|
||||
import { Route } from 'react-router-dom';
|
||||
import { ConnectedRouter } from 'react-router-redux';
|
||||
import history from 'Routing/history';
|
||||
import configureStore from 'Redux/configureStore';
|
||||
import { mergeConfig } from 'Actions/config';
|
||||
import { setStore } from 'ValueObjects/AssetProxy';
|
||||
import { ErrorBoundary } from 'UI'
|
||||
import App from 'App/App';
|
||||
import 'EditorWidgets';
|
||||
import 'MarkdownPlugins';
|
||||
import './index.css';
|
||||
|
||||
function bootstrap({ config }) {
|
||||
/**
|
||||
* Log the version number.
|
||||
*/
|
||||
console.log(`Netlify CMS version ${NETLIFY_CMS_VERSION}`);
|
||||
|
||||
/**
|
||||
* Create mount element dynamically.
|
||||
*/
|
||||
const el = document.createElement('div');
|
||||
el.id = 'nc-root';
|
||||
document.body.appendChild(el);
|
||||
|
||||
/**
|
||||
* Configure Redux store.
|
||||
*/
|
||||
const store = configureStore();
|
||||
|
||||
/**
|
||||
* Dispatch config to store if received. This config will be merged into
|
||||
* config.yml if it exists, and any portion that produces a conflict will be
|
||||
* overwritten.
|
||||
*/
|
||||
if (config) {
|
||||
store.dispatch(mergeConfig(config));
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass initial state into AssetProxy factory.
|
||||
*/
|
||||
setStore(store);
|
||||
|
||||
/**
|
||||
* Create connected root component.
|
||||
*/
|
||||
const Root = () => (
|
||||
<ErrorBoundary>
|
||||
<Provider store={store}>
|
||||
<ConnectedRouter history={history}>
|
||||
<Route component={App}/>
|
||||
</ConnectedRouter>
|
||||
</Provider>
|
||||
</ErrorBoundary>
|
||||
);
|
||||
|
||||
/**
|
||||
* Render application root.
|
||||
*/
|
||||
render(<Root />, el);
|
||||
}
|
||||
|
||||
export default bootstrap;
|
@ -53,6 +53,7 @@ class App extends React.Component {
|
||||
<div>
|
||||
<p>The <code>config.yml</code> file could not be loaded or failed to parse properly.</p>
|
||||
<p><strong>Error message:</strong> {config.get('error')}</p>
|
||||
<p>Check your console for details.</p>
|
||||
</div>
|
||||
</div>);
|
||||
}
|
||||
@ -105,7 +106,6 @@ class App extends React.Component {
|
||||
openMediaLibrary,
|
||||
} = this.props;
|
||||
|
||||
|
||||
if (config === null) {
|
||||
return null;
|
||||
}
|
||||
|
54
src/index.js
54
src/index.js
@ -1,54 +1,16 @@
|
||||
/**
|
||||
* This module provides a self-initializing CMS instance with API hooks added to
|
||||
* the `window` object.
|
||||
*/
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import { render } from 'react-dom';
|
||||
import { Provider } from 'react-redux';
|
||||
import { Route } from 'react-router-dom';
|
||||
import { ConnectedRouter } from 'react-router-redux';
|
||||
import history from 'Routing/history';
|
||||
import configureStore from 'Redux/configureStore';
|
||||
import { setStore } from 'ValueObjects/AssetProxy';
|
||||
import { ErrorBoundary } from 'UI'
|
||||
import bootstrap from './bootstrap';
|
||||
import registry from 'Lib/registry';
|
||||
import App from 'App/App';
|
||||
import 'EditorWidgets';
|
||||
import 'MarkdownPlugins';
|
||||
import './index.css';
|
||||
import createReactClass from 'create-react-class';
|
||||
|
||||
/**
|
||||
* Log the version number.
|
||||
* Load the app.
|
||||
*/
|
||||
console.log(`Netlify CMS version ${NETLIFY_CMS_VERSION}`);
|
||||
|
||||
/**
|
||||
* Create mount element dynamically.
|
||||
*/
|
||||
const el = document.createElement('div');
|
||||
el.id = 'nc-root';
|
||||
document.body.appendChild(el);
|
||||
|
||||
/**
|
||||
* Configure Redux store.
|
||||
*/
|
||||
const store = configureStore();
|
||||
setStore(store);
|
||||
|
||||
/**
|
||||
* Create connected root component.
|
||||
*/
|
||||
const Root = () => (
|
||||
<ErrorBoundary>
|
||||
<Provider store={store}>
|
||||
<ConnectedRouter history={history}>
|
||||
<Route component={App}/>
|
||||
</ConnectedRouter>
|
||||
</Provider>
|
||||
</ErrorBoundary>
|
||||
);
|
||||
|
||||
/**
|
||||
* Render application root.
|
||||
*/
|
||||
render(<Root />, el);
|
||||
bootstrap();
|
||||
|
||||
/**
|
||||
* Add extension hooks to global scope.
|
||||
|
7
src/init.js
Normal file
7
src/init.js
Normal file
@ -0,0 +1,7 @@
|
||||
/**
|
||||
* This module provides manual initialization and registry hooks.
|
||||
*/
|
||||
import bootstrap from './bootstrap';
|
||||
import registry from 'Lib/registry';
|
||||
|
||||
export { bootstrap as init, registry };
|
@ -13,7 +13,7 @@ describe('collections', () => {
|
||||
|
||||
it('should load the collections from the config', () => {
|
||||
expect(
|
||||
collections(undefined, configLoaded({
|
||||
collections(undefined, configLoaded(fromJS({
|
||||
collections: [
|
||||
{
|
||||
name: 'posts',
|
||||
@ -21,7 +21,7 @@ describe('collections', () => {
|
||||
fields: [{ name: 'title', widget: 'string' }],
|
||||
},
|
||||
],
|
||||
}))
|
||||
})))
|
||||
).toEqual(
|
||||
OrderedMap({
|
||||
posts: fromJS({
|
||||
|
@ -1,21 +1,21 @@
|
||||
import Immutable from 'immutable';
|
||||
import { Map } from 'immutable';
|
||||
import { configLoaded, configLoading, configFailed } from 'Actions/config';
|
||||
import config from '../config';
|
||||
import config from 'Reducers/config';
|
||||
|
||||
describe('config', () => {
|
||||
it('should handle an empty state', () => {
|
||||
expect(
|
||||
config(undefined, {})
|
||||
).toEqual(
|
||||
null
|
||||
Map({ isFetching: true })
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle an update', () => {
|
||||
expect(
|
||||
config(Immutable.Map({ a: 'b', c: 'd' }), configLoaded({ a: 'changed', e: 'new' }))
|
||||
config(Map({ a: 'b', c: 'd' }), configLoaded(Map({ a: 'changed', e: 'new' })))
|
||||
).toEqual(
|
||||
Immutable.Map({ a: 'changed', e: 'new' })
|
||||
Map({ a: 'changed', e: 'new' })
|
||||
);
|
||||
});
|
||||
|
||||
@ -23,15 +23,15 @@ describe('config', () => {
|
||||
expect(
|
||||
config(undefined, configLoading())
|
||||
).toEqual(
|
||||
Immutable.Map({ isFetching: true })
|
||||
Map({ isFetching: true })
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle an error', () => {
|
||||
expect(
|
||||
config(Immutable.Map({ isFetching: true }), configFailed(new Error('Config could not be loaded')))
|
||||
config(Map(), configFailed(new Error('Config could not be loaded')))
|
||||
).toEqual(
|
||||
Immutable.Map({ error: 'Error: Config could not be loaded' })
|
||||
Map({ error: 'Error: Config could not be loaded' })
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { OrderedMap, fromJS } from 'immutable';
|
||||
import { List } from 'immutable';
|
||||
import { has, get, escapeRegExp } from 'lodash';
|
||||
import consoleError from 'Lib/consoleError';
|
||||
import { CONFIG_SUCCESS } from 'Actions/config';
|
||||
@ -7,42 +7,50 @@ import { INFERABLE_FIELDS } from 'Constants/fieldInference';
|
||||
import { formatByExtension, formatToExtension, supportedFormats, frontmatterFormats } from 'Formats/formats';
|
||||
|
||||
const collections = (state = null, action) => {
|
||||
const configCollections = action.payload && action.payload.collections;
|
||||
switch (action.type) {
|
||||
case CONFIG_SUCCESS:
|
||||
return OrderedMap().withMutations((map) => {
|
||||
(configCollections || []).forEach((configCollection) => {
|
||||
validateCollection(configCollection);
|
||||
if (has(configCollection, 'folder')) {
|
||||
configCollection.type = FOLDER; // eslint-disable-line no-param-reassign
|
||||
} else if (has(configCollection, 'files')) {
|
||||
configCollection.type = FILES; // eslint-disable-line no-param-reassign
|
||||
const configCollections = action.payload ? action.payload.get('collections') : List();
|
||||
configCollections.forEach(validateCollection)
|
||||
return configCollections
|
||||
.toOrderedMap()
|
||||
.map(collection => {
|
||||
if (collection.has('folder')) {
|
||||
return collection.set('type', FOLDER);
|
||||
}
|
||||
map.set(configCollection.name, fromJS(configCollection));
|
||||
});
|
||||
});
|
||||
if (collection.has('files')) {
|
||||
return collection.set('type', FILES);
|
||||
}
|
||||
})
|
||||
.mapKeys((key, collection) => collection.get('name'));
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
function validateCollection(configCollection) {
|
||||
const collectionName = get(configCollection, 'name');
|
||||
if (!has(configCollection, 'folder') && !has(configCollection, 'files')) {
|
||||
throw new Error(`Unknown collection type for collection "${ collectionName }". Collections can be either Folder based or File based.`);
|
||||
}
|
||||
if (has(configCollection, 'format') && !supportedFormats.includes(get(configCollection, 'format'))) {
|
||||
throw new Error(`Unknown collection format for collection "${ collectionName }". Supported formats are ${ supportedFormats.join(',') }`);
|
||||
}
|
||||
if (!has(configCollection, 'format') && has(configCollection, 'extension') && !formatByExtension(get(configCollection, 'extension'))) {
|
||||
// Cannot infer format from extension.
|
||||
throw new Error(`Please set a format for collection "${ collectionName }". Supported formats are ${ supportedFormats.join(',') }`);
|
||||
}
|
||||
if (has(configCollection, 'frontmatter_delimiter') && !frontmatterFormats.includes(get(configCollection, 'format'))) {
|
||||
// Cannot set custom delimiter without explicit and proper frontmatter format declaration
|
||||
throw new Error(`Please set a proper frontmatter format for collection "${ collectionName }" to use a custom delimiter. Supported frontmatter formats are yaml-frontmatter, toml-frontmatter, and json-frontmatter.`);
|
||||
}
|
||||
const {
|
||||
name,
|
||||
folder,
|
||||
files,
|
||||
format,
|
||||
extension,
|
||||
frontmatter_delimiter: delimiter
|
||||
} = configCollection.toJS();
|
||||
|
||||
if (!folder && !files) {
|
||||
throw new Error(`Unknown collection type for collection "${name}". Collections can be either Folder based or File based.`);
|
||||
}
|
||||
if (format && !supportedFormats.includes(format)) {
|
||||
throw new Error(`Unknown collection format for collection "${name}". Supported formats are ${supportedFormats.join(',')}`);
|
||||
}
|
||||
if (!format && extension && !formatByExtension(extension)) {
|
||||
// Cannot infer format from extension.
|
||||
throw new Error(`Please set a format for collection "${name}". Supported formats are ${supportedFormats.join(',')}`);
|
||||
}
|
||||
if (delimiter && !frontmatterFormats.includes(format)) {
|
||||
// Cannot set custom delimiter without explicit and proper frontmatter format declaration
|
||||
throw new Error(`Please set a proper frontmatter format for collection "${name}" to use a custom delimiter. Supported frontmatter formats are yaml-frontmatter, toml-frontmatter, and json-frontmatter.`);
|
||||
}
|
||||
}
|
||||
|
||||
const selectors = {
|
||||
|
@ -1,14 +1,21 @@
|
||||
import Immutable from 'immutable';
|
||||
import { CONFIG_REQUEST, CONFIG_SUCCESS, CONFIG_FAILURE } from 'Actions/config';
|
||||
import { Map } from 'immutable';
|
||||
import { CONFIG_REQUEST, CONFIG_SUCCESS, CONFIG_FAILURE, CONFIG_MERGE } from 'Actions/config';
|
||||
|
||||
const config = (state = null, action) => {
|
||||
const config = (state = Map({ isFetching: true }), action) => {
|
||||
switch (action.type) {
|
||||
case CONFIG_MERGE:
|
||||
return state.mergeDeep(action.payload);
|
||||
case CONFIG_REQUEST:
|
||||
return Immutable.Map({ isFetching: true });
|
||||
return state.set('isFetching', true);
|
||||
case CONFIG_SUCCESS:
|
||||
return Immutable.fromJS(action.payload);
|
||||
/**
|
||||
* The loadConfig action merges any existing config into the loaded config
|
||||
* before firing this action (so the resulting config can be validated),
|
||||
* so we don't have to merge it here.
|
||||
*/
|
||||
return action.payload.delete('isFetching');
|
||||
case CONFIG_FAILURE:
|
||||
return Immutable.Map({ error: action.payload.toString() });
|
||||
return Map({ error: action.payload.toString() });
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { fromJS } from 'immutable';
|
||||
import { fromJS, List } from 'immutable';
|
||||
import { CONFIG_SUCCESS } from 'Actions/config';
|
||||
|
||||
const integrations = (state = null, action) => {
|
||||
switch (action.type) {
|
||||
case CONFIG_SUCCESS:
|
||||
const integrations = action.payload.integrations || [];
|
||||
const integrations = action.payload.get('integrations').toJS() || [];
|
||||
const newState = integrations.reduce((acc, integration) => {
|
||||
const { hooks, collections, provider, ...providerData } = integration;
|
||||
acc.providers[provider] = { ...providerData };
|
||||
|
@ -13,6 +13,7 @@ module.exports = merge.smart(require('./webpack.base.js'), {
|
||||
`webpack-dev-server/client?http://${ HOST }:${ PORT }/`,
|
||||
'./index',
|
||||
],
|
||||
init: './init',
|
||||
},
|
||||
output: {
|
||||
path: path.join(__dirname, 'dist'),
|
||||
|
@ -8,6 +8,7 @@ const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
|
||||
module.exports = merge.smart(require('./webpack.base.js'), {
|
||||
entry: {
|
||||
cms: './index',
|
||||
init: './init',
|
||||
},
|
||||
output: {
|
||||
path: path.join(__dirname, 'dist'),
|
||||
|
Loading…
x
Reference in New Issue
Block a user