@softwarefactory-project/re-ansi 0.5.0 → 0.7.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/README.md CHANGED
@@ -71,8 +71,8 @@ Get started by running:
71
71
  ```sh
72
72
  git clone https://github.com/softwarefactory-project/re-ansi
73
73
  cd re-ansi
74
- yarn install
75
- yarn start
74
+ npm install
75
+ npm run test
76
76
  ```
77
77
 
78
78
  Then build and run tests with `yarn test`.
@@ -81,6 +81,14 @@ Make sure to read about [React][reason-react] and [Reason][rescript-lang] too.
81
81
 
82
82
  ## Changes
83
83
 
84
+ ### 0.7.0
85
+
86
+ - Update to rescript version 11.
87
+
88
+ ### 0.6.0
89
+
90
+ - Fix support for bright colors.
91
+
84
92
  ### 0.5.0
85
93
 
86
94
  - Add support for `[m` and `[K`.
package/package.json CHANGED
@@ -1,9 +1,14 @@
1
1
  {
2
2
  "name": "@softwarefactory-project/re-ansi",
3
- "version": "0.5.0",
3
+ "version": "0.7.0",
4
4
  "description": "ANSI code to HTML",
5
- "files": ["README.md", "LICENSE", "bsconfig.json", "src"],
6
- "main": "./src/Ansi.bs.js",
5
+ "files": [
6
+ "README.md",
7
+ "LICENSE",
8
+ "rescript.json",
9
+ "src"
10
+ ],
11
+ "main": "./src/Ansi.res.js",
7
12
  "license": "Apache-2.0",
8
13
  "homepage": "https://github.com/softwarefactory-project/re-ansi",
9
14
  "repository": {
@@ -11,24 +16,19 @@
11
16
  "url": "git+https://softwarefactory-project.io/r/software-factory/re-ansi.git"
12
17
  },
13
18
  "scripts": {
14
- "build": "bsb -make-world",
15
- "start": "bsb -make-world -w",
16
- "test": "npm run build && node tests/Spec.bs.js"
19
+ "build": "rescript",
20
+ "clean": "rescript clean",
21
+ "dev": "rescript -w",
22
+ "test": "npm run build && node tests/Spec.res.js"
17
23
  },
18
24
  "keywords": [
19
- "ReasonReact",
20
25
  "ansi",
21
- "reason",
22
- "reason-react",
23
- "reasonml",
24
26
  "rescript",
25
- "bucklescript"
27
+ "react"
26
28
  ],
27
29
  "dependencies": {
28
- "reason-react": "^0.9.1",
29
- "bs-platform": "^8.2.0"
30
- },
31
- "devDependencies": {
32
- "react": "^16.14.0"
30
+ "@rescript/core": "^1.3.0",
31
+ "@rescript/react": "^0.12.1",
32
+ "rescript": "^11.1.0"
33
33
  }
34
34
  }
@@ -1,18 +1,16 @@
1
1
  {
2
2
  "name": "@softwarefactory-project/re-ansi",
3
- "reason": {
4
- "react-jsx": 3
3
+ "jsx": {
4
+ "version": 4
5
5
  },
6
6
  "sources": ["src", { "dir": "tests", "type": "dev" }],
7
- "bsc-flags": ["-bs-super-errors", "-bs-no-version-header"],
7
+ "bsc-flags": ["-open RescriptCore"],
8
8
  "package-specs": [
9
9
  {
10
10
  "module": "commonjs",
11
11
  "in-source": true
12
12
  }
13
13
  ],
14
- "suffix": ".bs.js",
15
- "bs-dependencies": ["reason-react"],
16
- "bs-dev-dependencies": [],
17
- "refmt": 3
14
+ "suffix": ".res.js",
15
+ "bs-dependencies": ["@rescript/react", "@rescript/core"]
18
16
  }
@@ -15,16 +15,14 @@
15
15
  // Ansi renders a log ANSI code to a React component
16
16
 
17
17
  // Document type
18
- type document = list(atom)
18
+ type rec document = list<atom>
19
19
  and atom =
20
20
  | Text(string)
21
21
  | Link(string)
22
22
  | LineBreak
23
23
  | DocStyle(ReactDOM.Style.t, document);
24
24
 
25
- type parser('a) = (int, option('a));
26
-
27
- open Belt;
25
+ type parser<'a> = (int, option<'a>);
28
26
 
29
27
  module AnsiCode = {
30
28
  type code =
@@ -35,7 +33,7 @@ module AnsiCode = {
35
33
  | Style(ReactDOM.Style.t);
36
34
 
37
35
  // Convert a 4 bits color code to its css color: https://en.wikipedia.org/wiki/ANSI_escape_code#3-bit_and_4-bit
38
- let fourBitColors = (code: int): option(string) =>
36
+ let fourBitColors = (code: int): option<string> =>
39
37
  switch (code) {
40
38
  | 00 => "black"->Some
41
39
  | 01 => "red"->Some
@@ -59,6 +57,21 @@ module AnsiCode = {
59
57
  None;
60
58
  };
61
59
 
60
+ let threeBitColors = (code: int): option<string> =>
61
+ switch (code) {
62
+ | 00 => "grey"->Some
63
+ | 01 => "red"->Some
64
+ | 02 => "green"->Some
65
+ | 03 => "yellow"->Some
66
+ | 04 => "blue"->Some
67
+ | 05 => "magenta"->Some
68
+ | 06 => "cyan"->Some
69
+ | 07 => "white"->Some
70
+ | _ =>
71
+ Js.log2("Unknown color value:", code);
72
+ None;
73
+ };
74
+
62
75
  // Css utility
63
76
  let combine = (css1, css2) => ReactDOM.Style.combine(css1, css2);
64
77
  let addWeight = fontWeight => ReactDOM.Style.make(~fontWeight, ());
@@ -70,11 +83,12 @@ module AnsiCode = {
70
83
  module ColorCss = {
71
84
  type t =
72
85
  | Foreground(int)
86
+ | BrightForeground(int)
73
87
  | Background(int);
74
- let getColorStyle = (colorMode: int, colorValue: int): option(t) =>
88
+ let getColorStyle = (colorMode: int, colorValue: int): option<t> =>
75
89
  switch (colorMode) {
76
- | 3
77
- | 9 => colorValue->Foreground->Some
90
+ | 3 => colorValue->Foreground->Some
91
+ | 9 => colorValue->BrightForeground->Some
78
92
  | 4
79
93
  | 0 => colorValue->Background->Some
80
94
  | _ =>
@@ -82,12 +96,16 @@ module AnsiCode = {
82
96
  None;
83
97
  };
84
98
 
85
- let getColorStyleCss = (color: t): option(ReactDOM.Style.t) =>
99
+ let getColorStyleCss = (color: t): option<ReactDOM.Style.t> =>
86
100
  switch (color) {
87
101
  | Foreground(v) =>
88
102
  v
89
103
  ->fourBitColors
90
104
  ->Option.flatMap(color => ReactDOM.Style.make(~color, ())->Some)
105
+ | BrightForeground(v) =>
106
+ v
107
+ ->threeBitColors
108
+ ->Option.flatMap(color => ReactDOM.Style.make(~color, ~fontWeight="bold", ())->Some)
91
109
  | Background(v) =>
92
110
  v
93
111
  ->fourBitColors
@@ -96,7 +114,7 @@ module AnsiCode = {
96
114
  )
97
115
  };
98
116
 
99
- let get = (colorMode: int, colorValue: int): option(ReactDOM.Style.t) =>
117
+ let get = (colorMode: int, colorValue: int): option<ReactDOM.Style.t> =>
100
118
  colorMode
101
119
  ->int_of_cp
102
120
  ->getColorStyle(colorValue->int_of_cp)
@@ -109,7 +127,7 @@ module AnsiCode = {
109
127
  | Regular
110
128
  | FontStyle(ReactDOM.Style.t);
111
129
 
112
- let getFontStyle = (fontMode: int): option(t) =>
130
+ let getFontStyle = (fontMode: int): option<t> =>
113
131
  switch (fontMode) {
114
132
  | 0 => Regular->Some
115
133
  | 1 => "bold"->addWeight->FontStyle->Some
@@ -123,61 +141,66 @@ module AnsiCode = {
123
141
  | _ => None
124
142
  };
125
143
 
126
- let getFontStyleCss = (font: t): option(ReactDOM.Style.t) =>
144
+ let getFontStyleCss = (font: t): option<ReactDOM.Style.t> =>
127
145
  switch (font) {
128
146
  | Regular => None
129
147
  | FontStyle(css) => css->Some
130
148
  };
131
149
 
132
- let get = (fontMode: int): option(ReactDOM.Style.t) =>
150
+ let get = (fontMode: int): option<ReactDOM.Style.t> =>
133
151
  fontMode->int_of_cp->getFontStyle->Option.flatMap(getFontStyleCss);
134
152
  };
135
153
 
136
154
  // Link management
137
155
  module HttpLink = {
138
- let linkRe = [%re "/^(http(s)?:\\/\\/[^\s]+)/"];
156
+ let linkRe = RegExp.fromString("^(http(s)?:\\/\\/[^\\s]+)");
139
157
 
140
- let get = (txt: string): parser(code) =>
158
+ let get = (txt: string): parser<code> =>
141
159
  linkRe
142
- ->Js.Re.exec_(txt)
143
- ->Option.flatMap(res =>
144
- (
145
- switch (res->Js.Re.captures) {
146
- | [|url, _, _|] => url->Js.Nullable.toOption
147
- | _ => None
148
- }
149
- )
160
+ ->RegExp.exec(txt)
161
+ ->Option.flatMap(res => res->Array.get(0)
150
162
  ->Option.flatMap(url =>
151
163
  (url->Js.String.length, url->HRef->Some)->Some
152
164
  )
153
165
  )
154
- ->Option.getWithDefault((1, None));
166
+ ->Option.getOr((1, None));
155
167
  };
156
168
 
157
169
  // Parse an ANSI code, returning the length of the sequence
158
- let parse = (txt: string, pos: int): parser(code) =>
170
+ let parse = (txt: string, pos: int): parser<code> =>
159
171
  switch (Js.String.codePointAt(pos, txt)) {
160
172
  | Some(0x68) => HttpLink.get(txt->Js.String.slice(~from=pos, ~to_=512))
161
173
  | Some(0x0a) => (1, CarriageReturn->Some)
162
174
  | Some(0x0d)
163
175
  | Some(0x1b) =>
164
176
  // escape sequence begin
165
- let rec cps = (idx: int, acc: list(int)): list(int) =>
177
+ let codePoints = [];
178
+
179
+ let rec readCodePoints = (idx: int) =>
166
180
  switch (idx > 10, Js.String.codePointAt(pos + idx, txt)) {
167
- | (false, Some(109)) => acc
168
- | (false, Some(n)) => cps(idx + 1, acc->List.add(n))
169
- | _ => acc
181
+ | (false, Some(109)) => ()
182
+ | (false, Some(n)) => {
183
+ codePoints->Array.push(n);
184
+ readCodePoints(idx + 1)
185
+ }
186
+ | _ => ()
170
187
  };
188
+ readCodePoints(1);
189
+
190
+ // use get for pattern match value, it's fine to use 'getUnsafe' because we'll get undefined value.
191
+ let get = (idx) => codePoints->Array.getUnsafe(idx)
171
192
 
172
- let codePoints = 1->cps([])->List.reverse;
173
- let length = codePoints->List.length + 2;
193
+ // Add 2 to take the 0x1b and 109 into account
194
+ let length = codePoints->Array.length + 2;
174
195
  switch (codePoints) {
175
196
  // \n\x0d[1A\x0d[J
176
- | [10, 27, 91, 49, 65, 27, 91, 74, ..._] => (9, EraseLine->Some)
197
+ | _ when
198
+ get(0) == 10 && get(1) == 27 && get(2) == 91 && get(3) == 49 &&
199
+ get(4) == 65 && get(5) == 27 && get(6) == 91 && get(7) == 74 => (9, EraseLine->Some)
177
200
  // [_K
178
- | [91, _, 75, ..._] => (4, EraseLine->Some)
201
+ | _ when get(0) == 91 && get(2) == 75 => (4, EraseLine->Some)
179
202
  // [K
180
- | [91, 75, ..._] => (3, EraseLine->Some)
203
+ | _ when get(0) == 91 && get(1) == 75 => (3, EraseLine->Some)
181
204
  // [00m
182
205
  | [91, 48, 48]
183
206
  // [0m
@@ -196,7 +219,7 @@ module AnsiCode = {
196
219
  length,
197
220
  ColorCss.get(
198
221
  colorMode,
199
- colorValue + (xs->List.length == 4 ? 10 : 0),
222
+ colorValue + (xs->Array.length == 4 ? 10 : 0),
200
223
  )
201
224
  ->Option.flatMap(colorCss => colorCss->Style->Some),
202
225
  )
@@ -226,7 +249,7 @@ module AnsiCode = {
226
249
  length,
227
250
  ColorCss.get(cm1, cv1)
228
251
  ->Option.flatMap(colorCss1 =>
229
- ColorCss.get(cm2, cv2 + (xs->List.length == 9 ? 10 : 0))
252
+ ColorCss.get(cm2, cv2 + (xs->Array.length == 9 ? 10 : 0))
230
253
  ->Option.flatMap(colorCss2 => {
231
254
  let css = combine(colorCss1, colorCss2);
232
255
  switch (style->FontCss.get) {
@@ -250,26 +273,26 @@ module Document = {
250
273
  txt->Js.String.slice(~from, ~to_)->Text;
251
274
 
252
275
  // Parse a document
253
- let parse = (txt: string, length: int, pos: int): parser(document) => {
276
+ let parse = (txt: string, length: int, pos: int): parser<document> => {
254
277
  let rec go = (pos: int, prev: int) =>
255
278
  switch (pos == length, txt->AnsiCode.parse(pos)) {
256
279
  // we reached the end of the txt
257
- | (true, _) => (pos, [text(txt, prev, pos)]->Some)
280
+ | (true, _) => (pos, list{text(txt, prev, pos)}->Some)
258
281
  // current codepoint is an ANSI code or a HRef
259
282
  | (_, (length, Some(code))) =>
260
283
  let prevElem = txt->text(prev, pos);
261
284
  let pos = pos + length;
262
285
  switch (code) {
263
- | Clear => (pos, [prevElem]->Some)
286
+ | Clear => (pos, list{prevElem}->Some)
264
287
  | EraseLine => pos->go(pos)
265
- | CarriageReturn => (pos, [prevElem, LineBreak]->Some)
266
- | HRef(link) => (pos, [prevElem, link->Link]->Some)
288
+ | CarriageReturn => (pos, list{prevElem, LineBreak}->Some)
289
+ | HRef(link) => (pos, list{prevElem, link->Link}->Some)
267
290
  | Style(style) =>
268
291
  // recursively parse the stylized block
269
292
  switch (pos->go(pos)) {
270
293
  | (pos, Some(styled)) => (
271
294
  pos,
272
- [prevElem, DocStyle(style, styled)]->Some,
295
+ list{prevElem, DocStyle(style, styled)}->Some,
273
296
  )
274
297
  | _ => (pos, None)
275
298
  }
@@ -283,7 +306,7 @@ module Document = {
283
306
 
284
307
  // Convert a string to a document
285
308
  let parse = (txt: string): document => {
286
- let rec go = (txt: string, acc: list(document)) => {
309
+ let rec go = (txt: string, acc: list<document>) => {
287
310
  let length = txt->Js.String.length;
288
311
  switch (txt->Document.parse(length, 0)) {
289
312
  | (pos, Some(doc)) when pos == length => acc->List.add(doc)
@@ -292,39 +315,39 @@ let parse = (txt: string): document => {
292
315
  | _ => acc
293
316
  };
294
317
  };
295
- txt->go([])->Belt.List.reverse->Belt.List.flatten;
318
+ txt->go(list{})->Belt.List.reverse->Belt.List.flatten;
296
319
  };
297
320
 
298
321
  // Convert a document to a React.element
299
322
  let render = (doc: document): React.element => {
300
323
  let rec go =
301
- (xs: document, idx: int, acc: list(React.element)): React.element =>
324
+ (xs: document, idx: int, acc: list<React.element>): React.element =>
302
325
  switch (xs) {
303
- | [] => acc->List.reverse->List.toArray->ReasonReact.array
304
- | [LineBreak, ...xs] =>
326
+ | list{} => acc->List.reverse->List.toArray->React.array
327
+ | list{LineBreak, ...xs} =>
305
328
  xs->go(idx + 1, acc->List.add(<br key={idx->string_of_int} />))
306
- | [Text(txt), ...xs] =>
307
- xs->go(idx + 1, acc->List.add(txt->React.string))
308
- | [Link(href), ...xs] =>
329
+ | list{Text(txt), ...xs} =>
330
+ xs->go(idx + 1, acc->List.add(React.string(txt)))
331
+ | list{Link(href), ...xs} =>
309
332
  xs->go(
310
333
  idx + 1,
311
334
  acc->List.add(
312
- <a key={idx->string_of_int} href> href->React.string </a>,
335
+ <a key={idx->string_of_int} href> {href->React.string} </a>,
313
336
  ),
314
337
  )
315
- | [DocStyle(style, elems), ...xs] =>
338
+ | list{DocStyle(style, elems), ...xs} =>
316
339
  xs->go(
317
340
  idx + 1,
318
341
  acc->List.add(
319
- <span key={idx->string_of_int} style> {elems->go(0, [])} </span>,
342
+ <span key={idx->string_of_int} style> {elems->go(0, list{})} </span>,
320
343
  ),
321
344
  )
322
345
  };
323
- doc->go(0, []);
346
+ doc->go(0, list{});
324
347
  };
325
348
 
326
349
  // The react component
327
- [@react.component]
350
+ @react.component
328
351
  let make = (~log: string) => {
329
352
  <div> {log->parse->render} </div>;
330
353
  };