@viewdo/dxp-story-cli
Version:
README.md
594 lines (470 loc) • 19.4 kB
JavaScript
const isUndefined = require('lodash/isUndefined');
const omitBy = require('lodash/omitBy');
const isObject = require('lodash/isObject');
const isEmpty = require('lodash/isEmpty');
const sortBy = require('lodash/sortBy');
const PathManager = require('./path-manager');
const fs = require('fs');
class iVXjsReader {
constructor(ivx_js_config = {}, source = "./src", opts = {}) {
const { story_key, episode_key } = opts;
Object.assign(this, {
PathManager,
ivx_js_config,
story_key,
episode_key,
handlers: [],
omitBy,
isUndefined,
sortBy,
isEmpty,
isObject
});
this._setUp(source)
}
_setUp(source) {
const { story_key, episode_key, ivx_js_config = {}, PathManager } = this;
const { metadata = {} } = ivx_js_config;
const new_metatdata = Object.assign({}, metadata, {
storyKey: story_key,
});
if (episode_key) new_metatdata.episodeKey = episode_key;
const updated_ivx_js_config = Object.assign({}, ivx_js_config, {
metadata: new_metatdata
});
const path_manager = new PathManager(source, '', {
story_key: new_metatdata.storyKey,
episode_key: new_metatdata.episodeKey
})
Object.assign(this, {
path_manager,
source: path_manager.getSource(),
ivx_js_config: updated_ivx_js_config,
story_key: new_metatdata.storyKey,
episode_key: new_metatdata.episodeKey
});
return this;
}
read(cb = () => { }) {
const { isEmpty } = this;
while (!isEmpty(this.handlers)) {
const handler = this.handlers.pop();
handler();
}
cb();
return this;
}
handleiVXjsConfig(callback) {
const { ivx_js_config, path_manager, story_key, episode_key } = this;
const story_opts = {
story_key,
episode_key
};
const states_folder_path = path_manager.getStatesFolderPath();
const page_elements_path = path_manager.getPageElementsFolderPath();
const metadata_path = path_manager.getMetadataFolderPath();
const static_data_path = path_manager.getStaticDataFolderPath();
const parent_states = this._getParentStates(ivx_js_config);
const config_path = path_manager.getStoryConfigSourcePath(story_key, episode_key);
const opts = {
states_folder_path,
page_elements_path,
parent_states,
metadata_path,
static_data_path,
config_path
};
const ivx_js_config_opts = Object.assign({}, story_opts, opts)
const handler = () => {
if (callback) callback(ivx_js_config, ivx_js_config_opts);
}
this._addHandlers([handler]);
return this;
}
handleStates(callback) {
const self = this;
const { path_manager, sortBy } = this;
const states_folder_path = path_manager.getStatesFolderPath();
const common_states_folder_path = path_manager.getCommonStatesFolderPath();
const states_opts = {
states_folder_path,
common_states_folder_path
}
let states = [];
let common_states = [];
return this
.handleiVXjsConfig((ivx_js_config) => {
states_opts.ivx_js_config = ivx_js_config;
states = sortBy(ivx_js_config.states, ['id']);
states.forEach((state) => {
const opts = self._getStateOptions(state, ivx_js_config, common_states);
const { embedded_parent_states = [] } = opts;
if (embedded_parent_states.length > 1) {
common_states = [...common_states, state];
}
});
})
.read(() => {
states_opts.common_states = common_states.sort((a, b) => {
if (a.id < b.id) return -1;
if (a.id > b.id) return 1;
return 0;
});
const handler = () => {
if (callback) callback(states, states_opts);
}
self._addHandlers([handler]);
});
}
handleState(callback) {
const self = this;
return this
.handleStates((states = [], opts = {}) => {
const { ivx_js_config = {}, common_states = [] } = opts;
states.forEach(state => {
self._createStateHandler(state, ivx_js_config, common_states, callback);
})
})
.read();
}
handleHtmlState(callback) {
const self = this;
return this
.handleStates((states = [], opts = {}) => {
const { ivx_js_config = {}, common_states } = opts;
const html_states = states.filter(state => state.type === 'html');
html_states.forEach((html_state) => {
self._createStateHandler(html_state, ivx_js_config, common_states, callback);
});
}).read();
}
handleVideoState(callback) {
const self = this;
const { path_manager } = self;
return this.handleStates((states, { ivx_js_config, common_states }) => {
const video_states = states.filter(state => state.type === 'video');
video_states.forEach((video_state) => {
const state_opts = self._getStateOptions(video_state, ivx_js_config, common_states);
const { state_path } = state_opts;
const tracks_folder_path = path_manager.getTracksFolderPath(state_path);
const opts = Object.assign(state_opts,
{
tracks_folder_path
}
)
const handler = () => {
if (callback) callback(video_state, opts);
}
self._addHandlers([handler]);
});
}).read();
}
handleVideoTrack(callback) {
const self = this;
return this.handleVideoState((video_state, opts) => {
const { playerSettings: player_settings = {} } = video_state;
const { tracks = [] } = player_settings;
const handlers = tracks.map(track => {
return () => {
if (callback) callback(track, video_state, opts);
}
});
self._addHandlers(handlers);
}).read();
}
handleNavigationState(callback) {
const self = this;
return this.handleStates((states, { common_states, ivx_js_config }) => {
const navigation_states = states.filter(state => state.type === 'navigation');
navigation_states.forEach((navigation_state) => {
self._createStateHandler(navigation_state, ivx_js_config, common_states, callback);
});
}).read();
}
handleNavigationLink(callback) {
const self = this;
const { path_manager } = this;
return this.handleNavigationState((navigation_state, state_opts) => {
const { links = [] } = navigation_state;
const handlers = links.map((link, index) => {
return () => {
let link_opts = Object.assign(state_opts, {
index
});
const link_path = path_manager.getNavigationLinkFolderPath(link, navigation_state, link_opts);
link_opts.link_path = link_path;
if (callback) callback(link, navigation_state, link_opts);
}
});
self._addHandlers(handlers);
}).read();
}
handleInputState(callback) {
const self = this;
return this.handleStates((states, { ivx_js_config, common_states }) => {
const input_states = states.filter(state => state.type === 'input');
input_states.forEach((input_state) => {
self._createStateHandler(input_state, ivx_js_config, common_states, callback);
});
}).read();
}
handleInput(callback) {
const self = this;
const { path_manager } = this;
return this.handleInputState((input_state, state_opts) => {
const { inputs = [] } = input_state;
const handlers = inputs.map((input, index) => {
return () => {
const input_path = path_manager.getInputFolderPath(input, input_state, state_opts);
const input_template_path = path_manager.getTemplatesFolderPath(input_path);
const input_opts = Object.assign(state_opts, {
index,
input_path,
input_template_path
});
if (callback) callback(input, input_state, input_opts);
}
});
self._addHandlers(handlers);
}).read();
}
_createStateHandler(state, ivx_js_config, common_states, callback) {
const opts = this._getStateOptions(state, ivx_js_config, common_states);
const handler = () => {
if (callback) callback(state, opts);
}
this._addHandlers([handler]);
}
_getStateOptions(state, ivx_js_config, common_states = []) {
const { path_manager } = this;
const { metadata = {} } = ivx_js_config;
const { storyKey: story_key, episodeKey: episode_key } = metadata;
const parent_states = this._getParentStates(ivx_js_config);
let opts = {
ivx_js_config,
common_states,
story_key,
episode_key
};
if (state.embedded) {
const { parent_state, embedded_view, embedded_parent_states } = this._getEmbeddedView(state, parent_states) || {};
opts = Object.assign(opts, {
embedded_parent_states,
parent_state,
embedded_view
})
}
if (!state.embedded) {
const state_embedded_views_path = path_manager.getEmbeddedViewsFolderPath(state, opts);
const state_embedded_view_paths = path_manager.getEmbeddedViewFolderPaths(state, opts);
opts = Object.assign(opts, {
state_embedded_views_path,
state_embedded_view_paths
})
}
if (state.type === 'navigation') {
opts = Object.assign(opts, {
state_links_path: path_manager.getNavigationLinksFolderPath(state, opts)
})
}
if (state.type === 'input') {
opts = Object.assign(opts, {
state_inputs_path: path_manager.getInputsFolderPath(state, opts)
})
}
const state_path = path_manager.getStateFolderPath(state, opts);
const state_events_path = path_manager.getEventsFolderPath(state_path);
const state_templates_path = path_manager.getTemplatesFolderPath(state_path);
const state_paths = {
state_path,
state_events_path,
state_templates_path
}
opts = Object.assign(opts, state_paths);
return opts;
}
_getParentStates(ivx_js_config) {
const { states = [] } = ivx_js_config;
return states.filter(state => !state.embedded);
}
_getEmbeddedView(embedded_state, parent_states) {
const { isUndefined } = this;
let embedded_view = null;
const { id: embedded_state_id } = embedded_state;
const embedded_parent_states = parent_states.filter((parent_state) => {
const { embeddedViews: embedded_views = [] } = parent_state;
const is_child = embedded_views.reduce((is_child_check, current_embedded_view) => {
if (is_child_check) return is_child_check;
const { states = [] } = current_embedded_view;
const contains_view = !isUndefined(states.find(state => state.stateId === embedded_state_id));
if (contains_view) embedded_view = current_embedded_view;
return contains_view;
}, false);
return is_child;
});
const parent_state = parent_states.find((parent_state) => {
const { embeddedViews: embedded_views = [] } = parent_state;
const is_child = embedded_views.reduce((is_child_check, current_embedded_view) => {
if (is_child_check) return is_child_check;
const { states = [] } = current_embedded_view;
const contains_view = !isUndefined(states.find(state => state.stateId === embedded_state_id));
if (contains_view) embedded_view = current_embedded_view;
return contains_view;
}, false);
return is_child;
});
return {
embedded_parent_states,
parent_state,
embedded_view
}
}
handlePageElement(callback) {
const self = this;
return this.handleiVXjsConfig((ivx_js_config) => {
const { pageElements: page_elements = [] } = ivx_js_config;
const handlers = page_elements.map((page_element) => {
const opts = self._getPageElementOptions(page_element, ivx_js_config);
return () => {
if (callback) callback(page_element, opts);
}
});
self._addHandlers(handlers);
}).read();
}
handleCtaLinkPageElement(callback) {
const self = this;
return this.handleiVXjsConfig((ivx_js_config) => {
self._handlePageElementsByType(ivx_js_config, 'cta-link', callback);
}).read();
}
handleCtaOverlayPageElement(callback) {
const self = this;
return this.handleiVXjsConfig((ivx_js_config) => {
self._handlePageElementsByType(ivx_js_config, 'overlay', callback);
}).read();
}
handleHtmlPageElement(callback) {
const self = this;
return this.handleiVXjsConfig((ivx_js_config) => {
self._handlePageElementsByType(ivx_js_config, 'html', callback);
}).read();
}
handleImagePageElement(callback) {
const self = this;
return this.handleiVXjsConfig((ivx_js_config) => {
self._handlePageElementsByType(ivx_js_config, 'image', callback);
}).read();
}
_handlePageElementsByType(ivx_js_config, type, callback) {
const self = this;
const { pageElements: page_elements = [] } = ivx_js_config;
const typed_page_elements = page_elements.filter(pageElement => pageElement.type === type);
const handlers = typed_page_elements.map((page_element) => {
const opts = self._getPageElementOptions(page_element, ivx_js_config);
return () => {
if (callback) callback(page_element, opts);
}
});
this._addHandlers(handlers);
}
_getPageElementOptions(page_element, ivx_js_config) {
const { path_manager } = this;
const { metadata = {} } = ivx_js_config;
const { storyKey: story_key, episodeKey: episode_key } = metadata;
const story_opts = {
story_key,
episode_key
}
const page_element_path = path_manager.getPageElementFolderPath(page_element, story_opts);
const page_element_events_path = path_manager.getEventsFolderPath(page_element_path);
const page_element_templates_path = path_manager.getTemplatesFolderPath(page_element_path);
return Object.assign(story_opts, {
ivx_js_config,
page_element_path,
page_element_templates_path,
page_element_events_path
});
}
handleMetadata(callback) {
const self = this;
const { path_manager } = this;
return this.handleiVXjsConfig((ivx_js_config) => {
const handler = () => {
const { metadata = {} } = ivx_js_config;
const { storyKey: story_key, episodeKey: episode_key } = metadata;
const metadata_path = path_manager.getMetadataFolderPath();
const opts = {
metadata_path,
story_key,
episode_key
}
if (callback) callback(metadata, opts);
}
self._addHandlers([handler]);
}).read();
}
handleStaticData(callback) {
const self = this;
const { path_manager, omitBy, isObject } = this;
return this.handleMetadata(metadata => {
const handler = () => {
const { data: static_data = {}, storyKey: story_key, episodeKey: episode_key } = metadata;
const story_opts = {
story_key,
episode_key
};
const static_data_path = path_manager.getStaticDataFolderPath();
const complex_data = Object.keys(static_data)
.reduce((current_complex_data, key) => {
const current_data = static_data[key];
const is_complex = Array.isArray(current_data) || isObject(current_data);
if (is_complex) current_complex_data[key] = current_data;
return current_complex_data;
}, {})
const static_data_complex_paths = path_manager.getStaticDataPaths(static_data);
const basic_data = omitBy(static_data, (property, key) => {
return static_data_complex_paths.find((data) => data.key === key);
});
const opts = Object.assign(story_opts, {
static_data_path,
static_data_complex_paths,
basic_data,
complex_data
});
if (callback) callback(static_data, opts);
}
self._addHandlers([handler]);
}).read();
}
_getBasicData(data_obj) {
const self = this;
return Object.keys(data_obj).reduce((basic_data, key) => {
const value = data_obj[key];
if (!self._isComplex(value)) basic_data[key] = value;
return basic_data;
}, {});
}
_getComplexData(data_obj) {
const self = this;
return Object.keys(data_obj).reduce((basic_data, key) => {
const value = data_obj[key];
if (self._isComplex(value)) basic_data[key] = value;
return basic_data;
}, {});
}
_isComplex(value) {
const { isObject } = this;
return isObject(value) || Array.isArray(value);
}
_addHandlers(handlers) {
const old_handlers = [...this.handlers];
Object.assign(this, {
handlers: [
...handlers,
...old_handlers
]
});
}
}
module.exports = iVXjsReader;