functionalscript 0.0.493 → 0.0.495

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.
@@ -1,7 +1,7 @@
1
1
  # This workflow will do a clean install of node dependencies, cache/restore them, build the source code and run tests across different versions of node
2
2
  # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
3
3
 
4
- name: node CI
4
+ name: CI
5
5
 
6
6
  on:
7
7
  push:
@@ -10,7 +10,7 @@ on:
10
10
  pull_request:
11
11
 
12
12
  jobs:
13
- build:
13
+ node:
14
14
 
15
15
  runs-on: ubuntu-latest
16
16
 
@@ -29,3 +29,23 @@ jobs:
29
29
  - run: npm test
30
30
  - run: npm run version
31
31
  - run: npm pack
32
+
33
+ deno:
34
+
35
+ runs-on: ubuntu-latest
36
+
37
+ steps:
38
+ - uses: actions/checkout@v2
39
+ - uses: denoland/setup-deno@v1
40
+ with:
41
+ deno-version: v1.x
42
+ - run: deno run --quiet --allow-read --allow-env --allow-net --allow-hrtime ./dev/test.mjs
43
+
44
+ bun:
45
+
46
+ runs-on: ubuntu-latest
47
+
48
+ steps:
49
+ - uses: actions/checkout@v2
50
+ - run: curl https://bun.sh/install | bash
51
+ - run: /home/runner/.bun/bin/bun ./dev/test.mjs
@@ -10,7 +10,7 @@ on:
10
10
  pull_request:
11
11
 
12
12
  jobs:
13
- build:
13
+ COM:
14
14
 
15
15
  strategy:
16
16
  matrix:
package/dev/module.mjs CHANGED
@@ -78,9 +78,24 @@ const remove_tail = v => dif => v.slice(0, v.length - dif)
78
78
  /** @type {any} */
79
79
  const self = globalThis
80
80
 
81
+ /** @type {() => Promise<FsPromises>} */
82
+ export const fs = () => import(self.Deno ? 'https://deno.land/std/node/fs/promises.ts' : 'node:fs/promises')
83
+
84
+ /** @type {(code: number) => never} */
85
+ export const exit = self.Deno ? self.Deno.exit : process.exit
86
+
87
+ /** @type {(v: string) => string|undefined} */
88
+ export const env =
89
+ self.Deno ? self.Deno.env.get :
90
+ a => {
91
+ const r = Object.getOwnPropertyDescriptor(process.env, a)
92
+ return r === void 0 ? void 0 :
93
+ typeof r.get === 'function' ? r.get() :
94
+ r.value
95
+ }
96
+
81
97
  export const loadModuleMap = async () => {
82
- /** @type {FsPromises} */
83
- const { readdir, readFile } = await import(self.Deno ? 'https://deno.land/std/node/fs/promises.ts' : 'node:fs/promises')
98
+ const { readdir, readFile } = await fs();
84
99
 
85
100
  /** @type {() => Promise<FunctionMap>} */
86
101
  const load = async () => {
@@ -1,6 +1,7 @@
1
1
  const list = require('../../types/list/module.f.cjs')
2
2
  const { fold } = list
3
- const { reset, fgGreen } = require('../../text/sgr/module.f.cjs')
3
+ const { reset, fgGreen, fgRed, bold } = require('../../text/sgr/module.f.cjs')
4
+ const result = require('../../types/result/module.f.cjs')
4
5
 
5
6
  /**
6
7
  * @typedef {{
@@ -28,7 +29,7 @@ const { reset, fgGreen } = require('../../text/sgr/module.f.cjs')
28
29
 
29
30
  /**
30
31
  * @template T
31
- * @typedef {(state: T) => readonly[number, T]} PerformanceNow
32
+ * @typedef {<R>(f: () => R) => (state: T) => readonly[R, number, T]} Measure
32
33
  */
33
34
 
34
35
  /**
@@ -36,72 +37,102 @@ const { reset, fgGreen } = require('../../text/sgr/module.f.cjs')
36
37
  * @typedef {{
37
38
  * readonly moduleMap: ModuleMap,
38
39
  * readonly log: Log<T>,
39
- * readonly performanceNow: PerformanceNow<T>,
40
+ * readonly error: Log<T>,
41
+ * readonly measure: Measure<T>,
40
42
  * readonly state: T,
43
+ * readonly tryCatch: <R>(f: () => R) => result.Result<R, unknown>,
44
+ * readonly env: (n: string) => string|undefined
41
45
  * }} Input
42
46
  */
43
47
 
44
48
  /** @type {(s: string) => boolean} */
45
49
  const isTest = s => s.endsWith('test.f.cjs')
46
50
 
51
+ /**
52
+ * @typedef {{
53
+ * readonly time: number,
54
+ * readonly pass: number,
55
+ * readonly fail: number,
56
+ * }} TestState
57
+ */
58
+
59
+ /** @type {(time: number) => (testState: TestState) => TestState} */
60
+ const addPass = delta => ts => ({ ...ts, time: ts.time + delta, pass: ts.pass + 1 })
61
+
62
+ /** @type {(time: number) => (testState: TestState) => TestState} */
63
+ const addFail = delta => ts => ({ ...ts, time: ts.time + delta, fail: ts.fail + 1 })
64
+
47
65
  /**
48
66
  * @template T
49
- * @typedef {readonly[number, T]} FullState
67
+ * @typedef {readonly[TestState, T]} FullState
50
68
  */
51
69
 
52
- /** @type {<T>(input: Input<T>) => T} */
70
+ /** @type {<T>(input: Input<T>) => readonly[number, T]} */
53
71
  const main = input => {
54
- let { moduleMap, log, performanceNow, state } = input
72
+ let { moduleMap, log, error, measure, tryCatch, env, state } = input
73
+ const isGitHub = env('GITHUB_ACTION') !== void 0
55
74
  /** @typedef {input extends Input<infer T> ? T : never} T */
56
- /** @type {(i: string) => (v: unknown) => (fs: FullState<T>) => FullState<T>} */
57
- const test = i => v => ([time, state]) => {
58
- const next = test(`${i}| `)
59
- switch (typeof v) {
60
- case 'function': {
61
- if (v.length === 0) {
62
- let b = 0;
63
- [b, state] = performanceNow(state)
64
- const r = v()
65
- let e = 0;
66
- [e, state] = performanceNow(state)
67
- const delta = e - b
68
- time += delta
69
- state = log(`${i}() ${fgGreen}ok${reset}, ${delta} ms`)(state);
70
- [time, state] = next(r)([time, state])
75
+ /** @type {(k: readonly[string, Module]) => (fs: FullState<T>) => FullState<T>} */
76
+ const f = ([k, v]) => {
77
+ /** @type {(i: string) => (v: unknown) => (fs: FullState<T>) => FullState<T>} */
78
+ const test = i => v => ([ts, state]) => {
79
+ const next = test(`${i}| `)
80
+ switch (typeof v) {
81
+ case 'function': {
82
+ if (v.length === 0) {
83
+ const [[s, r], delta, state0] = measure(() => tryCatch(/** @type {() => unknown} */(v)))(state)
84
+ state = state0
85
+ if (s === 'error') {
86
+ ts = addFail(delta)(ts)
87
+ if (isGitHub) {
88
+ // https://docs.github.com/en/actions/learn-github-actions/workflow-commands-for-github-actions
89
+ // https://github.com/OndraM/ci-detector/blob/main/src/Ci/GitHubActions.php
90
+ state = error(`::error file=${k},line=1,title=[3]['a']()::${r}`)(state)
91
+ } else {
92
+ state = error(`${i}() ${fgRed}error${reset}, ${delta} ms`)(state)
93
+ state = error(`${fgRed}${r}${reset}`)(state)
94
+ }
95
+ } else {
96
+ ts = addPass(delta)(ts)
97
+ state = log(`${i}() ${fgGreen}ok${reset}, ${delta} ms`)(state)
98
+ }
99
+ [ts, state] = next(r)([ts, state])
100
+ }
101
+ break
71
102
  }
72
- break
73
- }
74
- case 'object': {
75
- if (v !== null) {
76
- /** @type {(k: readonly[string|number, unknown]) => (fs: FullState<T>) => FullState<T>} */
77
- const f = ([k, v]) => ([time, state]) => {
78
- state = log(`${i}${k}:`)(state);
79
- [time, state] = next(v)([time, state])
80
- return [time, state]
103
+ case 'object': {
104
+ if (v !== null) {
105
+ /** @type {(k: readonly[string|number, unknown]) => (fs: FullState<T>) => FullState<T>} */
106
+ const f = ([k, v]) => ([time, state]) => {
107
+ state = log(`${i}${k}:`)(state);
108
+ [time, state] = next(v)([time, state])
109
+ return [time, state]
110
+ }
111
+ [ts, state] = fold
112
+ (f)
113
+ ([ts, state])
114
+ (v instanceof Array ? list.entries(v) : Object.entries(v))
81
115
  }
82
- [time, state] = fold
83
- (f)
84
- ([time, state])
85
- (v instanceof Array ? list.entries(v) : Object.entries(v))
116
+ break
86
117
  }
87
- break
88
118
  }
119
+ return [ts, state]
89
120
  }
90
- return [time, state]
91
- }
92
- const next = test('| ')
93
- /** @type {(k: readonly[string, Module]) => (fs: FullState<T>) => FullState<T>} */
94
- const f = ([k, v]) => ([time, state]) => {
95
- if (isTest(k)) {
96
- state = log(`testing ${k}`)(state);
97
- [time, state] = next(v.exports)([time, state])
121
+ return ([ts, state]) => {
122
+ if (isTest(k)) {
123
+ state = log(`testing ${k}`)(state);
124
+ [ts, state] = test('| ')(v.exports)([ts, state])
125
+ }
126
+ return [ts, state]
98
127
  }
99
- return [time, state]
100
128
  }
101
- let time = 0;
102
- [time, state] = fold(f)([time, state])(Object.entries(moduleMap))
103
- state = log(`total ${time} ms`)(state);
104
- return state
129
+ /** @type {TestState} */
130
+ let ts = { time: 0, pass: 0, fail: 0 };
131
+ [ts, state] = fold(f)([ts, state])(Object.entries(moduleMap))
132
+ const fgFail = ts.fail === 0 ? fgGreen : fgRed
133
+ state = log(`${bold}Number of tests: pass: ${fgGreen}${ts.pass}${reset}${bold}, fail: ${fgFail}${ts.fail}${reset}${bold}, total: ${ts.pass + ts.fail}${reset}`)(state)
134
+ state = log(`${bold}Time: ${ts.time} ms${reset}`)(state);
135
+ return [ts.fail !== 0 ? 1 : 0, state]
105
136
  }
106
137
 
107
- module.exports = main
138
+ module.exports = main
package/dev/test.mjs CHANGED
@@ -1,13 +1,44 @@
1
- import { loadModuleMap } from './module.mjs'
1
+ import { loadModuleMap, exit, env } from './module.mjs'
2
2
 
3
- /** @type {(s: string) => <T>(_: T) => T} */
4
- const log = s => state => {
5
- console.log(s)
3
+ /** @type {(f: (s: string) => void) => (s: string) => <T>(_: T) => T} */
4
+ const anyLog = f => s => state => {
5
+ f(s)
6
6
  return state
7
7
  }
8
8
 
9
- /** @type {<T>(_: T) => readonly[number, T]} */
10
- const performanceNow = state => [performance.now(), state]
9
+ /**
10
+ * @template T
11
+ * @typedef {readonly['ok', T]} Ok
12
+ */
13
+
14
+ /**
15
+ * @template E
16
+ * @typedef {readonly['error', E]} Error
17
+ */
18
+
19
+ /**
20
+ * @template T
21
+ * @template E
22
+ * @typedef {Ok<T>|Error<E>} Result
23
+ */
24
+
25
+ /** @type {<T>(f: () => T) => Result<T, unknown>} */
26
+ const tryCatch = f => {
27
+ // Side effect: `try catch` is not allowed in FunctionalScript.
28
+ try {
29
+ return ['ok', f()]
30
+ } catch (e) {
31
+ return ['error', e]
32
+ }
33
+ }
34
+
35
+ /** @type {<R>(f: () => R) => <T>(state: T) => readonly[R, number, T]} Measure} */
36
+ const measure = f => state => {
37
+ const b = performance.now()
38
+ const r = f()
39
+ const e = performance.now()
40
+ return [r, e - b, state]
41
+ }
11
42
 
12
43
  // test runner.
13
44
  const main = async() => {
@@ -15,11 +46,15 @@ const main = async() => {
15
46
 
16
47
  /** @type {any} */
17
48
  const f = moduleMap['./dev/test/module.f.cjs'].exports
18
- f({
49
+ const r = f({
19
50
  moduleMap,
20
- log,
21
- performanceNow,
51
+ log: anyLog(console.log),
52
+ error: anyLog(console.error),
53
+ measure,
54
+ tryCatch,
55
+ env,
22
56
  })
57
+ exit(r[0])
23
58
  }
24
59
 
25
60
  main()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "functionalscript",
3
- "version": "0.0.493",
3
+ "version": "0.0.495",
4
4
  "description": "FunctionalScript is a functional subset of JavaScript",
5
5
  "main": "module.f.cjs",
6
6
  "scripts": {
@@ -13,5 +13,7 @@ module.exports = {
13
13
  /** @readonly */
14
14
  bold: sgr(1),
15
15
  /** @readonly */
16
+ fgRed: sgr(31),
17
+ /** @readonly */
16
18
  fgGreen: sgr(32),
17
19
  }
@@ -1,17 +0,0 @@
1
- name: bun
2
-
3
- on:
4
- push:
5
- branches:
6
- - master
7
- pull_request:
8
-
9
- jobs:
10
- build:
11
-
12
- runs-on: ubuntu-latest
13
-
14
- steps:
15
- - uses: actions/checkout@v2
16
- - run: curl https://bun.sh/install | bash
17
- - run: /home/runner/.bun/bin/bun ./dev/test.mjs
@@ -1,19 +0,0 @@
1
- name: deno
2
-
3
- on:
4
- push:
5
- branches:
6
- - master
7
- pull_request:
8
-
9
- jobs:
10
- build:
11
-
12
- runs-on: ubuntu-latest
13
-
14
- steps:
15
- - uses: actions/checkout@v2
16
- - uses: denoland/setup-deno@v1
17
- with:
18
- deno-version: v1.x
19
- - run: deno run --allow-read --allow-env --allow-net --allow-hrtime ./dev/test.mjs