UNPKG

5.42 kBJavaScriptView Raw
1'use strict';
2
3/**
4this module provides functions for reading and writing local files,
5as well as functions for preparing data that is about to be written
6*/
7
8// Node.js built-ins
9
10const path = require('path');
11
12// foreign modules
13
14const pify = require('pify');
15const globp = pify(require('glob'));
16const loadJson = require('load-json-file');
17const readJsonFiles = require('@blinkmobile/json-as-files').readData;
18const rimrafp = pify(require('rimraf'));
19const writeJson = require('write-json-file');
20const writeJsonFiles = require('@blinkmobile/json-as-files').writeData;
21
22// local modules
23
24const asyncp = require('./utils/asyncp');
25
26// this module
27
28function defaultResource (base) {
29 return {
30 config: {
31 all: {},
32 default: {
33 footer: { $file: `${base}.footer.html` },
34 header: { $file: `${base}.header.html` },
35 styleSheet: { $file: `${base}.styleSheet.css` }
36 }
37 }
38 };
39}
40
41function defaultAnswerSpace (base) {
42 const obj = defaultResource(base);
43 Object.assign(obj.config.all, {
44 basicModeDisabledMessage: { $file: `${base}.basicModeDisabledMessage.html` }
45 });
46 Object.assign(obj.config.default, {
47 htmlHead: { $file: `${base}.htmlHead.html` },
48 introMessage: { $file: `${base}.introMessage.html` }
49 });
50 return obj;
51}
52
53function defaultInteraction (base) {
54 const obj = defaultResource(base);
55 Object.assign(obj.config.default, {
56 inputPrompt: { $file: `${base}.inputPrompt.html` },
57 madl: { $file: `${base}.madl.php` },
58 message: { $file: `${base}.message.txt` },
59 xsl: { $file: `${base}.xsl.xml` }
60 });
61 return obj;
62}
63
64function deleteInteraction (options) {
65 return rimrafp(path.join(options.cwd, 'interactions', options.name));
66}
67
68function fixResource (data) {
69 delete data.created_time;
70 delete data.links;
71 delete data.modified_time;
72 return data;
73}
74
75function fixAnswerSpace (data) {
76 fixResource(data);
77 delete data.sitemap;
78 return data;
79}
80
81function fixInteraction (data) {
82 fixResource(data);
83 if (typeof data.order !== 'number') {
84 delete data.order;
85 }
86 return data;
87}
88
89/** mutates defaults, does not mutate existing */
90function mergePlaceholders (existing, defaults) {
91 [
92 'all', 'default'
93 ].forEach((section) => {
94 const config = existing.config || {};
95 config[section] = config[section] || {};
96 Object.keys(config[section]).forEach((key) => {
97 const value = config[section][key];
98 if (value && typeof value === 'object' && value.$file) {
99 defaults.config[section][key] = value;
100 }
101 });
102 });
103 return defaults;
104}
105
106function listInteractions (options) {
107 const cwd = options.cwd;
108 return globp('*/*.json', {
109 cwd: path.join(cwd, 'interactions')
110 })
111 .then((files) => {
112 return files
113 // keep just the results that match NAME/NAME.json pattern
114 .filter((x) => /^([^\\\s]+)\/\1\.json$/.test(x))
115 // then just give us the names
116 .map((x) => x.split('/')[0]);
117 })
118 .then((names) => asyncp.mapSeries(names, asyncp.asyncify((name) => {
119 return readInteraction({ cwd, name })
120 .then((data) => ({ id: data.id, name: data.name }));
121 })));
122}
123
124function newInteraction (options) {
125 const name = options.name;
126 const data = {
127 config: {
128 all: {},
129 default: {
130 display: 'hide',
131 displayName: name
132 }
133 },
134 name
135 };
136 switch (options.type) {
137 case 'madl':
138 data.config.all.type = 'madl code';
139 data.config.default.madl = '?><?php\n';
140 break;
141
142 case 'message':
143 data.config.all.type = options.type;
144 data.config.default.message = '\n';
145 break;
146 }
147 return data;
148}
149
150function readAnswerSpace (options) {
151 return readJsonFiles({ filePath: path.join(options.cwd, 'answerSpace.json') })
152 .then((data) => fixAnswerSpace(data));
153}
154
155function readInteraction (options) {
156 const filePath = path.join(options.cwd, 'interactions', options.name, `${options.name}.json`);
157 return readJsonFiles({ filePath })
158 .then((data) => fixInteraction(data));
159}
160
161function writeAnswerSpace (options) {
162 const filePath = path.join(options.cwd, 'answerSpace.json');
163 const defaultPlaceholders = defaultAnswerSpace('answerSpace');
164
165 return writeMergedPlaceholders(filePath, defaultPlaceholders)
166 .then(() => {
167 const data = Object.assign({}, options.data);
168 fixAnswerSpace(data);
169 // persist the real data, honoring the $file placeholders
170 return writeJsonFiles({ data, filePath });
171 });
172}
173
174function writeInteraction (options) {
175 const cwd = options.cwd;
176 const name = options.name;
177 const filePath = path.join(cwd, 'interactions', name, `${name}.json`);
178 const defaultPlaceholders = defaultInteraction(name);
179
180 return writeMergedPlaceholders(filePath, defaultPlaceholders)
181 .then(() => {
182 const data = Object.assign({}, options.data);
183 fixInteraction(data);
184 // persist the real data, honoring the $file placeholders
185 return writeJsonFiles({ data, filePath });
186 });
187}
188
189function writeMergedPlaceholders (filePath, defaults) {
190 return loadJson(filePath)
191 .catch(() => ({})) // default to empty Object
192 .then((existing) => mergePlaceholders(existing, defaults))
193 .then((merged) => writeJson(filePath, merged));
194}
195
196module.exports = {
197 deleteInteraction,
198 fixAnswerSpace,
199 fixInteraction,
200 listInteractions,
201 newInteraction,
202 readAnswerSpace,
203 readInteraction,
204 writeAnswerSpace,
205 writeInteraction
206};