summaryrefslogtreecommitdiff
path: root/animism-align
diff options
context:
space:
mode:
authorJules Laplace <julescarbon@gmail.com>2020-11-16 17:03:07 +0100
committerJules Laplace <julescarbon@gmail.com>2020-11-16 17:03:07 +0100
commit92881093ae19e4d76193447c187028aee5cbe4c7 (patch)
tree8e3b4baa4ea790b5e9b30e2fed6ed4f1b871cc5c /animism-align
parent77489cf313dd47122b9be80f6d49caf513a9e03c (diff)
getting the viewer-only version of the site working. flask command to run test server
Diffstat (limited to 'animism-align')
-rw-r--r--animism-align/cli/app/server/viewer.py69
-rw-r--r--animism-align/cli/app/settings/app_cfg.py3
-rwxr-xr-xanimism-align/cli/cli.py9
-rw-r--r--animism-align/cli/commands/site/export.py6
-rw-r--r--animism-align/frontend/app/store.js24
-rw-r--r--animism-align/frontend/app/views/align/align.reducer.js2
-rw-r--r--animism-align/frontend/app/views/audio/audio.actions.js6
-rw-r--r--animism-align/frontend/app/views/paragraph/components/paragraph.list.js2
-rw-r--r--animism-align/frontend/app/views/paragraph/paragraph.reducer.js2
-rw-r--r--animism-align/frontend/app/views/viewer/nav/nav.parent.js2
-rw-r--r--animism-align/frontend/app/views/viewer/player/player.container.js1
-rw-r--r--animism-align/frontend/app/views/viewer/player/player.transcript.js1
-rw-r--r--animism-align/frontend/app/views/viewer/viewer.actions.js3
-rw-r--r--animism-align/frontend/app/views/viewer/viewer.container.js1
-rw-r--r--animism-align/frontend/site/actions.js11
-rw-r--r--animism-align/frontend/site/app.js31
-rw-r--r--animism-align/frontend/site/index.js4
-rw-r--r--animism-align/frontend/site/site/site.reducer.js12
-rw-r--r--animism-align/frontend/site/store.js31
-rw-r--r--animism-align/static/site.html10
-rw-r--r--animism-align/webpack.config.site.js11
21 files changed, 188 insertions, 53 deletions
diff --git a/animism-align/cli/app/server/viewer.py b/animism-align/cli/app/server/viewer.py
new file mode 100644
index 0000000..3eecc4e
--- /dev/null
+++ b/animism-align/cli/app/server/viewer.py
@@ -0,0 +1,69 @@
+import os
+import logging
+import logging.handlers
+
+logger = logging.getLogger("")
+logger.setLevel(logging.DEBUG)
+handler = logging.handlers.RotatingFileHandler("flask.log",
+ maxBytes=3000000, backupCount=2)
+formatter = logging.Formatter(
+ '[%(asctime)s] {%(filename)s:%(lineno)d} %(levelname)s - %(message)s')
+handler.setFormatter(formatter)
+logger.addHandler(handler)
+logging.getLogger().addHandler(logging.StreamHandler())
+
+from flask import Flask, Blueprint, send_from_directory, request
+
+from app.settings import app_cfg
+
+def create_app(script_info=None):
+ """
+ functional pattern for creating the flask app
+ """
+ logging.debug("Starting site viewer...")
+ # print(app_cfg.SERVER_NAME)
+ app = Flask(__name__, static_folder=app_cfg.DIR_STATIC_SITE_VIEWER, static_url_path='/')
+ app.config['SERVER_NAME'] = app_cfg.SERVER_NAME
+ app.url_map.strict_slashes = False
+
+ # index_html = 'site.html'
+
+ # @app.route('/episode1', methods=['GET'])
+ # def serve_dir_directory_index():
+ # return send_from_directory(app_cfg.DIR_STATIC_SITE_VIEWER, 'episode1/index.html')
+
+ # @app.errorhandler(404)
+ # def page_not_found(e):
+ # filename = request.url.replace(app_cfg.SERVER_NAME, '')
+ # print(filename)
+ # # return app.send_static_file(index_html), 200
+
+ # @app.route('/<path:filename>', methods=['GET'])
+ # def serve_file_in_dir(filename):
+ # print(filename)
+ # if os.path.isfile(os.path.join(app_cfg.DIR_STATIC_SITE_VIEWER, filename)):
+ # return send_from_directory(app_cfg.DIR_STATIC_SITE_VIEWER, filename)
+ # if os.path.isfile(os.path.join(app_cfg.DIR_STATIC_SITE_VIEWER, filename, 'index.html')):
+ # filename = os.path.join(filename, 'index.html')
+ # return send_from_directory(app_cfg.DIR_STATIC_SITE_VIEWER, filename)
+ # return 404
+
+ # return app.send_static_file(index_html), 200
+ # path = os.path.join(os.path.dirname(__file__), 'site.html')
+ # with open(path, "r") as f:
+ # return f.read(), 200
+
+ # @app.route('/', methods=['GET'])
+ # def index():
+ # return app.send_static_file('site.html')
+
+ @app.route('/favicon.ico')
+ def favicon():
+ return send_from_directory(os.path.join(app.root_path, 'static/img/'),
+ 'favicon.ico',mimetype='image/vnd.microsoft.icon')
+
+ @app.shell_context_processor
+ def shell_context():
+ return { 'app': app, 'db': db }
+
+ return app
diff --git a/animism-align/cli/app/settings/app_cfg.py b/animism-align/cli/app/settings/app_cfg.py
index 963e265..de5cb6e 100644
--- a/animism-align/cli/app/settings/app_cfg.py
+++ b/animism-align/cli/app/settings/app_cfg.py
@@ -35,6 +35,7 @@ CLICK_GROUPS = {
'site': 'commands/site',
'db': '',
'flask': '',
+ 'viewer': '',
}
@@ -63,6 +64,8 @@ if 'cli' in os.getcwd():
else:
DIR_STATIC = os.path.abspath('static')
+DIR_STATIC_SITE_VIEWER = join(DIR_DATA_STORE, 'exports/animism')
+
HASH_TREE_DEPTH = 3 # for sha256 subdirs
HASH_BRANCH_SIZE = 3 # for sha256 subdirs
diff --git a/animism-align/cli/cli.py b/animism-align/cli/cli.py
index 2158398..0913cd1 100755
--- a/animism-align/cli/cli.py
+++ b/animism-align/cli/cli.py
@@ -15,7 +15,7 @@ if __name__ == '__main__':
# argparse: intercept group
argv_tmp = sys.argv
sys.argv = sys.argv[:2]
- ap = argparse.ArgumentParser('\033[1m\033[94mSwimmer\033[0m')
+ ap = argparse.ArgumentParser('\033[1m\033[94mAnimism\033[0m')
ap.add_argument('group', choices=app_cfg.CLICK_GROUPS.keys())
args = ap.parse_args()
sys.argv = argv_tmp
@@ -29,6 +29,13 @@ if __name__ == '__main__':
cli = FlaskGroup(create_app=create_app)
+ elif args.group == 'viewer':
+
+ from flask.cli import FlaskGroup
+ from app.server.viewer import create_app
+
+ cli = FlaskGroup(create_app=create_app)
+
elif args.group == 'db':
import re
diff --git a/animism-align/cli/commands/site/export.py b/animism-align/cli/commands/site/export.py
index f4cc23e..f9560c6 100644
--- a/animism-align/cli/commands/site/export.py
+++ b/animism-align/cli/commands/site/export.py
@@ -10,7 +10,7 @@ import os
@click.command('info')
# @click.option('-g', '--graph', 'opt_graph_path', required=True,
# help='Graph name')
-@click.option('-o', '--output', 'opt_output_dir', required=False,
+@click.option('-o', '--output', 'opt_output_dir', required=False, default="animism",
help='Output directory')
@click.pass_context
def cli(ctx, opt_output_dir):
@@ -20,6 +20,7 @@ def cli(ctx, opt_output_dir):
# imports
import datetime
+ import json
from distutils.dir_util import copy_tree
# ------------------------------------------------
@@ -45,7 +46,7 @@ def cli(ctx, opt_output_dir):
prune_db(db)
media_to_copy = rewrite_db_media(db, site_fp_media, media_url)
- db['urls'] = { 'audio': 'media/animism_episode_01.mp3' }
+ db['urls'] = { 'audio': '/' + page_name + '/media/animism_episode_01.mp3' }
media_to_copy['audio'] = {
'src': join(app_cfg.DIR_DATA_STORE, 'peaks/animism_episode_01_2810.mp3'),
'dst': join(site_fp_media, 'animism_episode_01.mp3'),
@@ -56,6 +57,7 @@ def cli(ctx, opt_output_dir):
index_html = load_text(join(app_cfg.DIR_STATIC, 'site.html'), split=False)
index_html = index_html.replace('SITE_PATH', page_url)
+ index_html = index_html.replace('SITE_JSON', json.dumps(db, separators=(',', ':')))
index_html = index_html.replace('PAGE_TITLE', page_title)
index_html = index_html.replace('PAGE_DESCRIPTION', page_desc)
index_html = index_html.replace('PLAIN_CONTENT', plain_content(db, site_title))
diff --git a/animism-align/frontend/app/store.js b/animism-align/frontend/app/store.js
index f7477df..d1843f7 100644
--- a/animism-align/frontend/app/store.js
+++ b/animism-align/frontend/app/store.js
@@ -5,31 +5,39 @@ import { createBrowserHistory } from 'history'
import thunk from 'redux-thunk'
// import { login } from 'app/utils'
+// cms
import uploadReducer from 'app/views/upload/upload.reducer'
-import alignReducer from 'app/views/align/align.reducer'
-import audioReducer from 'app/views/audio/audio.reducer'
-import paragraphReducer from 'app/views/paragraph/paragraph.reducer'
import annotationReducer from 'app/views/annotation/annotation.reducer'
import siteReducer from 'app/views/site/site.reducer'
import mediaReducer from 'app/views/media/media.reducer'
-import viewerReducer from 'app/views/viewer/viewer.reducer'
import episodeReducer from 'app/views/episode/episode.reducer'
import venueReducer from 'app/views/venue/venue.reducer'
+// editor
+import alignReducer from 'app/views/align/align.reducer'
+import paragraphReducer from 'app/views/paragraph/paragraph.reducer'
+
+// viewer
+import audioReducer from 'app/views/audio/audio.reducer'
+import viewerReducer from 'app/views/viewer/viewer.reducer'
+
const createRootReducer = history => (
combineReducers({
auth: (state = {}) => state,
router: connectRouter(history),
site: siteReducer,
+
upload: uploadReducer,
- align: alignReducer,
- audio: audioReducer,
- paragraph: paragraphReducer,
annotation: annotationReducer,
media: mediaReducer,
- viewer: viewerReducer,
episode: episodeReducer,
venue: venueReducer,
+
+ align: alignReducer,
+ paragraph: paragraphReducer,
+
+ audio: audioReducer,
+ viewer: viewerReducer,
})
)
diff --git a/animism-align/frontend/app/views/align/align.reducer.js b/animism-align/frontend/app/views/align/align.reducer.js
index dec27a4..8a660c0 100644
--- a/animism-align/frontend/app/views/align/align.reducer.js
+++ b/animism-align/frontend/app/views/align/align.reducer.js
@@ -1,5 +1,5 @@
import * as types from 'app/types'
-import { session, getDefault, getDefaultInt } from 'app/session'
+// import { session, getDefault, getDefaultInt } from 'app/session'
const initialState = {
timeline: {
diff --git a/animism-align/frontend/app/views/audio/audio.actions.js b/animism-align/frontend/app/views/audio/audio.actions.js
index b5d4e1b..ef5a4f6 100644
--- a/animism-align/frontend/app/views/audio/audio.actions.js
+++ b/animism-align/frontend/app/views/audio/audio.actions.js
@@ -34,12 +34,14 @@ export const pause = () => dispatch => {
audioPlayer.pause()
}
export const seek = play_ts => dispatch => {
+ console.log('seek to', play_ts)
audioPlayer.currentTime = play_ts
- dispatch({ type: types.audio.seek, seek_ts: audioPlayer.currentTime })
+ console.log(play_ts, audioPlayer.currentTime)
+ dispatch({ type: types.audio.seek, seek_ts: audioPlayer.currentTime || play_ts })
}
export const jump = delta_ts => dispatch => {
audioPlayer.currentTime += delta_ts
- dispatch({ type: types.audio.seek, seek_ts: audioPlayer.currentTime })
+ dispatch({ type: types.audio.seek, seek_ts: audioPlayer.currentTime || play_ts })
}
export const toggle = () => dispatch => {
if (store.getState().audio.playing) {
diff --git a/animism-align/frontend/app/views/paragraph/components/paragraph.list.js b/animism-align/frontend/app/views/paragraph/components/paragraph.list.js
index a8f32b3..1507b51 100644
--- a/animism-align/frontend/app/views/paragraph/components/paragraph.list.js
+++ b/animism-align/frontend/app/views/paragraph/components/paragraph.list.js
@@ -14,6 +14,7 @@ class ParagraphList extends Component {
componentDidUpdate(prevProps) {
if (this.props.audio.play_ts === prevProps.audio.play_ts) return
+ if (!this.props.paragraphs) return
this.setCurrentParagraph()
}
@@ -64,6 +65,7 @@ class ParagraphList extends Component {
onAnnotationClick, onParagraphDoubleClick,
} = this.props
const { currentParagraph, currentAnnotation } = this.state
+ if (!paragraphs) return null
return paragraphs.map((paragraph, i) => {
if (selectedParagraph && selectedParagraph.id === paragraph.id) {
paragraph = selectedParagraph
diff --git a/animism-align/frontend/app/views/paragraph/paragraph.reducer.js b/animism-align/frontend/app/views/paragraph/paragraph.reducer.js
index 041014f..3b33128 100644
--- a/animism-align/frontend/app/views/paragraph/paragraph.reducer.js
+++ b/animism-align/frontend/app/views/paragraph/paragraph.reducer.js
@@ -1,5 +1,5 @@
import * as types from 'app/types'
-import { session, getDefault, getDefaultInt } from 'app/session'
+// import { session, getDefault, getDefaultInt } from 'app/session'
import { crudState, crudReducer } from 'app/api/crud.reducer'
diff --git a/animism-align/frontend/app/views/viewer/nav/nav.parent.js b/animism-align/frontend/app/views/viewer/nav/nav.parent.js
index d18aac0..cb83f94 100644
--- a/animism-align/frontend/app/views/viewer/nav/nav.parent.js
+++ b/animism-align/frontend/app/views/viewer/nav/nav.parent.js
@@ -61,7 +61,7 @@ class NavParent extends Component {
e && e.preventDefault()
e && e.stopPropagation()
const { viewer } = this.props
- // console.log('>> SEEK')
+ console.log('>> SEEK', viewer.nextSection)
if (viewer.nextSection) {
actions.viewer.seekToSection(viewer.nextSection)
} else {
diff --git a/animism-align/frontend/app/views/viewer/player/player.container.js b/animism-align/frontend/app/views/viewer/player/player.container.js
index 26697f9..0bf64d1 100644
--- a/animism-align/frontend/app/views/viewer/player/player.container.js
+++ b/animism-align/frontend/app/views/viewer/player/player.container.js
@@ -83,6 +83,7 @@ class PlayerContainer extends Component {
// console.log('inCurrentSection?', inCurrentSection)
// console.log('didSeek?', didSeek)
// if the current TS isn't in the same section as the current one...
+ console.log(currentSection.start_ts, play_ts, currentSection.end_ts)
if (inCurrentSection) {
return
}
diff --git a/animism-align/frontend/app/views/viewer/player/player.transcript.js b/animism-align/frontend/app/views/viewer/player/player.transcript.js
index 15d376a..9796008 100644
--- a/animism-align/frontend/app/views/viewer/player/player.transcript.js
+++ b/animism-align/frontend/app/views/viewer/player/player.transcript.js
@@ -60,6 +60,7 @@ class PlayerTranscript extends Component {
const className = "player-transcript " + color + " " + (
inlineParagraphCount > 2 ? 'scrollable' : 'not-scrollable'
)
+ console.log(this.props.section)
return (
<div
className={className}
diff --git a/animism-align/frontend/app/views/viewer/viewer.actions.js b/animism-align/frontend/app/views/viewer/viewer.actions.js
index 82d3cf1..1372013 100644
--- a/animism-align/frontend/app/views/viewer/viewer.actions.js
+++ b/animism-align/frontend/app/views/viewer/viewer.actions.js
@@ -47,6 +47,8 @@ export const loadSections = () => dispatch => {
const { order: annotationOrder, lookup: annotationLookup } = state.annotation.index
const { lookup: mediaLookup } = state.media.index
+ console.log(state)
+
// loop over the annotations in time order.
annotationOrder.forEach((annotation_id, i) => {
// fetch the current annotation
@@ -206,7 +208,6 @@ export const loadSections = () => dispatch => {
})
// console.log(sections)
// console.log(footnoteList)
- // console.log(fullscreenTimeline)
dispatch({ type: types.viewer.load_sections, sections, footnoteList })
}
diff --git a/animism-align/frontend/app/views/viewer/viewer.container.js b/animism-align/frontend/app/views/viewer/viewer.container.js
index 90787db..c81b79e 100644
--- a/animism-align/frontend/app/views/viewer/viewer.container.js
+++ b/animism-align/frontend/app/views/viewer/viewer.container.js
@@ -35,6 +35,7 @@ import VitrineModal from './modals/modals.vitrine'
class ViewerContainer extends Component {
render() {
const { loaded, viewer, playing } = this.props
+ // console.log(loaded, playing, viewer)
if (!loaded) {
return <div className='viewer loading'><Loader /></div>
}
diff --git a/animism-align/frontend/site/actions.js b/animism-align/frontend/site/actions.js
index f11ce13..2642469 100644
--- a/animism-align/frontend/site/actions.js
+++ b/animism-align/frontend/site/actions.js
@@ -1,8 +1,10 @@
import { bindActionCreators } from 'redux'
// import { actions as crudActions } from './api'
-import * as audioActions from 'app/views/audio.actions'
-import * as viewerActions from 'app/views/viewer.actions'
+import * as siteActions from 'site/site/site.actions'
+import * as audioActions from 'app/views/audio/audio.actions'
+import * as viewerActions from 'app/views/viewer/viewer.actions'
+import * as transcriptActions from 'app/views/paragraph/transcript.actions'
import { store } from './store'
@@ -11,12 +13,13 @@ export default
// .map(a => [a, crudActions[a]])
// .concat(
[
- // ['site', siteActions],
+ ['site', siteActions],
['audio', audioActions],
['viewer', viewerActions],
+ ['transcript', transcriptActions]
] //)
.map(p => [p[0], bindActionCreators(p[1], store.dispatch)])
.concat([
// ['socket', socketActions],
])
- .reduce((a,b) => (a[b[0]] = b[1])&&a,{}) \ No newline at end of file
+ .reduce((a,b) => (a[b[0]] = b[1])&&a,{})
diff --git a/animism-align/frontend/site/app.js b/animism-align/frontend/site/app.js
index 4f8a6cf..8509954 100644
--- a/animism-align/frontend/site/app.js
+++ b/animism-align/frontend/site/app.js
@@ -3,31 +3,26 @@ import { ConnectedRouter } from 'connected-react-router'
import { Route } from 'react-router'
// import ViewerContainer from './viewer/viewer.container'
+import Viewer from 'app/views/viewer/viewer.container'
import actions from './actions'
export default class App extends Component {
componentDidMount() {
- const path_partz = window.location.pathname.split('/')
- const graph_name = path_partz[1]
- let path_name = null
- if (path_partz.length > 2) {
- path_name = path_partz[2]
- }
- // console.log('loading', graph_name, path_name)
- actions.site.loadSite(graph_name, path_name)
+ actions.site.loadProject()
}
render() {
- return (
- <ConnectedRouter history={this.props.history}>
- <div className='app'>
- <Route exact key='root' path='/' render={() => {
- // setTimeout(() => this.props.history.push('/'), 10)
- return null
- }} />
- </div>
- </ConnectedRouter>
- )
+ return <Viewer />
+ // return (
+ // <ConnectedRouter history={this.props.history}>
+ // <div className='app'>
+ // <Route exact key='root' path='/' render={() => {
+ // // setTimeout(() => this.props.history.push('/'), 10)
+ // return null
+ // }} />
+ // </div>
+ // </ConnectedRouter>
+ // )
}
}
/*
diff --git a/animism-align/frontend/site/index.js b/animism-align/frontend/site/index.js
index 448d38d..7d47bd0 100644
--- a/animism-align/frontend/site/index.js
+++ b/animism-align/frontend/site/index.js
@@ -7,7 +7,7 @@ import '../app/common/form.css'
import { store } from './store'
-import Viewer from 'app/views/viewer/viewer.container'
+import App from './app'
const container = document.createElement('div')
container.classList.add('container')
@@ -15,6 +15,6 @@ document.body.appendChild(container)
ReactDOM.render(
<Provider store={store}>
- <Viewer />
+ <App />
</Provider>, container
)
diff --git a/animism-align/frontend/site/site/site.reducer.js b/animism-align/frontend/site/site/site.reducer.js
new file mode 100644
index 0000000..ea10886
--- /dev/null
+++ b/animism-align/frontend/site/site/site.reducer.js
@@ -0,0 +1,12 @@
+// import * as types from 'app/types'
+
+const initialState = {
+}
+
+export default function siteReducer(state = initialState, action) {
+ console.log(action.type, action)
+ switch (action.type) {
+ default:
+ return state
+ }
+}
diff --git a/animism-align/frontend/site/store.js b/animism-align/frontend/site/store.js
index 0b4aff7..2d9ffbc 100644
--- a/animism-align/frontend/site/store.js
+++ b/animism-align/frontend/site/store.js
@@ -1,25 +1,46 @@
-import { crudReducer } from 'app/api/crud.reducer'
+import { crudState, crudReducer } from 'app/api/crud.reducer'
import { applyMiddleware, compose, combineReducers, createStore } from 'redux'
import { connectRouter, routerMiddleware } from 'connected-react-router'
import { createBrowserHistory } from 'history'
import thunk from 'redux-thunk'
-// import siteReducer from './site/site.reducer'
+import audioReducer from 'app/views/audio/audio.reducer'
+import alignReducer from 'app/views/align/align.reducer'
+import viewerReducer from 'app/views/viewer/viewer.reducer'
+import paragraphReducer from 'app/views/paragraph/paragraph.reducer'
+import siteReducer from 'site/site/site.reducer'
+
+// document.querySelector("#plain_content").remove()
+const siteData = JSON.parse(document.querySelector("#site_data").innerText)
const createRootReducer = history => (
combineReducers([
"media",
- "upload",
"annotation",
"paragraph",
"episode",
"venue",
].reduce((a, type) => {
- a[type] = crudReducer(type)
+ a[type] = (() => {
+ const initialState = {
+ ...crudState(),
+ index: siteData[type],
+ }
+ if (type === 'paragraph') {
+ return (state=initialState, action) => paragraphReducer(state, action)
+ } else {
+ const typeReducer = crudReducer(type)
+ return (state=initialState, action) => typeReducer(state, action)
+ }
+ })()
return a
}, {
- auth: (state = {}) => state,
+ // auth: (state = {}) => state,
router: connectRouter(history),
+ site: siteReducer,
+ audio: audioReducer,
+ align: alignReducer,
+ viewer: viewerReducer,
}))
)
diff --git a/animism-align/static/site.html b/animism-align/static/site.html
index fd7eb84..6cc5a02 100644
--- a/animism-align/static/site.html
+++ b/animism-align/static/site.html
@@ -5,16 +5,14 @@
<title>PAGE_TITLE</title>
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<meta name="description" content="PAGE_DESCRIPTION" />
- <link rel="stylesheet" type="text/css" href="SITE_PATH/site.css" />
<style>
-/*
- html { background: #000; }
- .plain_content { display: none; }
-*/
+ html { background: #fff; }
+ #plain_content { display: none; }
</style>
</head>
<body>
-<div class="plain_content">PLAIN_CONTENT</div>
+<div id="plain_content">PLAIN_CONTENT</div>
+<script type="text/json" id="site_data">SITE_JSON</script>
<script>
var s = document.createElement('script');
s.setAttribute('src', 'BUNDLE_PATH?' + (Date.now() / 3600))
diff --git a/animism-align/webpack.config.site.js b/animism-align/webpack.config.site.js
index 7b76b93..bce064f 100644
--- a/animism-align/webpack.config.site.js
+++ b/animism-align/webpack.config.site.js
@@ -61,7 +61,16 @@ module.exports = {
presets: ['@babel/preset-react'],
plugins: [
"@babel/plugin-transform-runtime",
- ],
+ [ "@babel/plugin-proposal-class-properties", { "loose": true } ],
+ "transform-async-to-generator",
+ ["module-resolver", {
+ "root": ["./frontend"],
+ "alias": {
+ "app/store": "./frontend/site/store",
+ "app/actions": "./frontend/site/actions",
+ }
+ }]
+ ]
}
}
]