kni 4.0.3 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/kni.js CHANGED
@@ -1,316 +1,323 @@
1
1
  #!/usr/bin/env node
2
- 'use strict';
3
-
4
- var fs = require('fs');
5
- var tee = require('tee');
6
- var Console = require('./console');
7
- var Readline = require('./readline');
8
- var Engine = require('./engine');
9
- var Scanner = require('./scanner');
10
- var OutlineLexer = require('./outline-lexer');
11
- var InlineLexer = require('./inline-lexer');
12
- var Parser = require('./parser');
13
- var Story = require('./story');
14
- var Path = require('./path');
15
- var grammar = require('./grammar');
16
- var link = require('./link');
17
- var verify = require('./verify');
18
- var exec = require('shon/exec');
19
- var usage = require('./kni.json');
20
- var xorshift = require('xorshift');
21
- var table = require('table').default;
22
- var getBorderCharacters = require('table').getBorderCharacters;
23
- var describe = require('./describe');
24
- var makeHtml = require('./html');
25
-
26
- function main() {
27
- var config = exec(usage);
28
-
29
- if (!config) {
30
- return;
2
+
3
+ import {pathToFileURL} from 'url';
4
+ import tee from 'tee';
5
+ import Console from './console.js';
6
+ import Readline from './readline.js';
7
+ import Engine from './engine.js';
8
+ import Scanner from './scanner.js';
9
+ import OutlineLexer from './outline-lexer.js';
10
+ import InlineLexer from './inline-lexer.js';
11
+ import Parser from './parser.js';
12
+ import Story from './story.js';
13
+ import * as Path from './path.js';
14
+ import start from './grammar.js';
15
+ import link from './link.js';
16
+ import verify from './verify.js';
17
+ import exec from 'shon/exec.js';
18
+ import usage from './kni.json' with {type: 'json'};
19
+ import xorshift from 'xorshift';
20
+ import table from 'table';
21
+ import describe from './describe.js';
22
+ import makeHtml from './html.js';
23
+
24
+ const {default: tableDefault, getBorderCharacters} = table;
25
+
26
+ const run = (args, out, done) => {
27
+ const config = exec(usage, args);
28
+ if (!config) {
29
+ done(null);
30
+ return;
31
+ }
32
+
33
+ serial(config.scripts, readAndKeep, (err, kniscripts) => {
34
+ if (err) {
35
+ done(err);
36
+ return;
31
37
  }
32
38
 
33
- serial(config.scripts, readAndKeep, onKniscripts);
39
+ let interactive = true;
34
40
 
35
- function onKniscripts(err, kniscripts) {
36
- if (err) {
37
- console.error(err.message);
38
- process.exit(-1);
39
- return;
40
- }
41
+ if (config.transcript === out) {
42
+ config.transcript = null;
43
+ }
44
+ if (config.transcript) {
45
+ out = tee(config.transcript, out);
46
+ }
41
47
 
42
- var interactive = true;
48
+ let states;
49
+ if (config.fromJson) {
50
+ states = JSON.parse(kniscripts[0].content); // TODO test needed
51
+ } else {
52
+ const story = new Story();
43
53
 
44
- var out = process.stdout;
45
- if (config.transcript === process.stdout) {
46
- config.transcript = null;
47
- }
48
- if (config.transcript) {
49
- out = tee(config.transcript, out);
50
- }
54
+ for (let i = 0; i < kniscripts.length; i++) {
55
+ const kniscript = kniscripts[i].content;
51
56
 
52
- var states;
53
- if (config.fromJson) {
54
- states = JSON.parse(knicript);
55
-
56
- } else {
57
- var story = new Story();
58
-
59
- for (var i = 0; i < kniscripts.length; i++) {
60
- var kniscript = kniscripts[i].content;
61
-
62
- if (config.debugInput) {
63
- console.log(kniscript);
64
- }
65
-
66
- var path = Path.start();
67
- var base = [];
68
- if (kniscripts.length > 1) {
69
- path = kniscripts[i].stream.path;
70
- path = [path.split('/').pop().split('.').shift()];
71
- base = path;
72
- }
73
-
74
- var p = new Parser(grammar.start(story, path, base));
75
- var il = new InlineLexer(p);
76
- var ol = new OutlineLexer(il);
77
- var s = new Scanner(ol, kniscripts[i].stream.path);
78
-
79
- // Kick off each file with a fresh paragraph.
80
- p.next('token', '', '//', s);
81
-
82
- if (config.debugParser) {
83
- p.debug = true;
84
- interactive = false;
85
- }
86
- if (config.debugInlineLexer) {
87
- il.debug = true;
88
- interactive = false;
89
- }
90
- if (config.debugOutlineLexer) {
91
- ol.debug = true;
92
- interactive = false;
93
- }
94
- if (config.debugScanner) {
95
- s.debug = true;
96
- interactive = false;
97
- }
98
-
99
- s.next(kniscript);
100
- s.return();
101
- }
102
-
103
- link(story);
104
-
105
- states = story.states;
106
- if (story.errors.length) {
107
- dump(story.errors, process.stderr);
108
- if (config.transcript != null) {
109
- dump(story.errors, config.transcript);
110
- }
111
- process.exitCode = -1;
112
- return;
113
- }
57
+ if (config.debugInput) {
58
+ console.log(kniscript);
114
59
  }
115
60
 
116
- if (config.describe) {
117
- describeStory(states);
118
- interactive = false;
119
-
120
- } else if (config.toJson) {
121
- console.log(JSON.stringify(states, null, 4));
122
- interactive = false;
123
-
124
- } else if (config.toHtml) {
125
- makeHtml(states, config.toHtml, {
126
- title: config.htmlTitle,
127
- color: config.htmlColor,
128
- backgroundColor: config.htmlBackgroundColor,
129
- });
130
- interactive = false;
61
+ let path = Path.start();
62
+ let base = [];
63
+ if (kniscripts.length > 1) {
64
+ path = kniscripts[i].stream.path;
65
+ path = [path.split('/').pop().split('.').shift()];
66
+ base = path;
131
67
  }
132
68
 
133
- var randomer = xorshift;
69
+ const p = new Parser(start(story, path, base));
70
+ const il = new InlineLexer(p);
71
+ const ol = new OutlineLexer(il);
72
+ const s = new Scanner(ol, kniscripts[i].stream.path);
134
73
 
135
- if (config.transcript || config.seed) {
136
- // I rolled 4d64k this morning.
137
- randomer = new xorshift.constructor([
138
- 37615 ^ config.seed,
139
- 54552 ^ config.seed,
140
- 59156 ^ config.seed,
141
- 24695 ^ config.seed
142
- ]);
143
- }
74
+ // Kick off each file with a fresh paragraph.
75
+ p.next('token', '', '//', s);
144
76
 
145
- if (config.expected) {
146
- read(config.expected, function onTypescript(err, typescript) {
147
- if (err) {
148
- console.error(err.message);
149
- process.exitCode = -1;
150
- return;
151
- }
152
- test(kniscript, typescript);
153
- });
154
- return;
77
+ if (config.debugParser) {
78
+ p.debug = true;
79
+ interactive = false;
155
80
  }
156
-
157
- if (interactive) {
158
- var readline = new Readline(config.transcript);
159
- var render = new Console(out);
160
- var engine = new Engine({
161
- story: states,
162
- start: config.start,
163
- render: render,
164
- dialog: readline,
165
- randomer: randomer
166
- });
167
-
168
- if (config.debugRuntime) {
169
- engine.debug = true;
170
- }
171
-
172
- if (config.waypoint) {
173
- read(config.waypoint, function onWaypoint(err, waypoint) {
174
- if (err) {
175
- console.error(err.message);
176
- process.exitCode = -1;
177
- return;
178
- }
179
- waypoint = JSON.parse(waypoint);
180
- engine.continue(waypoint);
181
- });
182
- } else {
183
- engine.continue();
184
- }
81
+ if (config.debugInlineLexer) {
82
+ il.debug = true;
83
+ interactive = false;
84
+ }
85
+ if (config.debugOutlineLexer) {
86
+ ol.debug = true;
87
+ interactive = false;
88
+ }
89
+ if (config.debugScanner) {
90
+ s.debug = true;
91
+ interactive = false;
185
92
  }
186
- }
187
93
 
188
- }
94
+ s.next(kniscript);
95
+ s.return();
96
+ }
189
97
 
190
- function test(kniscript, typescript) {
191
- var result = verify(kniscript, typescript);
192
- if (!result.pass) {
193
- console.log(result.actual);
194
- process.exit(1);
195
- }
196
- }
98
+ link(story);
197
99
 
198
- function describeStory(states) {
199
- var keys = Object.keys(states);
200
- var cells = [['L:C', 'AT', 'DO', 'S', 'USING', 'S', 'GO']];
201
- for (var i = 0; i < keys.length; i++) {
202
- var key = keys[i];
203
- var node = states[key];
204
- var next;
205
- if (i === keys.length - 1) {
206
- next = null;
207
- } else {
208
- next = keys[i + 1];
100
+ states = story.states;
101
+ if (story.errors.length) {
102
+ if (config.transcript != null) {
103
+ dump(story.errors, config.transcript);
209
104
  }
210
- cells.push([
211
- stripe(i, node.position),
212
- stripe(i, key),
213
- stripe(i, node.mode || node.type),
214
- stripe(i, node.lift ? '-' : ' '),
215
- stripe(i, describe(node)),
216
- stripe(i, node.drop ? '-' : ' '),
217
- stripe(i, describeNext(node.next, next))
218
- ]);
105
+ const storyError = new Error('internal story error');
106
+ storyError.story = story;
107
+ done(storyError);
108
+ return;
109
+ }
219
110
  }
220
- console.log(table(cells, {
221
- border: getBorderCharacters('void'),
222
- columnDefault: {
223
- paddingLeft: 0,
224
- paddingRight: 2
225
- },
226
- columns: {
227
- 4: {
228
- width: 40,
229
- wrapWord: true
230
- }
231
- },
232
- drawHorizontalLine: no
233
- }));
234
- }
235
111
 
236
- function stripe(index, text) {
237
- if (index % 2 === 1) {
238
- return text;
239
- } else {
240
- return '\x1b[90m' + text + '\x1b[0m';
112
+ if (config.describe) {
113
+ describeStory(states, out, done);
114
+ return;
241
115
  }
242
- }
243
116
 
244
- function describeNext(jump, next) {
245
- if (jump === undefined) {
246
- return '';
247
- } else if (jump === next) {
248
- return '';
249
- } else if (jump == 'RET') {
250
- return '<-';
251
- } else if (jump == 'ESC') {
252
- return '<<';
253
- } else {
254
- return '-> ' + jump;
117
+ if (config.toJson) {
118
+ console.log(JSON.stringify(states, null, 4), done);
119
+ interactive = false;
120
+ } else if (config.toHtml) {
121
+ makeHtml(states, config.toHtml, {
122
+ title: config.htmlTitle,
123
+ color: config.htmlColor,
124
+ backgroundColor: config.htmlBackgroundColor,
125
+ });
126
+ interactive = false;
255
127
  }
256
- }
257
-
258
- function no() {
259
- return false;
260
- }
261
128
 
262
- function readAndKeep(stream, callback) {
263
- read(stream, function onRead(err, content) {
264
- if (err != null) {
265
- return callback(err);
266
- }
267
- callback(null, {stream: stream, content: content});
268
- });
269
- }
129
+ let randomer = xorshift;
270
130
 
271
- function read(stream, callback) {
272
- stream.setEncoding('utf8');
273
- var string = '';
274
- stream.on('data', onData);
275
- stream.on('end', onEnd);
276
- stream.on('error', onEnd);
277
- function onData(chunk) {
278
- string += chunk;
131
+ if (config.transcript || config.seed) {
132
+ // I rolled 4d64k this morning.
133
+ randomer = new xorshift.constructor([
134
+ 37615 ^ config.seed,
135
+ 54552 ^ config.seed,
136
+ 59156 ^ config.seed,
137
+ 24695 ^ config.seed,
138
+ ]);
279
139
  }
280
- function onEnd(err) {
140
+
141
+ if (config.expected) {
142
+ read(config.expected, (err, typescript) => {
281
143
  if (err) {
282
- callback(err);
283
- return;
144
+ done(err);
145
+ return;
284
146
  }
285
- callback(null, string);
286
- }
287
- }
288
-
289
- function serial(array, eachback, callback) {
290
- var values = [];
291
- next(0);
292
147
 
293
- function next(i) {
294
- if (i >= array.length) {
295
- return callback(null, values);
148
+ const result = verify(kniscripts[0].content, typescript);
149
+ if (!result.pass) {
150
+ console.log(result.actual);
151
+ done(new Error('verification failed'));
152
+ return;
296
153
  }
154
+ });
155
+ done(null);
156
+ return;
157
+ }
297
158
 
298
- eachback(array[i], onEach);
299
-
300
- function onEach(err, value) {
301
- if (err != null) {
302
- return callback(err, null);
303
- }
304
- values.push(value);
305
- next(i+1);
306
- }
159
+ if (interactive) {
160
+ const readline = new Readline(config.transcript);
161
+ const render = new Console(out);
162
+ const engine = new Engine({
163
+ story: states,
164
+ start: config.start,
165
+ render: render,
166
+ dialog: readline,
167
+ randomer: randomer,
168
+ });
169
+
170
+ if (config.debugRuntime) {
171
+ engine.debug = true;
172
+ }
173
+
174
+ if (config.waypoint) {
175
+ read(config.waypoint, (err, waypoint) => {
176
+ if (err) {
177
+ done(err);
178
+ return;
179
+ }
180
+ waypoint = JSON.parse(waypoint);
181
+ engine.continue(waypoint);
182
+ });
183
+ } else {
184
+ engine.continue();
185
+ }
186
+ }
187
+ });
188
+
189
+ done(null);
190
+ };
191
+
192
+ const describeStory = (states, out, done) => {
193
+ const keys = Object.keys(states);
194
+ const cells = [['L:C', 'AT', 'DO', 'S', 'USING', 'S', 'GO']];
195
+ for (let i = 0; i < keys.length; i++) {
196
+ const key = keys[i];
197
+ const node = states[key];
198
+ let next;
199
+ if (i === keys.length - 1) {
200
+ next = null;
201
+ } else {
202
+ next = keys[i + 1];
203
+ }
204
+ cells.push([
205
+ stripe(i, node.position),
206
+ stripe(i, key),
207
+ stripe(i, node.mode || node.type),
208
+ stripe(i, node.lift ? '-' : ' '),
209
+ stripe(i, describe(node)),
210
+ stripe(i, node.drop ? '-' : ' '),
211
+ stripe(i, describeNext(node.next, next)),
212
+ ]);
213
+ }
214
+ out.write(
215
+ tableDefault(cells, {
216
+ border: getBorderCharacters('void'),
217
+ columnDefault: {
218
+ paddingLeft: 0,
219
+ paddingRight: 2,
220
+ },
221
+ columns: {
222
+ 4: {
223
+ width: 40,
224
+ wrapWord: true,
225
+ },
226
+ },
227
+ drawHorizontalLine: no,
228
+ }),
229
+ done
230
+ );
231
+ };
232
+
233
+ const stripe = (index, text) => {
234
+ if (index % 2 === 1) {
235
+ return text;
236
+ } else {
237
+ return `\x1b[90m${text}\x1b[0m`;
238
+ }
239
+ };
240
+
241
+ const describeNext = (jump, next) => {
242
+ if (jump === undefined) {
243
+ return '';
244
+ } else if (jump === next) {
245
+ return '';
246
+ } else if (jump == 'RET') {
247
+ return '<-';
248
+ } else if (jump == 'ESC') {
249
+ return '<<';
250
+ } else {
251
+ return `-> ${jump}`;
252
+ }
253
+ };
254
+
255
+ const no = () => {
256
+ return false;
257
+ };
258
+
259
+ const readAndKeep = (stream, callback) => {
260
+ read(stream, (err, content) => {
261
+ if (err != null) {
262
+ return callback(err);
263
+ }
264
+ callback(null, {stream: stream, content: content});
265
+ });
266
+ };
267
+
268
+ const read = (stream, callback) => {
269
+ stream.setEncoding('utf8');
270
+ let string = '';
271
+ const onData = chunk => {
272
+ string += chunk;
273
+ };
274
+ const onEnd = err => {
275
+ if (err) {
276
+ callback(err);
277
+ return;
278
+ }
279
+ callback(null, string);
280
+ };
281
+ stream.on('data', onData);
282
+ stream.on('end', onEnd);
283
+ stream.on('error', onEnd);
284
+ };
285
+
286
+ const serial = (array, eachback, callback) => {
287
+ const values = [];
288
+ const next = i => {
289
+ if (i >= array.length) {
290
+ return callback(null, values);
307
291
  }
308
- }
309
292
 
310
- function dump(errors, writer) {
311
- for (var i = 0; i < errors.length; i++) {
312
- writer.write(errors[i] + '\n');
293
+ eachback(array[i], (err, value) => {
294
+ if (err != null) {
295
+ return callback(err, null);
296
+ }
297
+ values.push(value);
298
+ next(i + 1);
299
+ });
300
+ };
301
+ next(0);
302
+ };
303
+
304
+ const dump = (errors, writer) => {
305
+ for (let i = 0; i < errors.length; i++) {
306
+ writer.write(`${errors[i]}\n`);
307
+ }
308
+ };
309
+
310
+ if (import.meta.url === pathToFileURL(process.argv[1]).href) {
311
+ run(null, process.stdout, err => {
312
+ if (err) {
313
+ console.error(typeof err === 'object' && err.message ? err.message : err);
314
+ if (typeof err.story === 'object' && err.story) {
315
+ const story = err.story;
316
+ dump(story.errors, process.stderr);
317
+ }
318
+ process.exit(-1);
313
319
  }
320
+ });
314
321
  }
315
322
 
316
- main();
323
+ export default run;