@viewdo/dxp-story-cli
Version:
README.md
269 lines (223 loc) • 8.05 kB
JavaScript
// Deps
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const express = require('express');
const app = express();
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const path = require('path');
const jsonPlugin = require("../config/json-plugin");
const iVXStoryPlayer = require('ivx-story-player');
const exphbs = require('express-handlebars');
const opn = require('opn');
// Change when we move this over to the CLI
const defaultEpisodeKey = process.env.DEFAULT_EPISODE_KEY|| "index";
// Routes
const homePage = require("./routes/home");
// Story Player Preview Configs
const tools = "https://ivx-tools.develop.dotedu.com";
const spPort = 3060;
const appConfig = {
api: {
xapi: "http://localhost:3090"
},
urls: {
tools,
storyPlayer: `http://localhost:${spPort}`
},
storyPlayerUrls: {
experienceHostUrl: `${tools}/ivx-experience/v1/ivx-experience.js`
},
environment: "dev",
preview: true,
platformXapiUrl: `https://ivx-xapi.develop.dotedu.com/v1`,
port: spPort
}
// Tests to see if the current config is for the index.js file
const _getStoryInfoFromJsConfig = (iVXConfig, config) => {
const {stories = {}, source = "./src"} = iVXConfig;
const storyKeys = Object.keys(stories);
const {entry} = config;
let storyInfo;
storyKeys.forEach(storyKey=>{
const sourcePath = `${source}/${storyKey}`;
const defaultEpisodeJs = `${sourcePath}/${defaultEpisodeKey}.js`;
if(defaultEpisodeJs === entry) storyInfo = {storyKey};
const {episodes = {}} = stories[storyKey];
const episodeKeys = Object.keys(episodes);
episodeKeys.forEach(episodeKey=>{
const episodeJs = `${sourcePath}/${episodeKey}.js`;
if(entry === episodeJs) storyInfo = {storyKey, episodeKey};
});
});
return storyInfo;
};
// Tests to see if the current config is for the index.json.js file
const _getStoryInfoFromJsonConfig = (iVXConfig, config) => {
const {stories = {}, source = "./src"} = iVXConfig;
const storyKeys = Object.keys(stories);
const {entry} = config;
let storyInfo;
storyKeys.forEach(storyKey=>{
const sourcePath = `${source}/${storyKey}`;
const defaultEpisodeJs = `${sourcePath}/${defaultEpisodeKey}.json.js`;
if(defaultEpisodeJs === entry) storyInfo = {storyKey};
const {episodes = {}} = stories[storyKey];
const episodeKeys = Object.keys(episodes);
episodeKeys.forEach(episodeKey=>{
const episodeJs = `${sourcePath}/${episodeKey}.json.js`;
if(entry === episodeJs) storyInfo = {storyKey, episodeKey};
});
});
return storyInfo;
};
// Updates the JS Config with settings to support the dev server
const mapJsConfig = (config, storyInfo) => {
const {storyKey, episodeKey} = storyInfo;
let fileName = storyKey;
if(episodeKey) fileName = `${fileName}-${episodeKey}`;
const xapiCssFileExtractor = new ExtractTextPlugin(`${fileName}.css`);
const clonedConfig = Object.assign({}, config);
const { module: configModule = {}, plugins = [] } = clonedConfig;
const { rules = [] } = configModule;
const cleanedRules = rules.filter(rule => {
const { test } = rule;
const currentTest = new RegExp(test);
const lessTest = new RegExp(/\.less$/);
return currentTest.toString() !== lessTest.toString();
});
const updateModules = {
rules: [...cleanedRules,
{
test: /.html-templates/,
use: {
loader: 'file-loader',
options: {
name: (file) => {
return `${storyKey}/template/[name].html`;
},
context: ''
}
}
}, {
test: /\.less$/,
loader: xapiCssFileExtractor.extract({
fallback: 'style-loader',
use: ['css-loader', {
loader: 'less-loader',
options: {
relativeUrls: true
}
}]
})
}, {
/* For Windows... */
test: /.\\story\-config.json/,
use: [{
loader: 'file-loader',
options: {
name: (file) => {
return `${storyKey}\\index.html`;
},
context: ''
}
},{
loader: path.resolve(__dirname, 'loaders/story-config.js')
}]
},{
/* For *NIX... */
test: /.\/story\-config.json/,
use: [{
loader: 'file-loader',
options: {
name: (file) => {
return `${storyKey}/index.html`;
},
context: ''
}
},{
loader: path.resolve(__dirname, 'loaders/story-config.js')
}]
}]
}
const mappedConfig = Object.assign(clonedConfig, {
module: updateModules,
plugins: [...plugins,
xapiCssFileExtractor
],
output: {
filename: `${fileName}.js`,
path: path.resolve('/story/story') // THIS WORKS FOR SOME REASON
}
});
return mappedConfig;
}
// Updates the JSON config to use with the dev server
const mapJsonConfig = (config, storyInfo) => {
const {storyKey, episodeKey} = storyInfo;
let fileName = storyKey;
if(episodeKey) fileName = `${fileName}-${episodeKey}`;
const clonedConfig = Object.assign({}, config);
return Object.assign(clonedConfig, {
output: {
filename: `_json-evaluator.js`,
path: path.resolve(`/story`)
},
plugins: [new jsonPlugin({
buildFile: `${fileName}.json`,
buildPath: `story`,
serve: true
})]
});
}
// Depending on the config, this will add the changes to run the dev
// middleware server.
const _updateConfigs = (configs, iVXConfig) => {
const { stories, source = "./src" } = iVXConfig;
const storyKeys = Object.keys(stories);
return configs.map(config => {
const matchingJsConfig = _getStoryInfoFromJsConfig(iVXConfig, config);
const matchingJsonConfig = _getStoryInfoFromJsonConfig(iVXConfig, config);
if (matchingJsConfig) {
return mapJsConfig(config, matchingJsConfig);
}
if (matchingJsonConfig) {
return mapJsonConfig(config, matchingJsonConfig)
}
return config
});
}
/**
* Using the configs built by the main service for webpack, this will
* use the webpackDevMiddleware for express to create a local server as
* an instance of the xapi endpoint to support the preview with the ivx-story-player module.
*
* Returns a mini-api to open the window to preview and be able to reload the
* browser when an update to the src folder occurs.
*/
module.exports = (configs, iVXConfig) => {
const updatedConfigs = _updateConfigs(configs, iVXConfig);
app.set('views', './_cli/build-scripts/services/webpack/server/views/layouts');
app.engine('handlebars', exphbs({
defaultLayout: 'main',
layoutsDir: './_cli/build-scripts/services/webpack/server/views/layouts'
}));
app.set('view engine', 'handlebars');
app.use(webpackDevMiddleware(webpack(updatedConfigs), {
stats: {
chunks: true,
colors: true
}
}));
//Routes
homePage(app, iVXConfig, appConfig);
const spServer = iVXStoryPlayer(appConfig);
app.listen(3090, () => console.log(`The preview selector page can be found at http://localhost:${3090}` ));
return Object.assign({},
spServer,
{
openWindow: () => {
opn(appConfig.api.xapi);
}
}
);
}