1 | <div align="center">
|
2 |
|
3 | ### *Zero-dependency collection of tools aiming to speed up modern JavaScript development.*
|
4 |
|
5 | <br>
|
6 |
|
7 | ![Version image](https://badgen.net/badge/npm/2.2.0/green?icon=npm)
|
8 |
|
9 | [![install size](https://packagephobia.com/badge?p=@applicvision/js-toolbox@latest)](https://packagephobia.com/result?p=@applicvision/js-toolbox@latest)
|
10 |
|
11 | ```console
|
12 | > npm install @applicvision/js-toolbox
|
13 | ```
|
14 |
|
15 | </div>
|
16 |
|
17 | <br>
|
18 |
|
19 |
|
20 | ## Toolbox contents
|
21 |
|
22 | - [Tests](#tests)
|
23 | - [Auto reload](#auto-reload)
|
24 | - [Ayay](#ayay)
|
25 | - [IzzaDate](#izzadate)
|
26 | - [Argument Parser](#argument-parser)
|
27 | - [Terminal Style](#terminal-style)
|
28 |
|
29 | <br>
|
30 | <br>
|
31 |
|
32 | # Tests
|
33 |
|
34 | Write tests with EcmaScript modules syntax, and run them both in browser and in Node, without any build step. Compose your tests in suites using `describe`, and declare test units with `test` or `it`:
|
35 |
|
36 | ```javascript
|
37 | // test.js
|
38 | import {describe, it} from '@applicvision/js-toolbox/test'
|
39 | import assert from 'node:assert/strict'
|
40 |
|
41 | describe('My suite', () => {
|
42 | it('test', () => {
|
43 | assert.equal(1 + 1, 2)
|
44 | })
|
45 | })
|
46 | ```
|
47 |
|
48 | A bin called `testa` (Swedish for 'test' in imperative form) is exported which runs the test. So either put `test: testa` in your package.json script section, and call:
|
49 | ```
|
50 | npm test
|
51 | ```
|
52 |
|
53 | Or even shorter, just call with npx:
|
54 |
|
55 | ```
|
56 | npx testa
|
57 | ```
|
58 |
|
59 | In your `package.json` you can configure which files will be picked up by the test runner.
|
60 | ```json
|
61 | "config": {
|
62 | "testa": {
|
63 | "endsWith": ".test.js"
|
64 | }
|
65 | }
|
66 | ```
|
67 | By default, files ending with `.test.js` will be searched for from the project directory, but you can specify files and folders as argument to limit the search: `testa unit integration extra.test.js`.
|
68 |
|
69 | Passing `--help` will print the usage information:
|
70 |
|
71 | ```
|
72 | Options:
|
73 | -f, --filter Specify a regex to only run matching tests and describes.
|
74 | -l, --logger Specify a logger for the test. Available: animated, simple. Default: animated
|
75 | -m, --mode Specify mode, node or browser. Default node.
|
76 | -w, --watch In browser mode, reload on file changes in specified directory.
|
77 | -p, --port In browser mode, select the port to run the server on. Default 3000.
|
78 | --timeout Set general time limit for tests. Default 4s.
|
79 | --bail Exit immediately on first test failure.
|
80 | --only Run only tests marked with only.
|
81 | --notify Show MacOS notification (osascript)
|
82 | -h, --help Show this message and exit.
|
83 | ```
|
84 |
|
85 | ## Browser mode
|
86 | Going back to the start of the test module example
|
87 | ```javascript
|
88 | import { describe, test } from '@applicvision/js-toolbox/test'
|
89 | import assert from 'node:assert/strict'
|
90 | ```
|
91 | one might wonder how this code will run in the browser, without any compiling, transpiling, translation, build steps or anything.
|
92 |
|
93 | The answer to that is two fairly new features for EcmaScript modules. The first is in the browser, and is called importmaps, which is available in recent versions of the major web browsers. See more at https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type/importmap.
|
94 |
|
95 | The other is on the Node side, and is called `import.meta.resolve`. More info here: https://nodejs.org/dist/latest-v20.x/docs/api/esm.html#importmetaresolvespecifier.
|
96 |
|
97 | So the first line in the example above is resolved to the correct file in node_modules, when the browser requests the module.
|
98 |
|
99 | And the second line, `import node:assert/strict`, is intercepted and resolves a browser compatible implementation of `node:assert/strict`.
|
100 |
|
101 | A separate bin called `browser-test` is exposed. It simply calls the testrunner with test mode set to browser.
|
102 |
|
103 | The server for browser testing is run with http by default, but can also be run with https. To set this up, please create a `key.pem` (private key) and `cert.pem` (certificate), in any directory, and set the directory location to an environment variable called `JS_TOOLBOX_CERT`.
|
104 |
|
105 | Once the server is started, default port is 3000, simply visit the page in the browser to run the tests.
|
106 |
|
107 | Query parameters can be used to configure the tests. Available query parameters are:
|
108 | * `only` – run only tests marked with only
|
109 | * `filter=<regex>` – Run tests matching filter.
|
110 | * `expand` – Keep the test suites expanded when finished
|
111 |
|
112 | Two examples:
|
113 | ```bash
|
114 | 'https://localhost:3000/?only'
|
115 | #run only tests marked with .only
|
116 |
|
117 | 'https://localhost:3000/?filter=sum|random&filter=ayay'
|
118 | #run only tests matching /sum|random/ and /ayay/
|
119 | ```
|
120 | <br>
|
121 |
|
122 | # Auto reload
|
123 |
|
124 | Auto reload is a tool to make the browser reload when files are changed. Start it with the exported bin, `autoreload`.
|
125 | In package json, add a script row:
|
126 | ```
|
127 | autoreload: autoreload src
|
128 | ```
|
129 | And then call
|
130 | ```
|
131 | npm run autoreload
|
132 | ```
|
133 | Or with npx:
|
134 | ```
|
135 | npx autoreload src
|
136 | ```
|
137 |
|
138 | This starts the file watcher and the server. The default port is 3333, but this can be configured with option `--port`. The server can also be run with https. To set up this, please create a `key.pem` (private key) and `cert.pem` (certificate), in any directory, and set the directory location to an environment variable called `JS_TOOLBOX_CERT`, or pass the directory location with the option `--certificate`.
|
139 |
|
140 | Example:
|
141 | ```
|
142 | npx autoreload --certificate ~/certificates/localhost
|
143 | ```
|
144 |
|
145 | Then in your web application, during development, import the client script, and call start:
|
146 | ```html
|
147 | <script type="module">
|
148 | import { start } from 'https://localhost:3333/autoreload.js'
|
149 | start()
|
150 | </script>
|
151 | ```
|
152 |
|
153 | A little indicator will appear in the bottom right corner. If the indicator is green, it means autoreload is active, and file changes will trigger page reload. If it turns red, it means it lost connection to the server. It will automatically try to reconnect if it loses connection, and if connection is reestablished, the indicator color will return to green.
|
154 |
|
155 | The file watcher can also be used as a standalone bin:
|
156 | ```
|
157 | npx filewatcher .
|
158 | ```
|
159 |
|
160 | Then it will emit to standard out when files change, and another process could be piped to it to handle file changes. The auto reload functionality is built that way.
|
161 |
|
162 | <br>
|
163 |
|
164 | # Ayay
|
165 |
|
166 | A subclass of the native Array. The aim is to extend Array with immutable methods. Create instances of Ayay using the constructor, or any of the static factory methods (which are just the same as those of Array):
|
167 |
|
168 | ```javascript
|
169 | import Ayay from '@applicvision/js-toolbox/Ayay'
|
170 |
|
171 | const anArray = new Ayay()
|
172 | // or
|
173 | const anArray = Ayay.of(1, 2, 3)
|
174 | // or
|
175 | const anotherArray = Ayay.from([1, 2, 3, 4])
|
176 | ```
|
177 |
|
178 | There are also two other static methods for constructing instances:
|
179 |
|
180 | `fromPrettyJoined`, `seedWith`
|
181 |
|
182 | Available prototype methods:
|
183 |
|
184 | `arrayByDroppingLastItem`, `arrayByInsertingItem`, `arrayByMoving`, `arrayByRemovingItemAtIndex`, `arrayByReplacingItemAtIndex`, `arrayBySwappingItems`, `arraySortedBy`, `asyncFilter`, `average`, `chunksOf`, `compactMap`, `findLast`, `findLastIndex`, `get first`, `groupBy`, `indices`, `isEmpty`, `isShallowIdenticalWith`, `iterateWhile`, `get last`, `get lastIndex`, `mapWhile`, `omitInObject`, `permutations`, `pickInObject`, `get pipe`, `prettyJoin`, `randomIndex`, `randomItem`, `reduceWhile`, `reversedArray`, `sampleOfSize`, `shuffledArray`, `sumItems`, `sumItemsWhile`, `toVanilla`, `transpose`, `uniqueItems`, `useAsKeyPathIn`, `get vanilla`
|
185 |
|
186 | Methods have been named in order to hopefully make them self explanatory. But additional documentation and examples are available in the TypeScript definitions, and will show in your editor.
|
187 |
|
188 | One example:
|
189 | ```javascript
|
190 | const numberArray = Ayay.of(1, 2, 3, 4, 5, 6)
|
191 |
|
192 | numberArray
|
193 | .arrayByDroppingLastItem()
|
194 | .average() // 3
|
195 |
|
196 | // original array not modified
|
197 | numberArray // [1, 2, 3, 4, 5, 6]
|
198 |
|
199 | ```
|
200 |
|
201 | <br>
|
202 |
|
203 | # IzzaDate
|
204 |
|
205 | IzzaDate is a small subclass of the native Date class. It enables easier manipulation, and some immutable operations on Date objects, as well as a few methods for date comparison.
|
206 |
|
207 | Available methods:
|
208 |
|
209 | `add`, `components`, `copy`, `dateByAdding`, `dateBySetting`, `dateBySubtracting`, `daysSince`, `daysUntil`, `get`, `isEarlierInTheDay`, `isLaterInTheDay`, `isSameTimeOfDay`, `set`, `subtract`
|
210 |
|
211 | Documentation is available in TypeScript definition.
|
212 |
|
213 | One example:
|
214 | ```javascript
|
215 | import IzzaDate, { DAY } from '@applicivions/js-toolbox/izza-date'
|
216 | const now = new IzzaDate()
|
217 | const tomorrow = now.dateByAdding(1, DAY)
|
218 | now.daysUntil(tomorrow) // 1
|
219 | ```
|
220 | <br>
|
221 |
|
222 | # Terminal Style
|
223 |
|
224 | Simple utility to get colored output in the terminal.
|
225 |
|
226 | ```javascript
|
227 | import style from '@applicvision/js-toolbox/style'
|
228 |
|
229 | console.log(
|
230 | style.cyan('This text is Cyan'),
|
231 | style.bold.red('This text is red and bold')
|
232 | )
|
233 | ```
|
234 |
|
235 | Available styles are those defined in `util.inspect.colors`, please see https://nodejs.org/api/util.html#foreground-colors for reference.
|
236 |
|
237 | The default export is a function, with the following properties. Each property returns the style function with that text modifier activated.
|
238 |
|
239 | `blue`, `bold`, `cyan`, `dim`, `gray`, `green`, `italic`, `red`, `underline`
|
240 |
|
241 | The modifiers are chainable to combine styles:
|
242 |
|
243 | ```javascript
|
244 | style.green.bold.italic.underline('Hello')
|
245 | ```
|
246 |
|
247 | And can be nested
|
248 | ```javascript
|
249 | style.blue(`blue ${style.bold('bold and blue')} blue`)
|
250 | ```
|
251 |
|
252 | To pass custom style string, use `.custom`:
|
253 |
|
254 | ```javascript
|
255 | console.log(style.custom('bgRed', 'bold')('Hello Red background'))
|
256 | ```
|
257 | <br>
|
258 |
|
259 | # Argument Parser
|
260 | Argument parser is a simple utility to configure a command line tool. Specify which options and flags a program accepts. The parser will parse the arguments sent (by default `process.argv.slice(2)`).
|
261 |
|
262 | Given this
|
263 | ```javascript
|
264 | // program.js
|
265 | import {parseArguments} from '@applicvision/js-toolbox/args'
|
266 | const parsed = parseArguments
|
267 | .option('message', { description: 'Specify a message' })
|
268 | .flag('flag', { description: 'A boolean option'})
|
269 | .option('option', { description: 'Another good option' })
|
270 | .help('Welcome to the program')
|
271 | .parse()
|
272 | ```
|
273 |
|
274 | When invoked like this
|
275 | ```
|
276 | node program.js --option=value1 --option value2 --flag -mhello -- arg1 arg2
|
277 | ```
|
278 |
|
279 | The argument parser will return this:
|
280 | ```
|
281 | {
|
282 | options: { option: [ 'value1', 'value2' ], flag: true, message: 'hello' },
|
283 | args: [ 'arg1', 'arg2' ]
|
284 | }
|
285 | ```
|
286 |
|
287 | If `--help` or `-h` is passed, the parser will return an object with `help` as the only property. This help can be logged to display a generated message to the user:
|
288 | ```
|
289 | Welcome to the program
|
290 |
|
291 | Options:
|
292 | -m, --message Specify a message
|
293 | -f, --flag A boolean option
|
294 | -o, --option Another good option
|
295 | -h, --help Show this message and exit.
|
296 | ```
|
297 |
|
298 | if an unknown option is passed, or if a value is not correctly passed to an option, the parser will throw an error, with a message that can be logged to the user:
|
299 |
|
300 | ```
|
301 | node program.js --missing-option=value1
|
302 | ```
|
303 |
|
304 | ```
|
305 | Error Unknown option 'missing-option'
|
306 |
|
307 | Available options:
|
308 | -m, --message Specify a message
|
309 | -f, --flag A boolean option
|
310 | -o, --option Another good option
|
311 | -h, --help Show this message and exit.
|
312 |
|
313 | ```
|
314 |
|
315 |
|
316 | <div align="center">
|
317 | <br>
|
318 |
|
319 | *Passionately developed by ApplicVision*
|
320 |
|
321 | <br>
|
322 |
|
323 | [![ApplicVision logo](https://applicvision.com/img/applicvision.png)](https://applicvision.com)
|
324 |
|
325 | </div>
|