Skip to content

Commit 8fa5ffb

Browse files
committed
Introduce no-rootDirectory-yet case (create)
This is the first onboarding case when desktop app will be used to init writ. For now, add a new mode in the cli (create) that starts the CMS without an initial state. Speaking of which, now the cli delegates the watch mode's work to CMS. Reasoning: - If 'writ build' is entered, then it's ok to just run writ once - If 'writ watch' is entered, it means we are entering the live preview + cms mode. So let the CMS handle the rest - Because the CMS will anyway have to handle everything when writ is started via desktop app Speaking of which, now CMS server is not started within the routines of ssg. CMS initialization logic is moved up to the clients (cli, desktop (tbd)). This made the interior of the ssg immediately simpler. No need for the routines-wrapping index. It's all index.js now. Injecting cms.setState to watcher's onChange has also become simpler. Now, since the CMS will be responsible to run build and watch, new api endpoints, server routes and frontend modules are added. The prototype CMS UI now has these new buttons: - ssg.build() - ssg.watch() - get ssg options SSG options is what's passed as initial state to the CMS from the CLI (or other client). Currently, if no initial state is passed, meaning no rootDirectory to run writ in, none of the new buttons (or any others) work. But this is by design, for now. This case is going to evolve into onboarding flow, where the user inputs the ssg options and more and then watching and building will be triggered by the cms While at it: - Make the explore-panel (container of buttons) draggable - Inject settings to watcher
1 parent 8f4d28c commit 8fa5ffb

File tree

18 files changed

+344
-182
lines changed

18 files changed

+344
-182
lines changed

src/cli/bin

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#!/usr/bin/env node
2-
const writ = require('../')
2+
const ssg = require('../')
3+
const createCMS = require('../cms')
34
const manual = require('./manual')
45
const Flag = require('./flag')
56

@@ -12,7 +13,7 @@ if (!cli.length) {
1213

1314
let [ mode, ...opts ] = cli
1415

15-
const modes = ['start', 'build']
16+
const modes = ['start', 'build', 'create']
1617
const flags = {
1718
refreshTheme: new Flag('refresh-theme', 'r'),
1819
debug: new Flag('debug', 'd')
@@ -34,4 +35,23 @@ if (options.debug) {
3435
console.log('cli', { mode, opts, options })
3536
}
3637

37-
writ[mode](options)
38+
if (mode === 'start') {
39+
const cms = createCMS({
40+
ssgOptions: {
41+
rootDirectory: options.rootDirectory,
42+
refreshTheme: options.refreshTheme,
43+
debug: options.debug,
44+
cli: true
45+
}
46+
})
47+
cms.server.start({
48+
silent: false
49+
})
50+
} else if (mode === 'create') {
51+
const cms = createCMS()
52+
cms.server.start({
53+
silent: false
54+
})
55+
} else {
56+
ssg.build(options)
57+
}

src/cli/manual.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Commands
1313
--------
1414
build: Run once to produce build output
1515
start: Start livereloading preview and rerun on changes
16+
create: Create from scratch
1617
1718
1819
Options
@@ -54,4 +55,7 @@ $ writ start ../the-other-directory --debug
5455
5556
Refresh theme directory and start development
5657
$ writ start --refresh-theme
58+
59+
Create a project from scratch
60+
$ writ create
5761
`

src/cms/api/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ const createAPI = (providers) => {
1313
homepage: require('./models/homepage')(providers),
1414
tags: require('./models/tags')(providers),
1515
tag: require('./models/tag')(providers),
16+
ssg: require('./models/ssg')(providers),
17+
ssgOptions: require('./models/ssgOptions')(providers),
1618
}
1719
}
1820

src/cms/api/models/ssg.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
const ssg = require('../../../')
2+
3+
const createSSGModel = (state) => {
4+
return {
5+
async build({ rootDirectory, refreshTheme, debug, cli }) {
6+
return state.setState(
7+
await ssg.build({
8+
rootDirectory,
9+
refreshTheme,
10+
debug,
11+
cli
12+
})
13+
)
14+
},
15+
16+
async watch({ rootDirectory, refreshTheme, debug, cli }) {
17+
return state.setState(
18+
await ssg.watch({
19+
rootDirectory,
20+
refreshTheme,
21+
debug,
22+
cli,
23+
onChange: state.setState
24+
})
25+
)
26+
}
27+
}
28+
}
29+
30+
module.exports = createSSGModel

src/cms/api/models/ssgOptions.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
const createSSGOPtionsModel = ({ getSSGOptions }) => {
2+
return {
3+
get: getSSGOptions
4+
}
5+
}
6+
7+
module.exports = createSSGOPtionsModel

src/cms/index.js

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,28 @@
11
const createAPI = require('./api')
22
const createServer = require('./server')
33

4-
const createCMS = () => {
5-
const state = {
6-
settings: {},
7-
fileSystemTree: [],
8-
contentModel: {}
9-
}
4+
const createCMS = (initialState = {}) => {
5+
const store = (() => {
6+
const state = Object.assign({
7+
ssgOptions: {},
8+
settings: {},
9+
fileSystemTree: [],
10+
contentModel: {}
11+
}, initialState)
1012

11-
const api = createAPI({
12-
getSettings: () => state.settings,
13-
getFileSystemTree: () => state.fileSystemTree,
14-
getContentModel: () => state.contentModel
15-
})
13+
return {
14+
getSettings: () => state.settings,
15+
getFileSystemTree: () => state.fileSystemTree,
16+
getContentModel: () => state.contentModel,
17+
getSSGOptions: () => state.ssgOptions,
18+
19+
setState: (newState) => {
20+
Object.assign(state, newState)
21+
}
22+
}
23+
})()
24+
25+
const api = createAPI(store)
1626

1727
const server = createServer({
1828
api
@@ -21,11 +31,7 @@ const createCMS = () => {
2131
return {
2232
api,
2333
server,
24-
setState(newState) {
25-
state.settings = newState.settings
26-
state.fileSystemTree = newState.fileSystemTree
27-
state.contentModel = newState.contentModel
28-
}
34+
...store
2935
}
3036
}
3137

src/cms/server/public/api.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,43 @@ const makeQueryString = (params) => {
99
}
1010

1111
const api = {
12+
ssg: {
13+
build: async (options) => {
14+
const response = await fetch('/api/ssg/build', {
15+
method: 'post',
16+
headers: {
17+
'content-type': 'application/json'
18+
},
19+
body: JSON.stringify(options)
20+
})
21+
return response.text()
22+
},
23+
24+
watch: async (options) => {
25+
const response = await fetch('/api/ssg/watch', {
26+
method: 'post',
27+
headers: {
28+
'content-type': 'application/json'
29+
},
30+
body: JSON.stringify(options)
31+
})
32+
return response.text()
33+
}
34+
35+
},
36+
37+
ssgOptions: {
38+
get: async () => {
39+
const response = await fetch('/api/ssgOptions', {
40+
method: 'get',
41+
headers: {
42+
'content-type': 'application/json'
43+
}
44+
})
45+
return response.json()
46+
}
47+
},
48+
1249
category: {
1350
get: async (options) => {
1451
const { name } = options

src/cms/server/public/app.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import ssgBuild from './app/ssgBuild.js'
2+
import ssgWatch from './app/ssgWatch.js'
3+
import getSSGOptions from './app/getSSGOptions.js'
14
import getSettings from './app/getSettings.js'
25
import updateSettings from './app/updateSettings.js'
36
import getFileSystemTree from './app/getFileSystemTree.js'
@@ -20,6 +23,15 @@ import getTag from './app/getTag.js'
2023
const query = document.querySelector.bind(document)
2124

2225
const makeButtonsWork = () => {
26+
query('#ssg-build-btn').addEventListener('click', async () => {
27+
await ssgBuild()
28+
setIframeSrc()
29+
})
30+
query('#ssg-watch-btn').addEventListener('click', async () => {
31+
await ssgWatch()
32+
setIframeSrc()
33+
})
34+
query('#get-ssg-options-btn').addEventListener('click', getSSGOptions)
2335
query('#get-settings-btn').addEventListener('click', getSettings)
2436
query('#update-settings-btn').addEventListener('click', updateSettings)
2537
query('#get-file-system-tree-btn').addEventListener('click', getFileSystemTree)
@@ -45,7 +57,31 @@ const setIframeSrc = () => {
4557
query('#preview').src = `http://${hostname}:3000`
4658
}
4759

60+
const makeDraggable = (element) => {
61+
let offsetX = 0
62+
let offsetY = 0
63+
64+
element.addEventListener('mousedown', (e) => {
65+
offsetX = e.clientX - element.offsetLeft
66+
offsetY = e.clientY - element.offsetTop
67+
68+
const move = (e) => {
69+
element.style.left = (e.clientX - offsetX) + 'px'
70+
element.style.top = (e.clientY - offsetY) + 'px'
71+
}
72+
73+
const stop = () => {
74+
document.removeEventListener('mousemove', move)
75+
document.removeEventListener('mouseup', stop)
76+
}
77+
78+
document.addEventListener('mousemove', move)
79+
document.addEventListener('mouseup', stop)
80+
})
81+
}
82+
4883
window.addEventListener('DOMContentLoaded', () => {
4984
setIframeSrc()
5085
makeButtonsWork()
86+
makeDraggable(document.querySelector('.explore-panel'))
5187
})
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import api from '../api.js'
2+
import dialog from './components/dialog.js'
3+
4+
export default async () => {
5+
dialog.textContent('Loading').show()
6+
7+
const ssgOptions = await api.ssgOptions.get()
8+
9+
dialog.textContent(JSON.stringify(ssgOptions, null, 2))
10+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import api from '../api.js'
2+
3+
const ssgBuild = async () => {
4+
const ssgOptions = await api.ssgOptions.get()
5+
return api.ssg.build(ssgOptions)
6+
}
7+
8+
export default ssgBuild

0 commit comments

Comments
 (0)